aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJun Nakajima <jnakajim@gmail.com>2013-01-21 18:48:07 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-01-24 16:49:34 -0500
commite809c22b8fb0282048fc008cfcdd017186090dbc (patch)
treee7ec287e609adc1835d12ef489a8fa9ebdab0ecf
parentc0594ee9ea20388310d2669a7c6122c5305dcf80 (diff)
goldfish: add the goldfish virtual bus
This imports the current Google code and cleans it up slightly to use pr_ and to properly request its resources. Goldfish is an emulator used for Android development. It has a virtual bus where the emulator passes platform device information to the guest which then creates the appropriate devices. This part of the emulation is not architecture specific so should not be hiding in architecture trees as it does in the Google Android tree. The constants it uses do depend on the platform and the platform creates the bus device which then talks to the emulator to ascertain the actual devices present. Signed-off-by: Sheng Yang <sheng@linux.intel.com> Signed-off-by: Yunhong Jiang <yunhong.jiang@intel.com> Signed-off-by: Xiaohui Xin <xiaohui.xin@intel.com> Signed-off-by: Jun Nakajima <jun.nakajima@intel.com> Signed-off-by: Bruce Beare <bruce.j.beare@intel.com> [Moved out of x86, cleaned up headers] Signed-off-by: Alan Cox <alan@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/platform/Makefile1
-rw-r--r--drivers/platform/goldfish/Makefile4
-rw-r--r--drivers/platform/goldfish/pdev_bus.c240
3 files changed, 245 insertions, 0 deletions
diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile
index b17c16ce54ad..8a44a4cd6d1e 100644
--- a/drivers/platform/Makefile
+++ b/drivers/platform/Makefile
@@ -4,3 +4,4 @@
4 4
5obj-$(CONFIG_X86) += x86/ 5obj-$(CONFIG_X86) += x86/
6obj-$(CONFIG_OLPC) += olpc/ 6obj-$(CONFIG_OLPC) += olpc/
7obj-$(CONFIG_GOLDFISH) += goldfish/
diff --git a/drivers/platform/goldfish/Makefile b/drivers/platform/goldfish/Makefile
new file mode 100644
index 000000000000..6c591f6b2707
--- /dev/null
+++ b/drivers/platform/goldfish/Makefile
@@ -0,0 +1,4 @@
1#
2# Makefile for Goldfish platform specific drivers
3#
4obj-$(CONFIG_GOLDFISH) += pdev_bus.o
diff --git a/drivers/platform/goldfish/pdev_bus.c b/drivers/platform/goldfish/pdev_bus.c
new file mode 100644
index 000000000000..92cc4cfafde5
--- /dev/null
+++ b/drivers/platform/goldfish/pdev_bus.c
@@ -0,0 +1,240 @@
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
25#define PDEV_BUS_OP_DONE (0x00)
26#define PDEV_BUS_OP_REMOVE_DEV (0x04)
27#define PDEV_BUS_OP_ADD_DEV (0x08)
28
29#define PDEV_BUS_OP_INIT (0x00)
30
31#define PDEV_BUS_OP (0x00)
32#define PDEV_BUS_GET_NAME (0x04)
33#define PDEV_BUS_NAME_LEN (0x08)
34#define PDEV_BUS_ID (0x0c)
35#define PDEV_BUS_IO_BASE (0x10)
36#define PDEV_BUS_IO_SIZE (0x14)
37#define PDEV_BUS_IRQ (0x18)
38#define PDEV_BUS_IRQ_COUNT (0x1c)
39
40struct pdev_bus_dev {
41 struct list_head list;
42 struct platform_device pdev;
43 struct resource resources[0];
44};
45
46static void goldfish_pdev_worker(struct work_struct *work);
47
48static void __iomem *pdev_bus_base;
49static unsigned long pdev_bus_addr;
50static unsigned long pdev_bus_len;
51static u32 pdev_bus_irq;
52static LIST_HEAD(pdev_bus_new_devices);
53static LIST_HEAD(pdev_bus_registered_devices);
54static LIST_HEAD(pdev_bus_removed_devices);
55static DECLARE_WORK(pdev_bus_worker, goldfish_pdev_worker);
56
57
58static 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 pr_err("goldfish_pdev_worker failed to register device, %s\n",
73 pos->pdev.name);
74 list_add_tail(&pos->list, &pdev_bus_registered_devices);
75 }
76}
77
78static void goldfish_pdev_remove(void)
79{
80 struct pdev_bus_dev *pos, *n;
81 u32 base;
82
83 base = readl(pdev_bus_base + PDEV_BUS_IO_BASE);
84
85 list_for_each_entry_safe(pos, n, &pdev_bus_new_devices, list) {
86 if (pos->resources[0].start == base) {
87 list_del(&pos->list);
88 kfree(pos);
89 return;
90 }
91 }
92 list_for_each_entry_safe(pos, n, &pdev_bus_registered_devices, list) {
93 if (pos->resources[0].start == base) {
94 list_del(&pos->list);
95 list_add_tail(&pos->list, &pdev_bus_removed_devices);
96 schedule_work(&pdev_bus_worker);
97 return;
98 }
99 };
100 pr_err("goldfish_pdev_remove could not find device at %x\n", base);
101}
102
103static int goldfish_new_pdev(void)
104{
105 struct pdev_bus_dev *dev;
106 u32 name_len;
107 u32 irq = -1, irq_count;
108 int resource_count = 2;
109 u32 base;
110 char *name;
111
112 base = readl(pdev_bus_base + PDEV_BUS_IO_BASE);
113
114 irq_count = readl(pdev_bus_base + PDEV_BUS_IRQ_COUNT);
115 name_len = readl(pdev_bus_base + PDEV_BUS_NAME_LEN);
116 if (irq_count)
117 resource_count++;
118
119 dev = kzalloc(sizeof(*dev) +
120 sizeof(struct resource) * resource_count +
121 name_len + 1 + sizeof(*dev->pdev.dev.dma_mask), GFP_ATOMIC);
122 if (dev == NULL)
123 return -ENOMEM;
124
125 dev->pdev.num_resources = resource_count;
126 dev->pdev.resource = (struct resource *)(dev + 1);
127 dev->pdev.name = name = (char *)(dev->pdev.resource + resource_count);
128 dev->pdev.dev.coherent_dma_mask = ~0;
129 dev->pdev.dev.dma_mask = (void *)(dev->pdev.name + name_len + 1);
130 *dev->pdev.dev.dma_mask = ~0;
131
132 writel((unsigned long)name, pdev_bus_base + PDEV_BUS_GET_NAME);
133 name[name_len] = '\0';
134 dev->pdev.id = readl(pdev_bus_base + PDEV_BUS_ID);
135 dev->pdev.resource[0].start = base;
136 dev->pdev.resource[0].end = base +
137 readl(pdev_bus_base + PDEV_BUS_IO_SIZE) - 1;
138 dev->pdev.resource[0].flags = IORESOURCE_MEM;
139 if (irq_count) {
140 irq = readl(pdev_bus_base + PDEV_BUS_IRQ);
141 dev->pdev.resource[1].start = irq;
142 dev->pdev.resource[1].end = irq + irq_count - 1;
143 dev->pdev.resource[1].flags = IORESOURCE_IRQ;
144 }
145
146 pr_debug("goldfish_new_pdev %s at %x irq %d\n", name, base, irq);
147 list_add_tail(&dev->list, &pdev_bus_new_devices);
148 schedule_work(&pdev_bus_worker);
149
150 return 0;
151}
152
153static irqreturn_t goldfish_pdev_bus_interrupt(int irq, void *dev_id)
154{
155 irqreturn_t ret = IRQ_NONE;
156 while (1) {
157 u32 op = readl(pdev_bus_base + PDEV_BUS_OP);
158 switch (op) {
159 case PDEV_BUS_OP_DONE:
160 return IRQ_NONE;
161
162 case PDEV_BUS_OP_REMOVE_DEV:
163 goldfish_pdev_remove();
164 break;
165
166 case PDEV_BUS_OP_ADD_DEV:
167 goldfish_new_pdev();
168 break;
169 }
170 ret = IRQ_HANDLED;
171 }
172 return ret;
173}
174
175static int goldfish_pdev_bus_probe(struct platform_device *pdev)
176{
177 int ret;
178 struct resource *r;
179
180 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
181 if (r == NULL)
182 return -EINVAL;
183
184 pdev_bus_addr = r->start;
185 pdev_bus_len = resource_size(r);
186
187 if (request_mem_region(pdev_bus_addr, pdev_bus_len, "goldfish")) {
188 dev_err(&pdev->dev, "unable to reserve Goldfish MMIO.\n");
189 return -EBUSY;
190 }
191
192 pdev_bus_base = ioremap(pdev_bus_addr, pdev_bus_len);
193 if (pdev_bus_base == NULL) {
194 ret = -ENOMEM;
195 dev_err(&pdev->dev, "unable to map Goldfish MMIO.\n");
196 goto free_resources;
197 }
198
199 r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
200 if (r == NULL) {
201 ret = -ENOENT;
202 goto free_map;
203 }
204
205 pdev_bus_irq = r->start;
206
207 ret = request_irq(pdev_bus_irq, goldfish_pdev_bus_interrupt,
208 IRQF_SHARED, "goldfish_pdev_bus", pdev);
209 if (ret) {
210 dev_err(&pdev->dev, "unable to request Goldfish IRQ\n");
211 goto free_map;
212 }
213
214 writel(PDEV_BUS_OP_INIT, pdev_bus_base + PDEV_BUS_OP);
215 return 0;
216
217free_map:
218 iounmap(pdev_bus_base);
219free_resources:
220 release_mem_region(pdev_bus_addr, pdev_bus_len);
221 return ret;
222}
223
224static int goldfish_pdev_bus_remove(struct platform_device *pdev)
225{
226 iounmap(pdev_bus_base);
227 free_irq(pdev_bus_irq, pdev);
228 release_mem_region(pdev_bus_addr, pdev_bus_len);
229 return 0;
230}
231
232static struct platform_driver goldfish_pdev_bus_driver = {
233 .probe = goldfish_pdev_bus_probe,
234 .remove = goldfish_pdev_bus_remove,
235 .driver = {
236 .name = "goldfish_pdev_bus"
237 }
238};
239
240module_platform_driver(goldfish_pdev_bus_driver);