pinctrl: lynxpoint: Add pin control operations

Add implementation for:
    - pin control, group information retrieval: count, name and pins
    - pin muxing:
      - function information (count, name and groups)
      - mux setting
      - GPIO control (enable, disable, set direction)
    - pin configuration:
      - pull disable, up and down
      - any other option is treated as not supported.

Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Acked-by: Mika Westerberg <mika.westerberg@linux.intel.com>
This commit is contained in:
Andy Shevchenko 2019-11-20 21:31:11 +02:00
parent 18213ad418
commit 7f32d37009

View file

@ -146,6 +146,7 @@ static const struct intel_pinctrl_soc_data lptlp_soc_data = {
/* Bitmapped register offsets */
#define LP_ACPI_OWNED 0x00 /* Bitmap, set by bios, 0: pin reserved for ACPI */
#define LP_IRQ2IOXAPIC 0x10 /* Bitmap, set by bios, 1: pin routed to IOxAPIC */
#define LP_GC 0x7C /* set APIC IRQ to IRQ14 or IRQ15 for all pins */
#define LP_INT_STAT 0x80
#define LP_INT_ENABLE 0x90
@ -166,7 +167,10 @@ static const struct intel_pinctrl_soc_data lptlp_soc_data = {
/* LP_CONFIG2 reg bits */
#define GPINDIS_BIT BIT(2) /* disable input sensing */
#define GPIWP_BIT (BIT(0) | BIT(1)) /* weak pull options */
#define GPIWP_MASK GENMASK(1, 0) /* weak pull options */
#define GPIWP_NONE 0 /* none */
#define GPIWP_DOWN 1 /* weak pull down */
#define GPIWP_UP 2 /* weak pull up */
/*
* Lynxpoint gpios are controlled through both bitmapped registers and
@ -195,6 +199,8 @@ static const struct intel_pinctrl_soc_data lptlp_soc_data = {
* ...
* LP94_CONFIG1 (gpio 94) ...
* LP94_CONFIG2 (gpio 94) ...
*
* IOxAPIC redirection map applies only for gpio 8-10, 13-14, 45-55.
*/
static struct intel_community *lp_get_community(struct intel_pinctrl *lg,
@ -246,6 +252,308 @@ static bool lp_gpio_acpi_use(struct intel_pinctrl *lg, unsigned int pin)
return !(ioread32(acpi_use) & BIT(pin % 32));
}
static bool lp_gpio_ioxapic_use(struct gpio_chip *chip, unsigned int offset)
{
void __iomem *ioxapic_use = lp_gpio_reg(chip, offset, LP_IRQ2IOXAPIC);
u32 value;
value = ioread32(ioxapic_use);
if (offset >= 8 && offset <= 10)
return !!(value & BIT(offset - 8 + 0));
if (offset >= 13 && offset <= 14)
return !!(value & BIT(offset - 13 + 3));
if (offset >= 45 && offset <= 55)
return !!(value & BIT(offset - 45 + 5));
return false;
}
static int lp_get_groups_count(struct pinctrl_dev *pctldev)
{
struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev);
return lg->soc->ngroups;
}
static const char *lp_get_group_name(struct pinctrl_dev *pctldev,
unsigned int selector)
{
struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev);
return lg->soc->groups[selector].name;
}
static int lp_get_group_pins(struct pinctrl_dev *pctldev,
unsigned int selector,
const unsigned int **pins,
unsigned int *num_pins)
{
struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev);
*pins = lg->soc->groups[selector].pins;
*num_pins = lg->soc->groups[selector].npins;
return 0;
}
static const struct pinctrl_ops lptlp_pinctrl_ops = {
.get_groups_count = lp_get_groups_count,
.get_group_name = lp_get_group_name,
.get_group_pins = lp_get_group_pins,
};
static int lp_get_functions_count(struct pinctrl_dev *pctldev)
{
struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev);
return lg->soc->nfunctions;
}
static const char *lp_get_function_name(struct pinctrl_dev *pctldev,
unsigned int selector)
{
struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev);
return lg->soc->functions[selector].name;
}
static int lp_get_function_groups(struct pinctrl_dev *pctldev,
unsigned int selector,
const char * const **groups,
unsigned int *num_groups)
{
struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev);
*groups = lg->soc->functions[selector].groups;
*num_groups = lg->soc->functions[selector].ngroups;
return 0;
}
static int lp_pinmux_set_mux(struct pinctrl_dev *pctldev,
unsigned int function, unsigned int group)
{
struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev);
const struct intel_pingroup *grp = &lg->soc->groups[group];
unsigned long flags;
int i;
raw_spin_lock_irqsave(&lg->lock, flags);
/* Now enable the mux setting for each pin in the group */
for (i = 0; i < grp->npins; i++) {
void __iomem *reg = lp_gpio_reg(&lg->chip, grp->pins[i], LP_CONFIG1);
u32 value;
value = ioread32(reg);
value &= ~USE_SEL_MASK;
if (grp->modes)
value |= grp->modes[i];
else
value |= grp->mode;
iowrite32(value, reg);
}
raw_spin_unlock_irqrestore(&lg->lock, flags);
return 0;
}
static int lp_gpio_request_enable(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned int pin)
{
struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev);
void __iomem *reg = lp_gpio_reg(&lg->chip, pin, LP_CONFIG1);
void __iomem *conf2 = lp_gpio_reg(&lg->chip, pin, LP_CONFIG2);
unsigned long flags;
u32 value;
pm_runtime_get(lg->dev);
raw_spin_lock_irqsave(&lg->lock, flags);
/*
* Reconfigure pin to GPIO mode if needed and issue a warning,
* since we expect firmware to configure it properly.
*/
value = ioread32(reg);
if ((value & USE_SEL_MASK) != USE_SEL_GPIO) {
iowrite32((value & USE_SEL_MASK) | USE_SEL_GPIO, reg);
dev_warn(lg->dev, FW_BUG "pin %u forcibly reconfigured as GPIO\n", pin);
}
/* Enable input sensing */
iowrite32(ioread32(conf2) & ~GPINDIS_BIT, conf2);
raw_spin_unlock_irqrestore(&lg->lock, flags);
return 0;
}
static void lp_gpio_disable_free(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned int pin)
{
struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev);
void __iomem *conf2 = lp_gpio_reg(&lg->chip, pin, LP_CONFIG2);
unsigned long flags;
raw_spin_lock_irqsave(&lg->lock, flags);
/* Disable input sensing */
iowrite32(ioread32(conf2) | GPINDIS_BIT, conf2);
raw_spin_unlock_irqrestore(&lg->lock, flags);
pm_runtime_put(lg->dev);
}
static int lp_gpio_set_direction(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned int pin, bool input)
{
struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev);
void __iomem *reg = lp_gpio_reg(&lg->chip, pin, LP_CONFIG1);
unsigned long flags;
u32 value;
raw_spin_lock_irqsave(&lg->lock, flags);
value = ioread32(reg);
value &= ~DIR_BIT;
if (input) {
value |= DIR_BIT;
} else {
/*
* Before making any direction modifications, do a check if GPIO
* is set for direct IRQ. On Lynxpoint, setting GPIO to output
* does not make sense, so let's at least warn the caller before
* they shoot themselves in the foot.
*/
WARN(lp_gpio_ioxapic_use(&lg->chip, pin),
"Potential Error: Setting GPIO to output with IOxAPIC redirection");
}
iowrite32(value, reg);
raw_spin_unlock_irqrestore(&lg->lock, flags);
return 0;
}
static const struct pinmux_ops lptlp_pinmux_ops = {
.get_functions_count = lp_get_functions_count,
.get_function_name = lp_get_function_name,
.get_function_groups = lp_get_function_groups,
.set_mux = lp_pinmux_set_mux,
.gpio_request_enable = lp_gpio_request_enable,
.gpio_disable_free = lp_gpio_disable_free,
.gpio_set_direction = lp_gpio_set_direction,
};
static int lp_pin_config_get(struct pinctrl_dev *pctldev, unsigned int pin,
unsigned long *config)
{
struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev);
void __iomem *conf2 = lp_gpio_reg(&lg->chip, pin, LP_CONFIG2);
enum pin_config_param param = pinconf_to_config_param(*config);
unsigned long flags;
u32 value, pull;
u16 arg = 0;
raw_spin_lock_irqsave(&lg->lock, flags);
value = ioread32(conf2);
raw_spin_unlock_irqrestore(&lg->lock, flags);
pull = value & GPIWP_MASK;
switch (param) {
case PIN_CONFIG_BIAS_DISABLE:
if (pull)
return -EINVAL;
break;
case PIN_CONFIG_BIAS_PULL_DOWN:
if (pull != GPIWP_DOWN)
return -EINVAL;
arg = 1;
break;
case PIN_CONFIG_BIAS_PULL_UP:
if (pull != GPIWP_UP)
return -EINVAL;
arg = 1;
break;
default:
return -ENOTSUPP;
}
*config = pinconf_to_config_packed(param, arg);
return 0;
}
static int lp_pin_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
unsigned long *configs, unsigned int num_configs)
{
struct intel_pinctrl *lg = pinctrl_dev_get_drvdata(pctldev);
void __iomem *conf2 = lp_gpio_reg(&lg->chip, pin, LP_CONFIG2);
enum pin_config_param param;
unsigned long flags;
int i, ret = 0;
u32 value;
raw_spin_lock_irqsave(&lg->lock, flags);
value = ioread32(conf2);
for (i = 0; i < num_configs; i++) {
param = pinconf_to_config_param(configs[i]);
switch (param) {
case PIN_CONFIG_BIAS_DISABLE:
value &= ~GPIWP_MASK;
break;
case PIN_CONFIG_BIAS_PULL_DOWN:
value &= ~GPIWP_MASK;
value |= GPIWP_DOWN;
break;
case PIN_CONFIG_BIAS_PULL_UP:
value &= ~GPIWP_MASK;
value |= GPIWP_UP;
break;
default:
ret = -ENOTSUPP;
}
if (ret)
break;
}
if (!ret)
iowrite32(value, conf2);
raw_spin_unlock_irqrestore(&lg->lock, flags);
return ret;
}
static const struct pinconf_ops lptlp_pinconf_ops = {
.is_generic = true,
.pin_config_get = lp_pin_config_get,
.pin_config_set = lp_pin_config_set,
};
static const struct pinctrl_desc lptlp_pinctrl_desc = {
.pctlops = &lptlp_pinctrl_ops,
.pmxops = &lptlp_pinmux_ops,
.confops = &lptlp_pinconf_ops,
.owner = THIS_MODULE,
};
static int lp_gpio_request(struct gpio_chip *chip, unsigned int offset)
{
struct intel_pinctrl *lg = gpiochip_get_data(chip);
@ -525,6 +833,11 @@ static int lp_gpio_probe(struct platform_device *pdev)
if (!lg->communities)
return -ENOMEM;
lg->pctldesc = lptlp_pinctrl_desc;
lg->pctldesc.name = dev_name(dev);
lg->pctldesc.pins = lg->soc->pins;
lg->pctldesc.npins = lg->soc->npins;
platform_set_drvdata(pdev, lg);
io_rc = platform_get_resource(pdev, IORESOURCE_IO, 0);