MPC850 SPI Driver 네트워크보드에서구현한 SPI Device Driver 제작및이용방법입니다. 문서작성 : 이재훈 (kingseft.lee@samsung.com) 이용한 SPI EEPROM - X5043/X5045 512 x 8 bit SPI EEPROM (4Kbits = 512bytes) - 제조사 : XICOR (www.xicor.com) - SPI 로억세스제어함 - 구성회로도는아래와같다. - SPI 의 Instruction Set은다음과같다. Instrcution Name Instruction Format Operation WREN 00000110 Enable Write Operations WRDI 00000100 Disable Write Operation RDSR 00000101 Read Status Register WRSR 00000001 Write Status Register READ 0000A011 Read Data from EEPROM WRITE 0000A010 Write Data to EEPROM - SPI Status Register 의 0번째비트인 WIP(Write In Progress) 를검출하여서이것이 0 이면 Write in Progress 동작이멈추었다. 즉, Write 동작이종료되었다는것을판단해야한다. - SPI Status Register를읽은방법은 CS 를 Low로하여디바이스를선택하고, 8 bit RSDR Instruction 을적어주면 Status Register의값이 SO을통해나오게된다. - 아래의그림은 Read Status Register를읽는동작이다.
[SPI Status Register 를읽는동작 ] SPI EEPROM Memory Read/Write 1. Chip select 를 Low 로하여디바이스선택 2. 8 bit READ Instruction을써준다. 3. 8 bit Address를써준다. 4. 8 bit Data를써준다. 주의할것은 512 바이트이므로 0x00 0x1ff 까지이므로어드레스표현이 2바이트를차지한다. 그러므로 0x00 0xFF 까지는어드레스를써주는곳에그냥써주면되고, 0x100 0x1ff 까지는두바이트이므로상위 1바이트값은 2번의 Read Instruction 부분에서 Bit 3 에적어준다. 나머지어드레스는그대로어드레스를써주는곳에적는다. The address is automatically incremented to the next higher address after each byte of data is shifted out. When the highest address is reached, the address counter rolls over to address $000 allowing the read cycle to be continued indefinitely. The read operation is terminated by taking CS high. [Read data from EEPROM]
Write Memory Data to EEPROM 1. Chip Selct Low 2. WREN Instruction 써준다. 3. WRITE Instruction을써준다. 여기에서써줄주소가 0x100을넘는다면 3번째비트에 1을써주어야한다 4. 8 bit 어드레스를써준다. 5. 데이터를써준다. 6. Chip Select를 High로올린다. [Write Data to SPI EEPROM] SPI EEPROM 사용시의주의점 - 16 byte page write mode 를이용한다. - the WRITE operation o requires at least 16 clocks. CS must go low and remain low for the duration of the operation. The host may continue to write up to 16 bytes of data. The only restrinction is that the 16 bytes must reside within the same page. SPI Device driver file 디렉토리 arch/ppc/8xx_io/spi.c spi.c spi device driver source micropatch.c SCC2를 ethernet으로사용할경우반드시패치시켜주어야함. SPI 이용을위하여수정된부분 - arch/ppc/8xx_io/config.in - arch/ppc/8xx_io/makefile - arch/ppc/8xx_io/spi.c
- arch/ppc/8xx_io/micropatch.c - arch/ppc/8xx_io/spi.h /8xx_io/spi.h - arch/ppc/8xx_io/enet.c - drivers/char/mem.c SPI 초기화 Port B 레지스터설정 PBPAR (port B signal assignment register ), PBDIR (port B data direction register ) 를이용하여초기화한다. Signal function Signal PBPAR[DDn] = 0 PBPAR[DDn] = 1 PBDIR[DRn] = 0 PBDIR[DRn] = 1 default PB31 Port B 31 SPISEL --- Vdd PB30 Port B 30 TXD3 SPICLK SPICLK = GND PB29 Port B 29 RXD3 SPIMOSI SPIMOSI = Vdd PB28 Port B 28 BRG3 SPIMISO SPIMISO = SPIMOSI PB16 Port B 16 LIST4 --- --- SPI 드라이버에서사용하는레지스터세팅 cp->cp_pbpar &= ~0x00008000 ; // Port B 16 used cp->cp_pbpar = 0x0000000E ; // cp->cp_pbdat = 0x00008000 ; // C/S active low cp->cp_pbdir = 0x0000800E ; // cp->cp_pbodr &= ~(0x0000800E) ; // 본디바이스드라이버에서는 SPISEL을사용하지않고, PB16 을 C/S 신호로사용한다. SPI 레지스터설정 MPC850 user s manual: Ch31. Serial Peripheral Interface 참조 내부함수목록 void cpm_spi_init(void) : 디바이스드라이버초기화루틴 static int cpm_spi_open(struct inode *, struct file *) : open handler static int cpm_spi_close(struct inode *, struct file *) : close handler static ssize_t cpm_spi_write(struct file *, char *, size_t, loff_t *) : file write handler static ssize_t cpm_spi_read(struct file *, char *, size_t, loff_t *) : file read handler static int cpm_spi_ioctl(struct inode *, struct file *, u_int, u_char *) : ioctl handler
SPI memory 조작 Driver 등록 SPI driver를사용하려면다음과같은명령으로 spi special file을만들어둔다. SPI device는 major 번호 91을갖는다. 주의 : 본구현에서는 MTD및 JFFS2를적용하기위하여수정을하였다. MTD : Major 90 SPI : Major 91 I2C : Major 89 # mknod /dev/spi0 c 91 0 # mknod /dev/spi1 c 91 1 혹은 ramdisk 에이미만들어진 spi node 를이용할수도있다. Driver Open SPI memory 를사용하기전에반드시다음과같은함수호출로 SPI device 를열어야한다. /* Example : open */ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int fd; fd = open( /dev/spi0 /dev/spi0, O_RDWR ); Memory read open() 함수호출로 SPI device를열었으면 read() 함수를사용하여 SPI 메모리의내용을읽어낼수있다. 표준 read() 함수는다음과같은형식을갖는다. #include <unistd.h> ssize_t read( int filedes, void *buff, size_t nbytes ); 특정번지를선택하여읽으려면반드시 lseek() 함수를사용하여읽고싶은주소에위치를맞추 고 read() 함수를호출하면된다.
예제 : /* Example : read */ #include <sys/types.h> #include <unistd.h> unsigned char addr; int size, readcnt; unsigned char buf[32]; size = 2; /* size to read */ lseek( fd, 0, SEEK_SET ); readcnt = read( fd, buf, size ); Memory write open() 함수호출로 SPI device를열었으면 write() 함수를사용하여 SPI 메모리에특정한내용을써넣을수있다. 표준 write() 함수는다음과같은형식을갖는다. #include <unistd.h> ssize_t write ( int filedes, void *buff, size_t nbytes ); 특정번지를선택하여써넣으려면반드시 lseek() 함수를사용하여쓰고싶은주소에위치를맞 추고 write() 함수를호출하면된다. 예제 : /* Example : write #include <sys/types.h> #include <unistd.h> int size, writecnt; unsigned char buf[32]; size = 2; /* size to read */ buf[0] = 1; buf[1] = 2; buf[2] = 3; buf[4] = 4; lseek( fd, 0, SEEK_SET ); writecnt = write( fd, buf, size ); IOCTL function 표준 read() 또는 write() 함수를이용하지않고하나의함수로 read/write를하기위해 ioctl() 함수를사용할수있다. 표준 ioctl() 함수는다음과같은형식을가진다.
#include <sys/ioctl.h> int ioctl( int fd, int request, ); SPI device 를다음과같은형식으로사용할수있다. int ioctl( int fd, int request, struct spicmd_t *arg ); request 0 : read command, 1 : write command arg: typedef struct { unsigned long addr; /* start address to read/write */ unsigned int size; /* size to read/write */ unsigned char *buf; /* buffer pointer */ } spicmd_t; return value: on success returns byte size, on failure returns 1 예제 : memory read /* read example */ spicmd_t spicmd; unsigned char rxbuf[8]; int rxcnt; spicmd.addr = 0x10; spicmd.size = 2; rxcnt = ioctl( fd, 0, &spicmd ); if( rxcnt > 0 ) memcpy( rxbuf, spicmd.buf2, count ); 예제 : memory write /* write example */ spicmd_t spicmd; unsigned char txbuf[] = { 0x10, 0x20, 0x30, 0x40 }; int txcnt; spicmd.addr = 0x10; spicmd.size = sizeof(txbuf); txcnt = ioctl( fd, 1, &cpicmd );
SPI Device Driver for 삼성테크윈네트워크보드 1. 관련파일 /arch/ppc/8xx_io/spi.c /arch/ppc/8xx_io/micropatch.c /arch/ppc/8xx_io/testspi.c /arch/ppc/8xx_io/makefile spi.c spi device drivers for mpc8xx micropatch.c SCC2를 Ethernet으로사용시에는반드시패치를시켜주어야한다. testspi.c spi test program makefile 위의파일들을같이컴파일하기위해서 makefile 을수정함 2. 수정내역 [/arch/ppc/8xx_io/makefile] # # Makefile for the linux MPC8xx ppc-specific parts of comm processor # # Note! Dependencies are done automagically by 'make dep', which also # removes any old dependencies. DON'T put your own dependencies here # unless it's something special (ie not a.c file). # # Note 2! The CFLAGS definition is now in the main makefile... O_TARGET := 8xx_io.o obj-y := commproc.o uart.o obj-$(config_fec_enet) += fec.o obj-$(config_scc_enet) += enet.o obj-$(config_8xx_lcd) += lcd823.o obj-$(config_ucode_patch) += micropatch.o # kingseft obj-$(config_8xx_usb) obj-$(config_8xx_spi) # end of kingseft += usb.o += spi.o include $(TOPDIR)/Rules.make
[arch/ppc/8xx_io/commproc.c] void m8xx_cpm_reset(uint host_page_addr) { volatile immap_t *imp; volatile cpm8xx_t *commproc; pte_t *pte; imp = (immap_t *)IMAP_ADDR; commproc = (cpm8xx_t *)&imp->im_cpm; #ifdef CONFIG_UCODE_PATCH /* Perform a reset. */ commproc->cp_cpcr = (CPM_CR_RST CPM_CR_FLG); /* Wait for it. */ while (commproc->cp_cpcr & CPM_CR_FLG); #endif cpm_load_patch(imp); [/arch/ppc/8xx_io/config.in] # kingseft comment 'Generic MPC8xx Options' bool 'Copy-Back Data Cache (else Writethrough)' CONFIG_8xx_COPYBACK bool 'CPU6 Silicon Errata (860 Pre Rev. C)' CONFIG_8xx_CPU6 bool 'I2C/SPI Microcode Patch' CONFIG_UCODE_PATCH if [ "$CONFIG_UCODE_PATCH" = "y" ]; then bool 'SPI Function Enable' CONFIG_8xx_SPI fi bool 'USB Function Enable' CONFIG_8xx_USB if [! "$CONFIG_8xx_USB" = "n" ]; then bool 'USB debug Message' CONFIG_USB_FN_DEBUG fi # end of kingseft
[/scripts/ktconfig.tk] CONFIG_UCODE_PATCH 추가부분확인할것 [/include/config/ucode/patch.h] #define CONFIG_UCODE_PATCH 1 [/arch/ppc/8xx_io/enet.c] x_io/enet.c] int init scc_enet_init(void) { struct net_device *dev; struct scc_enet_private *cep; int i, j; unsigned char *eap; unsigned long mem_addr; pte_t *pte; bd_t *bd; volatile cbd_t *bdp; volatile cpm8xx_t *cp; volatile scc_t *sccp; volatile scc_enet_t *ep; volatile immap_t *immap; extern unsigned long _get_immr(void); #ifdef CONFIG_8xx_SPI /* FLOYDLSH */ unsigned char tmpbuf[64]; char *s, *e; int getenv_r(u_char *name, u_char *buf, unsigned len ); //extern ssize_t spi_read( u_long addr, char *, size_t ); #endif /* Set Ethernet station address. * * If we performed a MBX diskless boot, the Ethernet controller * has been initialized and we copy the address out into our * own structure. * * All other types of boards supply the address in the board
* information structure, so we copy that into the controller. */ #ifdef CONFIG_8xx_SPI /* FLOYDLSH */ i = getenv_r("ethaddr", tmpbuf, sizeof(tmpbuf)); if( i > 0 ) { s = tmpbuf; for( i=0; i<6; i++ ) { bd->bi_enetaddr[i] = s? simple_strtoul(s, &e, 16) : 0; if(s) s = (*e)? e+1 : e; } } #endif [scripts/ktconfig.tk] CONFIG_8xx_SPI 추가된부분확인 [drivers/char/mem.c] int init chr_dev_init(void) { #ifdef CONFIG_STATUS_LED statusled_init (); /* Status LED should be working *fast* */ #endif if (devfs_register_chrdev(mem_major,"mem",&memory_fops)) printk("unable to get major %d for memory devs\n", MEM_MAJOR); memory_devfs_register(); rand_initialize(); /* kingseft */ #ifdef CONFIG_8xx_SPI cpm_spi_init(); #endif /* end of kingseft */ [include/config/8xx/spi.h] #define CONFIG_8xx_SPI 1