diff options
author | NIIBE Yutaka <gniibe@fsij.org> | 2016-05-30 14:39:02 +0900 |
---|---|---|
committer | NIIBE Yutaka <gniibe@fsij.org> | 2016-05-30 14:39:02 +0900 |
commit | b90e58f763bd277b7a5285601a34818f44f2b01c (patch) | |
tree | 4535e6abc8b77d58215796c240489e7dbf499b44 /mcu | |
parent | 8209f3875510d1990142a367c8bab6164c2f82cc (diff) |
Move CHIP specific things to mcu/
Diffstat (limited to 'mcu')
-rw-r--r-- | mcu/adc-mkl27z.c | 320 | ||||
-rw-r--r-- | mcu/clk_gpio_init-mkl27z.c (renamed from mcu/clk_gpio_init-kl.c) | 2 | ||||
-rw-r--r-- | mcu/mkl27z.h (renamed from mcu/kl_sim.h) | 0 | ||||
-rw-r--r-- | mcu/sys-mkl27z.c | 414 | ||||
-rw-r--r-- | mcu/sys-mkl27z.h | 41 | ||||
-rw-r--r-- | mcu/usb-mkl27z.c | 1034 |
6 files changed, 1810 insertions, 1 deletions
diff --git a/mcu/adc-mkl27z.c b/mcu/adc-mkl27z.c new file mode 100644 index 0000000..19f9f5b --- /dev/null +++ b/mcu/adc-mkl27z.c @@ -0,0 +1,320 @@ +/* + * adc-mkl27z.c - ADC driver for MKL27Z + * In this ADC driver, there are NeuG specific parts. + * It only records lower 8-bit of 16-bit data. + * You need to modify to use this as generic ADC driver. + * + * Copyright (C) 2016 Flying Stone Technology + * Author: NIIBE Yutaka <gniibe@fsij.org> + * + * This file is a part of Chopstx, a thread library for embedded. + * + * Chopstx 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 3 of the License, or + * (at your option) any later version. + * + * Chopstx 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, see <http://www.gnu.org/licenses/>. + * + * As additional permission under GNU GPL version 3 section 7, you may + * distribute non-source form of the Program without the copy of the + * GNU GPL normally required by section 4, provided you inform the + * receipents of GNU GPL by a written offer. + * + */ + +#include <stdint.h> +#include <stdlib.h> +#include <chopstx.h> +#include <mcu/mkl27z.h> + +struct DMAMUX { + volatile uint32_t CHCFG0; + volatile uint32_t CHCFG1; + volatile uint32_t CHCFG2; + volatile uint32_t CHCFG3; +}; +static struct DMAMUX *const DMAMUX = (struct DMAMUX *const)0x40021000; + +#define INTR_REQ_DMA0 0 + +struct DMA { + volatile uint32_t SAR; + volatile uint32_t DAR; + volatile uint32_t DSR_BCR; + volatile uint32_t DCR; +}; +static struct DMA *const DMA0 = (struct DMA *const)0x40008100; +static struct DMA *const DMA1 = (struct DMA *const)0x40008110; + + +/* We don't use ADC interrupt. Just for reference. */ +#define INTR_REQ_ADC 15 + +struct ADC { + volatile uint32_t SC1[2];/* Status and Control Registers 1 */ + volatile uint32_t CFG1; /* Configuration Register 1 */ + volatile uint32_t CFG2; /* Configuration Register 2 */ + volatile uint32_t R[2]; /* Data Result Register */ + + /* Compare Value Registers 1, 2 */ + volatile uint32_t CV1; + volatile uint32_t CV2; + + volatile uint32_t SC2; /* Status and Control Register 2 */ + volatile uint32_t SC3; /* Status and Control Register 3 */ + + volatile uint32_t OFS; /* Offset Correction Register */ + volatile uint32_t PG; /* Plus-Side Gain Register */ + volatile uint32_t MG; /* Minus-Side Gain Register */ + + /* Plus-Side General Calibration Value Registers */ + volatile uint32_t CLPD; + volatile uint32_t CLPS; + volatile uint32_t CLP4; + volatile uint32_t CLP3; + volatile uint32_t CLP2; + volatile uint32_t CLP1; + volatile uint32_t CLP0; + uint32_t rsvd0; + /* Minus-Side General Calibration Value Registers */ + volatile uint32_t CLMD; + volatile uint32_t CLMS; + volatile uint32_t CLM4; + volatile uint32_t CLM3; + volatile uint32_t CLM2; + volatile uint32_t CLM1; + volatile uint32_t CLM0; +}; +static struct ADC *const ADC0 = (struct ADC *const)0x4003B000; + +/* SC1 */ +#define ADC_SC1_DIFF (1 << 5) +#define ADC_SC1_AIEN (1 << 6) +#define ADC_SC1_COCO (1 << 7) +#define ADC_SC1_TEMPSENSOR 26 +#define ADC_SC1_BANDGAP 27 +#define ADC_SC1_ADCSTOP 31 + +/* CFG1 */ +#define ADC_CLOCK_SOURCE_ASYNCH (3 << 0) +#define ADC_MODE_16BIT (3 << 2) +#define ADC_ADLSMP_SHORT (0 << 4) +#define ADC_ADLSMP_LONG (1 << 4) +#define ADC_ADIV_1 (0 << 5) +#define ADC_ADIV_8 (3 << 5) +#define ADC_ADLPC_NORMAL (0 << 7) +#define ADC_ADLPC_LOWPOWER (1 << 7) +/**/ +#define ADC_CLOCK_SOURCE ADC_CLOCK_SOURCE_ASYNCH +#define ADC_MODE ADC_MODE_16BIT +#define ADC_ADLSMP ADC_ADLSMP_SHORT +#define ADC_ADIV ADC_ADIV_1 +#define ADC_ADLPC ADC_ADLPC_LOWPOWER + +/* CFG2 */ +#define ADC_ADLSTS_DEFAULT 0 /* 24 cycles if CFG1.ADLSMP=1, 4 if not. */ +#define ADC_ADHSC_NORMAL (0 << 2) +#define ADC_ADHSC_HIGHSPEED (1 << 2) +#define ADC_ADACK_DISABLE (0 << 3) +#define ADC_ADACK_ENABLE (1 << 3) +#define ADC_MUXSEL_A (0 << 4) +#define ADC_MUXSEL_B (1 << 4) +/**/ +#define ADC_ADLSTS ADC_ADLSTS_DEFAULT +#define ADC_ADHSC ADC_ADHSC_NORMAL +#define ADC_ADACKEN ADC_ADACK_ENABLE +#define ADC_MUXSEL ADC_MUXSEL_A + +/* SC2 */ +#define ADC_SC2_REFSEL_DEFAULT 1 /* Internal Voltage Reference??? */ +#define ADC_SC2_DMAEN (1 << 2) +#define ADC_SC2_ACREN (1 << 3) +#define ADC_SC2_ACFGT (1 << 4) +#define ADC_SC2_ACFE (1 << 5) +#define ADC_SC2_ADTRG (1 << 6) /* For hardware trigger */ + +/* SC3 */ +#define ADC_SC3_AVGS11 0x03 +#define ADC_SC3_AVGE (1 << 2) +#define ADC_SC3_ADCO (1 << 3) +#define ADC_SC3_CALF (1 << 6) +#define ADC_SC3_CAL (1 << 7) + +#define ADC_DMA_SLOT_NUM 40 + +/* + * Buffer to save ADC data. + */ +uint32_t adc_buf[64]; + +static const uint32_t adc0_sc1_setting = ADC_SC1_TEMPSENSOR; + +static chopstx_intr_t adc_intr; + +struct adc_internal { + uint32_t buf[64]; + uint8_t *p; + int phase : 8; + int count : 8; +}; +struct adc_internal adc; + +/* + * Initialize ADC module, do calibration. + * + * This is called by MAIN, only once, hopefully before creating any + * other threads (to be accurate). + * + * We configure ADC0 to kick DMA0, configure DMA0 to kick DMA1. + * DMA0 records output of ADC0 to the ADC.BUF. + * DMA1 kicks ADC0 again to get another value. + * + * ADC0 --[finish conversion]--> DMA0 --[Link channel 1]--> DMA1 + */ +int +adc_init (void) +{ + uint32_t v; + + /* Enable ADC0 and DMAMUX clock. */ + SIM->SCGC6 |= (1 << 27) | (1 << 1); + /* Enable DMA clock. */ + SIM->SCGC7 |= (1 << 8); + + /* ADC0 setting for calibration. */ + ADC0->CFG1 = ADC_CLOCK_SOURCE | ADC_MODE | ADC_ADLSMP | ADC_ADIV | ADC_ADLPC; + ADC0->CFG2 = ADC_ADLSTS | ADC_ADHSC | ADC_ADACKEN | ADC_MUXSEL; + ADC0->SC2 = ADC_SC2_REFSEL_DEFAULT; + ADC0->SC3 = ADC_SC3_CAL | ADC_SC3_CALF | ADC_SC3_AVGE | ADC_SC3_AVGS11; + + /* Wait ADC completion */ + while ((ADC0->SC1[0] & ADC_SC1_COCO) == 0) + if ((ADC0->SC3 & ADC_SC3_CALF) != 0) + /* Calibration failure */ + return -1; + + if ((ADC0->SC3 & ADC_SC3_CALF) != 0) + /* Calibration failure */ + return -1; + + /* Configure PG by the calibration values. */ + v = ADC0->CLP0 + ADC0->CLP1 + ADC0->CLP2 + ADC0->CLP3 + ADC0->CLP4 + ADC0->CLPS; + ADC0->PG = 0x8000 | (v >> 1); + + /* Configure MG by the calibration values. */ + v = ADC0->CLM0 + ADC0->CLM1 + ADC0->CLM2 + ADC0->CLM3 + ADC0->CLM4 + ADC0->CLMS; + ADC0->MG = 0x8000 | (v >> 1); + + ADC0->SC1[0] = ADC_SC1_ADCSTOP; + + /* DMAMUX setting. */ + DMAMUX->CHCFG0 = (1 << 7) | ADC_DMA_SLOT_NUM; + + /* DMA0 initial setting. */ + DMA0->SAR = (uint32_t)&ADC0->R[0]; + + /* DMA1 initial setting. */ + DMA1->SAR = (uint32_t)&adc0_sc1_setting; + DMA1->DAR = (uint32_t)&ADC0->SC1[0]; + + chopstx_claim_irq (&adc_intr, INTR_REQ_DMA0); + return 0; +} + +/* + * Start using ADC. + */ +void +adc_start (void) +{ + ADC0->CFG1 = ADC_CLOCK_SOURCE | ADC_MODE | ADC_ADLSMP | ADC_ADIV | ADC_ADLPC; + ADC0->CFG2 = ADC_ADLSTS | ADC_ADHSC | ADC_ADACKEN | ADC_MUXSEL; + ADC0->SC2 = ADC_SC2_REFSEL_DEFAULT | ADC_SC2_DMAEN; + ADC0->SC3 = 0; +} + +/* + * Kick getting data for COUNT times. + * Data will be saved in ADC_BUF starting at OFFSET. + */ +static void +adc_start_conversion_internal (int count) +{ + /* DMA0 setting. */ + DMA0->DAR = (uint32_t)&adc.buf[0]; + DMA0->DSR_BCR = 4 * count; + DMA0->DCR = (1 << 31) | (1 << 30) | (1 << 29) | (0 << 20) | (1 << 19) + | (0 << 17) | (1 << 7) | (2 << 4) | (1 << 2); + + /* Kick DMA1. */ + DMA1->DSR_BCR = 4 * count; + DMA1->DCR = (1 << 30) | (1 << 29) | (0 << 19) | (0 << 17) | (1 << 16) | (1 << 7); +} + + +/* + * Kick getting data for COUNT times. + * Data will be saved in ADC_BUF starting at OFFSET. + */ +void +adc_start_conversion (int offset, int count) +{ + adc.p = (uint8_t *)&adc_buf[offset]; + adc.phase = 0; + adc.count = count; + adc_start_conversion_internal (count); +} + + +static void +adc_stop_conversion (void) +{ + ADC0->SC1[0] = ADC_SC1_ADCSTOP; +} + +/* + * Stop using ADC. + */ +void +adc_stop (void) +{ + SIM->SCGC6 &= ~(1 << 27); +} + +/* + * Return 0 on success. + * Return 1 on error. + */ +int +adc_wait_completion (void) +{ + while (1) + { + int i; + + /* Wait DMA completion */ + chopstx_poll (NULL, 1, &adc_intr); + + DMA0->DSR_BCR = (1 << 24); + DMA1->DSR_BCR = (1 << 24); + + adc_stop_conversion (); + + for (i = 0; i < adc.count; i++) + *adc.p++ = (uint8_t)adc.buf[i]; + + if (++adc.phase >= 4) + break; + + adc_start_conversion_internal (adc.count); + } + + return 0; +} diff --git a/mcu/clk_gpio_init-kl.c b/mcu/clk_gpio_init-mkl27z.c index dedbac1..e36788a 100644 --- a/mcu/clk_gpio_init-kl.c +++ b/mcu/clk_gpio_init-mkl27z.c @@ -26,7 +26,7 @@ * */ -#include <mcu/kl_sim.h> +#include <mcu/mkl27z.h> struct MCG { volatile uint8_t C1; /* MCG Control Register 1 */ diff --git a/mcu/kl_sim.h b/mcu/mkl27z.h index 368ef30..368ef30 100644 --- a/mcu/kl_sim.h +++ b/mcu/mkl27z.h diff --git a/mcu/sys-mkl27z.c b/mcu/sys-mkl27z.c new file mode 100644 index 0000000..e44e0c2 --- /dev/null +++ b/mcu/sys-mkl27z.c @@ -0,0 +1,414 @@ +/* + * sys.c - First pages for MKL27Z256. + * + * Copyright (C) 2016 Flying Stone Technology + * Author: NIIBE Yutaka <gniibe@fsij.org> + * + * Copying and distribution of this file, with or without modification, + * are permitted in any medium without royalty provided the copyright + * notice and this notice are preserved. This file is offered as-is, + * without any warranty. + * + * + * First two pages of Flash ROM is difficult to use because of + * predefined purposes. It's defined as a default vector page and + * a flash configuration page. + * + * We put something useful to those two pages, together with the + * data for predefined purposes. + */ + +#include <stdint.h> +#include <stdlib.h> +#include "board.h" + +#define ADDR_VECTORS (0x00000900) +#define ADDR_SCR_VTOR 0xe000ed08 + +static void __attribute__ ((naked,section(".fixed_function.reset"))) +reset (void) +{ + uint32_t r3 = ADDR_SCR_VTOR; + + asm volatile ("str %2, [%0]\n\t" /* Set SCR->VTOR */ + "ldr %0, [%2]\n\t" /* Stack address */ + "msr MSP, %0\n\t" /* Exception handler stack. */ + "ldr %0, [%2, #4]\n\t" /* The entry address */ + "bx %0\n\t" /* Jump to the entry */ + ".align 2" + : "=r" (r3) + : "0" (r3), "r" (ADDR_VECTORS) + : "memory"); + + /* Never reach here. */ +} + + +static uint32_t +stack_entry[] __attribute__ ((section(".first_page.first_words"),used)) = { + /* Since MSP are soon modified in RESET, we put 0 here. */ + 0, + (uint32_t)reset, +}; + +#include "mcu/clk_gpio_init-mkl27z.c" + +static void +set_led (int on) +{ + if (on) + GPIOB->PCOR = (1 << 0); /* PTB0: Clear: Light on */ + else + GPIOB->PSOR = (1 << 0); /* PTB0: Set : Light off */ +} + +/* + * Here comes other SYS routines and data. + */ + +const uint8_t sys_version[8] __attribute__((section(".sys.version"))) = { + 3*2+2, /* bLength */ + 0x03, /* bDescriptorType = STRING_DESCRIPTOR */ + /* sys version: "3.0" */ + '3', 0, '.', 0, '0', 0, +}; + +static const uint8_t board_name_string[] = BOARD_NAME; + +const uint8_t __attribute__((section(".sys.board_info"))) +*const sys_board_name = board_name_string; + +const uint32_t __attribute__((section(".sys.board_info"))) +sys_board_id = BOARD_ID; + +typedef void (*handler)(void); + +handler sys_vector[] __attribute__ ((section(".sys.vectors"))) = { + clock_init, + gpio_init, + (handler)set_led, + NULL, +}; + +static uint32_t +flash_config[] __attribute__ ((section(".flash_config"),used)) = { + 0xffffffff, 0xffffffff, /* Comparison Key */ + 0xffffffff, /* Protection bytes */ + 0xffff3ffe, /* FSEC=0xfe, FOPT=0x3f */ + /* FOPT=0x3f: + * BOOTSRC_SEL=00: Boot from flash + */ + /* FSEC=0xfe: + * unsecure + */ +}; + + +/* + * Flash memory routine + */ +struct FTFA { + volatile uint8_t FSTAT; + volatile uint8_t FCNFG; + volatile uint8_t FSEC; + volatile uint8_t FOPT; + /* Note: addressing (3,2,1,0, 7,6,5,4, B,A,9,8) */ + /* Use Bx macro. */ + volatile uint8_t FCCO[12]; + /* Note: addressing (3,2,1,0). Use Bx macro. */ + volatile uint8_t FPROT[4]; +}; +static struct FTFA *const FTFA = (struct FTFA *const)0x40020000; + +#define FSTAT_CCIF 0x80 +#define B3 0 +#define B2 1 +#define B1 2 +#define B0 3 +#define B7 4 +#define B6 5 +#define B5 6 +#define B4 7 +#define BB 8 +#define BA 9 +#define B9 10 +#define B8 11 + +uint32_t __attribute__ ((naked,section(".fixed_function.flash_do_internal"))) +flash_do_internal (void) +{ +#ifdef ORIGINAL_IN_C + uint8_t r; + + asm volatile ("cpsid i" : : : "memory"); + FTFA->FSTAT |= FSTAT_CCIF; + while (((r = FTFA->FSTAT) & FSTAT_CCIF) == 0) + ; + r &= ~FSTAT_CCIF; + asm volatile ("cpsie i" : : : "memory"); + + return (uint32_t)r; +#else + register unsigned int r0 asm ("r0"); + register unsigned int r1 asm ("r1") = (unsigned int)FTFA; + register unsigned int r2 asm ("r2") = FSTAT_CCIF; + + asm volatile ("cpsid i\n\t" + "ldrb %0, [%1]\n\t" + "orr %0, %2\n\t" + "strb %0, [%1]\n" + "0:\t" + "ldrb %0, [%1]\n\t" + "uxtb %0, %0\n\t" + "tst %0, %2\n\t" + "beq 0b\n\t" + "cpsie i\n\t" + "bic %0, %2\n\t" + "bx lr" + : "=r" (r0) + : "r" (r1), "r" (r2) + : "memory"); + return r0; +#endif +} + +/* + * Let execute flash command. + * Since the code should be on RAM, we copy the code onto stack + * and let it go. + */ +uint32_t __attribute__ ((naked,section(".fixed_function.flash_do"))) +flash_do (void) +{ + register unsigned int r0 asm ("r0"); + register unsigned int r1 asm ("r1"); + register unsigned int r2 asm ("r2"); + register unsigned int r3 asm ("r3") = (unsigned int)flash_do_internal&~3; + /* Address of Thumb code &1 == 1, so, we clear the last bits. ------^ */ + + asm volatile ("sub sp, #32\n\t" + "mov %1, sp\n\t" + "mov %2, #0\n" + "0:\t" + "cmp %2, #32\n\t" + "beq 1f\n\t" + "ldr %0, [%3, %2]\n\t" + "str %0, [%1, %2]\n\t" + "add %2, #4\n\t" + "b 0b\n" + "1:\t" + "add %1, #1\n\t" /* Thumb code requires LSB=1. */ + "mov %3, lr\n\t" + "blx %1\n\t" + "add sp, #32\n\t" + "bx %3" + : "=r" (r0), "=r" (r1), "=r" (r2), "=r" (r3) + : "3" (r3)); +} + +#define FLASH_COMMAND_PROGRAM_LONGWORD 0x06 +#define FLASH_COMMAND_ERASE_FLASH_SECTOR 0x09 + +int __attribute__ ((naked,section(".fixed_function.flash_erase_page"))) +flash_erase_page (uint32_t addr) +{ +#ifdef ORIGINAL_IN_C + FTFA->FCCO[B0] = FLASH_COMMAND_ERASE_FLASH_SECTOR; + FTFA->FCCO[B3] = (addr >> 0) & 0xff; + FTFA->FCCO[B2] = (addr >> 8) & 0xff; + FTFA->FCCO[B1] = (addr >> 16) & 0xff; + flash_do (); +#else + register unsigned int r0 asm ("r0") = addr; + register unsigned int r1 asm ("r1"); + register unsigned int r2 asm ("r2") = FLASH_COMMAND_ERASE_FLASH_SECTOR; + register unsigned int r3 asm ("r3") = (unsigned int)FTFA; + + asm volatile ("strb %2, [%3, #7]\n\t" + "strb %0, [%3, #4]\n\t" + "lsr %0, #8\n\t" + "strb %0, [%3, #5]\n\t" + "lsr %0, #8\n\t" + "strb %0, [%3, #6]\n\t" + "b flash_do" + : "=r" (r0), "=r" (r1), "=r" (r2), "=r" (r3) + : "0" (r0), "2" (r2), "3" (r3)); +#endif +} + +int __attribute__ ((naked,section(".fixed_function.flash_program_word"))) +flash_program_word (uint32_t addr, uint32_t word) +{ +#ifdef ORIGINAL_IN_C + FTFA->FCCO[B0] = FLASH_COMMAND_PROGRAM_LONGWORD; + FTFA->FCCO[B3] = (addr >> 0) & 0xff; + FTFA->FCCO[B2] = (addr >> 8) & 0xff; + FTFA->FCCO[B1] = (addr >> 16) & 0xff; + FTFA->FCCO[B4] = (word >> 0) & 0xff; + FTFA->FCCO[B5] = (word >> 8) & 0xff; + FTFA->FCCO[B6] = (word >> 16) & 0xff; + FTFA->FCCO[B7] = (word >> 24) & 0xff; + flash_do (); +#else + register unsigned int r0 asm ("r0") = addr; + register unsigned int r1 asm ("r1") = word; + register unsigned int r2 asm ("r2") = FLASH_COMMAND_PROGRAM_LONGWORD; + register unsigned int r3 asm ("r3") = (unsigned int)FTFA; + + asm volatile ("strb %2, [%3, #7]\n\t" + "strb %0, [%3, #4]\n\t" + "lsr %0, #8\n\t" + "strb %0, [%3, #5]\n\t" + "lsr %0, #8\n\t" + "strb %0, [%3, #6]\n\t" + "strb %1, [%3, #11]\n\t" + "lsr %1, #8\n\t" + "strb %1, [%3, #10]\n\t" + "lsr %1, #8\n\t" + "strb %1, [%3, #9]\n\t" + "lsr %1, #8\n\t" + "strb %1, [%3, #8]\n\t" + "b flash_do" + : "=r" (r0), "=r" (r1), "=r" (r2), "=r" (r3) + : "0" (r0), "1" (r1), "2" (r2), "3" (r3)); +#endif +} + + +/* + * CRC32 calculation routines. + */ +void __attribute__ ((naked,section(".fixed_function.crc32_init"))) +crc32_init (unsigned int *p) +{ +#ifdef ORIGINAL_IN_C + *p = 0xffffffff; +#else + register unsigned int r3 asm ("r3"); + + asm volatile ("mov %0, #1\n\t" + "neg %0, %0\n\t" + "str %0, [%1]\n\t" + "bx lr" + : "=r" (r3) + : "r" (p) + : "memory"); +#endif +} + +#ifdef ORIGINAL_IN_C +const unsigned int *const crc32_table= (const unsigned int *const)0x00000500; +#endif + +void __attribute__ ((naked,section(".fixed_function.crc32_u8"))) +crc32_u8 (unsigned int *p, unsigned char v) +{ +#ifdef ORIGINAL_IN_C + *p = crc32_table[(*p & 0xff) ^ v] ^ (*p >> 8); +#else + register unsigned int r2 asm ("r2"); + register unsigned int r3 asm ("r3"); + + asm volatile ("ldrb %2, [%4]\n\t" + "eor %0, %2\n\t" + "mov %2, #0xa0\n\t" /* (0x0500 >> 3) */ + "lsl %0, %0, #2\n\t" + "lsl %2, %2, #3\n\t" + "add %0, %0, %2\n\t" + "ldr %2, [%4]\n\t" + "ldr %1, [%0]\n\t" + "lsr %2, %2, #8\n\t" + "eor %2, %1\n\t" + "str %2, [%4]\n\t" + "bx lr" + : "=r" (v), "=r" (r2), "=r" (r3) + : "0" (v), "r" (p) + : "memory"); +#endif +} + +void __attribute__ ((naked,section(".fixed_function.crc32_u32"))) +crc32_u32 (unsigned int *p, unsigned int u) +{ +#ifdef ORIGINAL_IN_C + crc32_u8 (p, u & 0xff); + crc32_u8 (p, (u >> 8)& 0xff); + crc32_u8 (p, (u >> 16)& 0xff); + crc32_u8 (p, (u >> 24)& 0xff); +#else + register unsigned int r3 asm ("r3"); + register unsigned int r4 asm ("r4"); + register unsigned int r5 asm ("r5"); + + asm volatile ("push {%1, %2, %3, lr}\n\t" + "mov %2, %0\n\t" + "mov %3, %5\n\t" + "uxtb %0, %0\n\t" + "bl crc32_u8\n\t" + "lsr %0, %2, #8\n\t" + "mov %5, %3\n\t" + "uxtb %0, %0\n\t" + "bl crc32_u8\n\t" + "lsr %0, %2, #16\n\t" + "mov %5, %3\n\t" + "uxtb %0, %0\n\t" + "bl crc32_u8\n\t" + "mov %5, %3\n\t" + "lsr %0, %2, #24\n\t" + "bl crc32_u8\n\t" + "pop {%1, %2, %3, pc}" + : "=r" (u), "=r" (r3), "=r" (r4), "=r" (r5) + : "0" (u), "r" (p) + : "memory"); +#endif +} + +/* + * Table of CRC32, generated by gen_crc_table.py + */ +const unsigned int +crc32_table[256] __attribute__ ((section(".crc32_table"))) = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, +}; diff --git a/mcu/sys-mkl27z.h b/mcu/sys-mkl27z.h new file mode 100644 index 0000000..eed5d92 --- /dev/null +++ b/mcu/sys-mkl27z.h @@ -0,0 +1,41 @@ +extern const uint8_t sys_version[8]; +extern const uint32_t sys_board_id; +extern const char *const sys_board_name; + +typedef void (*handler)(void); +extern handler sys_vector[16]; + +/* + * Users can override INLINE by 'attribute((used))' to have an + * implementation defined. + */ +#if !defined(INLINE) +#define INLINE __inline__ +#endif + +static INLINE void +clock_init (void) +{ + (*sys_vector[0]) (); +} + +static INLINE void +gpio_init (void) +{ + (*sys_vector[1]) (); +} + +static inline void +set_led (int on) +{ + void (*func) (int) = (void (*)(int))sys_vector[2]; + + return (*func) (on); +} + +void crc32_init (unsigned int *); +void crc32_u8 (unsigned int *, unsigned char); +void crc32_u32 (unsigned int *, unsigned int); + +int flash_erase_page (uint32_t addr); +int flash_program_word (uint32_t addr, uint32_t word); diff --git a/mcu/usb-mkl27z.c b/mcu/usb-mkl27z.c new file mode 100644 index 0000000..385e5a8 --- /dev/null +++ b/mcu/usb-mkl27z.c @@ -0,0 +1,1034 @@ +/* + * usb-mkl27z.c - USB driver for MKL27Z + * + * Copyright (C) 2016 Flying Stone Technology + * Author: NIIBE Yutaka <gniibe@fsij.org> + * + * This file is a part of Chopstx, a thread library for embedded. + * + * Chopstx 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 3 of the License, or + * (at your option) any later version. + * + * Chopstx 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, see <http://www.gnu.org/licenses/>. + * + * As additional permission under GNU GPL version 3 section 7, you may + * distribute non-source form of the Program without the copy of the + * GNU GPL normally required by section 4, provided you inform the + * receipents of GNU GPL by a written offer. + * + */ + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include "usb_lld.h" + +struct endpoint_ctl { + uint32_t rx_odd: 1; + uint32_t tx_odd: 1; +}; +static struct endpoint_ctl ep[16]; + +struct USB_CONF { + const uint8_t PERID; /* Peripheral ID register */ + uint8_t rsvd0[3]; /* */ + const uint8_t IDCOMP; /* Peripheral ID Complement register */ + uint8_t rsvd1[3]; /* */ + const uint8_t REV; /* Peripheral Revision register */ + uint8_t rsvd2[3]; /* */ + volatile uint8_t ADDINFO; /* Peripheral Additional Info register */ +}; +static struct USB_CONF *const USB_CONF = (struct USB_CONF *const) 0x40072000; + +struct USB_CTRL0 { + volatile uint8_t OTGCTL; /* OTG Control register */ +}; +static struct USB_CTRL0 *const USB_CTRL0 = (struct USB_CTRL0 *const)0x4007201c; + +struct USB_CTRL1 { + volatile uint8_t ISTAT; /* Interrupt Status register */ + uint8_t rsvd5[3]; /* */ + volatile uint8_t INTEN; /* Interrupt Enable register */ + uint8_t rsvd6[3]; /* */ + volatile uint8_t ERRSTAT; /* Error Interrupt Status register */ + uint8_t rsvd7[3]; /* */ + volatile uint8_t ERREN; /* Error Interrupt Enable register */ + uint8_t rsvd8[3]; /* */ + volatile uint8_t STAT; /* Status register */ + uint8_t rsvd9[3]; /* */ + volatile uint8_t CTL; /* Control register */ + uint8_t rsvd10[3]; /* */ + volatile uint8_t ADDR; /* Address register */ + uint8_t rsvd11[3]; /* */ + volatile uint8_t BDTPAGE1; /* BDT Page register 1 */ + uint8_t rsvd12[3]; /* */ + volatile uint8_t FRMNUML; /* Frame Number register Low */ + uint8_t rsvd13[3]; /* */ + volatile uint8_t FRMNUMH; /* Frame Number register High */ + uint8_t rsvd14[11]; /* */ + volatile uint8_t BDTPAGE2; /* BDT Page Register 2 */ + uint8_t rsvd15[3]; /* */ + volatile uint8_t BDTPAGE3; /* BDT Page Register 3 */ +}; +static struct USB_CTRL1 *const USB_CTRL1 = (struct USB_CTRL1 *const)0x40072080; + +/* Interrupt source bits */ +#define USB_IS_STALL (1 << 7) +#define USB_IS_RESUME (1 << 5) +#define USB_IS_SLEEP (1 << 4) +#define USB_IS_TOKDNE (1 << 3) +#define USB_IS_SOFTOK (1 << 2) +#define USB_IS_ERROR (1 << 1) +#define USB_IS_USBRST (1 << 0) + + +struct USB_ENDPT { + volatile uint8_t EP; /* Endpoint Control register */ + uint8_t rsvd17[3]; +}; +static struct USB_ENDPT *const USB_ENDPT = (struct USB_ENDPT *const)0x400720c0; + +struct USB_CTRL2 { + volatile uint8_t USBCTRL; /* USB Control register */ + uint8_t rsvd33[3]; /* */ + volatile uint8_t OBSERVE; /* USB OTG Observe register */ + uint8_t rsvd34[3]; /* */ + volatile uint8_t CONTROL; /* USB OTG Control register */ + uint8_t rsvd35[3]; /* */ + volatile uint8_t USBTRC0; /* USB Transceiver Control register 0 */ + uint8_t rsvd36[7]; /* */ + volatile uint8_t USBFRMADJUST; /* Frame Adjut Register */ +}; +static struct USB_CTRL2 *const USB_CTRL2 = (struct USB_CTRL2 *const)0x40072100; + +/* Buffer Descriptor */ +struct BD { + volatile uint32_t ctrl; + volatile void *buf; +}; +/* + uint32_t rsvd0 : 2; + volatile uint32_t STALL: 1; + volatile uint32_t DTS: 1; + + volatile uint32_t NINC: 1; + volatile uint32_t KEEP: 1; + volatile uint32_t DATA01: 1; + volatile uint32_t OWN: 1; + + uint32_t rsvd1: 8; + volatile uint32_t BC: 10; + uint32_t rsvd2: 6; +*/ +#define TOK_PID(ctrl) ((ctrl >> 2) & 0x0f) + +extern uint8_t __usb_bdt__; + +static struct BD *const BD_table = (struct BD *const)&__usb_bdt__; + +static uint8_t setup[8]; +/* bmRequestType, bRequest */ +/* Value: 2-byte */ +/* Index: 2-byte */ +/* Length: 2-byte */ + +static void +kl27z_usb_init (void) +{ + int i; + + memset (ep, 0, sizeof (ep)); + memset (BD_table, 0, 16 * 2 * 2 * sizeof (struct BD)); + + /* D+ pull up */ + USB_CTRL0->OTGCTL = 0x80; + + USB_CTRL1->ERREN = 0xff; + + USB_CTRL1->BDTPAGE1 = ((uint32_t)BD_table) >> 8; + USB_CTRL1->BDTPAGE2 = ((uint32_t)BD_table) >> 16; + USB_CTRL1->BDTPAGE3 = ((uint32_t)BD_table) >> 24; + + /* Not suspended, Pull-down disabled. */ + USB_CTRL2->USBCTRL = 0x00; + /* DP Pullup in non-OTG device mode. */ + USB_CTRL2->CONTROL = 0x10; + + /* Disable all endpoints. */ + for (i = 0; i < 16; i++) + USB_ENDPT[i].EP = 0; + + /* + * Enable USB FS communication module, clearing all ODD-bits + * for BDT. + */ + USB_CTRL1->CTL = 0x03; + + /* ??? How we can ask re-enumeration? Is only hard RESET enough? */ +} + +static void +kl27z_set_daddr (uint8_t daddr) +{ + USB_CTRL1->ADDR = daddr; +} + +static void +kl27z_prepare_ep0_setup (void) +{ + /* Endpoint 0, TX=0. */ + BD_table[ep[0].rx_odd].ctrl = 0x00080088; /* Len=8, OWN=1, DATA01=0, DTS=1 */ + BD_table[ep[0].rx_odd].buf = setup; + + BD_table[!ep[0].rx_odd].ctrl = 0x0000; /* OWN=0 */ + BD_table[!ep[0].rx_odd].buf = NULL; +} + +static void +kl27z_prepare_ep0_in (const void *buf, uint8_t len, int data01) +{ + /* Endpoint 0, TX=1 *//* OWN=1, DTS=1 */ + BD_table[2+ep[0].tx_odd].ctrl = (len << 16) | 0x0088 | (data01 << 6); + BD_table[2+ep[0].tx_odd].buf = (void *)buf; +} + +static void +kl27z_prepare_ep0_out (void *buf, uint8_t len, int data01) +{ + /* Endpoint 0, TX=0 *//* OWN=1, DTS=1 */ + BD_table[ep[0].rx_odd].ctrl = (len << 16) | 0x0088 | (data01 << 6); + BD_table[ep[0].rx_odd].buf = buf; +} + +static int +kl27z_ep_is_disabled (uint8_t n) +{ + return (USB_ENDPT[n].EP == 0); +} + +static int +kl27z_ep_is_stall (uint8_t n) +{ + return (USB_ENDPT[n].EP & 0x02) >> 1; +} + +static void +kl27z_ep_stall (uint8_t n) +{ + USB_ENDPT[n].EP |= 0x02; +} + +static void +kl27z_ep_clear_stall (uint8_t n) +{ + USB_ENDPT[n].EP &= ~0x02; +} + +static void +kl27z_ep_clear_dtog (int rx, uint8_t n) +{ + uint32_t config; + + if (!kl27z_ep_is_stall (n)) + /* Just in case, when the endpoint is active */ + kl27z_ep_stall (n); + + if (rx) + { + config = BD_table[4*n+ep[n].rx_odd].ctrl; + + BD_table[4*n+!ep[n].rx_odd].ctrl &= ~(1 << 6); + if ((config & 0x0080)) /* OWN already? */ + { + /* + * How to update BDT entry which is owned by USBFS seems to + * be not clearly documented. It would be just OK to update + * it as long as the endpoint is stalled (BDT entry is + * actually not in use). We write 0 at first and then write + * value with OWN, to avoid possible failure. + */ + BD_table[4*n+ep[n].rx_odd].ctrl = 0; + BD_table[4*n+ep[n].rx_odd].ctrl = (config & ~(1 << 6)); + } + } + else + { + config = BD_table[4*n+2+ep[n].tx_odd].ctrl; + + BD_table[4*n+2+!ep[n].tx_odd].ctrl &= ~(1 << 6); + if ((config & 0x0080)) /* OWN already? */ + { + BD_table[4*n+2+ep[n].tx_odd].ctrl = 0; + BD_table[4*n+2+ep[n].tx_odd].ctrl = (config & ~(1 << 6)); + } + } + + kl27z_ep_clear_stall (n); +} + +#define USB_MAX_PACKET_SIZE 64 /* For FS device */ + +enum STANDARD_REQUESTS { + GET_STATUS = 0, + CLEAR_FEATURE, + RESERVED1, + SET_FEATURE, + RESERVED2, + SET_ADDRESS, + GET_DESCRIPTOR, + SET_DESCRIPTOR, + GET_CONFIGURATION, + SET_CONFIGURATION, + GET_INTERFACE, + SET_INTERFACE, + SYNCH_FRAME, + TOTAL_REQUEST /* Total number of Standard request */ +}; + + +enum FEATURE_SELECTOR { + ENDPOINT_STALL, + DEVICE_REMOTE_WAKEUP +}; + + +struct data_ctl { + uint8_t *addr; + uint16_t len; + uint8_t require_zlp; +}; + +/* The state machine states of a control pipe */ +enum { + WAIT_SETUP, + IN_DATA, + OUT_DATA, + LAST_IN_DATA, + WAIT_STATUS_IN, + WAIT_STATUS_OUT, + STALLED, + PAUSE +}; + +struct device_ctl { + /* control pipe state */ + uint8_t state; + + uint32_t tkdone; + uint32_t reset; + uint32_t error; + uint32_t stall; + + uint32_t send; + uint32_t recv; + + /* Device specific settings */ + uint8_t configuration; + uint8_t feature; +}; + +static struct device_ctl device_ctl; +static struct data_ctl data_ctl; + +static struct device_ctl *const dev_p = &device_ctl; +static struct data_ctl *const data_p = &data_ctl; + +static void handle_transaction (uint8_t stat); + +void +usb_lld_stall (int n) +{ + kl27z_ep_stall (n); +} + + +void +usb_lld_init (uint8_t feature) +{ + dev_p->state = WAIT_SETUP; + dev_p->tkdone = 0; + dev_p->reset = 0; + dev_p->error = 0; + dev_p->stall = 0; + + usb_lld_set_configuration (0); + dev_p->feature = feature; + + kl27z_set_daddr (0); + kl27z_usb_init (); + + /* Enable the endpoint 0. */ + USB_ENDPT[0].EP = 0x0d; + + /* Clear Interrupt Status Register, and enable interrupt for USB */ + USB_CTRL1->ISTAT = 0xff; /* All clear */ + + USB_CTRL1->INTEN = USB_IS_STALL | USB_IS_TOKDNE + | USB_IS_ERROR | USB_IS_USBRST; +} + +void +usb_interrupt_handler (void) +{ + uint8_t istat_value = USB_CTRL1->ISTAT; + uint8_t stat = USB_CTRL1->STAT; + + if ((istat_value & USB_IS_USBRST)) + { + USB_CTRL1->ISTAT = USB_IS_USBRST; + usb_cb_device_reset (); + dev_p->reset++; + } + else if ((istat_value & USB_IS_ERROR)) + { /* Clear Errors. */ + USB_CTRL1->ERRSTAT = USB_CTRL1->ERRSTAT; + USB_CTRL1->ISTAT = USB_IS_ERROR; + /*reset???*/ + dev_p->error++; + } + else if ((istat_value & USB_IS_TOKDNE)) + { + handle_transaction (stat); + dev_p->tkdone++; + } + else if ((istat_value & USB_IS_STALL)) + { + /* ??? stat includes ep_num in this case ???: No, it doesn't */ + + if (kl27z_ep_is_stall (0)) + { /* It's endpoint 0, recover from erorr. */ + dev_p->state = WAIT_SETUP; + kl27z_ep_clear_stall (0); + kl27z_prepare_ep0_setup (); + } + + USB_CTRL1->ISTAT = USB_IS_STALL; + dev_p->stall++; + } +} + +#define DATA0 0 +#define DATA1 1 + +static void +handle_datastage_out (uint8_t stat) +{ + int odd = (stat >> 2)&1; + int data01 = !((BD_table[odd].ctrl >> 6)&1); + uint32_t len = (BD_table[odd].ctrl >> 16)&0x3ff; + + data_p->len -= len; + data_p->addr += len; + + len = data_p->len; + if (len > USB_MAX_PACKET_SIZE) + len = USB_MAX_PACKET_SIZE; + + if (data_p->len == 0) + { + /* No more data to receive, proceed to send acknowledge for IN. */ + dev_p->state = WAIT_STATUS_IN; + kl27z_prepare_ep0_in (setup, 0, DATA1); + } + else + { + dev_p->state = OUT_DATA; + kl27z_prepare_ep0_out (data_p->addr, len, data01); + } +} + +static void +handle_datastage_in (uint8_t stat) +{ + int odd = (stat >> 2)&1; + int data01 = !((BD_table[2+odd].ctrl >> 6)&1); + uint32_t len = USB_MAX_PACKET_SIZE; + + if ((data_p->len == 0) && (dev_p->state == LAST_IN_DATA)) + { + if (data_p->require_zlp) + { + data_p->require_zlp = 0; + + /* No more data to send. Send empty packet */ + kl27z_prepare_ep0_in (setup, 0, data01); + } + else + { + /* No more data to send, proceed to receive OUT acknowledge. */ + dev_p->state = WAIT_STATUS_OUT; + kl27z_prepare_ep0_out (setup, 8, DATA1); + } + + return; + } + + dev_p->state = (data_p->len <= len) ? LAST_IN_DATA : IN_DATA; + + if (len > data_p->len) + len = data_p->len; + + kl27z_prepare_ep0_in (data_p->addr, len, data01); + data_p->len -= len; + data_p->addr += len; +} + +typedef int (*HANDLER) (uint8_t req, struct req_args *arg); + +static int +std_none (uint8_t req, struct req_args *arg) +{ + (void)req; (void)arg; + return USB_UNSUPPORT; +} + +static int +std_get_status (uint8_t req, struct req_args *arg) +{ + uint8_t rcp = req & RECIPIENT; + uint16_t status_info = 0; + + if (arg->value != 0 || arg->len != 2 || (arg->index >> 8) != 0 + || USB_SETUP_SET (req)) + return USB_UNSUPPORT; + + if (rcp == DEVICE_RECIPIENT) + { + if (arg->index == 0) + { + /* Get Device Status */ + uint8_t feature = dev_p->feature; + + /* Remote Wakeup enabled */ + if ((feature & (1 << 5))) + status_info |= 2; + else + status_info &= ~2; + + /* Bus-powered */ + if ((feature & (1 << 6))) + status_info |= 1; + else /* Self-powered */ + status_info &= ~1; + + return usb_lld_reply_request (&status_info, 2, arg); + } + } + else if (rcp == INTERFACE_RECIPIENT) + { + int r; + + if (dev_p->configuration == 0) + return USB_UNSUPPORT; + + r = usb_cb_interface (USB_QUERY_INTERFACE, arg); + if (r != USB_SUCCESS) + return USB_UNSUPPORT; + + return usb_lld_reply_request (&status_info, 2, arg); + } + else if (rcp == ENDPOINT_RECIPIENT) + { + uint8_t n = (arg->index & 0x0f); + + if ((arg->index & 0x70) || n == ENDP0) + return USB_UNSUPPORT; + + if (kl27z_ep_is_disabled (n)) + return USB_UNSUPPORT; + + status_info = kl27z_ep_is_stall (n); + return usb_lld_reply_request (&status_info, 2, arg); + } + + return USB_UNSUPPORT; +} + +static int +std_clear_feature (uint8_t req, struct req_args *arg) +{ + uint8_t rcp = req & RECIPIENT; + + if (USB_SETUP_GET (req)) + return USB_UNSUPPORT; + + if (rcp == DEVICE_RECIPIENT) + { + if (arg->len != 0 || arg->index != 0) + return USB_UNSUPPORT; + + if (arg->value == DEVICE_REMOTE_WAKEUP) + { + dev_p->feature &= ~(1 << 5); + return USB_SUCCESS; + } + } + else if (rcp == ENDPOINT_RECIPIENT) + { + uint8_t n = (arg->index & 0x0f); + + if (dev_p->configuration == 0) + return USB_UNSUPPORT; + + if (arg->len != 0 || (arg->index >> 8) != 0 + || arg->value != ENDPOINT_STALL || n == ENDP0) + return USB_UNSUPPORT; + + if (kl27z_ep_is_disabled (n)) + return USB_UNSUPPORT; + + kl27z_ep_clear_dtog ((arg->index & 0x80) == 0, n); + + // event?? + return USB_SUCCESS; + } + + return USB_UNSUPPORT; +} + +static int +std_set_feature (uint8_t req, struct req_args *arg) +{ + uint8_t rcp = req & RECIPIENT; + + if (USB_SETUP_GET (req)) + return USB_UNSUPPORT; + + if (rcp == DEVICE_RECIPIENT) + { + if (arg->len != 0 || arg->index != 0) + return USB_UNSUPPORT; + + if (arg->value == DEVICE_REMOTE_WAKEUP) + { + dev_p->feature |= 1 << 5; + // event?? + return USB_SUCCESS; + } + } + else if (rcp == ENDPOINT_RECIPIENT) + { + uint8_t n = (arg->index & 0x0f); + + if (dev_p->configuration == 0) + return USB_UNSUPPORT; + + if (arg->len != 0 || (arg->index >> 8) != 0 + || arg->value != 0 || n == ENDP0) + return USB_UNSUPPORT; + + if (kl27z_ep_is_disabled (n)) + return USB_UNSUPPORT; + + kl27z_ep_stall (n); + + // event?? + return USB_SUCCESS; + } + + return USB_UNSUPPORT; +} + +static int +std_set_address (uint8_t req, struct req_args *arg) +{ + uint8_t rcp = req & RECIPIENT; + + if (USB_SETUP_GET (req)) + return USB_UNSUPPORT; + + if (rcp == DEVICE_RECIPIENT && arg->len == 0 && arg->value <= 127 + && arg->index == 0 && dev_p->configuration == 0) + return USB_SUCCESS; + + return USB_UNSUPPORT; +} + +static int +std_get_descriptor (uint8_t req, struct req_args *arg) +{ + uint8_t rcp = req & RECIPIENT; + + if (USB_SETUP_SET (req)) + return USB_UNSUPPORT; + + return usb_cb_get_descriptor (rcp, (arg->value >> 8), + (arg->value & 0xff), arg); +} + +static int +std_get_configuration (uint8_t req, struct req_args *arg) +{ + uint8_t rcp = req & RECIPIENT; + + (void)arg; + if (USB_SETUP_SET (req)) + return USB_UNSUPPORT; + + if (rcp == DEVICE_RECIPIENT) + return usb_lld_reply_request (&dev_p->configuration, 1, arg); + + return USB_UNSUPPORT; +} + +static int +std_set_configuration (uint8_t req, struct req_args *arg) +{ + uint8_t rcp = req & RECIPIENT; + + if (USB_SETUP_GET (req)) + return USB_UNSUPPORT; + + if (rcp == DEVICE_RECIPIENT && arg->index == 0 && arg->len == 0) + return usb_cb_handle_event (USB_EVENT_CONFIG, arg->value); + + return USB_UNSUPPORT; +} + +static int +std_get_interface (uint8_t req, struct req_args *arg) +{ + uint8_t rcp = req & RECIPIENT; + + if (USB_SETUP_SET (req)) + return USB_UNSUPPORT; + + if (rcp == INTERFACE_RECIPIENT) + { + if (arg->value != 0 || (arg->index >> 8) != 0 || arg->len != 1) + return USB_UNSUPPORT; + + if (dev_p->configuration == 0) + return USB_UNSUPPORT; + + return usb_cb_interface (USB_GET_INTERFACE, arg); + } + + return USB_UNSUPPORT; +} + +static int +std_set_interface (uint8_t req, struct req_args *arg) +{ + uint8_t rcp = req & RECIPIENT; + + if (USB_SETUP_GET (req) || rcp != INTERFACE_RECIPIENT + || arg->len != 0 || (arg->index >> 8) != 0 + || (arg->value >> 8) != 0 || dev_p->configuration == 0) + return USB_UNSUPPORT; + + return usb_cb_interface (USB_SET_INTERFACE, arg); +} + + +static void +handle_setup0 (void) +{ + struct req_args *arg = (struct req_args *)&setup[2]; + int r = USB_UNSUPPORT; + HANDLER handler; + + data_p->addr = NULL; + data_p->len = 0; + data_p->require_zlp = 0; + + if ((setup[0] & REQUEST_TYPE) == STANDARD_REQUEST) + { + if (setup[1] < TOTAL_REQUEST) + { + switch (setup[1]) + { + case 0: handler = std_get_status; break; + case 1: handler = std_clear_feature; break; + case 3: handler = std_set_feature; break; + case 5: handler = std_set_address; break; + case 6: handler = std_get_descriptor; break; + case 8: handler = std_get_configuration; break; + case 9: handler = std_set_configuration; break; + case 10: handler = std_get_interface; break; + case 11: handler = std_set_interface; break; + default: handler = std_none; break; + } + + r = (*handler) (setup[0], arg); + } + } + else + r = usb_cb_setup (setup[0], setup[1], arg); + + if (r != USB_SUCCESS) + dev_p->state = STALLED; + else if (USB_SETUP_SET (setup[0])) + { + if (arg->len == 0) + { + /* Zero length packet for ACK. */ + kl27z_prepare_ep0_in (setup, 0, DATA1); + dev_p->state = WAIT_STATUS_IN; + } + } +} + +static void +handle_in0 (uint8_t stat) +{ + if (dev_p->state == IN_DATA || dev_p->state == LAST_IN_DATA) + handle_datastage_in (stat); + else if (dev_p->state == WAIT_STATUS_IN) + { /* Control WRITE transfer done successfully. */ + uint16_t value = (setup[3]<<8) | setup[2]; + + if ((setup[1] == SET_ADDRESS) && + ((setup[0] & (REQUEST_TYPE | RECIPIENT)) + == (STANDARD_REQUEST | DEVICE_RECIPIENT))) + { + kl27z_set_daddr (value); + usb_cb_handle_event (USB_EVENT_ADDRESS, value); + ep[0].rx_odd = 0; + } + else + usb_cb_ctrl_write_finish (setup[0], setup[1], + (struct req_args *)&setup[2]); + + dev_p->state = WAIT_SETUP; + kl27z_prepare_ep0_setup (); + } + else + dev_p->state = STALLED; +} + +static void +handle_out0 (uint8_t stat) +{ + if (dev_p->state == IN_DATA || dev_p->state == LAST_IN_DATA) + /* Host aborts the control READ transfer before finish. */ + dev_p->state = STALLED; + else if (dev_p->state == OUT_DATA) + /* It's normal control WRITE transfer. */ + handle_datastage_out (stat); + else if (dev_p->state == WAIT_STATUS_OUT) + { /* Control READ transfer done successfully. */ + dev_p->state = WAIT_SETUP; + kl27z_prepare_ep0_setup (); + } + else + dev_p->state = STALLED; +} + +static void +handle_transaction (uint8_t stat) +{ + int odd = (stat >> 2)&1; + uint8_t ep_num = (stat >> 4); + + if (ep_num == 0) + { + if ((stat & 0x08) == 0) + { + ep[0].rx_odd ^= 1; + if (TOK_PID (BD_table[odd].ctrl) == 0x0d) + { + handle_setup0 (); + USB_CTRL1->ISTAT = USB_IS_TOKDNE; + USB_CTRL1->CTL = 0x01; /* Clear TXSUSPENDTOKENBUSY. */ + } + else + { + USB_CTRL1->ISTAT = USB_IS_TOKDNE; + handle_out0 (stat); + } + } + else + { + ep[0].tx_odd ^= 1; + USB_CTRL1->ISTAT = USB_IS_TOKDNE; + handle_in0 (stat); + } + + if (dev_p->state == STALLED) + kl27z_ep_stall (0); + } + else + { + if ((stat & 0x08) == 0) + { + dev_p->recv++; + ep[ep_num].rx_odd ^= 1; + usb_cb_rx_ready (ep_num); + } + else + { + /* XXX: Can be NAK. Check BDT if it's NAK or not. */ + + dev_p->send++; + ep[ep_num].tx_odd ^= 1; + usb_cb_tx_done (ep_num); + } + + USB_CTRL1->ISTAT = USB_IS_TOKDNE; + } +} + +void +usb_lld_reset (uint8_t feature) +{ + dev_p->feature = feature; + usb_lld_set_configuration (0); + + /* Reset USB */ + USB_CTRL2->USBTRC0 = 0xc0; + + USB_CTRL1->CTL = 0x00; /* Disable USB FS communication module */ + + dev_p->state = WAIT_SETUP; + dev_p->tkdone = 0; + dev_p->error = 0; + dev_p->stall = 0; + + kl27z_set_daddr (0); + kl27z_usb_init (); + + /* Clear Interrupt Status Register, and enable interrupt for USB */ + USB_CTRL1->ISTAT = 0xff; /* All clear */ + + USB_CTRL1->INTEN = USB_IS_STALL | USB_IS_TOKDNE + | USB_IS_ERROR | USB_IS_USBRST; +} + +void +usb_lld_setup_endpoint (int n, int rx_en, int tx_en) +{ + if (n == 0) + { + /* Enable the endpoint 0. */ + USB_ENDPT[0].EP = 0x0d; + kl27z_prepare_ep0_setup (); + } + else + { + /* Enable the endpoint. */ + USB_ENDPT[n].EP = (rx_en << 3)|(tx_en << 2)|0x11; + + /* Configure BDT entry so that it starts with DATA0. */ + + /* RX */ + BD_table[4*n+ep[n].rx_odd].ctrl = 0x0000; + BD_table[4*n+ep[n].rx_odd].buf = NULL; + BD_table[4*n+!ep[n].rx_odd].ctrl = 0x0040; + BD_table[4*n+!ep[n].rx_odd].buf = NULL; + + /* TX */ + BD_table[4*n+2+ep[n].tx_odd].ctrl = 0x0000; + BD_table[4*n+2+ep[n].tx_odd].buf = NULL; + BD_table[4*n+2+!ep[n].tx_odd].ctrl = 0x0040; + BD_table[4*n+2+!ep[n].tx_odd].buf = NULL; + } +} + + +void +usb_lld_set_configuration (uint8_t config) +{ + dev_p->configuration = config; +} + +uint8_t +usb_lld_current_configuration (void) +{ + return dev_p->configuration; +} + +void +usb_lld_set_data_to_recv (void *p, size_t len) +{ + data_p->addr = (uint8_t *)p; + data_p->len = len; + if (len > USB_MAX_PACKET_SIZE) + len = USB_MAX_PACKET_SIZE; + + kl27z_prepare_ep0_out (p, len, DATA1); + dev_p->state = OUT_DATA; +} + +/* + * BUF: Pointer to data memory. Data memory should not be allocated + * on stack when BUFLEN > USB_MAX_PACKET_SIZE. + * + * BUFLEN: size of the data. + */ +int +usb_lld_reply_request (const void *buf, size_t buflen, struct req_args *a) +{ + uint32_t len_asked = a->len; + uint32_t len; + + data_p->addr = (void *)buf; + data_p->len = buflen; + + /* Restrict the data length to be the one host asks for */ + if (data_p->len > len_asked) + data_p->len = len_asked; + + data_p->require_zlp = (data_p->len != 0 + && (data_p->len % USB_MAX_PACKET_SIZE) == 0); + + if (data_p->len < USB_MAX_PACKET_SIZE) + { + len = data_p->len; + dev_p->state = LAST_IN_DATA; + } + else + { + len = USB_MAX_PACKET_SIZE; + dev_p->state = IN_DATA; + } + + if (len) + kl27z_prepare_ep0_in (data_p->addr, len, DATA1); + + data_p->len -= len; + data_p->addr += len; + + return USB_SUCCESS; +} + +void +usb_lld_rx_enable (int n, void *buf, size_t len) +{ + int data01 = !((BD_table[4*n+!ep[n].rx_odd].ctrl >> 6)&1); + + BD_table[4*n+ep[n].rx_odd].ctrl = (len << 16) | 0x0088 | (data01 << 6); + BD_table[4*n+ep[n].rx_odd].buf = buf; +} + +int +usb_lld_rx_data_len (int n) +{ + return (BD_table[4*n+!ep[n].rx_odd].ctrl >> 16)&0x3ff; +} + + +void +usb_lld_tx_enable (uint8_t n, const void *buf, size_t len) +{ + int data01 = !((BD_table[4*n+2+!ep[n].tx_odd].ctrl >> 6)&1); + + BD_table[4*n+2+ep[n].tx_odd].ctrl = (len << 16) | 0x0088 | (data01 << 6); + BD_table[4*n+2+ep[n].tx_odd].buf = (void *)buf; +} + +int +usb_lld_tx_result (int ep_num) +{ + (void)ep_num; + return 0; /* XXX: return -1 when NAK */ +} |