mirror of
https://github.com/torvalds/linux.git
synced 2024-12-11 21:52:04 +00:00
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:
parent
18213ad418
commit
7f32d37009
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user