/*  General part of a AMBA Plug & Play bus driver.
 *
 *  COPYRIGHT (c) 2008.
 *  Cobham Gaisler AB.
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.rtems.org/license/LICENSE.
 *
 *  This is the general part of the different AMBA Plug & Play
 *  drivers. The drivers are wrappers around this driver, making
 *  the code size smaller for systems with multiple AMBA Plug & 
 *  Play buses.
 *
 *  The BSP define APBUART_INFO_AVAIL in order to add the info routine
 *  used for debugging.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <drvmgr/drvmgr.h>
#include <grlib/ambapp_bus.h>

#include <bsp.h>
#include <grlib/ambapp.h>
#include <rtems/bspIo.h>

/*#define DEBUG 1*/
#define DBG(args...)
/*#define DBG(args...) printk(args)*/

struct grlib_gptimer_regs {
	volatile unsigned int scaler_value;   /* common timer registers */
	volatile unsigned int scaler_reload;
	volatile unsigned int status;
	volatile unsigned int notused;
};

/* AMBA IMPLEMENTATION */

static int ambapp_bus_init1(struct drvmgr_bus *bus);
static int ambapp_bus_remove(struct drvmgr_bus *bus);
static int ambapp_unite(struct drvmgr_drv *drv, struct drvmgr_dev *dev);
static int ambapp_int_register(
	struct drvmgr_dev *dev,
	int index,
	const char *info,
	drvmgr_isr isr,
	void *arg);
static int ambapp_int_unregister(
	struct drvmgr_dev *dev,
	int index,
	drvmgr_isr isr,
	void *arg);
static int ambapp_int_clear(struct drvmgr_dev *dev, int index);
static int ambapp_int_mask(struct drvmgr_dev *dev, int index);
static int ambapp_int_unmask(struct drvmgr_dev *dev, int index);
static int ambapp_get_params(
	struct drvmgr_dev *dev,
	struct drvmgr_bus_params *params);
static int ambapp_bus_freq_get(
	struct drvmgr_dev *dev,
	int options,
	unsigned int *freq_hz);
#ifdef AMBAPPBUS_INFO_AVAIL
static void ambapp_dev_info(
	struct drvmgr_dev *,
	void (*print)(void *p, char *str),
	void *p);
#endif

#ifdef RTEMS_SMP
static int ambapp_int_set_affinity(
	struct drvmgr_dev *dev,
	int index,
	const Processor_mask *cpus);
#endif

static struct drvmgr_bus_ops ambapp_bus_ops =
{
	.init		= 
	{
		/* init1 */ ambapp_bus_init1,
		/* init2 */ NULL,
		/* init3 */ NULL,
		/* init4 */ NULL
	},
	.remove		= ambapp_bus_remove,
	.unite		= ambapp_unite,
	.int_register	= ambapp_int_register,
	.int_unregister	= ambapp_int_unregister,
	.int_clear	= ambapp_int_clear,
	.int_mask	= ambapp_int_mask,
#ifdef RTEMS_SMP
	.int_set_affinity = ambapp_int_set_affinity,
#endif
	.int_unmask	= ambapp_int_unmask,
	.get_params	= ambapp_get_params,
	.get_freq	= ambapp_bus_freq_get,
#ifdef AMBAPPBUS_INFO_AVAIL
	.get_info_dev	= ambapp_dev_info,
#endif
};

struct ambapp_priv {
	struct ambapp_config		*config;
};

static int ambapp_unite(struct drvmgr_drv *drv, struct drvmgr_dev *dev)
{
	struct amba_drv_info *adrv;
	struct amba_dev_id *id;
	struct amba_dev_info *amba;

	if ( !drv || !dev || !dev->parent )
		return 0;

	if ( ! (((drv->bus_type == DRVMGR_BUS_TYPE_AMBAPP) && (dev->parent->bus_type == DRVMGR_BUS_TYPE_AMBAPP)) ||
	       ((drv->bus_type == DRVMGR_BUS_TYPE_AMBAPP_RMAP) && (dev->parent->bus_type == DRVMGR_BUS_TYPE_AMBAPP_RMAP)) ||
	       ((drv->bus_type == DRVMGR_BUS_TYPE_AMBAPP_DIST) && (dev->parent->bus_type == DRVMGR_BUS_TYPE_AMBAPP_DIST)))
	   ) {
		return 0;
	}

	amba = (struct amba_dev_info *)dev->businfo;
	if ( !amba )
		return 0;

	adrv = (struct amba_drv_info *)drv;
	id = adrv->ids;
	if ( !id )
		return 0;
	while( id->vendor != 0 ) {
		if ( (id->vendor == amba->id.vendor) &&
		      (id->device == amba->id.device) ) {
			/* Unite device and driver */
			DBG("DRV 0x%x and DEV 0x%x united\n", (unsigned int)drv, (unsigned int)dev);
			return 1;
		}
		id++;
	}

	return 0;
}

static int ambapp_int_get(struct drvmgr_dev *dev, int index)
{
	int irq;

	/* Relative (positive) or absolute (negative) IRQ number */
	if ( index >= 0 ) {
		/* IRQ Index relative to Cores base IRQ */

		/* Get Base IRQ */
		irq = ((struct amba_dev_info *)dev->businfo)->info.irq;
		if ( irq < 0 )
			return -1;
		irq += index;
	} else {
		/* Absolute IRQ number */
		irq = -index;
	}
	return irq;
}

static int ambapp_int_register(
	struct drvmgr_dev *dev,
	int index,
	const char *info,
	drvmgr_isr isr,
	void *arg)
{
	struct ambapp_priv *priv;
	int irq;

	priv = dev->parent->priv;

	/* Get IRQ number from index and device information */
	irq = ambapp_int_get(dev, index);
	if ( irq < 0 ) 
		return DRVMGR_EINVAL;

	DBG("Register interrupt on 0x%x for dev 0x%x (IRQ: %d)\n",
		(unsigned int)dev->parent->dev, (unsigned int)dev, irq);

	if ( priv->config->ops->int_register ) {
		/* Let device override driver default */
		return priv->config->ops->int_register(dev, irq, info, isr, arg);
	} else {
		return DRVMGR_ENOSYS;
	}
}

static int ambapp_int_unregister(
	struct drvmgr_dev *dev,
	int index,
	drvmgr_isr isr,
	void *arg)
{
	struct ambapp_priv *priv;
	int irq;

	priv = dev->parent->priv;

	/* Get IRQ number from index and device information */
	irq = ambapp_int_get(dev, index);
	if ( irq < 0 ) 
		return DRVMGR_EINVAL;

	DBG("Unregister interrupt on 0x%x for dev 0x%x (IRQ: %d)\n",
		(unsigned int)dev->parent->dev, (unsigned int)dev, irq);

	if ( priv->config->ops->int_unregister ) {
		/* Let device override driver default */
		return priv->config->ops->int_unregister(dev, irq, isr, arg);
	} else {
		return DRVMGR_ENOSYS;
	}
}

static int ambapp_int_clear(
	struct drvmgr_dev *dev,
	int index)
{
	struct ambapp_priv *priv;
	int irq;

	priv = dev->parent->priv;

	/* Get IRQ number from index and device information */
	irq = ambapp_int_get(dev, index);
	if ( irq < 0 ) 
		return -1;

	DBG("Clear interrupt on 0x%x for dev 0x%x (IRQ: %d)\n",
		(unsigned int)dev->parent->dev, (unsigned int)dev, irq);

	if ( priv->config->ops->int_clear ) {
		/* Let device override driver default */
		return priv->config->ops->int_clear(dev, irq);
	} else {
		return DRVMGR_ENOSYS;
	}
}

static int ambapp_int_mask(
	struct drvmgr_dev *dev,
	int index)
{
	struct ambapp_priv *priv;
	int irq;

	priv = dev->parent->priv;

	/* Get IRQ number from index and device information */
	irq = ambapp_int_get(dev, index);
	if ( irq < 0 ) 
		return -1;

	DBG("MASK interrupt on 0x%x for dev 0x%x (IRQ: %d)\n",
		(unsigned int)dev->parent->dev, (unsigned int)dev, irq);

	if ( priv->config->ops->int_mask ) {
		/* Let device override driver default */
		return priv->config->ops->int_mask(dev, irq);
	} else {
		return DRVMGR_ENOSYS;
	}
}

static int ambapp_int_unmask(
	struct drvmgr_dev *dev,
	int index)
{
	struct ambapp_priv *priv;
	int irq;

	priv = dev->parent->priv;

	/* Get IRQ number from index and device information */
	irq = ambapp_int_get(dev, index);
	if ( irq < 0 ) 
		return DRVMGR_EINVAL;

	DBG("UNMASK interrupt on 0x%x for dev 0x%x (IRQ: %d)\n",
		(unsigned int)dev->parent->dev, (unsigned int)dev, irq);

	if ( priv->config->ops->int_unmask ) {
		/* Let device override driver default */
		return priv->config->ops->int_unmask(dev, irq);
	} else {
		return DRVMGR_ENOSYS;
	}
}

/* Assign frequency to an AMBA Bus */
void ambapp_bus_freq_register(
	struct drvmgr_dev *dev,
	int amba_interface,
	unsigned int freq_hz
	)
{
	struct ambapp_priv *priv = (struct ambapp_priv *)dev->parent->priv;
	struct ambapp_dev *adev;
	struct amba_dev_info *pnp = dev->businfo;

	if ( freq_hz == 0 )
		return;

	if ( amba_interface == DEV_AHB_MST ) {
		adev = (struct ambapp_dev *)
			((unsigned int)pnp->info.ahb_mst -
				sizeof(struct ambapp_dev));
	} else if ( amba_interface == DEV_AHB_SLV ) {
		adev = (struct ambapp_dev *)
			((unsigned int)pnp->info.ahb_slv -
				sizeof(struct ambapp_dev));
	} else if ( amba_interface == DEV_APB_SLV ) {
		adev = (struct ambapp_dev *)
			((unsigned int)pnp->info.apb_slv -
				sizeof(struct ambapp_dev));
	} else {
		return;
	}

	/* Calculate Top bus frequency from lower part. The frequency comes
	 * from some kind of hardware able to report local bus frequency.
	 */
	ambapp_freq_init(priv->config->abus, adev, freq_hz);
}

static int ambapp_bus_freq_get(
	struct drvmgr_dev *dev,
	int options,
	unsigned int *freq_hz)
{
	struct ambapp_priv *priv = (struct ambapp_priv *)dev->parent->priv;
	struct ambapp_dev *adev;
	struct amba_dev_info *pnp = dev->businfo;

	if ( options == DEV_AHB_MST ) {
		adev = (struct ambapp_dev *)
			((unsigned int)pnp->info.ahb_mst -
				sizeof(struct ambapp_dev));
	} else if ( options == DEV_AHB_SLV ) {
		adev = (struct ambapp_dev *)
			((unsigned int)pnp->info.ahb_slv -
				sizeof(struct ambapp_dev));
	} else if ( options == DEV_APB_SLV ) {
		adev = (struct ambapp_dev *)
			((unsigned int)pnp->info.apb_slv -
				sizeof(struct ambapp_dev));
	} else {
		*freq_hz = 0;
		return -1;
	}

	/* Calculate core/bus frequency from top most bus frequency. */
	*freq_hz = ambapp_freq_get(priv->config->abus, adev);
	if ( *freq_hz == 0 )
		return -1;
	return 0;
}

static int ambapp_get_params(
	struct drvmgr_dev *dev,
	struct drvmgr_bus_params *params)
{
	struct ambapp_priv *priv = dev->parent->priv;

	if ( priv->config->ops->get_params ) {
		/* Let device override driver default */
		return priv->config->ops->get_params(dev, params);
	} else {
		return -1;
	}
}

#ifdef AMBAPPBUS_INFO_AVAIL
static void ambapp_dev_info(
	struct drvmgr_dev *dev,
	void (*print_line)(void *p, char *str),
	void *p)
{
	struct amba_dev_info *devinfo;
	struct ambapp_core *core;
	char buf[64];
	int ver, i;
	char *str1, *str2, *str3;
	unsigned int ahbmst_freq, ahbslv_freq, apbslv_freq;

	if (!dev)
		return;

	devinfo = (struct amba_dev_info *)dev->businfo;
	if (!devinfo)
		return;
	core = &devinfo->info;

	print_line(p, "AMBA PnP DEVICE");

	str1 = ambapp_vendor_id2str(devinfo->id.vendor);
	if (str1 == NULL)
		str1 = "unknown";
	sprintf(buf, "VENDOR ID:   0x%04x  (%s)", devinfo->id.vendor, str1);
	print_line(p, buf);

	str1 = ambapp_device_id2str(devinfo->id.vendor, devinfo->id.device);
	if (str1 == NULL)
		str1 = "unknown";
	sprintf(buf, "DEVICE ID:   0x%04x  (%s)", devinfo->id.device, str1);
	print_line(p, buf);

	ahbmst_freq = ahbslv_freq = apbslv_freq = 0;
	ver = 0;
	str1 = str2 = str3 = "";
	if (core->ahb_mst) {
		str1 = "AHBMST ";
		ver = core->ahb_mst->ver;
		ambapp_bus_freq_get(dev, DEV_AHB_MST, &ahbmst_freq);
	}
	if (core->ahb_slv) {
		str2 = "AHBSLV ";
		ver = core->ahb_slv->ver;
		ambapp_bus_freq_get(dev, DEV_AHB_SLV, &ahbslv_freq);
	}
	if (core->apb_slv) {
		str3 = "APBSLV";
		ver = core->apb_slv->ver;
		ambapp_bus_freq_get(dev, DEV_APB_SLV, &apbslv_freq);
	}

	sprintf(buf, "IRQ:         %d", ambapp_int_get(dev, 0));
	print_line(p, buf);

	sprintf(buf, "VERSION:     0x%x", ver);
	print_line(p, buf);

	sprintf(buf, "ambapp_core: %p", core);
	print_line(p, buf);

	sprintf(buf, "interfaces:  %s%s%s", str1, str2, str3);
	print_line(p, buf);

	if (ahbmst_freq != 0) {
		sprintf(buf, "AHBMST FREQ: %dkHz", ahbmst_freq/1000);
		print_line(p, buf);
	}

	if (ahbslv_freq != 0) {
		sprintf(buf, "AHBSLV FREQ: %dkHz", ahbslv_freq/1000);
		print_line(p, buf);
	}

	if (apbslv_freq != 0) {
		sprintf(buf, "APBSLV FREQ: %dkHz", apbslv_freq/1000);
		print_line(p, buf);
	}

	if (core->ahb_slv) {
		for(i=0; i<4; i++) {
			if (core->ahb_slv->type[i] == AMBA_TYPE_AHBIO)
				str1 = " ahbio";
			else if (core->ahb_slv->type[i] == AMBA_TYPE_MEM)
				str1 = "ahbmem";
			else
				continue;
			sprintf(buf, " %s[%d]:  0x%08x-0x%08x", str1, i,
				core->ahb_slv->start[i],
				core->ahb_slv->start[i]+core->ahb_slv->mask[i]-1);
			print_line(p, buf);
		}
	}
	if (core->apb_slv) {
		sprintf(buf, "       apb:  0x%08x-0x%08x",
			core->apb_slv->start,
			core->apb_slv->start + core->apb_slv->mask - 1);
		print_line(p, buf);
	}
}
#endif

/* Fix device in last stage and/or register additional devices.
 * Function returns:
 *  0  Register device as normal
 *  1  Fixup function handles registration
 */
static int ambapp_dev_fixup(struct drvmgr_dev *dev, struct amba_dev_info *pnp)
{
	/* OCCAN speciality:
	 *  Mulitple cores are supported through the same amba AHB interface.
	 *  The number of "sub cores" can be detected by decoding the AMBA
	 *  Plug&Play version information. verion = ncores. A maximum of 8
	 *  sub cores are supported, each separeated with 0x100 inbetween.
	 *
	 *  Now, lets detect sub cores.
	 */
	if ( (pnp->info.device == GAISLER_CANAHB) &&
	     (pnp->info.vendor == VENDOR_GAISLER) ) {
		struct drvmgr_dev *newdev, *devs_to_register[8];
		struct amba_dev_info *pnpinfo;
		int subcores;
		int core;

		devs_to_register[0] = dev;
		subcores = (pnp->info.ahb_slv->ver & 0x7) + 1;
		for(core = 1; core < subcores; core++) {
			drvmgr_alloc_dev(&newdev, sizeof(*pnpinfo));
			memcpy(newdev, dev, sizeof(*newdev));
			pnpinfo = (struct amba_dev_info *)(newdev+1);
			memcpy(pnpinfo, pnp, sizeof(*pnp));
			pnpinfo->info.index = core;
			pnpinfo->info.irq += core;
			newdev->businfo = (void *)pnpinfo;

			devs_to_register[core] = newdev;
		}
		/* Register all CAN devices */
		for(core = 0; core < subcores; core++)
			drvmgr_dev_register(devs_to_register[core]);
		return 1;
	} else if ( (pnp->info.device == GAISLER_GPIO) &&
		    (pnp->info.vendor == VENDOR_GAISLER) ) {
		/* PIO[N] is connected to IRQ[N]. */
		pnp->info.irq = 0;
	}
	return 0;
}

struct ambapp_dev_reg_struct {
	struct ambapp_bus	*abus;
	struct drvmgr_bus	*bus;
	struct ambapp_dev	*ahb_mst;
	struct ambapp_dev	*ahb_slv;
	struct ambapp_dev	*apb_slv;
};

static void ambapp_core_register(
	struct ambapp_dev	*ahb_mst,
	struct ambapp_dev	*ahb_slv,
	struct ambapp_dev	*apb_slv,
	struct ambapp_dev_reg_struct *arg
	)
{
	struct drvmgr_dev *newdev;
	struct amba_dev_info *pnpinfo;
	unsigned short device;
	unsigned char vendor;
	int namelen;
	char buf[64];

	if ( ahb_mst ) {
		device = ahb_mst->device;
		vendor = ahb_mst->vendor;
	}else if ( ahb_slv ) {
		device = ahb_slv->device;
		vendor = ahb_slv->vendor;
	}else if( apb_slv ) {
		device = apb_slv->device;
		vendor = apb_slv->vendor;
	} else {
		DBG("NO DEV!\n");
		return;
	}

	DBG("CORE REGISTER DEV [%x:%x] MST: 0x%x, SLV: 0x%x, APB: 0x%x\n", vendor, device, (unsigned int)ahb_mst, (unsigned int)ahb_slv, (unsigned int)apb_slv);

	/* Get unique device name from AMBA data base by combining VENDOR and
	 * DEVICE short names
	 */
	namelen = ambapp_vendev_id2str(vendor, device, buf);

	/* Allocate a device */		
	drvmgr_alloc_dev(&newdev, sizeof(struct amba_dev_info) + namelen);
	pnpinfo = (struct amba_dev_info *)(newdev + 1);
	newdev->parent = arg->bus; /* Ourselfs */
	newdev->minor_drv = 0;
	newdev->minor_bus = 0;
	newdev->priv = NULL;
	newdev->drv = NULL;
	if (namelen > 0) {
		newdev->name = (char *)(pnpinfo + 1);
		strcpy(newdev->name, buf);
	} else {
		newdev->name = NULL;
	}
	newdev->next_in_drv = NULL;
	newdev->bus = NULL;

	/* Init PnP information, Assign Core interfaces with this device */
	pnpinfo->id.vendor = vendor;
	pnpinfo->id.device = device;
	pnpinfo->info.vendor = vendor;
	pnpinfo->info.device = device;
	pnpinfo->info.index = 0;
	if ( ahb_mst ) {
		pnpinfo->info.ahb_mst = (struct ambapp_ahb_info *)
						ahb_mst->devinfo;
		ambapp_alloc_dev(ahb_mst, (void *)newdev);
		if ( pnpinfo->info.ahb_mst->irq )
			pnpinfo->info.irq = pnpinfo->info.ahb_mst->irq;
	}
	if ( ahb_slv ) {
		pnpinfo->info.ahb_slv = (struct ambapp_ahb_info *)
					ahb_slv->devinfo;
		ambapp_alloc_dev(ahb_slv, (void *)newdev);
		if ( pnpinfo->info.ahb_slv->irq )
			pnpinfo->info.irq = pnpinfo->info.ahb_slv->irq;
	}
	if ( apb_slv ) {
		pnpinfo->info.apb_slv = (struct ambapp_apb_info *)
					apb_slv->devinfo;
		ambapp_alloc_dev(apb_slv, (void *)newdev);
		if ( pnpinfo->info.apb_slv->irq )
			pnpinfo->info.irq = pnpinfo->info.apb_slv->irq;
	}
	if ( pnpinfo->info.irq == 0 )
		pnpinfo->info.irq = -1; /* indicate no IRQ */

	/* Connect device with PnP information */
	newdev->businfo = (void *)pnpinfo;

	if ( ambapp_dev_fixup(newdev, pnpinfo) == 0 )
		drvmgr_dev_register(newdev); /* Register New Device */
}

/* Fix device registration.
 * Function returns:
 *  0  Register device as normal
 *  1  Fixup function handles registration
 */
static int ambapp_dev_register_fixup(struct ambapp_dev *dev, struct ambapp_dev_reg_struct *p)
{
	/* GR740 GRPCI2 speciality:
	 * - In the GR740 the APB_SLV is detected before the AHB_SLV
	 *   which makes the registration incorrect. We deal with it in 
	 *   this function. */
	if (    (dev->dev_type == DEV_APB_SLV) &&
		    (dev->device == GAISLER_GRPCI2) &&
		    (dev->vendor == VENDOR_GAISLER) &&
		    (p->ahb_slv == NULL) ) {
		DBG("GRPCI2 APB_SLV detected before AHB_SLV. Skipping APB_SLV registration.\n");
		return 1;
	}
	return 0;
}

/* Register one AMBA device */
static int ambapp_dev_register(struct ambapp_dev *dev, int index, void *arg)
{
	struct ambapp_dev_reg_struct *p = arg;

#ifdef DEBUG
	char *type;

	if ( dev->dev_type == DEV_AHB_MST )
		type = "AHB MST";
	else if ( dev->dev_type == DEV_AHB_SLV )
		type = "AHB SLV";
	else if ( dev->dev_type == DEV_APB_SLV )
		type = "APB SLV";
	
	DBG("Found [%d:%x:%x], %s\n", index, dev->vendor, dev->device, type);
#endif

	/* Fixup for device registration */
	if (ambapp_dev_register_fixup(dev, p)){
		return 0;
	}

	if ( dev->dev_type == DEV_AHB_MST ) {
		if ( p->ahb_mst ) {
			/* This should not happen */
			printk("ambapp_dev_register: ahb_mst not NULL!\n");
			exit(1);
		}

		/* Remember AHB Master */
		p->ahb_mst = dev;

		/* Find AHB Slave and APB slave for this Core */
		ambapp_for_each(p->abus, (OPTIONS_AHB_SLVS|OPTIONS_APB_SLVS|OPTIONS_FREE), dev->vendor, dev->device, ambapp_dev_register, p);

		ambapp_core_register(p->ahb_mst, p->ahb_slv, p->apb_slv, p);
		p->ahb_mst = p->ahb_slv = p->apb_slv = NULL;
		return 0;

	} else if ( dev->dev_type == DEV_AHB_SLV ) {
		if ( p->ahb_slv ) {
			/* Already got our AHB Slave interface */
			return 0;
		}

		/* Remember AHB Slave */
		p->ahb_slv = dev;

		if ( p->ahb_mst ) {
			/* Continue searching for APB Slave */
			return 0;
		} else {
			/* Find APB Slave interface for this Core */
			ambapp_for_each(p->abus, (OPTIONS_APB_SLVS|OPTIONS_FREE), dev->vendor, dev->device, ambapp_dev_register, p);

			ambapp_core_register(p->ahb_mst, p->ahb_slv, p->apb_slv, p);
			p->ahb_mst = p->ahb_slv = p->apb_slv = NULL;
			return 0;
		}
	} else if ( dev->dev_type == DEV_APB_SLV ) {
		if ( p->apb_slv ) {
			/* This should not happen */
			printk("ambapp_dev_register: apb_slv not NULL!\n");
			exit(1);
		}
		/* Remember APB Slave */
		p->apb_slv = dev;

		if ( p->ahb_mst || p->ahb_slv ) {
			/* Stop scanning */
			return 1;
		} else {
			ambapp_core_register(p->ahb_mst, p->ahb_slv, p->apb_slv, p);
			p->ahb_mst = p->ahb_slv = p->apb_slv = NULL;
			return 0;
		}
	}

	return 0;
}

/* Register all AMBA devices available on the AMBAPP bus */
static int ambapp_ids_register(struct drvmgr_bus *bus)
{
	struct ambapp_priv *priv = bus->priv;
	struct ambapp_bus *abus;
	struct ambapp_dev_reg_struct arg;

	DBG("ambapp_ids_register:\n");

	memset(&arg, 0, sizeof(arg));

	abus = priv->config->abus;
	arg.abus = abus;
	arg.bus = bus;

	/* Combine the AHB MST, AHB SLV and APB SLV interfaces of a core. A core has often more than
	 * one interface. A core can not have more than one interface of the same type.
	 */
	ambapp_for_each(abus, (OPTIONS_ALL_DEVS|OPTIONS_FREE), -1, -1, ambapp_dev_register, &arg);

#ifdef DEBUG
	ambapp_print(abus->root, 1);
#endif

	return DRVMGR_OK;
}

/*** DEVICE FUNCTIONS ***/

int ambapp_bus_register(struct drvmgr_dev *dev, struct ambapp_config *config)
{
	struct ambapp_priv *priv;

	if ( !config || !config->ops )
		return DRVMGR_OK;

	DBG("AMBAPP BUS: initializing\n");

	/* Register BUS */
	drvmgr_alloc_bus(&dev->bus, sizeof(struct ambapp_priv));
	priv = (struct ambapp_priv *)(dev->bus + 1);
	priv->config = config;
	if ( priv->config->bus_type == DRVMGR_BUS_TYPE_AMBAPP_DIST )
		dev->bus->bus_type = DRVMGR_BUS_TYPE_AMBAPP_DIST;
	else if ( priv->config->bus_type == DRVMGR_BUS_TYPE_AMBAPP_RMAP )
		dev->bus->bus_type = DRVMGR_BUS_TYPE_AMBAPP_RMAP;
	else
		dev->bus->bus_type = DRVMGR_BUS_TYPE_AMBAPP;
	dev->bus->next = NULL;
	dev->bus->dev = dev;
	dev->bus->priv = priv;
	dev->bus->children = NULL;
	dev->bus->ops = &ambapp_bus_ops;
	dev->bus->funcs = config->funcs;
	dev->bus->dev_cnt = 0;
	dev->bus->reslist = NULL;
	dev->bus->maps_up = config->maps_up;
	dev->bus->maps_down = config->maps_down;

	/* Add resource configuration */
	if ( priv->config->resources )
		drvmgr_bus_res_add(dev->bus, priv->config->resources);

	drvmgr_bus_register(dev->bus);

	return DRVMGR_OK;
}

/*** BUS INITIALIZE FUNCTIONS ***/

/* Initialize the bus, register devices on this bus */
static int ambapp_bus_init1(struct drvmgr_bus *bus)
{
	/* Initialize the bus, register devices on this bus */
	return ambapp_ids_register(bus);
}

static int ambapp_bus_remove(struct drvmgr_bus *bus)
{
	return DRVMGR_OK;
}

#ifdef RTEMS_SMP
static int ambapp_int_set_affinity(
	struct drvmgr_dev *dev,
	int index,
	const Processor_mask *cpus)
{
	struct ambapp_priv *priv;
	int irq;

	priv = dev->parent->priv;

	/* Get IRQ number from index and device information */
	irq = ambapp_int_get(dev, index);
	if (irq < 0)
		return DRVMGR_EINVAL;

	DBG("Set interrupt affinity on 0x%x for dev 0x%x (IRQ: %d)\n",
		(unsigned int)dev->parent->dev, (unsigned int)dev, irq);

	if (priv->config->ops->int_set_affinity) {
		/* Let device override driver default */
		return priv->config->ops->int_set_affinity(dev, irq, cpus);
	} else {
		return DRVMGR_ENOSYS;
	}
}
#endif
