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 0x024
38
39 #define VSC7512_PHY_RES_START 0x710700f0
40 #define VSC7512_PHY_RES_SIZE 0x004
41
42 #define VSC7512_GPIO_RES_START 0x71070034
43 #define VSC7512_GPIO_RES_SIZE 0x06c
44
45 #define VSC7512_SIO_CTRL_RES_START 0x710700f8
46 #define VSC7512_SIO_CTRL_RES_SIZE 0x100
47
48 #define VSC7512_GCB_RST_SLEEP_US 100
49 #define VSC7512_GCB_RST_TIMEOUT_US 100000
50
ocelot_gcb_chip_rst_status(struct ocelot_ddata * ddata)51 static int ocelot_gcb_chip_rst_status(struct ocelot_ddata *ddata)
52 {
53 int val, err;
54
55 err = regmap_read(ddata->gcb_regmap, REG_GCB_SOFT_RST, &val);
56 if (err)
57 return err;
58
59 return val;
60 }
61
ocelot_chip_reset(struct device * dev)62 int ocelot_chip_reset(struct device *dev)
63 {
64 struct ocelot_ddata *ddata = dev_get_drvdata(dev);
65 int ret, val;
66
67 /*
68 * Reset the entire chip here to put it into a completely known state.
69 * Other drivers may want to reset their own subsystems. The register
70 * self-clears, so one write is all that is needed and wait for it to
71 * clear.
72 */
73 ret = regmap_write(ddata->gcb_regmap, REG_GCB_SOFT_RST, BIT_SOFT_CHIP_RST);
74 if (ret)
75 return ret;
76
77 return readx_poll_timeout(ocelot_gcb_chip_rst_status, ddata, val, !val,
78 VSC7512_GCB_RST_SLEEP_US, VSC7512_GCB_RST_TIMEOUT_US);
79 }
80 EXPORT_SYMBOL_NS(ocelot_chip_reset, MFD_OCELOT);
81
82 static const struct resource vsc7512_miim0_resources[] = {
83 DEFINE_RES_REG_NAMED(VSC7512_MIIM0_RES_START, VSC7512_MIIM_RES_SIZE, "gcb_miim0"),
84 DEFINE_RES_REG_NAMED(VSC7512_PHY_RES_START, VSC7512_PHY_RES_SIZE, "gcb_phy"),
85 };
86
87 static const struct resource vsc7512_miim1_resources[] = {
88 DEFINE_RES_REG_NAMED(VSC7512_MIIM1_RES_START, VSC7512_MIIM_RES_SIZE, "gcb_miim1"),
89 };
90
91 static const struct resource vsc7512_pinctrl_resources[] = {
92 DEFINE_RES_REG_NAMED(VSC7512_GPIO_RES_START, VSC7512_GPIO_RES_SIZE, "gcb_gpio"),
93 };
94
95 static const struct resource vsc7512_sgpio_resources[] = {
96 DEFINE_RES_REG_NAMED(VSC7512_SIO_CTRL_RES_START, VSC7512_SIO_CTRL_RES_SIZE, "gcb_sio"),
97 };
98
99 static const struct mfd_cell vsc7512_devs[] = {
100 {
101 .name = "ocelot-pinctrl",
102 .of_compatible = "mscc,ocelot-pinctrl",
103 .num_resources = ARRAY_SIZE(vsc7512_pinctrl_resources),
104 .resources = vsc7512_pinctrl_resources,
105 }, {
106 .name = "ocelot-sgpio",
107 .of_compatible = "mscc,ocelot-sgpio",
108 .num_resources = ARRAY_SIZE(vsc7512_sgpio_resources),
109 .resources = vsc7512_sgpio_resources,
110 }, {
111 .name = "ocelot-miim0",
112 .of_compatible = "mscc,ocelot-miim",
113 .of_reg = VSC7512_MIIM0_RES_START,
114 .use_of_reg = true,
115 .num_resources = ARRAY_SIZE(vsc7512_miim0_resources),
116 .resources = vsc7512_miim0_resources,
117 }, {
118 .name = "ocelot-miim1",
119 .of_compatible = "mscc,ocelot-miim",
120 .of_reg = VSC7512_MIIM1_RES_START,
121 .use_of_reg = true,
122 .num_resources = ARRAY_SIZE(vsc7512_miim1_resources),
123 .resources = vsc7512_miim1_resources,
124 },
125 };
126
ocelot_core_try_add_regmap(struct device * dev,const struct resource * res)127 static void ocelot_core_try_add_regmap(struct device *dev,
128 const struct resource *res)
129 {
130 if (dev_get_regmap(dev, res->name))
131 return;
132
133 ocelot_spi_init_regmap(dev, res);
134 }
135
ocelot_core_try_add_regmaps(struct device * dev,const struct mfd_cell * cell)136 static void ocelot_core_try_add_regmaps(struct device *dev,
137 const struct mfd_cell *cell)
138 {
139 int i;
140
141 for (i = 0; i < cell->num_resources; i++)
142 ocelot_core_try_add_regmap(dev, &cell->resources[i]);
143 }
144
ocelot_core_init(struct device * dev)145 int ocelot_core_init(struct device *dev)
146 {
147 int i, ndevs;
148
149 ndevs = ARRAY_SIZE(vsc7512_devs);
150
151 for (i = 0; i < ndevs; i++)
152 ocelot_core_try_add_regmaps(dev, &vsc7512_devs[i]);
153
154 return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, vsc7512_devs, ndevs, NULL, 0, NULL);
155 }
156 EXPORT_SYMBOL_NS(ocelot_core_init, MFD_OCELOT);
157
158 MODULE_DESCRIPTION("Externally Controlled Ocelot Chip Driver");
159 MODULE_AUTHOR("Colin Foster <colin.foster@in-advantage.com>");
160 MODULE_LICENSE("GPL");
161 MODULE_IMPORT_NS(MFD_OCELOT_SPI);
162