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