📄 bfsi.c
字号:
/* bfsi.c David Rowe 21 June 2006 Functions for Linux device drivers on the Blackfin that support interfacing the Blackfin to Silicon Labs chips. These functions are in a separate file from the target wcfxs driver so they can be re-used with different drivers, for example unit test software. For various reasons the CPHA=1 (sofware controlled SPISEL) mode needs to be used, for example the SiLabs chip expects SPISEL to go high between 8 bit transfers and the timing the Si3050 expects (Figs 3 & 38 of 3050 data sheet) are closest to Fig 10-12 of the BF533 hardware reference manual. See also unittest/spi for several unit tests.*//* Copyright (C) 2006 David Rowe This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include <linux/module.h>#include <linux/errno.h>#include <linux/fs.h>#include <linux/mm.h>#include <linux/interrupt.h>#include <linux/sched.h>#include <asm/uaccess.h>#include <asm/io.h>#include <asm/bfin5xx_spi.h>#include <linux/delay.h>#include <asm/dma.h>#include <asm/irq.h>#include <linux/proc_fs.h>#include <linux/platform_device.h>#include <linux/spi/spi.h>/* Default for BF537 STAMP is SPORT1 */#if defined(CONFIG_BF537)#define BFSI_SPORT1#endif#undef BFIN_SPI_DEBUG#ifdef BFIN_SPI_DEBUG#define PRINTK(args...) printk(args)#else#define PRINTK(args...)#endif/* I found these macros from the bfin5xx_spi.c driver by Luke Yang useful - thanks Luke :-) */#define DEFINE_SPI_REG(reg, off) \static inline u16 read_##reg(void) \ { return *(volatile unsigned short*)(SPI0_REGBASE + off); } \static inline void write_##reg(u16 v) \ {*(volatile unsigned short*)(SPI0_REGBASE + off) = v;\ __builtin_bfin_ssync();}DEFINE_SPI_REG(CTRL, 0x00)DEFINE_SPI_REG(FLAG, 0x04)DEFINE_SPI_REG(STAT, 0x08)DEFINE_SPI_REG(TDBR, 0x0C)DEFINE_SPI_REG(RDBR, 0x10)DEFINE_SPI_REG(BAUD, 0x14)DEFINE_SPI_REG(SHAW, 0x18)/* constants for isr cycle averaging */#define TC 1024 /* time constant */#define LTC 10 /* base 2 log of TC *//* ping-pong buffer states for debug */#define PING 0#define PONG 1/* misc statics */static u8 *iTxBuffer1;static u8 *iRxBuffer1;static int samples_per_chunk;static int internalclock = 0;static int bfsi_debug = 0;static int init_ok = 0;static u16 chip_select_mask = 0;/* isr callback installed by user */static void (*bfsi_isr_callback)(u8 *read_samples, u8 *write_samples) = NULL;/* debug variables */static int readchunk_first = 0;static int readchunk_second = 0;static int readchunk_didntswap = 0;static int lastreadpingpong;static int bad_x[5];static int last_x[5];static u8 *log_readchunk;static int writechunk_first = 0;static int writechunk_second = 0;static int writechunk_didntswap = 0;static int lastwritepingpong;/* previous and worst case number of cycles we took to process an interrupt */static u32 isr_cycles_last = 0;static u32 isr_cycles_worst = 0;static u32 isr_cycles_average = 0; /* scaled up by 2x */static u32 echo_sams = 0;/* monitor cycles between ISRs */static u32 isr_between_prev = 0;static u32 isr_between_worst = 0;static u32 isr_between_diff = 0;static u32 isr_between_difflastskip = 0;static u32 isr_between_skip = 0;/* freeze ISR activity for test purposes */static int bfsi_freeze = 0;/* sample cycles register of Blackfin */static inline unsigned int cycles(void) { int ret; __asm__ __volatile__ ( "%0 = CYCLES;\n\t" : "=&d" (ret) : : "R1" ); return ret;}/*------------------------- SPI FUNCTIONS -----------------------------*//* After much experimentation I found that (i) TIMOD=00 (i.e. using read_RDBR() to start transfer) was the best way to start transfers and (ii) polling RXS was the best way to end transfers, see p10-30 and p10-31 of BF533 data book. chip_select is the _number_ of the chip select line, e.g. to use SPISEL2 chip_select = 2.*/void bfsi_spi_write_8_bits(u16 chip_select, u8 bits){ u16 flag_enable, flag; if (chip_select < 8) { flag = read_FLAG(); flag_enable = flag & ~(1 << (chip_select + 8)); PRINTK("chip_select: %d write: flag: 0x%04x flag_enable: 0x%04x \n", chip_select, flag, flag_enable); /* drop SPISEL */ write_FLAG(flag_enable); } else { bfin_write_FIO_FLAG_C((1<<chip_select)); __builtin_bfin_ssync(); } /* read kicks off transfer, detect end by polling RXS */ write_TDBR(bits); read_RDBR(); __builtin_bfin_ssync(); do {} while (!(read_STAT() & RXS) ); /* raise SPISEL */ if (chip_select < 8) { write_FLAG(flag); } else { bfin_write_FIO_FLAG_S((1<<chip_select)); __builtin_bfin_ssync(); }}u8 bfsi_spi_read_8_bits(u16 chip_select){ u16 flag_enable, flag, ret; if (chip_select < 8) { flag = read_FLAG(); flag_enable = flag & ~(1 << (chip_select + 8)); PRINTK("read: flag: 0x%04x flag_enable: 0x%04x \n", flag, flag_enable); } else { bfin_write_FIO_FLAG_C((1<<chip_select)); __builtin_bfin_ssync(); } /* drop SPISEL */ write_FLAG(flag_enable); /* read kicks off transfer, detect end by polling RXS, we read the shadow register to prevent another transfer being started While reading we write a dummy tx value, 0xff. For the MMC card, a 0 bit indicates the start of a command sequence therefore an all 1's sequence keeps the MMC card in the current state. */ write_TDBR(0xff); read_RDBR(); __builtin_bfin_ssync(); do {} while (!(read_STAT() & RXS) ); ret = bfin_read_SPI_SHADOW(); /* raise SPISEL */ if (chip_select < 8) { write_FLAG(flag); } else { bfin_write_FIO_FLAG_S((1<<chip_select)); __builtin_bfin_ssync(); } return ret;}/* new_chip_select_mask: the logical OR of all the chip selects we wish to use for SPI, for example if we wish to use SPISEL2 and SPISEL3 chip_select_mask = (1<<2) | (1<<3). baud: The SPI clk divider value, see Blackfin Hardware data book, maximum speed when baud = 2, minimum when baud = 0xffff (0 & 1 disable SPI port). The maximum SPI clk for the Si Labs 3050 is 16.4MHz. On a 100MHz system clock Blackfin this means baud=4 minimum (12.5MHz). For the IP04 some extra code needed to be added to the three SPI routines to handle the use of PF12 as nCSB. It's starting to look a bit messy and is perhaps inefficient.*/void bfsi_spi_init(int baud, u16 new_chip_select_mask) { u16 ctl_reg, flag; int cs, bit; if (baud < 4) { printk("baud = %d may mean SPI clock too fast for Si labs 3050" "consider baud == 4 or greater", baud); } PRINTK("bfsi_spi_init\n"); PRINTK(" new_chip_select_mask = 0x%04x\n", new_chip_select_mask); PRINTK(" FIOD_DIR = 0x%04x\n", bfin_read_FIO_DIR()); /* grab SPISEL/GPIO pins for SPI, keep level of SPISEL pins H */ chip_select_mask |= new_chip_select_mask; flag = 0xff00 | (chip_select_mask & 0xff); /* set up chip selects greater than PF7 */ if (chip_select_mask & 0xff00) { bfin_write_FIO_DIR(bfin_read_FIO_DIR() | (chip_select_mask & 0xff00)); __builtin_bfin_ssync(); } PRINTK(" After FIOD_DIR = 0x%04x\n", bfin_read_FIO_DIR());#if defined(CONFIG_BF537) /* we need to work thru each bit in mask and set the MUX regs */ for(bit=0; bit<8; bit++) { if (chip_select_mask & (1<<bit)) { PRINTK("SPI CS bit: %d enabled\n", bit); cs = bit; if (cs == 1) { PRINTK("set for chip select 1\n"); bfin_write_PORTF_FER(bfin_read_PORTF_FER() | 0x3c00); __builtin_bfin_ssync(); } else if (cs == 2 || cs == 3) { PRINTK("set for chip select 2\n"); bfin_write_PORT_MUX(bfin_read_PORT_MUX() | PJSE_SPI); __builtin_bfin_ssync(); bfin_write_PORTF_FER(bfin_read_PORTF_FER() | 0x3800); __builtin_bfin_ssync(); } else if (cs == 4) { bfin_write_PORT_MUX(bfin_read_PORT_MUX() | PFS4E_SPI); __builtin_bfin_ssync(); bfin_write_PORTF_FER(bfin_read_PORTF_FER() | 0x3840); __builtin_bfin_ssync(); } else if (cs == 5) { bfin_write_PORT_MUX(bfin_read_PORT_MUX() | PFS5E_SPI); __builtin_bfin_ssync(); bfin_write_PORTF_FER(bfin_read_PORTF_FER() | 0x3820); __builtin_bfin_ssync(); } else if (cs == 6) { bfin_write_PORT_MUX(bfin_read_PORT_MUX() | PFS6E_SPI); __builtin_bfin_ssync(); bfin_write_PORTF_FER(bfin_read_PORTF_FER() | 0x3810); __builtin_bfin_ssync(); } else if (cs == 7) { bfin_write_PORT_MUX(bfin_read_PORT_MUX() | PJCE_SPI); __builtin_bfin_ssync(); bfin_write_PORTF_FER(bfin_read_PORTF_FER() | 0x3800); __builtin_bfin_ssync(); } } }#endif /* note TIMOD = 00 - reading SPI_RDBR kicks off transfer */ ctl_reg = SPE | MSTR | CPOL | CPHA | SZ; write_FLAG(flag); write_BAUD(baud); write_CTRL(ctl_reg);}/*-------------------------- RESET FUNCTION ----------------------------*/void bfsi_reset(int reset_bit) { PRINTK("toggle reset\n"); #if (defined(CONFIG_BF533) || defined(CONFIG_BF532)) PRINTK("set reset to PF%d\n",reset_bit); bfin_write_FIO_DIR(bfin_read_FIO_DIR() | (1<<reset_bit)); __builtin_bfin_ssync(); bfin_write_FIO_FLAG_C((1<<reset_bit)); __builtin_bfin_ssync(); udelay(100); bfin_write_FIO_FLAG_S((1<<reset_bit)); __builtin_bfin_ssync();#endif #if defined(CONFIG_BF537) if (reset_bit == 1) { PRINTK("set reset to PF10\n"); bfin_write_PORTF_FER(bfin_read_PORTF_FER() & 0xFBFF); __builtin_bfin_ssync(); bfin_write_PORTFIO_DIR(bfin_read_PORTFIO_DIR() | 0x0400); __builtin_bfin_ssync(); bfin_write_PORTFIO_CLEAR(1<<10); __builtin_bfin_ssync(); udelay(100); bfin_write_PORTFIO_SET(1<<10); __builtin_bfin_ssync(); } else if (reset_bit == 2) { PRINTK("Error: cannot set reset to PJ11\n"); } else if (reset_bit == 3) { PRINTK("Error: cannot set reset to PJ10\n"); } else if (reset_bit == 4) { PRINTK("set reset to PF6\n"); bfin_write_PORTF_FER(bfin_read_PORTF_FER() & 0xFFBF); __builtin_bfin_ssync(); bfin_write_PORTFIO_DIR(bfin_read_PORTFIO_DIR() | 0x0040); __builtin_bfin_ssync(); bfin_write_PORTFIO_CLEAR(1<<6); __builtin_bfin_ssync(); udelay(100); bfin_write_PORTFIO_SET(1<<6); __builtin_bfin_ssync(); } else if (reset_bit == 5) { PRINTK("set reset to PF5\n"); bfin_write_PORTF_FER(bfin_read_PORTF_FER() & 0xFFDF); __builtin_bfin_ssync(); bfin_write_PORTFIO_DIR(bfin_read_PORTFIO_DIR() | 0x0020); __builtin_bfin_ssync(); bfin_write_PORTFIO_CLEAR(1<<5); __builtin_bfin_ssync(); udelay(100); bfin_write_PORTFIO_SET(1<<5); __builtin_bfin_ssync(); } else if (reset_bit == 6) { PRINTK("set reset to PF4\n"); bfin_write_PORTF_FER(bfin_read_PORTF_FER() & 0xFFEF);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -