18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Bus & driver management routines for devices within
48c2ecf20Sopenharmony_ci * a MacIO ASIC. Interface to new driver model mostly
58c2ecf20Sopenharmony_ci * stolen from the PCI version.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  Copyright (C) 2005 Ben. Herrenschmidt (benh@kernel.crashing.org)
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * TODO:
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci *  - Don't probe below media bay by default, but instead provide
128c2ecf20Sopenharmony_ci *    some hooks for media bay to dynamically add/remove it's own
138c2ecf20Sopenharmony_ci *    sub-devices.
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/string.h>
178c2ecf20Sopenharmony_ci#include <linux/kernel.h>
188c2ecf20Sopenharmony_ci#include <linux/pci.h>
198c2ecf20Sopenharmony_ci#include <linux/pci_ids.h>
208c2ecf20Sopenharmony_ci#include <linux/init.h>
218c2ecf20Sopenharmony_ci#include <linux/module.h>
228c2ecf20Sopenharmony_ci#include <linux/slab.h>
238c2ecf20Sopenharmony_ci#include <linux/of_address.h>
248c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include <asm/machdep.h>
278c2ecf20Sopenharmony_ci#include <asm/macio.h>
288c2ecf20Sopenharmony_ci#include <asm/pmac_feature.h>
298c2ecf20Sopenharmony_ci#include <asm/prom.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#undef DEBUG
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define MAX_NODE_NAME_SIZE (20 - 12)
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic struct macio_chip      *macio_on_hold;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic int macio_bus_match(struct device *dev, struct device_driver *drv)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	const struct of_device_id * matches = drv->of_match_table;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	if (!matches)
428c2ecf20Sopenharmony_ci		return 0;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	return of_match_device(matches, dev) != NULL;
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistruct macio_dev *macio_dev_get(struct macio_dev *dev)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	struct device *tmp;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	if (!dev)
528c2ecf20Sopenharmony_ci		return NULL;
538c2ecf20Sopenharmony_ci	tmp = get_device(&dev->ofdev.dev);
548c2ecf20Sopenharmony_ci	if (tmp)
558c2ecf20Sopenharmony_ci		return to_macio_device(tmp);
568c2ecf20Sopenharmony_ci	else
578c2ecf20Sopenharmony_ci		return NULL;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_civoid macio_dev_put(struct macio_dev *dev)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	if (dev)
638c2ecf20Sopenharmony_ci		put_device(&dev->ofdev.dev);
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic int macio_device_probe(struct device *dev)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	int error = -ENODEV;
708c2ecf20Sopenharmony_ci	struct macio_driver *drv;
718c2ecf20Sopenharmony_ci	struct macio_dev *macio_dev;
728c2ecf20Sopenharmony_ci	const struct of_device_id *match;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	drv = to_macio_driver(dev->driver);
758c2ecf20Sopenharmony_ci	macio_dev = to_macio_device(dev);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (!drv->probe)
788c2ecf20Sopenharmony_ci		return error;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	macio_dev_get(macio_dev);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	match = of_match_device(drv->driver.of_match_table, dev);
838c2ecf20Sopenharmony_ci	if (match)
848c2ecf20Sopenharmony_ci		error = drv->probe(macio_dev, match);
858c2ecf20Sopenharmony_ci	if (error)
868c2ecf20Sopenharmony_ci		macio_dev_put(macio_dev);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	return error;
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic int macio_device_remove(struct device *dev)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	struct macio_dev * macio_dev = to_macio_device(dev);
948c2ecf20Sopenharmony_ci	struct macio_driver * drv = to_macio_driver(dev->driver);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	if (dev->driver && drv->remove)
978c2ecf20Sopenharmony_ci		drv->remove(macio_dev);
988c2ecf20Sopenharmony_ci	macio_dev_put(macio_dev);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	return 0;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic void macio_device_shutdown(struct device *dev)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	struct macio_dev * macio_dev = to_macio_device(dev);
1068c2ecf20Sopenharmony_ci	struct macio_driver * drv = to_macio_driver(dev->driver);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	if (dev->driver && drv->shutdown)
1098c2ecf20Sopenharmony_ci		drv->shutdown(macio_dev);
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic int macio_device_suspend(struct device *dev, pm_message_t state)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct macio_dev * macio_dev = to_macio_device(dev);
1158c2ecf20Sopenharmony_ci	struct macio_driver * drv = to_macio_driver(dev->driver);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	if (dev->driver && drv->suspend)
1188c2ecf20Sopenharmony_ci		return drv->suspend(macio_dev, state);
1198c2ecf20Sopenharmony_ci	return 0;
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic int macio_device_resume(struct device * dev)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	struct macio_dev * macio_dev = to_macio_device(dev);
1258c2ecf20Sopenharmony_ci	struct macio_driver * drv = to_macio_driver(dev->driver);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	if (dev->driver && drv->resume)
1288c2ecf20Sopenharmony_ci		return drv->resume(macio_dev);
1298c2ecf20Sopenharmony_ci	return 0;
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ciextern const struct attribute_group *macio_dev_groups[];
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistruct bus_type macio_bus_type = {
1358c2ecf20Sopenharmony_ci       .name	= "macio",
1368c2ecf20Sopenharmony_ci       .match	= macio_bus_match,
1378c2ecf20Sopenharmony_ci       .uevent = of_device_uevent_modalias,
1388c2ecf20Sopenharmony_ci       .probe	= macio_device_probe,
1398c2ecf20Sopenharmony_ci       .remove	= macio_device_remove,
1408c2ecf20Sopenharmony_ci       .shutdown = macio_device_shutdown,
1418c2ecf20Sopenharmony_ci       .suspend	= macio_device_suspend,
1428c2ecf20Sopenharmony_ci       .resume	= macio_device_resume,
1438c2ecf20Sopenharmony_ci       .dev_groups = macio_dev_groups,
1448c2ecf20Sopenharmony_ci};
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic int __init macio_bus_driver_init(void)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	return bus_register(&macio_bus_type);
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cipostcore_initcall(macio_bus_driver_init);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci/**
1558c2ecf20Sopenharmony_ci * macio_release_dev - free a macio device structure when all users of it are
1568c2ecf20Sopenharmony_ci * finished.
1578c2ecf20Sopenharmony_ci * @dev: device that's been disconnected
1588c2ecf20Sopenharmony_ci *
1598c2ecf20Sopenharmony_ci * Will be called only by the device core when all users of this macio device
1608c2ecf20Sopenharmony_ci * are done. This currently means never as we don't hot remove any macio
1618c2ecf20Sopenharmony_ci * device yet, though that will happen with mediabay based devices in a later
1628c2ecf20Sopenharmony_ci * implementation.
1638c2ecf20Sopenharmony_ci */
1648c2ecf20Sopenharmony_cistatic void macio_release_dev(struct device *dev)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	struct macio_dev *mdev;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci        mdev = to_macio_device(dev);
1698c2ecf20Sopenharmony_ci	kfree(mdev);
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci/**
1738c2ecf20Sopenharmony_ci * macio_resource_quirks - tweak or skip some resources for a device
1748c2ecf20Sopenharmony_ci * @np: pointer to the device node
1758c2ecf20Sopenharmony_ci * @res: resulting resource
1768c2ecf20Sopenharmony_ci * @index: index of resource in node
1778c2ecf20Sopenharmony_ci *
1788c2ecf20Sopenharmony_ci * If this routine returns non-null, then the resource is completely
1798c2ecf20Sopenharmony_ci * skipped.
1808c2ecf20Sopenharmony_ci */
1818c2ecf20Sopenharmony_cistatic int macio_resource_quirks(struct device_node *np, struct resource *res,
1828c2ecf20Sopenharmony_ci				 int index)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	/* Only quirks for memory resources for now */
1858c2ecf20Sopenharmony_ci	if ((res->flags & IORESOURCE_MEM) == 0)
1868c2ecf20Sopenharmony_ci		return 0;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	/* Grand Central has too large resource 0 on some machines */
1898c2ecf20Sopenharmony_ci	if (index == 0 && of_node_name_eq(np, "gc"))
1908c2ecf20Sopenharmony_ci		res->end = res->start + 0x1ffff;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	/* Airport has bogus resource 2 */
1938c2ecf20Sopenharmony_ci	if (index >= 2 && of_node_name_eq(np, "radio"))
1948c2ecf20Sopenharmony_ci		return 1;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci#ifndef CONFIG_PPC64
1978c2ecf20Sopenharmony_ci	/* DBDMAs may have bogus sizes */
1988c2ecf20Sopenharmony_ci	if ((res->start & 0x0001f000) == 0x00008000)
1998c2ecf20Sopenharmony_ci		res->end = res->start + 0xff;
2008c2ecf20Sopenharmony_ci#endif /* CONFIG_PPC64 */
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	/* ESCC parent eats child resources. We could have added a
2038c2ecf20Sopenharmony_ci	 * level of hierarchy, but I don't really feel the need
2048c2ecf20Sopenharmony_ci	 * for it
2058c2ecf20Sopenharmony_ci	 */
2068c2ecf20Sopenharmony_ci	if (of_node_name_eq(np, "escc"))
2078c2ecf20Sopenharmony_ci		return 1;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	/* ESCC has bogus resources >= 3 */
2108c2ecf20Sopenharmony_ci	if (index >= 3 && (of_node_name_eq(np, "ch-a") ||
2118c2ecf20Sopenharmony_ci			   of_node_name_eq(np, "ch-b")))
2128c2ecf20Sopenharmony_ci		return 1;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	/* Media bay has too many resources, keep only first one */
2158c2ecf20Sopenharmony_ci	if (index > 0 && of_node_name_eq(np, "media-bay"))
2168c2ecf20Sopenharmony_ci		return 1;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	/* Some older IDE resources have bogus sizes */
2198c2ecf20Sopenharmony_ci	if (of_node_name_eq(np, "IDE") || of_node_name_eq(np, "ATA") ||
2208c2ecf20Sopenharmony_ci	    of_node_is_type(np, "ide") || of_node_is_type(np, "ata")) {
2218c2ecf20Sopenharmony_ci		if (index == 0 && (res->end - res->start) > 0xfff)
2228c2ecf20Sopenharmony_ci			res->end = res->start + 0xfff;
2238c2ecf20Sopenharmony_ci		if (index == 1 && (res->end - res->start) > 0xff)
2248c2ecf20Sopenharmony_ci			res->end = res->start + 0xff;
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci	return 0;
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic void macio_create_fixup_irq(struct macio_dev *dev, int index,
2308c2ecf20Sopenharmony_ci				   unsigned int line)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	unsigned int irq;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	irq = irq_create_mapping(NULL, line);
2358c2ecf20Sopenharmony_ci	if (!irq) {
2368c2ecf20Sopenharmony_ci		dev->interrupt[index].start = irq;
2378c2ecf20Sopenharmony_ci		dev->interrupt[index].flags = IORESOURCE_IRQ;
2388c2ecf20Sopenharmony_ci		dev->interrupt[index].name = dev_name(&dev->ofdev.dev);
2398c2ecf20Sopenharmony_ci	}
2408c2ecf20Sopenharmony_ci	if (dev->n_interrupts <= index)
2418c2ecf20Sopenharmony_ci		dev->n_interrupts = index + 1;
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic void macio_add_missing_resources(struct macio_dev *dev)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	struct device_node *np = dev->ofdev.dev.of_node;
2478c2ecf20Sopenharmony_ci	unsigned int irq_base;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	/* Gatwick has some missing interrupts on child nodes */
2508c2ecf20Sopenharmony_ci	if (dev->bus->chip->type != macio_gatwick)
2518c2ecf20Sopenharmony_ci		return;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	/* irq_base is always 64 on gatwick. I have no cleaner way to get
2548c2ecf20Sopenharmony_ci	 * that value from here at this point
2558c2ecf20Sopenharmony_ci	 */
2568c2ecf20Sopenharmony_ci	irq_base = 64;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	/* Fix SCC */
2598c2ecf20Sopenharmony_ci	if (of_node_name_eq(np, "ch-a")) {
2608c2ecf20Sopenharmony_ci		macio_create_fixup_irq(dev, 0, 15 + irq_base);
2618c2ecf20Sopenharmony_ci		macio_create_fixup_irq(dev, 1,  4 + irq_base);
2628c2ecf20Sopenharmony_ci		macio_create_fixup_irq(dev, 2,  5 + irq_base);
2638c2ecf20Sopenharmony_ci		printk(KERN_INFO "macio: fixed SCC irqs on gatwick\n");
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	/* Fix media-bay */
2678c2ecf20Sopenharmony_ci	if (of_node_name_eq(np, "media-bay")) {
2688c2ecf20Sopenharmony_ci		macio_create_fixup_irq(dev, 0, 29 + irq_base);
2698c2ecf20Sopenharmony_ci		printk(KERN_INFO "macio: fixed media-bay irq on gatwick\n");
2708c2ecf20Sopenharmony_ci	}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	/* Fix left media bay childs */
2738c2ecf20Sopenharmony_ci	if (dev->media_bay != NULL && of_node_name_eq(np, "floppy")) {
2748c2ecf20Sopenharmony_ci		macio_create_fixup_irq(dev, 0, 19 + irq_base);
2758c2ecf20Sopenharmony_ci		macio_create_fixup_irq(dev, 1,  1 + irq_base);
2768c2ecf20Sopenharmony_ci		printk(KERN_INFO "macio: fixed left floppy irqs\n");
2778c2ecf20Sopenharmony_ci	}
2788c2ecf20Sopenharmony_ci	if (dev->media_bay != NULL && of_node_name_eq(np, "ata4")) {
2798c2ecf20Sopenharmony_ci		macio_create_fixup_irq(dev, 0, 14 + irq_base);
2808c2ecf20Sopenharmony_ci		macio_create_fixup_irq(dev, 0,  3 + irq_base);
2818c2ecf20Sopenharmony_ci		printk(KERN_INFO "macio: fixed left ide irqs\n");
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic void macio_setup_interrupts(struct macio_dev *dev)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	struct device_node *np = dev->ofdev.dev.of_node;
2888c2ecf20Sopenharmony_ci	unsigned int irq;
2898c2ecf20Sopenharmony_ci	int i = 0, j = 0;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	for (;;) {
2928c2ecf20Sopenharmony_ci		struct resource *res;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci		if (j >= MACIO_DEV_COUNT_IRQS)
2958c2ecf20Sopenharmony_ci			break;
2968c2ecf20Sopenharmony_ci		res = &dev->interrupt[j];
2978c2ecf20Sopenharmony_ci		irq = irq_of_parse_and_map(np, i++);
2988c2ecf20Sopenharmony_ci		if (!irq)
2998c2ecf20Sopenharmony_ci			break;
3008c2ecf20Sopenharmony_ci		res->start = irq;
3018c2ecf20Sopenharmony_ci		res->flags = IORESOURCE_IRQ;
3028c2ecf20Sopenharmony_ci		res->name = dev_name(&dev->ofdev.dev);
3038c2ecf20Sopenharmony_ci		if (macio_resource_quirks(np, res, i - 1)) {
3048c2ecf20Sopenharmony_ci			memset(res, 0, sizeof(struct resource));
3058c2ecf20Sopenharmony_ci			continue;
3068c2ecf20Sopenharmony_ci		} else
3078c2ecf20Sopenharmony_ci			j++;
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci	dev->n_interrupts = j;
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic void macio_setup_resources(struct macio_dev *dev,
3138c2ecf20Sopenharmony_ci				  struct resource *parent_res)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	struct device_node *np = dev->ofdev.dev.of_node;
3168c2ecf20Sopenharmony_ci	struct resource r;
3178c2ecf20Sopenharmony_ci	int index;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	for (index = 0; of_address_to_resource(np, index, &r) == 0; index++) {
3208c2ecf20Sopenharmony_ci		struct resource *res;
3218c2ecf20Sopenharmony_ci		if (index >= MACIO_DEV_COUNT_RESOURCES)
3228c2ecf20Sopenharmony_ci			break;
3238c2ecf20Sopenharmony_ci		res = &dev->resource[index];
3248c2ecf20Sopenharmony_ci		*res = r;
3258c2ecf20Sopenharmony_ci		res->name = dev_name(&dev->ofdev.dev);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci		if (macio_resource_quirks(np, res, index)) {
3288c2ecf20Sopenharmony_ci			memset(res, 0, sizeof(struct resource));
3298c2ecf20Sopenharmony_ci			continue;
3308c2ecf20Sopenharmony_ci		}
3318c2ecf20Sopenharmony_ci		/* Currently, we consider failure as harmless, this may
3328c2ecf20Sopenharmony_ci		 * change in the future, once I've found all the device
3338c2ecf20Sopenharmony_ci		 * tree bugs in older machines & worked around them
3348c2ecf20Sopenharmony_ci		 */
3358c2ecf20Sopenharmony_ci		if (insert_resource(parent_res, res)) {
3368c2ecf20Sopenharmony_ci			printk(KERN_WARNING "Can't request resource "
3378c2ecf20Sopenharmony_ci			       "%d for MacIO device %s\n",
3388c2ecf20Sopenharmony_ci			       index, dev_name(&dev->ofdev.dev));
3398c2ecf20Sopenharmony_ci		}
3408c2ecf20Sopenharmony_ci	}
3418c2ecf20Sopenharmony_ci	dev->n_resources = index;
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci/**
3458c2ecf20Sopenharmony_ci * macio_add_one_device - Add one device from OF node to the device tree
3468c2ecf20Sopenharmony_ci * @chip: pointer to the macio_chip holding the device
3478c2ecf20Sopenharmony_ci * @np: pointer to the device node in the OF tree
3488c2ecf20Sopenharmony_ci * @in_bay: set to 1 if device is part of a media-bay
3498c2ecf20Sopenharmony_ci *
3508c2ecf20Sopenharmony_ci * When media-bay is changed to hotswap drivers, this function will
3518c2ecf20Sopenharmony_ci * be exposed to the bay driver some way...
3528c2ecf20Sopenharmony_ci */
3538c2ecf20Sopenharmony_cistatic struct macio_dev * macio_add_one_device(struct macio_chip *chip,
3548c2ecf20Sopenharmony_ci					       struct device *parent,
3558c2ecf20Sopenharmony_ci					       struct device_node *np,
3568c2ecf20Sopenharmony_ci					       struct macio_dev *in_bay,
3578c2ecf20Sopenharmony_ci					       struct resource *parent_res)
3588c2ecf20Sopenharmony_ci{
3598c2ecf20Sopenharmony_ci	char name[MAX_NODE_NAME_SIZE + 1];
3608c2ecf20Sopenharmony_ci	struct macio_dev *dev;
3618c2ecf20Sopenharmony_ci	const u32 *reg;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	if (np == NULL)
3648c2ecf20Sopenharmony_ci		return NULL;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
3678c2ecf20Sopenharmony_ci	if (!dev)
3688c2ecf20Sopenharmony_ci		return NULL;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	dev->bus = &chip->lbus;
3718c2ecf20Sopenharmony_ci	dev->media_bay = in_bay;
3728c2ecf20Sopenharmony_ci	dev->ofdev.dev.of_node = np;
3738c2ecf20Sopenharmony_ci	dev->ofdev.archdata.dma_mask = 0xffffffffUL;
3748c2ecf20Sopenharmony_ci	dev->ofdev.dev.dma_mask = &dev->ofdev.archdata.dma_mask;
3758c2ecf20Sopenharmony_ci	dev->ofdev.dev.coherent_dma_mask = dev->ofdev.archdata.dma_mask;
3768c2ecf20Sopenharmony_ci	dev->ofdev.dev.parent = parent;
3778c2ecf20Sopenharmony_ci	dev->ofdev.dev.bus = &macio_bus_type;
3788c2ecf20Sopenharmony_ci	dev->ofdev.dev.release = macio_release_dev;
3798c2ecf20Sopenharmony_ci	dev->ofdev.dev.dma_parms = &dev->dma_parms;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	/* Standard DMA paremeters */
3828c2ecf20Sopenharmony_ci	dma_set_max_seg_size(&dev->ofdev.dev, 65536);
3838c2ecf20Sopenharmony_ci	dma_set_seg_boundary(&dev->ofdev.dev, 0xffffffff);
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci#if defined(CONFIG_PCI) && defined(CONFIG_DMA_OPS)
3868c2ecf20Sopenharmony_ci	/* Set the DMA ops to the ones from the PCI device, this could be
3878c2ecf20Sopenharmony_ci	 * fishy if we didn't know that on PowerMac it's always direct ops
3888c2ecf20Sopenharmony_ci	 * or iommu ops that will work fine
3898c2ecf20Sopenharmony_ci	 *
3908c2ecf20Sopenharmony_ci	 * To get all the fields, copy all archdata
3918c2ecf20Sopenharmony_ci	 */
3928c2ecf20Sopenharmony_ci	dev->ofdev.dev.archdata = chip->lbus.pdev->dev.archdata;
3938c2ecf20Sopenharmony_ci	dev->ofdev.dev.dma_ops = chip->lbus.pdev->dev.dma_ops;
3948c2ecf20Sopenharmony_ci#endif /* CONFIG_PCI && CONFIG_DMA_OPS */
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci#ifdef DEBUG
3978c2ecf20Sopenharmony_ci	printk("preparing mdev @%p, ofdev @%p, dev @%p, kobj @%p\n",
3988c2ecf20Sopenharmony_ci	       dev, &dev->ofdev, &dev->ofdev.dev, &dev->ofdev.dev.kobj);
3998c2ecf20Sopenharmony_ci#endif
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	/* MacIO itself has a different reg, we use it's PCI base */
4028c2ecf20Sopenharmony_ci	snprintf(name, sizeof(name), "%pOFn", np);
4038c2ecf20Sopenharmony_ci	if (np == chip->of_node) {
4048c2ecf20Sopenharmony_ci		dev_set_name(&dev->ofdev.dev, "%1d.%08x:%.*s",
4058c2ecf20Sopenharmony_ci			     chip->lbus.index,
4068c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI
4078c2ecf20Sopenharmony_ci			(unsigned int)pci_resource_start(chip->lbus.pdev, 0),
4088c2ecf20Sopenharmony_ci#else
4098c2ecf20Sopenharmony_ci			0, /* NuBus may want to do something better here */
4108c2ecf20Sopenharmony_ci#endif
4118c2ecf20Sopenharmony_ci			MAX_NODE_NAME_SIZE, name);
4128c2ecf20Sopenharmony_ci	} else {
4138c2ecf20Sopenharmony_ci		reg = of_get_property(np, "reg", NULL);
4148c2ecf20Sopenharmony_ci		dev_set_name(&dev->ofdev.dev, "%1d.%08x:%.*s",
4158c2ecf20Sopenharmony_ci			     chip->lbus.index,
4168c2ecf20Sopenharmony_ci			     reg ? *reg : 0, MAX_NODE_NAME_SIZE, name);
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	/* Setup interrupts & resources */
4208c2ecf20Sopenharmony_ci	macio_setup_interrupts(dev);
4218c2ecf20Sopenharmony_ci	macio_setup_resources(dev, parent_res);
4228c2ecf20Sopenharmony_ci	macio_add_missing_resources(dev);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	/* Register with core */
4258c2ecf20Sopenharmony_ci	if (of_device_register(&dev->ofdev) != 0) {
4268c2ecf20Sopenharmony_ci		printk(KERN_DEBUG"macio: device registration error for %s!\n",
4278c2ecf20Sopenharmony_ci		       dev_name(&dev->ofdev.dev));
4288c2ecf20Sopenharmony_ci		put_device(&dev->ofdev.dev);
4298c2ecf20Sopenharmony_ci		return NULL;
4308c2ecf20Sopenharmony_ci	}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	return dev;
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic int macio_skip_device(struct device_node *np)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	return of_node_name_prefix(np, "battery") ||
4388c2ecf20Sopenharmony_ci	       of_node_name_prefix(np, "escc-legacy");
4398c2ecf20Sopenharmony_ci}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci/**
4428c2ecf20Sopenharmony_ci * macio_pci_add_devices - Adds sub-devices of mac-io to the device tree
4438c2ecf20Sopenharmony_ci * @chip: pointer to the macio_chip holding the devices
4448c2ecf20Sopenharmony_ci *
4458c2ecf20Sopenharmony_ci * This function will do the job of extracting devices from the
4468c2ecf20Sopenharmony_ci * Open Firmware device tree, build macio_dev structures and add
4478c2ecf20Sopenharmony_ci * them to the Linux device tree.
4488c2ecf20Sopenharmony_ci *
4498c2ecf20Sopenharmony_ci * For now, childs of media-bay are added now as well. This will
4508c2ecf20Sopenharmony_ci * change rsn though.
4518c2ecf20Sopenharmony_ci */
4528c2ecf20Sopenharmony_cistatic void macio_pci_add_devices(struct macio_chip *chip)
4538c2ecf20Sopenharmony_ci{
4548c2ecf20Sopenharmony_ci	struct device_node *np, *pnode;
4558c2ecf20Sopenharmony_ci	struct macio_dev *rdev, *mdev, *mbdev = NULL, *sdev = NULL;
4568c2ecf20Sopenharmony_ci	struct device *parent = NULL;
4578c2ecf20Sopenharmony_ci	struct resource *root_res = &iomem_resource;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	/* Add a node for the macio bus itself */
4608c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI
4618c2ecf20Sopenharmony_ci	if (chip->lbus.pdev) {
4628c2ecf20Sopenharmony_ci		parent = &chip->lbus.pdev->dev;
4638c2ecf20Sopenharmony_ci		root_res = &chip->lbus.pdev->resource[0];
4648c2ecf20Sopenharmony_ci	}
4658c2ecf20Sopenharmony_ci#endif
4668c2ecf20Sopenharmony_ci	pnode = of_node_get(chip->of_node);
4678c2ecf20Sopenharmony_ci	if (pnode == NULL)
4688c2ecf20Sopenharmony_ci		return;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	/* Add macio itself to hierarchy */
4718c2ecf20Sopenharmony_ci	rdev = macio_add_one_device(chip, parent, pnode, NULL, root_res);
4728c2ecf20Sopenharmony_ci	if (rdev == NULL)
4738c2ecf20Sopenharmony_ci		return;
4748c2ecf20Sopenharmony_ci	root_res = &rdev->resource[0];
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	/* First scan 1st level */
4778c2ecf20Sopenharmony_ci	for (np = NULL; (np = of_get_next_child(pnode, np)) != NULL;) {
4788c2ecf20Sopenharmony_ci		if (macio_skip_device(np))
4798c2ecf20Sopenharmony_ci			continue;
4808c2ecf20Sopenharmony_ci		of_node_get(np);
4818c2ecf20Sopenharmony_ci		mdev = macio_add_one_device(chip, &rdev->ofdev.dev, np, NULL,
4828c2ecf20Sopenharmony_ci					    root_res);
4838c2ecf20Sopenharmony_ci		if (mdev == NULL)
4848c2ecf20Sopenharmony_ci			of_node_put(np);
4858c2ecf20Sopenharmony_ci		else if (of_node_name_prefix(np, "media-bay"))
4868c2ecf20Sopenharmony_ci			mbdev = mdev;
4878c2ecf20Sopenharmony_ci		else if (of_node_name_prefix(np, "escc"))
4888c2ecf20Sopenharmony_ci			sdev = mdev;
4898c2ecf20Sopenharmony_ci	}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	/* Add media bay devices if any */
4928c2ecf20Sopenharmony_ci	if (mbdev) {
4938c2ecf20Sopenharmony_ci		pnode = mbdev->ofdev.dev.of_node;
4948c2ecf20Sopenharmony_ci		for (np = NULL; (np = of_get_next_child(pnode, np)) != NULL;) {
4958c2ecf20Sopenharmony_ci			if (macio_skip_device(np))
4968c2ecf20Sopenharmony_ci				continue;
4978c2ecf20Sopenharmony_ci			of_node_get(np);
4988c2ecf20Sopenharmony_ci			if (macio_add_one_device(chip, &mbdev->ofdev.dev, np,
4998c2ecf20Sopenharmony_ci						 mbdev,  root_res) == NULL)
5008c2ecf20Sopenharmony_ci				of_node_put(np);
5018c2ecf20Sopenharmony_ci		}
5028c2ecf20Sopenharmony_ci	}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	/* Add serial ports if any */
5058c2ecf20Sopenharmony_ci	if (sdev) {
5068c2ecf20Sopenharmony_ci		pnode = sdev->ofdev.dev.of_node;
5078c2ecf20Sopenharmony_ci		for (np = NULL; (np = of_get_next_child(pnode, np)) != NULL;) {
5088c2ecf20Sopenharmony_ci			if (macio_skip_device(np))
5098c2ecf20Sopenharmony_ci				continue;
5108c2ecf20Sopenharmony_ci			of_node_get(np);
5118c2ecf20Sopenharmony_ci			if (macio_add_one_device(chip, &sdev->ofdev.dev, np,
5128c2ecf20Sopenharmony_ci						 NULL, root_res) == NULL)
5138c2ecf20Sopenharmony_ci				of_node_put(np);
5148c2ecf20Sopenharmony_ci		}
5158c2ecf20Sopenharmony_ci	}
5168c2ecf20Sopenharmony_ci}
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci/**
5208c2ecf20Sopenharmony_ci * macio_register_driver - Registers a new MacIO device driver
5218c2ecf20Sopenharmony_ci * @drv: pointer to the driver definition structure
5228c2ecf20Sopenharmony_ci */
5238c2ecf20Sopenharmony_ciint macio_register_driver(struct macio_driver *drv)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci	/* initialize common driver fields */
5268c2ecf20Sopenharmony_ci	drv->driver.bus = &macio_bus_type;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	/* register with core */
5298c2ecf20Sopenharmony_ci	return driver_register(&drv->driver);
5308c2ecf20Sopenharmony_ci}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci/**
5338c2ecf20Sopenharmony_ci * macio_unregister_driver - Unregisters a new MacIO device driver
5348c2ecf20Sopenharmony_ci * @drv: pointer to the driver definition structure
5358c2ecf20Sopenharmony_ci */
5368c2ecf20Sopenharmony_civoid macio_unregister_driver(struct macio_driver *drv)
5378c2ecf20Sopenharmony_ci{
5388c2ecf20Sopenharmony_ci	driver_unregister(&drv->driver);
5398c2ecf20Sopenharmony_ci}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci/* Managed MacIO resources */
5428c2ecf20Sopenharmony_cistruct macio_devres {
5438c2ecf20Sopenharmony_ci	u32	res_mask;
5448c2ecf20Sopenharmony_ci};
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_cistatic void maciom_release(struct device *gendev, void *res)
5478c2ecf20Sopenharmony_ci{
5488c2ecf20Sopenharmony_ci	struct macio_dev *dev = to_macio_device(gendev);
5498c2ecf20Sopenharmony_ci	struct macio_devres *dr = res;
5508c2ecf20Sopenharmony_ci	int i, max;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	max = min(dev->n_resources, 32);
5538c2ecf20Sopenharmony_ci	for (i = 0; i < max; i++) {
5548c2ecf20Sopenharmony_ci		if (dr->res_mask & (1 << i))
5558c2ecf20Sopenharmony_ci			macio_release_resource(dev, i);
5568c2ecf20Sopenharmony_ci	}
5578c2ecf20Sopenharmony_ci}
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ciint macio_enable_devres(struct macio_dev *dev)
5608c2ecf20Sopenharmony_ci{
5618c2ecf20Sopenharmony_ci	struct macio_devres *dr;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	dr = devres_find(&dev->ofdev.dev, maciom_release, NULL, NULL);
5648c2ecf20Sopenharmony_ci	if (!dr) {
5658c2ecf20Sopenharmony_ci		dr = devres_alloc(maciom_release, sizeof(*dr), GFP_KERNEL);
5668c2ecf20Sopenharmony_ci		if (!dr)
5678c2ecf20Sopenharmony_ci			return -ENOMEM;
5688c2ecf20Sopenharmony_ci	}
5698c2ecf20Sopenharmony_ci	return devres_get(&dev->ofdev.dev, dr, NULL, NULL) != NULL;
5708c2ecf20Sopenharmony_ci}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_cistatic struct macio_devres * find_macio_dr(struct macio_dev *dev)
5738c2ecf20Sopenharmony_ci{
5748c2ecf20Sopenharmony_ci	return devres_find(&dev->ofdev.dev, maciom_release, NULL, NULL);
5758c2ecf20Sopenharmony_ci}
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci/**
5788c2ecf20Sopenharmony_ci *	macio_request_resource - Request an MMIO resource
5798c2ecf20Sopenharmony_ci * 	@dev: pointer to the device holding the resource
5808c2ecf20Sopenharmony_ci *	@resource_no: resource number to request
5818c2ecf20Sopenharmony_ci *	@name: resource name
5828c2ecf20Sopenharmony_ci *
5838c2ecf20Sopenharmony_ci *	Mark  memory region number @resource_no associated with MacIO
5848c2ecf20Sopenharmony_ci *	device @dev as being reserved by owner @name.  Do not access
5858c2ecf20Sopenharmony_ci *	any address inside the memory regions unless this call returns
5868c2ecf20Sopenharmony_ci *	successfully.
5878c2ecf20Sopenharmony_ci *
5888c2ecf20Sopenharmony_ci *	Returns 0 on success, or %EBUSY on error.  A warning
5898c2ecf20Sopenharmony_ci *	message is also printed on failure.
5908c2ecf20Sopenharmony_ci */
5918c2ecf20Sopenharmony_ciint macio_request_resource(struct macio_dev *dev, int resource_no,
5928c2ecf20Sopenharmony_ci			   const char *name)
5938c2ecf20Sopenharmony_ci{
5948c2ecf20Sopenharmony_ci	struct macio_devres *dr = find_macio_dr(dev);
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	if (macio_resource_len(dev, resource_no) == 0)
5978c2ecf20Sopenharmony_ci		return 0;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	if (!request_mem_region(macio_resource_start(dev, resource_no),
6008c2ecf20Sopenharmony_ci				macio_resource_len(dev, resource_no),
6018c2ecf20Sopenharmony_ci				name))
6028c2ecf20Sopenharmony_ci		goto err_out;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	if (dr && resource_no < 32)
6058c2ecf20Sopenharmony_ci		dr->res_mask |= 1 << resource_no;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	return 0;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_cierr_out:
6108c2ecf20Sopenharmony_ci	printk (KERN_WARNING "MacIO: Unable to reserve resource #%d:%lx@%lx"
6118c2ecf20Sopenharmony_ci		" for device %s\n",
6128c2ecf20Sopenharmony_ci		resource_no,
6138c2ecf20Sopenharmony_ci		macio_resource_len(dev, resource_no),
6148c2ecf20Sopenharmony_ci		macio_resource_start(dev, resource_no),
6158c2ecf20Sopenharmony_ci		dev_name(&dev->ofdev.dev));
6168c2ecf20Sopenharmony_ci	return -EBUSY;
6178c2ecf20Sopenharmony_ci}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci/**
6208c2ecf20Sopenharmony_ci * macio_release_resource - Release an MMIO resource
6218c2ecf20Sopenharmony_ci * @dev: pointer to the device holding the resource
6228c2ecf20Sopenharmony_ci * @resource_no: resource number to release
6238c2ecf20Sopenharmony_ci */
6248c2ecf20Sopenharmony_civoid macio_release_resource(struct macio_dev *dev, int resource_no)
6258c2ecf20Sopenharmony_ci{
6268c2ecf20Sopenharmony_ci	struct macio_devres *dr = find_macio_dr(dev);
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	if (macio_resource_len(dev, resource_no) == 0)
6298c2ecf20Sopenharmony_ci		return;
6308c2ecf20Sopenharmony_ci	release_mem_region(macio_resource_start(dev, resource_no),
6318c2ecf20Sopenharmony_ci			   macio_resource_len(dev, resource_no));
6328c2ecf20Sopenharmony_ci	if (dr && resource_no < 32)
6338c2ecf20Sopenharmony_ci		dr->res_mask &= ~(1 << resource_no);
6348c2ecf20Sopenharmony_ci}
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci/**
6378c2ecf20Sopenharmony_ci *	macio_request_resources - Reserve all memory resources
6388c2ecf20Sopenharmony_ci *	@dev: MacIO device whose resources are to be reserved
6398c2ecf20Sopenharmony_ci *	@name: Name to be associated with resource.
6408c2ecf20Sopenharmony_ci *
6418c2ecf20Sopenharmony_ci *	Mark all memory regions associated with MacIO device @dev as
6428c2ecf20Sopenharmony_ci *	being reserved by owner @name.  Do not access any address inside
6438c2ecf20Sopenharmony_ci *	the memory regions unless this call returns successfully.
6448c2ecf20Sopenharmony_ci *
6458c2ecf20Sopenharmony_ci *	Returns 0 on success, or %EBUSY on error.  A warning
6468c2ecf20Sopenharmony_ci *	message is also printed on failure.
6478c2ecf20Sopenharmony_ci */
6488c2ecf20Sopenharmony_ciint macio_request_resources(struct macio_dev *dev, const char *name)
6498c2ecf20Sopenharmony_ci{
6508c2ecf20Sopenharmony_ci	int i;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	for (i = 0; i < dev->n_resources; i++)
6538c2ecf20Sopenharmony_ci		if (macio_request_resource(dev, i, name))
6548c2ecf20Sopenharmony_ci			goto err_out;
6558c2ecf20Sopenharmony_ci	return 0;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_cierr_out:
6588c2ecf20Sopenharmony_ci	while(--i >= 0)
6598c2ecf20Sopenharmony_ci		macio_release_resource(dev, i);
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	return -EBUSY;
6628c2ecf20Sopenharmony_ci}
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci/**
6658c2ecf20Sopenharmony_ci *	macio_release_resources - Release reserved memory resources
6668c2ecf20Sopenharmony_ci *	@dev: MacIO device whose resources were previously reserved
6678c2ecf20Sopenharmony_ci */
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_civoid macio_release_resources(struct macio_dev *dev)
6708c2ecf20Sopenharmony_ci{
6718c2ecf20Sopenharmony_ci	int i;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	for (i = 0; i < dev->n_resources; i++)
6748c2ecf20Sopenharmony_ci		macio_release_resource(dev, i);
6758c2ecf20Sopenharmony_ci}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_cistatic int macio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
6818c2ecf20Sopenharmony_ci{
6828c2ecf20Sopenharmony_ci	struct device_node* np;
6838c2ecf20Sopenharmony_ci	struct macio_chip* chip;
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	if (ent->vendor != PCI_VENDOR_ID_APPLE)
6868c2ecf20Sopenharmony_ci		return -ENODEV;
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	/* Note regarding refcounting: We assume pci_device_to_OF_node() is
6898c2ecf20Sopenharmony_ci	 * ported to new OF APIs and returns a node with refcount incremented.
6908c2ecf20Sopenharmony_ci	 */
6918c2ecf20Sopenharmony_ci	np = pci_device_to_OF_node(pdev);
6928c2ecf20Sopenharmony_ci	if (np == NULL)
6938c2ecf20Sopenharmony_ci		return -ENODEV;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	/* The above assumption is wrong !!!
6968c2ecf20Sopenharmony_ci	 * fix that here for now until I fix the arch code
6978c2ecf20Sopenharmony_ci	 */
6988c2ecf20Sopenharmony_ci	of_node_get(np);
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	/* We also assume that pmac_feature will have done a get() on nodes
7018c2ecf20Sopenharmony_ci	 * stored in the macio chips array
7028c2ecf20Sopenharmony_ci	 */
7038c2ecf20Sopenharmony_ci	chip = macio_find(np, macio_unknown);
7048c2ecf20Sopenharmony_ci       	of_node_put(np);
7058c2ecf20Sopenharmony_ci	if (chip == NULL)
7068c2ecf20Sopenharmony_ci		return -ENODEV;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	/* XXX Need locking ??? */
7098c2ecf20Sopenharmony_ci	if (chip->lbus.pdev == NULL) {
7108c2ecf20Sopenharmony_ci		chip->lbus.pdev = pdev;
7118c2ecf20Sopenharmony_ci		chip->lbus.chip = chip;
7128c2ecf20Sopenharmony_ci		pci_set_drvdata(pdev, &chip->lbus);
7138c2ecf20Sopenharmony_ci		pci_set_master(pdev);
7148c2ecf20Sopenharmony_ci	}
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	printk(KERN_INFO "MacIO PCI driver attached to %s chipset\n",
7178c2ecf20Sopenharmony_ci		chip->name);
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	/*
7208c2ecf20Sopenharmony_ci	 * HACK ALERT: The WallStreet PowerBook and some OHare based machines
7218c2ecf20Sopenharmony_ci	 * have 2 macio ASICs. I must probe the "main" one first or IDE
7228c2ecf20Sopenharmony_ci	 * ordering will be incorrect. So I put on "hold" the second one since
7238c2ecf20Sopenharmony_ci	 * it seem to appear first on PCI
7248c2ecf20Sopenharmony_ci	 */
7258c2ecf20Sopenharmony_ci	if (chip->type == macio_gatwick || chip->type == macio_ohareII)
7268c2ecf20Sopenharmony_ci		if (macio_chips[0].lbus.pdev == NULL) {
7278c2ecf20Sopenharmony_ci			macio_on_hold = chip;
7288c2ecf20Sopenharmony_ci			return 0;
7298c2ecf20Sopenharmony_ci		}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	macio_pci_add_devices(chip);
7328c2ecf20Sopenharmony_ci	if (macio_on_hold && macio_chips[0].lbus.pdev != NULL) {
7338c2ecf20Sopenharmony_ci		macio_pci_add_devices(macio_on_hold);
7348c2ecf20Sopenharmony_ci		macio_on_hold = NULL;
7358c2ecf20Sopenharmony_ci	}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	return 0;
7388c2ecf20Sopenharmony_ci}
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_cistatic void macio_pci_remove(struct pci_dev* pdev)
7418c2ecf20Sopenharmony_ci{
7428c2ecf20Sopenharmony_ci	panic("removing of macio-asic not supported !\n");
7438c2ecf20Sopenharmony_ci}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci/*
7468c2ecf20Sopenharmony_ci * MacIO is matched against any Apple ID, it's probe() function
7478c2ecf20Sopenharmony_ci * will then decide wether it applies or not
7488c2ecf20Sopenharmony_ci */
7498c2ecf20Sopenharmony_cistatic const struct pci_device_id pci_ids[] = { {
7508c2ecf20Sopenharmony_ci	.vendor		= PCI_VENDOR_ID_APPLE,
7518c2ecf20Sopenharmony_ci	.device		= PCI_ANY_ID,
7528c2ecf20Sopenharmony_ci	.subvendor	= PCI_ANY_ID,
7538c2ecf20Sopenharmony_ci	.subdevice	= PCI_ANY_ID,
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	}, { /* end: all zeroes */ }
7568c2ecf20Sopenharmony_ci};
7578c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE (pci, pci_ids);
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci/* pci driver glue; this is a "new style" PCI driver module */
7608c2ecf20Sopenharmony_cistatic struct pci_driver macio_pci_driver = {
7618c2ecf20Sopenharmony_ci	.name		= (char *) "macio",
7628c2ecf20Sopenharmony_ci	.id_table	= pci_ids,
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	.probe		= macio_pci_probe,
7658c2ecf20Sopenharmony_ci	.remove		= macio_pci_remove,
7668c2ecf20Sopenharmony_ci};
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci#endif /* CONFIG_PCI */
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_cistatic int __init macio_module_init (void)
7718c2ecf20Sopenharmony_ci{
7728c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI
7738c2ecf20Sopenharmony_ci	int rc;
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	rc = pci_register_driver(&macio_pci_driver);
7768c2ecf20Sopenharmony_ci	if (rc)
7778c2ecf20Sopenharmony_ci		return rc;
7788c2ecf20Sopenharmony_ci#endif /* CONFIG_PCI */
7798c2ecf20Sopenharmony_ci	return 0;
7808c2ecf20Sopenharmony_ci}
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_cimodule_init(macio_module_init);
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ciEXPORT_SYMBOL(macio_register_driver);
7858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(macio_unregister_driver);
7868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(macio_dev_get);
7878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(macio_dev_put);
7888c2ecf20Sopenharmony_ciEXPORT_SYMBOL(macio_request_resource);
7898c2ecf20Sopenharmony_ciEXPORT_SYMBOL(macio_release_resource);
7908c2ecf20Sopenharmony_ciEXPORT_SYMBOL(macio_request_resources);
7918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(macio_release_resources);
7928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(macio_enable_devres);
7938c2ecf20Sopenharmony_ci
794