Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
en:xu3_expansionboard [2016/05/18 15:20] charles.park |
en:xu3_expansionboard [2017/06/07 17:46] (current) odroid |
||
---|---|---|---|
Line 31: | Line 31: | ||
BH1780 | BH1780 | ||
cat /sys/class/i2c-dev/i2c-10/device/10-0029/lux | cat /sys/class/i2c-dev/i2c-10/device/10-0029/lux | ||
+ | | ||
+ | ==== SPI Flash Control on Expansion Board ==== | ||
+ | |||
+ | === Compile & run SPI test example source code === | ||
+ | |||
+ | <code> | ||
+ | gcc -o spi_flash_test spi_flash_test.c | ||
+ | sudo ./spi_flash_test | ||
+ | </code> | ||
+ | |||
+ | <file c spi_flash_test.c> | ||
+ | /* | ||
+ | * SPI testing program (using spidev driver) | ||
+ | * | ||
+ | * Copyright (c) 2007 MontaVista Software, Inc. | ||
+ | * Copyright (c) 2007 Anton Vorontsov <avorontsov@ru.mvista.com> | ||
+ | * | ||
+ | * This program is free software; you can redistribute it and/or modify | ||
+ | * it under the terms of the GNU General Public License as published by | ||
+ | * the Free Software Foundation; either version 2 of the License. | ||
+ | * | ||
+ | * Cross-compile with cross-gcc -I/path/to/cross-kernel/include | ||
+ | */ | ||
+ | |||
+ | /* | ||
+ | |||
+ | IOBOARD SST25WF020A SPI Flash Test (Use spidev driver) | ||
+ | |||
+ | */ | ||
+ | |||
+ | /*---------------------------------------------------------------------------*/ | ||
+ | #include <stdint.h> | ||
+ | #include <string.h> | ||
+ | #include <unistd.h> | ||
+ | #include <stdio.h> | ||
+ | #include <stdlib.h> | ||
+ | #include <fcntl.h> | ||
+ | #include <sys/ioctl.h> | ||
+ | #include <pthread.h> | ||
+ | #include <linux/spi/spidev.h> | ||
+ | |||
+ | /*---------------------------------------------------------------------------*/ | ||
+ | #define FLASH_SECTOR_4K (4 * 1024) | ||
+ | #define FLASH_TEST_SIZE 256 | ||
+ | |||
+ | const char *SPIDEV_NODE = "/dev/spidev1.0"; | ||
+ | |||
+ | /*---------------------------------------------------------------------------*/ | ||
+ | /*---------------------------------------------------------------------------*/ | ||
+ | /* Read Flash ID */ | ||
+ | #define CMD_READ_ID 0xAB /* 0x90 or 0xAB */ | ||
+ | |||
+ | /* Read memory */ | ||
+ | #define CMD_READ 0x03 | ||
+ | #define CMD_HIGH_SPEED_READ 0x0B | ||
+ | #define CMD_READ_STATUS 0x05 | ||
+ | |||
+ | /* Erase memory */ | ||
+ | #define CMD_ERASE_4KB 0x20 | ||
+ | #define CMD_ERASE_32KB 0x52 | ||
+ | #define CMD_ERASE_64KB 0xD8 | ||
+ | #define CMD_ERASE_ALL 0xC7 /* 0xC7 or 0x60 */ | ||
+ | |||
+ | /* BYTE write command */ | ||
+ | #define CMD_BYTE_WRITE 0x02 | ||
+ | |||
+ | /* Write Enable/Disable CMD */ | ||
+ | #define CMD_WRITE_ENABLE 0x06 | ||
+ | #define CMD_WRITE_DISABLE 0x04 | ||
+ | |||
+ | #define CMD_WRITE_PROTECT 0x01 | ||
+ | /* | ||
+ | BUSY | ||
+ | 1 : internal write operation is in progress | ||
+ | 0 : no internal write operation is in progress | ||
+ | */ | ||
+ | #define STATUS_BUSY 0x01 /* (Read) */ | ||
+ | /* | ||
+ | WEL | ||
+ | 1 : Device is memory write enabled | ||
+ | 0 : Device is not memory write enabled | ||
+ | */ | ||
+ | #define STATUS_WEL 0x02 /* (Read) */ | ||
+ | /* | ||
+ | BP1 BP0 | ||
+ | 0 0 : none protected memory | ||
+ | 0 1 : 030000H-03FFFFH protected | ||
+ | 1 0 : 020000H-03FFFFH protected | ||
+ | 1 1 : 000000H-03FFFFH protected (Power-up default value) | ||
+ | */ | ||
+ | #define STATUS_BP0 0x04 /* (R/W) */ | ||
+ | #define STATUS_BP1 0x08 /* (R/W) */ | ||
+ | /* | ||
+ | BPL | ||
+ | 1 : BP1 and BP0 are read-only bits | ||
+ | 0 : BP1 and BP0 are read/writable (Power-up default value) | ||
+ | */ | ||
+ | #define STATUS_BPL 0x80 /* (R/W) */ | ||
+ | |||
+ | /*---------------------------------------------------------------------------*/ | ||
+ | #define FLASH_SPI_MODE 0 | ||
+ | #define FLASH_SPI_SPEED 500000 | ||
+ | #define FLASH_SPI_BITS 8 | ||
+ | |||
+ | struct spi_flash { | ||
+ | /* spidev file descriptor */ | ||
+ | int fd; | ||
+ | |||
+ | /* SPI mode H/W control */ | ||
+ | unsigned char mode; /* SPI_MODE */ | ||
+ | unsigned char bits; /* SPI Data bits */ | ||
+ | unsigned int speed; /* SPI Control Speed */ | ||
+ | |||
+ | /* Flash control variable */ | ||
+ | unsigned char cmd; | ||
+ | union { | ||
+ | unsigned int addr; | ||
+ | unsigned char data[sizeof(unsigned int)]; | ||
+ | }; | ||
+ | unsigned char tx_data[10]; | ||
+ | unsigned int tx_len; | ||
+ | unsigned char rx_data[FLASH_SECTOR_4K]; | ||
+ | unsigned int rx_len; | ||
+ | }; | ||
+ | |||
+ | /*---------------------------------------------------------------------------*/ | ||
+ | static void pabort(const char *s) | ||
+ | { | ||
+ | perror(s); | ||
+ | abort(); | ||
+ | } | ||
+ | |||
+ | /*---------------------------------------------------------------------------*/ | ||
+ | static void transfer (struct spi_flash *flash) | ||
+ | { | ||
+ | struct spi_ioc_transfer tr[2]; | ||
+ | |||
+ | memset(tr, 0, sizeof(tr)); | ||
+ | memset(flash->rx_data, 0x00, sizeof(flash->rx_data)); | ||
+ | |||
+ | flash->tx_data[0] = flash->cmd; | ||
+ | flash->tx_data[1] = (flash->addr & 0x00FF0000) >> 16; | ||
+ | flash->tx_data[2] = (flash->addr & 0x0000FF00) >> 8; | ||
+ | flash->tx_data[3] = (flash->addr & 0x000000FF); | ||
+ | flash->tx_data[4] = flash->data[3]; /* Dummy Cycle */ | ||
+ | |||
+ | tr[0].tx_buf = (unsigned long)flash->tx_data; | ||
+ | tr[0].len = flash->tx_len < 5 ? flash->tx_len : 5; | ||
+ | |||
+ | if (flash->rx_len) { | ||
+ | tr[1].rx_buf = (unsigned long)flash->rx_data; | ||
+ | tr[1].len = flash->rx_len; | ||
+ | |||
+ | if (ioctl(flash->fd, SPI_IOC_MESSAGE(2), &tr[0]) < 2) | ||
+ | pabort("can't send SPI message"); | ||
+ | |||
+ | flash->rx_len = 0; | ||
+ | } else { | ||
+ | if (ioctl(flash->fd, SPI_IOC_MESSAGE(1), &tr[0]) < 1) | ||
+ | pabort("can't send SPI message"); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /*---------------------------------------------------------------------------*/ | ||
+ | static void spidev_init (struct spi_flash *flash) | ||
+ | { | ||
+ | int ret; | ||
+ | |||
+ | flash->fd = open(SPIDEV_NODE, O_RDWR); | ||
+ | |||
+ | if (flash->fd < 0) | ||
+ | pabort("can't open /dev/spidev1.0"); | ||
+ | |||
+ | /* | ||
+ | SPI H/W Init | ||
+ | SPI Mode = 0, Data bits = 8, Speed = 500Kbps | ||
+ | */ | ||
+ | flash->mode = FLASH_SPI_MODE; | ||
+ | flash->bits = FLASH_SPI_BITS; | ||
+ | flash->speed = FLASH_SPI_SPEED; | ||
+ | |||
+ | /* SPI mode */ | ||
+ | ret = ioctl(flash->fd, SPI_IOC_WR_MODE, &flash->mode); | ||
+ | if (ret == -1) | ||
+ | pabort("can't set SPI mode"); | ||
+ | |||
+ | ret = ioctl(flash->fd, SPI_IOC_RD_MODE, &flash->mode); | ||
+ | if (ret == -1) | ||
+ | pabort("can't get SPI mode"); | ||
+ | |||
+ | /* bits per word */ | ||
+ | ret = ioctl(flash->fd, SPI_IOC_WR_BITS_PER_WORD, &flash->bits); | ||
+ | if (ret == -1) | ||
+ | pabort("can't set bits per word"); | ||
+ | |||
+ | ret = ioctl(flash->fd, SPI_IOC_RD_BITS_PER_WORD, &flash->bits); | ||
+ | if (ret == -1) | ||
+ | pabort("can't get bits per word"); | ||
+ | |||
+ | /* max speed hz */ | ||
+ | ret = ioctl(flash->fd, SPI_IOC_WR_MAX_SPEED_HZ, &flash->speed); | ||
+ | if (ret == -1) | ||
+ | pabort("can't set max speed Hz"); | ||
+ | |||
+ | ret = ioctl(flash->fd, SPI_IOC_RD_MAX_SPEED_HZ, &flash->speed); | ||
+ | if (ret == -1) | ||
+ | pabort("can't get max speed Hz"); | ||
+ | |||
+ | /* H/W Setup Info Display */ | ||
+ | printf("SPI mode: %d\n", flash->mode); | ||
+ | printf("bits per word: %d\n", flash->bits); | ||
+ | printf("max speed: %d Hz (%d KHz)\n" , flash->speed | ||
+ | , flash->speed/1000); | ||
+ | } | ||
+ | |||
+ | /*---------------------------------------------------------------------------*/ | ||
+ | static int flash_read_id (struct spi_flash *flash) | ||
+ | { | ||
+ | flash->cmd = CMD_READ_ID; | ||
+ | flash->addr = 0; | ||
+ | flash->tx_len = 4; | ||
+ | |||
+ | flash->rx_len = 2; | ||
+ | transfer (flash); | ||
+ | |||
+ | printf("%s : rdata[0] = 0x%02X, rdata[1] = 0x%02X\n", | ||
+ | __func__, | ||
+ | flash->rx_data[0], | ||
+ | flash->rx_data[1]); | ||
+ | |||
+ | if ((flash->rx_data[0] == 0xBF) && (flash->rx_data[1] == 0x03)) { | ||
+ | printf("Flash Memory Product is SST(SST25WF020)\n"); | ||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | if (flash->rx_data[0] == 0x34) { | ||
+ | printf("Flash Memory Product is Microcphis(SST25WF020A)\n"); | ||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | pabort("Unknown Flash Memory Product!\n"); | ||
+ | return -1; | ||
+ | } | ||
+ | /*---------------------------------------------------------------------------*/ | ||
+ | static int flash_busy_check(int spi_fd, int udelay) | ||
+ | { | ||
+ | struct spi_flash flash; | ||
+ | unsigned char retry_cnt = 0; | ||
+ | |||
+ | do { | ||
+ | flash.fd = spi_fd; | ||
+ | flash.cmd = CMD_READ_STATUS; | ||
+ | flash.tx_len = 1; | ||
+ | |||
+ | /* Status read len */ | ||
+ | flash.rx_len = 1; | ||
+ | |||
+ | transfer(&flash); | ||
+ | usleep(udelay); | ||
+ | |||
+ | if (retry_cnt++ > 1000) { | ||
+ | printf("%s : Timeout Error!\n", __func__); | ||
+ | return -1; | ||
+ | } | ||
+ | |||
+ | } while(flash.rx_data[0] & STATUS_BUSY); | ||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | /*---------------------------------------------------------------------------*/ | ||
+ | static void flash_access_enable(int spi_fd, unsigned char enable) | ||
+ | { | ||
+ | struct spi_flash flash; | ||
+ | |||
+ | flash.fd = spi_fd; | ||
+ | flash.cmd = enable ? CMD_WRITE_ENABLE : CMD_WRITE_DISABLE; | ||
+ | flash.tx_len = 1; | ||
+ | |||
+ | flash.rx_len = 0; | ||
+ | |||
+ | transfer(&flash); | ||
+ | |||
+ | if (flash_busy_check(spi_fd, 100) != 0) { | ||
+ | pabort("Busy check Error!\n"); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /*---------------------------------------------------------------------------*/ | ||
+ | static void flash_data_erase(struct spi_flash *flash) | ||
+ | { | ||
+ | flash->cmd = CMD_ERASE_4KB; | ||
+ | flash->addr = 0; | ||
+ | flash->tx_len = 4; | ||
+ | |||
+ | flash->rx_len = 0; | ||
+ | |||
+ | flash_access_enable(flash->fd, 1); | ||
+ | transfer(flash); | ||
+ | flash_access_enable(flash->fd, 0); | ||
+ | |||
+ | if (flash_busy_check(flash->fd, 100) != 0) { | ||
+ | pabort("Busy check Error!\n"); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /*---------------------------------------------------------------------------*/ | ||
+ | static void flash_data_write(struct spi_flash *flash) | ||
+ | { | ||
+ | int i; | ||
+ | |||
+ | flash->cmd = CMD_BYTE_WRITE; | ||
+ | flash->addr = 0; | ||
+ | flash->tx_len = 5; | ||
+ | |||
+ | flash->rx_len = 0; | ||
+ | for (i = 0; i < FLASH_TEST_SIZE; i++) { | ||
+ | |||
+ | flash->addr = i; | ||
+ | flash->data[3] = i; | ||
+ | |||
+ | flash_access_enable(flash->fd, 1); | ||
+ | transfer(flash); | ||
+ | flash_access_enable(flash->fd, 0); | ||
+ | |||
+ | if (flash_busy_check(flash->fd, 100) != 0) { | ||
+ | pabort("Busy check Error!\n"); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /*---------------------------------------------------------------------------*/ | ||
+ | static void flash_data_dump(struct spi_flash *flash) | ||
+ | { | ||
+ | int i; | ||
+ | |||
+ | flash->cmd = CMD_READ; | ||
+ | flash->addr = 0; | ||
+ | flash->tx_len = 4; | ||
+ | |||
+ | flash->rx_len = FLASH_TEST_SIZE; | ||
+ | transfer(flash); | ||
+ | |||
+ | if (flash_busy_check(flash->fd, 100) != 0) { | ||
+ | pabort("Busy check Error!\n"); | ||
+ | } | ||
+ | |||
+ | for (i = 0; i < FLASH_TEST_SIZE; i++) { | ||
+ | if((i % 16) == 0) printf("\n"); | ||
+ | printf("0x%02X ", flash->rx_data[i]); | ||
+ | } | ||
+ | printf("\n"); | ||
+ | } | ||
+ | |||
+ | /*---------------------------------------------------------------------------*/ | ||
+ | static void flash_write_protect(int spi_fd, unsigned char status) | ||
+ | { | ||
+ | struct spi_flash flash; | ||
+ | |||
+ | flash_access_enable(spi_fd, 1); | ||
+ | |||
+ | flash.fd = spi_fd; | ||
+ | flash.cmd = CMD_WRITE_PROTECT; | ||
+ | flash.data[2] = status; | ||
+ | flash.tx_len = 2; | ||
+ | |||
+ | flash.rx_len = 0; | ||
+ | |||
+ | transfer(&flash); | ||
+ | |||
+ | flash_access_enable(spi_fd, 0); | ||
+ | } | ||
+ | |||
+ | /*---------------------------------------------------------------------------*/ | ||
+ | int main (void) | ||
+ | { | ||
+ | struct spi_flash *flash; | ||
+ | |||
+ | flash = (struct spi_flash *)malloc(sizeof(struct spi_flash)); | ||
+ | |||
+ | if (flash == NULL) | ||
+ | pabort("Can't allocation for spi flash!\n"); | ||
+ | |||
+ | spidev_init(flash); | ||
+ | |||
+ | /* flash id read */ | ||
+ | flash_read_id(flash); | ||
+ | |||
+ | /* software write protect disable */ | ||
+ | flash_write_protect(flash->fd, 0); | ||
+ | |||
+ | printf("\nFlash Memory Dump : Size = %d\n", FLASH_TEST_SIZE); | ||
+ | flash_data_dump(flash); | ||
+ | |||
+ | flash_data_erase(flash); | ||
+ | printf("\nFlash Memory Dump (after erase) : Size = %d\n", | ||
+ | FLASH_TEST_SIZE); | ||
+ | flash_data_dump(flash); | ||
+ | |||
+ | flash_data_write(flash); | ||
+ | printf("\nFlash Memory Dump (after write) : Size = %d\n", | ||
+ | FLASH_TEST_SIZE); | ||
+ | flash_data_dump(flash); | ||
+ | |||
+ | close(flash->fd); | ||
+ | free(flash); | ||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | /*---------------------------------------------------------------------------*/ | ||
+ | |||
+ | </file> | ||
+ | |||
+ | |||
+ | If you use an old kernel, you must edit/compile the device-tree source first to enable spidev on your **ODROID-XU4/XU3** | ||
+ | |||
+ | === How to fix device tree blob with command line === | ||
+ | Install device tree compiler package. | ||
+ | sudo apt-get install device-tree-compiler | ||
+ | |||
+ | Generate an exynos5422-odroidxu3.dts file from the stock dtb file. | ||
+ | <code> | ||
+ | sudo -s | ||
+ | cd /media/boot | ||
+ | dtc -I dtb -O dts ./exynos5422-odroidxu3.dtb > ./exynos5422-odroidxu3.dts | ||
+ | </code> | ||
+ | |||
+ | Edit dts file to use vi editor. | ||
+ | <code> | ||
+ | vi ./exynos5422-odroidxu3.dts | ||
+ | </code> | ||
+ | |||
+ | Find string "spi@12d3" & add spi control data. | ||
+ | |||
+ | <code> | ||
+ | ... | ||
+ | spi@12d30000 { | ||
+ | compatible = "samsung,exynos5410-spi"; | ||
+ | reg = <0x12d30000 0x100>; | ||
+ | interrupts = <0x0 0x45 0x0>; | ||
+ | dma-mode; | ||
+ | dmas = <0x43 0x5 0x43 0x4>; | ||
+ | dma-names = "tx", "rx"; | ||
+ | swap-mode; | ||
+ | #address-cells = <0x1>; | ||
+ | #size-cells = <0x0>; | ||
+ | clocks = <0x2 0x5cb 0x2 0x1027>; | ||
+ | clock-names = "spi", "spi_busclk0"; | ||
+ | pinctrl-names = "default"; | ||
+ | pinctrl-0 = <0x44>; | ||
+ | status = "okay"; | ||
+ | cs-gpios = <0x45 0x5 0x0>; | ||
+ | |||
+ | /* ADD Line Start(spi control data) */ | ||
+ | samsung,spi-src-slk = <0>; | ||
+ | num-cs = <0>; | ||
+ | |||
+ | spidev { | ||
+ | compatible = "spidev"; | ||
+ | reg = <0>; | ||
+ | spi-max-frequency = <20000000>; | ||
+ | |||
+ | controller-data { | ||
+ | cs-gpio = <0x45 0x5 0x0>; | ||
+ | samsung,spi-feedback-delay = <0>; | ||
+ | }; | ||
+ | }; | ||
+ | /* ADD Line Ebd */ | ||
+ | }; | ||
+ | ... | ||
+ | </code> | ||
+ | |||
+ | <WRAP center round important 100%> | ||
+ | The value of **cs-gpio** must be identical to **cs-gpios** in controller-data section. | ||
+ | </WRAP> | ||
+ | |||
+ | Compile dts file & update | ||
+ | <code> | ||
+ | dtc -O dtb -o ./exynos5422-odroidxu3.dtb ./exynos5422-odroidxu3.dts | ||
+ | </code> | ||
+ | |||
+ | Reboot. | ||
+ | reboot | ||
+ | Check your SPI node. | ||
+ | ls /dev/spidev* | ||
+ | |||
+ | After update, you might need a hard-boot. Reboot doesn't access the updated dtb file from time to time. | ||
+ | |||
+ |