• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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