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