diff --git a/root/target/linux/ipq40xx/patches-5.15/105-mtd-nand-add-Gigadevice-GD5F2GQ4XB-support.patch b/root/target/linux/ipq40xx/patches-5.15/105-mtd-nand-add-Gigadevice-GD5F2GQ4XB-support.patch new file mode 100644 index 00000000..1222b164 --- /dev/null +++ b/root/target/linux/ipq40xx/patches-5.15/105-mtd-nand-add-Gigadevice-GD5F2GQ4XB-support.patch @@ -0,0 +1,22 @@ +Index: linux-5.4.124/drivers/mtd/nand/spi/gigadevice.c +=================================================================== +--- linux-5.4.124.orig/drivers/mtd/nand/spi/gigadevice.c ++++ linux-5.4.124/drivers/mtd/nand/spi/gigadevice.c +@@ -242,7 +242,17 @@ static const struct spinand_info gigadev + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, + gd5fxgq4xa_ecc_get_status)), ++ SPINAND_INFO("GD5F2GQ4xB", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xD2), ++ NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, ++ gd5fxgq4xa_ecc_get_status)), + SPINAND_INFO("GD5F4GQ4xA", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf4), + NAND_MEMORG(1, 2048, 64, 64, 4096, 80, 1, 1, 1), + NAND_ECCREQ(8, 512), diff --git a/root/target/linux/ipq40xx/patches-5.15/106-mtd-nand-add-W25N02KV-support.patch b/root/target/linux/ipq40xx/patches-5.15/106-mtd-nand-add-W25N02KV-support.patch new file mode 100644 index 00000000..94ee3a17 --- /dev/null +++ b/root/target/linux/ipq40xx/patches-5.15/106-mtd-nand-add-W25N02KV-support.patch @@ -0,0 +1,33 @@ +--- a/drivers/mtd/nand/spi/winbond.c.orig 2022-04-05 20:17:39.937148919 +0200 ++++ b/drivers/mtd/nand/spi/winbond.c 2022-04-05 20:26:10.336062557 +0200 +@@ -76,7 +76,7 @@ + + static const struct spinand_info winbond_spinand_table[] = { + SPINAND_INFO("W25M02GV", +- SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab), ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab21), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 2), + NAND_ECCREQ(1, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -86,11 +86,20 @@ + SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), + SPINAND_SELECT_TARGET(w25m02gv_select_target)), + SPINAND_INFO("W25N01GV", +- SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa), ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa21), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(1, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, ++ &update_cache_variants), ++ 0, ++ SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)), ++ SPINAND_INFO("W25N02KV", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa22), ++ NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(1, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)), diff --git a/root/target/linux/ipq40xx/patches-5.15/311-qcom-ipq-add-bootconfig-driver.patch b/root/target/linux/ipq40xx/patches-5.15/311-qcom-ipq-add-bootconfig-driver.patch new file mode 100644 index 00000000..945d4fa4 --- /dev/null +++ b/root/target/linux/ipq40xx/patches-5.15/311-qcom-ipq-add-bootconfig-driver.patch @@ -0,0 +1,509 @@ +Index: linux-5.4.124/drivers/platform/Kconfig +=================================================================== +--- linux-5.4.124.orig/drivers/platform/Kconfig ++++ linux-5.4.124/drivers/platform/Kconfig +@@ -13,7 +13,11 @@ source "drivers/platform/chrome/Kconfig" + source "drivers/platform/mellanox/Kconfig" + + source "drivers/platform/mikrotik/Kconfig" + + source "drivers/platform/olpc/Kconfig" + + source "drivers/platform/surface/Kconfig" ++ ++if ARCH_QCOM ++source "drivers/platform/ipq/Kconfig" ++endif +Index: linux-5.4.124/drivers/platform/Makefile +=================================================================== +--- linux-5.4.124.orig/drivers/platform/Makefile ++++ linux-5.4.124/drivers/platform/Makefile +@@ -9,5 +9,6 @@ obj-$(CONFIG_MIPS) += mips/ + obj-$(CONFIG_OLPC_EC) += olpc/ + obj-$(CONFIG_GOLDFISH) += goldfish/ + obj-$(CONFIG_CHROME_PLATFORMS) += chrome/ + obj-$(CONFIG_MIKROTIK) += mikrotik/ + obj-$(CONFIG_SURFACE_PLATFORMS) += surface/ ++obj-$(CONFIG_ARCH_QCOM) += ipq/ +Index: linux-5.4.124/drivers/platform/ipq/bootconfig.c +=================================================================== +--- /dev/null ++++ linux-5.4.124/drivers/platform/ipq/bootconfig.c +@@ -0,0 +1,403 @@ ++/* ++ * Copyright (c) 2015-2016 The Linux Foundation. All rights reserved. ++ * ++ * Permission to use, copy, modify, and/or distribute this software for any ++ * purpose with or without fee is hereby granted, provided that the above ++ * copyright notice and this permission notice appear in all copies. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "bootconfig.h" ++ ++static struct proc_dir_entry *boot_info_dir; ++static struct proc_dir_entry *partname_dir[NUM_ALT_PARTITION]; ++ ++static unsigned int num_parts; ++static unsigned int flash_type_emmc; ++ ++struct sbl_if_dualboot_info_type_v2 *bootconfig1; ++struct sbl_if_dualboot_info_type_v2 *bootconfig2; ++ ++static int getbinary_show(struct seq_file *m, void *v) ++{ ++ struct sbl_if_dualboot_info_type_v2 *sbl_info_v2; ++ ++ sbl_info_v2 = m->private; ++ memcpy(m->buf + m->count, sbl_info_v2, ++ sizeof(struct sbl_if_dualboot_info_type_v2)); ++ m->count += sizeof(struct sbl_if_dualboot_info_type_v2); ++ ++ return 0; ++} ++ ++static int getbinary_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, getbinary_show, PDE_DATA(inode)); ++} ++ ++static const struct file_operations getbinary_ops = { ++ .open = getbinary_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static int part_upgradepartition_show(struct seq_file *m, void *v) ++{ ++ struct per_part_info *part_info_t = m->private; ++ ++ /* ++ * In case of NOR\NAND, SBLs change the names of paritions in ++ * such a way that the partition to upgrade is always suffixed ++ * by _1. This is not the case in eMMC as paritions are read ++ * from GPT and we have no control on it. So for eMMC we need ++ * to check and generate the name wheres for NOR\NAND it is ++ * always _1 SBLs should be modified not to change partition ++ * names so that it is consistent with GPT. Till that is done ++ * we will take care of it here. ++ */ ++ ++ if (flash_type_emmc && (part_info_t->primaryboot)) ++ seq_printf(m, "%s\n", part_info_t->name); ++ else ++ seq_printf(m, "%s_1\n", part_info_t->name); ++ ++ return 0; ++ ++} ++ ++static int part_upgradepartition_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, part_upgradepartition_show, PDE_DATA(inode)); ++} ++ ++static const struct file_operations upgradepartition_ops = { ++ .open = part_upgradepartition_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++ ++static ssize_t part_primaryboot_write(struct file *file, ++ const char __user *user, ++ size_t count, loff_t *data) ++{ ++ int ret; ++ char optstr[64]; ++ struct per_part_info *part_entry; ++ unsigned long val; ++ ++ part_entry = PDE_DATA(file_inode(file)); ++ ++ if (count == 0 || count > sizeof(optstr)) ++ return -EINVAL; ++ ++ ret = copy_from_user(optstr, user, count); ++ if (ret) ++ return ret; ++ ++ optstr[count - 1] = '\0'; ++ ++ ret = kstrtoul(optstr, 0, &val); ++ if (ret) ++ return ret; ++ ++ part_entry->primaryboot = val; ++ ++ return count; ++ ++} ++ ++static int part_primaryboot_show(struct seq_file *m, void *v) ++{ ++ struct per_part_info *part_entry; ++ ++ part_entry = m->private; ++ seq_printf(m, "%x\n", part_entry->primaryboot); ++ return 0; ++} ++ ++static int part_primaryboot_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, part_primaryboot_show, PDE_DATA(inode)); ++} ++ ++static const struct file_operations primaryboot_ops = { ++ .open = part_primaryboot_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++ .write = part_primaryboot_write, ++}; ++ ++ ++struct sbl_if_dualboot_info_type_v2 *read_bootconfig_mtd( ++ struct mtd_info *master, ++ uint64_t offset) ++{ ++ ++ size_t retlen = 0; ++ struct sbl_if_dualboot_info_type_v2 *bootconfig_mtd; ++ int ret; ++ ++ while (mtd_block_isbad(master, offset)) { ++ offset += master->erasesize; ++ if (offset >= master->size) { ++ pr_alert("Bad blocks occurred while reading from \"%s\"\n", ++ master->name); ++ return NULL; ++ } ++ } ++ bootconfig_mtd = kmalloc(sizeof(struct sbl_if_dualboot_info_type_v2), ++ GFP_ATOMIC); ++ ++ if (!bootconfig_mtd) ++ return NULL; ++ ++ ret = mtd_read(master, offset, ++ sizeof(struct sbl_if_dualboot_info_type_v2), ++ &retlen, (void *)bootconfig_mtd); ++ if (ret < 0) { ++ pr_alert("error occured while reading from \"%s\"\n", ++ master->name); ++ bootconfig_mtd = NULL; ++ kfree(bootconfig_mtd); ++ return NULL; ++ } ++ ++ if (bootconfig_mtd->magic_start != SMEM_DUAL_BOOTINFO_MAGIC_START) { ++ pr_alert("Magic not found in \"%s\"\n", master->name); ++ kfree(bootconfig_mtd); ++ return NULL; ++ } ++ ++ return bootconfig_mtd; ++} ++ ++struct sbl_if_dualboot_info_type_v2 *read_bootconfig_emmc(struct gendisk *disk, ++ struct hd_struct *part) ++{ ++ sector_t n; ++ Sector sect; ++ int ret; ++ unsigned char *data; ++ struct sbl_if_dualboot_info_type_v2 *bootconfig_emmc; ++ unsigned ssz; ++ struct block_device *bdev = NULL; ++ ++ bdev = bdget_disk(disk, 0); ++ if (!bdev) ++ return NULL; ++ ++ bdev->bd_invalidated = 1; ++ ret = blkdev_get(bdev, FMODE_READ , NULL); ++ if (ret) ++ return NULL; ++ ++ ssz = bdev_logical_block_size(bdev); ++ bootconfig_emmc = kmalloc(ssz, GFP_ATOMIC); ++ if (!bootconfig_emmc) ++ return NULL; ++ ++ n = part->start_sect * (bdev_logical_block_size(bdev) / 512); ++ data = read_dev_sector(bdev, n, §); ++ put_dev_sector(sect); ++ blkdev_put(bdev, FMODE_READ); ++ if (!data) { ++ kfree(bootconfig_emmc); ++ return NULL; ++ } ++ ++ memcpy(bootconfig_emmc, data, 512); ++ ++ if (bootconfig_emmc->magic_start != SMEM_DUAL_BOOTINFO_MAGIC_START) { ++ pr_alert("Magic not found\n"); ++ kfree(bootconfig_emmc); ++ return NULL; ++ } ++ ++ return bootconfig_emmc; ++} ++ ++#define BOOTCONFIG_PARTITION "0:BOOTCONFIG" ++#define BOOTCONFIG_PARTITION1 "0:BOOTCONFIG1" ++#define ROOTFS_PARTITION "rootfs" ++ ++static int __init bootconfig_partition_init(void) ++{ ++ struct per_part_info *part_info; ++ int i; ++ struct gendisk *disk = NULL; ++ struct disk_part_iter piter; ++ struct hd_struct *part; ++ struct mtd_info *mtd; ++ int partno; ++ ++ /* ++ * In case of NOR\NAND boot, there is a chance that emmc ++ * might have bootconfig paritions. This will try to read ++ * the bootconfig partitions and create a proc entry which ++ * is not correct since it is not booting from emmc. ++ */ ++ ++ mtd = get_mtd_device_nm(ROOTFS_PARTITION); ++ if (IS_ERR(mtd)) ++ flash_type_emmc = 1; ++ mtd = get_mtd_device_nm(BOOTCONFIG_PARTITION); ++ if (!IS_ERR(mtd)) { ++ ++ bootconfig1 = read_bootconfig_mtd(mtd, 0); ++ mtd = get_mtd_device_nm(BOOTCONFIG_PARTITION1); ++ if (IS_ERR(mtd)) { ++ pr_alert("%s: " BOOTCONFIG_PARTITION1 " not found\n", ++ __func__); ++ return 0; ++ } ++ ++ bootconfig2 = read_bootconfig_mtd(mtd, 0); ++ } else if (flash_type_emmc == 1) { ++ flash_type_emmc = 0; ++ disk = get_gendisk(MKDEV(MMC_BLOCK_MAJOR, 0), &partno); ++ if (!disk) ++ return 0; ++ ++ disk_part_iter_init(&piter, disk, DISK_PITER_INCL_PART0); ++ while ((part = disk_part_iter_next(&piter))) { ++ ++ if (part->info) { ++ if (!strcmp((char *)part->info->volname, ++ BOOTCONFIG_PARTITION)) { ++ bootconfig1 = read_bootconfig_emmc(disk, ++ part); ++ } ++ ++ if (!strcmp((char *)part->info->volname, ++ BOOTCONFIG_PARTITION1)) { ++ bootconfig2 = read_bootconfig_emmc(disk, ++ part); ++ flash_type_emmc = 1; ++ } ++ } ++ } ++ disk_part_iter_exit(&piter); ++ ++ } ++ ++ if (!bootconfig1) { ++ if (bootconfig2) ++ bootconfig1 = bootconfig2; ++ else ++ return 0; ++ } ++ ++ if (!bootconfig2) { ++ if (bootconfig1) ++ bootconfig2 = bootconfig1; ++ else ++ return 0; ++ } ++/* ++ * The following check is to handle the case when an image without ++ * apps upgrade support is upgraded to the image that supports APPS ++ * upgrade. Earlier, the bootconfig file will be chosen based on age, ++ * but now bootconfig1 only is considered and bootconfig2 is a backup. ++ * When bootconfig2 is active in the older image and sysupgrade ++ * is done to it, we copy the bootconfig2 to bootconfig1 so that the ++ * failsafe parameters can be retained. ++ */ ++ if (bootconfig2->age > bootconfig1->age) ++ bootconfig1 = bootconfig2; ++ ++ num_parts = bootconfig1->numaltpart; ++ bootconfig1->age++; ++ part_info = (struct per_part_info *)bootconfig1->per_part_entry; ++ boot_info_dir = proc_mkdir("boot_info", NULL); ++ if (!boot_info_dir) ++ return 0; ++ ++ for (i = 0; i < num_parts; i++) { ++ if (!flash_type_emmc && ++ (strncmp(part_info[i].name, "kernel", ++ ALT_PART_NAME_LENGTH) == 0)) ++ continue; ++ ++ partname_dir[i] = proc_mkdir(part_info[i].name, boot_info_dir); ++ if (partname_dir != NULL) { ++ proc_create_data("primaryboot", S_IRUGO, ++ partname_dir[i], ++ &primaryboot_ops, ++ part_info + i); ++ proc_create_data("upgradepartition", S_IRUGO, ++ partname_dir[i], ++ &upgradepartition_ops, ++ part_info + i); ++ } ++ } ++ ++ proc_create_data("getbinary_bootconfig", S_IRUGO, boot_info_dir, ++ &getbinary_ops, bootconfig1); ++ proc_create_data("getbinary_bootconfig1", S_IRUGO, boot_info_dir, ++ &getbinary_ops, bootconfig1); ++ ++ return 0; ++} ++module_init(bootconfig_partition_init); ++ ++static void __exit bootconfig_partition_exit(void) ++{ ++ struct per_part_info *part_info; ++ int i; ++ ++ if (!bootconfig1) ++ return; ++ ++ if (!bootconfig2) ++ return; ++ ++ part_info = (struct per_part_info *)bootconfig1->per_part_entry; ++ for (i = 0; i < num_parts; i++) { ++ if (!flash_type_emmc && ++ (strncmp(part_info[i].name, "kernel", ++ ALT_PART_NAME_LENGTH) == 0)) ++ continue; ++ ++ remove_proc_entry("primaryboot", partname_dir[i]); ++ remove_proc_entry("upgradepartition", partname_dir[i]); ++ remove_proc_entry(part_info[i].name, boot_info_dir); ++ } ++ remove_proc_entry("getbinary_bootconfig", boot_info_dir); ++ remove_proc_entry("getbinary_bootconfig1", boot_info_dir); ++ remove_proc_entry("boot_info", NULL); ++ kfree(bootconfig1); ++ kfree(bootconfig2); ++} ++ ++module_exit(bootconfig_partition_exit); ++ ++MODULE_LICENSE("Dual BSD/GPL"); +Index: linux-5.4.124/drivers/platform/ipq/bootconfig.h +=================================================================== +--- /dev/null ++++ linux-5.4.124/drivers/platform/ipq/bootconfig.h +@@ -0,0 +1,43 @@ ++/* ++ * Copyright (c) 2015-2016 The Linux Foundation. All rights reserved. ++ * ++ * Permission to use, copy, modify, and/or distribute this software for any ++ * purpose with or without fee is hereby granted, provided that the above ++ * copyright notice and this permission notice appear in all copies. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ */ ++ ++#ifndef _BOOTCONFIG_H_ ++#define _BOOTCONFIG_H_ ++ ++#define BOOTCONFIG_PART_IDX_MAX 21 ++ ++#define ALT_PART_NAME_LENGTH 16 ++struct per_part_info { ++ char name[ALT_PART_NAME_LENGTH]; ++ uint32_t primaryboot; ++}; ++ ++#define NUM_ALT_PARTITION 8 ++ ++/* version 2 */ ++#define SMEM_DUAL_BOOTINFO_MAGIC_START 0xA3A2A1A0 ++#define SMEM_DUAL_BOOTINFO_MAGIC_END 0xB3B2B1B0 ++ ++struct sbl_if_dualboot_info_type_v2 { ++ uint32_t magic_start; ++ uint32_t age; ++ uint32_t numaltpart; ++ struct per_part_info per_part_entry[NUM_ALT_PARTITION]; ++ uint32_t magic_end; ++} __packed; ++ ++#endif /* _BOOTCONFIG_H_ */ ++ +Index: linux-5.4.124/drivers/platform/ipq/Kconfig +=================================================================== +--- /dev/null ++++ linux-5.4.124/drivers/platform/ipq/Kconfig +@@ -0,0 +1,11 @@ ++menu "IPQ specific device drivers" ++ depends on ARCH_QCOM ++ ++config BOOTCONFIG_PARTITION ++ tristate "BOOTCONFIG Partition support" ++ help ++ Say Y here if you would like to use hard disks under Linux which ++ were partitioned using MTD/EFI. ++ ++endmenu ++ +Index: linux-5.4.124/drivers/platform/ipq/Makefile +=================================================================== +--- /dev/null ++++ linux-5.4.124/drivers/platform/ipq/Makefile +@@ -0,0 +1,5 @@ ++# ++# Makefile for the IPQ specific device drivers. ++# ++ ++obj-$(CONFIG_BOOTCONFIG_PARTITION) += bootconfig.o diff --git a/root/target/linux/ipq40xx/patches-5.15/420-firmware-qcom-scm-disable-SDI.patch b/root/target/linux/ipq40xx/patches-5.15/420-firmware-qcom-scm-disable-SDI.patch new file mode 100644 index 00000000..b2d7a403 --- /dev/null +++ b/root/target/linux/ipq40xx/patches-5.15/420-firmware-qcom-scm-disable-SDI.patch @@ -0,0 +1,47 @@ +--- a/drivers/firmware/qcom_scm.c ++++ b/drivers/firmware/qcom_scm.c +@@ -404,6 +404,20 @@ static int __qcom_scm_set_dload_mode(str + return qcom_scm_call_atomic(__scm->dev, &desc, NULL); + } + ++static int __qcom_scm_disable_sdi(struct device *dev) ++{ ++ struct qcom_scm_desc desc = { ++ .svc = QCOM_SCM_SVC_BOOT, ++ .cmd = QCOM_SCM_BOOT_CONFIG_SDI, ++ .arginfo = QCOM_SCM_ARGS(2), ++ .args[0] = 1 /* 1: disable watchdog debug */, ++ .args[1] = 0 /* 0: disable SDI */, ++ .owner = ARM_SMCCC_OWNER_SIP, ++ }; ++ ++ return qcom_scm_call(__scm->dev, &desc, NULL); ++} ++ + static void qcom_scm_set_download_mode(bool enable) + { + bool avail; +@@ -1320,6 +1334,13 @@ static int qcom_scm_probe(struct platfor + if (download_mode) + qcom_scm_set_download_mode(true); + ++ /* ++ * Factory firmware leaves SDI (a debug interface), which prevents ++ * clean reboot. ++ */ ++ if (of_machine_is_compatible("google,wifi")) ++ __qcom_scm_disable_sdi(__scm->dev); ++ + return 0; + } + +--- a/drivers/firmware/qcom_scm.h ++++ b/drivers/firmware/qcom_scm.h +@@ -77,6 +77,7 @@ extern int scm_legacy_call(struct device + #define QCOM_SCM_SVC_BOOT 0x01 + #define QCOM_SCM_BOOT_SET_ADDR 0x01 + #define QCOM_SCM_BOOT_TERMINATE_PC 0x02 ++#define QCOM_SCM_BOOT_CONFIG_SDI 0x09 + #define QCOM_SCM_BOOT_SET_DLOAD_MODE 0x10 + #define QCOM_SCM_BOOT_SET_REMOTE_STATE 0x0a + #define QCOM_SCM_FLUSH_FLAG_MASK 0x3 diff --git a/root/target/linux/ipq40xx/patches-5.15/421-firmware-qcom-scm-cold-boot-address.patch b/root/target/linux/ipq40xx/patches-5.15/421-firmware-qcom-scm-cold-boot-address.patch new file mode 100644 index 00000000..13ebb5b0 --- /dev/null +++ b/root/target/linux/ipq40xx/patches-5.15/421-firmware-qcom-scm-cold-boot-address.patch @@ -0,0 +1,121 @@ +--- a/drivers/firmware/qcom_scm-legacy.c ++++ b/drivers/firmware/qcom_scm-legacy.c +@@ -13,6 +13,9 @@ + #include + #include + ++#include ++#include ++ + #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(); ++} ++ + /** + * scm_legacy_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); diff --git a/root/target/linux/ipq40xx/patches-5.15/933-add-new-xtx-nand-rev.patch b/root/target/linux/ipq40xx/patches-5.15/933-add-new-xtx-nand-rev.patch new file mode 100644 index 00000000..885d6131 --- /dev/null +++ b/root/target/linux/ipq40xx/patches-5.15/933-add-new-xtx-nand-rev.patch @@ -0,0 +1,14 @@ +Index: linux-5.4.147/drivers/mtd/nand/spi/micron.c +=================================================================== +--- linux-5.4.147.orig/drivers/mtd/nand/spi/micron.c ++++ linux-5.4.147/drivers/mtd/nand/spi/micron.c +@@ -128,8 +128,8 @@ static const struct spinand_manufacturer + + const struct spinand_manufacturer micron_spinand_manufacturer = { + .id = SPINAND_MFR_MICRON, +- .name = "Micron", ++ .name = "Micron / XTX", + .chips = micron_spinand_table, + .nchips = ARRAY_SIZE(micron_spinand_table), + .ops = µn_spinand_manuf_ops, + };