From 51d3082fe6e55aecfa17113dbe98077c749f724c Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 23 Nov 2005 17:57:25 +1100 Subject: [PATCH] powerpc: Unify udbg (#2) This patch unifies udbg for both ppc32 and ppc64 when building the merged achitecture. xmon now has a single "back end". The powermac udbg stuff gets enriched with some ADB capabilities and btext output. In addition, the early_init callback is now called on ppc32 as well, approx. in the same order as ppc64 regarding device-tree manipulations. The init sequences of ppc32 and ppc64 are getting closer, I'll unify them in a later patch. For now, you can force udbg to the scc using "sccdbg" or to btext using "btextdbg" on powermacs. I'll implement a cleaner way of forcing udbg output to something else than the autodetected OF output device in a later patch. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- drivers/i2c/busses/i2c-keywest.c | 25 +++++++----- drivers/macintosh/via-cuda.c | 48 +++++++++++----------- drivers/macintosh/via-pmu.c | 87 +++++++++++++++++++++++++--------------- 3 files changed, 93 insertions(+), 67 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-keywest.c b/drivers/i2c/busses/i2c-keywest.c index d61f748278fc47..93e7080e3bc5af 100644 --- a/drivers/i2c/busses/i2c-keywest.c +++ b/drivers/i2c/busses/i2c-keywest.c @@ -505,16 +505,23 @@ static int create_iface(struct device_node *np, struct device *dev) { unsigned long steps; - unsigned bsteps, tsize, i, nchan, addroffset; + unsigned bsteps, tsize, i, nchan; struct keywest_iface* iface; - u32 *psteps, *prate; + u32 *psteps, *prate, *addrp; int rc; - if (np->n_intrs < 1 || np->n_addrs < 1) { - printk(KERN_ERR "%s: Missing interrupt or address !\n", + if (np->n_intrs < 1) { + printk(KERN_ERR "%s: Missing interrupt !\n", np->full_name); return -ENODEV; } + addrp = (u32 *)get_property(np, "AAPL,address", NULL); + if (addrp == NULL) { + printk(KERN_ERR "%s: Can't find address !\n", + np->full_name); + return -ENODEV; + } + if (pmac_low_i2c_lock(np)) return -ENODEV; @@ -525,13 +532,10 @@ create_iface(struct device_node *np, struct device *dev) for (bsteps = 0; (steps & 0x01) == 0; bsteps++) steps >>= 1; - if (np->parent->name[0] == 'u') { + if (np->parent->name[0] == 'u') nchan = 2; - addroffset = 3; - } else { - addroffset = 0; + else nchan = 1; - } tsize = sizeof(struct keywest_iface) + (sizeof(struct keywest_chan) + 4) * nchan; @@ -550,8 +554,7 @@ create_iface(struct device_node *np, struct device *dev) iface->irq = np->intrs[0].line; iface->channels = (struct keywest_chan *) (((unsigned long)(iface + 1) + 3UL) & ~3UL); - iface->base = ioremap(np->addrs[0].address + addroffset, - np->addrs[0].size); + iface->base = ioremap(*addrp, 0x1000); if (!iface->base) { printk(KERN_ERR "i2c-keywest: can't map inteface !\n"); kfree(iface); diff --git a/drivers/macintosh/via-cuda.c b/drivers/macintosh/via-cuda.c index d843a6c9c6df8d..18ff770ea668e8 100644 --- a/drivers/macintosh/via-cuda.c +++ b/drivers/macintosh/via-cuda.c @@ -127,39 +127,34 @@ struct adb_driver via_cuda_driver = { #endif /* CONFIG_ADB */ #ifdef CONFIG_PPC -int __init -find_via_cuda(void) +int __init find_via_cuda(void) { - int err; struct adb_request req; + phys_addr_t taddr; + u32 *reg; + int err; if (vias != 0) return 1; - vias = find_devices("via-cuda"); + vias = of_find_node_by_name(NULL, "via-cuda"); if (vias == 0) return 0; - if (vias->next != 0) - printk(KERN_WARNING "Warning: only using 1st via-cuda\n"); - -#if 0 - { int i; - - printk("find_via_cuda: node = %p, addrs =", vias->node); - for (i = 0; i < vias->n_addrs; ++i) - printk(" %x(%x)", vias->addrs[i].address, vias->addrs[i].size); - printk(", intrs ="); - for (i = 0; i < vias->n_intrs; ++i) - printk(" %x", vias->intrs[i].line); - printk("\n"); } -#endif - if (vias->n_addrs != 1 || vias->n_intrs != 1) { - printk(KERN_ERR "via-cuda: expecting 1 address (%d) and 1 interrupt (%d)\n", - vias->n_addrs, vias->n_intrs); - if (vias->n_addrs < 1 || vias->n_intrs < 1) - return 0; + reg = (u32 *)get_property(vias, "reg", NULL); + if (reg == NULL) { + printk(KERN_ERR "via-cuda: No \"reg\" property !\n"); + goto fail; + } + taddr = of_translate_address(vias, reg); + if (taddr == 0) { + printk(KERN_ERR "via-cuda: Can't translate address !\n"); + goto fail; + } + via = ioremap(taddr, 0x2000); + if (via == NULL) { + printk(KERN_ERR "via-cuda: Can't map address !\n"); + goto fail; } - via = ioremap(vias->addrs->address, 0x2000); cuda_state = idle; sys_ctrler = SYS_CTRLER_CUDA; @@ -185,6 +180,11 @@ find_via_cuda(void) cuda_poll(); return 1; + + fail: + of_node_put(vias); + vias = NULL; + return 0; } #endif /* CONFIG_PPC */ diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index 5640435085694f..13881f1996079c 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c @@ -147,6 +147,7 @@ static struct device_node *vias; static int pmu_kind = PMU_UNKNOWN; static int pmu_fully_inited = 0; static int pmu_has_adb; +static struct device_node *gpio_node; static unsigned char __iomem *gpio_reg = NULL; static int gpio_irq = -1; static int gpio_irq_enabled = -1; @@ -295,22 +296,26 @@ static struct backlight_controller pmu_backlight_controller = { }; #endif /* CONFIG_PMAC_BACKLIGHT */ -int -find_via_pmu(void) +int __init find_via_pmu(void) { + phys_addr_t taddr; + u32 *reg; + if (via != 0) return 1; - vias = find_devices("via-pmu"); - if (vias == 0) + vias = of_find_node_by_name(NULL, "via-pmu"); + if (vias == NULL) return 0; - if (vias->next != 0) - printk(KERN_WARNING "Warning: only using 1st via-pmu\n"); - if (vias->n_addrs < 1 || vias->n_intrs < 1) { - printk(KERN_ERR "via-pmu: %d addresses, %d interrupts!\n", - vias->n_addrs, vias->n_intrs); - if (vias->n_addrs < 1 || vias->n_intrs < 1) - return 0; + reg = (u32 *)get_property(vias, "reg", NULL); + if (reg == NULL) { + printk(KERN_ERR "via-pmu: No \"reg\" property !\n"); + goto fail; + } + taddr = of_translate_address(vias, reg); + if (taddr == 0) { + printk(KERN_ERR "via-pmu: Can't translate address !\n"); + goto fail; } spin_lock_init(&pmu_lock); @@ -331,7 +336,8 @@ find_via_pmu(void) pmu_kind = PMU_HEATHROW_BASED; else if (device_is_compatible(vias->parent, "Keylargo") || device_is_compatible(vias->parent, "K2-Keylargo")) { - struct device_node *gpio, *gpiop; + struct device_node *gpiop; + phys_addr_t gaddr = 0; pmu_kind = PMU_KEYLARGO_BASED; pmu_has_adb = (find_type_devices("adb") != NULL); @@ -341,19 +347,24 @@ find_via_pmu(void) PMU_INT_TICK | PMU_INT_ENVIRONMENT; - gpiop = find_devices("gpio"); - if (gpiop && gpiop->n_addrs) { - gpio_reg = ioremap(gpiop->addrs->address, 0x10); - gpio = find_devices("extint-gpio1"); - if (gpio == NULL) - gpio = find_devices("pmu-interrupt"); - if (gpio && gpio->parent == gpiop && gpio->n_intrs) - gpio_irq = gpio->intrs[0].line; + gpiop = of_find_node_by_name(NULL, "gpio"); + if (gpiop) { + reg = (u32 *)get_property(gpiop, "reg", NULL); + if (reg) + gaddr = of_translate_address(gpiop, reg); + if (gaddr != 0) + gpio_reg = ioremap(gaddr, 0x10); } + if (gpio_reg == NULL) + printk(KERN_ERR "via-pmu: Can't find GPIO reg !\n"); } else pmu_kind = PMU_UNKNOWN; - via = ioremap(vias->addrs->address, 0x2000); + via = ioremap(taddr, 0x2000); + if (via == NULL) { + printk(KERN_ERR "via-pmu: Can't map address !\n"); + goto fail; + } out_8(&via[IER], IER_CLR | 0x7f); /* disable all intrs */ out_8(&via[IFR], 0x7f); /* clear IFR */ @@ -371,17 +382,19 @@ find_via_pmu(void) sys_ctrler = SYS_CTRLER_PMU; return 1; + fail: + of_node_put(vias); + vias = NULL; + return 0; } #ifdef CONFIG_ADB -static int -pmu_probe(void) +static int pmu_probe(void) { return vias == NULL? -ENODEV: 0; } -static int __init -pmu_init(void) +static int __init pmu_init(void) { if (vias == NULL) return -ENODEV; @@ -405,7 +418,7 @@ static int __init via_pmu_start(void) bright_req_2.complete = 1; batt_req.complete = 1; -#if defined(CONFIG_PPC32) && !defined(CONFIG_PPC_MERGE) +#ifndef CONFIG_PPC_MERGE if (pmu_kind == PMU_KEYLARGO_BASED) openpic_set_irq_priority(vias->intrs[0].line, OPENPIC_PRIORITY_DEFAULT + 1); @@ -418,10 +431,22 @@ static int __init via_pmu_start(void) return -EAGAIN; } - if (pmu_kind == PMU_KEYLARGO_BASED && gpio_irq != -1) { - if (request_irq(gpio_irq, gpio1_interrupt, 0, "GPIO1 ADB", (void *)0)) - printk(KERN_ERR "pmu: can't get irq %d (GPIO1)\n", gpio_irq); - gpio_irq_enabled = 1; + if (pmu_kind == PMU_KEYLARGO_BASED) { + gpio_node = of_find_node_by_name(NULL, "extint-gpio1"); + if (gpio_node == NULL) + gpio_node = of_find_node_by_name(NULL, + "pmu-interrupt"); + if (gpio_node && gpio_node->n_intrs > 0) + gpio_irq = gpio_node->intrs[0].line; + + if (gpio_irq != -1) { + if (request_irq(gpio_irq, gpio1_interrupt, 0, + "GPIO1 ADB", (void *)0)) + printk(KERN_ERR "pmu: can't get irq %d" + " (GPIO1)\n", gpio_irq); + else + gpio_irq_enabled = 1; + } } /* Enable interrupts */ @@ -1371,7 +1396,6 @@ next: } pmu_done(req); } else { -#if defined(CONFIG_XMON) && !defined(CONFIG_PPC64) if (len == 4 && data[1] == 0x2c) { extern int xmon_wants_key, xmon_adb_keycode; if (xmon_wants_key) { @@ -1379,7 +1403,6 @@ next: return; } } -#endif /* defined(CONFIG_XMON) && !defined(CONFIG_PPC64) */ #ifdef CONFIG_ADB /* * XXX On the [23]400 the PMU gives us an up -- cgit 1.2.3-korg From 2c5bd01f8f5d7c655d9d1aa60b696d980947e3be Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 23 Nov 2005 17:59:04 +1100 Subject: [PATCH] powerpc: convert macio_asic to use prom_parse Converts the macio_asic core to use the new OF parsing routines instead of relying on the pre-parsed values in struct device_node. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- drivers/macintosh/macio_asic.c | 234 ++++++++++++++++++++++++----------------- 1 file changed, 140 insertions(+), 94 deletions(-) (limited to 'drivers') diff --git a/drivers/macintosh/macio_asic.c b/drivers/macintosh/macio_asic.c index 228e1852a83659..f14c744a94efaf 100644 --- a/drivers/macintosh/macio_asic.c +++ b/drivers/macintosh/macio_asic.c @@ -3,6 +3,13 @@ * a MacIO ASIC. Interface to new driver model mostly * stolen from the PCI version. * + * Copyright (C) 2005 Ben. Herrenschmidt (benh@kernel.crashing.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * * TODO: * * - Don't probe below media bay by default, but instead provide @@ -218,12 +225,14 @@ postcore_initcall(macio_bus_driver_init); /** - * macio_release_dev - free a macio device structure when all users of it are finished. + * macio_release_dev - free a macio device structure when all users of it are + * finished. * @dev: device that's been disconnected * - * Will be called only by the device core when all users of this macio device are - * done. This currently means never as we don't hot remove any macio device yet, - * though that will happen with mediabay based devices in a later implementation. + * Will be called only by the device core when all users of this macio device + * are done. This currently means never as we don't hot remove any macio + * device yet, though that will happen with mediabay based devices in a later + * implementation. */ static void macio_release_dev(struct device *dev) { @@ -242,7 +251,8 @@ static void macio_release_dev(struct device *dev) * 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) +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 */ @@ -258,12 +268,15 @@ static int macio_resource_quirks(struct device_node *np, struct resource *res, i 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 */ + /* 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"))) + 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")) @@ -285,6 +298,71 @@ static int macio_resource_quirks(struct device_node *np, struct resource *res, i } +static void macio_setup_interrupts(struct macio_dev *dev) +{ + struct device_node *np = dev->ofdev.node; + int i,j; + + /* 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; +} + +static void macio_setup_resources(struct macio_dev *dev, + struct resource *parent_res) +{ + struct device_node *np = dev->ofdev.node; + u32 *addr; + u64 size; + int index; + + for (index = 0; (addr = of_get_address(np, index, &size)) != NULL; + index++) { + struct resource *res = &dev->resource[index]; + if (index >= MACIO_DEV_COUNT_RESOURCES) + break; + res->start = of_translate_address(np, addr); + res->end = res->start + (unsigned long)size - 1; + res->flags = IORESOURCE_MEM; + res->name = dev->ofdev.dev.bus_id; + + if (macio_resource_quirks(np, res, index)) { + memset(res, 0, sizeof(struct resource)); + continue; + } + /* 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", + index, dev->ofdev.dev.bus_id); + } + } + dev->n_resources = index; +} + /** * macio_add_one_device - Add one device from OF node to the device tree * @chip: pointer to the macio_chip holding the device @@ -294,12 +372,13 @@ static int macio_resource_quirks(struct device_node *np, struct resource *res, i * When media-bay is changed to hotswap drivers, this function will * 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, +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 resource *parent_res) { struct macio_dev *dev; - int i, j; u32 *reg; if (np == NULL) @@ -326,7 +405,8 @@ static struct macio_dev * macio_add_one_device(struct macio_chip *chip, struct d /* 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:%.*s", 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 @@ -335,57 +415,16 @@ static struct macio_dev * macio_add_one_device(struct macio_chip *chip, struct d MAX_NODE_NAME_SIZE, np->name); } else { reg = (u32 *)get_property(np, "reg", NULL); - sprintf(dev->ofdev.dev.bus_id, "%1d.%08x:%.*s", chip->lbus.index, + 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; + /* Setup interrupts & resources */ + macio_setup_interrupts(dev); + macio_setup_resources(dev, parent_res); + /* Register with core */ if (of_device_register(&dev->ofdev) != 0) { printk(KERN_DEBUG"macio: device registration error for %s!\n", dev->ofdev.dev.bus_id); @@ -442,36 +481,42 @@ static void macio_pci_add_devices(struct macio_chip *chip) /* 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, root_res); - if (mdev == NULL) - of_node_put(np); - else if (strncmp(np->name, "media-bay", 9) == 0) - mbdev = mdev; - else if (strncmp(np->name, "escc", 4) == 0) - sdev = mdev; - } + if (macio_skip_device(np)) + continue; + of_node_get(np); + 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) + mbdev = mdev; + else if (strncmp(np->name, "escc", 4) == 0) + sdev = mdev; } /* Add media bay devices if any */ if (mbdev) - 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, - root_res) == NULL) - of_node_put(np); - } + for (np = NULL; (np = of_get_next_child(mbdev->ofdev.node, np)) + != NULL;) { + if (macio_skip_device(np)) + continue; + of_node_get(np); + 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) { - 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, - root_res) == NULL) - of_node_put(np); - } + for (np = NULL; (np = of_get_next_child(sdev->ofdev.node, np)) + != NULL;) { + if (macio_skip_device(np)) + continue; + of_node_get(np); + if (macio_add_one_device(chip, &sdev->ofdev.dev, np, + NULL, root_res) == NULL) + of_node_put(np); + } } } @@ -519,7 +564,8 @@ void macio_unregister_driver(struct macio_driver *drv) * 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) +int macio_request_resource(struct macio_dev *dev, int resource_no, + const char *name) { if (macio_resource_len(dev, resource_no) == 0) return 0; @@ -606,20 +652,20 @@ static int __devinit macio_pci_probe(struct pci_dev *pdev, const struct pci_devi if (ent->vendor != PCI_VENDOR_ID_APPLE) return -ENODEV; - /* Note regarding refcounting: We assume pci_device_to_OF_node() is ported - * to new OF APIs and returns a node with refcount incremented. This isn't - * the case today, but on the other hand ppc32 doesn't do refcounting. This - * will have to be fixed when going to ppc64. --BenH. + /* Note regarding refcounting: We assume pci_device_to_OF_node() is + * ported to new OF APIs and returns a node with refcount incremented. */ np = pci_device_to_OF_node(pdev); if (np == NULL) return -ENODEV; - /* This assumption is wrong, fix that here for now until I fix the arch */ + /* The above assumption is wrong !!! + * fix that here for now until I fix the arch code + */ of_node_get(np); - /* We also assume that pmac_feature will have done a get() on nodes stored - * in the macio chips array + /* We also assume that pmac_feature will have done a get() on nodes + * stored in the macio chips array */ chip = macio_find(np, macio_unknown); of_node_put(np); @@ -639,9 +685,9 @@ static int __devinit macio_pci_probe(struct pci_dev *pdev, const struct pci_devi /* * HACK ALERT: The WallStreet PowerBook and some OHare based machines - * have 2 macio ASICs. I must probe the "main" one first or IDE ordering - * will be incorrect. So I put on "hold" the second one since it seem to - * appear first on PCI + * have 2 macio ASICs. I must probe the "main" one first or IDE + * ordering will be incorrect. So I put on "hold" the second one since + * it seem to appear first on PCI */ if (chip->type == macio_gatwick || chip->type == macio_ohareII) if (macio_chips[0].lbus.pdev == NULL) { -- cgit 1.2.3-korg From fb64c2446b20bf0206a690e9e1df88b25ac421e6 Mon Sep 17 00:00:00 2001 From: Marcelo Tosatti Date: Thu, 24 Nov 2005 11:32:09 -0200 Subject: [PATCH] ppc32: m8xx watchdog update This updates m8xx_wdt as follows: 1) Remove now obsolete fpos check in the write() function. The driver is currently non functional due to this bug. 2) Use in/out macros for register access. 3) Allows m8xx_wdt to use a kernel timer instead of the builtin RTC/PIT for keep-alive trigger (which is responsible for servicing the watchdog until an userspace application takes over). For instance Cyclades PRxK boards (MPC 855T based) have a non-functional internal RTC/PIT unit. Behaviour for boards with RTC/PIT is unchaged. 4) The last change required moving the RTCSC register setting code to a weak function which can be overriden by board specific files. Otherwise the timer init code trashes the register making it impossible for m8xx_wdt to detect the situation. Signed-off-by: Marcelo Tosatti Signed-off-by: Paul Mackerras --- arch/ppc/syslib/m8xx_setup.c | 15 +++++-- arch/ppc/syslib/m8xx_wdt.c | 92 +++++++++++++++++++++++++++----------- arch/ppc/syslib/m8xx_wdt.h | 4 ++ drivers/char/watchdog/mpc8xx_wdt.c | 13 +++--- 4 files changed, 90 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/arch/ppc/syslib/m8xx_setup.c b/arch/ppc/syslib/m8xx_setup.c index 1cc3abe6fa4356..688616de3cde5c 100644 --- a/arch/ppc/syslib/m8xx_setup.c +++ b/arch/ppc/syslib/m8xx_setup.c @@ -135,6 +135,16 @@ static struct irqaction tbint_irqaction = { .name = "tbint", }; +/* per-board overridable init_internal_rtc() function. */ +void __init __attribute__ ((weak)) +init_internal_rtc(void) +{ + /* Disable the RTC one second and alarm interrupts. */ + out_be16(&((immap_t *)IMAP_ADDR)->im_sit.sit_rtcsc, in_be16(&((immap_t *)IMAP_ADDR)->im_sit.sit_rtcsc) & ~(RTCSC_SIE | RTCSC_ALE)); + /* Enable the RTC */ + out_be16(&((immap_t *)IMAP_ADDR)->im_sit.sit_rtcsc, in_be16(&((immap_t *)IMAP_ADDR)->im_sit.sit_rtcsc) | (RTCSC_RTF | RTCSC_RTE)); +} + /* The decrementer counts at the system (internal) clock frequency divided by * sixteen, or external oscillator divided by four. We force the processor * to use system clock divided by sixteen. @@ -183,10 +193,7 @@ void __init m8xx_calibrate_decr(void) out_be32(&((immap_t *)IMAP_ADDR)->im_sitk.sitk_rtcsck, KAPWR_KEY); out_be32(&((immap_t *)IMAP_ADDR)->im_sitk.sitk_tbk, KAPWR_KEY); - /* Disable the RTC one second and alarm interrupts. */ - out_be16(&((immap_t *)IMAP_ADDR)->im_sit.sit_rtcsc, in_be16(&((immap_t *)IMAP_ADDR)->im_sit.sit_rtcsc) & ~(RTCSC_SIE | RTCSC_ALE)); - /* Enable the RTC */ - out_be16(&((immap_t *)IMAP_ADDR)->im_sit.sit_rtcsc, in_be16(&((immap_t *)IMAP_ADDR)->im_sit.sit_rtcsc) | (RTCSC_RTF | RTCSC_RTE)); + init_internal_rtc(); /* Enabling the decrementer also enables the timebase interrupts * (or from the other point of view, to get decrementer interrupts diff --git a/arch/ppc/syslib/m8xx_wdt.c b/arch/ppc/syslib/m8xx_wdt.c index a21632d37e5ab7..df6c9557b86a4f 100644 --- a/arch/ppc/syslib/m8xx_wdt.c +++ b/arch/ppc/syslib/m8xx_wdt.c @@ -19,6 +19,7 @@ #include static int wdt_timeout; +int m8xx_has_internal_rtc = 0; static irqreturn_t m8xx_wdt_interrupt(int, void *, struct pt_regs *); static struct irqaction m8xx_wdt_irqaction = { @@ -45,35 +46,15 @@ static irqreturn_t m8xx_wdt_interrupt(int irq, void *dev, struct pt_regs *regs) return IRQ_HANDLED; } -void __init m8xx_wdt_handler_install(bd_t * binfo) +#define SYPCR_SWP 0x1 +#define SYPCR_SWE 0x4 + + +void __init m8xx_wdt_install_irq(volatile immap_t *imap, bd_t *binfo) { - volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR; u32 pitc; - u32 sypcr; u32 pitrtclk; - sypcr = in_be32(&imap->im_siu_conf.sc_sypcr); - - if (!(sypcr & 0x04)) { - printk(KERN_NOTICE "m8xx_wdt: wdt disabled (SYPCR: 0x%08X)\n", - sypcr); - return; - } - - m8xx_wdt_reset(); - - printk(KERN_NOTICE - "m8xx_wdt: active wdt found (SWTC: 0x%04X, SWP: 0x%01X)\n", - (sypcr >> 16), sypcr & 0x01); - - wdt_timeout = (sypcr >> 16) & 0xFFFF; - - if (!wdt_timeout) - wdt_timeout = 0xFFFF; - - if (sypcr & 0x01) - wdt_timeout *= 2048; - /* * Fire trigger if half of the wdt ticked down */ @@ -98,6 +79,67 @@ void __init m8xx_wdt_handler_install(bd_t * binfo) printk(KERN_NOTICE "m8xx_wdt: keep-alive trigger installed (PITC: 0x%04X)\n", pitc); +} + +static void m8xx_wdt_timer_func(unsigned long data); + +static struct timer_list m8xx_wdt_timer = + TIMER_INITIALIZER(m8xx_wdt_timer_func, 0, 0); + +void m8xx_wdt_stop_timer(void) +{ + del_timer(&m8xx_wdt_timer); +} + +void m8xx_wdt_install_timer(void) +{ + m8xx_wdt_timer.expires = jiffies + (HZ/2); + add_timer(&m8xx_wdt_timer); +} + +static void m8xx_wdt_timer_func(unsigned long data) +{ + m8xx_wdt_reset(); + m8xx_wdt_install_timer(); +} + +void __init m8xx_wdt_handler_install(bd_t * binfo) +{ + volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR; + u32 sypcr; + + sypcr = in_be32(&imap->im_siu_conf.sc_sypcr); + + if (!(sypcr & SYPCR_SWE)) { + printk(KERN_NOTICE "m8xx_wdt: wdt disabled (SYPCR: 0x%08X)\n", + sypcr); + return; + } + + m8xx_wdt_reset(); + + printk(KERN_NOTICE + "m8xx_wdt: active wdt found (SWTC: 0x%04X, SWP: 0x%01X)\n", + (sypcr >> 16), sypcr & SYPCR_SWP); + + wdt_timeout = (sypcr >> 16) & 0xFFFF; + + if (!wdt_timeout) + wdt_timeout = 0xFFFF; + + if (sypcr & SYPCR_SWP) + wdt_timeout *= 2048; + + m8xx_has_internal_rtc = in_be16(&imap->im_sit.sit_rtcsc) & RTCSC_RTE; + + /* if the internal RTC is off use a kernel timer */ + if (!m8xx_has_internal_rtc) { + if (wdt_timeout < (binfo->bi_intfreq/HZ)) + printk(KERN_ERR "m8xx_wdt: timeout too short for ktimer!\n"); + m8xx_wdt_install_timer(); + } else + m8xx_wdt_install_irq(imap, binfo); + wdt_timeout /= binfo->bi_intfreq; } diff --git a/arch/ppc/syslib/m8xx_wdt.h b/arch/ppc/syslib/m8xx_wdt.h index 0d81a9f8155f19..e75835f0012be0 100644 --- a/arch/ppc/syslib/m8xx_wdt.h +++ b/arch/ppc/syslib/m8xx_wdt.h @@ -9,8 +9,12 @@ #ifndef _PPC_SYSLIB_M8XX_WDT_H #define _PPC_SYSLIB_M8XX_WDT_H +extern int m8xx_has_internal_rtc; + extern void m8xx_wdt_handler_install(bd_t * binfo); extern int m8xx_wdt_get_timeout(void); extern void m8xx_wdt_reset(void); +extern void m8xx_wdt_install_timer(void); +extern void m8xx_wdt_stop_timer(void); #endif /* _PPC_SYSLIB_M8XX_WDT_H */ diff --git a/drivers/char/watchdog/mpc8xx_wdt.c b/drivers/char/watchdog/mpc8xx_wdt.c index 56d62ba7c6ce37..ac6fbace47241d 100644 --- a/drivers/char/watchdog/mpc8xx_wdt.c +++ b/drivers/char/watchdog/mpc8xx_wdt.c @@ -27,7 +27,10 @@ static void mpc8xx_wdt_handler_disable(void) { volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR; - imap->im_sit.sit_piscr &= ~(PISCR_PIE | PISCR_PTE); + if (!m8xx_has_internal_rtc) + m8xx_wdt_stop_timer(); + else + out_be32(imap->im_sit.sit_piscr, in_be32(&imap->im_sit.sit_piscr) & ~(PISCR_PIE | PISCR_PTE)); printk(KERN_NOTICE "mpc8xx_wdt: keep-alive handler deactivated\n"); } @@ -36,7 +39,10 @@ static void mpc8xx_wdt_handler_enable(void) { volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR; - imap->im_sit.sit_piscr |= PISCR_PIE | PISCR_PTE; + if (!m8xx_has_internal_rtc) + m8xx_wdt_install_timer(); + else + out_be32(&imap->im_sit.sit_piscr, in_be32(&imap->im_sit.sit_piscr) | PISCR_PIE | PISCR_PTE); printk(KERN_NOTICE "mpc8xx_wdt: keep-alive handler activated\n"); } @@ -68,9 +74,6 @@ static int mpc8xx_wdt_release(struct inode *inode, struct file *file) static ssize_t mpc8xx_wdt_write(struct file *file, const char *data, size_t len, loff_t * ppos) { - if (ppos != &file->f_pos) - return -ESPIPE; - if (len) m8xx_wdt_reset(); -- cgit 1.2.3-korg From bb6b9b28d6847bc71f910e2e82c9040ff4b97ec0 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 30 Nov 2005 16:54:12 +1100 Subject: [PATCH] powerpc: udbg updates The udbg low level io layer has an issue with udbg_getc() returning a char (unsigned on ppc) instead of an int, thus the -1 if you had no available input device could end up turned into 0xff, filling your display with bogus characters. This fixes it, along with adding a little blob to xmon to do a delay before exiting when getting an EOF and fixing the detection of ADB keyboards in udbg_adb.c Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- arch/powerpc/kernel/prom_parse.c | 2 +- arch/powerpc/kernel/udbg.c | 11 +++++++---- arch/powerpc/kernel/udbg_16550.c | 4 ++-- arch/powerpc/platforms/powermac/udbg_adb.c | 8 ++++---- arch/powerpc/platforms/powermac/udbg_scc.c | 4 ++-- arch/powerpc/platforms/pseries/lpar.c | 4 ++-- arch/powerpc/xmon/xmon.c | 4 +++- drivers/macintosh/via-pmu.c | 4 ++-- include/asm-powerpc/udbg.h | 2 +- 9 files changed, 24 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/arch/powerpc/kernel/prom_parse.c b/arch/powerpc/kernel/prom_parse.c index 9c2a5be7a56a88..23c85af53d47e6 100644 --- a/arch/powerpc/kernel/prom_parse.c +++ b/arch/powerpc/kernel/prom_parse.c @@ -276,7 +276,7 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus, finish: of_dump_addr("OF: parent translation for:", addr, pna); - DBG("OF: with offset: %lx\n", offset); + DBG("OF: with offset: "PRu64"\n", offset); /* Translate it into parent bus space */ return pbus->translate(addr, offset, pna); diff --git a/arch/powerpc/kernel/udbg.c b/arch/powerpc/kernel/udbg.c index cc2df5e61bb068..a058285a70e715 100644 --- a/arch/powerpc/kernel/udbg.c +++ b/arch/powerpc/kernel/udbg.c @@ -17,7 +17,7 @@ #include void (*udbg_putc)(char c); -char (*udbg_getc)(void); +int (*udbg_getc)(void); int (*udbg_getc_poll)(void); /* udbg library, used by xmon et al */ @@ -57,8 +57,8 @@ int udbg_write(const char *s, int n) int udbg_read(char *buf, int buflen) { - char c, *p = buf; - int i; + char *p = buf; + int i, c; if (!udbg_getc) return 0; @@ -66,8 +66,11 @@ int udbg_read(char *buf, int buflen) for (i = 0; i < buflen; ++i) { do { c = udbg_getc(); + if (c == -1 && i == 0) + return -1; + } while (c == 0x11 || c == 0x13); - if (c == 0) + if (c == 0 || c == -1) break; *p++ = c; } diff --git a/arch/powerpc/kernel/udbg_16550.c b/arch/powerpc/kernel/udbg_16550.c index 28a58da5592c85..e58c048a7b1959 100644 --- a/arch/powerpc/kernel/udbg_16550.c +++ b/arch/powerpc/kernel/udbg_16550.c @@ -69,14 +69,14 @@ static int udbg_550_getc_poll(void) return -1; } -static char udbg_550_getc(void) +static int udbg_550_getc(void) { if (udbg_comport) { while ((in_8(&udbg_comport->lsr) & LSR_DR) == 0) /* wait for char */; return in_8(&udbg_comport->rbr); } - return 0; + return -1; } void udbg_init_uart(void __iomem *comport, unsigned int speed, diff --git a/arch/powerpc/platforms/powermac/udbg_adb.c b/arch/powerpc/platforms/powermac/udbg_adb.c index 3d5ed23bf0e097..06c8265c2baff7 100644 --- a/arch/powerpc/platforms/powermac/udbg_adb.c +++ b/arch/powerpc/platforms/powermac/udbg_adb.c @@ -29,7 +29,7 @@ */ static void (*udbg_adb_old_putc)(char c); -static char (*udbg_adb_old_getc)(void); +static int (*udbg_adb_old_getc)(void); static int (*udbg_adb_old_getc_poll)(void); static enum { @@ -73,7 +73,7 @@ static unsigned char xmon_shift_keytab[128] = "\0.\0*\0+\0\0\0\0\0/\r\0-\0" /* 0x40 - 0x4f */ "\0\0000123456789\0\0\0"; /* 0x50 - 0x5f */ -static char udbg_adb_local_getc(void) +static int udbg_adb_local_getc(void) { int k, t, on; @@ -116,7 +116,7 @@ static char udbg_adb_local_getc(void) } #endif /* CONFIG_BOOTX_TEXT */ -static char udbg_adb_getc(void) +static int udbg_adb_getc(void) { #ifdef CONFIG_BOOTX_TEXT if (udbg_adb_use_btext && input_type != input_adb_none) @@ -195,7 +195,7 @@ int udbg_adb_init(int force_btext) */ for (np = NULL; (np = of_find_node_by_name(np, "keyboard")) != NULL;) { struct device_node *parent = of_get_parent(np); - int found = (parent && !strcmp(parent->type, "adb") == 0); + int found = (parent && strcmp(parent->type, "adb") == 0); of_node_put(parent); if (found) break; diff --git a/arch/powerpc/platforms/powermac/udbg_scc.c b/arch/powerpc/platforms/powermac/udbg_scc.c index df6dec49c4c7b6..e87d53acfb6182 100644 --- a/arch/powerpc/platforms/powermac/udbg_scc.c +++ b/arch/powerpc/platforms/powermac/udbg_scc.c @@ -47,14 +47,14 @@ static int udbg_scc_getc_poll(void) return -1; } -static char udbg_scc_getc(void) +static int udbg_scc_getc(void) { if (sccc) { while ((in_8(sccc) & SCC_RXRDY) == 0) ; return in_8(sccd); } - return 0; + return -1; } static unsigned char scc_inittab[] = { diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c index 615ffb9610595c..1fe445ab78a634 100644 --- a/arch/powerpc/platforms/pseries/lpar.c +++ b/arch/powerpc/platforms/pseries/lpar.c @@ -112,7 +112,7 @@ static int udbg_hvsi_getc_poll(void) return ch; } -static char udbg_hvsi_getc(void) +static int udbg_hvsi_getc(void) { int ch; for (;;) { @@ -173,7 +173,7 @@ static int udbg_getc_pollLP(void) return ch; } -static char udbg_getcLP(void) +static int udbg_getcLP(void) { int ch; for (;;) { diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c index c45a6ad5f3b75d..465b75c5647e26 100644 --- a/arch/powerpc/xmon/xmon.c +++ b/arch/powerpc/xmon/xmon.c @@ -450,7 +450,6 @@ int xmon_core(struct pt_regs *regs, int fromipi) leave: cpu_clear(cpu, cpus_in_xmon); xmon_fault_jmp[cpu] = NULL; - #else /* UP is simple... */ if (in_xmon) { @@ -805,7 +804,10 @@ cmds(struct pt_regs *excp) break; case 'x': case 'X': + return cmd; case EOF: + printf(" \n"); + mdelay(2000); return cmd; case '?': printf(help_string); diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index 13881f1996079c..d6dabee55f2fee 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c @@ -313,7 +313,7 @@ int __init find_via_pmu(void) goto fail; } taddr = of_translate_address(vias, reg); - if (taddr == 0) { + if (taddr == OF_BAD_ADDR) { printk(KERN_ERR "via-pmu: Can't translate address !\n"); goto fail; } @@ -376,7 +376,7 @@ int __init find_via_pmu(void) return 0; } - printk(KERN_INFO "PMU driver %d initialized for %s, firmware: %02x\n", + printk(KERN_INFO "PMU driver v%d initialized for %s, firmware: %02x\n", PMU_DRIVER_VERSION, pbook_type[pmu_kind], pmu_version); sys_ctrler = SYS_CTRLER_PMU; diff --git a/include/asm-powerpc/udbg.h b/include/asm-powerpc/udbg.h index 8d6b44c8f35d6e..58cdc883e38c5e 100644 --- a/include/asm-powerpc/udbg.h +++ b/include/asm-powerpc/udbg.h @@ -14,7 +14,7 @@ #include extern void (*udbg_putc)(char c); -extern char (*udbg_getc)(void); +extern int (*udbg_getc)(void); extern int (*udbg_getc_poll)(void); extern void udbg_puts(const char *s); -- cgit 1.2.3-korg From d2dd482bc17c3bc240045f80a7c4b4d5cea5e29c Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 30 Nov 2005 16:57:28 +1100 Subject: [PATCH] powerpc: Update OF address parsers This updates the OF address parsers to return the IO flags indicating the type of address obtained. It also adds a PCI call for converting physical addresses that hit IO space into into IO tokens, and add routines that return the translated addresses into struct resource Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- arch/powerpc/kernel/legacy_serial.c | 5 +- arch/powerpc/kernel/pci_64.c | 14 +++ arch/powerpc/kernel/prom_parse.c | 165 ++++++++++++++++++++++++++---- arch/powerpc/platforms/powermac/feature.c | 2 +- arch/ppc/kernel/pci.c | 15 +++ drivers/macintosh/macio_asic.c | 12 +-- include/asm-powerpc/pci-bridge.h | 9 ++ include/asm-powerpc/prom.h | 28 ++++- include/asm-ppc/pci-bridge.h | 9 ++ include/asm-ppc/prom.h | 28 ++++- 10 files changed, 251 insertions(+), 36 deletions(-) (limited to 'drivers') diff --git a/arch/powerpc/kernel/legacy_serial.c b/arch/powerpc/kernel/legacy_serial.c index 83023bb59ad907..d179ec5022926c 100644 --- a/arch/powerpc/kernel/legacy_serial.c +++ b/arch/powerpc/kernel/legacy_serial.c @@ -146,6 +146,7 @@ static int __init add_legacy_pci_port(struct device_node *np, { phys_addr_t addr, base; u32 *addrp; + unsigned int flags; int iotype, index = -1, lindex = 0; /* We only support ports that have a clock frequency properly @@ -159,12 +160,12 @@ static int __init add_legacy_pci_port(struct device_node *np, return -1; /* Get the PCI address. Assume BAR 0 */ - addrp = of_get_pci_address(pci_dev, 0, NULL); + addrp = of_get_pci_address(pci_dev, 0, NULL, &flags); if (addrp == NULL) return -1; /* We only support BAR 0 for now */ - iotype = (addrp[0] & 0x02000000) ? UPIO_MEM : UPIO_PORT; + iotype = (flags & IORESOURCE_MEM) ? UPIO_MEM : UPIO_PORT; addr = of_translate_address(pci_dev, addrp); /* Set the IO base to the same as the translated address for MMIO, diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c index 5f241fcd88e874..0988222741f0fa 100644 --- a/arch/powerpc/kernel/pci_64.c +++ b/arch/powerpc/kernel/pci_64.c @@ -1181,6 +1181,20 @@ void phbs_remap_io(void) remap_bus_range(hose->bus); } +unsigned int pci_address_to_pio(phys_addr_t address) +{ + struct pci_controller *hose, *tmp; + + list_for_each_entry_safe(hose, tmp, &hose_list, list_node) { + if (address >= hose->io_base_phys && + address < (hose->io_base_phys + hose->pci_io_size)) + return (unsigned int)hose->io_base_virt + + (address - hose->io_base_phys); + } + return (unsigned int)-1; +} +EXPORT_SYMBOL_GPL(pci_address_to_pio); + static void __devinit fixup_resource(struct resource *res, struct pci_dev *dev) { struct pci_controller *hose = pci_bus_to_host(dev->bus); diff --git a/arch/powerpc/kernel/prom_parse.c b/arch/powerpc/kernel/prom_parse.c index 23c85af53d47e6..5b764277f4702d 100644 --- a/arch/powerpc/kernel/prom_parse.c +++ b/arch/powerpc/kernel/prom_parse.c @@ -4,7 +4,9 @@ #include #include #include +#include #include +#include #ifdef DEBUG #define DBG(fmt...) do { printk(fmt); } while(0) @@ -54,6 +56,7 @@ struct of_bus { int *addrc, int *sizec); u64 (*map)(u32 *addr, u32 *range, int na, int ns, int pna); int (*translate)(u32 *addr, u64 offset, int na); + unsigned int (*get_flags)(u32 *addr); }; @@ -61,8 +64,8 @@ struct of_bus { * Default translator (generic bus) */ -static void of_default_count_cells(struct device_node *dev, - int *addrc, int *sizec) +static void of_bus_default_count_cells(struct device_node *dev, + int *addrc, int *sizec) { if (addrc) *addrc = prom_n_addr_cells(dev); @@ -70,7 +73,7 @@ static void of_default_count_cells(struct device_node *dev, *sizec = prom_n_size_cells(dev); } -static u64 of_default_map(u32 *addr, u32 *range, int na, int ns, int pna) +static u64 of_bus_default_map(u32 *addr, u32 *range, int na, int ns, int pna) { u64 cp, s, da; @@ -86,7 +89,7 @@ static u64 of_default_map(u32 *addr, u32 *range, int na, int ns, int pna) return da - cp; } -static int of_default_translate(u32 *addr, u64 offset, int na) +static int of_bus_default_translate(u32 *addr, u64 offset, int na) { u64 a = of_read_addr(addr, na); memset(addr, 0, na * 4); @@ -98,6 +101,11 @@ static int of_default_translate(u32 *addr, u64 offset, int na) return 0; } +static unsigned int of_bus_default_get_flags(u32 *addr) +{ + return IORESOURCE_MEM; +} + /* * PCI bus specific translator @@ -139,7 +147,24 @@ static u64 of_bus_pci_map(u32 *addr, u32 *range, int na, int ns, int pna) static int of_bus_pci_translate(u32 *addr, u64 offset, int na) { - return of_default_translate(addr + 1, offset, na - 1); + return of_bus_default_translate(addr + 1, offset, na - 1); +} + +static unsigned int of_bus_pci_get_flags(u32 *addr) +{ + unsigned int flags = 0; + u32 w = addr[0]; + + switch((w >> 24) & 0x03) { + case 0x01: + flags |= IORESOURCE_IO; + case 0x02: /* 32 bits */ + case 0x03: /* 64 bits */ + flags |= IORESOURCE_MEM; + } + if (w & 0x40000000) + flags |= IORESOURCE_PREFETCH; + return flags; } /* @@ -182,9 +207,22 @@ static u64 of_bus_isa_map(u32 *addr, u32 *range, int na, int ns, int pna) static int of_bus_isa_translate(u32 *addr, u64 offset, int na) { - return of_default_translate(addr + 1, offset, na - 1); + return of_bus_default_translate(addr + 1, offset, na - 1); +} + +static unsigned int of_bus_isa_get_flags(u32 *addr) +{ + unsigned int flags = 0; + u32 w = addr[0]; + + if (w & 1) + flags |= IORESOURCE_IO; + else + flags |= IORESOURCE_MEM; + return flags; } + /* * Array of bus specific translators */ @@ -198,6 +236,7 @@ static struct of_bus of_busses[] = { .count_cells = of_bus_pci_count_cells, .map = of_bus_pci_map, .translate = of_bus_pci_translate, + .get_flags = of_bus_pci_get_flags, }, /* ISA */ { @@ -207,15 +246,17 @@ static struct of_bus of_busses[] = { .count_cells = of_bus_isa_count_cells, .map = of_bus_isa_map, .translate = of_bus_isa_translate, + .get_flags = of_bus_isa_get_flags, }, /* Default */ { .name = "default", .addresses = "reg", .match = NULL, - .count_cells = of_default_count_cells, - .map = of_default_map, - .translate = of_default_translate, + .count_cells = of_bus_default_count_cells, + .map = of_bus_default_map, + .translate = of_bus_default_translate, + .get_flags = of_bus_default_get_flags, }, }; @@ -254,7 +295,8 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus, ranges = (u32 *)get_property(parent, "ranges", &rlen); if (ranges == NULL || rlen == 0) { offset = of_read_addr(addr, na); - memset(addr, 0, pna); + memset(addr, 0, pna * 4); + DBG("OF: no ranges, 1:1 translation\n"); goto finish; } @@ -370,7 +412,8 @@ u64 of_translate_address(struct device_node *dev, u32 *in_addr) } EXPORT_SYMBOL(of_translate_address); -u32 *of_get_address(struct device_node *dev, int index, u64 *size) +u32 *of_get_address(struct device_node *dev, int index, u64 *size, + unsigned int *flags) { u32 *prop; unsigned int psize; @@ -399,22 +442,106 @@ u32 *of_get_address(struct device_node *dev, int index, u64 *size) if (i == index) { if (size) *size = of_read_addr(prop + na, ns); + if (flags) + *flags = bus->get_flags(prop); return prop; } return NULL; } EXPORT_SYMBOL(of_get_address); -u32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size) +u32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size, + unsigned int *flags) { - u32 *addr; - int index; + u32 *prop; + unsigned int psize; + struct device_node *parent; + struct of_bus *bus; + int onesize, i, na, ns; - for (index = 0; (addr = of_get_address(dev, index, size)) != NULL; - index++) { - if ((addr[0] & 0xff) == ((bar_no * 4) + PCI_BASE_ADDRESS_0)) - return addr; - } + /* Get parent & match bus type */ + parent = of_get_parent(dev); + if (parent == NULL) + return NULL; + bus = of_match_bus(parent); + if (strcmp(bus->name, "pci")) + return NULL; + bus->count_cells(dev, &na, &ns); + of_node_put(parent); + if (!OF_CHECK_COUNTS(na, ns)) + return NULL; + + /* Get "reg" or "assigned-addresses" property */ + prop = (u32 *)get_property(dev, bus->addresses, &psize); + if (prop == NULL) + return NULL; + psize /= 4; + + onesize = na + ns; + for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++) + if ((prop[0] & 0xff) == ((bar_no * 4) + PCI_BASE_ADDRESS_0)) { + if (size) + *size = of_read_addr(prop + na, ns); + if (flags) + *flags = bus->get_flags(prop); + return prop; + } return NULL; } EXPORT_SYMBOL(of_get_pci_address); + +static int __of_address_to_resource(struct device_node *dev, u32 *addrp, + u64 size, unsigned int flags, + struct resource *r) +{ + u64 taddr; + + if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0) + return -EINVAL; + taddr = of_translate_address(dev, addrp); + if (taddr == OF_BAD_ADDR) + return -EINVAL; + memset(r, 0, sizeof(struct resource)); + if (flags & IORESOURCE_IO) { + unsigned int port; + port = pci_address_to_pio(taddr); + if (port == (unsigned int)-1) + return -EINVAL; + r->start = port; + r->end = port + size - 1; + } else { + r->start = taddr; + r->end = taddr + size - 1; + } + r->flags = flags; + r->name = dev->name; + return 0; +} + +int of_address_to_resource(struct device_node *dev, int index, + struct resource *r) +{ + u32 *addrp; + u64 size; + unsigned int flags; + + addrp = of_get_address(dev, index, &size, &flags); + if (addrp == NULL) + return -EINVAL; + return __of_address_to_resource(dev, addrp, size, flags, r); +} +EXPORT_SYMBOL_GPL(of_address_to_resource); + +int of_pci_address_to_resource(struct device_node *dev, int bar, + struct resource *r) +{ + u32 *addrp; + u64 size; + unsigned int flags; + + addrp = of_get_pci_address(dev, bar, &size, &flags); + if (addrp == NULL) + return -EINVAL; + return __of_address_to_resource(dev, addrp, size, flags, r); +} +EXPORT_SYMBOL_GPL(of_pci_address_to_resource); diff --git a/arch/powerpc/platforms/powermac/feature.c b/arch/powerpc/platforms/powermac/feature.c index 52a9d0c1b8b865..b2928bbe92279e 100644 --- a/arch/powerpc/platforms/powermac/feature.c +++ b/arch/powerpc/platforms/powermac/feature.c @@ -2683,7 +2683,7 @@ static void __init probe_one_macio(const char *name, const char *compat, int typ printk(KERN_ERR "pmac_feature: %s skipped\n", node->full_name); return; } - addrp = of_get_pci_address(node, 0, &size); + addrp = of_get_pci_address(node, 0, &size, NULL); if (addrp == NULL) { printk(KERN_ERR "pmac_feature: %s: can't find base !\n", node->full_name); diff --git a/arch/ppc/kernel/pci.c b/arch/ppc/kernel/pci.c index 0aa184112fb1a3..af364003880be2 100644 --- a/arch/ppc/kernel/pci.c +++ b/arch/ppc/kernel/pci.c @@ -1805,6 +1805,21 @@ void pci_iounmap(struct pci_dev *dev, void __iomem *addr) EXPORT_SYMBOL(pci_iomap); EXPORT_SYMBOL(pci_iounmap); +unsigned int pci_address_to_pio(phys_addr_t address) +{ + struct pci_controller* hose = hose_head; + + for (; hose; hose = hose->next) { + unsigned int size = hose->io_resource.end - + hose->io_resource.start + 1; + if (address >= hose->io_base_phys && + address < (hose->io_base_phys + size)) + return (unsigned int)hose->io_base_virt + + (address - hose->io_base_phys); + } + return (unsigned int)-1; +} +EXPORT_SYMBOL(pci_address_to_pio); /* * Null PCI config access functions, for the case when we can't diff --git a/drivers/macintosh/macio_asic.c b/drivers/macintosh/macio_asic.c index f14c744a94efaf..0137ff239f1352 100644 --- a/drivers/macintosh/macio_asic.c +++ b/drivers/macintosh/macio_asic.c @@ -332,18 +332,14 @@ static void macio_setup_resources(struct macio_dev *dev, struct resource *parent_res) { struct device_node *np = dev->ofdev.node; - u32 *addr; - u64 size; + struct resource r; int index; - for (index = 0; (addr = of_get_address(np, index, &size)) != NULL; - index++) { + for (index = 0; of_address_to_resource(np, index, &r) == 0; index++) { struct resource *res = &dev->resource[index]; if (index >= MACIO_DEV_COUNT_RESOURCES) break; - res->start = of_translate_address(np, addr); - res->end = res->start + (unsigned long)size - 1; - res->flags = IORESOURCE_MEM; + *res = r; res->name = dev->ofdev.dev.bus_id; if (macio_resource_quirks(np, res, index)) { @@ -353,7 +349,7 @@ static void macio_setup_resources(struct macio_dev *dev, /* 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 - */ +l */ if (insert_resource(parent_res, res)) { printk(KERN_WARNING "Can't request resource " "%d for MacIO device %s\n", diff --git a/include/asm-powerpc/pci-bridge.h b/include/asm-powerpc/pci-bridge.h index 00d21513d0096e..01132bb602e39d 100644 --- a/include/asm-powerpc/pci-bridge.h +++ b/include/asm-powerpc/pci-bridge.h @@ -156,6 +156,15 @@ extern struct pci_controller * pcibios_alloc_controller(struct device_node *dev); extern void pcibios_free_controller(struct pci_controller *phb); +#ifdef CONFIG_PCI +extern unsigned int pci_address_to_pio(phys_addr_t address); +#else +static inline unsigned int pci_address_to_pio(phys_addr_t address) +{ + return (unsigned int)-1; +} +#endif + /* Return values for ppc_md.pci_probe_mode function */ #define PCI_PROBE_NONE -1 /* Don't look at this bus at all */ #define PCI_PROBE_NORMAL 0 /* Do normal PCI probing */ diff --git a/include/asm-powerpc/prom.h b/include/asm-powerpc/prom.h index fb732c992bd289..73d27bad55fa88 100644 --- a/include/asm-powerpc/prom.h +++ b/include/asm-powerpc/prom.h @@ -223,14 +223,36 @@ extern struct resource *request_OF_resource(struct device_node* node, int index, const char* name_postfix); extern int release_OF_resource(struct device_node* node, int index); + /* - * Address translation function(s) + * OF address retreival & translation + */ + + +/* Translate an OF address block into a CPU physical address */ #define OF_BAD_ADDR ((u64)-1) extern u64 of_translate_address(struct device_node *np, u32 *addr); -extern u32 *of_get_address(struct device_node *dev, int index, u64 *size); -extern u32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size); +/* Extract an address from a device, returns the region size and + * the address space flags too. The PCI version uses a BAR number + * instead of an absolute index + */ +extern u32 *of_get_address(struct device_node *dev, int index, + u64 *size, unsigned int *flags); +extern u32 *of_get_pci_address(struct device_node *dev, int bar_no, + u64 *size, unsigned int *flags); + +/* Get an address as a resource. Note that if your address is + * a PIO address, the conversion will fail if the physical address + * can't be internally converted to an IO token with + * pci_address_to_pio(), that is because it's either called to early + * or it can't be matched to any host bridge IO space + */ +extern int of_address_to_resource(struct device_node *dev, int index, + struct resource *r); +extern int of_pci_address_to_resource(struct device_node *dev, int bar, + struct resource *r); #endif /* __KERNEL__ */ #endif /* _POWERPC_PROM_H */ diff --git a/include/asm-ppc/pci-bridge.h b/include/asm-ppc/pci-bridge.h index e58c78f90a5a20..95672ddfe52888 100644 --- a/include/asm-ppc/pci-bridge.h +++ b/include/asm-ppc/pci-bridge.h @@ -137,5 +137,14 @@ static inline unsigned char bridge_swizzle(unsigned char pin, */ extern int pciauto_bus_scan(struct pci_controller *, int); +#ifdef CONFIG_PCI +extern unsigned int pci_address_to_pio(phys_addr_t address); +#else +static inline unsigned int pci_address_to_pio(phys_addr_t address) +{ + return (unsigned int)-1; +} +#endif + #endif #endif /* __KERNEL__ */ diff --git a/include/asm-ppc/prom.h b/include/asm-ppc/prom.h index a10a2d64b300ba..eb317a0806e4b7 100644 --- a/include/asm-ppc/prom.h +++ b/include/asm-ppc/prom.h @@ -138,12 +138,34 @@ extern unsigned long sub_reloc_offset(unsigned long); /* - * Address translation function(s) + * OF address retreival & translation + */ + + +/* Translate an OF address block into a CPU physical address */ #define OF_BAD_ADDR ((u64)-1) extern u64 of_translate_address(struct device_node *np, u32 *addr); -extern u32 *of_get_address(struct device_node *dev, int index, u64 *size); -extern u32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size); + +/* Extract an address from a device, returns the region size and + * the address space flags too. The PCI version uses a BAR number + * instead of an absolute index + */ +extern u32 *of_get_address(struct device_node *dev, int index, + u64 *size, unsigned int *flags); +extern u32 *of_get_pci_address(struct device_node *dev, int bar_no, + u64 *size, unsigned int *flags); + +/* Get an address as a resource. Note that if your address is + * a PIO address, the conversion will fail if the physical address + * can't be internally converted to an IO token with + * pci_address_to_pio(), that is because it's either called to early + * or it can't be matched to any host bridge IO space + */ +extern int of_address_to_resource(struct device_node *dev, int index, + struct resource *r); +extern int of_pci_address_to_resource(struct device_node *dev, int bar, + struct resource *r); #endif /* _PPC_PROM_H */ -- cgit 1.2.3-korg From 398f68580f3edd99728028facf571e4a07f9a035 Mon Sep 17 00:00:00 2001 From: Marcelo Tosatti Date: Mon, 5 Dec 2005 19:31:36 -0200 Subject: [PATCH] powerpc/8xx: Fix m8xx_wdt issues The following fixes some issues with the last mpc8xx_wdt update: - Adds missing #include - Use "uint __iomem" pointer for in_be32/out_be32 Signed-off-by: Marcelo Tosatti Signed-off-by: Paul Mackerras --- drivers/char/watchdog/mpc8xx_wdt.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/char/watchdog/mpc8xx_wdt.c b/drivers/char/watchdog/mpc8xx_wdt.c index ac6fbace47241d..b2fc71e2085019 100644 --- a/drivers/char/watchdog/mpc8xx_wdt.c +++ b/drivers/char/watchdog/mpc8xx_wdt.c @@ -18,6 +18,7 @@ #include #include #include +#include #include static unsigned long wdt_opened; @@ -25,24 +26,26 @@ static int wdt_status; static void mpc8xx_wdt_handler_disable(void) { - volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR; + volatile uint __iomem *piscr; + piscr = (uint *)&((immap_t*)IMAP_ADDR)->im_sit.sit_piscr; if (!m8xx_has_internal_rtc) m8xx_wdt_stop_timer(); else - out_be32(imap->im_sit.sit_piscr, in_be32(&imap->im_sit.sit_piscr) & ~(PISCR_PIE | PISCR_PTE)); + out_be32(piscr, in_be32(piscr) & ~(PISCR_PIE | PISCR_PTE)); printk(KERN_NOTICE "mpc8xx_wdt: keep-alive handler deactivated\n"); } static void mpc8xx_wdt_handler_enable(void) { - volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR; + volatile uint __iomem *piscr; + piscr = (uint *)&((immap_t*)IMAP_ADDR)->im_sit.sit_piscr; if (!m8xx_has_internal_rtc) m8xx_wdt_install_timer(); else - out_be32(&imap->im_sit.sit_piscr, in_be32(&imap->im_sit.sit_piscr) | PISCR_PIE | PISCR_PTE); + out_be32(piscr, in_be32(piscr) | PISCR_PIE | PISCR_PTE); printk(KERN_NOTICE "mpc8xx_wdt: keep-alive handler activated\n"); } -- cgit 1.2.3-korg From 9cf84d7c97992dbe5360b241327341c07ce30fc9 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 13 Dec 2005 17:48:35 +1100 Subject: [PATCH] powerpc: Fix platinumfb for some modes The platinumfb driver used only on some powermacs has an issue with some video modes & limited VRAM. This patch fixes it. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- drivers/video/platinumfb.c | 12 ++++++++---- drivers/video/platinumfb.h | 4 +++- 2 files changed, 11 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/video/platinumfb.c b/drivers/video/platinumfb.c index ca4082ae5a18d3..ba0af1b66bb601 100644 --- a/drivers/video/platinumfb.c +++ b/drivers/video/platinumfb.c @@ -138,13 +138,15 @@ static int platinumfb_set_par (struct fb_info *info) init = platinum_reg_init[pinfo->vmode-1]; - if (pinfo->vmode == 13 && pinfo->cmode > 0) - offset = 0x10; + if ((pinfo->vmode == VMODE_832_624_75) && (pinfo->cmode > CMODE_8)) + offset = 0x10; + info->screen_base = pinfo->frame_buffer + init->fb_offset + offset; info->fix.smem_start = (pinfo->frame_buffer_phys) + init->fb_offset + offset; info->fix.visual = (pinfo->cmode == CMODE_8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR; - info->fix.line_length = vmode_attrs[pinfo->vmode-1].hres * (1<cmode) + offset; + info->fix.line_length = vmode_attrs[pinfo->vmode-1].hres * (1<cmode) + + offset; printk("line_length: %x\n", info->fix.line_length); return 0; } @@ -221,7 +223,9 @@ static int platinumfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, static inline int platinum_vram_reqd(int video_mode, int color_mode) { return vmode_attrs[video_mode-1].vres * - (vmode_attrs[video_mode-1].hres * (1< CMODE_8)) ? 0x10 : 0x20) + 0x1000; } #define STORE_D2(a, d) { \ diff --git a/drivers/video/platinumfb.h b/drivers/video/platinumfb.h index 2834fc1c344b34..f6bd77cafd174b 100644 --- a/drivers/video/platinumfb.h +++ b/drivers/video/platinumfb.h @@ -158,7 +158,9 @@ static struct platinum_regvals platinum_reg_init_14 = { /* 832x624, 75Hz (13) */ static struct platinum_regvals platinum_reg_init_13 = { 0x70, - { 864, 1680, 3360 }, /* MacOS does 1680 instead of 1696 to fit 16bpp in 1MB */ + { 864, 1680, 3344 }, /* MacOS does 1680 instead of 1696 to fit 16bpp in 1MB, + * and we use 3344 instead of 3360 to fit in 2Mb + */ { 0xff0, 4, 0, 0, 0, 0, 0x299, 0, 0, 0x21e, 0x120, 0x10, 0x23f, 0x1f, 0x25, 0x37, 0x8a, 0x22a, 0x23e, 0x536, 0x534, 4, 9, 0x52, -- cgit 1.2.3-korg From cc5d0189b9ba95260857a5018a1c2fef90008507 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 13 Dec 2005 18:01:21 +1100 Subject: [PATCH] powerpc: Remove device_node addrs/n_addr The pre-parsed addrs/n_addrs fields in struct device_node are finally gone. Remove the dodgy heuristics that did that parsing at boot and remove the fields themselves since we now have a good replacement with the new OF parsing code. This patch also fixes a bunch of drivers to use the new code instead, so that at least pmac32, pseries, iseries and g5 defconfigs build. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- arch/powerpc/kernel/btext.c | 2 - arch/powerpc/kernel/pci_64.c | 24 +- arch/powerpc/kernel/prom.c | 424 +-------------------------- arch/powerpc/kernel/prom_init.c | 3 +- arch/powerpc/kernel/rtas_pci.c | 49 +--- arch/powerpc/mm/numa.c | 6 +- arch/powerpc/platforms/powermac/feature.c | 75 +++-- arch/powerpc/platforms/powermac/nvram.c | 52 ++-- arch/powerpc/platforms/powermac/pci.c | 87 +++--- arch/powerpc/platforms/powermac/pic.c | 456 ++++++++++++++++-------------- arch/powerpc/platforms/powermac/pmac.h | 6 +- arch/powerpc/platforms/powermac/setup.c | 3 +- arch/powerpc/platforms/powermac/time.c | 15 +- drivers/block/swim3.c | 38 ++- drivers/ide/ppc/pmac.c | 4 +- drivers/macintosh/macio_asic.c | 26 +- drivers/macintosh/mediabay.c | 8 +- drivers/macintosh/via-cuda.c | 4 - drivers/macintosh/via-pmu.c | 9 +- drivers/scsi/mac53c94.c | 22 +- drivers/scsi/mesh.c | 3 +- drivers/serial/pmac_zilog.c | 15 +- drivers/video/controlfb.c | 114 ++++---- drivers/video/offb.c | 122 ++++---- drivers/video/platinumfb.c | 86 +++--- drivers/video/valkyriefb.c | 12 +- include/asm-powerpc/keylargo.h | 10 +- include/asm-powerpc/prom.h | 40 --- sound/oss/dmasound/dmasound_awacs.c | 81 +++--- sound/ppc/pmac.c | 100 ++++--- sound/ppc/pmac.h | 3 +- 31 files changed, 793 insertions(+), 1106 deletions(-) (limited to 'drivers') diff --git a/arch/powerpc/kernel/btext.c b/arch/powerpc/kernel/btext.c index 5de0d80ca2f2a4..6223d39177cb7c 100644 --- a/arch/powerpc/kernel/btext.c +++ b/arch/powerpc/kernel/btext.c @@ -211,8 +211,6 @@ int __init btext_find_display(int allow_nonstdout) struct device_node *np = NULL; int rc = -ENODEV; - printk("trying to initialize btext ...\n"); - name = (char *)get_property(of_chosen, "linux,stdout-path", NULL); if (name != NULL) { np = of_find_node_by_path(name); diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c index 4eb93fc1eef2e5..523f35087e81c0 100644 --- a/arch/powerpc/kernel/pci_64.c +++ b/arch/powerpc/kernel/pci_64.c @@ -896,6 +896,25 @@ static void __devinit pci_process_ISA_OF_ranges(struct device_node *isa_node, unsigned long phb_io_base_phys, void __iomem * phb_io_base_virt) { + /* Remove these asap */ + + struct pci_address { + u32 a_hi; + u32 a_mid; + u32 a_lo; + }; + + struct isa_address { + u32 a_hi; + u32 a_lo; + }; + + struct isa_range { + struct isa_address isa_addr; + struct pci_address pci_addr; + unsigned int size; + }; + struct isa_range *range; unsigned long pci_addr; unsigned int isa_addr; @@ -1330,8 +1349,9 @@ unsigned int pci_address_to_pio(phys_addr_t address) list_for_each_entry_safe(hose, tmp, &hose_list, list_node) { if (address >= hose->io_base_phys && address < (hose->io_base_phys + hose->pci_io_size)) - return (unsigned int)hose->io_base_virt + - (address - hose->io_base_phys); + return (unsigned int) + ((unsigned long)hose->io_base_virt + + (address - hose->io_base_phys)); } return (unsigned int)-1; } diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index 7e798d5b03b492..1b97e13657e5b8 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -57,21 +57,6 @@ #define DBG(fmt...) #endif -struct pci_reg_property { - struct pci_address addr; - u32 size_hi; - u32 size_lo; -}; - -struct isa_reg_property { - u32 space; - u32 address; - u32 size; -}; - - -typedef int interpret_func(struct device_node *, unsigned long *, - int, int, int); static int __initdata dt_root_addr_cells; static int __initdata dt_root_size_cells; @@ -410,237 +395,19 @@ static int __devinit finish_node_interrupts(struct device_node *np, return 0; } -static int __devinit interpret_pci_props(struct device_node *np, - unsigned long *mem_start, - int naddrc, int nsizec, - int measure_only) -{ - struct address_range *adr; - struct pci_reg_property *pci_addrs; - int i, l, n_addrs; - - pci_addrs = (struct pci_reg_property *) - get_property(np, "assigned-addresses", &l); - if (!pci_addrs) - return 0; - - n_addrs = l / sizeof(*pci_addrs); - - adr = prom_alloc(n_addrs * sizeof(*adr), mem_start); - if (!adr) - return -ENOMEM; - - if (measure_only) - return 0; - - np->addrs = adr; - np->n_addrs = n_addrs; - - for (i = 0; i < n_addrs; i++) { - adr[i].space = pci_addrs[i].addr.a_hi; - adr[i].address = pci_addrs[i].addr.a_lo | - ((u64)pci_addrs[i].addr.a_mid << 32); - adr[i].size = pci_addrs[i].size_lo; - } - - return 0; -} - -static int __init interpret_dbdma_props(struct device_node *np, - unsigned long *mem_start, - int naddrc, int nsizec, - int measure_only) -{ - struct reg_property32 *rp; - struct address_range *adr; - unsigned long base_address; - int i, l; - struct device_node *db; - - base_address = 0; - if (!measure_only) { - for (db = np->parent; db != NULL; db = db->parent) { - if (!strcmp(db->type, "dbdma") && db->n_addrs != 0) { - base_address = db->addrs[0].address; - break; - } - } - } - - rp = (struct reg_property32 *) get_property(np, "reg", &l); - if (rp != 0 && l >= sizeof(struct reg_property32)) { - i = 0; - adr = (struct address_range *) (*mem_start); - while ((l -= sizeof(struct reg_property32)) >= 0) { - if (!measure_only) { - adr[i].space = 2; - adr[i].address = rp[i].address + base_address; - adr[i].size = rp[i].size; - } - ++i; - } - np->addrs = adr; - np->n_addrs = i; - (*mem_start) += i * sizeof(struct address_range); - } - - return 0; -} - -static int __init interpret_macio_props(struct device_node *np, - unsigned long *mem_start, - int naddrc, int nsizec, - int measure_only) -{ - struct reg_property32 *rp; - struct address_range *adr; - unsigned long base_address; - int i, l; - struct device_node *db; - - base_address = 0; - if (!measure_only) { - for (db = np->parent; db != NULL; db = db->parent) { - if (!strcmp(db->type, "mac-io") && db->n_addrs != 0) { - base_address = db->addrs[0].address; - break; - } - } - } - - rp = (struct reg_property32 *) get_property(np, "reg", &l); - if (rp != 0 && l >= sizeof(struct reg_property32)) { - i = 0; - adr = (struct address_range *) (*mem_start); - while ((l -= sizeof(struct reg_property32)) >= 0) { - if (!measure_only) { - adr[i].space = 2; - adr[i].address = rp[i].address + base_address; - adr[i].size = rp[i].size; - } - ++i; - } - np->addrs = adr; - np->n_addrs = i; - (*mem_start) += i * sizeof(struct address_range); - } - - return 0; -} - -static int __init interpret_isa_props(struct device_node *np, - unsigned long *mem_start, - int naddrc, int nsizec, - int measure_only) -{ - struct isa_reg_property *rp; - struct address_range *adr; - int i, l; - - rp = (struct isa_reg_property *) get_property(np, "reg", &l); - if (rp != 0 && l >= sizeof(struct isa_reg_property)) { - i = 0; - adr = (struct address_range *) (*mem_start); - while ((l -= sizeof(struct isa_reg_property)) >= 0) { - if (!measure_only) { - adr[i].space = rp[i].space; - adr[i].address = rp[i].address; - adr[i].size = rp[i].size; - } - ++i; - } - np->addrs = adr; - np->n_addrs = i; - (*mem_start) += i * sizeof(struct address_range); - } - - return 0; -} - -static int __init interpret_root_props(struct device_node *np, - unsigned long *mem_start, - int naddrc, int nsizec, - int measure_only) -{ - struct address_range *adr; - int i, l; - unsigned int *rp; - int rpsize = (naddrc + nsizec) * sizeof(unsigned int); - - rp = (unsigned int *) get_property(np, "linux,usable-memory", &l); - if (rp == NULL) - rp = (unsigned int *) get_property(np, "reg", &l); - - if (rp != 0 && l >= rpsize) { - i = 0; - adr = (struct address_range *) (*mem_start); - while ((l -= rpsize) >= 0) { - if (!measure_only) { - adr[i].space = 0; - adr[i].address = rp[naddrc - 1]; - adr[i].size = rp[naddrc + nsizec - 1]; - } - ++i; - rp += naddrc + nsizec; - } - np->addrs = adr; - np->n_addrs = i; - (*mem_start) += i * sizeof(struct address_range); - } - - return 0; -} - static int __devinit finish_node(struct device_node *np, unsigned long *mem_start, - interpret_func *ifunc, - int naddrc, int nsizec, int measure_only) { struct device_node *child; - int *ip, rc = 0; - - /* get the device addresses and interrupts */ - if (ifunc != NULL) - rc = ifunc(np, mem_start, naddrc, nsizec, measure_only); - if (rc) - goto out; + int rc = 0; rc = finish_node_interrupts(np, mem_start, measure_only); if (rc) goto out; - /* Look for #address-cells and #size-cells properties. */ - ip = (int *) get_property(np, "#address-cells", NULL); - if (ip != NULL) - naddrc = *ip; - ip = (int *) get_property(np, "#size-cells", NULL); - if (ip != NULL) - nsizec = *ip; - - if (!strcmp(np->name, "device-tree") || np->parent == NULL) - ifunc = interpret_root_props; - else if (np->type == 0) - ifunc = NULL; - else if (!strcmp(np->type, "pci") || !strcmp(np->type, "vci")) - ifunc = interpret_pci_props; - else if (!strcmp(np->type, "dbdma")) - ifunc = interpret_dbdma_props; - else if (!strcmp(np->type, "mac-io") || ifunc == interpret_macio_props) - ifunc = interpret_macio_props; - else if (!strcmp(np->type, "isa")) - ifunc = interpret_isa_props; - else if (!strcmp(np->name, "uni-n") || !strcmp(np->name, "u3")) - ifunc = interpret_root_props; - else if (!((ifunc == interpret_dbdma_props - || ifunc == interpret_macio_props) - && (!strcmp(np->type, "escc") - || !strcmp(np->type, "media-bay")))) - ifunc = NULL; - for (child = np->child; child != NULL; child = child->sibling) { - rc = finish_node(child, mem_start, ifunc, - naddrc, nsizec, measure_only); + rc = finish_node(child, mem_start, measure_only); if (rc) goto out; } @@ -702,10 +469,10 @@ void __init finish_device_tree(void) * reason and then remove those additional 16 bytes */ size = 16; - finish_node(allnodes, &size, NULL, 0, 0, 1); + finish_node(allnodes, &size, 1); size -= 16; end = start = (unsigned long) __va(lmb_alloc(size, 128)); - finish_node(allnodes, &end, NULL, 0, 0, 0); + finish_node(allnodes, &end, 0); BUG_ON(end != start + size); DBG(" <- finish_device_tree\n"); @@ -1822,7 +1589,6 @@ static void of_node_release(struct kref *kref) prop = next; } kfree(node->intrs); - kfree(node->addrs); kfree(node->full_name); kfree(node->data); kfree(node); @@ -1904,9 +1670,7 @@ void of_detach_node(const struct device_node *np) * This should probably be split up into smaller chunks. */ -static int of_finish_dynamic_node(struct device_node *node, - unsigned long *unused1, int unused2, - int unused3, int unused4) +static int of_finish_dynamic_node(struct device_node *node) { struct device_node *parent = of_get_parent(node); int err = 0; @@ -1927,7 +1691,8 @@ static int of_finish_dynamic_node(struct device_node *node, return -ENODEV; /* fix up new node's linux_phandle field */ - if ((ibm_phandle = (unsigned int *)get_property(node, "ibm,phandle", NULL))) + if ((ibm_phandle = (unsigned int *)get_property(node, + "ibm,phandle", NULL))) node->linux_phandle = *ibm_phandle; out: @@ -1942,7 +1707,9 @@ static int prom_reconfig_notifier(struct notifier_block *nb, switch (action) { case PSERIES_RECONFIG_ADD: - err = finish_node(node, NULL, of_finish_dynamic_node, 0, 0, 0); + err = of_finish_dynamic_node(node); + if (!err) + finish_node(node, NULL, 0); if (err < 0) { printk(KERN_ERR "finish_node returned %d\n", err); err = NOTIFY_BAD; @@ -2016,175 +1783,4 @@ int prom_add_property(struct device_node* np, struct property* prop) return 0; } -/* I quickly hacked that one, check against spec ! */ -static inline unsigned long -bus_space_to_resource_flags(unsigned int bus_space) -{ - u8 space = (bus_space >> 24) & 0xf; - if (space == 0) - space = 0x02; - if (space == 0x02) - return IORESOURCE_MEM; - else if (space == 0x01) - return IORESOURCE_IO; - else { - printk(KERN_WARNING "prom.c: bus_space_to_resource_flags(), space: %x\n", - bus_space); - return 0; - } -} - -#ifdef CONFIG_PCI -static struct resource *find_parent_pci_resource(struct pci_dev* pdev, - struct address_range *range) -{ - unsigned long mask; - int i; - - /* Check this one */ - mask = bus_space_to_resource_flags(range->space); - for (i=0; iresource[i].flags & mask) == mask && - pdev->resource[i].start <= range->address && - pdev->resource[i].end > range->address) { - if ((range->address + range->size - 1) > pdev->resource[i].end) { - /* Add better message */ - printk(KERN_WARNING "PCI/OF resource overlap !\n"); - return NULL; - } - break; - } - } - if (i == DEVICE_COUNT_RESOURCE) - return NULL; - return &pdev->resource[i]; -} - -/* - * Request an OF device resource. Currently handles child of PCI devices, - * or other nodes attached to the root node. Ultimately, put some - * link to resources in the OF node. - */ -struct resource *request_OF_resource(struct device_node* node, int index, - const char* name_postfix) -{ - struct pci_dev* pcidev; - u8 pci_bus, pci_devfn; - unsigned long iomask; - struct device_node* nd; - struct resource* parent; - struct resource *res = NULL; - int nlen, plen; - - if (index >= node->n_addrs) - goto fail; - - /* Sanity check on bus space */ - iomask = bus_space_to_resource_flags(node->addrs[index].space); - if (iomask & IORESOURCE_MEM) - parent = &iomem_resource; - else if (iomask & IORESOURCE_IO) - parent = &ioport_resource; - else - goto fail; - - /* Find a PCI parent if any */ - nd = node; - pcidev = NULL; - while (nd) { - if (!pci_device_from_OF_node(nd, &pci_bus, &pci_devfn)) - pcidev = pci_find_slot(pci_bus, pci_devfn); - if (pcidev) break; - nd = nd->parent; - } - if (pcidev) - parent = find_parent_pci_resource(pcidev, &node->addrs[index]); - if (!parent) { - printk(KERN_WARNING "request_OF_resource(%s), parent not found\n", - node->name); - goto fail; - } - - res = __request_region(parent, node->addrs[index].address, - node->addrs[index].size, NULL); - if (!res) - goto fail; - nlen = strlen(node->name); - plen = name_postfix ? strlen(name_postfix) : 0; - res->name = (const char *)kmalloc(nlen+plen+1, GFP_KERNEL); - if (res->name) { - strcpy((char *)res->name, node->name); - if (plen) - strcpy((char *)res->name+nlen, name_postfix); - } - return res; -fail: - return NULL; -} -EXPORT_SYMBOL(request_OF_resource); - -int release_OF_resource(struct device_node *node, int index) -{ - struct pci_dev* pcidev; - u8 pci_bus, pci_devfn; - unsigned long iomask, start, end; - struct device_node* nd; - struct resource* parent; - struct resource *res = NULL; - - if (index >= node->n_addrs) - return -EINVAL; - - /* Sanity check on bus space */ - iomask = bus_space_to_resource_flags(node->addrs[index].space); - if (iomask & IORESOURCE_MEM) - parent = &iomem_resource; - else if (iomask & IORESOURCE_IO) - parent = &ioport_resource; - else - return -EINVAL; - - /* Find a PCI parent if any */ - nd = node; - pcidev = NULL; - while(nd) { - if (!pci_device_from_OF_node(nd, &pci_bus, &pci_devfn)) - pcidev = pci_find_slot(pci_bus, pci_devfn); - if (pcidev) break; - nd = nd->parent; - } - if (pcidev) - parent = find_parent_pci_resource(pcidev, &node->addrs[index]); - if (!parent) { - printk(KERN_WARNING "release_OF_resource(%s), parent not found\n", - node->name); - return -ENODEV; - } - /* Find us in the parent and its childs */ - res = parent->child; - start = node->addrs[index].address; - end = start + node->addrs[index].size - 1; - while (res) { - if (res->start == start && res->end == end && - (res->flags & IORESOURCE_BUSY)) - break; - if (res->start <= start && res->end >= end) - res = res->child; - else - res = res->sibling; - } - if (!res) - return -ENODEV; - - if (res->name) { - kfree(res->name); - res->name = NULL; - } - release_resource(res); - kfree(res); - - return 0; -} -EXPORT_SYMBOL(release_OF_resource); -#endif /* CONFIG_PCI */ diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index 6007d51d119b5f..e381f2fc121c57 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -558,7 +558,8 @@ unsigned long prom_memparse(const char *ptr, const char **retptr) static void __init early_cmdline_parse(void) { struct prom_t *_prom = &RELOC(prom); - char *opt, *p; + const char *opt; + char *p; int l = 0; RELOC(prom_cmd_line[0]) = 0; diff --git a/arch/powerpc/kernel/rtas_pci.c b/arch/powerpc/kernel/rtas_pci.c index 60dec2401c26dc..45b8109951fe49 100644 --- a/arch/powerpc/kernel/rtas_pci.c +++ b/arch/powerpc/kernel/rtas_pci.c @@ -188,39 +188,19 @@ int is_python(struct device_node *dev) return 0; } -static int get_phb_reg_prop(struct device_node *dev, - unsigned int addr_size_words, - struct reg_property64 *reg) +static void python_countermeasures(struct device_node *dev) { - unsigned int *ui_ptr = NULL, len; - - /* Found a PHB, now figure out where his registers are mapped. */ - ui_ptr = (unsigned int *)get_property(dev, "reg", &len); - if (ui_ptr == NULL) - return 1; - - if (addr_size_words == 1) { - reg->address = ((struct reg_property32 *)ui_ptr)->address; - reg->size = ((struct reg_property32 *)ui_ptr)->size; - } else { - *reg = *((struct reg_property64 *)ui_ptr); - } - - return 0; -} - -static void python_countermeasures(struct device_node *dev, - unsigned int addr_size_words) -{ - struct reg_property64 reg_struct; + struct resource registers; void __iomem *chip_regs; volatile u32 val; - if (get_phb_reg_prop(dev, addr_size_words, ®_struct)) + if (of_address_to_resource(dev, 0, ®isters)) { + printk(KERN_ERR "Can't get address for Python workarounds !\n"); return; + } /* Python's register file is 1 MB in size. */ - chip_regs = ioremap(reg_struct.address & ~(0xfffffUL), 0x100000); + chip_regs = ioremap(registers.start & ~(0xfffffUL), 0x100000); /* * Firmware doesn't always clear this bit which is critical @@ -301,11 +281,10 @@ static int phb_set_bus_ranges(struct device_node *dev, } static int __devinit setup_phb(struct device_node *dev, - struct pci_controller *phb, - unsigned int addr_size_words) + struct pci_controller *phb) { if (is_python(dev)) - python_countermeasures(dev, addr_size_words); + python_countermeasures(dev); if (phb_set_bus_ranges(dev, phb)) return 1; @@ -320,8 +299,8 @@ unsigned long __init find_and_init_phbs(void) { struct device_node *node; struct pci_controller *phb; - unsigned int root_size_cells = 0; unsigned int index; + unsigned int root_size_cells = 0; unsigned int *opprop = NULL; struct device_node *root = of_find_node_by_path("/"); @@ -343,10 +322,11 @@ unsigned long __init find_and_init_phbs(void) phb = pcibios_alloc_controller(node); if (!phb) continue; - setup_phb(node, phb, root_size_cells); + setup_phb(node, phb); pci_process_bridge_OF_ranges(phb, node, 0); pci_setup_phb_io(phb, index == 0); #ifdef CONFIG_PPC_PSERIES + /* XXX This code need serious fixing ... --BenH */ if (ppc64_interrupt_controller == IC_OPEN_PIC && pSeries_mpic) { int addr = root_size_cells * (index + 2) - 1; mpic_assign_isu(pSeries_mpic, index, opprop[addr]); @@ -381,22 +361,17 @@ unsigned long __init find_and_init_phbs(void) struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn) { - struct device_node *root = of_find_node_by_path("/"); - unsigned int root_size_cells = 0; struct pci_controller *phb; int primary; - root_size_cells = prom_n_size_cells(root); - primary = list_empty(&hose_list); phb = pcibios_alloc_controller(dn); if (!phb) return NULL; - setup_phb(dn, phb, root_size_cells); + setup_phb(dn, phb); pci_process_bridge_OF_ranges(phb, dn, primary); pci_setup_phb_io_dynamic(phb, primary); - of_node_put(root); pci_devs_phb_init_dynamic(phb); scan_phb(phb); diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index fc519cd90f7783..fc6f8ee9656fa8 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -432,7 +432,8 @@ static int __init parse_numa_properties(void) if (!memcell_buf || len <= 0) continue; - ranges = memory->n_addrs; + /* ranges in cell */ + ranges = (len >> 2) / (n_mem_addr_cells + n_mem_size_cells); new_range: /* these are order-sensitive, and modify the buffer pointer */ start = read_n_cells(n_mem_addr_cells, &memcell_buf); @@ -779,7 +780,8 @@ int hot_add_scn_to_nid(unsigned long scn_addr) if (!memcell_buf || len <= 0) continue; - ranges = memory->n_addrs; /* ranges in cell */ + /* ranges in cell */ + ranges = (len >> 2) / (n_mem_addr_cells + n_mem_size_cells); ha_new_range: start = read_n_cells(n_mem_addr_cells, &memcell_buf); size = read_n_cells(n_mem_size_cells, &memcell_buf); diff --git a/arch/powerpc/platforms/powermac/feature.c b/arch/powerpc/platforms/powermac/feature.c index b2928bbe92279e..b1f896952b1b72 100644 --- a/arch/powerpc/platforms/powermac/feature.c +++ b/arch/powerpc/platforms/powermac/feature.c @@ -1445,20 +1445,55 @@ static long g5_i2s_enable(struct device_node *node, long param, long value) /* Very crude implementation for now */ struct macio_chip *macio = &macio_chips[0]; unsigned long flags; - - if (value == 0) - return 0; /* don't disable yet */ + int cell; + u32 fcrs[3][3] = { + { 0, + K2_FCR1_I2S0_CELL_ENABLE | + K2_FCR1_I2S0_CLK_ENABLE_BIT | K2_FCR1_I2S0_ENABLE, + KL3_I2S0_CLK18_ENABLE + }, + { KL0_SCC_A_INTF_ENABLE, + K2_FCR1_I2S1_CELL_ENABLE | + K2_FCR1_I2S1_CLK_ENABLE_BIT | K2_FCR1_I2S1_ENABLE, + KL3_I2S1_CLK18_ENABLE + }, + { KL0_SCC_B_INTF_ENABLE, + SH_FCR1_I2S2_CELL_ENABLE | + SH_FCR1_I2S2_CLK_ENABLE_BIT | SH_FCR1_I2S2_ENABLE, + SH_FCR3_I2S2_CLK18_ENABLE + }, + }; + + if (macio->type != macio_keylargo2 /* && macio->type != macio_shasta*/) + return -ENODEV; + if (strncmp(node->name, "i2s-", 4)) + return -ENODEV; + cell = node->name[4] - 'a'; + switch(cell) { + case 0: + case 1: + break; +#if 0 + case 2: + if (macio->type == macio_shasta) + break; +#endif + default: + return -ENODEV; + } LOCK(flags); - MACIO_BIS(KEYLARGO_FCR3, KL3_CLK45_ENABLE | KL3_CLK49_ENABLE | - KL3_I2S0_CLK18_ENABLE); - udelay(10); - MACIO_BIS(KEYLARGO_FCR1, K2_FCR1_I2S0_CELL_ENABLE | - K2_FCR1_I2S0_CLK_ENABLE_BIT | K2_FCR1_I2S0_ENABLE); + if (value) { + MACIO_BIC(KEYLARGO_FCR0, fcrs[cell][0]); + MACIO_BIS(KEYLARGO_FCR1, fcrs[cell][1]); + MACIO_BIS(KEYLARGO_FCR3, fcrs[cell][2]); + } else { + MACIO_BIC(KEYLARGO_FCR3, fcrs[cell][2]); + MACIO_BIC(KEYLARGO_FCR1, fcrs[cell][1]); + MACIO_BIS(KEYLARGO_FCR0, fcrs[cell][0]); + } udelay(10); - MACIO_BIC(KEYLARGO_FCR1, K2_FCR1_I2S0_RESET); UNLOCK(flags); - udelay(10); return 0; } @@ -2960,26 +2995,6 @@ pmac_feature_init(void) set_initial_features(); } -int __init pmac_feature_late_init(void) -{ -#if 0 - struct device_node *np; - - /* Request some resources late */ - if (uninorth_node) - request_OF_resource(uninorth_node, 0, NULL); - np = find_devices("hammerhead"); - if (np) - request_OF_resource(np, 0, NULL); - np = find_devices("interrupt-controller"); - if (np) - request_OF_resource(np, 0, NULL); -#endif - return 0; -} - -device_initcall(pmac_feature_late_init); - #if 0 static void dump_HT_speeds(char *name, u32 cfg, u32 frq) { diff --git a/arch/powerpc/platforms/powermac/nvram.c b/arch/powerpc/platforms/powermac/nvram.c index 59e0e51cf663b7..3ebd045a335048 100644 --- a/arch/powerpc/platforms/powermac/nvram.c +++ b/arch/powerpc/platforms/powermac/nvram.c @@ -514,7 +514,7 @@ static void core99_nvram_sync(void) #endif } -static int __init core99_nvram_setup(struct device_node *dp) +static int __init core99_nvram_setup(struct device_node *dp, unsigned long addr) { int i; u32 gen_bank0, gen_bank1; @@ -528,7 +528,7 @@ static int __init core99_nvram_setup(struct device_node *dp) printk(KERN_ERR "nvram: can't allocate ram image\n"); return -ENOMEM; } - nvram_data = ioremap(dp->addrs[0].address, NVRAM_SIZE*2); + nvram_data = ioremap(addr, NVRAM_SIZE*2); nvram_naddrs = 1; /* Make sure we get the correct case */ DBG("nvram: Checking bank 0...\n"); @@ -570,34 +570,48 @@ static int __init core99_nvram_setup(struct device_node *dp) int __init pmac_nvram_init(void) { struct device_node *dp; + struct resource r1, r2; + unsigned int s1 = 0, s2 = 0; int err = 0; nvram_naddrs = 0; - dp = find_devices("nvram"); + dp = of_find_node_by_name(NULL, "nvram"); if (dp == NULL) { printk(KERN_ERR "Can't find NVRAM device\n"); return -ENODEV; } - nvram_naddrs = dp->n_addrs; + + /* Try to obtain an address */ + if (of_address_to_resource(dp, 0, &r1) == 0) { + nvram_naddrs = 1; + s1 = (r1.end - r1.start) + 1; + if (of_address_to_resource(dp, 1, &r2) == 0) { + nvram_naddrs = 2; + s2 = (r2.end - r2.start) + 1; + } + } + is_core_99 = device_is_compatible(dp, "nvram,flash"); - if (is_core_99) - err = core99_nvram_setup(dp); + if (is_core_99) { + err = core99_nvram_setup(dp, r1.start); + goto bail; + } + #ifdef CONFIG_PPC32 - else if (_machine == _MACH_chrp && nvram_naddrs == 1) { - nvram_data = ioremap(dp->addrs[0].address + isa_mem_base, - dp->addrs[0].size); + if (_machine == _MACH_chrp && nvram_naddrs == 1) { + nvram_data = ioremap(r1.start, s1); nvram_mult = 1; ppc_md.nvram_read_val = direct_nvram_read_byte; ppc_md.nvram_write_val = direct_nvram_write_byte; } else if (nvram_naddrs == 1) { - nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size); - nvram_mult = (dp->addrs[0].size + NVRAM_SIZE - 1) / NVRAM_SIZE; + nvram_data = ioremap(r1.start, s1); + nvram_mult = (s1 + NVRAM_SIZE - 1) / NVRAM_SIZE; ppc_md.nvram_read_val = direct_nvram_read_byte; ppc_md.nvram_write_val = direct_nvram_write_byte; } else if (nvram_naddrs == 2) { - nvram_addr = ioremap(dp->addrs[0].address, dp->addrs[0].size); - nvram_data = ioremap(dp->addrs[1].address, dp->addrs[1].size); + nvram_addr = ioremap(r1.start, s1); + nvram_data = ioremap(r2.start, s2); ppc_md.nvram_read_val = indirect_nvram_read_byte; ppc_md.nvram_write_val = indirect_nvram_write_byte; } else if (nvram_naddrs == 0 && sys_ctrler == SYS_CTRLER_PMU) { @@ -606,13 +620,15 @@ int __init pmac_nvram_init(void) ppc_md.nvram_read_val = pmu_nvram_read_byte; ppc_md.nvram_write_val = pmu_nvram_write_byte; #endif /* CONFIG_ADB_PMU */ - } -#endif - else { + } else { printk(KERN_ERR "Incompatible type of NVRAM\n"); - return -ENXIO; + err = -ENXIO; } - lookup_partitions(); +#endif /* CONFIG_PPC32 */ +bail: + of_node_put(dp); + if (err == 0) + lookup_partitions(); return err; } diff --git a/arch/powerpc/platforms/powermac/pci.c b/arch/powerpc/platforms/powermac/pci.c index e0b66f55a5f827..5aab261075de5c 100644 --- a/arch/powerpc/platforms/powermac/pci.c +++ b/arch/powerpc/platforms/powermac/pci.c @@ -285,15 +285,13 @@ static struct pci_ops chaos_pci_ops = }; static void __init setup_chaos(struct pci_controller *hose, - struct reg_property *addr) + struct resource *addr) { /* assume a `chaos' bridge */ hose->ops = &chaos_pci_ops; - hose->cfg_addr = ioremap(addr->address + 0x800000, 0x1000); - hose->cfg_data = ioremap(addr->address + 0xc00000, 0x1000); + hose->cfg_addr = ioremap(addr->start + 0x800000, 0x1000); + hose->cfg_data = ioremap(addr->start + 0xc00000, 0x1000); } -#else -#define setup_chaos(hose, addr) #endif /* CONFIG_PPC32 */ #ifdef CONFIG_PPC64 @@ -356,9 +354,11 @@ static unsigned long u3_ht_cfg_access(struct pci_controller* hose, /* For now, we don't self probe U3 HT bridge */ if (PCI_SLOT(devfn) == 0) return 0; - return ((unsigned long)hose->cfg_data) + U3_HT_CFA0(devfn, offset); + return ((unsigned long)hose->cfg_data) + + U3_HT_CFA0(devfn, offset); } else - return ((unsigned long)hose->cfg_data) + U3_HT_CFA1(bus, devfn, offset); + return ((unsigned long)hose->cfg_data) + + U3_HT_CFA1(bus, devfn, offset); } static int u3_ht_read_config(struct pci_bus *bus, unsigned int devfn, @@ -532,7 +532,8 @@ static void __init init_p2pbridge(void) } if (early_read_config_word(hose, bus, devfn, PCI_BRIDGE_CONTROL, &val) < 0) { - printk(KERN_ERR "init_p2pbridge: couldn't read bridge control\n"); + printk(KERN_ERR "init_p2pbridge: couldn't read bridge" + " control\n"); return; } val &= ~PCI_BRIDGE_CTL_MASTER_ABORT; @@ -576,36 +577,38 @@ static void __init fixup_nec_usb2(void) continue; early_read_config_dword(hose, bus, devfn, 0xe4, &data); if (data & 1UL) { - printk("Found NEC PD720100A USB2 chip with disabled EHCI, fixing up...\n"); + printk("Found NEC PD720100A USB2 chip with disabled" + " EHCI, fixing up...\n"); data &= ~1UL; early_write_config_dword(hose, bus, devfn, 0xe4, data); - early_write_config_byte(hose, bus, devfn | 2, PCI_INTERRUPT_LINE, + early_write_config_byte(hose, bus, + devfn | 2, PCI_INTERRUPT_LINE, nec->intrs[0].line); } } } static void __init setup_bandit(struct pci_controller *hose, - struct reg_property *addr) + struct resource *addr) { hose->ops = ¯isc_pci_ops; - hose->cfg_addr = ioremap(addr->address + 0x800000, 0x1000); - hose->cfg_data = ioremap(addr->address + 0xc00000, 0x1000); + hose->cfg_addr = ioremap(addr->start + 0x800000, 0x1000); + hose->cfg_data = ioremap(addr->start + 0xc00000, 0x1000); init_bandit(hose); } static int __init setup_uninorth(struct pci_controller *hose, - struct reg_property *addr) + struct resource *addr) { pci_assign_all_buses = 1; has_uninorth = 1; hose->ops = ¯isc_pci_ops; - hose->cfg_addr = ioremap(addr->address + 0x800000, 0x1000); - hose->cfg_data = ioremap(addr->address + 0xc00000, 0x1000); + hose->cfg_addr = ioremap(addr->start + 0x800000, 0x1000); + hose->cfg_data = ioremap(addr->start + 0xc00000, 0x1000); /* We "know" that the bridge at f2000000 has the PCI slots. */ - return addr->address == 0xf2000000; + return addr->start == 0xf2000000; } -#endif +#endif /* CONFIG_PPC32 */ #ifdef CONFIG_PPC64 static void __init setup_u3_agp(struct pci_controller* hose) @@ -722,7 +725,7 @@ static void __init setup_u3_ht(struct pci_controller* hose) hose->mem_resources[cur-1].end = res->start - 1; } } -#endif +#endif /* CONFIG_PPC64 */ /* * We assume that if we have a G3 powermac, we have one bridge called @@ -733,24 +736,17 @@ static int __init add_bridge(struct device_node *dev) { int len; struct pci_controller *hose; -#ifdef CONFIG_PPC32 - struct reg_property *addr; -#endif + struct resource rsrc; char *disp_name; int *bus_range; - int primary = 1; + int primary = 1, has_address = 0; DBG("Adding PCI host bridge %s\n", dev->full_name); -#ifdef CONFIG_PPC32 - /* XXX fix this */ - addr = (struct reg_property *) get_property(dev, "reg", &len); - if (addr == NULL || len < sizeof(*addr)) { - printk(KERN_WARNING "Can't use %s: no address\n", - dev->full_name); - return -ENODEV; - } -#endif + /* Fetch host bridge registers address */ + has_address = (of_address_to_resource(dev, 0, &rsrc) == 0); + + /* Get bus range if any */ bus_range = (int *) get_property(dev, "bus-range", &len); if (bus_range == NULL || len < 2 * sizeof(int)) { printk(KERN_WARNING "Can't get bus-range for %s, assume" @@ -770,6 +766,8 @@ static int __init add_bridge(struct device_node *dev) hose->last_busno = bus_range ? bus_range[1] : 0xff; disp_name = NULL; + + /* 64 bits only bridges */ #ifdef CONFIG_PPC64 if (device_is_compatible(dev, "u3-agp")) { setup_u3_agp(hose); @@ -782,25 +780,30 @@ static int __init add_bridge(struct device_node *dev) } printk(KERN_INFO "Found %s PCI host bridge. Firmware bus number: %d->%d\n", disp_name, hose->first_busno, hose->last_busno); -#else +#endif /* CONFIG_PPC64 */ + + /* 32 bits only bridges */ +#ifdef CONFIG_PPC32 if (device_is_compatible(dev, "uni-north")) { - primary = setup_uninorth(hose, addr); + primary = setup_uninorth(hose, &rsrc); disp_name = "UniNorth"; } else if (strcmp(dev->name, "pci") == 0) { /* XXX assume this is a mpc106 (grackle) */ setup_grackle(hose); disp_name = "Grackle (MPC106)"; } else if (strcmp(dev->name, "bandit") == 0) { - setup_bandit(hose, addr); + setup_bandit(hose, &rsrc); disp_name = "Bandit"; } else if (strcmp(dev->name, "chaos") == 0) { - setup_chaos(hose, addr); + setup_chaos(hose, &rsrc); disp_name = "Chaos"; primary = 0; } - printk(KERN_INFO "Found %s PCI host bridge at 0x%08lx. Firmware bus number: %d->%d\n", - disp_name, addr->address, hose->first_busno, hose->last_busno); -#endif + printk(KERN_INFO "Found %s PCI host bridge at 0x%08lx. " + "Firmware bus number: %d->%d\n", + disp_name, rsrc.start, hose->first_busno, hose->last_busno); +#endif /* CONFIG_PPC32 */ + DBG(" ->Hose at 0x%p, cfg_addr=0x%p,cfg_data=0x%p\n", hose, hose->cfg_addr, hose->cfg_data); @@ -814,8 +817,7 @@ static int __init add_bridge(struct device_node *dev) return 0; } -static void __init -pcibios_fixup_OF_interrupts(void) +static void __init pcibios_fixup_OF_interrupts(void) { struct pci_dev* dev = NULL; @@ -835,8 +837,7 @@ pcibios_fixup_OF_interrupts(void) } } -void __init -pmac_pcibios_fixup(void) +void __init pmac_pcibios_fixup(void) { /* Fixup interrupts according to OF tree */ pcibios_fixup_OF_interrupts(); diff --git a/arch/powerpc/platforms/powermac/pic.c b/arch/powerpc/platforms/powermac/pic.c index a36527c981335e..dbb524a851aa76 100644 --- a/arch/powerpc/platforms/powermac/pic.c +++ b/arch/powerpc/platforms/powermac/pic.c @@ -5,8 +5,8 @@ * in a separate file * * Copyright (C) 1997 Paul Mackerras (paulus@samba.org) - * - * Maintained by Benjamin Herrenschmidt (benh@kernel.crashing.org) + * Copyright (C) 2005 Benjamin Herrenschmidt (benh@kernel.crashing.org) + * IBM, Corp. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -54,12 +54,7 @@ struct pmac_irq_hw { }; /* Default addresses */ -static volatile struct pmac_irq_hw *pmac_irq_hw[4] = { - (struct pmac_irq_hw *) 0xf3000020, - (struct pmac_irq_hw *) 0xf3000010, - (struct pmac_irq_hw *) 0xf4000020, - (struct pmac_irq_hw *) 0xf4000010, -}; +static volatile struct pmac_irq_hw __iomem *pmac_irq_hw[4]; #define GC_LEVEL_MASK 0x3ff00000 #define OHARE_LEVEL_MASK 0x1ff00000 @@ -82,8 +77,7 @@ static unsigned long ppc_lost_interrupts[NR_MASK_WORDS]; * since it can lose interrupts (see pmac_set_irq_mask). * -- Cort */ -void -__set_lost(unsigned long irq_nr, int nokick) +void __set_lost(unsigned long irq_nr, int nokick) { if (!test_and_set_bit(irq_nr, ppc_lost_interrupts)) { atomic_inc(&ppc_n_lost_interrupts); @@ -92,8 +86,7 @@ __set_lost(unsigned long irq_nr, int nokick) } } -static void -pmac_mask_and_ack_irq(unsigned int irq_nr) +static void pmac_mask_and_ack_irq(unsigned int irq_nr) { unsigned long bit = 1UL << (irq_nr & 0x1f); int i = irq_nr >> 5; @@ -224,8 +217,7 @@ static irqreturn_t gatwick_action(int cpl, void *dev_id, struct pt_regs *regs) return IRQ_NONE; } -int -pmac_get_irq(struct pt_regs *regs) +static int pmac_get_irq(struct pt_regs *regs) { int irq; unsigned long bits = 0; @@ -256,34 +248,40 @@ pmac_get_irq(struct pt_regs *regs) /* This routine will fix some missing interrupt values in the device tree * on the gatwick mac-io controller used by some PowerBooks + * + * Walking of OF nodes could use a bit more fixing up here, but it's not + * very important as this is all boot time code on static portions of the + * device-tree. + * + * However, the modifications done to "intrs" will have to be removed and + * replaced with proper updates of the "interrupts" properties or + * AAPL,interrupts, yet to be decided, once the dynamic parsing is there. */ -static void __init -pmac_fix_gatwick_interrupts(struct device_node *gw, int irq_base) +static void __init pmac_fix_gatwick_interrupts(struct device_node *gw, + int irq_base) { struct device_node *node; int count; memset(gatwick_int_pool, 0, sizeof(gatwick_int_pool)); - node = gw->child; count = 0; - while(node) - { + for (node = NULL; (node = of_get_next_child(gw, node)) != NULL;) { /* Fix SCC */ - if (strcasecmp(node->name, "escc") == 0) - if (node->child) { - if (node->child->n_intrs < 3) { - node->child->intrs = &gatwick_int_pool[count]; - count += 3; - } - node->child->n_intrs = 3; - node->child->intrs[0].line = 15+irq_base; - node->child->intrs[1].line = 4+irq_base; - node->child->intrs[2].line = 5+irq_base; - printk(KERN_INFO "irq: fixed SCC on second controller (%d,%d,%d)\n", - node->child->intrs[0].line, - node->child->intrs[1].line, - node->child->intrs[2].line); + if ((strcasecmp(node->name, "escc") == 0) && node->child) { + if (node->child->n_intrs < 3) { + node->child->intrs = &gatwick_int_pool[count]; + count += 3; } + node->child->n_intrs = 3; + node->child->intrs[0].line = 15+irq_base; + node->child->intrs[1].line = 4+irq_base; + node->child->intrs[2].line = 5+irq_base; + printk(KERN_INFO "irq: fixed SCC on gatwick" + " (%d,%d,%d)\n", + node->child->intrs[0].line, + node->child->intrs[1].line, + node->child->intrs[2].line); + } /* Fix media-bay & left SWIM */ if (strcasecmp(node->name, "media-bay") == 0) { struct device_node* ya_node; @@ -292,12 +290,11 @@ pmac_fix_gatwick_interrupts(struct device_node *gw, int irq_base) node->intrs = &gatwick_int_pool[count++]; node->n_intrs = 1; node->intrs[0].line = 29+irq_base; - printk(KERN_INFO "irq: fixed media-bay on second controller (%d)\n", - node->intrs[0].line); + printk(KERN_INFO "irq: fixed media-bay on gatwick" + " (%d)\n", node->intrs[0].line); ya_node = node->child; - while(ya_node) - { + while(ya_node) { if (strcasecmp(ya_node->name, "floppy") == 0) { if (ya_node->n_intrs < 2) { ya_node->intrs = &gatwick_int_pool[count]; @@ -323,7 +320,6 @@ pmac_fix_gatwick_interrupts(struct device_node *gw, int irq_base) ya_node = ya_node->sibling; } } - node = node->sibling; } if (count > 10) { printk("WARNING !! Gatwick interrupt pool overflow\n"); @@ -338,45 +334,41 @@ pmac_fix_gatwick_interrupts(struct device_node *gw, int irq_base) * controller. If we find this second ohare, set it up and fix the * interrupt value in the device tree for the ethernet chip. */ -static int __init enable_second_ohare(void) +static void __init enable_second_ohare(struct device_node *np) { unsigned char bus, devfn; unsigned short cmd; - unsigned long addr; - struct device_node *irqctrler = find_devices("pci106b,7"); struct device_node *ether; - if (irqctrler == NULL || irqctrler->n_addrs <= 0) - return -1; - addr = (unsigned long) ioremap(irqctrler->addrs[0].address, 0x40); - pmac_irq_hw[1] = (volatile struct pmac_irq_hw *)(addr + 0x20); - max_irqs = 64; - if (pci_device_from_OF_node(irqctrler, &bus, &devfn) == 0) { - struct pci_controller* hose = pci_find_hose_for_OF_device(irqctrler); - if (!hose) - printk(KERN_ERR "Can't find PCI hose for OHare2 !\n"); - else { - early_read_config_word(hose, bus, devfn, PCI_COMMAND, &cmd); - cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; - cmd &= ~PCI_COMMAND_IO; - early_write_config_word(hose, bus, devfn, PCI_COMMAND, cmd); + /* This code doesn't strictly belong here, it could be part of + * either the PCI initialisation or the feature code. It's kept + * here for historical reasons. + */ + if (pci_device_from_OF_node(np, &bus, &devfn) == 0) { + struct pci_controller* hose = + pci_find_hose_for_OF_device(np); + if (!hose) { + printk(KERN_ERR "Can't find PCI hose for OHare2 !\n"); + return; } + early_read_config_word(hose, bus, devfn, PCI_COMMAND, &cmd); + cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; + cmd &= ~PCI_COMMAND_IO; + early_write_config_word(hose, bus, devfn, PCI_COMMAND, cmd); } /* Fix interrupt for the modem/ethernet combo controller. The number - in the device tree (27) is bogus (correct for the ethernet-only - board but not the combo ethernet/modem board). - The real interrupt is 28 on the second controller -> 28+32 = 60. - */ - ether = find_devices("pci1011,14"); + * in the device tree (27) is bogus (correct for the ethernet-only + * board but not the combo ethernet/modem board). + * The real interrupt is 28 on the second controller -> 28+32 = 60. + */ + ether = of_find_node_by_name(NULL, "pci1011,14"); if (ether && ether->n_intrs > 0) { ether->intrs[0].line = 60; printk(KERN_INFO "irq: Fixed ethernet IRQ to %d\n", ether->intrs[0].line); } - - /* Return the interrupt number of the cascade */ - return irqctrler->intrs[0].line; + of_node_put(ether); } #ifdef CONFIG_XMON @@ -394,189 +386,233 @@ static struct irqaction gatwick_cascade_action = { .mask = CPU_MASK_NONE, .name = "cascade", }; -#endif /* CONFIG_PPC32 */ -static int pmac_u3_cascade(struct pt_regs *regs, void *data) +static void __init pmac_pic_probe_oldstyle(void) { - return mpic_get_one_irq((struct mpic *)data, regs); -} - -void __init pmac_pic_init(void) -{ - struct device_node *irqctrler = NULL; - struct device_node *irqctrler2 = NULL; - struct device_node *np; -#ifdef CONFIG_PPC32 int i; - unsigned long addr; int irq_cascade = -1; -#endif - struct mpic *mpic1, *mpic2; + struct device_node *master = NULL; + struct device_node *slave = NULL; + u8 __iomem *addr; + struct resource r; - /* We first try to detect Apple's new Core99 chipset, since mac-io - * is quite different on those machines and contains an IBM MPIC2. - */ - np = find_type_devices("open-pic"); - while (np) { - if (np->parent && !strcmp(np->parent->name, "u3")) - irqctrler2 = np; - else - irqctrler = np; - np = np->next; - } - if (irqctrler != NULL && irqctrler->n_addrs > 0) { - unsigned char senses[128]; - - printk(KERN_INFO "PowerMac using OpenPIC irq controller at 0x%08x\n", - (unsigned int)irqctrler->addrs[0].address); - pmac_call_feature(PMAC_FTR_ENABLE_MPIC, irqctrler, 0, 0); - - prom_get_irq_senses(senses, 0, 128); - mpic1 = mpic_alloc(irqctrler->addrs[0].address, - MPIC_PRIMARY | MPIC_WANTS_RESET, - 0, 0, 128, 252, senses, 128, " OpenPIC "); - BUG_ON(mpic1 == NULL); - mpic_init(mpic1); - - if (irqctrler2 != NULL && irqctrler2->n_intrs > 0 && - irqctrler2->n_addrs > 0) { - printk(KERN_INFO "Slave OpenPIC at 0x%08x hooked on IRQ %d\n", - (u32)irqctrler2->addrs[0].address, - irqctrler2->intrs[0].line); - - pmac_call_feature(PMAC_FTR_ENABLE_MPIC, irqctrler2, 0, 0); - prom_get_irq_senses(senses, 128, 128 + 124); - - /* We don't need to set MPIC_BROKEN_U3 here since we don't have - * hypertransport interrupts routed to it - */ - mpic2 = mpic_alloc(irqctrler2->addrs[0].address, - MPIC_BIG_ENDIAN | MPIC_WANTS_RESET, - 0, 128, 124, 0, senses, 124, - " U3-MPIC "); - BUG_ON(mpic2 == NULL); - mpic_init(mpic2); - mpic_setup_cascade(irqctrler2->intrs[0].line, - pmac_u3_cascade, mpic2); - } -#if defined(CONFIG_XMON) && defined(CONFIG_PPC32) - { - struct device_node* pswitch; - int nmi_irq; - - pswitch = find_devices("programmer-switch"); - if (pswitch && pswitch->n_intrs) { - nmi_irq = pswitch->intrs[0].line; - mpic_irq_set_priority(nmi_irq, 9); - setup_irq(nmi_irq, &xmon_action); - } - } -#endif /* defined(CONFIG_XMON) && defined(CONFIG_PPC32) */ - return; - } - irqctrler = NULL; + /* Set our get_irq function */ + ppc_md.get_irq = pmac_get_irq; -#ifdef CONFIG_PPC32 - /* Get the level/edge settings, assume if it's not - * a Grand Central nor an OHare, then it's an Heathrow - * (or Paddington). + /* + * Find the interrupt controller type & node */ - ppc_md.get_irq = pmac_get_irq; - if (find_devices("gc")) + + if ((master = of_find_node_by_name(NULL, "gc")) != NULL) { + max_irqs = max_real_irqs = 32; level_mask[0] = GC_LEVEL_MASK; - else if (find_devices("ohare")) { + } else if ((master = of_find_node_by_name(NULL, "ohare")) != NULL) { + max_irqs = max_real_irqs = 32; level_mask[0] = OHARE_LEVEL_MASK; + /* We might have a second cascaded ohare */ - level_mask[1] = OHARE_LEVEL_MASK; - } else { + slave = of_find_node_by_name(NULL, "pci106b,7"); + if (slave) { + max_irqs = 64; + level_mask[1] = OHARE_LEVEL_MASK; + enable_second_ohare(slave); + } + } else if ((master = of_find_node_by_name(NULL, "mac-io")) != NULL) { + max_irqs = max_real_irqs = 64; level_mask[0] = HEATHROW_LEVEL_MASK; level_mask[1] = 0; + /* We might have a second cascaded heathrow */ - level_mask[2] = HEATHROW_LEVEL_MASK; - level_mask[3] = 0; - } + slave = of_find_node_by_name(master, "mac-io"); + + /* Check ordering of master & slave */ + if (device_is_compatible(master, "gatwick")) { + struct device_node *tmp; + BUG_ON(slave == NULL); + tmp = master; + master = slave; + slave = tmp; + } - /* - * G3 powermacs and 1999 G3 PowerBooks have 64 interrupts, - * 1998 G3 Series PowerBooks have 128, - * other powermacs have 32. - * The combo ethernet/modem card for the Powerstar powerbooks - * (2400/3400/3500, ohare based) has a second ohare chip - * effectively making a total of 64. - */ - max_irqs = max_real_irqs = 32; - irqctrler = find_devices("mac-io"); - if (irqctrler) - { - max_real_irqs = 64; - if (irqctrler->next) + /* We found a slave */ + if (slave) { max_irqs = 128; - else - max_irqs = 64; + level_mask[2] = HEATHROW_LEVEL_MASK; + level_mask[3] = 0; + pmac_fix_gatwick_interrupts(slave, max_real_irqs); + } } + BUG_ON(master == NULL); + + /* Set the handler for the main PIC */ for ( i = 0; i < max_real_irqs ; i++ ) irq_desc[i].handler = &pmac_pic; - /* get addresses of first controller */ - if (irqctrler) { - if (irqctrler->n_addrs > 0) { - addr = (unsigned long) - ioremap(irqctrler->addrs[0].address, 0x40); - for (i = 0; i < 2; ++i) - pmac_irq_hw[i] = (volatile struct pmac_irq_hw*) - (addr + (2 - i) * 0x10); - } + /* Get addresses of first controller if we have a node for it */ + BUG_ON(of_address_to_resource(master, 0, &r)); - /* get addresses of second controller */ - irqctrler = irqctrler->next; - if (irqctrler && irqctrler->n_addrs > 0) { - addr = (unsigned long) - ioremap(irqctrler->addrs[0].address, 0x40); - for (i = 2; i < 4; ++i) - pmac_irq_hw[i] = (volatile struct pmac_irq_hw*) - (addr + (4 - i) * 0x10); - irq_cascade = irqctrler->intrs[0].line; - if (device_is_compatible(irqctrler, "gatwick")) - pmac_fix_gatwick_interrupts(irqctrler, max_real_irqs); - } - } else { - /* older powermacs have a GC (grand central) or ohare at - f3000000, with interrupt control registers at f3000020. */ - addr = (unsigned long) ioremap(0xf3000000, 0x40); - pmac_irq_hw[0] = (volatile struct pmac_irq_hw *) (addr + 0x20); + /* Map interrupts of primary controller */ + addr = (u8 __iomem *) ioremap(r.start, 0x40); + i = 0; + pmac_irq_hw[i++] = (volatile struct pmac_irq_hw __iomem *) + (addr + 0x20); + if (max_real_irqs > 32) + pmac_irq_hw[i++] = (volatile struct pmac_irq_hw __iomem *) + (addr + 0x10); + of_node_put(master); + + printk(KERN_INFO "irq: Found primary Apple PIC %s for %d irqs\n", + master->full_name, max_real_irqs); + + /* Map interrupts of cascaded controller */ + if (slave && !of_address_to_resource(slave, 0, &r)) { + addr = (u8 __iomem *)ioremap(r.start, 0x40); + pmac_irq_hw[i++] = (volatile struct pmac_irq_hw __iomem *) + (addr + 0x20); + if (max_irqs > 64) + pmac_irq_hw[i++] = + (volatile struct pmac_irq_hw __iomem *) + (addr + 0x10); + irq_cascade = slave->intrs[0].line; + + printk(KERN_INFO "irq: Found slave Apple PIC %s for %d irqs" + " cascade: %d\n", slave->full_name, + max_irqs - max_real_irqs, irq_cascade); } - - /* PowerBooks 3400 and 3500 can have a second controller in a second - ohare chip, on the combo ethernet/modem card */ - if (machine_is_compatible("AAPL,3400/2400") - || machine_is_compatible("AAPL,3500")) - irq_cascade = enable_second_ohare(); + of_node_put(slave); /* disable all interrupts in all controllers */ for (i = 0; i * 32 < max_irqs; ++i) out_le32(&pmac_irq_hw[i]->enable, 0); + /* mark level interrupts */ for (i = 0; i < max_irqs; i++) if (level_mask[i >> 5] & (1UL << (i & 0x1f))) irq_desc[i].status = IRQ_LEVEL; - /* get interrupt line of secondary interrupt controller */ - if (irq_cascade >= 0) { - printk(KERN_INFO "irq: secondary controller on irq %d\n", - (int)irq_cascade); + /* Setup handlers for secondary controller and hook cascade irq*/ + if (slave) { for ( i = max_real_irqs ; i < max_irqs ; i++ ) irq_desc[i].handler = &gatwick_pic; setup_irq(irq_cascade, &gatwick_cascade_action); } - printk("System has %d possible interrupts\n", max_irqs); - if (max_irqs != max_real_irqs) - printk(KERN_DEBUG "%d interrupts on main controller\n", - max_real_irqs); - + printk(KERN_INFO "irq: System has %d possible interrupts\n", max_irqs); #ifdef CONFIG_XMON setup_irq(20, &xmon_action); -#endif /* CONFIG_XMON */ -#endif /* CONFIG_PPC32 */ +#endif +} +#endif /* CONFIG_PPC32 */ + +static int pmac_u3_cascade(struct pt_regs *regs, void *data) +{ + return mpic_get_one_irq((struct mpic *)data, regs); +} + +static void __init pmac_pic_setup_mpic_nmi(struct mpic *mpic) +{ +#if defined(CONFIG_XMON) && defined(CONFIG_PPC32) + struct device_node* pswitch; + int nmi_irq; + + pswitch = of_find_node_by_name(NULL, "programmer-switch"); + if (pswitch && pswitch->n_intrs) { + nmi_irq = pswitch->intrs[0].line; + mpic_irq_set_priority(nmi_irq, 9); + setup_irq(nmi_irq, &xmon_action); + } + of_node_put(pswitch); +#endif /* defined(CONFIG_XMON) && defined(CONFIG_PPC32) */ +} + +static int __init pmac_pic_probe_mpic(void) +{ + struct mpic *mpic1, *mpic2; + struct device_node *np, *master = NULL, *slave = NULL; + unsigned char senses[128]; + struct resource r; + + /* We can have up to 2 MPICs cascaded */ + for (np = NULL; (np = of_find_node_by_type(np, "open-pic")) + != NULL;) { + if (master == NULL && + get_property(np, "interrupt-parent", NULL) != NULL) + master = of_node_get(np); + else if (slave == NULL) + slave = of_node_get(np); + if (master && slave) + break; + } + + /* Check for bogus setups */ + if (master == NULL && slave != NULL) { + master = slave; + slave = NULL; + } + + /* Not found, default to good old pmac pic */ + if (master == NULL) + return -ENODEV; + + /* Set master handler */ + ppc_md.get_irq = mpic_get_irq; + + /* Setup master */ + BUG_ON(of_address_to_resource(master, 0, &r)); + pmac_call_feature(PMAC_FTR_ENABLE_MPIC, master, 0, 0); + prom_get_irq_senses(senses, 0, 128); + mpic1 = mpic_alloc(r.start, MPIC_PRIMARY | MPIC_WANTS_RESET, + 0, 0, 128, 252, senses, 128, " OpenPIC "); + BUG_ON(mpic1 == NULL); + mpic_init(mpic1); + + /* Install NMI if any */ + pmac_pic_setup_mpic_nmi(mpic1); + + of_node_put(master); + + /* No slave, let's go out */ + if (slave == NULL || slave->n_intrs < 1) + return 0; + + /* Setup slave, failures are non-fatal */ + if (of_address_to_resource(slave, 0, &r)) { + printk(KERN_ERR "Can't get address of MPIC %s\n", + slave->full_name); + return 0; + } + pmac_call_feature(PMAC_FTR_ENABLE_MPIC, slave, 0, 0); + prom_get_irq_senses(senses, 128, 128 + 124); + + /* We don't need to set MPIC_BROKEN_U3 here since we don't have + * hypertransport interrupts routed to it, at least not on currently + * supported machines, that may change. + */ + mpic2 = mpic_alloc(r.start, MPIC_BIG_ENDIAN | MPIC_WANTS_RESET, + 0, 128, 124, 0, senses, 124, " U3-MPIC "); + if (mpic2 == NULL) { + printk(KERN_ERR "Can't create slave MPIC %s\n", + slave->full_name); + return 0; + } + mpic_init(mpic2); + mpic_setup_cascade(slave->intrs[0].line, pmac_u3_cascade, mpic2); + + of_node_put(slave); + return 0; +} + + +void __init pmac_pic_init(void) +{ + /* We first try to detect Apple's new Core99 chipset, since mac-io + * is quite different on those machines and contains an IBM MPIC2. + */ + if (pmac_pic_probe_mpic() == 0) + return; + +#ifdef CONFIG_PPC32 + pmac_pic_probe_oldstyle(); +#endif } #if defined(CONFIG_PM) && defined(CONFIG_PPC32) diff --git a/arch/powerpc/platforms/powermac/pmac.h b/arch/powerpc/platforms/powermac/pmac.h index 2ad25e13423ea0..21c7b0f8f32996 100644 --- a/arch/powerpc/platforms/powermac/pmac.h +++ b/arch/powerpc/platforms/powermac/pmac.h @@ -42,10 +42,6 @@ extern void pmac_ide_init_hwif_ports(hw_regs_t *hw, unsigned long data_port, unsigned long ctrl_port, int *irq); extern int pmac_nvram_init(void); - -extern struct hw_interrupt_type pmac_pic; - -void pmac_pic_init(void); -int pmac_get_irq(struct pt_regs *regs); +extern void pmac_pic_init(void); #endif /* __PMAC_H__ */ diff --git a/arch/powerpc/platforms/powermac/setup.c b/arch/powerpc/platforms/powermac/setup.c index c0638e47c298e3..18c5620f87fa5d 100644 --- a/arch/powerpc/platforms/powermac/setup.c +++ b/arch/powerpc/platforms/powermac/setup.c @@ -75,7 +75,6 @@ #include #include #include -#include #include #include @@ -751,7 +750,7 @@ struct machdep_calls __initdata pmac_md = { .init_early = pmac_init_early, .show_cpuinfo = pmac_show_cpuinfo, .init_IRQ = pmac_pic_init, - .get_irq = mpic_get_irq, /* changed later */ + .get_irq = NULL, /* changed later */ .pcibios_fixup = pmac_pcibios_fixup, .restart = pmac_restart, .power_off = pmac_power_off, diff --git a/arch/powerpc/platforms/powermac/time.c b/arch/powerpc/platforms/powermac/time.c index feb0a94e78192e..5d9afa1fa02d0d 100644 --- a/arch/powerpc/platforms/powermac/time.c +++ b/arch/powerpc/platforms/powermac/time.c @@ -258,15 +258,20 @@ int __init via_calibrate_decr(void) volatile unsigned char __iomem *via; int count = VIA_TIMER_FREQ_6 / 100; unsigned int dstart, dend; + struct resource rsrc; - vias = find_devices("via-cuda"); + vias = of_find_node_by_name(NULL, "via-cuda"); if (vias == 0) - vias = find_devices("via-pmu"); + vias = of_find_node_by_name(NULL, "via-pmu"); if (vias == 0) - vias = find_devices("via"); - if (vias == 0 || vias->n_addrs == 0) + vias = of_find_node_by_name(NULL, "via"); + if (vias == 0 || of_address_to_resource(vias, 0, &rsrc)) return 0; - via = ioremap(vias->addrs[0].address, vias->addrs[0].size); + via = ioremap(rsrc.start, rsrc.end - rsrc.start + 1); + if (via == NULL) { + printk(KERN_ERR "Failed to map VIA for timer calibration !\n"); + return 0; + } /* set timer 1 for continuous interrupts */ out_8(&via[ACR], (via[ACR] & ~T1MODE) | T1MODE_CONT); diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c index af7cb2bfd67051..01f042f6f1c428 100644 --- a/drivers/block/swim3.c +++ b/drivers/block/swim3.c @@ -1083,23 +1083,33 @@ static int swim3_add_device(struct device_node *swim) { struct device_node *mediabay; struct floppy_state *fs = &floppy_states[floppy_count]; + struct resource res_reg, res_dma; - if (swim->n_addrs < 2) - { - printk(KERN_INFO "swim3: expecting 2 addrs (n_addrs:%d, n_intrs:%d)\n", - swim->n_addrs, swim->n_intrs); + if (of_address_to_resource(swim, 0, &res_reg) || + of_address_to_resource(swim, 1, &res_dma)) { + printk(KERN_ERR "swim3: Can't get addresses\n"); return -EINVAL; } - - if (swim->n_intrs < 2) - { - printk(KERN_INFO "swim3: expecting 2 intrs (n_addrs:%d, n_intrs:%d)\n", - swim->n_addrs, swim->n_intrs); + if (request_mem_region(res_reg.start, res_reg.end - res_reg.start + 1, + " (reg)") == NULL) { + printk(KERN_ERR "swim3: Can't request register space\n"); + return -EINVAL; + } + if (request_mem_region(res_dma.start, res_dma.end - res_dma.start + 1, + " (dma)") == NULL) { + release_mem_region(res_reg.start, + res_reg.end - res_reg.start + 1); + printk(KERN_ERR "swim3: Can't request DMA space\n"); return -EINVAL; } - if (!request_OF_resource(swim, 0, NULL)) { - printk(KERN_INFO "swim3: can't request IO resource !\n"); + if (swim->n_intrs < 2) { + printk(KERN_INFO "swim3: expecting 2 intrs (n_intrs:%d)\n", + swim->n_intrs); + release_mem_region(res_reg.start, + res_reg.end - res_reg.start + 1); + release_mem_region(res_dma.start, + res_dma.end - res_dma.start + 1); return -EINVAL; } @@ -1110,10 +1120,8 @@ static int swim3_add_device(struct device_node *swim) memset(fs, 0, sizeof(*fs)); spin_lock_init(&fs->lock); fs->state = idle; - fs->swim3 = (struct swim3 __iomem *) - ioremap(swim->addrs[0].address, 0x200); - fs->dma = (struct dbdma_regs __iomem *) - ioremap(swim->addrs[1].address, 0x200); + fs->swim3 = (struct swim3 __iomem *)ioremap(res_reg.start, 0x200); + fs->dma = (struct dbdma_regs __iomem *)ioremap(res_dma.start, 0x200); fs->swim3_intr = swim->intrs[0].line; fs->dma_intr = swim->intrs[1].line; fs->cur_cyl = -1; diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c index 16b28357885b38..a8d3bc0a9c5c06 100644 --- a/drivers/ide/ppc/pmac.c +++ b/drivers/ide/ppc/pmac.c @@ -1271,7 +1271,7 @@ static int pmac_ide_setup_device(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif) { struct device_node *np = pmif->node; - int *bidp, i; + int *bidp; pmif->cable_80 = 0; pmif->broken_dma = pmif->broken_dma_warn = 0; @@ -1430,7 +1430,7 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_device_id *match) pmif = &pmac_ide[i]; hwif = &ide_hwifs[i]; - if (mdev->ofdev.node->n_addrs == 0) { + if (macio_resource_count(mdev) == 0) { printk(KERN_WARNING "ide%d: no address for %s\n", i, mdev->ofdev.node->full_name); return -ENXIO; diff --git a/drivers/macintosh/macio_asic.c b/drivers/macintosh/macio_asic.c index 0137ff239f1352..2a545ceb523b05 100644 --- a/drivers/macintosh/macio_asic.c +++ b/drivers/macintosh/macio_asic.c @@ -256,42 +256,42 @@ static int macio_resource_quirks(struct device_node *np, struct resource *res, { 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; + if (index == 0 && !strcmp(np->name, "gc")) res->end = res->start + 0x1ffff; - } + /* Airport has bogus resource 2 */ if (index >= 2 && !strcmp(np->name, "radio")) return 1; + +#ifndef CONFIG_PPC64 /* DBDMAs may have bogus sizes */ - if ((res->start & 0x0001f000) == 0x00008000) { - np->addrs[index].size = 0x100; + if ((res->start & 0x0001f000) == 0x00008000) res->end = res->start + 0xff; - } +#endif /* CONFIG_PPC64 */ + /* 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; + if (index == 0 && (res->end - res->start) > 0xfff) res->end = res->start + 0xfff; - } - if (index == 1 && np->addrs[1].size > 0x100) { - np->addrs[1].size = 0x100; + if (index == 1 && (res->end - res->start) > 0xff) res->end = res->start + 0xff; - } } } return 0; @@ -349,7 +349,7 @@ static void macio_setup_resources(struct macio_dev *dev, /* 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 -l */ + */ if (insert_resource(parent_res, res)) { printk(KERN_WARNING "Can't request resource " "%d for MacIO device %s\n", diff --git a/drivers/macintosh/mediabay.c b/drivers/macintosh/mediabay.c index b856bb67169cf9..8dbf2852bae03f 100644 --- a/drivers/macintosh/mediabay.c +++ b/drivers/macintosh/mediabay.c @@ -647,6 +647,7 @@ static int __devinit media_bay_attach(struct macio_dev *mdev, const struct of_de struct media_bay_info* bay; u32 __iomem *regbase; struct device_node *ofnode; + unsigned long base; int i; ofnode = mdev->ofdev.node; @@ -656,10 +657,11 @@ static int __devinit media_bay_attach(struct macio_dev *mdev, const struct of_de if (macio_request_resources(mdev, "media-bay")) return -EBUSY; /* Media bay registers are located at the beginning of the - * mac-io chip, we get the parent address for now (hrm...) + * mac-io chip, for now, we trick and align down the first + * resource passed in */ - regbase = (u32 __iomem *) - ioremap(ofnode->parent->addrs[0].address, 0x100); + base = macio_resource_start(mdev, 0) & 0xffff0000u; + regbase = (u32 __iomem *)ioremap(base, 0x100); if (regbase == NULL) { macio_release_resources(mdev); return -ENOMEM; diff --git a/drivers/macintosh/via-cuda.c b/drivers/macintosh/via-cuda.c index 18ff770ea668e8..2d9d79150403b1 100644 --- a/drivers/macintosh/via-cuda.c +++ b/drivers/macintosh/via-cuda.c @@ -193,10 +193,6 @@ static int __init via_cuda_start(void) if (via == NULL) return -ENODEV; -#ifdef CONFIG_PPC - request_OF_resource(vias, 0, NULL); -#endif - if (request_irq(CUDA_IRQ, cuda_interrupt, 0, "ADB", cuda_interrupt)) { printk(KERN_ERR "cuda_init: can't get irq %d\n", CUDA_IRQ); return -EAGAIN; diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index d6dabee55f2fee..79c7b44a94eef5 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c @@ -298,7 +298,7 @@ static struct backlight_controller pmu_backlight_controller = { int __init find_via_pmu(void) { - phys_addr_t taddr; + u64 taddr; u32 *reg; if (via != 0) @@ -337,7 +337,7 @@ int __init find_via_pmu(void) else if (device_is_compatible(vias->parent, "Keylargo") || device_is_compatible(vias->parent, "K2-Keylargo")) { struct device_node *gpiop; - phys_addr_t gaddr = 0; + u64 gaddr = OF_BAD_ADDR; pmu_kind = PMU_KEYLARGO_BASED; pmu_has_adb = (find_type_devices("adb") != NULL); @@ -352,7 +352,7 @@ int __init find_via_pmu(void) reg = (u32 *)get_property(gpiop, "reg", NULL); if (reg) gaddr = of_translate_address(gpiop, reg); - if (gaddr != 0) + if (gaddr != OF_BAD_ADDR) gpio_reg = ioremap(gaddr, 0x10); } if (gpio_reg == NULL) @@ -479,9 +479,6 @@ static int __init via_pmu_dev_init(void) if (vias == NULL) return -ENODEV; -#ifndef CONFIG_PPC64 - request_OF_resource(vias, 0, NULL); -#endif #ifdef CONFIG_PMAC_BACKLIGHT /* Enable backlight */ register_backlight_controller(&pmu_backlight_controller, NULL, "pmu"); diff --git a/drivers/scsi/mac53c94.c b/drivers/scsi/mac53c94.c index 932dcf0366ebf9..311a4122bd7094 100644 --- a/drivers/scsi/mac53c94.c +++ b/drivers/scsi/mac53c94.c @@ -432,11 +432,12 @@ static int mac53c94_probe(struct macio_dev *mdev, const struct of_device_id *mat struct Scsi_Host *host; void *dma_cmd_space; unsigned char *clkprop; - int proplen; + int proplen, rc = -ENODEV; if (macio_resource_count(mdev) != 2 || macio_irq_count(mdev) != 2) { - printk(KERN_ERR "mac53c94: expected 2 addrs and intrs (got %d/%d)\n", - node->n_addrs, node->n_intrs); + printk(KERN_ERR "mac53c94: expected 2 addrs and intrs" + " (got %d/%d)\n", + macio_resource_count(mdev), macio_irq_count(mdev)); return -ENODEV; } @@ -448,6 +449,7 @@ static int mac53c94_probe(struct macio_dev *mdev, const struct of_device_id *mat host = scsi_host_alloc(&mac53c94_template, sizeof(struct fsc_state)); if (host == NULL) { printk(KERN_ERR "mac53c94: couldn't register host"); + rc = -ENOMEM; goto out_release; } @@ -486,6 +488,7 @@ static int mac53c94_probe(struct macio_dev *mdev, const struct of_device_id *mat if (dma_cmd_space == 0) { printk(KERN_ERR "mac53c94: couldn't allocate dma " "command space for %s\n", node->full_name); + rc = -ENOMEM; goto out_free; } state->dma_cmds = (struct dbdma_cmd *)DBDMA_ALIGN(dma_cmd_space); @@ -495,18 +498,21 @@ static int mac53c94_probe(struct macio_dev *mdev, const struct of_device_id *mat mac53c94_init(state); - if (request_irq(state->intr, do_mac53c94_interrupt, 0, "53C94", state)) { + if (request_irq(state->intr, do_mac53c94_interrupt, 0, "53C94",state)) { printk(KERN_ERR "mac53C94: can't get irq %d for %s\n", state->intr, node->full_name); goto out_free_dma; } - /* XXX FIXME: handle failure */ - scsi_add_host(host, &mdev->ofdev.dev); - scsi_scan_host(host); + rc = scsi_add_host(host, &mdev->ofdev.dev); + if (rc != 0) + goto out_release_irq; + scsi_scan_host(host); return 0; + out_release_irq: + free_irq(state->intr, state); out_free_dma: kfree(state->dma_cmd_space); out_free: @@ -518,7 +524,7 @@ static int mac53c94_probe(struct macio_dev *mdev, const struct of_device_id *mat out_release: macio_release_resources(mdev); - return -ENODEV; + return rc; } static int mac53c94_remove(struct macio_dev *mdev) diff --git a/drivers/scsi/mesh.c b/drivers/scsi/mesh.c index bdccf73cf9fe24..d6d2125f904445 100644 --- a/drivers/scsi/mesh.c +++ b/drivers/scsi/mesh.c @@ -1869,7 +1869,8 @@ static int mesh_probe(struct macio_dev *mdev, const struct of_device_id *match) if (macio_resource_count(mdev) != 2 || macio_irq_count(mdev) != 2) { printk(KERN_ERR "mesh: expected 2 addrs and 2 intrs" - " (got %d,%d)\n", mesh->n_addrs, mesh->n_intrs); + " (got %d,%d)\n", macio_resource_count(mdev), + macio_irq_count(mdev)); return -ENODEV; } diff --git a/drivers/serial/pmac_zilog.c b/drivers/serial/pmac_zilog.c index 5ddd8ab1f108af..ea24129eb6b9f5 100644 --- a/drivers/serial/pmac_zilog.c +++ b/drivers/serial/pmac_zilog.c @@ -1431,11 +1431,14 @@ static int __init pmz_init_port(struct uart_pmac_port *uap) char name[1]; } *slots; int len; + struct resource r_ports, r_rxdma, r_txdma; /* * Request & map chip registers */ - uap->port.mapbase = np->addrs[0].address; + if (of_address_to_resource(np, 0, &r_ports)) + return -ENODEV; + uap->port.mapbase = r_ports.start; uap->port.membase = ioremap(uap->port.mapbase, 0x1000); uap->control_reg = uap->port.membase; @@ -1445,16 +1448,20 @@ static int __init pmz_init_port(struct uart_pmac_port *uap) * Request & map DBDMA registers */ #ifdef HAS_DBDMA - if (np->n_addrs >= 3 && np->n_intrs >= 3) + if (of_address_to_resource(np, 1, &r_txdma) == 0 && + of_address_to_resource(np, 2, &r_rxdma) == 0) uap->flags |= PMACZILOG_FLAG_HAS_DMA; +#else + memset(&r_txdma, 0, sizeof(struct resource)); + memset(&r_rxdma, 0, sizeof(struct resource)); #endif if (ZS_HAS_DMA(uap)) { - uap->tx_dma_regs = ioremap(np->addrs[np->n_addrs - 2].address, 0x1000); + uap->tx_dma_regs = ioremap(r_txdma.start, 0x100); if (uap->tx_dma_regs == NULL) { uap->flags &= ~PMACZILOG_FLAG_HAS_DMA; goto no_dma; } - uap->rx_dma_regs = ioremap(np->addrs[np->n_addrs - 1].address, 0x1000); + uap->rx_dma_regs = ioremap(r_rxdma.start, 0x100); if (uap->rx_dma_regs == NULL) { iounmap(uap->tx_dma_regs); uap->tx_dma_regs = NULL; diff --git a/drivers/video/controlfb.c b/drivers/video/controlfb.c index 403d17377f8db6..03798e9c882d81 100644 --- a/drivers/video/controlfb.c +++ b/drivers/video/controlfb.c @@ -133,12 +133,6 @@ static int controlfb_mmap(struct fb_info *info, struct file *file, static int controlfb_set_par (struct fb_info *info); static int controlfb_check_var (struct fb_var_screeninfo *var, struct fb_info *info); -/* - * inititialization - */ -int control_init(void); -void control_setup(char *); - /******************** Prototypes for internal functions **********************/ static void set_control_clock(unsigned char *params); @@ -550,9 +544,46 @@ static void control_set_hardware(struct fb_info_control *p, struct fb_par_contro /* - * Called from fbmem.c for probing & initializing + * Parse user speficied options (`video=controlfb:') */ -int __init control_init(void) +static void __init control_setup(char *options) +{ + char *this_opt; + + if (!options || !*options) + return; + + while ((this_opt = strsep(&options, ",")) != NULL) { + if (!strncmp(this_opt, "vmode:", 6)) { + int vmode = simple_strtoul(this_opt+6, NULL, 0); + if (vmode > 0 && vmode <= VMODE_MAX && + control_mac_modes[vmode - 1].m[1] >= 0) + default_vmode = vmode; + } else if (!strncmp(this_opt, "cmode:", 6)) { + int depth = simple_strtoul(this_opt+6, NULL, 0); + switch (depth) { + case CMODE_8: + case CMODE_16: + case CMODE_32: + default_cmode = depth; + break; + case 8: + default_cmode = CMODE_8; + break; + case 15: + case 16: + default_cmode = CMODE_16; + break; + case 24: + case 32: + default_cmode = CMODE_32; + break; + } + } + } +} + +static int __init control_init(void) { struct device_node *dp; char *option = NULL; @@ -651,15 +682,16 @@ static void __init find_vram_size(struct fb_info_control *p) static int __init control_of_init(struct device_node *dp) { struct fb_info_control *p; - unsigned long addr; - int i; + struct resource fb_res, reg_res; if (control_fb) { printk(KERN_ERR "controlfb: only one control is supported\n"); return -ENXIO; } - if(dp->n_addrs != 2) { - printk(KERN_ERR "expecting 2 address for control (got %d)", dp->n_addrs); + + if (of_pci_address_to_resource(dp, 2, &fb_res) || + of_pci_address_to_resource(dp, 1, ®_res)) { + printk(KERN_ERR "can't get 2 addresses for control\n"); return -ENXIO; } p = kmalloc(sizeof(*p), GFP_KERNEL); @@ -669,18 +701,12 @@ static int __init control_of_init(struct device_node *dp) memset(p, 0, sizeof(*p)); /* Map in frame buffer and registers */ - for (i = 0; i < dp->n_addrs; ++i) { - addr = dp->addrs[i].address; - if (dp->addrs[i].size >= 0x800000) { - p->fb_orig_base = addr; - p->fb_orig_size = dp->addrs[i].size; - /* use the big-endian aperture (??) */ - p->frame_buffer_phys = addr + 0x800000; - } else { - p->control_regs_phys = addr; - p->control_regs_size = dp->addrs[i].size; - } - } + p->fb_orig_base = fb_res.start; + p->fb_orig_size = fb_res.end - fb_res.start + 1; + /* use the big-endian aperture (??) */ + p->frame_buffer_phys = fb_res.start + 0x800000; + p->control_regs_phys = reg_res.start; + p->control_regs_size = reg_res.end - reg_res.start + 1; if (!p->fb_orig_base || !request_mem_region(p->fb_orig_base,p->fb_orig_size,"controlfb")) { @@ -1059,43 +1085,3 @@ static void control_cleanup(void) } -/* - * Parse user speficied options (`video=controlfb:') - */ -void __init control_setup(char *options) -{ - char *this_opt; - - if (!options || !*options) - return; - - while ((this_opt = strsep(&options, ",")) != NULL) { - if (!strncmp(this_opt, "vmode:", 6)) { - int vmode = simple_strtoul(this_opt+6, NULL, 0); - if (vmode > 0 && vmode <= VMODE_MAX && - control_mac_modes[vmode - 1].m[1] >= 0) - default_vmode = vmode; - } else if (!strncmp(this_opt, "cmode:", 6)) { - int depth = simple_strtoul(this_opt+6, NULL, 0); - switch (depth) { - case CMODE_8: - case CMODE_16: - case CMODE_32: - default_cmode = depth; - break; - case 8: - default_cmode = CMODE_8; - break; - case 15: - case 16: - default_cmode = CMODE_16; - break; - case 24: - case 32: - default_cmode = CMODE_32; - break; - } - } - } -} - diff --git a/drivers/video/offb.c b/drivers/video/offb.c index 00d87f5bb7be0e..ad1434e3f2277f 100644 --- a/drivers/video/offb.c +++ b/drivers/video/offb.c @@ -223,6 +223,7 @@ static int offb_blank(int blank, struct fb_info *info) int __init offb_init(void) { struct device_node *dp = NULL, *boot_disp = NULL; + #if defined(CONFIG_BOOTX_TEXT) && defined(CONFIG_PPC32) struct device_node *macos_display = NULL; #endif @@ -234,60 +235,54 @@ int __init offb_init(void) if (boot_infos != 0) { unsigned long addr = (unsigned long) boot_infos->dispDeviceBase; + u32 *addrp; + u64 daddr, dsize; + unsigned int flags; + /* find the device node corresponding to the macos display */ while ((dp = of_find_node_by_type(dp, "display"))) { int i; - /* - * Grrr... It looks like the MacOS ATI driver - * munges the assigned-addresses property (but - * the AAPL,address value is OK). - */ - if (strncmp(dp->name, "ATY,", 4) == 0 - && dp->n_addrs == 1) { - unsigned int *ap = - (unsigned int *) get_property(dp, - "AAPL,address", - NULL); - if (ap != NULL) { - dp->addrs[0].address = *ap; - dp->addrs[0].size = 0x01000000; - } - } /* - * The LTPro on the Lombard powerbook has no addresses - * on the display nodes, they are on their parent. + * Look for an AAPL,address property first. */ - if (dp->n_addrs == 0 - && device_is_compatible(dp, "ATY,264LTPro")) { - int na; - unsigned int *ap = (unsigned int *) - get_property(dp, "AAPL,address", &na); - if (ap != 0) - for (na /= sizeof(unsigned int); - na > 0; --na, ++ap) - if (*ap <= addr - && addr < - *ap + 0x1000000) - goto foundit; + unsigned int na; + unsigned int *ap = + (unsigned int *)get_property(dp, "AAPL,address", + &na); + if (ap != 0) { + for (na /= sizeof(unsigned int); na > 0; + --na, ++ap) + if (*ap <= addr && + addr < *ap + 0x1000000) { + macos_display = dp; + goto foundit; + } } /* * See if the display address is in one of the address * ranges for this display. */ - for (i = 0; i < dp->n_addrs; ++i) { - if (dp->addrs[i].address <= addr - && addr < - dp->addrs[i].address + - dp->addrs[i].size) + i = 0; + for (;;) { + addrp = of_get_address(dp, i++, &dsize, &flags); + if (addrp == NULL) break; + if (!(flags & IORESOURCE_MEM)) + continue; + daddr = of_translate_address(dp, addrp); + if (daddr == OF_BAD_ADDR) + continue; + if (daddr <= addr && addr < (daddr + dsize)) { + macos_display = dp; + goto foundit; + } } - if (i < dp->n_addrs) { - foundit: + foundit: + if (macos_display) { printk(KERN_INFO "MacOS display is %s\n", dp->full_name); - macos_display = dp; break; } } @@ -326,8 +321,10 @@ static void __init offb_init_nodriver(struct device_node *dp) int *pp, i; unsigned int len; int width = 640, height = 480, depth = 8, pitch; - unsigned int rsize, *up; - unsigned long address = 0; + unsigned int flags, rsize, *up; + u64 address = OF_BAD_ADDR; + u32 *addrp; + u64 asize; if ((pp = (int *) get_property(dp, "depth", &len)) != NULL && len == sizeof(int)) @@ -363,7 +360,7 @@ static void __init offb_init_nodriver(struct device_node *dp) break; } if (pdev) { - for (i = 0; i < 6 && address == 0; i++) { + for (i = 0; i < 6 && address == OF_BAD_ADDR; i++) { if ((pci_resource_flags(pdev, i) & IORESOURCE_MEM) && (pci_resource_len(pdev, i) >= rsize)) @@ -374,27 +371,33 @@ static void __init offb_init_nodriver(struct device_node *dp) } #endif /* CONFIG_PCI */ - if (address == 0 && - (up = (unsigned *) get_property(dp, "address", &len)) != NULL && - len == sizeof(unsigned)) - address = (u_long) * up; - if (address == 0) { - for (i = 0; i < dp->n_addrs; ++i) - if (dp->addrs[i].size >= - pitch * height * depth / 8) - break; - if (i >= dp->n_addrs) { + /* This one is dodgy, we may drop it ... */ + if (address == OF_BAD_ADDR && + (up = (unsigned *) get_property(dp, "address", &len)) != NULL && + len == sizeof(unsigned int)) + address = (u64) * up; + + if (address == OF_BAD_ADDR) { + for (i = 0; (addrp = of_get_address(dp, i, &asize, &flags)) + != NULL; i++) { + if (!(flags & IORESOURCE_MEM)) + continue; + if (asize >= pitch * height * depth / 8) + break; + } + if (addrp == NULL) { printk(KERN_ERR "no framebuffer address found for %s\n", dp->full_name); return; } - - address = (u_long) dp->addrs[i].address; - -#ifdef CONFIG_PPC64 - address += ((struct pci_dn *)dp->data)->phb->pci_mem_offset; -#endif + address = of_translate_address(dp, addrp); + if (address == OF_BAD_ADDR) { + printk(KERN_ERR + "can't translate framebuffer address for %s\n", + dp->full_name); + return; + } /* kludge for valkyrie */ if (strcmp(dp->name, "valkyrie") == 0) @@ -459,7 +462,9 @@ static void __init offb_init_fb(const char *name, const char *full_name, par->cmap_type = cmap_unknown; if (depth == 8) { - /* XXX kludge for ati */ + + /* Palette hacks disabled for now */ +#if 0 if (dp && !strncmp(name, "ATY,Rage128", 11)) { unsigned long regbase = dp->addrs[2].address; par->cmap_adr = ioremap(regbase, 0x1FFF); @@ -490,6 +495,7 @@ static void __init offb_init_fb(const char *name, const char *full_name, par->cmap_adr = ioremap(regbase + 0x6000, 0x1000); par->cmap_type = cmap_gxt2000; } +#endif fix->visual = par->cmap_adr ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_STATIC_PSEUDOCOLOR; } else diff --git a/drivers/video/platinumfb.c b/drivers/video/platinumfb.c index ba0af1b66bb601..335e3746555983 100644 --- a/drivers/video/platinumfb.c +++ b/drivers/video/platinumfb.c @@ -69,6 +69,8 @@ struct fb_info_platinum { unsigned long total_vram; int clktype; int dactype; + + struct resource rsrc_fb, rsrc_reg; }; /* @@ -97,9 +99,6 @@ static int platinum_var_to_par(struct fb_var_screeninfo *var, * Interface used by the world */ -int platinumfb_init(void); -int platinumfb_setup(char*); - static struct fb_ops platinumfb_ops = { .owner = THIS_MODULE, .fb_check_var = platinumfb_check_var, @@ -485,7 +484,7 @@ static int platinum_var_to_par(struct fb_var_screeninfo *var, /* * Parse user speficied options (`video=platinumfb:') */ -int __init platinumfb_setup(char *options) +static int __init platinumfb_setup(char *options) { char *this_opt; @@ -526,19 +525,15 @@ int __init platinumfb_setup(char *options) #define invalidate_cache(addr) #endif -static int __devinit platinumfb_probe(struct of_device* odev, const struct of_device_id *match) +static int __devinit platinumfb_probe(struct of_device* odev, + const struct of_device_id *match) { struct device_node *dp = odev->node; struct fb_info *info; struct fb_info_platinum *pinfo; - unsigned long addr, size; volatile __u8 *fbuffer; - int i, bank0, bank1, bank2, bank3, rc; + int bank0, bank1, bank2, bank3, rc; - if (dp->n_addrs != 2) { - printk(KERN_ERR "expecting 2 address for platinum (got %d)", dp->n_addrs); - return -ENXIO; - } printk(KERN_INFO "platinumfb: Found Apple Platinum video hardware\n"); info = framebuffer_alloc(sizeof(*pinfo), &odev->dev); @@ -546,26 +541,39 @@ static int __devinit platinumfb_probe(struct of_device* odev, const struct of_de return -ENOMEM; pinfo = info->par; - /* Map in frame buffer and registers */ - for (i = 0; i < dp->n_addrs; ++i) { - addr = dp->addrs[i].address; - size = dp->addrs[i].size; - /* Let's assume we can request either all or nothing */ - if (!request_mem_region(addr, size, "platinumfb")) { - framebuffer_release(info); - return -ENXIO; - } - if (size >= 0x400000) { - /* frame buffer - map only 4MB */ - pinfo->frame_buffer_phys = addr; - pinfo->frame_buffer = __ioremap(addr, 0x400000, _PAGE_WRITETHRU); - pinfo->base_frame_buffer = pinfo->frame_buffer; - } else { - /* registers */ - pinfo->platinum_regs_phys = addr; - pinfo->platinum_regs = ioremap(addr, size); - } + if (of_address_to_resource(dp, 0, &pinfo->rsrc_reg) || + of_address_to_resource(dp, 1, &pinfo->rsrc_fb)) { + printk(KERN_ERR "platinumfb: Can't get resources\n"); + framebuffer_release(info); + return -ENXIO; } + if (!request_mem_region(pinfo->rsrc_reg.start, + pinfo->rsrc_reg.start - + pinfo->rsrc_reg.end + 1, + "platinumfb registers")) { + framebuffer_release(info); + return -ENXIO; + } + if (!request_mem_region(pinfo->rsrc_fb.start, + pinfo->rsrc_fb.start + - pinfo->rsrc_fb.end + 1, + "platinumfb framebuffer")) { + release_mem_region(pinfo->rsrc_reg.start, + pinfo->rsrc_reg.end - + pinfo->rsrc_reg.start + 1); + framebuffer_release(info); + return -ENXIO; + } + + /* frame buffer - map only 4MB */ + pinfo->frame_buffer_phys = pinfo->rsrc_fb.start; + pinfo->frame_buffer = __ioremap(pinfo->rsrc_fb.start, 0x400000, + _PAGE_WRITETHRU); + pinfo->base_frame_buffer = pinfo->frame_buffer; + + /* registers */ + pinfo->platinum_regs_phys = pinfo->rsrc_reg.start; + pinfo->platinum_regs = ioremap(pinfo->rsrc_reg.start, 0x1000); pinfo->cmap_regs_phys = 0xf301b000; /* XXX not in prom? */ request_mem_region(pinfo->cmap_regs_phys, 0x1000, "platinumfb cmap"); @@ -628,18 +636,16 @@ static int __devexit platinumfb_remove(struct of_device* odev) { struct fb_info *info = dev_get_drvdata(&odev->dev); struct fb_info_platinum *pinfo = info->par; - struct device_node *dp = odev->node; - unsigned long addr, size; - int i; unregister_framebuffer (info); /* Unmap frame buffer and registers */ - for (i = 0; i < dp->n_addrs; ++i) { - addr = dp->addrs[i].address; - size = dp->addrs[i].size; - release_mem_region(addr, size); - } + release_mem_region(pinfo->rsrc_fb.start, + pinfo->rsrc_fb.end - + pinfo->rsrc_fb.start + 1); + release_mem_region(pinfo->rsrc_reg.start, + pinfo->rsrc_reg.end - + pinfo->rsrc_reg.start + 1); iounmap(pinfo->frame_buffer); iounmap(pinfo->platinum_regs); release_mem_region(pinfo->cmap_regs_phys, 0x1000); @@ -666,7 +672,7 @@ static struct of_platform_driver platinum_driver = .remove = platinumfb_remove, }; -int __init platinumfb_init(void) +static int __init platinumfb_init(void) { #ifndef MODULE char *option = NULL; @@ -680,7 +686,7 @@ int __init platinumfb_init(void) return 0; } -void __exit platinumfb_exit(void) +static void __exit platinumfb_exit(void) { of_unregister_driver(&platinum_driver); } diff --git a/drivers/video/valkyriefb.c b/drivers/video/valkyriefb.c index ce97ec8eae9756..2bdeb4baa9529c 100644 --- a/drivers/video/valkyriefb.c +++ b/drivers/video/valkyriefb.c @@ -342,19 +342,19 @@ int __init valkyriefb_init(void) #else /* ppc (!CONFIG_MAC) */ { struct device_node *dp; + struct resource r; - dp = find_devices("valkyrie"); + dp = of_find_node_by_name(NULL, "valkyrie"); if (dp == 0) return 0; - if (dp->n_addrs != 1) { - printk(KERN_ERR "expecting 1 address for valkyrie (got %d)\n", - dp->n_addrs); + if (of_address_to_resource(dp, 0, &r)) { + printk(KERN_ERR "can't find address for valkyrie\n"); return 0; } - frame_buffer_phys = dp->addrs[0].address; - cmap_regs_phys = dp->addrs[0].address+0x304000; + frame_buffer_phys = r.start; + cmap_regs_phys = r.start + 0x304000; flags = _PAGE_WRITETHRU; } #endif /* ppc (!CONFIG_MAC) */ diff --git a/include/asm-powerpc/keylargo.h b/include/asm-powerpc/keylargo.h index a669a3f0f5a269..334d4c9356f353 100644 --- a/include/asm-powerpc/keylargo.h +++ b/include/asm-powerpc/keylargo.h @@ -232,10 +232,12 @@ #define K2_FCR1_I2S0_RESET 0x00000800 #define K2_FCR1_I2S0_CLK_ENABLE_BIT 0x00001000 #define K2_FCR1_I2S0_ENABLE 0x00002000 - #define K2_FCR1_PCI1_CLK_ENABLE 0x00004000 #define K2_FCR1_FW_CLK_ENABLE 0x00008000 #define K2_FCR1_FW_RESET_N 0x00010000 +#define K2_FCR1_I2S1_CELL_ENABLE 0x00020000 +#define K2_FCR1_I2S1_CLK_ENABLE_BIT 0x00080000 +#define K2_FCR1_I2S1_ENABLE 0x00100000 #define K2_FCR1_GMAC_CLK_ENABLE 0x00400000 #define K2_FCR1_GMAC_POWER_DOWN 0x00800000 #define K2_FCR1_GMAC_RESET_N 0x01000000 @@ -246,3 +248,9 @@ #define K2_FCR1_UATA_RESET_N 0x40000000 #define K2_FCR1_UATA_CHOOSE_CLK66 0x80000000 +/* Shasta definitions */ +#define SH_FCR1_I2S2_CELL_ENABLE 0x00000010 +#define SH_FCR1_I2S2_CLK_ENABLE_BIT 0x00000040 +#define SH_FCR1_I2S2_ENABLE 0x00000080 +#define SH_FCR3_I2S2_CLK18_ENABLE 0x00008000 + diff --git a/include/asm-powerpc/prom.h b/include/asm-powerpc/prom.h index 73d27bad55fa88..329e9bf62260cd 100644 --- a/include/asm-powerpc/prom.h +++ b/include/asm-powerpc/prom.h @@ -65,49 +65,11 @@ struct boot_param_header typedef u32 phandle; typedef u32 ihandle; -struct address_range { - unsigned long space; - unsigned long address; - unsigned long size; -}; - struct interrupt_info { int line; int sense; /* +ve/-ve logic, edge or level, etc. */ }; -struct pci_address { - u32 a_hi; - u32 a_mid; - u32 a_lo; -}; - -struct isa_address { - u32 a_hi; - u32 a_lo; -}; - -struct isa_range { - struct isa_address isa_addr; - struct pci_address pci_addr; - unsigned int size; -}; - -struct reg_property { - unsigned long address; - unsigned long size; -}; - -struct reg_property32 { - unsigned int address; - unsigned int size; -}; - -struct reg_property64 { - u64 address; - u64 size; -}; - struct property { char *name; int length; @@ -120,8 +82,6 @@ struct device_node { char *type; phandle node; phandle linux_phandle; - int n_addrs; - struct address_range *addrs; int n_intrs; struct interrupt_info *intrs; char *full_name; diff --git a/sound/oss/dmasound/dmasound_awacs.c b/sound/oss/dmasound/dmasound_awacs.c index cebd881b91ae97..74f975676ccb22 100644 --- a/sound/oss/dmasound/dmasound_awacs.c +++ b/sound/oss/dmasound/dmasound_awacs.c @@ -125,6 +125,7 @@ static int awacs_rate_index; static int awacs_subframe; static struct device_node* awacs_node; static struct device_node* i2s_node; +static struct resource awacs_rsrc[3]; static char awacs_name[64]; static int awacs_revision; @@ -667,9 +668,12 @@ static void PMacIrqCleanup(void) iounmap(awacs_txdma); iounmap(awacs_rxdma); - release_OF_resource(awacs_node, 0); - release_OF_resource(awacs_node, 1); - release_OF_resource(awacs_node, 2); + release_mem_region(awacs_rsrc[0].start, + awacs_rsrc[0].end - awacs_rsrc[0].start + 1); + release_mem_region(awacs_rsrc[1].start, + awacs_rsrc[1].end - awacs_rsrc[1].start + 1); + release_mem_region(awacs_rsrc[2].start, + awacs_rsrc[2].end - awacs_rsrc[2].start + 1); kfree(awacs_tx_cmd_space); kfree(awacs_rx_cmd_space); @@ -2863,46 +2867,58 @@ printk("dmasound_pmac: couldn't find a Codec we can handle\n"); * other info if necessary (early AWACS we want to read chip ids) */ - if (io->n_addrs < 3 || io->n_intrs < 3) { + if (of_get_address(io, 2, NULL, NULL) == NULL || io->n_intrs < 3) { /* OK - maybe we need to use the 'awacs' node (on earlier * machines). - */ + */ if (awacs_node) { io = awacs_node ; - if (io->n_addrs < 3 || io->n_intrs < 3) { - printk("dmasound_pmac: can't use %s" - " (%d addrs, %d intrs)\n", - io->full_name, io->n_addrs, io->n_intrs); + if (of_get_address(io, 2, NULL, NULL) == NULL || + io->n_intrs < 3) { + printk("dmasound_pmac: can't use %s\n", + io->full_name); return -ENODEV; } - } else { - printk("dmasound_pmac: can't use %s (%d addrs, %d intrs)\n", - io->full_name, io->n_addrs, io->n_intrs); - } + } else + printk("dmasound_pmac: can't use %s\n", io->full_name); } - if (!request_OF_resource(io, 0, NULL)) { + if (of_address_to_resource(io, 0, &awacs_rsrc[0]) || + request_mem_region(awacs_rsrc[0].start, + awacs_rsrc[0].end - awacs_rsrc[0].start + 1, + " (IO)") == NULL) { printk(KERN_ERR "dmasound: can't request IO resource !\n"); return -ENODEV; } - if (!request_OF_resource(io, 1, " (tx dma)")) { - release_OF_resource(io, 0); - printk(KERN_ERR "dmasound: can't request TX DMA resource !\n"); + if (of_address_to_resource(io, 1, &awacs_rsrc[1]) || + request_mem_region(awacs_rsrc[1].start, + awacs_rsrc[1].end - awacs_rsrc[1].start + 1, + " (tx dma)") == NULL) { + release_mem_region(awacs_rsrc[0].start, + awacs_rsrc[0].end - awacs_rsrc[0].start + 1); + printk(KERN_ERR "dmasound: can't request Tx DMA resource !\n"); return -ENODEV; } - - if (!request_OF_resource(io, 2, " (rx dma)")) { - release_OF_resource(io, 0); - release_OF_resource(io, 1); - printk(KERN_ERR "dmasound: can't request RX DMA resource !\n"); + if (of_address_to_resource(io, 2, &awacs_rsrc[2]) || + request_mem_region(awacs_rsrc[2].start, + awacs_rsrc[2].end - awacs_rsrc[2].start + 1, + " (rx dma)") == NULL) { + release_mem_region(awacs_rsrc[0].start, + awacs_rsrc[0].end - awacs_rsrc[0].start + 1); + release_mem_region(awacs_rsrc[1].start, + awacs_rsrc[1].end - awacs_rsrc[1].start + 1); + printk(KERN_ERR "dmasound: can't request Rx DMA resource !\n"); return -ENODEV; } awacs_beep_dev = input_allocate_device(); if (!awacs_beep_dev) { - release_OF_resource(io, 0); - release_OF_resource(io, 1); - release_OF_resource(io, 2); + release_mem_region(awacs_rsrc[0].start, + awacs_rsrc[0].end - awacs_rsrc[0].start + 1); + release_mem_region(awacs_rsrc[1].start, + awacs_rsrc[1].end - awacs_rsrc[1].start + 1); + release_mem_region(awacs_rsrc[2].start, + awacs_rsrc[2].end - awacs_rsrc[2].start + 1); printk(KERN_ERR "dmasound: can't allocate input device !\n"); return -ENOMEM; } @@ -2916,11 +2932,11 @@ printk("dmasound_pmac: couldn't find a Codec we can handle\n"); /* all OF versions I've seen use this value */ if (i2s_node) - i2s = ioremap(io->addrs[0].address, 0x1000); + i2s = ioremap(awacs_rsrc[0].start, 0x1000); else - awacs = ioremap(io->addrs[0].address, 0x1000); - awacs_txdma = ioremap(io->addrs[1].address, 0x100); - awacs_rxdma = ioremap(io->addrs[2].address, 0x100); + awacs = ioremap(awacs_rsrc[0].start, 0x1000); + awacs_txdma = ioremap(awacs_rsrc[1].start, 0x100); + awacs_rxdma = ioremap(awacs_rsrc[2].start, 0x100); /* first of all make sure that the chip is powered up....*/ pmac_call_feature(PMAC_FTR_SOUND_CHIP_ENABLE, io, 0, 1); @@ -3083,9 +3099,10 @@ printk("dmasound_pmac: Awacs/Screamer Codec Mfct: %d Rev %d\n", mfg, rev); struct device_node* mio; macio_base = NULL; for (mio = io->parent; mio; mio = mio->parent) { - if (strcmp(mio->name, "mac-io") == 0 - && mio->n_addrs > 0) { - macio_base = ioremap(mio->addrs[0].address, 0x40); + if (strcmp(mio->name, "mac-io") == 0) { + struct resource r; + if (of_address_to_resource(mio, 0, &r) == 0) + macio_base = ioremap(r.start, 0x40); break; } } diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c index 9b2b00fdc1ae92..a642e4cfcf45e2 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c @@ -803,21 +803,17 @@ static int snd_pmac_free(struct snd_pmac *chip) iounmap(chip->playback.dma); if (chip->capture.dma) iounmap(chip->capture.dma); -#ifndef CONFIG_PPC64 + if (chip->node) { int i; - for (i = 0; i < 3; i++) { - if (chip->of_requested & (1 << i)) { - if (chip->is_k2) - release_OF_resource(chip->node->parent, - i); - else - release_OF_resource(chip->node, i); - } + if (chip->requested & (1 << i)) + release_mem_region(chip->rsrc[i].start, + chip->rsrc[i].end - + chip->rsrc[i].start + 1); } } -#endif /* CONFIG_PPC64 */ + if (chip->pdev) pci_dev_put(chip->pdev); kfree(chip); @@ -991,6 +987,11 @@ static int __init snd_pmac_detect(struct snd_pmac *chip) chip->can_byte_swap = 0; /* FIXME: check this */ chip->control_mask = MASK_IEPC | 0x11;/* disable IEE */ break; + default: + printk(KERN_ERR "snd: Unknown layout ID 0x%x\n", + layout_id); + return -ENODEV; + } } prop = (unsigned int *)get_property(sound, "device-id", NULL); @@ -1175,46 +1176,69 @@ int __init snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return) } np = chip->node; + chip->requested = 0; if (chip->is_k2) { - if (np->parent->n_addrs < 2 || np->n_intrs < 3) { + static char *rnames[] = { + "Sound Control", "Sound DMA" }; + if (np->n_intrs < 3) { err = -ENODEV; goto __error; } - for (i = 0; i < 2; i++) { -#ifndef CONFIG_PPC64 - static char *name[2] = { "- Control", "- DMA" }; - if (! request_OF_resource(np->parent, i, name[i])) { - snd_printk(KERN_ERR "pmac: can't request resource %d!\n", i); + for (i = 0; i < 2; i ++) { + if (of_address_to_resource(np->parent, i, + &chip->rsrc[i])) { + printk(KERN_ERR "snd: can't translate rsrc " + " %d (%s)\n", i, rnames[i]); + err = -ENODEV; + goto __error; + } + if (request_mem_region(chip->rsrc[i].start, + chip->rsrc[i].end - + chip->rsrc[i].start + 1, + rnames[i]) == NULL) { + printk(KERN_ERR "snd: can't request rsrc " + " %d (%s: 0x%08lx:%08lx)\n", + i, rnames[i], chip->rsrc[i].start, + chip->rsrc[i].end); err = -ENODEV; goto __error; } - chip->of_requested |= (1 << i); -#endif /* CONFIG_PPC64 */ - ctrl_addr = np->parent->addrs[0].address; - txdma_addr = np->parent->addrs[1].address; - rxdma_addr = txdma_addr + 0x100; + chip->requested |= (1 << i); } - + ctrl_addr = chip->rsrc[0].start; + txdma_addr = chip->rsrc[1].start; + rxdma_addr = txdma_addr + 0x100; } else { - if (np->n_addrs < 3 || np->n_intrs < 3) { + static char *rnames[] = { + "Sound Control", "Sound Tx DMA", "Sound Rx DMA" }; + if (np->n_intrs < 3) { err = -ENODEV; goto __error; } - - for (i = 0; i < 3; i++) { -#ifndef CONFIG_PPC64 - static char *name[3] = { "- Control", "- Tx DMA", "- Rx DMA" }; - if (! request_OF_resource(np, i, name[i])) { - snd_printk(KERN_ERR "pmac: can't request resource %d!\n", i); + for (i = 0; i < 3; i ++) { + if (of_address_to_resource(np->parent, i, + &chip->rsrc[i])) { + printk(KERN_ERR "snd: can't translate rsrc " + " %d (%s)\n", i, rnames[i]); + err = -ENODEV; + goto __error; + } + if (request_mem_region(chip->rsrc[i].start, + chip->rsrc[i].end - + chip->rsrc[i].start + 1, + rnames[i]) == NULL) { + printk(KERN_ERR "snd: can't request rsrc " + " %d (%s: 0x%08lx:%08lx)\n", + i, rnames[i], chip->rsrc[i].start, + chip->rsrc[i].end); err = -ENODEV; goto __error; } - chip->of_requested |= (1 << i); -#endif /* CONFIG_PPC64 */ - ctrl_addr = np->addrs[0].address; - txdma_addr = np->addrs[1].address; - rxdma_addr = np->addrs[2].address; + chip->requested |= (1 << i); } + ctrl_addr = chip->rsrc[0].start; + txdma_addr = chip->rsrc[1].start; + rxdma_addr = chip->rsrc[2].start; } chip->awacs = ioremap(ctrl_addr, 0x1000); @@ -1266,9 +1290,11 @@ int __init snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return) } else if (chip->is_pbook_G3) { struct device_node* mio; for (mio = chip->node->parent; mio; mio = mio->parent) { - if (strcmp(mio->name, "mac-io") == 0 - && mio->n_addrs > 0) { - chip->macio_base = ioremap(mio->addrs[0].address, 0x40); + if (strcmp(mio->name, "mac-io") == 0) { + struct resource r; + if (of_address_to_resource(mio, 0, &r) == 0) + chip->macio_base = + ioremap(r.start, 0x40); break; } } diff --git a/sound/ppc/pmac.h b/sound/ppc/pmac.h index 086da7a1890902..3a9bd4dbb9a6bf 100644 --- a/sound/ppc/pmac.h +++ b/sound/ppc/pmac.h @@ -113,7 +113,8 @@ struct snd_pmac { unsigned int initialized : 1; unsigned int feature_is_set : 1; - unsigned int of_requested; + unsigned int requested; + struct resource rsrc[3]; int num_freqs; int *freq_table; -- cgit 1.2.3-korg From 1beb6a7d6cbed3ac03500ce9b5b9bb632c512039 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 14 Dec 2005 13:10:10 +1100 Subject: [PATCH] powerpc: Experimental support for new G5 Macs (#2) This adds some very basic support for the new machines, including the Quad G5 (tested), and other new dual core based machines and iMac G5 iSight (untested). This is still experimental ! There is no thermal control yet, there is no proper handing of MSIs, etc.. but it boots, I have all 4 cores up on my machine. Compared to the previous version of this patch, this one adds DART IOMMU support for the U4 chipset and thus should work fine on setups with more than 2Gb of RAM. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- arch/powerpc/Kconfig | 1 + arch/powerpc/kernel/pci_64.c | 35 ++- arch/powerpc/kernel/prom.c | 26 ++- arch/powerpc/kernel/udbg.c | 2 + arch/powerpc/platforms/maple/setup.c | 4 +- arch/powerpc/platforms/powermac/feature.c | 65 ++++-- arch/powerpc/platforms/powermac/pci.c | 210 +++++++++++++++--- arch/powerpc/platforms/powermac/pic.c | 72 +++--- arch/powerpc/platforms/powermac/setup.c | 13 +- arch/powerpc/platforms/powermac/smp.c | 319 ++++++++++++++------------- arch/powerpc/sysdev/Makefile | 2 +- arch/powerpc/sysdev/dart.h | 41 ++-- arch/powerpc/sysdev/dart_iommu.c | 350 ++++++++++++++++++++++++++++++ arch/powerpc/sysdev/mpic.c | 199 +++++++++++++---- arch/powerpc/sysdev/u3_iommu.c | 327 ---------------------------- drivers/ide/ppc/pmac.c | 2 +- drivers/macintosh/smu.c | 8 +- include/asm-powerpc/iommu.h | 6 +- include/asm-powerpc/mpic.h | 3 +- include/asm-powerpc/pmac_feature.h | 2 + include/linux/pci_regs.h | 1 + 21 files changed, 1059 insertions(+), 629 deletions(-) create mode 100644 arch/powerpc/sysdev/dart_iommu.c delete mode 100644 arch/powerpc/sysdev/u3_iommu.c (limited to 'drivers') diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 773b880d557743..5692edb3491e13 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -300,6 +300,7 @@ config PPC_PMAC64 bool depends on PPC_PMAC && POWER4 select U3_DART + select MPIC_BROKEN_U3 select GENERIC_TBSYNC default y diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c index 523f35087e81c0..f73a16e9867aad 100644 --- a/arch/powerpc/kernel/pci_64.c +++ b/arch/powerpc/kernel/pci_64.c @@ -34,7 +34,7 @@ #ifdef DEBUG #include -#define DBG(fmt...) udbg_printf(fmt) +#define DBG(fmt...) printk(fmt) #else #define DBG(fmt...) #endif @@ -323,6 +323,7 @@ static void pci_parse_of_addrs(struct device_node *node, struct pci_dev *dev) addrs = (u32 *) get_property(node, "assigned-addresses", &proplen); if (!addrs) return; + DBG(" parse addresses (%d bytes) @ %p\n", proplen, addrs); for (; proplen >= 20; proplen -= 20, addrs += 5) { flags = pci_parse_of_flags(addrs[0]); if (!flags) @@ -332,6 +333,9 @@ static void pci_parse_of_addrs(struct device_node *node, struct pci_dev *dev) if (!size) continue; i = addrs[0] & 0xff; + DBG(" base: %llx, size: %llx, i: %x\n", + (unsigned long long)base, (unsigned long long)size, i); + if (PCI_BASE_ADDRESS_0 <= i && i <= PCI_BASE_ADDRESS_5) { res = &dev->resource[(i - PCI_BASE_ADDRESS_0) >> 2]; } else if (i == dev->rom_base_reg) { @@ -362,6 +366,8 @@ struct pci_dev *of_create_pci_dev(struct device_node *node, if (type == NULL) type = ""; + DBG(" create device, devfn: %x, type: %s\n", devfn, type); + memset(dev, 0, sizeof(struct pci_dev)); dev->bus = bus; dev->sysdata = node; @@ -381,6 +387,8 @@ struct pci_dev *of_create_pci_dev(struct device_node *node, dev->bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn)); dev->class = get_int_prop(node, "class-code", 0); + DBG(" class: 0x%x\n", dev->class); + dev->current_state = 4; /* unknown power state */ if (!strcmp(type, "pci")) { @@ -402,6 +410,8 @@ struct pci_dev *of_create_pci_dev(struct device_node *node, pci_parse_of_addrs(node, dev); + DBG(" adding to system ...\n"); + pci_device_add(dev, bus); /* XXX pci_scan_msi_device(dev); */ @@ -418,15 +428,21 @@ void __devinit of_scan_bus(struct device_node *node, int reglen, devfn; struct pci_dev *dev; + DBG("of_scan_bus(%s) bus no %d... \n", node->full_name, bus->number); + while ((child = of_get_next_child(node, child)) != NULL) { + DBG(" * %s\n", child->full_name); reg = (u32 *) get_property(child, "reg", ®len); if (reg == NULL || reglen < 20) continue; devfn = (reg[0] >> 8) & 0xff; + /* create a new pci_dev for this device */ dev = of_create_pci_dev(child, bus, devfn); if (!dev) continue; + DBG("dev header type: %x\n", dev->hdr_type); + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) of_scan_pci_bridge(child, dev); @@ -446,16 +462,18 @@ void __devinit of_scan_pci_bridge(struct device_node *node, unsigned int flags; u64 size; + DBG("of_scan_pci_bridge(%s)\n", node->full_name); + /* parse bus-range property */ busrange = (u32 *) get_property(node, "bus-range", &len); if (busrange == NULL || len != 8) { - printk(KERN_ERR "Can't get bus-range for PCI-PCI bridge %s\n", + printk(KERN_DEBUG "Can't get bus-range for PCI-PCI bridge %s\n", node->full_name); return; } ranges = (u32 *) get_property(node, "ranges", &len); if (ranges == NULL) { - printk(KERN_ERR "Can't get ranges for PCI-PCI bridge %s\n", + printk(KERN_DEBUG "Can't get ranges for PCI-PCI bridge %s\n", node->full_name); return; } @@ -509,10 +527,13 @@ void __devinit of_scan_pci_bridge(struct device_node *node, } sprintf(bus->name, "PCI Bus %04x:%02x", pci_domain_nr(bus), bus->number); + DBG(" bus name: %s\n", bus->name); mode = PCI_PROBE_NORMAL; if (ppc_md.pci_probe_mode) mode = ppc_md.pci_probe_mode(bus); + DBG(" probe mode: %d\n", mode); + if (mode == PCI_PROBE_DEVTREE) of_scan_bus(node, bus); else if (mode == PCI_PROBE_NORMAL) @@ -528,6 +549,8 @@ void __devinit scan_phb(struct pci_controller *hose) int i, mode; struct resource *res; + DBG("Scanning PHB %s\n", node ? node->full_name : ""); + bus = pci_create_bus(NULL, hose->first_busno, hose->ops, node); if (bus == NULL) { printk(KERN_ERR "Failed to create bus for PCI domain %04x\n", @@ -552,8 +575,9 @@ void __devinit scan_phb(struct pci_controller *hose) mode = PCI_PROBE_NORMAL; #ifdef CONFIG_PPC_MULTIPLATFORM - if (ppc_md.pci_probe_mode) + if (node && ppc_md.pci_probe_mode) mode = ppc_md.pci_probe_mode(bus); + DBG(" probe mode: %d\n", mode); if (mode == PCI_PROBE_DEVTREE) { bus->subordinate = hose->last_busno; of_scan_bus(node, bus); @@ -842,8 +866,7 @@ pgprot_t pci_phys_mem_access_prot(struct file *file, * Returns a negative error code on failure, zero on success. */ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, - int write_combine) + enum pci_mmap_state mmap_state, int write_combine) { unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; struct resource *rp; diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index 1b97e13657e5b8..977ee3adaf2d23 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -298,6 +298,16 @@ static int __devinit finish_node_interrupts(struct device_node *np, int i, j, n, sense; unsigned int *irq, virq; struct device_node *ic; + int trace = 0; + + //#define TRACE(fmt...) do { if (trace) { printk(fmt); mdelay(1000); } } while(0) +#define TRACE(fmt...) + + if (!strcmp(np->name, "smu-doorbell")) + trace = 1; + + TRACE("Finishing SMU doorbell ! num_interrupt_controllers = %d\n", + num_interrupt_controllers); if (num_interrupt_controllers == 0) { /* @@ -332,11 +342,12 @@ static int __devinit finish_node_interrupts(struct device_node *np, } ints = (unsigned int *) get_property(np, "interrupts", &intlen); + TRACE("ints=%p, intlen=%d\n", ints, intlen); if (ints == NULL) return 0; intrcells = prom_n_intr_cells(np); intlen /= intrcells * sizeof(unsigned int); - + TRACE("intrcells=%d, new intlen=%d\n", intrcells, intlen); np->intrs = prom_alloc(intlen * sizeof(*(np->intrs)), mem_start); if (!np->intrs) return -ENOMEM; @@ -347,6 +358,7 @@ static int __devinit finish_node_interrupts(struct device_node *np, intrcount = 0; for (i = 0; i < intlen; ++i, ints += intrcells) { n = map_interrupt(&irq, &ic, np, ints, intrcells); + TRACE("map, irq=%d, ic=%p, n=%d\n", irq, ic, n); if (n <= 0) continue; @@ -357,6 +369,7 @@ static int __devinit finish_node_interrupts(struct device_node *np, np->intrs[intrcount].sense = map_isa_senses[sense]; } else { virq = virt_irq_create_mapping(irq[0]); + TRACE("virq=%d\n", virq); #ifdef CONFIG_PPC64 if (virq == NO_IRQ) { printk(KERN_CRIT "Could not allocate interrupt" @@ -366,6 +379,12 @@ static int __devinit finish_node_interrupts(struct device_node *np, #endif np->intrs[intrcount].line = irq_offset_up(virq); sense = (n > 1)? (irq[1] & 3): 1; + + /* Apple uses bits in there in a different way, let's + * only keep the real sense bit on macs + */ + if (_machine == PLATFORM_POWERMAC) + sense &= 0x1; np->intrs[intrcount].sense = map_mpic_senses[sense]; } @@ -375,12 +394,13 @@ static int __devinit finish_node_interrupts(struct device_node *np, char *name = get_property(ic->parent, "name", NULL); if (name && !strcmp(name, "u3")) np->intrs[intrcount].line += 128; - else if (!(name && !strcmp(name, "mac-io"))) + else if (!(name && (!strcmp(name, "mac-io") || + !strcmp(name, "u4")))) /* ignore other cascaded controllers, such as the k2-sata-root */ break; } -#endif +#endif /* CONFIG_PPC64 */ if (n > 2) { printk("hmmm, got %d intr cells for %s:", n, np->full_name); diff --git a/arch/powerpc/kernel/udbg.c b/arch/powerpc/kernel/udbg.c index a058285a70e715..9567d9474c8091 100644 --- a/arch/powerpc/kernel/udbg.c +++ b/arch/powerpc/kernel/udbg.c @@ -110,10 +110,12 @@ static int early_console_initialized; void __init disable_early_printk(void) { +#if 1 if (!early_console_initialized) return; unregister_console(&udbg_console); early_console_initialized = 0; +#endif } /* called by setup_system */ diff --git a/arch/powerpc/platforms/maple/setup.c b/arch/powerpc/platforms/maple/setup.c index 65fe4c166a68bd..dd73e38bfb7d04 100644 --- a/arch/powerpc/platforms/maple/setup.c +++ b/arch/powerpc/platforms/maple/setup.c @@ -195,7 +195,7 @@ static void __init maple_init_early(void) /* Setup interrupt mapping options */ ppc64_interrupt_controller = IC_OPEN_PIC; - iommu_init_early_u3(); + iommu_init_early_dart(); DBG(" <- maple_init_early\n"); } @@ -257,7 +257,7 @@ static int __init maple_probe(int platform) * occupies having to be broken up so the DART itself is not * part of the cacheable linar mapping */ - alloc_u3_dart_table(); + alloc_dart_table(); return 1; } diff --git a/arch/powerpc/platforms/powermac/feature.c b/arch/powerpc/platforms/powermac/feature.c index b1f896952b1b72..d2915d64d45e36 100644 --- a/arch/powerpc/platforms/powermac/feature.c +++ b/arch/powerpc/platforms/powermac/feature.c @@ -101,7 +101,8 @@ static const char *macio_names[] = "Keylargo", "Pangea", "Intrepid", - "K2" + "K2", + "Shasta", }; @@ -119,7 +120,7 @@ static const char *macio_names[] = static struct device_node *uninorth_node; static u32 __iomem *uninorth_base; static u32 uninorth_rev; -static int uninorth_u3; +static int uninorth_maj; static void __iomem *u3_ht; /* @@ -1399,8 +1400,15 @@ static long g5_fw_enable(struct device_node *node, long param, long value) static long g5_mpic_enable(struct device_node *node, long param, long value) { unsigned long flags; + struct device_node *parent = of_get_parent(node); + int is_u3; - if (node->parent == NULL || strcmp(node->parent->name, "u3")) + if (parent == NULL) + return 0; + is_u3 = strcmp(parent->name, "u3") == 0 || + strcmp(parent->name, "u4") == 0; + of_node_put(parent); + if (!is_u3) return 0; LOCK(flags); @@ -1464,7 +1472,7 @@ static long g5_i2s_enable(struct device_node *node, long param, long value) }, }; - if (macio->type != macio_keylargo2 /* && macio->type != macio_shasta*/) + if (macio->type != macio_keylargo2 && macio->type != macio_shasta) return -ENODEV; if (strncmp(node->name, "i2s-", 4)) return -ENODEV; @@ -1473,11 +1481,9 @@ static long g5_i2s_enable(struct device_node *node, long param, long value) case 0: case 1: break; -#if 0 case 2: if (macio->type == macio_shasta) break; -#endif default: return -ENODEV; } @@ -1508,7 +1514,7 @@ static long g5_reset_cpu(struct device_node *node, long param, long value) struct device_node *np; macio = &macio_chips[0]; - if (macio->type != macio_keylargo2) + if (macio->type != macio_keylargo2 && macio->type != macio_shasta) return -ENODEV; np = find_path_device("/cpus"); @@ -1547,7 +1553,8 @@ static long g5_reset_cpu(struct device_node *node, long param, long value) */ void g5_phy_disable_cpu1(void) { - UN_OUT(U3_API_PHY_CONFIG_1, 0); + if (uninorth_maj == 3) + UN_OUT(U3_API_PHY_CONFIG_1, 0); } #endif /* CONFIG_POWER4 */ @@ -2462,6 +2469,14 @@ static struct pmac_mb_def pmac_mb_defs[] = { PMAC_TYPE_POWERMAC_G5_U3L, g5_features, 0, }, + { "PowerMac11,2", "PowerMac G5 Dual Core", + PMAC_TYPE_POWERMAC_G5_U3L, g5_features, + 0, + }, + { "PowerMac12,1", "iMac G5 (iSight)", + PMAC_TYPE_POWERMAC_G5_U3L, g5_features, + 0, + }, { "RackMac3,1", "XServe G5", PMAC_TYPE_XSERVE_G5, g5_features, 0, @@ -2574,6 +2589,11 @@ static int __init probe_motherboard(void) pmac_mb.model_name = "Unknown K2-based"; pmac_mb.features = g5_features; break; + case macio_shasta: + pmac_mb.model_id = PMAC_TYPE_UNKNOWN_SHASTA; + pmac_mb.model_name = "Unknown Shasta-based"; + pmac_mb.features = g5_features; + break; #endif /* CONFIG_POWER4 */ default: return -ENODEV; @@ -2651,7 +2671,12 @@ static void __init probe_uninorth(void) /* Locate G5 u3 */ if (uninorth_node == NULL) { uninorth_node = of_find_node_by_name(NULL, "u3"); - uninorth_u3 = 1; + uninorth_maj = 3; + } + /* Locate G5 u4 */ + if (uninorth_node == NULL) { + uninorth_node = of_find_node_by_name(NULL, "u4"); + uninorth_maj = 4; } if (uninorth_node == NULL) return; @@ -2664,12 +2689,13 @@ static void __init probe_uninorth(void) return; uninorth_base = ioremap(address, 0x40000); uninorth_rev = in_be32(UN_REG(UNI_N_VERSION)); - if (uninorth_u3) + if (uninorth_maj == 3 || uninorth_maj == 4) u3_ht = ioremap(address + U3_HT_CONFIG_BASE, 0x1000); - printk(KERN_INFO "Found %s memory controller & host bridge," - " revision: %d\n", uninorth_u3 ? "U3" : "UniNorth", - uninorth_rev); + printk(KERN_INFO "Found %s memory controller & host bridge" + " @ 0x%08x revision: 0x%02x\n", uninorth_maj == 3 ? "U3" : + uninorth_maj == 4 ? "U4" : "UniNorth", + (unsigned int)address, uninorth_rev); printk(KERN_INFO "Mapped at 0x%08lx\n", (unsigned long)uninorth_base); /* Set the arbitrer QAck delay according to what Apple does @@ -2677,7 +2703,8 @@ static void __init probe_uninorth(void) if (uninorth_rev < 0x11) { actrl = UN_IN(UNI_N_ARB_CTRL) & ~UNI_N_ARB_CTRL_QACK_DELAY_MASK; actrl |= ((uninorth_rev < 3) ? UNI_N_ARB_CTRL_QACK_DELAY105 : - UNI_N_ARB_CTRL_QACK_DELAY) << UNI_N_ARB_CTRL_QACK_DELAY_SHIFT; + UNI_N_ARB_CTRL_QACK_DELAY) << + UNI_N_ARB_CTRL_QACK_DELAY_SHIFT; UN_OUT(UNI_N_ARB_CTRL, actrl); } @@ -2685,7 +2712,8 @@ static void __init probe_uninorth(void) * revs 1.5 to 2.O and Pangea. Seem to toggle the UniN Maxbus/PCI * memory timeout */ - if ((uninorth_rev >= 0x11 && uninorth_rev <= 0x24) || uninorth_rev == 0xc0) + if ((uninorth_rev >= 0x11 && uninorth_rev <= 0x24) || + uninorth_rev == 0xc0) UN_OUT(0x2160, UN_IN(0x2160) & 0x00ffffff); } @@ -2736,12 +2764,14 @@ static void __init probe_one_macio(const char *name, const char *compat, int typ node->full_name); return; } - if (type == macio_keylargo) { + if (type == macio_keylargo || type == macio_keylargo2) { u32 *did = (u32 *)get_property(node, "device-id", NULL); if (*did == 0x00000025) type = macio_pangea; if (*did == 0x0000003e) type = macio_intrepid; + if (*did == 0x0000004f) + type = macio_shasta; } macio_chips[i].of_node = node; macio_chips[i].type = type; @@ -2840,7 +2870,8 @@ set_initial_features(void) } #ifdef CONFIG_POWER4 - if (macio_chips[0].type == macio_keylargo2) { + if (macio_chips[0].type == macio_keylargo2 || + macio_chips[0].type == macio_shasta) { #ifndef CONFIG_SMP /* On SMP machines running UP, we have the second CPU eating * bus cycles. We need to take it off the bus. This is done diff --git a/arch/powerpc/platforms/powermac/pci.c b/arch/powerpc/platforms/powermac/pci.c index 5aab261075de5c..f671ed2539013e 100644 --- a/arch/powerpc/platforms/powermac/pci.c +++ b/arch/powerpc/platforms/powermac/pci.c @@ -1,7 +1,7 @@ /* * Support for PCI bridges found on Power Macintoshes. * - * Copyright (C) 2003 Benjamin Herrenschmuidt (benh@kernel.crashing.org) + * Copyright (C) 2003-2005 Benjamin Herrenschmuidt (benh@kernel.crashing.org) * Copyright (C) 1997 Paul Mackerras (paulus@samba.org) * * This program is free software; you can redistribute it and/or @@ -25,7 +25,7 @@ #include #include #ifdef CONFIG_PPC64 -#include +//#include #include #endif @@ -44,6 +44,7 @@ static int add_bridge(struct device_node *dev); static int has_uninorth; #ifdef CONFIG_PPC64 static struct pci_controller *u3_agp; +static struct pci_controller *u4_pcie; static struct pci_controller *u3_ht; #endif /* CONFIG_PPC64 */ @@ -97,11 +98,8 @@ static void __init fixup_bus_range(struct device_node *bridge) /* Lookup the "bus-range" property for the hose */ bus_range = (int *) get_property(bridge, "bus-range", &len); - if (bus_range == NULL || len < 2 * sizeof(int)) { - printk(KERN_WARNING "Can't get bus-range for %s\n", - bridge->full_name); + if (bus_range == NULL || len < 2 * sizeof(int)) return; - } bus_range[1] = fixup_one_level_bus_range(bridge->child, bus_range[1]); } @@ -128,14 +126,14 @@ static void __init fixup_bus_range(struct device_node *bridge) */ #define MACRISC_CFA0(devfn, off) \ - ((1 << (unsigned long)PCI_SLOT(dev_fn)) \ - | (((unsigned long)PCI_FUNC(dev_fn)) << 8) \ - | (((unsigned long)(off)) & 0xFCUL)) + ((1 << (unsigned int)PCI_SLOT(dev_fn)) \ + | (((unsigned int)PCI_FUNC(dev_fn)) << 8) \ + | (((unsigned int)(off)) & 0xFCUL)) #define MACRISC_CFA1(bus, devfn, off) \ - ((((unsigned long)(bus)) << 16) \ - |(((unsigned long)(devfn)) << 8) \ - |(((unsigned long)(off)) & 0xFCUL) \ + ((((unsigned int)(bus)) << 16) \ + |(((unsigned int)(devfn)) << 8) \ + |(((unsigned int)(off)) & 0xFCUL) \ |1UL) static unsigned long macrisc_cfg_access(struct pci_controller* hose, @@ -168,7 +166,8 @@ static int macrisc_read_config(struct pci_bus *bus, unsigned int devfn, hose = pci_bus_to_host(bus); if (hose == NULL) return PCIBIOS_DEVICE_NOT_FOUND; - + if (offset >= 0x100) + return PCIBIOS_BAD_REGISTER_NUMBER; addr = macrisc_cfg_access(hose, bus->number, devfn, offset); if (!addr) return PCIBIOS_DEVICE_NOT_FOUND; @@ -199,7 +198,8 @@ static int macrisc_write_config(struct pci_bus *bus, unsigned int devfn, hose = pci_bus_to_host(bus); if (hose == NULL) return PCIBIOS_DEVICE_NOT_FOUND; - + if (offset >= 0x100) + return PCIBIOS_BAD_REGISTER_NUMBER; addr = macrisc_cfg_access(hose, bus->number, devfn, offset); if (!addr) return PCIBIOS_DEVICE_NOT_FOUND; @@ -234,12 +234,13 @@ static struct pci_ops macrisc_pci_ops = /* * Verify that a specific (bus, dev_fn) exists on chaos */ -static int -chaos_validate_dev(struct pci_bus *bus, int devfn, int offset) +static int chaos_validate_dev(struct pci_bus *bus, int devfn, int offset) { struct device_node *np; u32 *vendor, *device; + if (offset >= 0x100) + return PCIBIOS_BAD_REGISTER_NUMBER; np = pci_busdev_to_OF_node(bus, devfn); if (np == NULL) return PCIBIOS_DEVICE_NOT_FOUND; @@ -341,10 +342,10 @@ static int u3_ht_skip_device(struct pci_controller *hose, } #define U3_HT_CFA0(devfn, off) \ - ((((unsigned long)devfn) << 8) | offset) + ((((unsigned int)devfn) << 8) | offset) #define U3_HT_CFA1(bus, devfn, off) \ (U3_HT_CFA0(devfn, off) \ - + (((unsigned long)bus) << 16) \ + + (((unsigned int)bus) << 16) \ + 0x01000000UL) static unsigned long u3_ht_cfg_access(struct pci_controller* hose, @@ -370,7 +371,8 @@ static int u3_ht_read_config(struct pci_bus *bus, unsigned int devfn, hose = pci_bus_to_host(bus); if (hose == NULL) return PCIBIOS_DEVICE_NOT_FOUND; - + if (offset >= 0x100) + return PCIBIOS_BAD_REGISTER_NUMBER; addr = u3_ht_cfg_access(hose, bus->number, devfn, offset); if (!addr) return PCIBIOS_DEVICE_NOT_FOUND; @@ -419,7 +421,8 @@ static int u3_ht_write_config(struct pci_bus *bus, unsigned int devfn, hose = pci_bus_to_host(bus); if (hose == NULL) return PCIBIOS_DEVICE_NOT_FOUND; - + if (offset >= 0x100) + return PCIBIOS_BAD_REGISTER_NUMBER; addr = u3_ht_cfg_access(hose, bus->number, devfn, offset); if (!addr) return PCIBIOS_DEVICE_NOT_FOUND; @@ -459,6 +462,112 @@ static struct pci_ops u3_ht_pci_ops = u3_ht_read_config, u3_ht_write_config }; + +#define U4_PCIE_CFA0(devfn, off) \ + ((1 << ((unsigned int)PCI_SLOT(dev_fn))) \ + | (((unsigned int)PCI_FUNC(dev_fn)) << 8) \ + | ((((unsigned int)(off)) >> 8) << 28) \ + | (((unsigned int)(off)) & 0xfcU)) + +#define U4_PCIE_CFA1(bus, devfn, off) \ + ((((unsigned int)(bus)) << 16) \ + |(((unsigned int)(devfn)) << 8) \ + | ((((unsigned int)(off)) >> 8) << 28) \ + |(((unsigned int)(off)) & 0xfcU) \ + |1UL) + +static unsigned long u4_pcie_cfg_access(struct pci_controller* hose, + u8 bus, u8 dev_fn, int offset) +{ + unsigned int caddr; + + if (bus == hose->first_busno) { + caddr = U4_PCIE_CFA0(dev_fn, offset); + } else + caddr = U4_PCIE_CFA1(bus, dev_fn, offset); + + /* Uninorth will return garbage if we don't read back the value ! */ + do { + out_le32(hose->cfg_addr, caddr); + } while (in_le32(hose->cfg_addr) != caddr); + + offset &= 0x03; + return ((unsigned long)hose->cfg_data) + offset; +} + +static int u4_pcie_read_config(struct pci_bus *bus, unsigned int devfn, + int offset, int len, u32 *val) +{ + struct pci_controller *hose; + unsigned long addr; + + hose = pci_bus_to_host(bus); + if (hose == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + if (offset >= 0x1000) + return PCIBIOS_BAD_REGISTER_NUMBER; + addr = u4_pcie_cfg_access(hose, bus->number, devfn, offset); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + /* + * Note: the caller has already checked that offset is + * suitably aligned and that len is 1, 2 or 4. + */ + switch (len) { + case 1: + *val = in_8((u8 *)addr); + break; + case 2: + *val = in_le16((u16 *)addr); + break; + default: + *val = in_le32((u32 *)addr); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static int u4_pcie_write_config(struct pci_bus *bus, unsigned int devfn, + int offset, int len, u32 val) +{ + struct pci_controller *hose; + unsigned long addr; + + hose = pci_bus_to_host(bus); + if (hose == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + if (offset >= 0x1000) + return PCIBIOS_BAD_REGISTER_NUMBER; + addr = u4_pcie_cfg_access(hose, bus->number, devfn, offset); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + /* + * Note: the caller has already checked that offset is + * suitably aligned and that len is 1, 2 or 4. + */ + switch (len) { + case 1: + out_8((u8 *)addr, val); + (void) in_8((u8 *)addr); + break; + case 2: + out_le16((u16 *)addr, val); + (void) in_le16((u16 *)addr); + break; + default: + out_le32((u32 *)addr, val); + (void) in_le32((u32 *)addr); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops u4_pcie_pci_ops = +{ + u4_pcie_read_config, + u4_pcie_write_config +}; + #endif /* CONFIG_PPC64 */ #ifdef CONFIG_PPC32 @@ -628,15 +737,36 @@ static void __init setup_u3_agp(struct pci_controller* hose) hose->ops = ¯isc_pci_ops; hose->cfg_addr = ioremap(0xf0000000 + 0x800000, 0x1000); hose->cfg_data = ioremap(0xf0000000 + 0xc00000, 0x1000); - u3_agp = hose; } +static void __init setup_u4_pcie(struct pci_controller* hose) +{ + /* We currently only implement the "non-atomic" config space, to + * be optimised later. + */ + hose->ops = &u4_pcie_pci_ops; + hose->cfg_addr = ioremap(0xf0000000 + 0x800000, 0x1000); + hose->cfg_data = ioremap(0xf0000000 + 0xc00000, 0x1000); + + /* The bus contains a bridge from root -> device, we need to + * make it visible on bus 0 so that we pick the right type + * of config cycles. If we didn't, we would have to force all + * config cycles to be type 1. So we override the "bus-range" + * property here + */ + hose->first_busno = 0x00; + hose->last_busno = 0xff; + u4_pcie = hose; +} + static void __init setup_u3_ht(struct pci_controller* hose) { struct device_node *np = (struct device_node *)hose->arch_data; + struct pci_controller *other = NULL; int i, cur; + hose->ops = &u3_ht_pci_ops; /* We hard code the address because of the different size of @@ -670,11 +800,20 @@ static void __init setup_u3_ht(struct pci_controller* hose) u3_ht = hose; - if (u3_agp == NULL) { - DBG("U3 has no AGP, using full resource range\n"); + if (u3_agp != NULL) + other = u3_agp; + else if (u4_pcie != NULL) + other = u4_pcie; + + if (other == NULL) { + DBG("U3/4 has no AGP/PCIE, using full resource range\n"); return; } + /* Fixup bus range vs. PCIE */ + if (u4_pcie) + hose->last_busno = u4_pcie->first_busno - 1; + /* We "remove" the AGP resources from the resources allocated to HT, * that is we create "holes". However, that code does assumptions * that so far happen to be true (cross fingers...), typically that @@ -682,7 +821,7 @@ static void __init setup_u3_ht(struct pci_controller* hose) */ cur = 0; for (i=0; i<3; i++) { - struct resource *res = &u3_agp->mem_resources[i]; + struct resource *res = &other->mem_resources[i]; if (res->flags != IORESOURCE_MEM) continue; /* We don't care about "fine" resources */ @@ -777,9 +916,13 @@ static int __init add_bridge(struct device_node *dev) setup_u3_ht(hose); disp_name = "U3-HT"; primary = 1; + } else if (device_is_compatible(dev, "u4-pcie")) { + setup_u4_pcie(hose); + disp_name = "U4-PCIE"; + primary = 0; } - printk(KERN_INFO "Found %s PCI host bridge. Firmware bus number: %d->%d\n", - disp_name, hose->first_busno, hose->last_busno); + printk(KERN_INFO "Found %s PCI host bridge. Firmware bus number:" + " %d->%d\n", disp_name, hose->first_busno, hose->last_busno); #endif /* CONFIG_PPC64 */ /* 32 bits only bridges */ @@ -900,6 +1043,8 @@ void __init pmac_pci_init(void) pci_setup_phb_io(u3_ht, 1); if (u3_agp) pci_setup_phb_io(u3_agp, 0); + if (u4_pcie) + pci_setup_phb_io(u4_pcie, 0); /* * On ppc64, fixup the IO resources on our host bridges as @@ -912,7 +1057,8 @@ void __init pmac_pci_init(void) /* Fixup the PCI<->OF mapping for U3 AGP due to bus renumbering. We * assume there is no P2P bridge on the AGP bus, which should be a - * safe assumptions hopefully. + * safe assumptions for now. We should do something better in the + * future though */ if (u3_agp) { struct device_node *np = u3_agp->arch_data; @@ -920,7 +1066,6 @@ void __init pmac_pci_init(void) for (np = np->child; np; np = np->sibling) PCI_DN(np)->busno = 0xf0; } - /* pmac_check_ht_link(); */ /* Tell pci.c to not use the common resource allocation mechanism */ @@ -1127,7 +1272,8 @@ void pmac_pci_fixup_pciata(struct pci_dev* dev) good: pci_read_config_byte(dev, PCI_CLASS_PROG, &progif); if ((progif & 5) != 5) { - printk(KERN_INFO "Forcing PCI IDE into native mode: %s\n", pci_name(dev)); + printk(KERN_INFO "Forcing PCI IDE into native mode: %s\n", + pci_name(dev)); (void) pci_write_config_byte(dev, PCI_CLASS_PROG, progif|5); if (pci_read_config_byte(dev, PCI_CLASS_PROG, &progif) || (progif & 5) != 5) @@ -1153,7 +1299,8 @@ static void fixup_k2_sata(struct pci_dev* dev) for (i = 0; i < 6; i++) { dev->resource[i].start = dev->resource[i].end = 0; dev->resource[i].flags = 0; - pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + 4 * i, 0); + pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + 4 * i, + 0); } } else { pci_read_config_word(dev, PCI_COMMAND, &cmd); @@ -1162,7 +1309,8 @@ static void fixup_k2_sata(struct pci_dev* dev) for (i = 0; i < 5; i++) { dev->resource[i].start = dev->resource[i].end = 0; dev->resource[i].flags = 0; - pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + 4 * i, 0); + pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + 4 * i, + 0); } } } diff --git a/arch/powerpc/platforms/powermac/pic.c b/arch/powerpc/platforms/powermac/pic.c index dbb524a851aa76..18bf3011d1e3ff 100644 --- a/arch/powerpc/platforms/powermac/pic.c +++ b/arch/powerpc/platforms/powermac/pic.c @@ -524,18 +524,56 @@ static void __init pmac_pic_setup_mpic_nmi(struct mpic *mpic) #endif /* defined(CONFIG_XMON) && defined(CONFIG_PPC32) */ } +static struct mpic * __init pmac_setup_one_mpic(struct device_node *np, + int master) +{ + unsigned char senses[128]; + int offset = master ? 0 : 128; + int count = master ? 128 : 124; + const char *name = master ? " MPIC 1 " : " MPIC 2 "; + struct resource r; + struct mpic *mpic; + unsigned int flags = master ? MPIC_PRIMARY : 0; + int rc; + + rc = of_address_to_resource(np, 0, &r); + if (rc) + return NULL; + + pmac_call_feature(PMAC_FTR_ENABLE_MPIC, np, 0, 0); + + prom_get_irq_senses(senses, offset, offset + count); + + flags |= MPIC_WANTS_RESET; + if (get_property(np, "big-endian", NULL)) + flags |= MPIC_BIG_ENDIAN; + + /* Primary Big Endian means HT interrupts. This is quite dodgy + * but works until I find a better way + */ + if (master && (flags & MPIC_BIG_ENDIAN)) + flags |= MPIC_BROKEN_U3; + + mpic = mpic_alloc(r.start, flags, 0, offset, count, master ? 252 : 0, + senses, count, name); + if (mpic == NULL) + return NULL; + + mpic_init(mpic); + + return mpic; + } + static int __init pmac_pic_probe_mpic(void) { struct mpic *mpic1, *mpic2; struct device_node *np, *master = NULL, *slave = NULL; - unsigned char senses[128]; - struct resource r; /* We can have up to 2 MPICs cascaded */ for (np = NULL; (np = of_find_node_by_type(np, "open-pic")) != NULL;) { if (master == NULL && - get_property(np, "interrupt-parent", NULL) != NULL) + get_property(np, "interrupts", NULL) == NULL) master = of_node_get(np); else if (slave == NULL) slave = of_node_get(np); @@ -557,13 +595,8 @@ static int __init pmac_pic_probe_mpic(void) ppc_md.get_irq = mpic_get_irq; /* Setup master */ - BUG_ON(of_address_to_resource(master, 0, &r)); - pmac_call_feature(PMAC_FTR_ENABLE_MPIC, master, 0, 0); - prom_get_irq_senses(senses, 0, 128); - mpic1 = mpic_alloc(r.start, MPIC_PRIMARY | MPIC_WANTS_RESET, - 0, 0, 128, 252, senses, 128, " OpenPIC "); + mpic1 = pmac_setup_one_mpic(master, 1); BUG_ON(mpic1 == NULL); - mpic_init(mpic1); /* Install NMI if any */ pmac_pic_setup_mpic_nmi(mpic1); @@ -574,27 +607,12 @@ static int __init pmac_pic_probe_mpic(void) if (slave == NULL || slave->n_intrs < 1) return 0; - /* Setup slave, failures are non-fatal */ - if (of_address_to_resource(slave, 0, &r)) { - printk(KERN_ERR "Can't get address of MPIC %s\n", - slave->full_name); - return 0; - } - pmac_call_feature(PMAC_FTR_ENABLE_MPIC, slave, 0, 0); - prom_get_irq_senses(senses, 128, 128 + 124); - - /* We don't need to set MPIC_BROKEN_U3 here since we don't have - * hypertransport interrupts routed to it, at least not on currently - * supported machines, that may change. - */ - mpic2 = mpic_alloc(r.start, MPIC_BIG_ENDIAN | MPIC_WANTS_RESET, - 0, 128, 124, 0, senses, 124, " U3-MPIC "); + mpic2 = pmac_setup_one_mpic(slave, 0); if (mpic2 == NULL) { - printk(KERN_ERR "Can't create slave MPIC %s\n", - slave->full_name); + printk(KERN_ERR "Failed to setup slave MPIC\n"); + of_node_put(slave); return 0; } - mpic_init(mpic2); mpic_setup_cascade(slave->intrs[0].line, pmac_u3_cascade, mpic2); of_node_put(slave); diff --git a/arch/powerpc/platforms/powermac/setup.c b/arch/powerpc/platforms/powermac/setup.c index 18c5620f87fa5d..1daa5a06e9ea47 100644 --- a/arch/powerpc/platforms/powermac/setup.c +++ b/arch/powerpc/platforms/powermac/setup.c @@ -345,7 +345,7 @@ void __init pmac_setup_arch(void) #ifdef CONFIG_SMP /* Check for Core99 */ - if (find_devices("uni-n") || find_devices("u3")) + if (find_devices("uni-n") || find_devices("u3") || find_devices("u4")) smp_ops = &core99_smp_ops; #ifdef CONFIG_PPC32 else @@ -635,7 +635,7 @@ static void __init pmac_init_early(void) /* Setup interrupt mapping options */ ppc64_interrupt_controller = IC_OPEN_PIC; - iommu_init_early_u3(); + iommu_init_early_dart(); #endif } @@ -711,7 +711,7 @@ static int __init pmac_probe(int platform) * occupies having to be broken up so the DART itself is not * part of the cacheable linar mapping */ - alloc_u3_dart_table(); + alloc_dart_table(); #endif #ifdef CONFIG_PMAC_SMU @@ -733,10 +733,11 @@ static int pmac_pci_probe_mode(struct pci_bus *bus) struct device_node *node = bus->sysdata; /* We need to use normal PCI probing for the AGP bus, - since the device for the AGP bridge isn't in the tree. */ - if (bus->self == NULL && device_is_compatible(node, "u3-agp")) + * since the device for the AGP bridge isn't in the tree. + */ + if (bus->self == NULL && (device_is_compatible(node, "u3-agp") || + device_is_compatible(node, "u4-pcie"))) return PCI_PROBE_NORMAL; - return PCI_PROBE_DEVTREE; } #endif diff --git a/arch/powerpc/platforms/powermac/smp.c b/arch/powerpc/platforms/powermac/smp.c index 862f1e985c19b6..df01bb8feb16d8 100644 --- a/arch/powerpc/platforms/powermac/smp.c +++ b/arch/powerpc/platforms/powermac/smp.c @@ -361,7 +361,6 @@ static void __init psurge_dual_sync_tb(int cpu_nr) set_dec(tb_ticks_per_jiffy); /* XXX fixme */ set_tb(0, 0); - last_jiffy_stamp(cpu_nr) = 0; if (cpu_nr > 0) { mb(); @@ -429,15 +428,62 @@ struct smp_ops_t psurge_smp_ops = { }; #endif /* CONFIG_PPC32 - actually powersurge support */ +/* + * Core 99 and later support + */ + +static void (*pmac_tb_freeze)(int freeze); +static unsigned long timebase; +static int tb_req; + +static void smp_core99_give_timebase(void) +{ + unsigned long flags; + + local_irq_save(flags); + + while(!tb_req) + barrier(); + tb_req = 0; + (*pmac_tb_freeze)(1); + mb(); + timebase = get_tb(); + mb(); + while (timebase) + barrier(); + mb(); + (*pmac_tb_freeze)(0); + mb(); + + local_irq_restore(flags); +} + + +static void __devinit smp_core99_take_timebase(void) +{ + unsigned long flags; + + local_irq_save(flags); + + tb_req = 1; + mb(); + while (!timebase) + barrier(); + mb(); + set_tb(timebase >> 32, timebase & 0xffffffff); + timebase = 0; + mb(); + set_dec(tb_ticks_per_jiffy/2); + + local_irq_restore(flags); +} + #ifdef CONFIG_PPC64 /* * G5s enable/disable the timebase via an i2c-connected clock chip. */ static struct device_node *pmac_tb_clock_chip_host; static u8 pmac_tb_pulsar_addr; -static void (*pmac_tb_freeze)(int freeze); -static DEFINE_SPINLOCK(timebase_lock); -static unsigned long timebase; static void smp_core99_cypress_tb_freeze(int freeze) { @@ -447,7 +493,8 @@ static void smp_core99_cypress_tb_freeze(int freeze) /* Strangely, the device-tree says address is 0xd2, but darwin * accesses 0xd0 ... */ - pmac_low_i2c_setmode(pmac_tb_clock_chip_host, pmac_low_i2c_mode_combined); + pmac_low_i2c_setmode(pmac_tb_clock_chip_host, + pmac_low_i2c_mode_combined); rc = pmac_low_i2c_xfer(pmac_tb_clock_chip_host, 0xd0 | pmac_low_i2c_read, 0x81, &data, 1); @@ -475,7 +522,8 @@ static void smp_core99_pulsar_tb_freeze(int freeze) u8 data; int rc; - pmac_low_i2c_setmode(pmac_tb_clock_chip_host, pmac_low_i2c_mode_combined); + pmac_low_i2c_setmode(pmac_tb_clock_chip_host, + pmac_low_i2c_mode_combined); rc = pmac_low_i2c_xfer(pmac_tb_clock_chip_host, pmac_tb_pulsar_addr | pmac_low_i2c_read, 0x2e, &data, 1); @@ -496,54 +544,14 @@ static void smp_core99_pulsar_tb_freeze(int freeze) } } - -static void smp_core99_give_timebase(void) -{ - /* Open i2c bus for synchronous access */ - if (pmac_low_i2c_open(pmac_tb_clock_chip_host, 0)) - panic("Can't open i2c for TB sync !\n"); - - spin_lock(&timebase_lock); - (*pmac_tb_freeze)(1); - mb(); - timebase = get_tb(); - spin_unlock(&timebase_lock); - - while (timebase) - barrier(); - - spin_lock(&timebase_lock); - (*pmac_tb_freeze)(0); - spin_unlock(&timebase_lock); - - /* Close i2c bus */ - pmac_low_i2c_close(pmac_tb_clock_chip_host); -} - - -static void __devinit smp_core99_take_timebase(void) -{ - while (!timebase) - barrier(); - spin_lock(&timebase_lock); - set_tb(timebase >> 32, timebase & 0xffffffff); - timebase = 0; - spin_unlock(&timebase_lock); -} - -static void __init smp_core99_setup(int ncpus) +static void __init smp_core99_setup_i2c_hwsync(int ncpus) { struct device_node *cc = NULL; struct device_node *p; + const char *name = NULL; u32 *reg; int ok; - /* HW sync only on these platforms */ - if (!machine_is_compatible("PowerMac7,2") && - !machine_is_compatible("PowerMac7,3") && - !machine_is_compatible("RackMac3,1")) - return; - /* Look for the clock chip */ while ((cc = of_find_node_by_name(cc, "i2c-hwclock")) != NULL) { p = of_get_parent(cc); @@ -561,114 +569,64 @@ static void __init smp_core99_setup(int ncpus) if (device_is_compatible(cc, "pulsar-legacy-slewing")) { pmac_tb_freeze = smp_core99_pulsar_tb_freeze; pmac_tb_pulsar_addr = 0xd2; - printk(KERN_INFO "Timebase clock is Pulsar chip\n"); + name = "Pulsar"; } else if (device_is_compatible(cc, "cy28508")) { pmac_tb_freeze = smp_core99_cypress_tb_freeze; - printk(KERN_INFO "Timebase clock is Cypress chip\n"); + name = "Cypress"; } break; case 0xd4: pmac_tb_freeze = smp_core99_pulsar_tb_freeze; pmac_tb_pulsar_addr = 0xd4; - printk(KERN_INFO "Timebase clock is Pulsar chip\n"); + name = "Pulsar"; break; } - if (pmac_tb_freeze != NULL) { - pmac_tb_clock_chip_host = of_get_parent(cc); - of_node_put(cc); + if (pmac_tb_freeze != NULL) break; - } } - if (pmac_tb_freeze == NULL) { - smp_ops->give_timebase = smp_generic_give_timebase; - smp_ops->take_timebase = smp_generic_take_timebase; + if (pmac_tb_freeze != NULL) { + struct device_node *p = of_get_parent(cc); + of_node_put(cc); + while(p && strcmp(p->type, "i2c")) { + cc = of_get_parent(p); + of_node_put(p); + p = cc; + } + if (p == NULL) + goto no_i2c_sync; + /* Open i2c bus for synchronous access */ + if (pmac_low_i2c_open(p, 0)) { + printk(KERN_ERR "Failed top open i2c bus %s for clock" + " sync, fallback to software sync !\n", + p->full_name); + of_node_put(p); + goto no_i2c_sync; + } + pmac_tb_clock_chip_host = p; + printk(KERN_INFO "Processor timebase sync using %s i2c clock\n", + name); + return; } + no_i2c_sync: + pmac_tb_freeze = NULL; } -/* nothing to do here, caches are already set up by service processor */ -static inline void __devinit core99_init_caches(int cpu) -{ -} +#endif /* CONFIG_PPC64 */ -#else /* CONFIG_PPC64 */ /* - * SMP G4 powermacs use a GPIO to enable/disable the timebase. + * SMP G4 and newer G5 use a GPIO to enable/disable the timebase. */ static unsigned int core99_tb_gpio; /* Timebase freeze GPIO */ -static unsigned int pri_tb_hi, pri_tb_lo; -static unsigned int pri_tb_stamp; - -/* not __init, called in sleep/wakeup code */ -void smp_core99_give_timebase(void) +static void smp_core99_gpio_tb_freeze(int freeze) { - unsigned long flags; - unsigned int t; - - /* wait for the secondary to be in take_timebase */ - for (t = 100000; t > 0 && !sec_tb_reset; --t) - udelay(10); - if (!sec_tb_reset) { - printk(KERN_WARNING "Timeout waiting sync on second CPU\n"); - return; - } - - /* freeze the timebase and read it */ - /* disable interrupts so the timebase is disabled for the - shortest possible time */ - local_irq_save(flags); - pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, core99_tb_gpio, 4); + if (freeze) + pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, core99_tb_gpio, 4); + else + pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, core99_tb_gpio, 0); pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, core99_tb_gpio, 0); - mb(); - pri_tb_hi = get_tbu(); - pri_tb_lo = get_tbl(); - pri_tb_stamp = last_jiffy_stamp(smp_processor_id()); - mb(); - - /* tell the secondary we're ready */ - sec_tb_reset = 2; - mb(); - - /* wait for the secondary to have taken it */ - /* note: can't use udelay here, since it needs the timebase running */ - for (t = 10000000; t > 0 && sec_tb_reset; --t) - barrier(); - if (sec_tb_reset) - /* XXX BUG_ON here? */ - printk(KERN_WARNING "Timeout waiting sync(2) on second CPU\n"); - - /* Now, restart the timebase by leaving the GPIO to an open collector */ - pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, core99_tb_gpio, 0); - pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, core99_tb_gpio, 0); - local_irq_restore(flags); -} - -/* not __init, called in sleep/wakeup code */ -void smp_core99_take_timebase(void) -{ - unsigned long flags; - - /* tell the primary we're here */ - sec_tb_reset = 1; - mb(); - - /* wait for the primary to set pri_tb_hi/lo */ - while (sec_tb_reset < 2) - mb(); - - /* set our stuff the same as the primary */ - local_irq_save(flags); - set_dec(1); - set_tb(pri_tb_hi, pri_tb_lo); - last_jiffy_stamp(smp_processor_id()) = pri_tb_stamp; - mb(); - - /* tell the primary we're done */ - sec_tb_reset = 0; - mb(); - local_irq_restore(flags); } /* L2 and L3 cache settings to pass from CPU0 to CPU1 on G4 cpus */ @@ -677,6 +635,7 @@ volatile static long int core99_l3_cache; static void __devinit core99_init_caches(int cpu) { +#ifndef CONFIG_PPC64 if (!cpu_has_feature(CPU_FTR_L2CR)) return; @@ -702,30 +661,80 @@ static void __devinit core99_init_caches(int cpu) _set_L3CR(core99_l3_cache); printk("CPU%d: L3CR set to %lx\n", cpu, core99_l3_cache); } +#endif /* !CONFIG_PPC64 */ } static void __init smp_core99_setup(int ncpus) { - struct device_node *cpu; - u32 *tbprop = NULL; - int i; +#ifdef CONFIG_PPC64 - core99_tb_gpio = KL_GPIO_TB_ENABLE; /* default value */ - cpu = of_find_node_by_type(NULL, "cpu"); - if (cpu != NULL) { - tbprop = (u32 *)get_property(cpu, "timebase-enable", NULL); - if (tbprop) - core99_tb_gpio = *tbprop; - of_node_put(cpu); + /* i2c based HW sync on some G5s */ + if (machine_is_compatible("PowerMac7,2") || + machine_is_compatible("PowerMac7,3") || + machine_is_compatible("RackMac3,1")) + smp_core99_setup_i2c_hwsync(ncpus); + + /* GPIO based HW sync on recent G5s */ + if (pmac_tb_freeze == NULL) { + struct device_node *np = + of_find_node_by_name(NULL, "timebase-enable"); + u32 *reg = (u32 *)get_property(np, "reg", NULL); + + if (np && reg && !strcmp(np->type, "gpio")) { + core99_tb_gpio = *reg; + if (core99_tb_gpio < 0x50) + core99_tb_gpio += 0x50; + pmac_tb_freeze = smp_core99_gpio_tb_freeze; + printk(KERN_INFO "Processor timebase sync using" + " GPIO 0x%02x\n", core99_tb_gpio); + } } - /* XXX should get this from reg properties */ - for (i = 1; i < ncpus; ++i) - smp_hw_index[i] = i; - powersave_nap = 0; -} +#else /* CONFIG_PPC64 */ + + /* GPIO based HW sync on ppc32 Core99 */ + if (pmac_tb_freeze == NULL && !machine_is_compatible("MacRISC4")) { + struct device_node *cpu; + u32 *tbprop = NULL; + + core99_tb_gpio = KL_GPIO_TB_ENABLE; /* default value */ + cpu = of_find_node_by_type(NULL, "cpu"); + if (cpu != NULL) { + tbprop = (u32 *)get_property(cpu, "timebase-enable", + NULL); + if (tbprop) + core99_tb_gpio = *tbprop; + of_node_put(cpu); + } + pmac_tb_freeze = smp_core99_gpio_tb_freeze; + printk(KERN_INFO "Processor timebase sync using" + " GPIO 0x%02x\n", core99_tb_gpio); + } + +#endif /* CONFIG_PPC64 */ + + /* No timebase sync, fallback to software */ + if (pmac_tb_freeze == NULL) { + smp_ops->give_timebase = smp_generic_give_timebase; + smp_ops->take_timebase = smp_generic_take_timebase; + printk(KERN_INFO "Processor timebase sync using software\n"); + } + +#ifndef CONFIG_PPC64 + { + int i; + + /* XXX should get this from reg properties */ + for (i = 1; i < ncpus; ++i) + smp_hw_index[i] = i; + } #endif + /* 32 bits SMP can't NAP */ + if (!machine_is_compatible("MacRISC4")) + powersave_nap = 0; +} + static int __init smp_core99_probe(void) { struct device_node *cpus; @@ -803,17 +812,25 @@ static void __devinit smp_core99_setup_cpu(int cpu_nr) mpic_setup_this_cpu(); if (cpu_nr == 0) { -#ifdef CONFIG_POWER4 +#ifdef CONFIG_PPC64 extern void g5_phy_disable_cpu1(void); + /* Close i2c bus if it was used for tb sync */ + if (pmac_tb_clock_chip_host) { + pmac_low_i2c_close(pmac_tb_clock_chip_host); + pmac_tb_clock_chip_host = NULL; + } + /* If we didn't start the second CPU, we must take * it off the bus */ if (machine_is_compatible("MacRISC4") && num_online_cpus() < 2) g5_phy_disable_cpu1(); -#endif /* CONFIG_POWER4 */ - if (ppc_md.progress) ppc_md.progress("core99_setup_cpu 0 done", 0x349); +#endif /* CONFIG_PPC64 */ + + if (ppc_md.progress) + ppc_md.progress("core99_setup_cpu 0 done", 0x349); } } diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index b3e3636a57b071..14b9abde2d27ea 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile @@ -4,6 +4,6 @@ obj-$(CONFIG_PPC_I8259) += i8259.o obj-$(CONFIG_PPC_MPC106) += grackle.o obj-$(CONFIG_BOOKE) += dcr.o obj-$(CONFIG_40x) += dcr.o -obj-$(CONFIG_U3_DART) += u3_iommu.o +obj-$(CONFIG_U3_DART) += dart_iommu.o obj-$(CONFIG_MMIO_NVRAM) += mmio_nvram.o obj-$(CONFIG_83xx) += ipic.o diff --git a/arch/powerpc/sysdev/dart.h b/arch/powerpc/sysdev/dart.h index 33ed9ed7fc1e35..c2d05763ccbe67 100644 --- a/arch/powerpc/sysdev/dart.h +++ b/arch/powerpc/sysdev/dart.h @@ -20,29 +20,44 @@ #define _POWERPC_SYSDEV_DART_H -/* physical base of DART registers */ -#define DART_BASE 0xf8033000UL - /* Offset from base to control register */ -#define DARTCNTL 0 +#define DART_CNTL 0 + /* Offset from base to exception register */ -#define DARTEXCP 0x10 +#define DART_EXCP_U3 0x10 /* Offset from base to TLB tag registers */ -#define DARTTAG 0x1000 +#define DART_TAGS_U3 0x1000 +/* U4 registers */ +#define DART_BASE_U4 0x10 +#define DART_SIZE_U4 0x20 +#define DART_EXCP_U4 0x30 +#define DART_TAGS_U4 0x1000 /* Control Register fields */ -/* base address of table (pfn) */ -#define DARTCNTL_BASE_MASK 0xfffff -#define DARTCNTL_BASE_SHIFT 12 +/* U3 registers */ +#define DART_CNTL_U3_BASE_MASK 0xfffff +#define DART_CNTL_U3_BASE_SHIFT 12 +#define DART_CNTL_U3_FLUSHTLB 0x400 +#define DART_CNTL_U3_ENABLE 0x200 +#define DART_CNTL_U3_SIZE_MASK 0x1ff +#define DART_CNTL_U3_SIZE_SHIFT 0 + +/* U4 registers */ +#define DART_BASE_U4_BASE_MASK 0xffffff +#define DART_BASE_U4_BASE_SHIFT 0 +#define DART_CNTL_U4_FLUSHTLB 0x20000000 +#define DART_CNTL_U4_ENABLE 0x80000000 +#define DART_SIZE_U4_SIZE_MASK 0x1fff +#define DART_SIZE_U4_SIZE_SHIFT 0 + +#define DART_REG(r) (dart + ((r) >> 2)) +#define DART_IN(r) (in_be32(DART_REG(r))) +#define DART_OUT(r,v) (out_be32(DART_REG(r), (v))) -#define DARTCNTL_FLUSHTLB 0x400 -#define DARTCNTL_ENABLE 0x200 /* size of table in pages */ -#define DARTCNTL_SIZE_MASK 0x1ff -#define DARTCNTL_SIZE_SHIFT 0 /* DART table fields */ diff --git a/arch/powerpc/sysdev/dart_iommu.c b/arch/powerpc/sysdev/dart_iommu.c new file mode 100644 index 00000000000000..df0dbdee762a87 --- /dev/null +++ b/arch/powerpc/sysdev/dart_iommu.c @@ -0,0 +1,350 @@ +/* + * arch/powerpc/sysdev/dart_iommu.c + * + * Copyright (C) 2004 Olof Johansson , IBM Corporation + * Copyright (C) 2005 Benjamin Herrenschmidt , + * IBM Corporation + * + * Based on pSeries_iommu.c: + * Copyright (C) 2001 Mike Corrigan & Dave Engebretsen, IBM Corporation + * Copyright (C) 2004 Olof Johansson , IBM Corporation + * + * Dynamic DMA mapping support, Apple U3, U4 & IBM CPC925 "DART" iommu. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dart.h" + +extern int iommu_force_on; + +/* Physical base address and size of the DART table */ +unsigned long dart_tablebase; /* exported to htab_initialize */ +static unsigned long dart_tablesize; + +/* Virtual base address of the DART table */ +static u32 *dart_vbase; + +/* Mapped base address for the dart */ +static unsigned int *__iomem dart; + +/* Dummy val that entries are set to when unused */ +static unsigned int dart_emptyval; + +static struct iommu_table iommu_table_dart; +static int iommu_table_dart_inited; +static int dart_dirty; +static int dart_is_u4; + +#define DBG(...) + +static inline void dart_tlb_invalidate_all(void) +{ + unsigned long l = 0; + unsigned int reg, inv_bit; + unsigned long limit; + + DBG("dart: flush\n"); + + /* To invalidate the DART, set the DARTCNTL_FLUSHTLB bit in the + * control register and wait for it to clear. + * + * Gotcha: Sometimes, the DART won't detect that the bit gets + * set. If so, clear it and set it again. + */ + + limit = 0; + + inv_bit = dart_is_u4 ? DART_CNTL_U4_FLUSHTLB : DART_CNTL_U3_FLUSHTLB; +retry: + l = 0; + reg = DART_IN(DART_CNTL); + reg |= inv_bit; + DART_OUT(DART_CNTL, reg); + + while ((DART_IN(DART_CNTL) & inv_bit) && l < (1L << limit)) + l++; + if (l == (1L << limit)) { + if (limit < 4) { + limit++; + reg = DART_IN(DART_CNTL); + reg &= ~inv_bit; + DART_OUT(DART_CNTL, reg); + goto retry; + } else + panic("DART: TLB did not flush after waiting a long " + "time. Buggy U3 ?"); + } +} + +static void dart_flush(struct iommu_table *tbl) +{ + if (dart_dirty) + dart_tlb_invalidate_all(); + dart_dirty = 0; +} + +static void dart_build(struct iommu_table *tbl, long index, + long npages, unsigned long uaddr, + enum dma_data_direction direction) +{ + unsigned int *dp; + unsigned int rpn; + + DBG("dart: build at: %lx, %lx, addr: %x\n", index, npages, uaddr); + + index <<= DART_PAGE_FACTOR; + npages <<= DART_PAGE_FACTOR; + + dp = ((unsigned int*)tbl->it_base) + index; + + /* On U3, all memory is contigous, so we can move this + * out of the loop. + */ + while (npages--) { + rpn = virt_to_abs(uaddr) >> DART_PAGE_SHIFT; + + *(dp++) = DARTMAP_VALID | (rpn & DARTMAP_RPNMASK); + + rpn++; + uaddr += DART_PAGE_SIZE; + } + + dart_dirty = 1; +} + + +static void dart_free(struct iommu_table *tbl, long index, long npages) +{ + unsigned int *dp; + + /* We don't worry about flushing the TLB cache. The only drawback of + * not doing it is that we won't catch buggy device drivers doing + * bad DMAs, but then no 32-bit architecture ever does either. + */ + + DBG("dart: free at: %lx, %lx\n", index, npages); + + index <<= DART_PAGE_FACTOR; + npages <<= DART_PAGE_FACTOR; + + dp = ((unsigned int *)tbl->it_base) + index; + + while (npages--) + *(dp++) = dart_emptyval; +} + + +static int dart_init(struct device_node *dart_node) +{ + unsigned int i; + unsigned long tmp, base, size; + struct resource r; + + if (dart_tablebase == 0 || dart_tablesize == 0) { + printk(KERN_INFO "DART: table not allocated, using " + "direct DMA\n"); + return -ENODEV; + } + + if (of_address_to_resource(dart_node, 0, &r)) + panic("DART: can't get register base ! "); + + /* Make sure nothing from the DART range remains in the CPU cache + * from a previous mapping that existed before the kernel took + * over + */ + flush_dcache_phys_range(dart_tablebase, + dart_tablebase + dart_tablesize); + + /* Allocate a spare page to map all invalid DART pages. We need to do + * that to work around what looks like a problem with the HT bridge + * prefetching into invalid pages and corrupting data + */ + tmp = lmb_alloc(DART_PAGE_SIZE, DART_PAGE_SIZE); + if (!tmp) + panic("DART: Cannot allocate spare page!"); + dart_emptyval = DARTMAP_VALID | ((tmp >> DART_PAGE_SHIFT) & + DARTMAP_RPNMASK); + + /* Map in DART registers */ + dart = ioremap(r.start, r.end - r.start + 1); + if (dart == NULL) + panic("DART: Cannot map registers!"); + + /* Map in DART table */ + dart_vbase = ioremap(virt_to_abs(dart_tablebase), dart_tablesize); + + /* Fill initial table */ + for (i = 0; i < dart_tablesize/4; i++) + dart_vbase[i] = dart_emptyval; + + /* Initialize DART with table base and enable it. */ + base = dart_tablebase >> DART_PAGE_SHIFT; + size = dart_tablesize >> DART_PAGE_SHIFT; + if (dart_is_u4) { + BUG_ON(size & ~DART_SIZE_U4_SIZE_MASK); + DART_OUT(DART_BASE_U4, base); + DART_OUT(DART_SIZE_U4, size); + DART_OUT(DART_CNTL, DART_CNTL_U4_ENABLE); + } else { + BUG_ON(size & ~DART_CNTL_U3_SIZE_MASK); + DART_OUT(DART_CNTL, + DART_CNTL_U3_ENABLE | + (base << DART_CNTL_U3_BASE_SHIFT) | + (size << DART_CNTL_U3_SIZE_SHIFT)); + } + + /* Invalidate DART to get rid of possible stale TLBs */ + dart_tlb_invalidate_all(); + + printk(KERN_INFO "DART IOMMU initialized for %s type chipset\n", + dart_is_u4 ? "U4" : "U3"); + + return 0; +} + +static void iommu_table_dart_setup(void) +{ + iommu_table_dart.it_busno = 0; + iommu_table_dart.it_offset = 0; + /* it_size is in number of entries */ + iommu_table_dart.it_size = (dart_tablesize / sizeof(u32)) >> DART_PAGE_FACTOR; + + /* Initialize the common IOMMU code */ + iommu_table_dart.it_base = (unsigned long)dart_vbase; + iommu_table_dart.it_index = 0; + iommu_table_dart.it_blocksize = 1; + iommu_init_table(&iommu_table_dart); + + /* Reserve the last page of the DART to avoid possible prefetch + * past the DART mapped area + */ + set_bit(iommu_table_dart.it_size - 1, iommu_table_dart.it_map); +} + +static void iommu_dev_setup_dart(struct pci_dev *dev) +{ + struct device_node *dn; + + /* We only have one iommu table on the mac for now, which makes + * things simple. Setup all PCI devices to point to this table + * + * We must use pci_device_to_OF_node() to make sure that + * we get the real "final" pointer to the device in the + * pci_dev sysdata and not the temporary PHB one + */ + dn = pci_device_to_OF_node(dev); + + if (dn) + PCI_DN(dn)->iommu_table = &iommu_table_dart; +} + +static void iommu_bus_setup_dart(struct pci_bus *bus) +{ + struct device_node *dn; + + if (!iommu_table_dart_inited) { + iommu_table_dart_inited = 1; + iommu_table_dart_setup(); + } + + dn = pci_bus_to_OF_node(bus); + + if (dn) + PCI_DN(dn)->iommu_table = &iommu_table_dart; +} + +static void iommu_dev_setup_null(struct pci_dev *dev) { } +static void iommu_bus_setup_null(struct pci_bus *bus) { } + +void iommu_init_early_dart(void) +{ + struct device_node *dn; + + /* Find the DART in the device-tree */ + dn = of_find_compatible_node(NULL, "dart", "u3-dart"); + if (dn == NULL) { + dn = of_find_compatible_node(NULL, "dart", "u4-dart"); + if (dn == NULL) + goto bail; + dart_is_u4 = 1; + } + + /* Setup low level TCE operations for the core IOMMU code */ + ppc_md.tce_build = dart_build; + ppc_md.tce_free = dart_free; + ppc_md.tce_flush = dart_flush; + + /* Initialize the DART HW */ + if (dart_init(dn) == 0) { + ppc_md.iommu_dev_setup = iommu_dev_setup_dart; + ppc_md.iommu_bus_setup = iommu_bus_setup_dart; + + /* Setup pci_dma ops */ + pci_iommu_init(); + + return; + } + + bail: + /* If init failed, use direct iommu and null setup functions */ + ppc_md.iommu_dev_setup = iommu_dev_setup_null; + ppc_md.iommu_bus_setup = iommu_bus_setup_null; + + /* Setup pci_dma ops */ + pci_direct_iommu_init(); +} + + +void __init alloc_dart_table(void) +{ + /* Only reserve DART space if machine has more than 2GB of RAM + * or if requested with iommu=on on cmdline. + */ + if (lmb_end_of_DRAM() <= 0x80000000ull && !iommu_force_on) + return; + + /* 512 pages (2MB) is max DART tablesize. */ + dart_tablesize = 1UL << 21; + /* 16MB (1 << 24) alignment. We allocate a full 16Mb chuck since we + * will blow up an entire large page anyway in the kernel mapping + */ + dart_tablebase = (unsigned long) + abs_to_virt(lmb_alloc_base(1UL<<24, 1UL<<24, 0x80000000L)); + + printk(KERN_INFO "DART table allocated at: %lx\n", dart_tablebase); +} diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 9513ea78e6c193..4f26304d0263b9 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -13,6 +13,9 @@ */ #undef DEBUG +#undef DEBUG_IPI +#undef DEBUG_IRQ +#undef DEBUG_LOW #include #include @@ -168,35 +171,86 @@ static void __init mpic_test_broken_ipi(struct mpic *mpic) /* Test if an interrupt is sourced from HyperTransport (used on broken U3s) * to force the edge setting on the MPIC and do the ack workaround. */ -static inline int mpic_is_ht_interrupt(struct mpic *mpic, unsigned int source_no) +static inline int mpic_is_ht_interrupt(struct mpic *mpic, unsigned int source) { - if (source_no >= 128 || !mpic->fixups) + if (source >= 128 || !mpic->fixups) return 0; - return mpic->fixups[source_no].base != NULL; + return mpic->fixups[source].base != NULL; } -static inline void mpic_apic_end_irq(struct mpic *mpic, unsigned int source_no) +static inline void mpic_ht_end_irq(struct mpic *mpic, unsigned int source) { - struct mpic_irq_fixup *fixup = &mpic->fixups[source_no]; + struct mpic_irq_fixup *fixup = &mpic->fixups[source]; - spin_lock(&mpic->fixup_lock); - writeb(0x11 + 2 * fixup->irq, fixup->base + 2); - writel(fixup->data, fixup->base + 4); - spin_unlock(&mpic->fixup_lock); + if (fixup->applebase) { + unsigned int soff = (fixup->index >> 3) & ~3; + unsigned int mask = 1U << (fixup->index & 0x1f); + writel(mask, fixup->applebase + soff); + } else { + spin_lock(&mpic->fixup_lock); + writeb(0x11 + 2 * fixup->index, fixup->base + 2); + writel(fixup->data, fixup->base + 4); + spin_unlock(&mpic->fixup_lock); + } } +static void mpic_startup_ht_interrupt(struct mpic *mpic, unsigned int source, + unsigned int irqflags) +{ + struct mpic_irq_fixup *fixup = &mpic->fixups[source]; + unsigned long flags; + u32 tmp; + + if (fixup->base == NULL) + return; + + DBG("startup_ht_interrupt(%u, %u) index: %d\n", + source, irqflags, fixup->index); + spin_lock_irqsave(&mpic->fixup_lock, flags); + /* Enable and configure */ + writeb(0x10 + 2 * fixup->index, fixup->base + 2); + tmp = readl(fixup->base + 4); + tmp &= ~(0x23U); + if (irqflags & IRQ_LEVEL) + tmp |= 0x22; + writel(tmp, fixup->base + 4); + spin_unlock_irqrestore(&mpic->fixup_lock, flags); +} + +static void mpic_shutdown_ht_interrupt(struct mpic *mpic, unsigned int source, + unsigned int irqflags) +{ + struct mpic_irq_fixup *fixup = &mpic->fixups[source]; + unsigned long flags; + u32 tmp; + + if (fixup->base == NULL) + return; + + DBG("shutdown_ht_interrupt(%u, %u)\n", source, irqflags); + + /* Disable */ + spin_lock_irqsave(&mpic->fixup_lock, flags); + writeb(0x10 + 2 * fixup->index, fixup->base + 2); + tmp = readl(fixup->base + 4); + tmp &= ~1U; + writel(tmp, fixup->base + 4); + spin_unlock_irqrestore(&mpic->fixup_lock, flags); +} -static void __init mpic_scan_ioapic(struct mpic *mpic, u8 __iomem *devbase) +static void __init mpic_scan_ht_pic(struct mpic *mpic, u8 __iomem *devbase, + unsigned int devfn, u32 vdid) { int i, irq, n; + u8 __iomem *base; u32 tmp; u8 pos; - for (pos = readb(devbase + 0x34); pos; pos = readb(devbase + pos + 1)) { - u8 id = readb(devbase + pos); - - if (id == 0x08) { + for (pos = readb(devbase + PCI_CAPABILITY_LIST); pos != 0; + pos = readb(devbase + pos + PCI_CAP_LIST_NEXT)) { + u8 id = readb(devbase + pos + PCI_CAP_LIST_ID); + if (id == PCI_CAP_ID_HT_IRQCONF) { id = readb(devbase + pos + 3); if (id == 0x80) break; @@ -205,33 +259,41 @@ static void __init mpic_scan_ioapic(struct mpic *mpic, u8 __iomem *devbase) if (pos == 0) return; - printk(KERN_INFO "mpic: - Workarounds @ %p, pos = 0x%02x\n", devbase, pos); + base = devbase + pos; + writeb(0x01, base + 2); + n = (readl(base + 4) >> 16) & 0xff; - devbase += pos; - - writeb(0x01, devbase + 2); - n = (readl(devbase + 4) >> 16) & 0xff; + printk(KERN_INFO "mpic: - HT:%02x.%x [0x%02x] vendor %04x device %04x" + " has %d irqs\n", + devfn >> 3, devfn & 0x7, pos, vdid & 0xffff, vdid >> 16, n + 1); for (i = 0; i <= n; i++) { - writeb(0x10 + 2 * i, devbase + 2); - tmp = readl(devbase + 4); - if ((tmp & 0x21) != 0x20) - continue; + writeb(0x10 + 2 * i, base + 2); + tmp = readl(base + 4); irq = (tmp >> 16) & 0xff; - mpic->fixups[irq].irq = i; - mpic->fixups[irq].base = devbase; - writeb(0x11 + 2 * i, devbase + 2); - mpic->fixups[irq].data = readl(devbase + 4) | 0x80000000; + DBG("HT PIC index 0x%x, irq 0x%x, tmp: %08x\n", i, irq, tmp); + /* mask it , will be unmasked later */ + tmp |= 0x1; + writel(tmp, base + 4); + mpic->fixups[irq].index = i; + mpic->fixups[irq].base = base; + /* Apple HT PIC has a non-standard way of doing EOIs */ + if ((vdid & 0xffff) == 0x106b) + mpic->fixups[irq].applebase = devbase + 0x60; + else + mpic->fixups[irq].applebase = NULL; + writeb(0x11 + 2 * i, base + 2); + mpic->fixups[irq].data = readl(base + 4) | 0x80000000; } } -static void __init mpic_scan_ioapics(struct mpic *mpic) +static void __init mpic_scan_ht_pics(struct mpic *mpic) { unsigned int devfn; u8 __iomem *cfgspace; - printk(KERN_INFO "mpic: Setting up IO-APICs workarounds for U3\n"); + printk(KERN_INFO "mpic: Setting up HT PICs workarounds for U3/U4\n"); /* Allocate fixups array */ mpic->fixups = alloc_bootmem(128 * sizeof(struct mpic_irq_fixup)); @@ -247,13 +309,14 @@ static void __init mpic_scan_ioapics(struct mpic *mpic) cfgspace = ioremap(0xf2000000, 0x10000); BUG_ON(cfgspace == NULL); - /* Now we scan all slots. We do a very quick scan, we read the header type, - * vendor ID and device ID only, that's plenty enough + /* Now we scan all slots. We do a very quick scan, we read the header + * type, vendor ID and device ID only, that's plenty enough */ for (devfn = 0; devfn < 0x100; devfn++) { u8 __iomem *devbase = cfgspace + (devfn << 8); u8 hdr_type = readb(devbase + PCI_HEADER_TYPE); u32 l = readl(devbase + PCI_VENDOR_ID); + u16 s; DBG("devfn %x, l: %x\n", devfn, l); @@ -261,8 +324,12 @@ static void __init mpic_scan_ioapics(struct mpic *mpic) if (l == 0xffffffff || l == 0x00000000 || l == 0x0000ffff || l == 0xffff0000) goto next; + /* Check if is supports capability lists */ + s = readw(devbase + PCI_STATUS); + if (!(s & PCI_STATUS_CAP_LIST)) + goto next; - mpic_scan_ioapic(mpic, devbase); + mpic_scan_ht_pic(mpic, devbase, devfn, l); next: /* next device, if function 0 */ @@ -363,6 +430,31 @@ static void mpic_enable_irq(unsigned int irq) break; } } while(mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) & MPIC_VECPRI_MASK); + +#ifdef CONFIG_MPIC_BROKEN_U3 + if (mpic->flags & MPIC_BROKEN_U3) { + unsigned int src = irq - mpic->irq_offset; + if (mpic_is_ht_interrupt(mpic, src) && + (irq_desc[irq].status & IRQ_LEVEL)) + mpic_ht_end_irq(mpic, src); + } +#endif /* CONFIG_MPIC_BROKEN_U3 */ +} + +static unsigned int mpic_startup_irq(unsigned int irq) +{ +#ifdef CONFIG_MPIC_BROKEN_U3 + struct mpic *mpic = mpic_from_irq(irq); + unsigned int src = irq - mpic->irq_offset; + + if (mpic_is_ht_interrupt(mpic, src)) + mpic_startup_ht_interrupt(mpic, src, irq_desc[irq].status); + +#endif /* CONFIG_MPIC_BROKEN_U3 */ + + mpic_enable_irq(irq); + + return 0; } static void mpic_disable_irq(unsigned int irq) @@ -386,12 +478,27 @@ static void mpic_disable_irq(unsigned int irq) } while(!(mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) & MPIC_VECPRI_MASK)); } +static void mpic_shutdown_irq(unsigned int irq) +{ +#ifdef CONFIG_MPIC_BROKEN_U3 + struct mpic *mpic = mpic_from_irq(irq); + unsigned int src = irq - mpic->irq_offset; + + if (mpic_is_ht_interrupt(mpic, src)) + mpic_shutdown_ht_interrupt(mpic, src, irq_desc[irq].status); + +#endif /* CONFIG_MPIC_BROKEN_U3 */ + + mpic_disable_irq(irq); +} + static void mpic_end_irq(unsigned int irq) { struct mpic *mpic = mpic_from_irq(irq); +#ifdef DEBUG_IRQ DBG("%s: end_irq: %d\n", mpic->name, irq); - +#endif /* We always EOI on end_irq() even for edge interrupts since that * should only lower the priority, the MPIC should have properly * latched another edge interrupt coming in anyway @@ -400,8 +507,9 @@ static void mpic_end_irq(unsigned int irq) #ifdef CONFIG_MPIC_BROKEN_U3 if (mpic->flags & MPIC_BROKEN_U3) { unsigned int src = irq - mpic->irq_offset; - if (mpic_is_ht_interrupt(mpic, src)) - mpic_apic_end_irq(mpic, src); + if (mpic_is_ht_interrupt(mpic, src) && + (irq_desc[irq].status & IRQ_LEVEL)) + mpic_ht_end_irq(mpic, src); } #endif /* CONFIG_MPIC_BROKEN_U3 */ @@ -482,6 +590,8 @@ struct mpic * __init mpic_alloc(unsigned long phys_addr, mpic->name = name; mpic->hc_irq.typename = name; + mpic->hc_irq.startup = mpic_startup_irq; + mpic->hc_irq.shutdown = mpic_shutdown_irq; mpic->hc_irq.enable = mpic_enable_irq; mpic->hc_irq.disable = mpic_disable_irq; mpic->hc_irq.end = mpic_end_irq; @@ -650,10 +760,10 @@ void __init mpic_init(struct mpic *mpic) mpic->irq_count = mpic->num_sources; #ifdef CONFIG_MPIC_BROKEN_U3 - /* Do the ioapic fixups on U3 broken mpic */ + /* Do the HT PIC fixups on U3 broken mpic */ DBG("MPIC flags: %x\n", mpic->flags); if ((mpic->flags & MPIC_BROKEN_U3) && (mpic->flags & MPIC_PRIMARY)) - mpic_scan_ioapics(mpic); + mpic_scan_ht_pics(mpic); #endif /* CONFIG_MPIC_BROKEN_U3 */ for (i = 0; i < mpic->num_sources; i++) { @@ -840,7 +950,9 @@ void mpic_send_ipi(unsigned int ipi_no, unsigned int cpu_mask) BUG_ON(mpic == NULL); +#ifdef DEBUG_IPI DBG("%s: send_ipi(ipi_no: %d)\n", mpic->name, ipi_no); +#endif mpic_cpu_write(MPIC_CPU_IPI_DISPATCH_0 + ipi_no * 0x10, mpic_physmask(cpu_mask & cpus_addr(cpu_online_map)[0])); @@ -851,19 +963,28 @@ int mpic_get_one_irq(struct mpic *mpic, struct pt_regs *regs) u32 irq; irq = mpic_cpu_read(MPIC_CPU_INTACK) & MPIC_VECPRI_VECTOR_MASK; +#ifdef DEBUG_LOW DBG("%s: get_one_irq(): %d\n", mpic->name, irq); - +#endif if (mpic->cascade && irq == mpic->cascade_vec) { +#ifdef DEBUG_LOW DBG("%s: cascading ...\n", mpic->name); +#endif irq = mpic->cascade(regs, mpic->cascade_data); mpic_eoi(mpic); return irq; } if (unlikely(irq == MPIC_VEC_SPURRIOUS)) return -1; - if (irq < MPIC_VEC_IPI_0) + if (irq < MPIC_VEC_IPI_0) { +#ifdef DEBUG_IRQ + DBG("%s: irq %d\n", mpic->name, irq + mpic->irq_offset); +#endif return irq + mpic->irq_offset; + } +#ifdef DEBUG_IPI DBG("%s: ipi %d !\n", mpic->name, irq - MPIC_VEC_IPI_0); +#endif return irq - MPIC_VEC_IPI_0 + mpic->ipi_offset; } diff --git a/arch/powerpc/sysdev/u3_iommu.c b/arch/powerpc/sysdev/u3_iommu.c deleted file mode 100644 index 5c1a26a6d00c3c..00000000000000 --- a/arch/powerpc/sysdev/u3_iommu.c +++ /dev/null @@ -1,327 +0,0 @@ -/* - * arch/powerpc/sysdev/u3_iommu.c - * - * Copyright (C) 2004 Olof Johansson , IBM Corporation - * - * Based on pSeries_iommu.c: - * Copyright (C) 2001 Mike Corrigan & Dave Engebretsen, IBM Corporation - * Copyright (C) 2004 Olof Johansson , IBM Corporation - * - * Dynamic DMA mapping support, Apple U3 & IBM CPC925 "DART" iommu. - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dart.h" - -extern int iommu_force_on; - -/* Physical base address and size of the DART table */ -unsigned long dart_tablebase; /* exported to htab_initialize */ -static unsigned long dart_tablesize; - -/* Virtual base address of the DART table */ -static u32 *dart_vbase; - -/* Mapped base address for the dart */ -static unsigned int *dart; - -/* Dummy val that entries are set to when unused */ -static unsigned int dart_emptyval; - -static struct iommu_table iommu_table_u3; -static int iommu_table_u3_inited; -static int dart_dirty; - -#define DBG(...) - -static inline void dart_tlb_invalidate_all(void) -{ - unsigned long l = 0; - unsigned int reg; - unsigned long limit; - - DBG("dart: flush\n"); - - /* To invalidate the DART, set the DARTCNTL_FLUSHTLB bit in the - * control register and wait for it to clear. - * - * Gotcha: Sometimes, the DART won't detect that the bit gets - * set. If so, clear it and set it again. - */ - - limit = 0; - -retry: - reg = in_be32((unsigned int *)dart+DARTCNTL); - reg |= DARTCNTL_FLUSHTLB; - out_be32((unsigned int *)dart+DARTCNTL, reg); - - l = 0; - while ((in_be32((unsigned int *)dart+DARTCNTL) & DARTCNTL_FLUSHTLB) && - l < (1L<it_base) + index; - - /* On U3, all memory is contigous, so we can move this - * out of the loop. - */ - while (npages--) { - rpn = virt_to_abs(uaddr) >> DART_PAGE_SHIFT; - - *(dp++) = DARTMAP_VALID | (rpn & DARTMAP_RPNMASK); - - rpn++; - uaddr += DART_PAGE_SIZE; - } - - dart_dirty = 1; -} - - -static void dart_free(struct iommu_table *tbl, long index, long npages) -{ - unsigned int *dp; - - /* We don't worry about flushing the TLB cache. The only drawback of - * not doing it is that we won't catch buggy device drivers doing - * bad DMAs, but then no 32-bit architecture ever does either. - */ - - DBG("dart: free at: %lx, %lx\n", index, npages); - - index <<= DART_PAGE_FACTOR; - npages <<= DART_PAGE_FACTOR; - - dp = ((unsigned int *)tbl->it_base) + index; - - while (npages--) - *(dp++) = dart_emptyval; -} - - -static int dart_init(struct device_node *dart_node) -{ - unsigned int regword; - unsigned int i; - unsigned long tmp; - - if (dart_tablebase == 0 || dart_tablesize == 0) { - printk(KERN_INFO "U3-DART: table not allocated, using direct DMA\n"); - return -ENODEV; - } - - /* Make sure nothing from the DART range remains in the CPU cache - * from a previous mapping that existed before the kernel took - * over - */ - flush_dcache_phys_range(dart_tablebase, dart_tablebase + dart_tablesize); - - /* Allocate a spare page to map all invalid DART pages. We need to do - * that to work around what looks like a problem with the HT bridge - * prefetching into invalid pages and corrupting data - */ - tmp = lmb_alloc(DART_PAGE_SIZE, DART_PAGE_SIZE); - if (!tmp) - panic("U3-DART: Cannot allocate spare page!"); - dart_emptyval = DARTMAP_VALID | ((tmp >> DART_PAGE_SHIFT) & DARTMAP_RPNMASK); - - /* Map in DART registers. FIXME: Use device node to get base address */ - dart = ioremap(DART_BASE, 0x7000); - if (dart == NULL) - panic("U3-DART: Cannot map registers!"); - - /* Set initial control register contents: table base, - * table size and enable bit - */ - regword = DARTCNTL_ENABLE | - ((dart_tablebase >> DART_PAGE_SHIFT) << DARTCNTL_BASE_SHIFT) | - (((dart_tablesize >> DART_PAGE_SHIFT) & DARTCNTL_SIZE_MASK) - << DARTCNTL_SIZE_SHIFT); - dart_vbase = ioremap(virt_to_abs(dart_tablebase), dart_tablesize); - - /* Fill initial table */ - for (i = 0; i < dart_tablesize/4; i++) - dart_vbase[i] = dart_emptyval; - - /* Initialize DART with table base and enable it. */ - out_be32((unsigned int *)dart, regword); - - /* Invalidate DART to get rid of possible stale TLBs */ - dart_tlb_invalidate_all(); - - printk(KERN_INFO "U3/CPC925 DART IOMMU initialized\n"); - - return 0; -} - -static void iommu_table_u3_setup(void) -{ - iommu_table_u3.it_busno = 0; - iommu_table_u3.it_offset = 0; - /* it_size is in number of entries */ - iommu_table_u3.it_size = (dart_tablesize / sizeof(u32)) >> DART_PAGE_FACTOR; - - /* Initialize the common IOMMU code */ - iommu_table_u3.it_base = (unsigned long)dart_vbase; - iommu_table_u3.it_index = 0; - iommu_table_u3.it_blocksize = 1; - iommu_init_table(&iommu_table_u3); - - /* Reserve the last page of the DART to avoid possible prefetch - * past the DART mapped area - */ - set_bit(iommu_table_u3.it_size - 1, iommu_table_u3.it_map); -} - -static void iommu_dev_setup_u3(struct pci_dev *dev) -{ - struct device_node *dn; - - /* We only have one iommu table on the mac for now, which makes - * things simple. Setup all PCI devices to point to this table - * - * We must use pci_device_to_OF_node() to make sure that - * we get the real "final" pointer to the device in the - * pci_dev sysdata and not the temporary PHB one - */ - dn = pci_device_to_OF_node(dev); - - if (dn) - PCI_DN(dn)->iommu_table = &iommu_table_u3; -} - -static void iommu_bus_setup_u3(struct pci_bus *bus) -{ - struct device_node *dn; - - if (!iommu_table_u3_inited) { - iommu_table_u3_inited = 1; - iommu_table_u3_setup(); - } - - dn = pci_bus_to_OF_node(bus); - - if (dn) - PCI_DN(dn)->iommu_table = &iommu_table_u3; -} - -static void iommu_dev_setup_null(struct pci_dev *dev) { } -static void iommu_bus_setup_null(struct pci_bus *bus) { } - -void iommu_init_early_u3(void) -{ - struct device_node *dn; - - /* Find the DART in the device-tree */ - dn = of_find_compatible_node(NULL, "dart", "u3-dart"); - if (dn == NULL) - return; - - /* Setup low level TCE operations for the core IOMMU code */ - ppc_md.tce_build = dart_build; - ppc_md.tce_free = dart_free; - ppc_md.tce_flush = dart_flush; - - /* Initialize the DART HW */ - if (dart_init(dn)) { - /* If init failed, use direct iommu and null setup functions */ - ppc_md.iommu_dev_setup = iommu_dev_setup_null; - ppc_md.iommu_bus_setup = iommu_bus_setup_null; - - /* Setup pci_dma ops */ - pci_direct_iommu_init(); - } else { - ppc_md.iommu_dev_setup = iommu_dev_setup_u3; - ppc_md.iommu_bus_setup = iommu_bus_setup_u3; - - /* Setup pci_dma ops */ - pci_iommu_init(); - } -} - - -void __init alloc_u3_dart_table(void) -{ - /* Only reserve DART space if machine has more than 2GB of RAM - * or if requested with iommu=on on cmdline. - */ - if (lmb_end_of_DRAM() <= 0x80000000ull && !iommu_force_on) - return; - - /* 512 pages (2MB) is max DART tablesize. */ - dart_tablesize = 1UL << 21; - /* 16MB (1 << 24) alignment. We allocate a full 16Mb chuck since we - * will blow up an entire large page anyway in the kernel mapping - */ - dart_tablebase = (unsigned long) - abs_to_virt(lmb_alloc_base(1UL<<24, 1UL<<24, 0x80000000L)); - - printk(KERN_INFO "U3-DART allocated at: %lx\n", dart_tablebase); -} diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c index a8d3bc0a9c5c06..5013b1285e2242 100644 --- a/drivers/ide/ppc/pmac.c +++ b/drivers/ide/ppc/pmac.c @@ -1686,7 +1686,7 @@ pmac_ide_probe(void) #else macio_register_driver(&pmac_ide_macio_driver); pci_register_driver(&pmac_ide_pci_driver); -#endif +#endif } #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c index e8378274d710f0..96226116a64625 100644 --- a/drivers/macintosh/smu.c +++ b/drivers/macintosh/smu.c @@ -53,7 +53,7 @@ #undef DEBUG_SMU #ifdef DEBUG_SMU -#define DPRINTK(fmt, args...) do { udbg_printf(KERN_DEBUG fmt , ##args); } while (0) +#define DPRINTK(fmt, args...) do { printk(KERN_DEBUG fmt , ##args); } while (0) #else #define DPRINTK(fmt, args...) do { } while (0) #endif @@ -909,10 +909,13 @@ static struct smu_sdbp_header *smu_create_sdb_partition(int id) struct property *prop; /* First query the partition info */ + DPRINTK("SMU: Query partition infos ... (irq=%d)\n", smu->db_irq); smu_queue_simple(&cmd, SMU_CMD_PARTITION_COMMAND, 2, smu_done_complete, &comp, SMU_CMD_PARTITION_LATEST, id); wait_for_completion(&comp); + DPRINTK("SMU: done, status: %d, reply_len: %d\n", + cmd.cmd.status, cmd.cmd.reply_len); /* Partition doesn't exist (or other error) */ if (cmd.cmd.status != 0 || cmd.cmd.reply_len != 6) @@ -975,6 +978,8 @@ struct smu_sdbp_header *__smu_get_sdb_partition(int id, unsigned int *size, sprintf(pname, "sdb-partition-%02x", id); + DPRINTK("smu_get_sdb_partition(%02x)\n", id); + if (interruptible) { int rc; rc = down_interruptible(&smu_part_access); @@ -986,6 +991,7 @@ struct smu_sdbp_header *__smu_get_sdb_partition(int id, unsigned int *size, part = (struct smu_sdbp_header *)get_property(smu->of_node, pname, size); if (part == NULL) { + DPRINTK("trying to extract from SMU ...\n"); part = smu_create_sdb_partition(id); if (part != NULL && size) *size = part->len << 2; diff --git a/include/asm-powerpc/iommu.h b/include/asm-powerpc/iommu.h index f89f0605089378..59f062668997af 100644 --- a/include/asm-powerpc/iommu.h +++ b/include/asm-powerpc/iommu.h @@ -56,7 +56,7 @@ struct device_node; /* Walks all buses and creates iommu tables */ extern void iommu_setup_pSeries(void); -extern void iommu_setup_u3(void); +extern void iommu_setup_dart(void); /* Frees table for an individual device node */ extern void iommu_free_table(struct device_node *dn); @@ -104,7 +104,7 @@ extern void iommu_unmap_single(struct iommu_table *tbl, dma_addr_t dma_handle, extern void iommu_init_early_pSeries(void); extern void iommu_init_early_iSeries(void); -extern void iommu_init_early_u3(void); +extern void iommu_init_early_dart(void); #ifdef CONFIG_PCI extern void pci_iommu_init(void); @@ -113,6 +113,6 @@ extern void pci_direct_iommu_init(void); static inline void pci_iommu_init(void) { } #endif -extern void alloc_u3_dart_table(void); +extern void alloc_dart_table(void); #endif /* _ASM_IOMMU_H */ diff --git a/include/asm-powerpc/mpic.h b/include/asm-powerpc/mpic.h index 6ce27e1b564648..bf7e717932054f 100644 --- a/include/asm-powerpc/mpic.h +++ b/include/asm-powerpc/mpic.h @@ -117,8 +117,9 @@ typedef int (*mpic_cascade_t)(struct pt_regs *regs, void *data); struct mpic_irq_fixup { u8 __iomem *base; + u8 __iomem *applebase; u32 data; - unsigned int irq; + unsigned int index; }; #endif /* CONFIG_MPIC_BROKEN_U3 */ diff --git a/include/asm-powerpc/pmac_feature.h b/include/asm-powerpc/pmac_feature.h index e9683bcff19bce..f6997ed5179ee5 100644 --- a/include/asm-powerpc/pmac_feature.h +++ b/include/asm-powerpc/pmac_feature.h @@ -121,6 +121,7 @@ #define PMAC_TYPE_IMAC_G5 0x152 /* iMac G5 */ #define PMAC_TYPE_XSERVE_G5 0x153 /* Xserve G5 */ #define PMAC_TYPE_UNKNOWN_K2 0x19f /* Any other K2 based */ +#define PMAC_TYPE_UNKNOWN_SHASTA 0x19e /* Any other Shasta based */ /* * Motherboard flags @@ -341,6 +342,7 @@ enum { macio_pangea, macio_intrepid, macio_keylargo2, + macio_shasta, }; struct macio_chip diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index e2a089b051eddd..d27a78b7129705 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -196,6 +196,7 @@ #define PCI_CAP_ID_MSI 0x05 /* Message Signalled Interrupts */ #define PCI_CAP_ID_CHSWP 0x06 /* CompactPCI HotSwap */ #define PCI_CAP_ID_PCIX 0x07 /* PCI-X */ +#define PCI_CAP_ID_HT_IRQCONF 0x08 /* HyperTransport IRQ Configuration */ #define PCI_CAP_ID_SHPC 0x0C /* PCI Standard Hot-Plug Controller */ #define PCI_CAP_ID_EXP 0x10 /* PCI Express */ #define PCI_CAP_ID_MSIX 0x11 /* MSI-X */ -- cgit 1.2.3-korg From a04c8780fd234aeeba5e87f7e37beffd05ef21ae Mon Sep 17 00:00:00 2001 From: Kristian Mueller Date: Thu, 15 Dec 2005 12:31:55 +0800 Subject: [PATCH] via-pmu: compile without Power Management support Fix compilation of via-pmu.c without Power Management support. Signed-off-by: Paul Mackerras --- drivers/macintosh/via-pmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index 79c7b44a94eef5..3c0552016b9138 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c @@ -158,8 +158,8 @@ static int pmu_version; static int drop_interrupts; #if defined(CONFIG_PM) && defined(CONFIG_PPC32) static int option_lid_wakeup = 1; -static int sleep_in_progress; #endif /* CONFIG_PM && CONFIG_PPC32 */ +static int sleep_in_progress; static unsigned long async_req_locks; static unsigned int pmu_irq_stats[11]; -- cgit 1.2.3-korg From 03929c76f3e5af919fb762e9882a9c286d361e7d Mon Sep 17 00:00:00 2001 From: Aristeu Sergio Rozanski Filho Date: Thu, 29 Dec 2005 19:18:49 -0200 Subject: [PATCH] ppc32: cpm_uart: fix xchar sending while using SCC as uart and as serial console at same time I got this: [ 138.214258] Oops: kernel access of bad area, sig: 11 [#1] [ 138.218832] PREEMPT [ 138.221021] NIP: C0105C48 LR: C0105E60 SP: C03D5D10 REGS: c03d5c60 TRAP: 0300 Not tainted [ 138.229280] MSR: 00009032 EE: 1 PR: 0 FP: 0 ME: 1 IR/DR: 11 [ 138.234713] DAR: 00000000, DSISR: C0000000 [ 138.238745] TASK = c0349420[693] 'sh' THREAD: c03d4000 [ 138.243754] Last syscall: 6 [ 138.246402] GPR00: FEFFFFFF C03D5D10 C0349420 C01FB094 00000011 00000000 C1ECFBBC C01F24B0 [ 138.254602] GPR08: FF002820 00000000 FF0028C0 00000000 19133615 A0CBCD5E 02000300 00000000 [ 138.262804] GPR16: 00000000 01FF9E4C 00000000 7FA9A770 00000000 00000000 1003E2A8 00000000 [ 138.271003] GPR24: 100562F4 7F9B6EF4 C0210000 C02A5338 C01FB094 00000000 C01FB094 C1F14574 [ 138.279376] NIP [c0105c48] cpm_uart_tx_pump+0x4c/0x22c [ 138.284419] LR [c0105e60] cpm_uart_start_tx+0x38/0xb0 [ 138.289361] Call trace: [ 138.291762] [c0105e60] cpm_uart_start_tx+0x38/0xb0 [ 138.296547] [c010277c] uart_send_xchar+0x88/0x118 [ 138.301244] [c01029a0] uart_unthrottle+0x6c/0x138 [ 138.305942] [c00ece10] check_unthrottle+0x60/0x64 [ 138.310641] [c00ecec4] reset_buffer_flags+0xb0/0x138 [ 138.315595] [c00ecf64] n_tty_flush_buffer+0x18/0x78 [ 138.320465] [c00e81b0] tty_ldisc_flush+0x64/0x7c [ 138.325078] [c010410c] uart_close+0xf0/0x2c8 [ 138.329348] [c00e9c48] release_dev+0x724/0x8d4 [ 138.333790] [c00e9e18] tty_release+0x20/0x3c [ 138.338061] [c006e544] __fput+0x178/0x1e0 [ 138.342076] [c006c43c] filp_close+0x54/0xac [ 138.346261] [c0002d90] ret_from_syscall+0x0/0x44 [ 138.352386] note: sh[693] exited with preempt_count 2 a easy way to reproduce it is log into the system using ssh and do: cat >/dev/ttyCPM0 then, switch to minicom and write some stuff on it back to ssh, a control C produce the oops this happens because uart_close calls uart_shutdown which frees xmit.buf, currently used by xchar sending in cpm_uart_tx_pump(), which seems wrong. the attached patch fixes the oops and also fixes xchar sending. Signed-off-by: Paul Mackerras --- drivers/serial/cpm_uart/cpm_uart_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/serial/cpm_uart/cpm_uart_core.c b/drivers/serial/cpm_uart/cpm_uart_core.c index 987d22b53c2209..16af5626c243be 100644 --- a/drivers/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/serial/cpm_uart/cpm_uart_core.c @@ -608,7 +608,7 @@ static int cpm_uart_tx_pump(struct uart_port *port) p = cpm2cpu_addr(bdp->cbd_bufaddr); - *p++ = xmit->buf[xmit->tail]; + *p++ = port->x_char; bdp->cbd_datlen = 1; bdp->cbd_sc |= BD_SC_READY; /* Get next BD. */ -- cgit 1.2.3-korg From 730745a5c45093982112ddc94cee6a9973455641 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Sat, 7 Jan 2006 11:30:44 +1100 Subject: [PATCH] 1/5 powerpc: Rework PowerMac i2c part 1 This is the first part of a rework of the PowerMac i2c code. It completely reworks the "low_i2c" layer. It is now more flexible, supports KeyWest, SMU and PMU i2c busses, and provides functions to match device nodes to i2c busses and adapters. This patch also extends & fix some bugs in the SMU driver related to i2c support and removes the clock spreading hacks from the pmac feature code rather than adapting them to the new API since they'll be replaced by the platform function code completely in patch 3/5 Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/powermac/feature.c | 127 ----- arch/powerpc/platforms/powermac/low_i2c.c | 853 ++++++++++++++++++++++++------ arch/powerpc/platforms/powermac/setup.c | 23 +- arch/powerpc/platforms/powermac/smp.c | 75 +-- drivers/i2c/busses/i2c-pmac-smu.c | 17 +- drivers/macintosh/smu.c | 58 +- drivers/macintosh/via-pmu.c | 264 +-------- include/asm-powerpc/pmac_feature.h | 4 - include/asm-powerpc/pmac_low_i2c.h | 85 ++- include/asm-powerpc/smu.h | 12 +- include/linux/pmu.h | 8 +- 11 files changed, 859 insertions(+), 667 deletions(-) (limited to 'drivers') diff --git a/arch/powerpc/platforms/powermac/feature.c b/arch/powerpc/platforms/powermac/feature.c index d2915d64d45e36..b271b11583acf4 100644 --- a/arch/powerpc/platforms/powermac/feature.c +++ b/arch/powerpc/platforms/powermac/feature.c @@ -1677,124 +1677,6 @@ intrepid_shutdown(struct macio_chip *macio, int sleep_mode) } -void pmac_tweak_clock_spreading(int enable) -{ - struct macio_chip *macio = &macio_chips[0]; - - /* Hack for doing clock spreading on some machines PowerBooks and - * iBooks. This implements the "platform-do-clockspreading" OF - * property as decoded manually on various models. For safety, we also - * check the product ID in the device-tree in cases we'll whack the i2c - * chip to make reasonably sure we won't set wrong values in there - * - * Of course, ultimately, we have to implement a real parser for - * the platform-do-* stuff... - */ - - if (macio->type == macio_intrepid) { - struct device_node *clock = - of_find_node_by_path("/uni-n@f8000000/hw-clock"); - if (clock && get_property(clock, "platform-do-clockspreading", - NULL)) { - printk(KERN_INFO "%sabling clock spreading on Intrepid" - " ASIC\n", enable ? "En" : "Dis"); - if (enable) - UN_OUT(UNI_N_CLOCK_SPREADING, 2); - else - UN_OUT(UNI_N_CLOCK_SPREADING, 0); - mdelay(40); - } - of_node_put(clock); - } - - while (machine_is_compatible("PowerBook5,2") || - machine_is_compatible("PowerBook5,3") || - machine_is_compatible("PowerBook6,2") || - machine_is_compatible("PowerBook6,3")) { - struct device_node *ui2c = of_find_node_by_type(NULL, "i2c"); - struct device_node *dt = of_find_node_by_name(NULL, "device-tree"); - u8 buffer[9]; - u32 *productID; - int i, rc, changed = 0; - - if (dt == NULL) - break; - productID = (u32 *)get_property(dt, "pid#", NULL); - if (productID == NULL) - break; - while(ui2c) { - struct device_node *p = of_get_parent(ui2c); - if (p && !strcmp(p->name, "uni-n")) - break; - ui2c = of_find_node_by_type(ui2c, "i2c"); - } - if (ui2c == NULL) - break; - DBG("Trying to bump clock speed for PID: %08x...\n", *productID); - rc = pmac_low_i2c_open(ui2c, 1); - if (rc != 0) - break; - pmac_low_i2c_setmode(ui2c, pmac_low_i2c_mode_combined); - rc = pmac_low_i2c_xfer(ui2c, 0xd2 | pmac_low_i2c_read, 0x80, buffer, 9); - DBG("read result: %d,", rc); - if (rc != 0) { - pmac_low_i2c_close(ui2c); - break; - } - for (i=0; i<9; i++) - DBG(" %02x", buffer[i]); - DBG("\n"); - - switch(*productID) { - case 0x1182: /* AlBook 12" rev 2 */ - case 0x1183: /* iBook G4 12" */ - buffer[0] = (buffer[0] & 0x8f) | 0x70; - buffer[2] = (buffer[2] & 0x7f) | 0x00; - buffer[5] = (buffer[5] & 0x80) | 0x31; - buffer[6] = (buffer[6] & 0x40) | 0xb0; - buffer[7] = (buffer[7] & 0x00) | (enable ? 0xc0 : 0xba); - buffer[8] = (buffer[8] & 0x00) | 0x30; - changed = 1; - break; - case 0x3142: /* AlBook 15" (ATI M10) */ - case 0x3143: /* AlBook 17" (ATI M10) */ - buffer[0] = (buffer[0] & 0xaf) | 0x50; - buffer[2] = (buffer[2] & 0x7f) | 0x00; - buffer[5] = (buffer[5] & 0x80) | 0x31; - buffer[6] = (buffer[6] & 0x40) | 0xb0; - buffer[7] = (buffer[7] & 0x00) | (enable ? 0xd0 : 0xc0); - buffer[8] = (buffer[8] & 0x00) | 0x30; - changed = 1; - break; - default: - DBG("i2c-hwclock: Machine model not handled\n"); - break; - } - if (!changed) { - pmac_low_i2c_close(ui2c); - break; - } - printk(KERN_INFO "%sabling clock spreading on i2c clock chip\n", - enable ? "En" : "Dis"); - - pmac_low_i2c_setmode(ui2c, pmac_low_i2c_mode_stdsub); - rc = pmac_low_i2c_xfer(ui2c, 0xd2 | pmac_low_i2c_write, 0x80, buffer, 9); - DBG("write result: %d,", rc); - pmac_low_i2c_setmode(ui2c, pmac_low_i2c_mode_combined); - rc = pmac_low_i2c_xfer(ui2c, 0xd2 | pmac_low_i2c_read, 0x80, buffer, 9); - DBG("read result: %d,", rc); - if (rc != 0) { - pmac_low_i2c_close(ui2c); - break; - } - for (i=0; i<9; i++) - DBG(" %02x", buffer[i]); - pmac_low_i2c_close(ui2c); - break; - } -} - - static int core99_sleep(void) { @@ -2980,12 +2862,6 @@ set_initial_features(void) MACIO_BIC(HEATHROW_FCR, HRW_SOUND_POWER_N); } - /* Some machine models need the clock chip to be properly setup for - * clock spreading now. This should be a platform function but we - * don't do these at the moment - */ - pmac_tweak_clock_spreading(1); - #endif /* CONFIG_POWER4 */ /* On all machines, switch modem & serial ports off */ @@ -3013,9 +2889,6 @@ pmac_feature_init(void) return; } - /* Setup low-level i2c stuffs */ - pmac_init_low_i2c(); - /* Probe machine type */ if (probe_motherboard()) printk(KERN_WARNING "Unknown PowerMac !\n"); diff --git a/arch/powerpc/platforms/powermac/low_i2c.c b/arch/powerpc/platforms/powermac/low_i2c.c index 606e0ed13731a5..f31d6a678b9ef1 100644 --- a/arch/powerpc/platforms/powermac/low_i2c.c +++ b/arch/powerpc/platforms/powermac/low_i2c.c @@ -1,22 +1,34 @@ /* - * arch/ppc/platforms/pmac_low_i2c.c + * arch/powerpc/platforms/powermac/low_i2c.c * - * Copyright (C) 2003 Ben. Herrenschmidt (benh@kernel.crashing.org) + * Copyright (C) 2003-2005 Ben. Herrenschmidt (benh@kernel.crashing.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * This file contains some low-level i2c access routines that - * need to be used by various bits of the PowerMac platform code - * at times where the real asynchronous & interrupt driven driver - * cannot be used. The API borrows some semantics from the darwin - * driver in order to ease the implementation of the platform - * properties parser + * The linux i2c layer isn't completely suitable for our needs for various + * reasons ranging from too late initialisation to semantics not perfectly + * matching some requirements of the apple platform functions etc... + * + * This file thus provides a simple low level unified i2c interface for + * powermac that covers the various types of i2c busses used in Apple machines. + * For now, keywest, PMU and SMU, though we could add Cuda, or other bit + * banging busses found on older chipstes in earlier machines if we ever need + * one of them. + * + * The drivers in this file are synchronous/blocking. In addition, the + * keywest one is fairly slow due to the use of msleep instead of interrupts + * as the interrupt is currently used by i2c-keywest. In the long run, we + * might want to get rid of those high-level interfaces to linux i2c layer + * either completely (converting all drivers) or replacing them all with a + * single stub driver on top of this one. Once done, the interrupt will be + * available for our use. */ #undef DEBUG +#undef DEBUG_LOW #include #include @@ -25,15 +37,16 @@ #include #include #include +#include +#include #include #include #include #include #include +#include #include -#define MAX_LOW_I2C_HOST 4 - #ifdef DEBUG #define DBG(x...) do {\ printk(KERN_DEBUG "low_i2c:" x); \ @@ -42,49 +55,54 @@ #define DBG(x...) #endif -struct low_i2c_host; - -typedef int (*low_i2c_func_t)(struct low_i2c_host *host, u8 addr, u8 sub, u8 *data, int len); - -struct low_i2c_host -{ - struct device_node *np; /* OF device node */ - struct semaphore mutex; /* Access mutex for use by i2c-keywest */ - low_i2c_func_t func; /* Access function */ - unsigned int is_open : 1; /* Poor man's access control */ - int mode; /* Current mode */ - int channel; /* Current channel */ - int num_channels; /* Number of channels */ - void __iomem *base; /* For keywest-i2c, base address */ - int bsteps; /* And register stepping */ - int speed; /* And speed */ -}; - -static struct low_i2c_host low_i2c_hosts[MAX_LOW_I2C_HOST]; +#ifdef DEBUG_LOW +#define DBG_LOW(x...) do {\ + printk(KERN_DEBUG "low_i2c:" x); \ + } while(0) +#else +#define DBG_LOW(x...) +#endif -/* No locking is necessary on allocation, we are running way before - * anything can race with us +/* + * A bus structure. Each bus in the system has such a structure associated. */ -static struct low_i2c_host *find_low_i2c_host(struct device_node *np) +struct pmac_i2c_bus { - int i; + struct list_head link; + struct device_node *controller; + struct device_node *busnode; + int type; + int flags; + struct i2c_adapter *adapter; + void *hostdata; + int channel; /* some hosts have multiple */ + int mode; /* current mode */ + struct semaphore sem; + int opened; + int polled; /* open mode */ + + /* ops */ + int (*open)(struct pmac_i2c_bus *bus); + void (*close)(struct pmac_i2c_bus *bus); + int (*xfer)(struct pmac_i2c_bus *bus, u8 addrdir, int subsize, + u32 subaddr, u8 *data, int len); +}; - for (i = 0; i < MAX_LOW_I2C_HOST; i++) - if (low_i2c_hosts[i].np == np) - return &low_i2c_hosts[i]; - return NULL; -} +static LIST_HEAD(pmac_i2c_busses); /* - * - * i2c-keywest implementation (UniNorth, U2, U3, Keylargo's) - * + * Keywest implementation */ -/* - * Keywest i2c definitions borrowed from drivers/i2c/i2c-keywest.h, - * should be moved somewhere in include/asm-ppc/ - */ +struct pmac_i2c_host_kw +{ + struct semaphore mutex; /* Access mutex for use by + * i2c-keywest */ + void __iomem *base; /* register base address */ + int bsteps; /* register stepping */ + int speed; /* speed */ +}; + /* Register indices */ typedef enum { reg_mode = 0, @@ -153,52 +171,56 @@ static const char *__kw_state_names[] = { "state_dead" }; -static inline u8 __kw_read_reg(struct low_i2c_host *host, reg_t reg) +static inline u8 __kw_read_reg(struct pmac_i2c_bus *bus, reg_t reg) { + struct pmac_i2c_host_kw *host = bus->hostdata; return readb(host->base + (((unsigned int)reg) << host->bsteps)); } -static inline void __kw_write_reg(struct low_i2c_host *host, reg_t reg, u8 val) +static inline void __kw_write_reg(struct pmac_i2c_bus *bus, reg_t reg, u8 val) { + struct pmac_i2c_host_kw *host = bus->hostdata; writeb(val, host->base + (((unsigned)reg) << host->bsteps)); - (void)__kw_read_reg(host, reg_subaddr); + (void)__kw_read_reg(bus, reg_subaddr); } -#define kw_write_reg(reg, val) __kw_write_reg(host, reg, val) -#define kw_read_reg(reg) __kw_read_reg(host, reg) +#define kw_write_reg(reg, val) __kw_write_reg(bus, reg, val) +#define kw_read_reg(reg) __kw_read_reg(bus, reg) - -/* Don't schedule, the g5 fan controller is too - * timing sensitive - */ -static u8 kw_wait_interrupt(struct low_i2c_host* host) +static u8 kw_i2c_wait_interrupt(struct pmac_i2c_bus* bus) { int i, j; u8 isr; - for (i = 0; i < 100000; i++) { + for (i = 0; i < 1000; i++) { isr = kw_read_reg(reg_isr) & KW_I2C_IRQ_MASK; if (isr != 0) return isr; /* This code is used with the timebase frozen, we cannot rely - * on udelay ! For now, just use a bogus loop + * on udelay nor schedule when in polled mode ! + * For now, just use a bogus loop.... */ - for (j = 1; j < 10000; j++) - mb(); + if (bus->polled) { + for (j = 1; j < 1000000; j++) + mb(); + } else + msleep(1); } return isr; } -static int kw_handle_interrupt(struct low_i2c_host *host, int state, int rw, int *rc, u8 **data, int *len, u8 isr) +static int kw_i2c_handle_interrupt(struct pmac_i2c_bus *bus, int state, int rw, + int *rc, u8 **data, int *len, u8 isr) { u8 ack; - DBG("kw_handle_interrupt(%s, isr: %x)\n", __kw_state_names[state], isr); + DBG_LOW("kw_handle_interrupt(%s, isr: %x)\n", + __kw_state_names[state], isr); if (isr == 0) { if (state != state_stop) { - DBG("KW: Timeout !\n"); + DBG_LOW("KW: Timeout !\n"); *rc = -EIO; goto stop; } @@ -220,15 +242,16 @@ static int kw_handle_interrupt(struct low_i2c_host *host, int state, int rw, int *rc = -EIO; goto stop; } - if ((ack & KW_I2C_STAT_LAST_AAK) == 0) { + if ((ack & KW_I2C_STAT_LAST_AAK) == 0) { *rc = -ENODEV; - DBG("KW: NAK on address\n"); + DBG_LOW("KW: NAK on address\n"); return state_stop; } else { if (rw) { state = state_read; if (*len > 1) - kw_write_reg(reg_control, KW_I2C_CTL_AAK); + kw_write_reg(reg_control, + KW_I2C_CTL_AAK); } else { state = state_write; kw_write_reg(reg_data, **data); @@ -250,7 +273,7 @@ static int kw_handle_interrupt(struct low_i2c_host *host, int state, int rw, int } else if (state == state_write) { ack = kw_read_reg(reg_status); if ((ack & KW_I2C_STAT_LAST_AAK) == 0) { - DBG("KW: nack on data write\n"); + DBG_LOW("KW: nack on data write\n"); *rc = -EIO; goto stop; } else if (*len) { @@ -291,35 +314,57 @@ static int kw_handle_interrupt(struct low_i2c_host *host, int state, int rw, int return state_stop; } -static int keywest_low_i2c_func(struct low_i2c_host *host, u8 addr, u8 subaddr, u8 *data, int len) +static int kw_i2c_open(struct pmac_i2c_bus *bus) { + struct pmac_i2c_host_kw *host = bus->hostdata; + down(&host->mutex); + return 0; +} + +static void kw_i2c_close(struct pmac_i2c_bus *bus) +{ + struct pmac_i2c_host_kw *host = bus->hostdata; + up(&host->mutex); +} + +static int kw_i2c_xfer(struct pmac_i2c_bus *bus, u8 addrdir, int subsize, + u32 subaddr, u8 *data, int len) +{ + struct pmac_i2c_host_kw *host = bus->hostdata; u8 mode_reg = host->speed; int state = state_addr; int rc = 0; /* Setup mode & subaddress if any */ - switch(host->mode) { - case pmac_low_i2c_mode_dumb: - printk(KERN_ERR "low_i2c: Dumb mode not supported !\n"); + switch(bus->mode) { + case pmac_i2c_mode_dumb: return -EINVAL; - case pmac_low_i2c_mode_std: + case pmac_i2c_mode_std: mode_reg |= KW_I2C_MODE_STANDARD; + if (subsize != 0) + return -EINVAL; break; - case pmac_low_i2c_mode_stdsub: + case pmac_i2c_mode_stdsub: mode_reg |= KW_I2C_MODE_STANDARDSUB; + if (subsize != 1) + return -EINVAL; break; - case pmac_low_i2c_mode_combined: + case pmac_i2c_mode_combined: mode_reg |= KW_I2C_MODE_COMBINED; + if (subsize != 1) + return -EINVAL; break; } /* Setup channel & clear pending irqs */ kw_write_reg(reg_isr, kw_read_reg(reg_isr)); - kw_write_reg(reg_mode, mode_reg | (host->channel << 4)); + kw_write_reg(reg_mode, mode_reg | (bus->channel << 4)); kw_write_reg(reg_status, 0); - /* Set up address and r/w bit */ - kw_write_reg(reg_addr, addr); + /* Set up address and r/w bit, strip possible stale bus number from + * address top bits + */ + kw_write_reg(reg_addr, addrdir & 0xff); /* Set up the sub address */ if ((mode_reg & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_STANDARDSUB @@ -330,27 +375,27 @@ static int keywest_low_i2c_func(struct low_i2c_host *host, u8 addr, u8 subaddr, kw_write_reg(reg_ier, 0 /*KW_I2C_IRQ_MASK*/); kw_write_reg(reg_control, KW_I2C_CTL_XADDR); - /* State machine, to turn into an interrupt handler */ + /* State machine, to turn into an interrupt handler in the future */ while(state != state_idle) { - u8 isr = kw_wait_interrupt(host); - state = kw_handle_interrupt(host, state, addr & 1, &rc, &data, &len, isr); + u8 isr = kw_i2c_wait_interrupt(bus); + state = kw_i2c_handle_interrupt(bus, state, addrdir & 1, &rc, + &data, &len, isr); } return rc; } -static void keywest_low_i2c_add(struct device_node *np) +static struct pmac_i2c_host_kw *__init kw_i2c_host_init(struct device_node *np) { - struct low_i2c_host *host = find_low_i2c_host(NULL); + struct pmac_i2c_host_kw *host; u32 *psteps, *prate, *addrp, steps; - struct device_node *parent; + host = kzalloc(sizeof(struct pmac_i2c_host_kw), GFP_KERNEL); if (host == NULL) { printk(KERN_ERR "low_i2c: Can't allocate host for %s\n", np->full_name); - return; + return NULL; } - memset(host, 0, sizeof(*host)); /* Apple is kind enough to provide a valid AAPL,address property * on all i2c keywest nodes so far ... we would have to fallback @@ -360,18 +405,14 @@ static void keywest_low_i2c_add(struct device_node *np) if (addrp == NULL) { printk(KERN_ERR "low_i2c: Can't find address for %s\n", np->full_name); - return; + kfree(host); + return NULL; } init_MUTEX(&host->mutex); - host->np = of_node_get(np); psteps = (u32 *)get_property(np, "AAPL,address-step", NULL); steps = psteps ? (*psteps) : 0x10; for (host->bsteps = 0; (steps & 0x01) == 0; host->bsteps++) steps >>= 1; - parent = of_get_parent(np); - host->num_channels = 1; - if (parent && parent->name[0] == 'u') - host->num_channels = 2; /* Select interface rate */ host->speed = KW_I2C_MODE_25KHZ; prate = (u32 *)get_property(np, "AAPL,i2c-rate", NULL); @@ -387,148 +428,620 @@ static void keywest_low_i2c_add(struct device_node *np) break; } - printk(KERN_INFO "low_i2c: Bus %s found at 0x%08x, %d channels," - " speed = %d KHz\n", - np->full_name, *addrp, host->num_channels, prate ? *prate : 25); - - host->mode = pmac_low_i2c_mode_std; + printk(KERN_INFO "KeyWest i2c @0x%08x %s\n", *addrp, np->full_name); host->base = ioremap((*addrp), 0x1000); - host->func = keywest_low_i2c_func; + + return host; } + +static void __init kw_i2c_add(struct pmac_i2c_host_kw *host, + struct device_node *controller, + struct device_node *busnode, + int channel) +{ + struct pmac_i2c_bus *bus; + + bus = kzalloc(sizeof(struct pmac_i2c_bus), GFP_KERNEL); + if (bus == NULL) + return; + + bus->controller = of_node_get(controller); + bus->busnode = of_node_get(busnode); + bus->type = pmac_i2c_bus_keywest; + bus->hostdata = host; + bus->channel = channel; + bus->mode = pmac_i2c_mode_std; + bus->open = kw_i2c_open; + bus->close = kw_i2c_close; + bus->xfer = kw_i2c_xfer; + init_MUTEX(&bus->sem); + if (controller == busnode) + bus->flags = pmac_i2c_multibus; + list_add(&bus->link, &pmac_i2c_busses); + + printk(KERN_INFO " channel %d bus %s\n", channel, + (controller == busnode) ? "" : busnode->full_name); +} + +static void __init kw_i2c_probe(void) +{ + struct device_node *np, *child, *parent; + + /* Probe keywest-i2c busses */ + for (np = NULL; + (np = of_find_compatible_node(np, "i2c","keywest-i2c")) != NULL;){ + struct pmac_i2c_host_kw *host; + int multibus, chans, i; + + /* Found one, init a host structure */ + host = kw_i2c_host_init(np); + if (host == NULL) + continue; + + /* Now check if we have a multibus setup (old style) or if we + * have proper bus nodes. Note that the "new" way (proper bus + * nodes) might cause us to not create some busses that are + * kept hidden in the device-tree. In the future, we might + * want to work around that by creating busses without a node + * but not for now + */ + child = of_get_next_child(np, NULL); + multibus = !child || strcmp(child->name, "i2c-bus"); + of_node_put(child); + + /* For a multibus setup, we get the bus count based on the + * parent type + */ + if (multibus) { + parent = of_get_parent(np); + if (parent == NULL) + continue; + chans = parent->name[0] == 'u' ? 2 : 1; + for (i = 0; i < chans; i++) + kw_i2c_add(host, np, np, i); + } else { + for (child = NULL; + (child = of_get_next_child(np, child)) != NULL;) { + u32 *reg = + (u32 *)get_property(child, "reg", NULL); + if (reg == NULL) + continue; + kw_i2c_add(host, np, child, *reg); + } + } + } +} + + /* * * PMU implementation * */ - #ifdef CONFIG_ADB_PMU -static int pmu_low_i2c_func(struct low_i2c_host *host, u8 addr, u8 sub, u8 *data, int len) +/* + * i2c command block to the PMU + */ +struct pmu_i2c_hdr { + u8 bus; + u8 mode; + u8 bus2; + u8 address; + u8 sub_addr; + u8 comb_addr; + u8 count; + u8 data[]; +}; + +static void pmu_i2c_complete(struct adb_request *req) { - // TODO - return -ENODEV; + complete(req->arg); } -static void pmu_low_i2c_add(struct device_node *np) +static int pmu_i2c_xfer(struct pmac_i2c_bus *bus, u8 addrdir, int subsize, + u32 subaddr, u8 *data, int len) { - struct low_i2c_host *host = find_low_i2c_host(NULL); + struct adb_request *req = bus->hostdata; + struct pmu_i2c_hdr *hdr = (struct pmu_i2c_hdr *)&req->data[1]; + struct completion comp; + int read = addrdir & 1; + int retry; + int rc = 0; - if (host == NULL) { - printk(KERN_ERR "low_i2c: Can't allocate host for %s\n", - np->full_name); - return; + /* For now, limit ourselves to 16 bytes transfers */ + if (len > 16) + return -EINVAL; + + init_completion(&comp); + + for (retry = 0; retry < 16; retry++) { + memset(req, 0, sizeof(struct adb_request)); + hdr->bus = bus->channel; + hdr->count = len; + + switch(bus->mode) { + case pmac_i2c_mode_std: + if (subsize != 0) + return -EINVAL; + hdr->address = addrdir; + hdr->mode = PMU_I2C_MODE_SIMPLE; + break; + case pmac_i2c_mode_stdsub: + case pmac_i2c_mode_combined: + if (subsize != 1) + return -EINVAL; + hdr->address = addrdir & 0xfe; + hdr->comb_addr = addrdir; + hdr->sub_addr = subaddr; + if (bus->mode == pmac_i2c_mode_stdsub) + hdr->mode = PMU_I2C_MODE_STDSUB; + else + hdr->mode = PMU_I2C_MODE_COMBINED; + break; + default: + return -EINVAL; + } + + INIT_COMPLETION(comp); + req->data[0] = PMU_I2C_CMD; + req->reply[0] = 0xff; + req->nbytes = sizeof(struct pmu_i2c_hdr) + 1; + req->done = pmu_i2c_complete; + req->arg = ∁ + if (!read) { + memcpy(hdr->data, data, len); + req->nbytes += len; + } + rc = pmu_queue_request(req); + if (rc) + return rc; + wait_for_completion(&comp); + if (req->reply[0] == PMU_I2C_STATUS_OK) + break; + msleep(15); } - memset(host, 0, sizeof(*host)); + if (req->reply[0] != PMU_I2C_STATUS_OK) + return -EIO; - init_MUTEX(&host->mutex); - host->np = of_node_get(np); - host->num_channels = 3; - host->mode = pmac_low_i2c_mode_std; - host->func = pmu_low_i2c_func; + for (retry = 0; retry < 16; retry++) { + memset(req, 0, sizeof(struct adb_request)); + + /* I know that looks like a lot, slow as hell, but darwin + * does it so let's be on the safe side for now + */ + msleep(15); + + hdr->bus = PMU_I2C_BUS_STATUS; + + INIT_COMPLETION(comp); + req->data[0] = PMU_I2C_CMD; + req->reply[0] = 0xff; + req->nbytes = 2; + req->done = pmu_i2c_complete; + req->arg = ∁ + rc = pmu_queue_request(req); + if (rc) + return rc; + wait_for_completion(&comp); + + if (req->reply[0] == PMU_I2C_STATUS_OK && !read) + return 0; + if (req->reply[0] == PMU_I2C_STATUS_DATAREAD && read) { + int rlen = req->reply_len - 1; + + if (rlen != len) { + printk(KERN_WARNING "low_i2c: PMU returned %d" + " bytes, expected %d !\n", rlen, len); + return -EIO; + } + memcpy(data, &req->reply[1], len); + return 0; + } + } + return -EIO; +} + +static void __init pmu_i2c_probe(void) +{ + struct pmac_i2c_bus *bus; + struct device_node *busnode; + int channel, sz; + + if (!pmu_present()) + return; + + /* There might or might not be a "pmu-i2c" node, we use that + * or via-pmu itself, whatever we find. I haven't seen a machine + * with separate bus nodes, so we assume a multibus setup + */ + busnode = of_find_node_by_name(NULL, "pmu-i2c"); + if (busnode == NULL) + busnode = of_find_node_by_name(NULL, "via-pmu"); + if (busnode == NULL) + return; + + printk(KERN_INFO "PMU i2c %s\n", busnode->full_name); + + /* + * We add bus 1 and 2 only for now, bus 0 is "special" + */ + for (channel = 1; channel <= 2; channel++) { + sz = sizeof(struct pmac_i2c_bus) + sizeof(struct adb_request); + bus = kzalloc(sz, GFP_KERNEL); + if (bus == NULL) + return; + + bus->controller = busnode; + bus->busnode = busnode; + bus->type = pmac_i2c_bus_pmu; + bus->channel = channel; + bus->mode = pmac_i2c_mode_std; + bus->hostdata = bus + 1; + bus->xfer = pmu_i2c_xfer; + init_MUTEX(&bus->sem); + bus->flags = pmac_i2c_multibus; + list_add(&bus->link, &pmac_i2c_busses); + + printk(KERN_INFO " channel %d bus \n", channel); + } } #endif /* CONFIG_ADB_PMU */ -void __init pmac_init_low_i2c(void) + +/* + * + * SMU implementation + * + */ + +#ifdef CONFIG_PMAC_SMU + +static void smu_i2c_complete(struct smu_i2c_cmd *cmd, void *misc) { - struct device_node *np; + complete(misc); +} - /* Probe keywest-i2c busses */ - np = of_find_compatible_node(NULL, "i2c", "keywest-i2c"); - while(np) { - keywest_low_i2c_add(np); - np = of_find_compatible_node(np, "i2c", "keywest-i2c"); +static int smu_i2c_xfer(struct pmac_i2c_bus *bus, u8 addrdir, int subsize, + u32 subaddr, u8 *data, int len) +{ + struct smu_i2c_cmd *cmd = bus->hostdata; + struct completion comp; + int read = addrdir & 1; + int rc = 0; + + memset(cmd, 0, sizeof(struct smu_i2c_cmd)); + cmd->info.bus = bus->channel; + cmd->info.devaddr = addrdir; + cmd->info.datalen = len; + + switch(bus->mode) { + case pmac_i2c_mode_std: + if (subsize != 0) + return -EINVAL; + cmd->info.type = SMU_I2C_TRANSFER_SIMPLE; + break; + case pmac_i2c_mode_stdsub: + case pmac_i2c_mode_combined: + if (subsize > 3 || subsize < 1) + return -EINVAL; + cmd->info.sublen = subsize; + /* that's big-endian only but heh ! */ + memcpy(&cmd->info.subaddr, ((char *)&subaddr) + (4 - subsize), + subsize); + if (bus->mode == pmac_i2c_mode_stdsub) + cmd->info.type = SMU_I2C_TRANSFER_STDSUB; + else + cmd->info.type = SMU_I2C_TRANSFER_COMBINED; + break; + default: + return -EINVAL; } + if (!read) + memcpy(cmd->info.data, data, len); + + init_completion(&comp); + cmd->done = smu_i2c_complete; + cmd->misc = ∁ + rc = smu_queue_i2c(cmd); + if (rc < 0) + return rc; + wait_for_completion(&comp); + rc = cmd->status; + + if (read) + memcpy(data, cmd->info.data, len); + return rc < 0 ? rc : 0; +} -#ifdef CONFIG_ADB_PMU - /* Probe PMU busses */ - np = of_find_node_by_name(NULL, "via-pmu"); - if (np) - pmu_low_i2c_add(np); -#endif /* CONFIG_ADB_PMU */ +static void __init smu_i2c_probe(void) +{ + struct device_node *controller, *busnode; + struct pmac_i2c_bus *bus; + u32 *reg; + int sz; + + if (!smu_present()) + return; + + controller = of_find_node_by_name(NULL, "smu_i2c_control"); + if (controller == NULL) + controller = of_find_node_by_name(NULL, "smu"); + if (controller == NULL) + return; + + printk(KERN_INFO "SMU i2c %s\n", controller->full_name); + + /* Look for childs, note that they might not be of the right + * type as older device trees mix i2c busses and other thigns + * at the same level + */ + for (busnode = NULL; + (busnode = of_get_next_child(controller, busnode)) != NULL;) { + if (strcmp(busnode->type, "i2c") && + strcmp(busnode->type, "i2c-bus")) + continue; + reg = (u32 *)get_property(busnode, "reg", NULL); + if (reg == NULL) + continue; + + sz = sizeof(struct pmac_i2c_bus) + sizeof(struct smu_i2c_cmd); + bus = kzalloc(sz, GFP_KERNEL); + if (bus == NULL) + return; + + bus->controller = controller; + bus->busnode = of_node_get(busnode); + bus->type = pmac_i2c_bus_smu; + bus->channel = *reg; + bus->mode = pmac_i2c_mode_std; + bus->hostdata = bus + 1; + bus->xfer = smu_i2c_xfer; + init_MUTEX(&bus->sem); + bus->flags = 0; + list_add(&bus->link, &pmac_i2c_busses); + + printk(KERN_INFO " channel %x bus %s\n", + bus->channel, busnode->full_name); + } +} + +#endif /* CONFIG_PMAC_SMU */ + +/* + * + * Core code + * + */ + + +struct pmac_i2c_bus *pmac_i2c_find_bus(struct device_node *node) +{ + struct device_node *p = of_node_get(node); + struct device_node *prev = NULL; + struct pmac_i2c_bus *bus; + + while(p) { + list_for_each_entry(bus, &pmac_i2c_busses, link) { + if (p == bus->busnode) { + if (prev && bus->flags & pmac_i2c_multibus) { + u32 *reg; + reg = (u32 *)get_property(prev, "reg", + NULL); + if (!reg) + continue; + if (((*reg) >> 8) != bus->channel) + continue; + } + of_node_put(p); + of_node_put(prev); + return bus; + } + } + of_node_put(prev); + prev = p; + p = of_get_parent(p); + } + return NULL; +} +EXPORT_SYMBOL_GPL(pmac_i2c_find_bus); + +u8 pmac_i2c_get_dev_addr(struct device_node *device) +{ + u32 *reg = (u32 *)get_property(device, "reg", NULL); + + if (reg == NULL) + return 0; + + return (*reg) & 0xff; +} +EXPORT_SYMBOL_GPL(pmac_i2c_get_dev_addr); + +struct device_node *pmac_i2c_get_controller(struct pmac_i2c_bus *bus) +{ + return bus->controller; +} +EXPORT_SYMBOL_GPL(pmac_i2c_get_controller); + +struct device_node *pmac_i2c_get_bus_node(struct pmac_i2c_bus *bus) +{ + return bus->busnode; +} +EXPORT_SYMBOL_GPL(pmac_i2c_get_bus_node); + +int pmac_i2c_get_type(struct pmac_i2c_bus *bus) +{ + return bus->type; +} +EXPORT_SYMBOL_GPL(pmac_i2c_get_type); + +int pmac_i2c_get_flags(struct pmac_i2c_bus *bus) +{ + return bus->flags; +} +EXPORT_SYMBOL_GPL(pmac_i2c_get_flags); - /* TODO: Add CUDA support as well */ +void pmac_i2c_attach_adapter(struct pmac_i2c_bus *bus, + struct i2c_adapter *adapter) +{ + WARN_ON(bus->adapter != NULL); + bus->adapter = adapter; } +EXPORT_SYMBOL_GPL(pmac_i2c_attach_adapter); + +void pmac_i2c_detach_adapter(struct pmac_i2c_bus *bus, + struct i2c_adapter *adapter) +{ + WARN_ON(bus->adapter != adapter); + bus->adapter = NULL; +} +EXPORT_SYMBOL_GPL(pmac_i2c_detach_adapter); + +struct i2c_adapter *pmac_i2c_get_adapter(struct pmac_i2c_bus *bus) +{ + return bus->adapter; +} +EXPORT_SYMBOL_GPL(pmac_i2c_get_adapter); + +extern int pmac_i2c_match_adapter(struct device_node *dev, + struct i2c_adapter *adapter) +{ + struct pmac_i2c_bus *bus = pmac_i2c_find_bus(dev); + + if (bus == NULL) + return 0; + return (bus->adapter == adapter); +} +EXPORT_SYMBOL_GPL(pmac_i2c_match_adapter); int pmac_low_i2c_lock(struct device_node *np) { - struct low_i2c_host *host = find_low_i2c_host(np); + struct pmac_i2c_bus *bus, *found = NULL; - if (!host) + list_for_each_entry(bus, &pmac_i2c_busses, link) { + if (np == bus->controller) { + found = bus; + break; + } + } + if (!found) return -ENODEV; - down(&host->mutex); - return 0; + return pmac_i2c_open(bus, 0); } -EXPORT_SYMBOL(pmac_low_i2c_lock); +EXPORT_SYMBOL_GPL(pmac_low_i2c_lock); int pmac_low_i2c_unlock(struct device_node *np) { - struct low_i2c_host *host = find_low_i2c_host(np); + struct pmac_i2c_bus *bus, *found = NULL; - if (!host) + list_for_each_entry(bus, &pmac_i2c_busses, link) { + if (np == bus->controller) { + found = bus; + break; + } + } + if (!found) return -ENODEV; - up(&host->mutex); + pmac_i2c_close(bus); return 0; } -EXPORT_SYMBOL(pmac_low_i2c_unlock); +EXPORT_SYMBOL_GPL(pmac_low_i2c_unlock); -int pmac_low_i2c_open(struct device_node *np, int channel) +int pmac_i2c_open(struct pmac_i2c_bus *bus, int polled) { - struct low_i2c_host *host = find_low_i2c_host(np); + int rc; + + down(&bus->sem); + bus->polled = polled; + bus->opened = 1; + bus->mode = pmac_i2c_mode_std; + if (bus->open && (rc = bus->open(bus)) != 0) { + bus->opened = 0; + up(&bus->sem); + return rc; + } + return 0; +} +EXPORT_SYMBOL_GPL(pmac_i2c_open); - if (!host) - return -ENODEV; +void pmac_i2c_close(struct pmac_i2c_bus *bus) +{ + WARN_ON(!bus->opened); + if (bus->close) + bus->close(bus); + bus->opened = 0; + up(&bus->sem); +} +EXPORT_SYMBOL_GPL(pmac_i2c_close); - if (channel >= host->num_channels) - return -EINVAL; +int pmac_i2c_setmode(struct pmac_i2c_bus *bus, int mode) +{ + WARN_ON(!bus->opened); - down(&host->mutex); - host->is_open = 1; - host->channel = channel; + /* Report me if you see the error below as there might be a new + * "combined4" mode that I need to implement for the SMU bus + */ + if (mode < pmac_i2c_mode_dumb || mode > pmac_i2c_mode_combined) { + printk(KERN_ERR "low_i2c: Invalid mode %d requested on" + " bus %s !\n", mode, bus->busnode->full_name); + return -EINVAL; + } + bus->mode = mode; return 0; } -EXPORT_SYMBOL(pmac_low_i2c_open); +EXPORT_SYMBOL_GPL(pmac_i2c_setmode); -int pmac_low_i2c_close(struct device_node *np) +int pmac_i2c_xfer(struct pmac_i2c_bus *bus, u8 addrdir, int subsize, + u32 subaddr, u8 *data, int len) { - struct low_i2c_host *host = find_low_i2c_host(np); + int rc; - if (!host) - return -ENODEV; + WARN_ON(!bus->opened); - host->is_open = 0; - up(&host->mutex); + DBG("xfer() chan=%d, addrdir=0x%x, mode=%d, subsize=%d, subaddr=0x%x," + " %d bytes, bus %s\n", bus->channel, addrdir, bus->mode, subsize, + subaddr, len, bus->busnode->full_name); - return 0; + rc = bus->xfer(bus, addrdir, subsize, subaddr, data, len); + +#ifdef DEBUG + if (rc) + DBG("xfer error %d\n", rc); +#endif + return rc; } -EXPORT_SYMBOL(pmac_low_i2c_close); +EXPORT_SYMBOL_GPL(pmac_i2c_xfer); -int pmac_low_i2c_setmode(struct device_node *np, int mode) +/* + * Initialize us: probe all i2c busses on the machine and instantiate + * busses. + */ +/* This is non-static as it might be called early by smp code */ +int __init pmac_i2c_init(void) { - struct low_i2c_host *host = find_low_i2c_host(np); + static int i2c_inited; - if (!host) - return -ENODEV; - WARN_ON(!host->is_open); - host->mode = mode; + if (i2c_inited) + return 0; + i2c_inited = 1; - return 0; -} -EXPORT_SYMBOL(pmac_low_i2c_setmode); + /* Probe keywest-i2c busses */ + kw_i2c_probe(); -int pmac_low_i2c_xfer(struct device_node *np, u8 addrdir, u8 subaddr, u8 *data, int len) -{ - struct low_i2c_host *host = find_low_i2c_host(np); +#ifdef CONFIG_ADB_PMU + pmu_i2c_probe(); +#endif - if (!host) - return -ENODEV; - WARN_ON(!host->is_open); +#ifdef CONFIG_PMAC_SMU + smu_i2c_probe(); +#endif - return host->func(host, addrdir, subaddr, data, len); + return 0; } -EXPORT_SYMBOL(pmac_low_i2c_xfer); +arch_initcall(pmac_i2c_init); diff --git a/arch/powerpc/platforms/powermac/setup.c b/arch/powerpc/platforms/powermac/setup.c index e5a5bdbdda7a8f..dc5cdc1484e89d 100644 --- a/arch/powerpc/platforms/powermac/setup.c +++ b/arch/powerpc/platforms/powermac/setup.c @@ -652,27 +652,22 @@ static int __init pmac_declare_of_platform_devices(void) { struct device_node *np, *npp; - np = find_devices("uni-n"); - if (np) { - for (np = np->child; np != NULL; np = np->sibling) - if (strncmp(np->name, "i2c", 3) == 0) { - of_platform_device_create(np, "uni-n-i2c", - NULL); - break; - } - } - np = find_devices("valkyrie"); + np = of_find_node_by_name(NULL, "valkyrie"); if (np) of_platform_device_create(np, "valkyrie", NULL); - np = find_devices("platinum"); + np = of_find_node_by_name(NULL, "platinum"); if (np) of_platform_device_create(np, "platinum", NULL); - - npp = of_find_node_by_name(NULL, "u3"); + npp = of_find_node_by_name(NULL, "uni-n"); + if (npp == NULL) + npp = of_find_node_by_name(NULL, "u3"); + if (npp == NULL) + npp = of_find_node_by_name(NULL, "u4"); if (npp) { for (np = NULL; (np = of_get_next_child(npp, np)) != NULL;) { if (strncmp(np->name, "i2c", 3) == 0) { - of_platform_device_create(np, "u3-i2c", NULL); + of_platform_device_create(np, "uni-n-i2c", + NULL); of_node_put(np); break; } diff --git a/arch/powerpc/platforms/powermac/smp.c b/arch/powerpc/platforms/powermac/smp.c index df01bb8feb16d8..ab72ba86be1e4c 100644 --- a/arch/powerpc/platforms/powermac/smp.c +++ b/arch/powerpc/platforms/powermac/smp.c @@ -482,7 +482,7 @@ static void __devinit smp_core99_take_timebase(void) /* * G5s enable/disable the timebase via an i2c-connected clock chip. */ -static struct device_node *pmac_tb_clock_chip_host; +static struct pmac_i2c_bus *pmac_tb_clock_chip_host; static u8 pmac_tb_pulsar_addr; static void smp_core99_cypress_tb_freeze(int freeze) @@ -493,20 +493,20 @@ static void smp_core99_cypress_tb_freeze(int freeze) /* Strangely, the device-tree says address is 0xd2, but darwin * accesses 0xd0 ... */ - pmac_low_i2c_setmode(pmac_tb_clock_chip_host, - pmac_low_i2c_mode_combined); - rc = pmac_low_i2c_xfer(pmac_tb_clock_chip_host, - 0xd0 | pmac_low_i2c_read, - 0x81, &data, 1); + pmac_i2c_setmode(pmac_tb_clock_chip_host, + pmac_i2c_mode_combined); + rc = pmac_i2c_xfer(pmac_tb_clock_chip_host, + 0xd0 | pmac_i2c_read, + 1, 0x81, &data, 1); if (rc != 0) goto bail; data = (data & 0xf3) | (freeze ? 0x00 : 0x0c); - pmac_low_i2c_setmode(pmac_tb_clock_chip_host, pmac_low_i2c_mode_stdsub); - rc = pmac_low_i2c_xfer(pmac_tb_clock_chip_host, - 0xd0 | pmac_low_i2c_write, - 0x81, &data, 1); + pmac_i2c_setmode(pmac_tb_clock_chip_host, pmac_i2c_mode_stdsub); + rc = pmac_i2c_xfer(pmac_tb_clock_chip_host, + 0xd0 | pmac_i2c_write, + 1, 0x81, &data, 1); bail: if (rc != 0) { @@ -522,20 +522,20 @@ static void smp_core99_pulsar_tb_freeze(int freeze) u8 data; int rc; - pmac_low_i2c_setmode(pmac_tb_clock_chip_host, - pmac_low_i2c_mode_combined); - rc = pmac_low_i2c_xfer(pmac_tb_clock_chip_host, - pmac_tb_pulsar_addr | pmac_low_i2c_read, - 0x2e, &data, 1); + pmac_i2c_setmode(pmac_tb_clock_chip_host, + pmac_i2c_mode_combined); + rc = pmac_i2c_xfer(pmac_tb_clock_chip_host, + pmac_tb_pulsar_addr | pmac_i2c_read, + 1, 0x2e, &data, 1); if (rc != 0) goto bail; data = (data & 0x88) | (freeze ? 0x11 : 0x22); - pmac_low_i2c_setmode(pmac_tb_clock_chip_host, pmac_low_i2c_mode_stdsub); - rc = pmac_low_i2c_xfer(pmac_tb_clock_chip_host, - pmac_tb_pulsar_addr | pmac_low_i2c_write, - 0x2e, &data, 1); + pmac_i2c_setmode(pmac_tb_clock_chip_host, pmac_i2c_mode_stdsub); + rc = pmac_i2c_xfer(pmac_tb_clock_chip_host, + pmac_tb_pulsar_addr | pmac_i2c_write, + 1, 0x2e, &data, 1); bail: if (rc != 0) { printk(KERN_ERR "Pulsar Timebase %s rc: %d\n", @@ -560,13 +560,15 @@ static void __init smp_core99_setup_i2c_hwsync(int ncpus) if (!ok) continue; + pmac_tb_clock_chip_host = pmac_i2c_find_bus(cc); + if (pmac_tb_clock_chip_host == NULL) + continue; reg = (u32 *)get_property(cc, "reg", NULL); if (reg == NULL) continue; - switch (*reg) { case 0xd2: - if (device_is_compatible(cc, "pulsar-legacy-slewing")) { + if (device_is_compatible(cc,"pulsar-legacy-slewing")) { pmac_tb_freeze = smp_core99_pulsar_tb_freeze; pmac_tb_pulsar_addr = 0xd2; name = "Pulsar"; @@ -585,30 +587,19 @@ static void __init smp_core99_setup_i2c_hwsync(int ncpus) break; } if (pmac_tb_freeze != NULL) { - struct device_node *p = of_get_parent(cc); - of_node_put(cc); - while(p && strcmp(p->type, "i2c")) { - cc = of_get_parent(p); - of_node_put(p); - p = cc; - } - if (p == NULL) - goto no_i2c_sync; /* Open i2c bus for synchronous access */ - if (pmac_low_i2c_open(p, 0)) { - printk(KERN_ERR "Failed top open i2c bus %s for clock" - " sync, fallback to software sync !\n", - p->full_name); - of_node_put(p); + if (pmac_i2c_open(pmac_tb_clock_chip_host, 1)) { + printk(KERN_ERR "Failed top open i2c bus for clock" + " sync, fallback to software sync !\n"); goto no_i2c_sync; } - pmac_tb_clock_chip_host = p; printk(KERN_INFO "Processor timebase sync using %s i2c clock\n", name); return; } no_i2c_sync: pmac_tb_freeze = NULL; + pmac_tb_clock_chip_host = NULL; } #endif /* CONFIG_PPC64 */ @@ -752,8 +743,18 @@ static int __init smp_core99_probe(void) if (ncpus <= 1) return 1; + /* We need to perform some early initialisations before we can start + * setting up SMP as we are running before initcalls + */ + pmac_i2c_init(); + + /* Setup various bits like timebase sync method, ability to nap, ... */ smp_core99_setup(ncpus); + + /* Install IPIs */ mpic_request_ipis(); + + /* Collect l2cr and l3cr values from CPU 0 */ core99_init_caches(0); return ncpus; @@ -817,7 +818,7 @@ static void __devinit smp_core99_setup_cpu(int cpu_nr) /* Close i2c bus if it was used for tb sync */ if (pmac_tb_clock_chip_host) { - pmac_low_i2c_close(pmac_tb_clock_chip_host); + pmac_i2c_close(pmac_tb_clock_chip_host); pmac_tb_clock_chip_host = NULL; } diff --git a/drivers/i2c/busses/i2c-pmac-smu.c b/drivers/i2c/busses/i2c-pmac-smu.c index bfefe7f7a53d1f..7d925be3fd4b02 100644 --- a/drivers/i2c/busses/i2c-pmac-smu.c +++ b/drivers/i2c/busses/i2c-pmac-smu.c @@ -103,8 +103,8 @@ static s32 smu_smbus_xfer( struct i2c_adapter* adap, cmd.info.subaddr[1] = 0; cmd.info.subaddr[2] = 0; if (!read) { - cmd.info.data[0] = data->byte & 0xff; - cmd.info.data[1] = (data->byte >> 8) & 0xff; + cmd.info.data[0] = data->word & 0xff; + cmd.info.data[1] = (data->word >> 8) & 0xff; } break; /* Note that these are broken vs. the expected smbus API where @@ -116,7 +116,7 @@ static s32 smu_smbus_xfer( struct i2c_adapter* adap, case I2C_SMBUS_BLOCK_DATA: cmd.info.type = SMU_I2C_TRANSFER_STDSUB; cmd.info.datalen = data->block[0] + 1; - if (cmd.info.datalen > 6) + if (cmd.info.datalen > (SMU_I2C_WRITE_MAX + 1)) return -EINVAL; if (!read) memcpy(cmd.info.data, data->block, cmd.info.datalen); @@ -273,7 +273,13 @@ static int dispose_iface(struct device *dev) static int create_iface_of_platform(struct of_device* dev, const struct of_device_id *match) { - return create_iface(dev->node, &dev->dev); + struct device_node *node = dev->node; + + if (device_is_compatible(node, "smu-i2c") || + (node->parent != NULL && + device_is_compatible(node->parent, "smu-i2c-control"))) + return create_iface(node, &dev->dev); + return -ENODEV; } @@ -288,6 +294,9 @@ static struct of_device_id i2c_smu_match[] = { .compatible = "smu-i2c", }, + { + .compatible = "i2c-bus", + }, {}, }; static struct of_platform_driver i2c_smu_of_platform_driver = diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c index 96226116a64625..9ecd76849e35bf 100644 --- a/drivers/macintosh/smu.c +++ b/drivers/macintosh/smu.c @@ -94,6 +94,8 @@ struct smu_device { static struct smu_device *smu; static DECLARE_MUTEX(smu_part_access); +static void smu_i2c_retry(unsigned long data); + /* * SMU driver low level stuff */ @@ -469,7 +471,6 @@ int __init smu_init (void) smu->of_node = np; smu->db_irq = NO_IRQ; smu->msg_irq = NO_IRQ; - init_timer(&smu->i2c_timer); /* smu_cmdbuf_abs is in the low 2G of RAM, can be converted to a * 32 bits value safely @@ -544,6 +545,10 @@ static int smu_late_init(void) if (!smu) return 0; + init_timer(&smu->i2c_timer); + smu->i2c_timer.function = smu_i2c_retry; + smu->i2c_timer.data = (unsigned long)smu; + /* * Try to request the interrupts */ @@ -570,28 +575,41 @@ static int smu_late_init(void) return 0; } -arch_initcall(smu_late_init); +/* This has to be before arch_initcall as the low i2c stuff relies on the + * above having been done before we reach arch_initcalls + */ +core_initcall(smu_late_init); /* * sysfs visibility */ +static void smu_create_i2c(struct device_node *np) +{ + char name[32]; + u32 *reg = (u32 *)get_property(np, "reg", NULL); + + if (reg != NULL) { + sprintf(name, "smu-i2c-%02x", *reg); + of_platform_device_create(np, name, &smu->of_dev->dev); + } +} + static void smu_expose_childs(void *unused) { - struct device_node *np; + struct device_node *np, *gp; for (np = NULL; (np = of_get_next_child(smu->of_node, np)) != NULL;) { - if (device_is_compatible(np, "smu-i2c")) { - char name[32]; - u32 *reg = (u32 *)get_property(np, "reg", NULL); - - if (reg == NULL) - continue; - sprintf(name, "smu-i2c-%02x", *reg); - of_platform_device_create(np, name, &smu->of_dev->dev); - } + if (device_is_compatible(np, "smu-i2c-control")) { + gp = NULL; + while ((gp = of_get_next_child(np, gp)) != NULL) + if (device_is_compatible(gp, "i2c-bus")) + smu_create_i2c(gp); + } else if (device_is_compatible(np, "smu-i2c")) + smu_create_i2c(np); if (device_is_compatible(np, "smu-sensors")) - of_platform_device_create(np, "smu-sensors", &smu->of_dev->dev); + of_platform_device_create(np, "smu-sensors", + &smu->of_dev->dev); } } @@ -712,13 +730,13 @@ static void smu_i2c_complete_command(struct smu_i2c_cmd *cmd, int fail) static void smu_i2c_retry(unsigned long data) { - struct smu_i2c_cmd *cmd = (struct smu_i2c_cmd *)data; + struct smu_i2c_cmd *cmd = smu->cmd_i2c_cur; DPRINTK("SMU: i2c failure, requeuing...\n"); /* requeue command simply by resetting reply_len */ cmd->pdata[0] = 0xff; - cmd->scmd.reply_len = 0x10; + cmd->scmd.reply_len = sizeof(cmd->pdata); smu_queue_cmd(&cmd->scmd); } @@ -747,10 +765,8 @@ static void smu_i2c_low_completion(struct smu_cmd *scmd, void *misc) */ if (fail && --cmd->retries > 0) { DPRINTK("SMU: i2c failure, starting timer...\n"); - smu->i2c_timer.function = smu_i2c_retry; - smu->i2c_timer.data = (unsigned long)cmd; - smu->i2c_timer.expires = jiffies + msecs_to_jiffies(5); - add_timer(&smu->i2c_timer); + BUG_ON(cmd != smu->cmd_i2c_cur); + mod_timer(&smu->i2c_timer, jiffies + msecs_to_jiffies(5)); return; } @@ -764,7 +780,7 @@ static void smu_i2c_low_completion(struct smu_cmd *scmd, void *misc) /* Ok, initial command complete, now poll status */ scmd->reply_buf = cmd->pdata; - scmd->reply_len = 0x10; + scmd->reply_len = sizeof(cmd->pdata); scmd->data_buf = cmd->pdata; scmd->data_len = 1; cmd->pdata[0] = 0; @@ -786,7 +802,7 @@ int smu_queue_i2c(struct smu_i2c_cmd *cmd) cmd->scmd.done = smu_i2c_low_completion; cmd->scmd.misc = cmd; cmd->scmd.reply_buf = cmd->pdata; - cmd->scmd.reply_len = 0x10; + cmd->scmd.reply_len = sizeof(cmd->pdata); cmd->scmd.data_buf = (u8 *)(char *)&cmd->info; cmd->scmd.status = 1; cmd->stage = 0; diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index 3c0552016b9138..aa481a88ccabff 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c @@ -197,7 +197,6 @@ static int pmu_adb_reset_bus(void); #endif /* CONFIG_ADB */ static int init_pmu(void); -static int pmu_queue_request(struct adb_request *req); static void pmu_start(void); static irqreturn_t via_pmu_interrupt(int irq, void *arg, struct pt_regs *regs); static irqreturn_t gpio1_interrupt(int irq, void *arg, struct pt_regs *regs); @@ -1802,258 +1801,6 @@ pmu_present(void) return via != 0; } -struct pmu_i2c_hdr { - u8 bus; - u8 mode; - u8 bus2; - u8 address; - u8 sub_addr; - u8 comb_addr; - u8 count; -}; - -int -pmu_i2c_combined_read(int bus, int addr, int subaddr, u8* data, int len) -{ - struct adb_request req; - struct pmu_i2c_hdr *hdr = (struct pmu_i2c_hdr *)&req.data[1]; - int retry; - int rc; - - for (retry=0; retry<16; retry++) { - memset(&req, 0, sizeof(req)); - - hdr->bus = bus; - hdr->address = addr & 0xfe; - hdr->mode = PMU_I2C_MODE_COMBINED; - hdr->bus2 = 0; - hdr->sub_addr = subaddr; - hdr->comb_addr = addr | 1; - hdr->count = len; - - req.nbytes = sizeof(struct pmu_i2c_hdr) + 1; - req.reply_expected = 0; - req.reply_len = 0; - req.data[0] = PMU_I2C_CMD; - req.reply[0] = 0xff; - rc = pmu_queue_request(&req); - if (rc) - return rc; - while(!req.complete) - pmu_poll(); - if (req.reply[0] == PMU_I2C_STATUS_OK) - break; - mdelay(15); - } - if (req.reply[0] != PMU_I2C_STATUS_OK) - return -1; - - for (retry=0; retry<16; retry++) { - memset(&req, 0, sizeof(req)); - - mdelay(15); - - hdr->bus = PMU_I2C_BUS_STATUS; - req.reply[0] = 0xff; - - req.nbytes = 2; - req.reply_expected = 0; - req.reply_len = 0; - req.data[0] = PMU_I2C_CMD; - rc = pmu_queue_request(&req); - if (rc) - return rc; - while(!req.complete) - pmu_poll(); - if (req.reply[0] == PMU_I2C_STATUS_DATAREAD) { - memcpy(data, &req.reply[1], req.reply_len - 1); - return req.reply_len - 1; - } - } - return -1; -} - -int -pmu_i2c_stdsub_write(int bus, int addr, int subaddr, u8* data, int len) -{ - struct adb_request req; - struct pmu_i2c_hdr *hdr = (struct pmu_i2c_hdr *)&req.data[1]; - int retry; - int rc; - - for (retry=0; retry<16; retry++) { - memset(&req, 0, sizeof(req)); - - hdr->bus = bus; - hdr->address = addr & 0xfe; - hdr->mode = PMU_I2C_MODE_STDSUB; - hdr->bus2 = 0; - hdr->sub_addr = subaddr; - hdr->comb_addr = addr & 0xfe; - hdr->count = len; - - req.data[0] = PMU_I2C_CMD; - memcpy(&req.data[sizeof(struct pmu_i2c_hdr) + 1], data, len); - req.nbytes = sizeof(struct pmu_i2c_hdr) + len + 1; - req.reply_expected = 0; - req.reply_len = 0; - req.reply[0] = 0xff; - rc = pmu_queue_request(&req); - if (rc) - return rc; - while(!req.complete) - pmu_poll(); - if (req.reply[0] == PMU_I2C_STATUS_OK) - break; - mdelay(15); - } - if (req.reply[0] != PMU_I2C_STATUS_OK) - return -1; - - for (retry=0; retry<16; retry++) { - memset(&req, 0, sizeof(req)); - - mdelay(15); - - hdr->bus = PMU_I2C_BUS_STATUS; - req.reply[0] = 0xff; - - req.nbytes = 2; - req.reply_expected = 0; - req.reply_len = 0; - req.data[0] = PMU_I2C_CMD; - rc = pmu_queue_request(&req); - if (rc) - return rc; - while(!req.complete) - pmu_poll(); - if (req.reply[0] == PMU_I2C_STATUS_OK) - return len; - } - return -1; -} - -int -pmu_i2c_simple_read(int bus, int addr, u8* data, int len) -{ - struct adb_request req; - struct pmu_i2c_hdr *hdr = (struct pmu_i2c_hdr *)&req.data[1]; - int retry; - int rc; - - for (retry=0; retry<16; retry++) { - memset(&req, 0, sizeof(req)); - - hdr->bus = bus; - hdr->address = addr | 1; - hdr->mode = PMU_I2C_MODE_SIMPLE; - hdr->bus2 = 0; - hdr->sub_addr = 0; - hdr->comb_addr = 0; - hdr->count = len; - - req.data[0] = PMU_I2C_CMD; - req.nbytes = sizeof(struct pmu_i2c_hdr) + 1; - req.reply_expected = 0; - req.reply_len = 0; - req.reply[0] = 0xff; - rc = pmu_queue_request(&req); - if (rc) - return rc; - while(!req.complete) - pmu_poll(); - if (req.reply[0] == PMU_I2C_STATUS_OK) - break; - mdelay(15); - } - if (req.reply[0] != PMU_I2C_STATUS_OK) - return -1; - - for (retry=0; retry<16; retry++) { - memset(&req, 0, sizeof(req)); - - mdelay(15); - - hdr->bus = PMU_I2C_BUS_STATUS; - req.reply[0] = 0xff; - - req.nbytes = 2; - req.reply_expected = 0; - req.reply_len = 0; - req.data[0] = PMU_I2C_CMD; - rc = pmu_queue_request(&req); - if (rc) - return rc; - while(!req.complete) - pmu_poll(); - if (req.reply[0] == PMU_I2C_STATUS_DATAREAD) { - memcpy(data, &req.reply[1], req.reply_len - 1); - return req.reply_len - 1; - } - } - return -1; -} - -int -pmu_i2c_simple_write(int bus, int addr, u8* data, int len) -{ - struct adb_request req; - struct pmu_i2c_hdr *hdr = (struct pmu_i2c_hdr *)&req.data[1]; - int retry; - int rc; - - for (retry=0; retry<16; retry++) { - memset(&req, 0, sizeof(req)); - - hdr->bus = bus; - hdr->address = addr & 0xfe; - hdr->mode = PMU_I2C_MODE_SIMPLE; - hdr->bus2 = 0; - hdr->sub_addr = 0; - hdr->comb_addr = 0; - hdr->count = len; - - req.data[0] = PMU_I2C_CMD; - memcpy(&req.data[sizeof(struct pmu_i2c_hdr) + 1], data, len); - req.nbytes = sizeof(struct pmu_i2c_hdr) + len + 1; - req.reply_expected = 0; - req.reply_len = 0; - req.reply[0] = 0xff; - rc = pmu_queue_request(&req); - if (rc) - return rc; - while(!req.complete) - pmu_poll(); - if (req.reply[0] == PMU_I2C_STATUS_OK) - break; - mdelay(15); - } - if (req.reply[0] != PMU_I2C_STATUS_OK) - return -1; - - for (retry=0; retry<16; retry++) { - memset(&req, 0, sizeof(req)); - - mdelay(15); - - hdr->bus = PMU_I2C_BUS_STATUS; - req.reply[0] = 0xff; - - req.nbytes = 2; - req.reply_expected = 0; - req.reply_len = 0; - req.data[0] = PMU_I2C_CMD; - rc = pmu_queue_request(&req); - if (rc) - return rc; - while(!req.complete) - pmu_poll(); - if (req.reply[0] == PMU_I2C_STATUS_OK) - return len; - } - return -1; -} - #ifdef CONFIG_PM static LIST_HEAD(sleep_notifiers); @@ -2358,9 +2105,6 @@ pmac_suspend_devices(void) return -EBUSY; } - /* Disable clock spreading on some machines */ - pmac_tweak_clock_spreading(0); - /* Stop preemption */ preempt_disable(); @@ -2431,9 +2175,6 @@ pmac_wakeup_devices(void) mdelay(10); preempt_enable(); - /* Re-enable clock spreading on some machines */ - pmac_tweak_clock_spreading(1); - /* Resume devices */ device_resume(); @@ -3150,16 +2891,13 @@ static int __init init_pmu_sysfs(void) subsys_initcall(init_pmu_sysfs); EXPORT_SYMBOL(pmu_request); +EXPORT_SYMBOL(pmu_queue_request); EXPORT_SYMBOL(pmu_poll); EXPORT_SYMBOL(pmu_poll_adb); EXPORT_SYMBOL(pmu_wait_complete); EXPORT_SYMBOL(pmu_suspend); EXPORT_SYMBOL(pmu_resume); EXPORT_SYMBOL(pmu_unlock); -EXPORT_SYMBOL(pmu_i2c_combined_read); -EXPORT_SYMBOL(pmu_i2c_stdsub_write); -EXPORT_SYMBOL(pmu_i2c_simple_read); -EXPORT_SYMBOL(pmu_i2c_simple_write); #if defined(CONFIG_PM) && defined(CONFIG_PPC32) EXPORT_SYMBOL(pmu_enable_irled); EXPORT_SYMBOL(pmu_battery_count); diff --git a/include/asm-powerpc/pmac_feature.h b/include/asm-powerpc/pmac_feature.h index f6997ed5179ee5..e654ad0e5b428f 100644 --- a/include/asm-powerpc/pmac_feature.h +++ b/include/asm-powerpc/pmac_feature.h @@ -318,10 +318,6 @@ extern void pmac_register_agp_pm(struct pci_dev *bridge, extern void pmac_suspend_agp_for_card(struct pci_dev *dev); extern void pmac_resume_agp_for_card(struct pci_dev *dev); -/* Used by the via-pmu driver for suspend/resume - */ -extern void pmac_tweak_clock_spreading(int enable); - /* * The part below is for use by macio_asic.c only, do not rely * on the data structures or constants below in a normal driver diff --git a/include/asm-powerpc/pmac_low_i2c.h b/include/asm-powerpc/pmac_low_i2c.h index 3fb8d51540dd80..adf4fa956572a7 100644 --- a/include/asm-powerpc/pmac_low_i2c.h +++ b/include/asm-powerpc/pmac_low_i2c.h @@ -15,30 +15,87 @@ /* i2c mode (based on the platform functions format) */ enum { - pmac_low_i2c_mode_dumb = 1, - pmac_low_i2c_mode_std = 2, - pmac_low_i2c_mode_stdsub = 3, - pmac_low_i2c_mode_combined = 4, + pmac_i2c_mode_dumb = 1, + pmac_i2c_mode_std = 2, + pmac_i2c_mode_stdsub = 3, + pmac_i2c_mode_combined = 4, }; /* RW bit in address */ enum { - pmac_low_i2c_read = 0x01, - pmac_low_i2c_write = 0x00 + pmac_i2c_read = 0x01, + pmac_i2c_write = 0x00 }; +/* i2c bus type */ +enum { + pmac_i2c_bus_keywest = 0, + pmac_i2c_bus_pmu = 1, + pmac_i2c_bus_smu = 2, +}; + +/* i2c bus features */ +enum { + /* can_largesub : supports >1 byte subaddresses (SMU only) */ + pmac_i2c_can_largesub = 0x00000001u, + + /* multibus : device node holds multiple busses, bus number is + * encoded in bits 0xff00 of "reg" of a given device + */ + pmac_i2c_multibus = 0x00000002u, +}; + +/* i2c busses in the system */ +struct pmac_i2c_bus; +struct i2c_adapter; + /* Init, called early during boot */ -extern void pmac_init_low_i2c(void); +extern int pmac_i2c_init(void); + +/* Lookup an i2c bus for a device-node. The node can be either the bus + * node itself or a device below it. In the case of a multibus, the bus + * node itself is the controller node, else, it's a child of the controller + * node + */ +extern struct pmac_i2c_bus *pmac_i2c_find_bus(struct device_node *node); + +/* Get the address for an i2c device. This strips the bus number if + * necessary. The 7 bits address is returned 1 bit right shifted so that the + * direction can be directly ored in + */ +extern u8 pmac_i2c_get_dev_addr(struct device_node *device); + +/* Get infos about a bus */ +extern struct device_node *pmac_i2c_get_controller(struct pmac_i2c_bus *bus); +extern struct device_node *pmac_i2c_get_bus_node(struct pmac_i2c_bus *bus); +extern int pmac_i2c_get_type(struct pmac_i2c_bus *bus); +extern int pmac_i2c_get_flags(struct pmac_i2c_bus *bus); + +/* i2c layer adapter attach/detach */ +extern void pmac_i2c_attach_adapter(struct pmac_i2c_bus *bus, + struct i2c_adapter *adapter); +extern void pmac_i2c_detach_adapter(struct pmac_i2c_bus *bus, + struct i2c_adapter *adapter); +extern struct i2c_adapter *pmac_i2c_get_adapter(struct pmac_i2c_bus *bus); + +/* March a device or bus with an i2c adapter structure, to be used by drivers + * to match device-tree nodes with i2c adapters during adapter discovery + * callbacks + */ +extern int pmac_i2c_match_adapter(struct device_node *dev, + struct i2c_adapter *adapter); + -/* Locking functions exposed to i2c-keywest */ -int pmac_low_i2c_lock(struct device_node *np); -int pmac_low_i2c_unlock(struct device_node *np); +/* (legacy) Locking functions exposed to i2c-keywest */ +extern int pmac_low_i2c_lock(struct device_node *np); +extern int pmac_low_i2c_unlock(struct device_node *np); /* Access functions for platform code */ -int pmac_low_i2c_open(struct device_node *np, int channel); -int pmac_low_i2c_close(struct device_node *np); -int pmac_low_i2c_setmode(struct device_node *np, int mode); -int pmac_low_i2c_xfer(struct device_node *np, u8 addrdir, u8 subaddr, u8 *data, int len); +extern int pmac_i2c_open(struct pmac_i2c_bus *bus, int polled); +extern void pmac_i2c_close(struct pmac_i2c_bus *bus); +extern int pmac_i2c_setmode(struct pmac_i2c_bus *bus, int mode); +extern int pmac_i2c_xfer(struct pmac_i2c_bus *bus, u8 addrdir, int subsize, + u32 subaddr, u8 *data, int len); #endif /* __KERNEL__ */ diff --git a/include/asm-powerpc/smu.h b/include/asm-powerpc/smu.h index 7fae3ce9a8c1f4..134c2b5be0f284 100644 --- a/include/asm-powerpc/smu.h +++ b/include/asm-powerpc/smu.h @@ -358,6 +358,9 @@ extern unsigned long smu_cmdbuf_abs; * Kenrel asynchronous i2c interface */ +#define SMU_I2C_READ_MAX 0x1d +#define SMU_I2C_WRITE_MAX 0x15 + /* SMU i2c header, exactly matches i2c header on wire */ struct smu_i2c_param { @@ -368,12 +371,9 @@ struct smu_i2c_param u8 subaddr[3]; /* subaddress */ u8 caddr; /* combined address, filled by SMU driver */ u8 datalen; /* length of transfer */ - u8 data[7]; /* data */ + u8 data[SMU_I2C_READ_MAX]; /* data */ }; -#define SMU_I2C_READ_MAX 0x0d -#define SMU_I2C_WRITE_MAX 0x05 - struct smu_i2c_cmd { /* public */ @@ -387,7 +387,7 @@ struct smu_i2c_cmd int read; int stage; int retries; - u8 pdata[0x10]; + u8 pdata[32]; struct list_head link; }; @@ -519,7 +519,7 @@ struct smu_sdbp_cpupiddata { * if not found. The data format is described below */ extern struct smu_sdbp_header *smu_get_sdb_partition(int id, - unsigned int *size); + unsigned int *size); #endif /* __KERNEL__ */ diff --git a/include/linux/pmu.h b/include/linux/pmu.h index 373bd3b9b330f2..217d3daf73363e 100644 --- a/include/linux/pmu.h +++ b/include/linux/pmu.h @@ -140,7 +140,7 @@ extern int find_via_pmu(void); extern int pmu_request(struct adb_request *req, void (*done)(struct adb_request *), int nbytes, ...); - +extern int pmu_queue_request(struct adb_request *req); extern void pmu_poll(void); extern void pmu_poll_adb(void); /* For use by xmon */ extern void pmu_wait_complete(struct adb_request *req); @@ -160,12 +160,6 @@ extern void pmu_unlock(void); extern int pmu_present(void); extern int pmu_get_model(void); -extern int pmu_i2c_combined_read(int bus, int addr, int subaddr, u8* data, int len); -extern int pmu_i2c_stdsub_write(int bus, int addr, int subaddr, u8* data, int len); -extern int pmu_i2c_simple_read(int bus, int addr, u8* data, int len); -extern int pmu_i2c_simple_write(int bus, int addr, u8* data, int len); - - #ifdef CONFIG_PM /* * Stuff for putting the powerbook to sleep and waking it again. -- cgit 1.2.3-korg From a28d3af2a26c89aaa6470ca36edb212e05143d67 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Sat, 7 Jan 2006 11:35:26 +1100 Subject: [PATCH] 2/5 powerpc: Rework PowerMac i2c part 2 This is the continuation of the previous patch. This one removes the old PowerMac i2c drivers (i2c-keywest and i2c-pmac-smu) and replaces them both with a single stub driver that uses the new PowerMac low i2c layer. Now that i2c-keywest is gone, the low-i2c code is extended to support interrupt driver transfers. All i2c busses now appear as platform devices. Compatibility with existing drivers should be maintained as the i2c bus names have been kept identical, except for the SMU bus but in that later case, all users has been fixed. With that patch added, matching a device node to an i2c_adapter becomes trivial. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/powermac/low_i2c.c | 318 ++++++++++--- arch/powerpc/platforms/powermac/setup.c | 18 +- drivers/i2c/busses/Kconfig | 24 +- drivers/i2c/busses/Makefile | 3 +- drivers/i2c/busses/i2c-keywest.c | 754 ------------------------------ drivers/i2c/busses/i2c-keywest.h | 108 ----- drivers/i2c/busses/i2c-pmac-smu.c | 324 ------------- drivers/i2c/busses/i2c-powermac.c | 290 ++++++++++++ drivers/macintosh/Kconfig | 10 +- drivers/macintosh/smu.c | 26 +- drivers/macintosh/windfarm_lm75_sensor.c | 50 +- include/asm-powerpc/pmac_low_i2c.h | 2 + 12 files changed, 563 insertions(+), 1364 deletions(-) delete mode 100644 drivers/i2c/busses/i2c-keywest.c delete mode 100644 drivers/i2c/busses/i2c-keywest.h delete mode 100644 drivers/i2c/busses/i2c-pmac-smu.c create mode 100644 drivers/i2c/busses/i2c-powermac.c (limited to 'drivers') diff --git a/arch/powerpc/platforms/powermac/low_i2c.c b/arch/powerpc/platforms/powermac/low_i2c.c index f31d6a678b9ef1..a25e447f907f61 100644 --- a/arch/powerpc/platforms/powermac/low_i2c.c +++ b/arch/powerpc/platforms/powermac/low_i2c.c @@ -39,6 +39,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -63,6 +67,9 @@ #define DBG_LOW(x...) #endif + +static int pmac_i2c_force_poll = 1; + /* * A bus structure. Each bus in the system has such a structure associated. */ @@ -80,6 +87,7 @@ struct pmac_i2c_bus struct semaphore sem; int opened; int polled; /* open mode */ + struct platform_device *platform_dev; /* ops */ int (*open)(struct pmac_i2c_bus *bus); @@ -101,6 +109,16 @@ struct pmac_i2c_host_kw void __iomem *base; /* register base address */ int bsteps; /* register stepping */ int speed; /* speed */ + int irq; + u8 *data; + unsigned len; + int state; + int rw; + int polled; + int result; + struct completion complete; + spinlock_t lock; + struct timer_list timeout_timer; }; /* Register indices */ @@ -115,6 +133,8 @@ typedef enum { reg_data } reg_t; +/* The Tumbler audio equalizer can be really slow sometimes */ +#define KW_POLL_TIMEOUT (2*HZ) /* Mode register */ #define KW_I2C_MODE_100KHZ 0x00 @@ -158,8 +178,9 @@ enum { }; #define WRONG_STATE(name) do {\ - printk(KERN_DEBUG "KW: wrong state. Got %s, state: %s (isr: %02x)\n", \ - name, __kw_state_names[state], isr); \ + printk(KERN_DEBUG "KW: wrong state. Got %s, state: %s " \ + "(isr: %02x)\n", \ + name, __kw_state_names[host->state], isr); \ } while(0) static const char *__kw_state_names[] = { @@ -171,23 +192,22 @@ static const char *__kw_state_names[] = { "state_dead" }; -static inline u8 __kw_read_reg(struct pmac_i2c_bus *bus, reg_t reg) +static inline u8 __kw_read_reg(struct pmac_i2c_host_kw *host, reg_t reg) { - struct pmac_i2c_host_kw *host = bus->hostdata; return readb(host->base + (((unsigned int)reg) << host->bsteps)); } -static inline void __kw_write_reg(struct pmac_i2c_bus *bus, reg_t reg, u8 val) +static inline void __kw_write_reg(struct pmac_i2c_host_kw *host, + reg_t reg, u8 val) { - struct pmac_i2c_host_kw *host = bus->hostdata; writeb(val, host->base + (((unsigned)reg) << host->bsteps)); - (void)__kw_read_reg(bus, reg_subaddr); + (void)__kw_read_reg(host, reg_subaddr); } -#define kw_write_reg(reg, val) __kw_write_reg(bus, reg, val) -#define kw_read_reg(reg) __kw_read_reg(bus, reg) +#define kw_write_reg(reg, val) __kw_write_reg(host, reg, val) +#define kw_read_reg(reg) __kw_read_reg(host, reg) -static u8 kw_i2c_wait_interrupt(struct pmac_i2c_bus* bus) +static u8 kw_i2c_wait_interrupt(struct pmac_i2c_host_kw *host) { int i, j; u8 isr; @@ -201,8 +221,8 @@ static u8 kw_i2c_wait_interrupt(struct pmac_i2c_bus* bus) * on udelay nor schedule when in polled mode ! * For now, just use a bogus loop.... */ - if (bus->polled) { - for (j = 1; j < 1000000; j++) + if (host->polled) { + for (j = 1; j < 100000; j++) mb(); } else msleep(1); @@ -210,86 +230,99 @@ static u8 kw_i2c_wait_interrupt(struct pmac_i2c_bus* bus) return isr; } -static int kw_i2c_handle_interrupt(struct pmac_i2c_bus *bus, int state, int rw, - int *rc, u8 **data, int *len, u8 isr) +static void kw_i2c_handle_interrupt(struct pmac_i2c_host_kw *host, u8 isr) { u8 ack; DBG_LOW("kw_handle_interrupt(%s, isr: %x)\n", - __kw_state_names[state], isr); + __kw_state_names[host->state], isr); + + if (host->state == state_idle) { + printk(KERN_WARNING "low_i2c: Keywest got an out of state" + " interrupt, ignoring\n"); + kw_write_reg(reg_isr, isr); + return; + } if (isr == 0) { - if (state != state_stop) { + if (host->state != state_stop) { DBG_LOW("KW: Timeout !\n"); - *rc = -EIO; + host->result = -EIO; goto stop; } - if (state == state_stop) { + if (host->state == state_stop) { ack = kw_read_reg(reg_status); - if (!(ack & KW_I2C_STAT_BUSY)) { - state = state_idle; - kw_write_reg(reg_ier, 0x00); - } + if (ack & KW_I2C_STAT_BUSY) + kw_write_reg(reg_status, 0); + host->state = state_idle; + kw_write_reg(reg_ier, 0x00); + if (!host->polled) + complete(&host->complete); } - return state; + return; } if (isr & KW_I2C_IRQ_ADDR) { ack = kw_read_reg(reg_status); - if (state != state_addr) { + if (host->state != state_addr) { kw_write_reg(reg_isr, KW_I2C_IRQ_ADDR); WRONG_STATE("KW_I2C_IRQ_ADDR"); - *rc = -EIO; + host->result = -EIO; goto stop; } if ((ack & KW_I2C_STAT_LAST_AAK) == 0) { - *rc = -ENODEV; + host->result = -ENODEV; DBG_LOW("KW: NAK on address\n"); - return state_stop; + host->state = state_stop; + return; } else { - if (rw) { - state = state_read; - if (*len > 1) + if (host->len == 0) { + kw_write_reg(reg_isr, KW_I2C_IRQ_ADDR); + goto stop; + } + if (host->rw) { + host->state = state_read; + if (host->len > 1) kw_write_reg(reg_control, KW_I2C_CTL_AAK); } else { - state = state_write; - kw_write_reg(reg_data, **data); - (*data)++; (*len)--; + host->state = state_write; + kw_write_reg(reg_data, *(host->data++)); + host->len--; } } kw_write_reg(reg_isr, KW_I2C_IRQ_ADDR); } if (isr & KW_I2C_IRQ_DATA) { - if (state == state_read) { - **data = kw_read_reg(reg_data); - (*data)++; (*len)--; + if (host->state == state_read) { + *(host->data++) = kw_read_reg(reg_data); + host->len--; kw_write_reg(reg_isr, KW_I2C_IRQ_DATA); - if ((*len) == 0) - state = state_stop; - else if ((*len) == 1) + if (host->len == 0) + host->state = state_stop; + else if (host->len == 1) kw_write_reg(reg_control, 0); - } else if (state == state_write) { + } else if (host->state == state_write) { ack = kw_read_reg(reg_status); if ((ack & KW_I2C_STAT_LAST_AAK) == 0) { DBG_LOW("KW: nack on data write\n"); - *rc = -EIO; + host->result = -EIO; goto stop; - } else if (*len) { - kw_write_reg(reg_data, **data); - (*data)++; (*len)--; + } else if (host->len) { + kw_write_reg(reg_data, *(host->data++)); + host->len--; } else { kw_write_reg(reg_control, KW_I2C_CTL_STOP); - state = state_stop; - *rc = 0; + host->state = state_stop; + host->result = 0; } kw_write_reg(reg_isr, KW_I2C_IRQ_DATA); } else { kw_write_reg(reg_isr, KW_I2C_IRQ_DATA); WRONG_STATE("KW_I2C_IRQ_DATA"); - if (state != state_stop) { - *rc = -EIO; + if (host->state != state_stop) { + host->result = -EIO; goto stop; } } @@ -297,21 +330,54 @@ static int kw_i2c_handle_interrupt(struct pmac_i2c_bus *bus, int state, int rw, if (isr & KW_I2C_IRQ_STOP) { kw_write_reg(reg_isr, KW_I2C_IRQ_STOP); - if (state != state_stop) { + if (host->state != state_stop) { WRONG_STATE("KW_I2C_IRQ_STOP"); - *rc = -EIO; + host->result = -EIO; } - return state_idle; + host->state = state_idle; + if (!host->polled) + complete(&host->complete); } if (isr & KW_I2C_IRQ_START) kw_write_reg(reg_isr, KW_I2C_IRQ_START); - return state; - + return; stop: kw_write_reg(reg_control, KW_I2C_CTL_STOP); - return state_stop; + host->state = state_stop; + return; +} + +/* Interrupt handler */ +static irqreturn_t kw_i2c_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + struct pmac_i2c_host_kw *host = dev_id; + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + del_timer(&host->timeout_timer); + kw_i2c_handle_interrupt(host, kw_read_reg(reg_isr)); + if (host->state != state_idle) { + host->timeout_timer.expires = jiffies + KW_POLL_TIMEOUT; + add_timer(&host->timeout_timer); + } + spin_unlock_irqrestore(&host->lock, flags); + return IRQ_HANDLED; +} + +static void kw_i2c_timeout(unsigned long data) +{ + struct pmac_i2c_host_kw *host = (struct pmac_i2c_host_kw *)data; + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + kw_i2c_handle_interrupt(host, kw_read_reg(reg_isr)); + if (host->state != state_idle) { + host->timeout_timer.expires = jiffies + KW_POLL_TIMEOUT; + add_timer(&host->timeout_timer); + } + spin_unlock_irqrestore(&host->lock, flags); } static int kw_i2c_open(struct pmac_i2c_bus *bus) @@ -332,8 +398,7 @@ static int kw_i2c_xfer(struct pmac_i2c_bus *bus, u8 addrdir, int subsize, { struct pmac_i2c_host_kw *host = bus->hostdata; u8 mode_reg = host->speed; - int state = state_addr; - int rc = 0; + int use_irq = host->irq != NO_IRQ && !bus->polled; /* Setup mode & subaddress if any */ switch(bus->mode) { @@ -371,18 +436,50 @@ static int kw_i2c_xfer(struct pmac_i2c_bus *bus, u8 addrdir, int subsize, || (mode_reg & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_COMBINED) kw_write_reg(reg_subaddr, subaddr); - /* Start sending address & disable interrupt*/ - kw_write_reg(reg_ier, 0 /*KW_I2C_IRQ_MASK*/); + /* Prepare for async operations */ + host->data = data; + host->len = len; + host->state = state_addr; + host->result = 0; + host->rw = (addrdir & 1); + host->polled = bus->polled; + + /* Enable interrupt if not using polled mode and interrupt is + * available + */ + if (use_irq) { + /* Clear completion */ + INIT_COMPLETION(host->complete); + /* Ack stale interrupts */ + kw_write_reg(reg_isr, kw_read_reg(reg_isr)); + /* Arm timeout */ + host->timeout_timer.expires = jiffies + KW_POLL_TIMEOUT; + add_timer(&host->timeout_timer); + /* Enable emission */ + kw_write_reg(reg_ier, KW_I2C_IRQ_MASK); + } + + /* Start sending address */ kw_write_reg(reg_control, KW_I2C_CTL_XADDR); - /* State machine, to turn into an interrupt handler in the future */ - while(state != state_idle) { - u8 isr = kw_i2c_wait_interrupt(bus); - state = kw_i2c_handle_interrupt(bus, state, addrdir & 1, &rc, - &data, &len, isr); + /* Wait for completion */ + if (use_irq) + wait_for_completion(&host->complete); + else { + while(host->state != state_idle) { + unsigned long flags; + + u8 isr = kw_i2c_wait_interrupt(host); + spin_lock_irqsave(&host->lock, flags); + kw_i2c_handle_interrupt(host, isr); + spin_unlock_irqrestore(&host->lock, flags); + } } - return rc; + /* Disable emission */ + kw_write_reg(reg_ier, 0); + + return host->result; } static struct pmac_i2c_host_kw *__init kw_i2c_host_init(struct device_node *np) @@ -409,6 +506,12 @@ static struct pmac_i2c_host_kw *__init kw_i2c_host_init(struct device_node *np) return NULL; } init_MUTEX(&host->mutex); + init_completion(&host->complete); + spin_lock_init(&host->lock); + init_timer(&host->timeout_timer); + host->timeout_timer.function = kw_i2c_timeout; + host->timeout_timer.data = (unsigned long)host; + psteps = (u32 *)get_property(np, "AAPL,address-step", NULL); steps = psteps ? (*psteps) : 0x10; for (host->bsteps = 0; (steps & 0x01) == 0; host->bsteps++) @@ -427,9 +530,28 @@ static struct pmac_i2c_host_kw *__init kw_i2c_host_init(struct device_node *np) host->speed = KW_I2C_MODE_25KHZ; break; } + if (np->n_intrs > 0) + host->irq = np->intrs[0].line; + else + host->irq = NO_IRQ; - printk(KERN_INFO "KeyWest i2c @0x%08x %s\n", *addrp, np->full_name); host->base = ioremap((*addrp), 0x1000); + if (host->base == NULL) { + printk(KERN_ERR "low_i2c: Can't map registers for %s\n", + np->full_name); + kfree(host); + return NULL; + } + + /* Make sure IRA is disabled */ + kw_write_reg(reg_ier, 0); + + /* Request chip interrupt */ + if (request_irq(host->irq, kw_i2c_irq, SA_SHIRQ, "keywest i2c", host)) + host->irq = NO_IRQ; + + printk(KERN_INFO "KeyWest i2c @0x%08x irq %d %s\n", + *addrp, host->irq, np->full_name); return host; } @@ -591,7 +713,7 @@ static int pmu_i2c_xfer(struct pmac_i2c_bus *bus, u8 addrdir, int subsize, req->nbytes = sizeof(struct pmu_i2c_hdr) + 1; req->done = pmu_i2c_complete; req->arg = ∁ - if (!read) { + if (!read && len) { memcpy(hdr->data, data, len); req->nbytes += len; } @@ -637,7 +759,8 @@ static int pmu_i2c_xfer(struct pmac_i2c_bus *bus, u8 addrdir, int subsize, " bytes, expected %d !\n", rlen, len); return -EIO; } - memcpy(data, &req->reply[1], len); + if (len) + memcpy(data, &req->reply[1], len); return 0; } } @@ -713,6 +836,10 @@ static int smu_i2c_xfer(struct pmac_i2c_bus *bus, u8 addrdir, int subsize, int read = addrdir & 1; int rc = 0; + if ((read && len > SMU_I2C_READ_MAX) || + ((!read) && len > SMU_I2C_WRITE_MAX)) + return -EINVAL; + memset(cmd, 0, sizeof(struct smu_i2c_cmd)); cmd->info.bus = bus->channel; cmd->info.devaddr = addrdir; @@ -740,7 +867,7 @@ static int smu_i2c_xfer(struct pmac_i2c_bus *bus, u8 addrdir, int subsize, default: return -EINVAL; } - if (!read) + if (!read && len) memcpy(cmd->info.data, data, len); init_completion(&comp); @@ -752,7 +879,7 @@ static int smu_i2c_xfer(struct pmac_i2c_bus *bus, u8 addrdir, int subsize, wait_for_completion(&comp); rc = cmd->status; - if (read) + if (read && len) memcpy(data, cmd->info.data, len); return rc < 0 ? rc : 0; } @@ -767,7 +894,7 @@ static void __init smu_i2c_probe(void) if (!smu_present()) return; - controller = of_find_node_by_name(NULL, "smu_i2c_control"); + controller = of_find_node_by_name(NULL, "smu-i2c-control"); if (controller == NULL) controller = of_find_node_by_name(NULL, "smu"); if (controller == NULL) @@ -884,6 +1011,13 @@ int pmac_i2c_get_flags(struct pmac_i2c_bus *bus) } EXPORT_SYMBOL_GPL(pmac_i2c_get_flags); +int pmac_i2c_get_channel(struct pmac_i2c_bus *bus) +{ + return bus->channel; +} +EXPORT_SYMBOL_GPL(pmac_i2c_get_channel); + + void pmac_i2c_attach_adapter(struct pmac_i2c_bus *bus, struct i2c_adapter *adapter) { @@ -906,6 +1040,17 @@ struct i2c_adapter *pmac_i2c_get_adapter(struct pmac_i2c_bus *bus) } EXPORT_SYMBOL_GPL(pmac_i2c_get_adapter); +struct pmac_i2c_bus *pmac_i2c_adapter_to_bus(struct i2c_adapter *adapter) +{ + struct pmac_i2c_bus *bus; + + list_for_each_entry(bus, &pmac_i2c_busses, link) + if (bus->adapter == adapter) + return bus; + return NULL; +} +EXPORT_SYMBOL_GPL(pmac_i2c_adapter_to_bus); + extern int pmac_i2c_match_adapter(struct device_node *dev, struct i2c_adapter *adapter) { @@ -956,7 +1101,7 @@ int pmac_i2c_open(struct pmac_i2c_bus *bus, int polled) int rc; down(&bus->sem); - bus->polled = polled; + bus->polled = polled || pmac_i2c_force_poll; bus->opened = 1; bus->mode = pmac_i2c_mode_std; if (bus->open && (rc = bus->open(bus)) != 0) { @@ -1034,14 +1179,43 @@ int __init pmac_i2c_init(void) kw_i2c_probe(); #ifdef CONFIG_ADB_PMU + /* Probe PMU i2c busses */ pmu_i2c_probe(); #endif #ifdef CONFIG_PMAC_SMU + /* Probe SMU i2c busses */ smu_i2c_probe(); #endif - return 0; } arch_initcall(pmac_i2c_init); +/* Since pmac_i2c_init can be called too early for the platform device + * registration, we need to do it at a later time. In our case, subsys + * happens to fit well, though I agree it's a bit of a hack... + */ +static int __init pmac_i2c_create_platform_devices(void) +{ + struct pmac_i2c_bus *bus; + int i = 0; + + /* In the case where we are initialized from smp_init(), we must + * not use the timer (and thus the irq). It's safe from now on + * though + */ + pmac_i2c_force_poll = 0; + + /* Create platform devices */ + list_for_each_entry(bus, &pmac_i2c_busses, link) { + bus->platform_dev = + platform_device_alloc("i2c-powermac", i++); + if (bus->platform_dev == NULL) + return -ENOMEM; + bus->platform_dev->dev.platform_data = bus; + platform_device_add(bus->platform_dev); + } + + return 0; +} +subsys_initcall(pmac_i2c_create_platform_devices); diff --git a/arch/powerpc/platforms/powermac/setup.c b/arch/powerpc/platforms/powermac/setup.c index dc5cdc1484e89d..3b1a9d4fcbc6c7 100644 --- a/arch/powerpc/platforms/powermac/setup.c +++ b/arch/powerpc/platforms/powermac/setup.c @@ -650,7 +650,7 @@ static int pmac_check_legacy_ioport(unsigned int baseport) static int __init pmac_declare_of_platform_devices(void) { - struct device_node *np, *npp; + struct device_node *np; np = of_find_node_by_name(NULL, "valkyrie"); if (np) @@ -658,22 +658,6 @@ static int __init pmac_declare_of_platform_devices(void) np = of_find_node_by_name(NULL, "platinum"); if (np) of_platform_device_create(np, "platinum", NULL); - npp = of_find_node_by_name(NULL, "uni-n"); - if (npp == NULL) - npp = of_find_node_by_name(NULL, "u3"); - if (npp == NULL) - npp = of_find_node_by_name(NULL, "u4"); - if (npp) { - for (np = NULL; (np = of_get_next_child(npp, np)) != NULL;) { - if (strncmp(np->name, "i2c", 3) == 0) { - of_platform_device_create(np, "uni-n-i2c", - NULL); - of_node_put(np); - break; - } - } - of_node_put(npp); - } np = of_find_node_by_type(NULL, "smu"); if (np) { of_platform_device_create(np, "smu", NULL); diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 4010fe92e72b63..08d5b8fed2dc5f 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -236,27 +236,17 @@ config I2C_IXP2000 This support is also available as a module. If so, the module will be called i2c-ixp2000. -config I2C_KEYWEST - tristate "Powermac Keywest I2C interface" +config I2C_POWERMAC + tristate "Powermac I2C interface" depends on I2C && PPC_PMAC + default y help - This supports the use of the I2C interface in the combo-I/O - chip on recent Apple machines. Say Y if you have such a machine. - - This support is also available as a module. If so, the module - will be called i2c-keywest. - -config I2C_PMAC_SMU - tristate "Powermac SMU I2C interface" - depends on I2C && PMAC_SMU - help - This supports the use of the I2C interface in the SMU - chip on recent Apple machines like the iMac G5. It is used - among others by the thermal control driver for those machines. - Say Y if you have such a machine. + This exposes the various PowerMac i2c interfaces to the linux i2c + layer and to userland. It is used by various drivers on the powemac + platform, thus should generally be enabled. This support is also available as a module. If so, the module - will be called i2c-pmac-smu. + will be called i2c-powermac. config I2C_MPC tristate "MPC107/824x/85xx/52xx" diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index f1df00f66c6cde..b44831dff6833a 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -19,8 +19,7 @@ obj-$(CONFIG_I2C_ISA) += i2c-isa.o obj-$(CONFIG_I2C_ITE) += i2c-ite.o obj-$(CONFIG_I2C_IXP2000) += i2c-ixp2000.o obj-$(CONFIG_I2C_IXP4XX) += i2c-ixp4xx.o -obj-$(CONFIG_I2C_KEYWEST) += i2c-keywest.o -obj-$(CONFIG_I2C_PMAC_SMU) += i2c-pmac-smu.o +obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o obj-$(CONFIG_I2C_MPC) += i2c-mpc.o obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o diff --git a/drivers/i2c/busses/i2c-keywest.c b/drivers/i2c/busses/i2c-keywest.c deleted file mode 100644 index 93e7080e3bc5af..00000000000000 --- a/drivers/i2c/busses/i2c-keywest.c +++ /dev/null @@ -1,754 +0,0 @@ -/* - i2c Support for Apple Keywest I2C Bus Controller - - Copyright (c) 2001 Benjamin Herrenschmidt - - Original work by - - Copyright (c) 2000 Philip Edelbrock - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - Changes: - - 2001/12/13 BenH New implementation - 2001/12/15 BenH Add support for "byte" and "quick" - transfers. Add i2c_xfer routine. - 2003/09/21 BenH Rework state machine with Paulus help - 2004/01/21 BenH Merge in Greg KH changes, polled mode is back - 2004/02/05 BenH Merge 64 bits fixes from the g5 ppc64 tree - - My understanding of the various modes supported by keywest are: - - - Dumb mode : not implemented, probably direct tweaking of lines - - Standard mode : simple i2c transaction of type - S Addr R/W A Data A Data ... T - - Standard sub mode : combined 8 bit subaddr write with data read - S Addr R/W A SubAddr A Data A Data ... T - - Combined mode : Subaddress and Data sequences appended with no stop - S Addr R/W A SubAddr S Addr R/W A Data A Data ... T - - Currently, this driver uses only Standard mode for i2c xfer, and - smbus byte & quick transfers ; and uses StandardSub mode for - other smbus transfers instead of combined as we need that for the - sound driver to be happy -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "i2c-keywest.h" - -#undef POLLED_MODE - -/* Some debug macros */ -#define WRONG_STATE(name) do {\ - pr_debug("KW: wrong state. Got %s, state: %s (isr: %02x)\n", \ - name, __kw_state_names[iface->state], isr); \ - } while(0) - -#ifdef DEBUG -static const char *__kw_state_names[] = { - "state_idle", - "state_addr", - "state_read", - "state_write", - "state_stop", - "state_dead" -}; -#endif /* DEBUG */ - -MODULE_AUTHOR("Benjamin Herrenschmidt "); -MODULE_DESCRIPTION("I2C driver for Apple's Keywest"); -MODULE_LICENSE("GPL"); - -#ifdef POLLED_MODE -/* Don't schedule, the g5 fan controller is too - * timing sensitive - */ -static u8 -wait_interrupt(struct keywest_iface* iface) -{ - int i; - u8 isr; - - for (i = 0; i < 200000; i++) { - isr = read_reg(reg_isr) & KW_I2C_IRQ_MASK; - if (isr != 0) - return isr; - udelay(10); - } - return isr; -} -#endif /* POLLED_MODE */ - -static void -do_stop(struct keywest_iface* iface, int result) -{ - write_reg(reg_control, KW_I2C_CTL_STOP); - iface->state = state_stop; - iface->result = result; -} - -/* Main state machine for standard & standard sub mode */ -static void -handle_interrupt(struct keywest_iface *iface, u8 isr) -{ - int ack; - - if (isr == 0) { - if (iface->state != state_stop) { - pr_debug("KW: Timeout !\n"); - do_stop(iface, -EIO); - } - if (iface->state == state_stop) { - ack = read_reg(reg_status); - if (!(ack & KW_I2C_STAT_BUSY)) { - iface->state = state_idle; - write_reg(reg_ier, 0x00); -#ifndef POLLED_MODE - complete(&iface->complete); -#endif /* POLLED_MODE */ - } - } - return; - } - - if (isr & KW_I2C_IRQ_ADDR) { - ack = read_reg(reg_status); - if (iface->state != state_addr) { - write_reg(reg_isr, KW_I2C_IRQ_ADDR); - WRONG_STATE("KW_I2C_IRQ_ADDR"); - do_stop(iface, -EIO); - return; - } - if ((ack & KW_I2C_STAT_LAST_AAK) == 0) { - iface->state = state_stop; - iface->result = -ENODEV; - pr_debug("KW: NAK on address\n"); - } else { - /* Handle rw "quick" mode */ - if (iface->datalen == 0) { - do_stop(iface, 0); - } else if (iface->read_write == I2C_SMBUS_READ) { - iface->state = state_read; - if (iface->datalen > 1) - write_reg(reg_control, KW_I2C_CTL_AAK); - } else { - iface->state = state_write; - write_reg(reg_data, *(iface->data++)); - iface->datalen--; - } - } - write_reg(reg_isr, KW_I2C_IRQ_ADDR); - } - - if (isr & KW_I2C_IRQ_DATA) { - if (iface->state == state_read) { - *(iface->data++) = read_reg(reg_data); - write_reg(reg_isr, KW_I2C_IRQ_DATA); - iface->datalen--; - if (iface->datalen == 0) - iface->state = state_stop; - else if (iface->datalen == 1) - write_reg(reg_control, 0); - } else if (iface->state == state_write) { - /* Check ack status */ - ack = read_reg(reg_status); - if ((ack & KW_I2C_STAT_LAST_AAK) == 0) { - pr_debug("KW: nack on data write (%x): %x\n", - iface->data[-1], ack); - do_stop(iface, -EIO); - } else if (iface->datalen) { - write_reg(reg_data, *(iface->data++)); - iface->datalen--; - } else { - write_reg(reg_control, KW_I2C_CTL_STOP); - iface->state = state_stop; - iface->result = 0; - } - write_reg(reg_isr, KW_I2C_IRQ_DATA); - } else { - write_reg(reg_isr, KW_I2C_IRQ_DATA); - WRONG_STATE("KW_I2C_IRQ_DATA"); - if (iface->state != state_stop) - do_stop(iface, -EIO); - } - } - - if (isr & KW_I2C_IRQ_STOP) { - write_reg(reg_isr, KW_I2C_IRQ_STOP); - if (iface->state != state_stop) { - WRONG_STATE("KW_I2C_IRQ_STOP"); - iface->result = -EIO; - } - iface->state = state_idle; - write_reg(reg_ier, 0x00); -#ifndef POLLED_MODE - complete(&iface->complete); -#endif /* POLLED_MODE */ - } - - if (isr & KW_I2C_IRQ_START) - write_reg(reg_isr, KW_I2C_IRQ_START); -} - -#ifndef POLLED_MODE - -/* Interrupt handler */ -static irqreturn_t -keywest_irq(int irq, void *dev_id, struct pt_regs *regs) -{ - struct keywest_iface *iface = (struct keywest_iface *)dev_id; - unsigned long flags; - - spin_lock_irqsave(&iface->lock, flags); - del_timer(&iface->timeout_timer); - handle_interrupt(iface, read_reg(reg_isr)); - if (iface->state != state_idle) { - iface->timeout_timer.expires = jiffies + POLL_TIMEOUT; - add_timer(&iface->timeout_timer); - } - spin_unlock_irqrestore(&iface->lock, flags); - return IRQ_HANDLED; -} - -static void -keywest_timeout(unsigned long data) -{ - struct keywest_iface *iface = (struct keywest_iface *)data; - unsigned long flags; - - pr_debug("timeout !\n"); - spin_lock_irqsave(&iface->lock, flags); - handle_interrupt(iface, read_reg(reg_isr)); - if (iface->state != state_idle) { - iface->timeout_timer.expires = jiffies + POLL_TIMEOUT; - add_timer(&iface->timeout_timer); - } - spin_unlock_irqrestore(&iface->lock, flags); -} - -#endif /* POLLED_MODE */ - -/* - * SMBUS-type transfer entrypoint - */ -static s32 -keywest_smbus_xfer( struct i2c_adapter* adap, - u16 addr, - unsigned short flags, - char read_write, - u8 command, - int size, - union i2c_smbus_data* data) -{ - struct keywest_chan* chan = i2c_get_adapdata(adap); - struct keywest_iface* iface = chan->iface; - int len; - u8* buffer; - u16 cur_word; - int rc = 0; - - if (iface->state == state_dead) - return -ENXIO; - - /* Prepare datas & select mode */ - iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK; - switch (size) { - case I2C_SMBUS_QUICK: - len = 0; - buffer = NULL; - iface->cur_mode |= KW_I2C_MODE_STANDARD; - break; - case I2C_SMBUS_BYTE: - len = 1; - buffer = &data->byte; - iface->cur_mode |= KW_I2C_MODE_STANDARD; - break; - case I2C_SMBUS_BYTE_DATA: - len = 1; - buffer = &data->byte; - iface->cur_mode |= KW_I2C_MODE_STANDARDSUB; - break; - case I2C_SMBUS_WORD_DATA: - len = 2; - cur_word = cpu_to_le16(data->word); - buffer = (u8 *)&cur_word; - iface->cur_mode |= KW_I2C_MODE_STANDARDSUB; - break; - case I2C_SMBUS_BLOCK_DATA: - len = data->block[0]; - buffer = &data->block[1]; - iface->cur_mode |= KW_I2C_MODE_STANDARDSUB; - break; - default: - return -1; - } - - /* Turn a standardsub read into a combined mode access */ - if (read_write == I2C_SMBUS_READ - && (iface->cur_mode & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_STANDARDSUB) { - iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK; - iface->cur_mode |= KW_I2C_MODE_COMBINED; - } - - /* Original driver had this limitation */ - if (len > 32) - len = 32; - - if (pmac_low_i2c_lock(iface->node)) - return -ENXIO; - - pr_debug("chan: %d, addr: 0x%x, transfer len: %d, read: %d\n", - chan->chan_no, addr, len, read_write == I2C_SMBUS_READ); - - iface->data = buffer; - iface->datalen = len; - iface->state = state_addr; - iface->result = 0; - iface->read_write = read_write; - - /* Setup channel & clear pending irqs */ - write_reg(reg_isr, read_reg(reg_isr)); - write_reg(reg_mode, iface->cur_mode | (chan->chan_no << 4)); - write_reg(reg_status, 0); - - /* Set up address and r/w bit */ - write_reg(reg_addr, - (addr << 1) | ((read_write == I2C_SMBUS_READ) ? 0x01 : 0x00)); - - /* Set up the sub address */ - if ((iface->cur_mode & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_STANDARDSUB - || (iface->cur_mode & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_COMBINED) - write_reg(reg_subaddr, command); - -#ifndef POLLED_MODE - /* Arm timeout */ - iface->timeout_timer.expires = jiffies + POLL_TIMEOUT; - add_timer(&iface->timeout_timer); -#endif - - /* Start sending address & enable interrupt*/ - write_reg(reg_control, KW_I2C_CTL_XADDR); - write_reg(reg_ier, KW_I2C_IRQ_MASK); - -#ifdef POLLED_MODE - pr_debug("using polled mode...\n"); - /* State machine, to turn into an interrupt handler */ - while(iface->state != state_idle) { - unsigned long flags; - - u8 isr = wait_interrupt(iface); - spin_lock_irqsave(&iface->lock, flags); - handle_interrupt(iface, isr); - spin_unlock_irqrestore(&iface->lock, flags); - } -#else /* POLLED_MODE */ - pr_debug("using interrupt mode...\n"); - wait_for_completion(&iface->complete); -#endif /* POLLED_MODE */ - - rc = iface->result; - pr_debug("transfer done, result: %d\n", rc); - - if (rc == 0 && size == I2C_SMBUS_WORD_DATA && read_write == I2C_SMBUS_READ) - data->word = le16_to_cpu(cur_word); - - /* Release sem */ - pmac_low_i2c_unlock(iface->node); - - return rc; -} - -/* - * Generic i2c master transfer entrypoint - */ -static int -keywest_xfer( struct i2c_adapter *adap, - struct i2c_msg *msgs, - int num) -{ - struct keywest_chan* chan = i2c_get_adapdata(adap); - struct keywest_iface* iface = chan->iface; - struct i2c_msg *pmsg; - int i, completed; - int rc = 0; - - if (iface->state == state_dead) - return -ENXIO; - - if (pmac_low_i2c_lock(iface->node)) - return -ENXIO; - - /* Set adapter to standard mode */ - iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK; - iface->cur_mode |= KW_I2C_MODE_STANDARD; - - completed = 0; - for (i = 0; rc >= 0 && i < num;) { - u8 addr; - - pmsg = &msgs[i++]; - addr = pmsg->addr; - if (pmsg->flags & I2C_M_TEN) { - printk(KERN_ERR "i2c-keywest: 10 bits addr not supported !\n"); - rc = -EINVAL; - break; - } - pr_debug("xfer: chan: %d, doing %s %d bytes to 0x%02x - %d of %d messages\n", - chan->chan_no, - pmsg->flags & I2C_M_RD ? "read" : "write", - pmsg->len, addr, i, num); - - /* Setup channel & clear pending irqs */ - write_reg(reg_mode, iface->cur_mode | (chan->chan_no << 4)); - write_reg(reg_isr, read_reg(reg_isr)); - write_reg(reg_status, 0); - - iface->data = pmsg->buf; - iface->datalen = pmsg->len; - iface->state = state_addr; - iface->result = 0; - if (pmsg->flags & I2C_M_RD) - iface->read_write = I2C_SMBUS_READ; - else - iface->read_write = I2C_SMBUS_WRITE; - - /* Set up address and r/w bit */ - if (pmsg->flags & I2C_M_REV_DIR_ADDR) - addr ^= 1; - write_reg(reg_addr, - (addr << 1) | - ((iface->read_write == I2C_SMBUS_READ) ? 0x01 : 0x00)); - -#ifndef POLLED_MODE - /* Arm timeout */ - iface->timeout_timer.expires = jiffies + POLL_TIMEOUT; - add_timer(&iface->timeout_timer); -#endif - - /* Start sending address & enable interrupt*/ - write_reg(reg_ier, KW_I2C_IRQ_MASK); - write_reg(reg_control, KW_I2C_CTL_XADDR); - -#ifdef POLLED_MODE - pr_debug("using polled mode...\n"); - /* State machine, to turn into an interrupt handler */ - while(iface->state != state_idle) { - u8 isr = wait_interrupt(iface); - handle_interrupt(iface, isr); - } -#else /* POLLED_MODE */ - pr_debug("using interrupt mode...\n"); - wait_for_completion(&iface->complete); -#endif /* POLLED_MODE */ - - rc = iface->result; - if (rc == 0) - completed++; - pr_debug("transfer done, result: %d\n", rc); - } - - /* Release sem */ - pmac_low_i2c_unlock(iface->node); - - return completed; -} - -static u32 -keywest_func(struct i2c_adapter * adapter) -{ - return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | - I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | - I2C_FUNC_SMBUS_BLOCK_DATA; -} - -/* For now, we only handle combined mode (smbus) */ -static struct i2c_algorithm keywest_algorithm = { - .smbus_xfer = keywest_smbus_xfer, - .master_xfer = keywest_xfer, - .functionality = keywest_func, -}; - - -static int -create_iface(struct device_node *np, struct device *dev) -{ - unsigned long steps; - unsigned bsteps, tsize, i, nchan; - struct keywest_iface* iface; - u32 *psteps, *prate, *addrp; - int rc; - - if (np->n_intrs < 1) { - printk(KERN_ERR "%s: Missing interrupt !\n", - np->full_name); - return -ENODEV; - } - addrp = (u32 *)get_property(np, "AAPL,address", NULL); - if (addrp == NULL) { - printk(KERN_ERR "%s: Can't find address !\n", - np->full_name); - return -ENODEV; - } - - if (pmac_low_i2c_lock(np)) - return -ENODEV; - - psteps = (u32 *)get_property(np, "AAPL,address-step", NULL); - steps = psteps ? (*psteps) : 0x10; - - /* Hrm... maybe we can be smarter here */ - for (bsteps = 0; (steps & 0x01) == 0; bsteps++) - steps >>= 1; - - if (np->parent->name[0] == 'u') - nchan = 2; - else - nchan = 1; - - tsize = sizeof(struct keywest_iface) + - (sizeof(struct keywest_chan) + 4) * nchan; - iface = kzalloc(tsize, GFP_KERNEL); - if (iface == NULL) { - printk(KERN_ERR "i2c-keywest: can't allocate inteface !\n"); - pmac_low_i2c_unlock(np); - return -ENOMEM; - } - spin_lock_init(&iface->lock); - init_completion(&iface->complete); - iface->node = of_node_get(np); - iface->bsteps = bsteps; - iface->chan_count = nchan; - iface->state = state_idle; - iface->irq = np->intrs[0].line; - iface->channels = (struct keywest_chan *) - (((unsigned long)(iface + 1) + 3UL) & ~3UL); - iface->base = ioremap(*addrp, 0x1000); - if (!iface->base) { - printk(KERN_ERR "i2c-keywest: can't map inteface !\n"); - kfree(iface); - pmac_low_i2c_unlock(np); - return -ENOMEM; - } - -#ifndef POLLED_MODE - init_timer(&iface->timeout_timer); - iface->timeout_timer.function = keywest_timeout; - iface->timeout_timer.data = (unsigned long)iface; -#endif - - /* Select interface rate */ - iface->cur_mode = KW_I2C_MODE_100KHZ; - prate = (u32 *)get_property(np, "AAPL,i2c-rate", NULL); - if (prate) switch(*prate) { - case 100: - iface->cur_mode = KW_I2C_MODE_100KHZ; - break; - case 50: - iface->cur_mode = KW_I2C_MODE_50KHZ; - break; - case 25: - iface->cur_mode = KW_I2C_MODE_25KHZ; - break; - default: - printk(KERN_WARNING "i2c-keywest: unknown rate %ldKhz, using 100KHz\n", - (long)*prate); - } - - /* Select standard mode by default */ - iface->cur_mode |= KW_I2C_MODE_STANDARD; - - /* Write mode */ - write_reg(reg_mode, iface->cur_mode); - - /* Switch interrupts off & clear them*/ - write_reg(reg_ier, 0x00); - write_reg(reg_isr, KW_I2C_IRQ_MASK); - -#ifndef POLLED_MODE - /* Request chip interrupt */ - rc = request_irq(iface->irq, keywest_irq, SA_INTERRUPT, "keywest i2c", iface); - if (rc) { - printk(KERN_ERR "i2c-keywest: can't get IRQ %d !\n", iface->irq); - iounmap(iface->base); - kfree(iface); - pmac_low_i2c_unlock(np); - return -ENODEV; - } -#endif /* POLLED_MODE */ - - pmac_low_i2c_unlock(np); - dev_set_drvdata(dev, iface); - - for (i=0; ichannels[i]; - - sprintf(chan->adapter.name, "%s %d", np->parent->name, i); - chan->iface = iface; - chan->chan_no = i; - chan->adapter.algo = &keywest_algorithm; - chan->adapter.algo_data = NULL; - chan->adapter.client_register = NULL; - chan->adapter.client_unregister = NULL; - i2c_set_adapdata(&chan->adapter, chan); - chan->adapter.dev.parent = dev; - - rc = i2c_add_adapter(&chan->adapter); - if (rc) { - printk("i2c-keywest.c: Adapter %s registration failed\n", - chan->adapter.name); - i2c_set_adapdata(&chan->adapter, NULL); - } - } - - printk(KERN_INFO "Found KeyWest i2c on \"%s\", %d channel%s, stepping: %d bits\n", - np->parent->name, nchan, nchan > 1 ? "s" : "", bsteps); - - return 0; -} - -static int -dispose_iface(struct device *dev) -{ - struct keywest_iface *iface = dev_get_drvdata(dev); - int i, rc; - - /* Make sure we stop all activity */ - if (pmac_low_i2c_lock(iface->node)) - return -ENODEV; - -#ifndef POLLED_MODE - spin_lock_irq(&iface->lock); - while (iface->state != state_idle) { - spin_unlock_irq(&iface->lock); - msleep(100); - spin_lock_irq(&iface->lock); - } -#endif /* POLLED_MODE */ - iface->state = state_dead; -#ifndef POLLED_MODE - spin_unlock_irq(&iface->lock); - free_irq(iface->irq, iface); -#endif /* POLLED_MODE */ - - pmac_low_i2c_unlock(iface->node); - - /* Release all channels */ - for (i=0; ichan_count; i++) { - struct keywest_chan* chan = &iface->channels[i]; - if (i2c_get_adapdata(&chan->adapter) == NULL) - continue; - rc = i2c_del_adapter(&chan->adapter); - i2c_set_adapdata(&chan->adapter, NULL); - /* We aren't that prepared to deal with this... */ - if (rc) - printk("i2c-keywest.c: i2c_del_adapter failed, that's bad !\n"); - } - iounmap(iface->base); - dev_set_drvdata(dev, NULL); - of_node_put(iface->node); - kfree(iface); - - return 0; -} - -static int -create_iface_macio(struct macio_dev* dev, const struct of_device_id *match) -{ - return create_iface(dev->ofdev.node, &dev->ofdev.dev); -} - -static int -dispose_iface_macio(struct macio_dev* dev) -{ - return dispose_iface(&dev->ofdev.dev); -} - -static int -create_iface_of_platform(struct of_device* dev, const struct of_device_id *match) -{ - return create_iface(dev->node, &dev->dev); -} - -static int -dispose_iface_of_platform(struct of_device* dev) -{ - return dispose_iface(&dev->dev); -} - -static struct of_device_id i2c_keywest_match[] = -{ - { - .type = "i2c", - .compatible = "keywest" - }, - {}, -}; - -static struct macio_driver i2c_keywest_macio_driver = -{ - .owner = THIS_MODULE, - .name = "i2c-keywest", - .match_table = i2c_keywest_match, - .probe = create_iface_macio, - .remove = dispose_iface_macio -}; - -static struct of_platform_driver i2c_keywest_of_platform_driver = -{ - .owner = THIS_MODULE, - .name = "i2c-keywest", - .match_table = i2c_keywest_match, - .probe = create_iface_of_platform, - .remove = dispose_iface_of_platform -}; - -static int __init -i2c_keywest_init(void) -{ - of_register_driver(&i2c_keywest_of_platform_driver); - macio_register_driver(&i2c_keywest_macio_driver); - - return 0; -} - -static void __exit -i2c_keywest_cleanup(void) -{ - of_unregister_driver(&i2c_keywest_of_platform_driver); - macio_unregister_driver(&i2c_keywest_macio_driver); -} - -module_init(i2c_keywest_init); -module_exit(i2c_keywest_cleanup); diff --git a/drivers/i2c/busses/i2c-keywest.h b/drivers/i2c/busses/i2c-keywest.h deleted file mode 100644 index c5022e1ca6ff19..00000000000000 --- a/drivers/i2c/busses/i2c-keywest.h +++ /dev/null @@ -1,108 +0,0 @@ -#ifndef __I2C_KEYWEST_H__ -#define __I2C_KEYWEST_H__ - -/* The Tumbler audio equalizer can be really slow sometimes */ -#define POLL_TIMEOUT (2*HZ) - -/* Register indices */ -typedef enum { - reg_mode = 0, - reg_control, - reg_status, - reg_isr, - reg_ier, - reg_addr, - reg_subaddr, - reg_data -} reg_t; - - -/* Mode register */ -#define KW_I2C_MODE_100KHZ 0x00 -#define KW_I2C_MODE_50KHZ 0x01 -#define KW_I2C_MODE_25KHZ 0x02 -#define KW_I2C_MODE_DUMB 0x00 -#define KW_I2C_MODE_STANDARD 0x04 -#define KW_I2C_MODE_STANDARDSUB 0x08 -#define KW_I2C_MODE_COMBINED 0x0C -#define KW_I2C_MODE_MODE_MASK 0x0C -#define KW_I2C_MODE_CHAN_MASK 0xF0 - -/* Control register */ -#define KW_I2C_CTL_AAK 0x01 -#define KW_I2C_CTL_XADDR 0x02 -#define KW_I2C_CTL_STOP 0x04 -#define KW_I2C_CTL_START 0x08 - -/* Status register */ -#define KW_I2C_STAT_BUSY 0x01 -#define KW_I2C_STAT_LAST_AAK 0x02 -#define KW_I2C_STAT_LAST_RW 0x04 -#define KW_I2C_STAT_SDA 0x08 -#define KW_I2C_STAT_SCL 0x10 - -/* IER & ISR registers */ -#define KW_I2C_IRQ_DATA 0x01 -#define KW_I2C_IRQ_ADDR 0x02 -#define KW_I2C_IRQ_STOP 0x04 -#define KW_I2C_IRQ_START 0x08 -#define KW_I2C_IRQ_MASK 0x0F - -/* Physical interface */ -struct keywest_iface -{ - struct device_node *node; - void __iomem * base; - unsigned bsteps; - int irq; - spinlock_t lock; - struct keywest_chan *channels; - unsigned chan_count; - u8 cur_mode; - char read_write; - u8 *data; - unsigned datalen; - int state; - int result; - struct timer_list timeout_timer; - struct completion complete; -}; - -enum { - state_idle, - state_addr, - state_read, - state_write, - state_stop, - state_dead -}; - -/* Channel on an interface */ -struct keywest_chan -{ - struct i2c_adapter adapter; - struct keywest_iface* iface; - unsigned chan_no; -}; - -/* Register access */ - -static inline u8 __read_reg(struct keywest_iface *iface, reg_t reg) -{ - return in_8(iface->base - + (((unsigned)reg) << iface->bsteps)); -} - -static inline void __write_reg(struct keywest_iface *iface, reg_t reg, u8 val) -{ - out_8(iface->base - + (((unsigned)reg) << iface->bsteps), val); - (void)__read_reg(iface, reg_subaddr); -} - -#define write_reg(reg, val) __write_reg(iface, reg, val) -#define read_reg(reg) __read_reg(iface, reg) - - - -#endif /* __I2C_KEYWEST_H__ */ diff --git a/drivers/i2c/busses/i2c-pmac-smu.c b/drivers/i2c/busses/i2c-pmac-smu.c deleted file mode 100644 index 7d925be3fd4b02..00000000000000 --- a/drivers/i2c/busses/i2c-pmac-smu.c +++ /dev/null @@ -1,324 +0,0 @@ -/* - i2c Support for Apple SMU Controller - - Copyright (c) 2005 Benjamin Herrenschmidt, IBM Corp. - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int probe; - -MODULE_AUTHOR("Benjamin Herrenschmidt "); -MODULE_DESCRIPTION("I2C driver for Apple's SMU"); -MODULE_LICENSE("GPL"); -module_param(probe, bool, 0); - - -/* Physical interface */ -struct smu_iface -{ - struct i2c_adapter adapter; - struct completion complete; - u32 busid; -}; - -static void smu_i2c_done(struct smu_i2c_cmd *cmd, void *misc) -{ - struct smu_iface *iface = misc; - complete(&iface->complete); -} - -/* - * SMBUS-type transfer entrypoint - */ -static s32 smu_smbus_xfer( struct i2c_adapter* adap, - u16 addr, - unsigned short flags, - char read_write, - u8 command, - int size, - union i2c_smbus_data* data) -{ - struct smu_iface *iface = i2c_get_adapdata(adap); - struct smu_i2c_cmd cmd; - int rc = 0; - int read = (read_write == I2C_SMBUS_READ); - - cmd.info.bus = iface->busid; - cmd.info.devaddr = (addr << 1) | (read ? 0x01 : 0x00); - - /* Prepare datas & select mode */ - switch (size) { - case I2C_SMBUS_QUICK: - cmd.info.type = SMU_I2C_TRANSFER_SIMPLE; - cmd.info.datalen = 0; - break; - case I2C_SMBUS_BYTE: - cmd.info.type = SMU_I2C_TRANSFER_SIMPLE; - cmd.info.datalen = 1; - if (!read) - cmd.info.data[0] = data->byte; - break; - case I2C_SMBUS_BYTE_DATA: - cmd.info.type = SMU_I2C_TRANSFER_STDSUB; - cmd.info.datalen = 1; - cmd.info.sublen = 1; - cmd.info.subaddr[0] = command; - cmd.info.subaddr[1] = 0; - cmd.info.subaddr[2] = 0; - if (!read) - cmd.info.data[0] = data->byte; - break; - case I2C_SMBUS_WORD_DATA: - cmd.info.type = SMU_I2C_TRANSFER_STDSUB; - cmd.info.datalen = 2; - cmd.info.sublen = 1; - cmd.info.subaddr[0] = command; - cmd.info.subaddr[1] = 0; - cmd.info.subaddr[2] = 0; - if (!read) { - cmd.info.data[0] = data->word & 0xff; - cmd.info.data[1] = (data->word >> 8) & 0xff; - } - break; - /* Note that these are broken vs. the expected smbus API where - * on reads, the lenght is actually returned from the function, - * but I think the current API makes no sense and I don't want - * any driver that I haven't verified for correctness to go - * anywhere near a pmac i2c bus anyway ... - */ - case I2C_SMBUS_BLOCK_DATA: - cmd.info.type = SMU_I2C_TRANSFER_STDSUB; - cmd.info.datalen = data->block[0] + 1; - if (cmd.info.datalen > (SMU_I2C_WRITE_MAX + 1)) - return -EINVAL; - if (!read) - memcpy(cmd.info.data, data->block, cmd.info.datalen); - cmd.info.sublen = 1; - cmd.info.subaddr[0] = command; - cmd.info.subaddr[1] = 0; - cmd.info.subaddr[2] = 0; - break; - case I2C_SMBUS_I2C_BLOCK_DATA: - cmd.info.type = SMU_I2C_TRANSFER_STDSUB; - cmd.info.datalen = data->block[0]; - if (cmd.info.datalen > 7) - return -EINVAL; - if (!read) - memcpy(cmd.info.data, &data->block[1], - cmd.info.datalen); - cmd.info.sublen = 1; - cmd.info.subaddr[0] = command; - cmd.info.subaddr[1] = 0; - cmd.info.subaddr[2] = 0; - break; - - default: - return -EINVAL; - } - - /* Turn a standardsub read into a combined mode access */ - if (read_write == I2C_SMBUS_READ && - cmd.info.type == SMU_I2C_TRANSFER_STDSUB) - cmd.info.type = SMU_I2C_TRANSFER_COMBINED; - - /* Finish filling command and submit it */ - cmd.done = smu_i2c_done; - cmd.misc = iface; - rc = smu_queue_i2c(&cmd); - if (rc < 0) - return rc; - wait_for_completion(&iface->complete); - rc = cmd.status; - - if (!read || rc < 0) - return rc; - - switch (size) { - case I2C_SMBUS_BYTE: - case I2C_SMBUS_BYTE_DATA: - data->byte = cmd.info.data[0]; - break; - case I2C_SMBUS_WORD_DATA: - data->word = ((u16)cmd.info.data[1]) << 8; - data->word |= cmd.info.data[0]; - break; - /* Note that these are broken vs. the expected smbus API where - * on reads, the lenght is actually returned from the function, - * but I think the current API makes no sense and I don't want - * any driver that I haven't verified for correctness to go - * anywhere near a pmac i2c bus anyway ... - */ - case I2C_SMBUS_BLOCK_DATA: - case I2C_SMBUS_I2C_BLOCK_DATA: - memcpy(&data->block[0], cmd.info.data, cmd.info.datalen); - break; - } - - return rc; -} - -static u32 -smu_smbus_func(struct i2c_adapter * adapter) -{ - return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | - I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | - I2C_FUNC_SMBUS_BLOCK_DATA; -} - -/* For now, we only handle combined mode (smbus) */ -static struct i2c_algorithm smu_algorithm = { - .smbus_xfer = smu_smbus_xfer, - .functionality = smu_smbus_func, -}; - -static int create_iface(struct device_node *np, struct device *dev) -{ - struct smu_iface* iface; - u32 *reg, busid; - int rc; - - reg = (u32 *)get_property(np, "reg", NULL); - if (reg == NULL) { - printk(KERN_ERR "i2c-pmac-smu: can't find bus number !\n"); - return -ENXIO; - } - busid = *reg; - - iface = kzalloc(sizeof(struct smu_iface), GFP_KERNEL); - if (iface == NULL) { - printk(KERN_ERR "i2c-pmac-smu: can't allocate inteface !\n"); - return -ENOMEM; - } - init_completion(&iface->complete); - iface->busid = busid; - - dev_set_drvdata(dev, iface); - - sprintf(iface->adapter.name, "smu-i2c-%02x", busid); - iface->adapter.algo = &smu_algorithm; - iface->adapter.algo_data = NULL; - iface->adapter.client_register = NULL; - iface->adapter.client_unregister = NULL; - i2c_set_adapdata(&iface->adapter, iface); - iface->adapter.dev.parent = dev; - - rc = i2c_add_adapter(&iface->adapter); - if (rc) { - printk(KERN_ERR "i2c-pamc-smu.c: Adapter %s registration " - "failed\n", iface->adapter.name); - i2c_set_adapdata(&iface->adapter, NULL); - } - - if (probe) { - unsigned char addr; - printk("Probe: "); - for (addr = 0x00; addr <= 0x7f; addr++) { - if (i2c_smbus_xfer(&iface->adapter,addr, - 0,0,0,I2C_SMBUS_QUICK,NULL) >= 0) - printk("%02x ", addr); - } - printk("\n"); - } - - printk(KERN_INFO "SMU i2c bus %x registered\n", busid); - - return 0; -} - -static int dispose_iface(struct device *dev) -{ - struct smu_iface *iface = dev_get_drvdata(dev); - int rc; - - rc = i2c_del_adapter(&iface->adapter); - i2c_set_adapdata(&iface->adapter, NULL); - /* We aren't that prepared to deal with this... */ - if (rc) - printk("i2c-pmac-smu.c: Failed to remove bus %s !\n", - iface->adapter.name); - dev_set_drvdata(dev, NULL); - kfree(iface); - - return 0; -} - - -static int create_iface_of_platform(struct of_device* dev, - const struct of_device_id *match) -{ - struct device_node *node = dev->node; - - if (device_is_compatible(node, "smu-i2c") || - (node->parent != NULL && - device_is_compatible(node->parent, "smu-i2c-control"))) - return create_iface(node, &dev->dev); - return -ENODEV; -} - - -static int dispose_iface_of_platform(struct of_device* dev) -{ - return dispose_iface(&dev->dev); -} - - -static struct of_device_id i2c_smu_match[] = -{ - { - .compatible = "smu-i2c", - }, - { - .compatible = "i2c-bus", - }, - {}, -}; -static struct of_platform_driver i2c_smu_of_platform_driver = -{ - .name = "i2c-smu", - .match_table = i2c_smu_match, - .probe = create_iface_of_platform, - .remove = dispose_iface_of_platform -}; - - -static int __init i2c_pmac_smu_init(void) -{ - of_register_driver(&i2c_smu_of_platform_driver); - return 0; -} - - -static void __exit i2c_pmac_smu_cleanup(void) -{ - of_unregister_driver(&i2c_smu_of_platform_driver); -} - -module_init(i2c_pmac_smu_init); -module_exit(i2c_pmac_smu_cleanup); diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c new file mode 100644 index 00000000000000..df786eb55295a4 --- /dev/null +++ b/drivers/i2c/busses/i2c-powermac.c @@ -0,0 +1,290 @@ +/* + i2c Support for Apple SMU Controller + + Copyright (c) 2005 Benjamin Herrenschmidt, IBM Corp. + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Benjamin Herrenschmidt "); +MODULE_DESCRIPTION("I2C driver for Apple PowerMac"); +MODULE_LICENSE("GPL"); + +/* + * SMBUS-type transfer entrypoint + */ +static s32 i2c_powermac_smbus_xfer( struct i2c_adapter* adap, + u16 addr, + unsigned short flags, + char read_write, + u8 command, + int size, + union i2c_smbus_data* data) +{ + struct pmac_i2c_bus *bus = i2c_get_adapdata(adap); + int rc = 0; + int read = (read_write == I2C_SMBUS_READ); + int addrdir = (addr << 1) | read; + u8 local[2]; + + rc = pmac_i2c_open(bus, 0); + if (rc) + return rc; + + switch (size) { + case I2C_SMBUS_QUICK: + rc = pmac_i2c_setmode(bus, pmac_i2c_mode_std); + if (rc) + goto bail; + rc = pmac_i2c_xfer(bus, addrdir, 0, 0, NULL, 0); + break; + case I2C_SMBUS_BYTE: + rc = pmac_i2c_setmode(bus, pmac_i2c_mode_std); + if (rc) + goto bail; + rc = pmac_i2c_xfer(bus, addrdir, 0, 0, &data->byte, 1); + break; + case I2C_SMBUS_BYTE_DATA: + rc = pmac_i2c_setmode(bus, read ? + pmac_i2c_mode_combined : + pmac_i2c_mode_stdsub); + if (rc) + goto bail; + rc = pmac_i2c_xfer(bus, addrdir, 1, command, &data->byte, 1); + break; + case I2C_SMBUS_WORD_DATA: + rc = pmac_i2c_setmode(bus, read ? + pmac_i2c_mode_combined : + pmac_i2c_mode_stdsub); + if (rc) + goto bail; + if (!read) { + local[0] = data->word & 0xff; + local[1] = (data->word >> 8) & 0xff; + } + rc = pmac_i2c_xfer(bus, addrdir, 1, command, local, 2); + if (rc == 0 && read) { + data->word = ((u16)local[1]) << 8; + data->word |= local[0]; + } + break; + + /* Note that these are broken vs. the expected smbus API where + * on reads, the lenght is actually returned from the function, + * but I think the current API makes no sense and I don't want + * any driver that I haven't verified for correctness to go + * anywhere near a pmac i2c bus anyway ... + * + * I'm also not completely sure what kind of phases to do between + * the actual command and the data (what I am _supposed_ to do that + * is). For now, I assume writes are a single stream and reads have + * a repeat start/addr phase (but not stop in between) + */ + case I2C_SMBUS_BLOCK_DATA: + rc = pmac_i2c_setmode(bus, read ? + pmac_i2c_mode_combined : + pmac_i2c_mode_stdsub); + if (rc) + goto bail; + rc = pmac_i2c_xfer(bus, addrdir, 1, command, data->block, + data->block[0] + 1); + + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + rc = pmac_i2c_setmode(bus, read ? + pmac_i2c_mode_combined : + pmac_i2c_mode_stdsub); + if (rc) + goto bail; + rc = pmac_i2c_xfer(bus, addrdir, 1, command, + read ? data->block : &data->block[1], + data->block[0]); + break; + + default: + rc = -EINVAL; + } + bail: + pmac_i2c_close(bus); + return rc; +} + +/* + * Generic i2c master transfer entrypoint. This driver only support single + * messages (for "lame i2c" transfers). Anything else should use the smbus + * entry point + */ +static int i2c_powermac_master_xfer( struct i2c_adapter *adap, + struct i2c_msg *msgs, + int num) +{ + struct pmac_i2c_bus *bus = i2c_get_adapdata(adap); + int rc = 0; + int read; + int addrdir; + + if (num != 1) + return -EINVAL; + if (msgs->flags & I2C_M_TEN) + return -EINVAL; + read = (msgs->flags & I2C_M_RD) != 0; + addrdir = (msgs->addr << 1) | read; + if (msgs->flags & I2C_M_REV_DIR_ADDR) + addrdir ^= 1; + + rc = pmac_i2c_open(bus, 0); + if (rc) + return rc; + rc = pmac_i2c_setmode(bus, pmac_i2c_mode_std); + if (rc) + goto bail; + rc = pmac_i2c_xfer(bus, addrdir, 0, 0, msgs->buf, msgs->len); + bail: + pmac_i2c_close(bus); + return rc < 0 ? rc : msgs->len; +} + +static u32 i2c_powermac_func(struct i2c_adapter * adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_I2C; +} + +/* For now, we only handle smbus */ +static struct i2c_algorithm i2c_powermac_algorithm = { + .smbus_xfer = i2c_powermac_smbus_xfer, + .master_xfer = i2c_powermac_master_xfer, + .functionality = i2c_powermac_func, +}; + + +static int i2c_powermac_remove(struct device *dev) +{ + struct i2c_adapter *adapter = dev_get_drvdata(dev); + struct pmac_i2c_bus *bus = i2c_get_adapdata(adapter); + int rc; + + rc = i2c_del_adapter(adapter); + pmac_i2c_detach_adapter(bus, adapter); + i2c_set_adapdata(adapter, NULL); + /* We aren't that prepared to deal with this... */ + if (rc) + printk("i2c-powermac.c: Failed to remove bus %s !\n", + adapter->name); + dev_set_drvdata(dev, NULL); + kfree(adapter); + + return 0; +} + + +static int i2c_powermac_probe(struct device *dev) +{ + struct pmac_i2c_bus *bus = dev->platform_data; + struct device_node *parent = NULL; + struct i2c_adapter *adapter; + char name[32], *basename; + int rc; + + if (bus == NULL) + return -EINVAL; + + /* Ok, now we need to make up a name for the interface that will + * match what we used to do in the past, that is basically the + * controller's parent device node for keywest. PMU didn't have a + * naming convention and SMU has a different one + */ + switch(pmac_i2c_get_type(bus)) { + case pmac_i2c_bus_keywest: + parent = of_get_parent(pmac_i2c_get_controller(bus)); + if (parent == NULL) + return -EINVAL; + basename = parent->name; + break; + case pmac_i2c_bus_pmu: + basename = "pmu"; + break; + case pmac_i2c_bus_smu: + /* This is not what we used to do but I'm fixing drivers at + * the same time as this change + */ + basename = "smu"; + break; + default: + return -EINVAL; + } + snprintf(name, 32, "%s %d", basename, pmac_i2c_get_channel(bus)); + of_node_put(parent); + + adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL); + if (adapter == NULL) { + printk(KERN_ERR "i2c-powermac: can't allocate inteface !\n"); + return -ENOMEM; + } + dev_set_drvdata(dev, adapter); + strcpy(adapter->name, name); + adapter->algo = &i2c_powermac_algorithm; + i2c_set_adapdata(adapter, bus); + adapter->dev.parent = dev; + pmac_i2c_attach_adapter(bus, adapter); + rc = i2c_add_adapter(adapter); + if (rc) { + printk(KERN_ERR "i2c-powermac: Adapter %s registration " + "failed\n", name); + i2c_set_adapdata(adapter, NULL); + pmac_i2c_detach_adapter(bus, adapter); + } + + printk(KERN_INFO "PowerMac i2c bus %s registered\n", name); + return rc; +} + + +static struct device_driver i2c_powermac_driver = { + .name = "i2c-powermac", + .bus = &platform_bus_type, + .probe = i2c_powermac_probe, + .remove = i2c_powermac_remove, +}; + +static int __init i2c_powermac_init(void) +{ + driver_register(&i2c_powermac_driver); + return 0; +} + + +static void __exit i2c_powermac_cleanup(void) +{ + driver_unregister(&i2c_powermac_driver); +} + +module_init(i2c_powermac_init); +module_exit(i2c_powermac_cleanup); diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig index a0ea44c3e8b10e..7d4a0ac28c065c 100644 --- a/drivers/macintosh/Kconfig +++ b/drivers/macintosh/Kconfig @@ -149,14 +149,14 @@ config MAC_EMUMOUSEBTN config THERM_WINDTUNNEL tristate "Support for thermal management on Windtunnel G4s" - depends on I2C && I2C_KEYWEST && PPC_PMAC && !PPC_PMAC64 + depends on I2C && I2C_POWERMAC && PPC_PMAC && !PPC_PMAC64 help This driver provides some thermostat and fan control for the desktop G4 "Windtunnel" config THERM_ADT746X tristate "Support for thermal mgmnt on laptops with ADT 746x chipset" - depends on I2C && I2C_KEYWEST && PPC_PMAC && !PPC_PMAC64 + depends on I2C && I2C_POWERMAC && PPC_PMAC && !PPC_PMAC64 help This driver provides some thermostat and fan control for the iBook G4, and the ATI based aluminium PowerBooks, allowing slighlty @@ -164,7 +164,7 @@ config THERM_ADT746X config THERM_PM72 tristate "Support for thermal management on PowerMac G5" - depends on I2C && I2C_KEYWEST && PPC_PMAC64 + depends on I2C && I2C_POWERMAC && PPC_PMAC64 help This driver provides thermostat and fan control for the desktop G5 machines. @@ -175,14 +175,14 @@ config WINDFARM config WINDFARM_PM81 tristate "Support for thermal management on iMac G5" depends on WINDFARM && I2C && CPU_FREQ_PMAC64 && PMAC_SMU - select I2C_PMAC_SMU + select I2C_POWERMAC help This driver provides thermal control for the iMacG5 config WINDFARM_PM91 tristate "Support for thermal management on PowerMac9,1" depends on WINDFARM && I2C && CPU_FREQ_PMAC64 && PMAC_SMU - select I2C_PMAC_SMU + select I2C_POWERMAC help This driver provides thermal control for the PowerMac9,1 which is the recent (SMU based) single CPU desktop G5 diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c index 9ecd76849e35bf..db2ae71d07ef08 100644 --- a/drivers/macintosh/smu.c +++ b/drivers/macintosh/smu.c @@ -584,34 +584,14 @@ core_initcall(smu_late_init); * sysfs visibility */ -static void smu_create_i2c(struct device_node *np) -{ - char name[32]; - u32 *reg = (u32 *)get_property(np, "reg", NULL); - - if (reg != NULL) { - sprintf(name, "smu-i2c-%02x", *reg); - of_platform_device_create(np, name, &smu->of_dev->dev); - } -} - static void smu_expose_childs(void *unused) { - struct device_node *np, *gp; - - for (np = NULL; (np = of_get_next_child(smu->of_node, np)) != NULL;) { - if (device_is_compatible(np, "smu-i2c-control")) { - gp = NULL; - while ((gp = of_get_next_child(np, gp)) != NULL) - if (device_is_compatible(gp, "i2c-bus")) - smu_create_i2c(gp); - } else if (device_is_compatible(np, "smu-i2c")) - smu_create_i2c(np); + struct device_node *np; + + for (np = NULL; (np = of_get_next_child(smu->of_node, np)) != NULL;) if (device_is_compatible(np, "smu-sensors")) of_platform_device_create(np, "smu-sensors", &smu->of_dev->dev); - } - } static DECLARE_WORK(smu_expose_childs_work, smu_expose_childs, NULL); diff --git a/drivers/macintosh/windfarm_lm75_sensor.c b/drivers/macintosh/windfarm_lm75_sensor.c index 57460e46c89f21..906d3ecae6e69e 100644 --- a/drivers/macintosh/windfarm_lm75_sensor.c +++ b/drivers/macintosh/windfarm_lm75_sensor.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "windfarm.h" @@ -157,53 +158,21 @@ static struct wf_lm75_sensor *wf_lm75_create(struct i2c_adapter *adapter, static int wf_lm75_attach(struct i2c_adapter *adapter) { - u8 bus_id; - struct device_node *smu, *bus, *dev; - - /* We currently only deal with LM75's hanging off the SMU - * i2c busses. If we extend that driver to other/older - * machines, we should split this function into SMU-i2c, - * keywest-i2c, PMU-i2c, ... - */ + struct device_node *busnode, *dev; + struct pmac_i2c_bus *bus; DBG("wf_lm75: adapter %s detected\n", adapter->name); - if (strncmp(adapter->name, "smu-i2c-", 8) != 0) - return 0; - smu = of_find_node_by_type(NULL, "smu"); - if (smu == NULL) - return 0; - - /* Look for the bus in the device-tree */ - bus_id = (u8)simple_strtoul(adapter->name + 8, NULL, 16); - - DBG("wf_lm75: bus ID is %x\n", bus_id); - - /* Look for sensors subdir */ - for (bus = NULL; - (bus = of_get_next_child(smu, bus)) != NULL;) { - u32 *reg; - - if (strcmp(bus->name, "i2c")) - continue; - reg = (u32 *)get_property(bus, "reg", NULL); - if (reg == NULL) - continue; - if (bus_id == *reg) - break; - } - of_node_put(smu); - if (bus == NULL) { - printk(KERN_WARNING "windfarm: SMU i2c bus 0x%x not found" - " in device-tree !\n", bus_id); - return 0; - } + bus = pmac_i2c_adapter_to_bus(adapter); + if (bus == NULL) + return -ENODEV; + busnode = pmac_i2c_get_bus_node(bus); DBG("wf_lm75: bus found, looking for device...\n"); /* Now look for lm75(s) in there */ for (dev = NULL; - (dev = of_get_next_child(bus, dev)) != NULL;) { + (dev = of_get_next_child(busnode, dev)) != NULL;) { const char *loc = get_property(dev, "hwsensor-location", NULL); u32 *reg = (u32 *)get_property(dev, "reg", NULL); @@ -217,9 +186,6 @@ static int wf_lm75_attach(struct i2c_adapter *adapter) else if (device_is_compatible(dev, "ds1775")) wf_lm75_create(adapter, *reg, 1, loc); } - - of_node_put(bus); - return 0; } diff --git a/include/asm-powerpc/pmac_low_i2c.h b/include/asm-powerpc/pmac_low_i2c.h index adf4fa956572a7..480018f41e1a6f 100644 --- a/include/asm-powerpc/pmac_low_i2c.h +++ b/include/asm-powerpc/pmac_low_i2c.h @@ -70,6 +70,7 @@ extern struct device_node *pmac_i2c_get_controller(struct pmac_i2c_bus *bus); extern struct device_node *pmac_i2c_get_bus_node(struct pmac_i2c_bus *bus); extern int pmac_i2c_get_type(struct pmac_i2c_bus *bus); extern int pmac_i2c_get_flags(struct pmac_i2c_bus *bus); +extern int pmac_i2c_get_channel(struct pmac_i2c_bus *bus); /* i2c layer adapter attach/detach */ extern void pmac_i2c_attach_adapter(struct pmac_i2c_bus *bus, @@ -77,6 +78,7 @@ extern void pmac_i2c_attach_adapter(struct pmac_i2c_bus *bus, extern void pmac_i2c_detach_adapter(struct pmac_i2c_bus *bus, struct i2c_adapter *adapter); extern struct i2c_adapter *pmac_i2c_get_adapter(struct pmac_i2c_bus *bus); +extern struct pmac_i2c_bus *pmac_i2c_adapter_to_bus(struct i2c_adapter *adapter); /* March a device or bus with an i2c adapter structure, to be used by drivers * to match device-tree nodes with i2c adapters during adapter discovery -- cgit 1.2.3-korg From 5b9ca526917b7bc7d1da3beaccb2251a8f0b5fe2 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Sat, 7 Jan 2006 11:41:02 +1100 Subject: [PATCH] 3/5 powerpc: Add platform functions interpreter This is the platform function interpreter itself along with the backends for UniN/U3/U4, mac-io, GPIOs and i2c. It adds the ability to execute those do-platform-* scripts in the device-tree (at least for most devices for which a backend is provided). This should replace the clock spreading hacks properly. It might also have an impact on all sort of machines since some of the scripts marked "at init" will now be executed on boot (or some other on sleep/wakeup), those will possibly do things that the kernel didn't do at all, like setting some values into some i2c devices (changing thermal sensor calibration or conversion rate) etc... Thus regression testing is MUCH welcome. Also loook for errors in dmesg. That's also why I've left rather verbose debugging enabled in this version of the patch. (I do expect some Windtunnel G4s to show some errors as they have an i2c clock chip on the PMU bus that uses some primitives that the i2c backend doesn't implement yet. I really need users that have one of those machine to come back to me so we can get that done right, though the errors themselves should be harmless, I suspect the machine might not run at full speed). Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/powermac/Makefile | 3 +- arch/powerpc/platforms/powermac/feature.c | 46 +- arch/powerpc/platforms/powermac/low_i2c.c | 294 +++++++- arch/powerpc/platforms/powermac/pfunc_base.c | 405 +++++++++++ arch/powerpc/platforms/powermac/pfunc_core.c | 989 +++++++++++++++++++++++++++ arch/powerpc/platforms/powermac/smp.c | 48 +- drivers/macintosh/via-pmu.c | 10 + include/asm-powerpc/pmac_feature.h | 19 + include/asm-powerpc/pmac_low_i2c.h | 3 + include/asm-powerpc/pmac_pfunc.h | 253 +++++++ 10 files changed, 2028 insertions(+), 42 deletions(-) create mode 100644 arch/powerpc/platforms/powermac/pfunc_base.c create mode 100644 arch/powerpc/platforms/powermac/pfunc_core.c create mode 100644 include/asm-powerpc/pmac_pfunc.h (limited to 'drivers') diff --git a/arch/powerpc/platforms/powermac/Makefile b/arch/powerpc/platforms/powermac/Makefile index faa1a2c82bcfc3..78093d7f97af9a 100644 --- a/arch/powerpc/platforms/powermac/Makefile +++ b/arch/powerpc/platforms/powermac/Makefile @@ -1,7 +1,8 @@ CFLAGS_bootx_init.o += -fPIC obj-y += pic.o setup.o time.o feature.o pci.o \ - sleep.o low_i2c.o cache.o + sleep.o low_i2c.o cache.o pfunc_core.o \ + pfunc_base.o obj-$(CONFIG_PMAC_BACKLIGHT) += backlight.o obj-$(CONFIG_CPU_FREQ_PMAC) += cpufreq_32.o obj-$(CONFIG_CPU_FREQ_PMAC64) += cpufreq_64.o diff --git a/arch/powerpc/platforms/powermac/feature.c b/arch/powerpc/platforms/powermac/feature.c index b271b11583acf4..558dd06920921f 100644 --- a/arch/powerpc/platforms/powermac/feature.c +++ b/arch/powerpc/platforms/powermac/feature.c @@ -58,12 +58,11 @@ extern int powersave_lowspeed; extern int powersave_nap; extern struct device_node *k2_skiplist[2]; - /* * We use a single global lock to protect accesses. Each driver has * to take care of its own locking */ -static DEFINE_SPINLOCK(feature_lock); +DEFINE_SPINLOCK(feature_lock); #define LOCK(flags) spin_lock_irqsave(&feature_lock, flags); #define UNLOCK(flags) spin_unlock_irqrestore(&feature_lock, flags); @@ -106,22 +105,12 @@ static const char *macio_names[] = }; +struct device_node *uninorth_node; +u32 __iomem *uninorth_base; -/* - * Uninorth reg. access. Note that Uni-N regs are big endian - */ - -#define UN_REG(r) (uninorth_base + ((r) >> 2)) -#define UN_IN(r) (in_be32(UN_REG(r))) -#define UN_OUT(r,v) (out_be32(UN_REG(r), (v))) -#define UN_BIS(r,v) (UN_OUT((r), UN_IN(r) | (v))) -#define UN_BIC(r,v) (UN_OUT((r), UN_IN(r) & ~(v))) - -static struct device_node *uninorth_node; -static u32 __iomem *uninorth_base; static u32 uninorth_rev; static int uninorth_maj; -static void __iomem *u3_ht; +static void __iomem *u3_ht_base; /* * For each motherboard family, we have a table of functions pointers @@ -1560,8 +1549,10 @@ void g5_phy_disable_cpu1(void) #ifndef CONFIG_POWER4 -static void -keylargo_shutdown(struct macio_chip *macio, int sleep_mode) + +#ifdef CONFIG_PM + +static void keylargo_shutdown(struct macio_chip *macio, int sleep_mode) { u32 temp; @@ -1614,8 +1605,7 @@ keylargo_shutdown(struct macio_chip *macio, int sleep_mode) (void)MACIO_IN32(KEYLARGO_FCR0); mdelay(1); } -static void -pangea_shutdown(struct macio_chip *macio, int sleep_mode) +static void pangea_shutdown(struct macio_chip *macio, int sleep_mode) { u32 temp; @@ -1648,8 +1638,7 @@ pangea_shutdown(struct macio_chip *macio, int sleep_mode) (void)MACIO_IN32(KEYLARGO_FCR0); mdelay(1); } -static void -intrepid_shutdown(struct macio_chip *macio, int sleep_mode) +static void intrepid_shutdown(struct macio_chip *macio, int sleep_mode) { u32 temp; @@ -1833,6 +1822,8 @@ core99_wake_up(void) return 0; } +#endif /* CONFIG_PM */ + static long core99_sleep_state(struct device_node *node, long param, long value) { @@ -1854,10 +1845,13 @@ core99_sleep_state(struct device_node *node, long param, long value) if ((pmac_mb.board_flags & PMAC_MB_CAN_SLEEP) == 0) return -EPERM; +#ifdef CONFIG_PM if (value == 1) return core99_sleep(); else if (value == 0) return core99_wake_up(); + +#endif /* CONFIG_PM */ return 0; } @@ -1981,7 +1975,9 @@ static struct feature_table_entry core99_features[] = { { PMAC_FTR_USB_ENABLE, core99_usb_enable }, { PMAC_FTR_1394_ENABLE, core99_firewire_enable }, { PMAC_FTR_1394_CABLE_POWER, core99_firewire_cable_power }, +#ifdef CONFIG_PM { PMAC_FTR_SLEEP_STATE, core99_sleep_state }, +#endif #ifdef CONFIG_SMP { PMAC_FTR_RESET_CPU, core99_reset_cpu }, #endif /* CONFIG_SMP */ @@ -2572,7 +2568,7 @@ static void __init probe_uninorth(void) uninorth_base = ioremap(address, 0x40000); uninorth_rev = in_be32(UN_REG(UNI_N_VERSION)); if (uninorth_maj == 3 || uninorth_maj == 4) - u3_ht = ioremap(address + U3_HT_CONFIG_BASE, 0x1000); + u3_ht_base = ioremap(address + U3_HT_CONFIG_BASE, 0x1000); printk(KERN_INFO "Found %s memory controller & host bridge" " @ 0x%08x revision: 0x%02x\n", uninorth_maj == 3 ? "U3" : @@ -2921,9 +2917,9 @@ void __init pmac_check_ht_link(void) u8 px_bus, px_devfn; struct pci_controller *px_hose; - (void)in_be32(u3_ht + U3_HT_LINK_COMMAND); - ucfg = cfg = in_be32(u3_ht + U3_HT_LINK_CONFIG); - ufreq = freq = in_be32(u3_ht + U3_HT_LINK_FREQ); + (void)in_be32(u3_ht_base + U3_HT_LINK_COMMAND); + ucfg = cfg = in_be32(u3_ht_base + U3_HT_LINK_CONFIG); + ufreq = freq = in_be32(u3_ht_base + U3_HT_LINK_FREQ); dump_HT_speeds("U3 HyperTransport", cfg, freq); pcix_node = of_find_compatible_node(NULL, "pci", "pci-x"); diff --git a/arch/powerpc/platforms/powermac/low_i2c.c b/arch/powerpc/platforms/powermac/low_i2c.c index a25e447f907f61..535c802b369fbe 100644 --- a/arch/powerpc/platforms/powermac/low_i2c.c +++ b/arch/powerpc/platforms/powermac/low_i2c.c @@ -49,6 +49,7 @@ #include #include #include +#include #include #ifdef DEBUG @@ -1162,9 +1163,291 @@ int pmac_i2c_xfer(struct pmac_i2c_bus *bus, u8 addrdir, int subsize, } EXPORT_SYMBOL_GPL(pmac_i2c_xfer); +/* some quirks for platform function decoding */ +enum { + pmac_i2c_quirk_invmask = 0x00000001u, +}; + +static void pmac_i2c_devscan(void (*callback)(struct device_node *dev, + int quirks)) +{ + struct pmac_i2c_bus *bus; + struct device_node *np; + static struct whitelist_ent { + char *name; + char *compatible; + int quirks; + } whitelist[] = { + /* XXX Study device-tree's & apple drivers are get the quirks + * right ! + */ + { "i2c-hwclock", NULL, pmac_i2c_quirk_invmask }, + { "i2c-cpu-voltage", NULL, 0}, + { "temp-monitor", NULL, 0 }, + { "supply-monitor", NULL, 0 }, + { NULL, NULL, 0 }, + }; + + /* Only some devices need to have platform functions instanciated + * here. For now, we have a table. Others, like 9554 i2c GPIOs used + * on Xserve, if we ever do a driver for them, will use their own + * platform function instance + */ + list_for_each_entry(bus, &pmac_i2c_busses, link) { + for (np = NULL; + (np = of_get_next_child(bus->busnode, np)) != NULL;) { + struct whitelist_ent *p; + /* If multibus, check if device is on that bus */ + if (bus->flags & pmac_i2c_multibus) + if (bus != pmac_i2c_find_bus(np)) + continue; + for (p = whitelist; p->name != NULL; p++) { + if (strcmp(np->name, p->name)) + continue; + if (p->compatible && + !device_is_compatible(np, p->compatible)) + continue; + callback(np, p->quirks); + break; + } + } + } +} + +#define MAX_I2C_DATA 64 + +struct pmac_i2c_pf_inst +{ + struct pmac_i2c_bus *bus; + u8 addr; + u8 buffer[MAX_I2C_DATA]; + u8 scratch[MAX_I2C_DATA]; + int bytes; + int quirks; +}; + +static void* pmac_i2c_do_begin(struct pmf_function *func, struct pmf_args *args) +{ + struct pmac_i2c_pf_inst *inst; + struct pmac_i2c_bus *bus; + + bus = pmac_i2c_find_bus(func->node); + if (bus == NULL) { + printk(KERN_ERR "low_i2c: Can't find bus for %s (pfunc)\n", + func->node->full_name); + return NULL; + } + if (pmac_i2c_open(bus, 0)) { + printk(KERN_ERR "low_i2c: Can't open i2c bus for %s (pfunc)\n", + func->node->full_name); + return NULL; + } + + /* XXX might need GFP_ATOMIC when called during the suspend process, + * but then, there are already lots of issues with suspending when + * near OOM that need to be resolved, the allocator itself should + * probably make GFP_NOIO implicit during suspend + */ + inst = kzalloc(sizeof(struct pmac_i2c_pf_inst), GFP_KERNEL); + if (inst == NULL) { + pmac_i2c_close(bus); + return NULL; + } + inst->bus = bus; + inst->addr = pmac_i2c_get_dev_addr(func->node); + inst->quirks = (int)(long)func->driver_data; + return inst; +} + +static void pmac_i2c_do_end(struct pmf_function *func, void *instdata) +{ + struct pmac_i2c_pf_inst *inst = instdata; + + if (inst == NULL) + return; + pmac_i2c_close(inst->bus); + if (inst) + kfree(inst); +} + +static int pmac_i2c_do_read(PMF_STD_ARGS, u32 len) +{ + struct pmac_i2c_pf_inst *inst = instdata; + + inst->bytes = len; + return pmac_i2c_xfer(inst->bus, inst->addr | pmac_i2c_read, 0, 0, + inst->buffer, len); +} + +static int pmac_i2c_do_write(PMF_STD_ARGS, u32 len, const u8 *data) +{ + struct pmac_i2c_pf_inst *inst = instdata; + + return pmac_i2c_xfer(inst->bus, inst->addr | pmac_i2c_write, 0, 0, + (u8 *)data, len); +} + +/* This function is used to do the masking & OR'ing for the "rmw" type + * callbacks. Ze should apply the mask and OR in the values in the + * buffer before writing back. The problem is that it seems that + * various darwin drivers implement the mask/or differently, thus + * we need to check the quirks first + */ +static void pmac_i2c_do_apply_rmw(struct pmac_i2c_pf_inst *inst, + u32 len, const u8 *mask, const u8 *val) +{ + int i; + + if (inst->quirks & pmac_i2c_quirk_invmask) { + for (i = 0; i < len; i ++) + inst->scratch[i] = (inst->buffer[i] & mask[i]) | val[i]; + } else { + for (i = 0; i < len; i ++) + inst->scratch[i] = (inst->buffer[i] & ~mask[i]) + | (val[i] & mask[i]); + } +} + +static int pmac_i2c_do_rmw(PMF_STD_ARGS, u32 masklen, u32 valuelen, + u32 totallen, const u8 *maskdata, + const u8 *valuedata) +{ + struct pmac_i2c_pf_inst *inst = instdata; + + if (masklen > inst->bytes || valuelen > inst->bytes || + totallen > inst->bytes || valuelen > masklen) + return -EINVAL; + + pmac_i2c_do_apply_rmw(inst, masklen, maskdata, valuedata); + + return pmac_i2c_xfer(inst->bus, inst->addr | pmac_i2c_write, 0, 0, + inst->scratch, totallen); +} + +static int pmac_i2c_do_read_sub(PMF_STD_ARGS, u8 subaddr, u32 len) +{ + struct pmac_i2c_pf_inst *inst = instdata; + + inst->bytes = len; + return pmac_i2c_xfer(inst->bus, inst->addr | pmac_i2c_read, 1, subaddr, + inst->buffer, len); +} + +static int pmac_i2c_do_write_sub(PMF_STD_ARGS, u8 subaddr, u32 len, + const u8 *data) +{ + struct pmac_i2c_pf_inst *inst = instdata; + + return pmac_i2c_xfer(inst->bus, inst->addr | pmac_i2c_write, 1, + subaddr, (u8 *)data, len); +} + +static int pmac_i2c_do_set_mode(PMF_STD_ARGS, int mode) +{ + struct pmac_i2c_pf_inst *inst = instdata; + + return pmac_i2c_setmode(inst->bus, mode); +} + +static int pmac_i2c_do_rmw_sub(PMF_STD_ARGS, u8 subaddr, u32 masklen, + u32 valuelen, u32 totallen, const u8 *maskdata, + const u8 *valuedata) +{ + struct pmac_i2c_pf_inst *inst = instdata; + + if (masklen > inst->bytes || valuelen > inst->bytes || + totallen > inst->bytes || valuelen > masklen) + return -EINVAL; + + pmac_i2c_do_apply_rmw(inst, masklen, maskdata, valuedata); + + return pmac_i2c_xfer(inst->bus, inst->addr | pmac_i2c_write, 1, + subaddr, inst->scratch, totallen); +} + +static int pmac_i2c_do_mask_and_comp(PMF_STD_ARGS, u32 len, + const u8 *maskdata, + const u8 *valuedata) +{ + struct pmac_i2c_pf_inst *inst = instdata; + int i, match; + + /* Get return value pointer, it's assumed to be a u32 */ + if (!args || !args->count || !args->u[0].p) + return -EINVAL; + + /* Check buffer */ + if (len > inst->bytes) + return -EINVAL; + + for (i = 0, match = 1; match && i < len; i ++) + if ((inst->buffer[i] & maskdata[i]) != valuedata[i]) + match = 0; + *args->u[0].p = match; + return 0; +} + +static int pmac_i2c_do_delay(PMF_STD_ARGS, u32 duration) +{ + msleep((duration + 999) / 1000); + return 0; +} + + +static struct pmf_handlers pmac_i2c_pfunc_handlers = { + .begin = pmac_i2c_do_begin, + .end = pmac_i2c_do_end, + .read_i2c = pmac_i2c_do_read, + .write_i2c = pmac_i2c_do_write, + .rmw_i2c = pmac_i2c_do_rmw, + .read_i2c_sub = pmac_i2c_do_read_sub, + .write_i2c_sub = pmac_i2c_do_write_sub, + .rmw_i2c_sub = pmac_i2c_do_rmw_sub, + .set_i2c_mode = pmac_i2c_do_set_mode, + .mask_and_compare = pmac_i2c_do_mask_and_comp, + .delay = pmac_i2c_do_delay, +}; + +static void __init pmac_i2c_dev_create(struct device_node *np, int quirks) +{ + DBG("dev_create(%s)\n", np->full_name); + + pmf_register_driver(np, &pmac_i2c_pfunc_handlers, + (void *)(long)quirks); +} + +static void __init pmac_i2c_dev_init(struct device_node *np, int quirks) +{ + DBG("dev_create(%s)\n", np->full_name); + + pmf_do_functions(np, NULL, 0, PMF_FLAGS_ON_INIT, NULL); +} + +static void pmac_i2c_dev_suspend(struct device_node *np, int quirks) +{ + DBG("dev_suspend(%s)\n", np->full_name); + pmf_do_functions(np, NULL, 0, PMF_FLAGS_ON_SLEEP, NULL); +} + +static void pmac_i2c_dev_resume(struct device_node *np, int quirks) +{ + DBG("dev_resume(%s)\n", np->full_name); + pmf_do_functions(np, NULL, 0, PMF_FLAGS_ON_WAKE, NULL); +} + +void pmac_pfunc_i2c_suspend(void) +{ + pmac_i2c_devscan(pmac_i2c_dev_suspend); +} + +void pmac_pfunc_i2c_resume(void) +{ + pmac_i2c_devscan(pmac_i2c_dev_resume); +} + /* - * Initialize us: probe all i2c busses on the machine and instantiate - * busses. + * Initialize us: probe all i2c busses on the machine, instantiate + * busses and platform functions as needed. */ /* This is non-static as it might be called early by smp code */ int __init pmac_i2c_init(void) @@ -1187,6 +1470,10 @@ int __init pmac_i2c_init(void) /* Probe SMU i2c busses */ smu_i2c_probe(); #endif + + /* Now add plaform functions for some known devices */ + pmac_i2c_devscan(pmac_i2c_dev_create); + return 0; } arch_initcall(pmac_i2c_init); @@ -1216,6 +1503,9 @@ static int __init pmac_i2c_create_platform_devices(void) platform_device_add(bus->platform_dev); } + /* Now call platform "init" functions */ + pmac_i2c_devscan(pmac_i2c_dev_init); + return 0; } subsys_initcall(pmac_i2c_create_platform_devices); diff --git a/arch/powerpc/platforms/powermac/pfunc_base.c b/arch/powerpc/platforms/powermac/pfunc_base.c new file mode 100644 index 00000000000000..4ffd2a9832a0c9 --- /dev/null +++ b/arch/powerpc/platforms/powermac/pfunc_base.c @@ -0,0 +1,405 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DBG(fmt...) printk(fmt) + +static irqreturn_t macio_gpio_irq(int irq, void *data, struct pt_regs *regs) +{ + pmf_do_irq(data); + + return IRQ_HANDLED; +} + +static int macio_do_gpio_irq_enable(struct pmf_function *func) +{ + if (func->node->n_intrs < 1) + return -EINVAL; + + return request_irq(func->node->intrs[0].line, macio_gpio_irq, 0, + func->node->name, func); +} + +static int macio_do_gpio_irq_disable(struct pmf_function *func) +{ + if (func->node->n_intrs < 1) + return -EINVAL; + + free_irq(func->node->intrs[0].line, func); + return 0; +} + +static int macio_do_gpio_write(PMF_STD_ARGS, u8 value, u8 mask) +{ + u8 __iomem *addr = (u8 __iomem *)func->driver_data; + unsigned long flags; + u8 tmp; + + /* Check polarity */ + if (args && args->count && !args->u[0].v) + value = ~value; + + /* Toggle the GPIO */ + spin_lock_irqsave(&feature_lock, flags); + tmp = readb(addr); + tmp = (tmp & ~mask) | (value & mask); + DBG("Do write 0x%02x to GPIO %s (%p)\n", + tmp, func->node->full_name, addr); + writeb(tmp, addr); + spin_unlock_irqrestore(&feature_lock, flags); + + return 0; +} + +static int macio_do_gpio_read(PMF_STD_ARGS, u8 mask, int rshift, u8 xor) +{ + u8 __iomem *addr = (u8 __iomem *)func->driver_data; + u32 value; + + /* Check if we have room for reply */ + if (args == NULL || args->count == 0 || args->u[0].p == NULL) + return -EINVAL; + + value = readb(addr); + *args->u[0].p = ((value & mask) >> rshift) ^ xor; + + return 0; +} + +static int macio_do_delay(PMF_STD_ARGS, u32 duration) +{ + /* assume we can sleep ! */ + msleep((duration + 999) / 1000); + return 0; +} + +static struct pmf_handlers macio_gpio_handlers = { + .irq_enable = macio_do_gpio_irq_enable, + .irq_disable = macio_do_gpio_irq_disable, + .write_gpio = macio_do_gpio_write, + .read_gpio = macio_do_gpio_read, + .delay = macio_do_delay, +}; + +static void macio_gpio_init_one(struct macio_chip *macio) +{ + struct device_node *gparent, *gp; + + /* + * Find the "gpio" parent node + */ + + for (gparent = NULL; + (gparent = of_get_next_child(macio->of_node, gparent)) != NULL;) + if (strcmp(gparent->name, "gpio") == 0) + break; + if (gparent == NULL) + return; + + DBG("Installing GPIO functions for macio %s\n", + macio->of_node->full_name); + + /* + * Ok, got one, we dont need anything special to track them down, so + * we just create them all + */ + for (gp = NULL; (gp = of_get_next_child(gparent, gp)) != NULL;) { + u32 *reg = (u32 *)get_property(gp, "reg", NULL); + unsigned long offset; + if (reg == NULL) + continue; + offset = *reg; + /* Deal with old style device-tree. We can safely hard code the + * offset for now too even if it's a bit gross ... + */ + if (offset < 0x50) + offset += 0x50; + offset += (unsigned long)macio->base; + pmf_register_driver(gp, &macio_gpio_handlers, (void *)offset); + } + + DBG("Calling initial GPIO functions for macio %s\n", + macio->of_node->full_name); + + /* And now we run all the init ones */ + for (gp = NULL; (gp = of_get_next_child(gparent, gp)) != NULL;) + pmf_do_functions(gp, NULL, 0, PMF_FLAGS_ON_INIT, NULL); + + /* Note: We do not at this point implement the "at sleep" or "at wake" + * functions. I yet to find any for GPIOs anyway + */ +} + +static int macio_do_write_reg32(PMF_STD_ARGS, u32 offset, u32 value, u32 mask) +{ + struct macio_chip *macio = func->driver_data; + unsigned long flags; + + spin_lock_irqsave(&feature_lock, flags); + MACIO_OUT32(offset, (MACIO_IN32(offset) & ~mask) | (value & mask)); + spin_unlock_irqrestore(&feature_lock, flags); + return 0; +} + +static int macio_do_read_reg32(PMF_STD_ARGS, u32 offset) +{ + struct macio_chip *macio = func->driver_data; + + /* Check if we have room for reply */ + if (args == NULL || args->count == 0 || args->u[0].p == NULL) + return -EINVAL; + + *args->u[0].p = MACIO_IN32(offset); + return 0; +} + +static int macio_do_write_reg8(PMF_STD_ARGS, u32 offset, u8 value, u8 mask) +{ + struct macio_chip *macio = func->driver_data; + unsigned long flags; + + spin_lock_irqsave(&feature_lock, flags); + MACIO_OUT8(offset, (MACIO_IN8(offset) & ~mask) | (value & mask)); + spin_unlock_irqrestore(&feature_lock, flags); + return 0; +} + +static int macio_do_read_reg8(PMF_STD_ARGS, u32 offset) +{ + struct macio_chip *macio = func->driver_data; + + /* Check if we have room for reply */ + if (args == NULL || args->count == 0 || args->u[0].p == NULL) + return -EINVAL; + + *((u8 *)(args->u[0].p)) = MACIO_IN8(offset); + return 0; +} + +static int macio_do_read_reg32_msrx(PMF_STD_ARGS, u32 offset, u32 mask, + u32 shift, u32 xor) +{ + struct macio_chip *macio = func->driver_data; + + /* Check if we have room for reply */ + if (args == NULL || args->count == 0 || args->u[0].p == NULL) + return -EINVAL; + + *args->u[0].p = ((MACIO_IN32(offset) & mask) >> shift) ^ xor; + return 0; +} + +static int macio_do_read_reg8_msrx(PMF_STD_ARGS, u32 offset, u32 mask, + u32 shift, u32 xor) +{ + struct macio_chip *macio = func->driver_data; + + /* Check if we have room for reply */ + if (args == NULL || args->count == 0 || args->u[0].p == NULL) + return -EINVAL; + + *((u8 *)(args->u[0].p)) = ((MACIO_IN8(offset) & mask) >> shift) ^ xor; + return 0; +} + +static int macio_do_write_reg32_slm(PMF_STD_ARGS, u32 offset, u32 shift, + u32 mask) +{ + struct macio_chip *macio = func->driver_data; + unsigned long flags; + u32 tmp, val; + + /* Check args */ + if (args == NULL || args->count == 0) + return -EINVAL; + + spin_lock_irqsave(&feature_lock, flags); + tmp = MACIO_IN32(offset); + val = args->u[0].v << shift; + tmp = (tmp & ~mask) | (val & mask); + MACIO_OUT32(offset, tmp); + spin_unlock_irqrestore(&feature_lock, flags); + return 0; +} + +static int macio_do_write_reg8_slm(PMF_STD_ARGS, u32 offset, u32 shift, + u32 mask) +{ + struct macio_chip *macio = func->driver_data; + unsigned long flags; + u32 tmp, val; + + /* Check args */ + if (args == NULL || args->count == 0) + return -EINVAL; + + spin_lock_irqsave(&feature_lock, flags); + tmp = MACIO_IN8(offset); + val = args->u[0].v << shift; + tmp = (tmp & ~mask) | (val & mask); + MACIO_OUT8(offset, tmp); + spin_unlock_irqrestore(&feature_lock, flags); + return 0; +} + +static struct pmf_handlers macio_mmio_handlers = { + .write_reg32 = macio_do_write_reg32, + .read_reg32 = macio_do_read_reg32, + .write_reg8 = macio_do_write_reg8, + .read_reg32 = macio_do_read_reg8, + .read_reg32_msrx = macio_do_read_reg32_msrx, + .read_reg8_msrx = macio_do_read_reg8_msrx, + .write_reg32_slm = macio_do_write_reg32_slm, + .write_reg8_slm = macio_do_write_reg8_slm, + .delay = macio_do_delay, +}; + +static void macio_mmio_init_one(struct macio_chip *macio) +{ + DBG("Installing MMIO functions for macio %s\n", + macio->of_node->full_name); + + pmf_register_driver(macio->of_node, &macio_mmio_handlers, macio); +} + +static struct device_node *unin_hwclock; + +static int unin_do_write_reg32(PMF_STD_ARGS, u32 offset, u32 value, u32 mask) +{ + unsigned long flags; + + spin_lock_irqsave(&feature_lock, flags); + /* This is fairly bogus in darwin, but it should work for our needs + * implemeted that way: + */ + UN_OUT(offset, (UN_IN(offset) & ~mask) | (value & mask)); + spin_unlock_irqrestore(&feature_lock, flags); + return 0; +} + + +static struct pmf_handlers unin_mmio_handlers = { + .write_reg32 = unin_do_write_reg32, + .delay = macio_do_delay, +}; + +static void uninorth_install_pfunc(void) +{ + struct device_node *np; + + DBG("Installing functions for UniN %s\n", + uninorth_node->full_name); + + /* + * Install handlers for the bridge itself + */ + pmf_register_driver(uninorth_node, &unin_mmio_handlers, NULL); + pmf_do_functions(uninorth_node, NULL, 0, PMF_FLAGS_ON_INIT, NULL); + + + /* + * Install handlers for the hwclock child if any + */ + for (np = NULL; (np = of_get_next_child(uninorth_node, np)) != NULL;) + if (strcmp(np->name, "hw-clock") == 0) { + unin_hwclock = np; + break; + } + if (unin_hwclock) { + DBG("Installing functions for UniN clock %s\n", + unin_hwclock->full_name); + pmf_register_driver(unin_hwclock, &unin_mmio_handlers, NULL); + pmf_do_functions(unin_hwclock, NULL, 0, PMF_FLAGS_ON_INIT, + NULL); + } +} + +/* We export this as the SMP code might init us early */ +int __init pmac_pfunc_base_install(void) +{ + static int pfbase_inited; + int i; + + if (pfbase_inited) + return 0; + pfbase_inited = 1; + + + DBG("Installing base platform functions...\n"); + + /* + * Locate mac-io chips and install handlers + */ + for (i = 0 ; i < MAX_MACIO_CHIPS; i++) { + if (macio_chips[i].of_node) { + macio_mmio_init_one(&macio_chips[i]); + macio_gpio_init_one(&macio_chips[i]); + } + } + + /* + * Install handlers for northbridge and direct mapped hwclock + * if any. We do not implement the config space access callback + * which is only ever used for functions that we do not call in + * the current driver (enabling/disabling cells in U2, mostly used + * to restore the PCI settings, we do that differently) + */ + if (uninorth_node && uninorth_base) + uninorth_install_pfunc(); + + DBG("All base functions installed\n"); + + return 0; +} + +arch_initcall(pmac_pfunc_base_install); + +#ifdef CONFIG_PM + +/* Those can be called by pmac_feature. Ultimately, I should use a sysdev + * or a device, but for now, that's good enough until I sort out some + * ordering issues. Also, we do not bother with GPIOs, as so far I yet have + * to see a case where a GPIO function has the on-suspend or on-resume bit + */ +void pmac_pfunc_base_suspend(void) +{ + int i; + + for (i = 0 ; i < MAX_MACIO_CHIPS; i++) { + if (macio_chips[i].of_node) + pmf_do_functions(macio_chips[i].of_node, NULL, 0, + PMF_FLAGS_ON_SLEEP, NULL); + } + if (uninorth_node) + pmf_do_functions(uninorth_node, NULL, 0, + PMF_FLAGS_ON_SLEEP, NULL); + if (unin_hwclock) + pmf_do_functions(unin_hwclock, NULL, 0, + PMF_FLAGS_ON_SLEEP, NULL); +} + +void pmac_pfunc_base_resume(void) +{ + int i; + + if (unin_hwclock) + pmf_do_functions(unin_hwclock, NULL, 0, + PMF_FLAGS_ON_WAKE, NULL); + if (uninorth_node) + pmf_do_functions(uninorth_node, NULL, 0, + PMF_FLAGS_ON_WAKE, NULL); + for (i = 0 ; i < MAX_MACIO_CHIPS; i++) { + if (macio_chips[i].of_node) + pmf_do_functions(macio_chips[i].of_node, NULL, 0, + PMF_FLAGS_ON_WAKE, NULL); + } +} + +#endif /* CONFIG_PM */ diff --git a/arch/powerpc/platforms/powermac/pfunc_core.c b/arch/powerpc/platforms/powermac/pfunc_core.c new file mode 100644 index 00000000000000..c32c623001dced --- /dev/null +++ b/arch/powerpc/platforms/powermac/pfunc_core.c @@ -0,0 +1,989 @@ +/* + * + * FIXME: Properly make this race free with refcounting etc... + * + * FIXME: LOCKING !!! + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* Debug */ +#define LOG_PARSE(fmt...) +#define LOG_ERROR(fmt...) printk(fmt) +#define LOG_BLOB(t,b,c) +#define DBG(fmt...) printk(fmt) + +/* Command numbers */ +#define PMF_CMD_LIST 0 +#define PMF_CMD_WRITE_GPIO 1 +#define PMF_CMD_READ_GPIO 2 +#define PMF_CMD_WRITE_REG32 3 +#define PMF_CMD_READ_REG32 4 +#define PMF_CMD_WRITE_REG16 5 +#define PMF_CMD_READ_REG16 6 +#define PMF_CMD_WRITE_REG8 7 +#define PMF_CMD_READ_REG8 8 +#define PMF_CMD_DELAY 9 +#define PMF_CMD_WAIT_REG32 10 +#define PMF_CMD_WAIT_REG16 11 +#define PMF_CMD_WAIT_REG8 12 +#define PMF_CMD_READ_I2C 13 +#define PMF_CMD_WRITE_I2C 14 +#define PMF_CMD_RMW_I2C 15 +#define PMF_CMD_GEN_I2C 16 +#define PMF_CMD_SHIFT_BYTES_RIGHT 17 +#define PMF_CMD_SHIFT_BYTES_LEFT 18 +#define PMF_CMD_READ_CFG 19 +#define PMF_CMD_WRITE_CFG 20 +#define PMF_CMD_RMW_CFG 21 +#define PMF_CMD_READ_I2C_SUBADDR 22 +#define PMF_CMD_WRITE_I2C_SUBADDR 23 +#define PMF_CMD_SET_I2C_MODE 24 +#define PMF_CMD_RMW_I2C_SUBADDR 25 +#define PMF_CMD_READ_REG32_MASK_SHR_XOR 26 +#define PMF_CMD_READ_REG16_MASK_SHR_XOR 27 +#define PMF_CMD_READ_REG8_MASK_SHR_XOR 28 +#define PMF_CMD_WRITE_REG32_SHL_MASK 29 +#define PMF_CMD_WRITE_REG16_SHL_MASK 30 +#define PMF_CMD_WRITE_REG8_SHL_MASK 31 +#define PMF_CMD_MASK_AND_COMPARE 32 +#define PMF_CMD_COUNT 33 + +/* This structure holds the state of the parser while walking through + * a function definition + */ +struct pmf_cmd { + const void *cmdptr; + const void *cmdend; + struct pmf_function *func; + void *instdata; + struct pmf_args *args; + int error; +}; + +#if 0 +/* Debug output */ +static void print_blob(const char *title, const void *blob, int bytes) +{ + printk("%s", title); + while(bytes--) { + printk("%02x ", *((u8 *)blob)); + blob += 1; + } + printk("\n"); +} +#endif + +/* + * Parser helpers + */ + +static u32 pmf_next32(struct pmf_cmd *cmd) +{ + u32 value; + if ((cmd->cmdend - cmd->cmdptr) < 4) { + cmd->error = 1; + return 0; + } + value = *((u32 *)cmd->cmdptr); + cmd->cmdptr += 4; + return value; +} + +static const void* pmf_next_blob(struct pmf_cmd *cmd, int count) +{ + const void *value; + if ((cmd->cmdend - cmd->cmdptr) < count) { + cmd->error = 1; + return NULL; + } + value = cmd->cmdptr; + cmd->cmdptr += count; + return value; +} + +/* + * Individual command parsers + */ + +#define PMF_PARSE_CALL(name, cmd, handlers, p...) \ + do { \ + if (cmd->error) \ + return -ENXIO; \ + if (handlers == NULL) \ + return 0; \ + if (handlers->name) \ + return handlers->name(cmd->func, cmd->instdata, \ + cmd->args, p); \ + return -1; \ + } while(0) \ + + +static int pmf_parser_write_gpio(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u8 value = (u8)pmf_next32(cmd); + u8 mask = (u8)pmf_next32(cmd); + + LOG_PARSE("pmf: write_gpio(value: %02x, mask: %02x)\n", value, mask); + + PMF_PARSE_CALL(write_gpio, cmd, h, value, mask); +} + +static int pmf_parser_read_gpio(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u8 mask = (u8)pmf_next32(cmd); + int rshift = (int)pmf_next32(cmd); + u8 xor = (u8)pmf_next32(cmd); + + LOG_PARSE("pmf: read_gpio(mask: %02x, rshift: %d, xor: %02x)\n", + mask, rshift, xor); + + PMF_PARSE_CALL(read_gpio, cmd, h, mask, rshift, xor); +} + +static int pmf_parser_write_reg32(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u32 value = pmf_next32(cmd); + u32 mask = pmf_next32(cmd); + + LOG_PARSE("pmf: write_reg32(offset: %08x, value: %08x, mask: %08x)\n", + offset, value, mask); + + PMF_PARSE_CALL(write_reg32, cmd, h, offset, value, mask); +} + +static int pmf_parser_read_reg32(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + + LOG_PARSE("pmf: read_reg32(offset: %08x)\n", offset); + + PMF_PARSE_CALL(read_reg32, cmd, h, offset); +} + + +static int pmf_parser_write_reg16(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u16 value = (u16)pmf_next32(cmd); + u16 mask = (u16)pmf_next32(cmd); + + LOG_PARSE("pmf: write_reg16(offset: %08x, value: %04x, mask: %04x)\n", + offset, value, mask); + + PMF_PARSE_CALL(write_reg16, cmd, h, offset, value, mask); +} + +static int pmf_parser_read_reg16(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + + LOG_PARSE("pmf: read_reg16(offset: %08x)\n", offset); + + PMF_PARSE_CALL(read_reg16, cmd, h, offset); +} + + +static int pmf_parser_write_reg8(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u8 value = (u16)pmf_next32(cmd); + u8 mask = (u16)pmf_next32(cmd); + + LOG_PARSE("pmf: write_reg8(offset: %08x, value: %02x, mask: %02x)\n", + offset, value, mask); + + PMF_PARSE_CALL(write_reg8, cmd, h, offset, value, mask); +} + +static int pmf_parser_read_reg8(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + + LOG_PARSE("pmf: read_reg8(offset: %08x)\n", offset); + + PMF_PARSE_CALL(read_reg8, cmd, h, offset); +} + +static int pmf_parser_delay(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 duration = pmf_next32(cmd); + + LOG_PARSE("pmf: delay(duration: %d us)\n", duration); + + PMF_PARSE_CALL(delay, cmd, h, duration); +} + +static int pmf_parser_wait_reg32(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u32 value = pmf_next32(cmd); + u32 mask = pmf_next32(cmd); + + LOG_PARSE("pmf: wait_reg32(offset: %08x, comp_value: %08x,mask: %08x)\n", + offset, value, mask); + + PMF_PARSE_CALL(wait_reg32, cmd, h, offset, value, mask); +} + +static int pmf_parser_wait_reg16(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u16 value = (u16)pmf_next32(cmd); + u16 mask = (u16)pmf_next32(cmd); + + LOG_PARSE("pmf: wait_reg16(offset: %08x, comp_value: %04x,mask: %04x)\n", + offset, value, mask); + + PMF_PARSE_CALL(wait_reg16, cmd, h, offset, value, mask); +} + +static int pmf_parser_wait_reg8(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u8 value = (u8)pmf_next32(cmd); + u8 mask = (u8)pmf_next32(cmd); + + LOG_PARSE("pmf: wait_reg8(offset: %08x, comp_value: %02x,mask: %02x)\n", + offset, value, mask); + + PMF_PARSE_CALL(wait_reg8, cmd, h, offset, value, mask); +} + +static int pmf_parser_read_i2c(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 bytes = pmf_next32(cmd); + + LOG_PARSE("pmf: read_i2c(bytes: %ud)\n", bytes); + + PMF_PARSE_CALL(read_i2c, cmd, h, bytes); +} + +static int pmf_parser_write_i2c(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 bytes = pmf_next32(cmd); + const void *blob = pmf_next_blob(cmd, bytes); + + LOG_PARSE("pmf: write_i2c(bytes: %ud) ...\n", bytes); + LOG_BLOB("pmf: data: \n", blob, bytes); + + PMF_PARSE_CALL(write_i2c, cmd, h, bytes, blob); +} + + +static int pmf_parser_rmw_i2c(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 maskbytes = pmf_next32(cmd); + u32 valuesbytes = pmf_next32(cmd); + u32 totalbytes = pmf_next32(cmd); + const void *maskblob = pmf_next_blob(cmd, maskbytes); + const void *valuesblob = pmf_next_blob(cmd, valuesbytes); + + LOG_PARSE("pmf: rmw_i2c(maskbytes: %ud, valuebytes: %ud, " + "totalbytes: %d) ...\n", + maskbytes, valuesbytes, totalbytes); + LOG_BLOB("pmf: mask data: \n", maskblob, maskbytes); + LOG_BLOB("pmf: values data: \n", valuesblob, valuesbytes); + + PMF_PARSE_CALL(rmw_i2c, cmd, h, maskbytes, valuesbytes, totalbytes, + maskblob, valuesblob); +} + +static int pmf_parser_read_cfg(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u32 bytes = pmf_next32(cmd); + + LOG_PARSE("pmf: read_cfg(offset: %x, bytes: %ud)\n", offset, bytes); + + PMF_PARSE_CALL(read_cfg, cmd, h, offset, bytes); +} + + +static int pmf_parser_write_cfg(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u32 bytes = pmf_next32(cmd); + const void *blob = pmf_next_blob(cmd, bytes); + + LOG_PARSE("pmf: write_cfg(offset: %x, bytes: %ud)\n", offset, bytes); + LOG_BLOB("pmf: data: \n", blob, bytes); + + PMF_PARSE_CALL(write_cfg, cmd, h, offset, bytes, blob); +} + +static int pmf_parser_rmw_cfg(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u32 maskbytes = pmf_next32(cmd); + u32 valuesbytes = pmf_next32(cmd); + u32 totalbytes = pmf_next32(cmd); + const void *maskblob = pmf_next_blob(cmd, maskbytes); + const void *valuesblob = pmf_next_blob(cmd, valuesbytes); + + LOG_PARSE("pmf: rmw_cfg(maskbytes: %ud, valuebytes: %ud," + " totalbytes: %d) ...\n", + maskbytes, valuesbytes, totalbytes); + LOG_BLOB("pmf: mask data: \n", maskblob, maskbytes); + LOG_BLOB("pmf: values data: \n", valuesblob, valuesbytes); + + PMF_PARSE_CALL(rmw_cfg, cmd, h, offset, maskbytes, valuesbytes, + totalbytes, maskblob, valuesblob); +} + + +static int pmf_parser_read_i2c_sub(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u8 subaddr = (u8)pmf_next32(cmd); + u32 bytes = pmf_next32(cmd); + + LOG_PARSE("pmf: read_i2c_sub(subaddr: %x, bytes: %ud)\n", + subaddr, bytes); + + PMF_PARSE_CALL(read_i2c_sub, cmd, h, subaddr, bytes); +} + +static int pmf_parser_write_i2c_sub(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u8 subaddr = (u8)pmf_next32(cmd); + u32 bytes = pmf_next32(cmd); + const void *blob = pmf_next_blob(cmd, bytes); + + LOG_PARSE("pmf: write_i2c_sub(subaddr: %x, bytes: %ud) ...\n", + subaddr, bytes); + LOG_BLOB("pmf: data: \n", blob, bytes); + + PMF_PARSE_CALL(write_i2c_sub, cmd, h, subaddr, bytes, blob); +} + +static int pmf_parser_set_i2c_mode(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u32 mode = pmf_next32(cmd); + + LOG_PARSE("pmf: set_i2c_mode(mode: %d)\n", mode); + + PMF_PARSE_CALL(set_i2c_mode, cmd, h, mode); +} + + +static int pmf_parser_rmw_i2c_sub(struct pmf_cmd *cmd, struct pmf_handlers *h) +{ + u8 subaddr = (u8)pmf_next32(cmd); + u32 maskbytes = pmf_next32(cmd); + u32 valuesbytes = pmf_next32(cmd); + u32 totalbytes = pmf_next32(cmd); + const void *maskblob = pmf_next_blob(cmd, maskbytes); + const void *valuesblob = pmf_next_blob(cmd, valuesbytes); + + LOG_PARSE("pmf: rmw_i2c_sub(subaddr: %x, maskbytes: %ud, valuebytes: %ud" + ", totalbytes: %d) ...\n", + subaddr, maskbytes, valuesbytes, totalbytes); + LOG_BLOB("pmf: mask data: \n", maskblob, maskbytes); + LOG_BLOB("pmf: values data: \n", valuesblob, valuesbytes); + + PMF_PARSE_CALL(rmw_i2c_sub, cmd, h, subaddr, maskbytes, valuesbytes, + totalbytes, maskblob, valuesblob); +} + +static int pmf_parser_read_reg32_msrx(struct pmf_cmd *cmd, + struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u32 mask = pmf_next32(cmd); + u32 shift = pmf_next32(cmd); + u32 xor = pmf_next32(cmd); + + LOG_PARSE("pmf: read_reg32_msrx(offset: %x, mask: %x, shift: %x," + " xor: %x\n", offset, mask, shift, xor); + + PMF_PARSE_CALL(read_reg32_msrx, cmd, h, offset, mask, shift, xor); +} + +static int pmf_parser_read_reg16_msrx(struct pmf_cmd *cmd, + struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u32 mask = pmf_next32(cmd); + u32 shift = pmf_next32(cmd); + u32 xor = pmf_next32(cmd); + + LOG_PARSE("pmf: read_reg16_msrx(offset: %x, mask: %x, shift: %x," + " xor: %x\n", offset, mask, shift, xor); + + PMF_PARSE_CALL(read_reg16_msrx, cmd, h, offset, mask, shift, xor); +} +static int pmf_parser_read_reg8_msrx(struct pmf_cmd *cmd, + struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u32 mask = pmf_next32(cmd); + u32 shift = pmf_next32(cmd); + u32 xor = pmf_next32(cmd); + + LOG_PARSE("pmf: read_reg8_msrx(offset: %x, mask: %x, shift: %x," + " xor: %x\n", offset, mask, shift, xor); + + PMF_PARSE_CALL(read_reg8_msrx, cmd, h, offset, mask, shift, xor); +} + +static int pmf_parser_write_reg32_slm(struct pmf_cmd *cmd, + struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u32 shift = pmf_next32(cmd); + u32 mask = pmf_next32(cmd); + + LOG_PARSE("pmf: write_reg32_slm(offset: %x, shift: %x, mask: %x\n", + offset, shift, mask); + + PMF_PARSE_CALL(write_reg32_slm, cmd, h, offset, shift, mask); +} + +static int pmf_parser_write_reg16_slm(struct pmf_cmd *cmd, + struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u32 shift = pmf_next32(cmd); + u32 mask = pmf_next32(cmd); + + LOG_PARSE("pmf: write_reg16_slm(offset: %x, shift: %x, mask: %x\n", + offset, shift, mask); + + PMF_PARSE_CALL(write_reg16_slm, cmd, h, offset, shift, mask); +} + +static int pmf_parser_write_reg8_slm(struct pmf_cmd *cmd, + struct pmf_handlers *h) +{ + u32 offset = pmf_next32(cmd); + u32 shift = pmf_next32(cmd); + u32 mask = pmf_next32(cmd); + + LOG_PARSE("pmf: write_reg8_slm(offset: %x, shift: %x, mask: %x\n", + offset, shift, mask); + + PMF_PARSE_CALL(write_reg8_slm, cmd, h, offset, shift, mask); +} + +static int pmf_parser_mask_and_compare(struct pmf_cmd *cmd, + struct pmf_handlers *h) +{ + u32 bytes = pmf_next32(cmd); + const void *maskblob = pmf_next_blob(cmd, bytes); + const void *valuesblob = pmf_next_blob(cmd, bytes); + + LOG_PARSE("pmf: mask_and_compare(length: %ud ...\n", bytes); + LOG_BLOB("pmf: mask data: \n", maskblob, bytes); + LOG_BLOB("pmf: values data: \n", valuesblob, bytes); + + PMF_PARSE_CALL(mask_and_compare, cmd, h, + bytes, maskblob, valuesblob); +} + + +typedef int (*pmf_cmd_parser_t)(struct pmf_cmd *cmd, struct pmf_handlers *h); + +static pmf_cmd_parser_t pmf_parsers[PMF_CMD_COUNT] = +{ + NULL, + pmf_parser_write_gpio, + pmf_parser_read_gpio, + pmf_parser_write_reg32, + pmf_parser_read_reg32, + pmf_parser_write_reg16, + pmf_parser_read_reg16, + pmf_parser_write_reg8, + pmf_parser_read_reg8, + pmf_parser_delay, + pmf_parser_wait_reg32, + pmf_parser_wait_reg16, + pmf_parser_wait_reg8, + pmf_parser_read_i2c, + pmf_parser_write_i2c, + pmf_parser_rmw_i2c, + NULL, /* Bogus command */ + NULL, /* Shift bytes right: NYI */ + NULL, /* Shift bytes left: NYI */ + pmf_parser_read_cfg, + pmf_parser_write_cfg, + pmf_parser_rmw_cfg, + pmf_parser_read_i2c_sub, + pmf_parser_write_i2c_sub, + pmf_parser_set_i2c_mode, + pmf_parser_rmw_i2c_sub, + pmf_parser_read_reg32_msrx, + pmf_parser_read_reg16_msrx, + pmf_parser_read_reg8_msrx, + pmf_parser_write_reg32_slm, + pmf_parser_write_reg16_slm, + pmf_parser_write_reg8_slm, + pmf_parser_mask_and_compare, +}; + +struct pmf_device { + struct list_head link; + struct device_node *node; + struct pmf_handlers *handlers; + struct list_head functions; + struct kref ref; +}; + +static LIST_HEAD(pmf_devices); +static spinlock_t pmf_lock = SPIN_LOCK_UNLOCKED; + +static void pmf_release_device(struct kref *kref) +{ + struct pmf_device *dev = container_of(kref, struct pmf_device, ref); + kfree(dev); +} + +static inline void pmf_put_device(struct pmf_device *dev) +{ + kref_put(&dev->ref, pmf_release_device); +} + +static inline struct pmf_device *pmf_get_device(struct pmf_device *dev) +{ + kref_get(&dev->ref); + return dev; +} + +static inline struct pmf_device *pmf_find_device(struct device_node *np) +{ + struct pmf_device *dev; + + list_for_each_entry(dev, &pmf_devices, link) { + if (dev->node == np) + return pmf_get_device(dev); + } + return NULL; +} + +static int pmf_parse_one(struct pmf_function *func, + struct pmf_handlers *handlers, + void *instdata, struct pmf_args *args) +{ + struct pmf_cmd cmd; + u32 ccode; + int count, rc; + + cmd.cmdptr = func->data; + cmd.cmdend = func->data + func->length; + cmd.func = func; + cmd.instdata = instdata; + cmd.args = args; + cmd.error = 0; + + LOG_PARSE("pmf: func %s, %d bytes, %s...\n", + func->name, func->length, + handlers ? "executing" : "parsing"); + + /* One subcommand to parse for now */ + count = 1; + + while(count-- && cmd.cmdptr < cmd.cmdend) { + /* Get opcode */ + ccode = pmf_next32(&cmd); + /* Check if we are hitting a command list, fetch new count */ + if (ccode == 0) { + count = pmf_next32(&cmd) - 1; + ccode = pmf_next32(&cmd); + } + if (cmd.error) { + LOG_ERROR("pmf: parse error, not enough data\n"); + return -ENXIO; + } + if (ccode >= PMF_CMD_COUNT) { + LOG_ERROR("pmf: command code %d unknown !\n", ccode); + return -ENXIO; + } + if (pmf_parsers[ccode] == NULL) { + LOG_ERROR("pmf: no parser for command %d !\n", ccode); + return -ENXIO; + } + rc = pmf_parsers[ccode](&cmd, handlers); + if (rc != 0) { + LOG_ERROR("pmf: parser for command %d returned" + " error %d\n", ccode, rc); + return rc; + } + } + + /* We are doing an initial parse pass, we need to adjust the size */ + if (handlers == NULL) + func->length = cmd.cmdptr - func->data; + + return 0; +} + +static int pmf_add_function_prop(struct pmf_device *dev, void *driverdata, + const char *name, u32 *data, + unsigned int length) +{ + int count = 0; + struct pmf_function *func = NULL; + + DBG("pmf: Adding functions for platform-do-%s\n", name); + + while (length >= 12) { + /* Allocate a structure */ + func = kzalloc(sizeof(struct pmf_function), GFP_KERNEL); + if (func == NULL) + goto bail; + kref_init(&func->ref); + INIT_LIST_HEAD(&func->irq_clients); + func->node = dev->node; + func->driver_data = driverdata; + func->name = name; + func->phandle = data[0]; + func->flags = data[1]; + data += 2; + length -= 8; + func->data = data; + func->length = length; + func->dev = dev; + DBG("pmf: idx %d: flags=%08x, phandle=%08x " + " %d bytes remaining, parsing...\n", + count+1, func->flags, func->phandle, length); + if (pmf_parse_one(func, NULL, NULL, NULL)) { + kfree(func); + goto bail; + } + length -= func->length; + data = (u32 *)(((u8 *)data) + func->length); + list_add(&func->link, &dev->functions); + pmf_get_device(dev); + count++; + } + bail: + DBG("pmf: Added %d functions\n", count); + + return count; +} + +static int pmf_add_functions(struct pmf_device *dev, void *driverdata) +{ + struct property *pp; +#define PP_PREFIX "platform-do-" + const int plen = strlen(PP_PREFIX); + int count = 0; + + for (pp = dev->node->properties; pp != 0; pp = pp->next) { + char *name; + if (strncmp(pp->name, PP_PREFIX, plen) != 0) + continue; + name = pp->name + plen; + if (strlen(name) && pp->length >= 12) + count += pmf_add_function_prop(dev, driverdata, name, + (u32 *)pp->value, + pp->length); + } + return count; +} + + +int pmf_register_driver(struct device_node *np, + struct pmf_handlers *handlers, + void *driverdata) +{ + struct pmf_device *dev; + unsigned long flags; + int rc = 0; + + if (handlers == NULL) + return -EINVAL; + + DBG("pmf: registering driver for node %s\n", np->full_name); + + spin_lock_irqsave(&pmf_lock, flags); + dev = pmf_find_device(np); + spin_unlock_irqrestore(&pmf_lock, flags); + if (dev != NULL) { + DBG("pmf: already there !\n"); + pmf_put_device(dev); + return -EBUSY; + } + + dev = kzalloc(sizeof(struct pmf_device), GFP_KERNEL); + if (dev == NULL) { + DBG("pmf: no memory !\n"); + return -ENOMEM; + } + kref_init(&dev->ref); + dev->node = of_node_get(np); + dev->handlers = handlers; + INIT_LIST_HEAD(&dev->functions); + + rc = pmf_add_functions(dev, driverdata); + if (rc == 0) { + DBG("pmf: no functions, disposing.. \n"); + of_node_put(np); + kfree(dev); + return -ENODEV; + } + + spin_lock_irqsave(&pmf_lock, flags); + list_add(&dev->link, &pmf_devices); + spin_unlock_irqrestore(&pmf_lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(pmf_register_driver); + +struct pmf_function *pmf_get_function(struct pmf_function *func) +{ + if (!try_module_get(func->dev->handlers->owner)) + return NULL; + kref_get(&func->ref); + return func; +} +EXPORT_SYMBOL_GPL(pmf_get_function); + +static void pmf_release_function(struct kref *kref) +{ + struct pmf_function *func = + container_of(kref, struct pmf_function, ref); + pmf_put_device(func->dev); + kfree(func); +} + +static inline void __pmf_put_function(struct pmf_function *func) +{ + kref_put(&func->ref, pmf_release_function); +} + +void pmf_put_function(struct pmf_function *func) +{ + if (func == NULL) + return; + module_put(func->dev->handlers->owner); + __pmf_put_function(func); +} +EXPORT_SYMBOL_GPL(pmf_put_function); + +void pmf_unregister_driver(struct device_node *np) +{ + struct pmf_device *dev; + unsigned long flags; + + DBG("pmf: unregistering driver for node %s\n", np->full_name); + + spin_lock_irqsave(&pmf_lock, flags); + dev = pmf_find_device(np); + if (dev == NULL) { + DBG("pmf: not such driver !\n"); + spin_unlock_irqrestore(&pmf_lock, flags); + return; + } + list_del(&dev->link); + + while(!list_empty(&dev->functions)) { + struct pmf_function *func = + list_entry(dev->functions.next, typeof(*func), link); + list_del(&func->link); + __pmf_put_function(func); + } + + pmf_put_device(dev); + spin_unlock_irqrestore(&pmf_lock, flags); +} +EXPORT_SYMBOL_GPL(pmf_unregister_driver); + +struct pmf_function *__pmf_find_function(struct device_node *target, + const char *name, u32 flags) +{ + struct device_node *actor = of_node_get(target); + struct pmf_device *dev; + struct pmf_function *func, *result = NULL; + char fname[64]; + u32 *prop, ph; + + /* + * Look for a "platform-*" function reference. If we can't find + * one, then we fallback to a direct call attempt + */ + snprintf(fname, 63, "platform-%s", name); + prop = (u32 *)get_property(target, fname, NULL); + if (prop == NULL) + goto find_it; + ph = *prop; + if (ph == 0) + goto find_it; + + /* + * Ok, now try to find the actor. If we can't find it, we fail, + * there is no point in falling back there + */ + of_node_put(actor); + actor = of_find_node_by_phandle(ph); + if (actor == NULL) + return NULL; + find_it: + dev = pmf_find_device(actor); + if (dev == NULL) + return NULL; + + list_for_each_entry(func, &dev->functions, link) { + if (name && strcmp(name, func->name)) + continue; + if (func->phandle && target->node != func->phandle) + continue; + if ((func->flags & flags) == 0) + continue; + result = func; + break; + } + of_node_put(actor); + pmf_put_device(dev); + return result; +} + + +int pmf_register_irq_client(struct device_node *target, + const char *name, + struct pmf_irq_client *client) +{ + struct pmf_function *func; + unsigned long flags; + + spin_lock_irqsave(&pmf_lock, flags); + func = __pmf_find_function(target, name, PMF_FLAGS_INT_GEN); + if (func == NULL) { + spin_unlock_irqrestore(&pmf_lock, flags); + return -ENODEV; + } + list_add(&client->link, &func->irq_clients); + spin_unlock_irqrestore(&pmf_lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(pmf_register_irq_client); + +void pmf_unregister_irq_client(struct device_node *np, + const char *name, + struct pmf_irq_client *client) +{ + unsigned long flags; + + spin_lock_irqsave(&pmf_lock, flags); + list_del(&client->link); + spin_unlock_irqrestore(&pmf_lock, flags); +} +EXPORT_SYMBOL_GPL(pmf_unregister_irq_client); + + +void pmf_do_irq(struct pmf_function *func) +{ + unsigned long flags; + struct pmf_irq_client *client; + + /* For now, using a spinlock over the whole function. Can be made + * to drop the lock using 2 lists if necessary + */ + spin_lock_irqsave(&pmf_lock, flags); + list_for_each_entry(client, &func->irq_clients, link) { + if (!try_module_get(client->owner)) + continue; + client->handler(client->data); + module_put(client->owner); + } + spin_unlock_irqrestore(&pmf_lock, flags); +} +EXPORT_SYMBOL_GPL(pmf_do_irq); + + +int pmf_call_one(struct pmf_function *func, struct pmf_args *args) +{ + struct pmf_device *dev = func->dev; + void *instdata = NULL; + int rc = 0; + + DBG(" ** pmf_call_one(%s/%s) **\n", dev->node->full_name, func->name); + + if (dev->handlers->begin) + instdata = dev->handlers->begin(func, args); + rc = pmf_parse_one(func, dev->handlers, instdata, args); + if (dev->handlers->end) + dev->handlers->end(func, instdata); + + return rc; +} +EXPORT_SYMBOL_GPL(pmf_call_one); + +int pmf_do_functions(struct device_node *np, const char *name, + u32 phandle, u32 fflags, struct pmf_args *args) +{ + struct pmf_device *dev; + struct pmf_function *func, *tmp; + unsigned long flags; + int rc = -ENODEV; + + spin_lock_irqsave(&pmf_lock, flags); + + dev = pmf_find_device(np); + if (dev == NULL) { + spin_unlock_irqrestore(&pmf_lock, flags); + return -ENODEV; + } + list_for_each_entry_safe(func, tmp, &dev->functions, link) { + if (name && strcmp(name, func->name)) + continue; + if (phandle && func->phandle && phandle != func->phandle) + continue; + if ((func->flags & fflags) == 0) + continue; + if (pmf_get_function(func) == NULL) + continue; + spin_unlock_irqrestore(&pmf_lock, flags); + rc = pmf_call_one(func, args); + pmf_put_function(func); + spin_lock_irqsave(&pmf_lock, flags); + } + pmf_put_device(dev); + spin_unlock_irqrestore(&pmf_lock, flags); + + return rc; +} +EXPORT_SYMBOL_GPL(pmf_do_functions); + + +struct pmf_function *pmf_find_function(struct device_node *target, + const char *name) +{ + struct pmf_function *func; + unsigned long flags; + + spin_lock_irqsave(&pmf_lock, flags); + func = __pmf_find_function(target, name, PMF_FLAGS_ON_DEMAND); + if (func) + func = pmf_get_function(func); + spin_unlock_irqrestore(&pmf_lock, flags); + return func; +} +EXPORT_SYMBOL_GPL(pmf_find_function); + +int pmf_call_function(struct device_node *target, const char *name, + struct pmf_args *args) +{ + struct pmf_function *func = pmf_find_function(target, name); + int rc; + + if (func == NULL) + return -ENODEV; + + rc = pmf_call_one(func, args); + pmf_put_function(func); + return rc; +} +EXPORT_SYMBOL_GPL(pmf_call_function); + diff --git a/arch/powerpc/platforms/powermac/smp.c b/arch/powerpc/platforms/powermac/smp.c index ab72ba86be1e4c..0df2cdcd805c17 100644 --- a/arch/powerpc/platforms/powermac/smp.c +++ b/arch/powerpc/platforms/powermac/smp.c @@ -52,8 +52,9 @@ #include #include #include +#include -#undef DEBUG +#define DEBUG #ifdef DEBUG #define DBG(fmt...) udbg_printf(fmt) @@ -62,6 +63,7 @@ #endif extern void __secondary_start_pmac_0(void); +extern int pmac_pfunc_base_install(void); #ifdef CONFIG_PPC32 @@ -602,11 +604,29 @@ static void __init smp_core99_setup_i2c_hwsync(int ncpus) pmac_tb_clock_chip_host = NULL; } -#endif /* CONFIG_PPC64 */ /* - * SMP G4 and newer G5 use a GPIO to enable/disable the timebase. + * Newer G5s uses a platform function + */ + +static void smp_core99_pfunc_tb_freeze(int freeze) +{ + struct device_node *cpus; + struct pmf_args args; + + cpus = of_find_node_by_path("/cpus"); + BUG_ON(cpus == NULL); + args.count = 1; + args.u[0].v = !freeze; + pmf_call_function(cpus, "cpu-timebase", &args); + of_node_put(cpus); +} + +#else /* CONFIG_PPC64 */ + +/* + * SMP G4 use a GPIO to enable/disable the timebase. */ static unsigned int core99_tb_gpio; /* Timebase freeze GPIO */ @@ -620,6 +640,9 @@ static void smp_core99_gpio_tb_freeze(int freeze) pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, core99_tb_gpio, 0); } + +#endif /* !CONFIG_PPC64 */ + /* L2 and L3 cache settings to pass from CPU0 to CPU1 on G4 cpus */ volatile static long int core99_l2_cache; volatile static long int core99_l3_cache; @@ -665,19 +688,15 @@ static void __init smp_core99_setup(int ncpus) machine_is_compatible("RackMac3,1")) smp_core99_setup_i2c_hwsync(ncpus); - /* GPIO based HW sync on recent G5s */ + /* pfunc based HW sync on recent G5s */ if (pmac_tb_freeze == NULL) { - struct device_node *np = - of_find_node_by_name(NULL, "timebase-enable"); - u32 *reg = (u32 *)get_property(np, "reg", NULL); - - if (np && reg && !strcmp(np->type, "gpio")) { - core99_tb_gpio = *reg; - if (core99_tb_gpio < 0x50) - core99_tb_gpio += 0x50; - pmac_tb_freeze = smp_core99_gpio_tb_freeze; + struct device_node *cpus = + of_find_node_by_path("/cpus"); + if (cpus && + get_property(cpus, "platform-cpu-timebase", NULL)) { + pmac_tb_freeze = smp_core99_pfunc_tb_freeze; printk(KERN_INFO "Processor timebase sync using" - " GPIO 0x%02x\n", core99_tb_gpio); + " platform function\n"); } } @@ -746,6 +765,7 @@ static int __init smp_core99_probe(void) /* We need to perform some early initialisations before we can start * setting up SMP as we are running before initcalls */ + pmac_pfunc_base_install(); pmac_i2c_init(); /* Setup various bits like timebase sync method, ability to nap, ... */ diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index aa481a88ccabff..6eb93e45fcd3f6 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c @@ -55,6 +55,8 @@ #include #include #include +#include +#include #include #include #include @@ -2105,6 +2107,10 @@ pmac_suspend_devices(void) return -EBUSY; } + /* Call platform functions marked "on sleep" */ + pmac_pfunc_i2c_suspend(); + pmac_pfunc_base_suspend(); + /* Stop preemption */ preempt_disable(); @@ -2175,6 +2181,10 @@ pmac_wakeup_devices(void) mdelay(10); preempt_enable(); + /* Call platform functions marked "on wake" */ + pmac_pfunc_base_resume(); + pmac_pfunc_i2c_resume(); + /* Resume devices */ device_resume(); diff --git a/include/asm-powerpc/pmac_feature.h b/include/asm-powerpc/pmac_feature.h index e654ad0e5b428f..3221628130c4e2 100644 --- a/include/asm-powerpc/pmac_feature.h +++ b/include/asm-powerpc/pmac_feature.h @@ -374,5 +374,24 @@ extern struct macio_chip* macio_find(struct device_node* child, int type); #define MACIO_IN8(r) (in_8(MACIO_FCR8(macio,r))) #define MACIO_OUT8(r,v) (out_8(MACIO_FCR8(macio,r), (v))) +/* + * Those are exported by pmac feature for internal use by arch code + * only like the platform function callbacks, do not use directly in drivers + */ +extern spinlock_t feature_lock; +extern struct device_node *uninorth_node; +extern u32 __iomem *uninorth_base; + +/* + * Uninorth reg. access. Note that Uni-N regs are big endian + */ + +#define UN_REG(r) (uninorth_base + ((r) >> 2)) +#define UN_IN(r) (in_be32(UN_REG(r))) +#define UN_OUT(r,v) (out_be32(UN_REG(r), (v))) +#define UN_BIS(r,v) (UN_OUT((r), UN_IN(r) | (v))) +#define UN_BIC(r,v) (UN_OUT((r), UN_IN(r) & ~(v))) + + #endif /* __PPC_ASM_PMAC_FEATURE_H */ #endif /* __KERNEL__ */ diff --git a/include/asm-powerpc/pmac_low_i2c.h b/include/asm-powerpc/pmac_low_i2c.h index 480018f41e1a6f..131011bd7e7682 100644 --- a/include/asm-powerpc/pmac_low_i2c.h +++ b/include/asm-powerpc/pmac_low_i2c.h @@ -99,6 +99,9 @@ extern int pmac_i2c_setmode(struct pmac_i2c_bus *bus, int mode); extern int pmac_i2c_xfer(struct pmac_i2c_bus *bus, u8 addrdir, int subsize, u32 subaddr, u8 *data, int len); +/* Suspend/resume code called by via-pmu directly for now */ +extern void pmac_pfunc_i2c_suspend(void); +extern void pmac_pfunc_i2c_resume(void); #endif /* __KERNEL__ */ #endif /* __PMAC_LOW_I2C_H__ */ diff --git a/include/asm-powerpc/pmac_pfunc.h b/include/asm-powerpc/pmac_pfunc.h new file mode 100644 index 00000000000000..d9728c80f86de3 --- /dev/null +++ b/include/asm-powerpc/pmac_pfunc.h @@ -0,0 +1,253 @@ +#ifndef __PMAC_PFUNC_H__ +#define __PMAC_PFUNC_H__ + +#include +#include + +/* Flags in command lists */ +#define PMF_FLAGS_ON_INIT 0x80000000u +#define PMF_FLGAS_ON_TERM 0x40000000u +#define PMF_FLAGS_ON_SLEEP 0x20000000u +#define PMF_FLAGS_ON_WAKE 0x10000000u +#define PMF_FLAGS_ON_DEMAND 0x08000000u +#define PMF_FLAGS_INT_GEN 0x04000000u +#define PMF_FLAGS_HIGH_SPEED 0x02000000u +#define PMF_FLAGS_LOW_SPEED 0x01000000u +#define PMF_FLAGS_SIDE_EFFECTS 0x00800000u + +/* + * Arguments to a platform function call. + * + * NOTE: By convention, pointer arguments point to an u32 + */ +struct pmf_args { + union { + u32 v; + u32 *p; + } u[4]; + unsigned int count; +}; + +/* + * A driver capable of interpreting commands provides a handlers + * structure filled with whatever handlers are implemented by this + * driver. Non implemented handlers are left NULL. + * + * PMF_STD_ARGS are the same arguments that are passed to the parser + * and that gets passed back to the various handlers. + * + * Interpreting a given function always start with a begin() call which + * returns an instance data to be passed around subsequent calls, and + * ends with an end() call. This allows the low level driver to implement + * locking policy or per-function instance data. + * + * For interrupt capable functions, irq_enable() is called when a client + * registers, and irq_disable() is called when the last client unregisters + * Note that irq_enable & irq_disable are called within a semaphore held + * by the core, thus you should not try to register yourself to some other + * pmf interrupt during those calls. + */ + +#define PMF_STD_ARGS struct pmf_function *func, void *instdata, \ + struct pmf_args *args + +struct pmf_function; + +struct pmf_handlers { + void * (*begin)(struct pmf_function *func, struct pmf_args *args); + void (*end)(struct pmf_function *func, void *instdata); + + int (*irq_enable)(struct pmf_function *func); + int (*irq_disable)(struct pmf_function *func); + + int (*write_gpio)(PMF_STD_ARGS, u8 value, u8 mask); + int (*read_gpio)(PMF_STD_ARGS, u8 mask, int rshift, u8 xor); + + int (*write_reg32)(PMF_STD_ARGS, u32 offset, u32 value, u32 mask); + int (*read_reg32)(PMF_STD_ARGS, u32 offset); + int (*write_reg16)(PMF_STD_ARGS, u32 offset, u16 value, u16 mask); + int (*read_reg16)(PMF_STD_ARGS, u32 offset); + int (*write_reg8)(PMF_STD_ARGS, u32 offset, u8 value, u8 mask); + int (*read_reg8)(PMF_STD_ARGS, u32 offset); + + int (*delay)(PMF_STD_ARGS, u32 duration); + + int (*wait_reg32)(PMF_STD_ARGS, u32 offset, u32 value, u32 mask); + int (*wait_reg16)(PMF_STD_ARGS, u32 offset, u16 value, u16 mask); + int (*wait_reg8)(PMF_STD_ARGS, u32 offset, u8 value, u8 mask); + + int (*read_i2c)(PMF_STD_ARGS, u32 len); + int (*write_i2c)(PMF_STD_ARGS, u32 len, const u8 *data); + int (*rmw_i2c)(PMF_STD_ARGS, u32 masklen, u32 valuelen, u32 totallen, + const u8 *maskdata, const u8 *valuedata); + + int (*read_cfg)(PMF_STD_ARGS, u32 offset, u32 len); + int (*write_cfg)(PMF_STD_ARGS, u32 offset, u32 len, const u8 *data); + int (*rmw_cfg)(PMF_STD_ARGS, u32 offset, u32 masklen, u32 valuelen, + u32 totallen, const u8 *maskdata, const u8 *valuedata); + + int (*read_i2c_sub)(PMF_STD_ARGS, u8 subaddr, u32 len); + int (*write_i2c_sub)(PMF_STD_ARGS, u8 subaddr, u32 len, const u8 *data); + int (*set_i2c_mode)(PMF_STD_ARGS, int mode); + int (*rmw_i2c_sub)(PMF_STD_ARGS, u8 subaddr, u32 masklen, u32 valuelen, + u32 totallen, const u8 *maskdata, + const u8 *valuedata); + + int (*read_reg32_msrx)(PMF_STD_ARGS, u32 offset, u32 mask, u32 shift, + u32 xor); + int (*read_reg16_msrx)(PMF_STD_ARGS, u32 offset, u32 mask, u32 shift, + u32 xor); + int (*read_reg8_msrx)(PMF_STD_ARGS, u32 offset, u32 mask, u32 shift, + u32 xor); + + int (*write_reg32_slm)(PMF_STD_ARGS, u32 offset, u32 shift, u32 mask); + int (*write_reg16_slm)(PMF_STD_ARGS, u32 offset, u32 shift, u32 mask); + int (*write_reg8_slm)(PMF_STD_ARGS, u32 offset, u32 shift, u32 mask); + + int (*mask_and_compare)(PMF_STD_ARGS, u32 len, const u8 *maskdata, + const u8 *valuedata); + + struct module *owner; +}; + + +/* + * Drivers who expose platform functions register at init time, this + * causes the platform functions for that device node to be parsed in + * advance and associated with the device. The data structures are + * partially public so a driver can walk the list of platform functions + * and eventually inspect the flags + */ +struct pmf_device; + +struct pmf_function { + /* All functions for a given driver are linked */ + struct list_head link; + + /* Function node & driver data */ + struct device_node *node; + void *driver_data; + + /* For internal use by core */ + struct pmf_device *dev; + + /* The name is the "xxx" in "platform-do-xxx", this is how + * platform functions are identified by this code. Some functions + * only operate for a given target, in which case the phandle is + * here (or 0 if the filter doesn't apply) + */ + const char *name; + u32 phandle; + + /* The flags for that function. You can have several functions + * with the same name and different flag + */ + u32 flags; + + /* The actual tokenized function blob */ + const void *data; + unsigned int length; + + /* Interrupt clients */ + struct list_head irq_clients; + + /* Refcounting */ + struct kref ref; +}; + +/* + * For platform functions that are interrupts, one can register + * irq_client structures. You canNOT use the same structure twice + * as it contains a link member. Also, the callback is called with + * a spinlock held, you must not call back into any of the pmf_* functions + * from within that callback + */ +struct pmf_irq_client { + void (*handler)(void *data); + void *data; + struct module *owner; + struct list_head link; +}; + + +/* + * Register/Unregister a function-capable driver and its handlers + */ +extern int pmf_register_driver(struct device_node *np, + struct pmf_handlers *handlers, + void *driverdata); + +extern void pmf_unregister_driver(struct device_node *np); + + +/* + * Register/Unregister interrupt clients + */ +extern int pmf_register_irq_client(struct device_node *np, + const char *name, + struct pmf_irq_client *client); + +extern void pmf_unregister_irq_client(struct device_node *np, + const char *name, + struct pmf_irq_client *client); + +/* + * Called by the handlers when an irq happens + */ +extern void pmf_do_irq(struct pmf_function *func); + + +/* + * Low level call to platform functions. + * + * The phandle can filter on the target object for functions that have + * multiple targets, the flags allow you to restrict the call to a given + * combination of flags. + * + * The args array contains as many arguments as is required by the function, + * this is dependent on the function you are calling, unfortunately Apple + * mecanism provides no way to encode that so you have to get it right at + * the call site. Some functions require no args, in which case, you can + * pass NULL. + * + * You can also pass NULL to the name. This will match any function that has + * the appropriate combination of flags & phandle or you can pass 0 to the + * phandle to match any + */ +extern int pmf_do_functions(struct device_node *np, const char *name, + u32 phandle, u32 flags, struct pmf_args *args); + + + +/* + * High level call to a platform function. + * + * This one looks for the platform-xxx first so you should call it to the + * actual target if any. It will fallback to platform-do-xxx if it can't + * find one. It will also exclusively target functions that have + * the "OnDemand" flag. + */ + +extern int pmf_call_function(struct device_node *target, const char *name, + struct pmf_args *args); + + +/* + * For low latency interrupt usage, you can lookup for on-demand functions + * using the functions below + */ + +extern struct pmf_function *pmf_find_function(struct device_node *target, + const char *name); + +extern struct pmf_function * pmf_get_function(struct pmf_function *func); +extern void pmf_put_function(struct pmf_function *func); + +extern int pmf_call_one(struct pmf_function *func, struct pmf_args *args); + + +/* Suspend/resume code called by via-pmu directly for now */ +extern void pmac_pfunc_base_suspend(void); +extern void pmac_pfunc_base_resume(void); + +#endif /* __PMAC_PFUNC_H__ */ -- cgit 1.2.3-korg