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 + -
显示快捷键?