/* Gaisler AMBA Plug&Play bus scanning. Functions * ending on _nomem is inteded to be used only during * initialization, only registers are used (no ram). * * (C) Copyright 2007 * Daniel Hellstrom, Gaisler Research, daniel@gaisler.com * * See file CREDITS for list of people who contributed to this * project. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ #include <common.h> #include <command.h> #include <ambapp.h> #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; } /* Get APB CTRL Plug&Play info area */ apbmst_base = apbmst.address[0] & LEON3_IO_AREA; apb = (apbctrl_pp_dev *) (apbmst_base | LEON3_CONF_AREA); 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; } 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; } /****************************** APB SLAVES ******************************/ int ambapp_apb_count(unsigned int vendor, unsigned int driver) { return ambapp_apb_scan(vendor, driver, NULL, 0, LEON3_APB_SLAVES); } int ambapp_apb_first(unsigned int vendor, unsigned int driver, ambapp_apbdev * dev) { return ambapp_apb_scan(vendor, driver, dev, 0, 1); } int ambapp_apb_next(unsigned int vendor, unsigned int driver, ambapp_apbdev * dev, int index) { return ambapp_apb_scan(vendor, driver, dev, index, 1); } int ambapp_apbs_first(unsigned int vendor, unsigned int driver, ambapp_apbdev * dev, int max_cnt) { return ambapp_apb_scan(vendor, driver, dev, 0, max_cnt); } 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; } 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; } unsigned int ambapp_ahb_get_info(ahbctrl_pp_dev * ahb, int info) { 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; } 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) { 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; } 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; } /****************************** AHB MASTERS ******************************/ int ambapp_ahbmst_count(unsigned int vendor, unsigned int driver) { /* Get number of devices of this vendor&device ID */ return ambapp_ahb_scan(vendor, driver, NULL, 0, LEON3_AHB_MASTERS, AHB_SCAN_MASTER); } int ambapp_ahbmst_first(unsigned int vendor, unsigned int driver, ambapp_ahbdev * dev) { /* find first device of this */ return ambapp_ahb_scan(vendor, driver, dev, 0, 1, AHB_SCAN_MASTER); } int ambapp_ahbmst_next(unsigned int vendor, unsigned int driver, ambapp_ahbdev * dev, int index) { /* find first device of this */ return ambapp_ahb_scan(vendor, driver, dev, index, 1, AHB_SCAN_MASTER); } int ambapp_ahbmsts_first(unsigned int vendor, unsigned int driver, ambapp_ahbdev * dev, int max_cnt) { /* find first device of this */ return ambapp_ahb_scan(vendor, driver, dev, 0, max_cnt, AHB_SCAN_MASTER); } /****************************** AHB SLAVES ******************************/ int ambapp_ahbslv_count(unsigned int vendor, unsigned int driver) { /* Get number of devices of this vendor&device ID */ return ambapp_ahb_scan(vendor, driver, NULL, 0, LEON3_AHB_SLAVES, AHB_SCAN_SLAVE); } int ambapp_ahbslv_first(unsigned int vendor, unsigned int driver, ambapp_ahbdev * dev) { /* find first device of this */ return ambapp_ahb_scan(vendor, driver, dev, 0, 1, AHB_SCAN_SLAVE); } int ambapp_ahbslv_next(unsigned int vendor, unsigned int driver, ambapp_ahbdev * dev, int index) { /* find first device of this */ return ambapp_ahb_scan(vendor, driver, dev, index, 1, AHB_SCAN_SLAVE); } int ambapp_ahbslvs_first(unsigned int vendor, unsigned int driver, ambapp_ahbdev * dev, int max_cnt) { /* find first device of this */ return ambapp_ahb_scan(vendor, driver, dev, 0, max_cnt, AHB_SCAN_SLAVE); }