The mb86s70 and mb86s73 Fujitsu SoCs differ in that the latter provide a pinmux. GPIOs are supported on top of Pinctrl api. Signed-off-by: Jassi Brar <jaswinder.singh@xxxxxxxxxx> Cc: Linus Walleij <linus.walleij@xxxxxxxxxx> Signed-off-by: Tetsuya Takinishi <t.takinishi@xxxxxxxxxxxxxx> Signed-off-by: Mollie Wu <mollie.wu@xxxxxxxxxx> --- .../bindings/gpio/fujitsu,mb86s7x-gpio.txt | 22 + .../bindings/pinctrl/fujitsu,mb86s7x-pinctrl.txt | 30 + drivers/pinctrl/Kconfig | 5 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-mb86s7x.c | 1281 ++++++++++++++++++++ 5 files changed, 1339 insertions(+) create mode 100644 Documentation/devicetree/bindings/gpio/fujitsu,mb86s7x-gpio.txt create mode 100644 Documentation/devicetree/bindings/pinctrl/fujitsu,mb86s7x-pinctrl.txt create mode 100644 drivers/pinctrl/pinctrl-mb86s7x.c diff --git a/Documentation/devicetree/bindings/gpio/fujitsu,mb86s7x-gpio.txt b/Documentation/devicetree/bindings/gpio/fujitsu,mb86s7x-gpio.txt new file mode 100644 index 0000000..c262efa --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/fujitsu,mb86s7x-gpio.txt @@ -0,0 +1,22 @@ +Fujitsu MB86S7x GPIO Controller +------------------------------- + +Required properties: +- compatible: Should be "fujitsu,mb86s7x-gpio" +- gpio-controller: Marks the device node as a gpio controller. +- #gpio-cells: Should be <2>. The first cell is the pin number and the + second cell is used to specify optional parameters: + - bit 0 specifies polarity (0 for normal, 1 for inverted). + +GPIO ranges are specified as described in +Documentation/devicetree/bindings/gpio/gpio.txt + +Examples: + gpio0: mb86s70_gpio0 { + compatible = "fujitsu,mb86s7x-gpio"; + reg = <0 0x31000000 0x10000>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&pinctrl 0 0 32>; + clocks = <&clk_alw_2_1>; + }; diff --git a/Documentation/devicetree/bindings/pinctrl/fujitsu,mb86s7x-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/fujitsu,mb86s7x-pinctrl.txt new file mode 100644 index 0000000..ce2011b --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/fujitsu,mb86s7x-pinctrl.txt @@ -0,0 +1,30 @@ +Fujitsu MB86S7x Pin Controller +------------------------------ + +Required properties: +- compatible: Should be "fujitsu,mb86s70-pinctrl" or "fujitsu,mb86s73-pinctrl" +- reg: Should contain the register physical address and length for the + pin controller. +- #gpio-range-cells: Should be 3 + +Optional subnode-properties: +- mb86s7x,function: String specifying the function name. +- mb86s7x,drvst: Drive strength needed for pins to operate in that mode. + 0 (Hi-z), 2mA, 4mA, 6mA or 8mA +- mb86s7x,pullup: Should be 0 for Pull-Down or non-zero for Pull-Up + +Examples: + pinctrl: pinctrl@2a4d0000 { + compatible = "fujitsu,mb86s73-pinctrl"; + reg = <0 0x2a4d0000 0x1000>, <0 0x312e0000 0x1000>; + #gpio-range-cells = <3>; + + hsspi0_pins_active: hsspi0_active { + mb86s7x,function = "hsspi0"; + mb86s7x,drvst = <8>; /* in mA */ + }; + hsspi0_pins_sleep: hsspi0_sleep { + mb86s7x,function = "hsspi0"; + mb86s7x,drvst = <0>; /* Implies Hi-z */ + }; + }; diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 0042ccb..1f053fc 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -213,6 +213,11 @@ config PINCTRL_FALCON depends on SOC_FALCON depends on PINCTRL_LANTIQ +config PINCTRL_MB86S7X + bool + select PINMUX + select GENERIC_PINCONF + config PINCTRL_MXS bool select PINMUX diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index c4b5d40..edcb823 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_PINCTRL_IMX6Q) += pinctrl-imx6dl.o obj-$(CONFIG_PINCTRL_IMX6SL) += pinctrl-imx6sl.o obj-$(CONFIG_PINCTRL_IMX6SX) += pinctrl-imx6sx.o obj-$(CONFIG_PINCTRL_FALCON) += pinctrl-falcon.o +obj-$(CONFIG_PINCTRL_MB86S7X) += pinctrl-mb86s7x.o obj-$(CONFIG_PINCTRL_MXS) += pinctrl-mxs.o obj-$(CONFIG_PINCTRL_IMX23) += pinctrl-imx23.o obj-$(CONFIG_PINCTRL_IMX25) += pinctrl-imx25.o diff --git a/drivers/pinctrl/pinctrl-mb86s7x.c b/drivers/pinctrl/pinctrl-mb86s7x.c new file mode 100644 index 0000000..74c72ec8 --- /dev/null +++ b/drivers/pinctrl/pinctrl-mb86s7x.c @@ -0,0 +1,1281 @@ +/* + * linux/drivers/pinctrl/pinctrl-mb86s7x.c + * + * Copyright (C) 2014 Fujitsu Semiconductor Limited + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/io.h> +#include <linux/init.h> +#include <linux/clk.h> +#include <linux/gpio.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pinctrl/consumer.h> +#include <linux/pinctrl/machine.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinconf-generic.h> +#include <linux/pinctrl/pinmux.h> +#include <linux/spinlock.h> +#include <linux/slab.h> + +#define PDR(x) (0x0 + x) +#define DDR(x) (0x10 + x) +#define PFR(x) (0x20 + x) + +#define CDRV_PDG(x) (0x0 + ((x) / 16) * 4) +#define FORCEZ_PDG(x) (0x100 + ((x) / 16) * 4) +#define PULL_PDG(x) (0x200 + ((x) / 16) * 4) +#define PIN_OFF(x) (((x) % 16) * 2) + +#define PMUX_MB86S70 0 +#define PMUX_MB86S73 1 + +/* Virtual pin numbers start so that reg offset calculations get simple */ +enum { + PINS_VIRT = 128, + PINS_PCIE0 = PINS_VIRT, + PINS_HOLE1, + PINS_USB3H0, + PINS_HOLE2, + PINS_USB2H0, + PINS_USB2D0, + PINS_SDH30, + PINS_HOLE3, + PINS_EMMC0, + PINS_HSSPI0, + PINS_GMACD0, + PINS_GMACM0, + PINS_I2S0, + PINS_UART0, + PINS_OTHER0, + PINS_JTAG0, + PINS_PCIE1, + PINS_HOLE4, + PINS_USB3H1, + MB86S70_PMUX_PINS, +}; + +struct mb86s70_gpio_chip { + spinlock_t lock; /* for goio regs */ + struct clk *clk; + void __iomem *base; + struct gpio_chip gc; +}; + +static int mb86s70_gpio_request(struct gpio_chip *gc, unsigned offset) +{ + int ret = pinctrl_request_gpio(gc->base + offset); + + if (!ret) { + struct mb86s70_gpio_chip *gchip = container_of(gc, + struct mb86s70_gpio_chip, gc); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&gchip->lock, flags); + val = readl(gchip->base + PFR(offset / 8 * 4)); + val &= ~(1 << (offset % 8)); /* as gpio-port */ + writel(val, gchip->base + PFR(offset / 8 * 4)); + spin_unlock_irqrestore(&gchip->lock, flags); + } + + return ret; +} + +static void mb86s70_gpio_free(struct gpio_chip *gc, unsigned offset) +{ + struct mb86s70_gpio_chip *gchip = container_of(gc, + struct mb86s70_gpio_chip, gc); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&gchip->lock, flags); + val = readl(gchip->base + PFR(offset / 8 * 4)); + val |= (1 << (offset % 8)); /* as peri-port */ + writel(val, gchip->base + PFR(offset / 8 * 4)); + spin_unlock_irqrestore(&gchip->lock, flags); + + pinctrl_free_gpio(gc->base + offset); +} + +static int mb86s70_gpio_direction_input(struct gpio_chip *gc, unsigned offset) +{ + struct mb86s70_gpio_chip *gchip = + container_of(gc, struct mb86s70_gpio_chip, gc); + unsigned long flags; + unsigned char val; + + spin_lock_irqsave(&gchip->lock, flags); + val = readl(gchip->base + DDR(offset / 8 * 4)); + val &= ~(1 << (offset % 8)); + writel(val, gchip->base + DDR(offset / 8 * 4)); + spin_unlock_irqrestore(&gchip->lock, flags); + + return 0; +} + +static int mb86s70_gpio_direction_output(struct gpio_chip *gc, + unsigned offset, int value) +{ + struct mb86s70_gpio_chip *gchip = + container_of(gc, struct mb86s70_gpio_chip, gc); + unsigned long flags; + unsigned char val; + + spin_lock_irqsave(&gchip->lock, flags); + + val = readl(gchip->base + PDR(offset / 8 * 4)); + if (value) + val |= (1 << (offset % 8)); + else + val &= ~(1 << (offset % 8)); + writel(val, gchip->base + PDR(offset / 8 * 4)); + + val = readl(gchip->base + DDR(offset / 8 * 4)); + val |= (1 << (offset % 8)); + writel(val, gchip->base + DDR(offset / 8 * 4)); + + spin_unlock_irqrestore(&gchip->lock, flags); + + return 0; +} + +static int mb86s70_gpio_get(struct gpio_chip *gc, unsigned offset) +{ + struct mb86s70_gpio_chip *gchip = + container_of(gc, struct mb86s70_gpio_chip, gc); + unsigned char val; + + val = readl(gchip->base + PDR(offset / 8 * 4)); + val &= (1 << (offset % 8)); + return val ? 1 : 0; +} + +static void mb86s70_gpio_set(struct gpio_chip *gc, unsigned offset, int value) +{ + struct mb86s70_gpio_chip *gchip = + container_of(gc, struct mb86s70_gpio_chip, gc); + unsigned long flags; + unsigned char val; + + spin_lock_irqsave(&gchip->lock, flags); + + val = readl(gchip->base + PDR(offset / 8 * 4)); + if (value) + val |= (1 << (offset % 8)); + else + val &= ~(1 << (offset % 8)); + writel(val, gchip->base + PDR(offset / 8 * 4)); + + spin_unlock_irqrestore(&gchip->lock, flags); +} + +static int mb86s70_gpio_probe(struct platform_device *pdev) +{ + struct mb86s70_gpio_chip *gchip; + struct resource *res; + int ret; + + gchip = devm_kzalloc(&pdev->dev, sizeof(*gchip), GFP_KERNEL); + if (gchip == NULL) + return -ENOMEM; + + platform_set_drvdata(pdev, gchip); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + gchip->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(gchip->base)) + return PTR_ERR(gchip->base); + + gchip->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(gchip->clk)) + return PTR_ERR(gchip->clk); + + clk_prepare_enable(gchip->clk); + + spin_lock_init(&gchip->lock); + + gchip->gc.direction_output = mb86s70_gpio_direction_output; + gchip->gc.direction_input = mb86s70_gpio_direction_input; + gchip->gc.request = mb86s70_gpio_request; + gchip->gc.free = mb86s70_gpio_free; + gchip->gc.get = mb86s70_gpio_get; + gchip->gc.set = mb86s70_gpio_set; + gchip->gc.label = dev_name(&pdev->dev); + gchip->gc.ngpio = 32; + gchip->gc.owner = THIS_MODULE; + gchip->gc.dev = &pdev->dev; + gchip->gc.base = -1; + + ret = gpiochip_add(&gchip->gc); + if (ret) { + dev_err(&pdev->dev, "couldn't register gpio driver\n"); + clk_disable_unprepare(gchip->clk); + } + + return ret; +} + +struct mb86s70_pmux_chip { + spinlock_t lock; /* mux regs */ + struct device *dev; + void __iomem *base; + void __iomem *conf_base; + struct pinctrl_dev *pctl; + struct pinctrl_desc desc; + bool pin_busy[MB86S70_PMUX_PINS]; + char pin_names[5 * MB86S70_PMUX_PINS]; + struct pinctrl_pin_desc pins[MB86S70_PMUX_PINS]; +}; + +struct mb86s70_pmx_grp { + const char *name; + const unsigned *pins; + const unsigned num_pins; +}; + +struct mb86s70_pmx_function { + const unsigned num_groups; + unsigned prog_val; + const char *name; + + const char *const *groups; +}; + +static const unsigned extint16_s70[] = {0}; +static const unsigned tsif0_s70[] = {16, 17, 18, 19}; +static const unsigned tsif1_s70[] = {16, 17, 18, 19}; +static const unsigned uart1_s70[] = {56, 57, 58, 59}; +static const unsigned uart2_s70[] = {60, 61, 62, 63}; +static const unsigned pl244_s70[] = {40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62}; +static const unsigned trace_s70[] = {24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 64, 65}; +static const unsigned memcs_s70[] = {16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, + 61, 62, 63, 64, 65}; +static const unsigned cap_s70[] = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47}; + +static const struct mb86s70_pmx_grp mb86s70_pgrps[] = { + { + .name = "extint16_grp", + .pins = extint16_s70, + .num_pins = ARRAY_SIZE(extint16_s70), + }, + { + .name = "tsif0_grp", + .pins = tsif0_s70, + .num_pins = ARRAY_SIZE(tsif0_s70), + }, + { + .name = "tsif1_grp", + .pins = tsif1_s70, + .num_pins = ARRAY_SIZE(tsif1_s70), + }, + { + .name = "uart1_grp", + .pins = uart1_s70, + .num_pins = ARRAY_SIZE(uart1_s70), + }, + { + .name = "uart2_grp", + .pins = uart2_s70, + .num_pins = ARRAY_SIZE(uart2_s70), + }, + { + .name = "pl244_grp", + .pins = pl244_s70, + .num_pins = ARRAY_SIZE(pl244_s70), + }, + { + .name = "trace_grp", + .pins = trace_s70, + .num_pins = ARRAY_SIZE(trace_s70), + }, + { + .name = "memcs_grp", + .pins = memcs_s70, + .num_pins = ARRAY_SIZE(memcs_s70), + }, + { + .name = "cap_grp", + .pins = cap_s70, + .num_pins = ARRAY_SIZE(cap_s70), + } +}; + +static const unsigned pcie0_s73[] = {PINS_PCIE0}; +static const unsigned usb3h0_s73[] = {PINS_USB3H0}; +static const unsigned usb2h0_s73[] = {PINS_USB2H0}; +static const unsigned usb2d0_s73[] = {PINS_USB2D0}; +static const unsigned sdh30_s73[] = {PINS_SDH30}; +static const unsigned emmc0_s73[] = {PINS_EMMC0}; +static const unsigned hsspi0_s73[] = {PINS_HSSPI0}; +static const unsigned gmacd0_s73[] = {PINS_GMACD0}; +static const unsigned gmacm0_s73[] = {PINS_GMACM0}; +static const unsigned i2s0_s73[] = {PINS_I2S0}; +static const unsigned other0_s73[] = {PINS_OTHER0}; +static const unsigned jtag0_s73[] = {PINS_JTAG0}; +static const unsigned pcie1_s73[] = {PINS_PCIE1}; +static const unsigned usb3h1_s73[] = {PINS_USB3H1}; +static const unsigned extint5_s73[] = {37}; +static const unsigned cfg_s73[] = {56, 57, 58, 59, 60, 61, 62, 63}; +static const unsigned uart0_s73[] = {8, 11, 12, 15}; +static const unsigned uart1_s73[] = {16, 17, 18, 19, 20, 21, 22, 23}; +static const unsigned uart2_s73[] = {24, 25, 26, 27, 28, 29, 30, 31}; +static const unsigned trace_s73[] = {44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61}; +static const unsigned smt_s73[] = {56, 57, 58, 59, 60, 61}; +static const unsigned memcs_s73[] = {8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55}; +static const unsigned pl244_s73[] = {8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, + 30, 31}; + +static const struct mb86s70_pmx_grp mb86s73_pgrps[] = { + { + .name = "extint5_grp", + .pins = extint5_s73, + .num_pins = ARRAY_SIZE(extint5_s73), + }, + { + .name = "pcie0_grp", + .pins = pcie0_s73, + .num_pins = ARRAY_SIZE(pcie0_s73), + }, + { + .name = "usb3h0_grp", + .pins = usb3h0_s73, + .num_pins = ARRAY_SIZE(usb3h0_s73), + }, + { + .name = "usb2h0_grp", + .pins = usb2h0_s73, + .num_pins = ARRAY_SIZE(usb2h0_s73), + }, + { + .name = "usb2d0_grp", + .pins = usb2d0_s73, + .num_pins = ARRAY_SIZE(usb2d0_s73), + }, + { + .name = "sdh30_grp", + .pins = sdh30_s73, + .num_pins = ARRAY_SIZE(sdh30_s73), + }, + { + .name = "emmc0_grp", + .pins = emmc0_s73, + .num_pins = ARRAY_SIZE(emmc0_s73), + }, + { + .name = "hsspi0_grp", + .pins = hsspi0_s73, + .num_pins = ARRAY_SIZE(hsspi0_s73), + }, + { + .name = "gmacd0_grp", + .pins = gmacd0_s73, + .num_pins = ARRAY_SIZE(gmacd0_s73), + }, + { + .name = "gmacm0_grp", + .pins = gmacm0_s73, + .num_pins = ARRAY_SIZE(gmacm0_s73), + }, + { + .name = "i2s0_grp", + .pins = i2s0_s73, + .num_pins = ARRAY_SIZE(i2s0_s73), + }, + { + .name = "other0_grp", + .pins = other0_s73, + .num_pins = ARRAY_SIZE(other0_s73), + }, + { + .name = "jtag0_grp", + .pins = jtag0_s73, + .num_pins = ARRAY_SIZE(jtag0_s73), + }, + { + .name = "pcie1_grp", + .pins = pcie1_s73, + .num_pins = ARRAY_SIZE(pcie1_s73), + }, + { + .name = "usb3h1_grp", + .pins = usb3h1_s73, + .num_pins = ARRAY_SIZE(usb3h1_s73), + }, + { + .name = "cfg_grp", + .pins = cfg_s73, + .num_pins = ARRAY_SIZE(cfg_s73), + }, + { + .name = "uart0_grp", + .pins = uart0_s73, + .num_pins = ARRAY_SIZE(uart0_s73), + }, + { + .name = "uart1_grp", + .pins = uart1_s73, + .num_pins = ARRAY_SIZE(uart1_s73), + }, + { + .name = "uart2_grp", + .pins = uart2_s73, + .num_pins = ARRAY_SIZE(uart2_s73), + }, + { + .name = "trace_grp", + .pins = trace_s73, + .num_pins = ARRAY_SIZE(trace_s73), + }, + { + .name = "smt_grp", + .pins = smt_s73, + .num_pins = ARRAY_SIZE(smt_s73), + }, + { + .name = "memcs_grp", + .pins = memcs_s73, + .num_pins = ARRAY_SIZE(memcs_s73), + }, + { + .name = "pl244_grp", + .pins = pl244_s73, + .num_pins = ARRAY_SIZE(pl244_s73), + }, +}; + +static int func_count, grp_count; +static const struct mb86s70_pmx_grp *mb86s7x_pgrps; +static const struct mb86s70_pmx_function *mb86s7x_pmx_funcs; + +static int mb86s70_pctrl_get_groups_count(struct pinctrl_dev *pctl) +{ + return grp_count; +} + +static const char * +mb86s70_pctrl_get_group_name(struct pinctrl_dev *pctl, unsigned selector) +{ + return mb86s7x_pgrps[selector].name; +} + +static int +mb86s70_pctrl_get_group_pins(struct pinctrl_dev *pctl, unsigned selector, + const unsigned **pins, unsigned *num_pins) +{ + *pins = mb86s7x_pgrps[selector].pins; + *num_pins = mb86s7x_pgrps[selector].num_pins; + + return 0; +} + +static int +mb86s70_pctrl_dt_node_to_map(struct pinctrl_dev *pctl, + struct device_node *node, + struct pinctrl_map **map, unsigned *num_maps) +{ + struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl); + int ret, i, configlen = 0; + unsigned long *pinconfig; + const char *function; + const char *group; + u32 val; + + *map = NULL; + *num_maps = 0; + + ret = of_property_read_string(node, "mb86s7x,function", &function); + if (ret) { + dev_err(pchip->dev, + "missing mb86s7x,function property in node %s\n", + node->name); + return -EINVAL; + } + + *map = devm_kzalloc(pchip->dev, + 2 * sizeof(struct pinctrl_map), GFP_KERNEL); + if (!*map) + return -ENOMEM; + + for (i = 0; i < func_count; i++) + if (!strcmp(mb86s7x_pmx_funcs[i].name, function)) + break; + + if (i == func_count) { + dev_err(pchip->dev, "function(%s) not found!\n", function); + return -EINVAL; + } + group = mb86s7x_pmx_funcs[i].groups[0]; + + (*map)[0].type = PIN_MAP_TYPE_MUX_GROUP; + (*map)[0].data.mux.group = group; + (*map)[0].data.mux.function = function; + *num_maps = 1; + + if (of_find_property(node, "mb86s7x,drvst", NULL)) + configlen++; + if (of_find_property(node, "mb86s7x,pullup", NULL)) + configlen++; + + if (configlen == 0) /* For S70 */ + return 0; + + i = 0; + pinconfig = devm_kzalloc(pchip->dev, + configlen * sizeof(*pinconfig), GFP_KERNEL); + + if (!of_property_read_u32(node, "mb86s7x,drvst", &val)) + pinconfig[i++] = + pinconf_to_config_packed(PIN_CONFIG_DRIVE_STRENGTH, + val); + + if (!of_property_read_u32(node, "mb86s7x,pullup", &val)) { + enum pin_config_param pull; + + if (val) + pull = PIN_CONFIG_BIAS_PULL_UP; + else + pull = PIN_CONFIG_BIAS_PULL_DOWN; + pinconfig[i++] = pinconf_to_config_packed(pull, 0); + } + + (*map)[1].type = PIN_MAP_TYPE_CONFIGS_GROUP; + (*map)[1].data.configs.group_or_pin = group; + (*map)[1].data.configs.configs = pinconfig; + (*map)[1].data.configs.num_configs = configlen; + + *num_maps = 2; + + return 0; +} + +static void +mb86s70_pctrl_dt_free_map(struct pinctrl_dev *pctl, + struct pinctrl_map *map, unsigned num_maps) +{ + struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl); + int i; + + for (i = 0; i < num_maps; i++) + if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP) + devm_kfree(pchip->dev, map[i].data.configs.configs); + + devm_kfree(pchip->dev, map); +} + +static const struct pinctrl_ops mb86s70_pctrl_ops = { + .get_groups_count = mb86s70_pctrl_get_groups_count, + .get_group_name = mb86s70_pctrl_get_group_name, + .get_group_pins = mb86s70_pctrl_get_group_pins, + .dt_node_to_map = mb86s70_pctrl_dt_node_to_map, + .dt_free_map = mb86s70_pctrl_dt_free_map, +}; + +static const char * const pcie0_groups[] = {"pcie0_grp"}; +static const char * const usb3h0_groups[] = {"usb3h0_grp"}; +static const char * const usb2h0_groups[] = {"usb2h0_grp"}; +static const char * const usb2d0_groups[] = {"usb2d0_grp"}; +static const char * const sdh30_groups[] = {"sdh30_grp"}; +static const char * const emmc0_groups[] = {"emmc0_grp"}; +static const char * const hsspi0_groups[] = {"hsspi0_grp"}; +static const char * const gmacd0_groups[] = {"gmacd0_grp"}; +static const char * const gmacm0_groups[] = {"gmacm0_grp"}; +static const char * const i2s0_groups[] = {"i2s0_grp"}; +static const char * const other0_groups[] = {"other0_grp"}; +static const char * const jtag0_groups[] = {"jtag0_grp"}; +static const char * const pcie1_groups[] = {"pcie1_grp"}; +static const char * const usb3h1_groups[] = {"usb3h1_grp"}; +static const char * const extint16_groups[] = {"extint16_grp"}; +static const char * const extint5_groups[] = {"extint5_grp"}; +static const char * const tsif0_groups[] = {"tsif0_grp"}; +static const char * const tsif1_groups[] = {"tsif1_grp"}; +static const char * const cfg_groups[] = {"cfg_grp"}; +static const char * const uart0_groups[] = {"uart0_grp"}; +static const char * const uart1_groups[] = {"uart1_grp"}; +static const char * const uart2_groups[] = {"uart2_grp"}; +static const char * const pl244_groups[] = {"pl244_grp"}; +static const char * const trace_groups[] = {"trace_grp"}; +static const char * const memcs_groups[] = {"memcs_grp"}; +static const char * const cap_groups[] = {"cap_grp"}; +static const char * const smt_groups[] = {"smt_grp"}; + +static const struct mb86s70_pmx_function mb86s73_pmx_funcs[] = { + { + .prog_val = 0x1, + .name = "extint5", + .groups = extint5_groups, + .num_groups = ARRAY_SIZE(extint5_groups), + }, + { + .prog_val = 0x0, + .name = "pcie0", + .groups = pcie0_groups, + .num_groups = ARRAY_SIZE(pcie0_groups), + }, + { + .prog_val = 0x0, + .name = "usb3h0", + .groups = usb3h0_groups, + .num_groups = ARRAY_SIZE(usb3h0_groups), + }, + { + .prog_val = 0x0, + .name = "usb2h0", + .groups = usb2h0_groups, + .num_groups = ARRAY_SIZE(usb2h0_groups), + }, + { + .prog_val = 0x0, + .name = "usb2d0", + .groups = usb2d0_groups, + .num_groups = ARRAY_SIZE(usb2d0_groups), + }, + { + .prog_val = 0x0, + .name = "sdh30", + .groups = sdh30_groups, + .num_groups = ARRAY_SIZE(sdh30_groups), + }, + { + .prog_val = 0x0, + .name = "emmc0", + .groups = emmc0_groups, + .num_groups = ARRAY_SIZE(emmc0_groups), + }, + { + .prog_val = 0x0, + .name = "hsspi0", + .groups = hsspi0_groups, + .num_groups = ARRAY_SIZE(hsspi0_groups), + }, + { + .prog_val = 0x0, + .name = "gmacd0", + .groups = gmacd0_groups, + .num_groups = ARRAY_SIZE(gmacd0_groups), + }, + { + .prog_val = 0x0, + .name = "gmacm0", + .groups = gmacm0_groups, + .num_groups = ARRAY_SIZE(gmacm0_groups), + }, + { + .prog_val = 0x0, + .name = "i2s0", + .groups = i2s0_groups, + .num_groups = ARRAY_SIZE(i2s0_groups), + }, + { + .prog_val = 0x0, + .name = "other0", + .groups = other0_groups, + .num_groups = ARRAY_SIZE(other0_groups), + }, + { + .prog_val = 0x0, + .name = "jtag0", + .groups = jtag0_groups, + .num_groups = ARRAY_SIZE(jtag0_groups), + }, + { + .prog_val = 0x0, + .name = "pcie1", + .groups = pcie1_groups, + .num_groups = ARRAY_SIZE(pcie1_groups), + }, + { + .prog_val = 0x0, + .name = "usb3h1", + .groups = usb3h1_groups, + .num_groups = ARRAY_SIZE(usb3h1_groups), + }, + { + .prog_val = 0x1, + .name = "cfg", + .groups = cfg_groups, + .num_groups = ARRAY_SIZE(cfg_groups), + }, + { + .prog_val = 0x1, + .name = "uart0", + .groups = uart0_groups, + .num_groups = ARRAY_SIZE(uart0_groups), + }, + { + .prog_val = 0x1, + .name = "uart1", + .groups = uart1_groups, + .num_groups = ARRAY_SIZE(uart1_groups), + }, + { + .prog_val = 0x1, + .name = "uart2", + .groups = uart2_groups, + .num_groups = ARRAY_SIZE(uart2_groups), + }, + { + .prog_val = 0x2, + .name = "trace", + .groups = trace_groups, + .num_groups = ARRAY_SIZE(trace_groups), + }, + { + .prog_val = 0x2, + .name = "pl244", + .groups = pl244_groups, + .num_groups = ARRAY_SIZE(pl244_groups), + }, + { + .prog_val = 0x3, + .name = "smt", + .groups = smt_groups, + .num_groups = ARRAY_SIZE(smt_groups), + }, + { + .prog_val = 0x3, + .name = "memcs", + .groups = memcs_groups, + .num_groups = ARRAY_SIZE(memcs_groups), + }, +}; + +static const struct mb86s70_pmx_function mb86s70_pmx_funcs[] = { + { + .prog_val = 0x2, + .name = "extint16", + .groups = extint16_groups, + .num_groups = ARRAY_SIZE(extint16_groups), + }, + { + .prog_val = 0x3, + .name = "tsif0", + .groups = tsif0_groups, + .num_groups = ARRAY_SIZE(tsif0_groups), + }, + { + .prog_val = 0x3, + .name = "tsif1", + .groups = tsif1_groups, + .num_groups = ARRAY_SIZE(tsif1_groups), + }, + { + .prog_val = 0x2, + .name = "uart1", + .groups = uart1_groups, + .num_groups = ARRAY_SIZE(uart1_groups), + }, + { + .prog_val = 0x2, + .name = "uart2", + .groups = uart2_groups, + .num_groups = ARRAY_SIZE(uart2_groups), + }, + { + .prog_val = 0x5, + .name = "pl244", + .groups = pl244_groups, + .num_groups = ARRAY_SIZE(pl244_groups), + }, + { + .prog_val = 0x3, + .name = "trace", + .groups = trace_groups, + .num_groups = ARRAY_SIZE(trace_groups), + }, + { + .prog_val = 0x4, + .name = "memcs", + .groups = memcs_groups, + .num_groups = ARRAY_SIZE(memcs_groups), + }, + { + .prog_val = 0x2, + .name = "capture", + .groups = cap_groups, + .num_groups = ARRAY_SIZE(cap_groups), + } +}; + +static int +mb86s70_pmx_get_functions_count(struct pinctrl_dev *pctl) +{ + return func_count; +} + +static const char * +mb86s70_pmx_get_function_name(struct pinctrl_dev *pctl, unsigned selector) +{ + return mb86s7x_pmx_funcs[selector].name; +} + +static int +mb86s70_pmx_get_function_groups(struct pinctrl_dev *pctl, + unsigned selector, const char * const **groups, + unsigned * const num_groups) +{ + *groups = mb86s7x_pmx_funcs[selector].groups; + *num_groups = mb86s7x_pmx_funcs[selector].num_groups; + + return 0; +} + +static int +mb86s70_pmx_enable(struct pinctrl_dev *pctl, + unsigned func_selector, unsigned group_selector) +{ + struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl); + unsigned long flags; + const unsigned *pins; + int i, j; + + if (group_selector >= grp_count) { + pr_err("%s:%d\n", __func__, __LINE__); + return -EINVAL; + } + + j = mb86s7x_pgrps[group_selector].num_pins; + pins = mb86s7x_pgrps[group_selector].pins; + + spin_lock_irqsave(&pchip->lock, flags); + + /* Busy if any pin in the same 'bunch' is taken */ + for (i = 0; i < j; i++) { + u32 val; + int p = pins[i] / 4 * 4; + + if (pins[i] >= PINS_VIRT) /* Not for virtual pins */ + continue; + + val = readl(pchip->base + p); + /* skip if no change needed */ + if (val == mb86s7x_pmx_funcs[func_selector].prog_val) + continue; + + if (pchip->pin_busy[p]) { + pr_err("%s:%d:%d %d busy\n", + __func__, __LINE__, pins[i], p); + goto busy_exit; + } + + if (pchip->pin_busy[p + 1]) { + pr_err("%s:%d:%d %d busy\n", + __func__, __LINE__, pins[i], p+1); + goto busy_exit; + } + + if (p == 64) + continue; + + if (pchip->pin_busy[p + 2]) { + pr_err("%s:%d:%d %d busy\n", + __func__, __LINE__, pins[i], p+2); + goto busy_exit; + } + + if (pchip->pin_busy[p + 3]) { + pr_err("%s:%d:%d %d busy\n", + __func__, __LINE__, pins[i], p+3); + goto busy_exit; + } + + continue; +busy_exit: + spin_unlock_irqrestore(&pchip->lock, flags); + pr_err("%s:%d Take a look!\n", __func__, __LINE__); + return -EBUSY; + } + + pr_debug("Going to enable %s on pins -", + mb86s7x_pmx_funcs[func_selector].name); + for (i = 0; i < j; i++) { + int p = pins[i]; + + pr_debug(" %d", p); + pchip->pin_busy[p] = true; + if (p < PINS_VIRT) /* Not for virtual pins */ + writel(mb86s7x_pmx_funcs[func_selector].prog_val, + pchip->base + p / 4 * 4); + } + + spin_unlock_irqrestore(&pchip->lock, flags); + pr_debug("\n"); + + return 0; +} + +static void +mb86s70_pmx_disable(struct pinctrl_dev *pctl, + unsigned func_selector, unsigned group_selector) +{ + struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl); + unsigned long flags; + const unsigned *pins; + int i, j; + + if (group_selector >= grp_count) { + pr_err("%s:%d\n", __func__, __LINE__); + return; + } + + j = mb86s7x_pgrps[group_selector].num_pins; + pins = mb86s7x_pgrps[group_selector].pins; + + pr_debug("Going to disable %s on pins -", + mb86s7x_pmx_funcs[func_selector].name); + spin_lock_irqsave(&pchip->lock, flags); + for (i = 0; i < j; i++) { + pr_debug(" %d", pins[i]); + pchip->pin_busy[pins[i]] = false; + } + spin_unlock_irqrestore(&pchip->lock, flags); + pr_debug("\n"); +} + +static int +mb86s70_pmx_gpio_set_direction(struct pinctrl_dev *pctl, + struct pinctrl_gpio_range *range, + unsigned pin, bool input) +{ + struct gpio_chip *gc = range->gc; + struct mb86s70_gpio_chip *gchip = container_of(gc, + struct mb86s70_gpio_chip, gc); + unsigned long flags; + u32 off, bit, val; + + if (pin >= 64) + return -EINVAL; + + spin_lock_irqsave(&gchip->lock, flags); + bit = (pin - gc->base) % 8; + off = (pin - gc->base) / 8 * 4; + val = readl(gchip->base + DDR(off)); + if (input) + val &= ~(1 << bit); + else + val |= (1 << bit); + writel(val, gchip->base + DDR(off)); + spin_unlock_irqrestore(&gchip->lock, flags); + + return 0; +} + +static int +mb86s70_pmx_gpio_request_enable(struct pinctrl_dev *pctl, + struct pinctrl_gpio_range *range, unsigned pin) +{ + struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl); + struct gpio_chip *gc = range->gc; + struct mb86s70_gpio_chip *gchip = container_of(gc, + struct mb86s70_gpio_chip, gc); + unsigned long flags; + int p = pin / 4 * 4; + u32 val, off, bit; + + if (p == 64) + return -ENODEV; + + spin_lock_irqsave(&pchip->lock, flags); + if (pchip->pin_busy[p] || pchip->pin_busy[p + 1] || + pchip->pin_busy[p + 2] || pchip->pin_busy[p + 3]) { + val = readl(pchip->base + p); + if (val != 0x1) { + spin_unlock_irqrestore(&pchip->lock, flags); + return -EBUSY; + } + } + + pchip->pin_busy[pin] = true; + writel(0x1, pchip->base + p); + spin_unlock_irqrestore(&pchip->lock, flags); + + spin_lock_irqsave(&gchip->lock, flags); + bit = (pin - range->pin_base) % 8; + off = (pin - range->pin_base) / 8 * 4; + val = readl(gchip->base + PFR(off)); + val &= ~(1 << bit); + writel(val, gchip->base + PFR(off)); + spin_unlock_irqrestore(&gchip->lock, flags); + + return 0; +} + +static void +mb86s70_pmx_gpio_disable_free(struct pinctrl_dev *pctl, + struct pinctrl_gpio_range *range, unsigned pin) +{ + struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl); + struct gpio_chip *gc = range->gc; + struct mb86s70_gpio_chip *gchip = container_of(gc, + struct mb86s70_gpio_chip, gc); + unsigned long flags; + u32 off, bit, val; + + if (pin >= 64) + return; + + spin_lock_irqsave(&pchip->lock, flags); + pchip->pin_busy[pin] = false; + spin_unlock_irqrestore(&pchip->lock, flags); + + spin_lock_irqsave(&gchip->lock, flags); + bit = (pin - range->pin_base) % 8; + off = (pin - range->pin_base) / 8 * 4; + val = readl(gchip->base + PFR(off)); + val |= (1 << bit); + writel(val, gchip->base + PFR(off)); + spin_unlock_irqrestore(&gchip->lock, flags); +} + +static const struct pinmux_ops mb86s70_pmx_ops = { + .get_functions_count = mb86s70_pmx_get_functions_count, + .get_function_name = mb86s70_pmx_get_function_name, + .get_function_groups = mb86s70_pmx_get_function_groups, + .enable = mb86s70_pmx_enable, + .disable = mb86s70_pmx_disable, + .gpio_set_direction = mb86s70_pmx_gpio_set_direction, + .gpio_request_enable = mb86s70_pmx_gpio_request_enable, + .gpio_disable_free = mb86s70_pmx_gpio_disable_free, +}; + +static int +mb86s70_pin_config_group_get(struct pinctrl_dev *pctl, unsigned group, + unsigned long *config) +{ + return -EINVAL; +} + +static int +mb86s70_pin_config_group_set(struct pinctrl_dev *pctl, unsigned group, + unsigned long *configs, unsigned num_configs) +{ + struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl); + unsigned long flags; + const unsigned *pin; + int i, j, p; + u32 val, ds; + + p = mb86s7x_pgrps[group].num_pins; + pin = mb86s7x_pgrps[group].pins; + + spin_lock_irqsave(&pchip->lock, flags); + + for (i = 0; i < num_configs; i++) { + switch (pinconf_to_config_param(configs[i])) { + case PIN_CONFIG_DRIVE_STRENGTH: + /* Drive Strength should be 2, 4, 6 or 8 mA */ + ds = pinconf_to_config_argument(configs[i]); + ds = (ds - 2) / 2; + for (j = 0; j < p; j++) { + /* Clear the Hi-z */ + val = readl_relaxed(pchip->conf_base + + FORCEZ_PDG(pin[j])); + val &= ~(0x3 << PIN_OFF(pin[j])); + writel_relaxed(val, pchip->conf_base + + FORCEZ_PDG(pin[j])); + val = readl_relaxed(pchip->conf_base + + CDRV_PDG(pin[j])); + val &= ~(0x3 << PIN_OFF(pin[j])); + val |= (ds << PIN_OFF(pin[j])); + writel_relaxed(val, pchip->conf_base + + CDRV_PDG(pin[j])); + } + break; + case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: + for (j = 0; j < p; j++) { + val = readl_relaxed(pchip->conf_base + + FORCEZ_PDG(pin[j])); + val &= ~(0x3 << PIN_OFF(pin[j])); + val |= (0x1 << PIN_OFF(pin[j])); + writel_relaxed(val, pchip->conf_base + + FORCEZ_PDG(pin[j])); + } + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + for (j = 0; j < p; j++) { + val = readl_relaxed(pchip->conf_base + + PULL_PDG(pin[j])); + val &= ~(0x3 << PIN_OFF(pin[j])); + writel_relaxed(val, pchip->conf_base + + PULL_PDG(pin[j])); + } + break; + case PIN_CONFIG_BIAS_PULL_UP: + for (j = 0; j < p; j++) { + val = readl_relaxed(pchip->conf_base + + PULL_PDG(pin[j])); + val &= ~(0x3 << PIN_OFF(pin[j])); + val |= (1 << PIN_OFF(pin[j])); + writel_relaxed(val, pchip->conf_base + + PULL_PDG(pin[j])); + } + break; + default: + pr_err("%s:%d!!\n", __func__, __LINE__); + break; + } + } + + spin_unlock_irqrestore(&pchip->lock, flags); + + return 0; +} + +static const struct pinconf_ops mb86s70_conf_ops = { + .pin_config_group_get = mb86s70_pin_config_group_get, + .pin_config_group_set = mb86s70_pin_config_group_set, +}; + +static const struct of_device_id mb86s70_pinmux_dt_ids[] = { + {.compatible = "fujitsu,mb86s70-pinctrl", .data = (void *)PMUX_MB86S70}, + {.compatible = "fujitsu,mb86s73-pinctrl", .data = (void *)PMUX_MB86S73}, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mb86s70_pinmux_dt_ids); + +static int mb86s70_pinmux_probe(struct platform_device *pdev) +{ + const struct of_device_id *device; + struct mb86s70_pmux_chip *pchip; + struct resource *res; + int i, type; + + device = of_match_device(mb86s70_pinmux_dt_ids, &pdev->dev); + if (!device) + return -ENODEV; + + type = (int)device->data; + + /* reserve memory space for pmux chip */ + pchip = devm_kzalloc(&pdev->dev, sizeof(*pchip), GFP_KERNEL); + if (pchip == NULL) { + dev_err(&pdev->dev, "can't allocate pinmux chip data.\n"); + return -ENOMEM; + } + pchip->dev = &pdev->dev; + + /* initiate pmux chip structure */ + platform_set_drvdata(pdev, pchip); + + /* IO resource */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pchip->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pchip->base)) + return PTR_ERR(pchip->base); + + /* initiate lock */ + spin_lock_init(&pchip->lock); + + pchip->desc.name = dev_name(&pdev->dev); + pchip->desc.npins = MB86S70_PMUX_PINS; + pchip->desc.pins = pchip->pins; + pchip->desc.pctlops = &mb86s70_pctrl_ops; + pchip->desc.pmxops = &mb86s70_pmx_ops; + pchip->desc.owner = THIS_MODULE; + if (type == PMUX_MB86S73) { + pchip->desc.confops = &mb86s70_conf_ops; + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + pchip->conf_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pchip->conf_base)) + return PTR_ERR(pchip->conf_base); + mb86s7x_pmx_funcs = mb86s73_pmx_funcs; + func_count = ARRAY_SIZE(mb86s73_pmx_funcs); + mb86s7x_pgrps = mb86s73_pgrps; + grp_count = ARRAY_SIZE(mb86s73_pgrps); + } else { + mb86s7x_pmx_funcs = mb86s70_pmx_funcs; + func_count = ARRAY_SIZE(mb86s70_pmx_funcs); + mb86s7x_pgrps = mb86s70_pgrps; + grp_count = ARRAY_SIZE(mb86s70_pgrps); + } + + for (i = 0; i < MB86S70_PMUX_PINS; i++) { + pchip->pin_busy[i] = false; + pchip->pins[i].number = i; + pchip->pins[i].name = &pchip->pin_names[5 * i]; + snprintf(&pchip->pin_names[5 * i], 5, "PD%d", i); + } + + pchip->pctl = pinctrl_register(&pchip->desc, &pdev->dev, pchip); + if (!pchip->pctl) { + dev_err(&pdev->dev, "couldn't register pinctrl driver\n"); + return -EINVAL; + } + + return 0; +} + +static const struct of_device_id mb86s70_gpio_dt_ids[] = { + { .compatible = "fujitsu,mb86s7x-gpio" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mb86s70_gpio_dt_ids); + +static struct platform_driver mb86s70_gpio_driver = { + .probe = mb86s70_gpio_probe, + .driver = { + .name = "mb86s70-gpio", + .owner = THIS_MODULE, + .of_match_table = mb86s70_gpio_dt_ids, + }, +}; + +static struct platform_driver mb86s70_pinmux_driver = { + .probe = mb86s70_pinmux_probe, + .driver = { + .name = "mb86s70-pinmux", + .owner = THIS_MODULE, + .of_match_table = mb86s70_pinmux_dt_ids, + }, +}; + +static int __init mb86s70_pins_init(void) +{ + int ret; + + ret = platform_driver_register(&mb86s70_pinmux_driver); + if (ret) + return ret; + + return platform_driver_register(&mb86s70_gpio_driver); +} +subsys_initcall(mb86s70_pins_init); + +MODULE_DESCRIPTION("MB86S7x PinCtrl Driver"); +MODULE_ALIAS("platform:mb86s70-pinmux"); +MODULE_ALIAS("platform:mb86s70-gpio"); +MODULE_LICENSE("GPL"); -- 1.8.1.2 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html