net: w5100: support SPI interface mode
This adds new w5100-spi driver which shares the bus interface independent code with existing w5100 driver. Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com> Cc: Mike Sinkovsky <msink@permonline.ru> Cc: David S. Miller <davem@davemloft.net> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
bf2c6b90b3
commit
630cf09751
|
@ -69,4 +69,18 @@ config WIZNET_BUS_ANY
|
||||||
Performance may decrease compared to explicitly selected bus mode.
|
Performance may decrease compared to explicitly selected bus mode.
|
||||||
endchoice
|
endchoice
|
||||||
|
|
||||||
|
config WIZNET_W5100_SPI
|
||||||
|
tristate "WIZnet W5100 Ethernet support for SPI mode"
|
||||||
|
depends on WIZNET_BUS_ANY
|
||||||
|
depends on SPI
|
||||||
|
---help---
|
||||||
|
In SPI mode host system accesses registers using SPI protocol
|
||||||
|
(mode 0) on the SPI bus.
|
||||||
|
|
||||||
|
Performance decreases compared to other bus interface mode.
|
||||||
|
In W5100 SPI mode, burst READ/WRITE processing are not provided.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module
|
||||||
|
will be called w5100-spi.
|
||||||
|
|
||||||
endif # NET_VENDOR_WIZNET
|
endif # NET_VENDOR_WIZNET
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
obj-$(CONFIG_WIZNET_W5100) += w5100.o
|
obj-$(CONFIG_WIZNET_W5100) += w5100.o
|
||||||
|
obj-$(CONFIG_WIZNET_W5100_SPI) += w5100-spi.o
|
||||||
obj-$(CONFIG_WIZNET_W5300) += w5300.o
|
obj-$(CONFIG_WIZNET_W5300) += w5300.o
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
/*
|
||||||
|
* Ethernet driver for the WIZnet W5100 chip.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 Akinobu Mita <akinobu.mita@gmail.com>
|
||||||
|
*
|
||||||
|
* Licensed under the GPL-2 or later.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/netdevice.h>
|
||||||
|
#include <linux/spi/spi.h>
|
||||||
|
|
||||||
|
#include "w5100.h"
|
||||||
|
|
||||||
|
#define W5100_SPI_WRITE_OPCODE 0xf0
|
||||||
|
#define W5100_SPI_READ_OPCODE 0x0f
|
||||||
|
|
||||||
|
static int w5100_spi_read(struct net_device *ndev, u16 addr)
|
||||||
|
{
|
||||||
|
struct spi_device *spi = to_spi_device(ndev->dev.parent);
|
||||||
|
u8 cmd[3] = { W5100_SPI_READ_OPCODE, addr >> 8, addr & 0xff };
|
||||||
|
u8 data;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = spi_write_then_read(spi, cmd, sizeof(cmd), &data, 1);
|
||||||
|
|
||||||
|
return ret ? ret : data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int w5100_spi_write(struct net_device *ndev, u16 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};
|
||||||
|
|
||||||
|
return spi_write_then_read(spi, cmd, sizeof(cmd), NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int w5100_spi_read16(struct net_device *ndev, u16 addr)
|
||||||
|
{
|
||||||
|
u16 data;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = w5100_spi_read(ndev, addr);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
data = ret << 8;
|
||||||
|
ret = w5100_spi_read(ndev, addr + 1);
|
||||||
|
|
||||||
|
return ret < 0 ? ret : data | ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int w5100_spi_write16(struct net_device *ndev, u16 addr, u16 data)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = w5100_spi_write(ndev, addr, data >> 8);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return w5100_spi_write(ndev, addr + 1, data & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int w5100_spi_readbulk(struct net_device *ndev, u16 addr, u8 *buf,
|
||||||
|
int len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
int ret = w5100_spi_read(ndev, addr + i);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
buf[i] = ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int w5100_spi_writebulk(struct net_device *ndev, u16 addr, const u8 *buf,
|
||||||
|
int len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
int ret = w5100_spi_write(ndev, addr + i, buf[i]);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct w5100_ops w5100_spi_ops = {
|
||||||
|
.may_sleep = true,
|
||||||
|
.read = w5100_spi_read,
|
||||||
|
.write = w5100_spi_write,
|
||||||
|
.read16 = w5100_spi_read16,
|
||||||
|
.write16 = w5100_spi_write16,
|
||||||
|
.readbulk = w5100_spi_readbulk,
|
||||||
|
.writebulk = w5100_spi_writebulk,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int w5100_spi_probe(struct spi_device *spi)
|
||||||
|
{
|
||||||
|
return w5100_probe(&spi->dev, &w5100_spi_ops, 0, NULL, spi->irq,
|
||||||
|
-EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int w5100_spi_remove(struct spi_device *spi)
|
||||||
|
{
|
||||||
|
return w5100_remove(&spi->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct spi_device_id w5100_spi_ids[] = {
|
||||||
|
{ "w5100", 0 },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(spi, w5100_spi_ids);
|
||||||
|
|
||||||
|
static struct spi_driver w5100_spi_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "w5100",
|
||||||
|
.pm = &w5100_pm_ops,
|
||||||
|
},
|
||||||
|
.probe = w5100_spi_probe,
|
||||||
|
.remove = w5100_spi_remove,
|
||||||
|
.id_table = w5100_spi_ids,
|
||||||
|
};
|
||||||
|
module_spi_driver(w5100_spi_driver);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("WIZnet W5100 Ethernet driver for SPI mode");
|
||||||
|
MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>");
|
||||||
|
MODULE_LICENSE("GPL");
|
Loading…
Reference in New Issue