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