Commit da6de6d3 authored by Mark Brown's avatar Mark Brown

spi: qup: Allow scaling power domains and

Merge series from Stephan Gerhold <stephan.gerhold@kernkonzept.com>:

Make it possible to scale performance states of the power domain and
interconnect of the SPI QUP controller in relation to the selected SPI
speed / core clock. This is done separately by:

  - Parsing the OPP table from the device tree for performance state
    votes of the power domain
  - Voting for the necessary bandwidth on the interconnect path to DRAM
parents 8a771075 ecdaa947
...@@ -44,9 +44,17 @@ properties: ...@@ -44,9 +44,17 @@ properties:
- const: tx - const: tx
- const: rx - const: rx
interconnects:
maxItems: 1
interrupts: interrupts:
maxItems: 1 maxItems: 1
operating-points-v2: true
power-domains:
maxItems: 1
reg: reg:
maxItems: 1 maxItems: 1
...@@ -62,7 +70,9 @@ unevaluatedProperties: false ...@@ -62,7 +70,9 @@ unevaluatedProperties: false
examples: examples:
- | - |
#include <dt-bindings/clock/qcom,gcc-msm8996.h> #include <dt-bindings/clock/qcom,gcc-msm8996.h>
#include <dt-bindings/interconnect/qcom,msm8996.h>
#include <dt-bindings/interrupt-controller/arm-gic.h> #include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/power/qcom-rpmpd.h>
spi@7575000 { spi@7575000 {
compatible = "qcom,spi-qup-v2.2.1"; compatible = "qcom,spi-qup-v2.2.1";
...@@ -76,6 +86,9 @@ examples: ...@@ -76,6 +86,9 @@ examples:
pinctrl-1 = <&blsp1_spi1_sleep>; pinctrl-1 = <&blsp1_spi1_sleep>;
dmas = <&blsp1_dma 12>, <&blsp1_dma 13>; dmas = <&blsp1_dma 12>, <&blsp1_dma 13>;
dma-names = "tx", "rx"; dma-names = "tx", "rx";
power-domains = <&rpmpd MSM8996_VDDCX>;
operating-points-v2 = <&spi_opp_table>;
interconnects = <&pnoc MASTER_BLSP_1 &bimc SLAVE_EBI_CH0>;
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
}; };
...@@ -6,12 +6,14 @@ ...@@ -6,12 +6,14 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/interconnect.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_opp.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/dmaengine.h> #include <linux/dmaengine.h>
...@@ -121,11 +123,14 @@ ...@@ -121,11 +123,14 @@
#define SPI_DELAY_THRESHOLD 1 #define SPI_DELAY_THRESHOLD 1
#define SPI_DELAY_RETRY 10 #define SPI_DELAY_RETRY 10
#define SPI_BUS_WIDTH 8
struct spi_qup { struct spi_qup {
void __iomem *base; void __iomem *base;
struct device *dev; struct device *dev;
struct clk *cclk; /* core clock */ struct clk *cclk; /* core clock */
struct clk *iclk; /* interface clock */ struct clk *iclk; /* interface clock */
struct icc_path *icc_path; /* interconnect to RAM */
int irq; int irq;
spinlock_t lock; spinlock_t lock;
...@@ -148,6 +153,8 @@ struct spi_qup { ...@@ -148,6 +153,8 @@ struct spi_qup {
int mode; int mode;
struct dma_slave_config rx_conf; struct dma_slave_config rx_conf;
struct dma_slave_config tx_conf; struct dma_slave_config tx_conf;
u32 bw_speed_hz;
}; };
static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer); static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer);
...@@ -180,6 +187,23 @@ static inline bool spi_qup_is_valid_state(struct spi_qup *controller) ...@@ -180,6 +187,23 @@ static inline bool spi_qup_is_valid_state(struct spi_qup *controller)
return opstate & QUP_STATE_VALID; return opstate & QUP_STATE_VALID;
} }
static int spi_qup_vote_bw(struct spi_qup *controller, u32 speed_hz)
{
u32 needed_peak_bw;
int ret;
if (controller->bw_speed_hz == speed_hz)
return 0;
needed_peak_bw = Bps_to_icc(speed_hz * SPI_BUS_WIDTH);
ret = icc_set_bw(controller->icc_path, 0, needed_peak_bw);
if (ret)
return ret;
controller->bw_speed_hz = speed_hz;
return 0;
}
static int spi_qup_set_state(struct spi_qup *controller, u32 state) static int spi_qup_set_state(struct spi_qup *controller, u32 state)
{ {
unsigned long loop; unsigned long loop;
...@@ -450,6 +474,12 @@ static int spi_qup_do_dma(struct spi_device *spi, struct spi_transfer *xfer, ...@@ -450,6 +474,12 @@ static int spi_qup_do_dma(struct spi_device *spi, struct spi_transfer *xfer,
struct scatterlist *tx_sgl, *rx_sgl; struct scatterlist *tx_sgl, *rx_sgl;
int ret; int ret;
ret = spi_qup_vote_bw(qup, xfer->speed_hz);
if (ret) {
dev_err(qup->dev, "fail to vote for ICC bandwidth: %d\n", ret);
return -EIO;
}
if (xfer->rx_buf) if (xfer->rx_buf)
rx_done = spi_qup_dma_done; rx_done = spi_qup_dma_done;
else if (xfer->tx_buf) else if (xfer->tx_buf)
...@@ -667,7 +697,7 @@ static int spi_qup_io_prep(struct spi_device *spi, struct spi_transfer *xfer) ...@@ -667,7 +697,7 @@ static int spi_qup_io_prep(struct spi_device *spi, struct spi_transfer *xfer)
return -EIO; return -EIO;
} }
ret = clk_set_rate(controller->cclk, xfer->speed_hz); ret = dev_pm_opp_set_rate(controller->dev, xfer->speed_hz);
if (ret) { if (ret) {
dev_err(controller->dev, "fail to set frequency %d", dev_err(controller->dev, "fail to set frequency %d",
xfer->speed_hz); xfer->speed_hz);
...@@ -993,6 +1023,7 @@ static void spi_qup_set_cs(struct spi_device *spi, bool val) ...@@ -993,6 +1023,7 @@ static void spi_qup_set_cs(struct spi_device *spi, bool val)
static int spi_qup_probe(struct platform_device *pdev) static int spi_qup_probe(struct platform_device *pdev)
{ {
struct spi_controller *host; struct spi_controller *host;
struct icc_path *icc_path;
struct clk *iclk, *cclk; struct clk *iclk, *cclk;
struct spi_qup *controller; struct spi_qup *controller;
struct resource *res; struct resource *res;
...@@ -1018,6 +1049,11 @@ static int spi_qup_probe(struct platform_device *pdev) ...@@ -1018,6 +1049,11 @@ static int spi_qup_probe(struct platform_device *pdev)
if (IS_ERR(iclk)) if (IS_ERR(iclk))
return PTR_ERR(iclk); return PTR_ERR(iclk);
icc_path = devm_of_icc_get(dev, NULL);
if (IS_ERR(icc_path))
return dev_err_probe(dev, PTR_ERR(icc_path),
"failed to get interconnect path\n");
/* This is optional parameter */ /* This is optional parameter */
if (of_property_read_u32(dev->of_node, "spi-max-frequency", &max_freq)) if (of_property_read_u32(dev->of_node, "spi-max-frequency", &max_freq))
max_freq = SPI_MAX_RATE; max_freq = SPI_MAX_RATE;
...@@ -1027,6 +1063,15 @@ static int spi_qup_probe(struct platform_device *pdev) ...@@ -1027,6 +1063,15 @@ static int spi_qup_probe(struct platform_device *pdev)
return -ENXIO; return -ENXIO;
} }
ret = devm_pm_opp_set_clkname(dev, "core");
if (ret)
return ret;
/* OPP table is optional */
ret = devm_pm_opp_of_add_table(dev);
if (ret && ret != -ENODEV)
return dev_err_probe(dev, ret, "invalid OPP table\n");
host = spi_alloc_host(dev, sizeof(struct spi_qup)); host = spi_alloc_host(dev, sizeof(struct spi_qup));
if (!host) { if (!host) {
dev_err(dev, "cannot allocate host\n"); dev_err(dev, "cannot allocate host\n");
...@@ -1060,6 +1105,7 @@ static int spi_qup_probe(struct platform_device *pdev) ...@@ -1060,6 +1105,7 @@ static int spi_qup_probe(struct platform_device *pdev)
controller->base = base; controller->base = base;
controller->iclk = iclk; controller->iclk = iclk;
controller->cclk = cclk; controller->cclk = cclk;
controller->icc_path = icc_path;
controller->irq = irq; controller->irq = irq;
ret = spi_qup_init_dma(host, res->start); ret = spi_qup_init_dma(host, res->start);
...@@ -1180,6 +1226,7 @@ static int spi_qup_pm_suspend_runtime(struct device *device) ...@@ -1180,6 +1226,7 @@ static int spi_qup_pm_suspend_runtime(struct device *device)
writel_relaxed(config, controller->base + QUP_CONFIG); writel_relaxed(config, controller->base + QUP_CONFIG);
clk_disable_unprepare(controller->cclk); clk_disable_unprepare(controller->cclk);
spi_qup_vote_bw(controller, 0);
clk_disable_unprepare(controller->iclk); clk_disable_unprepare(controller->iclk);
return 0; return 0;
...@@ -1231,6 +1278,7 @@ static int spi_qup_suspend(struct device *device) ...@@ -1231,6 +1278,7 @@ static int spi_qup_suspend(struct device *device)
return ret; return ret;
clk_disable_unprepare(controller->cclk); clk_disable_unprepare(controller->cclk);
spi_qup_vote_bw(controller, 0);
clk_disable_unprepare(controller->iclk); clk_disable_unprepare(controller->iclk);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment