/* * File : drv_gpio.c * This file is part of RT-Thread RTOS * COPYRIGHT (C) 2008 - 2016, RT-Thread Development Team * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Change Logs: * Date Author Notes * 2015-11-19 Urey the first version */ #include #include #include #include "board.h" #include "drv_gpio.h" #define GPIO_DEBUG 0 #if GPIO_DEBUG #define GPIO_DBG(...) rt_kprintf(__VA_ARGS__) #else #define GPIO_DBG(...) #endif struct jz_gpio_irq_def _g_gpio_irq_tbl[GPIO_NR_PORTS] = {0}; rt_inline int _fls(int x) { __asm__("clz %0, %1" : "=r" (x) : "r" (x)); return 32 - x; } void gpio_set_func(enum gpio_port port, uint32_t pins, enum gpio_function func) { RT_ASSERT(IS_GPIO_ALL_PORT(port)); writel(func & 0x8 ? pins : 0, GPIO_PXINTS(port)); writel(func & 0x4 ? pins : 0, GPIO_PXMSKS(port)); writel(func & 0x2 ? pins : 0, GPIO_PXPAT1S(port)); writel(func & 0x1 ? pins : 0, GPIO_PXPAT0S(port)); writel(func & 0x8 ? 0 : pins, GPIO_PXINTC(port)); writel(func & 0x4 ? 0 : pins, GPIO_PXMSKC(port)); writel(func & 0x2 ? 0 : pins, GPIO_PXPAT1C(port)); writel(func & 0x1 ? 0 : pins, GPIO_PXPAT0C(port)); writel(func & 0x10 ? pins : 0, GPIO_PXPENC(port)); writel(func & 0x10 ? 0 : pins, GPIO_PXPENS(port)); } void gpio_set_value(enum gpio_port port,enum gpio_pin pin,int value) { RT_ASSERT(IS_GPIO_ALL_PORT(port)); if (value) writel(pin, GPIO_PXPAT0S(port)); else writel(pin, GPIO_PXPAT0C(port)); } void gpio_enable_pull(enum gpio_port port, enum gpio_pin pin) { RT_ASSERT(IS_GPIO_ALL_PORT(port)); writel(pin, GPIO_PXPENC(port)); } void gpio_disable_pull(enum gpio_port port, enum gpio_pin pin) { RT_ASSERT(IS_GPIO_ALL_PORT(port)); writel(pin, GPIO_PXPENS(port)); } void gpio_ctrl_pull(enum gpio_port port, uint32_t pins,int enable) { RT_ASSERT(IS_GPIO_ALL_PORT(port)); if (enable) writel(pins, GPIO_PXPENC(port)); else writel(pins, GPIO_PXPENS(port)); } int gpio_get_value(enum gpio_port port, enum gpio_pin pin) { RT_ASSERT(IS_GPIO_ALL_PORT(port)); return !!(readl(GPIO_PXPIN(port)) & pin); } int gpio_get_flag(enum gpio_port port, enum gpio_pin pin) { RT_ASSERT(IS_GPIO_ALL_PORT(port)); return (readl(GPIO_PXFLG(port)) & pin); } void gpio_clear_flag(enum gpio_port port, enum gpio_pin pin) { RT_ASSERT(IS_GPIO_ALL_PORT(port)); writel(pin, GPIO_PXFLGC(port)); } void gpio_direction_input(enum gpio_port port, enum gpio_pin pin) { RT_ASSERT(IS_GPIO_ALL_PORT(port)); gpio_set_func(port,pin,GPIO_INPUT); } void gpio_direction_output(enum gpio_port port, enum gpio_pin pin,int value) { RT_ASSERT(IS_GPIO_ALL_PORT(port)); gpio_set_func(port, pin, value ? GPIO_OUTPUT1 : GPIO_OUTPUT0); } /********************************************************************************************************* ** IRQ *********************************************************************************************************/ void gpio_unmask_irq(enum gpio_port port, enum gpio_pin pin) { RT_ASSERT(IS_GPIO_ALL_PORT(port)); writel(pin, GPIO_PXMSKC(port)); } void gpio_mask_irq(enum gpio_port port, enum gpio_pin pin) { RT_ASSERT(IS_GPIO_ALL_PORT(port)); writel(BIT(pin), GPIO_PXMSKS(port)); } int gpio_set_irq_type(enum gpio_port port, enum gpio_pin pin, enum gpio_irq_type irq_type) { enum gpio_function func; RT_ASSERT(IS_GPIO_ALL_PORT(port)); if (irq_type & IRQ_TYPE_PROBE) return 0; switch (irq_type & IRQ_TYPE_SENSE_MASK) { case IRQ_TYPE_LEVEL_HIGH: func = GPIO_INT_HI; break; case IRQ_TYPE_LEVEL_LOW: func = GPIO_INT_LO; break; case IRQ_TYPE_EDGE_RISING: func = GPIO_INT_RE; break; case IRQ_TYPE_EDGE_FALLING: func = GPIO_INT_FE; break; case IRQ_TYPE_EDGE_BOTH: if (gpio_get_value(port, pin)) func = GPIO_INT_FE; else func = GPIO_INT_RE; break; default: return -1; } gpio_set_func(port,pin, func); return 0; } void gpio_ack_irq(enum gpio_port port, enum gpio_pin pin) { RT_ASSERT(IS_GPIO_ALL_PORT(port)); writel(pin, GPIO_PXFLGC(port)); } void gpio_set_irq_callback(enum gpio_port port, enum gpio_pin pin, void (*irq_cb)(void *),void *irq_arg) { uint32_t pin_id; RT_ASSERT(IS_GPIO_ALL_PORT(port)); pin_id = _fls(pin) - 1; GPIO_DBG("port = %d,pin = %d \n",port,pin_id); _g_gpio_irq_tbl[port].irq_cb[pin_id] = irq_cb; _g_gpio_irq_tbl[port].irq_arg[pin_id] = irq_arg; GPIO_DBG("set irq callback end... \n"); } void gpio_irq_handler(int irq, void *param) { struct jz_gpio_irq_def *irq_def = (struct jz_gpio_irq_def *)param; uint32_t pend,mask; uint32_t pin_id; enum gpio_port port = (IRQ_GPIO0 - irq); enum gpio_pin pin; RT_ASSERT(param != RT_NULL); GPIO_DBG("GPIO irq handler,irq=%d\n",irq); pend = readl(GPIO_PXFLG(port)); mask = readl(GPIO_PXMSK(port)); GPIO_DBG("port =%d pend =%08x mask =%08x\n",port,pend,mask); pend = pend & ~mask; while(pend) { pin_id = _fls(pend) - 1; pin = 0x01 << pin_id; GPIO_DBG("PORT%d PIN%d interrupt happened..\n",port,pin_id); if(irq_def->irq_cb[pin_id] != RT_NULL) { GPIO_DBG("do irq callback...\n",port,pin); irq_def->irq_cb[pin_id](irq_def->irq_arg[pin_id]); } pend &= ~(0x01 << pin_id); gpio_ack_irq(port, pin); } } int rt_hw_gpio_init(void) { GPIO_DBG("Install gpio interrupt source...\n"); /* install ISR */ rt_hw_interrupt_install(IRQ_GPIO0,gpio_irq_handler,&_g_gpio_irq_tbl[GPIO_PORT_A],"GPIOAINT"); rt_hw_interrupt_umask(IRQ_GPIO0); rt_hw_interrupt_install(IRQ_GPIO1,gpio_irq_handler,&_g_gpio_irq_tbl[GPIO_PORT_B],"GPIOBINT"); rt_hw_interrupt_umask(IRQ_GPIO1); rt_hw_interrupt_install(IRQ_GPIO2,gpio_irq_handler,&_g_gpio_irq_tbl[GPIO_PORT_C],"GPIOCINT"); rt_hw_interrupt_umask(IRQ_GPIO2); rt_hw_interrupt_install(IRQ_GPIO3,gpio_irq_handler,&_g_gpio_irq_tbl[GPIO_PORT_D],"GPIODINT"); rt_hw_interrupt_umask(IRQ_GPIO3); return 0; } INIT_BOARD_EXPORT(rt_hw_gpio_init);