📄 mp200_spi.c
字号:
/* * 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 = ®s_sp0, .trans = &trans_sp0, .config = &config_sp0, .smu = &smu_sp0, .probe = 0, }, { .regs = ®s_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 + -