linux/drivers/net/ethernet/microchip/encx24j600-regmap.c
<<
>>
Prefs
   1/**
   2 * Register map access API - ENCX24J600 support
   3 *
   4 * Copyright 2015 Gridpoint
   5 *
   6 * Author: Jon Ringle <jringle@gridpoint.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2 as
  10 * published by the Free Software Foundation.
  11 */
  12
  13#include <linux/delay.h>
  14#include <linux/errno.h>
  15#include <linux/init.h>
  16#include <linux/module.h>
  17#include <linux/netdevice.h>
  18#include <linux/regmap.h>
  19#include <linux/spi/spi.h>
  20
  21#include "encx24j600_hw.h"
  22
  23static inline bool is_bits_set(int value, int mask)
  24{
  25        return (value & mask) == mask;
  26}
  27
  28static int encx24j600_switch_bank(struct encx24j600_context *ctx,
  29                                  int bank)
  30{
  31        int ret = 0;
  32        int bank_opcode = BANK_SELECT(bank);
  33
  34        ret = spi_write(ctx->spi, &bank_opcode, 1);
  35        if (ret == 0)
  36                ctx->bank = bank;
  37
  38        return ret;
  39}
  40
  41static int encx24j600_cmdn(struct encx24j600_context *ctx, u8 opcode,
  42                           const void *buf, size_t len)
  43{
  44        struct spi_message m;
  45        struct spi_transfer t[2] = { { .tx_buf = &opcode, .len = 1, },
  46                                     { .tx_buf = buf, .len = len }, };
  47        spi_message_init(&m);
  48        spi_message_add_tail(&t[0], &m);
  49        spi_message_add_tail(&t[1], &m);
  50
  51        return spi_sync(ctx->spi, &m);
  52}
  53
  54static void regmap_lock_mutex(void *context)
  55{
  56        struct encx24j600_context *ctx = context;
  57
  58        mutex_lock(&ctx->mutex);
  59}
  60
  61static void regmap_unlock_mutex(void *context)
  62{
  63        struct encx24j600_context *ctx = context;
  64
  65        mutex_unlock(&ctx->mutex);
  66}
  67
  68static int regmap_encx24j600_sfr_read(void *context, u8 reg, u8 *val,
  69                                      size_t len)
  70{
  71        struct encx24j600_context *ctx = context;
  72        u8 banked_reg = reg & ADDR_MASK;
  73        u8 bank = ((reg & BANK_MASK) >> BANK_SHIFT);
  74        u8 cmd = RCRU;
  75        int ret = 0;
  76        int i = 0;
  77        u8 tx_buf[2];
  78
  79        if (reg < 0x80) {
  80                cmd = RCRCODE | banked_reg;
  81                if ((banked_reg < 0x16) && (ctx->bank != bank))
  82                        ret = encx24j600_switch_bank(ctx, bank);
  83                if (unlikely(ret))
  84                        return ret;
  85        } else {
  86                /* Translate registers that are more effecient using
  87                 * 3-byte SPI commands
  88                 */
  89                switch (reg) {
  90                case EGPRDPT:
  91                        cmd = RGPRDPT; break;
  92                case EGPWRPT:
  93                        cmd = RGPWRPT; break;
  94                case ERXRDPT:
  95                        cmd = RRXRDPT; break;
  96                case ERXWRPT:
  97                        cmd = RRXWRPT; break;
  98                case EUDARDPT:
  99                        cmd = RUDARDPT; break;
 100                case EUDAWRPT:
 101                        cmd = RUDAWRPT; break;
 102                case EGPDATA:
 103                case ERXDATA:
 104                case EUDADATA:
 105                default:
 106                        return -EINVAL;
 107                }
 108        }
 109
 110        tx_buf[i++] = cmd;
 111        if (cmd == RCRU)
 112                tx_buf[i++] = reg;
 113
 114        ret = spi_write_then_read(ctx->spi, tx_buf, i, val, len);
 115
 116        return ret;
 117}
 118
 119static int regmap_encx24j600_sfr_update(struct encx24j600_context *ctx,
 120                                        u8 reg, u8 *val, size_t len,
 121                                        u8 unbanked_cmd, u8 banked_code)
 122{
 123        u8 banked_reg = reg & ADDR_MASK;
 124        u8 bank = ((reg & BANK_MASK) >> BANK_SHIFT);
 125        u8 cmd = unbanked_cmd;
 126        struct spi_message m;
 127        struct spi_transfer t[3] = { { .tx_buf = &cmd, .len = sizeof(cmd), },
 128                                     { .tx_buf = &reg, .len = sizeof(reg), },
 129                                     { .tx_buf = val, .len = len }, };
 130
 131        if (reg < 0x80) {
 132                int ret = 0;
 133
 134                cmd = banked_code | banked_reg;
 135                if ((banked_reg < 0x16) && (ctx->bank != bank))
 136                        ret = encx24j600_switch_bank(ctx, bank);
 137                if (unlikely(ret))
 138                        return ret;
 139        } else {
 140                /* Translate registers that are more effecient using
 141                 * 3-byte SPI commands
 142                 */
 143                switch (reg) {
 144                case EGPRDPT:
 145                        cmd = WGPRDPT; break;
 146                case EGPWRPT:
 147                        cmd = WGPWRPT; break;
 148                case ERXRDPT:
 149                        cmd = WRXRDPT; break;
 150                case ERXWRPT:
 151                        cmd = WRXWRPT; break;
 152                case EUDARDPT:
 153                        cmd = WUDARDPT; break;
 154                case EUDAWRPT:
 155                        cmd = WUDAWRPT; break;
 156                case EGPDATA:
 157                case ERXDATA:
 158                case EUDADATA:
 159                default:
 160                        return -EINVAL;
 161                }
 162        }
 163
 164        spi_message_init(&m);
 165        spi_message_add_tail(&t[0], &m);
 166
 167        if (cmd == unbanked_cmd) {
 168                t[1].tx_buf = &reg;
 169                spi_message_add_tail(&t[1], &m);
 170        }
 171
 172        spi_message_add_tail(&t[2], &m);
 173        return spi_sync(ctx->spi, &m);
 174}
 175
 176static int regmap_encx24j600_sfr_write(void *context, u8 reg, u8 *val,
 177                                       size_t len)
 178{
 179        struct encx24j600_context *ctx = context;
 180
 181        return regmap_encx24j600_sfr_update(ctx, reg, val, len, WCRU, WCRCODE);
 182}
 183
 184static int regmap_encx24j600_sfr_set_bits(struct encx24j600_context *ctx,
 185                                          u8 reg, u8 val)
 186{
 187        return regmap_encx24j600_sfr_update(ctx, reg, &val, 1, BFSU, BFSCODE);
 188}
 189
 190static int regmap_encx24j600_sfr_clr_bits(struct encx24j600_context *ctx,
 191                                          u8 reg, u8 val)
 192{
 193        return regmap_encx24j600_sfr_update(ctx, reg, &val, 1, BFCU, BFCCODE);
 194}
 195
 196static int regmap_encx24j600_reg_update_bits(void *context, unsigned int reg,
 197                                             unsigned int mask,
 198                                             unsigned int val)
 199{
 200        struct encx24j600_context *ctx = context;
 201
 202        int ret = 0;
 203        unsigned int set_mask = mask & val;
 204        unsigned int clr_mask = mask & ~val;
 205
 206        if ((reg >= 0x40 && reg < 0x6c) || reg >= 0x80)
 207                return -EINVAL;
 208
 209        if (set_mask & 0xff)
 210                ret = regmap_encx24j600_sfr_set_bits(ctx, reg, set_mask);
 211
 212        set_mask = (set_mask & 0xff00) >> 8;
 213
 214        if ((set_mask & 0xff) && (ret == 0))
 215                ret = regmap_encx24j600_sfr_set_bits(ctx, reg + 1, set_mask);
 216
 217        if ((clr_mask & 0xff) && (ret == 0))
 218                ret = regmap_encx24j600_sfr_clr_bits(ctx, reg, clr_mask);
 219
 220        clr_mask = (clr_mask & 0xff00) >> 8;
 221
 222        if ((clr_mask & 0xff) && (ret == 0))
 223                ret = regmap_encx24j600_sfr_clr_bits(ctx, reg + 1, clr_mask);
 224
 225        return ret;
 226}
 227
 228int regmap_encx24j600_spi_write(void *context, u8 reg, const u8 *data,
 229                                size_t count)
 230{
 231        struct encx24j600_context *ctx = context;
 232
 233        if (reg < 0xc0)
 234                return encx24j600_cmdn(ctx, reg, data, count);
 235
 236        /* SPI 1-byte command. Ignore data */
 237        return spi_write(ctx->spi, &reg, 1);
 238}
 239EXPORT_SYMBOL_GPL(regmap_encx24j600_spi_write);
 240
 241int regmap_encx24j600_spi_read(void *context, u8 reg, u8 *data, size_t count)
 242{
 243        struct encx24j600_context *ctx = context;
 244
 245        if (reg == RBSEL && count > 1)
 246                count = 1;
 247
 248        return spi_write_then_read(ctx->spi, &reg, sizeof(reg), data, count);
 249}
 250EXPORT_SYMBOL_GPL(regmap_encx24j600_spi_read);
 251
 252static int regmap_encx24j600_write(void *context, const void *data,
 253                                   size_t len)
 254{
 255        u8 *dout = (u8 *)data;
 256        u8 reg = dout[0];
 257        ++dout;
 258        --len;
 259
 260        if (reg > 0xa0)
 261                return regmap_encx24j600_spi_write(context, reg, dout, len);
 262
 263        if (len > 2)
 264                return -EINVAL;
 265
 266        return regmap_encx24j600_sfr_write(context, reg, dout, len);
 267}
 268
 269static int regmap_encx24j600_read(void *context,
 270                                  const void *reg_buf, size_t reg_size,
 271                                  void *val, size_t val_size)
 272{
 273        u8 reg = *(const u8 *)reg_buf;
 274
 275        if (reg_size != 1) {
 276                pr_err("%s: reg=%02x reg_size=%zu\n", __func__, reg, reg_size);
 277                return -EINVAL;
 278        }
 279
 280        if (reg > 0xa0)
 281                return regmap_encx24j600_spi_read(context, reg, val, val_size);
 282
 283        if (val_size > 2) {
 284                pr_err("%s: reg=%02x val_size=%zu\n", __func__, reg, val_size);
 285                return -EINVAL;
 286        }
 287
 288        return regmap_encx24j600_sfr_read(context, reg, val, val_size);
 289}
 290
 291static bool encx24j600_regmap_readable(struct device *dev, unsigned int reg)
 292{
 293        if ((reg < 0x36) ||
 294            ((reg >= 0x40) && (reg < 0x4c)) ||
 295            ((reg >= 0x52) && (reg < 0x56)) ||
 296            ((reg >= 0x60) && (reg < 0x66)) ||
 297            ((reg >= 0x68) && (reg < 0x80)) ||
 298            ((reg >= 0x86) && (reg < 0x92)) ||
 299            (reg == 0xc8))
 300                return true;
 301        else
 302                return false;
 303}
 304
 305static bool encx24j600_regmap_writeable(struct device *dev, unsigned int reg)
 306{
 307        if ((reg < 0x12) ||
 308            ((reg >= 0x14) && (reg < 0x1a)) ||
 309            ((reg >= 0x1c) && (reg < 0x36)) ||
 310            ((reg >= 0x40) && (reg < 0x4c)) ||
 311            ((reg >= 0x52) && (reg < 0x56)) ||
 312            ((reg >= 0x60) && (reg < 0x68)) ||
 313            ((reg >= 0x6c) && (reg < 0x80)) ||
 314            ((reg >= 0x86) && (reg < 0x92)) ||
 315            ((reg >= 0xc0) && (reg < 0xc8)) ||
 316            ((reg >= 0xca) && (reg < 0xf0)))
 317                return true;
 318        else
 319                return false;
 320}
 321
 322static bool encx24j600_regmap_volatile(struct device *dev, unsigned int reg)
 323{
 324        switch (reg) {
 325        case ERXHEAD:
 326        case EDMACS:
 327        case ETXSTAT:
 328        case ETXWIRE:
 329        case ECON1:     /* Can be modified via single byte cmds */
 330        case ECON2:     /* Can be modified via single byte cmds */
 331        case ESTAT:
 332        case EIR:       /* Can be modified via single byte cmds */
 333        case MIRD:
 334        case MISTAT:
 335                return true;
 336        default:
 337                break;
 338        }
 339
 340        return false;
 341}
 342
 343static bool encx24j600_regmap_precious(struct device *dev, unsigned int reg)
 344{
 345        /* single byte cmds are precious */
 346        if (((reg >= 0xc0) && (reg < 0xc8)) ||
 347            ((reg >= 0xca) && (reg < 0xf0)))
 348                return true;
 349        else
 350                return false;
 351}
 352
 353static int regmap_encx24j600_phy_reg_read(void *context, unsigned int reg,
 354                                          unsigned int *val)
 355{
 356        struct encx24j600_context *ctx = context;
 357        int ret;
 358        unsigned int mistat;
 359
 360        reg = MIREGADR_VAL | (reg & PHREG_MASK);
 361        ret = regmap_write(ctx->regmap, MIREGADR, reg);
 362        if (unlikely(ret))
 363                goto err_out;
 364
 365        ret = regmap_write(ctx->regmap, MICMD, MIIRD);
 366        if (unlikely(ret))
 367                goto err_out;
 368
 369        usleep_range(26, 100);
 370        while ((ret = regmap_read(ctx->regmap, MISTAT, &mistat) != 0) &&
 371               (mistat & BUSY))
 372                cpu_relax();
 373
 374        if (unlikely(ret))
 375                goto err_out;
 376
 377        ret = regmap_write(ctx->regmap, MICMD, 0);
 378        if (unlikely(ret))
 379                goto err_out;
 380
 381        ret = regmap_read(ctx->regmap, MIRD, val);
 382
 383err_out:
 384        if (ret)
 385                pr_err("%s: error %d reading reg %02x\n", __func__, ret,
 386                       reg & PHREG_MASK);
 387
 388        return ret;
 389}
 390
 391static int regmap_encx24j600_phy_reg_write(void *context, unsigned int reg,
 392                                           unsigned int val)
 393{
 394        struct encx24j600_context *ctx = context;
 395        int ret;
 396        unsigned int mistat;
 397
 398        reg = MIREGADR_VAL | (reg & PHREG_MASK);
 399        ret = regmap_write(ctx->regmap, MIREGADR, reg);
 400        if (unlikely(ret))
 401                goto err_out;
 402
 403        ret = regmap_write(ctx->regmap, MIWR, val);
 404        if (unlikely(ret))
 405                goto err_out;
 406
 407        usleep_range(26, 100);
 408        while ((ret = regmap_read(ctx->regmap, MISTAT, &mistat) != 0) &&
 409               (mistat & BUSY))
 410                cpu_relax();
 411
 412err_out:
 413        if (ret)
 414                pr_err("%s: error %d writing reg %02x=%04x\n", __func__, ret,
 415                       reg & PHREG_MASK, val);
 416
 417        return ret;
 418}
 419
 420static bool encx24j600_phymap_readable(struct device *dev, unsigned int reg)
 421{
 422        switch (reg) {
 423        case PHCON1:
 424        case PHSTAT1:
 425        case PHANA:
 426        case PHANLPA:
 427        case PHANE:
 428        case PHCON2:
 429        case PHSTAT2:
 430        case PHSTAT3:
 431                return true;
 432        default:
 433                return false;
 434        }
 435}
 436
 437static bool encx24j600_phymap_writeable(struct device *dev, unsigned int reg)
 438{
 439        switch (reg) {
 440        case PHCON1:
 441        case PHCON2:
 442        case PHANA:
 443                return true;
 444        case PHSTAT1:
 445        case PHSTAT2:
 446        case PHSTAT3:
 447        case PHANLPA:
 448        case PHANE:
 449        default:
 450                return false;
 451        }
 452}
 453
 454static bool encx24j600_phymap_volatile(struct device *dev, unsigned int reg)
 455{
 456        switch (reg) {
 457        case PHSTAT1:
 458        case PHSTAT2:
 459        case PHSTAT3:
 460        case PHANLPA:
 461        case PHANE:
 462        case PHCON2:
 463                return true;
 464        default:
 465                return false;
 466        }
 467}
 468
 469static struct regmap_config regcfg = {
 470        .name = "reg",
 471        .reg_bits = 8,
 472        .val_bits = 16,
 473        .max_register = 0xee,
 474        .reg_stride = 2,
 475        .cache_type = REGCACHE_RBTREE,
 476        .val_format_endian = REGMAP_ENDIAN_LITTLE,
 477        .readable_reg = encx24j600_regmap_readable,
 478        .writeable_reg = encx24j600_regmap_writeable,
 479        .volatile_reg = encx24j600_regmap_volatile,
 480        .precious_reg = encx24j600_regmap_precious,
 481        .lock = regmap_lock_mutex,
 482        .unlock = regmap_unlock_mutex,
 483};
 484
 485static struct regmap_bus regmap_encx24j600 = {
 486        .write = regmap_encx24j600_write,
 487        .read = regmap_encx24j600_read,
 488        .reg_update_bits = regmap_encx24j600_reg_update_bits,
 489};
 490
 491static struct regmap_config phycfg = {
 492        .name = "phy",
 493        .reg_bits = 8,
 494        .val_bits = 16,
 495        .max_register = 0x1f,
 496        .cache_type = REGCACHE_RBTREE,
 497        .val_format_endian = REGMAP_ENDIAN_LITTLE,
 498        .readable_reg = encx24j600_phymap_readable,
 499        .writeable_reg = encx24j600_phymap_writeable,
 500        .volatile_reg = encx24j600_phymap_volatile,
 501};
 502
 503static struct regmap_bus phymap_encx24j600 = {
 504        .reg_write = regmap_encx24j600_phy_reg_write,
 505        .reg_read = regmap_encx24j600_phy_reg_read,
 506};
 507
 508void devm_regmap_init_encx24j600(struct device *dev,
 509                                 struct encx24j600_context *ctx)
 510{
 511        mutex_init(&ctx->mutex);
 512        regcfg.lock_arg = ctx;
 513        ctx->regmap = devm_regmap_init(dev, &regmap_encx24j600, ctx, &regcfg);
 514        ctx->phymap = devm_regmap_init(dev, &phymap_encx24j600, ctx, &phycfg);
 515}
 516EXPORT_SYMBOL_GPL(devm_regmap_init_encx24j600);
 517
 518MODULE_LICENSE("GPL");
 519