brintos

brintos / linux-shallow public Read only

0
0
Text · 11.5 KiB · 26b00e6 Raw
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 = &reg, .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 = &reg;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, &reg, 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, &reg, 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, &regmap_encx24j600, ctx, &regcfg);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