162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Marvell Orion pinctrl driver based on mvebu pinctrl core
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Author: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * The first 16 MPP pins on Orion are easy to handle: they are
862306a36Sopenharmony_ci * configured through 2 consecutive registers, located at the base
962306a36Sopenharmony_ci * address of the MPP device.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * However the last 4 MPP pins are handled by a register at offset
1262306a36Sopenharmony_ci * 0x50 from the base address, so it is not consecutive with the first
1362306a36Sopenharmony_ci * two registers.
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <linux/err.h>
1762306a36Sopenharmony_ci#include <linux/init.h>
1862306a36Sopenharmony_ci#include <linux/io.h>
1962306a36Sopenharmony_ci#include <linux/platform_device.h>
2062306a36Sopenharmony_ci#include <linux/clk.h>
2162306a36Sopenharmony_ci#include <linux/of.h>
2262306a36Sopenharmony_ci#include <linux/of_device.h>
2362306a36Sopenharmony_ci#include <linux/pinctrl/pinctrl.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include "pinctrl-mvebu.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic void __iomem *mpp_base;
2862306a36Sopenharmony_cistatic void __iomem *high_mpp_base;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic int orion_mpp_ctrl_get(struct mvebu_mpp_ctrl_data *data,
3162306a36Sopenharmony_ci			      unsigned pid, unsigned long *config)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	unsigned shift = (pid % MVEBU_MPPS_PER_REG) * MVEBU_MPP_BITS;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	if (pid < 16) {
3662306a36Sopenharmony_ci		unsigned off = (pid / MVEBU_MPPS_PER_REG) * MVEBU_MPP_BITS;
3762306a36Sopenharmony_ci		*config = (readl(mpp_base + off) >> shift) & MVEBU_MPP_MASK;
3862306a36Sopenharmony_ci	}
3962306a36Sopenharmony_ci	else {
4062306a36Sopenharmony_ci		*config = (readl(high_mpp_base) >> shift) & MVEBU_MPP_MASK;
4162306a36Sopenharmony_ci	}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	return 0;
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic int orion_mpp_ctrl_set(struct mvebu_mpp_ctrl_data *data,
4762306a36Sopenharmony_ci			      unsigned pid, unsigned long config)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	unsigned shift = (pid % MVEBU_MPPS_PER_REG) * MVEBU_MPP_BITS;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	if (pid < 16) {
5262306a36Sopenharmony_ci		unsigned off = (pid / MVEBU_MPPS_PER_REG) * MVEBU_MPP_BITS;
5362306a36Sopenharmony_ci		u32 reg = readl(mpp_base + off) & ~(MVEBU_MPP_MASK << shift);
5462306a36Sopenharmony_ci		writel(reg | (config << shift), mpp_base + off);
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci	else {
5762306a36Sopenharmony_ci		u32 reg = readl(high_mpp_base) & ~(MVEBU_MPP_MASK << shift);
5862306a36Sopenharmony_ci		writel(reg | (config << shift), high_mpp_base);
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	return 0;
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci#define V(f5181, f5182, f5281) \
6562306a36Sopenharmony_ci	((f5181 << 0) | (f5182 << 1) | (f5281 << 2))
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cienum orion_variant {
6862306a36Sopenharmony_ci	V_5181  = V(1, 0, 0),
6962306a36Sopenharmony_ci	V_5182  = V(0, 1, 0),
7062306a36Sopenharmony_ci	V_5281  = V(0, 0, 1),
7162306a36Sopenharmony_ci	V_ALL   = V(1, 1, 1),
7262306a36Sopenharmony_ci};
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic struct mvebu_mpp_mode orion_mpp_modes[] = {
7562306a36Sopenharmony_ci	MPP_MODE(0,
7662306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "pcie", "rstout",    V_ALL),
7762306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x2, "pci", "req2",       V_ALL),
7862306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x3, "gpio", NULL,        V_ALL)),
7962306a36Sopenharmony_ci	MPP_MODE(1,
8062306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
8162306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x2, "pci", "gnt2",       V_ALL)),
8262306a36Sopenharmony_ci	MPP_MODE(2,
8362306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
8462306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x2, "pci", "req3",       V_ALL),
8562306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x3, "pci-1", "pme",      V_ALL)),
8662306a36Sopenharmony_ci	MPP_MODE(3,
8762306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
8862306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x2, "pci", "gnt3",       V_ALL)),
8962306a36Sopenharmony_ci	MPP_MODE(4,
9062306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
9162306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x2, "pci", "req4",       V_ALL),
9262306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x4, "bootnand", "re",    V_5182 | V_5281),
9362306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x5, "sata0", "prsnt",    V_5182)),
9462306a36Sopenharmony_ci	MPP_MODE(5,
9562306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
9662306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x2, "pci", "gnt4",       V_ALL),
9762306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x4, "bootnand", "we",    V_5182 | V_5281),
9862306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x5, "sata1", "prsnt",    V_5182)),
9962306a36Sopenharmony_ci	MPP_MODE(6,
10062306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
10162306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x2, "pci", "req5",       V_ALL),
10262306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x4, "nand", "re0",       V_5182 | V_5281),
10362306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x5, "pci-1", "clk",      V_5181),
10462306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x5, "sata0", "act",      V_5182)),
10562306a36Sopenharmony_ci	MPP_MODE(7,
10662306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
10762306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x2, "pci", "gnt5",       V_ALL),
10862306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x4, "nand", "we0",       V_5182 | V_5281),
10962306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x5, "pci-1", "clk",      V_5181),
11062306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x5, "sata1", "act",      V_5182)),
11162306a36Sopenharmony_ci	MPP_MODE(8,
11262306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
11362306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x1, "ge", "col",         V_ALL)),
11462306a36Sopenharmony_ci	MPP_MODE(9,
11562306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
11662306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x1, "ge", "rxerr",       V_ALL)),
11762306a36Sopenharmony_ci	MPP_MODE(10,
11862306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
11962306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x1, "ge", "crs",         V_ALL)),
12062306a36Sopenharmony_ci	MPP_MODE(11,
12162306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
12262306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x1, "ge", "txerr",       V_ALL)),
12362306a36Sopenharmony_ci	MPP_MODE(12,
12462306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
12562306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x1, "ge", "txd4",        V_ALL),
12662306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x4, "nand", "re1",       V_5182 | V_5281),
12762306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x5, "sata0", "ledprsnt", V_5182)),
12862306a36Sopenharmony_ci	MPP_MODE(13,
12962306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
13062306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x1, "ge", "txd5",        V_ALL),
13162306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x4, "nand", "we1",       V_5182 | V_5281),
13262306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x5, "sata1", "ledprsnt", V_5182)),
13362306a36Sopenharmony_ci	MPP_MODE(14,
13462306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
13562306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x1, "ge", "txd6",        V_ALL),
13662306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x4, "nand", "re2",       V_5182 | V_5281),
13762306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x5, "sata0", "ledact",   V_5182)),
13862306a36Sopenharmony_ci	MPP_MODE(15,
13962306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
14062306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x1, "ge", "txd7",        V_ALL),
14162306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x4, "nand", "we2",       V_5182 | V_5281),
14262306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x5, "sata1", "ledact",   V_5182)),
14362306a36Sopenharmony_ci	MPP_MODE(16,
14462306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "uart1", "rxd",      V_5182 | V_5281),
14562306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x1, "ge", "rxd4",        V_ALL),
14662306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x5, "gpio", NULL,        V_5182)),
14762306a36Sopenharmony_ci	MPP_MODE(17,
14862306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "uart1", "txd",      V_5182 | V_5281),
14962306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x1, "ge", "rxd5",        V_ALL),
15062306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x5, "gpio", NULL,        V_5182)),
15162306a36Sopenharmony_ci	MPP_MODE(18,
15262306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "uart1", "cts",      V_5182 | V_5281),
15362306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x1, "ge", "rxd6",        V_ALL),
15462306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x5, "gpio", NULL,        V_5182)),
15562306a36Sopenharmony_ci	MPP_MODE(19,
15662306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "uart1", "rts",      V_5182 | V_5281),
15762306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x1, "ge", "rxd7",        V_ALL),
15862306a36Sopenharmony_ci		 MPP_VAR_FUNCTION(0x5, "gpio", NULL,        V_5182)),
15962306a36Sopenharmony_ci};
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic const struct mvebu_mpp_ctrl orion_mpp_controls[] = {
16262306a36Sopenharmony_ci	MPP_FUNC_CTRL(0, 19, NULL, orion_mpp_ctrl),
16362306a36Sopenharmony_ci};
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic struct pinctrl_gpio_range mv88f5181_gpio_ranges[] = {
16662306a36Sopenharmony_ci	MPP_GPIO_RANGE(0, 0, 0, 16),
16762306a36Sopenharmony_ci};
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic struct pinctrl_gpio_range mv88f5182_gpio_ranges[] = {
17062306a36Sopenharmony_ci	MPP_GPIO_RANGE(0, 0, 0, 19),
17162306a36Sopenharmony_ci};
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic struct pinctrl_gpio_range mv88f5281_gpio_ranges[] = {
17462306a36Sopenharmony_ci	MPP_GPIO_RANGE(0, 0, 0, 16),
17562306a36Sopenharmony_ci};
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic struct mvebu_pinctrl_soc_info mv88f5181_info = {
17862306a36Sopenharmony_ci	.variant = V_5181,
17962306a36Sopenharmony_ci	.controls = orion_mpp_controls,
18062306a36Sopenharmony_ci	.ncontrols = ARRAY_SIZE(orion_mpp_controls),
18162306a36Sopenharmony_ci	.modes = orion_mpp_modes,
18262306a36Sopenharmony_ci	.nmodes = ARRAY_SIZE(orion_mpp_modes),
18362306a36Sopenharmony_ci	.gpioranges = mv88f5181_gpio_ranges,
18462306a36Sopenharmony_ci	.ngpioranges = ARRAY_SIZE(mv88f5181_gpio_ranges),
18562306a36Sopenharmony_ci};
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic struct mvebu_pinctrl_soc_info mv88f5182_info = {
18862306a36Sopenharmony_ci	.variant = V_5182,
18962306a36Sopenharmony_ci	.controls = orion_mpp_controls,
19062306a36Sopenharmony_ci	.ncontrols = ARRAY_SIZE(orion_mpp_controls),
19162306a36Sopenharmony_ci	.modes = orion_mpp_modes,
19262306a36Sopenharmony_ci	.nmodes = ARRAY_SIZE(orion_mpp_modes),
19362306a36Sopenharmony_ci	.gpioranges = mv88f5182_gpio_ranges,
19462306a36Sopenharmony_ci	.ngpioranges = ARRAY_SIZE(mv88f5182_gpio_ranges),
19562306a36Sopenharmony_ci};
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic struct mvebu_pinctrl_soc_info mv88f5281_info = {
19862306a36Sopenharmony_ci	.variant = V_5281,
19962306a36Sopenharmony_ci	.controls = orion_mpp_controls,
20062306a36Sopenharmony_ci	.ncontrols = ARRAY_SIZE(orion_mpp_controls),
20162306a36Sopenharmony_ci	.modes = orion_mpp_modes,
20262306a36Sopenharmony_ci	.nmodes = ARRAY_SIZE(orion_mpp_modes),
20362306a36Sopenharmony_ci	.gpioranges = mv88f5281_gpio_ranges,
20462306a36Sopenharmony_ci	.ngpioranges = ARRAY_SIZE(mv88f5281_gpio_ranges),
20562306a36Sopenharmony_ci};
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci/*
20862306a36Sopenharmony_ci * There are multiple variants of the Orion SoCs, but in terms of pin
20962306a36Sopenharmony_ci * muxing, they are identical.
21062306a36Sopenharmony_ci */
21162306a36Sopenharmony_cistatic const struct of_device_id orion_pinctrl_of_match[] = {
21262306a36Sopenharmony_ci	{ .compatible = "marvell,88f5181-pinctrl", .data = &mv88f5181_info },
21362306a36Sopenharmony_ci	{ .compatible = "marvell,88f5181l-pinctrl", .data = &mv88f5181_info },
21462306a36Sopenharmony_ci	{ .compatible = "marvell,88f5182-pinctrl", .data = &mv88f5182_info },
21562306a36Sopenharmony_ci	{ .compatible = "marvell,88f5281-pinctrl", .data = &mv88f5281_info },
21662306a36Sopenharmony_ci	{ }
21762306a36Sopenharmony_ci};
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic int orion_pinctrl_probe(struct platform_device *pdev)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	const struct of_device_id *match =
22262306a36Sopenharmony_ci		of_match_device(orion_pinctrl_of_match, &pdev->dev);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	pdev->dev.platform_data = (void*)match->data;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	mpp_base = devm_platform_ioremap_resource(pdev, 0);
22762306a36Sopenharmony_ci	if (IS_ERR(mpp_base))
22862306a36Sopenharmony_ci		return PTR_ERR(mpp_base);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	high_mpp_base = devm_platform_ioremap_resource(pdev, 1);
23162306a36Sopenharmony_ci	if (IS_ERR(high_mpp_base))
23262306a36Sopenharmony_ci		return PTR_ERR(high_mpp_base);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	return mvebu_pinctrl_probe(pdev);
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic struct platform_driver orion_pinctrl_driver = {
23862306a36Sopenharmony_ci	.driver = {
23962306a36Sopenharmony_ci		.name = "orion-pinctrl",
24062306a36Sopenharmony_ci		.of_match_table = of_match_ptr(orion_pinctrl_of_match),
24162306a36Sopenharmony_ci	},
24262306a36Sopenharmony_ci	.probe = orion_pinctrl_probe,
24362306a36Sopenharmony_ci};
24462306a36Sopenharmony_cibuiltin_platform_driver(orion_pinctrl_driver);
245