/* * Copyright © 2012 Keith Packard * * 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., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #ifndef _AO_ARCH_FUNCS_H_ #define _AO_ARCH_FUNCS_H_ /* ao_spi_stm.c */ /* PCLK is set to 16MHz (HCLK 32MHz, APB prescaler 2) */ #define AO_SPI_SPEED_8MHz STM_SPI_CR1_BR_PCLK_2 #define AO_SPI_SPEED_4MHz STM_SPI_CR1_BR_PCLK_4 #define AO_SPI_SPEED_2MHz STM_SPI_CR1_BR_PCLK_8 #define AO_SPI_SPEED_1MHz STM_SPI_CR1_BR_PCLK_16 #define AO_SPI_SPEED_500kHz STM_SPI_CR1_BR_PCLK_32 #define AO_SPI_SPEED_250kHz STM_SPI_CR1_BR_PCLK_64 #define AO_SPI_SPEED_125kHz STM_SPI_CR1_BR_PCLK_128 #define AO_SPI_SPEED_62500Hz STM_SPI_CR1_BR_PCLK_256 #define AO_SPI_SPEED_FAST AO_SPI_SPEED_8MHz /* Companion bus wants something no faster than 200kHz */ #define AO_SPI_SPEED_200kHz AO_SPI_SPEED_125kHz #define AO_SPI_CONFIG_1 0x00 #define AO_SPI_1_CONFIG_PA5_PA6_PA7 AO_SPI_CONFIG_1 #define AO_SPI_2_CONFIG_PB13_PB14_PB15 AO_SPI_CONFIG_1 #define AO_SPI_CONFIG_2 0x04 #define AO_SPI_1_CONFIG_PB3_PB4_PB5 AO_SPI_CONFIG_2 #define AO_SPI_2_CONFIG_PD1_PD3_PD4 AO_SPI_CONFIG_2 #define AO_SPI_CONFIG_3 0x08 #define AO_SPI_1_CONFIG_PE13_PE14_PE15 AO_SPI_CONFIG_3 #define AO_SPI_CONFIG_NONE 0x0c #define AO_SPI_INDEX_MASK 0x01 #define AO_SPI_CONFIG_MASK 0x0c #define AO_SPI_1_PA5_PA6_PA7 (STM_SPI_INDEX(1) | AO_SPI_1_CONFIG_PA5_PA6_PA7) #define AO_SPI_1_PB3_PB4_PB5 (STM_SPI_INDEX(1) | AO_SPI_1_CONFIG_PB3_PB4_PB5) #define AO_SPI_1_PE13_PE14_PE15 (STM_SPI_INDEX(1) | AO_SPI_1_CONFIG_PE13_PE14_PE15) #define AO_SPI_2_PB13_PB14_PB15 (STM_SPI_INDEX(2) | AO_SPI_2_CONFIG_PB13_PB14_PB15) #define AO_SPI_2_PD1_PD3_PD4 (STM_SPI_INDEX(2) | AO_SPI_2_CONFIG_PD1_PD3_PD4) #define AO_SPI_INDEX(id) ((id) & AO_SPI_INDEX_MASK) #define AO_SPI_CONFIG(id) ((id) & AO_SPI_CONFIG_MASK) uint8_t ao_spi_try_get(uint8_t spi_index, uint32_t speed, uint8_t task_id); void ao_spi_get(uint8_t spi_index, uint32_t speed); void ao_spi_put(uint8_t spi_index); void ao_spi_send(const void *block, uint16_t len, uint8_t spi_index); void ao_spi_send_fixed(uint8_t value, uint16_t len, uint8_t spi_index); void ao_spi_send_sync(const void *block, uint16_t len, uint8_t spi_index); void ao_spi_start_bytes(uint8_t spi_index); void ao_spi_stop_bytes(uint8_t spi_index); static inline void ao_spi_send_byte(uint8_t byte, uint8_t spi_index) { struct stm_spi *stm_spi; switch (AO_SPI_INDEX(spi_index)) { case 0: stm_spi = &stm_spi1; break; case 1: stm_spi = &stm_spi2; break; } while (!(stm_spi->sr & (1 << STM_SPI_SR_TXE))) ; stm_spi->dr = byte; while (!(stm_spi->sr & (1 << STM_SPI_SR_RXNE))) ; (void) stm_spi->dr; } static inline uint8_t ao_spi_recv_byte(uint8_t spi_index) { struct stm_spi *stm_spi; switch (AO_SPI_INDEX(spi_index)) { case 0: stm_spi = &stm_spi1; break; case 1: stm_spi = &stm_spi2; break; } while (!(stm_spi->sr & (1 << STM_SPI_SR_TXE))) ; stm_spi->dr = 0xff; while (!(stm_spi->sr & (1 << STM_SPI_SR_RXNE))) ; return stm_spi->dr; } void ao_spi_recv(void *block, uint16_t len, uint8_t spi_index); void ao_spi_duplex(const void *out, void *in, uint16_t len, uint8_t spi_index); extern uint16_t ao_spi_speed[STM_NUM_SPI]; void ao_spi_init(void); #define ao_spi_set_cs(reg,mask) ((reg)->bsrr = ((uint32_t) (mask)) << 16) #define ao_spi_clr_cs(reg,mask) ((reg)->bsrr = (mask)) #define ao_spi_get_mask(reg,mask,bus, speed) do { \ ao_spi_get(bus, speed); \ ao_spi_set_cs(reg,mask); \ } while (0) static inline uint8_t ao_spi_try_get_mask(struct stm_gpio *reg, uint16_t mask, uint8_t bus, uint32_t speed, uint8_t task_id) { if (!ao_spi_try_get(bus, speed, task_id)) return 0; ao_spi_set_cs(reg, mask); return 1; } #define ao_spi_put_mask(reg,mask,bus) do { \ ao_spi_clr_cs(reg,mask); \ ao_spi_put(bus); \ } while (0) #define ao_spi_get_bit(reg,bit,pin,bus,speed) ao_spi_get_mask(reg,(1<stack + AO_STACK_SIZE); uint32_t a = (uint32_t) start; int i; /* Return address (goes into LR) */ ARM_PUSH32(sp, a); /* Clear register values r0-r12 */ i = 13; while (i--) ARM_PUSH32(sp, 0); /* APSR */ ARM_PUSH32(sp, 0); /* BASEPRI with interrupts enabled */ ARM_PUSH32(sp, 0); task->sp = sp; } static inline void ao_arch_save_regs(void) { /* Save general registers */ asm("push {r0-r12,lr}\n"); /* Save APSR */ asm("mrs r0,apsr"); asm("push {r0}"); #ifdef AO_NONMASK_INTERRUPTS /* Save BASEPRI */ asm("mrs r0,basepri"); #else /* Save PRIMASK */ asm("mrs r0,primask"); #endif asm("push {r0}"); } static inline void ao_arch_save_stack(void) { uint32_t *sp; asm("mov %0,sp" : "=&r" (sp) ); ao_cur_task->sp = (sp); } static inline void ao_arch_restore_stack(void) { /* Switch stacks */ asm("mov sp, %0" : : "r" (ao_cur_task->sp) ); #ifdef AO_NONMASK_INTERRUPTS /* Restore BASEPRI */ asm("pop {r0}"); asm("msr basepri,r0"); #else /* Restore PRIMASK */ asm("pop {r0}"); asm("msr primask,r0"); #endif /* Restore APSR */ asm("pop {r0}"); asm("msr apsr_nczvq,r0"); /* Restore general registers */ asm("pop {r0-r12,lr}\n"); /* Return to calling function */ asm("bx lr"); } #ifndef HAS_SAMPLE_PROFILE #define HAS_SAMPLE_PROFILE 0 #endif #if DEBUG #define HAS_ARCH_VALIDATE_CUR_STACK 1 static inline void ao_validate_cur_stack(void) { uint8_t *psp; asm("mrs %0,psp" : "=&r" (psp)); if (ao_cur_task && psp <= ao_cur_task->stack && psp >= ao_cur_task->stack - 256) ao_panic(AO_PANIC_STACK); } #endif #if !HAS_SAMPLE_PROFILE #define HAS_ARCH_START_SCHEDULER 1 static inline void ao_arch_start_scheduler(void) { uint32_t sp; uint32_t control; asm("mrs %0,msp" : "=&r" (sp)); asm("msr psp,%0" : : "r" (sp)); asm("mrs %0,control" : "=r" (control)); control |= (1 << 1); asm("msr control,%0" : : "r" (control)); asm("isb"); } #endif #define ao_arch_isr_stack() #endif static inline void ao_arch_wait_interrupt(void) { #ifdef AO_NONMASK_INTERRUPTS asm( "dsb\n" /* Serialize data */ "isb\n" /* Serialize instructions */ "cpsid i\n" /* Block all interrupts */ "msr basepri,%0\n" /* Allow all interrupts through basepri */ "wfi\n" /* Wait for an interrupt */ "cpsie i\n" /* Allow all interrupts */ "msr basepri,%1\n" /* Block interrupts through basepri */ : : "r" (0), "r" (AO_STM_NVIC_BASEPRI_MASK)); #else asm("\twfi\n"); ao_arch_release_interrupts(); ao_arch_block_interrupts(); #endif } #define ao_arch_critical(b) do { \ uint32_t __mask = ao_arch_irqsave(); \ do { b } while (0); \ ao_arch_irqrestore(__mask); \ } while (0) #endif /* _AO_ARCH_FUNCS_H_ */