Commit e5cabeb3 authored by Alexandre Bounine's avatar Alexandre Bounine Committed by Linus Torvalds

rapidio: add Port-Write handling for EM

Add RapidIO Port-Write message handling in the context of Error
   Management Extensions Specification Rev.1.3.
Signed-off-by: default avatarAlexandre Bounine <alexandre.bounine@idt.com>
Tested-by: default avatarThomas Moll <thomas.moll@sysgo.com>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
Cc: Kumar Gala <galak@kernel.crashing.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 818a04a0
......@@ -1057,7 +1057,7 @@ int fsl_rio_setup(struct of_device *dev)
dev_info(&dev->dev, "LAW start 0x%016llx, size 0x%016llx.\n",
law_start, law_size);
ops = kmalloc(sizeof(struct rio_ops), GFP_KERNEL);
ops = kzalloc(sizeof(struct rio_ops), GFP_KERNEL);
if (!ops) {
rc = -ENOMEM;
goto err_ops;
......
This diff is collapsed.
......@@ -5,6 +5,10 @@
* Copyright 2005 MontaVista Software, Inc.
* Matt Porter <mporter@kernel.crashing.org>
*
* Copyright 2009 Integrated Device Technology, Inc.
* Alex Bounine <alexandre.bounine@idt.com>
* - Added Port-Write/Error Management initialization and handling
*
* 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
......@@ -332,6 +336,329 @@ int rio_release_outb_dbell(struct rio_dev *rdev, struct resource *res)
return rc;
}
/**
* rio_request_inb_pwrite - request inbound port-write message service
* @mport: RIO device to which register inbound port-write callback routine
* @pwcback: Callback routine to execute when port-write is received
*
* Binds a port-write callback function to the RapidIO device.
* Returns 0 if the request has been satisfied.
*/
int rio_request_inb_pwrite(struct rio_dev *rdev,
int (*pwcback)(struct rio_dev *rdev, union rio_pw_msg *msg, int step))
{
int rc = 0;
spin_lock(&rio_global_list_lock);
if (rdev->pwcback != NULL)
rc = -ENOMEM;
else
rdev->pwcback = pwcback;
spin_unlock(&rio_global_list_lock);
return rc;
}
EXPORT_SYMBOL_GPL(rio_request_inb_pwrite);
/**
* rio_release_inb_pwrite - release inbound port-write message service
* @rdev: RIO device which registered for inbound port-write callback
*
* Removes callback from the rio_dev structure. Returns 0 if the request
* has been satisfied.
*/
int rio_release_inb_pwrite(struct rio_dev *rdev)
{
int rc = -ENOMEM;
spin_lock(&rio_global_list_lock);
if (rdev->pwcback) {
rdev->pwcback = NULL;
rc = 0;
}
spin_unlock(&rio_global_list_lock);
return rc;
}
EXPORT_SYMBOL_GPL(rio_release_inb_pwrite);
/**
* rio_mport_get_physefb - Helper function that returns register offset
* for Physical Layer Extended Features Block.
* @rdev: RIO device
*/
u32
rio_mport_get_physefb(struct rio_mport *port, int local,
u16 destid, u8 hopcount)
{
u32 ext_ftr_ptr;
u32 ftr_header;
ext_ftr_ptr = rio_mport_get_efb(port, local, destid, hopcount, 0);
while (ext_ftr_ptr) {
if (local)
rio_local_read_config_32(port, ext_ftr_ptr,
&ftr_header);
else
rio_mport_read_config_32(port, destid, hopcount,
ext_ftr_ptr, &ftr_header);
ftr_header = RIO_GET_BLOCK_ID(ftr_header);
switch (ftr_header) {
case RIO_EFB_SER_EP_ID_V13P:
case RIO_EFB_SER_EP_REC_ID_V13P:
case RIO_EFB_SER_EP_FREE_ID_V13P:
case RIO_EFB_SER_EP_ID:
case RIO_EFB_SER_EP_REC_ID:
case RIO_EFB_SER_EP_FREE_ID:
case RIO_EFB_SER_EP_FREC_ID:
return ext_ftr_ptr;
default:
break;
}
ext_ftr_ptr = rio_mport_get_efb(port, local, destid,
hopcount, ext_ftr_ptr);
}
return ext_ftr_ptr;
}
/**
* rio_get_comptag - Begin or continue searching for a RIO device by component tag
* @comp_tag: RIO component tad to match
* @from: Previous RIO device found in search, or %NULL for new search
*
* Iterates through the list of known RIO devices. If a RIO device is
* found with a matching @comp_tag, a pointer to its device
* structure is returned. Otherwise, %NULL is returned. A new search
* is initiated by passing %NULL to the @from argument. Otherwise, if
* @from is not %NULL, searches continue from next device on the global
* list.
*/
static struct rio_dev *rio_get_comptag(u32 comp_tag, struct rio_dev *from)
{
struct list_head *n;
struct rio_dev *rdev;
WARN_ON(in_interrupt());
spin_lock(&rio_global_list_lock);
n = from ? from->global_list.next : rio_devices.next;
while (n && (n != &rio_devices)) {
rdev = rio_dev_g(n);
if (rdev->comp_tag == comp_tag)
goto exit;
n = n->next;
}
rdev = NULL;
exit:
spin_unlock(&rio_global_list_lock);
return rdev;
}
/**
* rio_set_port_lockout - Sets/clears LOCKOUT bit (RIO EM 1.3) for a switch port.
* @rdev: Pointer to RIO device control structure
* @pnum: Switch port number to set LOCKOUT bit
* @lock: Operation : set (=1) or clear (=0)
*/
int rio_set_port_lockout(struct rio_dev *rdev, u32 pnum, int lock)
{
u8 hopcount = 0xff;
u16 destid = rdev->destid;
u32 regval;
if (rdev->rswitch) {
destid = rdev->rswitch->destid;
hopcount = rdev->rswitch->hopcount;
}
rio_mport_read_config_32(rdev->net->hport, destid, hopcount,
rdev->phys_efptr + RIO_PORT_N_CTL_CSR(pnum),
&regval);
if (lock)
regval |= RIO_PORT_N_CTL_LOCKOUT;
else
regval &= ~RIO_PORT_N_CTL_LOCKOUT;
rio_mport_write_config_32(rdev->net->hport, destid, hopcount,
rdev->phys_efptr + RIO_PORT_N_CTL_CSR(pnum),
regval);
return 0;
}
/**
* rio_inb_pwrite_handler - process inbound port-write message
* @pw_msg: pointer to inbound port-write message
*
* Processes an inbound port-write message. Returns 0 if the request
* has been satisfied.
*/
int rio_inb_pwrite_handler(union rio_pw_msg *pw_msg)
{
struct rio_dev *rdev;
struct rio_mport *mport;
u8 hopcount;
u16 destid;
u32 err_status;
int rc, portnum;
rdev = rio_get_comptag(pw_msg->em.comptag, NULL);
if (rdev == NULL) {
/* Someting bad here (probably enumeration error) */
pr_err("RIO: %s No matching device for CTag 0x%08x\n",
__func__, pw_msg->em.comptag);
return -EIO;
}
pr_debug("RIO: Port-Write message from %s\n", rio_name(rdev));
#ifdef DEBUG_PW
{
u32 i;
for (i = 0; i < RIO_PW_MSG_SIZE/sizeof(u32);) {
pr_debug("0x%02x: %08x %08x %08x %08x",
i*4, pw_msg->raw[i], pw_msg->raw[i + 1],
pw_msg->raw[i + 2], pw_msg->raw[i + 3]);
i += 4;
}
pr_debug("\n");
}
#endif
/* Call an external service function (if such is registered
* for this device). This may be the service for endpoints that send
* device-specific port-write messages. End-point messages expected
* to be handled completely by EP specific device driver.
* For switches rc==0 signals that no standard processing required.
*/
if (rdev->pwcback != NULL) {
rc = rdev->pwcback(rdev, pw_msg, 0);
if (rc == 0)
return 0;
}
/* For End-point devices processing stops here */
if (!(rdev->pef & RIO_PEF_SWITCH))
return 0;
if (rdev->phys_efptr == 0) {
pr_err("RIO_PW: Bad switch initialization for %s\n",
rio_name(rdev));
return 0;
}
mport = rdev->net->hport;
destid = rdev->rswitch->destid;
hopcount = rdev->rswitch->hopcount;
/*
* Process the port-write notification from switch
*/
portnum = pw_msg->em.is_port & 0xFF;
if (rdev->rswitch->em_handle)
rdev->rswitch->em_handle(rdev, portnum);
rio_mport_read_config_32(mport, destid, hopcount,
rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(portnum),
&err_status);
pr_debug("RIO_PW: SP%d_ERR_STS_CSR=0x%08x\n", portnum, err_status);
if (pw_msg->em.errdetect) {
pr_debug("RIO_PW: RIO_EM_P%d_ERR_DETECT=0x%08x\n",
portnum, pw_msg->em.errdetect);
/* Clear EM Port N Error Detect CSR */
rio_mport_write_config_32(mport, destid, hopcount,
rdev->em_efptr + RIO_EM_PN_ERR_DETECT(portnum), 0);
}
if (pw_msg->em.ltlerrdet) {
pr_debug("RIO_PW: RIO_EM_LTL_ERR_DETECT=0x%08x\n",
pw_msg->em.ltlerrdet);
/* Clear EM L/T Layer Error Detect CSR */
rio_mport_write_config_32(mport, destid, hopcount,
rdev->em_efptr + RIO_EM_LTL_ERR_DETECT, 0);
}
/* Clear Port Errors */
rio_mport_write_config_32(mport, destid, hopcount,
rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(portnum),
err_status & RIO_PORT_N_ERR_STS_CLR_MASK);
if (rdev->rswitch->port_ok & (1 << portnum)) {
if (err_status & RIO_PORT_N_ERR_STS_PORT_UNINIT) {
rdev->rswitch->port_ok &= ~(1 << portnum);
rio_set_port_lockout(rdev, portnum, 1);
rio_mport_write_config_32(mport, destid, hopcount,
rdev->phys_efptr +
RIO_PORT_N_ACK_STS_CSR(portnum),
RIO_PORT_N_ACK_CLEAR);
/* Schedule Extraction Service */
pr_debug("RIO_PW: Device Extraction on [%s]-P%d\n",
rio_name(rdev), portnum);
}
} else {
if (err_status & RIO_PORT_N_ERR_STS_PORT_OK) {
rdev->rswitch->port_ok |= (1 << portnum);
rio_set_port_lockout(rdev, portnum, 0);
/* Schedule Insertion Service */
pr_debug("RIO_PW: Device Insertion on [%s]-P%d\n",
rio_name(rdev), portnum);
}
}
/* Clear Port-Write Pending bit */
rio_mport_write_config_32(mport, destid, hopcount,
rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(portnum),
RIO_PORT_N_ERR_STS_PW_PEND);
return 0;
}
EXPORT_SYMBOL_GPL(rio_inb_pwrite_handler);
/**
* rio_mport_get_efb - get pointer to next extended features block
* @port: Master port to issue transaction
* @local: Indicate a local master port or remote device access
* @destid: Destination ID of the device
* @hopcount: Number of switch hops to the device
* @from: Offset of current Extended Feature block header (if 0 starts
* from ExtFeaturePtr)
*/
u32
rio_mport_get_efb(struct rio_mport *port, int local, u16 destid,
u8 hopcount, u32 from)
{
u32 reg_val;
if (from == 0) {
if (local)
rio_local_read_config_32(port, RIO_ASM_INFO_CAR,
&reg_val);
else
rio_mport_read_config_32(port, destid, hopcount,
RIO_ASM_INFO_CAR, &reg_val);
return reg_val & RIO_EXT_FTR_PTR_MASK;
} else {
if (local)
rio_local_read_config_32(port, from, &reg_val);
else
rio_mport_read_config_32(port, destid, hopcount,
from, &reg_val);
return RIO_GET_BLOCK_ID(reg_val);
}
}
/**
* rio_mport_get_feature - query for devices' extended features
* @port: Master port to issue transaction
......@@ -472,6 +799,7 @@ int rio_std_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount,
RIO_STD_RTE_CONF_PORT_SEL_CSR,
(u32)route_port);
}
udelay(10);
return 0;
}
......
......@@ -18,6 +18,10 @@
extern u32 rio_mport_get_feature(struct rio_mport *mport, int local, u16 destid,
u8 hopcount, int ftr);
extern u32 rio_mport_get_physefb(struct rio_mport *port, int local,
u16 destid, u8 hopcount);
extern u32 rio_mport_get_efb(struct rio_mport *port, int local, u16 destid,
u8 hopcount, u32 from);
extern int rio_create_sysfs_dev_files(struct rio_dev *rdev);
extern int rio_enum_mport(struct rio_mport *mport);
extern int rio_disc_mport(struct rio_mport *mport);
......@@ -29,6 +33,7 @@ extern int rio_std_route_get_entry(struct rio_mport *mport, u16 destid,
u8 *route_port);
extern int rio_std_route_clr_table(struct rio_mport *mport, u16 destid,
u8 hopcount, u16 table);
extern int rio_set_port_lockout(struct rio_dev *rdev, u32 pnum, int lock);
/* Structures internal to the RIO core code */
extern struct device_attribute rio_dev_attrs[];
......@@ -61,3 +66,28 @@ extern struct rio_route_ops __end_rio_route_ops[];
#define RIO_GET_DID(size, x) (size ? (x & 0xffff) : ((x & 0x00ff0000) >> 16))
#define RIO_SET_DID(size, x) (size ? (x & 0xffff) : ((x & 0x000000ff) << 16))
/*
* RapidIO Error Management
*/
extern struct rio_em_ops __start_rio_em_ops[];
extern struct rio_em_ops __end_rio_em_ops[];
/* Helpers internal to the RIO core code */
#define DECLARE_RIO_EM_SECTION(section, name, vid, did, init_hook, em_hook) \
static const struct rio_em_ops __rio_em_##name __used \
__section(section) = { vid, did, init_hook, em_hook };
/**
* DECLARE_RIO_EM_OPS - Registers switch EM operations
* @vid: RIO vendor ID
* @did: RIO device ID
* @init_hook: Callback that initializes device specific EM
* @em_hook: Callback that handles device specific EM
*
* A &struct rio_em_ops is initialized with the ops and placed into a
* RIO-specific kernel section.
*/
#define DECLARE_RIO_EM_OPS(vid, did, init_hook, em_hook) \
DECLARE_RIO_EM_SECTION(.rio_em_ops, vid##did, \
vid, did, init_hook, em_hook)
......@@ -25,6 +25,9 @@
#define SPP_ROUTE_CFG_DESTID(n) (0x11070 + 0x100*n)
#define SPP_ROUTE_CFG_PORT(n) (0x11074 + 0x100*n)
#define TSI568_SP_MODE_BC 0x10004
#define TSI568_SP_MODE_PW_DIS 0x08000000
static int
tsi568_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount,
u16 table, u16 route_destid, u8 route_port)
......@@ -104,3 +107,24 @@ tsi568_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount,
}
DECLARE_RIO_ROUTE_OPS(RIO_VID_TUNDRA, RIO_DID_TSI568, tsi568_route_add_entry, tsi568_route_get_entry, tsi568_route_clr_table);
static int
tsi568_em_init(struct rio_dev *rdev)
{
struct rio_mport *mport = rdev->net->hport;
u16 destid = rdev->rswitch->destid;
u8 hopcount = rdev->rswitch->hopcount;
u32 regval;
pr_debug("TSI568 %s [%d:%d]\n", __func__, destid, hopcount);
/* Make sure that Port-Writes are disabled (for all ports) */
rio_mport_read_config_32(mport, destid, hopcount,
TSI568_SP_MODE_BC, &regval);
rio_mport_write_config_32(mport, destid, hopcount,
TSI568_SP_MODE_BC, regval | TSI568_SP_MODE_PW_DIS);
return 0;
}
DECLARE_RIO_EM_OPS(RIO_VID_TUNDRA, RIO_DID_TSI568, tsi568_em_init, NULL);
......@@ -25,6 +25,14 @@
#define SPP_ROUTE_CFG_DESTID(n) (0x11070 + 0x100*n)
#define SPP_ROUTE_CFG_PORT(n) (0x11074 + 0x100*n)
#define TSI578_SP_MODE(n) (0x11004 + n*0x100)
#define TSI578_SP_MODE_PW_DIS 0x08000000
#define TSI578_SP_CTL_INDEP(n) (0x13004 + n*0x100)
#define TSI578_SP_LUT_PEINF(n) (0x13010 + n*0x100)
#define TSI578_SP_CS_TX(n) (0x13014 + n*0x100)
#define TSI578_SP_INT_STATUS(n) (0x13018 + n*0x100)
static int
tsi57x_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount,
u16 table, u16 route_destid, u8 route_port)
......@@ -104,3 +112,148 @@ DECLARE_RIO_ROUTE_OPS(RIO_VID_TUNDRA, RIO_DID_TSI572, tsi57x_route_add_entry, ts
DECLARE_RIO_ROUTE_OPS(RIO_VID_TUNDRA, RIO_DID_TSI574, tsi57x_route_add_entry, tsi57x_route_get_entry, tsi57x_route_clr_table);
DECLARE_RIO_ROUTE_OPS(RIO_VID_TUNDRA, RIO_DID_TSI577, tsi57x_route_add_entry, tsi57x_route_get_entry, tsi57x_route_clr_table);
DECLARE_RIO_ROUTE_OPS(RIO_VID_TUNDRA, RIO_DID_TSI578, tsi57x_route_add_entry, tsi57x_route_get_entry, tsi57x_route_clr_table);
static int
tsi57x_em_init(struct rio_dev *rdev)
{
struct rio_mport *mport = rdev->net->hport;
u16 destid = rdev->rswitch->destid;
u8 hopcount = rdev->rswitch->hopcount;
u32 regval;
int portnum;
pr_debug("TSI578 %s [%d:%d]\n", __func__, destid, hopcount);
for (portnum = 0; portnum < 16; portnum++) {
/* Make sure that Port-Writes are enabled (for all ports) */
rio_mport_read_config_32(mport, destid, hopcount,
TSI578_SP_MODE(portnum), &regval);
rio_mport_write_config_32(mport, destid, hopcount,
TSI578_SP_MODE(portnum),
regval & ~TSI578_SP_MODE_PW_DIS);
/* Clear all pending interrupts */
rio_mport_read_config_32(mport, destid, hopcount,
rdev->phys_efptr +
RIO_PORT_N_ERR_STS_CSR(portnum),
&regval);
rio_mport_write_config_32(mport, destid, hopcount,
rdev->phys_efptr +
RIO_PORT_N_ERR_STS_CSR(portnum),
regval & 0x07120214);
rio_mport_read_config_32(mport, destid, hopcount,
TSI578_SP_INT_STATUS(portnum), &regval);
rio_mport_write_config_32(mport, destid, hopcount,
TSI578_SP_INT_STATUS(portnum),
regval & 0x000700bd);
/* Enable all interrupts to allow ports to send a port-write */
rio_mport_read_config_32(mport, destid, hopcount,
TSI578_SP_CTL_INDEP(portnum), &regval);
rio_mport_write_config_32(mport, destid, hopcount,
TSI578_SP_CTL_INDEP(portnum),
regval | 0x000b0000);
/* Skip next (odd) port if the current port is in x4 mode */
rio_mport_read_config_32(mport, destid, hopcount,
rdev->phys_efptr + RIO_PORT_N_CTL_CSR(portnum),
&regval);
if ((regval & RIO_PORT_N_CTL_PWIDTH) == RIO_PORT_N_CTL_PWIDTH_4)
portnum++;
}
return 0;
}
static int
tsi57x_em_handler(struct rio_dev *rdev, u8 portnum)
{
struct rio_mport *mport = rdev->net->hport;
u16 destid = rdev->rswitch->destid;
u8 hopcount = rdev->rswitch->hopcount;
u32 intstat, err_status;
int sendcount, checkcount;
u8 route_port;
u32 regval;
rio_mport_read_config_32(mport, destid, hopcount,
rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(portnum),
&err_status);
if ((err_status & RIO_PORT_N_ERR_STS_PORT_OK) &&
(err_status & (RIO_PORT_N_ERR_STS_PW_OUT_ES |
RIO_PORT_N_ERR_STS_PW_INP_ES))) {
/* Remove any queued packets by locking/unlocking port */
rio_mport_read_config_32(mport, destid, hopcount,
rdev->phys_efptr + RIO_PORT_N_CTL_CSR(portnum),
&regval);
if (!(regval & RIO_PORT_N_CTL_LOCKOUT)) {
rio_mport_write_config_32(mport, destid, hopcount,
rdev->phys_efptr + RIO_PORT_N_CTL_CSR(portnum),
regval | RIO_PORT_N_CTL_LOCKOUT);
udelay(50);
rio_mport_write_config_32(mport, destid, hopcount,
rdev->phys_efptr + RIO_PORT_N_CTL_CSR(portnum),
regval);
}
/* Read from link maintenance response register to clear
* valid bit
*/
rio_mport_read_config_32(mport, destid, hopcount,
rdev->phys_efptr + RIO_PORT_N_MNT_RSP_CSR(portnum),
&regval);
/* Send a Packet-Not-Accepted/Link-Request-Input-Status control
* symbol to recover from IES/OES
*/
sendcount = 3;
while (sendcount) {
rio_mport_write_config_32(mport, destid, hopcount,
TSI578_SP_CS_TX(portnum), 0x40fc8000);
checkcount = 3;
while (checkcount--) {
udelay(50);
rio_mport_read_config_32(
mport, destid, hopcount,
rdev->phys_efptr +
RIO_PORT_N_MNT_RSP_CSR(portnum),
&regval);
if (regval & RIO_PORT_N_MNT_RSP_RVAL)
goto exit_es;
}
sendcount--;
}
}
exit_es:
/* Clear implementation specific error status bits */
rio_mport_read_config_32(mport, destid, hopcount,
TSI578_SP_INT_STATUS(portnum), &intstat);
pr_debug("TSI578[%x:%x] SP%d_INT_STATUS=0x%08x\n",
destid, hopcount, portnum, intstat);
if (intstat & 0x10000) {
rio_mport_read_config_32(mport, destid, hopcount,
TSI578_SP_LUT_PEINF(portnum), &regval);
regval = (mport->sys_size) ? (regval >> 16) : (regval >> 24);
route_port = rdev->rswitch->route_table[regval];
pr_debug("RIO: TSI578[%s] P%d LUT Parity Error (destID=%d)\n",
rio_name(rdev), portnum, regval);
tsi57x_route_add_entry(mport, destid, hopcount,
RIO_GLOBAL_TABLE, regval, route_port);
}
rio_mport_write_config_32(mport, destid, hopcount,
TSI578_SP_INT_STATUS(portnum),
intstat & 0x000700bd);
return 0;
}
DECLARE_RIO_EM_OPS(RIO_VID_TUNDRA, RIO_DID_TSI572, tsi57x_em_init, tsi57x_em_handler);
DECLARE_RIO_EM_OPS(RIO_VID_TUNDRA, RIO_DID_TSI574, tsi57x_em_init, tsi57x_em_handler);
DECLARE_RIO_EM_OPS(RIO_VID_TUNDRA, RIO_DID_TSI577, tsi57x_em_init, tsi57x_em_handler);
DECLARE_RIO_EM_OPS(RIO_VID_TUNDRA, RIO_DID_TSI578, tsi57x_em_init, tsi57x_em_handler);
......@@ -247,10 +247,13 @@
} \
\
/* RapidIO route ops */ \
.rio_route : AT(ADDR(.rio_route) - LOAD_OFFSET) { \
.rio_ops : AT(ADDR(.rio_ops) - LOAD_OFFSET) { \
VMLINUX_SYMBOL(__start_rio_route_ops) = .; \
*(.rio_route_ops) \
VMLINUX_SYMBOL(__end_rio_route_ops) = .; \
VMLINUX_SYMBOL(__start_rio_em_ops) = .; \
*(.rio_em_ops) \
VMLINUX_SYMBOL(__end_rio_em_ops) = .; \
} \
\
TRACEDATA \
......
......@@ -64,10 +64,13 @@
#define RIO_INB_MBOX_RESOURCE 1
#define RIO_OUTB_MBOX_RESOURCE 2
#define RIO_PW_MSG_SIZE 64
extern struct bus_type rio_bus_type;
extern struct list_head rio_devices; /* list of all devices */
struct rio_mport;
union rio_pw_msg;
/**
* struct rio_dev - RIO device info
......@@ -107,11 +110,15 @@ struct rio_dev {
u32 swpinfo; /* Only used for switches */
u32 src_ops;
u32 dst_ops;
u32 comp_tag;
u32 phys_efptr;
u32 em_efptr;
u64 dma_mask;
struct rio_switch *rswitch; /* RIO switch info */
struct rio_driver *driver; /* RIO driver claiming this device */
struct device dev; /* LDM device structure */
struct resource riores[RIO_MAX_DEV_RESOURCES];
int (*pwcback) (struct rio_dev *rdev, union rio_pw_msg *msg, int step);
u16 destid;
};
......@@ -211,9 +218,12 @@ struct rio_net {
* @hopcount: Hopcount to this switch
* @destid: Associated destid in the path
* @route_table: Copy of switch routing table
* @port_ok: Status of each port (one bit per port) - OK=1 or UNINIT=0
* @add_entry: Callback for switch-specific route add function
* @get_entry: Callback for switch-specific route get function
* @clr_table: Callback for switch-specific clear route table function
* @em_init: Callback for switch-specific error management initialization function
* @em_handle: Callback for switch-specific error management handler function
*/
struct rio_switch {
struct list_head node;
......@@ -221,12 +231,15 @@ struct rio_switch {
u16 hopcount;
u16 destid;
u8 *route_table;
u32 port_ok;
int (*add_entry) (struct rio_mport * mport, u16 destid, u8 hopcount,
u16 table, u16 route_destid, u8 route_port);
int (*get_entry) (struct rio_mport * mport, u16 destid, u8 hopcount,
u16 table, u16 route_destid, u8 * route_port);
int (*clr_table) (struct rio_mport *mport, u16 destid, u8 hopcount,
u16 table);
int (*em_init) (struct rio_dev *dev);
int (*em_handle) (struct rio_dev *dev, u8 swport);
};
/* Low-level architecture-dependent routines */
......@@ -238,6 +251,7 @@ struct rio_switch {
* @cread: Callback to perform network read of config space.
* @cwrite: Callback to perform network write of config space.
* @dsend: Callback to send a doorbell message.
* @pwenable: Callback to enable/disable port-write message handling.
*/
struct rio_ops {
int (*lcread) (struct rio_mport *mport, int index, u32 offset, int len,
......@@ -249,6 +263,7 @@ struct rio_ops {
int (*cwrite) (struct rio_mport *mport, int index, u16 destid,
u8 hopcount, u32 offset, int len, u32 data);
int (*dsend) (struct rio_mport *mport, int index, u16 destid, u16 data);
int (*pwenable) (struct rio_mport *mport, int enable);
};
#define RIO_RESOURCE_MEM 0x00000100
......@@ -325,6 +340,33 @@ struct rio_route_ops {
u16 table);
};
/**
* struct rio_em_ops - Per-switch error management operations
* @vid: RIO vendor ID
* @did: RIO device ID
* @init_hook: Switch specific error management initialization (may be NULL)
* @handler_hook: Switch specific error management handler (may be NULL)
*
* Defines the operations that are necessary to initialize and handle
* error management events for a particular RIO switch device.
*/
struct rio_em_ops {
u16 vid, did;
int (*init_hook) (struct rio_dev *dev);
int (*handler_hook) (struct rio_dev *dev, u8 swport);
};
union rio_pw_msg {
struct {
u32 comptag; /* Component Tag CSR */
u32 errdetect; /* Port N Error Detect CSR */
u32 is_port; /* Implementation specific + PortID */
u32 ltlerrdet; /* LTL Error Detect CSR */
u32 padding[12];
} em;
u32 raw[RIO_PW_MSG_SIZE/sizeof(u32)];
};
/* Architecture and hardware-specific functions */
extern int rio_init_mports(void);
extern void rio_register_mport(struct rio_mport *);
......
......@@ -413,6 +413,12 @@ void rio_release_regions(struct rio_dev *);
int rio_request_region(struct rio_dev *, int, char *);
void rio_release_region(struct rio_dev *, int);
/* Port-Write management */
extern int rio_request_inb_pwrite(struct rio_dev *,
int (*)(struct rio_dev *, union rio_pw_msg*, int));
extern int rio_release_inb_pwrite(struct rio_dev *);