18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Ethernet driver for the WIZnet W5100/W5200/W5500 chip.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2016 Akinobu Mita <akinobu.mita@gmail.com>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Datasheet:
88c2ecf20Sopenharmony_ci * http://www.wiznet.co.kr/wp-content/uploads/wiznethome/Chip/W5100/Document/W5100_Datasheet_v1.2.6.pdf
98c2ecf20Sopenharmony_ci * http://wiznethome.cafe24.com/wp-content/uploads/wiznethome/Chip/W5200/Documents/W5200_DS_V140E.pdf
108c2ecf20Sopenharmony_ci * http://wizwiki.net/wiki/lib/exe/fetch.php?media=products:w5500:w5500_ds_v106e_141230.pdf
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/delay.h>
168c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
178c2ecf20Sopenharmony_ci#include <linux/of_net.h>
188c2ecf20Sopenharmony_ci#include <linux/of_device.h>
198c2ecf20Sopenharmony_ci#include <linux/spi/spi.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include "w5100.h"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define W5100_SPI_WRITE_OPCODE 0xf0
248c2ecf20Sopenharmony_ci#define W5100_SPI_READ_OPCODE 0x0f
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic int w5100_spi_read(struct net_device *ndev, u32 addr)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	struct spi_device *spi = to_spi_device(ndev->dev.parent);
298c2ecf20Sopenharmony_ci	u8 cmd[3] = { W5100_SPI_READ_OPCODE, addr >> 8, addr & 0xff };
308c2ecf20Sopenharmony_ci	u8 data;
318c2ecf20Sopenharmony_ci	int ret;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	ret = spi_write_then_read(spi, cmd, sizeof(cmd), &data, 1);
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	return ret ? ret : data;
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic int w5100_spi_write(struct net_device *ndev, u32 addr, u8 data)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	struct spi_device *spi = to_spi_device(ndev->dev.parent);
418c2ecf20Sopenharmony_ci	u8 cmd[4] = { W5100_SPI_WRITE_OPCODE, addr >> 8, addr & 0xff, data};
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	return spi_write_then_read(spi, cmd, sizeof(cmd), NULL, 0);
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic int w5100_spi_read16(struct net_device *ndev, u32 addr)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	u16 data;
498c2ecf20Sopenharmony_ci	int ret;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	ret = w5100_spi_read(ndev, addr);
528c2ecf20Sopenharmony_ci	if (ret < 0)
538c2ecf20Sopenharmony_ci		return ret;
548c2ecf20Sopenharmony_ci	data = ret << 8;
558c2ecf20Sopenharmony_ci	ret = w5100_spi_read(ndev, addr + 1);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	return ret < 0 ? ret : data | ret;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic int w5100_spi_write16(struct net_device *ndev, u32 addr, u16 data)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	int ret;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	ret = w5100_spi_write(ndev, addr, data >> 8);
658c2ecf20Sopenharmony_ci	if (ret)
668c2ecf20Sopenharmony_ci		return ret;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	return w5100_spi_write(ndev, addr + 1, data & 0xff);
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic int w5100_spi_readbulk(struct net_device *ndev, u32 addr, u8 *buf,
728c2ecf20Sopenharmony_ci			      int len)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	int i;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	for (i = 0; i < len; i++) {
778c2ecf20Sopenharmony_ci		int ret = w5100_spi_read(ndev, addr + i);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci		if (ret < 0)
808c2ecf20Sopenharmony_ci			return ret;
818c2ecf20Sopenharmony_ci		buf[i] = ret;
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	return 0;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic int w5100_spi_writebulk(struct net_device *ndev, u32 addr, const u8 *buf,
888c2ecf20Sopenharmony_ci			       int len)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	int i;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	for (i = 0; i < len; i++) {
938c2ecf20Sopenharmony_ci		int ret = w5100_spi_write(ndev, addr + i, buf[i]);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci		if (ret)
968c2ecf20Sopenharmony_ci			return ret;
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	return 0;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic const struct w5100_ops w5100_spi_ops = {
1038c2ecf20Sopenharmony_ci	.may_sleep = true,
1048c2ecf20Sopenharmony_ci	.chip_id = W5100,
1058c2ecf20Sopenharmony_ci	.read = w5100_spi_read,
1068c2ecf20Sopenharmony_ci	.write = w5100_spi_write,
1078c2ecf20Sopenharmony_ci	.read16 = w5100_spi_read16,
1088c2ecf20Sopenharmony_ci	.write16 = w5100_spi_write16,
1098c2ecf20Sopenharmony_ci	.readbulk = w5100_spi_readbulk,
1108c2ecf20Sopenharmony_ci	.writebulk = w5100_spi_writebulk,
1118c2ecf20Sopenharmony_ci};
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci#define W5200_SPI_WRITE_OPCODE 0x80
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistruct w5200_spi_priv {
1168c2ecf20Sopenharmony_ci	/* Serialize access to cmd_buf */
1178c2ecf20Sopenharmony_ci	struct mutex cmd_lock;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	/* DMA (thus cache coherency maintenance) requires the
1208c2ecf20Sopenharmony_ci	 * transfer buffers to live in their own cache lines.
1218c2ecf20Sopenharmony_ci	 */
1228c2ecf20Sopenharmony_ci	u8 cmd_buf[4] ____cacheline_aligned;
1238c2ecf20Sopenharmony_ci};
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic struct w5200_spi_priv *w5200_spi_priv(struct net_device *ndev)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	return w5100_ops_priv(ndev);
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic int w5200_spi_init(struct net_device *ndev)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	struct w5200_spi_priv *spi_priv = w5200_spi_priv(ndev);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	mutex_init(&spi_priv->cmd_lock);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	return 0;
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic int w5200_spi_read(struct net_device *ndev, u32 addr)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	struct spi_device *spi = to_spi_device(ndev->dev.parent);
1428c2ecf20Sopenharmony_ci	u8 cmd[4] = { addr >> 8, addr & 0xff, 0, 1 };
1438c2ecf20Sopenharmony_ci	u8 data;
1448c2ecf20Sopenharmony_ci	int ret;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	ret = spi_write_then_read(spi, cmd, sizeof(cmd), &data, 1);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	return ret ? ret : data;
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic int w5200_spi_write(struct net_device *ndev, u32 addr, u8 data)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	struct spi_device *spi = to_spi_device(ndev->dev.parent);
1548c2ecf20Sopenharmony_ci	u8 cmd[5] = { addr >> 8, addr & 0xff, W5200_SPI_WRITE_OPCODE, 1, data };
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	return spi_write_then_read(spi, cmd, sizeof(cmd), NULL, 0);
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic int w5200_spi_read16(struct net_device *ndev, u32 addr)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	struct spi_device *spi = to_spi_device(ndev->dev.parent);
1628c2ecf20Sopenharmony_ci	u8 cmd[4] = { addr >> 8, addr & 0xff, 0, 2 };
1638c2ecf20Sopenharmony_ci	__be16 data;
1648c2ecf20Sopenharmony_ci	int ret;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	ret = spi_write_then_read(spi, cmd, sizeof(cmd), &data, sizeof(data));
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	return ret ? ret : be16_to_cpu(data);
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic int w5200_spi_write16(struct net_device *ndev, u32 addr, u16 data)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	struct spi_device *spi = to_spi_device(ndev->dev.parent);
1748c2ecf20Sopenharmony_ci	u8 cmd[6] = {
1758c2ecf20Sopenharmony_ci		addr >> 8, addr & 0xff,
1768c2ecf20Sopenharmony_ci		W5200_SPI_WRITE_OPCODE, 2,
1778c2ecf20Sopenharmony_ci		data >> 8, data & 0xff
1788c2ecf20Sopenharmony_ci	};
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	return spi_write_then_read(spi, cmd, sizeof(cmd), NULL, 0);
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic int w5200_spi_readbulk(struct net_device *ndev, u32 addr, u8 *buf,
1848c2ecf20Sopenharmony_ci			      int len)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	struct spi_device *spi = to_spi_device(ndev->dev.parent);
1878c2ecf20Sopenharmony_ci	struct w5200_spi_priv *spi_priv = w5200_spi_priv(ndev);
1888c2ecf20Sopenharmony_ci	struct spi_transfer xfer[] = {
1898c2ecf20Sopenharmony_ci		{
1908c2ecf20Sopenharmony_ci			.tx_buf = spi_priv->cmd_buf,
1918c2ecf20Sopenharmony_ci			.len = sizeof(spi_priv->cmd_buf),
1928c2ecf20Sopenharmony_ci		},
1938c2ecf20Sopenharmony_ci		{
1948c2ecf20Sopenharmony_ci			.rx_buf = buf,
1958c2ecf20Sopenharmony_ci			.len = len,
1968c2ecf20Sopenharmony_ci		},
1978c2ecf20Sopenharmony_ci	};
1988c2ecf20Sopenharmony_ci	int ret;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	mutex_lock(&spi_priv->cmd_lock);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	spi_priv->cmd_buf[0] = addr >> 8;
2038c2ecf20Sopenharmony_ci	spi_priv->cmd_buf[1] = addr;
2048c2ecf20Sopenharmony_ci	spi_priv->cmd_buf[2] = len >> 8;
2058c2ecf20Sopenharmony_ci	spi_priv->cmd_buf[3] = len;
2068c2ecf20Sopenharmony_ci	ret = spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer));
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	mutex_unlock(&spi_priv->cmd_lock);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	return ret;
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic int w5200_spi_writebulk(struct net_device *ndev, u32 addr, const u8 *buf,
2148c2ecf20Sopenharmony_ci			       int len)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	struct spi_device *spi = to_spi_device(ndev->dev.parent);
2178c2ecf20Sopenharmony_ci	struct w5200_spi_priv *spi_priv = w5200_spi_priv(ndev);
2188c2ecf20Sopenharmony_ci	struct spi_transfer xfer[] = {
2198c2ecf20Sopenharmony_ci		{
2208c2ecf20Sopenharmony_ci			.tx_buf = spi_priv->cmd_buf,
2218c2ecf20Sopenharmony_ci			.len = sizeof(spi_priv->cmd_buf),
2228c2ecf20Sopenharmony_ci		},
2238c2ecf20Sopenharmony_ci		{
2248c2ecf20Sopenharmony_ci			.tx_buf = buf,
2258c2ecf20Sopenharmony_ci			.len = len,
2268c2ecf20Sopenharmony_ci		},
2278c2ecf20Sopenharmony_ci	};
2288c2ecf20Sopenharmony_ci	int ret;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	mutex_lock(&spi_priv->cmd_lock);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	spi_priv->cmd_buf[0] = addr >> 8;
2338c2ecf20Sopenharmony_ci	spi_priv->cmd_buf[1] = addr;
2348c2ecf20Sopenharmony_ci	spi_priv->cmd_buf[2] = W5200_SPI_WRITE_OPCODE | (len >> 8);
2358c2ecf20Sopenharmony_ci	spi_priv->cmd_buf[3] = len;
2368c2ecf20Sopenharmony_ci	ret = spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer));
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	mutex_unlock(&spi_priv->cmd_lock);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	return ret;
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic const struct w5100_ops w5200_ops = {
2448c2ecf20Sopenharmony_ci	.may_sleep = true,
2458c2ecf20Sopenharmony_ci	.chip_id = W5200,
2468c2ecf20Sopenharmony_ci	.read = w5200_spi_read,
2478c2ecf20Sopenharmony_ci	.write = w5200_spi_write,
2488c2ecf20Sopenharmony_ci	.read16 = w5200_spi_read16,
2498c2ecf20Sopenharmony_ci	.write16 = w5200_spi_write16,
2508c2ecf20Sopenharmony_ci	.readbulk = w5200_spi_readbulk,
2518c2ecf20Sopenharmony_ci	.writebulk = w5200_spi_writebulk,
2528c2ecf20Sopenharmony_ci	.init = w5200_spi_init,
2538c2ecf20Sopenharmony_ci};
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci#define W5500_SPI_BLOCK_SELECT(addr) (((addr) >> 16) & 0x1f)
2568c2ecf20Sopenharmony_ci#define W5500_SPI_READ_CONTROL(addr) (W5500_SPI_BLOCK_SELECT(addr) << 3)
2578c2ecf20Sopenharmony_ci#define W5500_SPI_WRITE_CONTROL(addr)	\
2588c2ecf20Sopenharmony_ci	((W5500_SPI_BLOCK_SELECT(addr) << 3) | BIT(2))
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistruct w5500_spi_priv {
2618c2ecf20Sopenharmony_ci	/* Serialize access to cmd_buf */
2628c2ecf20Sopenharmony_ci	struct mutex cmd_lock;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	/* DMA (thus cache coherency maintenance) requires the
2658c2ecf20Sopenharmony_ci	 * transfer buffers to live in their own cache lines.
2668c2ecf20Sopenharmony_ci	 */
2678c2ecf20Sopenharmony_ci	u8 cmd_buf[3] ____cacheline_aligned;
2688c2ecf20Sopenharmony_ci};
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic struct w5500_spi_priv *w5500_spi_priv(struct net_device *ndev)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	return w5100_ops_priv(ndev);
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_cistatic int w5500_spi_init(struct net_device *ndev)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	struct w5500_spi_priv *spi_priv = w5500_spi_priv(ndev);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	mutex_init(&spi_priv->cmd_lock);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	return 0;
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic int w5500_spi_read(struct net_device *ndev, u32 addr)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	struct spi_device *spi = to_spi_device(ndev->dev.parent);
2878c2ecf20Sopenharmony_ci	u8 cmd[3] = {
2888c2ecf20Sopenharmony_ci		addr >> 8,
2898c2ecf20Sopenharmony_ci		addr,
2908c2ecf20Sopenharmony_ci		W5500_SPI_READ_CONTROL(addr)
2918c2ecf20Sopenharmony_ci	};
2928c2ecf20Sopenharmony_ci	u8 data;
2938c2ecf20Sopenharmony_ci	int ret;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	ret = spi_write_then_read(spi, cmd, sizeof(cmd), &data, 1);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	return ret ? ret : data;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic int w5500_spi_write(struct net_device *ndev, u32 addr, u8 data)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	struct spi_device *spi = to_spi_device(ndev->dev.parent);
3038c2ecf20Sopenharmony_ci	u8 cmd[4] = {
3048c2ecf20Sopenharmony_ci		addr >> 8,
3058c2ecf20Sopenharmony_ci		addr,
3068c2ecf20Sopenharmony_ci		W5500_SPI_WRITE_CONTROL(addr),
3078c2ecf20Sopenharmony_ci		data
3088c2ecf20Sopenharmony_ci	};
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	return spi_write_then_read(spi, cmd, sizeof(cmd), NULL, 0);
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_cistatic int w5500_spi_read16(struct net_device *ndev, u32 addr)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	struct spi_device *spi = to_spi_device(ndev->dev.parent);
3168c2ecf20Sopenharmony_ci	u8 cmd[3] = {
3178c2ecf20Sopenharmony_ci		addr >> 8,
3188c2ecf20Sopenharmony_ci		addr,
3198c2ecf20Sopenharmony_ci		W5500_SPI_READ_CONTROL(addr)
3208c2ecf20Sopenharmony_ci	};
3218c2ecf20Sopenharmony_ci	__be16 data;
3228c2ecf20Sopenharmony_ci	int ret;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	ret = spi_write_then_read(spi, cmd, sizeof(cmd), &data, sizeof(data));
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	return ret ? ret : be16_to_cpu(data);
3278c2ecf20Sopenharmony_ci}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_cistatic int w5500_spi_write16(struct net_device *ndev, u32 addr, u16 data)
3308c2ecf20Sopenharmony_ci{
3318c2ecf20Sopenharmony_ci	struct spi_device *spi = to_spi_device(ndev->dev.parent);
3328c2ecf20Sopenharmony_ci	u8 cmd[5] = {
3338c2ecf20Sopenharmony_ci		addr >> 8,
3348c2ecf20Sopenharmony_ci		addr,
3358c2ecf20Sopenharmony_ci		W5500_SPI_WRITE_CONTROL(addr),
3368c2ecf20Sopenharmony_ci		data >> 8,
3378c2ecf20Sopenharmony_ci		data
3388c2ecf20Sopenharmony_ci	};
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	return spi_write_then_read(spi, cmd, sizeof(cmd), NULL, 0);
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cistatic int w5500_spi_readbulk(struct net_device *ndev, u32 addr, u8 *buf,
3448c2ecf20Sopenharmony_ci			      int len)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	struct spi_device *spi = to_spi_device(ndev->dev.parent);
3478c2ecf20Sopenharmony_ci	struct w5500_spi_priv *spi_priv = w5500_spi_priv(ndev);
3488c2ecf20Sopenharmony_ci	struct spi_transfer xfer[] = {
3498c2ecf20Sopenharmony_ci		{
3508c2ecf20Sopenharmony_ci			.tx_buf = spi_priv->cmd_buf,
3518c2ecf20Sopenharmony_ci			.len = sizeof(spi_priv->cmd_buf),
3528c2ecf20Sopenharmony_ci		},
3538c2ecf20Sopenharmony_ci		{
3548c2ecf20Sopenharmony_ci			.rx_buf = buf,
3558c2ecf20Sopenharmony_ci			.len = len,
3568c2ecf20Sopenharmony_ci		},
3578c2ecf20Sopenharmony_ci	};
3588c2ecf20Sopenharmony_ci	int ret;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	mutex_lock(&spi_priv->cmd_lock);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	spi_priv->cmd_buf[0] = addr >> 8;
3638c2ecf20Sopenharmony_ci	spi_priv->cmd_buf[1] = addr;
3648c2ecf20Sopenharmony_ci	spi_priv->cmd_buf[2] = W5500_SPI_READ_CONTROL(addr);
3658c2ecf20Sopenharmony_ci	ret = spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer));
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	mutex_unlock(&spi_priv->cmd_lock);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	return ret;
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistatic int w5500_spi_writebulk(struct net_device *ndev, u32 addr, const u8 *buf,
3738c2ecf20Sopenharmony_ci			       int len)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	struct spi_device *spi = to_spi_device(ndev->dev.parent);
3768c2ecf20Sopenharmony_ci	struct w5500_spi_priv *spi_priv = w5500_spi_priv(ndev);
3778c2ecf20Sopenharmony_ci	struct spi_transfer xfer[] = {
3788c2ecf20Sopenharmony_ci		{
3798c2ecf20Sopenharmony_ci			.tx_buf = spi_priv->cmd_buf,
3808c2ecf20Sopenharmony_ci			.len = sizeof(spi_priv->cmd_buf),
3818c2ecf20Sopenharmony_ci		},
3828c2ecf20Sopenharmony_ci		{
3838c2ecf20Sopenharmony_ci			.tx_buf = buf,
3848c2ecf20Sopenharmony_ci			.len = len,
3858c2ecf20Sopenharmony_ci		},
3868c2ecf20Sopenharmony_ci	};
3878c2ecf20Sopenharmony_ci	int ret;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	mutex_lock(&spi_priv->cmd_lock);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	spi_priv->cmd_buf[0] = addr >> 8;
3928c2ecf20Sopenharmony_ci	spi_priv->cmd_buf[1] = addr;
3938c2ecf20Sopenharmony_ci	spi_priv->cmd_buf[2] = W5500_SPI_WRITE_CONTROL(addr);
3948c2ecf20Sopenharmony_ci	ret = spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer));
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	mutex_unlock(&spi_priv->cmd_lock);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	return ret;
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cistatic const struct w5100_ops w5500_ops = {
4028c2ecf20Sopenharmony_ci	.may_sleep = true,
4038c2ecf20Sopenharmony_ci	.chip_id = W5500,
4048c2ecf20Sopenharmony_ci	.read = w5500_spi_read,
4058c2ecf20Sopenharmony_ci	.write = w5500_spi_write,
4068c2ecf20Sopenharmony_ci	.read16 = w5500_spi_read16,
4078c2ecf20Sopenharmony_ci	.write16 = w5500_spi_write16,
4088c2ecf20Sopenharmony_ci	.readbulk = w5500_spi_readbulk,
4098c2ecf20Sopenharmony_ci	.writebulk = w5500_spi_writebulk,
4108c2ecf20Sopenharmony_ci	.init = w5500_spi_init,
4118c2ecf20Sopenharmony_ci};
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic const struct of_device_id w5100_of_match[] = {
4148c2ecf20Sopenharmony_ci	{ .compatible = "wiznet,w5100", .data = (const void*)W5100, },
4158c2ecf20Sopenharmony_ci	{ .compatible = "wiznet,w5200", .data = (const void*)W5200, },
4168c2ecf20Sopenharmony_ci	{ .compatible = "wiznet,w5500", .data = (const void*)W5500, },
4178c2ecf20Sopenharmony_ci	{ },
4188c2ecf20Sopenharmony_ci};
4198c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, w5100_of_match);
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_cistatic int w5100_spi_probe(struct spi_device *spi)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	const struct of_device_id *of_id;
4248c2ecf20Sopenharmony_ci	const struct w5100_ops *ops;
4258c2ecf20Sopenharmony_ci	kernel_ulong_t driver_data;
4268c2ecf20Sopenharmony_ci	int priv_size;
4278c2ecf20Sopenharmony_ci	const void *mac = of_get_mac_address(spi->dev.of_node);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	if (spi->dev.of_node) {
4308c2ecf20Sopenharmony_ci		of_id = of_match_device(w5100_of_match, &spi->dev);
4318c2ecf20Sopenharmony_ci		if (!of_id)
4328c2ecf20Sopenharmony_ci			return -ENODEV;
4338c2ecf20Sopenharmony_ci		driver_data = (kernel_ulong_t)of_id->data;
4348c2ecf20Sopenharmony_ci	} else {
4358c2ecf20Sopenharmony_ci		driver_data = spi_get_device_id(spi)->driver_data;
4368c2ecf20Sopenharmony_ci	}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	switch (driver_data) {
4398c2ecf20Sopenharmony_ci	case W5100:
4408c2ecf20Sopenharmony_ci		ops = &w5100_spi_ops;
4418c2ecf20Sopenharmony_ci		priv_size = 0;
4428c2ecf20Sopenharmony_ci		break;
4438c2ecf20Sopenharmony_ci	case W5200:
4448c2ecf20Sopenharmony_ci		ops = &w5200_ops;
4458c2ecf20Sopenharmony_ci		priv_size = sizeof(struct w5200_spi_priv);
4468c2ecf20Sopenharmony_ci		break;
4478c2ecf20Sopenharmony_ci	case W5500:
4488c2ecf20Sopenharmony_ci		ops = &w5500_ops;
4498c2ecf20Sopenharmony_ci		priv_size = sizeof(struct w5500_spi_priv);
4508c2ecf20Sopenharmony_ci		break;
4518c2ecf20Sopenharmony_ci	default:
4528c2ecf20Sopenharmony_ci		return -EINVAL;
4538c2ecf20Sopenharmony_ci	}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	return w5100_probe(&spi->dev, ops, priv_size, mac, spi->irq, -EINVAL);
4568c2ecf20Sopenharmony_ci}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_cistatic int w5100_spi_remove(struct spi_device *spi)
4598c2ecf20Sopenharmony_ci{
4608c2ecf20Sopenharmony_ci	return w5100_remove(&spi->dev);
4618c2ecf20Sopenharmony_ci}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_cistatic const struct spi_device_id w5100_spi_ids[] = {
4648c2ecf20Sopenharmony_ci	{ "w5100", W5100 },
4658c2ecf20Sopenharmony_ci	{ "w5200", W5200 },
4668c2ecf20Sopenharmony_ci	{ "w5500", W5500 },
4678c2ecf20Sopenharmony_ci	{}
4688c2ecf20Sopenharmony_ci};
4698c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(spi, w5100_spi_ids);
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_cistatic struct spi_driver w5100_spi_driver = {
4728c2ecf20Sopenharmony_ci	.driver		= {
4738c2ecf20Sopenharmony_ci		.name	= "w5100",
4748c2ecf20Sopenharmony_ci		.pm	= &w5100_pm_ops,
4758c2ecf20Sopenharmony_ci		.of_match_table = w5100_of_match,
4768c2ecf20Sopenharmony_ci	},
4778c2ecf20Sopenharmony_ci	.probe		= w5100_spi_probe,
4788c2ecf20Sopenharmony_ci	.remove		= w5100_spi_remove,
4798c2ecf20Sopenharmony_ci	.id_table	= w5100_spi_ids,
4808c2ecf20Sopenharmony_ci};
4818c2ecf20Sopenharmony_cimodule_spi_driver(w5100_spi_driver);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("WIZnet W5100/W5200/W5500 Ethernet driver for SPI mode");
4848c2ecf20Sopenharmony_ciMODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>");
4858c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
486