Merge branch 'ib-qcom-ssbi' into devel
This commit is contained in:
@@ -150,6 +150,7 @@ config PINCTRL_QCOM_SSBI_PMIC
|
||||
select PINMUX
|
||||
select PINCONF
|
||||
select GENERIC_PINCONF
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
help
|
||||
This is the pinctrl, pinmux, pinconf and gpiolib driver for the
|
||||
Qualcomm GPIO and MPP blocks found in the Qualcomm PMIC's chips,
|
||||
|
@@ -55,6 +55,8 @@
|
||||
|
||||
#define PM8XXX_MAX_GPIOS 44
|
||||
|
||||
#define PM8XXX_GPIO_PHYSICAL_OFFSET 1
|
||||
|
||||
/* custom pinconf parameters */
|
||||
#define PM8XXX_QCOM_DRIVE_STRENGH (PIN_CONFIG_END + 1)
|
||||
#define PM8XXX_QCOM_PULL_UP_STRENGTH (PIN_CONFIG_END + 2)
|
||||
@@ -99,6 +101,9 @@ struct pm8xxx_gpio {
|
||||
|
||||
struct pinctrl_desc desc;
|
||||
unsigned npins;
|
||||
|
||||
struct fwnode_handle *fwnode;
|
||||
struct irq_domain *domain;
|
||||
};
|
||||
|
||||
static const struct pinconf_generic_params pm8xxx_gpio_bindings[] = {
|
||||
@@ -499,11 +504,12 @@ static int pm8xxx_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||
|
||||
if (pin->mode == PM8XXX_GPIO_MODE_OUTPUT) {
|
||||
ret = pin->output_value;
|
||||
} else {
|
||||
} else if (pin->irq >= 0) {
|
||||
ret = irq_get_irqchip_state(pin->irq, IRQCHIP_STATE_LINE_LEVEL, &state);
|
||||
if (!ret)
|
||||
ret = !!state;
|
||||
}
|
||||
} else
|
||||
ret = -EINVAL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -533,7 +539,7 @@ static int pm8xxx_gpio_of_xlate(struct gpio_chip *chip,
|
||||
if (flags)
|
||||
*flags = gpio_desc->args[1];
|
||||
|
||||
return gpio_desc->args[0] - 1;
|
||||
return gpio_desc->args[0] - PM8XXX_GPIO_PHYSICAL_OFFSET;
|
||||
}
|
||||
|
||||
|
||||
@@ -541,8 +547,31 @@ static int pm8xxx_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip);
|
||||
struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data;
|
||||
struct irq_fwspec fwspec;
|
||||
int ret;
|
||||
|
||||
return pin->irq;
|
||||
fwspec.fwnode = pctrl->fwnode;
|
||||
fwspec.param_count = 2;
|
||||
fwspec.param[0] = offset + PM8XXX_GPIO_PHYSICAL_OFFSET;
|
||||
fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
|
||||
|
||||
ret = irq_create_fwspec_mapping(&fwspec);
|
||||
|
||||
/*
|
||||
* Cache the IRQ since pm8xxx_gpio_get() needs this to get determine the
|
||||
* line level.
|
||||
*/
|
||||
pin->irq = ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void pm8xxx_gpio_free(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip);
|
||||
struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data;
|
||||
|
||||
pin->irq = -1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
@@ -571,7 +600,7 @@ static void pm8xxx_gpio_dbg_show_one(struct seq_file *s,
|
||||
"no", "high", "medium", "low"
|
||||
};
|
||||
|
||||
seq_printf(s, " gpio%-2d:", offset + 1);
|
||||
seq_printf(s, " gpio%-2d:", offset + PM8XXX_GPIO_PHYSICAL_OFFSET);
|
||||
if (pin->disable) {
|
||||
seq_puts(s, " ---");
|
||||
} else {
|
||||
@@ -603,6 +632,7 @@ static void pm8xxx_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
||||
#endif
|
||||
|
||||
static const struct gpio_chip pm8xxx_gpio_template = {
|
||||
.free = pm8xxx_gpio_free,
|
||||
.direction_input = pm8xxx_gpio_direction_input,
|
||||
.direction_output = pm8xxx_gpio_direction_output,
|
||||
.get = pm8xxx_gpio_get,
|
||||
@@ -664,13 +694,75 @@ static int pm8xxx_pin_populate(struct pm8xxx_gpio *pctrl,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_chip pm8xxx_irq_chip = {
|
||||
.name = "ssbi-gpio",
|
||||
.irq_mask_ack = irq_chip_mask_ack_parent,
|
||||
.irq_unmask = irq_chip_unmask_parent,
|
||||
.irq_set_type = irq_chip_set_type_parent,
|
||||
.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE,
|
||||
};
|
||||
|
||||
static int pm8xxx_domain_translate(struct irq_domain *domain,
|
||||
struct irq_fwspec *fwspec,
|
||||
unsigned long *hwirq,
|
||||
unsigned int *type)
|
||||
{
|
||||
struct pm8xxx_gpio *pctrl = container_of(domain->host_data,
|
||||
struct pm8xxx_gpio, chip);
|
||||
|
||||
if (fwspec->param_count != 2 || fwspec->param[0] < 1 ||
|
||||
fwspec->param[0] > pctrl->chip.ngpio)
|
||||
return -EINVAL;
|
||||
|
||||
*hwirq = fwspec->param[0] - PM8XXX_GPIO_PHYSICAL_OFFSET;
|
||||
*type = fwspec->param[1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm8xxx_domain_alloc(struct irq_domain *domain, unsigned int virq,
|
||||
unsigned int nr_irqs, void *data)
|
||||
{
|
||||
struct pm8xxx_gpio *pctrl = container_of(domain->host_data,
|
||||
struct pm8xxx_gpio, chip);
|
||||
struct irq_fwspec *fwspec = data;
|
||||
struct irq_fwspec parent_fwspec;
|
||||
irq_hw_number_t hwirq;
|
||||
unsigned int type;
|
||||
int ret, i;
|
||||
|
||||
ret = pm8xxx_domain_translate(domain, fwspec, &hwirq, &type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < nr_irqs; i++)
|
||||
irq_domain_set_info(domain, virq + i, hwirq + i,
|
||||
&pm8xxx_irq_chip, pctrl, handle_level_irq,
|
||||
NULL, NULL);
|
||||
|
||||
parent_fwspec.fwnode = domain->parent->fwnode;
|
||||
parent_fwspec.param_count = 2;
|
||||
parent_fwspec.param[0] = hwirq + 0xc0;
|
||||
parent_fwspec.param[1] = fwspec->param[1];
|
||||
|
||||
return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
|
||||
&parent_fwspec);
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops pm8xxx_domain_ops = {
|
||||
.activate = gpiochip_irq_domain_activate,
|
||||
.alloc = pm8xxx_domain_alloc,
|
||||
.deactivate = gpiochip_irq_domain_deactivate,
|
||||
.free = irq_domain_free_irqs_common,
|
||||
.translate = pm8xxx_domain_translate,
|
||||
};
|
||||
|
||||
static const struct of_device_id pm8xxx_gpio_of_match[] = {
|
||||
{ .compatible = "qcom,pm8018-gpio" },
|
||||
{ .compatible = "qcom,pm8038-gpio" },
|
||||
{ .compatible = "qcom,pm8058-gpio" },
|
||||
{ .compatible = "qcom,pm8917-gpio" },
|
||||
{ .compatible = "qcom,pm8921-gpio" },
|
||||
{ .compatible = "qcom,ssbi-gpio" },
|
||||
{ .compatible = "qcom,pm8018-gpio", .data = (void *) 6 },
|
||||
{ .compatible = "qcom,pm8038-gpio", .data = (void *) 12 },
|
||||
{ .compatible = "qcom,pm8058-gpio", .data = (void *) 44 },
|
||||
{ .compatible = "qcom,pm8917-gpio", .data = (void *) 38 },
|
||||
{ .compatible = "qcom,pm8921-gpio", .data = (void *) 44 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pm8xxx_gpio_of_match);
|
||||
@@ -678,22 +770,18 @@ MODULE_DEVICE_TABLE(of, pm8xxx_gpio_of_match);
|
||||
static int pm8xxx_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct pm8xxx_pin_data *pin_data;
|
||||
struct irq_domain *parent_domain;
|
||||
struct device_node *parent_node;
|
||||
struct pinctrl_pin_desc *pins;
|
||||
struct pm8xxx_gpio *pctrl;
|
||||
int ret;
|
||||
int i, npins;
|
||||
int ret, i;
|
||||
|
||||
pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL);
|
||||
if (!pctrl)
|
||||
return -ENOMEM;
|
||||
|
||||
pctrl->dev = &pdev->dev;
|
||||
npins = platform_irq_count(pdev);
|
||||
if (!npins)
|
||||
return -EINVAL;
|
||||
if (npins < 0)
|
||||
return npins;
|
||||
pctrl->npins = npins;
|
||||
pctrl->npins = (uintptr_t) device_get_match_data(&pdev->dev);
|
||||
|
||||
pctrl->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
if (!pctrl->regmap) {
|
||||
@@ -720,12 +808,7 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev)
|
||||
|
||||
for (i = 0; i < pctrl->desc.npins; i++) {
|
||||
pin_data[i].reg = SSBI_REG_ADDR_GPIO(i);
|
||||
pin_data[i].irq = platform_get_irq(pdev, i);
|
||||
if (pin_data[i].irq < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"missing interrupts for pin %d\n", i);
|
||||
return pin_data[i].irq;
|
||||
}
|
||||
pin_data[i].irq = -1;
|
||||
|
||||
ret = pm8xxx_pin_populate(pctrl, &pin_data[i]);
|
||||
if (ret)
|
||||
@@ -756,10 +839,29 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev)
|
||||
pctrl->chip.of_gpio_n_cells = 2;
|
||||
pctrl->chip.label = dev_name(pctrl->dev);
|
||||
pctrl->chip.ngpio = pctrl->npins;
|
||||
|
||||
parent_node = of_irq_find_parent(pctrl->dev->of_node);
|
||||
if (!parent_node)
|
||||
return -ENXIO;
|
||||
|
||||
parent_domain = irq_find_host(parent_node);
|
||||
of_node_put(parent_node);
|
||||
if (!parent_domain)
|
||||
return -ENXIO;
|
||||
|
||||
pctrl->fwnode = of_node_to_fwnode(pctrl->dev->of_node);
|
||||
pctrl->domain = irq_domain_create_hierarchy(parent_domain, 0,
|
||||
pctrl->chip.ngpio,
|
||||
pctrl->fwnode,
|
||||
&pm8xxx_domain_ops,
|
||||
&pctrl->chip);
|
||||
if (!pctrl->domain)
|
||||
return -ENODEV;
|
||||
|
||||
ret = gpiochip_add_data(&pctrl->chip, pctrl);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed register gpiochip\n");
|
||||
return ret;
|
||||
goto err_chip_add_data;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -789,6 +891,8 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev)
|
||||
|
||||
unregister_gpiochip:
|
||||
gpiochip_remove(&pctrl->chip);
|
||||
err_chip_add_data:
|
||||
irq_domain_remove(pctrl->domain);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -798,6 +902,7 @@ static int pm8xxx_gpio_remove(struct platform_device *pdev)
|
||||
struct pm8xxx_gpio *pctrl = platform_get_drvdata(pdev);
|
||||
|
||||
gpiochip_remove(&pctrl->chip);
|
||||
irq_domain_remove(pctrl->domain);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Reference in New Issue
Block a user