omap-mcspi.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 511 行

C
511
字号
/* * BRIEF MODULE DESCRIPTION * *	SPI interface driver for the OMAP24XX Platform * * Copyright 2004 Texas Instruments Inc. * Author: Suresh Reddy. *	   sureshr@ti.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, or (at your *  option) any later version. * *  THIS  SOFTWARE  IS PROVIDED	  ``AS	IS'' AND   ANY	EXPRESS OR IMPLIED *  WARRANTIES,	  INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN *  NO	EVENT  SHALL   THE AUTHOR  BE	 LIABLE FOR ANY	  DIRECT, INDIRECT, *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT *  NOT LIMITED	  TO, PROCUREMENT OF  SUBSTITUTE GOODS	OR SERVICES; LOSS OF *  USE, DATA,	OR PROFITS; OR	BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON *  ANY THEORY OF LIABILITY, WHETHER IN	 CONTRACT, STRICT LIABILITY, OR TORT *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *  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. * *  History: *  ---------- *  2004-10-20 Nishanth Menon - removed wrongly used status variable channelconfigured */#include <linux/module.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/errno.h>#include <linux/delay.h>#include <linux/types.h>#include <asm/system.h>#include <asm/irq.h>#include <asm/hardware.h>#include <asm/io.h>#include "omap-mcspi.h"/*--------Debug prints--------------*/#undef TEST_MCSPI//#define TEST_MCSPI#ifdef TEST_MCSPI#define DPRINTK(fmt, args...) printk(KERN_INFO "\n  %s: %s: " fmt,__FILE__,__FUNCTION__ , ## args)#else#define DPRINTK(fmt, args...)#endif/*----------------------------------*//* General OMAP24xx SPI functions *//* Assumptions: *  OMAP24xx has two SPI modules. For convinience sake we number  *  them as SPI1 and SPI2. SPI1 supports upto 4 channels and SPI2  *  supports upto 2 channels. So we number the channels as follows *   Channel 0  SPI1 Channel 0 *   Channel 1  SPI1 Channel 1 *   Channel 2  SPI1 Channel 2 *   Channel 3  SPI1 Channel 3 *   Channel 4  SPI2 Channel 0 *   Channel 5  SPI2 Channel 1 */unsigned long spi1io_base, spi2io_base;int spi1initialised, spi2initialised;static __inline__ u32 spi_reg_in(const long spi_base, u32 offset){	return readl(spi_base + offset);}static __inline__ u32 spi_reg_out(const long spi_base, u32 offset, u32 val){	return writel(val, spi_base + offset);}int omap24xx_spi1_init(){	DPRINTK("\n spi1 init ");	if (spi1initialised) {		DPRINTK(KERN_INFO " SPI1 already initialised /n");		return -1;	}	DPRINTK("\n before IOREMAP");	spi1io_base =	    (unsigned long)ioremap_nocache(OMAP24xx_SPI1_BASE,					   OMAP24xx_SPI1_SIZE);	if (!spi1io_base) {		printk(KERN_INFO " SPI1 IO_REMAP failed \n");		return -1;	}	spi_reg_out(spi1io_base, MCSPI_SYSCONFIG,		    spi_reg_in(spi1io_base,			       MCSPI_SYSCONFIG) |		    MCSPI_SYSCONFIG_SOFTRESET_START);	while (!(spi_reg_in(spi1io_base, MCSPI_SYSSTATUS))) ;	spi1initialised = 1;	return 0;}int omap24xx_spi2_init(){	if (spi2initialised) {		printk(KERN_INFO " SPI2 already initialised /n");		return -1;	}	spi2io_base =	    (unsigned long)ioremap_nocache(OMAP24xx_SPI2_BASE,					   OMAP24xx_SPI2_SIZE);	if (!spi2io_base) {		printk(KERN_INFO " SPI2 IO_REMAP failed \n");		return -1;	}	spi_reg_out(spi2io_base, MCSPI_SYSCONFIG,		    spi_reg_in(spi2io_base,			       MCSPI_SYSCONFIG) |		    MCSPI_SYSCONFIG_SOFTRESET_START);	while (!(spi_reg_in(spi2io_base, MCSPI_SYSSTATUS))) ;	printk(KERN_INFO "SPI2 initialized");	spi2initialised = 1;	return 0;}int omap24xx_spi1_exit(){	if (spi1initialised) {		spi1initialised = 0;		iounmap((void *)spi1io_base);		return 0;	}	return -1;}int omap24xx_spi2_exit(){	if (spi2initialised) {		spi2initialised = 0;		iounmap((void *)spi2io_base);		return 0;	}	return -1;}int omap24xx_spi_channelconfig(int channel, struct spi_channel_config *config){	if (channel <= 3) {		if (!spi1initialised) {			printk(KERN_INFO " SPI1 not initialised \n");			return -1;		}		/* clear all regs */		spi_reg_out(spi1io_base, MCSPI_MODULCTRL, 0);		spi_reg_out(spi1io_base, MCSPI_CHCONF, 0);		/* configure SPI1 as master/slave */		spi_reg_out(spi1io_base, MCSPI_MODULCTRL,			    (spi_reg_in(spi1io_base, MCSPI_MODULCTRL) | config->			     mode));		/* configure SPI1 as Little/Big Endian */		spi_reg_out(spi1io_base, MCSPI_MODULCTRL,			    (spi_reg_in(spi1io_base, MCSPI_MODULCTRL) | config->			     endianess));		/* configure SPI1 as receive/transmit */		if (config->mode == MASTER) {			spi_reg_out(spi1io_base,				    MCSPI_CHCONF + (channel * CHANNEL_OFFSET),				    spi_reg_in(spi1io_base,					       MCSPI_CHCONF +					       (channel *						CHANNEL_OFFSET)) |				    MCSPI_CHCONF_IS_DL0RECEIVE);			spi_reg_out(spi1io_base,				    MCSPI_CHCONF + (channel * CHANNEL_OFFSET),				    spi_reg_in(spi1io_base,					       MCSPI_CHCONF +					       (channel *						CHANNEL_OFFSET)) |				    MCSPI_CHCONF_DPE1_TRANSMIT |				    MCSPI_CHCONF_DPE0_NO_TRANSMIT);		} else {			spi_reg_out(spi1io_base,				    MCSPI_CHCONF + (channel * CHANNEL_OFFSET),				    spi_reg_in(spi1io_base,					       MCSPI_CHCONF +					       (channel *						CHANNEL_OFFSET)) |				    MCSPI_CHCONF_IS_DL1RECEIVE);			spi_reg_out(spi1io_base,				    MCSPI_CHCONF + (channel * CHANNEL_OFFSET),				    spi_reg_in(spi1io_base,					       MCSPI_CHCONF +					       (channel *						CHANNEL_OFFSET)) |				    MCSPI_CHCONF_DPE0_TRANSMIT);		}		spi_reg_out(spi1io_base,			    MCSPI_CHCONF + (channel * CHANNEL_OFFSET),			    spi_reg_in(spi1io_base,				       MCSPI_CHCONF +				       (channel *					CHANNEL_OFFSET)) | config->			    transmitreceive);		/* configure word Length */		spi_reg_out(spi1io_base,			    MCSPI_CHCONF + (channel * CHANNEL_OFFSET),			    spi_reg_in(spi1io_base,				       MCSPI_CHCONF +				       (channel *					CHANNEL_OFFSET)) | config->wordlength);		/* configure spi polarity */		spi_reg_out(spi1io_base,			    MCSPI_CHCONF + (channel * CHANNEL_OFFSET),			    spi_reg_in(spi1io_base,				       MCSPI_CHCONF +				       (channel *					CHANNEL_OFFSET)) | config->spipolarity);		/* configure clock phase  */		spi_reg_out(spi1io_base,			    MCSPI_CHCONF + (channel * CHANNEL_OFFSET),			    spi_reg_in(spi1io_base,				       MCSPI_CHCONF +				       (channel *					CHANNEL_OFFSET)) | config->clkphase);		/* configure clock polarity */		spi_reg_out(spi1io_base,			    MCSPI_CHCONF + (channel * CHANNEL_OFFSET),			    spi_reg_in(spi1io_base,				       MCSPI_CHCONF +				       (channel *					CHANNEL_OFFSET)) | config->clkpolarity);		/* configure clock divisor */		spi_reg_out(spi1io_base,			    MCSPI_CHCONF + (channel * CHANNEL_OFFSET),			    spi_reg_in(spi1io_base,				       MCSPI_CHCONF +				       (channel *					CHANNEL_OFFSET)) | config->clkdivisor);	} else if (channel <= 5) {		if (!spi2initialised) {			printk(KERN_INFO " SPI2 not initialised \n");			return -1;		}		/* configure SPI2 as master/slave */		spi_reg_out(spi2io_base, MCSPI_MODULCTRL,			    (spi_reg_in(spi2io_base, MCSPI_MODULCTRL) | config->			     mode));		/* configure SPI2 as Little/Big Endian */		spi_reg_out(spi2io_base, MCSPI_MODULCTRL,			    (spi_reg_in(spi2io_base, MCSPI_MODULCTRL) | config->			     endianess));		/* configure SPI2 as receive/transmit */		if (config->mode == MASTER) {			spi_reg_out(spi2io_base,				    MCSPI_CHCONF + (channel * CHANNEL_OFFSET),				    spi_reg_in(spi2io_base,					       MCSPI_CHCONF +					       (channel *						CHANNEL_OFFSET)) |				    MCSPI_CHCONF_IS_DL0RECEIVE);			spi_reg_out(spi2io_base,				    MCSPI_CHCONF + (channel * CHANNEL_OFFSET),				    spi_reg_in(spi2io_base,					       MCSPI_CHCONF +					       (channel *						CHANNEL_OFFSET)) |				    MCSPI_CHCONF_DPE1_TRANSMIT);		} else {			spi_reg_out(spi2io_base,				    MCSPI_CHCONF + (channel * CHANNEL_OFFSET),				    spi_reg_in(spi2io_base,					       MCSPI_CHCONF +					       (channel *						CHANNEL_OFFSET)) |				    MCSPI_CHCONF_IS_DL1RECEIVE);			spi_reg_out(spi2io_base,				    MCSPI_CHCONF + (channel * CHANNEL_OFFSET),				    spi_reg_in(spi2io_base,					       MCSPI_CHCONF +					       (channel *						CHANNEL_OFFSET)) |				    MCSPI_CHCONF_DPE0_TRANSMIT);		}		spi_reg_out(spi2io_base,			    MCSPI_CHCONF + (channel * CHANNEL_OFFSET),			    spi_reg_in(spi2io_base,				       MCSPI_CHCONF +				       (channel *					CHANNEL_OFFSET)) | config->			    transmitreceive);		/* configure word Length */		spi_reg_out(spi2io_base,			    MCSPI_CHCONF + (channel * CHANNEL_OFFSET),			    spi_reg_in(spi2io_base,				       MCSPI_CHCONF +				       (channel *					CHANNEL_OFFSET)) | config->wordlength);		/* configure spi polarity */		spi_reg_out(spi2io_base,			    MCSPI_CHCONF + (channel * CHANNEL_OFFSET),			    spi_reg_in(spi2io_base,				       MCSPI_CHCONF +				       (channel *					CHANNEL_OFFSET)) | config->spipolarity);		/* configure clock phase  */		spi_reg_out(spi2io_base,			    MCSPI_CHCONF + (channel * CHANNEL_OFFSET),			    spi_reg_in(spi2io_base,				       MCSPI_CHCONF +				       (channel *					CHANNEL_OFFSET)) | config->clkphase);		/* configure clock polarity */		spi_reg_out(spi2io_base,			    MCSPI_CHCONF + (channel * CHANNEL_OFFSET),			    spi_reg_in(spi2io_base,				       MCSPI_CHCONF +				       (channel *					CHANNEL_OFFSET)) | config->clkpolarity);		/* configure clock divisor */		spi_reg_out(spi2io_base,			    MCSPI_CHCONF + (channel * CHANNEL_OFFSET),			    spi_reg_in(spi2io_base,				       MCSPI_CHCONF +				       (channel *					CHANNEL_OFFSET)) | config->clkdivisor);	} else {		printk(KERN_INFO " channel number out of range \n");		return -1;	}#ifdef TEST_MCSPI	{		int w;		printk("\n printing MCSPI_CHCONF val ");		w = spi_reg_in(spi1io_base,			       MCSPI_CHCONF + (channel * CHANNEL_OFFSET));		printk("\n MCSPI_CHCONF val = %x", w);	}#endif	return 0;}void omap24xx_spi_enablechannel(int channel){	if (channel <= 3) {		spi_reg_out(spi1io_base,			    MCSPI_CHCTRL + (channel * CHANNEL_OFFSET),			    spi_reg_in(spi1io_base,				       MCSPI_CHCTRL +				       (channel *					CHANNEL_OFFSET)) | MCSPI_CHCTRL_ACTIVE);	} else if (channel <= 5) {		spi_reg_out(spi2io_base,			    MCSPI_CHCTRL + (channel * CHANNEL_OFFSET),			    spi_reg_in(spi2io_base,				       MCSPI_CHCTRL +				       (channel *					CHANNEL_OFFSET)) | MCSPI_CHCTRL_ACTIVE);	} else {		printk(KERN_INFO " unknown channel number \n");	}}void omap24xx_spi_disablechannel(int channel){	if (channel <= 3) {		/* generalize this for all channels later with proper macros --TBD */		if( channel == 0)			while( !(spi_reg_in(spi1io_base, MCSPI_CHSTAT0) & 0x4));		spi_reg_out(spi1io_base,			    MCSPI_CHCTRL + (channel * CHANNEL_OFFSET),			    spi_reg_in(spi1io_base,				       MCSPI_CHCTRL +				       (channel *					CHANNEL_OFFSET)) &			    MCSPI_CHCTRL_NOTACTIVE);	} else if (channel <= 5) {		spi_reg_out(spi2io_base,			    MCSPI_CHCTRL + (channel * CHANNEL_OFFSET),			    spi_reg_in(spi2io_base,				       MCSPI_CHCTRL +				       (channel *					CHANNEL_OFFSET)) |			    MCSPI_CHCTRL_NOTACTIVE);	} else {		printk(KERN_INFO " unknown channel number \n");	}}void omap24xx_spi_writetochannel(int channel, u32 data){	if (channel <= 3) {		/* write data */		while( !(spi_reg_in(spi1io_base, MCSPI_CHSTAT0) & MCSPI_CHSTAT_TXS_EMPTY));		spi_reg_out(spi1io_base, MCSPI_TX + (channel * CHANNEL_OFFSET), data);	} else if (channel <= 5) {		while (!(spi_reg_in(spi2io_base, MCSPI_IRQSTATUS) &			 (TX_OFFSET * channel))) ;		spi_reg_out(spi2io_base, MCSPI_TX + (channel * CHANNEL_OFFSET),			    data);	} else {		printk(KERN_INFO " unknown channel number \n");	}}u32 omap24xx_spi_readfromchannel(int channel){	if (channel <= 3) {		udelay(10);		/* Wait for rx data TODO: make it consistent for all channels */		while( (spi_reg_in(spi1io_base, MCSPI_CHSTAT0) & MCSPI_CHSTAT_RXS_EMPTY));		return spi_reg_in(spi1io_base,				  MCSPI_RX + (channel * CHANNEL_OFFSET));		while( !(spi_reg_in(spi1io_base, MCSPI_CHSTAT0) & MCSPI_CHSTAT_RXS_EMPTY));	} else if (channel <= 5) {		udelay(10);		while (!(spi_reg_in(spi2io_base, MCSPI_IRQSTATUS) &			 (RX_STARTBIT + (RX_OFFSET * channel)))) ;		return spi_reg_in(spi2io_base,				  MCSPI_RX + (channel * CHANNEL_OFFSET));	} else {		printk(KERN_INFO " unknown channel number \n");		return -1;	}}u32 omap24xx_spi_readwritechannel(int channel, u32 data){	if (channel <= 3) {		spi_reg_out(spi1io_base, MCSPI_TX + (channel * CHANNEL_OFFSET),			    data);		udelay(10);		return spi_reg_in(spi1io_base,				  MCSPI_RX + (channel * CHANNEL_OFFSET));	} else if (channel <= 5) {		while (!		       (spi_reg_in(spi2io_base, MCSPI_IRQSTATUS) &			(TX_OFFSET * channel))) ;		spi_reg_out(spi2io_base, MCSPI_TX + (channel * CHANNEL_OFFSET),			    data);		udelay(10);		while (!		       (spi_reg_in(spi2io_base, MCSPI_IRQSTATUS) &			(RX_STARTBIT + (RX_OFFSET * channel)))) ;		return spi_reg_in(spi2io_base,				  MCSPI_RX + (channel * CHANNEL_OFFSET));	} else {		printk(KERN_INFO " unknown channel number \n");		return -1;	}}EXPORT_SYMBOL(omap24xx_spi1_init);EXPORT_SYMBOL(omap24xx_spi2_init);EXPORT_SYMBOL(omap24xx_spi1_exit);EXPORT_SYMBOL(omap24xx_spi2_exit);EXPORT_SYMBOL(omap24xx_spi_readwritechannel);EXPORT_SYMBOL(omap24xx_spi_channelconfig);EXPORT_SYMBOL(omap24xx_spi_enablechannel);EXPORT_SYMBOL(omap24xx_spi_disablechannel);EXPORT_SYMBOL(omap24xx_spi_writetochannel);EXPORT_SYMBOL(omap24xx_spi_readfromchannel);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?