From a7af9e4a7f25f5a32306c74e95a677e6c85f399e Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Sat, 12 Apr 2008 16:56:45 +0000 Subject: Add GPIO manager. Get the PMIC interrupt handling working (along with power button and HP detect). Add some reg field defined instead of using raw numbers. Add PMIC info to debug ports screen. Refine PMIC driver ops a little bit. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@17086 a1c6a512-1295-4272-9138-f99709370657 --- firmware/target/arm/imx31/debug-imx31.c | 48 +++++ firmware/target/arm/imx31/gigabeat-s/avic-imx31.c | 2 +- .../target/arm/imx31/gigabeat-s/backlight-imx31.c | 4 +- .../target/arm/imx31/gigabeat-s/button-imx31.c | 38 +++- .../target/arm/imx31/gigabeat-s/button-target.h | 3 + .../target/arm/imx31/gigabeat-s/gpio-gigabeat-s.c | 39 ++++ firmware/target/arm/imx31/gigabeat-s/gpio-imx31.c | 207 +++++++++++++++++++++ firmware/target/arm/imx31/gigabeat-s/gpio-imx31.h | 77 ++++++++ firmware/target/arm/imx31/gigabeat-s/gpio-target.h | 31 +++ .../target/arm/imx31/gigabeat-s/mc13783-imx31.c | 151 ++++++++++++++- firmware/target/arm/imx31/gigabeat-s/power-imx31.c | 10 +- .../target/arm/imx31/gigabeat-s/system-imx31.c | 2 + 12 files changed, 593 insertions(+), 19 deletions(-) create mode 100644 firmware/target/arm/imx31/gigabeat-s/gpio-gigabeat-s.c create mode 100644 firmware/target/arm/imx31/gigabeat-s/gpio-imx31.c create mode 100644 firmware/target/arm/imx31/gigabeat-s/gpio-imx31.h create mode 100644 firmware/target/arm/imx31/gigabeat-s/gpio-target.h (limited to 'firmware/target/arm') diff --git a/firmware/target/arm/imx31/debug-imx31.c b/firmware/target/arm/imx31/debug-imx31.c index ab8db78063..7f1c9166d6 100644 --- a/firmware/target/arm/imx31/debug-imx31.c +++ b/firmware/target/arm/imx31/debug-imx31.c @@ -24,6 +24,7 @@ #include "sprintf.h" #include "font.h" #include "debug-target.h" +#include "mc13783.h" bool __dbg_hw_info(void) { @@ -34,6 +35,43 @@ bool __dbg_ports(void) { char buf[50]; int line; + int i; + + static const char pmic_regset[] = + { + MC13783_INTERRUPT_STATUS0, + MC13783_INTERRUPT_SENSE0, + MC13783_INTERRUPT_STATUS1, + MC13783_INTERRUPT_SENSE1, + MC13783_RTC_TIME, + MC13783_RTC_ALARM, + MC13783_RTC_DAY, + MC13783_RTC_DAY_ALARM, + MC13783_ADC0, + MC13783_ADC1, + MC13783_ADC2, + MC13783_ADC3, + MC13783_ADC4, + }; + + static const char *pmic_regnames[ARRAYLEN(pmic_regset)] = + { + "Int Stat0 ", + "Int Sense0", + "Int Stat1 ", + "Int Sense1", + "RTC Time ", + "RTC Alarm ", + "RTC Day ", + "RTC Day Al", + "ADC0 ", + "ADC1 ", + "ADC2 ", + "ADC3 ", + "ADC4 ", + }; + + uint32_t pmic_regs[ARRAYLEN(pmic_regset)]; lcd_setmargins(0, 0); lcd_clear_display(); @@ -84,6 +122,16 @@ bool __dbg_ports(void) snprintf(buf, sizeof(buf), " ISR: %08lx", GPIO3_ISR); lcd_puts(0, line++, buf); line++; + lcd_puts(0, line++, "PMIC Registers"); line++; + + mc13783_read_regset(pmic_regset, pmic_regs, ARRAYLEN(pmic_regs)); + + for (i = 0; i < (int)ARRAYLEN(pmic_regs); i++) + { + snprintf(buf, sizeof(buf), "%s: %08lx", pmic_regnames[i], pmic_regs[i]); + lcd_puts(0, line++, buf); + } + lcd_update(); if (button_get_w_tmo(HZ/10) == (DEBUG_CANCEL|BUTTON_REL)) return false; diff --git a/firmware/target/arm/imx31/gigabeat-s/avic-imx31.c b/firmware/target/arm/imx31/gigabeat-s/avic-imx31.c index 194bc11ed6..6b64bfad77 100644 --- a/firmware/target/arm/imx31/gigabeat-s/avic-imx31.c +++ b/firmware/target/arm/imx31/gigabeat-s/avic-imx31.c @@ -43,7 +43,7 @@ static const char * avic_int_names[64] = "EXT_SENS1", "EXT_SENS2", "EXT_WDOG", "EXT_TV" }; -static void UIE_VECTOR(void) +void UIE_VECTOR(void) { int mode; long offset; diff --git a/firmware/target/arm/imx31/gigabeat-s/backlight-imx31.c b/firmware/target/arm/imx31/gigabeat-s/backlight-imx31.c index 051b1c8479..1a41f0c53e 100644 --- a/firmware/target/arm/imx31/gigabeat-s/backlight-imx31.c +++ b/firmware/target/arm/imx31/gigabeat-s/backlight-imx31.c @@ -34,13 +34,13 @@ bool _backlight_init(void) void _backlight_on(void) { /* LEDEN=1 */ - mc13783_set(MC13783_LED_CONTROL0, (1 << 0)); + mc13783_set(MC13783_LED_CONTROL0, MC13783_LEDEN); } void _backlight_off(void) { /* LEDEN=0 */ - mc13783_clear(MC13783_LED_CONTROL0, (1 << 0)); + mc13783_clear(MC13783_LED_CONTROL0, MC13783_LEDEN); } /* Assumes that the backlight has been initialized */ diff --git a/firmware/target/arm/imx31/gigabeat-s/button-imx31.c b/firmware/target/arm/imx31/gigabeat-s/button-imx31.c index 602f41abb9..c85fe5e37e 100644 --- a/firmware/target/arm/imx31/gigabeat-s/button-imx31.c +++ b/firmware/target/arm/imx31/gigabeat-s/button-imx31.c @@ -29,7 +29,8 @@ /* Most code in here is taken from the Linux BSP provided by Freescale * Copyright 2004-2006 Freescale Semiconductor, Inc. All Rights Reserved. */ -static uint32_t int_btn = BUTTON_NONE; +static bool headphones_detect = false; +static uint32_t int_btn = BUTTON_NONE; static bool hold_button = false; static bool hold_button_old = false; #define _button_hold() (GPIO3_DR & 0x10) @@ -48,7 +49,8 @@ static __attribute__((interrupt("IRQ"))) void KPP_HANDLER(void) unsigned short reg_val; int col, row; int i; - int button = BUTTON_NONE; + /* Power button is handled separately on PMIC */ + int button = int_btn & BUTTON_POWER; /* 1. Disable both (depress and release) keypad interrupts. */ KPP_KPSR &= ~(KPP_KPSR_KRIE | KPP_KPSR_KDIE); @@ -161,3 +163,35 @@ int button_read_device(void) /* If hold, ignore any pressed button */ return hold_button ? BUTTON_NONE : int_btn; } + +/* This is called from the mc13783 interrupt thread */ +void button_power_set_state(bool pressed) +{ + /* Prevent KPP_HANDLER from changing things */ + int oldlevel = disable_irq_save(); + + if (pressed) + { + int_btn |= BUTTON_POWER; + } + else + { + int_btn &= ~BUTTON_POWER; + } + + restore_irq(oldlevel); +} + +/* This is called from the mc13783 interrupt thread */ +void set_headphones_inserted(bool inserted) +{ + headphones_detect = inserted; +} + +/* This is called from the mc13783 interrupt thread */ +/* TODO: Just do a post to the button queue directly - implement the + * appropriate variant in the driver. */ +bool headphones_inserted(void) +{ + return headphones_detect; +} diff --git a/firmware/target/arm/imx31/gigabeat-s/button-target.h b/firmware/target/arm/imx31/gigabeat-s/button-target.h index 33f018e0bf..836a4c02cd 100644 --- a/firmware/target/arm/imx31/gigabeat-s/button-target.h +++ b/firmware/target/arm/imx31/gigabeat-s/button-target.h @@ -27,6 +27,9 @@ bool button_hold(void); void button_init_device(void); int button_read_device(void); +void button_power_set_state(bool pressed); +void set_headphones_inserted(bool inserted); +bool headphones_inserted(void); /* Toshiba Gigabeat specific button codes */ diff --git a/firmware/target/arm/imx31/gigabeat-s/gpio-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/gpio-gigabeat-s.c new file mode 100644 index 0000000000..cfbb7fcc4c --- /dev/null +++ b/firmware/target/arm/imx31/gigabeat-s/gpio-gigabeat-s.c @@ -0,0 +1,39 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (c) 2008 by Michael Sevakis + * + * Gigabeat S GPIO interrupt event descriptions + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * 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 "gpio-imx31.h" + +extern int mc13783_event(void); + +static const struct gpio_event gpio1_events = +{ + .line = MC13783_GPIO_LINE, + .sense = GPIO_SENSE_RISING, + .callback = mc13783_event, +}; + +const struct gpio_event_list gpio1_event_list = +{ + .priority = 7, + .count = 1, + .events = &gpio1_events, +}; diff --git a/firmware/target/arm/imx31/gigabeat-s/gpio-imx31.c b/firmware/target/arm/imx31/gigabeat-s/gpio-imx31.c new file mode 100644 index 0000000000..a7427f16c0 --- /dev/null +++ b/firmware/target/arm/imx31/gigabeat-s/gpio-imx31.c @@ -0,0 +1,207 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (c) 2008 by Michael Sevakis + * + * IMX31 GPIO event manager + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * 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" + +/* UIE vector found in avic-imx31.c */ +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_list gpio1_event_list; +#endif + +#if (GPIO_EVENT_MASK & USE_GPIO2_EVENTS) +static __attribute__((interrupt("IRQ"))) void GPIO2_HANDLER(void); +extern const struct gpio_event_list gpio2_event_list; +#endif + +#if (GPIO_EVENT_MASK & USE_GPIO3_EVENTS) +static __attribute__((interrupt("IRQ"))) void GPIO3_HANDLER(void); +extern const struct gpio_event_list gpio3_event_list; +#endif + +static struct gpio_module_descriptor +{ + volatile unsigned long *base; /* Module base address */ + enum IMX31_INT_LIST ints; /* AVIC int number */ + void (*handler)(void); /* Interrupt function */ + const struct gpio_event_list *list; /* Event handler list */ +} gpio_descs[GPIO_NUM_GPIO] = +{ +#if (GPIO_EVENT_MASK & USE_GPIO1_EVENTS) + { + .base = (unsigned long *)GPIO1_BASE_ADDR, + .ints = GPIO1, + .handler = GPIO1_HANDLER, + }, +#endif +#if (GPIO_EVENT_MASK & USE_GPIO2_EVENTS) + { + .base = (unsigned long *)GPIO2_BASE_ADDR, + .ints = GPIO2, + .handler = GPIO2_HANDLER, + }, +#endif +#if (GPIO_EVENT_MASK & USE_GPIO3_EVENTS) + { + .base = (unsigned long *)GPIO3_BASE_ADDR, + .ints = GPIO3, + .handler = GPIO3_HANDLER, + }, +#endif +}; + +static void gpio_call_events(enum gpio_module_number gpio) +{ + const struct gpio_module_descriptor * const desc = &gpio_descs[gpio]; + const struct gpio_event_list * const list = desc->list; + volatile unsigned long * const base = desc->base; + unsigned i; + + /* Intersect pending and unmasked bits */ + unsigned long pending = base[GPIO_ISR_I] & base[GPIO_IMR_I]; + + /* Call each event handler in order */ + for (i = 0; i < list->count; i++) + { + const struct gpio_event * const event = &list->events[i]; + unsigned long bit = 1ul << event->line; + + if ((pending & bit) && event->callback()) + pending &= ~bit; + } + + if (pending != 0) + { + /* Wasn't handled */ + UIE_VECTOR(); + } +} + +#if (GPIO_EVENT_MASK & USE_GPIO1_EVENTS) +static __attribute__((interrupt("IRQ"))) void GPIO1_HANDLER(void) +{ + gpio_call_events(GPIO1_NUM); +} +#endif + +#if (GPIO_EVENT_MASK & USE_GPIO2_EVENTS) +static __attribute__((interrupt("IRQ"))) void GPIO2_HANDLER(void) +{ + gpio_call_events(GPIO2_NUM); +} +#endif + +#if (GPIO_EVENT_MASK & USE_GPIO3_EVENTS) +static __attribute__((interrupt("IRQ"))) void GPIO3_HANDLER(void) +{ + gpio_call_events(GPIO3_NUM); +} +#endif + +void gpio_init(void) +{ + /* Mask-out GPIO interrupts - enable what's wanted later */ + GPIO1_IMR = 0; + GPIO2_IMR = 0; + GPIO3_IMR = 0; + + /* Init the externally-defined event lists for each port */ +#if (GPIO_EVENT_MASK & USE_GPIO1_EVENTS) + gpio_descs[GPIO1_NUM].list = &gpio1_event_list; +#endif +#if (GPIO_EVENT_MASK & USE_GPIO2_EVENTS) + gpio_descs[GPIO2_NUM].list = &gpio2_event_list; +#endif +#if (GPIO_EVENT_MASK & USE_GPIO3_EVENTS) + gpio_descs[GPIO3_NUM].list = &gpio3_event_list; +#endif +} + +bool gpio_enable_event(enum gpio_module_number gpio, unsigned id) +{ + const struct gpio_module_descriptor * const desc = &gpio_descs[gpio]; + const struct gpio_event * const event = &desc->list->events[id]; + volatile unsigned long * const base = desc->base; + volatile unsigned long * icr; + unsigned long mask; + unsigned long imr; + int shift; + + if (id >= desc->list->count) + return false; + + int oldlevel = disable_irq_save(); + + imr = base[GPIO_IMR_I]; + + if (imr == 0) + { + /* First enabled interrupt for this GPIO */ + avic_enable_int(desc->ints, IRQ, desc->list->priority, + desc->handler); + } + + /* Set the line sense */ + icr = &base[GPIO_ICR1_I] + event->line / 16; + shift = 2*(event->line % 16); + mask = GPIO_SENSE_CONFIG_MASK << shift; + + *icr = (*icr & ~mask) | ((event->sense << shift) & mask); + + /* Unmask the line */ + base[GPIO_IMR_I] = imr | (1ul << event->line); + + restore_irq(oldlevel); + + return true; +} + +void gpio_disable_event(enum gpio_module_number gpio, unsigned id) +{ + const struct gpio_module_descriptor * const desc = &gpio_descs[gpio]; + const struct gpio_event * const event = &desc->list->events[id]; + volatile unsigned long * const base = desc->base; + unsigned long imr; + + if (id >= desc->list->count) + return; + + int oldlevel = disable_irq_save(); + + /* Remove bit from mask */ + imr = base[GPIO_IMR_I] & ~(1ul << event->line); + + /* Mask the line */ + base[GPIO_IMR_I] = imr; + + if (imr == 0) + { + /* No events remain enabled */ + avic_disable_int(desc->ints); + } + + restore_irq(oldlevel); +} diff --git a/firmware/target/arm/imx31/gigabeat-s/gpio-imx31.h b/firmware/target/arm/imx31/gigabeat-s/gpio-imx31.h new file mode 100644 index 0000000000..a197558ad4 --- /dev/null +++ b/firmware/target/arm/imx31/gigabeat-s/gpio-imx31.h @@ -0,0 +1,77 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (c) 2008 by Michael Sevakis + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef _GPIO_IMX31_H_ +#define _GPIO_IMX31_H_ + +#include "gpio-target.h" + +#define USE_GPIO1_EVENTS (1 << 0) +#define USE_GPIO2_EVENTS (1 << 1) +#define USE_GPIO3_EVENTS (1 << 2) + +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 + GPIO_NUM_GPIO, +}; + +/* Possible values for gpio interrupt line config */ +enum gpio_int_sense_enum +{ + 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 */ +}; + +#define GPIO_SENSE_CONFIG_MASK 0x3 + +/* Pending events will be called in array order */ + +/* Describes a single event for a pin */ +struct gpio_event +{ + int line; /* Line number (0-31) */ + enum gpio_int_sense_enum sense; /* Type of sense */ + int (*callback)(void); /* Callback function (return nonzero + * to indicate this event was handled) */ +}; + +/* Describes the events attached to a port */ +struct gpio_event_list +{ + int priority; /* Interrupt priority for this GPIO */ + unsigned count; /* Count of events */ + const struct gpio_event *events; /* List of events */ +}; + +void gpio_init(void); +bool gpio_enable_event(enum gpio_module_number gpio, unsigned id); +void gpio_disable_event(enum gpio_module_number gpio, unsigned id); + +#endif /* _GPIO_IMX31_H_ */ diff --git a/firmware/target/arm/imx31/gigabeat-s/gpio-target.h b/firmware/target/arm/imx31/gigabeat-s/gpio-target.h new file mode 100644 index 0000000000..46e43af28d --- /dev/null +++ b/firmware/target/arm/imx31/gigabeat-s/gpio-target.h @@ -0,0 +1,31 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (c) 2008 by Michael Sevakis + * + * Gigabeat S GPIO interrupt event descriptions header + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef GPIO_TARGET_H +#define GPIO_TARGET_H + +#define GPIO_EVENT_MASK (USE_GPIO1_EVENTS) + +#define MC13783_GPIO_NUM GPIO1_NUM +#define MC13783_GPIO_ISR GPIO1_ISR +#define MC13783_GPIO_LINE 31 +#define MC13783_EVENT_ID 0 + +#endif /* GPIO_TARGET_H */ diff --git a/firmware/target/arm/imx31/gigabeat-s/mc13783-imx31.c b/firmware/target/arm/imx31/gigabeat-s/mc13783-imx31.c index 3af8f35f69..a04cd7f893 100644 --- a/firmware/target/arm/imx31/gigabeat-s/mc13783-imx31.c +++ b/firmware/target/arm/imx31/gigabeat-s/mc13783-imx31.c @@ -19,10 +19,13 @@ #include "system.h" #include "cpu.h" #include "spi-imx31.h" +#include "gpio-imx31.h" #include "mc13783.h" #include "debug.h" #include "kernel.h" +#include "button-target.h" + /* This is all based on communicating with the MC13783 PMU which is on * CSPI2 with the chip select at 0. The LCD controller resides on * CSPI3 cs1, but we have no idea how to communicate to it */ @@ -43,17 +46,68 @@ static int mc13783_thread_stack[DEFAULT_STACK_SIZE/sizeof(int)]; static const char *mc13783_thread_name = "pmic"; static struct wakeup mc13783_wake; +/* The next two functions are rather target-specific but they'll just be left + * here for the moment */ static __attribute__((noreturn)) void mc13783_interrupt_thread(void) { + const unsigned char status_regs[2] = + { + MC13783_INTERRUPT_STATUS0, + MC13783_INTERRUPT_STATUS1, + }; + uint32_t pending[2]; + uint32_t value; + + mc13783_read_regset(status_regs, pending, 2); + mc13783_write_regset(status_regs, pending, 2); + + gpio_enable_event(MC13783_GPIO_NUM, MC13783_EVENT_ID); + + /* Check initial states */ + value = mc13783_read(MC13783_INTERRUPT_SENSE1); + button_power_set_state((value & MC13783_ON1B) == 0); + set_headphones_inserted((value & MC13783_ON2B) == 0); + + /* Enable desired PMIC interrupts */ + mc13783_clear(MC13783_INTERRUPT_MASK1, MC13783_ON1B | MC13783_ON2B); + while (1) { wakeup_wait(&mc13783_wake, TIMEOUT_BLOCK); + + mc13783_read_regset(status_regs, pending, 2); + mc13783_write_regset(status_regs, pending, 2); + +#if 0 + if (pending[0]) + { + /* Handle ...PENDING0 */ + } +#endif + + if (pending[1]) + { + /* Handle ...PENDING1 */ + if (pending[1] & (MC13783_ON1B | MC13783_ON2B)) + { + value = mc13783_read(MC13783_INTERRUPT_SENSE1); + + if (pending[1] & MC13783_ON1B) + button_power_set_state((value & MC13783_ON1B) == 0); + + if (pending[1] & MC13783_ON2B) + set_headphones_inserted((value & MC13783_ON2B) == 0); + } + } } } -static __attribute__((interrupt("IRQ"))) void mc13783_interrupt(void) +/* GPIO interrupt handler for mc13783 */ +int mc13783_event(void) { + MC13783_GPIO_ISR = (1ul << MC13783_GPIO_LINE); wakeup_signal(&mc13783_wake); + return 1; /* Yes, it's handled */ } void mc13783_init(void) @@ -64,25 +118,44 @@ void mc13783_init(void) /* Enable the PMIC SPI module */ spi_enable_module(&mc13783_spi); + /* Mask any PMIC interrupts for now - poll initial status in thread + * and enable them there */ + mc13783_write(MC13783_INTERRUPT_MASK0, 0xffffff); + mc13783_write(MC13783_INTERRUPT_MASK1, 0xffffff); + + MC13783_GPIO_ISR = (1ul << MC13783_GPIO_LINE); + create_thread(mc13783_interrupt_thread, mc13783_thread_stack, sizeof(mc13783_thread_stack), 0, mc13783_thread_name IF_PRIO(, PRIORITY_REALTIME) IF_COP(, CPU)); } -void mc13783_set(unsigned address, uint32_t bits) +uint32_t mc13783_set(unsigned address, uint32_t bits) { spi_lock(&mc13783_spi); + uint32_t data = mc13783_read(address); - mc13783_write(address, data | bits); + + if (data != (uint32_t)-1) + mc13783_write(address, data | bits); + spi_unlock(&mc13783_spi); + + return data; } -void mc13783_clear(unsigned address, uint32_t bits) +uint32_t mc13783_clear(unsigned address, uint32_t bits) { spi_lock(&mc13783_spi); + uint32_t data = mc13783_read(address); - mc13783_write(address, data & ~bits); + + if (data != (uint32_t)-1) + mc13783_write(address, data & ~bits); + spi_unlock(&mc13783_spi); + + return data; } int mc13783_write(unsigned address, uint32_t data) @@ -108,7 +181,7 @@ int mc13783_write_multiple(unsigned start, const uint32_t *data, int count) { int i; struct spi_transfer xfer; - uint32_t packets[64]; + uint32_t packets[MC13783_NUM_REGS]; if (start + count > MC13783_NUM_REGS) return -1; @@ -129,6 +202,36 @@ int mc13783_write_multiple(unsigned start, const uint32_t *data, int count) return count - xfer.count; } +int mc13783_write_regset(const unsigned char *regs, const uint32_t *data, + int count) +{ + int i; + struct spi_transfer xfer; + uint32_t packets[MC13783_NUM_REGS]; + + if (count > MC13783_NUM_REGS) + return -1; + + for (i = 0; i < count; i++) + { + uint32_t reg = regs[i]; + + if (reg >= MC13783_NUM_REGS) + return -1; + + packets[i] = (1 << 31) | (reg << 25) | (data[i] & 0xffffff); + } + + xfer.txbuf = packets; + xfer.rxbuf = packets; + xfer.count = count; + + if (!spi_transfer(&mc13783_spi, &xfer)) + return -1; + + return count - xfer.count; +} + uint32_t mc13783_read(unsigned address) { uint32_t packet; @@ -146,25 +249,53 @@ uint32_t mc13783_read(unsigned address) if (!spi_transfer(&mc13783_spi, &xfer)) return (uint32_t)-1; - return packet & 0xffffff; + return packet; } int mc13783_read_multiple(unsigned start, uint32_t *buffer, int count) { int i; - uint32_t packets[64]; struct spi_transfer xfer; if (start + count > MC13783_NUM_REGS) return -1; - xfer.txbuf = packets; + xfer.txbuf = buffer; xfer.rxbuf = buffer; xfer.count = count; /* Prepare TX payload */ for (i = 0; i < count; i++, start++) - packets[i] = start << 25; + buffer[i] = start << 25; + + if (!spi_transfer(&mc13783_spi, &xfer)) + return -1; + + return count - xfer.count; +} + +int mc13783_read_regset(const unsigned char *regs, uint32_t *buffer, + int count) +{ + int i; + struct spi_transfer xfer; + + if (count > MC13783_NUM_REGS) + return -1; + + for (i = 0; i < count; i++) + { + unsigned reg = regs[i]; + + if (reg >= MC13783_NUM_REGS) + return -1; + + buffer[i] = reg << 25; + } + + xfer.txbuf = buffer; + xfer.rxbuf = buffer; + xfer.count = count; if (!spi_transfer(&mc13783_spi, &xfer)) return -1; diff --git a/firmware/target/arm/imx31/gigabeat-s/power-imx31.c b/firmware/target/arm/imx31/gigabeat-s/power-imx31.c index c739a19cba..f57c55a70b 100644 --- a/firmware/target/arm/imx31/gigabeat-s/power-imx31.c +++ b/firmware/target/arm/imx31/gigabeat-s/power-imx31.c @@ -17,14 +17,12 @@ * ****************************************************************************/ #include "config.h" -#include "cpu.h" -#include -#include "kernel.h" #include "system.h" #include "power.h" -#include "pcf50606.h" #include "backlight.h" #include "backlight-target.h" +#include "avic-imx31.h" +#include "mc13783.h" #ifndef SIMULATOR @@ -54,6 +52,10 @@ bool ide_powered(void) void power_off(void) { + mc13783_set(MC13783_POWER_CONTROL0, MC13783_USEROFFSPI); + + disable_interrupt(IRQ_FIQ_STATUS); + while (1); } #else /* SIMULATOR */ diff --git a/firmware/target/arm/imx31/gigabeat-s/system-imx31.c b/firmware/target/arm/imx31/gigabeat-s/system-imx31.c index bd7999558b..da5026a292 100644 --- a/firmware/target/arm/imx31/gigabeat-s/system-imx31.c +++ b/firmware/target/arm/imx31/gigabeat-s/system-imx31.c @@ -2,6 +2,7 @@ #include "system.h" #include "panic.h" #include "avic-imx31.h" +#include "gpio-imx31.h" #include "mmu-imx31.h" #include "system-target.h" #include "lcd.h" @@ -23,6 +24,7 @@ void system_init(void) /* MCR WFI enables wait mode */ CLKCTL_CCMR &= ~(3 << 14); avic_init(); + gpio_init(); } #ifdef BOOTLOADER -- cgit v1.2.3