├── clock.h ├── debug.h ├── sd.h ├── stm32f42x_uart.h ├── term.h ├── bb.h ├── Makefile ├── README.md ├── sd.c ├── sdio.h ├── debug.c ├── dump.c ├── term.c ├── clock.c ├── uart.h ├── monitor.c ├── util.c ├── sdio.c ├── uart.c └── sdio_util.c /clock.h: -------------------------------------------------------------------------------- 1 | void msleep(uint32_t delay); 2 | uint32_t mtime(void); 3 | void clock_init(int enable_systick); 4 | -------------------------------------------------------------------------------- /debug.h: -------------------------------------------------------------------------------- 1 | extern int debug_console; 2 | /* Function prototypes */ 3 | void debug_init(void); 4 | char debug_getc(int wait); 5 | void debug_putc(char c); 6 | void debug_puts(const char *s); 7 | void debug_wait(void); 8 | -------------------------------------------------------------------------------- /sd.h: -------------------------------------------------------------------------------- 1 | enum SD_CLOCK_DIV { 2 | CLOCK_24MHZ = 0, 3 | CLOCK_16MHZ, 4 | CLOCK_12MHZ, 5 | CLOCK_8MHZ, 6 | CLOCK_4MHZ, 7 | CLOCK_1MHZ, 8 | CLOCK_400KHZ 9 | }; 10 | 11 | int sd_bus(int bits, enum SD_CLOCK_DIV freq); 12 | 13 | -------------------------------------------------------------------------------- /stm32f42x_uart.h: -------------------------------------------------------------------------------- 1 | #ifndef UART7 2 | void uart7_isr(void); 3 | void uart8_isr(void); 4 | 5 | #define UART7_BASE (PERIPH_BASE_APB1 + 0x07800) 6 | #define UART8_BASE (PERIPH_BASE_APB1 + 0x07c00) 7 | #define UART7 UART7_BASE 8 | #define UART8 UART8_BASE 9 | 10 | #define RCC_APB1ENR_UART7EN (0x1 << 30) 11 | #define RCC_APB1ENR_UART8EN (0x1 << 31) 12 | 13 | #define NVIC_UART7_IRQ 82 14 | #define NVIC_UART8_IRQ 83 15 | #endif 16 | 17 | -------------------------------------------------------------------------------- /term.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 - Chuck McManis, All Rights Reserved 3 | * 4 | * Some 'curses' like functions that come in handy. 5 | */ 6 | 7 | #ifndef __TERM_H 8 | #define __TERM_H 9 | 10 | enum TEXT_COLOR { DEFAULT, RED, YELLOW, WHITE, GREEN, BLUE }; 11 | 12 | /* Prototypes */ 13 | void move_cursor(int channel, int row, int col); 14 | void clear_screen(int channel); 15 | void clear_eol(int channel); 16 | void text_color(int channel, enum TEXT_COLOR color); 17 | 18 | #endif /* __TERM_H */ 19 | -------------------------------------------------------------------------------- /bb.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple include file for our utility routines 3 | */ 4 | 5 | #include "uart.h" 6 | #include "clock.h" 7 | #include "term.h" 8 | #include "sdio.h" 9 | 10 | #ifndef NULL 11 | #define NULL (void *)(0x0000) 12 | #endif 13 | 14 | extern int console; // System console for the application 15 | 16 | void bb_setup(int32_t baud); 17 | void led_setup(void); 18 | 19 | void sd_ident(void); 20 | void sdio_explorer(void); 21 | 22 | char *stime(uint32_t); 23 | 24 | // dump functions 25 | uint8_t *dump_line(int console, uint8_t *addr, uint8_t *base); 26 | uint8_t *dump_page(int chan, uint8_t *addr, uint8_t *base); 27 | 28 | enum EDIT_EVENT { EDIT_CANCEL, EDIT_ACCEPT, EDIT_PREV, EDIT_NEXT }; 29 | enum EDIT_EVENT edit_number(int, int , int , uint32_t * , int , int ); 30 | 31 | #ifndef CONSOLE_UART 32 | #define CONSOLE_UART USART6 33 | #endif 34 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ## 2 | ## This file is part of the libopencm3 project. 3 | ## 4 | ## Copyright (C) 2009 Uwe Hermann 5 | ## 6 | ## This library is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU Lesser General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This library is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU Lesser General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU Lesser General Public License 17 | ## along with this library. If not, see . 18 | ## 19 | 20 | OBJS = util.o dump.o sdio.o sdio_util.o debug.o uart.o clock.o term.o 21 | BINARY = monitor 22 | 23 | LDSCRIPT = ../stm32f4-discovery.ld 24 | 25 | include ../../Makefile.include 26 | 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SDIO Stuff 2 | ---------- 3 | 4 | This is some code I am working on for reading SD Cards. This 5 | is the 'simple' code (it runs in polled mode). 6 | 7 | Ok, so updated this repo to be the complete example set. The 8 | initial part was a memory explorer which I have morphed into 9 | an SD card explorer as well. Note that if you're running this 10 | it will be most fun to have two terminal sessions going to your 11 | board, one for the debug stream and one for the console stream. 12 | (stderr and stdout if you will :-). 13 | 14 | Note that the example itself is kind of useless, and its built 15 | on my machine inside the libopencm3-examples tree ( under 16 | libopencm3-examples/stm32/f4/stm32f4-discovery/xplor ) if you're 17 | having trouble building it drop me an email. Also tool chain is 18 | the launchpad.net arm-none-eabi- toolchain from ARM inc. Guys 19 | you totally rock! And the Black Magic Debug Probe has been 20 | invaluable in getting this going, Gareth you're my hero. 21 | 22 | All open source code development for embedded systems is the best! 23 | 24 | --Chuck 25 | 26 | -------------------------------------------------------------------------------- /sd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * sd.c - Secure Digital API functions 3 | */ 4 | 5 | #include 6 | #include "sd.h" 7 | 8 | /* 9 | * sd_bus 10 | * 11 | * Set the bus width and the clock speed for the 12 | * SDIO bus. 13 | * 14 | * Returns 0 on success 15 | * -1 illegal bit specification 16 | * -2 illegal clock specification 17 | */ 18 | int 19 | sd_bus(int bits, enum SD_CLOCK_DIV freq) { 20 | int clkreg = 0; 21 | 22 | switch (bits) { 23 | case 1: 24 | clkreg |= SDIO_CLKCR_WIDBUS_1; 25 | break; 26 | case 4: 27 | clkreg |= SDIO_CLKCR_WIDBUS_4; 28 | break; 29 | default: 30 | return -1; 31 | } 32 | switch (freq) { 33 | CLOCK_24MHZ: 34 | break; 35 | CLOCK_16MHZ: 36 | clkreg |= 1; 37 | break; 38 | CLOCK_12MHZ: 39 | clkreg |= 2; 40 | break; 41 | CLOCK_8MHZ: 42 | clkreg |= 8; 43 | break; 44 | CLOCK_4MHZ: 45 | clkreg |= 10; 46 | break; 47 | CLOCK_1MHZ: 48 | clkreg |= 46; 49 | break; 50 | CLOCK_400KHZ: 51 | clkreg |= 118; 52 | break; 53 | default: 54 | return -2; 55 | } 56 | clkreg |= SDIO_CLKCR_CLKEN; 57 | SDIO_CLKCR = clkreg; 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /sdio.h: -------------------------------------------------------------------------------- 1 | /* this define lets the init code know that you are using a GPIO as a card 2 | * detect pin */ 3 | #define SDIO_HAS_CARD_DETECT 4 | 5 | enum SDIO_CLOCK_DIV { 6 | SDIO_24MHZ = 0, 7 | SDIO_16MHZ, 8 | SDIO_12MHZ, 9 | SDIO_8MHZ, 10 | SDIO_4MHZ, 11 | SDIO_1MHZ, 12 | SDIO_400KHZ 13 | }; 14 | 15 | enum SDIO_POWER_STATE { 16 | SDIO_POWER_ON, 17 | SDIO_POWER_OFF 18 | }; 19 | 20 | #define SDIO_CARD_CCS(c) (((c)->ocr & 0x40000000) != 0) 21 | #define SDIO_CARD_UHS2(c) (((c)->ocr & 0x40000000) != 0) 22 | #define SDIO_CARD_LVOK(c) (((c)->ocr & 0x01000000) != 0) 23 | 24 | typedef struct SDIO_CARD_DATA { 25 | uint32_t props; 26 | uint32_t ocr; 27 | uint32_t cid[4]; 28 | uint32_t csd[4]; 29 | uint32_t scr[2]; 30 | uint32_t status[16]; 31 | uint32_t size; 32 | uint16_t rca; 33 | } * SDIO_CARD; 34 | 35 | 36 | int sdio_bus(int bits, enum SDIO_CLOCK_DIV freq); 37 | void sdio_init(void); 38 | void sdio_reset(enum SDIO_POWER_STATE state); 39 | SDIO_CARD sdio_open(void); 40 | int sdio_command(uint32_t cmd, uint32_t arg); 41 | int sdio_readblock(SDIO_CARD, uint32_t lba, uint8_t *buf); 42 | int sdio_writeblock(SDIO_CARD, uint32_t lba, uint8_t *buf); 43 | int sdio_status(SDIO_CARD); 44 | void sdio_print_log(int console, int number_of_entries); 45 | const char *sdio_errmsg(int err); 46 | uint32_t sdio_bit_slice(uint32_t a[], int bits, int msb, int lsb); 47 | 48 | -------------------------------------------------------------------------------- /debug.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 - Chuck McManis, all rights reserved. 3 | * 4 | * These are some dead simple, polled, debug APIs for using the USART2 port 5 | * as a diagnostic console on PD5, nad PD6. I use the Black Magic ride along 6 | * UART to monitor this port. These are well tested and self contained so 7 | * generally plugging them in is 'easy' and you can then use these (and a 8 | * bunch of calls to debug_puts() :-)) to figure out what is going on in your 9 | * code if you can't run it under GDB. 10 | */ 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "uart.h" 16 | #include "debug.h" 17 | 18 | int debug_console; 19 | /* 20 | * Hard Defined, USART2, PD5 & PD6, 38400 baud. Compatible 21 | * with the Blackmagic Debug Probe ACM1 port. 22 | */ 23 | void 24 | debug_init(void) { 25 | debug_console = uart_init(PD5, PD6, 115200); 26 | } 27 | 28 | /* 29 | * Get a character, optionally wait for it. (no function key support) 30 | */ 31 | char 32 | debug_getc(int wait) { 33 | return uart_getc(debug_console, wait); 34 | } 35 | 36 | /* 37 | * Put a character 38 | */ 39 | void 40 | debug_putc(char c) { 41 | uart_putc(debug_console, c); 42 | } 43 | 44 | /* 45 | * Write out a string. 46 | */ 47 | void 48 | debug_puts(const char *s) { 49 | uart_puts(debug_console, s); 50 | } 51 | 52 | static const char *w_space = "[Press Space]\n"; 53 | /* 54 | * Write out the string '[Press Space]' and wait 55 | * for the space key to be pressed. 56 | */ 57 | void 58 | debug_wait() { 59 | debug_puts(w_space); 60 | while (debug_getc(0) != ' '); 61 | return; 62 | } 63 | -------------------------------------------------------------------------------- /dump.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Some functions for dumping out the contents of memory in 3 | * hexadecimal. 4 | */ 5 | 6 | #include 7 | #include "bb.h" 8 | 9 | /* Convienience defines for uart_putnum */ 10 | #define FMT_BYTE FMT_BASE_16 | FMT_LEADING_ZERO | FMT_SIZE_BYTE 11 | #define FMT_LONG FMT_BASE_16 | FMT_LEADING_ZERO | FMT_SIZE_LONG 12 | 13 | /* 14 | * Dump out the memory contents at address 'addr' 15 | * This is 'classic' hex dump format 16 | * 17 | * Note that the 'base' parameter allows you to 18 | * have the addresses be shown relative to the 19 | * the address passed. This lets the SD card code 20 | * display them as 0x000 - 0x1ff rather than the 21 | * address of the buffer they are held in. 22 | */ 23 | uint8_t * 24 | dump_line(int c, uint8_t *addr, uint8_t *base) { 25 | uint8_t *line_addr; 26 | uint8_t b; 27 | uint32_t tmp; 28 | int i; 29 | 30 | line_addr = addr; 31 | text_color(c, YELLOW); 32 | tmp = (uint32_t)line_addr - (uint32_t) base; 33 | uart_putnum(c, FMT_LONG, tmp); 34 | uart_puts(c, " | "); 35 | text_color(c, GREEN); 36 | for (i = 0; i < 16; i++) { 37 | uart_putnum(c, FMT_BYTE, *(line_addr+i)); 38 | uart_putc(c, ' '); 39 | if (i == 7) { 40 | uart_puts(c, " "); 41 | } 42 | } 43 | text_color(c, WHITE); 44 | uart_puts(c, "| "); 45 | for (i = 0; i < 16; i++) { 46 | b = *line_addr++; 47 | uart_putc(c, ((b > 126) || (b < 32)) ? '.' : (char) b); 48 | } 49 | text_color(c, DEFAULT); 50 | uart_puts(c, "\n"); 51 | return line_addr; 52 | } 53 | 54 | 55 | uint8_t * 56 | dump_page(int chan, uint8_t *addr, uint8_t *base) { 57 | int i; 58 | for (i = 0; i < 16; i++) { 59 | addr = dump_line(chan, addr, base); 60 | } 61 | return addr; 62 | } 63 | 64 | -------------------------------------------------------------------------------- /term.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008 - Chuck McManis, All Rights Reserved 3 | * 4 | * These are some 'curses' like functions which have come in 5 | * handy. 6 | */ 7 | 8 | #include 9 | #include "uart.h" 10 | #include "term.h" 11 | extern int console; 12 | 13 | #define CSI "\033[" 14 | 15 | /* 16 | * Send the escape sequence to position the cursor 17 | */ 18 | void 19 | move_cursor(int chan, int row, int col) { 20 | if ((row <= 0) || (col <= 0) || (row > 88) || (col > 200)) { 21 | return; 22 | } 23 | uart_puts(chan, CSI); 24 | uart_putnum(chan, FMT_BASE_10, row); 25 | uart_putc(chan, ';'); 26 | uart_putnum(chan, FMT_BASE_10, col); 27 | uart_putc(chan, 'H'); 28 | } 29 | 30 | static const char *__screen_clear = CSI "2J"; 31 | 32 | /* Clear the screen */ 33 | void 34 | clear_screen(int chan) { 35 | uart_puts(chan, __screen_clear); 36 | } 37 | 38 | static const char * __clear_eol = CSI "2j"; 39 | void 40 | clear_eol(int chan) { 41 | uart_puts(chan, __clear_eol); 42 | } 43 | 44 | static const char *__screen_colors[6] = { 45 | CSI "31;40;1m", // RED on black 46 | CSI "33;40;1m", // YELLOW on black 47 | CSI "37;40;1m", // WHITE on black 48 | CSI "32;40;1m", // GREEN on black 49 | CSI "34;40;1m", // BLUE on black 50 | CSI "0m", // DEFAULT on black 51 | }; 52 | 53 | 54 | /* Set Text Color */ 55 | void 56 | text_color(int chan, enum TEXT_COLOR color) { 57 | switch (color) { 58 | case RED: 59 | return uart_puts(chan, __screen_colors[0]); 60 | case YELLOW: 61 | return uart_puts(chan, __screen_colors[1]); 62 | case WHITE: 63 | return uart_puts(chan, __screen_colors[2]); 64 | case GREEN: 65 | return uart_puts(chan, __screen_colors[3]); 66 | case BLUE: 67 | return uart_puts(chan, __screen_colors[4]); 68 | default: 69 | return uart_puts(chan, __screen_colors[5]); 70 | } 71 | } 72 | 73 | -------------------------------------------------------------------------------- /clock.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Chuck McManis 3 | * 4 | * Simple clock setup/driver for the STM32F4-Discovery board. 5 | * libopencm3 does the heavy lifting, it just sets up SysTick 6 | * and the desired clock rate (168Mhz in this case) 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "clock.h" 15 | 16 | /* 17 | * Comment this #define out if you don't want the BLUE led on 18 | * the board blinking at 2Hz 19 | */ 20 | #define SYSTICK_HEARTBEAT 21 | 22 | /* 23 | * SysTick support routines 24 | */ 25 | 26 | /* monotonically increasing number of milliseconds from reset 27 | * overflows every 49 days if you're wondering 28 | */ 29 | volatile uint32_t system_millis; 30 | 31 | /* Called when systick fires */ 32 | void 33 | sys_tick_handler(void) { 34 | system_millis++; 35 | #ifdef SYSTICK_HEARTBEAT 36 | /* Generate a 2Hz heart beep blink */ 37 | if ((system_millis % 500) == 0) { 38 | gpio_toggle(GPIOD, GPIO15); // BLUE led 39 | } 40 | #endif 41 | } 42 | 43 | /* sleep for delay milliseconds */ 44 | void msleep(uint32_t delay) { 45 | uint32_t wake = system_millis + delay; 46 | while (wake > system_millis) ; 47 | } 48 | 49 | /* return the time */ 50 | uint32_t mtime() { 51 | return system_millis; 52 | } 53 | 54 | /* Set up a timer to create 1mS ticks. */ 55 | static void 56 | systick_setup(int tick_rate) { 57 | /* clock rate / 1000 to get 1mS interrupt rate */ 58 | systick_set_reload((168000000) / tick_rate); 59 | STK_CTRL = 0x07; 60 | #if 0 61 | /* libopencm3 bug, the above assign does all three of these 62 | * steps, but systick_set_clocksource fails to set the clock 63 | * source to AHB because it doesn't correctly shift the value 64 | */ 65 | systick_set_clocksource(1); 66 | systick_counter_enable(); 67 | systick_interrupt_enable(); 68 | #endif 69 | } 70 | 71 | /* Set STM32 to 168 MHz. */ 72 | void 73 | clock_init(int systick_rate) 74 | { 75 | rcc_clock_setup_hse_3v3(&hse_8mhz_3v3[CLOCK_3V3_168MHZ]); 76 | #ifdef SYSTICK_HEARTBEAT 77 | rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_IOPDEN); 78 | gpio_mode_setup(GPIOD, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO15); 79 | #endif 80 | if (systick_rate) { 81 | systick_setup(systick_rate); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /uart.h: -------------------------------------------------------------------------------- 1 | #ifndef __UART_H 2 | #define __UART_H 3 | 4 | /* 5 | * UART Pin Map 6 | */ 7 | 8 | /* All pins that can be a TX or RX pin for a USART or a UART */ 9 | enum UART_PORT_PIN { 10 | PA0, // UART4_TX 11 | PA1, // UART4_RX 12 | PA2, // USART2_TX 13 | PA3, // USART2_RX 14 | PA9, // USART1_TX 15 | PA10, // USART1_RX 16 | PB6, // USART1_TX 17 | PB7, // USART1_RX 18 | PB10, // USART3_TX 19 | PB11, // USART3_RX 20 | PC6, // USART6_TX 21 | PC7, // USART6_RX 22 | PC10, // USART3_TX, UART4_TX (these default to USART3) 23 | PC11, // USART3_RX, UART4_RX, 24 | PC12, // USART5_TX, 25 | PD2, // USART5_RX, 26 | PD5, // USART2_TX 27 | PD6, // USART2_RX 28 | PD8, // USART3_TX 29 | PD9, // USART3_RX 30 | PE0, // UART8_RX 31 | PE1, // UART8_TX 32 | PE7, // UART7_RX 33 | PE8, // UART7_TX 34 | PF6, // UART7_RX 35 | PF7, // UART7_TX 36 | PG9, // USART6_RX 37 | PG14, // USART6_TX 38 | }; 39 | 40 | /* Defines for the putnum function */ 41 | #define FMT_SIZE_MASK 0xf << 0 42 | #define FMT_SIZE_BYTE 1 43 | #define FMT_SIZE_WORD 2 44 | #define FMT_SIZE_LONG 4 45 | // #define FMT_SIZE_LONGLONG 8 // reserved 46 | #define FMT_BASE_10 0 << 5 47 | #define FMT_BASE_2 1 << 5 48 | #define FMT_BASE_8 2 << 5 49 | #define FMT_BASE_16 3 << 5 50 | #define FMT_BASE_MASK 3 << 5 51 | #define FMT_SIGNED 1 << 7 52 | #define FMT_LEADING_ZERO 1 << 8 53 | #define FMT_ALTERNATE_FORM 1 << 9 54 | #define FMT_LEFT_ADJUST 1 << 10 55 | #define FMT_NEWLINE 1 << 11 56 | #define FMT_DECIMAL FMT_BASE_10 | FMT_ALTERNATE_FORM | FMT_SIGNED 57 | #define FMT_HEX_BYTE FMT_BASE_16 | FMT_LEADING_ZERO | FMT_SIZE_BYTE 58 | #define FMT_HEX_WORD FMT_BASE_16 | FMT_LEADING_ZERO | FMT_SIZE_WORD 59 | #define FMT_HEX_LONG FMT_BASE_16 | FMT_LEADING_ZERO | FMT_SIZE_LONG 60 | #define FMT_BINARY_BYTE FMT_BASE_2 | FMT_LEADING_ZERO | FMT_SIZE_BYTE 61 | #define FMT_BINARY_WORD FMT_BASE_2 | FMT_LEADING_ZERO | FMT_SIZE_WORD 62 | #define FMT_BINARY_LONG FMT_BASE_2 | FMT_LEADING_ZERO | FMT_SIZE_LONG 63 | #define FMT_HEX_CONSTANT FMT_BASE_16 | FMT_ALTERNATE_FORM 64 | 65 | /* Formatting discussion : 66 | * So the interesting thing here is that when you try to format -1 as 67 | * a byte it shows up as FFFFFFFF rather than FF. So to get that behavior 68 | * we're going to cut off the number if its all 1 bits. 69 | * 70 | * To address the above and other issues, we add the 'size' flag which 71 | * is the natural size of the number. 72 | */ 73 | 74 | /* Prototypes */ 75 | int uart_init(enum UART_PORT_PIN tx, enum UART_PORT_PIN rx, int baudrate); 76 | void uart_setbaudrate(int ud, int baudrate); 77 | char uart_getc(int channel, int wait); 78 | void uart_putc(int channel, char c); 79 | void uart_puts(int channel, const char *s); 80 | char *uart_gets(int channel, char *s, int len); 81 | void uart_putnum(int channel, uint16_t fmt, uint32_t num); 82 | uint32_t uart_getnum(int channel); 83 | void ntoa(uint32_t val, uint16_t fmt, char *buf); 84 | uint32_t aton(char *buf); 85 | #endif // __UART_H 86 | -------------------------------------------------------------------------------- /monitor.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple monitor for the EMBEST Baseboard + STM32F4-Discovery 3 | * board. 4 | */ 5 | 6 | #include 7 | #include 8 | #include "bb.h" 9 | #include "debug.h" 10 | 11 | /* 12 | * Not sure I love this design, but this integer is the 'console' 13 | * port. That is the port where all the serial I/O is to be sent 14 | * or 'standard out' in UNIX/Linux parlance. It is defined as a global 15 | * here because it is impractical to be sending all around, much like 16 | * the file descriptor for standard out is global for the same reason. 17 | * It also means that if someone or something stomps on it then your 18 | * code will stop writing to the serial port you expected. 19 | */ 20 | int console = 0; 21 | 22 | const char *greet = "\nARM Baseboard Monitor v0.01\nEnter Command, ? or H for help.\n"; 23 | 24 | void do_cmd(int); 25 | #define is_hex(c) ((((c) >= '0') && ((c) <= '9')) ||\ 26 | (((c) >= 'a') && ((c) <= 'f')) ||\ 27 | (((c) >= 'A') && ((c) <= 'F'))) 28 | 29 | #define CMD_READ_SD 1 30 | #define CMD_WRITE_SD 2 31 | #define CMD_IDENT_SD 3 32 | void do_cmd(int cmd) { 33 | switch (cmd) { 34 | case CMD_READ_SD: 35 | case CMD_WRITE_SD: 36 | case CMD_IDENT_SD: 37 | break; 38 | default: 39 | uart_puts(console, (const char *)"Unrecognized command\n"); 40 | } 41 | } 42 | 43 | char buf[256]; 44 | 45 | 46 | #define valid_addr(x) \ 47 | ((((uint32_t) x >= 0x10000000) && ((uint32_t) x < 0x10010000)) ||\ 48 | (((uint32_t) x >= 0x20000000) && ((uint32_t) x < 0x20020000)) ||\ 49 | (((uint32_t) x >= 0x08000000) && ((uint32_t) x < 0x08100000))) 50 | 51 | #define MIN_ADDR (uint8_t *)(0x20000000) 52 | int 53 | main(void) 54 | { 55 | char c; 56 | uint8_t *addr; 57 | 58 | 59 | // setup 115,200 baud 60 | bb_setup(115200); 61 | debug_init(); 62 | debug_puts("\nBBMON: Debug Channel\n"); 63 | 64 | text_color(console, DEFAULT); 65 | clear_screen(console); 66 | move_cursor(console, 1,1); 67 | uart_puts(console, (const char *)greet); 68 | uart_puts(console, "Endian test : "); 69 | uart_putnum(console, FMT_HEX_CONSTANT, 0xaabbccdd); 70 | uart_puts(console, "\n"); 71 | uart_puts(console, "Greeting is at : 0x"); 72 | uart_putnum(console, FMT_HEX_CONSTANT, (uint32_t)(greet)); 73 | uart_puts(console, "\n"); 74 | addr = MIN_ADDR; 75 | move_cursor(console, 13, 1); 76 | addr = dump_page(console, addr, NULL); 77 | addr = dump_page(console, addr, NULL); 78 | /* really should go into command line mode here */ 79 | while(1) { 80 | move_cursor(console, 11, 1); 81 | uart_puts(console, "Enter Command: "); 82 | move_cursor(console, 11, 16); 83 | c = uart_getc(console, 1); 84 | switch (c) { 85 | case 'd': 86 | uart_puts(console, "dump (address) :"); 87 | addr = (uint8_t *)uart_getnum(console); 88 | addr = (valid_addr(addr)) ? addr : MIN_ADDR; 89 | move_cursor(console, 13, 1); 90 | addr = dump_page(console, addr, NULL); 91 | addr = dump_page(console, addr, NULL); 92 | break; 93 | case '\r': 94 | uart_puts(console, "\n"); 95 | move_cursor(console, 13, 1); 96 | addr = dump_page(console, addr, NULL); 97 | addr = dump_page(console, addr, NULL); 98 | break; 99 | case 'a': 100 | uart_puts(console, "address: "); 101 | addr = (uint8_t *)uart_getnum(console); 102 | addr = (valid_addr(addr)) ? addr : MIN_ADDR; 103 | break; 104 | case 's': 105 | sdio_explorer(); 106 | break; 107 | default: 108 | uart_puts(console, "?\n"); 109 | } 110 | } 111 | return 0; 112 | } 113 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Chuck McManis 3 | * 4 | * This software is distributed in the hope that it will be useful, 5 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 6 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "bb.h" 18 | 19 | /* 20 | * Do chip initialization to set up the chip and peripherals 21 | * 22 | * Not sure I like this design pattern, basically we call "bb_setup()" 23 | * which is a global, and then this code sets up all the things that 24 | * the bb "library" is going to be using. That said, I don't like the 25 | * fairly #ifdef laden style of Atmel or ST Micro either so I'm living 26 | * with this for now. 27 | */ 28 | void 29 | bb_setup(int32_t baud) { 30 | clock_init(1000); 31 | console = uart_init(PC6, PC7, baud); 32 | sdio_init(); 33 | } 34 | 35 | 36 | /* 37 | * stime(uint32_t) 38 | * 39 | * Convert a number representing milliseconds into a 'time' string 40 | * of HHH:MM:SS.mmm where HHH is hours, MM is minutes, SS is seconds 41 | * and .mmm is fractions of a second. 42 | */ 43 | char * 44 | stime(uint32_t t) { 45 | static char time_string[14]; 46 | uint16_t msecs = t % 1000; 47 | uint8_t secs = (t / 1000) % 60; 48 | uint8_t mins = (t / 60000) % 60; 49 | uint16_t hrs = (t /3600000); 50 | 51 | // HH:MM:SS.mmm\0 52 | // 0123456789abc 53 | time_string[0] = (hrs % 100) + '0'; 54 | time_string[1] = (hrs / 10) % 10 + '0'; 55 | time_string[2] = hrs % 10 + '0'; 56 | time_string[3] = ':'; 57 | time_string[4] = (mins / 10) % 10 + '0'; 58 | time_string[5] = mins % 10 + '0'; 59 | time_string[6] = ':'; 60 | time_string[7] = (secs / 10) % 10 + '0'; 61 | time_string[8] = secs % 10 + '0'; 62 | time_string[9] = '.'; 63 | time_string[10] = (msecs / 100) % 10 + '0'; 64 | time_string[11] = (msecs / 10) % 10 + '0'; 65 | time_string[12] = msecs % 10 + '0'; 66 | time_string[13] = 0; 67 | return &time_string[0]; 68 | } 69 | 70 | /* 71 | * Edit a number in place, needs work. 72 | * So ideally it should return a 'what happened' not a 73 | * number, so we'll change it to that. 74 | */ 75 | enum EDIT_EVENT 76 | edit_number(int chan, int row, int col, uint32_t *orig, int base, int size) { 77 | #if 0 78 | XXX Fix this 79 | int cursor = 0; 80 | char buf[33]; 81 | char c; 82 | int width = 8; 83 | 84 | ntoa(*orig, FMT_BASE_16, buf, base); 85 | while (1) { 86 | move_cursor(console, row, col); 87 | text_color(console, WHITE); 88 | uart_puts(console, buf); 89 | move_cursor(console, row, col+cursor); 90 | c = uart_getc(console, 1); 91 | if ((c >= '0') && (c <= '9')) { 92 | *(buf + cursor) = c; 93 | cursor = (cursor + 1) & 0x7; 94 | } else if (((c >= 'a') && (c <= 'f')) || 95 | ((c >= 'A') && (c <= 'F'))) { 96 | *(buf + cursor) = c & 0x4f; 97 | cursor = (cursor + 1) % width; 98 | } else if (c == 27) { 99 | move_cursor(console, row, col); 100 | text_color(console, DEFAULT); 101 | uart_puts(console, buf); 102 | return EDIT_CANCEL; 103 | } else if (c == 13 ) { 104 | *orig = aton(buf, base); 105 | move_cursor(console, row, col); 106 | text_color(console, DEFAULT); 107 | uart_puts(console, buf); 108 | return EDIT_ACCEPT; 109 | } else if ( c == 196 ) { 110 | cursor = (cursor > 0) ? cursor - 1 : width - 1; 111 | } else if ( c == 195 ) { 112 | cursor = (cursor + 1) % width; 113 | } 114 | } 115 | #endif 116 | return EDIT_ACCEPT; 117 | } 118 | -------------------------------------------------------------------------------- /sdio.c: -------------------------------------------------------------------------------- 1 | /* 2 | * sdio.c 3 | * 4 | * SDIO Bus Driver layer. This code sends commands and drives 5 | * the SDIO peripheral on the STM32F4xx, there is a layer above 6 | * this, the SD Card driver, which uses this driver to talk to 7 | * SD Cards. The SPI driver can also talk to SD Cards, hence the 8 | * split at this layer. 9 | * 10 | * Note that the simple implementation for the SDIO driver runs 11 | * in a 'polled' mode. This is easier to explain and debug and 12 | * sufficient for the first few projects. A more sophisticated 13 | * version with DMA and interrupts will follow. 14 | * 15 | * Could be part of the libopencm3 project if they wanted it. 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "term.h" 24 | #include "uart.h" 25 | #include "sdio.h" 26 | #include "debug.h" 27 | #include "clock.h" 28 | 29 | /* 30 | * Some Global defines, collected here. 31 | */ 32 | 33 | 34 | /* shorthand for debug_puts which used to be so that we could 35 | * turn it on/off with #define DEBUG(x) to nothing. But with 36 | * putnum that doesn't work as nicely as I would like. 37 | */ 38 | #define DEBUG(x) debug_puts((x)) 39 | 40 | /* 41 | * The logging feature. Useful during debugging but consumes 42 | * (28 * SDIO_LOGSIZE)+4 bytes of RAM to hold the log and the 43 | * index into the log. Default size is 10 entries (see below) 44 | */ 45 | #define SDIO_LOGGING 46 | 47 | /* 48 | * The Embest board ties PB15 to 'card detect' which is useful 49 | * for aborting early, and detecting card swap. Needs porting 50 | * for other implementations. 51 | */ 52 | #define SDIO_HAS_CARD_DETECT 53 | 54 | /* 55 | * Not defined by default 56 | */ 57 | #ifndef NULL 58 | #define NULL (void *)(0x00000000) 59 | #endif 60 | 61 | /* 62 | * Helper defines to pull out various bit fields of the CSD for 63 | * the size calculation. 64 | */ 65 | #define SDIO_CSD_VERSION(x) sdio_bit_slice(x->csd, 128, 127, 126) 66 | #define SDIO_CSD1_CSIZE_MULT(x) sdio_bit_slice(x->csd, 128, 49, 47) 67 | #define SDIO_CSD1_RBLKLEN(x) sdio_bit_slice(x->csd, 128, 83, 80) 68 | #define SDIO_CSD1_CSIZE(x) sdio_bit_slice(x->csd, 128, 73, 62) 69 | #define SDIO_CSD2_CSIZE(x) sdio_bit_slice(x->csd, 128, 69, 48) 70 | 71 | /* 72 | * Conveniently swaps the bytes in a long around 73 | * used by the SCR code. 74 | */ 75 | #define byte_swap(val) \ 76 | asm("rev %[swap], %[swap]" : [swap] "=r" (val) : "0" (val)); 77 | 78 | /* 79 | * sdio_bus 80 | * 81 | * Set the bus width and the clock speed for the 82 | * SDIO bus. 83 | * 84 | * Returns 0 on success 85 | * -1 illegal bit specification 86 | * -2 illegal clock specification 87 | */ 88 | int 89 | sdio_bus(int bits, enum SDIO_CLOCK_DIV freq) { 90 | int clkreg = 0; 91 | 92 | switch (bits) { 93 | case 1: 94 | clkreg |= SDIO_CLKCR_WIDBUS_1; 95 | break; 96 | case 4: 97 | clkreg |= SDIO_CLKCR_WIDBUS_4; 98 | break; 99 | default: 100 | return -1; 101 | } 102 | switch (freq) { 103 | case SDIO_24MHZ: 104 | break; 105 | case SDIO_16MHZ: 106 | clkreg |= 1; 107 | break; 108 | case SDIO_12MHZ: 109 | clkreg |= 2; 110 | break; 111 | case SDIO_8MHZ: 112 | clkreg |= 8; 113 | break; 114 | case SDIO_4MHZ: 115 | clkreg |= 10; 116 | break; 117 | case SDIO_1MHZ: 118 | clkreg |= 46; 119 | break; 120 | case SDIO_400KHZ: 121 | clkreg |= 118; 122 | break; 123 | default: 124 | return -2; 125 | } 126 | clkreg |= SDIO_CLKCR_CLKEN; 127 | SDIO_CLKCR = clkreg; 128 | return 0; 129 | } 130 | 131 | /* 132 | * Set up the GPIO pins and peripheral clocks for the SDIO 133 | * system. The code should probably take an option card detect 134 | * pin, at the moment it uses the one used by the Embest board. 135 | */ 136 | void 137 | sdio_init(void) 138 | { 139 | /* Enable clocks for SDIO and DMA2 */ 140 | rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_SDIOEN); 141 | 142 | #ifdef WITH_DMA2 143 | rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_DMA2EN); 144 | #endif 145 | 146 | gpio_mode_setup(GPIOD, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO12 | GPIO15); 147 | 148 | /* Setup GPIO Pins for SDIO: 149 | PC8 - PC11 - DAT0 thru DAT3 150 | PC12 - CLK 151 | PD2 - CMD 152 | */ 153 | // All SDIO lines are push-pull, 25Mhz 154 | gpio_set_output_options(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, 155 | GPIO12 ); 156 | 157 | // All SDIO lines are push-pull, 25Mhz 158 | gpio_set_output_options(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, 159 | GPIO8 | GPIO9 | GPIO10 | GPIO11 ); 160 | 161 | // D0 - D3 enable pullups (bi-directional) 162 | gpio_mode_setup(GPIOC, GPIO_MODE_AF, GPIO_PUPD_PULLUP, 163 | GPIO8 | GPIO9 | GPIO10 | GPIO11); 164 | // CLK line no pullup 165 | gpio_mode_setup(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO12); 166 | 167 | gpio_set_af(GPIOC, GPIO_AF12, 168 | GPIO8 | GPIO9 | GPIO10 | GPIO11 | GPIO12); 169 | gpio_set_af(GPIOD, GPIO_AF12, GPIO2); 170 | 171 | /* GPIOD setup */ 172 | gpio_set_output_options(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, GPIO2); 173 | gpio_mode_setup(GPIOD, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO2); 174 | 175 | #ifdef SDIO_HAS_CARD_DETECT 176 | /* SDIO Card Detect pin on the Embest Baseboard */ 177 | /* PB15 as a hacked Card Detect (active LOW for card present) */ 178 | gpio_mode_setup(GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO15); 179 | #endif 180 | } 181 | 182 | /* 183 | * Reset the state of the SDIO bus and peripheral. This code tries 184 | * to reset the bus *AND* the card if one is plugged in. The bus 185 | * can be reset by software but the card is reset by powering it down. 186 | * 187 | * The SDIO_POWER_STATE tells the code which state to leave the bus in, 188 | * powered up or powered down. 189 | * 190 | * If the state is POWER_ON, then the bus is reset to 400Khz, 1 bit wide 191 | * which is what he spec requires. Once the type and capabilities of the 192 | * card have been determined, it can be upgraded. 193 | */ 194 | void 195 | sdio_reset(enum SDIO_POWER_STATE state) { 196 | 197 | /* Step 1 power off the interface */ 198 | SDIO_POWER = SDIO_POWER_PWRCTRL_PWROFF; 199 | /* reset the SDIO peripheral interface */ 200 | rcc_peripheral_reset(&RCC_APB2RSTR, RCC_APB2RSTR_SDIORST); 201 | rcc_peripheral_clear_reset(&RCC_APB2RSTR, RCC_APB2RSTR_SDIORST); 202 | if (state == SDIO_POWER_ON) { 203 | SDIO_POWER = SDIO_POWER_PWRCTRL_PWRON; 204 | sdio_bus(1, SDIO_400KHZ); // required by the spec 205 | } 206 | } 207 | 208 | /* 209 | * The error message catalog. 210 | */ 211 | static const char *__sdio_error_msgs[] = { 212 | "Success", 213 | "Command Timeout", // -1 214 | "Command CRC Failure", // -2 215 | "Soft Timeout (No Response)", // -3 216 | "Data CRC Failure", // -4 217 | "RX FIFO Overrun", // -5 218 | "TX FIFO Underrun", // -6 219 | "Unsupported Card" // -7 220 | }; 221 | 222 | #define SDIO_ESUCCESS 0 223 | #define SDIO_ECTIMEOUT -1 224 | #define SDIO_ECCRCFAIL -2 225 | #define SDIO_ENORESP -3 226 | #define SDIO_EDCRCFAIL -4 227 | #define SDIO_ERXOVERR -5 228 | #define SDIO_ETXUNDER -6 229 | #define SDIO_EBADCARD -7 230 | #define SDIO_EUNKNOWN -8 231 | 232 | /* 233 | * Return a text string description of the error code. 234 | */ 235 | const char * 236 | sdio_errmsg(int err) { 237 | return (err <= SDIO_EUNKNOWN) ? (const char *) "Unknown Error" : 238 | __sdio_error_msgs[0-err]; 239 | } 240 | 241 | #ifdef SDIO_LOGGING 242 | 243 | /* 244 | * SDIO_LOGSIZE determines the impact on RAM at 28 * LOG_SIZE + 4 bytes. 245 | */ 246 | #define SDIO_LOGSIZE 10 247 | 248 | static struct log_entry { 249 | uint32_t arg; 250 | uint32_t resp[4]; 251 | int err; 252 | uint8_t cmd; 253 | } sdio_log_buffer[SDIO_LOGSIZE]; 254 | 255 | static int current_sdio_log_entry = 0; 256 | 257 | 258 | /* This is a helper function which keeps a 'log' of the last "n" 259 | * commands and their results, sent through the command API. This 260 | * can be helpful when debugging why something didn't work, you go 261 | * back and look at the previous n commands and their results. 262 | */ 263 | static void 264 | sdio_log(uint8_t cmd, uint32_t arg, int err) 265 | { 266 | sdio_log_buffer[current_sdio_log_entry].cmd = cmd; 267 | sdio_log_buffer[current_sdio_log_entry].arg = arg; 268 | sdio_log_buffer[current_sdio_log_entry].err = err; 269 | sdio_log_buffer[current_sdio_log_entry].resp[0] = SDIO_RESP1; 270 | sdio_log_buffer[current_sdio_log_entry].resp[1] = SDIO_RESP2; 271 | sdio_log_buffer[current_sdio_log_entry].resp[2] = SDIO_RESP3; 272 | sdio_log_buffer[current_sdio_log_entry].resp[3] = SDIO_RESP4; 273 | current_sdio_log_entry = ((current_sdio_log_entry + 1) % SDIO_LOGSIZE); 274 | } 275 | 276 | /* 277 | * Send a log entry out to the indicated UART 278 | * Note this creates a dependency on the uart driver I wrote. 279 | * When logging is not enabled, the SDIO code is independent. 280 | */ 281 | static void 282 | sdio_dump_log_entry(int u, struct log_entry *l) { 283 | int i; 284 | 285 | uart_puts(u, "CMD: "); 286 | uart_putnum(u, FMT_BASE_10, l->cmd); 287 | uart_puts(u, ", ARG: "); 288 | uart_putnum(u, FMT_HEX_LONG | FMT_ALTERNATE_FORM, l->arg); 289 | switch (l->cmd) { 290 | case 9: 291 | case 2: 292 | uart_puts(u, ", RESP: "); 293 | for (i = 0; i < 4; i++) { 294 | uart_putnum(u, FMT_HEX_CONSTANT, l->resp[i]); 295 | uart_puts(u, ", "); 296 | } 297 | break; 298 | case 0: 299 | uart_puts(u, ", "); 300 | break; 301 | default: 302 | uart_puts(u, ", RESP: "); 303 | uart_putnum(u, FMT_HEX_CONSTANT, l->resp[0]); 304 | uart_puts(u, ", "); 305 | } 306 | uart_puts(u, "Error: '"); 307 | uart_puts(u, sdio_errmsg(l->err)); 308 | uart_puts(u, "'\n"); 309 | } 310 | #endif 311 | 312 | /* 313 | * sdio_bit_slice - helper function 314 | * 315 | * A number of the things the SDIO returns are in bit 316 | * fields. This code is designed to slice out a range 317 | * of bits and return them as a value (up to 32 bits 318 | * worth). 319 | */ 320 | uint32_t 321 | sdio_bit_slice(uint32_t a[], int bits, int msb, int lsb) { 322 | uint32_t t; 323 | int i; 324 | 325 | if (((msb >= bits) || (msb < 0)) || 326 | (lsb > msb) || 327 | ((lsb < 0) || (lsb >= bits))) { 328 | DEBUG("Bad Slice values.\n"); 329 | return 0; 330 | } 331 | t = 0; 332 | for (i = msb; i > lsb; i--) { 333 | t |= (a[((bits-1) - i)/32] >> (i % 32)) & 0x1; 334 | t <<= 1; 335 | } 336 | t |= (a[((bits-1) - lsb)/32] >> (lsb % 32)) & 0x1; 337 | return t; 338 | } 339 | 340 | /* 341 | * A convienence define. These are the flags we care about when 342 | * sending a command. During command processing SDIO_STA_CMDACT 343 | * will be set. 344 | */ 345 | #define COMMAND_FLAGS (SDIO_STA_CMDSENT |\ 346 | SDIO_STA_CCRCFAIL |\ 347 | SDIO_STA_CMDREND |\ 348 | SDIO_STA_CTIMEOUT) 349 | 350 | /* 351 | * Send a command over the SDIO bus. 352 | * Passed a command (8 bit value) and an argument (32 bit value) 353 | * This command figures out if the command will return a short (32 bit) 354 | * or long (64 bit) response. It is up to the calling program to pull 355 | * data from the long response commands. 356 | * Passed: 357 | * cmd - Command to execute 358 | * arg - Argument to pass to the command 359 | * buf - pointer to a long aligned buffer if data 360 | * len - expected length of buffer (in bytes) 361 | */ 362 | int 363 | sdio_command(uint32_t cmd, uint32_t arg) 364 | { 365 | uint32_t tmp_val; 366 | int error = 0; 367 | 368 | tmp_val = SDIO_CMD & ~0x7ff; // Read pre-existing state 369 | tmp_val |= (cmd & SDIO_CMD_CMDINDEX_MSK); // Put the Command in 370 | tmp_val |= SDIO_CMD_CPSMEN; // We'll be running CPSM 371 | 372 | switch(cmd) { 373 | case 0: 374 | tmp_val |= SDIO_CMD_WAITRESP_NO_0; 375 | break; 376 | case 2: 377 | case 9: 378 | tmp_val |= SDIO_CMD_WAITRESP_LONG; 379 | break; 380 | default: 381 | tmp_val |= SDIO_CMD_WAITRESP_SHORT; // the common case 382 | break; 383 | } 384 | /* If a data transaction is in progress, wait for it to finish */ 385 | #if 0 386 | if ((cmd != 12) && (SDIO_STA & (SDIO_STA_RXACT | SDIO_STA_TXACT))) { 387 | // XXX: This should be an error, we don't have multithread 388 | tmp_val |= SDIO_CMD_WAITPEND; 389 | } 390 | #endif 391 | 392 | /* 393 | * EXECUTE: 394 | * o Reset all status bits 395 | * o Put ARG into SDIO ARG 396 | * o reset the error indicator 397 | * o Enable all interrupts. 398 | * o Do the command 399 | */ 400 | SDIO_ICR = 0x7ff; // Reset everything that isn't bolted down. 401 | SDIO_ARG = arg; 402 | SDIO_CMD = tmp_val; 403 | /* 404 | * In a polled mode we should be able to just read the status bits 405 | * directly. 406 | */ 407 | tmp_val = 0; 408 | do { 409 | tmp_val |= (SDIO_STA & 0x7ff); 410 | } while ((SDIO_STA & SDIO_STA_CMDACT) || (! tmp_val));; 411 | SDIO_ICR = tmp_val; 412 | 413 | /* 414 | * Compute the error here. Which can be one of 415 | * -- Success (either CMDSENT or CMDREND depending on response) 416 | * -- Timeout (based on CTIMEOUT) 417 | * -- No Response (based on no response in the time alloted) 418 | * -- CRC Error (based on CCRCFAIL) 419 | */ 420 | if (! tmp_val) { 421 | error = SDIO_ENORESP; 422 | } else if (tmp_val & SDIO_STA_CCRCFAIL) { 423 | error = SDIO_ECCRCFAIL; 424 | } else if (tmp_val & (SDIO_STA_CMDREND | SDIO_STA_CMDSENT)) { 425 | error = SDIO_ESUCCESS; 426 | } else if (tmp_val & SDIO_STA_CTIMEOUT) { 427 | error = SDIO_ECTIMEOUT; 428 | } else { 429 | error = SDIO_EUNKNOWN; 430 | } 431 | 432 | #ifdef SDIO_LOGGING 433 | // Note the result in our short log 434 | sdio_log(cmd, arg, error); 435 | #endif 436 | return error; 437 | } 438 | 439 | 440 | /* our static data buffer we use for data movement commands */ 441 | static uint32_t data_buf[129]; 442 | static int data_len; 443 | 444 | /* 445 | * Helper function - sdio_select 446 | * 447 | * This function "selects" a card using CMD7, note that if 448 | * you select card 0 that deselects the card (RCA is not allowed 449 | * to be 0) 450 | */ 451 | static int 452 | sdio_select(int rca) { 453 | int err; 454 | 455 | err = sdio_command(7, rca << 16); 456 | if ((rca == 0) && (err == SDIO_ECTIMEOUT)) { 457 | return 0; // "cheat" a timeout selecting 0 is a successful deselect 458 | } 459 | return err; 460 | } 461 | 462 | /* 463 | * Helper function - sdio_scr 464 | * 465 | * Unlike the CID and CSD functions this function transfers data 466 | * so it needs to use the DPSM. 467 | * 468 | * Note that data over the wire is byte swapped so we swap it back 469 | * to "fix" it. 470 | * 471 | * Note when this return 0 the first two longs in the data_buf are 472 | * the SCR register. 473 | */ 474 | 475 | static int 476 | sdio_scr(SDIO_CARD c) { 477 | int err; 478 | uint32_t tmp_reg; 479 | int ndx; 480 | 481 | /* Select the card */ 482 | err = sdio_select(c->rca); 483 | if (! err) { 484 | /* Set the Block Size */ 485 | err = sdio_command(16, 8); 486 | if (! err) { 487 | /* APPCMD (our RCA) */ 488 | err = sdio_command(55, c->rca << 16); 489 | if (! err) { 490 | SDIO_DTIMER = 0xffffffff; 491 | SDIO_DLEN = 8; 492 | SDIO_DCTRL = SDIO_DCTRL_DBLOCKSIZE_3 | 493 | SDIO_DCTRL_DTDIR | 494 | SDIO_DCTRL_DTEN; 495 | /* ACMD51 - Send SCR */ 496 | err = sdio_command(51, 0); 497 | if (! err) { 498 | data_len = 0; 499 | do { 500 | tmp_reg = SDIO_STA; 501 | if (tmp_reg & SDIO_STA_RXDAVL) { 502 | data_buf[data_len++] = SDIO_FIFO; 503 | } 504 | } while (tmp_reg & SDIO_STA_RXACT); 505 | if ((tmp_reg & SDIO_STA_DBCKEND) == 0) { 506 | if (tmp_reg & SDIO_STA_DCRCFAIL) { 507 | err = SDIO_EDCRCFAIL; 508 | } else if (tmp_reg & SDIO_STA_RXOVERR) { 509 | err = SDIO_ERXOVERR; 510 | } else { 511 | err = SDIO_EUNKNOWN; // XXX: unknown error 512 | } 513 | } 514 | if (! err) { 515 | for (ndx = 0; ndx < 2; ndx++) { 516 | byte_swap(data_buf[ndx]); 517 | c->scr[ndx] = data_buf[ndx]; 518 | } 519 | } 520 | } 521 | } 522 | } 523 | } 524 | (void) sdio_select(0); 525 | return err; 526 | } 527 | 528 | /* 529 | * Read a Block from our Card 530 | * 531 | * NB: There is a possibly useless test in this code, during the read 532 | * phase it allows that the SDIO card might try to send more than 512 533 | * bytes (128 32 bit longs) and allows it to do so, constantly over 534 | * writing the last long in the just-in-case-over-long-by-1 data buffer. 535 | * To compromise the system you would need a borked or custom crafted 536 | * sdio card which did that. 537 | */ 538 | int 539 | sdio_readblock(SDIO_CARD c, uint32_t lba, uint8_t *buf) { 540 | int err; 541 | uint32_t tmp_reg; 542 | uint32_t addr = lba; 543 | uint8_t *t; 544 | int ndx; 545 | 546 | if (! SDIO_CARD_CCS(c)) { 547 | addr = lba * 512; // non HC cards use byte address 548 | } 549 | err = sdio_select(c->rca); 550 | if (! err) { 551 | err = sdio_command(16, 512); 552 | if (!err) { 553 | SDIO_DTIMER = 0xffffffff; 554 | SDIO_DLEN = 512; 555 | SDIO_DCTRL = SDIO_DCTRL_DBLOCKSIZE_9 | 556 | SDIO_DCTRL_DTDIR | 557 | SDIO_DCTRL_DTEN; 558 | err = sdio_command(17, addr); 559 | if (! err) { 560 | data_len = 0; 561 | do { 562 | tmp_reg = SDIO_STA; 563 | if (tmp_reg & SDIO_STA_RXDAVL) { 564 | data_buf[data_len] = SDIO_FIFO; 565 | if (data_len < 128) { 566 | ++data_len; 567 | } 568 | } 569 | } while (tmp_reg & SDIO_STA_RXACT); 570 | if ((tmp_reg & SDIO_STA_DBCKEND) == 0) { 571 | if (tmp_reg & SDIO_STA_DCRCFAIL) { 572 | err = SDIO_EDCRCFAIL; 573 | } else if (tmp_reg & SDIO_STA_RXOVERR) { 574 | err = SDIO_ERXOVERR; 575 | } else { 576 | err = SDIO_EUNKNOWN; // Unknown Error! 577 | } 578 | } else { 579 | /* Data received, byte swap and put in user 580 | * supplied buffer. 581 | */ 582 | #if 0 583 | for (ndx = 0; ndx < data_len; ndx++) { 584 | byte_swap(data_buf[ndx]); 585 | } 586 | #endif 587 | t = (uint8_t *)(data_buf); 588 | /* copy out to the user buffer */ 589 | for (ndx = 0; ndx < 512; ndx ++) { 590 | *buf = *t; 591 | buf++; 592 | t++; 593 | } 594 | } 595 | } 596 | } 597 | } 598 | // deselect the card 599 | (void) sdio_select(0); 600 | return err; 601 | } 602 | 603 | /* 604 | * Write a Block from our Card 605 | */ 606 | int 607 | sdio_writeblock(SDIO_CARD c, uint32_t lba, uint8_t *buf) { 608 | int err; 609 | uint32_t tmp_reg; 610 | uint32_t addr = lba; 611 | uint8_t *t; 612 | int ndx; 613 | 614 | if (! SDIO_CARD_CCS(c)) { 615 | addr = lba * 512; // non HC cards use byte address 616 | } 617 | 618 | /* 619 | * Copy buffer to our word aligned buffer. Nominally you 620 | * can just use the passed in buffer and cast it to a 621 | * uint32_t * but that can cause issues if it isn't 622 | * aligned. 623 | */ 624 | t = (uint8_t *)(data_buf); 625 | for (ndx = 0; ndx < 512; ndx ++) { 626 | *t = *buf; 627 | buf++; 628 | t++; 629 | } 630 | err = sdio_select(c->rca); 631 | if (! err) { 632 | /* Set Block Size to 512 */ 633 | err = sdio_command(16, 512); 634 | if (!err) { 635 | SDIO_DTIMER = 0xffffffff; 636 | SDIO_DLEN = 512; 637 | SDIO_DCTRL = SDIO_DCTRL_DBLOCKSIZE_9 | 638 | SDIO_DCTRL_DTEN; 639 | err = sdio_command(24, addr); 640 | if (! err) { 641 | data_len = 0; 642 | do { 643 | tmp_reg = SDIO_STA; 644 | if (tmp_reg & SDIO_STA_TXFIFOHE) { 645 | SDIO_FIFO = data_buf[data_len]; 646 | if (data_len < 128) { 647 | ++data_len; 648 | } 649 | } 650 | } while (tmp_reg & SDIO_STA_TXACT); 651 | if ((tmp_reg & SDIO_STA_DBCKEND) == 0) { 652 | if (tmp_reg & SDIO_STA_DCRCFAIL) { 653 | err = SDIO_EDCRCFAIL; 654 | } else if (tmp_reg & SDIO_STA_TXUNDERR) { 655 | err = SDIO_ETXUNDER; 656 | } else { 657 | err = SDIO_EUNKNOWN; // Unknown Error! 658 | } 659 | } 660 | } 661 | } 662 | } 663 | // deselect the card 664 | (void) sdio_select(0); 665 | return err; 666 | } 667 | 668 | /* 669 | * sdio-status - Get Card Status page 670 | * 671 | * This function fetches the SD Card Status page and 672 | * copies it into the CARD structure. 673 | */ 674 | int 675 | sdio_status(SDIO_CARD c) { 676 | uint32_t tmp_reg; 677 | int ndx; 678 | int err; 679 | 680 | err = sdio_select(c->rca); 681 | if (! err) { 682 | err = sdio_command(16, 64); 683 | if (! err) { 684 | err = sdio_command(55, c->rca << 16); 685 | if (! err) { 686 | SDIO_DTIMER = 0xffffffff; 687 | SDIO_DLEN = 64; 688 | SDIO_DCTRL = SDIO_DCTRL_DBLOCKSIZE_6 | 689 | SDIO_DCTRL_DTDIR | 690 | SDIO_DCTRL_DTEN; 691 | /* ACMD13 - Send Status Reg */ 692 | err = sdio_command(13, 0); 693 | if (! err) { 694 | data_len = 0; 695 | do { 696 | tmp_reg = SDIO_STA; 697 | if (tmp_reg & SDIO_STA_RXDAVL) { 698 | data_buf[data_len] = SDIO_FIFO; 699 | if (data_len < 128) { 700 | ++data_len; 701 | } 702 | } 703 | } while (tmp_reg & SDIO_STA_RXACT); 704 | if ((tmp_reg & SDIO_STA_DBCKEND) == 0) { 705 | if (tmp_reg & SDIO_STA_DCRCFAIL) { 706 | err = SDIO_EDCRCFAIL; 707 | } else if (tmp_reg & SDIO_STA_RXOVERR) { 708 | err = SDIO_ERXOVERR; 709 | } else { 710 | err = SDIO_EUNKNOWN; // Unknown Error! 711 | } 712 | } else { 713 | for (ndx = 0; ndx < 16; ndx++) { 714 | byte_swap(data_buf[ndx]); 715 | c->status[ndx] = data_buf[ndx]; 716 | } 717 | } 718 | (void) sdio_select(0); 719 | } 720 | } 721 | } 722 | } 723 | return err; 724 | } 725 | 726 | static struct SDIO_CARD_DATA __sdio_card_data; 727 | #define MAX_RETRIES 5 728 | 729 | /* 730 | * sdio_open - Prepare to use SDIO card 731 | * 732 | * This function resets the SDIO bus and identifies the 733 | * card (if any) that is plugged in. If there is no card 734 | * present, or an error in figuring out what the card is 735 | * (for example its an old MMC card) the function returns 736 | * NULL. If it fails and you have logging enabled you can 737 | * look at the last few commands sent. 738 | */ 739 | SDIO_CARD 740 | sdio_open(void) { 741 | int err; 742 | int i; 743 | uint8_t *t; 744 | uint32_t tmp_reg; 745 | SDIO_CARD res = &__sdio_card_data; 746 | 747 | // basically bset(0, __sdio_card_data) 748 | t = (uint8_t *) &__sdio_card_data; 749 | for (i = 0; i < (int) sizeof(__sdio_card_data); i++) { 750 | *t++ = 0; 751 | } 752 | sdio_reset(SDIO_POWER_ON); 753 | err = sdio_command(0, 0); 754 | if (!err) { 755 | err = sdio_command(8, 0x1aa); 756 | if (!err) { 757 | // Woot! We support CMD8 so we're a v2 card at least */ 758 | tmp_reg = SDIO_RESP1; 759 | __sdio_card_data.props = 1; 760 | i = 0; 761 | err = sdio_command(5, 0); 762 | if (! err) { 763 | // It is an SDIO card which is unsupported! 764 | err = SDIO_EBADCARD; 765 | return NULL; 766 | } 767 | do { 768 | err = sdio_command(55, 0); // broadcast ACMD 769 | if (err) { 770 | break; // try again 771 | } 772 | // Testing Card Busy, Voltage match, and capacity 773 | err = sdio_command(41, 0xc0100000); 774 | if (err != -2) { // Expect CCRCFAIL here 775 | break; // try again 776 | } 777 | tmp_reg = SDIO_RESP1; // what did the card send? 778 | if ((tmp_reg & 0x80000000) == 0) { 779 | continue; // still powering up 780 | } 781 | res->ocr = tmp_reg; // Ok OCR is valid 782 | break; 783 | } while (++i < MAX_RETRIES); 784 | if (res->ocr) { 785 | err = sdio_command(2, 0); 786 | if (! err) { 787 | res->cid[0] = SDIO_RESP1; 788 | res->cid[1] = SDIO_RESP2; 789 | res->cid[2] = SDIO_RESP3; 790 | res->cid[3] = SDIO_RESP4; 791 | err = sdio_command(3, 0); // get the RCA 792 | if (! err) { 793 | tmp_reg = SDIO_RESP1; 794 | res->rca = (tmp_reg >> 16) & 0xffff; 795 | if (! res->rca) { 796 | /* 797 | * If the card says '0' tell it to pick 798 | * we assume this will work because the 799 | * previous send RCA worked and the card 800 | * should be in the ident state if it is 801 | * functioning correctly. 802 | */ 803 | (void) sdio_command(3, 0); // try again 804 | tmp_reg = SDIO_RESP1; 805 | res->rca = (tmp_reg >> 16) & 0xffff; 806 | } 807 | err = sdio_command(9, res->rca << 16); 808 | if (! err) { 809 | res->csd[0] = SDIO_RESP1; 810 | res->csd[1] = SDIO_RESP2; 811 | res->csd[2] = SDIO_RESP3; 812 | res->csd[3] = SDIO_RESP4; 813 | err = sdio_scr(res); // Capture the SCR 814 | if (! err) { 815 | /* All SD Cards support 4 bit bus and 24Mhz */ 816 | err = sdio_select(res->rca); 817 | if (! err) { 818 | err = sdio_command(55, res->rca << 16); 819 | if (! err) { 820 | err = sdio_command(6, 2); 821 | if (! err) { 822 | sdio_bus(4, SDIO_24MHZ); 823 | (void) sdio_select(0); 824 | } 825 | } 826 | } 827 | } 828 | } 829 | } 830 | } 831 | } 832 | } 833 | } 834 | /* Compute the size of the card based on fields in the CSD 835 | * block. There are two kinds, V1 or V2. 836 | * In the V1 Case : 837 | * Size = 1<size = 0; 847 | switch (SDIO_CSD_VERSION(res)) { 848 | case 0: 849 | tmp_reg = ((1 << (SDIO_CSD1_CSIZE_MULT(res) + 2)) * 850 | ( 1 << SDIO_CSD1_RBLKLEN(res))) >> 9; 851 | res->size = tmp_reg * (SDIO_CSD1_CSIZE(res) + 1); 852 | break; 853 | case 1: 854 | res->size = (SDIO_CSD2_CSIZE(res)+1) << 10; 855 | break; 856 | default: 857 | res->size = 0; // Bug if its not CSD V1 or V2 858 | } 859 | } 860 | return (err == 0) ? res : NULL; 861 | } 862 | 863 | void 864 | sdio_print_log(int dev, int nrec) { 865 | #ifdef SDIO_LOGGING 866 | int ent = current_sdio_log_entry; // global for the log 867 | int i; 868 | int delta = 0; 869 | for (i = 0; i < nrec; i++, delta--) { 870 | ent = (ent) ? ent - 1 : SDIO_LOGSIZE - 1; 871 | text_color(dev, YELLOW); 872 | uart_putnum(dev, FMT_BASE_10 | FMT_SIGNED, delta); 873 | uart_puts(dev, ": "); 874 | text_color(dev, DEFAULT); 875 | sdio_dump_log_entry(dev, &sdio_log_buffer[ent]); 876 | } 877 | #else 878 | uart_puts(dev, "Sorry Logging was not compiled in.\n"); 879 | return; 880 | #endif 881 | } 882 | -------------------------------------------------------------------------------- /uart.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 - Chuck McManis, all rights reserved. 3 | * 4 | * A slightly more complex UART/USART driver in order to 5 | * facilitate multiple character streams. When debugging 6 | * this example I found it useful to have a couple of 7 | * streams going. Note that debugging it under GDB is problematic 8 | * as GDB (at least in SWD mode) doesn't seem to allow interrupts 9 | * to happen while it is running. 10 | */ 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "uart.h" 17 | #include "clock.h" 18 | #include "debug.h" 19 | 20 | #ifndef NULL 21 | #define NULL (char *)(0) 22 | #endif 23 | 24 | /* Until libopencm3 gets defines for the STM32F42[79] which have 25 | * two additional UARTS 26 | */ 27 | 28 | #ifndef UART7 29 | #include "stm32f42x_uart.h" 30 | #endif 31 | 32 | /* This defines how many channels we can have configured/opened at once */ 33 | #ifndef MAX_UART_CHANNELS 34 | #define MAX_UART_CHANNELS 2 35 | #endif 36 | 37 | /* These are the attributes we can fetch with pin_map() */ 38 | enum PIN_ATTRIBUTE { 39 | USART, APB_REG, APB_ENA, BIT, GPIO_ENA, IRQ, AF, GPIO 40 | }; 41 | 42 | /* 43 | * Forward Prototypes 44 | */ 45 | static void common_usart_isr(uint32_t, int); 46 | 47 | /* This function is at the bottom of the file which helps map info from the PIN 48 | * to the various constants needed by the initialization function. 49 | */ 50 | static uint32_t uart_pin_map(enum UART_PORT_PIN pin, enum PIN_ATTRIBUTE attr); 51 | 52 | /* 53 | * We hold buffers for all possible ports that can 54 | * consume more ram than you would like, if so feel 55 | * free to change this define. Recommend you keep it at 56 | * least at 16 though. 57 | */ 58 | #define UART_BUF_SIZE 32 59 | 60 | static volatile char recv_buf[MAX_UART_CHANNELS][UART_BUF_SIZE]; 61 | static volatile char xmit_buf[MAX_UART_CHANNELS][UART_BUF_SIZE]; 62 | static volatile uint16_t nxt_recv_ndx[MAX_UART_CHANNELS]; 63 | static volatile uint16_t cur_recv_ndx[MAX_UART_CHANNELS]; 64 | static volatile uint16_t nxt_xmit_ndx[MAX_UART_CHANNELS]; 65 | static volatile uint16_t cur_xmit_ndx[MAX_UART_CHANNELS]; 66 | 67 | #define NUARTS 8 // Total possible uarts; 68 | 69 | /* This maps each UART to the channel it was assigned to */ 70 | static int uart_to_channel_map[NUARTS]; 71 | /* This maps each channel to the UART serving it */ 72 | static int channel_to_uart_map[MAX_UART_CHANNELS]; 73 | 74 | /* This is the order of UARTs in the uart_to_channel_map */ 75 | static const uint32_t UART_MAP[] = { 76 | USART1, 77 | USART2, 78 | USART3, 79 | UART4, 80 | UART5, 81 | USART6, 82 | UART7, 83 | UART8 84 | }; 85 | 86 | /* 87 | * Common USART/UART transmit and receive interrupts are 88 | * collated to here. Each serial port simply redirects to here 89 | * while passing in its BASE address in peripheral space and the 90 | * channel # it has been assigned. The channel number mapping is 91 | * established at initialization time. 92 | */ 93 | void 94 | common_usart_isr(uint32_t usart, int channel) { 95 | if (USART_SR(usart) & USART_SR_RXNE) { 96 | recv_buf[channel][nxt_recv_ndx[channel]] = USART_DR(usart); 97 | nxt_recv_ndx[channel] = (nxt_recv_ndx[channel] + 1) % UART_BUF_SIZE; 98 | } 99 | if (USART_SR(usart) & USART_SR_TXE) { 100 | if (nxt_xmit_ndx[channel] == cur_xmit_ndx[channel]) { 101 | usart_disable_tx_interrupt(usart); // nothing to send 102 | } else { 103 | USART_DR(usart) = xmit_buf[channel][cur_xmit_ndx[channel]]; 104 | cur_xmit_ndx[channel] = (cur_xmit_ndx[channel] + 1) % UART_BUF_SIZE; 105 | } 106 | } 107 | } 108 | 109 | /* Individual ISR's for the serial ports just redirect */ 110 | void usart1_isr() { 111 | common_usart_isr(USART1, uart_to_channel_map[0]); 112 | } 113 | 114 | void usart2_isr() { 115 | common_usart_isr(USART2, uart_to_channel_map[1]); 116 | } 117 | 118 | void usart3_isr() { 119 | common_usart_isr(USART3, uart_to_channel_map[2]); 120 | } 121 | 122 | void uart4_isr() { 123 | common_usart_isr(UART4_BASE, uart_to_channel_map[3]); 124 | } 125 | 126 | void uart5_isr() { 127 | common_usart_isr(UART5_BASE, uart_to_channel_map[4]); 128 | } 129 | 130 | void usart6_isr() { 131 | common_usart_isr(USART6, uart_to_channel_map[5]); 132 | } 133 | 134 | void uart7_isr() { 135 | common_usart_isr(UART7, uart_to_channel_map[6]); 136 | } 137 | 138 | void uart8_isr() { 139 | common_usart_isr(UART8, uart_to_channel_map[7]); 140 | } 141 | 142 | /* Next available channel to configure */ 143 | static int nxt_channel = 0; 144 | 145 | /* 146 | * uart_init(tx pin, rx pin, baudrate); 147 | * 148 | * Initialize a UART that will talk on the tx/rx pin pair at a given baudrate 149 | * Curently only 8n1 format, no-hw flow control, only. 150 | * 151 | * Returns channel unumber (0 - MAX_UART_CHANNELS) or -1 on error 152 | */ 153 | int 154 | uart_init(enum UART_PORT_PIN tx, enum UART_PORT_PIN rx, int baudrate) { 155 | uint32_t my_uart = uart_pin_map(tx, USART); 156 | int i; 157 | 158 | if (uart_pin_map(rx, USART) != my_uart) { 159 | /* Both pins are not connected to same serial port */ 160 | return -1; 161 | } 162 | if (nxt_channel >= MAX_UART_CHANNELS) { 163 | /* Need more channel configured */ 164 | return -2; 165 | } 166 | 167 | /* Enable Clock for the USART/UART involved */ 168 | rcc_peripheral_enable_clock((uint32_t *)uart_pin_map(tx, APB_REG), 169 | uart_pin_map(tx, APB_ENA)); 170 | 171 | /* Enable Clock for the GPIOs we are using */ 172 | rcc_peripheral_enable_clock(&RCC_AHB1ENR, uart_pin_map(tx, GPIO_ENA)); 173 | if (uart_pin_map(rx,GPIO_ENA) != uart_pin_map(tx,GPIO_ENA)) { 174 | rcc_peripheral_enable_clock(&RCC_AHB1ENR, uart_pin_map(rx, GPIO_ENA)); 175 | } 176 | 177 | /* GPIO pins */ 178 | 179 | /* Both AF Mode */ 180 | gpio_mode_setup(uart_pin_map(tx, GPIO), GPIO_MODE_AF, GPIO_PUPD_NONE, 181 | uart_pin_map(tx, BIT)); 182 | gpio_mode_setup(uart_pin_map(rx, GPIO), GPIO_MODE_AF, GPIO_PUPD_NONE, 183 | uart_pin_map(rx, BIT)); 184 | gpio_set_af(uart_pin_map(tx, GPIO), 185 | uart_pin_map(tx, AF), 186 | uart_pin_map(tx, BIT)); 187 | 188 | gpio_set_af(uart_pin_map(rx, GPIO), 189 | uart_pin_map(rx, AF), 190 | uart_pin_map(rx, BIT)); 191 | 192 | /* Transmit pin set to an output */ 193 | gpio_set_output_options(uart_pin_map(tx, GPIO), GPIO_OTYPE_PP, 194 | GPIO_OSPEED_25MHZ, uart_pin_map(tx, BIT)); 195 | 196 | 197 | /* Set up UART parameters */ 198 | usart_set_baudrate(my_uart, baudrate); 199 | usart_set_databits(my_uart, 8); 200 | usart_set_stopbits(my_uart, USART_STOPBITS_1); 201 | usart_set_mode(my_uart, USART_MODE_TX_RX); 202 | usart_set_parity(my_uart, USART_PARITY_NONE); 203 | usart_set_flow_control(my_uart, USART_FLOWCONTROL_NONE); 204 | usart_enable(my_uart); 205 | 206 | nxt_recv_ndx[nxt_channel] = cur_recv_ndx[nxt_channel] = 0; 207 | nxt_xmit_ndx[nxt_channel] = cur_xmit_ndx[nxt_channel] = 0; 208 | /* 209 | * This was done to try to get it to run under GDB with the Black 210 | * Magic debug probe but it didn't have any effect (interrupts 211 | * are still masked) 212 | */ 213 | nvic_set_priority(uart_pin_map(tx, IRQ), 0); // highest priority 214 | nvic_enable_irq(uart_pin_map(tx, IRQ)); 215 | USART_DR(my_uart) = 0; 216 | usart_enable_rx_interrupt(my_uart); 217 | 218 | /* Now create two mappings, channel => usart, and usart => channel */ 219 | channel_to_uart_map[nxt_channel] = my_uart; 220 | for (i = 0; i < NUARTS; i++) { 221 | if (UART_MAP[i] == my_uart) { 222 | uart_to_channel_map[i] = nxt_channel; 223 | break; 224 | } 225 | } 226 | nxt_channel++; 227 | return (nxt_channel - 1); 228 | } 229 | 230 | /* 231 | * This is a small hack which takes function keys (arrow keys, etc) 232 | * and returns a 'character' for them. It waits briefly to (2mS) to see 233 | * if another character is going to follow an ESC and if it does then 234 | * it trys to collect the entire escape sequence which can be translated 235 | * into a function key (or special key). 236 | */ 237 | static uint8_t fkey_buf[5]; 238 | static int fkey_ndx; 239 | 240 | static uint8_t map_function_key(void); 241 | 242 | /* 243 | * Ok, if we got a function key sequence we look here to map it 244 | * into an 8 bit return value. It was simply $[ we take the 245 | * character returned and turn on bit 7. 246 | */ 247 | static uint8_t 248 | map_function_key(void) { 249 | if (fkey_ndx == 1) { 250 | return 0x80 | fkey_buf[1]; 251 | } 252 | return 0xff; 253 | } 254 | 255 | /* --- "high" level APIs (channel based) --- 256 | * 257 | * uart_getc(channel, wait) 258 | * 259 | * If wait is non-zero the function blocks until a character 260 | * is available. If wait is 0 then the function returns immediately 261 | * with '0' as the value. (yes this means it can't read 'nul' bytes 262 | * interactively) 263 | */ 264 | char 265 | uart_getc(int channel, int wait) { 266 | char res; 267 | 268 | if ((cur_recv_ndx[channel] == nxt_recv_ndx[channel]) && ! wait) { 269 | return '\000'; 270 | } 271 | while (cur_recv_ndx[channel] == nxt_recv_ndx[channel]) { 272 | __asm__("NOP"); 273 | } 274 | res = recv_buf[channel][cur_recv_ndx[channel]]; 275 | cur_recv_ndx[channel] = (cur_recv_ndx[channel] + 1) % UART_BUF_SIZE; 276 | /* check for function key */ 277 | /* as written this code depends on the function key 278 | * string coming in at full speed and there being a 279 | * slight delay with the next character, but that might 280 | * not be a good assumption. We'll see if it is good enough 281 | * for now. 282 | * 283 | * In testing 2 mS sleeps to be sure another character isn't 284 | * coming in seems to be ok, also you don't normally type the CSI 285 | * sequence (^[ [) on your own. you can "fool" the input driver 286 | * if you do into trying to interpret a function key sequence with 287 | * unpredictable results. 288 | */ 289 | fkey_ndx = 0; 290 | if (res == 27) { 291 | msleep(2); 292 | if ((cur_recv_ndx[channel] != nxt_recv_ndx[channel]) && 293 | (recv_buf[channel][cur_recv_ndx[channel]] == 91)) { 294 | for (fkey_ndx = 0; fkey_ndx < 5; fkey_ndx++) { 295 | fkey_buf[fkey_ndx] = recv_buf[channel][cur_recv_ndx[channel]]; 296 | cur_recv_ndx[channel] = (cur_recv_ndx[channel] + 1) % 297 | UART_BUF_SIZE; 298 | msleep(2); 299 | if (cur_recv_ndx[channel] == nxt_recv_ndx[channel]) { 300 | break; // no more characters 301 | } 302 | } 303 | } 304 | } 305 | return (fkey_ndx == 0) ? res : map_function_key(); 306 | } 307 | 308 | /* 309 | * char *uart_gets() 310 | * 311 | * This is the 'old school' getstring function. It allows for basic 312 | * editng (delete or backspace) and returns on the receipt of . 313 | * It null terminates the string as it builds it and returns if it 314 | * runs out of space, it also returns if the is typed, and sends 315 | * ^G (BELL) if the input tries to delete before the start of the 316 | * buffer. 317 | */ 318 | char * 319 | uart_gets(int chan, char *buf, int len) { 320 | char *line_buf = buf; 321 | char c; 322 | 323 | *line_buf = '\000'; 324 | // read until you see a 325 | while (1) { 326 | c = uart_getc(chan, 1); 327 | if (c == '\r') { 328 | uart_putc(chan, '\r'); 329 | uart_putc(chan, '\n'); 330 | break; 331 | } 332 | // if you see a ^H or ^? () delete last character 333 | if ((c == '\010') || (c == '\177')) { 334 | if (line_buf > buf) { 335 | uart_puts(chan, "\010 \010"); // back up one space 336 | if (line_buf > buf) { 337 | line_buf--; 338 | } 339 | *line_buf = '\000'; 340 | } else { 341 | uart_putc(chan, '\a'); // beep the console 342 | } 343 | } else { 344 | uart_putc(chan, c); 345 | *line_buf = c; 346 | line_buf++; 347 | // if you're at the end of the buffer just return 348 | if (line_buf < (buf + len)) { 349 | *line_buf = '\000'; 350 | } else { 351 | --line_buf; 352 | *line_buf = '\000'; 353 | break; 354 | } 355 | } 356 | } 357 | return buf; 358 | } 359 | 360 | /* 361 | * uart_putc 362 | * 363 | * This pushes a character into the transmit buffer for 364 | * the channel and turns on TX interrupts (which will fire 365 | * because initially the register will be empty.) If the 366 | * ISR sends out the last character it turns off the transmit 367 | * interrupt flag, so that it won't keep firing on an empty 368 | * transmit buffer. 369 | */ 370 | void 371 | uart_putc(int chan, char c) { 372 | uint32_t usart = channel_to_uart_map[chan]; 373 | 374 | /* block if the transmit buffer is full */ 375 | while (((nxt_xmit_ndx[chan] + 1) % UART_BUF_SIZE) == cur_xmit_ndx[chan]) { 376 | __asm__("NOP"); 377 | } 378 | xmit_buf[chan][nxt_xmit_ndx[chan]] = c; 379 | nxt_xmit_ndx[chan] = (nxt_xmit_ndx[chan] + 1) % UART_BUF_SIZE; 380 | usart_enable_tx_interrupt(usart); 381 | } 382 | 383 | /* 384 | * uart_puts(char *string) 385 | * 386 | * Write a NUL terminated string to the UART. This routine writes 387 | * the characters in order (adding LF to CR). Note that line cleanup 388 | * is done here and not in putc so that you can send just \r or what 389 | * ever you want through putc without worrying about it. 390 | */ 391 | void 392 | uart_puts(int chan, const char *s) { 393 | if (s == NULL) 394 | return; 395 | 396 | while (*s) { 397 | if (*s == '\n') { 398 | uart_putc(chan, '\r'); 399 | } 400 | uart_putc(chan, *s++); 401 | } 402 | } 403 | 404 | /* 405 | * UART Pin Mapping 406 | * 407 | * To faciliate a very flexible USART/UART configuration utility 408 | * I needed a way to encode the relationship of various pins to 409 | * their USART registers, there GPIO pins, the various AHB and APB 410 | * registers. There are probably a zillion ways to do this but I 411 | * chose to create code which has embedded in it the notion of what 412 | * goes where. So that I could query it with a simple API. 413 | * 414 | * The function uart_pin_map takes a UART_PORT_PIN and can return 415 | * information about it like which GPIO it needes enabled, what flag 416 | * in the APB register it needs on, etc. It does not care if you use 417 | * the TX pin out of one GPIO port and the RX pin out of another. 418 | * 419 | * The only ambiguity is UART4/USART3 which share TX and RX pins 420 | * and differ only by their Alternate function value (7 or 8). So 421 | * we don't support UART4 on those pins (only USART3). 422 | */ 423 | 424 | /* 425 | * Prototypes for helper functions 426 | */ 427 | 428 | static uint32_t pin_to_bit(enum UART_PORT_PIN pin); 429 | static uint32_t pin_to_enable(enum UART_PORT_PIN pin); 430 | static uint32_t pin_uart_attr(enum UART_PORT_PIN pin, int attr); 431 | static uint32_t pin_to_af(enum UART_PORT_PIN pin); 432 | static uint32_t pin_to_gpio(enum UART_PORT_PIN pin); 433 | 434 | /* A really simple one, this maps the name (like PA0) to the bit number 435 | * define like GPIO0. 436 | */ 437 | static uint32_t 438 | pin_to_bit(enum UART_PORT_PIN pin) { 439 | switch (pin) { 440 | case PA0: 441 | case PE0: return GPIO0; 442 | case PA1: 443 | case PE1: return GPIO1; 444 | case PA2: 445 | case PD2: return GPIO2; 446 | case PA3: return GPIO3; 447 | case PD5: return GPIO5; 448 | case PB6: 449 | case PC6: 450 | case PD6: 451 | case PF6: return GPIO6; 452 | case PB7: 453 | case PC7: 454 | case PE7: 455 | case PF7: return GPIO7; 456 | case PD8: 457 | case PE8: return GPIO8; 458 | case PA9: 459 | case PD9: 460 | case PG9: return GPIO9; 461 | case PB10: 462 | case PA10: 463 | case PC10: return GPIO10; 464 | case PB11: 465 | case PC11: return GPIO11; 466 | case PC12: return GPIO12; 467 | case PG14: return GPIO14; 468 | default: return 0xfffffff; 469 | } 470 | } 471 | 472 | /* Return base register for each GPIO based on PIN name */ 473 | static uint32_t 474 | pin_to_gpio(enum UART_PORT_PIN pin) { 475 | switch (pin) { 476 | case PA0: 477 | case PA1: 478 | case PA2: 479 | case PA3: 480 | case PA9: 481 | case PA10: 482 | return GPIOA; 483 | case PB6: 484 | case PB7: 485 | case PB10: 486 | case PB11: 487 | return GPIOB; 488 | case PC6: 489 | case PC7: 490 | case PC10: 491 | case PC11: 492 | case PC12: 493 | return GPIOC; 494 | case PD2: 495 | case PD5: 496 | case PD6: 497 | case PD8: 498 | case PD9: 499 | return GPIOD; 500 | case PE0: 501 | case PE1: 502 | case PE7: 503 | case PE8: 504 | return GPIOE; 505 | case PF6: 506 | case PF7: 507 | return GPIOF; 508 | case PG9: 509 | case PG14: 510 | return GPIOG; 511 | default: return 0xfffffff; 512 | } 513 | } 514 | 515 | /* 516 | * This maps a pin to its GPIO clock, since you have to enable 517 | * the GPIO clock for the port this takes care of that for us. 518 | */ 519 | static uint32_t 520 | pin_to_enable(enum UART_PORT_PIN pin) { 521 | switch (pin) { 522 | case PA0: 523 | case PA1: 524 | case PA2: 525 | case PA3: 526 | case PA9: 527 | case PA10: return RCC_AHB1ENR_IOPAEN; 528 | 529 | case PB6: 530 | case PB7: 531 | case PB10: 532 | case PB11: return RCC_AHB1ENR_IOPBEN; 533 | 534 | case PC6: 535 | case PC7: 536 | case PC10: 537 | case PC11: 538 | case PC12: return RCC_AHB1ENR_IOPCEN; 539 | 540 | case PD2: 541 | case PD5: 542 | case PD6: 543 | case PD8: 544 | case PD9: return RCC_AHB1ENR_IOPDEN; 545 | 546 | case PE0: 547 | case PE1: 548 | case PE7: 549 | case PE8: return RCC_AHB1ENR_IOPEEN; 550 | 551 | case PF6: 552 | case PF7: return RCC_AHB1ENR_IOPFEN; 553 | 554 | case PG9: 555 | case PG14: return RCC_AHB1ENR_IOPGEN; 556 | default: return 0xfffffff; 557 | } 558 | } 559 | 560 | /* 561 | * A number of attributes common to each USART/UART such as 562 | * their ENABLE flag in the APB register, their NVIC interrupt 563 | * vector, their 'home' APB register (1 or 2) and their base 564 | * address. 565 | */ 566 | static uint32_t 567 | pin_uart_attr(enum UART_PORT_PIN pin, int attr) { 568 | switch (pin) { 569 | case PA0: 570 | case PA1: 571 | switch (attr) { 572 | case 1: return RCC_APB1ENR_UART4EN; 573 | case 2: return (uint32_t) &RCC_APB1ENR; 574 | case 3: return NVIC_UART4_IRQ; 575 | default: return UART4; 576 | } 577 | 578 | case PA2: 579 | case PA3: 580 | case PD5: 581 | case PD6: 582 | switch (attr) { 583 | case 1: return RCC_APB1ENR_USART2EN; 584 | case 2: return (uint32_t) &RCC_APB1ENR; 585 | case 3: return NVIC_USART2_IRQ; 586 | default: return USART2; 587 | } 588 | 589 | case PA9: 590 | case PA10: 591 | case PB6: 592 | case PB7: 593 | switch (attr) { 594 | case 1: return RCC_APB2ENR_USART1EN; 595 | case 2: return (uint32_t) &RCC_APB2ENR; 596 | case 3: return NVIC_USART1_IRQ; 597 | default: return USART1; 598 | } 599 | 600 | case PB10: 601 | case PB11: 602 | case PC10: 603 | case PC11: 604 | case PD8: 605 | case PD9: 606 | switch (attr) { 607 | case 1: return RCC_APB1ENR_USART3EN; 608 | case 2: return (uint32_t) &RCC_APB1ENR; 609 | case 3: return NVIC_USART3_IRQ; 610 | default: return USART3; 611 | } 612 | 613 | case PC6: 614 | case PC7: 615 | case PG9: 616 | case PG14: 617 | switch (attr) { 618 | case 1: return RCC_APB2ENR_USART6EN; 619 | case 2: return (uint32_t) &RCC_APB2ENR; 620 | case 3: return NVIC_USART6_IRQ; 621 | default: return USART6; 622 | } 623 | 624 | case PC12: 625 | case PD2: 626 | switch (attr) { 627 | case 1: return RCC_APB1ENR_UART5EN; 628 | case 2: return (uint32_t) &RCC_APB1ENR; 629 | case 3: return NVIC_UART5_IRQ; 630 | default: return UART5; 631 | } 632 | 633 | case PE0: 634 | case PE1: 635 | switch (attr) { 636 | case 1: return RCC_APB1ENR_UART8EN; 637 | case 2: return (uint32_t) &RCC_APB1ENR; 638 | case 3: return NVIC_UART8_IRQ; 639 | default: return UART8; 640 | } 641 | 642 | case PE7: 643 | case PE8: 644 | case PF6: 645 | case PF7: 646 | switch (attr) { 647 | case 1: return RCC_APB1ENR_UART7EN; 648 | case 2: return (uint32_t) &RCC_APB1ENR; 649 | case 3: return NVIC_UART7_IRQ; 650 | default: return UART7; 651 | } 652 | 653 | default: return 0xfffffff; 654 | } 655 | } 656 | 657 | /* This one then maps which Alternate function flag is needed for a pin 658 | * to make it a UART pin 659 | */ 660 | static uint32_t 661 | pin_to_af(enum UART_PORT_PIN pin) { 662 | switch (pin) { 663 | case PA0: 664 | case PA1: 665 | case PC6: 666 | case PC7: 667 | case PC12: 668 | case PD2: 669 | case PE0: 670 | case PE1: 671 | case PE7: 672 | case PE8: 673 | case PF6: 674 | case PF7: 675 | case PG9: 676 | case PG14: 677 | return GPIO_AF8; 678 | 679 | case PA2: 680 | case PA3: 681 | case PA9: 682 | case PA10: 683 | case PB6: 684 | case PB7: 685 | case PB10: 686 | case PB11: 687 | case PC10: 688 | case PC11: 689 | case PD5: 690 | case PD6: 691 | case PD8: 692 | case PD9: 693 | return GPIO_AF7; 694 | default: return 0xfffffff; 695 | } 696 | } 697 | 698 | /* 699 | * pin_map 700 | * 701 | * This code encodes the relationship between pins and various chip 702 | * registers, the USART, the AHB bus, the Alternate function code, 703 | * Etc. Call it with a pin and an attribute and get back an answer. 704 | * 705 | * See the UART initialization code for an example of its use. 706 | */ 707 | static uint32_t 708 | uart_pin_map(enum UART_PORT_PIN pin, enum PIN_ATTRIBUTE attr) { 709 | switch (attr) { 710 | case USART: 711 | return pin_uart_attr(pin, 0); 712 | case APB_ENA: 713 | return pin_uart_attr(pin, 1); 714 | case APB_REG: 715 | return pin_uart_attr(pin, 2); 716 | case IRQ: 717 | return pin_uart_attr(pin, 3); 718 | case BIT: 719 | return pin_to_bit(pin); 720 | case GPIO_ENA: 721 | return pin_to_enable(pin); 722 | case AF: 723 | return pin_to_af(pin); 724 | default: 725 | return pin_to_gpio(pin); 726 | } 727 | } 728 | 729 | /* 730 | * Convert a number to a string in the desired 731 | * base. 732 | * 733 | * Assumes: 734 | * - buf has enough space (up to 35 bytes for 735 | * a 32 bit number in binary) 736 | * Verifies: 737 | * - base is one of 2, 8, 10, or 16 738 | * Creates: 739 | * - null string on error ("") 740 | * 741 | * This got a bit out of hand but I wanted just *one* number printing 742 | * function rather than several. 743 | * 744 | * Notes from my notebook: 745 | * Number consists of: 746 | * 747 | * PREFIX|NUMBER|SUFFIX 748 | * |------ width -------| 749 | * |------ field width -----| 750 | 751 | * Prefix is one of "", "-", "0x", "0b", "0", "-0x" "-0b", "-0" 752 | * Suffix is one of "", ".0" 753 | * 754 | * The independent variable is the leading zero question. 755 | * So when LEADING ZERO is set you pad the width of Number to fill 756 | * the width, up to 32 digits of precision. 757 | * 758 | * With LEFT adjust you pad right, without it you pad left. 759 | * 760 | * Examples: 761 | * 0x12345000 - hex + alternate form 762 | * 1234abcd - just hex 763 | * 0C - hex + width of 1 byte + leading 0 764 | * 10.0 - decimal + alternate form 765 | * 0b11011 - Binary + alternate form 766 | * 0b00011011 - Binary + alternate form + leading 0 + 1 byte 767 | * 0100377 - octal + alternate form + 2 bytes 768 | * 037777777777 - octal equivalent of -1 769 | * -1.0 - decimal + sign + alternate form (adds the .0) 770 | * 771 | * So I'm still going back and forth on the width. It would be handy 772 | * to make it just 1, 2, or 4 for 1 byte value, 2 byte value, 4 byte 773 | * value with 8 reserved for 8 byte value. As that is the common use 774 | * case. And it would only apply to how many digits of the number were 775 | * considered and not the field width. 776 | */ 777 | void 778 | ntoa(uint32_t val, uint16_t fmt, char *buf) { 779 | char *d; 780 | uint32_t mask; 781 | int base, size, width; 782 | int sign_bit = 0; 783 | 784 | d = buf; 785 | *d = '\000'; 786 | d++; 787 | // defaults if not specified 788 | mask = 0xffffffff; 789 | base = 10; 790 | width = 0; 791 | size = fmt & FMT_SIZE_MASK; 792 | /* sigh how to make this cleaner? */ 793 | switch (fmt & FMT_BASE_MASK) { 794 | case FMT_BASE_2: 795 | base = 2; 796 | switch (size) { 797 | case 1: 798 | mask = 0xff; 799 | width = 8; 800 | break; 801 | case 2: 802 | mask = 0xffff; 803 | width = 16; 804 | break; 805 | case 4: 806 | mask = 0xffffffff; 807 | width = 32; 808 | break; 809 | default: 810 | width = 0; 811 | break; 812 | } 813 | break; 814 | case FMT_BASE_8: 815 | base = 8; 816 | switch (size) { 817 | case 1: 818 | mask = 0xff; 819 | width = 3; 820 | break; 821 | case 2: 822 | mask = 0xffff; 823 | width = 6; 824 | break; 825 | case 4: 826 | mask = 0xffffffff; 827 | width = 11; 828 | break; 829 | default: 830 | width = 0; 831 | break; 832 | } 833 | break; 834 | case FMT_BASE_16: 835 | base = 16; 836 | switch (size) { 837 | case 1: 838 | mask = 0xff; 839 | width = 2; 840 | break; 841 | case 2: 842 | mask = 0xffff; 843 | width = 4; 844 | break; 845 | case 4: 846 | mask = 0xffffffff; 847 | width = 8; 848 | break; 849 | default: 850 | width = 0; 851 | break; 852 | } 853 | break; 854 | case FMT_BASE_10: 855 | base = 10; 856 | switch (size) { 857 | case 1: 858 | mask = 0xff; 859 | width = 3; 860 | sign_bit = ((val & 0x80) != 0); 861 | break; 862 | case 2: 863 | mask = 0xffff; 864 | width = 5; 865 | sign_bit = ((val & 0x8000) != 0); 866 | break; 867 | case 4: 868 | mask = 0xffffffff; 869 | width = 10; 870 | sign_bit = ((val & 0x80000000) != 0); 871 | break; 872 | default: 873 | width = 0; 874 | sign_bit = ((val & 0x80000000) != 0); 875 | break; 876 | } 877 | if (fmt & FMT_ALTERNATE_FORM) { 878 | *d++ = '0'; 879 | *d++ = '.'; 880 | } 881 | break; 882 | default: 883 | break; 884 | } 885 | /* this assumes if you send it a byte, it gets promoted and sign extended */ 886 | if ((fmt & (FMT_SIGNED | FMT_BASE_MASK)) == (FMT_SIGNED | FMT_BASE_10)) { 887 | val = (val & 0x80000000) ? -val : val; 888 | } 889 | val = val & mask; 890 | 891 | do { 892 | *d++ = ((val % base) < 10) ? (val % base) + '0' : 893 | (val % base) + '7'; 894 | val /= base; 895 | width--; 896 | } while (val > 0); 897 | 898 | if (fmt & FMT_LEADING_ZERO) { 899 | while (width > 0) { 900 | *d++ = '0'; 901 | width--; 902 | } 903 | } 904 | if (fmt & FMT_ALTERNATE_FORM) { 905 | switch (fmt & FMT_BASE_MASK) { 906 | case FMT_BASE_16: 907 | *d++ = 'x'; 908 | *d++ = '0'; 909 | break; 910 | case FMT_BASE_8: 911 | *d++ = '0'; 912 | break; 913 | case FMT_BASE_2: 914 | *d++ = 'b'; 915 | *d++ = '0'; 916 | default: 917 | break; 918 | } 919 | } 920 | if (sign_bit && (fmt & FMT_SIGNED)) { 921 | *d++ = '-'; 922 | width--; 923 | } 924 | 925 | /* 926 | * So how often do you get to use an interview question in your 927 | * code? 928 | * At this point the string is in buf, only backwards so reverse it 929 | * in place. 930 | */ 931 | d--; // Point d at last character 932 | do { 933 | *d ^= *buf; 934 | *buf ^= *d; 935 | *d ^= *buf; 936 | } while (++buf < --d); 937 | return; 938 | } 939 | 940 | /* 941 | * Convert a string to a number in the desired 942 | * 943 | * Assumes: 944 | * - Stops at first non-legal digit in guessed 945 | * base or NUL character. 946 | * - only does signed input on decimal numbers 947 | * Returns: 948 | * - 0 even on failure. 949 | */ 950 | uint32_t 951 | aton(char *buf) { 952 | uint32_t res = 0; 953 | int base = 10; 954 | int sign_bit = 0; 955 | uint8_t digit; 956 | 957 | /* check for different base indicators */ 958 | if (((*buf >= '1') && (*buf <= '9')) || (*buf == '-')) { 959 | if (*buf == '-') { 960 | sign_bit = 1; 961 | buf++; 962 | } 963 | base = 10; 964 | } else if ((*buf) == '0') { 965 | buf++; 966 | if (*buf == 'b') { 967 | base = 2; 968 | buf++; 969 | } else if (*buf == 'x') { 970 | base = 16; 971 | buf++; 972 | } else if ((*buf >= '0') && (*buf <= '7')) { 973 | base = 8; 974 | } else if (*buf == '\000') { 975 | return 0; 976 | } 977 | } 978 | while (*buf != '\000') { 979 | digit = (*buf > '9') ? ((*buf & 0xdf) - '7') : (*buf - '0'); 980 | if (((base == 2) && (digit > 1)) || 981 | ((base == 8) && (digit > 7)) || 982 | ((base == 10) && (digit > 9)) || 983 | ((base == 16) && (digit > 15))) { 984 | return (sign_bit) ? -res : res; 985 | } 986 | res *= base; 987 | res += digit; 988 | buf++; 989 | } 990 | return (sign_bit) ? -res : res; 991 | } 992 | 993 | 994 | /* 995 | * Print a number given various formatting options. 996 | * NB: No floating point yet. 997 | * 998 | */ 999 | void 1000 | uart_putnum(int chan, uint16_t fmt, uint32_t num) { 1001 | char buf[48]; 1002 | 1003 | ntoa(num, fmt, buf); 1004 | uart_puts(chan, buf); 1005 | if (fmt & FMT_NEWLINE) { 1006 | uart_puts(chan, "\n"); 1007 | } 1008 | } 1009 | 1010 | /* 1011 | * uart_getnum 1012 | * 1013 | * Read a number in from the specified channel. Accepts base 1014 | * decorators (like 0x) to distinguish hexadecimal numbers from 1015 | * decimal ones. See aton for the list. 1016 | */ 1017 | uint32_t 1018 | uart_getnum(int chan) { 1019 | char buf[48]; 1020 | 1021 | uart_gets(chan, buf, 48); 1022 | return aton(buf); 1023 | } 1024 | -------------------------------------------------------------------------------- /sdio_util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * sdio.c - code to access the SD card on the Base Board 3 | * 4 | * In theory this is in two parts, the low level driver part and 5 | * the higher level "storage layer" part. Sort of like UNIX in that 6 | * way. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "bb.h" 14 | #include "debug.h" 15 | #ifndef NULL 16 | #define NULL (void *)(0) 17 | #endif 18 | 19 | #define USE_NEW_SDIO 20 | /* conveniently swaps the bytes in a long around */ 21 | #define byte_swap(val) \ 22 | asm("rev %[swap], %[swap]" : [swap] "=r" (val) : "0" (val)); 23 | 24 | #define DEBUG(x) debug_puts(x) 25 | 26 | /* Prototypes */ 27 | 28 | /* These are functions to show status on the terminal */ 29 | void show_sdio_clock(int, int); 30 | void show_sdio_status(int, int); 31 | void show_sdio_sdstatus(int, int); 32 | void debug_sdio_sdstatus(uint32_t buf[]); 33 | void show_sdio_cid(int, int, uint32_t []); 34 | void show_sdio_csd(int, int, uint32_t []); 35 | void show_sdio_power(int row, int col); 36 | void show_sdio_carddetect(int row, int col); 37 | void show_sdiolog(int row, int col); 38 | void show_sdio_scr(int row, int col, uint32_t scr); 39 | 40 | /* these are the actually useful functions */ 41 | void debug_status(uint32_t); 42 | static void en_dis(char *, uint32_t, char *, char *); 43 | void sdio_log(uint8_t cmd, uint32_t arg, int err, const char *resp); 44 | int sd_getsdstatus(int rca); 45 | 46 | char debug_buf[64]; 47 | 48 | /* 49 | * Capabilities of the card we've detected 50 | */ 51 | struct sd_caps { 52 | int valid; // Set 1 when structure is valid 53 | int f8_support; // Supports CMD8 54 | int sdio; // Is SDIO Card 55 | int v2; // V2.x 56 | int lv; // Low Voltage (1.8V) card 57 | int ok_volts; // Voltage compatible with host 58 | int hc; // High capacity 59 | uint16_t rca; // Relative Card Address 60 | uint32_t cid[4]; // CID values 61 | uint32_t csd[4]; // CSD values 62 | uint32_t size; // Size in K Bytes of our SD Card 63 | uint32_t blocks; // Size in blocks of our SD Card 64 | uint32_t scr; // lower 32 of SCR 65 | } card_caps; 66 | 67 | struct form_data { 68 | int nflags; 69 | struct { 70 | int row_offset; 71 | int col_offset; 72 | char *label; 73 | uint32_t flag; 74 | char *affirmative; 75 | char *negative; 76 | } flags[]; 77 | }; 78 | 79 | const struct form_data clock = { 80 | 5, 81 | { 82 | {3, 1, "Divider Bypass : ", SDIO_CLKCR_BYPASS, 0, 0}, 83 | {4, 5, "SDIO Clock : ", SDIO_CLKCR_CLKEN, 0, 0}, 84 | {5, 5, "Power Save : ", SDIO_CLKCR_PWRSAV, 0, 0}, 85 | {6, 0, "HW Flow Control : ", SDIO_CLKCR_HWFC_EN, 0, 0}, 86 | {7, 4, "Clock Phase : ", SDIO_CLKCR_NEGEDGE, "Falling Edge", "Rising Edge"} 87 | } 88 | }; 89 | 90 | 91 | static void 92 | en_dis(char *tag, uint32_t bits, char *en, char *ds) { 93 | uart_puts(console, tag); 94 | if (bits) { 95 | uart_puts(console, (en == 0) ? "ENABLED " : en); 96 | } else { 97 | uart_puts(console, (ds == 0) ? "DISABLED" : ds); 98 | } 99 | } 100 | 101 | 102 | uint8_t blk_read_buf[512]; 103 | 104 | 105 | /* 106 | * show_sdio_clock - put up information about what the clock 107 | * register is configured to do. 108 | */ 109 | static char *bus_widths[4] = { "1 bit", "4 bit", "8 bit", "*BAD*" }; 110 | 111 | void 112 | show_sdio_clock(int row, int col) { 113 | uint32_t reg = SDIO_CLKCR; 114 | int freq; 115 | int i; 116 | i = (reg & 0x7f); // should use the shifts 117 | freq = 48000000 / (i + 2); 118 | move_cursor(console, row, col); 119 | text_color(console, YELLOW); 120 | uart_puts(console, "SDIO Clock Settings"); 121 | text_color(console, DEFAULT); 122 | move_cursor(console, row + 1, col + 6); 123 | uart_puts(console, "Bus Width : "); 124 | i = (reg & (0x3 << SDIO_CLKCR_WIDBUS_SHIFT)) >> SDIO_CLKCR_WIDBUS_SHIFT; 125 | uart_puts(console, bus_widths[i]); 126 | move_cursor(console, row + 2, col+1); 127 | uart_puts(console, "SDIO Frequency : "); 128 | if (freq > 1000000) { 129 | i = freq % 1000000; 130 | freq = freq / 1000000; 131 | uart_putnum(console, FMT_BASE_10, freq); 132 | uart_puts(console, "Mhz "); 133 | } else { 134 | i = freq % 1000; 135 | freq = freq / 1000; 136 | uart_putnum(console, FMT_BASE_10, freq); 137 | uart_puts(console, "Khz "); 138 | } 139 | for (i = 0; i < clock.nflags; i++) { 140 | move_cursor(console, row + clock.flags[i].row_offset, 141 | col + clock.flags[i].col_offset); 142 | en_dis(clock.flags[i].label, 143 | (clock.flags[i].flag & reg) != 0, 144 | clock.flags[i].affirmative, 145 | clock.flags[i].negative); 146 | 147 | } 148 | } 149 | 150 | 151 | const struct { 152 | uint32_t bit; 153 | const char *msg; 154 | } status_flags[] = { 155 | { SDIO_STA_CEATAEND, "CE-ATA Command Complete!" }, 156 | { SDIO_STA_SDIOIT, "SDIO Interrupt Received!" }, 157 | { SDIO_STA_RXDAVL, "Data Available in Rx FIFO!" }, 158 | { SDIO_STA_TXDAVL, "Data Available in Tx FIFO!" }, 159 | { SDIO_STA_RXFIFOE, "Rx FIFO Empty!" }, 160 | { SDIO_STA_TXFIFOE, "Tx FIFO Empty!" }, 161 | { SDIO_STA_RXFIFOF, "Rx FIFO Full!" }, 162 | { SDIO_STA_TXFIFOF, "Tx FIFO Full!" }, 163 | { SDIO_STA_RXFIFOHF, "Rx FIFO Half Full!" }, 164 | { SDIO_STA_TXFIFOHE, "Tx FIFO Half Empty!" }, 165 | { SDIO_STA_RXACT, "Rx in Progress!" }, 166 | { SDIO_STA_TXACT, "Tx in Progress!" }, 167 | { SDIO_STA_CMDACT, "Command Transfer in Progress!" }, 168 | { SDIO_STA_DBCKEND, "Data Block Sent/Recieved, CRC OK!" }, 169 | { SDIO_STA_STBITERR, "Start bit not detected on data in wide bus!" }, 170 | { SDIO_STA_DATAEND, "Data end!" }, 171 | { SDIO_STA_CMDSENT, "Command Sent (no response req'd)!" }, 172 | { SDIO_STA_CMDREND, "Command Response Receved (CRC OK!)" }, 173 | { SDIO_STA_RXOVERR, "Rx FIFO Overrun error!" }, 174 | { SDIO_STA_TXUNDERR, "Tx FIFO Underrun Error!" }, 175 | { SDIO_STA_DTIMEOUT, "Data Timeout!" }, 176 | { SDIO_STA_CTIMEOUT, "Command Response Timeout!" }, 177 | { SDIO_STA_DCRCFAIL, "Data CRC Fail!" }, 178 | { SDIO_STA_CCRCFAIL, "Command CRC Fail!" }, 179 | { 0, 0 } 180 | }; 181 | 182 | 183 | 184 | void 185 | show_sdio_status(int row, int col) { 186 | uint32_t reg = SDIO_STA; 187 | int ndx = 0; 188 | int i = 0; 189 | move_cursor(console, row, col+2); 190 | text_color(console, YELLOW); 191 | uart_puts(console, "SDIO Status Register : "); 192 | text_color(console, DEFAULT); 193 | if (reg == 0) { 194 | uart_puts(console, "No Status Active!"); 195 | } else { 196 | while (status_flags[ndx].msg != 0) { 197 | if (reg & status_flags[ndx].bit) { 198 | uart_puts(console, status_flags[ndx].msg); 199 | move_cursor(console, row+i, col+23); 200 | i++; 201 | } 202 | ndx++; 203 | } 204 | } 205 | } 206 | 207 | void 208 | debug_status(uint32_t bits) { 209 | uint32_t reg = bits; 210 | int ndx = 0; 211 | DEBUG("\n SDIO Status Register : "); 212 | ntoa(bits, FMT_BINARY_LONG, debug_buf); 213 | DEBUG(debug_buf); 214 | DEBUG("\n"); 215 | if (reg == 0) { 216 | DEBUG(" - No Status Active!\n"); 217 | } else { 218 | while (status_flags[ndx].msg != 0) { 219 | if (reg & status_flags[ndx].bit) { 220 | DEBUG(" - "); 221 | DEBUG(status_flags[ndx].msg); 222 | DEBUG("\n"); 223 | } 224 | ndx++; 225 | } 226 | } 227 | } 228 | 229 | void show_sdio_response(int, int); 230 | 231 | /* XXX the response registers should be an array */ 232 | void 233 | show_sdio_response(int row, int col) { 234 | uint32_t resp[4]; 235 | char buf[16]; 236 | int i; 237 | resp[0] = SDIO_RESP1; 238 | resp[1] = SDIO_RESP2; 239 | resp[2] = SDIO_RESP3; 240 | resp[3] = SDIO_RESP4; 241 | move_cursor(console, row, col+9); 242 | text_color(console, YELLOW); 243 | uart_puts(console, "SDIO Response : "); 244 | text_color(console, DEFAULT); 245 | ntoa(resp[0], FMT_HEX_LONG | FMT_ALTERNATE_FORM, buf); 246 | uart_puts(console, buf); 247 | text_color(console, YELLOW); 248 | uart_puts(console, " <- [SHORT] "); 249 | text_color(console, DEFAULT); 250 | for (i = 1; i < 4; i++) { 251 | move_cursor(console, row + i, col + 25); 252 | ntoa(resp[i], FMT_HEX_LONG | FMT_ALTERNATE_FORM, buf); 253 | uart_puts(console, buf); 254 | } 255 | } 256 | 257 | /* 258 | * Error flags for RESP1 Register 259 | */ 260 | 261 | #define RESP1_ADDR_OUT_OF_RANGE ((uint32_t)0x80000000) 262 | #define RESP1_ADDR_MISALIGNED ((uint32_t)0x40000000) 263 | #define RESP1_BLOCK_LEN_ERR ((uint32_t)0x20000000) 264 | #define RESP1_ERASE_SEQ_ERR ((uint32_t)0x10000000) 265 | #define RESP1_BAD_ERASE_PARAM ((uint32_t)0x08000000) 266 | #define RESP1_WRITE_PROT_VIOLATION ((uint32_t)0x04000000) 267 | #define RESP1_LOCK_UNLOCK_FAILED ((uint32_t)0x01000000) 268 | #define RESP1_COM_CRC_FAILED ((uint32_t)0x00800000) 269 | #define RESP1_ILLEGAL_CMD ((uint32_t)0x00400000) 270 | #define RESP1_CARD_ECC_FAILED ((uint32_t)0x00200000) 271 | #define RESP1_CC_ERROR ((uint32_t)0x00100000) 272 | #define RESP1_GENERAL_UNKNOWN_ERROR ((uint32_t)0x00080000) 273 | #define RESP1_STREAM_READ_UNDERRUN ((uint32_t)0x00040000) 274 | #define RESP1_STREAM_WRITE_OVERRUN ((uint32_t)0x00020000) 275 | #define RESP1_CID_CSD_OVERWRIETE ((uint32_t)0x00010000) 276 | #define RESP1_WP_ERASE_SKIP ((uint32_t)0x00008000) 277 | #define RESP1_CARD_ECC_DISABLED ((uint32_t)0x00004000) 278 | #define RESP1_ERASE_RESET ((uint32_t)0x00002000) 279 | #define RESP1_AKE_SEQ_ERROR ((uint32_t)0x00000008) 280 | #define RESP1_ALL_ERRORS ((uint32_t)0xFDFFE008) 281 | 282 | static const struct { 283 | uint32_t bit; // error bit in R1 Register 284 | char *msg; // Text version of error 285 | } r1_errors[] = { 286 | { RESP1_ADDR_OUT_OF_RANGE, "Address out of range" }, 287 | { RESP1_ADDR_MISALIGNED, "Address misaligned" }, 288 | { RESP1_BLOCK_LEN_ERR, "Block length error" }, 289 | { RESP1_ERASE_SEQ_ERR, "Erase sequence error" }, 290 | { RESP1_BAD_ERASE_PARAM, "Erase parameter error" }, 291 | { RESP1_WRITE_PROT_VIOLATION, "Write protect violation" }, 292 | { RESP1_LOCK_UNLOCK_FAILED, "Lock / Unlock failure" }, 293 | { RESP1_COM_CRC_FAILED, "Com CRC failed" }, 294 | { RESP1_ILLEGAL_CMD, "Illegal command" }, 295 | { RESP1_CARD_ECC_FAILED, "Card ECC failure" }, 296 | { RESP1_CC_ERROR, "CC error" }, 297 | { RESP1_GENERAL_UNKNOWN_ERROR, "General (unknown) error" }, 298 | { RESP1_STREAM_READ_UNDERRUN, "Stream read underrun" }, 299 | { RESP1_STREAM_WRITE_OVERRUN, "Stream write overrun" }, 300 | { RESP1_CID_CSD_OVERWRIETE, "CID / CSD Overwrite" }, 301 | { RESP1_WP_ERASE_SKIP, "Write protect Erase skipped" }, 302 | { RESP1_CARD_ECC_DISABLED, "Card ECC disabled" }, 303 | { RESP1_ERASE_RESET, "Erase reset" }, 304 | { RESP1_AKE_SEQ_ERROR, "AKE sequence error" }, 305 | { 0, 0}, // must be last 306 | }; 307 | 308 | #if 0 309 | 310 | /* 311 | * Return an appropriate error message based on an R1 response 312 | * Passed the response code 313 | * Returns const char * to error message 314 | */ 315 | const char * 316 | sdio_resp_error_msg(uint8_t cmd, uint32_t resp) { 317 | int i; 318 | 319 | // Command ended successfully, check the response 320 | if ((resp & 0xff) != cmd) { 321 | return sdio_error_strings[0]; // Success 322 | } 323 | 324 | for (i = 0; r1_errors[i].bit != 0; i++) { 325 | if (resp & r1_errors[i].bit) { 326 | return r1_errors[i].msg; 327 | } 328 | } 329 | return sdio_error_strings[5]; // Did not recognize the error bit 330 | } 331 | #endif 332 | 333 | void 334 | show_sdio_power(int row, int col) { 335 | move_cursor(console, row, col+2); 336 | text_color(console, YELLOW); 337 | uart_puts(console, "SDIO Power: "); 338 | switch ( SDIO_POWER & 0x3 ) { 339 | case 0: 340 | text_color(console, RED); 341 | uart_puts(console, "OFF"); 342 | text_color(console, DEFAULT); 343 | break; 344 | case 1: 345 | uart_puts(console, "RSV1"); 346 | break; 347 | case 2: 348 | uart_puts(console, "RSV2"); 349 | break; 350 | case 3: 351 | text_color(console, GREEN); 352 | uart_puts(console, "ON"); 353 | text_color(console, DEFAULT); 354 | break; 355 | } 356 | } 357 | 358 | void 359 | show_sdio_carddetect(int row, int col) { 360 | move_cursor(console, row, col); 361 | text_color(console, YELLOW); 362 | uart_puts(console, "Card Present: "); 363 | text_color(console, DEFAULT); 364 | if (gpio_get(GPIOB, GPIO15)) { 365 | text_color(console, RED); 366 | uart_puts(console, "NOT DETECTED"); 367 | } else { 368 | uart_puts(console, "Detected"); 369 | } 370 | text_color(console, DEFAULT); 371 | } 372 | 373 | uint8_t blk_write_buf[512]; 374 | 375 | SDIO_CARD my_card; 376 | 377 | 378 | /* 379 | * SDIO Explorer 380 | * 381 | * So this becomes a simple way to 'play around' with the SDIO 382 | * registers in the chip to see what they do. 383 | */ 384 | void 385 | sdio_explorer(void) { 386 | int err, i; 387 | char c; 388 | uint8_t *ss, *ds; 389 | uint32_t blk; 390 | uint8_t *addr; 391 | 392 | clear_screen(console); 393 | my_card = sdio_open(); 394 | DEBUG("Last 10 SDIO commands:\n"); 395 | sdio_print_log(debug_console, 10); 396 | if (my_card == NULL) { 397 | DEBUG("Open failed!\n"); 398 | show_sdio_power(1, 40); 399 | show_sdio_clock(1, 1); 400 | show_sdio_carddetect(2, 40); 401 | debug_wait(); 402 | return; 403 | } 404 | DEBUG("Card size is : "); 405 | uart_putnum(debug_console, FMT_BASE_10, my_card->size); 406 | DEBUG(" blocks. ("); 407 | uart_putnum(debug_console, FMT_BASE_10, my_card->size >> 1); 408 | DEBUG("K bytes)\n"); 409 | DEBUG("Reading a block from the card\n"); 410 | err = sdio_readblock(my_card, 0, blk_read_buf); 411 | if (! err) { 412 | DEBUG("Success!\n"); 413 | addr = dump_page(debug_console, blk_read_buf, blk_read_buf); 414 | dump_page(debug_console, addr, blk_read_buf); 415 | } 416 | err = sdio_status(my_card); 417 | if (err) { 418 | DEBUG("Error reading SD Card Status : "); 419 | DEBUG(sdio_errmsg(err)); 420 | DEBUG("\n"); 421 | } else { 422 | debug_sdio_sdstatus(my_card->status); 423 | } 424 | 425 | show_sdio_power(1, 40); 426 | show_sdio_carddetect(2, 40); 427 | show_sdio_clock(1, 1); 428 | #if 0 429 | show_sdio_csd(17, 1, my_card->csd); 430 | show_sdio_cid(3, 32, my_card->cid); 431 | show_sdio_scr(32, 42, my_card->scr[0]); 432 | #endif 433 | 434 | move_cursor(console, 10, 1); 435 | text_color(console, YELLOW); 436 | uart_puts(console, "Card Size: "); 437 | text_color(console, GREEN); 438 | uart_putnum(console, FMT_BASE_10, my_card->size); 439 | uart_puts(console, " blocks."); 440 | while (1) { 441 | move_cursor(console, 11, 1); 442 | uart_puts(console, "[R]ead, [W]rite, or e[X]it: "); 443 | move_cursor(console, 11, 29); 444 | c = uart_getc(console, 1); 445 | if ((c == 'x') || (c == 'X')) { 446 | uart_puts(console, "Exit"); 447 | return; 448 | } 449 | if ((c == 'r') || (c == 'R')) { 450 | uart_puts(console, "Read"); 451 | move_cursor(console, 12, 1); 452 | uart_puts(console, "Enter Block # : "); 453 | move_cursor(console, 12, 17); 454 | blk = uart_getnum(console); 455 | if (blk >= my_card->size) { 456 | move_cursor(console, 13, 1); 457 | uart_puts(console, "Block number out of range!"); 458 | continue; 459 | } 460 | err = sdio_readblock(my_card, blk, blk_read_buf); 461 | move_cursor(console, 13, 1); 462 | uart_puts(console, "Result : "); 463 | uart_puts(console, sdio_errmsg(err)); 464 | move_cursor(console, 15, 1); 465 | addr = dump_page(console, blk_read_buf, blk_read_buf); 466 | dump_page(console, addr, blk_read_buf); 467 | } else if ((c == 'w') || (c == 'W')) { 468 | uart_puts(console, "Write"); 469 | move_cursor(console, 12, 1); 470 | uart_puts(console, "Enter Block # : "); 471 | move_cursor(console, 12, 17); 472 | blk = uart_getnum(console); 473 | for (i = 0; i < 512; i++) { 474 | *(blk_read_buf+i) = i & 0xff; 475 | } 476 | ss = (uint8_t *) "This is Block # "; 477 | ds = blk_read_buf; 478 | while (*ss != '\000') { 479 | *ds = *ss; 480 | ds++; ss++; 481 | } 482 | ntoa(blk, FMT_BASE_10, (char *) ds); 483 | err = sdio_writeblock(my_card, blk, blk_read_buf); 484 | move_cursor(console, 13, 1); 485 | uart_puts(console, "Result : "); 486 | uart_puts(console, sdio_errmsg(err)); 487 | } 488 | } 489 | } 490 | 491 | /* 492 | * Put up the SD Card's CID data 493 | */ 494 | void 495 | show_sdio_cid(int row, int col, uint32_t cid[]) { 496 | int i; 497 | char c; 498 | uint32_t t; 499 | char buf[10]; 500 | 501 | move_cursor(console, row, col); 502 | text_color(console, YELLOW); 503 | uart_puts(console, "SD Card CID Data:"); 504 | col += 2; 505 | move_cursor(console, row+1, col+6); 506 | uart_puts(console, "Manufacturer: "); 507 | text_color(console, DEFAULT); 508 | i = (cid[0] >> 24) & 0xff; 509 | uart_putnum(console, FMT_BASE_10, i); 510 | move_cursor(console, row+2, col); 511 | text_color(console, YELLOW); 512 | uart_puts(console, "OEM/Application ID: "); 513 | text_color(console, DEFAULT); 514 | c = (char) ((cid[0] >> 16) & 0xff); 515 | uart_putc(console, c); 516 | c = (char) ((cid[0] >> 8) & 0xff); 517 | uart_putc(console, c); 518 | text_color(console, YELLOW); 519 | move_cursor(console, row+3, col+6); 520 | uart_puts(console, "Product Name: "); 521 | text_color(console, DEFAULT); 522 | uart_putc(console, (char) (cid[0] & 0xff)); 523 | for (i = 0; i < 4; i++) { 524 | uart_putc(console, (char)((cid[1] >> 8 * (3-i)) & 0xff)); 525 | } 526 | text_color(console, YELLOW); 527 | move_cursor(console, row+4, col+10); 528 | uart_puts(console, "Revision: "); 529 | text_color(console, DEFAULT); 530 | c = (char)((cid[2] >> 28) & 0xf) + '0'; 531 | uart_putc(console, c); 532 | uart_putc(console, '.'); 533 | c = (char)((cid[2] >> 24) & 0xf) + '0'; 534 | uart_putc(console, c); 535 | move_cursor(console, row+5, col+15); 536 | text_color(console, YELLOW); 537 | uart_puts(console, "S/N: "); 538 | text_color(console, DEFAULT); 539 | t = ((cid[2] << 8) & 0xffffff00) | (cid[3] >> 24 & 0xff); 540 | ntoa(t, FMT_HEX_LONG, buf); 541 | uart_puts(console, buf); 542 | text_color(console, YELLOW); 543 | move_cursor(console, row+6, col+6); 544 | uart_puts(console, "Manufactured: "); 545 | text_color(console, DEFAULT); 546 | i = (cid[3] >> 8) & 0xf; 547 | uart_putnum(console, FMT_BASE_10, i); 548 | uart_puts(console, "/"); 549 | i = ((cid[3] >> 12) & 0xff) + 2000; 550 | uart_putnum(console, FMT_BASE_10, i); 551 | } 552 | 553 | static char *__csd_struct(int n) { 554 | switch (n) { 555 | case 0: 556 | return "CSD Version 1.0"; 557 | case 1: 558 | return "CSD Version 2.0"; 559 | default: 560 | break; 561 | } 562 | return "Unknown"; 563 | } 564 | 565 | static char *taac_time_constants[16][8] = { 566 | { }, 567 | { "1ns", "10ns", "100ns", "1us", "10us", "100us", "1ms", "10ms" }, 568 | { "1.2ns", "12ns", "120ns", "1.2us", "12us", "120us", "1.2ms", "12ms" }, 569 | { "1.3ns", "13ns", "130ns", "1.3us", "13us", "130us", "1.3ms", "13ms" }, 570 | { "1.5ns", "15ns", "150ns", "1.5us", "15us", "150us", "1.5ms", "15ms" }, 571 | { "2ns", "20ns", "200ns", "2us", "20us", "200us", "2ms", "20ms" }, 572 | { "2.5ns", "25ns", "250ns", "2.5us", "25us", "250us", "2.5ms", "25ms" }, 573 | { "3ns", "30ns", "300ns", "3us", "30us", "300us", "3ms", "30ms" }, 574 | { "3.5ns", "35ns", "350ns", "3.5us", "35us", "350us", "3.5ms", "35ms" }, 575 | { "4ns", "40ns", "400ns", "4us", "40us", "400us", "4ms", "40ms" }, 576 | { "4.5ns", "45ns", "450ns", "4.5us", "45us", "450us", "4.5ms", "45ms" }, 577 | { "5ns", "50ns", "500ns", "5us", "50us", "500us", "5ms", "50ms" }, 578 | { "5.5ns", "55ns", "550ns", "5.5us", "55us", "550us", "5.5ms", "55ms" }, 579 | { "6ns", "60ns", "600ns", "6us", "60us", "600us", "6ms", "60ms" }, 580 | { "7ns", "70ns", "700ns", "7us", "70us", "700us", "7ms", "70ms" }, 581 | { "8ns", "80ns", "800ns", "8us", "80us", "800us", "8ms", "80ms" } 582 | }; 583 | static char *__taac_val(int n) { 584 | int r, c; 585 | r = n & 0x3; 586 | c = (n >> 3) & 0xf; 587 | return taac_time_constants[c][r]; 588 | } 589 | 590 | static char *tran_time_constants[16][8] = { 591 | { }, 592 | { "100kbps", "1Mbps", "10Mbps", "100Mbps", NULL, NULL, NULL, NULL }, 593 | { "120kbps", "1.2Mbps", "12Mbps", "120Mbps", NULL, NULL, NULL, NULL }, 594 | { "130kbps", "1.3Mbps", "13Mbps", "130Mbps", NULL, NULL, NULL, NULL }, 595 | { "150kbps", "1.5Mbps", "15Mbps", "150Mbps", NULL, NULL, NULL, NULL }, 596 | { "200kbps", "2Mbps", "20Mbps", "200Mbps", NULL, NULL, NULL, NULL }, 597 | { "250kbps", "2.5Mbps", "25Mbps", "250Mbps", NULL, NULL, NULL, NULL }, 598 | { "300kbps", "3Mbps", "30Mbps", "300Mbps", NULL, NULL, NULL, NULL }, 599 | { "350kbps", "3.5Mbps", "35Mbps", "350Mbps", NULL, NULL, NULL, NULL }, 600 | { "400kbps", "4Mbps", "40Mbps", "400Mbps", NULL, NULL, NULL, NULL }, 601 | { "450kbps", "4.5Mbps", "45Mbps", "450Mbps", NULL, NULL, NULL, NULL }, 602 | { "500kbps", "5Mbps", "50Mbps", "500Mbps", NULL, NULL, NULL, NULL }, 603 | { "550kbps", "5.5Mbps", "55Mbps", "550Mbps", NULL, NULL, NULL, NULL }, 604 | { "600kbps", "6Mbps", "60Mbps", "600Mbps", NULL, NULL, NULL, NULL }, 605 | { "700kbps", "7Mbps", "70Mbps", "700Mbps", NULL, NULL, NULL, NULL }, 606 | { "800kbps", "8Mbps", "80Mbps", "800Mbps", NULL, NULL, NULL, NULL }, 607 | }; 608 | static char *__tran_val(int n) { 609 | int r, c; 610 | r = n & 0x3; 611 | c = (n >> 3) & 0xf; 612 | return tran_time_constants[c][r]; 613 | } 614 | static char *__dec_val(int n) { 615 | static char buf[40]; 616 | ntoa(n, FMT_BASE_10, buf); 617 | return buf; 618 | } 619 | 620 | static char *__hex_val(int n) { 621 | static char buf[40]; 622 | ntoa(n, FMT_BASE_16 | FMT_ALTERNATE_FORM, buf); 623 | return buf; 624 | } 625 | 626 | static char *__bin_val(int n) { 627 | static char buf[40]; 628 | ntoa(n, FMT_BASE_2 | FMT_ALTERNATE_FORM, buf); 629 | return buf; 630 | } 631 | 632 | static char *__blk_len(int n) { 633 | static char buf[40]; 634 | int i; 635 | i = 1 << n; 636 | ntoa(i, FMT_BASE_10, buf); 637 | return buf; 638 | } 639 | 640 | static char *__sm_val(int n) { 641 | static char buf[40]; 642 | int i; 643 | i = 1 << (n+2); 644 | ntoa(i, FMT_BASE_10, buf); 645 | return buf; 646 | } 647 | 648 | static char *__yn_val(int n) { 649 | return (n) ? "Yes" : "No"; 650 | } 651 | 652 | static char *__en_val(int n) { 653 | return (n) ? "Enabled" : "Disabled"; 654 | } 655 | 656 | static char *__curr_min_val(int n) { 657 | switch (n & 0x7) { 658 | case 0: 659 | return "0.5mA"; 660 | case 1: 661 | return "1mA"; 662 | case 2: 663 | return "5mA"; 664 | case 3: 665 | return "10mA"; 666 | case 4: 667 | return "25mA"; 668 | case 5: 669 | return "35mA"; 670 | case 6: 671 | return "60mA"; 672 | case 7: 673 | return "100mA"; 674 | } 675 | return ""; 676 | } 677 | 678 | static char *__curr_max_val(int n) { 679 | switch (n & 0x7) { 680 | case 0: 681 | return "1mA"; 682 | case 1: 683 | return "5mA"; 684 | case 2: 685 | return "10mA"; 686 | case 3: 687 | return "25mA"; 688 | case 4: 689 | return "35mA"; 690 | case 5: 691 | return "45mA"; 692 | case 6: 693 | return "80mA"; 694 | case 7: 695 | return "200mA"; 696 | } 697 | return ""; 698 | } 699 | 700 | struct { 701 | const char *name; 702 | int msb,lsb; 703 | char * (*fmt)(int); 704 | } v1_vals[] = { 705 | { " CSD Structure", 127, 126, __csd_struct }, 706 | { " TAAC", 119, 112, __taac_val }, 707 | { " NSAC", 111, 104, __dec_val }, 708 | { " TRAN_SPEED", 103, 96, __tran_val }, 709 | { " CCC", 95, 84, __bin_val }, 710 | { " READ_BL_LEN", 83, 80, __blk_len }, 711 | { " READ_BL_PARTIAL", 79, 79, __yn_val }, // Single bit 712 | { "WRITE_BLK_MISALIGN", 78, 78, __yn_val}, // Single bit 713 | { " READ_BLK_MISALIGN", 77, 77, __yn_val}, // Single bit 714 | { " DSR_IMP", 76, 76, __yn_val}, // Single bit 715 | { " C_SIZE", 73, 62, __dec_val}, 716 | { " VDD_R_CURR_MIN", 61, 59, __curr_min_val}, 717 | { " VDD_R_CURR_MAX", 58, 56, __curr_max_val}, 718 | { " VDD_W_CURR_MIN", 55, 53, __curr_min_val}, 719 | { " VDD_W_CURR_MAX", 52, 50, __curr_max_val}, 720 | { " C_SIZE_MULT", 49, 47, __sm_val}, 721 | { " ERASE_BLK_EN", 46, 46, __en_val}, // Single bit 722 | { " SECTOR_SIZE", 45, 39, __dec_val}, 723 | { " WP_GRP_SIZE", 38, 32, __dec_val}, 724 | { " WP_GRP_ENABLE", 31, 31, __en_val}, // Single bit 725 | { " R2W_FACTOR", 28, 26, __bin_val}, 726 | { " WRITE_BL_LEN", 25, 22, __blk_len}, 727 | { " WRITE_BL_PARTIAL", 21, 21, __yn_val}, // Single bit 728 | { " FILE_FORMAT_GRP", 15, 15, __yn_val}, // Single bit 729 | { " COPY", 14, 14, __yn_val}, // Single bit 730 | { "PERM_WRITE_PROTECT", 13, 13, __yn_val}, // Single bit 731 | { " TMP_WRITE_PROTECT", 12, 12, __yn_val}, // Single bit 732 | { " FILE_FORMAT", 11, 10, __dec_val}, 733 | { " CRC", 7, 1, __hex_val}, 734 | { NULL, 0, 0, NULL} 735 | }, v2_vals[] = { 736 | { " CSD Structure", 127, 126, NULL }, 737 | { " TAAC*", 119, 112, NULL }, 738 | { " NSAC*", 111, 104, NULL }, 739 | { " TRAN_SPEED*", 103, 96 , NULL }, 740 | { " CCC", 95, 84, NULL }, 741 | { " READ_BL_LEN*", 83, 80, NULL }, 742 | { " READ_BL_PARTIAL*", 79, 79, NULL }, 743 | { "WRITE_BLK_MISALIGN*", 78, 78, NULL }, 744 | { " READ_BLK_MISALIGN*", 77, 77, NULL }, 745 | { " DSR_IMP", 76, 76, NULL }, 746 | { " C_SIZE", 69, 48, NULL }, 747 | { " ERASE_BLK_LEN*", 46, 46, NULL }, 748 | { " SECTOR_SIZE*", 45, 39, NULL }, 749 | { " WP_GRP_SIZE*", 38, 32, NULL }, 750 | { " WP_GRP_ENABLE*", 31, 31, NULL }, 751 | { " R2W_FACTOR*", 28, 26, NULL }, 752 | { " WRITE_BL_LEN*", 25, 22, NULL }, 753 | { " WRITE_BL_PARTIAL*", 21, 21, NULL }, 754 | { " FILE_FORMAT_GRP*", 15, 15, NULL }, 755 | { " COPY", 14, 14, NULL }, 756 | { " PERM_WRITE_PROTECT", 13, 13, NULL }, 757 | { " TMP_WRITE_PROTECT", 12, 12, NULL }, 758 | { " FILE_FORMAT*", 11, 10, NULL }, 759 | { " CRC", 7, 1, NULL }, 760 | { NULL, 0, 0, NULL} 761 | }; 762 | 763 | // 0b 1 1101 1000 1110 1101 1011 0111 READ_BL_PARTIAL (79,79) 764 | // 0b 1 1101 1000 1110 1101 1011 0111 1 WRITE_BLK_MISALIGN (78,78) 765 | 766 | void debug_csd_v1(uint32_t csd[]); 767 | void debug_csd_v1(uint32_t csd[]) { 768 | int i; 769 | char buf[40]; 770 | DEBUG("CSD V1 DUMP:\n"); 771 | DEBUG(" 127 --- 96\n"); 772 | DEBUG(" +-------+-------+-------+-------+\n "); 773 | ntoa(csd[0], FMT_BASE_2 | FMT_LEADING_ZERO | 4, buf); 774 | DEBUG(buf); 775 | DEBUG(" -- "); 776 | ntoa(csd[0], FMT_BASE_16 | FMT_LEADING_ZERO | 4, buf); 777 | DEBUG(buf); 778 | DEBUG("\n +-------+-------+-------+-------+\n "); 779 | ntoa(csd[1], FMT_BASE_2 | FMT_LEADING_ZERO | 4, buf); 780 | DEBUG(buf); 781 | DEBUG(" -- "); 782 | ntoa(csd[1], FMT_BASE_16 | FMT_LEADING_ZERO | 4, buf); 783 | DEBUG(buf); 784 | DEBUG("\n +-------+-------+-------+-------+\n "); 785 | ntoa(csd[2], FMT_BASE_2 | FMT_LEADING_ZERO | 4, buf); 786 | DEBUG(buf); 787 | DEBUG(" -- "); 788 | ntoa(csd[2], FMT_BASE_16 | FMT_LEADING_ZERO | 4, buf); 789 | DEBUG(buf); 790 | DEBUG("\n +-------+-------+-------+-------+\n "); 791 | ntoa(csd[3], FMT_BASE_2 | FMT_LEADING_ZERO | 4, buf); 792 | DEBUG(buf); 793 | DEBUG(" -- "); 794 | ntoa(csd[3], FMT_BASE_16 | FMT_LEADING_ZERO | 4, buf); 795 | DEBUG(buf); 796 | DEBUG("\n +-------+-------+-------+-------+\n"); 797 | 798 | for (i = 0; v1_vals[i].name != NULL; i++) { 799 | DEBUG(" "); 800 | DEBUG(v1_vals[i].name); 801 | DEBUG(" : "); 802 | ntoa(sdio_bit_slice(csd, 128, v1_vals[i].msb, v1_vals[i].lsb), FMT_BASE_10, buf); 803 | DEBUG(buf); 804 | DEBUG(" -- "); 805 | ntoa(sdio_bit_slice(csd, 128, v1_vals[i].msb, v1_vals[i].lsb), 806 | FMT_BASE_2 | FMT_ALTERNATE_FORM, buf); 807 | DEBUG(buf); 808 | DEBUG("\n"); 809 | } 810 | } 811 | 812 | void debug_csd_v2(uint32_t csd[]); 813 | void debug_csd_v2(uint32_t csd[]) { 814 | int i; 815 | char buf[40]; 816 | DEBUG("CSD V2 DUMP:\n"); 817 | DEBUG(" 127 --- 96\n"); 818 | DEBUG(" +-------+-------+-------+-------+\n "); 819 | ntoa(csd[0], FMT_BASE_2 | FMT_LEADING_ZERO | 4, buf); 820 | DEBUG(buf); 821 | DEBUG(" -- "); 822 | ntoa(csd[0], FMT_BASE_16 | FMT_LEADING_ZERO | 4, buf); 823 | DEBUG(buf); 824 | DEBUG("\n +-------+-------+-------+-------+\n "); 825 | ntoa(csd[1], FMT_BASE_2 | FMT_LEADING_ZERO | 4, buf); 826 | DEBUG(buf); 827 | DEBUG(" -- "); 828 | ntoa(csd[1], FMT_BASE_16 | FMT_LEADING_ZERO | 4, buf); 829 | DEBUG(buf); 830 | DEBUG("\n +-------+-------+-------+-------+\n "); 831 | ntoa(csd[2], FMT_BASE_2 | FMT_LEADING_ZERO | 4, buf); 832 | DEBUG(buf); 833 | DEBUG(" -- "); 834 | ntoa(csd[2], FMT_BASE_16 | FMT_LEADING_ZERO | 4, buf); 835 | DEBUG(buf); 836 | DEBUG("\n +-------+-------+-------+-------+\n "); 837 | ntoa(csd[3], FMT_BASE_2 | FMT_LEADING_ZERO | 4, buf); 838 | DEBUG(buf); 839 | DEBUG(" -- "); 840 | ntoa(csd[3], FMT_BASE_16 | FMT_LEADING_ZERO | 4, buf); 841 | DEBUG(buf); 842 | DEBUG("\n +-------+-------+-------+-------+\n"); 843 | 844 | for (i = 0; v2_vals[i].name != NULL; i++) { 845 | DEBUG(" "); 846 | DEBUG(v2_vals[i].name); 847 | DEBUG(" : "); 848 | ntoa(sdio_bit_slice(csd, 128, v2_vals[i].msb, v2_vals[i].lsb), FMT_BASE_10, buf); 849 | DEBUG(buf); 850 | DEBUG(" -- "); 851 | ntoa(sdio_bit_slice(csd, 128, v2_vals[i].msb, v2_vals[i].lsb), 852 | FMT_BASE_2 | FMT_ALTERNATE_FORM, buf); 853 | DEBUG(buf); 854 | DEBUG("\n"); 855 | } 856 | } 857 | 858 | 859 | void 860 | show_sdio_csd(int row, int col, uint32_t csd[]) { 861 | int i; 862 | int k; 863 | int ver = 0; 864 | uint32_t c_size, c_size_mult, r_blk_len; 865 | char buf[32]; 866 | 867 | move_cursor(console, row, col); 868 | text_color(console, YELLOW); 869 | uart_puts(console, "CSD Information: "); 870 | k = (csd[0] >> 30) & 0x3; 871 | switch (k) { 872 | case 0: 873 | uart_puts(console, "Version 1.0"); 874 | ver = 1; 875 | break; 876 | case 1: 877 | uart_puts(console, "Version 2.0"); 878 | ver = 2; 879 | break; 880 | default: 881 | uart_puts(console, "Unknown Version"); 882 | break; 883 | } 884 | /* compute capacity */ 885 | if (ver == 1) { 886 | debug_csd_v1(csd); 887 | DEBUG("Ver 1 CSD\n"); 888 | c_size = sdio_bit_slice(csd, 128, 73, 62); 889 | c_size_mult = sdio_bit_slice(csd, 128, 49, 47); 890 | c_size_mult = 1 << (c_size_mult + 2); 891 | r_blk_len = sdio_bit_slice(csd, 128, 83, 80); 892 | r_blk_len = 1 << (r_blk_len); 893 | card_caps.size = (c_size + 1) * ((c_size_mult * r_blk_len) >> 10); 894 | for (i = 0; v1_vals[i].name != NULL; i++) { 895 | move_cursor(console, row+1+i, col); 896 | text_color(console, YELLOW); 897 | uart_puts(console, v1_vals[i].name); 898 | uart_puts(console, ": "); 899 | text_color(console, GREEN); 900 | uart_puts(console, v1_vals[i].fmt( 901 | sdio_bit_slice(csd, 128, v1_vals[i].msb, v1_vals[i].lsb))); 902 | // ntoa(sdio_bit_slice(csd, 128, 903 | // v1_vals[i].msb, v1_vals[i].lsb), v1_vals[i].fmt, buf); 904 | // uart_puts(console, buf); 905 | } 906 | row = row + 1 + i; 907 | } else if (ver == 2) { 908 | debug_csd_v2(csd); 909 | DEBUG("Ver 2 CSD\n"); 910 | c_size = sdio_bit_slice(csd, 128, 69, 48); 911 | card_caps.size = (c_size+1) * 512; // Adjust to # of 512 byte blocks 912 | } 913 | card_caps.blocks = card_caps.size << 1; // twice as many blocks 914 | move_cursor(console, row++, col); 915 | text_color(console, YELLOW); 916 | uart_puts(console, "Card size: "); 917 | ntoa(card_caps.size, FMT_BASE_10, buf); 918 | text_color(console, GREEN); 919 | uart_puts(console, buf); 920 | uart_puts(console, "K bytes."); 921 | for (i = 0; i < 4; i++) { 922 | move_cursor(console, row + i, col+2); 923 | text_color(console, YELLOW); 924 | uart_puts(console, "[ "); 925 | uart_putnum(console, FMT_BASE_10, i); 926 | uart_puts(console, " ] : "); 927 | text_color(console, GREEN); 928 | uart_putnum(console, FMT_HEX_LONG | FMT_ALTERNATE_FORM, csd[i]); 929 | } 930 | } 931 | 932 | #define SD_SCR_STRUCT(x) (sdio_bit_slice(x, 64, 63, 60)) 933 | #define SD_SCR_SPEC(x) (sdio_bit_slice(x, 64, 59, 56)) 934 | #define SD_SCR_DATA(x) (sdio_bit_slice(x, 64, 55, 55)) 935 | #define SD_SCR_SECURITY(x) (sdio_bit_slice(x, 64, 54, 52)) 936 | #define SD_SCR_BUSWID(x) (sdio_bit_slice(x, 64, 51, 48)) 937 | #define SD_SCR_SPEC3(x) (sdio_bit_slice(x, 64, 47, 47)) 938 | #define SD_SCR_EXSEC(x) (sdio_bit_slice(x, 64, 46, 43)) 939 | #define SD_SCR_SPEC4(x) (sdio_bit_slice(x, 64, 42, 42)) 940 | #define SD_SCR_CMDS(x) (sdio_bit_slice(x, 64, 35, 32)) 941 | 942 | void 943 | show_sdio_scr(int row, int col, uint32_t scr) { 944 | uint32_t s[2]; 945 | 946 | s[0] = scr; 947 | s[1] = 0; 948 | move_cursor(console, row, col); 949 | text_color(console, YELLOW); 950 | uart_puts(console, " -- SCR Register -- "); 951 | move_cursor(console, row+1, col+12); 952 | uart_puts(console, "SCR Structure: "); 953 | text_color(console, GREEN); 954 | switch (SD_SCR_STRUCT(s)) { 955 | case 0: 956 | uart_puts(console, "Version 1.01-4.00"); 957 | break; 958 | default: 959 | uart_puts(console, "?Reserved?"); 960 | } 961 | text_color(console, YELLOW); 962 | move_cursor(console, row+2, col+13); 963 | uart_puts(console, "SPEC Version: "); 964 | text_color(console, GREEN); 965 | uart_putnum(console, SD_SCR_SPEC(s), FMT_BASE_10); 966 | text_color(console, YELLOW); 967 | move_cursor(console, row+3, col+9); 968 | uart_puts(console, "Data After Erase: "); 969 | text_color(console, GREEN); 970 | uart_puts(console, (SD_SCR_DATA(s) != 0) ? "1" : "0"); 971 | text_color(console, YELLOW); 972 | move_cursor(console, row+4, col+4); 973 | uart_puts(console, "CPRM Security Support: "); 974 | text_color(console, GREEN); 975 | switch (SD_SCR_SECURITY(s)) { 976 | case 0: 977 | uart_puts(console, "None"); 978 | break; 979 | case 1: 980 | uart_puts(console, "Not Used"); 981 | break; 982 | case 2: 983 | uart_puts(console, "SDSC Security (V 1.01)"); 984 | break; 985 | case 3: 986 | uart_puts(console, "SDHC Security (V 2.00)"); 987 | break; 988 | case 4: 989 | uart_puts(console, "SDXC Security (V 3.xx)"); 990 | break; 991 | default: 992 | uart_puts(console, "?Reserved?"); 993 | break; 994 | } 995 | text_color(console, YELLOW); 996 | move_cursor(console, row+5, col+15); 997 | uart_puts(console, "Bus Widths: "); 998 | text_color(console, GREEN); 999 | uart_puts(console, (SD_SCR_BUSWID(s) == 5) ? "1b, 4b" : "?Unk?"); 1000 | text_color(console, YELLOW); 1001 | move_cursor(console, row+6, col+16); 1002 | uart_puts(console, "SD Spec 3: "); 1003 | text_color(console, GREEN); 1004 | uart_puts(console, (SD_SCR_SPEC3(s)) ? "True" : "False"); 1005 | text_color(console, YELLOW); 1006 | move_cursor(console, row+7, col+16); 1007 | uart_puts(console, "SD Spec 4: "); 1008 | text_color(console, GREEN); 1009 | uart_puts(console, (SD_SCR_SPEC4(s)) ? "True" : "False"); 1010 | text_color(console, YELLOW); 1011 | move_cursor(console, row+8, col+8); 1012 | uart_puts(console, "Extended Security: "); 1013 | text_color(console, GREEN); 1014 | uart_puts(console, (SD_SCR_EXSEC(s)) ? "True" : "False"); 1015 | text_color(console, YELLOW); 1016 | move_cursor(console, row+9, col); 1017 | uart_puts(console, "Addt'l Commands Supported: "); 1018 | text_color(console, GREEN); 1019 | if (SD_SCR_CMDS(s)) { 1020 | if (SD_SCR_CMDS(s) & 0x8) { 1021 | uart_puts(console, "CMD58/59, "); 1022 | } 1023 | if (SD_SCR_CMDS(s) & 0x4) { 1024 | uart_puts(console, "CMD48/49, "); 1025 | } 1026 | if (SD_SCR_CMDS(s) & 0x2) { 1027 | uart_puts(console, "CMD23, "); 1028 | } 1029 | if (SD_SCR_CMDS(s) & 0x1) { 1030 | uart_puts(console, "CMD20"); 1031 | } 1032 | } else { 1033 | uart_puts(console, "None"); 1034 | } 1035 | } 1036 | 1037 | 1038 | /* 1039 | * Dump out the SD Card status into the debug console 1040 | */ 1041 | void 1042 | debug_sdio_sdstatus(uint32_t buf[]) { 1043 | int i; 1044 | 1045 | DEBUG("SD Card Status:\n"); 1046 | DEBUG("Bus Width: "); 1047 | switch(sdio_bit_slice(buf, 512, 511, 510)) { 1048 | case 0: 1049 | DEBUG("1 bit\n"); 1050 | break; 1051 | default: 1052 | DEBUG("Reserved\n"); 1053 | break; 1054 | case 2: 1055 | DEBUG("4 bits\n"); 1056 | } 1057 | DEBUG("Secure Mode: "); 1058 | DEBUG((sdio_bit_slice(buf, 512, 509, 509) ? "Yes\n" : "No\n")); 1059 | DEBUG("Card Type: "); 1060 | DEBUG((sdio_bit_slice(buf, 512, 495, 480) ? "SD ROM\n" : "SD Card\n")); 1061 | DEBUG("Size of Protected Area: "); 1062 | uart_putnum(debug_console, FMT_BASE_10, sdio_bit_slice(buf, 512, 479, 448)); 1063 | DEBUG("\nSpeed Class: "); 1064 | switch (sdio_bit_slice(buf, 512, 447,440)) { 1065 | case 0: DEBUG("Class 0\n"); 1066 | break; 1067 | case 1: DEBUG("Class 2\n"); 1068 | break; 1069 | case 2: DEBUG("Class 4\n"); 1070 | break; 1071 | case 3: DEBUG("Class 6\n"); 1072 | break; 1073 | default: 1074 | DEBUG("Reserved\n"); 1075 | } 1076 | i = sdio_bit_slice(buf, 512, 439, 432); 1077 | DEBUG("Performance Move: "); 1078 | if ((i > 0) && (i < 255)) { 1079 | uart_putnum(debug_console, FMT_BASE_10, i); 1080 | DEBUG("MB/sec\n"); 1081 | } else { 1082 | DEBUG((i) ? "Inf\n" : "Undef\n"); 1083 | } 1084 | DEBUG("AU Size: "); 1085 | i = sdio_bit_slice(buf, 512, 431, 428); 1086 | if (i == 0) { 1087 | DEBUG("Undef\n"); 1088 | } else if (i < 10) { 1089 | uart_putnum(debug_console, FMT_BASE_10, 8 << i); 1090 | DEBUG("kB\n"); 1091 | } else { 1092 | DEBUG("Reserved\n"); 1093 | } 1094 | return; 1095 | } 1096 | 1097 | --------------------------------------------------------------------------------