11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * platform.c - platform 'pseudo' bus for legacy devices 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (c) 2002-3 Patrick Mochel 51da177e4SLinus Torvalds * Copyright (c) 2002-3 Open Source Development Labs 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * This file is released under the GPLv2 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds * Please see Documentation/driver-model/platform.txt for more 101da177e4SLinus Torvalds * information. 111da177e4SLinus Torvalds */ 121da177e4SLinus Torvalds 13*d052d1beSRussell King #include <linux/platform_device.h> 141da177e4SLinus Torvalds #include <linux/module.h> 151da177e4SLinus Torvalds #include <linux/init.h> 161da177e4SLinus Torvalds #include <linux/dma-mapping.h> 171da177e4SLinus Torvalds #include <linux/bootmem.h> 181da177e4SLinus Torvalds #include <linux/err.h> 191da177e4SLinus Torvalds 20a1bdc7aaSBen Dooks #include "base.h" 21a1bdc7aaSBen Dooks 221da177e4SLinus Torvalds struct device platform_bus = { 231da177e4SLinus Torvalds .bus_id = "platform", 241da177e4SLinus Torvalds }; 251da177e4SLinus Torvalds 261da177e4SLinus Torvalds /** 271da177e4SLinus Torvalds * platform_get_resource - get a resource for a device 281da177e4SLinus Torvalds * @dev: platform device 291da177e4SLinus Torvalds * @type: resource type 301da177e4SLinus Torvalds * @num: resource index 311da177e4SLinus Torvalds */ 321da177e4SLinus Torvalds struct resource * 331da177e4SLinus Torvalds platform_get_resource(struct platform_device *dev, unsigned int type, 341da177e4SLinus Torvalds unsigned int num) 351da177e4SLinus Torvalds { 361da177e4SLinus Torvalds int i; 371da177e4SLinus Torvalds 381da177e4SLinus Torvalds for (i = 0; i < dev->num_resources; i++) { 391da177e4SLinus Torvalds struct resource *r = &dev->resource[i]; 401da177e4SLinus Torvalds 411da177e4SLinus Torvalds if ((r->flags & (IORESOURCE_IO|IORESOURCE_MEM| 421da177e4SLinus Torvalds IORESOURCE_IRQ|IORESOURCE_DMA)) 431da177e4SLinus Torvalds == type) 441da177e4SLinus Torvalds if (num-- == 0) 451da177e4SLinus Torvalds return r; 461da177e4SLinus Torvalds } 471da177e4SLinus Torvalds return NULL; 481da177e4SLinus Torvalds } 491da177e4SLinus Torvalds 501da177e4SLinus Torvalds /** 511da177e4SLinus Torvalds * platform_get_irq - get an IRQ for a device 521da177e4SLinus Torvalds * @dev: platform device 531da177e4SLinus Torvalds * @num: IRQ number index 541da177e4SLinus Torvalds */ 551da177e4SLinus Torvalds int platform_get_irq(struct platform_device *dev, unsigned int num) 561da177e4SLinus Torvalds { 571da177e4SLinus Torvalds struct resource *r = platform_get_resource(dev, IORESOURCE_IRQ, num); 581da177e4SLinus Torvalds 591da177e4SLinus Torvalds return r ? r->start : 0; 601da177e4SLinus Torvalds } 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds /** 631da177e4SLinus Torvalds * platform_get_resource_byname - get a resource for a device by name 641da177e4SLinus Torvalds * @dev: platform device 651da177e4SLinus Torvalds * @type: resource type 661da177e4SLinus Torvalds * @name: resource name 671da177e4SLinus Torvalds */ 681da177e4SLinus Torvalds struct resource * 691da177e4SLinus Torvalds platform_get_resource_byname(struct platform_device *dev, unsigned int type, 701da177e4SLinus Torvalds char *name) 711da177e4SLinus Torvalds { 721da177e4SLinus Torvalds int i; 731da177e4SLinus Torvalds 741da177e4SLinus Torvalds for (i = 0; i < dev->num_resources; i++) { 751da177e4SLinus Torvalds struct resource *r = &dev->resource[i]; 761da177e4SLinus Torvalds 771da177e4SLinus Torvalds if ((r->flags & (IORESOURCE_IO|IORESOURCE_MEM| 781da177e4SLinus Torvalds IORESOURCE_IRQ|IORESOURCE_DMA)) == type) 791da177e4SLinus Torvalds if (!strcmp(r->name, name)) 801da177e4SLinus Torvalds return r; 811da177e4SLinus Torvalds } 821da177e4SLinus Torvalds return NULL; 831da177e4SLinus Torvalds } 841da177e4SLinus Torvalds 851da177e4SLinus Torvalds /** 861da177e4SLinus Torvalds * platform_get_irq - get an IRQ for a device 871da177e4SLinus Torvalds * @dev: platform device 881da177e4SLinus Torvalds * @name: IRQ name 891da177e4SLinus Torvalds */ 901da177e4SLinus Torvalds int platform_get_irq_byname(struct platform_device *dev, char *name) 911da177e4SLinus Torvalds { 921da177e4SLinus Torvalds struct resource *r = platform_get_resource_byname(dev, IORESOURCE_IRQ, name); 931da177e4SLinus Torvalds 941da177e4SLinus Torvalds return r ? r->start : 0; 951da177e4SLinus Torvalds } 961da177e4SLinus Torvalds 971da177e4SLinus Torvalds /** 981da177e4SLinus Torvalds * platform_add_devices - add a numbers of platform devices 991da177e4SLinus Torvalds * @devs: array of platform devices to add 1001da177e4SLinus Torvalds * @num: number of platform devices in array 1011da177e4SLinus Torvalds */ 1021da177e4SLinus Torvalds int platform_add_devices(struct platform_device **devs, int num) 1031da177e4SLinus Torvalds { 1041da177e4SLinus Torvalds int i, ret = 0; 1051da177e4SLinus Torvalds 1061da177e4SLinus Torvalds for (i = 0; i < num; i++) { 1071da177e4SLinus Torvalds ret = platform_device_register(devs[i]); 1081da177e4SLinus Torvalds if (ret) { 1091da177e4SLinus Torvalds while (--i >= 0) 1101da177e4SLinus Torvalds platform_device_unregister(devs[i]); 1111da177e4SLinus Torvalds break; 1121da177e4SLinus Torvalds } 1131da177e4SLinus Torvalds } 1141da177e4SLinus Torvalds 1151da177e4SLinus Torvalds return ret; 1161da177e4SLinus Torvalds } 1171da177e4SLinus Torvalds 1181da177e4SLinus Torvalds /** 1191da177e4SLinus Torvalds * platform_device_register - add a platform-level device 12067be2dd1SMartin Waitz * @pdev: platform device we're adding 1211da177e4SLinus Torvalds * 1221da177e4SLinus Torvalds */ 1231da177e4SLinus Torvalds int platform_device_register(struct platform_device * pdev) 1241da177e4SLinus Torvalds { 1251da177e4SLinus Torvalds int i, ret = 0; 1261da177e4SLinus Torvalds 1271da177e4SLinus Torvalds if (!pdev) 1281da177e4SLinus Torvalds return -EINVAL; 1291da177e4SLinus Torvalds 1301da177e4SLinus Torvalds if (!pdev->dev.parent) 1311da177e4SLinus Torvalds pdev->dev.parent = &platform_bus; 1321da177e4SLinus Torvalds 1331da177e4SLinus Torvalds pdev->dev.bus = &platform_bus_type; 1341da177e4SLinus Torvalds 1351da177e4SLinus Torvalds if (pdev->id != -1) 1361da177e4SLinus Torvalds snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%u", pdev->name, pdev->id); 1371da177e4SLinus Torvalds else 1381da177e4SLinus Torvalds strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE); 1391da177e4SLinus Torvalds 1401da177e4SLinus Torvalds for (i = 0; i < pdev->num_resources; i++) { 1411da177e4SLinus Torvalds struct resource *p, *r = &pdev->resource[i]; 1421da177e4SLinus Torvalds 1431da177e4SLinus Torvalds if (r->name == NULL) 1441da177e4SLinus Torvalds r->name = pdev->dev.bus_id; 1451da177e4SLinus Torvalds 1461da177e4SLinus Torvalds p = r->parent; 1471da177e4SLinus Torvalds if (!p) { 1481da177e4SLinus Torvalds if (r->flags & IORESOURCE_MEM) 1491da177e4SLinus Torvalds p = &iomem_resource; 1501da177e4SLinus Torvalds else if (r->flags & IORESOURCE_IO) 1511da177e4SLinus Torvalds p = &ioport_resource; 1521da177e4SLinus Torvalds } 1531da177e4SLinus Torvalds 1541da177e4SLinus Torvalds if (p && request_resource(p, r)) { 1551da177e4SLinus Torvalds printk(KERN_ERR 1561da177e4SLinus Torvalds "%s: failed to claim resource %d\n", 1571da177e4SLinus Torvalds pdev->dev.bus_id, i); 1581da177e4SLinus Torvalds ret = -EBUSY; 1591da177e4SLinus Torvalds goto failed; 1601da177e4SLinus Torvalds } 1611da177e4SLinus Torvalds } 1621da177e4SLinus Torvalds 1631da177e4SLinus Torvalds pr_debug("Registering platform device '%s'. Parent at %s\n", 1641da177e4SLinus Torvalds pdev->dev.bus_id, pdev->dev.parent->bus_id); 1651da177e4SLinus Torvalds 1661da177e4SLinus Torvalds ret = device_register(&pdev->dev); 1671da177e4SLinus Torvalds if (ret == 0) 1681da177e4SLinus Torvalds return ret; 1691da177e4SLinus Torvalds 1701da177e4SLinus Torvalds failed: 1711da177e4SLinus Torvalds while (--i >= 0) 1721da177e4SLinus Torvalds if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO)) 1731da177e4SLinus Torvalds release_resource(&pdev->resource[i]); 1741da177e4SLinus Torvalds return ret; 1751da177e4SLinus Torvalds } 1761da177e4SLinus Torvalds 1771da177e4SLinus Torvalds /** 1781da177e4SLinus Torvalds * platform_device_unregister - remove a platform-level device 17967be2dd1SMartin Waitz * @pdev: platform device we're removing 1801da177e4SLinus Torvalds * 1811da177e4SLinus Torvalds * Note that this function will also release all memory- and port-based 1821da177e4SLinus Torvalds * resources owned by the device (@dev->resource). 1831da177e4SLinus Torvalds */ 1841da177e4SLinus Torvalds void platform_device_unregister(struct platform_device * pdev) 1851da177e4SLinus Torvalds { 1861da177e4SLinus Torvalds int i; 1871da177e4SLinus Torvalds 1881da177e4SLinus Torvalds if (pdev) { 1891da177e4SLinus Torvalds for (i = 0; i < pdev->num_resources; i++) { 1901da177e4SLinus Torvalds struct resource *r = &pdev->resource[i]; 1911da177e4SLinus Torvalds if (r->flags & (IORESOURCE_MEM|IORESOURCE_IO)) 1921da177e4SLinus Torvalds release_resource(r); 1931da177e4SLinus Torvalds } 1941da177e4SLinus Torvalds 1951da177e4SLinus Torvalds device_unregister(&pdev->dev); 1961da177e4SLinus Torvalds } 1971da177e4SLinus Torvalds } 1981da177e4SLinus Torvalds 1991da177e4SLinus Torvalds struct platform_object { 2001da177e4SLinus Torvalds struct platform_device pdev; 2011da177e4SLinus Torvalds struct resource resources[0]; 2021da177e4SLinus Torvalds }; 2031da177e4SLinus Torvalds 2041da177e4SLinus Torvalds static void platform_device_release_simple(struct device *dev) 2051da177e4SLinus Torvalds { 2061da177e4SLinus Torvalds struct platform_device *pdev = to_platform_device(dev); 2071da177e4SLinus Torvalds 2081da177e4SLinus Torvalds kfree(container_of(pdev, struct platform_object, pdev)); 2091da177e4SLinus Torvalds } 2101da177e4SLinus Torvalds 2111da177e4SLinus Torvalds /** 2121da177e4SLinus Torvalds * platform_device_register_simple 2131da177e4SLinus Torvalds * @name: base name of the device we're adding 2141da177e4SLinus Torvalds * @id: instance id 2151da177e4SLinus Torvalds * @res: set of resources that needs to be allocated for the device 2161da177e4SLinus Torvalds * @num: number of resources 2171da177e4SLinus Torvalds * 2181da177e4SLinus Torvalds * This function creates a simple platform device that requires minimal 2191da177e4SLinus Torvalds * resource and memory management. Canned release function freeing 2201da177e4SLinus Torvalds * memory allocated for the device allows drivers using such devices 2211da177e4SLinus Torvalds * to be unloaded iwithout waiting for the last reference to the device 2221da177e4SLinus Torvalds * to be dropped. 2231da177e4SLinus Torvalds */ 2241da177e4SLinus Torvalds struct platform_device *platform_device_register_simple(char *name, unsigned int id, 2251da177e4SLinus Torvalds struct resource *res, unsigned int num) 2261da177e4SLinus Torvalds { 2271da177e4SLinus Torvalds struct platform_object *pobj; 2281da177e4SLinus Torvalds int retval; 2291da177e4SLinus Torvalds 2304aed0644SJiri Slaby pobj = kzalloc(sizeof(*pobj) + sizeof(struct resource) * num, GFP_KERNEL); 2311da177e4SLinus Torvalds if (!pobj) { 2321da177e4SLinus Torvalds retval = -ENOMEM; 2331da177e4SLinus Torvalds goto error; 2341da177e4SLinus Torvalds } 2351da177e4SLinus Torvalds 2361da177e4SLinus Torvalds pobj->pdev.name = name; 2371da177e4SLinus Torvalds pobj->pdev.id = id; 2381da177e4SLinus Torvalds pobj->pdev.dev.release = platform_device_release_simple; 2391da177e4SLinus Torvalds 2401da177e4SLinus Torvalds if (num) { 2411da177e4SLinus Torvalds memcpy(pobj->resources, res, sizeof(struct resource) * num); 2421da177e4SLinus Torvalds pobj->pdev.resource = pobj->resources; 2431da177e4SLinus Torvalds pobj->pdev.num_resources = num; 2441da177e4SLinus Torvalds } 2451da177e4SLinus Torvalds 2461da177e4SLinus Torvalds retval = platform_device_register(&pobj->pdev); 2471da177e4SLinus Torvalds if (retval) 2481da177e4SLinus Torvalds goto error; 2491da177e4SLinus Torvalds 2501da177e4SLinus Torvalds return &pobj->pdev; 2511da177e4SLinus Torvalds 2521da177e4SLinus Torvalds error: 2531da177e4SLinus Torvalds kfree(pobj); 2541da177e4SLinus Torvalds return ERR_PTR(retval); 2551da177e4SLinus Torvalds } 2561da177e4SLinus Torvalds 2571da177e4SLinus Torvalds 2581da177e4SLinus Torvalds /** 2591da177e4SLinus Torvalds * platform_match - bind platform device to platform driver. 2601da177e4SLinus Torvalds * @dev: device. 2611da177e4SLinus Torvalds * @drv: driver. 2621da177e4SLinus Torvalds * 2631da177e4SLinus Torvalds * Platform device IDs are assumed to be encoded like this: 2641da177e4SLinus Torvalds * "<name><instance>", where <name> is a short description of the 2651da177e4SLinus Torvalds * type of device, like "pci" or "floppy", and <instance> is the 2661da177e4SLinus Torvalds * enumerated instance of the device, like '0' or '42'. 2671da177e4SLinus Torvalds * Driver IDs are simply "<name>". 2681da177e4SLinus Torvalds * So, extract the <name> from the platform_device structure, 2691da177e4SLinus Torvalds * and compare it against the name of the driver. Return whether 2701da177e4SLinus Torvalds * they match or not. 2711da177e4SLinus Torvalds */ 2721da177e4SLinus Torvalds 2731da177e4SLinus Torvalds static int platform_match(struct device * dev, struct device_driver * drv) 2741da177e4SLinus Torvalds { 2751da177e4SLinus Torvalds struct platform_device *pdev = container_of(dev, struct platform_device, dev); 2761da177e4SLinus Torvalds 2771da177e4SLinus Torvalds return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0); 2781da177e4SLinus Torvalds } 2791da177e4SLinus Torvalds 2801da177e4SLinus Torvalds static int platform_suspend(struct device * dev, pm_message_t state) 2811da177e4SLinus Torvalds { 2821da177e4SLinus Torvalds int ret = 0; 2831da177e4SLinus Torvalds 2849480e307SRussell King if (dev->driver && dev->driver->suspend) 2859480e307SRussell King ret = dev->driver->suspend(dev, state); 2869480e307SRussell King 2871da177e4SLinus Torvalds return ret; 2881da177e4SLinus Torvalds } 2891da177e4SLinus Torvalds 2901da177e4SLinus Torvalds static int platform_resume(struct device * dev) 2911da177e4SLinus Torvalds { 2921da177e4SLinus Torvalds int ret = 0; 2931da177e4SLinus Torvalds 2949480e307SRussell King if (dev->driver && dev->driver->resume) 2959480e307SRussell King ret = dev->driver->resume(dev); 2969480e307SRussell King 2971da177e4SLinus Torvalds return ret; 2981da177e4SLinus Torvalds } 2991da177e4SLinus Torvalds 3001da177e4SLinus Torvalds struct bus_type platform_bus_type = { 3011da177e4SLinus Torvalds .name = "platform", 3021da177e4SLinus Torvalds .match = platform_match, 3031da177e4SLinus Torvalds .suspend = platform_suspend, 3041da177e4SLinus Torvalds .resume = platform_resume, 3051da177e4SLinus Torvalds }; 3061da177e4SLinus Torvalds 3071da177e4SLinus Torvalds int __init platform_bus_init(void) 3081da177e4SLinus Torvalds { 3091da177e4SLinus Torvalds device_register(&platform_bus); 3101da177e4SLinus Torvalds return bus_register(&platform_bus_type); 3111da177e4SLinus Torvalds } 3121da177e4SLinus Torvalds 3131da177e4SLinus Torvalds #ifndef ARCH_HAS_DMA_GET_REQUIRED_MASK 3141da177e4SLinus Torvalds u64 dma_get_required_mask(struct device *dev) 3151da177e4SLinus Torvalds { 3161da177e4SLinus Torvalds u32 low_totalram = ((max_pfn - 1) << PAGE_SHIFT); 3171da177e4SLinus Torvalds u32 high_totalram = ((max_pfn - 1) >> (32 - PAGE_SHIFT)); 3181da177e4SLinus Torvalds u64 mask; 3191da177e4SLinus Torvalds 3201da177e4SLinus Torvalds if (!high_totalram) { 3211da177e4SLinus Torvalds /* convert to mask just covering totalram */ 3221da177e4SLinus Torvalds low_totalram = (1 << (fls(low_totalram) - 1)); 3231da177e4SLinus Torvalds low_totalram += low_totalram - 1; 3241da177e4SLinus Torvalds mask = low_totalram; 3251da177e4SLinus Torvalds } else { 3261da177e4SLinus Torvalds high_totalram = (1 << (fls(high_totalram) - 1)); 3271da177e4SLinus Torvalds high_totalram += high_totalram - 1; 3281da177e4SLinus Torvalds mask = (((u64)high_totalram) << 32) + 0xffffffff; 3291da177e4SLinus Torvalds } 3301da177e4SLinus Torvalds return mask & *dev->dma_mask; 3311da177e4SLinus Torvalds } 3321da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(dma_get_required_mask); 3331da177e4SLinus Torvalds #endif 3341da177e4SLinus Torvalds 3351da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(platform_bus); 3361da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(platform_bus_type); 33746ea0d6cSRobert Schwebel EXPORT_SYMBOL_GPL(platform_add_devices); 3381da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(platform_device_register); 3391da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(platform_device_register_simple); 3401da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(platform_device_unregister); 3411da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(platform_get_irq); 3421da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(platform_get_resource); 3431da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(platform_get_irq_byname); 3441da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(platform_get_resource_byname); 345