From 7abf2b53a462612808d46d6d77a7f35261a0e5a3 Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Fri, 9 Apr 2010 01:21:53 +0000 Subject: Gigabeat S/i.MX31: Sort files in the /target tree into things that are SoC-generic (into /imx31) and player-specific (into /gigabeat-s, based upon current appearances). Move i2s clock init into the appropriate file. Housekeeping only-- no functional changes. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@25547 a1c6a512-1295-4272-9138-f99709370657 --- firmware/target/arm/imx31/ata-imx31.c | 685 +++++++++++++++ firmware/target/arm/imx31/ata-target.h | 82 ++ firmware/target/arm/imx31/avic-imx31.c | 212 +++++ firmware/target/arm/imx31/avic-imx31.h | 213 +++++ .../target/arm/imx31/gigabeat-s/adc-gigabeat-s.c | 136 +++ firmware/target/arm/imx31/gigabeat-s/adc-imx31.c | 136 --- firmware/target/arm/imx31/gigabeat-s/ata-imx31.c | 685 --------------- firmware/target/arm/imx31/gigabeat-s/ata-target.h | 82 -- firmware/target/arm/imx31/gigabeat-s/avic-imx31.c | 212 ----- firmware/target/arm/imx31/gigabeat-s/avic-imx31.h | 213 ----- .../arm/imx31/gigabeat-s/backlight-gigabeat-s.c | 214 +++++ .../target/arm/imx31/gigabeat-s/backlight-imx31.c | 214 ----- .../arm/imx31/gigabeat-s/button-gigabeat-s.c | 242 ++++++ .../target/arm/imx31/gigabeat-s/button-imx31.c | 242 ------ firmware/target/arm/imx31/gigabeat-s/gpio-imx31.c | 213 ----- firmware/target/arm/imx31/gigabeat-s/gpio-imx31.h | 114 --- firmware/target/arm/imx31/gigabeat-s/i2c-imx31.c | 338 -------- firmware/target/arm/imx31/gigabeat-s/i2c-imx31.h | 78 -- .../target/arm/imx31/gigabeat-s/i2s-gigabeat-s.c | 45 + firmware/target/arm/imx31/gigabeat-s/i2s-imx31.c | 26 - .../arm/imx31/gigabeat-s/kernel-gigabeat-s.c | 84 ++ .../target/arm/imx31/gigabeat-s/kernel-imx31.c | 84 -- .../target/arm/imx31/gigabeat-s/lcd-gigabeat-s.c | 229 +++++ firmware/target/arm/imx31/gigabeat-s/lcd-imx31.c | 229 ----- .../arm/imx31/gigabeat-s/mc13783-gigabeat-s.c | 2 +- .../target/arm/imx31/gigabeat-s/mc13783-imx31.c | 360 -------- firmware/target/arm/imx31/gigabeat-s/mmu-imx31.c | 33 - firmware/target/arm/imx31/gigabeat-s/mmu-imx31.h | 29 - .../target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c | 555 ++++++++++++ firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c | 555 ------------ .../target/arm/imx31/gigabeat-s/power-gigabeat-s.c | 150 ++++ .../target/arm/imx31/gigabeat-s/power-gigabeat-s.h | 27 + firmware/target/arm/imx31/gigabeat-s/power-imx31.c | 150 ---- firmware/target/arm/imx31/gigabeat-s/power-imx31.h | 27 - .../arm/imx31/gigabeat-s/powermgmt-gigabeat-s.c | 940 +++++++++++++++++++++ .../target/arm/imx31/gigabeat-s/powermgmt-imx31.c | 940 --------------------- .../target/arm/imx31/gigabeat-s/serial-imx31.h | 32 - firmware/target/arm/imx31/gigabeat-s/spi-imx31.c | 352 -------- firmware/target/arm/imx31/gigabeat-s/spi-imx31.h | 89 -- .../arm/imx31/gigabeat-s/system-gigabeat-s.c | 307 +++++++ .../target/arm/imx31/gigabeat-s/system-imx31.c | 307 ------- .../target/arm/imx31/gigabeat-s/timer-gigabeat-s.c | 113 +++ firmware/target/arm/imx31/gigabeat-s/timer-imx31.c | 113 --- .../target/arm/imx31/gigabeat-s/usb-gigabeat-s.c | 135 +++ firmware/target/arm/imx31/gigabeat-s/usb-imx31.c | 135 --- .../arm/imx31/gigabeat-s/wmcodec-gigabeat-s.c | 67 ++ .../target/arm/imx31/gigabeat-s/wmcodec-imx31.c | 83 -- firmware/target/arm/imx31/gpio-imx31.c | 213 +++++ firmware/target/arm/imx31/gpio-imx31.h | 114 +++ firmware/target/arm/imx31/i2c-imx31.c | 338 ++++++++ firmware/target/arm/imx31/i2c-imx31.h | 78 ++ firmware/target/arm/imx31/mc13783-imx31.c | 355 ++++++++ firmware/target/arm/imx31/mmu-imx31.c | 33 + firmware/target/arm/imx31/mmu-imx31.h | 29 + firmware/target/arm/imx31/serial-imx31.h | 32 + firmware/target/arm/imx31/spi-imx31.c | 352 ++++++++ firmware/target/arm/imx31/spi-imx31.h | 89 ++ 57 files changed, 6070 insertions(+), 6072 deletions(-) create mode 100644 firmware/target/arm/imx31/ata-imx31.c create mode 100644 firmware/target/arm/imx31/ata-target.h create mode 100644 firmware/target/arm/imx31/avic-imx31.c create mode 100644 firmware/target/arm/imx31/avic-imx31.h create mode 100644 firmware/target/arm/imx31/gigabeat-s/adc-gigabeat-s.c delete mode 100644 firmware/target/arm/imx31/gigabeat-s/adc-imx31.c delete mode 100644 firmware/target/arm/imx31/gigabeat-s/ata-imx31.c delete mode 100644 firmware/target/arm/imx31/gigabeat-s/ata-target.h delete mode 100644 firmware/target/arm/imx31/gigabeat-s/avic-imx31.c delete mode 100644 firmware/target/arm/imx31/gigabeat-s/avic-imx31.h create mode 100644 firmware/target/arm/imx31/gigabeat-s/backlight-gigabeat-s.c delete mode 100644 firmware/target/arm/imx31/gigabeat-s/backlight-imx31.c create mode 100644 firmware/target/arm/imx31/gigabeat-s/button-gigabeat-s.c delete mode 100644 firmware/target/arm/imx31/gigabeat-s/button-imx31.c delete mode 100644 firmware/target/arm/imx31/gigabeat-s/gpio-imx31.c delete mode 100644 firmware/target/arm/imx31/gigabeat-s/gpio-imx31.h delete mode 100644 firmware/target/arm/imx31/gigabeat-s/i2c-imx31.c delete mode 100644 firmware/target/arm/imx31/gigabeat-s/i2c-imx31.h create mode 100644 firmware/target/arm/imx31/gigabeat-s/i2s-gigabeat-s.c delete mode 100644 firmware/target/arm/imx31/gigabeat-s/i2s-imx31.c create mode 100644 firmware/target/arm/imx31/gigabeat-s/kernel-gigabeat-s.c delete mode 100644 firmware/target/arm/imx31/gigabeat-s/kernel-imx31.c create mode 100644 firmware/target/arm/imx31/gigabeat-s/lcd-gigabeat-s.c delete mode 100644 firmware/target/arm/imx31/gigabeat-s/lcd-imx31.c delete mode 100644 firmware/target/arm/imx31/gigabeat-s/mc13783-imx31.c delete mode 100644 firmware/target/arm/imx31/gigabeat-s/mmu-imx31.c delete mode 100644 firmware/target/arm/imx31/gigabeat-s/mmu-imx31.h create mode 100644 firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c delete mode 100644 firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c create mode 100644 firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.c create mode 100644 firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.h delete mode 100644 firmware/target/arm/imx31/gigabeat-s/power-imx31.c delete mode 100644 firmware/target/arm/imx31/gigabeat-s/power-imx31.h create mode 100644 firmware/target/arm/imx31/gigabeat-s/powermgmt-gigabeat-s.c delete mode 100644 firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c delete mode 100644 firmware/target/arm/imx31/gigabeat-s/serial-imx31.h delete mode 100644 firmware/target/arm/imx31/gigabeat-s/spi-imx31.c delete mode 100644 firmware/target/arm/imx31/gigabeat-s/spi-imx31.h create mode 100644 firmware/target/arm/imx31/gigabeat-s/system-gigabeat-s.c delete mode 100644 firmware/target/arm/imx31/gigabeat-s/system-imx31.c create mode 100644 firmware/target/arm/imx31/gigabeat-s/timer-gigabeat-s.c delete mode 100644 firmware/target/arm/imx31/gigabeat-s/timer-imx31.c create mode 100644 firmware/target/arm/imx31/gigabeat-s/usb-gigabeat-s.c delete mode 100644 firmware/target/arm/imx31/gigabeat-s/usb-imx31.c create mode 100644 firmware/target/arm/imx31/gigabeat-s/wmcodec-gigabeat-s.c delete mode 100644 firmware/target/arm/imx31/gigabeat-s/wmcodec-imx31.c create mode 100644 firmware/target/arm/imx31/gpio-imx31.c create mode 100644 firmware/target/arm/imx31/gpio-imx31.h create mode 100644 firmware/target/arm/imx31/i2c-imx31.c create mode 100644 firmware/target/arm/imx31/i2c-imx31.h create mode 100644 firmware/target/arm/imx31/mc13783-imx31.c create mode 100644 firmware/target/arm/imx31/mmu-imx31.c create mode 100644 firmware/target/arm/imx31/mmu-imx31.h create mode 100644 firmware/target/arm/imx31/serial-imx31.h create mode 100644 firmware/target/arm/imx31/spi-imx31.c create mode 100644 firmware/target/arm/imx31/spi-imx31.h (limited to 'firmware/target/arm') diff --git a/firmware/target/arm/imx31/ata-imx31.c b/firmware/target/arm/imx31/ata-imx31.c new file mode 100644 index 0000000000..5ce7ad0a03 --- /dev/null +++ b/firmware/target/arm/imx31/ata-imx31.c @@ -0,0 +1,685 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 by Will Robertson + * + * 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 "cpu.h" +#include "kernel.h" +#include "thread.h" +#include "system.h" +#include "power.h" +#include "panic.h" +#include "ata.h" +#include "ata-target.h" +#include "ccm-imx31.h" +#ifdef HAVE_ATA_DMA +#include "sdma-imx31.h" +#include "mmu-imx31.h" +#endif + +/* PIO modes timing info */ +static const struct ata_pio_timings +{ + uint16_t time_2w; /* t2 during write */ + uint16_t time_2r; /* t2 during read */ + uint8_t time_ax; /* tA */ + uint8_t time_1; /* t1 */ + uint8_t time_4; /* t4 */ + uint8_t time_9; /* t9 */ +} pio_timings[5] = +{ + [0] = /* PIO mode 0 */ + { + .time_1 = 70, + .time_2w = 290, + .time_2r = 290, + .time_ax = 35, + .time_4 = 30, + .time_9 = 20 + }, + [1] = /* PIO mode 1 */ + { + .time_1 = 50, + .time_2w = 290, + .time_2r = 290, + .time_ax = 35, + .time_4 = 20, + .time_9 = 15 + }, + [2] = /* PIO mode 2 */ + { + .time_1 = 30, + .time_2w = 290, + .time_2r = 290, + .time_ax = 35, + .time_4 = 15, + .time_9 = 10 + }, + [3] = /* PIO mode 3 */ + { + .time_1 = 30, + .time_2w = 80, + .time_2r = 80, + .time_ax = 35, + .time_4 = 10, + .time_9 = 10 + }, + [4] = /* PIO mode 4 */ + { + .time_1 = 25, + .time_2w = 70, + .time_2r = 70, + .time_ax = 35, + .time_4 = 10, + .time_9 = 10 + } +}; + +/* Track first init */ +static bool initialized = false; + +#ifdef HAVE_ATA_DMA +/* One DMA channel for reads, the other for writes othewise one channel would + * have to be reinitialized every time the direction changed. (Different + * SDMA scripts are used for reading or writing) */ +#define ATA_DMA_CH_NUM_RD 3 +#define ATA_DMA_CH_NUM_WR 4 +/* Use default priority for these channels (1) - ATA isn't realtime urgent. */ +/* Maximum DMA size per buffer descriptor (32-byte aligned) */ +#define ATA_MAX_BD_SIZE (65534 & ~31) /* 65504 */ + +/* Number of buffer descriptors required for a maximum sector count trasfer. + * NOTE: Assumes LBA28 and 512-byte sectors! */ +#define ATA_BASE_BD_COUNT ((256*512 + (ATA_MAX_BD_SIZE-1)) / ATA_MAX_BD_SIZE) +#define ATA_BD_COUNT (ATA_BASE_BD_COUNT + 2) + +static const struct ata_mdma_timings +{ + uint8_t time_m; /* tM */ + uint8_t time_jn; /* tH */ + uint8_t time_d; /* tD */ + uint8_t time_k; /* tKW */ +} mdma_timings[] = +{ + [0] = /* MDMA mode 0 */ + { + .time_m = 50, + .time_jn = 20, + .time_d = 215, + .time_k = 215 + }, + [1] = /* MDMA mode 1 */ + { + .time_m = 30, + .time_jn = 15, + .time_d = 80, + .time_k = 50 + }, + [2] = /* MDMA mode 2 */ + { + .time_m = 25, + .time_jn = 10, + .time_d = 70, + .time_k = 25 + } +}; + +static const struct ata_udma_timings +{ + uint8_t time_ack; /* tACK */ + uint8_t time_env; /* tENV */ + uint8_t time_rpx; /* tRP */ + uint8_t time_zah; /* tZAH */ + uint8_t time_mlix; /* tMLI */ + uint8_t time_dvh; /* tDVH */ + uint8_t time_dzfs; /* tDVS+tDVH? */ + uint8_t time_dvs; /* tDVS */ + uint8_t time_cvh; /* ?? */ + uint8_t time_ss; /* tSS */ + uint8_t time_cyc; /* tCYC */ +} udma_timings[] = +{ + [0] = /* UDMA mode 0 */ + { + .time_ack = 20, + .time_env = 20, + .time_rpx = 160, + .time_zah = 20, + .time_mlix = 20, + .time_dvh = 6, + .time_dzfs = 80, + .time_dvs = 70, + .time_cvh = 6, + .time_ss = 50, + .time_cyc = 114 + }, + [1] = /* UDMA mode 1 */ + { + .time_ack = 20, + .time_env = 20, + .time_rpx = 125, + .time_zah = 20, + .time_mlix = 20, + .time_dvh = 6, + .time_dzfs = 63, + .time_dvs = 48, + .time_cvh = 6, + .time_ss = 50, + .time_cyc = 75 + }, + [2] = /* UDMA mode 2 */ + { + .time_ack = 20, + .time_env = 20, + .time_rpx = 100, + .time_zah = 20, + .time_mlix = 20, + .time_dvh = 6, + .time_dzfs = 47, + .time_dvs = 34, + .time_cvh = 6, + .time_ss = 50, + .time_cyc = 55 + }, + [3] = /* UDMA mode 3 */ + { + .time_ack = 20, + .time_env = 20, + .time_rpx = 100, + .time_zah = 20, + .time_mlix = 20, + .time_dvh = 6, + .time_dzfs = 35, + .time_dvs = 20, + .time_cvh = 6, + .time_ss = 50, + .time_cyc = 39 + }, + [4] = /* UDMA mode 4 */ + { + .time_ack = 20, + .time_env = 20, + .time_rpx = 100, + .time_zah = 20, + .time_mlix = 20, + .time_dvh = 6, + .time_dzfs = 25, + .time_dvs = 7, + .time_cvh = 6, + .time_ss = 50, + .time_cyc = 25 + }, +#if 0 + [5] = /* UDMA mode 5 (bus clock 80MHz or higher only) */ + { + .time_ack = 20, + .time_env = 20, + .time_rpx = 85, + .time_zah = 20, + .time_mlix = 20, + .time_dvh = 6, + .time_dzfs = 40, + .time_dvs = 5, + .time_cvh = 10, + .time_ss = 50, + .time_cyc = 17 + } +#endif +}; + +/** Threading **/ +/* Signal to tell thread when DMA is done */ +static struct wakeup ata_dma_wakeup; + +/** SDMA **/ +/* Array of buffer descriptors for large transfers and alignnment */ +static struct buffer_descriptor ata_bda[ATA_BD_COUNT] DEVBSS_ATTR; +/* ATA channel descriptors */ +static struct channel_descriptor ata_cd_rd DEVBSS_ATTR; /* read channel */ +static struct channel_descriptor ata_cd_wr DEVBSS_ATTR; /* write channel */ +/* DMA channel to be started for transfer */ +static unsigned int current_channel = 0; + +/** Buffers **/ +/* Scatter buffer for first and last 32 bytes of a non cache-aligned transfer + * to cached RAM. */ +static uint32_t scatter_buffer[32/4*2] DEVBSS_ATTR; +/* Address of ends in destination buffer for unaligned reads - copied after + * DMA completes. */ +static void *sb_dst[2] = { NULL, NULL }; + +/** Modes **/ +#define ATA_DMA_MWDMA 0x00000000 /* Using multiword DMA */ +#define ATA_DMA_UDMA ATA_DMA_ULTRA_SELECTED /* Using Ultra DMA */ +#define ATA_DMA_PIO 0x80000000 /* Using PIO */ +#define ATA_DMA_DISABLED 0x80000001 /* DMA init error - use PIO */ +static unsigned long ata_dma_selected = ATA_DMA_PIO; +#endif /* HAVE_ATA_DMA */ + +static unsigned int get_T(void) +{ + /* T = ATA clock period in nanoseconds */ + return 1000 * 1000 * 1000 / ccm_get_ata_clk(); +} + +static void ata_wait_for_idle(void) +{ + while (!(ATA_INTERRUPT_PENDING & ATA_CONTROLLER_IDLE)); +} + +/* Route the INTRQ to either the MCU or SDMA depending upon whether there is + * a DMA transfer in progress. */ +static inline void ata_set_intrq(bool to_dma) +{ + ATA_INTERRUPT_ENABLE = + (ATA_INTERRUPT_ENABLE & ~(ATA_INTRQ1 | ATA_INTRQ2)) | + (to_dma ? ATA_INTRQ1 : ATA_INTRQ2); +} + +/* Setup the timing for PIO mode */ +void ata_set_pio_timings(int mode) +{ + const struct ata_pio_timings * const timings = &pio_timings[mode]; + unsigned int T = get_T(); + + ata_wait_for_idle(); + + ATA_TIME_1 = (timings->time_1 + T) / T; + ATA_TIME_2W = (timings->time_2w + T) / T; + ATA_TIME_2R = (timings->time_2r + T) / T; + ATA_TIME_AX = (timings->time_ax + T) / T + 2; /* 1.5 + tAX */ + ATA_TIME_PIO_RDX = 1; + ATA_TIME_4 = (timings->time_4 + T) / T; + ATA_TIME_9 = (timings->time_9 + T) / T; +} + +void ata_reset(void) +{ + /* Be sure we're not busy */ + ata_wait_for_idle(); + + ATA_INTF_CONTROL &= ~(ATA_ATA_RST | ATA_FIFO_RST); + sleep(HZ/100); + ATA_INTF_CONTROL = ATA_ATA_RST | ATA_FIFO_RST; + sleep(HZ/100); + + ata_wait_for_idle(); +} + +void ata_enable(bool on) +{ + /* Unconditionally clock module before writing regs */ + ccm_module_clock_gating(CG_ATA, CGM_ON_RUN_WAIT); + ata_wait_for_idle(); + + if (on) + { + ATA_INTF_CONTROL = ATA_ATA_RST | ATA_FIFO_RST; + sleep(HZ/100); + } + else + { + ATA_INTF_CONTROL &= ~(ATA_ATA_RST | ATA_FIFO_RST); + sleep(HZ/100); + + /* Disable off - unclock ATA module */ + ccm_module_clock_gating(CG_ATA, CGM_OFF); + } +} + +bool ata_is_coldstart(void) +{ + return true; +} + +#ifdef HAVE_ATA_DMA +static void ata_set_mdma_timings(unsigned int mode) +{ + const struct ata_mdma_timings * const timings = &mdma_timings[mode]; + unsigned int T = get_T(); + + ATA_TIME_M = (timings->time_m + T) / T; + ATA_TIME_JN = (timings->time_jn + T) / T; + ATA_TIME_D = (timings->time_d + T) / T; + ATA_TIME_K = (timings->time_k + T) / T; +} + +static void ata_set_udma_timings(unsigned int mode) +{ + const struct ata_udma_timings * const timings = &udma_timings[mode]; + unsigned int T = get_T(); + + ATA_TIME_ACK = (timings->time_ack + T) / T; + ATA_TIME_ENV = (timings->time_env + T) / T; + ATA_TIME_RPX = (timings->time_rpx + T) / T; + ATA_TIME_ZAH = (timings->time_zah + T) / T; + ATA_TIME_MLIX = (timings->time_mlix + T) / T; + ATA_TIME_DVH = (timings->time_dvh + T) / T + 1; + ATA_TIME_DZFS = (timings->time_dzfs + T) / T; + ATA_TIME_DVS = (timings->time_dvs + T) / T; + ATA_TIME_CVH = (timings->time_cvh + T) / T; + ATA_TIME_SS = (timings->time_ss + T) / T; + ATA_TIME_CYC = (timings->time_cyc + T) / T; +} + +void ata_dma_set_mode(unsigned char mode) +{ + unsigned int modeidx = mode & 0x07; + unsigned int dmamode = mode & 0xf8; + + ata_wait_for_idle(); + + if (ata_dma_selected == ATA_DMA_DISABLED) + { + /* Configuration error - no DMA */ + } + else if (dmamode == 0x40 && modeidx <= ATA_MAX_UDMA) + { + /* Using Ultra DMA */ + ata_set_udma_timings(dmamode); + ata_dma_selected = ATA_DMA_UDMA; + } + else if (dmamode == 0x20 && modeidx <= ATA_MAX_MWDMA) + { + /* Using Multiword DMA */ + ata_set_mdma_timings(dmamode); + ata_dma_selected = ATA_DMA_MWDMA; + } + else + { + /* Don't understand this - force PIO. */ + ata_dma_selected = ATA_DMA_PIO; + } +} + +/* Called by SDMA when transfer is complete */ +static void ata_dma_callback(void) +{ + /* Clear FIFO if not empty - shouldn't happen */ + while (ATA_FIFO_FILL != 0) + ATA_FIFO_DATA_32; + + /* Clear FIFO interrupts (the only ones that can be) */ + ATA_INTERRUPT_CLEAR = ATA_INTERRUPT_PENDING; + + ata_set_intrq(false); /* Return INTRQ to MCU */ + wakeup_signal(&ata_dma_wakeup); /* Signal waiting thread */ +} + +bool ata_dma_setup(void *addr, unsigned long bytes, bool write) +{ + struct buffer_descriptor *bd_p; + unsigned char *buf; + + if (UNLIKELY(bytes > ATA_BASE_BD_COUNT*ATA_MAX_BD_SIZE || + (ata_dma_selected & ATA_DMA_PIO))) + { + /* Too much? Implies BD count should be reevaluated since this + * shouldn't be reached based upon size. Otherwise we simply didn't + * understand the DMA mode setup. Force PIO in both cases. */ + ATA_INTF_CONTROL = ATA_FIFO_RST | ATA_ATA_RST; + return false; + } + + bd_p = &ata_bda[0]; + buf = (unsigned char *)addr_virt_to_phys((unsigned long)addr); + sb_dst[0] = NULL; /* Assume not needed */ + + if (write) + { + /* No cache alignment concerns */ + current_channel = ATA_DMA_CH_NUM_WR; + + if (LIKELY(buf != addr)) + { + /* addr is virtual */ + clean_dcache_range(addr, bytes); + } + + /* Setup ATA controller for DMA transmit */ + ATA_INTF_CONTROL = ATA_FIFO_RST | ATA_ATA_RST | ATA_FIFO_TX_EN | + ATA_DMA_PENDING | ata_dma_selected | ATA_DMA_WRITE; + ATA_FIFO_ALARM = SDMA_ATA_WML / 2; + } + else + { + current_channel = ATA_DMA_CH_NUM_RD; + + /* Setup ATA controller for DMA receive */ + ATA_INTF_CONTROL = ATA_FIFO_RST | ATA_ATA_RST | ATA_FIFO_RCV_EN | + ATA_DMA_PENDING | ata_dma_selected; + ATA_FIFO_ALARM = SDMA_ATA_WML / 2; + + if (LIKELY(buf != addr)) + { + /* addr is virtual */ + dump_dcache_range(addr, bytes); + + if ((unsigned long)addr & 31) + { + /* Not cache aligned, must use scatter buffers for first and + * last 32 bytes. */ + unsigned char *bufstart = buf; + + sb_dst[0] = addr; + bd_p->buf_addr = scatter_buffer; + bd_p->mode.count = 32; + bd_p->mode.status = BD_DONE | BD_CONT; + + buf += 32; + bytes -= 32; + bd_p++; + + while (bytes > ATA_MAX_BD_SIZE) + { + bd_p->buf_addr = buf; + bd_p->mode.count = ATA_MAX_BD_SIZE; + bd_p->mode.status = BD_DONE | BD_CONT; + buf += ATA_MAX_BD_SIZE; + bytes -= ATA_MAX_BD_SIZE; + bd_p++; + } + + if (bytes > 32) + { + unsigned long size = bytes - 32; + bd_p->buf_addr = buf; + bd_p->mode.count = size; + bd_p->mode.status = BD_DONE | BD_CONT; + buf += size; + bd_p++; + } + + /* There will be exactly 32 bytes left */ + + /* Final buffer - wrap to base bd, interrupt */ + sb_dst[1] = addr + (buf - bufstart); + bd_p->buf_addr = &scatter_buffer[32/4]; + bd_p->mode.count = 32; + bd_p->mode.status = BD_DONE | BD_WRAP | BD_INTR; + + return true; + } + } + } + + /* Setup buffer descriptors for both cache-aligned reads and all write + * operations. */ + while (bytes > ATA_MAX_BD_SIZE) + { + bd_p->buf_addr = buf; + bd_p->mode.count = ATA_MAX_BD_SIZE; + bd_p->mode.status = BD_DONE | BD_CONT; + buf += ATA_MAX_BD_SIZE; + bytes -= ATA_MAX_BD_SIZE; + bd_p++; + } + + /* Final buffer - wrap to base bd, interrupt */ + bd_p->buf_addr = buf; + bd_p->mode.count = bytes; + bd_p->mode.status = BD_DONE | BD_WRAP | BD_INTR; + + return true; +} + +bool ata_dma_finish(void) +{ + unsigned int channel = current_channel; + long timeout = current_tick + HZ*10; + + current_channel = 0; + + ata_set_intrq(true); /* Give INTRQ to DMA */ + sdma_channel_run(channel); /* Kick the channel to wait for events */ + + while (1) + { + int oldirq; + + if (LIKELY(wakeup_wait(&ata_dma_wakeup, HZ/2) == OBJ_WAIT_SUCCEEDED)) + break; + + ata_keep_active(); + + if (TIME_BEFORE(current_tick, timeout)) + continue; + + /* Epic fail - timed out - maybe. */ + oldirq = disable_irq_save(); + ata_set_intrq(false); /* Strip INTRQ from DMA */ + sdma_channel_stop(channel); /* Stop DMA */ + restore_irq(oldirq); + + if (wakeup_wait(&ata_dma_wakeup, TIMEOUT_NOBLOCK) == OBJ_WAIT_SUCCEEDED) + break; /* DMA really did finish after timeout */ + + sdma_channel_reset(channel); /* Reset everything + clear error */ + return false; + } + + if (sdma_channel_is_error(channel)) + { + /* Channel error in one or more descriptors */ + sdma_channel_reset(channel); /* Reset everything + clear error */ + return false; + } + + if (sb_dst[0] != NULL) + { + /* NOTE: This requires that unaligned access support be enabled! */ + register void *sbs = scatter_buffer; + register void *sbd0 = sb_dst[0]; + register void *sbd1 = sb_dst[1]; + asm volatile( + "add r0, %1, #32 \n" /* Prefetch at DMA-direct boundaries */ + "mcrr p15, 2, r0, r0, c12 \n" + "mcrr p15, 2, %2, %2, c12 \n" + "ldmia %0!, { r0-r3 } \n" /* Copy the 32-bytes to destination */ + "str r0, [%1], #4 \n" /* stmia doesn't work unaligned */ + "str r1, [%1], #4 \n" + "str r2, [%1], #4 \n" + "str r3, [%1], #4 \n" + "ldmia %0!, { r0-r3 } \n" + "str r0, [%1], #4 \n" + "str r1, [%1], #4 \n" + "str r2, [%1], #4 \n" + "str r3, [%1] \n" + "ldmia %0!, { r0-r3 } \n" /* Copy the 32-bytes to destination */ + "str r0, [%2], #4 \n" /* stmia doesn't work unaligned */ + "str r1, [%2], #4 \n" + "str r2, [%2], #4 \n" + "str r3, [%2], #4 \n" + "ldmia %0!, { r0-r3 } \n" + "str r0, [%2], #4 \n" + "str r1, [%2], #4 \n" + "str r2, [%2], #4 \n" + "str r3, [%2] \n" + : "+r"(sbs), "+r"(sbd0), "+r"(sbd1) + : + : "r0", "r1", "r2", "r3"); + } + + return true; +} +#endif /* HAVE_ATA_DMA */ + +void ata_device_init(void) +{ + /* Make sure we're not in reset mode */ + ata_enable(true); + + if (!initialized) + { + ATA_INTERRUPT_ENABLE = 0; + ATA_INTERRUPT_CLEAR = ATA_INTERRUPT_PENDING; + } + + ata_set_intrq(false); + + if (initialized) + return; + + /* All modes use same tOFF/tON */ + ATA_TIME_OFF = 3; + ATA_TIME_ON = 3; + + /* Setup mode 0 for all by default + * Mode may be switched later once identify info is ready in which + * case the main driver calls back */ + ata_set_pio_timings(0); + +#ifdef HAVE_ATA_DMA + ata_set_mdma_timings(0); + ata_set_udma_timings(0); + + ata_dma_selected = ATA_DMA_PIO; + + /* Called for first time at startup */ + wakeup_init(&ata_dma_wakeup); + + /* Read/write channels share buffer descriptors */ + ata_cd_rd.bd_count = ATA_BD_COUNT; + ata_cd_rd.callback = ata_dma_callback; + ata_cd_rd.shp_addr = SDMA_PER_ADDR_ATA_RX; + ata_cd_rd.wml = SDMA_ATA_WML; + ata_cd_rd.per_type = SDMA_PER_ATA; + ata_cd_rd.tran_type = SDMA_TRAN_PER_2_EMI; + ata_cd_rd.event_id1 = SDMA_REQ_ATA_TXFER_END; + ata_cd_rd.event_id2 = SDMA_REQ_ATA_RX; + + ata_cd_wr.bd_count = ATA_BD_COUNT; + ata_cd_wr.callback = ata_dma_callback; + ata_cd_wr.shp_addr = SDMA_PER_ADDR_ATA_TX; + ata_cd_wr.wml = SDMA_ATA_WML; + ata_cd_wr.per_type = SDMA_PER_ATA; + ata_cd_wr.tran_type = SDMA_TRAN_EMI_2_PER; + ata_cd_wr.event_id1 = SDMA_REQ_ATA_TXFER_END; + ata_cd_wr.event_id2 = SDMA_REQ_ATA_TX; + + if (!sdma_channel_init(ATA_DMA_CH_NUM_RD, &ata_cd_rd, ata_bda) || + !sdma_channel_init(ATA_DMA_CH_NUM_WR, &ata_cd_wr, ata_bda)) + { + /* Channel init error - disable DMA forever */ + ata_dma_selected = ATA_DMA_DISABLED; + } +#endif /* HAVE_ATA_DMA */ + + initialized = true; +} diff --git a/firmware/target/arm/imx31/ata-target.h b/firmware/target/arm/imx31/ata-target.h new file mode 100644 index 0000000000..f7f84f82e9 --- /dev/null +++ b/firmware/target/arm/imx31/ata-target.h @@ -0,0 +1,82 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006 by Linus Nielsen Feltzing + * + * 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. + * + ****************************************************************************/ +#ifndef ATA_TARGET_H +#define ATA_TARGET_H + +#ifdef BOOTLOADER +#define ATA_DRIVER_CLOSE +#endif + +/* Plain C read & write loops */ +/* They likely won't be used anyway since DMA potentially works for any + * sector number and alignment. */ +#define PREFER_C_READING +#define PREFER_C_WRITING + +#ifdef HAVE_ATA_DMA +#define ATA_MAX_MWDMA 2 +#define ATA_MAX_UDMA 4 +#endif + +#define ATA_DATA ATA_DRIVE_DATA +#define ATA_ERROR ATA_DRIVE_FEATURES +#define ATA_NSECTOR ATA_DRIVE_SECTOR_COUNT +#define ATA_SECTOR ATA_DRIVE_SECTOR_NUM +#define ATA_LCYL ATA_DRIVE_CYL_LOW +#define ATA_HCYL ATA_DRIVE_CYL_HIGH +#define ATA_SELECT ATA_DRIVE_CYL_HEAD +#define ATA_COMMAND ATA_DRIVE_COMMAND +#define ATA_CONTROL ATA_DRIVE_CONTROL + +#define STATUS_BSY 0x80 +#define STATUS_RDY 0x40 +#define STATUS_DF 0x20 +#define STATUS_DRQ 0x08 +#define STATUS_ERR 0x01 +#define ERROR_ABRT 0x04 +#define ERROR_IDNF 0x10 + +#define WRITE_PATTERN1 0xa5 +#define WRITE_PATTERN2 0x5a +#define WRITE_PATTERN3 0xaa +#define WRITE_PATTERN4 0x55 + +#define READ_PATTERN1 0xa5 +#define READ_PATTERN2 0x5a +#define READ_PATTERN3 0xaa +#define READ_PATTERN4 0x55 + +#define READ_PATTERN1_MASK 0xff +#define READ_PATTERN2_MASK 0xff +#define READ_PATTERN3_MASK 0xff +#define READ_PATTERN4_MASK 0xff + +#define SET_REG(reg,val) reg = (val) +#define SET_16BITREG(reg,val) reg = (val) + +void ata_reset(void); +void ata_device_init(void); +bool ata_is_coldstart(void); + +#define ATA_SET_DEVICE_FEATURES +void ata_set_pio_timings(int mode); + +#endif /* ATA_TARGET_H */ diff --git a/firmware/target/arm/imx31/avic-imx31.c b/firmware/target/arm/imx31/avic-imx31.c new file mode 100644 index 0000000000..4ba7da4be0 --- /dev/null +++ b/firmware/target/arm/imx31/avic-imx31.c @@ -0,0 +1,212 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 by James Espinoza + * + * 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 +#include "system.h" +#include "imx31l.h" +#include "avic-imx31.h" +#include "panic.h" +#include "debug.h" + +static const char * avic_int_names[64] = +{ + "RESERVED0", "RESERVED1", "RESERVED2", "I2C3", + "I2C2", "MPEG4_ENCODER", "RTIC", "FIR", + "MMC/SDHC2", "MMC/SDHC1", "I2C1", "SSI2", + "SSI1", "CSPI2", "CSPI1", "ATA", + "MBX", "CSPI3", "UART3", "IIM", + "SIM1", "SIM2", "RNGA", "EVTMON", + "KPP", "RTC", "PWN", "EPIT2", + "EPIT1", "GPT", "PWR_FAIL", "CCM_DVFS", + "UART2", "NANDFC", "SDMA", "USB_HOST1", + "USB_HOST2", "USB_OTG", "RESERVED3", "MSHC1", + "MSHC2", "IPU_ERR", "IPU", "RESERVED4", + "RESERVED5", "UART1", "UART4", "UART5", + "ETC_IRQ", "SCC_SCM", "SCC_SMN", "GPIO2", + "GPIO1", "CCM_CLK", "PCMCIA", "WDOG", + "GPIO3", "RESERVED6", "EXT_PWMG", "EXT_TEMP", + "EXT_SENS1", "EXT_SENS2", "EXT_WDOG", "EXT_TV" +}; + +void UIE_VECTOR(void) +{ + int mode; + int offset; + + asm volatile ( + "mrs %0, cpsr \n" /* Mask core IRQ/FIQ */ + "orr %0, %0, #0xc0 \n" + "msr cpsr_c, %0 \n" + "and %0, %0, #0x1f \n" /* Get mode bits */ + : "=&r"(mode) + ); + + offset = mode == 0x11 ? + (int32_t)AVIC_FIVECSR : ((int32_t)AVIC_NIVECSR >> 16); + + panicf("Unhandled %s %d: %s", + mode == 0x11 ? "FIQ" : "IRQ", offset, + offset >= 0 ? avic_int_names[offset] : ""); +} + +/* We use the AVIC */ +void __attribute__((interrupt("IRQ"))) irq_handler(void) +{ + const int offset = (int32_t)AVIC_NIVECSR >> 16; + + if (offset == -1) + { + /* This is called occasionally for some unknown reason even with the + * avic enabled but returning normally appears to cause no harm. The + * KPP and ATA seem to have a part in it (common but multiplexed pins + * that can interfere). It will be investigated more thoroughly but + * for now it is simply an occasional irritant. */ + return; + } + + disable_interrupt(IRQ_FIQ_STATUS); + panicf("Unhandled IRQ %d in irq_handler: %s", offset, + offset >= 0 ? avic_int_names[offset] : ""); +} + +/* Accoring to section 9.3.5 of the UM, the AVIC doesn't accelerate + * fast interrupts and they must be dispatched */ +void __attribute__((naked)) fiq_handler(void) +{ + asm volatile ( + "mov r10, #0x68000000 \n" /* load AVIC base address */ + "ldr r9, [r10, #0x44] \n" /* read FIVECSR of AVIC */ + "add r10, r10, #0x100 \n" /* move pointer to base of VECTOR table */ + "ldr r8, [r10, r9, lsl #2] \n" /* read FIQ vector from VECTOR table */ + "bx r8 \n" /* jump to FIQ service routine */ + ); +} + +void avic_init(void) +{ + struct avic_map * const avic = (struct avic_map *)AVIC_BASE_ADDR; + int i; + + /* Disable all interrupts and set to unhandled */ + avic_disable_int(INT_ALL); + + /* Reset AVIC control */ + avic->intcntl = 0; + + /* Init all interrupts to type IRQ */ + avic_set_int_type(INT_ALL, INT_TYPE_IRQ); + + /* Set all normal to lowest priority */ + for (i = 0; i < 8; i++) + avic->nipriority[i] = 0; + + /* Set NM bit to enable VIC */ + avic->intcntl |= AVIC_INTCNTL_NM; + + /* Enable VE bit in CP15 Control reg to enable VIC */ + asm volatile ( + "mrc p15, 0, r0, c1, c0, 0 \n" + "orr r0, r0, #(1 << 24) \n" + "mcr p15, 0, r0, c1, c0, 0 \n" + : : : "r0"); + + /* Enable normal interrupts at all priorities */ + avic->nimask = 0x1f; +} + +void avic_set_int_priority(enum IMX31_INT_LIST ints, + unsigned long ni_priority) +{ + struct avic_map * const avic = (struct avic_map *)AVIC_BASE_ADDR; + volatile uint32_t *reg = &avic->nipriority[7 - (ints >> 3)]; + unsigned int shift = (ints & 0x7) << 2; + uint32_t mask = 0xful << shift; + *reg = (*reg & ~mask) | ((ni_priority << shift) & mask); +} + +void avic_enable_int(enum IMX31_INT_LIST ints, enum INT_TYPE intstype, + unsigned long ni_priority, void (*handler)(void)) +{ + struct avic_map * const avic = (struct avic_map *)AVIC_BASE_ADDR; + int oldstatus = disable_interrupt_save(IRQ_FIQ_STATUS); + + if (ints != INT_ALL) /* No mass-enable allowed */ + { + avic_set_int_type(ints, intstype); + avic->vector[ints] = (long)handler; + avic->intennum = ints; + avic_set_int_priority(ints, ni_priority); + } + + restore_interrupt(oldstatus); +} + +void avic_disable_int(enum IMX31_INT_LIST ints) +{ + struct avic_map * const avic = (struct avic_map *)AVIC_BASE_ADDR; + uint32_t i; + + if (ints == INT_ALL) + { + for (i = 0; i < 64; i++) + { + avic->intdisnum = i; + avic->vector[i] = (long)UIE_VECTOR; + } + } + else + { + avic->intdisnum = ints; + avic->vector[ints] = (long)UIE_VECTOR; + } +} + +static void set_int_type(int i, enum INT_TYPE intstype) +{ + /* INTTYPEH: vectors 63-32, INTTYPEL: vectors 31-0 */ + struct avic_map * const avic = (struct avic_map *)AVIC_BASE_ADDR; + volatile uint32_t *reg = &avic->inttype[1 - (i >> 5)]; + uint32_t val = 1L << (i & 0x1f); + + if (intstype == INT_TYPE_IRQ) + val = *reg & ~val; + else + val = *reg | val; + + *reg = val; +} + +void avic_set_int_type(enum IMX31_INT_LIST ints, enum INT_TYPE intstype) +{ + int oldstatus = disable_interrupt_save(IRQ_FIQ_STATUS); + + if (ints == INT_ALL) + { + int i; + for (i = 0; i < 64; i++) + set_int_type(i, intstype); + } + else + { + set_int_type(ints, intstype); + } + + restore_interrupt(oldstatus); +} diff --git a/firmware/target/arm/imx31/avic-imx31.h b/firmware/target/arm/imx31/avic-imx31.h new file mode 100644 index 0000000000..a049713600 --- /dev/null +++ b/firmware/target/arm/imx31/avic-imx31.h @@ -0,0 +1,213 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 by James Espinoza + * + * 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. + * + ****************************************************************************/ +#ifndef AVIC_IMX31_H +#define AVIC_IMX31_H + +struct avic_map +{ + volatile uint32_t intcntl; /* 00h */ + volatile uint32_t nimask; /* 04h */ + volatile uint32_t intennum; /* 08h */ + volatile uint32_t intdisnum; /* 0Ch */ + union /* 10h */ + { + struct + { + volatile uint32_t intenableh; /* 10h */ + volatile uint32_t intenablel; /* 14h */ + }; + volatile uint32_t intenable[2]; /* H,L */ + }; + union + { + struct + { + volatile uint32_t inttypeh; /* 18h */ + volatile uint32_t inttypel; /* 1Ch */ + }; + volatile uint32_t inttype[2]; /* H,L */ + }; + union + { + struct + { + volatile uint32_t nipriority7; /* 20h */ + volatile uint32_t nipriority6; /* 24h */ + volatile uint32_t nipriority5; /* 28h */ + volatile uint32_t nipriority4; /* 2Ch */ + volatile uint32_t nipriority3; /* 30h */ + volatile uint32_t nipriority2; /* 34h */ + volatile uint32_t nipriority1; /* 38h */ + volatile uint32_t nipriority0; /* 3Ch */ + }; + volatile uint32_t nipriority[8]; /* 7-0 */ + }; + volatile uint32_t nivecsr; /* 40h */ + volatile uint32_t fivecsr; /* 44h */ + union + { + struct + { + volatile uint32_t intsrch; /* 48h */ + volatile uint32_t intsrcl; /* 4Ch */ + }; + volatile uint32_t intsrc[2]; /* H,L */ + }; + union + { + struct + { + volatile uint32_t intfrch; /* 50h */ + volatile uint32_t intfrcl; /* 54h */ + }; + volatile uint32_t intfrc[2]; /* H,L */ + }; + union + { + struct + { + volatile uint32_t nipndh; /* 58h */ + volatile uint32_t nipndl; /* 5Ch */ + }; + volatile uint32_t nipnd[2]; /* H,L */ + }; + union + { + struct + { + volatile uint32_t fipndh; /* 60h */ + volatile uint32_t fipndl; /* 64h */ + }; + volatile uint32_t fipnd[2]; /* H,L */ + }; + volatile uint32_t skip1[0x26]; /* 68h */ + union /* 100h */ + { + struct + { + volatile uint32_t reserved0; + volatile uint32_t reserved1; + volatile uint32_t reserved2; + volatile uint32_t i2c3; + volatile uint32_t i2c2; + volatile uint32_t mpeg4encoder; + volatile uint32_t rtic; + volatile uint32_t fir; + volatile uint32_t mmc_sdhc2; + volatile uint32_t mmc_sdhc1; + volatile uint32_t i2c1; + volatile uint32_t ssi2; + volatile uint32_t ssi1; + volatile uint32_t cspi2; + volatile uint32_t cspi1; + volatile uint32_t ata; + volatile uint32_t mbx; + volatile uint32_t cspi3; + volatile uint32_t uart3; + volatile uint32_t iim; + volatile uint32_t sim1; + volatile uint32_t sim2; + volatile uint32_t rnga; + volatile uint32_t evtmon; + volatile uint32_t kpp; + volatile uint32_t rtc; + volatile uint32_t pwn; + volatile uint32_t epit2; + volatile uint32_t epit1; + volatile uint32_t gpt; + volatile uint32_t pwr_fail; + volatile uint32_t ccm_dvfs; + volatile uint32_t uart2; + volatile uint32_t nandfc; + volatile uint32_t sdma; + volatile uint32_t usb_host1; + volatile uint32_t usb_host2; + volatile uint32_t usb_otg; + volatile uint32_t reserved3; + volatile uint32_t mshc1; + volatile uint32_t mshc2; + volatile uint32_t ipu_err; + volatile uint32_t ipu; + volatile uint32_t reserved4; + volatile uint32_t reserved5; + volatile uint32_t uart1; + volatile uint32_t uart4; + volatile uint32_t uart5; + volatile uint32_t etc_irq; + volatile uint32_t scc_scm; + volatile uint32_t scc_smn; + volatile uint32_t gpio2; + volatile uint32_t gpio1; + volatile uint32_t ccm_clk; + volatile uint32_t pcmcia; + volatile uint32_t wdog; + volatile uint32_t gpio3; + volatile uint32_t reserved6; + volatile uint32_t ext_pwmg; + volatile uint32_t ext_temp; + volatile uint32_t ext_sense1; + volatile uint32_t ext_sense2; + volatile uint32_t ext_wdog; + volatile uint32_t ext_tv; + }; + volatile uint32_t vector[0x40]; /* 100h */ + }; +}; + +#define INT_PRIO_DEFAULT 7 + +enum INT_TYPE +{ + INT_TYPE_IRQ = 0, + INT_TYPE_FIQ +}; + +enum IMX31_INT_LIST +{ + __IMX31_INT_FIRST = -1, + INT_RESERVED0, INT_RESERVED1, INT_RESERVED2, INT_I2C3, + INT_I2C2, INT_MPEG4_ENCODER, INT_RTIC, INT_FIR, + INT_MMC_SDHC2, INT_MMC_SDHC1, INT_I2C1, INT_SSI2, + INT_SSI1, INT_CSPI2, INT_CSPI1, INT_ATA, + INT_MBX, INT_CSPI3, INT_UART3, INT_IIM, + INT_SIM1, INT_SIM2, INT_RNGA, INT_EVTMON, + INT_KPP, INT_RTC, INT_PWN, INT_EPIT2, + INT_EPIT1, INT_GPT, INT_PWR_FAIL, INT_CCM_DVFS, + INT_UART2, INT_NANDFC, INT_SDMA, INT_USB_HOST1, + INT_USB_HOST2, INT_USB_OTG, INT_RESERVED3, INT_MSHC1, + INT_MSHC2, INT_IPU_ERR, INT_IPU, INT_RESERVED4, + INT_RESERVED5, INT_UART1, INT_UART4, INT_UART5, + INT_ETC_IRQ, INT_SCC_SCM, INT_SCC_SMN, INT_GPIO2, + INT_GPIO1, INT_CCM_CLK, INT_PCMCIA, INT_WDOG, + INT_GPIO3, INT_RESERVED6, INT_EXT_PWMG, INT_EXT_TEMP, + INT_EXT_SENS1, INT_EXT_SENS2, INT_EXT_WDOG, INT_EXT_TV, + INT_ALL +}; + +void avic_init(void); +void avic_enable_int(enum IMX31_INT_LIST ints, enum INT_TYPE intstype, + unsigned long ni_priority, void (*handler)(void)); +void avic_set_int_priority(enum IMX31_INT_LIST ints, + unsigned long ni_priority); +void avic_disable_int(enum IMX31_INT_LIST ints); +void avic_set_int_type(enum IMX31_INT_LIST ints, enum INT_TYPE intstype); + +#endif /* AVIC_IMX31_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 new file mode 100644 index 0000000000..52293228f8 --- /dev/null +++ b/firmware/target/arm/imx31/gigabeat-s/adc-gigabeat-s.c @@ -0,0 +1,136 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2008 by Michael Sevakis + * + * 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 "mc13783.h" +#include "adc.h" +#include "adc-target.h" +#include "kernel.h" + +/* Do this so we may read all channels in a single SPI message */ +static const unsigned char reg_array[4] = +{ + MC13783_ADC2, + MC13783_ADC2, + MC13783_ADC2, + MC13783_ADC2, +}; + +static uint32_t channels[2][4]; +static struct wakeup adc_wake; +static struct mutex adc_mtx; +static long last_adc_read[2]; /* One for each input group */ + +/* Read 10-bit ADC channel */ +unsigned short adc_read(int channel) +{ + uint32_t data; + int input_select; + + if ((unsigned)channel >= NUM_ADC_CHANNELS) + return ADC_READ_ERROR; + + input_select = channel >> 3; + + mutex_lock(&adc_mtx); + + /* Limit the traffic through here */ + if (current_tick != last_adc_read[input_select]) + { + /* Keep enable, start conversion, increment from channel 0, + * increment from channel 4 */ + uint32_t adc1 = MC13783_ADEN | MC13783_ASC | + (0 << MC13783_ADA1_POS) | (4 << MC13783_ADA2_POS); + + if (input_select == 1) + adc1 |= MC13783_ADSEL; /* 2nd set of inputs */ + + /* Start conversion */ + mc13783_write(MC13783_ADC1, adc1); + + /* Wait for done signal */ + wakeup_wait(&adc_wake, TIMEOUT_BLOCK); + + /* Read all 8 channels that are converted - two channels in each + * word. */ + mc13783_read_regset(reg_array, channels[input_select], 4); + + last_adc_read[input_select] = current_tick; + } + + data = channels[input_select][channel & 3]; + + mutex_unlock(&adc_mtx); + + /* Channels 0-3/8-11 in ADD1, 4-7/12-15 in ADD2 */ + return (channel & 4) ? + ((data & MC13783_ADD2) >> MC13783_ADD2_POS) : + ((data & MC13783_ADD1) >> MC13783_ADD1_POS); +} + +bool adc_enable_channel(int channel, bool enable) +{ + uint32_t bit, mask; + + switch (channel) + { + case ADC_CHARGER_CURRENT: + mask = MC13783_CHRGICON; + break; + + case ADC_BATTERY_TEMP: + mask = MC13783_RTHEN; + break; + + default: + return false; + } + + bit = enable ? mask : 0; + + return mc13783_write_masked(MC13783_ADC0, bit, mask) + != MC13783_DATA_ERROR; +} + +/* Called by mc13783 interrupt thread when conversion is complete */ +void adc_done(void) +{ + wakeup_signal(&adc_wake); +} + +void adc_init(void) +{ + wakeup_init(&adc_wake); + mutex_init(&adc_mtx); + + /* Init so first reads get data */ + last_adc_read[0] = last_adc_read[1] = current_tick-1; + + /* Enable increment-by-read, turn off extra conversions. */ + mc13783_write(MC13783_ADC0, MC13783_ADINC2 | MC13783_ADINC1); + + /* Enable ADC, set multi-channel mode */ + mc13783_write(MC13783_ADC1, MC13783_ADEN); + + /* Enable ADCDONE event */ + mc13783_write(MC13783_INTERRUPT_STATUS0, MC13783_ADCDONEI); + mc13783_enable_event(MC13783_ADCDONE_EVENT); +} diff --git a/firmware/target/arm/imx31/gigabeat-s/adc-imx31.c b/firmware/target/arm/imx31/gigabeat-s/adc-imx31.c deleted file mode 100644 index 52293228f8..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/adc-imx31.c +++ /dev/null @@ -1,136 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2008 by Michael Sevakis - * - * 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 "mc13783.h" -#include "adc.h" -#include "adc-target.h" -#include "kernel.h" - -/* Do this so we may read all channels in a single SPI message */ -static const unsigned char reg_array[4] = -{ - MC13783_ADC2, - MC13783_ADC2, - MC13783_ADC2, - MC13783_ADC2, -}; - -static uint32_t channels[2][4]; -static struct wakeup adc_wake; -static struct mutex adc_mtx; -static long last_adc_read[2]; /* One for each input group */ - -/* Read 10-bit ADC channel */ -unsigned short adc_read(int channel) -{ - uint32_t data; - int input_select; - - if ((unsigned)channel >= NUM_ADC_CHANNELS) - return ADC_READ_ERROR; - - input_select = channel >> 3; - - mutex_lock(&adc_mtx); - - /* Limit the traffic through here */ - if (current_tick != last_adc_read[input_select]) - { - /* Keep enable, start conversion, increment from channel 0, - * increment from channel 4 */ - uint32_t adc1 = MC13783_ADEN | MC13783_ASC | - (0 << MC13783_ADA1_POS) | (4 << MC13783_ADA2_POS); - - if (input_select == 1) - adc1 |= MC13783_ADSEL; /* 2nd set of inputs */ - - /* Start conversion */ - mc13783_write(MC13783_ADC1, adc1); - - /* Wait for done signal */ - wakeup_wait(&adc_wake, TIMEOUT_BLOCK); - - /* Read all 8 channels that are converted - two channels in each - * word. */ - mc13783_read_regset(reg_array, channels[input_select], 4); - - last_adc_read[input_select] = current_tick; - } - - data = channels[input_select][channel & 3]; - - mutex_unlock(&adc_mtx); - - /* Channels 0-3/8-11 in ADD1, 4-7/12-15 in ADD2 */ - return (channel & 4) ? - ((data & MC13783_ADD2) >> MC13783_ADD2_POS) : - ((data & MC13783_ADD1) >> MC13783_ADD1_POS); -} - -bool adc_enable_channel(int channel, bool enable) -{ - uint32_t bit, mask; - - switch (channel) - { - case ADC_CHARGER_CURRENT: - mask = MC13783_CHRGICON; - break; - - case ADC_BATTERY_TEMP: - mask = MC13783_RTHEN; - break; - - default: - return false; - } - - bit = enable ? mask : 0; - - return mc13783_write_masked(MC13783_ADC0, bit, mask) - != MC13783_DATA_ERROR; -} - -/* Called by mc13783 interrupt thread when conversion is complete */ -void adc_done(void) -{ - wakeup_signal(&adc_wake); -} - -void adc_init(void) -{ - wakeup_init(&adc_wake); - mutex_init(&adc_mtx); - - /* Init so first reads get data */ - last_adc_read[0] = last_adc_read[1] = current_tick-1; - - /* Enable increment-by-read, turn off extra conversions. */ - mc13783_write(MC13783_ADC0, MC13783_ADINC2 | MC13783_ADINC1); - - /* Enable ADC, set multi-channel mode */ - mc13783_write(MC13783_ADC1, MC13783_ADEN); - - /* Enable ADCDONE event */ - mc13783_write(MC13783_INTERRUPT_STATUS0, MC13783_ADCDONEI); - mc13783_enable_event(MC13783_ADCDONE_EVENT); -} diff --git a/firmware/target/arm/imx31/gigabeat-s/ata-imx31.c b/firmware/target/arm/imx31/gigabeat-s/ata-imx31.c deleted file mode 100644 index 5ce7ad0a03..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/ata-imx31.c +++ /dev/null @@ -1,685 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2007 by Will Robertson - * - * 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 "cpu.h" -#include "kernel.h" -#include "thread.h" -#include "system.h" -#include "power.h" -#include "panic.h" -#include "ata.h" -#include "ata-target.h" -#include "ccm-imx31.h" -#ifdef HAVE_ATA_DMA -#include "sdma-imx31.h" -#include "mmu-imx31.h" -#endif - -/* PIO modes timing info */ -static const struct ata_pio_timings -{ - uint16_t time_2w; /* t2 during write */ - uint16_t time_2r; /* t2 during read */ - uint8_t time_ax; /* tA */ - uint8_t time_1; /* t1 */ - uint8_t time_4; /* t4 */ - uint8_t time_9; /* t9 */ -} pio_timings[5] = -{ - [0] = /* PIO mode 0 */ - { - .time_1 = 70, - .time_2w = 290, - .time_2r = 290, - .time_ax = 35, - .time_4 = 30, - .time_9 = 20 - }, - [1] = /* PIO mode 1 */ - { - .time_1 = 50, - .time_2w = 290, - .time_2r = 290, - .time_ax = 35, - .time_4 = 20, - .time_9 = 15 - }, - [2] = /* PIO mode 2 */ - { - .time_1 = 30, - .time_2w = 290, - .time_2r = 290, - .time_ax = 35, - .time_4 = 15, - .time_9 = 10 - }, - [3] = /* PIO mode 3 */ - { - .time_1 = 30, - .time_2w = 80, - .time_2r = 80, - .time_ax = 35, - .time_4 = 10, - .time_9 = 10 - }, - [4] = /* PIO mode 4 */ - { - .time_1 = 25, - .time_2w = 70, - .time_2r = 70, - .time_ax = 35, - .time_4 = 10, - .time_9 = 10 - } -}; - -/* Track first init */ -static bool initialized = false; - -#ifdef HAVE_ATA_DMA -/* One DMA channel for reads, the other for writes othewise one channel would - * have to be reinitialized every time the direction changed. (Different - * SDMA scripts are used for reading or writing) */ -#define ATA_DMA_CH_NUM_RD 3 -#define ATA_DMA_CH_NUM_WR 4 -/* Use default priority for these channels (1) - ATA isn't realtime urgent. */ -/* Maximum DMA size per buffer descriptor (32-byte aligned) */ -#define ATA_MAX_BD_SIZE (65534 & ~31) /* 65504 */ - -/* Number of buffer descriptors required for a maximum sector count trasfer. - * NOTE: Assumes LBA28 and 512-byte sectors! */ -#define ATA_BASE_BD_COUNT ((256*512 + (ATA_MAX_BD_SIZE-1)) / ATA_MAX_BD_SIZE) -#define ATA_BD_COUNT (ATA_BASE_BD_COUNT + 2) - -static const struct ata_mdma_timings -{ - uint8_t time_m; /* tM */ - uint8_t time_jn; /* tH */ - uint8_t time_d; /* tD */ - uint8_t time_k; /* tKW */ -} mdma_timings[] = -{ - [0] = /* MDMA mode 0 */ - { - .time_m = 50, - .time_jn = 20, - .time_d = 215, - .time_k = 215 - }, - [1] = /* MDMA mode 1 */ - { - .time_m = 30, - .time_jn = 15, - .time_d = 80, - .time_k = 50 - }, - [2] = /* MDMA mode 2 */ - { - .time_m = 25, - .time_jn = 10, - .time_d = 70, - .time_k = 25 - } -}; - -static const struct ata_udma_timings -{ - uint8_t time_ack; /* tACK */ - uint8_t time_env; /* tENV */ - uint8_t time_rpx; /* tRP */ - uint8_t time_zah; /* tZAH */ - uint8_t time_mlix; /* tMLI */ - uint8_t time_dvh; /* tDVH */ - uint8_t time_dzfs; /* tDVS+tDVH? */ - uint8_t time_dvs; /* tDVS */ - uint8_t time_cvh; /* ?? */ - uint8_t time_ss; /* tSS */ - uint8_t time_cyc; /* tCYC */ -} udma_timings[] = -{ - [0] = /* UDMA mode 0 */ - { - .time_ack = 20, - .time_env = 20, - .time_rpx = 160, - .time_zah = 20, - .time_mlix = 20, - .time_dvh = 6, - .time_dzfs = 80, - .time_dvs = 70, - .time_cvh = 6, - .time_ss = 50, - .time_cyc = 114 - }, - [1] = /* UDMA mode 1 */ - { - .time_ack = 20, - .time_env = 20, - .time_rpx = 125, - .time_zah = 20, - .time_mlix = 20, - .time_dvh = 6, - .time_dzfs = 63, - .time_dvs = 48, - .time_cvh = 6, - .time_ss = 50, - .time_cyc = 75 - }, - [2] = /* UDMA mode 2 */ - { - .time_ack = 20, - .time_env = 20, - .time_rpx = 100, - .time_zah = 20, - .time_mlix = 20, - .time_dvh = 6, - .time_dzfs = 47, - .time_dvs = 34, - .time_cvh = 6, - .time_ss = 50, - .time_cyc = 55 - }, - [3] = /* UDMA mode 3 */ - { - .time_ack = 20, - .time_env = 20, - .time_rpx = 100, - .time_zah = 20, - .time_mlix = 20, - .time_dvh = 6, - .time_dzfs = 35, - .time_dvs = 20, - .time_cvh = 6, - .time_ss = 50, - .time_cyc = 39 - }, - [4] = /* UDMA mode 4 */ - { - .time_ack = 20, - .time_env = 20, - .time_rpx = 100, - .time_zah = 20, - .time_mlix = 20, - .time_dvh = 6, - .time_dzfs = 25, - .time_dvs = 7, - .time_cvh = 6, - .time_ss = 50, - .time_cyc = 25 - }, -#if 0 - [5] = /* UDMA mode 5 (bus clock 80MHz or higher only) */ - { - .time_ack = 20, - .time_env = 20, - .time_rpx = 85, - .time_zah = 20, - .time_mlix = 20, - .time_dvh = 6, - .time_dzfs = 40, - .time_dvs = 5, - .time_cvh = 10, - .time_ss = 50, - .time_cyc = 17 - } -#endif -}; - -/** Threading **/ -/* Signal to tell thread when DMA is done */ -static struct wakeup ata_dma_wakeup; - -/** SDMA **/ -/* Array of buffer descriptors for large transfers and alignnment */ -static struct buffer_descriptor ata_bda[ATA_BD_COUNT] DEVBSS_ATTR; -/* ATA channel descriptors */ -static struct channel_descriptor ata_cd_rd DEVBSS_ATTR; /* read channel */ -static struct channel_descriptor ata_cd_wr DEVBSS_ATTR; /* write channel */ -/* DMA channel to be started for transfer */ -static unsigned int current_channel = 0; - -/** Buffers **/ -/* Scatter buffer for first and last 32 bytes of a non cache-aligned transfer - * to cached RAM. */ -static uint32_t scatter_buffer[32/4*2] DEVBSS_ATTR; -/* Address of ends in destination buffer for unaligned reads - copied after - * DMA completes. */ -static void *sb_dst[2] = { NULL, NULL }; - -/** Modes **/ -#define ATA_DMA_MWDMA 0x00000000 /* Using multiword DMA */ -#define ATA_DMA_UDMA ATA_DMA_ULTRA_SELECTED /* Using Ultra DMA */ -#define ATA_DMA_PIO 0x80000000 /* Using PIO */ -#define ATA_DMA_DISABLED 0x80000001 /* DMA init error - use PIO */ -static unsigned long ata_dma_selected = ATA_DMA_PIO; -#endif /* HAVE_ATA_DMA */ - -static unsigned int get_T(void) -{ - /* T = ATA clock period in nanoseconds */ - return 1000 * 1000 * 1000 / ccm_get_ata_clk(); -} - -static void ata_wait_for_idle(void) -{ - while (!(ATA_INTERRUPT_PENDING & ATA_CONTROLLER_IDLE)); -} - -/* Route the INTRQ to either the MCU or SDMA depending upon whether there is - * a DMA transfer in progress. */ -static inline void ata_set_intrq(bool to_dma) -{ - ATA_INTERRUPT_ENABLE = - (ATA_INTERRUPT_ENABLE & ~(ATA_INTRQ1 | ATA_INTRQ2)) | - (to_dma ? ATA_INTRQ1 : ATA_INTRQ2); -} - -/* Setup the timing for PIO mode */ -void ata_set_pio_timings(int mode) -{ - const struct ata_pio_timings * const timings = &pio_timings[mode]; - unsigned int T = get_T(); - - ata_wait_for_idle(); - - ATA_TIME_1 = (timings->time_1 + T) / T; - ATA_TIME_2W = (timings->time_2w + T) / T; - ATA_TIME_2R = (timings->time_2r + T) / T; - ATA_TIME_AX = (timings->time_ax + T) / T + 2; /* 1.5 + tAX */ - ATA_TIME_PIO_RDX = 1; - ATA_TIME_4 = (timings->time_4 + T) / T; - ATA_TIME_9 = (timings->time_9 + T) / T; -} - -void ata_reset(void) -{ - /* Be sure we're not busy */ - ata_wait_for_idle(); - - ATA_INTF_CONTROL &= ~(ATA_ATA_RST | ATA_FIFO_RST); - sleep(HZ/100); - ATA_INTF_CONTROL = ATA_ATA_RST | ATA_FIFO_RST; - sleep(HZ/100); - - ata_wait_for_idle(); -} - -void ata_enable(bool on) -{ - /* Unconditionally clock module before writing regs */ - ccm_module_clock_gating(CG_ATA, CGM_ON_RUN_WAIT); - ata_wait_for_idle(); - - if (on) - { - ATA_INTF_CONTROL = ATA_ATA_RST | ATA_FIFO_RST; - sleep(HZ/100); - } - else - { - ATA_INTF_CONTROL &= ~(ATA_ATA_RST | ATA_FIFO_RST); - sleep(HZ/100); - - /* Disable off - unclock ATA module */ - ccm_module_clock_gating(CG_ATA, CGM_OFF); - } -} - -bool ata_is_coldstart(void) -{ - return true; -} - -#ifdef HAVE_ATA_DMA -static void ata_set_mdma_timings(unsigned int mode) -{ - const struct ata_mdma_timings * const timings = &mdma_timings[mode]; - unsigned int T = get_T(); - - ATA_TIME_M = (timings->time_m + T) / T; - ATA_TIME_JN = (timings->time_jn + T) / T; - ATA_TIME_D = (timings->time_d + T) / T; - ATA_TIME_K = (timings->time_k + T) / T; -} - -static void ata_set_udma_timings(unsigned int mode) -{ - const struct ata_udma_timings * const timings = &udma_timings[mode]; - unsigned int T = get_T(); - - ATA_TIME_ACK = (timings->time_ack + T) / T; - ATA_TIME_ENV = (timings->time_env + T) / T; - ATA_TIME_RPX = (timings->time_rpx + T) / T; - ATA_TIME_ZAH = (timings->time_zah + T) / T; - ATA_TIME_MLIX = (timings->time_mlix + T) / T; - ATA_TIME_DVH = (timings->time_dvh + T) / T + 1; - ATA_TIME_DZFS = (timings->time_dzfs + T) / T; - ATA_TIME_DVS = (timings->time_dvs + T) / T; - ATA_TIME_CVH = (timings->time_cvh + T) / T; - ATA_TIME_SS = (timings->time_ss + T) / T; - ATA_TIME_CYC = (timings->time_cyc + T) / T; -} - -void ata_dma_set_mode(unsigned char mode) -{ - unsigned int modeidx = mode & 0x07; - unsigned int dmamode = mode & 0xf8; - - ata_wait_for_idle(); - - if (ata_dma_selected == ATA_DMA_DISABLED) - { - /* Configuration error - no DMA */ - } - else if (dmamode == 0x40 && modeidx <= ATA_MAX_UDMA) - { - /* Using Ultra DMA */ - ata_set_udma_timings(dmamode); - ata_dma_selected = ATA_DMA_UDMA; - } - else if (dmamode == 0x20 && modeidx <= ATA_MAX_MWDMA) - { - /* Using Multiword DMA */ - ata_set_mdma_timings(dmamode); - ata_dma_selected = ATA_DMA_MWDMA; - } - else - { - /* Don't understand this - force PIO. */ - ata_dma_selected = ATA_DMA_PIO; - } -} - -/* Called by SDMA when transfer is complete */ -static void ata_dma_callback(void) -{ - /* Clear FIFO if not empty - shouldn't happen */ - while (ATA_FIFO_FILL != 0) - ATA_FIFO_DATA_32; - - /* Clear FIFO interrupts (the only ones that can be) */ - ATA_INTERRUPT_CLEAR = ATA_INTERRUPT_PENDING; - - ata_set_intrq(false); /* Return INTRQ to MCU */ - wakeup_signal(&ata_dma_wakeup); /* Signal waiting thread */ -} - -bool ata_dma_setup(void *addr, unsigned long bytes, bool write) -{ - struct buffer_descriptor *bd_p; - unsigned char *buf; - - if (UNLIKELY(bytes > ATA_BASE_BD_COUNT*ATA_MAX_BD_SIZE || - (ata_dma_selected & ATA_DMA_PIO))) - { - /* Too much? Implies BD count should be reevaluated since this - * shouldn't be reached based upon size. Otherwise we simply didn't - * understand the DMA mode setup. Force PIO in both cases. */ - ATA_INTF_CONTROL = ATA_FIFO_RST | ATA_ATA_RST; - return false; - } - - bd_p = &ata_bda[0]; - buf = (unsigned char *)addr_virt_to_phys((unsigned long)addr); - sb_dst[0] = NULL; /* Assume not needed */ - - if (write) - { - /* No cache alignment concerns */ - current_channel = ATA_DMA_CH_NUM_WR; - - if (LIKELY(buf != addr)) - { - /* addr is virtual */ - clean_dcache_range(addr, bytes); - } - - /* Setup ATA controller for DMA transmit */ - ATA_INTF_CONTROL = ATA_FIFO_RST | ATA_ATA_RST | ATA_FIFO_TX_EN | - ATA_DMA_PENDING | ata_dma_selected | ATA_DMA_WRITE; - ATA_FIFO_ALARM = SDMA_ATA_WML / 2; - } - else - { - current_channel = ATA_DMA_CH_NUM_RD; - - /* Setup ATA controller for DMA receive */ - ATA_INTF_CONTROL = ATA_FIFO_RST | ATA_ATA_RST | ATA_FIFO_RCV_EN | - ATA_DMA_PENDING | ata_dma_selected; - ATA_FIFO_ALARM = SDMA_ATA_WML / 2; - - if (LIKELY(buf != addr)) - { - /* addr is virtual */ - dump_dcache_range(addr, bytes); - - if ((unsigned long)addr & 31) - { - /* Not cache aligned, must use scatter buffers for first and - * last 32 bytes. */ - unsigned char *bufstart = buf; - - sb_dst[0] = addr; - bd_p->buf_addr = scatter_buffer; - bd_p->mode.count = 32; - bd_p->mode.status = BD_DONE | BD_CONT; - - buf += 32; - bytes -= 32; - bd_p++; - - while (bytes > ATA_MAX_BD_SIZE) - { - bd_p->buf_addr = buf; - bd_p->mode.count = ATA_MAX_BD_SIZE; - bd_p->mode.status = BD_DONE | BD_CONT; - buf += ATA_MAX_BD_SIZE; - bytes -= ATA_MAX_BD_SIZE; - bd_p++; - } - - if (bytes > 32) - { - unsigned long size = bytes - 32; - bd_p->buf_addr = buf; - bd_p->mode.count = size; - bd_p->mode.status = BD_DONE | BD_CONT; - buf += size; - bd_p++; - } - - /* There will be exactly 32 bytes left */ - - /* Final buffer - wrap to base bd, interrupt */ - sb_dst[1] = addr + (buf - bufstart); - bd_p->buf_addr = &scatter_buffer[32/4]; - bd_p->mode.count = 32; - bd_p->mode.status = BD_DONE | BD_WRAP | BD_INTR; - - return true; - } - } - } - - /* Setup buffer descriptors for both cache-aligned reads and all write - * operations. */ - while (bytes > ATA_MAX_BD_SIZE) - { - bd_p->buf_addr = buf; - bd_p->mode.count = ATA_MAX_BD_SIZE; - bd_p->mode.status = BD_DONE | BD_CONT; - buf += ATA_MAX_BD_SIZE; - bytes -= ATA_MAX_BD_SIZE; - bd_p++; - } - - /* Final buffer - wrap to base bd, interrupt */ - bd_p->buf_addr = buf; - bd_p->mode.count = bytes; - bd_p->mode.status = BD_DONE | BD_WRAP | BD_INTR; - - return true; -} - -bool ata_dma_finish(void) -{ - unsigned int channel = current_channel; - long timeout = current_tick + HZ*10; - - current_channel = 0; - - ata_set_intrq(true); /* Give INTRQ to DMA */ - sdma_channel_run(channel); /* Kick the channel to wait for events */ - - while (1) - { - int oldirq; - - if (LIKELY(wakeup_wait(&ata_dma_wakeup, HZ/2) == OBJ_WAIT_SUCCEEDED)) - break; - - ata_keep_active(); - - if (TIME_BEFORE(current_tick, timeout)) - continue; - - /* Epic fail - timed out - maybe. */ - oldirq = disable_irq_save(); - ata_set_intrq(false); /* Strip INTRQ from DMA */ - sdma_channel_stop(channel); /* Stop DMA */ - restore_irq(oldirq); - - if (wakeup_wait(&ata_dma_wakeup, TIMEOUT_NOBLOCK) == OBJ_WAIT_SUCCEEDED) - break; /* DMA really did finish after timeout */ - - sdma_channel_reset(channel); /* Reset everything + clear error */ - return false; - } - - if (sdma_channel_is_error(channel)) - { - /* Channel error in one or more descriptors */ - sdma_channel_reset(channel); /* Reset everything + clear error */ - return false; - } - - if (sb_dst[0] != NULL) - { - /* NOTE: This requires that unaligned access support be enabled! */ - register void *sbs = scatter_buffer; - register void *sbd0 = sb_dst[0]; - register void *sbd1 = sb_dst[1]; - asm volatile( - "add r0, %1, #32 \n" /* Prefetch at DMA-direct boundaries */ - "mcrr p15, 2, r0, r0, c12 \n" - "mcrr p15, 2, %2, %2, c12 \n" - "ldmia %0!, { r0-r3 } \n" /* Copy the 32-bytes to destination */ - "str r0, [%1], #4 \n" /* stmia doesn't work unaligned */ - "str r1, [%1], #4 \n" - "str r2, [%1], #4 \n" - "str r3, [%1], #4 \n" - "ldmia %0!, { r0-r3 } \n" - "str r0, [%1], #4 \n" - "str r1, [%1], #4 \n" - "str r2, [%1], #4 \n" - "str r3, [%1] \n" - "ldmia %0!, { r0-r3 } \n" /* Copy the 32-bytes to destination */ - "str r0, [%2], #4 \n" /* stmia doesn't work unaligned */ - "str r1, [%2], #4 \n" - "str r2, [%2], #4 \n" - "str r3, [%2], #4 \n" - "ldmia %0!, { r0-r3 } \n" - "str r0, [%2], #4 \n" - "str r1, [%2], #4 \n" - "str r2, [%2], #4 \n" - "str r3, [%2] \n" - : "+r"(sbs), "+r"(sbd0), "+r"(sbd1) - : - : "r0", "r1", "r2", "r3"); - } - - return true; -} -#endif /* HAVE_ATA_DMA */ - -void ata_device_init(void) -{ - /* Make sure we're not in reset mode */ - ata_enable(true); - - if (!initialized) - { - ATA_INTERRUPT_ENABLE = 0; - ATA_INTERRUPT_CLEAR = ATA_INTERRUPT_PENDING; - } - - ata_set_intrq(false); - - if (initialized) - return; - - /* All modes use same tOFF/tON */ - ATA_TIME_OFF = 3; - ATA_TIME_ON = 3; - - /* Setup mode 0 for all by default - * Mode may be switched later once identify info is ready in which - * case the main driver calls back */ - ata_set_pio_timings(0); - -#ifdef HAVE_ATA_DMA - ata_set_mdma_timings(0); - ata_set_udma_timings(0); - - ata_dma_selected = ATA_DMA_PIO; - - /* Called for first time at startup */ - wakeup_init(&ata_dma_wakeup); - - /* Read/write channels share buffer descriptors */ - ata_cd_rd.bd_count = ATA_BD_COUNT; - ata_cd_rd.callback = ata_dma_callback; - ata_cd_rd.shp_addr = SDMA_PER_ADDR_ATA_RX; - ata_cd_rd.wml = SDMA_ATA_WML; - ata_cd_rd.per_type = SDMA_PER_ATA; - ata_cd_rd.tran_type = SDMA_TRAN_PER_2_EMI; - ata_cd_rd.event_id1 = SDMA_REQ_ATA_TXFER_END; - ata_cd_rd.event_id2 = SDMA_REQ_ATA_RX; - - ata_cd_wr.bd_count = ATA_BD_COUNT; - ata_cd_wr.callback = ata_dma_callback; - ata_cd_wr.shp_addr = SDMA_PER_ADDR_ATA_TX; - ata_cd_wr.wml = SDMA_ATA_WML; - ata_cd_wr.per_type = SDMA_PER_ATA; - ata_cd_wr.tran_type = SDMA_TRAN_EMI_2_PER; - ata_cd_wr.event_id1 = SDMA_REQ_ATA_TXFER_END; - ata_cd_wr.event_id2 = SDMA_REQ_ATA_TX; - - if (!sdma_channel_init(ATA_DMA_CH_NUM_RD, &ata_cd_rd, ata_bda) || - !sdma_channel_init(ATA_DMA_CH_NUM_WR, &ata_cd_wr, ata_bda)) - { - /* Channel init error - disable DMA forever */ - ata_dma_selected = ATA_DMA_DISABLED; - } -#endif /* HAVE_ATA_DMA */ - - initialized = true; -} diff --git a/firmware/target/arm/imx31/gigabeat-s/ata-target.h b/firmware/target/arm/imx31/gigabeat-s/ata-target.h deleted file mode 100644 index f7f84f82e9..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/ata-target.h +++ /dev/null @@ -1,82 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2006 by Linus Nielsen Feltzing - * - * 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. - * - ****************************************************************************/ -#ifndef ATA_TARGET_H -#define ATA_TARGET_H - -#ifdef BOOTLOADER -#define ATA_DRIVER_CLOSE -#endif - -/* Plain C read & write loops */ -/* They likely won't be used anyway since DMA potentially works for any - * sector number and alignment. */ -#define PREFER_C_READING -#define PREFER_C_WRITING - -#ifdef HAVE_ATA_DMA -#define ATA_MAX_MWDMA 2 -#define ATA_MAX_UDMA 4 -#endif - -#define ATA_DATA ATA_DRIVE_DATA -#define ATA_ERROR ATA_DRIVE_FEATURES -#define ATA_NSECTOR ATA_DRIVE_SECTOR_COUNT -#define ATA_SECTOR ATA_DRIVE_SECTOR_NUM -#define ATA_LCYL ATA_DRIVE_CYL_LOW -#define ATA_HCYL ATA_DRIVE_CYL_HIGH -#define ATA_SELECT ATA_DRIVE_CYL_HEAD -#define ATA_COMMAND ATA_DRIVE_COMMAND -#define ATA_CONTROL ATA_DRIVE_CONTROL - -#define STATUS_BSY 0x80 -#define STATUS_RDY 0x40 -#define STATUS_DF 0x20 -#define STATUS_DRQ 0x08 -#define STATUS_ERR 0x01 -#define ERROR_ABRT 0x04 -#define ERROR_IDNF 0x10 - -#define WRITE_PATTERN1 0xa5 -#define WRITE_PATTERN2 0x5a -#define WRITE_PATTERN3 0xaa -#define WRITE_PATTERN4 0x55 - -#define READ_PATTERN1 0xa5 -#define READ_PATTERN2 0x5a -#define READ_PATTERN3 0xaa -#define READ_PATTERN4 0x55 - -#define READ_PATTERN1_MASK 0xff -#define READ_PATTERN2_MASK 0xff -#define READ_PATTERN3_MASK 0xff -#define READ_PATTERN4_MASK 0xff - -#define SET_REG(reg,val) reg = (val) -#define SET_16BITREG(reg,val) reg = (val) - -void ata_reset(void); -void ata_device_init(void); -bool ata_is_coldstart(void); - -#define ATA_SET_DEVICE_FEATURES -void ata_set_pio_timings(int mode); - -#endif /* ATA_TARGET_H */ diff --git a/firmware/target/arm/imx31/gigabeat-s/avic-imx31.c b/firmware/target/arm/imx31/gigabeat-s/avic-imx31.c deleted file mode 100644 index 4ba7da4be0..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/avic-imx31.c +++ /dev/null @@ -1,212 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2007 by James Espinoza - * - * 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 -#include "system.h" -#include "imx31l.h" -#include "avic-imx31.h" -#include "panic.h" -#include "debug.h" - -static const char * avic_int_names[64] = -{ - "RESERVED0", "RESERVED1", "RESERVED2", "I2C3", - "I2C2", "MPEG4_ENCODER", "RTIC", "FIR", - "MMC/SDHC2", "MMC/SDHC1", "I2C1", "SSI2", - "SSI1", "CSPI2", "CSPI1", "ATA", - "MBX", "CSPI3", "UART3", "IIM", - "SIM1", "SIM2", "RNGA", "EVTMON", - "KPP", "RTC", "PWN", "EPIT2", - "EPIT1", "GPT", "PWR_FAIL", "CCM_DVFS", - "UART2", "NANDFC", "SDMA", "USB_HOST1", - "USB_HOST2", "USB_OTG", "RESERVED3", "MSHC1", - "MSHC2", "IPU_ERR", "IPU", "RESERVED4", - "RESERVED5", "UART1", "UART4", "UART5", - "ETC_IRQ", "SCC_SCM", "SCC_SMN", "GPIO2", - "GPIO1", "CCM_CLK", "PCMCIA", "WDOG", - "GPIO3", "RESERVED6", "EXT_PWMG", "EXT_TEMP", - "EXT_SENS1", "EXT_SENS2", "EXT_WDOG", "EXT_TV" -}; - -void UIE_VECTOR(void) -{ - int mode; - int offset; - - asm volatile ( - "mrs %0, cpsr \n" /* Mask core IRQ/FIQ */ - "orr %0, %0, #0xc0 \n" - "msr cpsr_c, %0 \n" - "and %0, %0, #0x1f \n" /* Get mode bits */ - : "=&r"(mode) - ); - - offset = mode == 0x11 ? - (int32_t)AVIC_FIVECSR : ((int32_t)AVIC_NIVECSR >> 16); - - panicf("Unhandled %s %d: %s", - mode == 0x11 ? "FIQ" : "IRQ", offset, - offset >= 0 ? avic_int_names[offset] : ""); -} - -/* We use the AVIC */ -void __attribute__((interrupt("IRQ"))) irq_handler(void) -{ - const int offset = (int32_t)AVIC_NIVECSR >> 16; - - if (offset == -1) - { - /* This is called occasionally for some unknown reason even with the - * avic enabled but returning normally appears to cause no harm. The - * KPP and ATA seem to have a part in it (common but multiplexed pins - * that can interfere). It will be investigated more thoroughly but - * for now it is simply an occasional irritant. */ - return; - } - - disable_interrupt(IRQ_FIQ_STATUS); - panicf("Unhandled IRQ %d in irq_handler: %s", offset, - offset >= 0 ? avic_int_names[offset] : ""); -} - -/* Accoring to section 9.3.5 of the UM, the AVIC doesn't accelerate - * fast interrupts and they must be dispatched */ -void __attribute__((naked)) fiq_handler(void) -{ - asm volatile ( - "mov r10, #0x68000000 \n" /* load AVIC base address */ - "ldr r9, [r10, #0x44] \n" /* read FIVECSR of AVIC */ - "add r10, r10, #0x100 \n" /* move pointer to base of VECTOR table */ - "ldr r8, [r10, r9, lsl #2] \n" /* read FIQ vector from VECTOR table */ - "bx r8 \n" /* jump to FIQ service routine */ - ); -} - -void avic_init(void) -{ - struct avic_map * const avic = (struct avic_map *)AVIC_BASE_ADDR; - int i; - - /* Disable all interrupts and set to unhandled */ - avic_disable_int(INT_ALL); - - /* Reset AVIC control */ - avic->intcntl = 0; - - /* Init all interrupts to type IRQ */ - avic_set_int_type(INT_ALL, INT_TYPE_IRQ); - - /* Set all normal to lowest priority */ - for (i = 0; i < 8; i++) - avic->nipriority[i] = 0; - - /* Set NM bit to enable VIC */ - avic->intcntl |= AVIC_INTCNTL_NM; - - /* Enable VE bit in CP15 Control reg to enable VIC */ - asm volatile ( - "mrc p15, 0, r0, c1, c0, 0 \n" - "orr r0, r0, #(1 << 24) \n" - "mcr p15, 0, r0, c1, c0, 0 \n" - : : : "r0"); - - /* Enable normal interrupts at all priorities */ - avic->nimask = 0x1f; -} - -void avic_set_int_priority(enum IMX31_INT_LIST ints, - unsigned long ni_priority) -{ - struct avic_map * const avic = (struct avic_map *)AVIC_BASE_ADDR; - volatile uint32_t *reg = &avic->nipriority[7 - (ints >> 3)]; - unsigned int shift = (ints & 0x7) << 2; - uint32_t mask = 0xful << shift; - *reg = (*reg & ~mask) | ((ni_priority << shift) & mask); -} - -void avic_enable_int(enum IMX31_INT_LIST ints, enum INT_TYPE intstype, - unsigned long ni_priority, void (*handler)(void)) -{ - struct avic_map * const avic = (struct avic_map *)AVIC_BASE_ADDR; - int oldstatus = disable_interrupt_save(IRQ_FIQ_STATUS); - - if (ints != INT_ALL) /* No mass-enable allowed */ - { - avic_set_int_type(ints, intstype); - avic->vector[ints] = (long)handler; - avic->intennum = ints; - avic_set_int_priority(ints, ni_priority); - } - - restore_interrupt(oldstatus); -} - -void avic_disable_int(enum IMX31_INT_LIST ints) -{ - struct avic_map * const avic = (struct avic_map *)AVIC_BASE_ADDR; - uint32_t i; - - if (ints == INT_ALL) - { - for (i = 0; i < 64; i++) - { - avic->intdisnum = i; - avic->vector[i] = (long)UIE_VECTOR; - } - } - else - { - avic->intdisnum = ints; - avic->vector[ints] = (long)UIE_VECTOR; - } -} - -static void set_int_type(int i, enum INT_TYPE intstype) -{ - /* INTTYPEH: vectors 63-32, INTTYPEL: vectors 31-0 */ - struct avic_map * const avic = (struct avic_map *)AVIC_BASE_ADDR; - volatile uint32_t *reg = &avic->inttype[1 - (i >> 5)]; - uint32_t val = 1L << (i & 0x1f); - - if (intstype == INT_TYPE_IRQ) - val = *reg & ~val; - else - val = *reg | val; - - *reg = val; -} - -void avic_set_int_type(enum IMX31_INT_LIST ints, enum INT_TYPE intstype) -{ - int oldstatus = disable_interrupt_save(IRQ_FIQ_STATUS); - - if (ints == INT_ALL) - { - int i; - for (i = 0; i < 64; i++) - set_int_type(i, intstype); - } - else - { - set_int_type(ints, intstype); - } - - restore_interrupt(oldstatus); -} diff --git a/firmware/target/arm/imx31/gigabeat-s/avic-imx31.h b/firmware/target/arm/imx31/gigabeat-s/avic-imx31.h deleted file mode 100644 index a049713600..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/avic-imx31.h +++ /dev/null @@ -1,213 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2007 by James Espinoza - * - * 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. - * - ****************************************************************************/ -#ifndef AVIC_IMX31_H -#define AVIC_IMX31_H - -struct avic_map -{ - volatile uint32_t intcntl; /* 00h */ - volatile uint32_t nimask; /* 04h */ - volatile uint32_t intennum; /* 08h */ - volatile uint32_t intdisnum; /* 0Ch */ - union /* 10h */ - { - struct - { - volatile uint32_t intenableh; /* 10h */ - volatile uint32_t intenablel; /* 14h */ - }; - volatile uint32_t intenable[2]; /* H,L */ - }; - union - { - struct - { - volatile uint32_t inttypeh; /* 18h */ - volatile uint32_t inttypel; /* 1Ch */ - }; - volatile uint32_t inttype[2]; /* H,L */ - }; - union - { - struct - { - volatile uint32_t nipriority7; /* 20h */ - volatile uint32_t nipriority6; /* 24h */ - volatile uint32_t nipriority5; /* 28h */ - volatile uint32_t nipriority4; /* 2Ch */ - volatile uint32_t nipriority3; /* 30h */ - volatile uint32_t nipriority2; /* 34h */ - volatile uint32_t nipriority1; /* 38h */ - volatile uint32_t nipriority0; /* 3Ch */ - }; - volatile uint32_t nipriority[8]; /* 7-0 */ - }; - volatile uint32_t nivecsr; /* 40h */ - volatile uint32_t fivecsr; /* 44h */ - union - { - struct - { - volatile uint32_t intsrch; /* 48h */ - volatile uint32_t intsrcl; /* 4Ch */ - }; - volatile uint32_t intsrc[2]; /* H,L */ - }; - union - { - struct - { - volatile uint32_t intfrch; /* 50h */ - volatile uint32_t intfrcl; /* 54h */ - }; - volatile uint32_t intfrc[2]; /* H,L */ - }; - union - { - struct - { - volatile uint32_t nipndh; /* 58h */ - volatile uint32_t nipndl; /* 5Ch */ - }; - volatile uint32_t nipnd[2]; /* H,L */ - }; - union - { - struct - { - volatile uint32_t fipndh; /* 60h */ - volatile uint32_t fipndl; /* 64h */ - }; - volatile uint32_t fipnd[2]; /* H,L */ - }; - volatile uint32_t skip1[0x26]; /* 68h */ - union /* 100h */ - { - struct - { - volatile uint32_t reserved0; - volatile uint32_t reserved1; - volatile uint32_t reserved2; - volatile uint32_t i2c3; - volatile uint32_t i2c2; - volatile uint32_t mpeg4encoder; - volatile uint32_t rtic; - volatile uint32_t fir; - volatile uint32_t mmc_sdhc2; - volatile uint32_t mmc_sdhc1; - volatile uint32_t i2c1; - volatile uint32_t ssi2; - volatile uint32_t ssi1; - volatile uint32_t cspi2; - volatile uint32_t cspi1; - volatile uint32_t ata; - volatile uint32_t mbx; - volatile uint32_t cspi3; - volatile uint32_t uart3; - volatile uint32_t iim; - volatile uint32_t sim1; - volatile uint32_t sim2; - volatile uint32_t rnga; - volatile uint32_t evtmon; - volatile uint32_t kpp; - volatile uint32_t rtc; - volatile uint32_t pwn; - volatile uint32_t epit2; - volatile uint32_t epit1; - volatile uint32_t gpt; - volatile uint32_t pwr_fail; - volatile uint32_t ccm_dvfs; - volatile uint32_t uart2; - volatile uint32_t nandfc; - volatile uint32_t sdma; - volatile uint32_t usb_host1; - volatile uint32_t usb_host2; - volatile uint32_t usb_otg; - volatile uint32_t reserved3; - volatile uint32_t mshc1; - volatile uint32_t mshc2; - volatile uint32_t ipu_err; - volatile uint32_t ipu; - volatile uint32_t reserved4; - volatile uint32_t reserved5; - volatile uint32_t uart1; - volatile uint32_t uart4; - volatile uint32_t uart5; - volatile uint32_t etc_irq; - volatile uint32_t scc_scm; - volatile uint32_t scc_smn; - volatile uint32_t gpio2; - volatile uint32_t gpio1; - volatile uint32_t ccm_clk; - volatile uint32_t pcmcia; - volatile uint32_t wdog; - volatile uint32_t gpio3; - volatile uint32_t reserved6; - volatile uint32_t ext_pwmg; - volatile uint32_t ext_temp; - volatile uint32_t ext_sense1; - volatile uint32_t ext_sense2; - volatile uint32_t ext_wdog; - volatile uint32_t ext_tv; - }; - volatile uint32_t vector[0x40]; /* 100h */ - }; -}; - -#define INT_PRIO_DEFAULT 7 - -enum INT_TYPE -{ - INT_TYPE_IRQ = 0, - INT_TYPE_FIQ -}; - -enum IMX31_INT_LIST -{ - __IMX31_INT_FIRST = -1, - INT_RESERVED0, INT_RESERVED1, INT_RESERVED2, INT_I2C3, - INT_I2C2, INT_MPEG4_ENCODER, INT_RTIC, INT_FIR, - INT_MMC_SDHC2, INT_MMC_SDHC1, INT_I2C1, INT_SSI2, - INT_SSI1, INT_CSPI2, INT_CSPI1, INT_ATA, - INT_MBX, INT_CSPI3, INT_UART3, INT_IIM, - INT_SIM1, INT_SIM2, INT_RNGA, INT_EVTMON, - INT_KPP, INT_RTC, INT_PWN, INT_EPIT2, - INT_EPIT1, INT_GPT, INT_PWR_FAIL, INT_CCM_DVFS, - INT_UART2, INT_NANDFC, INT_SDMA, INT_USB_HOST1, - INT_USB_HOST2, INT_USB_OTG, INT_RESERVED3, INT_MSHC1, - INT_MSHC2, INT_IPU_ERR, INT_IPU, INT_RESERVED4, - INT_RESERVED5, INT_UART1, INT_UART4, INT_UART5, - INT_ETC_IRQ, INT_SCC_SCM, INT_SCC_SMN, INT_GPIO2, - INT_GPIO1, INT_CCM_CLK, INT_PCMCIA, INT_WDOG, - INT_GPIO3, INT_RESERVED6, INT_EXT_PWMG, INT_EXT_TEMP, - INT_EXT_SENS1, INT_EXT_SENS2, INT_EXT_WDOG, INT_EXT_TV, - INT_ALL -}; - -void avic_init(void); -void avic_enable_int(enum IMX31_INT_LIST ints, enum INT_TYPE intstype, - unsigned long ni_priority, void (*handler)(void)); -void avic_set_int_priority(enum IMX31_INT_LIST ints, - unsigned long ni_priority); -void avic_disable_int(enum IMX31_INT_LIST ints); -void avic_set_int_type(enum IMX31_INT_LIST ints, enum INT_TYPE intstype); - -#endif /* AVIC_IMX31_H */ diff --git a/firmware/target/arm/imx31/gigabeat-s/backlight-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/backlight-gigabeat-s.c new file mode 100644 index 0000000000..ec7bf7e8a9 --- /dev/null +++ b/firmware/target/arm/imx31/gigabeat-s/backlight-gigabeat-s.c @@ -0,0 +1,214 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006 by Linus Nielsen Feltzing + * + * 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 "backlight.h" +#include "mc13783.h" +#include "backlight-target.h" +#include "lcd.h" + +#ifdef HAVE_BACKLIGHT_BRIGHTNESS +/* Table that uses combinations of current level and pwm fraction to get + * as many uniquely-visible brightness levels as possible. The lowest current + * level for any average current is used even though many combinations give + * duplicate values. Current (I) values are in mA. */ +static const struct +{ + unsigned char md; + unsigned char pwm; +} led_md_pwm_table[] = +{ + /* I-level PWM(x/15) I-Avg */ + { 0, 0 }, /* 0 0 0.0 */ + { 1, 1 }, /* 3 1 0.2 */ + { 1, 2 }, /* 3 2 0.4 */ + { 1, 3 }, /* 3 3 0.6 */ + { 1, 4 }, /* 3 4 0.8 */ + { 1, 5 }, /* 3 5 1.0 */ + { 1, 6 }, /* 3 6 1.2 */ + { 1, 7 }, /* 3 7 1.4 */ + { 1, 8 }, /* 3 8 1.6 */ + { 1, 9 }, /* 3 9 1.8 */ + { 1, 10 }, /* 3 10 2.0 */ + { 1, 11 }, /* 3 11 2.2 */ + { 1, 12 }, /* 3 12 2.4 */ /* default */ + { 1, 13 }, /* 3 13 2.6 */ + { 1, 14 }, /* 3 14 2.8 */ + { 1, 15 }, /* 3 15 3.0 */ + { 2, 9 }, /* 6 9 3.6 */ + { 2, 10 }, /* 6 10 4.0 */ + { 2, 11 }, /* 6 11 4.4 */ + { 2, 12 }, /* 6 12 4.8 */ + { 2, 13 }, /* 6 13 5.2 */ + { 2, 14 }, /* 6 14 5.6 */ + { 2, 15 }, /* 6 15 6.0 */ + { 3, 11 }, /* 9 11 6.6 */ + { 3, 12 }, /* 9 12 7.2 */ + /* Anything higher is just too much */ +}; +#endif /* HAVE_BACKLIGHT_BRIGHTNESS */ + +/* Bits always combined with ramping bits */ +#define MC13783_LED_CONTROL0_BITS \ + (MC13783_BOOSTEN | MC13783_ABMODE_MONCH_LEDMD1234 | \ + MC13783_ABREF_400MV) + +static struct mutex backlight_mutex; /* Block brightness change while + * setting up fading */ +static bool backlight_on_status = true; /* Is on or off? */ +static uint32_t backlight_pwm_bits; /* Final PWM setting for fade-in */ + +/* Backlight ramping settings */ +static uint32_t led_ramp_mask = MC13783_LEDMDRAMPDOWN | MC13783_LEDMDRAMPUP; + +bool _backlight_init(void) +{ + mutex_init(&backlight_mutex); + + /* Set default LED register value */ + mc13783_write(MC13783_LED_CONTROL0, + MC13783_LED_CONTROL0_BITS | MC13783_LEDEN); + +#ifdef HAVE_BACKLIGHT_BRIGHTNESS + /* Our PWM and I-Level is different than retailos (but same apparent + * brightness), so init to our default. */ + _backlight_set_brightness(DEFAULT_BRIGHTNESS_SETTING); +#else + /* Use default PWM */ + backlight_pwm_bits = mc13783_read(MC13783_LED_CONTROL2) & MC13783_LEDMDDC; +#endif + + return true; +} + +void backlight_set_fade_out(bool value) +{ + if (value) + led_ramp_mask |= MC13783_LEDMDRAMPDOWN; + else + led_ramp_mask &= ~MC13783_LEDMDRAMPDOWN; +} + +void backlight_set_fade_in(bool value) +{ + if (value) + led_ramp_mask |= MC13783_LEDMDRAMPUP; + else + led_ramp_mask &= ~MC13783_LEDMDRAMPUP; +} + +void _backlight_on(void) +{ + static const char regs[2] = + { + MC13783_LED_CONTROL0, + MC13783_LED_CONTROL2 + }; + + uint32_t data[2]; + + mutex_lock(&backlight_mutex); + +#ifdef HAVE_LCD_ENABLE + lcd_enable(true); +#endif + + /* Set/clear LEDRAMPUP bit, clear LEDRAMPDOWN bit, + * Ensure LED supply is on. */ + data[0] = MC13783_LED_CONTROL0_BITS | MC13783_LEDEN; + + if (!backlight_on_status) + data[0] |= led_ramp_mask & MC13783_LEDMDRAMPUP; + + backlight_on_status = true; + + /* Specify final PWM setting */ + data[1] = mc13783_read(MC13783_LED_CONTROL2); + + if (data[1] != MC13783_DATA_ERROR) + { + data[1] &= ~MC13783_LEDMDDC; + data[1] |= backlight_pwm_bits; + + /* Write regs within 30us of each other (requires single xfer) */ + mc13783_write_regset(regs, data, 2); + } + + mutex_unlock(&backlight_mutex); +} + +void _backlight_off(void) +{ + uint32_t ctrl0 = MC13783_LED_CONTROL0_BITS | MC13783_LEDEN; + + mutex_lock(&backlight_mutex); + + if (backlight_on_status) + ctrl0 |= led_ramp_mask & MC13783_LEDMDRAMPDOWN; + + backlight_on_status = false; + + /* Set/clear LEDRAMPDOWN bit, clear LEDRAMPUP bit */ + mc13783_write(MC13783_LED_CONTROL0, ctrl0); + + /* Wait 100us - 500ms */ + sleep(HZ/100); + + /* Write final PWM setting */ + mc13783_write_masked(MC13783_LED_CONTROL2, + 0 << MC13783_LEDMDDC_POS, + MC13783_LEDMDDC); + + mutex_unlock(&backlight_mutex); +} + +#ifdef HAVE_BACKLIGHT_BRIGHTNESS +/* Assumes that the backlight has been initialized - parameter should + * already be range-checked in public interface. */ +void _backlight_set_brightness(int brightness) +{ + uint32_t md; + + mutex_lock(&backlight_mutex); + + md = led_md_pwm_table[brightness].md; + backlight_pwm_bits = backlight_on_status ? + (led_md_pwm_table[brightness].pwm << MC13783_LEDMDDC_POS) : 0; + + mc13783_write_masked(MC13783_LED_CONTROL2, + (md << MC13783_LEDMD_POS) | backlight_pwm_bits, + MC13783_LEDMD | MC13783_LEDMDDC); + + mutex_unlock(&backlight_mutex); +} +#endif /* HAVE_BACKLIGHT_BRIGHTNESS */ + +#ifdef HAVE_LCD_SLEEP +/* Turn off LED supply */ +void _backlight_lcd_sleep(void) +{ + mutex_lock(&backlight_mutex); + + mc13783_clear(MC13783_LED_CONTROL0, MC13783_LEDEN); + + mutex_unlock(&backlight_mutex); +} +#endif diff --git a/firmware/target/arm/imx31/gigabeat-s/backlight-imx31.c b/firmware/target/arm/imx31/gigabeat-s/backlight-imx31.c deleted file mode 100644 index ec7bf7e8a9..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/backlight-imx31.c +++ /dev/null @@ -1,214 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2006 by Linus Nielsen Feltzing - * - * 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 "backlight.h" -#include "mc13783.h" -#include "backlight-target.h" -#include "lcd.h" - -#ifdef HAVE_BACKLIGHT_BRIGHTNESS -/* Table that uses combinations of current level and pwm fraction to get - * as many uniquely-visible brightness levels as possible. The lowest current - * level for any average current is used even though many combinations give - * duplicate values. Current (I) values are in mA. */ -static const struct -{ - unsigned char md; - unsigned char pwm; -} led_md_pwm_table[] = -{ - /* I-level PWM(x/15) I-Avg */ - { 0, 0 }, /* 0 0 0.0 */ - { 1, 1 }, /* 3 1 0.2 */ - { 1, 2 }, /* 3 2 0.4 */ - { 1, 3 }, /* 3 3 0.6 */ - { 1, 4 }, /* 3 4 0.8 */ - { 1, 5 }, /* 3 5 1.0 */ - { 1, 6 }, /* 3 6 1.2 */ - { 1, 7 }, /* 3 7 1.4 */ - { 1, 8 }, /* 3 8 1.6 */ - { 1, 9 }, /* 3 9 1.8 */ - { 1, 10 }, /* 3 10 2.0 */ - { 1, 11 }, /* 3 11 2.2 */ - { 1, 12 }, /* 3 12 2.4 */ /* default */ - { 1, 13 }, /* 3 13 2.6 */ - { 1, 14 }, /* 3 14 2.8 */ - { 1, 15 }, /* 3 15 3.0 */ - { 2, 9 }, /* 6 9 3.6 */ - { 2, 10 }, /* 6 10 4.0 */ - { 2, 11 }, /* 6 11 4.4 */ - { 2, 12 }, /* 6 12 4.8 */ - { 2, 13 }, /* 6 13 5.2 */ - { 2, 14 }, /* 6 14 5.6 */ - { 2, 15 }, /* 6 15 6.0 */ - { 3, 11 }, /* 9 11 6.6 */ - { 3, 12 }, /* 9 12 7.2 */ - /* Anything higher is just too much */ -}; -#endif /* HAVE_BACKLIGHT_BRIGHTNESS */ - -/* Bits always combined with ramping bits */ -#define MC13783_LED_CONTROL0_BITS \ - (MC13783_BOOSTEN | MC13783_ABMODE_MONCH_LEDMD1234 | \ - MC13783_ABREF_400MV) - -static struct mutex backlight_mutex; /* Block brightness change while - * setting up fading */ -static bool backlight_on_status = true; /* Is on or off? */ -static uint32_t backlight_pwm_bits; /* Final PWM setting for fade-in */ - -/* Backlight ramping settings */ -static uint32_t led_ramp_mask = MC13783_LEDMDRAMPDOWN | MC13783_LEDMDRAMPUP; - -bool _backlight_init(void) -{ - mutex_init(&backlight_mutex); - - /* Set default LED register value */ - mc13783_write(MC13783_LED_CONTROL0, - MC13783_LED_CONTROL0_BITS | MC13783_LEDEN); - -#ifdef HAVE_BACKLIGHT_BRIGHTNESS - /* Our PWM and I-Level is different than retailos (but same apparent - * brightness), so init to our default. */ - _backlight_set_brightness(DEFAULT_BRIGHTNESS_SETTING); -#else - /* Use default PWM */ - backlight_pwm_bits = mc13783_read(MC13783_LED_CONTROL2) & MC13783_LEDMDDC; -#endif - - return true; -} - -void backlight_set_fade_out(bool value) -{ - if (value) - led_ramp_mask |= MC13783_LEDMDRAMPDOWN; - else - led_ramp_mask &= ~MC13783_LEDMDRAMPDOWN; -} - -void backlight_set_fade_in(bool value) -{ - if (value) - led_ramp_mask |= MC13783_LEDMDRAMPUP; - else - led_ramp_mask &= ~MC13783_LEDMDRAMPUP; -} - -void _backlight_on(void) -{ - static const char regs[2] = - { - MC13783_LED_CONTROL0, - MC13783_LED_CONTROL2 - }; - - uint32_t data[2]; - - mutex_lock(&backlight_mutex); - -#ifdef HAVE_LCD_ENABLE - lcd_enable(true); -#endif - - /* Set/clear LEDRAMPUP bit, clear LEDRAMPDOWN bit, - * Ensure LED supply is on. */ - data[0] = MC13783_LED_CONTROL0_BITS | MC13783_LEDEN; - - if (!backlight_on_status) - data[0] |= led_ramp_mask & MC13783_LEDMDRAMPUP; - - backlight_on_status = true; - - /* Specify final PWM setting */ - data[1] = mc13783_read(MC13783_LED_CONTROL2); - - if (data[1] != MC13783_DATA_ERROR) - { - data[1] &= ~MC13783_LEDMDDC; - data[1] |= backlight_pwm_bits; - - /* Write regs within 30us of each other (requires single xfer) */ - mc13783_write_regset(regs, data, 2); - } - - mutex_unlock(&backlight_mutex); -} - -void _backlight_off(void) -{ - uint32_t ctrl0 = MC13783_LED_CONTROL0_BITS | MC13783_LEDEN; - - mutex_lock(&backlight_mutex); - - if (backlight_on_status) - ctrl0 |= led_ramp_mask & MC13783_LEDMDRAMPDOWN; - - backlight_on_status = false; - - /* Set/clear LEDRAMPDOWN bit, clear LEDRAMPUP bit */ - mc13783_write(MC13783_LED_CONTROL0, ctrl0); - - /* Wait 100us - 500ms */ - sleep(HZ/100); - - /* Write final PWM setting */ - mc13783_write_masked(MC13783_LED_CONTROL2, - 0 << MC13783_LEDMDDC_POS, - MC13783_LEDMDDC); - - mutex_unlock(&backlight_mutex); -} - -#ifdef HAVE_BACKLIGHT_BRIGHTNESS -/* Assumes that the backlight has been initialized - parameter should - * already be range-checked in public interface. */ -void _backlight_set_brightness(int brightness) -{ - uint32_t md; - - mutex_lock(&backlight_mutex); - - md = led_md_pwm_table[brightness].md; - backlight_pwm_bits = backlight_on_status ? - (led_md_pwm_table[brightness].pwm << MC13783_LEDMDDC_POS) : 0; - - mc13783_write_masked(MC13783_LED_CONTROL2, - (md << MC13783_LEDMD_POS) | backlight_pwm_bits, - MC13783_LEDMD | MC13783_LEDMDDC); - - mutex_unlock(&backlight_mutex); -} -#endif /* HAVE_BACKLIGHT_BRIGHTNESS */ - -#ifdef HAVE_LCD_SLEEP -/* Turn off LED supply */ -void _backlight_lcd_sleep(void) -{ - mutex_lock(&backlight_mutex); - - mc13783_clear(MC13783_LED_CONTROL0, MC13783_LEDEN); - - mutex_unlock(&backlight_mutex); -} -#endif diff --git a/firmware/target/arm/imx31/gigabeat-s/button-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/button-gigabeat-s.c new file mode 100644 index 0000000000..ae158b811d --- /dev/null +++ b/firmware/target/arm/imx31/gigabeat-s/button-gigabeat-s.c @@ -0,0 +1,242 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006 by Linus Nielsen Feltzing + * + * 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 "cpu.h" +#include "system.h" +#include "button.h" +#include "backlight.h" +#include "backlight-target.h" +#include "avic-imx31.h" +#include "ccm-imx31.h" +#include "mc13783.h" + +/* 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 hold_button = false; +#ifdef BOOTLOADER +static bool initialized = false; +#else +static bool hold_button_old = false; +#endif +#define _button_hold() (GPIO3_DR & 0x10) + +static __attribute__((interrupt("IRQ"))) void KPP_HANDLER(void) +{ + static const struct key_mask_shift + { + uint8_t mask; + uint8_t shift; + } kms[3] = + { + { 0x1f, 0 }, /* BUTTON_LEFT...BUTTON_SELECT */ + { 0x03, 5 }, /* BUTTON_BACK...BUTTON_MENU */ + { 0x1f, 7 }, /* BUTTON_VOL_UP...BUTTON_NEXT */ + }; + + int col; + /* Power button is handled separately on PMIC, remote read in headphone + * jack driver. */ +#ifdef HAVE_HEADPHONE_DETECTION + int button = int_btn & (BUTTON_POWER | BUTTON_REMOTE); +#else + int button = int_btn & BUTTON_POWER; +#endif + + int oldlevel = disable_irq_save(); + + /* 1. Disable both (depress and release) keypad interrupts. */ + KPP_KPSR &= ~(KPP_KPSR_KRIE | KPP_KPSR_KDIE); + + for (col = 0; col < 3; col++) /* Col */ + { + /* 2. Write 1s to KPDR[10:8] setting column data to 1s */ + KPP_KPDR |= (0x7 << 8); + + /* 3. Configure columns as totem pole outputs(for quick + * discharging of keypad capacitance) */ + KPP_KPCR &= ~(0x7 << 8); + + /* Give the columns time to discharge */ + udelay(2); + + /* 4. Configure columns as open-drain */ + KPP_KPCR |= (0x7 << 8); + + /* 5. Write a single column to 0, others to 1. + * 6. Sample row inputs and save data. Multiple key presses + * can be detected on a single column. + * 7. Repeat steps 2 - 6 for remaining columns. */ + + /* Col bit starts at 8th bit in KPDR */ + KPP_KPDR &= ~(0x100 << col); + + /* Delay added to avoid propagating the 0 from column to row + * when scanning. */ + udelay(2); + + /* Read row input */ + button |= (~KPP_KPDR & kms[col].mask) << kms[col].shift; + } + + /* 8. Return all columns to 0 in preparation for standby mode. */ + KPP_KPDR &= ~(0x7 << 8); + + /* 9. Clear KPKD and KPKR status bit(s) by writing to a .1., + * set the KPKR synchronizer chain by writing "1" to KRSS register, + * clear the KPKD synchronizer chain by writing "1" to KDSC register */ + KPP_KPSR = KPP_KPSR_KRSS | KPP_KPSR_KDSC | KPP_KPSR_KPKR | KPP_KPSR_KPKD; + + /* 10. Re-enable the appropriate keypad interrupt(s) so that the KDIE + * detects a key hold condition, or the KRIE detects a key-release + * event. */ + if ((button & ~BUTTON_POWER) != BUTTON_NONE) + KPP_KPSR |= KPP_KPSR_KRIE; + else + KPP_KPSR |= KPP_KPSR_KDIE; + + int_btn = button; + + restore_irq(oldlevel); +} + +bool button_hold(void) +{ + return _button_hold(); +} + +#ifdef HAVE_HEADPHONE_DETECTION +/* Headphone driver pushes the data here */ +void button_headphone_set(int button) +{ + int oldstatus = disable_irq_save(); + int_btn = (int_btn & ~BUTTON_REMOTE) | button; + restore_irq(oldstatus); +} +#endif + +int button_read_device(void) +{ + /* Simple poll of GPIO status */ + hold_button = _button_hold(); + +#ifndef BOOTLOADER + /* Backlight hold handling */ + if (hold_button != hold_button_old) + { + hold_button_old = hold_button; + backlight_hold_changed(hold_button); + } +#endif + + /* Enable the keypad interrupt to cause it to fire if a key is down. + * KPP_HANDLER will clear and disable it after the scan. If no key + * is depressed then this bit will already be set in waiting for the + * first key down event. */ + KPP_KPSR |= KPP_KPSR_KDIE; + +#ifdef HAVE_HEADPHONE_DETECTION + /* If hold, ignore any pressed button. Remote has its own hold + * switch, so return state regardless. */ + return hold_button ? (int_btn & BUTTON_REMOTE) : int_btn; +#else + /* If hold, ignore any pressed button. */ + return hold_button ? BUTTON_NONE : int_btn; +#endif +} + +/* This is called from the mc13783 interrupt thread */ +void button_power_event(void) +{ + bool pressed = + (mc13783_read(MC13783_INTERRUPT_SENSE1) & MC13783_ONOFD1S) == 0; + + /* 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); +} + +void button_init_device(void) +{ +#ifdef BOOTLOADER + /* Can be called more than once in the bootloader */ + if (initialized) + return; + + initialized = true; +#endif + + /* Enable keypad clock */ + ccm_module_clock_gating(CG_KPP, CGM_ON_RUN_WAIT); + + /* 1. Enable number of rows in keypad (KPCR[4:0]) + * + * Configure the rows/cols in KPP + * LSB nybble in KPP is for 5 rows + * MSB nybble in KPP is for 3 cols */ + KPP_KPCR |= 0x1f; + + /* 2. Write 0's to KPDR[10:8] */ + KPP_KPDR &= ~(0x7 << 8); + + /* 3. Configure the keypad columns as open-drain (KPCR[10:8]). */ + KPP_KPCR |= (0x7 << 8); + + /* 4. Configure columns as output, rows as input (KDDR[10:8,4:0]) */ + KPP_KDDR = (KPP_KDDR | (0x7 << 8)) & ~0x1f; + + /* 5. Clear the KPKD Status Flag and Synchronizer chain. + * 6. Set the KDIE control bit bit. */ + KPP_KPSR = KPP_KPSR_KDIE | KPP_KPSR_KRSS | KPP_KPSR_KDSC | KPP_KPSR_KPKD; + + avic_enable_int(INT_KPP, INT_TYPE_IRQ, INT_PRIO_DEFAULT, KPP_HANDLER); + + button_power_event(); + mc13783_enable_event(MC13783_ONOFD1_EVENT); + +#ifdef HAVE_HEADPHONE_DETECTION + headphone_init(); +#endif +} + +#ifdef BUTTON_DRIVER_CLOSE +void button_close_device(void) +{ + int oldlevel = disable_irq_save(); + + avic_disable_int(INT_KPP); + KPP_KPSR &= ~(KPP_KPSR_KRIE | KPP_KPSR_KDIE); + int_btn = BUTTON_NONE; + + restore_irq(oldlevel); +} +#endif /* BUTTON_DRIVER_CLOSE */ diff --git a/firmware/target/arm/imx31/gigabeat-s/button-imx31.c b/firmware/target/arm/imx31/gigabeat-s/button-imx31.c deleted file mode 100644 index ae158b811d..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/button-imx31.c +++ /dev/null @@ -1,242 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2006 by Linus Nielsen Feltzing - * - * 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 "cpu.h" -#include "system.h" -#include "button.h" -#include "backlight.h" -#include "backlight-target.h" -#include "avic-imx31.h" -#include "ccm-imx31.h" -#include "mc13783.h" - -/* 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 hold_button = false; -#ifdef BOOTLOADER -static bool initialized = false; -#else -static bool hold_button_old = false; -#endif -#define _button_hold() (GPIO3_DR & 0x10) - -static __attribute__((interrupt("IRQ"))) void KPP_HANDLER(void) -{ - static const struct key_mask_shift - { - uint8_t mask; - uint8_t shift; - } kms[3] = - { - { 0x1f, 0 }, /* BUTTON_LEFT...BUTTON_SELECT */ - { 0x03, 5 }, /* BUTTON_BACK...BUTTON_MENU */ - { 0x1f, 7 }, /* BUTTON_VOL_UP...BUTTON_NEXT */ - }; - - int col; - /* Power button is handled separately on PMIC, remote read in headphone - * jack driver. */ -#ifdef HAVE_HEADPHONE_DETECTION - int button = int_btn & (BUTTON_POWER | BUTTON_REMOTE); -#else - int button = int_btn & BUTTON_POWER; -#endif - - int oldlevel = disable_irq_save(); - - /* 1. Disable both (depress and release) keypad interrupts. */ - KPP_KPSR &= ~(KPP_KPSR_KRIE | KPP_KPSR_KDIE); - - for (col = 0; col < 3; col++) /* Col */ - { - /* 2. Write 1s to KPDR[10:8] setting column data to 1s */ - KPP_KPDR |= (0x7 << 8); - - /* 3. Configure columns as totem pole outputs(for quick - * discharging of keypad capacitance) */ - KPP_KPCR &= ~(0x7 << 8); - - /* Give the columns time to discharge */ - udelay(2); - - /* 4. Configure columns as open-drain */ - KPP_KPCR |= (0x7 << 8); - - /* 5. Write a single column to 0, others to 1. - * 6. Sample row inputs and save data. Multiple key presses - * can be detected on a single column. - * 7. Repeat steps 2 - 6 for remaining columns. */ - - /* Col bit starts at 8th bit in KPDR */ - KPP_KPDR &= ~(0x100 << col); - - /* Delay added to avoid propagating the 0 from column to row - * when scanning. */ - udelay(2); - - /* Read row input */ - button |= (~KPP_KPDR & kms[col].mask) << kms[col].shift; - } - - /* 8. Return all columns to 0 in preparation for standby mode. */ - KPP_KPDR &= ~(0x7 << 8); - - /* 9. Clear KPKD and KPKR status bit(s) by writing to a .1., - * set the KPKR synchronizer chain by writing "1" to KRSS register, - * clear the KPKD synchronizer chain by writing "1" to KDSC register */ - KPP_KPSR = KPP_KPSR_KRSS | KPP_KPSR_KDSC | KPP_KPSR_KPKR | KPP_KPSR_KPKD; - - /* 10. Re-enable the appropriate keypad interrupt(s) so that the KDIE - * detects a key hold condition, or the KRIE detects a key-release - * event. */ - if ((button & ~BUTTON_POWER) != BUTTON_NONE) - KPP_KPSR |= KPP_KPSR_KRIE; - else - KPP_KPSR |= KPP_KPSR_KDIE; - - int_btn = button; - - restore_irq(oldlevel); -} - -bool button_hold(void) -{ - return _button_hold(); -} - -#ifdef HAVE_HEADPHONE_DETECTION -/* Headphone driver pushes the data here */ -void button_headphone_set(int button) -{ - int oldstatus = disable_irq_save(); - int_btn = (int_btn & ~BUTTON_REMOTE) | button; - restore_irq(oldstatus); -} -#endif - -int button_read_device(void) -{ - /* Simple poll of GPIO status */ - hold_button = _button_hold(); - -#ifndef BOOTLOADER - /* Backlight hold handling */ - if (hold_button != hold_button_old) - { - hold_button_old = hold_button; - backlight_hold_changed(hold_button); - } -#endif - - /* Enable the keypad interrupt to cause it to fire if a key is down. - * KPP_HANDLER will clear and disable it after the scan. If no key - * is depressed then this bit will already be set in waiting for the - * first key down event. */ - KPP_KPSR |= KPP_KPSR_KDIE; - -#ifdef HAVE_HEADPHONE_DETECTION - /* If hold, ignore any pressed button. Remote has its own hold - * switch, so return state regardless. */ - return hold_button ? (int_btn & BUTTON_REMOTE) : int_btn; -#else - /* If hold, ignore any pressed button. */ - return hold_button ? BUTTON_NONE : int_btn; -#endif -} - -/* This is called from the mc13783 interrupt thread */ -void button_power_event(void) -{ - bool pressed = - (mc13783_read(MC13783_INTERRUPT_SENSE1) & MC13783_ONOFD1S) == 0; - - /* 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); -} - -void button_init_device(void) -{ -#ifdef BOOTLOADER - /* Can be called more than once in the bootloader */ - if (initialized) - return; - - initialized = true; -#endif - - /* Enable keypad clock */ - ccm_module_clock_gating(CG_KPP, CGM_ON_RUN_WAIT); - - /* 1. Enable number of rows in keypad (KPCR[4:0]) - * - * Configure the rows/cols in KPP - * LSB nybble in KPP is for 5 rows - * MSB nybble in KPP is for 3 cols */ - KPP_KPCR |= 0x1f; - - /* 2. Write 0's to KPDR[10:8] */ - KPP_KPDR &= ~(0x7 << 8); - - /* 3. Configure the keypad columns as open-drain (KPCR[10:8]). */ - KPP_KPCR |= (0x7 << 8); - - /* 4. Configure columns as output, rows as input (KDDR[10:8,4:0]) */ - KPP_KDDR = (KPP_KDDR | (0x7 << 8)) & ~0x1f; - - /* 5. Clear the KPKD Status Flag and Synchronizer chain. - * 6. Set the KDIE control bit bit. */ - KPP_KPSR = KPP_KPSR_KDIE | KPP_KPSR_KRSS | KPP_KPSR_KDSC | KPP_KPSR_KPKD; - - avic_enable_int(INT_KPP, INT_TYPE_IRQ, INT_PRIO_DEFAULT, KPP_HANDLER); - - button_power_event(); - mc13783_enable_event(MC13783_ONOFD1_EVENT); - -#ifdef HAVE_HEADPHONE_DETECTION - headphone_init(); -#endif -} - -#ifdef BUTTON_DRIVER_CLOSE -void button_close_device(void) -{ - int oldlevel = disable_irq_save(); - - avic_disable_int(INT_KPP); - KPP_KPSR &= ~(KPP_KPSR_KRIE | KPP_KPSR_KDIE); - int_btn = BUTTON_NONE; - - restore_irq(oldlevel); -} -#endif /* BUTTON_DRIVER_CLOSE */ diff --git a/firmware/target/arm/imx31/gigabeat-s/gpio-imx31.c b/firmware/target/arm/imx31/gigabeat-s/gpio-imx31.c deleted file mode 100644 index 944f70eae3..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/gpio-imx31.c +++ /dev/null @@ -1,213 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (c) 2008 by Michael Sevakis - * - * IMX31 GPIO event manager - * - * 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" - -/* 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 -{ - struct gpio_map * const 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 = (struct gpio_map *)GPIO1_BASE_ADDR, - .ints = INT_GPIO1, - .handler = GPIO1_HANDLER, - }, -#endif -#if (GPIO_EVENT_MASK & USE_GPIO2_EVENTS) - { - .base = (struct gpio_map *)GPIO2_BASE_ADDR, - .ints = INT_GPIO2, - .handler = GPIO2_HANDLER, - }, -#endif -#if (GPIO_EVENT_MASK & USE_GPIO3_EVENTS) - { - .base = (struct gpio_map *)GPIO3_BASE_ADDR, - .ints = INT_GPIO3, - .handler = GPIO3_HANDLER, - }, -#endif -}; - -static void gpio_call_events(const struct gpio_module_descriptor * const desc) -{ - const struct gpio_event_list * const list = desc->list; - struct gpio_map * const base = desc->base; - const struct gpio_event * event, *event_last; - - /* Intersect pending and unmasked bits */ - uint32_t pnd = base->isr & base->imr; - - event = list->events; - event_last = event + list->count; - - /* Call each event handler in order */ - /* .count is surely expected to be > 0 */ - do - { - uint32_t mask = event->mask; - - 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(); - } -} - -#if (GPIO_EVENT_MASK & USE_GPIO1_EVENTS) -static __attribute__((interrupt("IRQ"))) void GPIO1_HANDLER(void) -{ - gpio_call_events(&gpio_descs[GPIO1_NUM]); -} -#endif - -#if (GPIO_EVENT_MASK & USE_GPIO2_EVENTS) -static __attribute__((interrupt("IRQ"))) void GPIO2_HANDLER(void) -{ - gpio_call_events(&gpio_descs[GPIO2_NUM]); -} -#endif - -#if (GPIO_EVENT_MASK & USE_GPIO3_EVENTS) -static __attribute__((interrupt("IRQ"))) void GPIO3_HANDLER(void) -{ - gpio_call_events(&gpio_descs[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_event_ids id) -{ - const struct gpio_module_descriptor * const desc = &gpio_descs[id >> 5]; - const struct gpio_event * const event = &desc->list->events[id & 31]; - struct gpio_map * const base = desc->base; - volatile uint32_t *icr; - uint32_t mask, line; - uint32_t imr; - int shift; - - int oldlevel = disable_irq_save(); - - imr = base->imr; - - if (imr == 0) - { - /* First enabled interrupt for this GPIO */ - avic_enable_int(desc->ints, INT_TYPE_IRQ, desc->list->ints_priority, - desc->handler); - } - - /* Set the line sense */ - line = find_first_set_bit(event->mask); - icr = &base->icr[line >> 4]; - shift = (line & 15) << 1; - mask = GPIO_SENSE_CONFIG_MASK << shift; - - *icr = (*icr & ~mask) | ((event->sense << shift) & mask); - - /* Unmask the line */ - base->imr = imr | event->mask; - - restore_irq(oldlevel); - - return true; -} - -void gpio_disable_event(enum gpio_event_ids id) -{ - const struct gpio_module_descriptor * const desc = &gpio_descs[id >> 5]; - const struct gpio_event * const event = &desc->list->events[id & 31]; - struct gpio_map * const base = desc->base; - uint32_t imr; - - int oldlevel = disable_irq_save(); - - /* Remove bit from mask */ - imr = base->imr & ~event->mask; - - /* Mask the line */ - base->imr = 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 deleted file mode 100644 index 72956d4efa..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/gpio-imx31.h +++ /dev/null @@ -1,114 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (c) 2008 by Michael Sevakis - * - * 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. - * - ****************************************************************************/ -#ifndef GPIO_IMX31_H -#define GPIO_IMX31_H - -/* Static registration mechanism for imx31 GPIO interrupts */ -#define USE_GPIO1_EVENTS (1 << 0) -#define USE_GPIO2_EVENTS (1 << 1) -#define USE_GPIO3_EVENTS (1 << 2) - -/* Module indexes defined by which GPIO modules are used */ -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, -}; - -/* 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 -}; - -#include "gpio-target.h" - -/* 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 - -/* Register map for each module */ -struct gpio_map -{ - volatile uint32_t dr; /* 00h */ - volatile uint32_t gdir; /* 04h */ - volatile uint32_t psr; /* 08h */ - union - { - struct - { - volatile uint32_t icr1; /* 0Ch */ - volatile uint32_t icr2; /* 10h */ - }; - volatile uint32_t icr[2]; /* 0Ch */ - }; - volatile uint32_t imr; /* 14h */ - volatile uint32_t isr; /* 18h */ -}; - -/* Pending events will be called in array order which allows easy - * pioritization */ - -/* Describes a single event for a pin */ -struct gpio_event -{ - uint32_t mask; /* mask: 1 << (0...31) */ - enum gpio_int_sense_enum sense; /* Type of sense */ - void (*callback)(void); /* Callback function */ -}; - -/* Describes the events attached to a port */ -struct gpio_event_list -{ - int ints_priority; /* Interrupt priority for this GPIO */ - unsigned count; /* Count of events for the module */ - const struct gpio_event *events; /* List of events */ -}; - -void gpio_init(void); -bool gpio_enable_event(enum gpio_event_ids id); -void gpio_disable_event(enum gpio_event_ids id); - -#endif /* GPIO_IMX31_H */ diff --git a/firmware/target/arm/imx31/gigabeat-s/i2c-imx31.c b/firmware/target/arm/imx31/gigabeat-s/i2c-imx31.c deleted file mode 100644 index 1ffdce38ea..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/i2c-imx31.c +++ /dev/null @@ -1,338 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2008 by Michael Sevakis - * - * 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 -#include "config.h" -#include "system.h" -#include "kernel.h" -#include "avic-imx31.h" -#include "ccm-imx31.h" -#include "i2c-imx31.h" - -/* Forward interrupt handler declarations */ -#if (I2C_MODULE_MASK & USE_I2C1_MODULE) -static __attribute__((interrupt("IRQ"))) void I2C1_HANDLER(void); -#endif -#if (I2C_MODULE_MASK & USE_I2C2_MODULE) -static __attribute__((interrupt("IRQ"))) void I2C2_HANDLER(void); -#endif -#if (I2C_MODULE_MASK & USE_I2C3_MODULE) -static __attribute__((interrupt("IRQ"))) void I2C3_HANDLER(void); -#endif - -static struct i2c_module_descriptor -{ - struct i2c_map *base; /* Module base address */ - enum IMX31_CG_LIST cg; /* Clock gating index */ - enum IMX31_INT_LIST ints; /* Module interrupt number */ - int enable; /* Enable count */ - void (*handler)(void); /* Module interrupt handler */ - struct mutex m; /* Node mutual-exclusion */ - struct wakeup w; /* I2C done signal */ - unsigned char *addr_data; /* Additional addressing data */ - int addr_count; /* Addressing byte count */ - unsigned char *data; /* TX/RX buffer (actual data) */ - int data_count; /* TX/RX byte count */ - unsigned char addr; /* Address + r/w bit */ -} i2c_descs[I2C_NUM_I2C] = -{ -#if (I2C_MODULE_MASK & USE_I2C1_MODULE) - { - .base = (struct i2c_map *)I2C1_BASE_ADDR, - .cg = CG_I2C1, - .ints = INT_I2C1, - .handler = I2C1_HANDLER, - }, -#endif -#if (I2C_MODULE_MASK & USE_I2C2_MODULE) - { - .base = (struct i2c_map *)I2C2_BASE_ADDR, - .cg = CG_I2C2, - .ints = INT_I2C2, - .handler = I2C2_HANDLER, - }, -#endif -#if (I2C_MODULE_MASK & USE_I2C3_MODULE) - { - .base = (struct i2c_map *)I2C3_BASE_ADDR, - .cg = CG_I2C3, - .ints = INT_I2C3, - .handler = I2C3_HANDLER, - }, -#endif -}; - -static void i2c_interrupt(enum i2c_module_number i2c) -{ - struct i2c_module_descriptor *const desc = &i2c_descs[i2c]; - struct i2c_map * const base = desc->base; - uint16_t i2sr = base->i2sr; - - base->i2sr = 0; /* Clear IIF */ - - if (desc->addr_count >= 0) - { - /* ADDR cycle - either done or more to send */ - if ((i2sr & I2C_I2SR_RXAK) != 0) - { - goto i2c_stop; /* problem */ - } - - if (--desc->addr_count < 0) - { - /* Switching to data cycle */ - if (desc->addr & 0x1) - { - base->i2cr &= ~I2C_I2CR_MTX; /* Switch to RX mode */ - base->i2dr; /* Dummy read */ - return; - } - /* else remaining data is TX - handle below */ - goto i2c_transmit; - } - else - { - base->i2dr = *desc->addr_data++; /* Send next addressing byte */ - return; - } - } - - if (base->i2cr & I2C_I2CR_MTX) - { - /* Transmitting data */ - if ((i2sr & I2C_I2SR_RXAK) == 0) - { -i2c_transmit: - if (desc->data_count > 0) - { - /* More bytes to send, got ACK from previous byte */ - base->i2dr = *desc->data++; - desc->data_count--; - return; - } - } - /* else done or no ACK received */ - } - else - { - /* Receiving data */ - if (--desc->data_count > 0) - { - if (desc->data_count == 1) - { - /* 2nd to Last byte - NACK */ - base->i2cr |= I2C_I2CR_TXAK; - } - - *desc->data++ = base->i2dr; /* Read data from I2DR and store */ - return; - } - else - { - /* Generate STOP signal before reading data */ - base->i2cr &= ~(I2C_I2CR_MSTA | I2C_I2CR_IIEN); - *desc->data++ = base->i2dr; /* Read data from I2DR and store */ - goto i2c_done; - } - } - -i2c_stop: - /* Generate STOP signal */ - base->i2cr &= ~(I2C_I2CR_MSTA | I2C_I2CR_IIEN); -i2c_done: - /* Signal thread we're done */ - wakeup_signal(&desc->w); -} - -#if (I2C_MODULE_MASK & USE_I2C1_MODULE) -static __attribute__((interrupt("IRQ"))) void I2C1_HANDLER(void) -{ - i2c_interrupt(I2C1_NUM); -} -#endif -#if (I2C_MODULE_MASK & USE_I2C2_MODULE) -static __attribute__((interrupt("IRQ"))) void I2C2_HANDLER(void) -{ - i2c_interrupt(I2C2_NUM); -} -#endif -#if (I2C_MODULE_MASK & USE_I2C3_MODULE) -static __attribute__((interrupt("IRQ"))) void I2C3_HANDLER(void) -{ - i2c_interrupt(I2C3_NUM); -} -#endif - -static int i2c_transfer(struct i2c_node * const node, - struct i2c_module_descriptor *const desc) -{ - struct i2c_map * const base = desc->base; - int count = desc->data_count; - uint16_t i2cr; - - /* Make sure bus is idle. */ - while (base->i2sr & I2C_I2SR_IBB); - - /* Set speed */ - base->ifdr = node->ifdr; - - /* Enable module */ - base->i2cr = I2C_I2CR_IEN; - - /* Enable Interrupt, Master */ - i2cr = I2C_I2CR_IEN | I2C_I2CR_IIEN | I2C_I2CR_MTX; - - if ((desc->addr & 0x1) && desc->data_count < 2) - { - /* Receiving less than two bytes - disable ACK generation */ - i2cr |= I2C_I2CR_TXAK; - } - - /* Set config */ - base->i2cr = i2cr; - - /* Generate START */ - base->i2cr = i2cr | I2C_I2CR_MSTA; - - /* Address slave (first byte sent) and begin session. */ - base->i2dr = desc->addr; - - /* Wait for transfer to complete */ - if (wakeup_wait(&desc->w, HZ) == OBJ_WAIT_SUCCEEDED) - { - count -= desc->data_count; - } - else - { - /* Generate STOP if timeout */ - base->i2cr &= ~(I2C_I2CR_MSTA | I2C_I2CR_IIEN); - count = -1; - } - - desc->addr_count = 0; - - return count; -} - -int i2c_read(struct i2c_node *node, int reg, - unsigned char *data, int data_count) -{ - struct i2c_module_descriptor *const desc = &i2c_descs[node->num]; - unsigned char ad[1]; - - mutex_lock(&desc->m); - - desc->addr = (node->addr & 0xfe) | 0x1; /* Slave address/rd */ - - if (reg >= 0) - { - /* Sub-address */ - desc->addr_count = 1; - desc->addr_data = ad; - ad[0] = reg; - } - /* else raw read from slave */ - - desc->data = data; - desc->data_count = data_count; - - data_count = i2c_transfer(node, desc); - - mutex_unlock(&desc->m); - - return data_count; -} - -int i2c_write(struct i2c_node *node, const unsigned char *data, int data_count) -{ - struct i2c_module_descriptor *const desc = &i2c_descs[node->num]; - - mutex_lock(&desc->m); - - desc->addr = node->addr & 0xfe; /* Slave address/wr */ - desc->data = (unsigned char *)data; - desc->data_count = data_count; - - data_count = i2c_transfer(node, desc); - - mutex_unlock(&desc->m); - - return data_count; -} - -void i2c_init(void) -{ - int i; - - /* Do one-time inits for each module that will be used - leave - * module disabled and unclocked until something wants it */ - for (i = 0; i < I2C_NUM_I2C; i++) - { - struct i2c_module_descriptor *const desc = &i2c_descs[i]; - ccm_module_clock_gating(desc->cg, CGM_ON_RUN_WAIT); - mutex_init(&desc->m); - wakeup_init(&desc->w); - desc->base->i2cr = 0; - ccm_module_clock_gating(desc->cg, CGM_OFF); - } -} - -void i2c_enable_node(struct i2c_node *node, bool enable) -{ - struct i2c_module_descriptor *const desc = &i2c_descs[node->num]; - - mutex_lock(&desc->m); - - if (enable) - { - if (++desc->enable == 1) - { - /* First enable */ - ccm_module_clock_gating(desc->cg, CGM_ON_RUN_WAIT); - avic_enable_int(desc->ints, INT_TYPE_IRQ, INT_PRIO_DEFAULT, - desc->handler); - } - } - else - { - if (desc->enable > 0 && --desc->enable == 0) - { - /* Last enable */ - while (desc->base->i2sr & I2C_I2SR_IBB); /* Wait for STOP */ - desc->base->i2cr &= ~I2C_I2CR_IEN; - avic_disable_int(desc->ints); - ccm_module_clock_gating(desc->cg, CGM_OFF); - } - } - - mutex_unlock(&desc->m); -} - -void i2c_lock_node(struct i2c_node *node) -{ - struct i2c_module_descriptor *const desc = &i2c_descs[node->num]; - mutex_lock(&desc->m); -} - -void i2c_unlock_node(struct i2c_node *node) -{ - struct i2c_module_descriptor *const desc = &i2c_descs[node->num]; - mutex_unlock(&desc->m); -} diff --git a/firmware/target/arm/imx31/gigabeat-s/i2c-imx31.h b/firmware/target/arm/imx31/gigabeat-s/i2c-imx31.h deleted file mode 100644 index b36acecfcb..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/i2c-imx31.h +++ /dev/null @@ -1,78 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2007 by Michael Sevakis - * - * 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. - * - ****************************************************************************/ -#ifndef I2C_IMX31_H -#define I2C_IMX31_H - -#include - -/* I2C module usage masks */ -#define USE_I2C1_MODULE (1 << 0) -#define USE_I2C2_MODULE (1 << 1) -#define USE_I2C3_MODULE (1 << 2) - -enum i2c_module_number -{ - __I2C_NUM_START = -1, -#if (I2C_MODULE_MASK & USE_I2C1_MODULE) - I2C1_NUM, -#endif -#if (I2C_MODULE_MASK & USE_I2C2_MODULE) - I2C2_NUM, -#endif -#if (I2C_MODULE_MASK & USE_I2C3_MODULE) - I2C3_NUM, -#endif - I2C_NUM_I2C, -}; - -/* Module interface map structure */ -struct i2c_map -{ - volatile uint16_t iadr; /* 0x00 */ - volatile uint16_t unused1; - volatile uint16_t ifdr; /* 0x04 */ - volatile uint16_t unused2; - volatile uint16_t i2cr; /* 0x08 */ - volatile uint16_t unused3; - volatile uint16_t i2sr; /* 0x0C */ - volatile uint16_t unused4; - volatile uint16_t i2dr; /* 0x10 */ -}; - -struct i2c_node -{ - enum i2c_module_number num; /* Module that this node uses */ - unsigned int ifdr; /* Maximum frequency for node */ - unsigned char addr; /* Slave address on module */ -}; - -void i2c_init(void); -/* Enable or disable the node - modules will be switch on/off accordingly. */ -void i2c_enable_node(struct i2c_node *node, bool enable); -/* If addr < 0, then raw read */ -int i2c_read(struct i2c_node *node, int addr, unsigned char *data, int count); -int i2c_write(struct i2c_node *node, const unsigned char *data, int count); -/* Gain mutually-exclusive access to the node and module to perform multiple - * operations atomically */ -void i2c_lock_node(struct i2c_node *node); -void i2c_unlock_node(struct i2c_node *node); - -#endif /* I2C_IMX31_H */ diff --git a/firmware/target/arm/imx31/gigabeat-s/i2s-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/i2s-gigabeat-s.c new file mode 100644 index 0000000000..c2ec0d6cab --- /dev/null +++ b/firmware/target/arm/imx31/gigabeat-s/i2s-gigabeat-s.c @@ -0,0 +1,45 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 by Michael Sevakis + * + * 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 "i2s.h" + +void i2s_reset(void) +{ + /* How SYSCLK for codec is derived (USBPLL=338.688MHz). + * + * SSI post dividers (SSI2 PODF=4, SSI2 PRE PODF=0): + * 338688000Hz / 5 = 67737600Hz = ssi1_clk + * + * SSI bit clock dividers (DIV2=1, PSR=0, PM=0): + * ssi1_clk / 4 = 16934400Hz = INT_BIT_CLK (MCLK) + * + * WM Codec post divider (MCLKDIV=1.5): + * INT_BIT_CLK (MCLK) / 1.5 = 11289600Hz = 256*fs = SYSCLK + */ + imx31_regmod32(&CCM_PDR1, + ((1-1) << CCM_PDR1_SSI1_PRE_PODF_POS) | + ((5-1) << CCM_PDR1_SSI1_PODF_POS) | + ((8-1) << CCM_PDR1_SSI2_PRE_PODF_POS) | + ((64-1) << CCM_PDR1_SSI2_PODF_POS), + CCM_PDR1_SSI1_PODF | CCM_PDR1_SSI2_PODF | + CCM_PDR1_SSI1_PRE_PODF | CCM_PDR1_SSI2_PRE_PODF); +} diff --git a/firmware/target/arm/imx31/gigabeat-s/i2s-imx31.c b/firmware/target/arm/imx31/gigabeat-s/i2s-imx31.c deleted file mode 100644 index d1c917a209..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/i2s-imx31.c +++ /dev/null @@ -1,26 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2007 by Michael Sevakis - * - * 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 "i2s.h" - -void i2s_reset(void) -{ -} diff --git a/firmware/target/arm/imx31/gigabeat-s/kernel-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/kernel-gigabeat-s.c new file mode 100644 index 0000000000..8e81447bd3 --- /dev/null +++ b/firmware/target/arm/imx31/gigabeat-s/kernel-gigabeat-s.c @@ -0,0 +1,84 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 by Michael Sevakis + * + * 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 "spi-imx31.h" +#include "mc13783.h" +#include "ccm-imx31.h" +#include "sdma-imx31.h" +#include "dvfs_dptc-imx31.h" +#include "kernel.h" +#include "thread.h" + +static __attribute__((interrupt("IRQ"))) void EPIT1_HANDLER(void) +{ + EPITSR1 = EPITSR_OCIF; /* Clear the pending status */ + + /* Run through the list of tick tasks */ + call_tick_tasks(); +} + +void tick_start(unsigned int interval_in_ms) +{ + ccm_module_clock_gating(CG_EPIT1, CGM_ON_RUN_WAIT); /* EPIT1 module + clock ON - before writing + regs! */ + EPITCR1 &= ~(EPITCR_OCIEN | EPITCR_EN); /* Disable the counter */ + CCM_WIMR0 &= ~CCM_WIMR0_IPI_INT_EPIT1; /* Clear wakeup mask */ + + /* mcu_main_clk = 528MHz = 27MHz * 2 * ((9 + 7/9) / 1) + * CLKSRC = ipg_clk = 528MHz / 4 / 2 = 66MHz, + * EPIT Output Disconnected, + * Enabled in wait mode + * Prescale 1/2640 for 25KHz + * Reload from modulus register, + * Compare interrupt enabled, + * Count from load value */ + EPITCR1 = EPITCR_CLKSRC_IPG_CLK | EPITCR_WAITEN | EPITCR_IOVW | + ((2640-1) << EPITCR_PRESCALER_POS) | EPITCR_RLD | + EPITCR_OCIEN | EPITCR_ENMOD; + + EPITLR1 = interval_in_ms*25; /* Count down from interval */ + EPITCMPR1 = 0; /* Event when counter reaches 0 */ + EPITSR1 = EPITSR_OCIF; /* Clear any pending interrupt */ + avic_enable_int(INT_EPIT1, INT_TYPE_IRQ, INT_PRIO_DEFAULT, + EPIT1_HANDLER); + EPITCR1 |= EPITCR_EN; /* Enable the counter */ +} + +void kernel_device_init(void) +{ + sdma_init(); + spi_init(); + mc13783_init(); + dvfs_dptc_start(); +} + +#ifdef BOOTLOADER +void tick_stop(void) +{ + avic_disable_int(INT_EPIT1); /* Disable insterrupt */ + EPITCR1 &= ~(EPITCR_OCIEN | EPITCR_EN); /* Disable counter */ + EPITSR1 = EPITSR_OCIF; /* Clear pending */ + ccm_module_clock_gating(CG_EPIT1, CGM_OFF); /* Turn off module clock */ +} +#endif diff --git a/firmware/target/arm/imx31/gigabeat-s/kernel-imx31.c b/firmware/target/arm/imx31/gigabeat-s/kernel-imx31.c deleted file mode 100644 index 8e81447bd3..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/kernel-imx31.c +++ /dev/null @@ -1,84 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2007 by Michael Sevakis - * - * 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 "spi-imx31.h" -#include "mc13783.h" -#include "ccm-imx31.h" -#include "sdma-imx31.h" -#include "dvfs_dptc-imx31.h" -#include "kernel.h" -#include "thread.h" - -static __attribute__((interrupt("IRQ"))) void EPIT1_HANDLER(void) -{ - EPITSR1 = EPITSR_OCIF; /* Clear the pending status */ - - /* Run through the list of tick tasks */ - call_tick_tasks(); -} - -void tick_start(unsigned int interval_in_ms) -{ - ccm_module_clock_gating(CG_EPIT1, CGM_ON_RUN_WAIT); /* EPIT1 module - clock ON - before writing - regs! */ - EPITCR1 &= ~(EPITCR_OCIEN | EPITCR_EN); /* Disable the counter */ - CCM_WIMR0 &= ~CCM_WIMR0_IPI_INT_EPIT1; /* Clear wakeup mask */ - - /* mcu_main_clk = 528MHz = 27MHz * 2 * ((9 + 7/9) / 1) - * CLKSRC = ipg_clk = 528MHz / 4 / 2 = 66MHz, - * EPIT Output Disconnected, - * Enabled in wait mode - * Prescale 1/2640 for 25KHz - * Reload from modulus register, - * Compare interrupt enabled, - * Count from load value */ - EPITCR1 = EPITCR_CLKSRC_IPG_CLK | EPITCR_WAITEN | EPITCR_IOVW | - ((2640-1) << EPITCR_PRESCALER_POS) | EPITCR_RLD | - EPITCR_OCIEN | EPITCR_ENMOD; - - EPITLR1 = interval_in_ms*25; /* Count down from interval */ - EPITCMPR1 = 0; /* Event when counter reaches 0 */ - EPITSR1 = EPITSR_OCIF; /* Clear any pending interrupt */ - avic_enable_int(INT_EPIT1, INT_TYPE_IRQ, INT_PRIO_DEFAULT, - EPIT1_HANDLER); - EPITCR1 |= EPITCR_EN; /* Enable the counter */ -} - -void kernel_device_init(void) -{ - sdma_init(); - spi_init(); - mc13783_init(); - dvfs_dptc_start(); -} - -#ifdef BOOTLOADER -void tick_stop(void) -{ - avic_disable_int(INT_EPIT1); /* Disable insterrupt */ - EPITCR1 &= ~(EPITCR_OCIEN | EPITCR_EN); /* Disable counter */ - EPITSR1 = EPITSR_OCIF; /* Clear pending */ - ccm_module_clock_gating(CG_EPIT1, CGM_OFF); /* Turn off module clock */ -} -#endif diff --git a/firmware/target/arm/imx31/gigabeat-s/lcd-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/lcd-gigabeat-s.c new file mode 100644 index 0000000000..71d8e4bef4 --- /dev/null +++ b/firmware/target/arm/imx31/gigabeat-s/lcd-gigabeat-s.c @@ -0,0 +1,229 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 by Will Robertson + * + * 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 + +#include "config.h" +#include "cpu.h" +#include "string.h" +#include "lcd.h" +#include "kernel.h" +#include "lcd-target.h" +#include "backlight-target.h" + +#define MAIN_LCD_IDMAC_CHANNEL 14 +#define LCDADDR(x, y) (&lcd_framebuffer[(y)][(x)]) + +static bool lcd_on = true; +static bool lcd_powered = true; +static unsigned lcd_yuv_options = 0; + +/* Copies a rectangle from one framebuffer to another. Can be used in + single transfer mode with width = num pixels, and height = 1 which + allows a full-width rectangle to be copied more efficiently. */ +extern void lcd_copy_buffer_rect(fb_data *dst, const fb_data *src, + int width, int height); + +/* LCD init */ +void lcd_init_device(void) +{ + /* Move the framebuffer */ +#ifdef BOOTLOADER + /* Only do this once to avoid flicker */ + memset(FRAME, 0x00, FRAME_SIZE); +#endif + IPU_IDMAC_CHA_EN &= ~(1ul << MAIN_LCD_IDMAC_CHANNEL); + IPU_IMA_ADDR = ((0x1 << 16) | (MAIN_LCD_IDMAC_CHANNEL << 4)) + (1 << 3); + IPU_IMA_DATA = FRAME_PHYS_ADDR; + IPU_IDMAC_CHA_EN |= (1ul << MAIN_LCD_IDMAC_CHANNEL); +} + +/* Update a fraction of the display. */ +void lcd_update_rect(int x, int y, int width, int height) +{ + fb_data *dst, *src; + + if (!lcd_on) + return; + + if (x + width > LCD_WIDTH) + width = LCD_WIDTH - x; /* Clip right */ + if (x < 0) + width += x, x = 0; /* Clip left */ + if (width <= 0) + return; /* nothing left to do */ + + if (y + height > LCD_HEIGHT) + height = LCD_HEIGHT - y; /* Clip bottom */ + if (y < 0) + height += y, y = 0; /* Clip top */ + if (height <= 0) + return; /* nothing left to do */ + + /* TODO: It may be faster to swap the addresses of lcd_driver_framebuffer + * and lcd_framebuffer */ + dst = (fb_data *)FRAME + LCD_WIDTH*y + x; + src = &lcd_framebuffer[y][x]; + + /* Copy part of the Rockbox framebuffer to the second framebuffer */ + if (width < LCD_WIDTH) + { + /* Not full width - do line-by-line */ + lcd_copy_buffer_rect(dst, src, width, height); + } + else + { + /* Full width - copy as one line */ + lcd_copy_buffer_rect(dst, src, LCD_WIDTH*height, 1); + } +} + +void lcd_sleep(void) +{ + if (lcd_powered) + { + lcd_enable(false); + lcd_powered = false; + IPU_IDMAC_CHA_EN &= ~(1ul << MAIN_LCD_IDMAC_CHANNEL); + _backlight_lcd_sleep(); + } +} + +void lcd_enable(bool state) +{ + if (state == lcd_on) + return; + + if (state) + { + IPU_IDMAC_CHA_EN |= 1ul << MAIN_LCD_IDMAC_CHANNEL; + sleep(HZ/50); + lcd_powered = true; + lcd_on = true; + lcd_update(); + send_event(LCD_EVENT_ACTIVATION, NULL); + } + else + { + lcd_on = false; + } +} + +bool lcd_active(void) +{ + return lcd_on; +} + +/* Update the display. + This must be called after all other LCD functions that change the display. */ +void lcd_update(void) +{ + if (!lcd_on) + return; + + lcd_copy_buffer_rect((fb_data *)FRAME, &lcd_framebuffer[0][0], + LCD_WIDTH*LCD_HEIGHT, 1); +} + +void lcd_yuv_set_options(unsigned options) +{ + lcd_yuv_options = options; +} + +/* Line write helper function for lcd_yuv_blit. Write two lines of yuv420. */ +extern void lcd_write_yuv420_lines(fb_data *dst, + unsigned char const * const src[3], + int width, + int stride); +extern void lcd_write_yuv420_lines_odither(fb_data *dst, + unsigned char const * const src[3], + int width, + int stride, + int x_screen, /* To align dither pattern */ + int y_screen); +/* Performance function to blit a YUV bitmap directly to the LCD */ +/* For the Gigabeat - show it rotated */ +/* So the LCD_WIDTH is now the height */ +void lcd_blit_yuv(unsigned char * const src[3], + int src_x, int src_y, int stride, + int x, int y, int width, int height) +{ + /* Caches for chroma data so it only need be recaculated every other + line */ + unsigned char const * yuv_src[3]; + off_t z; + + if (!lcd_on) + return; + + /* Sorry, but width and height must be >= 2 or else */ + width &= ~1; + height >>= 1; + + y = LCD_WIDTH - 1 - y; + fb_data *dst = (fb_data*)FRAME + x * LCD_WIDTH + y; + + z = stride*src_y; + yuv_src[0] = src[0] + z + src_x; + yuv_src[1] = src[1] + (z >> 2) + (src_x >> 1); + yuv_src[2] = src[2] + (yuv_src[1] - src[1]); + + if (lcd_yuv_options & LCD_YUV_DITHER) + { + do + { + lcd_write_yuv420_lines_odither(dst, yuv_src, width, stride, y, x); + yuv_src[0] += stride << 1; /* Skip down two luma lines */ + yuv_src[1] += stride >> 1; /* Skip down one chroma line */ + yuv_src[2] += stride >> 1; + dst -= 2; + y -= 2; + } + while (--height > 0); + } + else + { + do + { + lcd_write_yuv420_lines(dst, yuv_src, width, stride); + yuv_src[0] += stride << 1; /* Skip down two luma lines */ + yuv_src[1] += stride >> 1; /* Skip down one chroma line */ + yuv_src[2] += stride >> 1; + dst -= 2; + } + while (--height > 0); + } +} + +void lcd_set_contrast(int val) { + (void) val; + // TODO: +} + +void lcd_set_invert_display(bool yesno) { + (void) yesno; + // TODO: +} + +void lcd_set_flip(bool yesno) { + (void) yesno; + // TODO: +} + diff --git a/firmware/target/arm/imx31/gigabeat-s/lcd-imx31.c b/firmware/target/arm/imx31/gigabeat-s/lcd-imx31.c deleted file mode 100644 index 71d8e4bef4..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/lcd-imx31.c +++ /dev/null @@ -1,229 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2007 by Will Robertson - * - * 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 - -#include "config.h" -#include "cpu.h" -#include "string.h" -#include "lcd.h" -#include "kernel.h" -#include "lcd-target.h" -#include "backlight-target.h" - -#define MAIN_LCD_IDMAC_CHANNEL 14 -#define LCDADDR(x, y) (&lcd_framebuffer[(y)][(x)]) - -static bool lcd_on = true; -static bool lcd_powered = true; -static unsigned lcd_yuv_options = 0; - -/* Copies a rectangle from one framebuffer to another. Can be used in - single transfer mode with width = num pixels, and height = 1 which - allows a full-width rectangle to be copied more efficiently. */ -extern void lcd_copy_buffer_rect(fb_data *dst, const fb_data *src, - int width, int height); - -/* LCD init */ -void lcd_init_device(void) -{ - /* Move the framebuffer */ -#ifdef BOOTLOADER - /* Only do this once to avoid flicker */ - memset(FRAME, 0x00, FRAME_SIZE); -#endif - IPU_IDMAC_CHA_EN &= ~(1ul << MAIN_LCD_IDMAC_CHANNEL); - IPU_IMA_ADDR = ((0x1 << 16) | (MAIN_LCD_IDMAC_CHANNEL << 4)) + (1 << 3); - IPU_IMA_DATA = FRAME_PHYS_ADDR; - IPU_IDMAC_CHA_EN |= (1ul << MAIN_LCD_IDMAC_CHANNEL); -} - -/* Update a fraction of the display. */ -void lcd_update_rect(int x, int y, int width, int height) -{ - fb_data *dst, *src; - - if (!lcd_on) - return; - - if (x + width > LCD_WIDTH) - width = LCD_WIDTH - x; /* Clip right */ - if (x < 0) - width += x, x = 0; /* Clip left */ - if (width <= 0) - return; /* nothing left to do */ - - if (y + height > LCD_HEIGHT) - height = LCD_HEIGHT - y; /* Clip bottom */ - if (y < 0) - height += y, y = 0; /* Clip top */ - if (height <= 0) - return; /* nothing left to do */ - - /* TODO: It may be faster to swap the addresses of lcd_driver_framebuffer - * and lcd_framebuffer */ - dst = (fb_data *)FRAME + LCD_WIDTH*y + x; - src = &lcd_framebuffer[y][x]; - - /* Copy part of the Rockbox framebuffer to the second framebuffer */ - if (width < LCD_WIDTH) - { - /* Not full width - do line-by-line */ - lcd_copy_buffer_rect(dst, src, width, height); - } - else - { - /* Full width - copy as one line */ - lcd_copy_buffer_rect(dst, src, LCD_WIDTH*height, 1); - } -} - -void lcd_sleep(void) -{ - if (lcd_powered) - { - lcd_enable(false); - lcd_powered = false; - IPU_IDMAC_CHA_EN &= ~(1ul << MAIN_LCD_IDMAC_CHANNEL); - _backlight_lcd_sleep(); - } -} - -void lcd_enable(bool state) -{ - if (state == lcd_on) - return; - - if (state) - { - IPU_IDMAC_CHA_EN |= 1ul << MAIN_LCD_IDMAC_CHANNEL; - sleep(HZ/50); - lcd_powered = true; - lcd_on = true; - lcd_update(); - send_event(LCD_EVENT_ACTIVATION, NULL); - } - else - { - lcd_on = false; - } -} - -bool lcd_active(void) -{ - return lcd_on; -} - -/* Update the display. - This must be called after all other LCD functions that change the display. */ -void lcd_update(void) -{ - if (!lcd_on) - return; - - lcd_copy_buffer_rect((fb_data *)FRAME, &lcd_framebuffer[0][0], - LCD_WIDTH*LCD_HEIGHT, 1); -} - -void lcd_yuv_set_options(unsigned options) -{ - lcd_yuv_options = options; -} - -/* Line write helper function for lcd_yuv_blit. Write two lines of yuv420. */ -extern void lcd_write_yuv420_lines(fb_data *dst, - unsigned char const * const src[3], - int width, - int stride); -extern void lcd_write_yuv420_lines_odither(fb_data *dst, - unsigned char const * const src[3], - int width, - int stride, - int x_screen, /* To align dither pattern */ - int y_screen); -/* Performance function to blit a YUV bitmap directly to the LCD */ -/* For the Gigabeat - show it rotated */ -/* So the LCD_WIDTH is now the height */ -void lcd_blit_yuv(unsigned char * const src[3], - int src_x, int src_y, int stride, - int x, int y, int width, int height) -{ - /* Caches for chroma data so it only need be recaculated every other - line */ - unsigned char const * yuv_src[3]; - off_t z; - - if (!lcd_on) - return; - - /* Sorry, but width and height must be >= 2 or else */ - width &= ~1; - height >>= 1; - - y = LCD_WIDTH - 1 - y; - fb_data *dst = (fb_data*)FRAME + x * LCD_WIDTH + y; - - z = stride*src_y; - yuv_src[0] = src[0] + z + src_x; - yuv_src[1] = src[1] + (z >> 2) + (src_x >> 1); - yuv_src[2] = src[2] + (yuv_src[1] - src[1]); - - if (lcd_yuv_options & LCD_YUV_DITHER) - { - do - { - lcd_write_yuv420_lines_odither(dst, yuv_src, width, stride, y, x); - yuv_src[0] += stride << 1; /* Skip down two luma lines */ - yuv_src[1] += stride >> 1; /* Skip down one chroma line */ - yuv_src[2] += stride >> 1; - dst -= 2; - y -= 2; - } - while (--height > 0); - } - else - { - do - { - lcd_write_yuv420_lines(dst, yuv_src, width, stride); - yuv_src[0] += stride << 1; /* Skip down two luma lines */ - yuv_src[1] += stride >> 1; /* Skip down one chroma line */ - yuv_src[2] += stride >> 1; - dst -= 2; - } - while (--height > 0); - } -} - -void lcd_set_contrast(int val) { - (void) val; - // TODO: -} - -void lcd_set_invert_display(bool yesno) { - (void) yesno; - // TODO: -} - -void lcd_set_flip(bool yesno) { - (void) yesno; - // TODO: -} - diff --git a/firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c index fc9ad719a6..2060b7bc6a 100644 --- a/firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c +++ b/firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c @@ -27,7 +27,7 @@ #include "adc-target.h" #include "button-target.h" #include "usb-target.h" -#include "power-imx31.h" +#include "power-gigabeat-s.h" #include "powermgmt-target.h" /* Gigabeat S definitions for static MC13783 event registration */ diff --git a/firmware/target/arm/imx31/gigabeat-s/mc13783-imx31.c b/firmware/target/arm/imx31/gigabeat-s/mc13783-imx31.c deleted file mode 100644 index 2c5af8d5b7..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/mc13783-imx31.c +++ /dev/null @@ -1,360 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (c) 2008 by Michael Sevakis - * - * 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 "system.h" -#include "cpu.h" -#include "spi-imx31.h" -#include "gpio-imx31.h" -#include "mc13783.h" -#include "debug.h" -#include "kernel.h" - -#include "power-imx31.h" -#include "button-target.h" -#include "adc-target.h" -#include "usb-target.h" - -#ifdef BOOTLOADER -#define PMIC_DRIVER_CLOSE -#endif - -/* 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 */ -}; - -extern const struct mc13783_event_list mc13783_event_list; - -static int mc13783_thread_stack[DEFAULT_STACK_SIZE/sizeof(int)]; -static const char *mc13783_thread_name = "pmic"; -static struct wakeup mc13783_wake; - -/* Tracking for which interrupts are enabled */ -static uint32_t pmic_int_enabled[2] = - { 0x00000000, 0x00000000 }; - -static const unsigned char pmic_intm_regs[2] = - { MC13783_INTERRUPT_MASK0, MC13783_INTERRUPT_MASK1 }; - -static const unsigned char pmic_ints_regs[2] = - { MC13783_INTERRUPT_STATUS0, MC13783_INTERRUPT_STATUS1 }; - -#ifdef PMIC_DRIVER_CLOSE -static bool pmic_close = false; -static unsigned int mc13783_thread_id = 0; -#endif - -static void mc13783_interrupt_thread(void) -{ - uint32_t pending[2]; - - /* Enable mc13783 GPIO event */ - gpio_enable_event(MC13783_EVENT_ID); - - while (1) - { - const struct mc13783_event *event, *event_last; - - wakeup_wait(&mc13783_wake, TIMEOUT_BLOCK); - -#ifdef PMIC_DRIVER_CLOSE - if (pmic_close) - break; -#endif - - mc13783_read_regset(pmic_ints_regs, pending, 2); - - /* Only clear interrupts being dispatched */ - pending[0] &= pmic_int_enabled[0]; - pending[1] &= pmic_int_enabled[1]; - - mc13783_write_regset(pmic_ints_regs, pending, 2); - - /* Whatever is going to be serviced in this loop has been - * acknowledged. Reenable interrupt and if anything was still - * pending or became pending again, another signal will be - * generated. */ - imx31_regset32(&MC13783_GPIO_IMR, 1ul << MC13783_GPIO_LINE); - - event = mc13783_event_list.events; - event_last = event + mc13783_event_list.count; - - /* .count is surely expected to be > 0 */ - do - { - enum mc13783_event_sets set = event->set; - uint32_t pnd = pending[set]; - uint32_t mask = event->mask; - - if (pnd & mask) - { - event->callback(); - pnd &= ~mask; - pending[set] = pnd; - } - - if ((pending[0] | pending[1]) == 0) - break; /* Teminate early if nothing more to service */ - } - while (++event < event_last); - } - -#ifdef PMIC_DRIVER_CLOSE - gpio_disable_event(MC13783_EVENT_ID); -#endif -} - -/* GPIO interrupt handler for mc13783 */ -void mc13783_event(void) -{ - /* Mask the interrupt (unmasked when PMIC thread services it). */ - imx31_regclr32(&MC13783_GPIO_IMR, 1ul << MC13783_GPIO_LINE); - MC13783_GPIO_ISR = (1ul << MC13783_GPIO_LINE); - 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); - - /* Mask any PMIC interrupts for now - modules will enable them as - * required */ - mc13783_write(MC13783_INTERRUPT_MASK0, 0xffffff); - mc13783_write(MC13783_INTERRUPT_MASK1, 0xffffff); - - MC13783_GPIO_ISR = (1ul << MC13783_GPIO_LINE); - -#ifdef PMIC_DRIVER_CLOSE - mc13783_thread_id = -#endif - create_thread(mc13783_interrupt_thread, - mc13783_thread_stack, sizeof(mc13783_thread_stack), 0, - mc13783_thread_name IF_PRIO(, PRIORITY_REALTIME) IF_COP(, CPU)); -} - -#ifdef PMIC_DRIVER_CLOSE -void mc13783_close(void) -{ - unsigned int thread_id = mc13783_thread_id; - - if (thread_id == 0) - return; - - mc13783_thread_id = 0; - - pmic_close = true; - wakeup_signal(&mc13783_wake); - thread_wait(thread_id); -} -#endif /* PMIC_DRIVER_CLOSE */ - -bool mc13783_enable_event(enum mc13783_event_ids id) -{ - const struct mc13783_event * const event = - &mc13783_event_list.events[id]; - int set = event->set; - uint32_t mask = event->mask; - - spi_lock(&mc13783_spi); - - pmic_int_enabled[set] |= mask; - mc13783_clear(pmic_intm_regs[set], mask); - - spi_unlock(&mc13783_spi); - - return true; -} - -void mc13783_disable_event(enum mc13783_event_ids id) -{ - const struct mc13783_event * const event = - &mc13783_event_list.events[id]; - int set = event->set; - uint32_t mask = event->mask; - - spi_lock(&mc13783_spi); - - pmic_int_enabled[set] &= ~mask; - mc13783_set(pmic_intm_regs[set], mask); - - spi_unlock(&mc13783_spi); -} - -uint32_t mc13783_set(unsigned address, uint32_t bits) -{ - spi_lock(&mc13783_spi); - - uint32_t data = mc13783_read(address); - - if (data != MC13783_DATA_ERROR) - mc13783_write(address, data | bits); - - spi_unlock(&mc13783_spi); - - return data; -} - -uint32_t mc13783_clear(unsigned address, uint32_t bits) -{ - spi_lock(&mc13783_spi); - - uint32_t data = mc13783_read(address); - - if (data != MC13783_DATA_ERROR) - mc13783_write(address, data & ~bits); - - spi_unlock(&mc13783_spi); - - return data; -} - -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; -} - -uint32_t mc13783_write_masked(unsigned address, uint32_t data, uint32_t mask) -{ - uint32_t old; - - spi_lock(&mc13783_spi); - - old = mc13783_read(address); - - if (old != MC13783_DATA_ERROR) - { - data = (old & ~mask) | (data & mask); - - if (mc13783_write(address, data) != 1) - old = MC13783_DATA_ERROR; - } - - spi_unlock(&mc13783_spi); - - return old; -} - -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 ((unsigned)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; - struct spi_transfer xfer; - - if (address >= MC13783_NUM_REGS) - return MC13783_DATA_ERROR; - - packet = address << 25; - - xfer.txbuf = &packet; - xfer.rxbuf = &packet; - xfer.count = 1; - - if (!spi_transfer(&mc13783_spi, &xfer)) - return MC13783_DATA_ERROR; - - return packet; -} - -int mc13783_read_regset(const unsigned char *regs, uint32_t *buffer, - int count) -{ - int i; - struct spi_transfer xfer; - - if ((unsigned)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; - - return count - xfer.count; -} diff --git a/firmware/target/arm/imx31/gigabeat-s/mmu-imx31.c b/firmware/target/arm/imx31/gigabeat-s/mmu-imx31.c deleted file mode 100644 index 920a8c9fd3..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/mmu-imx31.c +++ /dev/null @@ -1,33 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2007 by Will Robertson - * - * 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 "cpu.h" -#include "mmu-imx31.h" -#include "mmu-arm.h" - -unsigned long addr_virt_to_phys(unsigned long addr) -{ - return addr | CSD0_BASE_ADDR; -} - -unsigned long addr_phys_to_virt(unsigned long addr) -{ - return addr & ~CSD0_BASE_ADDR; -} diff --git a/firmware/target/arm/imx31/gigabeat-s/mmu-imx31.h b/firmware/target/arm/imx31/gigabeat-s/mmu-imx31.h deleted file mode 100644 index c66a3d941d..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/mmu-imx31.h +++ /dev/null @@ -1,29 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2006,2007 by Greg White - * - * 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. - * - ****************************************************************************/ -#ifndef MMU_IMX31_H -#define MMU_IMX31_H - -void memory_init(void); -void set_page_tables(void); -unsigned long addr_virt_to_phys(unsigned long addr); -unsigned long addr_phys_to_virt(unsigned long addr); - -#endif /* MMU_IMX31_H */ diff --git a/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c new file mode 100644 index 0000000000..6cec3ecdd3 --- /dev/null +++ b/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c @@ -0,0 +1,555 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2008 by Michael Sevakis + * + * 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 +#include "system.h" +#include "kernel.h" +#include "audio.h" +#include "sound.h" +#include "ccm-imx31.h" +#include "sdma-imx31.h" +#include "mmu-imx31.h" + +#define DMA_PLAY_CH_NUM 2 +#define DMA_REC_CH_NUM 1 +#define DMA_PLAY_CH_PRIORITY 6 +#define DMA_REC_CH_PRIORITY 6 + +static struct buffer_descriptor dma_play_bd DEVBSS_ATTR; +static struct channel_descriptor dma_play_cd DEVBSS_ATTR; + +struct dma_data +{ + int locked; + int callback_pending; /* DMA interrupt happened while locked */ + int state; +}; + +static struct dma_data dma_play_data = +{ + /* Initialize to a locked, stopped state */ + .locked = 0, + .callback_pending = 0, + .state = 0 +}; + +static void play_dma_callback(void) +{ + unsigned char *start; + size_t size = 0; + pcm_more_callback_type get_more = pcm_callback_for_more; + + if (dma_play_data.locked != 0) + { + /* Callback is locked out */ + dma_play_data.callback_pending = dma_play_data.state; + return; + } + + if (dma_play_bd.mode.status & BD_RROR) + { + /* Stop on error */ + } + else if (get_more != NULL && (get_more(&start, &size), size != 0)) + { + start = (void*)(((unsigned long)start + 3) & ~3); + size &= ~3; + + /* Flush any pending cache writes */ + clean_dcache_range(start, size); + dma_play_bd.buf_addr = (void *)addr_virt_to_phys((unsigned long)start); + dma_play_bd.mode.count = size; + dma_play_bd.mode.command = TRANSFER_16BIT; + dma_play_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR; + sdma_channel_run(DMA_PLAY_CH_NUM); + return; + } + + /* Error, callback missing or no more DMA to do */ + pcm_play_dma_stop(); + pcm_play_dma_stopped_callback(); +} + +void pcm_play_lock(void) +{ + if (++dma_play_data.locked == 1) + imx31_regclr32(&SSI_SIER2, SSI_SIER_TDMAE); +} + +void pcm_play_unlock(void) +{ + if (--dma_play_data.locked == 0 && dma_play_data.state != 0) + { + int oldstatus = disable_irq_save(); + int pending = dma_play_data.callback_pending; + dma_play_data.callback_pending = 0; + SSI_SIER2 |= SSI_SIER_TDMAE; + restore_irq(oldstatus); + + /* Should an interrupt be forced instead? The upper pcm layer can + * call producer's callback in thread context so technically this is + * acceptable. */ + if (pending != 0) + play_dma_callback(); + } +} + +void pcm_dma_apply_settings(void) +{ + audiohw_set_frequency(pcm_fsel); +} + +void pcm_play_dma_init(void) +{ + /* Init channel information */ + dma_play_cd.bd_count = 1; + dma_play_cd.callback = play_dma_callback; + dma_play_cd.shp_addr = SDMA_PER_ADDR_SSI2_TX1; + dma_play_cd.wml = SDMA_SSI_TXFIFO_WML*2; + dma_play_cd.per_type = SDMA_PER_SSI_SHP; /* SSI2 shared with SDMA core */ + dma_play_cd.tran_type = SDMA_TRAN_EMI_2_PER; + dma_play_cd.event_id1 = SDMA_REQ_SSI2_TX1; + + sdma_channel_init(DMA_PLAY_CH_NUM, &dma_play_cd, &dma_play_bd); + sdma_channel_set_priority(DMA_PLAY_CH_NUM, DMA_PLAY_CH_PRIORITY); + + ccm_module_clock_gating(CG_SSI1, CGM_ON_RUN_WAIT); + ccm_module_clock_gating(CG_SSI2, CGM_ON_RUN_WAIT); + + /* Reset & disable SSIs */ + SSI_SCR1 &= ~SSI_SCR_SSIEN; + SSI_SCR2 &= ~SSI_SCR_SSIEN; + + SSI_SIER1 = 0; + SSI_SIER2 = 0; + + /* Set up audio mux */ + + /* Port 2 (internally connected to SSI2) + * All clocking is output sourced from port 4 */ + AUDMUX_PTCR2 = AUDMUX_PTCR_TFS_DIR | AUDMUX_PTCR_TFSEL_PORT4 | + AUDMUX_PTCR_TCLKDIR | AUDMUX_PTCR_TCSEL_PORT4 | + AUDMUX_PTCR_SYN; + + /* Receive data from port 4 */ + AUDMUX_PDCR2 = AUDMUX_PDCR_RXDSEL_PORT4; + /* All clock lines are inputs sourced from the master mode codec and + * sent back to SSI2 through port 2 */ + AUDMUX_PTCR4 = AUDMUX_PTCR_SYN; + + /* Receive data from port 2 */ + AUDMUX_PDCR4 = AUDMUX_PDCR_RXDSEL_PORT2; + + /* PORT1 (internally connected to SSI1) routes clocking to PORT5 to + * provide MCLK to the codec */ + /* TX clocks are inputs taken from SSI2 */ + /* RX clocks are outputs taken from PORT4 */ + AUDMUX_PTCR1 = AUDMUX_PTCR_RFS_DIR | AUDMUX_PTCR_RFSSEL_PORT4 | + AUDMUX_PTCR_RCLKDIR | AUDMUX_PTCR_RCSEL_PORT4; + /* RX data taken from PORT4 */ + AUDMUX_PDCR1 = AUDMUX_PDCR_RXDSEL_PORT4; + + /* PORT5 outputs TCLK sourced from PORT1 (SSI1) */ + AUDMUX_PTCR5 = AUDMUX_PTCR_TCLKDIR | AUDMUX_PTCR_TCSEL_PORT1; + AUDMUX_PDCR5 = 0; + + /* Setup SSIs */ + + /* SSI2 - SoC software interface for all I2S data out */ + SSI_SCR2 = SSI_SCR_SYN | SSI_SCR_I2S_MODE_SLAVE; + SSI_STCR2 = SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSI | + SSI_STCR_TEFS | SSI_STCR_TFEN0; + + /* 16 bits per word, 2 words per frame */ + SSI_STCCR2 = SSI_STRCCR_WL16 | ((2-1) << SSI_STRCCR_DC_POS) | + ((4-1) << SSI_STRCCR_PM_POS); + + /* Transmit low watermark */ + SSI_SFCSR2 = (SSI_SFCSR2 & ~SSI_SFCSR_TFWM0) | + ((8-SDMA_SSI_TXFIFO_WML) << SSI_SFCSR_TFWM0_POS); + SSI_STMSK2 = 0; + + /* SSI1 - provides MCLK to codec. Receives data from codec. */ + SSI_STCR1 = SSI_STCR_TXDIR; + + /* f(INT_BIT_CLK) = + * f(SYS_CLK) / [(DIV2 + 1)*(7*PSR + 1)*(PM + 1)*2] = + * 677737600 / [(1 + 1)*(7*0 + 1)*(0 + 1)*2] = + * 677737600 / 4 = 169344000 Hz + * + * 45.4.2.2 DIV2, PSR, and PM Bit Description states: + * Bits DIV2, PSR, and PM should not be all set to zero at the same + * time. + * + * The hardware seems to force a divide by 4 even if all bits are + * zero but comply by setting DIV2 and the others to zero. + */ + SSI_STCCR1 = SSI_STRCCR_DIV2 | ((1-1) << SSI_STRCCR_PM_POS); + + /* SSI1 - receive - asynchronous clocks */ + SSI_SCR1 = SSI_SCR_I2S_MODE_SLAVE; + + SSI_SRCR1 = SSI_SRCR_RXBIT0 | SSI_SRCR_RSCKP | SSI_SRCR_RFSI | + SSI_SRCR_REFS; + + /* 16 bits per word, 2 words per frame */ + SSI_SRCCR1 = SSI_STRCCR_WL16 | ((2-1) << SSI_STRCCR_DC_POS) | + ((4-1) << SSI_STRCCR_PM_POS); + + /* Receive high watermark */ + SSI_SFCSR1 = (SSI_SFCSR1 & ~SSI_SFCSR_RFWM0) | + (SDMA_SSI_RXFIFO_WML << SSI_SFCSR_RFWM0_POS); + SSI_SRMSK1 = 0; + + /* Enable SSI1 (codec clock) */ + SSI_SCR1 |= SSI_SCR_SSIEN; + + audiohw_init(); +} + +void pcm_postinit(void) +{ + audiohw_postinit(); +} + +static void play_start_pcm(void) +{ + /* Stop transmission (if in progress) */ + SSI_SCR2 &= ~SSI_SCR_TE; + + SSI_SCR2 |= SSI_SCR_SSIEN; /* Enable SSI */ + SSI_STCR2 |= SSI_STCR_TFEN0; /* Enable TX FIFO */ + + dma_play_data.state = 1; /* Enable DMA requests on unlock */ + + /* Do prefill to prevent swapped channels (see TLSbo61214 in MCIMX31CE). + * No actual solution was offered but this appears to work. */ + SSI_STX0_2 = 0; + SSI_STX0_2 = 0; + SSI_STX0_2 = 0; + SSI_STX0_2 = 0; + + SSI_SCR2 |= SSI_SCR_TE; /* Start transmitting */ +} + +static void play_stop_pcm(void) +{ + /* Wait for FIFO to empty */ + while (SSI_SFCSR_TFCNT0 & SSI_SFCSR2); + + /* Disable transmission */ + SSI_STCR2 &= ~SSI_STCR_TFEN0; + SSI_SCR2 &= ~(SSI_SCR_TE | SSI_SCR_SSIEN); + + /* Set state before pending to prevent race with interrupt */ + /* Do not enable DMA requests on unlock */ + dma_play_data.state = 0; + dma_play_data.callback_pending = 0; +} + +void pcm_play_dma_start(const void *addr, size_t size) +{ + sdma_channel_stop(DMA_PLAY_CH_NUM); + + /* Disable transmission */ + SSI_STCR2 &= ~SSI_STCR_TFEN0; + SSI_SCR2 &= ~(SSI_SCR_TE | SSI_SCR_SSIEN); + + addr = (void *)(((unsigned long)addr + 3) & ~3); + size &= ~3; + + if (size <= 0) + return; + + if (!sdma_channel_reset(DMA_PLAY_CH_NUM)) + return; + + clean_dcache_range(addr, size); + dma_play_bd.buf_addr = + (void *)addr_virt_to_phys((unsigned long)(void *)addr); + dma_play_bd.mode.count = size; + dma_play_bd.mode.command = TRANSFER_16BIT; + dma_play_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR; + + play_start_pcm(); + sdma_channel_run(DMA_PLAY_CH_NUM); +} + +void pcm_play_dma_stop(void) +{ + sdma_channel_stop(DMA_PLAY_CH_NUM); + play_stop_pcm(); +} + +void pcm_play_dma_pause(bool pause) +{ + if (pause) + { + sdma_channel_pause(DMA_PLAY_CH_NUM); + play_stop_pcm(); + } + else + { + play_start_pcm(); + sdma_channel_run(DMA_PLAY_CH_NUM); + } +} + +/* Return the number of bytes waiting - full L-R sample pairs only */ +size_t pcm_get_bytes_waiting(void) +{ + static unsigned long dsa DEVBSS_ATTR; + long offs, size; + int oldstatus; + + /* read burst dma source address register in channel context */ + sdma_read_words(&dsa, CHANNEL_CONTEXT_ADDR(DMA_PLAY_CH_NUM)+0x0b, 1); + + oldstatus = disable_irq_save(); + offs = dsa - (unsigned long)dma_play_bd.buf_addr; + size = dma_play_bd.mode.count; + restore_irq(oldstatus); + + /* Be addresses are coherent (no buffer change during read) */ + if (offs >= 0 && offs < size) + { + return (size - offs) & ~3; + } + + return 0; +} + +/* Return a pointer to the samples and the number of them in *count */ +const void * pcm_play_dma_get_peak_buffer(int *count) +{ + static unsigned long dsa DEVBSS_ATTR; + unsigned long addr; + long offs, size; + int oldstatus; + + /* read burst dma source address register in channel context */ + sdma_read_words(&dsa, CHANNEL_CONTEXT_ADDR(DMA_PLAY_CH_NUM)+0x0b, 1); + + oldstatus = disable_irq_save(); + addr = dsa; + offs = addr - (unsigned long)dma_play_bd.buf_addr; + size = dma_play_bd.mode.count; + restore_irq(oldstatus); + + /* Be addresses are coherent (no buffer change during read) */ + if (offs >= 0 && offs < size) + { + *count = (size - offs) >> 2; + return (void *)((addr + 2) & ~3); + } + + *count = 0; + return NULL; +} + +void * pcm_dma_addr(void *addr) +{ + return (void *)addr_virt_to_phys((unsigned long)addr); +} + +#ifdef HAVE_RECORDING +static struct buffer_descriptor dma_rec_bd DEVBSS_ATTR; +static struct channel_descriptor dma_rec_cd DEVBSS_ATTR; + +static struct dma_data dma_rec_data = +{ + /* Initialize to a locked, stopped state */ + .locked = 0, + .callback_pending = 0, + .state = 0 +}; + +static void rec_dma_callback(void) +{ + pcm_more_callback_type2 more_ready; + int status = 0; + + if (dma_rec_data.locked != 0) + { + dma_rec_data.callback_pending = dma_rec_data.state; + return; /* Callback is locked out */ + } + + if (dma_rec_bd.mode.status & BD_RROR) + status = DMA_REC_ERROR_DMA; + + more_ready = pcm_callback_more_ready; + + if (more_ready != NULL && more_ready(status) >= 0) + { + sdma_channel_run(DMA_REC_CH_NUM); + return; + } + + /* Finished recording */ + pcm_rec_dma_stop(); + pcm_rec_dma_stopped_callback(); +} + +void pcm_rec_lock(void) +{ + if (++dma_rec_data.locked == 1) + imx31_regclr32(&SSI_SIER1, SSI_SIER_RDMAE); +} + +void pcm_rec_unlock(void) +{ + if (--dma_rec_data.locked == 0 && dma_rec_data.state != 0) + { + int oldstatus = disable_irq_save(); + int pending = dma_rec_data.callback_pending; + dma_rec_data.callback_pending = 0; + SSI_SIER1 |= SSI_SIER_RDMAE; + restore_irq(oldstatus); + + /* Should an interrupt be forced instead? The upper pcm layer can + * call consumer's callback in thread context so technically this is + * acceptable. */ + if (pending != 0) + rec_dma_callback(); + } +} + +void pcm_record_more(void *start, size_t size) +{ + start = (void *)(((unsigned long)start + 3) & ~3); + size &= ~3; + + /* Invalidate - buffer must be coherent */ + dump_dcache_range(start, size); + + start = (void *)addr_virt_to_phys((unsigned long)start); + + pcm_rec_peak_addr = start; + dma_rec_bd.buf_addr = start; + dma_rec_bd.mode.count = size; + dma_rec_bd.mode.command = TRANSFER_16BIT; + dma_rec_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR; +} + +void pcm_rec_dma_stop(void) +{ + /* Stop receiving data */ + sdma_channel_stop(DMA_REC_CH_NUM); + + imx31_regclr32(&SSI_SIER1, SSI_SIER_RDMAE); + + SSI_SCR1 &= ~SSI_SCR_RE; /* Disable RX */ + SSI_SRCR1 &= ~SSI_SRCR_RFEN0; /* Disable RX FIFO */ + + /* Set state before pending to prevent race with interrupt */ + /* Do not enable DMA requests on unlock */ + dma_rec_data.state = 0; + dma_rec_data.callback_pending = 0; +} + +void pcm_rec_dma_start(void *addr, size_t size) +{ + pcm_rec_dma_stop(); + + addr = (void *)(((unsigned long)addr + 3) & ~3); + size &= ~3; + + if (size <= 0) + return; + + if (!sdma_channel_reset(DMA_REC_CH_NUM)) + return; + + /* Invalidate - buffer must be coherent */ + dump_dcache_range(addr, size); + + addr = (void *)addr_virt_to_phys((unsigned long)addr); + pcm_rec_peak_addr = addr; + dma_rec_bd.buf_addr = addr; + dma_rec_bd.mode.count = size; + dma_rec_bd.mode.command = TRANSFER_16BIT; + dma_rec_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR; + + dma_rec_data.state = 1; + + SSI_SRCR1 |= SSI_SRCR_RFEN0; /* Enable RX FIFO */ + + /* Ensure clear FIFO */ + while (SSI_SFCSR1 & SSI_SFCSR_RFCNT0) + SSI_SRX0_1; + + /* Enable receive */ + SSI_SCR1 |= SSI_SCR_RE; + sdma_channel_run(DMA_REC_CH_NUM); +} + +void pcm_rec_dma_close(void) +{ + pcm_rec_dma_stop(); + sdma_channel_close(DMA_REC_CH_NUM); +} + +void pcm_rec_dma_init(void) +{ + pcm_rec_dma_stop(); + + /* Init channel information */ + dma_rec_cd.bd_count = 1; + dma_rec_cd.callback = rec_dma_callback; + dma_rec_cd.shp_addr = SDMA_PER_ADDR_SSI1_RX1; + dma_rec_cd.wml = SDMA_SSI_RXFIFO_WML*2; + dma_rec_cd.per_type = SDMA_PER_SSI; + dma_rec_cd.tran_type = SDMA_TRAN_PER_2_EMI; + dma_rec_cd.event_id1 = SDMA_REQ_SSI1_RX1; + + sdma_channel_init(DMA_REC_CH_NUM, &dma_rec_cd, &dma_rec_bd); + sdma_channel_set_priority(DMA_REC_CH_NUM, DMA_REC_CH_PRIORITY); +} + +const void * pcm_rec_dma_get_peak_buffer(int *count) +{ + static unsigned long pda DEVBSS_ATTR; + unsigned long buf, addr, end, bufend; + int oldstatus; + + /* read burst dma destination address register in channel context */ + sdma_read_words(&pda, CHANNEL_CONTEXT_ADDR(DMA_REC_CH_NUM)+0x0a, 1); + + oldstatus = disable_irq_save(); + end = pda; + buf = (unsigned long)dma_rec_bd.buf_addr; + addr = (unsigned long)pcm_rec_peak_addr; + bufend = buf + dma_rec_bd.mode.count; + restore_irq(oldstatus); + + /* Be addresses are coherent (no buffer change during read) */ + if (addr >= buf && addr < bufend && + end >= buf && end < bufend) + { + *count = (end >> 2) - (addr >> 2); + return (void *)(addr & ~3); + } + + *count = 0; + return NULL; +} + +#endif /* HAVE_RECORDING */ diff --git a/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c b/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c deleted file mode 100644 index 6cec3ecdd3..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c +++ /dev/null @@ -1,555 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2008 by Michael Sevakis - * - * 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 -#include "system.h" -#include "kernel.h" -#include "audio.h" -#include "sound.h" -#include "ccm-imx31.h" -#include "sdma-imx31.h" -#include "mmu-imx31.h" - -#define DMA_PLAY_CH_NUM 2 -#define DMA_REC_CH_NUM 1 -#define DMA_PLAY_CH_PRIORITY 6 -#define DMA_REC_CH_PRIORITY 6 - -static struct buffer_descriptor dma_play_bd DEVBSS_ATTR; -static struct channel_descriptor dma_play_cd DEVBSS_ATTR; - -struct dma_data -{ - int locked; - int callback_pending; /* DMA interrupt happened while locked */ - int state; -}; - -static struct dma_data dma_play_data = -{ - /* Initialize to a locked, stopped state */ - .locked = 0, - .callback_pending = 0, - .state = 0 -}; - -static void play_dma_callback(void) -{ - unsigned char *start; - size_t size = 0; - pcm_more_callback_type get_more = pcm_callback_for_more; - - if (dma_play_data.locked != 0) - { - /* Callback is locked out */ - dma_play_data.callback_pending = dma_play_data.state; - return; - } - - if (dma_play_bd.mode.status & BD_RROR) - { - /* Stop on error */ - } - else if (get_more != NULL && (get_more(&start, &size), size != 0)) - { - start = (void*)(((unsigned long)start + 3) & ~3); - size &= ~3; - - /* Flush any pending cache writes */ - clean_dcache_range(start, size); - dma_play_bd.buf_addr = (void *)addr_virt_to_phys((unsigned long)start); - dma_play_bd.mode.count = size; - dma_play_bd.mode.command = TRANSFER_16BIT; - dma_play_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR; - sdma_channel_run(DMA_PLAY_CH_NUM); - return; - } - - /* Error, callback missing or no more DMA to do */ - pcm_play_dma_stop(); - pcm_play_dma_stopped_callback(); -} - -void pcm_play_lock(void) -{ - if (++dma_play_data.locked == 1) - imx31_regclr32(&SSI_SIER2, SSI_SIER_TDMAE); -} - -void pcm_play_unlock(void) -{ - if (--dma_play_data.locked == 0 && dma_play_data.state != 0) - { - int oldstatus = disable_irq_save(); - int pending = dma_play_data.callback_pending; - dma_play_data.callback_pending = 0; - SSI_SIER2 |= SSI_SIER_TDMAE; - restore_irq(oldstatus); - - /* Should an interrupt be forced instead? The upper pcm layer can - * call producer's callback in thread context so technically this is - * acceptable. */ - if (pending != 0) - play_dma_callback(); - } -} - -void pcm_dma_apply_settings(void) -{ - audiohw_set_frequency(pcm_fsel); -} - -void pcm_play_dma_init(void) -{ - /* Init channel information */ - dma_play_cd.bd_count = 1; - dma_play_cd.callback = play_dma_callback; - dma_play_cd.shp_addr = SDMA_PER_ADDR_SSI2_TX1; - dma_play_cd.wml = SDMA_SSI_TXFIFO_WML*2; - dma_play_cd.per_type = SDMA_PER_SSI_SHP; /* SSI2 shared with SDMA core */ - dma_play_cd.tran_type = SDMA_TRAN_EMI_2_PER; - dma_play_cd.event_id1 = SDMA_REQ_SSI2_TX1; - - sdma_channel_init(DMA_PLAY_CH_NUM, &dma_play_cd, &dma_play_bd); - sdma_channel_set_priority(DMA_PLAY_CH_NUM, DMA_PLAY_CH_PRIORITY); - - ccm_module_clock_gating(CG_SSI1, CGM_ON_RUN_WAIT); - ccm_module_clock_gating(CG_SSI2, CGM_ON_RUN_WAIT); - - /* Reset & disable SSIs */ - SSI_SCR1 &= ~SSI_SCR_SSIEN; - SSI_SCR2 &= ~SSI_SCR_SSIEN; - - SSI_SIER1 = 0; - SSI_SIER2 = 0; - - /* Set up audio mux */ - - /* Port 2 (internally connected to SSI2) - * All clocking is output sourced from port 4 */ - AUDMUX_PTCR2 = AUDMUX_PTCR_TFS_DIR | AUDMUX_PTCR_TFSEL_PORT4 | - AUDMUX_PTCR_TCLKDIR | AUDMUX_PTCR_TCSEL_PORT4 | - AUDMUX_PTCR_SYN; - - /* Receive data from port 4 */ - AUDMUX_PDCR2 = AUDMUX_PDCR_RXDSEL_PORT4; - /* All clock lines are inputs sourced from the master mode codec and - * sent back to SSI2 through port 2 */ - AUDMUX_PTCR4 = AUDMUX_PTCR_SYN; - - /* Receive data from port 2 */ - AUDMUX_PDCR4 = AUDMUX_PDCR_RXDSEL_PORT2; - - /* PORT1 (internally connected to SSI1) routes clocking to PORT5 to - * provide MCLK to the codec */ - /* TX clocks are inputs taken from SSI2 */ - /* RX clocks are outputs taken from PORT4 */ - AUDMUX_PTCR1 = AUDMUX_PTCR_RFS_DIR | AUDMUX_PTCR_RFSSEL_PORT4 | - AUDMUX_PTCR_RCLKDIR | AUDMUX_PTCR_RCSEL_PORT4; - /* RX data taken from PORT4 */ - AUDMUX_PDCR1 = AUDMUX_PDCR_RXDSEL_PORT4; - - /* PORT5 outputs TCLK sourced from PORT1 (SSI1) */ - AUDMUX_PTCR5 = AUDMUX_PTCR_TCLKDIR | AUDMUX_PTCR_TCSEL_PORT1; - AUDMUX_PDCR5 = 0; - - /* Setup SSIs */ - - /* SSI2 - SoC software interface for all I2S data out */ - SSI_SCR2 = SSI_SCR_SYN | SSI_SCR_I2S_MODE_SLAVE; - SSI_STCR2 = SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSI | - SSI_STCR_TEFS | SSI_STCR_TFEN0; - - /* 16 bits per word, 2 words per frame */ - SSI_STCCR2 = SSI_STRCCR_WL16 | ((2-1) << SSI_STRCCR_DC_POS) | - ((4-1) << SSI_STRCCR_PM_POS); - - /* Transmit low watermark */ - SSI_SFCSR2 = (SSI_SFCSR2 & ~SSI_SFCSR_TFWM0) | - ((8-SDMA_SSI_TXFIFO_WML) << SSI_SFCSR_TFWM0_POS); - SSI_STMSK2 = 0; - - /* SSI1 - provides MCLK to codec. Receives data from codec. */ - SSI_STCR1 = SSI_STCR_TXDIR; - - /* f(INT_BIT_CLK) = - * f(SYS_CLK) / [(DIV2 + 1)*(7*PSR + 1)*(PM + 1)*2] = - * 677737600 / [(1 + 1)*(7*0 + 1)*(0 + 1)*2] = - * 677737600 / 4 = 169344000 Hz - * - * 45.4.2.2 DIV2, PSR, and PM Bit Description states: - * Bits DIV2, PSR, and PM should not be all set to zero at the same - * time. - * - * The hardware seems to force a divide by 4 even if all bits are - * zero but comply by setting DIV2 and the others to zero. - */ - SSI_STCCR1 = SSI_STRCCR_DIV2 | ((1-1) << SSI_STRCCR_PM_POS); - - /* SSI1 - receive - asynchronous clocks */ - SSI_SCR1 = SSI_SCR_I2S_MODE_SLAVE; - - SSI_SRCR1 = SSI_SRCR_RXBIT0 | SSI_SRCR_RSCKP | SSI_SRCR_RFSI | - SSI_SRCR_REFS; - - /* 16 bits per word, 2 words per frame */ - SSI_SRCCR1 = SSI_STRCCR_WL16 | ((2-1) << SSI_STRCCR_DC_POS) | - ((4-1) << SSI_STRCCR_PM_POS); - - /* Receive high watermark */ - SSI_SFCSR1 = (SSI_SFCSR1 & ~SSI_SFCSR_RFWM0) | - (SDMA_SSI_RXFIFO_WML << SSI_SFCSR_RFWM0_POS); - SSI_SRMSK1 = 0; - - /* Enable SSI1 (codec clock) */ - SSI_SCR1 |= SSI_SCR_SSIEN; - - audiohw_init(); -} - -void pcm_postinit(void) -{ - audiohw_postinit(); -} - -static void play_start_pcm(void) -{ - /* Stop transmission (if in progress) */ - SSI_SCR2 &= ~SSI_SCR_TE; - - SSI_SCR2 |= SSI_SCR_SSIEN; /* Enable SSI */ - SSI_STCR2 |= SSI_STCR_TFEN0; /* Enable TX FIFO */ - - dma_play_data.state = 1; /* Enable DMA requests on unlock */ - - /* Do prefill to prevent swapped channels (see TLSbo61214 in MCIMX31CE). - * No actual solution was offered but this appears to work. */ - SSI_STX0_2 = 0; - SSI_STX0_2 = 0; - SSI_STX0_2 = 0; - SSI_STX0_2 = 0; - - SSI_SCR2 |= SSI_SCR_TE; /* Start transmitting */ -} - -static void play_stop_pcm(void) -{ - /* Wait for FIFO to empty */ - while (SSI_SFCSR_TFCNT0 & SSI_SFCSR2); - - /* Disable transmission */ - SSI_STCR2 &= ~SSI_STCR_TFEN0; - SSI_SCR2 &= ~(SSI_SCR_TE | SSI_SCR_SSIEN); - - /* Set state before pending to prevent race with interrupt */ - /* Do not enable DMA requests on unlock */ - dma_play_data.state = 0; - dma_play_data.callback_pending = 0; -} - -void pcm_play_dma_start(const void *addr, size_t size) -{ - sdma_channel_stop(DMA_PLAY_CH_NUM); - - /* Disable transmission */ - SSI_STCR2 &= ~SSI_STCR_TFEN0; - SSI_SCR2 &= ~(SSI_SCR_TE | SSI_SCR_SSIEN); - - addr = (void *)(((unsigned long)addr + 3) & ~3); - size &= ~3; - - if (size <= 0) - return; - - if (!sdma_channel_reset(DMA_PLAY_CH_NUM)) - return; - - clean_dcache_range(addr, size); - dma_play_bd.buf_addr = - (void *)addr_virt_to_phys((unsigned long)(void *)addr); - dma_play_bd.mode.count = size; - dma_play_bd.mode.command = TRANSFER_16BIT; - dma_play_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR; - - play_start_pcm(); - sdma_channel_run(DMA_PLAY_CH_NUM); -} - -void pcm_play_dma_stop(void) -{ - sdma_channel_stop(DMA_PLAY_CH_NUM); - play_stop_pcm(); -} - -void pcm_play_dma_pause(bool pause) -{ - if (pause) - { - sdma_channel_pause(DMA_PLAY_CH_NUM); - play_stop_pcm(); - } - else - { - play_start_pcm(); - sdma_channel_run(DMA_PLAY_CH_NUM); - } -} - -/* Return the number of bytes waiting - full L-R sample pairs only */ -size_t pcm_get_bytes_waiting(void) -{ - static unsigned long dsa DEVBSS_ATTR; - long offs, size; - int oldstatus; - - /* read burst dma source address register in channel context */ - sdma_read_words(&dsa, CHANNEL_CONTEXT_ADDR(DMA_PLAY_CH_NUM)+0x0b, 1); - - oldstatus = disable_irq_save(); - offs = dsa - (unsigned long)dma_play_bd.buf_addr; - size = dma_play_bd.mode.count; - restore_irq(oldstatus); - - /* Be addresses are coherent (no buffer change during read) */ - if (offs >= 0 && offs < size) - { - return (size - offs) & ~3; - } - - return 0; -} - -/* Return a pointer to the samples and the number of them in *count */ -const void * pcm_play_dma_get_peak_buffer(int *count) -{ - static unsigned long dsa DEVBSS_ATTR; - unsigned long addr; - long offs, size; - int oldstatus; - - /* read burst dma source address register in channel context */ - sdma_read_words(&dsa, CHANNEL_CONTEXT_ADDR(DMA_PLAY_CH_NUM)+0x0b, 1); - - oldstatus = disable_irq_save(); - addr = dsa; - offs = addr - (unsigned long)dma_play_bd.buf_addr; - size = dma_play_bd.mode.count; - restore_irq(oldstatus); - - /* Be addresses are coherent (no buffer change during read) */ - if (offs >= 0 && offs < size) - { - *count = (size - offs) >> 2; - return (void *)((addr + 2) & ~3); - } - - *count = 0; - return NULL; -} - -void * pcm_dma_addr(void *addr) -{ - return (void *)addr_virt_to_phys((unsigned long)addr); -} - -#ifdef HAVE_RECORDING -static struct buffer_descriptor dma_rec_bd DEVBSS_ATTR; -static struct channel_descriptor dma_rec_cd DEVBSS_ATTR; - -static struct dma_data dma_rec_data = -{ - /* Initialize to a locked, stopped state */ - .locked = 0, - .callback_pending = 0, - .state = 0 -}; - -static void rec_dma_callback(void) -{ - pcm_more_callback_type2 more_ready; - int status = 0; - - if (dma_rec_data.locked != 0) - { - dma_rec_data.callback_pending = dma_rec_data.state; - return; /* Callback is locked out */ - } - - if (dma_rec_bd.mode.status & BD_RROR) - status = DMA_REC_ERROR_DMA; - - more_ready = pcm_callback_more_ready; - - if (more_ready != NULL && more_ready(status) >= 0) - { - sdma_channel_run(DMA_REC_CH_NUM); - return; - } - - /* Finished recording */ - pcm_rec_dma_stop(); - pcm_rec_dma_stopped_callback(); -} - -void pcm_rec_lock(void) -{ - if (++dma_rec_data.locked == 1) - imx31_regclr32(&SSI_SIER1, SSI_SIER_RDMAE); -} - -void pcm_rec_unlock(void) -{ - if (--dma_rec_data.locked == 0 && dma_rec_data.state != 0) - { - int oldstatus = disable_irq_save(); - int pending = dma_rec_data.callback_pending; - dma_rec_data.callback_pending = 0; - SSI_SIER1 |= SSI_SIER_RDMAE; - restore_irq(oldstatus); - - /* Should an interrupt be forced instead? The upper pcm layer can - * call consumer's callback in thread context so technically this is - * acceptable. */ - if (pending != 0) - rec_dma_callback(); - } -} - -void pcm_record_more(void *start, size_t size) -{ - start = (void *)(((unsigned long)start + 3) & ~3); - size &= ~3; - - /* Invalidate - buffer must be coherent */ - dump_dcache_range(start, size); - - start = (void *)addr_virt_to_phys((unsigned long)start); - - pcm_rec_peak_addr = start; - dma_rec_bd.buf_addr = start; - dma_rec_bd.mode.count = size; - dma_rec_bd.mode.command = TRANSFER_16BIT; - dma_rec_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR; -} - -void pcm_rec_dma_stop(void) -{ - /* Stop receiving data */ - sdma_channel_stop(DMA_REC_CH_NUM); - - imx31_regclr32(&SSI_SIER1, SSI_SIER_RDMAE); - - SSI_SCR1 &= ~SSI_SCR_RE; /* Disable RX */ - SSI_SRCR1 &= ~SSI_SRCR_RFEN0; /* Disable RX FIFO */ - - /* Set state before pending to prevent race with interrupt */ - /* Do not enable DMA requests on unlock */ - dma_rec_data.state = 0; - dma_rec_data.callback_pending = 0; -} - -void pcm_rec_dma_start(void *addr, size_t size) -{ - pcm_rec_dma_stop(); - - addr = (void *)(((unsigned long)addr + 3) & ~3); - size &= ~3; - - if (size <= 0) - return; - - if (!sdma_channel_reset(DMA_REC_CH_NUM)) - return; - - /* Invalidate - buffer must be coherent */ - dump_dcache_range(addr, size); - - addr = (void *)addr_virt_to_phys((unsigned long)addr); - pcm_rec_peak_addr = addr; - dma_rec_bd.buf_addr = addr; - dma_rec_bd.mode.count = size; - dma_rec_bd.mode.command = TRANSFER_16BIT; - dma_rec_bd.mode.status = BD_DONE | BD_WRAP | BD_INTR; - - dma_rec_data.state = 1; - - SSI_SRCR1 |= SSI_SRCR_RFEN0; /* Enable RX FIFO */ - - /* Ensure clear FIFO */ - while (SSI_SFCSR1 & SSI_SFCSR_RFCNT0) - SSI_SRX0_1; - - /* Enable receive */ - SSI_SCR1 |= SSI_SCR_RE; - sdma_channel_run(DMA_REC_CH_NUM); -} - -void pcm_rec_dma_close(void) -{ - pcm_rec_dma_stop(); - sdma_channel_close(DMA_REC_CH_NUM); -} - -void pcm_rec_dma_init(void) -{ - pcm_rec_dma_stop(); - - /* Init channel information */ - dma_rec_cd.bd_count = 1; - dma_rec_cd.callback = rec_dma_callback; - dma_rec_cd.shp_addr = SDMA_PER_ADDR_SSI1_RX1; - dma_rec_cd.wml = SDMA_SSI_RXFIFO_WML*2; - dma_rec_cd.per_type = SDMA_PER_SSI; - dma_rec_cd.tran_type = SDMA_TRAN_PER_2_EMI; - dma_rec_cd.event_id1 = SDMA_REQ_SSI1_RX1; - - sdma_channel_init(DMA_REC_CH_NUM, &dma_rec_cd, &dma_rec_bd); - sdma_channel_set_priority(DMA_REC_CH_NUM, DMA_REC_CH_PRIORITY); -} - -const void * pcm_rec_dma_get_peak_buffer(int *count) -{ - static unsigned long pda DEVBSS_ATTR; - unsigned long buf, addr, end, bufend; - int oldstatus; - - /* read burst dma destination address register in channel context */ - sdma_read_words(&pda, CHANNEL_CONTEXT_ADDR(DMA_REC_CH_NUM)+0x0a, 1); - - oldstatus = disable_irq_save(); - end = pda; - buf = (unsigned long)dma_rec_bd.buf_addr; - addr = (unsigned long)pcm_rec_peak_addr; - bufend = buf + dma_rec_bd.mode.count; - restore_irq(oldstatus); - - /* Be addresses are coherent (no buffer change during read) */ - if (addr >= buf && addr < bufend && - end >= buf && end < bufend) - { - *count = (end >> 2) - (addr >> 2); - return (void *)(addr & ~3); - } - - *count = 0; - return NULL; -} - -#endif /* HAVE_RECORDING */ diff --git a/firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.c new file mode 100644 index 0000000000..7e3b39dba8 --- /dev/null +++ b/firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.c @@ -0,0 +1,150 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006 by Linus Nielsen Feltzing + * + * 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 "usb.h" +#include "usb_core.h" +#include "power.h" +#include "power-gigabeat-s.h" +#include "backlight.h" +#include "backlight-target.h" +#include "avic-imx31.h" +#include "mc13783.h" +#if CONFIG_TUNER +#include "fmradio_i2c.h" +#endif + +static unsigned int power_status = POWER_INPUT_NONE; + +/* Detect which power sources are present. */ +unsigned int power_input_status(void) +{ + unsigned int status = power_status; + + if (GPIO3_DR & (1 << 20)) + status |= POWER_INPUT_BATTERY; + + if (usb_allowed_current() < 500) + { + /* ACK that USB is connected but NOT chargeable */ + status &= ~(POWER_INPUT_USB_CHARGER & POWER_INPUT_CHARGER); + } + + return status; +} + +/* Detect changes in presence of the AC adaptor. */ +void charger_main_detect_event(void) +{ + if (mc13783_read(MC13783_INTERRUPT_SENSE0) & MC13783_SE1S) + power_status |= POWER_INPUT_MAIN_CHARGER; + else + power_status &= ~POWER_INPUT_MAIN_CHARGER; +} + +/* Detect changes in USB bus power. Called from usb connect event handler. */ +void charger_usb_detect_event(int status) +{ + /* USB plugged does not imply charging is possible or even + * powering the device to maintain the battery. */ + if (status == USB_INSERTED) + power_status |= POWER_INPUT_USB_CHARGER; + else + power_status &= ~POWER_INPUT_USB_CHARGER; +} + +/* charging_state is implemented in powermgmt-imx31.c */ + +void ide_power_enable(bool on) +{ + if (!on) + { + /* Bus must be isolated before power off */ + imx31_regset32(&GPIO2_DR, (1 << 16)); + } + + /* HD power switch */ + imx31_regmod32(&GPIO3_DR, on ? (1 << 5) : 0, (1 << 5)); + + if (on) + { + /* Bus switch may be turned on after powerup */ + sleep(HZ/10); + imx31_regclr32(&GPIO2_DR, (1 << 16)); + } +} + +bool ide_powered(void) +{ + return (GPIO3_DR & (1 << 5)) != 0; +} + +#if CONFIG_TUNER +static bool tuner_on = false; + +bool tuner_power(bool status) +{ + if (status != tuner_on) + { + tuner_on = status; + /* Handle power and pin setup */ + fmradio_i2c_enable(status); + status = !status; + } + + return status; +} + +bool tuner_powered(void) +{ + return tuner_on; +} +#endif /* #if CONFIG_TUNER */ + +void power_off(void) +{ + /* Cut backlight */ + _backlight_off(); + + /* Let it fade */ + sleep(5*HZ/4); + + /* Set user off mode */ + mc13783_set(MC13783_POWER_CONTROL0, MC13783_USEROFFSPI); + + /* Wait for power cut */ + disable_interrupt(IRQ_FIQ_STATUS); + + while (1); +} + +void power_init(void) +{ +#if CONFIG_TUNER + fmradio_i2c_init(); +#endif + + /* Poll initial state */ + charger_main_detect_event(); + + /* Enable detect event */ + mc13783_enable_event(MC13783_SE1_EVENT); +} diff --git a/firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.h b/firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.h new file mode 100644 index 0000000000..9294de102c --- /dev/null +++ b/firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.h @@ -0,0 +1,27 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2008 by Nils Wallménius + * + * 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. + * + ****************************************************************************/ +#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/power-imx31.c b/firmware/target/arm/imx31/gigabeat-s/power-imx31.c deleted file mode 100644 index b29d3cd0fb..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/power-imx31.c +++ /dev/null @@ -1,150 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2006 by Linus Nielsen Feltzing - * - * 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 "usb.h" -#include "usb_core.h" -#include "power.h" -#include "power-imx31.h" -#include "backlight.h" -#include "backlight-target.h" -#include "avic-imx31.h" -#include "mc13783.h" -#if CONFIG_TUNER -#include "fmradio_i2c.h" -#endif - -static unsigned int power_status = POWER_INPUT_NONE; - -/* Detect which power sources are present. */ -unsigned int power_input_status(void) -{ - unsigned int status = power_status; - - if (GPIO3_DR & (1 << 20)) - status |= POWER_INPUT_BATTERY; - - if (usb_allowed_current() < 500) - { - /* ACK that USB is connected but NOT chargeable */ - status &= ~(POWER_INPUT_USB_CHARGER & POWER_INPUT_CHARGER); - } - - return status; -} - -/* Detect changes in presence of the AC adaptor. */ -void charger_main_detect_event(void) -{ - if (mc13783_read(MC13783_INTERRUPT_SENSE0) & MC13783_SE1S) - power_status |= POWER_INPUT_MAIN_CHARGER; - else - power_status &= ~POWER_INPUT_MAIN_CHARGER; -} - -/* Detect changes in USB bus power. Called from usb connect event handler. */ -void charger_usb_detect_event(int status) -{ - /* USB plugged does not imply charging is possible or even - * powering the device to maintain the battery. */ - if (status == USB_INSERTED) - power_status |= POWER_INPUT_USB_CHARGER; - else - power_status &= ~POWER_INPUT_USB_CHARGER; -} - -/* charging_state is implemented in powermgmt-imx31.c */ - -void ide_power_enable(bool on) -{ - if (!on) - { - /* Bus must be isolated before power off */ - imx31_regset32(&GPIO2_DR, (1 << 16)); - } - - /* HD power switch */ - imx31_regmod32(&GPIO3_DR, on ? (1 << 5) : 0, (1 << 5)); - - if (on) - { - /* Bus switch may be turned on after powerup */ - sleep(HZ/10); - imx31_regclr32(&GPIO2_DR, (1 << 16)); - } -} - -bool ide_powered(void) -{ - return (GPIO3_DR & (1 << 5)) != 0; -} - -#if CONFIG_TUNER -static bool tuner_on = false; - -bool tuner_power(bool status) -{ - if (status != tuner_on) - { - tuner_on = status; - /* Handle power and pin setup */ - fmradio_i2c_enable(status); - status = !status; - } - - return status; -} - -bool tuner_powered(void) -{ - return tuner_on; -} -#endif /* #if CONFIG_TUNER */ - -void power_off(void) -{ - /* Cut backlight */ - _backlight_off(); - - /* Let it fade */ - sleep(5*HZ/4); - - /* Set user off mode */ - mc13783_set(MC13783_POWER_CONTROL0, MC13783_USEROFFSPI); - - /* Wait for power cut */ - disable_interrupt(IRQ_FIQ_STATUS); - - while (1); -} - -void power_init(void) -{ -#if CONFIG_TUNER - fmradio_i2c_init(); -#endif - - /* Poll initial state */ - charger_main_detect_event(); - - /* Enable detect event */ - mc13783_enable_event(MC13783_SE1_EVENT); -} diff --git a/firmware/target/arm/imx31/gigabeat-s/power-imx31.h b/firmware/target/arm/imx31/gigabeat-s/power-imx31.h deleted file mode 100644 index 9294de102c..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/power-imx31.h +++ /dev/null @@ -1,27 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2008 by Nils Wallménius - * - * 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. - * - ****************************************************************************/ -#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/powermgmt-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/powermgmt-gigabeat-s.c new file mode 100644 index 0000000000..34abf04940 --- /dev/null +++ b/firmware/target/arm/imx31/gigabeat-s/powermgmt-gigabeat-s.c @@ -0,0 +1,940 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (c) 2008 by Michael Sevakis + * + * 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 +#include "config.h" +#include "system.h" +#include "thread.h" +#include "mc13783.h" +#include "adc.h" +#include "powermgmt.h" +#include "power.h" +#include "power-gigabeat-s.h" + +/* TODO: Battery tests to get the right values! */ +const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = +{ + 3450 +}; + +const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] = +{ + 3400 +}; + +/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */ +const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] = +{ + /* Toshiba Gigabeat Li Ion 830mAH figured from discharge curve */ + { 3480, 3550, 3590, 3610, 3630, 3650, 3700, 3760, 3800, 3910, 3990 }, +}; + +/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */ +const unsigned short percent_to_volt_charge[11] = +{ + /* Toshiba Gigabeat Li Ion 830mAH */ + 3480, 3550, 3590, 3610, 3630, 3650, 3700, 3760, 3800, 3910, 3990 +}; + +/* Returns battery voltage from ADC [millivolts] */ +unsigned int battery_adc_voltage(void) +{ + /* ADC reading 0-1023 = 2400mV-4700mV */ + return ((adc_read(ADC_BATTERY) * 2303) >> 10) + 2400; +} + +/* Returns the application supply voltage from ADC [millvolts] */ +unsigned int application_supply_adc_voltage(void) +{ + return ((adc_read(ADC_APPLICATION_SUPPLY) * 2303) >> 10) + 2400; +} + +unsigned int chrgraw_adc_voltage(void) +{ + return (adc_read(ADC_CHARGER_VOLTAGE) * 23023) >> 10; +} + +/* Returns battery charge current from ADC [milliamps] */ +int battery_adc_charge_current(void) +{ + /* Positive reading = charger to battery + * Negative reading = battery to charger terminal + * ADC reading -512-511 = -2875mA-2875mA */ + unsigned int value = adc_read(ADC_CHARGER_CURRENT); + int I; + + if (value == ADC_READ_ERROR) + return INT_MIN; + + I = ((((int32_t)value << 22) >> 22) * 2881) >> 9; + return ILEVEL_ADJUST_IN(I); +} + +/* Estimate power dissipation in the charge path regulator in mW. */ +unsigned int cccv_regulator_dissipation(void) +{ + /* BATTISNS is shorted to BATT so we don't need to use the + * battery current reading. */ + int chrgraw = (adc_read(ADC_CHARGER_VOLTAGE) * 230225) >> 10; + int batt = ((adc_read(ADC_BATTERY) * 23023) >> 10) + 24000; + int ichrgsn = adc_read(ADC_CHARGER_CURRENT); + ichrgsn = ((((int32_t)ichrgsn << 22) >> 22) * 2881) >> 9; + ichrgsn = abs(ichrgsn); + + return (chrgraw - ichrgsn - batt)*ILEVEL_ADJUST_IN(ichrgsn) / 10000; +} + +/* Returns battery temperature from ADC [deg-C] */ +int battery_adc_temp(void) +{ + /* E[volts] = value * 2.3V / 1023 + * R[ohms] = E/I = E[volts] / 0.00002[A] (Thermistor bias current source) + * + * Steinhart-Hart thermistor equation: + * [A + B*ln(R) + D*ln^3(R)] = 1 / T[°K] + * + * Coeffients that fit experimental data (one thermistor so far, one run): + * A = 0.0013002631685462800 + * B = 0.0002000841932612330 + * D = 0.0000000640446750919 + */ + static const unsigned short ntc_table[] = + { +#if 0 /* These have degree deltas > 1 (except the final two) so leave them + * out. 70 deg C upper limit is quite sufficient. */ + 0, /* INF */ 1, /* 171 */ 2, /* 145 */ 3, /* 130 */ + 4, /* 121 */ 5, /* 114 */ 6, /* 108 */ 7, /* 104 */ + 8, /* 100 */ 9, /* 96 */ 10, /* 93 */ 11, /* 91 */ + 12, /* 88 */ 13, /* 86 */ 14, /* 84 */ 15, /* 82 */ + 16, /* 81 */ 17, /* 79 */ 18, /* 78 */ 19, /* 76 */ + 20, /* 75 */ 21, /* 74 */ 22, /* 72 */ 23, /* 71 */ +#endif + 24, /* 70 */ 25, /* 69 */ 26, /* 68 */ 27, /* 67 */ + 28, /* 66 */ 30, /* 65 */ 31, /* 64 */ 32, /* 63 */ + 33, /* 62 */ 35, /* 61 */ 36, /* 60 */ 38, /* 59 */ + 39, /* 58 */ 41, /* 57 */ 43, /* 56 */ 45, /* 55 */ + 47, /* 54 */ 49, /* 53 */ 51, /* 52 */ 53, /* 51 */ + 56, /* 50 */ 58, /* 49 */ 61, /* 48 */ 63, /* 47 */ + 66, /* 46 */ 69, /* 45 */ 73, /* 44 */ 76, /* 43 */ + 80, /* 42 */ 83, /* 41 */ 87, /* 40 */ 92, /* 39 */ + 96, /* 38 */ 101, /* 37 */ 106, /* 36 */ 111, /* 35 */ + 116, /* 34 */ 122, /* 33 */ 128, /* 32 */ 135, /* 31 */ + 142, /* 30 */ 149, /* 29 */ 156, /* 28 */ 164, /* 27 */ + 173, /* 26 */ 182, /* 25 */ 192, /* 24 */ 202, /* 23 */ + 212, /* 22 */ 224, /* 21 */ 236, /* 20 */ 249, /* 19 */ + 262, /* 18 */ 277, /* 17 */ 292, /* 16 */ 308, /* 15 */ + 325, /* 14 */ 344, /* 13 */ 363, /* 12 */ 384, /* 11 */ + 406, /* 10 */ 429, /* 9 */ 454, /* 8 */ 480, /* 7 */ + 509, /* 6 */ 539, /* 5 */ 571, /* 4 */ 605, /* 3 */ + 641, /* 2 */ 680, /* 1 */ 722, /* 0 */ 766, /* -1 */ + 813, /* -2 */ 864, /* -3 */ 918, /* -4 */ 976, /* -5 */ + }; + + unsigned int value = adc_read(ADC_BATTERY_TEMP); + + if (value > 0) + { + unsigned i; + + for (i = 1; i < ARRAYLEN(ntc_table); i++) + { + if (ntc_table[i] > value) + break; + } + + return 71 - i; + } + + return INT_MIN; +} + +/** Charger control **/ + +/* All code has a preference for the main charger being connected over + * USB. USB is considered in the algorithm only if it is the sole source. */ +static uint32_t int_sense0 = 0; /* Interrupt Sense 0 bits */ +static unsigned int last_inputs = POWER_INPUT_NONE; /* Detect input changes */ +static int charger_total_timer = 0; /* Total allowed charging time */ +static int icharger_ave = 0; /* Filtered charging current */ +static bool charger_close = false; /* Shutdown notification */ +static bool service_wdt = true; /* Service the watchdog timer, if things + go very wrong, cease and shut down. */ +static uint32_t charger_setting = 0; /* Current ICHRG and VCHRG regulator + * setting (register bits) */ +#define CHARGER_ADJUST ((uint32_t)-1)/* Force change in regulator setting */ +static int autorecharge_counter = 0 ; /* Battery < threshold debounce */ +static int chgcurr_timer = 0; /* Countdown to CHGCURR error */ +#define AUTORECHARGE_COUNTDOWN (10*2) /* 10s debounce */ +#define WATCHDOG_TIMEOUT (10*2) /* If not serviced, poweroff in 10s */ +#define CHGCURR_TIMEOUT (4*2) /* 4s debounce */ + +/* Temperature monitoring */ +static enum +{ + TEMP_STATE_NORMAL = 0, /* Within range */ + TEMP_STATE_WAIT = 1, /* Went out of range, wait to come back */ + TEMP_LOW_LIMIT = 0, /* Min temp */ + TEMP_HIGH_LIMIT = 1, /* Max temp */ +} temp_state = TEMP_STATE_NORMAL; + +/* Set power thread priority for charging mode or not */ +static inline void charging_set_thread_priority(bool charging) +{ +#ifdef HAVE_PRIORITY_SCHEDULING + thread_set_priority(THREAD_ID_CURRENT, + charging ? PRIORITY_REALTIME : PRIORITY_SYSTEM); +#endif + (void)charging; +} + +/* Update filtered charger current - exponential moving average */ +static bool charger_current_filter_step(void) +{ + int value = battery_adc_charge_current(); + + if (value == ADC_READ_ERROR) + return false; + + icharger_ave += value - (icharger_ave / ICHARGER_AVE_SAMPLES); + return true; +} + +/* Return true if the main charger is connected. */ +static bool main_charger_connected(void) +{ + return (last_inputs & + POWER_INPUT_MAIN_CHARGER & + POWER_INPUT_CHARGER) != 0; +} + +/* Return the voltage level which should automatically trigger + * another recharge cycle based upon which power source is available. + * Assumes at least one is. */ +static unsigned int auto_recharge_voltage(void) +{ + if (main_charger_connected()) + return BATT_VAUTO_RECHARGE; + else + return BATT_USB_VAUTO_RECHARGE; +} + +/* Return greater of supply (BP) or filtered battery voltage. */ +unsigned int input_millivolts(void) +{ + unsigned int app_millivolts = application_supply_adc_voltage(); + unsigned int bat_millivolts = battery_voltage(); + + return MAX(app_millivolts, bat_millivolts); +} + +/* Get smoothed readings for initializing filtered data. */ +static int stat_battery_reading(int type) +{ + int high = INT_MIN, low = INT_MAX; + int value = 0; + int i; + + for (i = 0; i < 7; i++) + { + int reading = ADC_READ_ERROR; + + sleep(2); /* Get unique readings */ + + switch (type) + { + case ADC_BATTERY: + reading = battery_adc_voltage(); + break; + + case ADC_CHARGER_CURRENT: + reading = battery_adc_charge_current(); + break; + } + + if (reading == ADC_READ_ERROR) + return INT_MIN; + + if (reading > high) + high = reading; + + if (reading < low) + low = reading; + + value += reading; + } + + /* Discard extremes */ + return (value - high - low) / 5; +} + +/* Update filtered battery voltage instead of waiting for filter + * decay. */ +static bool update_filtered_battery_voltage(void) +{ + int millivolts = stat_battery_reading(ADC_BATTERY); + + if (millivolts != INT_MIN) + { + reset_battery_filter(millivolts); + return true; + } + + return false; +} + +/* Sets the charge current limit based upon state. charge_state should be + * set before calling. */ +static bool adjust_charger_current(void) +{ + static const uint8_t charger_bits[][2] = + { + [DISCHARGING] = + { + /* These are actually zeros but reflect this setting */ + MC13783_ICHRG_0MA | MC13783_VCHRG_4_050V, + MC13783_ICHRG_0MA | MC13783_VCHRG_4_050V, + }, + /* Main(+USB): Charge slowly from the adapter until voltage is + * sufficient for normal charging. + * + * USB: The truth is that things will probably not make it this far. + * Cover the case, just in case the disk isn't used and it is + * manageable. */ + [TRICKLE] = + { + BATTERY_ITRICKLE | BATTERY_VCHARGING, + BATTERY_ITRICKLE_USB | BATTERY_VCHARGING + }, + [TOPOFF] = + { + BATTERY_IFAST | BATTERY_VCHARGING, + BATTERY_IFAST_USB | BATTERY_VCHARGING + }, + [CHARGING] = + { + BATTERY_IFAST | BATTERY_VCHARGING, + BATTERY_IFAST_USB | BATTERY_VCHARGING + }, + /* Must maintain battery when on USB power only - utterly nasty + * but true and something retailos does (it will even end up charging + * the battery but not reporting that it is doing so). + * Float lower than MAX - could end up slightly discharging after + * a full charge but this is safer than maxing it out. */ + [CHARGING+1] = + { + BATTERY_IFLOAT_USB | BATTERY_VFLOAT_USB, + BATTERY_IMAINTAIN_USB | BATTERY_VMAINTAIN_USB + }, +#if 0 + /* Slower settings to so that the linear regulator doesn't dissipate + * an excessive amount of power when coming out of precharge state. */ + [CHARGING+2] = + { + BATTERY_ISLOW | BATTERY_VCHARGING, + BATTERY_ISLOW_USB | BATTEYR_VCHARGING + }, +#endif + }; + + bool success = false; + int usb_select; + uint32_t i; + + usb_select = ((last_inputs & POWER_INPUT) == POWER_INPUT_USB) + ? 1 : 0; + + if (charge_state == DISCHARGING && usb_select == 1) + { + /* USB-only, DISCHARGING, = maintaining battery */ + int select = (last_inputs & POWER_INPUT_CHARGER) ? 0 : 1; + charger_setting = charger_bits[CHARGING+1][select]; + } + else + { + /* Take very good care not to write garbage. */ + int state = charge_state; + + if (state < DISCHARGING || state > CHARGING) + state = DISCHARGING; + + charger_setting = charger_bits[state][usb_select]; + } + + if (charger_setting != 0) + { + if ((charger_setting & MC13783_VCHRG) > BATTERY_VCHARGING || + (charger_setting & MC13783_ICHRG) > BATTERY_IFAST) + { + /* Table is corrupted somehow. We shouldn't run at all. + * + * Explanation: On two occasions, even though this driver monitors + * the regulator register settings on each step and + * ensures that only valid table indexes are used, + * the current and voltage seem to be misregulated, + * resulting in excessively high battery voltage that + * will trip the battery protection. After careful + * review it seems that two possibilities exist: + * This code or data got trashed at some point or + * there really is a hardware bug of some sort. So + * far the cause is unknown. Voltage is also + * monitored in the CHARGING case for that reason. + * The solution for data or code corruption is to + * just panic and refuse to run the device. The + * solution for overvoltage due to hardware bug is to + * disable the charging. The action taken will reveal + * the true cause, thus _who_ is responsible. + * "Burning lithium is baaaad", so sayeth The Council + * of Seven Ascended Masters. */ + charge_state = CHARGE_STATE_DISABLED; + service_wdt = false; + } + else + { + /* Turn on 5K pulldown. */ + i = mc13783_set(MC13783_CHARGER, MC13783_CHRGRAWPDEN); + + if (i != MC13783_DATA_ERROR) + { + charging_set_thread_priority(true); + + /* Turn regulator logically ON. Hardware may still override. + */ + i = mc13783_write_masked(MC13783_CHARGER, charger_setting, + MC13783_ICHRG | MC13783_VCHRG); + + if (i != MC13783_DATA_ERROR) + { + int icharger; + + /* Enable charge current conversion */ + adc_enable_channel(ADC_CHARGER_CURRENT, true); + + /* Charge path regulator turn on takes ~100ms max. */ + sleep(HZ/10); + + icharger = stat_battery_reading(ADC_CHARGER_CURRENT); + + if (icharger != INT_MIN) + { + icharger_ave = icharger * ICHARGER_AVE_SAMPLES; + + if (update_filtered_battery_voltage()) + return true; + } + } + } + + /* Force regulator OFF. */ + charge_state = CHARGE_STATE_ERROR; + } + } + + /* Turn regulator OFF. */ + icharger_ave = 0; + i = mc13783_write_masked(MC13783_CHARGER, + MC13783_ICHRG_0MA | MC13783_VCHRG_4_050V, + MC13783_ICHRG | MC13783_VCHRG | + MC13783_CHRGRAWPDEN); + + if (MC13783_DATA_ERROR == i) + { + /* Failed. Force poweroff by not servicing the watchdog. */ + service_wdt = false; + } + else if (0 == charger_setting) + { + /* Here because OFF was requested state */ + success = true; + } + + charger_setting = 0; + + adc_enable_channel(ADC_CHARGER_CURRENT, false); + update_filtered_battery_voltage(); + charging_set_thread_priority(false); + + return success; +} + +/* Stop the charger - if USB only then the regulator will not really be + * turned off. ERROR or DISABLED will turn it off however. */ +static void stop_charger(void) +{ + charger_total_timer = 0; + + if (charge_state > DISCHARGING) + charge_state = DISCHARGING; + + adjust_charger_current(); +} + +/* Return OK if it is acceptable to start the regulator. */ +static bool charging_ok(void) +{ + bool ok = charge_state >= DISCHARGING; /* Not an error condition? */ + + if (ok) + { + /* Is the battery even connected? */ + ok = (last_inputs & POWER_INPUT_BATTERY) != 0; + } + + if (ok) + { + /* No tolerance for any over/under temp - wait for it to + * come back into safe range. */ + static const signed char temp_ranges[2][2] = + { + /* Temperature range before beginning charging */ + { BATTERY_CHARGE_MIN, + BATTERY_CHARGE_MAX }, + /* Temperature range after out-of-range detected - + charging will self-resume */ + { BATTERY_CHARGE_RESTART_MIN, + BATTERY_CHARGE_RESTART_MAX }, + }; + + int temp = battery_adc_temp(); + const signed char *range = temp_ranges[temp_state]; + + ok = temp >= range[TEMP_LOW_LIMIT] && + temp <= range[TEMP_HIGH_LIMIT]; + + switch (temp_state) + { + case TEMP_STATE_NORMAL: + if (!ok) + temp_state = TEMP_STATE_WAIT; + break; + + case TEMP_STATE_WAIT: + if (ok) + temp_state = TEMP_STATE_NORMAL; + break; + + default: + break; + } + } + + if (ok) + { + /* Any events that should stop the regulator? */ + + /* Overvoltage at CHRGRAW? */ + ok = (int_sense0 & MC13783_CHGOVS) == 0; + + if (ok) + { + /* CHGCURR sensed? */ + ok = (int_sense0 & MC13783_CHGCURRS) != 0; + + if (!ok) + { + /* Debounce transient states */ + if (chgcurr_timer > 0) + { + chgcurr_timer--; + ok = true; + } + } + else + { + chgcurr_timer = CHGCURR_TIMEOUT; + } + } + + /* Charger may need to be reinserted */ + if (!ok) + charge_state = CHARGE_STATE_ERROR; + } + + if (charger_setting != 0) + { + if (ok) + { + /* Protect against any conceivable overcharge/voltage condition + * before hardware protection must intervene. Disable charger + * until reboot. */ + ok = battery_voltage() < BATT_TOO_HIGH; + if (!ok) + charge_state = CHARGE_STATE_DISABLED; + } + + if (ok) + { + /* Watch to not overheat FET (nothing should go over about 1012.7mW). + * Trying a higher voltage AC adapter can work (up to 6.90V) but + * we'll just reject that. Reducing current for adapters that bring + * CHRGRAW to > 4.900V is another possible action. */ + ok = cccv_regulator_dissipation() < 1150; + if (!ok) + charge_state = CHARGE_STATE_ERROR; + } + + if (!ok) + { + int state = charge_state; + + if (state > DISCHARGING) + state = DISCHARGING; + + /* Force off for all states including maintaining the battery level + * on USB. */ + charge_state = CHARGE_STATE_ERROR; + stop_charger(); + charge_state = state; + } + } + + return ok; +} + +void powermgmt_init_target(void) +{ +#ifdef IMX31_ALLOW_CHARGING + const uint32_t regval_w = + MC13783_VCHRG_4_050V | MC13783_ICHRG_0MA | + MC13783_ICHRGTR_0MA | MC13783_OVCTRL_6_90V; + + /* Use watchdog to shut system down if we lose control of the charging + * hardware. */ + watchdog_init(WATCHDOG_TIMEOUT); + + mc13783_write(MC13783_CHARGER, regval_w); + + if (mc13783_read(MC13783_CHARGER) == regval_w) + { + /* Divide CHRGRAW input by 10 */ + mc13783_clear(MC13783_ADC0, MC13783_CHRGRAWDIV); + /* Turn off BATTDETB. It's worthless on MESx0V since the battery + * isn't removable (nor the thermistor). */ + mc13783_clear(MC13783_POWER_CONTROL0, MC13783_BATTDETEN); + } + else + { + /* Register has the wrong value - set error condition and disable + * since something is wrong. */ + charge_state = CHARGE_STATE_DISABLED; + stop_charger(); + } +#else + /* Disable charger use */ + charge_state = CHARGE_STATE_DISABLED; +#endif +} + +/* Returns true if the unit is charging the batteries. */ +bool charging_state(void) +{ + switch (charge_state) + { + case TRICKLE: + case TOPOFF: + case CHARGING: + return true; + default: + return false; + } +} + +/* Filtered battery charge current */ +int battery_charge_current(void) +{ + return icharger_ave / ICHARGER_AVE_SAMPLES; +} + +static void charger_plugged(void) +{ + adc_enable_channel(ADC_BATTERY_TEMP, true); + autorecharge_counter = -1; +} + +static void charger_unplugged(void) +{ + /* Charger pulled - turn off current sources (though hardware + * will have done that anyway). */ + if (charge_state > CHARGE_STATE_DISABLED) + { + /* Reset state and clear any error. If disabled, the charger + * will not have been started or will have been stopped already. */ + stop_charger(); + charge_state = DISCHARGING; + } + + /* Might need to reevaluate these bits in charger_none. */ + last_inputs &= ~(POWER_INPUT | POWER_INPUT_CHARGER); + temp_state = TEMP_STATE_NORMAL; + autorecharge_counter = 0; + chgcurr_timer = 0; + + adc_enable_channel(ADC_BATTERY_TEMP, false); +} + +static void charger_none(void) +{ + unsigned int pwr = power_thread_inputs; + + if (last_inputs != pwr) + { + last_inputs = pwr; + + if (charge_state == CHARGE_STATE_DISABLED) + return; + + if ((pwr & (POWER_INPUT | POWER_INPUT_CHARGER)) == POWER_INPUT_USB) + { + /* USB connected but not configured. Maintain battery to the + * greatest degree possible. It probably won't be enough but the + * discharge won't be so severe. */ + charger_plugged(); + charger_setting = CHARGER_ADJUST; + } + else + { + charger_unplugged(); + last_inputs = pwr; /* Restore status */ + } + } + else if (charger_setting != 0) + { + /* Maintaining - keep filter going and check charge state */ + int_sense0 = mc13783_read(MC13783_INTERRUPT_SENSE0); + + if (!charger_current_filter_step()) + { + /* Failed to read current */ + charge_state = CHARGE_STATE_ERROR; + } + + charging_ok(); + } +} + +static void charger_control(void) +{ + unsigned int pwr = power_thread_inputs; + + if (last_inputs != pwr) + { + unsigned int changed = last_inputs ^ pwr; + + last_inputs = pwr; + + if (charger_setting != 0) + charger_setting = CHARGER_ADJUST; + + if (charge_state == DISCHARGING) + { + if (main_charger_connected()) + { + /* If main is connected, ignore USB plugs. */ + if (changed & POWER_INPUT_MAIN_CHARGER) + { + /* Main charger plugged - try charge */ + autorecharge_counter = -1; + } + } + else if (pwr & POWER_INPUT_USB_CHARGER + & POWER_INPUT_CHARGER) + { + /* USB power only */ + if (changed & POWER_INPUT_USB_CHARGER) + { + /* USB charger plugged - try charge */ + autorecharge_counter = -1; + } + else if (changed & POWER_INPUT_MAIN_CHARGER) + { + /* Main charger pulled - go to battery maintenence. */ + charger_setting = CHARGER_ADJUST; + } + } + } + } + + if (charger_setting != 0 && !charger_current_filter_step()) + { + /* Failed to read current */ + charge_state = CHARGE_STATE_ERROR; + } + + int_sense0 = mc13783_read(MC13783_INTERRUPT_SENSE0); + + if (!charging_ok()) + return; + + switch (charge_state) + { + case DISCHARGING: + { + /* Battery voltage may have dropped and a charge cycle should + * start again. Debounced. */ + if (autorecharge_counter < 0 && + battery_adc_voltage() < BATT_FULL_VOLTAGE) + { + /* Try starting a cycle now if battery isn't already topped + * off to allow user to ensure the battery is full. */ + } + else if (battery_voltage() > auto_recharge_voltage()) + { + /* Still above threshold - reset counter */ + autorecharge_counter = AUTORECHARGE_COUNTDOWN; + break; + } + else if (autorecharge_counter > 0) + { + /* Coundown to restart */ + autorecharge_counter--; + break; + } + + autorecharge_counter = 0; + + charging_set_thread_priority(true); + + if (stat_battery_reading(ADC_BATTERY) < BATT_VTRICKLE_CHARGE) + { + /* Battery is deeply discharged - precharge at lower current. */ + charge_state = TRICKLE; + } + else + { + /* Ok for fast charge */ + charge_state = CHARGING; + } + + charger_setting = CHARGER_ADJUST; + charger_total_timer = CHARGER_TOTAL_TIMER*60*2; + break; + } /* DISCHARGING: */ + + case TRICKLE: /* Very low - precharge */ + { + if (battery_voltage() <= BATT_VTRICKLE_CHARGE) + break; + + /* Switch to normal charge mode. */ + charge_state = CHARGING; + charger_setting = CHARGER_ADJUST; + break; + } /* TRICKLE: */ + + case CHARGING: /* Constant-current stage */ + case TOPOFF: /* Constant-voltage stage */ + { + /* Reg. mode is more informative than an operational necessity. */ + charge_state = (int_sense0 & MC13783_CCCVS) ? TOPOFF : CHARGING; + + if (main_charger_connected()) + { + /* Monitor and stop if current drops below threshold. */ + if (battery_charge_current() > BATTERY_ICHARGE_COMPLETE) + break; + } + else + { + /* Accurate I-level can't be determined since device also + * powers through the I sense. This simply stops the reporting + * of charging but the regulator remains on. */ + if (battery_voltage() <= BATT_USB_VSTOP) + break; + } + + stop_charger(); + break; + } /* CHARGING: TOPOFF: */ + + default: + break; + } /* switch */ + + /* Check if charger timer expired and stop it if so. */ + if (charger_total_timer > 0 && --charger_total_timer == 0) + { + charge_state = CHARGE_STATE_ERROR; + stop_charger(); /* Time ran out - error */ + } +} + +/* Main charging algorithm - called from powermgmt.c */ +void charging_algorithm_step(void) +{ +#ifdef IMX31_ALLOW_CHARGING + if (service_wdt) + watchdog_service(); +#endif + + /* Switch by input state */ + switch (charger_input_state) + { + case NO_CHARGER: + charger_none(); + break; + + case CHARGER_PLUGGED: + charger_plugged(); + break; + + case CHARGER: + charger_control(); + break; + + case CHARGER_UNPLUGGED: + charger_unplugged(); + break; + } /* switch */ + + if (charger_close) + { + if (charge_state != CHARGE_STATE_DISABLED) + { + /* Disable starts while shutting down */ + charge_state = CHARGE_STATE_DISABLED; + stop_charger(); + } + + charger_close = false; + return; + } + + if (charger_setting != 0) + { + if ((mc13783_read(MC13783_CHARGER) & (MC13783_ICHRG | MC13783_VCHRG)) != + charger_setting) + { + /* The hardware setting doesn't match. It could have turned the + * charger off in a race of plugging/unplugging or the setting + * was changed in one of the calls. */ + adjust_charger_current(); + } + } +} + +/* Disable the charger and prepare for poweroff - called off-thread so we + * signal the charging thread to prepare to quit. */ +void charging_algorithm_close(void) +{ + charger_close = true; + + /* Power management thread will set it false again */ + while (charger_close) + sleep(HZ/10); +} diff --git a/firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c b/firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c deleted file mode 100644 index bb9b8c23af..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c +++ /dev/null @@ -1,940 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (c) 2008 by Michael Sevakis - * - * 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 -#include "config.h" -#include "system.h" -#include "thread.h" -#include "mc13783.h" -#include "adc.h" -#include "powermgmt.h" -#include "power.h" -#include "power-imx31.h" - -/* TODO: Battery tests to get the right values! */ -const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = -{ - 3450 -}; - -const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] = -{ - 3400 -}; - -/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */ -const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] = -{ - /* Toshiba Gigabeat Li Ion 830mAH figured from discharge curve */ - { 3480, 3550, 3590, 3610, 3630, 3650, 3700, 3760, 3800, 3910, 3990 }, -}; - -/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */ -const unsigned short percent_to_volt_charge[11] = -{ - /* Toshiba Gigabeat Li Ion 830mAH */ - 3480, 3550, 3590, 3610, 3630, 3650, 3700, 3760, 3800, 3910, 3990 -}; - -/* Returns battery voltage from ADC [millivolts] */ -unsigned int battery_adc_voltage(void) -{ - /* ADC reading 0-1023 = 2400mV-4700mV */ - return ((adc_read(ADC_BATTERY) * 2303) >> 10) + 2400; -} - -/* Returns the application supply voltage from ADC [millvolts] */ -unsigned int application_supply_adc_voltage(void) -{ - return ((adc_read(ADC_APPLICATION_SUPPLY) * 2303) >> 10) + 2400; -} - -unsigned int chrgraw_adc_voltage(void) -{ - return (adc_read(ADC_CHARGER_VOLTAGE) * 23023) >> 10; -} - -/* Returns battery charge current from ADC [milliamps] */ -int battery_adc_charge_current(void) -{ - /* Positive reading = charger to battery - * Negative reading = battery to charger terminal - * ADC reading -512-511 = -2875mA-2875mA */ - unsigned int value = adc_read(ADC_CHARGER_CURRENT); - int I; - - if (value == ADC_READ_ERROR) - return INT_MIN; - - I = ((((int32_t)value << 22) >> 22) * 2881) >> 9; - return ILEVEL_ADJUST_IN(I); -} - -/* Estimate power dissipation in the charge path regulator in mW. */ -unsigned int cccv_regulator_dissipation(void) -{ - /* BATTISNS is shorted to BATT so we don't need to use the - * battery current reading. */ - int chrgraw = (adc_read(ADC_CHARGER_VOLTAGE) * 230225) >> 10; - int batt = ((adc_read(ADC_BATTERY) * 23023) >> 10) + 24000; - int ichrgsn = adc_read(ADC_CHARGER_CURRENT); - ichrgsn = ((((int32_t)ichrgsn << 22) >> 22) * 2881) >> 9; - ichrgsn = abs(ichrgsn); - - return (chrgraw - ichrgsn - batt)*ILEVEL_ADJUST_IN(ichrgsn) / 10000; -} - -/* Returns battery temperature from ADC [deg-C] */ -int battery_adc_temp(void) -{ - /* E[volts] = value * 2.3V / 1023 - * R[ohms] = E/I = E[volts] / 0.00002[A] (Thermistor bias current source) - * - * Steinhart-Hart thermistor equation: - * [A + B*ln(R) + D*ln^3(R)] = 1 / T[°K] - * - * Coeffients that fit experimental data (one thermistor so far, one run): - * A = 0.0013002631685462800 - * B = 0.0002000841932612330 - * D = 0.0000000640446750919 - */ - static const unsigned short ntc_table[] = - { -#if 0 /* These have degree deltas > 1 (except the final two) so leave them - * out. 70 deg C upper limit is quite sufficient. */ - 0, /* INF */ 1, /* 171 */ 2, /* 145 */ 3, /* 130 */ - 4, /* 121 */ 5, /* 114 */ 6, /* 108 */ 7, /* 104 */ - 8, /* 100 */ 9, /* 96 */ 10, /* 93 */ 11, /* 91 */ - 12, /* 88 */ 13, /* 86 */ 14, /* 84 */ 15, /* 82 */ - 16, /* 81 */ 17, /* 79 */ 18, /* 78 */ 19, /* 76 */ - 20, /* 75 */ 21, /* 74 */ 22, /* 72 */ 23, /* 71 */ -#endif - 24, /* 70 */ 25, /* 69 */ 26, /* 68 */ 27, /* 67 */ - 28, /* 66 */ 30, /* 65 */ 31, /* 64 */ 32, /* 63 */ - 33, /* 62 */ 35, /* 61 */ 36, /* 60 */ 38, /* 59 */ - 39, /* 58 */ 41, /* 57 */ 43, /* 56 */ 45, /* 55 */ - 47, /* 54 */ 49, /* 53 */ 51, /* 52 */ 53, /* 51 */ - 56, /* 50 */ 58, /* 49 */ 61, /* 48 */ 63, /* 47 */ - 66, /* 46 */ 69, /* 45 */ 73, /* 44 */ 76, /* 43 */ - 80, /* 42 */ 83, /* 41 */ 87, /* 40 */ 92, /* 39 */ - 96, /* 38 */ 101, /* 37 */ 106, /* 36 */ 111, /* 35 */ - 116, /* 34 */ 122, /* 33 */ 128, /* 32 */ 135, /* 31 */ - 142, /* 30 */ 149, /* 29 */ 156, /* 28 */ 164, /* 27 */ - 173, /* 26 */ 182, /* 25 */ 192, /* 24 */ 202, /* 23 */ - 212, /* 22 */ 224, /* 21 */ 236, /* 20 */ 249, /* 19 */ - 262, /* 18 */ 277, /* 17 */ 292, /* 16 */ 308, /* 15 */ - 325, /* 14 */ 344, /* 13 */ 363, /* 12 */ 384, /* 11 */ - 406, /* 10 */ 429, /* 9 */ 454, /* 8 */ 480, /* 7 */ - 509, /* 6 */ 539, /* 5 */ 571, /* 4 */ 605, /* 3 */ - 641, /* 2 */ 680, /* 1 */ 722, /* 0 */ 766, /* -1 */ - 813, /* -2 */ 864, /* -3 */ 918, /* -4 */ 976, /* -5 */ - }; - - unsigned int value = adc_read(ADC_BATTERY_TEMP); - - if (value > 0) - { - unsigned i; - - for (i = 1; i < ARRAYLEN(ntc_table); i++) - { - if (ntc_table[i] > value) - break; - } - - return 71 - i; - } - - return INT_MIN; -} - -/** Charger control **/ - -/* All code has a preference for the main charger being connected over - * USB. USB is considered in the algorithm only if it is the sole source. */ -static uint32_t int_sense0 = 0; /* Interrupt Sense 0 bits */ -static unsigned int last_inputs = POWER_INPUT_NONE; /* Detect input changes */ -static int charger_total_timer = 0; /* Total allowed charging time */ -static int icharger_ave = 0; /* Filtered charging current */ -static bool charger_close = false; /* Shutdown notification */ -static bool service_wdt = true; /* Service the watchdog timer, if things - go very wrong, cease and shut down. */ -static uint32_t charger_setting = 0; /* Current ICHRG and VCHRG regulator - * setting (register bits) */ -#define CHARGER_ADJUST ((uint32_t)-1)/* Force change in regulator setting */ -static int autorecharge_counter = 0 ; /* Battery < threshold debounce */ -static int chgcurr_timer = 0; /* Countdown to CHGCURR error */ -#define AUTORECHARGE_COUNTDOWN (10*2) /* 10s debounce */ -#define WATCHDOG_TIMEOUT (10*2) /* If not serviced, poweroff in 10s */ -#define CHGCURR_TIMEOUT (4*2) /* 4s debounce */ - -/* Temperature monitoring */ -static enum -{ - TEMP_STATE_NORMAL = 0, /* Within range */ - TEMP_STATE_WAIT = 1, /* Went out of range, wait to come back */ - TEMP_LOW_LIMIT = 0, /* Min temp */ - TEMP_HIGH_LIMIT = 1, /* Max temp */ -} temp_state = TEMP_STATE_NORMAL; - -/* Set power thread priority for charging mode or not */ -static inline void charging_set_thread_priority(bool charging) -{ -#ifdef HAVE_PRIORITY_SCHEDULING - thread_set_priority(THREAD_ID_CURRENT, - charging ? PRIORITY_REALTIME : PRIORITY_SYSTEM); -#endif - (void)charging; -} - -/* Update filtered charger current - exponential moving average */ -static bool charger_current_filter_step(void) -{ - int value = battery_adc_charge_current(); - - if (value == ADC_READ_ERROR) - return false; - - icharger_ave += value - (icharger_ave / ICHARGER_AVE_SAMPLES); - return true; -} - -/* Return true if the main charger is connected. */ -static bool main_charger_connected(void) -{ - return (last_inputs & - POWER_INPUT_MAIN_CHARGER & - POWER_INPUT_CHARGER) != 0; -} - -/* Return the voltage level which should automatically trigger - * another recharge cycle based upon which power source is available. - * Assumes at least one is. */ -static unsigned int auto_recharge_voltage(void) -{ - if (main_charger_connected()) - return BATT_VAUTO_RECHARGE; - else - return BATT_USB_VAUTO_RECHARGE; -} - -/* Return greater of supply (BP) or filtered battery voltage. */ -unsigned int input_millivolts(void) -{ - unsigned int app_millivolts = application_supply_adc_voltage(); - unsigned int bat_millivolts = battery_voltage(); - - return MAX(app_millivolts, bat_millivolts); -} - -/* Get smoothed readings for initializing filtered data. */ -static int stat_battery_reading(int type) -{ - int high = INT_MIN, low = INT_MAX; - int value = 0; - int i; - - for (i = 0; i < 7; i++) - { - int reading = ADC_READ_ERROR; - - sleep(2); /* Get unique readings */ - - switch (type) - { - case ADC_BATTERY: - reading = battery_adc_voltage(); - break; - - case ADC_CHARGER_CURRENT: - reading = battery_adc_charge_current(); - break; - } - - if (reading == ADC_READ_ERROR) - return INT_MIN; - - if (reading > high) - high = reading; - - if (reading < low) - low = reading; - - value += reading; - } - - /* Discard extremes */ - return (value - high - low) / 5; -} - -/* Update filtered battery voltage instead of waiting for filter - * decay. */ -static bool update_filtered_battery_voltage(void) -{ - int millivolts = stat_battery_reading(ADC_BATTERY); - - if (millivolts != INT_MIN) - { - reset_battery_filter(millivolts); - return true; - } - - return false; -} - -/* Sets the charge current limit based upon state. charge_state should be - * set before calling. */ -static bool adjust_charger_current(void) -{ - static const uint8_t charger_bits[][2] = - { - [DISCHARGING] = - { - /* These are actually zeros but reflect this setting */ - MC13783_ICHRG_0MA | MC13783_VCHRG_4_050V, - MC13783_ICHRG_0MA | MC13783_VCHRG_4_050V, - }, - /* Main(+USB): Charge slowly from the adapter until voltage is - * sufficient for normal charging. - * - * USB: The truth is that things will probably not make it this far. - * Cover the case, just in case the disk isn't used and it is - * manageable. */ - [TRICKLE] = - { - BATTERY_ITRICKLE | BATTERY_VCHARGING, - BATTERY_ITRICKLE_USB | BATTERY_VCHARGING - }, - [TOPOFF] = - { - BATTERY_IFAST | BATTERY_VCHARGING, - BATTERY_IFAST_USB | BATTERY_VCHARGING - }, - [CHARGING] = - { - BATTERY_IFAST | BATTERY_VCHARGING, - BATTERY_IFAST_USB | BATTERY_VCHARGING - }, - /* Must maintain battery when on USB power only - utterly nasty - * but true and something retailos does (it will even end up charging - * the battery but not reporting that it is doing so). - * Float lower than MAX - could end up slightly discharging after - * a full charge but this is safer than maxing it out. */ - [CHARGING+1] = - { - BATTERY_IFLOAT_USB | BATTERY_VFLOAT_USB, - BATTERY_IMAINTAIN_USB | BATTERY_VMAINTAIN_USB - }, -#if 0 - /* Slower settings to so that the linear regulator doesn't dissipate - * an excessive amount of power when coming out of precharge state. */ - [CHARGING+2] = - { - BATTERY_ISLOW | BATTERY_VCHARGING, - BATTERY_ISLOW_USB | BATTEYR_VCHARGING - }, -#endif - }; - - bool success = false; - int usb_select; - uint32_t i; - - usb_select = ((last_inputs & POWER_INPUT) == POWER_INPUT_USB) - ? 1 : 0; - - if (charge_state == DISCHARGING && usb_select == 1) - { - /* USB-only, DISCHARGING, = maintaining battery */ - int select = (last_inputs & POWER_INPUT_CHARGER) ? 0 : 1; - charger_setting = charger_bits[CHARGING+1][select]; - } - else - { - /* Take very good care not to write garbage. */ - int state = charge_state; - - if (state < DISCHARGING || state > CHARGING) - state = DISCHARGING; - - charger_setting = charger_bits[state][usb_select]; - } - - if (charger_setting != 0) - { - if ((charger_setting & MC13783_VCHRG) > BATTERY_VCHARGING || - (charger_setting & MC13783_ICHRG) > BATTERY_IFAST) - { - /* Table is corrupted somehow. We shouldn't run at all. - * - * Explanation: On two occasions, even though this driver monitors - * the regulator register settings on each step and - * ensures that only valid table indexes are used, - * the current and voltage seem to be misregulated, - * resulting in excessively high battery voltage that - * will trip the battery protection. After careful - * review it seems that two possibilities exist: - * This code or data got trashed at some point or - * there really is a hardware bug of some sort. So - * far the cause is unknown. Voltage is also - * monitored in the CHARGING case for that reason. - * The solution for data or code corruption is to - * just panic and refuse to run the device. The - * solution for overvoltage due to hardware bug is to - * disable the charging. The action taken will reveal - * the true cause, thus _who_ is responsible. - * "Burning lithium is baaaad", so sayeth The Council - * of Seven Ascended Masters. */ - charge_state = CHARGE_STATE_DISABLED; - service_wdt = false; - } - else - { - /* Turn on 5K pulldown. */ - i = mc13783_set(MC13783_CHARGER, MC13783_CHRGRAWPDEN); - - if (i != MC13783_DATA_ERROR) - { - charging_set_thread_priority(true); - - /* Turn regulator logically ON. Hardware may still override. - */ - i = mc13783_write_masked(MC13783_CHARGER, charger_setting, - MC13783_ICHRG | MC13783_VCHRG); - - if (i != MC13783_DATA_ERROR) - { - int icharger; - - /* Enable charge current conversion */ - adc_enable_channel(ADC_CHARGER_CURRENT, true); - - /* Charge path regulator turn on takes ~100ms max. */ - sleep(HZ/10); - - icharger = stat_battery_reading(ADC_CHARGER_CURRENT); - - if (icharger != INT_MIN) - { - icharger_ave = icharger * ICHARGER_AVE_SAMPLES; - - if (update_filtered_battery_voltage()) - return true; - } - } - } - - /* Force regulator OFF. */ - charge_state = CHARGE_STATE_ERROR; - } - } - - /* Turn regulator OFF. */ - icharger_ave = 0; - i = mc13783_write_masked(MC13783_CHARGER, - MC13783_ICHRG_0MA | MC13783_VCHRG_4_050V, - MC13783_ICHRG | MC13783_VCHRG | - MC13783_CHRGRAWPDEN); - - if (MC13783_DATA_ERROR == i) - { - /* Failed. Force poweroff by not servicing the watchdog. */ - service_wdt = false; - } - else if (0 == charger_setting) - { - /* Here because OFF was requested state */ - success = true; - } - - charger_setting = 0; - - adc_enable_channel(ADC_CHARGER_CURRENT, false); - update_filtered_battery_voltage(); - charging_set_thread_priority(false); - - return success; -} - -/* Stop the charger - if USB only then the regulator will not really be - * turned off. ERROR or DISABLED will turn it off however. */ -static void stop_charger(void) -{ - charger_total_timer = 0; - - if (charge_state > DISCHARGING) - charge_state = DISCHARGING; - - adjust_charger_current(); -} - -/* Return OK if it is acceptable to start the regulator. */ -static bool charging_ok(void) -{ - bool ok = charge_state >= DISCHARGING; /* Not an error condition? */ - - if (ok) - { - /* Is the battery even connected? */ - ok = (last_inputs & POWER_INPUT_BATTERY) != 0; - } - - if (ok) - { - /* No tolerance for any over/under temp - wait for it to - * come back into safe range. */ - static const signed char temp_ranges[2][2] = - { - /* Temperature range before beginning charging */ - { BATTERY_CHARGE_MIN, - BATTERY_CHARGE_MAX }, - /* Temperature range after out-of-range detected - - charging will self-resume */ - { BATTERY_CHARGE_RESTART_MIN, - BATTERY_CHARGE_RESTART_MAX }, - }; - - int temp = battery_adc_temp(); - const signed char *range = temp_ranges[temp_state]; - - ok = temp >= range[TEMP_LOW_LIMIT] && - temp <= range[TEMP_HIGH_LIMIT]; - - switch (temp_state) - { - case TEMP_STATE_NORMAL: - if (!ok) - temp_state = TEMP_STATE_WAIT; - break; - - case TEMP_STATE_WAIT: - if (ok) - temp_state = TEMP_STATE_NORMAL; - break; - - default: - break; - } - } - - if (ok) - { - /* Any events that should stop the regulator? */ - - /* Overvoltage at CHRGRAW? */ - ok = (int_sense0 & MC13783_CHGOVS) == 0; - - if (ok) - { - /* CHGCURR sensed? */ - ok = (int_sense0 & MC13783_CHGCURRS) != 0; - - if (!ok) - { - /* Debounce transient states */ - if (chgcurr_timer > 0) - { - chgcurr_timer--; - ok = true; - } - } - else - { - chgcurr_timer = CHGCURR_TIMEOUT; - } - } - - /* Charger may need to be reinserted */ - if (!ok) - charge_state = CHARGE_STATE_ERROR; - } - - if (charger_setting != 0) - { - if (ok) - { - /* Protect against any conceivable overcharge/voltage condition - * before hardware protection must intervene. Disable charger - * until reboot. */ - ok = battery_voltage() < BATT_TOO_HIGH; - if (!ok) - charge_state = CHARGE_STATE_DISABLED; - } - - if (ok) - { - /* Watch to not overheat FET (nothing should go over about 1012.7mW). - * Trying a higher voltage AC adapter can work (up to 6.90V) but - * we'll just reject that. Reducing current for adapters that bring - * CHRGRAW to > 4.900V is another possible action. */ - ok = cccv_regulator_dissipation() < 1150; - if (!ok) - charge_state = CHARGE_STATE_ERROR; - } - - if (!ok) - { - int state = charge_state; - - if (state > DISCHARGING) - state = DISCHARGING; - - /* Force off for all states including maintaining the battery level - * on USB. */ - charge_state = CHARGE_STATE_ERROR; - stop_charger(); - charge_state = state; - } - } - - return ok; -} - -void powermgmt_init_target(void) -{ -#ifdef IMX31_ALLOW_CHARGING - const uint32_t regval_w = - MC13783_VCHRG_4_050V | MC13783_ICHRG_0MA | - MC13783_ICHRGTR_0MA | MC13783_OVCTRL_6_90V; - - /* Use watchdog to shut system down if we lose control of the charging - * hardware. */ - watchdog_init(WATCHDOG_TIMEOUT); - - mc13783_write(MC13783_CHARGER, regval_w); - - if (mc13783_read(MC13783_CHARGER) == regval_w) - { - /* Divide CHRGRAW input by 10 */ - mc13783_clear(MC13783_ADC0, MC13783_CHRGRAWDIV); - /* Turn off BATTDETB. It's worthless on MESx0V since the battery - * isn't removable (nor the thermistor). */ - mc13783_clear(MC13783_POWER_CONTROL0, MC13783_BATTDETEN); - } - else - { - /* Register has the wrong value - set error condition and disable - * since something is wrong. */ - charge_state = CHARGE_STATE_DISABLED; - stop_charger(); - } -#else - /* Disable charger use */ - charge_state = CHARGE_STATE_DISABLED; -#endif -} - -/* Returns true if the unit is charging the batteries. */ -bool charging_state(void) -{ - switch (charge_state) - { - case TRICKLE: - case TOPOFF: - case CHARGING: - return true; - default: - return false; - } -} - -/* Filtered battery charge current */ -int battery_charge_current(void) -{ - return icharger_ave / ICHARGER_AVE_SAMPLES; -} - -static void charger_plugged(void) -{ - adc_enable_channel(ADC_BATTERY_TEMP, true); - autorecharge_counter = -1; -} - -static void charger_unplugged(void) -{ - /* Charger pulled - turn off current sources (though hardware - * will have done that anyway). */ - if (charge_state > CHARGE_STATE_DISABLED) - { - /* Reset state and clear any error. If disabled, the charger - * will not have been started or will have been stopped already. */ - stop_charger(); - charge_state = DISCHARGING; - } - - /* Might need to reevaluate these bits in charger_none. */ - last_inputs &= ~(POWER_INPUT | POWER_INPUT_CHARGER); - temp_state = TEMP_STATE_NORMAL; - autorecharge_counter = 0; - chgcurr_timer = 0; - - adc_enable_channel(ADC_BATTERY_TEMP, false); -} - -static void charger_none(void) -{ - unsigned int pwr = power_thread_inputs; - - if (last_inputs != pwr) - { - last_inputs = pwr; - - if (charge_state == CHARGE_STATE_DISABLED) - return; - - if ((pwr & (POWER_INPUT | POWER_INPUT_CHARGER)) == POWER_INPUT_USB) - { - /* USB connected but not configured. Maintain battery to the - * greatest degree possible. It probably won't be enough but the - * discharge won't be so severe. */ - charger_plugged(); - charger_setting = CHARGER_ADJUST; - } - else - { - charger_unplugged(); - last_inputs = pwr; /* Restore status */ - } - } - else if (charger_setting != 0) - { - /* Maintaining - keep filter going and check charge state */ - int_sense0 = mc13783_read(MC13783_INTERRUPT_SENSE0); - - if (!charger_current_filter_step()) - { - /* Failed to read current */ - charge_state = CHARGE_STATE_ERROR; - } - - charging_ok(); - } -} - -static void charger_control(void) -{ - unsigned int pwr = power_thread_inputs; - - if (last_inputs != pwr) - { - unsigned int changed = last_inputs ^ pwr; - - last_inputs = pwr; - - if (charger_setting != 0) - charger_setting = CHARGER_ADJUST; - - if (charge_state == DISCHARGING) - { - if (main_charger_connected()) - { - /* If main is connected, ignore USB plugs. */ - if (changed & POWER_INPUT_MAIN_CHARGER) - { - /* Main charger plugged - try charge */ - autorecharge_counter = -1; - } - } - else if (pwr & POWER_INPUT_USB_CHARGER - & POWER_INPUT_CHARGER) - { - /* USB power only */ - if (changed & POWER_INPUT_USB_CHARGER) - { - /* USB charger plugged - try charge */ - autorecharge_counter = -1; - } - else if (changed & POWER_INPUT_MAIN_CHARGER) - { - /* Main charger pulled - go to battery maintenence. */ - charger_setting = CHARGER_ADJUST; - } - } - } - } - - if (charger_setting != 0 && !charger_current_filter_step()) - { - /* Failed to read current */ - charge_state = CHARGE_STATE_ERROR; - } - - int_sense0 = mc13783_read(MC13783_INTERRUPT_SENSE0); - - if (!charging_ok()) - return; - - switch (charge_state) - { - case DISCHARGING: - { - /* Battery voltage may have dropped and a charge cycle should - * start again. Debounced. */ - if (autorecharge_counter < 0 && - battery_adc_voltage() < BATT_FULL_VOLTAGE) - { - /* Try starting a cycle now if battery isn't already topped - * off to allow user to ensure the battery is full. */ - } - else if (battery_voltage() > auto_recharge_voltage()) - { - /* Still above threshold - reset counter */ - autorecharge_counter = AUTORECHARGE_COUNTDOWN; - break; - } - else if (autorecharge_counter > 0) - { - /* Coundown to restart */ - autorecharge_counter--; - break; - } - - autorecharge_counter = 0; - - charging_set_thread_priority(true); - - if (stat_battery_reading(ADC_BATTERY) < BATT_VTRICKLE_CHARGE) - { - /* Battery is deeply discharged - precharge at lower current. */ - charge_state = TRICKLE; - } - else - { - /* Ok for fast charge */ - charge_state = CHARGING; - } - - charger_setting = CHARGER_ADJUST; - charger_total_timer = CHARGER_TOTAL_TIMER*60*2; - break; - } /* DISCHARGING: */ - - case TRICKLE: /* Very low - precharge */ - { - if (battery_voltage() <= BATT_VTRICKLE_CHARGE) - break; - - /* Switch to normal charge mode. */ - charge_state = CHARGING; - charger_setting = CHARGER_ADJUST; - break; - } /* TRICKLE: */ - - case CHARGING: /* Constant-current stage */ - case TOPOFF: /* Constant-voltage stage */ - { - /* Reg. mode is more informative than an operational necessity. */ - charge_state = (int_sense0 & MC13783_CCCVS) ? TOPOFF : CHARGING; - - if (main_charger_connected()) - { - /* Monitor and stop if current drops below threshold. */ - if (battery_charge_current() > BATTERY_ICHARGE_COMPLETE) - break; - } - else - { - /* Accurate I-level can't be determined since device also - * powers through the I sense. This simply stops the reporting - * of charging but the regulator remains on. */ - if (battery_voltage() <= BATT_USB_VSTOP) - break; - } - - stop_charger(); - break; - } /* CHARGING: TOPOFF: */ - - default: - break; - } /* switch */ - - /* Check if charger timer expired and stop it if so. */ - if (charger_total_timer > 0 && --charger_total_timer == 0) - { - charge_state = CHARGE_STATE_ERROR; - stop_charger(); /* Time ran out - error */ - } -} - -/* Main charging algorithm - called from powermgmt.c */ -void charging_algorithm_step(void) -{ -#ifdef IMX31_ALLOW_CHARGING - if (service_wdt) - watchdog_service(); -#endif - - /* Switch by input state */ - switch (charger_input_state) - { - case NO_CHARGER: - charger_none(); - break; - - case CHARGER_PLUGGED: - charger_plugged(); - break; - - case CHARGER: - charger_control(); - break; - - case CHARGER_UNPLUGGED: - charger_unplugged(); - break; - } /* switch */ - - if (charger_close) - { - if (charge_state != CHARGE_STATE_DISABLED) - { - /* Disable starts while shutting down */ - charge_state = CHARGE_STATE_DISABLED; - stop_charger(); - } - - charger_close = false; - return; - } - - if (charger_setting != 0) - { - if ((mc13783_read(MC13783_CHARGER) & (MC13783_ICHRG | MC13783_VCHRG)) != - charger_setting) - { - /* The hardware setting doesn't match. It could have turned the - * charger off in a race of plugging/unplugging or the setting - * was changed in one of the calls. */ - adjust_charger_current(); - } - } -} - -/* Disable the charger and prepare for poweroff - called off-thread so we - * signal the charging thread to prepare to quit. */ -void charging_algorithm_close(void) -{ - charger_close = true; - - /* Power management thread will set it false again */ - while (charger_close) - sleep(HZ/10); -} diff --git a/firmware/target/arm/imx31/gigabeat-s/serial-imx31.h b/firmware/target/arm/imx31/gigabeat-s/serial-imx31.h deleted file mode 100644 index cbb7be2ec3..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/serial-imx31.h +++ /dev/null @@ -1,32 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2007 by James Espinoza - * - * 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. - * - ****************************************************************************/ -#ifndef SERIAL_IMX31_H -#define SERIAL_IMX31_H - -#include -#include - -int tx_rdy(void); -int rx_rdy(void); -void tx_writec(const unsigned char c); -void dprintf(const char * str, ... ); - -#endif /* SERIAL_IMX31_H */ diff --git a/firmware/target/arm/imx31/gigabeat-s/spi-imx31.c b/firmware/target/arm/imx31/gigabeat-s/spi-imx31.c deleted file mode 100644 index ac063f9b10..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/spi-imx31.c +++ /dev/null @@ -1,352 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (c) 2007 Will Robertson - * - * 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 "avic-imx31.h" -#include "ccm-imx31.h" -#include "debug.h" -#include "kernel.h" - -/* 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 -{ - struct cspi_map * const 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 = (struct cspi_map *)CSPI1_BASE_ADDR, - .cg = CG_CSPI1, - .ints = INT_CSPI1, - .handler = CSPI1_HANDLER, - }, -#endif -#if (SPI_MODULE_MASK & USE_CSPI2_MODULE) - { - .base = (struct cspi_map *)CSPI2_BASE_ADDR, - .cg = CG_CSPI2, - .ints = INT_CSPI2, - .handler = CSPI2_HANDLER, - }, -#endif -#if (SPI_MODULE_MASK & USE_CSPI3_MODULE) - { - .base = (struct cspi_map *)CSPI3_BASE_ADDR, - .cg = CG_CSPI3, - .ints = INT_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]; - struct cspi_map * 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->statreg & CSPI_STATREG_RR) != 0) - { - uint32_t word = base->rxdata; - - 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->intreg; - - if (desc->rxcount <= 0) - { - /* No more to receive - stop RX interrupts */ - intreg &= ~(CSPI_INTREG_RHEN | CSPI_INTREG_RREN); - base->intreg = intreg; - break; - } - else if (!(intreg & CSPI_INTREG_RREN)) - { - /* < 4 words expected - switch to RX ready */ - intreg &= ~CSPI_INTREG_RHEN; - base->intreg = intreg | CSPI_INTREG_RREN; - } - } - } - } - - if (trans->count > 0) - { - /* Data to transmit - fill TXFIFO or write until exhausted */ - while ((base->statreg & 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->txdata = word; - - if (--trans->count <= 0) - { - /* Out of data - stop TX interrupts */ - base->intreg &= ~CSPI_INTREG_THEN; - break; - } - } - } - - /* If all interrupts have been remasked - we're done */ - if (base->intreg == 0) - { - base->statreg = 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) -{ - struct cspi_map * const base = desc->base; - - if ((base->conreg & 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->conreg = - (node->conreg & ~(0xfcc8e000 | CSPI_CONREG_XCH | CSPI_CONREG_SMC)) - | CSPI_CONREG_EN; - /* Set the wait-states */ - base->periodreg = node->periodreg & 0xffff; - /* Clear out any spuriously-pending interrupts */ - base->statreg = CSPI_STATREG_TC | CSPI_STATREG_BO; - } - - return true; -} - -static void spi_reset(struct cspi_map * const base) -{ - /* Reset */ - base->conreg &= ~CSPI_CONREG_EN; - base->conreg |= CSPI_CONREG_EN; - base->intreg = 0; - base->statreg = CSPI_STATREG_TC | CSPI_STATREG_BO; -} - -/* Initialize each of the used SPI descriptors */ -void spi_init(void) -{ - int i; - - 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); - } -} - -/* 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 */ - struct cspi_map * const base = desc->base; - - /* Enable clock-gating register */ - ccm_module_clock_gating(desc->cg, CGM_ON_RUN_WAIT); - /* Reset */ - spi_reset(base); - desc->last = NULL; - /* Enable interrupt at controller level */ - avic_enable_int(desc->ints, INT_TYPE_IRQ, INT_PRIO_DEFAULT, - desc->handler); - } - - mutex_unlock(&desc->m); -} - -/* 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 */ - struct cspi_map * const base = desc->base; - - /* Disable interrupt at controller level */ - avic_disable_int(desc->ints); - - /* Disable interface */ - base->conreg &= ~CSPI_CONREG_EN; - - /* Disable interface clock */ - ccm_module_clock_gating(desc->cg, CGM_OFF); - } - - mutex_unlock(&desc->m); -} - -/* 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) - { - struct cspi_map * 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->intreg = intreg; - - /* Start transfer */ - base->conreg |= CSPI_CONREG_XCH; - - if (wakeup_wait(&desc->w, HZ) != OBJ_WAIT_SUCCEEDED) - { - base->intreg = 0; /* Stop SPI ints */ - spi_reset(base); /* Reset module (esp. to empty FIFOs) */ - desc->last = NULL; /* Force reconfigure */ - 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 deleted file mode 100644 index cf536b646d..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/spi-imx31.h +++ /dev/null @@ -1,89 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (c) 2007 Will Robertson - * - * 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. - * - ****************************************************************************/ -#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 -{ - __CSPI_NUM_START = -1, /* The first will be 0 */ -#if (SPI_MODULE_MASK & USE_CSPI1_MODULE) - CSPI1_NUM, -#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 cspi_map -{ - volatile uint32_t rxdata; /* 00h */ - volatile uint32_t txdata; /* 04h */ - volatile uint32_t conreg; /* 08h */ - volatile uint32_t intreg; /* 0Ch */ - volatile uint32_t dmareg; /* 10h */ - volatile uint32_t statreg; /* 14h */ - volatile uint32_t periodreg; /* 18h */ - volatile uint32_t skip1[0x69]; /* 1Ch */ - volatile uint32_t testreg; /* 1C0h */ -}; - -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); - -/* 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-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/system-gigabeat-s.c new file mode 100644 index 0000000000..cd684e77ac --- /dev/null +++ b/firmware/target/arm/imx31/gigabeat-s/system-gigabeat-s.c @@ -0,0 +1,307 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 by James Espinoza + * + * 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 "kernel.h" +#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" +#include "serial-imx31.h" +#include "debug.h" +#include "ccm-imx31.h" +#include "mc13783.h" +#include "dvfs_dptc-imx31.h" + +static unsigned long product_rev; +static unsigned long system_rev; + +/** IC revision info routines **/ +unsigned int iim_system_rev(void) +{ + return system_rev & IIM_SREV_SREV; +} + +unsigned int iim_prod_rev(void) +{ + return product_rev; +} + +static void iim_init(void) +{ + /* Initialize the IC revision info (required by SDMA) */ + ccm_module_clock_gating(CG_IIM, CGM_ON_RUN_WAIT); + product_rev = IIM_PREV; + system_rev = IIM_SREV; +} + +/** Watchdog timer routines **/ + +/* Initialize the watchdog timer */ +void watchdog_init(unsigned int half_seconds) +{ + uint16_t wcr = ((half_seconds << WDOG_WCR_WT_POS) & WDOG_WCR_WT) | + WDOG_WCR_WOE | /* WDOG output enabled */ + WDOG_WCR_WDA | /* WDOG assertion - no effect */ + WDOG_WCR_SRS | /* System reset - no effect */ + WDOG_WCR_WRE; /* Generate a WDOG signal */ + + ccm_module_clock_gating(CG_WDOG, CGM_ON_RUN_WAIT); + + WDOG_WCR = wcr; + WDOG_WSR = 0x5555; + WDOG_WCR = wcr | WDOG_WCR_WDE; /* Enable timer - hardware does + not allow a disable now */ + WDOG_WSR = 0xaaaa; +} + +/* Service the watchdog timer */ +void watchdog_service(void) +{ + WDOG_WSR = 0x5555; + WDOG_WSR = 0xaaaa; +} + +/** GPT timer routines - basis for udelay **/ + +/* Start the general-purpose timer (1MHz) */ +void gpt_start(void) +{ + ccm_module_clock_gating(CG_GPT, CGM_ON_RUN_WAIT); + unsigned int ipg_mhz = ccm_get_ipg_clk() / 1000000; + + GPTCR &= ~GPTCR_EN; /* Disable counter */ + GPTCR |= GPTCR_SWR; /* Reset module */ + while (GPTCR & GPTCR_SWR); + /* No output + * No capture + * Enable in run mode only (doesn't tick while in WFI) + * Freerun mode (count to 0xFFFFFFFF and roll-over to 0x00000000) + */ + GPTCR = GPTCR_FRR | GPTCR_CLKSRC_IPG_CLK; + GPTPR = ipg_mhz - 1; + GPTCR |= GPTCR_EN; +} + +/* Stop the general-purpose timer */ +void gpt_stop(void) +{ + GPTCR &= ~GPTCR_EN; +} + +int system_memory_guard(int newmode) +{ + (void)newmode; + return 0; +} + +void system_reboot(void) +{ + /* Multi-context so no SPI available (WDT?) */ + while (1); +} + +void system_exception_wait(void) +{ + /* Called in many contexts so button reading may be a chore */ + avic_disable_int(INT_ALL); + core_idle(); + while (1); +} + +void system_init(void) +{ + static const int disable_clocks[] = + { + /* CGR0 */ + CG_SD_MMC1, + CG_SD_MMC2, + CG_IIM, + 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_UART3, + CG_UART4, + CG_UART5, + CG_1_WIRE, + + /* CGR2 */ + CG_SSI2, + CG_CSPI1, + CG_CSPI2, + CG_GACC, + CG_RTIC, + CG_FIR + }; + + unsigned int i; + + /* MCR WFI enables wait mode (CCM_CCMR_LPM_WAIT_MODE = 0) */ + imx31_regclr32(&CCM_CCMR, CCM_CCMR_LPM); + + iim_init(); + + imx31_regset32(&SDHC1_CLOCK_CONTROL, STOP_CLK); + imx31_regset32(&SDHC2_CLOCK_CONTROL, STOP_CLK); + imx31_regset32(&RNGA_CONTROL, RNGA_CONTROL_SLEEP); + imx31_regclr32(&UCR1_1, EUARTUCR1_UARTEN); + imx31_regclr32(&UCR1_2, EUARTUCR1_UARTEN); + imx31_regclr32(&UCR1_3, EUARTUCR1_UARTEN); + imx31_regclr32(&UCR1_4, EUARTUCR1_UARTEN); + imx31_regclr32(&UCR1_5, EUARTUCR1_UARTEN); + + for (i = 0; i < ARRAYLEN(disable_clocks); i++) + ccm_module_clock_gating(disable_clocks[i], CGM_OFF); + + avic_init(); + gpt_start(); + gpio_init(); +} + +void __attribute__((naked)) imx31_regmod32(volatile uint32_t *reg_p, + uint32_t value, + uint32_t mask) +{ + asm volatile("and r1, r1, r2 \n" + "mrs ip, cpsr \n" + "cpsid if \n" + "ldr r3, [r0] \n" + "bic r3, r3, r2 \n" + "orr r3, r3, r1 \n" + "str r3, [r0] \n" + "msr cpsr_c, ip \n" + "bx lr \n"); + (void)reg_p; (void)value; (void)mask; +} + +void __attribute__((naked)) imx31_regset32(volatile uint32_t *reg_p, + uint32_t mask) +{ + asm volatile("mrs r3, cpsr \n" + "cpsid if \n" + "ldr r2, [r0] \n" + "orr r2, r2, r1 \n" + "str r2, [r0] \n" + "msr cpsr_c, r3 \n" + "bx lr \n"); + (void)reg_p; (void)mask; +} + +void __attribute__((naked)) imx31_regclr32(volatile uint32_t *reg_p, + uint32_t mask) +{ + asm volatile("mrs r3, cpsr \n" + "cpsid if \n" + "ldr r2, [r0] \n" + "bic r2, r2, r1 \n" + "str r2, [r0] \n" + "msr cpsr_c, r3 \n" + "bx lr \n"); + (void)reg_p; (void)mask; +} + +#ifdef BOOTLOADER +void system_prepare_fw_start(void) +{ + dvfs_dptc_stop(); + disable_interrupt(IRQ_FIQ_STATUS); + avic_disable_int(INT_ALL); + mc13783_close(); + tick_stop(); +} +#endif + +inline void dumpregs(void) +{ + asm volatile ("mov %0,r0\n\t" + "mov %1,r1\n\t" + "mov %2,r2\n\t" + "mov %3,r3": + "=r"(regs.r0),"=r"(regs.r1), + "=r"(regs.r2),"=r"(regs.r3):); + + asm volatile ("mov %0,r4\n\t" + "mov %1,r5\n\t" + "mov %2,r6\n\t" + "mov %3,r7": + "=r"(regs.r4),"=r"(regs.r5), + "=r"(regs.r6),"=r"(regs.r7):); + + asm volatile ("mov %0,r8\n\t" + "mov %1,r9\n\t" + "mov %2,r10\n\t" + "mov %3,r12": + "=r"(regs.r8),"=r"(regs.r9), + "=r"(regs.r10),"=r"(regs.r11):); + + asm volatile ("mov %0,r12\n\t" + "mov %1,sp\n\t" + "mov %2,lr\n\t" + "mov %3,pc\n" + "sub %3,%3,#8": + "=r"(regs.r12),"=r"(regs.sp), + "=r"(regs.lr),"=r"(regs.pc):); +#ifdef HAVE_SERIAL + dprintf("Register Dump :\n"); + dprintf("R0=0x%x\tR1=0x%x\tR2=0x%x\tR3=0x%x\n",regs.r0,regs.r1,regs.r2,regs.r3); + dprintf("R4=0x%x\tR5=0x%x\tR6=0x%x\tR7=0x%x\n",regs.r4,regs.r5,regs.r6,regs.r7); + dprintf("R8=0x%x\tR9=0x%x\tR10=0x%x\tR11=0x%x\n",regs.r8,regs.r9,regs.r10,regs.r11); + dprintf("R12=0x%x\tSP=0x%x\tLR=0x%x\tPC=0x%x\n",regs.r12,regs.sp,regs.lr,regs.pc); + //dprintf("CPSR=0x%x\t\n",regs.cpsr); +#endif + DEBUGF("Register Dump :\n"); + DEBUGF("R0=0x%x\tR1=0x%x\tR2=0x%x\tR3=0x%x\n",regs.r0,regs.r1,regs.r2,regs.r3); + DEBUGF("R4=0x%x\tR5=0x%x\tR6=0x%x\tR7=0x%x\n",regs.r4,regs.r5,regs.r6,regs.r7); + DEBUGF("R8=0x%x\tR9=0x%x\tR10=0x%x\tR11=0x%x\n",regs.r8,regs.r9,regs.r10,regs.r11); + DEBUGF("R12=0x%x\tSP=0x%x\tLR=0x%x\tPC=0x%x\n",regs.r12,regs.sp,regs.lr,regs.pc); + //DEBUGF("CPSR=0x%x\t\n",regs.cpsr); + + } + +#ifdef HAVE_ADJUSTABLE_CPU_FREQ + +void set_cpu_frequency(long frequency) +{ + (void)freqency; +} + +#endif diff --git a/firmware/target/arm/imx31/gigabeat-s/system-imx31.c b/firmware/target/arm/imx31/gigabeat-s/system-imx31.c deleted file mode 100644 index cd684e77ac..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/system-imx31.c +++ /dev/null @@ -1,307 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2007 by James Espinoza - * - * 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 "kernel.h" -#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" -#include "serial-imx31.h" -#include "debug.h" -#include "ccm-imx31.h" -#include "mc13783.h" -#include "dvfs_dptc-imx31.h" - -static unsigned long product_rev; -static unsigned long system_rev; - -/** IC revision info routines **/ -unsigned int iim_system_rev(void) -{ - return system_rev & IIM_SREV_SREV; -} - -unsigned int iim_prod_rev(void) -{ - return product_rev; -} - -static void iim_init(void) -{ - /* Initialize the IC revision info (required by SDMA) */ - ccm_module_clock_gating(CG_IIM, CGM_ON_RUN_WAIT); - product_rev = IIM_PREV; - system_rev = IIM_SREV; -} - -/** Watchdog timer routines **/ - -/* Initialize the watchdog timer */ -void watchdog_init(unsigned int half_seconds) -{ - uint16_t wcr = ((half_seconds << WDOG_WCR_WT_POS) & WDOG_WCR_WT) | - WDOG_WCR_WOE | /* WDOG output enabled */ - WDOG_WCR_WDA | /* WDOG assertion - no effect */ - WDOG_WCR_SRS | /* System reset - no effect */ - WDOG_WCR_WRE; /* Generate a WDOG signal */ - - ccm_module_clock_gating(CG_WDOG, CGM_ON_RUN_WAIT); - - WDOG_WCR = wcr; - WDOG_WSR = 0x5555; - WDOG_WCR = wcr | WDOG_WCR_WDE; /* Enable timer - hardware does - not allow a disable now */ - WDOG_WSR = 0xaaaa; -} - -/* Service the watchdog timer */ -void watchdog_service(void) -{ - WDOG_WSR = 0x5555; - WDOG_WSR = 0xaaaa; -} - -/** GPT timer routines - basis for udelay **/ - -/* Start the general-purpose timer (1MHz) */ -void gpt_start(void) -{ - ccm_module_clock_gating(CG_GPT, CGM_ON_RUN_WAIT); - unsigned int ipg_mhz = ccm_get_ipg_clk() / 1000000; - - GPTCR &= ~GPTCR_EN; /* Disable counter */ - GPTCR |= GPTCR_SWR; /* Reset module */ - while (GPTCR & GPTCR_SWR); - /* No output - * No capture - * Enable in run mode only (doesn't tick while in WFI) - * Freerun mode (count to 0xFFFFFFFF and roll-over to 0x00000000) - */ - GPTCR = GPTCR_FRR | GPTCR_CLKSRC_IPG_CLK; - GPTPR = ipg_mhz - 1; - GPTCR |= GPTCR_EN; -} - -/* Stop the general-purpose timer */ -void gpt_stop(void) -{ - GPTCR &= ~GPTCR_EN; -} - -int system_memory_guard(int newmode) -{ - (void)newmode; - return 0; -} - -void system_reboot(void) -{ - /* Multi-context so no SPI available (WDT?) */ - while (1); -} - -void system_exception_wait(void) -{ - /* Called in many contexts so button reading may be a chore */ - avic_disable_int(INT_ALL); - core_idle(); - while (1); -} - -void system_init(void) -{ - static const int disable_clocks[] = - { - /* CGR0 */ - CG_SD_MMC1, - CG_SD_MMC2, - CG_IIM, - 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_UART3, - CG_UART4, - CG_UART5, - CG_1_WIRE, - - /* CGR2 */ - CG_SSI2, - CG_CSPI1, - CG_CSPI2, - CG_GACC, - CG_RTIC, - CG_FIR - }; - - unsigned int i; - - /* MCR WFI enables wait mode (CCM_CCMR_LPM_WAIT_MODE = 0) */ - imx31_regclr32(&CCM_CCMR, CCM_CCMR_LPM); - - iim_init(); - - imx31_regset32(&SDHC1_CLOCK_CONTROL, STOP_CLK); - imx31_regset32(&SDHC2_CLOCK_CONTROL, STOP_CLK); - imx31_regset32(&RNGA_CONTROL, RNGA_CONTROL_SLEEP); - imx31_regclr32(&UCR1_1, EUARTUCR1_UARTEN); - imx31_regclr32(&UCR1_2, EUARTUCR1_UARTEN); - imx31_regclr32(&UCR1_3, EUARTUCR1_UARTEN); - imx31_regclr32(&UCR1_4, EUARTUCR1_UARTEN); - imx31_regclr32(&UCR1_5, EUARTUCR1_UARTEN); - - for (i = 0; i < ARRAYLEN(disable_clocks); i++) - ccm_module_clock_gating(disable_clocks[i], CGM_OFF); - - avic_init(); - gpt_start(); - gpio_init(); -} - -void __attribute__((naked)) imx31_regmod32(volatile uint32_t *reg_p, - uint32_t value, - uint32_t mask) -{ - asm volatile("and r1, r1, r2 \n" - "mrs ip, cpsr \n" - "cpsid if \n" - "ldr r3, [r0] \n" - "bic r3, r3, r2 \n" - "orr r3, r3, r1 \n" - "str r3, [r0] \n" - "msr cpsr_c, ip \n" - "bx lr \n"); - (void)reg_p; (void)value; (void)mask; -} - -void __attribute__((naked)) imx31_regset32(volatile uint32_t *reg_p, - uint32_t mask) -{ - asm volatile("mrs r3, cpsr \n" - "cpsid if \n" - "ldr r2, [r0] \n" - "orr r2, r2, r1 \n" - "str r2, [r0] \n" - "msr cpsr_c, r3 \n" - "bx lr \n"); - (void)reg_p; (void)mask; -} - -void __attribute__((naked)) imx31_regclr32(volatile uint32_t *reg_p, - uint32_t mask) -{ - asm volatile("mrs r3, cpsr \n" - "cpsid if \n" - "ldr r2, [r0] \n" - "bic r2, r2, r1 \n" - "str r2, [r0] \n" - "msr cpsr_c, r3 \n" - "bx lr \n"); - (void)reg_p; (void)mask; -} - -#ifdef BOOTLOADER -void system_prepare_fw_start(void) -{ - dvfs_dptc_stop(); - disable_interrupt(IRQ_FIQ_STATUS); - avic_disable_int(INT_ALL); - mc13783_close(); - tick_stop(); -} -#endif - -inline void dumpregs(void) -{ - asm volatile ("mov %0,r0\n\t" - "mov %1,r1\n\t" - "mov %2,r2\n\t" - "mov %3,r3": - "=r"(regs.r0),"=r"(regs.r1), - "=r"(regs.r2),"=r"(regs.r3):); - - asm volatile ("mov %0,r4\n\t" - "mov %1,r5\n\t" - "mov %2,r6\n\t" - "mov %3,r7": - "=r"(regs.r4),"=r"(regs.r5), - "=r"(regs.r6),"=r"(regs.r7):); - - asm volatile ("mov %0,r8\n\t" - "mov %1,r9\n\t" - "mov %2,r10\n\t" - "mov %3,r12": - "=r"(regs.r8),"=r"(regs.r9), - "=r"(regs.r10),"=r"(regs.r11):); - - asm volatile ("mov %0,r12\n\t" - "mov %1,sp\n\t" - "mov %2,lr\n\t" - "mov %3,pc\n" - "sub %3,%3,#8": - "=r"(regs.r12),"=r"(regs.sp), - "=r"(regs.lr),"=r"(regs.pc):); -#ifdef HAVE_SERIAL - dprintf("Register Dump :\n"); - dprintf("R0=0x%x\tR1=0x%x\tR2=0x%x\tR3=0x%x\n",regs.r0,regs.r1,regs.r2,regs.r3); - dprintf("R4=0x%x\tR5=0x%x\tR6=0x%x\tR7=0x%x\n",regs.r4,regs.r5,regs.r6,regs.r7); - dprintf("R8=0x%x\tR9=0x%x\tR10=0x%x\tR11=0x%x\n",regs.r8,regs.r9,regs.r10,regs.r11); - dprintf("R12=0x%x\tSP=0x%x\tLR=0x%x\tPC=0x%x\n",regs.r12,regs.sp,regs.lr,regs.pc); - //dprintf("CPSR=0x%x\t\n",regs.cpsr); -#endif - DEBUGF("Register Dump :\n"); - DEBUGF("R0=0x%x\tR1=0x%x\tR2=0x%x\tR3=0x%x\n",regs.r0,regs.r1,regs.r2,regs.r3); - DEBUGF("R4=0x%x\tR5=0x%x\tR6=0x%x\tR7=0x%x\n",regs.r4,regs.r5,regs.r6,regs.r7); - DEBUGF("R8=0x%x\tR9=0x%x\tR10=0x%x\tR11=0x%x\n",regs.r8,regs.r9,regs.r10,regs.r11); - DEBUGF("R12=0x%x\tSP=0x%x\tLR=0x%x\tPC=0x%x\n",regs.r12,regs.sp,regs.lr,regs.pc); - //DEBUGF("CPSR=0x%x\t\n",regs.cpsr); - - } - -#ifdef HAVE_ADJUSTABLE_CPU_FREQ - -void set_cpu_frequency(long frequency) -{ - (void)freqency; -} - -#endif diff --git a/firmware/target/arm/imx31/gigabeat-s/timer-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/timer-gigabeat-s.c new file mode 100644 index 0000000000..f3f78a1ee1 --- /dev/null +++ b/firmware/target/arm/imx31/gigabeat-s/timer-gigabeat-s.c @@ -0,0 +1,113 @@ +/*************************************************************************** +* __________ __ ___. +* Open \______ \ ____ ____ | | _\_ |__ _______ ___ +* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +* \/ \/ \/ \/ \/ +* $Id$ +* +* Copyright (C) 2009 by Michael Sevakis +* +* 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 "timer.h" +#include "ccm-imx31.h" +#include "avic-imx31.h" + +static void __attribute__((interrupt("IRQ"))) EPIT2_HANDLER(void) +{ + EPITSR2 = EPITSR_OCIF; /* Clear the pending status */ + + if (pfn_timer != NULL) + pfn_timer(); +} + +static void stop_timer(bool clock_off) +{ + /* Ensure clock gating on (before touching any module registers) */ + ccm_module_clock_gating(CG_EPIT2, CGM_ON_RUN_WAIT); + /* Disable insterrupt */ + avic_disable_int(INT_EPIT2); + /* Clear wakeup mask */ + CCM_WIMR0 &= ~CCM_WIMR0_IPI_INT_EPIT2; + /* Disable counter */ + EPITCR2 &= ~(EPITCR_OCIEN | EPITCR_EN); + /* Clear pending */ + EPITSR2 = EPITSR_OCIF; + + if (clock_off) + { + /* Final stop, not reset; don't clock module any longer */ + ccm_module_clock_gating(CG_EPIT2, CGM_OFF); + } +} + +bool timer_set(long cycles, bool start) +{ + /* Maximum cycle count expressible in the cycles parameter is 2^31-1 + * and the modulus counter is capable of 2^32-1 and as a result there is + * no requirement to use a prescaler > 1. This gives a frequency range of + * ~0.015366822Hz - 66000000Hz. The highest input frequency gives the + * greatest possible accuracy anyway. */ + int oldstatus = disable_interrupt_save(IRQ_FIQ_STATUS); + + /* Halt timer if running - leave module clock enabled */ + stop_timer(false); + + if (start && pfn_unregister != NULL) + { + pfn_unregister(); + pfn_unregister = NULL; + } + + /* CLKSRC = ipg_clk, + * EPIT output disconnected, + * Enabled in wait mode + * Prescale 1 for 66MHz + * Reload from modulus register, + * Count from load value */ + EPITCR2 = EPITCR_CLKSRC_IPG_CLK | EPITCR_WAITEN | EPITCR_IOVW | + ((1-1) << EPITCR_PRESCALER_POS) | EPITCR_RLD | EPITCR_ENMOD; + EPITLR2 = cycles; + /* Event when counter reaches 0 */ + EPITCMPR2 = 0; + + restore_interrupt(oldstatus); + return true; +} + +bool timer_start(void) +{ + int oldstatus = disable_interrupt_save(IRQ_FIQ_STATUS); + + /* Halt timer if running - leave module clock enabled */ + stop_timer(false); + + /* Enable interrupt */ + EPITCR2 |= EPITCR_OCIEN; + avic_enable_int(INT_EPIT2, INT_TYPE_IRQ, INT_PRIO_DEFAULT, + EPIT2_HANDLER); + /* Start timer */ + EPITCR2 |= EPITCR_EN; + + restore_interrupt(oldstatus); + return true; +} + +void timer_stop(void) +{ + int oldstatus = disable_interrupt_save(IRQ_FIQ_STATUS); + /* Halt timer if running - stop module clock */ + stop_timer(true); + restore_interrupt(oldstatus); +} diff --git a/firmware/target/arm/imx31/gigabeat-s/timer-imx31.c b/firmware/target/arm/imx31/gigabeat-s/timer-imx31.c deleted file mode 100644 index f3f78a1ee1..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/timer-imx31.c +++ /dev/null @@ -1,113 +0,0 @@ -/*************************************************************************** -* __________ __ ___. -* Open \______ \ ____ ____ | | _\_ |__ _______ ___ -* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / -* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < -* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ -* \/ \/ \/ \/ \/ -* $Id$ -* -* Copyright (C) 2009 by Michael Sevakis -* -* 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 "timer.h" -#include "ccm-imx31.h" -#include "avic-imx31.h" - -static void __attribute__((interrupt("IRQ"))) EPIT2_HANDLER(void) -{ - EPITSR2 = EPITSR_OCIF; /* Clear the pending status */ - - if (pfn_timer != NULL) - pfn_timer(); -} - -static void stop_timer(bool clock_off) -{ - /* Ensure clock gating on (before touching any module registers) */ - ccm_module_clock_gating(CG_EPIT2, CGM_ON_RUN_WAIT); - /* Disable insterrupt */ - avic_disable_int(INT_EPIT2); - /* Clear wakeup mask */ - CCM_WIMR0 &= ~CCM_WIMR0_IPI_INT_EPIT2; - /* Disable counter */ - EPITCR2 &= ~(EPITCR_OCIEN | EPITCR_EN); - /* Clear pending */ - EPITSR2 = EPITSR_OCIF; - - if (clock_off) - { - /* Final stop, not reset; don't clock module any longer */ - ccm_module_clock_gating(CG_EPIT2, CGM_OFF); - } -} - -bool timer_set(long cycles, bool start) -{ - /* Maximum cycle count expressible in the cycles parameter is 2^31-1 - * and the modulus counter is capable of 2^32-1 and as a result there is - * no requirement to use a prescaler > 1. This gives a frequency range of - * ~0.015366822Hz - 66000000Hz. The highest input frequency gives the - * greatest possible accuracy anyway. */ - int oldstatus = disable_interrupt_save(IRQ_FIQ_STATUS); - - /* Halt timer if running - leave module clock enabled */ - stop_timer(false); - - if (start && pfn_unregister != NULL) - { - pfn_unregister(); - pfn_unregister = NULL; - } - - /* CLKSRC = ipg_clk, - * EPIT output disconnected, - * Enabled in wait mode - * Prescale 1 for 66MHz - * Reload from modulus register, - * Count from load value */ - EPITCR2 = EPITCR_CLKSRC_IPG_CLK | EPITCR_WAITEN | EPITCR_IOVW | - ((1-1) << EPITCR_PRESCALER_POS) | EPITCR_RLD | EPITCR_ENMOD; - EPITLR2 = cycles; - /* Event when counter reaches 0 */ - EPITCMPR2 = 0; - - restore_interrupt(oldstatus); - return true; -} - -bool timer_start(void) -{ - int oldstatus = disable_interrupt_save(IRQ_FIQ_STATUS); - - /* Halt timer if running - leave module clock enabled */ - stop_timer(false); - - /* Enable interrupt */ - EPITCR2 |= EPITCR_OCIEN; - avic_enable_int(INT_EPIT2, INT_TYPE_IRQ, INT_PRIO_DEFAULT, - EPIT2_HANDLER); - /* Start timer */ - EPITCR2 |= EPITCR_EN; - - restore_interrupt(oldstatus); - return true; -} - -void timer_stop(void) -{ - int oldstatus = disable_interrupt_save(IRQ_FIQ_STATUS); - /* Halt timer if running - stop module clock */ - stop_timer(true); - restore_interrupt(oldstatus); -} diff --git a/firmware/target/arm/imx31/gigabeat-s/usb-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/usb-gigabeat-s.c new file mode 100644 index 0000000000..d873c19ed3 --- /dev/null +++ b/firmware/target/arm/imx31/gigabeat-s/usb-gigabeat-s.c @@ -0,0 +1,135 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006 by Linus Nielsen Feltzing + * + * 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 "cpu.h" +#include "system.h" +#include "kernel.h" +#include "ata.h" +#include "usb.h" +#include "usb_core.h" +#include "usb_drv.h" +#include "usb-target.h" +#include "mc13783.h" +#include "ccm-imx31.h" +#include "avic-imx31.h" +#include "power-gigabeat-s.h" + +static int usb_status = USB_EXTRACTED; + +static void enable_transceiver(bool enable) +{ + if (enable) + { + if (GPIO1_DR & (1 << 30)) + { + imx31_regclr32(&GPIO3_DR, (1 << 16)); /* Reset ISP1504 */ + sleep(HZ/100); + imx31_regset32(&GPIO3_DR, (1 << 16)); + sleep(HZ/10); + imx31_regclr32(&GPIO1_DR, (1 << 30)); /* Select ISP1504 */ + } + } + else + { + imx31_regset32(&GPIO1_DR, (1 << 30)); /* Deselect ISP1504 */ + } +} + +/* Read the immediate state of the cable from the PMIC */ +bool usb_plugged(void) +{ + return mc13783_read(MC13783_INTERRUPT_SENSE0) & MC13783_USB4V4S; +} + +void usb_connect_event(void) +{ + int status = usb_plugged() ? USB_INSERTED : USB_EXTRACTED; + usb_status = status; + /* Notify power that USB charging is potentially available */ + charger_usb_detect_event(status); + usb_status_event((status == USB_INSERTED) ? USB_POWERED : USB_UNPOWERED); +} + +int usb_detect(void) +{ + return usb_status; +} + +void usb_init_device(void) +{ + /* Do one-time inits */ + usb_drv_startup(); + + /* Initially poll */ + usb_connect_event(); + + /* Enable PMIC event */ + mc13783_enable_event(MC13783_USB_EVENT); +} + +void usb_enable(bool on) +{ + /* Module clock should be on since since this could be called with + * OFF initially and writing module registers would hardlock otherwise. */ + ccm_module_clock_gating(CG_USBOTG, CGM_ON_RUN_WAIT); + enable_transceiver(true); + + if (on) + { + usb_core_init(); + } + else + { + usb_core_exit(); + enable_transceiver(false); + ccm_module_clock_gating(CG_USBOTG, CGM_OFF); + } +} + +void usb_attach(void) +{ + usb_drv_attach(); +} + +static void __attribute__((interrupt("IRQ"))) USB_OTG_HANDLER(void) +{ + usb_drv_int(); /* Call driver handler */ +} + +void usb_drv_int_enable(bool enable) +{ + if (enable) + { + avic_enable_int(INT_USB_OTG, INT_TYPE_IRQ, INT_PRIO_DEFAULT, + USB_OTG_HANDLER); + } + else + { + avic_disable_int(INT_USB_OTG); + } +} + +/* Called during the bus reset interrupt when in detect mode */ +void usb_drv_usb_detect_event(void) +{ + if (usb_drv_powered()) + usb_status_event(USB_INSERTED); +} diff --git a/firmware/target/arm/imx31/gigabeat-s/usb-imx31.c b/firmware/target/arm/imx31/gigabeat-s/usb-imx31.c deleted file mode 100644 index 64ff04e7ae..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/usb-imx31.c +++ /dev/null @@ -1,135 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2006 by Linus Nielsen Feltzing - * - * 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 "cpu.h" -#include "system.h" -#include "kernel.h" -#include "ata.h" -#include "usb.h" -#include "usb_core.h" -#include "usb_drv.h" -#include "usb-target.h" -#include "ccm-imx31.h" -#include "power-imx31.h" -#include "avic-imx31.h" -#include "mc13783.h" - -static int usb_status = USB_EXTRACTED; - -static void enable_transceiver(bool enable) -{ - if (enable) - { - if (GPIO1_DR & (1 << 30)) - { - imx31_regclr32(&GPIO3_DR, (1 << 16)); /* Reset ISP1504 */ - sleep(HZ/100); - imx31_regset32(&GPIO3_DR, (1 << 16)); - sleep(HZ/10); - imx31_regclr32(&GPIO1_DR, (1 << 30)); /* Select ISP1504 */ - } - } - else - { - imx31_regset32(&GPIO1_DR, (1 << 30)); /* Deselect ISP1504 */ - } -} - -/* Read the immediate state of the cable from the PMIC */ -bool usb_plugged(void) -{ - return mc13783_read(MC13783_INTERRUPT_SENSE0) & MC13783_USB4V4S; -} - -void usb_connect_event(void) -{ - int status = usb_plugged() ? USB_INSERTED : USB_EXTRACTED; - usb_status = status; - /* Notify power that USB charging is potentially available */ - charger_usb_detect_event(status); - usb_status_event((status == USB_INSERTED) ? USB_POWERED : USB_UNPOWERED); -} - -int usb_detect(void) -{ - return usb_status; -} - -void usb_init_device(void) -{ - /* Do one-time inits */ - usb_drv_startup(); - - /* Initially poll */ - usb_connect_event(); - - /* Enable PMIC event */ - mc13783_enable_event(MC13783_USB_EVENT); -} - -void usb_enable(bool on) -{ - /* Module clock should be on since since this could be called with - * OFF initially and writing module registers would hardlock otherwise. */ - ccm_module_clock_gating(CG_USBOTG, CGM_ON_RUN_WAIT); - enable_transceiver(true); - - if (on) - { - usb_core_init(); - } - else - { - usb_core_exit(); - enable_transceiver(false); - ccm_module_clock_gating(CG_USBOTG, CGM_OFF); - } -} - -void usb_attach(void) -{ - usb_drv_attach(); -} - -static void __attribute__((interrupt("IRQ"))) USB_OTG_HANDLER(void) -{ - usb_drv_int(); /* Call driver handler */ -} - -void usb_drv_int_enable(bool enable) -{ - if (enable) - { - avic_enable_int(INT_USB_OTG, INT_TYPE_IRQ, INT_PRIO_DEFAULT, - USB_OTG_HANDLER); - } - else - { - avic_disable_int(INT_USB_OTG); - } -} - -/* Called during the bus reset interrupt when in detect mode */ -void usb_drv_usb_detect_event(void) -{ - if (usb_drv_powered()) - usb_status_event(USB_INSERTED); -} diff --git a/firmware/target/arm/imx31/gigabeat-s/wmcodec-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/wmcodec-gigabeat-s.c new file mode 100644 index 0000000000..96324cc162 --- /dev/null +++ b/firmware/target/arm/imx31/gigabeat-s/wmcodec-gigabeat-s.c @@ -0,0 +1,67 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Gigabeat S specific code for the WM8978 codec + * + * Copyright (C) 2008 Michael Sevakis + * + * 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 "kernel.h" +#include "sound.h" +#include "wmcodec.h" +#include "i2s.h" +#include "i2c-imx31.h" + +/* NOTE: Some port-specific bits will have to be moved away (node and GPIO + * writes) for cleanest implementation. */ + +static struct i2c_node wm8978_i2c_node = +{ + .num = I2C1_NUM, + .ifdr = I2C_IFDR_DIV192, /* 66MHz/.4MHz = 165, closest = 192 = 343750Hz */ + /* Just hard-code for now - scaling may require + * updating */ + .addr = WMC_I2C_ADDR, +}; + +void audiohw_init(void) +{ + i2s_reset(); + + i2c_enable_node(&wm8978_i2c_node, true); + + audiohw_preinit(); + + imx31_regset32(&GPIO3_DR, (1 << 21)); /* Turn on analogue LDO */ +} + +void audiohw_enable_headphone_jack(bool enable) +{ + /* Turn headphone jack output on or off. */ + imx31_regmod32(&GPIO3_DR, enable ? (1 << 22) : 0, (1 << 22)); +} + +void wmcodec_write(int reg, int data) +{ + unsigned char d[2]; + /* |aaaaaaad|dddddddd| */ + d[0] = (reg << 1) | ((data & 0x100) >> 8); + d[1] = data; + i2c_write(&wm8978_i2c_node, d, 2); +} diff --git a/firmware/target/arm/imx31/gigabeat-s/wmcodec-imx31.c b/firmware/target/arm/imx31/gigabeat-s/wmcodec-imx31.c deleted file mode 100644 index 06bb4d6306..0000000000 --- a/firmware/target/arm/imx31/gigabeat-s/wmcodec-imx31.c +++ /dev/null @@ -1,83 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Gigabeat S specific code for the WM8978 codec - * - * Copyright (C) 2008 Michael Sevakis - * - * 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 "kernel.h" -#include "sound.h" -#include "wmcodec.h" -#include "i2c-imx31.h" - -/* NOTE: Some port-specific bits will have to be moved away (node and GPIO - * writes) for cleanest implementation. */ - -static struct i2c_node wm8978_i2c_node = -{ - .num = I2C1_NUM, - .ifdr = I2C_IFDR_DIV192, /* 66MHz/.4MHz = 165, closest = 192 = 343750Hz */ - /* Just hard-code for now - scaling may require - * updating */ - .addr = WMC_I2C_ADDR, -}; - -void audiohw_init(void) -{ - /* How SYSCLK for codec is derived (USBPLL=338.688MHz). - * - * SSI post dividers (SSI2 PODF=4, SSI2 PRE PODF=0): - * 338688000Hz / 5 = 67737600Hz = ssi1_clk - * - * SSI bit clock dividers (DIV2=1, PSR=0, PM=0): - * ssi1_clk / 4 = 16934400Hz = INT_BIT_CLK (MCLK) - * - * WM Codec post divider (MCLKDIV=1.5): - * INT_BIT_CLK (MCLK) / 1.5 = 11289600Hz = 256*fs = SYSCLK - */ - imx31_regmod32(&CCM_PDR1, - ((1-1) << CCM_PDR1_SSI1_PRE_PODF_POS) | - ((5-1) << CCM_PDR1_SSI1_PODF_POS) | - ((8-1) << CCM_PDR1_SSI2_PRE_PODF_POS) | - ((64-1) << CCM_PDR1_SSI2_PODF_POS), - CCM_PDR1_SSI1_PODF | CCM_PDR1_SSI2_PODF | - CCM_PDR1_SSI1_PRE_PODF | CCM_PDR1_SSI2_PRE_PODF); - - i2c_enable_node(&wm8978_i2c_node, true); - - audiohw_preinit(); - - imx31_regset32(&GPIO3_DR, (1 << 21)); /* Turn on analogue LDO */ -} - -void audiohw_enable_headphone_jack(bool enable) -{ - /* Turn headphone jack output on or off. */ - imx31_regmod32(&GPIO3_DR, enable ? (1 << 22) : 0, (1 << 22)); -} - -void wmcodec_write(int reg, int data) -{ - unsigned char d[2]; - /* |aaaaaaad|dddddddd| */ - d[0] = (reg << 1) | ((data & 0x100) >> 8); - d[1] = data; - i2c_write(&wm8978_i2c_node, d, 2); -} diff --git a/firmware/target/arm/imx31/gpio-imx31.c b/firmware/target/arm/imx31/gpio-imx31.c new file mode 100644 index 0000000000..944f70eae3 --- /dev/null +++ b/firmware/target/arm/imx31/gpio-imx31.c @@ -0,0 +1,213 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (c) 2008 by Michael Sevakis + * + * IMX31 GPIO event manager + * + * 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" + +/* 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 +{ + struct gpio_map * const 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 = (struct gpio_map *)GPIO1_BASE_ADDR, + .ints = INT_GPIO1, + .handler = GPIO1_HANDLER, + }, +#endif +#if (GPIO_EVENT_MASK & USE_GPIO2_EVENTS) + { + .base = (struct gpio_map *)GPIO2_BASE_ADDR, + .ints = INT_GPIO2, + .handler = GPIO2_HANDLER, + }, +#endif +#if (GPIO_EVENT_MASK & USE_GPIO3_EVENTS) + { + .base = (struct gpio_map *)GPIO3_BASE_ADDR, + .ints = INT_GPIO3, + .handler = GPIO3_HANDLER, + }, +#endif +}; + +static void gpio_call_events(const struct gpio_module_descriptor * const desc) +{ + const struct gpio_event_list * const list = desc->list; + struct gpio_map * const base = desc->base; + const struct gpio_event * event, *event_last; + + /* Intersect pending and unmasked bits */ + uint32_t pnd = base->isr & base->imr; + + event = list->events; + event_last = event + list->count; + + /* Call each event handler in order */ + /* .count is surely expected to be > 0 */ + do + { + uint32_t mask = event->mask; + + 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(); + } +} + +#if (GPIO_EVENT_MASK & USE_GPIO1_EVENTS) +static __attribute__((interrupt("IRQ"))) void GPIO1_HANDLER(void) +{ + gpio_call_events(&gpio_descs[GPIO1_NUM]); +} +#endif + +#if (GPIO_EVENT_MASK & USE_GPIO2_EVENTS) +static __attribute__((interrupt("IRQ"))) void GPIO2_HANDLER(void) +{ + gpio_call_events(&gpio_descs[GPIO2_NUM]); +} +#endif + +#if (GPIO_EVENT_MASK & USE_GPIO3_EVENTS) +static __attribute__((interrupt("IRQ"))) void GPIO3_HANDLER(void) +{ + gpio_call_events(&gpio_descs[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_event_ids id) +{ + const struct gpio_module_descriptor * const desc = &gpio_descs[id >> 5]; + const struct gpio_event * const event = &desc->list->events[id & 31]; + struct gpio_map * const base = desc->base; + volatile uint32_t *icr; + uint32_t mask, line; + uint32_t imr; + int shift; + + int oldlevel = disable_irq_save(); + + imr = base->imr; + + if (imr == 0) + { + /* First enabled interrupt for this GPIO */ + avic_enable_int(desc->ints, INT_TYPE_IRQ, desc->list->ints_priority, + desc->handler); + } + + /* Set the line sense */ + line = find_first_set_bit(event->mask); + icr = &base->icr[line >> 4]; + shift = (line & 15) << 1; + mask = GPIO_SENSE_CONFIG_MASK << shift; + + *icr = (*icr & ~mask) | ((event->sense << shift) & mask); + + /* Unmask the line */ + base->imr = imr | event->mask; + + restore_irq(oldlevel); + + return true; +} + +void gpio_disable_event(enum gpio_event_ids id) +{ + const struct gpio_module_descriptor * const desc = &gpio_descs[id >> 5]; + const struct gpio_event * const event = &desc->list->events[id & 31]; + struct gpio_map * const base = desc->base; + uint32_t imr; + + int oldlevel = disable_irq_save(); + + /* Remove bit from mask */ + imr = base->imr & ~event->mask; + + /* Mask the line */ + base->imr = imr; + + if (imr == 0) + { + /* No events remain enabled */ + avic_disable_int(desc->ints); + } + + restore_irq(oldlevel); +} diff --git a/firmware/target/arm/imx31/gpio-imx31.h b/firmware/target/arm/imx31/gpio-imx31.h new file mode 100644 index 0000000000..72956d4efa --- /dev/null +++ b/firmware/target/arm/imx31/gpio-imx31.h @@ -0,0 +1,114 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (c) 2008 by Michael Sevakis + * + * 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. + * + ****************************************************************************/ +#ifndef GPIO_IMX31_H +#define GPIO_IMX31_H + +/* Static registration mechanism for imx31 GPIO interrupts */ +#define USE_GPIO1_EVENTS (1 << 0) +#define USE_GPIO2_EVENTS (1 << 1) +#define USE_GPIO3_EVENTS (1 << 2) + +/* Module indexes defined by which GPIO modules are used */ +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, +}; + +/* 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 +}; + +#include "gpio-target.h" + +/* 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 + +/* Register map for each module */ +struct gpio_map +{ + volatile uint32_t dr; /* 00h */ + volatile uint32_t gdir; /* 04h */ + volatile uint32_t psr; /* 08h */ + union + { + struct + { + volatile uint32_t icr1; /* 0Ch */ + volatile uint32_t icr2; /* 10h */ + }; + volatile uint32_t icr[2]; /* 0Ch */ + }; + volatile uint32_t imr; /* 14h */ + volatile uint32_t isr; /* 18h */ +}; + +/* Pending events will be called in array order which allows easy + * pioritization */ + +/* Describes a single event for a pin */ +struct gpio_event +{ + uint32_t mask; /* mask: 1 << (0...31) */ + enum gpio_int_sense_enum sense; /* Type of sense */ + void (*callback)(void); /* Callback function */ +}; + +/* Describes the events attached to a port */ +struct gpio_event_list +{ + int ints_priority; /* Interrupt priority for this GPIO */ + unsigned count; /* Count of events for the module */ + const struct gpio_event *events; /* List of events */ +}; + +void gpio_init(void); +bool gpio_enable_event(enum gpio_event_ids id); +void gpio_disable_event(enum gpio_event_ids id); + +#endif /* GPIO_IMX31_H */ diff --git a/firmware/target/arm/imx31/i2c-imx31.c b/firmware/target/arm/imx31/i2c-imx31.c new file mode 100644 index 0000000000..1ffdce38ea --- /dev/null +++ b/firmware/target/arm/imx31/i2c-imx31.c @@ -0,0 +1,338 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2008 by Michael Sevakis + * + * 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 +#include "config.h" +#include "system.h" +#include "kernel.h" +#include "avic-imx31.h" +#include "ccm-imx31.h" +#include "i2c-imx31.h" + +/* Forward interrupt handler declarations */ +#if (I2C_MODULE_MASK & USE_I2C1_MODULE) +static __attribute__((interrupt("IRQ"))) void I2C1_HANDLER(void); +#endif +#if (I2C_MODULE_MASK & USE_I2C2_MODULE) +static __attribute__((interrupt("IRQ"))) void I2C2_HANDLER(void); +#endif +#if (I2C_MODULE_MASK & USE_I2C3_MODULE) +static __attribute__((interrupt("IRQ"))) void I2C3_HANDLER(void); +#endif + +static struct i2c_module_descriptor +{ + struct i2c_map *base; /* Module base address */ + enum IMX31_CG_LIST cg; /* Clock gating index */ + enum IMX31_INT_LIST ints; /* Module interrupt number */ + int enable; /* Enable count */ + void (*handler)(void); /* Module interrupt handler */ + struct mutex m; /* Node mutual-exclusion */ + struct wakeup w; /* I2C done signal */ + unsigned char *addr_data; /* Additional addressing data */ + int addr_count; /* Addressing byte count */ + unsigned char *data; /* TX/RX buffer (actual data) */ + int data_count; /* TX/RX byte count */ + unsigned char addr; /* Address + r/w bit */ +} i2c_descs[I2C_NUM_I2C] = +{ +#if (I2C_MODULE_MASK & USE_I2C1_MODULE) + { + .base = (struct i2c_map *)I2C1_BASE_ADDR, + .cg = CG_I2C1, + .ints = INT_I2C1, + .handler = I2C1_HANDLER, + }, +#endif +#if (I2C_MODULE_MASK & USE_I2C2_MODULE) + { + .base = (struct i2c_map *)I2C2_BASE_ADDR, + .cg = CG_I2C2, + .ints = INT_I2C2, + .handler = I2C2_HANDLER, + }, +#endif +#if (I2C_MODULE_MASK & USE_I2C3_MODULE) + { + .base = (struct i2c_map *)I2C3_BASE_ADDR, + .cg = CG_I2C3, + .ints = INT_I2C3, + .handler = I2C3_HANDLER, + }, +#endif +}; + +static void i2c_interrupt(enum i2c_module_number i2c) +{ + struct i2c_module_descriptor *const desc = &i2c_descs[i2c]; + struct i2c_map * const base = desc->base; + uint16_t i2sr = base->i2sr; + + base->i2sr = 0; /* Clear IIF */ + + if (desc->addr_count >= 0) + { + /* ADDR cycle - either done or more to send */ + if ((i2sr & I2C_I2SR_RXAK) != 0) + { + goto i2c_stop; /* problem */ + } + + if (--desc->addr_count < 0) + { + /* Switching to data cycle */ + if (desc->addr & 0x1) + { + base->i2cr &= ~I2C_I2CR_MTX; /* Switch to RX mode */ + base->i2dr; /* Dummy read */ + return; + } + /* else remaining data is TX - handle below */ + goto i2c_transmit; + } + else + { + base->i2dr = *desc->addr_data++; /* Send next addressing byte */ + return; + } + } + + if (base->i2cr & I2C_I2CR_MTX) + { + /* Transmitting data */ + if ((i2sr & I2C_I2SR_RXAK) == 0) + { +i2c_transmit: + if (desc->data_count > 0) + { + /* More bytes to send, got ACK from previous byte */ + base->i2dr = *desc->data++; + desc->data_count--; + return; + } + } + /* else done or no ACK received */ + } + else + { + /* Receiving data */ + if (--desc->data_count > 0) + { + if (desc->data_count == 1) + { + /* 2nd to Last byte - NACK */ + base->i2cr |= I2C_I2CR_TXAK; + } + + *desc->data++ = base->i2dr; /* Read data from I2DR and store */ + return; + } + else + { + /* Generate STOP signal before reading data */ + base->i2cr &= ~(I2C_I2CR_MSTA | I2C_I2CR_IIEN); + *desc->data++ = base->i2dr; /* Read data from I2DR and store */ + goto i2c_done; + } + } + +i2c_stop: + /* Generate STOP signal */ + base->i2cr &= ~(I2C_I2CR_MSTA | I2C_I2CR_IIEN); +i2c_done: + /* Signal thread we're done */ + wakeup_signal(&desc->w); +} + +#if (I2C_MODULE_MASK & USE_I2C1_MODULE) +static __attribute__((interrupt("IRQ"))) void I2C1_HANDLER(void) +{ + i2c_interrupt(I2C1_NUM); +} +#endif +#if (I2C_MODULE_MASK & USE_I2C2_MODULE) +static __attribute__((interrupt("IRQ"))) void I2C2_HANDLER(void) +{ + i2c_interrupt(I2C2_NUM); +} +#endif +#if (I2C_MODULE_MASK & USE_I2C3_MODULE) +static __attribute__((interrupt("IRQ"))) void I2C3_HANDLER(void) +{ + i2c_interrupt(I2C3_NUM); +} +#endif + +static int i2c_transfer(struct i2c_node * const node, + struct i2c_module_descriptor *const desc) +{ + struct i2c_map * const base = desc->base; + int count = desc->data_count; + uint16_t i2cr; + + /* Make sure bus is idle. */ + while (base->i2sr & I2C_I2SR_IBB); + + /* Set speed */ + base->ifdr = node->ifdr; + + /* Enable module */ + base->i2cr = I2C_I2CR_IEN; + + /* Enable Interrupt, Master */ + i2cr = I2C_I2CR_IEN | I2C_I2CR_IIEN | I2C_I2CR_MTX; + + if ((desc->addr & 0x1) && desc->data_count < 2) + { + /* Receiving less than two bytes - disable ACK generation */ + i2cr |= I2C_I2CR_TXAK; + } + + /* Set config */ + base->i2cr = i2cr; + + /* Generate START */ + base->i2cr = i2cr | I2C_I2CR_MSTA; + + /* Address slave (first byte sent) and begin session. */ + base->i2dr = desc->addr; + + /* Wait for transfer to complete */ + if (wakeup_wait(&desc->w, HZ) == OBJ_WAIT_SUCCEEDED) + { + count -= desc->data_count; + } + else + { + /* Generate STOP if timeout */ + base->i2cr &= ~(I2C_I2CR_MSTA | I2C_I2CR_IIEN); + count = -1; + } + + desc->addr_count = 0; + + return count; +} + +int i2c_read(struct i2c_node *node, int reg, + unsigned char *data, int data_count) +{ + struct i2c_module_descriptor *const desc = &i2c_descs[node->num]; + unsigned char ad[1]; + + mutex_lock(&desc->m); + + desc->addr = (node->addr & 0xfe) | 0x1; /* Slave address/rd */ + + if (reg >= 0) + { + /* Sub-address */ + desc->addr_count = 1; + desc->addr_data = ad; + ad[0] = reg; + } + /* else raw read from slave */ + + desc->data = data; + desc->data_count = data_count; + + data_count = i2c_transfer(node, desc); + + mutex_unlock(&desc->m); + + return data_count; +} + +int i2c_write(struct i2c_node *node, const unsigned char *data, int data_count) +{ + struct i2c_module_descriptor *const desc = &i2c_descs[node->num]; + + mutex_lock(&desc->m); + + desc->addr = node->addr & 0xfe; /* Slave address/wr */ + desc->data = (unsigned char *)data; + desc->data_count = data_count; + + data_count = i2c_transfer(node, desc); + + mutex_unlock(&desc->m); + + return data_count; +} + +void i2c_init(void) +{ + int i; + + /* Do one-time inits for each module that will be used - leave + * module disabled and unclocked until something wants it */ + for (i = 0; i < I2C_NUM_I2C; i++) + { + struct i2c_module_descriptor *const desc = &i2c_descs[i]; + ccm_module_clock_gating(desc->cg, CGM_ON_RUN_WAIT); + mutex_init(&desc->m); + wakeup_init(&desc->w); + desc->base->i2cr = 0; + ccm_module_clock_gating(desc->cg, CGM_OFF); + } +} + +void i2c_enable_node(struct i2c_node *node, bool enable) +{ + struct i2c_module_descriptor *const desc = &i2c_descs[node->num]; + + mutex_lock(&desc->m); + + if (enable) + { + if (++desc->enable == 1) + { + /* First enable */ + ccm_module_clock_gating(desc->cg, CGM_ON_RUN_WAIT); + avic_enable_int(desc->ints, INT_TYPE_IRQ, INT_PRIO_DEFAULT, + desc->handler); + } + } + else + { + if (desc->enable > 0 && --desc->enable == 0) + { + /* Last enable */ + while (desc->base->i2sr & I2C_I2SR_IBB); /* Wait for STOP */ + desc->base->i2cr &= ~I2C_I2CR_IEN; + avic_disable_int(desc->ints); + ccm_module_clock_gating(desc->cg, CGM_OFF); + } + } + + mutex_unlock(&desc->m); +} + +void i2c_lock_node(struct i2c_node *node) +{ + struct i2c_module_descriptor *const desc = &i2c_descs[node->num]; + mutex_lock(&desc->m); +} + +void i2c_unlock_node(struct i2c_node *node) +{ + struct i2c_module_descriptor *const desc = &i2c_descs[node->num]; + mutex_unlock(&desc->m); +} diff --git a/firmware/target/arm/imx31/i2c-imx31.h b/firmware/target/arm/imx31/i2c-imx31.h new file mode 100644 index 0000000000..b36acecfcb --- /dev/null +++ b/firmware/target/arm/imx31/i2c-imx31.h @@ -0,0 +1,78 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 by Michael Sevakis + * + * 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. + * + ****************************************************************************/ +#ifndef I2C_IMX31_H +#define I2C_IMX31_H + +#include + +/* I2C module usage masks */ +#define USE_I2C1_MODULE (1 << 0) +#define USE_I2C2_MODULE (1 << 1) +#define USE_I2C3_MODULE (1 << 2) + +enum i2c_module_number +{ + __I2C_NUM_START = -1, +#if (I2C_MODULE_MASK & USE_I2C1_MODULE) + I2C1_NUM, +#endif +#if (I2C_MODULE_MASK & USE_I2C2_MODULE) + I2C2_NUM, +#endif +#if (I2C_MODULE_MASK & USE_I2C3_MODULE) + I2C3_NUM, +#endif + I2C_NUM_I2C, +}; + +/* Module interface map structure */ +struct i2c_map +{ + volatile uint16_t iadr; /* 0x00 */ + volatile uint16_t unused1; + volatile uint16_t ifdr; /* 0x04 */ + volatile uint16_t unused2; + volatile uint16_t i2cr; /* 0x08 */ + volatile uint16_t unused3; + volatile uint16_t i2sr; /* 0x0C */ + volatile uint16_t unused4; + volatile uint16_t i2dr; /* 0x10 */ +}; + +struct i2c_node +{ + enum i2c_module_number num; /* Module that this node uses */ + unsigned int ifdr; /* Maximum frequency for node */ + unsigned char addr; /* Slave address on module */ +}; + +void i2c_init(void); +/* Enable or disable the node - modules will be switch on/off accordingly. */ +void i2c_enable_node(struct i2c_node *node, bool enable); +/* If addr < 0, then raw read */ +int i2c_read(struct i2c_node *node, int addr, unsigned char *data, int count); +int i2c_write(struct i2c_node *node, const unsigned char *data, int count); +/* Gain mutually-exclusive access to the node and module to perform multiple + * operations atomically */ +void i2c_lock_node(struct i2c_node *node); +void i2c_unlock_node(struct i2c_node *node); + +#endif /* I2C_IMX31_H */ diff --git a/firmware/target/arm/imx31/mc13783-imx31.c b/firmware/target/arm/imx31/mc13783-imx31.c new file mode 100644 index 0000000000..fc5dfa72f6 --- /dev/null +++ b/firmware/target/arm/imx31/mc13783-imx31.c @@ -0,0 +1,355 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (c) 2008 by Michael Sevakis + * + * 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 "system.h" +#include "cpu.h" +#include "spi-imx31.h" +#include "gpio-imx31.h" +#include "mc13783.h" +#include "debug.h" +#include "kernel.h" + +#ifdef BOOTLOADER +#define PMIC_DRIVER_CLOSE +#endif + +/* 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 */ +}; + +extern const struct mc13783_event_list mc13783_event_list; + +static int mc13783_thread_stack[DEFAULT_STACK_SIZE/sizeof(int)]; +static const char *mc13783_thread_name = "pmic"; +static struct wakeup mc13783_wake; + +/* Tracking for which interrupts are enabled */ +static uint32_t pmic_int_enabled[2] = + { 0x00000000, 0x00000000 }; + +static const unsigned char pmic_intm_regs[2] = + { MC13783_INTERRUPT_MASK0, MC13783_INTERRUPT_MASK1 }; + +static const unsigned char pmic_ints_regs[2] = + { MC13783_INTERRUPT_STATUS0, MC13783_INTERRUPT_STATUS1 }; + +#ifdef PMIC_DRIVER_CLOSE +static bool pmic_close = false; +static unsigned int mc13783_thread_id = 0; +#endif + +static void mc13783_interrupt_thread(void) +{ + uint32_t pending[2]; + + /* Enable mc13783 GPIO event */ + gpio_enable_event(MC13783_EVENT_ID); + + while (1) + { + const struct mc13783_event *event, *event_last; + + wakeup_wait(&mc13783_wake, TIMEOUT_BLOCK); + +#ifdef PMIC_DRIVER_CLOSE + if (pmic_close) + break; +#endif + + mc13783_read_regset(pmic_ints_regs, pending, 2); + + /* Only clear interrupts being dispatched */ + pending[0] &= pmic_int_enabled[0]; + pending[1] &= pmic_int_enabled[1]; + + mc13783_write_regset(pmic_ints_regs, pending, 2); + + /* Whatever is going to be serviced in this loop has been + * acknowledged. Reenable interrupt and if anything was still + * pending or became pending again, another signal will be + * generated. */ + imx31_regset32(&MC13783_GPIO_IMR, 1ul << MC13783_GPIO_LINE); + + event = mc13783_event_list.events; + event_last = event + mc13783_event_list.count; + + /* .count is surely expected to be > 0 */ + do + { + enum mc13783_event_sets set = event->set; + uint32_t pnd = pending[set]; + uint32_t mask = event->mask; + + if (pnd & mask) + { + event->callback(); + pnd &= ~mask; + pending[set] = pnd; + } + + if ((pending[0] | pending[1]) == 0) + break; /* Teminate early if nothing more to service */ + } + while (++event < event_last); + } + +#ifdef PMIC_DRIVER_CLOSE + gpio_disable_event(MC13783_EVENT_ID); +#endif +} + +/* GPIO interrupt handler for mc13783 */ +void mc13783_event(void) +{ + /* Mask the interrupt (unmasked when PMIC thread services it). */ + imx31_regclr32(&MC13783_GPIO_IMR, 1ul << MC13783_GPIO_LINE); + MC13783_GPIO_ISR = (1ul << MC13783_GPIO_LINE); + 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); + + /* Mask any PMIC interrupts for now - modules will enable them as + * required */ + mc13783_write(MC13783_INTERRUPT_MASK0, 0xffffff); + mc13783_write(MC13783_INTERRUPT_MASK1, 0xffffff); + + MC13783_GPIO_ISR = (1ul << MC13783_GPIO_LINE); + +#ifdef PMIC_DRIVER_CLOSE + mc13783_thread_id = +#endif + create_thread(mc13783_interrupt_thread, + mc13783_thread_stack, sizeof(mc13783_thread_stack), 0, + mc13783_thread_name IF_PRIO(, PRIORITY_REALTIME) IF_COP(, CPU)); +} + +#ifdef PMIC_DRIVER_CLOSE +void mc13783_close(void) +{ + unsigned int thread_id = mc13783_thread_id; + + if (thread_id == 0) + return; + + mc13783_thread_id = 0; + + pmic_close = true; + wakeup_signal(&mc13783_wake); + thread_wait(thread_id); +} +#endif /* PMIC_DRIVER_CLOSE */ + +bool mc13783_enable_event(enum mc13783_event_ids id) +{ + const struct mc13783_event * const event = + &mc13783_event_list.events[id]; + int set = event->set; + uint32_t mask = event->mask; + + spi_lock(&mc13783_spi); + + pmic_int_enabled[set] |= mask; + mc13783_clear(pmic_intm_regs[set], mask); + + spi_unlock(&mc13783_spi); + + return true; +} + +void mc13783_disable_event(enum mc13783_event_ids id) +{ + const struct mc13783_event * const event = + &mc13783_event_list.events[id]; + int set = event->set; + uint32_t mask = event->mask; + + spi_lock(&mc13783_spi); + + pmic_int_enabled[set] &= ~mask; + mc13783_set(pmic_intm_regs[set], mask); + + spi_unlock(&mc13783_spi); +} + +uint32_t mc13783_set(unsigned address, uint32_t bits) +{ + spi_lock(&mc13783_spi); + + uint32_t data = mc13783_read(address); + + if (data != MC13783_DATA_ERROR) + mc13783_write(address, data | bits); + + spi_unlock(&mc13783_spi); + + return data; +} + +uint32_t mc13783_clear(unsigned address, uint32_t bits) +{ + spi_lock(&mc13783_spi); + + uint32_t data = mc13783_read(address); + + if (data != MC13783_DATA_ERROR) + mc13783_write(address, data & ~bits); + + spi_unlock(&mc13783_spi); + + return data; +} + +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; +} + +uint32_t mc13783_write_masked(unsigned address, uint32_t data, uint32_t mask) +{ + uint32_t old; + + spi_lock(&mc13783_spi); + + old = mc13783_read(address); + + if (old != MC13783_DATA_ERROR) + { + data = (old & ~mask) | (data & mask); + + if (mc13783_write(address, data) != 1) + old = MC13783_DATA_ERROR; + } + + spi_unlock(&mc13783_spi); + + return old; +} + +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 ((unsigned)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; + struct spi_transfer xfer; + + if (address >= MC13783_NUM_REGS) + return MC13783_DATA_ERROR; + + packet = address << 25; + + xfer.txbuf = &packet; + xfer.rxbuf = &packet; + xfer.count = 1; + + if (!spi_transfer(&mc13783_spi, &xfer)) + return MC13783_DATA_ERROR; + + return packet; +} + +int mc13783_read_regset(const unsigned char *regs, uint32_t *buffer, + int count) +{ + int i; + struct spi_transfer xfer; + + if ((unsigned)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; + + return count - xfer.count; +} diff --git a/firmware/target/arm/imx31/mmu-imx31.c b/firmware/target/arm/imx31/mmu-imx31.c new file mode 100644 index 0000000000..920a8c9fd3 --- /dev/null +++ b/firmware/target/arm/imx31/mmu-imx31.c @@ -0,0 +1,33 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 by Will Robertson + * + * 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 "cpu.h" +#include "mmu-imx31.h" +#include "mmu-arm.h" + +unsigned long addr_virt_to_phys(unsigned long addr) +{ + return addr | CSD0_BASE_ADDR; +} + +unsigned long addr_phys_to_virt(unsigned long addr) +{ + return addr & ~CSD0_BASE_ADDR; +} diff --git a/firmware/target/arm/imx31/mmu-imx31.h b/firmware/target/arm/imx31/mmu-imx31.h new file mode 100644 index 0000000000..c66a3d941d --- /dev/null +++ b/firmware/target/arm/imx31/mmu-imx31.h @@ -0,0 +1,29 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006,2007 by Greg White + * + * 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. + * + ****************************************************************************/ +#ifndef MMU_IMX31_H +#define MMU_IMX31_H + +void memory_init(void); +void set_page_tables(void); +unsigned long addr_virt_to_phys(unsigned long addr); +unsigned long addr_phys_to_virt(unsigned long addr); + +#endif /* MMU_IMX31_H */ diff --git a/firmware/target/arm/imx31/serial-imx31.h b/firmware/target/arm/imx31/serial-imx31.h new file mode 100644 index 0000000000..cbb7be2ec3 --- /dev/null +++ b/firmware/target/arm/imx31/serial-imx31.h @@ -0,0 +1,32 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 by James Espinoza + * + * 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. + * + ****************************************************************************/ +#ifndef SERIAL_IMX31_H +#define SERIAL_IMX31_H + +#include +#include + +int tx_rdy(void); +int rx_rdy(void); +void tx_writec(const unsigned char c); +void dprintf(const char * str, ... ); + +#endif /* SERIAL_IMX31_H */ diff --git a/firmware/target/arm/imx31/spi-imx31.c b/firmware/target/arm/imx31/spi-imx31.c new file mode 100644 index 0000000000..ac063f9b10 --- /dev/null +++ b/firmware/target/arm/imx31/spi-imx31.c @@ -0,0 +1,352 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (c) 2007 Will Robertson + * + * 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 "avic-imx31.h" +#include "ccm-imx31.h" +#include "debug.h" +#include "kernel.h" + +/* 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 +{ + struct cspi_map * const 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 = (struct cspi_map *)CSPI1_BASE_ADDR, + .cg = CG_CSPI1, + .ints = INT_CSPI1, + .handler = CSPI1_HANDLER, + }, +#endif +#if (SPI_MODULE_MASK & USE_CSPI2_MODULE) + { + .base = (struct cspi_map *)CSPI2_BASE_ADDR, + .cg = CG_CSPI2, + .ints = INT_CSPI2, + .handler = CSPI2_HANDLER, + }, +#endif +#if (SPI_MODULE_MASK & USE_CSPI3_MODULE) + { + .base = (struct cspi_map *)CSPI3_BASE_ADDR, + .cg = CG_CSPI3, + .ints = INT_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]; + struct cspi_map * 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->statreg & CSPI_STATREG_RR) != 0) + { + uint32_t word = base->rxdata; + + 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->intreg; + + if (desc->rxcount <= 0) + { + /* No more to receive - stop RX interrupts */ + intreg &= ~(CSPI_INTREG_RHEN | CSPI_INTREG_RREN); + base->intreg = intreg; + break; + } + else if (!(intreg & CSPI_INTREG_RREN)) + { + /* < 4 words expected - switch to RX ready */ + intreg &= ~CSPI_INTREG_RHEN; + base->intreg = intreg | CSPI_INTREG_RREN; + } + } + } + } + + if (trans->count > 0) + { + /* Data to transmit - fill TXFIFO or write until exhausted */ + while ((base->statreg & 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->txdata = word; + + if (--trans->count <= 0) + { + /* Out of data - stop TX interrupts */ + base->intreg &= ~CSPI_INTREG_THEN; + break; + } + } + } + + /* If all interrupts have been remasked - we're done */ + if (base->intreg == 0) + { + base->statreg = 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) +{ + struct cspi_map * const base = desc->base; + + if ((base->conreg & 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->conreg = + (node->conreg & ~(0xfcc8e000 | CSPI_CONREG_XCH | CSPI_CONREG_SMC)) + | CSPI_CONREG_EN; + /* Set the wait-states */ + base->periodreg = node->periodreg & 0xffff; + /* Clear out any spuriously-pending interrupts */ + base->statreg = CSPI_STATREG_TC | CSPI_STATREG_BO; + } + + return true; +} + +static void spi_reset(struct cspi_map * const base) +{ + /* Reset */ + base->conreg &= ~CSPI_CONREG_EN; + base->conreg |= CSPI_CONREG_EN; + base->intreg = 0; + base->statreg = CSPI_STATREG_TC | CSPI_STATREG_BO; +} + +/* Initialize each of the used SPI descriptors */ +void spi_init(void) +{ + int i; + + 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); + } +} + +/* 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 */ + struct cspi_map * const base = desc->base; + + /* Enable clock-gating register */ + ccm_module_clock_gating(desc->cg, CGM_ON_RUN_WAIT); + /* Reset */ + spi_reset(base); + desc->last = NULL; + /* Enable interrupt at controller level */ + avic_enable_int(desc->ints, INT_TYPE_IRQ, INT_PRIO_DEFAULT, + desc->handler); + } + + mutex_unlock(&desc->m); +} + +/* 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 */ + struct cspi_map * const base = desc->base; + + /* Disable interrupt at controller level */ + avic_disable_int(desc->ints); + + /* Disable interface */ + base->conreg &= ~CSPI_CONREG_EN; + + /* Disable interface clock */ + ccm_module_clock_gating(desc->cg, CGM_OFF); + } + + mutex_unlock(&desc->m); +} + +/* 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) + { + struct cspi_map * 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->intreg = intreg; + + /* Start transfer */ + base->conreg |= CSPI_CONREG_XCH; + + if (wakeup_wait(&desc->w, HZ) != OBJ_WAIT_SUCCEEDED) + { + base->intreg = 0; /* Stop SPI ints */ + spi_reset(base); /* Reset module (esp. to empty FIFOs) */ + desc->last = NULL; /* Force reconfigure */ + retval = false; + } + } + + mutex_unlock(&desc->m); + + return retval; +} diff --git a/firmware/target/arm/imx31/spi-imx31.h b/firmware/target/arm/imx31/spi-imx31.h new file mode 100644 index 0000000000..cf536b646d --- /dev/null +++ b/firmware/target/arm/imx31/spi-imx31.h @@ -0,0 +1,89 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (c) 2007 Will Robertson + * + * 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. + * + ****************************************************************************/ +#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 +{ + __CSPI_NUM_START = -1, /* The first will be 0 */ +#if (SPI_MODULE_MASK & USE_CSPI1_MODULE) + CSPI1_NUM, +#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 cspi_map +{ + volatile uint32_t rxdata; /* 00h */ + volatile uint32_t txdata; /* 04h */ + volatile uint32_t conreg; /* 08h */ + volatile uint32_t intreg; /* 0Ch */ + volatile uint32_t dmareg; /* 10h */ + volatile uint32_t statreg; /* 14h */ + volatile uint32_t periodreg; /* 18h */ + volatile uint32_t skip1[0x69]; /* 1Ch */ + volatile uint32_t testreg; /* 1C0h */ +}; + +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); + +/* 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 */ -- cgit v1.2.3