spi_bfin5xx.c

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

C
1,489
字号
		drv_data->cur_chip->baud, drv_data->cur_chip->flag,		drv_data->cur_chip->ctl_reg);	dev_dbg(&drv_data->pdev->dev,		"the first transfer len is %d\n",		drv_data->cur_transfer->len);	/* Mark as busy and launch transfers */	tasklet_schedule(&drv_data->pump_transfers);	drv_data->busy = 1;	spin_unlock_irqrestore(&drv_data->lock, flags);}/* * got a msg to transfer, queue it in drv_data->queue. * And kick off message pumper */static int transfer(struct spi_device *spi, struct spi_message *msg){	struct driver_data *drv_data = spi_master_get_devdata(spi->master);	unsigned long flags;	spin_lock_irqsave(&drv_data->lock, flags);	if (drv_data->run == QUEUE_STOPPED) {		spin_unlock_irqrestore(&drv_data->lock, flags);		return -ESHUTDOWN;	}	msg->actual_length = 0;	msg->status = -EINPROGRESS;	msg->state = START_STATE;	dev_dbg(&spi->dev, "adding an msg in transfer() \n");	list_add_tail(&msg->queue, &drv_data->queue);	if (drv_data->run == QUEUE_RUNNING && !drv_data->busy)		queue_work(drv_data->workqueue, &drv_data->pump_messages);	spin_unlock_irqrestore(&drv_data->lock, flags);	return 0;}#define MAX_SPI_SSEL	7static u16 ssel[3][MAX_SPI_SSEL] = {	{P_SPI0_SSEL1, P_SPI0_SSEL2, P_SPI0_SSEL3,	P_SPI0_SSEL4, P_SPI0_SSEL5,	P_SPI0_SSEL6, P_SPI0_SSEL7},	{P_SPI1_SSEL1, P_SPI1_SSEL2, P_SPI1_SSEL3,	P_SPI1_SSEL4, P_SPI1_SSEL5,	P_SPI1_SSEL6, P_SPI1_SSEL7},	{P_SPI2_SSEL1, P_SPI2_SSEL2, P_SPI2_SSEL3,	P_SPI2_SSEL4, P_SPI2_SSEL5,	P_SPI2_SSEL6, P_SPI2_SSEL7},};/* first setup for new devices */static int setup(struct spi_device *spi){	struct bfin5xx_spi_chip *chip_info = NULL;	struct chip_data *chip;	struct driver_data *drv_data = spi_master_get_devdata(spi->master);	u8 spi_flg;	/* Abort device setup if requested features are not supported */	if (spi->mode & ~(SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST)) {		dev_err(&spi->dev, "requested mode not fully supported\n");		return -EINVAL;	}	/* Zero (the default) here means 8 bits */	if (!spi->bits_per_word)		spi->bits_per_word = 8;	if (spi->bits_per_word != 8 && spi->bits_per_word != 16)		return -EINVAL;	/* Only alloc (or use chip_info) on first setup */	chip = spi_get_ctldata(spi);	if (chip == NULL) {		chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL);		if (!chip)			return -ENOMEM;		chip->enable_dma = 0;		chip_info = spi->controller_data;	}	/* chip_info isn't always needed */	if (chip_info) {		/* Make sure people stop trying to set fields via ctl_reg		 * when they should actually be using common SPI framework.		 * Currently we let through: WOM EMISO PSSE GM SZ TIMOD.		 * Not sure if a user actually needs/uses any of these,		 * but let's assume (for now) they do.		 */		if (chip_info->ctl_reg & (SPE|MSTR|CPOL|CPHA|LSBF|SIZE)) {			dev_err(&spi->dev, "do not set bits in ctl_reg "				"that the SPI framework manages\n");			return -EINVAL;		}		chip->enable_dma = chip_info->enable_dma != 0		    && drv_data->master_info->enable_dma;		chip->ctl_reg = chip_info->ctl_reg;		chip->bits_per_word = chip_info->bits_per_word;		chip->cs_change_per_word = chip_info->cs_change_per_word;		chip->cs_chg_udelay = chip_info->cs_chg_udelay;	}	/* translate common spi framework into our register */	if (spi->mode & SPI_CPOL)		chip->ctl_reg |= CPOL;	if (spi->mode & SPI_CPHA)		chip->ctl_reg |= CPHA;	if (spi->mode & SPI_LSB_FIRST)		chip->ctl_reg |= LSBF;	/* we dont support running in slave mode (yet?) */	chip->ctl_reg |= MSTR;	/*	 * if any one SPI chip is registered and wants DMA, request the	 * DMA channel for it	 */	if (chip->enable_dma && !drv_data->dma_requested) {		/* register dma irq handler */		if (request_dma(drv_data->dma_channel, "BF53x_SPI_DMA") < 0) {			dev_dbg(&spi->dev,				"Unable to request BlackFin SPI DMA channel\n");			return -ENODEV;		}		if (set_dma_callback(drv_data->dma_channel,			(void *)dma_irq_handler, drv_data) < 0) {			dev_dbg(&spi->dev, "Unable to set dma callback\n");			return -EPERM;		}		dma_disable_irq(drv_data->dma_channel);		drv_data->dma_requested = 1;	}	/*	 * Notice: for blackfin, the speed_hz is the value of register	 * SPI_BAUD, not the real baudrate	 */	chip->baud = hz_to_spi_baud(spi->max_speed_hz);	spi_flg = ~(1 << (spi->chip_select));	chip->flag = ((u16) spi_flg << 8) | (1 << (spi->chip_select));	chip->chip_select_num = spi->chip_select;	switch (chip->bits_per_word) {	case 8:		chip->n_bytes = 1;		chip->width = CFG_SPI_WORDSIZE8;		chip->read = chip->cs_change_per_word ?			u8_cs_chg_reader : u8_reader;		chip->write = chip->cs_change_per_word ?			u8_cs_chg_writer : u8_writer;		chip->duplex = chip->cs_change_per_word ?			u8_cs_chg_duplex : u8_duplex;		break;	case 16:		chip->n_bytes = 2;		chip->width = CFG_SPI_WORDSIZE16;		chip->read = chip->cs_change_per_word ?			u16_cs_chg_reader : u16_reader;		chip->write = chip->cs_change_per_word ?			u16_cs_chg_writer : u16_writer;		chip->duplex = chip->cs_change_per_word ?			u16_cs_chg_duplex : u16_duplex;		break;	default:		dev_err(&spi->dev, "%d bits_per_word is not supported\n",				chip->bits_per_word);		kfree(chip);		return -ENODEV;	}	dev_dbg(&spi->dev, "setup spi chip %s, width is %d, dma is %d\n",			spi->modalias, chip->width, chip->enable_dma);	dev_dbg(&spi->dev, "ctl_reg is 0x%x, flag_reg is 0x%x\n",			chip->ctl_reg, chip->flag);	spi_set_ctldata(spi, chip);	dev_dbg(&spi->dev, "chip select number is %d\n", chip->chip_select_num);	if ((chip->chip_select_num > 0)		&& (chip->chip_select_num <= spi->master->num_chipselect))		peripheral_request(ssel[spi->master->bus_num]			[chip->chip_select_num-1], DRV_NAME);	cs_deactive(drv_data, chip);	return 0;}/* * callback for spi framework. * clean driver specific data */static void cleanup(struct spi_device *spi){	struct chip_data *chip = spi_get_ctldata(spi);	if ((chip->chip_select_num > 0)		&& (chip->chip_select_num <= spi->master->num_chipselect))		peripheral_free(ssel[spi->master->bus_num]					[chip->chip_select_num-1]);	kfree(chip);}static inline int 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;	/* init transfer tasklet */	tasklet_init(&drv_data->pump_transfers,		     pump_transfers, (unsigned long)drv_data);	/* init messages workqueue */	INIT_WORK(&drv_data->pump_messages, 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 inline 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->pump_messages);	return 0;}static inline 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 inline int destroy_queue(struct driver_data *drv_data){	int status;	status = stop_queue(drv_data);	if (status != 0)		return status;	destroy_workqueue(drv_data->workqueue);	return 0;}static int __init bfin5xx_spi_probe(struct platform_device *pdev){	struct device *dev = &pdev->dev;	struct bfin5xx_spi_master *platform_info;	struct spi_master *master;	struct driver_data *drv_data = 0;	struct resource *res;	int status = 0;	platform_info = dev->platform_data;	/* Allocate master with space for drv_data */	master = spi_alloc_master(dev, sizeof(struct driver_data) + 16);	if (!master) {		dev_err(&pdev->dev, "can not alloc spi_master\n");		return -ENOMEM;	}	drv_data = spi_master_get_devdata(master);	drv_data->master = master;	drv_data->master_info = platform_info;	drv_data->pdev = pdev;	drv_data->pin_req = platform_info->pin_req;	master->bus_num = pdev->id;	master->num_chipselect = platform_info->num_chipselect;	master->cleanup = cleanup;	master->setup = setup;	master->transfer = transfer;	/* Find and map our resources */	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);	if (res == NULL) {		dev_err(dev, "Cannot get IORESOURCE_MEM\n");		status = -ENOENT;		goto out_error_get_res;	}	drv_data->regs_base = ioremap(res->start, (res->end - res->start + 1));	if (drv_data->regs_base == NULL) {		dev_err(dev, "Cannot map IO\n");		status = -ENXIO;		goto out_error_ioremap;	}	drv_data->dma_channel = platform_get_irq(pdev, 0);	if (drv_data->dma_channel < 0) {		dev_err(dev, "No DMA channel specified\n");		status = -ENOENT;		goto out_error_no_dma_ch;	}	/* Initial and start queue */	status = init_queue(drv_data);	if (status != 0) {		dev_err(dev, "problem initializing queue\n");		goto out_error_queue_alloc;	}	status = start_queue(drv_data);	if (status != 0) {		dev_err(dev, "problem starting queue\n");		goto out_error_queue_alloc;	}	/* Register with the SPI framework */	platform_set_drvdata(pdev, drv_data);	status = spi_register_master(master);	if (status != 0) {		dev_err(dev, "problem registering spi master\n");		goto out_error_queue_alloc;	}	status = peripheral_request_list(drv_data->pin_req, DRV_NAME);	if (status != 0) {		dev_err(&pdev->dev, ": Requesting Peripherals failed\n");		goto out_error;	}	dev_info(dev, "%s, Version %s, regs_base@%p, dma channel@%d\n",		DRV_DESC, DRV_VERSION, drv_data->regs_base,		drv_data->dma_channel);	return status;out_error_queue_alloc:	destroy_queue(drv_data);out_error_no_dma_ch:	iounmap((void *) drv_data->regs_base);out_error_ioremap:out_error_get_res:out_error:	spi_master_put(master);	return status;}/* stop hardware and remove the driver */static int __devexit bfin5xx_spi_remove(struct platform_device *pdev){	struct driver_data *drv_data = platform_get_drvdata(pdev);	int status = 0;	if (!drv_data)		return 0;	/* Remove the queue */	status = destroy_queue(drv_data);	if (status != 0)		return status;	/* Disable the SSP at the peripheral and SOC level */	bfin_spi_disable(drv_data);	/* Release DMA */	if (drv_data->master_info->enable_dma) {		if (dma_channel_active(drv_data->dma_channel))			free_dma(drv_data->dma_channel);	}	/* Disconnect from the SPI framework */	spi_unregister_master(drv_data->master);	peripheral_free_list(drv_data->pin_req);	/* Prevent double remove */	platform_set_drvdata(pdev, NULL);	return 0;}#ifdef CONFIG_PMstatic int bfin5xx_spi_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)		return status;	/* stop hardware */	bfin_spi_disable(drv_data);	return 0;}static int bfin5xx_spi_resume(struct platform_device *pdev){	struct driver_data *drv_data = platform_get_drvdata(pdev);	int status = 0;	/* Enable the SPI interface */	bfin_spi_enable(drv_data);	/* Start the queue running */	status = start_queue(drv_data);	if (status != 0) {		dev_err(&pdev->dev, "problem starting queue (%d)\n", status);		return status;	}	return 0;}#else#define bfin5xx_spi_suspend NULL#define bfin5xx_spi_resume NULL#endif				/* CONFIG_PM */MODULE_ALIAS("bfin-spi-master");	/* for platform bus hotplug */static struct platform_driver bfin5xx_spi_driver = {	.driver	= {		.name	= DRV_NAME,		.owner	= THIS_MODULE,	},	.suspend	= bfin5xx_spi_suspend,	.resume		= bfin5xx_spi_resume,	.remove		= __devexit_p(bfin5xx_spi_remove),};static int __init bfin5xx_spi_init(void){	return platform_driver_probe(&bfin5xx_spi_driver, bfin5xx_spi_probe);}module_init(bfin5xx_spi_init);static void __exit bfin5xx_spi_exit(void){	platform_driver_unregister(&bfin5xx_spi_driver);}module_exit(bfin5xx_spi_exit);

⌨️ 快捷键说明

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