18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Marvell Orion pinctrl driver based on mvebu pinctrl core
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Author: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * The first 16 MPP pins on Orion are easy to handle: they are
88c2ecf20Sopenharmony_ci * configured through 2 consecutive registers, located at the base
98c2ecf20Sopenharmony_ci * address of the MPP device.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * However the last 4 MPP pins are handled by a register at offset
128c2ecf20Sopenharmony_ci * 0x50 from the base address, so it is not consecutive with the first
138c2ecf20Sopenharmony_ci * two registers.
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/err.h>
178c2ecf20Sopenharmony_ci#include <linux/init.h>
188c2ecf20Sopenharmony_ci#include <linux/io.h>
198c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
208c2ecf20Sopenharmony_ci#include <linux/clk.h>
218c2ecf20Sopenharmony_ci#include <linux/of.h>
228c2ecf20Sopenharmony_ci#include <linux/of_device.h>
238c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinctrl.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include "pinctrl-mvebu.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic void __iomem *mpp_base;
288c2ecf20Sopenharmony_cistatic void __iomem *high_mpp_base;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic int orion_mpp_ctrl_get(struct mvebu_mpp_ctrl_data *data,
318c2ecf20Sopenharmony_ci			      unsigned pid, unsigned long *config)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	unsigned shift = (pid % MVEBU_MPPS_PER_REG) * MVEBU_MPP_BITS;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	if (pid < 16) {
368c2ecf20Sopenharmony_ci		unsigned off = (pid / MVEBU_MPPS_PER_REG) * MVEBU_MPP_BITS;
378c2ecf20Sopenharmony_ci		*config = (readl(mpp_base + off) >> shift) & MVEBU_MPP_MASK;
388c2ecf20Sopenharmony_ci	}
398c2ecf20Sopenharmony_ci	else {
408c2ecf20Sopenharmony_ci		*config = (readl(high_mpp_base) >> shift) & MVEBU_MPP_MASK;
418c2ecf20Sopenharmony_ci	}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	return 0;
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic int orion_mpp_ctrl_set(struct mvebu_mpp_ctrl_data *data,
478c2ecf20Sopenharmony_ci			      unsigned pid, unsigned long config)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	unsigned shift = (pid % MVEBU_MPPS_PER_REG) * MVEBU_MPP_BITS;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	if (pid < 16) {
528c2ecf20Sopenharmony_ci		unsigned off = (pid / MVEBU_MPPS_PER_REG) * MVEBU_MPP_BITS;
538c2ecf20Sopenharmony_ci		u32 reg = readl(mpp_base + off) & ~(MVEBU_MPP_MASK << shift);
548c2ecf20Sopenharmony_ci		writel(reg | (config << shift), mpp_base + off);
558c2ecf20Sopenharmony_ci	}
568c2ecf20Sopenharmony_ci	else {
578c2ecf20Sopenharmony_ci		u32 reg = readl(high_mpp_base) & ~(MVEBU_MPP_MASK << shift);
588c2ecf20Sopenharmony_ci		writel(reg | (config << shift), high_mpp_base);
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	return 0;
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci#define V(f5181, f5182, f5281) \
658c2ecf20Sopenharmony_ci	((f5181 << 0) | (f5182 << 1) | (f5281 << 2))
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cienum orion_variant {
688c2ecf20Sopenharmony_ci	V_5181  = V(1, 0, 0),
698c2ecf20Sopenharmony_ci	V_5182  = V(0, 1, 0),
708c2ecf20Sopenharmony_ci	V_5281  = V(0, 0, 1),
718c2ecf20Sopenharmony_ci	V_ALL   = V(1, 1, 1),
728c2ecf20Sopenharmony_ci};
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic struct mvebu_mpp_mode orion_mpp_modes[] = {
758c2ecf20Sopenharmony_ci	MPP_MODE(0,
768c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "pcie", "rstout",    V_ALL),
778c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x2, "pci", "req2",       V_ALL),
788c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x3, "gpio", NULL,        V_ALL)),
798c2ecf20Sopenharmony_ci	MPP_MODE(1,
808c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
818c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x2, "pci", "gnt2",       V_ALL)),
828c2ecf20Sopenharmony_ci	MPP_MODE(2,
838c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
848c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x2, "pci", "req3",       V_ALL),
858c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x3, "pci-1", "pme",      V_ALL)),
868c2ecf20Sopenharmony_ci	MPP_MODE(3,
878c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
888c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x2, "pci", "gnt3",       V_ALL)),
898c2ecf20Sopenharmony_ci	MPP_MODE(4,
908c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
918c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x2, "pci", "req4",       V_ALL),
928c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x4, "bootnand", "re",    V_5182 | V_5281),
938c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x5, "sata0", "prsnt",    V_5182)),
948c2ecf20Sopenharmony_ci	MPP_MODE(5,
958c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
968c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x2, "pci", "gnt4",       V_ALL),
978c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x4, "bootnand", "we",    V_5182 | V_5281),
988c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x5, "sata1", "prsnt",    V_5182)),
998c2ecf20Sopenharmony_ci	MPP_MODE(6,
1008c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
1018c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x2, "pci", "req5",       V_ALL),
1028c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x4, "nand", "re0",       V_5182 | V_5281),
1038c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x5, "pci-1", "clk",      V_5181),
1048c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x5, "sata0", "act",      V_5182)),
1058c2ecf20Sopenharmony_ci	MPP_MODE(7,
1068c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
1078c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x2, "pci", "gnt5",       V_ALL),
1088c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x4, "nand", "we0",       V_5182 | V_5281),
1098c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x5, "pci-1", "clk",      V_5181),
1108c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x5, "sata1", "act",      V_5182)),
1118c2ecf20Sopenharmony_ci	MPP_MODE(8,
1128c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
1138c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x1, "ge", "col",         V_ALL)),
1148c2ecf20Sopenharmony_ci	MPP_MODE(9,
1158c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
1168c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x1, "ge", "rxerr",       V_ALL)),
1178c2ecf20Sopenharmony_ci	MPP_MODE(10,
1188c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
1198c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x1, "ge", "crs",         V_ALL)),
1208c2ecf20Sopenharmony_ci	MPP_MODE(11,
1218c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
1228c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x1, "ge", "txerr",       V_ALL)),
1238c2ecf20Sopenharmony_ci	MPP_MODE(12,
1248c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
1258c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x1, "ge", "txd4",        V_ALL),
1268c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x4, "nand", "re1",       V_5182 | V_5281),
1278c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x5, "sata0", "ledprsnt", V_5182)),
1288c2ecf20Sopenharmony_ci	MPP_MODE(13,
1298c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
1308c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x1, "ge", "txd5",        V_ALL),
1318c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x4, "nand", "we1",       V_5182 | V_5281),
1328c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x5, "sata1", "ledprsnt", V_5182)),
1338c2ecf20Sopenharmony_ci	MPP_MODE(14,
1348c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
1358c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x1, "ge", "txd6",        V_ALL),
1368c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x4, "nand", "re2",       V_5182 | V_5281),
1378c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x5, "sata0", "ledact",   V_5182)),
1388c2ecf20Sopenharmony_ci	MPP_MODE(15,
1398c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "gpio", NULL,        V_ALL),
1408c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x1, "ge", "txd7",        V_ALL),
1418c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x4, "nand", "we2",       V_5182 | V_5281),
1428c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x5, "sata1", "ledact",   V_5182)),
1438c2ecf20Sopenharmony_ci	MPP_MODE(16,
1448c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "uart1", "rxd",      V_5182 | V_5281),
1458c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x1, "ge", "rxd4",        V_ALL),
1468c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x5, "gpio", NULL,        V_5182)),
1478c2ecf20Sopenharmony_ci	MPP_MODE(17,
1488c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "uart1", "txd",      V_5182 | V_5281),
1498c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x1, "ge", "rxd5",        V_ALL),
1508c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x5, "gpio", NULL,        V_5182)),
1518c2ecf20Sopenharmony_ci	MPP_MODE(18,
1528c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "uart1", "cts",      V_5182 | V_5281),
1538c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x1, "ge", "rxd6",        V_ALL),
1548c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x5, "gpio", NULL,        V_5182)),
1558c2ecf20Sopenharmony_ci	MPP_MODE(19,
1568c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x0, "uart1", "rts",      V_5182 | V_5281),
1578c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x1, "ge", "rxd7",        V_ALL),
1588c2ecf20Sopenharmony_ci		 MPP_VAR_FUNCTION(0x5, "gpio", NULL,        V_5182)),
1598c2ecf20Sopenharmony_ci};
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic const struct mvebu_mpp_ctrl orion_mpp_controls[] = {
1628c2ecf20Sopenharmony_ci	MPP_FUNC_CTRL(0, 19, NULL, orion_mpp_ctrl),
1638c2ecf20Sopenharmony_ci};
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic struct pinctrl_gpio_range mv88f5181_gpio_ranges[] = {
1668c2ecf20Sopenharmony_ci	MPP_GPIO_RANGE(0, 0, 0, 16),
1678c2ecf20Sopenharmony_ci};
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic struct pinctrl_gpio_range mv88f5182_gpio_ranges[] = {
1708c2ecf20Sopenharmony_ci	MPP_GPIO_RANGE(0, 0, 0, 19),
1718c2ecf20Sopenharmony_ci};
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic struct pinctrl_gpio_range mv88f5281_gpio_ranges[] = {
1748c2ecf20Sopenharmony_ci	MPP_GPIO_RANGE(0, 0, 0, 16),
1758c2ecf20Sopenharmony_ci};
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic struct mvebu_pinctrl_soc_info mv88f5181_info = {
1788c2ecf20Sopenharmony_ci	.variant = V_5181,
1798c2ecf20Sopenharmony_ci	.controls = orion_mpp_controls,
1808c2ecf20Sopenharmony_ci	.ncontrols = ARRAY_SIZE(orion_mpp_controls),
1818c2ecf20Sopenharmony_ci	.modes = orion_mpp_modes,
1828c2ecf20Sopenharmony_ci	.nmodes = ARRAY_SIZE(orion_mpp_modes),
1838c2ecf20Sopenharmony_ci	.gpioranges = mv88f5181_gpio_ranges,
1848c2ecf20Sopenharmony_ci	.ngpioranges = ARRAY_SIZE(mv88f5181_gpio_ranges),
1858c2ecf20Sopenharmony_ci};
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic struct mvebu_pinctrl_soc_info mv88f5182_info = {
1888c2ecf20Sopenharmony_ci	.variant = V_5182,
1898c2ecf20Sopenharmony_ci	.controls = orion_mpp_controls,
1908c2ecf20Sopenharmony_ci	.ncontrols = ARRAY_SIZE(orion_mpp_controls),
1918c2ecf20Sopenharmony_ci	.modes = orion_mpp_modes,
1928c2ecf20Sopenharmony_ci	.nmodes = ARRAY_SIZE(orion_mpp_modes),
1938c2ecf20Sopenharmony_ci	.gpioranges = mv88f5182_gpio_ranges,
1948c2ecf20Sopenharmony_ci	.ngpioranges = ARRAY_SIZE(mv88f5182_gpio_ranges),
1958c2ecf20Sopenharmony_ci};
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic struct mvebu_pinctrl_soc_info mv88f5281_info = {
1988c2ecf20Sopenharmony_ci	.variant = V_5281,
1998c2ecf20Sopenharmony_ci	.controls = orion_mpp_controls,
2008c2ecf20Sopenharmony_ci	.ncontrols = ARRAY_SIZE(orion_mpp_controls),
2018c2ecf20Sopenharmony_ci	.modes = orion_mpp_modes,
2028c2ecf20Sopenharmony_ci	.nmodes = ARRAY_SIZE(orion_mpp_modes),
2038c2ecf20Sopenharmony_ci	.gpioranges = mv88f5281_gpio_ranges,
2048c2ecf20Sopenharmony_ci	.ngpioranges = ARRAY_SIZE(mv88f5281_gpio_ranges),
2058c2ecf20Sopenharmony_ci};
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci/*
2088c2ecf20Sopenharmony_ci * There are multiple variants of the Orion SoCs, but in terms of pin
2098c2ecf20Sopenharmony_ci * muxing, they are identical.
2108c2ecf20Sopenharmony_ci */
2118c2ecf20Sopenharmony_cistatic const struct of_device_id orion_pinctrl_of_match[] = {
2128c2ecf20Sopenharmony_ci	{ .compatible = "marvell,88f5181-pinctrl", .data = &mv88f5181_info },
2138c2ecf20Sopenharmony_ci	{ .compatible = "marvell,88f5181l-pinctrl", .data = &mv88f5181_info },
2148c2ecf20Sopenharmony_ci	{ .compatible = "marvell,88f5182-pinctrl", .data = &mv88f5182_info },
2158c2ecf20Sopenharmony_ci	{ .compatible = "marvell,88f5281-pinctrl", .data = &mv88f5281_info },
2168c2ecf20Sopenharmony_ci	{ }
2178c2ecf20Sopenharmony_ci};
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic int orion_pinctrl_probe(struct platform_device *pdev)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	const struct of_device_id *match =
2228c2ecf20Sopenharmony_ci		of_match_device(orion_pinctrl_of_match, &pdev->dev);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	pdev->dev.platform_data = (void*)match->data;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	mpp_base = devm_platform_ioremap_resource(pdev, 0);
2278c2ecf20Sopenharmony_ci	if (IS_ERR(mpp_base))
2288c2ecf20Sopenharmony_ci		return PTR_ERR(mpp_base);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	high_mpp_base = devm_platform_ioremap_resource(pdev, 1);
2318c2ecf20Sopenharmony_ci	if (IS_ERR(high_mpp_base))
2328c2ecf20Sopenharmony_ci		return PTR_ERR(high_mpp_base);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	return mvebu_pinctrl_probe(pdev);
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic struct platform_driver orion_pinctrl_driver = {
2388c2ecf20Sopenharmony_ci	.driver = {
2398c2ecf20Sopenharmony_ci		.name = "orion-pinctrl",
2408c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(orion_pinctrl_of_match),
2418c2ecf20Sopenharmony_ci	},
2428c2ecf20Sopenharmony_ci	.probe = orion_pinctrl_probe,
2438c2ecf20Sopenharmony_ci};
2448c2ecf20Sopenharmony_cibuiltin_platform_driver(orion_pinctrl_driver);
245