|
@@ -20,6 +20,7 @@
|
|
|
#include <linux/init.h>
|
|
|
#include <linux/module.h>
|
|
|
#include <linux/workqueue.h>
|
|
|
+#include <linux/interrupt.h>
|
|
|
#include <linux/delay.h>
|
|
|
#include <linux/clk.h>
|
|
|
#include <linux/dma-mapping.h>
|
|
@@ -153,6 +154,7 @@ struct s3c64xx_spi_dma_data {
|
|
|
* @tx_dmach: Controller's DMA channel for Tx.
|
|
|
* @sfr_start: BUS address of SPI controller regs.
|
|
|
* @regs: Pointer to ioremap'ed controller registers.
|
|
|
+ * @irq: interrupt
|
|
|
* @xfer_completion: To indicate completion of xfer task.
|
|
|
* @cur_mode: Stores the active configuration of the controller.
|
|
|
* @cur_bpw: Stores the active bits per word settings.
|
|
@@ -930,6 +932,33 @@ setup_exit:
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+static irqreturn_t s3c64xx_spi_irq(int irq, void *data)
|
|
|
+{
|
|
|
+ struct s3c64xx_spi_driver_data *sdd = data;
|
|
|
+ struct spi_master *spi = sdd->master;
|
|
|
+ unsigned int val;
|
|
|
+
|
|
|
+ val = readl(sdd->regs + S3C64XX_SPI_PENDING_CLR);
|
|
|
+
|
|
|
+ val &= S3C64XX_SPI_PND_RX_OVERRUN_CLR |
|
|
|
+ S3C64XX_SPI_PND_RX_UNDERRUN_CLR |
|
|
|
+ S3C64XX_SPI_PND_TX_OVERRUN_CLR |
|
|
|
+ S3C64XX_SPI_PND_TX_UNDERRUN_CLR;
|
|
|
+
|
|
|
+ writel(val, sdd->regs + S3C64XX_SPI_PENDING_CLR);
|
|
|
+
|
|
|
+ if (val & S3C64XX_SPI_PND_RX_OVERRUN_CLR)
|
|
|
+ dev_err(&spi->dev, "RX overrun\n");
|
|
|
+ if (val & S3C64XX_SPI_PND_RX_UNDERRUN_CLR)
|
|
|
+ dev_err(&spi->dev, "RX underrun\n");
|
|
|
+ if (val & S3C64XX_SPI_PND_TX_OVERRUN_CLR)
|
|
|
+ dev_err(&spi->dev, "TX overrun\n");
|
|
|
+ if (val & S3C64XX_SPI_PND_TX_UNDERRUN_CLR)
|
|
|
+ dev_err(&spi->dev, "TX underrun\n");
|
|
|
+
|
|
|
+ return IRQ_HANDLED;
|
|
|
+}
|
|
|
+
|
|
|
static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel)
|
|
|
{
|
|
|
struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
|
|
@@ -970,7 +999,8 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
|
|
|
struct s3c64xx_spi_driver_data *sdd;
|
|
|
struct s3c64xx_spi_info *sci;
|
|
|
struct spi_master *master;
|
|
|
- int ret;
|
|
|
+ int ret, irq;
|
|
|
+ char clk_name[16];
|
|
|
|
|
|
if (pdev->id < 0) {
|
|
|
dev_err(&pdev->dev,
|
|
@@ -1010,6 +1040,12 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
|
|
|
return -ENXIO;
|
|
|
}
|
|
|
|
|
|
+ irq = platform_get_irq(pdev, 0);
|
|
|
+ if (irq < 0) {
|
|
|
+ dev_warn(&pdev->dev, "Failed to get IRQ: %d\n", irq);
|
|
|
+ return irq;
|
|
|
+ }
|
|
|
+
|
|
|
master = spi_alloc_master(&pdev->dev,
|
|
|
sizeof(struct s3c64xx_spi_driver_data));
|
|
|
if (master == NULL) {
|
|
@@ -1104,10 +1140,21 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
|
|
|
INIT_WORK(&sdd->work, s3c64xx_spi_work);
|
|
|
INIT_LIST_HEAD(&sdd->queue);
|
|
|
|
|
|
+ ret = request_irq(irq, s3c64xx_spi_irq, 0, "spi-s3c64xx", sdd);
|
|
|
+ if (ret != 0) {
|
|
|
+ dev_err(&pdev->dev, "Failed to request IRQ %d: %d\n",
|
|
|
+ irq, ret);
|
|
|
+ goto err8;
|
|
|
+ }
|
|
|
+
|
|
|
+ writel(S3C64XX_SPI_INT_RX_OVERRUN_EN | S3C64XX_SPI_INT_RX_UNDERRUN_EN |
|
|
|
+ S3C64XX_SPI_INT_TX_OVERRUN_EN | S3C64XX_SPI_INT_TX_UNDERRUN_EN,
|
|
|
+ sdd->regs + S3C64XX_SPI_INT_EN);
|
|
|
+
|
|
|
if (spi_register_master(master)) {
|
|
|
dev_err(&pdev->dev, "cannot register SPI master\n");
|
|
|
ret = -EBUSY;
|
|
|
- goto err8;
|
|
|
+ goto err9;
|
|
|
}
|
|
|
|
|
|
dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d "
|
|
@@ -1119,6 +1166,8 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
+err9:
|
|
|
+ free_irq(irq, sdd);
|
|
|
err8:
|
|
|
destroy_workqueue(sdd->workqueue);
|
|
|
err7:
|
|
@@ -1157,6 +1206,10 @@ static int s3c64xx_spi_remove(struct platform_device *pdev)
|
|
|
|
|
|
spi_unregister_master(master);
|
|
|
|
|
|
+ writel(0, sdd->regs + S3C64XX_SPI_INT_EN);
|
|
|
+
|
|
|
+ free_irq(platform_get_irq(pdev, 0), sdd);
|
|
|
+
|
|
|
destroy_workqueue(sdd->workqueue);
|
|
|
|
|
|
clk_disable(sdd->src_clk);
|