at v3.12 240 lines 6.3 kB view raw
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);