mirror of
https://github.com/Ysurac/openmptcprouter.git
synced 2025-03-09 15:40:20 +00:00
Add a directory by kernel instead of a common root, add qnap-301w and rpi4 kernel 6.1 suppport
This commit is contained in:
parent
e910436a7a
commit
46837ec4c0
9459 changed files with 362648 additions and 116345 deletions
25
5.4/target/linux/ipq40xx/patches-5.4/
Normal file
25
5.4/target/linux/ipq40xx/patches-5.4/
Normal file
|
@ -0,0 +1,25 @@
|
|||
--- a/drivers/mtd/nand/spi/xtx.c
|
||||
+++ b/drivers/mtd/nand/spi/xtx.c
|
||||
@@ -37,8 +37,8 @@
|
||||
if (section)
|
||||
return -ERANGE;
|
||||
|
||||
- region->offset = 8;
|
||||
- region->length = 40;
|
||||
+ region->offset = 48;
|
||||
+ region->length = 16;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -49,8 +49,9 @@
|
||||
if (section)
|
||||
return -ERANGE;
|
||||
|
||||
- region->offset = 1;
|
||||
- region->length = 7;
|
||||
+ /* Reserve 2 bytes for the BBM. */
|
||||
+ region->offset = 2;
|
||||
+ region->length = 62;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
From 4267880319bc1a2270d352e0ded6d6386242a7ef 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
|
||||
|
||||
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 +++++
|
||||
4 files changed, 91 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/gpio/gpiolib-of.c
|
||||
+++ b/drivers/gpio/gpiolib-of.c
|
||||
@@ -19,6 +19,8 @@
|
||||
#include <linux/pinctrl/pinctrl.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/platform_device.h>
|
||||
|
||||
#include "gpiolib.h"
|
||||
#include "gpiolib-of.h"
|
||||
@@ -915,3 +917,68 @@ void of_gpiochip_remove(struct gpio_chip
|
||||
{
|
||||
of_node_put(chip->of_node);
|
||||
}
|
||||
+
|
||||
+static struct of_device_id gpio_export_ids[] = {
|
||||
+ { .compatible = "gpio-export" },
|
||||
+ { /* sentinel */ }
|
||||
+};
|
||||
+
|
||||
+static int of_gpio_export_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct device_node *np = pdev->dev.of_node;
|
||||
+ struct device_node *cnp;
|
||||
+ u32 val;
|
||||
+ int nb = 0;
|
||||
+
|
||||
+ for_each_child_of_node(np, cnp) {
|
||||
+ const char *name = NULL;
|
||||
+ int gpio;
|
||||
+ bool dmc;
|
||||
+ int max_gpio = 1;
|
||||
+ int i;
|
||||
+
|
||||
+ of_property_read_string(cnp, "gpio-export,name", &name);
|
||||
+
|
||||
+ if (!name)
|
||||
+ max_gpio = of_gpio_count(cnp);
|
||||
+
|
||||
+ for (i = 0; i < max_gpio; i++) {
|
||||
+ unsigned flags = 0;
|
||||
+ enum of_gpio_flags of_flags;
|
||||
+
|
||||
+ gpio = of_get_gpio_flags(cnp, i, &of_flags);
|
||||
+ if (!gpio_is_valid(gpio))
|
||||
+ return gpio;
|
||||
+
|
||||
+ if (of_flags == OF_GPIO_ACTIVE_LOW)
|
||||
+ flags |= GPIOF_ACTIVE_LOW;
|
||||
+
|
||||
+ if (!of_property_read_u32(cnp, "gpio-export,output", &val))
|
||||
+ flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
|
||||
+ else
|
||||
+ flags |= GPIOF_IN;
|
||||
+
|
||||
+ if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np)))
|
||||
+ continue;
|
||||
+
|
||||
+ dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change");
|
||||
+ gpio_export_with_name(gpio, dmc, name);
|
||||
+ nb++;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ dev_info(&pdev->dev, "%d gpio(s) exported\n", nb);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver gpio_export_driver = {
|
||||
+ .driver = {
|
||||
+ .name = "gpio-export",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .of_match_table = of_match_ptr(gpio_export_ids),
|
||||
+ },
|
||||
+ .probe = of_gpio_export_probe,
|
||||
+};
|
||||
+
|
||||
+module_platform_driver(gpio_export_driver);
|
||||
--- a/drivers/gpio/gpiolib-sysfs.c
|
||||
+++ b/drivers/gpio/gpiolib-sysfs.c
|
||||
@@ -571,7 +571,7 @@ static struct class gpio_class = {
|
||||
*
|
||||
* Returns zero on success, else an error.
|
||||
*/
|
||||
-int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
|
||||
+int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name)
|
||||
{
|
||||
struct gpio_chip *chip;
|
||||
struct gpio_device *gdev;
|
||||
@@ -633,6 +633,8 @@ int gpiod_export(struct gpio_desc *desc,
|
||||
offset = gpio_chip_hwgpio(desc);
|
||||
if (chip->names && chip->names[offset])
|
||||
ioname = chip->names[offset];
|
||||
+ if (name)
|
||||
+ ioname = name;
|
||||
|
||||
dev = device_create_with_groups(&gpio_class, &gdev->dev,
|
||||
MKDEV(0, 0), data, gpio_groups,
|
||||
@@ -654,6 +656,12 @@ err_unlock:
|
||||
gpiod_dbg(desc, "%s: status %d\n", __func__, status);
|
||||
return status;
|
||||
}
|
||||
+EXPORT_SYMBOL_GPL(__gpiod_export);
|
||||
+
|
||||
+int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
|
||||
+{
|
||||
+ return __gpiod_export(desc, direction_may_change, NULL);
|
||||
+}
|
||||
EXPORT_SYMBOL_GPL(gpiod_export);
|
||||
|
||||
static int match_export(struct device *dev, const void *desc)
|
||||
--- a/include/asm-generic/gpio.h
|
||||
+++ b/include/asm-generic/gpio.h
|
||||
@@ -127,6 +127,12 @@ static inline int gpio_export(unsigned g
|
||||
return gpiod_export(gpio_to_desc(gpio), direction_may_change);
|
||||
}
|
||||
|
||||
+int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name);
|
||||
+static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change, const char *name)
|
||||
+{
|
||||
+ return __gpiod_export(gpio_to_desc(gpio), direction_may_change, name);
|
||||
+}
|
||||
+
|
||||
static inline int gpio_export_link(struct device *dev, const char *name,
|
||||
unsigned gpio)
|
||||
{
|
||||
--- a/include/linux/gpio/consumer.h
|
||||
+++ b/include/linux/gpio/consumer.h
|
||||
@@ -668,6 +668,7 @@ static inline void devm_acpi_dev_remove_
|
||||
|
||||
#if IS_ENABLED(CONFIG_GPIOLIB) && IS_ENABLED(CONFIG_GPIO_SYSFS)
|
||||
|
||||
+int _gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name);
|
||||
int gpiod_export(struct gpio_desc *desc, bool direction_may_change);
|
||||
int gpiod_export_link(struct device *dev, const char *name,
|
||||
struct gpio_desc *desc);
|
||||
@@ -675,6 +676,13 @@ void gpiod_unexport(struct gpio_desc *de
|
||||
|
||||
#else /* CONFIG_GPIOLIB && CONFIG_GPIO_SYSFS */
|
||||
|
||||
+static inline int _gpiod_export(struct gpio_desc *desc,
|
||||
+ bool direction_may_change,
|
||||
+ const char *name)
|
||||
+{
|
||||
+ return -ENOSYS;
|
||||
+}
|
||||
+
|
||||
static inline int gpiod_export(struct gpio_desc *desc,
|
||||
bool direction_may_change)
|
||||
{
|
|
@ -0,0 +1,20 @@
|
|||
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,6 +242,15 @@ static const struct spinand_info gigadev
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
|
||||
gd5fxgq4xa_ecc_get_status)),
|
||||
+ SPINAND_INFO("GD5F2GQ4xB", 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", 0xF4,
|
||||
NAND_MEMORG(1, 2048, 64, 64, 4096, 80, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
|
@ -0,0 +1,53 @@
|
|||
--- a/drivers/mtd/nand/spi/winbond.c
|
||||
+++ b/drivers/mtd/nand/spi/winbond.c
|
||||
@@ -75,7 +75,7 @@
|
||||
}
|
||||
|
||||
static const struct spinand_info winbond_spinand_table[] = {
|
||||
- SPINAND_INFO("W25M02GV", 0xAB,
|
||||
+ SPINAND_INFO("W25M02GV", 0xAB21,
|
||||
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 2),
|
||||
NAND_ECCREQ(1, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
@@ -84,8 +84,16 @@
|
||||
0,
|
||||
SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
|
||||
SPINAND_SELECT_TARGET(w25m02gv_select_target)),
|
||||
- SPINAND_INFO("W25N01GV", 0xAA,
|
||||
+ SPINAND_INFO("W25N01GV", 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", 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,
|
||||
@@ -102,17 +110,21 @@
|
||||
static int winbond_spinand_detect(struct spinand_device *spinand)
|
||||
{
|
||||
u8 *id = spinand->id.data;
|
||||
+ u16 did;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Winbond SPI NAND read ID need a dummy byte,
|
||||
* so the first byte in raw_id is dummy.
|
||||
+ * Second and also third byte are device id
|
||||
*/
|
||||
if (id[1] != SPINAND_MFR_WINBOND)
|
||||
return 0;
|
||||
+ else
|
||||
+ did = (id[2] << 8) + id[3];
|
||||
|
||||
ret = spinand_match_and_init(spinand, winbond_spinand_table,
|
||||
- ARRAY_SIZE(winbond_spinand_table), id[2]);
|
||||
+ ARRAY_SIZE(winbond_spinand_table), did);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
--- a/drivers/mtd/nand/spi/xtx.c
|
||||
+++ b/drivers/mtd/nand/spi/xtx.c
|
||||
@@ -37,8 +37,8 @@
|
||||
if (section)
|
||||
return -ERANGE;
|
||||
|
||||
- region->offset = 8;
|
||||
- region->length = 40;
|
||||
+ region->offset = 48;
|
||||
+ region->length = 16;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -49,8 +49,9 @@
|
||||
if (section)
|
||||
return -ERANGE;
|
||||
|
||||
- region->offset = 1;
|
||||
- region->length = 7;
|
||||
+ /* Reserve 2 bytes for the BBM. */
|
||||
+ region->offset = 2;
|
||||
+ region->length = 62;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,506 @@
|
|||
Index: linux-5.4.124/drivers/platform/Kconfig
|
||||
===================================================================
|
||||
--- linux-5.4.124.orig/drivers/platform/Kconfig
|
||||
+++ linux-5.4.124/drivers/platform/Kconfig
|
||||
@@ -13,5 +13,9 @@ source "drivers/platform/chrome/Kconfig"
|
||||
source "drivers/platform/mellanox/Kconfig"
|
||||
|
||||
source "drivers/platform/olpc/Kconfig"
|
||||
|
||||
source "drivers/platform/mikrotik/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,4 +9,5 @@ obj-$(CONFIG_MIPS) += mips/
|
||||
obj-$(CONFIG_OLPC_EC) += olpc/
|
||||
obj-$(CONFIG_GOLDFISH) += goldfish/
|
||||
obj-$(CONFIG_CHROME_PLATFORMS) += chrome/
|
||||
obj-$(CONFIG_MIKROTIK) += mikrotik/
|
||||
+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 <linux/kernel.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/seq_file.h>
|
||||
+#include <asm/setup.h>
|
||||
+#include <linux/mtd/partitions.h>
|
||||
+#include <linux/proc_fs.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/uaccess.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/version.h>
|
||||
+#include <linux/genhd.h>
|
||||
+#include <linux/major.h>
|
||||
+#include <linux/mtd/blktrans.h>
|
||||
+#include <linux/mtd/mtd.h>
|
||||
+#include <linux/types.h>
|
||||
+#include <linux/blkdev.h>
|
||||
+#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
|
|
@ -0,0 +1,53 @@
|
|||
--- a/fs/jffs2/writev.c
|
||||
+++ b/fs/jffs2/writev.c
|
||||
@@ -16,9 +16,18 @@
|
||||
int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct kvec *vecs,
|
||||
unsigned long count, loff_t to, size_t *retlen)
|
||||
{
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = mtd_writev(c->mtd, vecs, count, to, retlen);
|
||||
+
|
||||
if (!jffs2_is_writebuffered(c)) {
|
||||
if (jffs2_sum_active()) {
|
||||
int res;
|
||||
+
|
||||
+ if (ret ||
|
||||
+ *retlen != iov_length((struct iovec *) vecs, count))
|
||||
+ return ret;
|
||||
+
|
||||
res = jffs2_sum_add_kvec(c, vecs, count, (uint32_t) to);
|
||||
if (res) {
|
||||
return res;
|
||||
@@ -26,18 +35,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
- return mtd_writev(c->mtd, vecs, count, to, retlen);
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
int jffs2_flash_direct_write(struct jffs2_sb_info *c, loff_t ofs, size_t len,
|
||||
size_t *retlen, const u_char *buf)
|
||||
{
|
||||
int ret;
|
||||
+
|
||||
ret = mtd_write(c->mtd, ofs, len, retlen, buf);
|
||||
|
||||
if (jffs2_sum_active()) {
|
||||
struct kvec vecs[1];
|
||||
int res;
|
||||
+
|
||||
+ if (ret || *retlen != len)
|
||||
+ return ret;
|
||||
|
||||
vecs[0].iov_base = (unsigned char *) buf;
|
||||
vecs[0].iov_len = len;
|
||||
@@ -47,5 +60,6 @@
|
||||
return res;
|
||||
}
|
||||
}
|
||||
+
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
--- a/drivers/net/phy/qca807x.c
|
||||
+++ b/drivers/net/phy/qca807x.c
|
||||
@@ -122,9 +122,24 @@
|
||||
#define PSGMII_MODE_CTRL_AZ_WORKAROUND_MASK GENMASK(3, 0)
|
||||
#define PSGMII_MMD3_SERDES_CONTROL 0x805a
|
||||
|
||||
+#define QCA8075_PHY4_PREFER_FIBER 0x400
|
||||
+
|
||||
struct qca807x_gpio_priv {
|
||||
struct phy_device *phy;
|
||||
};
|
||||
+
|
||||
+/* Phy medium type */
|
||||
+typedef enum {
|
||||
+ QCA8075_PHY_MEDIUM_COPPER = 0,
|
||||
+ QCA8075_PHY_MEDIUM_FIBER = 1, /**< Fiber */
|
||||
+ QCA8075_PHY_MEDIUM_NULL = 2 /**< NULL */
|
||||
+} qca8075_phy_medium_t;
|
||||
+
|
||||
+static qca8075_phy_medium_t last_phy_medium = QCA8075_PHY_MEDIUM_NULL;
|
||||
+static int last_phydev_link_state = -1;
|
||||
+static int copper_link = 0;
|
||||
+static int fiber_link = 0;
|
||||
+static int report_last_status = 0;
|
||||
|
||||
static int qca807x_get_downshift(struct phy_device *phydev, u8 *data)
|
||||
{
|
||||
@@ -400,6 +415,142 @@
|
||||
}
|
||||
#endif
|
||||
|
||||
+static qca8075_phy_medium_t phy_prefer_medium_get(struct phy_device *phydev)
|
||||
+{
|
||||
+ int val;
|
||||
+ val = phy_read(phydev, QCA807X_CHIP_CONFIGURATION);
|
||||
+
|
||||
+ return ((val & QCA8075_PHY4_PREFER_FIBER) ?
|
||||
+ QCA8075_PHY_MEDIUM_FIBER : QCA8075_PHY_MEDIUM_COPPER);
|
||||
+}
|
||||
+
|
||||
+static qca8075_phy_medium_t phy_active_medium_get(struct phy_device *phydev) {
|
||||
+ int val;
|
||||
+ val = phy_read(phydev, QCA807X_MEDIA_SELECT_STATUS);
|
||||
+
|
||||
+ if (val & QCA807X_MEDIA_DETECTED_COPPER) {
|
||||
+ return QCA8075_PHY_MEDIUM_COPPER;
|
||||
+ } else if ((val & QCA807X_MEDIA_DETECTED_1000_BASE_X) ||
|
||||
+ (val & QCA807X_MEDIA_DETECTED_100_BASE_FX)) {
|
||||
+ return QCA8075_PHY_MEDIUM_FIBER;
|
||||
+ } else {
|
||||
+ return phy_prefer_medium_get(phydev);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int ar40xx_update_link(struct phy_device *phydev)
|
||||
+{
|
||||
+ int status = 0, bmcr;
|
||||
+ qca8075_phy_medium_t phy_medium;
|
||||
+
|
||||
+ phy_medium = phy_active_medium_get(phydev);
|
||||
+
|
||||
+ /* Do a fake read */
|
||||
+ bmcr = phy_read(phydev, MII_BMSR);
|
||||
+ if (bmcr < 0)
|
||||
+ return bmcr;
|
||||
+
|
||||
+ /* Autoneg is being started, therefore disregard BMSR value and
|
||||
+ * report link as down.
|
||||
+ */
|
||||
+ if (bmcr & BMCR_ANRESTART)
|
||||
+ goto done;
|
||||
+
|
||||
+ /* The link state is latched low so that momentary link
|
||||
+ * drops can be detected. Do not double-read the status
|
||||
+ * in polling mode to detect such short link drops except
|
||||
+ * the link was already down.
|
||||
+ */
|
||||
+ if (!phy_polling_mode(phydev) || !phydev->link) {
|
||||
+ status = phy_read(phydev, MII_BMSR);
|
||||
+ if (status < 0)
|
||||
+ return status;
|
||||
+ else if (status & BMSR_LSTATUS)
|
||||
+ goto done;
|
||||
+ }
|
||||
+
|
||||
+ /* Read link and autonegotiation status */
|
||||
+ status = phy_read(phydev, MII_BMSR);
|
||||
+ if (status < 0)
|
||||
+ return status;
|
||||
+done:
|
||||
+ if ( report_last_status > 0 ) {
|
||||
+ report_last_status = 0;
|
||||
+ phydev->link = last_phydev_link_state;
|
||||
+ return 0;
|
||||
+ }
|
||||
+ /* reporting copper/fiber link state to netdev */
|
||||
+ if ((status & BMSR_LSTATUS) == 0) {
|
||||
+ phydev->link = 0;
|
||||
+ if (last_phy_medium == phy_medium) { /* medium not changed */
|
||||
+ if(phydev->link != last_phydev_link_state)
|
||||
+ report_last_status++;
|
||||
+ if (phy_medium == QCA8075_PHY_MEDIUM_FIBER)
|
||||
+ fiber_link = 0;
|
||||
+ else
|
||||
+ copper_link = 0;
|
||||
+ } else { /* medium changed, check current medium*/
|
||||
+ if (phy_medium == QCA8075_PHY_MEDIUM_FIBER) { /* fiber active*/
|
||||
+ if (copper_link == 1) { /* copper active, but not preferred*/
|
||||
+ if(phydev->link == last_phydev_link_state) {
|
||||
+ phydev->link = !phydev->link; /* toggle link state */
|
||||
+ report_last_status++;
|
||||
+ }
|
||||
+ }
|
||||
+ fiber_link = 0;
|
||||
+ } else { /* copper active*/
|
||||
+ if (fiber_link == 1) { /* fiber active, preferred*/
|
||||
+ if(phydev->link == last_phydev_link_state) {
|
||||
+ phydev->link = !phydev->link; /* toggle link state */
|
||||
+ report_last_status++;
|
||||
+ }
|
||||
+ }
|
||||
+ copper_link = 0;
|
||||
+ }
|
||||
+ }
|
||||
+ } else {
|
||||
+ phydev->link = 1;
|
||||
+ if (last_phy_medium == phy_medium){
|
||||
+ if (phy_medium == QCA8075_PHY_MEDIUM_FIBER)
|
||||
+ fiber_link = 1;
|
||||
+ else
|
||||
+ copper_link = 1;
|
||||
+ }
|
||||
+ else {
|
||||
+ if (phy_medium == QCA8075_PHY_MEDIUM_FIBER) { /* fiber active*/
|
||||
+ if (copper_link == 1) { /* copper active, but not preferred*/
|
||||
+ if(phydev->link == last_phydev_link_state) {
|
||||
+ phydev->link = !phydev->link;
|
||||
+ report_last_status++;
|
||||
+ }
|
||||
+ }
|
||||
+ fiber_link = 1;
|
||||
+ } else { /* copper active*/
|
||||
+ if (fiber_link == 1) { /* fiber active, preferred*/
|
||||
+ if(phydev->link == last_phydev_link_state) {
|
||||
+ phydev->link = !phydev->link;
|
||||
+ report_last_status++;
|
||||
+ }
|
||||
+ }
|
||||
+ copper_link = 1;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ phydev->autoneg_complete = status & BMSR_ANEGCOMPLETE ? 1 : 0;
|
||||
+
|
||||
+ /* Consider the case that autoneg was started and "aneg complete"
|
||||
+ * bit has been reset, but "link up" bit not yet.
|
||||
+ */
|
||||
+ if (phydev->autoneg == AUTONEG_ENABLE && !phydev->autoneg_complete)
|
||||
+ phydev->link = 0;
|
||||
+
|
||||
+ last_phy_medium = phy_medium;
|
||||
+ last_phydev_link_state = phydev->link;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int qca807x_read_copper_status(struct phy_device *phydev, bool combo_port)
|
||||
{
|
||||
int ss, err, page, old_link = phydev->link;
|
||||
@@ -415,7 +566,7 @@
|
||||
}
|
||||
|
||||
/* Update the link, but return if there was an error */
|
||||
- err = genphy_update_link(phydev);
|
||||
+ err = ar40xx_update_link(phydev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -499,7 +650,7 @@
|
||||
}
|
||||
|
||||
/* Update the link, but return if there was an error */
|
||||
- err = genphy_update_link(phydev);
|
||||
+ err = ar40xx_update_link(phydev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -559,7 +710,7 @@
|
||||
|
||||
static int qca807x_read_status(struct phy_device *phydev)
|
||||
{
|
||||
- int val;
|
||||
+ int val, err;
|
||||
|
||||
/* Check for Combo port */
|
||||
if (phy_read(phydev, QCA807X_CHIP_CONFIGURATION)) {
|
||||
@@ -572,6 +723,11 @@
|
||||
} else if ((val & QCA807X_MEDIA_DETECTED_1000_BASE_X) ||
|
||||
(val & QCA807X_MEDIA_DETECTED_100_BASE_FX)) {
|
||||
qca807x_read_fiber_status(phydev, true);
|
||||
+ } else {
|
||||
+ /* Update the link, but return if there was an error */
|
||||
+ err = ar40xx_update_link(phydev);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
}
|
||||
} else {
|
||||
qca807x_read_copper_status(phydev, true);
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
Index: linux-5.4.124/drivers/net/phy/ar40xx.c
|
||||
===================================================================
|
||||
--- linux-5.4.124.orig/drivers/net/phy/ar40xx.c
|
||||
+++ linux-5.4.124/drivers/net/phy/ar40xx.c
|
||||
@@ -771,9 +771,58 @@ ar40xx_sw_get_port_link(struct switch_de
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int
|
||||
+ar40xx_sw_delay_reset(struct switch_dev *dev,
|
||||
+ const struct switch_attr *attr,
|
||||
+ struct switch_val *value)
|
||||
+{
|
||||
+ struct ar40xx_priv *priv = swdev_to_ar40xx(dev);
|
||||
+ struct mii_bus *bus;
|
||||
+ int i;
|
||||
+ u16 val;
|
||||
+
|
||||
+ bus = priv->mii_bus;
|
||||
+ for (i = 0; i < AR40XX_NUM_PORTS - 2; i++) {
|
||||
+ mdiobus_write(bus, i, MII_CTRL1000, 0);
|
||||
+ mdiobus_write(bus, i, MII_ADVERTISE, 0);
|
||||
+ mdiobus_write(bus, i, MII_BMCR, BMCR_RESET | BMCR_ANENABLE);
|
||||
+ ar40xx_phy_dbg_read(priv, i, AR40XX_PHY_DEBUG_0, &val);
|
||||
+ val |= AR40XX_PHY_MANU_CTRL_EN;
|
||||
+ ar40xx_phy_dbg_write(priv, i, AR40XX_PHY_DEBUG_0, val);
|
||||
+ /* disable transmit */
|
||||
+ ar40xx_phy_dbg_read(priv, i, AR40XX_PHY_DEBUG_2, &val);
|
||||
+ val &= 0xf00f;
|
||||
+ ar40xx_phy_dbg_write(priv, i, AR40XX_PHY_DEBUG_2, val);
|
||||
+ }
|
||||
+
|
||||
+ msleep(1000 * value->value.i);
|
||||
+
|
||||
+ for (i = 0; i < AR40XX_NUM_PORTS - 2; i++) {
|
||||
+ ar40xx_phy_dbg_read(priv, i, AR40XX_PHY_DEBUG_0, &val);
|
||||
+ val &= ~AR40XX_PHY_MANU_CTRL_EN;
|
||||
+ ar40xx_phy_dbg_write(priv, i, AR40XX_PHY_DEBUG_0, val);
|
||||
+ mdiobus_write(bus, i,
|
||||
+ MII_ADVERTISE, ADVERTISE_ALL |
|
||||
+ ADVERTISE_PAUSE_CAP |
|
||||
+ ADVERTISE_PAUSE_ASYM);
|
||||
+ mdiobus_write(bus, i, MII_CTRL1000, ADVERTISE_1000FULL);
|
||||
+ mdiobus_write(bus, i, MII_BMCR, BMCR_RESET | BMCR_ANENABLE);
|
||||
+ }
|
||||
+
|
||||
+ ar40xx_phy_poll_reset(priv);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static const struct switch_attr ar40xx_sw_attr_globals[] = {
|
||||
{
|
||||
.type = SWITCH_TYPE_INT,
|
||||
+ .name = "soft_reset",
|
||||
+ .description = "Switch soft reset with delay (seconds)",
|
||||
+ .set = ar40xx_sw_delay_reset
|
||||
+ },
|
||||
+ {
|
||||
+ .type = SWITCH_TYPE_INT,
|
||||
.name = "enable_vlan",
|
||||
.description = "Enable VLAN mode",
|
||||
.set = ar40xx_sw_set_vlan,
|
|
@ -0,0 +1,151 @@
|
|||
--- a/drivers/net/phy/ar40xx.c
|
||||
+++ b/drivers/net/phy/ar40xx.c
|
||||
@@ -82,6 +82,13 @@
|
||||
MIB_DESC(1, AR40XX_STATS_TXLATECOL, "TxLateCol"),
|
||||
};
|
||||
|
||||
+static int
|
||||
+ar40xx_atu_flush(struct ar40xx_priv *);
|
||||
+
|
||||
+static int
|
||||
+ar40xx_atu_dump(struct ar40xx_priv *);
|
||||
+
|
||||
+
|
||||
static u32
|
||||
ar40xx_read(struct ar40xx_priv *priv, int reg)
|
||||
{
|
||||
@@ -810,6 +817,35 @@
|
||||
}
|
||||
|
||||
ar40xx_phy_poll_reset(priv);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+ar40xx_sw_atu_flush(struct switch_dev *dev,
|
||||
+ const struct switch_attr *attr,
|
||||
+ struct switch_val *val)
|
||||
+{
|
||||
+ struct ar40xx_priv *priv = swdev_to_ar40xx(dev);
|
||||
+
|
||||
+ return ar40xx_atu_flush(priv);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+ar40xx_sw_atu_dump(struct switch_dev *dev,
|
||||
+ const struct switch_attr *attr,
|
||||
+ struct switch_val *val)
|
||||
+{
|
||||
+ struct ar40xx_priv *priv = swdev_to_ar40xx(dev);
|
||||
+ int len=0;
|
||||
+
|
||||
+ len = ar40xx_atu_dump(priv);
|
||||
+ if(len >= 0){
|
||||
+ val->value.s = priv->buf;
|
||||
+ val->len = len;
|
||||
+ } else {
|
||||
+ val->len = -1;
|
||||
+ }
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -868,6 +904,18 @@
|
||||
.max = AR40XX_NUM_PORTS - 1
|
||||
},
|
||||
{
|
||||
+ .type = SWITCH_TYPE_NOVAL,
|
||||
+ .name = "flush_arl",
|
||||
+ .description = "Flush ARL table",
|
||||
+ .set = ar40xx_sw_atu_flush,
|
||||
+ },
|
||||
+ {
|
||||
+ .type = SWITCH_TYPE_STRING,
|
||||
+ .name = "dump_arl",
|
||||
+ .description = "Dump ARL table with mac and port map",
|
||||
+ .get = ar40xx_sw_atu_dump,
|
||||
+ },
|
||||
+ {
|
||||
.type = SWITCH_TYPE_INT,
|
||||
.name = "linkdown",
|
||||
.description = "Link down all the PHYs",
|
||||
@@ -925,6 +973,65 @@
|
||||
pr_err("ar40xx: timeout for reg %08x: %08x & %08x != %08x\n",
|
||||
(unsigned int)reg, t, mask, val);
|
||||
return -ETIMEDOUT;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+ar40xx_atu_dump(struct ar40xx_priv *priv)
|
||||
+{
|
||||
+ u32 ret;
|
||||
+ u32 i = 0, len = 0, entry_len = 0;
|
||||
+ volatile u32 reg[4] = {0,0,0,0};
|
||||
+ u8 addr[ETH_ALEN] = { 0 };
|
||||
+ char *buf;
|
||||
+
|
||||
+ buf = priv->buf;
|
||||
+ memset(priv->buf, 0, sizeof(priv->buf));
|
||||
+ do {
|
||||
+ ret = ar40xx_wait_bit(priv, AR40XX_REG_ATU_FUNC,
|
||||
+ AR40XX_ATU_FUNC_BUSY, 0);
|
||||
+ if(ret != 0)
|
||||
+ return -ETIMEDOUT;
|
||||
+
|
||||
+ reg[3] = AR40XX_ATU_FUNC_BUSY | AR40XX_ATU_FUNC_OP_GET_NEXT;
|
||||
+ ar40xx_write(priv, AR40XX_REG_ATU_DATA0, reg[0]);
|
||||
+ ar40xx_write(priv, AR40XX_REG_ATU_DATA1, reg[1]);
|
||||
+ ar40xx_write(priv, AR40XX_REG_ATU_DATA2, reg[2]);
|
||||
+ ar40xx_write(priv, AR40XX_REG_ATU_FUNC, reg[3]);
|
||||
+
|
||||
+ ret = ar40xx_wait_bit(priv, AR40XX_REG_ATU_FUNC,
|
||||
+ AR40XX_ATU_FUNC_BUSY, 0);
|
||||
+ if(ret != 0)
|
||||
+ return -ETIMEDOUT;
|
||||
+
|
||||
+ reg[0] = ar40xx_read(priv, AR40XX_REG_ATU_DATA0);
|
||||
+ reg[1] = ar40xx_read(priv, AR40XX_REG_ATU_DATA1);
|
||||
+ reg[2] = ar40xx_read(priv, AR40XX_REG_ATU_DATA2);
|
||||
+ reg[3] = ar40xx_read(priv, AR40XX_REG_ATU_FUNC);
|
||||
+
|
||||
+ if((reg[2] & 0xf) == 0)
|
||||
+ break;
|
||||
+
|
||||
+ for(i=2; i<6; i++)
|
||||
+ addr[i] = (reg[0] >> ((5 - i) << 3)) & 0xff;
|
||||
+ for(i=0; i<2; i++)
|
||||
+ addr[i] = (reg[1] >> ((1 - i) << 3)) & 0xff;
|
||||
+
|
||||
+ len += snprintf(buf + len, sizeof(priv->buf) - len, "MAC: %02x:%02x:%02x:%02x:%02x:%02x ",
|
||||
+ addr[0],addr[1],addr[2],addr[3],addr[4],addr[5]);
|
||||
+ len += snprintf(buf + len, sizeof(priv->buf) - len, "PORTMAP: 0x%02x ", ((reg[1] >> 16) & 0x7f));
|
||||
+
|
||||
+ len += snprintf(buf + len, sizeof(priv->buf) - len, "VID: 0x%x ", ((reg[2] >> 8) & 0xfff));
|
||||
+
|
||||
+ len += snprintf(buf + len, sizeof(priv->buf) - len, "STATUS: 0x%x\n", ((reg[2] & 0xf) == 0xf ? 1 : 0));
|
||||
+
|
||||
+ if (!entry_len)
|
||||
+ entry_len = len;
|
||||
+
|
||||
+ if (sizeof(priv->buf) - len <= entry_len)
|
||||
+ break;
|
||||
+ } while(1);
|
||||
+
|
||||
+ return len;
|
||||
}
|
||||
|
||||
static int
|
||||
--- a/drivers/net/phy/ar40xx.h
|
||||
+++ b/drivers/net/phy/ar40xx.h
|
||||
@@ -243,6 +243,10 @@
|
||||
#define AR40XX_PORT_LOOKUP_LOOPBACK BIT(21)
|
||||
#define AR40XX_PORT_LOOKUP_ING_MIRROR_EN BIT(25)
|
||||
|
||||
+#define AR40XX_REG_ATU_DATA0 0x600
|
||||
+#define AR40XX_REG_ATU_DATA1 0x604
|
||||
+#define AR40XX_REG_ATU_DATA2 0x608
|
||||
+
|
||||
#define AR40XX_REG_ATU_FUNC 0x60c
|
||||
#define AR40XX_ATU_FUNC_OP BITS(0, 4)
|
||||
#define AR40XX_ATU_FUNC_OP_NOOP 0x0
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
--- a/drivers/net/phy/ar40xx.h
|
||||
+++ b/drivers/net/phy/ar40xx.h
|
||||
@@ -282,6 +282,13 @@
|
||||
#define AR40XX_PHY_SPEC_STATUS_DUPLEX BIT(13)
|
||||
#define AR40XX_PHY_SPEC_STATUS_SPEED BITS(14, 2)
|
||||
|
||||
+#define COMBO_PHY_ID 4
|
||||
+#define QCA807X_CHIP_CONFIGURATION 0x1f /* Chip Configuration Register */
|
||||
+
|
||||
+#define QCA8075_PHY4_PREFER_FIBER 0x400
|
||||
+#define PHY4_PREFER_COPPER 0x0
|
||||
+#define PHY4_PREFER_FIBER 0x1
|
||||
+
|
||||
/* port forwarding state */
|
||||
enum {
|
||||
AR40XX_PORT_STATE_DISABLED = 0,
|
||||
--- a/drivers/net/phy/ar40xx.c
|
||||
+++ b/drivers/net/phy/ar40xx.c
|
||||
@@ -612,6 +612,62 @@
|
||||
ar40xx_port_phy_linkdown(priv);
|
||||
else
|
||||
ar40xx_phy_init(priv);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int phy_prefer_medium_set(struct ar40xx_priv *priv, u16 medium)
|
||||
+{
|
||||
+ struct mii_bus *bus;
|
||||
+ u16 phy_medium;
|
||||
+
|
||||
+ bus = priv->mii_bus;
|
||||
+ phy_medium =
|
||||
+ mdiobus_read(bus, COMBO_PHY_ID , QCA807X_CHIP_CONFIGURATION);
|
||||
+
|
||||
+ if (medium == PHY4_PREFER_FIBER) {
|
||||
+ phy_medium |= QCA8075_PHY4_PREFER_FIBER;
|
||||
+ } else {
|
||||
+ phy_medium &= ~QCA8075_PHY4_PREFER_FIBER;
|
||||
+ }
|
||||
+
|
||||
+ mdiobus_write(bus, COMBO_PHY_ID, QCA807X_CHIP_CONFIGURATION, phy_medium );
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+ar40xx_sw_set_preference(struct switch_dev *dev,
|
||||
+ const struct switch_attr *attr,
|
||||
+ struct switch_val *val)
|
||||
+{
|
||||
+ struct ar40xx_priv *priv = swdev_to_ar40xx(dev);
|
||||
+ u16 pref;
|
||||
+ if (val->value.i == 0)
|
||||
+ pref = PHY4_PREFER_COPPER;
|
||||
+ else
|
||||
+ pref = PHY4_PREFER_FIBER;
|
||||
+
|
||||
+ phy_prefer_medium_set(priv, pref);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+ar40xx_sw_get_preference(struct switch_dev *dev,
|
||||
+ const struct switch_attr *attr,
|
||||
+ struct switch_val *val)
|
||||
+{
|
||||
+ struct ar40xx_priv *priv = swdev_to_ar40xx(dev);
|
||||
+
|
||||
+ struct mii_bus *bus;
|
||||
+ u16 phy_medium;
|
||||
+
|
||||
+ bus = priv->mii_bus;
|
||||
+ phy_medium =
|
||||
+ mdiobus_read(bus, COMBO_PHY_ID , QCA807X_CHIP_CONFIGURATION);
|
||||
+
|
||||
+ val->value.i = phy_medium;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -922,6 +978,14 @@
|
||||
.set = ar40xx_sw_set_linkdown,
|
||||
.max = 1
|
||||
},
|
||||
+ {
|
||||
+ .type = SWITCH_TYPE_INT,
|
||||
+ .name = "preference",
|
||||
+ .description = "Set fiber/copper combo preference",
|
||||
+ .set = ar40xx_sw_set_preference,
|
||||
+ .get = ar40xx_sw_get_preference,
|
||||
+ .max = 1
|
||||
+ },
|
||||
};
|
||||
|
||||
static const struct switch_attr ar40xx_sw_attr_port[] = {
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
Index: linux-5.4.147/drivers/usb/host/xhci.h
|
||||
===================================================================
|
||||
--- linux-5.4.147.orig/drivers/usb/host/xhci.h
|
||||
+++ linux-5.4.147/drivers/usb/host/xhci.h
|
||||
@@ -1654,7 +1654,7 @@ struct urb_priv {
|
||||
* (1K bytes * 8bytes/bit) / (4*32 bits) = 64 segment entries in the table,
|
||||
* meaning 64 ring segments.
|
||||
* Initial allocated size of the ERST, in number of entries */
|
||||
-#define ERST_NUM_SEGS 1
|
||||
+#define ERST_NUM_SEGS 16
|
||||
/* Initial allocated size of the ERST, in number of entries */
|
||||
#define ERST_SIZE 64
|
||||
/* Initial number of event segment rings allocated */
|
|
@ -0,0 +1,105 @@
|
|||
--- a/drivers/spi/spi-qup.c
|
||||
+++ b/drivers/spi/spi-qup.c
|
||||
@@ -273,9 +273,6 @@
|
||||
writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
|
||||
controller->base + QUP_OPERATIONAL);
|
||||
|
||||
- if (!remainder)
|
||||
- goto exit;
|
||||
-
|
||||
if (is_block_mode) {
|
||||
num_words = (remainder > words_per_block) ?
|
||||
words_per_block : remainder;
|
||||
@@ -305,13 +302,11 @@
|
||||
* to refresh opflags value because MAX_INPUT_DONE_FLAG may now be
|
||||
* present and this is used to determine if transaction is complete
|
||||
*/
|
||||
-exit:
|
||||
- if (!remainder) {
|
||||
- *opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
|
||||
- if (is_block_mode && *opflags & QUP_OP_MAX_INPUT_DONE_FLAG)
|
||||
- writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
|
||||
- controller->base + QUP_OPERATIONAL);
|
||||
- }
|
||||
+ *opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
|
||||
+ if (is_block_mode && *opflags & QUP_OP_MAX_INPUT_DONE_FLAG)
|
||||
+ writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
|
||||
+ controller->base + QUP_OPERATIONAL);
|
||||
+
|
||||
}
|
||||
|
||||
static void spi_qup_write_to_fifo(struct spi_qup *controller, u32 num_words)
|
||||
@@ -358,10 +353,6 @@
|
||||
/* ACK by clearing service flag */
|
||||
writel_relaxed(QUP_OP_OUT_SERVICE_FLAG,
|
||||
controller->base + QUP_OPERATIONAL);
|
||||
-
|
||||
- /* make sure the interrupt is valid */
|
||||
- if (!remainder)
|
||||
- return;
|
||||
|
||||
if (is_block_mode) {
|
||||
num_words = (remainder > words_per_block) ?
|
||||
@@ -576,24 +567,10 @@
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static bool spi_qup_data_pending(struct spi_qup *controller)
|
||||
-{
|
||||
- unsigned int remainder_tx, remainder_rx;
|
||||
-
|
||||
- remainder_tx = DIV_ROUND_UP(spi_qup_len(controller) -
|
||||
- controller->tx_bytes, controller->w_size);
|
||||
-
|
||||
- remainder_rx = DIV_ROUND_UP(spi_qup_len(controller) -
|
||||
- controller->rx_bytes, controller->w_size);
|
||||
-
|
||||
- return remainder_tx || remainder_rx;
|
||||
-}
|
||||
-
|
||||
static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct spi_qup *controller = dev_id;
|
||||
u32 opflags, qup_err, spi_err;
|
||||
- unsigned long flags;
|
||||
int error = 0;
|
||||
|
||||
qup_err = readl_relaxed(controller->base + QUP_ERROR_FLAGS);
|
||||
@@ -625,11 +602,6 @@
|
||||
error = -EIO;
|
||||
}
|
||||
|
||||
- spin_lock_irqsave(&controller->lock, flags);
|
||||
- if (!controller->error)
|
||||
- controller->error = error;
|
||||
- spin_unlock_irqrestore(&controller->lock, flags);
|
||||
-
|
||||
if (spi_qup_is_dma_xfer(controller->mode)) {
|
||||
writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
|
||||
} else {
|
||||
@@ -638,21 +610,10 @@
|
||||
|
||||
if (opflags & QUP_OP_OUT_SERVICE_FLAG)
|
||||
spi_qup_write(controller);
|
||||
-
|
||||
- if (!spi_qup_data_pending(controller))
|
||||
- complete(&controller->done);
|
||||
- }
|
||||
-
|
||||
- if (error)
|
||||
+ }
|
||||
+
|
||||
+ if ((opflags & QUP_OP_MAX_INPUT_DONE_FLAG) || error)
|
||||
complete(&controller->done);
|
||||
-
|
||||
- if (opflags & QUP_OP_MAX_INPUT_DONE_FLAG) {
|
||||
- if (!spi_qup_is_dma_xfer(controller->mode)) {
|
||||
- if (spi_qup_data_pending(controller))
|
||||
- return IRQ_HANDLED;
|
||||
- }
|
||||
- complete(&controller->done);
|
||||
- }
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
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
|
||||
@@ -91,7 +91,7 @@ static int mt29f2g01abagd_ecc_get_status
|
||||
}
|
||||
|
||||
static const struct spinand_info micron_spinand_table[] = {
|
||||
- SPINAND_INFO("MT29F2G01ABAGD", 0x24,
|
||||
+ SPINAND_INFO("MT29F2G01ABAGD", 0x24, // Note: XTX nand flash XT26G02E have same MAN_ID and DEV_ID
|
||||
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
@@ -128,6 +128,6 @@ static const struct spinand_manufacturer
|
||||
|
||||
const struct spinand_manufacturer micron_spinand_manufacturer = {
|
||||
.id = SPINAND_MFR_MICRON,
|
||||
- .name = "Micron",
|
||||
+ .name = "Micron / XTX",
|
||||
.ops = µn_spinand_manuf_ops,
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue