Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.15 518 lines 12 kB view raw
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");