blob: 598a7b00fdb9c75ea42b8116e05a44877f51e144 [file] [log] [blame]
Akinobu Mita630cf092016-04-15 00:11:32 +09001/*
Akinobu Mita0c165ff2016-04-15 00:11:33 +09002 * Ethernet driver for the WIZnet W5100/W5200 chip.
Akinobu Mita630cf092016-04-15 00:11:32 +09003 *
4 * Copyright (C) 2016 Akinobu Mita <akinobu.mita@gmail.com>
5 *
6 * Licensed under the GPL-2 or later.
Akinobu Mita0c165ff2016-04-15 00:11:33 +09007 *
8 * Datasheet:
9 * http://www.wiznet.co.kr/wp-content/uploads/wiznethome/Chip/W5100/Document/W5100_Datasheet_v1.2.6.pdf
10 * http://wiznethome.cafe24.com/wp-content/uploads/wiznethome/Chip/W5200/Documents/W5200_DS_V140E.pdf
Akinobu Mita630cf092016-04-15 00:11:32 +090011 */
12
13#include <linux/kernel.h>
14#include <linux/module.h>
15#include <linux/delay.h>
16#include <linux/netdevice.h>
17#include <linux/spi/spi.h>
18
19#include "w5100.h"
20
21#define W5100_SPI_WRITE_OPCODE 0xf0
22#define W5100_SPI_READ_OPCODE 0x0f
23
24static int w5100_spi_read(struct net_device *ndev, u16 addr)
25{
26 struct spi_device *spi = to_spi_device(ndev->dev.parent);
27 u8 cmd[3] = { W5100_SPI_READ_OPCODE, addr >> 8, addr & 0xff };
28 u8 data;
29 int ret;
30
31 ret = spi_write_then_read(spi, cmd, sizeof(cmd), &data, 1);
32
33 return ret ? ret : data;
34}
35
36static int w5100_spi_write(struct net_device *ndev, u16 addr, u8 data)
37{
38 struct spi_device *spi = to_spi_device(ndev->dev.parent);
39 u8 cmd[4] = { W5100_SPI_WRITE_OPCODE, addr >> 8, addr & 0xff, data};
40
41 return spi_write_then_read(spi, cmd, sizeof(cmd), NULL, 0);
42}
43
44static int w5100_spi_read16(struct net_device *ndev, u16 addr)
45{
46 u16 data;
47 int ret;
48
49 ret = w5100_spi_read(ndev, addr);
50 if (ret < 0)
51 return ret;
52 data = ret << 8;
53 ret = w5100_spi_read(ndev, addr + 1);
54
55 return ret < 0 ? ret : data | ret;
56}
57
58static int w5100_spi_write16(struct net_device *ndev, u16 addr, u16 data)
59{
60 int ret;
61
62 ret = w5100_spi_write(ndev, addr, data >> 8);
63 if (ret)
64 return ret;
65
66 return w5100_spi_write(ndev, addr + 1, data & 0xff);
67}
68
69static int w5100_spi_readbulk(struct net_device *ndev, u16 addr, u8 *buf,
70 int len)
71{
72 int i;
73
74 for (i = 0; i < len; i++) {
75 int ret = w5100_spi_read(ndev, addr + i);
76
77 if (ret < 0)
78 return ret;
79 buf[i] = ret;
80 }
81
82 return 0;
83}
84
85static int w5100_spi_writebulk(struct net_device *ndev, u16 addr, const u8 *buf,
86 int len)
87{
88 int i;
89
90 for (i = 0; i < len; i++) {
91 int ret = w5100_spi_write(ndev, addr + i, buf[i]);
92
93 if (ret)
94 return ret;
95 }
96
97 return 0;
98}
99
100static const struct w5100_ops w5100_spi_ops = {
101 .may_sleep = true,
Akinobu Mita0c165ff2016-04-15 00:11:33 +0900102 .chip_id = W5100,
Akinobu Mita630cf092016-04-15 00:11:32 +0900103 .read = w5100_spi_read,
104 .write = w5100_spi_write,
105 .read16 = w5100_spi_read16,
106 .write16 = w5100_spi_write16,
107 .readbulk = w5100_spi_readbulk,
108 .writebulk = w5100_spi_writebulk,
109};
110
Akinobu Mita0c165ff2016-04-15 00:11:33 +0900111#define W5200_SPI_WRITE_OPCODE 0x80
112
113struct w5200_spi_priv {
114 /* Serialize access to cmd_buf */
115 struct mutex cmd_lock;
116
117 /* DMA (thus cache coherency maintenance) requires the
118 * transfer buffers to live in their own cache lines.
119 */
120 u8 cmd_buf[4] ____cacheline_aligned;
121};
122
123static struct w5200_spi_priv *w5200_spi_priv(struct net_device *ndev)
124{
125 return w5100_ops_priv(ndev);
126}
127
128static int w5200_spi_init(struct net_device *ndev)
129{
130 struct w5200_spi_priv *spi_priv = w5200_spi_priv(ndev);
131
132 mutex_init(&spi_priv->cmd_lock);
133
134 return 0;
135}
136
137static int w5200_spi_read(struct net_device *ndev, u16 addr)
138{
139 struct spi_device *spi = to_spi_device(ndev->dev.parent);
140 u8 cmd[4] = { addr >> 8, addr & 0xff, 0, 1 };
141 u8 data;
142 int ret;
143
144 ret = spi_write_then_read(spi, cmd, sizeof(cmd), &data, 1);
145
146 return ret ? ret : data;
147}
148
149static int w5200_spi_write(struct net_device *ndev, u16 addr, u8 data)
150{
151 struct spi_device *spi = to_spi_device(ndev->dev.parent);
152 u8 cmd[5] = { addr >> 8, addr & 0xff, W5200_SPI_WRITE_OPCODE, 1, data };
153
154 return spi_write_then_read(spi, cmd, sizeof(cmd), NULL, 0);
155}
156
157static int w5200_spi_read16(struct net_device *ndev, u16 addr)
158{
159 struct spi_device *spi = to_spi_device(ndev->dev.parent);
160 u8 cmd[4] = { addr >> 8, addr & 0xff, 0, 2 };
161 __be16 data;
162 int ret;
163
164 ret = spi_write_then_read(spi, cmd, sizeof(cmd), &data, sizeof(data));
165
166 return ret ? ret : be16_to_cpu(data);
167}
168
169static int w5200_spi_write16(struct net_device *ndev, u16 addr, u16 data)
170{
171 struct spi_device *spi = to_spi_device(ndev->dev.parent);
172 u8 cmd[6] = {
173 addr >> 8, addr & 0xff,
174 W5200_SPI_WRITE_OPCODE, 2,
175 data >> 8, data & 0xff
176 };
177
178 return spi_write_then_read(spi, cmd, sizeof(cmd), NULL, 0);
179}
180
181static int w5200_spi_readbulk(struct net_device *ndev, u16 addr, u8 *buf,
182 int len)
183{
184 struct spi_device *spi = to_spi_device(ndev->dev.parent);
185 struct w5200_spi_priv *spi_priv = w5200_spi_priv(ndev);
186 struct spi_transfer xfer[] = {
187 {
188 .tx_buf = spi_priv->cmd_buf,
189 .len = sizeof(spi_priv->cmd_buf),
190 },
191 {
192 .rx_buf = buf,
193 .len = len,
194 },
195 };
196 int ret;
197
198 mutex_lock(&spi_priv->cmd_lock);
199
200 spi_priv->cmd_buf[0] = addr >> 8;
201 spi_priv->cmd_buf[1] = addr;
202 spi_priv->cmd_buf[2] = len >> 8;
203 spi_priv->cmd_buf[3] = len;
204 ret = spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer));
205
206 mutex_unlock(&spi_priv->cmd_lock);
207
208 return ret;
209}
210
211static int w5200_spi_writebulk(struct net_device *ndev, u16 addr, const u8 *buf,
212 int len)
213{
214 struct spi_device *spi = to_spi_device(ndev->dev.parent);
215 struct w5200_spi_priv *spi_priv = w5200_spi_priv(ndev);
216 struct spi_transfer xfer[] = {
217 {
218 .tx_buf = spi_priv->cmd_buf,
219 .len = sizeof(spi_priv->cmd_buf),
220 },
221 {
222 .tx_buf = buf,
223 .len = len,
224 },
225 };
226 int ret;
227
228 mutex_lock(&spi_priv->cmd_lock);
229
230 spi_priv->cmd_buf[0] = addr >> 8;
231 spi_priv->cmd_buf[1] = addr;
232 spi_priv->cmd_buf[2] = W5200_SPI_WRITE_OPCODE | (len >> 8);
233 spi_priv->cmd_buf[3] = len;
234 ret = spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer));
235
236 mutex_unlock(&spi_priv->cmd_lock);
237
238 return ret;
239}
240
241static const struct w5100_ops w5200_ops = {
242 .may_sleep = true,
243 .chip_id = W5200,
244 .read = w5200_spi_read,
245 .write = w5200_spi_write,
246 .read16 = w5200_spi_read16,
247 .write16 = w5200_spi_write16,
248 .readbulk = w5200_spi_readbulk,
249 .writebulk = w5200_spi_writebulk,
250 .init = w5200_spi_init,
251};
252
Akinobu Mita630cf092016-04-15 00:11:32 +0900253static int w5100_spi_probe(struct spi_device *spi)
254{
Akinobu Mita0c165ff2016-04-15 00:11:33 +0900255 const struct spi_device_id *id = spi_get_device_id(spi);
256 const struct w5100_ops *ops;
257 int priv_size;
258
259 switch (id->driver_data) {
260 case W5100:
261 ops = &w5100_spi_ops;
262 priv_size = 0;
263 break;
264 case W5200:
265 ops = &w5200_ops;
266 priv_size = sizeof(struct w5200_spi_priv);
267 break;
268 default:
269 return -EINVAL;
270 }
271
272 return w5100_probe(&spi->dev, ops, priv_size, NULL, spi->irq, -EINVAL);
Akinobu Mita630cf092016-04-15 00:11:32 +0900273}
274
275static int w5100_spi_remove(struct spi_device *spi)
276{
277 return w5100_remove(&spi->dev);
278}
279
280static const struct spi_device_id w5100_spi_ids[] = {
Akinobu Mita0c165ff2016-04-15 00:11:33 +0900281 { "w5100", W5100 },
282 { "w5200", W5200 },
Akinobu Mita630cf092016-04-15 00:11:32 +0900283 {}
284};
285MODULE_DEVICE_TABLE(spi, w5100_spi_ids);
286
287static struct spi_driver w5100_spi_driver = {
288 .driver = {
289 .name = "w5100",
290 .pm = &w5100_pm_ops,
291 },
292 .probe = w5100_spi_probe,
293 .remove = w5100_spi_remove,
294 .id_table = w5100_spi_ids,
295};
296module_spi_driver(w5100_spi_driver);
297
Akinobu Mita0c165ff2016-04-15 00:11:33 +0900298MODULE_DESCRIPTION("WIZnet W5100/W5200 Ethernet driver for SPI mode");
Akinobu Mita630cf092016-04-15 00:11:32 +0900299MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>");
300MODULE_LICENSE("GPL");