Use the IO Expansion Board

Schematic : ExpansionBoard.pdf
Driver source code : https://github.com/hardkernel/linux/tree/odroidxu3-3.10.y/drivers/hardkernel
Android Example source code : http://dn.odroid.com/ODROID-XU/Expansion_Board

Test O/S : Ubuntu 15.04 (20150710, Kernel version should be 3.10.82 or higher. Otherwise, update the kernel first)

I2C Driver Probe

$ sudo modprobe i2c-gpio-custom bus0=10,33,23,10,10
$ sudo modprobe ioboard-bmp180
$ sudo echo ioboard-bmp180 0x77 > /sys/class/i2c-dev/i2c-10/device/new_device
$ sudo modprobe ioboard-bh1780
$ sudo echo ioboard-bh1780 0x29 > /sys/class/i2c-dev/i2c-10/device/new_device

Get Sensors value on Expansion Board

Enable Sensors

BMP180

$ echo 1 > /sys/class/i2c-dev/i2c-10/device/10-0077/enable

BH1780

$ echo 1 > /sys/class/i2c-dev/i2c-10/device/10-0029/enable

Get values BMP180

$ cat /sys/class/i2c-dev/i2c-10/device/10-0077/temperature
$ cat /sys/class/i2c-dev/i2c-10/device/10-0077/pressure

BH1780

$ cat /sys/class/i2c-dev/i2c-10/device/10-0029/lux

SPI Flash Control on Expansion Board

Compile & run SPI test example source code

$ gcc -o spi_flash_test spi_flash_test.c
$ sudo ./spi_flash_test
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;
}
 
/*---------------------------------------------------------------------------*/

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.

$ sudo -s
$ cd /media/boot
$ dtc -I dtb -O dts ./exynos5422-odroidxu3.dtb > ./exynos5422-odroidxu3.dts

Edit dts file to use vi editor.

$ vi ./exynos5422-odroidxu3.dts

Find string “spi@12d3” & add spi control data.

...
	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 */
	};
...

The value of cs-gpio must be identical to cs-gpios in controller-data section.

Compile dts file & update

$ dtc -O dtb -o ./exynos5422-odroidxu3.dtb ./exynos5422-odroidxu3.dts

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.