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                                rdev->phys_efptr +
 179                                        RIO_PORT_N_ERR_STS_CSR(portnum),
 180                                &regval);
 181                rio_write_config_32(rdev,
 182                                rdev->phys_efptr +
 183                                        RIO_PORT_N_ERR_STS_CSR(portnum),
 184                                regval & 0x07120214);
 185
 186                rio_read_config_32(rdev,
 187                                TSI578_SP_INT_STATUS(portnum), &regval);
 188                rio_write_config_32(rdev,
 189                                TSI578_SP_INT_STATUS(portnum),
 190                                regval & 0x000700bd);
 191
 192                /* Enable all interrupts to allow ports to send a port-write */
 193                rio_read_config_32(rdev,
 194                                TSI578_SP_CTL_INDEP(portnum), &regval);
 195                rio_write_config_32(rdev,
 196                                TSI578_SP_CTL_INDEP(portnum),
 197                                regval | 0x000b0000);
 198
 199                /* Skip next (odd) port if the current port is in x4 mode */
 200                rio_read_config_32(rdev,
 201                                rdev->phys_efptr + RIO_PORT_N_CTL_CSR(portnum),
 202                                &regval);
 203                if ((regval & RIO_PORT_N_CTL_PWIDTH) == RIO_PORT_N_CTL_PWIDTH_4)
 204                        portnum++;
 205        }
 206
 207        /* set TVAL = ~50us */
 208        rio_write_config_32(rdev,
 209                rdev->phys_efptr + RIO_PORT_LINKTO_CTL_CSR, 0x9a << 8);
 210
 211        return 0;
 212}
 213
 214static int
 215tsi57x_em_handler(struct rio_dev *rdev, u8 portnum)
 216{
 217        struct rio_mport *mport = rdev->net->hport;
 218        u32 intstat, err_status;
 219        int sendcount, checkcount;
 220        u8 route_port;
 221        u32 regval;
 222
 223        rio_read_config_32(rdev,
 224                        rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(portnum),
 225                        &err_status);
 226
 227        if ((err_status & RIO_PORT_N_ERR_STS_PORT_OK) &&
 228            (err_status & (RIO_PORT_N_ERR_STS_PW_OUT_ES |
 229                          RIO_PORT_N_ERR_STS_PW_INP_ES))) {
 230                /* Remove any queued packets by locking/unlocking port */
 231                rio_read_config_32(rdev,
 232                        rdev->phys_efptr + RIO_PORT_N_CTL_CSR(portnum),
 233                        &regval);
 234                if (!(regval & RIO_PORT_N_CTL_LOCKOUT)) {
 235                        rio_write_config_32(rdev,
 236                                rdev->phys_efptr + RIO_PORT_N_CTL_CSR(portnum),
 237                                regval | RIO_PORT_N_CTL_LOCKOUT);
 238                        udelay(50);
 239                        rio_write_config_32(rdev,
 240                                rdev->phys_efptr + RIO_PORT_N_CTL_CSR(portnum),
 241                                regval);
 242                }
 243
 244                /* Read from link maintenance response register to clear
 245                 * valid bit
 246                 */
 247                rio_read_config_32(rdev,
 248                        rdev->phys_efptr + RIO_PORT_N_MNT_RSP_CSR(portnum),
 249                        &regval);
 250
 251                /* Send a Packet-Not-Accepted/Link-Request-Input-Status control
 252                 * symbol to recover from IES/OES
 253                 */
 254                sendcount = 3;
 255                while (sendcount) {
 256                        rio_write_config_32(rdev,
 257                                          TSI578_SP_CS_TX(portnum), 0x40fc8000);
 258                        checkcount = 3;
 259                        while (checkcount--) {
 260                                udelay(50);
 261                                rio_read_config_32(rdev,
 262                                        rdev->phys_efptr +
 263                                                RIO_PORT_N_MNT_RSP_CSR(portnum),
 264                                        &regval);
 265                                if (regval & RIO_PORT_N_MNT_RSP_RVAL)
 266                                        goto exit_es;
 267                        }
 268
 269                        sendcount--;
 270                }
 271        }
 272
 273exit_es:
 274        /* Clear implementation specific error status bits */
 275        rio_read_config_32(rdev, TSI578_SP_INT_STATUS(portnum), &intstat);
 276        pr_debug("TSI578[%x:%x] SP%d_INT_STATUS=0x%08x\n",
 277                 rdev->destid, rdev->hopcount, portnum, intstat);
 278
 279        if (intstat & 0x10000) {
 280                rio_read_config_32(rdev,
 281                                TSI578_SP_LUT_PEINF(portnum), &regval);
 282                regval = (mport->sys_size) ? (regval >> 16) : (regval >> 24);
 283                route_port = rdev->rswitch->route_table[regval];
 284                pr_debug("RIO: TSI578[%s] P%d LUT Parity Error (destID=%d)\n",
 285                        rio_name(rdev), portnum, regval);
 286                tsi57x_route_add_entry(mport, rdev->destid, rdev->hopcount,
 287                                RIO_GLOBAL_TABLE, regval, route_port);
 288        }
 289
 290        rio_write_config_32(rdev, TSI578_SP_INT_STATUS(portnum),
 291                            intstat & 0x000700bd);
 292
 293        return 0;
 294}
 295
 296static struct rio_switch_ops tsi57x_switch_ops = {
 297        .owner = THIS_MODULE,
 298        .add_entry = tsi57x_route_add_entry,
 299        .get_entry = tsi57x_route_get_entry,
 300        .clr_table = tsi57x_route_clr_table,
 301        .set_domain = tsi57x_set_domain,
 302        .get_domain = tsi57x_get_domain,
 303        .em_init = tsi57x_em_init,
 304        .em_handle = tsi57x_em_handler,
 305};
 306
 307static int tsi57x_probe(struct rio_dev *rdev, const struct rio_device_id *id)
 308{
 309        pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev));
 310
 311        spin_lock(&rdev->rswitch->lock);
 312
 313        if (rdev->rswitch->ops) {
 314                spin_unlock(&rdev->rswitch->lock);
 315                return -EINVAL;
 316        }
 317        rdev->rswitch->ops = &tsi57x_switch_ops;
 318
 319        if (rdev->do_enum) {
 320                /* Ensure that default routing is disabled on startup */
 321                rio_write_config_32(rdev, RIO_STD_RTE_DEFAULT_PORT,
 322                                    RIO_INVALID_ROUTE);
 323        }
 324
 325        spin_unlock(&rdev->rswitch->lock);
 326        return 0;
 327}
 328
 329static void tsi57x_remove(struct rio_dev *rdev)
 330{
 331        pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev));
 332        spin_lock(&rdev->rswitch->lock);
 333        if (rdev->rswitch->ops != &tsi57x_switch_ops) {
 334                spin_unlock(&rdev->rswitch->lock);
 335                return;
 336        }
 337        rdev->rswitch->ops = NULL;
 338        spin_unlock(&rdev->rswitch->lock);
 339}
 340
 341static struct rio_device_id tsi57x_id_table[] = {
 342        {RIO_DEVICE(RIO_DID_TSI572, RIO_VID_TUNDRA)},
 343        {RIO_DEVICE(RIO_DID_TSI574, RIO_VID_TUNDRA)},
 344        {RIO_DEVICE(RIO_DID_TSI577, RIO_VID_TUNDRA)},
 345        {RIO_DEVICE(RIO_DID_TSI578, RIO_VID_TUNDRA)},
 346        { 0, }  /* terminate list */
 347};
 348
 349static struct rio_driver tsi57x_driver = {
 350        .name = "tsi57x",
 351        .id_table = tsi57x_id_table,
 352        .probe = tsi57x_probe,
 353        .remove = tsi57x_remove,
 354};
 355
 356static int __init tsi57x_init(void)
 357{
 358        return rio_register_driver(&tsi57x_driver);
 359}
 360
 361static void __exit tsi57x_exit(void)
 362{
 363        rio_unregister_driver(&tsi57x_driver);
 364}
 365
 366device_initcall(tsi57x_init);
 367module_exit(tsi57x_exit);
 368
 369MODULE_DESCRIPTION("IDT Tsi57x Serial RapidIO switch family driver");
 370MODULE_AUTHOR("Integrated Device Technology, Inc.");
 371MODULE_LICENSE("GPL");
 372