18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Register map access API - ENCX24J600 support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2015 Gridpoint
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Jon Ringle <jringle@gridpoint.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/delay.h>
118c2ecf20Sopenharmony_ci#include <linux/errno.h>
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
158c2ecf20Sopenharmony_ci#include <linux/regmap.h>
168c2ecf20Sopenharmony_ci#include <linux/spi/spi.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "encx24j600_hw.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic int encx24j600_switch_bank(struct encx24j600_context *ctx,
218c2ecf20Sopenharmony_ci				  int bank)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	int ret = 0;
248c2ecf20Sopenharmony_ci	int bank_opcode = BANK_SELECT(bank);
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	ret = spi_write(ctx->spi, &bank_opcode, 1);
278c2ecf20Sopenharmony_ci	if (ret == 0)
288c2ecf20Sopenharmony_ci		ctx->bank = bank;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	return ret;
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic int encx24j600_cmdn(struct encx24j600_context *ctx, u8 opcode,
348c2ecf20Sopenharmony_ci			   const void *buf, size_t len)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	struct spi_message m;
378c2ecf20Sopenharmony_ci	struct spi_transfer t[2] = { { .tx_buf = &opcode, .len = 1, },
388c2ecf20Sopenharmony_ci				     { .tx_buf = buf, .len = len }, };
398c2ecf20Sopenharmony_ci	spi_message_init(&m);
408c2ecf20Sopenharmony_ci	spi_message_add_tail(&t[0], &m);
418c2ecf20Sopenharmony_ci	spi_message_add_tail(&t[1], &m);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	return spi_sync(ctx->spi, &m);
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic void regmap_lock_mutex(void *context)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	struct encx24j600_context *ctx = context;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	mutex_lock(&ctx->mutex);
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic void regmap_unlock_mutex(void *context)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	struct encx24j600_context *ctx = context;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	mutex_unlock(&ctx->mutex);
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic int regmap_encx24j600_sfr_read(void *context, u8 reg, u8 *val,
618c2ecf20Sopenharmony_ci				      size_t len)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct encx24j600_context *ctx = context;
648c2ecf20Sopenharmony_ci	u8 banked_reg = reg & ADDR_MASK;
658c2ecf20Sopenharmony_ci	u8 bank = ((reg & BANK_MASK) >> BANK_SHIFT);
668c2ecf20Sopenharmony_ci	u8 cmd = RCRU;
678c2ecf20Sopenharmony_ci	int ret = 0;
688c2ecf20Sopenharmony_ci	int i = 0;
698c2ecf20Sopenharmony_ci	u8 tx_buf[2];
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	if (reg < 0x80) {
728c2ecf20Sopenharmony_ci		cmd = RCRCODE | banked_reg;
738c2ecf20Sopenharmony_ci		if ((banked_reg < 0x16) && (ctx->bank != bank))
748c2ecf20Sopenharmony_ci			ret = encx24j600_switch_bank(ctx, bank);
758c2ecf20Sopenharmony_ci		if (unlikely(ret))
768c2ecf20Sopenharmony_ci			return ret;
778c2ecf20Sopenharmony_ci	} else {
788c2ecf20Sopenharmony_ci		/* Translate registers that are more effecient using
798c2ecf20Sopenharmony_ci		 * 3-byte SPI commands
808c2ecf20Sopenharmony_ci		 */
818c2ecf20Sopenharmony_ci		switch (reg) {
828c2ecf20Sopenharmony_ci		case EGPRDPT:
838c2ecf20Sopenharmony_ci			cmd = RGPRDPT; break;
848c2ecf20Sopenharmony_ci		case EGPWRPT:
858c2ecf20Sopenharmony_ci			cmd = RGPWRPT; break;
868c2ecf20Sopenharmony_ci		case ERXRDPT:
878c2ecf20Sopenharmony_ci			cmd = RRXRDPT; break;
888c2ecf20Sopenharmony_ci		case ERXWRPT:
898c2ecf20Sopenharmony_ci			cmd = RRXWRPT; break;
908c2ecf20Sopenharmony_ci		case EUDARDPT:
918c2ecf20Sopenharmony_ci			cmd = RUDARDPT; break;
928c2ecf20Sopenharmony_ci		case EUDAWRPT:
938c2ecf20Sopenharmony_ci			cmd = RUDAWRPT; break;
948c2ecf20Sopenharmony_ci		case EGPDATA:
958c2ecf20Sopenharmony_ci		case ERXDATA:
968c2ecf20Sopenharmony_ci		case EUDADATA:
978c2ecf20Sopenharmony_ci		default:
988c2ecf20Sopenharmony_ci			return -EINVAL;
998c2ecf20Sopenharmony_ci		}
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	tx_buf[i++] = cmd;
1038c2ecf20Sopenharmony_ci	if (cmd == RCRU)
1048c2ecf20Sopenharmony_ci		tx_buf[i++] = reg;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	ret = spi_write_then_read(ctx->spi, tx_buf, i, val, len);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	return ret;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic int regmap_encx24j600_sfr_update(struct encx24j600_context *ctx,
1128c2ecf20Sopenharmony_ci					u8 reg, u8 *val, size_t len,
1138c2ecf20Sopenharmony_ci					u8 unbanked_cmd, u8 banked_code)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	u8 banked_reg = reg & ADDR_MASK;
1168c2ecf20Sopenharmony_ci	u8 bank = ((reg & BANK_MASK) >> BANK_SHIFT);
1178c2ecf20Sopenharmony_ci	u8 cmd = unbanked_cmd;
1188c2ecf20Sopenharmony_ci	struct spi_message m;
1198c2ecf20Sopenharmony_ci	struct spi_transfer t[3] = { { .tx_buf = &cmd, .len = sizeof(cmd), },
1208c2ecf20Sopenharmony_ci				     { .tx_buf = &reg, .len = sizeof(reg), },
1218c2ecf20Sopenharmony_ci				     { .tx_buf = val, .len = len }, };
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (reg < 0x80) {
1248c2ecf20Sopenharmony_ci		int ret = 0;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci		cmd = banked_code | banked_reg;
1278c2ecf20Sopenharmony_ci		if ((banked_reg < 0x16) && (ctx->bank != bank))
1288c2ecf20Sopenharmony_ci			ret = encx24j600_switch_bank(ctx, bank);
1298c2ecf20Sopenharmony_ci		if (unlikely(ret))
1308c2ecf20Sopenharmony_ci			return ret;
1318c2ecf20Sopenharmony_ci	} else {
1328c2ecf20Sopenharmony_ci		/* Translate registers that are more effecient using
1338c2ecf20Sopenharmony_ci		 * 3-byte SPI commands
1348c2ecf20Sopenharmony_ci		 */
1358c2ecf20Sopenharmony_ci		switch (reg) {
1368c2ecf20Sopenharmony_ci		case EGPRDPT:
1378c2ecf20Sopenharmony_ci			cmd = WGPRDPT; break;
1388c2ecf20Sopenharmony_ci		case EGPWRPT:
1398c2ecf20Sopenharmony_ci			cmd = WGPWRPT; break;
1408c2ecf20Sopenharmony_ci		case ERXRDPT:
1418c2ecf20Sopenharmony_ci			cmd = WRXRDPT; break;
1428c2ecf20Sopenharmony_ci		case ERXWRPT:
1438c2ecf20Sopenharmony_ci			cmd = WRXWRPT; break;
1448c2ecf20Sopenharmony_ci		case EUDARDPT:
1458c2ecf20Sopenharmony_ci			cmd = WUDARDPT; break;
1468c2ecf20Sopenharmony_ci		case EUDAWRPT:
1478c2ecf20Sopenharmony_ci			cmd = WUDAWRPT; break;
1488c2ecf20Sopenharmony_ci		case EGPDATA:
1498c2ecf20Sopenharmony_ci		case ERXDATA:
1508c2ecf20Sopenharmony_ci		case EUDADATA:
1518c2ecf20Sopenharmony_ci		default:
1528c2ecf20Sopenharmony_ci			return -EINVAL;
1538c2ecf20Sopenharmony_ci		}
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	spi_message_init(&m);
1578c2ecf20Sopenharmony_ci	spi_message_add_tail(&t[0], &m);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (cmd == unbanked_cmd) {
1608c2ecf20Sopenharmony_ci		t[1].tx_buf = &reg;
1618c2ecf20Sopenharmony_ci		spi_message_add_tail(&t[1], &m);
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	spi_message_add_tail(&t[2], &m);
1658c2ecf20Sopenharmony_ci	return spi_sync(ctx->spi, &m);
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cistatic int regmap_encx24j600_sfr_write(void *context, u8 reg, u8 *val,
1698c2ecf20Sopenharmony_ci				       size_t len)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	struct encx24j600_context *ctx = context;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	return regmap_encx24j600_sfr_update(ctx, reg, val, len, WCRU, WCRCODE);
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic int regmap_encx24j600_sfr_set_bits(struct encx24j600_context *ctx,
1778c2ecf20Sopenharmony_ci					  u8 reg, u8 val)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	return regmap_encx24j600_sfr_update(ctx, reg, &val, 1, BFSU, BFSCODE);
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic int regmap_encx24j600_sfr_clr_bits(struct encx24j600_context *ctx,
1838c2ecf20Sopenharmony_ci					  u8 reg, u8 val)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	return regmap_encx24j600_sfr_update(ctx, reg, &val, 1, BFCU, BFCCODE);
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic int regmap_encx24j600_reg_update_bits(void *context, unsigned int reg,
1898c2ecf20Sopenharmony_ci					     unsigned int mask,
1908c2ecf20Sopenharmony_ci					     unsigned int val)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	struct encx24j600_context *ctx = context;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	int ret = 0;
1958c2ecf20Sopenharmony_ci	unsigned int set_mask = mask & val;
1968c2ecf20Sopenharmony_ci	unsigned int clr_mask = mask & ~val;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if ((reg >= 0x40 && reg < 0x6c) || reg >= 0x80)
1998c2ecf20Sopenharmony_ci		return -EINVAL;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	if (set_mask & 0xff)
2028c2ecf20Sopenharmony_ci		ret = regmap_encx24j600_sfr_set_bits(ctx, reg, set_mask);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	set_mask = (set_mask & 0xff00) >> 8;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	if ((set_mask & 0xff) && (ret == 0))
2078c2ecf20Sopenharmony_ci		ret = regmap_encx24j600_sfr_set_bits(ctx, reg + 1, set_mask);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	if ((clr_mask & 0xff) && (ret == 0))
2108c2ecf20Sopenharmony_ci		ret = regmap_encx24j600_sfr_clr_bits(ctx, reg, clr_mask);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	clr_mask = (clr_mask & 0xff00) >> 8;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	if ((clr_mask & 0xff) && (ret == 0))
2158c2ecf20Sopenharmony_ci		ret = regmap_encx24j600_sfr_clr_bits(ctx, reg + 1, clr_mask);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	return ret;
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ciint regmap_encx24j600_spi_write(void *context, u8 reg, const u8 *data,
2218c2ecf20Sopenharmony_ci				size_t count)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	struct encx24j600_context *ctx = context;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	if (reg < 0xc0)
2268c2ecf20Sopenharmony_ci		return encx24j600_cmdn(ctx, reg, data, count);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	/* SPI 1-byte command. Ignore data */
2298c2ecf20Sopenharmony_ci	return spi_write(ctx->spi, &reg, 1);
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(regmap_encx24j600_spi_write);
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ciint regmap_encx24j600_spi_read(void *context, u8 reg, u8 *data, size_t count)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	struct encx24j600_context *ctx = context;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	if (reg == RBSEL && count > 1)
2388c2ecf20Sopenharmony_ci		count = 1;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	return spi_write_then_read(ctx->spi, &reg, sizeof(reg), data, count);
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(regmap_encx24j600_spi_read);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic int regmap_encx24j600_write(void *context, const void *data,
2458c2ecf20Sopenharmony_ci				   size_t len)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	u8 *dout = (u8 *)data;
2488c2ecf20Sopenharmony_ci	u8 reg = dout[0];
2498c2ecf20Sopenharmony_ci	++dout;
2508c2ecf20Sopenharmony_ci	--len;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	if (reg > 0xa0)
2538c2ecf20Sopenharmony_ci		return regmap_encx24j600_spi_write(context, reg, dout, len);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	if (len > 2)
2568c2ecf20Sopenharmony_ci		return -EINVAL;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	return regmap_encx24j600_sfr_write(context, reg, dout, len);
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cistatic int regmap_encx24j600_read(void *context,
2628c2ecf20Sopenharmony_ci				  const void *reg_buf, size_t reg_size,
2638c2ecf20Sopenharmony_ci				  void *val, size_t val_size)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	u8 reg = *(const u8 *)reg_buf;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	if (reg_size != 1) {
2688c2ecf20Sopenharmony_ci		pr_err("%s: reg=%02x reg_size=%zu\n", __func__, reg, reg_size);
2698c2ecf20Sopenharmony_ci		return -EINVAL;
2708c2ecf20Sopenharmony_ci	}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	if (reg > 0xa0)
2738c2ecf20Sopenharmony_ci		return regmap_encx24j600_spi_read(context, reg, val, val_size);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	if (val_size > 2) {
2768c2ecf20Sopenharmony_ci		pr_err("%s: reg=%02x val_size=%zu\n", __func__, reg, val_size);
2778c2ecf20Sopenharmony_ci		return -EINVAL;
2788c2ecf20Sopenharmony_ci	}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	return regmap_encx24j600_sfr_read(context, reg, val, val_size);
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic bool encx24j600_regmap_readable(struct device *dev, unsigned int reg)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	if ((reg < 0x36) ||
2868c2ecf20Sopenharmony_ci	    ((reg >= 0x40) && (reg < 0x4c)) ||
2878c2ecf20Sopenharmony_ci	    ((reg >= 0x52) && (reg < 0x56)) ||
2888c2ecf20Sopenharmony_ci	    ((reg >= 0x60) && (reg < 0x66)) ||
2898c2ecf20Sopenharmony_ci	    ((reg >= 0x68) && (reg < 0x80)) ||
2908c2ecf20Sopenharmony_ci	    ((reg >= 0x86) && (reg < 0x92)) ||
2918c2ecf20Sopenharmony_ci	    (reg == 0xc8))
2928c2ecf20Sopenharmony_ci		return true;
2938c2ecf20Sopenharmony_ci	else
2948c2ecf20Sopenharmony_ci		return false;
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic bool encx24j600_regmap_writeable(struct device *dev, unsigned int reg)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	if ((reg < 0x12) ||
3008c2ecf20Sopenharmony_ci	    ((reg >= 0x14) && (reg < 0x1a)) ||
3018c2ecf20Sopenharmony_ci	    ((reg >= 0x1c) && (reg < 0x36)) ||
3028c2ecf20Sopenharmony_ci	    ((reg >= 0x40) && (reg < 0x4c)) ||
3038c2ecf20Sopenharmony_ci	    ((reg >= 0x52) && (reg < 0x56)) ||
3048c2ecf20Sopenharmony_ci	    ((reg >= 0x60) && (reg < 0x68)) ||
3058c2ecf20Sopenharmony_ci	    ((reg >= 0x6c) && (reg < 0x80)) ||
3068c2ecf20Sopenharmony_ci	    ((reg >= 0x86) && (reg < 0x92)) ||
3078c2ecf20Sopenharmony_ci	    ((reg >= 0xc0) && (reg < 0xc8)) ||
3088c2ecf20Sopenharmony_ci	    ((reg >= 0xca) && (reg < 0xf0)))
3098c2ecf20Sopenharmony_ci		return true;
3108c2ecf20Sopenharmony_ci	else
3118c2ecf20Sopenharmony_ci		return false;
3128c2ecf20Sopenharmony_ci}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic bool encx24j600_regmap_volatile(struct device *dev, unsigned int reg)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	switch (reg) {
3178c2ecf20Sopenharmony_ci	case ERXHEAD:
3188c2ecf20Sopenharmony_ci	case EDMACS:
3198c2ecf20Sopenharmony_ci	case ETXSTAT:
3208c2ecf20Sopenharmony_ci	case ETXWIRE:
3218c2ecf20Sopenharmony_ci	case ECON1:	/* Can be modified via single byte cmds */
3228c2ecf20Sopenharmony_ci	case ECON2:	/* Can be modified via single byte cmds */
3238c2ecf20Sopenharmony_ci	case ESTAT:
3248c2ecf20Sopenharmony_ci	case EIR:	/* Can be modified via single byte cmds */
3258c2ecf20Sopenharmony_ci	case MIRD:
3268c2ecf20Sopenharmony_ci	case MISTAT:
3278c2ecf20Sopenharmony_ci		return true;
3288c2ecf20Sopenharmony_ci	default:
3298c2ecf20Sopenharmony_ci		break;
3308c2ecf20Sopenharmony_ci	}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	return false;
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic bool encx24j600_regmap_precious(struct device *dev, unsigned int reg)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	/* single byte cmds are precious */
3388c2ecf20Sopenharmony_ci	if (((reg >= 0xc0) && (reg < 0xc8)) ||
3398c2ecf20Sopenharmony_ci	    ((reg >= 0xca) && (reg < 0xf0)))
3408c2ecf20Sopenharmony_ci		return true;
3418c2ecf20Sopenharmony_ci	else
3428c2ecf20Sopenharmony_ci		return false;
3438c2ecf20Sopenharmony_ci}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_cistatic int regmap_encx24j600_phy_reg_read(void *context, unsigned int reg,
3468c2ecf20Sopenharmony_ci					  unsigned int *val)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	struct encx24j600_context *ctx = context;
3498c2ecf20Sopenharmony_ci	int ret;
3508c2ecf20Sopenharmony_ci	unsigned int mistat;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	reg = MIREGADR_VAL | (reg & PHREG_MASK);
3538c2ecf20Sopenharmony_ci	ret = regmap_write(ctx->regmap, MIREGADR, reg);
3548c2ecf20Sopenharmony_ci	if (unlikely(ret))
3558c2ecf20Sopenharmony_ci		goto err_out;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	ret = regmap_write(ctx->regmap, MICMD, MIIRD);
3588c2ecf20Sopenharmony_ci	if (unlikely(ret))
3598c2ecf20Sopenharmony_ci		goto err_out;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	usleep_range(26, 100);
3628c2ecf20Sopenharmony_ci	while (((ret = regmap_read(ctx->regmap, MISTAT, &mistat)) == 0) &&
3638c2ecf20Sopenharmony_ci	       (mistat & BUSY))
3648c2ecf20Sopenharmony_ci		cpu_relax();
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	if (unlikely(ret))
3678c2ecf20Sopenharmony_ci		goto err_out;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	ret = regmap_write(ctx->regmap, MICMD, 0);
3708c2ecf20Sopenharmony_ci	if (unlikely(ret))
3718c2ecf20Sopenharmony_ci		goto err_out;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	ret = regmap_read(ctx->regmap, MIRD, val);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cierr_out:
3768c2ecf20Sopenharmony_ci	if (ret)
3778c2ecf20Sopenharmony_ci		pr_err("%s: error %d reading reg %02x\n", __func__, ret,
3788c2ecf20Sopenharmony_ci		       reg & PHREG_MASK);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	return ret;
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cistatic int regmap_encx24j600_phy_reg_write(void *context, unsigned int reg,
3848c2ecf20Sopenharmony_ci					   unsigned int val)
3858c2ecf20Sopenharmony_ci{
3868c2ecf20Sopenharmony_ci	struct encx24j600_context *ctx = context;
3878c2ecf20Sopenharmony_ci	int ret;
3888c2ecf20Sopenharmony_ci	unsigned int mistat;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	reg = MIREGADR_VAL | (reg & PHREG_MASK);
3918c2ecf20Sopenharmony_ci	ret = regmap_write(ctx->regmap, MIREGADR, reg);
3928c2ecf20Sopenharmony_ci	if (unlikely(ret))
3938c2ecf20Sopenharmony_ci		goto err_out;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	ret = regmap_write(ctx->regmap, MIWR, val);
3968c2ecf20Sopenharmony_ci	if (unlikely(ret))
3978c2ecf20Sopenharmony_ci		goto err_out;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	usleep_range(26, 100);
4008c2ecf20Sopenharmony_ci	while (((ret = regmap_read(ctx->regmap, MISTAT, &mistat)) == 0) &&
4018c2ecf20Sopenharmony_ci	       (mistat & BUSY))
4028c2ecf20Sopenharmony_ci		cpu_relax();
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cierr_out:
4058c2ecf20Sopenharmony_ci	if (ret)
4068c2ecf20Sopenharmony_ci		pr_err("%s: error %d writing reg %02x=%04x\n", __func__, ret,
4078c2ecf20Sopenharmony_ci		       reg & PHREG_MASK, val);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	return ret;
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistatic bool encx24j600_phymap_readable(struct device *dev, unsigned int reg)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	switch (reg) {
4158c2ecf20Sopenharmony_ci	case PHCON1:
4168c2ecf20Sopenharmony_ci	case PHSTAT1:
4178c2ecf20Sopenharmony_ci	case PHANA:
4188c2ecf20Sopenharmony_ci	case PHANLPA:
4198c2ecf20Sopenharmony_ci	case PHANE:
4208c2ecf20Sopenharmony_ci	case PHCON2:
4218c2ecf20Sopenharmony_ci	case PHSTAT2:
4228c2ecf20Sopenharmony_ci	case PHSTAT3:
4238c2ecf20Sopenharmony_ci		return true;
4248c2ecf20Sopenharmony_ci	default:
4258c2ecf20Sopenharmony_ci		return false;
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_cistatic bool encx24j600_phymap_writeable(struct device *dev, unsigned int reg)
4308c2ecf20Sopenharmony_ci{
4318c2ecf20Sopenharmony_ci	switch (reg) {
4328c2ecf20Sopenharmony_ci	case PHCON1:
4338c2ecf20Sopenharmony_ci	case PHCON2:
4348c2ecf20Sopenharmony_ci	case PHANA:
4358c2ecf20Sopenharmony_ci		return true;
4368c2ecf20Sopenharmony_ci	case PHSTAT1:
4378c2ecf20Sopenharmony_ci	case PHSTAT2:
4388c2ecf20Sopenharmony_ci	case PHSTAT3:
4398c2ecf20Sopenharmony_ci	case PHANLPA:
4408c2ecf20Sopenharmony_ci	case PHANE:
4418c2ecf20Sopenharmony_ci	default:
4428c2ecf20Sopenharmony_ci		return false;
4438c2ecf20Sopenharmony_ci	}
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_cistatic bool encx24j600_phymap_volatile(struct device *dev, unsigned int reg)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	switch (reg) {
4498c2ecf20Sopenharmony_ci	case PHSTAT1:
4508c2ecf20Sopenharmony_ci	case PHSTAT2:
4518c2ecf20Sopenharmony_ci	case PHSTAT3:
4528c2ecf20Sopenharmony_ci	case PHANLPA:
4538c2ecf20Sopenharmony_ci	case PHANE:
4548c2ecf20Sopenharmony_ci	case PHCON2:
4558c2ecf20Sopenharmony_ci		return true;
4568c2ecf20Sopenharmony_ci	default:
4578c2ecf20Sopenharmony_ci		return false;
4588c2ecf20Sopenharmony_ci	}
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_cistatic struct regmap_config regcfg = {
4628c2ecf20Sopenharmony_ci	.name = "reg",
4638c2ecf20Sopenharmony_ci	.reg_bits = 8,
4648c2ecf20Sopenharmony_ci	.val_bits = 16,
4658c2ecf20Sopenharmony_ci	.max_register = 0xee,
4668c2ecf20Sopenharmony_ci	.reg_stride = 2,
4678c2ecf20Sopenharmony_ci	.cache_type = REGCACHE_RBTREE,
4688c2ecf20Sopenharmony_ci	.val_format_endian = REGMAP_ENDIAN_LITTLE,
4698c2ecf20Sopenharmony_ci	.readable_reg = encx24j600_regmap_readable,
4708c2ecf20Sopenharmony_ci	.writeable_reg = encx24j600_regmap_writeable,
4718c2ecf20Sopenharmony_ci	.volatile_reg = encx24j600_regmap_volatile,
4728c2ecf20Sopenharmony_ci	.precious_reg = encx24j600_regmap_precious,
4738c2ecf20Sopenharmony_ci	.lock = regmap_lock_mutex,
4748c2ecf20Sopenharmony_ci	.unlock = regmap_unlock_mutex,
4758c2ecf20Sopenharmony_ci};
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_cistatic struct regmap_bus regmap_encx24j600 = {
4788c2ecf20Sopenharmony_ci	.write = regmap_encx24j600_write,
4798c2ecf20Sopenharmony_ci	.read = regmap_encx24j600_read,
4808c2ecf20Sopenharmony_ci	.reg_update_bits = regmap_encx24j600_reg_update_bits,
4818c2ecf20Sopenharmony_ci};
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_cistatic struct regmap_config phycfg = {
4848c2ecf20Sopenharmony_ci	.name = "phy",
4858c2ecf20Sopenharmony_ci	.reg_bits = 8,
4868c2ecf20Sopenharmony_ci	.val_bits = 16,
4878c2ecf20Sopenharmony_ci	.max_register = 0x1f,
4888c2ecf20Sopenharmony_ci	.cache_type = REGCACHE_RBTREE,
4898c2ecf20Sopenharmony_ci	.val_format_endian = REGMAP_ENDIAN_LITTLE,
4908c2ecf20Sopenharmony_ci	.readable_reg = encx24j600_phymap_readable,
4918c2ecf20Sopenharmony_ci	.writeable_reg = encx24j600_phymap_writeable,
4928c2ecf20Sopenharmony_ci	.volatile_reg = encx24j600_phymap_volatile,
4938c2ecf20Sopenharmony_ci};
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_cistatic struct regmap_bus phymap_encx24j600 = {
4968c2ecf20Sopenharmony_ci	.reg_write = regmap_encx24j600_phy_reg_write,
4978c2ecf20Sopenharmony_ci	.reg_read = regmap_encx24j600_phy_reg_read,
4988c2ecf20Sopenharmony_ci};
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ciint devm_regmap_init_encx24j600(struct device *dev,
5018c2ecf20Sopenharmony_ci				struct encx24j600_context *ctx)
5028c2ecf20Sopenharmony_ci{
5038c2ecf20Sopenharmony_ci	mutex_init(&ctx->mutex);
5048c2ecf20Sopenharmony_ci	regcfg.lock_arg = ctx;
5058c2ecf20Sopenharmony_ci	ctx->regmap = devm_regmap_init(dev, &regmap_encx24j600, ctx, &regcfg);
5068c2ecf20Sopenharmony_ci	if (IS_ERR(ctx->regmap))
5078c2ecf20Sopenharmony_ci		return PTR_ERR(ctx->regmap);
5088c2ecf20Sopenharmony_ci	ctx->phymap = devm_regmap_init(dev, &phymap_encx24j600, ctx, &phycfg);
5098c2ecf20Sopenharmony_ci	if (IS_ERR(ctx->phymap))
5108c2ecf20Sopenharmony_ci		return PTR_ERR(ctx->phymap);
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	return 0;
5138c2ecf20Sopenharmony_ci}
5148c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_regmap_init_encx24j600);
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
517