};
static int gpio_banks;
+static unsigned long at91_gpio_caps;
+
+/* All PIO controllers support PIO3 features */
+#define AT91_GPIO_CAP_PIO3 (1 << 0)
+
+#define has_pio3() (at91_gpio_caps & AT91_GPIO_CAP_PIO3)
+
+/*--------------------------------------------------------------------------*/
static inline void __iomem *pin_to_controller(unsigned pin)
{
}
+static char peripheral_function(void __iomem *pio, unsigned mask)
+{
+ char ret = 'X';
+ u8 select;
+
+ if (pio) {
+ if (has_pio3()) {
+ select = !!(__raw_readl(pio + PIO_ABCDSR1) & mask);
+ select |= (!!(__raw_readl(pio + PIO_ABCDSR2) & mask) << 1);
+ ret = 'A' + select;
+ } else {
+ ret = __raw_readl(pio + PIO_ABSR) & mask ?
+ 'B' : 'A';
+ }
+ }
+
+ return ret;
+}
+
/*--------------------------------------------------------------------------*/
/* Not all hardware capabilities are exposed through these calls; they
__raw_writel(mask, pio + PIO_IDR);
__raw_writel(mask, pio + (use_pullup ? PIO_PUER : PIO_PUDR));
- __raw_writel(mask, pio + PIO_ASR);
+ if (has_pio3()) {
+ __raw_writel(__raw_readl(pio + PIO_ABCDSR1) & ~mask,
+ pio + PIO_ABCDSR1);
+ __raw_writel(__raw_readl(pio + PIO_ABCDSR2) & ~mask,
+ pio + PIO_ABCDSR2);
+ } else {
+ __raw_writel(mask, pio + PIO_ASR);
+ }
__raw_writel(mask, pio + PIO_PDR);
return 0;
}
__raw_writel(mask, pio + PIO_IDR);
__raw_writel(mask, pio + (use_pullup ? PIO_PUER : PIO_PUDR));
- __raw_writel(mask, pio + PIO_BSR);
+ if (has_pio3()) {
+ __raw_writel(__raw_readl(pio + PIO_ABCDSR1) | mask,
+ pio + PIO_ABCDSR1);
+ __raw_writel(__raw_readl(pio + PIO_ABCDSR2) & ~mask,
+ pio + PIO_ABCDSR2);
+ } else {
+ __raw_writel(mask, pio + PIO_BSR);
+ }
__raw_writel(mask, pio + PIO_PDR);
return 0;
}
/*
- * mux the pin to the gpio controller (instead of "A" or "B" peripheral), and
- * configure it for an input.
+ * mux the pin to the "C" internal peripheral role.
+ */
+int __init_or_module at91_set_C_periph(unsigned pin, int use_pullup)
+{
+ void __iomem *pio = pin_to_controller(pin);
+ unsigned mask = pin_to_mask(pin);
+
+ if (!pio || !has_pio3())
+ return -EINVAL;
+
+ __raw_writel(mask, pio + PIO_IDR);
+ __raw_writel(mask, pio + (use_pullup ? PIO_PUER : PIO_PUDR));
+ __raw_writel(__raw_readl(pio + PIO_ABCDSR1) & ~mask, pio + PIO_ABCDSR1);
+ __raw_writel(__raw_readl(pio + PIO_ABCDSR2) | mask, pio + PIO_ABCDSR2);
+ __raw_writel(mask, pio + PIO_PDR);
+ return 0;
+}
+EXPORT_SYMBOL(at91_set_C_periph);
+
+
+/*
+ * mux the pin to the "D" internal peripheral role.
+ */
+int __init_or_module at91_set_D_periph(unsigned pin, int use_pullup)
+{
+ void __iomem *pio = pin_to_controller(pin);
+ unsigned mask = pin_to_mask(pin);
+
+ if (!pio || !has_pio3())
+ return -EINVAL;
+
+ __raw_writel(mask, pio + PIO_IDR);
+ __raw_writel(mask, pio + (use_pullup ? PIO_PUER : PIO_PUDR));
+ __raw_writel(__raw_readl(pio + PIO_ABCDSR1) | mask, pio + PIO_ABCDSR1);
+ __raw_writel(__raw_readl(pio + PIO_ABCDSR2) | mask, pio + PIO_ABCDSR2);
+ __raw_writel(mask, pio + PIO_PDR);
+ return 0;
+}
+EXPORT_SYMBOL(at91_set_D_periph);
+
+
+/*
+ * mux the pin to the gpio controller (instead of "A", "B", "C"
+ * or "D" peripheral), and configure it for an input.
*/
int __init_or_module at91_set_gpio_input(unsigned pin, int use_pullup)
{
/*
- * mux the pin to the gpio controller (instead of "A" or "B" peripheral),
- * and configure it for an output.
+ * mux the pin to the gpio controller (instead of "A", "B", "C"
+ * or "D" peripheral), and configure it for an output.
*/
int __init_or_module at91_set_gpio_output(unsigned pin, int value)
{
if (!pio)
return -EINVAL;
+
+ if (has_pio3() && is_on)
+ __raw_writel(mask, pio + PIO_IFSCDR);
__raw_writel(mask, pio + (is_on ? PIO_IFER : PIO_IFDR));
return 0;
}
EXPORT_SYMBOL(at91_set_deglitch);
+/*
+ * enable/disable the debounce filter;
+ */
+int __init_or_module at91_set_debounce(unsigned pin, int is_on, int div)
+{
+ void __iomem *pio = pin_to_controller(pin);
+ unsigned mask = pin_to_mask(pin);
+
+ if (!pio || !has_pio3())
+ return -EINVAL;
+
+ if (is_on) {
+ __raw_writel(mask, pio + PIO_IFSCER);
+ __raw_writel(div & PIO_SCDR_DIV, pio + PIO_SCDR);
+ __raw_writel(mask, pio + PIO_IFER);
+ } else {
+ __raw_writel(mask, pio + PIO_IFDR);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(at91_set_debounce);
+
/*
* enable/disable the multi-driver; This is only valid for output and
* allows the output pin to run as an open collector output.
}
EXPORT_SYMBOL(at91_set_multi_drive);
+/*
+ * enable/disable the pull-down.
+ * If pull-up already enabled while calling the function, we disable it.
+ */
+int __init_or_module at91_set_pulldown(unsigned pin, int is_on)
+{
+ void __iomem *pio = pin_to_controller(pin);
+ unsigned mask = pin_to_mask(pin);
+
+ if (!pio || !has_pio3())
+ return -EINVAL;
+
+ /* Disable pull-up anyway */
+ __raw_writel(mask, pio + PIO_PUDR);
+ __raw_writel(mask, pio + (is_on ? PIO_PPDER : PIO_PPDDR));
+ return 0;
+}
+EXPORT_SYMBOL(at91_set_pulldown);
+
+/*
+ * disable Schmitt trigger
+ */
+int __init_or_module at91_disable_schmitt_trig(unsigned pin)
+{
+ void __iomem *pio = pin_to_controller(pin);
+ unsigned mask = pin_to_mask(pin);
+
+ if (!pio || !has_pio3())
+ return -EINVAL;
+
+ __raw_writel(__raw_readl(pio + PIO_SCHMITT) | mask, pio + PIO_SCHMITT);
+ return 0;
+}
+EXPORT_SYMBOL(at91_disable_schmitt_trig);
+
/*
* assuming the pin is muxed as a gpio output, set its value.
*/
* To use any AT91_PIN_* as an externally triggered IRQ, first call
* at91_set_gpio_input() then maybe enable its glitch filter.
* Then just request_irq() with the pin ID; it works like any ARM IRQ
- * handler, though it always triggers on rising and falling edges.
+ * handler.
+ * First implementation always triggers on rising and falling edges
+ * whereas the newer PIO3 can be additionally configured to trigger on
+ * level, edge with any polarity.
*
* Alternatively, certain pins may be used directly as IRQ0..IRQ6 after
* configuring them with at91_set_a_periph() or at91_set_b_periph().
}
}
+/* Alternate irq type for PIO3 support */
+static int alt_gpio_irq_type(struct irq_data *d, unsigned type)
+{
+ struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d);
+ void __iomem *pio = at91_gpio->regbase;
+ unsigned mask = 1 << d->hwirq;
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ __raw_writel(mask, pio + PIO_ESR);
+ __raw_writel(mask, pio + PIO_REHLSR);
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ __raw_writel(mask, pio + PIO_ESR);
+ __raw_writel(mask, pio + PIO_FELLSR);
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ __raw_writel(mask, pio + PIO_LSR);
+ __raw_writel(mask, pio + PIO_FELLSR);
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ __raw_writel(mask, pio + PIO_LSR);
+ __raw_writel(mask, pio + PIO_REHLSR);
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ /*
+ * disable additional interrupt modes:
+ * fall back to default behavior
+ */
+ __raw_writel(mask, pio + PIO_AIMDR);
+ return 0;
+ case IRQ_TYPE_NONE:
+ default:
+ pr_warn("AT91: No type for irq %d\n", gpio_to_irq(d->irq));
+ return -EINVAL;
+ }
+
+ /* enable additional interrupt modes */
+ __raw_writel(mask, pio + PIO_AIMER);
+
+ return 0;
+}
+
static struct irq_chip gpio_irqchip = {
.name = "GPIO",
.irq_disable = gpio_irq_mask,
.irq_mask = gpio_irq_mask,
.irq_unmask = gpio_irq_unmask,
- .irq_set_type = gpio_irq_type,
+ /* .irq_set_type is set dynamically */
.irq_set_wake = gpio_irq_set_wake,
};
#ifdef CONFIG_DEBUG_FS
+static void gpio_printf(struct seq_file *s, void __iomem *pio, unsigned mask)
+{
+ char *trigger = NULL;
+ char *polarity = NULL;
+
+ if (__raw_readl(pio + PIO_IMR) & mask) {
+ if (!has_pio3() || !(__raw_readl(pio + PIO_AIMMR) & mask )) {
+ trigger = "edge";
+ polarity = "both";
+ } else {
+ if (__raw_readl(pio + PIO_ELSR) & mask) {
+ trigger = "level";
+ polarity = __raw_readl(pio + PIO_FRLHSR) & mask ?
+ "high" : "low";
+ } else {
+ trigger = "edge";
+ polarity = __raw_readl(pio + PIO_FRLHSR) & mask ?
+ "rising" : "falling";
+ }
+ }
+ seq_printf(s, "IRQ:%s-%s\t", trigger, polarity);
+ } else {
+ seq_printf(s, "GPIO:%s\t\t",
+ __raw_readl(pio + PIO_PDSR) & mask ? "1" : "0");
+ }
+}
+
static int at91_gpio_show(struct seq_file *s, void *unused)
{
int bank, j;
/* print heading */
seq_printf(s, "Pin\t");
for (bank = 0; bank < gpio_banks; bank++) {
- seq_printf(s, "PIO%c\t", 'A' + bank);
+ seq_printf(s, "PIO%c\t\t", 'A' + bank);
};
seq_printf(s, "\n\n");
unsigned mask = pin_to_mask(pin);
if (__raw_readl(pio + PIO_PSR) & mask)
- seq_printf(s, "GPIO:%s", __raw_readl(pio + PIO_PDSR) & mask ? "1" : "0");
+ gpio_printf(s, pio, mask);
else
- seq_printf(s, "%s", __raw_readl(pio + PIO_ABSR) & mask ? "B" : "A");
-
- seq_printf(s, "\t");
+ seq_printf(s, "%c\t\t",
+ peripheral_function(pio, mask));
}
seq_printf(s, "\n");
int alias_idx = of_alias_get_id(node, "gpio");
struct at91_gpio_chip *at91_gpio = &gpio_chip[alias_idx];
+ /* Setup proper .irq_set_type function */
+ if (has_pio3())
+ gpio_irqchip.irq_set_type = alt_gpio_irq_type;
+ else
+ gpio_irqchip.irq_set_type = gpio_irq_type;
+
/* Disable irqs of this PIO controller */
__raw_writel(~0, at91_gpio->regbase + PIO_IDR);
int gpio_irqnbr = 0;
struct at91_gpio_chip *this, *prev;
+ /* Setup proper .irq_set_type function */
+ if (has_pio3())
+ gpio_irqchip.irq_set_type = alt_gpio_irq_type;
+ else
+ gpio_irqchip.irq_set_type = gpio_irq_type;
+
for (pioc = 0, this = gpio_chip, prev = NULL;
pioc++ < gpio_banks;
prev = this, this++) {
at91_get_gpio_value(pin) ?
"set" : "clear");
else
- seq_printf(s, "[periph %s]\n",
- __raw_readl(pio + PIO_ABSR) &
- mask ? "B" : "A");
+ seq_printf(s, "[periph %c]\n",
+ peripheral_function(pio, mask));
}
}
}
goto ioremap_err;
}
+ /* Get capabilities from compatibility property */
+ if (of_device_is_compatible(np, "atmel,at91sam9x5-gpio"))
+ at91_gpio_caps |= AT91_GPIO_CAP_PIO3;
+
/* Setup clock */
if (at91_gpio_setup_clk(alias_idx))
goto ioremap_err;