spi_imx.c

来自「linux 内核源代码」· C语言 代码 · 共 1,763 行 · 第 1/4 页

C
1,763
字号
	/* SPI chip-select management */	if (chip_info->cs_control)		chip->cs_control = chip_info->cs_control;	else		chip->cs_control = null_cs_control;	/* Save controller_state */	spi_set_ctldata(spi, chip);	/* Summary */	dev_dbg(&spi->dev,		"setup succeded\n"		"    loopback enable   = %s\n"		"    dma enable        = %s\n"		"    insert /ss pulse  = %s\n"		"    period wait       = %d\n"		"    mode              = %d\n"		"    bits per word     = %d\n"		"    min speed         = %d Hz\n"		"    rounded max speed = %d Hz\n",		chip->test & SPI_TEST_LBC ? "Yes" : "No",		chip->enable_dma ? "Yes" : "No",		chip->control & SPI_CONTROL_SSCTL ? "Yes" : "No",		chip->period & SPI_PERIOD_WAIT,		spi->mode,		spi->bits_per_word,		spi_speed_hz(SPI_CONTROL_DATARATE_MIN),		spi->max_speed_hz);	return status;err_first_setup:	kfree(chip);	return status;}static void cleanup(struct spi_device *spi){	kfree(spi_get_ctldata(spi));}static int __init init_queue(struct driver_data *drv_data){	INIT_LIST_HEAD(&drv_data->queue);	spin_lock_init(&drv_data->lock);	drv_data->run = QUEUE_STOPPED;	drv_data->busy = 0;	tasklet_init(&drv_data->pump_transfers,			pump_transfers,	(unsigned long)drv_data);	INIT_WORK(&drv_data->work, pump_messages);	drv_data->workqueue = create_singlethread_workqueue(					drv_data->master->dev.parent->bus_id);	if (drv_data->workqueue == NULL)		return -EBUSY;	return 0;}static int start_queue(struct driver_data *drv_data){	unsigned long flags;	spin_lock_irqsave(&drv_data->lock, flags);	if (drv_data->run == QUEUE_RUNNING || drv_data->busy) {		spin_unlock_irqrestore(&drv_data->lock, flags);		return -EBUSY;	}	drv_data->run = QUEUE_RUNNING;	drv_data->cur_msg = NULL;	drv_data->cur_transfer = NULL;	drv_data->cur_chip = NULL;	spin_unlock_irqrestore(&drv_data->lock, flags);	queue_work(drv_data->workqueue, &drv_data->work);	return 0;}static int stop_queue(struct driver_data *drv_data){	unsigned long flags;	unsigned limit = 500;	int status = 0;	spin_lock_irqsave(&drv_data->lock, flags);	/* This is a bit lame, but is optimized for the common execution path.	 * A wait_queue on the drv_data->busy could be used, but then the common	 * execution path (pump_messages) would be required to call wake_up or	 * friends on every SPI message. Do this instead */	drv_data->run = QUEUE_STOPPED;	while (!list_empty(&drv_data->queue) && drv_data->busy && limit--) {		spin_unlock_irqrestore(&drv_data->lock, flags);		msleep(10);		spin_lock_irqsave(&drv_data->lock, flags);	}	if (!list_empty(&drv_data->queue) || drv_data->busy)		status = -EBUSY;	spin_unlock_irqrestore(&drv_data->lock, flags);	return status;}static int destroy_queue(struct driver_data *drv_data){	int status;	status = stop_queue(drv_data);	if (status != 0)		return status;	if (drv_data->workqueue)		destroy_workqueue(drv_data->workqueue);	return 0;}static int __init spi_imx_probe(struct platform_device *pdev){	struct device *dev = &pdev->dev;	struct spi_imx_master *platform_info;	struct spi_master *master;	struct driver_data *drv_data = NULL;	struct resource *res;	int irq, status = 0;	platform_info = dev->platform_data;	if (platform_info == NULL) {		dev_err(&pdev->dev, "probe - no platform data supplied\n");		status = -ENODEV;		goto err_no_pdata;	}	/* Allocate master with space for drv_data */	master = spi_alloc_master(dev, sizeof(struct driver_data));	if (!master) {		dev_err(&pdev->dev, "probe - cannot alloc spi_master\n");		status = -ENOMEM;		goto err_no_mem;	}	drv_data = spi_master_get_devdata(master);	drv_data->master = master;	drv_data->master_info = platform_info;	drv_data->pdev = pdev;	master->bus_num = pdev->id;	master->num_chipselect = platform_info->num_chipselect;	master->cleanup = cleanup;	master->setup = setup;	master->transfer = transfer;	drv_data->dummy_dma_buf = SPI_DUMMY_u32;	/* Find and map resources */	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);	if (!res) {		dev_err(&pdev->dev, "probe - MEM resources not defined\n");		status = -ENODEV;		goto err_no_iores;	}	drv_data->ioarea = request_mem_region(res->start,						res->end - res->start + 1,						pdev->name);	if (drv_data->ioarea == NULL) {		dev_err(&pdev->dev, "probe - cannot reserve region\n");		status = -ENXIO;		goto err_no_iores;	}	drv_data->regs = ioremap(res->start, res->end - res->start + 1);	if (drv_data->regs == NULL) {		dev_err(&pdev->dev, "probe - cannot map IO\n");		status = -ENXIO;		goto err_no_iomap;	}	drv_data->rd_data_phys = (dma_addr_t)res->start;	/* Attach to IRQ */	irq = platform_get_irq(pdev, 0);	if (irq < 0) {		dev_err(&pdev->dev, "probe - IRQ resource not defined\n");		status = -ENODEV;		goto err_no_irqres;	}	status = request_irq(irq, spi_int, IRQF_DISABLED, dev->bus_id, drv_data);	if (status < 0) {		dev_err(&pdev->dev, "probe - cannot get IRQ (%d)\n", status);		goto err_no_irqres;	}	/* Setup DMA if requested */	drv_data->tx_channel = -1;	drv_data->rx_channel = -1;	if (platform_info->enable_dma) {		/* Get rx DMA channel */		status = imx_dma_request_by_prio(&drv_data->rx_channel,			"spi_imx_rx", DMA_PRIO_HIGH);		if (status < 0) {			dev_err(dev,				"probe - problem (%d) requesting rx channel\n",				status);			goto err_no_rxdma;		} else			imx_dma_setup_handlers(drv_data->rx_channel, NULL,						dma_err_handler, drv_data);		/* Get tx DMA channel */		status = imx_dma_request_by_prio(&drv_data->tx_channel,						"spi_imx_tx", DMA_PRIO_MEDIUM);		if (status < 0) {			dev_err(dev,				"probe - problem (%d) requesting tx channel\n",				status);			imx_dma_free(drv_data->rx_channel);			goto err_no_txdma;		} else			imx_dma_setup_handlers(drv_data->tx_channel,						dma_tx_handler, dma_err_handler,						drv_data);		/* Set request source and burst length for allocated channels */		switch (drv_data->pdev->id) {		case 1:			/* Using SPI1 */			RSSR(drv_data->rx_channel) = DMA_REQ_SPI1_R;			RSSR(drv_data->tx_channel) = DMA_REQ_SPI1_T;			break;		case 2:			/* Using SPI2 */			RSSR(drv_data->rx_channel) = DMA_REQ_SPI2_R;			RSSR(drv_data->tx_channel) = DMA_REQ_SPI2_T;			break;		default:			dev_err(dev, "probe - bad SPI Id\n");			imx_dma_free(drv_data->rx_channel);			imx_dma_free(drv_data->tx_channel);			status = -ENODEV;			goto err_no_devid;		}		BLR(drv_data->rx_channel) = SPI_DMA_BLR;		BLR(drv_data->tx_channel) = SPI_DMA_BLR;	}	/* Load default SPI configuration */	writel(SPI_RESET_START, drv_data->regs + SPI_RESET);	writel(0, drv_data->regs + SPI_RESET);	writel(SPI_DEFAULT_CONTROL, drv_data->regs + SPI_CONTROL);	/* Initial and start queue */	status = init_queue(drv_data);	if (status != 0) {		dev_err(&pdev->dev, "probe - problem initializing queue\n");		goto err_init_queue;	}	status = start_queue(drv_data);	if (status != 0) {		dev_err(&pdev->dev, "probe - problem starting queue\n");		goto err_start_queue;	}	/* Register with the SPI framework */	platform_set_drvdata(pdev, drv_data);	status = spi_register_master(master);	if (status != 0) {		dev_err(&pdev->dev, "probe - problem registering spi master\n");		goto err_spi_register;	}	dev_dbg(dev, "probe succeded\n");	return 0;err_init_queue:err_start_queue:err_spi_register:	destroy_queue(drv_data);err_no_rxdma:err_no_txdma:err_no_devid:	free_irq(irq, drv_data);err_no_irqres:	iounmap(drv_data->regs);err_no_iomap:	release_resource(drv_data->ioarea);	kfree(drv_data->ioarea);err_no_iores:	spi_master_put(master);err_no_pdata:err_no_mem:	return status;}static int __exit spi_imx_remove(struct platform_device *pdev){	struct driver_data *drv_data = platform_get_drvdata(pdev);	int irq;	int status = 0;	if (!drv_data)		return 0;	tasklet_kill(&drv_data->pump_transfers);	/* Remove the queue */	status = destroy_queue(drv_data);	if (status != 0) {		dev_err(&pdev->dev, "queue remove failed (%d)\n", status);		return status;	}	/* Reset SPI */	writel(SPI_RESET_START, drv_data->regs + SPI_RESET);	writel(0, drv_data->regs + SPI_RESET);	/* Release DMA */	if (drv_data->master_info->enable_dma) {		RSSR(drv_data->rx_channel) = 0;		RSSR(drv_data->tx_channel) = 0;		imx_dma_free(drv_data->tx_channel);		imx_dma_free(drv_data->rx_channel);	}	/* Release IRQ */	irq = platform_get_irq(pdev, 0);	if (irq >= 0)		free_irq(irq, drv_data);	/* Release map resources */	iounmap(drv_data->regs);	release_resource(drv_data->ioarea);	kfree(drv_data->ioarea);	/* Disconnect from the SPI framework */	spi_unregister_master(drv_data->master);	spi_master_put(drv_data->master);	/* Prevent double remove */	platform_set_drvdata(pdev, NULL);	dev_dbg(&pdev->dev, "remove succeded\n");	return 0;}static void spi_imx_shutdown(struct platform_device *pdev){	struct driver_data *drv_data = platform_get_drvdata(pdev);	/* Reset SPI */	writel(SPI_RESET_START, drv_data->regs + SPI_RESET);	writel(0, drv_data->regs + SPI_RESET);	dev_dbg(&pdev->dev, "shutdown succeded\n");}#ifdef CONFIG_PMstatic int suspend_devices(struct device *dev, void *pm_message){	pm_message_t *state = pm_message;	if (dev->power.power_state.event != state->event) {		dev_warn(dev, "pm state does not match request\n");		return -1;	}	return 0;}static int spi_imx_suspend(struct platform_device *pdev, pm_message_t state){	struct driver_data *drv_data = platform_get_drvdata(pdev);	int status = 0;	status = stop_queue(drv_data);	if (status != 0) {		dev_warn(&pdev->dev, "suspend cannot stop queue\n");		return status;	}	dev_dbg(&pdev->dev, "suspended\n");	return 0;}static int spi_imx_resume(struct platform_device *pdev){	struct driver_data *drv_data = platform_get_drvdata(pdev);	int status = 0;	/* Start the queue running */	status = start_queue(drv_data);	if (status != 0)		dev_err(&pdev->dev, "problem starting queue (%d)\n", status);	else		dev_dbg(&pdev->dev, "resumed\n");	return status;}#else#define spi_imx_suspend NULL#define spi_imx_resume NULL#endif /* CONFIG_PM */static struct platform_driver driver = {	.driver = {		.name = "spi_imx",		.bus = &platform_bus_type,		.owner = THIS_MODULE,	},	.remove = __exit_p(spi_imx_remove),	.shutdown = spi_imx_shutdown,	.suspend = spi_imx_suspend,	.resume = spi_imx_resume,};static int __init spi_imx_init(void){	return platform_driver_probe(&driver, spi_imx_probe);}module_init(spi_imx_init);static void __exit spi_imx_exit(void){	platform_driver_unregister(&driver);}module_exit(spi_imx_exit);MODULE_AUTHOR("Andrea Paterniani, <a.paterniani@swapp-eng.it>");MODULE_DESCRIPTION("iMX SPI Controller Driver");MODULE_LICENSE("GPL");

⌨️ 快捷键说明

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