• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 Google, Inc.
3  * Copyright (C) 2011 Intel, Inc.
4  * Copyright (C) 2013 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 #include <linux/slab.h>
23 #include <linux/io.h>
24 #include <linux/goldfish.h>
25 
26 #define PDEV_BUS_OP_DONE        (0x00)
27 #define PDEV_BUS_OP_REMOVE_DEV  (0x04)
28 #define PDEV_BUS_OP_ADD_DEV     (0x08)
29 
30 #define PDEV_BUS_OP_INIT        (0x00)
31 
32 #define PDEV_BUS_OP             (0x00)
33 #define PDEV_BUS_GET_NAME       (0x04)
34 #define PDEV_BUS_NAME_LEN       (0x08)
35 #define PDEV_BUS_ID             (0x0c)
36 #define PDEV_BUS_IO_BASE        (0x10)
37 #define PDEV_BUS_IO_SIZE        (0x14)
38 #define PDEV_BUS_IRQ            (0x18)
39 #define PDEV_BUS_IRQ_COUNT      (0x1c)
40 #define PDEV_BUS_GET_NAME_HIGH  (0x20)
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 unsigned long pdev_bus_addr;
52 static unsigned long pdev_bus_len;
53 static u32 pdev_bus_irq;
54 static LIST_HEAD(pdev_bus_new_devices);
55 static LIST_HEAD(pdev_bus_registered_devices);
56 static LIST_HEAD(pdev_bus_removed_devices);
57 static DECLARE_WORK(pdev_bus_worker, goldfish_pdev_worker);
58 
59 
goldfish_pdev_worker(struct work_struct * work)60 static void goldfish_pdev_worker(struct work_struct *work)
61 {
62 	int ret;
63 	struct pdev_bus_dev *pos, *n;
64 
65 	list_for_each_entry_safe(pos, n, &pdev_bus_removed_devices, list) {
66 		list_del(&pos->list);
67 		platform_device_unregister(&pos->pdev);
68 		kfree(pos);
69 	}
70 	list_for_each_entry_safe(pos, n, &pdev_bus_new_devices, list) {
71 		list_del(&pos->list);
72 		ret = platform_device_register(&pos->pdev);
73 		if (ret)
74 			pr_err("goldfish_pdev_worker failed to register device, %s\n",
75 								pos->pdev.name);
76 		list_add_tail(&pos->list, &pdev_bus_registered_devices);
77 	}
78 }
79 
goldfish_pdev_remove(void)80 static void goldfish_pdev_remove(void)
81 {
82 	struct pdev_bus_dev *pos, *n;
83 	u32 base;
84 
85 	base = readl(pdev_bus_base + PDEV_BUS_IO_BASE);
86 
87 	list_for_each_entry_safe(pos, n, &pdev_bus_new_devices, list) {
88 		if (pos->resources[0].start == base) {
89 			list_del(&pos->list);
90 			kfree(pos);
91 			return;
92 		}
93 	}
94 	list_for_each_entry_safe(pos, n, &pdev_bus_registered_devices, list) {
95 		if (pos->resources[0].start == base) {
96 			list_del(&pos->list);
97 			list_add_tail(&pos->list, &pdev_bus_removed_devices);
98 			schedule_work(&pdev_bus_worker);
99 			return;
100 		}
101 	};
102 	pr_err("goldfish_pdev_remove could not find device at %x\n", base);
103 }
104 
goldfish_new_pdev(void)105 static int goldfish_new_pdev(void)
106 {
107 	struct pdev_bus_dev *dev;
108 	u32 name_len;
109 	u32 irq = -1, irq_count;
110 	int resource_count = 2;
111 	u32 base;
112 	char *name;
113 
114 	base = readl(pdev_bus_base + PDEV_BUS_IO_BASE);
115 
116 	irq_count = readl(pdev_bus_base + PDEV_BUS_IRQ_COUNT);
117 	name_len = readl(pdev_bus_base + PDEV_BUS_NAME_LEN);
118 	if (irq_count)
119 		resource_count++;
120 
121 	dev = kzalloc(sizeof(*dev) +
122 		sizeof(struct resource) * resource_count +
123 		name_len + 1 + sizeof(*dev->pdev.dev.dma_mask), GFP_ATOMIC);
124 	if (dev == NULL)
125 		return -ENOMEM;
126 
127 	dev->pdev.num_resources = resource_count;
128 	dev->pdev.resource = (struct resource *)(dev + 1);
129 	dev->pdev.name = name = (char *)(dev->pdev.resource + resource_count);
130 	dev->pdev.dev.coherent_dma_mask = ~0;
131 	dev->pdev.dev.dma_mask = (void *)(dev->pdev.name + name_len + 1);
132 	*dev->pdev.dev.dma_mask = ~0;
133 
134 	gf_write_ptr(name, pdev_bus_base + PDEV_BUS_GET_NAME,
135 		pdev_bus_base + PDEV_BUS_GET_NAME_HIGH);
136 
137 	name[name_len] = '\0';
138 	dev->pdev.id = readl(pdev_bus_base + PDEV_BUS_ID);
139 	dev->pdev.resource[0].start = base;
140 	dev->pdev.resource[0].end = base +
141 				readl(pdev_bus_base + PDEV_BUS_IO_SIZE) - 1;
142 	dev->pdev.resource[0].flags = IORESOURCE_MEM;
143 	if (irq_count) {
144 		irq = readl(pdev_bus_base + PDEV_BUS_IRQ);
145 		dev->pdev.resource[1].start = irq;
146 		dev->pdev.resource[1].end = irq + irq_count - 1;
147 		dev->pdev.resource[1].flags = IORESOURCE_IRQ;
148 	}
149 
150 	pr_debug("goldfish_new_pdev %s at %x irq %d\n", name, base, irq);
151 	list_add_tail(&dev->list, &pdev_bus_new_devices);
152 	schedule_work(&pdev_bus_worker);
153 
154 	return 0;
155 }
156 
goldfish_pdev_bus_interrupt(int irq,void * dev_id)157 static irqreturn_t goldfish_pdev_bus_interrupt(int irq, void *dev_id)
158 {
159 	irqreturn_t ret = IRQ_NONE;
160 
161 	while (1) {
162 		u32 op = readl(pdev_bus_base + PDEV_BUS_OP);
163 
164 		switch (op) {
165 		case PDEV_BUS_OP_REMOVE_DEV:
166 			goldfish_pdev_remove();
167 			ret = IRQ_HANDLED;
168 			break;
169 
170 		case PDEV_BUS_OP_ADD_DEV:
171 			goldfish_new_pdev();
172 			ret = IRQ_HANDLED;
173 			break;
174 
175 		case PDEV_BUS_OP_DONE:
176 		default:
177 			return ret;
178 		}
179 	}
180 }
181 
goldfish_pdev_bus_probe(struct platform_device * pdev)182 static int goldfish_pdev_bus_probe(struct platform_device *pdev)
183 {
184 	int ret;
185 	struct resource *r;
186 
187 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
188 	if (r == NULL)
189 		return -EINVAL;
190 
191 	pdev_bus_addr = r->start;
192 	pdev_bus_len = resource_size(r);
193 
194 	pdev_bus_base = ioremap(pdev_bus_addr, pdev_bus_len);
195 	if (pdev_bus_base == NULL) {
196 		ret = -ENOMEM;
197 		dev_err(&pdev->dev, "unable to map Goldfish MMIO.\n");
198 		goto free_resources;
199 	}
200 
201 	r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
202 	if (r == NULL) {
203 		ret = -ENOENT;
204 		goto free_map;
205 	}
206 
207 	pdev_bus_irq = r->start;
208 
209 	ret = request_irq(pdev_bus_irq, goldfish_pdev_bus_interrupt,
210 				IRQF_SHARED, "goldfish_pdev_bus", pdev);
211 	if (ret) {
212 		dev_err(&pdev->dev, "unable to request Goldfish IRQ\n");
213 		goto free_map;
214 	}
215 
216 	writel(PDEV_BUS_OP_INIT, pdev_bus_base + PDEV_BUS_OP);
217 	return 0;
218 
219 free_map:
220 	iounmap(pdev_bus_base);
221 free_resources:
222 	release_mem_region(pdev_bus_addr, pdev_bus_len);
223 	return ret;
224 }
225 
226 static struct platform_driver goldfish_pdev_bus_driver = {
227 	.probe = goldfish_pdev_bus_probe,
228 	.driver = {
229 		.name = "goldfish_pdev_bus"
230 	}
231 };
232 builtin_platform_driver(goldfish_pdev_bus_driver);
233