[zynq] Add support for GPIO interrupts
This commit is contained in:
@@ -28,15 +28,92 @@
|
||||
#include <string.h>
|
||||
#include <dev/gpio.h>
|
||||
#include <platform/gpio.h>
|
||||
#include <platform/interrupts.h>
|
||||
#include <target/gpioconfig.h>
|
||||
|
||||
#define MAX_GPIO 128
|
||||
|
||||
struct {
|
||||
int_handler callback;
|
||||
void *args;
|
||||
} irq_callbacks[MAX_GPIO];
|
||||
|
||||
static enum handler_return gpio_int_handler(void *arg) {
|
||||
|
||||
/* The mask register uses 1 to respresent masked, 0 for unmasked. Comparing that
|
||||
* register with the interrupt status register is the only way to determine
|
||||
* which gpio triggered the interrupt in the gic. */
|
||||
for (uint32_t bank = 0; bank < 4; bank++) {
|
||||
uint32_t mask = *REG32(GPIO_INT_MASK(bank));
|
||||
uint32_t stat = *REG32(GPIO_INT_STAT(bank));
|
||||
uint32_t active = ~mask & stat;
|
||||
|
||||
if (active == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//printf("mask 0x%08x stat 0x%08x active 0x%08x\n", mask, stat, active);
|
||||
while (active) {
|
||||
/* Find the rightmost set bit, calculate the associated gpio, and call the callback */
|
||||
uint16_t bit = 32 - clz(active) - 1;
|
||||
uint16_t gpio = bit + (bank * 32);
|
||||
|
||||
active ^= (1 << bit);
|
||||
if (irq_callbacks[gpio].callback) {
|
||||
irq_callbacks[gpio].callback(irq_callbacks[gpio].args);
|
||||
}
|
||||
//printf("bit %u bank %u gpio %u was triggered\n", bit, bank, gpio);
|
||||
}
|
||||
|
||||
*REG32(GPIO_INT_STAT(bank)) = stat;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void zynq_unmask_gpio_interrupt(unsigned gpio)
|
||||
{
|
||||
uint16_t bank = gpio / 31;
|
||||
uint16_t bit = gpio % 32;
|
||||
|
||||
RMWREG32(GPIO_INT_EN(bank), bit, 1, 1);
|
||||
RMWREG32(GPIO_INT_STAT(bank), bit, 1, 1);
|
||||
}
|
||||
|
||||
void zynq_mask_gpio_interrupt(unsigned gpio)
|
||||
{
|
||||
uint16_t bank = gpio / 31;
|
||||
uint16_t bit = gpio % 32;
|
||||
|
||||
RMWREG32(GPIO_INT_DIS(bank), bit, 1, 1);
|
||||
}
|
||||
|
||||
|
||||
void zynq_gpio_init(void)
|
||||
{
|
||||
register_int_handler(GPIO_INT, gpio_int_handler, NULL);
|
||||
unmask_interrupt(GPIO_INT);
|
||||
}
|
||||
|
||||
void zynq_gpio_early_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
void zynq_gpio_init(void)
|
||||
void register_gpio_int_handler(unsigned gpio, int_handler handler, void *args)
|
||||
{
|
||||
DEBUG_ASSERT(gpio < MAX_GPIO);
|
||||
DEBUG_ASSERT(handler);
|
||||
|
||||
irq_callbacks[gpio].callback = handler;
|
||||
irq_callbacks[gpio].args = args;
|
||||
}
|
||||
|
||||
void unregister_gpio_int_handler(unsigned gpio)
|
||||
{
|
||||
DEBUG_ASSERT(gpio < MAX_GPIO);
|
||||
|
||||
irq_callbacks[gpio].callback = NULL;
|
||||
irq_callbacks[gpio].args = NULL;
|
||||
}
|
||||
|
||||
int gpio_config(unsigned gpio, unsigned flags)
|
||||
@@ -57,10 +134,40 @@ int gpio_config(unsigned gpio, unsigned flags)
|
||||
}
|
||||
|
||||
if (flags & GPIO_OUTPUT || flags & GPIO_INPUT) {
|
||||
if (flags & GPIO_OUTPUT && flags & GPIO_INPUT) {
|
||||
printf("Cannot configure a gpio as both an input and output direction.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
RMWREG32(GPIO_DIRM(bank), bit, 1, ((flags & GPIO_OUTPUT) > 0));
|
||||
RMWREG32(GPIO_OEN(bank), bit, 1, ((flags & GPIO_OUTPUT) > 0));
|
||||
}
|
||||
|
||||
if (flags & GPIO_EDGE || flags & GPIO_LEVEL) {
|
||||
if (flags & GPIO_EDGE && flags & GPIO_LEVEL) {
|
||||
printf("Cannot configure a gpio as both edge and level sensitive.\n");
|
||||
return -1;
|
||||
}
|
||||
RMWREG32(GPIO_INT_TYPE(bank), bit, 1, ((flags & GPIO_EDGE) > 0));
|
||||
}
|
||||
|
||||
if (flags & GPIO_RISING || flags & GPIO_FALLING) {
|
||||
/* Zynq has a specific INT_ANY register for handling interrupts that trigger on both
|
||||
* rising and falling edges, but it specifically must only be used in edge mode */
|
||||
if (flags & GPIO_RISING && flags & GPIO_FALLING) {
|
||||
if ((flags & GPIO_EDGE) == 0) {
|
||||
printf("polarity must be rising or falling if level sensitivity is used.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
RMWREG32(GPIO_INT_ANY(bank), bit, 1, 1);
|
||||
} else {
|
||||
RMWREG32(GPIO_INT_POLARITY(bank), bit, 1, ((flags & GPIO_RISING) > 0));
|
||||
RMWREG32(GPIO_INT_ANY(bank), bit, 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -83,3 +190,24 @@ int gpio_get(unsigned gpio)
|
||||
|
||||
return ((*REG32(GPIO_DATA_RO(bank)) & (1 << bit)) > 0);
|
||||
}
|
||||
|
||||
#include <lib/console.h>
|
||||
#ifdef WITH_LIB_CONSOLE
|
||||
static int cmd_zynq_gpio(int argc, const cmd_args *argv)
|
||||
{
|
||||
for (unsigned int bank = 0; bank < 4; bank++) {
|
||||
printf("DIRM_%u (0x%08x): 0x%08x\n", bank, GPIO_DIRM(bank), *REG32(GPIO_DIRM(bank)));
|
||||
printf("OEN_%u (0x%08x): 0x%08x\n", bank, GPIO_OEN(bank), *REG32(GPIO_OEN(bank)));
|
||||
printf("INT_MASK_%u (0x%08x): 0x%08x\n", bank, GPIO_INT_MASK(bank), *REG32(GPIO_INT_MASK(bank)));
|
||||
printf("INT_STAT_%u (0x%08x): 0x%08x\n", bank, GPIO_INT_STAT(bank), *REG32(GPIO_INT_STAT(bank)));
|
||||
printf("INT_TYPE_%u (0x%08x): 0x%08x\n", bank, GPIO_INT_TYPE(bank), *REG32(GPIO_INT_TYPE(bank)));
|
||||
printf("INT_POLARITY_%u (0x%08x): 0x%08x\n", bank, GPIO_INT_POLARITY(bank), *REG32(GPIO_INT_POLARITY(bank)));
|
||||
printf("INT_ANY_%u (0x%08x): 0x%08x\n", bank, GPIO_INT_ANY(bank), *REG32(GPIO_INT_ANY(bank)));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
STATIC_COMMAND_START
|
||||
{ "zynq_gpio", "Dump Zynq GPIO registers", &cmd_zynq_gpio },
|
||||
STATIC_COMMAND_END(zynq_gpio);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <platform/interrupts.h>
|
||||
#include <platform/zynq.h>
|
||||
|
||||
/* GPIO registers are not indexed in a particularly convenient manner, but can be calculated
|
||||
@@ -46,3 +47,10 @@
|
||||
#define GPIO_INT_TYPE(bank) (GPIO_REGS(bank) + 0x18)
|
||||
#define GPIO_INT_POLARITY(bank) (GPIO_REGS(bank) + 0x1C)
|
||||
#define GPIO_INT_ANY(bank) (GPIO_REGS(bank) + 0x20)
|
||||
|
||||
void zynq_unmask_gpio_interrupt(unsigned gpio);
|
||||
void zynq_mask_gpio_interrupt(unsigned gpio);
|
||||
void zynq_gpio_init(void);
|
||||
void zynq_gpio_early_init(void);
|
||||
void register_gpio_int_handler(unsigned gpio, int_handler handler, void *args);
|
||||
void unregister_gpio_int_handler(unsigned gpio);
|
||||
|
||||
@@ -305,13 +305,12 @@ void platform_init_mmu_mappings(void)
|
||||
|
||||
void platform_early_init(void)
|
||||
{
|
||||
/* Unlock the registers and leave them that way */
|
||||
#if 0
|
||||
ps7_init();
|
||||
#else
|
||||
/* Unlock the registers and leave them that way */
|
||||
zynq_slcr_unlock();
|
||||
zynq_mio_init();
|
||||
zynq_gpio_init();
|
||||
zynq_pll_init();
|
||||
zynq_clk_init();
|
||||
#if ZYNQ_SDRAM_INIT
|
||||
@@ -332,6 +331,7 @@ void platform_early_init(void)
|
||||
|
||||
/* initialize the interrupt controller */
|
||||
arm_gic_init();
|
||||
zynq_gpio_init();
|
||||
|
||||
/* initialize the timer block */
|
||||
arm_cortex_a9_timer_init(CPUPRIV_BASE, zynq_get_arm_timer_freq());
|
||||
|
||||
@@ -24,4 +24,6 @@
|
||||
|
||||
/* gpios on the zybo target */
|
||||
#define GPIO_LEDY (7)
|
||||
#define ZYBO_BTN4 (50)
|
||||
#define ZYBO_BTN5 (51)
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#include <kernel/vm.h>
|
||||
#include <platform/zynq.h>
|
||||
#include <platform/gem.h>
|
||||
#include <platform/gpio.h>
|
||||
#include <platform/interrupts.h>
|
||||
#include <target/gpioconfig.h>
|
||||
|
||||
#define ZYNQ_PKTBUF_CNT 128
|
||||
@@ -186,6 +188,21 @@ void target_early_init(void)
|
||||
gpio_set(GPIO_LEDY, 0);
|
||||
}
|
||||
|
||||
static enum handler_return toggle_ledy(void *arg) {
|
||||
static bool on = false;
|
||||
|
||||
gpio_set(GPIO_LEDY, on);
|
||||
on = !on;
|
||||
|
||||
return INT_NO_RESCHEDULE;
|
||||
}
|
||||
|
||||
void target_set_debug_led(unsigned int led, bool on)
|
||||
{
|
||||
if (led == 0) {
|
||||
gpio_set(GPIO_LEDY, on);
|
||||
}
|
||||
}
|
||||
void target_init(void)
|
||||
{
|
||||
paddr_t buf_vaddr;
|
||||
@@ -210,12 +227,9 @@ void target_init(void)
|
||||
|
||||
pktbuf_create_bufs((void *)buf_vaddr, ZYNQ_PKTBUF_CNT * sizeof(pktbuf_buf_t));
|
||||
gem_init(GEM0_BASE);
|
||||
|
||||
register_gpio_int_handler(ZYBO_BTN5, toggle_ledy, NULL);
|
||||
zynq_unmask_gpio_interrupt(ZYBO_BTN5);
|
||||
}
|
||||
|
||||
void target_set_debug_led(unsigned int led, bool on)
|
||||
{
|
||||
if (led == 0) {
|
||||
gpio_set(GPIO_LEDY, on);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user