Linux Audio

Check our new training course

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