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

📄 mp200_spi.c

📁 The attached file is the driver of SPI
💻 C
📖 第 1 页 / 共 2 页
字号:
	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 + -