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(ddata->gcb_regmap, REG_GCB_SOFT_RST, &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(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, 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, &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, &vsc7512_devs[i]); 226 227 return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, vsc7512_devs, ndevs, NULL, 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