| /* |
| * 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 |
| |
| #ifdef PDEBUG |
| #define DPRINTF(p) printf p |
| #else |
| #define DPRINTF(p) |
| #endif |
| |
| 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_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; |
| } |
| } |
| |
| static 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 |
| static 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; |
| } |
| } |
| } |
| |
| static 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; |
| } |
| |
| static int |
| ambapp_find (unsigned int ioarea, |
| struct ambapp_dev_hdr *parent, |
| struct ambapp_mmap *mmaps, |
| void *internal, |
| int (*find_match) (struct ambapp_dev_hdr * dev, void *arg), |
| void *arg, int vendor, int device) |
| { |
| 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)); |
| |
| if (parent) |
| { |
| /* scan first bus for 64 devices, rest for 16 devices */ |
| maxloops = 16; |
| } |
| else |
| { |
| if (internal) |
| { |
| 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++) |
| { |
| memcpy (&ahb_buf, ahb, sizeof (struct ambapp_pnp_ahb)); |
| if (ahb_buf.id != 0) |
| { |
| struct ambapp_dev_hdr _dev; |
| struct ambapp_ahb_info _ahb; |
| memset (&_dev, 0, sizeof (_dev)); |
| memset (&_ahb, 0, sizeof (_ahb)); |
| _dev.devinfo = &_ahb; |
| _dev.dev_type = DEV_AHB_MST; |
| dev = &_dev; |
| |
| ambapp_ahb_dev_init (ioarea, mmaps, &ahb_buf, dev); |
| |
| DPRINTF ((" = test %d:%d == %d:%d\n", vendor, device, dev->vendor, |
| dev->device)); |
| |
| if (vendor == dev->vendor && |
| device == dev->device && find_match (dev, arg)) |
| { |
| return 1; |
| } |
| } |
| ahb++; |
| } |
| |
| |
| /* AHB SLAVES */ |
| ahb = |
| (struct ambapp_pnp_ahb *) (ioarea | AMBA_CONF_AREA | |
| AMBA_AHB_SLAVE_CONF_AREA); |
| for (i = 0; i < maxloops; i++) |
| { |
| memcpy (&ahb_buf, ahb, sizeof (struct ambapp_pnp_ahb)); |
| if (ahb_buf.id != 0) |
| { |
| struct ambapp_dev_hdr _dev; |
| struct ambapp_ahb_info _ahb; |
| memset (&_dev, 0, sizeof (_dev)); |
| memset (&_ahb, 0, sizeof (_ahb)); |
| _dev.devinfo = &_ahb; |
| _dev.dev_type = DEV_AHB_MST; |
| dev = &_dev; |
| |
| ambapp_ahb_dev_init (ioarea, mmaps, &ahb_buf, dev); |
| |
| DPRINTF ((" = test %d:%d == %d:%d\n", vendor, device, dev->vendor, |
| dev->device)); |
| |
| if (vendor == dev->vendor && |
| device == dev->device && find_match (dev, arg)) |
| { |
| return 1; |
| } |
| |
| /* 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 (internal == 0 |
| || ambapp_has_been_scanned (internal, |
| bridge_address) == NULL) |
| { |
| if (internal) |
| ambapp_add_scanned_bus (internal, bridge_address); |
| |
| if (ambapp_find (bridge_address, dev, mmaps, internal, |
| find_match, arg, vendor, device)) |
| 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++) |
| { |
| memcpy (&apb_buf, apb, sizeof (struct ambapp_pnp_apb)); |
| if (apb_buf.id) |
| { |
| struct ambapp_dev_hdr _apbdev; |
| struct ambapp_apb_info _apb; |
| memset (&_apbdev, 0, sizeof (_apbdev)); |
| memset (&_apb, 0, sizeof (_apb)); |
| _apbdev.devinfo = &_apb; |
| _apbdev.dev_type = DEV_APB_SLV; |
| apbdev = &_apbdev; |
| |
| ambapp_apb_dev_init (apbbase, mmaps, &apb_buf, apbdev); |
| |
| DPRINTF ((" = test %d:%d == %d:%d\n", vendor, device, |
| apbdev->vendor, apbdev->device)); |
| |
| if (vendor == apbdev->vendor && |
| device == apbdev->device && |
| find_match (apbdev, arg)) |
| { |
| |
| return 1; |
| } |
| } |
| apb++; |
| } |
| } |
| } |
| ahb++; |
| } |
| |
| if (parent == NULL) |
| { |
| /*free(internal); */ |
| } |
| |
| return 0; |
| } |
| |
| struct ambapp_dev_find_match_arg |
| { |
| int index; |
| int count; |
| int type; |
| void *dev; |
| }; |
| |
| /* AMBA PP find routines */ |
| static int |
| ambapp_dev_find_match (struct ambapp_dev_hdr *dev, 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; |
| } |
| |
| static int |
| find_apbslvs_next (int vendor, int device, struct ambapp_apb_info *dev, |
| int index, int maxno) |
| { |
| struct ambapp_dev_find_match_arg arg; |
| unsigned int busses[MAX_NUM_BUSES]; |
| memset (busses, 0, sizeof (busses)); |
| |
| arg.index = index; |
| arg.count = maxno; |
| arg.type = DEV_APB_SLV; /* APB */ |
| arg.dev = dev; |
| |
| ambapp_find (LEON3_IO_AREA, NULL, NULL, &busses, |
| ambapp_dev_find_match, &arg, vendor, device); |
| |
| return maxno - arg.count; |
| } |
| |
| int |
| find_apbslv (int vendor, int device, struct ambapp_apb_info *dev) |
| { |
| return find_apbslvs_next (vendor, device, dev, 0, 1); |
| } |
| |
| struct ambapp_dev_hdr *ambapp_root = NULL; |
| unsigned int busses[MAX_NUM_BUSES]; |
| extern unsigned int console; |
| extern unsigned int rtc; |
| extern unsigned int irqmp; |
| |
| void |
| pnpinit (void) |
| { |
| struct ambapp_apb_info dev; |
| int n; |
| if ((n = find_apbslv (VENDOR_GAISLER, GAISLER_APBUART, &dev)) == 1) |
| { |
| console = dev.start; |
| DPRINTF (("Found abuart at 0x%x\n", console)); |
| } |
| if ((n = find_apbslv (VENDOR_GAISLER, GAISLER_GPTIMER, &dev)) == 1) |
| { |
| rtc = dev.start + 0x10; |
| DPRINTF (("Found rtc at 0x%x\n", rtc)); |
| } |
| if ((n = find_apbslv (VENDOR_GAISLER, GAISLER_IRQMP, &dev)) == 1) |
| { |
| irqmp = dev.start; |
| DPRINTF (("Found irqmp at 0x%x\n", rtc)); |
| } |
| } |