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