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

📄 pxa2xx_spi.c

📁 pxa3xx ssp driver for linux
💻 C
📖 第 1 页 / 共 4 页
字号:
                 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 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;
 
         destroy_workqueue(drv_data->workqueue);
 
         return 0;
 }
 
 static int pxa2xx_spi_probe(struct platform_device *pdev)
 {
         struct device *dev = &pdev->dev;
         struct pxa2xx_spi_master *platform_info;
         struct spi_master *master;
         struct driver_data *drv_data = 0;
         struct resource *memory_resource;
         int irq;
         int status = 0;
 
         platform_info = dev->platform_data;
 
         if (platform_info->ssp_type == SSP_UNDEFINED) {
                 dev_err(&pdev->dev, "undefined SSP\n");
                 return -ENODEV;
         }
 
         /* Allocate master with space for drv_data and null dma buffer */
         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;
 
         master->bus_num = pdev->id;
         master->num_chipselect = platform_info->num_chipselect;
         master->cleanup = cleanup;
         master->setup = setup;
         master->transfer = transfer;
 
         drv_data->ssp_type = platform_info->ssp_type;
         drv_data->null_dma_buf = (u32 *)ALIGN((u32)(drv_data +
                                                 sizeof(struct driver_data)), 8);
 
         /* Setup register addresses */
         memory_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
         if (!memory_resource) {
                 dev_err(&pdev->dev, "memory resources not defined\n");
                 status = -ENODEV;
                 goto out_error_master_alloc;
         }
 
         drv_data->ioaddr = (void *)io_p2v((unsigned long)(memory_resource->start));
         drv_data->ssdr_physical = memory_resource->start + 0x00000010;
         if (platform_info->ssp_type == PXA25x_SSP) {
                 drv_data->int_cr1 = SSCR1_TIE | SSCR1_RIE;
                 drv_data->dma_cr1 = 0;
                 drv_data->clear_sr = SSSR_ROR;
                 drv_data->mask_sr = SSSR_RFS | SSSR_TFS | SSSR_ROR;
         } else {
                 drv_data->int_cr1 = SSCR1_TIE | SSCR1_RIE | SSCR1_TINTE;
                 drv_data->dma_cr1 = SSCR1_TSRE | SSCR1_RSRE | SSCR1_TINTE;
                 drv_data->clear_sr = SSSR_ROR | SSSR_TINT;
                 drv_data->mask_sr = SSSR_TINT | SSSR_RFS | SSSR_TFS | SSSR_ROR;
         }
 
         /* Attach to IRQ */
         irq = platform_get_irq(pdev, 0);
         if (irq < 0) {
                 dev_err(&pdev->dev, "irq resource not defined\n");
                 status = -ENODEV;
                 goto out_error_master_alloc;
         }
 
         status = request_irq(irq, ssp_int, 0, dev->bus_id, drv_data);
         if (status < 0) {
                 dev_err(&pdev->dev, "can not get IRQ\n");
      	goto out_error_master_alloc;
         }
 
         /* Setup DMA if requested */
         drv_data->tx_channel = -1;
         drv_data->rx_channel = -1;
         if (platform_info->enable_dma) {
 
                 /* Get two DMA channels (rx and tx) */
                 drv_data->rx_channel = pxa_request_dma("pxa2xx_spi_ssp_rx",
                                                         DMA_PRIO_HIGH,
                                                         dma_handler,
                                                         drv_data);
                 if (drv_data->rx_channel < 0) {
                         dev_err(dev, "problem (%d) requesting rx channel\n",
                                 drv_data->rx_channel);
                         status = -ENODEV;
                         goto out_error_irq_alloc;
                 }
                 drv_data->tx_channel = pxa_request_dma("pxa2xx_spi_ssp_tx",
                                                         DMA_PRIO_MEDIUM,
                                                         dma_handler,
                                                         drv_data);
                 if (drv_data->tx_channel < 0) {
                         dev_err(dev, "problem (%d) requesting tx channel\n",
                                 drv_data->tx_channel);
                         status = -ENODEV;
                         goto out_error_dma_alloc;
                 }
 
                 if (drv_data->ioaddr == SSP1_VIRT) {
                                 DRCMRRXSSDR = DRCMR_MAPVLD
                                                 | drv_data->rx_channel;
                                 DRCMRTXSSDR = DRCMR_MAPVLD
                                                 | drv_data->tx_channel;
                 } else if (drv_data->ioaddr == SSP2_VIRT) {
                                 DRCMRRXSS2DR = DRCMR_MAPVLD
                                                 | drv_data->rx_channel;
                                 DRCMRTXSS2DR = DRCMR_MAPVLD
                                                 | drv_data->tx_channel;
                 } else if (drv_data->ioaddr == SSP3_VIRT) {
                                 DRCMRRXSS3DR = DRCMR_MAPVLD
                                                 | drv_data->rx_channel;
                                 DRCMRTXSS3DR = DRCMR_MAPVLD
                                                 | drv_data->tx_channel;
                 } else {
                         dev_err(dev, "bad SSP type\n");
                         goto out_error_dma_alloc;
                 }
         }
 
         /* Enable SOC clock */
         pxa_set_cken(platform_info->clock_enable, 1);
 
         /* Load default SSP configuration */
         write_SSCR0(0, drv_data->ioaddr);
         write_SSCR1(SSCR1_RxTresh(4) | SSCR1_TxTresh(12), drv_data->ioaddr);
        write_SSCR0(SSCR0_SerClkDiv(2)
                         | SSCR0_Motorola
                         | SSCR0_DataSize(8),
                         drv_data->ioaddr);
         if (drv_data->ssp_type != PXA25x_SSP)
                 write_SSTO(0, drv_data->ioaddr);
         write_SSPSP(0, drv_data->ioaddr);
 
         /* Initial and start queue */
         status = init_queue(drv_data);
         if (status != 0) {
                 dev_err(&pdev->dev, "problem initializing queue\n");
                 goto out_error_clock_enabled;
         }
         status = start_queue(drv_data);
         if (status != 0) {
                 dev_err(&pdev->dev, "problem starting queue\n");
                 goto out_error_clock_enabled;
         }
 
         /* Register with the SPI framework */
         platform_set_drvdata(pdev, drv_data);
         status = spi_register_master(master);
         if (status != 0) {
                 dev_err(&pdev->dev, "problem registering spi master\n");
                 goto out_error_queue_alloc;
         }
 
         return status;
 
 out_error_queue_alloc:
         destroy_queue(drv_data);
 
 out_error_clock_enabled:
         pxa_set_cken(platform_info->clock_enable, 0);
 
 out_error_dma_alloc:
         if (drv_data->tx_channel != -1)
                 pxa_free_dma(drv_data->tx_channel);
         if (drv_data->rx_channel != -1)
                 pxa_free_dma(drv_data->rx_channel);
 
 out_error_irq_alloc:
         free_irq(irq, drv_data);
 
 out_error_master_alloc:
         spi_master_put(master);
         return status;
 }
 
 static int pxa2xx_spi_remove(struct platform_device *pdev)
 {
         struct driver_data *drv_data = platform_get_drvdata(pdev);
         int irq;
         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 */
         write_SSCR0(0, drv_data->ioaddr);
         pxa_set_cken(drv_data->master_info->clock_enable, 0);
 
         /* Release DMA */
         if (drv_data->master_info->enable_dma) {
                 if (drv_data->ioaddr == SSP1_VIRT) {
                         DRCMRRXSSDR = 0;
                         DRCMRTXSSDR = 0;
                 } else if (drv_data->ioaddr == SSP2_VIRT) {
                         DRCMRRXSS2DR = 0;
                         DRCMRTXSS2DR = 0;
                 } else if (drv_data->ioaddr == SSP3_VIRT) {
                         DRCMRRXSS3DR = 0;
                         DRCMRTXSS3DR = 0;
                 }
                 pxa_free_dma(drv_data->tx_channel);
                 pxa_free_dma(drv_data->rx_channel);
         }
 
         /* Release IRQ */
         irq = platform_get_irq(pdev, 0);
         if (irq >= 0)
                 free_irq(irq, drv_data);
 
         /* Disconnect from the SPI framework */
         spi_unregister_master(drv_data->master);
 
         /* Prevent double remove */
         platform_set_drvdata(pdev, NULL);
 
         return 0;
 }
 
 static void pxa2xx_spi_shutdown(struct platform_device *pdev)
 {
         int status = 0;
 
         if ((status = pxa2xx_spi_remove(pdev)) != 0)
                 dev_err(&pdev->dev, "shutdown failed with %d\n", status);
 }
 
 #ifdef CONFIG_PM
 static 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;
 }
 
  int pxa2xx_spi_suspend(struct platform_device *pdev, pm_message_t state)
 {
         struct driver_data *drv_data = platform_get_drvdata(pdev);
         int status = 0;
 
         /* Check all childern for current power state */
         if (device_for_each_child(&pdev->dev, &state, suspend_devices) != 0) {
                 dev_warn(&pdev->dev, "suspend aborted\n");
                 return -1;
         }
 
         status = stop_queue(drv_data);
         if (status != 0)
                 return status;
         write_SSCR0(0, drv_data->ioaddr);
         pxa_set_cken(drv_data->master_info->clock_enable, 0);
 
         return 0;
 }
 
 static int pxa2xx_spi_resume(struct platform_device *pdev)
 {
         struct driver_data *drv_data = platform_get_drvdata(pdev);
         int status = 0;
 
         /* Enable the SSP clock */
         pxa_set_cken(drv_data->master_info->clock_enable, 1);
 
         /* 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 pxa2xx_spi_suspend NULL
 #define pxa2xx_spi_resume NULL
 #endif /* CONFIG_PM */
 
 static struct platform_driver driver = {
         .driver = {
                 .name = "pxa2xx-spi",
                 .bus = &platform_bus_type,
                 .owner = THIS_MODULE,
         },
         .probe = pxa2xx_spi_probe,
         .remove = __devexit_p(pxa2xx_spi_remove),
         .shutdown = pxa2xx_spi_shutdown,
         .suspend = pxa2xx_spi_suspend,
         .resume = pxa2xx_spi_resume,
 };
 
 static int __init pxa2xx_spi_init(void)
 {
         platform_driver_register(&driver);
 
         return 0;
 }
module_init(pxa2xx_spi_init);
 
 static void __exit pxa2xx_spi_exit(void)
 {
         platform_driver_unregister(&driver);
 }
 module_exit(pxa2xx_spi_exit);

⌨️ 快捷键说明

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