| /* |
| * |
| * (c) 2008 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> |
| * |
| * See file CREDITS for list of people who contributed to this |
| * project. |
| * |
| * 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. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
| * MA 02111-1307 USA |
| */ |
| |
| #include <common.h> |
| #include <linux/clk.h> |
| #include <errno.h> |
| #include <asm/io.h> |
| #include <mach/gpio.h> |
| #include <gpio.h> |
| |
| static int gpio_banks; |
| static struct at91_gpio_bank *gpio; |
| |
| static inline void __iomem *pin_to_controller(unsigned pin) |
| { |
| pin -= PIN_BASE; |
| pin /= 32; |
| if (likely(pin < gpio_banks)) |
| return gpio[pin].regbase; |
| |
| return NULL; |
| } |
| |
| static inline unsigned pin_to_mask(unsigned pin) |
| { |
| pin -= PIN_BASE; |
| return 1 << (pin % 32); |
| } |
| |
| /* |
| * mux the pin to the "GPIO" peripheral role. |
| */ |
| int 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 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)); |
| __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 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)); |
| __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 gpio controller (instead of "A" or "B" peripheral), and |
| * configure it for an input. |
| */ |
| int 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" or "B" peripheral), |
| * and configure it for an output. |
| */ |
| int 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 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; |
| __raw_writel(mask, pio + (is_on ? PIO_IFER : PIO_IFDR)); |
| return 0; |
| } |
| EXPORT_SYMBOL(at91_set_deglitch); |
| |
| /* |
| * enable/disable the multi-driver; This is only valid for output and |
| * allows the output pin to run as an open collector output. |
| */ |
| int 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); |
| |
| /* |
| * 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); |
| |
| int gpio_direction_input(unsigned pin) |
| { |
| void __iomem *pio = pin_to_controller(pin); |
| unsigned mask = pin_to_mask(pin); |
| |
| if (!pio || !(__raw_readl(pio + PIO_PSR) & mask)) |
| return -EINVAL; |
| __raw_writel(mask, pio + PIO_ODR); |
| return 0; |
| } |
| EXPORT_SYMBOL(gpio_direction_input); |
| |
| int gpio_direction_output(unsigned pin, int value) |
| { |
| void __iomem *pio = pin_to_controller(pin); |
| unsigned mask = pin_to_mask(pin); |
| |
| if (!pio || !(__raw_readl(pio + PIO_PSR) & mask)) |
| return -EINVAL; |
| __raw_writel(mask, pio + (value ? PIO_SODR : PIO_CODR)); |
| __raw_writel(mask, pio + PIO_OER); |
| return 0; |
| } |
| EXPORT_SYMBOL(gpio_direction_output); |
| |
| /*--------------------------------------------------------------------------*/ |
| |
| int at91_gpio_init(struct at91_gpio_bank *data, int nr_banks) |
| { |
| unsigned i; |
| struct at91_gpio_bank *last; |
| |
| gpio = data; |
| gpio_banks = nr_banks; |
| |
| for (i = 0, last = NULL; i < nr_banks; i++, last = data, data++) { |
| data->chipbase = PIN_BASE + i * 32; |
| data->regbase = data->offset + |
| (void __iomem *)AT91_BASE_SYS; |
| |
| /* enable PIO controller's clock */ |
| clk_enable(data->clock); |
| |
| /* AT91SAM9263_ID_PIOCDE groups PIOC, PIOD, PIOE */ |
| if (last && last->id == data->id) |
| last->next = data; |
| } |
| |
| return 0; |
| } |