1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* DSA driver for: |
3 | * Vitesse VSC7385 SparX-G5 5+1-port Integrated Gigabit Ethernet Switch |
4 | * Vitesse VSC7388 SparX-G8 8-port Integrated Gigabit Ethernet Switch |
5 | * Vitesse VSC7395 SparX-G5e 5+1-port Integrated Gigabit Ethernet Switch |
6 | * Vitesse VSC7398 SparX-G8e 8-port Integrated Gigabit Ethernet Switch |
7 | * |
8 | * This driver takes control of the switch chip over SPI and |
9 | * configures it to route packages around when connected to a CPU port. |
10 | * |
11 | * Copyright (C) 2018 Linus Wallej <linus.walleij@linaro.org> |
12 | * Includes portions of code from the firmware uploader by: |
13 | * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org> |
14 | */ |
15 | #include <linux/kernel.h> |
16 | #include <linux/module.h> |
17 | #include <linux/of.h> |
18 | #include <linux/spi/spi.h> |
19 | |
20 | #include "vitesse-vsc73xx.h" |
21 | |
22 | #define VSC73XX_CMD_SPI_MODE_READ 0 |
23 | #define VSC73XX_CMD_SPI_MODE_WRITE 1 |
24 | #define VSC73XX_CMD_SPI_MODE_SHIFT 4 |
25 | #define VSC73XX_CMD_SPI_BLOCK_SHIFT 5 |
26 | #define VSC73XX_CMD_SPI_BLOCK_MASK 0x7 |
27 | #define VSC73XX_CMD_SPI_SUBBLOCK_MASK 0xf |
28 | |
29 | /* |
30 | * struct vsc73xx_spi - VSC73xx SPI state container |
31 | */ |
32 | struct vsc73xx_spi { |
33 | struct spi_device *spi; |
34 | struct mutex lock; /* Protects SPI traffic */ |
35 | struct vsc73xx vsc; |
36 | }; |
37 | |
38 | static const struct vsc73xx_ops vsc73xx_spi_ops; |
39 | |
40 | static u8 vsc73xx_make_addr(u8 mode, u8 block, u8 subblock) |
41 | { |
42 | u8 ret; |
43 | |
44 | ret = |
45 | (block & VSC73XX_CMD_SPI_BLOCK_MASK) << VSC73XX_CMD_SPI_BLOCK_SHIFT; |
46 | ret |= (mode & 1) << VSC73XX_CMD_SPI_MODE_SHIFT; |
47 | ret |= subblock & VSC73XX_CMD_SPI_SUBBLOCK_MASK; |
48 | |
49 | return ret; |
50 | } |
51 | |
52 | static int vsc73xx_spi_read(struct vsc73xx *vsc, u8 block, u8 subblock, u8 reg, |
53 | u32 *val) |
54 | { |
55 | struct vsc73xx_spi *vsc_spi = vsc->priv; |
56 | struct spi_transfer t[2]; |
57 | struct spi_message m; |
58 | u8 cmd[4]; |
59 | u8 buf[4]; |
60 | int ret; |
61 | |
62 | if (!vsc73xx_is_addr_valid(block, subblock)) |
63 | return -EINVAL; |
64 | |
65 | spi_message_init(m: &m); |
66 | |
67 | memset(&t, 0, sizeof(t)); |
68 | |
69 | t[0].tx_buf = cmd; |
70 | t[0].len = sizeof(cmd); |
71 | spi_message_add_tail(t: &t[0], m: &m); |
72 | |
73 | t[1].rx_buf = buf; |
74 | t[1].len = sizeof(buf); |
75 | spi_message_add_tail(t: &t[1], m: &m); |
76 | |
77 | cmd[0] = vsc73xx_make_addr(VSC73XX_CMD_SPI_MODE_READ, block, subblock); |
78 | cmd[1] = reg; |
79 | cmd[2] = 0; |
80 | cmd[3] = 0; |
81 | |
82 | mutex_lock(&vsc_spi->lock); |
83 | ret = spi_sync(spi: vsc_spi->spi, message: &m); |
84 | mutex_unlock(lock: &vsc_spi->lock); |
85 | |
86 | if (ret) |
87 | return ret; |
88 | |
89 | *val = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; |
90 | |
91 | return 0; |
92 | } |
93 | |
94 | static int vsc73xx_spi_write(struct vsc73xx *vsc, u8 block, u8 subblock, u8 reg, |
95 | u32 val) |
96 | { |
97 | struct vsc73xx_spi *vsc_spi = vsc->priv; |
98 | struct spi_transfer t[2]; |
99 | struct spi_message m; |
100 | u8 cmd[2]; |
101 | u8 buf[4]; |
102 | int ret; |
103 | |
104 | if (!vsc73xx_is_addr_valid(block, subblock)) |
105 | return -EINVAL; |
106 | |
107 | spi_message_init(m: &m); |
108 | |
109 | memset(&t, 0, sizeof(t)); |
110 | |
111 | t[0].tx_buf = cmd; |
112 | t[0].len = sizeof(cmd); |
113 | spi_message_add_tail(t: &t[0], m: &m); |
114 | |
115 | t[1].tx_buf = buf; |
116 | t[1].len = sizeof(buf); |
117 | spi_message_add_tail(t: &t[1], m: &m); |
118 | |
119 | cmd[0] = vsc73xx_make_addr(VSC73XX_CMD_SPI_MODE_WRITE, block, subblock); |
120 | cmd[1] = reg; |
121 | |
122 | buf[0] = (val >> 24) & 0xff; |
123 | buf[1] = (val >> 16) & 0xff; |
124 | buf[2] = (val >> 8) & 0xff; |
125 | buf[3] = val & 0xff; |
126 | |
127 | mutex_lock(&vsc_spi->lock); |
128 | ret = spi_sync(spi: vsc_spi->spi, message: &m); |
129 | mutex_unlock(lock: &vsc_spi->lock); |
130 | |
131 | return ret; |
132 | } |
133 | |
134 | static int vsc73xx_spi_probe(struct spi_device *spi) |
135 | { |
136 | struct device *dev = &spi->dev; |
137 | struct vsc73xx_spi *vsc_spi; |
138 | int ret; |
139 | |
140 | vsc_spi = devm_kzalloc(dev, size: sizeof(*vsc_spi), GFP_KERNEL); |
141 | if (!vsc_spi) |
142 | return -ENOMEM; |
143 | |
144 | spi_set_drvdata(spi, data: vsc_spi); |
145 | vsc_spi->spi = spi_dev_get(spi); |
146 | vsc_spi->vsc.dev = dev; |
147 | vsc_spi->vsc.priv = vsc_spi; |
148 | vsc_spi->vsc.ops = &vsc73xx_spi_ops; |
149 | mutex_init(&vsc_spi->lock); |
150 | |
151 | spi->mode = SPI_MODE_0; |
152 | spi->bits_per_word = 8; |
153 | ret = spi_setup(spi); |
154 | if (ret < 0) { |
155 | dev_err(dev, "spi setup failed.\n" ); |
156 | return ret; |
157 | } |
158 | |
159 | return vsc73xx_probe(vsc: &vsc_spi->vsc); |
160 | } |
161 | |
162 | static void vsc73xx_spi_remove(struct spi_device *spi) |
163 | { |
164 | struct vsc73xx_spi *vsc_spi = spi_get_drvdata(spi); |
165 | |
166 | if (!vsc_spi) |
167 | return; |
168 | |
169 | vsc73xx_remove(vsc: &vsc_spi->vsc); |
170 | } |
171 | |
172 | static void vsc73xx_spi_shutdown(struct spi_device *spi) |
173 | { |
174 | struct vsc73xx_spi *vsc_spi = spi_get_drvdata(spi); |
175 | |
176 | if (!vsc_spi) |
177 | return; |
178 | |
179 | vsc73xx_shutdown(vsc: &vsc_spi->vsc); |
180 | |
181 | spi_set_drvdata(spi, NULL); |
182 | } |
183 | |
184 | static const struct vsc73xx_ops vsc73xx_spi_ops = { |
185 | .read = vsc73xx_spi_read, |
186 | .write = vsc73xx_spi_write, |
187 | }; |
188 | |
189 | static const struct of_device_id vsc73xx_of_match[] = { |
190 | { |
191 | .compatible = "vitesse,vsc7385" , |
192 | }, |
193 | { |
194 | .compatible = "vitesse,vsc7388" , |
195 | }, |
196 | { |
197 | .compatible = "vitesse,vsc7395" , |
198 | }, |
199 | { |
200 | .compatible = "vitesse,vsc7398" , |
201 | }, |
202 | { }, |
203 | }; |
204 | MODULE_DEVICE_TABLE(of, vsc73xx_of_match); |
205 | |
206 | static const struct spi_device_id vsc73xx_spi_ids[] = { |
207 | { "vsc7385" }, |
208 | { "vsc7388" }, |
209 | { "vsc7395" }, |
210 | { "vsc7398" }, |
211 | { }, |
212 | }; |
213 | MODULE_DEVICE_TABLE(spi, vsc73xx_spi_ids); |
214 | |
215 | static struct spi_driver vsc73xx_spi_driver = { |
216 | .probe = vsc73xx_spi_probe, |
217 | .remove = vsc73xx_spi_remove, |
218 | .shutdown = vsc73xx_spi_shutdown, |
219 | .id_table = vsc73xx_spi_ids, |
220 | .driver = { |
221 | .name = "vsc73xx-spi" , |
222 | .of_match_table = vsc73xx_of_match, |
223 | }, |
224 | }; |
225 | module_spi_driver(vsc73xx_spi_driver); |
226 | |
227 | MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>" ); |
228 | MODULE_DESCRIPTION("Vitesse VSC7385/7388/7395/7398 SPI driver" ); |
229 | MODULE_LICENSE("GPL v2" ); |
230 | |