📄 mp200_spi.c
字号:
if (spi == NULL) { return -EINVAL; } if (size <= 0) { return 0; } spi->trans->size = size; if (spi->config->dma == SPI_DMA_ON) { if (spi->config->nbw <= SPI_NB_8BIT) { spi->trans->dma_tx_regs->mode = MP200_DMAC_DEFMODE_8BIT; } else if (spi->config->nbw <= SPI_NB_16BIT) { spi->trans->dma_tx_regs->mode = MP200_DMAC_DEFMODE_16BIT; } else { spi->trans->dma_tx_regs->mode = MP200_DMAC_DEFMODE_32BIT; } spi->trans->dma_tx_regs->aoff = 0; spi->trans->dma_tx_regs->asize = size; spi->trans->dma_tx_regs->asize_count = 0; spi->trans->dma_tx_regs->leng = size; intmask = (MP200_DMAC_INT_ERROR_EN | MP200_DMAC_INT_LENG_EN); ret = mp200_start_dma(spi->trans->tx_lch, spi->trans->buf.dma_addr, 0, spi->trans->tx_data, intmask); if (ret < 0) {#ifdef SPI_DEBUG printk(KERN_INFO "%s(): dma error\n", __FUNCTION__);#endif spi->trans->size = 0; return ret; } if (spi->config->m_s == SPI_M_S_MASTER) { *spi->regs->control2 |= SPx_CONTROL2_TX_STOP_MODE; } *spi->regs->enset = (SPx_ENSET_TX_ALLERR_EN | SPx_ENSET_TX_STOP_EN); *spi->regs->control = (SPx_CONTROL_WRT | SPx_CONTROL_START); } else { unit = spi_unit(spi->config->nbw); 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_WRITE); if (ret < 0) { spi->trans->size = 0; spi_sft_reset(spi); spin_unlock_irq(&spi->trans->spinlock); return ret; } if (spi->config->m_s == SPI_M_S_MASTER) { udelay(masterwrite); } } spi->trans->size = 0; spin_unlock_irq(&spi->trans->spinlock); if (waitqueue_active(&spi->trans->wait)) { wake_up_interruptible(&spi->trans->wait); } } return 0;}static irqreturn_t spi_interrupt(int irq, void *dev_id, struct pt_regs *regs){ spi_data_t *spi; unsigned int status; if (dev_id == NULL) { return IRQ_NONE; } spi = (spi_data_t *) dev_id; status = *spi->regs->status; *spi->regs->ffclr = status;#ifdef SPI_DEBUG printk(KERN_INFO "%s(): status = %08x\n", __FUNCTION__, status);#endif /* error */ if ((status & SPx_STATUS_ALLERR) != 0) { spi->trans->spi_err = (status & SPx_STATUS_ALLERR); if ((status & (SPx_STATUS_TERR | SPx_STATUS_RX_OVR)) != 0) { spi_rx_stop(spi); } if ((status & SPx_STATUS_TX_UDR) != 0) { if (spi->config->m_s == SPI_M_S_SLAVE) { if (spi->trans->dma_tx_regs->wcount == 0) { spi->trans->spi_err &= ~(SPx_STATUS_TX_UDR); } } spi_tx_stop(spi); } if (waitqueue_active(&spi->trans->wait)) { wake_up_interruptible(&spi->trans->wait); } return IRQ_HANDLED; } /* tx stop (dma master only) */ if ((status & SPx_STATUS_TX_STOP) != 0) { if ((spi->trans->size > 0) || ((*spi->regs->control & SPx_CONTROL_TX_EMP) == 0)) { /* error */ spi->trans->spi_err |= SPx_STATUS_TX_STOP; } spi_tx_stop(spi); if (waitqueue_active(&spi->trans->wait)) { wake_up_interruptible(&spi->trans->wait); } return IRQ_HANDLED; } return IRQ_HANDLED;}static void spi_interrupt_dma_rx(void *data, int intsts, int intrawsts){ spi_data_t *spi; if (data == NULL) { return; }#ifdef SPI_DEBUG printk(KERN_INFO "%s(): intsts = %08x\n", __FUNCTION__, intsts);#endif spi = (spi_data_t *) data; if (intsts & (MP200_DMAC_INT_ERROR_WR | MP200_DMAC_INT_ERROR_RD)) { spi->trans->dma_err = (intsts & (MP200_DMAC_INT_ERROR_WR | MP200_DMAC_INT_ERROR_RD)); } else if (intsts & MP200_DMAC_INT_LENG_WR) { spi->trans->size = mp200_get_dma_pos(spi->trans->rx_lch) - spi->trans->buf.dma_addr; } else { return; }#ifdef SPI_DEBUG printk(KERN_INFO "%s(): size = %d\n", __FUNCTION__, spi->trans->size);#endif spi_rx_stop(spi); if (waitqueue_active(&spi->trans->wait)) { wake_up_interruptible(&spi->trans->wait); }}static void spi_interrupt_dma_tx(void *data, int intsts, int intrawsts){ spi_data_t *spi; if (data == NULL) { return; }#ifdef SPI_DEBUG printk(KERN_INFO "%s(): intsts = %08x\n", __FUNCTION__, intsts);#endif spi = (spi_data_t *) data; if (intsts & (MP200_DMAC_INT_ERROR_WR | MP200_DMAC_INT_ERROR_RD)) { spi->trans->dma_err = (intsts & (MP200_DMAC_INT_ERROR_WR | MP200_DMAC_INT_ERROR_RD)); } else if (intsts & MP200_DMAC_INT_LENG_WR) { spi->trans->size = 0; return; } else { return; } spi_tx_stop(spi); if (waitqueue_active(&spi->trans->wait)) { wake_up_interruptible(&spi->trans->wait); }}int spi_read(SPI_CONFIG * config, char *buf, unsigned int count, unsigned int flags){ DECLARE_WAITQUEUE(wait, current); spi_data_t *spi; unsigned int spi_err; unsigned int dma_err; unsigned int len = 0; unsigned int unit; int size; int ret = 0; if ((config == NULL) || (buf == NULL)) { return -EINVAL; } if ((config->dev != SPI_DEV_SP0) && (config->dev != SPI_DEV_SP1)) { return -ENODEV; } spi = &spi_private[config->dev]; if (spi->probe == 0) { return -EPERM; } if ((flags & SPI_NONBLOCK) != 0) { if (down_trylock(&spi->sem) != 0) { return -EAGAIN; } } else { if (down_interruptible(&spi->sem) != 0) { return -ERESTARTSYS; } } spi->trans->state = SPI_READ; ret = spi_config(spi, config); if (ret < 0) { spi->trans->state = SPI_UNUSED; up(&spi->sem); return ret; } unit = spi_unit(config->nbr); count = (count / unit) * unit;#ifdef SPI_DEBUG printk(KERN_INFO "%s(): count = %d\n", __FUNCTION__, count);#endif spi->trans->size = 0; add_wait_queue(&spi->trans->wait, &wait); while ((count - len) > 0) { set_current_state(TASK_INTERRUPTIBLE); spin_lock_irq(&spi->trans->spinlock); spi_err = spi->trans->spi_err; dma_err = spi->trans->dma_err; spin_unlock_irq(&spi->trans->spinlock); if ((spi_err != 0) || (dma_err != 0)) { spi_init_data(spi); ret = -EIO; break; } size = spi->trans->size; if (size > 0) { spi->trans->size = 0; if ((count - len) < size) { size = count - len; } memcpy((char *)(buf + len), (char *)spi->trans->buf.addr, size); len = len + size; ret = len; if (((count - len) > 0) && (spi->config->m_s == SPI_M_S_MASTER)) { udelay(masterloop); } } else { if ((count - len) > spi->trans->buf.size) { size = spi->trans->buf.size; } else { size = count - len; } ret = spi_rx_start(spi, size); if (ret < 0) { break; } if (signal_pending(current)) { spi_rx_stop(spi); ret = -ERESTARTSYS; break; } schedule(); } } remove_wait_queue(&spi->trans->wait, &wait); set_current_state(TASK_RUNNING); spi->trans->state = SPI_UNUSED; up(&spi->sem); return ret;}int spi_write(SPI_CONFIG * config, char *buf, unsigned int count, unsigned int flags){ DECLARE_WAITQUEUE(wait, current); spi_data_t *spi; unsigned int spi_err; unsigned int dma_err; unsigned int len = 0; unsigned int unit; int size; int ret = 0; if ((config == NULL) || (buf == NULL)) { return -EINVAL; } if ((config->dev != SPI_DEV_SP0) && (config->dev != SPI_DEV_SP1)) { return -ENODEV; } spi = &spi_private[config->dev]; if (spi->probe == 0) { return -EPERM; } if ((flags & SPI_NONBLOCK) != 0) { if (down_trylock(&spi->sem) != 0) { return -EAGAIN; } } else { if (down_interruptible(&spi->sem) != 0) { return -ERESTARTSYS; } } spi->trans->state = SPI_WRITE; ret = spi_config(spi, config); if (ret < 0) { spi->trans->state = SPI_UNUSED; up(&spi->sem); return ret; } unit = spi_unit(config->nbw); count = (count / unit) * unit;#ifdef SPI_DEBUG printk(KERN_INFO "%s(): count = %d\n", __FUNCTION__, count);#endif spi->trans->size = 0; add_wait_queue(&spi->trans->wait, &wait); while ((count - len) > 0) { set_current_state(TASK_INTERRUPTIBLE); spin_lock_irq(&spi->trans->spinlock); spi_err = spi->trans->spi_err; dma_err = spi->trans->dma_err; spin_unlock_irq(&spi->trans->spinlock); if ((spi_err != 0) || (dma_err != 0)) { spi_init_data(spi); ret = -EIO; break; } if ((count - len) > spi->trans->buf.size) { size = spi->trans->buf.size; } else { size = count - len; } memcpy((char *)spi->trans->buf.addr, (char *)(buf + len), size); ret = spi_tx_start(spi, size); if (ret < 0) { break; } len = len + size; ret = len; if (signal_pending(current)) { spi_tx_stop(spi); ret = -ERESTARTSYS; break; } schedule(); if (((count - len) > 0) && (spi->config->m_s == SPI_M_S_MASTER)) { udelay(masterloop); } } remove_wait_queue(&spi->trans->wait, &wait); set_current_state(TASK_RUNNING); spi->trans->state = SPI_UNUSED; up(&spi->sem); return ret;}int spi_cmd_read(SPI_CONFIG * config, char *cmd, char *buf, unsigned int flags){ spi_data_t *spi; unsigned int unit; unsigned int data = 0; int ret = 0; if ((config == NULL) || (cmd == NULL) || (buf == NULL)) { return -EINVAL; } if ((config->dev != SPI_DEV_SP0) && (config->dev != SPI_DEV_SP1)) { return -ENODEV; } spi = &spi_private[config->dev]; if (spi->probe == 0) { return -EPERM; }#ifdef SPI_DEBUG printk(KERN_INFO "%s(): cmd = %08x\n", __FUNCTION__, *(unsigned int *)cmd);#endif if ((flags & SPI_NONBLOCK) != 0) { if (down_trylock(&spi->sem) != 0) { return -EAGAIN; } } else { if (down_interruptible(&spi->sem) != 0) { return -ERESTARTSYS; } } /* dma is unsupported */ if (config->dma == SPI_DMA_ON) { config->dma = SPI_DMA_OFF; } spin_lock_irq(&spi->trans->spinlock); while (1) { if ((flags & SPI_RW_2CYCLE) != 0) { spi->trans->state = SPI_WRITE; ret = spi_config(spi, config); if (ret < 0) { break; } unit = spi_unit(spi->config->nbw); ret = spi_xferbytes(spi, cmd, unit, SPI_WRITE); if (ret < 0) { break; } spi->trans->state = SPI_READ; ret = spi_config(spi, config); if (ret < 0) { break; } unit = spi_unit(spi->config->nbr); ret = spi_xferbytes(spi, buf, unit, SPI_READ); if (ret < 0) { break; } } else { spi->trans->state = SPI_RW; ret = spi_config(spi, config); if (ret < 0) { break; } memcpy((char *)&data, cmd, spi_unit(spi->config->nbw)); data = (data << spi->config->nbr); unit = spi_unit(spi->config->nbr + spi->config->nbw); ret = spi_xferbytes(spi, (char *)&data, unit, SPI_RW); if (ret < 0) { break; } memcpy(buf, (char *)&data, spi_unit(spi->config->nbr)); } break; } if (ret < 0) { spi_sft_reset(spi); } spi->trans->state = SPI_UNUSED; spin_unlock_irq(&spi->trans->spinlock); up(&spi->sem); return ret;}static int spi_probe(struct device *dev){ struct platform_device *pdev; spi_data_t *spi; int ret; if (dev == NULL) { return -EINVAL; } pdev = to_platform_device(dev); if ((pdev->id != SPI_DEV_SP0) && (pdev->id != SPI_DEV_SP1)) { return -ENODEV; } spi = &spi_private[pdev->id]; if (spi->probe == 0) { spi_power_on(spi); ret = spi_allocate_buffer(spi, SPI_BUFSIZE); if (ret < 0) { spi_power_off(spi); return ret; } ret = spi_request_dma(spi); if (ret < 0) { spi_free_buffer(spi); spi_power_off(spi); return ret; } ret = request_irq(spi->trans->int_spi, spi_interrupt, SA_INTERRUPT, SPI_NAME, (void *)spi); if (ret < 0) { spi_free_dma(spi); spi_free_buffer(spi); spi_power_off(spi); return ret; } ret = spi_config(spi, spi->config); if (ret < 0) { free_irq(spi->trans->int_spi, (void *)spi); spi_free_dma(spi); spi_free_buffer(spi); spi_power_off(spi); return ret; } init_waitqueue_head(&spi->trans->wait); sema_init(&spi->sem, 1); spi_init_data(spi); spi->probe = 1; } return 0;}static int spi_remove(struct device *dev){ struct platform_device *pdev; spi_data_t *spi; if (dev == NULL) { return -EINVAL; } pdev = to_platform_device(dev); if ((pdev->id != SPI_DEV_SP0) && (pdev->id != SPI_DEV_SP1)) { return -ENODEV; } spi = &spi_private[pdev->id]; if (spi->probe != 0) { spi->probe = 0; free_irq(spi->trans->int_spi, (void *)spi); spi_free_dma(spi); spi_free_buffer(spi); spi_power_off(spi); } return 0;}static int spi_suspend(struct device *dev, u32 state, u32 level){ struct platform_device *pdev; spi_data_t *spi; int ret = 0; if (dev == NULL) { return -EINVAL; } switch (level) { case SUSPEND_DISABLE: pdev = to_platform_device(dev); if ((pdev->id != SPI_DEV_SP0) && (pdev->id != SPI_DEV_SP1)) { return -ENODEV; } spi = &spi_private[pdev->id]; if (spi->trans->state != SPI_UNUSED) { return -EBUSY; } break; case SUSPEND_SAVE_STATE: break; case SUSPEND_POWER_DOWN: ret = spi_remove(dev); break; default: break; } return ret;}static int spi_resume(struct device *dev, u32 level){ int ret = 0; if (dev == NULL) { return -EINVAL; } switch (level) { case RESUME_POWER_ON: ret = spi_probe(dev); break; case RESUME_RESTORE_STATE: break; case RESUME_ENABLE: break; default: break; } return ret;}static struct device_driver spi_drv = { .name = SPI_NAME, .bus = &platform_bus_type, .probe = spi_probe, .remove = spi_remove, .suspend = spi_suspend, .resume = spi_resume,};static int __init spi_init(void){ printk(KERN_INFO "Starting %s.\n", SPI_NAME); spi_clock_source = spi_get_clock_source(); return driver_register(&spi_drv);}static void __exit spi_exit(void){ driver_unregister(&spi_drv);}module_init(spi_init);module_exit(spi_exit);MODULE_LICENSE("GPL");EXPORT_SYMBOL(spi_read);EXPORT_SYMBOL(spi_write);EXPORT_SYMBOL(spi_cmd_read);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -