linux/drivers/rapidio/switches/tsi57x.c
<<
>>
Prefs
   1/*
   2 * RapidIO Tsi57x switch family support
   3 *
   4 * Copyright 2009-2010 Integrated Device Technology, Inc.
   5 * Alexandre Bounine <alexandre.bounine@idt.com>
   6 *  - Added EM support
   7 *  - Modified switch operations initialization.
   8 *
   9 * Copyright 2005 MontaVista Software, Inc.
  10 * Matt Porter <mporter@kernel.crashing.org>
  11 *
  12 * This program is free software; you can redistribute  it and/or modify it
  13 * under  the terms of  the GNU General  Public License as published by the
  14 * Free Software Foundation;  either version 2 of the  License, or (at your
  15 * option) any later version.
  16 */
  17
  18#include <linux/rio.h>
  19#include <linux/rio_drv.h>
  20#include <linux/rio_ids.h>
  21#include <linux/delay.h>
  22#include <linux/module.h>
  23#include "../rio.h"
  24
  25/* Global (broadcast) route registers */
  26#define SPBC_ROUTE_CFG_DESTID   0x10070
  27#define SPBC_ROUTE_CFG_PORT     0x10074
  28
  29/* Per port route registers */
  30#define SPP_ROUTE_CFG_DESTID(n) (0x11070 + 0x100*n)
  31#define SPP_ROUTE_CFG_PORT(n)   (0x11074 + 0x100*n)
  32
  33#define TSI578_SP_MODE(n)       (0x11004 + n*0x100)
  34#define TSI578_SP_MODE_GLBL     0x10004
  35#define  TSI578_SP_MODE_PW_DIS  0x08000000
  36#define  TSI578_SP_MODE_LUT_512 0x01000000
  37
  38#define TSI578_SP_CTL_INDEP(n)  (0x13004 + n*0x100)
  39#define TSI578_SP_LUT_PEINF(n)  (0x13010 + n*0x100)
  40#define TSI578_SP_CS_TX(n)      (0x13014 + n*0x100)
  41#define TSI578_SP_INT_STATUS(n) (0x13018 + n*0x100)
  42
  43#define TSI578_GLBL_ROUTE_BASE  0x10078
  44
  45static int
  46tsi57x_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount,
  47                       u16 table, u16 route_destid, u8 route_port)
  48{
  49        if (table == RIO_GLOBAL_TABLE) {
  50                rio_mport_write_config_32(mport, destid, hopcount,
  51                                          SPBC_ROUTE_CFG_DESTID, route_destid);
  52                rio_mport_write_config_32(mport, destid, hopcount,
  53                                          SPBC_ROUTE_CFG_PORT, route_port);
  54        } else {
  55                rio_mport_write_config_32(mport, destid, hopcount,
  56                                SPP_ROUTE_CFG_DESTID(table), route_destid);
  57                rio_mport_write_config_32(mport, destid, hopcount,
  58                                SPP_ROUTE_CFG_PORT(table), route_port);
  59        }
  60
  61        udelay(10);
  62
  63        return 0;
  64}
  65
  66static int
  67tsi57x_route_get_entry(struct rio_mport *mport, u16 destid, u8 hopcount,
  68                       u16 table, u16 route_destid, u8 *route_port)
  69{
  70        int ret = 0;
  71        u32 result;
  72
  73        if (table == RIO_GLOBAL_TABLE) {
  74                /* Use local RT of the ingress port to avoid possible
  75                   race condition */
  76                rio_mport_read_config_32(mport, destid, hopcount,
  77                        RIO_SWP_INFO_CAR, &result);
  78                table = (result & RIO_SWP_INFO_PORT_NUM_MASK);
  79        }
  80
  81        rio_mport_write_config_32(mport, destid, hopcount,
  82                                SPP_ROUTE_CFG_DESTID(table), route_destid);
  83        rio_mport_read_config_32(mport, destid, hopcount,
  84                                SPP_ROUTE_CFG_PORT(table), &result);
  85
  86        *route_port = (u8)result;
  87        if (*route_port > 15)
  88                ret = -1;
  89
  90        return ret;
  91}
  92
  93static int
  94tsi57x_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount,
  95                       u16 table)
  96{
  97        u32 route_idx;
  98        u32 lut_size;
  99
 100        lut_size = (mport->sys_size) ? 0x1ff : 0xff;
 101
 102        if (table == RIO_GLOBAL_TABLE) {
 103                rio_mport_write_config_32(mport, destid, hopcount,
 104                                          SPBC_ROUTE_CFG_DESTID, 0x80000000);
 105                for (route_idx = 0; route_idx <= lut_size; route_idx++)
 106                        rio_mport_write_config_32(mport, destid, hopcount,
 107                                                  SPBC_ROUTE_CFG_PORT,
 108                                                  RIO_INVALID_ROUTE);
 109        } else {
 110                rio_mport_write_config_32(mport, destid, hopcount,
 111                                SPP_ROUTE_CFG_DESTID(table), 0x80000000);
 112                for (route_idx = 0; route_idx <= lut_size; route_idx++)
 113                        rio_mport_write_config_32(mport, destid, hopcount,
 114                                SPP_ROUTE_CFG_PORT(table) , RIO_INVALID_ROUTE);
 115        }
 116
 117        return 0;
 118}
 119
 120static int
 121tsi57x_set_domain(struct rio_mport *mport, u16 destid, u8 hopcount,
 122                       u8 sw_domain)
 123{
 124        u32 regval;
 125
 126        /*
 127         * Switch domain configuration operates only at global level
 128         */
 129
 130        /* Turn off flat (LUT_512) mode */
 131        rio_mport_read_config_32(mport, destid, hopcount,
 132                                 TSI578_SP_MODE_GLBL, &regval);
 133        rio_mport_write_config_32(mport, destid, hopcount, TSI578_SP_MODE_GLBL,
 134                                  regval & ~TSI578_SP_MODE_LUT_512);
 135        /* Set switch domain base */
 136        rio_mport_write_config_32(mport, destid, hopcount,
 137                                  TSI578_GLBL_ROUTE_BASE,
 138                                  (u32)(sw_domain << 24));
 139        return 0;
 140}
 141
 142static int
 143tsi57x_get_domain(struct rio_mport *mport, u16 destid, u8 hopcount,
 144                       u8 *sw_domain)
 145{
 146        u32 regval;
 147
 148        /*
 149         * Switch domain configuration operates only at global level
 150         */
 151        rio_mport_read_config_32(mport, destid, hopcount,
 152                                TSI578_GLBL_ROUTE_BASE, &regval);
 153
 154        *sw_domain = (u8)(regval >> 24);
 155
 156        return 0;
 157}
 158
 159static int
 160tsi57x_em_init(struct rio_dev *rdev)
 161{
 162        u32 regval;
 163        int portnum;
 164
 165        pr_debug("TSI578 %s [%d:%d]\n", __func__, rdev->destid, rdev->hopcount);
 166
 167        for (portnum = 0;
 168             portnum < RIO_GET_TOTAL_PORTS(rdev->swpinfo); portnum++) {
 169                /* Make sure that Port-Writes are enabled (for all ports) */
 170                rio_read_config_32(rdev,
 171                                TSI578_SP_MODE(portnum), &regval);
 172                rio_write_config_32(rdev,
 173                                TSI578_SP_MODE(portnum),
 174                                regval & ~TSI578_SP_MODE_PW_DIS);
 175
 176                /* Clear all pending interrupts */
 177                rio_read_config_32(rdev,
 178                                RIO_DEV_PORT_N_ERR_STS_CSR(rdev, portnum),
 179                                &regval);
 180                rio_write_config_32(rdev,
 181                                RIO_DEV_PORT_N_ERR_STS_CSR(rdev, portnum),
 182                                regval & 0x07120214);
 183
 184                rio_read_config_32(rdev,
 185                                TSI578_SP_INT_STATUS(portnum), &regval);
 186                rio_write_config_32(rdev,
 187                                TSI578_SP_INT_STATUS(portnum),
 188                                regval & 0x000700bd);
 189
 190                /* Enable all interrupts to allow ports to send a port-write */
 191                rio_read_config_32(rdev,
 192                                TSI578_SP_CTL_INDEP(portnum), &regval);
 193                rio_write_config_32(rdev,
 194                                TSI578_SP_CTL_INDEP(portnum),
 195                                regval | 0x000b0000);
 196
 197                /* Skip next (odd) port if the current port is in x4 mode */
 198                rio_read_config_32(rdev,
 199                                RIO_DEV_PORT_N_CTL_CSR(rdev, portnum),
 200                                &regval);
 201                if ((regval & RIO_PORT_N_CTL_PWIDTH) == RIO_PORT_N_CTL_PWIDTH_4)
 202                        portnum++;
 203        }
 204
 205        /* set TVAL = ~50us */
 206        rio_write_config_32(rdev,
 207                rdev->phys_efptr + RIO_PORT_LINKTO_CTL_CSR, 0x9a << 8);
 208
 209        return 0;
 210}
 211
 212static int
 213tsi57x_em_handler(struct rio_dev *rdev, u8 portnum)
 214{
 215        struct rio_mport *mport = rdev->net->hport;
 216        u32 intstat, err_status;
 217        int sendcount, checkcount;
 218        u8 route_port;
 219        u32 regval;
 220
 221        rio_read_config_32(rdev,
 222                        RIO_DEV_PORT_N_ERR_STS_CSR(rdev, portnum),
 223                        &err_status);
 224
 225        if ((err_status & RIO_PORT_N_ERR_STS_PORT_OK) &&
 226            (err_status & (RIO_PORT_N_ERR_STS_OUT_ES |
 227                          RIO_PORT_N_ERR_STS_INP_ES))) {
 228                /* Remove any queued packets by locking/unlocking port */
 229                rio_read_config_32(rdev,
 230                        RIO_DEV_PORT_N_CTL_CSR(rdev, portnum),
 231                        &regval);
 232                if (!(regval & RIO_PORT_N_CTL_LOCKOUT)) {
 233                        rio_write_config_32(rdev,
 234                                RIO_DEV_PORT_N_CTL_CSR(rdev, portnum),
 235                                regval | RIO_PORT_N_CTL_LOCKOUT);
 236                        udelay(50);
 237                        rio_write_config_32(rdev,
 238                                RIO_DEV_PORT_N_CTL_CSR(rdev, portnum),
 239                                regval);
 240                }
 241
 242                /* Read from link maintenance response register to clear
 243                 * valid bit
 244                 */
 245                rio_read_config_32(rdev,
 246                        RIO_DEV_PORT_N_MNT_RSP_CSR(rdev, portnum),
 247                        &regval);
 248
 249                /* Send a Packet-Not-Accepted/Link-Request-Input-Status control
 250                 * symbol to recover from IES/OES
 251                 */
 252                sendcount = 3;
 253                while (sendcount) {
 254                        rio_write_config_32(rdev,
 255                                          TSI578_SP_CS_TX(portnum), 0x40fc8000);
 256                        checkcount = 3;
 257                        while (checkcount--) {
 258                                udelay(50);
 259                                rio_read_config_32(rdev,
 260                                        RIO_DEV_PORT_N_MNT_RSP_CSR(rdev,
 261                                                                   portnum),
 262                                        &regval);
 263                                if (regval & RIO_PORT_N_MNT_RSP_RVAL)
 264                                        goto exit_es;
 265                        }
 266
 267                        sendcount--;
 268                }
 269        }
 270
 271exit_es:
 272        /* Clear implementation specific error status bits */
 273        rio_read_config_32(rdev, TSI578_SP_INT_STATUS(portnum), &intstat);
 274        pr_debug("TSI578[%x:%x] SP%d_INT_STATUS=0x%08x\n",
 275                 rdev->destid, rdev->hopcount, portnum, intstat);
 276
 277        if (intstat & 0x10000) {
 278                rio_read_config_32(rdev,
 279                                TSI578_SP_LUT_PEINF(portnum), &regval);
 280                regval = (mport->sys_size) ? (regval >> 16) : (regval >> 24);
 281                route_port = rdev->rswitch->route_table[regval];
 282                pr_debug("RIO: TSI578[%s] P%d LUT Parity Error (destID=%d)\n",
 283                        rio_name(rdev), portnum, regval);
 284                tsi57x_route_add_entry(mport, rdev->destid, rdev->hopcount,
 285                                RIO_GLOBAL_TABLE, regval, route_port);
 286        }
 287
 288        rio_write_config_32(rdev, TSI578_SP_INT_STATUS(portnum),
 289                            intstat & 0x000700bd);
 290
 291        return 0;
 292}
 293
 294static struct rio_switch_ops tsi57x_switch_ops = {
 295        .owner = THIS_MODULE,
 296        .add_entry = tsi57x_route_add_entry,
 297        .get_entry = tsi57x_route_get_entry,
 298        .clr_table = tsi57x_route_clr_table,
 299        .set_domain = tsi57x_set_domain,
 300        .get_domain = tsi57x_get_domain,
 301        .em_init = tsi57x_em_init,
 302        .em_handle = tsi57x_em_handler,
 303};
 304
 305static int tsi57x_probe(struct rio_dev *rdev, const struct rio_device_id *id)
 306{
 307        pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev));
 308
 309        spin_lock(&rdev->rswitch->lock);
 310
 311        if (rdev->rswitch->ops) {
 312                spin_unlock(&rdev->rswitch->lock);
 313                return -EINVAL;
 314        }
 315        rdev->rswitch->ops = &tsi57x_switch_ops;
 316
 317        if (rdev->do_enum) {
 318                /* Ensure that default routing is disabled on startup */
 319                rio_write_config_32(rdev, RIO_STD_RTE_DEFAULT_PORT,
 320                                    RIO_INVALID_ROUTE);
 321        }
 322
 323        spin_unlock(&rdev->rswitch->lock);
 324        return 0;
 325}
 326
 327static void tsi57x_remove(struct rio_dev *rdev)
 328{
 329        pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev));
 330        spin_lock(&rdev->rswitch->lock);
 331        if (rdev->rswitch->ops != &tsi57x_switch_ops) {
 332                spin_unlock(&rdev->rswitch->lock);
 333                return;
 334        }
 335        rdev->rswitch->ops = NULL;
 336        spin_unlock(&rdev->rswitch->lock);
 337}
 338
 339static struct rio_device_id tsi57x_id_table[] = {
 340        {RIO_DEVICE(RIO_DID_TSI572, RIO_VID_TUNDRA)},
 341        {RIO_DEVICE(RIO_DID_TSI574, RIO_VID_TUNDRA)},
 342        {RIO_DEVICE(RIO_DID_TSI577, RIO_VID_TUNDRA)},
 343        {RIO_DEVICE(RIO_DID_TSI578, RIO_VID_TUNDRA)},
 344        { 0, }  /* terminate list */
 345};
 346
 347static struct rio_driver tsi57x_driver = {
 348        .name = "tsi57x",
 349        .id_table = tsi57x_id_table,
 350        .probe = tsi57x_probe,
 351        .remove = tsi57x_remove,
 352};
 353
 354static int __init tsi57x_init(void)
 355{
 356        return rio_register_driver(&tsi57x_driver);
 357}
 358
 359static void __exit tsi57x_exit(void)
 360{
 361        rio_unregister_driver(&tsi57x_driver);
 362}
 363
 364device_initcall(tsi57x_init);
 365module_exit(tsi57x_exit);
 366
 367MODULE_DESCRIPTION("IDT Tsi57x Serial RapidIO switch family driver");
 368MODULE_AUTHOR("Integrated Device Technology, Inc.");
 369MODULE_LICENSE("GPL");
 370