forked from robimarko/openwrt
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ipq40xx: qcom_scm: Fix cold boot address command
See my upstream questions: https://lore.kernel.org/linux-arm-msm/20200913201608.GA3162100@bDebian/ This effectively reverts upstream Linux commit 13e77747800e ("firmware: qcom: scm: Use atomic SCM for cold boot"), because Google WiFi boot firmwares don't support the atomic variant. This fixes SMP support for Google WiFi. Signed-off-by: Brian Norris <computersforpeace@gmail.com>
- Loading branch information
1 parent
a93ec36
commit 26af098
Showing
1 changed file
with
121 additions
and
0 deletions.
There are no files selected for viewing
121 changes: 121 additions & 0 deletions
121
target/linux/ipq40xx/patches-5.10/421-firmware-qcom-scm-cold-boot-address.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
--- a/drivers/firmware/qcom_scm-legacy.c | ||
+++ b/drivers/firmware/qcom_scm-legacy.c | ||
@@ -13,6 +13,9 @@ | ||
#include <linux/arm-smccc.h> | ||
#include <linux/dma-mapping.h> | ||
|
||
+#include <asm/cacheflush.h> | ||
+#include <asm/outercache.h> | ||
+ | ||
#include "qcom_scm.h" | ||
|
||
static DEFINE_MUTEX(qcom_scm_lock); | ||
@@ -117,6 +120,25 @@ static void __scm_legacy_do(const struct | ||
} while (res->a0 == QCOM_SCM_INTERRUPTED); | ||
} | ||
|
||
+static void qcom_scm_inv_range(unsigned long start, unsigned long end) | ||
+{ | ||
+ u32 cacheline_size, ctr; | ||
+ | ||
+ asm volatile("mrc p15, 0, %0, c0, c0, 1" : "=r" (ctr)); | ||
+ cacheline_size = 4 << ((ctr >> 16) & 0xf); | ||
+ | ||
+ start = round_down(start, cacheline_size); | ||
+ end = round_up(end, cacheline_size); | ||
+ outer_inv_range(start, end); | ||
+ while (start < end) { | ||
+ asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start) | ||
+ : "memory"); | ||
+ start += cacheline_size; | ||
+ } | ||
+ dsb(); | ||
+ isb(); | ||
+} | ||
+ | ||
/** | ||
* qcom_scm_call() - Sends a command to the SCM and waits for the command to | ||
* finish processing. | ||
@@ -160,10 +182,16 @@ int scm_legacy_call(struct device *dev, | ||
|
||
rsp = scm_legacy_command_to_response(cmd); | ||
|
||
- cmd_phys = dma_map_single(dev, cmd, alloc_len, DMA_TO_DEVICE); | ||
- if (dma_mapping_error(dev, cmd_phys)) { | ||
- kfree(cmd); | ||
- return -ENOMEM; | ||
+ if (dev) { | ||
+ cmd_phys = dma_map_single(dev, cmd, alloc_len, DMA_TO_DEVICE); | ||
+ if (dma_mapping_error(dev, cmd_phys)) { | ||
+ kfree(cmd); | ||
+ return -ENOMEM; | ||
+ } | ||
+ } else { | ||
+ cmd_phys = virt_to_phys(cmd); | ||
+ __cpuc_flush_dcache_area(cmd, alloc_len); | ||
+ outer_flush_range(cmd_phys, cmd_phys + alloc_len); | ||
} | ||
|
||
smc.args[0] = 1; | ||
@@ -179,13 +207,26 @@ int scm_legacy_call(struct device *dev, | ||
goto out; | ||
|
||
do { | ||
- dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len, | ||
- sizeof(*rsp), DMA_FROM_DEVICE); | ||
+ if (dev) { | ||
+ dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + | ||
+ cmd_len, sizeof(*rsp), | ||
+ DMA_FROM_DEVICE); | ||
+ } else { | ||
+ unsigned long start = (uintptr_t)cmd + sizeof(*cmd) + | ||
+ cmd_len; | ||
+ qcom_scm_inv_range(start, start + sizeof(*rsp)); | ||
+ } | ||
} while (!rsp->is_complete); | ||
|
||
- dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len + | ||
- le32_to_cpu(rsp->buf_offset), | ||
- resp_len, DMA_FROM_DEVICE); | ||
+ if (dev) { | ||
+ dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len + | ||
+ le32_to_cpu(rsp->buf_offset), | ||
+ resp_len, DMA_FROM_DEVICE); | ||
+ } else { | ||
+ unsigned long start = (uintptr_t)cmd + sizeof(*cmd) + cmd_len + | ||
+ le32_to_cpu(rsp->buf_offset); | ||
+ qcom_scm_inv_range(start, start + resp_len); | ||
+ } | ||
|
||
if (res) { | ||
res_buf = scm_legacy_get_response_buffer(rsp); | ||
@@ -193,7 +234,8 @@ int scm_legacy_call(struct device *dev, | ||
res->result[i] = le32_to_cpu(res_buf[i]); | ||
} | ||
out: | ||
- dma_unmap_single(dev, cmd_phys, alloc_len, DMA_TO_DEVICE); | ||
+ if (dev) | ||
+ dma_unmap_single(dev, cmd_phys, alloc_len, DMA_TO_DEVICE); | ||
kfree(cmd); | ||
return ret; | ||
} | ||
--- a/drivers/firmware/qcom_scm.c | ||
+++ b/drivers/firmware/qcom_scm.c | ||
@@ -344,6 +344,17 @@ int qcom_scm_set_cold_boot_addr(void *en | ||
desc.args[0] = flags; | ||
desc.args[1] = virt_to_phys(entry); | ||
|
||
+ /* | ||
+ * Factory firmware doesn't support the atomic variant. Non-atomic SCMs | ||
+ * require ugly DMA invalidation support that was dropped upstream a | ||
+ * while ago. For more info, see: | ||
+ * | ||
+ * [RFC] qcom_scm: IPQ4019 firmware does not support atomic API? | ||
+ * https://lore.kernel.org/linux-arm-msm/20200913201608.GA3162100@bDebian/ | ||
+ */ | ||
+ if (of_machine_is_compatible("google,wifi")) | ||
+ return qcom_scm_call(__scm ? __scm->dev : NULL, &desc, NULL); | ||
+ | ||
return qcom_scm_call_atomic(__scm ? __scm->dev : NULL, &desc, NULL); | ||
} | ||
EXPORT_SYMBOL(qcom_scm_set_cold_boot_addr); |