Commit bc266185 authored by Grant Likely's avatar Grant Likely
Browse files
Conflicts:
	drivers/spi/spi-s3c64xx.c
parents dcd6c922 b97b6621
Loading
Loading
Loading
Loading
+103 −11
Original line number Diff line number Diff line
@@ -20,10 +20,12 @@
#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>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/spi/spi.h>

#include <mach/dma.h>
@@ -153,6 +155,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.
@@ -780,6 +783,8 @@ static void s3c64xx_spi_work(struct work_struct *work)
	while (!acquire_dma(sdd))
		msleep(10);

	pm_runtime_get_sync(&sdd->pdev->dev);

	spin_lock_irqsave(&sdd->lock, flags);

	while (!list_empty(&sdd->queue)
@@ -808,6 +813,8 @@ static void s3c64xx_spi_work(struct work_struct *work)
	/* Free DMA channels */
	sdd->ops->release(sdd->rx_dma.ch, &s3c64xx_spi_dma_client);
	sdd->ops->release(sdd->tx_dma.ch, &s3c64xx_spi_dma_client);

	pm_runtime_put(&sdd->pdev->dev);
}

static int s3c64xx_spi_transfer(struct spi_device *spi,
@@ -890,6 +897,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
		goto setup_exit;
	}

	pm_runtime_get_sync(&sdd->pdev->dev);

	/* Check if we can provide the requested rate */
	if (!sci->clk_from_cmu) {
		u32 psr, speed;
@@ -922,6 +931,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
			err = -EINVAL;
	}

	pm_runtime_put(&sdd->pdev->dev);

setup_exit:

	/* setup() returns with device de-selected */
@@ -930,6 +941,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 +1008,7 @@ 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) {
@@ -1006,6 +1044,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) {
@@ -1100,10 +1144,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 "
@@ -1113,8 +1168,12 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
					mem_res->end, mem_res->start,
					sdd->rx_dma.dmach, sdd->tx_dma.dmach);

	pm_runtime_enable(&pdev->dev);

	return 0;

err9:
	free_irq(irq, sdd);
err8:
	destroy_workqueue(sdd->workqueue);
err7:
@@ -1144,6 +1203,8 @@ static int s3c64xx_spi_remove(struct platform_device *pdev)
	struct resource	*mem_res;
	unsigned long flags;

	pm_runtime_disable(&pdev->dev);

	spin_lock_irqsave(&sdd->lock, flags);
	sdd->state |= SUSPND;
	spin_unlock_irqrestore(&sdd->lock, flags);
@@ -1153,6 +1214,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);
@@ -1174,9 +1239,9 @@ static int s3c64xx_spi_remove(struct platform_device *pdev)
}

#ifdef CONFIG_PM
static int s3c64xx_spi_suspend(struct platform_device *pdev, pm_message_t state)
static int s3c64xx_spi_suspend(struct device *dev)
{
	struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
	struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
	struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
	unsigned long flags;

@@ -1196,9 +1261,10 @@ static int s3c64xx_spi_suspend(struct platform_device *pdev, pm_message_t state)
	return 0;
}

static int s3c64xx_spi_resume(struct platform_device *pdev)
static int s3c64xx_spi_resume(struct device *dev)
{
	struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
	struct platform_device *pdev = to_platform_device(dev);
	struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
	struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
	struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
	unsigned long flags;
@@ -1217,19 +1283,45 @@ static int s3c64xx_spi_resume(struct platform_device *pdev)

	return 0;
}
#else
#define s3c64xx_spi_suspend	NULL
#define s3c64xx_spi_resume	NULL
#endif /* CONFIG_PM */

#ifdef CONFIG_PM_RUNTIME
static int s3c64xx_spi_runtime_suspend(struct device *dev)
{
	struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
	struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);

	clk_disable(sdd->clk);
	clk_disable(sdd->src_clk);

	return 0;
}

static int s3c64xx_spi_runtime_resume(struct device *dev)
{
	struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
	struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);

	clk_enable(sdd->src_clk);
	clk_enable(sdd->clk);

	return 0;
}
#endif /* CONFIG_PM_RUNTIME */

static const struct dev_pm_ops s3c64xx_spi_pm = {
	SET_SYSTEM_SLEEP_PM_OPS(s3c64xx_spi_suspend, s3c64xx_spi_resume)
	SET_RUNTIME_PM_OPS(s3c64xx_spi_runtime_suspend,
			   s3c64xx_spi_runtime_resume, NULL)
};

static struct platform_driver s3c64xx_spi_driver = {
	.driver = {
		.name	= "s3c64xx-spi",
		.owner = THIS_MODULE,
		.pm = &s3c64xx_spi_pm,
	},
	.remove = s3c64xx_spi_remove,
	.suspend = s3c64xx_spi_suspend,
	.resume = s3c64xx_spi_resume,
};
MODULE_ALIAS("platform:s3c64xx-spi");