1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) |
2 | /* |
3 | * Core driver for the Ocelot chip family. |
4 | * |
5 | * The VSC7511, 7512, 7513, and 7514 can be controlled internally via an |
6 | * on-chip MIPS processor, or externally via SPI, I2C, PCIe. This core driver is |
7 | * intended to be the bus-agnostic glue between, for example, the SPI bus and |
8 | * the child devices. |
9 | * |
10 | * Copyright 2021-2022 Innovative Advantage Inc. |
11 | * |
12 | * Author: Colin Foster <colin.foster@in-advantage.com> |
13 | */ |
14 | |
15 | #include <linux/bits.h> |
16 | #include <linux/device.h> |
17 | #include <linux/export.h> |
18 | #include <linux/iopoll.h> |
19 | #include <linux/ioport.h> |
20 | #include <linux/kernel.h> |
21 | #include <linux/mfd/core.h> |
22 | #include <linux/mfd/ocelot.h> |
23 | #include <linux/module.h> |
24 | #include <linux/regmap.h> |
25 | #include <linux/types.h> |
26 | |
27 | #include <soc/mscc/ocelot.h> |
28 | |
29 | #include "ocelot.h" |
30 | |
31 | #define REG_GCB_SOFT_RST 0x0008 |
32 | |
33 | #define BIT_SOFT_CHIP_RST BIT(0) |
34 | |
35 | #define VSC7512_MIIM0_RES_START 0x7107009c |
36 | #define VSC7512_MIIM1_RES_START 0x710700c0 |
37 | #define VSC7512_MIIM_RES_SIZE 0x00000024 |
38 | |
39 | #define VSC7512_PHY_RES_START 0x710700f0 |
40 | #define VSC7512_PHY_RES_SIZE 0x00000004 |
41 | |
42 | #define VSC7512_GPIO_RES_START 0x71070034 |
43 | #define VSC7512_GPIO_RES_SIZE 0x0000006c |
44 | |
45 | #define VSC7512_SIO_CTRL_RES_START 0x710700f8 |
46 | #define VSC7512_SIO_CTRL_RES_SIZE 0x00000100 |
47 | |
48 | #define VSC7512_HSIO_RES_START 0x710d0000 |
49 | #define VSC7512_HSIO_RES_SIZE 0x00000128 |
50 | |
51 | #define VSC7512_ANA_RES_START 0x71880000 |
52 | #define VSC7512_ANA_RES_SIZE 0x00010000 |
53 | |
54 | #define VSC7512_QS_RES_START 0x71080000 |
55 | #define VSC7512_QS_RES_SIZE 0x00000100 |
56 | |
57 | #define VSC7512_QSYS_RES_START 0x71800000 |
58 | #define VSC7512_QSYS_RES_SIZE 0x00200000 |
59 | |
60 | #define VSC7512_REW_RES_START 0x71030000 |
61 | #define VSC7512_REW_RES_SIZE 0x00010000 |
62 | |
63 | #define VSC7512_SYS_RES_START 0x71010000 |
64 | #define VSC7512_SYS_RES_SIZE 0x00010000 |
65 | |
66 | #define VSC7512_S0_RES_START 0x71040000 |
67 | #define VSC7512_S1_RES_START 0x71050000 |
68 | #define VSC7512_S2_RES_START 0x71060000 |
69 | #define VCAP_RES_SIZE 0x00000400 |
70 | |
71 | #define VSC7512_PORT_0_RES_START 0x711e0000 |
72 | #define VSC7512_PORT_1_RES_START 0x711f0000 |
73 | #define VSC7512_PORT_2_RES_START 0x71200000 |
74 | #define VSC7512_PORT_3_RES_START 0x71210000 |
75 | #define VSC7512_PORT_4_RES_START 0x71220000 |
76 | #define VSC7512_PORT_5_RES_START 0x71230000 |
77 | #define VSC7512_PORT_6_RES_START 0x71240000 |
78 | #define VSC7512_PORT_7_RES_START 0x71250000 |
79 | #define VSC7512_PORT_8_RES_START 0x71260000 |
80 | #define VSC7512_PORT_9_RES_START 0x71270000 |
81 | #define VSC7512_PORT_10_RES_START 0x71280000 |
82 | #define VSC7512_PORT_RES_SIZE 0x00010000 |
83 | |
84 | #define VSC7512_GCB_RST_SLEEP_US 100 |
85 | #define VSC7512_GCB_RST_TIMEOUT_US 100000 |
86 | |
87 | static int ocelot_gcb_chip_rst_status(struct ocelot_ddata *ddata) |
88 | { |
89 | int val, err; |
90 | |
91 | err = regmap_read(map: ddata->gcb_regmap, REG_GCB_SOFT_RST, val: &val); |
92 | if (err) |
93 | return err; |
94 | |
95 | return val; |
96 | } |
97 | |
98 | int ocelot_chip_reset(struct device *dev) |
99 | { |
100 | struct ocelot_ddata *ddata = dev_get_drvdata(dev); |
101 | int ret, val; |
102 | |
103 | /* |
104 | * Reset the entire chip here to put it into a completely known state. |
105 | * Other drivers may want to reset their own subsystems. The register |
106 | * self-clears, so one write is all that is needed and wait for it to |
107 | * clear. |
108 | */ |
109 | ret = regmap_write(map: ddata->gcb_regmap, REG_GCB_SOFT_RST, BIT_SOFT_CHIP_RST); |
110 | if (ret) |
111 | return ret; |
112 | |
113 | return readx_poll_timeout(ocelot_gcb_chip_rst_status, ddata, val, !val, |
114 | VSC7512_GCB_RST_SLEEP_US, VSC7512_GCB_RST_TIMEOUT_US); |
115 | } |
116 | EXPORT_SYMBOL_NS(ocelot_chip_reset, MFD_OCELOT); |
117 | |
118 | static const struct resource vsc7512_miim0_resources[] = { |
119 | DEFINE_RES_REG_NAMED(VSC7512_MIIM0_RES_START, VSC7512_MIIM_RES_SIZE, "gcb_miim0" ), |
120 | DEFINE_RES_REG_NAMED(VSC7512_PHY_RES_START, VSC7512_PHY_RES_SIZE, "gcb_phy" ), |
121 | }; |
122 | |
123 | static const struct resource vsc7512_miim1_resources[] = { |
124 | DEFINE_RES_REG_NAMED(VSC7512_MIIM1_RES_START, VSC7512_MIIM_RES_SIZE, "gcb_miim1" ), |
125 | }; |
126 | |
127 | static const struct resource vsc7512_pinctrl_resources[] = { |
128 | DEFINE_RES_REG_NAMED(VSC7512_GPIO_RES_START, VSC7512_GPIO_RES_SIZE, "gcb_gpio" ), |
129 | }; |
130 | |
131 | static const struct resource vsc7512_sgpio_resources[] = { |
132 | DEFINE_RES_REG_NAMED(VSC7512_SIO_CTRL_RES_START, VSC7512_SIO_CTRL_RES_SIZE, "gcb_sio" ), |
133 | }; |
134 | |
135 | static const struct resource vsc7512_serdes_resources[] = { |
136 | DEFINE_RES_REG_NAMED(VSC7512_HSIO_RES_START, VSC7512_HSIO_RES_SIZE, "hsio" ), |
137 | }; |
138 | |
139 | static const struct resource vsc7512_switch_resources[] = { |
140 | DEFINE_RES_REG_NAMED(VSC7512_ANA_RES_START, VSC7512_ANA_RES_SIZE, "ana" ), |
141 | DEFINE_RES_REG_NAMED(VSC7512_HSIO_RES_START, VSC7512_HSIO_RES_SIZE, "hsio" ), |
142 | DEFINE_RES_REG_NAMED(VSC7512_QS_RES_START, VSC7512_QS_RES_SIZE, "qs" ), |
143 | DEFINE_RES_REG_NAMED(VSC7512_QSYS_RES_START, VSC7512_QSYS_RES_SIZE, "qsys" ), |
144 | DEFINE_RES_REG_NAMED(VSC7512_REW_RES_START, VSC7512_REW_RES_SIZE, "rew" ), |
145 | DEFINE_RES_REG_NAMED(VSC7512_SYS_RES_START, VSC7512_SYS_RES_SIZE, "sys" ), |
146 | DEFINE_RES_REG_NAMED(VSC7512_S0_RES_START, VCAP_RES_SIZE, "s0" ), |
147 | DEFINE_RES_REG_NAMED(VSC7512_S1_RES_START, VCAP_RES_SIZE, "s1" ), |
148 | DEFINE_RES_REG_NAMED(VSC7512_S2_RES_START, VCAP_RES_SIZE, "s2" ), |
149 | DEFINE_RES_REG_NAMED(VSC7512_PORT_0_RES_START, VSC7512_PORT_RES_SIZE, "port0" ), |
150 | DEFINE_RES_REG_NAMED(VSC7512_PORT_1_RES_START, VSC7512_PORT_RES_SIZE, "port1" ), |
151 | DEFINE_RES_REG_NAMED(VSC7512_PORT_2_RES_START, VSC7512_PORT_RES_SIZE, "port2" ), |
152 | DEFINE_RES_REG_NAMED(VSC7512_PORT_3_RES_START, VSC7512_PORT_RES_SIZE, "port3" ), |
153 | DEFINE_RES_REG_NAMED(VSC7512_PORT_4_RES_START, VSC7512_PORT_RES_SIZE, "port4" ), |
154 | DEFINE_RES_REG_NAMED(VSC7512_PORT_5_RES_START, VSC7512_PORT_RES_SIZE, "port5" ), |
155 | DEFINE_RES_REG_NAMED(VSC7512_PORT_6_RES_START, VSC7512_PORT_RES_SIZE, "port6" ), |
156 | DEFINE_RES_REG_NAMED(VSC7512_PORT_7_RES_START, VSC7512_PORT_RES_SIZE, "port7" ), |
157 | DEFINE_RES_REG_NAMED(VSC7512_PORT_8_RES_START, VSC7512_PORT_RES_SIZE, "port8" ), |
158 | DEFINE_RES_REG_NAMED(VSC7512_PORT_9_RES_START, VSC7512_PORT_RES_SIZE, "port9" ), |
159 | DEFINE_RES_REG_NAMED(VSC7512_PORT_10_RES_START, VSC7512_PORT_RES_SIZE, "port10" ) |
160 | }; |
161 | |
162 | static const struct mfd_cell vsc7512_devs[] = { |
163 | { |
164 | .name = "ocelot-pinctrl" , |
165 | .of_compatible = "mscc,ocelot-pinctrl" , |
166 | .num_resources = ARRAY_SIZE(vsc7512_pinctrl_resources), |
167 | .resources = vsc7512_pinctrl_resources, |
168 | }, { |
169 | .name = "ocelot-sgpio" , |
170 | .of_compatible = "mscc,ocelot-sgpio" , |
171 | .num_resources = ARRAY_SIZE(vsc7512_sgpio_resources), |
172 | .resources = vsc7512_sgpio_resources, |
173 | }, { |
174 | .name = "ocelot-miim0" , |
175 | .of_compatible = "mscc,ocelot-miim" , |
176 | .of_reg = VSC7512_MIIM0_RES_START, |
177 | .use_of_reg = true, |
178 | .num_resources = ARRAY_SIZE(vsc7512_miim0_resources), |
179 | .resources = vsc7512_miim0_resources, |
180 | }, { |
181 | .name = "ocelot-miim1" , |
182 | .of_compatible = "mscc,ocelot-miim" , |
183 | .of_reg = VSC7512_MIIM1_RES_START, |
184 | .use_of_reg = true, |
185 | .num_resources = ARRAY_SIZE(vsc7512_miim1_resources), |
186 | .resources = vsc7512_miim1_resources, |
187 | }, { |
188 | .name = "ocelot-serdes" , |
189 | .of_compatible = "mscc,vsc7514-serdes" , |
190 | .num_resources = ARRAY_SIZE(vsc7512_serdes_resources), |
191 | .resources = vsc7512_serdes_resources, |
192 | }, { |
193 | .name = "ocelot-ext-switch" , |
194 | .of_compatible = "mscc,vsc7512-switch" , |
195 | .num_resources = ARRAY_SIZE(vsc7512_switch_resources), |
196 | .resources = vsc7512_switch_resources, |
197 | }, |
198 | }; |
199 | |
200 | static void ocelot_core_try_add_regmap(struct device *dev, |
201 | const struct resource *res) |
202 | { |
203 | if (dev_get_regmap(dev, name: res->name)) |
204 | return; |
205 | |
206 | ocelot_spi_init_regmap(dev, res); |
207 | } |
208 | |
209 | static void ocelot_core_try_add_regmaps(struct device *dev, |
210 | const struct mfd_cell *cell) |
211 | { |
212 | int i; |
213 | |
214 | for (i = 0; i < cell->num_resources; i++) |
215 | ocelot_core_try_add_regmap(dev, res: &cell->resources[i]); |
216 | } |
217 | |
218 | int ocelot_core_init(struct device *dev) |
219 | { |
220 | int i, ndevs; |
221 | |
222 | ndevs = ARRAY_SIZE(vsc7512_devs); |
223 | |
224 | for (i = 0; i < ndevs; i++) |
225 | ocelot_core_try_add_regmaps(dev, cell: &vsc7512_devs[i]); |
226 | |
227 | return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, cells: vsc7512_devs, n_devs: ndevs, NULL, irq_base: 0, NULL); |
228 | } |
229 | EXPORT_SYMBOL_NS(ocelot_core_init, MFD_OCELOT); |
230 | |
231 | MODULE_DESCRIPTION("Externally Controlled Ocelot Chip Driver" ); |
232 | MODULE_AUTHOR("Colin Foster <colin.foster@in-advantage.com>" ); |
233 | MODULE_LICENSE("GPL" ); |
234 | MODULE_IMPORT_NS(MFD_OCELOT_SPI); |
235 | |