#include <linux/irq.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
+#include <linux/mfd/syscon.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/pinctrl/consumer.h>
#define MVEBU_GPIO_SOC_VARIANT_ORION 0x1
#define MVEBU_GPIO_SOC_VARIANT_MV78200 0x2
#define MVEBU_GPIO_SOC_VARIANT_ARMADAXP 0x3
+#define MVEBU_GPIO_SOC_VARIANT_A8K 0x4
#define MVEBU_MAX_GPIO_PER_BANK 32
struct mvebu_gpio_chip {
struct gpio_chip chip;
struct regmap *regs;
+ u32 offset;
struct regmap *percpu_regs;
int irqbase;
struct irq_domain *domain;
switch (mvchip->soc_variant) {
case MVEBU_GPIO_SOC_VARIANT_ORION:
case MVEBU_GPIO_SOC_VARIANT_MV78200:
+ case MVEBU_GPIO_SOC_VARIANT_A8K:
*map = mvchip->regs;
- *offset = GPIO_EDGE_CAUSE_OFF;
+ *offset = GPIO_EDGE_CAUSE_OFF + mvchip->offset;
break;
case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
cpu = smp_processor_id();
switch (mvchip->soc_variant) {
case MVEBU_GPIO_SOC_VARIANT_ORION:
+ case MVEBU_GPIO_SOC_VARIANT_A8K:
*map = mvchip->regs;
- *offset = GPIO_EDGE_MASK_OFF;
+ *offset = GPIO_EDGE_MASK_OFF + mvchip->offset;
break;
case MVEBU_GPIO_SOC_VARIANT_MV78200:
cpu = smp_processor_id();
switch (mvchip->soc_variant) {
case MVEBU_GPIO_SOC_VARIANT_ORION:
+ case MVEBU_GPIO_SOC_VARIANT_A8K:
*map = mvchip->regs;
- *offset = GPIO_LEVEL_MASK_OFF;
+ *offset = GPIO_LEVEL_MASK_OFF + mvchip->offset;
break;
case MVEBU_GPIO_SOC_VARIANT_MV78200:
cpu = smp_processor_id();
{
struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip);
- regmap_update_bits(mvchip->regs, GPIO_OUT_OFF,
+ regmap_update_bits(mvchip->regs, GPIO_OUT_OFF + mvchip->offset,
BIT(pin), value ? BIT(pin) : 0);
}
struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip);
u32 u;
- regmap_read(mvchip->regs, GPIO_IO_CONF_OFF, &u);
+ regmap_read(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, &u);
if (u & BIT(pin)) {
u32 data_in, in_pol;
- regmap_read(mvchip->regs, GPIO_DATA_IN_OFF, &data_in);
- regmap_read(mvchip->regs, GPIO_IN_POL_OFF, &in_pol);
+ regmap_read(mvchip->regs, GPIO_DATA_IN_OFF + mvchip->offset,
+ &data_in);
+ regmap_read(mvchip->regs, GPIO_IN_POL_OFF + mvchip->offset,
+ &in_pol);
u = data_in ^ in_pol;
} else {
- regmap_read(mvchip->regs, GPIO_OUT_OFF, &u);
+ regmap_read(mvchip->regs, GPIO_OUT_OFF + mvchip->offset, &u);
}
return (u >> pin) & 1;
{
struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip);
- regmap_update_bits(mvchip->regs, GPIO_BLINK_EN_OFF,
+ regmap_update_bits(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset,
BIT(pin), value ? BIT(pin) : 0);
}
if (ret)
return ret;
- regmap_update_bits(mvchip->regs, GPIO_IO_CONF_OFF,
+ regmap_update_bits(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset,
BIT(pin), BIT(pin));
return 0;
mvebu_gpio_blink(chip, pin, 0);
mvebu_gpio_set(chip, pin, value);
- regmap_update_bits(mvchip->regs, GPIO_IO_CONF_OFF,
+ regmap_update_bits(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset,
BIT(pin), 0);
return 0;
pin = d->hwirq;
- regmap_read(mvchip->regs, GPIO_IO_CONF_OFF, &u);
+ regmap_read(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, &u);
if ((u & BIT(pin)) == 0)
return -EINVAL;
switch (type) {
case IRQ_TYPE_EDGE_RISING:
case IRQ_TYPE_LEVEL_HIGH:
- regmap_update_bits(mvchip->regs, GPIO_IN_POL_OFF,
+ regmap_update_bits(mvchip->regs,
+ GPIO_IN_POL_OFF + mvchip->offset,
BIT(pin), 0);
break;
case IRQ_TYPE_EDGE_FALLING:
case IRQ_TYPE_LEVEL_LOW:
- regmap_update_bits(mvchip->regs, GPIO_IN_POL_OFF,
+ regmap_update_bits(mvchip->regs,
+ GPIO_IN_POL_OFF + mvchip->offset,
BIT(pin), BIT(pin));
break;
case IRQ_TYPE_EDGE_BOTH: {
u32 data_in, in_pol, val;
- regmap_read(mvchip->regs, GPIO_IN_POL_OFF, &in_pol);
- regmap_read(mvchip->regs, GPIO_DATA_IN_OFF, &data_in);
+ regmap_read(mvchip->regs,
+ GPIO_IN_POL_OFF + mvchip->offset, &in_pol);
+ regmap_read(mvchip->regs,
+ GPIO_DATA_IN_OFF + mvchip->offset, &data_in);
/*
* set initial polarity based on current input level
else
val = 0; /* raising */
- regmap_update_bits(mvchip->regs, GPIO_IN_POL_OFF,
+ regmap_update_bits(mvchip->regs,
+ GPIO_IN_POL_OFF + mvchip->offset,
BIT(pin), val);
break;
}
chained_irq_enter(chip, desc);
- regmap_read(mvchip->regs, GPIO_DATA_IN_OFF, &data_in);
+ regmap_read(mvchip->regs, GPIO_DATA_IN_OFF + mvchip->offset, &data_in);
level_mask = mvebu_gpio_read_level_mask(mvchip);
edge_cause = mvebu_gpio_read_edge_cause(mvchip);
edge_mask = mvebu_gpio_read_edge_mask(mvchip);
/* Swap polarity (race with GPIO line) */
u32 polarity;
- regmap_read(mvchip->regs, GPIO_IN_POL_OFF, &polarity);
+ regmap_read(mvchip->regs,
+ GPIO_IN_POL_OFF + mvchip->offset,
+ &polarity);
polarity ^= BIT(i);
- regmap_write(mvchip->regs, GPIO_IN_POL_OFF, polarity);
+ regmap_write(mvchip->regs,
+ GPIO_IN_POL_OFF + mvchip->offset,
+ polarity);
}
generic_handle_irq(irq);
state->period = 1;
}
- regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF, &u);
+ regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset, &u);
if (u)
state->enabled = true;
else
{
struct mvebu_pwm *mvpwm = mvchip->mvpwm;
- regmap_read(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF,
+ regmap_read(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset,
&mvpwm->blink_select);
mvpwm->blink_on_duration =
readl_relaxed(mvebu_pwmreg_blink_on_duration(mvpwm));
{
struct mvebu_pwm *mvpwm = mvchip->mvpwm;
- regmap_write(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF,
+ regmap_write(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset,
mvpwm->blink_select);
writel_relaxed(mvpwm->blink_on_duration,
mvebu_pwmreg_blink_on_duration(mvpwm));
set = U32_MAX;
else
return -EINVAL;
- regmap_write(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF, 0);
+ regmap_write(mvchip->regs,
+ GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, 0);
mvpwm = devm_kzalloc(dev, sizeof(struct mvebu_pwm), GFP_KERNEL);
if (!mvpwm)
u32 out, io_conf, blink, in_pol, data_in, cause, edg_msk, lvl_msk;
int i;
- regmap_read(mvchip->regs, GPIO_OUT_OFF, &out);
- regmap_read(mvchip->regs, GPIO_IO_CONF_OFF, &io_conf);
- regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF, &blink);
- regmap_read(mvchip->regs, GPIO_IN_POL_OFF, &in_pol);
- regmap_read(mvchip->regs, GPIO_DATA_IN_OFF, &data_in);
+ regmap_read(mvchip->regs, GPIO_OUT_OFF + mvchip->offset, &out);
+ regmap_read(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, &io_conf);
+ regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset, &blink);
+ regmap_read(mvchip->regs, GPIO_IN_POL_OFF + mvchip->offset, &in_pol);
+ regmap_read(mvchip->regs, GPIO_DATA_IN_OFF + mvchip->offset, &data_in);
cause = mvebu_gpio_read_edge_cause(mvchip);
edg_msk = mvebu_gpio_read_edge_mask(mvchip);
lvl_msk = mvebu_gpio_read_level_mask(mvchip);
.compatible = "marvell,armada-370-xp-gpio",
.data = (void *) MVEBU_GPIO_SOC_VARIANT_ORION,
},
+ {
+ .compatible = "marvell,armada-8k-gpio",
+ .data = (void *) MVEBU_GPIO_SOC_VARIANT_A8K,
+ },
{
/* sentinel */
},
struct mvebu_gpio_chip *mvchip = platform_get_drvdata(pdev);
int i;
- regmap_read(mvchip->regs, GPIO_OUT_OFF, &mvchip->out_reg);
- regmap_read(mvchip->regs, GPIO_IO_CONF_OFF, &mvchip->io_conf_reg);
- regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF, &mvchip->blink_en_reg);
- regmap_read(mvchip->regs, GPIO_IN_POL_OFF, &mvchip->in_pol_reg);
+ regmap_read(mvchip->regs, GPIO_OUT_OFF + mvchip->offset,
+ &mvchip->out_reg);
+ regmap_read(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset,
+ &mvchip->io_conf_reg);
+ regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset,
+ &mvchip->blink_en_reg);
+ regmap_read(mvchip->regs, GPIO_IN_POL_OFF + mvchip->offset,
+ &mvchip->in_pol_reg);
switch (mvchip->soc_variant) {
case MVEBU_GPIO_SOC_VARIANT_ORION:
- regmap_read(mvchip->regs, GPIO_EDGE_MASK_OFF,
+ case MVEBU_GPIO_SOC_VARIANT_A8K:
+ regmap_read(mvchip->regs, GPIO_EDGE_MASK_OFF + mvchip->offset,
&mvchip->edge_mask_regs[0]);
- regmap_read(mvchip->regs, GPIO_LEVEL_MASK_OFF,
+ regmap_read(mvchip->regs, GPIO_LEVEL_MASK_OFF + mvchip->offset,
&mvchip->level_mask_regs[0]);
break;
case MVEBU_GPIO_SOC_VARIANT_MV78200:
struct mvebu_gpio_chip *mvchip = platform_get_drvdata(pdev);
int i;
- regmap_write(mvchip->regs, GPIO_OUT_OFF, mvchip->out_reg);
- regmap_write(mvchip->regs, GPIO_IO_CONF_OFF, mvchip->io_conf_reg);
- regmap_write(mvchip->regs, GPIO_BLINK_EN_OFF, mvchip->blink_en_reg);
- regmap_write(mvchip->regs, GPIO_IN_POL_OFF, mvchip->in_pol_reg);
+ regmap_write(mvchip->regs, GPIO_OUT_OFF + mvchip->offset,
+ mvchip->out_reg);
+ regmap_write(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset,
+ mvchip->io_conf_reg);
+ regmap_write(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset,
+ mvchip->blink_en_reg);
+ regmap_write(mvchip->regs, GPIO_IN_POL_OFF + mvchip->offset,
+ mvchip->in_pol_reg);
switch (mvchip->soc_variant) {
case MVEBU_GPIO_SOC_VARIANT_ORION:
- regmap_write(mvchip->regs, GPIO_EDGE_MASK_OFF,
+ case MVEBU_GPIO_SOC_VARIANT_A8K:
+ regmap_write(mvchip->regs, GPIO_EDGE_MASK_OFF + mvchip->offset,
mvchip->edge_mask_regs[0]);
- regmap_write(mvchip->regs, GPIO_LEVEL_MASK_OFF,
+ regmap_write(mvchip->regs, GPIO_LEVEL_MASK_OFF + mvchip->offset,
mvchip->level_mask_regs[0]);
break;
case MVEBU_GPIO_SOC_VARIANT_MV78200:
.fast_io = true,
};
+static int mvebu_gpio_probe_raw(struct platform_device *pdev,
+ struct mvebu_gpio_chip *mvchip)
+{
+ struct resource *res;
+ void __iomem *base;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ mvchip->regs = devm_regmap_init_mmio(&pdev->dev, base,
+ &mvebu_gpio_regmap_config);
+ if (IS_ERR(mvchip->regs))
+ return PTR_ERR(mvchip->regs);
+
+ /*
+ * For the legacy SoCs, the regmap directly maps to the GPIO
+ * registers, so no offset is needed.
+ */
+ mvchip->offset = 0;
+
+ /*
+ * The Armada XP has a second range of registers for the
+ * per-CPU registers
+ */
+ if (mvchip->soc_variant == MVEBU_GPIO_SOC_VARIANT_ARMADAXP) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ mvchip->percpu_regs =
+ devm_regmap_init_mmio(&pdev->dev, base,
+ &mvebu_gpio_regmap_config);
+ if (IS_ERR(mvchip->percpu_regs))
+ return PTR_ERR(mvchip->percpu_regs);
+ }
+
+ return 0;
+}
+
+static int mvebu_gpio_probe_syscon(struct platform_device *pdev,
+ struct mvebu_gpio_chip *mvchip)
+{
+ mvchip->regs = syscon_node_to_regmap(pdev->dev.parent->of_node);
+ if (IS_ERR(mvchip->regs))
+ return PTR_ERR(mvchip->regs);
+
+ if (of_property_read_u32(pdev->dev.of_node, "offset", &mvchip->offset))
+ return -EINVAL;
+
+ return 0;
+}
+
static int mvebu_gpio_probe(struct platform_device *pdev)
{
struct mvebu_gpio_chip *mvchip;
const struct of_device_id *match;
struct device_node *np = pdev->dev.of_node;
- struct resource *res;
struct irq_chip_generic *gc;
struct irq_chip_type *ct;
- void __iomem *base;
unsigned int ngpios;
bool have_irqs;
int soc_variant;
mvchip->chip.of_node = np;
mvchip->chip.dbg_show = mvebu_gpio_dbg_show;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(base))
- return PTR_ERR(base);
-
- mvchip->regs = devm_regmap_init_mmio(&pdev->dev, base,
- &mvebu_gpio_regmap_config);
- if (IS_ERR(mvchip->regs))
- return PTR_ERR(mvchip->regs);
+ if (soc_variant == MVEBU_GPIO_SOC_VARIANT_A8K)
+ err = mvebu_gpio_probe_syscon(pdev, mvchip);
+ else
+ err = mvebu_gpio_probe_raw(pdev, mvchip);
- /*
- * The Armada XP has a second range of registers for the
- * per-CPU registers
- */
- if (soc_variant == MVEBU_GPIO_SOC_VARIANT_ARMADAXP) {
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(base))
- return PTR_ERR(base);
-
- mvchip->percpu_regs =
- devm_regmap_init_mmio(&pdev->dev, base,
- &mvebu_gpio_regmap_config);
- if (IS_ERR(mvchip->percpu_regs))
- return PTR_ERR(mvchip->percpu_regs);
- }
+ if (err)
+ return err;
/*
* Mask and clear GPIO interrupts.
*/
switch (soc_variant) {
case MVEBU_GPIO_SOC_VARIANT_ORION:
- regmap_write(mvchip->regs, GPIO_EDGE_CAUSE_OFF, 0);
- regmap_write(mvchip->regs, GPIO_EDGE_MASK_OFF, 0);
- regmap_write(mvchip->regs, GPIO_LEVEL_MASK_OFF, 0);
+ case MVEBU_GPIO_SOC_VARIANT_A8K:
+ regmap_write(mvchip->regs,
+ GPIO_EDGE_CAUSE_OFF + mvchip->offset, 0);
+ regmap_write(mvchip->regs,
+ GPIO_EDGE_MASK_OFF + mvchip->offset, 0);
+ regmap_write(mvchip->regs,
+ GPIO_LEVEL_MASK_OFF + mvchip->offset, 0);
break;
case MVEBU_GPIO_SOC_VARIANT_MV78200:
regmap_write(mvchip->regs, GPIO_EDGE_CAUSE_OFF, 0);