123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
- * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
- *
- */
- #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 <linux/clk.h>
- #include "../core.h"
- #include "../pinctrl-utils.h"
- static struct clk *clk_ssc;
- /**
- * 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", "gpio14",
- "gpio15", "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21",
- "gpio22", "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28",
- "gpio29", "gpio30", "gpio31",
- };
- 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;
- int ret;
- ret = clk_prepare_enable(clk_ssc);
- if (ret)
- return ret;
- 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);
- clk_disable_unprepare(clk_ssc);
- 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 -EOPNOTSUPP;
- }
- 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;
- ret = clk_prepare_enable(clk_ssc);
- if (ret)
- return ret;
- 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 -EOPNOTSUPP;
- }
- *config = pinconf_to_config_packed(param, arg);
- clk_disable_unprepare(clk_ssc);
- 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;
- ret = clk_prepare_enable(clk_ssc);
- if (ret)
- return ret;
- 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);
- }
- clk_disable_unprepare(clk_ssc);
- 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);
- clk_ssc = devm_clk_get(dev, "ssc-clk");
- if (IS_ERR_OR_NULL(clk_ssc)) {
- dev_err(dev, "Error in getting ssc-clk\n");
- return -EPROBE_DEFER;
- }
- 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,
- };
- module_platform_driver(slpi_pinctrl_driver);
- MODULE_DESCRIPTION("QTI SLPI GPIO pin control driver");
- MODULE_LICENSE("GPL");
|