diff options
author | Daniel Hellstrom <daniel@gaisler.com> | 2010-01-25 09:54:51 +0100 |
---|---|---|
committer | Francois Retief <fgretief@spaceteq.co.za> | 2015-11-13 10:23:32 +0200 |
commit | 898cc81da3dd5bfa0f77f1791f76d3512b5e4dce (patch) | |
tree | c3ebbab26c559c40e7b7a8edf4238b2c4daf5b62 /arch/sparc/cpu/leon3/ambapp.c | |
parent | 0070109f683657ba3f864792ae134f74282ae4e8 (diff) |
sparc: leon3: Reimplemented AMBA Plug&Play scanning routines.
Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
Diffstat (limited to 'arch/sparc/cpu/leon3/ambapp.c')
-rw-r--r-- | arch/sparc/cpu/leon3/ambapp.c | 544 |
1 files changed, 258 insertions, 286 deletions
diff --git a/arch/sparc/cpu/leon3/ambapp.c b/arch/sparc/cpu/leon3/ambapp.c index a30d1f2a99..bb81338dac 100644 --- a/arch/sparc/cpu/leon3/ambapp.c +++ b/arch/sparc/cpu/leon3/ambapp.c @@ -1,343 +1,315 @@ -/* Gaisler AMBA Plug&Play bus scanning. Functions - * ending on _nomem is inteded to be used only during - * initialization, only registers are used (no ram). +/* GRLIB AMBA Plug&Play information scanning, relies on assembler + * routines. * - * (C) Copyright 2007 - * Daniel Hellstrom, Gaisler Research, daniel@gaisler.com + * (C) Copyright 2010, 2015 + * Daniel Hellstrom, Cobham Gaisler, daniel@gaisler.com. * * SPDX-License-Identifier: GPL-2.0+ */ +/* #define DEBUG */ + #include <common.h> -#include <command.h> #include <ambapp.h> +#include <config.h> + +/************ C INTERFACE OF ASSEMBLER SCAN ROUTINES ************/ +struct ambapp_find_apb_info { + /* Address of APB device Plug&Play information */ + struct ambapp_pnp_apb *pnp; + /* AHB Bus index of where the APB-Master Bridge device was found */ + int ahb_bus_index; + int dec_index; +}; -#if defined(CONFIG_CMD_AMBAPP) -extern void ambapp_print_apb(apbctrl_pp_dev * apb, - ambapp_ahbdev * apbmst, int index); -extern void ambapp_print_ahb(ahbctrl_pp_dev * ahb, int index); -extern int ambapp_apb_print; -extern int ambapp_ahb_print; -#endif - -static int ambapp_apb_scan(unsigned int vendor, /* Plug&Play Vendor ID */ - unsigned int driver, /* Plug&Play Device ID */ - ambapp_apbdev * dev, /* Result(s) is placed here */ - int index, /* Index of device to start copying Plug&Play - * info into dev - */ - int max_cnt /* Maximal count that dev can hold, if dev - * is NULL function will stop scanning after - * max_cnt devices are found. - */ - ) -{ - int i, cnt = 0; - unsigned int apbmst_base; - ambapp_ahbdev apbmst; - apbctrl_pp_dev *apb; - - if (max_cnt == 0) - return 0; - - /* Get AMBA APB Master */ - if (ambapp_ahbslv_first(VENDOR_GAISLER, GAISLER_APBMST, &apbmst) != 1) { - return 0; - } +struct ambapp_find_ahb_info { + /* Address of AHB device Plug&Play information */ + struct ambapp_pnp_ahb *pnp; + /* AHB Bus index of where the AHB device was found */ + int ahb_bus_index; + int dec_index; +}; - /* Get APB CTRL Plug&Play info area */ - apbmst_base = apbmst.address[0] & LEON3_IO_AREA; - apb = (apbctrl_pp_dev *) (apbmst_base | LEON3_CONF_AREA); +extern void ambapp_find_buses(unsigned int ioarea, struct ambapp_bus *abus); - for (i = 0; i < LEON3_APB_SLAVES; i++) { -#if defined(CONFIG_CMD_AMBAPP) - if (ambapp_apb_print && amba_vendor(apb->conf) - && amba_device(apb->conf)) { - ambapp_print_apb(apb, &apbmst, i); - } -#endif - if ((amba_vendor(apb->conf) == vendor) && - (amba_device(apb->conf) == driver) && ((index < 0) - || (index-- == 0))) { - /* Convert Plug&Play info into a more readable format */ - cnt++; - if (dev) { - dev->irq = amba_irq(apb->conf); - dev->ver = amba_ver(apb->conf); - dev->address = - (apbmst_base | - (((apb-> - bar & 0xfff00000) >> 12))) & (((apb-> - bar & - 0x0000fff0) - << 4) | - 0xfff00000); - dev++; - } - /* found max devices? */ - if (cnt >= max_cnt) - return cnt; - } - /* Get next Plug&Play entry */ - apb++; - } - return cnt; -} +extern int ambapp_find_apb(struct ambapp_bus *abus, unsigned int dev_vend, + int index, struct ambapp_find_apb_info *result); -unsigned int ambapp_apb_next_nomem(register unsigned int vendor, /* Plug&Play Vendor ID */ - register unsigned int driver, /* Plug&Play Device ID */ - register int index) -{ - register int i; - register ahbctrl_pp_dev *apbmst; - register apbctrl_pp_dev *apb; - register unsigned int apbmst_base; - - /* APBMST is a AHB Slave */ - apbmst = ambapp_ahb_next_nomem(VENDOR_GAISLER, GAISLER_APBMST, 1, 0); - if (!apbmst) - return 0; - - apbmst_base = amba_membar_start(apbmst->bars[0]); - if (amba_membar_type(apbmst->bars[0]) == AMBA_TYPE_AHBIO) - apbmst_base = AMBA_TYPE_AHBIO_ADDR(apbmst_base); - apbmst_base &= LEON3_IO_AREA; - - /* Find the vendor/driver device on the first APB bus */ - apb = (apbctrl_pp_dev *) (apbmst_base | LEON3_CONF_AREA); - - for (i = 0; i < LEON3_APB_SLAVES; i++) { - if ((amba_vendor(apb->conf) == vendor) && - (amba_device(apb->conf) == driver) && ((index < 0) - || (index-- == 0))) { - /* Convert Plug&Play info info a more readable format */ - return (apbmst_base | (((apb->bar & 0xfff00000) >> 12))) - & (((apb->bar & 0x0000fff0) << 4) | 0xfff00000); - } - /* Get next Plug&Play entry */ - apb++; - } - return 0; -} +extern int ambapp_find_ahb(struct ambapp_bus *abus, unsigned int dev_vend, + int index, int type, struct ambapp_find_ahb_info *result); -/****************************** APB SLAVES ******************************/ +/************ C ROUTINES USED BY U-BOOT AMBA CORE DRIVERS ************/ +struct ambapp_bus ambapp_plb; -int ambapp_apb_count(unsigned int vendor, unsigned int driver) +void ambapp_bus_init( + unsigned int ioarea, + unsigned int freq, + struct ambapp_bus *abus) { - return ambapp_apb_scan(vendor, driver, NULL, 0, LEON3_APB_SLAVES); + int i; + + ambapp_find_buses(ioarea, abus); + for (i = 0; i < 6; i++) + if (abus->ioareas[i] == 0) + break; + abus->buses = i; + abus->freq = freq; } -int ambapp_apb_first(unsigned int vendor, - unsigned int driver, ambapp_apbdev * dev) +/* Parse APB PnP Information */ +void ambapp_apb_parse(struct ambapp_find_apb_info *info, ambapp_apbdev *dev) { - return ambapp_apb_scan(vendor, driver, dev, 0, 1); + struct ambapp_pnp_apb *apb = info->pnp; + unsigned int apbbase = (unsigned int)apb & 0xfff00000; + + dev->vendor = amba_vendor(apb->id); + dev->device = amba_device(apb->id); + dev->irq = amba_irq(apb->id); + dev->ver = amba_ver(apb->id); + dev->address = (apbbase | (((apb->iobar & 0xfff00000) >> 12))) & + (((apb->iobar & 0x0000fff0) << 4) | 0xfff00000); + dev->mask = amba_apb_mask(apb->iobar); + dev->ahb_bus_index = info->ahb_bus_index - 1; } -int ambapp_apb_next(unsigned int vendor, - unsigned int driver, ambapp_apbdev * dev, int index) +/* Parse AHB PnP information */ +void ambapp_ahb_parse(struct ambapp_find_ahb_info *info, ambapp_ahbdev *dev) { - return ambapp_apb_scan(vendor, driver, dev, index, 1); + struct ambapp_pnp_ahb *ahb = info->pnp; + unsigned int ahbbase = (unsigned int)ahb & 0xfff00000; + int i, type; + unsigned int addr, mask, mbar; + + dev->vendor = amba_vendor(ahb->id); + dev->device = amba_device(ahb->id); + dev->irq = amba_irq(ahb->id); + dev->ver = amba_ver(ahb->id); + dev->userdef[0] = ahb->custom[0]; + dev->userdef[1] = ahb->custom[1]; + dev->userdef[2] = ahb->custom[2]; + dev->ahb_bus_index = info->ahb_bus_index - 1; + for (i = 0; i < 4; i++) { + mbar = ahb->mbar[i]; + addr = amba_membar_start(mbar); + type = amba_membar_type(mbar); + if (type == AMBA_TYPE_AHBIO) { + addr = amba_ahbio_adr(addr, ahbbase); + mask = (((unsigned int) + (amba_membar_mask((~mbar))<<8)|0xff))+1; + } else { + /* AHB memory area, absolute address */ + mask = (~((unsigned int) + (amba_membar_mask(mbar)<<20)))+1; + } + dev->address[i] = addr; + dev->mask[i] = mask; + dev->type[i] = type; + } } -int ambapp_apbs_first(unsigned int vendor, - unsigned int driver, ambapp_apbdev * dev, int max_cnt) +int ambapp_apb_find(struct ambapp_bus *abus, int vendor, int device, + int index, ambapp_apbdev *dev) { - return ambapp_apb_scan(vendor, driver, dev, 0, max_cnt); -} + unsigned int devid = AMBA_PNP_ID(vendor, device); + int found; + struct ambapp_find_apb_info apbdev; -enum { - AHB_SCAN_MASTER = 0, - AHB_SCAN_SLAVE = 1 -}; - -/* Scan AMBA Plug&Play bus for AMBA AHB Masters or AHB Slaves - * for a certain matching Vendor and Device ID. - * - * Return number of devices found. - * - * Compact edition... - */ -static int ambapp_ahb_scan(unsigned int vendor, /* Plug&Play Vendor ID */ - unsigned int driver, /* Plug&Play Device ID */ - ambapp_ahbdev * dev, /* Result(s) is placed here */ - int index, /* Index of device to start copying Plug&Play - * info into dev - */ - int max_cnt, /* Maximal count that dev can hold, if dev - * is NULL function will stop scanning after - * max_cnt devices are found. - */ - int type /* Selectes what type of devices to scan. - * 0=AHB Masters - * 1=AHB Slaves - */ - ) -{ - int i, j, cnt = 0, max_pp_devs; - unsigned int addr; - ahbctrl_info *info = (ahbctrl_info *) (LEON3_IO_AREA | LEON3_CONF_AREA); - ahbctrl_pp_dev *ahb; - - if (max_cnt == 0) - return 0; - - if (type == 0) { - max_pp_devs = LEON3_AHB_MASTERS; - ahb = info->masters; - } else { - max_pp_devs = LEON3_AHB_SLAVES; - ahb = info->slaves; - } + found = ambapp_find_apb(abus, devid, index, &apbdev); + if (found == 1) + ambapp_apb_parse(&apbdev, dev); - for (i = 0; i < max_pp_devs; i++) { -#if defined(CONFIG_CMD_AMBAPP) - if (ambapp_ahb_print && amba_vendor(ahb->conf) && - amba_device(ahb->conf)) { - ambapp_print_ahb(ahb, i); - } -#endif - if ((amba_vendor(ahb->conf) == vendor) && - (amba_device(ahb->conf) == driver) && - ((index < 0) || (index-- == 0))) { - /* Convert Plug&Play info info a more readable format */ - cnt++; - if (dev) { - dev->irq = amba_irq(ahb->conf); - dev->ver = amba_ver(ahb->conf); - dev->userdef[0] = ahb->userdef[0]; - dev->userdef[1] = ahb->userdef[1]; - dev->userdef[2] = ahb->userdef[2]; - for (j = 0; j < 4; j++) { - addr = amba_membar_start(ahb->bars[j]); - if (amba_membar_type(ahb->bars[j]) == - AMBA_TYPE_AHBIO) - addr = - AMBA_TYPE_AHBIO_ADDR(addr); - dev->address[j] = addr; - } - dev++; - } - /* found max devices? */ - if (cnt >= max_cnt) - return cnt; - } - /* Get next Plug&Play entry */ - ahb++; - } - return cnt; + return found; } -unsigned int ambapp_ahb_get_info(ahbctrl_pp_dev * ahb, int info) +int ambapp_apb_count(struct ambapp_bus *abus, int vendor, int device) { - register unsigned int ret; - - if (!ahb) - return 0; - - switch (info) { - default: - info = 0; - case 0: - case 1: - case 2: - case 3: - /* Get Address from PnP Info */ - ret = amba_membar_start(ahb->bars[info]); - if (amba_membar_type(ahb->bars[info]) == AMBA_TYPE_AHBIO) - ret = AMBA_TYPE_AHBIO_ADDR(ret); - return ret; - } - return 0; - + unsigned int devid = AMBA_PNP_ID(vendor, device); + int found; + struct ambapp_find_apb_info apbdev; + + found = ambapp_find_apb(abus, devid, 63, &apbdev); + if (found == 1) + return 64; + else + return 63 - apbdev.dec_index; } -ahbctrl_pp_dev *ambapp_ahb_next_nomem(register unsigned int vendor, /* Plug&Play Vendor ID */ - register unsigned int driver, /* Plug&Play Device ID */ - register unsigned int opts, /* 1=slave, 0=master */ - register int index) +int ambapp_ahb_find(struct ambapp_bus *abus, int vendor, int device, + int index, ambapp_ahbdev *dev, int type) { - register ahbctrl_pp_dev *ahb; - register ahbctrl_info *info = - (ahbctrl_info *) (LEON3_IO_AREA | LEON3_CONF_AREA); - register int i; - register int max_pp_devs; - - if (opts == 0) { - max_pp_devs = LEON3_AHB_MASTERS; - ahb = info->masters; - } else { - max_pp_devs = LEON3_AHB_SLAVES; - ahb = info->slaves; - } + int found; + struct ambapp_find_ahb_info ahbdev; + unsigned int devid = AMBA_PNP_ID(vendor, device); - for (i = 0; i < max_pp_devs; i++) { - if ((amba_vendor(ahb->conf) == vendor) && - (amba_device(ahb->conf) == driver) && - ((index < 0) || (index-- == 0))) { - /* Convert Plug&Play info info a more readable format */ - return ahb; - } - /* Get next Plug&Play entry */ - ahb++; - } - return 0; + found = ambapp_find_ahb(abus, devid, index, type, &ahbdev); + if (found == 1) + ambapp_ahb_parse(&ahbdev, dev); + + return found; } -/****************************** AHB MASTERS ******************************/ -int ambapp_ahbmst_count(unsigned int vendor, unsigned int driver) +int ambapp_ahbmst_find(struct ambapp_bus *abus, int vendor, int device, + int index, ambapp_ahbdev *dev) { - /* Get number of devices of this vendor&device ID */ - return ambapp_ahb_scan(vendor, driver, NULL, 0, LEON3_AHB_MASTERS, - AHB_SCAN_MASTER); + return ambapp_ahb_find(abus, vendor, device, index, dev, DEV_AHB_MST); } -int ambapp_ahbmst_first(unsigned int vendor, unsigned int driver, - ambapp_ahbdev * dev) +int ambapp_ahbslv_find(struct ambapp_bus *abus, int vendor, int device, + int index, ambapp_ahbdev *dev) { - /* find first device of this */ - return ambapp_ahb_scan(vendor, driver, dev, 0, 1, AHB_SCAN_MASTER); + return ambapp_ahb_find(abus, vendor, device, index, dev, DEV_AHB_SLV); } -int ambapp_ahbmst_next(unsigned int vendor, - unsigned int driver, ambapp_ahbdev * dev, int index) +int ambapp_ahb_count(struct ambapp_bus *abus, int vendor, int device, int type) { - /* find first device of this */ - return ambapp_ahb_scan(vendor, driver, dev, index, 1, AHB_SCAN_MASTER); + int found; + struct ambapp_find_ahb_info ahbdev; + unsigned int devid = AMBA_PNP_ID(vendor, device); + + found = ambapp_find_ahb(abus, devid, 63, type, &ahbdev); + if (found == 1) + return 64; + else + return 63 - ahbdev.dec_index; } -int ambapp_ahbmsts_first(unsigned int vendor, - unsigned int driver, ambapp_ahbdev * dev, int max_cnt) +int ambapp_ahbmst_count(struct ambapp_bus *abus, int vendor, int device) { - /* find first device of this */ - return ambapp_ahb_scan(vendor, driver, dev, 0, max_cnt, - AHB_SCAN_MASTER); + return ambapp_ahb_count(abus, vendor, device, DEV_AHB_MST); } -/****************************** AHB SLAVES ******************************/ -int ambapp_ahbslv_count(unsigned int vendor, unsigned int driver) +int ambapp_ahbslv_count(struct ambapp_bus *abus, int vendor, int device) { - /* Get number of devices of this vendor&device ID */ - return ambapp_ahb_scan(vendor, driver, NULL, 0, LEON3_AHB_SLAVES, - AHB_SCAN_SLAVE); + return ambapp_ahb_count(abus, vendor, device, DEV_AHB_SLV); } -int ambapp_ahbslv_first(unsigned int vendor, unsigned int driver, - ambapp_ahbdev * dev) +/* The define CONFIG_SYS_GRLIB_SINGLE_BUS may be defined on GRLIB systems + * where only one AHB Bus is available - no bridges are present. This option + * is available only to reduce the footprint. + * + * Defining this on a multi-bus GRLIB system may also work depending on the + * design. + */ + +#ifndef CONFIG_SYS_GRLIB_SINGLE_BUS + +/* GAISLER AHB2AHB Version 1 Bridge Definitions */ +#define AHB2AHB_V1_FLAG_FFACT 0x0f0 /* Frequency factor against top bus */ +#define AHB2AHB_V1_FLAG_FFACT_DIR 0x100 /* Factor direction, 0=down, 1=up */ +#define AHB2AHB_V1_FLAG_MBUS 0x00c /* Master bus number mask */ +#define AHB2AHB_V1_FLAG_SBUS 0x003 /* Slave bus number mask */ + +/* Get Parent bus frequency. Note that since we go from a "child" bus + * to a parent bus, the frequency factor direction is inverted. + */ +unsigned int gaisler_ahb2ahb_v1_freq(ambapp_ahbdev *ahb, unsigned int freq) { - /* find first device of this */ - return ambapp_ahb_scan(vendor, driver, dev, 0, 1, AHB_SCAN_SLAVE); + int dir; + unsigned char ffact; + + /* Get division/multiple factor */ + ffact = (ahb->userdef[0] & AHB2AHB_V1_FLAG_FFACT) >> 4; + if (ffact != 0) { + dir = ahb->userdef[0] & AHB2AHB_V1_FLAG_FFACT_DIR; + + /* Calculate frequency by dividing or + * multiplying system frequency + */ + if (dir) + freq = freq * ffact; + else + freq = freq / ffact; + } + + return freq; } -int ambapp_ahbslv_next(unsigned int vendor, - unsigned int driver, ambapp_ahbdev * dev, int index) +/* AHB2AHB and L2CACHE ver 2 is not supported yet. */ +unsigned int gaisler_ahb2ahb_v2_freq(ambapp_ahbdev *ahb, unsigned int freq) { - /* find first device of this */ - return ambapp_ahb_scan(vendor, driver, dev, index, 1, AHB_SCAN_SLAVE); + panic("gaisler_ahb2ahb_v2_freq: AHB2AHB ver 2 not supported\n"); + return -1; } +#endif -int ambapp_ahbslvs_first(unsigned int vendor, - unsigned int driver, ambapp_ahbdev * dev, int max_cnt) +/* Return the frequency of a AHB bus identified by index found + * note that this is not the AHB Bus number. + */ +unsigned int ambapp_bus_freq(struct ambapp_bus *abus, int ahb_bus_index) { - /* find first device of this */ - return ambapp_ahb_scan(vendor, driver, dev, 0, max_cnt, AHB_SCAN_SLAVE); + unsigned int freq = abus->freq; +#ifndef CONFIG_SYS_GRLIB_SINGLE_BUS + unsigned int ioarea, ioarea_parent, bridge_pnp_ofs; + struct ambapp_find_ahb_info ahbinfo; + ambapp_ahbdev ahb; + int parent; + + debug("ambapp_bus_freq: get freq on bus %d\n", ahb_bus_index); + + while (ahb_bus_index != 0) { + debug(" BUS[0]: 0x%08x\n", abus->ioareas[0]); + debug(" BUS[1]: 0x%08x\n", abus->ioareas[1]); + debug(" BUS[2]: 0x%08x\n", abus->ioareas[2]); + debug(" BUS[3]: 0x%08x\n", abus->ioareas[3]); + debug(" BUS[4]: 0x%08x\n", abus->ioareas[4]); + debug(" BUS[5]: 0x%08x\n", abus->ioareas[5]); + + /* Get I/O area of AHB bus */ + ioarea = abus->ioareas[ahb_bus_index]; + + printf(" IOAREA: 0x%08x\n", ioarea); + + /* Get parent bus */ + parent = (ioarea & 0x7); + if (parent == 0) { + panic("%s: parent=0 indicates no parent! Stopping.\n", + __func__); + return -1; + } + parent = parent - 1; + bridge_pnp_ofs = ioarea & 0x7e0; + + debug(" PARENT: %d\n", parent); + debug(" BRIDGE_OFS: 0x%08x\n", bridge_pnp_ofs); + + /* Get AHB/AHB bridge PnP address */ + ioarea_parent = (abus->ioareas[parent] & 0xfff00000) | + AMBA_CONF_AREA | AMBA_AHB_SLAVE_CONF_AREA; + ahbinfo.pnp = (struct ambapp_pnp_ahb *) + (ioarea_parent | bridge_pnp_ofs); + + debug(" IOAREA PARENT: 0x%08x\n", ioarea_parent); + debug(" BRIDGE PNP: 0x%p\n", ahbinfo.pnp); + + /* Parse the AHB information */ + ahbinfo.ahb_bus_index = parent; + ambapp_ahb_parse(&ahbinfo, &ahb); + + debug(" BRIDGE ID: VENDOR=%d(0x%x), DEVICE=%d(0x%x)\n", + ahb.vendor, ahb.vendor, ahb.device, ahb.device); + + /* Different bridges may convert frequency differently */ + if ((ahb.vendor == VENDOR_GAISLER) && + ((ahb.device == GAISLER_AHB2AHB) || + (ahb.device == GAISLER_L2CACHE))) { + /* Get new frequency */ + if (ahb.ver > 1) + freq = gaisler_ahb2ahb_v2_freq(&ahb, freq); + else + freq = gaisler_ahb2ahb_v1_freq(&ahb, freq); + + debug(" NEW FREQ: %dHz\n", freq); + } else { + panic("%s: unsupported AMBA bridge\n", __func__); + return -1; + } + + /* Step upwards towards system top bus */ + ahb_bus_index = parent; + } +#endif + + debug("ambapp_bus_freq: %dHz\n", freq); + + return freq; } |