diff options
Diffstat (limited to 'drivers/net/ethernet/wiznet/w5100-spi.c')
-rw-r--r-- | drivers/net/ethernet/wiznet/w5100-spi.c | 192 |
1 files changed, 178 insertions, 14 deletions
diff --git a/drivers/net/ethernet/wiznet/w5100-spi.c b/drivers/net/ethernet/wiznet/w5100-spi.c index 598a7b0..b868e45 100644 --- a/drivers/net/ethernet/wiznet/w5100-spi.c +++ b/drivers/net/ethernet/wiznet/w5100-spi.c @@ -1,5 +1,5 @@ /* - * Ethernet driver for the WIZnet W5100/W5200 chip. + * Ethernet driver for the WIZnet W5100/W5200/W5500 chip. * * Copyright (C) 2016 Akinobu Mita <akinobu.mita@gmail.com> * @@ -8,6 +8,7 @@ * Datasheet: * http://www.wiznet.co.kr/wp-content/uploads/wiznethome/Chip/W5100/Document/W5100_Datasheet_v1.2.6.pdf * http://wiznethome.cafe24.com/wp-content/uploads/wiznethome/Chip/W5200/Documents/W5200_DS_V140E.pdf + * http://wizwiki.net/wiki/lib/exe/fetch.php?media=products:w5500:w5500_ds_v106e_141230.pdf */ #include <linux/kernel.h> @@ -21,7 +22,7 @@ #define W5100_SPI_WRITE_OPCODE 0xf0 #define W5100_SPI_READ_OPCODE 0x0f -static int w5100_spi_read(struct net_device *ndev, u16 addr) +static int w5100_spi_read(struct net_device *ndev, u32 addr) { struct spi_device *spi = to_spi_device(ndev->dev.parent); u8 cmd[3] = { W5100_SPI_READ_OPCODE, addr >> 8, addr & 0xff }; @@ -33,7 +34,7 @@ static int w5100_spi_read(struct net_device *ndev, u16 addr) return ret ? ret : data; } -static int w5100_spi_write(struct net_device *ndev, u16 addr, u8 data) +static int w5100_spi_write(struct net_device *ndev, u32 addr, u8 data) { struct spi_device *spi = to_spi_device(ndev->dev.parent); u8 cmd[4] = { W5100_SPI_WRITE_OPCODE, addr >> 8, addr & 0xff, data}; @@ -41,7 +42,7 @@ static int w5100_spi_write(struct net_device *ndev, u16 addr, u8 data) return spi_write_then_read(spi, cmd, sizeof(cmd), NULL, 0); } -static int w5100_spi_read16(struct net_device *ndev, u16 addr) +static int w5100_spi_read16(struct net_device *ndev, u32 addr) { u16 data; int ret; @@ -55,7 +56,7 @@ static int w5100_spi_read16(struct net_device *ndev, u16 addr) return ret < 0 ? ret : data | ret; } -static int w5100_spi_write16(struct net_device *ndev, u16 addr, u16 data) +static int w5100_spi_write16(struct net_device *ndev, u32 addr, u16 data) { int ret; @@ -66,7 +67,7 @@ static int w5100_spi_write16(struct net_device *ndev, u16 addr, u16 data) return w5100_spi_write(ndev, addr + 1, data & 0xff); } -static int w5100_spi_readbulk(struct net_device *ndev, u16 addr, u8 *buf, +static int w5100_spi_readbulk(struct net_device *ndev, u32 addr, u8 *buf, int len) { int i; @@ -82,7 +83,7 @@ static int w5100_spi_readbulk(struct net_device *ndev, u16 addr, u8 *buf, return 0; } -static int w5100_spi_writebulk(struct net_device *ndev, u16 addr, const u8 *buf, +static int w5100_spi_writebulk(struct net_device *ndev, u32 addr, const u8 *buf, int len) { int i; @@ -134,7 +135,7 @@ static int w5200_spi_init(struct net_device *ndev) return 0; } -static int w5200_spi_read(struct net_device *ndev, u16 addr) +static int w5200_spi_read(struct net_device *ndev, u32 addr) { struct spi_device *spi = to_spi_device(ndev->dev.parent); u8 cmd[4] = { addr >> 8, addr & 0xff, 0, 1 }; @@ -146,7 +147,7 @@ static int w5200_spi_read(struct net_device *ndev, u16 addr) return ret ? ret : data; } -static int w5200_spi_write(struct net_device *ndev, u16 addr, u8 data) +static int w5200_spi_write(struct net_device *ndev, u32 addr, u8 data) { struct spi_device *spi = to_spi_device(ndev->dev.parent); u8 cmd[5] = { addr >> 8, addr & 0xff, W5200_SPI_WRITE_OPCODE, 1, data }; @@ -154,7 +155,7 @@ static int w5200_spi_write(struct net_device *ndev, u16 addr, u8 data) return spi_write_then_read(spi, cmd, sizeof(cmd), NULL, 0); } -static int w5200_spi_read16(struct net_device *ndev, u16 addr) +static int w5200_spi_read16(struct net_device *ndev, u32 addr) { struct spi_device *spi = to_spi_device(ndev->dev.parent); u8 cmd[4] = { addr >> 8, addr & 0xff, 0, 2 }; @@ -166,7 +167,7 @@ static int w5200_spi_read16(struct net_device *ndev, u16 addr) return ret ? ret : be16_to_cpu(data); } -static int w5200_spi_write16(struct net_device *ndev, u16 addr, u16 data) +static int w5200_spi_write16(struct net_device *ndev, u32 addr, u16 data) { struct spi_device *spi = to_spi_device(ndev->dev.parent); u8 cmd[6] = { @@ -178,7 +179,7 @@ static int w5200_spi_write16(struct net_device *ndev, u16 addr, u16 data) return spi_write_then_read(spi, cmd, sizeof(cmd), NULL, 0); } -static int w5200_spi_readbulk(struct net_device *ndev, u16 addr, u8 *buf, +static int w5200_spi_readbulk(struct net_device *ndev, u32 addr, u8 *buf, int len) { struct spi_device *spi = to_spi_device(ndev->dev.parent); @@ -208,7 +209,7 @@ static int w5200_spi_readbulk(struct net_device *ndev, u16 addr, u8 *buf, return ret; } -static int w5200_spi_writebulk(struct net_device *ndev, u16 addr, const u8 *buf, +static int w5200_spi_writebulk(struct net_device *ndev, u32 addr, const u8 *buf, int len) { struct spi_device *spi = to_spi_device(ndev->dev.parent); @@ -250,6 +251,164 @@ static const struct w5100_ops w5200_ops = { .init = w5200_spi_init, }; +#define W5500_SPI_BLOCK_SELECT(addr) (((addr) >> 16) & 0x1f) +#define W5500_SPI_READ_CONTROL(addr) (W5500_SPI_BLOCK_SELECT(addr) << 3) +#define W5500_SPI_WRITE_CONTROL(addr) \ + ((W5500_SPI_BLOCK_SELECT(addr) << 3) | BIT(2)) + +struct w5500_spi_priv { + /* Serialize access to cmd_buf */ + struct mutex cmd_lock; + + /* DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + u8 cmd_buf[3] ____cacheline_aligned; +}; + +static struct w5500_spi_priv *w5500_spi_priv(struct net_device *ndev) +{ + return w5100_ops_priv(ndev); +} + +static int w5500_spi_init(struct net_device *ndev) +{ + struct w5500_spi_priv *spi_priv = w5500_spi_priv(ndev); + + mutex_init(&spi_priv->cmd_lock); + + return 0; +} + +static int w5500_spi_read(struct net_device *ndev, u32 addr) +{ + struct spi_device *spi = to_spi_device(ndev->dev.parent); + u8 cmd[3] = { + addr >> 8, + addr, + W5500_SPI_READ_CONTROL(addr) + }; + u8 data; + int ret; + + ret = spi_write_then_read(spi, cmd, sizeof(cmd), &data, 1); + + return ret ? ret : data; +} + +static int w5500_spi_write(struct net_device *ndev, u32 addr, u8 data) +{ + struct spi_device *spi = to_spi_device(ndev->dev.parent); + u8 cmd[4] = { + addr >> 8, + addr, + W5500_SPI_WRITE_CONTROL(addr), + data + }; + + return spi_write_then_read(spi, cmd, sizeof(cmd), NULL, 0); +} + +static int w5500_spi_read16(struct net_device *ndev, u32 addr) +{ + struct spi_device *spi = to_spi_device(ndev->dev.parent); + u8 cmd[3] = { + addr >> 8, + addr, + W5500_SPI_READ_CONTROL(addr) + }; + __be16 data; + int ret; + + ret = spi_write_then_read(spi, cmd, sizeof(cmd), &data, sizeof(data)); + + return ret ? ret : be16_to_cpu(data); +} + +static int w5500_spi_write16(struct net_device *ndev, u32 addr, u16 data) +{ + struct spi_device *spi = to_spi_device(ndev->dev.parent); + u8 cmd[5] = { + addr >> 8, + addr, + W5500_SPI_WRITE_CONTROL(addr), + data >> 8, + data + }; + + return spi_write_then_read(spi, cmd, sizeof(cmd), NULL, 0); +} + +static int w5500_spi_readbulk(struct net_device *ndev, u32 addr, u8 *buf, + int len) +{ + struct spi_device *spi = to_spi_device(ndev->dev.parent); + struct w5500_spi_priv *spi_priv = w5500_spi_priv(ndev); + struct spi_transfer xfer[] = { + { + .tx_buf = spi_priv->cmd_buf, + .len = sizeof(spi_priv->cmd_buf), + }, + { + .rx_buf = buf, + .len = len, + }, + }; + int ret; + + mutex_lock(&spi_priv->cmd_lock); + + spi_priv->cmd_buf[0] = addr >> 8; + spi_priv->cmd_buf[1] = addr; + spi_priv->cmd_buf[2] = W5500_SPI_READ_CONTROL(addr); + ret = spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer)); + + mutex_unlock(&spi_priv->cmd_lock); + + return ret; +} + +static int w5500_spi_writebulk(struct net_device *ndev, u32 addr, const u8 *buf, + int len) +{ + struct spi_device *spi = to_spi_device(ndev->dev.parent); + struct w5500_spi_priv *spi_priv = w5500_spi_priv(ndev); + struct spi_transfer xfer[] = { + { + .tx_buf = spi_priv->cmd_buf, + .len = sizeof(spi_priv->cmd_buf), + }, + { + .tx_buf = buf, + .len = len, + }, + }; + int ret; + + mutex_lock(&spi_priv->cmd_lock); + + spi_priv->cmd_buf[0] = addr >> 8; + spi_priv->cmd_buf[1] = addr; + spi_priv->cmd_buf[2] = W5500_SPI_WRITE_CONTROL(addr); + ret = spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer)); + + mutex_unlock(&spi_priv->cmd_lock); + + return ret; +} + +static const struct w5100_ops w5500_ops = { + .may_sleep = true, + .chip_id = W5500, + .read = w5500_spi_read, + .write = w5500_spi_write, + .read16 = w5500_spi_read16, + .write16 = w5500_spi_write16, + .readbulk = w5500_spi_readbulk, + .writebulk = w5500_spi_writebulk, + .init = w5500_spi_init, +}; + static int w5100_spi_probe(struct spi_device *spi) { const struct spi_device_id *id = spi_get_device_id(spi); @@ -265,6 +424,10 @@ static int w5100_spi_probe(struct spi_device *spi) ops = &w5200_ops; priv_size = sizeof(struct w5200_spi_priv); break; + case W5500: + ops = &w5500_ops; + priv_size = sizeof(struct w5500_spi_priv); + break; default: return -EINVAL; } @@ -280,6 +443,7 @@ static int w5100_spi_remove(struct spi_device *spi) static const struct spi_device_id w5100_spi_ids[] = { { "w5100", W5100 }, { "w5200", W5200 }, + { "w5500", W5500 }, {} }; MODULE_DEVICE_TABLE(spi, w5100_spi_ids); @@ -295,6 +459,6 @@ static struct spi_driver w5100_spi_driver = { }; module_spi_driver(w5100_spi_driver); -MODULE_DESCRIPTION("WIZnet W5100/W5200 Ethernet driver for SPI mode"); +MODULE_DESCRIPTION("WIZnet W5100/W5200/W5500 Ethernet driver for SPI mode"); MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>"); MODULE_LICENSE("GPL"); |