/* * platform.c - platform 'pseudo' bus for legacy devices * * Copyright (c) 2002-3 Patrick Mochel * Copyright (c) 2002-3 Open Source Development Labs * * This file is released under the GPLv2 * * Please see Documentation/driver-model/platform.txt for more * information. */ #include #include #include #include #include #include struct device platform_bus = { .bus_id = "platform", }; /** * platform_get_resource - get a resource for a device * @dev: platform device * @type: resource type * @num: resource index */ struct resource * platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num) { int i; for (i = 0; i < dev->num_resources; i++) { struct resource *r = &dev->resource[i]; if ((r->flags & (IORESOURCE_IO|IORESOURCE_MEM| IORESOURCE_IRQ|IORESOURCE_DMA)) == type) if (num-- == 0) return r; } return NULL; } /** * platform_get_irq - get an IRQ for a device * @dev: platform device * @num: IRQ number index */ int platform_get_irq(struct platform_device *dev, unsigned int num) { struct resource *r = platform_get_resource(dev, IORESOURCE_IRQ, num); return r ? r->start : 0; } /** * platform_get_resource_byname - get a resource for a device by name * @dev: platform device * @type: resource type * @name: resource name */ struct resource * platform_get_resource_byname(struct platform_device *dev, unsigned int type, char *name) { int i; for (i = 0; i < dev->num_resources; i++) { struct resource *r = &dev->resource[i]; if ((r->flags & (IORESOURCE_IO|IORESOURCE_MEM| IORESOURCE_IRQ|IORESOURCE_DMA)) == type) if (!strcmp(r->name, name)) return r; } return NULL; } /** * platform_get_irq - get an IRQ for a device * @dev: platform device * @name: IRQ name */ int platform_get_irq_byname(struct platform_device *dev, char *name) { struct resource *r = platform_get_resource_byname(dev, IORESOURCE_IRQ, name); return r ? r->start : 0; } /** * platform_add_devices - add a numbers of platform devices * @devs: array of platform devices to add * @num: number of platform devices in array */ int platform_add_devices(struct platform_device **devs, int num) { int i, ret = 0; for (i = 0; i < num; i++) { ret = platform_device_register(devs[i]); if (ret) { while (--i >= 0) platform_device_unregister(devs[i]); break; } } return ret; } /** * platform_device_register - add a platform-level device * @pdev: platform device we're adding * */ int platform_device_register(struct platform_device * pdev) { int i, ret = 0; if (!pdev) return -EINVAL; if (!pdev->dev.parent) pdev->dev.parent = &platform_bus; pdev->dev.bus = &platform_bus_type; if (pdev->id != -1) snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%u", pdev->name, pdev->id); else strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE); for (i = 0; i < pdev->num_resources; i++) { struct resource *p, *r = &pdev->resource[i]; if (r->name == NULL) r->name = pdev->dev.bus_id; p = r->parent; if (!p) { if (r->flags & IORESOURCE_MEM) p = &iomem_resource; else if (r->flags & IORESOURCE_IO) p = &ioport_resource; } if (p && request_resource(p, r)) { printk(KERN_ERR "%s: failed to claim resource %d\n", pdev->dev.bus_id, i); ret = -EBUSY; goto failed; } } pr_debug("Registering platform device '%s'. Parent at %s\n", pdev->dev.bus_id, pdev->dev.parent->bus_id); ret = device_register(&pdev->dev); if (ret == 0) return ret; failed: while (--i >= 0) if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO)) release_resource(&pdev->resource[i]); return ret; } /** * platform_device_unregister - remove a platform-level device * @pdev: platform device we're removing * * Note that this function will also release all memory- and port-based * resources owned by the device (@dev->resource). */ void platform_device_unregister(struct platform_device * pdev) { int i; if (pdev) { for (i = 0; i < pdev->num_resources; i++) { struct resource *r = &pdev->resource[i]; if (r->flags & (IORESOURCE_MEM|IORESOURCE_IO)) release_resource(r); } device_unregister(&pdev->dev); } } struct platform_object { struct platform_device pdev; struct resource resources[0]; }; static void platform_device_release_simple(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); kfree(container_of(pdev, struct platform_object, pdev)); } /** * platform_device_register_simple * @name: base name of the device we're adding * @id: instance id * @res: set of resources that needs to be allocated for the device * @num: number of resources * * This function creates a simple platform device that requires minimal * resource and memory management. Canned release function freeing * memory allocated for the device allows drivers using such devices * to be unloaded iwithout waiting for the last reference to the device * to be dropped. */ struct platform_device *platform_device_register_simple(char *name, unsigned int id, struct resource *res, unsigned int num) { struct platform_object *pobj; int retval; pobj = kmalloc(sizeof(struct platform_object) + sizeof(struct resource) * num, GFP_KERNEL); if (!pobj) { retval = -ENOMEM; goto error; } memset(pobj, 0, sizeof(*pobj)); pobj->pdev.name = name; pobj->pdev.id = id; pobj->pdev.dev.release = platform_device_release_simple; if (num) { memcpy(pobj->resources, res, sizeof(struct resource) * num); pobj->pdev.resource = pobj->resources; pobj->pdev.num_resources = num; } retval = platform_device_register(&pobj->pdev); if (retval) goto error; return &pobj->pdev; error: kfree(pobj); return ERR_PTR(retval); } /** * platform_match - bind platform device to platform driver. * @dev: device. * @drv: driver. * * Platform device IDs are assumed to be encoded like this: * "", where is a short description of the * type of device, like "pci" or "floppy", and is the * enumerated instance of the device, like '0' or '42'. * Driver IDs are simply "". * So, extract the from the platform_device structure, * and compare it against the name of the driver. Return whether * they match or not. */ static int platform_match(struct device * dev, struct device_driver * drv) { struct platform_device *pdev = container_of(dev, struct platform_device, dev); return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0); } static int platform_suspend(struct device * dev, pm_message_t state) { int ret = 0; if (dev->driver && dev->driver->suspend) { ret = dev->driver->suspend(dev, state, SUSPEND_DISABLE); if (ret == 0) ret = dev->driver->suspend(dev, state, SUSPEND_SAVE_STATE); if (ret == 0) ret = dev->driver->suspend(dev, state, SUSPEND_POWER_DOWN); } return ret; } static int platform_resume(struct device * dev) { int ret = 0; if (dev->driver && dev->driver->resume) { ret = dev->driver->resume(dev, RESUME_POWER_ON); if (ret == 0) ret = dev->driver->resume(dev, RESUME_RESTORE_STATE); if (ret == 0) ret = dev->driver->resume(dev, RESUME_ENABLE); } return ret; } struct bus_type platform_bus_type = { .name = "platform", .match = platform_match, .suspend = platform_suspend, .resume = platform_resume, }; int __init platform_bus_init(void) { device_register(&platform_bus); return bus_register(&platform_bus_type); } #ifndef ARCH_HAS_DMA_GET_REQUIRED_MASK u64 dma_get_required_mask(struct device *dev) { u32 low_totalram = ((max_pfn - 1) << PAGE_SHIFT); u32 high_totalram = ((max_pfn - 1) >> (32 - PAGE_SHIFT)); u64 mask; if (!high_totalram) { /* convert to mask just covering totalram */ low_totalram = (1 << (fls(low_totalram) - 1)); low_totalram += low_totalram - 1; mask = low_totalram; } else { high_totalram = (1 << (fls(high_totalram) - 1)); high_totalram += high_totalram - 1; mask = (((u64)high_totalram) << 32) + 0xffffffff; } return mask & *dev->dma_mask; } EXPORT_SYMBOL_GPL(dma_get_required_mask); #endif EXPORT_SYMBOL_GPL(platform_bus); EXPORT_SYMBOL_GPL(platform_bus_type); EXPORT_SYMBOL_GPL(platform_add_devices); EXPORT_SYMBOL_GPL(platform_device_register); EXPORT_SYMBOL_GPL(platform_device_register_simple); EXPORT_SYMBOL_GPL(platform_device_unregister); EXPORT_SYMBOL_GPL(platform_get_irq); EXPORT_SYMBOL_GPL(platform_get_resource); EXPORT_SYMBOL_GPL(platform_get_irq_byname); EXPORT_SYMBOL_GPL(platform_get_resource_byname);