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