// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Ethernet driver for the WIZnet W5100 / W5200 / W5500 chip .
*
* Copyright ( C ) 2016 Akinobu Mita < akinobu . mita @ gmail . com >
*
* 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>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/of.h>
#include <linux/of_net.h>
#include <linux/spi/spi.h>
#include "w5100.h"
#define W5100_SPI_WRITE_OPCODE 0 xf0
#define W5100_SPI_READ_OPCODE 0 x0f
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 & 0 xff };
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, u32 addr, u8 data)
{
struct spi_device *spi = to_spi_device(ndev->dev.parent);
u8 cmd[4 ] = { W5100_SPI_WRITE_OPCODE, addr >> 8 , addr & 0 xff, data};
return spi_write_then_read(spi, cmd, sizeof (cmd), NULL, 0 );
}
static int w5100_spi_read16(struct net_device *ndev, u32 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, u32 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 & 0 xff);
}
static int w5100_spi_readbulk(struct net_device *ndev, u32 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, u32 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 ,
.chip_id = W5100,
.read = w5100_spi_read,
.write = w5100_spi_write,
.read16 = w5100_spi_read16,
.write16 = w5100_spi_write16,
.readbulk = w5100_spi_readbulk,
.writebulk = w5100_spi_writebulk,
};
#define W5200_SPI_WRITE_OPCODE 0 x80
struct w5200_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[4 ] ____cacheline_aligned;
};
static struct w5200_spi_priv *w5200_spi_priv(struct net_device *ndev)
{
return w5100_ops_priv(ndev);
}
static int w5200_spi_init(struct net_device *ndev)
{
struct w5200_spi_priv *spi_priv = w5200_spi_priv(ndev);
mutex_init(&spi_priv->cmd_lock);
return 0 ;
}
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 & 0 xff, 0 , 1 };
u8 data;
int ret;
ret = spi_write_then_read(spi, cmd, sizeof (cmd), &data, 1 );
return ret ? ret : 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 & 0 xff, W5200_SPI_WRITE_OPCODE, 1 , data };
return spi_write_then_read(spi, cmd, sizeof (cmd), NULL, 0 );
}
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 & 0 xff, 0 , 2 };
__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 w5200_spi_write16(struct net_device *ndev, u32 addr, u16 data)
{
struct spi_device *spi = to_spi_device(ndev->dev.parent);
u8 cmd[6 ] = {
addr >> 8 , addr & 0 xff,
W5200_SPI_WRITE_OPCODE, 2 ,
data >> 8 , data & 0 xff
};
return spi_write_then_read(spi, cmd, sizeof (cmd), NULL, 0 );
}
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);
struct w5200_spi_priv *spi_priv = w5200_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 ] = len >> 8 ;
spi_priv->cmd_buf[3 ] = len;
ret = spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer));
mutex_unlock(&spi_priv->cmd_lock);
return ret;
}
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);
struct w5200_spi_priv *spi_priv = w5200_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 ] = W5200_SPI_WRITE_OPCODE | (len >> 8 );
spi_priv->cmd_buf[3 ] = len;
ret = spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer));
mutex_unlock(&spi_priv->cmd_lock);
return ret;
}
static const struct w5100_ops w5200_ops = {
.may_sleep = true ,
.chip_id = W5200,
.read = w5200_spi_read,
.write = w5200_spi_write,
.read16 = w5200_spi_read16,
.write16 = w5200_spi_write16,
.readbulk = w5200_spi_readbulk,
.writebulk = w5200_spi_writebulk,
.init = w5200_spi_init,
};
#define W5500_SPI_BLOCK_SELECT(addr) (((addr) >> 16 ) & 0 x1f)
#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 const struct of_device_id w5100_of_match[] = {
{ .compatible = "wiznet,w5100" , .data = (const void *)W5100, },
{ .compatible = "wiznet,w5200" , .data = (const void *)W5200, },
{ .compatible = "wiznet,w5500" , .data = (const void *)W5500, },
{ },
};
MODULE_DEVICE_TABLE(of, w5100_of_match);
static int w5100_spi_probe(struct spi_device *spi)
{
const struct w5100_ops *ops;
kernel_ulong_t driver_data;
const void *mac = NULL;
u8 tmpmac[ETH_ALEN];
int priv_size;
int ret;
ret = of_get_mac_address(spi->dev.of_node, tmpmac);
if (!ret)
mac = tmpmac;
driver_data = (uintptr_t)spi_get_device_match_data(spi);
switch (driver_data) {
case W5100:
ops = &w5100_spi_ops;
priv_size = 0 ;
break ;
case W5200:
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;
}
return w5100_probe(&spi->dev, ops, priv_size, mac, spi->irq, -EINVAL);
}
static void w5100_spi_remove(struct spi_device *spi)
{
w5100_remove(&spi->dev);
}
static const struct spi_device_id w5100_spi_ids[] = {
{ "w5100" , W5100 },
{ "w5200" , W5200 },
{ "w5500" , W5500 },
{}
};
MODULE_DEVICE_TABLE(spi, w5100_spi_ids);
static struct spi_driver w5100_spi_driver = {
.driver = {
.name = "w5100" ,
.pm = &w5100_pm_ops,
.of_match_table = w5100_of_match,
},
.probe = w5100_spi_probe,
.remove = w5100_spi_remove,
.id_table = w5100_spi_ids,
};
module_spi_driver(w5100_spi_driver);
MODULE_DESCRIPTION("WIZnet W5100/W5200/W5500 Ethernet driver for SPI mode" );
MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>" );
MODULE_LICENSE("GPL" );
Messung V0.5 in Prozent C=99 H=99 G=98
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-10-02)
¤
*© Formatika GbR, Deutschland