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