320 lines
8.4 KiB
C
320 lines
8.4 KiB
C
/*
|
|
* Copyright (c) 2006-2021, RT-Thread Development Team
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
* 2019-07-29 zdzn first version
|
|
*/
|
|
|
|
#include "drv_gpio.h"
|
|
|
|
#ifdef BSP_USING_PIN
|
|
/*
|
|
* gpio_int[0] for BANK0 (pins 0-27)
|
|
* gpio_int[1] for BANK1 (pins 28-45)
|
|
* gpio_int[2] for BANK2 (pins 46-53)
|
|
*/
|
|
static struct gpio_irq_def _g_gpio_irq_tbl[GPIO_IRQ_NUM];
|
|
|
|
void gpio_set_pud(rt_uint8_t pin, rt_uint8_t pud)
|
|
{
|
|
rt_uint8_t num = pin / 32;
|
|
rt_uint8_t shift = pin % 32;
|
|
BCM283X_GPIO_GPPUD = pud;
|
|
DELAY_MICROS(10);
|
|
BCM283X_GPIO_GPPUDCLK(num) = 1 << shift;
|
|
DELAY_MICROS(10);
|
|
BCM283X_GPIO_GPPUD = BCM283X_GPIO_PUD_OFF;
|
|
BCM283X_GPIO_GPPUDCLK(num) = 0 << shift;
|
|
}
|
|
|
|
static void gpio_ack_irq(int irq, bcm_gpio_pin pin)
|
|
{
|
|
rt_uint32_t data;
|
|
data = IRQ_PEND2;
|
|
data &= (0x0 << (irq - 32));
|
|
IRQ_PEND2 = data;
|
|
|
|
data = IRQ_DISABLE2;
|
|
data |= (0x1 << (irq - 32));
|
|
IRQ_DISABLE2 = data;
|
|
}
|
|
|
|
void gpio_irq_disable(rt_uint8_t index, bcm_gpio_pin pin)
|
|
{
|
|
int irq = 0;
|
|
rt_uint32_t reg_value;
|
|
rt_uint8_t irq_type;
|
|
irq = IRQ_GPIO0 + index;
|
|
|
|
gpio_ack_irq(irq, pin);
|
|
|
|
irq_type = _g_gpio_irq_tbl[index].irq_type[pin];
|
|
rt_uint8_t shift = pin % 32;
|
|
rt_uint32_t mask = 1 << shift;
|
|
|
|
switch (irq_type)
|
|
{
|
|
case PIN_IRQ_MODE_RISING:
|
|
reg_value = BCM283X_GPIO_GPREN(pin /32);
|
|
BCM283X_GPIO_GPREN(pin /32) = (reg_value & ~ mask) | (PIN_LOW & mask);
|
|
break;
|
|
case PIN_IRQ_MODE_FALLING:
|
|
reg_value = BCM283X_GPIO_GPFEN(pin /32);
|
|
BCM283X_GPIO_GPFEN(pin /32) = (reg_value & ~ mask) | (PIN_LOW & mask);
|
|
break;
|
|
case PIN_IRQ_MODE_RISING_FALLING:
|
|
reg_value = BCM283X_GPIO_GPAREN(pin /32);
|
|
BCM283X_GPIO_GPAREN(pin /32) = (reg_value & ~ mask) | (PIN_LOW & mask);
|
|
reg_value = BCM283X_GPIO_GPAFEN(pin /32);
|
|
BCM283X_GPIO_GPAFEN(pin /32) = (reg_value & ~ mask) | (PIN_LOW & mask);
|
|
break;
|
|
case PIN_IRQ_MODE_HIGH_LEVEL:
|
|
reg_value = BCM283X_GPIO_GPHEN(pin /32);
|
|
BCM283X_GPIO_GPHEN(pin /32) = (reg_value & ~ mask) | (PIN_LOW & mask);
|
|
break;
|
|
case PIN_IRQ_MODE_LOW_LEVEL:
|
|
reg_value = BCM283X_GPIO_GPLEN(pin /32);
|
|
BCM283X_GPIO_GPLEN(pin /32) = (reg_value & ~ mask) | (PIN_LOW & mask);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void gpio_irq_enable(rt_uint8_t index, bcm_gpio_pin pin)
|
|
{
|
|
rt_uint32_t offset;
|
|
rt_uint32_t data;
|
|
|
|
offset = pin;
|
|
if (index == 0)
|
|
offset = IRQ_GPIO0 - 32;
|
|
else if (index == 1)
|
|
offset = IRQ_GPIO1 - 32;
|
|
else
|
|
offset = IRQ_GPIO2 - 32;
|
|
|
|
data = IRQ_ENABLE2;
|
|
data |= 0x1 << offset;
|
|
IRQ_ENABLE2 = data;
|
|
|
|
}
|
|
|
|
static void raspi_pin_mode(struct rt_device *dev, rt_base_t pin, rt_uint8_t mode)
|
|
{
|
|
RT_ASSERT((BCM_GPIO_PIN_0 <= pin) && (pin < BCM_GPIO_PIN_NULL));
|
|
RT_ASSERT(!(mode & 0x8));
|
|
|
|
switch (mode)
|
|
{
|
|
case PIN_MODE_OUTPUT:
|
|
GPIO_FSEL(pin, BCM283X_GPIO_FSEL_OUTP);
|
|
break;
|
|
case PIN_MODE_INPUT:
|
|
GPIO_FSEL(pin, BCM283X_GPIO_FSEL_INPT);
|
|
break;
|
|
case PIN_MODE_INPUT_PULLUP:
|
|
gpio_set_pud(pin, BCM283X_GPIO_PUD_UP);
|
|
GPIO_FSEL(pin, BCM283X_GPIO_FSEL_INPT);
|
|
break;
|
|
case PIN_MODE_INPUT_PULLDOWN:
|
|
gpio_set_pud(pin, BCM283X_GPIO_PUD_DOWN);
|
|
GPIO_FSEL(pin, BCM283X_GPIO_FSEL_INPT);
|
|
break;
|
|
case PIN_MODE_OUTPUT_OD:
|
|
gpio_set_pud(pin, BCM283X_GPIO_PUD_OFF);
|
|
GPIO_FSEL(pin, BCM283X_GPIO_FSEL_OUTP);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void raspi_pin_write(struct rt_device *dev, rt_base_t pin, rt_uint8_t value)
|
|
{
|
|
RT_ASSERT((BCM_GPIO_PIN_0 <= pin) && (pin < BCM_GPIO_PIN_NULL));
|
|
RT_ASSERT(!(value & 0xE));
|
|
|
|
if (value)
|
|
BCM283X_GPIO_GPSET(pin / 32) |= (1 << (pin %32));
|
|
else
|
|
BCM283X_GPIO_GPCLR(pin / 32) |= (1 << (pin %32));
|
|
|
|
}
|
|
|
|
static rt_ssize_t raspi_pin_read(struct rt_device *device, rt_base_t pin)
|
|
{
|
|
RT_ASSERT((BCM_GPIO_PIN_0 <= pin) && (pin < BCM_GPIO_PIN_NULL));
|
|
return (BCM2835_GPIO_GPLEV(pin / 32) & (1 << (pin % 32)))? PIN_HIGH : PIN_LOW;
|
|
}
|
|
|
|
static rt_err_t raspi_pin_attach_irq(struct rt_device *device, rt_base_t pin, rt_uint8_t mode, void (*hdr)(void *args), void *args)
|
|
{
|
|
RT_ASSERT((BCM_GPIO_PIN_0 <= pin) && (pin < BCM_GPIO_PIN_NULL));
|
|
|
|
rt_uint8_t index;
|
|
rt_uint32_t reg_value;
|
|
if (pin <= 27)
|
|
index = 0;
|
|
else if (pin <= 45)
|
|
index = 1;
|
|
else
|
|
index = 2;
|
|
_g_gpio_irq_tbl[index].irq_cb[pin] = hdr;
|
|
_g_gpio_irq_tbl[index].irq_arg[pin] = args;
|
|
_g_gpio_irq_tbl[index].irq_type[pin] = mode;
|
|
|
|
rt_uint8_t shift = pin % 32;
|
|
rt_uint32_t mask = 1 << shift;
|
|
|
|
switch (mode)
|
|
{
|
|
case PIN_IRQ_MODE_RISING:
|
|
reg_value = BCM283X_GPIO_GPREN(pin /32);
|
|
BCM283X_GPIO_GPREN(pin /32) = (reg_value & ~ mask) | (PIN_HIGH & mask);
|
|
break;
|
|
case PIN_IRQ_MODE_FALLING:
|
|
reg_value = BCM283X_GPIO_GPFEN(pin /32);
|
|
BCM283X_GPIO_GPFEN(pin /32) = (reg_value & ~ mask) | (PIN_HIGH & mask);
|
|
break;
|
|
case PIN_IRQ_MODE_RISING_FALLING:
|
|
reg_value = BCM283X_GPIO_GPAREN(pin /32);
|
|
BCM283X_GPIO_GPAREN(pin /32) = (reg_value & ~ mask) | (PIN_HIGH & mask);
|
|
reg_value = BCM283X_GPIO_GPAFEN(pin /32);
|
|
BCM283X_GPIO_GPAFEN(pin /32) = (reg_value & ~ mask) | (PIN_HIGH & mask);
|
|
break;
|
|
case PIN_IRQ_MODE_HIGH_LEVEL:
|
|
reg_value = BCM283X_GPIO_GPHEN(pin /32);
|
|
BCM283X_GPIO_GPHEN(pin /32) = (reg_value & ~ mask) | (PIN_HIGH & mask);
|
|
break;
|
|
case PIN_IRQ_MODE_LOW_LEVEL:
|
|
reg_value = BCM283X_GPIO_GPLEN(pin /32);
|
|
BCM283X_GPIO_GPLEN(pin /32) = (reg_value & ~ mask) | (PIN_HIGH & mask);
|
|
break;
|
|
}
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_err_t raspi_pin_detach_irq(struct rt_device *device, rt_base_t pin)
|
|
{
|
|
RT_ASSERT((BCM_GPIO_PIN_0 <= pin) && (pin < BCM_GPIO_PIN_NULL));
|
|
|
|
rt_uint8_t index;
|
|
if (pin <= 27)
|
|
index = 0;
|
|
else if (pin <= 45)
|
|
index = 1;
|
|
else
|
|
index = 2;
|
|
|
|
gpio_irq_disable(index, pin);
|
|
|
|
_g_gpio_irq_tbl[index].irq_cb[pin] = RT_NULL;
|
|
_g_gpio_irq_tbl[index].irq_arg[pin] = RT_NULL;
|
|
_g_gpio_irq_tbl[index].irq_type[pin] = RT_NULL;
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
rt_err_t raspi_pin_irq_enable(struct rt_device *device, rt_base_t pin, rt_uint8_t enabled)
|
|
{
|
|
RT_ASSERT((BCM_GPIO_PIN_0 <= pin) && (pin < BCM_GPIO_PIN_NULL));
|
|
|
|
rt_uint8_t index;
|
|
if (pin <= 27)
|
|
index = 0;
|
|
else if (pin <= 45)
|
|
index = 1;
|
|
else
|
|
index = 2;
|
|
|
|
if (enabled)
|
|
gpio_irq_enable(index, pin);
|
|
else
|
|
gpio_irq_disable(index, pin);
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static void gpio_irq_handler(int irq, void *param)
|
|
{
|
|
struct gpio_irq_def *irq_def = (struct gpio_irq_def *)param;
|
|
rt_uint32_t pin;
|
|
rt_uint32_t value;
|
|
rt_uint32_t tmpvalue;
|
|
|
|
if (irq == IRQ_GPIO0)
|
|
{
|
|
/* 0~27 */
|
|
|
|
value = BCM283X_GPIO_GPEDS(0);
|
|
value &= 0x0fffffff;
|
|
pin = 0;
|
|
BCM283X_GPIO_GPEDS(0) = 0;
|
|
}
|
|
else if (irq == IRQ_GPIO1)
|
|
{
|
|
/* 28-45 */
|
|
tmpvalue = BCM283X_GPIO_GPEDS(0);
|
|
tmpvalue &= (~0x0fffffff);
|
|
|
|
value = BCM283X_GPIO_GPEDS(1);
|
|
value &= 0x3fff;
|
|
value = (value<<4) | tmpvalue;
|
|
pin = 28;
|
|
BCM283X_GPIO_GPEDS(0) = 0;
|
|
BCM283X_GPIO_GPEDS(1) = 0;
|
|
}
|
|
else if (irq == IRQ_GPIO2)
|
|
{
|
|
/* 46-53 */
|
|
value = BCM283X_GPIO_GPEDS(1);
|
|
value &= (~0x3fff);
|
|
value &= 0xff600000;
|
|
pin = 46;
|
|
BCM283X_GPIO_GPEDS(1) = 0;
|
|
}
|
|
|
|
while (value)
|
|
{
|
|
if ((value & 0x1) && (irq_def->irq_cb[pin] != RT_NULL))
|
|
{
|
|
irq_def->irq_cb[pin](irq_def->irq_arg[pin]);
|
|
gpio_ack_irq(irq,pin);
|
|
}
|
|
pin++;
|
|
value = value >> 1;
|
|
}
|
|
}
|
|
|
|
static const struct rt_pin_ops ops =
|
|
{
|
|
raspi_pin_mode,
|
|
raspi_pin_write,
|
|
raspi_pin_read,
|
|
raspi_pin_attach_irq,
|
|
raspi_pin_detach_irq,
|
|
raspi_pin_irq_enable,
|
|
RT_NULL,
|
|
};
|
|
#endif
|
|
|
|
int rt_hw_gpio_init(void)
|
|
{
|
|
#ifdef BSP_USING_PIN
|
|
rt_device_pin_register("gpio", &ops, RT_NULL);
|
|
|
|
/* install ISR */
|
|
rt_hw_interrupt_install(IRQ_GPIO0, gpio_irq_handler, &_g_gpio_irq_tbl[0], "gpio0_irq");
|
|
rt_hw_interrupt_umask(IRQ_GPIO0);
|
|
|
|
rt_hw_interrupt_install(IRQ_GPIO1, gpio_irq_handler, &_g_gpio_irq_tbl[1], "gpio1_irq");
|
|
rt_hw_interrupt_umask(IRQ_GPIO1);
|
|
|
|
rt_hw_interrupt_install(IRQ_GPIO2, gpio_irq_handler, &_g_gpio_irq_tbl[2], "gpio2_irq");
|
|
rt_hw_interrupt_umask(IRQ_GPIO2);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
INIT_DEVICE_EXPORT(rt_hw_gpio_init);
|