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