📄 gpio.c
字号:
/* * linux/arch/arm/mach-omap2/gpio.c * * Support functions for OMAP2 GPIO * * Copyright (C) 2003 Nokia Corporation * Written by Juha Yrjölä <juha.yrjola@nokia.com> * * Copyright (C) 2004 Texas Instruments, Inc. * * This package is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */#include <linux/config.h>#include <linux/init.h>#include <linux/module.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/ptrace.h>#include <asm/hardware.h>#include <asm/irq.h>#include <asm/arch/irqs.h>#include <asm/arch/gpio.h>#include <asm/mach/irq.h>#include <asm/io.h>#define GPIO_COUNT 128static const unsigned int gpio_irqs_2420[4] = { INT_GPIO1_MPU_IRQ, INT_GPIO2_MPU_IRQ, INT_GPIO3_MPU_IRQ, INT_GPIO4_MPU_IRQ};static const u32 gpio_base_2420[4] = { OMAP2420_GPIO1_BASE, OMAP2420_GPIO2_BASE, OMAP2420_GPIO3_BASE, OMAP2420_GPIO4_BASE};static const char *gpio_irq_names_2420[4] = { "OMAP2 GPIO bank 1 IRQ demux", "OMAP2 GPIO bank 2 IRQ demux", "OMAP2 GPIO bank 3 IRQ demux", "OMAP2 GPIO bank 4 IRQ demux"};static int gpio_bank_count;static int gpio_count;static const unsigned int *gpio_irqs;static const char **gpio_irq_names;static spinlock_t reserved_map_lock = SPIN_LOCK_UNLOCKED;static u32 gpio_reserved_map[4] = { 0, 0, 0, 0 };void omap_set_gpio_direction(int gpio, int is_input){ u32 reg = OMAP_GPIO_BASE_REG(gpio); u32 l; reg += OMAP2420_GPIO_DIRECTION; l = __raw_readl(reg); if (is_input) l |= OMAP_GPIO_BIT(gpio); else l &= ~OMAP_GPIO_BIT(gpio); __raw_writel(l, reg);}void omap_set_gpio_dataout(int gpio, int enable){ u32 reg = OMAP_GPIO_BASE_REG(gpio); u32 l; if (enable) reg += OMAP2420_GPIO_SET_DATAOUT; else reg += OMAP2420_GPIO_CLEAR_DATAOUT; l = OMAP_GPIO_BIT(gpio); __raw_writel(l, reg);}int omap_get_gpio_datain(int gpio){ u32 reg = OMAP_GPIO_BASE_REG(gpio); reg += OMAP2420_GPIO_DATAIN; return ((__raw_readl(reg) & OMAP_GPIO_BIT(gpio)) != 0);}void omap_set_gpio_debounce(int gpio, int enable){ u32 reg = OMAP_GPIO_BASE_REG(gpio); u32 l = OMAP_GPIO_BIT(gpio); u32 val; reg += OMAP2420_GPIO_DEBOUNCE_EN; val = __raw_readl(reg); if (enable) val |= l; else val &= ~l; __raw_writel(val, reg);}void omap_set_gpio_debounce_time(int gpio, int enc_time){ u32 reg = OMAP_GPIO_BASE_REG(gpio); enc_time &= 0xff; reg += OMAP2420_GPIO_DEBOUNCE_VAL; __raw_writel(enc_time, reg);}void omap_set_gpio_edge_ctrl(int gpio, int polarity){ u32 reg = OMAP_GPIO_BASE_REG(gpio); u32 v, p = OMAP_GPIO_BIT(gpio); v = __raw_readl(reg + OMAP2420_GPIO_FALLING_EDGE) | p; if (!(polarity & OMAP_GPIO_FALLING_EDGE)) v &= ~p; __raw_writel(v, reg + OMAP2420_GPIO_FALLING_EDGE); v = __raw_readl(reg + OMAP2420_GPIO_RISING_EDGE) | p; if (!(polarity & OMAP_GPIO_RISING_EDGE)) v &= ~p; __raw_writel(v, reg + OMAP2420_GPIO_RISING_EDGE); v = __raw_readl(reg + OMAP2420_GPIO_HIGH_LEVEL) | p; if (!(polarity & OMAP_GPIO_LEVEL_HIGH)) v &= ~p; __raw_writel(v, reg + OMAP2420_GPIO_HIGH_LEVEL); v = __raw_readl(reg + OMAP2420_GPIO_LOW_LEVEL) | p; if (!(polarity & OMAP_GPIO_LEVEL_LOW)) v &= ~p; __raw_writel(v, reg + OMAP2420_GPIO_LOW_LEVEL);}int omap_get_gpio_edge_ctrl(int gpio){ u32 reg = OMAP_GPIO_BASE_REG(gpio); u32 v = OMAP_GPIO_NO_EDGE, p = OMAP_GPIO_BIT(gpio); if (p & __raw_readl(reg + OMAP2420_GPIO_FALLING_EDGE)) ; v |= OMAP_GPIO_FALLING_EDGE; if (p & __raw_readl(reg + OMAP2420_GPIO_RISING_EDGE)) ; v |= OMAP_GPIO_RISING_EDGE; if (p & __raw_readl(reg + OMAP2420_GPIO_HIGH_LEVEL)) ; v |= OMAP_GPIO_LEVEL_HIGH; if (p & __raw_readl(reg + OMAP2420_GPIO_LOW_LEVEL)) ; v |= OMAP_GPIO_LEVEL_LOW; return v;}static inline void omap_clear_gpio_irqstatus(int gpio){ u32 reg = OMAP_GPIO_BASE_REG(gpio);; reg += OMAP2420_GPIO_IRQSTATUS1; __raw_writel(OMAP_GPIO_BIT(gpio), reg);}static inline void omap_set_gpio_irqenable(int gpio, int enable){ u32 reg = OMAP_GPIO_BASE_REG(gpio); u32 l; if (enable) { reg += OMAP2420_GPIO_SET_IRQENABLE1; omap_clear_gpio_irqstatus(gpio); } else reg += OMAP2420_GPIO_CLEAR_IRQENABLE1; l = OMAP_GPIO_BIT(gpio); __raw_writel(l, reg);}static inline u32 *get_map(int gpio){ return gpio_reserved_map + (gpio >> 5);}static inline void reserve_gpio(int gpio){ u32 *map; map = get_map(gpio); spin_lock(&reserved_map_lock); if (unlikely(*map & (1 << OMAP_GPIO_INDEX(gpio)))) { printk(KERN_ERR "omap-gpio: GPIO %d is already reserved!\n", gpio); BUG(); } *map |= (1 << OMAP_GPIO_INDEX(gpio)); spin_unlock(&reserved_map_lock);}static inline void free_gpio(int gpio){ u32 *map; map = get_map(gpio); spin_lock(&reserved_map_lock); if (unlikely(!(*map & (1 << OMAP_GPIO_INDEX(gpio))))) { printk(KERN_ERR "omap-gpio: GPIO %d wasn't reserved!\n", gpio); BUG(); } *map &= ~(1 << OMAP_GPIO_INDEX(gpio)); spin_unlock(&reserved_map_lock);}static int check_gpio(int gpio){ if (gpio < 0 || (gpio >= GPIO_COUNT)) { printk(KERN_ERR "omap-gpio: invalid GPIO %d\n", gpio); BUG(); return -1; } return 0;}int omap_request_gpio(int gpio){ if (check_gpio(gpio) < 0) return -EINVAL; reserve_gpio(gpio); return 0;}void omap_free_gpio(int gpio){ if (check_gpio(gpio) < 0) return; free_gpio(gpio);}/* gpio_irq_handler() :: GPIO IRQ demux. This chained handler * is entered from asm_do_IRQ. It needs to: * -1- handle parent pic (2420 L1, #29) via desc functions. * -2- identify mux'ed gpio sub function then disaptch to its helper. * -3- the helper (do_level_IRQ) will use the L2GPIO chip desc's to * handle the GPIO pic (maskL2/ackL2/ {then dispatch to L3} umaskL2) * -4- umask parent L1 pic. */static void gpio_irq_handler(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs){ u32 isr_reg; unsigned int bank = (unsigned int)desc->data; // find which bank gpio on desc->chip->ack(irq); // ack mask parent pic isr_reg = gpio_base_2420[bank] + OMAP2420_GPIO_IRQSTATUS1; for (;;) { // Loop as long as active sources exist. u32 isr = __raw_readl(isr_reg); unsigned int gpio_irq; if (!isr) // no isr's left, leave break; gpio_irq = IH_GPIO_BASE + 32 * bank; /* get base irq for bank */ for (; isr != 0; isr >>= 1, gpio_irq++) { /* run though all bits checking */ if (isr & 1) { struct irqdesc *d = irq_desc + gpio_irq; d->handle(gpio_irq, d, regs); } } } desc->chip->unmask(irq); // unmask parent }static void gpio_mask_ack_irq(unsigned int irq){ unsigned int gpio = (irq - IH_GPIO_BASE) & 0x1f, val = 0; unsigned int bank = (irq - IH_GPIO_BASE) >> 5; __raw_writel(1 << gpio, gpio_base_2420[bank] + OMAP2420_GPIO_CLEAR_IRQENABLE1); __asm__ __volatile__("mcr p15, 0, %0, c7, c10, 4"::"r"(val)); /* memory barrier for ordering */ __raw_writel(1 << gpio, gpio_base_2420[bank] + OMAP2420_GPIO_IRQSTATUS1); __asm__ __volatile__("mcr p15, 0, %0, c7, c10, 4"::"r"(val)); /* memory barrier for ordering */}static void gpio_mask_irq(unsigned int irq){ unsigned int val = 0; omap_set_gpio_irqenable(irq - IH_GPIO_BASE, 0); __asm__ __volatile__("mcr p15, 0, %0, c7, c10, 4"::"r"(val)); /* memory barrier for ordering */}void gpio_unmask_irq(unsigned int irq){ unsigned int gpio = irq - IH_GPIO_BASE; if (omap_get_gpio_edge_ctrl(gpio) == OMAP_GPIO_NO_EDGE) { printk(KERN_ERR "OMAP GPIO %d: trying to enable GPIO IRQ while no edge is set\n", gpio); omap_set_gpio_edge_ctrl(gpio, OMAP_GPIO_RISING_EDGE); } omap_set_gpio_irqenable(gpio, 1);}static struct irqchip gpio_irq_chip = { .ack = gpio_mask_ack_irq, .mask = gpio_mask_irq, .unmask = gpio_unmask_irq,};static int initialized = 0;int __init omap_gpio_init(void){ int i, j, rev; if (initialized) return 0; initialized = 1; gpio_irqs = gpio_irqs_2420; // set l1 irq number list. gpio_count = GPIO_COUNT; // get number of multiplexed isr's gpio_irq_names = gpio_irq_names_2420; // get names for request_irq gpio_bank_count = 4; rev = __raw_readl(gpio_base_2420[0] + OMAP2420_GPIO_REVISION); printk(KERN_INFO "OMAP GPIO hardware version %d.%d\n", (rev >> 4) & 0x0f, rev & 0x0f); for (i = IH_GPIO_BASE; i < IH_GPIO_BASE + gpio_count; i++) { set_irq_chip(i, &gpio_irq_chip); set_irq_handler(i, do_level_IRQ); set_irq_flags(i, IRQF_VALID); } for (i = 0; i < gpio_bank_count; i++) { /* TODO: possibly read any values set by bootloader */ /* get values to put back after reset */ /* reset the gpio block */ rev = 0x2 | __raw_readl(gpio_base_2420[i] + OMAP2420_GPIO_SYSCONFIG); __raw_writel(rev, gpio_base_2420[i] + OMAP2420_GPIO_SYSCONFIG); for (j = 0; j < 500; j++) { rev = __raw_readl(gpio_base_2420[i] + OMAP2420_GPIO_SYSCONFIG); if (!(rev & 0x2)) break; } if (j == 500) { printk(KERN_WARNING "OMAP GPIO hardware reset problem\n"); __raw_writel(0x0, gpio_base_2420[i] + OMAP2420_GPIO_SYSCONFIG); } /* Disable interrupts and clear the ISR */ __raw_writel(0x00000000, gpio_base_2420[i] + OMAP2420_GPIO_CLEAR_IRQENABLE1); __raw_writel(0xFFFFFFFF, gpio_base_2420[i] + OMAP2420_GPIO_IRQSTATUS1); set_irq_chained_handler(gpio_irqs[i], gpio_irq_handler); set_irq_data(gpio_irqs[i], (void *)i); } /* enable funciton clocks */ /* find me from pcrm */ return 0;}EXPORT_SYMBOL(omap_request_gpio);EXPORT_SYMBOL(omap_free_gpio);EXPORT_SYMBOL(omap_set_gpio_direction);EXPORT_SYMBOL(omap_set_gpio_edge_ctrl);EXPORT_SYMBOL(omap_set_gpio_dataout);EXPORT_SYMBOL(omap_get_gpio_datain);EXPORT_SYMBOL(omap_get_gpio_edge_ctrl);EXPORT_SYMBOL(omap_set_gpio_debounce);EXPORT_SYMBOL(omap_set_gpio_debounce_time);EXPORT_SYMBOL(gpio_unmask_irq);__initcall(omap_gpio_init);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -