forked from Imagelibrary/rtems
This patch changes the license to BSD-2 for all source files where the copyright is held by Aeroflex Gaisler, Cobham Gaisler, or Gaisler Research. Some files also includes copyright right statements from OAR and/or embedded Brains in addition to Gaisler. Updates #3053.
860 lines
22 KiB
C
860 lines
22 KiB
C
/* SPDX-License-Identifier: BSD-2-Clause */
|
|
|
|
/* General part of a AMBA Plug & Play bus driver.
|
|
*
|
|
* COPYRIGHT (c) 2008.
|
|
* Cobham Gaisler AB.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* This is the general part of the different AMBA Plug & Play
|
|
* drivers. The drivers are wrappers around this driver, making
|
|
* the code size smaller for systems with multiple AMBA Plug &
|
|
* Play buses.
|
|
*
|
|
* The BSP define APBUART_INFO_AVAIL in order to add the info routine
|
|
* used for debugging.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <drvmgr/drvmgr.h>
|
|
#include <grlib/ambapp_bus.h>
|
|
|
|
#include <bsp.h>
|
|
#include <grlib/ambapp.h>
|
|
#include <rtems/bspIo.h>
|
|
|
|
/*#define DEBUG 1*/
|
|
#define DBG(args...)
|
|
/*#define DBG(args...) printk(args)*/
|
|
|
|
struct grlib_gptimer_regs {
|
|
volatile unsigned int scaler_value; /* common timer registers */
|
|
volatile unsigned int scaler_reload;
|
|
volatile unsigned int status;
|
|
volatile unsigned int notused;
|
|
};
|
|
|
|
/* AMBA IMPLEMENTATION */
|
|
|
|
static int ambapp_bus_init1(struct drvmgr_bus *bus);
|
|
static int ambapp_bus_remove(struct drvmgr_bus *bus);
|
|
static int ambapp_unite(struct drvmgr_drv *drv, struct drvmgr_dev *dev);
|
|
static int ambapp_int_register(
|
|
struct drvmgr_dev *dev,
|
|
int index,
|
|
const char *info,
|
|
drvmgr_isr isr,
|
|
void *arg);
|
|
static int ambapp_int_unregister(
|
|
struct drvmgr_dev *dev,
|
|
int index,
|
|
drvmgr_isr isr,
|
|
void *arg);
|
|
static int ambapp_int_clear(struct drvmgr_dev *dev, int index);
|
|
static int ambapp_int_mask(struct drvmgr_dev *dev, int index);
|
|
static int ambapp_int_unmask(struct drvmgr_dev *dev, int index);
|
|
static int ambapp_get_params(
|
|
struct drvmgr_dev *dev,
|
|
struct drvmgr_bus_params *params);
|
|
static int ambapp_bus_freq_get(
|
|
struct drvmgr_dev *dev,
|
|
int options,
|
|
unsigned int *freq_hz);
|
|
#ifdef AMBAPPBUS_INFO_AVAIL
|
|
static void ambapp_dev_info(
|
|
struct drvmgr_dev *,
|
|
void (*print)(void *p, char *str),
|
|
void *p);
|
|
#endif
|
|
|
|
#ifdef RTEMS_SMP
|
|
static int ambapp_int_set_affinity(
|
|
struct drvmgr_dev *dev,
|
|
int index,
|
|
const Processor_mask *cpus);
|
|
#endif
|
|
|
|
static struct drvmgr_bus_ops ambapp_bus_ops =
|
|
{
|
|
.init =
|
|
{
|
|
/* init1 */ ambapp_bus_init1,
|
|
/* init2 */ NULL,
|
|
/* init3 */ NULL,
|
|
/* init4 */ NULL
|
|
},
|
|
.remove = ambapp_bus_remove,
|
|
.unite = ambapp_unite,
|
|
.int_register = ambapp_int_register,
|
|
.int_unregister = ambapp_int_unregister,
|
|
.int_clear = ambapp_int_clear,
|
|
.int_mask = ambapp_int_mask,
|
|
#ifdef RTEMS_SMP
|
|
.int_set_affinity = ambapp_int_set_affinity,
|
|
#endif
|
|
.int_unmask = ambapp_int_unmask,
|
|
.get_params = ambapp_get_params,
|
|
.get_freq = ambapp_bus_freq_get,
|
|
#ifdef AMBAPPBUS_INFO_AVAIL
|
|
.get_info_dev = ambapp_dev_info,
|
|
#endif
|
|
};
|
|
|
|
struct ambapp_priv {
|
|
struct ambapp_config *config;
|
|
};
|
|
|
|
static int ambapp_unite(struct drvmgr_drv *drv, struct drvmgr_dev *dev)
|
|
{
|
|
struct amba_drv_info *adrv;
|
|
struct amba_dev_id *id;
|
|
struct amba_dev_info *amba;
|
|
|
|
if ( !drv || !dev || !dev->parent )
|
|
return 0;
|
|
|
|
if ( ! (((drv->bus_type == DRVMGR_BUS_TYPE_AMBAPP) && (dev->parent->bus_type == DRVMGR_BUS_TYPE_AMBAPP)) ||
|
|
((drv->bus_type == DRVMGR_BUS_TYPE_AMBAPP_RMAP) && (dev->parent->bus_type == DRVMGR_BUS_TYPE_AMBAPP_RMAP)) ||
|
|
((drv->bus_type == DRVMGR_BUS_TYPE_AMBAPP_DIST) && (dev->parent->bus_type == DRVMGR_BUS_TYPE_AMBAPP_DIST)))
|
|
) {
|
|
return 0;
|
|
}
|
|
|
|
amba = (struct amba_dev_info *)dev->businfo;
|
|
if ( !amba )
|
|
return 0;
|
|
|
|
adrv = (struct amba_drv_info *)drv;
|
|
id = adrv->ids;
|
|
if ( !id )
|
|
return 0;
|
|
while( id->vendor != 0 ) {
|
|
if ( (id->vendor == amba->id.vendor) &&
|
|
(id->device == amba->id.device) ) {
|
|
/* Unite device and driver */
|
|
DBG("DRV 0x%x and DEV 0x%x united\n", (unsigned int)drv, (unsigned int)dev);
|
|
return 1;
|
|
}
|
|
id++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ambapp_int_get(struct drvmgr_dev *dev, int index)
|
|
{
|
|
int irq;
|
|
|
|
/* Relative (positive) or absolute (negative) IRQ number */
|
|
if ( index >= 0 ) {
|
|
/* IRQ Index relative to Cores base IRQ */
|
|
|
|
/* Get Base IRQ */
|
|
irq = ((struct amba_dev_info *)dev->businfo)->info.irq;
|
|
if ( irq < 0 )
|
|
return -1;
|
|
irq += index;
|
|
} else {
|
|
/* Absolute IRQ number */
|
|
irq = -index;
|
|
}
|
|
return irq;
|
|
}
|
|
|
|
static int ambapp_int_register(
|
|
struct drvmgr_dev *dev,
|
|
int index,
|
|
const char *info,
|
|
drvmgr_isr isr,
|
|
void *arg)
|
|
{
|
|
struct ambapp_priv *priv;
|
|
int irq;
|
|
|
|
priv = dev->parent->priv;
|
|
|
|
/* Get IRQ number from index and device information */
|
|
irq = ambapp_int_get(dev, index);
|
|
if ( irq < 0 )
|
|
return DRVMGR_EINVAL;
|
|
|
|
DBG("Register interrupt on 0x%x for dev 0x%x (IRQ: %d)\n",
|
|
(unsigned int)dev->parent->dev, (unsigned int)dev, irq);
|
|
|
|
if ( priv->config->ops->int_register ) {
|
|
/* Let device override driver default */
|
|
return priv->config->ops->int_register(dev, irq, info, isr, arg);
|
|
} else {
|
|
return DRVMGR_ENOSYS;
|
|
}
|
|
}
|
|
|
|
static int ambapp_int_unregister(
|
|
struct drvmgr_dev *dev,
|
|
int index,
|
|
drvmgr_isr isr,
|
|
void *arg)
|
|
{
|
|
struct ambapp_priv *priv;
|
|
int irq;
|
|
|
|
priv = dev->parent->priv;
|
|
|
|
/* Get IRQ number from index and device information */
|
|
irq = ambapp_int_get(dev, index);
|
|
if ( irq < 0 )
|
|
return DRVMGR_EINVAL;
|
|
|
|
DBG("Unregister interrupt on 0x%x for dev 0x%x (IRQ: %d)\n",
|
|
(unsigned int)dev->parent->dev, (unsigned int)dev, irq);
|
|
|
|
if ( priv->config->ops->int_unregister ) {
|
|
/* Let device override driver default */
|
|
return priv->config->ops->int_unregister(dev, irq, isr, arg);
|
|
} else {
|
|
return DRVMGR_ENOSYS;
|
|
}
|
|
}
|
|
|
|
static int ambapp_int_clear(
|
|
struct drvmgr_dev *dev,
|
|
int index)
|
|
{
|
|
struct ambapp_priv *priv;
|
|
int irq;
|
|
|
|
priv = dev->parent->priv;
|
|
|
|
/* Get IRQ number from index and device information */
|
|
irq = ambapp_int_get(dev, index);
|
|
if ( irq < 0 )
|
|
return -1;
|
|
|
|
DBG("Clear interrupt on 0x%x for dev 0x%x (IRQ: %d)\n",
|
|
(unsigned int)dev->parent->dev, (unsigned int)dev, irq);
|
|
|
|
if ( priv->config->ops->int_clear ) {
|
|
/* Let device override driver default */
|
|
return priv->config->ops->int_clear(dev, irq);
|
|
} else {
|
|
return DRVMGR_ENOSYS;
|
|
}
|
|
}
|
|
|
|
static int ambapp_int_mask(
|
|
struct drvmgr_dev *dev,
|
|
int index)
|
|
{
|
|
struct ambapp_priv *priv;
|
|
int irq;
|
|
|
|
priv = dev->parent->priv;
|
|
|
|
/* Get IRQ number from index and device information */
|
|
irq = ambapp_int_get(dev, index);
|
|
if ( irq < 0 )
|
|
return -1;
|
|
|
|
DBG("MASK interrupt on 0x%x for dev 0x%x (IRQ: %d)\n",
|
|
(unsigned int)dev->parent->dev, (unsigned int)dev, irq);
|
|
|
|
if ( priv->config->ops->int_mask ) {
|
|
/* Let device override driver default */
|
|
return priv->config->ops->int_mask(dev, irq);
|
|
} else {
|
|
return DRVMGR_ENOSYS;
|
|
}
|
|
}
|
|
|
|
static int ambapp_int_unmask(
|
|
struct drvmgr_dev *dev,
|
|
int index)
|
|
{
|
|
struct ambapp_priv *priv;
|
|
int irq;
|
|
|
|
priv = dev->parent->priv;
|
|
|
|
/* Get IRQ number from index and device information */
|
|
irq = ambapp_int_get(dev, index);
|
|
if ( irq < 0 )
|
|
return DRVMGR_EINVAL;
|
|
|
|
DBG("UNMASK interrupt on 0x%x for dev 0x%x (IRQ: %d)\n",
|
|
(unsigned int)dev->parent->dev, (unsigned int)dev, irq);
|
|
|
|
if ( priv->config->ops->int_unmask ) {
|
|
/* Let device override driver default */
|
|
return priv->config->ops->int_unmask(dev, irq);
|
|
} else {
|
|
return DRVMGR_ENOSYS;
|
|
}
|
|
}
|
|
|
|
/* Assign frequency to an AMBA Bus */
|
|
void ambapp_bus_freq_register(
|
|
struct drvmgr_dev *dev,
|
|
int amba_interface,
|
|
unsigned int freq_hz
|
|
)
|
|
{
|
|
struct ambapp_priv *priv = (struct ambapp_priv *)dev->parent->priv;
|
|
struct ambapp_dev *adev;
|
|
struct amba_dev_info *pnp = dev->businfo;
|
|
|
|
if ( freq_hz == 0 )
|
|
return;
|
|
|
|
if ( amba_interface == DEV_AHB_MST ) {
|
|
adev = (struct ambapp_dev *)
|
|
((unsigned int)pnp->info.ahb_mst -
|
|
sizeof(struct ambapp_dev));
|
|
} else if ( amba_interface == DEV_AHB_SLV ) {
|
|
adev = (struct ambapp_dev *)
|
|
((unsigned int)pnp->info.ahb_slv -
|
|
sizeof(struct ambapp_dev));
|
|
} else if ( amba_interface == DEV_APB_SLV ) {
|
|
adev = (struct ambapp_dev *)
|
|
((unsigned int)pnp->info.apb_slv -
|
|
sizeof(struct ambapp_dev));
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
/* Calculate Top bus frequency from lower part. The frequency comes
|
|
* from some kind of hardware able to report local bus frequency.
|
|
*/
|
|
ambapp_freq_init(priv->config->abus, adev, freq_hz);
|
|
}
|
|
|
|
static int ambapp_bus_freq_get(
|
|
struct drvmgr_dev *dev,
|
|
int options,
|
|
unsigned int *freq_hz)
|
|
{
|
|
struct ambapp_priv *priv = (struct ambapp_priv *)dev->parent->priv;
|
|
struct ambapp_dev *adev;
|
|
struct amba_dev_info *pnp = dev->businfo;
|
|
|
|
if ( options == DEV_AHB_MST ) {
|
|
adev = (struct ambapp_dev *)
|
|
((unsigned int)pnp->info.ahb_mst -
|
|
sizeof(struct ambapp_dev));
|
|
} else if ( options == DEV_AHB_SLV ) {
|
|
adev = (struct ambapp_dev *)
|
|
((unsigned int)pnp->info.ahb_slv -
|
|
sizeof(struct ambapp_dev));
|
|
} else if ( options == DEV_APB_SLV ) {
|
|
adev = (struct ambapp_dev *)
|
|
((unsigned int)pnp->info.apb_slv -
|
|
sizeof(struct ambapp_dev));
|
|
} else {
|
|
*freq_hz = 0;
|
|
return -1;
|
|
}
|
|
|
|
/* Calculate core/bus frequency from top most bus frequency. */
|
|
*freq_hz = ambapp_freq_get(priv->config->abus, adev);
|
|
if ( *freq_hz == 0 )
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static int ambapp_get_params(
|
|
struct drvmgr_dev *dev,
|
|
struct drvmgr_bus_params *params)
|
|
{
|
|
struct ambapp_priv *priv = dev->parent->priv;
|
|
|
|
if ( priv->config->ops->get_params ) {
|
|
/* Let device override driver default */
|
|
return priv->config->ops->get_params(dev, params);
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
#ifdef AMBAPPBUS_INFO_AVAIL
|
|
static void ambapp_dev_info(
|
|
struct drvmgr_dev *dev,
|
|
void (*print_line)(void *p, char *str),
|
|
void *p)
|
|
{
|
|
struct amba_dev_info *devinfo;
|
|
struct ambapp_core *core;
|
|
char buf[64];
|
|
int ver, i;
|
|
char *str1, *str2, *str3;
|
|
unsigned int ahbmst_freq, ahbslv_freq, apbslv_freq;
|
|
|
|
if (!dev)
|
|
return;
|
|
|
|
devinfo = (struct amba_dev_info *)dev->businfo;
|
|
if (!devinfo)
|
|
return;
|
|
core = &devinfo->info;
|
|
|
|
print_line(p, "AMBA PnP DEVICE");
|
|
|
|
str1 = ambapp_vendor_id2str(devinfo->id.vendor);
|
|
if (str1 == NULL)
|
|
str1 = "unknown";
|
|
sprintf(buf, "VENDOR ID: 0x%04x (%s)", devinfo->id.vendor, str1);
|
|
print_line(p, buf);
|
|
|
|
str1 = ambapp_device_id2str(devinfo->id.vendor, devinfo->id.device);
|
|
if (str1 == NULL)
|
|
str1 = "unknown";
|
|
sprintf(buf, "DEVICE ID: 0x%04x (%s)", devinfo->id.device, str1);
|
|
print_line(p, buf);
|
|
|
|
ahbmst_freq = ahbslv_freq = apbslv_freq = 0;
|
|
ver = 0;
|
|
str1 = str2 = str3 = "";
|
|
if (core->ahb_mst) {
|
|
str1 = "AHBMST ";
|
|
ver = core->ahb_mst->common.ver;
|
|
ambapp_bus_freq_get(dev, DEV_AHB_MST, &ahbmst_freq);
|
|
}
|
|
if (core->ahb_slv) {
|
|
str2 = "AHBSLV ";
|
|
ver = core->ahb_slv->common.ver;
|
|
ambapp_bus_freq_get(dev, DEV_AHB_SLV, &ahbslv_freq);
|
|
}
|
|
if (core->apb_slv) {
|
|
str3 = "APBSLV";
|
|
ver = core->apb_slv->common.ver;
|
|
ambapp_bus_freq_get(dev, DEV_APB_SLV, &apbslv_freq);
|
|
}
|
|
|
|
sprintf(buf, "IRQ: %d", ambapp_int_get(dev, 0));
|
|
print_line(p, buf);
|
|
|
|
sprintf(buf, "VERSION: 0x%x", ver);
|
|
print_line(p, buf);
|
|
|
|
sprintf(buf, "ambapp_core: %p", core);
|
|
print_line(p, buf);
|
|
|
|
sprintf(buf, "interfaces: %s%s%s", str1, str2, str3);
|
|
print_line(p, buf);
|
|
|
|
if (ahbmst_freq != 0) {
|
|
sprintf(buf, "AHBMST FREQ: %dkHz", ahbmst_freq/1000);
|
|
print_line(p, buf);
|
|
}
|
|
|
|
if (ahbslv_freq != 0) {
|
|
sprintf(buf, "AHBSLV FREQ: %dkHz", ahbslv_freq/1000);
|
|
print_line(p, buf);
|
|
}
|
|
|
|
if (apbslv_freq != 0) {
|
|
sprintf(buf, "APBSLV FREQ: %dkHz", apbslv_freq/1000);
|
|
print_line(p, buf);
|
|
}
|
|
|
|
if (core->ahb_slv) {
|
|
for(i=0; i<4; i++) {
|
|
if (core->ahb_slv->type[i] == AMBA_TYPE_AHBIO)
|
|
str1 = " ahbio";
|
|
else if (core->ahb_slv->type[i] == AMBA_TYPE_MEM)
|
|
str1 = "ahbmem";
|
|
else
|
|
continue;
|
|
sprintf(buf, " %s[%d]: 0x%08x-0x%08x", str1, i,
|
|
core->ahb_slv->start[i],
|
|
core->ahb_slv->start[i]+core->ahb_slv->mask[i]-1);
|
|
print_line(p, buf);
|
|
}
|
|
}
|
|
if (core->apb_slv) {
|
|
sprintf(buf, " apb: 0x%08x-0x%08x",
|
|
core->apb_slv->start,
|
|
core->apb_slv->start + core->apb_slv->mask - 1);
|
|
print_line(p, buf);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Fix device in last stage and/or register additional devices.
|
|
* Function returns:
|
|
* 0 Register device as normal
|
|
* 1 Fixup function handles registration
|
|
*/
|
|
static int ambapp_dev_fixup(struct drvmgr_dev *dev, struct amba_dev_info *pnp)
|
|
{
|
|
/* OCCAN speciality:
|
|
* Mulitple cores are supported through the same amba AHB interface.
|
|
* The number of "sub cores" can be detected by decoding the AMBA
|
|
* Plug&Play version information. verion = ncores. A maximum of 8
|
|
* sub cores are supported, each separeated with 0x100 inbetween.
|
|
*
|
|
* Now, lets detect sub cores.
|
|
*/
|
|
if ( (pnp->info.device == GAISLER_CANAHB) &&
|
|
(pnp->info.vendor == VENDOR_GAISLER) ) {
|
|
struct drvmgr_dev *newdev, *devs_to_register[8];
|
|
struct amba_dev_info *pnpinfo;
|
|
int subcores;
|
|
int core;
|
|
|
|
devs_to_register[0] = dev;
|
|
subcores = (pnp->info.ahb_slv->common.ver & 0x7) + 1;
|
|
for(core = 1; core < subcores; core++) {
|
|
drvmgr_alloc_dev(&newdev, sizeof(*pnpinfo));
|
|
memcpy(newdev, dev, sizeof(*newdev));
|
|
pnpinfo = (struct amba_dev_info *)(newdev+1);
|
|
memcpy(pnpinfo, pnp, sizeof(*pnp));
|
|
pnpinfo->info.index = core;
|
|
pnpinfo->info.irq += core;
|
|
newdev->businfo = (void *)pnpinfo;
|
|
|
|
devs_to_register[core] = newdev;
|
|
}
|
|
/* Register all CAN devices */
|
|
for(core = 0; core < subcores; core++)
|
|
drvmgr_dev_register(devs_to_register[core]);
|
|
return 1;
|
|
} else if ( (pnp->info.device == GAISLER_GPIO) &&
|
|
(pnp->info.vendor == VENDOR_GAISLER) ) {
|
|
/* PIO[N] is connected to IRQ[N]. */
|
|
pnp->info.irq = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
struct ambapp_dev_reg_struct {
|
|
struct ambapp_bus *abus;
|
|
struct drvmgr_bus *bus;
|
|
struct ambapp_dev *ahb_mst;
|
|
struct ambapp_dev *ahb_slv;
|
|
struct ambapp_dev *apb_slv;
|
|
};
|
|
|
|
static void ambapp_core_register(
|
|
struct ambapp_dev *ahb_mst,
|
|
struct ambapp_dev *ahb_slv,
|
|
struct ambapp_dev *apb_slv,
|
|
struct ambapp_dev_reg_struct *arg
|
|
)
|
|
{
|
|
struct drvmgr_dev *newdev;
|
|
struct amba_dev_info *pnpinfo;
|
|
unsigned short device;
|
|
unsigned char vendor;
|
|
int namelen;
|
|
char buf[64];
|
|
|
|
if ( ahb_mst ) {
|
|
device = ahb_mst->device;
|
|
vendor = ahb_mst->vendor;
|
|
}else if ( ahb_slv ) {
|
|
device = ahb_slv->device;
|
|
vendor = ahb_slv->vendor;
|
|
}else if( apb_slv ) {
|
|
device = apb_slv->device;
|
|
vendor = apb_slv->vendor;
|
|
} else {
|
|
DBG("NO DEV!\n");
|
|
return;
|
|
}
|
|
|
|
DBG("CORE REGISTER DEV [%x:%x] MST: 0x%x, SLV: 0x%x, APB: 0x%x\n", vendor, device, (unsigned int)ahb_mst, (unsigned int)ahb_slv, (unsigned int)apb_slv);
|
|
|
|
/* Get unique device name from AMBA data base by combining VENDOR and
|
|
* DEVICE short names
|
|
*/
|
|
namelen = ambapp_vendev_id2str(vendor, device, buf);
|
|
|
|
/* Allocate a device */
|
|
drvmgr_alloc_dev(&newdev, sizeof(struct amba_dev_info) + namelen);
|
|
pnpinfo = (struct amba_dev_info *)(newdev + 1);
|
|
newdev->parent = arg->bus; /* Ourselfs */
|
|
newdev->minor_drv = 0;
|
|
newdev->minor_bus = 0;
|
|
newdev->priv = NULL;
|
|
newdev->drv = NULL;
|
|
if (namelen > 0) {
|
|
newdev->name = (char *)(pnpinfo + 1);
|
|
strcpy(newdev->name, buf);
|
|
} else {
|
|
newdev->name = NULL;
|
|
}
|
|
newdev->next_in_drv = NULL;
|
|
newdev->bus = NULL;
|
|
|
|
/* Init PnP information, Assign Core interfaces with this device */
|
|
pnpinfo->id.vendor = vendor;
|
|
pnpinfo->id.device = device;
|
|
pnpinfo->info.vendor = vendor;
|
|
pnpinfo->info.device = device;
|
|
pnpinfo->info.index = 0;
|
|
if ( ahb_mst ) {
|
|
pnpinfo->info.ahb_mst = (struct ambapp_ahb_info *)
|
|
ahb_mst->devinfo;
|
|
ambapp_alloc_dev(ahb_mst, (void *)newdev);
|
|
if ( pnpinfo->info.ahb_mst->common.irq )
|
|
pnpinfo->info.irq = pnpinfo->info.ahb_mst->common.irq;
|
|
}
|
|
if ( ahb_slv ) {
|
|
pnpinfo->info.ahb_slv = (struct ambapp_ahb_info *)
|
|
ahb_slv->devinfo;
|
|
ambapp_alloc_dev(ahb_slv, (void *)newdev);
|
|
if ( pnpinfo->info.ahb_slv->common.irq )
|
|
pnpinfo->info.irq = pnpinfo->info.ahb_slv->common.irq;
|
|
}
|
|
if ( apb_slv ) {
|
|
pnpinfo->info.apb_slv = (struct ambapp_apb_info *)
|
|
apb_slv->devinfo;
|
|
ambapp_alloc_dev(apb_slv, (void *)newdev);
|
|
if ( pnpinfo->info.apb_slv->common.irq )
|
|
pnpinfo->info.irq = pnpinfo->info.apb_slv->common.irq;
|
|
}
|
|
if ( pnpinfo->info.irq == 0 )
|
|
pnpinfo->info.irq = -1; /* indicate no IRQ */
|
|
|
|
/* Connect device with PnP information */
|
|
newdev->businfo = (void *)pnpinfo;
|
|
|
|
if ( ambapp_dev_fixup(newdev, pnpinfo) == 0 )
|
|
drvmgr_dev_register(newdev); /* Register New Device */
|
|
}
|
|
|
|
/* Fix device registration.
|
|
* Function returns:
|
|
* 0 Register device as normal
|
|
* 1 Fixup function handles registration
|
|
*/
|
|
static int ambapp_dev_register_fixup(struct ambapp_dev *dev, struct ambapp_dev_reg_struct *p)
|
|
{
|
|
/* GR740 GRPCI2 speciality:
|
|
* - In the GR740 the APB_SLV is detected before the AHB_SLV
|
|
* which makes the registration incorrect. We deal with it in
|
|
* this function. */
|
|
if ( (dev->dev_type == DEV_APB_SLV) &&
|
|
(dev->device == GAISLER_GRPCI2) &&
|
|
(dev->vendor == VENDOR_GAISLER) &&
|
|
(p->ahb_slv == NULL) ) {
|
|
DBG("GRPCI2 APB_SLV detected before AHB_SLV. Skipping APB_SLV registration.\n");
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Register one AMBA device */
|
|
static int ambapp_dev_register(struct ambapp_dev *dev, int index, void *arg)
|
|
{
|
|
struct ambapp_dev_reg_struct *p = arg;
|
|
|
|
#ifdef DEBUG
|
|
char *type;
|
|
|
|
if ( dev->dev_type == DEV_AHB_MST )
|
|
type = "AHB MST";
|
|
else if ( dev->dev_type == DEV_AHB_SLV )
|
|
type = "AHB SLV";
|
|
else if ( dev->dev_type == DEV_APB_SLV )
|
|
type = "APB SLV";
|
|
|
|
DBG("Found [%d:%x:%x], %s\n", index, dev->vendor, dev->device, type);
|
|
#endif
|
|
|
|
/* Fixup for device registration */
|
|
if (ambapp_dev_register_fixup(dev, p)){
|
|
return 0;
|
|
}
|
|
|
|
if ( dev->dev_type == DEV_AHB_MST ) {
|
|
if ( p->ahb_mst ) {
|
|
/* This should not happen */
|
|
printk("ambapp_dev_register: ahb_mst not NULL!\n");
|
|
exit(1);
|
|
}
|
|
|
|
/* Remember AHB Master */
|
|
p->ahb_mst = dev;
|
|
|
|
/* Find AHB Slave and APB slave for this Core */
|
|
ambapp_for_each(p->abus, (OPTIONS_AHB_SLVS|OPTIONS_APB_SLVS|OPTIONS_FREE), dev->vendor, dev->device, ambapp_dev_register, p);
|
|
|
|
ambapp_core_register(p->ahb_mst, p->ahb_slv, p->apb_slv, p);
|
|
p->ahb_mst = p->ahb_slv = p->apb_slv = NULL;
|
|
return 0;
|
|
|
|
} else if ( dev->dev_type == DEV_AHB_SLV ) {
|
|
if ( p->ahb_slv ) {
|
|
/* Already got our AHB Slave interface */
|
|
return 0;
|
|
}
|
|
|
|
/* Remember AHB Slave */
|
|
p->ahb_slv = dev;
|
|
|
|
if ( p->ahb_mst ) {
|
|
/* Continue searching for APB Slave */
|
|
return 0;
|
|
} else {
|
|
/* Find APB Slave interface for this Core */
|
|
ambapp_for_each(p->abus, (OPTIONS_APB_SLVS|OPTIONS_FREE), dev->vendor, dev->device, ambapp_dev_register, p);
|
|
|
|
ambapp_core_register(p->ahb_mst, p->ahb_slv, p->apb_slv, p);
|
|
p->ahb_mst = p->ahb_slv = p->apb_slv = NULL;
|
|
return 0;
|
|
}
|
|
} else if ( dev->dev_type == DEV_APB_SLV ) {
|
|
if ( p->apb_slv ) {
|
|
/* This should not happen */
|
|
printk("ambapp_dev_register: apb_slv not NULL!\n");
|
|
exit(1);
|
|
}
|
|
/* Remember APB Slave */
|
|
p->apb_slv = dev;
|
|
|
|
if ( p->ahb_mst || p->ahb_slv ) {
|
|
/* Stop scanning */
|
|
return 1;
|
|
} else {
|
|
ambapp_core_register(p->ahb_mst, p->ahb_slv, p->apb_slv, p);
|
|
p->ahb_mst = p->ahb_slv = p->apb_slv = NULL;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Register all AMBA devices available on the AMBAPP bus */
|
|
static int ambapp_ids_register(struct drvmgr_bus *bus)
|
|
{
|
|
struct ambapp_priv *priv = bus->priv;
|
|
struct ambapp_bus *abus;
|
|
struct ambapp_dev_reg_struct arg;
|
|
|
|
DBG("ambapp_ids_register:\n");
|
|
|
|
memset(&arg, 0, sizeof(arg));
|
|
|
|
abus = priv->config->abus;
|
|
arg.abus = abus;
|
|
arg.bus = bus;
|
|
|
|
/* Combine the AHB MST, AHB SLV and APB SLV interfaces of a core. A core has often more than
|
|
* one interface. A core can not have more than one interface of the same type.
|
|
*/
|
|
ambapp_for_each(abus, (OPTIONS_ALL_DEVS|OPTIONS_FREE), -1, -1, ambapp_dev_register, &arg);
|
|
|
|
#ifdef DEBUG
|
|
ambapp_print(abus->root, 1);
|
|
#endif
|
|
|
|
return DRVMGR_OK;
|
|
}
|
|
|
|
/*** DEVICE FUNCTIONS ***/
|
|
|
|
int ambapp_bus_register(struct drvmgr_dev *dev, struct ambapp_config *config)
|
|
{
|
|
struct ambapp_priv *priv;
|
|
|
|
if ( !config || !config->ops )
|
|
return DRVMGR_OK;
|
|
|
|
DBG("AMBAPP BUS: initializing\n");
|
|
|
|
/* Register BUS */
|
|
drvmgr_alloc_bus(&dev->bus, sizeof(struct ambapp_priv));
|
|
priv = (struct ambapp_priv *)(dev->bus + 1);
|
|
priv->config = config;
|
|
if ( priv->config->bus_type == DRVMGR_BUS_TYPE_AMBAPP_DIST )
|
|
dev->bus->bus_type = DRVMGR_BUS_TYPE_AMBAPP_DIST;
|
|
else if ( priv->config->bus_type == DRVMGR_BUS_TYPE_AMBAPP_RMAP )
|
|
dev->bus->bus_type = DRVMGR_BUS_TYPE_AMBAPP_RMAP;
|
|
else
|
|
dev->bus->bus_type = DRVMGR_BUS_TYPE_AMBAPP;
|
|
dev->bus->next = NULL;
|
|
dev->bus->dev = dev;
|
|
dev->bus->priv = priv;
|
|
dev->bus->children = NULL;
|
|
dev->bus->ops = &ambapp_bus_ops;
|
|
dev->bus->funcs = config->funcs;
|
|
dev->bus->dev_cnt = 0;
|
|
dev->bus->reslist = NULL;
|
|
dev->bus->maps_up = config->maps_up;
|
|
dev->bus->maps_down = config->maps_down;
|
|
|
|
/* Add resource configuration */
|
|
if ( priv->config->resources )
|
|
drvmgr_bus_res_add(dev->bus, priv->config->resources);
|
|
|
|
drvmgr_bus_register(dev->bus);
|
|
|
|
return DRVMGR_OK;
|
|
}
|
|
|
|
/*** BUS INITIALIZE FUNCTIONS ***/
|
|
|
|
/* Initialize the bus, register devices on this bus */
|
|
static int ambapp_bus_init1(struct drvmgr_bus *bus)
|
|
{
|
|
/* Initialize the bus, register devices on this bus */
|
|
return ambapp_ids_register(bus);
|
|
}
|
|
|
|
static int ambapp_bus_remove(struct drvmgr_bus *bus)
|
|
{
|
|
return DRVMGR_OK;
|
|
}
|
|
|
|
#ifdef RTEMS_SMP
|
|
static int ambapp_int_set_affinity(
|
|
struct drvmgr_dev *dev,
|
|
int index,
|
|
const Processor_mask *cpus)
|
|
{
|
|
struct ambapp_priv *priv;
|
|
int irq;
|
|
|
|
priv = dev->parent->priv;
|
|
|
|
/* Get IRQ number from index and device information */
|
|
irq = ambapp_int_get(dev, index);
|
|
if (irq < 0)
|
|
return DRVMGR_EINVAL;
|
|
|
|
DBG("Set interrupt affinity on 0x%x for dev 0x%x (IRQ: %d)\n",
|
|
(unsigned int)dev->parent->dev, (unsigned int)dev, irq);
|
|
|
|
if (priv->config->ops->int_set_affinity) {
|
|
/* Let device override driver default */
|
|
return priv->config->ops->int_set_affinity(dev, irq, cpus);
|
|
} else {
|
|
return DRVMGR_ENOSYS;
|
|
}
|
|
}
|
|
#endif
|