diff options
Diffstat (limited to 'src/target/firmware/calypso')
24 files changed, 4147 insertions, 0 deletions
diff --git a/src/target/firmware/calypso/Makefile b/src/target/firmware/calypso/Makefile new file mode 100644 index 00000000..c0620ed8 --- /dev/null +++ b/src/target/firmware/calypso/Makefile @@ -0,0 +1,4 @@ + +LIBRARIES+=calypso +calypso_DIR=calypso +calypso_SRCS=arm.c buzzer.c clock.c delay.c dma.c dsp.c du.c i2c.c irq.c rtc.c sim.c spi.c tpu.c tsp.c keypad.c misc.c timer.c backlight.c uart.c uwire.c diff --git a/src/target/firmware/calypso/arm.c b/src/target/firmware/calypso/arm.c new file mode 100644 index 00000000..8794ee35 --- /dev/null +++ b/src/target/firmware/calypso/arm.c @@ -0,0 +1,26 @@ + +/* enable IRQ+FIQ interrupts */ +void arm_enable_interrupts (void) +{ + unsigned long temp; + __asm__ __volatile__("mrs %0, cpsr\n" + "bic %0, %0, #0xc0\n" + "msr cpsr_c, %0" + : "=r" (temp) + : + : "memory"); +} + +/* disable IRQ/FIQ interrupts + * returns true if interrupts had been enabled before we disabled them */ +int arm_disable_interrupts(void) +{ + unsigned long old,temp; + __asm__ __volatile__("mrs %0, cpsr\n" + "orr %1, %0, #0xc0\n" + "msr cpsr_c, %1" + : "=r" (old), "=r" (temp) + : + : "memory"); + return (old & 0x80) == 0; +} diff --git a/src/target/firmware/calypso/backlight.c b/src/target/firmware/calypso/backlight.c new file mode 100644 index 00000000..a18dcb91 --- /dev/null +++ b/src/target/firmware/calypso/backlight.c @@ -0,0 +1,69 @@ +/* Calypso DBB internal PWL (Pulse Width / Light) Driver */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <memory.h> + +#define BASE_ADDR_PWL 0xfffe8000 +#define PWL_REG(m) (BASE_ADDR_PWL + (m)) + +#define ASIC_CONF_REG 0xfffef008 +#define LIGHT_LEVEL_REG 0xfffe4810 + +enum pwl_reg { + PWL_LEVEL = 0, + PWL_CTRL = 1, +}; + +#define ASCONF_PWL_ENA (1 << 4) + +void bl_mode_pwl(int on) +{ + uint16_t reg; + + reg = readw(ASIC_CONF_REG); + + if (on) { + /* Enable pwl */ + writeb(0x01, PWL_REG(PWL_CTRL)); + /* Switch pin from LT to PWL */ + reg |= ASCONF_PWL_ENA; + writew(reg, ASIC_CONF_REG); + } else { + /* Switch pin from PWL to LT */ + reg |= ~ASCONF_PWL_ENA; + writew(reg, ASIC_CONF_REG); + /* Disable pwl */ + writeb(0x00, PWL_REG(PWL_CTRL)); + } +} + +void bl_level(uint8_t level) +{ + if (readw(ASIC_CONF_REG) & ASCONF_PWL_ENA) { + writeb(level, PWL_REG(PWL_LEVEL)); + } else { + /* we need to scale the light level, as the + * ARMIO light controller only knows 0..63 */ + writeb(level>>2, LIGHT_LEVEL_REG); + } +} diff --git a/src/target/firmware/calypso/buzzer.c b/src/target/firmware/calypso/buzzer.c new file mode 100644 index 00000000..e76906f9 --- /dev/null +++ b/src/target/firmware/calypso/buzzer.c @@ -0,0 +1,86 @@ +/* Calypso DBB internal PWT (Pulse Width / T) Buzzer Driver */ + +/* (C) 2010 by Jose Pereira <onaips@gmail.com> + * + * All Rights Reserved + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <memory.h> + +#define BASE_ADDR_PWL 0xfffe8800 +#define PWT_REG(m) (BASE_ADDR_PWL + (m)) + +#define ASIC_CONF_REG 0xfffef008 +#define BUZZ_LEVEL_REG 0xfffe480e + +enum pwt_reg { + FRC_REG = 0, + VRC_REG = 1, + GCR_REG = 2, +}; + +#define ASCONF_PWT_ENA (1 << 5) + +void buzzer_mode_pwt(int on) +{ + uint16_t reg; + + reg = readw(ASIC_CONF_REG); + + if (on) { + /* Enable pwt */ + writeb(0x01, PWT_REG(GCR_REG)); + /* Switch pin from LT to PWL */ + reg |= ASCONF_PWT_ENA; + writew(reg, ASIC_CONF_REG); + } else { + /* Switch pin from PWT to BU */ + reg |= ~ASCONF_PWT_ENA; + writew(reg, ASIC_CONF_REG); + /* Disable pwt */ + writeb(0x00, PWT_REG(GCR_REG)); + } +} + +void buzzer_volume(uint8_t level) +{ + + if (readw(ASIC_CONF_REG) & ASCONF_PWT_ENA) { + + if (level) { + //scaling the volume as pwt only knows 0..63 + level = level >> 1; + //if level > 0 buzzer is on + level |= 0x01; + } + + writeb(level,PWT_REG(VRC_REG)); + + } else { + /* we need to scale the buzz level, as the + * ARMIO buzz controller only knows 0..63 */ + writeb(level>>2, BUZZ_LEVEL_REG); + } +} + +void buzzer_note(uint8_t note) +{ + if ( (readw(ASIC_CONF_REG) & ASCONF_PWT_ENA) ) + writeb(note,PWT_REG(FRC_REG)); +} diff --git a/src/target/firmware/calypso/clock.c b/src/target/firmware/calypso/clock.c new file mode 100644 index 00000000..246b6e00 --- /dev/null +++ b/src/target/firmware/calypso/clock.c @@ -0,0 +1,200 @@ +/* Driver for Calypso clock management */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +//#define DEBUG +#include <debug.h> + +#include <memory.h> +#include <calypso/clock.h> + +#define REG_DPLL 0xffff9800 +#define DPLL_LOCK (1 << 0) +#define DPLL_BREAKLN (1 << 1) +#define DPLL_BYPASS_DIV_SHIFT 2 /* 2 bits */ +#define DPLL_PLL_ENABLE (1 << 4) +#define DPLL_PLL_DIV_SHIFT 5 /* 2 bits */ +#define DPLL_PLL_MULT_SHIFT 7 /* 5 bits */ +#define DPLL_TEST (1 << 12) +#define DPLL_IOB (1 << 13) /* Initialize on break */ +#define DPLL_IAI (1 << 14) /* Initialize after Idle */ + +#define BASE_ADDR_CLKM 0xfffffd00 +#define CLKM_REG(m) (BASE_ADDR_CLKM+(m)) + +enum clkm_reg { + CNTL_ARM_CLK = 0, + CNTL_CLK = 2, + CNTL_RST = 4, + CNTL_ARM_DIV = 8, +}; + +/* CNTL_ARM_CLK */ +#define ARM_CLK_BIG_SLEEP (1 << 0) /* MCU Master Clock enabled? */ +#define ARM_CLK_CLKIN_SEL0 (1 << 1) /* MCU source clock (0 = DPLL output, 1 = VTCXO or CLKIN */ +#define ARM_CLK_CLKIN_SEL (1 << 2) /* 0 = VTCXO or 1 = CLKIN */ +#define ARM_CLK_MCLK_DIV5 (1 << 3) /* enable 1.5 or 2.5 division factor */ +#define ARM_CLK_MCLK_DIV_SHIFT 4 /* 3 bits */ +#define ARM_CLK_DEEP_POWER_SHIFT 8 +#define ARM_CLK_DEEP_SLEEP 12 + +/* CNTL_CLK */ +#define CLK_IRQ_CLK_DIS (1 << 0) /* IRQ clock control (0 always, 1 according ARM_MCLK_EN) */ +#define CLK_BRIDGE_CLK_DIS (1 << 1) +#define CLK_TIMER_CLK_DIS (1 << 2) +#define CLK_DPLL_DIS (1 << 3) /* 0: DPLL is not stopped during SLEEP */ +#define CLK_CLKOUT_EN (1 << 4) /* Enable CLKOUT output pins */ +#define CLK_EN_IDLE3_FLG (1 << 5) /* DSP idle flag control (1 = + * SAM/HOM register forced to HOM when DSP IDLE3) */ +#define CLK_VCLKOUT_DIV2 (1 << 6) /* 1: VCLKOUT-FR is divided by 2 */ +#define CLK_VTCXO_DIV2 (1 << 7) /* 1: VTCXO is dividied by 2 */ + +#define BASE_ADDR_MEMIF 0xfffffb00 +#define MEMIF_REG(x) (BASE_ADDR_MEMIF+(x)) + +enum memif_reg { + API_RHEA_CTL = 0x0e, + EXTRA_CONF = 0x10, +}; + +static void dump_reg16(uint32_t addr, char *name) +{ + printf("%s=0x%04x\n", name, readw(addr)); +} + +void calypso_clk_dump(void) +{ + dump_reg16(REG_DPLL, "REG_DPLL"); + dump_reg16(CLKM_REG(CNTL_ARM_CLK), "CNTL_ARM_CLK"); + dump_reg16(CLKM_REG(CNTL_CLK), "CNTL_CLK"); + dump_reg16(CLKM_REG(CNTL_RST), "CNTL_RST"); + dump_reg16(CLKM_REG(CNTL_ARM_DIV), "CNTL_ARM_DIV"); +} + +void calypso_pll_set(uint16_t inp) +{ + uint8_t mult = inp >> 8; + uint8_t div = inp & 0xff; + uint16_t reg = readw(REG_DPLL); + + reg &= ~0x0fe0; + reg |= (div & 0x3) << DPLL_PLL_DIV_SHIFT; + reg |= (mult & 0x1f) << DPLL_PLL_MULT_SHIFT; + reg |= DPLL_PLL_ENABLE; + + writew(reg, REG_DPLL); +} + +void calypso_reset_set(enum calypso_rst calypso_rst, int active) +{ + uint8_t reg = readb(CLKM_REG(CNTL_RST)); + + if (active) + reg |= calypso_rst; + else + reg &= ~calypso_rst; + + writeb(reg, CLKM_REG(CNTL_RST)); +} + +int calypso_reset_get(enum calypso_rst calypso_rst) +{ + uint8_t reg = readb(CLKM_REG(CNTL_RST)); + + if (reg & calypso_rst) + return 1; + else + return 0; +} + +void calypso_clock_set(uint8_t vtcxo_div2, uint16_t inp, enum mclk_div mclk_div) +{ + uint16_t cntl_clock = readw(CLKM_REG(CNTL_CLK)); + uint16_t cntl_arm_clk = readw(CLKM_REG(CNTL_ARM_CLK)); + + /* First set the vtcxo_div2 */ + cntl_clock &= ~CLK_VCLKOUT_DIV2; + if (vtcxo_div2) + cntl_clock |= CLK_VTCXO_DIV2; + else + cntl_clock &= ~CLK_VTCXO_DIV2; + writew(cntl_clock, CLKM_REG(CNTL_CLK)); + + /* Then configure the MCLK divider */ + cntl_arm_clk &= ~ARM_CLK_CLKIN_SEL0; + if (mclk_div & 0x80) { + mclk_div &= ~0x80; + cntl_arm_clk |= ARM_CLK_MCLK_DIV5; + } else + cntl_arm_clk &= ~ARM_CLK_MCLK_DIV5; + cntl_arm_clk &= ~(0x7 << ARM_CLK_MCLK_DIV_SHIFT); + cntl_arm_clk |= (mclk_div << ARM_CLK_MCLK_DIV_SHIFT); + writew(cntl_arm_clk, CLKM_REG(CNTL_ARM_CLK)); + + /* Then finally set the PLL */ + calypso_pll_set(inp); +} + +void calypso_mem_cfg(enum calypso_bank bank, uint8_t ws, + enum calypso_mem_width width, int we) +{ + writew((ws & 0x1f) | ((width & 3) << 5) | ((we & 1) << 7), + BASE_ADDR_MEMIF + bank); +} + +void calypso_bootrom(int enable) +{ + uint16_t conf = readw(MEMIF_REG(EXTRA_CONF)); + + conf |= (3 << 8); + + if (enable) + conf &= ~(1 << 9); + + writew(conf, MEMIF_REG(EXTRA_CONF)); +} + +void calypso_debugunit(int enable) +{ + uint16_t conf = readw(MEMIF_REG(EXTRA_CONF)); + + if (enable) + conf &= ~(1 << 11); + else + conf |= (1 << 11); + + writew(conf, MEMIF_REG(EXTRA_CONF)); +} + +#define REG_RHEA_CNTL 0xfffff900 +#define REG_API_CNTL 0xfffff902 +#define REG_ARM_RHEA 0xfffff904 + +void calypso_rhea_cfg(uint8_t fac0, uint8_t fac1, uint8_t timeout, + uint8_t ws_h, uint8_t ws_l, uint8_t w_en0, uint8_t w_en1) +{ + writew(fac0 | (fac1 << 4) | (timeout << 8), REG_RHEA_CNTL); + writew(ws_h | (ws_l << 5), REG_API_CNTL); + writew(w_en0 | (w_en1 << 1), REG_ARM_RHEA); +} diff --git a/src/target/firmware/calypso/delay.c b/src/target/firmware/calypso/delay.c new file mode 100644 index 00000000..443ca827 --- /dev/null +++ b/src/target/firmware/calypso/delay.c @@ -0,0 +1,16 @@ +#include <delay.h> + +/* FIXME: We need properly calibrated delay loops at some point! */ +void delay_us(unsigned int us) +{ + volatile unsigned int i; + + for (i= 0; i < us*4; i++) { i; } +} + +void delay_ms(unsigned int ms) +{ + volatile unsigned int i; + + for (i= 0; i < ms*1300; i++) { i; } +} diff --git a/src/target/firmware/calypso/dma.c b/src/target/firmware/calypso/dma.c new file mode 100644 index 00000000..35c5be82 --- /dev/null +++ b/src/target/firmware/calypso/dma.c @@ -0,0 +1,44 @@ +/* Driver for Calypso DMA controller */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <memory.h> + +#define BASE_ADDR_DMA 0xfffffc00 + +enum dma_reg { + CONTROLLER_CONF = 0x00, + ALLOC_CONFIG = 0x02, +}; +#define DMA_REG(m) (BASE_ADDR_DMA + (m)) + +#define DMA_RAD(x) DMA_REG((x)*0x10 + 0x0) +#define DMA_RDPATH(x) DMA_REG((x)*0x10 + 0x2) +#define DMA_AAD(x) DMA_REG((x)*0x10 + 0x4) +#define DMA_ALGTH(x) DMA_REG((x)*0x10 + 0x6) +#define DMA_CTRL(x) DMA_REG((x)*0x10 + 0x8) +#define DMA_CUR_OFF_API(x) DMA_REG((x)*0x10 + 0xa) + +void dma_init(void) +{ + /* DMA 1 (RIF Tx), 2 (RIF Rx) allocated to DSP, all others to ARM */ + writew(0x000c, DMA_REG(ALLOC_CONFIG)); +} diff --git a/src/target/firmware/calypso/dsp.c b/src/target/firmware/calypso/dsp.c new file mode 100644 index 00000000..f22a7e0e --- /dev/null +++ b/src/target/firmware/calypso/dsp.c @@ -0,0 +1,693 @@ +#define DEBUG +/* Driver for the Calypso integrated DSP */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <delay.h> +#include <memory.h> +#include <calypso/clock.h> +#include <calypso/dsp.h> +#include <calypso/dsp_api.h> +#include <calypso/tpu.h> + +#include <abb/twl3025.h> + +#include <osmocore/gsm_utils.h> + + +#define REG_API_CONTROL 0xfffe0000 +#define APIC_R_SMODE_HOM (1 << 1) /* API is configured in HOM mode */ +#define APIC_R_HINT (1 << 3) /* Host processor interrupt (DSP->MCU) */ +#define APIC_W_DSPINT (1 << 2) /* ARM issues interrupt to DSP */ + +#define REG_API_WS 0xfffff902 /* Number of wait states for ARM access to API memory */ +#define REG_ARM_RHEA_CTL 0xfffff904 /* Write buffer bypassing */ +#define REG_EXT_RHEA_CTL 0xfffff906 /* Some timeout */ + +#define API_SIZE 0x2000U /* in words */ + +#define BASE_API_RAM 0xffd00000 /* Base address of API RAM from ARM point of view */ + +#define DSP_BASE_API 0x0800 /* Base address of API RAM for DSP */ +#define DSP_BASE_API_MIRROR 0xe000 /* Base address of API RAM for DSP (API boot mirror) */ +#define DSP_START 0x7000 /* DSP Start address */ + +/* Boot loader */ +#define BL_CMD_STATUS (BASE_API_RAM + 0x0ffe) /* Status / Command var */ +#define BL_ADDR_LO (BASE_API_RAM + 0x0ffc) /* Address (16 lsbs) */ +#define BL_ADDR_HI (BASE_API_RAM + 0x0ff8) /* Address (ext page bits) */ +#define BL_SIZE (BASE_API_RAM + 0x0ffa) /* Size */ + +#define BL_MAX_BLOCK_SIZE 0x7F0 /* Maximum size of copied block */ + + /* Possible values for the download status */ +#define BL_STATUS_NA 0 +#define BL_STATUS_IDLE 1 +#define BL_CMD_COPY_BLOCK 2 +#define BL_CMD_COPY_MODE 4 + +#define BL_MODE_PROG_WRITE 0 +#define BL_MODE_DATA_WRITE 1 +#define BL_MODE_PROG_READ 2 +#define BL_MODE_DATA_READ 3 +#define BL_MODE_PROM_READ 4 +#define BL_MODE_DROM_READ 5 + + +struct dsp_section { + uint32_t addr; /* addr for DSP */ + uint32_t size; /* size in words */ + const uint16_t *data; +}; + +#include "dsp_params.c" +#include "dsp_bootcode.c" +#include "dsp_dumpcode.c" + +struct dsp_api dsp_api = { + .ndb = (T_NDB_MCU_DSP *) BASE_API_NDB, + .db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_0, + .db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_0, + .param = (T_PARAM_MCU_DSP *) BASE_API_PARAM, + .r_page = 0, + .w_page = 0, +}; + + +void dsp_dump_version(void) +{ + printf("DSP Download Status: 0x%04x\n", readw(BL_CMD_STATUS)); + printf("DSP API Version: 0x%04x 0x%04x\n", + dsp_api.ndb->d_version_number1, dsp_api.ndb->d_version_number2); +} + +static void dsp_bl_wait_ready(void) +{ + while (readw(BL_CMD_STATUS) != BL_STATUS_IDLE); +} + +static void dsp_bl_start_at(uint16_t addr) +{ + writew(0, BL_ADDR_HI); + writew(addr, BL_ADDR_LO); + writew(0, BL_SIZE); + writew(BL_CMD_COPY_BLOCK, BL_CMD_STATUS); +} + +static int dsp_bl_upload_sections(const struct dsp_section *sec) +{ + /* Make sure the bootloader is ready */ + dsp_bl_wait_ready(); + + /* Set mode */ + writew(BL_MODE_DATA_WRITE, BASE_API_RAM); + writew(BL_CMD_COPY_MODE, BL_CMD_STATUS); + dsp_bl_wait_ready(); + + /* Scan all sections */ + for (; sec->data; sec++) { + volatile uint16_t *api = (volatile uint16_t *)BASE_API_RAM; + unsigned int i; + + if (sec->size > BL_MAX_BLOCK_SIZE) + return -1; /* not supported for now */ + + /* Copy data to API */ + for (i=0; i<sec->size; i++) + api[i] = sec->data[i]; + + /* Issue DRAM write */ + writew(sec->addr >> 16, BL_ADDR_HI); + writew(sec->addr & 0xffff, BL_ADDR_LO); + writew(sec->size, BL_SIZE); + writew(BL_CMD_COPY_BLOCK, BL_CMD_STATUS); + + /* Wait for completion */ + dsp_bl_wait_ready(); + } + + return 0; +} + +static int dsp_upload_sections_api(const struct dsp_section *sec, uint16_t dsp_base_api) +{ + for (; sec->data; sec++) { + unsigned int i; + volatile uint16_t *dptr; + + if (sec->addr & ~((1<<16)-1)) /* 64k max addr */ + return -1; + if (sec->addr < dsp_base_api) + return -1; + if ((sec->addr + sec->size) > (dsp_base_api + API_SIZE)) + return -1; + + dptr = (volatile uint16_t *)(BASE_API_RAM + ((sec->addr - dsp_base_api) * sizeof(uint16_t))); + for (i=0; i<sec->size; i++) + *dptr++ = sec->data[i]; + } + + /* FIXME need eioio or wb ? */ + + return 0; +} + +static void dsp_pre_boot(const struct dsp_section *bootcode) +{ + dputs("Assert DSP into Reset\n"); + calypso_reset_set(RESET_DSP, 1); + + if (bootcode) { + dputs("Loading initial DSP bootcode (API boot mode)\n"); + dsp_upload_sections_api(dsp_bootcode, DSP_BASE_API_MIRROR); + + writew(BL_STATUS_NA, BL_CMD_STATUS); + } else + delay_ms(10); + + dputs("Releasing DSP from Reset\n"); + calypso_reset_set(RESET_DSP, 0); + + /* Wait 10 us */ + delay_ms(100); + + dsp_bl_wait_ready(); +} + +static void dsp_set_params(int16_t *param_tab, int param_size) +{ + int i; + int16_t *param_ptr = (int16_t *) BASE_API_PARAM; + + /* Start DSP up to bootloader */ + dsp_pre_boot(dsp_bootcode); + + /* FIXME: Implement Patch download, if any */ + + dputs("Setting some dsp_api.ndb values\n"); + dsp_api.ndb->d_background_enable = 0; + dsp_api.ndb->d_background_abort = 0; + dsp_api.ndb->d_background_state = 0; + dsp_api.ndb->d_debug_ptr = 0x0074; + dsp_api.ndb->d_debug_bk = 0x0001; + dsp_api.ndb->d_pll_config = 0x154; //C_PLL_CONFIG; + dsp_api.ndb->p_debug_buffer = 0x17ff; //C_DEBUG_BUFFER_ADD; + dsp_api.ndb->d_debug_buffer_size = 7; //C_DEBUG_BUFFER_SIZE; + dsp_api.ndb->d_debug_trace_type = 0; //C_DEBUG_TRACE_TYPE; + dsp_api.ndb->d_dsp_state = 3; //C_DSP_IDLE3; + dsp_api.ndb->d_audio_gain_ul = 0; + dsp_api.ndb->d_audio_gain_dl = 0; + dsp_api.ndb->d_es_level_api = 0x5213; + dsp_api.ndb->d_mu_api = 0x5000; + + dputs("Setting API NDB parameters\n"); + for (i = 0; i < param_size; i ++) + *param_ptr++ = param_tab[i]; + + dsp_dump_version(); + + dputs("Finishing download phase\n"); + dsp_bl_start_at(DSP_START); + + dsp_dump_version(); +} + +void dsp_api_memset(uint16_t *ptr, int octets) +{ + uint16_t i; + for (i = 0; i < octets / sizeof(uint16_t); i++) + *ptr++ = 0; +} + +/* memcpy from RAM to DSP API, 16 bits by 16 bits. If odd byte count, last word will + * be zero filled */ +void dsp_memcpy_to_api(volatile uint16_t *dsp_buf, const uint8_t *mcu_buf, int n, int be) +{ + int odd, i; + + odd = n & 1; + n >>= 1; + + if (be) { + for (i=0; i<n; i++) { + uint16_t w; + w = *(mcu_buf++) << 8; + w |= *(mcu_buf++); + *(dsp_buf++) = w; + } + if (odd) + *dsp_buf = *mcu_buf << 8; + } else { + for (i=0; i<n; i++) { + uint16_t w; + w = *(mcu_buf++); + w |= *(mcu_buf++) << 8; + *(dsp_buf++) = w; + } + if (odd) + *dsp_buf = *mcu_buf; + } +} + +/* memcpy from DSP API to RAM, accessing API 16 bits word at a time */ +void dsp_memcpy_from_api(uint8_t *mcu_buf, const volatile uint16_t *dsp_buf, int n, int be) +{ + int odd, i; + + odd = n & 1; + n >>= 1; + + if (be) { + for (i=0; i<n; i++) { + uint16_t w = *(dsp_buf++); + *(mcu_buf++) = w >> 8; + *(mcu_buf++) = w; + } + if (odd) + *mcu_buf = *(dsp_buf++) >> 8; + } else { + for (i=0; i<n; i++) { + uint16_t w = *(dsp_buf++); + *(mcu_buf++) = w; + *(mcu_buf++) = w >> 8; + } + if (odd) + *mcu_buf = *(dsp_buf++); + } +} + +static void dsp_audio_init(void) +{ + T_NDB_MCU_DSP *ndb = dsp_api.ndb; + uint8_t i; + + ndb->d_vbctrl1 = ABB_VAL_T(VBCTRL1, 0x00B); /* VULSWITCH=0, VDLAUX=1, VDLEAR=1 */ + ndb->d_vbctrl2 = ABB_VAL_T(VBCTRL2, 0x000); /* MICBIASEL=0, VDLHSO=0, MICAUX=0 */ + + /* + * TODO: the following two settings are used to control + * the volume and uplink/downlink/sidetone gain. Make them + * adjustable by the user. + */ + + ndb->d_vbuctrl = ABB_VAL_T(VBUCTRL, 0x009); /* Uplink gain amp 3dB, Sidetone gain -5dB */ + ndb->d_vbdctrl = ABB_VAL_T(VBDCTRL, 0x066); /* Downlink gain amp 0dB, Volume control -6 dB */ + + ndb->d_toneskb_init = 0; /* MCU/DSP audio task com. register */ + ndb->d_toneskb_status = 0; /* MCU/DSP audio task com. register */ + + ndb->d_shiftul = 0x100; + ndb->d_shiftdl = 0x100; + + ndb->d_melo_osc_used = 0; + ndb->d_melo_osc_active = 0; + +#define SC_END_OSCILLATOR_MASK 0xfffe + + ndb->a_melo_note0[0] = SC_END_OSCILLATOR_MASK; + ndb->a_melo_note1[0] = SC_END_OSCILLATOR_MASK; + ndb->a_melo_note2[0] = SC_END_OSCILLATOR_MASK; + ndb->a_melo_note3[0] = SC_END_OSCILLATOR_MASK; + ndb->a_melo_note4[0] = SC_END_OSCILLATOR_MASK; + ndb->a_melo_note5[0] = SC_END_OSCILLATOR_MASK; + ndb->a_melo_note6[0] = SC_END_OSCILLATOR_MASK; + ndb->a_melo_note7[0] = SC_END_OSCILLATOR_MASK; + +#define MAX_FIR_COEF 31 + + /* Initialize the FIR as an all band pass */ + dsp_api.param->a_fir31_downlink[0] = 0x4000; + dsp_api.param->a_fir31_uplink[0] = 0x4000; + for (i = 1; i < MAX_FIR_COEF; i++) + { + dsp_api.param->a_fir31_downlink[i] = 0; + dsp_api.param->a_fir31_uplink[i] = 0; + } + +#define B_GSM_ONLY ((1L << 13) | (1L << 11)) /* GSM normal mode */ +#define B_BT_CORDLESS (1L << 12) /* Bluetooth cordless mode */ +#define B_BT_HEADSET (1L << 14) /* Bluetooth headset mode */ + + /* Bit set by the MCU to close the loop between the audio UL and DL path. */ + /* This features is used to find the FIR coefficient. */ +#define B_FIR_LOOP (1L << 1) + + /* Reset the FIR loopback and the audio mode */ + ndb->d_audio_init &= ~(B_FIR_LOOP | B_GSM_ONLY | B_BT_HEADSET | B_BT_CORDLESS); + + /* Set the GSM mode */ + ndb->d_audio_init |= (B_GSM_ONLY); + + ndb->d_aec_ctrl = 0; + + /* DSP background task through pending task queue */ + dsp_api.param->d_gsm_bgd_mgt = 0; + + ndb->d_audio_compressor_ctrl = 0x0401; + +#define NO_MELODY_SELECTED (0) + + ndb->d_melody_selection = NO_MELODY_SELECTED; +} + +static void dsp_ndb_init(void) +{ + T_NDB_MCU_DSP *ndb = dsp_api.ndb; + uint8_t i; + + #define APCDEL_DOWN (2+0) // minimum value: 2 + #define APCDEL_UP (6+3+1) // minimum value: 6 + + /* load APC ramp: set to "no ramp" so that there will be no output if + * not properly initialised at some other place. */ + for (i = 0; i < 16; i++) + dsp_api.ndb->a_ramp[i] = ABB_VAL(APCRAM, ABB_RAMP_VAL(0, 0)); + + /* Iota registers values will be programmed at 1st DSP communication interrupt */ + + /* Enable f_tx delay of 400000 cyc DEBUG */ + ndb->d_debug1 = ABB_VAL_T(0, 0x000); + ndb->d_afcctladd= ABB_VAL_T(AFCCTLADD, 0x000); // Value at reset + ndb->d_vbuctrl = ABB_VAL_T(VBUCTRL, 0x0C9); // Uplink gain amp 0dB, Sidetone gain to mute + ndb->d_vbdctrl = ABB_VAL_T(VBDCTRL, 0x006); // Downlink gain amp 0dB, Volume control 0 dB + ndb->d_bbctrl = ABB_VAL_T(BBCTRL, 0x2C1); // value at reset + ndb->d_bulgcal = ABB_VAL_T(BULGCAL, 0x000); // value at reset + ndb->d_apcoff = ABB_VAL_T(APCOFF, 0x040); // value at reset + ndb->d_bulioff = ABB_VAL_T(BULIOFF, 0x0FF); // value at reset + ndb->d_bulqoff = ABB_VAL_T(BULQOFF, 0x0FF); // value at reset + ndb->d_dai_onoff= ABB_VAL_T(APCOFF, 0x000); // value at reset + ndb->d_auxdac = ABB_VAL_T(AUXDAC, 0x000); // value at reset + ndb->d_vbctrl1 = ABB_VAL_T(VBCTRL1, 0x00B); // VULSWITCH=0, VDLAUX=1, VDLEAR=1. + ndb->d_vbctrl2 = ABB_VAL_T(VBCTRL2, 0x000); // MICBIASEL=0, VDLHSO=0, MICAUX=0 + + /* APCDEL will be initialized on rach only */ + ndb->d_apcdel1 = ABB_VAL_T(APCDEL1, ((APCDEL_DOWN-2) << 5) | (APCDEL_UP-6)); + ndb->d_apcdel2 = ABB_VAL_T(APCDEL2, 0x000); + + ndb->d_fb_mode = 1; /* mode 1 FCCH burst detection */ + ndb->d_fb_det = 0; /* we have not yet detected a FB */ + ndb->a_cd[0] = (1<<B_FIRE1); /* CCCH/SACCH downlink */ + ndb->a_dd_0[0] = 0; + ndb->a_dd_0[2] = 0xffff; + ndb->a_dd_1[0] = 0; + ndb->a_dd_1[2] = 0xffff; + ndb->a_du_0[0] = 0; + ndb->a_du_0[2] = 0xffff; + ndb->a_du_1[0] = 0; + ndb->a_du_1[2] = 0xffff; + ndb->a_fd[0] = (1<<B_FIRE1); + ndb->a_fd[2] = 0xffff; + ndb->d_a5mode = 0; + ndb->d_tch_mode = 0x0800; /* Set ABB model to Iota */ + + #define GUARD_BITS 8 // 11 or 9 for TSM30, 7 for Freerunner + ndb->d_tch_mode |= (((GUARD_BITS - 4) & 0x000F) << 7); //Bit 7..10: guard bits + + ndb->a_sch26[0] = (1<<B_SCH_CRC); + + /* Interrupt RIF transmit if FIFO <= threshold with threshold == 0 */ + /* MCM = 1, XRST = 0, CLKX_AUTO=1, TXM=1, NCLK_EN=1, NCLK13_EN=1, + * THRESHOLD = 0, DIV_CLK = 0 (13MHz) */ + ndb->d_spcx_rif = 0x179; + + /* Init audio related parameters */ + dsp_audio_init(); +} + +static void dsp_db_init(void) +{ + dsp_api_memset((uint16_t *)BASE_API_W_PAGE_0, sizeof(T_DB_MCU_TO_DSP)); + dsp_api_memset((uint16_t *)BASE_API_W_PAGE_1, sizeof(T_DB_MCU_TO_DSP)); + dsp_api_memset((uint16_t *)BASE_API_R_PAGE_0, sizeof(T_DB_DSP_TO_MCU)); + dsp_api_memset((uint16_t *)BASE_API_R_PAGE_1, sizeof(T_DB_DSP_TO_MCU)); +} + +void dsp_power_on(void) +{ + /* probably a good idea to initialize the whole API area to a known value */ + dsp_api_memset((uint16_t *)BASE_API_RAM, API_SIZE * 2); // size is in words + + dsp_set_params((int16_t *)&dsp_params, sizeof(dsp_params)/2); + dsp_ndb_init(); + dsp_db_init(); + dsp_api.frame_ctr = 0; + dsp_api.r_page = dsp_api.w_page = dsp_api.r_page_used = 0; +} + +/* test for frequency burst detection */ +#define REG_INT_STAT 0xffff1004 +static void wait_for_frame_irq(void) +{ + //puts("Waiting for Frame Interrupt"); + //while (readb(REG_INT_STAT) & 1) + while (readb((void *)0xffff1000) & (1<<4)) + ;// putchar('.'); + //puts("Done!\n"); +} + +void dsp_end_scenario(void) +{ + /* FIXME: we don't yet deal with the MISC_TASK */ + + /* End the DSP Scenario */ + dsp_api.ndb->d_dsp_page = B_GSM_TASK | dsp_api.w_page; + dsp_api.w_page ^= 1; + + /* Tell TPU to generate a FRAME interrupt to the DSP */ + tpu_dsp_frameirq_enable(); + tpu_frame_irq_en(1, 1); +} + +void dsp_load_rx_task(uint16_t task, uint8_t burst_id, uint8_t tsc) +{ + dsp_api.db_w->d_task_d = task; + dsp_api.db_w->d_burst_d = burst_id; + dsp_api.db_w->d_ctrl_system |= tsc & 0x7; +} + +void dsp_load_tx_task(uint16_t task, uint8_t burst_id, uint8_t tsc) +{ + dsp_api.db_w->d_task_u = task; + dsp_api.db_w->d_burst_u = burst_id; + dsp_api.db_w->d_ctrl_system |= tsc & 0x7; +} + +/* no AMR yet */ +void dsp_load_tch_param(struct gsm_time *next_time, + uint8_t chan_mode, uint8_t chan_type, uint8_t chan_sub, + uint8_t tch_loop, uint8_t sync_tch, uint8_t tn) +{ + uint16_t d_ctrl_tch; + uint16_t fn, a5fn0, a5fn1; + + /* d_ctrl_tch + ---------- + bit [0..3] -> b_chan_mode + bit [4..7] -> b_chan_type + bit [8] -> b_sync_tch_ul + bit [9] -> b_sync_tch_dl + bit [10] -> b_stop_tch_ul + bit [11] -> b_stop_tch_dl + bit [12..14] -> b_tch_loop + bit [15] -> b_subchannel */ + d_ctrl_tch = (chan_mode << B_CHAN_MODE) | + (chan_type << B_CHAN_TYPE) | + (chan_sub << B_SUBCHANNEL) | + (sync_tch << B_SYNC_TCH_UL) | + (sync_tch << B_SYNC_TCH_DL) | + (tch_loop << B_TCH_LOOP); + + /* used for ciphering and TCH traffic */ + + /* d_fn + ---- + + for TCH_F: + bit [0..7] -> b_fn_report = (fn - (tn * 13) + 104) % 104) + bit [8..15] -> b_fn_sid = (fn % 104) + + for TCH_H: + tn_report = (tn & ~1) | subchannel + bit [0..7] -> b_fn_report = (fn - tn_report * 13) + 104) % 104) + bit [8..15] -> b_fn_sid = (fn % 104) + + for other: irrelevant + */ + + if (chan_type == TCH_F) { + fn = ((next_time->fn - (tn * 13) + 104) % 104) | + ((next_time->fn % 104) << 8); + } else if (chan_type == TCH_H) { + uint8_t tn_report = (tn & ~1) | chan_sub; + fn = ((next_time->fn - (tn_report * 13) + 104) % 104) | + ((next_time->fn % 104) << 8); + } else { + /* irrelevant */ + fn = 0; + } + + /* a_a5fn + ------ + byte[0] bit [0..4] -> T2 + byte[0] bit [5..10] -> T3 + byte[1] bit [0..10] -> T1 */ + + a5fn0 = ((uint16_t)next_time->t3 << 5) | + (uint16_t)next_time->t2; + a5fn1 = (uint16_t)next_time->t1; + + dsp_api.db_w->d_fn = fn; /* Fn_sid & Fn_report */ + dsp_api.db_w->a_a5fn[0] = a5fn0; /* ciphering FN part 1 */ + dsp_api.db_w->a_a5fn[1] = a5fn1; /* ciphering FN part 2 */ + dsp_api.db_w->d_ctrl_tch = d_ctrl_tch; /* Channel config. */ +} + +void dsp_load_ciph_param(int mode, uint8_t *key) +{ + dsp_api.ndb->d_a5mode = mode; + + if (!mode || !key) + return; + + /* key is expected in the same format as in RSL + * Encryption information IE. So we need to load the + * bytes backward in A5 unit */ + dsp_api.ndb->a_kc[0] = (uint16_t)key[7] | ((uint16_t)key[6] << 8); + dsp_api.ndb->a_kc[1] = (uint16_t)key[5] | ((uint16_t)key[4] << 8); + dsp_api.ndb->a_kc[2] = (uint16_t)key[3] | ((uint16_t)key[2] << 8); + dsp_api.ndb->a_kc[3] = (uint16_t)key[1] | ((uint16_t)key[0] << 8); +} + +#define SC_CHKSUM_VER (BASE_API_W_PAGE_0 + (2 * (0x08DB - 0x800))) +static void dsp_dump_csum(void) +{ + printf("dsp page : %u\n", dsp_api.ndb->d_dsp_page); + printf("dsp code version : 0x%04x\n", dsp_api.db_r->a_pm[0]); + printf("dsp checksum : 0x%04x\n", dsp_api.db_r->a_pm[1]); + printf("dsp patch version : 0x%04x\n", readw(SC_CHKSUM_VER)); +} + +void dsp_checksum_task(void) +{ + dsp_dump_csum(); + dsp_api.db_w->d_task_md = CHECKSUM_DSP_TASK; + dsp_api.ndb->d_fb_mode = 1; + + dsp_end_scenario(); + + wait_for_frame_irq(); + + dsp_dump_csum(); +} + +#define L1D_AUXAPC 0x0012 +#define L1D_APCRAM 0x0014 + +void dsp_load_apc_dac(uint16_t apc) +{ + dsp_api.db_w->d_power_ctl = (apc << 6) | L1D_AUXAPC; +} + + +static void _dsp_dump_range(uint32_t addr, uint32_t size, int mode) +{ + uint32_t bs; + + /* Mode selection */ + writew(mode, BASE_API_RAM); + writew(BL_CMD_COPY_MODE, BL_CMD_STATUS); + dsp_bl_wait_ready(); + + /* Block by block dump */ + while (size) { + volatile uint16_t *api = (volatile uint16_t *)BASE_API_RAM; + + bs = (size > BL_MAX_BLOCK_SIZE) ? BL_MAX_BLOCK_SIZE : size; + size -= bs; + + writew(addr >> 16, BL_ADDR_HI); + writew(addr & 0xffff, BL_ADDR_LO); + writew(bs, BL_SIZE); + writew(BL_CMD_COPY_BLOCK, BL_CMD_STATUS); + + dsp_bl_wait_ready(); + + while (bs--) { + /* FIXME workaround: small delay to prevent overflowing + * the sercomm buffer */ + delay_ms(2); + if ((addr&15)==0) + printf("%05x : ", addr); + printf("%04hx%c", *api++, ((addr&15)==15)?'\n':' '); + addr++; + } + }; + puts("\n"); +} + +void dsp_dump(void) +{ + static const struct { + const char *name; + uint32_t addr; + uint32_t size; + int mode; + } dr[] = { + { "Registers", 0x00000, 0x0060, BL_MODE_DATA_READ }, + { "DROM", 0x09000, 0x5000, BL_MODE_DROM_READ }, + { "PDROM", 0x0e000, 0x2000, BL_MODE_DROM_READ }, + { "PROM0", 0x07000, 0x7000, BL_MODE_PROM_READ }, + { "PROM1", 0x18000, 0x8000, BL_MODE_PROM_READ }, + { "PROM2", 0x28000, 0x8000, BL_MODE_PROM_READ }, + { "PROM3", 0x38000, 0x2000, BL_MODE_PROM_READ }, + { NULL, 0, 0, -1 } + }; + + int i; + + /* Start DSP up to bootloader */ + dsp_pre_boot(dsp_bootcode); + + /* Load and execute our dump code in the DSP */ + dsp_upload_sections_api(dsp_dumpcode, DSP_BASE_API); + dsp_bl_start_at(DSP_DUMPCODE_START); + + /* our dump code actually simulates the boot loader + * but with added read commands */ + dsp_bl_wait_ready(); + + /* Test the 'version' command */ + writew(0xffff, BL_CMD_STATUS); + dsp_bl_wait_ready(); + printf("DSP bootloader version 0x%04x\n", readw(BASE_API_RAM)); + + /* Dump each range */ + for (i=0; dr[i].name; i++) { + printf("DSP dump: %s [%05x-%05x]\n", dr[i].name, + dr[i].addr, dr[i].addr+dr[i].size-1); + _dsp_dump_range(dr[i].addr, dr[i].size, dr[i].mode); + } +} + diff --git a/src/target/firmware/calypso/dsp_bootcode.c b/src/target/firmware/calypso/dsp_bootcode.c new file mode 100644 index 00000000..2db46568 --- /dev/null +++ b/src/target/firmware/calypso/dsp_bootcode.c @@ -0,0 +1,9 @@ +/* Calypso integrated DSP boot code */ + +#define _SA_DECL (const uint16_t *)&(const uint16_t []) + +/* We don't really need any DSP boot code, it happily works with its own ROM */ +static const struct dsp_section *dsp_bootcode = NULL; + +#undef _SA_DECL + diff --git a/src/target/firmware/calypso/dsp_dumpcode.c b/src/target/firmware/calypso/dsp_dumpcode.c new file mode 100644 index 00000000..265a1c12 --- /dev/null +++ b/src/target/firmware/calypso/dsp_dumpcode.c @@ -0,0 +1,45 @@ +/* Generated from src/target_dsp/calypso/dsp_dump.bin */ + +#define _SA_DECL (const uint16_t *)&(const uint16_t []) + +static const struct dsp_section dsp_dumpcode[] = { + { + .addr = 0x1000, + .size = 0x005b, + .data = _SA_DECL { + 0x69f8, 0x0029, 0x0002, 0xea1f, + 0x7718, 0x1100, 0x7714, 0x0000, + 0x7712, 0x0800, 0x767f, 0x0001, + 0x607f, 0xffff, 0xf820, 0x1014, + 0xf273, 0x1008, 0x7682, 0x0100, + 0x607f, 0x0004, 0xf820, 0x101c, + 0xf273, 0x1008, 0x7214, 0x0800, + 0x607f, 0x0002, 0xf820, 0x100c, + 0x127e, 0x8813, 0x3c7c, 0x137d, + 0x8911, 0xf84c, 0x1028, 0xf4e2, + 0x7715, 0x0014, 0x963d, 0xfa30, + 0x104b, 0x6d89, 0x963f, 0xfa30, + 0x103f, 0x963e, 0xf495, 0xf830, + 0x103a, 0x47f8, 0x0011, 0x7f92, + 0xf073, 0x1008, 0x47f8, 0x0011, + 0x7e92, 0xf073, 0x1008, 0xf830, + 0x1046, 0x47f8, 0x0011, 0xe589, + 0xf073, 0x1008, 0x47f8, 0x0011, + 0xe598, 0xf073, 0x1008, 0x4911, + 0x891a, 0xf830, 0x1055, 0xf072, + 0x1052, 0xf074, 0x7213, 0xf073, + 0x1008, 0xf072, 0x1058, 0xf074, + 0xe4b8, 0xf073, 0x1008, + }, + }, + { /* Guard */ + .addr = 0, + .size = 0, + .data = NULL, + }, +}; + +#define DSP_DUMPCODE_START 0x1000 + +#undef _SA_DECL + diff --git a/src/target/firmware/calypso/dsp_params.c b/src/target/firmware/calypso/dsp_params.c new file mode 100644 index 00000000..e08b46e6 --- /dev/null +++ b/src/target/firmware/calypso/dsp_params.c @@ -0,0 +1,94 @@ +/* Values from an actual phone firmware that uses the 3306 DSP ROM code version */ +static T_PARAM_MCU_DSP dsp_params = { + .d_transfer_rate = 0x6666, + /* Latencies */ + .d_lat_mcu_bridge = 15, + .d_lat_mcu_hom2sam = 12, + .d_lat_mcu_bef_fast_access = 5, + .d_lat_dsp_after_sam = 4, + /* DSP Start Address */ + .d_gprs_install_address = 0x7002, /* needs to be set by patch or manually */ + .d_misc_config = 1, + .d_cn_sw_workaround = 0xE, + .d_hole2_param = { 0, 0, 0, 0 }, + /* Frequency Burst */ + .d_fb_margin_beg = 24, + .d_fb_margin_end = 22, + .d_nsubb_idle = 296, + .d_nsubb_dedic = 30, + .d_fb_thr_det_iacq = 0x3333, + .d_fb_thr_det_track = 0x28f6, + /* Demodulation */ + .d_dc_off_thres = 0x7fff, + .d_dummy_thres = 17408, + .d_dem_pond_gewl = 26624, + .d_dem_pond_red = 20152, + /* TCH Full Speech */ + .d_maccthresh1 = 7872, + .d_mldt = -4, + .d_maccthresh = 7872, + .d_gu = 5772, + .d_go = 7872, + .d_attmax = 53, + .d_sm = -892, + .d_b = 208, + /* V.42 bis */ + .d_v42b_switch_hyst = 16, + .d_v42b_switch_min = 64, + .d_v42b_switch_max = 250, + .d_v42b_reset_delay = 10, + /* TCH Half Speech */ + .d_ldT_hr = -5, + .d_maccthresh_hr = 6500, + .d_maccthresh1_hr = 6500, + .d_gu_hr = 2620, + .d_go_hr = 3700, + .d_b_hr = 182, + .d_sm_hr = -1608, + .d_attmax_hr = 53, + /* TCH Enhanced FR Speech */ + .c_mldt_efr = -4, + .c_maccthresh_efr = 8000, + .c_maccthresh1_efr = 8000, + .c_gu_efr = 4522, + .c_go_efr = 6500, + .c_b_efr = 174, + .c_sm_efr = -878, + .c_attmax_efr = 53, + /* CHED TCH Full Speech */ + .d_sd_min_thr_tchfs = 15, + .d_ma_min_thr_tchfs = 738, + .d_md_max_thr_tchfs = 1700, + .d_md1_max_thr_tchfs = 99, + /* CHED TCH Half Speech */ + .d_sd_min_thr_tchhs = 37, + .d_ma_min_thr_tchhs = 344, + .d_sd_av_thr_tchhs = 1845, + .d_md_max_thr_tchhs = 2175, + .d_md1_max_thr_tchhs = 138, + /* CHED TCH/F EFR Speech */ + .d_sd_min_thr_tchefs = 15, + .d_ma_min_thr_tchefs = 738, + .d_md_max_thr_tchefs = 0x4ce, + .d_md1_max_thr_tchefs = 0x63, + /* */ + .d_wed_fil_ini = 0x122a, + .d_wed_fil_tc = 0x7c00, + .d_x_min = 0xf, + .d_x_max = 0x17, + .d_slope = 0x87, + .d_y_min = 0x2bf, + .d_y_max = 0x99c, + .d_wed_diff_threshold = 0x196, + .d_mabfi_min_thr_tchhs = 0x14c8, + /* FACCH module */ + .d_facch_thr = 0, + /* IDS module */ + .d_max_ovsp_ul = 8, + .d_sync_thres = 0x3f50, + .d_idle_thres = 0x4000, + .d_m1_thres = 5, + .d_max_ovsp_dl = 8, + .d_gsm_bgd_mgt = 0, + /* we don't set the FIR coefficients !?! */ +}; diff --git a/src/target/firmware/calypso/du.c b/src/target/firmware/calypso/du.c new file mode 100644 index 00000000..58783b06 --- /dev/null +++ b/src/target/firmware/calypso/du.c @@ -0,0 +1,51 @@ +/* Calypso DU (Debug Unit) Driver */ + +/* (C) 2010 by Ingo Albrecht <prom@berlin.ccc.de> + * + * All Rights Reserved + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <memory.h> +#include <stdint.h> +#include <stdio.h> + +#include <calypso/du.h> + +#define BASE_ADDR_DU 0x03c00000 +#define DU_REG(m) (BASE_ADDR_DU+(m)) + +void calypso_du_init() { + unsigned char c; + calypso_debugunit(1); + for(c = 0; c < 64; c++) { + writew(DU_REG(c), 0x00000000); + } +} + +void calypso_du_stop() { + calypso_debugunit(0); +} + +void calypso_du_dump() { + unsigned char c; + puts("Debug unit traceback:\n"); + for(c = 0; c < 64; c++) { + uint32_t w = readw(DU_REG(c)); + printf("t-%2x: 0x%8x\n", c, (unsigned int)w); + } +} diff --git a/src/target/firmware/calypso/i2c.c b/src/target/firmware/calypso/i2c.c new file mode 100644 index 00000000..344424de --- /dev/null +++ b/src/target/firmware/calypso/i2c.c @@ -0,0 +1,123 @@ +/* Driver for I2C Master Controller inside TI Calypso */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <memory.h> +#include <i2c.h> + +#define BASE_ADDR_I2C 0xfffe2800 +#define I2C_REG(x) (BASE_ADDR_I2C+(x)) + +enum i2c_reg { + DEVICE_REG = 0, + ADDRESS_REG, + DATA_WR_REG, + DATA_RD_REG, + CMD_REG, + CONF_FIFO_REG, + CONF_CLK_REG, + CONF_CLK_FUNC_REF, + STATUS_FIFO_REG, + STATUS_ACTIVITY_REG, +}; + +#define I2C_CMD_SOFT_RESET (1 << 0) +#define I2C_CMD_EN_CLK (1 << 1) +#define I2C_CMD_START (1 << 2) +#define I2C_CMD_RW_READ (1 << 3) +#define I2C_CMD_COMP_READ (1 << 4) +#define I2C_CMD_IRQ_ENABLE (1 << 5) + +#define I2C_STATUS_ERROR_DATA (1 << 0) +#define I2C_STATUS_ERROR_DEV (1 << 1) +#define I2C_STATUS_IDLE (1 << 2) // 1: not idle, 0: idle +#define I2C_STATUS_INTERRUPT (1 << 3) + +int i2c_write(uint8_t chip, uint32_t addr, int alen, const uint8_t *buffer, int len) +{ + uint8_t cmd; + + /* Calypso I2C controller doesn't support fancy addressing */ + if (alen > 1) + return -1; + + /* FIXME: implement writes longer than fifo size */ + if (len > 16) + return -1; + + printd("i2c_write(chip=0x%02u, addr=0x%02u): ", chip, addr) + + writeb(chip & 0x3f, I2C_REG(DEVICE_REG)); + writeb(addr & 0xff, I2C_REG(ADDRESS_REG)); + + /* we have to tell the controller how many bits we'll put into the fifo ?!? */ + writeb(len-1, I2C_REG(CONF_FIFO_REG)); + + /* fill the FIFO */ + while (len--) { + uint8_t byte = *buffer++; + writeb(byte, I2C_REG(DATA_WR_REG)); + printd("%02X ", byte); + } + dputchar('\n'); + + /* start the transfer */ + cmd = readb(I2C_REG(CMD_REG)); + cmd |= I2C_CMD_START; + writeb(cmd, I2C_REG(CMD_REG)); + + /* wait until transfer completes */ + while (1) { + uint8_t reg = readb(I2C_REG(STATUS_ACTIVITY_REG)); + printd("I2C Status: 0x%02x\n", rerg & 0xf); + if (!(reg & I2C_STATUS_IDLE)) // 0: idle 1: not idle + break; + } + dputs("I2C transfer completed\n"); + + return 0; +} + +void i2c_init(int speed, int slaveadd) +{ + /* scl_out = clk_func_ref / 3, + clk_func_ref = master_clock_freq / (divisor_2 + 1) + master_clock_freq = ext_clock_freq / divisor_1 */ + /* clk_func_ref = scl_out * 3, + divisor_2 = (master_clock_freq / clk_func_ref) - 1 + divisor_1 = ext_clock_freq / master_clock_freq */ + /* for a target freq of 200kHz: + ext_clock_freq = 13MHz + clk_func_ref = 3 * 300kHZ = 600kHz + divisor_1 = 1 => master_clock_freq = ext_clock_freq = 13MHz + divisor_2 = 21 => clk_func_ref = 13MHz / (21+2) = 590.91 kHz + scl_out = clk_func_ref / 3 = 509.91 kHz / 3 = 196.97kHz */ + writeb(I2C_CMD_SOFT_RESET, I2C_REG(CMD_REG)); + + writeb(0x00, I2C_REG(CONF_CLK_REG)); + writeb(21, I2C_REG(CONF_CLK_FUNC_REF)); + + writeb(I2C_CMD_EN_CLK, I2C_REG(CMD_REG)); +} diff --git a/src/target/firmware/calypso/irq.c b/src/target/firmware/calypso/irq.c new file mode 100644 index 00000000..a3d57fbe --- /dev/null +++ b/src/target/firmware/calypso/irq.c @@ -0,0 +1,266 @@ +/* Driver for Calypso IRQ controller */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <memory.h> +#include <arm.h> +#include <calypso/irq.h> + +#define BASE_ADDR_IRQ 0xfffffa00 + +enum irq_reg { + IT_REG1 = 0x00, + IT_REG2 = 0x02, + MASK_IT_REG1 = 0x08, + MASK_IT_REG2 = 0x0a, + IRQ_NUM = 0x10, + FIQ_NUM = 0x12, + IRQ_CTRL = 0x14, +}; + +#define ILR_IRQ(x) (0x20 + (x*2)) +#define IRQ_REG(x) ((void *)BASE_ADDR_IRQ + (x)) + +#define NR_IRQS 32 + +static uint8_t default_irq_prio[] = { + [IRQ_WATCHDOG] = 0xff, + [IRQ_TIMER1] = 0xff, + [IRQ_TIMER2] = 0xff, + [IRQ_TSP_RX] = 0, + [IRQ_TPU_FRAME] = 3, + [IRQ_TPU_PAGE] = 0xff, + [IRQ_SIMCARD] = 0xff, + [IRQ_UART_MODEM] = 8, + [IRQ_KEYPAD_GPIO] = 4, + [IRQ_RTC_TIMER] = 9, + [IRQ_RTC_ALARM_I2C] = 10, + [IRQ_ULPD_GAUGING] = 2, + [IRQ_EXTERNAL] = 12, + [IRQ_SPI] = 0xff, + [IRQ_DMA] = 0xff, + [IRQ_API] = 0xff, + [IRQ_SIM_DETECT] = 0, + [IRQ_EXTERNAL_FIQ] = 7, + [IRQ_UART_IRDA] = 2, + [IRQ_ULPD_GSM_TIMER] = 1, + [IRQ_GEA] = 0xff, +}; + +static irq_handler *irq_handlers[NR_IRQS]; + +static void _irq_enable(enum irq_nr nr, int enable) +{ + uint16_t *reg = IRQ_REG(MASK_IT_REG1); + uint16_t val; + + if (nr > 15) { + reg = IRQ_REG(MASK_IT_REG2); + nr -= 16; + } + + val = readw(reg); + if (enable) + val &= ~(1 << nr); + else + val |= (1 << nr); + writew(val, reg); +} + +void irq_enable(enum irq_nr nr) +{ + _irq_enable(nr, 1); +} + +void irq_disable(enum irq_nr nr) +{ + _irq_enable(nr, 0); +} + +void irq_config(enum irq_nr nr, int fiq, int edge, int8_t prio) +{ + uint16_t val; + + if (prio == -1) + prio = default_irq_prio[nr]; + + if (prio > 31) + prio = 31; + + val = prio << 2; + if (edge) + val |= 0x02; + if (fiq) + val |= 0x01; + + writew(val, IRQ_REG(ILR_IRQ(nr))); +} + +/* Entry point for interrupts */ +void irq(void) +{ + uint8_t num, tmp; + irq_handler *handler; + +#if 1 + /* Hardware interrupt detection mode */ + num = readb(IRQ_REG(IRQ_NUM)) & 0x1f; + + printd("i%02x\n", num); + + handler = irq_handlers[num]; + + if (handler) + handler(num); +#else + /* Software interrupt detection mode */ + { + uint16_t it_reg, mask_reg; + uint32_t irqs; + + it_reg = readw(IRQ_REG(IT_REG1)); + mask_reg = readw(IRQ_REG(MASK_IT_REG1)); + irqs = it_reg & ~mask_reg; + + it_reg = readw(IRQ_REG(IT_REG2)); + mask_reg = readw(IRQ_REG(MASK_IT_REG2)); + irqs |= (it_reg & ~mask_reg) << 16; + + for (num = 0; num < 32; num++) { + if (irqs & (1 << num)) { + printd("i%d\n", num); + handler = irq_handlers[num]; + if (handler) + handler(num); + /* clear this interrupt */ + if (num < 16) + writew(~(1 << num), IRQ_REG(IT_REG1)); + else + writew(~(1 << (num-16)), IRQ_REG(IT_REG2)); + } + } + dputchar('\n'); + } +#endif + /* Start new IRQ agreement */ + tmp = readb(IRQ_REG(IRQ_CTRL)); + tmp |= 0x01; + writeb(tmp, IRQ_REG(IRQ_CTRL)); +} + +/* Entry point for FIQs */ +void fiq(void) +{ + uint8_t num, tmp; + irq_handler *handler; + + num = readb(IRQ_REG(FIQ_NUM)) & 0x1f; + if (num) { + printd("f%02x\n", num); + } + + handler = irq_handlers[num]; + + if (handler) + handler(num); + + /* Start new FIQ agreement */ + tmp = readb(IRQ_REG(IRQ_CTRL)); + tmp |= 0x02; + writeb(tmp, IRQ_REG(IRQ_CTRL)); +} + +void irq_register_handler(enum irq_nr nr, irq_handler *handler) +{ + if (nr > NR_IRQS) + return; + + irq_handlers[nr] = handler; +} + +#define BASE_ADDR_IBOOT_EXC 0x0080001C +extern uint32_t _exceptions; + +/* Install the exception handlers to where the ROM loader jumps */ +void calypso_exceptions_install(void) +{ + uint32_t *exceptions_dst = (uint32_t *) BASE_ADDR_IBOOT_EXC; + uint32_t *exceptions_src = &_exceptions; + int i; + + for (i = 0; i < 7; i++) + *exceptions_dst++ = *exceptions_src++; + +} + +static void set_default_priorities(void) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(default_irq_prio); i++) { + uint16_t val; + uint8_t prio = default_irq_prio[i]; + if (prio > 31) + prio = 31; + + val = readw(IRQ_REG(ILR_IRQ(i))); + val &= ~(0x1f << 2); + val |= prio << 2; + writew(val, IRQ_REG(ILR_IRQ(i))); + } +} + +static uint32_t irq_nest_mask; +/* mask off all interrupts that have a lower priority than irq_nr */ +static void mask_all_lower_prio_irqs(enum irq_nr irqnr) +{ + uint8_t our_prio = readb(IRQ_REG(ILR_IRQ(irqnr))) >> 2; + int i; + + for (i = 0; i < _NR_IRQ; i++) { + uint8_t prio; + + if (i == irqnr) + continue; + + prio = readb(IRQ_REG(ILR_IRQ(i))) >> 2; + if (prio >= our_prio) + irq_nest_mask |= (1 << i); + } +} + +void irq_init(void) +{ + /* set default priorities */ + set_default_priorities(); + /* mask all interrupts off */ + writew(0xffff, IRQ_REG(MASK_IT_REG1)); + writew(0xffff, IRQ_REG(MASK_IT_REG2)); + /* clear all pending interrupts */ + writew(0, IRQ_REG(IT_REG1)); + writew(0, IRQ_REG(IT_REG2)); + /* enable interrupts globally to the ARM core */ + arm_enable_interrupts(); +} diff --git a/src/target/firmware/calypso/keypad.c b/src/target/firmware/calypso/keypad.c new file mode 100644 index 00000000..fd4e0ff2 --- /dev/null +++ b/src/target/firmware/calypso/keypad.c @@ -0,0 +1,198 @@ +/* Driver for the keypad attached to the TI Calypso */ + +/* (C) 2010 by roh <roh@hyte.de> + * + * All Rights Reserved + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <defines.h> +#include <debug.h> +#include <delay.h> +#include <memory.h> +#include <keypad.h> + +#include <calypso/irq.h> +#include <abb/twl3025.h> + + +#define KBR_LATCH_REG 0xfffe480a +#define KBC_REG 0xfffe480c +#define KBD_GPIO_INT 0xfffe4816 +#define KBD_GPIO_MASKIT 0xfffe4818 + +static key_handler_t key_handler = NULL; + +void emit_key(uint8_t key, uint8_t state) +{ + printf("key=%u %s\n", key, state == PRESSED ? "pressed" : "released"); + + if (state == RELEASED) + if (key == KEY_POWER) + twl3025_power_off(); + + if(key_handler) { + key_handler(key, state); + } +} + +volatile uint32_t lastbuttons = 0; + +#define BTN_TO_KEY(name) \ + ((diff & BTN_##name) == BTN_##name) \ + { \ + key = KEY_##name; \ + diff = diff & ~BTN_##name; \ + state = (buttons & BTN_##name) ? PRESSED : RELEASED; \ + } + +void dispatch_buttons(uint32_t buttons) +{ + uint8_t state; + + if (buttons == lastbuttons) + return; + + uint32_t diff = buttons ^ lastbuttons; + uint8_t key=KEY_INV; + + while (diff != 0) + { + if BTN_TO_KEY(POWER) + else if BTN_TO_KEY(0) + else if BTN_TO_KEY(1) + else if BTN_TO_KEY(2) + else if BTN_TO_KEY(3) + else if BTN_TO_KEY(4) + else if BTN_TO_KEY(5) + else if BTN_TO_KEY(6) + else if BTN_TO_KEY(7) + else if BTN_TO_KEY(8) + else if BTN_TO_KEY(9) + else if BTN_TO_KEY(STAR) + else if BTN_TO_KEY(HASH) + else if BTN_TO_KEY(MENU) + else if BTN_TO_KEY(LEFT_SB) + else if BTN_TO_KEY(RIGHT_SB) + else if BTN_TO_KEY(UP) + else if BTN_TO_KEY(DOWN) + else if BTN_TO_KEY(LEFT) + else if BTN_TO_KEY(RIGHT) + else if BTN_TO_KEY(OK) + else + { + printf("\nunknown keycode: 0x%08x\n", diff); + break; + } + emit_key(key, state); + } + lastbuttons = buttons; +} + +static uint8_t polling = 0; +static uint8_t with_interrupts = 0; + +static void keypad_irq(__unused enum irq_nr nr) +{ + /* enable polling */ + polling = 1; + irq_disable(IRQ_KEYPAD_GPIO); +} + +void keypad_init(uint8_t interrupts) +{ + lastbuttons = 0; + polling = 0; + writew(0, KBD_GPIO_MASKIT); + writew(0, KBC_REG); + + if(interrupts) { + with_interrupts = 1; + irq_register_handler(IRQ_KEYPAD_GPIO, &keypad_irq); + irq_config(IRQ_KEYPAD_GPIO, 0, 0, 0); + irq_enable(IRQ_KEYPAD_GPIO); + } +} + +void keypad_set_handler(key_handler_t handler) +{ + key_handler = handler; +} + +void keypad_poll() +{ + static uint16_t reg; + static uint16_t col; + static uint32_t buttons = 0, debounce1 = 0, debounce2 = 0; + + if (with_interrupts && !polling) + return; + + /* start polling */ + if (polling == 1) { + writew(0x1f & ~0x1, KBC_REG); /* first col */ + col = 0; + polling = 2; + return; + } + + /* enable keypad irq after the signal settles */ + if (polling == 3) { + if(with_interrupts) { + irq_enable(IRQ_KEYPAD_GPIO); + polling = 0; + } else { + polling = 1; + } + return; + } + + reg = readw(KBR_LATCH_REG); + buttons = (buttons & ~(0x1f << (col * 5))) + | ((~reg & 0x1f) << (col * 5 )); + /* if key is released, stay in column for faster debounce */ + if ((debounce1 | debounce2) & ~buttons) { + debounce2 = debounce1; + debounce1 = buttons; + return; + } + + col++; + if (col > 4) { + col = 0; + /* if power button, ignore other states */ + if (buttons & BTN_POWER) + buttons = lastbuttons | BTN_POWER; + else if (lastbuttons & BTN_POWER) + buttons = lastbuttons & ~BTN_POWER; + dispatch_buttons(buttons); + if (buttons == 0) { + writew(0x0, KBC_REG); + polling = 3; + return; + } + } + if (col == 4) + writew(0xff, KBC_REG); + else + writew(0x1f & ~(0x1 << col ), KBC_REG); + +} + diff --git a/src/target/firmware/calypso/misc.c b/src/target/firmware/calypso/misc.c new file mode 100644 index 00000000..460cc5d5 --- /dev/null +++ b/src/target/firmware/calypso/misc.c @@ -0,0 +1,60 @@ + +#include <stdint.h> +#include <stdio.h> +#include <memory.h> + +/* dump a memory range */ +void memdump_range(unsigned int *ptr, unsigned int len) +{ + unsigned int *end = ptr + (len/4); + unsigned int *tmp; + + for (tmp = ptr; tmp < end; tmp += 8) { + int i; + printf("%08X: ", (unsigned int) tmp); + + for (i = 0; i < 8; i++) + printf("%08X %s", *(tmp+i), i == 3 ? " " : ""); + + putchar('\n'); + } +} + +#define KBIT 1024 +#define MBIT (1024*KBIT) +void dump_mem(void) +{ + puts("Dump 64kBits of internal ROM\n"); + memdump_range((void *)0x03800000, 64*KBIT/8); + + puts("Dump 8Mbits of external flash\n"); + memdump_range((void *)0x00000000, 8*MBIT/8); + + puts("Dump 2Mbits of internal RAM\n"); + memdump_range((void *)0x00800000, 2*MBIT/8); + + puts("Dump 2Mbits of external RAM\n"); + memdump_range((void *)0x01000000, 2*MBIT/8); +} + +#define REG_DEV_ID_CODE 0xfffef000 +#define REG_DEV_VER_CODE 0xfffef002 +#define REG_DEV_ARMVER_CODE 0xfffffe00 +#define REG_cDSP_ID_CODE 0xfffffe02 +#define REG_DIE_ID_CODE 0xfffef010 + +void dump_dev_id(void) +{ + int i; + + printf("Device ID code: 0x%04x\n", readw(REG_DEV_ID_CODE)); + printf("Device Version code: 0x%04x\n", readw(REG_DEV_VER_CODE)); + printf("ARM ID code: 0x%04x\n", readw(REG_DEV_ARMVER_CODE)); + printf("cDSP ID code: 0x%04x\n", readw(REG_cDSP_ID_CODE)); + puts("Die ID code: "); + for (i = 0; i < 64/8; i += 4) + printf("%08x", readl(REG_DIE_ID_CODE+i)); + putchar('\n'); +} + + diff --git a/src/target/firmware/calypso/rtc.c b/src/target/firmware/calypso/rtc.c new file mode 100644 index 00000000..ce750c29 --- /dev/null +++ b/src/target/firmware/calypso/rtc.c @@ -0,0 +1,83 @@ +/* Driver for Calypso RTC controller */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <defines.h> +#include <debug.h> +#include <memory.h> +#include <display.h> +#include <calypso/irq.h> + +#define BASE_ADDR_RTC 0xfffe1800 +#define RTC_REG(x) ((void *)BASE_ADDR_RTC + (x)) + +enum rtc_reg { + SECOND_REG = 0x00, + MINUTES_REG = 0x01, + HOURS_REG = 0x02, + DAYS_REG = 0x03, + MONTHS_REG = 0x04, + YEARS_REG = 0x05, + WEEK_REG = 0x06, + /* reserved */ + ALARM_SECOND_REG = 0x08, + ALARM_MINUTES_REG = 0x09, + ALARM_HOURS_REG = 0x0a, + ALARM_DAYS_REG = 0x0b, + ALARM_MONTHS_REG = 0x0c, + ALARM_YEARS_REG = 0x0d, + /* reserved */ + /* reserved */ + CTRL_REG = 0x10, + STATUS_REG = 0x11, + INT_REG = 0x12, + COMP_LSB_REG = 0x13, + COMP_MSB_REG = 0x14, + RES_PROG_REG = 0x15, +}; + +static int tick_ctr; + +static void rtc_irq_tick(__unused enum irq_nr nr) +{ + if (tick_ctr & 1) + display_set_attr(DISP_ATTR_INVERT); + else + display_unset_attr(DISP_ATTR_INVERT); + tick_ctr++; +} + +void rtc_init(void) +{ + irq_register_handler(IRQ_RTC_TIMER, &rtc_irq_tick); + irq_config(IRQ_RTC_TIMER, 0, 1, 0); + irq_enable(IRQ_RTC_TIMER); + + /* clear power-up reset */ + writeb(0x80, RTC_REG(STATUS_REG)); + /* enable RTC running */ + writeb(0x01, RTC_REG(CTRL_REG)); + /* enable periodic interrupts every second */ + writeb(0x04, RTC_REG(INT_REG)); +} diff --git a/src/target/firmware/calypso/sim.c b/src/target/firmware/calypso/sim.c new file mode 100755 index 00000000..a539cf8a --- /dev/null +++ b/src/target/firmware/calypso/sim.c @@ -0,0 +1,740 @@ +/* Driver for Simcard Controller inside TI Calypso/Iota */ + +/* (C) 2010 by Philipp Fabian Benedikt Maier <philipp-maier@runningserver.com> + * + * All Rights Reserved + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <memory.h> +#include <abb/twl3025.h> +#include <calypso/sim.h> +#include <calypso/irq.h> + +static int sim_rx_character_count = 0; /* How many bytes have been received by calypso_sim_receive() */ +static int sim_tx_character_count = 0; /* How many bytes have been transmitted by calypso_sim_transmit() */ +static int sim_tx_character_length = 0; /* How many bytes have to be transmitted by calypso_sim_transmit() */ +static uint8_t *rx_buffer = 0; /* RX-Buffer that is issued by calypso_sim_receive() */ +static uint8_t *tx_buffer = 0; /* TX-Buffer that is issued by calypso_sim_transmit() */ +volatile static int rxDoneFlag = 0; /* Used for rx synchronization instead of a semaphore in calypso_sim_receive() */ +volatile static int txDoneFlag = 0; /* Used for rx synchronization instead of a semaphore in calypso_sim_transmit() */ + +/* Display Register dump */ +void calypso_sim_regdump(void) +{ +#if (SIM_DEBUG == 1) + unsigned int regVal; + + + puts("\n\n\n"); + puts("====================== CALYPSO SIM REGISTER DUMP =====================\n"); + puts("Reg_sim_cmd register (R/W) - FFFE:0000\n"); + + + regVal = readw(REG_SIM_CMD); + printf(" |-REG_SIM_CMD = %04x\n", readw(REG_SIM_CMD)); + + if(regVal & REG_SIM_CMD_CMDCARDRST) + puts(" | |-REG_SIM_CMD_CMDCARDRST = 1 ==> SIM card reset sequence enabled.\n"); + else + puts(" | |-REG_SIM_CMD_CMDCARDRST = 0 ==> SIM card reset sequence disabled.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CMD_CMDIFRST) + puts(" | |-REG_SIM_CMD_CMDIFRST = 1\n"); + else + puts(" | |-REG_SIM_CMD_CMDIFRST = 0\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CMD_CMDSTOP) + puts(" | |-REG_SIM_CMD_CMDSTOP = 1\n"); + else + puts(" | |-REG_SIM_CMD_CMDSTOP = 0\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CMD_CMDSTART) + puts(" | |-REG_SIM_CMD_CMDSTART = 1 ==> SIM card start procedure active.\n"); + else + puts(" | |-REG_SIM_CMD_CMDSTART = 0\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CMD_CMDSTART) + puts(" | |-REG_SIM_CMD_MODULE_CLK_EN = 1 ==> Clock of the module enabled.\n"); + else + puts(" | |-REG_SIM_CMD_MODULE_CLK_EN = 0 ==> Clock of the module disabled.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + regVal = readw(REG_SIM_STAT); + printf(" |-REG_SIM_STAT = %04x\n", regVal); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_STAT_STATNOCARD) + puts(" | |-REG_SIM_STAT_STATNOCARD = 1 ==> No card!\n"); + else + puts(" | |-REG_SIM_STAT_STATNOCARD = 0 ==> Card detected!\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_STAT_STATTXPAR) + puts(" | |-REG_SIM_STAT_STATTXPAR = 1 ==> Parity ok!\n"); + else + puts(" | |-REG_SIM_STAT_STATTXPAR = 0 ==> Parity error!\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_STAT_STATFIFOFULL) + puts(" | |-REG_SIM_STAT_STATFIFOFULL = 1 ==> Fifo full!\n"); + else + puts(" | |-REG_SIM_STAT_STATFIFOFULL = 0\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_STAT_STATFIFOEMPTY) + puts(" | |-REG_SIM_STAT_STATFIFOEMPTY = 1 ==> Fifo empty!\n"); + else + puts(" | |-REG_SIM_STAT_STATFIFOEMPTY = 0\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + regVal = readw(REG_SIM_CONF1); + printf(" |-REG_SIM_CONF1 = %04x\n", regVal); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CONF1_CONFCHKPAR) + puts(" | |-REG_SIM_CONF1_CONFCHKPAR = 1 ==> Parity check on reception enabled.\n"); + else + puts(" | |-REG_SIM_CONF1_CONFCHKPAR = 0 ==> Parity check on reception disabled.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CONF1_CONFCODCONV) + puts(" | |-REG_SIM_CONF1_CONFCODCONV = 1 ==> Coding convention is inverse.\n"); + else + puts(" | |-REG_SIM_CONF1_CONFCODCONV = 0 ==> Coding convention is direct (normal).\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CONF1_CONFTXRX) + puts(" | |-REG_SIM_CONF1_CONFTXRX = 1 ==> SIO line direction is in transmit mode.\n"); + else + puts(" | |-REG_SIM_CONF1_CONFTXRX = 0 ==> SIO line direction is in receive mode.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CONF1_CONFSCLKEN) + puts(" | |-REG_SIM_CONF1_CONFSCLKEN = 1 ==> SIM clock in normal mode.\n"); + else + puts(" | |-REG_SIM_CONF1_CONFSCLKEN = 0 ==> SIM clock in standby mode.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CONF1_reserved) + puts(" | |-REG_SIM_CONF1_reserved = 1 ==> ETU period is 4*1/Fsclk.\n"); + else + puts(" | |-REG_SIM_CONF1_reserved = 0 ==> ETU period is CONFETUPERIOD.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CONF1_CONFSCLKDIV) + puts(" | |-REG_SIM_CONF1_CONFSCLKDIV = 1 ==> SIM clock frequency is 13/8 Mhz.\n"); + else + puts(" | |-REG_SIM_CONF1_CONFSCLKDIV = 0 ==> SIM clock frequency is 13/4 Mhz.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CONF1_CONFSCLKLEV) + puts(" | |-REG_SIM_CONF1_CONFSCLKLEV = 1 ==> SIM clock idle level is high.\n"); + else + puts(" | |-REG_SIM_CONF1_CONFSCLKLEV = 0 ==> SIM clock idle level is low.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CONF1_CONFETUPERIOD) + puts(" | |-REG_SIM_CONF1_CONFETUPERIOD = 1 ==> ETU period is 512/8*1/Fsclk.\n"); + else + puts(" | |-REG_SIM_CONF1_CONFETUPERIOD = 0 ==> ETU period is 372/8*1/Fsclk.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CONF1_CONFBYPASS) + puts(" | |-REG_SIM_CONF1_CONFBYPASS = 1 ==> Hardware timers and start and stop sequences are bypassed.\n"); + else + puts(" | |-REG_SIM_CONF1_CONFBYPASS = 0 ==> Hardware timers and start and stop sequences are normal.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CONF1_CONFSVCCLEV) + puts(" | |-REG_SIM_CONF1_CONFSVCCLEV = 1 ==> SVCC Level is high (Only valid when CONFBYPASS = 1).\n"); + else + puts(" | |-REG_SIM_CONF1_CONFSVCCLEV = 0 ==> SVCC Level is low (Only valid when CONFBYPASS = 1).\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CONF1_CONFSRSTLEV) + puts(" | |-REG_SIM_CONF1_CONFSRSTLEV = 1 ==> SRST Level is high (Only valid when CONFBYPASS = 1).\n"); + else + puts(" | |-REG_SIM_CONF1_CONFSRSTLEV = 0 ==> SRST Level is low (Only valid when CONFBYPASS = 1).\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + printf(" | |-REG_SIM_CONF1_CONFTRIG = 0x%x (FIFO trigger level)\n",(regVal >> REG_SIM_CONF1_CONFTRIG) & REG_SIM_CONF1_CONFTRIG_MASK); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CONF1_CONFSIOLOW) + puts(" | |-REG_SIM_CONF1_CONFSIOLOW = 1 ==> I/O is forced to low.\n"); + else + puts(" | |-REG_SIM_CONF1_CONFSIOLOW = 0\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + regVal = readw(REG_SIM_CONF2); + printf(" |-REG_SIM_CONF2 = %04x\n", regVal); + printf(" | |-REG_SIM_CONF2_CONFTFSIM = 0x%x (time delay for filtering of SIM_CD)\n",(regVal >> REG_SIM_CONF2_CONFTFSIM) & REG_SIM_CONF2_CONFTFSIM_MASK); + printf(" | |-REG_SIM_CONF2_CONFTDSIM = 0x%x (time delay for contact activation/deactivation)\n",(regVal >> REG_SIM_CONF2_CONFTDSIM) & REG_SIM_CONF2_CONFTDSIM_MASK); + printf(" | |-REG_SIM_CONF2_CONFWAITI = 0x%x (CONFWAITI overflow wait time between two received chars)\n",(regVal >> REG_SIM_CONF2_CONFWAITI) & REG_SIM_CONF2_CONFWAITI_MASK); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + regVal = readw(REG_SIM_IT); + printf(" |-REG_SIM_IT = %04x\n", regVal); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_IT_SIM_NATR) + puts(" | |-REG_SIM_IT_SIM_NATR = 1 ==> No answer to reset!\n"); + else + puts(" | |-REG_SIM_IT_SIM_NATR = 0 ==> On read access to REG_SIM_IT.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_IT_SIM_WT) + puts(" | |-REG_SIM_IT_SIM_WT = 1 ==> Character underflow!\n"); + else + puts(" | |-REG_SIM_IT_SIM_WT = 0 ==> On read access to REG_SIM_IT.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_IT_SIM_OV) + puts(" | |-REG_SIM_IT_SIM_OV = 1 ==> Receive overflow!\n"); + else + puts(" | |-REG_SIM_IT_SIM_OV = 0 ==> On read access to REG_SIM_IT.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_IT_SIM_TX) + puts(" | |-REG_SIM_IT_SIM_TX = 1 ==> Waiting for character to transmit...\n"); + else + { + puts(" | |-REG_SIM_IT_SIM_TX = 0 ==> On write access to REG_SIM_DTX or on switching\n"); + puts(" | | from transmit to receive mode (CONFTXRX bit)\n"); + } + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_IT_SIM_RX) + puts(" | |-REG_SIM_IT_SIM_RX = 1 ==> Waiting characters to be read...\n"); + else + puts(" | |-REG_SIM_IT_SIM_RX = 0 ==> On read access to REG_SIM_DRX.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + regVal = readw(REG_SIM_DRX); + printf(" |-REG_SIM_DRX = %04x\n", regVal); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + printf(" | |-REG_SIM_DRX_SIM_DRX = 0x%x (next data byte in FIFO available for reading)\n",(regVal >> REG_SIM_DRX_SIM_DRX) & REG_SIM_DRX_SIM_DRX_MASK); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_DRX_STATRXPAR) + puts(" | |-REG_SIM_DRX_STATRXPAR = 1 ==> Parity Ok.\n"); + else + puts(" | |-REG_SIM_DRX_STATRXPAR = 0 ==> Parity error!\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + regVal = readw(REG_SIM_DTX); + printf(" |-REG_SIM_DTX = %02x (next data byte to be transmitted)\n", regVal); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + regVal = readw(REG_SIM_MASKIT); + printf(" |-REG_SIM_MASKIT = %04x\n", regVal); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_MASKIT_MASK_SIM_NATR) + puts(" | |-REG_SIM_MASKIT_MASK_SIM_NATR = 1 ==> No-answer-to-reset interrupt is masked.\n"); + else + puts(" | |-REG_SIM_MASKIT_MASK_SIM_NATR = 0 ==> No-answer-to-reset interrupt is unmasked.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_MASKIT_MASK_SIM_WT) + puts(" | |-REG_SIM_MASKIT_MASK_SIM_WT = 1 ==> Character wait-time overflow interrupt is masked.\n"); + else + puts(" | |-REG_SIM_MASKIT_MASK_SIM_WT = 0 ==> Character wait-time overflow interrupt is unmasked.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_MASKIT_MASK_SIM_OV) + puts(" | |-REG_SIM_MASKIT_MASK_SIM_OV = 1 ==> Receive overflow interrupt is masked.\n"); + else + puts(" | |-REG_SIM_MASKIT_MASK_SIM_OV = 0 ==> Receive overflow interrupt is unmasked.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_MASKIT_MASK_SIM_TX) + puts(" | |-REG_SIM_MASKIT_MASK_SIM_TX = 1 ==> Waiting characters to be transmit interrupt is masked.\n"); + else + puts(" | |-REG_SIM_MASKIT_MASK_SIM_TX = 0 ==> Waiting characters to be transmit interrupt is unmasked.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_MASKIT_MASK_SIM_RX) + puts(" | |-REG_SIM_MASKIT_MASK_SIM_RX = 1 ==> Waiting characters to be read interrupt is masked.\n"); + else + puts(" | |-REG_SIM_MASKIT_MASK_SIM_RX = 0 ==> Waiting characters to be read interrupt is unmasked.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_MASKIT_MASK_SIM_CD) + puts(" | |-REG_SIM_MASKIT_MASK_SIM_CD = 1 ==> SIM card insertion/extraction interrupt is masked.\n"); + else + puts(" | |-REG_SIM_MASKIT_MASK_SIM_CD = 0 ==> SIM card insertion/extraction interrupt is unmasked.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + regVal = REG_SIM_IT_CD; + printf(" |-REG_SIM_IT_CD = %04x\n", regVal); + if(regVal & REG_SIM_IT_CD_IT_CD) + puts(" |-REG_SIM_IT_CD_IT_CD = 1 ==> SIM card insertion/extraction interrupt is masked.\n"); + else + puts(" |-REG_SIM_IT_CD_IT_CD = 0 ==> SIM card insertion/extraction interrupt is unmasked.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); +#endif + return; +} + +/* Apply power to the simcard (use nullpointer to ignore atr) */ +int calypso_sim_powerup(uint8_t *atr) +{ + /* Enable level shifters and voltage regulator */ + twl3025_reg_write(VRPCSIM, VRPCSIM_SIMLEN | VRPCSIM_RSIMEN | VRPCSIM_SIMSEL); +#if (SIM_DEBUG == 1) + puts(" * Power enabled!\n"); +#endif + delay_ms(SIM_OPERATION_DELAY); + + /* Enable clock */ + writew(REG_SIM_CMD_MODULE_CLK_EN | REG_SIM_CMD_CMDSTART, REG_SIM_CMD); +#if (SIM_DEBUG == 1) + puts(" * Clock enabled!\n"); +#endif + delay_ms(SIM_OPERATION_DELAY); + + /* Release reset */ + writew(readw(REG_SIM_CONF1) | REG_SIM_CONF1_CONFBYPASS | REG_SIM_CONF1_CONFSRSTLEV | REG_SIM_CONF1_CONFSVCCLEV, REG_SIM_CONF1); +#if (SIM_DEBUG == 1) + puts(" * Reset released!\n"); +#endif + + /* Catch ATR */ + if(atr != 0) + return calypso_sim_receive(atr); + else + return 0; +} + + +/* Powerdown simcard */ +void calypso_sim_powerdown(void) +{ + writew(readw(REG_SIM_CONF1) & ~REG_SIM_CONF1_CONFBYPASS, REG_SIM_CONF1); +#if (SIM_DEBUG == 1) + puts(" * Reset pulled down!\n"); +#endif + delay_ms(SIM_OPERATION_DELAY); + + writew(REG_SIM_CMD_MODULE_CLK_EN | REG_SIM_CMD_CMDSTOP, REG_SIM_CMD); +#if (SIM_DEBUG == 1) + puts(" * Clock disabled!\n"); +#endif + delay_ms(SIM_OPERATION_DELAY); + + writew(0, REG_SIM_CMD); +#if (SIM_DEBUG == 1) + puts(" * Module disabled!\n"); +#endif + delay_ms(SIM_OPERATION_DELAY); + + /* Disable level shifters and voltage regulator */ + twl3025_reg_write(VRPCSIM, 0); +#if (SIM_DEBUG == 1) + puts(" * Power disabled!\n"); +#endif + delay_ms(SIM_OPERATION_DELAY); + + return; +} + +/* reset the simcard (see note 1) */ +int calypso_sim_reset(uint8_t *atr) +{ + + /* Pull reset down */ + writew(readw(REG_SIM_CONF1) & ~REG_SIM_CONF1_CONFSRSTLEV , REG_SIM_CONF1); +#if (SIM_DEBUG == 1) + puts(" * Reset pulled down!\n"); +#endif + + delay_ms(SIM_OPERATION_DELAY); + + /* Pull reset down */ + writew(readw(REG_SIM_CONF1) | REG_SIM_CONF1_CONFSRSTLEV , REG_SIM_CONF1); +#if (SIM_DEBUG == 1) + puts(" * Reset released!\n"); +#endif + + /* Catch ATR */ + if(atr != 0) + return calypso_sim_receive(atr); + else + return 0; +} + +/* Receive raw data through the sim interface */ +int calypso_sim_receive(uint8_t *data) +{ + /* Prepare buffers and flags */ + rx_buffer = data; + sim_rx_character_count = 0; + rxDoneFlag = 0; + + /* Switch I/O direction to input */ + writew(readw(REG_SIM_CONF1) & ~REG_SIM_CONF1_CONFTXRX, REG_SIM_CONF1); + + /* Unmask the interrupts that are needed to perform this action */ + writew(~(REG_SIM_MASKIT_MASK_SIM_RX | REG_SIM_MASKIT_MASK_SIM_WT), REG_SIM_MASKIT); + + /* Wait till rxDoneFlag is set */ + while(rxDoneFlag == 0); + + /* Disable all interrupt driven functions by masking all interrupts */ + writew(0xFF, REG_SIM_MASKIT); + + /* Hand back the number of bytes received */ + return sim_rx_character_count; + + return; +} + +/* Transmit raw data through the sim interface */ +int calypso_sim_transmit(uint8_t *data, int length) +{ + /* Prepare buffers and flags */ + tx_buffer = data; + sim_tx_character_count = 0; + txDoneFlag = 0; + sim_tx_character_length = length; + + /* Switch I/O direction to output */ + writew(readw(REG_SIM_CONF1) | REG_SIM_CONF1_CONFTXRX, REG_SIM_CONF1); + + /* Unmask the interrupts that are needed to perform this action */ + writew(~(REG_SIM_MASKIT_MASK_SIM_TX), REG_SIM_MASKIT); + + /* Transmit the first byte manually to start the interrupt cascade */ + writew(*tx_buffer,REG_SIM_DTX); + tx_buffer++; + sim_tx_character_count++; + + /* Wait till rxDoneFlag is set */ + while(txDoneFlag == 0); + + /* Disable all interrupt driven functions by masking all interrupts */ + writew(0xFF, REG_SIM_MASKIT); + + return 0; +} + + +/* IRQ-Handler for simcard interface */ +void sim_irq_handler(enum irq_nr irq) +{ + int regVal = readw(REG_SIM_IT); + + + /* Display interrupt information */ +#if (SIM_DEBUG == 1) + puts("SIM-ISR: Interrupt caught: "); +#endif + if(regVal & REG_SIM_IT_SIM_NATR) + { +#if (SIM_DEBUG == 1) + puts(" No answer to reset!\n"); +#endif + } + + /* Used by: calypso_sim_receive() to determine when the transmission is over */ + if(regVal & REG_SIM_IT_SIM_WT) + { +#if (SIM_DEBUG == 1) + puts(" Character underflow!\n"); +#endif + rxDoneFlag = 1; + } + + if(regVal & REG_SIM_IT_SIM_OV) + { +#if (SIM_DEBUG == 1) + puts(" Receive overflow!\n"); +#endif + } + + /* Used by: calypso_sim_transmit() to transmit the data */ + if(regVal & REG_SIM_IT_SIM_TX) + { +#if (SIM_DEBUG == 1) + puts(" Waiting for character to transmit...\n"); +#endif + if(sim_tx_character_count >= sim_tx_character_length) + txDoneFlag = 1; + else + { + writew(*tx_buffer,REG_SIM_DTX); + tx_buffer++; + sim_tx_character_count++; + } + } + + /* Used by: calypso_sim_receive() to receive the incoming data */ + if(regVal & REG_SIM_IT_SIM_RX) + { +#if (SIM_DEBUG == 1) + puts(" Waiting characters to be read...\n"); +#endif + /* Increment character count - this is what calypso_sim_receive() hands back */ + sim_rx_character_count++; + + /* Read byte from rx-fifo and write it to the issued buffer */ + *rx_buffer = (uint8_t) (readw(REG_SIM_DRX) & 0xFF); + rx_buffer++; + } +} + +/* Transceive T0 Apdu to sim acording to GSM 11.11 Page 34 */ +int calypso_sim_transceive(uint8_t cla, /* Class (in GSM context mostly 0xA0 */ + uint8_t ins, /* Instruction */ + uint8_t p1, /* First parameter */ + uint8_t p2, /* Second parameter */ + uint8_t p3le, /* Length of the data that should be transceived */ + uint8_t *data, /* Data payload */ + uint8_t *status, /* Status word (2 byte array, see note 1) */ + uint8_t mode) /* Mode of operation: 1=GET, 0=PUT */ + + /* Note 1: You can use a null-pointer (0) if you are not interested in + the status word */ +{ + uint8_t transmissionBuffer[256]; + uint8_t numberOfReceivedBytes; + +#if (SIM_DEBUG == 1) + printf("SIM-T0: Transceiving APDU-Header: (%02x %02x %02x %02x %02x)\n",cla,ins,p1,p2,p3le); +#endif + + /* Transmit APDU header */ + memset(transmissionBuffer,0,sizeof(transmissionBuffer)); + transmissionBuffer[0] = cla; + transmissionBuffer[1] = ins; + transmissionBuffer[2] = p1; + transmissionBuffer[3] = p2; + transmissionBuffer[4] = p3le; + calypso_sim_transmit(transmissionBuffer,5); + + /* Case 1: No input, No Output */ + if(p3le == 0) + { +#if (SIM_DEBUG == 1) + puts("SIM-T0: Case 1: No input, No Output (See also GSM 11.11 Page 34)\n"); +#endif + numberOfReceivedBytes = calypso_sim_receive(transmissionBuffer); + + if(numberOfReceivedBytes == 2) + { +#if (SIM_DEBUG == 1) + printf("SIM-T0: Status-word received: %02x %02x\n", transmissionBuffer[0], transmissionBuffer[1]); +#endif + /* Hand back status word */ + if(status != 0) + { + status[0] = transmissionBuffer[0]; + status[1] = transmissionBuffer[1]; + } + + return 0; + } + else + { +#if (SIM_DEBUG == 1) + puts("SIM-T0: T0 Protocol error -- aborting!\n"); +#endif + return -1; + } + } + + /* Case 2: No input / Output of known length */ + else if(mode == SIM_APDU_PUT) + { +#if (SIM_DEBUG == 1) + puts("SIM-T0: Case 2: No input / Output of known length (See also GSM 11.11 Page 34)\n"); +#endif + + numberOfReceivedBytes = calypso_sim_receive(transmissionBuffer); + + /* Error situation: The card has aborted, sends no data but a status word */ + if(numberOfReceivedBytes == 2) + { +#if (SIM_DEBUG == 1) + printf("SIM-T0: Status-word received (ERROR): %02x %02x\n", transmissionBuffer[0], transmissionBuffer[1]); +#endif + /* Hand back status word */ + if(status != 0) + { + status[0] = transmissionBuffer[0]; + status[1] = transmissionBuffer[1]; + } + + return 0; + } + /* Acknoledge byte received */ + else if(numberOfReceivedBytes == 1) + { +#if (SIM_DEBUG == 1) + printf("SIM-T0: ACK received: %02x\n", transmissionBuffer[0]); +#endif + /* Check if ACK is valid */ + if(transmissionBuffer[0] != ins) + { +#if (SIM_DEBUG == 1) + puts("SIM-T0: T0 Protocol error: Invalid ACK byte -- aborting!\n"); +#endif + return -1; + } + + /* Transmit body */ + calypso_sim_transmit(data,p3le); + + /* Receive status word */ + numberOfReceivedBytes = calypso_sim_receive(transmissionBuffer); + + /* Check status word */ + if(numberOfReceivedBytes == 2) + { +#if (SIM_DEBUG == 1) + printf("SIM-T0: Status-word received: %02x %02x\n", transmissionBuffer[0], transmissionBuffer[1]); +#endif + + /* Hand back status word */ + if(status != 0) + { + status[0] = transmissionBuffer[0]; + status[1] = transmissionBuffer[1]; + } + + return 0; + } + else + { +#if (SIM_DEBUG == 1) + puts("SIM-T0: T0 Protocol error: Missing or invalid status word -- aborting!\n"); +#endif + return -1; + } + } + else + { +#if (SIM_DEBUG == 1) + puts("SIM-T0: T0 Protocol error: Missing ACK byte -- aborting!\n"); +#endif + return -1; + } + } + + /* Case 4: Input / No output */ + else if(mode == SIM_APDU_GET) + { +#if (SIM_DEBUG == 1) + puts("SIM-T0: Case 4: Input / No output (See also GSM 11.11 Page 34)\n"); +#endif + + numberOfReceivedBytes = calypso_sim_receive(data); + + /* Error situation: The card has aborted, sends no data but a status word */ + if(numberOfReceivedBytes == 2) + { + +#if (SIM_DEBUG == 1) + printf("SIM-T0: Status-word received (ERROR): %02x %02x\n", data[0], data[1]); +#endif + /* Hand back status word */ + if(status != 0) + { + status[0] = data[0]; + status[1] = data[1]; + } + + return 0; + } + + /* Data correctly received */ + else if(numberOfReceivedBytes == p3le + 1 + 2) + { +#if (SIM_DEBUG == 1) + printf("SIM-T0: ACK received: %02x\n", data[0]); +#endif + /* Check if ACK is valid */ + if(data[0] != ins) + { +#if (SIM_DEBUG == 1) + puts("SIM-T0: T0 Protocol error: Invalid ACK byte -- aborting!\n"); +#endif + return -1; + } + +#if (SIM_DEBUG == 1) + printf("SIM-T0: Status-word received: %02x %02x\n", data[p3le + 1], data[p3le + 2]); +#endif + /* Hand back status word */ + if(status != 0) + { + status[0] = data[p3le + 1]; + status[1] = data[p3le + 2]; + } + + /* Move data one position left to cut away the ACK-Byte */ + memcpy(data,data+1,p3le); + + return 0; + } + else + { +#if (SIM_DEBUG == 1) + puts("SIM-T0: T0 Protocol error: Incorrect or missing answer -- aborting!\n"); +#endif + return -1; + } + } + + /* Should not happen, if it happens then the programmer has submitted invalid parameters! */ + else + { +#if (SIM_DEBUG == 1) + puts("SIM-T0: T0 Protocol error: Invalid case (program bug!) -- aborting!\n"); +#endif + } + + /* Note: The other cases are not implemented because they are already covered + by the CASE 1,2 and 4. */ + + return 0; +} + + +/* Initialize simcard interface */ +void calypso_sim_init(void) +{ + /* Register IRQ handler and turn interrupts on */ +#if (SIM_DEBUG == 1) + puts("SIM: Registering interrupt handler for simcard-interface\n"); +#endif + irq_register_handler(IRQ_SIMCARD, &sim_irq_handler); + irq_config(IRQ_SIMCARD, 0, 0, 0xff); + irq_enable(IRQ_SIMCARD); +} + diff --git a/src/target/firmware/calypso/spi.c b/src/target/firmware/calypso/spi.c new file mode 100644 index 00000000..049ac080 --- /dev/null +++ b/src/target/firmware/calypso/spi.c @@ -0,0 +1,141 @@ +/* Driver for SPI Master Controller inside TI Calypso */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +//#define DEBUG +#include <debug.h> + +#include <memory.h> +#include <spi.h> +#include <delay.h> + +#define BASE_ADDR_SPI 0xfffe3000 +#define SPI_REG(n) (BASE_ADDR_SPI+(n)) + +enum spi_regs { + REG_SET1 = 0x00, + REG_SET2 = 0x02, + REG_CTRL = 0x04, + REG_STATUS = 0x06, + REG_TX_LSB = 0x08, + REG_TX_MSB = 0x0a, + REG_RX_LSB = 0x0c, + REG_RX_MSB = 0x0e, +}; + +#define SPI_SET1_EN_CLK (1 << 0) +#define SPI_SET1_WR_IRQ_DIS (1 << 4) +#define SPI_SET1_RDWR_IRQ_DIS (1 << 5) + +#define SPI_CTRL_RDWR (1 << 0) +#define SPI_CTRL_WR (1 << 1) +#define SPI_CTRL_NB_SHIFT 2 +#define SPI_CTRL_AD_SHIFT 7 + +#define SPI_STATUS_RE (1 << 0) /* Read End */ +#define SPI_STATUS_WE (1 << 1) /* Write End */ + +void spi_init(void) +{ + writew(SPI_SET1_EN_CLK | SPI_SET1_WR_IRQ_DIS | SPI_SET1_RDWR_IRQ_DIS, + SPI_REG(REG_SET1)); + + writew(0x0001, SPI_REG(REG_SET2)); +} + +int spi_xfer(uint8_t dev_idx, uint8_t bitlen, const void *dout, void *din) +{ + uint8_t bytes_per_xfer; + uint8_t reg_status, reg_ctrl = 0; + uint32_t tmp; + + if (bitlen == 0) + return 0; + + if (bitlen > 32) + return -1; + + if (dev_idx > 4) + return -1; + + bytes_per_xfer = bitlen / 8; + if (bitlen % 8) + bytes_per_xfer ++; + + reg_ctrl |= (bitlen - 1) << SPI_CTRL_NB_SHIFT; + reg_ctrl |= (dev_idx & 0x7) << SPI_CTRL_AD_SHIFT; + + if (bitlen <= 8) { + tmp = *(uint8_t *)dout; + tmp <<= 24 + (8-bitlen); /* align to MSB */ + } else if (bitlen <= 16) { + tmp = *(uint16_t *)dout; + tmp <<= 16 + (16-bitlen); /* align to MSB */ + } else { + tmp = *(uint32_t *)dout; + tmp <<= (32-bitlen); /* align to MSB */ + } + printd("spi_xfer(dev_idx=%u, bitlen=%u, data_out=0x%08x): ", + dev_idx, bitlen, tmp); + + /* fill transmit registers */ + writew(tmp >> 16, SPI_REG(REG_TX_MSB)); + writew(tmp & 0xffff, SPI_REG(REG_TX_LSB)); + + /* initiate transfer */ + if (din) + reg_ctrl |= SPI_CTRL_RDWR; + else + reg_ctrl |= SPI_CTRL_WR; + writew(reg_ctrl, SPI_REG(REG_CTRL)); + printd("reg_ctrl=0x%04x ", reg_ctrl); + + /* wait until the transfer is complete */ + while (1) { + reg_status = readw(SPI_REG(REG_STATUS)); + printd("status=0x%04x ", reg_status); + if (din && (reg_status & SPI_STATUS_RE)) + break; + else if (reg_status & SPI_STATUS_WE) + break; + } + /* FIXME: calibrate how much delay we really need (seven 13MHz cycles) */ + delay_ms(1); + + if (din) { + tmp = readw(SPI_REG(REG_RX_MSB)) << 16; + tmp |= readw(SPI_REG(REG_RX_LSB)); + printd("data_in=0x%08x ", tmp); + + if (bitlen <= 8) + *(uint8_t *)din = tmp & 0xff; + else if (bitlen <= 16) + *(uint16_t *)din = tmp & 0xffff; + else + *(uint32_t *)din = tmp; + } + dputchar('\n'); + + return 0; +} diff --git a/src/target/firmware/calypso/timer.c b/src/target/firmware/calypso/timer.c new file mode 100644 index 00000000..1dd55f26 --- /dev/null +++ b/src/target/firmware/calypso/timer.c @@ -0,0 +1,156 @@ +/* Calypso DBB internal Timer Driver */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdio.h> +#include <memory.h> +#include <stdint.h> + +#include <defines.h> + +#include <calypso/timer.h> +#include <calypso/irq.h> + +#define BASE_ADDR_TIMER 0xfffe3800 +#define TIMER2_OFFSET 0x3000 + +#define TIMER_REG(n, m) (((n)-1) ? (BASE_ADDR_TIMER + TIMER2_OFFSET + (m)) : (BASE_ADDR_TIMER + (m))) + +enum timer_reg { + CNTL_TIMER = 0x00, + LOAD_TIMER = 0x02, + READ_TIMER = 0x04, +}; + +enum timer_ctl { + CNTL_START = (1 << 0), + CNTL_AUTO_RELOAD = (1 << 1), + CNTL_CLOCK_ENABLE = (1 << 5), +}; + +/* Regular Timers (1 and 2) */ + +void hwtimer_enable(int num, int on) +{ + uint8_t ctl; + + if (num < 1 || num > 2) { + printf("Unknown timer %u\n", num); + return; + } + + ctl = readb(TIMER_REG(num, CNTL_TIMER)); + if (on) + ctl |= CNTL_START|CNTL_CLOCK_ENABLE; + else + ctl &= ~CNTL_START; + writeb(ctl, TIMER_REG(num, CNTL_TIMER)); +} + +void hwtimer_config(int num, uint8_t pre_scale, int auto_reload) +{ + uint8_t ctl; + + ctl = (pre_scale & 0x7) << 2; + if (auto_reload) + ctl |= CNTL_AUTO_RELOAD; + + writeb(ctl, TIMER_REG(num, CNTL_TIMER)); +} + +void hwtimer_load(int num, uint16_t val) +{ + writew(val, TIMER_REG(num, LOAD_TIMER)); +} + +uint16_t hwtimer_read(int num) +{ + uint8_t ctl = readb(TIMER_REG(num, CNTL_TIMER)); + + /* somehow a read results in an abort */ + if ((ctl & (CNTL_START|CNTL_CLOCK_ENABLE)) != (CNTL_START|CNTL_CLOCK_ENABLE)) + return 0xFFFF; + return readw(TIMER_REG(num, READ_TIMER)); +} + +void hwtimer_init(void) +{ + writeb(CNTL_CLOCK_ENABLE, TIMER_REG(1, CNTL_TIMER)); + writeb(CNTL_CLOCK_ENABLE, TIMER_REG(2, CNTL_TIMER)); +} + +/* Watchdog Timer */ + +#define BASE_ADDR_WDOG 0xfffff800 +#define WDOG_REG(m) (BASE_ADDR_WDOG + m) + +enum wdog_reg { + WD_CNTL_TIMER = CNTL_TIMER, + WD_LOAD_TIMER = LOAD_TIMER, + WD_READ_TIMER = 0x02, + WD_MODE = 0x04, +}; + +enum wdog_ctl { + WD_CTL_START = (1 << 7), + WD_CTL_AUTO_RELOAD = (1 << 8) +}; + +enum wdog_mode { + WD_MODE_DIS_ARM = 0xF5, + WD_MODE_DIS_CONFIRM = 0xA0, + WD_MODE_ENABLE = (1 << 15) +}; + +#define WD_CTL_PRESCALE(value) (((value)&0x07) << 9) + +static void wdog_irq(__unused enum irq_nr nr) +{ + puts("=> WATCHDOG\n"); +} + +void wdog_enable(int on) +{ + if (on) { + irq_config(IRQ_WATCHDOG, 0, 0, 0); + irq_register_handler(IRQ_WATCHDOG, &wdog_irq); + irq_enable(IRQ_WATCHDOG); + writew(WD_MODE_ENABLE, WDOG_REG(WD_MODE)); + } else { + writew(WD_MODE_DIS_ARM, WDOG_REG(WD_MODE)); + writew(WD_MODE_DIS_CONFIRM, WDOG_REG(WD_MODE)); + } +} + +void wdog_reset(void) +{ +#if 0 + // XXX: this is supposed to reset immediately but does not seem to + writew(0xF5, WDOG_REG(WD_MODE)); + writew(0xFF, WDOG_REG(WD_MODE)); +#else + // enable watchdog + writew(WD_MODE_ENABLE, WDOG_REG(WD_MODE)); + // force expiration + writew(0x0000, WDOG_REG(WD_LOAD_TIMER)); + writew(0x0000, WDOG_REG(WD_LOAD_TIMER)); +#endif +} diff --git a/src/target/firmware/calypso/tpu.c b/src/target/firmware/calypso/tpu.c new file mode 100644 index 00000000..0b60292a --- /dev/null +++ b/src/target/firmware/calypso/tpu.c @@ -0,0 +1,346 @@ +/* Calypso DBB internal TPU (Time Processing Unit) Driver */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <delay.h> +#include <memory.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> + +/* Using TPU_DEBUG you will send special HLDC messages to the host PC + * containing the full TPU RAM content at the time you call tpu_enable() */ +//#define TPU_DEBUG + +#define BASE_ADDR_TPU 0xffff1000 +#define TPU_REG(x) (BASE_ADDR_TPU+(x)) + +#define BASE_ADDR_TPU_RAM 0xffff9000 +#define TPU_RAM_END 0xffff97ff + +enum tpu_reg_arm { + TPU_CTRL = 0x0, /* Control & Status Register */ + INT_CTRL = 0x2, /* Interrupt Control Register */ + INT_STAT = 0x4, /* Interrupt Status Register */ + TPU_OFFSET = 0xC, /* Offset operand value register */ + TPU_SYNCHRO = 0xE, /* synchro operand value register */ + IT_DSP_PG = 0x20, +}; + +enum tpu_ctrl_bits { + TPU_CTRL_RESET = (1 << 0), + TPU_CTRL_PAGE = (1 << 1), + TPU_CTRL_EN = (1 << 2), + /* unused */ + TPU_CTRL_DSP_EN = (1 << 4), + /* unused */ + TPU_CTRL_MCU_RAM_ACC = (1 << 6), + TPU_CTRL_TSP_RESET = (1 << 7), + TPU_CTRL_IDLE = (1 << 8), + TPU_CTRL_WAIT = (1 << 9), + TPU_CTRL_CK_ENABLE = (1 << 10), + TPU_CTRL_FULL_WRITE = (1 << 11), +}; + +enum tpu_int_ctrl_bits { + ICTRL_MCU_FRAME = (1 << 0), + ICTRL_MCU_PAGE = (1 << 1), + ICTRL_DSP_FRAME = (1 << 2), + ICTRL_DSP_FRAME_FORCE = (1 << 3), +}; + +static uint16_t *tpu_ptr = (uint16_t *)BASE_ADDR_TPU_RAM; + +#ifdef TPU_DEBUG +#include <comm/sercomm.h> +#include <layer1/sync.h> +static void tpu_ram_read_en(int enable) +{ + uint16_t reg; + + reg = readw(TPU_REG(TPU_CTRL)); + if (enable) + reg |= TPU_CTRL_MCU_RAM_ACC; + else + reg &= ~TPU_CTRL_MCU_RAM_ACC; + writew(reg, TPU_REG(TPU_CTRL)); +} + +static void tpu_debug(void) +{ + uint16_t *tpu_base = (uint16_t *)BASE_ADDR_TPU_RAM; + unsigned int tpu_size = tpu_ptr - tpu_base; + struct msgb *msg = sercomm_alloc_msgb(tpu_size*2); + uint16_t *data; + uint32_t *fn; + uint16_t reg; + int i; + + /* prepend tpu memory dump with frame number */ + fn = (uint32_t *) msgb_put(msg, sizeof(fn)); + *fn = l1s.current_time.fn; + + tpu_ram_read_en(1); + + data = (uint16_t *) msgb_put(msg, tpu_size*2); + for (i = 0; i < tpu_size; i ++) + data[i] = tpu_base[i]; + + tpu_ram_read_en(0); + + sercomm_sendmsg(SC_DLCI_DEBUG, msg); +} +#else +static void tpu_debug(void) { } +#endif + +#define BIT_SET 1 +#define BIT_CLEAR 0 + +/* wait for a certain control bit to be set */ +static int tpu_wait_ctrl_bit(uint16_t bit, int set) +{ + int timeout = 10*1000; + + while (1) { + uint16_t reg = readw(TPU_REG(TPU_CTRL)); + if (set) { + if (reg & bit) + break; + } else { + if (!(reg & bit)) + break; + } + timeout--; + if (timeout <= 0) { + puts("Timeout while waiting for TPU ctrl bit!\n"); + return -1; + } + } + + return 0; +} + +/* assert or de-assert TPU reset */ +void tpu_reset(int active) +{ + uint16_t reg; + + printd("tpu_reset(%u)\n", active); + reg = readw(TPU_REG(TPU_CTRL)); + if (active) { + reg |= (TPU_CTRL_RESET|TPU_CTRL_TSP_RESET); + writew(reg, TPU_REG(TPU_CTRL)); + tpu_wait_ctrl_bit(TPU_CTRL_RESET, BIT_SET); + } else { + reg &= ~(TPU_CTRL_RESET|TPU_CTRL_TSP_RESET); + writew(reg, TPU_REG(TPU_CTRL)); + tpu_wait_ctrl_bit(TPU_CTRL_RESET, BIT_CLEAR); + } +} + +/* Enable or Disable a new scenario loaded into the TPU */ +void tpu_enable(int active) +{ + uint16_t reg = readw(TPU_REG(TPU_CTRL)); + + printd("tpu_enable(%u)\n", active); + + tpu_debug(); + + if (active) + reg |= TPU_CTRL_EN; + else + reg &= ~TPU_CTRL_EN; + writew(reg, TPU_REG(TPU_CTRL)); + + /* After the new scenario is loaded, TPU switches the MCU-visible memory + * page, i.e. we can write without any danger */ + tpu_rewind(); +#if 0 + { + int i; + uint16_t oldreg = 0; + + for (i = 0; i < 100000; i++) { + reg = readw(TPU_REG(TPU_CTRL)); + if (i == 0 || oldreg != reg) { + printd("%d TPU state: 0x%04x\n", i, reg); + } + oldreg = reg; + } + } +#endif +} + +/* Enable or Disable the clock of the TPU Module */ +void tpu_clk_enable(int active) +{ + uint16_t reg = readw(TPU_REG(TPU_CTRL)); + + printd("tpu_clk_enable(%u)\n", active); + if (active) { + reg |= TPU_CTRL_CK_ENABLE; + writew(reg, TPU_REG(TPU_CTRL)); + tpu_wait_ctrl_bit(TPU_CTRL_CK_ENABLE, BIT_SET); + } else { + reg &= ~TPU_CTRL_CK_ENABLE; + writew(reg, TPU_REG(TPU_CTRL)); + tpu_wait_ctrl_bit(TPU_CTRL_CK_ENABLE, BIT_CLEAR); + } +} + +/* Enable Frame Interrupt generation on next frame. DSP will reset it */ +void tpu_dsp_frameirq_enable(void) +{ + uint16_t reg = readw(TPU_REG(TPU_CTRL)); + reg |= TPU_CTRL_DSP_EN; + writew(reg, TPU_REG(TPU_CTRL)); + + tpu_wait_ctrl_bit(TPU_CTRL_DSP_EN, BIT_SET); +} + +/* Is a Frame interrupt still pending for the DSP ? */ +int tpu_dsp_fameirq_pending(void) +{ + uint16_t reg = readw(TPU_REG(TPU_CTRL)); + + if (reg & TPU_CTRL_DSP_EN) + return 1; + + return 0; +} + +void tpu_rewind(void) +{ + dputs("tpu_rewind()\n"); + tpu_ptr = (uint16_t *) BASE_ADDR_TPU_RAM; +} + +void tpu_enqueue(uint16_t instr) +{ + printd("tpu_enqueue(tpu_ptr=%p, instr=0x%04x)\n", tpu_ptr, instr); + *tpu_ptr++ = instr; + if (tpu_ptr > (uint16_t *) TPU_RAM_END) + puts("TPU enqueue beyond end of TPU memory\n"); +} + +void tpu_init(void) +{ + uint16_t *ptr; + + /* Put TPU into Reset and enable clock */ + tpu_reset(1); + tpu_clk_enable(1); + + /* set all TPU RAM to zero */ + for (ptr = (uint16_t *) BASE_ADDR_TPU_RAM; ptr < (uint16_t *) TPU_RAM_END; ptr++) + *ptr = 0x0000; + + /* Get TPU out of reset */ + tpu_reset(0); + /* Disable all interrupts */ + writeb(0x7, TPU_REG(INT_CTRL)); + + tpu_rewind(); + tpu_enq_offset(0); + tpu_enq_sync(0); +} + +void tpu_test(void) +{ + int i; + + /* program a sequence of TSPACT events into the TPU */ + for (i = 0; i < 10; i++) { + puts("TSP ACT enable: "); + tsp_act_enable(0x0001); + tpu_enq_wait(10); + puts("TSP ACT disable: "); + tsp_act_disable(0x0001); + tpu_enq_wait(10); + } + tpu_enq_sleep(); + + /* tell the chip to execute the scenario */ + tpu_enable(1); +} + +void tpu_wait_idle(void) +{ + dputs("Waiting for TPU Idle "); + /* Wait until TPU is doing something */ + delay_us(3); + /* Wait until TPU is idle */ + while (readw(TPU_REG(TPU_CTRL)) & TPU_CTRL_IDLE) + dputchar('.'); + dputs("Done!\n"); +} + +void tpu_frame_irq_en(int mcu, int dsp) +{ + uint8_t reg = readb(TPU_REG(INT_CTRL)); + if (mcu) + reg &= ~ICTRL_MCU_FRAME; + else + reg |= ICTRL_MCU_FRAME; + + if (dsp) + reg &= ~ICTRL_DSP_FRAME; + else + reg |= ICTRL_DSP_FRAME; + + writeb(reg, TPU_REG(INT_CTRL)); +} + +void tpu_force_dsp_frame_irq(void) +{ + uint8_t reg = readb(TPU_REG(INT_CTRL)); + reg |= ICTRL_DSP_FRAME_FORCE; + writeb(reg, TPU_REG(INT_CTRL)); +} + +uint16_t tpu_get_offset(void) +{ + return readw(TPU_REG(TPU_OFFSET)); +} + +uint16_t tpu_get_synchro(void) +{ + return readw(TPU_REG(TPU_SYNCHRO)); +} + +/* add two numbers, modulo 5000, and ensure the result is positive */ +uint16_t add_mod5000(int16_t a, int16_t b) +{ + int32_t sum = (int32_t)a + (int32_t)b; + + sum %= 5000; + + /* wrap around zero */ + if (sum < 0) + sum += 5000; + + return sum; +} diff --git a/src/target/firmware/calypso/tsp.c b/src/target/firmware/calypso/tsp.c new file mode 100644 index 00000000..5d24f48e --- /dev/null +++ b/src/target/firmware/calypso/tsp.c @@ -0,0 +1,121 @@ +/* Calypso DBB internal TSP (Time Serial Port) Driver */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <memory.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> + +static uint16_t tspact_state; + +/* initiate a TSP write through the TPU */ +void tsp_write(uint8_t dev_idx, uint8_t bitlen, uint32_t dout) +{ + if (bitlen <= 8) { + tpu_enq_move(TPUI_TX_1, dout & 0xff); + } else if (bitlen <= 16) { + tpu_enq_move(TPUI_TX_1, (dout >> 8) & 0xff); + tpu_enq_move(TPUI_TX_2, dout & 0xff); + } else if (bitlen <= 24) { + tpu_enq_move(TPUI_TX_1, (dout >> 16) & 0xff); + tpu_enq_move(TPUI_TX_2, (dout >> 8) & 0xff); + tpu_enq_move(TPUI_TX_3, dout & 0xff); + } else { + tpu_enq_move(TPUI_TX_1, (dout >> 24) & 0xff); + tpu_enq_move(TPUI_TX_2, (dout >> 16) & 0xff); + tpu_enq_move(TPUI_TX_3, (dout >> 8) & 0xff); + tpu_enq_move(TPUI_TX_4, dout & 0xff); + } + tpu_enq_move(TPUI_TSP_CTRL1, (dev_idx << 5) | (bitlen - 1)); + tpu_enq_move(TPUI_TSP_CTRL2, TPUI_CTRL2_WR); +} + +/* Configure clock edge and chip enable polarity for a device */ +void tsp_setup(uint8_t dev_idx, int clk_rising, int en_positive, int en_edge) +{ + uint8_t reg = TPUI_TSP_SET1 + (dev_idx / 2); + uint8_t val = 0; + uint8_t shift; + + if (dev_idx & 1) + shift = 4; + else + shift = 0; + + if (clk_rising) + val |= 1; + if (en_positive) + val |= 2; + if (en_edge) + val |= 4; + + tpu_enq_move(reg, (val << shift)); +} + +/* Update the TSPACT state, including enable and disable */ +void tsp_act_update(uint16_t new_act) +{ + uint8_t low = new_act & 0xff; + uint8_t high = new_act >> 8; + + if (low != (tspact_state & 0xff)) + tpu_enq_move(TPUI_TSP_ACT_L, low); + if (high != (tspact_state >> 8)) + tpu_enq_move(TPUI_TSP_ACT_U, high); + + tspact_state = new_act; +} + +/* Enable one or multiple TSPACT signals */ +void tsp_act_enable(uint16_t bitmask) +{ + uint16_t new_act = tspact_state | bitmask; + tsp_act_update(new_act); +} + +/* Disable one or multiple TSPACT signals */ +void tsp_act_disable(uint16_t bitmask) +{ + uint16_t new_act = tspact_state & ~bitmask; + tsp_act_update(new_act); +} + +/* Obtain the current tspact state */ +uint16_t tsp_act_state(void) +{ + return tspact_state; +} + +/* Toggle one or multiple TSPACT signals */ +void tsp_act_toggle(uint16_t bitmask) +{ + uint16_t new_act = tspact_state ^ bitmask; + tsp_act_update(new_act); +} + +void tsp_init(void) +{ + tsp_act_update(0); +} diff --git a/src/target/firmware/calypso/uart.c b/src/target/firmware/calypso/uart.c new file mode 100644 index 00000000..394078de --- /dev/null +++ b/src/target/firmware/calypso/uart.c @@ -0,0 +1,440 @@ +/* Calypso DBB internal UART Driver */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by Ingo Albrecht <prom@berlin.ccc.de> + * + * All Rights Reserved + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <debug.h> + +#include <memory.h> +#include <stdint.h> +#include <string.h> +#include <stdio.h> + +#include <defines.h> +#include <console.h> +#include <comm/sercomm.h> + +#include <calypso/irq.h> +#include <calypso/uart.h> + +#define BASE_ADDR_UART_MODEM 0xffff5000 +#define OFFSET_IRDA 0x800 + +#define UART_REG(n,m) (BASE_ADDR_UART_MODEM + ((n)*OFFSET_IRDA)+(m)) + +#define LCR7BIT 0x80 +#define LCRBFBIT 0x40 +#define MCR6BIT 0x20 +#define REG_OFFS(m) ((m) & ~(LCR7BIT|LCRBFBIT|MCR6BIT)) +/* read access LCR[7] = 0 */ +enum uart_reg { + RHR = 0, + IER = 1, + IIR = 2, + LCR = 3, + MCR = 4, + LSR = 5, + MSR = 6, + SPR = 7, + MDR1 = 8, + DMR2 = 9, + SFLSR = 0x0a, + RESUME = 0x0b, + SFREGL = 0x0c, + SFREGH = 0x0d, + BLR = 0x0e, + ACREG = 0x0f, + SCR = 0x10, + SSR = 0x11, + EBLR = 0x12, +/* read access LCR[7] = 1 */ + DLL = RHR | LCR7BIT, + DLH = IER | LCR7BIT, + DIV1_6 = ACREG | LCR7BIT, +/* read/write access LCR[7:0] = 0xbf */ + EFR = IIR | LCRBFBIT, + XON1 = MCR | LCRBFBIT, + XON2 = LSR | LCRBFBIT, + XOFF1 = MSR | LCRBFBIT, + XOFF2 = SPR | LCRBFBIT, +/* read/write access if EFR[4] = 1 and MCR[6] = 1 */ + TCR = MSR | MCR6BIT, + TLR = SPR | MCR6BIT, +}; +/* write access LCR[7] = 0 */ +#define THR RHR +#define FCR IIR /* only if EFR[4] = 1 */ +#define TXFLL SFLSR +#define TXFLH RESUME +#define RXFLL SFREGL +#define RXFLH SFREGH + +enum fcr_bits { + FIFO_EN = (1 << 0), + RX_FIFO_CLEAR = (1 << 1), + TX_FIFO_CLEAR = (1 << 2), + DMA_MODE = (1 << 3), +}; +#define TX_FIFO_TRIG_SHIFT 4 +#define RX_FIFO_TRIG_SHIFT 6 + +enum iir_bits { + IIR_INT_PENDING = 0x01, + IIR_INT_TYPE = 0x3E, + IIR_INT_TYPE_RX_STATUS_ERROR = 0x06, + IIR_INT_TYPE_RX_TIMEOUT = 0x0C, + IIR_INT_TYPE_RHR = 0x04, + IIR_INT_TYPE_THR = 0x02, + IIR_INT_TYPE_MSR = 0x00, + IIR_INT_TYPE_XOFF = 0x10, + IIR_INT_TYPE_FLOW = 0x20, + IIR_FCR0_MIRROR = 0xC0, +}; + +#define UART_REG_UIR 0xffff6000 + +/* enable or disable the divisor latch for access to DLL, DLH */ +static void uart_set_lcr7bit(int uart, int on) +{ + uint8_t reg; + + reg = readb(UART_REG(uart, LCR)); + if (on) + reg |= (1 << 7); + else + reg &= ~(1 << 7); + writeb(reg, UART_REG(uart, LCR)); +} + +static uint8_t old_lcr; +static void uart_set_lcr_bf(int uart, int on) +{ + if (on) { + old_lcr = readb(UART_REG(uart, LCR)); + writeb(0xBF, UART_REG(uart, LCR)); + } else { + writeb(old_lcr, UART_REG(uart, LCR)); + } +} + +/* Enable or disable the TCR_TLR latch bit in MCR[6] */ +static void uart_set_mcr6bit(int uart, int on) +{ + uint8_t mcr; + /* we assume EFR[4] is always set to 1 */ + mcr = readb(UART_REG(uart, MCR)); + if (on) + mcr |= (1 << 6); + else + mcr &= ~(1 << 6); + writeb(mcr, UART_REG(uart, MCR)); +} + +static void uart_reg_write(int uart, enum uart_reg reg, uint8_t val) +{ + if (reg & LCRBFBIT) + uart_set_lcr_bf(uart, 1); + else if (reg & LCR7BIT) + uart_set_lcr7bit(uart, 1); + else if (reg & MCR6BIT) + uart_set_mcr6bit(uart, 1); + + writeb(val, UART_REG(uart, REG_OFFS(reg))); + + if (reg & LCRBFBIT) + uart_set_lcr_bf(uart, 0); + else if (reg & LCR7BIT) + uart_set_lcr7bit(uart, 0); + else if (reg & MCR6BIT) + uart_set_mcr6bit(uart, 0); +} + +/* read from a UART register, applying any required latch bits */ +static uint8_t uart_reg_read(int uart, enum uart_reg reg) +{ + uint8_t ret; + + if (reg & LCRBFBIT) + uart_set_lcr_bf(uart, 1); + else if (reg & LCR7BIT) + uart_set_lcr7bit(uart, 1); + else if (reg & MCR6BIT) + uart_set_mcr6bit(uart, 1); + + ret = readb(UART_REG(uart, REG_OFFS(reg))); + + if (reg & LCRBFBIT) + uart_set_lcr_bf(uart, 0); + else if (reg & LCR7BIT) + uart_set_lcr7bit(uart, 0); + else if (reg & MCR6BIT) + uart_set_mcr6bit(uart, 0); + + return ret; +} + +static void uart_irq_handler_cons(__unused enum irq_nr irqnr) +{ + const uint8_t uart = CONS_UART_NR; + uint8_t iir; + + //uart_putchar_nb(uart, 'U'); + + iir = uart_reg_read(uart, IIR); + if (iir & IIR_INT_PENDING) + return; + + switch (iir & IIR_INT_TYPE) { + case IIR_INT_TYPE_RHR: + break; + case IIR_INT_TYPE_THR: + if (cons_rb_flush() == 1) { + /* everything was flushed, disable THR IRQ */ + uint8_t ier = uart_reg_read(uart, IER); + ier &= ~(1 << 1); + uart_reg_write(uart, IER, ier); + } + break; + case IIR_INT_TYPE_MSR: + break; + case IIR_INT_TYPE_RX_STATUS_ERROR: + break; + case IIR_INT_TYPE_RX_TIMEOUT: + break; + case IIR_INT_TYPE_XOFF: + break; + } +} + +static void uart_irq_handler_sercomm(__unused enum irq_nr irqnr) +{ + const uint8_t uart = SERCOMM_UART_NR; + uint8_t iir, ch; + + //uart_putchar_nb(uart, 'U'); + + iir = uart_reg_read(uart, IIR); + if (iir & IIR_INT_PENDING) + return; + + switch (iir & IIR_INT_TYPE) { + case IIR_INT_TYPE_RX_TIMEOUT: + case IIR_INT_TYPE_RHR: + /* as long as we have rx data available */ + while (uart_getchar_nb(uart, &ch)) { + if (sercomm_drv_rx_char(ch) < 0) { + /* sercomm cannot receive more data right now */ + uart_irq_enable(uart, UART_IRQ_RX_CHAR, 0); + } + } + break; + case IIR_INT_TYPE_THR: + /* as long as we have space in the FIFO */ + while (!uart_tx_busy(uart)) { + /* get a byte from sercomm */ + if (!sercomm_drv_pull(&ch)) { + /* no more bytes in sercomm, stop TX interrupts */ + uart_irq_enable(uart, UART_IRQ_TX_EMPTY, 0); + break; + } + /* write the byte into the TX FIFO */ + uart_putchar_nb(uart, ch); + } + break; + case IIR_INT_TYPE_MSR: + printf("UART IRQ MSR\n"); + break; + case IIR_INT_TYPE_RX_STATUS_ERROR: + printf("UART IRQ RX_SE\n"); + break; + case IIR_INT_TYPE_XOFF: + printf("UART IRQXOFF\n"); + break; + } +} + +static const uint8_t uart2irq[] = { + [0] = IRQ_UART_IRDA, + [1] = IRQ_UART_MODEM, +}; + +void uart_init(uint8_t uart, uint8_t interrupts) +{ + uint8_t irq = uart2irq[uart]; + + uart_reg_write(uart, IER, 0x00); + if (uart == CONS_UART_NR) { + cons_init(); + if(interrupts) { + irq_register_handler(irq, &uart_irq_handler_cons); + irq_config(irq, 0, 0, 0xff); + irq_enable(irq); + } + } else { + sercomm_init(); + if(interrupts) { + irq_register_handler(irq, &uart_irq_handler_sercomm); + irq_config(irq, 0, 0, 0xff); + irq_enable(irq); + } + uart_irq_enable(uart, UART_IRQ_RX_CHAR, 1); + } +#if 0 + if (uart == 1) { + /* assign UART to MCU and unmask interrupts*/ + writeb(UART_REG_UIR, 0x00); + } +#endif + + /* if we don't initialize these, we get strange corruptions in the + received data... :-( */ + uart_reg_write(uart, MDR1, 0x07); /* turn off UART */ + uart_reg_write(uart, XON1, 0x00); /* Xon1/Addr Register */ + uart_reg_write(uart, XON2, 0x00); /* Xon2/Addr Register */ + uart_reg_write(uart, XOFF1, 0x00); /* Xoff1 Register */ + uart_reg_write(uart, XOFF2, 0x00); /* Xoff2 Register */ + uart_reg_write(uart, EFR, 0x00); /* Enhanced Features Register */ + + /* select UART mode */ + uart_reg_write(uart, MDR1, 0); + /* no XON/XOFF flow control, ENHANCED_EN, no auto-RTS/CTS */ + uart_reg_write(uart, EFR, (1 << 4)); + /* enable Tx/Rx FIFO, Tx trigger at 56 spaces, Rx trigger at 60 chars */ + uart_reg_write(uart, FCR, FIFO_EN | RX_FIFO_CLEAR | TX_FIFO_CLEAR | + (3 << TX_FIFO_TRIG_SHIFT) | (3 << RX_FIFO_TRIG_SHIFT)); + + /* THR interrupt only when TX FIFO and TX shift register are empty */ + uart_reg_write(uart, SCR, (1 << 0));// | (1 << 3)); + + /* 8 bit, 1 stop bit, no parity, no break */ + uart_reg_write(uart, LCR, 0x03); + + uart_set_lcr7bit(uart, 0); +} + +void uart_poll(uint8_t uart) { + if(uart == CONS_UART_NR) { + uart_irq_handler_cons(0); + } else { + uart_irq_handler_sercomm(0); + } +} + +void uart_irq_enable(uint8_t uart, enum uart_irq irq, int on) +{ + uint8_t ier = uart_reg_read(uart, IER); + uint8_t mask = 0; + + switch (irq) { + case UART_IRQ_TX_EMPTY: + mask = (1 << 1); + break; + case UART_IRQ_RX_CHAR: + mask = (1 << 0); + break; + } + + if (on) + ier |= mask; + else + ier &= ~mask; + + uart_reg_write(uart, IER, ier); +} + + +void uart_putchar_wait(uint8_t uart, int c) +{ + /* wait while TX FIFO indicates full */ + while (readb(UART_REG(uart, SSR)) & 0x01) { } + + /* put character in TX FIFO */ + writeb(c, UART_REG(uart, THR)); +} + +int uart_putchar_nb(uint8_t uart, int c) +{ + /* if TX FIFO indicates full, abort */ + if (readb(UART_REG(uart, SSR)) & 0x01) + return 0; + + writeb(c, UART_REG(uart, THR)); + return 1; +} + +int uart_getchar_nb(uint8_t uart, uint8_t *ch) +{ + uint8_t lsr; + + lsr = readb(UART_REG(uart, LSR)); + + /* something strange happened */ + if (lsr & 0x02) + printf("LSR RX_OE\n"); + if (lsr & 0x04) + printf("LSR RX_PE\n"); + if (lsr & 0x08) + printf("LSR RX_FE\n"); + if (lsr & 0x10) + printf("LSR RX_BI\n"); + if (lsr & 0x80) + printf("LSR RX_FIFO_STS\n"); + + /* is the Rx FIFO empty? */ + if (!(lsr & 0x01)) + return 0; + + *ch = readb(UART_REG(uart, RHR)); + //printf("getchar_nb(%u) = %02x\n", uart, *ch); + return 1; +} + +int uart_tx_busy(uint8_t uart) +{ + if (readb(UART_REG(uart, SSR)) & 0x01) + return 1; + return 0; +} + +static const uint16_t divider[] = { + [UART_38400] = 21, /* 38,690 */ + [UART_57600] = 14, /* 58,035 */ + [UART_115200] = 7, /* 116,071 */ + [UART_230400] = 4, /* 203,125! (-3% would be 223,488) */ + [UART_460800] = 2, /* 406,250! (-3% would be 446,976) */ + [UART_921600] = 1, /* 812,500! (-3% would be 893,952) */ +}; + +int uart_baudrate(uint8_t uart, enum uart_baudrate bdrt) +{ + uint16_t div; + + if (bdrt > ARRAY_SIZE(divider)) + return -1; + + div = divider[bdrt]; + uart_set_lcr7bit(uart, 1); + writeb(div & 0xff, UART_REG(uart, DLL)); + writeb(div >> 8, UART_REG(uart, DLH)); + uart_set_lcr7bit(uart, 0); + + return 0; +} diff --git a/src/target/firmware/calypso/uwire.c b/src/target/firmware/calypso/uwire.c new file mode 100644 index 00000000..ac8f15e4 --- /dev/null +++ b/src/target/firmware/calypso/uwire.c @@ -0,0 +1,136 @@ +/* Driver for uWire Master Controller inside TI Calypso */ + +/* (C) 2010 by Sylvain Munaut <tnt@246tNt.com> + * + * All Rights Reserved + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +//#define DEBUG +#include <debug.h> + +#include <memory.h> +#include <uwire.h> +#include <delay.h> + +#define BASE_ADDR_UWIRE 0xfffe4000 +#define UWIRE_REG(n) (BASE_ADDR_UWIRE+(n)) + +enum uwire_regs { + REG_DATA = 0x00, + REG_CSR = 0x02, + REG_SR1 = 0x04, + REG_SR2 = 0x06, + REG_SR3 = 0x08, +}; + +#define UWIRE_CSR_BITS_RD(n) (((n) & 0x1f) << 0) +#define UWIRE_CSR_BITS_WR(n) (((n) & 0x1f) << 5) +#define UWIRE_CSR_IDX(n) (((n) & 3) << 10) +#define UWIRE_CSR_CS_CMD (1 << 12) +#define UWIRE_CSR_START (1 << 13) +#define UWIRE_CSR_CSRB (1 << 14) +#define UWIRE_CSR_RDRB (1 << 15) + +#define UWIRE_CSn_EDGE_RD (1 << 0) /* 1=falling 0=rising */ +#define UWIRE_CSn_EDGE_WR (1 << 1) /* 1=falling 0=rising */ +#define UWIRE_CSn_CS_LVL (1 << 2) +#define UWIRE_CSn_FRQ_DIV2 (0 << 3) +#define UWIRE_CSn_FRQ_DIV4 (1 << 3) +#define UWIRE_CSn_FRQ_DIV8 (2 << 3) +#define UWIRE_CSn_CKH + +#define UWIRE_CSn_SHIFT(n) (((n) & 1) ? 6 : 0) +#define UWIRE_CSn_REG(n) (((n) & 2) ? REG_SR2 : REG_SR1) + +#define UWIRE_SR3_CLK_EN (1 << 0) +#define UWIRE_SR3_CLK_DIV2 (0 << 1) +#define UWIRE_SR3_CLK_DIV4 (1 << 1) +#define UWIRE_SR3_CLK_DIV7 (2 << 1) +#define UWIRE_SR3_CLK_DIV10 (3 << 1) + +static inline void _uwire_wait(int mask, int val) +{ + while ((readw(UWIRE_REG(REG_CSR)) & mask) != val); +} + +void uwire_init(void) +{ + writew(UWIRE_SR3_CLK_EN | UWIRE_SR3_CLK_DIV2, UWIRE_REG(REG_SR3)); + /* FIXME only init CS0 for now */ + writew(((UWIRE_CSn_CS_LVL | UWIRE_CSn_FRQ_DIV2) << UWIRE_CSn_SHIFT(0)), + UWIRE_REG(UWIRE_CSn_REG(0))); + writew(UWIRE_CSR_IDX(0) | UWIRE_CSR_CS_CMD, UWIRE_REG(REG_CSR)); + _uwire_wait(UWIRE_CSR_CSRB, 0); +} + +int uwire_xfer(int cs, int bitlen, const void *dout, void *din) +{ + uint16_t tmp = 0; + + if (bitlen <= 0 || bitlen > 16) + return -1; + if (cs < 0 || cs > 4) + return -1; + + /* FIXME uwire_init always selects CS0 for now */ + + printd("uwire_xfer(dev_idx=%u, bitlen=%u\n", cs, bitlen); + + /* select the chip */ + writew(UWIRE_CSR_IDX(0) | UWIRE_CSR_CS_CMD, UWIRE_REG(REG_CSR)); + _uwire_wait(UWIRE_CSR_CSRB, 0); + + if (dout) { + if (bitlen <= 8) + tmp = *(uint8_t *)dout; + else if (bitlen <= 16) + tmp = *(uint16_t *)dout; + tmp <<= 16 - bitlen; /* align to MSB */ + writew(tmp, UWIRE_REG(REG_DATA)); + printd(", data_out=0x%04hx", tmp); + } + + tmp = (dout ? UWIRE_CSR_BITS_WR(bitlen) : 0) | + (din ? UWIRE_CSR_BITS_RD(bitlen) : 0) | + UWIRE_CSR_START; + writew(tmp, UWIRE_REG(REG_CSR)); + + _uwire_wait(UWIRE_CSR_CSRB, 0); + + if (din) { + _uwire_wait(UWIRE_CSR_RDRB, UWIRE_CSR_RDRB); + + tmp = readw(UWIRE_REG(REG_DATA)); + printd(", data_in=0x%08x", tmp); + + if (bitlen <= 8) + *(uint8_t *)din = tmp & 0xff; + else if (bitlen <= 16) + *(uint16_t *)din = tmp & 0xffff; + } + /* unselect the chip */ + writew(UWIRE_CSR_IDX(0) | 0, UWIRE_REG(REG_CSR)); + _uwire_wait(UWIRE_CSR_CSRB, 0); + + printd(")\n"); + + return 0; +} |