Commit | Line | Data |
---|---|---|
35bdd290 AR |
1 | /* |
2 | * Copyright (c) 2009-2011 Wind River Systems, Inc. | |
3 | * Copyright (c) 2011 ST Microelectronics (Alessandro Rubini) | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License version 2 as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
12 | * See the GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program; if not, write to the Free Software | |
16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
17 | * | |
18 | */ | |
19 | ||
20 | #include <linux/kernel.h> | |
21 | #include <linux/module.h> | |
22 | #include <linux/spinlock.h> | |
23 | #include <linux/errno.h> | |
24 | #include <linux/device.h> | |
25 | #include <linux/slab.h> | |
26 | #include <linux/list.h> | |
27 | #include <linux/io.h> | |
28 | #include <linux/ioport.h> | |
29 | #include <linux/pci.h> | |
30 | #include <linux/debugfs.h> | |
31 | #include <linux/seq_file.h> | |
32 | #include <linux/platform_device.h> | |
33 | #include <linux/mfd/core.h> | |
34 | #include <linux/mfd/sta2x11-mfd.h> | |
35 | ||
36 | #include <asm/sta2x11.h> | |
37 | ||
38 | /* This describes STA2X11 MFD chip for us, we may have several */ | |
39 | struct sta2x11_mfd { | |
40 | struct sta2x11_instance *instance; | |
41 | spinlock_t lock; | |
42 | struct list_head list; | |
43 | void __iomem *sctl_regs; | |
44 | void __iomem *apbreg_regs; | |
45 | }; | |
46 | ||
47 | static LIST_HEAD(sta2x11_mfd_list); | |
48 | ||
49 | /* Three functions to act on the list */ | |
50 | static struct sta2x11_mfd *sta2x11_mfd_find(struct pci_dev *pdev) | |
51 | { | |
52 | struct sta2x11_instance *instance; | |
53 | struct sta2x11_mfd *mfd; | |
54 | ||
55 | if (!pdev && !list_empty(&sta2x11_mfd_list)) { | |
56 | pr_warning("%s: Unspecified device, " | |
57 | "using first instance\n", __func__); | |
58 | return list_entry(sta2x11_mfd_list.next, | |
59 | struct sta2x11_mfd, list); | |
60 | } | |
61 | ||
62 | instance = sta2x11_get_instance(pdev); | |
63 | if (!instance) | |
64 | return NULL; | |
65 | list_for_each_entry(mfd, &sta2x11_mfd_list, list) { | |
66 | if (mfd->instance == instance) | |
67 | return mfd; | |
68 | } | |
69 | return NULL; | |
70 | } | |
71 | ||
72 | static int __devinit sta2x11_mfd_add(struct pci_dev *pdev, gfp_t flags) | |
73 | { | |
74 | struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev); | |
75 | struct sta2x11_instance *instance; | |
76 | ||
77 | if (mfd) | |
78 | return -EBUSY; | |
79 | instance = sta2x11_get_instance(pdev); | |
80 | if (!instance) | |
81 | return -EINVAL; | |
82 | mfd = kzalloc(sizeof(*mfd), flags); | |
83 | if (!mfd) | |
84 | return -ENOMEM; | |
85 | INIT_LIST_HEAD(&mfd->list); | |
86 | spin_lock_init(&mfd->lock); | |
87 | mfd->instance = instance; | |
88 | list_add(&mfd->list, &sta2x11_mfd_list); | |
89 | return 0; | |
90 | } | |
91 | ||
92 | static int __devexit mfd_remove(struct pci_dev *pdev) | |
93 | { | |
94 | struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev); | |
95 | ||
96 | if (!mfd) | |
97 | return -ENODEV; | |
98 | list_del(&mfd->list); | |
99 | kfree(mfd); | |
100 | return 0; | |
101 | } | |
102 | ||
103 | /* These two functions are exported and are not expected to fail */ | |
104 | u32 sta2x11_sctl_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val) | |
105 | { | |
106 | struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev); | |
107 | u32 r; | |
108 | unsigned long flags; | |
109 | ||
110 | if (!mfd) { | |
111 | dev_warn(&pdev->dev, ": can't access sctl regs\n"); | |
112 | return 0; | |
113 | } | |
114 | if (!mfd->sctl_regs) { | |
115 | dev_warn(&pdev->dev, ": system ctl not initialized\n"); | |
116 | return 0; | |
117 | } | |
118 | spin_lock_irqsave(&mfd->lock, flags); | |
119 | r = readl(mfd->sctl_regs + reg); | |
120 | r &= ~mask; | |
121 | r |= val; | |
122 | if (mask) | |
123 | writel(r, mfd->sctl_regs + reg); | |
124 | spin_unlock_irqrestore(&mfd->lock, flags); | |
125 | return r; | |
126 | } | |
127 | EXPORT_SYMBOL(sta2x11_sctl_mask); | |
128 | ||
129 | u32 sta2x11_apbreg_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val) | |
130 | { | |
131 | struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev); | |
132 | u32 r; | |
133 | unsigned long flags; | |
134 | ||
135 | if (!mfd) { | |
136 | dev_warn(&pdev->dev, ": can't access apb regs\n"); | |
137 | return 0; | |
138 | } | |
139 | if (!mfd->apbreg_regs) { | |
140 | dev_warn(&pdev->dev, ": apb bridge not initialized\n"); | |
141 | return 0; | |
142 | } | |
143 | spin_lock_irqsave(&mfd->lock, flags); | |
144 | r = readl(mfd->apbreg_regs + reg); | |
145 | r &= ~mask; | |
146 | r |= val; | |
147 | if (mask) | |
148 | writel(r, mfd->apbreg_regs + reg); | |
149 | spin_unlock_irqrestore(&mfd->lock, flags); | |
150 | return r; | |
151 | } | |
152 | EXPORT_SYMBOL(sta2x11_apbreg_mask); | |
153 | ||
154 | /* Two debugfs files, for our registers (FIXME: one instance only) */ | |
155 | #define REG(regname) {.name = #regname, .offset = SCTL_ ## regname} | |
156 | static struct debugfs_reg32 sta2x11_sctl_regs[] = { | |
157 | REG(SCCTL), REG(ARMCFG), REG(SCPLLCTL), REG(SCPLLFCTRL), | |
158 | REG(SCRESFRACT), REG(SCRESCTRL1), REG(SCRESXTRL2), REG(SCPEREN0), | |
159 | REG(SCPEREN1), REG(SCPEREN2), REG(SCGRST), REG(SCPCIPMCR1), | |
160 | REG(SCPCIPMCR2), REG(SCPCIPMSR1), REG(SCPCIPMSR2), REG(SCPCIPMSR3), | |
161 | REG(SCINTREN), REG(SCRISR), REG(SCCLKSTAT0), REG(SCCLKSTAT1), | |
162 | REG(SCCLKSTAT2), REG(SCRSTSTA), | |
163 | }; | |
164 | #undef REG | |
165 | ||
166 | static struct debugfs_regset32 sctl_regset = { | |
167 | .regs = sta2x11_sctl_regs, | |
168 | .nregs = ARRAY_SIZE(sta2x11_sctl_regs), | |
169 | }; | |
170 | ||
171 | #define REG(regname) {.name = #regname, .offset = regname} | |
172 | static struct debugfs_reg32 sta2x11_apbreg_regs[] = { | |
173 | REG(APBREG_BSR), REG(APBREG_PAER), REG(APBREG_PWAC), REG(APBREG_PRAC), | |
174 | REG(APBREG_PCG), REG(APBREG_PUR), REG(APBREG_EMU_PCG), | |
175 | }; | |
176 | #undef REG | |
177 | ||
178 | static struct debugfs_regset32 apbreg_regset = { | |
179 | .regs = sta2x11_apbreg_regs, | |
180 | .nregs = ARRAY_SIZE(sta2x11_apbreg_regs), | |
181 | }; | |
182 | ||
183 | static struct dentry *sta2x11_sctl_debugfs; | |
184 | static struct dentry *sta2x11_apbreg_debugfs; | |
185 | ||
186 | /* Probe for the two platform devices */ | |
187 | static int sta2x11_sctl_probe(struct platform_device *dev) | |
188 | { | |
189 | struct pci_dev **pdev; | |
190 | struct sta2x11_mfd *mfd; | |
191 | struct resource *res; | |
192 | ||
193 | pdev = dev->dev.platform_data; | |
194 | mfd = sta2x11_mfd_find(*pdev); | |
195 | if (!mfd) | |
196 | return -ENODEV; | |
197 | ||
198 | res = platform_get_resource(dev, IORESOURCE_MEM, 0); | |
199 | if (!res) | |
200 | return -ENOMEM; | |
201 | ||
202 | if (!request_mem_region(res->start, resource_size(res), | |
203 | "sta2x11-sctl")) | |
204 | return -EBUSY; | |
205 | ||
206 | mfd->sctl_regs = ioremap(res->start, resource_size(res)); | |
207 | if (!mfd->sctl_regs) { | |
208 | release_mem_region(res->start, resource_size(res)); | |
209 | return -ENOMEM; | |
210 | } | |
211 | sctl_regset.base = mfd->sctl_regs; | |
212 | sta2x11_sctl_debugfs = debugfs_create_regset32("sta2x11-sctl", | |
213 | S_IFREG | S_IRUGO, | |
214 | NULL, &sctl_regset); | |
215 | return 0; | |
216 | } | |
217 | ||
218 | static int sta2x11_apbreg_probe(struct platform_device *dev) | |
219 | { | |
220 | struct pci_dev **pdev; | |
221 | struct sta2x11_mfd *mfd; | |
222 | struct resource *res; | |
223 | ||
224 | pdev = dev->dev.platform_data; | |
225 | dev_dbg(&dev->dev, "%s: pdata is %p\n", __func__, pdev); | |
226 | dev_dbg(&dev->dev, "%s: *pdata is %p\n", __func__, *pdev); | |
227 | ||
228 | mfd = sta2x11_mfd_find(*pdev); | |
229 | if (!mfd) | |
230 | return -ENODEV; | |
231 | ||
232 | res = platform_get_resource(dev, IORESOURCE_MEM, 0); | |
233 | if (!res) | |
234 | return -ENOMEM; | |
235 | ||
236 | if (!request_mem_region(res->start, resource_size(res), | |
237 | "sta2x11-apbreg")) | |
238 | return -EBUSY; | |
239 | ||
240 | mfd->apbreg_regs = ioremap(res->start, resource_size(res)); | |
241 | if (!mfd->apbreg_regs) { | |
242 | release_mem_region(res->start, resource_size(res)); | |
243 | return -ENOMEM; | |
244 | } | |
245 | dev_dbg(&dev->dev, "%s: regbase %p\n", __func__, mfd->apbreg_regs); | |
246 | ||
247 | apbreg_regset.base = mfd->apbreg_regs; | |
248 | sta2x11_apbreg_debugfs = debugfs_create_regset32("sta2x11-apbreg", | |
249 | S_IFREG | S_IRUGO, | |
250 | NULL, &apbreg_regset); | |
251 | return 0; | |
252 | } | |
253 | ||
254 | /* The two platform drivers */ | |
255 | static struct platform_driver sta2x11_sctl_platform_driver = { | |
256 | .driver = { | |
257 | .name = "sta2x11-sctl", | |
258 | .owner = THIS_MODULE, | |
259 | }, | |
260 | .probe = sta2x11_sctl_probe, | |
261 | }; | |
262 | ||
263 | static int __init sta2x11_sctl_init(void) | |
264 | { | |
265 | pr_info("%s\n", __func__); | |
266 | return platform_driver_register(&sta2x11_sctl_platform_driver); | |
267 | } | |
268 | ||
269 | static struct platform_driver sta2x11_platform_driver = { | |
270 | .driver = { | |
271 | .name = "sta2x11-apbreg", | |
272 | .owner = THIS_MODULE, | |
273 | }, | |
274 | .probe = sta2x11_apbreg_probe, | |
275 | }; | |
276 | ||
277 | static int __init sta2x11_apbreg_init(void) | |
278 | { | |
279 | pr_info("%s\n", __func__); | |
280 | return platform_driver_register(&sta2x11_platform_driver); | |
281 | } | |
282 | ||
283 | /* | |
284 | * What follows is the PCI device that hosts the above two pdevs. | |
285 | * Each logic block is 4kB and they are all consecutive: we use this info. | |
286 | */ | |
287 | ||
288 | /* Bar 0 */ | |
289 | enum bar0_cells { | |
290 | STA2X11_GPIO_0 = 0, | |
291 | STA2X11_GPIO_1, | |
292 | STA2X11_GPIO_2, | |
293 | STA2X11_GPIO_3, | |
294 | STA2X11_SCTL, | |
295 | STA2X11_SCR, | |
296 | STA2X11_TIME, | |
297 | }; | |
298 | /* Bar 1 */ | |
299 | enum bar1_cells { | |
300 | STA2X11_APBREG = 0, | |
301 | }; | |
302 | #define CELL_4K(_name, _cell) { \ | |
303 | .name = _name, \ | |
304 | .start = _cell * 4096, .end = _cell * 4096 + 4095, \ | |
305 | .flags = IORESOURCE_MEM, \ | |
306 | } | |
307 | ||
308 | static const __devinitconst struct resource gpio_resources[] = { | |
309 | { | |
310 | .name = "sta2x11_gpio", /* 4 consecutive cells, 1 driver */ | |
311 | .start = 0, | |
312 | .end = (4 * 4096) - 1, | |
313 | .flags = IORESOURCE_MEM, | |
314 | } | |
315 | }; | |
316 | static const __devinitconst struct resource sctl_resources[] = { | |
317 | CELL_4K("sta2x11-sctl", STA2X11_SCTL), | |
318 | }; | |
319 | static const __devinitconst struct resource scr_resources[] = { | |
320 | CELL_4K("sta2x11-scr", STA2X11_SCR), | |
321 | }; | |
322 | static const __devinitconst struct resource time_resources[] = { | |
323 | CELL_4K("sta2x11-time", STA2X11_TIME), | |
324 | }; | |
325 | ||
326 | static const __devinitconst struct resource apbreg_resources[] = { | |
327 | CELL_4K("sta2x11-apbreg", STA2X11_APBREG), | |
328 | }; | |
329 | ||
330 | #define DEV(_name, _r) \ | |
331 | { .name = _name, .num_resources = ARRAY_SIZE(_r), .resources = _r, } | |
332 | ||
333 | static __devinitdata struct mfd_cell sta2x11_mfd_bar0[] = { | |
334 | DEV("sta2x11-gpio", gpio_resources), /* offset 0: we add pdata later */ | |
335 | DEV("sta2x11-sctl", sctl_resources), | |
336 | DEV("sta2x11-scr", scr_resources), | |
337 | DEV("sta2x11-time", time_resources), | |
338 | }; | |
339 | ||
340 | static __devinitdata struct mfd_cell sta2x11_mfd_bar1[] = { | |
341 | DEV("sta2x11-apbreg", apbreg_resources), | |
342 | }; | |
343 | ||
344 | static int sta2x11_mfd_suspend(struct pci_dev *pdev, pm_message_t state) | |
345 | { | |
346 | pci_save_state(pdev); | |
347 | pci_disable_device(pdev); | |
348 | pci_set_power_state(pdev, pci_choose_state(pdev, state)); | |
349 | ||
350 | return 0; | |
351 | } | |
352 | ||
353 | static int sta2x11_mfd_resume(struct pci_dev *pdev) | |
354 | { | |
355 | int err; | |
356 | ||
357 | pci_set_power_state(pdev, 0); | |
358 | err = pci_enable_device(pdev); | |
359 | if (err) | |
360 | return err; | |
361 | pci_restore_state(pdev); | |
362 | ||
363 | return 0; | |
364 | } | |
365 | ||
366 | static int __devinit sta2x11_mfd_probe(struct pci_dev *pdev, | |
367 | const struct pci_device_id *pci_id) | |
368 | { | |
369 | int err, i; | |
370 | struct sta2x11_gpio_pdata *gpio_data; | |
371 | ||
372 | dev_info(&pdev->dev, "%s\n", __func__); | |
373 | ||
374 | err = pci_enable_device(pdev); | |
375 | if (err) { | |
376 | dev_err(&pdev->dev, "Can't enable device.\n"); | |
377 | return err; | |
378 | } | |
379 | ||
380 | err = pci_enable_msi(pdev); | |
381 | if (err) | |
382 | dev_info(&pdev->dev, "Enable msi failed\n"); | |
383 | ||
384 | /* Read gpio config data as pci device's platform data */ | |
385 | gpio_data = dev_get_platdata(&pdev->dev); | |
386 | if (!gpio_data) | |
387 | dev_warn(&pdev->dev, "no gpio configuration\n"); | |
388 | ||
389 | dev_dbg(&pdev->dev, "%s, gpio_data = %p (%p)\n", __func__, | |
390 | gpio_data, &gpio_data); | |
391 | dev_dbg(&pdev->dev, "%s, pdev = %p (%p)\n", __func__, | |
392 | pdev, &pdev); | |
393 | ||
394 | /* platform data is the pci device for all of them */ | |
395 | for (i = 0; i < ARRAY_SIZE(sta2x11_mfd_bar0); i++) { | |
396 | sta2x11_mfd_bar0[i].pdata_size = sizeof(pdev); | |
397 | sta2x11_mfd_bar0[i].platform_data = &pdev; | |
398 | } | |
399 | sta2x11_mfd_bar1[0].pdata_size = sizeof(pdev); | |
400 | sta2x11_mfd_bar1[0].platform_data = &pdev; | |
401 | ||
402 | /* Record this pdev before mfd_add_devices: their probe looks for it */ | |
403 | sta2x11_mfd_add(pdev, GFP_ATOMIC); | |
404 | ||
405 | ||
406 | err = mfd_add_devices(&pdev->dev, -1, | |
407 | sta2x11_mfd_bar0, | |
408 | ARRAY_SIZE(sta2x11_mfd_bar0), | |
409 | &pdev->resource[0], | |
410 | 0); | |
411 | if (err) { | |
412 | dev_err(&pdev->dev, "mfd_add_devices[0] failed: %d\n", err); | |
413 | goto err_disable; | |
414 | } | |
415 | ||
416 | err = mfd_add_devices(&pdev->dev, -1, | |
417 | sta2x11_mfd_bar1, | |
418 | ARRAY_SIZE(sta2x11_mfd_bar1), | |
419 | &pdev->resource[1], | |
420 | 0); | |
421 | if (err) { | |
422 | dev_err(&pdev->dev, "mfd_add_devices[1] failed: %d\n", err); | |
423 | goto err_disable; | |
424 | } | |
425 | ||
426 | return 0; | |
427 | ||
428 | err_disable: | |
429 | mfd_remove_devices(&pdev->dev); | |
430 | pci_disable_device(pdev); | |
431 | pci_disable_msi(pdev); | |
432 | return err; | |
433 | } | |
434 | ||
435 | static DEFINE_PCI_DEVICE_TABLE(sta2x11_mfd_tbl) = { | |
436 | {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_GPIO)}, | |
437 | {0,}, | |
438 | }; | |
439 | ||
440 | static struct pci_driver sta2x11_mfd_driver = { | |
441 | .name = "sta2x11-mfd", | |
442 | .id_table = sta2x11_mfd_tbl, | |
443 | .probe = sta2x11_mfd_probe, | |
444 | .suspend = sta2x11_mfd_suspend, | |
445 | .resume = sta2x11_mfd_resume, | |
446 | }; | |
447 | ||
448 | static int __init sta2x11_mfd_init(void) | |
449 | { | |
450 | pr_info("%s\n", __func__); | |
451 | return pci_register_driver(&sta2x11_mfd_driver); | |
452 | } | |
453 | ||
454 | /* | |
455 | * All of this must be ready before "normal" devices like MMCI appear. | |
456 | * But MFD (the pci device) can't be too early. The following choice | |
457 | * prepares platform drivers very early and probe the PCI device later, | |
458 | * but before other PCI devices. | |
459 | */ | |
460 | subsys_initcall(sta2x11_apbreg_init); | |
461 | subsys_initcall(sta2x11_sctl_init); | |
462 | rootfs_initcall(sta2x11_mfd_init); | |
463 | ||
464 | MODULE_LICENSE("GPL v2"); | |
465 | MODULE_AUTHOR("Wind River"); | |
466 | MODULE_DESCRIPTION("STA2x11 mfd for GPIO, SCTL and APBREG"); | |
467 | MODULE_DEVICE_TABLE(pci, sta2x11_mfd_tbl); |