Commit | Line | Data |
---|---|---|
88e0abcd PM |
1 | /* |
2 | * This program is free software; you can redistribute it and/or modify | |
3 | * it under the terms of the GNU General Public License version 2 as | |
4 | * published by the Free Software Foundation. | |
5 | * | |
6 | * This program is distributed in the hope that it will be useful, | |
7 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
8 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
9 | * GNU General Public License for more details. | |
10 | * | |
11 | * Copyright (C) 2012 ARM Limited | |
12 | */ | |
13 | ||
974cc7b9 | 14 | #include <linux/basic_mmio_gpio.h> |
88e0abcd | 15 | #include <linux/err.h> |
88e0abcd | 16 | #include <linux/io.h> |
974cc7b9 | 17 | #include <linux/mfd/core.h> |
88e0abcd | 18 | #include <linux/of_address.h> |
3b9334ac | 19 | #include <linux/of_platform.h> |
974cc7b9 | 20 | #include <linux/platform_data/syscon.h> |
88e0abcd | 21 | #include <linux/platform_device.h> |
88e0abcd PM |
22 | #include <linux/slab.h> |
23 | #include <linux/stat.h> | |
88e0abcd PM |
24 | #include <linux/vexpress.h> |
25 | ||
26 | #define SYS_ID 0x000 | |
27 | #define SYS_SW 0x004 | |
28 | #define SYS_LED 0x008 | |
29 | #define SYS_100HZ 0x024 | |
88e0abcd PM |
30 | #define SYS_FLAGSSET 0x030 |
31 | #define SYS_FLAGSCLR 0x034 | |
32 | #define SYS_NVFLAGS 0x038 | |
33 | #define SYS_NVFLAGSSET 0x038 | |
34 | #define SYS_NVFLAGSCLR 0x03c | |
35 | #define SYS_MCI 0x048 | |
36 | #define SYS_FLASH 0x04c | |
37 | #define SYS_CFGSW 0x058 | |
38 | #define SYS_24MHZ 0x05c | |
39 | #define SYS_MISC 0x060 | |
40 | #define SYS_DMA 0x064 | |
41 | #define SYS_PROCID0 0x084 | |
42 | #define SYS_PROCID1 0x088 | |
43 | #define SYS_CFGDATA 0x0a0 | |
44 | #define SYS_CFGCTRL 0x0a4 | |
45 | #define SYS_CFGSTAT 0x0a8 | |
46 | ||
47 | #define SYS_HBI_MASK 0xfff | |
88e0abcd PM |
48 | #define SYS_PROCIDx_HBI_SHIFT 0 |
49 | ||
50 | #define SYS_MCI_CARDIN (1 << 0) | |
51 | #define SYS_MCI_WPROT (1 << 1) | |
52 | ||
88e0abcd PM |
53 | #define SYS_MISC_MASTERSITE (1 << 14) |
54 | ||
88e0abcd | 55 | |
974cc7b9 | 56 | static void __iomem *__vexpress_sysreg_base; |
88e0abcd | 57 | |
974cc7b9 PM |
58 | static void __iomem *vexpress_sysreg_base(void) |
59 | { | |
60 | if (!__vexpress_sysreg_base) { | |
61 | struct device_node *node = of_find_compatible_node(NULL, NULL, | |
62 | "arm,vexpress-sysreg"); | |
88e0abcd | 63 | |
974cc7b9 PM |
64 | __vexpress_sysreg_base = of_iomap(node, 0); |
65 | } | |
66 | ||
67 | WARN_ON(!__vexpress_sysreg_base); | |
68 | ||
69 | return __vexpress_sysreg_base; | |
70 | } | |
88e0abcd PM |
71 | |
72 | ||
3b9334ac PM |
73 | static int vexpress_sysreg_get_master(void) |
74 | { | |
974cc7b9 | 75 | if (readl(vexpress_sysreg_base() + SYS_MISC) & SYS_MISC_MASTERSITE) |
3b9334ac PM |
76 | return VEXPRESS_SITE_DB2; |
77 | ||
78 | return VEXPRESS_SITE_DB1; | |
79 | } | |
80 | ||
88e0abcd PM |
81 | void vexpress_flags_set(u32 data) |
82 | { | |
974cc7b9 PM |
83 | writel(~0, vexpress_sysreg_base() + SYS_FLAGSCLR); |
84 | writel(data, vexpress_sysreg_base() + SYS_FLAGSSET); | |
85 | } | |
86 | ||
87 | unsigned int vexpress_get_mci_cardin(struct device *dev) | |
88 | { | |
89 | return readl(vexpress_sysreg_base() + SYS_MCI) & SYS_MCI_CARDIN; | |
88e0abcd PM |
90 | } |
91 | ||
92 | u32 vexpress_get_procid(int site) | |
93 | { | |
94 | if (site == VEXPRESS_SITE_MASTER) | |
3b9334ac | 95 | site = vexpress_sysreg_get_master(); |
88e0abcd | 96 | |
974cc7b9 | 97 | return readl(vexpress_sysreg_base() + (site == VEXPRESS_SITE_DB1 ? |
88e0abcd PM |
98 | SYS_PROCID0 : SYS_PROCID1)); |
99 | } | |
100 | ||
88e0abcd PM |
101 | void __iomem *vexpress_get_24mhz_clock_base(void) |
102 | { | |
974cc7b9 | 103 | return vexpress_sysreg_base() + SYS_24MHZ; |
88e0abcd PM |
104 | } |
105 | ||
3b9334ac | 106 | |
52666298 PM |
107 | void __init vexpress_sysreg_early_init(void __iomem *base) |
108 | { | |
974cc7b9 | 109 | __vexpress_sysreg_base = base; |
3b9334ac PM |
110 | |
111 | vexpress_config_set_master(vexpress_sysreg_get_master()); | |
88e0abcd PM |
112 | } |
113 | ||
114 | ||
974cc7b9 | 115 | /* The sysreg block is just a random collection of various functions... */ |
8ea402f5 | 116 | |
974cc7b9 PM |
117 | static struct syscon_platform_data vexpress_sysreg_sys_id_pdata = { |
118 | .label = "sys_id", | |
88e0abcd PM |
119 | }; |
120 | ||
974cc7b9 PM |
121 | static struct bgpio_pdata vexpress_sysreg_sys_led_pdata = { |
122 | .label = "sys_led", | |
123 | .base = -1, | |
124 | .ngpio = 8, | |
88e0abcd PM |
125 | }; |
126 | ||
974cc7b9 PM |
127 | static struct bgpio_pdata vexpress_sysreg_sys_mci_pdata = { |
128 | .label = "sys_mci", | |
129 | .base = -1, | |
130 | .ngpio = 2, | |
8ea402f5 PM |
131 | }; |
132 | ||
974cc7b9 PM |
133 | static struct bgpio_pdata vexpress_sysreg_sys_flash_pdata = { |
134 | .label = "sys_flash", | |
135 | .base = -1, | |
136 | .ngpio = 1, | |
8ea402f5 PM |
137 | }; |
138 | ||
974cc7b9 PM |
139 | static struct syscon_platform_data vexpress_sysreg_sys_misc_pdata = { |
140 | .label = "sys_misc", | |
141 | }; | |
8ea402f5 | 142 | |
974cc7b9 PM |
143 | static struct syscon_platform_data vexpress_sysreg_sys_procid_pdata = { |
144 | .label = "sys_procid", | |
145 | }; | |
88e0abcd | 146 | |
974cc7b9 PM |
147 | static struct mfd_cell vexpress_sysreg_cells[] = { |
148 | { | |
149 | .name = "syscon", | |
150 | .num_resources = 1, | |
151 | .resources = (struct resource []) { | |
152 | DEFINE_RES_MEM(SYS_ID, 0x4), | |
153 | }, | |
154 | .platform_data = &vexpress_sysreg_sys_id_pdata, | |
155 | .pdata_size = sizeof(vexpress_sysreg_sys_id_pdata), | |
156 | }, { | |
157 | .name = "basic-mmio-gpio", | |
158 | .of_compatible = "arm,vexpress-sysreg,sys_led", | |
159 | .num_resources = 1, | |
160 | .resources = (struct resource []) { | |
161 | DEFINE_RES_MEM_NAMED(SYS_LED, 0x4, "dat"), | |
162 | }, | |
163 | .platform_data = &vexpress_sysreg_sys_led_pdata, | |
164 | .pdata_size = sizeof(vexpress_sysreg_sys_led_pdata), | |
165 | }, { | |
166 | .name = "basic-mmio-gpio", | |
167 | .of_compatible = "arm,vexpress-sysreg,sys_mci", | |
168 | .num_resources = 1, | |
169 | .resources = (struct resource []) { | |
170 | DEFINE_RES_MEM_NAMED(SYS_MCI, 0x4, "dat"), | |
171 | }, | |
172 | .platform_data = &vexpress_sysreg_sys_mci_pdata, | |
173 | .pdata_size = sizeof(vexpress_sysreg_sys_mci_pdata), | |
174 | }, { | |
175 | .name = "basic-mmio-gpio", | |
176 | .of_compatible = "arm,vexpress-sysreg,sys_flash", | |
177 | .num_resources = 1, | |
178 | .resources = (struct resource []) { | |
179 | DEFINE_RES_MEM_NAMED(SYS_FLASH, 0x4, "dat"), | |
180 | }, | |
181 | .platform_data = &vexpress_sysreg_sys_flash_pdata, | |
182 | .pdata_size = sizeof(vexpress_sysreg_sys_flash_pdata), | |
183 | }, { | |
184 | .name = "syscon", | |
185 | .num_resources = 1, | |
186 | .resources = (struct resource []) { | |
187 | DEFINE_RES_MEM(SYS_MISC, 0x4), | |
188 | }, | |
189 | .platform_data = &vexpress_sysreg_sys_misc_pdata, | |
190 | .pdata_size = sizeof(vexpress_sysreg_sys_misc_pdata), | |
191 | }, { | |
192 | .name = "syscon", | |
193 | .num_resources = 1, | |
194 | .resources = (struct resource []) { | |
195 | DEFINE_RES_MEM(SYS_PROCID0, 0x8), | |
196 | }, | |
197 | .platform_data = &vexpress_sysreg_sys_procid_pdata, | |
198 | .pdata_size = sizeof(vexpress_sysreg_sys_procid_pdata), | |
199 | }, { | |
200 | .name = "vexpress-syscfg", | |
201 | .num_resources = 1, | |
202 | .resources = (struct resource []) { | |
203 | DEFINE_RES_MEM(SYS_CFGDATA, 0xc), | |
204 | }, | |
205 | } | |
206 | }; | |
88e0abcd | 207 | |
612b95cd | 208 | static int vexpress_sysreg_probe(struct platform_device *pdev) |
88e0abcd | 209 | { |
974cc7b9 PM |
210 | struct resource *mem; |
211 | void __iomem *base; | |
212 | struct bgpio_chip *mmc_gpio_chip; | |
6b2c31c7 | 213 | u32 dt_hbi; |
88e0abcd | 214 | |
974cc7b9 PM |
215 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
216 | if (!mem) | |
217 | return -EINVAL; | |
88e0abcd | 218 | |
974cc7b9 PM |
219 | base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); |
220 | if (!base) | |
221 | return -ENOMEM; | |
88e0abcd | 222 | |
3b9334ac | 223 | vexpress_config_set_master(vexpress_sysreg_get_master()); |
88e0abcd | 224 | |
6b2c31c7 PM |
225 | /* Confirm board type against DT property, if available */ |
226 | if (of_property_read_u32(of_allnodes, "arm,hbi", &dt_hbi) == 0) { | |
227 | u32 id = vexpress_get_procid(VEXPRESS_SITE_MASTER); | |
228 | u32 hbi = (id >> SYS_PROCIDx_HBI_SHIFT) & SYS_HBI_MASK; | |
229 | ||
230 | if (WARN_ON(dt_hbi != hbi)) | |
231 | dev_warn(&pdev->dev, "DT HBI (%x) is not matching hardware (%x)!\n", | |
232 | dt_hbi, hbi); | |
233 | } | |
234 | ||
974cc7b9 PM |
235 | /* |
236 | * Duplicated SYS_MCI pseudo-GPIO controller for compatibility with | |
237 | * older trees using sysreg node for MMC control lines. | |
238 | */ | |
239 | mmc_gpio_chip = devm_kzalloc(&pdev->dev, sizeof(*mmc_gpio_chip), | |
240 | GFP_KERNEL); | |
241 | if (!mmc_gpio_chip) | |
242 | return -ENOMEM; | |
243 | bgpio_init(mmc_gpio_chip, &pdev->dev, 0x4, base + SYS_MCI, | |
244 | NULL, NULL, NULL, NULL, 0); | |
245 | mmc_gpio_chip->gc.ngpio = 2; | |
246 | gpiochip_add(&mmc_gpio_chip->gc); | |
247 | ||
248 | return mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO, | |
249 | vexpress_sysreg_cells, | |
250 | ARRAY_SIZE(vexpress_sysreg_cells), mem, 0, NULL); | |
88e0abcd PM |
251 | } |
252 | ||
253 | static const struct of_device_id vexpress_sysreg_match[] = { | |
254 | { .compatible = "arm,vexpress-sysreg", }, | |
255 | {}, | |
256 | }; | |
257 | ||
258 | static struct platform_driver vexpress_sysreg_driver = { | |
259 | .driver = { | |
260 | .name = "vexpress-sysreg", | |
261 | .of_match_table = vexpress_sysreg_match, | |
262 | }, | |
263 | .probe = vexpress_sysreg_probe, | |
264 | }; | |
265 | ||
266 | static int __init vexpress_sysreg_init(void) | |
267 | { | |
3b9334ac PM |
268 | struct device_node *node; |
269 | ||
270 | /* Need the sysreg early, before any other device... */ | |
271 | for_each_matching_node(node, vexpress_sysreg_match) | |
272 | of_platform_device_create(node, NULL, NULL); | |
273 | ||
88e0abcd PM |
274 | return platform_driver_register(&vexpress_sysreg_driver); |
275 | } | |
276 | core_initcall(vexpress_sysreg_init); |