diff options
Diffstat (limited to 'drivers/pinctrl/qcom/pinctrl-lpass-lpi.c')
| -rw-r--r-- | drivers/pinctrl/qcom/pinctrl-lpass-lpi.c | 173 |
1 files changed, 119 insertions, 54 deletions
diff --git a/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c index 3dc670faa59e..1c97ec44aa5f 100644 --- a/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c @@ -8,7 +8,8 @@ #include <linux/clk.h> #include <linux/gpio/driver.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/platform_device.h> #include <linux/seq_file.h> #include <linux/pinctrl/pinconf-generic.h> @@ -19,6 +20,8 @@ #include "pinctrl-lpass-lpi.h" +#define MAX_NR_GPIO 32 +#define GPIO_FUNC 0 #define MAX_LPI_NUM_CLKS 2 struct lpi_pinctrl { @@ -29,20 +32,36 @@ struct lpi_pinctrl { char __iomem *tlmm_base; char __iomem *slew_base; struct clk_bulk_data clks[MAX_LPI_NUM_CLKS]; - struct mutex slew_access_lock; + /* Protects from concurrent register updates */ + struct mutex lock; + DECLARE_BITMAP(ever_gpio, MAX_NR_GPIO); const struct lpi_pinctrl_variant_data *data; }; static int lpi_gpio_read(struct lpi_pinctrl *state, unsigned int pin, unsigned int addr) { - return ioread32(state->tlmm_base + LPI_TLMM_REG_OFFSET * pin + addr); + u32 pin_offset; + + if (state->data->flags & LPI_FLAG_USE_PREDEFINED_PIN_OFFSET) + pin_offset = state->data->groups[pin].pin_offset; + else + pin_offset = LPI_TLMM_REG_OFFSET * pin; + + return ioread32(state->tlmm_base + pin_offset + addr); } static int lpi_gpio_write(struct lpi_pinctrl *state, unsigned int pin, unsigned int addr, unsigned int val) { - iowrite32(val, state->tlmm_base + LPI_TLMM_REG_OFFSET * pin + addr); + u32 pin_offset; + + if (state->data->flags & LPI_FLAG_USE_PREDEFINED_PIN_OFFSET) + pin_offset = state->data->groups[pin].pin_offset; + else + pin_offset = LPI_TLMM_REG_OFFSET * pin; + + iowrite32(val, state->tlmm_base + pin_offset + addr); return 0; } @@ -84,10 +103,10 @@ static int lpi_gpio_get_function_groups(struct pinctrl_dev *pctldev, } static int lpi_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned int function, - unsigned int group_num) + unsigned int group) { struct lpi_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); - const struct lpi_pingroup *g = &pctrl->data->groups[group_num]; + const struct lpi_pingroup *g = &pctrl->data->groups[group]; u32 val; int i, pin = g->pin; @@ -99,9 +118,33 @@ static int lpi_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned int function, if (WARN_ON(i == g->nfuncs)) return -EINVAL; + mutex_lock(&pctrl->lock); val = lpi_gpio_read(pctrl, pin, LPI_GPIO_CFG_REG); + + /* + * If this is the first time muxing to GPIO and the direction is + * output, make sure that we're not going to be glitching the pin + * by reading the current state of the pin and setting it as the + * output. + */ + if (i == GPIO_FUNC && (val & LPI_GPIO_OE_MASK) && + !test_and_set_bit(group, pctrl->ever_gpio)) { + u32 io_val = lpi_gpio_read(pctrl, group, LPI_GPIO_VALUE_REG); + + if (io_val & LPI_GPIO_VALUE_IN_MASK) { + if (!(io_val & LPI_GPIO_VALUE_OUT_MASK)) + lpi_gpio_write(pctrl, group, LPI_GPIO_VALUE_REG, + io_val | LPI_GPIO_VALUE_OUT_MASK); + } else { + if (io_val & LPI_GPIO_VALUE_OUT_MASK) + lpi_gpio_write(pctrl, group, LPI_GPIO_VALUE_REG, + io_val & ~LPI_GPIO_VALUE_OUT_MASK); + } + } + u32p_replace_bits(&val, i, LPI_GPIO_FUNCTION_MASK); lpi_gpio_write(pctrl, pin, LPI_GPIO_CFG_REG, val); + mutex_unlock(&pctrl->lock); return 0; } @@ -145,7 +188,7 @@ static int lpi_config_get(struct pinctrl_dev *pctldev, arg = 1; break; case PIN_CONFIG_INPUT_ENABLE: - case PIN_CONFIG_OUTPUT: + case PIN_CONFIG_LEVEL: if (is_out) arg = 1; break; @@ -157,6 +200,41 @@ static int lpi_config_get(struct pinctrl_dev *pctldev, return 0; } +static int lpi_config_set_slew_rate(struct lpi_pinctrl *pctrl, + const struct lpi_pingroup *g, + unsigned int group, unsigned int slew) +{ + unsigned long sval; + void __iomem *reg; + int slew_offset; + + if (slew > LPI_SLEW_RATE_MAX) { + dev_err(pctrl->dev, "invalid slew rate %u for pin: %d\n", + slew, group); + return -EINVAL; + } + + slew_offset = g->slew_offset; + if (slew_offset == LPI_NO_SLEW) + return 0; + + if (pctrl->data->flags & LPI_FLAG_SLEW_RATE_SAME_REG) + reg = pctrl->tlmm_base + LPI_TLMM_REG_OFFSET * group + LPI_GPIO_CFG_REG; + else + reg = pctrl->slew_base + LPI_SLEW_RATE_CTL_REG; + + mutex_lock(&pctrl->lock); + + sval = ioread32(reg); + sval &= ~(LPI_SLEW_RATE_MASK << slew_offset); + sval |= slew << slew_offset; + iowrite32(sval, reg); + + mutex_unlock(&pctrl->lock); + + return 0; +} + static int lpi_config_set(struct pinctrl_dev *pctldev, unsigned int group, unsigned long *configs, unsigned int nconfs) { @@ -164,8 +242,7 @@ static int lpi_config_set(struct pinctrl_dev *pctldev, unsigned int group, unsigned int param, arg, pullup = LPI_GPIO_BIAS_DISABLE, strength = 2; bool value, output_enabled = false; const struct lpi_pingroup *g; - unsigned long sval; - int i, slew_offset; + int i, ret; u32 val; g = &pctrl->data->groups[group]; @@ -189,7 +266,7 @@ static int lpi_config_set(struct pinctrl_dev *pctldev, unsigned int group, case PIN_CONFIG_INPUT_ENABLE: output_enabled = false; break; - case PIN_CONFIG_OUTPUT: + case PIN_CONFIG_LEVEL: output_enabled = true; value = arg; break; @@ -197,30 +274,25 @@ static int lpi_config_set(struct pinctrl_dev *pctldev, unsigned int group, strength = arg; break; case PIN_CONFIG_SLEW_RATE: - if (arg > LPI_SLEW_RATE_MAX) { - dev_err(pctldev->dev, "invalid slew rate %u for pin: %d\n", - arg, group); - return -EINVAL; - } - - slew_offset = g->slew_offset; - if (slew_offset == LPI_NO_SLEW) - break; - - mutex_lock(&pctrl->slew_access_lock); - - sval = ioread32(pctrl->slew_base + LPI_SLEW_RATE_CTL_REG); - sval &= ~(LPI_SLEW_RATE_MASK << slew_offset); - sval |= arg << slew_offset; - iowrite32(sval, pctrl->slew_base + LPI_SLEW_RATE_CTL_REG); - - mutex_unlock(&pctrl->slew_access_lock); + ret = lpi_config_set_slew_rate(pctrl, g, group, arg); + if (ret) + return ret; break; default: return -EINVAL; } } + /* + * As per Hardware Programming Guide, when configuring pin as output, + * set the pin value before setting output-enable (OE). + */ + if (output_enabled) { + val = u32_encode_bits(value ? 1 : 0, LPI_GPIO_VALUE_OUT_MASK); + lpi_gpio_write(pctrl, group, LPI_GPIO_VALUE_REG, val); + } + + mutex_lock(&pctrl->lock); val = lpi_gpio_read(pctrl, group, LPI_GPIO_CFG_REG); u32p_replace_bits(&val, pullup, LPI_GPIO_PULL_MASK); @@ -229,11 +301,7 @@ static int lpi_config_set(struct pinctrl_dev *pctldev, unsigned int group, u32p_replace_bits(&val, output_enabled, LPI_GPIO_OE_MASK); lpi_gpio_write(pctrl, group, LPI_GPIO_CFG_REG, val); - - if (output_enabled) { - val = u32_encode_bits(value ? 1 : 0, LPI_GPIO_VALUE_OUT_MASK); - lpi_gpio_write(pctrl, group, LPI_GPIO_VALUE_REG, val); - } + mutex_unlock(&pctrl->lock); return 0; } @@ -260,7 +328,7 @@ static int lpi_gpio_direction_output(struct gpio_chip *chip, struct lpi_pinctrl *state = gpiochip_get_data(chip); unsigned long config; - config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val); + config = pinconf_to_config_packed(PIN_CONFIG_LEVEL, val); return lpi_config_set(state->ctrl, pin, &config, 1); } @@ -273,18 +341,17 @@ static int lpi_gpio_get(struct gpio_chip *chip, unsigned int pin) LPI_GPIO_VALUE_IN_MASK; } -static void lpi_gpio_set(struct gpio_chip *chip, unsigned int pin, int value) +static int lpi_gpio_set(struct gpio_chip *chip, unsigned int pin, int value) { struct lpi_pinctrl *state = gpiochip_get_data(chip); unsigned long config; - config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value); + config = pinconf_to_config_packed(PIN_CONFIG_LEVEL, value); - lpi_config_set(state->ctrl, pin, &config, 1); + return lpi_config_set(state->ctrl, pin, &config, 1); } #ifdef CONFIG_DEBUG_FS -#include <linux/seq_file.h> static unsigned int lpi_regval_to_drive(u32 val) { @@ -390,6 +457,9 @@ int lpi_pinctrl_probe(struct platform_device *pdev) if (!data) return -EINVAL; + if (WARN_ON(data->npins > MAX_NR_GPIO)) + return -EINVAL; + pctrl->data = data; pctrl->dev = &pdev->dev; @@ -401,16 +471,14 @@ int lpi_pinctrl_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(pctrl->tlmm_base), "TLMM resource not provided\n"); - pctrl->slew_base = devm_platform_ioremap_resource(pdev, 1); - if (IS_ERR(pctrl->slew_base)) - return dev_err_probe(dev, PTR_ERR(pctrl->slew_base), - "Slew resource not provided\n"); - - if (of_property_read_bool(dev->of_node, "qcom,adsp-bypass-mode")) - ret = devm_clk_bulk_get_optional(dev, MAX_LPI_NUM_CLKS, pctrl->clks); - else - ret = devm_clk_bulk_get(dev, MAX_LPI_NUM_CLKS, pctrl->clks); + if (!(data->flags & LPI_FLAG_SLEW_RATE_SAME_REG)) { + pctrl->slew_base = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(pctrl->slew_base)) + return dev_err_probe(dev, PTR_ERR(pctrl->slew_base), + "Slew resource not provided\n"); + } + ret = devm_clk_bulk_get_optional(dev, MAX_LPI_NUM_CLKS, pctrl->clks); if (ret) return ret; @@ -430,10 +498,9 @@ int lpi_pinctrl_probe(struct platform_device *pdev) pctrl->chip.base = -1; pctrl->chip.ngpio = data->npins; pctrl->chip.label = dev_name(dev); - pctrl->chip.of_gpio_n_cells = 2; pctrl->chip.can_sleep = false; - mutex_init(&pctrl->slew_access_lock); + mutex_init(&pctrl->lock); pctrl->ctrl = devm_pinctrl_register(dev, &pctrl->desc, pctrl); if (IS_ERR(pctrl->ctrl)) { @@ -455,25 +522,23 @@ int lpi_pinctrl_probe(struct platform_device *pdev) return 0; err_pinctrl: - mutex_destroy(&pctrl->slew_access_lock); + mutex_destroy(&pctrl->lock); clk_bulk_disable_unprepare(MAX_LPI_NUM_CLKS, pctrl->clks); return ret; } EXPORT_SYMBOL_GPL(lpi_pinctrl_probe); -int lpi_pinctrl_remove(struct platform_device *pdev) +void lpi_pinctrl_remove(struct platform_device *pdev) { struct lpi_pinctrl *pctrl = platform_get_drvdata(pdev); int i; - mutex_destroy(&pctrl->slew_access_lock); + mutex_destroy(&pctrl->lock); clk_bulk_disable_unprepare(MAX_LPI_NUM_CLKS, pctrl->clks); for (i = 0; i < pctrl->data->npins; i++) pinctrl_generic_remove_group(pctrl->ctrl, i); - - return 0; } EXPORT_SYMBOL_GPL(lpi_pinctrl_remove); |
