diff options
Diffstat (limited to 'drivers/rapidio/rio-scan.c')
-rw-r--r-- | drivers/rapidio/rio-scan.c | 166 |
1 files changed, 130 insertions, 36 deletions
diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c index cbf0d5f4fba..74633cc6b2e 100644 --- a/drivers/rapidio/rio-scan.c +++ b/drivers/rapidio/rio-scan.c @@ -4,6 +4,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 @@ -31,15 +35,16 @@ LIST_HEAD(rio_devices); static LIST_HEAD(rio_switches); -#define RIO_ENUM_CMPL_MAGIC 0xdeadbeef - static void rio_enum_timeout(unsigned long); +static void rio_init_em(struct rio_dev *rdev); + DEFINE_SPINLOCK(rio_global_list_lock); static int next_destid = 0; static int next_switchid = 0; static int next_net = 0; +static int next_comptag; static struct timer_list rio_enum_timer = TIMER_INITIALIZER(rio_enum_timeout, 0, 0); @@ -52,13 +57,6 @@ static int rio_mport_phys_table[] = { -1, }; -static int rio_sport_phys_table[] = { - RIO_EFB_PAR_EP_FREE_ID, - RIO_EFB_SER_EP_FREE_ID, - RIO_EFB_SER_EP_FREC_ID, - -1, -}; - /** * rio_get_device_id - Get the base/extended device id for a device * @port: RIO master port @@ -119,12 +117,26 @@ static int rio_clear_locks(struct rio_mport *port) u32 result; int ret = 0; - /* Write component tag CSR magic complete value */ - rio_local_write_config_32(port, RIO_COMPONENT_TAG_CSR, - RIO_ENUM_CMPL_MAGIC); - list_for_each_entry(rdev, &rio_devices, global_list) - rio_write_config_32(rdev, RIO_COMPONENT_TAG_CSR, - RIO_ENUM_CMPL_MAGIC); + /* Assign component tag to all devices */ + next_comptag = 1; + rio_local_write_config_32(port, RIO_COMPONENT_TAG_CSR, next_comptag++); + + list_for_each_entry(rdev, &rio_devices, global_list) { + /* Mark device as discovered */ + rio_read_config_32(rdev, + rdev->phys_efptr + RIO_PORT_GEN_CTL_CSR, + &result); + rio_write_config_32(rdev, + rdev->phys_efptr + RIO_PORT_GEN_CTL_CSR, + result | RIO_PORT_GEN_DISCOVERED); + + rio_write_config_32(rdev, RIO_COMPONENT_TAG_CSR, next_comptag); + rdev->comp_tag = next_comptag++; + if (next_comptag >= 0x10000) { + pr_err("RIO: Component Tag Counter Overflow\n"); + break; + } + } /* Release host device id locks */ rio_local_write_config_32(port, RIO_HOST_DID_LOCK_CSR, @@ -267,6 +279,30 @@ static void rio_route_set_ops(struct rio_dev *rdev) } /** + * rio_em_set_ops- Sets Error Managment operations for a particular vendor switch + * @rdev: RIO device + * + * Searches the RIO EM ops table for known switch types. If the vid + * and did match a switch table entry, then set the em_init() and + * em_handle() ops to the table entry values. + */ +static void rio_em_set_ops(struct rio_dev *rdev) +{ + struct rio_em_ops *cur = __start_rio_em_ops; + struct rio_em_ops *end = __end_rio_em_ops; + + while (cur < end) { + if ((cur->vid == rdev->vid) && (cur->did == rdev->did)) { + pr_debug("RIO: adding EM ops for %s\n", rio_name(rdev)); + rdev->rswitch->em_init = cur->init_hook; + rdev->rswitch->em_handle = cur->handler_hook; + break; + } + cur++; + } +} + +/** * rio_add_device- Adds a RIO device to the device model * @rdev: RIO device * @@ -336,8 +372,14 @@ static struct rio_dev __devinit *rio_setup_device(struct rio_net *net, rdev->asm_rev = result >> 16; rio_mport_read_config_32(port, destid, hopcount, RIO_PEF_CAR, &rdev->pef); - if (rdev->pef & RIO_PEF_EXT_FEATURES) + if (rdev->pef & RIO_PEF_EXT_FEATURES) { rdev->efptr = result & 0xffff; + rdev->phys_efptr = rio_mport_get_physefb(port, 0, destid, + hopcount); + + rdev->em_efptr = rio_mport_get_feature(port, 0, destid, + hopcount, RIO_EFB_ERR_MGMNT); + } rio_mport_read_config_32(port, destid, hopcount, RIO_SRC_OPS_CAR, &rdev->src_ops); @@ -366,6 +408,7 @@ static struct rio_dev __devinit *rio_setup_device(struct rio_net *net, rswitch->switchid = next_switchid; rswitch->hopcount = hopcount; rswitch->destid = destid; + rswitch->port_ok = 0; rswitch->route_table = kzalloc(sizeof(u8)* RIO_MAX_ROUTE_ENTRIES(port->sys_size), GFP_KERNEL); @@ -379,6 +422,7 @@ static struct rio_dev __devinit *rio_setup_device(struct rio_net *net, dev_set_name(&rdev->dev, "%02x:s:%04x", rdev->net->id, rdev->rswitch->switchid); rio_route_set_ops(rdev); + rio_em_set_ops(rdev); if (do_enum && rdev->rswitch->clr_table) rdev->rswitch->clr_table(port, destid, hopcount, @@ -429,23 +473,29 @@ cleanup: * * Reads the port error status CSR for a particular switch port to * determine if the port has an active link. Returns - * %PORT_N_ERR_STS_PORT_OK if the port is active or %0 if it is + * %RIO_PORT_N_ERR_STS_PORT_OK if the port is active or %0 if it is * inactive. */ static int rio_sport_is_active(struct rio_mport *port, u16 destid, u8 hopcount, int sport) { - u32 result; + u32 result = 0; u32 ext_ftr_ptr; - int *entry = rio_sport_phys_table; - - do { - if ((ext_ftr_ptr = - rio_mport_get_feature(port, 0, destid, hopcount, *entry))) + ext_ftr_ptr = rio_mport_get_efb(port, 0, destid, hopcount, 0); + while (ext_ftr_ptr) { + rio_mport_read_config_32(port, destid, hopcount, + ext_ftr_ptr, &result); + result = RIO_GET_BLOCK_ID(result); + if ((result == RIO_EFB_SER_EP_FREE_ID) || + (result == RIO_EFB_SER_EP_FREE_ID_V13P) || + (result == RIO_EFB_SER_EP_FREC_ID)) break; - } while (*++entry >= 0); + + ext_ftr_ptr = rio_mport_get_efb(port, 0, destid, hopcount, + ext_ftr_ptr); + } if (ext_ftr_ptr) rio_mport_read_config_32(port, destid, hopcount, @@ -453,7 +503,7 @@ rio_sport_is_active(struct rio_mport *port, u16 destid, u8 hopcount, int sport) RIO_PORT_N_ERR_STS_CSR(sport), &result); - return (result & PORT_N_ERR_STS_PORT_OK); + return result & RIO_PORT_N_ERR_STS_PORT_OK; } /** @@ -762,8 +812,10 @@ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port, rio_name(rdev), rdev->vid, rdev->did, num_ports); sw_destid = next_destid; for (port_num = 0; port_num < num_ports; port_num++) { - if (sw_inport == port_num) + if (sw_inport == port_num) { + rdev->rswitch->port_ok |= (1 << port_num); continue; + } cur_destid = next_destid; @@ -773,6 +825,7 @@ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port, pr_debug( "RIO: scanning device on port %d\n", port_num); + rdev->rswitch->port_ok |= (1 << port_num); rio_route_add_entry(port, rdev->rswitch, RIO_GLOBAL_TABLE, RIO_ANY_DESTID(port->sys_size), @@ -797,9 +850,28 @@ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port, port_num; } } + } else { + /* If switch supports Error Management, + * set PORT_LOCKOUT bit for unused port + */ + if (rdev->em_efptr) + rio_set_port_lockout(rdev, port_num, 1); + + rdev->rswitch->port_ok &= ~(1 << port_num); } } + /* Direct Port-write messages to the enumeratiing host */ + if ((rdev->src_ops & RIO_SRC_OPS_PORT_WRITE) && + (rdev->em_efptr)) { + rio_write_config_32(rdev, + rdev->em_efptr + RIO_EM_PW_TGT_DEVID, + (port->host_deviceid << 16) | + (port->sys_size << 15)); + } + + rio_init_em(rdev); + /* Check for empty switch */ if (next_destid == sw_destid) { next_destid++; @@ -819,21 +891,16 @@ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port, * rio_enum_complete- Tests if enumeration of a network is complete * @port: Master port to send transaction * - * Tests the Component Tag CSR for presence of the magic enumeration - * complete flag. Return %1 if enumeration is complete or %0 if + * Tests the Component Tag CSR for non-zero value (enumeration + * complete flag). Return %1 if enumeration is complete or %0 if * enumeration is incomplete. */ static int rio_enum_complete(struct rio_mport *port) { u32 tag_csr; - int ret = 0; rio_local_read_config_32(port, RIO_COMPONENT_TAG_CSR, &tag_csr); - - if (tag_csr == RIO_ENUM_CMPL_MAGIC) - ret = 1; - - return ret; + return (tag_csr & 0xffff) ? 1 : 0; } /** @@ -915,7 +982,7 @@ rio_disc_peer(struct rio_net *net, struct rio_mport *port, u16 destid, * * Reads the port error status CSR for the master port to * determine if the port has an active link. Returns - * %PORT_N_ERR_STS_PORT_OK if the master port is active + * %RIO_PORT_N_ERR_STS_PORT_OK if the master port is active * or %0 if it is inactive. */ static int rio_mport_is_active(struct rio_mport *port) @@ -936,7 +1003,7 @@ static int rio_mport_is_active(struct rio_mport *port) RIO_PORT_N_ERR_STS_CSR(port->index), &result); - return (result & PORT_N_ERR_STS_PORT_OK); + return result & RIO_PORT_N_ERR_STS_PORT_OK; } /** @@ -1008,6 +1075,32 @@ static void rio_update_route_tables(struct rio_mport *port) } /** + * rio_init_em - Initializes RIO Error Management (for switches) + * @port: Master port associated with the RIO network + * + * For each enumerated switch, call device-specific error management + * initialization routine (if supplied by the switch driver). + */ +static void rio_init_em(struct rio_dev *rdev) +{ + if (rio_is_switch(rdev) && (rdev->em_efptr) && + (rdev->rswitch->em_init)) { + rdev->rswitch->em_init(rdev); + } +} + +/** + * rio_pw_enable - Enables/disables port-write handling by a master port + * @port: Master port associated with port-write handling + * @enable: 1=enable, 0=disable + */ +static void rio_pw_enable(struct rio_mport *port, int enable) +{ + if (port->ops->pwenable) + port->ops->pwenable(port, enable); +} + +/** * rio_enum_mport- Start enumeration through a master port * @mport: Master port to send transactions * @@ -1050,6 +1143,7 @@ int __devinit rio_enum_mport(struct rio_mport *mport) } rio_update_route_tables(mport); rio_clear_locks(mport); + rio_pw_enable(mport, 1); } else { printk(KERN_INFO "RIO: master port %d link inactive\n", mport->id); |