diff options
Diffstat (limited to 'libgloss/sparc_leon/pnpinit_malloc.c')
-rw-r--r-- | libgloss/sparc_leon/pnpinit_malloc.c | 679 |
1 files changed, 679 insertions, 0 deletions
diff --git a/libgloss/sparc_leon/pnpinit_malloc.c b/libgloss/sparc_leon/pnpinit_malloc.c new file mode 100644 index 000000000..ed7640588 --- /dev/null +++ b/libgloss/sparc_leon/pnpinit_malloc.c @@ -0,0 +1,679 @@ +/* + * Copyright (c) 2011 Aeroflex Gaisler + * + * BSD license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#include <asm-leon/amba.h> +#undef AMBA_TYPE_AHBIO_ADDR +#include <asm-leon/lambapp.h> +#include <string.h> + +#define AMBA_CONF_AREA 0xff000 +#define AMBA_AHB_SLAVE_CONF_AREA (1 << 11) +#define AMBA_APB_SLAVES 16 + +#define DPRINTF(p) printf p + +/* Allocate */ +struct ambapp_dev_hdr * +ambapp_alloc_dev_struct (int dev_type) +{ + int size = sizeof (struct ambapp_dev_hdr); + struct ambapp_dev_hdr *dev; + + if (dev_type == DEV_APB_SLV) + { + size += sizeof (struct ambapp_apb_info); + } + else + { + /* AHB */ + size += sizeof (struct ambapp_ahb_info); + } + dev = malloc (size); + if (dev == NULL) + return NULL; + memset (dev, 0, size); + dev->devinfo = (void *) (dev + 1); + dev->dev_type = dev_type; + return dev; +} + +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; +} + +void +ambapp_ahb_dev_init (unsigned int ioarea, + struct ambapp_mmap *mmaps, + struct ambapp_pnp_ahb *ahb, struct ambapp_dev_hdr *dev) +{ + 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->devinfo; + ahb_info->ver = ambapp_pnp_ver (ahb->id); + ahb_info->irq = ambapp_pnp_irq (ahb->id); + 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]; + + DPRINTF (("+AHB device %d:%d\n", dev->device, dev->vendor)); + + /* 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; + } +} + +void +ambapp_apb_dev_init (unsigned int base, + struct ambapp_mmap *mmaps, + struct ambapp_pnp_apb *apb, struct ambapp_dev_hdr *dev) +{ + 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->devinfo; + apb_info->ver = ambapp_pnp_ver (apb->id); + apb_info->irq = ambapp_pnp_irq (apb->id); + apb_info->start = ambapp_pnp_apb_start (apb->iobar, base); + apb_info->mask = ambapp_pnp_apb_mask (apb->iobar); + + DPRINTF (("+APB device %d:%d\n", dev->device, dev->vendor)); + + +} + +#define MAX_NUM_BUSES 16 +void +ambapp_add_scanned_bus (unsigned int *ioareas, unsigned int ioarea) +{ + int i; + for (i = 0; i < MAX_NUM_BUSES; i++) + { + if (ioareas[i] == 0) + { + ioareas[i] = ioarea; + return; + } + } +} + +int +ambapp_has_been_scanned (unsigned int *ioareas, unsigned int ioarea) +{ + int i; + if (!ioareas) + return 0; + + for (i = 0; i < MAX_NUM_BUSES; i++) + { + if (ioareas[i] == 0) + { + break; + } + else if (ioareas[i] == ioarea) + { + return 1; + } + } + return 0; +} + +int +ambapp_scan (unsigned int ioarea, + struct ambapp_dev_hdr *parent, + struct ambapp_mmap *mmaps, + void *(*memfunc) (void *dest, const void *src, int n), + struct ambapp_dev_hdr **root, void *internal) +{ + struct ambapp_pnp_ahb *ahb, ahb_buf; + struct ambapp_pnp_apb *apb, apb_buf; + struct ambapp_dev_hdr *dev, *prev, *prevapb, *apbdev; + struct ambapp_ahb_info *ahb_info; + int maxloops = 64; + unsigned int apbbase, bridge_address; + int i, j; + + DPRINTF (("Scan at 0x%08x\n", ioarea)); + + /* Default to memcpy() */ + if (!memfunc) + memfunc = (void *(*)(void *dest, const void *src, int n)) memcpy; + + *root = NULL; + + if (parent) + { + /* scan first bus for 64 devices, rest for 16 devices */ + maxloops = 16; + } + else + { + DPRINTF (("+(malloc:")); + internal = malloc (sizeof (unsigned int) * MAX_NUM_BUSES); + DPRINTF (("0x%x)\n", internal)); + + if (!internal) + return -1; + memset (internal, 0, sizeof (unsigned int) * MAX_NUM_BUSES); + + ambapp_add_scanned_bus (internal, ioarea); + } + + prev = parent; + + /* AHB MASTERS */ + ahb = (struct ambapp_pnp_ahb *) (ioarea | AMBA_CONF_AREA); + for (i = 0; i < maxloops; i++) + { + memfunc (&ahb_buf, ahb, sizeof (struct ambapp_pnp_ahb)); + if (ahb_buf.id != 0) + { + /* A AHB device present here */ + dev = ambapp_alloc_dev_struct (DEV_AHB_MST); + if (!dev) + return -1; + + ambapp_ahb_dev_init (ioarea, mmaps, &ahb_buf, dev); + + if (*root == NULL) + *root = dev; + + if (prev != parent) + prev->next = dev; + dev->prev = prev; + prev = dev; + } + ahb++; + } + + /* AHB SLAVES */ + ahb = + (struct ambapp_pnp_ahb *) (ioarea | AMBA_CONF_AREA | + AMBA_AHB_SLAVE_CONF_AREA); + for (i = 0; i < maxloops; i++) + { + memfunc (&ahb_buf, ahb, sizeof (struct ambapp_pnp_ahb)); + if (ahb_buf.id != 0) + { + /* A AHB device present here */ + dev = ambapp_alloc_dev_struct (DEV_AHB_SLV); + if (!dev) + return -1; + + ambapp_ahb_dev_init (ioarea, mmaps, &ahb_buf, dev); + + if (prev != parent) + prev->next = dev; + dev->prev = prev; + prev = dev; + + /* Is it a AHB/AHB Bridge ? */ + if ((dev->device == GAISLER_AHB2AHB) + && (dev->vendor == VENDOR_GAISLER)) + { + /* AHB/AHB Bridge Found, recurse down the Bridge */ + ahb_info = dev->devinfo; + if (ahb_info->ver) + { + bridge_address = + ambapp_addr_from (mmaps, ahb_info->custom[1]); + + DPRINTF (("+(AHBAHB:0x%x)\n", bridge_address)); + + /* Makes sure bus only scanned once */ + if (ambapp_has_been_scanned (internal, bridge_address) == + NULL) + { + ambapp_add_scanned_bus (internal, bridge_address); + if (ambapp_scan + (bridge_address, dev, mmaps, memfunc, + &dev->children, internal)) + 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; + ahb_info = dev->devinfo; + apbbase = ahb_info->start[0]; + apb = (struct ambapp_pnp_apb *) (apbbase | AMBA_CONF_AREA); + for (j = 0; j < AMBA_APB_SLAVES; j++) + { + memfunc (&apb_buf, apb, sizeof (struct ambapp_pnp_apb)); + if (apb_buf.id) + { + apbdev = ambapp_alloc_dev_struct (DEV_APB_SLV); + if (!dev) + return -1; + + ambapp_apb_dev_init (apbbase, mmaps, &apb_buf, apbdev); + + if (prevapb != dev) + prevapb->next = apbdev; + else + dev->children = apbdev; + apbdev->prev = prevapb; + prevapb = apbdev; + } + apb++; + } + } + } + ahb++; + } + + if (parent == NULL) + { + free (internal); + } + + return 0; +} + +/* Match search options againt device */ +int +ambapp_dev_match_options (struct ambapp_dev_hdr *dev, unsigned int options, + int vendor, int device) +{ + if ((((options & (OPTIONS_ALL_DEVS)) == OPTIONS_ALL_DEVS) || /* Match 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)) && /* Match ID */ + ((device == -1) || (device == dev->device)) && (((options & OPTIONS_ALL) == OPTIONS_ALL) || /* Match 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_hdr *dev, + unsigned int options, + int vendor, + int device, int maxdepth, ambapp_func_t func, void *arg) +{ + int index; + struct ambapp_dev_hdr *apbslv; + + if (maxdepth < 0) + return 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) + { + if (func (apbslv, index, maxdepth, arg) == 1) + return 1; /* Signalled stopped */ + } + index++; + apbslv = apbslv->next; + } + } + return 0; +} + +/* Traverse the prescanned device information */ +int +ambapp_for_each (struct ambapp_dev_hdr *root, + unsigned int options, + int vendor, + int device, int maxdepth, ambapp_func_t func, void *arg) +{ + struct ambapp_dev_hdr *dev; + int ahb_slave = 0; + int index; + + if (maxdepth < 0) + return 0; + + /* 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 */ + if (func (dev, index, maxdepth, arg) == 1) + return 1; /* Signalled stopped */ + } + + if ((options & OPTIONS_DEPTH_FIRST) && (options & OPTIONS_APB_SLVS)) + { + /* Check is APB bridge, and process all APB Slaves in that case */ + if (ambapp_for_each_apb + (dev, options, vendor, device, (maxdepth - 1), func, + arg) == 1) + return 1; /* Signalled stopped */ + } + + if (options & OPTIONS_DEPTH_FIRST) + { + if (dev->children && (dev->children->dev_type != DEV_APB_SLV)) + { + /* Found AHB Bridge, recurse */ + if (ambapp_for_each + (dev->children, options, vendor, device, (maxdepth - 1), + func, arg) == 1) + return 1; + } + } + + 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 */ + if (ambapp_for_each_apb + (dev, options, vendor, device, (maxdepth - 1), func, arg) == 1) + return 1; /* 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 */ + if (ambapp_for_each + (dev->children, options, vendor, device, (maxdepth - 1), + func, arg) == 1) + return 1; + } + dev = dev->next; + } + } + + return 0; +} + +int +ambapp_alloc_dev (struct ambapp_dev_hdr *dev, void *owner) +{ + if (dev->owner) + return -1; + dev->owner = owner; + return 0; +} + +void +ambapp_free_dev (struct ambapp_dev_hdr *dev) +{ + dev->owner = NULL; +} + +struct ambapp_dev_find_match_arg +{ + int index; + int count; + int type; + void *dev; +}; + +/* AMBA PP find routines */ +int +ambapp_dev_find_match (struct ambapp_dev_hdr *dev, int index, int maxdepth, + void *arg) +{ + struct ambapp_dev_find_match_arg *p = arg; + + if (p->index == 0) + { + /* Found controller, stop */ + if (p->type == DEV_APB_SLV) + { + *(struct ambapp_apb_info *) p->dev = + *(struct ambapp_apb_info *) dev->devinfo; + p->dev = ((struct ambapp_apb_info *) p->dev) + 1; + } + else + { + *(struct ambapp_ahb_info *) p->dev = + *(struct ambapp_ahb_info *) dev->devinfo; + p->dev = ((struct ambapp_ahb_info *) p->dev) + 1; + } + p->count--; + if (p->count < 1) + return 1; + } + else + { + p->index--; + } + return 0; +} + +int +ambapp_find_apbslvs_next (struct ambapp_dev_hdr *root, int vendor, int device, + struct ambapp_apb_info *dev, int index, int maxno) +{ + struct ambapp_dev_find_match_arg arg; + + arg.index = index; + arg.count = maxno; + arg.type = DEV_APB_SLV; /* APB */ + arg.dev = dev; + + ambapp_for_each (root, (OPTIONS_ALL | OPTIONS_APB_SLVS), vendor, device, 10, + ambapp_dev_find_match, &arg); + + return maxno - arg.count; +} + +int +ambapp_find_apbslv (struct ambapp_dev_hdr *root, int vendor, int device, + struct ambapp_apb_info *dev) +{ + return ambapp_find_apbslvs_next (root, vendor, device, dev, 0, 1); +} + +int +ambapp_find_apbslv_next (struct ambapp_dev_hdr *root, int vendor, int device, + struct ambapp_apb_info *dev, int index) +{ + return ambapp_find_apbslvs_next (root, vendor, device, dev, index, 1); +} + +int +ambapp_find_apbslvs (struct ambapp_dev_hdr *root, int vendor, int device, + struct ambapp_apb_info *dev, int maxno) +{ + return ambapp_find_apbslvs_next (root, vendor, device, dev, 0, maxno); +} + +int +ambapp_find_ahbslvs_next (struct ambapp_dev_hdr *root, int vendor, int device, + struct ambapp_ahb_info *dev, int index, int maxno) +{ + struct ambapp_dev_find_match_arg arg; + + arg.index = index; + arg.count = maxno; + arg.type = DEV_AHB_SLV; /* AHB SLV */ + arg.dev = dev; + + ambapp_for_each (root, (OPTIONS_ALL | OPTIONS_AHB_SLVS), vendor, device, 10, + ambapp_dev_find_match, &arg); + + return maxno - arg.count; +} + +int +ambapp_find_ahbslv_next (struct ambapp_dev_hdr *root, int vendor, int device, + struct ambapp_ahb_info *dev, int index) +{ + return ambapp_find_ahbslvs_next (root, vendor, device, dev, index, 1); +} + +int +ambapp_find_ahbslv (struct ambapp_dev_hdr *root, int vendor, int device, + struct ambapp_ahb_info *dev) +{ + return ambapp_find_ahbslvs_next (root, vendor, device, dev, 0, 1); +} + +int +ambapp_find_ahbslvs (struct ambapp_dev_hdr *root, int vendor, int device, + struct ambapp_ahb_info *dev, int maxno) +{ + return ambapp_find_ahbslvs_next (root, vendor, device, dev, 0, maxno); +} + +struct ambapp_dev_hdr * +ambapp_find_parent (struct ambapp_dev_hdr *dev) +{ + while (dev->prev) + { + if (dev == dev->prev->children) + { + return dev->prev; + } + dev = dev->prev; + } + return NULL; +} + + +struct ambapp_dev_hdr *ambapp_root = NULL; +extern unsigned int console; +extern unsigned int rtc; + +void +pnpinit (void) +{ + struct ambapp_apb_info dev; + int n; + ambapp_scan (LEON3_IO_AREA, NULL, NULL, NULL, &ambapp_root, NULL); + if ((n = + ambapp_find_apbslv (ambapp_root, VENDOR_GAISLER, GAISLER_APBUART, + &dev)) == 1) + { + console = dev.start; + DPRINTF (("Found abuart at 0x%x\n", console)); + } + if ((n = + ambapp_find_apbslv (ambapp_root, VENDOR_GAISLER, GAISLER_GPTIMER, + &dev)) == 1) + { + rtc = dev.start + 0x10; + DPRINTF (("Found rtc at 0x%x\n", rtc)); + } +} |