From fbe7b94c9c65f2df74acd5dff7503c9833ec2579 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Thu, 18 Feb 2010 16:46:36 +0100 Subject: Initial import of OsmocomBB into git repository --- src/target/firmware/.gitignore | 7 + src/target/firmware/Makefile | 69 + src/target/firmware/Makefile.inc | 19 + src/target/firmware/abb/twl3025.c | 291 ++++ src/target/firmware/apps/compal_dsp_dump/main.c | 78 + src/target/firmware/apps/compal_dump/main.c | 90 ++ src/target/firmware/apps/hello_world/main.c | 126 ++ src/target/firmware/apps/l1test/main.c | 289 ++++ src/target/firmware/apps/layer1/main.c | 214 +++ .../firmware/board/common/compal_ramload.lds | 83 ++ .../firmware/board/common/compal_ramload_start.S | 257 ++++ .../firmware/board/common/rffe_compal_dualband.c | 70 + src/target/firmware/board/compal_e88/init.c | 126 ++ src/target/firmware/calypso/Makefile | 17 + src/target/firmware/calypso/arm.c | 26 + src/target/firmware/calypso/backlight.c | 67 + src/target/firmware/calypso/clock.c | 202 +++ src/target/firmware/calypso/dma.c | 44 + src/target/firmware/calypso/dsp.c | 391 +++++ src/target/firmware/calypso/dsp_bootcode.c | 9 + src/target/firmware/calypso/dsp_dumpcode.c | 45 + src/target/firmware/calypso/dsp_params.c | 94 ++ src/target/firmware/calypso/du.c | 51 + src/target/firmware/calypso/i2c.c | 123 ++ src/target/firmware/calypso/irq.c | 266 ++++ src/target/firmware/calypso/keypad.c | 165 +++ src/target/firmware/calypso/misc.c | 60 + src/target/firmware/calypso/rtc.c | 82 ++ src/target/firmware/calypso/spi.c | 141 ++ src/target/firmware/calypso/timer.c | 127 ++ src/target/firmware/calypso/tpu.c | 288 ++++ src/target/firmware/calypso/tsp.c | 121 ++ src/target/firmware/calypso/uart.c | 396 +++++ src/target/firmware/comm/Makefile | 26 + src/target/firmware/comm/msgb.c | 134 ++ src/target/firmware/comm/sercomm.c | 244 +++ src/target/firmware/comm/sercomm_cons.c | 126 ++ src/target/firmware/display/font_r8x8.c | Bin 0 -> 50349 bytes src/target/firmware/display/st7558.c | 122 ++ src/target/firmware/flash/cfi_flash.c | 435 ++++++ src/target/firmware/include/abb/twl3025.h | 128 ++ src/target/firmware/include/arm.h | 7 + src/target/firmware/include/asm/assembler.h | 97 ++ src/target/firmware/include/asm/atomic.h | 106 ++ src/target/firmware/include/asm/bitops.h | 225 +++ src/target/firmware/include/asm/ctype.h | 54 + src/target/firmware/include/asm/div64.h | 48 + src/target/firmware/include/asm/linkage.h | 18 + src/target/firmware/include/asm/ptrace.h | 128 ++ src/target/firmware/include/asm/system.h | 109 ++ src/target/firmware/include/board.h | 6 + src/target/firmware/include/calypso/backlight.h | 10 + src/target/firmware/include/calypso/clock.h | 67 + src/target/firmware/include/calypso/dma.h | 6 + src/target/firmware/include/calypso/dsp.h | 31 + src/target/firmware/include/calypso/dsp_api.h | 1554 ++++++++++++++++++++ src/target/firmware/include/calypso/du.h | 32 + src/target/firmware/include/calypso/irq.h | 49 + .../firmware/include/calypso/l1_environment.h | 365 +++++ src/target/firmware/include/calypso/misc.h | 7 + src/target/firmware/include/calypso/rtc.h | 6 + src/target/firmware/include/calypso/timer.h | 22 + src/target/firmware/include/calypso/tpu.h | 117 ++ src/target/firmware/include/calypso/tsp.h | 30 + src/target/firmware/include/calypso/uart.h | 30 + src/target/firmware/include/cfi_flash.h | 68 + src/target/firmware/include/comm/msgb.h | 104 ++ src/target/firmware/include/comm/sercomm.h | 57 + src/target/firmware/include/comm/sercomm_cons.h | 10 + src/target/firmware/include/console.h | 20 + src/target/firmware/include/debug.h | 31 + src/target/firmware/include/delay.h | 7 + src/target/firmware/include/display/st7558.h | 15 + src/target/firmware/include/gsm.h | 29 + src/target/firmware/include/i2c.h | 7 + src/target/firmware/include/keypad.h | 66 + src/target/firmware/include/layer1/afc.h | 13 + src/target/firmware/include/layer1/agc.h | 6 + src/target/firmware/include/layer1/avg.h | 23 + src/target/firmware/include/layer1/l23_api.h | 11 + src/target/firmware/include/layer1/sync.h | 75 + src/target/firmware/include/layer1/tdma_sched.h | 52 + src/target/firmware/include/layer1/tpu_window.h | 17 + src/target/firmware/include/linuxlist.h | 360 +++++ src/target/firmware/include/memory.h | 28 + src/target/firmware/include/rf/trf6151.h | 35 + src/target/firmware/include/rffe.h | 15 + src/target/firmware/include/spi.h | 7 + src/target/firmware/include/stdio.h | 47 + src/target/firmware/include/string.h | 12 + src/target/firmware/layer1/Makefile | 18 + src/target/firmware/layer1/afc.c | 122 ++ src/target/firmware/layer1/agc.c | 67 + src/target/firmware/layer1/avg.c | 57 + src/target/firmware/layer1/gsm.c | 117 ++ src/target/firmware/layer1/init.c | 70 + src/target/firmware/layer1/l23_api.c | 63 + src/target/firmware/layer1/sync.c | 911 ++++++++++++ src/target/firmware/layer1/tdma_sched.c | 163 ++ src/target/firmware/layer1/tpu_window.c | 94 ++ src/target/firmware/lib/Makefile | 26 + src/target/firmware/lib/bitops.h | 33 + src/target/firmware/lib/changebit.S | 21 + src/target/firmware/lib/clearbit.S | 22 + src/target/firmware/lib/console.c | 202 +++ src/target/firmware/lib/copy_template.S | 255 ++++ src/target/firmware/lib/ctype.c | 34 + src/target/firmware/lib/div64.S | 200 +++ src/target/firmware/lib/lib1funcs.S | 334 +++++ src/target/firmware/lib/memcpy.S | 59 + src/target/firmware/lib/memset.S | 80 + src/target/firmware/lib/printf.c | 19 + src/target/firmware/lib/setbit.S | 22 + src/target/firmware/lib/string.c | 50 + src/target/firmware/lib/testchangebit.S | 18 + src/target/firmware/lib/testclearbit.S | 18 + src/target/firmware/lib/testsetbit.S | 18 + src/target/firmware/lib/vsprintf.c | 847 +++++++++++ src/target/firmware/rf/trf6151.c | 380 +++++ 119 files changed, 14208 insertions(+) create mode 100644 src/target/firmware/.gitignore create mode 100644 src/target/firmware/Makefile create mode 100644 src/target/firmware/Makefile.inc create mode 100644 src/target/firmware/abb/twl3025.c create mode 100644 src/target/firmware/apps/compal_dsp_dump/main.c create mode 100644 src/target/firmware/apps/compal_dump/main.c create mode 100644 src/target/firmware/apps/hello_world/main.c create mode 100644 src/target/firmware/apps/l1test/main.c create mode 100644 src/target/firmware/apps/layer1/main.c create mode 100644 src/target/firmware/board/common/compal_ramload.lds create mode 100644 src/target/firmware/board/common/compal_ramload_start.S create mode 100644 src/target/firmware/board/common/rffe_compal_dualband.c create mode 100644 src/target/firmware/board/compal_e88/init.c create mode 100644 src/target/firmware/calypso/Makefile create mode 100644 src/target/firmware/calypso/arm.c create mode 100644 src/target/firmware/calypso/backlight.c create mode 100644 src/target/firmware/calypso/clock.c create mode 100644 src/target/firmware/calypso/dma.c create mode 100644 src/target/firmware/calypso/dsp.c create mode 100644 src/target/firmware/calypso/dsp_bootcode.c create mode 100644 src/target/firmware/calypso/dsp_dumpcode.c create mode 100644 src/target/firmware/calypso/dsp_params.c create mode 100644 src/target/firmware/calypso/du.c create mode 100644 src/target/firmware/calypso/i2c.c create mode 100644 src/target/firmware/calypso/irq.c create mode 100644 src/target/firmware/calypso/keypad.c create mode 100644 src/target/firmware/calypso/misc.c create mode 100644 src/target/firmware/calypso/rtc.c create mode 100644 src/target/firmware/calypso/spi.c create mode 100644 src/target/firmware/calypso/timer.c create mode 100644 src/target/firmware/calypso/tpu.c create mode 100644 src/target/firmware/calypso/tsp.c create mode 100644 src/target/firmware/calypso/uart.c create mode 100644 src/target/firmware/comm/Makefile create mode 100644 src/target/firmware/comm/msgb.c create mode 100644 src/target/firmware/comm/sercomm.c create mode 100644 src/target/firmware/comm/sercomm_cons.c create mode 100644 src/target/firmware/display/font_r8x8.c create mode 100644 src/target/firmware/display/st7558.c create mode 100644 src/target/firmware/flash/cfi_flash.c create mode 100644 src/target/firmware/include/abb/twl3025.h create mode 100644 src/target/firmware/include/arm.h create mode 100644 src/target/firmware/include/asm/assembler.h create mode 100644 src/target/firmware/include/asm/atomic.h create mode 100644 src/target/firmware/include/asm/bitops.h create mode 100644 src/target/firmware/include/asm/ctype.h create mode 100644 src/target/firmware/include/asm/div64.h create mode 100644 src/target/firmware/include/asm/linkage.h create mode 100644 src/target/firmware/include/asm/ptrace.h create mode 100644 src/target/firmware/include/asm/system.h create mode 100644 src/target/firmware/include/board.h create mode 100644 src/target/firmware/include/calypso/backlight.h create mode 100644 src/target/firmware/include/calypso/clock.h create mode 100644 src/target/firmware/include/calypso/dma.h create mode 100644 src/target/firmware/include/calypso/dsp.h create mode 100644 src/target/firmware/include/calypso/dsp_api.h create mode 100644 src/target/firmware/include/calypso/du.h create mode 100644 src/target/firmware/include/calypso/irq.h create mode 100644 src/target/firmware/include/calypso/l1_environment.h create mode 100644 src/target/firmware/include/calypso/misc.h create mode 100644 src/target/firmware/include/calypso/rtc.h create mode 100644 src/target/firmware/include/calypso/timer.h create mode 100644 src/target/firmware/include/calypso/tpu.h create mode 100644 src/target/firmware/include/calypso/tsp.h create mode 100644 src/target/firmware/include/calypso/uart.h create mode 100644 src/target/firmware/include/cfi_flash.h create mode 100644 src/target/firmware/include/comm/msgb.h create mode 100644 src/target/firmware/include/comm/sercomm.h create mode 100644 src/target/firmware/include/comm/sercomm_cons.h create mode 100644 src/target/firmware/include/console.h create mode 100644 src/target/firmware/include/debug.h create mode 100644 src/target/firmware/include/delay.h create mode 100644 src/target/firmware/include/display/st7558.h create mode 100644 src/target/firmware/include/gsm.h create mode 100644 src/target/firmware/include/i2c.h create mode 100644 src/target/firmware/include/keypad.h create mode 100644 src/target/firmware/include/layer1/afc.h create mode 100644 src/target/firmware/include/layer1/agc.h create mode 100644 src/target/firmware/include/layer1/avg.h create mode 100644 src/target/firmware/include/layer1/l23_api.h create mode 100644 src/target/firmware/include/layer1/sync.h create mode 100644 src/target/firmware/include/layer1/tdma_sched.h create mode 100644 src/target/firmware/include/layer1/tpu_window.h create mode 100644 src/target/firmware/include/linuxlist.h create mode 100644 src/target/firmware/include/memory.h create mode 100644 src/target/firmware/include/rf/trf6151.h create mode 100644 src/target/firmware/include/rffe.h create mode 100644 src/target/firmware/include/spi.h create mode 100644 src/target/firmware/include/stdio.h create mode 100644 src/target/firmware/include/string.h create mode 100644 src/target/firmware/layer1/Makefile create mode 100644 src/target/firmware/layer1/afc.c create mode 100644 src/target/firmware/layer1/agc.c create mode 100644 src/target/firmware/layer1/avg.c create mode 100644 src/target/firmware/layer1/gsm.c create mode 100644 src/target/firmware/layer1/init.c create mode 100644 src/target/firmware/layer1/l23_api.c create mode 100644 src/target/firmware/layer1/sync.c create mode 100644 src/target/firmware/layer1/tdma_sched.c create mode 100644 src/target/firmware/layer1/tpu_window.c create mode 100644 src/target/firmware/lib/Makefile create mode 100644 src/target/firmware/lib/bitops.h create mode 100644 src/target/firmware/lib/changebit.S create mode 100644 src/target/firmware/lib/clearbit.S create mode 100644 src/target/firmware/lib/console.c create mode 100644 src/target/firmware/lib/copy_template.S create mode 100644 src/target/firmware/lib/ctype.c create mode 100644 src/target/firmware/lib/div64.S create mode 100644 src/target/firmware/lib/lib1funcs.S create mode 100644 src/target/firmware/lib/memcpy.S create mode 100644 src/target/firmware/lib/memset.S create mode 100644 src/target/firmware/lib/printf.c create mode 100644 src/target/firmware/lib/setbit.S create mode 100644 src/target/firmware/lib/string.c create mode 100644 src/target/firmware/lib/testchangebit.S create mode 100644 src/target/firmware/lib/testclearbit.S create mode 100644 src/target/firmware/lib/testsetbit.S create mode 100644 src/target/firmware/lib/vsprintf.c create mode 100644 src/target/firmware/rf/trf6151.c (limited to 'src/target') diff --git a/src/target/firmware/.gitignore b/src/target/firmware/.gitignore new file mode 100644 index 00000000..5dff144b --- /dev/null +++ b/src/target/firmware/.gitignore @@ -0,0 +1,7 @@ +*.o +*.a +*.lst +*.bin +*.elf +*.size +*~ diff --git a/src/target/firmware/Makefile b/src/target/firmware/Makefile new file mode 100644 index 00000000..0e5ff2cc --- /dev/null +++ b/src/target/firmware/Makefile @@ -0,0 +1,69 @@ +INCLUDES=-Iinclude/ -I../../../include +-include Makefile.inc + +# individual list of object files, they should probably become libraries +FLASH_OBJS=flash/cfi_flash.o +DISPLAY_OBJS=display/font_r8x8.o display/st7558.o +ABB_OBJS=abb/twl3025.o +RF_OBJS=rf/trf6151.o +BOARD_C123_OBJS=board/common/rffe_compal_dualband.o board/compal_e88/init.o +START=board/common/compal_ramload_start.S +LDS=board/common/compal_ramload.lds + +# The objects that we want to link with every application +OBJS=start.o $(ABB_OBJS) $(RF_OBJS) $(DISPLAY_OBJS) $(FLASH_OBJS) $(BOARD_C123_OBJS) + +# The libraries that we want to link with every application +LIBS=calypso/libcalypso.a layer1/liblayer1.a lib/libmini.a comm/libcomm.a + +# The list of applications we ant to build. Please add your apps here! +APPS=hello_world l1test compal_dump compal_dsp_dump layer1 + +APP_BINS=$(APPS:=.bin) +APP_ELFS=$(APPS:=.elf) +APP_OBJS=$(patsubst %,apps/%/main.o, $(APPS)) +APP_SIZES=$(APP_ELFS:.elf=.size) + +LST=$(OBJS:.o=.lst) $(APP_OBJS:.o=.lst) $(START:.S=.lst) + +all: $(APP_BINS) $(APP_ELFS) $(APP_SIZES) + +start.o: $(START) + $(CROSS_COMPILE)$(CC) $(CFLAGS) -c -o $@ $^ + +%.o: %.c + $(CROSS_COMPILE)$(CC) $(CFLAGS) -c -o $@ $^ + +%.elf: $(OBJS) apps/%/main.o $(LIBS) + $(CROSS_COMPILE)$(LD) $(LDFLAGS) -T $(LDS) -Bstatic -Map $@.map -o $@ --start-group $^ --end-group + $(CROSS_COMPILE)$(SIZE) $@ + +%.size: %.elf + $(CROSS_COMPILE)$(SIZE) -A $^ > $@ + +%.bin: %.elf + $(CROSS_COMPILE)objcopy --gap-fill=0xff -O binary $^ $@ + +# FIXME: we don't do dependencies into the subdirectories, so we always rebuild +.PHONY: calypso/libcalypso.a +calypso/libcalypso.a: + make -C calypso all + +# FIXME: we don't do dependencies into the subdirectories, so we always rebuild +.PHONY: layer1/liblayer1.a +layer1/liblayer1.a: + make -C layer1 all + +lib/libmini.a: + make -C lib all + +# FIXME: we don't do dependencies into the subdirectories, so we always rebuild +.PHONY: comm/libcomm.a +comm/libcomm.a: + make -C comm all +clean: + make -C calypso clean + make -C layer1 clean + make -C lib clean + make -C comm clean + rm -f *.map $(OBJS) $(APP_BINS) $(APP_ELFS) $(APP_SIZES) $(LST) diff --git a/src/target/firmware/Makefile.inc b/src/target/firmware/Makefile.inc new file mode 100644 index 00000000..f3f19478 --- /dev/null +++ b/src/target/firmware/Makefile.inc @@ -0,0 +1,19 @@ +CROSS_COMPILE?=arm-elf- +CC=gcc +LD=ld +SIZE=size +OBJCOPY=objcopy + +DEBUGF=dwarf-2 + +CFLAGS=-mcpu=arm7tdmi $(INCLUDES) +CFLAGS += -Wall -Wextra -Wcast-align -Wimplicit -Wunused +CFLAGS += -Wswitch -Wredundant-decls -Wreturn-type -Wshadow -Wnested-externs +CFLAGS += -Wbad-function-cast -Wsign-compare -Waggregate-return +CFLAGS += -Wa,-adhlns=$(subst $(suffix $<),.lst,$<) +CFLAGS += -Os -ffunction-sections +CFLAGS += -g$(DEBUGF) + +ASFLAGS=-Wa,-adhlns=$(<:.S=.lst),--g$(DEBUGF) $(INCLUDES) -D__ASSEMBLY__ + +LDFLAGS = -nostartfiles -nostdlib -nodefaultlibs --gc-sections #-Wl,-Map=$(TARGET).map,--cref diff --git a/src/target/firmware/abb/twl3025.c b/src/target/firmware/abb/twl3025.c new file mode 100644 index 00000000..bce6cc6d --- /dev/null +++ b/src/target/firmware/abb/twl3025.c @@ -0,0 +1,291 @@ +/* Driver for Analog Baseband Circuit (TWL3025) */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* TWL3025 */ +#define REG_PAGE(n) (n >> 7) +#define REG_ADDR(n) (n & 0x3f) + +#define TWL3025_DEV_IDX 0 /* On the SPI bus */ +#define TWL3025_TSP_DEV_IDX 0 /* On the TSP bus */ + +struct twl3025 { + uint8_t page; +}; +static struct twl3025 twl3025_state; + +/* Switch the register page of the TWL3025 */ +static void twl3025_switch_page(uint8_t page) +{ + if (page == 0) + twl3025_reg_write(PAGEREG, 1 << 0); + else + twl3025_reg_write(PAGEREG, 1 << 1); + + twl3025_state.page = page; +} + +static void handle_charger(void) +{ + uint16_t status; + printd("handle_charger();"); + + status = twl3025_reg_read(VRPCSTS); +// printd("\nvrpcsts: 0x%02x", status); + + if (status & 0x40) { + printd(" inserted\n"); + } else { + printd(" removed\n"); + } + +// twl3025_dump_madc(); +} + +static void handle_adc_done(void) +{ + printd("handle_adc_done();"); +} + +static void twl3025_irq(enum irq_nr nr) +{ + uint16_t src; + printd("twl3025_irq: 0x%02x\n",nr); + switch (nr){ + case IRQ_EXTERNAL: // charger in/out, pwrbtn, adc done + src = twl3025_reg_read(ITSTATREG); +// printd("itstatreg 0x%02x\n", src); + if (src & 0x08) + handle_charger(); + if (src & 0x20) + handle_adc_done(); + break; + case IRQ_EXTERNAL_FIQ: // vcc <2.8V emergency power off + puts("\nBROWNOUT!1!"); + twl3025_power_off(); + break; + default: + return; + } +} + +void twl3025_init(void) +{ + spi_init(); + twl3025_switch_page(0); + twl3025_clk13m(1); + twl3025_reg_write(AFCCTLADD, 0x01); /* AFCCK(1:0) must not be zero! */ + twl3025_unit_enable(TWL3025_UNIT_AFC, 1); + + irq_register_handler(IRQ_EXTERNAL, &twl3025_irq); + irq_config(IRQ_EXTERNAL, 0, 0, 0); + irq_enable(IRQ_EXTERNAL); + + irq_register_handler(IRQ_EXTERNAL_FIQ, &twl3025_irq); + irq_config(IRQ_EXTERNAL_FIQ, 1, 0, 0); + irq_enable(IRQ_EXTERNAL_FIQ); +} + +void twl3025_reg_write(uint8_t reg, uint16_t data) +{ + uint16_t tx; + + printd("tw3025_reg_write(%u,%u)=0x%04x\n", REG_PAGE(reg), + REG_ADDR(reg), data); + + if (reg != PAGEREG && REG_PAGE(reg) != twl3025_state.page) + twl3025_switch_page(REG_PAGE(reg)); + + tx = ((data & 0x3ff) << 6) | (REG_ADDR(reg) << 1); + + spi_xfer(TWL3025_DEV_IDX, 16, &tx, NULL); +} + +void twl3025_tsp_write(uint8_t data) +{ + tsp_write(TWL3025_TSP_DEV_IDX, 7, data); +} + +uint16_t twl3025_reg_read(uint8_t reg) +{ + uint16_t tx, rx; + + if (REG_PAGE(reg) != twl3025_state.page) + twl3025_switch_page(REG_PAGE(reg)); + + tx = (REG_ADDR(reg) << 1) | 1; + + /* A read cycle contains two SPI transfers */ + spi_xfer(TWL3025_DEV_IDX, 16, &tx, &rx); + delay_ms(1); + spi_xfer(TWL3025_DEV_IDX, 16, &tx, &rx); + + rx >>= 6; + + printd("tw3025_reg_read(%u,%u)=0x%04x\n", REG_PAGE(reg), + REG_ADDR(reg), rx); + + return rx; +} + +static void twl3025_wait_ibic_access(void) +{ + /* Wait 6 * 32kHz clock cycles for first IBIC access (187us + 10% = 210us) */ + delay_ms(1); +} + +void twl3025_power_off(void) +{ + twl3025_reg_write(VRPCDEV, 0x01); +} + +void twl3025_clk13m(int enable) +{ + if (enable) { + twl3025_reg_write(TOGBR2, TOGBR2_ACTS); + twl3025_wait_ibic_access(); + /* for whatever reason we need to do this twice */ + twl3025_reg_write(TOGBR2, TOGBR2_ACTS); + twl3025_wait_ibic_access(); + } else { + twl3025_reg_write(TOGBR2, TOGBR2_ACTR); + twl3025_wait_ibic_access(); + } +} + +#define TSP_DELAY 6 /* 13* Tclk6M5 = ~ 3 GSM Qbits + 3 TPU instructions */ +#define BDLON_TO_BDLCAL 6 +#define BDLCAL_DURATION 66 +#define BDLON_TO_BDLENA 7 +#define BULON_TO_BULENA 16 + +/* Enqueue a series of TSP commands in the TPU to (de)activate the downlink path */ +void twl3025_downlink(int on, int16_t at) +{ + int16_t bdl_ena = at - TSP_DELAY - 6; + + if (on) { + if (bdl_ena < 0) + printf("BDLENA time negative (%d)\n", bdl_ena); + /* FIXME: calibration should be done just before BDLENA */ + twl3025_tsp_write(BDLON); + tpu_enq_wait(BDLON_TO_BDLCAL - TSP_DELAY); + twl3025_tsp_write(BDLON | BDLCAL); + tpu_enq_wait(BDLCAL_DURATION - TSP_DELAY); + twl3025_tsp_write(BDLON); + //tpu_enq_wait(BDLCAL_TO_BDLENA) this is only 3.7us == 4 qbits, i.e. less than the TSP_DELAY + tpu_enq_at(bdl_ena); + twl3025_tsp_write(BDLON | BDLENA); + } else { + tpu_enq_at(bdl_ena); + twl3025_tsp_write(BDLON); + //tpu_enq_wait(nBDLENA_TO_nBDLON) this is only 3.7us == 4 qbits, i.e. less than the TSP_DELAY + twl3025_tsp_write(0); + } +} + +void twl3025_afc_set(int16_t val) +{ + printf("twl3025_afc_set(%d)\n", val); + + if (val > 4095) + val = 4095; + else if (val <= -4096) + val = -4096; + + /* FIXME: we currently write from the USP rather than BSP */ + twl3025_reg_write(AUXAFC2, val >> 10); + twl3025_reg_write(AUXAFC1, val & 0x3ff); +} + +int16_t twl3025_afc_get(void) +{ + int16_t val; + + val = (twl3025_reg_read(AUXAFC2) & 0x7); + val = val << 10; + val = val | (twl3025_reg_read(AUXAFC1) & 0x3ff); + + if (val > 4095) + val = -(8192 - val); + return val; +} + +void twl3025_unit_enable(enum twl3025_unit unit, int on) +{ + uint16_t togbr1 = 0; + + switch (unit) { + case TWL3025_UNIT_AFC: + if (on) + togbr1 = (1 << 7); + else + togbr1 = (1 << 6); + break; + case TWL3025_UNIT_MAD: + if (on) + togbr1 = (1 << 9); + else + togbr1 = (1 << 8); + break; + case TWL3025_UNIT_ADA: + if (on) + togbr1 = (1 << 5); + else + togbr1 = (1 << 4); + case TWL3025_UNIT_VDL: + if (on) + togbr1 = (1 << 3); + else + togbr1 = (1 << 2); + break; + case TWL3025_UNIT_VUL: + if (on) + togbr1 = (1 << 1); + else + togbr1 = (1 << 0); + break; + } + twl3025_reg_write(TOGBR1, togbr1); +} + +uint8_t twl3025_afcout_get(void) +{ + return twl3025_reg_read(AFCOUT) & 0xff; +} + +void twl3025_afcout_set(uint8_t val) +{ + twl3025_reg_write(AFCCTLADD, 0x05); + twl3025_reg_write(AFCOUT, val); +} diff --git a/src/target/firmware/apps/compal_dsp_dump/main.c b/src/target/firmware/apps/compal_dsp_dump/main.c new file mode 100644 index 00000000..468db235 --- /dev/null +++ b/src/target/firmware/apps/compal_dsp_dump/main.c @@ -0,0 +1,78 @@ +/* main program of Free Software for Calypso Phone */ + +/* (C) 2010 Harald Welte + * (C) 2010 Sylvain Munaut + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* FIXME: We need proper 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; } +} + +/* Main Program */ +const char *hr = "======================================================================\n"; + +void main(void) +{ + int i; + uint16_t twl_reg; + + puts("\n\nCompal DSP data dumper\n"); + puts(hr); + + /* Dump device identification */ + dump_dev_id(); + puts(hr); + + /* Initialize basic board support */ + board_init(); + puts(hr); + + /* Dump DSP content */ + dsp_dump(); + + while (1) {} +} + diff --git a/src/target/firmware/apps/compal_dump/main.c b/src/target/firmware/apps/compal_dump/main.c new file mode 100644 index 00000000..444dfd47 --- /dev/null +++ b/src/target/firmware/apps/compal_dump/main.c @@ -0,0 +1,90 @@ +/* main program of Free Software for Calypso Phone */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +/* FIXME: We need proper 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; } +} + +#define KBIT 1024 +#define MBIT (1024*KBIT) + +#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 + +/* Main Program */ +const char *hr = "======================================================================\n"; + +int main(void) +{ + puts("\n\nCompal device data dumper\n"); + puts(hr); + + /* Disable watchdog (for phones that have it enabled after boot) */ + wdog_enable(0); + + /* Initialize TWL3025 for power control */ + twl3025_init(); + + /* Dump device identification */ + dump_dev_id(); + puts(hr); + + /* Initialize flash, dumping the protection area. */ + cfi_flash_t f; + flash_init(&f, 0x00000000); + flash_dump_info(&f); + puts(hr); + + /* Dump flash contents */ + printf("Dump %d kbytes of external flash\n", f.f_size/1024); + memdump_range((void *)0x00000000, f.f_size); + puts(hr); + + /* Power down */ + twl3025_power_off(); + + while (1) {} +} + diff --git a/src/target/firmware/apps/hello_world/main.c b/src/target/firmware/apps/hello_world/main.c new file mode 100644 index 00000000..7f6aeef9 --- /dev/null +++ b/src/target/firmware/apps/hello_world/main.c @@ -0,0 +1,126 @@ +/* main program of Free Software for Calypso Phone */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* FIXME: We need proper 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; } +} + +/* Main Program */ +const char *hr = "======================================================================\n"; + +void key_handler(enum key_codes code, enum key_states state); + +static void *console_rx_cb(uint8_t dlci, struct msgb *msg) +{ + if (dlci != SC_DLCI_CONSOLE) { + printf("Message for unknown DLCI %u\n", dlci); + return; + } + + printf("Message on console DLCI: '%s'\n", msg->data); + st7558_puts(msg->data); + msgb_free(msg); +} + +int main(void) +{ + board_init(); + puts("\n\nHello World from " __FILE__ " program code\n"); + puts(hr); + /* Dump device identification */ + dump_dev_id(); + puts(hr); + + /* Dump clock config before PLL set */ + calypso_clk_dump(); + puts(hr); + + keypad_set_handler(&key_handler); + + /* Dump clock config aftee PLL set */ + calypso_clk_dump(); + puts(hr); + + /* Dump all memory */ + //dump_mem(); +#if 0 + /* Dump Bootloader */ + memdump_range((void *)0x00000000, 0x2000); + puts(hr); +#endif + + st7558_set_attr(DISP_ATTR_INVERT); + st7558_puts("Hello World"); + + sercomm_register_rx_cb(SC_DLCI_CONSOLE, console_rx_cb); + + /* beyond this point we only react to interrupts */ + puts("entering interrupt loop\n"); + while (1) { + } + + twl3025_power_off(); + + while (1) {} +} + +void key_handler(enum key_codes code, enum key_states state) +{ + if (state != PRESSED) + return; + + switch (code) { + default: + break; + } +} diff --git a/src/target/firmware/apps/l1test/main.c b/src/target/firmware/apps/l1test/main.c new file mode 100644 index 00000000..b19f72c1 --- /dev/null +++ b/src/target/firmware/apps/l1test/main.c @@ -0,0 +1,289 @@ +/* main program of Free Software for Calypso Phone */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#define SCAN + +#ifdef SCAN +/* if scanning is enabled, scan from 0 ... 124 */ +#define BASE_ARFCN 0 +#else +/* fixed ARFCN in GSM1800 at which Harald has his GSM test license */ +#define BASE_ARFCN 871 +#endif + +/* FIXME: We need proper 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; } +} + +/* Main Program */ +const char *hr = "======================================================================\n"; + +/* Best ARFCN MAP ************************************************************/ + +struct arfcn_map { + uint16_t arfcn; + int16_t dbm8; +}; + +static struct arfcn_map best_arfcn_map[10]; +static void best_arfcn_update(uint16_t arfcn, int16_t dbm8) +{ + unsigned int i; + for (i = 0; i < ARRAY_SIZE(best_arfcn_map); i++) { + if (best_arfcn_map[i].dbm8 < dbm8 || + best_arfcn_map[i].dbm8 == 0) { + best_arfcn_map[i].dbm8 = dbm8; + best_arfcn_map[i].arfcn = arfcn; + return; + } + } +} + +static void best_arfcn_dump(void) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(best_arfcn_map); i++) { + if (best_arfcn_map[i].dbm8 == 0) + continue; + printf("ARFCN %3d: %d dBm\n", + best_arfcn_map[i].arfcn, + best_arfcn_map[i].dbm8/8); + } +} + + +/* MAIN program **************************************************************/ + +enum l1test_state { + STATE_NONE, + STATE_PM, + STATE_FB, +}; + +static enum l1test_state l1test_state; + +static void l1test_state_change(enum l1test_state new_state) +{ + switch (new_state) { + case STATE_PM: + puts("Performing power measurement over GSM900\n"); + l1s_pm_test(1, BASE_ARFCN); + break; + case STATE_FB: + puts("Starting FCCH Recognition\n"); + l1s_fb_test(1, 0); + break; + case STATE_NONE: + /* disable frame interrupts */ + tpu_frame_irq_en(0, 0); + break; + } +} + +/* completion call-back for the L1 Sync Pwer Measurement */ +static void l1s_signal_cb(struct l1_signal *sig) +{ + uint16_t i, next_arfcn; + + switch (sig->signum) { + case L1_SIG_PM: + best_arfcn_update(sig->arfcn, sig->pm.dbm8[0]); + next_arfcn = sig->arfcn + 1; + + if (next_arfcn >= 124) { + puts("ARFCN Top 10 Rx Level\n"); + best_arfcn_dump(); + + trf6151_rx_window(0, best_arfcn_map[0].arfcn, 40, 0); + tpu_end_scenario(); + + /* PM phase completed, do FB det */ + l1test_state_change(STATE_FB); + + break; + } + + /* restart Power Measurement */ + l1s_pm_test(1, next_arfcn); + break; + case L1_SIG_NB: + puts("NB SNR "); + for (i = 0; i < 4; i++) { + uint16_t snr = sig->nb.meas[i].snr; + printf("%d.%03u ", l1s_snr_int(snr), l1s_snr_fract(snr)); + } + putchar('\n'); + printf("--> Frame %d %d 0x%04X ", sig->nb.fire, sig->nb.crc, sig->nb.num_biterr); + for (i = 0; i < ARRAY_SIZE(sig->nb.frame); i++) + printf("%02X ", sig->nb.frame[i]); + putchar('\n'); + break; + } +} + +static void key_handler(enum key_codes code, enum key_states state); + +int main(void) +{ + board_init(); + puts("\n\nHello World from " __FILE__ " program code\n"); + + puts(hr); + /* Dump device identification */ + dump_dev_id(); + puts(hr); + + keypad_set_handler(&key_handler); + + /* Dump clock config aftee PLL set */ + calypso_clk_dump(); + puts(hr); + + st7558_set_attr(DISP_ATTR_INVERT); + st7558_puts("l1test.bin"); + + layer1_init(); + l1s_set_handler(&l1s_signal_cb); + + //dsp_checksum_task(); +#ifdef SCAN + l1test_state_change(STATE_PM); +#else + l1test_state_change(STATE_FB); +#endif + tpu_frame_irq_en(1, 1); + + while (1) {} + + /* NOT REACHED */ + + twl3025_power_off(); +} + +static int8_t vga_gain = 40; +static int high_gain = 0; +static int afcout = 0; + +static void update_vga_gain(void) +{ + printf("VGA Gain: %u %s\n", vga_gain, high_gain ? "HIGH" : "LOW"); + trf6151_set_gain(vga_gain, high_gain); + tpu_enq_sleep(); + tpu_enable(1); + tpu_wait_idle(); +} + +static void tspact_toggle(uint8_t num) +{ + printf("TSPACT%u toggle\n", num); + tsp_act_toggle((1 << num)); + tpu_enq_sleep(); + tpu_enable(1); + tpu_wait_idle(); +} + +static void key_handler(enum key_codes code, enum key_states state) +{ + if (state != PRESSED) + return; + + switch (code) { + case KEY_1: /* VGA gain decrement */ + vga_gain -= 2; + if (vga_gain < 14) + vga_gain = 14; + update_vga_gain(); + break; + case KEY_2: /* High/Low Rx gain */ + high_gain ^= 1; + update_vga_gain(); + break; + case KEY_3: /* VGA gain increment */ + vga_gain += 2; + if (vga_gain > 40) + vga_gain = 40; + update_vga_gain(); + break; + case KEY_4: + tspact_toggle(6); /* TRENA (RFFE) */ + break; + case KEY_5: + tspact_toggle(8); /* GSM_TXEN (RFFE) */ + break; + case KEY_6: + tspact_toggle(1); /* PAENA (RFFE) */ + break; + case KEY_7: /* decrement AFC OUT */ + afcout -= 100; + if (afcout < -4096) + afcout = -4096; + twl3025_afc_set(afcout); + printf("AFC OUT: %u\n", twl3025_afcout_get()); + break; + case KEY_9: /* increase AFC OUT */ + afcout += 100; + if (afcout > 4095) + afcout = 4095; + twl3025_afc_set(afcout); + printf("AFC OUT: %u\n", twl3025_afcout_get()); + break; + default: + break; + } +} diff --git a/src/target/firmware/apps/layer1/main.c b/src/target/firmware/apps/layer1/main.c new file mode 100644 index 00000000..3b1b686a --- /dev/null +++ b/src/target/firmware/apps/layer1/main.c @@ -0,0 +1,214 @@ +/* main program of Free Software for Calypso Phone */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include + +/* FIXME: We need proper 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; } +} + +const char *hr = "======================================================================\n"; + +/* MAIN program **************************************************************/ + +/* completion call-back for the L1 Sync Pwer Measurement */ +static void l1s_signal_cb(struct l1_signal *sig) +{ + uint16_t i, next_arfcn; + + switch (sig->signum) { + case L1_SIG_PM: + break; + case L1_SIG_NB: + break; + } +} + +static void key_handler(enum key_codes code, enum key_states state); +static void la1_l23_rx_cb(uint8_t dlci, struct msgb *msg); + +int main(void) +{ + board_init(); + puts("\n\nHello World from " __FILE__ " program code\n"); + + puts(hr); + /* Dump device identification */ + dump_dev_id(); + puts(hr); + + keypad_set_handler(&key_handler); + + /* Dump clock config aftee PLL set */ + calypso_clk_dump(); + puts(hr); + + st7558_set_attr(DISP_ATTR_INVERT); + st7558_puts("layer1.bin"); + + sercomm_register_rx_cb(SC_DLCI_L1A_L23, la1_l23_rx_cb); + + layer1_init(); + l1s_set_handler(&l1s_signal_cb); + + tpu_frame_irq_en(1, 1); + + while (1) {} + + /* NOT REACHED */ + + twl3025_power_off(); +} + +static int8_t vga_gain = 40; +static int high_gain = 0; +static int afcout = 0; + +static void update_vga_gain(void) +{ + printf("VGA Gain: %u %s\n", vga_gain, high_gain ? "HIGH" : "LOW"); + trf6151_set_gain(vga_gain, high_gain); + tpu_enq_sleep(); + tpu_enable(1); + tpu_wait_idle(); +} + +static void tspact_toggle(uint8_t num) +{ + printf("TSPACT%u toggle\n", num); + tsp_act_toggle((1 << num)); + tpu_enq_sleep(); + tpu_enable(1); + tpu_wait_idle(); +} + +static void key_handler(enum key_codes code, enum key_states state) +{ + if (state != PRESSED) + return; + + switch (code) { + case KEY_1: /* VGA gain decrement */ + vga_gain -= 2; + if (vga_gain < 14) + vga_gain = 14; + update_vga_gain(); + break; + case KEY_2: /* High/Low Rx gain */ + high_gain ^= 1; + update_vga_gain(); + break; + case KEY_3: /* VGA gain increment */ + vga_gain += 2; + if (vga_gain > 40) + vga_gain = 40; + update_vga_gain(); + break; + case KEY_4: + tspact_toggle(6); /* TRENA (RFFE) */ + break; + case KEY_5: + tspact_toggle(8); /* GSM_TXEN (RFFE) */ + break; + case KEY_6: + tspact_toggle(1); /* PAENA (RFFE) */ + break; + case KEY_7: /* decrement AFC OUT */ + afcout -= 100; + if (afcout < -4096) + afcout = -4096; + twl3025_afc_set(afcout); + printf("AFC OUT: %u\n", twl3025_afcout_get()); + break; + case KEY_9: /* increase AFC OUT */ + afcout += 100; + if (afcout > 4095) + afcout = 4095; + twl3025_afc_set(afcout); + printf("AFC OUT: %u\n", twl3025_afcout_get()); + break; + default: + break; + } +} + +static void la1_l23_rx_cb(uint8_t dlci, struct msgb *msg) +{ + struct l1_info_ul *ul = msg->data; + struct l1_sync_new_ccch_req *sync_req; + + if (sizeof(*ul) > msg->len) { + printf("la1_l23_cb: Short message. %u\n", msg->len); + goto exit; + } + + switch (ul->msg_type) { + case SYNC_NEW_CCCH_REQ: + if (sizeof(*ul) + sizeof(*sync_req) > msg->len) { + printf("Short sync msg. %u\n", msg->len); + break; + } + + sync_req = (struct l1_sync_new_ccch_req *) (&msg->data[0] + sizeof(*ul)); + printf("Asked to tune to frequency: %u\n", sync_req->band_arfcn); + break; + case DEDIC_MODE_EST_REQ: + break; + } + +exit: + msgb_free(msg); +} diff --git a/src/target/firmware/board/common/compal_ramload.lds b/src/target/firmware/board/common/compal_ramload.lds new file mode 100644 index 00000000..ae791c5d --- /dev/null +++ b/src/target/firmware/board/common/compal_ramload.lds @@ -0,0 +1,83 @@ +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) +ENTRY(_start) +MEMORY +{ + /* area that can be initialized by the loader (plus some reserved stuff) */ + LRAM (rw) : ORIGIN = 0x00800000, LENGTH = 0x00010000 + /* remainder of internal ram, can be used for bss and the like */ + IRAM (rw) : ORIGIN = 0x00810000, LENGTH = 0x00030000 + /* external ram on a C123 */ + ERAM (rw) : ORIGIN = 0x01000000, LENGTH = 0x00040000 +} +SECTIONS +{ + . = 0x800000; + + /* reserved (what is in here?) */ + .compal.reserved1 (NOLOAD) : { . = 0x100; } > LRAM + + /* XXX: leftovers from exception vector trickery development? */ + /* .compal.reserved1 (NOLOAD) : { . = 0x1C; } > LRAM */ + /* .compal.reserved2 (NOLOAD) : { . = 0xC8; } > LRAM */ + + /* image signature (prepended by compal_dnload according to phone type) */ + .compal.header (NOLOAD) : { . = 4; } > LRAM + + /* code */ + . = ALIGN(4); + .text_start : { + /* initialization code */ + PROVIDE(_start = .); + KEEP(*(.init)) + *(.text._start) + _exceptions = .; + } > LRAM + + /* exception vectors from 0x80001c to 0x800034 */ + .text.exceptions 0x80001c: AT (LOADADDR(.text_start) + SIZEOF(.text_start)) { + KEEP(*(.text.exceptions)) + * (.text.exceptions) + . = ALIGN(4); + } > LRAM + + /* code */ + . = ALIGN(4); + .text (LOADADDR(.text.exceptions) + SIZEOF(.text.exceptions)) : + AT (LOADADDR(.text.exceptions) + SIZEOF(.text.exceptions)) { + /* regular code */ + *(.text*) + } > LRAM + + /* read-only data */ + . = ALIGN(4); + .rodata : { + *(.rodata) + } > LRAM + + /* initialized data */ + . = ALIGN(4); + .data : { + *(.data) + } > LRAM + + /* pic offset tables */ + . = ALIGN(4); + .got : { + *(.got) + } > LRAM + + /* uninitialized data */ + .bss (NOLOAD) : { + . = ALIGN(4); + __bss_start = .; + *(.bss) + } > IRAM + . = ALIGN(4); + __bss_end = .; + + /* end of image */ + . = ALIGN(4); + _end = .; + PROVIDE(end = .); +} diff --git a/src/target/firmware/board/common/compal_ramload_start.S b/src/target/firmware/board/common/compal_ramload_start.S new file mode 100644 index 00000000..4f6fadef --- /dev/null +++ b/src/target/firmware/board/common/compal_ramload_start.S @@ -0,0 +1,257 @@ + +#define BA_UART_MODEM 0xFFFF5800 + +# + +.macro senduart, rd, rx + strb \rd, [\rx, #0] +.endm + +.macro busyuart, rd, rx +1001: + @busy waiting until THR is empty + ldrb \rd, [\rx, #5] @ read LSR register + mov \rd, \rd, lsr #6 + tst \rd, #1 + beq 1001b +.endm + +.macro loadsp, rd + ldr \rd, =BA_UART_MODEM +.endm + + + .EQU ARM_MODE_FIQ, 0x11 + .EQU ARM_MODE_IRQ, 0x12 + .EQU ARM_MODE_SVC, 0x13 + + .EQU I_BIT, 0x80 + .EQU F_BIT, 0x40 + +#define TOP_OF_RAM 0x083fff0 +#define FIQ_STACK_SIZE 1024 +#define IRQ_STACK_SIZE 1024 + +.section .text._start +.globl _start +_start: + @ clear bss section + .global __bss_start + .global __bss_end + mov r0, #0 + ldr r1, =__bss_start + ldr r2, =__bss_end +2: cmp r1, r2 + strlo r0, [r1], #4 + blo 2b + + ldr r0, =TOP_OF_RAM + /* initialize FIQ stack */ + msr CPSR_c, #ARM_MODE_FIQ | I_BIT | F_BIT + mov r13, r0 + sub r0, r0, #FIQ_STACK_SIZE + + /* initialize IRQ stack */ + msr CPSR_c, #ARM_MODE_IRQ | I_BIT | F_BIT + mov r13, r0 + sub r0, r0, #IRQ_STACK_SIZE + + /* initialize supervisor stack */ + msr CPSR_c, #ARM_MODE_SVC | I_BIT | F_BIT + mov r13, r0 + + @ set backlight to moderate level + bl pwl_init + mov r0, #50 + bl pwl_set_level + + @ldr r0, =string + @bl puts_asm + + @ some memory dumps + @ldr r0, =0xfffef000 + @bl memdump + @ldr r0, =0xfffffe00 + @bl memdump + + ldr pc, _jump_main + + @ endless loop at end of program +_end: b _end + b _start + +_jump_main: .word main + +string: .word 0x6c6c6548 + .word 0x6f57206f + .word 0x00646c72 + +foo: .word 0xee4c9f63 +bar: .word 0x639f4cee + + .align 2 + .type phexbuf, #object +phexbuf: .space 12 + .size phexubf, . - phexbuf + +.globl phex +phex: adr r3, phexbuf + mov r2, #0 + strb r2, [r3, r1] +1: subs r1, r1, #1 + movmi r0, r3 + bmi puts_asm + and r2, r0, #15 + mov r0, r0, lsr #4 + cmp r2, #10 + addge r2, r2, #7 + add r2, r2, #'0' + strb r2, [r3, r1] + b 1b + +puts_asm: loadsp r3 +1: ldrb r2, [r0], #1 + teq r2, #0 + moveq pc, lr +2: senduart r2, r3 + busyuart r1, r3 + teq r2, #'\n' + moveq r2, #'\r' + beq 2b + teq r0, #0 + bne 1b + mov pc, lr +.globl putchar_asm +putchar_asm: + mov r2, r0 + mov r0, #0 + loadsp r3 + b 2b + +memdump: mov r12, r0 + mov r10, lr + mov r11, #0 +2: mov r0, r11, lsl #2 + add r0, r0, r12 + mov r1, #8 + bl phex + mov r0, #':' + bl putchar_asm +1: mov r0, #' ' + bl putchar_asm + ldr r0, [r12, r11, lsl #2] + mov r1, #8 + bl phex + and r0, r11, #7 + teq r0, #3 + moveq r0, #' ' + bleq putchar_asm + and r0, r11, #7 + add r11, r11, #1 + teq r0, #7 + bne 1b + mov r0, #'\n' + bl putchar_asm + cmp r11, #64 + blt 2b + mov pc, r10 + + +#define ASIC_CONF_REG 0xfffef008 +#define BA_PWL 0xfffe8000 + +pwl_init: ldr r1, =ASIC_CONF_REG + ldr r2, [r1] + orr r2, r2, #0x10 @ set light output to PWL + str r2, [r1] + ldr r1, =BA_PWL + mov r0, #1 + strb r0, [r1, #1] @ enable clock of PWL unut + mov pc, lr + +pwl_set_level: ldr r1, =BA_PWL + strb r0, [r1] + mov pc, lr + +handle_abort: + @ print the PC we would jump back to... + sub lr, lr, #4 @ we assume to be ARM32 + + mov r0, lr + mov r1, #8 + bl phex + + @ print abort message + mov r0, #'A' + bl putchar_asm + mov r0, #'B' + bl putchar_asm + mov r0, #'O' + bl putchar_asm + mov r0, #'R' + bl putchar_asm + mov r0, #'T' + bl putchar_asm +0: @ dead + b 0b + +irq_entry: + /* Adjust and save LR_irq in IRQ stack */ + sub lr, lr, #4 + stmfd sp!, {lr} + + /* Save SPSR for nested interrupt */ + mrs r14, SPSR + stmfd sp!, {r14} + + /* Call the interrupt handler C function */ + stmfd sp!, {r0-r4, r12} + bl irq + ldmfd sp!, {r0-r4, r12} + + /* Restore SPSR_irq from IRQ stack */ + ldmia sp!, {r14} + msr SPSR_cxsf, r14 + + /* Restore adjusted LR_irq from IRQ stack directly in the PC */ + ldmia sp!, {pc}^ + +fiq_entry: + /* Adjust and save LR_irq in IRQ stack */ + sub lr, lr, #4 + stmfd sp!, {lr} + + /* Save SPSR for nested interrupt */ + mrs r14, SPSR + stmfd sp!, {r14} + + /* Call the interrupt handler C function */ + stmfd sp!, {r0-r4, r12} + bl fiq + ldmfd sp!, {r0-r4, r12} + + /* Restore SPSR_irq from IRQ stack */ + ldmia sp!, {r14} + msr SPSR_cxsf, r14 + + /* Restore adjusted LR_irq from IRQ stack directly in the PC */ + ldmia sp!, {pc}^ + +/* Exception Vectors like they are needed for the exception vector + indirection of the internal boot ROM. The following section must be liked + to appear at 0x80'001c */ +.section .text.exceptions +_undef_instr: + b handle_abort +_sw_interr: + b _sw_interr +_prefetch_abort: + b handle_abort +_data_abort: + b handle_abort +_reserved: + b _reserved +_irq: + b irq_entry +_fiq: + b fiq_entry diff --git a/src/target/firmware/board/common/rffe_compal_dualband.c b/src/target/firmware/board/common/rffe_compal_dualband.c new file mode 100644 index 00000000..b796c6f5 --- /dev/null +++ b/src/target/firmware/board/common/rffe_compal_dualband.c @@ -0,0 +1,70 @@ +#include +#include + +#include +#include +#include +#include +#include + +/* describe how the RF frontend is wired on the Motorola E88 board (C117/C118/C121/C123) */ + +#define RITA_RESET TSPACT(0) /* Reset of the Rita TRF6151 */ +#define PA_ENABLE TSPACT(1) /* Enable the Power Amplifier */ +#define TRENA TSPACT(6) /* Transmit Enable (Antenna Switch) */ +#define GSM_TXEN TSPACT(8) /* GSM (as opposed to DCS) Transmit */ + +#define IOTA_STROBE TSPEN0 /* Strobe for the Iota TSP */ +#define RITA_STROBE TSPEN2 /* Strobe for the Rita TSP */ + +/* switch RF Frontend Mode */ +void rffe_mode(enum gsm_band band, int tx) +{ + uint16_t tspact = tsp_act_state(); + + /* First we mask off all bits from the state cache */ + tspact &= ~PA_ENABLE; + tspact |= TRENA | GSM_TXEN; /* low-active */ + + /* Then we selectively set the bits on, if required */ + if (tx) { + tspact &= ~TRENA; + if (band == GSM_900) + tspact &= ~GSM_TXEN; + } + + tsp_act_update(tspact); +} + +#define MCU_SW_TRACE 0xfffef00e +#define ARM_CONF_REG 0xfffef006 + +void rffe_init(void) +{ + uint16_t reg; + + reg = readw(ARM_CONF_REG); + reg &= ~ (1 << 5); /* TSPACT6 I/O function, not nCS6 */ + writew(reg, ARM_CONF_REG); + + reg = readw(MCU_SW_TRACE); + reg &= ~(1 << 5); /* TSPACT8 I/O function, not nMREQ */ + writew(reg, MCU_SW_TRACE); +} + +uint8_t rffe_get_gain(void) +{ + return trf6151_get_gain(); +} + +/* Given the expected input level of exp_inp dBm/8 and the target of target_bb + * dBm8, configure the RF Frontend with the respective gain */ +void rffe_set_gain(int16_t exp_inp, int16_t target_bb) +{ + +} + +void rffe_rx_win_ctrl(int16_t exp_inp, int16_t target_bb) +{ + +} diff --git a/src/target/firmware/board/compal_e88/init.c b/src/target/firmware/board/compal_e88/init.c new file mode 100644 index 00000000..f51f3e8d --- /dev/null +++ b/src/target/firmware/board/compal_e88/init.c @@ -0,0 +1,126 @@ +/* Initialization for the Compal E88 (Motorola C115...C123) */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#define ARMIO_LATCH_OUT 0xfffe4802 +#define ARMIO_CNTL_REG 0xfffe4804 +#define ASIC_CONF_REG 0xfffef008 + +static void board_io_init(void) +{ + uint16_t reg; + + reg = readw(ASIC_CONF_REG); + /* LCD Set I/O(3) / SA0 to I/O(3) mode */ + reg &= ~(1 << 10); + /* Set function pins to I2C Mode */ + reg |= 0x1080; /* SCL / SDA */ + /* TWL3025: Set SPI+RIF RX clock to rising edge */ + reg |= (1 << 13) | (1 << 14); + writew(reg, ASIC_CONF_REG); + + /* LCD Set I/O(3) to output mode */ + reg = readw(ARMIO_CNTL_REG); + reg &= ~(1 << 3); + writew(reg, ARMIO_CNTL_REG); + + /* LCD Set I/O(3) output low */ + reg = readw(ARMIO_LATCH_OUT); + reg &= ~(1 << 3); + writew(reg, ARMIO_LATCH_OUT); +} + +void board_init(void) +{ + /* FIXME: this needs to go to board_e99/init.c once we have it */ + wdog_enable(0); + + static cfi_flash_t flash; + // XXX: move after mapping initialization and use final address + flash_init(&flash, 0x00000000); + + calypso_mem_cfg(CALYPSO_nCS0, 3, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS1, 3, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS2, 5, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS3, 5, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_CS4, 0, CALYPSO_MEM_8bit, 1); + calypso_mem_cfg(CALYPSO_nCS6, 0, CALYPSO_MEM_32bit, 1); + calypso_mem_cfg(CALYPSO_nCS7, 0, CALYPSO_MEM_32bit, 0); + + /* Set VTCXO_DIV2 = 1, configure PLL for 104 MHz and give ARM half of that */ + calypso_clock_set(2, CALYPSO_PLL13_104_MHZ, ARM_MCLK_DIV_2); + + /* Configure the RHEA bridge with some sane default values */ + calypso_rhea_cfg(0, 0, 0xff, 0, 1, 0, 0); + + board_io_init(); + + /* Enable bootrom mapping to route exception vectors to RAM */ + calypso_bootrom(1); + calypso_exceptions_install(); + + irq_init(); + + /* initialize MODEM UART to be used for sercomm*/ + uart_init(SERCOMM_UART_NR); + uart_baudrate(SERCOMM_UART_NR, UART_115200); + + /* initialize IRDA UART to be used for old-school console code. + * note: IRDA uart only accessible on C115 and C117 PCB */ + uart_init(CONS_UART_NR); + uart_baudrate(CONS_UART_NR, UART_115200); + + hwtimer_init(); + + dma_init(); + rtc_init(); + + /* Initialize LCD driver (uses I2C) */ + st7558_init(); + + keypad_init(); + + /* Initialize ABB driver (uses SPI) */ + twl3025_init(); +} diff --git a/src/target/firmware/calypso/Makefile b/src/target/firmware/calypso/Makefile new file mode 100644 index 00000000..a2be3cfa --- /dev/null +++ b/src/target/firmware/calypso/Makefile @@ -0,0 +1,17 @@ +INCLUDES=-I../include/ +-include ../Makefile.inc + +OBJS=arm.o clock.o dma.o dsp.o du.o i2c.o irq.o rtc.o spi.o tpu.o tsp.o keypad.o misc.o timer.o backlight.o uart.o + +LST=$(OBJS:.o=.lst) + +all: libcalypso.a + +%.o: %.c + $(CROSS_COMPILE)$(CC) $(CFLAGS) -c -o $@ $^ + +libcalypso.a: $(OBJS) + $(CROSS_COMPILE)$(AR) cru $@ $^ + +clean: + rm -f *.a $(OBJS) $(LST) 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..e2ff29cc --- /dev/null +++ b/src/target/firmware/calypso/backlight.c @@ -0,0 +1,67 @@ +/* Calypso DBB internal PWL (Pulse Width / Light) Driver */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#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 = 2, +}; + +#define ASCONF_PWL_ENA (1 << 4) + +void bl_mode_pwl(int on) +{ + uint16_t reg; + + reg = readw(ASIC_CONF_REG); + + if (on) { + 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); + 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/clock.c b/src/target/firmware/calypso/clock.c new file mode 100644 index 00000000..d5b2c090 --- /dev/null +++ b/src/target/firmware/calypso/clock.c @@ -0,0 +1,202 @@ +/* Driver for Calypso clock management */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +//#define DEBUG +#include + +#include +#include + +#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); + + // XXX: this can't be correct + if (enable) + conf |= (1 << 8); + else + conf |= (1 << 8); + 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/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 + * + * 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 + +#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..59176567 --- /dev/null +++ b/src/target/firmware/calypso/dsp.c @@ -0,0 +1,391 @@ +#define DEBUG +/* Driver for the Calypso integrated DSP */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 form 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 mirrot */ +#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 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; isize; 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"); + writew(0, BL_SIZE); + writew(DSP_START, BL_ADDR_LO); + writew(BL_CMD_COPY_BLOCK, BL_CMD_STATUS); + + 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; +} + +static void dsp_ndb_init(void) +{ + T_NDB_MCU_DSP *ndb = dsp_api.ndb; + + 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<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<a_fd[2] = 0xffff; + ndb->d_a5mode = 0; + ndb->d_tch_mode = 0x0800; + /* FIXME: set guard bits */ + ndb->a_sch26[0] = (1<d_spcx_rif = 0x179; +} + +static void dsp_db_init(void) +{ + dsp_api_memset((void *)BASE_API_W_PAGE_0, sizeof(T_DB_MCU_TO_DSP)); + dsp_api_memset((void *)BASE_API_W_PAGE_1, sizeof(T_DB_MCU_TO_DSP)); + dsp_api_memset((void *)BASE_API_R_PAGE_0, sizeof(T_DB_DSP_TO_MCU)); + dsp_api_memset((void *)BASE_API_R_PAGE_1, sizeof(T_DB_DSP_TO_MCU)); +} + +void dsp_power_on(void) +{ + 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; +} + +#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--) { + if ((addr&15)==0) + printf("%05ux : ", 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); + + writew(0, BL_ADDR_HI); + writew(DSP_DUMPCODE_START, BL_ADDR_LO); + writew(0, BL_SIZE); + writew(BL_CMD_COPY_BLOCK, BL_CMD_STATUS); + + /* our dump code actually simulates the boot loaded + * 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 [%05ux-%05ux]\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..ec44a0ed --- /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 = 7172, + .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 + * + * 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 +#include +#include + +#include + +#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..a46fd72a --- /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 + * + * 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 +#include + +#include +#include +#include + +#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) +#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 controler 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) + 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..d019918d --- /dev/null +++ b/src/target/firmware/calypso/irq.c @@ -0,0 +1,266 @@ +/* Driver for Calypso IRQ controller */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#include +#include +#include +#include + +#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 irq) +{ + uint8_t our_prio = readb(IRQ_REG(ILR_IRQ(irq))) >> 2; + int i; + + for (i = 0; i < _NR_IRQ; i++) { + uint8_t prio; + + if (i == irq) + 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..27f48c0b --- /dev/null +++ b/src/target/firmware/calypso/keypad.c @@ -0,0 +1,165 @@ +/* Driver for the keypad attached to the TI Calypso */ + +/* (C) 2010 by roh + * + * 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 +#include + +#include +#include +#include +#include + +#include +#include + + +#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; + +#define BTN_TO_KEY(name) \ + ((diff & BTN_##name) == BTN_##name) \ + { \ + key = KEY_##name; \ + diff = diff & ~BTN_##name; \ + } + +void dispatch_buttons(uint32_t buttons) +{ + uint8_t state; + + if (buttons == lastbuttons) + return; + + if (buttons > lastbuttons) + state = PRESSED; + else + state = RELEASED; + + 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; + } + if ( key == KEY_POWER ) + diff = 0; + emit_key(key, state); + } + lastbuttons = buttons; +} + +static void keypad_irq(enum irq_nr nr) +{ + keypad_scan(); +} + +void keypad_init() +{ + lastbuttons = 0; + writew(0, KBD_GPIO_MASKIT); + writew(0, KBC_REG); + + 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_scan() +{ + uint16_t reg; + uint16_t col; + uint32_t buttons; + +// putchar('\n'); + buttons = 0x0; + //scan for BTN_POWER + writew(0xff, KBC_REG); + delay_ms(1); + reg = readw(KBR_LATCH_REG); +// printd("%02x ", (~reg & 0x1f)); + buttons = buttons | ((~reg & 0x1f) << 20 ); + + //scan for muxed keys if not powerbtn + if ((~reg & 0x1f) != 0x10) + for (col=0;col<4;col++) + { + writew(0x1f & ~(0x1 << col ), KBC_REG); + delay_ms(1); + reg = readw(KBR_LATCH_REG); + buttons = buttons | ((~reg & 0x1f) << (col * 5 )); +// printd("%02x ", (~reg & 0x1f)); + } + //enable keypad irq via master 'or' gate (needs col lines low in idle to work) + writew(0, KBC_REG); + dispatch_buttons(buttons); + +} + 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 +#include +#include + +/* 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..afa3824b --- /dev/null +++ b/src/target/firmware/calypso/rtc.c @@ -0,0 +1,82 @@ +/* Driver for Calypso RTC controller */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#include +#include +#include +#include + +#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(enum irq_nr nr) +{ + if (tick_ctr & 1) + st7558_set_attr(DISP_ATTR_INVERT); + else + st7558_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/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 + * + * 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 +#include + +//#define DEBUG +#include + +#include +#include +#include + +#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..050c7c93 --- /dev/null +++ b/src/target/firmware/calypso/timer.c @@ -0,0 +1,127 @@ +/* Calypso DBB internal Timer Driver */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include +#include + +#include +#include + +#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, +}; + +static void wdog_irq(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(0x8000, WDOG_REG(WD_MODE)); + } else { + writew(0xF5, WDOG_REG(WD_MODE)); + writew(0xA0, WDOG_REG(WD_MODE)); + } +} + diff --git a/src/target/firmware/calypso/tpu.c b/src/target/firmware/calypso/tpu.c new file mode 100644 index 00000000..f0172e2e --- /dev/null +++ b/src/target/firmware/calypso/tpu.c @@ -0,0 +1,288 @@ +/* Calypso DBB internal TPU (Time Processing Unit) Driver */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#include +#include +#include +#include +#include + +#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), +}; + +#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); + 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 teh 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; +} + +static uint16_t *tpu_ptr; + +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) +{ + /* Get TPU out of reset */ + tpu_reset(1); + tpu_clk_enable(1); + 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(uint16_t a, uint16_t b) +{ + int32_t sum = (uint32_t)a + (uint32_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 + * + * 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 +#include + +#include +#include +#include +#include + +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..7e68edc3 --- /dev/null +++ b/src/target/firmware/calypso/uart.c @@ -0,0 +1,396 @@ +/* Calypso DBB internal UART Driver */ + +/* (C) 2010 by Harald Welte + * (C) 2010 by Ingo Albrecht + * + * 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 + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#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) +{ + old_lcr = readb(UART_REG(uart, LCR)); + + if (on) + 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(enum irq_nr irq) +{ + 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(enum irq_nr irq) +{ + 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: + break; + case IIR_INT_TYPE_RX_STATUS_ERROR: + break; + case IIR_INT_TYPE_XOFF: + break; + } +} + +static const uint8_t uart2irq[] = { + [0] = IRQ_UART_IRDA, + [1] = IRQ_UART_MODEM, +}; + +void uart_init(uint8_t uart) +{ + uint8_t irq = uart2irq[uart]; + + uart_reg_write(uart, IER, 0x00); + if (uart == CONS_UART_NR) { + cons_init(); + irq_register_handler(irq, &uart_irq_handler_cons); + irq_config(irq, 0, 0, 0xff); + irq_enable(irq); + } else { + sercomm_init(); + 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 + /* 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_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) +{ + if (!(readb(UART_REG(uart, LSR)) & 0x01)) + return 0; + + *ch = readb(UART_REG(uart, RHR)); + 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/comm/Makefile b/src/target/firmware/comm/Makefile new file mode 100644 index 00000000..c2c6fcf0 --- /dev/null +++ b/src/target/firmware/comm/Makefile @@ -0,0 +1,26 @@ +INCLUDES=-I../include/ +-include ../Makefile.inc + +LIBNAME=comm +CSRCS=msgb.c sercomm.c sercomm_cons.c +SSRCS= + +COBJS=$(CSRCS:.c=.o) +SOBJS=$(SSRCS:.S=.o) +OBJS=$(COBJS) $(SOBJS) + +LST=$(OBJS:.o=.lst) + +all: lib$(LIBNAME).a + +$(COBJS): %.o : %.c + $(CROSS_COMPILE)$(CC) $(CFLAGS) -c -o $@ $^ + +$(SOBJS): %.o : %.S + $(CROSS_COMPILE)$(CC) $(ASFLAGS) -c -o $@ $^ + +lib$(LIBNAME).a: $(OBJS) + $(CROSS_COMPILE)$(AR) cru $@ $^ + +clean: + rm -f *.a $(OBJS) $(LST) diff --git a/src/target/firmware/comm/msgb.c b/src/target/firmware/comm/msgb.c new file mode 100644 index 00000000..8245ed20 --- /dev/null +++ b/src/target/firmware/comm/msgb.c @@ -0,0 +1,134 @@ +/* (C) 2008-2010 by Harald Welte + * 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 +#include +#include +#include + +#include + +#include + +#include + +#define NO_TALLOC + +void *tall_msgb_ctx; + +#ifdef NO_TALLOC +/* This is a poor mans static allocator for msgb objects */ +#define MSGB_DATA_SIZE 256 +#define MSGB_NUM 16 +struct supermsg { + uint8_t allocated; + struct msgb msg; + uint8_t buf[MSGB_DATA_SIZE]; +}; +static struct supermsg msgs[MSGB_NUM]; +static void *_talloc_zero(void *ctx, unsigned int size, const char *name) +{ + unsigned int i; + if (size > sizeof(struct msgb) + MSGB_DATA_SIZE) + goto panic; + for (i = 0; i < ARRAY_SIZE(msgs); i++) { + if (!msgs[i].allocated) { + msgs[i].allocated = 1; + memset(&msgs[i].msg, 0, sizeof(&msgs[i].msg)); + memset(&msgs[i].buf, 0, sizeof(&msgs[i].buf)); + return &msgs[i].msg; + } + } + +panic: + while (1) { + bl_level(++i % 50); + delay_ms(50); + } + return NULL; +} +static void talloc_free(void *msg) +{ + struct supermsg *smsg = container_of(msg, struct supermsg, msg); + smsg->allocated = 0; +} +#endif + +struct msgb *msgb_alloc(uint16_t size, const char *name) +{ + struct msgb *msg; + + msg = _talloc_zero(tall_msgb_ctx, sizeof(*msg) + size, name); + + if (!msg) { + cons_puts("unable to allocate msgb\n"); + return NULL; + } + + msg->data_len = size; + msg->len = 0; + msg->data = msg->_data; + + msg->head = msg->data; + msg->data = msg->data; + /* reset tail pointer */ + msg->tail = msg->data; + + return msg; +} + +void msgb_free(struct msgb *m) +{ + talloc_free(m); +} + +void msgb_enqueue(struct llist_head *queue, struct msgb *msg) +{ + llist_add_tail(&msg->list, queue); +} + +struct msgb *msgb_dequeue(struct llist_head *queue) +{ + struct llist_head *lh; + + if (llist_empty(queue)) + return NULL; + + lh = queue->next; + llist_del(lh); + + return llist_entry(lh, struct msgb, list); +} + +void msgb_reset(struct msgb *msg) +{ + msg->len = 0; + msg->len = 0; + msg->data = msg->_data; + + msg->head = msg->data; + msg->data = msg->data; + /* reset tail pointer */ + msg->tail = msg->data; + + /* reset pointers */ + msg->l2h = NULL; + msg->l3h = NULL; +} diff --git a/src/target/firmware/comm/sercomm.c b/src/target/firmware/comm/sercomm.c new file mode 100644 index 00000000..d852eb99 --- /dev/null +++ b/src/target/firmware/comm/sercomm.c @@ -0,0 +1,244 @@ +/* Serial communications layer, based on HDLC */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include +#include + +#ifdef HOST_BUILD +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) +#endif +#include +#include + +#else +#include +#include + +#include +#include +#include +#endif + +#define SERCOMM_RX_MSG_SIZE 256 + +enum rx_state { + RX_ST_WAIT_START, + RX_ST_ADDR, + RX_ST_CTRL, + RX_ST_DATA, + RX_ST_ESCAPE, +}; + +static struct { + int initialized; + + /* transmit side */ + struct { + struct llist_head dlci_queues[_SC_DLCI_MAX]; + struct msgb *msg; + uint8_t *next_char; + } tx; + + /* receive side */ + struct { + dlci_cb_t dlci_handler[_SC_DLCI_MAX]; + struct msgb *msg; + enum rx_state state; + uint8_t dlci; + uint8_t ctrl; + } rx; + +} sercomm; + +void sercomm_init(void) +{ + unsigned int i; + for (i = 0; i < ARRAY_SIZE(sercomm.tx.dlci_queues); i++) + INIT_LLIST_HEAD(&sercomm.tx.dlci_queues[i]); + + sercomm.rx.msg = NULL; + sercomm.initialized = 1; +} + +int sercomm_initialized(void) +{ + return sercomm.initialized; +} + +/* user interface for transmitting messages for a given DLCI */ +void sercomm_sendmsg(uint8_t dlci, struct msgb *msg) +{ + uint8_t *hdr; + + /* prepend address + control octet */ + hdr = msgb_push(msg, 2); + hdr[0] = dlci; + hdr[1] = HDLC_C_UI; + msgb_enqueue(&sercomm.tx.dlci_queues[dlci], msg); + +#ifndef HOST_BUILD + /* tell UART that we have something to send */ + uart_irq_enable(SERCOMM_UART_NR, UART_IRQ_TX_EMPTY, 1); +#endif +} + +/* how deep is the Tx queue for a given DLCI */ +unsigned int sercomm_tx_queue_depth(uint8_t dlci) +{ + struct llist_head *le; + unsigned int num = 0; + + llist_for_each(le, &sercomm.tx.dlci_queues[dlci]) { + num++; + } + + return num; +} + +/* fetch one octet of to-be-transmitted serial data */ +int sercomm_drv_pull(uint8_t *ch) +{ + if (!sercomm.tx.msg) { + unsigned int i; + /* dequeue a new message from the queues */ + for (i = 0; i < ARRAY_SIZE(sercomm.tx.dlci_queues); i++) { + sercomm.tx.msg = msgb_dequeue(&sercomm.tx.dlci_queues[i]); + if (sercomm.tx.msg) + break; + } + if (sercomm.tx.msg) { + /* start of a new message, send start flag octet */ + *ch = HDLC_FLAG; + sercomm.tx.next_char = sercomm.tx.msg->data; + return 1; + } else { + /* no more data avilable */ + return 0; + } + } + + /* escaping for the two control octets */ + if (*sercomm.tx.next_char == HDLC_FLAG || + *sercomm.tx.next_char == HDLC_ESCAPE) { + /* send an escape octet */ + *ch = HDLC_ESCAPE; + /* invert bit 5 of the next octet to be sent */ + *sercomm.tx.next_char ^= (1 << 5); + } else if (sercomm.tx.next_char == sercomm.tx.msg->tail) { + /* last character has already been transmitted, + * send end-of-message octet */ + *ch = HDLC_FLAG; + /* we've reached the end of the message buffer */ + msgb_free(sercomm.tx.msg); + sercomm.tx.msg = NULL; + sercomm.tx.next_char = NULL; + } else { + /* standard case, simply send next octet */ + *ch = *sercomm.tx.next_char++; + } + return 1; +} + +/* register a handler for a given DLCI */ +int sercomm_register_rx_cb(uint8_t dlci, dlci_cb_t cb) +{ + if (dlci >= ARRAY_SIZE(sercomm.rx.dlci_handler)) + return -EINVAL; + + if (sercomm.rx.dlci_handler[dlci]) + return -EBUSY; + + sercomm.rx.dlci_handler[dlci] = cb; + return 0; +} + +/* dispatch an incomnig message once it is completely received */ +static void dispatch_rx_msg(uint8_t dlci, struct msgb *msg) +{ + if (sercomm.rx.dlci_handler[dlci]) + sercomm.rx.dlci_handler[dlci](dlci, msg); + else + msgb_free(msg); +} + +/* the driver has received one byte, pass it into sercomm layer */ +int sercomm_drv_rx_char(uint8_t ch) +{ + uint8_t *ptr; + + if (!sercomm.rx.msg) + sercomm.rx.msg = sercomm_alloc_msgb(SERCOMM_RX_MSG_SIZE); + + if (msgb_tailroom(sercomm.rx.msg) == 0) { + msgb_free(sercomm.rx.msg); + sercomm.rx.msg = sercomm_alloc_msgb(SERCOMM_RX_MSG_SIZE); + sercomm.rx.state = RX_ST_WAIT_START; + return 0; + } + + switch (sercomm.rx.state) { + case RX_ST_WAIT_START: + if (ch != HDLC_FLAG) + return 0; + sercomm.rx.state = RX_ST_ADDR; + break; + case RX_ST_ADDR: + sercomm.rx.dlci = ch; + sercomm.rx.state = RX_ST_CTRL; + break; + case RX_ST_CTRL: + sercomm.rx.ctrl = ch; + sercomm.rx.state = RX_ST_DATA; + break; + case RX_ST_DATA: + if (ch == HDLC_ESCAPE) { + /* drop the escape octet, but change state */ + sercomm.rx.state = RX_ST_ESCAPE; + break; + } else if (ch == HDLC_FLAG) { + /* message is finished */ + dispatch_rx_msg(sercomm.rx.dlci, sercomm.rx.msg); + /* allocate new buffer */ + sercomm.rx.msg = NULL; + /* start all over again */ + sercomm.rx.state = RX_ST_WAIT_START; + + /* do not add the control char */ + break; + } + /* default case: store the octet */ + ptr = msgb_put(sercomm.rx.msg, 1); + *ptr = ch; + break; + case RX_ST_ESCAPE: + /* store bif-5-inverted octet in buffer */ + ch ^= (1 << 5); + ptr = msgb_put(sercomm.rx.msg, 1); + *ptr = ch; + /* transition back to nromal DATA state */ + sercomm.rx.state = RX_ST_DATA; + break; + } + return 1; +} diff --git a/src/target/firmware/comm/sercomm_cons.c b/src/target/firmware/comm/sercomm_cons.c new file mode 100644 index 00000000..fa08b50e --- /dev/null +++ b/src/target/firmware/comm/sercomm_cons.c @@ -0,0 +1,126 @@ +/* Serial console layer, layered on top of sercomm HDLC */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include +#include + +#include + +#include +#include +#include +#include + +static struct { + struct msgb *cur_msg; +} scons; + +static void raw_puts(const char *s) +{ + int i = strlen(s); + while (i--) + uart_putchar_wait(SERCOMM_UART_NR, *s++); +} + +#ifdef DEBUG +#define raw_putd(x) raw_puts(x) +#else +#define raw_putd(x) +#endif + +int sercomm_puts(const char *s) +{ + const int len = strlen(s) + 1; + unsigned int bytes_left = len; + + if (!sercomm_initialized()) { + raw_putd("sercomm not initialized: "); + raw_puts(s); + return len - 1; + } + + while (bytes_left > 0) { + unsigned int write_num, space_left, flush; + uint8_t *data; + + if (!scons.cur_msg) + scons.cur_msg = sercomm_alloc_msgb(SERCOMM_CONS_ALLOC); + + if (!scons.cur_msg) { + raw_putd("cannot allocate sercomm msgb: "); + raw_puts(s); + return -ENOMEM; + } + + /* space left in the current msgb */ + space_left = msgb_tailroom(scons.cur_msg); + + if (space_left <= bytes_left) { + write_num = space_left; + /* flush buffer when it is full */ + flush = 1; + } else { + write_num = bytes_left; + flush = 0; + } + + /* obtain pointer where to copy the data */ + data = msgb_put(scons.cur_msg, write_num); + + /* copy data while looking for \n line termination */ + { + unsigned int i; + for (i = 0; i < write_num; i++) { + /* flush buffer at end of line */ + if (*s == '\n') + flush = 1; + *data++ = *s++; + } + } + bytes_left -= write_num; + + if (flush) { + sercomm_sendmsg(SC_DLCI_CONSOLE, scons.cur_msg); + /* reset scons.cur_msg pointer to ensure we allocate + * a new one next round */ + scons.cur_msg = NULL; + } + } + + return len - 1; +} + +int sercomm_putchar(int c) +{ + char s[2]; + int rc; + + s[0] = c & 0xff; + s[1] = '\0'; + + rc = sercomm_puts(s); + if (rc < 0) + return rc; + + return c; +} diff --git a/src/target/firmware/display/font_r8x8.c b/src/target/firmware/display/font_r8x8.c new file mode 100644 index 00000000..f6a8a820 Binary files /dev/null and b/src/target/firmware/display/font_r8x8.c differ diff --git a/src/target/firmware/display/st7558.c b/src/target/firmware/display/st7558.c new file mode 100644 index 00000000..daba94bd --- /dev/null +++ b/src/target/firmware/display/st7558.c @@ -0,0 +1,122 @@ +/* Sitronix ST7558 LCD Driver */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#include +#include +#include +#include +#include +#include + +#define MORE_CONTROL 0x80 +#define CONTROL_RS_RAM 0x40 +#define CONTROL_RS_CMD 0x00 +#define Y_ADDR(n) (0x40|((n)&0xf)) +#define X_ADDR(n) (0x80|((n)&0x3f)) + +static const uint8_t setup[] = { CONTROL_RS_CMD, 0x2e, 0x21, 0x12, 0xc0, 0x0b, 0x20, 0x11, 0x00, 0x40, 0x80 }; +static const uint8_t home[] = { CONTROL_RS_CMD, Y_ADDR(0), X_ADDR(0) }; + +/* video modes */ +static const uint8_t invert[] = { CONTROL_RS_CMD, 0x20, 0x0d }; +static const uint8_t normal[] = { CONTROL_RS_CMD, 0x20, 0x0c }; +static const uint8_t off[] = { CONTROL_RS_CMD, 0x20, 0x08 }; + +#define ST7558_SLAVE_ADDR 0x3c +static int st7558_write(const uint8_t *data, int len) +{ + int rc = i2c_write(ST7558_SLAVE_ADDR, data[0], 1, data+1, len-1); + /* FIXME: find out why this is needed! */ + delay_ms(10); + return rc; +} + +void st7558_init(void) +{ + /* Release nRESET */ + calypso_reset_set(RESET_EXT, 0); + + i2c_init(0,0); + delay_ms(10); + + st7558_write(setup, sizeof(setup)); + st7558_clrscr(); +} + +void st7558_set_attr(unsigned long attr) +{ + if (attr & DISP_ATTR_INVERT) + st7558_write(invert, sizeof(invert)); +} + +void st7558_unset_attr(unsigned long attr) +{ + if (attr & DISP_ATTR_INVERT) + st7558_write(normal, sizeof(normal)); +} + +static const uint8_t zero16[] = { CONTROL_RS_RAM, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }; +void st7558_clrscr(void) +{ + int i; + + st7558_write(home, sizeof(home)); + + for (i = 0; i < 102*9; i += 16) + st7558_write(zero16, sizeof(zero16)); + + st7558_write(home, sizeof(home)); +} + +/* FIXME: we need a mini-libc */ +static void *mcpy(uint8_t *dst, const uint8_t *src, int len) +{ + while (len--) + *dst++ = *src++; + + return dst; +} + +extern const unsigned char fontdata_r8x8[]; + +void st7558_putchar(unsigned char c) +{ + uint8_t putc_buf[16]; + uint8_t bytes_per_char = 8; + + putc_buf[0] = CONTROL_RS_RAM; + mcpy(putc_buf+1, fontdata_r8x8+(c*bytes_per_char), bytes_per_char); + st7558_write(putc_buf, 1+bytes_per_char); +} + +void st7558_puts(const char *str) +{ + char c; + + while ((c = *str++)) + st7558_putchar(c); +} diff --git a/src/target/firmware/flash/cfi_flash.c b/src/target/firmware/flash/cfi_flash.c new file mode 100644 index 00000000..faf44cbe --- /dev/null +++ b/src/target/firmware/flash/cfi_flash.c @@ -0,0 +1,435 @@ +/* NOR Flash Driver for Intel 28F160C3 NOR flash */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include +#include +#include + +/* XXX: memdump_range() */ +#include + +enum flash_cmd { + FLASH_CMD_RESET = 0xff, + FLASH_CMD_READ_ID = 0x90, + FLASH_CMD_CFI = 0x98, + FLASH_CMD_READ_STATUS = 0x70, + FLASH_CMD_CLEAR_STATUS = 0x50, + FLASH_CMD_WRITE = 0x40, + FLASH_CMD_BLOCK_ERASE = 0x20, + FLASH_CMD_ERASE_CONFIRM = 0xD0, + FLASH_CMD_PROTECT = 0x60, +}; + +enum flash_prot_cmd { + FLASH_PROT_LOCK = 0x01, + FLASH_PROT_UNLOCK = 0xD0, + FLASH_PROT_LOCKDOWN = 0x2F +}; + +enum flash_offset { + FLASH_OFFSET_MANUFACTURER_ID = 0x00, + FLASH_OFFSET_DEVICE_ID = 0x01, + FLASH_OFFSET_INTEL_PROTECTION = 0x81, + FLASH_OFFSET_CFI_RESP = 0x10 +}; + +enum flash_block_offset { + FLASH_OFFSET_BLOCK_LOCKSTATE = 0x02 +}; + +enum flash_status { + FLASH_STATUS_READY = 0x80, + FLASH_STATUS_ERASE_SUSPENDED = 0x40, + FLASH_STATUS_ERASE_ERROR = 0x20, + FLASH_STATUS_PROGRAM_ERROR = 0x10, + FLASH_STATUS_VPP_LOW = 0x08, + FLASH_STATUS_PROGRAM_SUSPENDED = 0x04, + FLASH_STATUS_LOCKED_ERROR = 0x02, + FLASH_STATUS_RESERVED = 0x01 +}; + +static inline void flash_write_cmd(const void *base_addr, uint16_t cmd) +{ + writew(cmd, base_addr); +} + +static inline uint16_t flash_read16(const void *base_addr, uint32_t offset) +{ + return readw(base_addr + (offset << 1)); +} + +static char flash_protected(uint32_t block_offset) { + return block_offset < 64*1024; +} + +uint8_t flash_block_getlock(cfi_flash_t *flash, uint32_t block_offset) { + const void *base_addr = flash->f_base; + uint8_t lockstate; + flash_write_cmd(base_addr, FLASH_CMD_READ_ID); + lockstate = flash_read16(base_addr, block_offset + FLASH_OFFSET_BLOCK_LOCKSTATE); + flash_write_cmd(base_addr, FLASH_CMD_RESET); + return lockstate; +} + +void flash_block_unlock(cfi_flash_t *flash, uint32_t block_offset) { + const void *base_addr = flash->f_base; + printf("Unlocking block at 0x%08x\n", block_offset); + + if(flash_protected(block_offset)) { + puts("error: block is soft-protected\n"); + return; + } + + flash_write_cmd(base_addr, FLASH_CMD_PROTECT); + flash_write_cmd(base_addr + block_offset, FLASH_PROT_UNLOCK); + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +void flash_block_lock(cfi_flash_t *flash, uint32_t block_offset) { + const void *base_addr = flash->f_base; + printf("Locking block at 0x%08x\n", block_offset); + flash_write_cmd(base_addr, FLASH_CMD_PROTECT); + flash_write_cmd(base_addr + block_offset, FLASH_PROT_LOCK); + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +void flash_block_lockdown(cfi_flash_t *flash, uint32_t block_offset) { + const void *base_addr = flash->f_base; + printf("Locking down block at 0x%08x\n", block_offset); + flash_write_cmd(base_addr, FLASH_CMD_PROTECT); + flash_write_cmd(base_addr + block_offset, FLASH_PROT_LOCKDOWN); + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +void flash_block_erase(cfi_flash_t *flash, uint32_t block_offset) { + const void *base_addr = flash->f_base; + printf("Erasing block 0x%08x...", block_offset); + + if(flash_protected(block_offset)) { + puts("error: block is soft-protected\n"); + return; + } + + void *block_addr = ((uint8_t*)base_addr) + block_offset; + + flash_write_cmd(base_addr, FLASH_CMD_CLEAR_STATUS); + + flash_write_cmd(block_addr, FLASH_CMD_BLOCK_ERASE); + flash_write_cmd(block_addr, FLASH_CMD_ERASE_CONFIRM); + + flash_write_cmd(base_addr, FLASH_CMD_READ_STATUS); + uint16_t status; + do { + status = flash_read16(base_addr, 0); + } while(!(status&FLASH_STATUS_READY)); + + if(status&FLASH_STATUS_ERASE_ERROR) { + puts("error: "); + if(status&FLASH_STATUS_VPP_LOW) { + puts("vpp insufficient\n"); + } + if(status&FLASH_STATUS_LOCKED_ERROR) { + puts("block is lock-protected\n"); + } + } else { + puts("done\n"); + } + + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +void flash_program(cfi_flash_t *flash, uint32_t dst, void *src, uint32_t nbytes) { + const void *base_addr = flash->f_base; + uint32_t i; + + printf("Programming %u bytes to 0x%08x from 0x%p...", nbytes, dst, src); + + if(dst%2) { + puts("error: unaligned destination\n"); + return; + } + + if(nbytes%2) { + puts("error: unaligned count\n"); + return; + } + + if(flash_protected(dst)) { + puts("error: block is soft-protected\n"); + return; + } + + flash_write_cmd(base_addr, FLASH_CMD_CLEAR_STATUS); + + puts("writing..."); + for(i = 0; i < nbytes; i += 2) { + uint16_t *src_addr = (uint16_t*)(src + i); + uint16_t *dst_addr = (uint16_t*)(base_addr + dst + i); + + uint16_t data = *src_addr; + + flash_write_cmd(dst_addr, FLASH_CMD_WRITE); + flash_write_cmd(dst_addr, data); + + flash_write_cmd(base_addr, FLASH_CMD_READ_STATUS); + uint16_t status; + do { + status = flash_read16(base_addr, 0); + } while(!(status&FLASH_STATUS_READY)); + + if(status&FLASH_STATUS_PROGRAM_ERROR) { + puts("error: "); + if(status&FLASH_STATUS_VPP_LOW) { + puts("vpp insufficient"); + } + if(status&FLASH_STATUS_LOCKED_ERROR) { + puts("block is lock-protected"); + } + goto err_reset; + } + } + + flash_write_cmd(base_addr, FLASH_CMD_RESET); + + puts("verifying..."); + for(i = 0; i < nbytes; i += 2) { + uint16_t *src_addr = (uint16_t*)(src + i); + uint16_t *dst_addr = (uint16_t*)(base_addr + dst + i); + if(*src_addr != *dst_addr) { + puts("error: verification failed"); + goto err; + } + } + + puts("done\n"); + + return; + + err_reset: + flash_write_cmd(base_addr, FLASH_CMD_RESET); + + err: + printf(" at offset 0x%x\n", i); +} + +typedef void (*flash_block_cb_t)(cfi_flash_t *flash, + uint32_t block_offset, + uint32_t block_size); + +void flash_iterate_blocks(cfi_flash_t *flash, struct cfi_query *qry, + uint32_t start_offset, uint32_t end_offset, + flash_block_cb_t callback) +{ + int region, block; + + uint32_t block_start = 0; + for(region = 0; region < qry->num_erase_regions; region++) { + uint16_t actual_count = qry->erase_regions[region].b_count + 1; + uint32_t actual_size = qry->erase_regions[region].b_size * 256; + for(block = 0; block < actual_count; block++) { + uint32_t block_end = block_start + actual_size; + if(block_start >= start_offset && block_end-1 <= end_offset) { + callback(flash, block_start, actual_size); + } + block_start = block_end; + } + } +} + +static void get_id(void *base_addr, uint16_t *manufacturer_id, uint16_t *device_id) { + flash_write_cmd(base_addr, FLASH_CMD_READ_ID); + + *manufacturer_id = flash_read16(base_addr, FLASH_OFFSET_MANUFACTURER_ID); + *device_id = flash_read16(base_addr, FLASH_OFFSET_DEVICE_ID); + + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +static void get_query(void *base_addr, struct cfi_query *query) { + unsigned int i; + + flash_write_cmd(base_addr, FLASH_CMD_CFI); + + for(i = 0; i < sizeof(struct cfi_query); i++) { + uint16_t byte = flash_read16(base_addr, FLASH_OFFSET_CFI_RESP+i); + *(((unsigned char*)query)+i) = byte; + } + + if(query->qry[0] != 'Q' || query->qry[1] != 'R' || query->qry[2] != 'Y') { + puts("Error: CFI query signature not found\n"); + } + + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +static void dump_query(void *base_addr, struct cfi_query *query) { + unsigned int i; + + flash_write_cmd(base_addr, FLASH_CMD_CFI); + + for(i = 0; i < sizeof(struct cfi_query); i++) { + uint8_t byte = *(((uint8_t*)query)+i); + printf("%04X: %02X\n", FLASH_OFFSET_CFI_RESP+i, byte); + } + + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +static void dump_layout(void *base_addr, const struct cfi_query *qry) { + int region; + + flash_write_cmd(base_addr, FLASH_CMD_READ_ID); + for(region = 0; region < qry->num_erase_regions; region++) { + uint16_t actual_count = qry->erase_regions[region].b_count + 1; + uint32_t actual_size = qry->erase_regions[region].b_size * 256; + printf("Region of 0x%04x times 0x%6x bytes\n", actual_count, + actual_size); + } + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +static void dump_locks(void *base_addr, const struct cfi_query *qry) { + int region, block; + + uint32_t block_addr = 0; + flash_write_cmd(base_addr, FLASH_CMD_READ_ID); + for(region = 0; region < qry->num_erase_regions; region++) { + uint16_t actual_count = qry->erase_regions[region].b_count + 1; + uint32_t actual_size = qry->erase_regions[region].b_size * 256; + for(block = 0; block < actual_count; block++) { + uint8_t lock = flash_read16(base_addr, block_addr+2); + printf("Block 0x%08x lock 0x%02x\n", block_addr*2, lock); + block_addr += actual_size / 2; + } + } + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +static void dump_protection(void *base_addr) { + flash_write_cmd(base_addr, FLASH_CMD_READ_ID); + + uint16_t lock = flash_read16(base_addr, FLASH_OFFSET_INTEL_PROTECTION); + printf("Protection Lock: 0x%04x\n", lock); + + puts("Protection Data: "); + int i; + for(i = 0; i < 8; i++) { + printf("%04x", flash_read16(base_addr, FLASH_OFFSET_INTEL_PROTECTION + 1 + i)); + } + putchar('\n'); + + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +static void dump_timing(void *base_addr, struct cfi_query *qry) { + uint32_t block_erase_typ = 1<block_erase_timeout_typ; + uint32_t block_erase_max = (1<block_erase_timeout_max) * block_erase_typ; + uint32_t word_program_typ = 1<word_write_timeout_typ; + uint32_t word_program_max = (1<word_write_timeout_max) * word_program_typ; + printf("Block Erase Typical: %u ms\n", block_erase_typ); + printf("Block Erase Maximum: %u ms\n", block_erase_max); + printf("Word Program Typical: %u us\n", word_program_typ); + printf("Word Program Maximum: %u us\n", word_program_max); +} + +static void dump_algorithms(void *base_addr, struct cfi_query *qry) { + printf("Primary Algorithm ID: %04x\n", qry->p_id); + printf("Primary Extended Query: %04x\n", qry->p_adr); + + printf("Alternate Algorithm ID: %04x\n", qry->a_id); + printf("Alternate Extended Query: %04x\n", qry->a_adr); +} + +void +lockdown_block_cb(cfi_flash_t *flash, + uint32_t block_offset, + uint32_t block_size) +{ + flash_block_lockdown(flash, block_offset); +} + +void +print_block_cb(cfi_flash_t *flash, + uint32_t block_offset, + uint32_t block_size) +{ + printf("%08x size %08x\n", block_offset, block_size); +} + +void flash_dump_info(cfi_flash_t *flash) { + void *base_addr = flash->f_base; + struct cfi_query *qry = &flash->f_query; + + printf("Flash Manufacturer ID: %04x\n", flash->f_manuf_id); + printf("Flash Device ID: %04x\n", flash->f_dev_id); + + printf("Flash Size: 0x%08x bytes\n", flash->f_size); + + dump_algorithms(base_addr, qry); + + dump_timing(base_addr, qry); + + dump_protection(base_addr); + + dump_layout(base_addr, qry); + + dump_locks(base_addr, qry); +} + +void flash_init(cfi_flash_t *flash, void *base_addr) { + printf("Initializing CFI flash at 0x%p\n", base_addr); + + flash->f_base = base_addr; + + get_id(base_addr, &flash->f_manuf_id, &flash->f_dev_id); + + get_query(base_addr, &flash->f_query); + + flash->f_size = 1<f_query.dev_size; +} + +void flash_test() { + /* block iterator test */ +#if 0 + flash_iterate_blocks(flash, qry, 0x0000, 0xFFFF, &lockdown_block_cb); +#endif + + /* programming test */ +#if 0 + static uint8_t magic[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xDE, 0xAD, 0xBE, 0xEF}; + + memdump_range(&magic, sizeof(magic)); + +#if 0 +#define ADDR 0x001E0000 + flash_block_unlock(flash, ADDR); + memdump_range(ADDR, 16); + flash_block_erase(flash, ADDR); + memdump_range(ADDR, 16); + flash_program(flash, ADDR, &magic, sizeof(magic)); + memdump_range(ADDR, 16); +#undef ADDR +#endif + +#endif +} diff --git a/src/target/firmware/include/abb/twl3025.h b/src/target/firmware/include/abb/twl3025.h new file mode 100644 index 00000000..c6c30aac --- /dev/null +++ b/src/target/firmware/include/abb/twl3025.h @@ -0,0 +1,128 @@ +#ifndef _TWL3025_H +#define _TWL3025_H + +#define PAGE(n) (n << 7) +enum twl3025_reg { + VRPCCFG = PAGE(1) | 30, + VRPCDEV = PAGE(0) | 30, + VRPCMSK = PAGE(1) | 31, + VRPCMSKABB = PAGE(1) | 29, + VRPCSTS = PAGE(0) | 31, + /* Monitoring ADC Registers */ + MADCTRL = PAGE(0) | 13, + MADCSTAT = PAGE(0) | 24, + VBATREG = PAGE(0) | 15, + VCHGREG = PAGE(0) | 16, + ICHGREG = PAGE(0) | 17, + VBKPREG = PAGE(0) | 18, + ADIN1REG = PAGE(0) | 19, + ADIN2REG = PAGE(0) | 20, + ADIN3REG = PAGE(0) | 21, + ADIN4REG = PAGE(0) | 22, + /* Clock Generator Registers */ + TOGBR1 = PAGE(0) | 4, + TOGBR2 = PAGE(0) | 5, + PWDNRG = PAGE(1) | 9, + TAPCTRL = PAGE(1) | 19, + TAPREG = PAGE(1) | 20, + /* Automatic Frequency Control (AFC) Registers */ + AUXAFC1 = PAGE(0) | 7, + AUXAFC2 = PAGE(0) | 8, + AFCCTLADD = PAGE(1) | 21, + AFCOUT = PAGE(1) | 22, + /* Automatic Power Control (APC) Registers */ + APCDEL1 = PAGE(0) | 2, + APCDEL2 = PAGE(1) | 26, + AUXAPC = PAGE(0) | 9, + APCRAM = PAGE(0) | 10, + APCOFF = PAGE(0) | 11, + APCOUT = PAGE(1) | 12, + /* Auxiliary DAC Control Register */ + AUXDAC = PAGE(0) | 12, + /* SimCard Control Register */ + VRPCSIM = PAGE(1) | 23, + /* LED Driver Register */ + AUXLED = PAGE(1) | 24, + /* Battery Charger Interface (BCI) Registers */ + CHGREG = PAGE(0) | 25, + BCICTL1 = PAGE(0) | 28, + BCICTL2 = PAGE(0) | 29, + BCICONF = PAGE(1) | 13, + /* Interrupt and Bus Control (IBIC) Registers */ + ITMASK = PAGE(0) | 28, + ITSTATREG = PAGE(0) | 27, /* both pages! */ + PAGEREG = PAGE(0) | 1, /* both pages! */ + /* Baseband Codec (BBC) Registers */ + BULIOFF = PAGE(1) | 2, + BULQOFF = PAGE(1) | 3, + BULIDAC = PAGE(1) | 5, + BULQDAC = PAGE(1) | 4, + BULGCAL = PAGE(1) | 14, + BULDATA1 = PAGE(0) | 3, /* 16 words */ + BBCTRL = PAGE(1) | 6, + /* Voiceband Codec (VBC) Registers */ + VBCTRL1 = PAGE(1) | 8, + VBCTRL2 = PAGE(1) | 11, + VBPOP = PAGE(1) | 10, + VBUCTRL = PAGE(1) | 7, + VBDCTRL = PAGE(0) | 6, +}; +#define BULDATA2 BULDATA1 + +enum togbr2_bits { + TOGBR2_KEEPR = (1 << 0), /* Clear KEEPON bit */ + TOGBR2_KEEPS = (1 << 1), /* Set KEEPON bit */ + TOGBR2_ACTR = (1 << 2), /* Dectivate MCLK */ + TOGBR2_ACTS = (1 << 3), /* Activate MCLK */ + TOGBR2_IBUFPTR1 = (1 << 4), /* Initialize pointer of burst buffer 1 */ + TOGBR2_IBUFPTR2 = (1 << 5), /* Initialize pointer of burst buffer 2 */ + TOGBR2_IAPCPTR = (1 << 6), /* Initialize pointer of APC RAM */ +}; + +enum twl3025_unit { + TWL3025_UNIT_AFC, + TWL3025_UNIT_MAD, + TWL3025_UNIT_ADA, + TWL3025_UNIT_VDL, + TWL3025_UNIT_VUL, +}; + +void twl3025_init(void); +void twl3025_reg_write(uint8_t reg, uint16_t data); +uint16_t twl3025_reg_read(uint8_t reg); + +void twl3025_power_off(void); + +void twl3025_clk13m(int enable); + +void twl3025_unit_enable(enum twl3025_unit unit, int on); + +enum twl3025_tsp_bits { + BULON = 0x80, + BULCAL = 0x40, + BULENA = 0x20, + BDLON = 0x10, + BDLCAL = 0x08, + BDLENA = 0x04, + STARTADC = 0x02, +}; + +/* Enqueue a TSP signal change via the TPU */ +void twl3025_tsp_write(uint8_t data); + +/* Enqueue a series of TSP commands in the TPU to (de)activate the downlink path */ +void twl3025_downlink(int on, int16_t at); + +/* Update the AFC DAC value */ +void twl3025_afc_set(int16_t val); + +/* Get the AFC DAC value */ +int16_t twl3025_afc_get(void); + +/* Get the AFC DAC output value */ +uint8_t twl3025_afcout_get(void); + +/* Force a certain static AFC DAC output value */ +void twl3025_afcout_set(uint8_t val); + +#endif diff --git a/src/target/firmware/include/arm.h b/src/target/firmware/include/arm.h new file mode 100644 index 00000000..272c9c39 --- /dev/null +++ b/src/target/firmware/include/arm.h @@ -0,0 +1,7 @@ +#ifndef _ARM_H +#define _ARM_H + +void arm_enable_interrupts(void); +int arm_disable_interrupts(void); + +#endif diff --git a/src/target/firmware/include/asm/assembler.h b/src/target/firmware/include/asm/assembler.h new file mode 100644 index 00000000..b43f9d17 --- /dev/null +++ b/src/target/firmware/include/asm/assembler.h @@ -0,0 +1,97 @@ +/* + * linux/include/asm-arm/assembler.h + * + * Copyright (C) 1996-2000 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This file contains arm architecture specific defines + * for the different processors. + * + * Do not include any C declarations in this file - it is included by + * assembler source. + */ +#ifndef __ASSEMBLY__ +#error "Only include this from assembly code" +#endif + +#include + +#define pull lsl +#define push lsr +#define get_byte_0 lsr #24 +#define get_byte_1 lsr #16 +#define get_byte_2 lsr #8 +#define get_byte_3 lsl #0 +#define put_byte_0 lsl #24 +#define put_byte_1 lsl #16 +#define put_byte_2 lsl #8 +#define put_byte_3 lsl #0 + +#define PLD(code...) + +#define MODE_USR USR_MODE +#define MODE_FIQ FIQ_MODE +#define MODE_IRQ IRQ_MODE +#define MODE_SVC SVC_MODE + +#define DEFAULT_FIQ MODE_FIQ + +/* + * LOADREGS - ldm with PC in register list (eg, ldmfd sp!, {pc}) + */ +#ifdef __STDC__ +#define LOADREGS(cond, base, reglist...)\ + ldm##cond base,reglist +#else +#define LOADREGS(cond, base, reglist...)\ + ldm/**/cond base,reglist +#endif + +/* + * Build a return instruction for this processor type. + */ +#define RETINSTR(instr, regs...)\ + instr regs + +/* + * Enable and disable interrupts + */ + .macro disable_irq + msr cpsr_c, #PSR_I_BIT | SVC_MODE + .endm + + .macro enable_irq + msr cpsr_c, #SVC_MODE + .endm + +/* + * Save the current IRQ state and disable IRQs. Note that this macro + * assumes FIQs are enabled, and that the processor is in SVC mode. + */ + .macro save_and_disable_irqs, oldcpsr + mrs \oldcpsr, cpsr + disable_irq + .endm + +/* + * Restore interrupt state previously stored in a register. We don't + * guarantee that this will preserve the flags. + */ + .macro restore_irqs, oldcpsr + msr cpsr_c, \oldcpsr + .endm + +/* + * These two are used to save LR/restore PC over a user-based access. + * The old 26-bit architecture requires that we do. On 32-bit + * architecture, we can safely ignore this requirement. + */ + .macro save_lr + .endm + + .macro restore_pc + mov pc, lr + .endm diff --git a/src/target/firmware/include/asm/atomic.h b/src/target/firmware/include/asm/atomic.h new file mode 100644 index 00000000..19e8ce6f --- /dev/null +++ b/src/target/firmware/include/asm/atomic.h @@ -0,0 +1,106 @@ +/* + * linux/include/asm-arm/atomic.h + * + * Copyright (C) 1996 Russell King. + * Copyright (C) 2002 Deep Blue Solutions Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __ASM_ARM_ATOMIC_H +#define __ASM_ARM_ATOMIC_H + +typedef struct { volatile int counter; } atomic_t; + +#define ATOMIC_INIT(i) { (i) } + +#define atomic_read(v) ((v)->counter) + +#include +#include + +#define atomic_set(v,i) (((v)->counter) = (i)) + +static inline int atomic_add_return(int i, atomic_t *v) +{ + unsigned long flags; + int val; + + local_irq_save(flags); + val = v->counter; + v->counter = val += i; + local_irq_restore(flags); + + return val; +} + +static inline int atomic_sub_return(int i, atomic_t *v) +{ + unsigned long flags; + int val; + + local_irq_save(flags); + val = v->counter; + v->counter = val -= i; + local_irq_restore(flags); + + return val; +} + +static inline int atomic_cmpxchg(atomic_t *v, int old, int new) +{ + int ret; + unsigned long flags; + + local_irq_save(flags); + ret = v->counter; + if (likely(ret == old)) + v->counter = new; + local_irq_restore(flags); + + return ret; +} + +static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) +{ + unsigned long flags; + + local_irq_save(flags); + *addr &= ~mask; + local_irq_restore(flags); +} + +#define atomic_xchg(v, new) (xchg(&((v)->counter), new)) + +static inline int atomic_add_unless(atomic_t *v, int a, int u) +{ + int c, old; + + c = atomic_read(v); + while (c != u && (old = atomic_cmpxchg((v), c, c + a)) != c) + c = old; + return c != u; +} +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) + +#define atomic_add(i, v) (void) atomic_add_return(i, v) +#define atomic_inc(v) (void) atomic_add_return(1, v) +#define atomic_sub(i, v) (void) atomic_sub_return(i, v) +#define atomic_dec(v) (void) atomic_sub_return(1, v) + +#define atomic_inc_and_test(v) (atomic_add_return(1, v) == 0) +#define atomic_dec_and_test(v) (atomic_sub_return(1, v) == 0) +#define atomic_inc_return(v) (atomic_add_return(1, v)) +#define atomic_dec_return(v) (atomic_sub_return(1, v)) +#define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0) + +#define atomic_add_negative(i,v) (atomic_add_return(i, v) < 0) + +/* Atomic operations are already serializing on ARM */ +#define smp_mb__before_atomic_dec() barrier() +#define smp_mb__after_atomic_dec() barrier() +#define smp_mb__before_atomic_inc() barrier() +#define smp_mb__after_atomic_inc() barrier() + +#endif diff --git a/src/target/firmware/include/asm/bitops.h b/src/target/firmware/include/asm/bitops.h new file mode 100644 index 00000000..337d800d --- /dev/null +++ b/src/target/firmware/include/asm/bitops.h @@ -0,0 +1,225 @@ +/* + * Copyright 1995, Russell King. + * Various bits and pieces copyrights include: + * Linus Torvalds (test_bit). + * Big endian support: Copyright 2001, Nicolas Pitre + * reworked by rmk. + * + * bit 0 is the LSB of an "unsigned long" quantity. + * + * Please note that the code in this file should never be included + * from user space. Many of these are not implemented in assembler + * since they would be too costly. Also, they require privileged + * instructions (which are not available from user mode) to ensure + * that they are atomic. + */ + +#ifndef __ASM_ARM_BITOPS_H +#define __ASM_ARM_BITOPS_H + +#include + +#define smp_mb__before_clear_bit() mb() +#define smp_mb__after_clear_bit() mb() + +/* + * These functions are the basis of our bit ops. + * + * First, the atomic bitops. These use native endian. + */ +static inline void ____atomic_set_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long flags; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + local_irq_save(flags); + *p |= mask; + local_irq_restore(flags); +} + +static inline void ____atomic_clear_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long flags; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + local_irq_save(flags); + *p &= ~mask; + local_irq_restore(flags); +} + +static inline void ____atomic_change_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long flags; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + local_irq_save(flags); + *p ^= mask; + local_irq_restore(flags); +} + +static inline int +____atomic_test_and_set_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long flags; + unsigned int res; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + local_irq_save(flags); + res = *p; + *p = res | mask; + local_irq_restore(flags); + + return res & mask; +} + +static inline int +____atomic_test_and_clear_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long flags; + unsigned int res; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + local_irq_save(flags); + res = *p; + *p = res & ~mask; + local_irq_restore(flags); + + return res & mask; +} + +static inline int +____atomic_test_and_change_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long flags; + unsigned int res; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + local_irq_save(flags); + res = *p; + *p = res ^ mask; + local_irq_restore(flags); + + return res & mask; +} + +//#include + +/* + * A note about Endian-ness. + * ------------------------- + * + * When the ARM is put into big endian mode via CR15, the processor + * merely swaps the order of bytes within words, thus: + * + * ------------ physical data bus bits ----------- + * D31 ... D24 D23 ... D16 D15 ... D8 D7 ... D0 + * little byte 3 byte 2 byte 1 byte 0 + * big byte 0 byte 1 byte 2 byte 3 + * + * This means that reading a 32-bit word at address 0 returns the same + * value irrespective of the endian mode bit. + * + * Peripheral devices should be connected with the data bus reversed in + * "Big Endian" mode. ARM Application Note 61 is applicable, and is + * available from http://www.arm.com/. + * + * The following assumes that the data bus connectivity for big endian + * mode has been followed. + * + * Note that bit 0 is defined to be 32-bit word bit 0, not byte 0 bit 0. + */ + +/* + * Little endian assembly bitops. nr = 0 -> byte 0 bit 0. + */ +extern void _set_bit_le(int nr, volatile unsigned long * p); +extern void _clear_bit_le(int nr, volatile unsigned long * p); +extern void _change_bit_le(int nr, volatile unsigned long * p); +extern int _test_and_set_bit_le(int nr, volatile unsigned long * p); +extern int _test_and_clear_bit_le(int nr, volatile unsigned long * p); +extern int _test_and_change_bit_le(int nr, volatile unsigned long * p); +extern int _find_first_zero_bit_le(const void * p, unsigned size); +extern int _find_next_zero_bit_le(const void * p, int size, int offset); +extern int _find_first_bit_le(const unsigned long *p, unsigned size); +extern int _find_next_bit_le(const unsigned long *p, int size, int offset); + +/* + * Big endian assembly bitops. nr = 0 -> byte 3 bit 0. + */ +extern void _set_bit_be(int nr, volatile unsigned long * p); +extern void _clear_bit_be(int nr, volatile unsigned long * p); +extern void _change_bit_be(int nr, volatile unsigned long * p); +extern int _test_and_set_bit_be(int nr, volatile unsigned long * p); +extern int _test_and_clear_bit_be(int nr, volatile unsigned long * p); +extern int _test_and_change_bit_be(int nr, volatile unsigned long * p); +extern int _find_first_zero_bit_be(const void * p, unsigned size); +extern int _find_next_zero_bit_be(const void * p, int size, int offset); +extern int _find_first_bit_be(const unsigned long *p, unsigned size); +extern int _find_next_bit_be(const unsigned long *p, int size, int offset); + +/* + * The __* form of bitops are non-atomic and may be reordered. + */ +#define ATOMIC_BITOP_LE(name,nr,p) \ + (__builtin_constant_p(nr) ? \ + ____atomic_##name(nr, p) : \ + _##name##_le(nr,p)) + +#define ATOMIC_BITOP_BE(name,nr,p) \ + (__builtin_constant_p(nr) ? \ + ____atomic_##name(nr, p) : \ + _##name##_be(nr,p)) + +#define NONATOMIC_BITOP(name,nr,p) \ + (____nonatomic_##name(nr, p)) + +/* + * These are the little endian, atomic definitions. + */ +#define set_bit(nr,p) ATOMIC_BITOP_LE(set_bit,nr,p) +#define clear_bit(nr,p) ATOMIC_BITOP_LE(clear_bit,nr,p) +#define change_bit(nr,p) ATOMIC_BITOP_LE(change_bit,nr,p) +#define test_and_set_bit(nr,p) ATOMIC_BITOP_LE(test_and_set_bit,nr,p) +#define test_and_clear_bit(nr,p) ATOMIC_BITOP_LE(test_and_clear_bit,nr,p) +#define test_and_change_bit(nr,p) ATOMIC_BITOP_LE(test_and_change_bit,nr,p) +#define find_first_zero_bit(p,sz) _find_first_zero_bit_le(p,sz) +#define find_next_zero_bit(p,sz,off) _find_next_zero_bit_le(p,sz,off) +#define find_first_bit(p,sz) _find_first_bit_le(p,sz) +#define find_next_bit(p,sz,off) _find_next_bit_le(p,sz,off) + +#define WORD_BITOFF_TO_LE(x) ((x)) + +#if 0 +#include +#include +#include +#include + +#include + +#include +#include +#endif + +#define BITS_PER_LONG 32 +#define BITOP_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) +#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG) + +static inline int test_bit(int nr, const volatile unsigned long *addr) +{ + return 1UL & (addr[BITOP_WORD(nr)] >> (nr & (BITS_PER_LONG-1))); +} + +#endif /* _ARM_BITOPS_H */ diff --git a/src/target/firmware/include/asm/ctype.h b/src/target/firmware/include/asm/ctype.h new file mode 100644 index 00000000..afa36392 --- /dev/null +++ b/src/target/firmware/include/asm/ctype.h @@ -0,0 +1,54 @@ +#ifndef _LINUX_CTYPE_H +#define _LINUX_CTYPE_H + +/* + * NOTE! This ctype does not handle EOF like the standard C + * library is required to. + */ + +#define _U 0x01 /* upper */ +#define _L 0x02 /* lower */ +#define _D 0x04 /* digit */ +#define _C 0x08 /* cntrl */ +#define _P 0x10 /* punct */ +#define _S 0x20 /* white space (space/lf/tab) */ +#define _X 0x40 /* hex digit */ +#define _SP 0x80 /* hard space (0x20) */ + +extern unsigned char _ctype[]; + +#define __ismask(x) (_ctype[(int)(unsigned char)(x)]) + +#define isalnum(c) ((__ismask(c)&(_U|_L|_D)) != 0) +#define isalpha(c) ((__ismask(c)&(_U|_L)) != 0) +#define iscntrl(c) ((__ismask(c)&(_C)) != 0) +#define isdigit(c) ((__ismask(c)&(_D)) != 0) +#define isgraph(c) ((__ismask(c)&(_P|_U|_L|_D)) != 0) +#define islower(c) ((__ismask(c)&(_L)) != 0) +#define isprint(c) ((__ismask(c)&(_P|_U|_L|_D|_SP)) != 0) +#define ispunct(c) ((__ismask(c)&(_P)) != 0) +#define isspace(c) ((__ismask(c)&(_S)) != 0) +#define isupper(c) ((__ismask(c)&(_U)) != 0) +#define isxdigit(c) ((__ismask(c)&(_D|_X)) != 0) + +#define isascii(c) (((unsigned char)(c))<=0x7f) +#define toascii(c) (((unsigned char)(c))&0x7f) + +static inline unsigned char __tolower(unsigned char c) +{ + if (isupper(c)) + c -= 'A'-'a'; + return c; +} + +static inline unsigned char __toupper(unsigned char c) +{ + if (islower(c)) + c -= 'a'-'A'; + return c; +} + +#define tolower(c) __tolower(c) +#define toupper(c) __toupper(c) + +#endif diff --git a/src/target/firmware/include/asm/div64.h b/src/target/firmware/include/asm/div64.h new file mode 100644 index 00000000..36826168 --- /dev/null +++ b/src/target/firmware/include/asm/div64.h @@ -0,0 +1,48 @@ +#ifndef __ASM_ARM_DIV64 +#define __ASM_ARM_DIV64 + +#include + +/* + * The semantics of do_div() are: + * + * uint32_t do_div(uint64_t *n, uint32_t base) + * { + * uint32_t remainder = *n % base; + * *n = *n / base; + * return remainder; + * } + * + * In other words, a 64-bit dividend with a 32-bit divisor producing + * a 64-bit result and a 32-bit remainder. To accomplish this optimally + * we call a special __do_div64 helper with completely non standard + * calling convention for arguments and results (beware). + */ + +#ifdef __ARMEB__ +#define __xh "r0" +#define __xl "r1" +#else +#define __xl "r0" +#define __xh "r1" +#endif + +#define do_div(n,base) \ +({ \ + register unsigned int __base asm("r4") = base; \ + register unsigned long long __n asm("r0") = n; \ + register unsigned long long __res asm("r2"); \ + register unsigned int __rem asm(__xh); \ + asm( __asmeq("%0", __xh) \ + __asmeq("%1", "r2") \ + __asmeq("%2", "r0") \ + __asmeq("%3", "r4") \ + "bl __do_div64" \ + : "=r" (__rem), "=r" (__res) \ + : "r" (__n), "r" (__base) \ + : "ip", "lr", "cc"); \ + n = __res; \ + __rem; \ +}) + +#endif diff --git a/src/target/firmware/include/asm/linkage.h b/src/target/firmware/include/asm/linkage.h new file mode 100644 index 00000000..ac1c900f --- /dev/null +++ b/src/target/firmware/include/asm/linkage.h @@ -0,0 +1,18 @@ +#ifndef __ASM_LINKAGE_H +#define __ASM_LINKAGE_H + +/* asm-arm/linkage.h */ + +#define __ALIGN .align 0 +#define __ALIGN_STR ".align 0" + +/* linux/linkage.h */ + +#define ALIGN __ALIGN + +#define ENTRY(name) \ + .globl name; \ + ALIGN; \ + name: + +#endif diff --git a/src/target/firmware/include/asm/ptrace.h b/src/target/firmware/include/asm/ptrace.h new file mode 100644 index 00000000..f3a654e3 --- /dev/null +++ b/src/target/firmware/include/asm/ptrace.h @@ -0,0 +1,128 @@ +/* + * linux/include/asm-arm/ptrace.h + * + * Copyright (C) 1996-2003 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __ASM_ARM_PTRACE_H +#define __ASM_ARM_PTRACE_H + +/* + * PSR bits + */ +#define USR26_MODE 0x00000000 +#define FIQ26_MODE 0x00000001 +#define IRQ26_MODE 0x00000002 +#define SVC26_MODE 0x00000003 +#define USR_MODE 0x00000010 +#define FIQ_MODE 0x00000011 +#define IRQ_MODE 0x00000012 +#define SVC_MODE 0x00000013 +#define ABT_MODE 0x00000017 +#define UND_MODE 0x0000001b +#define SYSTEM_MODE 0x0000001f +#define MODE32_BIT 0x00000010 +#define MODE_MASK 0x0000001f +#define PSR_T_BIT 0x00000020 +#define PSR_F_BIT 0x00000040 +#define PSR_I_BIT 0x00000080 +#define PSR_J_BIT 0x01000000 +#define PSR_Q_BIT 0x08000000 +#define PSR_V_BIT 0x10000000 +#define PSR_C_BIT 0x20000000 +#define PSR_Z_BIT 0x40000000 +#define PSR_N_BIT 0x80000000 +#define PCMASK 0 + +/* + * Groups of PSR bits + */ +#define PSR_f 0xff000000 /* Flags */ +#define PSR_s 0x00ff0000 /* Status */ +#define PSR_x 0x0000ff00 /* Extension */ +#define PSR_c 0x000000ff /* Control */ + +#ifndef __ASSEMBLY__ + +/* + * This struct defines the way the registers are stored on the + * stack during a system call. Note that sizeof(struct pt_regs) + * has to be a multiple of 8. + */ +struct pt_regs { + long uregs[18]; +}; + +#define ARM_cpsr uregs[16] +#define ARM_pc uregs[15] +#define ARM_lr uregs[14] +#define ARM_sp uregs[13] +#define ARM_ip uregs[12] +#define ARM_fp uregs[11] +#define ARM_r10 uregs[10] +#define ARM_r9 uregs[9] +#define ARM_r8 uregs[8] +#define ARM_r7 uregs[7] +#define ARM_r6 uregs[6] +#define ARM_r5 uregs[5] +#define ARM_r4 uregs[4] +#define ARM_r3 uregs[3] +#define ARM_r2 uregs[2] +#define ARM_r1 uregs[1] +#define ARM_r0 uregs[0] +#define ARM_ORIG_r0 uregs[17] + +#define user_mode(regs) \ + (((regs)->ARM_cpsr & 0xf) == 0) + +#ifdef CONFIG_ARM_THUMB +#define thumb_mode(regs) \ + (((regs)->ARM_cpsr & PSR_T_BIT)) +#else +#define thumb_mode(regs) (0) +#endif + +#define processor_mode(regs) \ + ((regs)->ARM_cpsr & MODE_MASK) + +#define interrupts_enabled(regs) \ + (!((regs)->ARM_cpsr & PSR_I_BIT)) + +#define fast_interrupts_enabled(regs) \ + (!((regs)->ARM_cpsr & PSR_F_BIT)) + +#define condition_codes(regs) \ + ((regs)->ARM_cpsr & (PSR_V_BIT|PSR_C_BIT|PSR_Z_BIT|PSR_N_BIT)) + +/* Are the current registers suitable for user mode? + * (used to maintain security in signal handlers) + */ +static inline int valid_user_regs(struct pt_regs *regs) +{ + if (user_mode(regs) && + (regs->ARM_cpsr & (PSR_F_BIT|PSR_I_BIT)) == 0) + return 1; + + /* + * Force CPSR to something logical... + */ + regs->ARM_cpsr &= PSR_f | PSR_s | PSR_x | PSR_T_BIT | MODE32_BIT; + + return 0; +} + +#define pc_pointer(v) \ + ((v) & ~PCMASK) + +#define instruction_pointer(regs) \ + (pc_pointer((regs)->ARM_pc)) + +#define profile_pc(regs) instruction_pointer(regs) + +#endif /* __ASSEMBLY__ */ + +#endif + diff --git a/src/target/firmware/include/asm/system.h b/src/target/firmware/include/asm/system.h new file mode 100644 index 00000000..2bf0cc51 --- /dev/null +++ b/src/target/firmware/include/asm/system.h @@ -0,0 +1,109 @@ +#ifndef __ASM_ARM_SYSTEM_H +#define __ASM_ARM_SYSTEM_H + +/* Generic ARM7TDMI (ARMv4T) synchronisation primitives, mostly + * taken from Linux kernel source, licensed under GPL */ + +#define local_irq_save(x) \ + ({ \ + unsigned long temp; \ + (void) (&temp == &x); \ + __asm__ __volatile__( \ + "mrs %0, cpsr @ local_irq_save\n" \ +" orr %1, %0, #128\n" \ +" msr cpsr_c, %1" \ + : "=r" (x), "=r" (temp) \ + : \ + : "memory", "cc"); \ + }) + +/* + * Enable IRQs + */ +#define local_irq_enable() \ + ({ \ + unsigned long temp; \ + __asm__ __volatile__( \ + "mrs %0, cpsr @ local_irq_enable\n" \ +" bic %0, %0, #128\n" \ +" msr cpsr_c, %0" \ + : "=r" (temp) \ + : \ + : "memory", "cc"); \ + }) + +/* + * Disable IRQs + */ +#define local_irq_disable() \ + ({ \ + unsigned long temp; \ + __asm__ __volatile__( \ + "mrs %0, cpsr @ local_irq_disable\n" \ +" orr %0, %0, #128\n" \ +" msr cpsr_c, %0" \ + : "=r" (temp) \ + : \ + : "memory", "cc"); \ + }) + +/* + * Enable FIQs + */ +#define local_fiq_enable() \ + ({ \ + unsigned long temp; \ + __asm__ __volatile__( \ + "mrs %0, cpsr @ stf\n" \ +" bic %0, %0, #64\n" \ +" msr cpsr_c, %0" \ + : "=r" (temp) \ + : \ + : "memory", "cc"); \ + }) + +/* + * Disable FIQs + */ +#define local_fiq_disable() \ + ({ \ + unsigned long temp; \ + __asm__ __volatile__( \ + "mrs %0, cpsr @ clf\n" \ +" orr %0, %0, #64\n" \ +" msr cpsr_c, %0" \ + : "=r" (temp) \ + : \ + : "memory", "cc"); \ + }) + +/* + * Save the current interrupt enable state. + */ +#define local_save_flags(x) \ + ({ \ + __asm__ __volatile__( \ + "mrs %0, cpsr @ local_save_flags" \ + : "=r" (x) : : "memory", "cc"); \ + }) + +/* + * restore saved IRQ & FIQ state + */ +#define local_irq_restore(x) \ + __asm__ __volatile__( \ + "msr cpsr_c, %0 @ local_irq_restore\n" \ + : \ + : "r" (x) \ + : "memory", "cc") + +#define irqs_disabled() \ +({ \ + unsigned long flags; \ + local_save_flags(flags); \ + (int)(flags & PSR_I_BIT); \ +}) + +#define __asmeq(x, y) ".ifnc " x "," y " ; .err ; .endif\n\t" + +#endif diff --git a/src/target/firmware/include/board.h b/src/target/firmware/include/board.h new file mode 100644 index 00000000..558b636f --- /dev/null +++ b/src/target/firmware/include/board.h @@ -0,0 +1,6 @@ +#ifndef _BOARD_H +#define _BOARD_H + +void board_init(void); + +#endif /* _BOARD_H */ diff --git a/src/target/firmware/include/calypso/backlight.h b/src/target/firmware/include/calypso/backlight.h new file mode 100644 index 00000000..3a6abd55 --- /dev/null +++ b/src/target/firmware/include/calypso/backlight.h @@ -0,0 +1,10 @@ +#ifndef _CAL_BACKLIGHT_H +#define _CAL_BACKLIGHT_H + +/* Switch backlight to PWL mode (or back) */ +void bl_mode_pwl(int on); + +/* Set the backlight level */ +void bl_level(uint8_t level); + +#endif /* CAL_BACKLIGHT_H */ diff --git a/src/target/firmware/include/calypso/clock.h b/src/target/firmware/include/calypso/clock.h new file mode 100644 index 00000000..abcfde1d --- /dev/null +++ b/src/target/firmware/include/calypso/clock.h @@ -0,0 +1,67 @@ +#ifndef _CALYPSO_CLK_H +#define _CALYPSO_CLK_H + +#include + +#define CALYPSO_PLL26_52_MHZ ((2 << 8) | 0) +#define CALYPSO_PLL26_86_7_MHZ ((10 << 8) | 2) +#define CALYPSO_PLL26_87_MHZ ((3 << 8) | 0) +#define CALYPSO_PLL13_104_MHZ ((8 << 8) | 0) + +enum mclk_div { + _ARM_MCLK_DIV_1 = 0, + ARM_MCLK_DIV_1 = 1, + ARM_MCLK_DIV_2 = 2, + ARM_MCLK_DIV_3 = 3, + ARM_MCLK_DIV_4 = 4, + ARM_MCLK_DIV_5 = 5, + ARM_MCLK_DIV_6 = 6, + ARM_MCLK_DIV_7 = 7, + ARM_MCLK_DIV_1_5 = 0x80 | 1, + ARM_MCLK_DIV_2_5 = 0x80 | 2, +}; + +void calypso_clock_set(uint8_t vtcxo_div2, uint16_t inp, enum mclk_div mclk_div); +void calypso_pll_set(uint16_t inp); +void calypso_clk_dump(void); + +/* CNTL_RST */ +enum calypso_rst { + RESET_DSP = (1 << 1), + RESET_EXT = (1 << 2), + RESET_WDOG = (1 << 3), +}; + +void calypso_reset_set(enum calypso_rst calypso_rst, int active); +int calypso_reset_get(enum calypso_rst); + +enum calypso_bank { + CALYPSO_nCS0 = 0, + CALYPSO_nCS1 = 2, + CALYPSO_nCS2 = 4, + CALYPSO_nCS3 = 6, + CALYPSO_nCS7 = 8, + CALYPSO_CS4 = 0xa, + CALYPSO_nCS6 = 0xc, +}; + +enum calypso_mem_width { + CALYPSO_MEM_8bit = 0, + CALYPSO_MEM_16bit = 1, + CALYPSO_MEM_32bit = 2, +}; + +void calypso_mem_cfg(enum calypso_bank bank, uint8_t ws, + enum calypso_mem_width width, int we); + +/* Enable or disable the internal bootrom mapped to 0x0000'0000 */ +void calypso_bootrom(int enable); + +/* Enable or disable the debug unit */ +void calypso_debugunit(int enable); + +/* configure the RHEA bus bridge[s] */ +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); + +#endif /* _CALYPSO_CLK_H */ diff --git a/src/target/firmware/include/calypso/dma.h b/src/target/firmware/include/calypso/dma.h new file mode 100644 index 00000000..00b9bde7 --- /dev/null +++ b/src/target/firmware/include/calypso/dma.h @@ -0,0 +1,6 @@ +#ifndef _CALYPSO_DMA_H +#define _CALYPSO_DMA_H + +void dma_init(void); + +#endif /* _CALYPSO_DMA_H */ diff --git a/src/target/firmware/include/calypso/dsp.h b/src/target/firmware/include/calypso/dsp.h new file mode 100644 index 00000000..24779a62 --- /dev/null +++ b/src/target/firmware/include/calypso/dsp.h @@ -0,0 +1,31 @@ +#ifndef _CALYPSO_DSP_H +#define _CALYPSO_DSP_H + +#include + +struct dsp_api { + T_NDB_MCU_DSP *ndb; + T_DB_DSP_TO_MCU *db_r; + T_DB_MCU_TO_DSP *db_w; + T_PARAM_MCU_DSP *param; + int r_page; + int w_page; + int r_page_used; + int frame_ctr; +}; + +extern struct dsp_api dsp_api; + +void dsp_power_on(void); +void dsp_dump_version(void); +void dsp_dump(void); +void dsp_checksum_task(void); +void dsp_api_memset(uint16_t *ptr, int octets); +void dsp_load_afc_dac(uint16_t afc); +void dsp_load_apc_dac(uint16_t apc); +void dsp_end_scenario(void); + +void dsp_load_rx_task(uint16_t task, uint8_t burst_id, uint8_t tsc); +void dsp_load_tx_task(uint16_t task, uint8_t burst_id, uint8_t tsc); + +#endif diff --git a/src/target/firmware/include/calypso/dsp_api.h b/src/target/firmware/include/calypso/dsp_api.h new file mode 100644 index 00000000..5444ec1d --- /dev/null +++ b/src/target/firmware/include/calypso/dsp_api.h @@ -0,0 +1,1554 @@ +#ifndef _CAL_DSP_API_H +#define _CAL_DSP_API_H + +/* This is a header file with structures imported from the TSM30 source code (l1_defty.h) + * + * As this header file only is a list of definitions and data structures, it is + * not ocnsidered to be a copyrightable work itself. + * + * Nonetheless, it might be good to rewrite it (without ugly typedefs!) */ + +#if(L1_DYN_DSP_DWNLD == 1) + #include "l1_dyn_dwl_defty.h" +#endif + +/* Include a header file that defines everything this l1_defty.h needs */ +#include "l1_environment.h" + +#define BASE_API_NDB 0xFFD001A8L /* 268 words */ +#define BASE_API_PARAM 0xFFD00862L /* 57 words */ +#define BASE_API_R_PAGE_0 0xFFD00050L /* 20 words */ +#define BASE_API_R_PAGE_1 0xFFD00078L /* 20 words */ +#define BASE_API_W_PAGE_0 0xFFD00000L /* 20 words */ +#define BASE_API_W_PAGE_1 0xFFD00028L /* 20 words */ + + +/***********************************************************/ +/* */ +/* Data structure for global info components. */ +/* */ +/***********************************************************/ + +typedef struct +{ + API d_task_d; // (0) Downlink task command. + API d_burst_d; // (1) Downlink burst identifier. + API d_task_u; // (2) Uplink task command. + API d_burst_u; // (3) Uplink burst identifier. + API d_task_md; // (4) Downlink Monitoring (FB/SB) command. +#if (DSP == 33) || (DSP == 34) || (DSP == 35) || (DSP == 36) + API d_background; // (5) Background tasks +#else + API d_reserved; // (5) Reserved +#endif + API d_debug; // (6) Debug/Acknowledge/general purpose word. + API d_task_ra; // (7) RA task command. + API d_fn; // (8) FN, in Rep. period and FN%104, used for TRAFFIC/TCH only. + // bit [0..7] -> b_fn_report, FN in the normalized reporting period. + // bit [8..15] -> b_fn_sid, FN % 104, used for SID positionning. + API d_ctrl_tch; // (9) Tch channel description. + // bit [0..3] -> b_chan_mode, channel mode. + // bit [4..5] -> b_chan_type, channel type. + // bit [6] -> reset SACCH + // bit [7] -> vocoder ON + // bit [8] -> b_sync_tch_ul, synchro. TCH/UL. + // bit [9] -> b_sync_tch_dl, synchro. TCH/DL. + // bit [10] -> b_stop_tch_ul, stop TCH/UL. + // bit [11] -> b_stop_tch_dl, stop TCH/DL. + // bit [12.13] -> b_tch_loop, tch loops A/B/C. + API hole; // (10) unused hole. + +#if ((ANLG_FAM == 1) || (ANLG_FAM == 2) || (ANLG_FAM == 3)) + API d_ctrl_abb; // (11) Bit field indicating the analog baseband register to send. + // bit [0] -> b_ramp: the ramp information(a_ramp[]) is located in NDB + // bit [1.2] -> unused + // bit [3] -> b_apcdel: delays-register in NDB + // bit [4] -> b_afc: freq control register in DB + // bit [5..15] -> unused +#endif + API a_a5fn[2]; // (12..13) Encryption Frame number. + // word 0, bit [0..4] -> T2. + // word 0, bit [5..10] -> T3. + // word 1, bit [0..11] -> T1. + API d_power_ctl; // (14) Power level control. + API d_afc; // (15) AFC value (enabled by "b_afc" in "d_ctrl_TCM4400 or in d_ctrl_abb"). + API d_ctrl_system; // (16) Controle Register for RESET/RESUME. + // bit [0..2] -> b_tsq, training sequence. + // bit [3] -> b_bcch_freq_ind, BCCH frequency indication. + // bit [15] -> b_task_abort, DSP task abort command. +} +T_DB_MCU_TO_DSP; + +typedef struct +{ + API d_task_d; // (0) Downlink task command. + API d_burst_d; // (1) Downlink burst identifier. + API d_task_u; // (2) Uplink task command. + API d_burst_u; // (3) Uplink burst identifier. + API d_task_md; // (4) Downlink Monitoring (FB/SB) task command. +#if (DSP == 33) || (DSP == 34) || (DSP == 35) || (DSP == 36) + API d_background; // (5) Background tasks +#else + API d_reserved; // (5) Reserved +#endif + API d_debug; // (6) Debug/Acknowledge/general purpose word. + API d_task_ra; // (7) RA task command. + +#if (DSP == 33) || (DSP == 34) || (DSP == 35) || (DSP == 36) + API a_serv_demod[4]; // ( 8..11) Serv. cell demod. result, array of 4 words (D_TOA,D_PM,D_ANGLE,D_SNR). + API a_pm[3]; // (12..14) Power measurement results, array of 3 words. + API a_sch[5]; // (15..19) Header + SB information, array of 5 words. +#else + API a_pm[3]; // ( 8..10) Power measurement results, array of 3 words. + API a_serv_demod[4]; // (11..14) Serv. cell demod. result, array of 4 words (D_TOA,D_PM,D_ANGLE,D_SNR). + API a_sch[5]; // (15..19) Header + SB information, array of 5 words. +#endif +} +T_DB_DSP_TO_MCU; + +#if (DSP == 34) || (DSP == 35) || (DSP == 36) // NDB GSM + typedef struct + { + // MISC Tasks + API d_dsp_page; + + // DSP status returned (DSP --> MCU). + API d_error_status; + + // RIF control (MCU -> DSP). + API d_spcx_rif; + + API d_tch_mode; // TCH mode register. + // bit [0..1] -> b_dai_mode. + // bit [2] -> b_dtx. + + API d_debug1; // bit 0 at 1 enable dsp f_tx delay for Omega + + API d_dsp_test; + + // Words dedicated to Software version (DSP code + Patch) + API d_version_number1; + API d_version_number2; + + API d_debug_ptr; + API d_debug_bk; + + API d_pll_config; + + // GSM/GPRS DSP Debug trace support + API p_debug_buffer; + API d_debug_buffer_size; + API d_debug_trace_type; + + #if (W_A_DSP_IDLE3 == 1) + // DSP report its state: 0 run, 1 Idle1, 2 Idle2, 3 Idle3. + API d_dsp_state; + // 5 words are reserved for any possible mapping modification + API d_hole1_ndb[2]; + #else + // 6 words are reserved for any possible mapping modification + API d_hole1_ndb[3]; + #endif + + #if (AMR == 1) + API p_debug_amr; + #else + API d_hole_debug_amr; + #endif + + #if (CHIPSET == 12) + #if (DSP == 35) || (DSP == 36) + API d_hole2_ndb[1]; + API d_mcsi_select; + #else + API d_hole2_ndb[2]; + #endif + #else + API d_hole2_ndb[2]; + #endif + + // New words APCDEL1 and APCDEL2 for 2TX: TX/PRACH combinations + API d_apcdel1_bis; + API d_apcdel2_bis; + + + // New registers due to IOTA analog base band + API d_apcdel2; + API d_vbctrl2; + API d_bulgcal; + + // Analog Based Band + API d_afcctladd; + + API d_vbuctrl; + API d_vbdctrl; + API d_apcdel1; + API d_apcoff; + API d_bulioff; + API d_bulqoff; + API d_dai_onoff; + API d_auxdac; + + #if (ANLG_FAM == 1) + API d_vbctrl; + #elif ((ANLG_FAM == 2) || (ANLG_FAM == 3)) + API d_vbctrl1; + #endif + + API d_bbctrl; + + // Monitoring tasks control (MCU <- DSP) + // FB task + API d_fb_det; // FB detection result. (1 for FOUND). + API d_fb_mode; // Mode for FB detection algorithm. + API a_sync_demod[4]; // FB/SB demod. result, (D_TOA,D_PM,D_ANGLE,D_SNR). + + // SB Task + API a_sch26[5]; // Header + SB information, array of 5 words. + + API d_audio_gain_ul; + API d_audio_gain_dl; + + // Controller of the melody E2 audio compressor + API d_audio_compressor_ctrl; + + // AUDIO module + API d_audio_init; + API d_audio_status; + + // Audio tasks + // TONES (MCU -> DSP) + API d_toneskb_init; + API d_toneskb_status; + API d_k_x1_t0; + API d_k_x1_t1; + API d_k_x1_t2; + API d_pe_rep; + API d_pe_off; + API d_se_off; + API d_bu_off; + API d_t0_on; + API d_t0_off; + API d_t1_on; + API d_t1_off; + API d_t2_on; + API d_t2_off; + API d_k_x1_kt0; + API d_k_x1_kt1; + API d_dur_kb; + API d_shiftdl; + API d_shiftul; + + API d_aec_ctrl; + + API d_es_level_api; + API d_mu_api; + + // Melody Ringer module + API d_melo_osc_used; + API d_melo_osc_active; + API a_melo_note0[4]; + API a_melo_note1[4]; + API a_melo_note2[4]; + API a_melo_note3[4]; + API a_melo_note4[4]; + API a_melo_note5[4]; + API a_melo_note6[4]; + API a_melo_note7[4]; + + // selection of the melody format + API d_melody_selection; + + // Holes due to the format melody E1 + API a_melo_holes[3]; + + // Speech Recognition module + API d_sr_status; // status of the DSP speech reco task + API d_sr_param; // paramters for the DSP speech reco task: OOV threshold. + API d_sr_bit_exact_test; // bit exact test + API d_sr_nb_words; // number of words used in the speech recognition task + API d_sr_db_level; // estimate voice level in dB + API d_sr_db_noise; // estimate noise in dB + API d_sr_mod_size; // size of the model + API a_n_best_words[4]; // array of the 4 best words + API a_n_best_score[8]; // array of the 4 best scores (each score is 32 bits length) + + // Audio buffer + API a_dd_1[22]; // Header + DATA traffic downlink information, sub. chan. 1. + API a_du_1[22]; // Header + DATA traffic uplink information, sub. chan. 1. + + // V42bis module + API d_v42b_nego0; + API d_v42b_nego1; + API d_v42b_control; + API d_v42b_ratio_ind; + API d_mcu_control; + API d_mcu_control_sema; + + // Background tasks + API d_background_enable; + API d_background_abort; + API d_background_state; + API d_max_background; + API a_background_tasks[16]; + API a_back_task_io[16]; + + // GEA module defined in l1p_deft.h (the following section is overlaid with GPRS NDB memory) + API d_gea_mode_ovly; + API a_gea_kc_ovly[4]; + +#if (ANLG_FAM == 3) + // SYREN specific registers + API d_vbpop; + API d_vau_delay_init; + API d_vaud_cfg; + API d_vauo_onoff; + API d_vaus_vol; + API d_vaud_pll; + API d_hole3_ndb[1]; +#elif ((ANLG_FAM == 1) || (ANLG_FAM == 2)) + + API d_hole3_ndb[7]; + +#endif + + // word used for the init of USF threshold + API d_thr_usf_detect; + + // Encryption module + API d_a5mode; // Encryption Mode. + + API d_sched_mode_gprs_ovly; + + // 7 words are reserved for any possible mapping modification + API d_hole4_ndb[5]; + + // Ramp definition for Omega device + API a_ramp[16]; + + // CCCH/SACCH downlink information...(!!) + API a_cd[15]; // Header + CCCH/SACCH downlink information. + + // FACCH downlink information........(!!) + API a_fd[15]; // Header + FACCH downlink information. + + // Traffic downlink data frames......(!!) + API a_dd_0[22]; // Header + DATA traffic downlink information, sub. chan. 0. + + // CCCH/SACCH uplink information.....(!!) + API a_cu[15]; // Header + CCCH/SACCH uplink information. + + // FACCH downlink information........(!!) + API a_fu[15]; // Header + FACCH uplink information + + // Traffic downlink data frames......(!!) + API a_du_0[22]; // Header + DATA traffic uplink information, sub. chan. 0. + + // Random access.....................(MCU -> DSP). + API d_rach; // RACH information. + + //...................................(MCU -> DSP). + API a_kc[4]; // Encryption Key Code. + + // Integrated Data Services module + API d_ra_conf; + API d_ra_act; + API d_ra_test; + API d_ra_statu; + API d_ra_statd; + API d_fax; + API a_data_buf_ul[21]; + API a_data_buf_dl[37]; + + // GTT API mapping for DSP code 34 (for test only) + #if (L1_GTT == 1) + API d_tty_status; + API d_tty_detect_thres; + API d_ctm_detect_shift; + API d_tty_fa_thres; + API d_tty_mod_norm; + API d_tty_reset_buffer_ul; + API d_tty_loop_ctrl; + API p_tty_loop_buffer; + #else + API a_tty_holes[8]; + #endif + + API a_sr_holes0[414]; + + #if (L1_NEW_AEC) + // new AEC + API d_cont_filter; + API d_granularity_att; + API d_coef_smooth; + API d_es_level_max; + API d_fact_vad; + API d_thrs_abs; + API d_fact_asd_fil; + API d_fact_asd_mut; + API d_far_end_pow_h; + API d_far_end_pow_l; + API d_far_end_noise_h; + API d_far_end_noise_l; + #else + API a_new_aec_holes[12]; + #endif // L1_NEW_AEC + + // Speech recognition model + API a_sr_holes1[145]; + API d_cport_init; + API d_cport_ctrl; + API a_cport_cfr[2]; + API d_cport_tcl_tadt; + API d_cport_tdat; + API d_cport_tvs; + API d_cport_status; + API d_cport_reg_value; + + API a_cport_holes[1011]; + + API a_model[1041]; + + // EOTD buffer +#if (L1_EOTD==1) + API d_eotd_first; + API d_eotd_max; + API d_eotd_nrj_high; + API d_eotd_nrj_low; + API a_eotd_crosscor[18]; +#else + API a_eotd_holes[22]; +#endif + // AMR ver 1.0 buffers + API a_amr_config[4]; + API a_ratscch_ul[6]; + API a_ratscch_dl[6]; + API d_amr_snr_est; // estimation of the SNR of the AMR speech block + #if (L1_VOICE_MEMO_AMR) + API d_amms_ul_voc; + #else + API a_voice_memo_amr_holes[1]; + #endif + API d_thr_onset_afs; // thresh detection ONSET AFS + API d_thr_sid_first_afs; // thresh detection SID_FIRST AFS + API d_thr_ratscch_afs; // thresh detection RATSCCH AFS + API d_thr_update_afs; // thresh detection SID_UPDATE AFS + API d_thr_onset_ahs; // thresh detection ONSET AHS + API d_thr_sid_ahs; // thresh detection SID frames AHS + API d_thr_ratscch_marker;// thresh detection RATSCCH MARKER + API d_thr_sp_dgr; // thresh detection SPEECH DEGRADED/NO_DATA + API d_thr_soft_bits; + #if (MELODY_E2) + API d_melody_e2_osc_stop; + API d_melody_e2_osc_active; + API d_melody_e2_semaphore; + API a_melody_e2_osc[16][3]; + API d_melody_e2_globaltimefactor; + API a_melody_e2_instrument_ptr[8]; + API d_melody_e2_deltatime; + + #if (AMR_THRESHOLDS_WORKAROUND) + API a_d_macc_thr_afs[8]; + API a_d_macc_thr_ahs[6]; + #else + API a_melody_e2_holes0[14]; + #endif + + API a_melody_e2_holes1[693]; + API a_dsp_trace[SC_AUDIO_MELODY_E2_MAX_SIZE_OF_DSP_TRACE]; + API a_melody_e2_instrument_wave[SC_AUDIO_MELODY_E2_MAX_SIZE_OF_INSTRUMENT]; + #else + API d_holes[61]; + #if (AMR_THRESHOLDS_WORKAROUND) + API a_d_macc_thr_afs[8]; + API a_d_macc_thr_ahs[6]; + #endif + #endif + + } + T_NDB_MCU_DSP; +#elif (DSP == 33) // NDB GSM + typedef struct + { + // MISC Tasks + API d_dsp_page; + + // DSP status returned (DSP --> MCU). + API d_error_status; + + // RIF control (MCU -> DSP). + API d_spcx_rif; + + API d_tch_mode; // TCH mode register. + // bit [0..1] -> b_dai_mode. + // bit [2] -> b_dtx. + + API d_debug1; // bit 0 at 1 enable dsp f_tx delay for Omega + + API d_dsp_test; + + // Words dedicated to Software version (DSP code + Patch) + API d_version_number1; + API d_version_number2; + + API d_debug_ptr; + API d_debug_bk; + + API d_pll_config; + + // GSM/GPRS DSP Debug trace support + API p_debug_buffer; + API d_debug_buffer_size; + API d_debug_trace_type; + + #if (W_A_DSP_IDLE3 == 1) + // DSP report its state: 0 run, 1 Idle1, 2 Idle2, 3 Idle3. + API d_dsp_state; + // 10 words are reserved for any possible mapping modification + API d_hole1_ndb[5]; + #else + // 11 words are reserved for any possible mapping modification + API d_hole1_ndb[6]; + #endif + + // New words APCDEL1 and APCDEL2 for 2TX: TX/PRACH combinations + API d_apcdel1_bis; + API d_apcdel2_bis; + + + // New registers due to IOTA analog base band + API d_apcdel2; + API d_vbctrl2; + API d_bulgcal; + + // Analog Based Band + API d_afcctladd; + + API d_vbuctrl; + API d_vbdctrl; + API d_apcdel1; + API d_apcoff; + API d_bulioff; + API d_bulqoff; + API d_dai_onoff; + API d_auxdac; + + #if (ANLG_FAM == 1) + API d_vbctrl; + #elif ((ANLG_FAM == 2) || (ANLG_FAM == 3)) + API d_vbctrl1; + #endif + + API d_bbctrl; + + // Monitoring tasks control (MCU <- DSP) + // FB task + API d_fb_det; // FB detection result. (1 for FOUND). + API d_fb_mode; // Mode for FB detection algorithm. + API a_sync_demod[4]; // FB/SB demod. result, (D_TOA,D_PM,D_ANGLE,D_SNR). + + // SB Task + API a_sch26[5]; // Header + SB information, array of 5 words. + + API d_audio_gain_ul; + API d_audio_gain_dl; + + // Controller of the melody E2 audio compressor + API d_audio_compressor_ctrl; + + // AUDIO module + API d_audio_init; + API d_audio_status; + + // Audio tasks + // TONES (MCU -> DSP) + API d_toneskb_init; + API d_toneskb_status; + API d_k_x1_t0; + API d_k_x1_t1; + API d_k_x1_t2; + API d_pe_rep; + API d_pe_off; + API d_se_off; + API d_bu_off; + API d_t0_on; + API d_t0_off; + API d_t1_on; + API d_t1_off; + API d_t2_on; + API d_t2_off; + API d_k_x1_kt0; + API d_k_x1_kt1; + API d_dur_kb; + API d_shiftdl; + API d_shiftul; + + API d_aec_ctrl; + + API d_es_level_api; + API d_mu_api; + + // Melody Ringer module + API d_melo_osc_used; + API d_melo_osc_active; + API a_melo_note0[4]; + API a_melo_note1[4]; + API a_melo_note2[4]; + API a_melo_note3[4]; + API a_melo_note4[4]; + API a_melo_note5[4]; + API a_melo_note6[4]; + API a_melo_note7[4]; + + // selection of the melody format + API d_melody_selection; + + // Holes due to the format melody E1 + API a_melo_holes[3]; + + // Speech Recognition module + API d_sr_status; // status of the DSP speech reco task + API d_sr_param; // paramters for the DSP speech reco task: OOV threshold. + API d_sr_bit_exact_test; // bit exact test + API d_sr_nb_words; // number of words used in the speech recognition task + API d_sr_db_level; // estimate voice level in dB + API d_sr_db_noise; // estimate noise in dB + API d_sr_mod_size; // size of the model + API a_n_best_words[4]; // array of the 4 best words + API a_n_best_score[8]; // array of the 4 best scores (each score is 32 bits length) + + // Audio buffer + API a_dd_1[22]; // Header + DATA traffic downlink information, sub. chan. 1. + API a_du_1[22]; // Header + DATA traffic uplink information, sub. chan. 1. + + // V42bis module + API d_v42b_nego0; + API d_v42b_nego1; + API d_v42b_control; + API d_v42b_ratio_ind; + API d_mcu_control; + API d_mcu_control_sema; + + // Background tasks + API d_background_enable; + API d_background_abort; + API d_background_state; + API d_max_background; + API a_background_tasks[16]; + API a_back_task_io[16]; + + // GEA module defined in l1p_deft.h (the following section is overlaid with GPRS NDB memory) + API d_gea_mode_ovly; + API a_gea_kc_ovly[4]; + + API d_hole3_ndb[8]; + + // Encryption module + API d_a5mode; // Encryption Mode. + + API d_sched_mode_gprs_ovly; + + // 7 words are reserved for any possible mapping modification + API d_hole4_ndb[5]; + + // Ramp definition for Omega device + API a_ramp[16]; + + // CCCH/SACCH downlink information...(!!) + API a_cd[15]; // Header + CCCH/SACCH downlink information. + + // FACCH downlink information........(!!) + API a_fd[15]; // Header + FACCH downlink information. + + // Traffic downlink data frames......(!!) + API a_dd_0[22]; // Header + DATA traffic downlink information, sub. chan. 0. + + // CCCH/SACCH uplink information.....(!!) + API a_cu[15]; // Header + CCCH/SACCH uplink information. + + // FACCH downlink information........(!!) + API a_fu[15]; // Header + FACCH uplink information + + // Traffic downlink data frames......(!!) + API a_du_0[22]; // Header + DATA traffic uplink information, sub. chan. 0. + + // Random access.....................(MCU -> DSP). + API d_rach; // RACH information. + + //...................................(MCU -> DSP). + API a_kc[4]; // Encryption Key Code. + + // Integrated Data Services module + API d_ra_conf; + API d_ra_act; + API d_ra_test; + API d_ra_statu; + API d_ra_statd; + API d_fax; + API a_data_buf_ul[21]; + API a_data_buf_dl[37]; + + #if (L1_NEW_AEC) + // new AEC + API a_new_aec_holes[422]; + API d_cont_filter; + API d_granularity_att; + API d_coef_smooth; + API d_es_level_max; + API d_fact_vad; + API d_thrs_abs; + API d_fact_asd_fil; + API d_fact_asd_mut; + API d_far_end_pow_h; + API d_far_end_pow_l; + API d_far_end_noise_h; + API d_far_end_noise_l; + #endif + + // Speech recognition model + #if (L1_NEW_AEC) + API a_sr_holes[1165]; + #else + API a_sr_holes[1599]; + #endif // L1_NEW_AEC + API a_model[1041]; + + // EOTD buffer + #if (L1_EOTD==1) + API d_eotd_first; + API d_eotd_max; + API d_eotd_nrj_high; + API d_eotd_nrj_low; + API a_eotd_crosscor[18]; + #else + API a_eotd_holes[22]; + #endif + + #if (MELODY_E2) + API a_melody_e2_holes0[27]; + API d_melody_e2_osc_used; + API d_melody_e2_osc_active; + API d_melody_e2_semaphore; + API a_melody_e2_osc[16][3]; + API d_melody_e2_globaltimefactor; + API a_melody_e2_instrument_ptr[8]; + API a_melody_e2_holes1[708]; + API a_dsp_trace[SC_AUDIO_MELODY_E2_MAX_SIZE_OF_DSP_TRACE]; + API a_melody_e2_instrument_wave[SC_AUDIO_MELODY_E2_MAX_SIZE_OF_INSTRUMENT]; + #endif + } + T_NDB_MCU_DSP; + +#elif ((DSP == 32) || (DSP == 31)) + typedef struct + { + // Monitoring tasks control..........(MCU <- DSP) + API d_fb_det; // FB detection result. (1 for FOUND). + API d_fb_mode; // Mode for FB detection algorithm. + API a_sync_demod[4]; // FB/SB demod. result, (D_TOA,D_PM,D_ANGLE,D_SNR). + + // CCCH/SACCH downlink information...(!!) + API a_cd[15]; // Header + CCCH/SACCH downlink information. + + // FACCH downlink information........(!!) + API a_fd[15]; // Header + FACCH downlink information. + + // Traffic downlink data frames......(!!) + API a_dd_0[22]; // Header + DATA traffic downlink information, sub. chan. 0. + API a_dd_1[22]; // Header + DATA traffic downlink information, sub. chan. 1. + + // CCCH/SACCH uplink information.....(!!) + API a_cu[15]; // Header + CCCH/SACCH uplink information. + + #if (SPEECH_RECO) + // FACCH downlink information........(!!) + API a_fu[3]; // Header + FACCH uplink information + // The size of this buffer is 15 word but some speech reco words + // are overlayer with this buffer. This is the reason why the size is 3 instead of 15. + API d_sr_status; // status of the DSP speech reco task + API d_sr_param; // paramters for the DSP speech reco task: OOV threshold. + API sr_hole1; // hole + API d_sr_bit_exact_test; // bit exact test + API d_sr_nb_words; // number of words used in the speech recognition task + API d_sr_db_level; // estimate voice level in dB + API d_sr_db_noise; // estimate noise in dB + API d_sr_mod_size; // size of the model + API sr_holes_1[4]; // hole + #else + // FACCH downlink information........(!!) + API a_fu[15]; // Header + FACCH uplink information + #endif + + // Traffic uplink data frames........(!!) + API a_du_0[22]; // Header + DATA traffic uplink information, sub. chan. 0. + API a_du_1[22]; // Header + DATA traffic uplink information, sub. chan. 1. + + // Random access.....................(MCU -> DSP). + API d_rach; // RACH information. + + //...................................(MCU -> DSP). + API d_a5mode; // Encryption Mode. + API a_kc[4]; // Encryption Key Code. + API d_tch_mode; // TCH mode register. + // bit [0..1] -> b_dai_mode. + // bit [2] -> b_dtx. + + // OMEGA...........................(MCU -> DSP). + #if ((ANLG_FAM == 1) || (ANLG_FAM == 2)) + API a_ramp[16]; + #if (MELODY_E1) + API d_melo_osc_used; + API d_melo_osc_active; + API a_melo_note0[4]; + API a_melo_note1[4]; + API a_melo_note2[4]; + API a_melo_note3[4]; + API a_melo_note4[4]; + API a_melo_note5[4]; + API a_melo_note6[4]; + API a_melo_note7[4]; + #if (DSP==31) + // selection of the melody format + API d_melody_selection; + API holes[9]; + #else // DSP==32 + API d_dco_type; // Tide + API p_start_IQ; + API d_level_off; + API d_dco_dbg; + API d_tide_resa; + API d_asynch_margin; // Perseus Asynch Audio Workaround + API hole[4]; + #endif // DSP 32 + + #else // NO MELODY E1 + #if (DSP==31) + // selection of the melody format + API d_melody_selection; + API holes[43]; // 43 unused holes. + #else // DSP==32 + API holes[34]; // 34 unused holes. + API d_dco_type; // Tide + API p_start_IQ; + API d_level_off; + API d_dco_dbg; + API d_tide_resa; + API d_asynch_margin; // Perseus Asynch Audio Workaround + API hole[4]; + #endif //DSP == 32 + #endif // NO MELODY E1 + + API d_debug3; + API d_debug2; + API d_debug1; // bit 0 at 1 enable dsp f_tx delay for Omega + API d_afcctladd; + API d_vbuctrl; + API d_vbdctrl; + API d_apcdel1; + API d_aec_ctrl; + API d_apcoff; + API d_bulioff; + API d_bulqoff; + API d_dai_onoff; + API d_auxdac; + + #if (ANLG_FAM == 1) + API d_vbctrl; + #elif (ANLG_FAM == 2) + API d_vbctrl1; + #endif + + API d_bbctrl; + #else + #error DSPCODE not supported with given ANALOG + #endif //(ANALOG)1, 2 + //...................................(MCU -> DSP). + API a_sch26[5]; // Header + SB information, array of 5 words. + + // TONES.............................(MCU -> DSP) + API d_toneskb_init; + API d_toneskb_status; + API d_k_x1_t0; + API d_k_x1_t1; + API d_k_x1_t2; + API d_pe_rep; + API d_pe_off; + API d_se_off; + API d_bu_off; + API d_t0_on; + API d_t0_off; + API d_t1_on; + API d_t1_off; + API d_t2_on; + API d_t2_off; + API d_k_x1_kt0; + API d_k_x1_kt1; + API d_dur_kb; + + // PLL...............................(MCU -> DSP). + API d_pll_clkmod1; + API d_pll_clkmod2; + + // DSP status returned..........(DSP --> MCU). + API d_error_status; + + // RIF control.......................(MCU -> DSP). + API d_spcx_rif; + + API d_shiftdl; + API d_shiftul; + + API p_saec_prog; + API p_aec_prog; + API p_spenh_prog; + + API a_ovly[75]; + API d_ra_conf; + API d_ra_act; + API d_ra_test; + API d_ra_statu; + API d_ra_statd; + API d_fax; + #if (SPEECH_RECO) + API a_data_buf_ul[3]; + API a_n_best_words[4]; // array of the 4 best words + API a_n_best_score[8]; // array of the 4 best scores (each score is 32 bits length) + API sr_holes_2[6]; + API a_data_buf_dl[37]; + + API a_hole[24]; + + API d_sched_mode_gprs_ovly; + + API fir_holes1[384]; + API a_fir31_uplink[31]; + API a_fir31_downlink[31]; + API d_audio_init; + API d_audio_status; + + API a_model[1041]; // array of the speech reco model + #else + API a_data_buf_ul[21]; + API a_data_buf_dl[37]; + + API a_hole[24]; + + API d_sched_mode_gprs_ovly; + + API fir_holes1[384]; + API a_fir31_uplink[31]; + API a_fir31_downlink[31]; + API d_audio_init; + API d_audio_status; + +#if (L1_EOTD ==1) + API a_eotd_hole[369]; + + API d_eotd_first; + API d_eotd_max; + API d_eotd_nrj_high; + API d_eotd_nrj_low; + API a_eotd_crosscor[18]; +#endif + #endif + } + T_NDB_MCU_DSP; + + +#else // OTHER DSP CODE like 17 + +typedef struct +{ + // Monitoring tasks control..........(MCU <- DSP) + API d_fb_det; // FB detection result. (1 for FOUND). + API d_fb_mode; // Mode for FB detection algorithm. + API a_sync_demod[4]; // FB/SB demod. result, (D_TOA,D_PM,D_ANGLE,D_SNR). + + // CCCH/SACCH downlink information...(!!) + API a_cd[15]; // Header + CCCH/SACCH downlink information. + + // FACCH downlink information........(!!) + API a_fd[15]; // Header + FACCH downlink information. + + // Traffic downlink data frames......(!!) + #if (DATA14_4 == 0) + API a_dd_0[20]; // Header + DATA traffic downlink information, sub. chan. 0. + API a_dd_1[20]; // Header + DATA traffic downlink information, sub. chan. 1. + #endif + #if (DATA14_4 == 1) + API a_dd_0[22]; // Header + DATA traffic downlink information, sub. chan. 0. + API a_dd_1[22]; // Header + DATA traffic downlink information, sub. chan. 1. + #endif + + // CCCH/SACCH uplink information.....(!!) + API a_cu[15]; // Header + CCCH/SACCH uplink information. + + #if (SPEECH_RECO) + // FACCH downlink information........(!!) + API a_fu[3]; // Header + FACCH uplink information + // The size of this buffer is 15 word but some speech reco words + // are overlayer with this buffer. This is the reason why the size is 3 instead of 15. + API d_sr_status; // status of the DSP speech reco task + API d_sr_param; // paramters for the DSP speech reco task: OOV threshold. + API sr_hole1; // hole + API d_sr_bit_exact_test; // bit exact test + API d_sr_nb_words; // number of words used in the speech recognition task + API d_sr_db_level; // estimate voice level in dB + API d_sr_db_noise; // estimate noise in dB + API d_sr_mod_size; // size of the model + API sr_holes_1[4]; // hole + #else + // FACCH downlink information........(!!) + API a_fu[15]; // Header + FACCH uplink information + #endif + + // Traffic uplink data frames........(!!) + #if (DATA14_4 == 0) + API a_du_0[20]; // Header + DATA traffic uplink information, sub. chan. 0. + API a_du_1[20]; // Header + DATA traffic uplink information, sub. chan. 1. + #endif + #if (DATA14_4 == 1) + API a_du_0[22]; // Header + DATA traffic uplink information, sub. chan. 0. + API a_du_1[22]; // Header + DATA traffic uplink information, sub. chan. 1. + #endif + + // Random access.....................(MCU -> DSP). + API d_rach; // RACH information. + + //...................................(MCU -> DSP). + API d_a5mode; // Encryption Mode. + API a_kc[4]; // Encryption Key Code. + API d_tch_mode; // TCH mode register. + // bit [0..1] -> b_dai_mode. + // bit [2] -> b_dtx. + + // OMEGA...........................(MCU -> DSP). + +#if ((ANLG_FAM == 1) || (ANLG_FAM == 2)) + API a_ramp[16]; + #if (MELODY_E1) + API d_melo_osc_used; + API d_melo_osc_active; + API a_melo_note0[4]; + API a_melo_note1[4]; + API a_melo_note2[4]; + API a_melo_note3[4]; + API a_melo_note4[4]; + API a_melo_note5[4]; + API a_melo_note6[4]; + API a_melo_note7[4]; + #if (DSP == 17) + // selection of the melody format + API d_dco_type; // Tide + API p_start_IQ; + API d_level_off; + API d_dco_dbg; + API d_tide_resa; + API d_asynch_margin; // Perseus Asynch Audio Workaround + API hole[4]; + #else + API d_melody_selection; + API holes[9]; + #endif + #else // NO MELODY E1 + // selection of the melody format + #if (DSP == 17) + API holes[34]; // 34 unused holes. + API d_dco_type; // Tide + API p_start_IQ; + API d_level_off; + API d_dco_dbg; + API d_tide_resa; + API d_asynch_margin; // Perseus Asynch Audio Workaround + API hole[4] + #else + // selection of the melody format + API d_melody_selection; + API holes[43]; // 43 unused holes. + #endif + #endif + API d_debug3; + API d_debug2; + API d_debug1; // bit 0 at 1 enable dsp f_tx delay for Omega + API d_afcctladd; + API d_vbuctrl; + API d_vbdctrl; + API d_apcdel1; + API d_aec_ctrl; + API d_apcoff; + API d_bulioff; + API d_bulqoff; + API d_dai_onoff; + API d_auxdac; + #if (ANLG_FAM == 1) + API d_vbctrl; + #elif (ANLG_FAM == 2) + API d_vbctrl1; + #endif + API d_bbctrl; + + #else + #error DSPCODE not supported with given ANALOG + #endif //(ANALOG)1, 2 + //...................................(MCU -> DSP). + API a_sch26[5]; // Header + SB information, array of 5 words. + + // TONES.............................(MCU -> DSP) + API d_toneskb_init; + API d_toneskb_status; + API d_k_x1_t0; + API d_k_x1_t1; + API d_k_x1_t2; + API d_pe_rep; + API d_pe_off; + API d_se_off; + API d_bu_off; + API d_t0_on; + API d_t0_off; + API d_t1_on; + API d_t1_off; + API d_t2_on; + API d_t2_off; + API d_k_x1_kt0; + API d_k_x1_kt1; + API d_dur_kb; + + // PLL...............................(MCU -> DSP). + API d_pll_clkmod1; + API d_pll_clkmod2; + + // DSP status returned..........(DSP --> MCU). + API d_error_status; + + // RIF control.......................(MCU -> DSP). + API d_spcx_rif; + + API d_shiftdl; + API d_shiftul; + + #if (AEC == 1) + // AEC control.......................(MCU -> DSP). + #if (VOC == FR_EFR) + API p_aec_init; + API p_aec_prog; + API p_spenh_init; + API p_spenh_prog; + #endif + + #if (VOC == FR_HR_EFR) + API p_saec_prog; + API p_aec_prog; + API p_spenh_prog; + #endif + #endif + + API a_ovly[75]; + API d_ra_conf; + API d_ra_act; + API d_ra_test; + API d_ra_statu; + API d_ra_statd; + API d_fax; + #if (SPEECH_RECO) + API a_data_buf_ul[3]; + API a_n_best_words[4]; // array of the 4 best words + API a_n_best_score[8]; // array of the 4 best scores (each score is 32 bits length) + API sr_holes_2[6]; + API a_data_buf_dl[37]; + + API fir_holes1[409]; + API a_fir31_uplink[31]; + API a_fir31_downlink[31]; + API d_audio_init; + API d_audio_status; + API a_model[1041]; // array of the speech reco model + #else + API a_data_buf_ul[21]; + API a_data_buf_dl[37]; + + API fir_holes1[409]; + API a_fir31_uplink[31]; + API a_fir31_downlink[31]; + API d_audio_init; + API d_audio_status; + #endif +} +T_NDB_MCU_DSP; +#endif + +#if (DSP == 34) || (DSP == 35) || (DSP == 36) +typedef struct +{ + API_SIGNED d_transfer_rate; + + // Common GSM/GPRS + // These words specified the latencies to applies on some peripherics + API_SIGNED d_lat_mcu_bridge; + API_SIGNED d_lat_mcu_hom2sam; + API_SIGNED d_lat_mcu_bef_fast_access; + API_SIGNED d_lat_dsp_after_sam; + + // DSP Start address + API_SIGNED d_gprs_install_address; + + API_SIGNED d_misc_config; + + API_SIGNED d_cn_sw_workaround; + + API_SIGNED d_hole2_param[4]; + + //...................................Frequency Burst. + API_SIGNED d_fb_margin_beg; + API_SIGNED d_fb_margin_end; + API_SIGNED d_nsubb_idle; + API_SIGNED d_nsubb_dedic; + API_SIGNED d_fb_thr_det_iacq; + API_SIGNED d_fb_thr_det_track; + //...................................Demodulation. + API_SIGNED d_dc_off_thres; + API_SIGNED d_dummy_thres; + API_SIGNED d_dem_pond_gewl; + API_SIGNED d_dem_pond_red; + + //...................................TCH Full Speech. + API_SIGNED d_maccthresh1; + API_SIGNED d_mldt; + API_SIGNED d_maccthresh; + API_SIGNED d_gu; + API_SIGNED d_go; + API_SIGNED d_attmax; + API_SIGNED d_sm; + API_SIGNED d_b; + + // V42Bis module + API_SIGNED d_v42b_switch_hyst; + API_SIGNED d_v42b_switch_min; + API_SIGNED d_v42b_switch_max; + API_SIGNED d_v42b_reset_delay; + + //...................................TCH Half Speech. + API_SIGNED d_ldT_hr; + API_SIGNED d_maccthresh_hr; + API_SIGNED d_maccthresh1_hr; + API_SIGNED d_gu_hr; + API_SIGNED d_go_hr; + API_SIGNED d_b_hr; + API_SIGNED d_sm_hr; + API_SIGNED d_attmax_hr; + + //...................................TCH Enhanced FR Speech. + API_SIGNED c_mldt_efr; + API_SIGNED c_maccthresh_efr; + API_SIGNED c_maccthresh1_efr; + API_SIGNED c_gu_efr; + API_SIGNED c_go_efr; + API_SIGNED c_b_efr; + API_SIGNED c_sm_efr; + API_SIGNED c_attmax_efr; + + //...................................CHED + API_SIGNED d_sd_min_thr_tchfs; + API_SIGNED d_ma_min_thr_tchfs; + API_SIGNED d_md_max_thr_tchfs; + API_SIGNED d_md1_max_thr_tchfs; + + API_SIGNED d_sd_min_thr_tchhs; + API_SIGNED d_ma_min_thr_tchhs; + API_SIGNED d_sd_av_thr_tchhs; + API_SIGNED d_md_max_thr_tchhs; + API_SIGNED d_md1_max_thr_tchhs; + + API_SIGNED d_sd_min_thr_tchefs; + API_SIGNED d_ma_min_thr_tchefs; + API_SIGNED d_md_max_thr_tchefs; + API_SIGNED d_md1_max_thr_tchefs; + + API_SIGNED d_wed_fil_ini; + API_SIGNED d_wed_fil_tc; + API_SIGNED d_x_min; + API_SIGNED d_x_max; + API_SIGNED d_slope; + API_SIGNED d_y_min; + API_SIGNED d_y_max; + API_SIGNED d_wed_diff_threshold; + API_SIGNED d_mabfi_min_thr_tchhs; + + // FACCH module + API_SIGNED d_facch_thr; + + // IDS module + API_SIGNED d_max_ovsp_ul; + API_SIGNED d_sync_thres; + API_SIGNED d_idle_thres; + API_SIGNED d_m1_thres; + API_SIGNED d_max_ovsp_dl; + API_SIGNED d_gsm_bgd_mgt; + + // FIR coefficients + API a_fir_holes[4]; + API a_fir31_uplink[31]; + API a_fir31_downlink[31]; +} +T_PARAM_MCU_DSP; +#elif (DSP == 33) +typedef struct +{ + API_SIGNED d_transfer_rate; + + // Common GSM/GPRS + // These words specified the latencies to applies on some peripherics + API_SIGNED d_lat_mcu_bridge; + API_SIGNED d_lat_mcu_hom2sam; + API_SIGNED d_lat_mcu_bef_fast_access; + API_SIGNED d_lat_dsp_after_sam; + + // DSP Start address + API_SIGNED d_gprs_install_address; + + API_SIGNED d_misc_config; + + API_SIGNED d_cn_sw_workaround; + + #if DCO_ALGO + API_SIGNED d_cn_dco_param; + + API_SIGNED d_hole2_param[3]; + #else + API_SIGNED d_hole2_param[4]; + #endif + + //...................................Frequency Burst. + API_SIGNED d_fb_margin_beg; + API_SIGNED d_fb_margin_end; + API_SIGNED d_nsubb_idle; + API_SIGNED d_nsubb_dedic; + API_SIGNED d_fb_thr_det_iacq; + API_SIGNED d_fb_thr_det_track; + //...................................Demodulation. + API_SIGNED d_dc_off_thres; + API_SIGNED d_dummy_thres; + API_SIGNED d_dem_pond_gewl; + API_SIGNED d_dem_pond_red; + + //...................................TCH Full Speech. + API_SIGNED d_maccthresh1; + API_SIGNED d_mldt; + API_SIGNED d_maccthresh; + API_SIGNED d_gu; + API_SIGNED d_go; + API_SIGNED d_attmax; + API_SIGNED d_sm; + API_SIGNED d_b; + + // V42Bis module + API_SIGNED d_v42b_switch_hyst; + API_SIGNED d_v42b_switch_min; + API_SIGNED d_v42b_switch_max; + API_SIGNED d_v42b_reset_delay; + + //...................................TCH Half Speech. + API_SIGNED d_ldT_hr; + API_SIGNED d_maccthresh_hr; + API_SIGNED d_maccthresh1_hr; + API_SIGNED d_gu_hr; + API_SIGNED d_go_hr; + API_SIGNED d_b_hr; + API_SIGNED d_sm_hr; + API_SIGNED d_attmax_hr; + + //...................................TCH Enhanced FR Speech. + API_SIGNED c_mldt_efr; + API_SIGNED c_maccthresh_efr; + API_SIGNED c_maccthresh1_efr; + API_SIGNED c_gu_efr; + API_SIGNED c_go_efr; + API_SIGNED c_b_efr; + API_SIGNED c_sm_efr; + API_SIGNED c_attmax_efr; + + //...................................CHED + API_SIGNED d_sd_min_thr_tchfs; + API_SIGNED d_ma_min_thr_tchfs; + API_SIGNED d_md_max_thr_tchfs; + API_SIGNED d_md1_max_thr_tchfs; + + API_SIGNED d_sd_min_thr_tchhs; + API_SIGNED d_ma_min_thr_tchhs; + API_SIGNED d_sd_av_thr_tchhs; + API_SIGNED d_md_max_thr_tchhs; + API_SIGNED d_md1_max_thr_tchhs; + + API_SIGNED d_sd_min_thr_tchefs; + API_SIGNED d_ma_min_thr_tchefs; + API_SIGNED d_md_max_thr_tchefs; + API_SIGNED d_md1_max_thr_tchefs; + + API_SIGNED d_wed_fil_ini; + API_SIGNED d_wed_fil_tc; + API_SIGNED d_x_min; + API_SIGNED d_x_max; + API_SIGNED d_slope; + API_SIGNED d_y_min; + API_SIGNED d_y_max; + API_SIGNED d_wed_diff_threshold; + API_SIGNED d_mabfi_min_thr_tchhs; + + // FACCH module + API_SIGNED d_facch_thr; + + // IDS module + API_SIGNED d_max_ovsp_ul; + API_SIGNED d_sync_thres; + API_SIGNED d_idle_thres; + API_SIGNED d_m1_thres; + API_SIGNED d_max_ovsp_dl; + API_SIGNED d_gsm_bgd_mgt; + + // FIR coefficients + API a_fir_holes[4]; + API a_fir31_uplink[31]; + API a_fir31_downlink[31]; +} +T_PARAM_MCU_DSP; + +#else + +typedef struct +{ + //...................................Frequency Burst. + API_SIGNED d_nsubb_idle; + API_SIGNED d_nsubb_dedic; + API_SIGNED d_fb_thr_det_iacq; + API_SIGNED d_fb_thr_det_track; + //...................................Demodulation. + API_SIGNED d_dc_off_thres; + API_SIGNED d_dummy_thres; + API_SIGNED d_dem_pond_gewl; + API_SIGNED d_dem_pond_red; + API_SIGNED hole[1]; + API_SIGNED d_transfer_rate; + //...................................TCH Full Speech. + API_SIGNED d_maccthresh1; + API_SIGNED d_mldt; + API_SIGNED d_maccthresh; + API_SIGNED d_gu; + API_SIGNED d_go; + API_SIGNED d_attmax; + API_SIGNED d_sm; + API_SIGNED d_b; + + #if (VOC == FR_HR) || (VOC == FR_HR_EFR) + //...................................TCH Half Speech. + API_SIGNED d_ldT_hr; + API_SIGNED d_maccthresh_hr; + API_SIGNED d_maccthresh1_hr; + API_SIGNED d_gu_hr; + API_SIGNED d_go_hr; + API_SIGNED d_b_hr; + API_SIGNED d_sm_hr; + API_SIGNED d_attmax_hr; + #endif + + #if (VOC == FR_EFR) || (VOC == FR_HR_EFR) + //...................................TCH Enhanced FR Speech. + API_SIGNED c_mldt_efr; + API_SIGNED c_maccthresh_efr; + API_SIGNED c_maccthresh1_efr; + API_SIGNED c_gu_efr; + API_SIGNED c_go_efr; + API_SIGNED c_b_efr; + API_SIGNED c_sm_efr; + API_SIGNED c_attmax_efr; + #endif + + //...................................TCH Full Speech. + API_SIGNED d_sd_min_thr_tchfs; + API_SIGNED d_ma_min_thr_tchfs; + API_SIGNED d_md_max_thr_tchfs; + API_SIGNED d_md1_max_thr_tchfs; + + #if (VOC == FR) || (VOC == FR_HR) || (VOC == FR_HR_EFR) + //...................................TCH Half Speech. + API_SIGNED d_sd_min_thr_tchhs; + API_SIGNED d_ma_min_thr_tchhs; + API_SIGNED d_sd_av_thr_tchhs; + API_SIGNED d_md_max_thr_tchhs; + API_SIGNED d_md1_max_thr_tchhs; + #endif + + #if (VOC == FR_EFR) || (VOC == FR_HR_EFR) + //...................................TCH Enhanced FR Speech. + API_SIGNED d_sd_min_thr_tchefs; //(24L *C_POND_RED) + API_SIGNED d_ma_min_thr_tchefs; //(1200L *C_POND_RED) + API_SIGNED d_md_max_thr_tchefs; //(2000L *C_POND_RED) + API_SIGNED d_md1_max_thr_tchefs; //(160L *C_POND_RED) + API_SIGNED d_hole1; + #endif + + API_SIGNED d_wed_fil_ini; + API_SIGNED d_wed_fil_tc; + API_SIGNED d_x_min; + API_SIGNED d_x_max; + API_SIGNED d_slope; + API_SIGNED d_y_min; + API_SIGNED d_y_max; + API_SIGNED d_wed_diff_threshold; + API_SIGNED d_mabfi_min_thr_tchhs; + API_SIGNED d_facch_thr; + API_SIGNED d_dsp_test; + + + #if (DATA14_4 == 0 ) || (VOC == FR_HR_EFR) + API_SIGNED d_patch_addr1; + API_SIGNED d_patch_data1; + API_SIGNED d_patch_addr2; + API_SIGNED d_patch_data2; + API_SIGNED d_patch_addr3; + API_SIGNED d_patch_data3; + API_SIGNED d_patch_addr4; + API_SIGNED d_patch_data4; + #endif + + //................................... + API_SIGNED d_version_number; // DSP patch version + API_SIGNED d_ti_version; // customer number. No more used since 1.5 + + API_SIGNED d_dsp_page; + + #if IDS + API_SIGNED d_max_ovsp_ul; + API_SIGNED d_sync_thres; + API_SIGNED d_idle_thres; + API_SIGNED d_m1_thres; + API_SIGNED d_max_ovsp_dl; + #endif + + +} +T_PARAM_MCU_DSP; +#endif + +#if (DSP_DEBUG_TRACE_ENABLE == 1) +typedef struct +{ + API d_debug_ptr_begin; + API d_debug_ptr_end; +} +T_DB2_DSP_TO_MCU; +#endif + +/* DSP error as per ndb->d_error_status */ +enum dsp_error { + DSP_ERR_RHEA = 0x0001, + DSP_ERR_IQ_SAMPLES = 0x0004, + DSP_ERR_DMA_PROG = 0x0008, + DSP_ERR_DMA_TASK = 0x0010, + DSP_ERR_DMA_PEND = 0x0020, + DSP_ERR_VM = 0x0080, + DSP_ERR_DMA_UL_TASK = 0x0100, + DSP_ERR_DMA_UL_PROG = 0x0200, + DSP_ERR_DMA_UL_PEND = 0x0400, + DSP_ERR_STACK_OV = 0x0800, +}; + +#endif /* _CAL_DSP_API_H */ diff --git a/src/target/firmware/include/calypso/du.h b/src/target/firmware/include/calypso/du.h new file mode 100644 index 00000000..f2eae091 --- /dev/null +++ b/src/target/firmware/include/calypso/du.h @@ -0,0 +1,32 @@ +/* Calypso DU (Debug Unit) Driver */ + +/* (C) 2010 by Ingo Albrecht + * + * 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. + * + */ + +#ifndef _CALYPSO_DU_H +#define _CALYPSO_DU_H + +#include + +void calypso_du_init(); +void calypso_du_stop(); +void calypsu_du_dump(); + +#endif /* _CALYPSO_DU_H */ diff --git a/src/target/firmware/include/calypso/irq.h b/src/target/firmware/include/calypso/irq.h new file mode 100644 index 00000000..5ea59797 --- /dev/null +++ b/src/target/firmware/include/calypso/irq.h @@ -0,0 +1,49 @@ +#ifndef _CALYPSO_IRQ_H +#define _CALYPSO_IRQ_H + +enum irq_nr { + IRQ_WATCHDOG = 0, + IRQ_TIMER1 = 1, + IRQ_TIMER2 = 2, + IRQ_TSP_RX = 3, + IRQ_TPU_FRAME = 4, + IRQ_TPU_PAGE = 5, + IRQ_SIMCARD = 6, + IRQ_UART_MODEM = 7, + IRQ_KEYPAD_GPIO = 8, + IRQ_RTC_TIMER = 9, + IRQ_RTC_ALARM_I2C = 10, + IRQ_ULPD_GAUGING = 11, + IRQ_EXTERNAL = 12, + IRQ_SPI = 13, + IRQ_DMA = 14, + IRQ_API = 15, + IRQ_SIM_DETECT = 16, + IRQ_EXTERNAL_FIQ = 17, + IRQ_UART_IRDA = 18, + IRQ_ULPD_GSM_TIMER = 19, + IRQ_GEA = 20, + _NR_IRQ +}; + +typedef void irq_handler(enum irq_nr nr); + +/* initialize IRQ driver and enable interrupts */ +void irq_init(void); + +/* enable a certain interrupt */ +void irq_enable(enum irq_nr nr); + +/* disable a certain interrupt */ +void irq_disable(enum irq_nr nr); + +/* configure a certain interrupt */ +void irq_config(enum irq_nr nr, int fiq, int edge, int8_t prio); + +/* register an interrupt handler */ +void irq_register_handler(enum irq_nr nr, irq_handler *handler); + +/* Install the exception handlers to where the ROM loader jumps */ +void calypso_exceptions_install(void); + +#endif /* _CALYPSO_IRQ_H */ diff --git a/src/target/firmware/include/calypso/l1_environment.h b/src/target/firmware/include/calypso/l1_environment.h new file mode 100644 index 00000000..2d1f8d97 --- /dev/null +++ b/src/target/firmware/include/calypso/l1_environment.h @@ -0,0 +1,365 @@ +#include + +typedef unsigned short API; +typedef signed short API_SIGNED; + +#define FAR + +#define CHIPSET 12 +#define DSP 36 +#define ANLG_FAM 2 /* Iota */ + +/* MFTAB */ +#define L1_MAX_FCT 5 /* Max number of fctions in a frame */ +#define MFTAB_SIZE 20 + +#define NBMAX_CARRIER 174+374 /* Number of carriers (GSM-Ext + DCS */ + +#define DPAGC_FIFO_LEN 4 + +#define SIZE_HIST 10 + +#if !L1_GPRS +# define NBR_DL_L1S_TASKS 32 +#else +# define NBR_DL_L1S_TASKS 45 +#endif + +#define NBR_L1A_PROCESSES 46 + +#define W_A_DSP_IDLE3 1 + + + +// Identifier for all DSP tasks. +// ...RX & TX tasks identifiers. +#define NO_DSP_TASK 0 // No task. +#define NP_DSP_TASK 21 // Normal Paging reading task. +#define EP_DSP_TASK 22 // Extended Paging reading task. +#define NBS_DSP_TASK 19 // Normal BCCH serving reading task. +#define EBS_DSP_TASK 20 // Extended BCCH serving reading task. +#define NBN_DSP_TASK 17 // Normal BCCH neighbour reading task. +#define EBN_DSP_TASK 18 // Extended BCCH neighbour reading task. +#define ALLC_DSP_TASK 24 // CCCH reading task while performing FULL BCCH/CCCH reading task. +#define CB_DSP_TASK 25 // CBCH reading task. +#define DDL_DSP_TASK 26 // SDCCH/D (data) reading task. +#define ADL_DSP_TASK 27 // SDCCH/A (SACCH) reading task. +#define DUL_DSP_TASK 12 // SDCCH/D (data) transmit task. +#define AUL_DSP_TASK 11 // SDCCH/A (SACCH) transmit task. +#define RACH_DSP_TASK 10 // RACH transmit task. +#define TCHT_DSP_TASK 13 // TCH Traffic data DSP task id (RX or TX) +#define TCHA_DSP_TASK 14 // TCH SACCH data DSP task id (RX or TX) +#define TCHD_DSP_TASK 28 // TCH Traffic data DSP task id (RX or TX) + +#define TCH_DTX_UL 15 // Replace UL task in DSP->MCU com. to say "burst not transmitted". + +#if (L1_GPRS) + // Identifier for DSP tasks Packet dedicated. + // ...RX & TX tasks identifiers. + //------------------------------------------------------------------------ + // WARNING ... Need to aligned following macro with MCU/DSP GPRS Interface + //------------------------------------------------------------------------ + #define PNP_DSP_TASK 30 + #define PEP_DSP_TASK 31 + #define PALLC_DSP_TASK 32 + #define PBS_DSP_TASK 33 + + #define PTCCH_DSP_TASK 33 + +#endif + +// Identifier for measurement, FB / SB search tasks. +// Values 1,2,3 reserved for "number of measurements". +#define FB_DSP_TASK 5 // Freq. Burst reading task in Idle mode. +#define SB_DSP_TASK 6 // Sync. Burst reading task in Idle mode. +#define TCH_FB_DSP_TASK 8 // Freq. Burst reading task in Dedicated mode. +#define TCH_SB_DSP_TASK 9 // Sync. Burst reading task in Dedicated mode. +#define IDLE1 1 + +// Debug tasks +#define CHECKSUM_DSP_TASK 33 +#define TST_NDB 35 // Checksum DSP->MCU +#define TST_DB 36 // DB communication check +#define INIT_VEGA 37 +#define DSP_LOOP_C 38 + +// Identifier for measurement, FB / SB search tasks. +// Values 1,2,3 reserved for "number of measurements". +#define TCH_LOOP_A 31 +#define TCH_LOOP_B 32 + +// bits in d_gsm_bgd_mgt - background task management +#define B_DSPBGD_RECO 1 // start of reco in dsp background +#define B_DSPBGD_UPD 2 // start of alignement update in dsp background +#define B_DSPBGD_STOP_RECO 256 // stop of reco in dsp background +#define B_DSPBGD_STOP_UPD 512 // stop of alignement update in dsp background + +// bit in d_pll_config +#define B_32KHZ_CALIB (1 << 14) // force DSP in Idle1 during 32 khz calibration +// **************************************************************** +// NDB AREA (PARAM) MCU<->DSP COMMUNICATION DEFINITIONS +// **************************************************************** +// bits in d_tch_mode +#define B_EOTD (1 << 0) // EOTD mode +#define B_PLAY_UL (1 << 3) // Play UL +#define B_DCO_ON (1 << 4) // DCO ON/OFF +#define B_AUDIO_ASYNC (1 << 1) // WCP reserved + +// **************************************************************** +// PARAMETER AREA (PARAM) MCU<->DSP COMMUNICATION DEFINITIONS +// **************************************************************** +#define C_POND_RED 1L +// below values are defined in the file l1_time.h +//#define D_NSUBB_IDLE 296L +//#define D_NSUBB_DEDIC 30L +#define D_FB_THR_DET_IACQ 0x3333L +#define D_FB_THR_DET_TRACK 0x28f6L +#define D_DC_OFF_THRES 0x7fffL +#define D_DUMMY_THRES 17408L +#define D_DEM_POND_GEWL 26624L +#define D_DEM_POND_RED 20152L +#define D_HOLE 0L +#define D_TRANSFER_RATE 0x6666L + +// Full Rate vocoder definitions. +#define D_MACCTHRESH1 7872L +#define D_MLDT -4L +#define D_MACCTHRESH 7872L +#define D_GU 5772L +#define D_GO 7872L +#define D_ATTMAX 53L +#define D_SM -892L +#define D_B 208L +#define D_SD_MIN_THR_TCHFS 15L //(24L *C_POND_RED) +#define D_MA_MIN_THR_TCHFS 738L //(1200L *C_POND_RED) +#define D_MD_MAX_THR_TCHFS 1700L //(2000L *C_POND_RED) +#define D_MD1_MAX_THR_TCHFS 99L //(160L *C_POND_RED) + +#if (DSP == 33) || (DSP == 34) || (DSP == 35) || (DSP == 36) + // Frequency burst definitions + #define D_FB_MARGIN_BEG 24 + #define D_FB_MARGIN_END 22 + + // V42bis definitions + #define D_V42B_SWITCH_HYST 16L + #define D_V42B_SWITCH_MIN 64L + #define D_V42B_SWITCH_MAX 250L + #define D_V42B_RESET_DELAY 10L + + // Latencies definitions + #if (DSP == 33) || (DSP == 34) || (DSP == 35) || (DSP == 36) + // C.f. BUG1404 + #define D_LAT_MCU_BRIDGE 0x000FL + #else + #define D_LAT_MCU_BRIDGE 0x0009L + #endif + + #define D_LAT_MCU_HOM2SAM 0x000CL + + #define D_LAT_MCU_BEF_FAST_ACCESS 0x0005L + #define D_LAT_DSP_AFTER_SAM 0x0004L + + // Background Task in GSM mode: Initialization. + #define D_GSM_BGD_MGT 0L + +#if (CHIPSET == 4) + #define D_MISC_CONFIG 0L +#elif (CHIPSET == 7) || (CHIPSET == 8) || (CHIPSET == 10) || (CHIPSET == 11) || (CHIPSET == 12) + #define D_MISC_CONFIG 1L +#else + #define D_MISC_CONFIG 0L +#endif + +#endif + +// Hall Rate vocoder and ched definitions. + +#define D_SD_MIN_THR_TCHHS 37L +#define D_MA_MIN_THR_TCHHS 344L +#define D_MD_MAX_THR_TCHHS 2175L +#define D_MD1_MAX_THR_TCHHS 138L +#define D_SD_AV_THR_TCHHS 1845L +#define D_WED_FIL_TC 0x7c00L +#define D_WED_FIL_INI 4650L +#define D_X_MIN 15L +#define D_X_MAX 23L +#define D_Y_MIN 703L +#define D_Y_MAX 2460L +#define D_SLOPE 135L +#define D_WED_DIFF_THRESHOLD 406L +#define D_MABFI_MIN_THR_TCHHS 5320L +#define D_LDT_HR -5 +#define D_MACCTRESH_HR 6500 +#define D_MACCTRESH1_HR 6500 +#define D_GU_HR 2620 +#define D_GO_HR 3700 +#define D_B_HR 182 +#define D_SM_HR -1608 +#define D_ATTMAX_HR 53 + +// Enhanced Full Rate vocoder and ched definitions. + +#define C_MLDT_EFR -4 +#define C_MACCTHRESH_EFR 8000 +#define C_MACCTHRESH1_EFR 8000 +#define C_GU_EFR 4522 +#define C_GO_EFR 6500 +#define C_B_EFR 174 +#define C_SM_EFR -878 +#define C_ATTMAX_EFR 53 +#define D_SD_MIN_THR_TCHEFS 15L //(24L *C_POND_RED) +#define D_MA_MIN_THR_TCHEFS 738L //(1200L *C_POND_RED) +#define D_MD_MAX_THR_TCHEFS 1230L //(2000L *C_POND_RED) +#define D_MD1_MAX_THR_TCHEFS 99L //(160L *C_POND_RED) + + +// Integrated Data Services definitions. +#define D_MAX_OVSPD_UL 8 +// Detect frames containing 90% of 1s as synchro frames +#define D_SYNC_THRES 0x3f50 +// IDLE frames are only frames with 100 % of 1s +#define D_IDLE_THRES 0x4000 +#define D_M1_THRES 5 +#define D_MAX_OVSP_DL 8 + +// d_ra_act: bit field definition +#define B_F48BLK 5 + +// Mask for b_itc information (d_ra_conf) +#define CE_MASK 0x04 + +#define D_FACCH_THR 0 +#define D_DSP_TEST 0 +#define D_VERSION_NUMBER 0 +#define D_TI_VERSION 0 + + +/*------------------------------------------------------------------------------*/ +/* */ +/* DEFINITIONS FOR DSP <-> MCU COMMUNICATION. */ +/* ++++++++++++++++++++++++++++++++++++++++++ */ +/* */ +/*------------------------------------------------------------------------------*/ +// COMMUNICATION Interrupt definition +//------------------------------------ +#define ALL_16BIT 0xffffL +#define B_GSM_PAGE (1 << 0) +#define B_GSM_TASK (1 << 1) +#define B_MISC_PAGE (1 << 2) +#define B_MISC_TASK (1 << 3) + +#define B_GSM_PAGE_MASK (ALL_16BIT ^ B_GSM_PAGE) +#define B_GSM_TASK_MASK (ALL_16BIT ^ B_GSM_TASK) +#define B_MISC_PAGE_MASK (ALL_16BIT ^ B_MISC_PAGE) +#define B_MISC_TASK_MASK (ALL_16BIT ^ B_MISC_TASK) + +// Common definition +//---------------------------------- +// Index to *_DEMOD* arrays. +#define D_TOA 0 // Time Of Arrival. +#define D_PM 1 // Power Measurement. +#define D_ANGLE 2 // Angle (AFC correction) +#define D_SNR 3 // Signal / Noise Ratio. + +// Bit name/position definitions. +#define B_FIRE0 5 // Fire result bit 0. (00 -> NO ERROR) (01 -> ERROR CORRECTED) +#define B_FIRE1 6 // Fire result bit 1. (10 -> ERROR) (11 -> unused) +#define B_SCH_CRC 8 // CRC result for SB decoding. (1 for ERROR). +#define B_BLUD 15 // Uplink,Downlink data block Present. (1 for PRESENT). +#define B_AF 14 // Activity bit: 1 if data block is valid. +#define B_BFI 2 // Bad Frame Indicator +#define B_UFI 0 // UNRELIABLE FRAME Indicator +#define B_ECRC 9 // Enhanced full rate CRC bit +#define B_EMPTY_BLOCK 10 // for voice memo purpose, this bit is used to determine + +#if (DEBUG_DEDIC_TCH_BLOCK_STAT == 1) + #define FACCH_GOOD 10 + #define FACCH_BAD 11 +#endif + +#if (AMR == 1) + // Place of the RX type in the AMR block header + #define RX_TYPE_SHIFT 3 + #define RX_TYPE_MASK 0x0038 + + // Place of the vocoder type in the AMR block header + #define VOCODER_TYPE_SHIFT 0 + #define VOCODER_TYPE_MASK 0x0007 + + // List of the possible RX types in a_dd block + #define SPEECH_GOOD 0 + #define SPEECH_DEGRADED 1 + #define ONSET 2 + #define SPEECH_BAD 3 + #define SID_FIRST 4 + #define SID_UPDATE 5 + #define SID_BAD 6 + #define AMR_NO_DATA 7 + #define AMR_INHIBIT 8 + + // List of possible RX types in RATSCCH block + #define C_RATSCCH_GOOD 5 + + // List of the possible AMR channel rate + #define AMR_CHANNEL_4_75 0 + #define AMR_CHANNEL_5_15 1 + #define AMR_CHANNEL_5_9 2 + #define AMR_CHANNEL_6_7 3 + #define AMR_CHANNEL_7_4 4 + #define AMR_CHANNEL_7_95 5 + #define AMR_CHANNEL_10_2 6 + #define AMR_CHANNEL_12_2 7 + + // Types of RATSCCH blocks + #define C_RATSCCH_UNKNOWN 0 + #define C_RATSCCH_CMI_PHASE_REQ 1 + #define C_RATSCCH_AMR_CONFIG_REQ_MAIN 2 + #define C_RATSCCH_AMR_CONFIG_REQ_ALT 3 + #define C_RATSCCH_AMR_CONFIG_REQ_ALT_IGNORE 4 // Alternative AMR_CONFIG_REQ with updates coming in the next THRES_REQ block + #define C_RATSCCH_THRES_REQ 5 + + // These flags define a bitmap that indicates which AMR parameters are being modified by a RATSCCH + #define C_AMR_CHANGE_CMIP 0 + #define C_AMR_CHANGE_ACS 1 + #define C_AMR_CHANGE_ICM 2 + #define C_AMR_CHANGE_THR1 3 + #define C_AMR_CHANGE_THR2 4 + #define C_AMR_CHANGE_THR3 5 + #define C_AMR_CHANGE_HYST1 6 + #define C_AMR_CHANGE_HYST2 7 + #define C_AMR_CHANGE_HYST3 8 + + // CMIP default value + #define C_AMR_CMIP_DEFAULT 1 // According to ETSI specification 05.09, cmip is always 1 by default (new channel, handover...) + +#endif +// "d_ctrl_tch" bits positions for TCH configuration. +#define B_CHAN_MODE 0 +#define B_CHAN_TYPE 4 +#define B_RESET_SACCH 6 +#define B_VOCODER_ON 7 +#define B_SYNC_TCH_UL 8 +#if (AMR == 1) + #define B_SYNC_AMR 9 +#else +#define B_SYNC_TCH_DL 9 +#endif +#define B_STOP_TCH_UL 10 +#define B_STOP_TCH_DL 11 +#define B_TCH_LOOP 12 +#define B_SUBCHANNEL 15 + +// "d_ctrl_abb" bits positions for conditionnal loading of abb registers. +#define B_RAMP 0 +#if ((ANLG_FAM == 1) || (ANLG_FAM == 2) || (ANLG_FAM == 3)) + #define B_BULRAMPDEL 3 // Note: this name is changed + #define B_BULRAMPDEL2 2 // Note: this name is changed + #define B_BULRAMPDEL_BIS 9 + #define B_BULRAMPDEL2_BIS 10 +#endif +#define B_AFC 4 + +// "d_ctrl_system" bits positions. +#define B_TSQ 0 +#define B_BCCH_FREQ_IND 3 +#define B_TASK_ABORT 15 // Abort RF tasks for DSP. diff --git a/src/target/firmware/include/calypso/misc.h b/src/target/firmware/include/calypso/misc.h new file mode 100644 index 00000000..3bd81d22 --- /dev/null +++ b/src/target/firmware/include/calypso/misc.h @@ -0,0 +1,7 @@ +#ifndef _CAL_MISC_H + +void memdump_range(unsigned int *ptr, unsigned int len); +void dump_mem(void); +void dump_dev_id(void); + +#endif /* _CAL_MISC_H */ diff --git a/src/target/firmware/include/calypso/rtc.h b/src/target/firmware/include/calypso/rtc.h new file mode 100644 index 00000000..17528d00 --- /dev/null +++ b/src/target/firmware/include/calypso/rtc.h @@ -0,0 +1,6 @@ +#ifndef _CALYPSO_RTC_H +#define _CALYPSO_RTC_H + +void rtc_init(void); + +#endif /* _CALYPSO_RTC_H */ diff --git a/src/target/firmware/include/calypso/timer.h b/src/target/firmware/include/calypso/timer.h new file mode 100644 index 00000000..96587d5a --- /dev/null +++ b/src/target/firmware/include/calypso/timer.h @@ -0,0 +1,22 @@ +#ifndef _CAL_TIMER_H +#define _CAL_TIMER_H + +/* Enable or Disable a timer */ +void hwtimer_enable(int num, int on); + +/* Configure pre-scaler and if timer is auto-reload */ +void hwtimer_config(int num, uint8_t pre_scale, int auto_reload); + +/* Load a timer with the given value */ +void hwtimer_load(int num, uint16_t val); + +/* Read the current timer value */ +uint16_t hwtimer_read(int num); + +/* Enable or disable the watchdog */ +void wdog_enable(int on); + +/* power up the timers */ +void hwtimer_init(void); + +#endif /* _CAL_TIMER_H */ diff --git a/src/target/firmware/include/calypso/tpu.h b/src/target/firmware/include/calypso/tpu.h new file mode 100644 index 00000000..f0226e63 --- /dev/null +++ b/src/target/firmware/include/calypso/tpu.h @@ -0,0 +1,117 @@ +#ifndef _CALYPSO_TPU_H +#define _CALYPSO_TPU_H + +/* Assert or de-assert TPU reset */ +void tpu_reset(int active); +/* Enable or Disable a new scenario loaded into the TPU */ +void tpu_enable(int active); +/* Enable or Disable the clock of teh TPU Module */ +void tpu_clk_enable(int active); +/* Enable Frame Interrupt generation on next frame. DSP will reset it */ +void tpu_dsp_frameirq_enable(void); +/* Is a Frame interrupt still pending for the DSP ? */ +int tpu_dsp_fameirq_pending(void); +/* Rewind the TPU, i.e. restart enqueueing instructions at the base addr */ +void tpu_rewind(void); +/* Enqueue a raw TPU instruction */ +void tpu_enqueue(uint16_t instr); +/* Initialize TPU and TPU driver */ +void tpu_init(void); +/* (Busy)Wait until TPU is idle */ +void tpu_wait_idle(void); +/* Enable FRAME interrupt generation */ +void tpu_frame_irq_en(int mcu, int dsp); +/* Force the generation of a DSP interrupt */ +void tpu_force_dsp_frame_irq(void); + +/* Get the current TPU SYNCHRO register */ +uint16_t tpu_get_synchro(void); +/* Get the current TPU OFFSET register */ +uint16_t tpu_get_offset(void); + +enum tpu_instr { + TPU_INSTR_AT = (1 << 13), + TPU_INSTR_OFFSET = (2 << 13), + TPU_INSTR_SYNCHRO = (3 << 13), /* Loading delta synchro value in TPU synchro register */ + TPU_INSTR_WAIT = (5 << 13), /* Wait a certain period (in GSM qbits) */ + TPU_INSTR_SLEEP = (0 << 13), /* Stop the sequencer by disabling TPU ENABLE bit in ctrl reg */ + /* data processing */ + TPU_INSTR_MOVE = (4 << 13), +}; + +/* Addresses internal to the TPU, only accessible via MOVE */ +enum tpu_reg_int { + TPUI_TSP_CTRL1 = 0x00, + TPUI_TSP_CTRL2 = 0x01, + TPUI_TX_1 = 0x04, + TPUI_TX_2 = 0x03, + TPUI_TX_3 = 0x03, + TPUI_TX_4 = 0x05, + TPUI_TSP_ACT_L = 0x06, + TPUI_TSP_ACT_U = 0x07, + TPUI_TSP_SET1 = 0x09, + TPUI_TSP_SET2 = 0x0a, + TPUI_TSP_SET3 = 0x0b, + TPUI_DSP_INT_PG = 0x10, + TPUI_GAUGING_EN = 0x11, +}; + +enum tpui_ctrl2_bits { + TPUI_CTRL2_RD = (1 << 0), + TPUI_CTRL2_WR = (1 << 1), +}; + +static inline uint16_t tpu_mod5000(int16_t time) +{ + if (time < 0) + return time + 5000; + if (time >= 5000) + return time - 5000; + return time; +} + +/* Enqueue a SLEEP operation (stop sequencer by disabling TPU ENABLE bit) */ +static inline void tpu_enq_sleep(void) +{ + tpu_enqueue(TPU_INSTR_SLEEP); +} + +/* Enqueue a MOVE operation */ +static inline void tpu_enq_move(uint8_t addr, uint8_t data) +{ + tpu_enqueue(TPU_INSTR_MOVE | (data << 5) | (addr & 0x1f)); +} + +/* Enqueue an AT operation */ +static inline void tpu_enq_at(int16_t time) +{ + tpu_enqueue(TPU_INSTR_AT | tpu_mod5000(time)); +} + +/* Enqueue a SYNC operation */ +static inline void tpu_enq_sync(int16_t time) +{ + tpu_enqueue(TPU_INSTR_SYNCHRO | time); +} + +/* Enqueue a WAIT operation */ +static inline void tpu_enq_wait(int16_t time) +{ + tpu_enqueue(TPU_INSTR_WAIT | time); +} + +/* Enqueue an OFFSET operation */ +static inline void tpu_enq_offset(int16_t time) +{ + tpu_enqueue(TPU_INSTR_OFFSET | time); +} + +static inline void tpu_enq_dsp_irq(void) +{ + tpu_enq_move(TPUI_DSP_INT_PG, 0x0001); +} + +/* add two numbers, modulo 5000, and ensure the result is positive */ +uint16_t add_mod5000(uint16_t a, uint16_t b); + +#endif /* _CALYPSO_TPU_H */ diff --git a/src/target/firmware/include/calypso/tsp.h b/src/target/firmware/include/calypso/tsp.h new file mode 100644 index 00000000..0252f36e --- /dev/null +++ b/src/target/firmware/include/calypso/tsp.h @@ -0,0 +1,30 @@ +#ifndef _CALYPSO_TSP_H +#define _CALYPSO_TSP_H + +#define TSPACT(x) (1 << x) + +/* initiate a TSP write through the TPU */ +void tsp_write(uint8_t dev_idx, uint8_t bitlen, uint32_t dout); + +/* 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); + +/* Obtain the current tspact state */ +uint16_t tsp_act_state(void); + +/* Update the TSPACT state, including enable and disable */ +void tsp_act_update(uint16_t new_act); + +/* Enable one or multiple TSPACT signals */ +void tsp_act_enable(uint16_t bitmask); + +/* Disable one or multiple TSPACT signals */ +void tsp_act_disable(uint16_t bitmask); + +/* Toggle one or multiple TSPACT signals */ +void tsp_act_toggle(uint16_t bitmask); + +/* Initialize TSP driver */ +void tsp_init(void); + +#endif /* _CALYPSO_TSP_H */ diff --git a/src/target/firmware/include/calypso/uart.h b/src/target/firmware/include/calypso/uart.h new file mode 100644 index 00000000..845612f0 --- /dev/null +++ b/src/target/firmware/include/calypso/uart.h @@ -0,0 +1,30 @@ +#ifndef _CAL_UART_H +#define _CAL_UART_H + +#include + +enum uart_baudrate { + UART_38400, + UART_57600, + UART_115200, + UART_230400, + UART_460800, + UART_614400, + UART_921600, +}; + +void uart_init(uint8_t uart); +void uart_putchar_wait(uint8_t uart, int c); +int uart_putchar_nb(uint8_t uart, int c); +int uart_getchar_nb(uint8_t uart, uint8_t *ch); +int uart_tx_busy(uint8_t uart); +int uart_baudrate(uint8_t uart, enum uart_baudrate bdrt); + +enum uart_irq { + UART_IRQ_TX_EMPTY, + UART_IRQ_RX_CHAR, +}; + +void uart_irq_enable(uint8_t uart, enum uart_irq irq, int on); + +#endif /* _CAL_UART_H */ diff --git a/src/target/firmware/include/cfi_flash.h b/src/target/firmware/include/cfi_flash.h new file mode 100644 index 00000000..2ab8842a --- /dev/null +++ b/src/target/firmware/include/cfi_flash.h @@ -0,0 +1,68 @@ + +#ifndef _CFI_FLASH_H +#define _CFI_FLASH_H + +#include + + +#define CFI_FLASH_MAX_ERASE_REGIONS 4 + +/* structure of erase region descriptor */ +struct cfi_region { + uint16_t b_count; + uint16_t b_size; +} __attribute__((packed)); + + +/* structure of cfi query response */ +struct cfi_query { + uint8_t qry[3]; + uint16_t p_id; + uint16_t p_adr; + uint16_t a_id; + uint16_t a_adr; + uint8_t vcc_min; + uint8_t vcc_max; + uint8_t vpp_min; + uint8_t vpp_max; + uint8_t word_write_timeout_typ; + uint8_t buf_write_timeout_typ; + uint8_t block_erase_timeout_typ; + uint8_t chip_erase_timeout_typ; + uint8_t word_write_timeout_max; + uint8_t buf_write_timeout_max; + uint8_t block_erase_timeout_max; + uint8_t chip_erase_timeout_max; + uint8_t dev_size; + uint16_t interface_desc; + uint16_t max_buf_write_size; + uint8_t num_erase_regions; + struct cfi_region erase_regions[CFI_FLASH_MAX_ERASE_REGIONS]; +} __attribute__((packed)); + +typedef struct { + void *f_base; + + uint32_t f_size; + + uint16_t f_manuf_id; + uint16_t f_dev_id; + + struct cfi_query f_query; +} cfi_flash_t; + +typedef uint8_t flash_lock; + +void flash_init(cfi_flash_t *flash, void *base_addr); + +void flash_dump_info(cfi_flash_t *flash); + +flash_lock flash_block_getlock(cfi_flash_t *base_addr, uint32_t block_offset); + +void flash_block_unlock(cfi_flash_t *base_addr, uint32_t block_offset); +void flash_block_lock(cfi_flash_t *base_addr, uint32_t block_offset); +void flash_block_lockdown(cfi_flash_t *base_addr, uint32_t block_offset); + +void flash_block_erase(cfi_flash_t *base_addr, uint32_t block_addr); + +#endif diff --git a/src/target/firmware/include/comm/msgb.h b/src/target/firmware/include/comm/msgb.h new file mode 100644 index 00000000..f7c9d147 --- /dev/null +++ b/src/target/firmware/include/comm/msgb.h @@ -0,0 +1,104 @@ +#ifndef _MSGB_H +#define _MSGB_H + +/* (C) 2008-2010 by Harald Welte + * 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 + +struct msgb { + struct llist_head list; + + /* the A-bis layer 2 header: OML, RSL(RLL), NS */ + unsigned char *l2h; + /* the layer 3 header. For OML: FOM; RSL: 04.08; GPRS: BSSGP */ + unsigned char *l3h; + + uint16_t data_len; + uint16_t len; + + unsigned char *head; /* start of buffer */ + unsigned char *tail; /* end of message */ + unsigned char *data; /* start of message */ + unsigned char _data[0]; +}; + +extern struct msgb *msgb_alloc(uint16_t size, const char *name); +extern void msgb_free(struct msgb *m); +extern void msgb_enqueue(struct llist_head *queue, struct msgb *msg); +extern struct msgb *msgb_dequeue(struct llist_head *queue); +extern void msgb_reset(struct msgb *m); + +#define msgb_l2(m) ((void *)(m->l2h)) +#define msgb_l3(m) ((void *)(m->l3h)) + +static inline unsigned int msgb_l2len(const struct msgb *msgb) +{ + return msgb->tail - (uint8_t *)msgb_l2(msgb); +} + +static inline unsigned int msgb_l3len(const struct msgb *msgb) +{ + return msgb->tail - (uint8_t *)msgb_l3(msgb); +} + +static inline unsigned int msgb_headlen(const struct msgb *msgb) +{ + return msgb->len - msgb->data_len; +} +static inline unsigned char *msgb_put(struct msgb *msgb, unsigned int len) +{ + unsigned char *tmp = msgb->tail; + msgb->tail += len; + msgb->len += len; + return tmp; +} +static inline unsigned char *msgb_push(struct msgb *msgb, unsigned int len) +{ + msgb->data -= len; + msgb->len += len; + return msgb->data; +} +static inline unsigned char *msgb_pull(struct msgb *msgb, unsigned int len) +{ + msgb->len -= len; + return msgb->data += len; +} +static inline int msgb_tailroom(const struct msgb *msgb) +{ + return (msgb->data + msgb->data_len) - msgb->tail; +} + +/* increase the headroom of an empty msgb, reducing the tailroom */ +static inline void msgb_reserve(struct msgb *msg, int len) +{ + msg->data += len; + msg->tail += len; +} + +static inline struct msgb *msgb_alloc_headroom(int size, int headroom, + const char *name) +{ + struct msgb *msg = msgb_alloc(size, name); + if (msg) + msgb_reserve(msg, headroom); + return msg; +} + +#endif /* _MSGB_H */ diff --git a/src/target/firmware/include/comm/sercomm.h b/src/target/firmware/include/comm/sercomm.h new file mode 100644 index 00000000..24ad865c --- /dev/null +++ b/src/target/firmware/include/comm/sercomm.h @@ -0,0 +1,57 @@ +#ifndef _SERCOMM_H +#define _SERCOMM_H + +/* SERCOMM layer on UART1 (modem UART) */ + +#ifdef HOST_BUILD +#include +#else +#include +#define SERCOMM_UART_NR 1 +#endif + +#define HDLC_FLAG 0x7E +#define HDLC_ESCAPE 0x7D + +#define HDLC_C_UI 0x03 +#define HDLC_C_P_BIT (1 << 4) +#define HDLC_C_F_BIT (1 << 4) + +/* a low sercomm_dlci means high priority. A high DLCI means low priority */ +enum sercomm_dlci { + SC_DLCI_HIGHEST = 0, + SC_DLCI_L1A_L23 = 5, + SC_DLCI_CONSOLE = 10, + _SC_DLCI_MAX +}; + +void sercomm_init(void); +int sercomm_initialized(void); + +/* User Interface: Tx */ + +/* user interface for transmitting messages for a given DLCI */ +void sercomm_sendmsg(uint8_t dlci, struct msgb *msg); +/* how deep is the Tx queue for a given DLCI */ +unsigned int sercomm_tx_queue_depth(uint8_t dlci); + +/* User Interface: Rx */ + +/* receiving messages for a given DLCI */ +typedef void (*dlci_cb_t)(uint8_t dlci, struct msgb *msg); +int sercomm_register_rx_cb(uint8_t dlci, dlci_cb_t cb); + +/* Driver Interface */ + +/* fetch one octet of to-be-transmitted serial data. returns 0 if no more data */ +int sercomm_drv_pull(uint8_t *ch); +/* the driver has received one byte, pass it into sercomm layer. + returns 1 in case of success, 0 in case of unrecognized char */ +int sercomm_drv_rx_char(uint8_t ch); + +static inline struct msgb *sercomm_alloc_msgb(unsigned int len) +{ + return msgb_alloc_headroom(len, 4, "sercomm_tx"); +} + +#endif /* _SERCOMM_H */ diff --git a/src/target/firmware/include/comm/sercomm_cons.h b/src/target/firmware/include/comm/sercomm_cons.h new file mode 100644 index 00000000..11f66545 --- /dev/null +++ b/src/target/firmware/include/comm/sercomm_cons.h @@ -0,0 +1,10 @@ +#ifndef _SERCOMM_CONS_H +#define _SERCOMM_CONS_H + +/* how large buffers do we allocate? */ +#define SERCOMM_CONS_ALLOC 256 + +int sercomm_puts(const char *s); +int sercomm_putchar(int c); + +#endif /* _SERCOMM_CONS_H */ diff --git a/src/target/firmware/include/console.h b/src/target/firmware/include/console.h new file mode 100644 index 00000000..7146e990 --- /dev/null +++ b/src/target/firmware/include/console.h @@ -0,0 +1,20 @@ +#ifndef _CONSOLE_H +#define _CONSOLE_H + +/* This is the direct (IRQ driven) UART console, bypassing the HDLC layer. + * You should not need to call those functions unless you've decided to + * not use the HLDC layer or have a device with two UARTs */ + +int cons_rb_append(const char *data, int len); +int cons_puts(const char *s); +int cons_putchar(char c); +int cons_rb_flush(void); +void cons_init(void); + +/* We want the console on UART 0 (IRDA UART) */ +#define CONS_UART_NR 0 + +/* Size of the static ring-buffer that we keep for console print messages */ +#define CONS_RB_SIZE 4096 + +#endif /* _CONSOLE_H */ diff --git a/src/target/firmware/include/debug.h b/src/target/firmware/include/debug.h new file mode 100644 index 00000000..27c4185d --- /dev/null +++ b/src/target/firmware/include/debug.h @@ -0,0 +1,31 @@ +#ifndef _DEBUG_H +#define _DEBUG_H + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +/* + * Check at compile time that something is of a particular type. + * Always evaluates to 1 so you may use it easily in comparisons. + */ +#define typecheck(type,x) \ +({ type __dummy; \ + typeof(x) __dummy2; \ + (void)(&__dummy == &__dummy2); \ + 1; \ +}) + +#ifdef DEBUG +#define dputchar(x) putchar(x) +#define dputs(x) puts(x) +#define dphex(x,y) phex(x,y) +#define printd(x, args ...) printf(x, ## args) +#else +#define dputchar(x) +#define dputs(x) +#define dphex(x,y) +#define printd(x, args ...) +#endif + +#endif /* _DEBUG_H */ diff --git a/src/target/firmware/include/delay.h b/src/target/firmware/include/delay.h new file mode 100644 index 00000000..0d6f3efd --- /dev/null +++ b/src/target/firmware/include/delay.h @@ -0,0 +1,7 @@ +#ifndef delay_h +#define delay_h + +void delay_ms(unsigned int ms); +void delay_us(unsigned int us); + +#endif diff --git a/src/target/firmware/include/display/st7558.h b/src/target/firmware/include/display/st7558.h new file mode 100644 index 00000000..efed064c --- /dev/null +++ b/src/target/firmware/include/display/st7558.h @@ -0,0 +1,15 @@ +#ifndef _ST7558_H +#define _ST7558_H + +enum display_attr { + DISP_ATTR_INVERT = 0x0001, +}; + +void st7558_init(void); +void st7558_set_attr(unsigned long attr); +void st7558_unset_attr(unsigned long attr); +void st7558_clrscr(void); +void st7558_putchar(unsigned char c); +void st7558_puts(const char *str); + +#endif diff --git a/src/target/firmware/include/gsm.h b/src/target/firmware/include/gsm.h new file mode 100644 index 00000000..f3250128 --- /dev/null +++ b/src/target/firmware/include/gsm.h @@ -0,0 +1,29 @@ +#ifndef _GSM_H +#define _GSM_H + +#include + +enum gsm_band { + GSM_850 = 1, + GSM_900 = 2, + GSM_1800 = 4, + GSM_1900 = 8, + GSM_450 = 0x10, + GSM_480 = 0x20, + GSM_750 = 0x40, + GSM_810 = 0x80, +}; + +#define ARFCN_PCS 0x8000 + +enum gsm_band gsm_arfcn2band(uint16_t arfcn); + +/* Convert an ARFCN to the frequency in MHz * 10 */ +uint16_t gsm_arfcn2freq10(uint16_t arfcn, int uplink); + +/* Convert from frame number to GSM time */ +void gsm_fn2gsmtime(struct gsm_time *time, uint32_t fn); + +/* Convert from GSM time to frame number */ +uint32_t gsm_gsmtime2fn(struct gsm_time *time); +#endif diff --git a/src/target/firmware/include/i2c.h b/src/target/firmware/include/i2c.h new file mode 100644 index 00000000..37097a85 --- /dev/null +++ b/src/target/firmware/include/i2c.h @@ -0,0 +1,7 @@ +#ifndef _I2C_H +#define _I2C_H + +int i2c_write(uint8_t chip, uint32_t addr, int alen, const uint8_t *buffer, int len); +void i2c_init(int speed, int slaveadd); + +#endif /* I2C_H */ diff --git a/src/target/firmware/include/keypad.h b/src/target/firmware/include/keypad.h new file mode 100644 index 00000000..dd89734c --- /dev/null +++ b/src/target/firmware/include/keypad.h @@ -0,0 +1,66 @@ +#ifndef _KEYPAD_H +#define _KEYPAD_H + +enum buttons { + BTN_0 = 0x00002000, + BTN_1 = 0x00008000, + BTN_2 = 0x00000400, + BTN_3 = 0x00000020, + BTN_4 = 0x00010000, + BTN_5 = 0x00000800, + BTN_6 = 0x00000040, + BTN_7 = 0x00020000, + BTN_8 = 0x00001000, + BTN_9 = 0x00000080, + BTN_STAR = 0x00040000, + BTN_HASH = 0x00000100, + BTN_MENU = 0x00004000, + BTN_LEFT_SB = 0x00080000, + BTN_RIGHT_SB = 0x00000200, + BTN_UP = 0x00000002, + BTN_DOWN = 0x00000004, + BTN_LEFT = 0x00000008, + BTN_RIGHT = 0x00000010, + BTN_OK = 0x00000001, + BTN_POWER = 0x01000000, +}; + +enum key_codes { + KEY_0 = 0, + KEY_1, + KEY_2, + KEY_3, + KEY_4, + KEY_5, + KEY_6, + KEY_7, + KEY_8, + KEY_9, + KEY_STAR, //* + KEY_HASH, //# + KEY_MENU, //center of directional keys + KEY_LEFT_SB, //softbutton + KEY_RIGHT_SB, //softbutton + KEY_UP, + KEY_DOWN, + KEY_LEFT, + KEY_RIGHT, + KEY_OK, //green off-hook + KEY_POWER, //red on-hook + KEY_INV = 0xFF +}; + +enum key_states { + PRESSED, + RELEASED, +}; + +void keypad_init(); + +void keypad_scan(); + +typedef void (*key_handler_t)(enum key_codes code, enum key_states state); + +void keypad_set_handler(key_handler_t handler); + +#endif /* KEYPAD_H */ diff --git a/src/target/firmware/include/layer1/afc.h b/src/target/firmware/include/layer1/afc.h new file mode 100644 index 00000000..2e927a50 --- /dev/null +++ b/src/target/firmware/include/layer1/afc.h @@ -0,0 +1,13 @@ +#ifndef _L1_AFC_H +#define _L1_AFC_H + +/* Input a frequency error sample into the AFC averaging */ +void afc_input(int32_t freq_error, uint16_t arfcn, int valid); + +/* Update the AFC with a frequency error, bypassing averaging */ +void afc_correct(int16_t freq_error, uint16_t arfcn); + +/* Update DSP with new AFC DAC value to be used for next TDMA frame */ +void afc_load_dsp(void); + +#endif diff --git a/src/target/firmware/include/layer1/agc.h b/src/target/firmware/include/layer1/agc.h new file mode 100644 index 00000000..e4b13f13 --- /dev/null +++ b/src/target/firmware/include/layer1/agc.h @@ -0,0 +1,6 @@ +#ifndef _L1_AGC_H +#define _L1_AGC_H + +int16_t agc_inp_dbm8_by_pm(int16_t pm); + +#endif /* _L1_AGC_H */ diff --git a/src/target/firmware/include/layer1/avg.h b/src/target/firmware/include/layer1/avg.h new file mode 100644 index 00000000..6c5de172 --- /dev/null +++ b/src/target/firmware/include/layer1/avg.h @@ -0,0 +1,23 @@ +#ifndef _L1_AVG_H +#define _L1_AVG_H + +struct running_avg { + /* configuration */ + uint16_t period; /* over how many samples to average */ + uint16_t min_valid; + + int32_t acc_val; + uint16_t num_samples; /* how often did we try to sample? */ + uint16_t num_samples_valid; /* how often did we receive valid samples? */ + + void (*outfn)(struct running_avg *, int32_t avg); + void *priv; +}; + +/* input a new sample into the averaging process */ +void runavg_input(struct running_avg *ravg, int32_t val, int valid); + +/* check if sufficient samples have been obtained, and call outfn() */ +int runavg_check_output(struct running_avg *ravg); + +#endif /* _AVG_H */ diff --git a/src/target/firmware/include/layer1/l23_api.h b/src/target/firmware/include/layer1/l23_api.h new file mode 100644 index 00000000..a03c59c8 --- /dev/null +++ b/src/target/firmware/include/layer1/l23_api.h @@ -0,0 +1,11 @@ +#ifndef _L1_L23_API_H +#define _L1_L23_API_H + +#include +#include +#include + +void l1_queue_for_l2(struct msgb *msg); +struct msgb *l1_create_l2_msg(int msg_type, uint32_t fn, uint16_t snr); + +#endif /* _L1_L23_API_H */ diff --git a/src/target/firmware/include/layer1/sync.h b/src/target/firmware/include/layer1/sync.h new file mode 100644 index 00000000..28eda42c --- /dev/null +++ b/src/target/firmware/include/layer1/sync.h @@ -0,0 +1,75 @@ +#ifndef _L1_SYNC_H +#define _L1_SYNC_H + +#include +#include + +struct l1_cell_info { + uint16_t arfcn; + uint32_t bsic; + uint32_t fn_offset; + uint32_t time_alignment; +}; + +struct l1s_state { + struct gsm_time current_time; /* current time */ + struct gsm_time next_time; /* time at next TMDMA irq */ + + struct l1_cell_info serving_cell; + + struct tdma_scheduler tdma_sched; + + uint32_t tpu_offset; + + int task; +}; + +extern struct l1s_state l1s; + +enum l1_sig_num { + L1_SIG_PM, /* Power Measurement */ + L1_SIG_NB, /* Normal Burst */ +}; + +struct l1s_meas_hdr { + uint16_t snr; /* signal/noise ratio */ + int16_t toa_qbit; /* time of arrival (qbits) */ + int16_t pm_dbm8; /* power level in dbm/8 */ + int16_t freq_err; /* Frequency error in Hz */ +}; + +struct l1_signal { + uint16_t signum; + uint16_t arfcn; + union { + struct { + int16_t dbm8[2]; + } pm; + struct { + struct l1s_meas_hdr meas[4]; + uint16_t crc; + uint16_t fire; + uint16_t num_biterr; + uint8_t frame[24]; + } nb; + }; +}; + +typedef void (*l1s_cb_t)(struct l1_signal *sig); + +void l1s_set_handler(l1s_cb_t handler); + +int16_t l1s_snr_int(uint16_t snr); +uint16_t l1s_snr_fract(uint16_t snr); + +void l1s_fb_test(uint8_t base_fn, uint8_t fb_mode); +void l1s_sb_test(uint8_t base_fn); +void l1s_pm_test(uint8_t base_fn, uint16_t arfcn); +void l1s_nb_test(uint8_t base_fn); + +void l1s_init(void); + +/* init.c */ +void layer1_init(void); + +#endif /* _L1_SYNC_H */ diff --git a/src/target/firmware/include/layer1/tdma_sched.h b/src/target/firmware/include/layer1/tdma_sched.h new file mode 100644 index 00000000..65c59b83 --- /dev/null +++ b/src/target/firmware/include/layer1/tdma_sched.h @@ -0,0 +1,52 @@ +#ifndef _L1_TDMA_SCHED_H +#define _L1_TDMA_SCHED_H + +#include + +/* TDMA scheduler */ + +/* The idea of this scheduler is that we have a circular buffer of buckets, + * where each bucket corresponds to one future TDMA frame [interrupt]. Each + * bucket contains of a list of callbacks which are executed when the bucket + * index reaches that particular bucket. */ + +#define TDMASCHED_NUM_FRAMES 25 +#define TDMASCHED_NUM_CB 5 + +typedef int tdma_sched_cb(uint16_t p1, uint16_t p2); + +/* A single item in a TDMA scheduler bucket */ +struct tdma_sched_item { + tdma_sched_cb *cb; + uint16_t p1; + uint16_t p2; +}; + +/* A bucket inside the TDMA scheduler */ +struct tdma_sched_bucket { + struct tdma_sched_item item[TDMASCHED_NUM_CB]; + uint8_t num_items; +}; + +/* The scheduler itself, consisting of buckets and a current index */ +struct tdma_scheduler { + struct tdma_sched_bucket bucket[TDMASCHED_NUM_FRAMES]; + uint8_t cur_bucket; +}; + +/* Schedule an item at 'frame_offset' TDMA frames in the future */ +int tdma_schedule(uint8_t frame_offset, tdma_sched_cb *cb, uint16_t p1, uint16_t p2); + +/* Schedule a set of items starting from 'frame_offset' TDMA frames in the future */ +int tdma_schedule_set(uint8_t frame_offset, const struct tdma_sched_item *item_set, uint8_t num_items); + +/* Execute pre-scheduled events for current frame */ +int tdma_sched_execute(void); + +/* reset the scheduler; erase all scheduled items */ +void tdma_sched_reset(void); + +/* debug function: print number of entries of all TDMA buckets */ +void tdma_sched_dump(void); + +#endif /* _L1_TDMA_SCHED_H */ diff --git a/src/target/firmware/include/layer1/tpu_window.h b/src/target/firmware/include/layer1/tpu_window.h new file mode 100644 index 00000000..01fab910 --- /dev/null +++ b/src/target/firmware/include/layer1/tpu_window.h @@ -0,0 +1,17 @@ +#ifndef _L1_TPU_CTRL_H +#define _L1_TPU_CTRL_H + +enum l1_rxwin_type { + L1_RXWIN_PW, /* power measurement */ + L1_RXWIN_FB, /* FCCH burst detection */ + L1_RXWIN_SB, /* SCH burst detection */ + L1_RXWIN_NB, /* Normal burst decoding */ + _NUM_L1_RXWIN +}; + + +void l1s_rx_win_ctrl(uint16_t arfcn, enum l1_rxwin_type wtype); + +void tpu_end_scenario(void); + +#endif /* _L1_TPU_CTRL_H */ diff --git a/src/target/firmware/include/linuxlist.h b/src/target/firmware/include/linuxlist.h new file mode 100644 index 00000000..fb99c5ec --- /dev/null +++ b/src/target/firmware/include/linuxlist.h @@ -0,0 +1,360 @@ +#ifndef _LINUX_LLIST_H +#define _LINUX_LLIST_H + +#include + +#ifndef inline +#define inline __inline__ +#endif + +static inline void prefetch(const void *x) {;} + +/** + * container_of - cast a member of a structure out to the containing structure + * + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (typeof( ((type *)0)->member ) *)(ptr); \ + (type *)( (char *)__mptr - offsetof(type, member) );}) + + +/* + * These are non-NULL pointers that will result in page faults + * under normal circumstances, used to verify that nobody uses + * non-initialized llist entries. + */ +#define LLIST_POISON1 ((void *) 0x00100100) +#define LLIST_POISON2 ((void *) 0x00200200) + +/* + * Simple doubly linked llist implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole llists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct llist_head { + struct llist_head *next, *prev; +}; + +#define LLIST_HEAD_INIT(name) { &(name), &(name) } + +#define LLIST_HEAD(name) \ + struct llist_head name = LLIST_HEAD_INIT(name) + +#define INIT_LLIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal llist manipulation where we know + * the prev/next entries already! + */ +static inline void __llist_add(struct llist_head *_new, + struct llist_head *prev, + struct llist_head *next) +{ + next->prev = _new; + _new->next = next; + _new->prev = prev; + prev->next = _new; +} + +/** + * llist_add - add a new entry + * @new: new entry to be added + * @head: llist head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void llist_add(struct llist_head *_new, struct llist_head *head) +{ + __llist_add(_new, head, head->next); +} + +/** + * llist_add_tail - add a new entry + * @new: new entry to be added + * @head: llist head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void llist_add_tail(struct llist_head *_new, struct llist_head *head) +{ + __llist_add(_new, head->prev, head); +} + +/* + * Delete a llist entry by making the prev/next entries + * point to each other. + * + * This is only for internal llist manipulation where we know + * the prev/next entries already! + */ +static inline void __llist_del(struct llist_head * prev, struct llist_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * llist_del - deletes entry from llist. + * @entry: the element to delete from the llist. + * Note: llist_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void llist_del(struct llist_head *entry) +{ + __llist_del(entry->prev, entry->next); + entry->next = (struct llist_head *)LLIST_POISON1; + entry->prev = (struct llist_head *)LLIST_POISON2; +} + +/** + * llist_del_init - deletes entry from llist and reinitialize it. + * @entry: the element to delete from the llist. + */ +static inline void llist_del_init(struct llist_head *entry) +{ + __llist_del(entry->prev, entry->next); + INIT_LLIST_HEAD(entry); +} + +/** + * llist_move - delete from one llist and add as another's head + * @llist: the entry to move + * @head: the head that will precede our entry + */ +static inline void llist_move(struct llist_head *llist, struct llist_head *head) +{ + __llist_del(llist->prev, llist->next); + llist_add(llist, head); +} + +/** + * llist_move_tail - delete from one llist and add as another's tail + * @llist: the entry to move + * @head: the head that will follow our entry + */ +static inline void llist_move_tail(struct llist_head *llist, + struct llist_head *head) +{ + __llist_del(llist->prev, llist->next); + llist_add_tail(llist, head); +} + +/** + * llist_empty - tests whether a llist is empty + * @head: the llist to test. + */ +static inline int llist_empty(const struct llist_head *head) +{ + return head->next == head; +} + +static inline void __llist_splice(struct llist_head *llist, + struct llist_head *head) +{ + struct llist_head *first = llist->next; + struct llist_head *last = llist->prev; + struct llist_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/** + * llist_splice - join two llists + * @llist: the new llist to add. + * @head: the place to add it in the first llist. + */ +static inline void llist_splice(struct llist_head *llist, struct llist_head *head) +{ + if (!llist_empty(llist)) + __llist_splice(llist, head); +} + +/** + * llist_splice_init - join two llists and reinitialise the emptied llist. + * @llist: the new llist to add. + * @head: the place to add it in the first llist. + * + * The llist at @llist is reinitialised + */ +static inline void llist_splice_init(struct llist_head *llist, + struct llist_head *head) +{ + if (!llist_empty(llist)) { + __llist_splice(llist, head); + INIT_LLIST_HEAD(llist); + } +} + +/** + * llist_entry - get the struct for this entry + * @ptr: the &struct llist_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the llist_struct within the struct. + */ +#define llist_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * llist_for_each - iterate over a llist + * @pos: the &struct llist_head to use as a loop counter. + * @head: the head for your llist. + */ +#define llist_for_each(pos, head) \ + for (pos = (head)->next, prefetch(pos->next); pos != (head); \ + pos = pos->next, prefetch(pos->next)) + +/** + * __llist_for_each - iterate over a llist + * @pos: the &struct llist_head to use as a loop counter. + * @head: the head for your llist. + * + * This variant differs from llist_for_each() in that it's the + * simplest possible llist iteration code, no prefetching is done. + * Use this for code that knows the llist to be very short (empty + * or 1 entry) most of the time. + */ +#define __llist_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * llist_for_each_prev - iterate over a llist backwards + * @pos: the &struct llist_head to use as a loop counter. + * @head: the head for your llist. + */ +#define llist_for_each_prev(pos, head) \ + for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \ + pos = pos->prev, prefetch(pos->prev)) + +/** + * llist_for_each_safe - iterate over a llist safe against removal of llist entry + * @pos: the &struct llist_head to use as a loop counter. + * @n: another &struct llist_head to use as temporary storage + * @head: the head for your llist. + */ +#define llist_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * llist_for_each_entry - iterate over llist of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your llist. + * @member: the name of the llist_struct within the struct. + */ +#define llist_for_each_entry(pos, head, member) \ + for (pos = llist_entry((head)->next, typeof(*pos), member), \ + prefetch(pos->member.next); \ + &pos->member != (head); \ + pos = llist_entry(pos->member.next, typeof(*pos), member), \ + prefetch(pos->member.next)) + +/** + * llist_for_each_entry_reverse - iterate backwards over llist of given type. + * @pos: the type * to use as a loop counter. + * @head: the head for your llist. + * @member: the name of the llist_struct within the struct. + */ +#define llist_for_each_entry_reverse(pos, head, member) \ + for (pos = llist_entry((head)->prev, typeof(*pos), member), \ + prefetch(pos->member.prev); \ + &pos->member != (head); \ + pos = llist_entry(pos->member.prev, typeof(*pos), member), \ + prefetch(pos->member.prev)) + +/** + * llist_for_each_entry_continue - iterate over llist of given type + * continuing after existing point + * @pos: the type * to use as a loop counter. + * @head: the head for your llist. + * @member: the name of the llist_struct within the struct. + */ +#define llist_for_each_entry_continue(pos, head, member) \ + for (pos = llist_entry(pos->member.next, typeof(*pos), member), \ + prefetch(pos->member.next); \ + &pos->member != (head); \ + pos = llist_entry(pos->member.next, typeof(*pos), member), \ + prefetch(pos->member.next)) + +/** + * llist_for_each_entry_safe - iterate over llist of given type safe against removal of llist entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your llist. + * @member: the name of the llist_struct within the struct. + */ +#define llist_for_each_entry_safe(pos, n, head, member) \ + for (pos = llist_entry((head)->next, typeof(*pos), member), \ + n = llist_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = llist_entry(n->member.next, typeof(*n), member)) + +/** + * llist_for_each_rcu - iterate over an rcu-protected llist + * @pos: the &struct llist_head to use as a loop counter. + * @head: the head for your llist. + */ +#define llist_for_each_rcu(pos, head) \ + for (pos = (head)->next, prefetch(pos->next); pos != (head); \ + pos = pos->next, ({ smp_read_barrier_depends(); 0;}), prefetch(pos->next)) + +#define __llist_for_each_rcu(pos, head) \ + for (pos = (head)->next; pos != (head); \ + pos = pos->next, ({ smp_read_barrier_depends(); 0;})) + +/** + * llist_for_each_safe_rcu - iterate over an rcu-protected llist safe + * against removal of llist entry + * @pos: the &struct llist_head to use as a loop counter. + * @n: another &struct llist_head to use as temporary storage + * @head: the head for your llist. + */ +#define llist_for_each_safe_rcu(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next) + +/** + * llist_for_each_entry_rcu - iterate over rcu llist of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your llist. + * @member: the name of the llist_struct within the struct. + */ +#define llist_for_each_entry_rcu(pos, head, member) \ + for (pos = llist_entry((head)->next, typeof(*pos), member), \ + prefetch(pos->member.next); \ + &pos->member != (head); \ + pos = llist_entry(pos->member.next, typeof(*pos), member), \ + ({ smp_read_barrier_depends(); 0;}), \ + prefetch(pos->member.next)) + + +/** + * llist_for_each_continue_rcu - iterate over an rcu-protected llist + * continuing after existing point. + * @pos: the &struct llist_head to use as a loop counter. + * @head: the head for your llist. + */ +#define llist_for_each_continue_rcu(pos, head) \ + for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \ + (pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next)) + + +#endif diff --git a/src/target/firmware/include/memory.h b/src/target/firmware/include/memory.h new file mode 100644 index 00000000..b0a0490c --- /dev/null +++ b/src/target/firmware/include/memory.h @@ -0,0 +1,28 @@ +#ifndef _MEMORY_H +#define _MEMORY_H + +#define __arch_getb(a) (*(volatile unsigned char *)(a)) +#define __arch_getw(a) (*(volatile unsigned short *)(a)) +#define __arch_getl(a) (*(volatile unsigned int *)(a)) + +#define __arch_putb(v,a) (*(volatile unsigned char *)(a) = (v)) +#define __arch_putw(v,a) (*(volatile unsigned short *)(a) = (v)) +#define __arch_putl(v,a) (*(volatile unsigned int *)(a) = (v)) + +#define __raw_writeb(v,a) __arch_putb(v,a) +#define __raw_writew(v,a) __arch_putw(v,a) +#define __raw_writel(v,a) __arch_putl(v,a) + +#define __raw_readb(a) __arch_getb(a) +#define __raw_readw(a) __arch_getw(a) +#define __raw_readl(a) __arch_getl(a) + +#define writeb(v,a) __arch_putb(v,a) +#define writew(v,a) __arch_putw(v,a) +#define writel(v,a) __arch_putl(v,a) + +#define readb(a) __arch_getb(a) +#define readw(a) __arch_getw(a) +#define readl(a) __arch_getl(a) + +#endif /* _MEMORY_H */ diff --git a/src/target/firmware/include/rf/trf6151.h b/src/target/firmware/include/rf/trf6151.h new file mode 100644 index 00000000..41cbe6c4 --- /dev/null +++ b/src/target/firmware/include/rf/trf6151.h @@ -0,0 +1,35 @@ +#ifndef _TRF6151_H +#define _TRF6151_H + +#include + +/* initialize (reset + power up) */ +void trf6151_init(void); + +/* switch power off or on */ +void trf6151_power(int on); + +/* set the VGA and RF gain */ +int trf6151_set_gain(uint8_t dbm, int high); + +/* obtain the current total gain of the TRF6151 */ +uint8_t trf6151_get_gain(void); + +/* Request the PLL to be tuned to the given frequency */ +void trf6151_set_arfcn(uint16_t arfcn, int uplink); + +enum trf6151_mode { + TRF6151_IDLE, + TRF6151_RX, + TRF6151_TX, +}; + +/* Set the operational mode of the TRF6151 chip */ +void trf6151_set_mode(enum trf6151_mode mode); + +void trf6151_test(uint16_t arfcn); + +/* prepare a Rx window with the TRF6151 finished at time 'start' (in qbits) */ +void trf6151_rx_window(int16_t start_qbits, uint16_t arfcn, uint8_t vga_dbm, int rf_gain_high); + +#endif /* TRF6151_H */ diff --git a/src/target/firmware/include/rffe.h b/src/target/firmware/include/rffe.h new file mode 100644 index 00000000..00a27080 --- /dev/null +++ b/src/target/firmware/include/rffe.h @@ -0,0 +1,15 @@ +#ifndef _RFFE_H +#define _RFFE_H + +#include "gsm.h" + +/* initialize RF Frontend */ +void rffe_init(void); + +/* switch RF Frontend Mode */ +void rffe_mode(enum gsm_band band, int tx); + +/* get current gain of RF frontend (anything between antenna and baseband in dBm */ +uint8_t rffe_get_gain(void); + +#endif diff --git a/src/target/firmware/include/spi.h b/src/target/firmware/include/spi.h new file mode 100644 index 00000000..0925a9a3 --- /dev/null +++ b/src/target/firmware/include/spi.h @@ -0,0 +1,7 @@ +#ifndef _SPI_H +#define _SPI_H + +void spi_init(void); +int spi_xfer(uint8_t dev_idx, uint8_t bitlen, const void *dout, void *din); + +#endif /* _SPI_H */ diff --git a/src/target/firmware/include/stdio.h b/src/target/firmware/include/stdio.h new file mode 100644 index 00000000..c1f86371 --- /dev/null +++ b/src/target/firmware/include/stdio.h @@ -0,0 +1,47 @@ +#ifndef _STDIO_H +#define _STDIO_H + +#ifndef NULL +#define NULL 0 +#endif /* NULL */ + +#include + +int printf(const char *format, ...); +int sprintf(char *str, const char *format, ...); +int snprintf(char *str, size_t size, const char *format, ...); + +#include + +int vprintf(const char *format, va_list ap); +int vsprintf(char *str, const char *format, va_list ap); +int vsnprintf(char *str, size_t size, const char *format, va_list ap); +int puts(const char *s); + +#if 0 +/* start.S based uart console */ +#include +#define putchar(c) uart_putchar_wait(1, c) +int puts(const char *s); +#endif + +#if 0 +/* regular UART console */ +#include +#define putchar(c) cons_putchar(c) +#define _puts(s) cons_puts(s) +#define ARCH_HAS_CONSOLE +#endif + +#if 1 +/* sercomm based console */ +#include +#define putchar(c) sercomm_putchar(c) +#define _puts(s) sercomm_puts(s) +#define ARCH_HAS_CONSOLE +#endif + +/* non-standard */ +extern void phex(unsigned int c, unsigned int len); + +#endif /* _STDIO_H */ diff --git a/src/target/firmware/include/string.h b/src/target/firmware/include/string.h new file mode 100644 index 00000000..f060659a --- /dev/null +++ b/src/target/firmware/include/string.h @@ -0,0 +1,12 @@ +#ifndef _STRING_H +#define _STRING_H + +#include + +size_t strnlen(const char *s, size_t count); +size_t strlen(const char *s); + +void *memset(void *s, int c, size_t n); +void *memcpy(void *dest, const void *src, size_t n); + +#endif diff --git a/src/target/firmware/layer1/Makefile b/src/target/firmware/layer1/Makefile new file mode 100644 index 00000000..113e7e60 --- /dev/null +++ b/src/target/firmware/layer1/Makefile @@ -0,0 +1,18 @@ +INCLUDES=-I../include/ -I../../../../include +-include ../Makefile.inc + +LIBNAME=layer1 +OBJS=avg.o agc.o afc.o sync.o gsm.o tdma_sched.o tpu_window.o init.o l23_api.o + +LST=$(OBJS:.o=.lst) + +all: lib$(LIBNAME).a + +%.o: %.c + $(CROSS_COMPILE)$(CC) $(CFLAGS) -c -o $@ $^ + +lib$(LIBNAME).a: $(OBJS) + $(CROSS_COMPILE)$(AR) cru $@ $^ + +clean: + rm -f *.a $(OBJS) $(LST) diff --git a/src/target/firmware/layer1/afc.c b/src/target/firmware/layer1/afc.c new file mode 100644 index 00000000..846d208f --- /dev/null +++ b/src/target/firmware/layer1/afc.c @@ -0,0 +1,122 @@ +/* AFC (Automatic Frequency Correction) Implementation */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#include +#include + +#include +#include +#include + +/* Over how many TDMA frames do we want to average? (this may change in dedicated mode) */ +#define AFC_PERIOD 40 +/* How many of our measurements have to be valid? */ +#define AFC_MIN_MUN_VALID 8 + +/* The actual AFC code */ + +struct afc_state { + struct running_avg ravg; /* running average */ + int16_t dac_value; /* current DAC output value */ + uint16_t arfcn; +}; + +static void afc_ravg_output(struct running_avg *ravg, int32_t avg); + +static struct afc_state afc_state = { + .ravg = { + .outfn = &afc_ravg_output, + .period = AFC_PERIOD, + .min_valid = AFC_MIN_MUN_VALID, + }, +}; + +/* The AFC DAC in the ABB has to be configured as follows: + * DAC = 1MHz / 947MHz * FreqErr(Hz) / AFCslop(ppm/LSB) + * where: + * 947 MHz is the center of EGSM + * AFCslope is coded F1.15, thus a normalization factor of 2^15 aplpies + */ + +#define AFC_NORM_FACTOR_GSM ((1<<15) / 947) +#define AFC_NORM_FACTOR_DCS ((1<<15) / 1894) + +/* we assume 4.9ppb per LSB, equals 0.0049 * 32768 == 160 */ +#define AFC_SLOPE 160 +//#define AFC_SLOPE 141 + +/* The DSP can measure the frequency error in the following ranges: + * FB_MODE0: +/- 20 kHz + * FB_MODE1: +/- 4 kHz + * Sync Burst: +/- 1 kHz + * Normal Burst: +/- 400 Hz + */ + +/* Update the AFC with a frequency error, bypassing averaging */ +void afc_correct(int16_t freq_error, uint16_t arfcn) +{ + int32_t afc_norm_factor; + int16_t delta; + + switch (gsm_arfcn2band(arfcn)) { + case GSM_900: + case GSM_850: + afc_norm_factor = AFC_NORM_FACTOR_GSM; + break; + default: + afc_norm_factor = AFC_NORM_FACTOR_DCS; + } + + delta = (int16_t) ((afc_norm_factor * (int32_t)freq_error) / AFC_SLOPE); + printd("afc_correct(error=%dHz, arfcn=%u): delta=%d, afc_dac(old=%d,new=%d)\n", + freq_error, arfcn, delta, afc_state.dac_value, afc_state.dac_value+delta); + afc_state.dac_value += delta; + + /* The AFC DAC has only 13 bits */ + if (afc_state.dac_value > 4095) + afc_state.dac_value = 4095; + else if (afc_state.dac_value < -4096) + afc_state.dac_value = -4096; +} + +void afc_input(int32_t freq_error, uint16_t arfcn, int valid) +{ + afc_state.arfcn = arfcn; + runavg_input(&afc_state.ravg, freq_error, valid); + runavg_check_output(&afc_state.ravg); +} + +/* callback function for runavg */ +static void afc_ravg_output(struct running_avg *ravg, int32_t avg) +{ + afc_correct(avg, afc_state.arfcn); +} + +/* Update DSP with new AFC DAC value to be used for next TDMA frame */ +void afc_load_dsp(void) +{ + dsp_api.db_w->d_afc = afc_state.dac_value; + dsp_api.db_w->d_ctrl_abb |= (1 << B_AFC); +} diff --git a/src/target/firmware/layer1/agc.c b/src/target/firmware/layer1/agc.c new file mode 100644 index 00000000..aa4af5e1 --- /dev/null +++ b/src/target/firmware/layer1/agc.c @@ -0,0 +1,67 @@ +/* AFC (Automatic Gain Control) Implementation */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#include +#include +#include + +#include +#include + +/* This is a value that has been measured on the C123 by Harald: 71dBm, + it is the difference between the input level at the antenna and what + the DSP reports, subtracted by the total gain of the TRF6151 */ +#define SYSTEM_INHERENT_GAIN 71 + +/* compute the input level present at the antenna based on a baseband + * power measurement of the DSP at baseband */ +int16_t agc_inp_dbm8_by_pm(int16_t pm) +{ + /* pm is in 1/8 dBm at baseband */ + int16_t total_gain_dbm8; + + /* compute total current gain */ + total_gain_dbm8 = (SYSTEM_INHERENT_GAIN + rffe_get_gain()) * 8; + + /* subtract gain from power measurement at baseband level */ + return pm - total_gain_dbm8; +} + +uint8_t agc_il_by_dbm8(int16_t dbm8) +{ + uint16_t il; + + /* convert from 1/8 dBm to l1c format: [220..0] in -1/2dBm unit */ + if (dbm8 >= 0) + il = 0; + else + il = -dbm8; + + /* saturate */ + if (il > 4 * 255) + il = 4 * 255; + + return (uint8_t)(il >> 2); +} diff --git a/src/target/firmware/layer1/avg.c b/src/target/firmware/layer1/avg.c new file mode 100644 index 00000000..a4bf565b --- /dev/null +++ b/src/target/firmware/layer1/avg.c @@ -0,0 +1,57 @@ +/* Averaging Implementation */ + +/* (C) 2010 by Harald Welte + * + * 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 + +#include + +/* input a new sample into the averaging process */ +void runavg_input(struct running_avg *ravg, int32_t val, int valid) +{ + ravg->num_samples++; + if (valid) { + ravg->acc_val += val; + ravg->num_samples_valid++; + } +} + +/* check if sufficient samples have been obtained, and call outfn() */ +int runavg_check_output(struct running_avg *ravg) +{ + if (ravg->num_samples < ravg->period) + return 0; + + if (ravg->num_samples_valid >= ravg->min_valid) { + int32_t avg = ravg->acc_val / ravg->num_samples_valid; + + ravg->outfn(ravg, avg); + + ravg->num_samples = ravg->num_samples_valid = 0; + ravg->acc_val = 0; + + return 1; + } + + return 0; +} + + diff --git a/src/target/firmware/layer1/gsm.c b/src/target/firmware/layer1/gsm.c new file mode 100644 index 00000000..99f67f30 --- /dev/null +++ b/src/target/firmware/layer1/gsm.c @@ -0,0 +1,117 @@ +/* Generic GSM utility routines */ + +/* (C) 2010 by Harald Welte + * + * 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 + +#include + +enum gsm_band gsm_arfcn2band(uint16_t arfcn) +{ + if (arfcn & ARFCN_PCS) + return GSM_1900; + else if (arfcn <= 124) + return GSM_900; + else if (arfcn >= 955 && arfcn <= 1023) + return GSM_900; + else if (arfcn >= 128 && arfcn <= 251) + return GSM_850; + else if (arfcn >= 512 && arfcn <= 885) + return GSM_1800; + else if (arfcn >= 259 && arfcn <= 293) + return GSM_450; + else if (arfcn >= 306 && arfcn <= 340) + return GSM_480; + else if (arfcn >= 350 && arfcn <= 425) + return GSM_810; + else if (arfcn >= 438 && arfcn <= 511) + return GSM_750; + else + return GSM_1800; +} + +/* Convert an ARFCN to the frequency in MHz * 10 */ +uint16_t gsm_arfcn2freq10(uint16_t arfcn, int uplink) +{ + uint16_t freq10_ul; + uint16_t freq10_dl; + + if (arfcn & ARFCN_PCS) { + /* DCS 1900 */ + arfcn &= ~ARFCN_PCS; + freq10_ul = 18502 + 2 * (arfcn-512); + freq10_dl = freq10_ul + 800; + } else if (arfcn <= 124) { + /* Primary GSM + ARFCN 0 of E-GSM */ + freq10_ul = 8900 + 2 * arfcn; + freq10_dl = freq10_ul + 450; + } else if (arfcn >= 955 && arfcn <= 1023) { + /* E-GSM and R-GSM */ + freq10_ul = 8900 + 2 * (arfcn - 1024); + freq10_dl = freq10_ul + 450; + } else if (arfcn >= 128 && arfcn <= 251) { + /* GSM 850 */ + freq10_ul = 8242 + 2 * (arfcn - 128); + freq10_dl = freq10_ul + 450; + } else if (arfcn >= 512 && arfcn <= 885) { + /* DCS 1800 */ + freq10_ul = 17102 + 2 * (arfcn - 512); + freq10_dl = freq10_ul + 950; + } else if (arfcn >= 259 && arfcn <= 293) { + /* GSM 450 */ + freq10_ul = 4506 + 2 * (arfcn - 259); + freq10_dl = freq10_ul + 100; + } else if (arfcn >= 306 && arfcn <= 340) { + /* GSM 480 */ + freq10_ul = 4790 + 2 * (arfcn - 306); + freq10_dl = freq10_ul + 100; + } else if (arfcn >= 350 && arfcn <= 425) { + /* GSM 810 */ + freq10_ul = 8060 + 2 * (arfcn - 350); + freq10_dl = freq10_ul + 450; + } else if (arfcn >= 438 && arfcn <= 511) { + /* GSM 750 */ + freq10_ul = 7472 + 2 * (arfcn - 438); + freq10_dl = freq10_ul + 300; + } else + return 0xffff; + + if (uplink) + return freq10_ul; + else + return freq10_dl; +} + +void gsm_fn2gsmtime(struct gsm_time *time, uint32_t fn) +{ + time->fn = fn; + time->t1 = time->fn / (26*51); + time->t2 = time->fn % 26; + time->t3 = time->fn % 51; + time->tc = (time->fn / 51) % 8; +} + +uint32_t gsm_gsmtime2fn(struct gsm_time *time) +{ + /* TS 05.02 Chapter 4.3.3 TDMA frame number */ + return (51 * ((time->t3 - time->t2 + 26) % 26) + time->t3 + (26 * 51 * time->t1)); +} diff --git a/src/target/firmware/layer1/init.c b/src/target/firmware/layer1/init.c new file mode 100644 index 00000000..1c387772 --- /dev/null +++ b/src/target/firmware/layer1/init.c @@ -0,0 +1,70 @@ +/* OsmocomBB Layer1 initialization */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +void layer1_init(void) +{ + struct msgb *msg; + + /* initialize TDMA Frame IRQ driven synchronous L1 */ + l1s_init(); + /* power up the DSP */ + dsp_power_on(); + + /* Initialize TPU, TSP and TRF drivers */ + tpu_init(); + tsp_init(); + trf6151_init(); + + rffe_init(); + +#if 0 /* only if RX TPU window is disabled! */ + /* Put TWL3025 in downlink mode (includes calibration) */ + twl3025_downlink(1, 1000); +#endif + + /* issue the TRF and TWL initialization sequence */ + tpu_enq_sleep(); + tpu_enable(1); + tpu_wait_idle(); + + /* Disable RTC interrupt as it causes lost TDMA frames */ + irq_disable(IRQ_RTC_TIMER); + + /* inform l2 and upwards that we are ready for orders */ + msg = l1_create_l2_msg(LAYER1_RESET, 0, 0); + l1_queue_for_l2(msg); +} diff --git a/src/target/firmware/layer1/l23_api.c b/src/target/firmware/layer1/l23_api.c new file mode 100644 index 00000000..85f73cde --- /dev/null +++ b/src/target/firmware/layer1/l23_api.c @@ -0,0 +1,63 @@ +/* Synchronous part of GSM Layer 1: API to Layer2+ */ + +/* (C) 2010 by Holger Hans Peter Freyther + * + * 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 +#include + +#include +#include + +#include +#include + +/* the size we will allocate struct msgb* for HDLC */ +#define L3_MSG_SIZE (sizeof(struct l1_ccch_info_ind) + 4) +#define L3_MSG_HEAD 4 + +void l1_queue_for_l2(struct msgb *msg) +{ + /* forward via serial for now */ + sercomm_sendmsg(SC_DLCI_L1A_L23, msg); +} + +struct msgb *l1_create_l2_msg(int msg_type, uint32_t fn, uint16_t snr) +{ + struct l1_info_dl *dl; + struct msgb *msg; + + msg = msgb_alloc_headroom(L3_MSG_SIZE, L3_MSG_HEAD, "l1_burst"); + if (!msg) { + while (1) { + puts("OOPS. Out of buffers...\n"); + } + + return NULL; + } + + dl = (struct l1_info_dl *) msgb_put(msg, sizeof(*dl)); + dl->msg_type = msg_type; + /* FIXME: we may want to compute T1/T2/T3 in L23 */ + gsm_fn2gsmtime(&dl->time, fn); + dl->snr[0] = snr; + + return msg; +} diff --git a/src/target/firmware/layer1/sync.c b/src/target/firmware/layer1/sync.c new file mode 100644 index 00000000..088b2ea7 --- /dev/null +++ b/src/target/firmware/layer1/sync.c @@ -0,0 +1,911 @@ +/* Synchronous part of GSM Layer 1 */ + +/* (C) 2010 by Harald Welte + * (C) 2010 by Dieter Spaar + * (C) 2010 by Holger Hans Peter Freyther + * + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +//#define DEBUG_EVERY_TDMA + +/* A debug macro to print every TDMA frame */ +#ifdef DEBUG_EVERY_TDMA +#define putchart(x) putchar(x) +#else +#define putchart(x) +#endif + +struct l1s_state l1s; + +static l1s_cb_t l1s_cb = NULL; + +void l1s_set_handler(l1s_cb_t cb) +{ + l1s_cb = cb; +} + +#define ADD_MODULO(sum, delta, modulo) do { \ + if ((sum += delta) >= modulo) \ + sum -= modulo; \ + } while (0) + +#define GSM_MAX_FN (26*51*2048) + +static void l1s_time_inc(struct gsm_time *time, uint32_t delta_fn) +{ + ADD_MODULO(time->fn, delta_fn, GSM_MAX_FN); + + if (delta_fn == 1) { + ADD_MODULO(time->t2, 1, 26); + ADD_MODULO(time->t3, 1, 51); + + /* if the new frame number is a multiple of 51 */ + if (time->t3 == 0) { + ADD_MODULO(time->tc, 1, 8); + + /* if new FN is multiple of 51 and 26 */ + if (time->t2 == 0) + ADD_MODULO(time->t1, 1, 2048); + } + } else + gsm_fn2gsmtime(time, time->fn); +} + +static void l1s_time_dump(const struct gsm_time *time) +{ + printf("fn=%u(%u/%2u/%2u)", time->fn, time->t1, time->t2, time->t3); +} + +/* determine the GSM time and BSIC from a Sync Burst */ +static uint8_t l1s_decode_sb(struct gsm_time *time, uint32_t sb) +{ + uint8_t bsic = (sb >> 2) & 0x3f; + uint8_t t3p; + + memset(time, 0, sizeof(*time)); + + /* TS 05.02 Chapter 3.3.2.2.1 SCH Frame Numbers */ + time->t1 = ((sb >> 23) & 1) | ((sb >> 7) & 0x1fe) | ((sb << 9) & 0x600); + time->t2 = (sb >> 18) & 0x1f; + t3p = ((sb >> 24) & 1) | ((sb >> 15) & 6); + time->t3 = t3p*10 + 1; + + /* TS 05.02 Chapter 4.3.3 TDMA frame number */ + time->fn = gsm_gsmtime2fn(time); + + time->tc = (time->fn / 51) % 8; + + return bsic; +} + +static int last_task_fnr; + +extern uint16_t rf_arfcn; // TODO + +/* clip a signed 16bit value at a certain limit */ +int16_t clip_int16(int16_t angle, int16_t clip_at) +{ + if (angle > clip_at) + angle = clip_at; + else if (angle < -clip_at) + angle = -clip_at; + + return angle; +} + +int16_t l1s_snr_int(uint16_t snr) +{ + return snr >> 10; +} + +uint16_t l1s_snr_fract(uint16_t snr) +{ + uint32_t fract = snr & 0x3ff; + fract = fract * 1000 / (2 << 10); + + return fract & 0xffff; +} + +static void l1ddsp_meas_read(uint8_t nbmeas, uint16_t *pm) +{ + uint8_t i; + + for (i = 0; i < nbmeas; i++) + pm[i] = (uint16_t) ((dsp_api.db_r->a_pm[i] & 0xffff) >> 3); + dsp_api.r_page_used = 1; +} + +/* Convert an angle in fx1.15 notatinon into Hz */ +#define BITFREQ_DIV_2PI 43104 /* 270kHz / 2 * pi */ +#define ANG2FREQ_SCALING (2<<15) /* 2^15 scaling factor for fx1.15 */ +#define ANGLE_TO_FREQ(angle) ((int16_t)angle * BITFREQ_DIV_2PI / ANG2FREQ_SCALING) + +#define AFC_MAX_ANGLE 328 /* 0.01 radian in fx1.15 */ +#define AFC_SNR_THRESHOLD 2560 /* 2.5 dB in fx6.10 */ + +#define BITS_PER_TDMA 1250 +#define QBITS_PER_TDMA (BITS_PER_TDMA * 4) /* 5000 */ +#define TPU_RANGE QBITS_PER_TDMA +#define SWITCH_TIME (TPU_RANGE-10) + + +static int fb_once = 0; + +/* synchronize the L1S to a new timebase (typically a new cell */ +static void synchronize_tdma(struct l1_cell_info *cinfo) +{ + int32_t fn_offset; + uint32_t tpu_shift = cinfo->time_alignment; + + /* NB detection only works if the TOA of the SB + * is within 0...8. We have to add 75 to get an SB TOA of 4. */ + tpu_shift += 75; + + tpu_shift = (l1s.tpu_offset + tpu_shift) % QBITS_PER_TDMA; + + fn_offset = cinfo->fn_offset - 1; + + /* if we're already very close to the end of the TPU frame, + * the next interrupt will basically occur now and we need to compensate */ + if (tpu_shift < SWITCH_TIME) + fn_offset++; + +#if 0 /* probably wrong as we already added "offset" and "shift" above */ + /* increment the TPU quarter-bit offset */ + l1s.tpu_offset = (l1s.tpu_offset + tpu_shift) % TPU_RANGE; +#else + l1s.tpu_offset = tpu_shift; +#endif + + puts("Synchronize_TDMA\n"); + /* request the TPU to adjust the SYNCHRO and OFFSET registers */ + tpu_enq_at(SWITCH_TIME); + tpu_enq_sync(l1s.tpu_offset); +#if 0 + /* FIXME: properly end the TPU window at the emd of l1_sync() */ + tpu_end_scenario(); +#endif + + /* Change the current time to reflect the new value */ + l1s_time_inc(&l1s.current_time, fn_offset); + l1s.next_time = l1s.current_time; + l1s_time_inc(&l1s.next_time, 1); + + /* The serving cell now no longer has a frame or bit offset */ + cinfo->fn_offset = 0; + cinfo->time_alignment = 0; +} + +static void l1s_reset_hw(void) +{ + dsp_api.w_page = 0; + dsp_api.r_page = 0; + dsp_api.r_page_used = 0; + dsp_api.db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_0; + dsp_api.db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_0; + dsp_api.ndb->d_dsp_page = 0; + + /* we have to really reset the TPU, otherwise FB detection + * somtimes returns wrong TOA values. */ + tpu_reset(1); + tpu_reset(0); + tpu_rewind(); + tpu_enq_wait(5); /* really needed ? */ + tpu_enq_offset(l1s.tpu_offset); + tpu_end_scenario(); +} + +struct mon_state { + uint32_t fnr_report; /* frame number when DSP reported it */ + int attempt; /* which attempt was this ? */ + + int16_t toa; + uint16_t pm; + uint16_t angle; + uint16_t snr; + + /* computed values */ + int16_t freq_diff; +}; + +static void dump_mon_state(struct mon_state *fb) +{ +#if 0 + printf("(%u:%u): TOA=%5u, Power=%4ddBm, Angle=%5dHz, " + "SNR=%04x(%d.%u) OFFSET=%u SYNCHRO=%u\n", fb->fnr_report, fb->attempt, + fb->toa, agc_inp_dbm8_by_pm(fb->pm)/8, + ANGLE_TO_FREQ(fb->angle), fb->snr, l1s_snr_int(fb->snr), + l1s_snr_fract(fb->snr), tpu_get_offset(), tpu_get_synchro()); +#else + printf("(%u:%u): TOA=%5u, Power=%4ddBm, Angle=%5dHz ", fb->fnr_report, fb->attempt, + fb->toa, agc_inp_dbm8_by_pm(fb->pm)/8, + ANGLE_TO_FREQ(fb->angle)); +#endif +} + +static struct mon_state _last_fb, *last_fb = &_last_fb; + +static int read_fb_result(int attempt) +{ + last_fb->toa = dsp_api.ndb->a_sync_demod[D_TOA]; + last_fb->pm = dsp_api.ndb->a_sync_demod[D_PM]>>3; + last_fb->angle = dsp_api.ndb->a_sync_demod[D_ANGLE]; + last_fb->snr = dsp_api.ndb->a_sync_demod[D_SNR]; + + //last_fb->angle = clip_int16(last_fb->angle, AFC_MAX_ANGLE); + last_fb->freq_diff = ANGLE_TO_FREQ(last_fb->angle); + last_fb->fnr_report = l1s.current_time.fn; + last_fb->attempt = attempt; + + dump_mon_state(last_fb); + + dsp_api.ndb->d_fb_det = 0; + dsp_api.ndb->a_sync_demod[D_TOA] = 0; /* TSM30 does it (really needed ?) */ + + /* Update AFC with current frequency offset */ + afc_correct(last_fb->freq_diff, rf_arfcn); + + //tpu_dsp_frameirq_enable(); + return 1; +} + +static void read_sb_result(int attempt) +{ + last_fb->toa = dsp_api.db_r->a_serv_demod[D_TOA]; + last_fb->pm = dsp_api.db_r->a_serv_demod[D_PM]>>3; + last_fb->angle = dsp_api.db_r->a_serv_demod[D_ANGLE]; + last_fb->snr = dsp_api.db_r->a_serv_demod[D_SNR]; + + last_fb->freq_diff = ANGLE_TO_FREQ(last_fb->angle); + last_fb->fnr_report = l1s.current_time.fn; + last_fb->attempt = attempt; + + dump_mon_state(last_fb); + + if (last_fb->snr > AFC_SNR_THRESHOLD) + afc_input(last_fb->freq_diff, rf_arfcn, 1); + else + afc_input(last_fb->freq_diff, rf_arfcn, 0); + + dsp_api.r_page_used = 1; +} + +#define TIMER_TICKS_PER_TDMA 1875 + +static int last_timestamp; + +static inline void check_lost_frame(void) +{ + int diff, timestamp = hwtimer_read(1); + + if (last_timestamp < timestamp) + last_timestamp += (4*TIMER_TICKS_PER_TDMA); + + diff = last_timestamp - timestamp; + if (diff != 1875) + printf("LOST!\n"); + + last_timestamp = timestamp; +} + +/* main routine for synchronous part of layer 1, called by frame interrupt + * generated by TPU once every TDMA frame */ +static void l1_sync(void) +{ + putchart('+'); + + check_lost_frame(); + + /* Increment Time */ + l1s.current_time = l1s.next_time; + l1s_time_inc(&l1s.next_time, 1); + //l1s_time_dump(&l1s.current_time); putchar(' '); + + dsp_api.frame_ctr++; + dsp_api.r_page_used = 0; + + /* Update pointers */ + if (dsp_api.w_page == 0) + dsp_api.db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_0; + else + dsp_api.db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_1; + + if (dsp_api.r_page == 0) + dsp_api.db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_0; + else + dsp_api.db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_1; + + /* Reset MCU->DSP page */ + dsp_api_memset((uint16_t *) dsp_api.db_w, sizeof(*dsp_api.db_w)); + + /* Update AFC */ + afc_load_dsp(); + + if (dsp_api.ndb->d_error_status) { + printf("DSP Error Status: %u\n", dsp_api.ndb->d_error_status); + dsp_api.ndb->d_error_status = 0; + } + +#if 0 + if (l1s.task != dsp_api.db_r->d_task_md) + printf("DSP task (%u) and L1S task (%u) disagree\n", dsp_api.db_r->d_task_md, l1s.task); +#endif + /* execute the sched_items that have been scheduled for this TDMA frame */ + tdma_sched_execute(); + + if (dsp_api.r_page_used) { + /* clear and switch the read page */ + dsp_api_memset((uint16_t *) dsp_api.db_r, sizeof(*dsp_api.db_r)); + + /* TSM30 does it (really needed ?): + * Set crc result as "SB not found". */ + dsp_api.db_r->a_sch[0] = (1<d_task_d = NO_DSP_TASK; /* Init. RX task to NO TASK */ + dsp_api.db_w->d_task_u = NO_DSP_TASK; /* Init. TX task to NO TASK */ + dsp_api.db_w->d_task_ra = NO_DSP_TASK; /* Init. RA task to NO TASK */ + dsp_api.db_w->d_task_md = NO_DSP_TASK; /* Init. MONITORING task to NO TASK */ + dsp_api.ndb->d_dsp_page = 0; + + /* Set "b_abort" to TRUE, dsp will reset current and pending tasks */ + dsp_api.db_w->d_ctrl_system |= (1 << B_TASK_ABORT); + return 0; +} + +void l1s_dsp_abort(void) +{ + /* abort right now */ + tdma_schedule(0, &l1s_abort_cmd, 0, 0); +} + +/* FCCH Burst *****************************************************************/ + +/* scheduler callback to issue a FB detection task to the DSP */ +static int l1s_fbdet_cmd(uint16_t p1, uint16_t fb_mode) +{ + if (fb_mode == 0) { + putchart('F'); + } else { + putchart('V'); + } + + /* Program DSP */ + l1s.task = dsp_api.db_w->d_task_md = FB_DSP_TASK; /* maybe with I/Q swap? */ + dsp_api.ndb->d_fb_mode = fb_mode; + dsp_end_scenario(); + last_task_fnr = dsp_api.frame_ctr; + + /* Program TPU */ + l1s_rx_win_ctrl(rf_arfcn, L1_RXWIN_FB); + tpu_end_scenario(); + + return 0; +} + + +/* scheduler callback to check for a FB detection response */ +static int l1s_fbdet_resp(uint16_t p1, uint16_t attempt) +{ + int ntdma, qbits, fn_offset; + + putchart('f'); + + if (!dsp_api.ndb->d_fb_det) { + /* we did not detect a FB, fall back to mode 0! */ + if (attempt == 12) { + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + + /* if we are already synchronized initially */ + if (fb_once == 1) + l1s_fb_test(1, 1); + else + l1s_fb_test(1, 0); + } + return 0; + } + + printf("FB%u ", dsp_api.ndb->d_fb_mode); + read_fb_result(attempt); + + last_fb->toa -= 23; + + if (last_fb->toa < 0) { + qbits = (last_fb->toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (last_fb->toa) / BITS_PER_TDMA; + qbits = (last_fb->toa - ntdma * BITS_PER_TDMA) * 4; + } + + { + fn_offset = l1s.current_time.fn - attempt + ntdma; + int fnr_delta = last_fb->fnr_report - attempt; + int bits_delta = fnr_delta * BITS_PER_TDMA; + + struct l1_cell_info *cinfo = &l1s.serving_cell; + + cinfo->fn_offset = fnr_delta; + cinfo->time_alignment = qbits; + cinfo->arfcn = rf_arfcn; + + if (last_fb->toa > bits_delta) + printf("=> DSP reports FB in bit that is %d bits in the future?!?\n", + last_fb->toa - bits_delta); + else { + int fb_fnr = last_task_fnr + last_fb->toa/BITS_PER_TDMA; + printf("=>FB @ FNR %u fn_offset=%d qbits=%u\n", fb_fnr, fn_offset, qbits); + } + } + + if (dsp_api.frame_ctr > 500 && fb_once == 0) { + /* Don't synchronize_tdma() yet, it does probably not work + * reliable due to the TPU reset) */ + l1s_reset_hw(); + tdma_sched_reset(); + fb_once = 1; + } else { + /* We found a frequency burst, reset everything and start next task */ + l1s_reset_hw(); + tdma_sched_reset(); + } + +#if 1 + /* restart a SB or new FB detection task */ + if (dsp_api.frame_ctr > 1000 && fb_once == 1 && + abs(last_fb->freq_diff) < 1000) { + int delay; + + /* synchronize before reading SB */ + synchronize_tdma(&l1s.serving_cell); + + delay = fn_offset + 11 - l1s.current_time.fn - 1; + dsp_api.ndb->d_fb_det = 0; + dsp_api.ndb->a_sync_demod[D_TOA] = 0; /* TSM30 does it (really needed ?) */ + fb_once = 0; + l1s_sb_test(delay); + } else +#endif + { + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + /* use FB_MODE_1 if we are within certain limits */ + if (abs(last_fb->freq_diff < 2000)) + l1s_fb_test(fn_offset + 10 - l1s.current_time.fn - 1, 1); + else + l1s_fb_test(fn_offset + 10 - l1s.current_time.fn - 1, 0); + } + + return 0; +} + +#define SCHED_ITEM(x, y, z) { .cb = x, .p1 = y, .p2 = z } +#define SCHED_END_FRAME() { .cb = NULL, .p1 = 0, .p2 = 0 } + +/* we don't really use this because we need to configure the fb_mode! */ +static const struct tdma_sched_item fb_sched_set[] = { + SCHED_ITEM(l1s_fbdet_cmd, 0, 0), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 2), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 3), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 4), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 5), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 6), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 7), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 8), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 9), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 10), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 11), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 12), SCHED_END_FRAME(), +}; + +void l1s_fb_test(uint8_t base_fn, uint8_t fb_mode) +{ +#if 1 + int i; + /* schedule the FB detection command */ + tdma_schedule(base_fn, &l1s_fbdet_cmd, 0, fb_mode); + + /* schedule 12 attempts to read the result */ + for (i = 1; i <= 12; i++) { + uint8_t fn = base_fn + 1 + i; + tdma_schedule(fn, &l1s_fbdet_resp, 0, i); + } +#else + /* use the new scheduler 'set' and simply schedule the whole set */ + /* WARNING: we cannot set FB_MODE_1 this way !!! */ + tdma_schedule_set(base_fn, fb_sched_set, ARRAY_SIZE(fb_sched_set)); +#endif +} + +/* SCH Burst Detection ********************************************************/ + +static int sb_once = 0; + +static uint8_t sb_cnt; + +/* Note: When we get the SB response, it is 2 TDMA frames after the SB + * actually happened, as it is a "C W W R" task */ +#define SB2_LATENCY 2 + +static int l1s_sbdet_resp(uint16_t p1, uint16_t attempt) +{ + uint32_t sb; + uint8_t bsic; + static struct gsm_time sb_time; + int qbits, fn_offset; + struct l1_cell_info *cinfo = &l1s.serving_cell; + int fnr_delta, bits_delta; + struct l1_sync_new_ccch_resp *l1; + struct msgb *msg; + + putchart('s'); + + if (dsp_api.db_r->a_sch[0] & (1<a_sch[3] | dsp_api.db_r->a_sch[4] << 16; + bsic = l1s_decode_sb(&sb_time, sb); + printf("=> SB 0x%08x: BSIC=%u ", sb, bsic); + l1s_time_dump(&sb_time); + + l1s.serving_cell.bsic = bsic; + + /* calculate synchronisation value (TODO: only complete for qbits) */ + last_fb->toa -= 23; + qbits = last_fb->toa * 4; + fn_offset = l1s.current_time.fn; // TODO + + if (qbits > QBITS_PER_TDMA) { + qbits -= QBITS_PER_TDMA; + fn_offset -= 1; + } else if (qbits < 0) { + qbits += QBITS_PER_TDMA; + fn_offset += 1; + } + + fnr_delta = last_fb->fnr_report - attempt; + bits_delta = fnr_delta * BITS_PER_TDMA; + + cinfo->fn_offset = fnr_delta; + cinfo->time_alignment = qbits; + cinfo->arfcn = rf_arfcn; + + if (last_fb->toa > bits_delta) + printf("=> DSP reports SB in bit that is %d bits in the future?!?\n", + last_fb->toa - bits_delta); + else + printf(" qbits=%u\n", qbits); + + if (sb_cnt > 5 && sb_once == 0) { + synchronize_tdma(&l1s.serving_cell); + sb_once = 1; + } + + /* if we have recived a SYNC burst, update our local GSM time */ + gsm_fn2gsmtime(&l1s.current_time, sb_time.fn + SB2_LATENCY); + /* compute next time from new current time */ + l1s.next_time = l1s.current_time; + l1s_time_inc(&l1s.next_time, 1); + + /* place it in the queue for the layer2 */ + msg = l1_create_l2_msg(SYNC_NEW_CCCH_RESP, sb_time.fn, last_fb->snr); + l1 = (struct l1_sync_new_ccch_resp *) msgb_put(msg, sizeof(*l1)); + l1->bsic = bsic; + l1_queue_for_l2(msg); + +#if 0 + tdma_sched_reset(); +#else + /* + If we call tdma_sched_reset(), which is only needed if there are + further l1s_sbdet_resp() scheduled, we will bring dsp_api.db_r and + dsp_api.db_w out of sync because we changed dsp_api.db_w for l1s_sbdet_cmd() + and canceled l1s_sbdet_resp() which would change dsp_api.db_r. The DSP + however expects dsp_api.db_w and dsp_api.db_r to be in sync (either + "0 - 0" or "1 - 1"). So we have to bring dsp_api.db_w and dsp_api.db_r + into sync again, otherwise NB reading will complain. We probably don't + need the Abort command and could just bring dsp_api.db_w and dsp_api.db_r + into sync. + */ + if (attempt != 2) { + tdma_sched_reset(); + l1s_dsp_abort(); + } +#endif + if (sb_cnt > 10 && sb_time.t3 == 41) { + l1s_reset_hw(); + /* current t3 == 43, need to start NB detection in t3 = 1, difference is 9 */ + l1s_nb_test(9); + } else { + /* We have just seen a SCH burst, we know the next one is not in + * less than 7 TDMA frames from now */ + l1s_sb_test(7); + } + + return 0; +} + +static int l1s_sbdet_cmd(uint16_t p1, uint16_t p2) +{ + putchart('S'); + + l1s.task = dsp_api.db_w->d_task_md = SB_DSP_TASK; + dsp_api.ndb->d_fb_mode = 0; /* wideband search */ + dsp_end_scenario(); + + last_task_fnr = dsp_api.frame_ctr; + + /* Program TPU */ + l1s_rx_win_ctrl(rf_arfcn, L1_RXWIN_SB); + tpu_end_scenario(); + + return 0; +} + +void l1s_sb_test(uint8_t base_fn) +{ +#if 1 + /* This is how it is done by the TSM30 */ + tdma_schedule(base_fn, &l1s_sbdet_cmd, 0, 1); + tdma_schedule(base_fn + 1, &l1s_sbdet_cmd, 0, 2); + tdma_schedule(base_fn + 3, &l1s_sbdet_resp, 0, 1); + tdma_schedule(base_fn + 4, &l1s_sbdet_resp, 0, 2); +#else + tdma_schedule(base_fn, &l1s_sbdet_cmd, 0, 1); + tdma_schedule(base_fn + 1, &l1s_sbdet_resp, 0, 1); + tdma_schedule(base_fn + 2, &l1s_sbdet_resp, 0, 2); +#endif +} + +/* Power Measurement **********************************************************/ + +/* scheduler callback to issue a power measurement task to the DSP */ +static int l1s_pm_cmd(uint16_t p1, uint16_t arfcn) +{ + putchart('P'); + + l1s.task = dsp_api.db_w->d_task_md = 2; + dsp_api.ndb->d_fb_mode = 0; /* wideband search */ + dsp_end_scenario(); + last_task_fnr = dsp_api.frame_ctr; + + /* Program TPU */ + //l1s_rx_win_ctrl(arfcn, L1_RXWIN_PW); + l1s_rx_win_ctrl(arfcn, L1_RXWIN_NB); + tpu_end_scenario(); + + return 0; +} + +/* scheduler callback to read power measurement resposnse from the DSP */ +static int l1s_pm_resp(uint16_t p1, uint16_t p2) +{ + uint16_t pm_level[2]; + struct l1_signal sig; + + putchart('p'); + + l1ddsp_meas_read(2, pm_level); + + printd("PM MEAS: %-4d dBm, %-4d dBm ARFCN=%u\n", + agc_inp_dbm8_by_pm(pm_level[0])/8, + agc_inp_dbm8_by_pm(pm_level[1])/8, rf_arfcn); + + /* build and deliver signal */ + sig.signum = L1_SIG_PM; + sig.arfcn = rf_arfcn; + sig.pm.dbm8[0] = agc_inp_dbm8_by_pm(pm_level[0]); + sig.pm.dbm8[1] = agc_inp_dbm8_by_pm(pm_level[1]); + + if (l1s_cb) + l1s_cb(&sig); + + return 0; +} + +void l1s_pm_test(uint8_t base_fn, uint16_t arfcn) +{ + tdma_schedule(base_fn, &l1s_pm_cmd, 0, arfcn); + tdma_schedule(base_fn + 2, &l1s_pm_resp, 0, 0); +} + +/* Normal Burst ***************************************************************/ + +static int l1s_nb_resp(uint16_t p1, uint16_t burst_id) +{ + static struct l1_signal _nb_sig, *sig = &_nb_sig; + struct msgb *msg; + + putchart('n'); + + /* just for debugging, d_task_d should not be 0 */ + if (dsp_api.db_r->d_task_d == 0) { + puts("EMPTY\n"); + return 0; + } + + /* DSP burst ID needs to corespond with what we expect */ + if (dsp_api.db_r->d_burst_d != burst_id) { + puts("BURST ID\n"); + return 0; + } + + sig->nb.meas[burst_id].toa_qbit = dsp_api.db_r->a_serv_demod[D_TOA]; + sig->nb.meas[burst_id].pm_dbm8 = dsp_api.db_r->a_serv_demod[D_PM] >> 3; + sig->nb.meas[burst_id].freq_err = ANGLE_TO_FREQ(dsp_api.db_r->a_serv_demod[D_ANGLE]); + sig->nb.meas[burst_id].snr = dsp_api.db_r->a_serv_demod[D_SNR]; + + /* feed computed frequency error into AFC loop */ + if (sig->nb.meas[burst_id].snr > AFC_SNR_THRESHOLD) + afc_input(sig->nb.meas[burst_id].freq_err, rf_arfcn, 1); + else + afc_input(sig->nb.meas[burst_id].freq_err, rf_arfcn, 0); + + /* 4th burst, get frame data */ + if (dsp_api.db_r->d_burst_d == 3) { + struct l1_info_dl *dl; + struct l1_ccch_info_ind *l1; + uint8_t i, j; + + sig->signum = L1_SIG_NB; + sig->nb.num_biterr = dsp_api.ndb->a_cd[2] & 0xffff; + sig->nb.crc = ((dsp_api.ndb->a_cd[0] & 0xffff) & ((1 << B_FIRE1) | (1 << B_FIRE0))) >> B_FIRE0; + sig->nb.fire = ((dsp_api.ndb->a_cd[0] & 0xffff) & (1 << B_FIRE1)) >> B_FIRE1; + + /* copy actual data, skipping the information block [0,1,2] */ + for (j = 0,i = 3; i < 15; i++) { + sig->nb.frame[j++] = dsp_api.ndb->a_cd[i] & 0xFF; + sig->nb.frame[j++] = (dsp_api.ndb->a_cd[i] >> 8) & 0xFF; + } + + /* actually issue the signal */ + if (l1s_cb) + l1s_cb(sig); + + /* place it in the queue for the layer2 */ + msg = l1_create_l2_msg(CCCH_INFO_IND, l1s.current_time.fn-4, last_fb->snr); + dl = (struct l1_info_dl *) msg->data; + l1 = (struct l1_ccch_info_ind *) msgb_put(msg, sizeof(*l1)); + + /* copy the snr and data */ + for (i = 0; i < 3; ++i) + dl->snr[i] = sig->nb.meas[i].snr; + for (i = 0; i < 23; ++i) + l1->data[i] = sig->nb.frame[i]; + l1_queue_for_l2(msg); + + /* clear downlink task */ + l1s.task = dsp_api.db_w->d_task_d = 0; + + l1s_sb_test(4); + } + + /* mark READ page as being used */ + dsp_api.r_page_used = 1; + + return 0; +} + +static int l1s_nb_cmd(uint16_t p1, uint16_t burst_id) +{ + uint8_t tsc = l1s.serving_cell.bsic & 0x7; + + putchart('N'); + dsp_load_rx_task(ALLC_DSP_TASK, burst_id, tsc); + dsp_end_scenario(); + + l1s_rx_win_ctrl(rf_arfcn, L1_RXWIN_NB); + tpu_end_scenario(); + + return 0; +} + +static const struct tdma_sched_item nb_sched_set[] = { + SCHED_ITEM(l1s_nb_cmd, 0, 0), SCHED_END_FRAME(), + SCHED_ITEM(l1s_nb_cmd, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM(l1s_nb_resp, 0, 0), SCHED_ITEM(l1s_nb_cmd, 0, 2), SCHED_END_FRAME(), + SCHED_ITEM(l1s_nb_resp, 0, 1), SCHED_ITEM(l1s_nb_cmd, 0, 3), SCHED_END_FRAME(), + SCHED_ITEM(l1s_nb_resp, 0, 2), SCHED_END_FRAME(), + SCHED_ITEM(l1s_nb_resp, 0, 3), SCHED_END_FRAME(), +}; + +void l1s_nb_test(uint8_t base_fn) +{ + puts("Starting NB\n"); + tdma_schedule_set(base_fn, nb_sched_set, ARRAY_SIZE(nb_sched_set)); +} + +/* Interrupt handler */ +static void frame_irq(enum irq_nr nr) +{ + l1_sync(); +} + +void l1s_init(void) +{ + /* register FRAME interrupt as FIQ so it can interrupt normal IRQs */ + irq_register_handler(IRQ_TPU_FRAME, &frame_irq); + irq_config(IRQ_TPU_FRAME, 1, 1, 0); + irq_enable(IRQ_TPU_FRAME); + + /* configure timer 1 to be auto-reload and have a prescale of 12 (13MHz/12 == qbit clock) */ + hwtimer_enable(1, 1); + hwtimer_load(1, (1875*4)-1); + hwtimer_config(1, 0, 1); + hwtimer_enable(1, 1); +} + diff --git a/src/target/firmware/layer1/tdma_sched.c b/src/target/firmware/layer1/tdma_sched.c new file mode 100644 index 00000000..097e9a42 --- /dev/null +++ b/src/target/firmware/layer1/tdma_sched.c @@ -0,0 +1,163 @@ +/* TDMA Scheduler Implementation */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include +#include + +#include +#include + +#include +#include + +#include + +static uint8_t wrap_bucket(uint8_t offset) +{ + uint16_t bucket; + + bucket = (l1s.tdma_sched.cur_bucket + offset) + % ARRAY_SIZE(l1s.tdma_sched.bucket); + + return bucket; +} + +/* Schedule an item at 'frame_offset' TDMA frames in the future */ +int tdma_schedule(uint8_t frame_offset, tdma_sched_cb *cb, uint16_t p1, uint16_t p2) +{ + struct tdma_scheduler *sched = &l1s.tdma_sched; + uint8_t bucket_nr = wrap_bucket(frame_offset); + struct tdma_sched_bucket *bucket = &sched->bucket[bucket_nr]; + struct tdma_sched_item *sched_item; + + if (bucket->num_items >= ARRAY_SIZE(bucket->item)) { + puts("tdma_schedule bucket overflow\n"); + return -1; + } + + sched_item = &bucket->item[bucket->num_items++]; + + sched_item->cb = cb; + sched_item->p1 = p1; + sched_item->p2 = p2; + + return 0; +} + +/* Schedule a set of items starting from 'frame_offset' TDMA frames in the future */ +int tdma_schedule_set(uint8_t frame_offset, const struct tdma_sched_item *item_set, + uint8_t num_items) +{ + struct tdma_scheduler *sched = &l1s.tdma_sched; + uint8_t bucket_nr = wrap_bucket(frame_offset); + int i; + + for (i = 0; i < num_items; i++) { + const struct tdma_sched_item *sched_item = &item_set[i]; + struct tdma_sched_bucket *bucket = &sched->bucket[bucket_nr]; + + if (sched_item->cb == NULL) { + /* advance to next bucket (== TDMA frame) */ + bucket_nr = wrap_bucket(++frame_offset); + continue; + } + /* check for bucket overflow */ + if (bucket->num_items >= ARRAY_SIZE(bucket->item)) { + puts("tdma_schedule bucket overflow\n"); + return -1; + } + /* copy the item from the set into the current bucket item position */ + memcpy(&bucket->item[bucket->num_items++], sched_item, sizeof(*sched_item)); + } + + return num_items; +} + +/* Execute pre-scheduled events for current frame */ +int tdma_sched_execute(void) +{ + struct tdma_scheduler *sched = &l1s.tdma_sched; + struct tdma_sched_bucket *bucket; + uint8_t next_bucket; + int i, num_events = 0; + + /* determine current bucket */ + bucket = &sched->bucket[sched->cur_bucket]; + + /* iterate over items in this bucket and call callback function */ + for (i = 0; i < bucket->num_items; i++) { + struct tdma_sched_item *item = &bucket->item[i]; + int rc; + + num_events++; + + rc = item->cb(item->p1, item->p2); + if (rc < 0) { + printf("Error %d during processing of item %u of bucket %u\n", + rc, i, sched->cur_bucket); + return rc; + } + /* if the cb() we just called has scheduled more items for the + * current TDMA, bucket->num_items will have increased and we + * will simply continue to execute them as intended */ + } + + /* clear/reset the bucket */ + bucket->num_items = 0; + + /* advance to the next bucket */ + next_bucket = wrap_bucket(1); + sched->cur_bucket = next_bucket; + + /* return number of items that we called */ + return num_events; +} + +void tdma_sched_reset(void) +{ + struct tdma_scheduler *sched = &l1s.tdma_sched; + unsigned int bucket_nr; + + for (bucket_nr = 0; bucket_nr < ARRAY_SIZE(sched->bucket); bucket_nr++) { + struct tdma_sched_bucket *bucket = &sched->bucket[bucket_nr]; + /* current bucket will be reset by iteration code above! */ + if (bucket_nr != sched->cur_bucket) + bucket->num_items = 0; + } + + /* Don't reset cur_bucket, as it would upset the bucket iteration code + * in tdma_sched_execute() */ +} + +void tdma_sched_dump(void) +{ + unsigned int i; + + printf("\n(%2u)", l1s.tdma_sched.cur_bucket); + for (i = 0; i < ARRAY_SIZE(l1s.tdma_sched.bucket); i++) { + int bucket_nr = wrap_bucket(i); + struct tdma_sched_bucket *bucket = &l1s.tdma_sched.bucket[bucket_nr]; + printf("%u:", bucket->num_items); + } + putchar('\n'); +} diff --git a/src/target/firmware/layer1/tpu_window.c b/src/target/firmware/layer1/tpu_window.c new file mode 100644 index 00000000..dc9fc34a --- /dev/null +++ b/src/target/firmware/layer1/tpu_window.c @@ -0,0 +1,94 @@ +/* TPU window control routines for Layer 1 */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include +#include + +#include +#include +#include +#include +#include + +#include + +/* all units in GSM quarter-bits (923.1ns) */ +#define L1_TDMA_LENGTH_Q 5000 +#define L1_BURST_LENGTH_Q 625 /* L1_TDMA_LENGTH_Q/8 */ + +#define L1_NB_MARGIN_Q (3 * 4) +#define L1_SB_MARGIN_Q (23 * 4) +#define L1_TAIL_DURATION_Q (3 * 4) + +/* Sample length as required by the Calypso DSP */ +#define L1_NB_DURATION_Q (L1_BURST_LENGTH_Q + 2 * L1_NB_MARGIN_Q - L1_TAIL_DURATION_Q) +#define L1_SB_DURATION_Q (L1_BURST_LENGTH_Q + 2 * L1_SB_MARGIN_Q - L1_TAIL_DURATION_Q) +#define L1_FB_DURATION_Q (11 * L1_TDMA_LENGTH_Q + 2057) /* more than 11 full slots */ +#define L1_FB26_DURATION_Q (L1_TDMA_LENGTH_Q + 798) +#define L1_PW_DURATION_Q 289 + +#define DSP_SETUP_TIME 66 + +static const uint16_t rx_burst_duration[_NUM_L1_RXWIN] = { + [L1_RXWIN_PW] = L1_PW_DURATION_Q, + [L1_RXWIN_FB] = L1_FB_DURATION_Q, + [L1_RXWIN_SB] = L1_SB_DURATION_Q, + [L1_RXWIN_NB] = L1_NB_DURATION_Q, +}; + +void l1s_rx_win_ctrl(uint16_t arfcn, enum l1_rxwin_type wtype) +{ + int16_t start = DSP_SETUP_TIME; + int16_t stop = start + rx_burst_duration[wtype] - 1; + + /* FIXME: AGC */ + /* FIXME: RF PLL */ + + /* window open for TRF6151 */ + /* FIXME: why do we need the magic value 100 ? */ + rffe_mode(gsm_arfcn2band(arfcn), 0); + trf6151_rx_window(start - 100, arfcn, 40, 0); + + /* Window open for ABB */ + twl3025_downlink(1, start); + + /* Delay 11 full TDMA frames */ + if (wtype == L1_RXWIN_FB) { + uint8_t i; + for (i = 0; i < 11; i++) + tpu_enq_at(0); + + stop -= 11 * L1_TDMA_LENGTH_Q; + } + + /* Window close for ABB */ + twl3025_downlink(0, stop); + + /* FIXME: window close for TRF6151 */ +} + +void tpu_end_scenario(void) +{ + tpu_enq_sleep(); + tpu_enable(1); +} diff --git a/src/target/firmware/lib/Makefile b/src/target/firmware/lib/Makefile new file mode 100644 index 00000000..4056e91b --- /dev/null +++ b/src/target/firmware/lib/Makefile @@ -0,0 +1,26 @@ +INCLUDES=-I../include/ +-include ../Makefile.inc + +LIBNAME=mini +CSRCS=vsprintf.c string.c ctype.c printf.c console.c +SSRCS=changebit.S clearbit.S div64.S lib1funcs.S memcpy.S memset.S setbit.S testchangebit.S testclearbit.S testsetbit.S + +COBJS=$(CSRCS:.c=.o) +SOBJS=$(SSRCS:.S=.o) +OBJS=$(COBJS) $(SOBJS) + +LST=$(OBJS:.o=.lst) + +all: lib$(LIBNAME).a + +$(COBJS): %.o : %.c + $(CROSS_COMPILE)$(CC) $(CFLAGS) -c -o $@ $^ + +$(SOBJS): %.o : %.S + $(CROSS_COMPILE)$(CC) $(ASFLAGS) -c -o $@ $^ + +lib$(LIBNAME).a: $(OBJS) + $(CROSS_COMPILE)$(AR) cru $@ $^ + +clean: + rm -f *.a $(OBJS) $(LST) diff --git a/src/target/firmware/lib/bitops.h b/src/target/firmware/lib/bitops.h new file mode 100644 index 00000000..428c9a63 --- /dev/null +++ b/src/target/firmware/lib/bitops.h @@ -0,0 +1,33 @@ + .macro bitop, instr + and r2, r0, #7 + mov r3, #1 + mov r3, r3, lsl r2 + save_and_disable_irqs ip + ldrb r2, [r1, r0, lsr #3] + \instr r2, r2, r3 + strb r2, [r1, r0, lsr #3] + restore_irqs ip + mov pc, lr + .endm + +/** + * testop - implement a test_and_xxx_bit operation. + * @instr: operational instruction + * @store: store instruction + * + * Note: we can trivially conditionalise the store instruction + * to avoid dirting the data cache. + */ + .macro testop, instr, store + add r1, r1, r0, lsr #3 + and r3, r0, #7 + mov r0, #1 + save_and_disable_irqs ip + ldrb r2, [r1] + tst r2, r0, lsl r3 + \instr r2, r2, r0, lsl r3 + \store r2, [r1] + restore_irqs ip + moveq r0, #0 + mov pc, lr + .endm diff --git a/src/target/firmware/lib/changebit.S b/src/target/firmware/lib/changebit.S new file mode 100644 index 00000000..7c709fb3 --- /dev/null +++ b/src/target/firmware/lib/changebit.S @@ -0,0 +1,21 @@ +/* + * linux/arch/arm/lib/changebit.S + * + * Copyright (C) 1995-1996 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include "bitops.h" + .text + +/* Purpose : Function to change a bit + * Prototype: int change_bit(int bit, void *addr) + */ +ENTRY(_change_bit_be) + eor r0, r0, #0x18 @ big endian byte ordering +ENTRY(_change_bit_le) + bitop eor diff --git a/src/target/firmware/lib/clearbit.S b/src/target/firmware/lib/clearbit.S new file mode 100644 index 00000000..cb48f7ac --- /dev/null +++ b/src/target/firmware/lib/clearbit.S @@ -0,0 +1,22 @@ +/* + * linux/arch/arm/lib/clearbit.S + * + * Copyright (C) 1995-1996 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include "bitops.h" + .text + +/* + * Purpose : Function to clear a bit + * Prototype: int clear_bit(int bit, void *addr) + */ +ENTRY(_clear_bit_be) + eor r0, r0, #0x18 @ big endian byte ordering +ENTRY(_clear_bit_le) + bitop bic diff --git a/src/target/firmware/lib/console.c b/src/target/firmware/lib/console.c new file mode 100644 index 00000000..f7d0226e --- /dev/null +++ b/src/target/firmware/lib/console.c @@ -0,0 +1,202 @@ +/* Ringbuffer based serial console layer, imported from OpenPCD */ + +/* (C) 2006-2010 by Harald Welte + * + * 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 +#include +#include +#include + +#include + +struct cons { + char buf[CONS_RB_SIZE]; + char *next_inbyte; + char *next_outbyte; + int initialized; +}; +static struct cons cons; + +void cons_init(void) +{ + memset(cons.buf, 0, sizeof(cons.buf)); + cons.next_inbyte = &cons.buf[0]; + cons.next_outbyte = &cons.buf[0]; + cons.initialized = 1; +} + +/* determine how many bytes are left in the ringbuffer without overwriting + bytes that haven't been written to the console yet */ +static int __cons_rb_space(void) +{ + if (cons.next_inbyte == cons.next_outbyte) + return sizeof(cons.buf)-1; + else if (cons.next_outbyte > cons.next_inbyte) + return (cons.next_outbyte - cons.next_inbyte) -1; + else + return sizeof(cons.buf) - 1 - (cons.next_inbyte - cons.next_outbyte); +} + +/* pull one char out of debug ring buffer */ +static int cons_rb_pull(char *ret) +{ + unsigned long flags; + + local_irq_save(flags); + + if (cons.next_outbyte == cons.next_inbyte) { + local_irq_restore(flags); + return -1; + } + + *ret = *cons.next_outbyte; + + cons.next_outbyte++; + if (cons.next_outbyte >= &cons.buf[0]+sizeof(cons.buf)) { + cons.next_outbyte = &cons.buf[0]; + } +#if 0 + else if (cons.next_outbyte > &cons.buf[0]+sizeof(cons.buf)) { + cons.next_outbyte -= sizeof(cons.buf); + } +#endif + + local_irq_restore(flags); + + return 0; +} + +/* returns if everything was flushed (1) or if there's more to flush (0) */ +static void __rb_flush_wait(void) +{ + char ch; + while (cons_rb_pull(&ch) >= 0) + uart_putchar_wait(CONS_UART_NR, ch); +} + +/* returns if everything was flushed (1) or if there's more to flush (0) */ +static int __rb_flush(void) +{ + while (!uart_tx_busy(CONS_UART_NR)) { + char ch; + if (cons_rb_pull(&ch) < 0) { + /* no more data to write, disable interest in Tx FIFO interrupts */ + return 1; + } + uart_putchar_nb(CONS_UART_NR, ch); + } + + /* if we reach here, UART Tx FIFO is busy again */ + return 0; +} + +/* flush pending data from debug ring buffer to serial port */ +int cons_rb_flush(void) +{ + return __rb_flush(); +} + +static void cons_memcpy(char *pos, const char *data, int len) +{ +#if 0 + /* Somehow our memcpy is broken !?! */ + memcpy(pos, data, len); +#else + int i; + for (i = 0; i < len; i++) + *pos++ = *data++; +#endif +} + +/* Append bytes to ring buffer, not more than we have left! */ +static void __cons_rb_append(const char *data, int len) +{ + if (cons.next_inbyte + len >= &cons.buf[0]+sizeof(cons.buf)) { + int before_tail = (&cons.buf[0]+sizeof(cons.buf)) - cons.next_inbyte; + /* copy the first part before we wrap */ + cons_memcpy(cons.next_inbyte, data, before_tail); + data += before_tail; + len -= before_tail; + /* reset the buffer */ + cons.next_inbyte = &cons.buf[0]; + } + cons_memcpy(cons.next_inbyte, data, len); + cons.next_inbyte += len; +} + +/* append bytes to the ringbuffer, do one wrap */ +int cons_rb_append(const char *data, int len) +{ + unsigned long flags; + int bytes_left; + const char *data_cur; + + /* we will never be able to write more than the console buffer */ + if (len > (int) sizeof(cons.buf)) + len = sizeof(cons.buf); + + local_irq_save(flags); + + bytes_left = __cons_rb_space(); + data_cur = data; + + if (len > bytes_left) { + /* append what we can */ + __cons_rb_append(data_cur, bytes_left); + /* busy-wait for all characters to be transmitted */ + __rb_flush_wait(); + /* fill it with the remaining bytes */ + len -= bytes_left; + data_cur += bytes_left; + } + __cons_rb_append(data_cur, len); + + /* we want to get Tx FIFO interrupts */ + uart_irq_enable(CONS_UART_NR, UART_IRQ_TX_EMPTY, 1); + + local_irq_restore(flags); + + return len; +} + +int cons_puts(const char *s) +{ + if (cons.initialized) { + return cons_rb_append(s, strlen(s)); + } else { + /* if the console is not active yet, we need to fall back */ + int i = strlen(s); + while (i--) + uart_putchar_wait(CONS_UART_NR, *s++); + return i; + } +} + +int cons_putchar(char c) +{ + if (cons.initialized) + return cons_rb_append(&c, 1); + else { + /* if the console is not active yet, we need to fall back */ + uart_putchar_wait(CONS_UART_NR, c); + return 0; + } +} diff --git a/src/target/firmware/lib/copy_template.S b/src/target/firmware/lib/copy_template.S new file mode 100644 index 00000000..cab355c0 --- /dev/null +++ b/src/target/firmware/lib/copy_template.S @@ -0,0 +1,255 @@ +/* + * linux/arch/arm/lib/copy_template.s + * + * Code template for optimized memory copy functions + * + * Author: Nicolas Pitre + * Created: Sep 28, 2005 + * Copyright: MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * This can be used to enable code to cacheline align the source pointer. + * Experiments on tested architectures (StrongARM and XScale) didn't show + * this a worthwhile thing to do. That might be different in the future. + */ +//#define CALGN(code...) code +#define CALGN(code...) + +/* + * Theory of operation + * ------------------- + * + * This file provides the core code for a forward memory copy used in + * the implementation of memcopy(), copy_to_user() and copy_from_user(). + * + * The including file must define the following accessor macros + * according to the need of the given function: + * + * ldr1w ptr reg abort + * + * This loads one word from 'ptr', stores it in 'reg' and increments + * 'ptr' to the next word. The 'abort' argument is used for fixup tables. + * + * ldr4w ptr reg1 reg2 reg3 reg4 abort + * ldr8w ptr, reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + * + * This loads four or eight words starting from 'ptr', stores them + * in provided registers and increments 'ptr' past those words. + * The'abort' argument is used for fixup tables. + * + * ldr1b ptr reg cond abort + * + * Similar to ldr1w, but it loads a byte and increments 'ptr' one byte. + * It also must apply the condition code if provided, otherwise the + * "al" condition is assumed by default. + * + * str1w ptr reg abort + * str8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + * str1b ptr reg cond abort + * + * Same as their ldr* counterparts, but data is stored to 'ptr' location + * rather than being loaded. + * + * enter reg1 reg2 + * + * Preserve the provided registers on the stack plus any additional + * data as needed by the implementation including this code. Called + * upon code entry. + * + * exit reg1 reg2 + * + * Restore registers with the values previously saved with the + * 'preserv' macro. Called upon code termination. + */ + + + enter r4, lr + + subs r2, r2, #4 + blt 8f + ands ip, r0, #3 + PLD( pld [r1, #0] ) + bne 9f + ands ip, r1, #3 + bne 10f + +1: subs r2, r2, #(28) + stmfd sp!, {r5 - r8} + blt 5f + + CALGN( ands ip, r1, #31 ) + CALGN( rsb r3, ip, #32 ) + CALGN( sbcnes r4, r3, r2 ) @ C is always set here + CALGN( bcs 2f ) + CALGN( adr r4, 6f ) + CALGN( subs r2, r2, r3 ) @ C gets set + CALGN( add pc, r4, ip ) + + PLD( pld [r1, #0] ) +2: PLD( subs r2, r2, #96 ) + PLD( pld [r1, #28] ) + PLD( blt 4f ) + PLD( pld [r1, #60] ) + PLD( pld [r1, #92] ) + +3: PLD( pld [r1, #124] ) +4: ldr8w r1, r3, r4, r5, r6, r7, r8, ip, lr, abort=20f + subs r2, r2, #32 + str8w r0, r3, r4, r5, r6, r7, r8, ip, lr, abort=20f + bge 3b + PLD( cmn r2, #96 ) + PLD( bge 4b ) + +5: ands ip, r2, #28 + rsb ip, ip, #32 + addne pc, pc, ip @ C is always clear here + b 7f +6: nop + ldr1w r1, r3, abort=20f + ldr1w r1, r4, abort=20f + ldr1w r1, r5, abort=20f + ldr1w r1, r6, abort=20f + ldr1w r1, r7, abort=20f + ldr1w r1, r8, abort=20f + ldr1w r1, lr, abort=20f + + add pc, pc, ip + nop + nop + str1w r0, r3, abort=20f + str1w r0, r4, abort=20f + str1w r0, r5, abort=20f + str1w r0, r6, abort=20f + str1w r0, r7, abort=20f + str1w r0, r8, abort=20f + str1w r0, lr, abort=20f + + CALGN( bcs 2b ) + +7: ldmfd sp!, {r5 - r8} + +8: movs r2, r2, lsl #31 + ldr1b r1, r3, ne, abort=21f + ldr1b r1, r4, cs, abort=21f + ldr1b r1, ip, cs, abort=21f + str1b r0, r3, ne, abort=21f + str1b r0, r4, cs, abort=21f + str1b r0, ip, cs, abort=21f + + exit r4, pc + +9: rsb ip, ip, #4 + cmp ip, #2 + ldr1b r1, r3, gt, abort=21f + ldr1b r1, r4, ge, abort=21f + ldr1b r1, lr, abort=21f + str1b r0, r3, gt, abort=21f + str1b r0, r4, ge, abort=21f + subs r2, r2, ip + str1b r0, lr, abort=21f + blt 8b + ands ip, r1, #3 + beq 1b + +10: bic r1, r1, #3 + cmp ip, #2 + ldr1w r1, lr, abort=21f + beq 17f + bgt 18f + + + .macro forward_copy_shift pull push + + subs r2, r2, #28 + blt 14f + + CALGN( ands ip, r1, #31 ) + CALGN( rsb ip, ip, #32 ) + CALGN( sbcnes r4, ip, r2 ) @ C is always set here + CALGN( subcc r2, r2, ip ) + CALGN( bcc 15f ) + +11: stmfd sp!, {r5 - r9} + + PLD( pld [r1, #0] ) + PLD( subs r2, r2, #96 ) + PLD( pld [r1, #28] ) + PLD( blt 13f ) + PLD( pld [r1, #60] ) + PLD( pld [r1, #92] ) + +12: PLD( pld [r1, #124] ) +13: ldr4w r1, r4, r5, r6, r7, abort=19f + mov r3, lr, pull #\pull + subs r2, r2, #32 + ldr4w r1, r8, r9, ip, lr, abort=19f + orr r3, r3, r4, push #\push + mov r4, r4, pull #\pull + orr r4, r4, r5, push #\push + mov r5, r5, pull #\pull + orr r5, r5, r6, push #\push + mov r6, r6, pull #\pull + orr r6, r6, r7, push #\push + mov r7, r7, pull #\pull + orr r7, r7, r8, push #\push + mov r8, r8, pull #\pull + orr r8, r8, r9, push #\push + mov r9, r9, pull #\pull + orr r9, r9, ip, push #\push + mov ip, ip, pull #\pull + orr ip, ip, lr, push #\push + str8w r0, r3, r4, r5, r6, r7, r8, r9, ip, , abort=19f + bge 12b + PLD( cmn r2, #96 ) + PLD( bge 13b ) + + ldmfd sp!, {r5 - r9} + +14: ands ip, r2, #28 + beq 16f + +15: mov r3, lr, pull #\pull + ldr1w r1, lr, abort=21f + subs ip, ip, #4 + orr r3, r3, lr, push #\push + str1w r0, r3, abort=21f + bgt 15b + CALGN( cmp r2, #0 ) + CALGN( bge 11b ) + +16: sub r1, r1, #(\push / 8) + b 8b + + .endm + + + forward_copy_shift pull=8 push=24 + +17: forward_copy_shift pull=16 push=16 + +18: forward_copy_shift pull=24 push=8 + + +/* + * Abort preamble and completion macros. + * If a fixup handler is required then those macros must surround it. + * It is assumed that the fixup code will handle the private part of + * the exit macro. + */ + + .macro copy_abort_preamble +19: ldmfd sp!, {r5 - r9} + b 21f +20: ldmfd sp!, {r5 - r8} +21: + .endm + + .macro copy_abort_end + ldmfd sp!, {r4, pc} + .endm + diff --git a/src/target/firmware/lib/ctype.c b/src/target/firmware/lib/ctype.c new file mode 100644 index 00000000..6ec51cc2 --- /dev/null +++ b/src/target/firmware/lib/ctype.c @@ -0,0 +1,34 @@ +/* + * linux/lib/ctype.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#include + +unsigned char _ctype[] = { +_C,_C,_C,_C,_C,_C,_C,_C, /* 0-7 */ +_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C, /* 8-15 */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 16-23 */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 24-31 */ +_S|_SP,_P,_P,_P,_P,_P,_P,_P, /* 32-39 */ +_P,_P,_P,_P,_P,_P,_P,_P, /* 40-47 */ +_D,_D,_D,_D,_D,_D,_D,_D, /* 48-55 */ +_D,_D,_P,_P,_P,_P,_P,_P, /* 56-63 */ +_P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U, /* 64-71 */ +_U,_U,_U,_U,_U,_U,_U,_U, /* 72-79 */ +_U,_U,_U,_U,_U,_U,_U,_U, /* 80-87 */ +_U,_U,_U,_P,_P,_P,_P,_P, /* 88-95 */ +_P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L, /* 96-103 */ +_L,_L,_L,_L,_L,_L,_L,_L, /* 104-111 */ +_L,_L,_L,_L,_L,_L,_L,_L, /* 112-119 */ +_L,_L,_L,_P,_P,_P,_P,_C, /* 120-127 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 128-143 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 144-159 */ +_S|_SP,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 160-175 */ +_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 176-191 */ +_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U, /* 192-207 */ +_U,_U,_U,_U,_U,_U,_U,_P,_U,_U,_U,_U,_U,_U,_U,_L, /* 208-223 */ +_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L, /* 224-239 */ +_L,_L,_L,_L,_L,_L,_L,_P,_L,_L,_L,_L,_L,_L,_L,_L}; /* 240-255 */ + diff --git a/src/target/firmware/lib/div64.S b/src/target/firmware/lib/div64.S new file mode 100644 index 00000000..7eeef50c --- /dev/null +++ b/src/target/firmware/lib/div64.S @@ -0,0 +1,200 @@ +/* + * linux/arch/arm/lib/div64.S + * + * Optimized computation of 64-bit dividend / 32-bit divisor + * + * Author: Nicolas Pitre + * Created: Oct 5, 2003 + * Copyright: Monta Vista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include + +#ifdef __ARMEB__ +#define xh r0 +#define xl r1 +#define yh r2 +#define yl r3 +#else +#define xl r0 +#define xh r1 +#define yl r2 +#define yh r3 +#endif + +/* + * __do_div64: perform a division with 64-bit dividend and 32-bit divisor. + * + * Note: Calling convention is totally non standard for optimal code. + * This is meant to be used by do_div() from include/asm/div64.h only. + * + * Input parameters: + * xh-xl = dividend (clobbered) + * r4 = divisor (preserved) + * + * Output values: + * yh-yl = result + * xh = remainder + * + * Clobbered regs: xl, ip + */ + +ENTRY(__do_div64) + + @ Test for easy paths first. + subs ip, r4, #1 + bls 9f @ divisor is 0 or 1 + tst ip, r4 + beq 8f @ divisor is power of 2 + + @ See if we need to handle upper 32-bit result. + cmp xh, r4 + mov yh, #0 + blo 3f + + @ Align divisor with upper part of dividend. + @ The aligned divisor is stored in yl preserving the original. + @ The bit position is stored in ip. + +#if __LINUX_ARM_ARCH__ >= 5 + + clz yl, r4 + clz ip, xh + sub yl, yl, ip + mov ip, #1 + mov ip, ip, lsl yl + mov yl, r4, lsl yl + +#else + + mov yl, r4 + mov ip, #1 +1: cmp yl, #0x80000000 + cmpcc yl, xh + movcc yl, yl, lsl #1 + movcc ip, ip, lsl #1 + bcc 1b + +#endif + + @ The division loop for needed upper bit positions. + @ Break out early if dividend reaches 0. +2: cmp xh, yl + orrcs yh, yh, ip + subcss xh, xh, yl + movnes ip, ip, lsr #1 + mov yl, yl, lsr #1 + bne 2b + + @ See if we need to handle lower 32-bit result. +3: cmp xh, #0 + mov yl, #0 + cmpeq xl, r4 + movlo xh, xl + movlo pc, lr + + @ The division loop for lower bit positions. + @ Here we shift remainer bits leftwards rather than moving the + @ divisor for comparisons, considering the carry-out bit as well. + mov ip, #0x80000000 +4: movs xl, xl, lsl #1 + adcs xh, xh, xh + beq 6f + cmpcc xh, r4 +5: orrcs yl, yl, ip + subcs xh, xh, r4 + movs ip, ip, lsr #1 + bne 4b + mov pc, lr + + @ The top part of remainder became zero. If carry is set + @ (the 33th bit) this is a false positive so resume the loop. + @ Otherwise, if lower part is also null then we are done. +6: bcs 5b + cmp xl, #0 + moveq pc, lr + + @ We still have remainer bits in the low part. Bring them up. + +#if __LINUX_ARM_ARCH__ >= 5 + + clz xh, xl @ we know xh is zero here so... + add xh, xh, #1 + mov xl, xl, lsl xh + mov ip, ip, lsr xh + +#else + +7: movs xl, xl, lsl #1 + mov ip, ip, lsr #1 + bcc 7b + +#endif + + @ Current remainder is now 1. It is worthless to compare with + @ divisor at this point since divisor can not be smaller than 3 here. + @ If possible, branch for another shift in the division loop. + @ If no bit position left then we are done. + movs ip, ip, lsr #1 + mov xh, #1 + bne 4b + mov pc, lr + +8: @ Division by a power of 2: determine what that divisor order is + @ then simply shift values around + +#if __LINUX_ARM_ARCH__ >= 5 + + clz ip, r4 + rsb ip, ip, #31 + +#else + + mov yl, r4 + cmp r4, #(1 << 16) + mov ip, #0 + movhs yl, yl, lsr #16 + movhs ip, #16 + + cmp yl, #(1 << 8) + movhs yl, yl, lsr #8 + addhs ip, ip, #8 + + cmp yl, #(1 << 4) + movhs yl, yl, lsr #4 + addhs ip, ip, #4 + + cmp yl, #(1 << 2) + addhi ip, ip, #3 + addls ip, ip, yl, lsr #1 + +#endif + + mov yh, xh, lsr ip + mov yl, xl, lsr ip + rsb ip, ip, #32 + orr yl, yl, xh, lsl ip + mov xh, xl, lsl ip + mov xh, xh, lsr ip + mov pc, lr + + @ eq -> division by 1: obvious enough... +9: moveq yl, xl + moveq yh, xh + moveq xh, #0 + moveq pc, lr + + @ Division by 0: + str lr, [sp, #-8]! + bl __div0 + + @ as wrong as it could be... + mov yl, #0 + mov yh, #0 + mov xh, #0 + ldr pc, [sp], #8 + diff --git a/src/target/firmware/lib/lib1funcs.S b/src/target/firmware/lib/lib1funcs.S new file mode 100644 index 00000000..b02a85eb --- /dev/null +++ b/src/target/firmware/lib/lib1funcs.S @@ -0,0 +1,334 @@ +/* + * linux/arch/arm/lib/lib1funcs.S: Optimized ARM division routines + * + * Author: Nicolas Pitre + * - contributed to gcc-3.4 on Sep 30, 2003 + * - adapted for the Linux kernel on Oct 2, 2003 + */ + +/* Copyright 1995, 1996, 1998, 1999, 2000, 2003 Free Software Foundation, Inc. + +This file 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, or (at your option) any +later version. + +In addition to the permissions in the GNU General Public License, the +Free Software Foundation gives you unlimited permission to link the +compiled version of this file into combinations with other programs, +and to distribute those combinations without any restriction coming +from the use of this file. (The General Public License restrictions +do apply in other respects; for example, they cover modification of +the file, and distribution when not linked into a combine +executable.) + +This file 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; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + + +#include +#include + + +.macro ARM_DIV_BODY dividend, divisor, result, curbit + +#if __LINUX_ARM_ARCH__ >= 5 + + clz \curbit, \divisor + clz \result, \dividend + sub \result, \curbit, \result + mov \curbit, #1 + mov \divisor, \divisor, lsl \result + mov \curbit, \curbit, lsl \result + mov \result, #0 + +#else + + @ Initially shift the divisor left 3 bits if possible, + @ set curbit accordingly. This allows for curbit to be located + @ at the left end of each 4 bit nibbles in the division loop + @ to save one loop in most cases. + tst \divisor, #0xe0000000 + moveq \divisor, \divisor, lsl #3 + moveq \curbit, #8 + movne \curbit, #1 + + @ Unless the divisor is very big, shift it up in multiples of + @ four bits, since this is the amount of unwinding in the main + @ division loop. Continue shifting until the divisor is + @ larger than the dividend. +1: cmp \divisor, #0x10000000 + cmplo \divisor, \dividend + movlo \divisor, \divisor, lsl #4 + movlo \curbit, \curbit, lsl #4 + blo 1b + + @ For very big divisors, we must shift it a bit at a time, or + @ we will be in danger of overflowing. +1: cmp \divisor, #0x80000000 + cmplo \divisor, \dividend + movlo \divisor, \divisor, lsl #1 + movlo \curbit, \curbit, lsl #1 + blo 1b + + mov \result, #0 + +#endif + + @ Division loop +1: cmp \dividend, \divisor + subhs \dividend, \dividend, \divisor + orrhs \result, \result, \curbit + cmp \dividend, \divisor, lsr #1 + subhs \dividend, \dividend, \divisor, lsr #1 + orrhs \result, \result, \curbit, lsr #1 + cmp \dividend, \divisor, lsr #2 + subhs \dividend, \dividend, \divisor, lsr #2 + orrhs \result, \result, \curbit, lsr #2 + cmp \dividend, \divisor, lsr #3 + subhs \dividend, \dividend, \divisor, lsr #3 + orrhs \result, \result, \curbit, lsr #3 + cmp \dividend, #0 @ Early termination? + movnes \curbit, \curbit, lsr #4 @ No, any more bits to do? + movne \divisor, \divisor, lsr #4 + bne 1b + +.endm + + +.macro ARM_DIV2_ORDER divisor, order + +#if __LINUX_ARM_ARCH__ >= 5 + + clz \order, \divisor + rsb \order, \order, #31 + +#else + + cmp \divisor, #(1 << 16) + movhs \divisor, \divisor, lsr #16 + movhs \order, #16 + movlo \order, #0 + + cmp \divisor, #(1 << 8) + movhs \divisor, \divisor, lsr #8 + addhs \order, \order, #8 + + cmp \divisor, #(1 << 4) + movhs \divisor, \divisor, lsr #4 + addhs \order, \order, #4 + + cmp \divisor, #(1 << 2) + addhi \order, \order, #3 + addls \order, \order, \divisor, lsr #1 + +#endif + +.endm + + +.macro ARM_MOD_BODY dividend, divisor, order, spare + +#if __LINUX_ARM_ARCH__ >= 5 + + clz \order, \divisor + clz \spare, \dividend + sub \order, \order, \spare + mov \divisor, \divisor, lsl \order + +#else + + mov \order, #0 + + @ Unless the divisor is very big, shift it up in multiples of + @ four bits, since this is the amount of unwinding in the main + @ division loop. Continue shifting until the divisor is + @ larger than the dividend. +1: cmp \divisor, #0x10000000 + cmplo \divisor, \dividend + movlo \divisor, \divisor, lsl #4 + addlo \order, \order, #4 + blo 1b + + @ For very big divisors, we must shift it a bit at a time, or + @ we will be in danger of overflowing. +1: cmp \divisor, #0x80000000 + cmplo \divisor, \dividend + movlo \divisor, \divisor, lsl #1 + addlo \order, \order, #1 + blo 1b + +#endif + + @ Perform all needed substractions to keep only the reminder. + @ Do comparisons in batch of 4 first. + subs \order, \order, #3 @ yes, 3 is intended here + blt 2f + +1: cmp \dividend, \divisor + subhs \dividend, \dividend, \divisor + cmp \dividend, \divisor, lsr #1 + subhs \dividend, \dividend, \divisor, lsr #1 + cmp \dividend, \divisor, lsr #2 + subhs \dividend, \dividend, \divisor, lsr #2 + cmp \dividend, \divisor, lsr #3 + subhs \dividend, \dividend, \divisor, lsr #3 + cmp \dividend, #1 + mov \divisor, \divisor, lsr #4 + subges \order, \order, #4 + bge 1b + + tst \order, #3 + teqne \dividend, #0 + beq 5f + + @ Either 1, 2 or 3 comparison/substractions are left. +2: cmn \order, #2 + blt 4f + beq 3f + cmp \dividend, \divisor + subhs \dividend, \dividend, \divisor + mov \divisor, \divisor, lsr #1 +3: cmp \dividend, \divisor + subhs \dividend, \dividend, \divisor + mov \divisor, \divisor, lsr #1 +4: cmp \dividend, \divisor + subhs \dividend, \dividend, \divisor +5: +.endm + + +ENTRY(__udivsi3) +ENTRY(__aeabi_uidiv) + + subs r2, r1, #1 + moveq pc, lr + bcc Ldiv0 + cmp r0, r1 + bls 11f + tst r1, r2 + beq 12f + + ARM_DIV_BODY r0, r1, r2, r3 + + mov r0, r2 + mov pc, lr + +11: moveq r0, #1 + movne r0, #0 + mov pc, lr + +12: ARM_DIV2_ORDER r1, r2 + + mov r0, r0, lsr r2 + mov pc, lr + + +ENTRY(__umodsi3) + + subs r2, r1, #1 @ compare divisor with 1 + bcc Ldiv0 + cmpne r0, r1 @ compare dividend with divisor + moveq r0, #0 + tsthi r1, r2 @ see if divisor is power of 2 + andeq r0, r0, r2 + movls pc, lr + + ARM_MOD_BODY r0, r1, r2, r3 + + mov pc, lr + + +ENTRY(__divsi3) +ENTRY(__aeabi_idiv) + + cmp r1, #0 + eor ip, r0, r1 @ save the sign of the result. + beq Ldiv0 + rsbmi r1, r1, #0 @ loops below use unsigned. + subs r2, r1, #1 @ division by 1 or -1 ? + beq 10f + movs r3, r0 + rsbmi r3, r0, #0 @ positive dividend value + cmp r3, r1 + bls 11f + tst r1, r2 @ divisor is power of 2 ? + beq 12f + + ARM_DIV_BODY r3, r1, r0, r2 + + cmp ip, #0 + rsbmi r0, r0, #0 + mov pc, lr + +10: teq ip, r0 @ same sign ? + rsbmi r0, r0, #0 + mov pc, lr + +11: movlo r0, #0 + moveq r0, ip, asr #31 + orreq r0, r0, #1 + mov pc, lr + +12: ARM_DIV2_ORDER r1, r2 + + cmp ip, #0 + mov r0, r3, lsr r2 + rsbmi r0, r0, #0 + mov pc, lr + + +ENTRY(__modsi3) + + cmp r1, #0 + beq Ldiv0 + rsbmi r1, r1, #0 @ loops below use unsigned. + movs ip, r0 @ preserve sign of dividend + rsbmi r0, r0, #0 @ if negative make positive + subs r2, r1, #1 @ compare divisor with 1 + cmpne r0, r1 @ compare dividend with divisor + moveq r0, #0 + tsthi r1, r2 @ see if divisor is power of 2 + andeq r0, r0, r2 + bls 10f + + ARM_MOD_BODY r0, r1, r2, r3 + +10: cmp ip, #0 + rsbmi r0, r0, #0 + mov pc, lr + +ENTRY(__aeabi_uidivmod) + + stmfd sp!, {r0, r1, ip, lr} + bl __aeabi_uidiv + ldmfd sp!, {r1, r2, ip, lr} + mul r3, r0, r2 + sub r1, r1, r3 + mov pc, lr + +ENTRY(__aeabi_idivmod) + + stmfd sp!, {r0, r1, ip, lr} + bl __aeabi_idiv + ldmfd sp!, {r1, r2, ip, lr} + mul r3, r0, r2 + sub r1, r1, r3 + mov pc, lr + +Ldiv0: + + str lr, [sp, #-8]! + bl __div0 + mov r0, #0 @ About as wrong as it could be. + ldr pc, [sp], #8 + +ENTRY(__div0) + mov pc, lr diff --git a/src/target/firmware/lib/memcpy.S b/src/target/firmware/lib/memcpy.S new file mode 100644 index 00000000..2bbd5692 --- /dev/null +++ b/src/target/firmware/lib/memcpy.S @@ -0,0 +1,59 @@ +/* + * linux/arch/arm/lib/memcpy.S + * + * Author: Nicolas Pitre + * Created: Sep 28, 2005 + * Copyright: MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + + .macro ldr1w ptr reg abort + ldr \reg, [\ptr], #4 + .endm + + .macro ldr4w ptr reg1 reg2 reg3 reg4 abort + ldmia \ptr!, {\reg1, \reg2, \reg3, \reg4} + .endm + + .macro ldr8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + ldmia \ptr!, {\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8} + .endm + + .macro ldr1b ptr reg cond=al abort + ldr\cond\()b \reg, [\ptr], #1 + .endm + + .macro str1w ptr reg abort + str \reg, [\ptr], #4 + .endm + + .macro str8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + stmia \ptr!, {\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8} + .endm + + .macro str1b ptr reg cond=al abort + str\cond\()b \reg, [\ptr], #1 + .endm + + .macro enter reg1 reg2 + stmdb sp!, {r0, \reg1, \reg2} + .endm + + .macro exit reg1 reg2 + ldmfd sp!, {r0, \reg1, \reg2} + .endm + + .text + +/* Prototype: void *memcpy(void *dest, const void *src, size_t n); */ + +ENTRY(memcpy) + +#include "copy_template.S" + diff --git a/src/target/firmware/lib/memset.S b/src/target/firmware/lib/memset.S new file mode 100644 index 00000000..04e254a8 --- /dev/null +++ b/src/target/firmware/lib/memset.S @@ -0,0 +1,80 @@ +/* + * linux/arch/arm/lib/memset.S + * + * Copyright (C) 1995-2000 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ASM optimised string functions + */ +#include +#include + + .text + .align 5 + .word 0 + +1: subs r2, r2, #4 @ 1 do we have enough + blt 5f @ 1 bytes to align with? + cmp r3, #2 @ 1 + strltb r1, [r0], #1 @ 1 + strleb r1, [r0], #1 @ 1 + strb r1, [r0], #1 @ 1 + add r2, r2, r3 @ 1 (r2 = r2 - (4 - r3)) +/* + * The pointer is now aligned and the length is adjusted. Try doing the + * memzero again. + */ + +ENTRY(memset) + ands r3, r0, #3 @ 1 unaligned? + bne 1b @ 1 +/* + * we know that the pointer in r0 is aligned to a word boundary. + */ + orr r1, r1, r1, lsl #8 + orr r1, r1, r1, lsl #16 + mov r3, r1 + cmp r2, #16 + blt 4f +/* + * We need an extra register for this loop - save the return address and + * use the LR + */ + str lr, [sp, #-4]! + mov ip, r1 + mov lr, r1 + +2: subs r2, r2, #64 + stmgeia r0!, {r1, r3, ip, lr} @ 64 bytes at a time. + stmgeia r0!, {r1, r3, ip, lr} + stmgeia r0!, {r1, r3, ip, lr} + stmgeia r0!, {r1, r3, ip, lr} + bgt 2b + LOADREGS(eqfd, sp!, {pc}) @ Now <64 bytes to go. +/* + * No need to correct the count; we're only testing bits from now on + */ + tst r2, #32 + stmneia r0!, {r1, r3, ip, lr} + stmneia r0!, {r1, r3, ip, lr} + tst r2, #16 + stmneia r0!, {r1, r3, ip, lr} + ldr lr, [sp], #4 + +4: tst r2, #8 + stmneia r0!, {r1, r3} + tst r2, #4 + strne r1, [r0], #4 +/* + * When we get here, we've got less than 4 bytes to zero. We + * may have an unaligned pointer as well. + */ +5: tst r2, #2 + strneb r1, [r0], #1 + strneb r1, [r0], #1 + tst r2, #1 + strneb r1, [r0], #1 + RETINSTR(mov,pc,lr) diff --git a/src/target/firmware/lib/printf.c b/src/target/firmware/lib/printf.c new file mode 100644 index 00000000..a4fc6876 --- /dev/null +++ b/src/target/firmware/lib/printf.c @@ -0,0 +1,19 @@ + +#include +#include + +static char printf_buffer[1024]; + +int printf(const char *fmt, ...) +{ + va_list args; + int r; + + va_start(args, fmt); + r = vsnprintf(printf_buffer, sizeof(printf_buffer), fmt, args); + va_end(args); + + puts(printf_buffer); + + return r; +} diff --git a/src/target/firmware/lib/setbit.S b/src/target/firmware/lib/setbit.S new file mode 100644 index 00000000..9009bc1e --- /dev/null +++ b/src/target/firmware/lib/setbit.S @@ -0,0 +1,22 @@ +/* + * linux/arch/arm/lib/setbit.S + * + * Copyright (C) 1995-1996 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include "bitops.h" + .text + +/* + * Purpose : Function to set a bit + * Prototype: int set_bit(int bit, void *addr) + */ +ENTRY(_set_bit_be) + eor r0, r0, #0x18 @ big endian byte ordering +ENTRY(_set_bit_le) + bitop orr diff --git a/src/target/firmware/lib/string.c b/src/target/firmware/lib/string.c new file mode 100644 index 00000000..437373bd --- /dev/null +++ b/src/target/firmware/lib/string.c @@ -0,0 +1,50 @@ +/* + * linux/lib/string.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* + * stupid library routines.. The optimized versions should generally be found + * as inline code in + * + * These are buggy as well.. + * + * * Fri Jun 25 1999, Ingo Oeser + * - Added strsep() which will replace strtok() soon (because strsep() is + * reentrant and should be faster). Use only strsep() in new code, please. + * + * * Sat Feb 09 2002, Jason Thomas , + * Matthew Hawkins + * - Kissed strtok() goodbye + */ + +#include +#include +#include + + +#ifndef __HAVE_ARCH_STRNLEN +/** + * strnlen - Find the length of a length-limited string + * @s: The string to be sized + * @count: The maximum number of bytes to search + */ +size_t strnlen(const char *s, size_t count) +{ + const char *sc; + + for (sc = s; count-- && *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} +#endif + +size_t strlen(const char *s) +{ + const char *sc; + + for (sc = s; *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} diff --git a/src/target/firmware/lib/testchangebit.S b/src/target/firmware/lib/testchangebit.S new file mode 100644 index 00000000..37c303e3 --- /dev/null +++ b/src/target/firmware/lib/testchangebit.S @@ -0,0 +1,18 @@ +/* + * linux/arch/arm/lib/testchangebit.S + * + * Copyright (C) 1995-1996 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include "bitops.h" + .text + +ENTRY(_test_and_change_bit_be) + eor r0, r0, #0x18 @ big endian byte ordering +ENTRY(_test_and_change_bit_le) + testop eor, strb diff --git a/src/target/firmware/lib/testclearbit.S b/src/target/firmware/lib/testclearbit.S new file mode 100644 index 00000000..985c3996 --- /dev/null +++ b/src/target/firmware/lib/testclearbit.S @@ -0,0 +1,18 @@ +/* + * linux/arch/arm/lib/testclearbit.S + * + * Copyright (C) 1995-1996 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include "bitops.h" + .text + +ENTRY(_test_and_clear_bit_be) + eor r0, r0, #0x18 @ big endian byte ordering +ENTRY(_test_and_clear_bit_le) + testop bicne, strneb diff --git a/src/target/firmware/lib/testsetbit.S b/src/target/firmware/lib/testsetbit.S new file mode 100644 index 00000000..4a8a164b --- /dev/null +++ b/src/target/firmware/lib/testsetbit.S @@ -0,0 +1,18 @@ +/* + * linux/arch/arm/lib/testsetbit.S + * + * Copyright (C) 1995-1996 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include "bitops.h" + .text + +ENTRY(_test_and_set_bit_be) + eor r0, r0, #0x18 @ big endian byte ordering +ENTRY(_test_and_set_bit_le) + testop orreq, streqb diff --git a/src/target/firmware/lib/vsprintf.c b/src/target/firmware/lib/vsprintf.c new file mode 100644 index 00000000..81057e4d --- /dev/null +++ b/src/target/firmware/lib/vsprintf.c @@ -0,0 +1,847 @@ +/* + * linux/lib/vsprintf.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */ +/* + * Wirzenius wrote this portably, Torvalds fucked it up :-) + */ + +/* + * Fri Jul 13 2001 Crutcher Dunnavant + * - changed to provide snprintf and vsnprintf functions + * So Feb 1 16:51:32 CET 2004 Juergen Quade + * - scnprintf and vscnprintf + */ + +#include +#include +#include +#include +#include +#include + +#include + +/** + * simple_strtoul - convert a string to an unsigned long + * @cp: The start of the string + * @endp: A pointer to the end of the parsed string will be placed here + * @base: The number base to use + */ +unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base) +{ + unsigned long result = 0,value; + + if (!base) { + base = 10; + if (*cp == '0') { + base = 8; + cp++; + if ((toupper(*cp) == 'X') && isxdigit(cp[1])) { + cp++; + base = 16; + } + } + } else if (base == 16) { + if (cp[0] == '0' && toupper(cp[1]) == 'X') + cp += 2; + } + while (isxdigit(*cp) && + (value = isdigit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10) < base) { + result = result*base + value; + cp++; + } + if (endp) + *endp = (char *)cp; + return result; +} + + +/** + * simple_strtol - convert a string to a signed long + * @cp: The start of the string + * @endp: A pointer to the end of the parsed string will be placed here + * @base: The number base to use + */ +long simple_strtol(const char *cp,char **endp,unsigned int base) +{ + if(*cp=='-') + return -simple_strtoul(cp+1,endp,base); + return simple_strtoul(cp,endp,base); +} + + +/** + * simple_strtoull - convert a string to an unsigned long long + * @cp: The start of the string + * @endp: A pointer to the end of the parsed string will be placed here + * @base: The number base to use + */ +unsigned long long simple_strtoull(const char *cp,char **endp,unsigned int base) +{ + unsigned long long result = 0,value; + + if (!base) { + base = 10; + if (*cp == '0') { + base = 8; + cp++; + if ((toupper(*cp) == 'X') && isxdigit(cp[1])) { + cp++; + base = 16; + } + } + } else if (base == 16) { + if (cp[0] == '0' && toupper(cp[1]) == 'X') + cp += 2; + } + while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp) + ? toupper(*cp) : *cp)-'A'+10) < base) { + result = result*base + value; + cp++; + } + if (endp) + *endp = (char *)cp; + return result; +} + + +/** + * simple_strtoll - convert a string to a signed long long + * @cp: The start of the string + * @endp: A pointer to the end of the parsed string will be placed here + * @base: The number base to use + */ +long long simple_strtoll(const char *cp,char **endp,unsigned int base) +{ + if(*cp=='-') + return -simple_strtoull(cp+1,endp,base); + return simple_strtoull(cp,endp,base); +} + +static int skip_atoi(const char **s) +{ + int i=0; + + while (isdigit(**s)) + i = i*10 + *((*s)++) - '0'; + return i; +} + +#define ZEROPAD 1 /* pad with zero */ +#define SIGN 2 /* unsigned/signed long */ +#define PLUS 4 /* show plus */ +#define SPACE 8 /* space if plus */ +#define LEFT 16 /* left justified */ +#define SPECIAL 32 /* 0x */ +#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ + +static char * number(char * buf, char * end, unsigned long long num, int base, int size, int precision, int type) +{ + char c,sign,tmp[66]; + const char *digits; + static const char small_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + static const char large_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + int i; + + digits = (type & LARGE) ? large_digits : small_digits; + if (type & LEFT) + type &= ~ZEROPAD; + if (base < 2 || base > 36) + return NULL; + c = (type & ZEROPAD) ? '0' : ' '; + sign = 0; + if (type & SIGN) { + if ((signed long long) num < 0) { + sign = '-'; + num = - (signed long long) num; + size--; + } else if (type & PLUS) { + sign = '+'; + size--; + } else if (type & SPACE) { + sign = ' '; + size--; + } + } + if (type & SPECIAL) { + if (base == 16) + size -= 2; + else if (base == 8) + size--; + } + i = 0; + if (num == 0) + tmp[i++]='0'; + else while (num != 0) + tmp[i++] = digits[do_div(num,base)]; + if (i > precision) + precision = i; + size -= precision; + if (!(type&(ZEROPAD+LEFT))) { + while(size-->0) { + if (buf <= end) + *buf = ' '; + ++buf; + } + } + if (sign) { + if (buf <= end) + *buf = sign; + ++buf; + } + if (type & SPECIAL) { + if (base==8) { + if (buf <= end) + *buf = '0'; + ++buf; + } else if (base==16) { + if (buf <= end) + *buf = '0'; + ++buf; + if (buf <= end) + *buf = digits[33]; + ++buf; + } + } + if (!(type & LEFT)) { + while (size-- > 0) { + if (buf <= end) + *buf = c; + ++buf; + } + } + while (i < precision--) { + if (buf <= end) + *buf = '0'; + ++buf; + } + while (i-- > 0) { + if (buf <= end) + *buf = tmp[i]; + ++buf; + } + while (size-- > 0) { + if (buf <= end) + *buf = ' '; + ++buf; + } + return buf; +} + +/** + * vsnprintf - Format a string and place it in a buffer + * @buf: The buffer to place the result into + * @size: The size of the buffer, including the trailing null space + * @fmt: The format string to use + * @args: Arguments for the format string + * + * The return value is the number of characters which would + * be generated for the given input, excluding the trailing + * '\0', as per ISO C99. If you want to have the exact + * number of characters written into @buf as return value + * (not including the trailing '\0'), use vscnprintf. If the + * return is greater than or equal to @size, the resulting + * string is truncated. + * + * Call this function if you are already dealing with a va_list. + * You probably want snprintf instead. + */ +int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) +{ + int len; + unsigned long long num; + int i, base; + char *str, *end, c; + const char *s; + + int flags; /* flags to number() */ + + int field_width; /* width of output field */ + int precision; /* min. # of digits for integers; max + number of chars for from string */ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + /* 'z' support added 23/7/1999 S.H. */ + /* 'z' changed to 'Z' --davidm 1/25/99 */ + /* 't' added for ptrdiff_t */ + + /* Reject out-of-range values early */ + if ((int) size < 0) { + return 0; + } + + str = buf; + end = buf + size - 1; + + if (end < buf - 1) { + end = ((void *) -1); + size = end - buf + 1; + } + + for (; *fmt ; ++fmt) { + if (*fmt != '%') { + if (str <= end) + *str = *fmt; + ++str; + continue; + } + + /* process flags */ + flags = 0; + repeat: + ++fmt; /* this also skips first '%' */ + switch (*fmt) { + case '-': flags |= LEFT; goto repeat; + case '+': flags |= PLUS; goto repeat; + case ' ': flags |= SPACE; goto repeat; + case '#': flags |= SPECIAL; goto repeat; + case '0': flags |= ZEROPAD; goto repeat; + } + + /* get field width */ + field_width = -1; + if (isdigit(*fmt)) + field_width = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + field_width = va_arg(args, int); + if (field_width < 0) { + field_width = -field_width; + flags |= LEFT; + } + } + + /* get the precision */ + precision = -1; + if (*fmt == '.') { + ++fmt; + if (isdigit(*fmt)) + precision = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + precision = va_arg(args, int); + } + if (precision < 0) + precision = 0; + } + + /* get the conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || + *fmt =='Z' || *fmt == 'z' || *fmt == 't') { + qualifier = *fmt; + ++fmt; + if (qualifier == 'l' && *fmt == 'l') { + qualifier = 'L'; + ++fmt; + } + } + + /* default base */ + base = 10; + + switch (*fmt) { + case 'c': + if (!(flags & LEFT)) { + while (--field_width > 0) { + if (str <= end) + *str = ' '; + ++str; + } + } + c = (unsigned char) va_arg(args, int); + if (str <= end) + *str = c; + ++str; + while (--field_width > 0) { + if (str <= end) + *str = ' '; + ++str; + } + continue; + + case 's': + s = va_arg(args, char *); + + len = strnlen(s, precision); + + if (!(flags & LEFT)) { + while (len < field_width--) { + if (str <= end) + *str = ' '; + ++str; + } + } + for (i = 0; i < len; ++i) { + if (str <= end) + *str = *s; + ++str; ++s; + } + while (len < field_width--) { + if (str <= end) + *str = ' '; + ++str; + } + continue; + + case 'p': + if (field_width == -1) { + field_width = 2*sizeof(void *); + flags |= ZEROPAD; + } + str = number(str, end, + (unsigned long) va_arg(args, void *), + 16, field_width, precision, flags); + continue; + + + case 'n': + /* FIXME: + * What does C99 say about the overflow case here? */ + if (qualifier == 'l') { + long * ip = va_arg(args, long *); + *ip = (str - buf); + } else if (qualifier == 'Z' || qualifier == 'z') { + size_t * ip = va_arg(args, size_t *); + *ip = (str - buf); + } else { + int * ip = va_arg(args, int *); + *ip = (str - buf); + } + continue; + + case '%': + if (str <= end) + *str = '%'; + ++str; + continue; + + /* integer number formats - set up the flags and "break" */ + case 'o': + base = 8; + break; + + case 'X': + flags |= LARGE; + case 'x': + base = 16; + break; + + case 'd': + case 'i': + flags |= SIGN; + case 'u': + break; + + default: + if (str <= end) + *str = '%'; + ++str; + if (*fmt) { + if (str <= end) + *str = *fmt; + ++str; + } else { + --fmt; + } + continue; + } + if (qualifier == 'L') + num = va_arg(args, long long); + else if (qualifier == 'l') { + num = va_arg(args, unsigned long); + if (flags & SIGN) + num = (signed long) num; + } else if (qualifier == 'Z' || qualifier == 'z') { + num = va_arg(args, size_t); + } else if (qualifier == 't') { + num = va_arg(args, long); + } else if (qualifier == 'h') { + num = (unsigned short) va_arg(args, int); + if (flags & SIGN) + num = (signed short) num; + } else { + num = va_arg(args, unsigned int); + if (flags & SIGN) + num = (signed int) num; + } + str = number(str, end, num, base, + field_width, precision, flags); + } + if (str <= end) + *str = '\0'; + else if (size > 0) + /* don't write out a null byte if the buf size is zero */ + *end = '\0'; + /* the trailing null byte doesn't count towards the total + * ++str; + */ + return str-buf; +} + + +/** + * vscnprintf - Format a string and place it in a buffer + * @buf: The buffer to place the result into + * @size: The size of the buffer, including the trailing null space + * @fmt: The format string to use + * @args: Arguments for the format string + * + * The return value is the number of characters which have been written into + * the @buf not including the trailing '\0'. If @size is <= 0 the function + * returns 0. + * + * Call this function if you are already dealing with a va_list. + * You probably want scnprintf instead. + */ +int vscnprintf(char *buf, size_t size, const char *fmt, va_list args) +{ + unsigned int i; + + i=vsnprintf(buf,size,fmt,args); + return (i >= size) ? (size - 1) : i; +} + + +/** + * snprintf - Format a string and place it in a buffer + * @buf: The buffer to place the result into + * @size: The size of the buffer, including the trailing null space + * @fmt: The format string to use + * @...: Arguments for the format string + * + * The return value is the number of characters which would be + * generated for the given input, excluding the trailing null, + * as per ISO C99. If the return is greater than or equal to + * @size, the resulting string is truncated. + */ +int snprintf(char * buf, size_t size, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i=vsnprintf(buf,size,fmt,args); + va_end(args); + return i; +} + + +/** + * scnprintf - Format a string and place it in a buffer + * @buf: The buffer to place the result into + * @size: The size of the buffer, including the trailing null space + * @fmt: The format string to use + * @...: Arguments for the format string + * + * The return value is the number of characters written into @buf not including + * the trailing '\0'. If @size is <= 0 the function returns 0. If the return is + * greater than or equal to @size, the resulting string is truncated. + */ + +int scnprintf(char * buf, size_t size, const char *fmt, ...) +{ + va_list args; + unsigned int i; + + va_start(args, fmt); + i = vsnprintf(buf, size, fmt, args); + va_end(args); + return (i >= size) ? (size - 1) : i; +} + +/** + * vsprintf - Format a string and place it in a buffer + * @buf: The buffer to place the result into + * @fmt: The format string to use + * @args: Arguments for the format string + * + * The function returns the number of characters written + * into @buf. Use vsnprintf or vscnprintf in order to avoid + * buffer overflows. + * + * Call this function if you are already dealing with a va_list. + * You probably want sprintf instead. + */ +int vsprintf(char *buf, const char *fmt, va_list args) +{ + return vsnprintf(buf, INT_MAX, fmt, args); +} + + +/** + * sprintf - Format a string and place it in a buffer + * @buf: The buffer to place the result into + * @fmt: The format string to use + * @...: Arguments for the format string + * + * The function returns the number of characters written + * into @buf. Use snprintf or scnprintf in order to avoid + * buffer overflows. + */ +int sprintf(char * buf, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i=vsnprintf(buf, INT_MAX, fmt, args); + va_end(args); + return i; +} + + +/** + * vsscanf - Unformat a buffer into a list of arguments + * @buf: input buffer + * @fmt: format of buffer + * @args: arguments + */ +int vsscanf(const char * buf, const char * fmt, va_list args) +{ + const char *str = buf; + char *next; + char digit; + int num = 0; + int qualifier; + int base; + int field_width; + int is_sign = 0; + + while(*fmt && *str) { + /* skip any white space in format */ + /* white space in format matchs any amount of + * white space, including none, in the input. + */ + if (isspace(*fmt)) { + while (isspace(*fmt)) + ++fmt; + while (isspace(*str)) + ++str; + } + + /* anything that is not a conversion must match exactly */ + if (*fmt != '%' && *fmt) { + if (*fmt++ != *str++) + break; + continue; + } + + if (!*fmt) + break; + ++fmt; + + /* skip this conversion. + * advance both strings to next white space + */ + if (*fmt == '*') { + while (!isspace(*fmt) && *fmt) + fmt++; + while (!isspace(*str) && *str) + str++; + continue; + } + + /* get field width */ + field_width = -1; + if (isdigit(*fmt)) + field_width = skip_atoi(&fmt); + + /* get conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || + *fmt == 'Z' || *fmt == 'z') { + qualifier = *fmt++; + if (qualifier == *fmt) { + if (qualifier == 'h') { + qualifier = 'H'; + fmt++; + } else if (qualifier == 'l') { + qualifier = 'L'; + fmt++; + } + } + } + base = 10; + is_sign = 0; + + if (!*fmt || !*str) + break; + + switch(*fmt++) { + case 'c': + { + char *s = (char *) va_arg(args,char*); + if (field_width == -1) + field_width = 1; + do { + *s++ = *str++; + } while (--field_width > 0 && *str); + num++; + } + continue; + case 's': + { + char *s = (char *) va_arg(args, char *); + if(field_width == -1) + field_width = INT_MAX; + /* first, skip leading white space in buffer */ + while (isspace(*str)) + str++; + + /* now copy until next white space */ + while (*str && !isspace(*str) && field_width--) { + *s++ = *str++; + } + *s = '\0'; + num++; + } + continue; + case 'n': + /* return number of characters read so far */ + { + int *i = (int *)va_arg(args,int*); + *i = str - buf; + } + continue; + case 'o': + base = 8; + break; + case 'x': + case 'X': + base = 16; + break; + case 'i': + base = 0; + case 'd': + is_sign = 1; + case 'u': + break; + case '%': + /* looking for '%' in str */ + if (*str++ != '%') + return num; + continue; + default: + /* invalid format; stop here */ + return num; + } + + /* have some sort of integer conversion. + * first, skip white space in buffer. + */ + while (isspace(*str)) + str++; + + digit = *str; + if (is_sign && digit == '-') + digit = *(str + 1); + + if (!digit + || (base == 16 && !isxdigit(digit)) + || (base == 10 && !isdigit(digit)) + || (base == 8 && (!isdigit(digit) || digit > '7')) + || (base == 0 && !isdigit(digit))) + break; + + switch(qualifier) { + case 'H': /* that's 'hh' in format */ + if (is_sign) { + signed char *s = (signed char *) va_arg(args,signed char *); + *s = (signed char) simple_strtol(str,&next,base); + } else { + unsigned char *s = (unsigned char *) va_arg(args, unsigned char *); + *s = (unsigned char) simple_strtoul(str, &next, base); + } + break; + case 'h': + if (is_sign) { + short *s = (short *) va_arg(args,short *); + *s = (short) simple_strtol(str,&next,base); + } else { + unsigned short *s = (unsigned short *) va_arg(args, unsigned short *); + *s = (unsigned short) simple_strtoul(str, &next, base); + } + break; + case 'l': + if (is_sign) { + long *l = (long *) va_arg(args,long *); + *l = simple_strtol(str,&next,base); + } else { + unsigned long *l = (unsigned long*) va_arg(args,unsigned long*); + *l = simple_strtoul(str,&next,base); + } + break; + case 'L': + if (is_sign) { + long long *l = (long long*) va_arg(args,long long *); + *l = simple_strtoll(str,&next,base); + } else { + unsigned long long *l = (unsigned long long*) va_arg(args,unsigned long long*); + *l = simple_strtoull(str,&next,base); + } + break; + case 'Z': + case 'z': + { + size_t *s = (size_t*) va_arg(args,size_t*); + *s = (size_t) simple_strtoul(str,&next,base); + } + break; + default: + if (is_sign) { + int *i = (int *) va_arg(args, int*); + *i = (int) simple_strtol(str,&next,base); + } else { + unsigned int *i = (unsigned int*) va_arg(args, unsigned int*); + *i = (unsigned int) simple_strtoul(str,&next,base); + } + break; + } + num++; + + if (!next) + break; + str = next; + } + return num; +} + + +/** + * sscanf - Unformat a buffer into a list of arguments + * @buf: input buffer + * @fmt: formatting of buffer + * @...: resulting arguments + */ +int sscanf(const char * buf, const char * fmt, ...) +{ + va_list args; + int i; + + va_start(args,fmt); + i = vsscanf(buf,fmt,args); + va_end(args); + return i; +} + +/* generic puts() implementation independent of who provides putchar() */ +int puts(const char *s) +{ +#ifdef ARCH_HAS_CONSOLE + return _puts(s); +#else + while (1) { + char c = *s++; + if (c == 0) + return; + putchar(c); + } + return 0; +#endif +} diff --git a/src/target/firmware/rf/trf6151.c b/src/target/firmware/rf/trf6151.c new file mode 100644 index 00000000..ea009858 --- /dev/null +++ b/src/target/firmware/rf/trf6151.c @@ -0,0 +1,380 @@ +/* Driver for RF Transceiver Circuit (TRF6151) */ + +/* (C) 2010 by Harald Welte + * + * 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 +#include + +#include +#include +#include +#include + +#include +#include + +#include + +enum trf6151_reg { + REG_RX = 0, /* RF general settings */ + REG_PLL = 1, /* PLL settings */ + REG_PWR = 2, /* Power on/off funcitonal blocks */ + REG_CFG = 3, /* Transceiver and PA controller settings */ + REG_TEST1 = 4, + REG_TEST2 = 5, + REG_TEST3 = 6, + REG_TEST4 = 7, + _MAX_REG +}; + +/* REG_RX */ +#define RX_READ_EN (1 << 7) +#define RX_CAL_MODE (1 << 8) +#define RX_RF_GAIN_HIGH (3 << 9) +#define RX_VGA_GAIN_SHIFT 11 + +/* REG_PWR */ +#define PWR_BANDGAP_SHIFT 3 +#define PWR_BANDGAP_OFF (0 << PWR_BANDGAP_SHIFT) +#define PWR_BANDGAP_ON_SPEEDUP (2 << PWR_BANDGAP_SHIFT) +#define PWR_BANDGAP_ON (3 << PWR_BANDGAP_SHIFT) +#define PWR_REGUL_ON (1 << 5) +#define PWR_SYNTHE_OFF (0) +#define PWR_SYNTHE_RX_ON (1 << 9) +#define PWR_SYNTHE_TX_ON (1 << 10) +#define PWR_RX_MODE (1 << 11) +#define PWR_TX_MODE (1 << 13) +#define PWR_PACTRL_APC (1 << 14) +#define PWR_PACTRL_APCEN (1 << 15) + +/* REG_CFG */ +#define CFG_TX_LOOP_MANU (1 << 3) +#define CFG_PACTLR_IDIOD_30uA (0 << 4) +#define CFG_PACTLR_IDIOD_300uA (1 << 4) +#define CFG_PACTLR_RES_OPEN (0 << 10) +#define CFG_PACTLR_RES_150k (1 << 10) +#define CFG_PACTLR_RES_300k (2 << 10) +#define CFG_PACTLR_CAP_0pF (0 << 12) +#define CFG_PACTLR_CAP_12p5F (1 << 12) +#define CFG_PACTLR_CAP_25pF (3 << 12) +#define CFG_PACTLR_CAP_50pF (2 << 12) +#define CFG_TEMP_SENSOR (1 << 14) +#define CFG_ILOGIC_INIT_DIS (1 << 15) + +/* FIXME: This must be defined in the RFFE configuration */ +#define TRF6151_TSP_UID 2 +#define TRF6151_PACTRL_CFG (CFG_PACTLR_RES_OPEN|CFG_PACTLR_CAP_0pF|CFG_PACTLR_IDIOD_30uA) + +#define PLL_VAL(a, b) ((a << 3) | (((b)-64) << 9)) + +/* All values in qbits unless otherwise speciifed */ +#define TRF6151_LDO_DELAY_TS 6 /* six TDMA frames (at least 25ms) */ +#define TRF6151_RX_PLL_DELAY 184 /* 170 us */ +#define TRF6151_TX_PLL_DELAY 260 /* 170 us */ + +uint16_t rf_arfcn = 871; /* TODO: this needs to be private */ +static uint16_t rf_band; + +static uint16_t trf6151_reg_cache[_MAX_REG] = { + [REG_RX] = 0x9E00, + [REG_PLL] = 0x0000, + [REG_PWR] = 0x0000, + [REG_CFG] = 0x2980, +}; + +/* Write to a TRF6151 register (4 TPU instructions) */ +static void trf6151_reg_write(uint16_t reg, uint16_t val) +{ + printd("trf6151_reg_write(reg=%u, val=0x%04x)\n", reg, val); + /* each TSP write takes 4 TPU instructions */ + tsp_write(TRF6151_TSP_UID, 16, (reg | val)); + trf6151_reg_cache[reg] = val; +} + +int trf6151_set_gain(uint8_t dbm, int high) +{ + uint16_t reg = trf6151_reg_cache[REG_RX] & 0x07ff; + + if (dbm < 14 || dbm > 40) + return -1; + + /* clear the gain bits first */ + reg &= ~((0x1F) << RX_VGA_GAIN_SHIFT); + /* OR-in the new gain value */ + reg |= (6 + (dbm-14)/2) << RX_VGA_GAIN_SHIFT; + + if (high) + reg |= RX_RF_GAIN_HIGH; + else + reg &= ~RX_RF_GAIN_HIGH; + + trf6151_reg_write(REG_RX, reg); + + return 0; +} + +#define SCALE_100KHZ 100 + +/* Compute TRF6151 PLL valuese for all 4 RX bands */ +static uint16_t trf6151_pll_rx(uint32_t freq_khz) +{ + uint32_t freq_100khz = freq_khz / SCALE_100KHZ; /* Scale from *1000 (k) to *100000 (0.1M) */ + uint32_t fb_100khz; /* frequency of B alone, without A (units of 100kHz) */ + uint32_t l; + uint32_t b10; /* B value expanded by a factor of 10 */ + uint32_t a, b; /* The PLL multipliers we want to compute */ + + /* L = 4 for low band, 2 for high band */ + if (freq_khz < 1000000) + l = 4; + else + l = 2; + + /* To compute B, we assume A is zero */ + b = (freq_100khz * 65 * l) / (64 * 26 * 10); + + if ((l == 4 && (b < 135 || b > 150)) || + (l == 2 && (b < 141 || b > 155))) + printf("Frequency %u kHz is out of spec\n", freq_khz); + + /* Compute PLL frequency assuming A == 0 */ + fb_100khz = (b * 64 * 26 * 10) / (65 * l); + + /* Compute how many 100kHz units A needs to add */ + a = freq_100khz - fb_100khz; + + if (l == 2) + a = a / 2; + + /* since all frequencies are expanded a factor of 10, we don't need to multiply A */ + printd("Freq %u kHz => A = %u, B = %u\n", freq_khz, a, b); + + /* return value in trf6151 register layout form */ + return PLL_VAL(a, b); +} + +enum trf6151_pwr_unit { + TRF1651_PACTLR_APC, + TRF6151_PACTRL_APCEN, + TRF6151_TRANSMITTER, + TRF6151_REGULATORS, +}; + +enum trf6151_gsm_band { + GSM900 = 1, + GSM1800 = 2, + GSM850_LOW = 4, + GSM850_HIGH = 5, + GSM1900 = 6, +}; + +static inline void trf6151_reset(void) +{ + /* pull the nRESET line low */ + tsp_act_disable((1 << 0)); + tpu_enq_wait(50); + /* release nRESET */ + tsp_act_enable((1 << 0)); +} + +void trf6151_init(void) +{ + /* Configure TSPEN0, which is connected to TWL3025, + * FIXME: why is this here and not in the TWL3025 driver? */ + tsp_setup(0, 1, 0, 0); + /* Configure TSPEN2, which is connected ot TRF6151 STROBE */ + tsp_setup(TRF6151_TSP_UID, 0, 1, 1); + + trf6151_reset(); + + /* configure TRF6151 for operation */ + trf6151_power(1); + trf6151_reg_write(REG_CFG, TRF6151_PACTRL_CFG | CFG_ILOGIC_INIT_DIS); + + /* FIXME: Uplink / Downlink Calibration */ +} + +void trf6151_power(int on) +{ + if (on) { + trf6151_reg_write(REG_PWR, PWR_REGUL_ON | PWR_BANDGAP_ON); + /* wait until regulators are stable (25ms == 27100 qbits) */ + tpu_enq_wait(5000); + tpu_enq_wait(5000); + tpu_enq_wait(5000); + tpu_enq_wait(5000); + tpu_enq_wait(5000); + tpu_enq_wait(2100); + } else + trf6151_reg_write(REG_PWR, PWR_BANDGAP_ON); +} + +/* Set the operational mode of the TRF6151 chip */ +void trf6151_set_mode(enum trf6151_mode mode) +{ + uint16_t pwr = (PWR_REGUL_ON | PWR_BANDGAP_ON | (rf_band<<6)); + + switch (mode) { + case TRF6151_IDLE: + /* should we switch of the RF gain for power saving? */ + break; + case TRF6151_RX: + pwr |= (PWR_SYNTHE_RX_ON | PWR_RX_MODE); + break; + case TRF6151_TX: + pwr |= (PWR_SYNTHE_TX_ON | PWR_TX_MODE); + break; + } + trf6151_reg_write(REG_PWR, pwr); +} + +static void trf6151_band_select(enum trf6151_gsm_band band) +{ + uint16_t pwr = trf6151_reg_cache[REG_PWR]; + + pwr &= ~(3 << 6); + pwr |= (band << 6); + + trf6151_reg_write(REG_PWR, pwr); +} + +/* Set ARFCN. Takes 2 reg_write, i.e. 8 TPU instructions */ +void trf6151_set_arfcn(uint16_t arfcn, int uplink) +{ + uint32_t freq_khz; + + switch (gsm_arfcn2band(arfcn)) { + case GSM_850: + rf_band = GSM850_LOW; /* FIXME: what about HIGH */ + break; + case GSM_900: + rf_band = GSM900; + break; + case GSM_1800: + rf_band = GSM1800; + break; + case GSM_1900: + rf_band = GSM1900; + break; + case GSM_450: + case GSM_480: + case GSM_750: + case GSM_810: + printf("Unsupported rf_band.\n"); + break; + } + + trf6151_band_select(rf_band); + + freq_khz = gsm_arfcn2freq10(arfcn, uplink) * 100; + printd("ARFCN %u -> %u kHz\n", arfcn, freq_khz); + + if (uplink == 0) + trf6151_reg_write(REG_PLL, trf6151_pll_rx(freq_khz)); + else + printf("We don't support uplink tuning yet!\n"); + + rf_arfcn = arfcn; // TODO: arfcn is referenced at other places +} + +void trf6151_calib_dc_offs(void) +{ + uint16_t rx = trf6151_reg_cache[REG_RX]; + + /* Set RX CAL Mode bit, it will re-set automatically */ + trf6151_reg_write(REG_RX, rx | RX_CAL_MODE); + /* DC offset calibration can take up to 50us, i.e. 54.16 * 923ns*/ + tpu_enq_wait(55); +} + +/* Frontend gain can be switched high or low (dB) */ +#define TRF6151_FE_GAIN_LOW 7 +#define TRF6151_FE_GAIN_HIGH 27 + +/* VGA at baseband can be adjusted in this range (dB) */ +#define TRF6151_VGA_GAIN_MIN 14 +#define TRF6151_VGA_GAIN_MAX 40 + +uint8_t trf6151_get_gain(void) +{ + uint16_t vga, reg_rx = trf6151_reg_cache[REG_RX]; + uint8_t gain = 0; + + switch ((reg_rx >> 9) & 3) { + case 0: + gain += TRF6151_FE_GAIN_LOW; + break; + case 3: + gain += TRF6151_FE_GAIN_HIGH; + break; + } + + vga = (reg_rx >> RX_VGA_GAIN_SHIFT) & 0x1f; + if (vga < 6) + vga = 6; + + gain += TRF6151_VGA_GAIN_MIN + (vga - 6) * 2; + + return gain; +} + +void trf6151_test(uint16_t arfcn) +{ + /* Select ARFCN 871 downlink */ + trf6151_set_arfcn(arfcn, 0); + trf6151_set_gain(40, 0); + + trf6151_set_mode(TRF6151_RX); + //trf6151_reg_write(REG_PWR, (PWR_SYNTHE_RX_ON | PWR_RX_MODE | PWR_REGUL_ON | (rf_band<<6) | PWR_BANDGAP_ON)); + /* Wait for PLL stabilization (170us max) */ + tpu_enq_wait(TRF6151_RX_PLL_DELAY); + + /* Use DC offset calibration after RX mode has been switched on + * (might not be needed) */ + trf6151_calib_dc_offs(); + + tpu_enq_sleep(); + tpu_enable(1); + tpu_wait_idle(); +} + +#define TRF6151_REGWR_QBITS 8 /* 4 GSM qbits + 4 TPU instructions */ +#define TRF6151_RX_TPU_INSTR 4 /* set_gain(1), set_arfcn(2), set_mode(1) */ + +/* delay caused by this driver programming the TPU for RX mode */ +#define TRF6151_RX_TPU_DELAY (TRF6151_RX_TPU_INSTR * TRF6151_REGWR_QBITS) + +/* prepare a Rx window with the TRF6151 finished at time 'start' (in qbits) */ +void trf6151_rx_window(int16_t start_qbits, uint16_t arfcn, uint8_t vga_dbm, int rf_gain_high) +{ + int16_t start_pll_qbits; + + /* Set the AGC and PLL registers right now, not time critical */ + trf6151_set_gain(vga_dbm, rf_gain_high); + trf6151_set_arfcn(arfcn, 0); + + /* power up at the right time _before_ the 'start_qbits' point in time */ + start_pll_qbits = add_mod5000(start_qbits, -(TRF6151_RX_PLL_DELAY + TRF6151_RX_TPU_DELAY)); + tpu_enq_at(start_pll_qbits); + trf6151_set_mode(TRF6151_RX); + + /* FIXME: power down at the right time again */ +} -- cgit v1.2.3