Merge tag 'for-6.2-rc2-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave...
[linux-2.6-block.git] / drivers / mfd / ocelot-core.c
CommitLineData
f3e89362
CF
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
51static 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
62int 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}
80EXPORT_SYMBOL_NS(ocelot_chip_reset, MFD_OCELOT);
81
82static 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
87static const struct resource vsc7512_miim1_resources[] = {
88 DEFINE_RES_REG_NAMED(VSC7512_MIIM1_RES_START, VSC7512_MIIM_RES_SIZE, "gcb_miim1"),
89};
90
91static const struct resource vsc7512_pinctrl_resources[] = {
92 DEFINE_RES_REG_NAMED(VSC7512_GPIO_RES_START, VSC7512_GPIO_RES_SIZE, "gcb_gpio"),
93};
94
95static 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
99static 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
127static 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
136static 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
145int 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}
156EXPORT_SYMBOL_NS(ocelot_core_init, MFD_OCELOT);
157
158MODULE_DESCRIPTION("Externally Controlled Ocelot Chip Driver");
159MODULE_AUTHOR("Colin Foster <colin.foster@in-advantage.com>");
160MODULE_LICENSE("GPL");
161MODULE_IMPORT_NS(MFD_OCELOT_SPI);