soc: pinctrl-lpi: add changes to support slew rate config

Add changes to read slew rate from device tree table
and set the same under set configs list.

Change-Id: I0d7e950d67d34b63c5a6436bb4d10b08a80c2c58
Signed-off-by: Karthikeyan Mani <kmani@codeaurora.org>
This commit is contained in:
Karthikeyan Mani 2019-02-20 17:24:10 -08:00
commit 39b70f5ff2

View file

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
*/
#include <linux/gpio.h>
@ -14,6 +14,7 @@
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/clk.h>
#include <linux/bitops.h>
#include <soc/snd_event.h>
#include <linux/pm_runtime.h>
#include <dsp/audio_notifier.h>
@ -21,39 +22,45 @@
#include "core.h"
#include "pinctrl-utils.h"
#define LPI_AUTO_SUSPEND_DELAY 100 /* delay in msec */
#define LPI_AUTO_SUSPEND_DELAY 100 /* delay in msec */
#define LPI_ADDRESS_SIZE 0x20000
#define LPI_ADDRESS_SIZE 0x20000
#define LPI_SLEW_ADDRESS_SIZE 0x1000
#define LPI_GPIO_REG_VAL_CTL 0x00
#define LPI_GPIO_REG_DIR_CTL 0x04
#define LPI_GPIO_REG_VAL_CTL 0x00
#define LPI_GPIO_REG_DIR_CTL 0x04
#define LPI_GPIO_REG_PULL_SHIFT 0x0
#define LPI_GPIO_REG_PULL_MASK 0x3
#define LPI_SLEW_REG_VAL_CTL 0x00
#define LPI_SLEW_RATE_MAX 0x03
#define LPI_SLEW_BITS_SIZE 0x02
#define LPI_SLEW_OFFSET_INVALID 0xFFFFFFFF
#define LPI_GPIO_REG_FUNCTION_SHIFT 0x2
#define LPI_GPIO_REG_FUNCTION_MASK 0x3C
#define LPI_GPIO_REG_PULL_SHIFT 0x0
#define LPI_GPIO_REG_PULL_MASK 0x3
#define LPI_GPIO_REG_OUT_STRENGTH_SHIFT 0x6
#define LPI_GPIO_REG_OUT_STRENGTH_MASK 0x1C0
#define LPI_GPIO_REG_FUNCTION_SHIFT 0x2
#define LPI_GPIO_REG_FUNCTION_MASK 0x3C
#define LPI_GPIO_REG_OE_SHIFT 0x9
#define LPI_GPIO_REG_OE_MASK 0x200
#define LPI_GPIO_REG_OUT_STRENGTH_SHIFT 0x6
#define LPI_GPIO_REG_OUT_STRENGTH_MASK 0x1C0
#define LPI_GPIO_REG_DIR_SHIFT 0x1
#define LPI_GPIO_REG_DIR_MASK 0x2
#define LPI_GPIO_REG_OE_SHIFT 0x9
#define LPI_GPIO_REG_OE_MASK 0x200
#define LPI_GPIO_BIAS_DISABLE 0x0
#define LPI_GPIO_PULL_DOWN 0x1
#define LPI_GPIO_KEEPER 0x2
#define LPI_GPIO_PULL_UP 0x3
#define LPI_GPIO_REG_DIR_SHIFT 0x1
#define LPI_GPIO_REG_DIR_MASK 0x2
#define LPI_GPIO_FUNC_GPIO "gpio"
#define LPI_GPIO_FUNC_FUNC1 "func1"
#define LPI_GPIO_FUNC_FUNC2 "func2"
#define LPI_GPIO_FUNC_FUNC3 "func3"
#define LPI_GPIO_FUNC_FUNC4 "func4"
#define LPI_GPIO_FUNC_FUNC5 "func5"
#define LPI_GPIO_BIAS_DISABLE 0x0
#define LPI_GPIO_PULL_DOWN 0x1
#define LPI_GPIO_KEEPER 0x2
#define LPI_GPIO_PULL_UP 0x3
#define LPI_GPIO_FUNC_GPIO "gpio"
#define LPI_GPIO_FUNC_FUNC1 "func1"
#define LPI_GPIO_FUNC_FUNC2 "func2"
#define LPI_GPIO_FUNC_FUNC3 "func3"
#define LPI_GPIO_FUNC_FUNC4 "func4"
#define LPI_GPIO_FUNC_FUNC5 "func5"
static bool lpi_dev_up;
static struct device *lpi_dev;
@ -70,30 +77,39 @@ enum lpi_gpio_func_index {
/**
* struct lpi_gpio_pad - keep current GPIO settings
* @offset: Nth GPIO in supported GPIOs.
* @offset: stores one of gpio_offset or slew_offset at a given time.
* @gpio_offset: Nth GPIO in supported GPIOs.
* @slew_offset: Nth GPIO's position in slew register in supported GPIOs.
* @output_enabled: Set to true if GPIO output logic is enabled.
* @value: value of a pin
* @base: Address base of LPI GPIO PAD.
* @base: stores one of gpio_base or slew_base at a given time.
* @gpio_base: Address base of LPI GPIO PAD.
* @slew_base: Address base of LPI SLEW PAD.
* @pullup: Constant current which flow through GPIO output buffer.
* @strength: No, Low, Medium, High
* @function: See lpi_gpio_functions[]
*/
struct lpi_gpio_pad {
u32 offset;
bool output_enabled;
bool value;
char __iomem *base;
unsigned int pullup;
unsigned int strength;
unsigned int function;
u32 offset;
u32 gpio_offset;
u32 slew_offset;
bool output_enabled;
bool value;
char __iomem *base;
char __iomem *gpio_base;
char __iomem *slew_base;
unsigned int pullup;
unsigned int strength;
unsigned int function;
};
struct lpi_gpio_state {
struct device *dev;
struct pinctrl_dev *ctrl;
struct gpio_chip chip;
char __iomem *base;
struct clk *lpass_npa_rsc_island;
struct device *dev;
struct pinctrl_dev *ctrl;
struct gpio_chip chip;
char __iomem *base;
struct clk *lpass_npa_rsc_island;
struct mutex slew_access_lock;
};
static const char *const lpi_gpio_groups[] = {
@ -106,6 +122,7 @@ static const char *const lpi_gpio_groups[] = {
#define LPI_TLMM_MAX_PINS 100
static u32 lpi_offset[LPI_TLMM_MAX_PINS];
static u32 lpi_slew_offset[LPI_TLMM_MAX_PINS];
static const char *const lpi_gpio_functions[] = {
[LPI_GPIO_FUNC_INDEX_GPIO] = LPI_GPIO_FUNC_GPIO,
@ -272,7 +289,9 @@ static int lpi_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
{
struct lpi_gpio_pad *pad;
unsigned int param, arg;
int i, ret = 0, val;
int i, ret = 0;
volatile unsigned long val;
struct lpi_gpio_state *state = dev_get_drvdata(pctldev->dev);
pad = pctldev->desc->pins[pin].drv_data;
@ -306,12 +325,44 @@ static int lpi_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
case PIN_CONFIG_DRIVE_STRENGTH:
pad->strength = arg;
break;
case PIN_CONFIG_SLEW_RATE:
if (pad->slew_base == NULL ||
pad->slew_offset == LPI_SLEW_OFFSET_INVALID) {
dev_dbg(pctldev->dev, "%s: invalid slew settings for pin: %d\n",
__func__, pin);
goto set_gpio;
}
if (arg > LPI_SLEW_RATE_MAX) {
dev_err(pctldev->dev, "%s: invalid slew rate %u for pin: %d\n",
__func__, arg, pin);
goto set_gpio;
}
pad->base = pad->slew_base;
pad->offset = 0;
mutex_lock(&state->slew_access_lock);
val = lpi_gpio_read(pad, LPI_SLEW_REG_VAL_CTL);
pad->offset = pad->slew_offset;
for (i = 0; i < LPI_SLEW_BITS_SIZE; i++) {
if (arg & 0x01)
set_bit(pad->offset, &val);
else
clear_bit(pad->offset, &val);
pad->offset++;
arg = arg >> 1;
}
pad->offset = 0;
lpi_gpio_write(pad, LPI_SLEW_REG_VAL_CTL, val);
mutex_unlock(&state->slew_access_lock);
break;
default:
ret = -EINVAL;
goto done;
}
}
set_gpio:
pad->base = pad->gpio_base;
pad->offset = pad->gpio_offset;
val = lpi_gpio_read(pad, LPI_GPIO_REG_VAL_CTL);
val &= ~(LPI_GPIO_REG_PULL_MASK | LPI_GPIO_REG_OUT_STRENGTH_MASK |
LPI_GPIO_REG_OE_MASK);
@ -502,7 +553,8 @@ static int lpi_pinctrl_probe(struct platform_device *pdev)
struct lpi_gpio_state *state;
int ret, npins, i;
char __iomem *lpi_base;
u32 reg;
char __iomem *slew_base;
u32 reg, slew_reg;
struct clk *lpass_npa_rsc_island = NULL;
ret = of_property_read_u32(dev->of_node, "reg", &reg);
@ -524,6 +576,16 @@ static int lpi_pinctrl_probe(struct platform_device *pdev)
return ret;
}
ret = of_property_read_u32_array(dev->of_node,
"qcom,lpi-slew-offset-tbl",
lpi_slew_offset, npins);
if (ret < 0) {
for (i = 0; i < npins; i++)
lpi_slew_offset[i] = LPI_SLEW_OFFSET_INVALID;
dev_dbg(dev, "%s: error in reading lpi slew offset table: %d\n",
__func__, ret);
}
state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;
@ -532,6 +594,23 @@ static int lpi_pinctrl_probe(struct platform_device *pdev)
state->dev = &pdev->dev;
slew_reg = 0;
ret = of_property_read_u32(dev->of_node, "qcom,slew-reg", &slew_reg);
if (!ret) {
slew_base = devm_ioremap(dev, slew_reg, LPI_SLEW_ADDRESS_SIZE);
if (slew_base == NULL) {
dev_err(dev,
"%s devm_ioremap failed for slew rate reg\n",
__func__);
ret = -ENOMEM;
goto err_io;
}
} else {
slew_base = NULL;
dev_dbg(dev, "error in reading lpi slew register: %d\n",
__func__, ret);
}
pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL);
if (!pindesc)
return -ENOMEM;
@ -566,8 +645,13 @@ static int lpi_pinctrl_probe(struct platform_device *pdev)
pindesc->number = i;
pindesc->name = lpi_gpio_groups[i];
pad->base = lpi_base;
pad->offset = lpi_offset[i];
pad->gpio_base = lpi_base;
pad->slew_base = slew_base;
pad->base = pad->gpio_base;
pad->gpio_offset = lpi_offset[i];
pad->slew_offset = lpi_slew_offset[i];
pad->offset = pad->gpio_offset;
}
state->chip = lpi_gpio_template;
@ -578,6 +662,8 @@ static int lpi_pinctrl_probe(struct platform_device *pdev)
state->chip.of_gpio_n_cells = 2;
state->chip.can_sleep = false;
mutex_init(&state->slew_access_lock);
state->ctrl = devm_pinctrl_register(dev, pctrldesc, state);
if (IS_ERR(state->ctrl))
return PTR_ERR(state->ctrl);
@ -636,18 +722,23 @@ err_snd_evt:
err_range:
gpiochip_remove(&state->chip);
err_chip:
mutex_destroy(&state->slew_access_lock);
err_io:
return ret;
}
static int lpi_pinctrl_remove(struct platform_device *pdev)
{
struct lpi_gpio_state *state = platform_get_drvdata(pdev);
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
snd_event_client_deregister(&pdev->dev);
audio_notifier_deregister("lpi_tlmm");
gpiochip_remove(&state->chip);
mutex_destroy(&state->slew_access_lock);
return 0;
}