GNU Linux-libre 4.14.266-gnu1
[releases.git] / drivers / net / dsa / microchip / ksz_spi.c
1 /*
2  * Microchip KSZ series register access through SPI
3  *
4  * Copyright (C) 2017
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 #include <asm/unaligned.h>
20
21 #include <linux/delay.h>
22 #include <linux/kernel.h>
23 #include <linux/module.h>
24 #include <linux/spi/spi.h>
25
26 #include "ksz_priv.h"
27
28 /* SPI frame opcodes */
29 #define KS_SPIOP_RD                     3
30 #define KS_SPIOP_WR                     2
31
32 #define SPI_ADDR_SHIFT                  24
33 #define SPI_ADDR_MASK                   (BIT(SPI_ADDR_SHIFT) - 1)
34 #define SPI_TURNAROUND_SHIFT            5
35
36 static int ksz_spi_read_reg(struct spi_device *spi, u32 reg, u8 *val,
37                             unsigned int len)
38 {
39         u32 txbuf;
40         int ret;
41
42         txbuf = reg & SPI_ADDR_MASK;
43         txbuf |= KS_SPIOP_RD << SPI_ADDR_SHIFT;
44         txbuf <<= SPI_TURNAROUND_SHIFT;
45         txbuf = cpu_to_be32(txbuf);
46
47         ret = spi_write_then_read(spi, &txbuf, 4, val, len);
48         return ret;
49 }
50
51 static int ksz_spi_read(struct ksz_device *dev, u32 reg, u8 *data,
52                         unsigned int len)
53 {
54         struct spi_device *spi = dev->priv;
55
56         return ksz_spi_read_reg(spi, reg, data, len);
57 }
58
59 static int ksz_spi_read8(struct ksz_device *dev, u32 reg, u8 *val)
60 {
61         return ksz_spi_read(dev, reg, val, 1);
62 }
63
64 static int ksz_spi_read16(struct ksz_device *dev, u32 reg, u16 *val)
65 {
66         int ret = ksz_spi_read(dev, reg, (u8 *)val, 2);
67
68         if (!ret)
69                 *val = be16_to_cpu(*val);
70
71         return ret;
72 }
73
74 static int ksz_spi_read24(struct ksz_device *dev, u32 reg, u32 *val)
75 {
76         int ret;
77
78         *val = 0;
79         ret = ksz_spi_read(dev, reg, (u8 *)val, 3);
80         if (!ret) {
81                 *val = be32_to_cpu(*val);
82                 /* convert to 24bit */
83                 *val >>= 8;
84         }
85
86         return ret;
87 }
88
89 static int ksz_spi_read32(struct ksz_device *dev, u32 reg, u32 *val)
90 {
91         int ret = ksz_spi_read(dev, reg, (u8 *)val, 4);
92
93         if (!ret)
94                 *val = be32_to_cpu(*val);
95
96         return ret;
97 }
98
99 static int ksz_spi_write_reg(struct spi_device *spi, u32 reg, u8 *val,
100                              unsigned int len)
101 {
102         u32 txbuf;
103         u8 data[12];
104         int i;
105
106         txbuf = reg & SPI_ADDR_MASK;
107         txbuf |= (KS_SPIOP_WR << SPI_ADDR_SHIFT);
108         txbuf <<= SPI_TURNAROUND_SHIFT;
109         txbuf = cpu_to_be32(txbuf);
110
111         data[0] = txbuf & 0xFF;
112         data[1] = (txbuf & 0xFF00) >> 8;
113         data[2] = (txbuf & 0xFF0000) >> 16;
114         data[3] = (txbuf & 0xFF000000) >> 24;
115         for (i = 0; i < len; i++)
116                 data[i + 4] = val[i];
117
118         return spi_write(spi, &data, 4 + len);
119 }
120
121 static int ksz_spi_write8(struct ksz_device *dev, u32 reg, u8 value)
122 {
123         struct spi_device *spi = dev->priv;
124
125         return ksz_spi_write_reg(spi, reg, &value, 1);
126 }
127
128 static int ksz_spi_write16(struct ksz_device *dev, u32 reg, u16 value)
129 {
130         struct spi_device *spi = dev->priv;
131
132         value = cpu_to_be16(value);
133         return ksz_spi_write_reg(spi, reg, (u8 *)&value, 2);
134 }
135
136 static int ksz_spi_write24(struct ksz_device *dev, u32 reg, u32 value)
137 {
138         struct spi_device *spi = dev->priv;
139
140         /* make it to big endian 24bit from MSB */
141         value <<= 8;
142         value = cpu_to_be32(value);
143         return ksz_spi_write_reg(spi, reg, (u8 *)&value, 3);
144 }
145
146 static int ksz_spi_write32(struct ksz_device *dev, u32 reg, u32 value)
147 {
148         struct spi_device *spi = dev->priv;
149
150         value = cpu_to_be32(value);
151         return ksz_spi_write_reg(spi, reg, (u8 *)&value, 4);
152 }
153
154 static const struct ksz_io_ops ksz_spi_ops = {
155         .read8 = ksz_spi_read8,
156         .read16 = ksz_spi_read16,
157         .read24 = ksz_spi_read24,
158         .read32 = ksz_spi_read32,
159         .write8 = ksz_spi_write8,
160         .write16 = ksz_spi_write16,
161         .write24 = ksz_spi_write24,
162         .write32 = ksz_spi_write32,
163 };
164
165 static int ksz_spi_probe(struct spi_device *spi)
166 {
167         struct ksz_device *dev;
168         int ret;
169
170         dev = ksz_switch_alloc(&spi->dev, &ksz_spi_ops, spi);
171         if (!dev)
172                 return -ENOMEM;
173
174         if (spi->dev.platform_data)
175                 dev->pdata = spi->dev.platform_data;
176
177         ret = ksz_switch_register(dev);
178         if (ret)
179                 return ret;
180
181         spi_set_drvdata(spi, dev);
182
183         return 0;
184 }
185
186 static int ksz_spi_remove(struct spi_device *spi)
187 {
188         struct ksz_device *dev = spi_get_drvdata(spi);
189
190         if (dev)
191                 ksz_switch_remove(dev);
192
193         return 0;
194 }
195
196 static const struct of_device_id ksz_dt_ids[] = {
197         { .compatible = "microchip,ksz9477" },
198         {},
199 };
200 MODULE_DEVICE_TABLE(of, ksz_dt_ids);
201
202 static struct spi_driver ksz_spi_driver = {
203         .driver = {
204                 .name   = "ksz9477-switch",
205                 .owner  = THIS_MODULE,
206                 .of_match_table = of_match_ptr(ksz_dt_ids),
207         },
208         .probe  = ksz_spi_probe,
209         .remove = ksz_spi_remove,
210 };
211
212 module_spi_driver(ksz_spi_driver);
213
214 MODULE_AUTHOR("Woojung Huh <Woojung.Huh@microchip.com>");
215 MODULE_DESCRIPTION("Microchip KSZ Series Switch SPI access Driver");
216 MODULE_LICENSE("GPL");