linux/drivers/net/ethernet/ti/cpsw_sl.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Texas Instruments Ethernet Switch media-access-controller (MAC) submodule/
   4 * Ethernet MAC Sliver (CPGMAC_SL)
   5 *
   6 * Copyright (C) 2019 Texas Instruments
   7 *
   8 */
   9
  10#include <linux/delay.h>
  11#include <linux/io.h>
  12#include <linux/kernel.h>
  13
  14#include "cpsw_sl.h"
  15
  16#define CPSW_SL_REG_NOTUSED U16_MAX
  17
  18static const u16 cpsw_sl_reg_map_cpsw[] = {
  19        [CPSW_SL_IDVER] = 0x00,
  20        [CPSW_SL_MACCONTROL] = 0x04,
  21        [CPSW_SL_MACSTATUS] = 0x08,
  22        [CPSW_SL_SOFT_RESET] = 0x0c,
  23        [CPSW_SL_RX_MAXLEN] = 0x10,
  24        [CPSW_SL_BOFFTEST] = 0x14,
  25        [CPSW_SL_RX_PAUSE] = 0x18,
  26        [CPSW_SL_TX_PAUSE] = 0x1c,
  27        [CPSW_SL_EMCONTROL] = 0x20,
  28        [CPSW_SL_RX_PRI_MAP] = 0x24,
  29        [CPSW_SL_TX_GAP] = 0x28,
  30};
  31
  32static const u16 cpsw_sl_reg_map_66ak2hk[] = {
  33        [CPSW_SL_IDVER] = 0x00,
  34        [CPSW_SL_MACCONTROL] = 0x04,
  35        [CPSW_SL_MACSTATUS] = 0x08,
  36        [CPSW_SL_SOFT_RESET] = 0x0c,
  37        [CPSW_SL_RX_MAXLEN] = 0x10,
  38        [CPSW_SL_BOFFTEST] = CPSW_SL_REG_NOTUSED,
  39        [CPSW_SL_RX_PAUSE] = 0x18,
  40        [CPSW_SL_TX_PAUSE] = 0x1c,
  41        [CPSW_SL_EMCONTROL] = 0x20,
  42        [CPSW_SL_RX_PRI_MAP] = 0x24,
  43        [CPSW_SL_TX_GAP] = CPSW_SL_REG_NOTUSED,
  44};
  45
  46static const u16 cpsw_sl_reg_map_66ak2x_xgbe[] = {
  47        [CPSW_SL_IDVER] = 0x00,
  48        [CPSW_SL_MACCONTROL] = 0x04,
  49        [CPSW_SL_MACSTATUS] = 0x08,
  50        [CPSW_SL_SOFT_RESET] = 0x0c,
  51        [CPSW_SL_RX_MAXLEN] = 0x10,
  52        [CPSW_SL_BOFFTEST] = CPSW_SL_REG_NOTUSED,
  53        [CPSW_SL_RX_PAUSE] = 0x18,
  54        [CPSW_SL_TX_PAUSE] = 0x1c,
  55        [CPSW_SL_EMCONTROL] = 0x20,
  56        [CPSW_SL_RX_PRI_MAP] = CPSW_SL_REG_NOTUSED,
  57        [CPSW_SL_TX_GAP] = 0x28,
  58};
  59
  60static const u16 cpsw_sl_reg_map_66ak2elg_am65[] = {
  61        [CPSW_SL_IDVER] = CPSW_SL_REG_NOTUSED,
  62        [CPSW_SL_MACCONTROL] = 0x00,
  63        [CPSW_SL_MACSTATUS] = 0x04,
  64        [CPSW_SL_SOFT_RESET] = 0x08,
  65        [CPSW_SL_RX_MAXLEN] = CPSW_SL_REG_NOTUSED,
  66        [CPSW_SL_BOFFTEST] = 0x0c,
  67        [CPSW_SL_RX_PAUSE] = 0x10,
  68        [CPSW_SL_TX_PAUSE] = 0x40,
  69        [CPSW_SL_EMCONTROL] = 0x70,
  70        [CPSW_SL_RX_PRI_MAP] = CPSW_SL_REG_NOTUSED,
  71        [CPSW_SL_TX_GAP] = 0x74,
  72};
  73
  74#define CPSW_SL_SOFT_RESET_BIT          BIT(0)
  75
  76#define CPSW_SL_STATUS_PN_IDLE          BIT(31)
  77#define CPSW_SL_AM65_STATUS_PN_E_IDLE   BIT(30)
  78#define CPSW_SL_AM65_STATUS_PN_P_IDLE   BIT(29)
  79#define CPSW_SL_AM65_STATUS_PN_TX_IDLE  BIT(28)
  80
  81#define CPSW_SL_STATUS_IDLE_MASK_BASE (CPSW_SL_STATUS_PN_IDLE)
  82
  83#define CPSW_SL_STATUS_IDLE_MASK_K3 \
  84        (CPSW_SL_STATUS_IDLE_MASK_BASE | CPSW_SL_AM65_STATUS_PN_E_IDLE | \
  85         CPSW_SL_AM65_STATUS_PN_P_IDLE | CPSW_SL_AM65_STATUS_PN_TX_IDLE)
  86
  87#define CPSW_SL_CTL_FUNC_BASE \
  88        (CPSW_SL_CTL_FULLDUPLEX |\
  89        CPSW_SL_CTL_LOOPBACK |\
  90        CPSW_SL_CTL_RX_FLOW_EN |\
  91        CPSW_SL_CTL_TX_FLOW_EN |\
  92        CPSW_SL_CTL_GMII_EN |\
  93        CPSW_SL_CTL_TX_PACE |\
  94        CPSW_SL_CTL_GIG |\
  95        CPSW_SL_CTL_CMD_IDLE |\
  96        CPSW_SL_CTL_IFCTL_A |\
  97        CPSW_SL_CTL_IFCTL_B |\
  98        CPSW_SL_CTL_GIG_FORCE |\
  99        CPSW_SL_CTL_EXT_EN |\
 100        CPSW_SL_CTL_RX_CEF_EN |\
 101        CPSW_SL_CTL_RX_CSF_EN |\
 102        CPSW_SL_CTL_RX_CMF_EN)
 103
 104struct cpsw_sl {
 105        struct device *dev;
 106        void __iomem *sl_base;
 107        const u16 *regs;
 108        u32 control_features;
 109        u32 idle_mask;
 110};
 111
 112struct cpsw_sl_dev_id {
 113        const char *device_id;
 114        const u16 *regs;
 115        const u32 control_features;
 116        const u32 regs_offset;
 117        const u32 idle_mask;
 118};
 119
 120static const struct cpsw_sl_dev_id cpsw_sl_id_match[] = {
 121        {
 122                .device_id = "cpsw",
 123                .regs = cpsw_sl_reg_map_cpsw,
 124                .control_features = CPSW_SL_CTL_FUNC_BASE |
 125                                    CPSW_SL_CTL_MTEST |
 126                                    CPSW_SL_CTL_TX_SHORT_GAP_EN |
 127                                    CPSW_SL_CTL_TX_SG_LIM_EN,
 128                .idle_mask = CPSW_SL_STATUS_IDLE_MASK_BASE,
 129        },
 130        {
 131                .device_id = "66ak2hk",
 132                .regs = cpsw_sl_reg_map_66ak2hk,
 133                .control_features = CPSW_SL_CTL_FUNC_BASE |
 134                                    CPSW_SL_CTL_TX_SHORT_GAP_EN,
 135                .idle_mask = CPSW_SL_STATUS_IDLE_MASK_BASE,
 136        },
 137        {
 138                .device_id = "66ak2x_xgbe",
 139                .regs = cpsw_sl_reg_map_66ak2x_xgbe,
 140                .control_features = CPSW_SL_CTL_FUNC_BASE |
 141                                    CPSW_SL_CTL_XGIG |
 142                                    CPSW_SL_CTL_TX_SHORT_GAP_EN |
 143                                    CPSW_SL_CTL_CRC_TYPE |
 144                                    CPSW_SL_CTL_XGMII_EN,
 145                .idle_mask = CPSW_SL_STATUS_IDLE_MASK_BASE,
 146        },
 147        {
 148                .device_id = "66ak2el",
 149                .regs = cpsw_sl_reg_map_66ak2elg_am65,
 150                .regs_offset = 0x330,
 151                .control_features = CPSW_SL_CTL_FUNC_BASE |
 152                                    CPSW_SL_CTL_MTEST |
 153                                    CPSW_SL_CTL_TX_SHORT_GAP_EN |
 154                                    CPSW_SL_CTL_CRC_TYPE |
 155                                    CPSW_SL_CTL_EXT_EN_RX_FLO |
 156                                    CPSW_SL_CTL_EXT_EN_TX_FLO |
 157                                    CPSW_SL_CTL_TX_SG_LIM_EN,
 158                .idle_mask = CPSW_SL_STATUS_IDLE_MASK_BASE,
 159        },
 160        {
 161                .device_id = "66ak2g",
 162                .regs = cpsw_sl_reg_map_66ak2elg_am65,
 163                .regs_offset = 0x330,
 164                .control_features = CPSW_SL_CTL_FUNC_BASE |
 165                                    CPSW_SL_CTL_MTEST |
 166                                    CPSW_SL_CTL_CRC_TYPE |
 167                                    CPSW_SL_CTL_EXT_EN_RX_FLO |
 168                                    CPSW_SL_CTL_EXT_EN_TX_FLO,
 169        },
 170        {
 171                .device_id = "am65",
 172                .regs = cpsw_sl_reg_map_66ak2elg_am65,
 173                .regs_offset = 0x330,
 174                .control_features = CPSW_SL_CTL_FUNC_BASE |
 175                                    CPSW_SL_CTL_MTEST |
 176                                    CPSW_SL_CTL_XGIG |
 177                                    CPSW_SL_CTL_TX_SHORT_GAP_EN |
 178                                    CPSW_SL_CTL_CRC_TYPE |
 179                                    CPSW_SL_CTL_XGMII_EN |
 180                                    CPSW_SL_CTL_EXT_EN_RX_FLO |
 181                                    CPSW_SL_CTL_EXT_EN_TX_FLO |
 182                                    CPSW_SL_CTL_TX_SG_LIM_EN |
 183                                    CPSW_SL_CTL_EXT_EN_XGIG,
 184                .idle_mask = CPSW_SL_STATUS_IDLE_MASK_K3,
 185        },
 186        { },
 187};
 188
 189u32 cpsw_sl_reg_read(struct cpsw_sl *sl, enum cpsw_sl_regs reg)
 190{
 191        int val;
 192
 193        if (sl->regs[reg] == CPSW_SL_REG_NOTUSED) {
 194                dev_err(sl->dev, "cpsw_sl: not sup r reg: %04X\n",
 195                        sl->regs[reg]);
 196                return 0;
 197        }
 198
 199        val = readl(sl->sl_base + sl->regs[reg]);
 200        dev_dbg(sl->dev, "cpsw_sl: reg: %04X r 0x%08X\n", sl->regs[reg], val);
 201        return val;
 202}
 203
 204void cpsw_sl_reg_write(struct cpsw_sl *sl, enum cpsw_sl_regs reg, u32 val)
 205{
 206        if (sl->regs[reg] == CPSW_SL_REG_NOTUSED) {
 207                dev_err(sl->dev, "cpsw_sl: not sup w reg: %04X\n",
 208                        sl->regs[reg]);
 209                return;
 210        }
 211
 212        dev_dbg(sl->dev, "cpsw_sl: reg: %04X w 0x%08X\n", sl->regs[reg], val);
 213        writel(val, sl->sl_base + sl->regs[reg]);
 214}
 215
 216static const struct cpsw_sl_dev_id *cpsw_sl_match_id(
 217                const struct cpsw_sl_dev_id *id,
 218                const char *device_id)
 219{
 220        if (!id || !device_id)
 221                return NULL;
 222
 223        while (id->device_id) {
 224                if (strcmp(device_id, id->device_id) == 0)
 225                        return id;
 226                id++;
 227        }
 228        return NULL;
 229}
 230
 231struct cpsw_sl *cpsw_sl_get(const char *device_id, struct device *dev,
 232                            void __iomem *sl_base)
 233{
 234        const struct cpsw_sl_dev_id *sl_dev_id;
 235        struct cpsw_sl *sl;
 236
 237        sl = devm_kzalloc(dev, sizeof(struct cpsw_sl), GFP_KERNEL);
 238        if (!sl)
 239                return ERR_PTR(-ENOMEM);
 240        sl->dev = dev;
 241        sl->sl_base = sl_base;
 242
 243        sl_dev_id = cpsw_sl_match_id(cpsw_sl_id_match, device_id);
 244        if (!sl_dev_id) {
 245                dev_err(sl->dev, "cpsw_sl: dev_id %s not found.\n", device_id);
 246                return ERR_PTR(-EINVAL);
 247        }
 248        sl->regs = sl_dev_id->regs;
 249        sl->control_features = sl_dev_id->control_features;
 250        sl->idle_mask = sl_dev_id->idle_mask;
 251        sl->sl_base += sl_dev_id->regs_offset;
 252
 253        return sl;
 254}
 255
 256void cpsw_sl_reset(struct cpsw_sl *sl, unsigned long tmo)
 257{
 258        unsigned long timeout = jiffies + msecs_to_jiffies(tmo);
 259
 260        /* Set the soft reset bit */
 261        cpsw_sl_reg_write(sl, CPSW_SL_SOFT_RESET, CPSW_SL_SOFT_RESET_BIT);
 262
 263        /* Wait for the bit to clear */
 264        do {
 265                usleep_range(100, 200);
 266        } while ((cpsw_sl_reg_read(sl, CPSW_SL_SOFT_RESET) &
 267                  CPSW_SL_SOFT_RESET_BIT) &&
 268                  time_after(timeout, jiffies));
 269
 270        if (cpsw_sl_reg_read(sl, CPSW_SL_SOFT_RESET) & CPSW_SL_SOFT_RESET_BIT)
 271                dev_err(sl->dev, "cpsw_sl failed to soft-reset.\n");
 272}
 273
 274u32 cpsw_sl_ctl_set(struct cpsw_sl *sl, u32 ctl_funcs)
 275{
 276        u32 val;
 277
 278        if (ctl_funcs & ~sl->control_features) {
 279                dev_err(sl->dev, "cpsw_sl: unsupported func 0x%08X\n",
 280                        ctl_funcs & (~sl->control_features));
 281                return -EINVAL;
 282        }
 283
 284        val = cpsw_sl_reg_read(sl, CPSW_SL_MACCONTROL);
 285        val |= ctl_funcs;
 286        cpsw_sl_reg_write(sl, CPSW_SL_MACCONTROL, val);
 287
 288        return 0;
 289}
 290
 291u32 cpsw_sl_ctl_clr(struct cpsw_sl *sl, u32 ctl_funcs)
 292{
 293        u32 val;
 294
 295        if (ctl_funcs & ~sl->control_features) {
 296                dev_err(sl->dev, "cpsw_sl: unsupported func 0x%08X\n",
 297                        ctl_funcs & (~sl->control_features));
 298                return -EINVAL;
 299        }
 300
 301        val = cpsw_sl_reg_read(sl, CPSW_SL_MACCONTROL);
 302        val &= ~ctl_funcs;
 303        cpsw_sl_reg_write(sl, CPSW_SL_MACCONTROL, val);
 304
 305        return 0;
 306}
 307
 308void cpsw_sl_ctl_reset(struct cpsw_sl *sl)
 309{
 310        cpsw_sl_reg_write(sl, CPSW_SL_MACCONTROL, 0);
 311}
 312
 313int cpsw_sl_wait_for_idle(struct cpsw_sl *sl, unsigned long tmo)
 314{
 315        unsigned long timeout = jiffies + msecs_to_jiffies(tmo);
 316
 317        do {
 318                usleep_range(100, 200);
 319        } while (!(cpsw_sl_reg_read(sl, CPSW_SL_MACSTATUS) &
 320                  sl->idle_mask) && time_after(timeout, jiffies));
 321
 322        if (!(cpsw_sl_reg_read(sl, CPSW_SL_MACSTATUS) & sl->idle_mask)) {
 323                dev_err(sl->dev, "cpsw_sl failed to soft-reset.\n");
 324                return -ETIMEDOUT;
 325        }
 326
 327        return 0;
 328}
 329