From 3d8e5084980a5f9ca9289c88747a8ceb2fb84857 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 6 Feb 2004 14:38:55 +1100 Subject: [PATCH] ppc32: Update macio_asic, add some resource management --- drivers/macintosh/macio_asic.c | 281 +++++++++++++++++++++++++++++---- include/asm-ppc/macio.h | 71 ++++++++- 2 files changed, 318 insertions(+), 34 deletions(-) diff --git a/drivers/macintosh/macio_asic.c b/drivers/macintosh/macio_asic.c index c8d7d791aaec5..a35d44c77f30e 100644 --- a/drivers/macintosh/macio_asic.c +++ b/drivers/macintosh/macio_asic.c @@ -23,10 +23,13 @@ #include #include +#define DEBUG + +#define MAX_NODE_NAME_SIZE (BUS_ID_SIZE - 12) + static struct macio_chip *macio_on_hold; -static int -macio_bus_match(struct device *dev, struct device_driver *drv) +static int macio_bus_match(struct device *dev, struct device_driver *drv) { struct macio_dev * macio_dev = to_macio_device(dev); struct macio_driver * macio_drv = to_macio_driver(drv); @@ -85,41 +88,42 @@ static int macio_device_probe(struct device *dev) static int macio_device_remove(struct device *dev) { struct macio_dev * macio_dev = to_macio_device(dev); - struct macio_driver * drv = to_macio_driver(macio_dev->ofdev.dev.driver); + struct macio_driver * drv = to_macio_driver(dev->driver); - if (drv && drv->remove) + if (dev->driver && drv->remove) drv->remove(macio_dev); macio_dev_put(macio_dev); return 0; } +static void macio_device_shutdown(struct device *dev) +{ + struct macio_dev * macio_dev = to_macio_device(dev); + struct macio_driver * drv = to_macio_driver(dev->driver); + + if (dev->driver && drv->shutdown) + drv->shutdown(macio_dev); +} + static int macio_device_suspend(struct device *dev, u32 state) { struct macio_dev * macio_dev = to_macio_device(dev); - struct macio_driver * drv; - int error = 0; + struct macio_driver * drv = to_macio_driver(dev->driver); - if (macio_dev->ofdev.dev.driver == NULL) - return 0; - drv = to_macio_driver(macio_dev->ofdev.dev.driver); - if (drv->suspend) - error = drv->suspend(macio_dev, state); - return error; + if (dev->driver && drv->suspend) + return drv->suspend(macio_dev, state); + return 0; } static int macio_device_resume(struct device * dev) { struct macio_dev * macio_dev = to_macio_device(dev); - struct macio_driver * drv; - int error = 0; + struct macio_driver * drv = to_macio_driver(dev->driver); - if (macio_dev->ofdev.dev.driver == NULL) - return 0; - drv = to_macio_driver(macio_dev->ofdev.dev.driver); - if (drv->resume) - error = drv->resume(macio_dev); - return error; + if (dev->driver && drv->resume) + return drv->resume(macio_dev); + return 0; } struct bus_type macio_bus_type = { @@ -129,8 +133,7 @@ struct bus_type macio_bus_type = { .resume = macio_device_resume, }; -static int __init -macio_bus_driver_init(void) +static int __init macio_bus_driver_init(void) { return bus_register(&macio_bus_type); } @@ -154,6 +157,58 @@ static void macio_release_dev(struct device *dev) kfree(mdev); } +/** + * macio_resource_quirks - tweak or skip some resources for a device + * @np: pointer to the device node + * @res: resulting resource + * @index: index of resource in node + * + * If this routine returns non-null, then the resource is completely + * skipped. + */ +static int macio_resource_quirks(struct device_node *np, struct resource *res, int index) +{ + if (res->flags & IORESOURCE_MEM) { + /* Grand Central has too large resource 0 on some machines */ + if (index == 0 && !strcmp(np->name, "gc")) { + np->addrs[0].size = 0x20000; + res->end = res->start + 0x1ffff; + } + /* Airport has bogus resource 2 */ + if (index >= 2 && !strcmp(np->name, "radio")) + return 1; + /* DBDMAs may have bogus sizes */ + if ((res->start & 0x0001f000) == 0x00008000) { + np->addrs[index].size = 0x100; + res->end = res->start + 0xff; + } + /* ESCC parent eats child resources. We could have added a level of hierarchy, + * but I don't really feel the need for it */ + if (!strcmp(np->name, "escc")) + return 1; + /* ESCC has bogus resources >= 3 */ + if (index >= 3 && !(strcmp(np->name, "ch-a") && strcmp(np->name, "ch-b"))) + return 1; + /* Media bay has too many resources, keep only first one */ + if (index > 0 && !strcmp(np->name, "media-bay")) + return 1; + /* Some older IDE resources have bogus sizes */ + if (!(strcmp(np->name, "IDE") && strcmp(np->name, "ATA") && + strcmp(np->type, "ide") && strcmp(np->type, "ata"))) { + if (index == 0 && np->addrs[0].size > 0x1000) { + np->addrs[0].size = 0x1000; + res->end = res->start + 0xfff; + } + if (index == 1 && np->addrs[1].size > 0x100) { + np->addrs[1].size = 0x100; + res->end = res->start + 0xff; + } + } + } + return 0; +} + + /** * macio_add_one_device - Add one device from OF node to the device tree * @chip: pointer to the macio_chip holding the device @@ -164,9 +219,11 @@ static void macio_release_dev(struct device *dev) * be exposed to the bay driver some way... */ static struct macio_dev * macio_add_one_device(struct macio_chip *chip, struct device *parent, - struct device_node *np, struct macio_dev *in_bay) + struct device_node *np, struct macio_dev *in_bay, + struct resource *parent_res) { struct macio_dev *dev; + int i, j; u32 *reg; if (np == NULL) @@ -186,22 +243,76 @@ static struct macio_dev * macio_add_one_device(struct macio_chip *chip, struct d dev->ofdev.dev.bus = &macio_bus_type; dev->ofdev.dev.release = macio_release_dev; +#ifdef DEBUG + printk("preparing mdev @%p, ofdev @%p, dev @%p, kobj @%p\n", + dev, &dev->ofdev, &dev->ofdev.dev, &dev->ofdev.dev.kobj); +#endif + /* MacIO itself has a different reg, we use it's PCI base */ if (np == chip->of_node) { - sprintf(dev->ofdev.dev.bus_id, "%1d.%08lx:%.8s", chip->lbus.index, + sprintf(dev->ofdev.dev.bus_id, "%1d.%08lx:%.*s", chip->lbus.index, #ifdef CONFIG_PCI pci_resource_start(chip->lbus.pdev, 0), #else 0, /* NuBus may want to do something better here */ #endif - np->name); + MAX_NODE_NAME_SIZE, np->name); } else { reg = (u32 *)get_property(np, "reg", NULL); - sprintf(dev->ofdev.dev.bus_id, "%1d.%08x:%.8s", chip->lbus.index, - reg ? *reg : 0, np->name); + sprintf(dev->ofdev.dev.bus_id, "%1d.%08x:%.*s", chip->lbus.index, + reg ? *reg : 0, MAX_NODE_NAME_SIZE, np->name); } + /* For now, we use pre-parsed entries in the device-tree for + * interrupt routing and addresses, but we should change that + * to dynamically parsed entries and so get rid of most of the + * clutter in struct device_node + */ + for (i = j = 0; i < np->n_intrs; i++) { + struct resource *res = &dev->interrupt[j]; + + if (j >= MACIO_DEV_COUNT_IRQS) + break; + res->start = np->intrs[i].line; + res->flags = IORESOURCE_IO; + if (np->intrs[j].sense) + res->flags |= IORESOURCE_IRQ_LOWLEVEL; + else + res->flags |= IORESOURCE_IRQ_HIGHEDGE; + res->name = dev->ofdev.dev.bus_id; + if (macio_resource_quirks(np, res, i)) + memset(res, 0, sizeof(struct resource)); + else + j++; + } + dev->n_interrupts = j; + for (i = j = 0; i < np->n_addrs; i++) { + struct resource *res = &dev->resource[j]; + + if (j >= MACIO_DEV_COUNT_RESOURCES) + break; + res->start = np->addrs[i].address; + res->end = np->addrs[i].address + np->addrs[i].size - 1; + res->flags = IORESOURCE_MEM; + res->name = dev->ofdev.dev.bus_id; + if (macio_resource_quirks(np, res, i)) + memset(res, 0, sizeof(struct resource)); + else { + j++; + /* Currently, we consider failure as harmless, this may + * change in the future, once I've found all the device + * tree bugs in older machines & worked around them + */ + if (insert_resource(parent_res, res)) + printk(KERN_WARNING "Can't request resource %d for MacIO" + " device %s\n", i, dev->ofdev.dev.bus_id); + } + } + dev->n_resources = j; + if (of_device_register(&dev->ofdev) != 0) { + printk(KERN_DEBUG"macio: device registration error for %s!\n", + dev->ofdev.dev.bus_id); kfree(dev); return NULL; } @@ -234,25 +345,30 @@ static void macio_pci_add_devices(struct macio_chip *chip) struct device_node *np, *pnode; struct macio_dev *rdev, *mdev, *mbdev = NULL, *sdev = NULL; struct device *parent = NULL; + struct resource *root_res = &iomem_resource; /* Add a node for the macio bus itself */ #ifdef CONFIG_PCI - if (chip->lbus.pdev) + if (chip->lbus.pdev) { parent = &chip->lbus.pdev->dev; + root_res = &chip->lbus.pdev->resource[0]; + } #endif pnode = of_node_get(chip->of_node); if (pnode == NULL) return; - rdev = macio_add_one_device(chip, parent, pnode, NULL); + /* Add macio itself to hierarchy */ + rdev = macio_add_one_device(chip, parent, pnode, NULL, root_res); if (rdev == NULL) return; + root_res = &rdev->resource[0]; /* First scan 1st level */ for (np = NULL; (np = of_get_next_child(pnode, np)) != NULL;) { if (!macio_skip_device(np)) { of_node_get(np); - mdev = macio_add_one_device(chip, &rdev->ofdev.dev, np, NULL); + mdev = macio_add_one_device(chip, &rdev->ofdev.dev, np, NULL, root_res); if (mdev == NULL) of_node_put(np); else if (strncmp(np->name, "media-bay", 9) == 0) @@ -267,17 +383,20 @@ static void macio_pci_add_devices(struct macio_chip *chip) for (np = NULL; (np = of_get_next_child(mbdev->ofdev.node, np)) != NULL;) if (!macio_skip_device(np)) { of_node_get(np); - if (macio_add_one_device(chip, &mbdev->ofdev.dev, np, mbdev) == NULL) + if (macio_add_one_device(chip, &mbdev->ofdev.dev, np, mbdev, + root_res) == NULL) of_node_put(np); } /* Add serial ports if any */ - if (sdev) + if (sdev) { for (np = NULL; (np = of_get_next_child(sdev->ofdev.node, np)) != NULL;) if (!macio_skip_device(np)) { of_node_get(np); - if (macio_add_one_device(chip, &sdev->ofdev.dev, np, NULL) == NULL) + if (macio_add_one_device(chip, &sdev->ofdev.dev, np, NULL, + root_res) == NULL) of_node_put(np); } + } } @@ -294,6 +413,7 @@ int macio_register_driver(struct macio_driver *drv) drv->driver.bus = &macio_bus_type; drv->driver.probe = macio_device_probe; drv->driver.remove = macio_device_remove; + drv->driver.shutdown = macio_device_shutdown; /* register with core */ count = driver_register(&drv->driver); @@ -309,6 +429,97 @@ void macio_unregister_driver(struct macio_driver *drv) driver_unregister(&drv->driver); } +/** + * macio_request_resource - Request an MMIO resource + * @dev: pointer to the device holding the resource + * @resource_no: resource number to request + * @name: resource name + * + * Mark memory region number @resource_no associated with MacIO + * device @dev as being reserved by owner @name. Do not access + * any address inside the memory regions unless this call returns + * successfully. + * + * Returns 0 on success, or %EBUSY on error. A warning + * message is also printed on failure. + */ +int macio_request_resource(struct macio_dev *dev, int resource_no, const char *name) +{ + if (macio_resource_len(dev, resource_no) == 0) + return 0; + + if (!request_mem_region(macio_resource_start(dev, resource_no), + macio_resource_len(dev, resource_no), + name)) + goto err_out; + + return 0; + +err_out: + printk (KERN_WARNING "MacIO: Unable to reserve resource #%d:%lx@%lx" + " for device %s\n", + resource_no, + macio_resource_len(dev, resource_no), + macio_resource_start(dev, resource_no), + dev->ofdev.dev.bus_id); + return -EBUSY; +} + +/** + * macio_release_resource - Release an MMIO resource + * @dev: pointer to the device holding the resource + * @resource_no: resource number to release + */ +void macio_release_resource(struct macio_dev *dev, int resource_no) +{ + if (macio_resource_len(dev, resource_no) == 0) + return; + release_mem_region(macio_resource_start(dev, resource_no), + macio_resource_len(dev, resource_no)); +} + +/** + * macio_request_resources - Reserve all memory resources + * @dev: MacIO device whose resources are to be reserved + * @name: Name to be associated with resource. + * + * Mark all memory regions associated with MacIO device @dev as + * being reserved by owner @name. Do not access any address inside + * the memory regions unless this call returns successfully. + * + * Returns 0 on success, or %EBUSY on error. A warning + * message is also printed on failure. + */ +int macio_request_resources(struct macio_dev *dev, const char *name) +{ + int i; + + for (i = 0; i < dev->n_resources; i++) + if (macio_request_resource(dev, i, name)) + goto err_out; + return 0; + +err_out: + while(--i >= 0) + macio_release_resource(dev, i); + + return -EBUSY; +} + +/** + * macio_release_resources - Release reserved memory resources + * @dev: MacIO device whose resources were previously reserved + */ + +void macio_release_resources(struct macio_dev *dev) +{ + int i; + + for (i = 0; i < dev->n_resources; i++) + macio_release_resource(dev, i); +} + + #ifdef CONFIG_PCI static int __devinit macio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) @@ -416,3 +627,7 @@ EXPORT_SYMBOL(macio_register_driver); EXPORT_SYMBOL(macio_unregister_driver); EXPORT_SYMBOL(macio_dev_get); EXPORT_SYMBOL(macio_dev_put); +EXPORT_SYMBOL(macio_request_resource); +EXPORT_SYMBOL(macio_release_resource); +EXPORT_SYMBOL(macio_request_resources); +EXPORT_SYMBOL(macio_release_resources); diff --git a/include/asm-ppc/macio.h b/include/asm-ppc/macio.h index 25381dc34fcd5..00605bb88b2a1 100644 --- a/include/asm-ppc/macio.h +++ b/include/asm-ppc/macio.h @@ -9,7 +9,7 @@ extern struct bus_type macio_bus_type; struct macio_driver; struct macio_chip; -#define MACIO_DEV_COUNT_RESOURCE 8 +#define MACIO_DEV_COUNT_RESOURCES 8 #define MACIO_DEV_COUNT_IRQS 8 /* @@ -38,6 +38,10 @@ struct macio_dev struct macio_bus *bus; /* macio bus this device is on */ struct macio_dev *media_bay; /* Device is part of a media bay */ struct of_device ofdev; + int n_resources; + struct resource resource[MACIO_DEV_COUNT_RESOURCES]; + int n_interrupts; + struct resource interrupt[MACIO_DEV_COUNT_IRQS]; }; #define to_macio_device(d) container_of(d, struct macio_dev, ofdev.dev) #define of_to_macio_device(d) container_of(d, struct macio_dev, ofdev) @@ -45,6 +49,71 @@ struct macio_dev extern struct macio_dev *macio_dev_get(struct macio_dev *dev); extern void macio_dev_put(struct macio_dev *dev); +/* + * Accessors to resources & interrupts and other device + * fields + */ + +static inline int macio_resource_count(struct macio_dev *dev) +{ + return dev->n_resources; +} + +static inline unsigned long macio_resource_start(struct macio_dev *dev, int resource_no) +{ + return dev->resource[resource_no].start; +} + +static inline unsigned long macio_resource_end(struct macio_dev *dev, int resource_no) +{ + return dev->resource[resource_no].end; +} + +static inline unsigned long macio_resource_len(struct macio_dev *dev, int resource_no) +{ + struct resource *res = &dev->resource[resource_no]; + if (res->start == 0 || res->end == 0 || res->end < res->start) + return 0; + return res->end - res->start + 1; +} + +extern int macio_request_resource(struct macio_dev *dev, int resource_no, const char *name); +extern void macio_release_resource(struct macio_dev *dev, int resource_no); +extern int macio_request_resources(struct macio_dev *dev, const char *name); +extern void macio_release_resources(struct macio_dev *dev); + +static inline int macio_irq_count(struct macio_dev *dev) +{ + return dev->n_interrupts; +} + +static inline int macio_irq(struct macio_dev *dev, int irq_no) +{ + return dev->interrupt[irq_no].start; +} + +static inline void macio_set_drvdata(struct macio_dev *dev, void *data) +{ + dev_set_drvdata(&dev->ofdev.dev, data); +} + +static inline void* macio_get_drvdata(struct macio_dev *dev) +{ + return dev_get_drvdata(&dev->ofdev.dev); +} + +static inline struct device_node *macio_get_of_node(struct macio_dev *mdev) +{ + return mdev->ofdev.node; +} + +#ifdef CONFIG_PCI +static inline struct pci_dev *macio_get_pci_dev(struct macio_dev *mdev) +{ + return mdev->bus->pdev; +} +#endif + /* * A driver for a mac-io chip based device */ -- 2.53.0