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

📄 mp200_spi.c

📁 The attached file is the driver of SPI
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *  File Name	    : linux/drivers/char/mp200_spi.c *  Function	    : MP200 SPI interface *  Release Version : Ver 1.03 *  Release Date    : 2006/11/29 * *  Copyright (C) NEC Electronics Corporation 2005-2006 * * *  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 Free Softwere Foundation; either version 2 *  of 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 warrnty 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., 59 Temple Place - Suite 330, Boston, *  MA 02111-1307, USA. * */#include <linux/module.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/dma-mapping.h>#include <linux/interrupt.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/dma.h>#include <asm/arch/smu.h>#include <asm/arch/pmu.h>#include "mp200_spi.h"static void spi_interrupt_dma_rx(void *, int, int);static void spi_interrupt_dma_tx(void *, int, int);static unsigned int masterread = 10;module_param(masterread, uint, 0444);static unsigned int masterwrite = 30;module_param(masterwrite, uint, 0444);static unsigned int masterloop = 400;module_param(masterloop, uint, 0444);static int spi_clock_source = 0;/* spi register info (SP0) */static spi_regs_t regs_sp0 = {	.mode = &SPx_MODE(MP200_SP0_BASE),	.pol = &SPx_POL(MP200_SP0_BASE),	.control = &SPx_CONTROL(MP200_SP0_BASE),	.tx_tim = &SPx_TX_TIM(MP200_SP0_BASE),	.tx_data = &SPx_TX_DATA(MP200_SP0_BASE),	.rx_data = &SPx_RX_DATA(MP200_SP0_BASE),	.status = &SPx_STATUS(MP200_SP0_BASE),	.raw_status = &SPx_RAW_STATUS(MP200_SP0_BASE),	.enset = &SPx_ENSET(MP200_SP0_BASE),	.enclr = &SPx_ENCLR(MP200_SP0_BASE),	.ffclr = &SPx_FFCLR(MP200_SP0_BASE),	.control2 = &SPx_CONTROL2(MP200_SP0_BASE),};/* spi register info (SP1) */static spi_regs_t regs_sp1 = {	.mode = &SPx_MODE(MP200_SP1_BASE),	.pol = &SPx_POL(MP200_SP1_BASE),	.control = &SPx_CONTROL(MP200_SP1_BASE),	.tx_tim = &SPx_TX_TIM(MP200_SP1_BASE),	.tx_data = &SPx_TX_DATA(MP200_SP1_BASE),	.rx_data = &SPx_RX_DATA(MP200_SP1_BASE),	.status = &SPx_STATUS(MP200_SP1_BASE),	.raw_status = &SPx_RAW_STATUS(MP200_SP1_BASE),	.enset = &SPx_ENSET(MP200_SP1_BASE),	.enclr = &SPx_ENCLR(MP200_SP1_BASE),	.ffclr = &SPx_FFCLR(MP200_SP1_BASE),	.control2 = &SPx_CONTROL2(MP200_SP1_BASE),};/* transfer info (SP0) */static spi_trans_t trans_sp0 = {	.rx_lch = MP200_DMAC_P2M_LCH1,	.tx_lch = MP200_DMAC_M2P_LCH1,	.rx_data = SPx_RX_DATA_PHYS(MP200_SP0_BASE),	.tx_data = SPx_TX_DATA_PHYS(MP200_SP0_BASE),	.int_spi = INT_SP0,	.spinlock = SPIN_LOCK_UNLOCKED,};/* transfer info (SP1) */static spi_trans_t trans_sp1 = {	.rx_lch = MP200_DMAC_P2M_LCH2,	.tx_lch = MP200_DMAC_M2P_LCH2,	.rx_data = SPx_RX_DATA_PHYS(MP200_SP1_BASE),	.tx_data = SPx_TX_DATA_PHYS(MP200_SP1_BASE),	.int_spi = INT_SP1,	.spinlock = SPIN_LOCK_UNLOCKED,};/* spi config info (SP0) */static SPI_CONFIG config_sp0 = {	.dev = SPI_DEV_SP0,	.nbr = SPI_NB_16BIT,	.nbw = SPI_NB_16BIT,	.cs_sel = SPI_CS_SEL_CS0,	.m_s = SPI_M_S_MASTER,	.dma = SPI_DMA_OFF,	.pol = SPI_POL_SP0_CS0,	.sclk = SPI_SCLK_3MHZ};/* spi config info (SP1) */static SPI_CONFIG config_sp1 = {	.dev = SPI_DEV_SP1,	.nbr = SPI_NB_16BIT,	.nbw = SPI_NB_16BIT,	.cs_sel = SPI_CS_SEL_CS0,	.m_s = SPI_M_S_MASTER,	.dma = SPI_DMA_ON,	.pol = SPI_POL_SP1_CS0,	.sclk = SPI_SCLK_3MHZ};/* smu info (SP0) */static spi_smu_t smu_sp0 = {	.pclk = MP200_CLOCKGATE_SPI0_PCLK,	.sclk = MP200_CLOCKGATE_SPI0_SCLK,	.pclk_ctrl = MP200_CLKCTRL_SPI0PCLK,	.sclk_ctrl = MP200_CLKCTRL_SPI0SCLKLP,	.reset = MP200_RESETDEVICE_SPI0,	.sclk_div = SMU_SPI0_SCLK_DIV,	.pctl_spi = SPI_SMU_PCTL_SPI0_SK,};/* smu info (SP1) */static spi_smu_t smu_sp1 = {	.pclk = MP200_CLOCKGATE_SPI1_PCLK,	.sclk = MP200_CLOCKGATE_SPI1_SCLK,	.pclk_ctrl = MP200_CLKCTRL_SPI1PCLK,	.sclk_ctrl = MP200_CLKCTRL_SPI1SCLKLP,	.reset = MP200_RESETDEVICE_SPI1,	.sclk_div = SMU_SPI1_SCLK_DIV,	.pctl_spi = SPI_SMU_PCTL_SPI1_SK,};/* spi private data info */static spi_data_t spi_private[] = {	{	 .regs = &regs_sp0,	 .trans = &trans_sp0,	 .config = &config_sp0,	 .smu = &smu_sp0,	 .probe = 0,	 },	{	 .regs = &regs_sp1,	 .trans = &trans_sp1,	 .config = &config_sp1,	 .smu = &smu_sp1,	 .probe = 0,	 },};static int spi_get_clock_source(void){	unsigned long plln, pllm, pllp;	unsigned long pllclk, nrm_div;	static const unsigned long smu_table_lplln[] = {		122880, 126976, 131072, 135168, 139264, 143360,		147456, 151552, 155648, 159744, 163840	};	int clock;	switch (inl(SMU_PLLSEL) & MASK_SMU_PLLSEL) {	case SMU_PLLSEL_OSC:		/* 12MHz */		clock = 12000;		break;	case SMU_PLLSEL_RTC:		/* 32KHz */		clock = 32;		break;	case SMU_PLLSEL_PLL:		/* PLL1 */		if (inl(SMU_PLLSEL2) & SMU_NRM_CK_SEL) {			/* LPLL */			plln = inl(SMU_LPLL_CTRL1);			pllclk = smu_table_lplln[plln];		} else {			/* HPLL */			plln = inl(SMU_HPLL_CTRL1) + 1;			pllm = inl(SMU_HPLL_CTRL2) + 1;			pllp = inl(SMU_HPLL_CTRL3);			pllclk = (12000 * plln) / pllm;			if (pllp != 0) {				pllclk >>= 1;			}		}		nrm_div = (inl(SMU_AUTO_FRQ_CHANGE) & 0xF000000) >> 24;		clock = pllclk / (nrm_div + 1);		break;	default:		/* 12MHz */		clock = 12000;		break;	}#ifdef SPI_DEBUG	printk(KERN_INFO "%s(): clock = %d[KHz]\n", __FUNCTION__, clock);#endif	return clock;}static void spi_sft_reset(spi_data_t * spi){	if (spi == NULL) {		return;	}	*spi->regs->control |= SPx_CONTROL_RST;	udelay((1000 * 4 / spi->config->sclk) + 1);	*spi->regs->control &= ~(SPx_CONTROL_RST);}static void spi_power_on(spi_data_t * spi){	mp200_pmu_clkctrl_off(spi->smu->sclk_ctrl);	mp200_pmu_clkctrl_off(spi->smu->pclk_ctrl);	mp200_pmu_open_clockgate(spi->smu->pclk);	mp200_pmu_open_clockgate(spi->smu->sclk);	mp200_pmu_unreset_device(spi->smu->reset);	mp200_pmu_clkctrl_on(spi->smu->pclk_ctrl);	mp200_pmu_clkctrl_on(spi->smu->sclk_ctrl);}static void spi_power_off(spi_data_t * spi){	mp200_pmu_clkctrl_off(spi->smu->sclk_ctrl);	mp200_pmu_clkctrl_off(spi->smu->pclk_ctrl);	mp200_pmu_reset_device(spi->smu->reset);	mp200_pmu_close_clockgate(spi->smu->sclk);	mp200_pmu_close_clockgate(spi->smu->pclk);}static unsigned int spi_set_sclk_div(spi_data_t * spi, unsigned int sclk){	int div;	if (spi == NULL) {		return -EINVAL;	}	if (sclk < SPI_SCLK_1500KHZ) {		sclk = SPI_SCLK_1500KHZ;	} else if (sclk > SPI_SCLK_48MHZ) {		sclk = SPI_SCLK_48MHZ;	}	div = (spi_clock_source / sclk) - 1;	if (div < 1) {		div = 1;		sclk = spi_clock_source / (div + 1);	} else if (div > 255) {		div = 255;		sclk = spi_clock_source / (div + 1);	}	outl(div, spi->smu->sclk_div);#ifdef SPI_DEBUG	printk(KERN_INFO "%s(): sclk(1/%d) = %d[KHz]\n", __FUNCTION__,	       (div + 1), sclk);#endif	return sclk;}static unsigned int spi_unit(unsigned int bit){	if (bit == 0) {		return 0;	} else if (bit <= SPI_NB_8BIT) {		return 1;	} else if (bit <= SPI_NB_16BIT) {		return 2;	} else {		return 4;	}}static void spi_init_data(spi_data_t * spi){	spi->trans->dma_err = 0;	spi->trans->spi_err = 0;	spi->trans->size = 0;}static int spi_allocate_buffer(spi_data_t * spi, unsigned int size){	dma_addr_t phys;	void *virt;	if (spi == NULL) {		return -EINVAL;	}	virt = dma_alloc_coherent(NULL, size, &phys, 0);	if (virt == NULL) {		printk(KERN_INFO "%s: memory allocation error\n", SPI_NAME);		return -ENOMEM;	}	spi->trans->buf.dma_addr = phys;	spi->trans->buf.addr = (unsigned long)virt;	spi->trans->buf.size = size;	return 0;}static void spi_free_buffer(spi_data_t * spi){	if (spi == NULL) {		return;	}	dma_free_coherent(NULL, spi->trans->buf.size,			  (void *)(spi->trans->buf.addr),			  spi->trans->buf.dma_addr);	spi->trans->buf.dma_addr = 0;	spi->trans->buf.addr = 0;	spi->trans->buf.size = 0;}static int spi_request_dma(spi_data_t * spi){	int ret;	if (spi == NULL) {		return -EINVAL;	}	ret =	    mp200_request_dma(spi->trans->rx_lch, SPI_NAME,			      spi_interrupt_dma_rx, (void *)spi,			      &spi->trans->dma_rx_regs);	if (ret < 0) {#ifdef SPI_DEBUG		printk(KERN_INFO "%s(): error\n", __FUNCTION__);#endif		return ret;	}	ret =	    mp200_request_dma(spi->trans->tx_lch, SPI_NAME,			      spi_interrupt_dma_tx, (void *)spi,			      &spi->trans->dma_tx_regs);	if (ret < 0) {#ifdef SPI_DEBUG		printk(KERN_INFO "%s(): error\n", __FUNCTION__);#endif		mp200_free_dma(spi->trans->rx_lch);		return ret;	}	return 0;}static void spi_free_dma(spi_data_t * spi){	if (spi == NULL) {		return;	}	mp200_free_dma(spi->trans->rx_lch);	mp200_free_dma(spi->trans->tx_lch);}static int spi_config(spi_data_t * spi, SPI_CONFIG * config){	unsigned int mode = 0;	unsigned int pol = 0;	unsigned int csw = 0;	unsigned int csw_pol = 0;	unsigned int nb = 0;	if (spi == NULL) {		return -EINVAL;	}	/* dev */	if ((config->dev != SPI_DEV_SP0) && (config->dev != SPI_DEV_SP1)) {		return -EINVAL;	}	/* bit length */	if (spi->trans->state == SPI_READ) {		nb = config->nbr;	} else if (spi->trans->state == SPI_WRITE) {		nb = config->nbw;	} else if (spi->trans->state == SPI_RW) {		nb = config->nbr + config->nbw;	} else {		nb = SPI_NB_16BIT;	}	if ((nb < SPI_NB_8BIT) || (SPI_NB_32BIT < nb)) {		return -EINVAL;	}	mode |= ((nb - 1) << 8);	/* chip select */	switch (config->cs_sel) {	case SPI_CS_SEL_CS0:	case SPI_CS_SEL_CS1:	case SPI_CS_SEL_CS2:	case SPI_CS_SEL_CS3:		mode |= config->cs_sel;		break;	default:		return -EINVAL;	}	/* master/slave */	switch (config->m_s) {	case SPI_M_S_MASTER:	case SPI_M_S_SLAVE:		mode |= config->m_s;		break;	default:		return -EINVAL;	}	/* dma on/off */	switch (config->dma) {	case SPI_DMA_OFF:	case SPI_DMA_ON:		mode |= config->dma;		break;	default:		return -EINVAL;	}	/* pol */	csw = (config->pol & SPI_CSW_MASK);	if ((csw < SPI_CSW_2CLK) || (SPI_CSW_16CLK < csw)) {		return -EINVAL;	}	pol = (config->pol & ~(SPI_CSW_MASK));	if ((pol & ~(SPI_POL_MASK)) != 0) {		return -EINVAL;	}	csw_pol = csw;	if (config->dev == SPI_DEV_SP0) {		csw_pol |= ((SPI_POL_SP0_CS3 & SPI_POL_MASK) << 9);		csw_pol |= ((SPI_POL_SP0_CS2 & SPI_POL_MASK) << 6);		csw_pol |= ((SPI_POL_SP0_CS1 & SPI_POL_MASK) << 3);		csw_pol |= ((SPI_POL_SP0_CS0 & SPI_POL_MASK) << 0);	} else {		csw_pol |= ((SPI_POL_SP1_CS3 & SPI_POL_MASK) << 9);		csw_pol |= ((SPI_POL_SP1_CS2 & SPI_POL_MASK) << 6);		csw_pol |= ((SPI_POL_SP1_CS1 & SPI_POL_MASK) << 3);		csw_pol |= ((SPI_POL_SP1_CS0 & SPI_POL_MASK) << 0);	}	csw_pol &= ~(SPI_POL_MASK << ((config->cs_sel >> 4) * 3));	csw_pol |= (pol << ((config->cs_sel >> 4) * 3));	/* set SMU_PCTL_SPI */	if (config->dev == SPI_DEV_SP1) {		outl(inl(SMU_PCTL_SPI) & ~(SPI_SMU_PCTL_SPI1), SMU_PCTL_SPI);	}	if (config->m_s == SPI_M_S_MASTER) {		outl((inl(SMU_PCTL_SPI) & ~(spi->smu->pctl_spi)), SMU_PCTL_SPI);	} else {		outl((inl(SMU_PCTL_SPI) | spi->smu->pctl_spi), SMU_PCTL_SPI);	}	/* set sclk */	config->sclk = spi_set_sclk_div(spi, config->sclk);	/* set SPx_POL */	if (*spi->regs->pol != csw_pol) {		*spi->regs->pol = csw_pol;	}	/* set SPx_TX_TIM */	if (*spi->regs->tx_tim != SPx_TX_TIM_1WORD) {		*spi->regs->tx_tim = SPx_TX_TIM_1WORD;	}	/* set SPx_MODE */	if (*spi->regs->mode != mode) {		*spi->regs->mode = mode;		spi_sft_reset(spi);	}	memcpy(spi->config, config, sizeof(SPI_CONFIG));	return 0;}static int spi_xferbytes(spi_data_t * spi, char *buf, unsigned int count, unsigned int flags){	unsigned int data = 0;	int ret = 0;	if ((spi == NULL) || (buf == NULL) || (count > 4)) {		return -EINVAL;	}	if (count == 0) {		return 0;	}	if (flags == SPI_READ) {		*spi->regs->control = (SPx_CONTROL_RD | SPx_CONTROL_START);		while ((*spi->regs->			control & (SPx_CONTROL_START | SPx_CONTROL_RX_EMP)) !=		       0) {			if ((*spi->regs->			     raw_status & SPx_RAW_STATUS_RX_ALLERR_RAW) != 0) {				ret = -EIO;				break;			}		}		data = *spi->regs->rx_data;		memcpy(buf, (char *)&data, count);	} else if (flags == SPI_WRITE) {		memcpy((char *)&data, buf, count);		*spi->regs->tx_data = data;		*spi->regs->control = (SPx_CONTROL_WRT | SPx_CONTROL_START);		while ((*spi->regs->control & SPx_CONTROL_START) != 0) {			if ((*spi->regs->			     raw_status & SPx_RAW_STATUS_TX_ALLERR_RAW) != 0) {				ret = -EIO;				break;			}		}	} else if (flags == SPI_RW) {		memcpy((char *)&data, buf, count);		*spi->regs->tx_data = data;		*spi->regs->control =		    (SPx_CONTROL_RD | SPx_CONTROL_WRT | SPx_CONTROL_START);		while ((*spi->regs->			control & (SPx_CONTROL_START | SPx_CONTROL_RX_EMP)) !=		       0) {			if ((*spi->regs->			     raw_status & SPx_RAW_STATUS_ALLERR_RAW) != 0) {				ret = -EIO;				break;			}		}		data = *spi->regs->rx_data;		memcpy(buf, (char *)&data, count);	}	return ret;}static void spi_rx_stop(spi_data_t * spi){	if (spi == NULL) {		return;	}	if (spi->config->dma == SPI_DMA_ON) {		if (spi->config->m_s == SPI_M_S_MASTER) {			if ((*spi->regs->control & SPx_CONTROL_START) != 0) {				*spi->regs->control |= SPx_CONTROL_STOP;				udelay((1000 * 32 / spi->config->sclk) + 1);			}		}		mp200_stop_dma(spi->trans->rx_lch);	}	*spi->regs->enclr = SPx_ENCLR_RX_ALL_MASK;	spi_sft_reset(spi);}static void spi_tx_stop(spi_data_t * spi){	if (spi == NULL) {		return;	}	if (spi->config->dma == SPI_DMA_ON) {		if (spi->config->m_s == SPI_M_S_MASTER) {			if ((*spi->regs->control & SPx_CONTROL_START) != 0) {				*spi->regs->control |= SPx_CONTROL_STOP;				udelay((1000 * 32 / spi->config->sclk) + 1);			}			*spi->regs->control2 &= ~(SPx_CONTROL2_TX_STOP_MODE);		}		mp200_stop_dma(spi->trans->tx_lch);	}	*spi->regs->enclr = SPx_ENCLR_TX_ALL_MASK;	spi_sft_reset(spi);}static int spi_rx_start(spi_data_t * spi, int size){	unsigned int unit;	int intmask;	int ret;	int i;	char *buf;	if (spi == NULL) {		return -EINVAL;	}	if (size <= 0) {		return 0;	}	if (spi->config->dma == SPI_DMA_ON) {		if (spi->config->nbr <= SPI_NB_8BIT) {			spi->trans->dma_rx_regs->mode = MP200_DMAC_DEFMODE_8BIT;		} else if (spi->config->nbr <= SPI_NB_16BIT) {			spi->trans->dma_rx_regs->mode =			    MP200_DMAC_DEFMODE_16BIT;		} else {			spi->trans->dma_rx_regs->mode =			    MP200_DMAC_DEFMODE_32BIT;		}		spi->trans->dma_rx_regs->boff = 0;		spi->trans->dma_rx_regs->bsize = size;		spi->trans->dma_rx_regs->bsize_count = 0;		spi->trans->dma_rx_regs->leng = size;		intmask = (MP200_DMAC_INT_ERROR_EN | MP200_DMAC_INT_LENG_EN);		ret =		    mp200_start_dma(spi->trans->rx_lch, spi->trans->rx_data, 0,				    spi->trans->buf.dma_addr, intmask);		if (ret < 0) {#ifdef SPI_DEBUG			printk(KERN_INFO "%s(): dma error\n", __FUNCTION__);#endif			return ret;		}		*spi->regs->enset = SPx_ENSET_RX_ALLERR_EN;		*spi->regs->control = (SPx_CONTROL_RD | SPx_CONTROL_START);	} else {		unit = spi_unit(spi->config->nbr);		spin_lock_irq(&spi->trans->spinlock);		for (i = 0; i < size; i += unit) {			buf = (char *)(spi->trans->buf.addr + i);			ret = spi_xferbytes(spi, buf, unit, SPI_READ);			if (ret < 0) {				spi_sft_reset(spi);				spin_unlock_irq(&spi->trans->spinlock);				return ret;			}			if (spi->config->m_s == SPI_M_S_MASTER) {				udelay(masterread);			}		}		spi->trans->size = size;		spin_unlock_irq(&spi->trans->spinlock);		if (waitqueue_active(&spi->trans->wait)) {			wake_up_interruptible(&spi->trans->wait);		}	}	return 0;}static int spi_tx_start(spi_data_t * spi, int size){	unsigned int unit;	int intmask;	int ret;	int i;	char *buf;

⌨️ 快捷键说明

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