2008-03-26 21:51:29 +00:00
|
|
|
/* 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>
|
|
|
|
|
2008-03-26 22:00:38 +00:00
|
|
|
#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
|
|
|
|
|
2008-03-26 21:51:29 +00:00
|
|
|
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++) {
|
2008-03-26 22:00:38 +00:00
|
|
|
#if defined(CONFIG_CMD_AMBAPP)
|
|
|
|
if (ambapp_apb_print && amba_vendor(apb->conf)
|
|
|
|
&& amba_device(apb->conf)) {
|
|
|
|
ambapp_print_apb(apb, &apbmst, i);
|
|
|
|
}
|
|
|
|
#endif
|
2008-03-26 21:51:29 +00:00
|
|
|
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++) {
|
2008-03-26 22:00:38 +00:00
|
|
|
#if defined(CONFIG_CMD_AMBAPP)
|
|
|
|
if (ambapp_ahb_print && amba_vendor(ahb->conf) &&
|
|
|
|
amba_device(ahb->conf)) {
|
|
|
|
ambapp_print_ahb(ahb, i);
|
|
|
|
}
|
|
|
|
#endif
|
2008-03-26 21:51:29 +00:00
|
|
|
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);
|
|
|
|
}
|