summaryrefslogtreecommitdiffstats
path: root/drivers/rapidio/switches
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/rapidio/switches')
-rw-r--r--drivers/rapidio/switches/Kconfig28
-rw-r--r--drivers/rapidio/switches/Makefile9
-rw-r--r--drivers/rapidio/switches/idtcps.c137
-rw-r--r--drivers/rapidio/switches/tsi500.c20
-rw-r--r--drivers/rapidio/switches/tsi568.c146
-rw-r--r--drivers/rapidio/switches/tsi57x.c315
6 files changed, 653 insertions, 2 deletions
diff --git a/drivers/rapidio/switches/Kconfig b/drivers/rapidio/switches/Kconfig
new file mode 100644
index 00000000000..2b4e9b2b663
--- /dev/null
+++ b/drivers/rapidio/switches/Kconfig
@@ -0,0 +1,28 @@
+#
+# RapidIO switches configuration
+#
+config RAPIDIO_TSI57X
+ bool "IDT Tsi57x SRIO switches support"
+ depends on RAPIDIO
+ ---help---
+ Includes support for IDT Tsi57x family of serial RapidIO switches.
+
+config RAPIDIO_CPS_XX
+ bool "IDT CPS-xx SRIO switches support"
+ depends on RAPIDIO
+ ---help---
+ Includes support for IDT CPS-16/12/10/8 serial RapidIO switches.
+
+config RAPIDIO_TSI568
+ bool "Tsi568 SRIO switch support"
+ depends on RAPIDIO
+ default n
+ ---help---
+ Includes support for IDT Tsi568 serial RapidIO switch.
+
+config RAPIDIO_TSI500
+ bool "Tsi500 Parallel RapidIO switch support"
+ depends on RAPIDIO
+ default n
+ ---help---
+ Includes support for IDT Tsi500 parallel RapidIO switch.
diff --git a/drivers/rapidio/switches/Makefile b/drivers/rapidio/switches/Makefile
index b924f830176..fe4adc3e8d5 100644
--- a/drivers/rapidio/switches/Makefile
+++ b/drivers/rapidio/switches/Makefile
@@ -2,4 +2,11 @@
# Makefile for RIO switches
#
-obj-$(CONFIG_RAPIDIO) += tsi500.o
+obj-$(CONFIG_RAPIDIO_TSI57X) += tsi57x.o
+obj-$(CONFIG_RAPIDIO_CPS_XX) += idtcps.o
+obj-$(CONFIG_RAPIDIO_TSI568) += tsi568.o
+obj-$(CONFIG_RAPIDIO_TSI500) += tsi500.o
+
+ifeq ($(CONFIG_RAPIDIO_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
diff --git a/drivers/rapidio/switches/idtcps.c b/drivers/rapidio/switches/idtcps.c
new file mode 100644
index 00000000000..2c790c144f8
--- /dev/null
+++ b/drivers/rapidio/switches/idtcps.c
@@ -0,0 +1,137 @@
+/*
+ * IDT CPS RapidIO switches support
+ *
+ * Copyright 2009-2010 Integrated Device Technology, Inc.
+ * Alexandre Bounine <alexandre.bounine@idt.com>
+ *
+ * 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.
+ */
+
+#include <linux/rio.h>
+#include <linux/rio_drv.h>
+#include <linux/rio_ids.h>
+#include "../rio.h"
+
+#define CPS_DEFAULT_ROUTE 0xde
+#define CPS_NO_ROUTE 0xdf
+
+#define IDTCPS_RIO_DOMAIN 0xf20020
+
+static int
+idtcps_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount,
+ u16 table, u16 route_destid, u8 route_port)
+{
+ u32 result;
+
+ if (table == RIO_GLOBAL_TABLE) {
+ rio_mport_write_config_32(mport, destid, hopcount,
+ RIO_STD_RTE_CONF_DESTID_SEL_CSR, route_destid);
+
+ rio_mport_read_config_32(mport, destid, hopcount,
+ RIO_STD_RTE_CONF_PORT_SEL_CSR, &result);
+
+ result = (0xffffff00 & result) | (u32)route_port;
+ rio_mport_write_config_32(mport, destid, hopcount,
+ RIO_STD_RTE_CONF_PORT_SEL_CSR, result);
+ }
+
+ return 0;
+}
+
+static int
+idtcps_route_get_entry(struct rio_mport *mport, u16 destid, u8 hopcount,
+ u16 table, u16 route_destid, u8 *route_port)
+{
+ u32 result;
+
+ if (table == RIO_GLOBAL_TABLE) {
+ rio_mport_write_config_32(mport, destid, hopcount,
+ RIO_STD_RTE_CONF_DESTID_SEL_CSR, route_destid);
+
+ rio_mport_read_config_32(mport, destid, hopcount,
+ RIO_STD_RTE_CONF_PORT_SEL_CSR, &result);
+
+ if (CPS_DEFAULT_ROUTE == (u8)result ||
+ CPS_NO_ROUTE == (u8)result)
+ *route_port = RIO_INVALID_ROUTE;
+ else
+ *route_port = (u8)result;
+ }
+
+ return 0;
+}
+
+static int
+idtcps_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount,
+ u16 table)
+{
+ u32 i;
+
+ if (table == RIO_GLOBAL_TABLE) {
+ for (i = 0x80000000; i <= 0x800000ff;) {
+ rio_mport_write_config_32(mport, destid, hopcount,
+ RIO_STD_RTE_CONF_DESTID_SEL_CSR, i);
+ rio_mport_write_config_32(mport, destid, hopcount,
+ RIO_STD_RTE_CONF_PORT_SEL_CSR,
+ (CPS_DEFAULT_ROUTE << 24) |
+ (CPS_DEFAULT_ROUTE << 16) |
+ (CPS_DEFAULT_ROUTE << 8) | CPS_DEFAULT_ROUTE);
+ i += 4;
+ }
+ }
+
+ return 0;
+}
+
+static int
+idtcps_set_domain(struct rio_mport *mport, u16 destid, u8 hopcount,
+ u8 sw_domain)
+{
+ /*
+ * Switch domain configuration operates only at global level
+ */
+ rio_mport_write_config_32(mport, destid, hopcount,
+ IDTCPS_RIO_DOMAIN, (u32)sw_domain);
+ return 0;
+}
+
+static int
+idtcps_get_domain(struct rio_mport *mport, u16 destid, u8 hopcount,
+ u8 *sw_domain)
+{
+ u32 regval;
+
+ /*
+ * Switch domain configuration operates only at global level
+ */
+ rio_mport_read_config_32(mport, destid, hopcount,
+ IDTCPS_RIO_DOMAIN, &regval);
+
+ *sw_domain = (u8)(regval & 0xff);
+
+ return 0;
+}
+
+static int idtcps_switch_init(struct rio_dev *rdev, int do_enum)
+{
+ pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev));
+ rdev->rswitch->add_entry = idtcps_route_add_entry;
+ rdev->rswitch->get_entry = idtcps_route_get_entry;
+ rdev->rswitch->clr_table = idtcps_route_clr_table;
+ rdev->rswitch->set_domain = idtcps_set_domain;
+ rdev->rswitch->get_domain = idtcps_get_domain;
+ rdev->rswitch->em_init = NULL;
+ rdev->rswitch->em_handle = NULL;
+
+ return 0;
+}
+
+DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS6Q, idtcps_switch_init);
+DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS8, idtcps_switch_init);
+DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS10Q, idtcps_switch_init);
+DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS12, idtcps_switch_init);
+DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS16, idtcps_switch_init);
+DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDT70K200, idtcps_switch_init);
diff --git a/drivers/rapidio/switches/tsi500.c b/drivers/rapidio/switches/tsi500.c
index c77c23bd984..914eddd5aa4 100644
--- a/drivers/rapidio/switches/tsi500.c
+++ b/drivers/rapidio/switches/tsi500.c
@@ -1,6 +1,10 @@
/*
* RapidIO Tsi500 switch support
*
+ * Copyright 2009-2010 Integrated Device Technology, Inc.
+ * Alexandre Bounine <alexandre.bounine@idt.com>
+ * - Modified switch operations initialization.
+ *
* Copyright 2005 MontaVista Software, Inc.
* Matt Porter <mporter@kernel.crashing.org>
*
@@ -57,4 +61,18 @@ tsi500_route_get_entry(struct rio_mport *mport, u16 destid, u8 hopcount, u16 tab
return ret;
}
-DECLARE_RIO_ROUTE_OPS(RIO_VID_TUNDRA, RIO_DID_TSI500, tsi500_route_add_entry, tsi500_route_get_entry);
+static int tsi500_switch_init(struct rio_dev *rdev, int do_enum)
+{
+ pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev));
+ rdev->rswitch->add_entry = tsi500_route_add_entry;
+ rdev->rswitch->get_entry = tsi500_route_get_entry;
+ rdev->rswitch->clr_table = NULL;
+ rdev->rswitch->set_domain = NULL;
+ rdev->rswitch->get_domain = NULL;
+ rdev->rswitch->em_init = NULL;
+ rdev->rswitch->em_handle = NULL;
+
+ return 0;
+}
+
+DECLARE_RIO_SWITCH_INIT(RIO_VID_TUNDRA, RIO_DID_TSI500, tsi500_switch_init);
diff --git a/drivers/rapidio/switches/tsi568.c b/drivers/rapidio/switches/tsi568.c
new file mode 100644
index 00000000000..f7fd7898606
--- /dev/null
+++ b/drivers/rapidio/switches/tsi568.c
@@ -0,0 +1,146 @@
+/*
+ * RapidIO Tsi568 switch support
+ *
+ * Copyright 2009-2010 Integrated Device Technology, Inc.
+ * Alexandre Bounine <alexandre.bounine@idt.com>
+ * - Added EM support
+ * - Modified switch operations initialization.
+ *
+ * Copyright 2005 MontaVista Software, Inc.
+ * Matt Porter <mporter@kernel.crashing.org>
+ *
+ * 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.
+ */
+
+#include <linux/rio.h>
+#include <linux/rio_drv.h>
+#include <linux/rio_ids.h>
+#include <linux/delay.h>
+#include "../rio.h"
+
+/* Global (broadcast) route registers */
+#define SPBC_ROUTE_CFG_DESTID 0x10070
+#define SPBC_ROUTE_CFG_PORT 0x10074
+
+/* Per port route registers */
+#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)
+{
+ if (table == RIO_GLOBAL_TABLE) {
+ rio_mport_write_config_32(mport, destid, hopcount,
+ SPBC_ROUTE_CFG_DESTID, route_destid);
+ rio_mport_write_config_32(mport, destid, hopcount,
+ SPBC_ROUTE_CFG_PORT, route_port);
+ } else {
+ rio_mport_write_config_32(mport, destid, hopcount,
+ SPP_ROUTE_CFG_DESTID(table),
+ route_destid);
+ rio_mport_write_config_32(mport, destid, hopcount,
+ SPP_ROUTE_CFG_PORT(table), route_port);
+ }
+
+ udelay(10);
+
+ return 0;
+}
+
+static int
+tsi568_route_get_entry(struct rio_mport *mport, u16 destid, u8 hopcount,
+ u16 table, u16 route_destid, u8 *route_port)
+{
+ int ret = 0;
+ u32 result;
+
+ if (table == RIO_GLOBAL_TABLE) {
+ rio_mport_write_config_32(mport, destid, hopcount,
+ SPBC_ROUTE_CFG_DESTID, route_destid);
+ rio_mport_read_config_32(mport, destid, hopcount,
+ SPBC_ROUTE_CFG_PORT, &result);
+ } else {
+ rio_mport_write_config_32(mport, destid, hopcount,
+ SPP_ROUTE_CFG_DESTID(table),
+ route_destid);
+ rio_mport_read_config_32(mport, destid, hopcount,
+ SPP_ROUTE_CFG_PORT(table), &result);
+ }
+
+ *route_port = result;
+ if (*route_port > 15)
+ ret = -1;
+
+ return ret;
+}
+
+static int
+tsi568_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount,
+ u16 table)
+{
+ u32 route_idx;
+ u32 lut_size;
+
+ lut_size = (mport->sys_size) ? 0x1ff : 0xff;
+
+ if (table == RIO_GLOBAL_TABLE) {
+ rio_mport_write_config_32(mport, destid, hopcount,
+ SPBC_ROUTE_CFG_DESTID, 0x80000000);
+ for (route_idx = 0; route_idx <= lut_size; route_idx++)
+ rio_mport_write_config_32(mport, destid, hopcount,
+ SPBC_ROUTE_CFG_PORT,
+ RIO_INVALID_ROUTE);
+ } else {
+ rio_mport_write_config_32(mport, destid, hopcount,
+ SPP_ROUTE_CFG_DESTID(table),
+ 0x80000000);
+ for (route_idx = 0; route_idx <= lut_size; route_idx++)
+ rio_mport_write_config_32(mport, destid, hopcount,
+ SPP_ROUTE_CFG_PORT(table),
+ RIO_INVALID_ROUTE);
+ }
+
+ return 0;
+}
+
+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;
+}
+
+static int tsi568_switch_init(struct rio_dev *rdev, int do_enum)
+{
+ pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev));
+ rdev->rswitch->add_entry = tsi568_route_add_entry;
+ rdev->rswitch->get_entry = tsi568_route_get_entry;
+ rdev->rswitch->clr_table = tsi568_route_clr_table;
+ rdev->rswitch->set_domain = NULL;
+ rdev->rswitch->get_domain = NULL;
+ rdev->rswitch->em_init = tsi568_em_init;
+ rdev->rswitch->em_handle = NULL;
+
+ return 0;
+}
+
+DECLARE_RIO_SWITCH_INIT(RIO_VID_TUNDRA, RIO_DID_TSI568, tsi568_switch_init);
diff --git a/drivers/rapidio/switches/tsi57x.c b/drivers/rapidio/switches/tsi57x.c
new file mode 100644
index 00000000000..d34df722d95
--- /dev/null
+++ b/drivers/rapidio/switches/tsi57x.c
@@ -0,0 +1,315 @@
+/*
+ * RapidIO Tsi57x switch family support
+ *
+ * Copyright 2009-2010 Integrated Device Technology, Inc.
+ * Alexandre Bounine <alexandre.bounine@idt.com>
+ * - Added EM support
+ * - Modified switch operations initialization.
+ *
+ * Copyright 2005 MontaVista Software, Inc.
+ * Matt Porter <mporter@kernel.crashing.org>
+ *
+ * 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.
+ */
+
+#include <linux/rio.h>
+#include <linux/rio_drv.h>
+#include <linux/rio_ids.h>
+#include <linux/delay.h>
+#include "../rio.h"
+
+/* Global (broadcast) route registers */
+#define SPBC_ROUTE_CFG_DESTID 0x10070
+#define SPBC_ROUTE_CFG_PORT 0x10074
+
+/* Per port route registers */
+#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_GLBL 0x10004
+#define TSI578_SP_MODE_PW_DIS 0x08000000
+#define TSI578_SP_MODE_LUT_512 0x01000000
+
+#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)
+
+#define TSI578_GLBL_ROUTE_BASE 0x10078
+
+static int
+tsi57x_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount,
+ u16 table, u16 route_destid, u8 route_port)
+{
+ if (table == RIO_GLOBAL_TABLE) {
+ rio_mport_write_config_32(mport, destid, hopcount,
+ SPBC_ROUTE_CFG_DESTID, route_destid);
+ rio_mport_write_config_32(mport, destid, hopcount,
+ SPBC_ROUTE_CFG_PORT, route_port);
+ } else {
+ rio_mport_write_config_32(mport, destid, hopcount,
+ SPP_ROUTE_CFG_DESTID(table), route_destid);
+ rio_mport_write_config_32(mport, destid, hopcount,
+ SPP_ROUTE_CFG_PORT(table), route_port);
+ }
+
+ udelay(10);
+
+ return 0;
+}
+
+static int
+tsi57x_route_get_entry(struct rio_mport *mport, u16 destid, u8 hopcount,
+ u16 table, u16 route_destid, u8 *route_port)
+{
+ int ret = 0;
+ u32 result;
+
+ if (table == RIO_GLOBAL_TABLE) {
+ /* Use local RT of the ingress port to avoid possible
+ race condition */
+ rio_mport_read_config_32(mport, destid, hopcount,
+ RIO_SWP_INFO_CAR, &result);
+ table = (result & RIO_SWP_INFO_PORT_NUM_MASK);
+ }
+
+ rio_mport_write_config_32(mport, destid, hopcount,
+ SPP_ROUTE_CFG_DESTID(table), route_destid);
+ rio_mport_read_config_32(mport, destid, hopcount,
+ SPP_ROUTE_CFG_PORT(table), &result);
+
+ *route_port = (u8)result;
+ if (*route_port > 15)
+ ret = -1;
+
+ return ret;
+}
+
+static int
+tsi57x_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount,
+ u16 table)
+{
+ u32 route_idx;
+ u32 lut_size;
+
+ lut_size = (mport->sys_size) ? 0x1ff : 0xff;
+
+ if (table == RIO_GLOBAL_TABLE) {
+ rio_mport_write_config_32(mport, destid, hopcount,
+ SPBC_ROUTE_CFG_DESTID, 0x80000000);
+ for (route_idx = 0; route_idx <= lut_size; route_idx++)
+ rio_mport_write_config_32(mport, destid, hopcount,
+ SPBC_ROUTE_CFG_PORT,
+ RIO_INVALID_ROUTE);
+ } else {
+ rio_mport_write_config_32(mport, destid, hopcount,
+ SPP_ROUTE_CFG_DESTID(table), 0x80000000);
+ for (route_idx = 0; route_idx <= lut_size; route_idx++)
+ rio_mport_write_config_32(mport, destid, hopcount,
+ SPP_ROUTE_CFG_PORT(table) , RIO_INVALID_ROUTE);
+ }
+
+ return 0;
+}
+
+static int
+tsi57x_set_domain(struct rio_mport *mport, u16 destid, u8 hopcount,
+ u8 sw_domain)
+{
+ u32 regval;
+
+ /*
+ * Switch domain configuration operates only at global level
+ */
+
+ /* Turn off flat (LUT_512) mode */
+ rio_mport_read_config_32(mport, destid, hopcount,
+ TSI578_SP_MODE_GLBL, &regval);
+ rio_mport_write_config_32(mport, destid, hopcount, TSI578_SP_MODE_GLBL,
+ regval & ~TSI578_SP_MODE_LUT_512);
+ /* Set switch domain base */
+ rio_mport_write_config_32(mport, destid, hopcount,
+ TSI578_GLBL_ROUTE_BASE,
+ (u32)(sw_domain << 24));
+ return 0;
+}
+
+static int
+tsi57x_get_domain(struct rio_mport *mport, u16 destid, u8 hopcount,
+ u8 *sw_domain)
+{
+ u32 regval;
+
+ /*
+ * Switch domain configuration operates only at global level
+ */
+ rio_mport_read_config_32(mport, destid, hopcount,
+ TSI578_GLBL_ROUTE_BASE, &regval);
+
+ *sw_domain = (u8)(regval >> 24);
+
+ return 0;
+}
+
+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;
+}
+
+static int tsi57x_switch_init(struct rio_dev *rdev, int do_enum)
+{
+ pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev));
+ rdev->rswitch->add_entry = tsi57x_route_add_entry;
+ rdev->rswitch->get_entry = tsi57x_route_get_entry;
+ rdev->rswitch->clr_table = tsi57x_route_clr_table;
+ rdev->rswitch->set_domain = tsi57x_set_domain;
+ rdev->rswitch->get_domain = tsi57x_get_domain;
+ rdev->rswitch->em_init = tsi57x_em_init;
+ rdev->rswitch->em_handle = tsi57x_em_handler;
+
+ return 0;
+}
+
+DECLARE_RIO_SWITCH_INIT(RIO_VID_TUNDRA, RIO_DID_TSI572, tsi57x_switch_init);
+DECLARE_RIO_SWITCH_INIT(RIO_VID_TUNDRA, RIO_DID_TSI574, tsi57x_switch_init);
+DECLARE_RIO_SWITCH_INIT(RIO_VID_TUNDRA, RIO_DID_TSI577, tsi57x_switch_init);
+DECLARE_RIO_SWITCH_INIT(RIO_VID_TUNDRA, RIO_DID_TSI578, tsi57x_switch_init);