xref: /linux/drivers/mfd/ocelot-core.c (revision 1a69f7a161a78aead07cd4b811d796950e892fa4)
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 
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 
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 
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 
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 
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