summaryrefslogtreecommitdiffstats
path: root/drivers/spi/spi-pl022.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi/spi-pl022.c')
-rw-r--r--drivers/spi/spi-pl022.c238
1 files changed, 127 insertions, 111 deletions
diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c
index 730b4a37b823..f103e470cb63 100644
--- a/drivers/spi/spi-pl022.c
+++ b/drivers/spi/spi-pl022.c
@@ -113,7 +113,6 @@
#define SSP_CR0_MASK_CSS_ST (0x1FUL << 16)
#define SSP_CR0_MASK_FRF_ST (0x3UL << 21)
-
/*
* SSP Control Register 0 - SSP_CR1
*/
@@ -283,7 +282,6 @@
#define SPI_POLLING_TIMEOUT 1000
-
/*
* The type of reading going on on this chip
*/
@@ -515,9 +513,6 @@ static void giveback(struct pl022 *pl022)
if (msg->complete)
msg->complete(msg->context);
/* This message is completed, so let's turn off the clocks & power */
- clk_disable(pl022->clk);
- amba_pclk_disable(pl022->adev);
- amba_vcore_disable(pl022->adev);
pm_runtime_put(&pl022->adev->dev);
}
@@ -752,7 +747,6 @@ static void readwriter(struct pl022 *pl022)
*/
}
-
/**
* next_transfer - Move to the Next transfer in the current spi message
* @pl022: SSP driver private data structure
@@ -1019,14 +1013,14 @@ static int configure_dma(struct pl022 *pl022)
dmaengine_slave_config(txchan, &tx_conf);
/* Create sglists for the transfers */
- pages = (pl022->cur_transfer->len >> PAGE_SHIFT) + 1;
+ pages = DIV_ROUND_UP(pl022->cur_transfer->len, PAGE_SIZE);
dev_dbg(&pl022->adev->dev, "using %d pages for transfer\n", pages);
- ret = sg_alloc_table(&pl022->sgt_rx, pages, GFP_KERNEL);
+ ret = sg_alloc_table(&pl022->sgt_rx, pages, GFP_ATOMIC);
if (ret)
goto err_alloc_rx_sg;
- ret = sg_alloc_table(&pl022->sgt_tx, pages, GFP_KERNEL);
+ ret = sg_alloc_table(&pl022->sgt_tx, pages, GFP_ATOMIC);
if (ret)
goto err_alloc_tx_sg;
@@ -1534,8 +1528,7 @@ static void pump_messages(struct work_struct *work)
/* Initial message state */
pl022->cur_msg->state = STATE_START;
pl022->cur_transfer = list_entry(pl022->cur_msg->transfers.next,
- struct spi_transfer,
- transfer_list);
+ struct spi_transfer, transfer_list);
/* Setup the SPI using the per chip configuration */
pl022->cur_chip = spi_get_ctldata(pl022->cur_msg->spi);
@@ -1545,9 +1538,6 @@ static void pump_messages(struct work_struct *work)
* (poll/interrupt/DMA)
*/
pm_runtime_get_sync(&pl022->adev->dev);
- amba_vcore_enable(pl022->adev);
- amba_pclk_enable(pl022->adev);
- clk_enable(pl022->clk);
restore_state(pl022);
flush(pl022);
@@ -1557,7 +1547,6 @@ static void pump_messages(struct work_struct *work)
do_interrupt_dma_transfer(pl022);
}
-
static int __init init_queue(struct pl022 *pl022)
{
INIT_LIST_HEAD(&pl022->queue);
@@ -1566,8 +1555,8 @@ static int __init init_queue(struct pl022 *pl022)
pl022->running = false;
pl022->busy = false;
- tasklet_init(&pl022->pump_transfers,
- pump_transfers, (unsigned long)pl022);
+ tasklet_init(&pl022->pump_transfers, pump_transfers,
+ (unsigned long)pl022);
INIT_WORK(&pl022->pump_messages, pump_messages);
pl022->workqueue = create_singlethread_workqueue(
@@ -1578,7 +1567,6 @@ static int __init init_queue(struct pl022 *pl022)
return 0;
}
-
static int start_queue(struct pl022 *pl022)
{
unsigned long flags;
@@ -1601,7 +1589,6 @@ static int start_queue(struct pl022 *pl022)
return 0;
}
-
static int stop_queue(struct pl022 *pl022)
{
unsigned long flags;
@@ -1797,71 +1784,70 @@ static int pl022_transfer(struct spi_device *spi, struct spi_message *msg)
return 0;
}
-static int calculate_effective_freq(struct pl022 *pl022,
- int freq,
- struct ssp_clock_params *clk_freq)
+static inline u32 spi_rate(u32 rate, u16 cpsdvsr, u16 scr)
+{
+ return rate / (cpsdvsr * (1 + scr));
+}
+
+static int calculate_effective_freq(struct pl022 *pl022, int freq, struct
+ ssp_clock_params * clk_freq)
{
/* Lets calculate the frequency parameters */
- u16 cpsdvsr = 2;
- u16 scr = 0;
- bool freq_found = false;
- u32 rate;
- u32 max_tclk;
- u32 min_tclk;
+ u16 cpsdvsr = CPSDVR_MIN, scr = SCR_MIN;
+ u32 rate, max_tclk, min_tclk, best_freq = 0, best_cpsdvsr = 0,
+ best_scr = 0, tmp, found = 0;
rate = clk_get_rate(pl022->clk);
/* cpsdvscr = 2 & scr 0 */
- max_tclk = (rate / (CPSDVR_MIN * (1 + SCR_MIN)));
+ max_tclk = spi_rate(rate, CPSDVR_MIN, SCR_MIN);
/* cpsdvsr = 254 & scr = 255 */
- min_tclk = (rate / (CPSDVR_MAX * (1 + SCR_MAX)));
-
- if ((freq <= max_tclk) && (freq >= min_tclk)) {
- while (cpsdvsr <= CPSDVR_MAX && !freq_found) {
- while (scr <= SCR_MAX && !freq_found) {
- if ((rate /
- (cpsdvsr * (1 + scr))) > freq)
- scr += 1;
- else {
- /*
- * This bool is made true when
- * effective frequency >=
- * target frequency is found
- */
- freq_found = true;
- if ((rate /
- (cpsdvsr * (1 + scr))) != freq) {
- if (scr == SCR_MIN) {
- cpsdvsr -= 2;
- scr = SCR_MAX;
- } else
- scr -= 1;
- }
- }
- }
- if (!freq_found) {
- cpsdvsr += 2;
- scr = SCR_MIN;
- }
- }
- if (cpsdvsr != 0) {
- dev_dbg(&pl022->adev->dev,
- "SSP Effective Frequency is %u\n",
- (rate / (cpsdvsr * (1 + scr))));
- clk_freq->cpsdvsr = (u8) (cpsdvsr & 0xFF);
- clk_freq->scr = (u8) (scr & 0xFF);
- dev_dbg(&pl022->adev->dev,
- "SSP cpsdvsr = %d, scr = %d\n",
- clk_freq->cpsdvsr, clk_freq->scr);
- }
- } else {
+ min_tclk = spi_rate(rate, CPSDVR_MAX, SCR_MAX);
+
+ if (!((freq <= max_tclk) && (freq >= min_tclk))) {
dev_err(&pl022->adev->dev,
"controller data is incorrect: out of range frequency");
return -EINVAL;
}
+
+ /*
+ * best_freq will give closest possible available rate (<= requested
+ * freq) for all values of scr & cpsdvsr.
+ */
+ while ((cpsdvsr <= CPSDVR_MAX) && !found) {
+ while (scr <= SCR_MAX) {
+ tmp = spi_rate(rate, cpsdvsr, scr);
+
+ if (tmp > freq)
+ scr++;
+ /*
+ * If found exact value, update and break.
+ * If found more closer value, update and continue.
+ */
+ else if ((tmp == freq) || (tmp > best_freq)) {
+ best_freq = tmp;
+ best_cpsdvsr = cpsdvsr;
+ best_scr = scr;
+
+ if (tmp == freq)
+ break;
+ }
+ scr++;
+ }
+ cpsdvsr += 2;
+ scr = SCR_MIN;
+ }
+
+ clk_freq->cpsdvsr = (u8) (best_cpsdvsr & 0xFF);
+ clk_freq->scr = (u8) (best_scr & 0xFF);
+ dev_dbg(&pl022->adev->dev,
+ "SSP Target Frequency is: %u, Effective Frequency is %u\n",
+ freq, best_freq);
+ dev_dbg(&pl022->adev->dev, "SSP cpsdvsr = %d, scr = %d\n",
+ clk_freq->cpsdvsr, clk_freq->scr);
+
return 0;
}
-
/*
* A piece of default chip info unless the platform
* supplies it.
@@ -1879,7 +1865,6 @@ static const struct pl022_config_chip pl022_default_chip_info = {
.cs_control = null_cs_control,
};
-
/**
* pl022_setup - setup function registered to SPI master framework
* @spi: spi device which is requesting setup
@@ -1956,7 +1941,6 @@ static int pl022_setup(struct spi_device *spi)
goto err_config_params;
}
-
status = verify_controller_parameters(pl022, chip_info);
if (status) {
dev_err(&spi->dev, "controller data is incorrect");
@@ -2096,7 +2080,8 @@ static int pl022_setup(struct spi_device *spi)
}
SSP_WRITE_BITS(chip->cr1, SSP_DISABLED, SSP_CR1_MASK_SSE, 1);
SSP_WRITE_BITS(chip->cr1, chip_info->hierarchy, SSP_CR1_MASK_MS, 2);
- SSP_WRITE_BITS(chip->cr1, chip_info->slave_tx_disable, SSP_CR1_MASK_SOD, 3);
+ SSP_WRITE_BITS(chip->cr1, chip_info->slave_tx_disable, SSP_CR1_MASK_SOD,
+ 3);
/* Save controller_state */
spi_set_ctldata(spi, chip);
@@ -2122,7 +2107,6 @@ static void pl022_cleanup(struct spi_device *spi)
kfree(chip);
}
-
static int __devinit
pl022_probe(struct amba_device *adev, const struct amba_id *id)
{
@@ -2186,8 +2170,6 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
}
printk(KERN_INFO "pl022: mapped registers from 0x%08x to %p\n",
adev->res.start, pl022->virtbase);
- pm_runtime_enable(dev);
- pm_runtime_resume(dev);
pl022->clk = clk_get(&adev->dev, NULL);
if (IS_ERR(pl022->clk)) {
@@ -2196,6 +2178,12 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
goto err_no_clk;
}
+ status = clk_prepare(pl022->clk);
+ if (status) {
+ dev_err(&adev->dev, "could not prepare SSP/SPI bus clock\n");
+ goto err_clk_prep;
+ }
+
/* Disable SSP */
writew((readw(SSP_CR1(pl022->virtbase)) & (~SSP_CR1_MASK_SSE)),
SSP_CR1(pl022->virtbase));
@@ -2235,22 +2223,22 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
goto err_spi_register;
}
dev_dbg(dev, "probe succeeded\n");
- /*
- * Disable the silicon block pclk and any voltage domain and just
- * power it up and clock it when it's needed
- */
- amba_pclk_disable(adev);
- amba_vcore_disable(adev);
+
+ /* let runtime pm put suspend */
+ pm_runtime_put(dev);
return 0;
err_spi_register:
err_start_queue:
err_init_queue:
destroy_queue(pl022);
- pl022_dma_remove(pl022);
+ if (platform_info->enable_dma)
+ pl022_dma_remove(pl022);
+
free_irq(adev->irq[0], pl022);
- pm_runtime_disable(&adev->dev);
err_no_irq:
+ clk_unprepare(pl022->clk);
+ err_clk_prep:
clk_put(pl022->clk);
err_no_clk:
iounmap(pl022->virtbase);
@@ -2271,13 +2259,22 @@ pl022_remove(struct amba_device *adev)
if (!pl022)
return 0;
+ /*
+ * undo pm_runtime_put() in probe. I assume that we're not
+ * accessing the primecell here.
+ */
+ pm_runtime_get_noresume(&adev->dev);
+
/* Remove the queue */
if (destroy_queue(pl022) != 0)
dev_err(&adev->dev, "queue remove failed\n");
load_ssp_default_config(pl022);
- pl022_dma_remove(pl022);
+ if (pl022->master_info->enable_dma)
+ pl022_dma_remove(pl022);
+
free_irq(adev->irq[0], pl022);
clk_disable(pl022->clk);
+ clk_unprepare(pl022->clk);
clk_put(pl022->clk);
iounmap(pl022->virtbase);
amba_release_regions(adev);
@@ -2288,46 +2285,70 @@ pl022_remove(struct amba_device *adev)
return 0;
}
-#ifdef CONFIG_PM
-static int pl022_suspend(struct amba_device *adev, pm_message_t state)
+#ifdef CONFIG_SUSPEND
+static int pl022_suspend(struct device *dev)
{
- struct pl022 *pl022 = amba_get_drvdata(adev);
+ struct pl022 *pl022 = dev_get_drvdata(dev);
int status = 0;
status = stop_queue(pl022);
if (status) {
- dev_warn(&adev->dev, "suspend cannot stop queue\n");
+ dev_warn(dev, "suspend cannot stop queue\n");
return status;
}
- amba_vcore_enable(adev);
- amba_pclk_enable(adev);
+ amba_vcore_enable(pl022->adev);
+ amba_pclk_enable(pl022->adev);
load_ssp_default_config(pl022);
- amba_pclk_disable(adev);
- amba_vcore_disable(adev);
- dev_dbg(&adev->dev, "suspended\n");
+ amba_pclk_disable(pl022->adev);
+ amba_vcore_disable(pl022->adev);
+ dev_dbg(dev, "suspended\n");
return 0;
}
-static int pl022_resume(struct amba_device *adev)
+static int pl022_resume(struct device *dev)
{
- struct pl022 *pl022 = amba_get_drvdata(adev);
+ struct pl022 *pl022 = dev_get_drvdata(dev);
int status = 0;
/* Start the queue running */
status = start_queue(pl022);
if (status)
- dev_err(&adev->dev, "problem starting queue (%d)\n", status);
+ dev_err(dev, "problem starting queue (%d)\n", status);
else
- dev_dbg(&adev->dev, "resumed\n");
+ dev_dbg(dev, "resumed\n");
return status;
}
-#else
-#define pl022_suspend NULL
-#define pl022_resume NULL
#endif /* CONFIG_PM */
+#ifdef CONFIG_PM_RUNTIME
+static int pl022_runtime_suspend(struct device *dev)
+{
+ struct pl022 *pl022 = dev_get_drvdata(dev);
+
+ clk_disable(pl022->clk);
+ amba_vcore_disable(pl022->adev);
+
+ return 0;
+}
+
+static int pl022_runtime_resume(struct device *dev)
+{
+ struct pl022 *pl022 = dev_get_drvdata(dev);
+
+ amba_vcore_enable(pl022->adev);
+ clk_enable(pl022->clk);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops pl022_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pl022_suspend, pl022_resume)
+ SET_RUNTIME_PM_OPS(pl022_runtime_suspend, pl022_runtime_resume, NULL)
+};
+
static struct vendor_data vendor_arm = {
.fifodepth = 8,
.max_bpw = 16,
@@ -2337,7 +2358,6 @@ static struct vendor_data vendor_arm = {
.loopback = true,
};
-
static struct vendor_data vendor_st = {
.fifodepth = 32,
.max_bpw = 32,
@@ -2392,9 +2412,9 @@ static struct amba_id pl022_ids[] = {
* and 32 locations deep TX/RX FIFO but no extended
* CR0/CR1 register
*/
- .id = 0x00080023,
- .mask = 0xffffffff,
- .data = &vendor_st_pl023,
+ .id = 0x00080023,
+ .mask = 0xffffffff,
+ .data = &vendor_st_pl023,
},
{
.id = 0x10080023,
@@ -2407,27 +2427,23 @@ static struct amba_id pl022_ids[] = {
static struct amba_driver pl022_driver = {
.drv = {
.name = "ssp-pl022",
+ .pm = &pl022_dev_pm_ops,
},
.id_table = pl022_ids,
.probe = pl022_probe,
.remove = __devexit_p(pl022_remove),
- .suspend = pl022_suspend,
- .resume = pl022_resume,
};
-
static int __init pl022_init(void)
{
return amba_driver_register(&pl022_driver);
}
-
subsys_initcall(pl022_init);
static void __exit pl022_exit(void)
{
amba_driver_unregister(&pl022_driver);
}
-
module_exit(pl022_exit);
MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>");
OpenPOWER on IntegriCloud