Loading Documentation/devicetree/bindings/pinctrl/qcom,slpi-pinctrl.txt 0 → 100644 +123 −0 Original line number Diff line number Diff line Qualcomm Technologies, Inc. SLPI Pin controller driver This DT bindings describes the Pin controller driver being added for supporting SLPI (Sensor Low Power Island) TLMM from QTI chipsets. Following properties are for SLPI Pin controller device main node. - compatible: Usage: required Value type: <string> Definition: must be "qcom,slpi-pinctrl" - reg: Usage: required Value type: <prop-encoded-array> Definition: Base address and size of the SLPI TLMM register space. - qcom,num-pins: Usage: required Value type: <u32> Definition: Number of PINs supported by the SLPI TLMM. Please refer to pinctrl-bindings.txt in this directory for details of the common pinctrl bindings used by client devices, including the meaning of the phrase "pin configuration node". The pin configuration nodes act as a container for an arbitrary number of subnodes. Each of these subnodes represents some desired configuration for a pin or a list of pins. This configuration can include the mux function to select on those pin(s), and various pin configuration parameters, as listed below. SUBNODES: The name of each subnode is not important; all subnodes should be enumerated and processed purely based on their content. Each subnode only affects those parameters that are explicitly listed. In other words, a subnode that lists a mux function but no pin configuration parameters implies no information about any pin configuration parameters. Similarly, a pin subnode that describes a pullup parameter implies no information about e.g. the mux function. The following generic properties as defined in pinctrl-bindings.txt are valid to specify in a pin configuration subnode: - pins: Usage: required Value type: <string-array> Definition: List of gpio pins affected by the properties specified in this subnode. Valid pins are: gpio0-gpio31 for LPI. - function: Usage: required Value type: <string> Definition: Specify the alternative function to be configured for the specified pins. Valid values are: "gpio", "func1", "func2", "func3", "func4", "func5" - bias-disable: Usage: optional Value type: <none> Definition: The specified pins should be configured as no pull. - bias-pull-down: Usage: optional Value type: <none> Definition: The specified pins should be configured as pull down. - bias-bus-hold: Usage: optional Value type: <none> Definition: The specified pins should be configured as bus-keeper mode. - bias-pull-up: Usage: optional Value type: <empty> Definition: The specified pins should be configured as pull up. - qcom,drive-strength: Usage: optional Value type: <u32> Definition: Selects the drive strength for the specified pins. Example: slpi_tlmm: slpi_pinctrl@02b40000 { compatible = "qcom,slpi-pinctrl"; qcom,num-pins = <14>; reg = <0x02b40000 0x20000>; slpi_mclk0_active: slpi_mclk0_active { mux { pins = "gpio0", "gpio1", "gpio2", "gpio3"; function = "func2"; }; config { pins = "gpio0", "gpio1", "gpio2", "gpio3"; drive-strength = <8>; bias-disable; }; }; slpi_mclk0_sleep: slpi_mclk0_sleep { mux { pins = "gpio0", "gpio1", "gpio2", "gpio3"; function = "func2"; }; config { pins = "gpio0", "gpio1", "gpio2", "gpio3"; drive-strength = <2>; bias-pull-down; }; }; }; drivers/pinctrl/qcom/Kconfig +7 −0 Original line number Diff line number Diff line Loading @@ -198,4 +198,11 @@ config PINCTRL_SDXPRAIRIE the Qualcomm Technologies Inc TLMM block found on the Qualcomm Technologies Inc SDXPRAIRIE platform. config PINCTRL_SLPI tristate "Qualcomm Technologies, Inc SLPI pin controller driver" depends on GPIOLIB && OF help This is the pinctrl, pinmux and pinconf driver for the SLPI pin controller block. endif drivers/pinctrl/qcom/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -24,3 +24,4 @@ obj-$(CONFIG_PINCTRL_SDMSHRIKE) += pinctrl-sdmshrike.o obj-$(CONFIG_PINCTRL_SM6150) += pinctrl-sm6150.o obj-$(CONFIG_PINCTRL_SDXPRAIRIE) += pinctrl-sdxprairie.o obj-$(CONFIG_PINCTRL_SDMMAGPIE) += pinctrl-sdmmagpie.o obj-$(CONFIG_PINCTRL_SLPI) += pinctrl-slpi.o drivers/pinctrl/qcom/pinctrl-slpi.c 0 → 100644 +461 −0 Original line number Diff line number Diff line /* * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * 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/gpio.h> #include <linux/io.h> #include <linux/module.h> #include <linux/of.h> #include <linux/pinctrl/pinconf-generic.h> #include <linux/pinctrl/pinconf.h> #include <linux/pinctrl/pinmux.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/types.h> #include "../core.h" #include "../pinctrl-utils.h" /** * struct slpi_pin - SLPI pin definition * @name: Name of the pin. * @ctl_reg: Offset of the register holding control bits for this group. * @io_reg: Offset of the register holding input/output bits for this group. * @pull_bit: Offset in @ctl_reg for the bias configuration. * @mux_bit: Offset in @ctl_reg for the pinmux function selection. * @drv_bit: Offset in @ctl_reg for the drive strength configuration. * @oe_bit: Offset in @ctl_reg for controlling output enable. * @in_bit: Offset in @io_reg for the input bit value. * @out_bit: Offset in @io_reg for the output bit value. */ struct slpi_pin { void __iomem *base; unsigned int offset; unsigned int ctl_reg; unsigned int io_reg; unsigned int pull_bit:5; unsigned int mux_bit:5; unsigned int drv_bit:5; unsigned int oe_bit:5; unsigned int in_bit:5; unsigned int out_bit:5; }; /* The index of each function in slpi_pin_functions[] array */ enum slpi_pin_func_index { SLPI_PIN_FUNC_INDEX_GPIO = 0x00, SLPI_PIN_FUNC_INDEX_FUNC1 = 0x01, SLPI_PIN_FUNC_INDEX_FUNC2 = 0x02, SLPI_PIN_FUNC_INDEX_FUNC3 = 0x03, SLPI_PIN_FUNC_INDEX_FUNC4 = 0x04, SLPI_PIN_FUNC_INDEX_FUNC5 = 0x05, }; #define SLPI_PIN_FUNC_GPIO "gpio" #define SLPI_PIN_FUNC_FUNC1 "func1" #define SLPI_PIN_FUNC_FUNC2 "func2" #define SLPI_PIN_FUNC_FUNC3 "func3" #define SLPI_PIN_FUNC_FUNC4 "func4" #define SLPI_PIN_FUNC_FUNC5 "func5" static const char *const slpi_gpio_groups[] = { "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", "gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", }; static const char *const slpi_pin_functions[] = { [SLPI_PIN_FUNC_INDEX_GPIO] = SLPI_PIN_FUNC_GPIO, [SLPI_PIN_FUNC_INDEX_FUNC1] = SLPI_PIN_FUNC_FUNC1, [SLPI_PIN_FUNC_INDEX_FUNC2] = SLPI_PIN_FUNC_FUNC2, [SLPI_PIN_FUNC_INDEX_FUNC3] = SLPI_PIN_FUNC_FUNC3, [SLPI_PIN_FUNC_INDEX_FUNC4] = SLPI_PIN_FUNC_FUNC4, [SLPI_PIN_FUNC_INDEX_FUNC5] = SLPI_PIN_FUNC_FUNC5, }; static unsigned int slpi_read(struct slpi_pin *pin, u32 reg) { return readl_relaxed(pin->base + pin->offset + reg); } static void slpi_write(u32 val, struct slpi_pin *pin, u32 reg) { return writel_relaxed(val, pin->base + pin->offset + reg); } static int slpi_get_groups_count(struct pinctrl_dev *pctldev) { /* Every PIN is a group */ return pctldev->desc->npins; } static const char *slpi_get_group_name(struct pinctrl_dev *pctldev, unsigned int pin) { return pctldev->desc->pins[pin].name; } static int slpi_get_group_pins(struct pinctrl_dev *pctldev, unsigned int pin, const unsigned int **pins, unsigned int *num_pins) { *pins = &pctldev->desc->pins[pin].number; *num_pins = 1; return 0; } static const struct pinctrl_ops slpi_pinctrl_ops = { .get_groups_count = slpi_get_groups_count, .get_group_name = slpi_get_group_name, .get_group_pins = slpi_get_group_pins, .dt_node_to_map = pinconf_generic_dt_node_to_map_group, .dt_free_map = pinctrl_utils_free_map, }; static int slpi_get_functions_count(struct pinctrl_dev *pctldev) { return ARRAY_SIZE(slpi_pin_functions); } static const char *slpi_get_function_name(struct pinctrl_dev *pctldev, unsigned int function) { return slpi_pin_functions[function]; } static int slpi_get_function_groups(struct pinctrl_dev *pctldev, unsigned int function, const char * const **groups, unsigned int * const num_groups) { *groups = slpi_gpio_groups; *num_groups = pctldev->desc->npins; return 0; } static int slpi_pinmux_set_mux(struct pinctrl_dev *pctldev, unsigned int function, unsigned int pin_index) { struct slpi_pin *pin; u32 val; pin = pctldev->desc->pins[pin_index].drv_data; if (WARN_ON(function >= ARRAY_SIZE(slpi_pin_functions))) return -EINVAL; val = slpi_read(pin, pin->ctl_reg); val &= ~(0x7 << pin->mux_bit); val |= function << pin->mux_bit; slpi_write(val, pin, pin->ctl_reg); return 0; } static const struct pinmux_ops slpi_pinmux_ops = { .get_functions_count = slpi_get_functions_count, .get_function_name = slpi_get_function_name, .get_function_groups = slpi_get_function_groups, .set_mux = slpi_pinmux_set_mux, }; static int slpi_config_reg(const struct slpi_pin *pin, unsigned int param, unsigned int *mask, unsigned int *bit) { switch (param) { case PIN_CONFIG_BIAS_DISABLE: case PIN_CONFIG_BIAS_PULL_DOWN: case PIN_CONFIG_BIAS_BUS_HOLD: case PIN_CONFIG_BIAS_PULL_UP: *bit = pin->pull_bit; *mask = 3; break; case PIN_CONFIG_DRIVE_STRENGTH: *bit = pin->drv_bit; *mask = 7; break; case PIN_CONFIG_OUTPUT: case PIN_CONFIG_INPUT_ENABLE: *bit = pin->oe_bit; *mask = 1; break; default: return -ENOTSUPP; } return 0; } #define MSM_NO_PULL 0 #define MSM_PULL_DOWN 1 #define MSM_KEEPER 2 #define MSM_PULL_UP 3 static unsigned int slpi_regval_to_drive(u32 val) { return (val + 1) * 2; } static int slpi_config_group_get(struct pinctrl_dev *pctldev, unsigned int pin_index, unsigned long *config) { unsigned int param = pinconf_to_config_param(*config); struct slpi_pin *pin; unsigned int mask; unsigned int arg; unsigned int bit; int ret; u32 val; pin = pctldev->desc->pins[pin_index].drv_data; ret = slpi_config_reg(pin, param, &mask, &bit); if (ret < 0) return ret; val = slpi_read(pin, pin->ctl_reg); arg = (val >> bit) & mask; /* Convert register value to pinconf value */ switch (param) { case PIN_CONFIG_BIAS_DISABLE: arg = arg == MSM_NO_PULL; break; case PIN_CONFIG_BIAS_PULL_DOWN: arg = arg == MSM_PULL_DOWN; break; case PIN_CONFIG_BIAS_BUS_HOLD: arg = arg == MSM_KEEPER; break; case PIN_CONFIG_BIAS_PULL_UP: arg = arg == MSM_PULL_UP; break; case PIN_CONFIG_DRIVE_STRENGTH: arg = slpi_regval_to_drive(arg); break; case PIN_CONFIG_OUTPUT: /* Pin is not output */ if (!arg) return -EINVAL; val = slpi_read(pin, pin->io_reg); arg = !!(val & BIT(pin->in_bit)); break; case PIN_CONFIG_INPUT_ENABLE: /* Pin is output */ if (arg) return -EINVAL; arg = 1; break; default: return -ENOTSUPP; } *config = pinconf_to_config_packed(param, arg); return 0; } static int slpi_config_group_set(struct pinctrl_dev *pctldev, unsigned int pin_index, unsigned long *configs, unsigned int num_configs) { struct slpi_pin *pin; unsigned int param; unsigned int mask; unsigned int arg; unsigned int bit; int ret; u32 val; int i; pin = pctldev->desc->pins[pin_index].drv_data; for (i = 0; i < num_configs; i++) { param = pinconf_to_config_param(configs[i]); arg = pinconf_to_config_argument(configs[i]); ret = slpi_config_reg(pin, param, &mask, &bit); if (ret < 0) return ret; /* Convert pinconf values to register values */ switch (param) { case PIN_CONFIG_BIAS_DISABLE: arg = MSM_NO_PULL; break; case PIN_CONFIG_BIAS_PULL_DOWN: arg = MSM_PULL_DOWN; break; case PIN_CONFIG_BIAS_BUS_HOLD: arg = MSM_KEEPER; break; case PIN_CONFIG_BIAS_PULL_UP: arg = MSM_PULL_UP; break; case PIN_CONFIG_DRIVE_STRENGTH: /* Check for invalid values */ if (arg > 16 || arg < 2 || (arg % 2) != 0) arg = -1; else arg = (arg / 2) - 1; break; case PIN_CONFIG_OUTPUT: /* set output value */ val = slpi_read(pin, pin->io_reg); if (arg) val |= BIT(pin->out_bit); else val &= ~BIT(pin->out_bit); slpi_write(val, pin, pin->io_reg); /* enable output */ arg = 1; break; case PIN_CONFIG_INPUT_ENABLE: /* disable output */ arg = 0; break; default: dev_err(pctldev->dev, "Unsupported config parameter: %x\n", param); return -EINVAL; } /* Range-check user-supplied value */ if (arg & ~mask) { dev_err(pctldev->dev, "config %x: %x is invalid\n", param, arg); return -EINVAL; } val = slpi_read(pin, pin->ctl_reg); val &= ~(mask << bit); val |= arg << bit; slpi_write(val, pin, pin->ctl_reg); } return 0; } static const struct pinconf_ops slpi_pinconf_ops = { .is_generic = true, .pin_config_group_get = slpi_config_group_get, .pin_config_group_set = slpi_config_group_set, }; static int slpi_pinctrl_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct pinctrl_dev *pctldev; struct pinctrl_pin_desc *pindesc; struct pinctrl_desc *pctrldesc; struct slpi_pin *pin, *pins; struct resource *res; int ret, npins, i; void __iomem *base; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) return PTR_ERR(base); ret = of_property_read_u32(dev->of_node, "qcom,num-pins", &npins); if (ret < 0) return ret; pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL); if (!pindesc) return -ENOMEM; WARN_ON(npins > ARRAY_SIZE(slpi_gpio_groups)); pins = devm_kcalloc(dev, npins, sizeof(*pins), GFP_KERNEL); if (!pins) return -ENOMEM; pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL); if (!pctrldesc) return -ENOMEM; pctrldesc->pctlops = &slpi_pinctrl_ops; pctrldesc->pmxops = &slpi_pinmux_ops; pctrldesc->confops = &slpi_pinconf_ops; pctrldesc->owner = THIS_MODULE; pctrldesc->name = dev_name(&pdev->dev); pctrldesc->pins = pindesc; pctrldesc->npins = npins; for (i = 0; i < npins; i++, pindesc++) { pin = &pins[i]; pindesc->drv_data = pin; pindesc->number = i; pindesc->name = slpi_gpio_groups[i]; pin->base = base; pin->offset = i * 0x1000; pin->ctl_reg = 0x0; pin->io_reg = 0x4; pin->pull_bit = 0; pin->out_bit = 1; pin->mux_bit = 2; pin->oe_bit = 9; pin->drv_bit = 6; pin->in_bit = 0; } pctldev = devm_pinctrl_register(&pdev->dev, pctrldesc, NULL); if (IS_ERR(pctldev)) { dev_err(dev, "Failed to register pinctrl device\n"); return PTR_ERR(pctldev); } return 0; } static const struct of_device_id slpi_pinctrl_of_match[] = { { .compatible = "qcom,slpi-pinctrl" }, /* Generic */ { }, }; MODULE_DEVICE_TABLE(of, slpi_pinctrl_of_match); static struct platform_driver slpi_pinctrl_driver = { .driver = { .name = "qcom-slpi-pinctrl", .of_match_table = slpi_pinctrl_of_match, }, .probe = slpi_pinctrl_probe, }; static int __init slpi_pinctrl_init(void) { return platform_driver_register(&slpi_pinctrl_driver); } arch_initcall(slpi_pinctrl_init); static void __exit slpi_pinctrl_exit(void) { platform_driver_unregister(&slpi_pinctrl_driver); } module_exit(slpi_pinctrl_exit); MODULE_DESCRIPTION("QTI SLPI GPIO pin control driver"); MODULE_LICENSE("GPL v2"); Loading
Documentation/devicetree/bindings/pinctrl/qcom,slpi-pinctrl.txt 0 → 100644 +123 −0 Original line number Diff line number Diff line Qualcomm Technologies, Inc. SLPI Pin controller driver This DT bindings describes the Pin controller driver being added for supporting SLPI (Sensor Low Power Island) TLMM from QTI chipsets. Following properties are for SLPI Pin controller device main node. - compatible: Usage: required Value type: <string> Definition: must be "qcom,slpi-pinctrl" - reg: Usage: required Value type: <prop-encoded-array> Definition: Base address and size of the SLPI TLMM register space. - qcom,num-pins: Usage: required Value type: <u32> Definition: Number of PINs supported by the SLPI TLMM. Please refer to pinctrl-bindings.txt in this directory for details of the common pinctrl bindings used by client devices, including the meaning of the phrase "pin configuration node". The pin configuration nodes act as a container for an arbitrary number of subnodes. Each of these subnodes represents some desired configuration for a pin or a list of pins. This configuration can include the mux function to select on those pin(s), and various pin configuration parameters, as listed below. SUBNODES: The name of each subnode is not important; all subnodes should be enumerated and processed purely based on their content. Each subnode only affects those parameters that are explicitly listed. In other words, a subnode that lists a mux function but no pin configuration parameters implies no information about any pin configuration parameters. Similarly, a pin subnode that describes a pullup parameter implies no information about e.g. the mux function. The following generic properties as defined in pinctrl-bindings.txt are valid to specify in a pin configuration subnode: - pins: Usage: required Value type: <string-array> Definition: List of gpio pins affected by the properties specified in this subnode. Valid pins are: gpio0-gpio31 for LPI. - function: Usage: required Value type: <string> Definition: Specify the alternative function to be configured for the specified pins. Valid values are: "gpio", "func1", "func2", "func3", "func4", "func5" - bias-disable: Usage: optional Value type: <none> Definition: The specified pins should be configured as no pull. - bias-pull-down: Usage: optional Value type: <none> Definition: The specified pins should be configured as pull down. - bias-bus-hold: Usage: optional Value type: <none> Definition: The specified pins should be configured as bus-keeper mode. - bias-pull-up: Usage: optional Value type: <empty> Definition: The specified pins should be configured as pull up. - qcom,drive-strength: Usage: optional Value type: <u32> Definition: Selects the drive strength for the specified pins. Example: slpi_tlmm: slpi_pinctrl@02b40000 { compatible = "qcom,slpi-pinctrl"; qcom,num-pins = <14>; reg = <0x02b40000 0x20000>; slpi_mclk0_active: slpi_mclk0_active { mux { pins = "gpio0", "gpio1", "gpio2", "gpio3"; function = "func2"; }; config { pins = "gpio0", "gpio1", "gpio2", "gpio3"; drive-strength = <8>; bias-disable; }; }; slpi_mclk0_sleep: slpi_mclk0_sleep { mux { pins = "gpio0", "gpio1", "gpio2", "gpio3"; function = "func2"; }; config { pins = "gpio0", "gpio1", "gpio2", "gpio3"; drive-strength = <2>; bias-pull-down; }; }; };
drivers/pinctrl/qcom/Kconfig +7 −0 Original line number Diff line number Diff line Loading @@ -198,4 +198,11 @@ config PINCTRL_SDXPRAIRIE the Qualcomm Technologies Inc TLMM block found on the Qualcomm Technologies Inc SDXPRAIRIE platform. config PINCTRL_SLPI tristate "Qualcomm Technologies, Inc SLPI pin controller driver" depends on GPIOLIB && OF help This is the pinctrl, pinmux and pinconf driver for the SLPI pin controller block. endif
drivers/pinctrl/qcom/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -24,3 +24,4 @@ obj-$(CONFIG_PINCTRL_SDMSHRIKE) += pinctrl-sdmshrike.o obj-$(CONFIG_PINCTRL_SM6150) += pinctrl-sm6150.o obj-$(CONFIG_PINCTRL_SDXPRAIRIE) += pinctrl-sdxprairie.o obj-$(CONFIG_PINCTRL_SDMMAGPIE) += pinctrl-sdmmagpie.o obj-$(CONFIG_PINCTRL_SLPI) += pinctrl-slpi.o
drivers/pinctrl/qcom/pinctrl-slpi.c 0 → 100644 +461 −0 Original line number Diff line number Diff line /* * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * 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/gpio.h> #include <linux/io.h> #include <linux/module.h> #include <linux/of.h> #include <linux/pinctrl/pinconf-generic.h> #include <linux/pinctrl/pinconf.h> #include <linux/pinctrl/pinmux.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/types.h> #include "../core.h" #include "../pinctrl-utils.h" /** * struct slpi_pin - SLPI pin definition * @name: Name of the pin. * @ctl_reg: Offset of the register holding control bits for this group. * @io_reg: Offset of the register holding input/output bits for this group. * @pull_bit: Offset in @ctl_reg for the bias configuration. * @mux_bit: Offset in @ctl_reg for the pinmux function selection. * @drv_bit: Offset in @ctl_reg for the drive strength configuration. * @oe_bit: Offset in @ctl_reg for controlling output enable. * @in_bit: Offset in @io_reg for the input bit value. * @out_bit: Offset in @io_reg for the output bit value. */ struct slpi_pin { void __iomem *base; unsigned int offset; unsigned int ctl_reg; unsigned int io_reg; unsigned int pull_bit:5; unsigned int mux_bit:5; unsigned int drv_bit:5; unsigned int oe_bit:5; unsigned int in_bit:5; unsigned int out_bit:5; }; /* The index of each function in slpi_pin_functions[] array */ enum slpi_pin_func_index { SLPI_PIN_FUNC_INDEX_GPIO = 0x00, SLPI_PIN_FUNC_INDEX_FUNC1 = 0x01, SLPI_PIN_FUNC_INDEX_FUNC2 = 0x02, SLPI_PIN_FUNC_INDEX_FUNC3 = 0x03, SLPI_PIN_FUNC_INDEX_FUNC4 = 0x04, SLPI_PIN_FUNC_INDEX_FUNC5 = 0x05, }; #define SLPI_PIN_FUNC_GPIO "gpio" #define SLPI_PIN_FUNC_FUNC1 "func1" #define SLPI_PIN_FUNC_FUNC2 "func2" #define SLPI_PIN_FUNC_FUNC3 "func3" #define SLPI_PIN_FUNC_FUNC4 "func4" #define SLPI_PIN_FUNC_FUNC5 "func5" static const char *const slpi_gpio_groups[] = { "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", "gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", }; static const char *const slpi_pin_functions[] = { [SLPI_PIN_FUNC_INDEX_GPIO] = SLPI_PIN_FUNC_GPIO, [SLPI_PIN_FUNC_INDEX_FUNC1] = SLPI_PIN_FUNC_FUNC1, [SLPI_PIN_FUNC_INDEX_FUNC2] = SLPI_PIN_FUNC_FUNC2, [SLPI_PIN_FUNC_INDEX_FUNC3] = SLPI_PIN_FUNC_FUNC3, [SLPI_PIN_FUNC_INDEX_FUNC4] = SLPI_PIN_FUNC_FUNC4, [SLPI_PIN_FUNC_INDEX_FUNC5] = SLPI_PIN_FUNC_FUNC5, }; static unsigned int slpi_read(struct slpi_pin *pin, u32 reg) { return readl_relaxed(pin->base + pin->offset + reg); } static void slpi_write(u32 val, struct slpi_pin *pin, u32 reg) { return writel_relaxed(val, pin->base + pin->offset + reg); } static int slpi_get_groups_count(struct pinctrl_dev *pctldev) { /* Every PIN is a group */ return pctldev->desc->npins; } static const char *slpi_get_group_name(struct pinctrl_dev *pctldev, unsigned int pin) { return pctldev->desc->pins[pin].name; } static int slpi_get_group_pins(struct pinctrl_dev *pctldev, unsigned int pin, const unsigned int **pins, unsigned int *num_pins) { *pins = &pctldev->desc->pins[pin].number; *num_pins = 1; return 0; } static const struct pinctrl_ops slpi_pinctrl_ops = { .get_groups_count = slpi_get_groups_count, .get_group_name = slpi_get_group_name, .get_group_pins = slpi_get_group_pins, .dt_node_to_map = pinconf_generic_dt_node_to_map_group, .dt_free_map = pinctrl_utils_free_map, }; static int slpi_get_functions_count(struct pinctrl_dev *pctldev) { return ARRAY_SIZE(slpi_pin_functions); } static const char *slpi_get_function_name(struct pinctrl_dev *pctldev, unsigned int function) { return slpi_pin_functions[function]; } static int slpi_get_function_groups(struct pinctrl_dev *pctldev, unsigned int function, const char * const **groups, unsigned int * const num_groups) { *groups = slpi_gpio_groups; *num_groups = pctldev->desc->npins; return 0; } static int slpi_pinmux_set_mux(struct pinctrl_dev *pctldev, unsigned int function, unsigned int pin_index) { struct slpi_pin *pin; u32 val; pin = pctldev->desc->pins[pin_index].drv_data; if (WARN_ON(function >= ARRAY_SIZE(slpi_pin_functions))) return -EINVAL; val = slpi_read(pin, pin->ctl_reg); val &= ~(0x7 << pin->mux_bit); val |= function << pin->mux_bit; slpi_write(val, pin, pin->ctl_reg); return 0; } static const struct pinmux_ops slpi_pinmux_ops = { .get_functions_count = slpi_get_functions_count, .get_function_name = slpi_get_function_name, .get_function_groups = slpi_get_function_groups, .set_mux = slpi_pinmux_set_mux, }; static int slpi_config_reg(const struct slpi_pin *pin, unsigned int param, unsigned int *mask, unsigned int *bit) { switch (param) { case PIN_CONFIG_BIAS_DISABLE: case PIN_CONFIG_BIAS_PULL_DOWN: case PIN_CONFIG_BIAS_BUS_HOLD: case PIN_CONFIG_BIAS_PULL_UP: *bit = pin->pull_bit; *mask = 3; break; case PIN_CONFIG_DRIVE_STRENGTH: *bit = pin->drv_bit; *mask = 7; break; case PIN_CONFIG_OUTPUT: case PIN_CONFIG_INPUT_ENABLE: *bit = pin->oe_bit; *mask = 1; break; default: return -ENOTSUPP; } return 0; } #define MSM_NO_PULL 0 #define MSM_PULL_DOWN 1 #define MSM_KEEPER 2 #define MSM_PULL_UP 3 static unsigned int slpi_regval_to_drive(u32 val) { return (val + 1) * 2; } static int slpi_config_group_get(struct pinctrl_dev *pctldev, unsigned int pin_index, unsigned long *config) { unsigned int param = pinconf_to_config_param(*config); struct slpi_pin *pin; unsigned int mask; unsigned int arg; unsigned int bit; int ret; u32 val; pin = pctldev->desc->pins[pin_index].drv_data; ret = slpi_config_reg(pin, param, &mask, &bit); if (ret < 0) return ret; val = slpi_read(pin, pin->ctl_reg); arg = (val >> bit) & mask; /* Convert register value to pinconf value */ switch (param) { case PIN_CONFIG_BIAS_DISABLE: arg = arg == MSM_NO_PULL; break; case PIN_CONFIG_BIAS_PULL_DOWN: arg = arg == MSM_PULL_DOWN; break; case PIN_CONFIG_BIAS_BUS_HOLD: arg = arg == MSM_KEEPER; break; case PIN_CONFIG_BIAS_PULL_UP: arg = arg == MSM_PULL_UP; break; case PIN_CONFIG_DRIVE_STRENGTH: arg = slpi_regval_to_drive(arg); break; case PIN_CONFIG_OUTPUT: /* Pin is not output */ if (!arg) return -EINVAL; val = slpi_read(pin, pin->io_reg); arg = !!(val & BIT(pin->in_bit)); break; case PIN_CONFIG_INPUT_ENABLE: /* Pin is output */ if (arg) return -EINVAL; arg = 1; break; default: return -ENOTSUPP; } *config = pinconf_to_config_packed(param, arg); return 0; } static int slpi_config_group_set(struct pinctrl_dev *pctldev, unsigned int pin_index, unsigned long *configs, unsigned int num_configs) { struct slpi_pin *pin; unsigned int param; unsigned int mask; unsigned int arg; unsigned int bit; int ret; u32 val; int i; pin = pctldev->desc->pins[pin_index].drv_data; for (i = 0; i < num_configs; i++) { param = pinconf_to_config_param(configs[i]); arg = pinconf_to_config_argument(configs[i]); ret = slpi_config_reg(pin, param, &mask, &bit); if (ret < 0) return ret; /* Convert pinconf values to register values */ switch (param) { case PIN_CONFIG_BIAS_DISABLE: arg = MSM_NO_PULL; break; case PIN_CONFIG_BIAS_PULL_DOWN: arg = MSM_PULL_DOWN; break; case PIN_CONFIG_BIAS_BUS_HOLD: arg = MSM_KEEPER; break; case PIN_CONFIG_BIAS_PULL_UP: arg = MSM_PULL_UP; break; case PIN_CONFIG_DRIVE_STRENGTH: /* Check for invalid values */ if (arg > 16 || arg < 2 || (arg % 2) != 0) arg = -1; else arg = (arg / 2) - 1; break; case PIN_CONFIG_OUTPUT: /* set output value */ val = slpi_read(pin, pin->io_reg); if (arg) val |= BIT(pin->out_bit); else val &= ~BIT(pin->out_bit); slpi_write(val, pin, pin->io_reg); /* enable output */ arg = 1; break; case PIN_CONFIG_INPUT_ENABLE: /* disable output */ arg = 0; break; default: dev_err(pctldev->dev, "Unsupported config parameter: %x\n", param); return -EINVAL; } /* Range-check user-supplied value */ if (arg & ~mask) { dev_err(pctldev->dev, "config %x: %x is invalid\n", param, arg); return -EINVAL; } val = slpi_read(pin, pin->ctl_reg); val &= ~(mask << bit); val |= arg << bit; slpi_write(val, pin, pin->ctl_reg); } return 0; } static const struct pinconf_ops slpi_pinconf_ops = { .is_generic = true, .pin_config_group_get = slpi_config_group_get, .pin_config_group_set = slpi_config_group_set, }; static int slpi_pinctrl_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct pinctrl_dev *pctldev; struct pinctrl_pin_desc *pindesc; struct pinctrl_desc *pctrldesc; struct slpi_pin *pin, *pins; struct resource *res; int ret, npins, i; void __iomem *base; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) return PTR_ERR(base); ret = of_property_read_u32(dev->of_node, "qcom,num-pins", &npins); if (ret < 0) return ret; pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL); if (!pindesc) return -ENOMEM; WARN_ON(npins > ARRAY_SIZE(slpi_gpio_groups)); pins = devm_kcalloc(dev, npins, sizeof(*pins), GFP_KERNEL); if (!pins) return -ENOMEM; pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL); if (!pctrldesc) return -ENOMEM; pctrldesc->pctlops = &slpi_pinctrl_ops; pctrldesc->pmxops = &slpi_pinmux_ops; pctrldesc->confops = &slpi_pinconf_ops; pctrldesc->owner = THIS_MODULE; pctrldesc->name = dev_name(&pdev->dev); pctrldesc->pins = pindesc; pctrldesc->npins = npins; for (i = 0; i < npins; i++, pindesc++) { pin = &pins[i]; pindesc->drv_data = pin; pindesc->number = i; pindesc->name = slpi_gpio_groups[i]; pin->base = base; pin->offset = i * 0x1000; pin->ctl_reg = 0x0; pin->io_reg = 0x4; pin->pull_bit = 0; pin->out_bit = 1; pin->mux_bit = 2; pin->oe_bit = 9; pin->drv_bit = 6; pin->in_bit = 0; } pctldev = devm_pinctrl_register(&pdev->dev, pctrldesc, NULL); if (IS_ERR(pctldev)) { dev_err(dev, "Failed to register pinctrl device\n"); return PTR_ERR(pctldev); } return 0; } static const struct of_device_id slpi_pinctrl_of_match[] = { { .compatible = "qcom,slpi-pinctrl" }, /* Generic */ { }, }; MODULE_DEVICE_TABLE(of, slpi_pinctrl_of_match); static struct platform_driver slpi_pinctrl_driver = { .driver = { .name = "qcom-slpi-pinctrl", .of_match_table = slpi_pinctrl_of_match, }, .probe = slpi_pinctrl_probe, }; static int __init slpi_pinctrl_init(void) { return platform_driver_register(&slpi_pinctrl_driver); } arch_initcall(slpi_pinctrl_init); static void __exit slpi_pinctrl_exit(void) { platform_driver_unregister(&slpi_pinctrl_driver); } module_exit(slpi_pinctrl_exit); MODULE_DESCRIPTION("QTI SLPI GPIO pin control driver"); MODULE_LICENSE("GPL v2");