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#define PDEV_BUS_GET_NAME_HIGH (0x20) 40 41struct pdev_bus_dev { 42 struct list_head list; 43 struct platform_device pdev; 44 struct resource resources[0]; 45}; 46 47static void goldfish_pdev_worker(struct work_struct *work); 48 49static void __iomem *pdev_bus_base; 50static unsigned long pdev_bus_addr; 51static unsigned long pdev_bus_len; 52static u32 pdev_bus_irq; 53static LIST_HEAD(pdev_bus_new_devices); 54static LIST_HEAD(pdev_bus_registered_devices); 55static LIST_HEAD(pdev_bus_removed_devices); 56static DECLARE_WORK(pdev_bus_worker, goldfish_pdev_worker); 57 58 59static void goldfish_pdev_worker(struct work_struct *work) 60{ 61 int ret; 62 struct pdev_bus_dev *pos, *n; 63 64 list_for_each_entry_safe(pos, n, &pdev_bus_removed_devices, list) { 65 list_del(&pos->list); 66 platform_device_unregister(&pos->pdev); 67 kfree(pos); 68 } 69 list_for_each_entry_safe(pos, n, &pdev_bus_new_devices, list) { 70 list_del(&pos->list); 71 ret = platform_device_register(&pos->pdev); 72 if (ret) 73 pr_err("goldfish_pdev_worker failed to register device, %s\n", 74 pos->pdev.name); 75 list_add_tail(&pos->list, &pdev_bus_registered_devices); 76 } 77} 78 79static void goldfish_pdev_remove(void) 80{ 81 struct pdev_bus_dev *pos, *n; 82 u32 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 pr_err("goldfish_pdev_remove could not find device at %x\n", base); 102} 103 104static int goldfish_new_pdev(void) 105{ 106 struct pdev_bus_dev *dev; 107 u32 name_len; 108 u32 irq = -1, irq_count; 109 int resource_count = 2; 110 u32 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) + 121 sizeof(struct resource) * resource_count + 122 name_len + 1 + sizeof(*dev->pdev.dev.dma_mask), 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 dev->pdev.dev.dma_mask = (void *)(dev->pdev.name + name_len + 1); 131 *dev->pdev.dev.dma_mask = ~0; 132 133#ifdef CONFIG_64BIT 134 writel((u32)((u64)name>>32), pdev_bus_base + PDEV_BUS_GET_NAME_HIGH); 135#endif 136 writel((u32)(unsigned long)name, pdev_bus_base + PDEV_BUS_GET_NAME); 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 157static irqreturn_t goldfish_pdev_bus_interrupt(int irq, void *dev_id) 158{ 159 irqreturn_t ret = IRQ_NONE; 160 while (1) { 161 u32 op = readl(pdev_bus_base + PDEV_BUS_OP); 162 switch (op) { 163 case PDEV_BUS_OP_DONE: 164 return IRQ_NONE; 165 166 case PDEV_BUS_OP_REMOVE_DEV: 167 goldfish_pdev_remove(); 168 break; 169 170 case PDEV_BUS_OP_ADD_DEV: 171 goldfish_new_pdev(); 172 break; 173 } 174 ret = IRQ_HANDLED; 175 } 176 return ret; 177} 178 179static int goldfish_pdev_bus_probe(struct platform_device *pdev) 180{ 181 int ret; 182 struct resource *r; 183 184 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 185 if (r == NULL) 186 return -EINVAL; 187 188 pdev_bus_addr = r->start; 189 pdev_bus_len = resource_size(r); 190 191 pdev_bus_base = ioremap(pdev_bus_addr, pdev_bus_len); 192 if (pdev_bus_base == NULL) { 193 ret = -ENOMEM; 194 dev_err(&pdev->dev, "unable to map Goldfish MMIO.\n"); 195 goto free_resources; 196 } 197 198 r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 199 if (r == NULL) { 200 ret = -ENOENT; 201 goto free_map; 202 } 203 204 pdev_bus_irq = r->start; 205 206 ret = request_irq(pdev_bus_irq, goldfish_pdev_bus_interrupt, 207 IRQF_SHARED, "goldfish_pdev_bus", pdev); 208 if (ret) { 209 dev_err(&pdev->dev, "unable to request Goldfish IRQ\n"); 210 goto free_map; 211 } 212 213 writel(PDEV_BUS_OP_INIT, pdev_bus_base + PDEV_BUS_OP); 214 return 0; 215 216free_map: 217 iounmap(pdev_bus_base); 218free_resources: 219 release_mem_region(pdev_bus_addr, pdev_bus_len); 220 return ret; 221} 222 223static int goldfish_pdev_bus_remove(struct platform_device *pdev) 224{ 225 iounmap(pdev_bus_base); 226 free_irq(pdev_bus_irq, pdev); 227 release_mem_region(pdev_bus_addr, pdev_bus_len); 228 return 0; 229} 230 231static struct platform_driver goldfish_pdev_bus_driver = { 232 .probe = goldfish_pdev_bus_probe, 233 .remove = goldfish_pdev_bus_remove, 234 .driver = { 235 .name = "goldfish_pdev_bus" 236 } 237}; 238 239module_platform_driver(goldfish_pdev_bus_driver); 240