/* 2009-08-20: File added by Sony Corporation */
/*
 * ahb.c - ahb 'pseudo' bus
 *
 * Copyright (c) 2002-3 Patrick Mochel
 * Copyright (c) 2002-3 Open Source Development Labs
 * Copyright (c) NEC Electronics Corporation 2006
 *
 * This file is released under the GPLv2
 *
 * Please see Documentation/driver-model/platform.txt for more
 * information.
 */

#include <linux/device.h>
#include <linux/module.h>
#include <linux/init.h>

#include <asm/arch/ahb.h>

static struct platform_device gshark_device = {
	.name = "gshark",
	.id = -1,
};

static struct platform_device fap_device = {
	.name = "fap",
	.id = -1,
};

static struct platform_device *ahb_devs[] = {
	&gshark_device,
	&fap_device,
};

struct device ahb_bus = {
	.bus_id = "ahb",
};

struct resource *ahb_get_resource(struct platform_device *dev,
				  unsigned int type, unsigned int num)
{
	return platform_get_resource(dev, type, num);
}

int ahb_get_irq(struct platform_device *dev, unsigned int num)
{
	return platform_get_irq(dev, num);
}

static void ahb_device_release(struct device *dev)
{
}

/**
 *	ahb_add_devices - add a numbers of ahb devices
 *	@devs: array of ahb devices to add
 *	@num: number of ahb devices in array
 */
int ahb_add_devices(struct platform_device **devs, int num)
{
	int i, ret = 0;

	for (i = 0; i < num; i++) {
		ret = ahb_device_register(devs[i]);
		if (ret) {
			while (--i >= 0) {
				ahb_device_unregister(devs[i]);
			}
			break;
		}
	}

	return ret;
}

/**
 *	ahb_device_register - add a ahb-level device
 *	@dev:	ahb device we're adding
 *
 */
int ahb_device_register(struct platform_device *pdev)
{
	int i, ret = 0;

	if (!pdev) {
		return -EINVAL;
	}

	if (!pdev->dev.parent) {
		pdev->dev.parent = &ahb_bus;
	}

	pdev->dev.release = ahb_device_release;
	pdev->dev.bus = &ahb_bus_type;

	if (pdev->id != -1) {
		snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s%u", pdev->name,
			 pdev->id);
	} else {
		strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);
	}

	for (i = 0; i < pdev->num_resources; i++) {
		struct resource *p, *r = &pdev->resource[i];

		if (r->name == NULL) {
			r->name = pdev->dev.bus_id;
		}

		p = NULL;
		if (r->flags & IORESOURCE_MEM) {
			p = &iomem_resource;
		} else if (r->flags & IORESOURCE_IO) {
			p = &ioport_resource;
		}

		if (p && request_resource(p, r)) {
			printk(KERN_ERR
			       "%s: failed to claim resource %d\n",
			       pdev->dev.bus_id, i);
			ret = -EBUSY;
			goto failed;
		}
	}

	pr_debug("Registering ahb device '%s'. Parent at %s\n",
		 pdev->dev.bus_id, pdev->dev.parent->bus_id);

	ret = device_register(&pdev->dev);
	if (ret == 0) {
		return ret;
	}

      failed:
	while (--i >= 0) {
		if (pdev->resource[i].flags & (IORESOURCE_MEM | IORESOURCE_IO)) {
			release_resource(&pdev->resource[i]);
		}
	}
	return ret;
}

void ahb_device_unregister(struct platform_device *pdev)
{
	platform_device_unregister(pdev);
}

int ahb_driver_register(struct device_driver *drv)
{
	drv->bus = &ahb_bus_type;
	return driver_register(drv);
}

void ahb_driver_unregister(struct device_driver *drv)
{
	driver_unregister(drv);
}

struct bus_type ahb_bus_type = {
	.name = "ahb",
};

static int __init ahb_bus_init(void)
{
	int ret;

	ret = device_register(&ahb_bus);
	if (ret != 0) {
		return ret;
	}
	ahb_bus.release = ahb_device_release;

	ahb_bus_type.match = platform_bus_type.match;
	ahb_bus_type.suspend = platform_bus_type.suspend;
	ahb_bus_type.resume = platform_bus_type.resume;
	ret = bus_register(&ahb_bus_type);
	if (ret != 0) {
		goto exit_1;
	}

	ret = ahb_add_devices(ahb_devs, ARRAY_SIZE(ahb_devs));
	if (ret != 0) {
		goto exit_2;
	}

	return ret;

      exit_2:
	bus_unregister(&ahb_bus_type);
      exit_1:
	device_unregister(&ahb_bus);

	return ret;
}

static void __exit ahb_bus_exit(void)
{
	int n;

	n = ARRAY_SIZE(ahb_devs);
	while (--n >= 0) {
		ahb_device_unregister(ahb_devs[n]);
	}

	bus_unregister(&ahb_bus_type);
	device_unregister(&ahb_bus);
}

module_init(ahb_bus_init);
module_exit(ahb_bus_exit);

EXPORT_SYMBOL(ahb_bus);
EXPORT_SYMBOL(ahb_bus_type);
EXPORT_SYMBOL(ahb_device_register);
EXPORT_SYMBOL(ahb_device_unregister);
EXPORT_SYMBOL(ahb_get_irq);
EXPORT_SYMBOL(ahb_get_resource);
EXPORT_SYMBOL(ahb_driver_register);
EXPORT_SYMBOL(ahb_driver_unregister);

MODULE_LICENSE("GPL");
