diff options
author | John Crispin <john@phrozen.org> | 2017-03-13 13:30:10 +0100 |
---|---|---|
committer | Felix Fietkau <nbd@nbd.name> | 2017-03-13 13:40:55 +0100 |
commit | 1adf51702e94063fb058ce13226ec5a04d15cfd4 (patch) | |
tree | 00fb0f446f516289abb93ff78f103294254545c3 /target/linux/ipq806x/patches-4.9 | |
parent | 99b6aefd638c7934136ffcde271e6fc3f4f6c464 (diff) | |
download | mtk-20170518-1adf51702e94063fb058ce13226ec5a04d15cfd4.zip mtk-20170518-1adf51702e94063fb058ce13226ec5a04d15cfd4.tar.gz mtk-20170518-1adf51702e94063fb058ce13226ec5a04d15cfd4.tar.bz2 |
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>
Diffstat (limited to 'target/linux/ipq806x/patches-4.9')
68 files changed, 4796 insertions, 134 deletions
diff --git a/target/linux/ipq806x/patches-4.9/0001-dtbindings-qcom_adm-Fix-channel-specifiers.patch b/target/linux/ipq806x/patches-4.9/0001-dtbindings-qcom_adm-Fix-channel-specifiers.patch index fb629ae..2a36a85 100644 --- a/target/linux/ipq806x/patches-4.9/0001-dtbindings-qcom_adm-Fix-channel-specifiers.patch +++ b/target/linux/ipq806x/patches-4.9/0001-dtbindings-qcom_adm-Fix-channel-specifiers.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0002-dmaengine-Add-ADM-driver.patch b/target/linux/ipq806x/patches-4.9/0002-dmaengine-Add-ADM-driver.patch index 4b56fed..212a902 100644 --- a/target/linux/ipq806x/patches-4.9/0002-dmaengine-Add-ADM-driver.patch +++ b/target/linux/ipq806x/patches-4.9/0002-dmaengine-Add-ADM-driver.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0006-spi-qup-Make-sure-mode-is-only-determined-once.patch b/target/linux/ipq806x/patches-4.9/0003-spi-qup-Make-sure-mode-is-only-determined-once.patch index 9d99699..fd9df44 100644 --- a/target/linux/ipq806x/patches-4.9/0006-spi-qup-Make-sure-mode-is-only-determined-once.patch +++ b/target/linux/ipq806x/patches-4.9/0003-spi-qup-Make-sure-mode-is-only-determined-once.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0007-spi-qup-Fix-transaction-done-signaling.patch b/target/linux/ipq806x/patches-4.9/0004-spi-qup-Fix-transaction-done-signaling.patch index b42f8cd..5881ffa 100644 --- a/target/linux/ipq806x/patches-4.9/0007-spi-qup-Fix-transaction-done-signaling.patch +++ b/target/linux/ipq806x/patches-4.9/0004-spi-qup-Fix-transaction-done-signaling.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0008-spi-qup-Fix-DMA-mode-to-work-correctly.patch b/target/linux/ipq806x/patches-4.9/0005-spi-qup-Fix-DMA-mode-to-work-correctly.patch index d950446..20ab5c1 100644 --- a/target/linux/ipq806x/patches-4.9/0008-spi-qup-Fix-DMA-mode-to-work-correctly.patch +++ b/target/linux/ipq806x/patches-4.9/0005-spi-qup-Fix-DMA-mode-to-work-correctly.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0009-spi-qup-Fix-block-mode-to-work-correctly.patch b/target/linux/ipq806x/patches-4.9/0006-spi-qup-Fix-block-mode-to-work-correctly.patch index 2f316b1..e7d0d65 100644 --- a/target/linux/ipq806x/patches-4.9/0009-spi-qup-Fix-block-mode-to-work-correctly.patch +++ b/target/linux/ipq806x/patches-4.9/0006-spi-qup-Fix-block-mode-to-work-correctly.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0010-spi-qup-properly-detect-extra-interrupts.patch b/target/linux/ipq806x/patches-4.9/0007-spi-qup-properly-detect-extra-interrupts.patch index 74ef25a..8a3b027 100644 --- a/target/linux/ipq806x/patches-4.9/0010-spi-qup-properly-detect-extra-interrupts.patch +++ b/target/linux/ipq806x/patches-4.9/0007-spi-qup-properly-detect-extra-interrupts.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0011-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch b/target/linux/ipq806x/patches-4.9/0008-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch index da1569f..9893596 100644 --- a/target/linux/ipq806x/patches-4.9/0011-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch +++ b/target/linux/ipq806x/patches-4.9/0008-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0012-spi-qup-refactor-spi_qup_io_config-in-two-functions.patch b/target/linux/ipq806x/patches-4.9/0009-spi-qup-refactor-spi_qup_io_config-in-two-functions.patch index 7beb5d9..36c225b 100644 --- a/target/linux/ipq806x/patches-4.9/0012-spi-qup-refactor-spi_qup_io_config-in-two-functions.patch +++ b/target/linux/ipq806x/patches-4.9/0009-spi-qup-refactor-spi_qup_io_config-in-two-functions.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0013-spi-qup-call-io_config-in-mode-specific-function.patch b/target/linux/ipq806x/patches-4.9/0010-spi-qup-call-io_config-in-mode-specific-function.patch index 3aebaac..dfbed62 100644 --- a/target/linux/ipq806x/patches-4.9/0013-spi-qup-call-io_config-in-mode-specific-function.patch +++ b/target/linux/ipq806x/patches-4.9/0010-spi-qup-call-io_config-in-mode-specific-function.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0014-spi-qup-allow-block-mode-to-generate-multiple-transa.patch b/target/linux/ipq806x/patches-4.9/0011-spi-qup-allow-block-mode-to-generate-multiple-transa.patch index 6c275f6..39a1aec 100644 --- a/target/linux/ipq806x/patches-4.9/0014-spi-qup-allow-block-mode-to-generate-multiple-transa.patch +++ b/target/linux/ipq806x/patches-4.9/0011-spi-qup-allow-block-mode-to-generate-multiple-transa.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0015-spi-qup-refactor-spi_qup_prep_sg-to-be-more-take-spe.patch b/target/linux/ipq806x/patches-4.9/0012-spi-qup-refactor-spi_qup_prep_sg-to-be-more-take-spe.patch index 90ffe86..990cccd 100644 --- a/target/linux/ipq806x/patches-4.9/0015-spi-qup-refactor-spi_qup_prep_sg-to-be-more-take-spe.patch +++ b/target/linux/ipq806x/patches-4.9/0012-spi-qup-refactor-spi_qup_prep_sg-to-be-more-take-spe.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0016-spi-qup-allow-mulitple-DMA-transactions-per-spi-xfer.patch b/target/linux/ipq806x/patches-4.9/0013-spi-qup-allow-mulitple-DMA-transactions-per-spi-xfer.patch index de324ff..13e199c 100644 --- a/target/linux/ipq806x/patches-4.9/0016-spi-qup-allow-mulitple-DMA-transactions-per-spi-xfer.patch +++ b/target/linux/ipq806x/patches-4.9/0013-spi-qup-allow-mulitple-DMA-transactions-per-spi-xfer.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0017-spi-qup-Fix-sg-nents-calculation.patch b/target/linux/ipq806x/patches-4.9/0014-spi-qup-Fix-sg-nents-calculation.patch index d6d92b8..2d321f1 100644 --- a/target/linux/ipq806x/patches-4.9/0017-spi-qup-Fix-sg-nents-calculation.patch +++ b/target/linux/ipq806x/patches-4.9/0014-spi-qup-Fix-sg-nents-calculation.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0026-cpufreq-dt-qcom-ipq4019-Add-compat-for-qcom-ipq4019.patch b/target/linux/ipq806x/patches-4.9/0015-cpufreq-dt-qcom-ipq4019-Add-compat-for-qcom-ipq4019.patch index 3d6a827..06b61a0 100644 --- a/target/linux/ipq806x/patches-4.9/0026-cpufreq-dt-qcom-ipq4019-Add-compat-for-qcom-ipq4019.patch +++ b/target/linux/ipq806x/patches-4.9/0015-cpufreq-dt-qcom-ipq4019-Add-compat-for-qcom-ipq4019.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0027-clk-ipq4019-report-accurate-fixed-clock-rates.patch b/target/linux/ipq806x/patches-4.9/0016-clk-ipq4019-report-accurate-fixed-clock-rates.patch index 87b25d5..6f76653 100644 --- a/target/linux/ipq806x/patches-4.9/0027-clk-ipq4019-report-accurate-fixed-clock-rates.patch +++ b/target/linux/ipq806x/patches-4.9/0016-clk-ipq4019-report-accurate-fixed-clock-rates.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0028-qcom-ipq4019-add-cpu-operating-points-for-cpufreq-su.patch b/target/linux/ipq806x/patches-4.9/0017-qcom-ipq4019-add-cpu-operating-points-for-cpufreq-su.patch index c2b0b44..7cbd6a4 100644 --- a/target/linux/ipq806x/patches-4.9/0028-qcom-ipq4019-add-cpu-operating-points-for-cpufreq-su.patch +++ b/target/linux/ipq806x/patches-4.9/0017-qcom-ipq4019-add-cpu-operating-points-for-cpufreq-su.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0029-qcom-ipq4019-turn-on-DMA-for-i2c.patch b/target/linux/ipq806x/patches-4.9/0018-qcom-ipq4019-turn-on-DMA-for-i2c.patch index 98dffc9..42bb4a6 100644 --- a/target/linux/ipq806x/patches-4.9/0029-qcom-ipq4019-turn-on-DMA-for-i2c.patch +++ b/target/linux/ipq806x/patches-4.9/0018-qcom-ipq4019-turn-on-DMA-for-i2c.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0030-qcom-ipq4019-use-correct-clock-for-i2c-bus-0.patch b/target/linux/ipq806x/patches-4.9/0019-qcom-ipq4019-use-correct-clock-for-i2c-bus-0.patch index 7f79dba..54ee571 100644 --- a/target/linux/ipq806x/patches-4.9/0030-qcom-ipq4019-use-correct-clock-for-i2c-bus-0.patch +++ b/target/linux/ipq806x/patches-4.9/0019-qcom-ipq4019-use-correct-clock-for-i2c-bus-0.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0031-qcom-ipq4019-enable-DMA-for-spi.patch b/target/linux/ipq806x/patches-4.9/0020-qcom-ipq4019-enable-DMA-for-spi.patch index e715618..c1fa5c7 100644 --- a/target/linux/ipq806x/patches-4.9/0031-qcom-ipq4019-enable-DMA-for-spi.patch +++ b/target/linux/ipq806x/patches-4.9/0020-qcom-ipq4019-enable-DMA-for-spi.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0032-qcom-ipq4019-use-v2-of-the-kpss-bringup-mechanism.patch b/target/linux/ipq806x/patches-4.9/0021-qcom-ipq4019-use-v2-of-the-kpss-bringup-mechanism.patch index 9263ffd..887ebf3 100644 --- a/target/linux/ipq806x/patches-4.9/0032-qcom-ipq4019-use-v2-of-the-kpss-bringup-mechanism.patch +++ b/target/linux/ipq806x/patches-4.9/0021-qcom-ipq4019-use-v2-of-the-kpss-bringup-mechanism.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0033-dts-ipq4019-support-ARMv7-PMU.patch b/target/linux/ipq806x/patches-4.9/0022-dts-ipq4019-support-ARMv7-PMU.patch index 0ea66d9..e5dbba7 100644 --- a/target/linux/ipq806x/patches-4.9/0033-dts-ipq4019-support-ARMv7-PMU.patch +++ b/target/linux/ipq806x/patches-4.9/0022-dts-ipq4019-support-ARMv7-PMU.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0034-qcom-ipq4019-Add-IPQ4019-USB-HS-SS-PHY-drivers.patch b/target/linux/ipq806x/patches-4.9/0023-qcom-ipq4019-Add-IPQ4019-USB-HS-SS-PHY-drivers.patch index 86a8a87..e8f4bfc 100644 --- a/target/linux/ipq806x/patches-4.9/0034-qcom-ipq4019-Add-IPQ4019-USB-HS-SS-PHY-drivers.patch +++ b/target/linux/ipq806x/patches-4.9/0023-qcom-ipq4019-Add-IPQ4019-USB-HS-SS-PHY-drivers.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0035-qcom-ipq4019-add-USB-nodes-to-ipq4019-SoC-device-tre.patch b/target/linux/ipq806x/patches-4.9/0024-qcom-ipq4019-add-USB-nodes-to-ipq4019-SoC-device-tre.patch index 61768bc..cea6bd0 100644 --- a/target/linux/ipq806x/patches-4.9/0035-qcom-ipq4019-add-USB-nodes-to-ipq4019-SoC-device-tre.patch +++ b/target/linux/ipq806x/patches-4.9/0024-qcom-ipq4019-add-USB-nodes-to-ipq4019-SoC-device-tre.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0036-qcom-ipq4019-enable-USB-bus-for-DK01.1-board.patch b/target/linux/ipq806x/patches-4.9/0025-qcom-ipq4019-enable-USB-bus-for-DK01.1-board.patch index 77be518..975f42f 100644 --- a/target/linux/ipq806x/patches-4.9/0036-qcom-ipq4019-enable-USB-bus-for-DK01.1-board.patch +++ b/target/linux/ipq806x/patches-4.9/0025-qcom-ipq4019-enable-USB-bus-for-DK01.1-board.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0037-dts-ipq4019-Add-support-for-IPQ4019-DK04-board.patch b/target/linux/ipq806x/patches-4.9/0026-dts-ipq4019-Add-support-for-IPQ4019-DK04-board.patch index 21d7e39..32c52b0 100644 --- a/target/linux/ipq806x/patches-4.9/0037-dts-ipq4019-Add-support-for-IPQ4019-DK04-board.patch +++ b/target/linux/ipq806x/patches-4.9/0026-dts-ipq4019-Add-support-for-IPQ4019-DK04-board.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/012-1-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch b/target/linux/ipq806x/patches-4.9/0027-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch index 5c22b79..f7ff1da 100644 --- a/target/linux/ipq806x/patches-4.9/012-1-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch +++ b/target/linux/ipq806x/patches-4.9/0027-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch @@ -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 @@ diff --git a/target/linux/ipq806x/patches-4.9/012-2-clk-qcom-Add-support-for-RPM-Clocks.patch b/target/linux/ipq806x/patches-4.9/0028-clk-qcom-Add-support-for-RPM-Clocks.patch index 4933ffb..72b392d 100644 --- a/target/linux/ipq806x/patches-4.9/012-2-clk-qcom-Add-support-for-RPM-Clocks.patch +++ b/target/linux/ipq806x/patches-4.9/0028-clk-qcom-Add-support-for-RPM-Clocks.patch @@ -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 @@ diff --git a/target/linux/ipq806x/patches-4.9/012-3-clk-qcom-clk-rpm-Fix-clk_hw-references.patch b/target/linux/ipq806x/patches-4.9/0029-clk-qcom-clk-rpm-Fix-clk_hw-references.patch index e5c1870..1b1b558 100644 --- a/target/linux/ipq806x/patches-4.9/012-3-clk-qcom-clk-rpm-Fix-clk_hw-references.patch +++ b/target/linux/ipq806x/patches-4.9/0029-clk-qcom-clk-rpm-Fix-clk_hw-references.patch @@ -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. diff --git a/target/linux/ipq806x/patches-4.9/0030-clk-Disable-i2c-device-on-gsbi4.patch b/target/linux/ipq806x/patches-4.9/0030-clk-Disable-i2c-device-on-gsbi4.patch new file mode 100644 index 0000000..a4fe548 --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0030-clk-Disable-i2c-device-on-gsbi4.patch @@ -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, + }, + }, + }; diff --git a/target/linux/ipq806x/patches-4.9/0031-mtd-add-SMEM-parser-for-QCOM-platforms.patch b/target/linux/ipq806x/patches-4.9/0031-mtd-add-SMEM-parser-for-QCOM-platforms.patch new file mode 100644 index 0000000..d956a3b --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0031-mtd-add-SMEM-parser-for-QCOM-platforms.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0032-phy-add-qcom-dwc3-phy.patch b/target/linux/ipq806x/patches-4.9/0032-phy-add-qcom-dwc3-phy.patch new file mode 100644 index 0000000..314631d --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0032-phy-add-qcom-dwc3-phy.patch @@ -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"); diff --git a/target/linux/ipq806x/patches-4.9/0033-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch b/target/linux/ipq806x/patches-4.9/0033-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch new file mode 100644 index 0000000..a6c7953 --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0033-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch @@ -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. + diff --git a/target/linux/ipq806x/patches-4.9/0034-ARM-Add-Krait-L2-register-accessor-functions.patch b/target/linux/ipq806x/patches-4.9/0034-ARM-Add-Krait-L2-register-accessor-functions.patch new file mode 100644 index 0000000..8c69b97 --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0034-ARM-Add-Krait-L2-register-accessor-functions.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0035-clk-mux-Split-out-register-accessors-for-reuse.patch b/target/linux/ipq806x/patches-4.9/0035-clk-mux-Split-out-register-accessors-for-reuse.patch new file mode 100644 index 0000000..426cabd --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0035-clk-mux-Split-out-register-accessors-for-reuse.patch @@ -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); diff --git a/target/linux/ipq806x/patches-4.9/0036-clk-Avoid-sending-high-rates-to-downstream-clocks-du.patch b/target/linux/ipq806x/patches-4.9/0036-clk-Avoid-sending-high-rates-to-downstream-clocks-du.patch new file mode 100644 index 0000000..f4a47c7 --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0036-clk-Avoid-sending-high-rates-to-downstream-clocks-du.patch @@ -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; + diff --git a/target/linux/ipq806x/patches-4.9/0037-clk-Add-safe-switch-hook.patch b/target/linux/ipq806x/patches-4.9/0037-clk-Add-safe-switch-hook.patch new file mode 100644 index 0000000..bff605f --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0037-clk-Add-safe-switch-hook.patch @@ -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, diff --git a/target/linux/ipq806x/patches-4.9/0038-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch b/target/linux/ipq806x/patches-4.9/0038-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch new file mode 100644 index 0000000..e3797cf --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0038-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0039-clk-qcom-Add-HFPLL-driver.patch b/target/linux/ipq806x/patches-4.9/0039-clk-qcom-Add-HFPLL-driver.patch new file mode 100644 index 0000000..92266f7 --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0039-clk-qcom-Add-HFPLL-driver.patch @@ -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"); diff --git a/target/linux/ipq806x/patches-4.9/0040-clk-qcom-Add-IPQ806X-s-HFPLLs.patch b/target/linux/ipq806x/patches-4.9/0040-clk-qcom-Add-IPQ806X-s-HFPLLs.patch new file mode 100644 index 0000000..8a51af7 --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0040-clk-qcom-Add-IPQ806X-s-HFPLLs.patch @@ -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[] = { diff --git a/target/linux/ipq806x/patches-4.9/0041-clk-qcom-Add-support-for-Krait-clocks.patch b/target/linux/ipq806x/patches-4.9/0041-clk-qcom-Add-support-for-Krait-clocks.patch new file mode 100644 index 0000000..902e49d --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0041-clk-qcom-Add-support-for-Krait-clocks.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0042-clk-qcom-Add-KPSS-ACC-GCC-driver.patch b/target/linux/ipq806x/patches-4.9/0042-clk-qcom-Add-KPSS-ACC-GCC-driver.patch new file mode 100644 index 0000000..c499436 --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0042-clk-qcom-Add-KPSS-ACC-GCC-driver.patch @@ -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"); diff --git a/target/linux/ipq806x/patches-4.9/0043-clk-qcom-Add-Krait-clock-controller-driver.patch b/target/linux/ipq806x/patches-4.9/0043-clk-qcom-Add-Krait-clock-controller-driver.patch new file mode 100644 index 0000000..7e2e41e --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0043-clk-qcom-Add-Krait-clock-controller-driver.patch @@ -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"); diff --git a/target/linux/ipq806x/patches-4.9/0044-clk-qcom-krait-Remove-CLK_IS_ROOT.patch b/target/linux/ipq806x/patches-4.9/0044-clk-qcom-krait-Remove-CLK_IS_ROOT.patch new file mode 100644 index 0000000..c3761ee --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0044-clk-qcom-krait-Remove-CLK_IS_ROOT.patch @@ -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); + diff --git a/target/linux/ipq806x/patches-4.9/0045-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch b/target/linux/ipq806x/patches-4.9/0045-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch new file mode 100644 index 0000000..f5b5ea0 --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0045-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch @@ -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"); diff --git a/target/linux/ipq806x/patches-4.9/0046-cpufreq-qcom-Remove-platform-data.patch b/target/linux/ipq806x/patches-4.9/0046-cpufreq-qcom-Remove-platform-data.patch new file mode 100644 index 0000000..aba2d19 --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0046-cpufreq-qcom-Remove-platform-data.patch @@ -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"); diff --git a/target/linux/ipq806x/patches-4.9/0047-mtd-nand-Create-a-BBT-flag-to-access-bad-block-marke.patch b/target/linux/ipq806x/patches-4.9/0047-mtd-nand-Create-a-BBT-flag-to-access-bad-block-marke.patch new file mode 100644 index 0000000..f70821e --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0047-mtd-nand-Create-a-BBT-flag-to-access-bad-block-marke.patch @@ -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. diff --git a/target/linux/ipq806x/patches-4.9/0048-PM-OPP-HACK-Allow-to-set-regulator-without-opp_list.patch b/target/linux/ipq806x/patches-4.9/0048-PM-OPP-HACK-Allow-to-set-regulator-without-opp_list.patch new file mode 100644 index 0000000..da9c0db --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0048-PM-OPP-HACK-Allow-to-set-regulator-without-opp_list.patch @@ -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))) { diff --git a/target/linux/ipq806x/patches-4.9/0049-PM-OPP-Support-adjusting-OPP-voltages-at-runtime.patch b/target/linux/ipq806x/patches-4.9/0049-PM-OPP-Support-adjusting-OPP-voltages-at-runtime.patch new file mode 100644 index 0000000..e2a4eed --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0049-PM-OPP-Support-adjusting-OPP-voltages-at-runtime.patch @@ -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; diff --git a/target/linux/ipq806x/patches-4.9/0050-OPP-Allow-notifiers-to-call-dev_pm_opp_get_-voltage-.patch b/target/linux/ipq806x/patches-4.9/0050-OPP-Allow-notifiers-to-call-dev_pm_opp_get_-voltage-.patch new file mode 100644 index 0000000..7b41157 --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0050-OPP-Allow-notifiers-to-call-dev_pm_opp_get_-voltage-.patch @@ -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); diff --git a/target/linux/ipq806x/patches-4.9/0051-PM-OPP-Add-a-helper-to-get-an-opp-regulator-for-devi.patch b/target/linux/ipq806x/patches-4.9/0051-PM-OPP-Add-a-helper-to-get-an-opp-regulator-for-devi.patch new file mode 100644 index 0000000..fc1a36e --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0051-PM-OPP-Add-a-helper-to-get-an-opp-regulator-for-devi.patch @@ -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); + diff --git a/target/linux/ipq806x/patches-4.9/0052-PM-OPP-Update-the-voltage-tolerance-when-adjusting-t.patch b/target/linux/ipq806x/patches-4.9/0052-PM-OPP-Update-the-voltage-tolerance-when-adjusting-t.patch new file mode 100644 index 0000000..9065911 --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0052-PM-OPP-Update-the-voltage-tolerance-when-adjusting-t.patch @@ -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); diff --git a/target/linux/ipq806x/patches-4.9/167-ARM-qcom_rpm_fix_support_for_smb208.patch b/target/linux/ipq806x/patches-4.9/0053-regulator-add-smb208-support.patch index d46e97b..0d2862c 100644 --- a/target/linux/ipq806x/patches-4.9/167-ARM-qcom_rpm_fix_support_for_smb208.patch +++ b/target/linux/ipq806x/patches-4.9/0053-regulator-add-smb208-support.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0054-cpufreq-dt-Handle-OPP-voltage-adjust-events.patch b/target/linux/ipq806x/patches-4.9/0054-cpufreq-dt-Handle-OPP-voltage-adjust-events.patch new file mode 100644 index 0000000..8c1b75a --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0054-cpufreq-dt-Handle-OPP-voltage-adjust-events.patch @@ -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: diff --git a/target/linux/ipq806x/patches-4.9/0055-cpufreq-dt-Add-L2-frequency-scaling-support.patch b/target/linux/ipq806x/patches-4.9/0055-cpufreq-dt-Add-L2-frequency-scaling-support.patch new file mode 100644 index 0000000..e2d6233 --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0055-cpufreq-dt-Add-L2-frequency-scaling-support.patch @@ -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 */ diff --git a/target/linux/ipq806x/patches-4.9/0056-cpufreq-dt-Add-missing-rcu-locks.patch b/target/linux/ipq806x/patches-4.9/0056-cpufreq-dt-Add-missing-rcu-locks.patch new file mode 100644 index 0000000..c0eb2eb --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0056-cpufreq-dt-Add-missing-rcu-locks.patch @@ -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) { diff --git a/target/linux/ipq806x/patches-4.9/0057-clk-qcom-Add-regmap-mux-div-clocks-support.patch b/target/linux/ipq806x/patches-4.9/0057-clk-qcom-Add-regmap-mux-div-clocks-support.patch new file mode 100644 index 0000000..c5cdc79 --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0057-clk-qcom-Add-regmap-mux-div-clocks-support.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0058-clk-qcom-Always-add-factor-clock-for-xo-clocks.patch b/target/linux/ipq806x/patches-4.9/0058-clk-qcom-Always-add-factor-clock-for-xo-clocks.patch new file mode 100644 index 0000000..f679cf7 --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0058-clk-qcom-Always-add-factor-clock-for-xo-clocks.patch @@ -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); + } diff --git a/target/linux/ipq806x/patches-4.9/0059-ARM-cpuidle-Add-cpuidle-support-for-QCOM-cpus.patch b/target/linux/ipq806x/patches-4.9/0059-ARM-cpuidle-Add-cpuidle-support-for-QCOM-cpus.patch new file mode 100644 index 0000000..b734986 --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0059-ARM-cpuidle-Add-cpuidle-support-for-QCOM-cpus.patch @@ -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. diff --git a/target/linux/ipq806x/patches-4.9/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch b/target/linux/ipq806x/patches-4.9/0060-HACK-arch-arm-force-ZRELADDR-on-arch-qcom.patch index c308560..5f04d56 100644 --- a/target/linux/ipq806x/patches-4.9/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch +++ b/target/linux/ipq806x/patches-4.9/0060-HACK-arch-arm-force-ZRELADDR-on-arch-qcom.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0061-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch b/target/linux/ipq806x/patches-4.9/0061-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch new file mode 100644 index 0000000..a4a9575 --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0061-mtd-rootfs-conflicts-with-OpenWrt-auto-mounting.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/307-gcc-Added-the-enable-regs-and-mask-for-PRNG.patch b/target/linux/ipq806x/patches-4.9/0062-ipq806x-gcc-Added-the-enable-regs-and-mask-for-PRNG.patch index 7a7cff3..a3e0a1d 100644 --- a/target/linux/ipq806x/patches-4.9/307-gcc-Added-the-enable-regs-and-mask-for-PRNG.patch +++ b/target/linux/ipq806x/patches-4.9/0062-ipq806x-gcc-Added-the-enable-regs-and-mask-for-PRNG.patch @@ -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 = { diff --git a/target/linux/ipq806x/patches-4.9/0063-ipq806x-clk-gcc-add-tsens-child-node.patch b/target/linux/ipq806x/patches-4.9/0063-ipq806x-clk-gcc-add-tsens-child-node.patch new file mode 100644 index 0000000..9f154ae --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0063-ipq806x-clk-gcc-add-tsens-child-node.patch @@ -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; + } + diff --git a/target/linux/ipq806x/patches-4.9/311-add-rpmcc-for-ipq806x.patch b/target/linux/ipq806x/patches-4.9/0064-clk-clk-rpm-fixes.patch index bb9dd87..b803488 100644 --- a/target/linux/ipq806x/patches-4.9/311-add-rpmcc-for-ipq806x.patch +++ b/target/linux/ipq806x/patches-4.9/0064-clk-clk-rpm-fixes.patch @@ -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 : diff --git a/target/linux/ipq806x/patches-4.9/801-override-compiler-flags.patch b/target/linux/ipq806x/patches-4.9/0065-arm-override-compiler-flags.patch index 6591dbd..e5af7ff 100644 --- a/target/linux/ipq806x/patches-4.9/801-override-compiler-flags.patch +++ b/target/linux/ipq806x/patches-4.9/0065-arm-override-compiler-flags.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/802-GPIO-add-named-gpio-exports.patch b/target/linux/ipq806x/patches-4.9/0066-GPIO-add-named-gpio-exports.patch index 3434864..4aa8000 100644 --- a/target/linux/ipq806x/patches-4.9/802-GPIO-add-named-gpio-exports.patch +++ b/target/linux/ipq806x/patches-4.9/0066-GPIO-add-named-gpio-exports.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/996-ATAG_DTB_COMPAT_CMDLINE_MANGLE.patch b/target/linux/ipq806x/patches-4.9/0067-generic-Mangle-bootloader-s-kernel-arguments.patch index 36490a2..123d353 100644 --- a/target/linux/ipq806x/patches-4.9/996-ATAG_DTB_COMPAT_CMDLINE_MANGLE.patch +++ b/target/linux/ipq806x/patches-4.9/0067-generic-Mangle-bootloader-s-kernel-arguments.patch @@ -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 diff --git a/target/linux/ipq806x/patches-4.9/0068-spi-add-gpio-cs-support.patch b/target/linux/ipq806x/patches-4.9/0068-spi-add-gpio-cs-support.patch new file mode 100644 index 0000000..0c03bc9 --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0068-spi-add-gpio-cs-support.patch @@ -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); |