📄 pxa2xx_spi.c
字号:
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 + -