• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* arch/arm/mach-goldfish/pdev_bus.c
2 **
3 ** Copyright (C) 2007 Google, Inc.
4 **
5 ** This software is licensed under the terms of the GNU General Public
6 ** License version 2, as published by the Free Software Foundation, and
7 ** may be copied, distributed, and modified under those terms.
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.  See the
12 ** GNU General Public License for more details.
13 **
14 */
15 
16 #include <linux/kernel.h>
17 #include <linux/init.h>
18 #include <linux/interrupt.h>
19 #include <linux/irq.h>
20 #include <linux/platform_device.h>
21 
22 #include <mach/hardware.h>
23 #include <asm/io.h>
24 
25 #include <asm/mach-goldfish/irq.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 uint32_t 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(KERN_ERR "goldfish_pdev_worker failed to register device, %s\n", pos->pdev.name);
73 		else
74 			printk(KERN_ERR "goldfish_pdev_worker registered %s\n", pos->pdev.name);
75 		list_add_tail(&pos->list, &pdev_bus_registered_devices);
76 	}
77 }
78 
goldfish_pdev_remove(void)79 static void goldfish_pdev_remove(void)
80 {
81 	struct pdev_bus_dev *pos, *n;
82 	uint32_t base;
83 
84 	base = readl(pdev_bus_base + PDEV_BUS_IO_BASE);
85 
86 	list_for_each_entry_safe(pos, n, &pdev_bus_new_devices, list) {
87 		if (pos->resources[0].start == base) {
88 			list_del(&pos->list);
89 			kfree(pos);
90 			return;
91 		}
92 	}
93 	list_for_each_entry_safe(pos, n, &pdev_bus_registered_devices, list) {
94 		if (pos->resources[0].start == base) {
95 			list_del(&pos->list);
96 			list_add_tail(&pos->list, &pdev_bus_removed_devices);
97 			schedule_work(&pdev_bus_worker);
98 			return;
99 		}
100 	};
101 	printk(KERN_ERR "goldfish_pdev_remove could not find device at %x\n", base);
102 }
103 
goldfish_new_pdev(void)104 static int goldfish_new_pdev(void)
105 {
106 	struct pdev_bus_dev *dev;
107 	uint32_t name_len;
108 	uint32_t irq = -1, irq_count;
109 	int resource_count = 2;
110 	uint32_t base;
111 	char *name;
112 
113 	base = readl(pdev_bus_base + PDEV_BUS_IO_BASE);
114 
115 	irq_count = readl(pdev_bus_base + PDEV_BUS_IRQ_COUNT);
116 	name_len = readl(pdev_bus_base + PDEV_BUS_NAME_LEN);
117 	if (irq_count)
118 		resource_count++;
119 
120 	dev = kzalloc(sizeof(*dev) + sizeof(struct resource) * resource_count + name_len + 1, GFP_ATOMIC);
121 	if (dev == NULL)
122 		return -ENOMEM;
123 
124 	dev->pdev.num_resources = resource_count;
125 	dev->pdev.resource = (struct resource *)(dev + 1);
126 	dev->pdev.name = name = (char *)(dev->pdev.resource + resource_count);
127 	dev->pdev.dev.coherent_dma_mask = ~0;
128 
129 	writel((unsigned long)name, pdev_bus_base + PDEV_BUS_GET_NAME);
130 	name[name_len] = '\0';
131 	dev->pdev.id = readl(pdev_bus_base + PDEV_BUS_ID);
132 	dev->pdev.resource[0].start = base;
133 	dev->pdev.resource[0].end = base + readl(pdev_bus_base + PDEV_BUS_IO_SIZE) - 1;
134 	dev->pdev.resource[0].flags = IORESOURCE_MEM;
135 	if (irq_count) {
136 		irq = readl(pdev_bus_base + PDEV_BUS_IRQ);
137 		dev->pdev.resource[1].start = GOLDFISH_IRQ_BASE+irq;
138 		dev->pdev.resource[1].end = GOLDFISH_IRQ_BASE+irq + irq_count - 1;
139 		dev->pdev.resource[1].flags = IORESOURCE_IRQ;
140 	}
141 
142 	printk(KERN_INFO "goldfish_new_pdev %s at %x irq %d\n", name, base, irq);
143 	list_add_tail(&dev->list, &pdev_bus_new_devices);
144 	schedule_work(&pdev_bus_worker);
145 
146 	return 0;
147 }
148 
goldfish_pdev_bus_interrupt(int irq,void * dev_id)149 static irqreturn_t goldfish_pdev_bus_interrupt(int irq, void *dev_id)
150 {
151 	irqreturn_t ret = IRQ_NONE;
152 	while (1) {
153 		uint32_t op = readl(pdev_bus_base + PDEV_BUS_OP);
154 		switch (op) {
155 		case PDEV_BUS_OP_DONE:
156 			return IRQ_NONE;
157 
158 		case PDEV_BUS_OP_REMOVE_DEV:
159 			goldfish_pdev_remove();
160 			break;
161 
162 		case PDEV_BUS_OP_ADD_DEV:
163 			goldfish_new_pdev();
164 			break;
165 		}
166 		ret = IRQ_HANDLED;
167 	}
168 }
169 
goldfish_pdev_bus_probe(struct platform_device * pdev)170 static int __devinit goldfish_pdev_bus_probe(struct platform_device *pdev)
171 {
172 	int ret;
173 	struct resource *r;
174 	r = platform_get_resource(pdev, IORESOURCE_IO, 0);
175 	if (r == NULL)
176 		return -EINVAL;
177 	pdev_bus_base = IO_ADDRESS(r->start);
178 
179 	r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
180 	if (r == NULL)
181 		return -EINVAL;
182 	pdev_bus_irq = r->start;
183 
184 	ret = request_irq(pdev_bus_irq, goldfish_pdev_bus_interrupt, IRQF_SHARED, "goldfish_pdev_bus", pdev);
185 	if (ret)
186 		goto err_request_irq_failed;
187 
188 	writel(PDEV_BUS_OP_INIT, pdev_bus_base + PDEV_BUS_OP);
189 
190 err_request_irq_failed:
191 	return ret;
192 }
193 
goldfish_pdev_bus_remove(struct platform_device * pdev)194 static int __devexit goldfish_pdev_bus_remove(struct platform_device *pdev)
195 {
196 	free_irq(pdev_bus_irq, pdev);
197 	return 0;
198 }
199 
200 static struct platform_driver goldfish_pdev_bus_driver = {
201 	.probe = goldfish_pdev_bus_probe,
202 	.remove = __devexit_p(goldfish_pdev_bus_remove),
203 	.driver = {
204 		.name = "goldfish_pdev_bus"
205 	}
206 };
207 
goldfish_pdev_bus_init(void)208 static int __init goldfish_pdev_bus_init(void)
209 {
210 	return platform_driver_register(&goldfish_pdev_bus_driver);
211 }
212 
goldfish_pdev_bus_exit(void)213 static void __exit goldfish_pdev_bus_exit(void)
214 {
215 	platform_driver_unregister(&goldfish_pdev_bus_driver);
216 }
217 
218 module_init(goldfish_pdev_bus_init);
219 module_exit(goldfish_pdev_bus_exit);
220 
221