Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
2
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
cf2e933
Documentation
arch
alpha
arc
arm
boot
common
configs
crypto
firmware
include
kernel
kvm
lib
mach-at91
include
Kconfig
Kconfig.non_dt
Makefile
Makefile.boot
at91_aic.h
at91_rstc.h
at91_shdwc.h
at91_tc.h
at91rm9200.c
at91rm9200_devices.c
at91rm9200_time.c
at91sam9260.c
at91sam9260_devices.c
at91sam9261.c
at91sam9261_devices.c
at91sam9263.c
at91sam9263_devices.c
at91sam926x_time.c
at91sam9_alt_reset.S
at91sam9g45.c
at91sam9g45_devices.c
at91sam9g45_reset.S
at91sam9n12.c
at91sam9rl.c
at91sam9rl_devices.c
at91sam9x5.c
at91x40.c
at91x40_time.c
board-1arm.c
board-afeb-9260v1.c
board-cam60.c
board-carmeva.c
board-cpu9krea.c
board-cpuat91.c
board-csb337.c
board-csb637.c
board-dt-rm9200.c
board-dt-sam9.c
board-dt-sama5.c
board-eb01.c
board-eb9200.c
board-ecbat91.c
board-eco920.c
board-flexibity.c
board-foxg20.c
board-gsia18s.c
board-kafa.c
board-kb9202.c
board-pcontrol-g20.c
board-picotux200.c
board-rm9200ek.c
board-rsi-ews.c
board-sam9-l9260.c
board-sam9260ek.c
board-sam9261ek.c
board-sam9263ek.c
board-sam9g20ek.c
board-sam9m10g45ek.c
board-sam9rlek.c
board-snapper9260.c
board-stamp9g20.c
board-yl-9200.c
board.h
clock.c
clock.h
generic.h
gpio.c
gpio.h
gsia18s.h
irq.c
leds.c
pm.c
pm.h
pm_slowclock.S
sam9_smc.c
sam9_smc.h
sama5d3.c
setup.c
soc.h
stamp9g20.h
sysirq_mask.c
mach-bcm
mach-berlin
mach-clps711x
mach-cns3xxx
mach-davinci
mach-dove
mach-ebsa110
mach-efm32
mach-ep93xx
mach-exynos
mach-footbridge
mach-gemini
mach-highbank
mach-hisi
mach-imx
mach-integrator
mach-iop13xx
mach-iop32x
mach-iop33x
mach-ixp4xx
mach-keystone
mach-kirkwood
mach-ks8695
mach-lpc32xx
mach-mmp
mach-moxart
mach-msm
mach-mv78xx0
mach-mvebu
mach-mxs
mach-netx
mach-nomadik
mach-nspire
mach-omap1
mach-omap2
mach-orion5x
mach-picoxcell
mach-prima2
mach-pxa
mach-qcom
mach-realview
mach-rockchip
mach-rpc
mach-s3c24xx
mach-s3c64xx
mach-s5p64x0
mach-s5pc100
mach-s5pv210
mach-sa1100
mach-shmobile
mach-socfpga
mach-spear
mach-sti
mach-sunxi
mach-tegra
mach-u300
mach-ux500
mach-versatile
mach-vexpress
mach-vt8500
mach-w90x900
mach-zynq
mm
net
nwfpe
oprofile
plat-iop
plat-omap
plat-orion
plat-pxa
plat-samsung
plat-versatile
tools
vfp
xen
Kconfig
Kconfig-nommu
Kconfig.debug
Makefile
arm64
avr32
blackfin
c6x
cris
frv
hexagon
ia64
m32r
m68k
metag
microblaze
mips
mn10300
openrisc
parisc
powerpc
s390
score
sh
sparc
tile
um
unicore32
x86
xtensa
.gitignore
Kconfig
block
crypto
drivers
firmware
fs
include
init
ipc
kernel
lib
mm
net
samples
scripts
security
sound
tools
usr
virt
.gitignore
.mailmap
COPYING
CREDITS
Kbuild
Kconfig
MAINTAINERS
Makefile
README
REPORTING-BUGS
Breadcrumbs
linux
/
arch
/
arm
/
mach-at91
/
gpio.c
Copy path
Blame
Blame
Latest commit
Linus Walleij
and
Nicolas Ferre
ARM: at91: localize GPIO header
May 7, 2014
cf2e933
·
May 7, 2014
History
History
969 lines (803 loc) · 24.3 KB
Breadcrumbs
linux
/
arch
/
arm
/
mach-at91
/
gpio.c
Top
File metadata and controls
Code
Blame
969 lines (803 loc) · 24.3 KB
Raw
/* * linux/arch/arm/mach-at91/gpio.c * * Copyright (C) 2005 HP Labs * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include <linux/clk.h> #include <linux/errno.h> #include <linux/device.h> #include <linux/gpio.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/debugfs.h> #include <linux/seq_file.h> #include <linux/kernel.h> #include <linux/list.h> #include <linux/module.h> #include <linux/io.h> #include <linux/irqdomain.h> #include <linux/irqchip/chained_irq.h> #include <linux/of_address.h> #include <mach/hardware.h> #include <mach/at91_pio.h> #include "generic.h" #include "gpio.h" #define MAX_NB_GPIO_PER_BANK 32 struct at91_gpio_chip { struct gpio_chip chip; struct at91_gpio_chip *next; /* Bank sharing same clock */ int pioc_hwirq; /* PIO bank interrupt identifier on AIC */ int pioc_virq; /* PIO bank Linux virtual interrupt */ int pioc_idx; /* PIO bank index */ void __iomem *regbase; /* PIO bank virtual address */ struct clk *clock; /* associated clock */ struct irq_domain *domain; /* associated irq domain */ }; #define to_at91_gpio_chip(c) container_of(c, struct at91_gpio_chip, chip) static int at91_gpiolib_request(struct gpio_chip *chip, unsigned offset); static void at91_gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip); static void at91_gpiolib_set(struct gpio_chip *chip, unsigned offset, int val); static int at91_gpiolib_get(struct gpio_chip *chip, unsigned offset); static int at91_gpiolib_direction_output(struct gpio_chip *chip, unsigned offset, int val); static int at91_gpiolib_direction_input(struct gpio_chip *chip, unsigned offset); static int at91_gpiolib_to_irq(struct gpio_chip *chip, unsigned offset); #define AT91_GPIO_CHIP(name) \ { \ .chip = { \ .label = name, \ .request = at91_gpiolib_request, \ .direction_input = at91_gpiolib_direction_input, \ .direction_output = at91_gpiolib_direction_output, \ .get = at91_gpiolib_get, \ .set = at91_gpiolib_set, \ .dbg_show = at91_gpiolib_dbg_show, \ .to_irq = at91_gpiolib_to_irq, \ .ngpio = MAX_NB_GPIO_PER_BANK, \ }, \ } static struct at91_gpio_chip gpio_chip[] = { AT91_GPIO_CHIP("pioA"), AT91_GPIO_CHIP("pioB"), AT91_GPIO_CHIP("pioC"), AT91_GPIO_CHIP("pioD"), AT91_GPIO_CHIP("pioE"), }; 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) { pin /= MAX_NB_GPIO_PER_BANK; if (likely(pin < gpio_banks)) return gpio_chip[pin].regbase; return NULL; } static inline unsigned pin_to_mask(unsigned pin) { return 1 << (pin % MAX_NB_GPIO_PER_BANK); } 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 * only encapsulate the most common features and modes. (So if you * want to change signals in groups, do it directly.) * * Bootloaders will usually handle some of the pin multiplexing setup. * The intent is certainly that by the time Linux is fully booted, all * pins should have been fully initialized. These setup calls should * only be used by board setup routines, or possibly in driver probe(). * * For bootloaders doing all that setup, these calls could be inlined * as NOPs so Linux won't duplicate any setup code */ /* * mux the pin to the "GPIO" peripheral role. */ int __init_or_module at91_set_GPIO_periph(unsigned pin, int use_pullup) { void __iomem *pio = pin_to_controller(pin); unsigned mask = pin_to_mask(pin); if (!pio) return -EINVAL; __raw_writel(mask, pio + PIO_IDR); __raw_writel(mask, pio + (use_pullup ? PIO_PUER : PIO_PUDR)); __raw_writel(mask, pio + PIO_PER); return 0; } EXPORT_SYMBOL(at91_set_GPIO_periph); /* * mux the pin to the "A" internal peripheral role. */ int __init_or_module at91_set_A_periph(unsigned pin, int use_pullup) { void __iomem *pio = pin_to_controller(pin); unsigned mask = pin_to_mask(pin); if (!pio) return -EINVAL; __raw_writel(mask, pio + PIO_IDR); __raw_writel(mask, pio + (use_pullup ? PIO_PUER : PIO_PUDR)); 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; } EXPORT_SYMBOL(at91_set_A_periph); /* * mux the pin to the "B" internal peripheral role. */ int __init_or_module at91_set_B_periph(unsigned pin, int use_pullup) { void __iomem *pio = pin_to_controller(pin); unsigned mask = pin_to_mask(pin); if (!pio) return -EINVAL; __raw_writel(mask, pio + PIO_IDR); __raw_writel(mask, pio + (use_pullup ? PIO_PUER : PIO_PUDR)); 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; } EXPORT_SYMBOL(at91_set_B_periph); /* * 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) { void __iomem *pio = pin_to_controller(pin); unsigned mask = pin_to_mask(pin); if (!pio) return -EINVAL; __raw_writel(mask, pio + PIO_IDR); __raw_writel(mask, pio + (use_pullup ? PIO_PUER : PIO_PUDR)); __raw_writel(mask, pio + PIO_ODR); __raw_writel(mask, pio + PIO_PER); return 0; } EXPORT_SYMBOL(at91_set_gpio_input); /* * 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) { void __iomem *pio = pin_to_controller(pin); unsigned mask = pin_to_mask(pin); if (!pio) return -EINVAL; __raw_writel(mask, pio + PIO_IDR); __raw_writel(mask, pio + PIO_PUDR); __raw_writel(mask, pio + (value ? PIO_SODR : PIO_CODR)); __raw_writel(mask, pio + PIO_OER); __raw_writel(mask, pio + PIO_PER); return 0; } EXPORT_SYMBOL(at91_set_gpio_output); /* * enable/disable the glitch filter; mostly used with IRQ handling. */ int __init_or_module at91_set_deglitch(unsigned pin, int is_on) { void __iomem *pio = pin_to_controller(pin); unsigned mask = pin_to_mask(pin); 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. */ int __init_or_module at91_set_multi_drive(unsigned pin, int is_on) { void __iomem *pio = pin_to_controller(pin); unsigned mask = pin_to_mask(pin); if (!pio) return -EINVAL; __raw_writel(mask, pio + (is_on ? PIO_MDER : PIO_MDDR)); return 0; } 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. */ int at91_set_gpio_value(unsigned pin, int value) { void __iomem *pio = pin_to_controller(pin); unsigned mask = pin_to_mask(pin); if (!pio) return -EINVAL; __raw_writel(mask, pio + (value ? PIO_SODR : PIO_CODR)); return 0; } EXPORT_SYMBOL(at91_set_gpio_value); /* * read the pin's value (works even if it's not muxed as a gpio). */ int at91_get_gpio_value(unsigned pin) { void __iomem *pio = pin_to_controller(pin); unsigned mask = pin_to_mask(pin); u32 pdsr; if (!pio) return -EINVAL; pdsr = __raw_readl(pio + PIO_PDSR); return (pdsr & mask) != 0; } EXPORT_SYMBOL(at91_get_gpio_value); /*--------------------------------------------------------------------------*/ #ifdef CONFIG_PM static u32 wakeups[MAX_GPIO_BANKS]; static u32 backups[MAX_GPIO_BANKS]; static int gpio_irq_set_wake(struct irq_data *d, unsigned state) { struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d); unsigned mask = 1 << d->hwirq; unsigned bank = at91_gpio->pioc_idx; if (unlikely(bank >= MAX_GPIO_BANKS)) return -EINVAL; if (state) wakeups[bank] |= mask; else wakeups[bank] &= ~mask; irq_set_irq_wake(at91_gpio->pioc_virq, state); return 0; } void at91_gpio_suspend(void) { int i; for (i = 0; i < gpio_banks; i++) { void __iomem *pio = gpio_chip[i].regbase; backups[i] = __raw_readl(pio + PIO_IMR); __raw_writel(backups[i], pio + PIO_IDR); __raw_writel(wakeups[i], pio + PIO_IER); if (!wakeups[i]) { clk_unprepare(gpio_chip[i].clock); clk_disable(gpio_chip[i].clock); } else { #ifdef CONFIG_PM_DEBUG printk(KERN_DEBUG "GPIO-%c may wake for %08x\n", 'A'+i, wakeups[i]); #endif } } } void at91_gpio_resume(void) { int i; for (i = 0; i < gpio_banks; i++) { void __iomem *pio = gpio_chip[i].regbase; if (!wakeups[i]) { if (clk_prepare(gpio_chip[i].clock) == 0) clk_enable(gpio_chip[i].clock); } __raw_writel(wakeups[i], pio + PIO_IDR); __raw_writel(backups[i], pio + PIO_IER); } } #else #define gpio_irq_set_wake NULL #endif /* Several AIC controller irqs are dispatched through this GPIO handler. * 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. * 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(). * IRQ0..IRQ6 should be configurable, e.g. level vs edge triggering. */ static void gpio_irq_mask(struct irq_data *d) { struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d); void __iomem *pio = at91_gpio->regbase; unsigned mask = 1 << d->hwirq; if (pio) __raw_writel(mask, pio + PIO_IDR); } static void gpio_irq_unmask(struct irq_data *d) { struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d); void __iomem *pio = at91_gpio->regbase; unsigned mask = 1 << d->hwirq; if (pio) __raw_writel(mask, pio + PIO_IER); } static int gpio_irq_type(struct irq_data *d, unsigned type) { switch (type) { case IRQ_TYPE_NONE: case IRQ_TYPE_EDGE_BOTH: return 0; default: return -EINVAL; } } /* 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 is set dynamically */ .irq_set_wake = gpio_irq_set_wake, }; static void gpio_irq_handler(unsigned irq, struct irq_desc *desc) { struct irq_chip *chip = irq_desc_get_chip(desc); struct irq_data *idata = irq_desc_get_irq_data(desc); struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(idata); void __iomem *pio = at91_gpio->regbase; unsigned long isr; int n; chained_irq_enter(chip, desc); for (;;) { /* Reading ISR acks pending (edge triggered) GPIO interrupts. * When there none are pending, we're finished unless we need * to process multiple banks (like ID_PIOCDE on sam9263). */ isr = __raw_readl(pio + PIO_ISR) & __raw_readl(pio + PIO_IMR); if (!isr) { if (!at91_gpio->next) break; at91_gpio = at91_gpio->next; pio = at91_gpio->regbase; continue; } n = find_first_bit(&isr, BITS_PER_LONG); while (n < BITS_PER_LONG) { generic_handle_irq(irq_find_mapping(at91_gpio->domain, n)); n = find_next_bit(&isr, BITS_PER_LONG, n + 1); } } chained_irq_exit(chip, desc); /* now it may re-trigger */ } /*--------------------------------------------------------------------------*/ #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\t", 'A' + bank); }; seq_printf(s, "\n\n"); /* print pin status */ for (j = 0; j < 32; j++) { seq_printf(s, "%i:\t", j); for (bank = 0; bank < gpio_banks; bank++) { unsigned pin = (32 * bank) + j; void __iomem *pio = pin_to_controller(pin); unsigned mask = pin_to_mask(pin); if (__raw_readl(pio + PIO_PSR) & mask) gpio_printf(s, pio, mask); else seq_printf(s, "%c\t\t", peripheral_function(pio, mask)); } seq_printf(s, "\n"); } return 0; } static int at91_gpio_open(struct inode *inode, struct file *file) { return single_open(file, at91_gpio_show, NULL); } static const struct file_operations at91_gpio_operations = { .open = at91_gpio_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static int __init at91_gpio_debugfs_init(void) { /* /sys/kernel/debug/at91_gpio */ (void) debugfs_create_file("at91_gpio", S_IFREG | S_IRUGO, NULL, NULL, &at91_gpio_operations); return 0; } postcore_initcall(at91_gpio_debugfs_init); #endif /*--------------------------------------------------------------------------*/ /* * This lock class tells lockdep that GPIO irqs are in a different * category than their parents, so it won't report false recursion. */ static struct lock_class_key gpio_lock_class; /* * irqdomain initialization: pile up irqdomains on top of AIC range */ static void __init at91_gpio_irqdomain(struct at91_gpio_chip *at91_gpio) { int irq_base; irq_base = irq_alloc_descs(-1, 0, at91_gpio->chip.ngpio, 0); if (irq_base < 0) panic("at91_gpio.%d: error %d: couldn't allocate IRQ numbers.\n", at91_gpio->pioc_idx, irq_base); at91_gpio->domain = irq_domain_add_legacy(NULL, at91_gpio->chip.ngpio, irq_base, 0, &irq_domain_simple_ops, NULL); if (!at91_gpio->domain) panic("at91_gpio.%d: couldn't allocate irq domain.\n", at91_gpio->pioc_idx); } /* * Called from the processor-specific init to enable GPIO interrupt support. */ void __init at91_gpio_irq_setup(void) { unsigned pioc; 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++) { int offset; __raw_writel(~0, this->regbase + PIO_IDR); /* setup irq domain for this GPIO controller */ at91_gpio_irqdomain(this); for (offset = 0; offset < this->chip.ngpio; offset++) { unsigned int virq = irq_find_mapping(this->domain, offset); irq_set_lockdep_class(virq, &gpio_lock_class); /* * Can use the "simple" and not "edge" handler since it's * shorter, and the AIC handles interrupts sanely. */ irq_set_chip_and_handler(virq, &gpio_irqchip, handle_simple_irq); set_irq_flags(virq, IRQF_VALID); irq_set_chip_data(virq, this); gpio_irqnbr++; } /* The toplevel handler handles one bank of GPIOs, except * on some SoC it can handles up to three... * We only set up the handler for the first of the list. */ if (prev && prev->next == this) continue; this->pioc_virq = irq_create_mapping(NULL, this->pioc_hwirq); irq_set_chip_data(this->pioc_virq, this); irq_set_chained_handler(this->pioc_virq, gpio_irq_handler); } pr_info("AT91: %d gpio irqs in %d banks\n", gpio_irqnbr, gpio_banks); } /* gpiolib support */ static int at91_gpiolib_request(struct gpio_chip *chip, unsigned offset) { struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); void __iomem *pio = at91_gpio->regbase; unsigned mask = 1 << offset; __raw_writel(mask, pio + PIO_PER); return 0; } static int at91_gpiolib_direction_input(struct gpio_chip *chip, unsigned offset) { struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); void __iomem *pio = at91_gpio->regbase; unsigned mask = 1 << offset; __raw_writel(mask, pio + PIO_ODR); return 0; } static int at91_gpiolib_direction_output(struct gpio_chip *chip, unsigned offset, int val) { struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); void __iomem *pio = at91_gpio->regbase; unsigned mask = 1 << offset; __raw_writel(mask, pio + (val ? PIO_SODR : PIO_CODR)); __raw_writel(mask, pio + PIO_OER); return 0; } static int at91_gpiolib_get(struct gpio_chip *chip, unsigned offset) { struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); void __iomem *pio = at91_gpio->regbase; unsigned mask = 1 << offset; u32 pdsr; pdsr = __raw_readl(pio + PIO_PDSR); return (pdsr & mask) != 0; } static void at91_gpiolib_set(struct gpio_chip *chip, unsigned offset, int val) { struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); void __iomem *pio = at91_gpio->regbase; unsigned mask = 1 << offset; __raw_writel(mask, pio + (val ? PIO_SODR : PIO_CODR)); } static void at91_gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) { int i; for (i = 0; i < chip->ngpio; i++) { unsigned pin = chip->base + i; void __iomem *pio = pin_to_controller(pin); unsigned mask = pin_to_mask(pin); const char *gpio_label; gpio_label = gpiochip_is_requested(chip, i); if (gpio_label) { seq_printf(s, "[%s] GPIO%s%d: ", gpio_label, chip->label, i); if (__raw_readl(pio + PIO_PSR) & mask) seq_printf(s, "[gpio] %s\n", at91_get_gpio_value(pin) ? "set" : "clear"); else seq_printf(s, "[periph %c]\n", peripheral_function(pio, mask)); } } } static int at91_gpiolib_to_irq(struct gpio_chip *chip, unsigned offset) { struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); int virq; if (offset < chip->ngpio) virq = irq_create_mapping(at91_gpio->domain, offset); else virq = -ENXIO; dev_dbg(chip->dev, "%s: request IRQ for GPIO %d, return %d\n", chip->label, offset + chip->base, virq); return virq; } static int __init at91_gpio_setup_clk(int idx) { struct at91_gpio_chip *at91_gpio = &gpio_chip[idx]; /* retreive PIO controller's clock */ at91_gpio->clock = clk_get_sys(NULL, at91_gpio->chip.label); if (IS_ERR(at91_gpio->clock)) { pr_err("at91_gpio.%d, failed to get clock, ignoring.\n", idx); goto err; } if (clk_prepare(at91_gpio->clock)) goto clk_prep_err; /* enable PIO controller's clock */ if (clk_enable(at91_gpio->clock)) { pr_err("at91_gpio.%d, failed to enable clock, ignoring.\n", idx); goto clk_err; } return 0; clk_err: clk_unprepare(at91_gpio->clock); clk_prep_err: clk_put(at91_gpio->clock); err: return -EINVAL; } static void __init at91_gpio_init_one(int idx, u32 regbase, int pioc_hwirq) { struct at91_gpio_chip *at91_gpio = &gpio_chip[idx]; at91_gpio->chip.base = idx * MAX_NB_GPIO_PER_BANK; at91_gpio->pioc_hwirq = pioc_hwirq; at91_gpio->pioc_idx = idx; at91_gpio->regbase = ioremap(regbase, 512); if (!at91_gpio->regbase) { pr_err("at91_gpio.%d, failed to map registers, ignoring.\n", idx); return; } if (at91_gpio_setup_clk(idx)) goto ioremap_err; gpio_banks = max(gpio_banks, idx + 1); return; ioremap_err: iounmap(at91_gpio->regbase); } /* * Called from the processor-specific init to enable GPIO pin support. */ void __init at91_gpio_init(struct at91_gpio_bank *data, int nr_banks) { unsigned i; struct at91_gpio_chip *at91_gpio, *last = NULL; BUG_ON(nr_banks > MAX_GPIO_BANKS); if (of_have_populated_dt()) return; for (i = 0; i < nr_banks; i++) at91_gpio_init_one(i, data[i].regbase, data[i].id); for (i = 0; i < gpio_banks; i++) { at91_gpio = &gpio_chip[i]; /* * GPIO controller are grouped on some SoC: * PIOC, PIOD and PIOE can share the same IRQ line */ if (last && last->pioc_hwirq == at91_gpio->pioc_hwirq) last->next = at91_gpio; last = at91_gpio; gpiochip_add(&at91_gpio->chip); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
You can’t perform that action at this time.