From 2220a4b695f2f5ac9fe212de4bcfa5365318136f Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Sat, 28 Jan 2017 14:43:35 -0500 Subject: Improve imx31 interrupt code for PMIC and GPIO Fix stuff that was bugging me about the way I did it at first. While messing around I found RDS code wasn't masking its GPIO ISR as it should, which might lead to two different interrupts messing with the static data. Change-Id: I54626809ea3039a842af0cc9e3e42853326c4193 --- firmware/SOURCES | 2 - firmware/export/config/gigabeats.h | 10 +- firmware/export/mc13783.h | 90 ++++++--- .../target/arm/imx31/gigabeat-s/adc-gigabeat-s.c | 4 +- firmware/target/arm/imx31/gigabeat-s/adc-target.h | 1 - .../arm/imx31/gigabeat-s/button-gigabeat-s.c | 8 +- .../target/arm/imx31/gigabeat-s/button-target.h | 2 - .../arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c | 41 ++-- .../target/arm/imx31/gigabeat-s/gpio-gigabeat-s.c | 52 ----- firmware/target/arm/imx31/gigabeat-s/gpio-target.h | 51 +++-- .../arm/imx31/gigabeat-s/headphone-gigabeat-s.c | 5 +- .../arm/imx31/gigabeat-s/mc13783-gigabeat-s.c | 86 -------- .../target/arm/imx31/gigabeat-s/mc13783-target.h | 44 ++++- .../target/arm/imx31/gigabeat-s/power-gigabeat-s.c | 6 +- .../target/arm/imx31/gigabeat-s/power-gigabeat-s.h | 1 - .../target/arm/imx31/gigabeat-s/usb-gigabeat-s.c | 6 +- firmware/target/arm/imx31/gpio-imx31.c | 198 ++++++++++--------- firmware/target/arm/imx31/gpio-imx31.h | 209 ++++++++++++++++---- firmware/target/arm/imx31/mc13783-imx31.c | 219 +++++++++++---------- 19 files changed, 551 insertions(+), 484 deletions(-) delete mode 100644 firmware/target/arm/imx31/gigabeat-s/gpio-gigabeat-s.c delete mode 100644 firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c diff --git a/firmware/SOURCES b/firmware/SOURCES index d00e56f1a6..88e40fa74f 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -1139,10 +1139,8 @@ target/arm/imx31/uart-imx31.c target/arm/imx31/gigabeat-s/adc-gigabeat-s.c target/arm/imx31/gigabeat-s/backlight-gigabeat-s.c target/arm/imx31/gigabeat-s/button-gigabeat-s.c -target/arm/imx31/gigabeat-s/gpio-gigabeat-s.c target/arm/imx31/gigabeat-s/kernel-gigabeat-s.c target/arm/imx31/gigabeat-s/i2s-gigabeat-s.c -target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c target/arm/imx31/gigabeat-s/power-gigabeat-s.c target/arm/imx31/gigabeat-s/powermgmt-gigabeat-s.c target/arm/imx31/gigabeat-s/system-gigabeat-s.c diff --git a/firmware/export/config/gigabeats.h b/firmware/export/config/gigabeats.h index b20a6934a7..e1bbb18529 100644 --- a/firmware/export/config/gigabeats.h +++ b/firmware/export/config/gigabeats.h @@ -86,12 +86,6 @@ #define AB_REPEAT_ENABLE -/* Define this if you have a SI4700 fm radio tuner */ -#define CONFIG_TUNER SI4700 - -#define HAVE_RDS_CAP -#define RDS_ISR_PROCESSING - /* Define this if you have the WM8978 audio codec */ #define HAVE_WM8978 @@ -124,6 +118,10 @@ #define HAVE_LCD_ENABLE #ifndef BOOTLOADER +/* Define this if you have a SI4700 fm radio tuner */ +#define CONFIG_TUNER SI4700 +#define HAVE_RDS_CAP +#define RDS_ISR_PROCESSING /* define this if you can flip your LCD */ #define HAVE_LCD_FLIP diff --git a/firmware/export/mc13783.h b/firmware/export/mc13783.h index 6ad34346df..d427830786 100644 --- a/firmware/export/mc13783.h +++ b/firmware/export/mc13783.h @@ -1256,9 +1256,6 @@ enum mc13783_regs_enum #define MC13783_TC3PERIOD_POS (21) #define MC13783_TC3TRIODE (0x1 << 23) -/* For event enum values which are target-defined */ -#include "mc13783-target.h" - void mc13783_init(void); void mc13783_close(void); uint32_t mc13783_set(unsigned address, uint32_t bits); @@ -1296,7 +1293,7 @@ enum mc13783_int_ids MC13783_INT_ID_CHGSHORT = 9, MC13783_INT_ID_CCCV = 10, MC13783_INT_ID_CHGCURR = 11, - MC13783_INT_ID_BPONI = 12, + MC13783_INT_ID_BPON = 12, MC13783_INT_ID_LOBATL = 13, MC13783_INT_ID_LOBATH = 14, MC13783_INT_ID_UDP = 15, @@ -1306,41 +1303,70 @@ enum mc13783_int_ids MC13783_INT_ID_CKDET = 22, MC13783_INT_ID_UDM = 23, /* *STATUS1/MASK1/SENSE1 */ - MC13783_INT_ID_1HZ = 0 + 0x20, - MC13783_INT_ID_TODA = 1 + 0x20, - MC13783_INT_ID_ONOFD1 = 3 + 0x20, /* ON1B */ - MC13783_INT_ID_ONOFD2 = 4 + 0x20, /* ON2B */ - MC13783_INT_ID_ONOFD3 = 5 + 0x20, /* ON3B */ - MC13783_INT_ID_SYSRST = 6 + 0x20, - MC13783_INT_ID_RTCRST = 7 + 0x20, - MC13783_INT_ID_PCI = 8 + 0x20, - MC13783_INT_ID_WARM = 9 + 0x20, - MC13783_INT_ID_MEMHLD = 10 + 0x20, - MC13783_INT_ID_PWRRDY = 11 + 0x20, - MC13783_INT_ID_THWARNL = 12 + 0x20, - MC13783_INT_ID_THWARNH = 13 + 0x20, - MC13783_INT_ID_CLK = 14 + 0x20, - MC13783_INT_ID_SEMAF = 15 + 0x20, - MC13783_INT_ID_MC2B = 17 + 0x20, - MC13783_INT_ID_HSDET = 18 + 0x20, - MC13783_INT_ID_HSL = 19 + 0x20, - MC13783_INT_ID_ALSPTH = 20 + 0x20, - MC13783_INT_ID_AHSSHORT = 21 + 0x20, + MC13783_INT_ID_1HZ = 0 + 32, + MC13783_INT_ID_TODA = 1 + 32, + MC13783_INT_ID_ONOFD1 = 3 + 32, /* ON1B */ + MC13783_INT_ID_ONOFD2 = 4 + 32, /* ON2B */ + MC13783_INT_ID_ONOFD3 = 5 + 32, /* ON3B */ + MC13783_INT_ID_SYSRST = 6 + 32, + MC13783_INT_ID_RTCRST = 7 + 32, + MC13783_INT_ID_PCI = 8 + 32, + MC13783_INT_ID_WARM = 9 + 32, + MC13783_INT_ID_MEMHLD = 10 + 32, + MC13783_INT_ID_PWRRDY = 11 + 32, + MC13783_INT_ID_THWARNL = 12 + 32, + MC13783_INT_ID_THWARNH = 13 + 32, + MC13783_INT_ID_CLK = 14 + 32, + MC13783_INT_ID_SEMAF = 15 + 32, + MC13783_INT_ID_MC2B = 17 + 32, + MC13783_INT_ID_HSDET = 18 + 32, + MC13783_INT_ID_HSL = 19 + 32, + MC13783_INT_ID_ALSPTH = 20 + 32, + MC13783_INT_ID_AHSSHORT = 21 + 32, }; -#define MC13783_INT_ID_SET_DIV (0x20) -#define MC13783_INT_ID_NUM_MASK (0x1f) +#ifdef DEFINE_MC13783_VECTOR_TABLE struct mc13783_event { - enum mc13783_int_ids int_id : 8; - uint32_t sense : 24; - void (*callback)(void); + uint32_t id : 8; /* MC13783_INT_ID_x */ + uint32_t sense : 24; /* MC13783_xS */ + void (*callback)(void); /* MC13783_EVENT_CB_x */ }; -void mc13783_enable_event(enum mc13783_event_ids id, bool enable); +/* Declares vector table. Order-implied priority */ +#define MC13783_EVENT_VECTOR_TBL_START() \ + static FORCE_INLINE uintptr_t __mc13783_event_vector_tbl(int __what) \ + { \ + static const struct mc13783_event __tbl[] = { + +#define MC13783_EVENT_VECTOR(__name, __sense) \ + { .id = MC13783_INT_ID_##__name, \ + .sense = (__sense), \ + .callback = ({ void MC13783_EVENT_CB_##__name(void); \ + MC13783_EVENT_CB_##__name; }) }, + +#define MC13783_EVENT_VECTOR_TBL_END() \ + }; \ + switch (__what) \ + { \ + default: return (uintptr_t)__tbl; \ + case 1: return (uintptr_t)ARRAYLEN(__tbl); \ + } \ + } + +#define mc13783_event_vector_tbl \ + ((const struct mc13783_event *)__mc13783_event_vector_tbl(0)) + +#define mc13783_event_vector_tbl_len \ + ((unsigned int)__mc13783_event_vector_tbl(1)) + +#endif /* DEFINE_MC13783_VECTOR_TABLE */ + +void mc13783_enable_event(enum mc13783_int_ids id, bool enable); -/* Read the sense bit if one exists - valid only within event handlers */ -uint32_t mc13783_event_sense(enum mc13783_event_ids id); +/* Read the sense bit(s) if configured - valid only within the respective + event handler */ +uint32_t mc13783_event_sense(void); #endif /* _MC13783_H_ */ diff --git a/firmware/target/arm/imx31/gigabeat-s/adc-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/adc-gigabeat-s.c index b46fc2f63f..2a89a82e46 100644 --- a/firmware/target/arm/imx31/gigabeat-s/adc-gigabeat-s.c +++ b/firmware/target/arm/imx31/gigabeat-s/adc-gigabeat-s.c @@ -111,7 +111,7 @@ bool adc_enable_channel(int channel, bool enable) } /* ADC conversion complete event - called from PMIC ISR */ -void adc_done(void) +void MC13783_EVENT_CB_ADCDONE(void) { semaphore_release(&adc_done_signal); } @@ -132,5 +132,5 @@ void adc_init(void) /* Enable ADCDONE event */ mc13783_write(MC13783_INTERRUPT_STATUS0, MC13783_ADCDONEI); - mc13783_enable_event(MC13783_ADCDONE_EVENT, true); + mc13783_enable_event(MC13783_INT_ID_ADCDONE, true); } diff --git a/firmware/target/arm/imx31/gigabeat-s/adc-target.h b/firmware/target/arm/imx31/gigabeat-s/adc-target.h index 00027e05df..dbca920b26 100644 --- a/firmware/target/arm/imx31/gigabeat-s/adc-target.h +++ b/firmware/target/arm/imx31/gigabeat-s/adc-target.h @@ -46,7 +46,6 @@ #define ADC_UNREG_POWER ADC_BATTERY /* For compatibility */ #define ADC_READ_ERROR 0xFFFF -void adc_done(void); /* Enable conversion of specified channel (if switchoff is possible) */ bool adc_enable_channel(int channel, bool enable); diff --git a/firmware/target/arm/imx31/gigabeat-s/button-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/button-gigabeat-s.c index 3972e5722f..cdd6da041b 100644 --- a/firmware/target/arm/imx31/gigabeat-s/button-gigabeat-s.c +++ b/firmware/target/arm/imx31/gigabeat-s/button-gigabeat-s.c @@ -157,9 +157,9 @@ static void power_button_update(bool pressed) } /* Power button event - called from PMIC ISR */ -void button_power_event(void) +void MC13783_EVENT_CB_ONOFD1(void) { - power_button_update(!mc13783_event_sense(MC13783_ONOFD1_EVENT)); + power_button_update(!mc13783_event_sense()); } void button_init_device(void) @@ -197,7 +197,7 @@ void button_init_device(void) power_button_update(!(mc13783_read(MC13783_INTERRUPT_SENSE1) & MC13783_ONOFD1S)); - mc13783_enable_event(MC13783_ONOFD1_EVENT, true); + mc13783_enable_event(MC13783_INT_ID_ONOFD1, true); #ifdef HAVE_HEADPHONE_DETECTION headphone_init(); @@ -213,7 +213,7 @@ void button_close_device(void) /* Assumes HP detection is not available */ initialized = false; - mc13783_enable_event(MC13783_ONOFD1_EVENT, false); + mc13783_enable_event(MC13783_INT_ID_ONOFD1, false); ext_btn = BUTTON_NONE; } #endif /* BUTTON_DRIVER_CLOSE */ diff --git a/firmware/target/arm/imx31/gigabeat-s/button-target.h b/firmware/target/arm/imx31/gigabeat-s/button-target.h index ce624ed6cc..fba02d5dd2 100644 --- a/firmware/target/arm/imx31/gigabeat-s/button-target.h +++ b/firmware/target/arm/imx31/gigabeat-s/button-target.h @@ -30,8 +30,6 @@ #endif void button_close_device(void); -void button_power_event(void); -void headphone_detect_event(void); void headphone_init(void); void button_headphone_set(int button); diff --git a/firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c index 108a6d0944..ee91b99c0f 100644 --- a/firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c +++ b/firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c @@ -26,7 +26,7 @@ #include "thread.h" #include "mc13783.h" #include "iomuxc-imx31.h" -#include "gpio-imx31.h" +#include "gpio-target.h" #include "i2c-imx31.h" #include "fmradio_i2c.h" #include "rds.h" @@ -139,20 +139,25 @@ static struct si4700_i2c_transfer_desc .xfer = { .node = &si4700_i2c_node } }; +static bool int_restore; + static void si4700_rds_read_raw_callback(struct i2c_transfer_desc *xfer) { struct si4700_i2c_transfer_desc *xf = (struct si4700_i2c_transfer_desc *)xfer; - if (xfer->rxcount != 0) - return; /* Read didn't finish */ - - uint16_t rds_data[4]; + if (xfer->rxcount == 0) + { + uint16_t rds_data[4]; + si4700_rds_read_raw_async_complete(xf->regbuf, rds_data); - si4700_rds_read_raw_async_complete(xf->regbuf, rds_data); + if (rds_process(rds_data)) + si4700_rds_set_event(); + } + /* else read didn't finish */ - if (rds_process(rds_data)) - si4700_rds_set_event(); + if (int_restore) + gpio_int_enable(SI4700_EVENT_ID); } /* Callback from si4700_rds_read_raw to execute the read */ @@ -169,10 +174,13 @@ void si4700_read_raw_async(int count) } /* RDS GPIO interrupt handler - start RDS data read */ -void si4700_stc_rds_event(void) +void INT_SI4700_RDS(void) { - /* read and clear the interrupt */ - SI4700_GPIO_STC_RDS_ISR = (1ul << SI4700_GPIO_STC_RDS_LINE); + /* mask and clear the interrupt */ + gpio_int_disable(SI4700_EVENT_ID); + gpio_int_clear(SI4700_EVENT_ID); + + /* read the RDS data */ si4700_rds_read_raw_async(); } @@ -180,13 +188,10 @@ void si4700_stc_rds_event(void) powering down */ void si4700_rds_powerup(bool on) { - gpio_disable_event(SI4700_STC_RDS_EVENT_ID); - - if (on) - { - SI4700_GPIO_STC_RDS_ISR = (1ul << SI4700_GPIO_STC_RDS_LINE); - gpio_enable_event(SI4700_STC_RDS_EVENT_ID); - } + int_restore = on; + gpio_int_disable(SI4700_EVENT_ID); + gpio_int_clear(SI4700_EVENT_ID); + gpio_enable_event(SI4700_EVENT_ID, on); } /* One-time RDS init at startup */ diff --git a/firmware/target/arm/imx31/gigabeat-s/gpio-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/gpio-gigabeat-s.c deleted file mode 100644 index 51446934aa..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/gpio-gigabeat-s.c +++ /dev/null @@ -1,52 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (c) 2008 by Michael Sevakis - * - * Gigabeat S GPIO interrupt event descriptions - * - * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ****************************************************************************/ -#include "config.h" -#include "system.h" -#include "avic-imx31.h" -#include "gpio-imx31.h" - -/* Gigabeat S definitions for static GPIO event registration */ - -/* Describes single events for each GPIO1 pin */ -const struct gpio_event gpio1_events[] = -{ - /* mc13783 keeps the PRIINT high (no low pulse) if other unmasked - * interrupts become active when clearing them or if a source being - * cleared becomes active at that time. Edge-detection will not get - * a rising edge in that case so use high-level sense. */ - [MC13783_EVENT_ID-GPIO1_EVENT_FIRST] = - { - .mask = 1 << MC13783_GPIO_LINE, - .sense = GPIO_SENSE_HIGH_LEVEL, - .callback = mc13783_event, - }, -#ifndef BOOTLOADER - /* Generates a 5ms low pulse on the line - detect the falling edge */ - [SI4700_STC_RDS_EVENT_ID] = - { - .mask = 1 << SI4700_GPIO_STC_RDS_LINE, - .sense = GPIO_SENSE_FALLING, - .callback = si4700_stc_rds_event, - }, -#endif -}; diff --git a/firmware/target/arm/imx31/gigabeat-s/gpio-target.h b/firmware/target/arm/imx31/gigabeat-s/gpio-target.h index 4903d0f631..543b25f244 100644 --- a/firmware/target/arm/imx31/gigabeat-s/gpio-target.h +++ b/firmware/target/arm/imx31/gigabeat-s/gpio-target.h @@ -23,34 +23,33 @@ #ifndef GPIO_TARGET_H #define GPIO_TARGET_H -/* MC13783 GPIO pin info for this target */ -#define MC13783_GPIO_IMR GPIO1_IMR -#define MC13783_GPIO_NUM GPIO1_NUM -#define MC13783_GPIO_ISR GPIO1_ISR -#define MC13783_GPIO_LINE 31 - -/* SI4700 GPIO STC/RDS pin info for this target */ -#define SI4700_GPIO_STC_RDS_IMR GPIO1_IMR -#define SI4700_GPIO_STC_RDS_NUM GPIO1_NUM -#define SI4700_GPIO_STC_RDS_ISR GPIO1_ISR -#define SI4700_GPIO_STC_RDS_LINE 27 +/* Gigabeat S definitions for static GPIO event registration */ +#include "gpio-imx31.h" + +#ifdef DEFINE_GPIO_VECTOR_TABLE + +GPIO_VECTOR_TBL_START() + /* mc13783 keeps the PRIINT high (no low pulse) if other unmasked + * interrupts become active when clearing them or if a source being + * cleared becomes active at that time. Edge-detection will not get + * a rising edge in that case so use high-level sense. */ + GPIO_EVENT_VECTOR(GPIO1_31, GPIO_SENSE_HIGH_LEVEL) +#if CONFIG_TUNER + /* Generates a 5ms low pulse on the line - detect the falling edge */ + GPIO_EVENT_VECTOR(GPIO1_27, GPIO_SENSE_FALLING) +#endif /* CONFIG_TUNER */ +GPIO_VECTOR_TBL_END() #define GPIO1_INT_PRIO INT_PRIO_DEFAULT -/* Declare event indexes in priority order in a packed array */ -enum gpio_event_ids -{ - /* GPIO1 event IDs */ - MC13783_EVENT_ID = GPIO1_EVENT_FIRST, - SI4700_STC_RDS_EVENT_ID, - GPIO1_NUM_EVENTS = 2, - /* GPIO2 event IDs */ - /* none defined */ - /* GPIO3 event IDs */ - /* none defined */ -}; - -void mc13783_event(void); -void si4700_stc_rds_event(void); +#endif /* DEFINE_GPIO_VECTOR_TABLE */ + +#define INT_MC13783 GPIO1_31_EVENT_CB +#define MC13783_EVENT_ID GPIO1_31_ID + +#if CONFIG_TUNER +#define INT_SI4700_RDS GPIO1_27_EVENT_CB +#define SI4700_EVENT_ID GPIO1_27_ID +#endif /* CONFIG_TUNER */ #endif /* GPIO_TARGET_H */ diff --git a/firmware/target/arm/imx31/gigabeat-s/headphone-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/headphone-gigabeat-s.c index 6fdde32185..cf0a378fc7 100644 --- a/firmware/target/arm/imx31/gigabeat-s/headphone-gigabeat-s.c +++ b/firmware/target/arm/imx31/gigabeat-s/headphone-gigabeat-s.c @@ -25,7 +25,6 @@ #include "kernel.h" #include "thread.h" #include "mc13783.h" -#include "mc13783-target.h" #include "adc.h" #include "button.h" @@ -146,7 +145,7 @@ static void NORETURN_ATTR headphone_thread(void) } /* HP plugged/unplugged event - called from PMIC ISR */ -void headphone_detect_event(void) +void MC13783_EVENT_CB_ONOFD2(void) { /* Trigger the thread immediately. */ semaphore_release(&headphone_wakeup); @@ -170,5 +169,5 @@ void INIT_ATTR headphone_init(void) IF_COP(, CPU)); /* Enable PMIC event */ - mc13783_enable_event(MC13783_ONOFD2_EVENT, true); + mc13783_enable_event(MC13783_INT_ID_ONOFD2, true); } diff --git a/firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c deleted file mode 100644 index 76001dddd6..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c +++ /dev/null @@ -1,86 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (c) 2008 by Michael Sevakis - * - * Gigabeat S MC13783 event descriptions - * - * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ****************************************************************************/ -#include "config.h" -#include "system.h" -#include "spi-imx31.h" -#include "mc13783.h" -#include "mc13783-target.h" -#include "adc-target.h" -#include "button-target.h" -#include "power-gigabeat-s.h" -#include "powermgmt-target.h" - -/* Gigabeat S mc13783 serial interface node. */ - -struct spi_node mc13783_spi = -{ - /* Based upon original firmware settings */ - CSPI2_NUM, /* CSPI module 2 */ - CSPI_CONREG_CHIP_SELECT_SS0 | /* Chip select 0 */ - CSPI_CONREG_DRCTL_DONT_CARE | /* Don't care about CSPI_RDY */ - CSPI_CONREG_DATA_RATE_DIV_32 | /* Clock = IPG_CLK/32 = 2,062,500Hz. */ - CSPI_BITCOUNT(32-1) | /* All 32 bits are to be transferred */ - CSPI_CONREG_SSPOL | /* SS active high */ - CSPI_CONREG_SSCTL | /* Negate SS between SPI bursts */ - CSPI_CONREG_MODE, /* Master mode */ - 0, /* SPI clock - no wait states */ -}; - - -/* Gigabeat S definitions for static MC13783 event registration */ - -const struct mc13783_event mc13783_events[MC13783_NUM_EVENTS] = -{ - [MC13783_ADCDONE_EVENT] = /* ADC conversion complete */ - { - .int_id = MC13783_INT_ID_ADCDONE, - .sense = 0, - .callback = adc_done, - }, - [MC13783_ONOFD1_EVENT] = /* Power button */ - { - .int_id = MC13783_INT_ID_ONOFD1, - .sense = MC13783_ONOFD1S, - .callback = button_power_event, - }, - [MC13783_SE1_EVENT] = /* Main charger detection */ - { - .int_id = MC13783_INT_ID_SE1, - .sense = MC13783_SE1S, - .callback = charger_main_detect_event, - }, - [MC13783_USB_EVENT] = /* USB insertion/USB charger detection */ - { - .int_id = MC13783_INT_ID_USB, - .sense = MC13783_USB4V4S, - .callback = usb_connect_event, - }, -#ifdef HAVE_HEADPHONE_DETECTION - [MC13783_ONOFD2_EVENT] = /* Headphone jack */ - { - .int_id = MC13783_INT_ID_ONOFD2, - .sense = 0, - .callback = headphone_detect_event, - }, -#endif -}; diff --git a/firmware/target/arm/imx31/gigabeat-s/mc13783-target.h b/firmware/target/arm/imx31/gigabeat-s/mc13783-target.h index 48d634035a..179c65cad6 100644 --- a/firmware/target/arm/imx31/gigabeat-s/mc13783-target.h +++ b/firmware/target/arm/imx31/gigabeat-s/mc13783-target.h @@ -23,17 +23,41 @@ #ifndef MC13783_TARGET_H #define MC13783_TARGET_H -/* Declare event indexes in priority order in a packed array */ -enum mc13783_event_ids +#include "mc13783.h" + +#ifdef DEFINE_MC13783_VECTOR_TABLE + +/* Gigabeat S mc13783 serial interface node. */ +static struct spi_node mc13783_spi = { - MC13783_ADCDONE_EVENT = 0, /* ADC conversion complete */ - MC13783_ONOFD1_EVENT, /* Power button */ -#ifdef HAVE_HEADPHONE_DETECTION - MC13783_ONOFD2_EVENT, /* Headphone jack */ -#endif - MC13783_SE1_EVENT, /* Main charger detection */ - MC13783_USB_EVENT, /* USB insertion */ - MC13783_NUM_EVENTS, + /* Based upon original firmware settings */ + CSPI2_NUM, /* CSPI module 2 */ + CSPI_CONREG_CHIP_SELECT_SS0 | /* Chip select 0 */ + CSPI_CONREG_DRCTL_DONT_CARE | /* Don't care about CSPI_RDY */ + CSPI_CONREG_DATA_RATE_DIV_32 | /* Clock = IPG_CLK/32 = 2,062,500Hz. */ + CSPI_BITCOUNT(32-1) | /* All 32 bits are to be transferred */ + CSPI_CONREG_SSPOL | /* SS active high */ + CSPI_CONREG_SSCTL | /* Negate SS between SPI bursts */ + CSPI_CONREG_MODE, /* Master mode */ + 0, /* SPI clock - no wait states */ }; +/* Gigabeat S definitions for static MC13783 event registration */ +MC13783_EVENT_VECTOR_TBL_START() + /* ADC conversion complete */ + MC13783_EVENT_VECTOR(ADCDONE, 0) + /* Power button */ + MC13783_EVENT_VECTOR(ONOFD1, MC13783_ONOFD1S) + /* Main charger detection */ + MC13783_EVENT_VECTOR(SE1, MC13783_SE1S) + /* USB insertion/USB charger detection */ + MC13783_EVENT_VECTOR(USB, MC13783_USB4V4S) +#ifdef HAVE_HEADPHONE_DETECTION + /* Headphone jack */ + MC13783_EVENT_VECTOR(ONOFD2, 0) +#endif /* HAVE_HEADPHONE_DETECTION */ +MC13783_EVENT_VECTOR_TBL_END() + +#endif /* DEFINE_MC13783_VECTOR_TABLE */ + #endif /* MC13783_TARGET_H */ diff --git a/firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.c index 5d89802bc9..81f150acd7 100644 --- a/firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.c +++ b/firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.c @@ -66,9 +66,9 @@ static void update_main_charger(bool present) } /* Detect changes in presence of the AC adaptor. Called from PMIC ISR. */ -void charger_main_detect_event(void) +void MC13783_EVENT_CB_SE1(void) { - update_main_charger(mc13783_event_sense(MC13783_SE1_EVENT)); + update_main_charger(mc13783_event_sense()); } /* Detect changes in USB bus power. Called from usb connect event ISR. */ @@ -159,5 +159,5 @@ void power_init(void) & MC13783_SE1S); /* Enable detect event */ - mc13783_enable_event(MC13783_SE1_EVENT, true); + mc13783_enable_event(MC13783_INT_ID_SE1, true); } diff --git a/firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.h b/firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.h index 9294de102c..2cd509267c 100644 --- a/firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.h +++ b/firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.h @@ -21,7 +21,6 @@ #ifndef POWER_IMX31_H #define POWER_IMX31_H -void charger_main_detect_event(void); void charger_usb_detect_event(int status); #endif /* POWER_IMX31_H */ diff --git a/firmware/target/arm/imx31/gigabeat-s/usb-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/usb-gigabeat-s.c index 9129568b7a..71e8342595 100644 --- a/firmware/target/arm/imx31/gigabeat-s/usb-gigabeat-s.c +++ b/firmware/target/arm/imx31/gigabeat-s/usb-gigabeat-s.c @@ -70,10 +70,10 @@ static void update_usb_status(bool sense) } /* Detect presence of USB bus - called from PMIC ISR */ -void usb_connect_event(void) +void MC13783_EVENT_CB_USB(void) { /* Read the associated sense value */ - update_usb_status(mc13783_event_sense(MC13783_USB_EVENT)); + update_usb_status(mc13783_event_sense()); } int usb_detect(void) @@ -90,7 +90,7 @@ void usb_init_device(void) update_usb_status(usb_plugged()); /* Enable PMIC event */ - mc13783_enable_event(MC13783_USB_EVENT, true); + mc13783_enable_event(MC13783_INT_ID_USB, true); } void usb_enable(bool on) diff --git a/firmware/target/arm/imx31/gpio-imx31.c b/firmware/target/arm/imx31/gpio-imx31.c index e368d1ae07..7a7363be88 100644 --- a/firmware/target/arm/imx31/gpio-imx31.c +++ b/firmware/target/arm/imx31/gpio-imx31.c @@ -23,7 +23,8 @@ #include "config.h" #include "system.h" #include "avic-imx31.h" -#include "gpio-imx31.h" +#define DEFINE_GPIO_VECTOR_TABLE +#include "gpio-target.h" /* UIE vector found in avic-imx31.c */ extern void UIE_VECTOR(void); @@ -31,101 +32,91 @@ extern void UIE_VECTOR(void); /* Event lists are allocated for the specific target */ #if (GPIO_EVENT_MASK & USE_GPIO1_EVENTS) static __attribute__((interrupt("IRQ"))) void GPIO1_HANDLER(void); -extern const struct gpio_event gpio1_events[GPIO1_NUM_EVENTS]; #endif #if (GPIO_EVENT_MASK & USE_GPIO2_EVENTS) static __attribute__((interrupt("IRQ"))) void GPIO2_HANDLER(void); -extern const struct gpio_event gpio2_events[GPIO2_NUM_EVENTS]; #endif #if (GPIO_EVENT_MASK & USE_GPIO3_EVENTS) static __attribute__((interrupt("IRQ"))) void GPIO3_HANDLER(void); -extern const struct gpio_event gpio3_events[GPIO3_NUM_EVENTS]; #endif -#define DR (0x00 / sizeof (unsigned long)) /* 00h */ -#define GDIR (0x04 / sizeof (unsigned long)) /* 04h */ -#define PSR (0x08 / sizeof (unsigned long)) /* 08h */ -#define ICR (0x0C / sizeof (unsigned long)) /* 0Ch ICR1,2 */ -#define IMR (0x14 / sizeof (unsigned long)) /* 14h */ -#define ISR (0x18 / sizeof (unsigned long)) - -static const struct gpio_module_desc +static struct gpio_module_desc { volatile unsigned long * const base; /* Module base address */ void (* const handler)(void); /* Interrupt function */ - const struct gpio_event * const events; /* Event handler list */ + const struct gpio_event *events; /* Event handler list */ + unsigned long enabled; /* Enabled event mask */ const uint8_t ints; /* AVIC int number */ const uint8_t int_priority; /* AVIC int priority */ - const uint8_t count; /* Number of events */ -} gpio_descs[GPIO_NUM_GPIO] = + uint8_t count; /* Number of events */ +} * const gpio_descs[] = { #if (GPIO_EVENT_MASK & USE_GPIO1_EVENTS) - { + [GPIO1_NUM] = &(struct gpio_module_desc) { .base = (unsigned long *)GPIO1_BASE_ADDR, .ints = INT_GPIO1, .handler = GPIO1_HANDLER, - .events = gpio1_events, - .count = GPIO1_NUM_EVENTS, .int_priority = GPIO1_INT_PRIO }, #endif #if (GPIO_EVENT_MASK & USE_GPIO2_EVENTS) - { + [GPIO2_NUM] = &(struct gpio_module_desc) { .base = (unsigned long *)GPIO2_BASE_ADDR, .ints = INT_GPIO2, .handler = GPIO2_HANDLER, - .events = gpio2_events, - .count = GPIO2_NUM_EVENTS, .int_priority = GPIO2_INT_PRIO }, #endif #if (GPIO_EVENT_MASK & USE_GPIO3_EVENTS) - { + [GPIO3_NUM] = &(struct gpio_module_desc) { .base = (unsigned long *)GPIO3_BASE_ADDR, .ints = INT_GPIO3, .handler = GPIO3_HANDLER, - .events = gpio3_events, - .count = GPIO3_NUM_EVENTS, .int_priority = GPIO3_INT_PRIO, }, #endif }; +#define GPIO_MODULE_CNT ARRAYLEN(gpio_descs) + +static const struct gpio_event * event_from_id( + const struct gpio_module_desc *desc, enum gpio_id id) +{ + const struct gpio_event *events = desc->events; + for (unsigned int i = 0; i < desc->count; i++) + { + if (events[i].id == id) + return &events[i]; + } + + return NULL; +} + static void gpio_call_events(enum gpio_module_number gpio) { - const struct gpio_module_desc * const desc = &gpio_descs[gpio]; + const struct gpio_module_desc * const desc = gpio_descs[gpio]; volatile unsigned long * const base = desc->base; - const struct gpio_event * event, *event_last; - event = desc->events; - event_last = event + desc->count; + /* Send only events that are not masked */ + unsigned long pnd = base[GPIO_ISR] & base[GPIO_IMR]; - /* Intersect pending and unmasked bits */ - unsigned long pnd = base[ISR] & base[IMR]; + if (pnd & ~desc->enabled) + UIE_VECTOR(); /* One or more aren't handled properly */ - /* Call each event handler in order */ - /* .count is surely expected to be > 0 */ - do - { - unsigned long mask = event->mask; + const struct gpio_event *event = desc->events; + while (pnd) + { + unsigned long mask = 1ul << (event->id % 32); if (pnd & mask) { event->callback(); pnd &= ~mask; } - if (pnd == 0) - break; /* Teminate early if nothing more to service */ - } - while (++event < event_last); - - if (pnd != 0) - { - /* One or more weren't handled */ - UIE_VECTOR(); + event++; } } @@ -152,69 +143,90 @@ static __attribute__((interrupt("IRQ"))) void GPIO3_HANDLER(void) void INIT_ATTR gpio_init(void) { - /* Mask-out GPIO interrupts - enable what's wanted later */ - int i; - for (i = 0; i < GPIO_NUM_GPIO; i++) - gpio_descs[i].base[IMR] = 0; + for (unsigned int mod = 0; mod < GPIO_MODULE_CNT; mod++) + { + struct gpio_module_desc * const desc = gpio_descs[mod]; + if (!desc) + continue; + + /* Parse the event table per module. First contiguous run seen for a + * module is used. */ + const struct gpio_event *event = gpio_event_vector_tbl; + for (unsigned int i = 0; i < gpio_event_vector_tbl_len; i++, event++) + { + if (event->id / 32 == mod) + { + desc->events = event; + while (++desc->count < 32 && (++event)->id / 32 == mod); + break; + } + } + + /* Mask-out GPIO interrupts - enable what's wanted later */ + desc->base[GPIO_IMR] = 0; + } } -bool gpio_enable_event(enum gpio_event_ids id) +bool gpio_enable_event(enum gpio_id id, bool enable) { - const struct gpio_module_desc * const desc = &gpio_descs[id >> 5]; - const struct gpio_event * const event = &desc->events[id & 31]; - volatile unsigned long * const base = desc->base; - volatile unsigned long *icr; - unsigned long mask, line; - unsigned long imr; - int shift; + unsigned int mod = id / 32; - int oldlevel = disable_irq_save(); + struct gpio_module_desc * const desc = gpio_descs[mod]; - imr = base[IMR]; + if (mod >= GPIO_MODULE_CNT || desc == NULL) + return false; - if (imr == 0) - { - /* First enabled interrupt for this GPIO */ - avic_enable_int(desc->ints, INT_TYPE_IRQ, desc->int_priority, - desc->handler); - } - - /* Set the line sense */ - line = find_first_set_bit(event->mask); - icr = &base[ICR + (line >> 4)]; - shift = 2*(line & 15); - mask = GPIO_SENSE_CONFIG_MASK << shift; + const struct gpio_event * const event = event_from_id(desc, id); + if (!event) + return false; - *icr = (*icr & ~mask) | ((event->sense << shift) & mask); + volatile unsigned long * const base = desc->base; + unsigned long num = id % 32; + unsigned long mask = 1ul << num; - /* Unmask the line */ - base[IMR] = imr | event->mask; + unsigned long cpsr = disable_irq_save(); - restore_irq(oldlevel); + unsigned long imr = base[GPIO_IMR]; - return true; -} + if (enable) + { + if (desc->enabled == 0) + { + /* First enabled interrupt for this GPIO */ + avic_enable_int(desc->ints, INT_TYPE_IRQ, desc->int_priority, + desc->handler); + } -void gpio_disable_event(enum gpio_event_ids id) -{ - const struct gpio_module_desc * const desc = &gpio_descs[id >> 5]; - const struct gpio_event * const event = &desc->events[id & 31]; - volatile unsigned long * const base = desc->base; - unsigned long imr; + /* Set the line sense */ + if (event->sense == GPIO_SENSE_EDGE_SEL) + { + /* Interrupt configuration register is ignored */ + base[GPIO_EDGE_SEL] |= mask; + } + else + { + volatile unsigned long *icrp = &base[GPIO_ICR + num / 16]; + unsigned int shift = 2*(num % 16); + bitmod32(icrp, event->sense << shift, 0x3 << shift); + base[GPIO_EDGE_SEL] &= ~mask; + } - int oldlevel = disable_irq_save(); + /* Unmask the line */ + desc->enabled |= mask; + imr |= mask; + } + else + { + imr &= ~mask; + desc->enabled &= ~mask; - /* Remove bit from mask */ - imr = base[IMR] & ~event->mask; + if (desc->enabled == 0) + avic_disable_int(desc->ints); /* No events remain enabled */ + } - /* Mask the line */ - base[IMR] = imr; + base[GPIO_IMR] = imr; - if (imr == 0) - { - /* No events remain enabled */ - avic_disable_int(desc->ints); - } + restore_irq(cpsr); - restore_irq(oldlevel); + return true; } diff --git a/firmware/target/arm/imx31/gpio-imx31.h b/firmware/target/arm/imx31/gpio-imx31.h index a1358672e8..86ca964f94 100644 --- a/firmware/target/arm/imx31/gpio-imx31.h +++ b/firmware/target/arm/imx31/gpio-imx31.h @@ -26,62 +26,193 @@ #define USE_GPIO2_EVENTS (1 << 1) #define USE_GPIO3_EVENTS (1 << 2) -/* Module indexes defined by which GPIO modules are used */ +/* Module logical indexes */ enum gpio_module_number { - __GPIO_NUM_START = -1, -#if (GPIO_EVENT_MASK & USE_GPIO1_EVENTS) - GPIO1_NUM, -#endif -#if (GPIO_EVENT_MASK & USE_GPIO2_EVENTS) - GPIO2_NUM, -#endif -#if (GPIO_EVENT_MASK & USE_GPIO3_EVENTS) - GPIO3_NUM, -#endif + GPIO1_NUM, /* ID 0..31 */ + GPIO2_NUM, /* ID 32..63 */ + GPIO3_NUM, /* ID 64..95 */ GPIO_NUM_GPIO, }; -/* Possible values for gpio interrupt line config */ -enum gpio_int_sense_enum +enum gpio_id { - GPIO_SENSE_LOW_LEVEL = 0, /* High-level sensitive */ - GPIO_SENSE_HIGH_LEVEL, /* Low-level sensitive */ - GPIO_SENSE_RISING, /* Rising-edge sensitive */ - GPIO_SENSE_FALLING, /* Falling-edge sensitive */ + /* GPIO1 */ + GPIO1_0_ID = 0, + GPIO1_1_ID, + GPIO1_2_ID, + GPIO1_3_ID, + GPIO1_4_ID, + GPIO1_5_ID, + GPIO1_6_ID, + GPIO1_7_ID, + GPIO1_8_ID, + GPIO1_9_ID, + GPIO1_10_ID, + GPIO1_11_ID, + GPIO1_12_ID, + GPIO1_13_ID, + GPIO1_14_ID, + GPIO1_15_ID, + GPIO1_16_ID, + GPIO1_17_ID, + GPIO1_18_ID, + GPIO1_19_ID, + GPIO1_20_ID, + GPIO1_21_ID, + GPIO1_22_ID, + GPIO1_23_ID, + GPIO1_24_ID, + GPIO1_25_ID, + GPIO1_26_ID, + GPIO1_27_ID, + GPIO1_28_ID, + GPIO1_29_ID, + GPIO1_30_ID, + GPIO1_31_ID, + /* GPIO2 */ + GPIO2_0_ID = 32, + GPIO2_1_ID, + GPIO2_2_ID, + GPIO2_3_ID, + GPIO2_4_ID, + GPIO2_5_ID, + GPIO2_6_ID, + GPIO2_7_ID, + GPIO2_8_ID, + GPIO2_9_ID, + GPIO2_10_ID, + GPIO2_11_ID, + GPIO2_12_ID, + GPIO2_13_ID, + GPIO2_14_ID, + GPIO2_15_ID, + GPIO2_16_ID, + GPIO2_17_ID, + GPIO2_18_ID, + GPIO2_19_ID, + GPIO2_20_ID, + GPIO2_21_ID, + GPIO2_22_ID, + GPIO2_23_ID, + GPIO2_24_ID, + GPIO2_25_ID, + GPIO2_26_ID, + GPIO2_27_ID, + GPIO2_28_ID, + GPIO2_29_ID, + GPIO2_30_ID, + GPIO2_31_ID, + /* GPIO3 */ + GPIO3_0_ID = 64, + GPIO3_1_ID, + GPIO3_2_ID, + GPIO3_3_ID, + GPIO3_4_ID, + GPIO3_5_ID, + GPIO3_6_ID, + GPIO3_7_ID, + GPIO3_8_ID, + GPIO3_9_ID, + GPIO3_10_ID, + GPIO3_11_ID, + GPIO3_12_ID, + GPIO3_13_ID, + GPIO3_14_ID, + GPIO3_15_ID, + GPIO3_16_ID, + GPIO3_17_ID, + GPIO3_18_ID, + GPIO3_19_ID, + GPIO3_20_ID, + GPIO3_21_ID, + GPIO3_22_ID, + GPIO3_23_ID, + GPIO3_24_ID, + GPIO3_25_ID, + GPIO3_26_ID, + GPIO3_27_ID, + GPIO3_28_ID, + GPIO3_29_ID, + GPIO3_30_ID, + GPIO3_31_ID, }; -#define GPIO_SENSE_CONFIG_MASK 0x3 +/* Possible values for gpio interrupt line config */ +enum gpio_int_sense +{ + GPIO_SENSE_LOW_LEVEL, /* High-level sensitive */ + GPIO_SENSE_HIGH_LEVEL, /* Low-level sensitive */ + GPIO_SENSE_RISING, /* Rising-edge sensitive */ + GPIO_SENSE_FALLING, /* Falling-edge sensitive */ + GPIO_SENSE_EDGE_SEL, /* Detect any edge */ +}; -/* Pending events will be called in array order which allows easy - * pioritization */ +/* Handlers will be called in declared order for a given module + Handlers of same module should be grouped together; module order + doesn't matter */ +#ifdef DEFINE_GPIO_VECTOR_TABLE /* Describes a single event for a pin */ struct gpio_event { - unsigned long mask; /* mask: 1 << (0...31) */ - enum gpio_int_sense_enum sense; /* Type of sense */ - void (*callback)(void); /* Callback function */ + uint8_t id; /* GPIOx_y_ID */ + uint8_t sense; /* GPIO_SENSE_x */ + void (*callback)(void); /* GPIOx_y_EVENT_CB */ }; -/* Module corresponding to the event ID is identified by range */ -enum gpio_event_bases -{ -#if (GPIO_EVENT_MASK & USE_GPIO1_EVENTS) - GPIO1_EVENT_FIRST = 32*GPIO1_NUM, -#endif -#if (GPIO_EVENT_MASK & USE_GPIO2_EVENTS) - GPIO2_EVENT_FIRST = 32*GPIO2_NUM, -#endif -#if (GPIO_EVENT_MASK & USE_GPIO3_EVENTS) - GPIO3_EVENT_FIRST = 32*GPIO3_NUM, -#endif -}; +#define GPIO_VECTOR_TBL_START() \ + static FORCE_INLINE uintptr_t __gpio_event_vector_tbl(int __what) \ + { \ + static const struct gpio_event __tbl[] = { + +#define GPIO_EVENT_VECTOR(__name, __sense) \ + { .id = (__name##_ID), \ + .sense = (__sense), \ + .callback = ({ void __name##_EVENT_CB(void); \ + __name##_EVENT_CB; }) }, + +#define GPIO_VECTOR_TBL_END() \ + }; \ + switch (__what) \ + { \ + default: return (uintptr_t)__tbl; \ + case 1: return (uintptr_t)ARRAYLEN(__tbl); \ + } \ + } -#include "gpio-target.h" +#define gpio_event_vector_tbl \ + ((const struct gpio_event *)__gpio_event_vector_tbl(0)) + +#define gpio_event_vector_tbl_len \ + ((unsigned int)__gpio_event_vector_tbl(1)) + +#endif /* DEFINE_GPIO_VECTOR_TABLE */ + +#define GPIO_BASE_ADDR \ + (volatile unsigned long * const [GPIO_NUM_GPIO]) { \ + (volatile unsigned long *)GPIO1_BASE_ADDR, \ + (volatile unsigned long *)GPIO2_BASE_ADDR, \ + (volatile unsigned long *)GPIO3_BASE_ADDR } + +#define GPIO_DR (0x00 / sizeof (unsigned long)) /* 00h */ +#define GPIO_GDIR (0x04 / sizeof (unsigned long)) /* 04h */ +#define GPIO_PSR (0x08 / sizeof (unsigned long)) /* 08h */ +#define GPIO_ICR (0x0C / sizeof (unsigned long)) /* 0Ch ICR1,2 */ +#define GPIO_IMR (0x14 / sizeof (unsigned long)) /* 14h */ +#define GPIO_ISR (0x18 / sizeof (unsigned long)) /* 18h */ +#define GPIO_EDGE_SEL (0x1C / sizeof (unsigned long)) /* 1Ch */ void gpio_init(void); -bool gpio_enable_event(enum gpio_event_ids id); -void gpio_disable_event(enum gpio_event_ids id); +bool gpio_enable_event(enum gpio_id id, bool enable); + +static FORCE_INLINE void gpio_int_clear(enum gpio_id id) + { GPIO_BASE_ADDR[id / 32][GPIO_ISR] = 1ul << (id % 32); } + +static FORCE_INLINE void gpio_int_enable(enum gpio_id id) + { bitset32(&GPIO_BASE_ADDR[id / 32][GPIO_IMR], 1ul << (id % 32)); } + +static FORCE_INLINE void gpio_int_disable(enum gpio_id id) + { bitclr32(&GPIO_BASE_ADDR[id / 32][GPIO_IMR], 1ul << (id % 32)); } #endif /* GPIO_IMX31_H */ diff --git a/firmware/target/arm/imx31/mc13783-imx31.c b/firmware/target/arm/imx31/mc13783-imx31.c index 627048fa54..6bf0b878f4 100644 --- a/firmware/target/arm/imx31/mc13783-imx31.c +++ b/firmware/target/arm/imx31/mc13783-imx31.c @@ -20,9 +20,9 @@ ****************************************************************************/ #include "system.h" #include "cpu.h" -#include "gpio-imx31.h" -#include "mc13783.h" +#define DEFINE_MC13783_VECTOR_TABLE #include "mc13783-target.h" +#include "gpio-target.h" #include "debug.h" #include "kernel.h" @@ -32,21 +32,42 @@ struct mc13783_transfer_desc struct spi_transfer_desc xfer; union { + /* Pick _either_ data or semaphore */ struct semaphore sema; - uint32_t data; + uint32_t data[4]; }; }; -extern const struct mc13783_event mc13783_events[MC13783_NUM_EVENTS]; -extern struct spi_node mc13783_spi; +static uint32_t pmic_int_enb[2]; /* Enabled ints */ +static uint32_t pmic_int_sense_enb[2]; /* Enabled sense reading */ +static struct mc13783_transfer_desc int_xfers[2]; /* ISR transfer descriptor */ +static const struct mc13783_event *current_event; /* Current event in callback */ +static bool int_restore; /* Prevent SPI callback from + unmasking GPIO interrupt + (lockout) */ -static uint32_t pmic_int_enb[2]; /* Enabled ints */ -static uint32_t pmic_int_sense_enb[2]; /* Enabled sense reading */ -static uint32_t int_pnd_buf[2]; /* Pending ints */ -static uint32_t int_data_buf[4]; /* ISR data buffer */ -static struct spi_transfer_desc int_xfers[2]; /* ISR transfer descriptor */ -static bool restore_event = true; /* Protect SPI callback from unmasking GPIO - interrupt (lockout) */ +static const struct mc13783_event * event_from_id(enum mc13783_int_ids id) +{ + for (unsigned int i = 0; i < mc13783_event_vector_tbl_len; i++) + { + if (mc13783_event_vector_tbl[i].id == id) + return &mc13783_event_vector_tbl[i]; + } + + return NULL; +} + +/* Called when a transfer is finished and data is ready/written */ +static void mc13783_xfer_complete_cb(struct spi_transfer_desc *xfer) +{ + semaphore_release(&((struct mc13783_transfer_desc *)xfer)->sema); +} + +static inline bool wait_for_transfer_complete(struct mc13783_transfer_desc *xfer) +{ + return semaphore_wait(&xfer->sema, TIMEOUT_BLOCK) + == OBJ_WAIT_SUCCEEDED && xfer->xfer.count == 0; +} static inline bool mc13783_transfer(struct spi_transfer_desc *xfer, uint32_t *txbuf, @@ -64,90 +85,96 @@ static inline bool mc13783_transfer(struct spi_transfer_desc *xfer, return spi_transfer(xfer); } -/* Called when a transfer is finished and data is ready/written */ -static void mc13783_xfer_complete_cb(struct spi_transfer_desc *xfer) +static inline void sync_transfer_init(struct mc13783_transfer_desc *xfer) { - semaphore_release(&((struct mc13783_transfer_desc *)xfer)->sema); + semaphore_init(&xfer->sema, 1, 0); } -static inline bool wait_for_transfer_complete(struct mc13783_transfer_desc *xfer) +static inline bool mc13783_sync_transfer(struct mc13783_transfer_desc *xfer, + uint32_t *txbuf, + uint32_t *rxbuf, + int count) { - return semaphore_wait(&xfer->sema, TIMEOUT_BLOCK) - == OBJ_WAIT_SUCCEEDED && xfer->xfer.count == 0; + sync_transfer_init(xfer); + return mc13783_transfer(&xfer->xfer, txbuf, rxbuf, count, mc13783_xfer_complete_cb); } /* Efficient interrupt status and acking */ static void mc13783_int_svc_complete_callback(struct spi_transfer_desc *xfer) { + struct mc13783_transfer_desc *desc1 = (struct mc13783_transfer_desc *)xfer; + uint32_t pnd0 = desc1->data[0], pnd1 = desc1->data[1]; + /* Restore PMIC interrupt events */ - if (restore_event) - bitset32(&MC13783_GPIO_IMR, 1ul << MC13783_GPIO_LINE); + if (int_restore) + gpio_int_enable(MC13783_EVENT_ID); /* Call handlers */ - for ( - const struct mc13783_event *event = mc13783_events; - int_pnd_buf[0] | int_pnd_buf[1]; - event++ - ) + const struct mc13783_event *event = mc13783_event_vector_tbl; + while (pnd0 | pnd1) { - unsigned int set = event->int_id / MC13783_INT_ID_SET_DIV; - uint32_t pnd = int_pnd_buf[set]; - uint32_t mask = 1 << (event->int_id & MC13783_INT_ID_NUM_MASK); + uint32_t id = event->id; + uint32_t set = id / 32; + uint32_t bit = 1 << (id % 32); - if (pnd & mask) + uint32_t pnd = set == 0 ? pnd0 : pnd1; + if (pnd & bit) { + current_event = event; event->callback(); - int_pnd_buf[set] = pnd & ~mask; + set == 0 ? (pnd0 &= ~bit) : (pnd1 &= ~bit); } - } - (void)xfer; + event++; + } } static void mc13783_int_svc_callback(struct spi_transfer_desc *xfer) { /* Only clear interrupts with handlers */ - int_pnd_buf[0] &= pmic_int_enb[0]; - int_pnd_buf[1] &= pmic_int_enb[1]; + struct mc13783_transfer_desc *desc0 = (struct mc13783_transfer_desc *)xfer; + struct mc13783_transfer_desc *desc1 = &int_xfers[1]; - /* Only read sense if enabled interrupts have them enabled */ - if ((int_pnd_buf[0] & pmic_int_sense_enb[0]) || - (int_pnd_buf[1] & pmic_int_sense_enb[1])) - { - int_data_buf[2] = MC13783_INTERRUPT_SENSE0 << 25; - int_data_buf[3] = MC13783_INTERRUPT_SENSE1 << 25; - int_xfers[1].rxbuf = int_data_buf; - int_xfers[1].count = 4; - } + uint32_t pnd0 = desc0->data[0] & pmic_int_enb[0]; + uint32_t pnd1 = desc0->data[1] & pmic_int_enb[1]; + + desc1->data[0] = pnd0; + desc1->data[1] = pnd1; /* Setup the write packets with status(es) to clear */ - int_data_buf[0] = (1 << 31) | (MC13783_INTERRUPT_STATUS0 << 25) - | int_pnd_buf[0]; - int_data_buf[1] = (1 << 31) | (MC13783_INTERRUPT_STATUS1 << 25) - | int_pnd_buf[1]; - (void)xfer; + desc0->data[0] = (1 << 31) | (MC13783_INTERRUPT_STATUS0 << 25) | pnd0; + desc0->data[1] = (1 << 31) | (MC13783_INTERRUPT_STATUS1 << 25) | pnd1; + + /* Only read sense if any pending interrupts have them enabled */ + if ((pnd0 & pmic_int_sense_enb[0]) || (pnd1 & pmic_int_sense_enb[1])) + { + desc0->data[2] = MC13783_INTERRUPT_SENSE0 << 25; + desc0->data[3] = MC13783_INTERRUPT_SENSE1 << 25; + desc1->xfer.rxbuf = desc0->data; + desc1->xfer.count = 4; + } } /* GPIO interrupt handler for mc13783 */ -void mc13783_event(void) +void INT_MC13783(void) { /* Mask the interrupt (unmasked after final read services it). */ - bitclr32(&MC13783_GPIO_IMR, 1ul << MC13783_GPIO_LINE); - MC13783_GPIO_ISR = (1ul << MC13783_GPIO_LINE); + gpio_int_disable(MC13783_EVENT_ID); + gpio_int_clear(MC13783_EVENT_ID); /* Setup the read packets */ - int_pnd_buf[0] = MC13783_INTERRUPT_STATUS0 << 25; - int_pnd_buf[1] = MC13783_INTERRUPT_STATUS1 << 25; + int_xfers[0].data[0] = MC13783_INTERRUPT_STATUS0 << 25; + int_xfers[0].data[1] = MC13783_INTERRUPT_STATUS1 << 25; unsigned long cpsr = disable_irq_save(); /* Do these without intervening transfers */ - if (mc13783_transfer(&int_xfers[0], int_pnd_buf, int_pnd_buf, 2, - mc13783_int_svc_callback)) + if (mc13783_transfer(&int_xfers[0].xfer, int_xfers[0].data, + int_xfers[0].data, 2, mc13783_int_svc_callback)) { /* Start this provisionally and fill-in actual values during the first transfer's callback - set whatever could be known */ - mc13783_transfer(&int_xfers[1], int_data_buf, NULL, 2, + mc13783_transfer(&int_xfers[1].xfer, int_xfers[0].data, NULL, 2, mc13783_int_svc_complete_callback); } @@ -166,43 +193,45 @@ void INIT_ATTR mc13783_init(void) mc13783_write(MC13783_INTERRUPT_MASK0, 0xffffff); mc13783_write(MC13783_INTERRUPT_MASK1, 0xffffff); - MC13783_GPIO_ISR = (1ul << MC13783_GPIO_LINE); - gpio_enable_event(MC13783_EVENT_ID); + gpio_int_clear(MC13783_EVENT_ID); + gpio_enable_event(MC13783_EVENT_ID, true); } void mc13783_close(void) { - restore_event = false; - gpio_disable_event(MC13783_EVENT_ID); + int_restore = false; + gpio_int_disable(MC13783_EVENT_ID); + gpio_enable_event(MC13783_EVENT_ID, false); spi_enable_node(&mc13783_spi, false); } -void mc13783_enable_event(enum mc13783_event_ids id, bool enable) +void mc13783_enable_event(enum mc13783_int_ids id, bool enable) { static const unsigned char pmic_intm_regs[2] = { MC13783_INTERRUPT_MASK0, MC13783_INTERRUPT_MASK1 }; - const struct mc13783_event * const event = &mc13783_events[id]; - unsigned int set = event->int_id / MC13783_INT_ID_SET_DIV; - uint32_t mask = 1 << (event->int_id & MC13783_INT_ID_NUM_MASK); + const struct mc13783_event * const event = event_from_id(id); + if (event == NULL) + return; + + unsigned int set = id / 32; + uint32_t mask = 1 << (id % 32); + uint32_t bit = enable ? mask : 0; /* Mask GPIO while changing bits around */ - restore_event = false; - bitclr32(&MC13783_GPIO_IMR, 1ul << MC13783_GPIO_LINE); - mc13783_write_masked(pmic_intm_regs[set], - enable ? 0 : mask, mask); - bitmod32(&pmic_int_enb[set], enable ? mask : 0, mask); - bitmod32(&pmic_int_sense_enb[set], enable ? event->sense : 0, - event->sense); - restore_event = true; - bitset32(&MC13783_GPIO_IMR, 1ul << MC13783_GPIO_LINE); + int_restore = false; + gpio_int_disable(MC13783_EVENT_ID); + mc13783_write_masked(pmic_intm_regs[set], bit ^ mask, mask); + bitmod32(&pmic_int_sense_enb[set], event->sense ? bit : 0, mask); + bitmod32(&pmic_int_enb[set], bit, mask); + int_restore = true; + gpio_int_enable(MC13783_EVENT_ID); } -uint32_t mc13783_event_sense(enum mc13783_event_ids id) +uint32_t mc13783_event_sense(void) { - const struct mc13783_event * const event = &mc13783_events[id]; - unsigned int set = event->int_id / MC13783_INT_ID_SET_DIV; - return int_data_buf[2 + set] & event->sense; + const struct mc13783_event *event = current_event; + return int_xfers[0].data[2 + event->id / 32] & event->sense; } uint32_t mc13783_set(unsigned address, uint32_t bits) @@ -219,8 +248,7 @@ uint32_t mc13783_clear(unsigned address, uint32_t bits) static void mc13783_write_masked_cb(struct spi_transfer_desc *xfer) { struct mc13783_transfer_desc *desc = (struct mc13783_transfer_desc *)xfer; - uint32_t *packets = desc->xfer.rxbuf; /* Will have been advanced by 1 */ - packets[0] |= packets[-1] & ~desc->data; + desc->data[1] |= desc->data[0] & desc->data[2]; /* & ~mask */ } uint32_t mc13783_write_masked(unsigned address, uint32_t data, uint32_t mask) @@ -230,28 +258,23 @@ uint32_t mc13783_write_masked(unsigned address, uint32_t data, uint32_t mask) mask &= 0xffffff; - uint32_t packets[2] = - { - address << 25, - (1 << 31) | (address << 25) | (data & mask) - }; - struct mc13783_transfer_desc xfers[2]; - xfers[0].data = mask; - semaphore_init(&xfers[1].sema, 1, 0); + xfers[0].data[0] = address << 25; + xfers[0].data[1] = (1 << 31) | (address << 25) | (data & mask); + xfers[0].data[2] = ~mask; unsigned long cpsr = disable_irq_save(); /* Queue up two transfers in a row */ - bool ok = mc13783_transfer(&xfers[0].xfer, &packets[0], &packets[0], 1, + bool ok = mc13783_transfer(&xfers[0].xfer, + &xfers[0].data[0], &xfers[0].data[0], 1, mc13783_write_masked_cb) && - mc13783_transfer(&xfers[1].xfer, &packets[1], NULL, 1, - mc13783_xfer_complete_cb); + mc13783_sync_transfer(&xfers[1], &xfers[0].data[1], NULL, 1); restore_irq(cpsr); if (ok && wait_for_transfer_complete(&xfers[1])) - return packets[0]; + return xfers[0].data[0]; return MC13783_DATA_ERROR; } @@ -264,10 +287,7 @@ uint32_t mc13783_read(unsigned address) uint32_t packet = address << 25; struct mc13783_transfer_desc xfer; - semaphore_init(&xfer.sema, 1, 0); - - if (mc13783_transfer(&xfer.xfer, &packet, &packet, 1, - mc13783_xfer_complete_cb) && + if (mc13783_sync_transfer(&xfer, &packet, &packet, 1) && wait_for_transfer_complete(&xfer)) { return packet; @@ -284,10 +304,7 @@ int mc13783_write(unsigned address, uint32_t data) uint32_t packet = (1 << 31) | (address << 25) | (data & 0xffffff); struct mc13783_transfer_desc xfer; - semaphore_init(&xfer.sema, 1, 0); - - if (mc13783_transfer(&xfer.xfer, &packet, NULL, 1, - mc13783_xfer_complete_cb) && + if (mc13783_sync_transfer(&xfer, &packet, NULL, 1) && wait_for_transfer_complete(&xfer)) { return 1 - xfer.xfer.count; @@ -300,7 +317,7 @@ int mc13783_read_regs(const unsigned char *regs, uint32_t *buffer, int count) { struct mc13783_transfer_desc xfer; - semaphore_init(&xfer.sema, 1, 0); + sync_transfer_init(&xfer); if (mc13783_read_async(&xfer.xfer, regs, buffer, count, mc13783_xfer_complete_cb) && @@ -316,7 +333,7 @@ int mc13783_write_regs(const unsigned char *regs, uint32_t *buffer, int count) { struct mc13783_transfer_desc xfer; - semaphore_init(&xfer.sema, 1, 0); + sync_transfer_init(&xfer); if (mc13783_write_async(&xfer.xfer, regs, buffer, count, mc13783_xfer_complete_cb) && -- cgit v1.2.3