| 1 | |
| 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 <[email protected]>
|
| 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 <[email protected]>");
|
| 233 | MODULE_LICENSE("GPL");
|
| 234 | MODULE_IMPORT_NS("MFD_OCELOT_SPI");
|