⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 bfsi.c

📁 IP04是一个使用Blackfin开源硬件结合Asterisk开源软件建立的IPPBX系统.
💻 C
📖 第 1 页 / 共 3 页
字号:
/*  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 + -