From 0b1d7e76d751b9659f4d25be72143cd491704b7e Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Fri, 11 Apr 2008 08:51:27 +0000 Subject: Serial driver for imx31. Perhaps not 100% but maybe 80-90% (future developments will tell). Factor-out the mc13783 stuff and make that driver a layer above the SPI. TODO: start processing PMIC interrupts. Start a clkctl API for imx31 (we'll see if this sticks around but it seems reasonable here). Misc. stuff for convenience/neatness. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@17070 a1c6a512-1295-4272-9138-f99709370657 --- firmware/SOURCES | 2 + firmware/export/config-gigabeat-s.h | 3 + firmware/export/imx31l.h | 103 ++++-- firmware/export/mc13783.h | 99 ++++++ firmware/kernel.c | 3 + .../target/arm/imx31/gigabeat-s/backlight-imx31.c | 10 +- .../target/arm/imx31/gigabeat-s/clkctl-imx31.c | 45 +++ .../target/arm/imx31/gigabeat-s/clkctl-imx31.h | 86 +++++ .../target/arm/imx31/gigabeat-s/kernel-imx31.c | 19 +- .../target/arm/imx31/gigabeat-s/mc13783-imx31.c | 173 ++++++++++ firmware/target/arm/imx31/gigabeat-s/spi-imx31.c | 355 ++++++++++++++++++--- firmware/target/arm/imx31/gigabeat-s/spi-imx31.h | 56 +++- .../target/arm/imx31/gigabeat-s/system-target.h | 3 + 13 files changed, 864 insertions(+), 93 deletions(-) create mode 100644 firmware/export/mc13783.h create mode 100644 firmware/target/arm/imx31/gigabeat-s/clkctl-imx31.c create mode 100644 firmware/target/arm/imx31/gigabeat-s/clkctl-imx31.h create mode 100644 firmware/target/arm/imx31/gigabeat-s/mc13783-imx31.c diff --git a/firmware/SOURCES b/firmware/SOURCES index da7bf37f61..6d32e63be5 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -647,11 +647,13 @@ target/arm/imx31/gigabeat-s/ata-imx31.c target/arm/imx31/gigabeat-s/avic-imx31.c target/arm/imx31/gigabeat-s/backlight-imx31.c target/arm/imx31/gigabeat-s/button-imx31.c +target/arm/imx31/gigabeat-s/clkctl-imx31.c target/arm/imx31/gigabeat-s/dma_start.c target/arm/imx31/gigabeat-s/kernel-imx31.c target/arm/imx31/gigabeat-s/i2c-imx31.c target/arm/imx31/gigabeat-s/i2s-imx31.c target/arm/imx31/gigabeat-s/lcd-imx31.c +target/arm/imx31/gigabeat-s/mc13783-imx31.c target/arm/imx31/gigabeat-s/mmu-imx31.c target/arm/imx31/gigabeat-s/power-imx31.c target/arm/imx31/gigabeat-s/powermgmt-imx31.c diff --git a/firmware/export/config-gigabeat-s.h b/firmware/export/config-gigabeat-s.h index 970a882069..09f5e18ecc 100644 --- a/firmware/export/config-gigabeat-s.h +++ b/firmware/export/config-gigabeat-s.h @@ -83,6 +83,9 @@ /* Define this if you want to use coldfire's i2c interface */ #define CONFIG_I2C I2C_IMX31L +/* Define the bitmask of serial interface modules (CSPI) used */ +#define SPI_MODULE_MASK (USE_CSPI2_MODULE) + /* Type of mobile power - check this out */ #define BATTERY_CAPACITY_DEFAULT 2000 /* default battery capacity */ #define BATTERY_CAPACITY_MIN 1500 /* min. capacity selectable */ diff --git a/firmware/export/imx31l.h b/firmware/export/imx31l.h index 637b89b0ab..7dc2659b33 100755 --- a/firmware/export/imx31l.h +++ b/firmware/export/imx31l.h @@ -516,32 +516,41 @@ #define GPIO3_ISR (*(REG32_PTR_T)(GPIO3_BASE_ADDR+0x18)) /* CSPI */ -#define CSPI_RXDATA1 (*(REG32_PTR_T)(CSPI1_BASE_ADDR+0x00)) -#define CSPI_TXDATA1 (*(REG32_PTR_T)(CSPI1_BASE_ADDR+0x04)) -#define CSPI_CONREG1 (*(REG32_PTR_T)(CSPI1_BASE_ADDR+0x08)) -#define CSPI_INTREG1 (*(REG32_PTR_T)(CSPI1_BASE_ADDR+0x0C)) -#define CSPI_DMAREG1 (*(REG32_PTR_T)(CSPI1_BASE_ADDR+0x10)) -#define CSPI_STATREG1 (*(REG32_PTR_T)(CSPI1_BASE_ADDR+0x14)) -#define CSPI_PERIODREG1 (*(REG32_PTR_T)(CSPI1_BASE_ADDR+0x18)) -#define CSPI_TESTREG1 (*(REG32_PTR_T)(CSPI1_BASE_ADDR+0x1C0)) - -#define CSPI_RXDATA2 (*(REG32_PTR_T)(CSPI2_BASE_ADDR+0x00)) -#define CSPI_TXDATA2 (*(REG32_PTR_T)(CSPI2_BASE_ADDR+0x04)) -#define CSPI_CONREG2 (*(REG32_PTR_T)(CSPI2_BASE_ADDR+0x08)) -#define CSPI_INTREG2 (*(REG32_PTR_T)(CSPI2_BASE_ADDR+0x0C)) -#define CSPI_DMAREG2 (*(REG32_PTR_T)(CSPI2_BASE_ADDR+0x10)) -#define CSPI_STATREG2 (*(REG32_PTR_T)(CSPI2_BASE_ADDR+0x14)) -#define CSPI_PERIODREG2 (*(REG32_PTR_T)(CSPI2_BASE_ADDR+0x18)) -#define CSPI_TESTREG2 (*(REG32_PTR_T)(CSPI2_BASE_ADDR+0x1C0)) - -#define CSPI_RXDATA3 (*(REG32_PTR_T)(CSPI3_BASE_ADDR+0x00)) -#define CSPI_TXDATA3 (*(REG32_PTR_T)(CSPI3_BASE_ADDR+0x04)) -#define CSPI_CONREG3 (*(REG32_PTR_T)(CSPI3_BASE_ADDR+0x08)) -#define CSPI_INTREG3 (*(REG32_PTR_T)(CSPI3_BASE_ADDR+0x0C)) -#define CSPI_DMAREG3 (*(REG32_PTR_T)(CSPI3_BASE_ADDR+0x10)) -#define CSPI_STATREG3 (*(REG32_PTR_T)(CSPI3_BASE_ADDR+0x14)) -#define CSPI_PERIODREG3 (*(REG32_PTR_T)(CSPI3_BASE_ADDR+0x18)) -#define CSPI_TESTREG3 (*(REG32_PTR_T)(CSPI3_BASE_ADDR+0x1C0)) +#define CSPI_RXDATA_I 0x00 /* Offset - 0x00 */ +#define CSPI_TXDATA_I 0x01 /* Offset - 0x04 */ +#define CSPI_CONREG_I 0x02 /* Offset - 0x08 */ +#define CSPI_INTREG_I 0x03 /* Offset - 0x0C */ +#define CSPI_DMAREG_I 0x04 /* Offset - 0x10 */ +#define CSPI_STATREG_I 0x05 /* Offset - 0x14 */ +#define CSPI_PERIODREG_I 0x06 /* Offset - 0x18 */ +#define CSPI_TESTREG_I 0x70 /* Offset - 0x1C0 */ + +#define CSPI_RXDATA1 (((REG32_PTR_T)CSPI1_BASE_ADDR)[CSPI_RXDATA_I]) +#define CSPI_TXDATA1 (((REG32_PTR_T)CSPI1_BASE_ADDR)[CSPI_TXDATA_I]) +#define CSPI_CONREG1 (((REG32_PTR_T)CSPI1_BASE_ADDR)[CSPI_CONREG_I]) +#define CSPI_INTREG1 (((REG32_PTR_T)CSPI1_BASE_ADDR)[CSPI_INTREG_I]) +#define CSPI_DMAREG1 (((REG32_PTR_T)CSPI1_BASE_ADDR)[CSPI_DMAREG_I]) +#define CSPI_STATREG1 (((REG32_PTR_T)CSPI1_BASE_ADDR)[CSPI_STATREG_I]) +#define CSPI_PERIODREG1 (((REG32_PTR_T)CSPI1_BASE_ADDR)[CSPI_PERIODREG_I]) +#define CSPI_TESTREG1 (((REG32_PTR_T)CSPI1_BASE_ADDR)[CSPI_TESTREG_I]) + +#define CSPI_RXDATA2 (((REG32_PTR_T)CSPI2_BASE_ADDR)[CSPI_RXDATA_I]) +#define CSPI_TXDATA2 (((REG32_PTR_T)CSPI2_BASE_ADDR)[CSPI_TXDATA_I]) +#define CSPI_CONREG2 (((REG32_PTR_T)CSPI2_BASE_ADDR)[CSPI_CONREG_I]) +#define CSPI_INTREG2 (((REG32_PTR_T)CSPI2_BASE_ADDR)[CSPI_INTREG_I]) +#define CSPI_DMAREG2 (((REG32_PTR_T)CSPI2_BASE_ADDR)[CSPI_DMAREG_I]) +#define CSPI_STATREG2 (((REG32_PTR_T)CSPI2_BASE_ADDR)[CSPI_STATREG_I]) +#define CSPI_PERIODREG2 (((REG32_PTR_T)CSPI2_BASE_ADDR)[CSPI_PERIODREG_I]) +#define CSPI_TESTREG2 (((REG32_PTR_T)CSPI2_BASE_ADDR)[CSPI_TESTREG_I]) + +#define CSPI_RXDATA3 (((REG32_PTR_T)CSPI3_BASE_ADDR)[CSPI_RXDATA_I]) +#define CSPI_TXDATA3 (((REG32_PTR_T)CSPI3_BASE_ADDR)[CSPI_TXDATA_I]) +#define CSPI_CONREG3 (((REG32_PTR_T)CSPI3_BASE_ADDR)[CSPI_CONREG_I]) +#define CSPI_INTREG3 (((REG32_PTR_T)CSPI3_BASE_ADDR)[CSPI_INTREG_I]) +#define CSPI_DMAREG3 (((REG32_PTR_T)CSPI3_BASE_ADDR)[CSPI_DMAREG_I]) +#define CSPI_STATREG3 (((REG32_PTR_T)CSPI3_BASE_ADDR)[CSPI_STATREG_I]) +#define CSPI_PERIODREG3 (((REG32_PTR_T)CSPI3_BASE_ADDR)[CSPI_PERIODREG_I]) +#define CSPI_TESTREG3 (((REG32_PTR_T)CSPI3_BASE_ADDR)[CSPI_TESTREG_I]) /* CSPI CONREG flags/fields */ #define CSPI_CONREG_CHIP_SELECT_SS0 (0 << 24) @@ -563,6 +572,7 @@ #define CSPI_CONREG_DATA_RATE_DIV_256 (6 << 16) #define CSPI_CONREG_DATA_RATE_DIV_512 (7 << 16) #define CSPI_CONREG_DATA_RATE_DIV_MASK (7 << 16) +#define CSPI_BITCOUNT(n) ((n) << 8) #define CSPI_CONREG_SSPOL (1 << 7) #define CSPI_CONREG_SSCTL (1 << 6) #define CSPI_CONREG_PHA (1 << 6) @@ -581,7 +591,7 @@ #define CSPI_INTREG_RREN (1 << 3) #define CSPI_INTREG_TFEN (1 << 2) #define CSPI_INTREG_THEN (1 << 1) -#define CSPI_INTREF_TEEN (1 << 0) +#define CSPI_INTREG_TEEN (1 << 0) /* CSPI DMAREG flags */ #define CSPI_DMAREG_RFDEN (1 << 5) @@ -764,12 +774,6 @@ #define CLKCTL_PMCR1 (*(REG32_PTR_T)(CCM_BASE_ADDR+0x60)) #define CLKCTL_PDR2 (*(REG32_PTR_T)(CCM_BASE_ADDR+0x64)) -#define CG_OFF 0x0 /* Always off */ -#define CG_ON_RUN 0x1 /* On in run mode, off in wait and doze */ -#define CG_ON_RUN_WAIT 0x2 /* On in run and wait modes, off in doze */ -#define CG_ON_ALL 0x3 /* Always on */ -#define CG_MASK 0x3 /* bitmask */ - #define CGR0_SD_MMC1(cg) ((cg) << 0*2) #define CGR0_SD_MMC2(cg) ((cg) << 1*2) #define CGR0_GPT(cg) ((cg) << 2*2) @@ -812,6 +816,39 @@ #define CGR2_RTIC(cg) ((cg) << 5*2) #define CGR2_FIR(cg) ((cg) << 6*2) +#define WIM_GPIO3 (1 << 0) +#define WIM_GPIO2 (1 << 1) +#define WIM_GPIO1 (1 << 2) +#define WIM_PCMCIA (1 << 3) +#define WIM_WDT (1 << 4) +#define WIM_USB_OTG (1 << 5) +#define WIM_IPI_INT_UH2 (1 << 6) +#define WIM_IPI_INT_UH1 (1 << 7) +#define WIM_IPI_INT_UART5_ANDED (1 << 8) +#define WIM_IPI_INT_UART4_ANDED (1 << 9) +#define WIM_IPI_INT_UART3_ANDED (1 << 10) +#define WIM_IPI_INT_UART2_ANDED (1 << 11) +#define WIM_IPI_INT_UART1_ANDED (1 << 12) +#define WIM_IPI_INT_SIM_DATA_IRQ (1 << 13) +#define WIM_IPI_INT_SDHC2 (1 << 14) +#define WIM_IPI_INT_SDHC1 (1 << 15) +#define WIM_IPI_INT_RTC (1 << 16) +#define WIM_IPI_INT_PWM (1 << 17) +#define WIM_IPI_INT_KPP (1 << 18) +#define WIM_IPI_INT_IIM (1 << 19) +#define WIM_IPI_INT_GPT (1 << 20) +#define WIM_IPI_INT_FIR (1 << 21) +#define WIM_IPI_INT_EPIT2 (1 << 22) +#define WIM_IPI_INT_EPIT1 (1 << 23) +#define WIM_IPI_INT_CSPI2 (1 << 24) +#define WIM_IPI_INT_CSPI1 (1 << 25) +#define WIM_IPI_INT_POWER_FAIL (1 << 26) +#define WIM_IPI_INT_CSPI3 (1 << 27) +#define WIM_RESERVED28 (1 << 28) +#define WIM_RESERVED29 (1 << 29) +#define WIM_RESERVED30 (1 << 30) +#define WIM_RESERVED31 (1 << 31) + /* WEIM - CS0 */ #define CSCRU 0x00 #define CSCRL 0x04 diff --git a/firmware/export/mc13783.h b/firmware/export/mc13783.h new file mode 100644 index 0000000000..fe9ff74e33 --- /dev/null +++ b/firmware/export/mc13783.h @@ -0,0 +1,99 @@ +/*************************************************************************** + * __________ __ ___. + * 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 _MC13783_H_ +#define _MC13783_H_ + +enum mc13783_regs_enum +{ + MC13783_INTERRUPT_STATUS0 = 0x00, + MC13783_INTERRUPT_MASK0, + MC13783_INTERRUPT_SENSE0, + MC13783_INTERRUPT_STATUS1, + MC13783_INTERRUPT_MASK1, + MC13783_INTERRUPT_SENSE1, + MC13783_POWER_UP_MODE_SENSE, + MC13783_IDENTIFICATION, + MC13783_SEMAPHORE, + MC13783_ARBITRATION_PERIPHERAL_AUDIO, + MC13783_ARBITRATION_SWITCHERS, + MC13783_ARBITRATION_REGULATORS0, + MC13783_ARBITRATION_REGULATORS1, + MC13783_POWER_CONTROL0, + MC13783_POWER_CONTROL1, + MC13783_POWER_CONTROL2, + MC13783_REGEN_ASSIGNMENT, + MC13783_CONTROL_SPARE, + MC13783_MEMORYA, + MC13783_MEMORYB, + MC13783_RTC_TIME, + MC13783_RTC_ALARM, + MC13783_RTC_DAY, + MC13783_RTC_DAY_ALARM, + MC13783_SWITCHERS0, + MC13783_SWITCHERS1, + MC13783_SWITCHERS2, + MC13783_SWITCHERS3, + MC13783_SWITCHERS4, + MC13783_SWITCHERS5, + MC13783_REGULATOR_SETTING0, + MC13783_REGULATOR_SETTING1, + MC13783_REGULATOR_MODE0, + MC13783_REGULATOR_MODE1, + MC13783_POWER_MISCELLANEOUS, + MC13783_POWER_SPARE, + MC13783_AUDIO_RX0, + MC13783_AUDIO_RX1, + MC13783_AUDIO_TX, + MC13783_SSI_NETWORK, + MC13783_AUDIO_CODEC, + MC13783_AUDIO_STEREO_CODEC, + MC13783_AUDIO_SPARE, + MC13783_ADC0, + MC13783_ADC1, + MC13783_ADC2, + MC13783_ADC3, + MC13783_ADC4, + MC13783_CHARGER, + MC13783_USB0, + MC13783_CHARGER_USB1, + MC13783_LED_CONTROL0, + MC13783_LED_CONTROL1, + MC13783_LED_CONTROL2, + MC13783_LED_CONTROL3, + MC13783_LED_CONTROL4, + MC13783_LED_CONTROL5, + MC13783_SPARE, + MC13783_TRIM0, + MC13783_TRIM1, + MC13783_TEST0, + MC13783_TEST1, + MC13783_TEST2, + MC13783_TEST3, + MC13783_NUM_REGS, +}; + +void mc13783_init(void); +void mc13783_set(unsigned address, uint32_t bits); +void mc13783_clear(unsigned address, uint32_t bits); +int mc13783_write(unsigned address, uint32_t data); +int mc13783_write_multiple(unsigned start, const uint32_t *buffer, int count); +uint32_t mc13783_read(unsigned address); +int mc13783_read_multiple(unsigned start, uint32_t *buffer, int count); + +#endif /* _MC13783_H_ */ diff --git a/firmware/kernel.c b/firmware/kernel.c index 8ee553e121..f86a188d05 100644 --- a/firmware/kernel.c +++ b/firmware/kernel.c @@ -81,6 +81,9 @@ void kernel_init(void) memset(&all_queues, 0, sizeof(all_queues)); corelock_init(&all_queues.cl); tick_start(1000/HZ); +#ifdef KDEV_INIT + kernel_device_init(); +#endif } } diff --git a/firmware/target/arm/imx31/gigabeat-s/backlight-imx31.c b/firmware/target/arm/imx31/gigabeat-s/backlight-imx31.c index fc9abdb0e9..051b1c8479 100644 --- a/firmware/target/arm/imx31/gigabeat-s/backlight-imx31.c +++ b/firmware/target/arm/imx31/gigabeat-s/backlight-imx31.c @@ -23,7 +23,7 @@ #include "backlight.h" #include "lcd.h" #include "power.h" -#include "spi-imx31.h" +#include "mc13783.h" #include "debug.h" bool _backlight_init(void) @@ -33,14 +33,14 @@ bool _backlight_init(void) void _backlight_on(void) { - // This relies on the SPI interface being initialised already - spi_send(51, 1); + /* LEDEN=1 */ + mc13783_set(MC13783_LED_CONTROL0, (1 << 0)); } void _backlight_off(void) { - // This relies on the SPI interface being initialised already - spi_send(51, 0); + /* LEDEN=0 */ + mc13783_clear(MC13783_LED_CONTROL0, (1 << 0)); } /* Assumes that the backlight has been initialized */ diff --git a/firmware/target/arm/imx31/gigabeat-s/clkctl-imx31.c b/firmware/target/arm/imx31/gigabeat-s/clkctl-imx31.c new file mode 100644 index 0000000000..a01fab07d0 --- /dev/null +++ b/firmware/target/arm/imx31/gigabeat-s/clkctl-imx31.c @@ -0,0 +1,45 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (c) 2008 Michael Sevakis + * + * Clock control functions for IMX31 processor + * + * 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 "system.h" +#include "cpu.h" +#include "clkctl-imx31.h" + +void imx31_clkctl_module_clock_gating(enum IMX31_CG_LIST cg, + enum IMX31_CG_MODES mode) +{ + volatile unsigned long *reg; + unsigned long mask; + int shift; + int oldlevel; + + if (cg >= CG_NUM_CLOCKS) + return; + + reg = &CLKCTL_CGR0 + cg / 16; /* Select CGR0, CGR1, CGR2 */ + shift = 2*(cg % 16); /* Get field shift */ + mask = CG_MASK << shift; /* Select field */ + + oldlevel = disable_interrupt_save(IRQ_FIQ_STATUS); + + *reg = (*reg & ~mask) | ((mode << shift) & mask); + + restore_interrupt(oldlevel); +} diff --git a/firmware/target/arm/imx31/gigabeat-s/clkctl-imx31.h b/firmware/target/arm/imx31/gigabeat-s/clkctl-imx31.h new file mode 100644 index 0000000000..da15ef2706 --- /dev/null +++ b/firmware/target/arm/imx31/gigabeat-s/clkctl-imx31.h @@ -0,0 +1,86 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (c) 2008 Michael Sevakis + * + * Clock control functions for IMX31 processor + * + * 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 _CLKCTL_IMX31_H_ +#define _CLKCTL_IMX31_H_ + +enum IMX31_CG_LIST +{ + /* CGR0 */ + CG_SD_MMC1 = 0, + CG_SD_MMC2, + CG_GPT, + CG_EPIT1, + CG_EPIT2, + CG_IIM, + CG_ATA, + CG_SDMA, + CG_CSPI3, + CG_RNG, + CG_UART1, + CG_UART2, + CG_SSI1, + CG_I2C1, + CG_I2C2, + CG_I2C3, + /* CGR1 */ + CG_HANTRO, + CG_MEMSTICK1, + CG_MEMSTICK2, + CG_CSI, + CG_RTC, + CG_WDOG, + CG_PWM, + CG_SIM, + CG_ECT, + CG_USBOTG, + CG_KPP, + CG_IPU, + CG_UART3, + CG_UART4, + CG_UART5, + CG_1_WIRE, + /* CGR2 */ + CG_SSI2, + CG_CSPI1, + CG_CSPI2, + CG_GACC, + CG_EMI, + CG_RTIC, + CG_FIR, + CG_NUM_CLOCKS +}; + +enum IMX31_CG_MODES +{ + CGM_OFF = 0x0, /* Always off */ + CGM_ON_RUN = 0x1, /* On in run mode, off in wait and doze */ + CGM_ON_RUN_WAIT = 0x2, /* On in run and wait modes, off in doze */ + CGM_ON_ALL = 0x3, /* Always on */ +}; + +#define CG_MASK 0x3 /* bitmask */ + +/* Enable or disable module clocks independently - module must _not_ be + * active! */ +void imx31_clkctl_module_clock_gating(enum IMX31_CG_LIST cg, + enum IMX31_CG_MODES mode); + +#endif /* _CLKCTL_IMX31_H_ */ diff --git a/firmware/target/arm/imx31/gigabeat-s/kernel-imx31.c b/firmware/target/arm/imx31/gigabeat-s/kernel-imx31.c index aaa4bde0eb..f0f578b03e 100644 --- a/firmware/target/arm/imx31/gigabeat-s/kernel-imx31.c +++ b/firmware/target/arm/imx31/gigabeat-s/kernel-imx31.c @@ -19,6 +19,9 @@ #include "config.h" #include "system.h" #include "avic-imx31.h" +#include "spi-imx31.h" +#include "mc13783.h" +#include "clkctl-imx31.h" #include "kernel.h" #include "thread.h" @@ -42,10 +45,11 @@ static __attribute__((interrupt("IRQ"))) void EPIT1_HANDLER(void) void tick_start(unsigned int interval_in_ms) { - CLKCTL_CGR0 |= CGR0_EPIT1(CG_ON_ALL); /* EPIT1 module clock ON - - before writing regs! */ + imx31_clkctl_module_clock_gating(CG_EPIT1, CGM_ON_ALL); /* EPIT1 module + clock ON - before writing + regs! */ EPITCR1 &= ~(EPITCR_OCIEN | EPITCR_EN); /* Disable the counter */ - CLKCTL_WIMR0 &= ~(1 << 23); /* Clear wakeup mask */ + CLKCTL_WIMR0 &= ~WIM_IPI_INT_EPIT1; /* Clear wakeup mask */ /* mcu_main_clk = 528MHz = 27MHz * 2 * ((9 + 7/9) / 1) * CLKSRC = ipg_clk = 528MHz / 4 / 2 = 66MHz, @@ -66,11 +70,18 @@ void tick_start(unsigned int interval_in_ms) EPITCR1 |= EPITCR_EN; /* Enable the counter */ } +void kernel_device_init(void) +{ + spi_init(); + mc13783_init(); +} + #ifdef BOOTLOADER void tick_stop(void) { avic_disable_int(EPIT1); /* Disable insterrupt */ EPITCR1 &= ~(EPITCR_OCIEN | EPITCR_EN); /* Disable counter */ - CLKCTL_CGR0 &= ~CGR0_EPIT1(CG_MASK); /* EPIT1 module clock OFF */ + EPITSR1 = EPITSR_OCIF; /* Clear pending */ + imx31_clkctl_module_clock_gating(CG_EPIT1, CGM_OFF); /* Turn off module clock */ } #endif diff --git a/firmware/target/arm/imx31/gigabeat-s/mc13783-imx31.c b/firmware/target/arm/imx31/gigabeat-s/mc13783-imx31.c new file mode 100644 index 0000000000..3af8f35f69 --- /dev/null +++ b/firmware/target/arm/imx31/gigabeat-s/mc13783-imx31.c @@ -0,0 +1,173 @@ +/*************************************************************************** + * __________ __ ___. + * 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. + * + ****************************************************************************/ +#include "system.h" +#include "cpu.h" +#include "spi-imx31.h" +#include "mc13783.h" +#include "debug.h" +#include "kernel.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 */ +static struct spi_node mc13783_spi = +{ + 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_4 | /* Clock = IPG_CLK/4 - 16.5MHz */ + 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 */ +}; + +static int mc13783_thread_stack[DEFAULT_STACK_SIZE/sizeof(int)]; +static const char *mc13783_thread_name = "pmic"; +static struct wakeup mc13783_wake; + +static __attribute__((noreturn)) void mc13783_interrupt_thread(void) +{ + while (1) + { + wakeup_wait(&mc13783_wake, TIMEOUT_BLOCK); + } +} + +static __attribute__((interrupt("IRQ"))) void mc13783_interrupt(void) +{ + wakeup_signal(&mc13783_wake); +} + +void mc13783_init(void) +{ + /* Serial interface must have been initialized first! */ + wakeup_init(&mc13783_wake); + + /* Enable the PMIC SPI module */ + spi_enable_module(&mc13783_spi); + + 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) +{ + spi_lock(&mc13783_spi); + uint32_t data = mc13783_read(address); + mc13783_write(address, data | bits); + spi_unlock(&mc13783_spi); +} + +void mc13783_clear(unsigned address, uint32_t bits) +{ + spi_lock(&mc13783_spi); + uint32_t data = mc13783_read(address); + mc13783_write(address, data & ~bits); + spi_unlock(&mc13783_spi); +} + +int mc13783_write(unsigned address, uint32_t data) +{ + struct spi_transfer xfer; + uint32_t packet; + + if (address >= MC13783_NUM_REGS) + return -1; + + packet = (1 << 31) | (address << 25) | (data & 0xffffff); + xfer.txbuf = &packet; + xfer.rxbuf = &packet; + xfer.count = 1; + + if (!spi_transfer(&mc13783_spi, &xfer)) + return -1; + + return 1 - xfer.count; +} + +int mc13783_write_multiple(unsigned start, const uint32_t *data, int count) +{ + int i; + struct spi_transfer xfer; + uint32_t packets[64]; + + if (start + count > MC13783_NUM_REGS) + return -1; + + /* Prepare payload */ + for (i = 0; i < count; i++, start++) + { + packets[i] = (1 << 31) | (start << 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; + struct spi_transfer xfer; + + if (address >= MC13783_NUM_REGS) + return (uint32_t)-1; + + packet = address << 25; + + xfer.txbuf = &packet; + xfer.rxbuf = &packet; + xfer.count = 1; + + if (!spi_transfer(&mc13783_spi, &xfer)) + return (uint32_t)-1; + + return packet & 0xffffff; +} + +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.rxbuf = buffer; + xfer.count = count; + + /* Prepare TX payload */ + for (i = 0; i < count; i++, start++) + packets[i] = start << 25; + + if (!spi_transfer(&mc13783_spi, &xfer)) + return -1; + + return count - xfer.count; +} diff --git a/firmware/target/arm/imx31/gigabeat-s/spi-imx31.c b/firmware/target/arm/imx31/gigabeat-s/spi-imx31.c index 10ee3f44c0..bcbe85a76b 100644 --- a/firmware/target/arm/imx31/gigabeat-s/spi-imx31.c +++ b/firmware/target/arm/imx31/gigabeat-s/spi-imx31.c @@ -18,66 +18,325 @@ ****************************************************************************/ #include "cpu.h" #include "spi-imx31.h" +#include "avic-imx31.h" +#include "clkctl-imx31.h" #include "debug.h" #include "kernel.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 */ - -void spi_init(void) { - CSPI_CONREG2 |= (2 << 20); // Burst will be triggered at SPI_RDY low - CSPI_CONREG2 |= (2 << 16); // Clock = IPG_CLK/16 - we want about 20mhz - CSPI_CONREG2 |= (31 << 8); // All 32 bits are to be transferred - CSPI_CONREG2 |= (1 << 3); // Start burst on TXFIFO write. - CSPI_CONREG2 |= (1 << 1); // Master mode. - CSPI_CONREG2 |= 1; // Enable CSPI2; +/* Forward interrupt handler declarations */ +#if (SPI_MODULE_MASK & USE_CSPI1_MODULE) +static __attribute__((interrupt("IRQ"))) void CSPI1_HANDLER(void); +#endif +#if (SPI_MODULE_MASK & USE_CSPI2_MODULE) +static __attribute__((interrupt("IRQ"))) void CSPI2_HANDLER(void); +#endif +#if (SPI_MODULE_MASK & USE_CSPI3_MODULE) +static __attribute__((interrupt("IRQ"))) void CSPI3_HANDLER(void); +#endif + +/* State data associatated with each CSPI module */ +static struct spi_module_descriptor +{ + volatile unsigned long *base; + int enab; + struct spi_node *last; + enum IMX31_CG_LIST cg; + enum IMX31_INT_LIST ints; + int byte_size; + void (*handler)(void); + struct mutex m; + struct wakeup w; + struct spi_transfer *trans; + int rxcount; +} spi_descs[SPI_NUM_CSPI] = +/* Init non-zero members */ +{ +#if (SPI_MODULE_MASK & USE_CSPI1_MODULE) + { + .base = (unsigned long *)CSPI1_BASE_ADDR, + .cg = CG_CSPI1, + .ints = CSPI1, + .handler = CSPI1_HANDLER, + }, +#endif +#if (SPI_MODULE_MASK & USE_CSPI2_MODULE) + { + .base = (unsigned long *)CSPI2_BASE_ADDR, + .cg = CG_CSPI2, + .ints = CSPI2, + .handler = CSPI2_HANDLER, + }, +#endif +#if (SPI_MODULE_MASK & USE_CSPI3_MODULE) + { + .base = (unsigned long *)CSPI3_BASE_ADDR, + .cg = CG_CSPI3, + .ints = CSPI3, + .handler = CSPI3_HANDLER, + }, +#endif +}; + +/* Common code for interrupt handlers */ +static void spi_interrupt(enum spi_module_number spi) +{ + struct spi_module_descriptor *desc = &spi_descs[spi]; + volatile unsigned long * const base = desc->base; + struct spi_transfer *trans = desc->trans; + int inc = desc->byte_size + 1; + + if (desc->rxcount > 0) + { + /* Data received - empty out RXFIFO */ + while ((base[CSPI_STATREG_I] & CSPI_STATREG_RR) != 0) + { + uint32_t word = base[CSPI_RXDATA_I]; + + switch (desc->byte_size & 3) + { + case 3: + *(unsigned char *)(trans->rxbuf + 3) = word >> 24; + case 2: + *(unsigned char *)(trans->rxbuf + 2) = word >> 16; + case 1: + *(unsigned char *)(trans->rxbuf + 1) = word >> 8; + case 0: + *(unsigned char *)(trans->rxbuf + 0) = word; + } + + trans->rxbuf += inc; + + if (--desc->rxcount < 4) + { + unsigned long intreg = base[CSPI_INTREG_I]; + + if (desc->rxcount <= 0) + { + /* No more to receive - stop RX interrupts */ + intreg &= ~(CSPI_INTREG_RHEN | CSPI_INTREG_RREN); + base[CSPI_INTREG_I] = intreg; + break; + } + else if (!(intreg & CSPI_INTREG_RREN)) + { + /* < 4 words expected - switch to RX ready */ + intreg &= ~CSPI_INTREG_RHEN; + base[CSPI_INTREG_I] = intreg | CSPI_INTREG_RREN; + } + } + } + } + + if (trans->count > 0) + { + /* Data to transmit - fill TXFIFO or write until exhausted */ + while ((base[CSPI_STATREG_I] & CSPI_STATREG_TF) == 0) + { + uint32_t word = 0; + + switch (desc->byte_size & 3) + { + case 3: + word = *(unsigned char *)(trans->txbuf + 3) << 24; + case 2: + word |= *(unsigned char *)(trans->txbuf + 2) << 16; + case 1: + word |= *(unsigned char *)(trans->txbuf + 1) << 8; + case 0: + word |= *(unsigned char *)(trans->txbuf + 0); + } + + trans->txbuf += inc; + + base[CSPI_TXDATA_I] = word; + + if (--trans->count <= 0) + { + /* Out of data - stop TX interrupts */ + base[CSPI_INTREG_I] &= ~CSPI_INTREG_THEN; + break; + } + } + } + + /* If all interrupts have been remasked - we're done */ + if (base[CSPI_INTREG_I] == 0) + { + base[CSPI_STATREG_I] = CSPI_STATREG_TC | CSPI_STATREG_BO; + wakeup_signal(&desc->w); + } +} + +/* Interrupt handlers for each CSPI module */ +#if (SPI_MODULE_MASK & USE_CSPI1_MODULE) +static __attribute__((interrupt("IRQ"))) void CSPI1_HANDLER(void) +{ + spi_interrupt(CSPI1_NUM); +} +#endif + +#if (SPI_MODULE_MASK & USE_CSPI2_MODULE) +static __attribute__((interrupt("IRQ"))) void CSPI2_HANDLER(void) +{ + spi_interrupt(CSPI2_NUM); +} +#endif + +#if (SPI_MODULE_MASK & USE_CSPI3_MODULE) +static __attribute__((interrupt("IRQ"))) void CSPI3_HANDLER(void) +{ + spi_interrupt(CSPI3_NUM); +} +#endif + +/* Write the context for the node and remember it to avoid unneeded reconfigure */ +static bool spi_set_context(struct spi_node *node, + struct spi_module_descriptor *desc) +{ + volatile unsigned long * const base = desc->base; + + if ((base[CSPI_CONREG_I] & CSPI_CONREG_EN) == 0) + return false; + + if (node != desc->last) + { + /* Switch the module's node */ + desc->last = node; + desc->byte_size = (((node->conreg >> 8) & 0x1f) + 1 + 7) / 8 - 1; + + /* Keep reserved and start bits cleared. Keep enabled bit. */ + base[CSPI_CONREG_I] = + (node->conreg & ~(0xfcc8e000 | CSPI_CONREG_XCH | CSPI_CONREG_SMC)) + | CSPI_CONREG_EN; + /* Set the wait-states */ + base[CSPI_PERIODREG_I] = node->periodreg & 0xffff; + /* Clear out any spuriously-pending interrupts */ + base[CSPI_STATREG_I] = CSPI_STATREG_TC | CSPI_STATREG_BO; + } + + return true; } -static int spi_transfer(int address, long data, long* buffer, bool read) { - return -1; /* Disable for now - hangs - and we'll use interrupts */ +/* Initialize each of the used SPI descriptors */ +void spi_init(void) +{ + int i; - unsigned long packet = 0; - if(!read) { - /* Set the appropriate bit in the packet to indicate a write */ - packet |= (1<<31); + for (i = 0; i < SPI_NUM_CSPI; i++) + { + struct spi_module_descriptor * const desc = &spi_descs[i]; + mutex_init(&desc->m); + wakeup_init(&desc->w); } - /* Set the address of the packet */ - packet |= (address << 25); - - /* Ensure data only occupies 24 bits, then mash the data into the packet */ - data &= ~(DATAMASK); - packet |= data; - - /* Wait for some room in TXFIFO */ - while(CSPI_STATREG2 & (1<<2)); - - /* Send the packet */ - CSPI_TXDATA2 = packet; - - /* Poll the XCH bit to wait for the end of the transfer, with - * a one second timeout */ - int newtick = current_tick + HZ; - while((CSPI_CONREG2 & (1<<2)) && (current_tick < newtick)); - - if(newtick > current_tick) { - *buffer = CSPI_RXDATA2; - return 0; - } else { - /* Indicate the fact that the transfer timed out */ - return -1; +} + +/* Get mutually-exclusive access to the node */ +void spi_lock(struct spi_node *node) +{ + mutex_lock(&spi_descs[node->num].m); +} + +/* Release mutual exclusion */ +void spi_unlock(struct spi_node *node) +{ + mutex_unlock(&spi_descs[node->num].m); +} + +/* Enable the specified module for the node */ +void spi_enable_module(struct spi_node *node) +{ + struct spi_module_descriptor * const desc = &spi_descs[node->num]; + + mutex_lock(&desc->m); + + if (++desc->enab == 1) + { + /* First enable for this module */ + volatile unsigned long * const base = desc->base; + + /* Enable clock-gating register */ + imx31_clkctl_module_clock_gating(desc->cg, CGM_ON_ALL); + + /* Reset */ + base[CSPI_CONREG_I] &= ~CSPI_CONREG_EN; + base[CSPI_CONREG_I] |= CSPI_CONREG_EN; + base[CSPI_INTREG_I] = 0; + base[CSPI_STATREG_I] = CSPI_STATREG_TC | CSPI_STATREG_BO; + + /* Enable interrupt at controller level */ + avic_enable_int(desc->ints, IRQ, 6, desc->handler); } + + mutex_unlock(&desc->m); } -void spi_send(int address, unsigned long data) { - long dummy; - if(spi_transfer(address, data, &dummy, false)) { - DEBUGF("SPI Send timed out"); +/* Disabled the specified module for the node */ +void spi_disable_module(struct spi_node *node) +{ + struct spi_module_descriptor * const desc = &spi_descs[node->num]; + + mutex_lock(&desc->m); + + if (desc->enab > 0 && --desc->enab == 0) + { + /* Last enable for this module */ + volatile unsigned long * const base = desc->base; + + /* Disable interrupt at controller level */ + avic_disable_int(desc->ints); + + /* Disable interface */ + base[CSPI_CONREG_I] &= ~CSPI_CONREG_EN; + + /* Disable interface clock */ + imx31_clkctl_module_clock_gating(desc->cg, CGM_OFF); } + + mutex_unlock(&desc->m); } -void spi_read(int address, unsigned long* buffer) { - if(spi_transfer(address, 0, buffer, true)) { - DEBUGF("SPI read timed out"); +/* Send and/or receive data on the specified node */ +int spi_transfer(struct spi_node *node, struct spi_transfer *trans) +{ + struct spi_module_descriptor * const desc = &spi_descs[node->num]; + int retval; + + if (trans->count <= 0) + return true; + + mutex_lock(&desc->m); + + retval = spi_set_context(node, desc); + + if (retval) + { + volatile unsigned long * const base = desc->base; + unsigned long intreg; + + desc->trans = trans; + desc->rxcount = trans->count; + + /* Enable needed interrupts - FIFOs will start filling */ + intreg = CSPI_INTREG_THEN; + + intreg |= (trans->count < 4) ? + CSPI_INTREG_RREN : /* Must grab data on every word */ + CSPI_INTREG_RHEN; /* Enough data to wait for half-full */ + + base[CSPI_INTREG_I] = intreg; + + /* Start transfer */ + base[CSPI_CONREG_I] |= CSPI_CONREG_XCH; + + if (wakeup_wait(&desc->w, HZ) != OBJ_WAIT_SUCCEEDED) + { + base[CSPI_INTREG_I] = 0; + base[CSPI_CONREG_I] &= ~CSPI_CONREG_XCH; + retval = false; + } } + + mutex_unlock(&desc->m); + + return retval; } diff --git a/firmware/target/arm/imx31/gigabeat-s/spi-imx31.h b/firmware/target/arm/imx31/gigabeat-s/spi-imx31.h index 5f6cfc984f..c17a272aaa 100644 --- a/firmware/target/arm/imx31/gigabeat-s/spi-imx31.h +++ b/firmware/target/arm/imx31/gigabeat-s/spi-imx31.h @@ -16,8 +16,58 @@ * KIND, either express or implied. * ****************************************************************************/ -#define DATAMASK 0xFF000000 +#ifndef _SPI_IMX31_H_ +#define _SPI_IMX31_H_ +#define USE_CSPI1_MODULE (1 << 0) +#define USE_CSPI2_MODULE (1 << 1) +#define USE_CSPI3_MODULE (1 << 2) + +/* SPI_MODULE_MASK is set in target's config */ +enum spi_module_number +{ +#if (SPI_MODULE_MASK & USE_CSPI1_MODULE) + CSPI1_NUM = 0, +#endif +#if (SPI_MODULE_MASK & USE_CSPI2_MODULE) + CSPI2_NUM, +#endif +#if (SPI_MODULE_MASK & USE_CSPI3_MODULE) + CSPI3_NUM, +#endif + SPI_NUM_CSPI, +}; + +struct spi_node +{ + enum spi_module_number num; /* Module number (CSPIx_NUM) */ + unsigned long conreg; /* CSPI conreg setup */ + unsigned long periodreg; /* CSPI periodreg setup */ +}; + +struct spi_transfer +{ + const void *txbuf; + void *rxbuf; + int count; +}; + +/* One-time init of SPI driver */ void spi_init(void); -void spi_send(int address, unsigned long data); -void spi_read(int address, unsigned long* buffer); + +/* Enable the specified module for the node */ +void spi_enable_module(struct spi_node *node); + +/* Disabled the specified module for the node */ +void spi_disable_module(struct spi_node *node); + +/* Lock module mutex */ +void spi_lock(struct spi_node *node); + +/* Unlock module mutex */ +void spi_unlock(struct spi_node *node); + +/* Send and/or receive data on the specified node */ +int spi_transfer(struct spi_node *node, struct spi_transfer *trans); + +#endif /* _SPI_IMX31_H_ */ diff --git a/firmware/target/arm/imx31/gigabeat-s/system-target.h b/firmware/target/arm/imx31/gigabeat-s/system-target.h index 327d72b7bc..8db0c78eb7 100644 --- a/firmware/target/arm/imx31/gigabeat-s/system-target.h +++ b/firmware/target/arm/imx31/gigabeat-s/system-target.h @@ -35,6 +35,9 @@ static inline void udelay(unsigned int usecs) void system_prepare_fw_start(void); void tick_stop(void); +void kernel_device_init(void); + +#define KDEV_INIT #define HAVE_INVALIDATE_ICACHE static inline void invalidate_icache(void) -- cgit v1.2.3