ipq806x: clean up patches, port missing patches from 4.4
Signed-off-by: John Crispin <john@phrozen.org> Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
parent
99b6aefd63
commit
1adf51702e
68 changed files with 4796 additions and 134 deletions
|
@ -1,7 +1,7 @@
|
|||
From f275ae09b82aa423d2ea5a2be3ea315c8fcf6143 Mon Sep 17 00:00:00 2001
|
||||
From 28d0ed88f536dd639adf1b0c7c08e04be3c8f294 Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Pedersen <twp@codeaurora.org>
|
||||
Date: Mon, 16 May 2016 17:58:50 -0700
|
||||
Subject: [PATCH 01/37] dtbindings: qcom_adm: Fix channel specifiers
|
||||
Subject: [PATCH 01/69] dtbindings: qcom_adm: Fix channel specifiers
|
||||
|
||||
Original patch from Andy Gross.
|
||||
|
||||
|
@ -17,7 +17,7 @@ via other means.
|
|||
Signed-off-by: Andy Gross <agross@codeaurora.org>
|
||||
Signed-off-by: Thomas Pedersen <twp@codeaurora.org>
|
||||
---
|
||||
Documentation/devicetree/bindings/dma/qcom_adm.txt | 16 ++++++----------
|
||||
Documentation/devicetree/bindings/dma/qcom_adm.txt | 16 ++++++----------
|
||||
1 file changed, 6 insertions(+), 10 deletions(-)
|
||||
|
||||
--- a/Documentation/devicetree/bindings/dma/qcom_adm.txt
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
From 1d32bf93c8e83db0aca04d2961badef7e86d663b Mon Sep 17 00:00:00 2001
|
||||
From 563fa24db4e529c5a3311928d73a8a90531ee527 Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Pedersen <twp@codeaurora.org>
|
||||
Date: Mon, 16 May 2016 17:58:51 -0700
|
||||
Subject: [PATCH 02/37] dmaengine: Add ADM driver
|
||||
Subject: [PATCH 02/69] dmaengine: Add ADM driver
|
||||
|
||||
Original patch by Andy Gross.
|
||||
|
||||
|
@ -18,9 +18,9 @@ and also incorporates CRCI (client rate control interface) flow control.
|
|||
Signed-off-by: Andy Gross <agross@codeaurora.org>
|
||||
Signed-off-by: Thomas Pedersen <twp@codeaurora.org>
|
||||
---
|
||||
drivers/dma/qcom/Kconfig | 10 +
|
||||
drivers/dma/qcom/Makefile | 1 +
|
||||
drivers/dma/qcom/qcom_adm.c | 900 +++++++++++++++++++++++++++++++++++++++++++
|
||||
drivers/dma/qcom/Kconfig | 10 +
|
||||
drivers/dma/qcom/Makefile | 1 +
|
||||
drivers/dma/qcom/qcom_adm.c | 900 ++++++++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 911 insertions(+)
|
||||
create mode 100644 drivers/dma/qcom/qcom_adm.c
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
From 2852d6df4b60987f9248c3d36d5fe2462e6556b9 Mon Sep 17 00:00:00 2001
|
||||
From 57c4d2626bcb990a2e677b4f769a88c3d8e0911d Mon Sep 17 00:00:00 2001
|
||||
From: Andy Gross <andy.gross@linaro.org>
|
||||
Date: Tue, 12 Apr 2016 09:11:47 -0500
|
||||
Subject: [PATCH 06/37] spi: qup: Make sure mode is only determined once
|
||||
Subject: [PATCH 03/69] spi: qup: Make sure mode is only determined once
|
||||
|
||||
This patch calculates the mode once. All decisions on the current
|
||||
transaction
|
||||
|
@ -9,7 +9,7 @@ is made using the mode instead of use_dma
|
|||
|
||||
Signed-off-by: Andy Gross <andy.gross@linaro.org>
|
||||
---
|
||||
drivers/spi/spi-qup.c | 87 +++++++++++++++++++++----------------------------
|
||||
drivers/spi/spi-qup.c | 87 ++++++++++++++++++++++-----------------------------
|
||||
1 file changed, 37 insertions(+), 50 deletions(-)
|
||||
|
||||
--- a/drivers/spi/spi-qup.c
|
|
@ -1,7 +1,7 @@
|
|||
From 826290ee1fd1bcd26b6771f94f6680a4ff8951c4 Mon Sep 17 00:00:00 2001
|
||||
From fbdf80d138f8c7fda8e598287109fb90446d557d Mon Sep 17 00:00:00 2001
|
||||
From: Andy Gross <andy.gross@linaro.org>
|
||||
Date: Fri, 29 Jan 2016 22:06:50 -0600
|
||||
Subject: [PATCH 07/37] spi: qup: Fix transaction done signaling
|
||||
Subject: [PATCH 04/69] spi: qup: Fix transaction done signaling
|
||||
|
||||
Wait to signal done until we get all of the interrupts we are expecting
|
||||
to get for a transaction. If we don't wait for the input done flag, we
|
||||
|
@ -12,7 +12,7 @@ CC: Grant Grundler <grundler@chromium.org>
|
|||
CC: Sarthak Kukreti <skukreti@codeaurora.org>
|
||||
Signed-off-by: Andy Gross <andy.gross@linaro.org>
|
||||
---
|
||||
drivers/spi/spi-qup.c | 3 ++-
|
||||
drivers/spi/spi-qup.c | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/spi/spi-qup.c
|
|
@ -1,7 +1,7 @@
|
|||
From 715d008b67b21fb8bfefaeeefa5b8ddf23777872 Mon Sep 17 00:00:00 2001
|
||||
From 1204ea49f3f7ded898d1ee202776093715a9ecf6 Mon Sep 17 00:00:00 2001
|
||||
From: Andy Gross <andy.gross@linaro.org>
|
||||
Date: Tue, 2 Feb 2016 17:00:53 -0600
|
||||
Subject: [PATCH 08/37] spi: qup: Fix DMA mode to work correctly
|
||||
Subject: [PATCH 05/69] spi: qup: Fix DMA mode to work correctly
|
||||
|
||||
This patch fixes a few issues with the DMA mode. The QUP needs to be
|
||||
placed in the run mode before the DMA transactions are executed. The
|
||||
|
@ -10,7 +10,7 @@ This is due to v1.1.1 using ADM DMA and later revisions using BAM.
|
|||
|
||||
Signed-off-by: Andy Gross <andy.gross@linaro.org>
|
||||
---
|
||||
drivers/spi/spi-qup.c | 94 ++++++++++++++++++++++++++++++++-----------------
|
||||
drivers/spi/spi-qup.c | 94 +++++++++++++++++++++++++++++++++------------------
|
||||
1 file changed, 62 insertions(+), 32 deletions(-)
|
||||
|
||||
--- a/drivers/spi/spi-qup.c
|
|
@ -1,14 +1,14 @@
|
|||
From 4dc7631bbf7c7ac7548026ce45d889235e4f5892 Mon Sep 17 00:00:00 2001
|
||||
From b56c1e35cc550fd014fa601ca56b964d88fd44a9 Mon Sep 17 00:00:00 2001
|
||||
From: Andy Gross <andy.gross@linaro.org>
|
||||
Date: Sun, 31 Jan 2016 21:28:13 -0600
|
||||
Subject: [PATCH 09/37] spi: qup: Fix block mode to work correctly
|
||||
Subject: [PATCH 06/69] spi: qup: Fix block mode to work correctly
|
||||
|
||||
This patch corrects the behavior of the BLOCK transactions. During block
|
||||
transactions, the controller must be read/written to in block size transactions.
|
||||
|
||||
Signed-off-by: Andy Gross <andy.gross@linaro.org>
|
||||
---
|
||||
drivers/spi/spi-qup.c | 182 ++++++++++++++++++++++++++++++++++++++-----------
|
||||
drivers/spi/spi-qup.c | 182 +++++++++++++++++++++++++++++++++++++++-----------
|
||||
1 file changed, 142 insertions(+), 40 deletions(-)
|
||||
|
||||
--- a/drivers/spi/spi-qup.c
|
|
@ -1,7 +1,7 @@
|
|||
From 543618f5388d487ba88e3d5304c161fc3ccf61d1 Mon Sep 17 00:00:00 2001
|
||||
From 0f32f976ebaa7d8643fcd9419f12bc801ba14407 Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Thu, 10 Mar 2016 16:44:55 -0600
|
||||
Subject: [PATCH 10/37] spi: qup: properly detect extra interrupts
|
||||
Subject: [PATCH 07/69] spi: qup: properly detect extra interrupts
|
||||
|
||||
It's possible for a SPI transaction to complete and get another
|
||||
interrupt and have it processed on the same spi_transfer before the
|
||||
|
@ -13,7 +13,7 @@ properly detect these bad interrupts and print warning messages.
|
|||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
drivers/spi/spi-qup.c | 15 +++++++++------
|
||||
drivers/spi/spi-qup.c | 15 +++++++++------
|
||||
1 file changed, 9 insertions(+), 6 deletions(-)
|
||||
|
||||
--- a/drivers/spi/spi-qup.c
|
|
@ -1,7 +1,7 @@
|
|||
From ba96e9449a63acd658d8ad0a5b3755b559410999 Mon Sep 17 00:00:00 2001
|
||||
From 9864f39695aefe0831b3c6e86c0dff30489ad580 Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Thu, 10 Mar 2016 16:48:27 -0600
|
||||
Subject: [PATCH 11/37] spi: qup: don't re-read opflags to see if transaction
|
||||
Subject: [PATCH 08/69] spi: qup: don't re-read opflags to see if transaction
|
||||
is done for reads
|
||||
|
||||
For reads, we will get another interrupt so we need to handle things
|
||||
|
@ -9,7 +9,7 @@ then. For writes, we can finish up now.
|
|||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
drivers/spi/spi-qup.c | 3 ++-
|
||||
drivers/spi/spi-qup.c | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/spi/spi-qup.c
|
|
@ -1,7 +1,7 @@
|
|||
From ef00ad56d728618203358d9eba7ca8e7eb48e701 Mon Sep 17 00:00:00 2001
|
||||
From e06f04d55752e460d8f332f28317aebc27ab1b17 Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Tue, 26 Apr 2016 12:57:46 -0500
|
||||
Subject: [PATCH 12/37] spi: qup: refactor spi_qup_io_config in two functions
|
||||
Subject: [PATCH 09/69] spi: qup: refactor spi_qup_io_config in two functions
|
||||
|
||||
This is preparation for handling transactions larger than 64K-1 bytes in
|
||||
block mode which is currently unsupported quietly fails.
|
||||
|
@ -13,7 +13,7 @@ This is just refactoring, there should be no functional change
|
|||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
drivers/spi/spi-qup.c | 141 ++++++++++++++++++++++++++++++-------------------
|
||||
drivers/spi/spi-qup.c | 141 ++++++++++++++++++++++++++++++--------------------
|
||||
1 file changed, 86 insertions(+), 55 deletions(-)
|
||||
|
||||
--- a/drivers/spi/spi-qup.c
|
|
@ -1,7 +1,7 @@
|
|||
From 9263d98e255e1d51b41c752d53e39877728a9419 Mon Sep 17 00:00:00 2001
|
||||
From afe108e638a2dd441b11cd2c7b1e0658bb47b5e8 Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Tue, 26 Apr 2016 13:14:45 -0500
|
||||
Subject: [PATCH 13/37] spi: qup: call io_config in mode specific function
|
||||
Subject: [PATCH 10/69] spi: qup: call io_config in mode specific function
|
||||
|
||||
DMA transactions should only only need to call io_config only once, but
|
||||
block mode might call it several times to setup several transactions so
|
||||
|
@ -12,7 +12,7 @@ This is just refactoring, there should be no functional change
|
|||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
drivers/spi/spi-qup.c | 327 +++++++++++++++++++++++++------------------------
|
||||
drivers/spi/spi-qup.c | 327 +++++++++++++++++++++++++-------------------------
|
||||
1 file changed, 166 insertions(+), 161 deletions(-)
|
||||
|
||||
--- a/drivers/spi/spi-qup.c
|
|
@ -1,7 +1,7 @@
|
|||
From 05a08cc5620df0fcf8e260feee04b9671705723e Mon Sep 17 00:00:00 2001
|
||||
From 6858a6a75f1ed364764afba938d77bbb57f80559 Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Tue, 26 Apr 2016 15:46:24 -0500
|
||||
Subject: [PATCH 14/37] spi: qup: allow block mode to generate multiple
|
||||
Subject: [PATCH 11/69] spi: qup: allow block mode to generate multiple
|
||||
transactions
|
||||
|
||||
This let's you write more to the SPI bus than 64K-1 which is important
|
||||
|
@ -13,7 +13,7 @@ transactions
|
|||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
drivers/spi/spi-qup.c | 120 ++++++++++++++++++++++++++++++-------------------
|
||||
drivers/spi/spi-qup.c | 120 +++++++++++++++++++++++++++++++-------------------
|
||||
1 file changed, 75 insertions(+), 45 deletions(-)
|
||||
|
||||
--- a/drivers/spi/spi-qup.c
|
|
@ -1,7 +1,7 @@
|
|||
From a24914d34a4c6df4323c6d98950166600da79bc6 Mon Sep 17 00:00:00 2001
|
||||
From fca27bd516d30e33b9373a8c61ca4431077e479e Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Wed, 4 May 2016 16:33:42 -0500
|
||||
Subject: [PATCH 15/37] spi: qup: refactor spi_qup_prep_sg to be more take
|
||||
Subject: [PATCH 12/69] spi: qup: refactor spi_qup_prep_sg to be more take
|
||||
specific sgl and nent
|
||||
|
||||
This is in preparation for splitting DMA into multiple transacations,
|
||||
|
@ -9,7 +9,7 @@ this contains no code changes just refactoring.
|
|||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
drivers/spi/spi-qup.c | 28 +++++++++++-----------------
|
||||
drivers/spi/spi-qup.c | 28 +++++++++++-----------------
|
||||
1 file changed, 11 insertions(+), 17 deletions(-)
|
||||
|
||||
--- a/drivers/spi/spi-qup.c
|
|
@ -1,14 +1,14 @@
|
|||
From 6b2bb8803f19116bad41a271f9035d4c853f4553 Mon Sep 17 00:00:00 2001
|
||||
From 028f915b20ec343dda88f1bcc99f07f6b428b4aa Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Thu, 5 May 2016 10:07:11 -0500
|
||||
Subject: [PATCH 16/37] spi: qup: allow mulitple DMA transactions per spi xfer
|
||||
Subject: [PATCH 13/69] spi: qup: allow mulitple DMA transactions per spi xfer
|
||||
|
||||
Much like the block mode changes, we are breaking up DMA transactions
|
||||
into 64K chunks so we can reset the QUP engine.
|
||||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
drivers/spi/spi-qup.c | 120 +++++++++++++++++++++++++++++++++++--------------
|
||||
drivers/spi/spi-qup.c | 120 ++++++++++++++++++++++++++++++++++++--------------
|
||||
1 file changed, 86 insertions(+), 34 deletions(-)
|
||||
|
||||
--- a/drivers/spi/spi-qup.c
|
|
@ -1,7 +1,7 @@
|
|||
From 5ffa559b35dd90469e1f7fc21a77c6a2d5a8ca0f Mon Sep 17 00:00:00 2001
|
||||
From f5913e137c3dac4972ac0ddd5f248924d02d3dcb Mon Sep 17 00:00:00 2001
|
||||
From: Varadarajan Narayanan <varada@codeaurora.org>
|
||||
Date: Wed, 25 May 2016 13:40:03 +0530
|
||||
Subject: [PATCH 17/37] spi: qup: Fix sg nents calculation
|
||||
Subject: [PATCH 14/69] spi: qup: Fix sg nents calculation
|
||||
|
||||
lib/scatterlist.c:sg_nents_for_len() returns the number of SG
|
||||
entries that total up to greater than or equal to the given
|
||||
|
@ -20,7 +20,7 @@ timing out.
|
|||
|
||||
Signed-off-by: Varadarajan Narayanan <varada@codeaurora.org>
|
||||
---
|
||||
drivers/spi/spi-qup.c | 38 ++++++++++++++++++++++++++++++++++++--
|
||||
drivers/spi/spi-qup.c | 38 ++++++++++++++++++++++++++++++++++++--
|
||||
1 file changed, 36 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/spi/spi-qup.c
|
|
@ -1,8 +1,7 @@
|
|||
From 94aa51061597d9db86f3ad4d54eb4a560fa66f2f Mon Sep 17 00:00:00 2001
|
||||
From 5543e93f51d5e23f9b3a7fe11a722c91fc410485 Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Wed, 13 Apr 2016 14:03:14 -0500
|
||||
Subject: [PATCH 26/37] cpufreq: dt: qcom: ipq4019: Add compat for qcom
|
||||
ipq4019
|
||||
Subject: [PATCH 15/69] cpufreq: dt: qcom: ipq4019: Add compat for qcom ipq4019
|
||||
|
||||
Instantiate cpufreq-dt-platdev driver for ipq4019 to support changing
|
||||
CPU frequencies.
|
||||
|
@ -12,7 +11,7 @@ http://comments.gmane.org/gmane.linux.power-management.general/73887
|
|||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
drivers/cpufreq/cpufreq-dt-platdev.c | 2 ++
|
||||
drivers/cpufreq/cpufreq-dt-platdev.c | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
--- a/drivers/cpufreq/cpufreq-dt-platdev.c
|
|
@ -1,14 +1,14 @@
|
|||
From 4f0328557a670d481f2609474b56890c28ab4694 Mon Sep 17 00:00:00 2001
|
||||
From 5e2df5f44e35d79fff2ab8bbb8a726ad5de78a3e Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@qca.qualcomm.com>
|
||||
Date: Thu, 28 Apr 2016 12:55:08 -0500
|
||||
Subject: [PATCH 27/37] clk: ipq4019: report accurate fixed clock rates
|
||||
Subject: [PATCH 16/69] clk: ipq4019: report accurate fixed clock rates
|
||||
|
||||
This looks like a copy-and-paste gone wrong, but update all
|
||||
the fixed clock rates to report the correct values.
|
||||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@qca.qualcomm.com>
|
||||
---
|
||||
drivers/clk/qcom/gcc-ipq4019.c | 10 +++++-----
|
||||
drivers/clk/qcom/gcc-ipq4019.c | 10 +++++-----
|
||||
1 file changed, 5 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/drivers/clk/qcom/gcc-ipq4019.c
|
|
@ -1,14 +1,14 @@
|
|||
From ce6cb947b9535b2d81717925cb6a3bc9f8500f44 Mon Sep 17 00:00:00 2001
|
||||
From 18c3b42575a154343831aec0637aab00e19440e1 Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Thu, 17 Mar 2016 15:01:09 -0500
|
||||
Subject: [PATCH 28/37] qcom: ipq4019: add cpu operating points for cpufreq
|
||||
Subject: [PATCH 17/69] qcom: ipq4019: add cpu operating points for cpufreq
|
||||
support
|
||||
|
||||
This adds some operating points for cpu frequeny scaling
|
||||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
arch/arm/boot/dts/qcom-ipq4019.dtsi | 34 ++++++++++++++++++++++++++--------
|
||||
arch/arm/boot/dts/qcom-ipq4019.dtsi | 34 ++++++++++++++++++++++++++--------
|
||||
1 file changed, 26 insertions(+), 8 deletions(-)
|
||||
|
||||
--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
|
|
@ -1,13 +1,13 @@
|
|||
From f033e344b8a02e98beaf16c1eb188bc175219756 Mon Sep 17 00:00:00 2001
|
||||
From 71f82049dca86bc89b9da07e051e4ed492820233 Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Mon, 28 Mar 2016 11:16:51 -0500
|
||||
Subject: [PATCH 29/37] qcom: ipq4019: turn on DMA for i2c
|
||||
Subject: [PATCH 18/69] qcom: ipq4019: turn on DMA for i2c
|
||||
|
||||
These are the required nodes for i2c-qup to use DMA
|
||||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
arch/arm/boot/dts/qcom-ipq4019.dtsi | 2 ++
|
||||
arch/arm/boot/dts/qcom-ipq4019.dtsi | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
|
|
@ -1,7 +1,7 @@
|
|||
From 79f698655871f2eeceb1f9051e60d97bc434f52f Mon Sep 17 00:00:00 2001
|
||||
From 7292bf171cdf2fb48607058f12ddd0d812a87428 Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@qca.qualcomm.com>
|
||||
Date: Fri, 29 Apr 2016 12:48:02 -0500
|
||||
Subject: [PATCH 30/37] qcom: ipq4019: use correct clock for i2c bus 0
|
||||
Subject: [PATCH 19/69] qcom: ipq4019: use correct clock for i2c bus 0
|
||||
|
||||
For the record the mapping is as follows:
|
||||
|
||||
|
@ -12,7 +12,7 @@ QUP3 = I2C QUP2
|
|||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@qca.qualcomm.com>
|
||||
---
|
||||
arch/arm/boot/dts/qcom-ipq4019.dtsi | 2 +-
|
||||
arch/arm/boot/dts/qcom-ipq4019.dtsi | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
|
|
@ -1,13 +1,13 @@
|
|||
From 20249a0d746265a6663208fa768614784553edac Mon Sep 17 00:00:00 2001
|
||||
From 4593e768393b9589f0a8987eaf57316c214865fe Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Mon, 11 Apr 2016 14:49:12 -0500
|
||||
Subject: [PATCH 31/37] qcom: ipq4019: enable DMA for spi
|
||||
Subject: [PATCH 20/69] qcom: ipq4019: enable DMA for spi
|
||||
|
||||
These are the required nodes for spi-qup to use DMA
|
||||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
arch/arm/boot/dts/qcom-ipq4019.dtsi | 2 ++
|
||||
arch/arm/boot/dts/qcom-ipq4019.dtsi | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
|
|
@ -1,14 +1,14 @@
|
|||
From 4d7fe4171b01cbfc01e4a00f44b3e7f7a8013eb3 Mon Sep 17 00:00:00 2001
|
||||
From 644ad7209637b02a0ca6d72f0715a9f52532fc70 Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Fri, 8 Apr 2016 15:26:10 -0500
|
||||
Subject: [PATCH 32/37] qcom: ipq4019: use v2 of the kpss bringup mechanism
|
||||
Subject: [PATCH 21/69] qcom: ipq4019: use v2 of the kpss bringup mechanism
|
||||
|
||||
v1 was the incorrect choice here and sometimes the board would not come
|
||||
up properly
|
||||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
arch/arm/boot/dts/qcom-ipq4019.dtsi | 32 ++++++++++++++++++++++++--------
|
||||
arch/arm/boot/dts/qcom-ipq4019.dtsi | 32 ++++++++++++++++++++++++--------
|
||||
1 file changed, 24 insertions(+), 8 deletions(-)
|
||||
|
||||
--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
|
|
@ -1,14 +1,14 @@
|
|||
From 25ee3761c072037b48246b992c3aec671c77a406 Mon Sep 17 00:00:00 2001
|
||||
From 47f00399b195e0987c67006b329587bef0692bf4 Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Pedersen <twp@codeaurora.org>
|
||||
Date: Wed, 4 May 2016 12:25:41 -0700
|
||||
Subject: [PATCH 33/37] dts: ipq4019: support ARMv7 PMU
|
||||
Subject: [PATCH 22/69] dts: ipq4019: support ARMv7 PMU
|
||||
|
||||
Add support for cortex-a7-pmu present on ipq4019 SoCs.
|
||||
|
||||
Signed-off-by: Thomas Pedersen <twp@codeaurora.org>
|
||||
Signed-off-by: Matthew McClintock <mmcclint@qca.qualcomm.com>
|
||||
---
|
||||
arch/arm/boot/dts/qcom-ipq4019.dtsi | 6 ++++++
|
||||
arch/arm/boot/dts/qcom-ipq4019.dtsi | 6 ++++++
|
||||
1 file changed, 6 insertions(+)
|
||||
|
||||
--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
|
|
@ -1,17 +1,17 @@
|
|||
From e8d6fd46f5f3c5860fa7fa98004de9bd97c0d869 Mon Sep 17 00:00:00 2001
|
||||
From 04d3f9be0ce80fac99d4ca1f46faf3605258ca1f Mon Sep 17 00:00:00 2001
|
||||
From: Senthilkumar N L <snlakshm@codeaurora.org>
|
||||
Date: Tue, 6 Jan 2015 12:52:23 +0530
|
||||
Subject: [PATCH 34/37] qcom: ipq4019: Add IPQ4019 USB HS/SS PHY drivers
|
||||
Subject: [PATCH 23/69] qcom: ipq4019: Add IPQ4019 USB HS/SS PHY drivers
|
||||
|
||||
These drivers handles control and configuration of the HS
|
||||
and SS USB PHY transceivers.
|
||||
|
||||
Signed-off-by: Senthilkumar N L <snlakshm@codeaurora.org>
|
||||
---
|
||||
drivers/usb/phy/Kconfig | 11 ++
|
||||
drivers/usb/phy/Makefile | 2 +
|
||||
drivers/usb/phy/phy-qca-baldur.c | 262 ++++++++++++++++++++++++++++++++++++++
|
||||
drivers/usb/phy/phy-qca-uniphy.c | 171 +++++++++++++++++++++++++
|
||||
drivers/usb/phy/Kconfig | 11 ++
|
||||
drivers/usb/phy/Makefile | 2 +
|
||||
drivers/usb/phy/phy-qca-baldur.c | 262 +++++++++++++++++++++++++++++++++++++++
|
||||
drivers/usb/phy/phy-qca-uniphy.c | 171 +++++++++++++++++++++++++
|
||||
4 files changed, 446 insertions(+)
|
||||
create mode 100644 drivers/usb/phy/phy-qca-baldur.c
|
||||
create mode 100644 drivers/usb/phy/phy-qca-uniphy.c
|
|
@ -1,14 +1,13 @@
|
|||
From d2ed553484fecdf02fa53bf431599412348afa95 Mon Sep 17 00:00:00 2001
|
||||
From 0fba6eceb6e16fa8fd5834d65fcb771fa263a44b Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Thu, 17 Mar 2016 16:22:28 -0500
|
||||
Subject: [PATCH 35/37] qcom: ipq4019: add USB nodes to ipq4019 SoC device
|
||||
tree
|
||||
Subject: [PATCH 24/69] qcom: ipq4019: add USB nodes to ipq4019 SoC device tree
|
||||
|
||||
This adds the SoC nodes to the ipq4019 device tree
|
||||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
arch/arm/boot/dts/qcom-ipq4019.dtsi | 67 +++++++++++++++++++++++++++++++++++
|
||||
arch/arm/boot/dts/qcom-ipq4019.dtsi | 67 +++++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 67 insertions(+)
|
||||
|
||||
--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
|
|
@ -1,14 +1,14 @@
|
|||
From c15caf58ff5c3b4093fa388b9c6228bb3c9c5413 Mon Sep 17 00:00:00 2001
|
||||
From ae5e11c926f710595d0080e51bd10e704776669d Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Mon, 21 Mar 2016 16:12:05 -0500
|
||||
Subject: [PATCH 36/37] qcom: ipq4019: enable USB bus for DK01.1 board
|
||||
Subject: [PATCH 25/69] qcom: ipq4019: enable USB bus for DK01.1 board
|
||||
|
||||
This enables the USB block
|
||||
|
||||
Change-Id: I384dd1874bba341713f384cf6199abd446e3f3c0
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi | 24 ++++++++++++++++++++++++
|
||||
arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi | 24 ++++++++++++++++++++++++
|
||||
1 file changed, 24 insertions(+)
|
||||
|
||||
--- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi
|
|
@ -1,7 +1,7 @@
|
|||
From ff1ecc5bfc11377e82894d05aa45a92657ef8a06 Mon Sep 17 00:00:00 2001
|
||||
From ec3e465ecf3f7dd26f2e22170e4c5f4b9979df5d Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
Date: Mon, 21 Mar 2016 15:55:21 -0500
|
||||
Subject: [PATCH 37/37] dts: ipq4019: Add support for IPQ4019 DK04 board
|
||||
Subject: [PATCH 26/69] dts: ipq4019: Add support for IPQ4019 DK04 board
|
||||
|
||||
This is pretty similiar to a DK01 but has a bit more IO. Some notable
|
||||
differences are listed below however they are not in the device tree yet
|
||||
|
@ -14,10 +14,10 @@ as we continue adding more support
|
|||
|
||||
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||
---
|
||||
arch/arm/boot/dts/Makefile | 1 +
|
||||
arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi | 12 +-
|
||||
arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1-c1.dts | 21 +++
|
||||
arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi | 163 +++++++++++++++++++++++
|
||||
arch/arm/boot/dts/Makefile | 1 +
|
||||
arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi | 12 +-
|
||||
arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1-c1.dts | 21 +++
|
||||
arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi | 163 ++++++++++++++++++++++++
|
||||
4 files changed, 189 insertions(+), 8 deletions(-)
|
||||
create mode 100644 arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1-c1.dts
|
||||
create mode 100644 arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
|
|
@ -1,17 +1,7 @@
|
|||
From patchwork Wed Nov 2 15:56:56 2016
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
MIME-Version: 1.0
|
||||
Content-Transfer-Encoding: 7bit
|
||||
Subject: [v9,1/3] clk: qcom: Add support for SMD-RPM Clocks
|
||||
From 41ee71bae788e1858c0a387d010c342e6bb3f4b0 Mon Sep 17 00:00:00 2001
|
||||
From: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
X-Patchwork-Id: 9409419
|
||||
Message-Id: <20161102155658.32203-2-georgi.djakov@linaro.org>
|
||||
To: sboyd@codeaurora.org, mturquette@baylibre.com
|
||||
Cc: linux-clk@vger.kernel.org, devicetree@vger.kernel.org,
|
||||
robh+dt@kernel.org, mark.rutland@arm.com,
|
||||
linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
|
||||
georgi.djakov@linaro.org
|
||||
Date: Wed, 2 Nov 2016 17:56:56 +0200
|
||||
Date: Wed, 2 Nov 2016 17:56:56 +0200
|
||||
Subject: [PATCH 27/69] clk: qcom: Add support for SMD-RPM Clocks
|
||||
|
||||
This adds initial support for clocks controlled by the Resource
|
||||
Power Manager (RPM) processor on some Qualcomm SoCs, which use
|
||||
|
@ -39,11 +29,6 @@ Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
|
|||
create mode 100644 drivers/clk/qcom/clk-smd-rpm.c
|
||||
create mode 100644 include/dt-bindings/clock/qcom,rpmcc.h
|
||||
|
||||
--
|
||||
To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
|
||||
the body of a message to majordomo@vger.kernel.org
|
||||
More majordomo info at http://vger.kernel.org/majordomo-info.html
|
||||
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
|
||||
@@ -0,0 +1,36 @@
|
|
@ -1,7 +1,7 @@
|
|||
From 872f91b5ea720c72f81fb46d353c43ecb3263ffa Mon Sep 17 00:00:00 2001
|
||||
From 21e7116c9d639f3283d4cec286fed1e703832b43 Mon Sep 17 00:00:00 2001
|
||||
From: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
Date: Wed, 2 Nov 2016 17:56:57 +0200
|
||||
Subject: clk: qcom: Add support for RPM Clocks
|
||||
Subject: [PATCH 28/69] clk: qcom: Add support for RPM Clocks
|
||||
|
||||
This adds initial support for clocks controlled by the Resource
|
||||
Power Manager (RPM) processor on some Qualcomm SoCs, which use
|
||||
|
@ -54,11 +54,12 @@ Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
|||
depends on COMMON_CLK_QCOM && QCOM_SMD_RPM
|
||||
--- a/drivers/clk/qcom/Makefile
|
||||
+++ b/drivers/clk/qcom/Makefile
|
||||
@@ -30,3 +30,4 @@ obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8
|
||||
@@ -29,4 +29,5 @@ obj-$(CONFIG_MSM_LCC_8960) += lcc-msm896
|
||||
obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
|
||||
obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
|
||||
obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o
|
||||
obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
|
||||
+obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o
|
||||
obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/clk/qcom/clk-rpm.c
|
||||
@@ -0,0 +1,489 @@
|
|
@ -1,7 +1,7 @@
|
|||
From c260524aba53b57f72b5446ed553080df30b4d09 Mon Sep 17 00:00:00 2001
|
||||
From 7028c21deb2c6205bb896cc3719414de3d6d6a6e Mon Sep 17 00:00:00 2001
|
||||
From: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
Date: Wed, 23 Nov 2016 16:52:49 +0200
|
||||
Subject: clk: qcom: clk-rpm: Fix clk_hw references
|
||||
Subject: [PATCH 29/69] clk: qcom: clk-rpm: Fix clk_hw references
|
||||
|
||||
Fix the clk_hw references to the actual clocks and add a xlate function
|
||||
to return the hw pointers from the already existing static array.
|
|
@ -0,0 +1,40 @@
|
|||
From 0c974b87829e007dc4fae94e20d488204e20e662 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <john@phrozen.org>
|
||||
Date: Thu, 9 Mar 2017 08:16:10 +0100
|
||||
Subject: [PATCH 30/69] clk: Disable i2c device on gsbi4
|
||||
|
||||
This patch was not annotated and comes from the v4.4 tree.
|
||||
|
||||
Signed-off-by: John Crispin <john@phrozen.org>
|
||||
---
|
||||
drivers/clk/qcom/gcc-ipq806x.c | 5 +++--
|
||||
1 file changed, 3 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/clk/qcom/gcc-ipq806x.c
|
||||
+++ b/drivers/clk/qcom/gcc-ipq806x.c
|
||||
@@ -294,7 +294,7 @@ static struct clk_rcg gsbi1_uart_src = {
|
||||
.parent_names = gcc_pxo_pll8,
|
||||
.num_parents = 2,
|
||||
.ops = &clk_rcg_ops,
|
||||
- .flags = CLK_SET_PARENT_GATE,
|
||||
+ .flags = CLK_SET_PARENT_GATE | CLK_IGNORE_UNUSED,
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -312,7 +312,7 @@ static struct clk_branch gsbi1_uart_clk
|
||||
},
|
||||
.num_parents = 1,
|
||||
.ops = &clk_branch_ops,
|
||||
- .flags = CLK_SET_RATE_PARENT,
|
||||
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -890,6 +890,7 @@ static struct clk_branch gsbi1_h_clk = {
|
||||
.hw.init = &(struct clk_init_data){
|
||||
.name = "gsbi1_h_clk",
|
||||
.ops = &clk_branch_ops,
|
||||
++ .flags = CLK_IGNORE_UNUSED,
|
||||
},
|
||||
},
|
||||
};
|
|
@ -0,0 +1,275 @@
|
|||
From d8eeb4de90e968ba32d956728c866f20752cf2c3 Mon Sep 17 00:00:00 2001
|
||||
From: Mathieu Olivari <mathieu@codeaurora.org>
|
||||
Date: Thu, 9 Mar 2017 08:18:08 +0100
|
||||
Subject: [PATCH 31/69] mtd: add SMEM parser for QCOM platforms
|
||||
|
||||
On QCOM platforms using MTD devices storage (such as IPQ806x), SMEM is
|
||||
used to store partition layout. This new parser can now be used to read
|
||||
SMEM and use it to register an MTD layout according to its content.
|
||||
|
||||
Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
|
||||
Signed-off-by: Ram Chandra Jangir <rjangi@codeaurora.org>
|
||||
---
|
||||
drivers/mtd/Kconfig | 7 ++
|
||||
drivers/mtd/Makefile | 1 +
|
||||
drivers/mtd/qcom_smem_part.c | 228 +++++++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 236 insertions(+)
|
||||
create mode 100644 drivers/mtd/qcom_smem_part.c
|
||||
|
||||
--- a/drivers/mtd/Kconfig
|
||||
+++ b/drivers/mtd/Kconfig
|
||||
@@ -190,6 +190,13 @@ config MTD_MYLOADER_PARTS
|
||||
You will still need the parsing functions to be called by the driver
|
||||
for your particular device. It won't happen automatically.
|
||||
|
||||
+config MTD_QCOM_SMEM_PARTS
|
||||
+ tristate "QCOM SMEM partitioning support"
|
||||
+ depends on QCOM_SMEM
|
||||
+ help
|
||||
+ This provides partitions parser for QCOM devices using SMEM
|
||||
+ such as IPQ806x.
|
||||
+
|
||||
comment "User Modules And Translation Layers"
|
||||
|
||||
#
|
||||
--- /dev/null
|
||||
+++ b/drivers/mtd/qcom_smem_part.c
|
||||
@@ -0,0 +1,228 @@
|
||||
+/*
|
||||
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License version 2 and
|
||||
+ * only version 2 as published by the Free Software Foundation.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/device.h>
|
||||
+#include <linux/slab.h>
|
||||
+
|
||||
+#include <linux/mtd/mtd.h>
|
||||
+#include <linux/mtd/partitions.h>
|
||||
+#include <linux/spi/spi.h>
|
||||
+#include <linux/module.h>
|
||||
+
|
||||
+#include <linux/soc/qcom/smem.h>
|
||||
+
|
||||
+/* Processor/host identifier for the application processor */
|
||||
+#define SMEM_HOST_APPS 0
|
||||
+
|
||||
+/* SMEM items index */
|
||||
+#define SMEM_AARM_PARTITION_TABLE 9
|
||||
+#define SMEM_BOOT_FLASH_TYPE 421
|
||||
+#define SMEM_BOOT_FLASH_BLOCK_SIZE 424
|
||||
+
|
||||
+/* SMEM Flash types */
|
||||
+#define SMEM_FLASH_NAND 2
|
||||
+#define SMEM_FLASH_SPI 6
|
||||
+
|
||||
+#define SMEM_PART_NAME_SZ 16
|
||||
+#define SMEM_PARTS_MAX 32
|
||||
+
|
||||
+struct smem_partition {
|
||||
+ char name[SMEM_PART_NAME_SZ];
|
||||
+ __le32 start;
|
||||
+ __le32 size;
|
||||
+ __le32 attr;
|
||||
+};
|
||||
+
|
||||
+struct smem_partition_table {
|
||||
+ u8 magic[8];
|
||||
+ __le32 version;
|
||||
+ __le32 len;
|
||||
+ struct smem_partition parts[SMEM_PARTS_MAX];
|
||||
+};
|
||||
+
|
||||
+/* SMEM Magic values in partition table */
|
||||
+static const u8 SMEM_PTABLE_MAGIC[] = {
|
||||
+ 0xaa, 0x73, 0xee, 0x55,
|
||||
+ 0xdb, 0xbd, 0x5e, 0xe3,
|
||||
+};
|
||||
+
|
||||
+static int qcom_smem_get_flash_blksz(u64 **smem_blksz)
|
||||
+{
|
||||
+ size_t size;
|
||||
+
|
||||
+ *smem_blksz = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_BLOCK_SIZE,
|
||||
+ &size);
|
||||
+
|
||||
+ if (IS_ERR(*smem_blksz)) {
|
||||
+ pr_err("Unable to read flash blksz from SMEM\n");
|
||||
+ return -ENOENT;
|
||||
+ }
|
||||
+
|
||||
+ if (size != sizeof(**smem_blksz)) {
|
||||
+ pr_err("Invalid flash blksz size in SMEM\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qcom_smem_get_flash_type(u64 **smem_flash_type)
|
||||
+{
|
||||
+ size_t size;
|
||||
+
|
||||
+ *smem_flash_type = qcom_smem_get(SMEM_HOST_APPS, SMEM_BOOT_FLASH_TYPE,
|
||||
+ &size);
|
||||
+
|
||||
+ if (IS_ERR(*smem_flash_type)) {
|
||||
+ pr_err("Unable to read flash type from SMEM\n");
|
||||
+ return -ENOENT;
|
||||
+ }
|
||||
+
|
||||
+ if (size != sizeof(**smem_flash_type)) {
|
||||
+ pr_err("Invalid flash type size in SMEM\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qcom_smem_get_flash_partitions(struct smem_partition_table **pparts)
|
||||
+{
|
||||
+ size_t size;
|
||||
+
|
||||
+ *pparts = qcom_smem_get(SMEM_HOST_APPS, SMEM_AARM_PARTITION_TABLE,
|
||||
+ &size);
|
||||
+
|
||||
+ if (IS_ERR(*pparts)) {
|
||||
+ pr_err("Unable to read partition table from SMEM\n");
|
||||
+ return -ENOENT;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int of_dev_node_match(struct device *dev, void *data)
|
||||
+{
|
||||
+ return dev->of_node == data;
|
||||
+}
|
||||
+
|
||||
+static bool is_spi_device(struct device_node *np)
|
||||
+{
|
||||
+ struct device *dev;
|
||||
+
|
||||
+ dev = bus_find_device(&spi_bus_type, NULL, np, of_dev_node_match);
|
||||
+ if (!dev)
|
||||
+ return false;
|
||||
+
|
||||
+ put_device(dev);
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
+static int parse_qcom_smem_partitions(struct mtd_info *master,
|
||||
+ const struct mtd_partition **pparts,
|
||||
+ struct mtd_part_parser_data *data)
|
||||
+{
|
||||
+ struct smem_partition_table *smem_parts;
|
||||
+ u64 *smem_flash_type, *smem_blksz;
|
||||
+ struct mtd_partition *mtd_parts;
|
||||
+ struct device_node *of_node = master->dev.of_node;
|
||||
+ int i, ret;
|
||||
+
|
||||
+ /*
|
||||
+ * SMEM will only store the partition table of the boot device.
|
||||
+ * If this is not the boot device, do not return any partition.
|
||||
+ */
|
||||
+ ret = qcom_smem_get_flash_type(&smem_flash_type);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ if ((*smem_flash_type == SMEM_FLASH_NAND && !mtd_type_is_nand(master))
|
||||
+ || (*smem_flash_type == SMEM_FLASH_SPI && !is_spi_device(of_node)))
|
||||
+ return 0;
|
||||
+
|
||||
+ /*
|
||||
+ * Just for sanity purpose, make sure the block size in SMEM matches the
|
||||
+ * block size of the MTD device
|
||||
+ */
|
||||
+ ret = qcom_smem_get_flash_blksz(&smem_blksz);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ if (*smem_blksz != master->erasesize) {
|
||||
+ pr_err("SMEM block size differs from MTD block size\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ /* Get partition pointer from SMEM */
|
||||
+ ret = qcom_smem_get_flash_partitions(&smem_parts);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ if (memcmp(SMEM_PTABLE_MAGIC, smem_parts->magic,
|
||||
+ sizeof(SMEM_PTABLE_MAGIC))) {
|
||||
+ pr_err("SMEM partition magic invalid\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ /* Allocate and populate the mtd structures */
|
||||
+ mtd_parts = kcalloc(le32_to_cpu(smem_parts->len), sizeof(*mtd_parts),
|
||||
+ GFP_KERNEL);
|
||||
+ if (!mtd_parts)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ for (i = 0; i < smem_parts->len; i++) {
|
||||
+ struct smem_partition *s_part = &smem_parts->parts[i];
|
||||
+ struct mtd_partition *m_part = &mtd_parts[i];
|
||||
+
|
||||
+ m_part->name = s_part->name;
|
||||
+ m_part->size = le32_to_cpu(s_part->size) * (*smem_blksz);
|
||||
+ m_part->offset = le32_to_cpu(s_part->start) * (*smem_blksz);
|
||||
+
|
||||
+ /*
|
||||
+ * The last SMEM partition may have its size marked as
|
||||
+ * something like 0xffffffff, which means "until the end of the
|
||||
+ * flash device". In this case, truncate it.
|
||||
+ */
|
||||
+ if (m_part->offset + m_part->size > master->size)
|
||||
+ m_part->size = master->size - m_part->offset;
|
||||
+ }
|
||||
+
|
||||
+ *pparts = mtd_parts;
|
||||
+
|
||||
+ return smem_parts->len;
|
||||
+}
|
||||
+
|
||||
+static struct mtd_part_parser qcom_smem_parser = {
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .parse_fn = parse_qcom_smem_partitions,
|
||||
+ .name = "qcom-smem",
|
||||
+};
|
||||
+
|
||||
+static int __init qcom_smem_parser_init(void)
|
||||
+{
|
||||
+ register_mtd_parser(&qcom_smem_parser);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void __exit qcom_smem_parser_exit(void)
|
||||
+{
|
||||
+ deregister_mtd_parser(&qcom_smem_parser);
|
||||
+}
|
||||
+
|
||||
+module_init(qcom_smem_parser_init);
|
||||
+module_exit(qcom_smem_parser_exit);
|
||||
+
|
||||
+MODULE_LICENSE("GPL");
|
||||
+MODULE_AUTHOR("Mathieu Olivari <mathieu@codeaurora.org>");
|
||||
+MODULE_DESCRIPTION("Parsing code for SMEM based partition tables");
|
||||
--- a/drivers/mtd/Makefile
|
||||
+++ b/drivers/mtd/Makefile
|
||||
@@ -16,6 +16,7 @@ obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o
|
||||
obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o
|
||||
obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o
|
||||
obj-$(CONFIG_MTD_MYLOADER_PARTS) += myloader.o
|
||||
+obj-$(CONFIG_MTD_QCOM_SMEM_PARTS) += qcom_smem_part.o
|
||||
|
||||
# 'Users' - code which presents functionality to userspace.
|
||||
obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o
|
|
@ -0,0 +1,526 @@
|
|||
From b9004f4fd23e4c614d71c972f3a9311665480e29 Mon Sep 17 00:00:00 2001
|
||||
From: Andy Gross <agross@codeaurora.org>
|
||||
Date: Thu, 9 Mar 2017 08:19:18 +0100
|
||||
Subject: [PATCH 32/69] phy: add qcom dwc3 phy
|
||||
|
||||
Signed-off-by: Andy Gross <agross@codeaurora.org>
|
||||
---
|
||||
drivers/phy/Kconfig | 12 ++
|
||||
drivers/phy/Makefile | 1 +
|
||||
drivers/phy/phy-qcom-dwc3.c | 484 ++++++++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 497 insertions(+)
|
||||
create mode 100644 drivers/phy/phy-qcom-dwc3.c
|
||||
|
||||
--- a/drivers/phy/Kconfig
|
||||
+++ b/drivers/phy/Kconfig
|
||||
@@ -489,4 +489,16 @@ config PHY_NS2_PCIE
|
||||
help
|
||||
Enable this to support the Broadcom Northstar2 PCIe PHY.
|
||||
If unsure, say N.
|
||||
+
|
||||
+config PHY_QCOM_DWC3
|
||||
+ tristate "QCOM DWC3 USB PHY support"
|
||||
+ depends on ARCH_QCOM
|
||||
+ depends on HAS_IOMEM
|
||||
+ depends on OF
|
||||
+ select GENERIC_PHY
|
||||
+ help
|
||||
+ This option enables support for the Synopsis PHYs present inside the
|
||||
+ Qualcomm USB3.0 DWC3 controller. This driver supports both HS and SS
|
||||
+ PHY controllers.
|
||||
+
|
||||
endmenu
|
||||
--- a/drivers/phy/Makefile
|
||||
+++ b/drivers/phy/Makefile
|
||||
@@ -60,3 +60,4 @@ obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-
|
||||
obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o
|
||||
obj-$(CONFIG_ARCH_TEGRA) += tegra/
|
||||
obj-$(CONFIG_PHY_NS2_PCIE) += phy-bcm-ns2-pcie.o
|
||||
+obj-$(CONFIG_PHY_QCOM_DWC3) += phy-qcom-dwc3.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/phy/phy-qcom-dwc3.c
|
||||
@@ -0,0 +1,484 @@
|
||||
+/* Copyright (c) 2014-2015, Code Aurora Forum. All rights reserved.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License version 2 and
|
||||
+ * only version 2 as published by the Free Software Foundation.
|
||||
+ *
|
||||
+* This program is distributed in the hope that it will be useful,
|
||||
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+* GNU General Public License for more details.
|
||||
+*/
|
||||
+
|
||||
+#include <linux/clk.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/phy/phy.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/delay.h>
|
||||
+
|
||||
+/**
|
||||
+ * USB QSCRATCH Hardware registers
|
||||
+ */
|
||||
+#define QSCRATCH_GENERAL_CFG (0x08)
|
||||
+#define HSUSB_PHY_CTRL_REG (0x10)
|
||||
+
|
||||
+/* PHY_CTRL_REG */
|
||||
+#define HSUSB_CTRL_DMSEHV_CLAMP BIT(24)
|
||||
+#define HSUSB_CTRL_USB2_SUSPEND BIT(23)
|
||||
+#define HSUSB_CTRL_UTMI_CLK_EN BIT(21)
|
||||
+#define HSUSB_CTRL_UTMI_OTG_VBUS_VALID BIT(20)
|
||||
+#define HSUSB_CTRL_USE_CLKCORE BIT(18)
|
||||
+#define HSUSB_CTRL_DPSEHV_CLAMP BIT(17)
|
||||
+#define HSUSB_CTRL_COMMONONN BIT(11)
|
||||
+#define HSUSB_CTRL_ID_HV_CLAMP BIT(9)
|
||||
+#define HSUSB_CTRL_OTGSESSVLD_CLAMP BIT(8)
|
||||
+#define HSUSB_CTRL_CLAMP_EN BIT(7)
|
||||
+#define HSUSB_CTRL_RETENABLEN BIT(1)
|
||||
+#define HSUSB_CTRL_POR BIT(0)
|
||||
+
|
||||
+/* QSCRATCH_GENERAL_CFG */
|
||||
+#define HSUSB_GCFG_XHCI_REV BIT(2)
|
||||
+
|
||||
+/**
|
||||
+ * USB QSCRATCH Hardware registers
|
||||
+ */
|
||||
+#define SSUSB_PHY_CTRL_REG (0x00)
|
||||
+#define SSUSB_PHY_PARAM_CTRL_1 (0x04)
|
||||
+#define SSUSB_PHY_PARAM_CTRL_2 (0x08)
|
||||
+#define CR_PROTOCOL_DATA_IN_REG (0x0c)
|
||||
+#define CR_PROTOCOL_DATA_OUT_REG (0x10)
|
||||
+#define CR_PROTOCOL_CAP_ADDR_REG (0x14)
|
||||
+#define CR_PROTOCOL_CAP_DATA_REG (0x18)
|
||||
+#define CR_PROTOCOL_READ_REG (0x1c)
|
||||
+#define CR_PROTOCOL_WRITE_REG (0x20)
|
||||
+
|
||||
+/* PHY_CTRL_REG */
|
||||
+#define SSUSB_CTRL_REF_USE_PAD BIT(28)
|
||||
+#define SSUSB_CTRL_TEST_POWERDOWN BIT(27)
|
||||
+#define SSUSB_CTRL_LANE0_PWR_PRESENT BIT(24)
|
||||
+#define SSUSB_CTRL_SS_PHY_EN BIT(8)
|
||||
+#define SSUSB_CTRL_SS_PHY_RESET BIT(7)
|
||||
+
|
||||
+/* SSPHY control registers */
|
||||
+#define SSPHY_CTRL_RX_OVRD_IN_HI(lane) (0x1006 + 0x100 * lane)
|
||||
+#define SSPHY_CTRL_TX_OVRD_DRV_LO(lane) (0x1002 + 0x100 * lane)
|
||||
+
|
||||
+/* RX OVRD IN HI bits */
|
||||
+#define RX_OVRD_IN_HI_RX_RESET_OVRD BIT(13)
|
||||
+#define RX_OVRD_IN_HI_RX_RX_RESET BIT(12)
|
||||
+#define RX_OVRD_IN_HI_RX_EQ_OVRD BIT(11)
|
||||
+#define RX_OVRD_IN_HI_RX_EQ_MASK 0x0700
|
||||
+#define RX_OVRD_IN_HI_RX_EQ_SHIFT 8
|
||||
+#define RX_OVRD_IN_HI_RX_EQ_EN_OVRD BIT(7)
|
||||
+#define RX_OVRD_IN_HI_RX_EQ_EN BIT(6)
|
||||
+#define RX_OVRD_IN_HI_RX_LOS_FILTER_OVRD BIT(5)
|
||||
+#define RX_OVRD_IN_HI_RX_LOS_FILTER_MASK 0x0018
|
||||
+#define RX_OVRD_IN_HI_RX_RATE_OVRD BIT(2)
|
||||
+#define RX_OVRD_IN_HI_RX_RATE_MASK 0x0003
|
||||
+
|
||||
+/* TX OVRD DRV LO register bits */
|
||||
+#define TX_OVRD_DRV_LO_AMPLITUDE_MASK 0x007F
|
||||
+#define TX_OVRD_DRV_LO_PREEMPH_MASK 0x3F80
|
||||
+#define TX_OVRD_DRV_LO_PREEMPH_SHIFT 7
|
||||
+#define TX_OVRD_DRV_LO_EN BIT(14)
|
||||
+
|
||||
+/* SS CAP register bits */
|
||||
+#define SS_CR_CAP_ADDR_REG BIT(0)
|
||||
+#define SS_CR_CAP_DATA_REG BIT(0)
|
||||
+#define SS_CR_READ_REG BIT(0)
|
||||
+#define SS_CR_WRITE_REG BIT(0)
|
||||
+
|
||||
+struct qcom_dwc3_usb_phy {
|
||||
+ void __iomem *base;
|
||||
+ struct device *dev;
|
||||
+ struct clk *xo_clk;
|
||||
+ struct clk *ref_clk;
|
||||
+};
|
||||
+
|
||||
+struct qcom_dwc3_phy_drvdata {
|
||||
+ struct phy_ops ops;
|
||||
+ u32 clk_rate;
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
+ * Write register and read back masked value to confirm it is written
|
||||
+ *
|
||||
+ * @base - QCOM DWC3 PHY base virtual address.
|
||||
+ * @offset - register offset.
|
||||
+ * @mask - register bitmask specifying what should be updated
|
||||
+ * @val - value to write.
|
||||
+ */
|
||||
+static inline void qcom_dwc3_phy_write_readback(
|
||||
+ struct qcom_dwc3_usb_phy *phy_dwc3, u32 offset,
|
||||
+ const u32 mask, u32 val)
|
||||
+{
|
||||
+ u32 write_val, tmp = readl(phy_dwc3->base + offset);
|
||||
+
|
||||
+ tmp &= ~mask; /* retain other bits */
|
||||
+ write_val = tmp | val;
|
||||
+
|
||||
+ writel(write_val, phy_dwc3->base + offset);
|
||||
+
|
||||
+ /* Read back to see if val was written */
|
||||
+ tmp = readl(phy_dwc3->base + offset);
|
||||
+ tmp &= mask; /* clear other bits */
|
||||
+
|
||||
+ if (tmp != val)
|
||||
+ dev_err(phy_dwc3->dev, "write: %x to QSCRATCH: %x FAILED\n",
|
||||
+ val, offset);
|
||||
+}
|
||||
+
|
||||
+static int wait_for_latch(void __iomem *addr)
|
||||
+{
|
||||
+ u32 retry = 10;
|
||||
+
|
||||
+ while (true) {
|
||||
+ if (!readl(addr))
|
||||
+ break;
|
||||
+
|
||||
+ if (--retry == 0)
|
||||
+ return -ETIMEDOUT;
|
||||
+
|
||||
+ usleep_range(10, 20);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * Write SSPHY register
|
||||
+ *
|
||||
+ * @base - QCOM DWC3 PHY base virtual address.
|
||||
+ * @addr - SSPHY address to write.
|
||||
+ * @val - value to write.
|
||||
+ */
|
||||
+static int qcom_dwc3_ss_write_phycreg(struct qcom_dwc3_usb_phy *phy_dwc3,
|
||||
+ u32 addr, u32 val)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ writel(addr, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG);
|
||||
+ writel(SS_CR_CAP_ADDR_REG, phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG);
|
||||
+
|
||||
+ ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG);
|
||||
+ if (ret)
|
||||
+ goto err_wait;
|
||||
+
|
||||
+ writel(val, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG);
|
||||
+ writel(SS_CR_CAP_DATA_REG, phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG);
|
||||
+
|
||||
+ ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG);
|
||||
+ if (ret)
|
||||
+ goto err_wait;
|
||||
+
|
||||
+ writel(SS_CR_WRITE_REG, phy_dwc3->base + CR_PROTOCOL_WRITE_REG);
|
||||
+
|
||||
+ ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_WRITE_REG);
|
||||
+
|
||||
+err_wait:
|
||||
+ if (ret)
|
||||
+ dev_err(phy_dwc3->dev, "timeout waiting for latch\n");
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * Read SSPHY register.
|
||||
+ *
|
||||
+ * @base - QCOM DWC3 PHY base virtual address.
|
||||
+ * @addr - SSPHY address to read.
|
||||
+ */
|
||||
+static int qcom_dwc3_ss_read_phycreg(void __iomem *base, u32 addr, u32 *val)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ writel(addr, base + CR_PROTOCOL_DATA_IN_REG);
|
||||
+ writel(SS_CR_CAP_ADDR_REG, base + CR_PROTOCOL_CAP_ADDR_REG);
|
||||
+
|
||||
+ ret = wait_for_latch(base + CR_PROTOCOL_CAP_ADDR_REG);
|
||||
+ if (ret)
|
||||
+ goto err_wait;
|
||||
+
|
||||
+ /*
|
||||
+ * Due to hardware bug, first read of SSPHY register might be
|
||||
+ * incorrect. Hence as workaround, SW should perform SSPHY register
|
||||
+ * read twice, but use only second read and ignore first read.
|
||||
+ */
|
||||
+ writel(SS_CR_READ_REG, base + CR_PROTOCOL_READ_REG);
|
||||
+
|
||||
+ ret = wait_for_latch(base + CR_PROTOCOL_READ_REG);
|
||||
+ if (ret)
|
||||
+ goto err_wait;
|
||||
+
|
||||
+ /* throwaway read */
|
||||
+ readl(base + CR_PROTOCOL_DATA_OUT_REG);
|
||||
+
|
||||
+ writel(SS_CR_READ_REG, base + CR_PROTOCOL_READ_REG);
|
||||
+
|
||||
+ ret = wait_for_latch(base + CR_PROTOCOL_READ_REG);
|
||||
+ if (ret)
|
||||
+ goto err_wait;
|
||||
+
|
||||
+ *val = readl(base + CR_PROTOCOL_DATA_OUT_REG);
|
||||
+
|
||||
+err_wait:
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int qcom_dwc3_phy_power_on(struct phy *phy)
|
||||
+{
|
||||
+ int ret;
|
||||
+ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
|
||||
+
|
||||
+ ret = clk_prepare_enable(phy_dwc3->xo_clk);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = clk_prepare_enable(phy_dwc3->ref_clk);
|
||||
+ if (ret)
|
||||
+ clk_disable_unprepare(phy_dwc3->xo_clk);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int qcom_dwc3_phy_power_off(struct phy *phy)
|
||||
+{
|
||||
+ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
|
||||
+
|
||||
+ clk_disable_unprepare(phy_dwc3->ref_clk);
|
||||
+ clk_disable_unprepare(phy_dwc3->xo_clk);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qcom_dwc3_hs_phy_init(struct phy *phy)
|
||||
+{
|
||||
+ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
|
||||
+ u32 val;
|
||||
+
|
||||
+ /*
|
||||
+ * HSPHY Initialization: Enable UTMI clock, select 19.2MHz fsel
|
||||
+ * enable clamping, and disable RETENTION (power-on default is ENABLED)
|
||||
+ */
|
||||
+ val = HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_DMSEHV_CLAMP |
|
||||
+ HSUSB_CTRL_RETENABLEN | HSUSB_CTRL_COMMONONN |
|
||||
+ HSUSB_CTRL_OTGSESSVLD_CLAMP | HSUSB_CTRL_ID_HV_CLAMP |
|
||||
+ HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_UTMI_OTG_VBUS_VALID |
|
||||
+ HSUSB_CTRL_UTMI_CLK_EN | HSUSB_CTRL_CLAMP_EN | 0x70;
|
||||
+
|
||||
+ /* use core clock if external reference is not present */
|
||||
+ if (!phy_dwc3->xo_clk)
|
||||
+ val |= HSUSB_CTRL_USE_CLKCORE;
|
||||
+
|
||||
+ writel(val, phy_dwc3->base + HSUSB_PHY_CTRL_REG);
|
||||
+ usleep_range(2000, 2200);
|
||||
+
|
||||
+ /* Disable (bypass) VBUS and ID filters */
|
||||
+ writel(HSUSB_GCFG_XHCI_REV, phy_dwc3->base + QSCRATCH_GENERAL_CFG);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qcom_dwc3_ss_phy_init(struct phy *phy)
|
||||
+{
|
||||
+ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
|
||||
+ int ret;
|
||||
+ u32 data = 0;
|
||||
+
|
||||
+ /* reset phy */
|
||||
+ data = readl(phy_dwc3->base + SSUSB_PHY_CTRL_REG);
|
||||
+ writel(data | SSUSB_CTRL_SS_PHY_RESET,
|
||||
+ phy_dwc3->base + SSUSB_PHY_CTRL_REG);
|
||||
+ usleep_range(2000, 2200);
|
||||
+ writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
|
||||
+
|
||||
+ /* clear REF_PAD if we don't have XO clk */
|
||||
+ if (!phy_dwc3->xo_clk)
|
||||
+ data &= ~SSUSB_CTRL_REF_USE_PAD;
|
||||
+ else
|
||||
+ data |= SSUSB_CTRL_REF_USE_PAD;
|
||||
+
|
||||
+ writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
|
||||
+
|
||||
+ /* wait for ref clk to become stable, this can take up to 30ms */
|
||||
+ msleep(30);
|
||||
+
|
||||
+ data |= SSUSB_CTRL_SS_PHY_EN | SSUSB_CTRL_LANE0_PWR_PRESENT;
|
||||
+ writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
|
||||
+
|
||||
+ /*
|
||||
+ * Fix RX Equalization setting as follows
|
||||
+ * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0
|
||||
+ * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1
|
||||
+ * LANE0.RX_OVRD_IN_HI.RX_EQ set to 3
|
||||
+ * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1
|
||||
+ */
|
||||
+ ret = qcom_dwc3_ss_read_phycreg(phy_dwc3->base,
|
||||
+ SSPHY_CTRL_RX_OVRD_IN_HI(0), &data);
|
||||
+ if (ret)
|
||||
+ goto err_phy_trans;
|
||||
+
|
||||
+ data &= ~RX_OVRD_IN_HI_RX_EQ_EN;
|
||||
+ data |= RX_OVRD_IN_HI_RX_EQ_EN_OVRD;
|
||||
+ data &= ~RX_OVRD_IN_HI_RX_EQ_MASK;
|
||||
+ data |= 0x3 << RX_OVRD_IN_HI_RX_EQ_SHIFT;
|
||||
+ data |= RX_OVRD_IN_HI_RX_EQ_OVRD;
|
||||
+ ret = qcom_dwc3_ss_write_phycreg(phy_dwc3,
|
||||
+ SSPHY_CTRL_RX_OVRD_IN_HI(0), data);
|
||||
+ if (ret)
|
||||
+ goto err_phy_trans;
|
||||
+
|
||||
+ /*
|
||||
+ * Set EQ and TX launch amplitudes as follows
|
||||
+ * LANE0.TX_OVRD_DRV_LO.PREEMPH set to 22
|
||||
+ * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 127
|
||||
+ * LANE0.TX_OVRD_DRV_LO.EN set to 1.
|
||||
+ */
|
||||
+ ret = qcom_dwc3_ss_read_phycreg(phy_dwc3->base,
|
||||
+ SSPHY_CTRL_TX_OVRD_DRV_LO(0), &data);
|
||||
+ if (ret)
|
||||
+ goto err_phy_trans;
|
||||
+
|
||||
+ data &= ~TX_OVRD_DRV_LO_PREEMPH_MASK;
|
||||
+ data |= 0x16 << TX_OVRD_DRV_LO_PREEMPH_SHIFT;
|
||||
+ data &= ~TX_OVRD_DRV_LO_AMPLITUDE_MASK;
|
||||
+ data |= 0x7f;
|
||||
+ data |= TX_OVRD_DRV_LO_EN;
|
||||
+ ret = qcom_dwc3_ss_write_phycreg(phy_dwc3,
|
||||
+ SSPHY_CTRL_TX_OVRD_DRV_LO(0), data);
|
||||
+ if (ret)
|
||||
+ goto err_phy_trans;
|
||||
+
|
||||
+ /*
|
||||
+ * Set the QSCRATCH PHY_PARAM_CTRL1 parameters as follows
|
||||
+ * TX_FULL_SWING [26:20] amplitude to 127
|
||||
+ * TX_DEEMPH_3_5DB [13:8] to 22
|
||||
+ * LOS_BIAS [2:0] to 0x5
|
||||
+ */
|
||||
+ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_PARAM_CTRL_1,
|
||||
+ 0x07f03f07, 0x07f01605);
|
||||
+
|
||||
+err_phy_trans:
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int qcom_dwc3_ss_phy_exit(struct phy *phy)
|
||||
+{
|
||||
+ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
|
||||
+
|
||||
+ /* Sequence to put SSPHY in low power state:
|
||||
+ * 1. Clear REF_PHY_EN in PHY_CTRL_REG
|
||||
+ * 2. Clear REF_USE_PAD in PHY_CTRL_REG
|
||||
+ * 3. Set TEST_POWERED_DOWN in PHY_CTRL_REG to enable PHY retention
|
||||
+ */
|
||||
+ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,
|
||||
+ SSUSB_CTRL_SS_PHY_EN, 0x0);
|
||||
+ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,
|
||||
+ SSUSB_CTRL_REF_USE_PAD, 0x0);
|
||||
+ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,
|
||||
+ 0x0, SSUSB_CTRL_TEST_POWERDOWN);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct qcom_dwc3_phy_drvdata qcom_dwc3_hs_drvdata = {
|
||||
+ .ops = {
|
||||
+ .init = qcom_dwc3_hs_phy_init,
|
||||
+ .power_on = qcom_dwc3_phy_power_on,
|
||||
+ .power_off = qcom_dwc3_phy_power_off,
|
||||
+ .owner = THIS_MODULE,
|
||||
+ },
|
||||
+ .clk_rate = 60000000,
|
||||
+};
|
||||
+
|
||||
+static const struct qcom_dwc3_phy_drvdata qcom_dwc3_ss_drvdata = {
|
||||
+ .ops = {
|
||||
+ .init = qcom_dwc3_ss_phy_init,
|
||||
+ .exit = qcom_dwc3_ss_phy_exit,
|
||||
+ .power_on = qcom_dwc3_phy_power_on,
|
||||
+ .power_off = qcom_dwc3_phy_power_off,
|
||||
+ .owner = THIS_MODULE,
|
||||
+ },
|
||||
+ .clk_rate = 125000000,
|
||||
+};
|
||||
+
|
||||
+static const struct of_device_id qcom_dwc3_phy_table[] = {
|
||||
+ { .compatible = "qcom,dwc3-hs-usb-phy", .data = &qcom_dwc3_hs_drvdata },
|
||||
+ { .compatible = "qcom,dwc3-ss-usb-phy", .data = &qcom_dwc3_ss_drvdata },
|
||||
+ { /* Sentinel */ }
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, qcom_dwc3_phy_table);
|
||||
+
|
||||
+static int qcom_dwc3_phy_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct qcom_dwc3_usb_phy *phy_dwc3;
|
||||
+ struct phy_provider *phy_provider;
|
||||
+ struct phy *generic_phy;
|
||||
+ struct resource *res;
|
||||
+ const struct of_device_id *match;
|
||||
+ const struct qcom_dwc3_phy_drvdata *data;
|
||||
+
|
||||
+ phy_dwc3 = devm_kzalloc(&pdev->dev, sizeof(*phy_dwc3), GFP_KERNEL);
|
||||
+ if (!phy_dwc3)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ match = of_match_node(qcom_dwc3_phy_table, pdev->dev.of_node);
|
||||
+ data = match->data;
|
||||
+
|
||||
+ phy_dwc3->dev = &pdev->dev;
|
||||
+
|
||||
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
+ phy_dwc3->base = devm_ioremap_resource(phy_dwc3->dev, res);
|
||||
+ if (IS_ERR(phy_dwc3->base))
|
||||
+ return PTR_ERR(phy_dwc3->base);
|
||||
+
|
||||
+ phy_dwc3->ref_clk = devm_clk_get(phy_dwc3->dev, "ref");
|
||||
+ if (IS_ERR(phy_dwc3->ref_clk)) {
|
||||
+ dev_dbg(phy_dwc3->dev, "cannot get reference clock\n");
|
||||
+ return PTR_ERR(phy_dwc3->ref_clk);
|
||||
+ }
|
||||
+
|
||||
+ clk_set_rate(phy_dwc3->ref_clk, data->clk_rate);
|
||||
+
|
||||
+ phy_dwc3->xo_clk = devm_clk_get(phy_dwc3->dev, "xo");
|
||||
+ if (IS_ERR(phy_dwc3->xo_clk)) {
|
||||
+ dev_dbg(phy_dwc3->dev, "cannot get TCXO clock\n");
|
||||
+ phy_dwc3->xo_clk = NULL;
|
||||
+ }
|
||||
+
|
||||
+ generic_phy = devm_phy_create(phy_dwc3->dev, pdev->dev.of_node,
|
||||
+ &data->ops);
|
||||
+
|
||||
+ if (IS_ERR(generic_phy))
|
||||
+ return PTR_ERR(generic_phy);
|
||||
+
|
||||
+ phy_set_drvdata(generic_phy, phy_dwc3);
|
||||
+ platform_set_drvdata(pdev, phy_dwc3);
|
||||
+
|
||||
+ phy_provider = devm_of_phy_provider_register(phy_dwc3->dev,
|
||||
+ of_phy_simple_xlate);
|
||||
+
|
||||
+ if (IS_ERR(phy_provider))
|
||||
+ return PTR_ERR(phy_provider);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver qcom_dwc3_phy_driver = {
|
||||
+ .probe = qcom_dwc3_phy_probe,
|
||||
+ .driver = {
|
||||
+ .name = "qcom-dwc3-usb-phy",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .of_match_table = qcom_dwc3_phy_table,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+module_platform_driver(qcom_dwc3_phy_driver);
|
||||
+
|
||||
+MODULE_ALIAS("platform:phy-qcom-dwc3");
|
||||
+MODULE_LICENSE("GPL v2");
|
||||
+MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>");
|
||||
+MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
|
||||
+MODULE_DESCRIPTION("DesignWare USB3 QCOM PHY driver");
|
|
@ -0,0 +1,29 @@
|
|||
From 48051ece78136e4235a2415a52797db56f8a4478 Mon Sep 17 00:00:00 2001
|
||||
From: Mathieu Olivari <mathieu@codeaurora.org>
|
||||
Date: Tue, 21 Apr 2015 19:09:07 -0700
|
||||
Subject: [PATCH 33/69] ARM: qcom: automatically select PCI_DOMAINS if PCI is
|
||||
enabled
|
||||
|
||||
If multiple PCIe devices are present in the system, the kernel will
|
||||
panic at boot time when trying to scan the PCI buses. This happens on
|
||||
IPQ806x based platforms, which has 3 PCIe ports.
|
||||
|
||||
Enabling this option allows the kernel to assign the pci-domains
|
||||
according to the device-tree content. This allows multiple PCIe
|
||||
controllers to coexist in the system.
|
||||
|
||||
Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
|
||||
---
|
||||
arch/arm/mach-qcom/Kconfig | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
--- a/arch/arm/mach-qcom/Kconfig
|
||||
+++ b/arch/arm/mach-qcom/Kconfig
|
||||
@@ -6,6 +6,7 @@ menuconfig ARCH_QCOM
|
||||
select ARM_AMBA
|
||||
select PINCTRL
|
||||
select QCOM_SCM if SMP
|
||||
+ select PCI_DOMAINS if PCI
|
||||
help
|
||||
Support for Qualcomm's devicetree based systems.
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
From 1d6b12f73c98ecf252743936a53242356cc50a34 Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Date: Fri, 20 Mar 2015 23:45:20 -0700
|
||||
Subject: [PATCH 34/69] ARM: Add Krait L2 register accessor functions
|
||||
|
||||
Krait CPUs have a handful of L2 cache controller registers that
|
||||
live behind a cp15 based indirection register. First you program
|
||||
the indirection register (l2cpselr) to point the L2 'window'
|
||||
register (l2cpdr) at what you want to read/write. Then you
|
||||
read/write the 'window' register to do what you want. The
|
||||
l2cpselr register is not banked per-cpu so we must lock around
|
||||
accesses to it to prevent other CPUs from re-pointing l2cpdr
|
||||
underneath us.
|
||||
|
||||
Cc: Mark Rutland <mark.rutland@arm.com>
|
||||
Cc: Russell King <linux@arm.linux.org.uk>
|
||||
Cc: Courtney Cavin <courtney.cavin@sonymobile.com>
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
---
|
||||
arch/arm/common/Kconfig | 3 ++
|
||||
arch/arm/common/Makefile | 1 +
|
||||
arch/arm/common/krait-l2-accessors.c | 58 +++++++++++++++++++++++++++++++
|
||||
arch/arm/include/asm/krait-l2-accessors.h | 20 +++++++++++
|
||||
4 files changed, 82 insertions(+)
|
||||
create mode 100644 arch/arm/common/krait-l2-accessors.c
|
||||
create mode 100644 arch/arm/include/asm/krait-l2-accessors.h
|
||||
|
||||
--- a/arch/arm/common/Kconfig
|
||||
+++ b/arch/arm/common/Kconfig
|
||||
@@ -9,6 +9,9 @@ config DMABOUNCE
|
||||
bool
|
||||
select ZONE_DMA
|
||||
|
||||
+config KRAIT_L2_ACCESSORS
|
||||
+ bool
|
||||
+
|
||||
config SHARP_LOCOMO
|
||||
bool
|
||||
|
||||
--- a/arch/arm/common/Makefile
|
||||
+++ b/arch/arm/common/Makefile
|
||||
@@ -7,6 +7,7 @@ obj-y += firmware.o
|
||||
obj-$(CONFIG_ICST) += icst.o
|
||||
obj-$(CONFIG_SA1111) += sa1111.o
|
||||
obj-$(CONFIG_DMABOUNCE) += dmabounce.o
|
||||
+obj-$(CONFIG_KRAIT_L2_ACCESSORS) += krait-l2-accessors.o
|
||||
obj-$(CONFIG_SHARP_LOCOMO) += locomo.o
|
||||
obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o
|
||||
obj-$(CONFIG_SHARP_SCOOP) += scoop.o
|
||||
--- /dev/null
|
||||
+++ b/arch/arm/common/krait-l2-accessors.c
|
||||
@@ -0,0 +1,58 @@
|
||||
+/*
|
||||
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License version 2 and
|
||||
+ * only version 2 as published by the Free Software Foundation.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/spinlock.h>
|
||||
+#include <linux/export.h>
|
||||
+
|
||||
+#include <asm/barrier.h>
|
||||
+#include <asm/krait-l2-accessors.h>
|
||||
+
|
||||
+static DEFINE_RAW_SPINLOCK(krait_l2_lock);
|
||||
+
|
||||
+void krait_set_l2_indirect_reg(u32 addr, u32 val)
|
||||
+{
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ raw_spin_lock_irqsave(&krait_l2_lock, flags);
|
||||
+ /*
|
||||
+ * Select the L2 window by poking l2cpselr, then write to the window
|
||||
+ * via l2cpdr.
|
||||
+ */
|
||||
+ asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr));
|
||||
+ isb();
|
||||
+ asm volatile ("mcr p15, 3, %0, c15, c0, 7 @ l2cpdr" : : "r" (val));
|
||||
+ isb();
|
||||
+
|
||||
+ raw_spin_unlock_irqrestore(&krait_l2_lock, flags);
|
||||
+}
|
||||
+EXPORT_SYMBOL(krait_set_l2_indirect_reg);
|
||||
+
|
||||
+u32 krait_get_l2_indirect_reg(u32 addr)
|
||||
+{
|
||||
+ u32 val;
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ raw_spin_lock_irqsave(&krait_l2_lock, flags);
|
||||
+ /*
|
||||
+ * Select the L2 window by poking l2cpselr, then read from the window
|
||||
+ * via l2cpdr.
|
||||
+ */
|
||||
+ asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr));
|
||||
+ isb();
|
||||
+ asm volatile ("mrc p15, 3, %0, c15, c0, 7 @ l2cpdr" : "=r" (val));
|
||||
+
|
||||
+ raw_spin_unlock_irqrestore(&krait_l2_lock, flags);
|
||||
+
|
||||
+ return val;
|
||||
+}
|
||||
+EXPORT_SYMBOL(krait_get_l2_indirect_reg);
|
||||
--- /dev/null
|
||||
+++ b/arch/arm/include/asm/krait-l2-accessors.h
|
||||
@@ -0,0 +1,20 @@
|
||||
+/*
|
||||
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License version 2 and
|
||||
+ * only version 2 as published by the Free Software Foundation.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ */
|
||||
+
|
||||
+#ifndef __ASMARM_KRAIT_L2_ACCESSORS_H
|
||||
+#define __ASMARM_KRAIT_L2_ACCESSORS_H
|
||||
+
|
||||
+extern void krait_set_l2_indirect_reg(u32 addr, u32 val);
|
||||
+extern u32 krait_get_l2_indirect_reg(u32 addr);
|
||||
+
|
||||
+#endif
|
|
@ -0,0 +1,192 @@
|
|||
From 9d381d65eae163d8f50d97a3ad9033bba176f62b Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Date: Fri, 20 Mar 2015 23:45:21 -0700
|
||||
Subject: [PATCH 35/69] clk: mux: Split out register accessors for reuse
|
||||
|
||||
We want to reuse the logic in clk-mux.c for other clock drivers
|
||||
that don't use readl as register accessors. Fortunately, there
|
||||
really isn't much to the mux code besides the table indirection
|
||||
and quirk flags if you assume any bit shifting and masking has
|
||||
been done already. Pull that logic out into reusable functions
|
||||
that operate on an optional table and some flags so that other
|
||||
drivers can use the same logic.
|
||||
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
---
|
||||
drivers/clk/clk-mux.c | 76 ++++++++++++++++++++++++++++----------------
|
||||
include/linux/clk-provider.h | 11 +++++--
|
||||
2 files changed, 57 insertions(+), 30 deletions(-)
|
||||
|
||||
--- a/drivers/clk/clk-mux.c
|
||||
+++ b/drivers/clk/clk-mux.c
|
||||
@@ -26,35 +26,27 @@
|
||||
* parent - parent is adjustable through clk_set_parent
|
||||
*/
|
||||
|
||||
-static u8 clk_mux_get_parent(struct clk_hw *hw)
|
||||
+#define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw)
|
||||
+
|
||||
+unsigned int clk_mux_get_parent(struct clk_hw *hw, unsigned int val,
|
||||
+ unsigned int *table, unsigned long flags)
|
||||
{
|
||||
struct clk_mux *mux = to_clk_mux(hw);
|
||||
int num_parents = clk_hw_get_num_parents(hw);
|
||||
- u32 val;
|
||||
|
||||
- /*
|
||||
- * FIXME need a mux-specific flag to determine if val is bitwise or numeric
|
||||
- * e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1
|
||||
- * to 0x7 (index starts at one)
|
||||
- * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so
|
||||
- * val = 0x4 really means "bit 2, index starts at bit 0"
|
||||
- */
|
||||
- val = clk_readl(mux->reg) >> mux->shift;
|
||||
- val &= mux->mask;
|
||||
-
|
||||
- if (mux->table) {
|
||||
+ if (table) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_parents; i++)
|
||||
- if (mux->table[i] == val)
|
||||
+ if (table[i] == val)
|
||||
return i;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
- if (val && (mux->flags & CLK_MUX_INDEX_BIT))
|
||||
+ if (val && (flags & CLK_MUX_INDEX_BIT))
|
||||
val = ffs(val) - 1;
|
||||
|
||||
- if (val && (mux->flags & CLK_MUX_INDEX_ONE))
|
||||
+ if (val && (flags & CLK_MUX_INDEX_ONE))
|
||||
val--;
|
||||
|
||||
if (val >= num_parents)
|
||||
@@ -62,23 +54,53 @@ static u8 clk_mux_get_parent(struct clk_
|
||||
|
||||
return val;
|
||||
}
|
||||
+EXPORT_SYMBOL_GPL(clk_mux_get_parent);
|
||||
|
||||
-static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
|
||||
+static u8 _clk_mux_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_mux *mux = to_clk_mux(hw);
|
||||
u32 val;
|
||||
- unsigned long flags = 0;
|
||||
|
||||
- if (mux->table) {
|
||||
- index = mux->table[index];
|
||||
+ /*
|
||||
+ * FIXME need a mux-specific flag to determine if val is bitwise or numeric
|
||||
+ * e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1
|
||||
+ * to 0x7 (index starts at one)
|
||||
+ * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so
|
||||
+ * val = 0x4 really means "bit 2, index starts at bit 0"
|
||||
+ */
|
||||
+ val = clk_readl(mux->reg) >> mux->shift;
|
||||
+ val &= mux->mask;
|
||||
+
|
||||
+ return clk_mux_get_parent(hw, val, mux->table, mux->flags);
|
||||
+}
|
||||
+
|
||||
+unsigned int clk_mux_reindex(u8 index, unsigned int *table,
|
||||
+ unsigned long flags)
|
||||
+{
|
||||
+ unsigned int val = index;
|
||||
+
|
||||
+ if (table) {
|
||||
+ val = table[val];
|
||||
} else {
|
||||
- if (mux->flags & CLK_MUX_INDEX_BIT)
|
||||
- index = 1 << index;
|
||||
+ if (flags & CLK_MUX_INDEX_BIT)
|
||||
+ val = 1 << index;
|
||||
|
||||
- if (mux->flags & CLK_MUX_INDEX_ONE)
|
||||
- index++;
|
||||
+ if (flags & CLK_MUX_INDEX_ONE)
|
||||
+ val++;
|
||||
}
|
||||
|
||||
+ return val;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(clk_mux_reindex);
|
||||
+
|
||||
+static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
|
||||
+{
|
||||
+ struct clk_mux *mux = to_clk_mux(hw);
|
||||
+ u32 val;
|
||||
+ unsigned long flags = 0;
|
||||
+
|
||||
+ index = clk_mux_reindex(index, mux->table, mux->flags);
|
||||
+
|
||||
if (mux->lock)
|
||||
spin_lock_irqsave(mux->lock, flags);
|
||||
else
|
||||
@@ -102,14 +124,14 @@ static int clk_mux_set_parent(struct clk
|
||||
}
|
||||
|
||||
const struct clk_ops clk_mux_ops = {
|
||||
- .get_parent = clk_mux_get_parent,
|
||||
+ .get_parent = _clk_mux_get_parent,
|
||||
.set_parent = clk_mux_set_parent,
|
||||
.determine_rate = __clk_mux_determine_rate,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(clk_mux_ops);
|
||||
|
||||
const struct clk_ops clk_mux_ro_ops = {
|
||||
- .get_parent = clk_mux_get_parent,
|
||||
+ .get_parent = _clk_mux_get_parent,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(clk_mux_ro_ops);
|
||||
|
||||
@@ -117,7 +139,7 @@ struct clk_hw *clk_hw_register_mux_table
|
||||
const char * const *parent_names, u8 num_parents,
|
||||
unsigned long flags,
|
||||
void __iomem *reg, u8 shift, u32 mask,
|
||||
- u8 clk_mux_flags, u32 *table, spinlock_t *lock)
|
||||
+ u8 clk_mux_flags, unsigned int *table, spinlock_t *lock)
|
||||
{
|
||||
struct clk_mux *mux;
|
||||
struct clk_hw *hw;
|
||||
--- a/include/linux/clk-provider.h
|
||||
+++ b/include/linux/clk-provider.h
|
||||
@@ -466,7 +466,7 @@ void clk_hw_unregister_divider(struct cl
|
||||
struct clk_mux {
|
||||
struct clk_hw hw;
|
||||
void __iomem *reg;
|
||||
- u32 *table;
|
||||
+ unsigned int *table;
|
||||
u32 mask;
|
||||
u8 shift;
|
||||
u8 flags;
|
||||
@@ -484,6 +484,11 @@ struct clk_mux {
|
||||
extern const struct clk_ops clk_mux_ops;
|
||||
extern const struct clk_ops clk_mux_ro_ops;
|
||||
|
||||
+unsigned int clk_mux_get_parent(struct clk_hw *hw, unsigned int val,
|
||||
+ unsigned int *table, unsigned long flags);
|
||||
+unsigned int clk_mux_reindex(u8 index, unsigned int *table,
|
||||
+ unsigned long flags);
|
||||
+
|
||||
struct clk *clk_register_mux(struct device *dev, const char *name,
|
||||
const char * const *parent_names, u8 num_parents,
|
||||
unsigned long flags,
|
||||
@@ -499,12 +504,12 @@ struct clk *clk_register_mux_table(struc
|
||||
const char * const *parent_names, u8 num_parents,
|
||||
unsigned long flags,
|
||||
void __iomem *reg, u8 shift, u32 mask,
|
||||
- u8 clk_mux_flags, u32 *table, spinlock_t *lock);
|
||||
+ u8 clk_mux_flags, unsigned int *table, spinlock_t *lock);
|
||||
struct clk_hw *clk_hw_register_mux_table(struct device *dev, const char *name,
|
||||
const char * const *parent_names, u8 num_parents,
|
||||
unsigned long flags,
|
||||
void __iomem *reg, u8 shift, u32 mask,
|
||||
- u8 clk_mux_flags, u32 *table, spinlock_t *lock);
|
||||
+ u8 clk_mux_flags, unsigned int *table, spinlock_t *lock);
|
||||
|
||||
void clk_unregister_mux(struct clk *clk);
|
||||
void clk_hw_unregister_mux(struct clk_hw *hw);
|
|
@ -0,0 +1,107 @@
|
|||
From 88e1d6d9c113fe50810d1b03eb1fdbf015e5d1bd Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Date: Fri, 20 Mar 2015 23:45:22 -0700
|
||||
Subject: [PATCH 36/69] clk: Avoid sending high rates to downstream clocks
|
||||
during set_rate
|
||||
|
||||
If a clock is on and we call clk_set_rate() on it we may get into
|
||||
a situation where the clock temporarily increases in rate
|
||||
dramatically while we walk the tree and call .set_rate() ops. For
|
||||
example, consider a case where a PLL feeds into a divider.
|
||||
Initially the divider is set to divide by 1 and the PLL is
|
||||
running fairly slow (100MHz). The downstream consumer of the
|
||||
divider output can only handle rates =< 400 MHz, but the divider
|
||||
can only choose between divisors of 1 and 4.
|
||||
|
||||
+-----+ +----------------+
|
||||
| PLL |-->| div 1 or div 4 |---> consumer device
|
||||
+-----+ +----------------+
|
||||
|
||||
To achieve a rate of 400MHz on the output of the divider, we
|
||||
would have to set the rate of the PLL to 1.6 GHz and then divide
|
||||
it by 4. The current code would set the PLL to 1.6GHz first while
|
||||
the divider is still set to 1, thus causing the downstream
|
||||
consumer of the clock to receive a few clock cycles of 1.6GHz
|
||||
clock (far beyond it's maximum acceptable rate). We should be
|
||||
changing the divider first before increasing the PLL rate to
|
||||
avoid this problem.
|
||||
|
||||
Therefore, set the rate of any child clocks that are increasing
|
||||
in rate from their current rate so that they can increase their
|
||||
dividers if necessary. We assume that there isn't such a thing as
|
||||
minimum rate requirements.
|
||||
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
|
||||
Conflicts:
|
||||
drivers/clk/clk.c
|
||||
---
|
||||
drivers/clk/clk.c | 22 +++++++++++++++-------
|
||||
1 file changed, 15 insertions(+), 7 deletions(-)
|
||||
|
||||
--- a/drivers/clk/clk.c
|
||||
+++ b/drivers/clk/clk.c
|
||||
@@ -1466,12 +1466,12 @@ static struct clk_core *clk_propagate_ra
|
||||
* walk down a subtree and set the new rates notifying the rate
|
||||
* change on the way
|
||||
*/
|
||||
-static void clk_change_rate(struct clk_core *core)
|
||||
+static void
|
||||
+clk_change_rate(struct clk_core *core, unsigned long best_parent_rate)
|
||||
{
|
||||
struct clk_core *child;
|
||||
struct hlist_node *tmp;
|
||||
unsigned long old_rate;
|
||||
- unsigned long best_parent_rate = 0;
|
||||
bool skip_set_rate = false;
|
||||
struct clk_core *old_parent;
|
||||
struct clk_core *parent = NULL;
|
||||
@@ -1523,6 +1523,7 @@ static void clk_change_rate(struct clk_c
|
||||
trace_clk_set_rate_complete(core, core->new_rate);
|
||||
|
||||
core->rate = clk_recalc(core, best_parent_rate);
|
||||
+ core->rate = core->new_rate;
|
||||
|
||||
if (core->flags & CLK_SET_RATE_UNGATE) {
|
||||
unsigned long flags;
|
||||
@@ -1550,12 +1551,13 @@ static void clk_change_rate(struct clk_c
|
||||
/* Skip children who will be reparented to another clock */
|
||||
if (child->new_parent && child->new_parent != core)
|
||||
continue;
|
||||
- clk_change_rate(child);
|
||||
+ if (child->new_rate != child->rate)
|
||||
+ clk_change_rate(child, core->new_rate);
|
||||
}
|
||||
|
||||
- /* handle the new child who might not be in core->children yet */
|
||||
- if (core->new_child)
|
||||
- clk_change_rate(core->new_child);
|
||||
+ /* handle the new child who might not be in clk->children yet */
|
||||
+ if (core->new_child && core->new_child->new_rate != core->new_child->rate)
|
||||
+ clk_change_rate(core->new_child, core->new_rate);
|
||||
}
|
||||
|
||||
static int clk_core_set_rate_nolock(struct clk_core *core,
|
||||
@@ -1563,6 +1565,7 @@ static int clk_core_set_rate_nolock(stru
|
||||
{
|
||||
struct clk_core *top, *fail_clk;
|
||||
unsigned long rate = req_rate;
|
||||
+ unsigned long parent_rate;
|
||||
|
||||
if (!core)
|
||||
return 0;
|
||||
@@ -1588,8 +1591,13 @@ static int clk_core_set_rate_nolock(stru
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
+ if (top->parent)
|
||||
+ parent_rate = top->parent->rate;
|
||||
+ else
|
||||
+ parent_rate = 0;
|
||||
+
|
||||
/* change the rates */
|
||||
- clk_change_rate(top);
|
||||
+ clk_change_rate(top, parent_rate);
|
||||
|
||||
core->req_rate = req_rate;
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
From a1adfb782789ae9b25c928dfe3d639288563a86c Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Date: Fri, 20 Mar 2015 23:45:23 -0700
|
||||
Subject: [PATCH 37/69] clk: Add safe switch hook
|
||||
|
||||
Sometimes clocks can't accept their parent source turning off
|
||||
while the source is reprogrammed to a different rate. Most
|
||||
notably CPU clocks require a way to switch away from the current
|
||||
PLL they're running on, reprogram that PLL to a new rate, and
|
||||
then switch back to the PLL with the new rate once they're done.
|
||||
Add a hook that drivers can implement allowing them to return a
|
||||
'safe parent' and 'safe frequency' that they can switch their
|
||||
parent to while the upstream source is reprogrammed to support
|
||||
this.
|
||||
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
---
|
||||
drivers/clk/clk.c | 73 +++++++++++++++++++++++++++++++++++++++++---
|
||||
include/linux/clk-provider.h | 2 ++
|
||||
2 files changed, 70 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/drivers/clk/clk.c
|
||||
+++ b/drivers/clk/clk.c
|
||||
@@ -51,9 +51,13 @@ struct clk_core {
|
||||
struct clk_core **parents;
|
||||
u8 num_parents;
|
||||
u8 new_parent_index;
|
||||
+ u8 safe_parent_index;
|
||||
unsigned long rate;
|
||||
unsigned long req_rate;
|
||||
+ unsigned long old_rate;
|
||||
unsigned long new_rate;
|
||||
+ unsigned long safe_freq;
|
||||
+ struct clk_core *safe_parent;
|
||||
struct clk_core *new_parent;
|
||||
struct clk_core *new_child;
|
||||
unsigned long flags;
|
||||
@@ -1310,7 +1314,9 @@ out:
|
||||
static void clk_calc_subtree(struct clk_core *core, unsigned long new_rate,
|
||||
struct clk_core *new_parent, u8 p_index)
|
||||
{
|
||||
- struct clk_core *child;
|
||||
+ struct clk_core *child, *parent;
|
||||
+ struct clk_hw *parent_hw;
|
||||
+ unsigned long safe_freq = 0;
|
||||
|
||||
core->new_rate = new_rate;
|
||||
core->new_parent = new_parent;
|
||||
@@ -1320,6 +1326,23 @@ static void clk_calc_subtree(struct clk_
|
||||
if (new_parent && new_parent != core->parent)
|
||||
new_parent->new_child = core;
|
||||
|
||||
+ if (core->ops->get_safe_parent) {
|
||||
+ parent_hw = core->ops->get_safe_parent(core->hw, &safe_freq);
|
||||
+ if (parent_hw) {
|
||||
+ parent = parent_hw->core;
|
||||
+ p_index = clk_fetch_parent_index(core, parent);
|
||||
+ core->safe_parent_index = p_index;
|
||||
+ core->safe_parent = parent;
|
||||
+ if (safe_freq)
|
||||
+ core->safe_freq = safe_freq;
|
||||
+ else
|
||||
+ core->safe_freq = 0;
|
||||
+ }
|
||||
+ } else {
|
||||
+ core->safe_parent = NULL;
|
||||
+ core->safe_freq = 0;
|
||||
+ }
|
||||
+
|
||||
hlist_for_each_entry(child, &core->children, child_node) {
|
||||
child->new_rate = clk_recalc(child, new_rate);
|
||||
clk_calc_subtree(child, child->new_rate, NULL, 0);
|
||||
@@ -1432,14 +1455,51 @@ static struct clk_core *clk_propagate_ra
|
||||
unsigned long event)
|
||||
{
|
||||
struct clk_core *child, *tmp_clk, *fail_clk = NULL;
|
||||
+ struct clk_core *old_parent;
|
||||
int ret = NOTIFY_DONE;
|
||||
|
||||
- if (core->rate == core->new_rate)
|
||||
+ if (core->rate == core->new_rate && event != POST_RATE_CHANGE)
|
||||
return NULL;
|
||||
|
||||
+ switch (event) {
|
||||
+ case PRE_RATE_CHANGE:
|
||||
+ if (core->safe_parent) {
|
||||
+ if (core->safe_freq)
|
||||
+ core->ops->set_rate_and_parent(core->hw,
|
||||
+ core->safe_freq,
|
||||
+ core->safe_parent->rate,
|
||||
+ core->safe_parent_index);
|
||||
+ else
|
||||
+ core->ops->set_parent(core->hw,
|
||||
+ core->safe_parent_index);
|
||||
+ }
|
||||
+ core->old_rate = core->rate;
|
||||
+ break;
|
||||
+ case POST_RATE_CHANGE:
|
||||
+ if (core->safe_parent) {
|
||||
+ old_parent = __clk_set_parent_before(core,
|
||||
+ core->new_parent);
|
||||
+ if (core->ops->set_rate_and_parent) {
|
||||
+ core->ops->set_rate_and_parent(core->hw,
|
||||
+ core->new_rate,
|
||||
+ core->new_parent ?
|
||||
+ core->new_parent->rate : 0,
|
||||
+ core->new_parent_index);
|
||||
+ } else if (core->ops->set_parent) {
|
||||
+ core->ops->set_parent(core->hw,
|
||||
+ core->new_parent_index);
|
||||
+ }
|
||||
+ __clk_set_parent_after(core, core->new_parent,
|
||||
+ old_parent);
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
if (core->notifier_count) {
|
||||
- ret = __clk_notify(core, event, core->rate, core->new_rate);
|
||||
- if (ret & NOTIFY_STOP_MASK)
|
||||
+ if (event != POST_RATE_CHANGE || core->old_rate != core->rate)
|
||||
+ ret = __clk_notify(core, event, core->old_rate,
|
||||
+ core->new_rate);
|
||||
+ if (ret & NOTIFY_STOP_MASK && event != POST_RATE_CHANGE)
|
||||
fail_clk = core;
|
||||
}
|
||||
|
||||
@@ -1495,7 +1555,8 @@ clk_change_rate(struct clk_core *core, u
|
||||
clk_enable_unlock(flags);
|
||||
}
|
||||
|
||||
- if (core->new_parent && core->new_parent != core->parent) {
|
||||
+ if (core->new_parent && core->new_parent != core->parent &&
|
||||
+ !core->safe_parent) {
|
||||
old_parent = __clk_set_parent_before(core, core->new_parent);
|
||||
trace_clk_set_parent(core, core->new_parent);
|
||||
|
||||
@@ -1601,6 +1662,8 @@ static int clk_core_set_rate_nolock(stru
|
||||
|
||||
core->req_rate = req_rate;
|
||||
|
||||
+ clk_propagate_rate_change(top, POST_RATE_CHANGE);
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
--- a/include/linux/clk-provider.h
|
||||
+++ b/include/linux/clk-provider.h
|
||||
@@ -206,6 +206,8 @@ struct clk_ops {
|
||||
struct clk_rate_request *req);
|
||||
int (*set_parent)(struct clk_hw *hw, u8 index);
|
||||
u8 (*get_parent)(struct clk_hw *hw);
|
||||
+ struct clk_hw *(*get_safe_parent)(struct clk_hw *hw,
|
||||
+ unsigned long *safe_freq);
|
||||
int (*set_rate)(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate);
|
||||
int (*set_rate_and_parent)(struct clk_hw *hw,
|
|
@ -0,0 +1,340 @@
|
|||
From f044ffe2d612dcaa2de36c918aaab79c8db1482e Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Date: Fri, 20 Mar 2015 23:45:24 -0700
|
||||
Subject: [PATCH 38/69] clk: qcom: Add support for High-Frequency PLLs (HFPLLs)
|
||||
|
||||
HFPLLs are the main frequency source for Krait CPU clocks. Add
|
||||
support for changing the rate of these PLLs.
|
||||
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
---
|
||||
drivers/clk/qcom/Makefile | 1 +
|
||||
drivers/clk/qcom/clk-hfpll.c | 253 +++++++++++++++++++++++++++++++++++++++++++
|
||||
drivers/clk/qcom/clk-hfpll.h | 54 +++++++++
|
||||
3 files changed, 308 insertions(+)
|
||||
create mode 100644 drivers/clk/qcom/clk-hfpll.c
|
||||
create mode 100644 drivers/clk/qcom/clk-hfpll.h
|
||||
|
||||
--- a/drivers/clk/qcom/Makefile
|
||||
+++ b/drivers/clk/qcom/Makefile
|
||||
@@ -9,6 +9,7 @@ clk-qcom-y += clk-rcg2.o
|
||||
clk-qcom-y += clk-branch.o
|
||||
clk-qcom-y += clk-regmap-divider.o
|
||||
clk-qcom-y += clk-regmap-mux.o
|
||||
+clk-qcom-y += clk-hfpll.o
|
||||
clk-qcom-y += reset.o
|
||||
clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o
|
||||
|
||||
--- /dev/null
|
||||
+++ b/drivers/clk/qcom/clk-hfpll.c
|
||||
@@ -0,0 +1,253 @@
|
||||
+/*
|
||||
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License version 2 and
|
||||
+ * only version 2 as published by the Free Software Foundation.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ */
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/export.h>
|
||||
+#include <linux/regmap.h>
|
||||
+#include <linux/delay.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/clk-provider.h>
|
||||
+#include <linux/spinlock.h>
|
||||
+
|
||||
+#include "clk-regmap.h"
|
||||
+#include "clk-hfpll.h"
|
||||
+
|
||||
+#define PLL_OUTCTRL BIT(0)
|
||||
+#define PLL_BYPASSNL BIT(1)
|
||||
+#define PLL_RESET_N BIT(2)
|
||||
+
|
||||
+/* Initialize a HFPLL at a given rate and enable it. */
|
||||
+static void __clk_hfpll_init_once(struct clk_hw *hw)
|
||||
+{
|
||||
+ struct clk_hfpll *h = to_clk_hfpll(hw);
|
||||
+ struct hfpll_data const *hd = h->d;
|
||||
+ struct regmap *regmap = h->clkr.regmap;
|
||||
+
|
||||
+ if (likely(h->init_done))
|
||||
+ return;
|
||||
+
|
||||
+ /* Configure PLL parameters for integer mode. */
|
||||
+ if (hd->config_val)
|
||||
+ regmap_write(regmap, hd->config_reg, hd->config_val);
|
||||
+ regmap_write(regmap, hd->m_reg, 0);
|
||||
+ regmap_write(regmap, hd->n_reg, 1);
|
||||
+
|
||||
+ if (hd->user_reg) {
|
||||
+ u32 regval = hd->user_val;
|
||||
+ unsigned long rate;
|
||||
+
|
||||
+ rate = clk_hw_get_rate(hw);
|
||||
+
|
||||
+ /* Pick the right VCO. */
|
||||
+ if (hd->user_vco_mask && rate > hd->low_vco_max_rate)
|
||||
+ regval |= hd->user_vco_mask;
|
||||
+ regmap_write(regmap, hd->user_reg, regval);
|
||||
+ }
|
||||
+
|
||||
+ if (hd->droop_reg)
|
||||
+ regmap_write(regmap, hd->droop_reg, hd->droop_val);
|
||||
+
|
||||
+ h->init_done = true;
|
||||
+}
|
||||
+
|
||||
+static void __clk_hfpll_enable(struct clk_hw *hw)
|
||||
+{
|
||||
+ struct clk_hfpll *h = to_clk_hfpll(hw);
|
||||
+ struct hfpll_data const *hd = h->d;
|
||||
+ struct regmap *regmap = h->clkr.regmap;
|
||||
+ u32 val;
|
||||
+
|
||||
+ __clk_hfpll_init_once(hw);
|
||||
+
|
||||
+ /* Disable PLL bypass mode. */
|
||||
+ regmap_update_bits(regmap, hd->mode_reg, PLL_BYPASSNL, PLL_BYPASSNL);
|
||||
+
|
||||
+ /*
|
||||
+ * H/W requires a 5us delay between disabling the bypass and
|
||||
+ * de-asserting the reset. Delay 10us just to be safe.
|
||||
+ */
|
||||
+ udelay(10);
|
||||
+
|
||||
+ /* De-assert active-low PLL reset. */
|
||||
+ regmap_update_bits(regmap, hd->mode_reg, PLL_RESET_N, PLL_RESET_N);
|
||||
+
|
||||
+ /* Wait for PLL to lock. */
|
||||
+ if (hd->status_reg) {
|
||||
+ do {
|
||||
+ regmap_read(regmap, hd->status_reg, &val);
|
||||
+ } while (!(val & BIT(hd->lock_bit)));
|
||||
+ } else {
|
||||
+ udelay(60);
|
||||
+ }
|
||||
+
|
||||
+ /* Enable PLL output. */
|
||||
+ regmap_update_bits(regmap, hd->mode_reg, PLL_OUTCTRL, PLL_OUTCTRL);
|
||||
+}
|
||||
+
|
||||
+/* Enable an already-configured HFPLL. */
|
||||
+static int clk_hfpll_enable(struct clk_hw *hw)
|
||||
+{
|
||||
+ unsigned long flags;
|
||||
+ struct clk_hfpll *h = to_clk_hfpll(hw);
|
||||
+ struct hfpll_data const *hd = h->d;
|
||||
+ struct regmap *regmap = h->clkr.regmap;
|
||||
+ u32 mode;
|
||||
+
|
||||
+ spin_lock_irqsave(&h->lock, flags);
|
||||
+ regmap_read(regmap, hd->mode_reg, &mode);
|
||||
+ if (!(mode & (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)))
|
||||
+ __clk_hfpll_enable(hw);
|
||||
+ spin_unlock_irqrestore(&h->lock, flags);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void __clk_hfpll_disable(struct clk_hfpll *h)
|
||||
+{
|
||||
+ struct hfpll_data const *hd = h->d;
|
||||
+ struct regmap *regmap = h->clkr.regmap;
|
||||
+
|
||||
+ /*
|
||||
+ * Disable the PLL output, disable test mode, enable the bypass mode,
|
||||
+ * and assert the reset.
|
||||
+ */
|
||||
+ regmap_update_bits(regmap, hd->mode_reg,
|
||||
+ PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL, 0);
|
||||
+}
|
||||
+
|
||||
+static void clk_hfpll_disable(struct clk_hw *hw)
|
||||
+{
|
||||
+ struct clk_hfpll *h = to_clk_hfpll(hw);
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ spin_lock_irqsave(&h->lock, flags);
|
||||
+ __clk_hfpll_disable(h);
|
||||
+ spin_unlock_irqrestore(&h->lock, flags);
|
||||
+}
|
||||
+
|
||||
+static long clk_hfpll_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
+ unsigned long *parent_rate)
|
||||
+{
|
||||
+ struct clk_hfpll *h = to_clk_hfpll(hw);
|
||||
+ struct hfpll_data const *hd = h->d;
|
||||
+ unsigned long rrate;
|
||||
+
|
||||
+ rate = clamp(rate, hd->min_rate, hd->max_rate);
|
||||
+
|
||||
+ rrate = DIV_ROUND_UP(rate, *parent_rate) * *parent_rate;
|
||||
+ if (rrate > hd->max_rate)
|
||||
+ rrate -= *parent_rate;
|
||||
+
|
||||
+ return rrate;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * For optimization reasons, assumes no downstream clocks are actively using
|
||||
+ * it.
|
||||
+ */
|
||||
+static int clk_hfpll_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
+ unsigned long parent_rate)
|
||||
+{
|
||||
+ struct clk_hfpll *h = to_clk_hfpll(hw);
|
||||
+ struct hfpll_data const *hd = h->d;
|
||||
+ struct regmap *regmap = h->clkr.regmap;
|
||||
+ unsigned long flags;
|
||||
+ u32 l_val, val;
|
||||
+ bool enabled;
|
||||
+
|
||||
+ l_val = rate / parent_rate;
|
||||
+
|
||||
+ spin_lock_irqsave(&h->lock, flags);
|
||||
+
|
||||
+ enabled = __clk_is_enabled(hw->clk);
|
||||
+ if (enabled)
|
||||
+ __clk_hfpll_disable(h);
|
||||
+
|
||||
+ /* Pick the right VCO. */
|
||||
+ if (hd->user_reg && hd->user_vco_mask) {
|
||||
+ regmap_read(regmap, hd->user_reg, &val);
|
||||
+ if (rate <= hd->low_vco_max_rate)
|
||||
+ val &= ~hd->user_vco_mask;
|
||||
+ else
|
||||
+ val |= hd->user_vco_mask;
|
||||
+ regmap_write(regmap, hd->user_reg, val);
|
||||
+ }
|
||||
+
|
||||
+ regmap_write(regmap, hd->l_reg, l_val);
|
||||
+
|
||||
+ if (enabled)
|
||||
+ __clk_hfpll_enable(hw);
|
||||
+
|
||||
+ spin_unlock_irqrestore(&h->lock, flags);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static unsigned long clk_hfpll_recalc_rate(struct clk_hw *hw,
|
||||
+ unsigned long parent_rate)
|
||||
+{
|
||||
+ struct clk_hfpll *h = to_clk_hfpll(hw);
|
||||
+ struct hfpll_data const *hd = h->d;
|
||||
+ struct regmap *regmap = h->clkr.regmap;
|
||||
+ u32 l_val;
|
||||
+
|
||||
+ regmap_read(regmap, hd->l_reg, &l_val);
|
||||
+
|
||||
+ return l_val * parent_rate;
|
||||
+}
|
||||
+
|
||||
+static void clk_hfpll_init(struct clk_hw *hw)
|
||||
+{
|
||||
+ struct clk_hfpll *h = to_clk_hfpll(hw);
|
||||
+ struct hfpll_data const *hd = h->d;
|
||||
+ struct regmap *regmap = h->clkr.regmap;
|
||||
+ u32 mode, status;
|
||||
+
|
||||
+ regmap_read(regmap, hd->mode_reg, &mode);
|
||||
+ if (mode != (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)) {
|
||||
+ __clk_hfpll_init_once(hw);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (hd->status_reg) {
|
||||
+ regmap_read(regmap, hd->status_reg, &status);
|
||||
+ if (!(status & BIT(hd->lock_bit))) {
|
||||
+ WARN(1, "HFPLL %s is ON, but not locked!\n",
|
||||
+ __clk_get_name(hw->clk));
|
||||
+ clk_hfpll_disable(hw);
|
||||
+ __clk_hfpll_init_once(hw);
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int hfpll_is_enabled(struct clk_hw *hw)
|
||||
+{
|
||||
+ struct clk_hfpll *h = to_clk_hfpll(hw);
|
||||
+ struct hfpll_data const *hd = h->d;
|
||||
+ struct regmap *regmap = h->clkr.regmap;
|
||||
+ u32 mode;
|
||||
+
|
||||
+ regmap_read(regmap, hd->mode_reg, &mode);
|
||||
+ mode &= 0x7;
|
||||
+ return mode == (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL);
|
||||
+}
|
||||
+
|
||||
+const struct clk_ops clk_ops_hfpll = {
|
||||
+ .enable = clk_hfpll_enable,
|
||||
+ .disable = clk_hfpll_disable,
|
||||
+ .is_enabled = hfpll_is_enabled,
|
||||
+ .round_rate = clk_hfpll_round_rate,
|
||||
+ .set_rate = clk_hfpll_set_rate,
|
||||
+ .recalc_rate = clk_hfpll_recalc_rate,
|
||||
+ .init = clk_hfpll_init,
|
||||
+};
|
||||
+EXPORT_SYMBOL_GPL(clk_ops_hfpll);
|
||||
--- /dev/null
|
||||
+++ b/drivers/clk/qcom/clk-hfpll.h
|
||||
@@ -0,0 +1,54 @@
|
||||
+/*
|
||||
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License version 2 and
|
||||
+ * only version 2 as published by the Free Software Foundation.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ */
|
||||
+#ifndef __QCOM_CLK_HFPLL_H__
|
||||
+#define __QCOM_CLK_HFPLL_H__
|
||||
+
|
||||
+#include <linux/clk-provider.h>
|
||||
+#include <linux/spinlock.h>
|
||||
+#include "clk-regmap.h"
|
||||
+
|
||||
+struct hfpll_data {
|
||||
+ u32 mode_reg;
|
||||
+ u32 l_reg;
|
||||
+ u32 m_reg;
|
||||
+ u32 n_reg;
|
||||
+ u32 user_reg;
|
||||
+ u32 droop_reg;
|
||||
+ u32 config_reg;
|
||||
+ u32 status_reg;
|
||||
+ u8 lock_bit;
|
||||
+
|
||||
+ u32 droop_val;
|
||||
+ u32 config_val;
|
||||
+ u32 user_val;
|
||||
+ u32 user_vco_mask;
|
||||
+ unsigned long low_vco_max_rate;
|
||||
+
|
||||
+ unsigned long min_rate;
|
||||
+ unsigned long max_rate;
|
||||
+};
|
||||
+
|
||||
+struct clk_hfpll {
|
||||
+ struct hfpll_data const *d;
|
||||
+ int init_done;
|
||||
+
|
||||
+ struct clk_regmap clkr;
|
||||
+ spinlock_t lock;
|
||||
+};
|
||||
+
|
||||
+#define to_clk_hfpll(_hw) \
|
||||
+ container_of(to_clk_regmap(_hw), struct clk_hfpll, clkr)
|
||||
+
|
||||
+extern const struct clk_ops clk_ops_hfpll;
|
||||
+
|
||||
+#endif
|
|
@ -0,0 +1,195 @@
|
|||
From 23f680d03e5894f494572a5162d21328bd86890c Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Date: Fri, 20 Mar 2015 23:45:25 -0700
|
||||
Subject: [PATCH 39/69] clk: qcom: Add HFPLL driver
|
||||
|
||||
On some devices (MSM8974 for example), the HFPLLs are
|
||||
instantiated within the Krait processor subsystem as separate
|
||||
register regions. Add a driver for these PLLs so that we can
|
||||
provide HFPLL clocks for use by the system.
|
||||
|
||||
Cc: <devicetree@vger.kernel.org>
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
---
|
||||
.../devicetree/bindings/clock/qcom,hfpll.txt | 40 ++++++++
|
||||
drivers/clk/qcom/Kconfig | 8 ++
|
||||
drivers/clk/qcom/Makefile | 1 +
|
||||
drivers/clk/qcom/hfpll.c | 106 +++++++++++++++++++++
|
||||
4 files changed, 155 insertions(+)
|
||||
create mode 100644 Documentation/devicetree/bindings/clock/qcom,hfpll.txt
|
||||
create mode 100644 drivers/clk/qcom/hfpll.c
|
||||
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/clock/qcom,hfpll.txt
|
||||
@@ -0,0 +1,40 @@
|
||||
+High-Frequency PLL (HFPLL)
|
||||
+
|
||||
+PROPERTIES
|
||||
+
|
||||
+- compatible:
|
||||
+ Usage: required
|
||||
+ Value type: <string>
|
||||
+ Definition: must be "qcom,hfpll"
|
||||
+
|
||||
+- reg:
|
||||
+ Usage: required
|
||||
+ Value type: <prop-encoded-array>
|
||||
+ Definition: address and size of HPLL registers. An optional second
|
||||
+ element specifies the address and size of the alias
|
||||
+ register region.
|
||||
+
|
||||
+- clock-output-names:
|
||||
+ Usage: required
|
||||
+ Value type: <string>
|
||||
+ Definition: Name of the PLL. Typically hfpllX where X is a CPU number
|
||||
+ starting at 0. Otherwise hfpll_Y where Y is more specific
|
||||
+ such as "l2".
|
||||
+
|
||||
+Example:
|
||||
+
|
||||
+1) An HFPLL for the L2 cache.
|
||||
+
|
||||
+ clock-controller@f9016000 {
|
||||
+ compatible = "qcom,hfpll";
|
||||
+ reg = <0xf9016000 0x30>;
|
||||
+ clock-output-names = "hfpll_l2";
|
||||
+ };
|
||||
+
|
||||
+2) An HFPLL for CPU0. This HFPLL has the alias register region.
|
||||
+
|
||||
+ clock-controller@f908a000 {
|
||||
+ compatible = "qcom,hfpll";
|
||||
+ reg = <0xf908a000 0x30>, <0xf900a000 0x30>;
|
||||
+ clock-output-names = "hfpll0";
|
||||
+ };
|
||||
--- a/drivers/clk/qcom/Kconfig
|
||||
+++ b/drivers/clk/qcom/Kconfig
|
||||
@@ -179,3 +179,11 @@ config MSM_MMCC_8996
|
||||
Support for the multimedia clock controller on msm8996 devices.
|
||||
Say Y if you want to support multimedia devices such as display,
|
||||
graphics, video encode/decode, camera, etc.
|
||||
+
|
||||
+config QCOM_HFPLL
|
||||
+ tristate "High-Frequency PLL (HFPLL) Clock Controller"
|
||||
+ depends on COMMON_CLK_QCOM
|
||||
+ help
|
||||
+ Support for the high-frequency PLLs present on Qualcomm devices.
|
||||
+ Say Y if you want to support CPU frequency scaling on devices
|
||||
+ such as MSM8974, APQ8084, etc.
|
||||
--- a/drivers/clk/qcom/Makefile
|
||||
+++ b/drivers/clk/qcom/Makefile
|
||||
@@ -32,3 +32,4 @@ obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8
|
||||
obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o
|
||||
obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o
|
||||
obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
|
||||
+obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/clk/qcom/hfpll.c
|
||||
@@ -0,0 +1,106 @@
|
||||
+/*
|
||||
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License version 2 and
|
||||
+ * only version 2 as published by the Free Software Foundation.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/clk.h>
|
||||
+#include <linux/clk-provider.h>
|
||||
+#include <linux/regmap.h>
|
||||
+
|
||||
+#include "clk-regmap.h"
|
||||
+#include "clk-hfpll.h"
|
||||
+
|
||||
+static const struct hfpll_data hdata = {
|
||||
+ .mode_reg = 0x00,
|
||||
+ .l_reg = 0x04,
|
||||
+ .m_reg = 0x08,
|
||||
+ .n_reg = 0x0c,
|
||||
+ .user_reg = 0x10,
|
||||
+ .config_reg = 0x14,
|
||||
+ .config_val = 0x430405d,
|
||||
+ .status_reg = 0x1c,
|
||||
+ .lock_bit = 16,
|
||||
+
|
||||
+ .user_val = 0x8,
|
||||
+ .user_vco_mask = 0x100000,
|
||||
+ .low_vco_max_rate = 1248000000,
|
||||
+ .min_rate = 537600000UL,
|
||||
+ .max_rate = 2900000000UL,
|
||||
+};
|
||||
+
|
||||
+static const struct of_device_id qcom_hfpll_match_table[] = {
|
||||
+ { .compatible = "qcom,hfpll" },
|
||||
+ { }
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, qcom_hfpll_match_table);
|
||||
+
|
||||
+static const struct regmap_config hfpll_regmap_config = {
|
||||
+ .reg_bits = 32,
|
||||
+ .reg_stride = 4,
|
||||
+ .val_bits = 32,
|
||||
+ .max_register = 0x30,
|
||||
+ .fast_io = true,
|
||||
+};
|
||||
+
|
||||
+static int qcom_hfpll_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct resource *res;
|
||||
+ struct device *dev = &pdev->dev;
|
||||
+ void __iomem *base;
|
||||
+ struct regmap *regmap;
|
||||
+ struct clk_hfpll *h;
|
||||
+ struct clk_init_data init = {
|
||||
+ .parent_names = (const char *[]){ "xo" },
|
||||
+ .num_parents = 1,
|
||||
+ .ops = &clk_ops_hfpll,
|
||||
+ };
|
||||
+
|
||||
+ h = devm_kzalloc(dev, sizeof(*h), GFP_KERNEL);
|
||||
+ if (!h)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
+ base = devm_ioremap_resource(dev, res);
|
||||
+ if (IS_ERR(base))
|
||||
+ return PTR_ERR(base);
|
||||
+
|
||||
+ regmap = devm_regmap_init_mmio(&pdev->dev, base, &hfpll_regmap_config);
|
||||
+ if (IS_ERR(regmap))
|
||||
+ return PTR_ERR(regmap);
|
||||
+
|
||||
+ if (of_property_read_string_index(dev->of_node, "clock-output-names",
|
||||
+ 0, &init.name))
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ h->d = &hdata;
|
||||
+ h->clkr.hw.init = &init;
|
||||
+ spin_lock_init(&h->lock);
|
||||
+
|
||||
+ return devm_clk_register_regmap(&pdev->dev, &h->clkr);
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver qcom_hfpll_driver = {
|
||||
+ .probe = qcom_hfpll_probe,
|
||||
+ .driver = {
|
||||
+ .name = "qcom-hfpll",
|
||||
+ .of_match_table = qcom_hfpll_match_table,
|
||||
+ },
|
||||
+};
|
||||
+module_platform_driver(qcom_hfpll_driver);
|
||||
+
|
||||
+MODULE_DESCRIPTION("QCOM HFPLL Clock Driver");
|
||||
+MODULE_LICENSE("GPL v2");
|
||||
+MODULE_ALIAS("platform:qcom-hfpll");
|
|
@ -0,0 +1,118 @@
|
|||
From 0dfdf84ee3982e88a62123b3de1c094d2c0829af Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Date: Fri, 20 Mar 2015 23:45:27 -0700
|
||||
Subject: [PATCH 40/69] clk: qcom: Add IPQ806X's HFPLLs
|
||||
|
||||
Describe the HFPLLs present on IPQ806X devices.
|
||||
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
---
|
||||
drivers/clk/qcom/gcc-ipq806x.c | 83 ++++++++++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 83 insertions(+)
|
||||
|
||||
--- a/drivers/clk/qcom/gcc-ipq806x.c
|
||||
+++ b/drivers/clk/qcom/gcc-ipq806x.c
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "clk-pll.h"
|
||||
#include "clk-rcg.h"
|
||||
#include "clk-branch.h"
|
||||
+#include "clk-hfpll.h"
|
||||
#include "reset.h"
|
||||
|
||||
static struct clk_pll pll0 = {
|
||||
@@ -113,6 +114,85 @@ static struct clk_regmap pll8_vote = {
|
||||
},
|
||||
};
|
||||
|
||||
+static struct hfpll_data hfpll0_data = {
|
||||
+ .mode_reg = 0x3200,
|
||||
+ .l_reg = 0x3208,
|
||||
+ .m_reg = 0x320c,
|
||||
+ .n_reg = 0x3210,
|
||||
+ .config_reg = 0x3204,
|
||||
+ .status_reg = 0x321c,
|
||||
+ .config_val = 0x7845c665,
|
||||
+ .droop_reg = 0x3214,
|
||||
+ .droop_val = 0x0108c000,
|
||||
+ .min_rate = 600000000UL,
|
||||
+ .max_rate = 1800000000UL,
|
||||
+};
|
||||
+
|
||||
+static struct clk_hfpll hfpll0 = {
|
||||
+ .d = &hfpll0_data,
|
||||
+ .clkr.hw.init = &(struct clk_init_data){
|
||||
+ .parent_names = (const char *[]){ "pxo" },
|
||||
+ .num_parents = 1,
|
||||
+ .name = "hfpll0",
|
||||
+ .ops = &clk_ops_hfpll,
|
||||
+ .flags = CLK_IGNORE_UNUSED,
|
||||
+ },
|
||||
+ .lock = __SPIN_LOCK_UNLOCKED(hfpll0.lock),
|
||||
+};
|
||||
+
|
||||
+static struct hfpll_data hfpll1_data = {
|
||||
+ .mode_reg = 0x3240,
|
||||
+ .l_reg = 0x3248,
|
||||
+ .m_reg = 0x324c,
|
||||
+ .n_reg = 0x3250,
|
||||
+ .config_reg = 0x3244,
|
||||
+ .status_reg = 0x325c,
|
||||
+ .config_val = 0x7845c665,
|
||||
+ .droop_reg = 0x3314,
|
||||
+ .droop_val = 0x0108c000,
|
||||
+ .min_rate = 600000000UL,
|
||||
+ .max_rate = 1800000000UL,
|
||||
+};
|
||||
+
|
||||
+static struct clk_hfpll hfpll1 = {
|
||||
+ .d = &hfpll1_data,
|
||||
+ .clkr.hw.init = &(struct clk_init_data){
|
||||
+ .parent_names = (const char *[]){ "pxo" },
|
||||
+ .num_parents = 1,
|
||||
+ .name = "hfpll1",
|
||||
+ .ops = &clk_ops_hfpll,
|
||||
+ .flags = CLK_IGNORE_UNUSED,
|
||||
+ },
|
||||
+ .lock = __SPIN_LOCK_UNLOCKED(hfpll1.lock),
|
||||
+};
|
||||
+
|
||||
+static struct hfpll_data hfpll_l2_data = {
|
||||
+ .mode_reg = 0x3300,
|
||||
+ .l_reg = 0x3308,
|
||||
+ .m_reg = 0x330c,
|
||||
+ .n_reg = 0x3310,
|
||||
+ .config_reg = 0x3304,
|
||||
+ .status_reg = 0x331c,
|
||||
+ .config_val = 0x7845c665,
|
||||
+ .droop_reg = 0x3314,
|
||||
+ .droop_val = 0x0108c000,
|
||||
+ .min_rate = 600000000UL,
|
||||
+ .max_rate = 1800000000UL,
|
||||
+};
|
||||
+
|
||||
+static struct clk_hfpll hfpll_l2 = {
|
||||
+ .d = &hfpll_l2_data,
|
||||
+ .clkr.hw.init = &(struct clk_init_data){
|
||||
+ .parent_names = (const char *[]){ "pxo" },
|
||||
+ .num_parents = 1,
|
||||
+ .name = "hfpll_l2",
|
||||
+ .ops = &clk_ops_hfpll,
|
||||
+ .flags = CLK_IGNORE_UNUSED,
|
||||
+ },
|
||||
+ .lock = __SPIN_LOCK_UNLOCKED(hfpll_l2.lock),
|
||||
+};
|
||||
+
|
||||
+
|
||||
static struct clk_pll pll14 = {
|
||||
.l_reg = 0x31c4,
|
||||
.m_reg = 0x31c8,
|
||||
@@ -2801,6 +2881,9 @@ static struct clk_regmap *gcc_ipq806x_cl
|
||||
[UBI32_CORE2_CLK_SRC] = &ubi32_core2_src_clk.clkr,
|
||||
[NSSTCM_CLK_SRC] = &nss_tcm_src.clkr,
|
||||
[NSSTCM_CLK] = &nss_tcm_clk.clkr,
|
||||
+ [PLL9] = &hfpll0.clkr,
|
||||
+ [PLL10] = &hfpll1.clkr,
|
||||
+ [PLL12] = &hfpll_l2.clkr,
|
||||
};
|
||||
|
||||
static const struct qcom_reset_map gcc_ipq806x_resets[] = {
|
|
@ -0,0 +1,263 @@
|
|||
From b9747125a8e7633ed2701a70e32dbb0442193774 Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Date: Fri, 20 Mar 2015 23:45:28 -0700
|
||||
Subject: [PATCH 41/69] clk: qcom: Add support for Krait clocks
|
||||
|
||||
The Krait clocks are made up of a series of muxes and a divider
|
||||
that choose between a fixed rate clock and dedicated HFPLLs for
|
||||
each CPU. Instead of using mmio accesses to remux parents, the
|
||||
Krait implementation exposes the remux control via cp15
|
||||
registers. Support these clocks.
|
||||
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
---
|
||||
drivers/clk/qcom/Kconfig | 4 ++
|
||||
drivers/clk/qcom/Makefile | 1 +
|
||||
drivers/clk/qcom/clk-krait.c | 167 +++++++++++++++++++++++++++++++++++++++++++
|
||||
drivers/clk/qcom/clk-krait.h | 49 +++++++++++++
|
||||
4 files changed, 221 insertions(+)
|
||||
create mode 100644 drivers/clk/qcom/clk-krait.c
|
||||
create mode 100644 drivers/clk/qcom/clk-krait.h
|
||||
|
||||
--- a/drivers/clk/qcom/Kconfig
|
||||
+++ b/drivers/clk/qcom/Kconfig
|
||||
@@ -187,3 +187,7 @@ config QCOM_HFPLL
|
||||
Support for the high-frequency PLLs present on Qualcomm devices.
|
||||
Say Y if you want to support CPU frequency scaling on devices
|
||||
such as MSM8974, APQ8084, etc.
|
||||
+
|
||||
+config KRAIT_CLOCKS
|
||||
+ bool
|
||||
+ select KRAIT_L2_ACCESSORS
|
||||
--- a/drivers/clk/qcom/Makefile
|
||||
+++ b/drivers/clk/qcom/Makefile
|
||||
@@ -9,6 +9,7 @@ clk-qcom-y += clk-rcg2.o
|
||||
clk-qcom-y += clk-branch.o
|
||||
clk-qcom-y += clk-regmap-divider.o
|
||||
clk-qcom-y += clk-regmap-mux.o
|
||||
+clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o
|
||||
clk-qcom-y += clk-hfpll.o
|
||||
clk-qcom-y += reset.o
|
||||
clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/clk/qcom/clk-krait.c
|
||||
@@ -0,0 +1,167 @@
|
||||
+/*
|
||||
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License version 2 and
|
||||
+ * only version 2 as published by the Free Software Foundation.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/delay.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/clk-provider.h>
|
||||
+#include <linux/spinlock.h>
|
||||
+
|
||||
+#include <asm/krait-l2-accessors.h>
|
||||
+
|
||||
+#include "clk-krait.h"
|
||||
+
|
||||
+/* Secondary and primary muxes share the same cp15 register */
|
||||
+static DEFINE_SPINLOCK(krait_clock_reg_lock);
|
||||
+
|
||||
+#define LPL_SHIFT 8
|
||||
+static void __krait_mux_set_sel(struct krait_mux_clk *mux, int sel)
|
||||
+{
|
||||
+ unsigned long flags;
|
||||
+ u32 regval;
|
||||
+
|
||||
+ spin_lock_irqsave(&krait_clock_reg_lock, flags);
|
||||
+ regval = krait_get_l2_indirect_reg(mux->offset);
|
||||
+ regval &= ~(mux->mask << mux->shift);
|
||||
+ regval |= (sel & mux->mask) << mux->shift;
|
||||
+ if (mux->lpl) {
|
||||
+ regval &= ~(mux->mask << (mux->shift + LPL_SHIFT));
|
||||
+ regval |= (sel & mux->mask) << (mux->shift + LPL_SHIFT);
|
||||
+ }
|
||||
+ krait_set_l2_indirect_reg(mux->offset, regval);
|
||||
+ spin_unlock_irqrestore(&krait_clock_reg_lock, flags);
|
||||
+
|
||||
+ /* Wait for switch to complete. */
|
||||
+ mb();
|
||||
+ udelay(1);
|
||||
+}
|
||||
+
|
||||
+static int krait_mux_set_parent(struct clk_hw *hw, u8 index)
|
||||
+{
|
||||
+ struct krait_mux_clk *mux = to_krait_mux_clk(hw);
|
||||
+ u32 sel;
|
||||
+
|
||||
+ sel = clk_mux_reindex(index, mux->parent_map, 0);
|
||||
+ mux->en_mask = sel;
|
||||
+ /* Don't touch mux if CPU is off as it won't work */
|
||||
+ if (__clk_is_enabled(hw->clk))
|
||||
+ __krait_mux_set_sel(mux, sel);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static u8 krait_mux_get_parent(struct clk_hw *hw)
|
||||
+{
|
||||
+ struct krait_mux_clk *mux = to_krait_mux_clk(hw);
|
||||
+ u32 sel;
|
||||
+
|
||||
+ sel = krait_get_l2_indirect_reg(mux->offset);
|
||||
+ sel >>= mux->shift;
|
||||
+ sel &= mux->mask;
|
||||
+ mux->en_mask = sel;
|
||||
+
|
||||
+ return clk_mux_get_parent(hw, sel, mux->parent_map, 0);
|
||||
+}
|
||||
+
|
||||
+static struct clk_hw *krait_mux_get_safe_parent(struct clk_hw *hw,
|
||||
+ unsigned long *safe_freq)
|
||||
+{
|
||||
+ int i;
|
||||
+ struct krait_mux_clk *mux = to_krait_mux_clk(hw);
|
||||
+ int num_parents = clk_hw_get_num_parents(hw);
|
||||
+
|
||||
+ i = mux->safe_sel;
|
||||
+ for (i = 0; i < num_parents; i++)
|
||||
+ if (mux->safe_sel == mux->parent_map[i])
|
||||
+ break;
|
||||
+
|
||||
+ return clk_hw_get_parent_by_index(hw, i);
|
||||
+}
|
||||
+
|
||||
+static int krait_mux_enable(struct clk_hw *hw)
|
||||
+{
|
||||
+ struct krait_mux_clk *mux = to_krait_mux_clk(hw);
|
||||
+
|
||||
+ __krait_mux_set_sel(mux, mux->en_mask);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void krait_mux_disable(struct clk_hw *hw)
|
||||
+{
|
||||
+ struct krait_mux_clk *mux = to_krait_mux_clk(hw);
|
||||
+
|
||||
+ __krait_mux_set_sel(mux, mux->safe_sel);
|
||||
+}
|
||||
+
|
||||
+const struct clk_ops krait_mux_clk_ops = {
|
||||
+ .enable = krait_mux_enable,
|
||||
+ .disable = krait_mux_disable,
|
||||
+ .set_parent = krait_mux_set_parent,
|
||||
+ .get_parent = krait_mux_get_parent,
|
||||
+ .determine_rate = __clk_mux_determine_rate_closest,
|
||||
+ .get_safe_parent = krait_mux_get_safe_parent,
|
||||
+};
|
||||
+EXPORT_SYMBOL_GPL(krait_mux_clk_ops);
|
||||
+
|
||||
+/* The divider can divide by 2, 4, 6 and 8. But we only really need div-2. */
|
||||
+static long krait_div2_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
+ unsigned long *parent_rate)
|
||||
+{
|
||||
+ *parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), rate * 2);
|
||||
+ return DIV_ROUND_UP(*parent_rate, 2);
|
||||
+}
|
||||
+
|
||||
+static int krait_div2_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
+ unsigned long parent_rate)
|
||||
+{
|
||||
+ struct krait_div2_clk *d = to_krait_div2_clk(hw);
|
||||
+ unsigned long flags;
|
||||
+ u32 val;
|
||||
+ u32 mask = BIT(d->width) - 1;
|
||||
+
|
||||
+ if (d->lpl)
|
||||
+ mask = mask << (d->shift + LPL_SHIFT) | mask << d->shift;
|
||||
+
|
||||
+ spin_lock_irqsave(&krait_clock_reg_lock, flags);
|
||||
+ val = krait_get_l2_indirect_reg(d->offset);
|
||||
+ val &= ~mask;
|
||||
+ krait_set_l2_indirect_reg(d->offset, val);
|
||||
+ spin_unlock_irqrestore(&krait_clock_reg_lock, flags);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static unsigned long
|
||||
+krait_div2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
|
||||
+{
|
||||
+ struct krait_div2_clk *d = to_krait_div2_clk(hw);
|
||||
+ u32 mask = BIT(d->width) - 1;
|
||||
+ u32 div;
|
||||
+
|
||||
+ div = krait_get_l2_indirect_reg(d->offset);
|
||||
+ div >>= d->shift;
|
||||
+ div &= mask;
|
||||
+ div = (div + 1) * 2;
|
||||
+
|
||||
+ return DIV_ROUND_UP(parent_rate, div);
|
||||
+}
|
||||
+
|
||||
+const struct clk_ops krait_div2_clk_ops = {
|
||||
+ .round_rate = krait_div2_round_rate,
|
||||
+ .set_rate = krait_div2_set_rate,
|
||||
+ .recalc_rate = krait_div2_recalc_rate,
|
||||
+};
|
||||
+EXPORT_SYMBOL_GPL(krait_div2_clk_ops);
|
||||
--- /dev/null
|
||||
+++ b/drivers/clk/qcom/clk-krait.h
|
||||
@@ -0,0 +1,49 @@
|
||||
+/*
|
||||
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License version 2 and
|
||||
+ * only version 2 as published by the Free Software Foundation.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ */
|
||||
+
|
||||
+#ifndef __QCOM_CLK_KRAIT_H
|
||||
+#define __QCOM_CLK_KRAIT_H
|
||||
+
|
||||
+#include <linux/clk-provider.h>
|
||||
+
|
||||
+struct krait_mux_clk {
|
||||
+ unsigned int *parent_map;
|
||||
+ bool has_safe_parent;
|
||||
+ u8 safe_sel;
|
||||
+ u32 offset;
|
||||
+ u32 mask;
|
||||
+ u32 shift;
|
||||
+ u32 en_mask;
|
||||
+ bool lpl;
|
||||
+
|
||||
+ struct clk_hw hw;
|
||||
+};
|
||||
+
|
||||
+#define to_krait_mux_clk(_hw) container_of(_hw, struct krait_mux_clk, hw)
|
||||
+
|
||||
+extern const struct clk_ops krait_mux_clk_ops;
|
||||
+
|
||||
+struct krait_div2_clk {
|
||||
+ u32 offset;
|
||||
+ u8 width;
|
||||
+ u32 shift;
|
||||
+ bool lpl;
|
||||
+
|
||||
+ struct clk_hw hw;
|
||||
+};
|
||||
+
|
||||
+#define to_krait_div2_clk(_hw) container_of(_hw, struct krait_div2_clk, hw)
|
||||
+
|
||||
+extern const struct clk_ops krait_div2_clk_ops;
|
||||
+
|
||||
+#endif
|
|
@ -0,0 +1,196 @@
|
|||
From 6039eb63fabdd6871fc70940aa98102665c78eed Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Date: Fri, 20 Mar 2015 23:45:29 -0700
|
||||
Subject: [PATCH 42/69] clk: qcom: Add KPSS ACC/GCC driver
|
||||
|
||||
The ACC and GCC regions present in KPSSv1 contain registers to
|
||||
control clocks and power to each Krait CPU and L2. For CPUfreq
|
||||
purposes probe these devices and expose a mux clock that chooses
|
||||
between PXO and PLL8.
|
||||
|
||||
Cc: <devicetree@vger.kernel.org>
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
---
|
||||
.../devicetree/bindings/arm/msm/qcom,kpss-acc.txt | 7 ++
|
||||
.../devicetree/bindings/arm/msm/qcom,kpss-gcc.txt | 28 +++++++
|
||||
drivers/clk/qcom/Kconfig | 8 ++
|
||||
drivers/clk/qcom/Makefile | 1 +
|
||||
drivers/clk/qcom/kpss-xcc.c | 95 ++++++++++++++++++++++
|
||||
5 files changed, 139 insertions(+)
|
||||
create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt
|
||||
create mode 100644 drivers/clk/qcom/kpss-xcc.c
|
||||
|
||||
--- a/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt
|
||||
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt
|
||||
@@ -21,10 +21,17 @@ PROPERTIES
|
||||
the register region. An optional second element specifies
|
||||
the base address and size of the alias register region.
|
||||
|
||||
+- clock-output-names:
|
||||
+ Usage: optional
|
||||
+ Value type: <string>
|
||||
+ Definition: Name of the output clock. Typically acpuX_aux where X is a
|
||||
+ CPU number starting at 0.
|
||||
+
|
||||
Example:
|
||||
|
||||
clock-controller@2088000 {
|
||||
compatible = "qcom,kpss-acc-v2";
|
||||
reg = <0x02088000 0x1000>,
|
||||
<0x02008000 0x1000>;
|
||||
+ clock-output-names = "acpu0_aux";
|
||||
};
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt
|
||||
@@ -0,0 +1,28 @@
|
||||
+Krait Processor Sub-system (KPSS) Global Clock Controller (GCC)
|
||||
+
|
||||
+PROPERTIES
|
||||
+
|
||||
+- compatible:
|
||||
+ Usage: required
|
||||
+ Value type: <string>
|
||||
+ Definition: should be one of:
|
||||
+ "qcom,kpss-gcc"
|
||||
+
|
||||
+- reg:
|
||||
+ Usage: required
|
||||
+ Value type: <prop-encoded-array>
|
||||
+ Definition: base address and size of the register region
|
||||
+
|
||||
+- clock-output-names:
|
||||
+ Usage: required
|
||||
+ Value type: <string>
|
||||
+ Definition: Name of the output clock. Typically acpu_l2_aux indicating
|
||||
+ an L2 cache auxiliary clock.
|
||||
+
|
||||
+Example:
|
||||
+
|
||||
+ l2cc: clock-controller@2011000 {
|
||||
+ compatible = "qcom,kpss-gcc";
|
||||
+ reg = <0x2011000 0x1000>;
|
||||
+ clock-output-names = "acpu_l2_aux";
|
||||
+ };
|
||||
--- a/drivers/clk/qcom/Kconfig
|
||||
+++ b/drivers/clk/qcom/Kconfig
|
||||
@@ -188,6 +188,14 @@ config QCOM_HFPLL
|
||||
Say Y if you want to support CPU frequency scaling on devices
|
||||
such as MSM8974, APQ8084, etc.
|
||||
|
||||
+config KPSS_XCC
|
||||
+ tristate "KPSS Clock Controller"
|
||||
+ depends on COMMON_CLK_QCOM
|
||||
+ help
|
||||
+ Support for the Krait ACC and GCC clock controllers. Say Y
|
||||
+ if you want to support CPU frequency scaling on devices such
|
||||
+ as MSM8960, APQ8064, etc.
|
||||
+
|
||||
config KRAIT_CLOCKS
|
||||
bool
|
||||
select KRAIT_L2_ACCESSORS
|
||||
--- a/drivers/clk/qcom/Makefile
|
||||
+++ b/drivers/clk/qcom/Makefile
|
||||
@@ -33,4 +33,5 @@ obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8
|
||||
obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o
|
||||
obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o
|
||||
obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
|
||||
+obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o
|
||||
obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/clk/qcom/kpss-xcc.c
|
||||
@@ -0,0 +1,95 @@
|
||||
+/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License version 2 and
|
||||
+ * only version 2 as published by the Free Software Foundation.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/of_device.h>
|
||||
+#include <linux/clk.h>
|
||||
+#include <linux/clk-provider.h>
|
||||
+
|
||||
+static const char *aux_parents[] = {
|
||||
+ "pll8_vote",
|
||||
+ "pxo",
|
||||
+};
|
||||
+
|
||||
+static unsigned int aux_parent_map[] = {
|
||||
+ 3,
|
||||
+ 0,
|
||||
+};
|
||||
+
|
||||
+static const struct of_device_id kpss_xcc_match_table[] = {
|
||||
+ { .compatible = "qcom,kpss-acc-v1", .data = (void *)1UL },
|
||||
+ { .compatible = "qcom,kpss-gcc" },
|
||||
+ {}
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, kpss_xcc_match_table);
|
||||
+
|
||||
+static int kpss_xcc_driver_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ const struct of_device_id *id;
|
||||
+ struct clk *clk;
|
||||
+ struct resource *res;
|
||||
+ void __iomem *base;
|
||||
+ const char *name;
|
||||
+
|
||||
+ id = of_match_device(kpss_xcc_match_table, &pdev->dev);
|
||||
+ if (!id)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
+ base = devm_ioremap_resource(&pdev->dev, res);
|
||||
+ if (IS_ERR(base))
|
||||
+ return PTR_ERR(base);
|
||||
+
|
||||
+ if (id->data) {
|
||||
+ if (of_property_read_string_index(pdev->dev.of_node,
|
||||
+ "clock-output-names", 0, &name))
|
||||
+ return -ENODEV;
|
||||
+ base += 0x14;
|
||||
+ } else {
|
||||
+ name = "acpu_l2_aux";
|
||||
+ base += 0x28;
|
||||
+ }
|
||||
+
|
||||
+ clk = clk_register_mux_table(&pdev->dev, name, aux_parents,
|
||||
+ ARRAY_SIZE(aux_parents), 0, base, 0, 0x3,
|
||||
+ 0, aux_parent_map, NULL);
|
||||
+
|
||||
+ platform_set_drvdata(pdev, clk);
|
||||
+
|
||||
+ return PTR_ERR_OR_ZERO(clk);
|
||||
+}
|
||||
+
|
||||
+static int kpss_xcc_driver_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ clk_unregister_mux(platform_get_drvdata(pdev));
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver kpss_xcc_driver = {
|
||||
+ .probe = kpss_xcc_driver_probe,
|
||||
+ .remove = kpss_xcc_driver_remove,
|
||||
+ .driver = {
|
||||
+ .name = "kpss-xcc",
|
||||
+ .of_match_table = kpss_xcc_match_table,
|
||||
+ },
|
||||
+};
|
||||
+module_platform_driver(kpss_xcc_driver);
|
||||
+
|
||||
+MODULE_DESCRIPTION("Krait Processor Sub System (KPSS) Clock Driver");
|
||||
+MODULE_LICENSE("GPL v2");
|
||||
+MODULE_ALIAS("platform:kpss-xcc");
|
|
@ -0,0 +1,426 @@
|
|||
From 7fb5976eb0231a06f484a6bde5e5fbfee7ee4f4a Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Date: Fri, 20 Mar 2015 23:45:30 -0700
|
||||
Subject: [PATCH 43/69] clk: qcom: Add Krait clock controller driver
|
||||
|
||||
The Krait CPU clocks are made up of a primary mux and secondary
|
||||
mux for each CPU and the L2, controlled via cp15 accessors. For
|
||||
Kraits within KPSSv1 each secondary mux accepts a different aux
|
||||
source, but on KPSSv2 each secondary mux accepts the same aux
|
||||
source.
|
||||
|
||||
Cc: <devicetree@vger.kernel.org>
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
---
|
||||
.../devicetree/bindings/clock/qcom,krait-cc.txt | 22 ++
|
||||
drivers/clk/qcom/Kconfig | 8 +
|
||||
drivers/clk/qcom/Makefile | 1 +
|
||||
drivers/clk/qcom/krait-cc.c | 352 +++++++++++++++++++++
|
||||
4 files changed, 383 insertions(+)
|
||||
create mode 100644 Documentation/devicetree/bindings/clock/qcom,krait-cc.txt
|
||||
create mode 100644 drivers/clk/qcom/krait-cc.c
|
||||
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/clock/qcom,krait-cc.txt
|
||||
@@ -0,0 +1,22 @@
|
||||
+Krait Clock Controller
|
||||
+
|
||||
+PROPERTIES
|
||||
+
|
||||
+- compatible:
|
||||
+ Usage: required
|
||||
+ Value type: <string>
|
||||
+ Definition: must be one of:
|
||||
+ "qcom,krait-cc-v1"
|
||||
+ "qcom,krait-cc-v2"
|
||||
+
|
||||
+- #clock-cells:
|
||||
+ Usage: required
|
||||
+ Value type: <u32>
|
||||
+ Definition: must be 1
|
||||
+
|
||||
+Example:
|
||||
+
|
||||
+ kraitcc: clock-controller {
|
||||
+ compatible = "qcom,krait-cc-v1";
|
||||
+ #clock-cells = <1>;
|
||||
+ };
|
||||
--- a/drivers/clk/qcom/Kconfig
|
||||
+++ b/drivers/clk/qcom/Kconfig
|
||||
@@ -196,6 +196,14 @@ config KPSS_XCC
|
||||
if you want to support CPU frequency scaling on devices such
|
||||
as MSM8960, APQ8064, etc.
|
||||
|
||||
+config KRAITCC
|
||||
+ tristate "Krait Clock Controller"
|
||||
+ depends on COMMON_CLK_QCOM && ARM
|
||||
+ select KRAIT_CLOCKS
|
||||
+ help
|
||||
+ Support for the Krait CPU clocks on Qualcomm devices.
|
||||
+ Say Y if you want to support CPU frequency scaling.
|
||||
+
|
||||
config KRAIT_CLOCKS
|
||||
bool
|
||||
select KRAIT_L2_ACCESSORS
|
||||
--- a/drivers/clk/qcom/Makefile
|
||||
+++ b/drivers/clk/qcom/Makefile
|
||||
@@ -35,3 +35,4 @@ obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o
|
||||
obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
|
||||
obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o
|
||||
obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
|
||||
+obj-$(CONFIG_KRAITCC) += krait-cc.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/clk/qcom/krait-cc.c
|
||||
@@ -0,0 +1,352 @@
|
||||
+/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License version 2 and
|
||||
+ * only version 2 as published by the Free Software Foundation.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/of_device.h>
|
||||
+#include <linux/clk.h>
|
||||
+#include <linux/clk-provider.h>
|
||||
+#include <linux/slab.h>
|
||||
+
|
||||
+#include "clk-krait.h"
|
||||
+
|
||||
+static unsigned int sec_mux_map[] = {
|
||||
+ 2,
|
||||
+ 0,
|
||||
+};
|
||||
+
|
||||
+static unsigned int pri_mux_map[] = {
|
||||
+ 1,
|
||||
+ 2,
|
||||
+ 0,
|
||||
+};
|
||||
+
|
||||
+static int
|
||||
+krait_add_div(struct device *dev, int id, const char *s, unsigned offset)
|
||||
+{
|
||||
+ struct krait_div2_clk *div;
|
||||
+ struct clk_init_data init = {
|
||||
+ .num_parents = 1,
|
||||
+ .ops = &krait_div2_clk_ops,
|
||||
+ .flags = CLK_SET_RATE_PARENT,
|
||||
+ };
|
||||
+ const char *p_names[1];
|
||||
+ struct clk *clk;
|
||||
+
|
||||
+ div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
|
||||
+ if (!div)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ div->width = 2;
|
||||
+ div->shift = 6;
|
||||
+ div->lpl = id >= 0;
|
||||
+ div->offset = offset;
|
||||
+ div->hw.init = &init;
|
||||
+
|
||||
+ init.name = kasprintf(GFP_KERNEL, "hfpll%s_div", s);
|
||||
+ if (!init.name)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ init.parent_names = p_names;
|
||||
+ p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s);
|
||||
+ if (!p_names[0]) {
|
||||
+ kfree(init.name);
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+
|
||||
+ clk = devm_clk_register(dev, &div->hw);
|
||||
+ kfree(p_names[0]);
|
||||
+ kfree(init.name);
|
||||
+
|
||||
+ return PTR_ERR_OR_ZERO(clk);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+krait_add_sec_mux(struct device *dev, int id, const char *s, unsigned offset,
|
||||
+ bool unique_aux)
|
||||
+{
|
||||
+ struct krait_mux_clk *mux;
|
||||
+ static const char *sec_mux_list[] = {
|
||||
+ "acpu_aux",
|
||||
+ "qsb",
|
||||
+ };
|
||||
+ struct clk_init_data init = {
|
||||
+ .parent_names = sec_mux_list,
|
||||
+ .num_parents = ARRAY_SIZE(sec_mux_list),
|
||||
+ .ops = &krait_mux_clk_ops,
|
||||
+ .flags = CLK_SET_RATE_PARENT,
|
||||
+ };
|
||||
+ struct clk *clk;
|
||||
+
|
||||
+ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
|
||||
+ if (!mux)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ mux->offset = offset;
|
||||
+ mux->lpl = id >= 0;
|
||||
+ mux->has_safe_parent = true;
|
||||
+ mux->safe_sel = 2;
|
||||
+ mux->mask = 0x3;
|
||||
+ mux->shift = 2;
|
||||
+ mux->parent_map = sec_mux_map;
|
||||
+ mux->hw.init = &init;
|
||||
+
|
||||
+ init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
|
||||
+ if (!init.name)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ if (unique_aux) {
|
||||
+ sec_mux_list[0] = kasprintf(GFP_KERNEL, "acpu%s_aux", s);
|
||||
+ if (!sec_mux_list[0]) {
|
||||
+ clk = ERR_PTR(-ENOMEM);
|
||||
+ goto err_aux;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ clk = devm_clk_register(dev, &mux->hw);
|
||||
+
|
||||
+ if (unique_aux)
|
||||
+ kfree(sec_mux_list[0]);
|
||||
+err_aux:
|
||||
+ kfree(init.name);
|
||||
+ return PTR_ERR_OR_ZERO(clk);
|
||||
+}
|
||||
+
|
||||
+static struct clk *
|
||||
+krait_add_pri_mux(struct device *dev, int id, const char *s, unsigned offset)
|
||||
+{
|
||||
+ struct krait_mux_clk *mux;
|
||||
+ const char *p_names[3];
|
||||
+ struct clk_init_data init = {
|
||||
+ .parent_names = p_names,
|
||||
+ .num_parents = ARRAY_SIZE(p_names),
|
||||
+ .ops = &krait_mux_clk_ops,
|
||||
+ .flags = CLK_SET_RATE_PARENT,
|
||||
+ };
|
||||
+ struct clk *clk;
|
||||
+
|
||||
+ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
|
||||
+ if (!mux)
|
||||
+ return ERR_PTR(-ENOMEM);
|
||||
+
|
||||
+ mux->has_safe_parent = true;
|
||||
+ mux->safe_sel = 0;
|
||||
+ mux->mask = 0x3;
|
||||
+ mux->shift = 0;
|
||||
+ mux->offset = offset;
|
||||
+ mux->lpl = id >= 0;
|
||||
+ mux->parent_map = pri_mux_map;
|
||||
+ mux->hw.init = &init;
|
||||
+
|
||||
+ init.name = kasprintf(GFP_KERNEL, "krait%s_pri_mux", s);
|
||||
+ if (!init.name)
|
||||
+ return ERR_PTR(-ENOMEM);
|
||||
+
|
||||
+ p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s);
|
||||
+ if (!p_names[0]) {
|
||||
+ clk = ERR_PTR(-ENOMEM);
|
||||
+ goto err_p0;
|
||||
+ }
|
||||
+
|
||||
+ p_names[1] = kasprintf(GFP_KERNEL, "hfpll%s_div", s);
|
||||
+ if (!p_names[1]) {
|
||||
+ clk = ERR_PTR(-ENOMEM);
|
||||
+ goto err_p1;
|
||||
+ }
|
||||
+
|
||||
+ p_names[2] = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
|
||||
+ if (!p_names[2]) {
|
||||
+ clk = ERR_PTR(-ENOMEM);
|
||||
+ goto err_p2;
|
||||
+ }
|
||||
+
|
||||
+ clk = devm_clk_register(dev, &mux->hw);
|
||||
+
|
||||
+ kfree(p_names[2]);
|
||||
+err_p2:
|
||||
+ kfree(p_names[1]);
|
||||
+err_p1:
|
||||
+ kfree(p_names[0]);
|
||||
+err_p0:
|
||||
+ kfree(init.name);
|
||||
+ return clk;
|
||||
+}
|
||||
+
|
||||
+/* id < 0 for L2, otherwise id == physical CPU number */
|
||||
+static struct clk *krait_add_clks(struct device *dev, int id, bool unique_aux)
|
||||
+{
|
||||
+ int ret;
|
||||
+ unsigned offset;
|
||||
+ void *p = NULL;
|
||||
+ const char *s;
|
||||
+ struct clk *clk;
|
||||
+
|
||||
+ if (id >= 0) {
|
||||
+ offset = 0x4501 + (0x1000 * id);
|
||||
+ s = p = kasprintf(GFP_KERNEL, "%d", id);
|
||||
+ if (!s)
|
||||
+ return ERR_PTR(-ENOMEM);
|
||||
+ } else {
|
||||
+ offset = 0x500;
|
||||
+ s = "_l2";
|
||||
+ }
|
||||
+
|
||||
+ ret = krait_add_div(dev, id, s, offset);
|
||||
+ if (ret) {
|
||||
+ clk = ERR_PTR(ret);
|
||||
+ goto err;
|
||||
+ }
|
||||
+
|
||||
+ ret = krait_add_sec_mux(dev, id, s, offset, unique_aux);
|
||||
+ if (ret) {
|
||||
+ clk = ERR_PTR(ret);
|
||||
+ goto err;
|
||||
+ }
|
||||
+
|
||||
+ clk = krait_add_pri_mux(dev, id, s, offset);
|
||||
+err:
|
||||
+ kfree(p);
|
||||
+ return clk;
|
||||
+}
|
||||
+
|
||||
+static struct clk *krait_of_get(struct of_phandle_args *clkspec, void *data)
|
||||
+{
|
||||
+ unsigned int idx = clkspec->args[0];
|
||||
+ struct clk **clks = data;
|
||||
+
|
||||
+ if (idx >= 5) {
|
||||
+ pr_err("%s: invalid clock index %d\n", __func__, idx);
|
||||
+ return ERR_PTR(-EINVAL);
|
||||
+ }
|
||||
+
|
||||
+ return clks[idx] ? : ERR_PTR(-ENODEV);
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id krait_cc_match_table[] = {
|
||||
+ { .compatible = "qcom,krait-cc-v1", (void *)1UL },
|
||||
+ { .compatible = "qcom,krait-cc-v2" },
|
||||
+ {}
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, krait_cc_match_table);
|
||||
+
|
||||
+static int krait_cc_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct device *dev = &pdev->dev;
|
||||
+ const struct of_device_id *id;
|
||||
+ unsigned long cur_rate, aux_rate;
|
||||
+ int cpu;
|
||||
+ struct clk *clk;
|
||||
+ struct clk **clks;
|
||||
+ struct clk *l2_pri_mux_clk;
|
||||
+
|
||||
+ id = of_match_device(krait_cc_match_table, dev);
|
||||
+ if (!id)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ /* Rate is 1 because 0 causes problems for __clk_mux_determine_rate */
|
||||
+ clk = clk_register_fixed_rate(dev, "qsb", NULL, CLK_IS_ROOT, 1);
|
||||
+ if (IS_ERR(clk))
|
||||
+ return PTR_ERR(clk);
|
||||
+
|
||||
+ if (!id->data) {
|
||||
+ clk = clk_register_fixed_factor(dev, "acpu_aux",
|
||||
+ "gpll0_vote", 0, 1, 2);
|
||||
+ if (IS_ERR(clk))
|
||||
+ return PTR_ERR(clk);
|
||||
+ }
|
||||
+
|
||||
+ /* Krait configurations have at most 4 CPUs and one L2 */
|
||||
+ clks = devm_kcalloc(dev, 5, sizeof(*clks), GFP_KERNEL);
|
||||
+ if (!clks)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ for_each_possible_cpu(cpu) {
|
||||
+ clk = krait_add_clks(dev, cpu, id->data);
|
||||
+ if (IS_ERR(clk))
|
||||
+ return PTR_ERR(clk);
|
||||
+ clks[cpu] = clk;
|
||||
+ }
|
||||
+
|
||||
+ l2_pri_mux_clk = krait_add_clks(dev, -1, id->data);
|
||||
+ if (IS_ERR(l2_pri_mux_clk))
|
||||
+ return PTR_ERR(l2_pri_mux_clk);
|
||||
+ clks[4] = l2_pri_mux_clk;
|
||||
+
|
||||
+ /*
|
||||
+ * We don't want the CPU or L2 clocks to be turned off at late init
|
||||
+ * if CPUFREQ or HOTPLUG configs are disabled. So, bump up the
|
||||
+ * refcount of these clocks. Any cpufreq/hotplug manager can assume
|
||||
+ * that the clocks have already been prepared and enabled by the time
|
||||
+ * they take over.
|
||||
+ */
|
||||
+ for_each_online_cpu(cpu) {
|
||||
+ clk_prepare_enable(l2_pri_mux_clk);
|
||||
+ WARN(clk_prepare_enable(clks[cpu]),
|
||||
+ "Unable to turn on CPU%d clock", cpu);
|
||||
+ }
|
||||
+
|
||||
+ /*
|
||||
+ * Force reinit of HFPLLs and muxes to overwrite any potential
|
||||
+ * incorrect configuration of HFPLLs and muxes by the bootloader.
|
||||
+ * While at it, also make sure the cores are running at known rates
|
||||
+ * and print the current rate.
|
||||
+ *
|
||||
+ * The clocks are set to aux clock rate first to make sure the
|
||||
+ * secondary mux is not sourcing off of QSB. The rate is then set to
|
||||
+ * two different rates to force a HFPLL reinit under all
|
||||
+ * circumstances.
|
||||
+ */
|
||||
+ cur_rate = clk_get_rate(l2_pri_mux_clk);
|
||||
+ aux_rate = 384000000;
|
||||
+ if (cur_rate == 1) {
|
||||
+ pr_info("L2 @ QSB rate. Forcing new rate.\n");
|
||||
+ cur_rate = aux_rate;
|
||||
+ }
|
||||
+ clk_set_rate(l2_pri_mux_clk, aux_rate);
|
||||
+ clk_set_rate(l2_pri_mux_clk, 2);
|
||||
+ clk_set_rate(l2_pri_mux_clk, cur_rate);
|
||||
+ pr_info("L2 @ %lu KHz\n", clk_get_rate(l2_pri_mux_clk) / 1000);
|
||||
+ for_each_possible_cpu(cpu) {
|
||||
+ clk = clks[cpu];
|
||||
+ cur_rate = clk_get_rate(clk);
|
||||
+ if (cur_rate == 1) {
|
||||
+ pr_info("CPU%d @ QSB rate. Forcing new rate.\n", cpu);
|
||||
+ cur_rate = aux_rate;
|
||||
+ }
|
||||
+ clk_set_rate(clk, aux_rate);
|
||||
+ clk_set_rate(clk, 2);
|
||||
+ clk_set_rate(clk, cur_rate);
|
||||
+ pr_info("CPU%d @ %lu KHz\n", cpu, clk_get_rate(clk) / 1000);
|
||||
+ }
|
||||
+
|
||||
+ of_clk_add_provider(dev->of_node, krait_of_get, clks);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver krait_cc_driver = {
|
||||
+ .probe = krait_cc_probe,
|
||||
+ .driver = {
|
||||
+ .name = "krait-cc",
|
||||
+ .of_match_table = krait_cc_match_table,
|
||||
+ },
|
||||
+};
|
||||
+module_platform_driver(krait_cc_driver);
|
||||
+
|
||||
+MODULE_DESCRIPTION("Krait CPU Clock Driver");
|
||||
+MODULE_LICENSE("GPL v2");
|
||||
+MODULE_ALIAS("platform:krait-cc");
|
|
@ -0,0 +1,23 @@
|
|||
From 58f8215f1d9397f9130657cc2c15a956bd99210e Mon Sep 17 00:00:00 2001
|
||||
From: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
Date: Wed, 13 Jul 2016 15:22:25 +0300
|
||||
Subject: [PATCH 44/69] clk: qcom: krait: Remove CLK_IS_ROOT
|
||||
|
||||
The flag CLK_IS_ROOT is no-op now. Remove it.
|
||||
|
||||
Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
---
|
||||
drivers/clk/qcom/krait-cc.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/clk/qcom/krait-cc.c
|
||||
+++ b/drivers/clk/qcom/krait-cc.c
|
||||
@@ -258,7 +258,7 @@ static int krait_cc_probe(struct platfor
|
||||
return -ENODEV;
|
||||
|
||||
/* Rate is 1 because 0 causes problems for __clk_mux_determine_rate */
|
||||
- clk = clk_register_fixed_rate(dev, "qsb", NULL, CLK_IS_ROOT, 1);
|
||||
+ clk = clk_register_fixed_rate(dev, "qsb", NULL, 0, 1);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
|
@ -0,0 +1,295 @@
|
|||
From 42eea6bc2858ab9649cf6931455e391e48939685 Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Date: Fri, 20 Mar 2015 23:45:31 -0700
|
||||
Subject: [PATCH 45/69] cpufreq: Add module to register cpufreq on Krait CPUs
|
||||
|
||||
Register a cpufreq-generic device whenever we detect that a
|
||||
"qcom,krait" compatible CPU is present in DT.
|
||||
|
||||
Cc: <devicetree@vger.kernel.org>
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
---
|
||||
.../devicetree/bindings/arm/msm/qcom,pvs.txt | 38 ++++
|
||||
drivers/cpufreq/Kconfig.arm | 9 +
|
||||
drivers/cpufreq/Makefile | 1 +
|
||||
drivers/cpufreq/qcom-cpufreq.c | 204 +++++++++++++++++++++
|
||||
4 files changed, 252 insertions(+)
|
||||
create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt
|
||||
create mode 100644 drivers/cpufreq/qcom-cpufreq.c
|
||||
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt
|
||||
@@ -0,0 +1,38 @@
|
||||
+Qualcomm Process Voltage Scaling Tables
|
||||
+
|
||||
+The node name is required to be "qcom,pvs". There shall only be one
|
||||
+such node present in the root of the tree.
|
||||
+
|
||||
+PROPERTIES
|
||||
+
|
||||
+- qcom,pvs-format-a or qcom,pvs-format-b:
|
||||
+ Usage: required
|
||||
+ Value type: <empty>
|
||||
+ Definition: Indicates the format of qcom,speedX-pvsY-bin-vZ properties.
|
||||
+ If qcom,pvs-format-a is used the table is two columns
|
||||
+ (frequency and voltage in that order). If qcom,pvs-format-b is used the table is three columns (frequency, voltage,
|
||||
+ and current in that order).
|
||||
+
|
||||
+- qcom,speedX-pvsY-bin-vZ:
|
||||
+ Usage: required
|
||||
+ Value type: <prop-encoded-array>
|
||||
+ Definition: The PVS table corresponding to the speed bin X, pvs bin Y,
|
||||
+ and version Z.
|
||||
+Example:
|
||||
+
|
||||
+ qcom,pvs {
|
||||
+ qcom,pvs-format-a;
|
||||
+ qcom,speed0-pvs0-bin-v0 =
|
||||
+ < 384000000 950000 >,
|
||||
+ < 486000000 975000 >,
|
||||
+ < 594000000 1000000 >,
|
||||
+ < 702000000 1025000 >,
|
||||
+ < 810000000 1075000 >,
|
||||
+ < 918000000 1100000 >,
|
||||
+ < 1026000000 1125000 >,
|
||||
+ < 1134000000 1175000 >,
|
||||
+ < 1242000000 1200000 >,
|
||||
+ < 1350000000 1225000 >,
|
||||
+ < 1458000000 1237500 >,
|
||||
+ < 1512000000 1250000 >;
|
||||
+ };
|
||||
--- a/drivers/cpufreq/Kconfig.arm
|
||||
+++ b/drivers/cpufreq/Kconfig.arm
|
||||
@@ -88,6 +88,15 @@ config ARM_OMAP2PLUS_CPUFREQ
|
||||
depends on ARCH_OMAP2PLUS
|
||||
default ARCH_OMAP2PLUS
|
||||
|
||||
+config ARM_QCOM_CPUFREQ
|
||||
+ tristate "Qualcomm based"
|
||||
+ depends on ARCH_QCOM
|
||||
+ select PM_OPP
|
||||
+ help
|
||||
+ This adds the CPUFreq driver for Qualcomm SoC based boards.
|
||||
+
|
||||
+ If in doubt, say N.
|
||||
+
|
||||
config ARM_S3C_CPUFREQ
|
||||
bool
|
||||
help
|
||||
--- a/drivers/cpufreq/Makefile
|
||||
+++ b/drivers/cpufreq/Makefile
|
||||
@@ -62,6 +62,7 @@ obj-$(CONFIG_ARM_MT8173_CPUFREQ) += mt81
|
||||
obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o
|
||||
obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o
|
||||
obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o
|
||||
+obj-$(CONFIG_ARM_QCOM_CPUFREQ) += qcom-cpufreq.o
|
||||
obj-$(CONFIG_ARM_S3C24XX_CPUFREQ) += s3c24xx-cpufreq.o
|
||||
obj-$(CONFIG_ARM_S3C24XX_CPUFREQ_DEBUGFS) += s3c24xx-cpufreq-debugfs.o
|
||||
obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/cpufreq/qcom-cpufreq.c
|
||||
@@ -0,0 +1,204 @@
|
||||
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License version 2 and
|
||||
+ * only version 2 as published by the Free Software Foundation.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/cpu.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/pm_opp.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/cpufreq-dt.h>
|
||||
+
|
||||
+static void __init get_krait_bin_format_a(int *speed, int *pvs, int *pvs_ver)
|
||||
+{
|
||||
+ void __iomem *base;
|
||||
+ u32 pte_efuse;
|
||||
+
|
||||
+ *speed = *pvs = *pvs_ver = 0;
|
||||
+
|
||||
+ base = ioremap(0x007000c0, 4);
|
||||
+ if (!base) {
|
||||
+ pr_warn("Unable to read efuse data. Defaulting to 0!\n");
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ pte_efuse = readl_relaxed(base);
|
||||
+ iounmap(base);
|
||||
+
|
||||
+ *speed = pte_efuse & 0xf;
|
||||
+ if (*speed == 0xf)
|
||||
+ *speed = (pte_efuse >> 4) & 0xf;
|
||||
+
|
||||
+ if (*speed == 0xf) {
|
||||
+ *speed = 0;
|
||||
+ pr_warn("Speed bin: Defaulting to %d\n", *speed);
|
||||
+ } else {
|
||||
+ pr_info("Speed bin: %d\n", *speed);
|
||||
+ }
|
||||
+
|
||||
+ *pvs = (pte_efuse >> 10) & 0x7;
|
||||
+ if (*pvs == 0x7)
|
||||
+ *pvs = (pte_efuse >> 13) & 0x7;
|
||||
+
|
||||
+ if (*pvs == 0x7) {
|
||||
+ *pvs = 0;
|
||||
+ pr_warn("PVS bin: Defaulting to %d\n", *pvs);
|
||||
+ } else {
|
||||
+ pr_info("PVS bin: %d\n", *pvs);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void __init get_krait_bin_format_b(int *speed, int *pvs, int *pvs_ver)
|
||||
+{
|
||||
+ u32 pte_efuse, redundant_sel;
|
||||
+ void __iomem *base;
|
||||
+
|
||||
+ *speed = 0;
|
||||
+ *pvs = 0;
|
||||
+ *pvs_ver = 0;
|
||||
+
|
||||
+ base = ioremap(0xfc4b80b0, 8);
|
||||
+ if (!base) {
|
||||
+ pr_warn("Unable to read efuse data. Defaulting to 0!\n");
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ pte_efuse = readl_relaxed(base);
|
||||
+ redundant_sel = (pte_efuse >> 24) & 0x7;
|
||||
+ *speed = pte_efuse & 0x7;
|
||||
+ /* 4 bits of PVS are in efuse register bits 31, 8-6. */
|
||||
+ *pvs = ((pte_efuse >> 28) & 0x8) | ((pte_efuse >> 6) & 0x7);
|
||||
+ *pvs_ver = (pte_efuse >> 4) & 0x3;
|
||||
+
|
||||
+ switch (redundant_sel) {
|
||||
+ case 1:
|
||||
+ *speed = (pte_efuse >> 27) & 0xf;
|
||||
+ break;
|
||||
+ case 2:
|
||||
+ *pvs = (pte_efuse >> 27) & 0xf;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ /* Check SPEED_BIN_BLOW_STATUS */
|
||||
+ if (pte_efuse & BIT(3)) {
|
||||
+ pr_info("Speed bin: %d\n", *speed);
|
||||
+ } else {
|
||||
+ pr_warn("Speed bin not set. Defaulting to 0!\n");
|
||||
+ *speed = 0;
|
||||
+ }
|
||||
+
|
||||
+ /* Check PVS_BLOW_STATUS */
|
||||
+ pte_efuse = readl_relaxed(base + 0x4) & BIT(21);
|
||||
+ if (pte_efuse) {
|
||||
+ pr_info("PVS bin: %d\n", *pvs);
|
||||
+ } else {
|
||||
+ pr_warn("PVS bin not set. Defaulting to 0!\n");
|
||||
+ *pvs = 0;
|
||||
+ }
|
||||
+
|
||||
+ pr_info("PVS version: %d\n", *pvs_ver);
|
||||
+ iounmap(base);
|
||||
+}
|
||||
+
|
||||
+static int __init qcom_cpufreq_populate_opps(void)
|
||||
+{
|
||||
+ int len, rows, cols, i, k, speed, pvs, pvs_ver;
|
||||
+ char table_name[] = "qcom,speedXX-pvsXX-bin-vXX";
|
||||
+ struct device_node *np;
|
||||
+ struct device *dev;
|
||||
+ int cpu = 0;
|
||||
+
|
||||
+ np = of_find_node_by_name(NULL, "qcom,pvs");
|
||||
+ if (!np)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ if (of_property_read_bool(np, "qcom,pvs-format-a")) {
|
||||
+ get_krait_bin_format_a(&speed, &pvs, &pvs_ver);
|
||||
+ cols = 2;
|
||||
+ } else if (of_property_read_bool(np, "qcom,pvs-format-b")) {
|
||||
+ get_krait_bin_format_b(&speed, &pvs, &pvs_ver);
|
||||
+ cols = 3;
|
||||
+ } else {
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ snprintf(table_name, sizeof(table_name),
|
||||
+ "qcom,speed%d-pvs%d-bin-v%d", speed, pvs, pvs_ver);
|
||||
+
|
||||
+ if (!of_find_property(np, table_name, &len))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ len /= sizeof(u32);
|
||||
+ if (len % cols || len == 0)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ rows = len / cols;
|
||||
+
|
||||
+ for (i = 0, k = 0; i < rows; i++) {
|
||||
+ u32 freq, volt;
|
||||
+
|
||||
+ of_property_read_u32_index(np, table_name, k++, &freq);
|
||||
+ of_property_read_u32_index(np, table_name, k++, &volt);
|
||||
+ while (k % cols)
|
||||
+ k++; /* Skip uA entries if present */
|
||||
+ for (cpu = 0; cpu < num_possible_cpus(); cpu++) {
|
||||
+ dev = get_cpu_device(cpu);
|
||||
+ if (!dev)
|
||||
+ return -ENODEV;
|
||||
+ if (dev_pm_opp_add(dev, freq, volt))
|
||||
+ pr_warn("failed to add OPP %u\n", freq);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int __init qcom_cpufreq_driver_init(void)
|
||||
+{
|
||||
+ struct cpufreq_dt_platform_data pdata = { .independent_clocks = true };
|
||||
+ struct platform_device_info devinfo = {
|
||||
+ .name = "cpufreq-dt",
|
||||
+ .data = &pdata,
|
||||
+ .size_data = sizeof(pdata),
|
||||
+ };
|
||||
+ struct device *cpu_dev;
|
||||
+ struct device_node *np;
|
||||
+ int ret;
|
||||
+
|
||||
+ cpu_dev = get_cpu_device(0);
|
||||
+ if (!cpu_dev)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ np = of_node_get(cpu_dev->of_node);
|
||||
+ if (!np)
|
||||
+ return -ENOENT;
|
||||
+
|
||||
+ if (!of_device_is_compatible(np, "qcom,krait")) {
|
||||
+ of_node_put(np);
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+ of_node_put(np);
|
||||
+
|
||||
+ ret = qcom_cpufreq_populate_opps();
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ return PTR_ERR_OR_ZERO(platform_device_register_full(&devinfo));
|
||||
+}
|
||||
+module_init(qcom_cpufreq_driver_init);
|
||||
+
|
||||
+MODULE_DESCRIPTION("Qualcomm CPUfreq driver");
|
||||
+MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,46 @@
|
|||
From f3a327717565cadc8ce5c148860ce0baeb4fbe20 Mon Sep 17 00:00:00 2001
|
||||
From: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
Date: Thu, 14 Jul 2016 14:48:21 +0300
|
||||
Subject: [PATCH 46/69] cpufreq: qcom: Remove platform data
|
||||
|
||||
Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
---
|
||||
drivers/cpufreq/qcom-cpufreq.c | 12 +++---------
|
||||
1 file changed, 3 insertions(+), 9 deletions(-)
|
||||
|
||||
--- a/drivers/cpufreq/qcom-cpufreq.c
|
||||
+++ b/drivers/cpufreq/qcom-cpufreq.c
|
||||
@@ -20,7 +20,6 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/slab.h>
|
||||
-#include <linux/cpufreq-dt.h>
|
||||
|
||||
static void __init get_krait_bin_format_a(int *speed, int *pvs, int *pvs_ver)
|
||||
{
|
||||
@@ -168,12 +167,6 @@ static int __init qcom_cpufreq_populate_
|
||||
|
||||
static int __init qcom_cpufreq_driver_init(void)
|
||||
{
|
||||
- struct cpufreq_dt_platform_data pdata = { .independent_clocks = true };
|
||||
- struct platform_device_info devinfo = {
|
||||
- .name = "cpufreq-dt",
|
||||
- .data = &pdata,
|
||||
- .size_data = sizeof(pdata),
|
||||
- };
|
||||
struct device *cpu_dev;
|
||||
struct device_node *np;
|
||||
int ret;
|
||||
@@ -196,9 +189,10 @@ static int __init qcom_cpufreq_driver_in
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- return PTR_ERR_OR_ZERO(platform_device_register_full(&devinfo));
|
||||
+ return PTR_ERR_OR_ZERO(platform_device_register_simple("cpufreq-dt", -1,
|
||||
+ NULL, 0));
|
||||
}
|
||||
-module_init(qcom_cpufreq_driver_init);
|
||||
+late_initcall(qcom_cpufreq_driver_init);
|
||||
|
||||
MODULE_DESCRIPTION("Qualcomm CPUfreq driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,72 @@
|
|||
From c7c6a0f50f9ac3620c611ce06ba1f9fafea0444e Mon Sep 17 00:00:00 2001
|
||||
From: Archit Taneja <architt@codeaurora.org>
|
||||
Date: Mon, 3 Aug 2015 10:38:14 +0530
|
||||
Subject: [PATCH 47/69] mtd: nand: Create a BBT flag to access bad block
|
||||
markers in raw mode
|
||||
|
||||
Some controllers can access the factory bad block marker from OOB only
|
||||
when they read it in raw mode. When ECC is enabled, these controllers
|
||||
discard reading/writing bad block markers, preventing access to them
|
||||
altogether.
|
||||
|
||||
The bbt driver assumes MTD_OPS_PLACE_OOB when scanning for bad blocks.
|
||||
This results in the nand driver's ecc->read_oob() op to be called, which
|
||||
works with ECC enabled.
|
||||
|
||||
Create a new BBT option flag that tells nand_bbt to force the mode to
|
||||
MTD_OPS_RAW. This would result in the correct op being called for the
|
||||
underlying nand controller driver.
|
||||
|
||||
Reviewed-by: Andy Gross <agross@codeaurora.org>
|
||||
Signed-off-by: Archit Taneja <architt@codeaurora.org>
|
||||
---
|
||||
drivers/mtd/nand/nand_base.c | 6 +++++-
|
||||
drivers/mtd/nand/nand_bbt.c | 6 +++++-
|
||||
include/linux/mtd/bbm.h | 6 ++++++
|
||||
3 files changed, 16 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/mtd/nand/nand_base.c
|
||||
+++ b/drivers/mtd/nand/nand_base.c
|
||||
@@ -414,7 +414,11 @@ static int nand_default_block_markbad(st
|
||||
} else {
|
||||
ops.len = ops.ooblen = 1;
|
||||
}
|
||||
- ops.mode = MTD_OPS_PLACE_OOB;
|
||||
+
|
||||
+ if (unlikely(chip->bbt_options & NAND_BBT_ACCESS_BBM_RAW))
|
||||
+ ops.mode = MTD_OPS_RAW;
|
||||
+ else
|
||||
+ ops.mode = MTD_OPS_PLACE_OOB;
|
||||
|
||||
/* Write to first/last page(s) if necessary */
|
||||
if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
|
||||
--- a/drivers/mtd/nand/nand_bbt.c
|
||||
+++ b/drivers/mtd/nand/nand_bbt.c
|
||||
@@ -420,7 +420,11 @@ static int scan_block_fast(struct mtd_in
|
||||
ops.oobbuf = buf;
|
||||
ops.ooboffs = 0;
|
||||
ops.datbuf = NULL;
|
||||
- ops.mode = MTD_OPS_PLACE_OOB;
|
||||
+
|
||||
+ if (unlikely(bd->options & NAND_BBT_ACCESS_BBM_RAW))
|
||||
+ ops.mode = MTD_OPS_RAW;
|
||||
+ else
|
||||
+ ops.mode = MTD_OPS_PLACE_OOB;
|
||||
|
||||
for (j = 0; j < numpages; j++) {
|
||||
/*
|
||||
--- a/include/linux/mtd/bbm.h
|
||||
+++ b/include/linux/mtd/bbm.h
|
||||
@@ -116,6 +116,12 @@ struct nand_bbt_descr {
|
||||
#define NAND_BBT_NO_OOB_BBM 0x00080000
|
||||
|
||||
/*
|
||||
+ * Force MTD_OPS_RAW mode when trying to access bad block markes from OOB. To
|
||||
+ * be used by controllers which can access BBM only when ECC is disabled, i.e,
|
||||
+ * when in RAW access mode
|
||||
+ */
|
||||
+#define NAND_BBT_ACCESS_BBM_RAW 0x00100000
|
||||
+/*
|
||||
* Flag set by nand_create_default_bbt_descr(), marking that the nand_bbt_descr
|
||||
* was allocated dynamicaly and must be freed in nand_release(). Has no meaning
|
||||
* in nand_chip.bbt_options.
|
|
@ -0,0 +1,27 @@
|
|||
From 5c294df1715d673f94f3b0a6e1ea3a426ca35e6e Mon Sep 17 00:00:00 2001
|
||||
From: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
Date: Thu, 28 Apr 2016 16:20:12 +0300
|
||||
Subject: [PATCH 48/69] PM / OPP: HACK: Allow to set regulator without opp_list
|
||||
|
||||
Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
---
|
||||
drivers/base/power/opp/core.c | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/base/power/opp/core.c
|
||||
+++ b/drivers/base/power/opp/core.c
|
||||
@@ -1339,12 +1339,13 @@ struct opp_table *dev_pm_opp_set_regulat
|
||||
ret = -ENOMEM;
|
||||
goto unlock;
|
||||
}
|
||||
-
|
||||
+#if 0
|
||||
/* This should be called before OPPs are initialized */
|
||||
if (WARN_ON(!list_empty(&opp_table->opp_list))) {
|
||||
ret = -EBUSY;
|
||||
goto err;
|
||||
}
|
||||
+#endif
|
||||
|
||||
/* Already have a regulator set */
|
||||
if (WARN_ON(!IS_ERR(opp_table->regulator))) {
|
|
@ -0,0 +1,147 @@
|
|||
From c949f08cf20fe82971fbdb4015daa38210da492e Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Date: Fri, 18 Sep 2015 17:52:06 -0700
|
||||
Subject: [PATCH 49/69] PM / OPP: Support adjusting OPP voltages at runtime
|
||||
|
||||
On some SoCs the Adaptive Voltage Scaling (AVS) technique is
|
||||
employed to optimize the operating voltage of a device. At a
|
||||
given frequency, the hardware monitors dynamic factors and either
|
||||
makes a suggestion for how much to adjust a voltage for the
|
||||
current frequency, or it automatically adjusts the voltage
|
||||
without software intervention. Add an API to the OPP library for
|
||||
the former case, so that AVS type devices can update the voltages
|
||||
for an OPP when the hardware determines the voltage should
|
||||
change. The assumption is that drivers like CPUfreq or devfreq
|
||||
will register for the OPP notifiers and adjust the voltage
|
||||
according to suggestions that AVS makes.
|
||||
|
||||
Cc: Nishanth Menon <nm@ti.com>
|
||||
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
|
||||
Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
---
|
||||
drivers/base/power/opp/core.c | 77 +++++++++++++++++++++++++++++++++++++++++++
|
||||
include/linux/pm_opp.h | 11 +++++++
|
||||
2 files changed, 88 insertions(+)
|
||||
|
||||
--- a/drivers/base/power/opp/core.c
|
||||
+++ b/drivers/base/power/opp/core.c
|
||||
@@ -1521,6 +1521,83 @@ unlock:
|
||||
}
|
||||
|
||||
/**
|
||||
+ * dev_pm_opp_adjust_voltage() - helper to change the voltage of an OPP
|
||||
+ * @dev: device for which we do this operation
|
||||
+ * @freq: OPP frequency to adjust voltage of
|
||||
+ * @u_volt: new OPP voltage
|
||||
+ *
|
||||
+ * Change the voltage of an OPP with an RCU operation.
|
||||
+ *
|
||||
+ * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the
|
||||
+ * copy operation, returns 0 if no modifcation was done OR modification was
|
||||
+ * successful.
|
||||
+ *
|
||||
+ * Locking: The internal device_opp and opp structures are RCU protected.
|
||||
+ * Hence this function internally uses RCU updater strategy with mutex locks to
|
||||
+ * keep the integrity of the internal data structures. Callers should ensure
|
||||
+ * that this function is *NOT* called under RCU protection or in contexts where
|
||||
+ * mutex locking or synchronize_rcu() blocking calls cannot be used.
|
||||
+ */
|
||||
+int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq,
|
||||
+ unsigned long u_volt)
|
||||
+{
|
||||
+ struct opp_table *opp_table;
|
||||
+ struct dev_pm_opp *new_opp, *tmp_opp, *opp = ERR_PTR(-ENODEV);
|
||||
+ int r = 0;
|
||||
+
|
||||
+ /* keep the node allocated */
|
||||
+ new_opp = kmalloc(sizeof(*new_opp), GFP_KERNEL);
|
||||
+ if (!new_opp)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ mutex_lock(&opp_table_lock);
|
||||
+
|
||||
+ /* Find the opp_table */
|
||||
+ opp_table = _find_opp_table(dev);
|
||||
+ if (IS_ERR(opp_table)) {
|
||||
+ r = PTR_ERR(opp_table);
|
||||
+ dev_warn(dev, "%s: Device OPP not found (%d)\n", __func__, r);
|
||||
+ goto unlock;
|
||||
+ }
|
||||
+
|
||||
+ /* Do we have the frequency? */
|
||||
+ list_for_each_entry(tmp_opp, &opp_table->opp_list, node) {
|
||||
+ if (tmp_opp->rate == freq) {
|
||||
+ opp = tmp_opp;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ if (IS_ERR(opp)) {
|
||||
+ r = PTR_ERR(opp);
|
||||
+ goto unlock;
|
||||
+ }
|
||||
+
|
||||
+ /* Is update really needed? */
|
||||
+ if (opp->u_volt == u_volt)
|
||||
+ goto unlock;
|
||||
+ /* copy the old data over */
|
||||
+ *new_opp = *opp;
|
||||
+
|
||||
+ /* plug in new node */
|
||||
+ new_opp->u_volt = u_volt;
|
||||
+
|
||||
+ list_replace_rcu(&opp->node, &new_opp->node);
|
||||
+ mutex_unlock(&opp_table_lock);
|
||||
+ call_srcu(&opp_table->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu);
|
||||
+
|
||||
+ /* Notify the change of the OPP */
|
||||
+ srcu_notifier_call_chain(&opp_table->srcu_head, OPP_EVENT_ADJUST_VOLTAGE,
|
||||
+ new_opp);
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+unlock:
|
||||
+ mutex_unlock(&opp_table_lock);
|
||||
+ kfree(new_opp);
|
||||
+ return r;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
* dev_pm_opp_enable() - Enable a specific OPP
|
||||
* @dev: device for which we do this operation
|
||||
* @freq: OPP frequency to enable
|
||||
--- a/include/linux/pm_opp.h
|
||||
+++ b/include/linux/pm_opp.h
|
||||
@@ -23,6 +23,7 @@ struct opp_table;
|
||||
|
||||
enum dev_pm_opp_event {
|
||||
OPP_EVENT_ADD, OPP_EVENT_REMOVE, OPP_EVENT_ENABLE, OPP_EVENT_DISABLE,
|
||||
+ OPP_EVENT_ADJUST_VOLTAGE,
|
||||
};
|
||||
|
||||
#if defined(CONFIG_PM_OPP)
|
||||
@@ -53,6 +54,9 @@ int dev_pm_opp_add(struct device *dev, u
|
||||
unsigned long u_volt);
|
||||
void dev_pm_opp_remove(struct device *dev, unsigned long freq);
|
||||
|
||||
+int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq,
|
||||
+ unsigned long u_volt);
|
||||
+
|
||||
int dev_pm_opp_enable(struct device *dev, unsigned long freq);
|
||||
|
||||
int dev_pm_opp_disable(struct device *dev, unsigned long freq);
|
||||
@@ -139,6 +143,13 @@ static inline void dev_pm_opp_remove(str
|
||||
{
|
||||
}
|
||||
|
||||
+static inline int
|
||||
+dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq,
|
||||
+ unsigned long u_volt)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static inline int dev_pm_opp_enable(struct device *dev, unsigned long freq)
|
||||
{
|
||||
return 0;
|
|
@ -0,0 +1,107 @@
|
|||
From 4a17bbfcf72c94b37079e39a7c1e1e8653f7fe92 Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Date: Fri, 18 Sep 2015 17:52:07 -0700
|
||||
Subject: [PATCH 50/69] OPP: Allow notifiers to call dev_pm_opp_get_{voltage,
|
||||
freq} RCU-free
|
||||
|
||||
We pass the dev_pm_opp structure to OPP notifiers but the users
|
||||
of the notifier need to surround calls to dev_pm_opp_get_*() with
|
||||
RCU read locks to avoid lockdep warnings. The notifier is already
|
||||
called with the dev_opp's srcu lock held, so it should be safe to
|
||||
assume the devm_pm_opp structure is already protected inside the
|
||||
notifier. Update the lockdep check for this.
|
||||
|
||||
Cc: Krzysztof Kozlowski <k.kozlowski@samsung.com>
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
|
||||
Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
---
|
||||
drivers/base/power/opp/core.c | 19 ++++++++++---------
|
||||
1 file changed, 10 insertions(+), 9 deletions(-)
|
||||
|
||||
--- a/drivers/base/power/opp/core.c
|
||||
+++ b/drivers/base/power/opp/core.c
|
||||
@@ -32,9 +32,10 @@ LIST_HEAD(opp_tables);
|
||||
/* Lock to allow exclusive modification to the device and opp lists */
|
||||
DEFINE_MUTEX(opp_table_lock);
|
||||
|
||||
-#define opp_rcu_lockdep_assert() \
|
||||
+#define opp_rcu_lockdep_assert(s) \
|
||||
do { \
|
||||
RCU_LOCKDEP_WARN(!rcu_read_lock_held() && \
|
||||
+ !(s && srcu_read_lock_held(s)) && \
|
||||
!lockdep_is_held(&opp_table_lock), \
|
||||
"Missing rcu_read_lock() or " \
|
||||
"opp_table_lock protection"); \
|
||||
@@ -72,7 +73,7 @@ struct opp_table *_find_opp_table(struct
|
||||
{
|
||||
struct opp_table *opp_table;
|
||||
|
||||
- opp_rcu_lockdep_assert();
|
||||
+ opp_rcu_lockdep_assert(NULL);
|
||||
|
||||
if (IS_ERR_OR_NULL(dev)) {
|
||||
pr_err("%s: Invalid parameters\n", __func__);
|
||||
@@ -106,7 +107,7 @@ unsigned long dev_pm_opp_get_voltage(str
|
||||
struct dev_pm_opp *tmp_opp;
|
||||
unsigned long v = 0;
|
||||
|
||||
- opp_rcu_lockdep_assert();
|
||||
+ opp_rcu_lockdep_assert(NULL);
|
||||
|
||||
tmp_opp = rcu_dereference(opp);
|
||||
if (IS_ERR_OR_NULL(tmp_opp))
|
||||
@@ -138,7 +139,7 @@ unsigned long dev_pm_opp_get_freq(struct
|
||||
struct dev_pm_opp *tmp_opp;
|
||||
unsigned long f = 0;
|
||||
|
||||
- opp_rcu_lockdep_assert();
|
||||
+ opp_rcu_lockdep_assert(NULL);
|
||||
|
||||
tmp_opp = rcu_dereference(opp);
|
||||
if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available)
|
||||
@@ -172,7 +173,7 @@ bool dev_pm_opp_is_turbo(struct dev_pm_o
|
||||
{
|
||||
struct dev_pm_opp *tmp_opp;
|
||||
|
||||
- opp_rcu_lockdep_assert();
|
||||
+ opp_rcu_lockdep_assert(NULL);
|
||||
|
||||
tmp_opp = rcu_dereference(opp);
|
||||
if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available) {
|
||||
@@ -300,7 +301,7 @@ struct dev_pm_opp *dev_pm_opp_get_suspen
|
||||
{
|
||||
struct opp_table *opp_table;
|
||||
|
||||
- opp_rcu_lockdep_assert();
|
||||
+ opp_rcu_lockdep_assert(NULL);
|
||||
|
||||
opp_table = _find_opp_table(dev);
|
||||
if (IS_ERR(opp_table) || !opp_table->suspend_opp ||
|
||||
@@ -380,7 +381,7 @@ struct dev_pm_opp *dev_pm_opp_find_freq_
|
||||
struct opp_table *opp_table;
|
||||
struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE);
|
||||
|
||||
- opp_rcu_lockdep_assert();
|
||||
+ opp_rcu_lockdep_assert(NULL);
|
||||
|
||||
opp_table = _find_opp_table(dev);
|
||||
if (IS_ERR(opp_table)) {
|
||||
@@ -444,7 +445,7 @@ struct dev_pm_opp *dev_pm_opp_find_freq_
|
||||
{
|
||||
struct opp_table *opp_table;
|
||||
|
||||
- opp_rcu_lockdep_assert();
|
||||
+ opp_rcu_lockdep_assert(NULL);
|
||||
|
||||
if (!dev || !freq) {
|
||||
dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq);
|
||||
@@ -486,7 +487,7 @@ struct dev_pm_opp *dev_pm_opp_find_freq_
|
||||
struct opp_table *opp_table;
|
||||
struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE);
|
||||
|
||||
- opp_rcu_lockdep_assert();
|
||||
+ opp_rcu_lockdep_assert(NULL);
|
||||
|
||||
if (!dev || !freq) {
|
||||
dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq);
|
|
@ -0,0 +1,52 @@
|
|||
From d06ca5e7a3cf726f5be5ffd96e93ccd798b8c09a Mon Sep 17 00:00:00 2001
|
||||
From: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
Date: Thu, 12 May 2016 14:41:33 +0300
|
||||
Subject: [PATCH 51/69] PM / OPP: Add a helper to get an opp regulator for
|
||||
device
|
||||
|
||||
Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
---
|
||||
drivers/base/power/opp/core.c | 21 +++++++++++++++++++++
|
||||
include/linux/pm_opp.h | 1 +
|
||||
2 files changed, 22 insertions(+)
|
||||
|
||||
--- a/drivers/base/power/opp/core.c
|
||||
+++ b/drivers/base/power/opp/core.c
|
||||
@@ -151,6 +151,27 @@ unsigned long dev_pm_opp_get_freq(struct
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq);
|
||||
|
||||
+struct regulator *dev_pm_opp_get_regulator(struct device *dev)
|
||||
+{
|
||||
+ struct opp_table *opp_table;
|
||||
+ struct regulator *reg;
|
||||
+
|
||||
+ rcu_read_lock();
|
||||
+
|
||||
+ opp_table = _find_opp_table(dev);
|
||||
+ if (IS_ERR(opp_table)) {
|
||||
+ rcu_read_unlock();
|
||||
+ return ERR_CAST(opp_table);
|
||||
+ }
|
||||
+
|
||||
+ reg = opp_table->regulator;
|
||||
+
|
||||
+ rcu_read_unlock();
|
||||
+
|
||||
+ return reg;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(dev_pm_opp_get_regulator);
|
||||
+
|
||||
/**
|
||||
* dev_pm_opp_is_turbo() - Returns if opp is turbo OPP or not
|
||||
* @opp: opp for which turbo mode is being verified
|
||||
--- a/include/linux/pm_opp.h
|
||||
+++ b/include/linux/pm_opp.h
|
||||
@@ -31,6 +31,7 @@ enum dev_pm_opp_event {
|
||||
unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp);
|
||||
|
||||
unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp);
|
||||
+struct regulator *dev_pm_opp_get_regulator(struct device *dev);
|
||||
|
||||
bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp);
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
From 4533c285c2aedce6d4434d7b877066de3b1ecb33 Mon Sep 17 00:00:00 2001
|
||||
From: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
Date: Thu, 25 Aug 2016 18:43:35 +0300
|
||||
Subject: [PATCH 52/69] PM / OPP: Update the voltage tolerance when adjusting
|
||||
the OPP
|
||||
|
||||
When the voltage is adjusted, the voltage tolerance is not updated.
|
||||
This can lead to situations where the voltage min value is greater
|
||||
than the voltage max value. The final result is triggering a BUG()
|
||||
in the regulator core.
|
||||
Fix this by updating the voltage tolerance values too.
|
||||
|
||||
Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
---
|
||||
drivers/base/power/opp/core.c | 5 +++++
|
||||
1 file changed, 5 insertions(+)
|
||||
|
||||
--- a/drivers/base/power/opp/core.c
|
||||
+++ b/drivers/base/power/opp/core.c
|
||||
@@ -1566,6 +1566,7 @@ int dev_pm_opp_adjust_voltage(struct dev
|
||||
struct opp_table *opp_table;
|
||||
struct dev_pm_opp *new_opp, *tmp_opp, *opp = ERR_PTR(-ENODEV);
|
||||
int r = 0;
|
||||
+ unsigned long tol;
|
||||
|
||||
/* keep the node allocated */
|
||||
new_opp = kmalloc(sizeof(*new_opp), GFP_KERNEL);
|
||||
@@ -1602,6 +1603,10 @@ int dev_pm_opp_adjust_voltage(struct dev
|
||||
|
||||
/* plug in new node */
|
||||
new_opp->u_volt = u_volt;
|
||||
+ tol = u_volt * opp_table->voltage_tolerance_v1 / 100;
|
||||
+ new_opp->u_volt = u_volt;
|
||||
+ new_opp->u_volt_min = u_volt - tol;
|
||||
+ new_opp->u_volt_max = u_volt + tol;
|
||||
|
||||
list_replace_rcu(&opp->node, &new_opp->node);
|
||||
mutex_unlock(&opp_table_lock);
|
|
@ -1,14 +1,25 @@
|
|||
|
||||
In commit "regulator: qcom: Rework to single platform device" the smb208 regulator
|
||||
used in IPQ8064 was left out.
|
||||
|
||||
Add it to that new framework and update Docs accordingly.
|
||||
From ef10381ca4d01848ebedb4afb2c78feb8052f103 Mon Sep 17 00:00:00 2001
|
||||
From: Adrian Panella <ianchi74@outlook.com>
|
||||
Date: Thu, 9 Mar 2017 08:26:54 +0100
|
||||
Subject: [PATCH 53/69] regulator: add smb208 support
|
||||
|
||||
Signed-off-by: Adrian Panella <ianchi74@outlook.com>
|
||||
---
|
||||
Documentation/devicetree/bindings/mfd/qcom-rpm.txt | 4 ++++
|
||||
drivers/regulator/qcom_rpm-regulator.c | 9 +++++++++
|
||||
2 files changed, 13 insertions(+)
|
||||
|
||||
--- a/Documentation/devicetree/bindings/mfd/qcom-rpm.txt
|
||||
+++ b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt
|
||||
@@ -171,6 +171,9 @@ pm8018:
|
||||
@@ -61,6 +61,7 @@ Regulator nodes are identified by their
|
||||
"qcom,rpm-pm8901-regulators"
|
||||
"qcom,rpm-pm8921-regulators"
|
||||
"qcom,rpm-pm8018-regulators"
|
||||
+ "qcom,rpm-smb208-regulators"
|
||||
|
||||
- vdd_l0_l1_lvs-supply:
|
||||
- vdd_l2_l11_l12-supply:
|
||||
@@ -171,6 +172,9 @@ pm8018:
|
||||
s1, s2, s3, s4, s5, , l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11,
|
||||
l12, l14, lvs1
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
From 10577f74c35bd395951d1b2382c8d821089b5745 Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Date: Fri, 18 Sep 2015 17:52:08 -0700
|
||||
Subject: [PATCH 54/69] cpufreq-dt: Handle OPP voltage adjust events
|
||||
|
||||
On some SoCs the Adaptive Voltage Scaling (AVS) technique is
|
||||
employed to optimize the operating voltage of a device. At a
|
||||
given frequency, the hardware monitors dynamic factors and either
|
||||
makes a suggestion for how much to adjust a voltage for the
|
||||
current frequency, or it automatically adjusts the voltage
|
||||
without software intervention.
|
||||
|
||||
In the former case, an AVS driver will call
|
||||
dev_pm_opp_modify_voltage() and update the voltage for the
|
||||
particular OPP the CPUs are using. Add an OPP notifier to
|
||||
cpufreq-dt so that we can adjust the voltage of the CPU when AVS
|
||||
updates the OPP.
|
||||
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
|
||||
Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
---
|
||||
drivers/cpufreq/cpufreq-dt.c | 68 ++++++++++++++++++++++++++++++++++++++++++--
|
||||
1 file changed, 65 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/drivers/cpufreq/cpufreq-dt.c
|
||||
+++ b/drivers/cpufreq/cpufreq-dt.c
|
||||
@@ -32,6 +32,9 @@ struct private_data {
|
||||
struct device *cpu_dev;
|
||||
struct thermal_cooling_device *cdev;
|
||||
const char *reg_name;
|
||||
+ struct notifier_block opp_nb;
|
||||
+ struct mutex lock;
|
||||
+ unsigned long opp_freq;
|
||||
};
|
||||
|
||||
static struct freq_attr *cpufreq_dt_attr[] = {
|
||||
@@ -43,9 +46,16 @@ static struct freq_attr *cpufreq_dt_attr
|
||||
static int set_target(struct cpufreq_policy *policy, unsigned int index)
|
||||
{
|
||||
struct private_data *priv = policy->driver_data;
|
||||
+ int ret;
|
||||
+ unsigned long target_freq = policy->freq_table[index].frequency * 1000;
|
||||
+
|
||||
+ mutex_lock(&priv->lock);
|
||||
+ ret = dev_pm_opp_set_rate(priv->cpu_dev, target_freq);
|
||||
+ if (!ret)
|
||||
+ priv->opp_freq = target_freq;
|
||||
+ mutex_unlock(&priv->lock);
|
||||
|
||||
- return dev_pm_opp_set_rate(priv->cpu_dev,
|
||||
- policy->freq_table[index].frequency * 1000);
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -86,6 +96,39 @@ node_put:
|
||||
return name;
|
||||
}
|
||||
|
||||
+static int opp_notifier(struct notifier_block *nb, unsigned long event,
|
||||
+ void *data)
|
||||
+{
|
||||
+ struct dev_pm_opp *opp = data;
|
||||
+ struct private_data *priv = container_of(nb, struct private_data,
|
||||
+ opp_nb);
|
||||
+ struct device *cpu_dev = priv->cpu_dev;
|
||||
+ struct regulator *cpu_reg;
|
||||
+ unsigned long volt, freq;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ if (event == OPP_EVENT_ADJUST_VOLTAGE) {
|
||||
+ cpu_reg = dev_pm_opp_get_regulator(cpu_dev);
|
||||
+ if (IS_ERR(cpu_reg)) {
|
||||
+ ret = PTR_ERR(cpu_reg);
|
||||
+ goto out;
|
||||
+ }
|
||||
+ volt = dev_pm_opp_get_voltage(opp);
|
||||
+ freq = dev_pm_opp_get_freq(opp);
|
||||
+
|
||||
+ mutex_lock(&priv->lock);
|
||||
+ if (freq == priv->opp_freq) {
|
||||
+ ret = regulator_set_voltage_triplet(cpu_reg, volt, volt, volt);
|
||||
+ }
|
||||
+ mutex_unlock(&priv->lock);
|
||||
+ if (ret)
|
||||
+ dev_err(cpu_dev, "failed to scale voltage: %d\n", ret);
|
||||
+ }
|
||||
+
|
||||
+out:
|
||||
+ return notifier_from_errno(ret);
|
||||
+}
|
||||
+
|
||||
static int resources_available(void)
|
||||
{
|
||||
struct device *cpu_dev;
|
||||
@@ -153,6 +196,7 @@ static int cpufreq_init(struct cpufreq_p
|
||||
bool fallback = false;
|
||||
const char *name;
|
||||
int ret;
|
||||
+ struct srcu_notifier_head *opp_srcu_head;
|
||||
|
||||
cpu_dev = get_cpu_device(policy->cpu);
|
||||
if (!cpu_dev) {
|
||||
@@ -239,13 +283,29 @@ static int cpufreq_init(struct cpufreq_p
|
||||
goto out_free_opp;
|
||||
}
|
||||
|
||||
+ mutex_init(&priv->lock);
|
||||
+
|
||||
+ rcu_read_lock();
|
||||
+ opp_srcu_head = dev_pm_opp_get_notifier(cpu_dev);
|
||||
+ if (IS_ERR(opp_srcu_head)) {
|
||||
+ ret = PTR_ERR(opp_srcu_head);
|
||||
+ rcu_read_unlock();
|
||||
+ goto out_free_priv;
|
||||
+ }
|
||||
+
|
||||
+ priv->opp_nb.notifier_call = opp_notifier;
|
||||
+ ret = srcu_notifier_chain_register(opp_srcu_head, &priv->opp_nb);
|
||||
+ rcu_read_unlock();
|
||||
+ if (ret)
|
||||
+ goto out_free_priv;
|
||||
+
|
||||
priv->reg_name = name;
|
||||
priv->opp_table = opp_table;
|
||||
|
||||
ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
|
||||
if (ret) {
|
||||
dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
|
||||
- goto out_free_priv;
|
||||
+ goto out_unregister_nb;
|
||||
}
|
||||
|
||||
priv->cpu_dev = cpu_dev;
|
||||
@@ -284,6 +344,8 @@ static int cpufreq_init(struct cpufreq_p
|
||||
|
||||
out_free_cpufreq_table:
|
||||
dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
|
||||
+out_unregister_nb:
|
||||
+ srcu_notifier_chain_unregister(opp_srcu_head, &priv->opp_nb);
|
||||
out_free_priv:
|
||||
kfree(priv);
|
||||
out_free_opp:
|
|
@ -0,0 +1,90 @@
|
|||
From 0759cdff49f1cf361bf503c13f7bcb33da43ab95 Mon Sep 17 00:00:00 2001
|
||||
From: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
Date: Tue, 8 Sep 2015 11:24:41 +0300
|
||||
Subject: [PATCH 55/69] cpufreq-dt: Add L2 frequency scaling support
|
||||
|
||||
Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
---
|
||||
drivers/cpufreq/cpufreq-dt.c | 41 ++++++++++++++++++++++++++++++++++++++++-
|
||||
include/linux/cpufreq.h | 2 ++
|
||||
2 files changed, 42 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/cpufreq/cpufreq-dt.c
|
||||
+++ b/drivers/cpufreq/cpufreq-dt.c
|
||||
@@ -48,11 +48,41 @@ static int set_target(struct cpufreq_pol
|
||||
struct private_data *priv = policy->driver_data;
|
||||
int ret;
|
||||
unsigned long target_freq = policy->freq_table[index].frequency * 1000;
|
||||
+ struct clk *l2_clk = policy->l2_clk;
|
||||
+ unsigned int l2_freq;
|
||||
+ unsigned long new_l2_freq = 0;
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
ret = dev_pm_opp_set_rate(priv->cpu_dev, target_freq);
|
||||
- if (!ret)
|
||||
+
|
||||
+ if (!ret) {
|
||||
+ if (!IS_ERR(l2_clk) && policy->l2_rate[0] && policy->l2_rate[1] &&
|
||||
+ policy->l2_rate[2]) {
|
||||
+ static unsigned long krait_l2[CONFIG_NR_CPUS] = { };
|
||||
+ int cpu, ret = 0;
|
||||
+
|
||||
+ if (target_freq >= policy->l2_rate[2])
|
||||
+ new_l2_freq = policy->l2_rate[2];
|
||||
+ else if (target_freq >= policy->l2_rate[1])
|
||||
+ new_l2_freq = policy->l2_rate[1];
|
||||
+ else
|
||||
+ new_l2_freq = policy->l2_rate[0];
|
||||
+
|
||||
+ krait_l2[policy->cpu] = new_l2_freq;
|
||||
+ for_each_present_cpu(cpu)
|
||||
+ new_l2_freq = max(new_l2_freq, krait_l2[cpu]);
|
||||
+
|
||||
+ l2_freq = clk_get_rate(l2_clk);
|
||||
+
|
||||
+ if (l2_freq != new_l2_freq) {
|
||||
+ /* scale l2 with the core */
|
||||
+ ret = clk_set_rate(l2_clk, new_l2_freq);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
priv->opp_freq = target_freq;
|
||||
+ }
|
||||
+
|
||||
mutex_unlock(&priv->lock);
|
||||
|
||||
return ret;
|
||||
@@ -197,6 +227,8 @@ static int cpufreq_init(struct cpufreq_p
|
||||
const char *name;
|
||||
int ret;
|
||||
struct srcu_notifier_head *opp_srcu_head;
|
||||
+ struct device_node *l2_np;
|
||||
+ struct clk *l2_clk = NULL;
|
||||
|
||||
cpu_dev = get_cpu_device(policy->cpu);
|
||||
if (!cpu_dev) {
|
||||
@@ -318,6 +350,13 @@ static int cpufreq_init(struct cpufreq_p
|
||||
policy->suspend_freq = dev_pm_opp_get_freq(suspend_opp) / 1000;
|
||||
rcu_read_unlock();
|
||||
|
||||
+ l2_clk = clk_get(cpu_dev, "l2");
|
||||
+ if (!IS_ERR(l2_clk))
|
||||
+ policy->l2_clk = l2_clk;
|
||||
+ l2_np = of_find_node_by_name(NULL, "qcom,l2");
|
||||
+ if (l2_np)
|
||||
+ of_property_read_u32_array(l2_np, "qcom,l2-rates", policy->l2_rate, 3);
|
||||
+
|
||||
ret = cpufreq_table_validate_and_show(policy, freq_table);
|
||||
if (ret) {
|
||||
dev_err(cpu_dev, "%s: invalid frequency table: %d\n", __func__,
|
||||
--- a/include/linux/cpufreq.h
|
||||
+++ b/include/linux/cpufreq.h
|
||||
@@ -73,6 +73,8 @@ struct cpufreq_policy {
|
||||
unsigned int cpu; /* cpu managing this policy, must be online */
|
||||
|
||||
struct clk *clk;
|
||||
+ struct clk *l2_clk; /* L2 clock */
|
||||
+ unsigned int l2_rate[3]; /* L2 bus clock rate thresholds */
|
||||
struct cpufreq_cpuinfo cpuinfo;/* see above */
|
||||
|
||||
unsigned int min; /* in kHz */
|
|
@ -0,0 +1,23 @@
|
|||
From 001a8dcb56ced58c518aaa10a4f0ba5e878705b6 Mon Sep 17 00:00:00 2001
|
||||
From: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
Date: Tue, 17 May 2016 16:15:43 +0300
|
||||
Subject: [PATCH 56/69] cpufreq-dt: Add missing rcu locks
|
||||
|
||||
Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
---
|
||||
drivers/cpufreq/cpufreq-dt.c | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
--- a/drivers/cpufreq/cpufreq-dt.c
|
||||
+++ b/drivers/cpufreq/cpufreq-dt.c
|
||||
@@ -143,8 +143,10 @@ static int opp_notifier(struct notifier_
|
||||
ret = PTR_ERR(cpu_reg);
|
||||
goto out;
|
||||
}
|
||||
+ rcu_read_lock();
|
||||
volt = dev_pm_opp_get_voltage(opp);
|
||||
freq = dev_pm_opp_get_freq(opp);
|
||||
+ rcu_read_unlock();
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
if (freq == priv->opp_freq) {
|
|
@ -0,0 +1,372 @@
|
|||
From f72c5aa18281c44945fea6181d0d816a7605505c Mon Sep 17 00:00:00 2001
|
||||
From: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
Date: Wed, 18 Mar 2015 17:23:29 +0200
|
||||
Subject: [PATCH 57/69] clk: qcom: Add regmap mux-div clocks support
|
||||
|
||||
Add support for hardware that can switch both parent clocks and divider
|
||||
at the same time. This avoids generating intermediate frequencies from
|
||||
either the old parent clock and new divider or new parent clock and
|
||||
old divider combinations.
|
||||
|
||||
Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
---
|
||||
drivers/clk/qcom/Makefile | 1 +
|
||||
drivers/clk/qcom/clk-regmap-mux-div.c | 272 ++++++++++++++++++++++++++++++++++
|
||||
drivers/clk/qcom/clk-regmap-mux-div.h | 65 ++++++++
|
||||
3 files changed, 338 insertions(+)
|
||||
create mode 100644 drivers/clk/qcom/clk-regmap-mux-div.c
|
||||
create mode 100644 drivers/clk/qcom/clk-regmap-mux-div.h
|
||||
|
||||
--- a/drivers/clk/qcom/Makefile
|
||||
+++ b/drivers/clk/qcom/Makefile
|
||||
@@ -9,6 +9,7 @@ clk-qcom-y += clk-rcg2.o
|
||||
clk-qcom-y += clk-branch.o
|
||||
clk-qcom-y += clk-regmap-divider.o
|
||||
clk-qcom-y += clk-regmap-mux.o
|
||||
+clk-qcom-y += clk-regmap-mux-div.o
|
||||
clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o
|
||||
clk-qcom-y += clk-hfpll.o
|
||||
clk-qcom-y += reset.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/clk/qcom/clk-regmap-mux-div.c
|
||||
@@ -0,0 +1,272 @@
|
||||
+/*
|
||||
+ * Copyright (c) 2015, Linaro Limited
|
||||
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
|
||||
+ *
|
||||
+ * This software is licensed under the terms of the GNU General Public
|
||||
+ * License version 2, as published by the Free Software Foundation, and
|
||||
+ * may be copied, distributed, and modified under those terms.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/bitops.h>
|
||||
+#include <linux/delay.h>
|
||||
+#include <linux/export.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/regmap.h>
|
||||
+
|
||||
+#include "clk-regmap-mux-div.h"
|
||||
+
|
||||
+#define CMD_RCGR 0x0
|
||||
+#define CMD_RCGR_UPDATE BIT(0)
|
||||
+#define CMD_RCGR_DIRTY_CFG BIT(4)
|
||||
+#define CMD_RCGR_ROOT_OFF BIT(31)
|
||||
+#define CFG_RCGR 0x4
|
||||
+
|
||||
+#define to_clk_regmap_mux_div(_hw) \
|
||||
+ container_of(to_clk_regmap(_hw), struct clk_regmap_mux_div, clkr)
|
||||
+
|
||||
+int __mux_div_set_src_div(struct clk_regmap_mux_div *md, u32 src, u32 div)
|
||||
+{
|
||||
+ int ret, count;
|
||||
+ u32 val, mask;
|
||||
+ const char *name = clk_hw_get_name(&md->clkr.hw);
|
||||
+
|
||||
+ val = (div << md->hid_shift) | (src << md->src_shift);
|
||||
+ mask = ((BIT(md->hid_width) - 1) << md->hid_shift) |
|
||||
+ ((BIT(md->src_width) - 1) << md->src_shift);
|
||||
+
|
||||
+ ret = regmap_update_bits(md->clkr.regmap, CFG_RCGR + md->reg_offset,
|
||||
+ mask, val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = regmap_update_bits(md->clkr.regmap, CMD_RCGR + md->reg_offset,
|
||||
+ CMD_RCGR_UPDATE, CMD_RCGR_UPDATE);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* Wait for update to take effect */
|
||||
+ for (count = 500; count > 0; count--) {
|
||||
+ ret = regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset,
|
||||
+ &val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ if (!(val & CMD_RCGR_UPDATE))
|
||||
+ return 0;
|
||||
+ udelay(1);
|
||||
+ }
|
||||
+
|
||||
+ pr_err("%s: RCG did not update its configuration", name);
|
||||
+ return -EBUSY;
|
||||
+}
|
||||
+
|
||||
+static void __mux_div_get_src_div(struct clk_regmap_mux_div *md, u32 *src,
|
||||
+ u32 *div)
|
||||
+{
|
||||
+ u32 val, __div, __src;
|
||||
+ const char *name = clk_hw_get_name(&md->clkr.hw);
|
||||
+
|
||||
+ regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset, &val);
|
||||
+
|
||||
+ if (val & CMD_RCGR_DIRTY_CFG) {
|
||||
+ pr_err("%s: RCG configuration is pending\n", name);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ regmap_read(md->clkr.regmap, CFG_RCGR + md->reg_offset, &val);
|
||||
+ __src = (val >> md->src_shift);
|
||||
+ __src &= BIT(md->src_width) - 1;
|
||||
+ *src = __src;
|
||||
+
|
||||
+ __div = (val >> md->hid_shift);
|
||||
+ __div &= BIT(md->hid_width) - 1;
|
||||
+ *div = __div;
|
||||
+}
|
||||
+
|
||||
+static int mux_div_enable(struct clk_hw *hw)
|
||||
+{
|
||||
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
|
||||
+
|
||||
+ return __mux_div_set_src_div(md, md->src, md->div);
|
||||
+}
|
||||
+
|
||||
+static inline bool is_better_rate(unsigned long req, unsigned long best,
|
||||
+ unsigned long new)
|
||||
+{
|
||||
+ return (req <= new && new < best) || (best < req && best < new);
|
||||
+}
|
||||
+
|
||||
+static int mux_div_determine_rate(struct clk_hw *hw,
|
||||
+ struct clk_rate_request *req)
|
||||
+{
|
||||
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
|
||||
+ unsigned int i, div, max_div;
|
||||
+ unsigned long actual_rate, best_rate = 0;
|
||||
+ unsigned long req_rate = req->rate;
|
||||
+
|
||||
+ for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
|
||||
+ struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i);
|
||||
+ unsigned long parent_rate = clk_hw_get_rate(parent);
|
||||
+
|
||||
+ max_div = BIT(md->hid_width) - 1;
|
||||
+ for (div = 1; div < max_div; div++) {
|
||||
+ parent_rate = mult_frac(req_rate, div, 2);
|
||||
+ parent_rate = clk_hw_round_rate(parent, parent_rate);
|
||||
+ actual_rate = mult_frac(parent_rate, 2, div);
|
||||
+
|
||||
+ if (is_better_rate(req_rate, best_rate, actual_rate)) {
|
||||
+ best_rate = actual_rate;
|
||||
+ req->rate = best_rate;
|
||||
+ req->best_parent_rate = parent_rate;
|
||||
+ req->best_parent_hw = parent;
|
||||
+ }
|
||||
+
|
||||
+ if (actual_rate < req_rate || best_rate <= req_rate)
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (!best_rate)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int __mux_div_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
|
||||
+ unsigned long prate, u32 src)
|
||||
+{
|
||||
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
|
||||
+ int ret;
|
||||
+ u32 div, max_div, best_src = 0, best_div = 0;
|
||||
+ unsigned int i;
|
||||
+ unsigned long actual_rate, best_rate = 0;
|
||||
+
|
||||
+ for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
|
||||
+ struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i);
|
||||
+ unsigned long parent_rate = clk_hw_get_rate(parent);
|
||||
+
|
||||
+ max_div = BIT(md->hid_width) - 1;
|
||||
+ for (div = 1; div < max_div; div++) {
|
||||
+ parent_rate = mult_frac(rate, div, 2);
|
||||
+ parent_rate = clk_hw_round_rate(parent, parent_rate);
|
||||
+ actual_rate = mult_frac(parent_rate, 2, div);
|
||||
+
|
||||
+ if (is_better_rate(rate, best_rate, actual_rate)) {
|
||||
+ best_rate = actual_rate;
|
||||
+ best_src = md->parent_map[i].cfg;
|
||||
+ best_div = div - 1;
|
||||
+ }
|
||||
+
|
||||
+ if (actual_rate < rate || best_rate <= rate)
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ ret = __mux_div_set_src_div(md, best_src, best_div);
|
||||
+ if (!ret) {
|
||||
+ md->div = best_div;
|
||||
+ md->src = best_src;
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static u8 mux_div_get_parent(struct clk_hw *hw)
|
||||
+{
|
||||
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
|
||||
+ const char *name = clk_hw_get_name(hw);
|
||||
+ u32 i, div, src = 0;
|
||||
+
|
||||
+ __mux_div_get_src_div(md, &src, &div);
|
||||
+
|
||||
+ for (i = 0; i < clk_hw_get_num_parents(hw); i++)
|
||||
+ if (src == md->parent_map[i].cfg)
|
||||
+ return i;
|
||||
+
|
||||
+ pr_err("%s: Can't find parent with src %d\n", name, src);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int mux_div_set_parent(struct clk_hw *hw, u8 index)
|
||||
+{
|
||||
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
|
||||
+
|
||||
+ return __mux_div_set_src_div(md, md->parent_map[index].cfg, md->div);
|
||||
+}
|
||||
+
|
||||
+static int mux_div_set_rate(struct clk_hw *hw,
|
||||
+ unsigned long rate, unsigned long prate)
|
||||
+{
|
||||
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
|
||||
+
|
||||
+ return __mux_div_set_rate_and_parent(hw, rate, prate, md->src);
|
||||
+}
|
||||
+
|
||||
+static int mux_div_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
|
||||
+ unsigned long prate, u8 index)
|
||||
+{
|
||||
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
|
||||
+
|
||||
+ return __mux_div_set_rate_and_parent(hw, rate, prate,
|
||||
+ md->parent_map[index].cfg);
|
||||
+}
|
||||
+
|
||||
+static unsigned long mux_div_recalc_rate(struct clk_hw *hw, unsigned long prate)
|
||||
+{
|
||||
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
|
||||
+ u32 div, src;
|
||||
+ int i, num_parents = clk_hw_get_num_parents(hw);
|
||||
+ const char *name = clk_hw_get_name(hw);
|
||||
+
|
||||
+ __mux_div_get_src_div(md, &src, &div);
|
||||
+ for (i = 0; i < num_parents; i++)
|
||||
+ if (src == md->parent_map[i].cfg) {
|
||||
+ struct clk_hw *p = clk_hw_get_parent_by_index(hw, i);
|
||||
+ unsigned long parent_rate = clk_hw_get_rate(p);
|
||||
+
|
||||
+ return mult_frac(parent_rate, 2, div + 1);
|
||||
+ }
|
||||
+
|
||||
+ pr_err("%s: Can't find parent %d\n", name, src);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct clk_hw *mux_div_get_safe_parent(struct clk_hw *hw,
|
||||
+ unsigned long *safe_freq)
|
||||
+{
|
||||
+ int i;
|
||||
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
|
||||
+
|
||||
+ if (md->safe_freq)
|
||||
+ *safe_freq = md->safe_freq;
|
||||
+
|
||||
+ for (i = 0; i < clk_hw_get_num_parents(hw); i++)
|
||||
+ if (md->safe_src == md->parent_map[i].cfg)
|
||||
+ break;
|
||||
+
|
||||
+ return clk_hw_get_parent_by_index(hw, i);
|
||||
+}
|
||||
+
|
||||
+static void mux_div_disable(struct clk_hw *hw)
|
||||
+{
|
||||
+ struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
|
||||
+
|
||||
+ __mux_div_set_src_div(md, md->safe_src, md->safe_div);
|
||||
+}
|
||||
+
|
||||
+const struct clk_ops clk_regmap_mux_div_ops = {
|
||||
+ .enable = mux_div_enable,
|
||||
+ .disable = mux_div_disable,
|
||||
+ .get_parent = mux_div_get_parent,
|
||||
+ .set_parent = mux_div_set_parent,
|
||||
+ .set_rate = mux_div_set_rate,
|
||||
+ .set_rate_and_parent = mux_div_set_rate_and_parent,
|
||||
+ .determine_rate = mux_div_determine_rate,
|
||||
+ .recalc_rate = mux_div_recalc_rate,
|
||||
+ .get_safe_parent = mux_div_get_safe_parent,
|
||||
+};
|
||||
+EXPORT_SYMBOL_GPL(clk_regmap_mux_div_ops);
|
||||
--- /dev/null
|
||||
+++ b/drivers/clk/qcom/clk-regmap-mux-div.h
|
||||
@@ -0,0 +1,65 @@
|
||||
+/*
|
||||
+ * Copyright (c) 2015, Linaro Limited
|
||||
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
|
||||
+ *
|
||||
+ * This software is licensed under the terms of the GNU General Public
|
||||
+ * License version 2, as published by the Free Software Foundation, and
|
||||
+ * may be copied, distributed, and modified under those terms.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ */
|
||||
+
|
||||
+#ifndef __QCOM_CLK_REGMAP_MUX_DIV_H__
|
||||
+#define __QCOM_CLK_REGMAP_MUX_DIV_H__
|
||||
+
|
||||
+#include <linux/clk-provider.h>
|
||||
+#include "clk-rcg.h"
|
||||
+#include "clk-regmap.h"
|
||||
+
|
||||
+/**
|
||||
+ * struct mux_div_clk - combined mux/divider clock
|
||||
+ * @reg_offset: offset of the mux/divider register
|
||||
+ * @hid_width: number of bits in half integer divider
|
||||
+ * @hid_shift: lowest bit of hid value field
|
||||
+ * @src_width: number of bits in source select
|
||||
+ * @src_shift: lowest bit of source select field
|
||||
+ * @div: the divider raw configuration value
|
||||
+ * @src: the mux index which will be used if the clock is enabled
|
||||
+ * @safe_src: the safe source mux value we switch to, while the main PLL is
|
||||
+ * reconfigured
|
||||
+ * @safe_div: the safe divider value that we set, while the main PLL is
|
||||
+ * reconfigured
|
||||
+ * @safe_freq: When switching rates from A to B, the mux div clock will
|
||||
+ * instead switch from A -> safe_freq -> B. This allows the
|
||||
+ * mux_div clock to change rates while enabled, even if this
|
||||
+ * behavior is not supported by the parent clocks.
|
||||
+ * If changing the rate of parent A also causes the rate of
|
||||
+ * parent B to change, then safe_freq must be defined.
|
||||
+ * safe_freq is expected to have a source clock which is always
|
||||
+ * on and runs at only one rate.
|
||||
+ * @parent_map: pointer to parent_map struct
|
||||
+ * @clkr: handle between common and hardware-specific interfaces
|
||||
+ */
|
||||
+
|
||||
+struct clk_regmap_mux_div {
|
||||
+ u32 reg_offset;
|
||||
+ u32 hid_width;
|
||||
+ u32 hid_shift;
|
||||
+ u32 src_width;
|
||||
+ u32 src_shift;
|
||||
+ u32 div;
|
||||
+ u32 src;
|
||||
+ u32 safe_src;
|
||||
+ u32 safe_div;
|
||||
+ unsigned long safe_freq;
|
||||
+ const struct parent_map *parent_map;
|
||||
+ struct clk_regmap clkr;
|
||||
+};
|
||||
+
|
||||
+extern const struct clk_ops clk_regmap_mux_div_ops;
|
||||
+int __mux_div_set_src_div(struct clk_regmap_mux_div *md, u32 src, u32 div);
|
||||
+
|
||||
+#endif
|
|
@ -0,0 +1,38 @@
|
|||
From 6081776c1eef63e3083387bb9ec2bf7edf92428b Mon Sep 17 00:00:00 2001
|
||||
From: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
Date: Wed, 2 Nov 2016 17:56:58 +0200
|
||||
Subject: [PATCH 58/69] clk: qcom: Always add factor clock for xo clocks
|
||||
|
||||
Currently the RPM/RPM-SMD clock drivers do not register the xo clocks,
|
||||
so we should always add factor clock. When we later add xo clocks support
|
||||
into the drivers, we should update this function to skip registration.
|
||||
By doing so we avoid any DT dependencies.
|
||||
|
||||
Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
|
||||
---
|
||||
drivers/clk/qcom/common.c | 15 ++++++---------
|
||||
1 file changed, 6 insertions(+), 9 deletions(-)
|
||||
|
||||
--- a/drivers/clk/qcom/common.c
|
||||
+++ b/drivers/clk/qcom/common.c
|
||||
@@ -153,15 +153,12 @@ int qcom_cc_register_board_clk(struct de
|
||||
const char *name, unsigned long rate)
|
||||
{
|
||||
bool add_factor = true;
|
||||
- struct device_node *node;
|
||||
|
||||
- /* The RPM clock driver will add the factor clock if present */
|
||||
- if (IS_ENABLED(CONFIG_QCOM_RPMCC)) {
|
||||
- node = of_find_compatible_node(NULL, NULL, "qcom,rpmcc");
|
||||
- if (of_device_is_available(node))
|
||||
- add_factor = false;
|
||||
- of_node_put(node);
|
||||
- }
|
||||
+ /*
|
||||
+ * TODO: The RPM clock driver currently does not support the xo clock.
|
||||
+ * When xo is added to the RPM clock driver, we should change this
|
||||
+ * function to skip registration of xo factor clocks.
|
||||
+ */
|
||||
|
||||
return _qcom_cc_register_board_clk(dev, path, name, rate, add_factor);
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
From 04ca10340f1b4d92e849724d322a7ca225d11539 Mon Sep 17 00:00:00 2001
|
||||
From: Lina Iyer <lina.iyer@linaro.org>
|
||||
Date: Wed, 25 Mar 2015 14:25:29 -0600
|
||||
Subject: [PATCH 59/69] ARM: cpuidle: Add cpuidle support for QCOM cpus
|
||||
|
||||
Define ARM_QCOM_CPUIDLE config item to enable cpuidle support.
|
||||
|
||||
Cc: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Cc: Arnd Bergmann <arnd@arndb.de>
|
||||
Cc: Kevin Hilman <khilman@linaro.org>
|
||||
Cc: Daniel Lezcano <daniel.lezcano@linaro.org>
|
||||
Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
|
||||
---
|
||||
drivers/cpuidle/Kconfig.arm | 7 +++++++
|
||||
1 file changed, 7 insertions(+)
|
||||
|
||||
--- a/drivers/cpuidle/Kconfig.arm
|
||||
+++ b/drivers/cpuidle/Kconfig.arm
|
||||
@@ -74,3 +74,10 @@ config ARM_MVEBU_V7_CPUIDLE
|
||||
depends on ARCH_MVEBU && !ARM64
|
||||
help
|
||||
Select this to enable cpuidle on Armada 370, 38x and XP processors.
|
||||
+
|
||||
+config ARM_QCOM_CPUIDLE
|
||||
+ bool "CPU Idle Driver for QCOM processors"
|
||||
+ depends on ARCH_QCOM
|
||||
+ select ARM_CPUIDLE
|
||||
+ help
|
||||
+ Select this to enable cpuidle on QCOM processors.
|
|
@ -1,7 +1,7 @@
|
|||
From b12e230f09d4481424e6a5d7e2ae566b6954e83f Mon Sep 17 00:00:00 2001
|
||||
From fa71139b55e114aa8c3c4823ff8ee7d49ee810d4 Mon Sep 17 00:00:00 2001
|
||||
From: Mathieu Olivari <mathieu@codeaurora.org>
|
||||
Date: Wed, 29 Apr 2015 15:21:46 -0700
|
||||
Subject: [PATCH] HACK: arch: arm: force ZRELADDR on arch-qcom
|
||||
Subject: [PATCH 60/69] HACK: arch: arm: force ZRELADDR on arch-qcom
|
||||
|
||||
ARCH_QCOM is using the ARCH_MULTIPLATFORM option, as now recommended
|
||||
on most ARM architectures. This automatically calculate ZRELADDR by
|
|
@ -0,0 +1,23 @@
|
|||
From 5001f2e1a325b68dbf225bd17f69a4d3d975cca5 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <john@phrozen.org>
|
||||
Date: Thu, 9 Mar 2017 09:31:44 +0100
|
||||
Subject: [PATCH 61/69] mtd: "rootfs" conflicts with OpenWrt auto mounting
|
||||
|
||||
Signed-off-by: John Crispin <john@phrozen.org>
|
||||
---
|
||||
drivers/mtd/qcom_smem_part.c | 4 ++++
|
||||
1 file changed, 4 insertions(+)
|
||||
|
||||
--- a/drivers/mtd/qcom_smem_part.c
|
||||
+++ b/drivers/mtd/qcom_smem_part.c
|
||||
@@ -189,6 +189,10 @@ static int parse_qcom_smem_partitions(st
|
||||
m_part->size = le32_to_cpu(s_part->size) * (*smem_blksz);
|
||||
m_part->offset = le32_to_cpu(s_part->start) * (*smem_blksz);
|
||||
|
||||
+ /* "rootfs" conflicts with OpenWrt auto mounting */
|
||||
+ if (mtd_type_is_nand(master) && !strcmp(m_part->name, "rootfs"))
|
||||
+ m_part->name = "ubi";
|
||||
+
|
||||
/*
|
||||
* The last SMEM partition may have its size marked as
|
||||
* something like 0xffffffff, which means "until the end of the
|
|
@ -1,7 +1,7 @@
|
|||
From dd43e356db678a564ad2cc111962d72ba3162a38 Mon Sep 17 00:00:00 2001
|
||||
From a16fcf911a020e46439a3bb3e702463fc3159831 Mon Sep 17 00:00:00 2001
|
||||
From: Abhishek Sahu <absahu@codeaurora.org>
|
||||
Date: Wed, 18 Nov 2015 12:38:56 +0530
|
||||
Subject: ipq806x: gcc: Added the enable regs and mask for PRNG
|
||||
Subject: [PATCH 62/69] ipq806x: gcc: Added the enable regs and mask for PRNG
|
||||
|
||||
kernel got hanged while reading from /dev/hwrng at the
|
||||
time of PRNG clock enable
|
||||
|
@ -14,7 +14,7 @@ Signed-off-by: Abhishek Sahu <absahu@codeaurora.org>
|
|||
|
||||
--- a/drivers/clk/qcom/gcc-ipq806x.c
|
||||
+++ b/drivers/clk/qcom/gcc-ipq806x.c
|
||||
@@ -1153,6 +1153,8 @@ static struct clk_rcg prng_src = {
|
||||
@@ -1234,6 +1234,8 @@ static struct clk_rcg prng_src = {
|
||||
.parent_map = gcc_pxo_pll8_map,
|
||||
},
|
||||
.clkr = {
|
|
@ -0,0 +1,46 @@
|
|||
From 3064376aa3e8dae03dc2c5c3c064e2283c4337d8 Mon Sep 17 00:00:00 2001
|
||||
From: Pavel Kubelun <be.dissent@gmail.com>
|
||||
Date: Tue, 22 Nov 2016 17:37:56 +0300
|
||||
Subject: [PATCH 63/69] ipq806x: clk: gcc: add tsens child node
|
||||
|
||||
Thermal sensors in ipq806x are inside a Global clock controller.
|
||||
Add a child node into it to be used by the TSENS driver.
|
||||
|
||||
Signed-off-by: Pavel Kubelun <be.dissent@gmail.com>
|
||||
---
|
||||
drivers/clk/qcom/gcc-ipq806x.c | 10 +++++++++-
|
||||
1 file changed, 9 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/clk/qcom/gcc-ipq806x.c
|
||||
+++ b/drivers/clk/qcom/gcc-ipq806x.c
|
||||
@@ -970,7 +970,7 @@ static struct clk_branch gsbi1_h_clk = {
|
||||
.hw.init = &(struct clk_init_data){
|
||||
.name = "gsbi1_h_clk",
|
||||
.ops = &clk_branch_ops,
|
||||
-+ .flags = CLK_IGNORE_UNUSED,
|
||||
+ .flags = CLK_IGNORE_UNUSED,
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -3073,6 +3073,7 @@ MODULE_DEVICE_TABLE(of, gcc_ipq806x_matc
|
||||
static int gcc_ipq806x_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
+ struct platform_device *tsens;
|
||||
struct regmap *regmap;
|
||||
int ret;
|
||||
|
||||
@@ -3102,6 +3103,13 @@ static int gcc_ipq806x_probe(struct plat
|
||||
regmap_write(regmap, 0x3cf8, 8);
|
||||
regmap_write(regmap, 0x3d18, 8);
|
||||
|
||||
+ tsens = platform_device_register_data(&pdev->dev, "qcom-tsens", -1,
|
||||
+ NULL, 0);
|
||||
+ if (IS_ERR(tsens))
|
||||
+ return PTR_ERR(tsens);
|
||||
+
|
||||
+ platform_set_drvdata(pdev, tsens);
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,3 +1,15 @@
|
|||
From d30840e2b1cf79d90392e6051b0c0b6006d29d8b Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <john@phrozen.org>
|
||||
Date: Thu, 9 Mar 2017 09:32:40 +0100
|
||||
Subject: [PATCH 64/69] clk: clk-rpm fixes
|
||||
|
||||
Signed-off-by: John Crispin <john@phrozen.org>
|
||||
---
|
||||
.../devicetree/bindings/clock/qcom,rpmcc.txt | 1 +
|
||||
drivers/clk/qcom/clk-rpm.c | 35 ++++++++++++++++++++++
|
||||
include/dt-bindings/clock/qcom,rpmcc.h | 4 +++
|
||||
3 files changed, 40 insertions(+)
|
||||
|
||||
--- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
|
||||
+++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
|
||||
@@ -12,6 +12,7 @@ Required properties :
|
|
@ -1,3 +1,13 @@
|
|||
From 4d8e29642661397a339ac3485f212c6360445421 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <john@phrozen.org>
|
||||
Date: Thu, 9 Mar 2017 09:33:32 +0100
|
||||
Subject: [PATCH 65/69] arm: override compiler flags
|
||||
|
||||
Signed-off-by: John Crispin <john@phrozen.org>
|
||||
---
|
||||
arch/arm/Makefile | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/arch/arm/Makefile
|
||||
+++ b/arch/arm/Makefile
|
||||
@@ -65,7 +65,7 @@ KBUILD_CFLAGS += $(call cc-option,-fno-i
|
|
@ -1,14 +1,14 @@
|
|||
From 4267880319bc1a2270d352e0ded6d6386242a7ef Mon Sep 17 00:00:00 2001
|
||||
From a37b0c9113647b2120cf1a18cfc46afdb3f1fccc Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Tue, 12 Aug 2014 20:49:27 +0200
|
||||
Subject: [PATCH 24/53] GPIO: add named gpio exports
|
||||
Subject: [PATCH 66/69] GPIO: add named gpio exports
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
drivers/gpio/gpiolib-of.c | 68 +++++++++++++++++++++++++++++++++++++++++
|
||||
drivers/gpio/gpiolib-sysfs.c | 10 +++++-
|
||||
include/asm-generic/gpio.h | 6 ++++
|
||||
include/linux/gpio/consumer.h | 8 +++++
|
||||
drivers/gpio/gpiolib-of.c | 68 +++++++++++++++++++++++++++++++++++++++++++
|
||||
drivers/gpio/gpiolib-sysfs.c | 10 ++++++-
|
||||
include/asm-generic/gpio.h | 6 ++++
|
||||
include/linux/gpio/consumer.h | 8 +++++
|
||||
4 files changed, 91 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/gpio/gpiolib-of.c
|
|
@ -1,7 +1,7 @@
|
|||
Author: Adrian Panella <ianchi74@outlook.com>
|
||||
Date: Fri Jun 10 19:10:15 2016 -0500
|
||||
|
||||
generic: Mangle bootloader's kernel arguments
|
||||
From 71270226b14733a4b1f2cde58ea9265caa50b38d Mon Sep 17 00:00:00 2001
|
||||
From: Adrian Panella <ianchi74@outlook.com>
|
||||
Date: Thu, 9 Mar 2017 09:37:17 +0100
|
||||
Subject: [PATCH 67/69] generic: Mangle bootloader's kernel arguments
|
||||
|
||||
The command-line arguments provided by the boot loader will be
|
||||
appended to a new device tree property: bootloader-args.
|
||||
|
@ -13,8 +13,12 @@ sent by bootloader will be ignored.
|
|||
This is usefull in dual boot systems, to get the current root partition
|
||||
without afecting the rest of the system.
|
||||
|
||||
|
||||
Signed-off-by: Adrian Panella <ianchi74@outlook.com>
|
||||
---
|
||||
arch/arm/Kconfig | 11 +++++
|
||||
arch/arm/boot/compressed/atags_to_fdt.c | 72 ++++++++++++++++++++++++++++++++-
|
||||
init/main.c | 16 ++++++++
|
||||
3 files changed, 98 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/arch/arm/Kconfig
|
||||
+++ b/arch/arm/Kconfig
|
|
@ -0,0 +1,71 @@
|
|||
From b9c998eb7735df8000cf48d77f9271823e8e73da Mon Sep 17 00:00:00 2001
|
||||
From: Ram Chandra Jangir <rjangi@codeaurora.org>
|
||||
Date: Thu, 9 Mar 2017 09:39:05 +0100
|
||||
Subject: [PATCH 68/69] spi: add gpio cs support
|
||||
|
||||
Signed-off-by: John Crispin <john@phrozen.org>
|
||||
---
|
||||
drivers/spi/spi-qup.c | 38 ++++++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 38 insertions(+)
|
||||
|
||||
--- a/drivers/spi/spi-qup.c
|
||||
+++ b/drivers/spi/spi-qup.c
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
+#include <linux/gpio.h>
|
||||
|
||||
#define QUP_CONFIG 0x0000
|
||||
#define QUP_STATE 0x0004
|
||||
@@ -1019,6 +1020,38 @@ err_tx:
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static void spi_qup_set_cs(struct spi_device *spi, bool val)
|
||||
+{
|
||||
+ struct spi_qup *controller;
|
||||
+ u32 spi_ioc;
|
||||
+ u32 spi_ioc_orig;
|
||||
+
|
||||
+ controller = spi_master_get_devdata(spi->master);
|
||||
+ spi_ioc = readl_relaxed(controller->base + SPI_IO_CONTROL);
|
||||
+ spi_ioc_orig = spi_ioc;
|
||||
+ if (!val)
|
||||
+ spi_ioc |= SPI_IO_C_FORCE_CS;
|
||||
+ else
|
||||
+ spi_ioc &= ~SPI_IO_C_FORCE_CS;
|
||||
+
|
||||
+ if (spi_ioc != spi_ioc_orig)
|
||||
+ writel_relaxed(spi_ioc, controller->base + SPI_IO_CONTROL);
|
||||
+}
|
||||
+
|
||||
+static int spi_qup_setup(struct spi_device *spi)
|
||||
+{
|
||||
+ if (spi->cs_gpio >= 0) {
|
||||
+ if (spi->mode & SPI_CS_HIGH)
|
||||
+ gpio_set_value(spi->cs_gpio, 0);
|
||||
+ else
|
||||
+ gpio_set_value(spi->cs_gpio, 1);
|
||||
+
|
||||
+ udelay(10);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int spi_qup_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master;
|
||||
@@ -1115,6 +1148,11 @@ static int spi_qup_probe(struct platform
|
||||
if (of_device_is_compatible(dev->of_node, "qcom,spi-qup-v1.1.1"))
|
||||
controller->qup_v1 = 1;
|
||||
|
||||
+ if (!controller->qup_v1)
|
||||
+ master->set_cs = spi_qup_set_cs;
|
||||
+ else
|
||||
+ master->setup = spi_qup_setup;
|
||||
+
|
||||
spin_lock_init(&controller->lock);
|
||||
init_completion(&controller->done);
|
||||
init_completion(&controller->dma_tx_done);
|
Loading…
Reference in a new issue