summaryrefslogtreecommitdiff
path: root/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pinctrl/qcom/pinctrl-lpass-lpi.c')
-rw-r--r--drivers/pinctrl/qcom/pinctrl-lpass-lpi.c173
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);