diff options
Diffstat (limited to 'bsps/sparc/shared/amba/ambapp.c')
-rw-r--r-- | bsps/sparc/shared/amba/ambapp.c | 455 |
1 files changed, 455 insertions, 0 deletions
diff --git a/bsps/sparc/shared/amba/ambapp.c b/bsps/sparc/shared/amba/ambapp.c new file mode 100644 index 0000000000..e3c777a5f5 --- /dev/null +++ b/bsps/sparc/shared/amba/ambapp.c @@ -0,0 +1,455 @@ +/* + * AMBA Plug & Play routines + * + * COPYRIGHT (c) 2011. + * Aeroflex Gaisler. + * + * 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. + */ + +#include <string.h> +#include <stdlib.h> +#include <string.h> + +#include <ambapp.h> +#include <bsp.h> + +#define AMBA_CONF_AREA 0xff000 +#define AMBA_AHB_SLAVE_CONF_AREA (1 << 11) +#define AMBA_APB_SLAVES 16 + +/* Allocate one AMBA device */ +static struct ambapp_dev *ambapp_alloc_dev_struct(int dev_type) +{ + struct ambapp_dev *dev; + size_t size = sizeof(*dev); + + if (dev_type == DEV_APB_SLV) + size += sizeof(struct ambapp_apb_info); + else + size += sizeof(struct ambapp_ahb_info); /* AHB */ + dev = (struct ambapp_dev *)calloc(1, size); + if (dev != NULL) + dev->dev_type = dev_type; + return dev; +} + +static unsigned int +ambapp_addr_from (struct ambapp_mmap *mmaps, unsigned int address) +{ + /* no translation? */ + if (!mmaps) + return address; + + while (mmaps->size) { + if ((address >= mmaps->remote_adr) && + (address <= (mmaps->remote_adr + (mmaps->size - 1)))) { + return (address - mmaps->remote_adr) + mmaps->local_adr; + } + mmaps++; + } + return 1; +} + +static void ambapp_ahb_dev_init( + unsigned int ioarea, + struct ambapp_mmap *mmaps, + struct ambapp_pnp_ahb *ahb, + struct ambapp_dev *dev, + int ahbidx + ) +{ + int bar; + struct ambapp_ahb_info *ahb_info; + unsigned int addr, mask, mbar; + + /* Setup device struct */ + dev->vendor = ambapp_pnp_vendor(ahb->id); + dev->device = ambapp_pnp_device(ahb->id); + ahb_info = DEV_TO_AHB(dev); + ahb_info->ver = ambapp_pnp_ver(ahb->id); + ahb_info->irq = ambapp_pnp_irq(ahb->id); + ahb_info->ahbidx = ahbidx; + ahb_info->custom[0] = (unsigned int)ahb->custom[0]; + ahb_info->custom[1] = (unsigned int)ahb->custom[1]; + ahb_info->custom[2] = (unsigned int)ahb->custom[2]; + + /* Memory BARs */ + for (bar=0; bar<4; bar++) { + mbar = ahb->mbar[bar]; + if (mbar == 0) { + addr = 0; + mask = 0; + } else { + addr = ambapp_pnp_start(mbar); + if (ambapp_pnp_mbar_type(mbar) == AMBA_TYPE_AHBIO) { + /* AHB I/O area is releative IO_AREA */ + addr = AMBA_TYPE_AHBIO_ADDR(addr, ioarea); + mask = (((unsigned int)(ambapp_pnp_mbar_mask(~mbar) << 8) | 0xff)) + 1; + } else { + /* AHB memory area, absolute address */ + addr = ambapp_addr_from(mmaps, addr); + mask = (~((unsigned int)(ambapp_pnp_mbar_mask(mbar) << 20))) + 1; + } + } + ahb_info->start[bar] = addr; + ahb_info->mask[bar] = mask; + ahb_info->type[bar] = ambapp_pnp_mbar_type(mbar); + } +} + +static void ambapp_apb_dev_init( + unsigned int base, + struct ambapp_mmap *mmaps, + struct ambapp_pnp_apb *apb, + struct ambapp_dev *dev, + int ahbidx + ) +{ + struct ambapp_apb_info *apb_info; + + /* Setup device struct */ + dev->vendor = ambapp_pnp_vendor(apb->id); + dev->device = ambapp_pnp_device(apb->id); + apb_info = DEV_TO_APB(dev); + apb_info->ver = ambapp_pnp_ver(apb->id); + apb_info->irq = ambapp_pnp_irq(apb->id); + apb_info->ahbidx = ahbidx; + apb_info->start = ambapp_pnp_apb_start(apb->iobar, base); + apb_info->mask = ambapp_pnp_apb_mask(apb->iobar); +} + +static int ambapp_add_ahbbus( + struct ambapp_bus *abus, + unsigned int ioarea + ) +{ + int i; + for (i=0; i<AHB_BUS_MAX; i++) { + if (abus->ahbs[i].ioarea == 0) { + abus->ahbs[i].ioarea = ioarea; + return i; + } else if (abus->ahbs[i].ioarea == ioarea) { + /* Bus already added */ + return -1; + } + } + return -1; +} + +/* Internal AMBA Scanning Function */ +static int ambapp_scan2( + struct ambapp_bus *abus, + unsigned int ioarea, + ambapp_memcpy_t memfunc, + struct ambapp_dev *parent, + struct ambapp_dev **root + ) +{ + struct ambapp_pnp_ahb *ahb, ahb_buf; + struct ambapp_pnp_apb *apb, apb_buf; + struct ambapp_dev *dev, *prev, *prevapb, *apbdev; + struct ambapp_ahb_info *ahb_info; + int maxloops = 64; + unsigned int apbbase, bridge_adr; + int i, j, ahbidx; + + *root = NULL; + + if (parent) { + /* scan first bus for 64 devices, rest for 16 devices */ + maxloops = 16; + } + + ahbidx = ambapp_add_ahbbus(abus, ioarea); + if (ahbidx < 0) { + /* Bus already scanned, stop */ + return 0; + } + + prev = parent; + + /* AHB MASTERS */ + ahb = (struct ambapp_pnp_ahb *) (ioarea | AMBA_CONF_AREA); + for (i = 0; i < maxloops; i++, ahb++) { + memfunc(&ahb_buf, ahb, sizeof(struct ambapp_pnp_ahb), abus); + if (ahb_buf.id == 0) + continue; + + /* An AHB device present here */ + dev = ambapp_alloc_dev_struct(DEV_AHB_MST); + if (!dev) + return -1; + + ambapp_ahb_dev_init(ioarea, abus->mmaps, &ahb_buf, dev, ahbidx); + + if (*root == NULL) + *root = dev; + + if (prev != parent) + prev->next = dev; + dev->prev = prev; + prev = dev; + } + + /* AHB SLAVES */ + ahb = (struct ambapp_pnp_ahb *) + (ioarea | AMBA_CONF_AREA | AMBA_AHB_SLAVE_CONF_AREA); + for (i = 0; i < maxloops; i++, ahb++) { + memfunc(&ahb_buf, ahb, sizeof(struct ambapp_pnp_ahb), abus); + if (ahb_buf.id == 0) + continue; + + /* An AHB device present here */ + dev = ambapp_alloc_dev_struct(DEV_AHB_SLV); + if (!dev) + return -1; + + ambapp_ahb_dev_init(ioarea, abus->mmaps, &ahb_buf, dev, ahbidx); + + if (*root == NULL) + *root = dev; + + if (prev != parent) + prev->next = dev; + dev->prev = prev; + prev = dev; + + ahb_info = DEV_TO_AHB(dev); + + /* Is it a AHB/AHB Bridge ? */ + if (((dev->device == GAISLER_AHB2AHB) && + (dev->vendor == VENDOR_GAISLER) && (ahb_info->ver > 0)) || + ((dev->device == GAISLER_L2CACHE) && + (dev->vendor == VENDOR_GAISLER)) || + ((dev->device == GAISLER_GRIOMMU) && + (dev->vendor == VENDOR_GAISLER))) { + /* AHB/AHB Bridge Found, recurse down the + * Bridge + */ + if (ahb_info->custom[1] != 0) { + bridge_adr = ambapp_addr_from(abus->mmaps, + ahb_info->custom[1]); + /* Scan next bus if not already scanned */ + if (ambapp_scan2(abus, bridge_adr, memfunc, dev, + &dev->children)) + return -1; + } + } else if ((dev->device == GAISLER_APBMST) && + (dev->vendor == VENDOR_GAISLER)) { + /* AHB/APB Bridge Found, add the APB devices to this + * AHB Slave's children + */ + prevapb = dev; + apbbase = ahb_info->start[0]; + + /* APB SLAVES */ + apb = (struct ambapp_pnp_apb *) + (apbbase | AMBA_CONF_AREA); + for (j=0; j<AMBA_APB_SLAVES; j++, apb++) { + memfunc(&apb_buf, apb, sizeof(*apb), abus); + if (apb_buf.id == 0) + continue; + + apbdev = ambapp_alloc_dev_struct(DEV_APB_SLV); + if (!apbdev) + return -1; + + ambapp_apb_dev_init(apbbase, abus->mmaps, + &apb_buf, apbdev, ahbidx); + + if (prevapb != dev) + prevapb->next = apbdev; + else + dev->children = apbdev; + apbdev->prev = prevapb; + prevapb = apbdev; + } + } + } + + /* Remember first AHB MST/SLV device on bus and Parent Bridge */ + abus->ahbs[ahbidx].dev = *root; + abus->ahbs[ahbidx].bridge = parent; + + return 0; +} + +/* Build AMBA Plug & Play device graph */ +int ambapp_scan( + struct ambapp_bus *abus, + unsigned int ioarea, + ambapp_memcpy_t memfunc, + struct ambapp_mmap *mmaps + ) +{ + memset(abus, 0, sizeof(*abus)); + abus->mmaps = mmaps; + + /* Default to memcpy() */ + if (!memfunc) + memfunc = (ambapp_memcpy_t)memcpy; + + return ambapp_scan2(abus, ioarea, memfunc, NULL, &abus->root); +} + +/* Match search options againt device */ +static int ambapp_dev_match_options(struct ambapp_dev *dev, unsigned int options, int vendor, int device) +{ + if ((((options & (OPTIONS_ALL_DEVS)) == OPTIONS_ALL_DEVS) || /* TYPE */ + ((options & OPTIONS_AHB_MSTS) && (dev->dev_type == DEV_AHB_MST)) || + ((options & OPTIONS_AHB_SLVS) && (dev->dev_type == DEV_AHB_SLV)) || + ((options & OPTIONS_APB_SLVS) && (dev->dev_type == DEV_APB_SLV))) && + ((vendor == -1) || (vendor == dev->vendor)) && /* VENDOR/DEV ID */ + ((device == -1) || (device == dev->device)) && + (((options & OPTIONS_ALL) == OPTIONS_ALL) || /* Allocated State */ + ((options & OPTIONS_FREE) && DEV_IS_FREE(dev)) || + ((options & OPTIONS_ALLOCATED) && DEV_IS_ALLOCATED(dev)))) { + return 1; + } + return 0; +} + +/* If device is an APB bridge all devices on the APB bridge is processed */ +static int ambapp_for_each_apb( + struct ambapp_dev *dev, + unsigned int options, + int vendor, + int device, + ambapp_func_t func, + void *arg) +{ + int index, ret; + struct ambapp_dev *apbslv; + + ret = 0; + if (dev->children && (dev->children->dev_type == DEV_APB_SLV)) { + /* Found a APB Bridge */ + index = 0; + apbslv = dev->children; + while (apbslv) { + if (ambapp_dev_match_options(apbslv, options, + vendor, device) == 1) { + ret = func(apbslv, index, arg); + if (ret != 0) + break; /* Signalled stopped */ + } + index++; + apbslv = apbslv->next; + } + } + + return ret; +} + +/* Traverse the prescanned device information */ +static int ambapp_for_each_dev( + struct ambapp_dev *root, + unsigned int options, + int vendor, + int device, + ambapp_func_t func, + void *arg) +{ + struct ambapp_dev *dev; + int ahb_slave = 0; + int index, ret; + + /* Start at device 'root' and process downwards. + * + * Breadth first search, search order + * 1. AHB MSTS + * 2. AHB SLVS + * 3. APB SLVS on primary bus + * 4. AHB/AHB secondary... -> step to 1. + */ + + /* AHB MST / AHB SLV */ + if (options & (OPTIONS_AHB_MSTS|OPTIONS_AHB_SLVS|OPTIONS_DEPTH_FIRST)) { + index = 0; + dev = root; + while (dev) { + if ((dev->dev_type == DEV_AHB_SLV) && !ahb_slave) { + /* First AHB Slave */ + ahb_slave = 1; + index = 0; + } + + /* Conditions must be fullfilled for function to be + * called + */ + if (ambapp_dev_match_options(dev, options, vendor, device) == 1) { + /* Correct device and vendor ID */ + ret = func(dev, index, arg); + if (ret != 0) + return ret; /* Signalled stopped */ + } + + if ((options & OPTIONS_DEPTH_FIRST) && (options & OPTIONS_APB_SLVS)) { + /* Check is APB bridge, and process all APB + * Slaves in that case + */ + ret = ambapp_for_each_apb(dev, options, vendor, device, func, arg); + if (ret != 0) + return ret; /* Signalled stopped */ + } + + if (options & OPTIONS_DEPTH_FIRST) { + if (dev->children && (dev->children->dev_type != DEV_APB_SLV)) { + /* Found AHB Bridge, recurse */ + ret = ambapp_for_each_dev(dev->children, options, vendor, device, + func, arg); + if (ret != 0) + return ret; + } + } + + index++; + dev = dev->next; + } + } + + /* Find APB Bridges */ + if ((options & OPTIONS_APB_SLVS) && !(options & OPTIONS_DEPTH_FIRST)) { + dev = root; + while (dev) { + /* Check is APB bridge, and process all APB Slaves in + * that case + */ + ret = ambapp_for_each_apb(dev, options, vendor, device, func, arg); + if (ret != 0) + return ret; /* Signalled stopped */ + dev = dev->next; + } + } + + /* Find AHB Bridges */ + if (!(options & OPTIONS_DEPTH_FIRST)) { + dev = root; + while (dev) { + if (dev->children && (dev->children->dev_type != DEV_APB_SLV)) { + /* Found AHB Bridge, recurse */ + ret = ambapp_for_each_dev(dev->children, options, vendor, device, + func, arg); + if (ret != 0) + return ret; + } + dev = dev->next; + } + } + + return 0; +} + +int ambapp_for_each( + struct ambapp_bus *abus, + unsigned int options, + int vendor, + int device, + ambapp_func_t func, + void *arg) +{ + return ambapp_for_each_dev(abus->root, options, vendor, device, func, arg); +} |