1 /* arch/x86/mach-goldfish/pdev_bus.c
2 **
3 ** Copyright (C) 2007 Google, Inc.
4 ** Copyright (C) 2011 Intel, Inc.
5 **
6 ** This software is licensed under the terms of the GNU General Public
7 ** License version 2, as published by the Free Software Foundation, and
8 ** may be copied, distributed, and modified under those terms.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 */
16
17 #include <linux/kernel.h>
18 #include <linux/init.h>
19 #include <linux/interrupt.h>
20 #include <linux/irq.h>
21 #include <linux/platform_device.h>
22
23 #include <asm/io.h>
24 #include <asm/mach-goldfish/irqs.h>
25 #include <asm/mach-goldfish/hardware.h>
26
27 #define PDEV_BUS_OP_DONE (0x00)
28 #define PDEV_BUS_OP_REMOVE_DEV (0x04)
29 #define PDEV_BUS_OP_ADD_DEV (0x08)
30
31 #define PDEV_BUS_OP_INIT (0x00)
32
33 #define PDEV_BUS_OP (0x00)
34 #define PDEV_BUS_GET_NAME (0x04)
35 #define PDEV_BUS_NAME_LEN (0x08)
36 #define PDEV_BUS_ID (0x0c)
37 #define PDEV_BUS_IO_BASE (0x10)
38 #define PDEV_BUS_IO_SIZE (0x14)
39 #define PDEV_BUS_IRQ (0x18)
40 #define PDEV_BUS_IRQ_COUNT (0x1c)
41
42 struct pdev_bus_dev {
43 struct list_head list;
44 struct platform_device pdev;
45 struct resource resources[0];
46 };
47
48 static void goldfish_pdev_worker(struct work_struct *work);
49
50 static void __iomem *pdev_bus_base;
51 static uint32_t pdev_bus_irq;
52 static LIST_HEAD(pdev_bus_new_devices);
53 static LIST_HEAD(pdev_bus_registered_devices);
54 static LIST_HEAD(pdev_bus_removed_devices);
55 static DECLARE_WORK(pdev_bus_worker, goldfish_pdev_worker);
56
57
goldfish_pdev_worker(struct work_struct * work)58 static void goldfish_pdev_worker(struct work_struct *work)
59 {
60 int ret;
61 struct pdev_bus_dev *pos, *n;
62
63 list_for_each_entry_safe(pos, n, &pdev_bus_removed_devices, list) {
64 list_del(&pos->list);
65 platform_device_unregister(&pos->pdev);
66 kfree(pos);
67 }
68 list_for_each_entry_safe(pos, n, &pdev_bus_new_devices, list) {
69 list_del(&pos->list);
70 ret = platform_device_register(&pos->pdev);
71 if(ret) {
72 printk("goldfish_pdev_worker failed to register device, %s\n", pos->pdev.name);
73 }
74 else {
75 printk("goldfish_pdev_worker registered %s\n", pos->pdev.name);
76 }
77 list_add_tail(&pos->list, &pdev_bus_registered_devices);
78 }
79 }
80
goldfish_pdev_remove(void)81 static void goldfish_pdev_remove(void)
82 {
83 struct pdev_bus_dev *pos, *n;
84 uint32_t base;
85
86 base = readl(pdev_bus_base + PDEV_BUS_IO_BASE);
87
88 list_for_each_entry_safe(pos, n, &pdev_bus_new_devices, list) {
89 if(pos->resources[0].start == base) {
90 list_del(&pos->list);
91 kfree(pos);
92 return;
93 }
94 }
95 list_for_each_entry_safe(pos, n, &pdev_bus_registered_devices, list) {
96 if(pos->resources[0].start == base) {
97 list_del(&pos->list);
98 list_add_tail(&pos->list, &pdev_bus_removed_devices);
99 schedule_work(&pdev_bus_worker);
100 return;
101 }
102 };
103 printk("goldfish_pdev_remove could not find device at %x\n", base);
104 }
105
goldfish_new_pdev(void)106 static int goldfish_new_pdev(void)
107 {
108 struct pdev_bus_dev *dev;
109 uint32_t name_len;
110 uint32_t irq = -1, irq_count;
111 int resource_count = 2;
112 uint32_t base;
113 char *name;
114
115 base = readl(pdev_bus_base + PDEV_BUS_IO_BASE);
116
117 irq_count = readl(pdev_bus_base + PDEV_BUS_IRQ_COUNT);
118 name_len = readl(pdev_bus_base + PDEV_BUS_NAME_LEN);
119 if(irq_count)
120 resource_count++;
121
122 dev = kzalloc(sizeof(*dev) + sizeof(struct resource) * resource_count + name_len + 1, GFP_ATOMIC);
123 if(dev == NULL)
124 return -ENOMEM;
125
126 dev->pdev.num_resources = resource_count;
127 dev->pdev.resource = (struct resource *)(dev + 1);
128 dev->pdev.name = name = (char *)(dev->pdev.resource + resource_count);
129 dev->pdev.dev.coherent_dma_mask = ~0;
130
131 writel((unsigned long)name, pdev_bus_base + PDEV_BUS_GET_NAME);
132 name[name_len] = '\0';
133 dev->pdev.id = readl(pdev_bus_base + PDEV_BUS_ID);
134 dev->pdev.resource[0].start = base;
135 dev->pdev.resource[0].end = base + readl(pdev_bus_base + PDEV_BUS_IO_SIZE) - 1;
136 dev->pdev.resource[0].flags = IORESOURCE_MEM;
137 if(irq_count) {
138 irq = readl(pdev_bus_base + PDEV_BUS_IRQ);
139 dev->pdev.resource[1].start = irq;
140 dev->pdev.resource[1].end = irq + irq_count - 1;
141 dev->pdev.resource[1].flags = IORESOURCE_IRQ;
142 }
143
144 printk("goldfish_new_pdev %s at %x irq %d\n", name, base, irq);
145 list_add_tail(&dev->list, &pdev_bus_new_devices);
146 schedule_work(&pdev_bus_worker);
147
148 return 0;
149 }
150
goldfish_pdev_bus_interrupt(int irq,void * dev_id)151 static irqreturn_t goldfish_pdev_bus_interrupt(int irq, void *dev_id)
152 {
153 irqreturn_t ret = IRQ_NONE;
154 while(1) {
155 uint32_t op = readl(pdev_bus_base + PDEV_BUS_OP);
156 switch(op) {
157 case PDEV_BUS_OP_DONE:
158 return IRQ_NONE;
159
160 case PDEV_BUS_OP_REMOVE_DEV:
161 goldfish_pdev_remove();
162 break;
163
164 case PDEV_BUS_OP_ADD_DEV:
165 goldfish_new_pdev();
166 break;
167 }
168 ret = IRQ_HANDLED;
169 }
170 }
171
goldfish_pdev_bus_probe(struct platform_device * pdev)172 static int __devinit goldfish_pdev_bus_probe(struct platform_device *pdev)
173 {
174 int ret;
175 struct resource *r;
176 r = platform_get_resource(pdev, IORESOURCE_IO, 0);
177 if(r == NULL)
178 return -EINVAL;
179 pdev_bus_base = ioremap(GOLDFISH_IO_START + r->start, GOLDFISH_IO_SIZE);
180
181 r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
182 if(r == NULL)
183 return -EINVAL;
184 pdev_bus_irq = r->start;
185
186 ret = request_irq(pdev_bus_irq, goldfish_pdev_bus_interrupt, IRQF_SHARED, "goldfish_pdev_bus", pdev);
187 if(ret)
188 goto err_request_irq_failed;
189
190 writel(PDEV_BUS_OP_INIT, pdev_bus_base + PDEV_BUS_OP);
191
192 err_request_irq_failed:
193 return ret;
194 }
195
goldfish_pdev_bus_remove(struct platform_device * pdev)196 static int __devexit goldfish_pdev_bus_remove(struct platform_device *pdev)
197 {
198 free_irq(pdev_bus_irq, pdev);
199 return 0;
200 }
201
202 static struct platform_driver goldfish_pdev_bus_driver = {
203 .probe = goldfish_pdev_bus_probe,
204 .remove = __devexit_p(goldfish_pdev_bus_remove),
205 .driver = {
206 .name = "goldfish_pdev_bus"
207 }
208 };
209
goldfish_pdev_bus_init(void)210 static int __init goldfish_pdev_bus_init(void)
211 {
212 return platform_driver_register(&goldfish_pdev_bus_driver);
213 }
214
goldfish_pdev_bus_exit(void)215 static void __exit goldfish_pdev_bus_exit(void)
216 {
217 platform_driver_unregister(&goldfish_pdev_bus_driver);
218 }
219
220 module_init(goldfish_pdev_bus_init);
221 module_exit(goldfish_pdev_bus_exit);
222
223 static struct resource goldfish_pdev_bus_resources[] = {
224 {
225 .start = GOLDFISH_PDEV_BUS_BASE,
226 .end = GOLDFISH_PDEV_BUS_BASE + GOLDFISH_PDEV_BUS_END - 1,
227 .flags = IORESOURCE_IO,
228 },
229 {
230 .start = IRQ_PDEV_BUS,
231 .end = IRQ_PDEV_BUS,
232 .flags = IORESOURCE_IRQ,
233 }
234 };
235
236 struct platform_device goldfish_pdev_bus_device = {
237 .name = "goldfish_pdev_bus",
238 .id = -1,
239 .num_resources = ARRAY_SIZE(goldfish_pdev_bus_resources),
240 .resource = goldfish_pdev_bus_resources
241 };
goldfish_init(void)242 static int __init goldfish_init(void)
243 {
244 return platform_device_register(&goldfish_pdev_bus_device);
245 }
246 device_initcall(goldfish_init);
247