1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Marvell Orion pinctrl driver based on mvebu pinctrl core
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Author: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * The first 16 MPP pins on Orion are easy to handle: they are
8*4882a593Smuzhiyun * configured through 2 consecutive registers, located at the base
9*4882a593Smuzhiyun * address of the MPP device.
10*4882a593Smuzhiyun *
11*4882a593Smuzhiyun * However the last 4 MPP pins are handled by a register at offset
12*4882a593Smuzhiyun * 0x50 from the base address, so it is not consecutive with the first
13*4882a593Smuzhiyun * two registers.
14*4882a593Smuzhiyun */
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun #include <linux/err.h>
17*4882a593Smuzhiyun #include <linux/init.h>
18*4882a593Smuzhiyun #include <linux/io.h>
19*4882a593Smuzhiyun #include <linux/platform_device.h>
20*4882a593Smuzhiyun #include <linux/clk.h>
21*4882a593Smuzhiyun #include <linux/of.h>
22*4882a593Smuzhiyun #include <linux/of_device.h>
23*4882a593Smuzhiyun #include <linux/pinctrl/pinctrl.h>
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun #include "pinctrl-mvebu.h"
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun static void __iomem *mpp_base;
28*4882a593Smuzhiyun static void __iomem *high_mpp_base;
29*4882a593Smuzhiyun
orion_mpp_ctrl_get(struct mvebu_mpp_ctrl_data * data,unsigned pid,unsigned long * config)30*4882a593Smuzhiyun static int orion_mpp_ctrl_get(struct mvebu_mpp_ctrl_data *data,
31*4882a593Smuzhiyun unsigned pid, unsigned long *config)
32*4882a593Smuzhiyun {
33*4882a593Smuzhiyun unsigned shift = (pid % MVEBU_MPPS_PER_REG) * MVEBU_MPP_BITS;
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun if (pid < 16) {
36*4882a593Smuzhiyun unsigned off = (pid / MVEBU_MPPS_PER_REG) * MVEBU_MPP_BITS;
37*4882a593Smuzhiyun *config = (readl(mpp_base + off) >> shift) & MVEBU_MPP_MASK;
38*4882a593Smuzhiyun }
39*4882a593Smuzhiyun else {
40*4882a593Smuzhiyun *config = (readl(high_mpp_base) >> shift) & MVEBU_MPP_MASK;
41*4882a593Smuzhiyun }
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun return 0;
44*4882a593Smuzhiyun }
45*4882a593Smuzhiyun
orion_mpp_ctrl_set(struct mvebu_mpp_ctrl_data * data,unsigned pid,unsigned long config)46*4882a593Smuzhiyun static int orion_mpp_ctrl_set(struct mvebu_mpp_ctrl_data *data,
47*4882a593Smuzhiyun unsigned pid, unsigned long config)
48*4882a593Smuzhiyun {
49*4882a593Smuzhiyun unsigned shift = (pid % MVEBU_MPPS_PER_REG) * MVEBU_MPP_BITS;
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun if (pid < 16) {
52*4882a593Smuzhiyun unsigned off = (pid / MVEBU_MPPS_PER_REG) * MVEBU_MPP_BITS;
53*4882a593Smuzhiyun u32 reg = readl(mpp_base + off) & ~(MVEBU_MPP_MASK << shift);
54*4882a593Smuzhiyun writel(reg | (config << shift), mpp_base + off);
55*4882a593Smuzhiyun }
56*4882a593Smuzhiyun else {
57*4882a593Smuzhiyun u32 reg = readl(high_mpp_base) & ~(MVEBU_MPP_MASK << shift);
58*4882a593Smuzhiyun writel(reg | (config << shift), high_mpp_base);
59*4882a593Smuzhiyun }
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun return 0;
62*4882a593Smuzhiyun }
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun #define V(f5181, f5182, f5281) \
65*4882a593Smuzhiyun ((f5181 << 0) | (f5182 << 1) | (f5281 << 2))
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun enum orion_variant {
68*4882a593Smuzhiyun V_5181 = V(1, 0, 0),
69*4882a593Smuzhiyun V_5182 = V(0, 1, 0),
70*4882a593Smuzhiyun V_5281 = V(0, 0, 1),
71*4882a593Smuzhiyun V_ALL = V(1, 1, 1),
72*4882a593Smuzhiyun };
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun static struct mvebu_mpp_mode orion_mpp_modes[] = {
75*4882a593Smuzhiyun MPP_MODE(0,
76*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x0, "pcie", "rstout", V_ALL),
77*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x2, "pci", "req2", V_ALL),
78*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x3, "gpio", NULL, V_ALL)),
79*4882a593Smuzhiyun MPP_MODE(1,
80*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_ALL),
81*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x2, "pci", "gnt2", V_ALL)),
82*4882a593Smuzhiyun MPP_MODE(2,
83*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_ALL),
84*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x2, "pci", "req3", V_ALL),
85*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x3, "pci-1", "pme", V_ALL)),
86*4882a593Smuzhiyun MPP_MODE(3,
87*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_ALL),
88*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x2, "pci", "gnt3", V_ALL)),
89*4882a593Smuzhiyun MPP_MODE(4,
90*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_ALL),
91*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x2, "pci", "req4", V_ALL),
92*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x4, "bootnand", "re", V_5182 | V_5281),
93*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x5, "sata0", "prsnt", V_5182)),
94*4882a593Smuzhiyun MPP_MODE(5,
95*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_ALL),
96*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x2, "pci", "gnt4", V_ALL),
97*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x4, "bootnand", "we", V_5182 | V_5281),
98*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x5, "sata1", "prsnt", V_5182)),
99*4882a593Smuzhiyun MPP_MODE(6,
100*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_ALL),
101*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x2, "pci", "req5", V_ALL),
102*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x4, "nand", "re0", V_5182 | V_5281),
103*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x5, "pci-1", "clk", V_5181),
104*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x5, "sata0", "act", V_5182)),
105*4882a593Smuzhiyun MPP_MODE(7,
106*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_ALL),
107*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x2, "pci", "gnt5", V_ALL),
108*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x4, "nand", "we0", V_5182 | V_5281),
109*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x5, "pci-1", "clk", V_5181),
110*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x5, "sata1", "act", V_5182)),
111*4882a593Smuzhiyun MPP_MODE(8,
112*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_ALL),
113*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x1, "ge", "col", V_ALL)),
114*4882a593Smuzhiyun MPP_MODE(9,
115*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_ALL),
116*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x1, "ge", "rxerr", V_ALL)),
117*4882a593Smuzhiyun MPP_MODE(10,
118*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_ALL),
119*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x1, "ge", "crs", V_ALL)),
120*4882a593Smuzhiyun MPP_MODE(11,
121*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_ALL),
122*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x1, "ge", "txerr", V_ALL)),
123*4882a593Smuzhiyun MPP_MODE(12,
124*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_ALL),
125*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x1, "ge", "txd4", V_ALL),
126*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x4, "nand", "re1", V_5182 | V_5281),
127*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x5, "sata0", "ledprsnt", V_5182)),
128*4882a593Smuzhiyun MPP_MODE(13,
129*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_ALL),
130*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x1, "ge", "txd5", V_ALL),
131*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x4, "nand", "we1", V_5182 | V_5281),
132*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x5, "sata1", "ledprsnt", V_5182)),
133*4882a593Smuzhiyun MPP_MODE(14,
134*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_ALL),
135*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x1, "ge", "txd6", V_ALL),
136*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x4, "nand", "re2", V_5182 | V_5281),
137*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x5, "sata0", "ledact", V_5182)),
138*4882a593Smuzhiyun MPP_MODE(15,
139*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_ALL),
140*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x1, "ge", "txd7", V_ALL),
141*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x4, "nand", "we2", V_5182 | V_5281),
142*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x5, "sata1", "ledact", V_5182)),
143*4882a593Smuzhiyun MPP_MODE(16,
144*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x0, "uart1", "rxd", V_5182 | V_5281),
145*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x1, "ge", "rxd4", V_ALL),
146*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x5, "gpio", NULL, V_5182)),
147*4882a593Smuzhiyun MPP_MODE(17,
148*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x0, "uart1", "txd", V_5182 | V_5281),
149*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x1, "ge", "rxd5", V_ALL),
150*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x5, "gpio", NULL, V_5182)),
151*4882a593Smuzhiyun MPP_MODE(18,
152*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x0, "uart1", "cts", V_5182 | V_5281),
153*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x1, "ge", "rxd6", V_ALL),
154*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x5, "gpio", NULL, V_5182)),
155*4882a593Smuzhiyun MPP_MODE(19,
156*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x0, "uart1", "rts", V_5182 | V_5281),
157*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x1, "ge", "rxd7", V_ALL),
158*4882a593Smuzhiyun MPP_VAR_FUNCTION(0x5, "gpio", NULL, V_5182)),
159*4882a593Smuzhiyun };
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun static const struct mvebu_mpp_ctrl orion_mpp_controls[] = {
162*4882a593Smuzhiyun MPP_FUNC_CTRL(0, 19, NULL, orion_mpp_ctrl),
163*4882a593Smuzhiyun };
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun static struct pinctrl_gpio_range mv88f5181_gpio_ranges[] = {
166*4882a593Smuzhiyun MPP_GPIO_RANGE(0, 0, 0, 16),
167*4882a593Smuzhiyun };
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun static struct pinctrl_gpio_range mv88f5182_gpio_ranges[] = {
170*4882a593Smuzhiyun MPP_GPIO_RANGE(0, 0, 0, 19),
171*4882a593Smuzhiyun };
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun static struct pinctrl_gpio_range mv88f5281_gpio_ranges[] = {
174*4882a593Smuzhiyun MPP_GPIO_RANGE(0, 0, 0, 16),
175*4882a593Smuzhiyun };
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun static struct mvebu_pinctrl_soc_info mv88f5181_info = {
178*4882a593Smuzhiyun .variant = V_5181,
179*4882a593Smuzhiyun .controls = orion_mpp_controls,
180*4882a593Smuzhiyun .ncontrols = ARRAY_SIZE(orion_mpp_controls),
181*4882a593Smuzhiyun .modes = orion_mpp_modes,
182*4882a593Smuzhiyun .nmodes = ARRAY_SIZE(orion_mpp_modes),
183*4882a593Smuzhiyun .gpioranges = mv88f5181_gpio_ranges,
184*4882a593Smuzhiyun .ngpioranges = ARRAY_SIZE(mv88f5181_gpio_ranges),
185*4882a593Smuzhiyun };
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun static struct mvebu_pinctrl_soc_info mv88f5182_info = {
188*4882a593Smuzhiyun .variant = V_5182,
189*4882a593Smuzhiyun .controls = orion_mpp_controls,
190*4882a593Smuzhiyun .ncontrols = ARRAY_SIZE(orion_mpp_controls),
191*4882a593Smuzhiyun .modes = orion_mpp_modes,
192*4882a593Smuzhiyun .nmodes = ARRAY_SIZE(orion_mpp_modes),
193*4882a593Smuzhiyun .gpioranges = mv88f5182_gpio_ranges,
194*4882a593Smuzhiyun .ngpioranges = ARRAY_SIZE(mv88f5182_gpio_ranges),
195*4882a593Smuzhiyun };
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun static struct mvebu_pinctrl_soc_info mv88f5281_info = {
198*4882a593Smuzhiyun .variant = V_5281,
199*4882a593Smuzhiyun .controls = orion_mpp_controls,
200*4882a593Smuzhiyun .ncontrols = ARRAY_SIZE(orion_mpp_controls),
201*4882a593Smuzhiyun .modes = orion_mpp_modes,
202*4882a593Smuzhiyun .nmodes = ARRAY_SIZE(orion_mpp_modes),
203*4882a593Smuzhiyun .gpioranges = mv88f5281_gpio_ranges,
204*4882a593Smuzhiyun .ngpioranges = ARRAY_SIZE(mv88f5281_gpio_ranges),
205*4882a593Smuzhiyun };
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun /*
208*4882a593Smuzhiyun * There are multiple variants of the Orion SoCs, but in terms of pin
209*4882a593Smuzhiyun * muxing, they are identical.
210*4882a593Smuzhiyun */
211*4882a593Smuzhiyun static const struct of_device_id orion_pinctrl_of_match[] = {
212*4882a593Smuzhiyun { .compatible = "marvell,88f5181-pinctrl", .data = &mv88f5181_info },
213*4882a593Smuzhiyun { .compatible = "marvell,88f5181l-pinctrl", .data = &mv88f5181_info },
214*4882a593Smuzhiyun { .compatible = "marvell,88f5182-pinctrl", .data = &mv88f5182_info },
215*4882a593Smuzhiyun { .compatible = "marvell,88f5281-pinctrl", .data = &mv88f5281_info },
216*4882a593Smuzhiyun { }
217*4882a593Smuzhiyun };
218*4882a593Smuzhiyun
orion_pinctrl_probe(struct platform_device * pdev)219*4882a593Smuzhiyun static int orion_pinctrl_probe(struct platform_device *pdev)
220*4882a593Smuzhiyun {
221*4882a593Smuzhiyun const struct of_device_id *match =
222*4882a593Smuzhiyun of_match_device(orion_pinctrl_of_match, &pdev->dev);
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun pdev->dev.platform_data = (void*)match->data;
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun mpp_base = devm_platform_ioremap_resource(pdev, 0);
227*4882a593Smuzhiyun if (IS_ERR(mpp_base))
228*4882a593Smuzhiyun return PTR_ERR(mpp_base);
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun high_mpp_base = devm_platform_ioremap_resource(pdev, 1);
231*4882a593Smuzhiyun if (IS_ERR(high_mpp_base))
232*4882a593Smuzhiyun return PTR_ERR(high_mpp_base);
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun return mvebu_pinctrl_probe(pdev);
235*4882a593Smuzhiyun }
236*4882a593Smuzhiyun
237*4882a593Smuzhiyun static struct platform_driver orion_pinctrl_driver = {
238*4882a593Smuzhiyun .driver = {
239*4882a593Smuzhiyun .name = "orion-pinctrl",
240*4882a593Smuzhiyun .of_match_table = of_match_ptr(orion_pinctrl_of_match),
241*4882a593Smuzhiyun },
242*4882a593Smuzhiyun .probe = orion_pinctrl_probe,
243*4882a593Smuzhiyun };
244*4882a593Smuzhiyun builtin_platform_driver(orion_pinctrl_driver);
245