kernel: refresh 5.15 patches and config

This commit is contained in:
lean 2022-04-03 13:03:13 +08:00
parent f5307dff2a
commit e449c765d6
73 changed files with 9882 additions and 112 deletions

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -167,6 +167,7 @@ extern void cleanup_module(void);
@@ -164,6 +164,7 @@ extern void cleanup_module(void);
/* Generic info of form tag = "info" */
#define MODULE_INFO(tag, info) __MODULE_INFO(tag, tag, info)
@ -22,7 +22,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
/* For userspace: you can also call me... */
#define MODULE_ALIAS(_alias) MODULE_INFO(alias, _alias)
@@ -236,12 +237,12 @@ extern void cleanup_module(void);
@@ -233,12 +234,12 @@ extern void cleanup_module(void);
* Author(s), use "Name <email>" or just "Name", for multiple
* authors use multiple MODULE_AUTHOR() statements/lines.
*/
@ -38,7 +38,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
/* Creates an alias so file2alias.c can find device table. */
#define MODULE_DEVICE_TABLE(type, name) \
extern typeof(name) __mod_##type##__##name##_device_table \
@@ -268,7 +269,9 @@ extern typeof(name) __mod_##type##__##na
@@ -265,7 +266,9 @@ extern typeof(name) __mod_##type##__##na
*/
#if defined(MODULE) || !defined(CONFIG_SYSFS)
@ -49,7 +49,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
#else
#define MODULE_VERSION(_version) \
MODULE_INFO(version, _version); \
@@ -291,7 +294,7 @@ extern typeof(name) __mod_##type##__##na
@@ -288,7 +291,7 @@ extern typeof(name) __mod_##type##__##na
/* Optional firmware file (or files) needed by the module
* format is simply firmware file name. Multiple firmware
* files require multiple MODULE_FIRMWARE() specifiers */

View File

@ -103,23 +103,15 @@ Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
__stop_unwind_tab = .; \
}
@@ -126,13 +126,13 @@
@@ -126,7 +126,7 @@
__vectors_lma = .; \
OVERLAY 0xffff0000 : NOCROSSREFS AT(__vectors_lma) { \
.vectors { \
- *(.vectors) \
+ KEEP(*(.vectors)) \
+ KEEP(*(.vectors)) \
} \
.vectors.bhb.loop8 { \
- *(.vectors.bhb.loop8) \
+ KEEP(*(.vectors.bhb.loop8)) \
} \
.vectors.bhb.bpiall { \
- *(.vectors.bhb.bpiall) \
+ KEEP(*(.vectors.bhb.bpiall)) \
} \
} \
ARM_LMA(__vectors, .vectors); \
*(.vectors.bhb.loop8) \
@@ -144,7 +144,7 @@
\
__stubs_lma = .; \

View File

@ -0,0 +1,22 @@
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -2379,7 +2379,7 @@ config PADATA
bool
config ASN1
- tristate
+ tristate "ASN1"
help
Build a simple ASN.1 grammar compiler that produces a bytecode output
that can be interpreted by the ASN.1 stream decoder and used to
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -609,7 +609,7 @@ config LIBFDT
bool
config OID_REGISTRY
- tristate
+ tristate "OID"
help
Enable fast lookup object identifier registry.

View File

@ -125,7 +125,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
#include <linux/mutex.h>
#include <linux/err.h>
#include <linux/property.h>
@@ -3340,3 +3341,5 @@ static int __init regmap_initcall(void)
@@ -3341,3 +3342,5 @@ static int __init regmap_initcall(void)
return 0;
}
postcore_initcall(regmap_initcall);

View File

@ -1,10 +1,11 @@
--- a/block/blk.h
+++ b/block/blk.h
@@ -354,6 +354,7 @@ void blk_free_ext_minor(unsigned int min
@@ -354,6 +354,8 @@ void blk_free_ext_minor(unsigned int min
#define ADDPART_FLAG_NONE 0
#define ADDPART_FLAG_RAID 1
#define ADDPART_FLAG_WHOLEDISK 2
+#define ADDPART_FLAG_ROOTDEV 4
+#define ADDPART_FLAG_READONLY 4
+#define ADDPART_FLAG_ROOTDEV 8
int bdev_add_partition(struct gendisk *disk, int partno, sector_t start,
sector_t length);
int bdev_del_partition(struct gendisk *disk, int partno);
@ -73,13 +74,25 @@
#ifdef CONFIG_SGI_PARTITION
sgi_partition,
#endif
@@ -598,6 +605,11 @@ static bool blk_add_partition(struct gen
@@ -408,6 +415,11 @@ static struct block_device *add_partitio
goto out_del;
}
+#ifdef CONFIG_FIT_PARTITION
+ if (flags & ADDPART_FLAG_READONLY)
+ bdev->bd_read_only = true;
+#endif
+
/* everything is up and running, commence */
err = xa_insert(&disk->part_tbl, partno, bdev, GFP_KERNEL);
if (err)
@@ -598,6 +610,11 @@ static bool blk_add_partition(struct gen
(state->parts[p].flags & ADDPART_FLAG_RAID))
md_autodetect_dev(part->bd_dev);
+#ifdef CONFIG_FIT_PARTITION
+ if ((state->parts[p].flags & ADDPART_FLAG_ROOTDEV) && ROOT_DEV == 0)
+ ROOT_DEV = part_to_dev(part)->devt;
+ ROOT_DEV = part->bd_dev;
+#endif
+
return true;
@ -87,19 +100,7 @@
--- a/drivers/mtd/ubi/block.c
+++ b/drivers/mtd/ubi/block.c
@@ -419,7 +419,11 @@ int ubiblock_create(struct ubi_volume_in
gd->fops = &ubiblock_ops;
gd->major = ubiblock_major;
+#ifdef CONFIG_FIT_PARTITION
+ gd->minors = 0;
+#else
gd->minors = 1;
+#endif
gd->first_minor = idr_alloc(&ubiblock_minor_idr, dev, 0, 0, GFP_KERNEL);
if (gd->first_minor < 0) {
dev_err(disk_to_dev(gd),
@@ -428,6 +432,9 @@ int ubiblock_create(struct ubi_volume_in
@@ -428,6 +428,9 @@ int ubiblock_create(struct ubi_volume_in
goto out_cleanup_disk;
}
gd->private_data = dev;
@ -109,6 +110,18 @@
sprintf(gd->disk_name, "ubiblock%d_%d", dev->ubi_num, dev->vol_id);
set_capacity(gd, disk_capacity);
dev->gd = gd;
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -345,6 +345,9 @@ int add_mtd_blktrans_dev(struct mtd_blkt
gd->first_minor = (new->devnum) << tr->part_bits;
gd->minors = 1 << tr->part_bits;
gd->fops = &mtd_block_ops;
+#ifdef CONFIG_FIT_PARTITION
+ gd->flags |= GENHD_FL_EXT_DEVT;
+#endif
if (tr->part_bits)
if (new->devnum < 26)
--- a/block/partitions/efi.c
+++ b/block/partitions/efi.c
@@ -716,6 +716,9 @@ int efi_partition(struct parsed_partitio
@ -145,43 +158,6 @@
typedef struct _gpt_header {
__le64 signature;
--- a/drivers/mtd/mtdblock.c
+++ b/drivers/mtd/mtdblock.c
@@ -338,7 +338,11 @@ static void mtdblock_remove_dev(struct m
static struct mtd_blktrans_ops mtdblock_tr = {
.name = "mtdblock",
.major = MTD_BLOCK_MAJOR,
+#ifdef CONFIG_FIT_PARTITION
+ .part_bits = 1,
+#else
.part_bits = 0,
+#endif
.blksize = 512,
.open = mtdblock_open,
.flush = mtdblock_flush,
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -346,18 +346,8 @@ int add_mtd_blktrans_dev(struct mtd_blkt
gd->minors = 1 << tr->part_bits;
gd->fops = &mtd_block_ops;
- if (tr->part_bits)
- if (new->devnum < 26)
- snprintf(gd->disk_name, sizeof(gd->disk_name),
- "%s%c", tr->name, 'a' + new->devnum);
- else
- snprintf(gd->disk_name, sizeof(gd->disk_name),
- "%s%c%c", tr->name,
- 'a' - 1 + new->devnum / 26,
- 'a' + new->devnum % 26);
- else
- snprintf(gd->disk_name, sizeof(gd->disk_name),
- "%s%d", tr->name, new->devnum);
+ snprintf(gd->disk_name, sizeof(gd->disk_name),
+ "%s%d", tr->name, new->devnum);
set_capacity(gd, ((u64)new->size * tr->blksize) >> 9);
--- a/block/partitions/msdos.c
+++ b/block/partitions/msdos.c
@@ -564,6 +564,15 @@ static void parse_minix(struct parsed_pa

View File

@ -20,7 +20,7 @@ Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
#include <linux/nvmem-provider.h>
#include <linux/mtd/mtd.h>
@@ -697,6 +698,19 @@ int add_mtd_device(struct mtd_info *mtd)
@@ -697,6 +698,16 @@ int add_mtd_device(struct mtd_info *mtd)
of this try_ nonsense, and no bitching about it
either. :) */
__module_get(THIS_MODULE);
@ -31,9 +31,6 @@ Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+ unsigned int index = mtd->index;
+ pr_notice("mtd: device %d (%s) set to be root filesystem\n",
+ mtd->index, mtd->name);
+#ifdef CONFIG_FIT_PARTITION
+ index <<= 1;
+#endif
+ ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, index);
+ }
+

View File

@ -0,0 +1,120 @@
From 6fa9e3678eb002246df1280322b6a024853950a5 Mon Sep 17 00:00:00 2001
From: Ansuel Smith <ansuelsmth@gmail.com>
Date: Mon, 11 Oct 2021 00:53:14 +0200
Subject: [PATCH] drivers: mtd: parsers: add nvmem support to cmdlinepart
Assuming cmdlinepart is only one level deep partition scheme and that
static partition are also defined in DTS, we can assign an of_node for
partition declared from bootargs. cmdlinepart have priority than
fiexed-partition parser so in this specific case the parser doesn't
assign an of_node. Fix this by searching a defined of_node using a
similar fixed_partition parser and if a partition is found with the same
label, check that it has the same offset and size and return the DT
of_node to correctly use NVMEM cells.
Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
drivers/mtd/parsers/cmdlinepart.c | 71 +++++++++++++++++++++++++++++++
1 file changed, 71 insertions(+)
--- a/drivers/mtd/parsers/cmdlinepart.c
+++ b/drivers/mtd/parsers/cmdlinepart.c
@@ -43,6 +43,7 @@
#include <linux/mtd/partitions.h>
#include <linux/module.h>
#include <linux/err.h>
+#include <linux/of.h>
/* debug macro */
#if 0
@@ -323,6 +324,68 @@ static int mtdpart_setup_real(char *s)
return 0;
}
+static int search_fixed_partition(struct mtd_info *master,
+ struct mtd_partition *target_part,
+ struct mtd_partition *fixed_part)
+{
+ struct device_node *mtd_node;
+ struct device_node *ofpart_node;
+ struct device_node *pp;
+ struct mtd_partition part;
+ const char *partname;
+
+ mtd_node = mtd_get_of_node(master);
+ if (!mtd_node)
+ return -EINVAL;
+
+ ofpart_node = of_get_child_by_name(mtd_node, "partitions");
+
+ for_each_child_of_node(ofpart_node, pp) {
+ const __be32 *reg;
+ int len;
+ int a_cells, s_cells;
+
+ reg = of_get_property(pp, "reg", &len);
+ if (!reg) {
+ pr_debug("%s: ofpart partition %pOF (%pOF) missing reg property.\n",
+ master->name, pp,
+ mtd_node);
+ continue;
+ }
+
+ a_cells = of_n_addr_cells(pp);
+ s_cells = of_n_size_cells(pp);
+ if (len / 4 != a_cells + s_cells) {
+ pr_debug("%s: ofpart partition %pOF (%pOF) error parsing reg property.\n",
+ master->name, pp,
+ mtd_node);
+ continue;
+ }
+
+ part.offset = of_read_number(reg, a_cells);
+ part.size = of_read_number(reg + a_cells, s_cells);
+ part.of_node = pp;
+
+ partname = of_get_property(pp, "label", &len);
+ if (!partname)
+ partname = of_get_property(pp, "name", &len);
+ part.name = partname;
+
+ if (!strncmp(target_part->name, part.name, len)) {
+ if (part.offset != target_part->offset)
+ return -EINVAL;
+
+ if (part.size != target_part->size)
+ return -EINVAL;
+
+ memcpy(fixed_part, &part, sizeof(struct mtd_partition));
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
/*
* Main function to be called from the MTD mapping driver/device to
* obtain the partitioning information. At this point the command line
@@ -338,6 +401,7 @@ static int parse_cmdline_partitions(stru
int i, err;
struct cmdline_mtd_partition *part;
const char *mtd_id = master->name;
+ struct mtd_partition fixed_part;
/* parse command line */
if (!cmdline_parsed) {
@@ -382,6 +446,13 @@ static int parse_cmdline_partitions(stru
sizeof(*part->parts) * (part->num_parts - i));
i--;
}
+
+ err = search_fixed_partition(master, &part->parts[i], &fixed_part);
+ if (!err) {
+ part->parts[i].of_node = fixed_part.of_node;
+ pr_info("Found partition defined in DT for %s. Assigning OF node to support nvmem.",
+ part->parts[i].name);
+ }
}
*pparts = kmemdup(part->parts, sizeof(*part->parts) * part->num_parts,

View File

@ -0,0 +1,23 @@
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -46,6 +46,10 @@ config MTD_NAND_ECC_SW_BCH
ECC codes. They are used with NAND devices requiring more than 1 bit
of error correction.
+config MTD_NAND_MTK_BMT
+ bool "Support MediaTek NAND Bad-block Management Table"
+ default n
+
endmenu
endmenu
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -2,6 +2,7 @@
nandcore-objs := core.o bbt.o
obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o
+obj-$(CONFIG_MTD_NAND_MTK_BMT) += mtk_bmt.o mtk_bmt_v2.o mtk_bmt_bbt.o mtk_bmt_nmbm.o
obj-y += onenand/
obj-y += raw/

View File

@ -0,0 +1,821 @@
--- a/include/linux/if_bridge.h
+++ b/include/linux/if_bridge.h
@@ -59,6 +59,7 @@ struct br_ip_list {
#define BR_MRP_LOST_IN_CONT BIT(19)
#define BR_TX_FWD_OFFLOAD BIT(20)
#define BR_BPDU_FILTER BIT(21)
+#define BR_OFFLOAD BIT(22)
#define BR_DEFAULT_AGEING_TIME (300 * HZ)
--- a/net/bridge/Makefile
+++ b/net/bridge/Makefile
@@ -5,7 +5,7 @@
obj-$(CONFIG_BRIDGE) += bridge.o
-bridge-y := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+bridge-y := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o br_offload.o \
br_ioctl.o br_stp.o br_stp_bpdu.o \
br_stp_if.o br_stp_timer.o br_netlink.o \
br_netlink_tunnel.o br_arp_nd_proxy.o
--- a/net/bridge/br.c
+++ b/net/bridge/br.c
@@ -18,6 +18,7 @@
#include <net/switchdev.h>
#include "br_private.h"
+#include "br_private_offload.h"
/*
* Handle changes in state of network devices enslaved to a bridge.
@@ -381,6 +382,10 @@ static int __init br_init(void)
if (err)
goto err_out;
+ err = br_offload_init();
+ if (err)
+ goto err_out0;
+
err = register_pernet_subsys(&br_net_ops);
if (err)
goto err_out1;
@@ -430,6 +435,8 @@ err_out3:
err_out2:
unregister_pernet_subsys(&br_net_ops);
err_out1:
+ br_offload_fini();
+err_out0:
br_fdb_fini();
err_out:
stp_proto_unregister(&br_stp_proto);
@@ -452,6 +459,7 @@ static void __exit br_deinit(void)
#if IS_ENABLED(CONFIG_ATM_LANE)
br_fdb_test_addr_hook = NULL;
#endif
+ br_offload_fini();
br_fdb_fini();
}
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -524,6 +524,8 @@ void br_dev_setup(struct net_device *dev
br->bridge_hello_time = br->hello_time = 2 * HZ;
br->bridge_forward_delay = br->forward_delay = 15 * HZ;
br->bridge_ageing_time = br->ageing_time = BR_DEFAULT_AGEING_TIME;
+ br->offload_cache_size = 128;
+ br->offload_cache_reserved = 8;
dev->max_mtu = ETH_MAX_MTU;
br_netfilter_rtable_init(br);
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -23,6 +23,7 @@
#include <net/switchdev.h>
#include <trace/events/bridge.h>
#include "br_private.h"
+#include "br_private_offload.h"
static const struct rhashtable_params br_fdb_rht_params = {
.head_offset = offsetof(struct net_bridge_fdb_entry, rhnode),
@@ -518,6 +519,8 @@ static struct net_bridge_fdb_entry *fdb_
fdb->key.vlan_id = vid;
fdb->flags = flags;
fdb->updated = fdb->used = jiffies;
+ INIT_HLIST_HEAD(&fdb->offload_in);
+ INIT_HLIST_HEAD(&fdb->offload_out);
if (rhashtable_lookup_insert_fast(&br->fdb_hash_tbl,
&fdb->rhnode,
br_fdb_rht_params)) {
@@ -794,6 +797,8 @@ static void fdb_notify(struct net_bridge
struct sk_buff *skb;
int err = -ENOBUFS;
+ br_offload_fdb_update(fdb);
+
if (swdev_notify)
br_switchdev_fdb_notify(br, fdb, type);
--- a/net/bridge/br_forward.c
+++ b/net/bridge/br_forward.c
@@ -16,6 +16,7 @@
#include <linux/if_vlan.h>
#include <linux/netfilter_bridge.h>
#include "br_private.h"
+#include "br_private_offload.h"
/* Don't forward packets to originating port or forwarding disabled */
static inline int should_deliver(const struct net_bridge_port *p,
@@ -32,6 +33,8 @@ static inline int should_deliver(const s
int br_dev_queue_push_xmit(struct net *net, struct sock *sk, struct sk_buff *skb)
{
+ br_offload_output(skb);
+
skb_push(skb, ETH_HLEN);
if (!is_skb_forwardable(skb->dev, skb))
goto drop;
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -25,6 +25,7 @@
#include <net/net_namespace.h>
#include "br_private.h"
+#include "br_private_offload.h"
/*
* Determine initial path cost based on speed.
@@ -428,7 +429,7 @@ static struct net_bridge_port *new_nbp(s
p->path_cost = port_cost(dev);
p->priority = 0x8000 >> BR_PORT_BITS;
p->port_no = index;
- p->flags = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD;
+ p->flags = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD | BR_OFFLOAD;
br_init_port(p);
br_set_state(p, BR_STATE_DISABLED);
br_stp_port_timer_init(p);
@@ -771,6 +772,9 @@ void br_port_flags_change(struct net_bri
if (mask & BR_NEIGH_SUPPRESS)
br_recalculate_neigh_suppress_enabled(br);
+
+ if (mask & BR_OFFLOAD)
+ br_offload_port_state(p);
}
bool br_port_flag_is_set(const struct net_device *dev, unsigned long flag)
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -22,6 +22,7 @@
#include <linux/rculist.h>
#include "br_private.h"
#include "br_private_tunnel.h"
+#include "br_private_offload.h"
static int
br_netif_receive_skb(struct net *net, struct sock *sk, struct sk_buff *skb)
@@ -164,6 +165,7 @@ int br_handle_frame_finish(struct net *n
dst->used = now;
br_forward(dst->dst, skb, local_rcv, false);
} else {
+ br_offload_skb_disable(skb);
if (!mcast_hit)
br_flood(br, skb, pkt_type, local_rcv, false);
else
@@ -297,6 +299,9 @@ static rx_handler_result_t br_handle_fra
memset(skb->cb, 0, sizeof(struct br_input_skb_cb));
p = br_port_get_rcu(skb->dev);
+ if (br_offload_input(p, skb))
+ return RX_HANDLER_CONSUMED;
+
if (p->flags & BR_VLAN_TUNNEL)
br_handle_ingress_vlan_tunnel(skb, p, nbp_vlan_group_rcu(p));
--- /dev/null
+++ b/net/bridge/br_offload.c
@@ -0,0 +1,438 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/kernel.h>
+#include <linux/workqueue.h>
+#include "br_private.h"
+#include "br_private_offload.h"
+
+static DEFINE_SPINLOCK(offload_lock);
+
+struct bridge_flow_key {
+ u8 dest[ETH_ALEN];
+ u8 src[ETH_ALEN];
+#ifdef CONFIG_BRIDGE_VLAN_FILTERING
+ u16 vlan_tag;
+ bool vlan_present;
+#endif
+};
+
+struct bridge_flow {
+ struct net_bridge_port *port;
+ struct rhash_head node;
+ struct bridge_flow_key key;
+#ifdef CONFIG_BRIDGE_VLAN_FILTERING
+ bool vlan_out_present;
+ u16 vlan_out;
+#endif
+
+ unsigned long used;
+ struct net_bridge_fdb_entry *fdb_in, *fdb_out;
+ struct hlist_node fdb_list_in, fdb_list_out;
+
+ struct rcu_head rcu;
+};
+
+static const struct rhashtable_params flow_params = {
+ .automatic_shrinking = true,
+ .head_offset = offsetof(struct bridge_flow, node),
+ .key_len = sizeof(struct bridge_flow_key),
+ .key_offset = offsetof(struct bridge_flow, key),
+};
+
+static struct kmem_cache *offload_cache __read_mostly;
+
+static void
+flow_rcu_free(struct rcu_head *head)
+{
+ struct bridge_flow *flow;
+
+ flow = container_of(head, struct bridge_flow, rcu);
+ kmem_cache_free(offload_cache, flow);
+}
+
+static void
+__br_offload_flow_free(struct bridge_flow *flow)
+{
+ flow->used = 0;
+ hlist_del(&flow->fdb_list_in);
+ hlist_del(&flow->fdb_list_out);
+
+ call_rcu(&flow->rcu, flow_rcu_free);
+}
+
+static void
+br_offload_flow_free(struct bridge_flow *flow)
+{
+ if (rhashtable_remove_fast(&flow->port->offload.rht, &flow->node,
+ flow_params) != 0)
+ return;
+
+ __br_offload_flow_free(flow);
+}
+
+static bool
+br_offload_flow_fdb_refresh_time(struct bridge_flow *flow,
+ struct net_bridge_fdb_entry *fdb)
+{
+ if (!time_after(flow->used, fdb->updated))
+ return false;
+
+ fdb->updated = flow->used;
+
+ return true;
+}
+
+
+static void
+br_offload_flow_refresh_time(struct bridge_flow *flow)
+{
+ br_offload_flow_fdb_refresh_time(flow, flow->fdb_in);
+ br_offload_flow_fdb_refresh_time(flow, flow->fdb_out);
+}
+
+static void
+br_offload_destroy_cb(void *ptr, void *arg)
+{
+ struct bridge_flow *flow = ptr;
+
+ __br_offload_flow_free(flow);
+}
+
+static bool
+br_offload_need_gc(struct net_bridge_port *p)
+{
+ return (atomic_read(&p->offload.rht.nelems) +
+ p->br->offload_cache_reserved) >= p->br->offload_cache_size;
+}
+
+static void
+br_offload_gc_work(struct work_struct *work)
+{
+ struct rhashtable_iter hti;
+ struct net_bridge_port *p;
+ struct bridge_flow *gc_flow = NULL;
+ struct bridge_flow *flow;
+ unsigned long gc_used;
+
+ p = container_of(work, struct net_bridge_port, offload.gc_work);
+
+ if (!br_offload_need_gc(p))
+ return;
+
+ rhashtable_walk_enter(&p->offload.rht, &hti);
+ rhashtable_walk_start(&hti);
+ while ((flow = rhashtable_walk_next(&hti)) != NULL) {
+ unsigned long used;
+
+ if (IS_ERR(flow))
+ continue;
+
+ used = READ_ONCE(flow->used);
+ if (!used)
+ continue;
+
+ if (gc_flow && !time_before(used, gc_used))
+ continue;
+
+ gc_flow = flow;
+ gc_used = used;
+ }
+ rhashtable_walk_stop(&hti);
+ rhashtable_walk_exit(&hti);
+
+ if (!gc_flow)
+ return;
+
+ spin_lock_bh(&offload_lock);
+ if (br_offload_need_gc(p) && gc_flow &&
+ gc_flow->used == gc_used)
+ br_offload_flow_free(gc_flow);
+ if (p->offload.enabled && br_offload_need_gc(p))
+ queue_work(system_long_wq, work);
+ spin_unlock_bh(&offload_lock);
+
+}
+
+void br_offload_port_state(struct net_bridge_port *p)
+{
+ struct net_bridge_port_offload *o = &p->offload;
+ bool enabled = true;
+ bool flush = false;
+
+ if (p->state != BR_STATE_FORWARDING ||
+ !(p->flags & BR_OFFLOAD))
+ enabled = false;
+
+ spin_lock_bh(&offload_lock);
+ if (o->enabled == enabled)
+ goto out;
+
+ if (enabled) {
+ if (!o->gc_work.func)
+ INIT_WORK(&o->gc_work, br_offload_gc_work);
+ rhashtable_init(&o->rht, &flow_params);
+ } else {
+ flush = true;
+ rhashtable_free_and_destroy(&o->rht, br_offload_destroy_cb, o);
+ }
+
+ o->enabled = enabled;
+
+out:
+ spin_unlock_bh(&offload_lock);
+
+ if (flush)
+ flush_work(&o->gc_work);
+}
+
+void br_offload_fdb_update(const struct net_bridge_fdb_entry *fdb)
+{
+ struct bridge_flow *f;
+ struct hlist_node *tmp;
+
+ spin_lock_bh(&offload_lock);
+
+ hlist_for_each_entry_safe(f, tmp, &fdb->offload_in, fdb_list_in)
+ br_offload_flow_free(f);
+
+ hlist_for_each_entry_safe(f, tmp, &fdb->offload_out, fdb_list_out)
+ br_offload_flow_free(f);
+
+ spin_unlock_bh(&offload_lock);
+}
+
+static void
+br_offload_prepare_key(struct net_bridge_port *p, struct bridge_flow_key *key,
+ struct sk_buff *skb)
+{
+ memset(key, 0, sizeof(*key));
+ memcpy(key, eth_hdr(skb), 2 * ETH_ALEN);
+#ifdef CONFIG_BRIDGE_VLAN_FILTERING
+ if (!br_opt_get(p->br, BROPT_VLAN_ENABLED))
+ return;
+
+ if (!skb_vlan_tag_present(skb) || skb->vlan_proto != p->br->vlan_proto)
+ return;
+
+ key->vlan_present = true;
+ key->vlan_tag = skb_vlan_tag_get_id(skb);
+#endif
+}
+
+void br_offload_output(struct sk_buff *skb)
+{
+ struct net_bridge_port_offload *o;
+ struct br_input_skb_cb *cb = (struct br_input_skb_cb *)skb->cb;
+ struct net_bridge_port *p, *inp;
+ struct net_device *dev;
+ struct net_bridge_fdb_entry *fdb_in, *fdb_out;
+ struct net_bridge_vlan_group *vg;
+ struct bridge_flow_key key;
+ struct bridge_flow *flow;
+ u16 vlan;
+
+ if (!cb->offload)
+ return;
+
+ rcu_read_lock();
+
+ p = br_port_get_rcu(skb->dev);
+ if (!p)
+ goto out;
+
+ o = &p->offload;
+ if (!o->enabled)
+ goto out;
+
+ if (atomic_read(&p->offload.rht.nelems) >= p->br->offload_cache_size)
+ goto out;
+
+ dev = dev_get_by_index_rcu(dev_net(p->br->dev), cb->input_ifindex);
+ if (!dev)
+ goto out;
+
+ inp = br_port_get_rcu(dev);
+ if (!inp)
+ goto out;
+
+ vg = nbp_vlan_group_rcu(inp);
+ vlan = cb->input_vlan_present ? cb->input_vlan_tag : br_get_pvid(vg);
+ fdb_in = br_fdb_find_rcu(p->br, eth_hdr(skb)->h_source, vlan);
+ if (!fdb_in)
+ goto out;
+
+ vg = nbp_vlan_group_rcu(p);
+ vlan = skb_vlan_tag_present(skb) ? skb_vlan_tag_get_id(skb) : br_get_pvid(vg);
+ fdb_out = br_fdb_find_rcu(p->br, eth_hdr(skb)->h_dest, vlan);
+ if (!fdb_out)
+ goto out;
+
+ br_offload_prepare_key(p, &key, skb);
+#ifdef CONFIG_BRIDGE_VLAN_FILTERING
+ key.vlan_present = cb->input_vlan_present;
+ key.vlan_tag = cb->input_vlan_tag;
+#endif
+
+ flow = kmem_cache_alloc(offload_cache, GFP_ATOMIC);
+ flow->port = fdb_in->dst;
+ memcpy(&flow->key, &key, sizeof(key));
+
+#ifdef CONFIG_BRIDGE_VLAN_FILTERING
+ flow->vlan_out_present = skb_vlan_tag_present(skb);
+ flow->vlan_out = skb_vlan_tag_get(skb);
+#endif
+
+ flow->fdb_in = fdb_in;
+ flow->fdb_out = fdb_out;
+ flow->used = jiffies;
+
+ spin_lock_bh(&offload_lock);
+ if (!o->enabled ||
+ atomic_read(&p->offload.rht.nelems) >= p->br->offload_cache_size ||
+ rhashtable_insert_fast(&flow->port->offload.rht, &flow->node, flow_params)) {
+ kmem_cache_free(offload_cache, flow);
+ goto out_unlock;
+ }
+
+ hlist_add_head(&flow->fdb_list_in, &fdb_in->offload_in);
+ hlist_add_head(&flow->fdb_list_out, &fdb_out->offload_out);
+
+ if (br_offload_need_gc(p))
+ queue_work(system_long_wq, &p->offload.gc_work);
+
+out_unlock:
+ spin_unlock_bh(&offload_lock);
+
+out:
+ rcu_read_unlock();
+}
+
+bool br_offload_input(struct net_bridge_port *p, struct sk_buff *skb)
+{
+ struct net_bridge_port_offload *o = &p->offload;
+ struct br_input_skb_cb *cb = (struct br_input_skb_cb *)skb->cb;
+ struct bridge_flow_key key;
+ struct net_bridge_port *dst;
+ struct bridge_flow *flow;
+ unsigned long now = jiffies;
+ bool ret = false;
+
+ if (skb->len < sizeof(key))
+ return false;
+
+ if (!o->enabled)
+ return false;
+
+ if (is_multicast_ether_addr(eth_hdr(skb)->h_dest))
+ return false;
+
+ br_offload_prepare_key(p, &key, skb);
+
+ rcu_read_lock();
+ flow = rhashtable_lookup(&o->rht, &key, flow_params);
+ if (!flow) {
+ cb->offload = 1;
+#ifdef CONFIG_BRIDGE_VLAN_FILTERING
+ cb->input_vlan_present = key.vlan_present != 0;
+ cb->input_vlan_tag = key.vlan_tag;
+ cb->input_ifindex = p->dev->ifindex;
+#endif
+ goto out;
+ }
+
+ if (flow->fdb_in->dst != p)
+ goto out;
+
+ dst = flow->fdb_out->dst;
+ if (!dst)
+ goto out;
+
+ ret = true;
+#ifdef CONFIG_BRIDGE_VLAN_FILTERING
+ if (!flow->vlan_out_present && key.vlan_present) {
+ __vlan_hwaccel_clear_tag(skb);
+ } else if (flow->vlan_out_present) {
+ if (skb_vlan_tag_present(skb) &&
+ skb->vlan_proto != p->br->vlan_proto) {
+ /* Protocol-mismatch, empty out vlan_tci for new tag */
+ skb_push(skb, ETH_HLEN);
+ skb = vlan_insert_tag_set_proto(skb, skb->vlan_proto,
+ skb_vlan_tag_get(skb));
+ if (unlikely(!skb))
+ goto out;
+
+ skb_pull(skb, ETH_HLEN);
+ skb_reset_mac_len(skb);
+ }
+
+ __vlan_hwaccel_put_tag(skb, p->br->vlan_proto,
+ flow->vlan_out);
+ }
+#endif
+
+ skb->dev = dst->dev;
+ skb_push(skb, ETH_HLEN);
+
+ if (skb_warn_if_lro(skb) || !is_skb_forwardable(skb->dev, skb)) {
+ kfree_skb(skb);
+ goto out;
+ }
+
+ if (now - flow->used >= HZ) {
+ flow->used = now;
+ br_offload_flow_refresh_time(flow);
+ }
+
+ skb_forward_csum(skb);
+ dev_queue_xmit(skb);
+
+out:
+ rcu_read_unlock();
+ return ret;
+}
+
+static void
+br_offload_check_gc(struct net_bridge *br)
+{
+ struct net_bridge_port *p;
+
+ spin_lock_bh(&br->lock);
+ list_for_each_entry(p, &br->port_list, list)
+ if (br_offload_need_gc(p))
+ queue_work(system_long_wq, &p->offload.gc_work);
+ spin_unlock_bh(&br->lock);
+}
+
+
+int br_offload_set_cache_size(struct net_bridge *br, unsigned long val,
+ struct netlink_ext_ack *extack)
+{
+ br->offload_cache_size = val;
+ br_offload_check_gc(br);
+
+ return 0;
+}
+
+int br_offload_set_cache_reserved(struct net_bridge *br, unsigned long val,
+ struct netlink_ext_ack *extack)
+{
+ br->offload_cache_reserved = val;
+ br_offload_check_gc(br);
+
+ return 0;
+}
+
+int __init br_offload_init(void)
+{
+ offload_cache = kmem_cache_create("bridge_offload_cache",
+ sizeof(struct bridge_flow),
+ 0, SLAB_HWCACHE_ALIGN, NULL);
+ if (!offload_cache)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void br_offload_fini(void)
+{
+ kmem_cache_destroy(offload_cache);
+}
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -268,7 +268,13 @@ struct net_bridge_fdb_entry {
unsigned long updated ____cacheline_aligned_in_smp;
unsigned long used;
- struct rcu_head rcu;
+ union {
+ struct {
+ struct hlist_head offload_in;
+ struct hlist_head offload_out;
+ };
+ struct rcu_head rcu;
+ };
};
#define MDB_PG_FLAGS_PERMANENT BIT(0)
@@ -343,6 +349,12 @@ struct net_bridge_mdb_entry {
struct rcu_head rcu;
};
+struct net_bridge_port_offload {
+ struct rhashtable rht;
+ struct work_struct gc_work;
+ bool enabled;
+};
+
struct net_bridge_port {
struct net_bridge *br;
struct net_device *dev;
@@ -403,6 +415,7 @@ struct net_bridge_port {
u16 backup_redirected_cnt;
struct bridge_stp_xstats stp_xstats;
+ struct net_bridge_port_offload offload;
};
#define kobj_to_brport(obj) container_of(obj, struct net_bridge_port, kobj)
@@ -519,6 +532,9 @@ struct net_bridge {
struct kobject *ifobj;
u32 auto_cnt;
+ u32 offload_cache_size;
+ u32 offload_cache_reserved;
+
#ifdef CONFIG_NET_SWITCHDEV
/* Counter used to make sure that hardware domains get unique
* identifiers in case a bridge spans multiple switchdev instances.
@@ -553,6 +569,10 @@ struct br_input_skb_cb {
#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
u8 br_netfilter_broute:1;
#endif
+ u8 offload:1;
+ u8 input_vlan_present:1;
+ u16 input_vlan_tag;
+ int input_ifindex;
#ifdef CONFIG_NET_SWITCHDEV
/* Set if TX data plane offloading is used towards at least one
--- /dev/null
+++ b/net/bridge/br_private_offload.h
@@ -0,0 +1,23 @@
+#ifndef __BR_OFFLOAD_H
+#define __BR_OFFLOAD_H
+
+bool br_offload_input(struct net_bridge_port *p, struct sk_buff *skb);
+void br_offload_output(struct sk_buff *skb);
+void br_offload_port_state(struct net_bridge_port *p);
+void br_offload_fdb_update(const struct net_bridge_fdb_entry *fdb);
+int br_offload_init(void);
+void br_offload_fini(void);
+int br_offload_set_cache_size(struct net_bridge *br, unsigned long val,
+ struct netlink_ext_ack *extack);
+int br_offload_set_cache_reserved(struct net_bridge *br, unsigned long val,
+ struct netlink_ext_ack *extack);
+
+static inline void br_offload_skb_disable(struct sk_buff *skb)
+{
+ struct br_input_skb_cb *cb = (struct br_input_skb_cb *)skb->cb;
+
+ if (cb->offload)
+ cb->offload = 0;
+}
+
+#endif
--- a/net/bridge/br_stp.c
+++ b/net/bridge/br_stp.c
@@ -12,6 +12,7 @@
#include "br_private.h"
#include "br_private_stp.h"
+#include "br_private_offload.h"
/* since time values in bpdu are in jiffies and then scaled (1/256)
* before sending, make sure that is at least one STP tick.
@@ -52,6 +53,8 @@ void br_set_state(struct net_bridge_port
(unsigned int) p->port_no, p->dev->name,
br_port_state_names[p->state]);
+ br_offload_port_state(p);
+
if (p->br->stp_enabled == BR_KERNEL_STP) {
switch (p->state) {
case BR_STATE_BLOCKING:
--- a/net/bridge/br_sysfs_br.c
+++ b/net/bridge/br_sysfs_br.c
@@ -18,6 +18,7 @@
#include <linux/sched/signal.h>
#include "br_private.h"
+#include "br_private_offload.h"
/* IMPORTANT: new bridge options must be added with netlink support only
* please do not add new sysfs entries
@@ -930,6 +931,38 @@ static ssize_t vlan_stats_per_port_store
static DEVICE_ATTR_RW(vlan_stats_per_port);
#endif
+static ssize_t offload_cache_size_show(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct net_bridge *br = to_bridge(d);
+ return sprintf(buf, "%u\n", br->offload_cache_size);
+}
+
+static ssize_t offload_cache_size_store(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ return store_bridge_parm(d, buf, len, br_offload_set_cache_size);
+}
+static DEVICE_ATTR_RW(offload_cache_size);
+
+static ssize_t offload_cache_reserved_show(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct net_bridge *br = to_bridge(d);
+ return sprintf(buf, "%u\n", br->offload_cache_reserved);
+}
+
+static ssize_t offload_cache_reserved_store(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ return store_bridge_parm(d, buf, len, br_offload_set_cache_reserved);
+}
+static DEVICE_ATTR_RW(offload_cache_reserved);
+
static struct attribute *bridge_attrs[] = {
&dev_attr_forward_delay.attr,
&dev_attr_hello_time.attr,
@@ -984,6 +1017,8 @@ static struct attribute *bridge_attrs[]
&dev_attr_vlan_stats_enabled.attr,
&dev_attr_vlan_stats_per_port.attr,
#endif
+ &dev_attr_offload_cache_size.attr,
+ &dev_attr_offload_cache_reserved.attr,
NULL
};
--- a/net/bridge/br_sysfs_if.c
+++ b/net/bridge/br_sysfs_if.c
@@ -241,6 +241,7 @@ BRPORT_ATTR_FLAG(broadcast_flood, BR_BCA
BRPORT_ATTR_FLAG(neigh_suppress, BR_NEIGH_SUPPRESS);
BRPORT_ATTR_FLAG(isolated, BR_ISOLATED);
BRPORT_ATTR_FLAG(bpdu_filter, BR_BPDU_FILTER);
+BRPORT_ATTR_FLAG(offload, BR_OFFLOAD);
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf)
@@ -295,6 +296,7 @@ static const struct brport_attribute *br
&brport_attr_isolated,
&brport_attr_bpdu_filter,
&brport_attr_backup_port,
+ &brport_attr_offload,
NULL
};
--- a/net/bridge/br_vlan_tunnel.c
+++ b/net/bridge/br_vlan_tunnel.c
@@ -15,6 +15,7 @@
#include "br_private.h"
#include "br_private_tunnel.h"
+#include "br_private_offload.h"
static inline int br_vlan_tunid_cmp(struct rhashtable_compare_arg *arg,
const void *ptr)
@@ -180,6 +181,7 @@ void br_handle_ingress_vlan_tunnel(struc
skb_dst_drop(skb);
__vlan_hwaccel_put_tag(skb, p->br->vlan_proto, vlan->vid);
+ br_offload_skb_disable(skb);
}
int br_handle_egress_vlan_tunnel(struct sk_buff *skb,
@@ -201,6 +203,7 @@ int br_handle_egress_vlan_tunnel(struct
if (err)
return err;
+ br_offload_skb_disable(skb);
tunnel_dst = rcu_dereference(vlan->tinfo.tunnel_dst);
if (tunnel_dst && dst_hold_safe(&tunnel_dst->dst))
skb_dst_set(skb, &tunnel_dst->dst);

View File

@ -98,7 +98,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o
--- /dev/null
+++ b/net/netfilter/xt_FLOWOFFLOAD.c
@@ -0,0 +1,656 @@
@@ -0,0 +1,657 @@
+/*
+ * Copyright (C) 2018-2021 Felix Fietkau <nbd@nbd.name>
+ *
@ -575,16 +575,17 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
+ }
+
+ table = &flowtable[!!(info->flags & XT_FLOWOFFLOAD_HW)];
+
+ net = read_pnet(&table->ft.net);
+ if (!net)
+ write_pnet(&table->ft.net, xt_net(par));
+
+ if (flow_offload_add(&table->ft, flow) < 0)
+ goto err_flow_add;
+
+ xt_flowoffload_check_device(table, devs[0]);
+ xt_flowoffload_check_device(table, devs[1]);
+
+ net = read_pnet(&table->ft.net);
+ if (!net)
+ write_pnet(&table->ft.net, xt_net(par));
+
+ dst_release(route.tuple[dir].dst);
+ dst_release(route.tuple[!dir].dst);
+

View File

@ -0,0 +1,25 @@
From 804fbb3f2ec9283f7b778e057a68bfff440a0be6 Mon Sep 17 00:00:00 2001
From: Rui Salvaterra <rsalvaterra@gmail.com>
Date: Wed, 30 Mar 2022 22:51:55 +0100
Subject: [PATCH] kernel: ct: size the hashtable more adequately
To set the default size of the connection tracking hash table, a divider of
16384 becomes inadequate for a router handling lots of connections. Divide by
2048 instead, making the default size scale better with the available RAM.
Signed-off-by: Rui Salvaterra <rsalvaterra@gmail.com>
---
net/netfilter/nf_conntrack_core.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -2676,7 +2676,7 @@ int nf_conntrack_init_start(void)
if (!nf_conntrack_htable_size) {
nf_conntrack_htable_size
- = (((nr_pages << PAGE_SHIFT) / 16384)
+ = (((nr_pages << PAGE_SHIFT) / 2048)
/ sizeof(struct hlist_head));
if (BITS_PER_LONG >= 64 &&
nr_pages > (4 * (1024 * 1024 * 1024 / PAGE_SIZE)))

View File

@ -19,7 +19,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1648,6 +1648,10 @@ enum netdev_priv_flags {
@@ -1655,6 +1655,10 @@ enum netdev_priv_flags {
IFF_TX_SKB_NO_LINEAR = 1<<31,
};
@ -30,7 +30,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
#define IFF_802_1Q_VLAN IFF_802_1Q_VLAN
#define IFF_EBRIDGE IFF_EBRIDGE
#define IFF_BONDING IFF_BONDING
@@ -1680,6 +1684,7 @@ enum netdev_priv_flags {
@@ -1687,6 +1691,7 @@ enum netdev_priv_flags {
#define IFF_L3MDEV_RX_HANDLER IFF_L3MDEV_RX_HANDLER
#define IFF_LIVE_RENAME_OK IFF_LIVE_RENAME_OK
#define IFF_TX_SKB_NO_LINEAR IFF_TX_SKB_NO_LINEAR
@ -38,7 +38,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
/* Specifies the type of the struct net_device::ml_priv pointer */
enum netdev_ml_priv_type {
@@ -1981,6 +1986,7 @@ struct net_device {
@@ -1988,6 +1993,7 @@ struct net_device {
/* Read-mostly cache-line for fast-path access */
unsigned int flags;
unsigned int priv_flags;
@ -46,7 +46,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
const struct net_device_ops *netdev_ops;
int ifindex;
unsigned short gflags;
@@ -2041,6 +2047,11 @@ struct net_device {
@@ -2048,6 +2054,11 @@ struct net_device {
const struct tlsdev_ops *tlsdev_ops;
#endif
@ -58,7 +58,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
const struct header_ops *header_ops;
unsigned char operstate;
@@ -2115,6 +2126,10 @@ struct net_device {
@@ -2122,6 +2133,10 @@ struct net_device {
struct mctp_dev __rcu *mctp_ptr;
#endif
@ -116,7 +116,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
help
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -3578,6 +3578,11 @@ static int xmit_one(struct sk_buff *skb,
@@ -3582,6 +3582,11 @@ static int xmit_one(struct sk_buff *skb,
if (dev_nit_active(dev))
dev_queue_xmit_nit(skb, dev);

View File

@ -0,0 +1,154 @@
From 5f62951fba63a9f9cfff564209426bdea5fcc371 Mon Sep 17 00:00:00 2001
From: Alex Marginean <alexandru.marginean@nxp.com>
Date: Tue, 27 Aug 2019 15:16:56 +0300
Subject: [PATCH] drivers: net: phy: aquantia: enable AQR112 and AQR412
Adds support for AQR112 and AQR412 which is mostly based on existing code
with the addition of code configuring the protocol on system side.
This allows changing the system side protocol without having to deploy a
different firmware on the PHY.
Signed-off-by: Alex Marginean <alexandru.marginean@nxp.com>
---
drivers/net/phy/aquantia_main.c | 88 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 88 insertions(+)
--- a/drivers/net/phy/aquantia_main.c
+++ b/drivers/net/phy/aquantia_main.c
@@ -20,9 +20,11 @@
#define PHY_ID_AQR105 0x03a1b4a2
#define PHY_ID_AQR106 0x03a1b4d0
#define PHY_ID_AQR107 0x03a1b4e0
+#define PHY_ID_AQR112 0x03a1b662
#define PHY_ID_AQR113C 0x31c31c12
#define PHY_ID_AQCS109 0x03a1b5c2
#define PHY_ID_AQR405 0x03a1b4b0
+#define PHY_ID_AQR412 0x03a1b712
#define PHY_ID_AQR813 0x31c31cb2
#define MDIO_PHYXS_VEND_IF_STATUS 0xe812
@@ -124,6 +126,29 @@
#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2 BIT(1)
#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 BIT(0)
+/* registers in MDIO_MMD_VEND1 region */
+#define AQUANTIA_VND1_GLOBAL_SC 0x000
+#define AQUANTIA_VND1_GLOBAL_SC_LP BIT(0xb)
+
+/* global start rate, the protocol associated with this speed is used by default
+ * on SI.
+ */
+#define AQUANTIA_VND1_GSTART_RATE 0x31a
+#define AQUANTIA_VND1_GSTART_RATE_OFF 0
+#define AQUANTIA_VND1_GSTART_RATE_100M 1
+#define AQUANTIA_VND1_GSTART_RATE_1G 2
+#define AQUANTIA_VND1_GSTART_RATE_10G 3
+#define AQUANTIA_VND1_GSTART_RATE_2_5G 4
+#define AQUANTIA_VND1_GSTART_RATE_5G 5
+
+/* SYSCFG registers for 100M, 1G, 2.5G, 5G, 10G */
+#define AQUANTIA_VND1_GSYSCFG_BASE 0x31b
+#define AQUANTIA_VND1_GSYSCFG_100M 0
+#define AQUANTIA_VND1_GSYSCFG_1G 1
+#define AQUANTIA_VND1_GSYSCFG_2_5G 2
+#define AQUANTIA_VND1_GSYSCFG_5G 3
+#define AQUANTIA_VND1_GSYSCFG_10G 4
+
struct aqr107_hw_stat {
const char *name;
int reg;
@@ -244,6 +269,51 @@ static int aqr_config_aneg(struct phy_de
return genphy_c45_check_and_restart_aneg(phydev, changed);
}
+static struct {
+ u16 syscfg;
+ int cnt;
+ u16 start_rate;
+} aquantia_syscfg[PHY_INTERFACE_MODE_MAX] = {
+ [PHY_INTERFACE_MODE_SGMII] = {0x04b, AQUANTIA_VND1_GSYSCFG_1G,
+ AQUANTIA_VND1_GSTART_RATE_1G},
+ [PHY_INTERFACE_MODE_2500BASEX] = {0x144, AQUANTIA_VND1_GSYSCFG_2_5G,
+ AQUANTIA_VND1_GSTART_RATE_2_5G},
+ [PHY_INTERFACE_MODE_XGMII] = {0x100, AQUANTIA_VND1_GSYSCFG_10G,
+ AQUANTIA_VND1_GSTART_RATE_10G},
+ [PHY_INTERFACE_MODE_USXGMII] = {0x080, AQUANTIA_VND1_GSYSCFG_10G,
+ AQUANTIA_VND1_GSTART_RATE_10G},
+};
+
+/* Sets up protocol on system side before calling aqr_config_aneg */
+static int aqr_config_aneg_set_prot(struct phy_device *phydev)
+{
+ int if_type = phydev->interface;
+ int i;
+
+ if (!aquantia_syscfg[if_type].cnt)
+ return 0;
+
+ /* set PHY in low power mode so we can configure protocols */
+ phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GLOBAL_SC,
+ AQUANTIA_VND1_GLOBAL_SC_LP);
+ mdelay(10);
+
+ /* set the default rate to enable the SI link */
+ phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GSTART_RATE,
+ aquantia_syscfg[if_type].start_rate);
+
+ for (i = 0; i <= aquantia_syscfg[if_type].cnt; i++)
+ phy_write_mmd(phydev, MDIO_MMD_VEND1,
+ AQUANTIA_VND1_GSYSCFG_BASE + i,
+ aquantia_syscfg[if_type].syscfg);
+
+ /* wake PHY back up */
+ phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GLOBAL_SC, 0);
+ mdelay(10);
+
+ return aqr_config_aneg(phydev);
+}
+
static int aqr_config_intr(struct phy_device *phydev)
{
bool en = phydev->interrupts == PHY_INTERRUPT_ENABLED;
@@ -767,6 +837,30 @@ static struct phy_driver aqr_driver[] =
.get_stats = aqr107_get_stats,
.link_change_notify = aqr107_link_change_notify,
},
+{
+ PHY_ID_MATCH_MODEL(PHY_ID_AQR112),
+ .name = "Aquantia AQR112",
+ .probe = aqr107_probe,
+ .config_aneg = aqr_config_aneg_set_prot,
+ .config_intr = aqr_config_intr,
+ .handle_interrupt = aqr_handle_interrupt,
+ .read_status = aqr107_read_status,
+ .get_sset_count = aqr107_get_sset_count,
+ .get_strings = aqr107_get_strings,
+ .get_stats = aqr107_get_stats,
+},
+{
+ PHY_ID_MATCH_MODEL(PHY_ID_AQR412),
+ .name = "Aquantia AQR412",
+ .probe = aqr107_probe,
+ .config_aneg = aqr_config_aneg_set_prot,
+ .config_intr = aqr_config_intr,
+ .handle_interrupt = aqr_handle_interrupt,
+ .read_status = aqr107_read_status,
+ .get_sset_count = aqr107_get_sset_count,
+ .get_strings = aqr107_get_strings,
+ .get_stats = aqr107_get_stats,
+},
};
module_phy_driver(aqr_driver);
@@ -777,9 +871,11 @@ static struct mdio_device_id __maybe_unu
{ PHY_ID_MATCH_MODEL(PHY_ID_AQR105) },
{ PHY_ID_MATCH_MODEL(PHY_ID_AQR106) },
{ PHY_ID_MATCH_MODEL(PHY_ID_AQR107) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR112) },
{ PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) },
{ PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) },
{ PHY_ID_MATCH_MODEL(PHY_ID_AQR405) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR412) },
{ PHY_ID_MATCH_MODEL(PHY_ID_AQR813) },
{ }
};

View File

@ -0,0 +1,34 @@
From 5f008cb22f60da4e10375f22266c1a4e20b1252e Mon Sep 17 00:00:00 2001
From: Alex Marginean <alexandru.marginean@nxp.com>
Date: Fri, 20 Sep 2019 18:22:52 +0300
Subject: [PATCH] drivers: net: phy: aquantia: fix system side protocol
misconfiguration
Do not set up protocols for speeds that are not supported by FW. Enabling
these protocols leads to link issues on system side.
Signed-off-by: Alex Marginean <alexandru.marginean@nxp.com>
---
drivers/net/phy/aquantia_main.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
--- a/drivers/net/phy/aquantia_main.c
+++ b/drivers/net/phy/aquantia_main.c
@@ -302,10 +302,16 @@ static int aqr_config_aneg_set_prot(stru
phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GSTART_RATE,
aquantia_syscfg[if_type].start_rate);
- for (i = 0; i <= aquantia_syscfg[if_type].cnt; i++)
+ for (i = 0; i <= aquantia_syscfg[if_type].cnt; i++) {
+ u16 reg = phy_read_mmd(phydev, MDIO_MMD_VEND1,
+ AQUANTIA_VND1_GSYSCFG_BASE + i);
+ if (!reg)
+ continue;
+
phy_write_mmd(phydev, MDIO_MMD_VEND1,
AQUANTIA_VND1_GSYSCFG_BASE + i,
aquantia_syscfg[if_type].syscfg);
+ }
/* wake PHY back up */
phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GLOBAL_SC, 0);

View File

@ -0,0 +1,43 @@
From 2e677e4ae8f8330f68013163b060d0fda3a43095 Mon Sep 17 00:00:00 2001
From: "Langer, Thomas" <tlanger@maxlinear.com>
Date: Fri, 9 Jul 2021 17:36:46 +0200
Subject: [PATCH] PONRTSYS-8842: aquantia: Add AQR113 driver support
Add a new entry for AQR113 PHY_ID
---
drivers/net/phy/aquantia_main.c | 10 ++++++++++
1 file changed, 10 insertions(+)
--- a/drivers/net/phy/aquantia_main.c
+++ b/drivers/net/phy/aquantia_main.c
@@ -21,6 +21,7 @@
#define PHY_ID_AQR106 0x03a1b4d0
#define PHY_ID_AQR107 0x03a1b4e0
#define PHY_ID_AQR112 0x03a1b662
+#define PHY_ID_AQR113 0x31c31c40
#define PHY_ID_AQR113C 0x31c31c12
#define PHY_ID_AQCS109 0x03a1b5c2
#define PHY_ID_AQR405 0x03a1b4b0
@@ -856,6 +857,14 @@ static struct phy_driver aqr_driver[] =
.get_stats = aqr107_get_stats,
},
{
+ PHY_ID_MATCH_MODEL(PHY_ID_AQR113),
+ .name = "Aquantia AQR113",
+ .config_aneg = aqr_config_aneg,
+ .config_intr = aqr_config_intr,
+ .handle_interrupt = aqr_handle_interrupt,
+ .read_status = aqr107_read_status,
+},
+{
PHY_ID_MATCH_MODEL(PHY_ID_AQR412),
.name = "Aquantia AQR412",
.probe = aqr107_probe,
@@ -878,6 +887,7 @@ static struct mdio_device_id __maybe_unu
{ PHY_ID_MATCH_MODEL(PHY_ID_AQR106) },
{ PHY_ID_MATCH_MODEL(PHY_ID_AQR107) },
{ PHY_ID_MATCH_MODEL(PHY_ID_AQR112) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR113) },
{ PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) },
{ PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) },
{ PHY_ID_MATCH_MODEL(PHY_ID_AQR405) },

View File

@ -0,0 +1,63 @@
From 3b92ee7b7899b6beffb2b484c58326e36612a873 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Thu, 23 Dec 2021 14:52:56 +0000
Subject: [PATCH] net: phy: aquantia: add PHY_ID for AQR112R
As advised by Ian Chang this PHY is used in Puzzle devices.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/net/phy/aquantia_main.c | 10 ++++++++++
1 file changed, 10 insertions(+)
--- a/drivers/net/phy/aquantia_main.c
+++ b/drivers/net/phy/aquantia_main.c
@@ -21,6 +21,8 @@
#define PHY_ID_AQR106 0x03a1b4d0
#define PHY_ID_AQR107 0x03a1b4e0
#define PHY_ID_AQR112 0x03a1b662
+#define PHY_ID_AQR112C 0x03a1b790
+#define PHY_ID_AQR112R 0x31c31d12
#define PHY_ID_AQR113 0x31c31c40
#define PHY_ID_AQR113C 0x31c31c12
#define PHY_ID_AQCS109 0x03a1b5c2
@@ -857,6 +859,30 @@ static struct phy_driver aqr_driver[] =
.get_stats = aqr107_get_stats,
},
{
+ PHY_ID_MATCH_MODEL(PHY_ID_AQR112C),
+ .name = "Aquantia AQR112C",
+ .probe = aqr107_probe,
+ .config_aneg = aqr_config_aneg_set_prot,
+ .config_intr = aqr_config_intr,
+ .handle_interrupt = aqr_handle_interrupt,
+ .read_status = aqr107_read_status,
+ .get_sset_count = aqr107_get_sset_count,
+ .get_strings = aqr107_get_strings,
+ .get_stats = aqr107_get_stats,
+},
+{
+ PHY_ID_MATCH_MODEL(PHY_ID_AQR112R),
+ .name = "Aquantia AQR112R",
+ .probe = aqr107_probe,
+ .config_aneg = aqr_config_aneg_set_prot,
+ .config_intr = aqr_config_intr,
+ .handle_interrupt = aqr_handle_interrupt,
+ .read_status = aqr107_read_status,
+ .get_sset_count = aqr107_get_sset_count,
+ .get_strings = aqr107_get_strings,
+ .get_stats = aqr107_get_stats,
+},
+{
PHY_ID_MATCH_MODEL(PHY_ID_AQR113),
.name = "Aquantia AQR113",
.config_aneg = aqr_config_aneg,
@@ -887,6 +913,8 @@ static struct mdio_device_id __maybe_unu
{ PHY_ID_MATCH_MODEL(PHY_ID_AQR106) },
{ PHY_ID_MATCH_MODEL(PHY_ID_AQR107) },
{ PHY_ID_MATCH_MODEL(PHY_ID_AQR112) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR112C) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR112R) },
{ PHY_ID_MATCH_MODEL(PHY_ID_AQR113) },
{ PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) },
{ PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) },

View File

@ -0,0 +1,33 @@
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -1085,6 +1085,7 @@ static const struct usb_device_id produc
{QMI_MATCH_FF_FF_FF(0x2c7c, 0x0512)}, /* Quectel EG12/EM12 */
{QMI_MATCH_FF_FF_FF(0x2c7c, 0x0620)}, /* Quectel EM160R-GL */
{QMI_MATCH_FF_FF_FF(0x2c7c, 0x0800)}, /* Quectel RM500Q-GL */
+ {QMI_MATCH_FF_FF_FF(0x05c6, 0xf601)}, /* MeigLink SLM750 */
/* 3. Combined interface devices matching on interface number */
{QMI_FIXED_INTF(0x0408, 0xea42, 4)}, /* Yota / Megafon M100-1 */
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -243,6 +243,8 @@ static void option_instat_callback(struc
#define UBLOX_PRODUCT_R6XX 0x90fa
/* These Yuga products use Qualcomm's vendor ID */
#define YUGA_PRODUCT_CLM920_NC5 0x9625
+/* These MeigLink products use Qualcomm's vendor ID */
+#define MEIGLINK_PRODUCT_SLM750 0xf601
#define QUECTEL_VENDOR_ID 0x2c7c
/* These Quectel products use Quectel's vendor ID */
@@ -1127,6 +1129,11 @@ static const struct usb_device_id option
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG95, 0xff, 0, 0) },
{ USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_BG96),
.driver_info = RSVD(4) },
+ /* Meiglink products using Qualcomm vendor ID */
+ // Works OK. In case of some issues check macros that are used by Quectel Products
+ { USB_DEVICE_AND_INTERFACE_INFO(QUALCOMM_VENDOR_ID, MEIGLINK_PRODUCT_SLM750, 0xff, 0xff, 0xff),
+ .driver_info = NUMEP2 },
+ { USB_DEVICE_AND_INTERFACE_INFO(QUALCOMM_VENDOR_ID, MEIGLINK_PRODUCT_SLM750, 0xff, 0, 0) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0xff, 0xff),
.driver_info = RSVD(1) | RSVD(2) | RSVD(3) | RSVD(4) | NUMEP2 },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0, 0) },

View File

@ -0,0 +1,12 @@
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -1158,6 +1158,9 @@ int __init early_init_dt_scan_chosen(uns
p = of_get_flat_dt_prop(node, "bootargs", &l);
if (p != NULL && l > 0)
strlcpy(data, p, min(l, COMMAND_LINE_SIZE));
+ p = of_get_flat_dt_prop(node, "bootargs-append", &l);
+ if (p != NULL && l > 0)
+ strlcat(data, p, min_t(int, strlen(data) + (int)l, COMMAND_LINE_SIZE));
/*
* CONFIG_CMDLINE is meant to be a default in case nothing else

View File

@ -0,0 +1,389 @@
From patchwork Tue Jun 8 04:07:19 2021
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
X-Patchwork-Submitter: John Thomson <git@johnthomson.fastmail.com.au>
X-Patchwork-Id: 1489105
X-Patchwork-Delegate: tudor.ambarus@gmail.com
Return-Path:
<linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org>
X-Original-To: incoming@patchwork.ozlabs.org
Delivered-To: patchwork-incoming@bilbo.ozlabs.org
Authentication-Results: ozlabs.org;
spf=none (no SPF record) smtp.mailfrom=lists.infradead.org
(client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org;
envelope-from=linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org;
receiver=<UNKNOWN>)
Authentication-Results: ozlabs.org;
dkim=pass (2048-bit key;
secure) header.d=lists.infradead.org header.i=@lists.infradead.org
header.a=rsa-sha256 header.s=bombadil.20210309 header.b=EMabhVoR;
dkim=fail reason="signature verification failed" (2048-bit key;
unprotected) header.d=fastmail.com.au header.i=@fastmail.com.au
header.a=rsa-sha256 header.s=fm3 header.b=dLzuZ6dB;
dkim=fail reason="signature verification failed" (2048-bit key;
unprotected) header.d=messagingengine.com header.i=@messagingengine.com
header.a=rsa-sha256 header.s=fm3 header.b=nSRGsW+C;
dkim-atps=neutral
Received: from bombadil.infradead.org (bombadil.infradead.org
[IPv6:2607:7c80:54:e::133])
(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest
SHA256)
(No client certificate requested)
by ozlabs.org (Postfix) with ESMTPS id 4FzcFN1j1nz9sW8
for <incoming@patchwork.ozlabs.org>; Tue, 8 Jun 2021 14:09:28 +1000 (AEST)
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed;
d=lists.infradead.org; s=bombadil.20210309; h=Sender:
Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post:
List-Archive:List-Unsubscribe:List-Id:MIME-Version:Message-Id:Date:Subject:Cc
:To:From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From:
Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:
List-Owner; bh=6mUWQd71FwsINycGYY1qOhKz+ecWJVNtwDkTebG3XkA=; b=EMabhVoRE3ad89
o3L2AgyKrs+blSofUC3hoSsQe7gi3m4si8S9HW8Z+8SsS5TufUsvGwDl80qSYGlQOytQF+1yRUWvE
6FJ/+bqv+TwjqZFibgJ6+9OVsQN9dZ/no1R0bBXIpmrf8ORUmv58QK4ZQquaFKbyXKpFeWOC2MSv4
H2MAhyhTU8a3gtooH6G8+KvsJEfVgh6C+aDbwxyh2UY3chHKuw1kvL6AktbfUE2xl4zxi3x3kc70B
Wi3LiJBFokxVdgnROXxTU5tI0XboWYkQV64gLuQNV4XKClcuhVpzloDK8Iok6NTd7b32a7TdEFlCS
lGKsEKmxtUlW2FpfoduA==;
Received: from localhost ([::1] helo=bombadil.infradead.org)
by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux))
id 1lqT1r-006OAW-DX; Tue, 08 Jun 2021 04:07:51 +0000
Received: from new1-smtp.messagingengine.com ([66.111.4.221])
by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux))
id 1lqT1l-006O9b-Fq
for linux-mtd@lists.infradead.org; Tue, 08 Jun 2021 04:07:50 +0000
Received: from compute2.internal (compute2.nyi.internal [10.202.2.42])
by mailnew.nyi.internal (Postfix) with ESMTP id 4456B580622;
Tue, 8 Jun 2021 00:07:42 -0400 (EDT)
Received: from mailfrontend2 ([10.202.2.163])
by compute2.internal (MEProxy); Tue, 08 Jun 2021 00:07:42 -0400
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=fastmail.com.au;
h=from:to:cc:subject:date:message-id:mime-version
:content-transfer-encoding; s=fm3; bh=ZXRH+YluM1mHCS1EWUiCY/Sg8O
LccfHe1oW5iAay6y8=; b=dLzuZ6dBYf7ZA8tWLOBFZYLi7ERsGe/4vnMXG+ovvb
dNBO0+SaFGwoqYSFrfq/TeyHfKyvxrA7+LCdopIuT4abpLHxtRwtRiafQcDYCPat
qJIqOZO+wCZC5S9Jc1OP7+t1FviGpgevqIMotci37P+RWc5u3AweMzFljZk90E8C
uorV6rXagD+OssJQzllRnAIK88+rOAC9ZyXv2gWxy4d1HSCwSWgzx2vnV9CNp918
YC/3tiHas9krbrPIaAsdBROr7Bvoe/ShRRzruKRuvZVgg5NN90vX+/5ZjI8u04GM
p2bWCbC62CP6wlcgDaz+c/Sgr5ITd2GPENJsHfqmLRBA==
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=
messagingengine.com; h=cc:content-transfer-encoding:date:from
:message-id:mime-version:subject:to:x-me-proxy:x-me-proxy
:x-me-sender:x-me-sender:x-sasl-enc; s=fm3; bh=ZXRH+YluM1mHCS1EW
UiCY/Sg8OLccfHe1oW5iAay6y8=; b=nSRGsW+CQ2Zx1RVpIUu8W/VD/k5P+32BW
5k2ltd+UhI3dfldBPzHrYiOP/IJqGkNW+V+rHASacW/vFygnaZoxNjRYKnOsu+26
wb2yK3jpl6lsNTg3N1Z4XJrYY2lf9H29DMFbhC67l0PTc050rcZk4XsKTLAlv14Q
VA4WREYSaX/4IN4O+ES4TMq0a/3gKZh6nvbbJXbsXfK0WlSHTGZtZmW3fyrqvbXa
t+R7L8vvqWvwls0pV+Sn8LeQqb7+A69w0UOnuznjkcA3sCc2YehcHbxcUEnMH+9N
bxOjmIDeg9/4X/829tUWUJiLhE5SFmQZ1P6oFtmbWoLrDz0ZJIVBw==
X-ME-Sender: <xms:C-2-YD2uka4HsA6gcdsV2Ia7vebY4Yjp9E8q7KBMb54jnAzGL7-67Q>
<xme:C-2-YCEaxASy5VlcrvNO_jLFpMDGkFCRsuVNuZGEQsiRZygk8jPHWq7unPjeT6uYS
2pUP6PrTQ2rggjEIg>
X-ME-Received:
<xmr:C-2-YD4exeK49N_YZWWf2BWDhVyCbCY3wwvjTyDOFxeugx7Jg08pzMUToo9oJjrBpcVTaA3kbfk>
X-ME-Proxy-Cause:
gggruggvucftvghtrhhoucdtuddrgeduledrfedtkedgjeduucetufdoteggodetrfdotf
fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen
uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne
cujfgurhephffvufffkffoggfgsedtkeertdertddtnecuhfhrohhmpeflohhhnhcuvfhh
ohhmshhonhcuoehgihhtsehjohhhnhhthhhomhhsohhnrdhfrghsthhmrghilhdrtghomh
drrghuqeenucggtffrrghtthgvrhhnpefffeeihfdukedtuedufeetieeuudfhhefhkefh
tefgtdeuffekffelleetveduieenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmh
epmhgrihhlfhhrohhmpehgihhtsehjohhhnhhthhhomhhsohhnrdhfrghsthhmrghilhdr
tghomhdrrghu
X-ME-Proxy: <xmx:C-2-YI0AJZGjcB3wIbI9BoC9X8VNl4i9A7cQnBkvwZ25czWJlkKCLw>
<xmx:C-2-YGGufw99T-O81-FeiSyEruv6_Pr0IHFhspQdxjv5k1VFTZ0lzQ>
<xmx:C-2-YJ8BW7DhSDSCEAPSJWrwh_hHP79qreTZtWh_kOUwSh1c0MMlAg>
<xmx:Du2-YJBeX2Fg9oFZVXGwEJ1ZrZnXHiAqNON8tbpzquYgcm2o_LM48g>
Received: by mail.messagingengine.com (Postfix) with ESMTPA; Tue,
8 Jun 2021 00:07:35 -0400 (EDT)
From: John Thomson <git@johnthomson.fastmail.com.au>
To: Miquel Raynal <miquel.raynal@bootlin.com>,
Richard Weinberger <richard@nod.at>, Vignesh Raghavendra <vigneshr@ti.com>,
Tudor Ambarus <tudor.ambarus@microchip.com>,
Michael Walle <michael@walle.cc>, Pratyush Yadav <p.yadav@ti.com>,
linux-mtd@lists.infradead.org
Cc: linux-kernel@vger.kernel.org,
John Thomson <git@johnthomson.fastmail.com.au>,
kernel test robot <lkp@intel.com>, Dan Carpenter <dan.carpenter@oracle.com>
Subject: [PATCH] mtd: spi-nor: write support for minor aligned partitions
Date: Tue, 8 Jun 2021 14:07:19 +1000
Message-Id: <20210608040719.14431-1-git@johnthomson.fastmail.com.au>
X-Mailer: git-send-email 2.31.1
MIME-Version: 1.0
X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3
X-CRM114-CacheID: sfid-20210607_210745_712053_67A7D864
X-CRM114-Status: GOOD ( 26.99 )
X-Spam-Score: -0.8 (/)
X-Spam-Report: Spam detection software,
running on the system "bombadil.infradead.org",
has NOT identified this incoming email as spam. The original
message has been attached to this so you can view it or label
similar future email. If you have any questions, see
the administrator of that system for details.
Content preview: Do not prevent writing to mtd partitions where a partition
boundary sits on a minor erasesize boundary. This addresses a FIXME that
has been present since the start of the linux git history: /* Doesn' [...]
Content analysis details: (-0.8 points, 5.0 required)
pts rule name description
---- ----------------------
--------------------------------------------------
-0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at https://www.dnswl.org/,
low trust [66.111.4.221 listed in list.dnswl.org]
-0.0 SPF_PASS SPF: sender matches SPF record
-0.0 SPF_HELO_PASS SPF: HELO matches SPF record
0.0 RCVD_IN_MSPIKE_H3 RBL: Good reputation (+3)
[66.111.4.221 listed in wl.mailspike.net]
-0.1 DKIM_VALID Message has at least one valid DKIM or DK signature
0.1 DKIM_SIGNED Message has a DKIM or DK signature,
not necessarily
valid
-0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from
envelope-from domain
0.0 RCVD_IN_MSPIKE_WL Mailspike good senders
X-BeenThere: linux-mtd@lists.infradead.org
X-Mailman-Version: 2.1.34
Precedence: list
List-Id: Linux MTD discussion mailing list <linux-mtd.lists.infradead.org>
List-Unsubscribe: <http://lists.infradead.org/mailman/options/linux-mtd>,
<mailto:linux-mtd-request@lists.infradead.org?subject=unsubscribe>
List-Archive: <http://lists.infradead.org/pipermail/linux-mtd/>
List-Post: <mailto:linux-mtd@lists.infradead.org>
List-Help: <mailto:linux-mtd-request@lists.infradead.org?subject=help>
List-Subscribe: <http://lists.infradead.org/mailman/listinfo/linux-mtd>,
<mailto:linux-mtd-request@lists.infradead.org?subject=subscribe>
Sender: "linux-mtd" <linux-mtd-bounces@lists.infradead.org>
Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org
Do not prevent writing to mtd partitions where a partition boundary sits
on a minor erasesize boundary.
This addresses a FIXME that has been present since the start of the
linux git history:
/* Doesn't start on a boundary of major erase size */
/* FIXME: Let it be writable if it is on a boundary of
* _minor_ erase size though */
Allow a uniform erase region spi-nor device to be configured
to use the non-uniform erase regions code path for an erase with:
CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE=y
On supporting hardware (SECT_4K: majority of current SPI-NOR device)
provide the facility for an erase to use the least number
of SPI-NOR operations, as well as access to 4K erase without
requiring CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
Introduce erasesize_minor to the mtd struct,
the smallest erasesize supported by the device
On existing devices, this is useful where write support is wanted
for data on a 4K partition, such as some u-boot-env partitions,
or RouterBoot soft_config, while still netting the performance
benefits of using 64K sectors
Performance:
time mtd erase firmware
OpenWrt 5.10 ramips MT7621 w25q128jv 0xfc0000 partition length
Without this patch
MTD_SPI_NOR_USE_4K_SECTORS=y |n
real 2m 11.66s |0m 50.86s
user 0m 0.00s |0m 0.00s
sys 1m 56.20s |0m 50.80s
With this patch
MTD_SPI_NOR_USE_VARIABLE_ERASE=n|y |4K_SECTORS=y
real 0m 51.68s |0m 50.85s |2m 12.89s
user 0m 0.00s |0m 0.00s |0m 0.01s
sys 0m 46.94s |0m 50.38s |2m 12.46s
Signed-off-by: John Thomson <git@johnthomson.fastmail.com.au>
---
Have not tested on variable erase regions device.
checkpatch does not like the printk(KERN_WARNING
these should be changed separately beforehand?
Changes RFC -> v1:
Fix uninitialized variable smatch warning
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
---
drivers/mtd/mtdpart.c | 52 ++++++++++++++++++++++++++++---------
drivers/mtd/spi-nor/Kconfig | 10 +++++++
drivers/mtd/spi-nor/core.c | 10 +++++--
include/linux/mtd/mtd.h | 2 ++
4 files changed, 60 insertions(+), 14 deletions(-)
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -40,10 +40,11 @@ static struct mtd_info *allocate_partiti
struct mtd_info *master = mtd_get_master(parent);
int wr_alignment = (parent->flags & MTD_NO_ERASE) ?
master->writesize : master->erasesize;
+ int wr_alignment_minor = 0;
u64 parent_size = mtd_is_partition(parent) ?
parent->part.size : parent->size;
struct mtd_info *child;
- u32 remainder;
+ u32 remainder, remainder_minor;
char *name;
u64 tmp;
@@ -145,6 +146,7 @@ static struct mtd_info *allocate_partiti
int i, max = parent->numeraseregions;
u64 end = child->part.offset + child->part.size;
struct mtd_erase_region_info *regions = parent->eraseregions;
+ uint32_t erasesize_minor = child->erasesize;
/* Find the first erase regions which is part of this
* partition. */
@@ -155,15 +157,24 @@ static struct mtd_info *allocate_partiti
if (i > 0)
i--;
- /* Pick biggest erasesize */
for (; i < max && regions[i].offset < end; i++) {
+ /* Pick biggest erasesize */
if (child->erasesize < regions[i].erasesize)
child->erasesize = regions[i].erasesize;
+ /* Pick smallest non-zero erasesize */
+ if ((erasesize_minor > regions[i].erasesize) && (regions[i].erasesize > 0))
+ erasesize_minor = regions[i].erasesize;
}
+
+ if (erasesize_minor < child->erasesize)
+ child->erasesize_minor = erasesize_minor;
+
BUG_ON(child->erasesize == 0);
} else {
/* Single erase size */
child->erasesize = master->erasesize;
+ if (master->erasesize_minor)
+ child->erasesize_minor = master->erasesize_minor;
}
/*
@@ -171,26 +182,43 @@ static struct mtd_info *allocate_partiti
* exposes several regions with different erasesize. Adjust
* wr_alignment accordingly.
*/
- if (!(child->flags & MTD_NO_ERASE))
+ if (!(child->flags & MTD_NO_ERASE)) {
wr_alignment = child->erasesize;
+ if (IS_ENABLED(CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE) && child->erasesize_minor)
+ wr_alignment_minor = child->erasesize_minor;
+ }
tmp = mtd_get_master_ofs(child, 0);
remainder = do_div(tmp, wr_alignment);
if ((child->flags & MTD_WRITEABLE) && remainder) {
- /* Doesn't start on a boundary of major erase size */
- /* FIXME: Let it be writable if it is on a boundary of
- * _minor_ erase size though */
- child->flags &= ~MTD_WRITEABLE;
- printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n",
- part->name);
+ if (wr_alignment_minor) {
+ tmp = mtd_get_master_ofs(child, 0);
+ remainder_minor = do_div(tmp, wr_alignment_minor);
+ if (remainder_minor == 0)
+ child->erasesize = child->erasesize_minor;
+ }
+
+ if ((!wr_alignment_minor) || (wr_alignment_minor && remainder_minor != 0)) {
+ child->flags &= ~MTD_WRITEABLE;
+ printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n",
+ part->name);
+ }
}
tmp = mtd_get_master_ofs(child, 0) + child->part.size;
remainder = do_div(tmp, wr_alignment);
if ((child->flags & MTD_WRITEABLE) && remainder) {
- child->flags &= ~MTD_WRITEABLE;
- printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n",
- part->name);
+ if (wr_alignment_minor) {
+ tmp = mtd_get_master_ofs(child, 0) + child->part.size;
+ remainder_minor = do_div(tmp, wr_alignment_minor);
+ if (remainder_minor == 0)
+ child->erasesize = child->erasesize_minor;
+ }
+ if ((!wr_alignment_minor) || (wr_alignment_minor && remainder_minor != 0)) {
+ child->flags &= ~MTD_WRITEABLE;
+ printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n",
+ part->name);
+ }
}
child->size = child->part.size;
--- a/drivers/mtd/spi-nor/Kconfig
+++ b/drivers/mtd/spi-nor/Kconfig
@@ -10,6 +10,16 @@ menuconfig MTD_SPI_NOR
if MTD_SPI_NOR
+config MTD_SPI_NOR_USE_VARIABLE_ERASE
+ bool "Disable uniform_erase to allow use of all hardware supported erasesizes"
+ depends on !MTD_SPI_NOR_USE_4K_SECTORS
+ default n
+ help
+ Allow mixed use of all hardware supported erasesizes,
+ by forcing spi_nor to use the multiple eraseregions code path.
+ For example: A 68K erase will use one 64K erase, and one 4K erase
+ on supporting hardware.
+
config MTD_SPI_NOR_USE_4K_SECTORS
bool "Use small 4096 B erase sectors"
default y
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -1262,6 +1262,8 @@ static u8 spi_nor_convert_3to4_erase(u8
static bool spi_nor_has_uniform_erase(const struct spi_nor *nor)
{
+ if (IS_ENABLED(CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE))
+ return false;
return !!nor->params->erase_map.uniform_erase_type;
}
@@ -2379,6 +2381,7 @@ static int spi_nor_select_erase(struct s
{
struct spi_nor_erase_map *map = &nor->params->erase_map;
const struct spi_nor_erase_type *erase = NULL;
+ const struct spi_nor_erase_type *erase_minor = NULL;
struct mtd_info *mtd = &nor->mtd;
u32 wanted_size = nor->info->sector_size;
int i;
@@ -2411,8 +2414,9 @@ static int spi_nor_select_erase(struct s
*/
for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
if (map->erase_type[i].size) {
- erase = &map->erase_type[i];
- break;
+ if (!erase)
+ erase = &map->erase_type[i];
+ erase_minor = &map->erase_type[i];
}
}
@@ -2420,6 +2424,8 @@ static int spi_nor_select_erase(struct s
return -EINVAL;
mtd->erasesize = erase->size;
+ if (erase_minor && erase_minor->size < erase->size)
+ mtd->erasesize_minor = erase_minor->size;
return 0;
}
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -243,6 +243,8 @@ struct mtd_info {
* information below if they desire
*/
uint32_t erasesize;
+ /* "Minor" (smallest) erase size supported by the whole device */
+ uint32_t erasesize_minor;
/* Minimal writable flash unit size. In case of NOR flash it is 1 (even
* though individual bits can be cleared), in case of NAND flash it is
* one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR

View File

@ -19,7 +19,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
--- a/drivers/mtd/spi-nor/Kconfig
+++ b/drivers/mtd/spi-nor/Kconfig
@@ -68,6 +68,17 @@ config MTD_SPI_NOR_SWP_KEEP
@@ -78,6 +78,17 @@ config MTD_SPI_NOR_SWP_KEEP
endchoice
@ -39,7 +39,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
endif # MTD_SPI_NOR
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -2625,6 +2625,21 @@ static void spi_nor_info_init_params(str
@@ -2631,6 +2631,21 @@ static void spi_nor_info_init_params(str
*/
erase_mask = 0;
i = 0;
@ -61,7 +61,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
if (info->flags & SECT_4K_PMC) {
erase_mask |= BIT(i);
spi_nor_set_erase_type(&map->erase_type[i], 4096u,
@@ -2636,6 +2651,7 @@ static void spi_nor_info_init_params(str
@@ -2642,6 +2657,7 @@ static void spi_nor_info_init_params(str
SPINOR_OP_BE_4K);
i++;
}

View File

@ -59,7 +59,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
+};
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -1846,6 +1846,7 @@ static const struct spi_nor_manufacturer
@@ -1848,6 +1848,7 @@ static const struct spi_nor_manufacturer
&spi_nor_winbond,
&spi_nor_xilinx,
&spi_nor_xmc,

View File

@ -0,0 +1,11 @@
--- a/drivers/mtd/spi-nor/xmc.c
+++ b/drivers/mtd/spi-nor/xmc.c
@@ -14,6 +14,8 @@ static const struct flash_info xmc_parts
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ "XM25QH128A", INFO(0x207018, 0, 64 * 1024, 256,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "XM25QH128C", INFO(0x204018, 0, 64 * 1024, 256,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
};
const struct spi_nor_manufacturer spi_nor_xmc = {

View File

@ -8,12 +8,13 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
--- a/init/do_mounts.c
+++ b/init/do_mounts.c
@@ -447,7 +447,28 @@ retry:
@@ -447,7 +447,30 @@ retry:
out:
put_page(page);
}
-
+
+#ifdef CONFIG_MTD_ROOTFS_ROOT_DEV
+static int __init mount_ubi_rootfs(void)
+{
+ int flags = MS_SILENT;
@ -34,11 +35,12 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+
+ return -EINVAL;
+}
+#endif
+
#ifdef CONFIG_ROOT_NFS
#define NFSROOT_TIMEOUT_MIN 5
@@ -580,6 +601,10 @@ void __init mount_root(void)
@@ -580,6 +603,10 @@ void __init mount_root(void)
return;
}
#endif

View File

@ -64,7 +64,7 @@ Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
mutex_lock(&mtd_table_mutex);
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -703,6 +703,8 @@ extern struct mtd_info *get_mtd_device(s
@@ -705,6 +705,8 @@ extern struct mtd_info *get_mtd_device(s
extern int __get_mtd_device(struct mtd_info *mtd);
extern void __put_mtd_device(struct mtd_info *mtd);
extern struct mtd_info *get_mtd_device_nm(const char *name);

View File

@ -0,0 +1,34 @@
From 8bf2ce6ea4ee840b70f55a27f80e1cd308051b13 Mon Sep 17 00:00:00 2001
From: Nick Hainke <vincent@systemli.org>
Date: Mon, 27 Dec 2021 00:38:13 +0100
Subject: [PATCH 1/2] mtd: spi-nor: locking support for MX25L6405D
Macronix MX25L6405D supports locking with four block-protection bits.
Currently, the driver only sets three bits. If the bootloader does not
sustain the flash chip in an unlocked state, the flash might be
non-writeable. Add the corresponding flag to enable locking support with
four bits in the status register.
Tested on Nanostation M2 XM.
Similar to commit 7ea40b54e83b ("mtd: spi-nor: enable locking support for
MX25L12805D")
Signed-off-by: David Bauer <mail@david-bauer.net>
Signed-off-by: Nick Hainke <vincent@systemli.org>
---
drivers/mtd/spi-nor/macronix.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
--- a/drivers/mtd/spi-nor/macronix.c
+++ b/drivers/mtd/spi-nor/macronix.c
@@ -41,7 +41,8 @@ static const struct flash_info macronix_
{ "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K) },
{ "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, SECT_4K) },
{ "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64, SECT_4K) },
- { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, SECT_4K) },
+ { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, SECT_4K |
+ SPI_NOR_HAS_LOCK | SPI_NOR_4BIT_BP) },
{ "mx25u2033e", INFO(0xc22532, 0, 64 * 1024, 4, SECT_4K) },
{ "mx25u3235f", INFO(0xc22536, 0, 64 * 1024, 64,
SECT_4K | SPI_NOR_DUAL_READ |

View File

@ -0,0 +1,30 @@
From 245224608b5368c10407da07557e546743d3c489 Mon Sep 17 00:00:00 2001
From: Nick Hainke <vincent@systemli.org>
Date: Mon, 27 Dec 2021 09:33:13 +0100
Subject: [PATCH 2/2] mtd: spi-nor: disable 16-bit-sr for macronix
Macronix flash chips seem to consist of only one status register.
These chips will not work with the "16-bit Write Status (01h) Command".
Disable SNOR_F_HAS_16BIT_SR for all Macronix chips.
Tested with MX25L6405D.
Fixes: 39d1e3340c73 ("mtd: spi-nor: Fix clearing of QE bit on
lock()/unlock()")
Signed-off-by: David Bauer <mail@david-bauer.net>
Signed-off-by: Nick Hainke <vincent@systemli.org>
---
drivers/mtd/spi-nor/macronix.c | 1 +
1 file changed, 1 insertion(+)
--- a/drivers/mtd/spi-nor/macronix.c
+++ b/drivers/mtd/spi-nor/macronix.c
@@ -94,6 +94,7 @@ static void macronix_default_init(struct
{
nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable;
nor->params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode;
+ nor->flags &= ~SNOR_F_HAS_16BIT_SR;
nor->flags |= SNOR_F_HAS_LOCK;
}

View File

@ -37,23 +37,21 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
__NF_SYSCTL_CT_LAST_SYSCTL,
};
@@ -1026,6 +1027,15 @@ static struct ctl_table nf_ct_sysctl_tab
@@ -1026,6 +1027,13 @@ static struct ctl_table nf_ct_sysctl_tab
.proc_handler = nf_hooks_lwtunnel_sysctl_handler,
},
#endif
+ [NF_SYSCTL_CT_PROTO_TCP_NO_WINDOW_CHECK] = {
+ .procname = "nf_conntrack_tcp_no_window_check",
+ .data = &init_net.ct.sysctl_no_window_check,
+ .maxlen = sizeof(u8),
+ .maxlen = sizeof(unsigned int),
+ .mode = 0644,
+ .proc_handler = proc_dou8vec_minmax,
+ .extra1 = SYSCTL_ZERO,
+ .extra2 = SYSCTL_ONE,
+ .proc_handler = proc_dointvec,
+ },
{}
};
@@ -1153,6 +1163,7 @@ static int nf_conntrack_standalone_init_
@@ -1153,6 +1161,7 @@ static int nf_conntrack_standalone_init_
#ifdef CONFIG_NF_CONNTRACK_EVENTS
table[NF_SYSCTL_CT_EVENTS].data = &net->ct.sysctl_events;
#endif
@ -61,7 +59,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP
table[NF_SYSCTL_CT_TIMESTAMP].data = &net->ct.sysctl_tstamp;
#endif
@@ -1222,6 +1233,7 @@ static int nf_conntrack_pernet_init(stru
@@ -1222,6 +1231,7 @@ static int nf_conntrack_pernet_init(stru
int ret;
net->ct.sysctl_checksum = 1;

View File

@ -0,0 +1,89 @@
From 844c273286f328acf0dab5fbd5d864366b4904dc Mon Sep 17 00:00:00 2001
From: Ansuel Smith <ansuelsmth@gmail.com>
Date: Tue, 30 Mar 2021 18:21:14 +0200
Subject: [PATCH] of_net: add mac-address-increment support
Lots of embedded devices use the mac-address of other interface
extracted from nvmem cells and increments it by one or two. Add two
bindings to integrate this and directly use the right mac-address for
the interface. Some example are some routers that use the gmac
mac-address stored in the art partition and increments it by one for the
wifi. mac-address-increment-byte bindings is used to tell what byte of
the mac-address has to be increased (if not defined the last byte is
increased) and mac-address-increment tells how much the byte decided
early has to be increased.
Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
net/core/of_net.c | 43 +++++++++++++++++++++++++++++++++++++++----
1 file changed, 39 insertions(+), 4 deletions(-)
--- a/net/core/of_net.c
+++ b/net/core/of_net.c
@@ -119,27 +119,62 @@ static int of_get_mac_addr_nvmem(struct
* this case, the real MAC is in 'local-mac-address', and 'mac-address' exists
* but is all zeros.
*
+ * DT can tell the system to increment the mac-address after is extracted by
+ * using:
+ * - mac-address-increment-byte to decide what byte to increase
+ * (if not defined is increased the last byte)
+ * - mac-address-increment to decide how much to increase. The value WILL
+ * overflow to other bytes if the increment is over 255 or the total
+ * increment will exceed 255 of the current byte.
+ * (example 00:01:02:03:04:ff + 1 == 00:01:02:03:05:00)
+ * (example 00:01:02:03:04:fe + 5 == 00:01:02:03:05:03)
+ *
* Return: 0 on success and errno in case of error.
*/
int of_get_mac_address(struct device_node *np, u8 *addr)
{
+ u32 inc_idx, mac_inc, mac_val;
int ret;
+ /* Check first if the increment byte is present and valid.
+ * If not set assume to increment the last byte if found.
+ */
+ if (of_property_read_u32(np, "mac-address-increment-byte", &inc_idx))
+ inc_idx = 5;
+ if (inc_idx < 3 || inc_idx > 5)
+ return -EINVAL;
+
if (!np)
return -ENODEV;
ret = of_get_mac_addr(np, "mac-address", addr);
if (!ret)
- return 0;
+ goto found;
ret = of_get_mac_addr(np, "local-mac-address", addr);
if (!ret)
- return 0;
+ goto found;
ret = of_get_mac_addr(np, "address", addr);
if (!ret)
- return 0;
+ goto found;
+
+ ret = of_get_mac_addr_nvmem(np, addr);
+ if (ret)
+ return ret;
+
+found:
+ if (!of_property_read_u32(np, "mac-address-increment", &mac_inc)) {
+ /* Convert to a contiguous value */
+ mac_val = (addr[3] << 16) + (addr[4] << 8) + addr[5];
+ mac_val += mac_inc << 8 * (5-inc_idx);
+
+ /* Apply the incremented value handling overflow case */
+ addr[3] = (mac_val >> 16) & 0xff;
+ addr[4] = (mac_val >> 8) & 0xff;
+ addr[5] = (mac_val >> 0) & 0xff;
+ }
- return of_get_mac_addr_nvmem(np, addr);
+ return ret;
}
EXPORT_SYMBOL(of_get_mac_address);

View File

@ -0,0 +1,38 @@
--- a/net/core/of_net.c
+++ b/net/core/of_net.c
@@ -95,6 +95,27 @@ static int of_get_mac_addr_nvmem(struct
return 0;
}
+static int of_add_mac_address(struct device_node *np, u8* addr)
+{
+ struct property *prop;
+
+ prop = kzalloc(sizeof(*prop), GFP_KERNEL);
+ if (!prop)
+ return -ENOMEM;
+
+ prop->name = "mac-address";
+ prop->length = ETH_ALEN;
+ prop->value = kmemdup(addr, ETH_ALEN, GFP_KERNEL);
+ if (!prop->value || of_update_property(np, prop))
+ goto free;
+
+ return 0;
+free:
+ kfree(prop->value);
+ kfree(prop);
+ return -ENOMEM;
+}
+
/**
* of_get_mac_address()
* @np: Caller's Device Node
@@ -175,6 +196,7 @@ found:
addr[5] = (mac_val >> 0) & 0xff;
}
+ of_add_mac_address(np, addr);
return ret;
}
EXPORT_SYMBOL(of_get_mac_address);

View File

@ -0,0 +1,327 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Sat, 5 Feb 2022 17:59:07 +0100
Subject: [PATCH] net: ethernet: mtk_eth_soc: add support for coherent
DMA
It improves performance by eliminating the need for a cache flush on rx and tx
In preparation for supporting WED (Wireless Ethernet Dispatch), also add a
function for disabling coherent DMA at runtime.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -9,6 +9,7 @@
#include <linux/of_device.h>
#include <linux/of_mdio.h>
#include <linux/of_net.h>
+#include <linux/of_address.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/clk.h>
@@ -828,7 +829,7 @@ static int mtk_init_fq_dma(struct mtk_et
dma_addr_t dma_addr;
int i;
- eth->scratch_ring = dma_alloc_coherent(eth->dev,
+ eth->scratch_ring = dma_alloc_coherent(eth->dma_dev,
cnt * sizeof(struct mtk_tx_dma),
&eth->phy_scratch_ring,
GFP_ATOMIC);
@@ -840,10 +841,10 @@ static int mtk_init_fq_dma(struct mtk_et
if (unlikely(!eth->scratch_head))
return -ENOMEM;
- dma_addr = dma_map_single(eth->dev,
+ dma_addr = dma_map_single(eth->dma_dev,
eth->scratch_head, cnt * MTK_QDMA_PAGE_SIZE,
DMA_FROM_DEVICE);
- if (unlikely(dma_mapping_error(eth->dev, dma_addr)))
+ if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr)))
return -ENOMEM;
phy_ring_tail = eth->phy_scratch_ring +
@@ -897,26 +898,26 @@ static void mtk_tx_unmap(struct mtk_eth
{
if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
if (tx_buf->flags & MTK_TX_FLAGS_SINGLE0) {
- dma_unmap_single(eth->dev,
+ dma_unmap_single(eth->dma_dev,
dma_unmap_addr(tx_buf, dma_addr0),
dma_unmap_len(tx_buf, dma_len0),
DMA_TO_DEVICE);
} else if (tx_buf->flags & MTK_TX_FLAGS_PAGE0) {
- dma_unmap_page(eth->dev,
+ dma_unmap_page(eth->dma_dev,
dma_unmap_addr(tx_buf, dma_addr0),
dma_unmap_len(tx_buf, dma_len0),
DMA_TO_DEVICE);
}
} else {
if (dma_unmap_len(tx_buf, dma_len0)) {
- dma_unmap_page(eth->dev,
+ dma_unmap_page(eth->dma_dev,
dma_unmap_addr(tx_buf, dma_addr0),
dma_unmap_len(tx_buf, dma_len0),
DMA_TO_DEVICE);
}
if (dma_unmap_len(tx_buf, dma_len1)) {
- dma_unmap_page(eth->dev,
+ dma_unmap_page(eth->dma_dev,
dma_unmap_addr(tx_buf, dma_addr1),
dma_unmap_len(tx_buf, dma_len1),
DMA_TO_DEVICE);
@@ -994,9 +995,9 @@ static int mtk_tx_map(struct sk_buff *sk
if (skb_vlan_tag_present(skb))
txd4 |= TX_DMA_INS_VLAN | skb_vlan_tag_get(skb);
- mapped_addr = dma_map_single(eth->dev, skb->data,
+ mapped_addr = dma_map_single(eth->dma_dev, skb->data,
skb_headlen(skb), DMA_TO_DEVICE);
- if (unlikely(dma_mapping_error(eth->dev, mapped_addr)))
+ if (unlikely(dma_mapping_error(eth->dma_dev, mapped_addr)))
return -ENOMEM;
WRITE_ONCE(itxd->txd1, mapped_addr);
@@ -1035,10 +1036,10 @@ static int mtk_tx_map(struct sk_buff *sk
frag_map_size = min(frag_size, MTK_TX_DMA_BUF_LEN);
- mapped_addr = skb_frag_dma_map(eth->dev, frag, offset,
+ mapped_addr = skb_frag_dma_map(eth->dma_dev, frag, offset,
frag_map_size,
DMA_TO_DEVICE);
- if (unlikely(dma_mapping_error(eth->dev, mapped_addr)))
+ if (unlikely(dma_mapping_error(eth->dma_dev, mapped_addr)))
goto err_dma;
if (i == nr_frags - 1 &&
@@ -1316,18 +1317,18 @@ static int mtk_poll_rx(struct napi_struc
netdev->stats.rx_dropped++;
goto release_desc;
}
- dma_addr = dma_map_single(eth->dev,
+ dma_addr = dma_map_single(eth->dma_dev,
new_data + NET_SKB_PAD +
eth->ip_align,
ring->buf_size,
DMA_FROM_DEVICE);
- if (unlikely(dma_mapping_error(eth->dev, dma_addr))) {
+ if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr))) {
skb_free_frag(new_data);
netdev->stats.rx_dropped++;
goto release_desc;
}
- dma_unmap_single(eth->dev, trxd.rxd1,
+ dma_unmap_single(eth->dma_dev, trxd.rxd1,
ring->buf_size, DMA_FROM_DEVICE);
/* receive data */
@@ -1600,7 +1601,7 @@ static int mtk_tx_alloc(struct mtk_eth *
if (!ring->buf)
goto no_tx_mem;
- ring->dma = dma_alloc_coherent(eth->dev, MTK_DMA_SIZE * sz,
+ ring->dma = dma_alloc_coherent(eth->dma_dev, MTK_DMA_SIZE * sz,
&ring->phys, GFP_ATOMIC);
if (!ring->dma)
goto no_tx_mem;
@@ -1618,7 +1619,7 @@ static int mtk_tx_alloc(struct mtk_eth *
* descriptors in ring->dma_pdma.
*/
if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
- ring->dma_pdma = dma_alloc_coherent(eth->dev, MTK_DMA_SIZE * sz,
+ ring->dma_pdma = dma_alloc_coherent(eth->dma_dev, MTK_DMA_SIZE * sz,
&ring->phys_pdma,
GFP_ATOMIC);
if (!ring->dma_pdma)
@@ -1677,7 +1678,7 @@ static void mtk_tx_clean(struct mtk_eth
}
if (ring->dma) {
- dma_free_coherent(eth->dev,
+ dma_free_coherent(eth->dma_dev,
MTK_DMA_SIZE * sizeof(*ring->dma),
ring->dma,
ring->phys);
@@ -1685,7 +1686,7 @@ static void mtk_tx_clean(struct mtk_eth
}
if (ring->dma_pdma) {
- dma_free_coherent(eth->dev,
+ dma_free_coherent(eth->dma_dev,
MTK_DMA_SIZE * sizeof(*ring->dma_pdma),
ring->dma_pdma,
ring->phys_pdma);
@@ -1730,18 +1731,18 @@ static int mtk_rx_alloc(struct mtk_eth *
return -ENOMEM;
}
- ring->dma = dma_alloc_coherent(eth->dev,
+ ring->dma = dma_alloc_coherent(eth->dma_dev,
rx_dma_size * sizeof(*ring->dma),
&ring->phys, GFP_ATOMIC);
if (!ring->dma)
return -ENOMEM;
for (i = 0; i < rx_dma_size; i++) {
- dma_addr_t dma_addr = dma_map_single(eth->dev,
+ dma_addr_t dma_addr = dma_map_single(eth->dma_dev,
ring->data[i] + NET_SKB_PAD + eth->ip_align,
ring->buf_size,
DMA_FROM_DEVICE);
- if (unlikely(dma_mapping_error(eth->dev, dma_addr)))
+ if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr)))
return -ENOMEM;
ring->dma[i].rxd1 = (unsigned int)dma_addr;
@@ -1777,7 +1778,7 @@ static void mtk_rx_clean(struct mtk_eth
continue;
if (!ring->dma[i].rxd1)
continue;
- dma_unmap_single(eth->dev,
+ dma_unmap_single(eth->dma_dev,
ring->dma[i].rxd1,
ring->buf_size,
DMA_FROM_DEVICE);
@@ -1788,7 +1789,7 @@ static void mtk_rx_clean(struct mtk_eth
}
if (ring->dma) {
- dma_free_coherent(eth->dev,
+ dma_free_coherent(eth->dma_dev,
ring->dma_size * sizeof(*ring->dma),
ring->dma,
ring->phys);
@@ -2141,7 +2142,7 @@ static void mtk_dma_free(struct mtk_eth
if (eth->netdev[i])
netdev_reset_queue(eth->netdev[i]);
if (eth->scratch_ring) {
- dma_free_coherent(eth->dev,
+ dma_free_coherent(eth->dma_dev,
MTK_DMA_SIZE * sizeof(struct mtk_tx_dma),
eth->scratch_ring,
eth->phy_scratch_ring);
@@ -2491,6 +2492,8 @@ static void mtk_dim_tx(struct work_struc
static int mtk_hw_init(struct mtk_eth *eth)
{
+ u32 dma_mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA |
+ ETHSYS_DMA_AG_MAP_PPE;
int i, val, ret;
if (test_and_set_bit(MTK_HW_INIT, &eth->state))
@@ -2503,6 +2506,10 @@ static int mtk_hw_init(struct mtk_eth *e
if (ret)
goto err_disable_pm;
+ if (eth->ethsys)
+ regmap_update_bits(eth->ethsys, ETHSYS_DMA_AG_MAP, dma_mask,
+ of_dma_is_coherent(eth->dma_dev->of_node) * dma_mask);
+
if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) {
ret = device_reset(eth->dev);
if (ret) {
@@ -3056,6 +3063,35 @@ free_netdev:
return err;
}
+void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev)
+{
+ struct net_device *dev, *tmp;
+ LIST_HEAD(dev_list);
+ int i;
+
+ rtnl_lock();
+
+ for (i = 0; i < MTK_MAC_COUNT; i++) {
+ dev = eth->netdev[i];
+
+ if (!dev || !(dev->flags & IFF_UP))
+ continue;
+
+ list_add_tail(&dev->close_list, &dev_list);
+ }
+
+ dev_close_many(&dev_list, false);
+
+ eth->dma_dev = dma_dev;
+
+ list_for_each_entry_safe(dev, tmp, &dev_list, close_list) {
+ list_del_init(&dev->close_list);
+ dev_open(dev, NULL);
+ }
+
+ rtnl_unlock();
+}
+
static int mtk_probe(struct platform_device *pdev)
{
struct device_node *mac_np;
@@ -3069,6 +3105,7 @@ static int mtk_probe(struct platform_dev
eth->soc = of_device_get_match_data(&pdev->dev);
eth->dev = &pdev->dev;
+ eth->dma_dev = &pdev->dev;
eth->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(eth->base))
return PTR_ERR(eth->base);
@@ -3117,6 +3154,16 @@ static int mtk_probe(struct platform_dev
}
}
+ if (of_dma_is_coherent(pdev->dev.of_node)) {
+ struct regmap *cci;
+
+ cci = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "mediatek,cci-control");
+ /* enable CPU/bus coherency */
+ if (!IS_ERR(cci))
+ regmap_write(cci, 0, 3);
+ }
+
if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) {
eth->sgmii = devm_kzalloc(eth->dev, sizeof(*eth->sgmii),
GFP_KERNEL);
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -462,6 +462,12 @@
#define RSTCTRL_FE BIT(6)
#define RSTCTRL_PPE BIT(31)
+/* ethernet dma channel agent map */
+#define ETHSYS_DMA_AG_MAP 0x408
+#define ETHSYS_DMA_AG_MAP_PDMA BIT(0)
+#define ETHSYS_DMA_AG_MAP_QDMA BIT(1)
+#define ETHSYS_DMA_AG_MAP_PPE BIT(2)
+
/* SGMII subsystem config registers */
/* Register to auto-negotiation restart */
#define SGMSYS_PCS_CONTROL_1 0x0
@@ -879,6 +885,7 @@ struct mtk_sgmii {
/* struct mtk_eth - This is the main datasructure for holding the state
* of the driver
* @dev: The device pointer
+ * @dev: The device pointer used for dma mapping/alloc
* @base: The mapped register i/o base
* @page_lock: Make sure that register operations are atomic
* @tx_irq__lock: Make sure that IRQ register operations are atomic
@@ -922,6 +929,7 @@ struct mtk_sgmii {
struct mtk_eth {
struct device *dev;
+ struct device *dma_dev;
void __iomem *base;
spinlock_t page_lock;
spinlock_t tx_irq_lock;
@@ -1020,6 +1028,7 @@ int mtk_gmac_rgmii_path_setup(struct mtk
int mtk_eth_offload_init(struct mtk_eth *eth);
int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type,
void *type_data);
+void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev);
#endif /* MTK_ETH_H */

View File

@ -0,0 +1,30 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Mon, 7 Feb 2022 10:27:22 +0100
Subject: [PATCH] arm64: dts: mediatek: mt7622: add support for coherent
DMA
It improves performance by eliminating the need for a cache flush on rx and tx
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/arch/arm64/boot/dts/mediatek/mt7622.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt7622.dtsi
@@ -357,7 +357,7 @@
};
cci_control2: slave-if@5000 {
- compatible = "arm,cci-400-ctrl-if";
+ compatible = "arm,cci-400-ctrl-if", "syscon";
interface-type = "ace";
reg = <0x5000 0x1000>;
};
@@ -937,6 +937,8 @@
power-domains = <&scpsys MT7622_POWER_DOMAIN_ETHSYS>;
mediatek,ethsys = <&ethsys>;
mediatek,sgmiisys = <&sgmiisys>;
+ mediatek,cci-control = <&cci_control2>;
+ dma-coherent;
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";

View File

@ -0,0 +1,269 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Sat, 5 Feb 2022 18:29:22 +0100
Subject: [PATCH] net: ethernet: mtk_eth_soc: implement flow offloading
to WED devices
This allows hardware flow offloading from Ethernet to WLAN on MT7622 SoC
Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
@@ -329,6 +329,24 @@ int mtk_foe_entry_set_pppoe(struct mtk_f
return 0;
}
+int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq,
+ int bss, int wcid)
+{
+ struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry);
+ u32 *ib2 = mtk_foe_entry_ib2(entry);
+
+ *ib2 &= ~MTK_FOE_IB2_PORT_MG;
+ *ib2 |= MTK_FOE_IB2_WDMA_WINFO;
+ if (wdma_idx)
+ *ib2 |= MTK_FOE_IB2_WDMA_DEVIDX;
+
+ l2->vlan2 = FIELD_PREP(MTK_FOE_VLAN2_WINFO_BSS, bss) |
+ FIELD_PREP(MTK_FOE_VLAN2_WINFO_WCID, wcid) |
+ FIELD_PREP(MTK_FOE_VLAN2_WINFO_RING, txq);
+
+ return 0;
+}
+
static inline bool mtk_foe_entry_usable(struct mtk_foe_entry *entry)
{
return !(entry->ib1 & MTK_FOE_IB1_STATIC) &&
--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
@@ -48,9 +48,9 @@ enum {
#define MTK_FOE_IB2_DEST_PORT GENMASK(7, 5)
#define MTK_FOE_IB2_MULTICAST BIT(8)
-#define MTK_FOE_IB2_WHNAT_QID2 GENMASK(13, 12)
-#define MTK_FOE_IB2_WHNAT_DEVIDX BIT(16)
-#define MTK_FOE_IB2_WHNAT_NAT BIT(17)
+#define MTK_FOE_IB2_WDMA_QID2 GENMASK(13, 12)
+#define MTK_FOE_IB2_WDMA_DEVIDX BIT(16)
+#define MTK_FOE_IB2_WDMA_WINFO BIT(17)
#define MTK_FOE_IB2_PORT_MG GENMASK(17, 12)
@@ -58,9 +58,9 @@ enum {
#define MTK_FOE_IB2_DSCP GENMASK(31, 24)
-#define MTK_FOE_VLAN2_WHNAT_BSS GEMMASK(5, 0)
-#define MTK_FOE_VLAN2_WHNAT_WCID GENMASK(13, 6)
-#define MTK_FOE_VLAN2_WHNAT_RING GENMASK(15, 14)
+#define MTK_FOE_VLAN2_WINFO_BSS GENMASK(5, 0)
+#define MTK_FOE_VLAN2_WINFO_WCID GENMASK(13, 6)
+#define MTK_FOE_VLAN2_WINFO_RING GENMASK(15, 14)
enum {
MTK_FOE_STATE_INVALID,
@@ -281,6 +281,8 @@ int mtk_foe_entry_set_ipv6_tuple(struct
int mtk_foe_entry_set_dsa(struct mtk_foe_entry *entry, int port);
int mtk_foe_entry_set_vlan(struct mtk_foe_entry *entry, int vid);
int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid);
+int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq,
+ int bss, int wcid);
int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
u16 timestamp);
int mtk_ppe_debugfs_init(struct mtk_ppe *ppe);
--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
@@ -10,6 +10,7 @@
#include <net/pkt_cls.h>
#include <net/dsa.h>
#include "mtk_eth_soc.h"
+#include "mtk_wed.h"
struct mtk_flow_data {
struct ethhdr eth;
@@ -39,6 +40,7 @@ struct mtk_flow_entry {
struct rhash_head node;
unsigned long cookie;
u16 hash;
+ s8 wed_index;
};
static const struct rhashtable_params mtk_flow_ht_params = {
@@ -80,6 +82,35 @@ mtk_flow_offload_mangle_eth(const struct
memcpy(dest, src, act->mangle.mask ? 2 : 4);
}
+static int
+mtk_flow_get_wdma_info(struct net_device *dev, const u8 *addr, struct mtk_wdma_info *info)
+{
+ struct net_device_path_ctx ctx = {
+ .dev = dev,
+ .daddr = addr,
+ };
+ struct net_device_path path = {};
+
+ if (!IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED))
+ return -1;
+
+ if (!dev->netdev_ops->ndo_fill_forward_path)
+ return -1;
+
+ if (dev->netdev_ops->ndo_fill_forward_path(&ctx, &path))
+ return -1;
+
+ if (path.type != DEV_PATH_MTK_WDMA)
+ return -1;
+
+ info->wdma_idx = path.mtk_wdma.wdma_idx;
+ info->queue = path.mtk_wdma.queue;
+ info->bss = path.mtk_wdma.bss;
+ info->wcid = path.mtk_wdma.wcid;
+
+ return 0;
+}
+
static int
mtk_flow_mangle_ports(const struct flow_action_entry *act,
@@ -149,10 +180,20 @@ mtk_flow_get_dsa_port(struct net_device
static int
mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe,
- struct net_device *dev)
+ struct net_device *dev, const u8 *dest_mac,
+ int *wed_index)
{
+ struct mtk_wdma_info info = {};
int pse_port, dsa_port;
+ if (mtk_flow_get_wdma_info(dev, dest_mac, &info) == 0) {
+ mtk_foe_entry_set_wdma(foe, info.wdma_idx, info.queue, info.bss,
+ info.wcid);
+ pse_port = 3;
+ *wed_index = info.wdma_idx;
+ goto out;
+ }
+
dsa_port = mtk_flow_get_dsa_port(&dev);
if (dsa_port >= 0)
mtk_foe_entry_set_dsa(foe, dsa_port);
@@ -164,6 +205,7 @@ mtk_flow_set_output_device(struct mtk_et
else
return -EOPNOTSUPP;
+out:
mtk_foe_entry_set_pse_port(foe, pse_port);
return 0;
@@ -179,6 +221,7 @@ mtk_flow_offload_replace(struct mtk_eth
struct net_device *odev = NULL;
struct mtk_flow_entry *entry;
int offload_type = 0;
+ int wed_index = -1;
u16 addr_type = 0;
u32 timestamp;
u8 l4proto = 0;
@@ -329,10 +372,14 @@ mtk_flow_offload_replace(struct mtk_eth
if (data.pppoe.num == 1)
mtk_foe_entry_set_pppoe(&foe, data.pppoe.sid);
- err = mtk_flow_set_output_device(eth, &foe, odev);
+ err = mtk_flow_set_output_device(eth, &foe, odev, data.eth.h_dest,
+ &wed_index);
if (err)
return err;
+ if (wed_index >= 0 && (err = mtk_wed_flow_add(wed_index)) < 0)
+ return err;
+
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
if (!entry)
return -ENOMEM;
@@ -346,6 +393,7 @@ mtk_flow_offload_replace(struct mtk_eth
}
entry->hash = hash;
+ entry->wed_index = wed_index;
err = rhashtable_insert_fast(&eth->flow_table, &entry->node,
mtk_flow_ht_params);
if (err < 0)
@@ -356,6 +404,8 @@ clear_flow:
mtk_foe_entry_clear(&eth->ppe, hash);
free:
kfree(entry);
+ if (wed_index >= 0)
+ mtk_wed_flow_remove(wed_index);
return err;
}
@@ -372,6 +422,8 @@ mtk_flow_offload_destroy(struct mtk_eth
mtk_foe_entry_clear(&eth->ppe, entry->hash);
rhashtable_remove_fast(&eth->flow_table, &entry->node,
mtk_flow_ht_params);
+ if (entry->wed_index >= 0)
+ mtk_wed_flow_remove(entry->wed_index);
kfree(entry);
return 0;
--- a/drivers/net/ethernet/mediatek/mtk_wed.h
+++ b/drivers/net/ethernet/mediatek/mtk_wed.h
@@ -7,6 +7,7 @@
#include <linux/soc/mediatek/mtk_wed.h>
#include <linux/debugfs.h>
#include <linux/regmap.h>
+#include <linux/netdevice.h>
struct mtk_eth;
@@ -27,6 +28,12 @@ struct mtk_wed_hw {
int index;
};
+struct mtk_wdma_info {
+ u8 wdma_idx;
+ u8 queue;
+ u16 wcid;
+ u8 bss;
+};
#ifdef CONFIG_NET_MEDIATEK_SOC_WED
static inline void
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -849,6 +849,7 @@ enum net_device_path_type {
DEV_PATH_BRIDGE,
DEV_PATH_PPPOE,
DEV_PATH_DSA,
+ DEV_PATH_MTK_WDMA,
};
struct net_device_path {
@@ -874,6 +875,12 @@ struct net_device_path {
int port;
u16 proto;
} dsa;
+ struct {
+ u8 wdma_idx;
+ u8 queue;
+ u16 wcid;
+ u8 bss;
+ } mtk_wdma;
};
};
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -761,6 +761,10 @@ int dev_fill_forward_path(const struct n
if (WARN_ON_ONCE(last_dev == ctx.dev))
return -1;
}
+
+ if (!ctx.dev)
+ return ret;
+
path = dev_fwd_path(stack);
if (!path)
return -1;

View File

@ -0,0 +1,62 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Sat, 5 Feb 2022 18:36:36 +0100
Subject: [PATCH] arm64: dts: mediatek: mt7622: introduce nodes for
Wireless Ethernet Dispatch
Introduce wed0 and wed1 nodes in order to enable offloading forwarding
between ethernet and wireless devices on the mt7622 chipset.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/arch/arm64/boot/dts/mediatek/mt7622.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt7622.dtsi
@@ -893,6 +893,11 @@
};
};
+ hifsys: syscon@1af00000 {
+ compatible = "mediatek,mt7622-hifsys", "syscon";
+ reg = <0 0x1af00000 0 0x70>;
+ };
+
ethsys: syscon@1b000000 {
compatible = "mediatek,mt7622-ethsys",
"syscon";
@@ -911,6 +916,26 @@
#dma-cells = <1>;
};
+ pcie_mirror: pcie-mirror@10000400 {
+ compatible = "mediatek,mt7622-pcie-mirror",
+ "syscon";
+ reg = <0 0x10000400 0 0x10>;
+ };
+
+ wed0: wed@1020a000 {
+ compatible = "mediatek,mt7622-wed",
+ "syscon";
+ reg = <0 0x1020a000 0 0x1000>;
+ interrupts = <GIC_SPI 214 IRQ_TYPE_LEVEL_LOW>;
+ };
+
+ wed1: wed@1020b000 {
+ compatible = "mediatek,mt7622-wed",
+ "syscon";
+ reg = <0 0x1020b000 0 0x1000>;
+ interrupts = <GIC_SPI 215 IRQ_TYPE_LEVEL_LOW>;
+ };
+
eth: ethernet@1b100000 {
compatible = "mediatek,mt7622-eth",
"mediatek,mt2701-eth",
@@ -938,6 +963,9 @@
mediatek,ethsys = <&ethsys>;
mediatek,sgmiisys = <&sgmiisys>;
mediatek,cci-control = <&cci_control2>;
+ mediatek,wed = <&wed0>, <&wed1>;
+ mediatek,pcie-mirror = <&pcie_mirror>;
+ mediatek,hifsys = <&hifsys>;
dma-coherent;
#address-cells = <1>;
#size-cells = <0>;

View File

@ -0,0 +1,79 @@
From: David Bentham <db260179@gmail.com>
Date: Mon, 21 Feb 2022 15:36:16 +0100
Subject: [PATCH] net: ethernet: mtk_eth_soc: add ipv6 flow offload
support
Add the missing IPv6 flow offloading support for routing only.
Hardware flow offloading is done by the packet processing engine (PPE)
of the Ethernet MAC and as it doesn't support mangling of IPv6 packets,
IPv6 NAT cannot be supported.
Signed-off-by: David Bentham <db260179@gmail.com>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
@@ -6,6 +6,7 @@
#include <linux/if_ether.h>
#include <linux/rhashtable.h>
#include <linux/ip.h>
+#include <linux/ipv6.h>
#include <net/flow_offload.h>
#include <net/pkt_cls.h>
#include <net/dsa.h>
@@ -20,6 +21,11 @@ struct mtk_flow_data {
__be32 src_addr;
__be32 dst_addr;
} v4;
+
+ struct {
+ struct in6_addr src_addr;
+ struct in6_addr dst_addr;
+ } v6;
};
__be16 src_port;
@@ -65,6 +71,14 @@ mtk_flow_set_ipv4_addr(struct mtk_foe_en
data->v4.dst_addr, data->dst_port);
}
+static int
+mtk_flow_set_ipv6_addr(struct mtk_foe_entry *foe, struct mtk_flow_data *data)
+{
+ return mtk_foe_entry_set_ipv6_tuple(foe,
+ data->v6.src_addr.s6_addr32, data->src_port,
+ data->v6.dst_addr.s6_addr32, data->dst_port);
+}
+
static void
mtk_flow_offload_mangle_eth(const struct flow_action_entry *act, void *eth)
{
@@ -299,6 +313,9 @@ mtk_flow_offload_replace(struct mtk_eth
case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
offload_type = MTK_PPE_PKT_TYPE_IPV4_HNAPT;
break;
+ case FLOW_DISSECTOR_KEY_IPV6_ADDRS:
+ offload_type = MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T;
+ break;
default:
return -EOPNOTSUPP;
}
@@ -334,6 +351,17 @@ mtk_flow_offload_replace(struct mtk_eth
mtk_flow_set_ipv4_addr(&foe, &data, false);
}
+ if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
+ struct flow_match_ipv6_addrs addrs;
+
+ flow_rule_match_ipv6_addrs(rule, &addrs);
+
+ data.v6.src_addr = addrs.key->src;
+ data.v6.dst_addr = addrs.key->dst;
+
+ mtk_flow_set_ipv6_addr(&foe, &data);
+ }
+
flow_action_for_each(i, act, &rule->action) {
if (act->id != FLOW_ACTION_MANGLE)
continue;

View File

@ -0,0 +1,29 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Mon, 21 Feb 2022 15:37:21 +0100
Subject: [PATCH] net: ethernet: mtk_eth_soc: support TC_SETUP_BLOCK for
PPE offload
This allows offload entries to be created from user space
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
@@ -566,10 +566,13 @@ mtk_eth_setup_tc_block(struct net_device
int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type,
void *type_data)
{
- if (type == TC_SETUP_FT)
+ switch (type) {
+ case TC_SETUP_BLOCK:
+ case TC_SETUP_FT:
return mtk_eth_setup_tc_block(dev, type_data);
-
- return -EOPNOTSUPP;
+ default:
+ return -EOPNOTSUPP;
+ }
}
int mtk_eth_offload_init(struct mtk_eth *eth)

View File

@ -0,0 +1,159 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Mon, 21 Feb 2022 15:38:20 +0100
Subject: [PATCH] net: ethernet: mtk_eth_soc: allocate struct mtk_ppe
separately
Preparation for adding more data to it, which will increase its size.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -2312,7 +2312,7 @@ static int mtk_open(struct net_device *d
if (err)
return err;
- if (eth->soc->offload_version && mtk_ppe_start(&eth->ppe) == 0)
+ if (eth->soc->offload_version && mtk_ppe_start(eth->ppe) == 0)
gdm_config = MTK_GDMA_TO_PPE;
mtk_gdm_config(eth, gdm_config);
@@ -2386,7 +2386,7 @@ static int mtk_stop(struct net_device *d
mtk_dma_free(eth);
if (eth->soc->offload_version)
- mtk_ppe_stop(&eth->ppe);
+ mtk_ppe_stop(eth->ppe);
return 0;
}
@@ -3278,10 +3278,11 @@ static int mtk_probe(struct platform_dev
}
if (eth->soc->offload_version) {
- err = mtk_ppe_init(&eth->ppe, eth->dev,
- eth->base + MTK_ETH_PPE_BASE, 2);
- if (err)
+ eth->ppe = mtk_ppe_init(eth->dev, eth->base + MTK_ETH_PPE_BASE, 2);
+ if (!eth->ppe) {
+ err = -ENOMEM;
goto err_free_dev;
+ }
err = mtk_eth_offload_init(eth);
if (err)
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -982,7 +982,7 @@ struct mtk_eth {
u32 rx_dma_l4_valid;
int ip_align;
- struct mtk_ppe ppe;
+ struct mtk_ppe *ppe;
struct rhashtable flow_table;
};
--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
@@ -384,10 +384,15 @@ int mtk_foe_entry_commit(struct mtk_ppe
return hash;
}
-int mtk_ppe_init(struct mtk_ppe *ppe, struct device *dev, void __iomem *base,
+struct mtk_ppe *mtk_ppe_init(struct device *dev, void __iomem *base,
int version)
{
struct mtk_foe_entry *foe;
+ struct mtk_ppe *ppe;
+
+ ppe = devm_kzalloc(dev, sizeof(*ppe), GFP_KERNEL);
+ if (!ppe)
+ return NULL;
/* need to allocate a separate device, since it PPE DMA access is
* not coherent.
@@ -399,13 +404,13 @@ int mtk_ppe_init(struct mtk_ppe *ppe, st
foe = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*foe),
&ppe->foe_phys, GFP_KERNEL);
if (!foe)
- return -ENOMEM;
+ return NULL;
ppe->foe_table = foe;
mtk_ppe_debugfs_init(ppe);
- return 0;
+ return ppe;
}
static void mtk_ppe_init_foe_table(struct mtk_ppe *ppe)
--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
@@ -246,8 +246,7 @@ struct mtk_ppe {
void *acct_table;
};
-int mtk_ppe_init(struct mtk_ppe *ppe, struct device *dev, void __iomem *base,
- int version);
+struct mtk_ppe *mtk_ppe_init(struct device *dev, void __iomem *base, int version);
int mtk_ppe_start(struct mtk_ppe *ppe);
int mtk_ppe_stop(struct mtk_ppe *ppe);
--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
@@ -414,7 +414,7 @@ mtk_flow_offload_replace(struct mtk_eth
entry->cookie = f->cookie;
timestamp = mtk_eth_timestamp(eth);
- hash = mtk_foe_entry_commit(&eth->ppe, &foe, timestamp);
+ hash = mtk_foe_entry_commit(eth->ppe, &foe, timestamp);
if (hash < 0) {
err = hash;
goto free;
@@ -429,7 +429,7 @@ mtk_flow_offload_replace(struct mtk_eth
return 0;
clear_flow:
- mtk_foe_entry_clear(&eth->ppe, hash);
+ mtk_foe_entry_clear(eth->ppe, hash);
free:
kfree(entry);
if (wed_index >= 0)
@@ -447,7 +447,7 @@ mtk_flow_offload_destroy(struct mtk_eth
if (!entry)
return -ENOENT;
- mtk_foe_entry_clear(&eth->ppe, entry->hash);
+ mtk_foe_entry_clear(eth->ppe, entry->hash);
rhashtable_remove_fast(&eth->flow_table, &entry->node,
mtk_flow_ht_params);
if (entry->wed_index >= 0)
@@ -469,7 +469,7 @@ mtk_flow_offload_stats(struct mtk_eth *e
if (!entry)
return -ENOENT;
- timestamp = mtk_foe_entry_timestamp(&eth->ppe, entry->hash);
+ timestamp = mtk_foe_entry_timestamp(eth->ppe, entry->hash);
if (timestamp < 0)
return -ETIMEDOUT;
@@ -525,7 +525,7 @@ mtk_eth_setup_tc_block(struct net_device
struct flow_block_cb *block_cb;
flow_setup_cb_t *cb;
- if (!eth->ppe.foe_table)
+ if (!eth->ppe || !eth->ppe->foe_table)
return -EOPNOTSUPP;
if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
@@ -577,7 +577,7 @@ int mtk_eth_setup_tc(struct net_device *
int mtk_eth_offload_init(struct mtk_eth *eth)
{
- if (!eth->ppe.foe_table)
+ if (!eth->ppe || !eth->ppe->foe_table)
return 0;
return rhashtable_init(&eth->flow_table, &mtk_flow_ht_params);

View File

@ -0,0 +1,424 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Mon, 21 Feb 2022 15:39:18 +0100
Subject: [PATCH] net: ethernet: mtk_eth_soc: rework hardware flow table
management
The hardware was designed to handle flow detection and creation of flow entries
by itself, relying on the software primarily for filling in egress routing
information.
When there is a hash collision between multiple flows, this allows the hardware
to maintain the entry for the most active flow.
Additionally, the hardware only keeps offloading active for entries with at
least 30 packets per second.
With this rework, the code no longer creates a hardware entries directly.
Instead, the hardware entry is only created when the PPE reports a matching
unbound flow with the minimum target rate.
In order to reduce CPU overhead, looking for flows belonging to a hash entry
is rate limited to once every 100ms.
This rework is also used as preparation for emulating bridge offload by
managing L4 offload entries on demand.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -21,6 +21,7 @@
#include <linux/pinctrl/devinfo.h>
#include <linux/phylink.h>
#include <linux/jhash.h>
+#include <linux/bitfield.h>
#include <net/dsa.h>
#include "mtk_eth_soc.h"
@@ -1281,7 +1282,7 @@ static int mtk_poll_rx(struct napi_struc
struct net_device *netdev;
unsigned int pktlen;
dma_addr_t dma_addr;
- u32 hash;
+ u32 hash, reason;
int mac;
ring = mtk_get_rx_ring(eth);
@@ -1357,6 +1358,11 @@ static int mtk_poll_rx(struct napi_struc
skb_set_hash(skb, hash, PKT_HASH_TYPE_L4);
}
+ reason = FIELD_GET(MTK_RXD4_PPE_CPU_REASON, trxd.rxd4);
+ if (reason == MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED)
+ mtk_ppe_check_skb(eth->ppe, skb,
+ trxd.rxd4 & MTK_RXD4_FOE_ENTRY);
+
if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX &&
(trxd.rxd2 & RX_DMA_VTAG))
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
@@ -3278,7 +3284,7 @@ static int mtk_probe(struct platform_dev
}
if (eth->soc->offload_version) {
- eth->ppe = mtk_ppe_init(eth->dev, eth->base + MTK_ETH_PPE_BASE, 2);
+ eth->ppe = mtk_ppe_init(eth, eth->base + MTK_ETH_PPE_BASE, 2);
if (!eth->ppe) {
err = -ENOMEM;
goto err_free_dev;
--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
@@ -6,9 +6,12 @@
#include <linux/iopoll.h>
#include <linux/etherdevice.h>
#include <linux/platform_device.h>
+#include "mtk_eth_soc.h"
#include "mtk_ppe.h"
#include "mtk_ppe_regs.h"
+static DEFINE_SPINLOCK(ppe_lock);
+
static void ppe_w32(struct mtk_ppe *ppe, u32 reg, u32 val)
{
writel(val, ppe->base + reg);
@@ -41,6 +44,11 @@ static u32 ppe_clear(struct mtk_ppe *ppe
return ppe_m32(ppe, reg, val, 0);
}
+static u32 mtk_eth_timestamp(struct mtk_eth *eth)
+{
+ return mtk_r32(eth, 0x0010) & MTK_FOE_IB1_BIND_TIMESTAMP;
+}
+
static int mtk_ppe_wait_busy(struct mtk_ppe *ppe)
{
int ret;
@@ -353,26 +361,59 @@ static inline bool mtk_foe_entry_usable(
FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1) != MTK_FOE_STATE_BIND;
}
-int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
- u16 timestamp)
+static bool
+mtk_flow_entry_match(struct mtk_flow_entry *entry, struct mtk_foe_entry *data)
+{
+ int type, len;
+
+ if ((data->ib1 ^ entry->data.ib1) & MTK_FOE_IB1_UDP)
+ return false;
+
+ type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->data.ib1);
+ if (type > MTK_PPE_PKT_TYPE_IPV4_DSLITE)
+ len = offsetof(struct mtk_foe_entry, ipv6._rsv);
+ else
+ len = offsetof(struct mtk_foe_entry, ipv4.ib2);
+
+ return !memcmp(&entry->data.data, &data->data, len - 4);
+}
+
+static void
+mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
{
struct mtk_foe_entry *hwe;
- u32 hash;
+ struct mtk_foe_entry foe;
+ spin_lock_bh(&ppe_lock);
+ if (entry->hash == 0xffff)
+ goto out;
+
+ hwe = &ppe->foe_table[entry->hash];
+ memcpy(&foe, hwe, sizeof(foe));
+ if (!mtk_flow_entry_match(entry, &foe)) {
+ entry->hash = 0xffff;
+ goto out;
+ }
+
+ entry->data.ib1 = foe.ib1;
+
+out:
+ spin_unlock_bh(&ppe_lock);
+}
+
+static void
+__mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
+ u16 hash)
+{
+ struct mtk_foe_entry *hwe;
+ u16 timestamp;
+
+ timestamp = mtk_eth_timestamp(ppe->eth);
timestamp &= MTK_FOE_IB1_BIND_TIMESTAMP;
entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP;
entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_TIMESTAMP, timestamp);
- hash = mtk_ppe_hash_entry(entry);
hwe = &ppe->foe_table[hash];
- if (!mtk_foe_entry_usable(hwe)) {
- hwe++;
- hash++;
-
- if (!mtk_foe_entry_usable(hwe))
- return -ENOSPC;
- }
-
memcpy(&hwe->data, &entry->data, sizeof(hwe->data));
wmb();
hwe->ib1 = entry->ib1;
@@ -380,13 +421,77 @@ int mtk_foe_entry_commit(struct mtk_ppe
dma_wmb();
mtk_ppe_cache_clear(ppe);
+}
- return hash;
+void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+{
+ spin_lock_bh(&ppe_lock);
+ hlist_del_init(&entry->list);
+ if (entry->hash != 0xffff) {
+ ppe->foe_table[entry->hash].ib1 &= ~MTK_FOE_IB1_STATE;
+ ppe->foe_table[entry->hash].ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE,
+ MTK_FOE_STATE_BIND);
+ dma_wmb();
+ }
+ entry->hash = 0xffff;
+ spin_unlock_bh(&ppe_lock);
+}
+
+int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+{
+ u32 hash = mtk_ppe_hash_entry(&entry->data);
+
+ entry->hash = 0xffff;
+ spin_lock_bh(&ppe_lock);
+ hlist_add_head(&entry->list, &ppe->foe_flow[hash / 2]);
+ spin_unlock_bh(&ppe_lock);
+
+ return 0;
+}
+
+void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash)
+{
+ struct hlist_head *head = &ppe->foe_flow[hash / 2];
+ struct mtk_flow_entry *entry;
+ struct mtk_foe_entry *hwe = &ppe->foe_table[hash];
+ bool found = false;
+
+ if (hlist_empty(head))
+ return;
+
+ spin_lock_bh(&ppe_lock);
+ hlist_for_each_entry(entry, head, list) {
+ if (found || !mtk_flow_entry_match(entry, hwe)) {
+ if (entry->hash != 0xffff)
+ entry->hash = 0xffff;
+ continue;
+ }
+
+ entry->hash = hash;
+ __mtk_foe_entry_commit(ppe, &entry->data, hash);
+ found = true;
+ }
+ spin_unlock_bh(&ppe_lock);
+}
+
+int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+{
+ u16 now = mtk_eth_timestamp(ppe->eth) & MTK_FOE_IB1_BIND_TIMESTAMP;
+ u16 timestamp;
+
+ mtk_flow_entry_update(ppe, entry);
+ timestamp = entry->data.ib1 & MTK_FOE_IB1_BIND_TIMESTAMP;
+
+ if (timestamp > now)
+ return MTK_FOE_IB1_BIND_TIMESTAMP + 1 - timestamp + now;
+ else
+ return now - timestamp;
}
-struct mtk_ppe *mtk_ppe_init(struct device *dev, void __iomem *base,
+struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
int version)
{
+ struct device *dev = eth->dev;
struct mtk_foe_entry *foe;
struct mtk_ppe *ppe;
@@ -398,6 +503,7 @@ struct mtk_ppe *mtk_ppe_init(struct devi
* not coherent.
*/
ppe->base = base;
+ ppe->eth = eth;
ppe->dev = dev;
ppe->version = version;
--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
@@ -235,7 +235,17 @@ enum {
MTK_PPE_CPU_REASON_INVALID = 0x1f,
};
+struct mtk_flow_entry {
+ struct rhash_head node;
+ struct hlist_node list;
+ unsigned long cookie;
+ struct mtk_foe_entry data;
+ u16 hash;
+ s8 wed_index;
+};
+
struct mtk_ppe {
+ struct mtk_eth *eth;
struct device *dev;
void __iomem *base;
int version;
@@ -243,18 +253,33 @@ struct mtk_ppe {
struct mtk_foe_entry *foe_table;
dma_addr_t foe_phys;
+ u16 foe_check_time[MTK_PPE_ENTRIES];
+ struct hlist_head foe_flow[MTK_PPE_ENTRIES / 2];
+
void *acct_table;
};
-struct mtk_ppe *mtk_ppe_init(struct device *dev, void __iomem *base, int version);
+struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int version);
int mtk_ppe_start(struct mtk_ppe *ppe);
int mtk_ppe_stop(struct mtk_ppe *ppe);
+void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash);
+
static inline void
-mtk_foe_entry_clear(struct mtk_ppe *ppe, u16 hash)
+mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash)
{
- ppe->foe_table[hash].ib1 = 0;
- dma_wmb();
+ u16 now, diff;
+
+ if (!ppe)
+ return;
+
+ now = (u16)jiffies;
+ diff = now - ppe->foe_check_time[hash];
+ if (diff < HZ / 10)
+ return;
+
+ ppe->foe_check_time[hash] = now;
+ __mtk_ppe_check_skb(ppe, skb, hash);
}
static inline int
@@ -282,8 +307,9 @@ int mtk_foe_entry_set_vlan(struct mtk_fo
int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid);
int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq,
int bss, int wcid);
-int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
- u16 timestamp);
+int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
+void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
+int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
int mtk_ppe_debugfs_init(struct mtk_ppe *ppe);
#endif
--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
@@ -42,13 +42,6 @@ struct mtk_flow_data {
} pppoe;
};
-struct mtk_flow_entry {
- struct rhash_head node;
- unsigned long cookie;
- u16 hash;
- s8 wed_index;
-};
-
static const struct rhashtable_params mtk_flow_ht_params = {
.head_offset = offsetof(struct mtk_flow_entry, node),
.key_offset = offsetof(struct mtk_flow_entry, cookie),
@@ -56,12 +49,6 @@ static const struct rhashtable_params mt
.automatic_shrinking = true,
};
-static u32
-mtk_eth_timestamp(struct mtk_eth *eth)
-{
- return mtk_r32(eth, 0x0010) & MTK_FOE_IB1_BIND_TIMESTAMP;
-}
-
static int
mtk_flow_set_ipv4_addr(struct mtk_foe_entry *foe, struct mtk_flow_data *data,
bool egress)
@@ -237,10 +224,8 @@ mtk_flow_offload_replace(struct mtk_eth
int offload_type = 0;
int wed_index = -1;
u16 addr_type = 0;
- u32 timestamp;
u8 l4proto = 0;
int err = 0;
- int hash;
int i;
if (rhashtable_lookup(&eth->flow_table, &f->cookie, mtk_flow_ht_params))
@@ -413,23 +398,21 @@ mtk_flow_offload_replace(struct mtk_eth
return -ENOMEM;
entry->cookie = f->cookie;
- timestamp = mtk_eth_timestamp(eth);
- hash = mtk_foe_entry_commit(eth->ppe, &foe, timestamp);
- if (hash < 0) {
- err = hash;
+ memcpy(&entry->data, &foe, sizeof(entry->data));
+ entry->wed_index = wed_index;
+
+ if (mtk_foe_entry_commit(eth->ppe, entry) < 0)
goto free;
- }
- entry->hash = hash;
- entry->wed_index = wed_index;
err = rhashtable_insert_fast(&eth->flow_table, &entry->node,
mtk_flow_ht_params);
if (err < 0)
- goto clear_flow;
+ goto clear;
return 0;
-clear_flow:
- mtk_foe_entry_clear(eth->ppe, hash);
+
+clear:
+ mtk_foe_entry_clear(eth->ppe, entry);
free:
kfree(entry);
if (wed_index >= 0)
@@ -447,7 +430,7 @@ mtk_flow_offload_destroy(struct mtk_eth
if (!entry)
return -ENOENT;
- mtk_foe_entry_clear(eth->ppe, entry->hash);
+ mtk_foe_entry_clear(eth->ppe, entry);
rhashtable_remove_fast(&eth->flow_table, &entry->node,
mtk_flow_ht_params);
if (entry->wed_index >= 0)
@@ -461,7 +444,6 @@ static int
mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f)
{
struct mtk_flow_entry *entry;
- int timestamp;
u32 idle;
entry = rhashtable_lookup(&eth->flow_table, &f->cookie,
@@ -469,11 +451,7 @@ mtk_flow_offload_stats(struct mtk_eth *e
if (!entry)
return -ENOENT;
- timestamp = mtk_foe_entry_timestamp(eth->ppe, entry->hash);
- if (timestamp < 0)
- return -ETIMEDOUT;
-
- idle = mtk_eth_timestamp(eth) - timestamp;
+ idle = mtk_foe_entry_idle_time(eth->ppe, entry);
f->stats.lastused = jiffies - idle * HZ;
return 0;

View File

@ -0,0 +1,44 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Mon, 21 Feb 2022 15:55:19 +0100
Subject: [PATCH] net: ethernet: mtk_eth_soc: remove bridge flow offload
type entry support
According to MediaTek, this feature is not supported in current hardware
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
@@ -84,13 +84,6 @@ static u32 mtk_ppe_hash_entry(struct mtk
u32 hash;
switch (FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, e->ib1)) {
- case MTK_PPE_PKT_TYPE_BRIDGE:
- hv1 = e->bridge.src_mac_lo;
- hv1 ^= ((e->bridge.src_mac_hi & 0xffff) << 16);
- hv2 = e->bridge.src_mac_hi >> 16;
- hv2 ^= e->bridge.dest_mac_lo;
- hv3 = e->bridge.dest_mac_hi;
- break;
case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
hv1 = e->ipv4.orig.ports;
@@ -572,7 +565,6 @@ int mtk_ppe_start(struct mtk_ppe *ppe)
MTK_PPE_FLOW_CFG_IP4_NAT |
MTK_PPE_FLOW_CFG_IP4_NAPT |
MTK_PPE_FLOW_CFG_IP4_DSLITE |
- MTK_PPE_FLOW_CFG_L2_BRIDGE |
MTK_PPE_FLOW_CFG_IP4_NAT_FRAG;
ppe_w32(ppe, MTK_PPE_FLOW_CFG, val);
--- a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
@@ -32,7 +32,6 @@ static const char *mtk_foe_pkt_type_str(
static const char * const type_str[] = {
[MTK_PPE_PKT_TYPE_IPV4_HNAPT] = "IPv4 5T",
[MTK_PPE_PKT_TYPE_IPV4_ROUTE] = "IPv4 3T",
- [MTK_PPE_PKT_TYPE_BRIDGE] = "L2",
[MTK_PPE_PKT_TYPE_IPV4_DSLITE] = "DS-LITE",
[MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T] = "IPv6 3T",
[MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T] = "IPv6 5T",

View File

@ -0,0 +1,553 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Wed, 23 Feb 2022 10:56:34 +0100
Subject: [PATCH] net: ethernet: mtk_eth_soc: support creating mac
address based offload entries
This will be used to implement a limited form of bridge offloading.
Since the hardware does not support flow table entries with just source
and destination MAC address, the driver has to emulate it.
The hardware automatically creates entries entries for incoming flows, even
when they are bridged instead of routed, and reports when packets for these
flows have reached the minimum PPS rate for offloading.
After this happens, we look up the L2 flow offload entry based on the MAC
header and fill in the output routing information in the flow table.
The dynamically created per-flow entries are automatically removed when
either the hardware flowtable entry expires, is replaced, or if the offload
rule they belong to is removed
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
@@ -6,12 +6,22 @@
#include <linux/iopoll.h>
#include <linux/etherdevice.h>
#include <linux/platform_device.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <net/dsa.h>
#include "mtk_eth_soc.h"
#include "mtk_ppe.h"
#include "mtk_ppe_regs.h"
static DEFINE_SPINLOCK(ppe_lock);
+static const struct rhashtable_params mtk_flow_l2_ht_params = {
+ .head_offset = offsetof(struct mtk_flow_entry, l2_node),
+ .key_offset = offsetof(struct mtk_flow_entry, data.bridge),
+ .key_len = offsetof(struct mtk_foe_bridge, key_end),
+ .automatic_shrinking = true,
+};
+
static void ppe_w32(struct mtk_ppe *ppe, u32 reg, u32 val)
{
writel(val, ppe->base + reg);
@@ -123,6 +133,9 @@ mtk_foe_entry_l2(struct mtk_foe_entry *e
{
int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
+ if (type == MTK_PPE_PKT_TYPE_BRIDGE)
+ return &entry->bridge.l2;
+
if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE)
return &entry->ipv6.l2;
@@ -134,6 +147,9 @@ mtk_foe_entry_ib2(struct mtk_foe_entry *
{
int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
+ if (type == MTK_PPE_PKT_TYPE_BRIDGE)
+ return &entry->bridge.ib2;
+
if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE)
return &entry->ipv6.ib2;
@@ -168,7 +184,12 @@ int mtk_foe_entry_prepare(struct mtk_foe
if (type == MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T)
entry->ipv6.ports = ports_pad;
- if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) {
+ if (type == MTK_PPE_PKT_TYPE_BRIDGE) {
+ ether_addr_copy(entry->bridge.src_mac, src_mac);
+ ether_addr_copy(entry->bridge.dest_mac, dest_mac);
+ entry->bridge.ib2 = val;
+ l2 = &entry->bridge.l2;
+ } else if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) {
entry->ipv6.ib2 = val;
l2 = &entry->ipv6.l2;
} else {
@@ -372,12 +393,96 @@ mtk_flow_entry_match(struct mtk_flow_ent
}
static void
+__mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+{
+ struct hlist_head *head;
+ struct hlist_node *tmp;
+
+ if (entry->type == MTK_FLOW_TYPE_L2) {
+ rhashtable_remove_fast(&ppe->l2_flows, &entry->l2_node,
+ mtk_flow_l2_ht_params);
+
+ head = &entry->l2_flows;
+ hlist_for_each_entry_safe(entry, tmp, head, l2_data.list)
+ __mtk_foe_entry_clear(ppe, entry);
+ return;
+ }
+
+ hlist_del_init(&entry->list);
+ if (entry->hash != 0xffff) {
+ ppe->foe_table[entry->hash].ib1 &= ~MTK_FOE_IB1_STATE;
+ ppe->foe_table[entry->hash].ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE,
+ MTK_FOE_STATE_BIND);
+ dma_wmb();
+ }
+ entry->hash = 0xffff;
+
+ if (entry->type != MTK_FLOW_TYPE_L2_SUBFLOW)
+ return;
+
+ hlist_del_init(&entry->l2_data.list);
+ kfree(entry);
+}
+
+static int __mtk_foe_entry_idle_time(struct mtk_ppe *ppe, u32 ib1)
+{
+ u16 timestamp;
+ u16 now;
+
+ now = mtk_eth_timestamp(ppe->eth) & MTK_FOE_IB1_BIND_TIMESTAMP;
+ timestamp = ib1 & MTK_FOE_IB1_BIND_TIMESTAMP;
+
+ if (timestamp > now)
+ return MTK_FOE_IB1_BIND_TIMESTAMP + 1 - timestamp + now;
+ else
+ return now - timestamp;
+}
+
+static void
+mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+{
+ struct mtk_flow_entry *cur;
+ struct mtk_foe_entry *hwe;
+ struct hlist_node *tmp;
+ int idle;
+
+ idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
+ hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_data.list) {
+ int cur_idle;
+ u32 ib1;
+
+ hwe = &ppe->foe_table[cur->hash];
+ ib1 = READ_ONCE(hwe->ib1);
+
+ if (FIELD_GET(MTK_FOE_IB1_STATE, ib1) != MTK_FOE_STATE_BIND) {
+ cur->hash = 0xffff;
+ __mtk_foe_entry_clear(ppe, cur);
+ continue;
+ }
+
+ cur_idle = __mtk_foe_entry_idle_time(ppe, ib1);
+ if (cur_idle >= idle)
+ continue;
+
+ idle = cur_idle;
+ entry->data.ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP;
+ entry->data.ib1 |= hwe->ib1 & MTK_FOE_IB1_BIND_TIMESTAMP;
+ }
+}
+
+static void
mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
{
struct mtk_foe_entry *hwe;
struct mtk_foe_entry foe;
spin_lock_bh(&ppe_lock);
+
+ if (entry->type == MTK_FLOW_TYPE_L2) {
+ mtk_flow_entry_update_l2(ppe, entry);
+ goto out;
+ }
+
if (entry->hash == 0xffff)
goto out;
@@ -419,21 +524,28 @@ __mtk_foe_entry_commit(struct mtk_ppe *p
void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
{
spin_lock_bh(&ppe_lock);
- hlist_del_init(&entry->list);
- if (entry->hash != 0xffff) {
- ppe->foe_table[entry->hash].ib1 &= ~MTK_FOE_IB1_STATE;
- ppe->foe_table[entry->hash].ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE,
- MTK_FOE_STATE_BIND);
- dma_wmb();
- }
- entry->hash = 0xffff;
+ __mtk_foe_entry_clear(ppe, entry);
spin_unlock_bh(&ppe_lock);
}
+static int
+mtk_foe_entry_commit_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+{
+ entry->type = MTK_FLOW_TYPE_L2;
+
+ return rhashtable_insert_fast(&ppe->l2_flows, &entry->l2_node,
+ mtk_flow_l2_ht_params);
+}
+
int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
{
- u32 hash = mtk_ppe_hash_entry(&entry->data);
+ int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->data.ib1);
+ u32 hash;
+
+ if (type == MTK_PPE_PKT_TYPE_BRIDGE)
+ return mtk_foe_entry_commit_l2(ppe, entry);
+ hash = mtk_ppe_hash_entry(&entry->data);
entry->hash = 0xffff;
spin_lock_bh(&ppe_lock);
hlist_add_head(&entry->list, &ppe->foe_flow[hash / 2]);
@@ -442,18 +554,72 @@ int mtk_foe_entry_commit(struct mtk_ppe
return 0;
}
+static void
+mtk_foe_entry_commit_subflow(struct mtk_ppe *ppe, struct mtk_flow_entry *entry,
+ u16 hash)
+{
+ struct mtk_flow_entry *flow_info;
+ struct mtk_foe_entry foe, *hwe;
+ struct mtk_foe_mac_info *l2;
+ u32 ib1_mask = MTK_FOE_IB1_PACKET_TYPE | MTK_FOE_IB1_UDP;
+ int type;
+
+ flow_info = kzalloc(offsetof(struct mtk_flow_entry, l2_data.end),
+ GFP_ATOMIC);
+ if (!flow_info)
+ return;
+
+ flow_info->l2_data.base_flow = entry;
+ flow_info->type = MTK_FLOW_TYPE_L2_SUBFLOW;
+ flow_info->hash = hash;
+ hlist_add_head(&flow_info->list, &ppe->foe_flow[hash / 2]);
+ hlist_add_head(&flow_info->l2_data.list, &entry->l2_flows);
+
+ hwe = &ppe->foe_table[hash];
+ memcpy(&foe, hwe, sizeof(foe));
+ foe.ib1 &= ib1_mask;
+ foe.ib1 |= entry->data.ib1 & ~ib1_mask;
+
+ l2 = mtk_foe_entry_l2(&foe);
+ memcpy(l2, &entry->data.bridge.l2, sizeof(*l2));
+
+ type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, foe.ib1);
+ if (type == MTK_PPE_PKT_TYPE_IPV4_HNAPT)
+ memcpy(&foe.ipv4.new, &foe.ipv4.orig, sizeof(foe.ipv4.new));
+ else if (type >= MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T && l2->etype == ETH_P_IP)
+ l2->etype = ETH_P_IPV6;
+
+ *mtk_foe_entry_ib2(&foe) = entry->data.bridge.ib2;
+
+ __mtk_foe_entry_commit(ppe, &foe, hash);
+}
+
void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash)
{
struct hlist_head *head = &ppe->foe_flow[hash / 2];
- struct mtk_flow_entry *entry;
struct mtk_foe_entry *hwe = &ppe->foe_table[hash];
+ struct mtk_flow_entry *entry;
+ struct mtk_foe_bridge key = {};
+ struct ethhdr *eh;
bool found = false;
-
- if (hlist_empty(head))
- return;
+ u8 *tag;
spin_lock_bh(&ppe_lock);
+
+ if (FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) == MTK_FOE_STATE_BIND)
+ goto out;
+
hlist_for_each_entry(entry, head, list) {
+ if (entry->type == MTK_FLOW_TYPE_L2_SUBFLOW) {
+ if (unlikely(FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) ==
+ MTK_FOE_STATE_BIND))
+ continue;
+
+ entry->hash = 0xffff;
+ __mtk_foe_entry_clear(ppe, entry);
+ continue;
+ }
+
if (found || !mtk_flow_entry_match(entry, hwe)) {
if (entry->hash != 0xffff)
entry->hash = 0xffff;
@@ -464,21 +630,50 @@ void __mtk_ppe_check_skb(struct mtk_ppe
__mtk_foe_entry_commit(ppe, &entry->data, hash);
found = true;
}
+
+ if (found)
+ goto out;
+
+ eh = eth_hdr(skb);
+ ether_addr_copy(key.dest_mac, eh->h_dest);
+ ether_addr_copy(key.src_mac, eh->h_source);
+ tag = skb->data - 2;
+ key.vlan = 0;
+ switch (skb->protocol) {
+#if IS_ENABLED(CONFIG_NET_DSA)
+ case htons(ETH_P_XDSA):
+ if (!netdev_uses_dsa(skb->dev) ||
+ skb->dev->dsa_ptr->tag_ops->proto != DSA_TAG_PROTO_MTK)
+ goto out;
+
+ tag += 4;
+ if (get_unaligned_be16(tag) != ETH_P_8021Q)
+ break;
+
+ fallthrough;
+#endif
+ case htons(ETH_P_8021Q):
+ key.vlan = get_unaligned_be16(tag + 2) & VLAN_VID_MASK;
+ break;
+ default:
+ break;
+ }
+
+ entry = rhashtable_lookup_fast(&ppe->l2_flows, &key, mtk_flow_l2_ht_params);
+ if (!entry)
+ goto out;
+
+ mtk_foe_entry_commit_subflow(ppe, entry, hash);
+
+out:
spin_unlock_bh(&ppe_lock);
}
int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
{
- u16 now = mtk_eth_timestamp(ppe->eth) & MTK_FOE_IB1_BIND_TIMESTAMP;
- u16 timestamp;
-
mtk_flow_entry_update(ppe, entry);
- timestamp = entry->data.ib1 & MTK_FOE_IB1_BIND_TIMESTAMP;
- if (timestamp > now)
- return MTK_FOE_IB1_BIND_TIMESTAMP + 1 - timestamp + now;
- else
- return now - timestamp;
+ return __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
}
struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
@@ -492,6 +687,8 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_
if (!ppe)
return NULL;
+ rhashtable_init(&ppe->l2_flows, &mtk_flow_l2_ht_params);
+
/* need to allocate a separate device, since it PPE DMA access is
* not coherent.
*/
--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
@@ -6,6 +6,7 @@
#include <linux/kernel.h>
#include <linux/bitfield.h>
+#include <linux/rhashtable.h>
#define MTK_ETH_PPE_BASE 0xc00
@@ -84,19 +85,16 @@ struct mtk_foe_mac_info {
u16 src_mac_lo;
};
+/* software-only entry type */
struct mtk_foe_bridge {
- u32 dest_mac_hi;
-
- u16 src_mac_lo;
- u16 dest_mac_lo;
+ u8 dest_mac[ETH_ALEN];
+ u8 src_mac[ETH_ALEN];
+ u16 vlan;
- u32 src_mac_hi;
+ struct {} key_end;
u32 ib2;
- u32 _rsv[5];
-
- u32 udf_tsid;
struct mtk_foe_mac_info l2;
};
@@ -235,13 +233,33 @@ enum {
MTK_PPE_CPU_REASON_INVALID = 0x1f,
};
+enum {
+ MTK_FLOW_TYPE_L4,
+ MTK_FLOW_TYPE_L2,
+ MTK_FLOW_TYPE_L2_SUBFLOW,
+};
+
struct mtk_flow_entry {
+ union {
+ struct hlist_node list;
+ struct {
+ struct rhash_head l2_node;
+ struct hlist_head l2_flows;
+ };
+ };
+ u8 type;
+ s8 wed_index;
+ u16 hash;
+ union {
+ struct mtk_foe_entry data;
+ struct {
+ struct mtk_flow_entry *base_flow;
+ struct hlist_node list;
+ struct {} end;
+ } l2_data;
+ };
struct rhash_head node;
- struct hlist_node list;
unsigned long cookie;
- struct mtk_foe_entry data;
- u16 hash;
- s8 wed_index;
};
struct mtk_ppe {
@@ -256,6 +274,8 @@ struct mtk_ppe {
u16 foe_check_time[MTK_PPE_ENTRIES];
struct hlist_head foe_flow[MTK_PPE_ENTRIES / 2];
+ struct rhashtable l2_flows;
+
void *acct_table;
};
--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
@@ -31,6 +31,8 @@ struct mtk_flow_data {
__be16 src_port;
__be16 dst_port;
+ u16 vlan_in;
+
struct {
u16 id;
__be16 proto;
@@ -260,9 +262,45 @@ mtk_flow_offload_replace(struct mtk_eth
return -EOPNOTSUPP;
}
+ switch (addr_type) {
+ case 0:
+ offload_type = MTK_PPE_PKT_TYPE_BRIDGE;
+ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
+ struct flow_match_eth_addrs match;
+
+ flow_rule_match_eth_addrs(rule, &match);
+ memcpy(data.eth.h_dest, match.key->dst, ETH_ALEN);
+ memcpy(data.eth.h_source, match.key->src, ETH_ALEN);
+ } else {
+ return -EOPNOTSUPP;
+ }
+
+ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
+ struct flow_match_vlan match;
+
+ flow_rule_match_vlan(rule, &match);
+
+ if (match.key->vlan_tpid != cpu_to_be16(ETH_P_8021Q))
+ return -EOPNOTSUPP;
+
+ data.vlan_in = match.key->vlan_id;
+ }
+ break;
+ case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
+ offload_type = MTK_PPE_PKT_TYPE_IPV4_HNAPT;
+ break;
+ case FLOW_DISSECTOR_KEY_IPV6_ADDRS:
+ offload_type = MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
flow_action_for_each(i, act, &rule->action) {
switch (act->id) {
case FLOW_ACTION_MANGLE:
+ if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE)
+ return -EOPNOTSUPP;
if (act->mangle.htype == FLOW_ACT_MANGLE_HDR_TYPE_ETH)
mtk_flow_offload_mangle_eth(act, &data.eth);
break;
@@ -294,17 +332,6 @@ mtk_flow_offload_replace(struct mtk_eth
}
}
- switch (addr_type) {
- case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
- offload_type = MTK_PPE_PKT_TYPE_IPV4_HNAPT;
- break;
- case FLOW_DISSECTOR_KEY_IPV6_ADDRS:
- offload_type = MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T;
- break;
- default:
- return -EOPNOTSUPP;
- }
-
if (!is_valid_ether_addr(data.eth.h_source) ||
!is_valid_ether_addr(data.eth.h_dest))
return -EINVAL;
@@ -318,10 +345,13 @@ mtk_flow_offload_replace(struct mtk_eth
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
struct flow_match_ports ports;
+ if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE)
+ return -EOPNOTSUPP;
+
flow_rule_match_ports(rule, &ports);
data.src_port = ports.key->src;
data.dst_port = ports.key->dst;
- } else {
+ } else if (offload_type != MTK_PPE_PKT_TYPE_BRIDGE) {
return -EOPNOTSUPP;
}
@@ -351,6 +381,9 @@ mtk_flow_offload_replace(struct mtk_eth
if (act->id != FLOW_ACTION_MANGLE)
continue;
+ if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE)
+ return -EOPNOTSUPP;
+
switch (act->mangle.htype) {
case FLOW_ACT_MANGLE_HDR_TYPE_TCP:
case FLOW_ACT_MANGLE_HDR_TYPE_UDP:
@@ -376,6 +409,9 @@ mtk_flow_offload_replace(struct mtk_eth
return err;
}
+ if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE)
+ foe.bridge.vlan = data.vlan_in;
+
if (data.vlan.num == 1) {
if (data.vlan.proto != htons(ETH_P_8021Q))
return -EOPNOTSUPP;

View File

@ -0,0 +1,41 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Mon, 21 Mar 2022 20:39:59 +0100
Subject: [PATCH] net: ethernet: mtk_eth_soc: enable threaded NAPI
This can improve performance under load by ensuring that NAPI processing is
not pinned on CPU 0.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -2186,8 +2186,8 @@ static irqreturn_t mtk_handle_irq_rx(int
eth->rx_events++;
if (likely(napi_schedule_prep(&eth->rx_napi))) {
- __napi_schedule(&eth->rx_napi);
mtk_rx_irq_disable(eth, MTK_RX_DONE_INT);
+ __napi_schedule(&eth->rx_napi);
}
return IRQ_HANDLED;
@@ -2199,8 +2199,8 @@ static irqreturn_t mtk_handle_irq_tx(int
eth->tx_events++;
if (likely(napi_schedule_prep(&eth->tx_napi))) {
- __napi_schedule(&eth->tx_napi);
mtk_tx_irq_disable(eth, MTK_TX_DONE_INT);
+ __napi_schedule(&eth->tx_napi);
}
return IRQ_HANDLED;
@@ -3313,6 +3313,8 @@ static int mtk_probe(struct platform_dev
* for NAPI to work
*/
init_dummy_netdev(&eth->dummy_dev);
+ eth->dummy_dev.threaded = 1;
+ strcpy(eth->dummy_dev.name, "mtk_eth");
netif_napi_add(&eth->dummy_dev, &eth->tx_napi, mtk_napi_tx,
MTK_NAPI_WEIGHT);
netif_napi_add(&eth->dummy_dev, &eth->rx_napi, mtk_napi_rx,

View File

@ -0,0 +1,222 @@
From fc23ea48ba52c24f201fe5ca0132ee1a3de5a70a Mon Sep 17 00:00:00 2001
From: Mauri Sandberg <maukka@ext.kapsi.fi>
Date: Thu, 25 Mar 2021 11:48:05 +0200
Subject: [PATCH 2/2] gpio: gpio-cascade: add generic GPIO cascade
Adds support for building cascades of GPIO lines. That is, it allows
setups when there is one upstream line and multiple cascaded lines, out
of which one can be chosen at a time. The status of the upstream line
can be conveyed to the selected cascaded line or, vice versa, the status
of the cascaded line can be conveyed to the upstream line.
A multiplexer is being used to select, which cascaded GPIO line is being
used at any given time.
At the moment only input direction is supported. In future it should be
possible to add support for output direction, too.
Signed-off-by: Mauri Sandberg <maukka@ext.kapsi.fi>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
---
v7 -> v8:
- rearrange members in struct gpio_cascade
- cosmetic changes in file header and in one function declaration
- added Reviewed-by tags by Linus and Andy
v6 -> v7:
- In Kconfig add info about module name
- adhere to new convention that allows lines longer than 80 chars
- use dev_probe_err with upstream gpio line too
- refactor for cleaner exit of probe function.
v5 -> v6:
- In Kconfig, remove dependency to OF_GPIO and select only MULTIPLEXER
- refactor code preferring one-liners
- clean up prints, removing them from success-path.
- don't explicitly set gpio_chip.of_node as it's done in the GPIO library
- use devm_gpiochip_add_data instead of gpiochip_add
v4 -> v5:
- renamed gpio-mux-input -> gpio-cascade. refactored code accordingly
here and there and changed to use new bindings and compatible string
- ambigious and vague 'pin' was rename to 'upstream_line'
- dropped Tested-by and Reviewed-by due to changes in bindings
- dropped Reported-by suggested by an automatic bot as it was not really
appropriate to begin with
- functionally it's the same as v4
v3 -> v4:
- Changed author email
- Included Tested-by and Reviewed-by from Drew
v2 -> v3:
- use managed device resources
- update Kconfig description
v1 -> v2:
- removed .owner from platform_driver as per test bot's instruction
- added MODULE_AUTHOR, MODULE_DESCRIPTION, MODULE_LICENSE
- added gpio_mux_input_get_direction as it's recommended for all chips
- removed because this is input only chip: gpio_mux_input_set_value
- removed because they are not needed for input/output only chips:
gpio_mux_input_direction_input
gpio_mux_input_direction_output
- fixed typo in an error message
- added info message about successful registration
- removed can_sleep flag as this does not sleep while getting GPIO value
like I2C or SPI do
- Updated description in Kconfig
---
drivers/gpio/Kconfig | 15 +++++
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-cascade.c | 117 ++++++++++++++++++++++++++++++++++++
3 files changed, 133 insertions(+)
create mode 100644 drivers/gpio/gpio-cascade.c
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1683,4 +1683,19 @@ config GPIO_VIRTIO
endmenu
+comment "Other GPIO expanders"
+
+config GPIO_CASCADE
+ tristate "General GPIO cascade"
+ select MULTIPLEXER
+ help
+ Say yes here to enable support for generic GPIO cascade.
+
+ This allows building one-to-many cascades of GPIO lines using
+ different types of multiplexers readily available. At the
+ moment only input lines are supported.
+
+ To build the driver as a module choose 'm' and the resulting module
+ will be called 'gpio-cascade'.
+
endif
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd
obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o
obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o
obj-$(CONFIG_GPIO_CADENCE) += gpio-cadence.o
+obj-$(CONFIG_GPIO_CASCADE) += gpio-cascade.o
obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o
obj-$(CONFIG_GPIO_SNPS_CREG) += gpio-creg-snps.o
obj-$(CONFIG_GPIO_CRYSTAL_COVE) += gpio-crystalcove.o
--- /dev/null
+++ b/drivers/gpio/gpio-cascade.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * A generic GPIO cascade driver
+ *
+ * Copyright (C) 2021 Mauri Sandberg <maukka@ext.kapsi.fi>
+ *
+ * This allows building cascades of GPIO lines in a manner illustrated
+ * below:
+ *
+ * /|---- Cascaded GPIO line 0
+ * Upstream | |---- Cascaded GPIO line 1
+ * GPIO line ----+ | .
+ * | | .
+ * \|---- Cascaded GPIO line n
+ *
+ * A multiplexer is being used to select, which cascaded line is being
+ * addressed at any given time.
+ *
+ * At the moment only input mode is supported due to lack of means for
+ * testing output functionality. At least theoretically output should be
+ * possible with open drain constructions.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/mux/consumer.h>
+
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+
+struct gpio_cascade {
+ struct gpio_chip gpio_chip;
+ struct device *parent;
+ struct mux_control *mux_control;
+ struct gpio_desc *upstream_line;
+};
+
+static struct gpio_cascade *chip_to_cascade(struct gpio_chip *gc)
+{
+ return container_of(gc, struct gpio_cascade, gpio_chip);
+}
+
+static int gpio_cascade_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+ return GPIO_LINE_DIRECTION_IN;
+}
+
+static int gpio_cascade_get_value(struct gpio_chip *gc, unsigned int offset)
+{
+ struct gpio_cascade *cas = chip_to_cascade(gc);
+ int ret;
+
+ ret = mux_control_select(cas->mux_control, offset);
+ if (ret)
+ return ret;
+
+ ret = gpiod_get_value(cas->upstream_line);
+ mux_control_deselect(cas->mux_control);
+ return ret;
+}
+
+static int gpio_cascade_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct gpio_cascade *cas;
+ struct mux_control *mc;
+ struct gpio_desc *upstream;
+ struct gpio_chip *gc;
+
+ cas = devm_kzalloc(dev, sizeof(*cas), GFP_KERNEL);
+ if (!cas)
+ return -ENOMEM;
+
+ mc = devm_mux_control_get(dev, NULL);
+ if (IS_ERR(mc))
+ return dev_err_probe(dev, PTR_ERR(mc), "unable to get mux-control\n");
+
+ cas->mux_control = mc;
+ upstream = devm_gpiod_get(dev, "upstream", GPIOD_IN);
+ if (IS_ERR(upstream))
+ return dev_err_probe(dev, PTR_ERR(upstream), "unable to claim upstream GPIO line\n");
+
+ cas->upstream_line = upstream;
+ cas->parent = dev;
+
+ gc = &cas->gpio_chip;
+ gc->get = gpio_cascade_get_value;
+ gc->get_direction = gpio_cascade_get_direction;
+ gc->base = -1;
+ gc->ngpio = mux_control_states(mc);
+ gc->label = dev_name(cas->parent);
+ gc->parent = cas->parent;
+ gc->owner = THIS_MODULE;
+
+ platform_set_drvdata(pdev, cas);
+ return devm_gpiochip_add_data(dev, &cas->gpio_chip, NULL);
+}
+
+static const struct of_device_id gpio_cascade_id[] = {
+ { .compatible = "gpio-cascade" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, gpio_cascade_id);
+
+static struct platform_driver gpio_cascade_driver = {
+ .driver = {
+ .name = "gpio-cascade",
+ .of_match_table = gpio_cascade_id,
+ },
+ .probe = gpio_cascade_probe,
+};
+module_platform_driver(gpio_cascade_driver);
+
+MODULE_AUTHOR("Mauri Sandberg <maukka@ext.kapsi.fi>");
+MODULE_DESCRIPTION("Generic GPIO cascade");
+MODULE_LICENSE("GPL");

View File

@ -0,0 +1,59 @@
From 078c6a1cbd4cd7496048786beec2e312577bebbf Mon Sep 17 00:00:00 2001
From: Pawel Dembicki <paweldembicki@gmail.com>
Date: Tue, 11 Jan 2022 23:11:32 +0100
Subject: [PATCH] net: qmi_wwan: add ZTE MF286D modem 19d2:1485
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Modem from ZTE MF286D is an Qualcomm MDM9250 based 3G/4G modem.
T: Bus=02 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 3 Spd=5000 MxCh= 0
D: Ver= 3.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 9 #Cfgs= 1
P: Vendor=19d2 ProdID=1485 Rev=52.87
S: Manufacturer=ZTE,Incorporated
S: Product=ZTE Technologies MSM
S: SerialNumber=MF286DZTED000000
C:* #Ifs= 7 Cfg#= 1 Atr=80 MxPwr=896mA
A: FirstIf#= 0 IfCount= 2 Cls=02(comm.) Sub=06 Prot=00
I:* If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=02 Prot=ff Driver=rndis_host
E: Ad=82(I) Atr=03(Int.) MxPS= 8 Ivl=32ms
I:* If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=rndis_host
E: Ad=81(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
E: Ad=01(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
I:* If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=option
E: Ad=83(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
E: Ad=02(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
I:* If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=option
E: Ad=85(I) Atr=03(Int.) MxPS= 10 Ivl=32ms
E: Ad=84(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
E: Ad=03(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
I:* If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=option
E: Ad=87(I) Atr=03(Int.) MxPS= 10 Ivl=32ms
E: Ad=86(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
E: Ad=04(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
I:* If#= 5 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=qmi_wwan
E: Ad=88(I) Atr=03(Int.) MxPS= 8 Ivl=32ms
E: Ad=8e(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
E: Ad=0f(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
I:* If#= 6 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=usbfs
E: Ad=05(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
E: Ad=89(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com>
Acked-by: Bjørn Mork <bjorn@mork.no>
Signed-off-by: David S. Miller <davem@davemloft.net>
---
drivers/net/usb/qmi_wwan.c | 1 +
1 file changed, 1 insertion(+)
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -1313,6 +1313,7 @@ static const struct usb_device_id produc
{QMI_FIXED_INTF(0x19d2, 0x1426, 2)}, /* ZTE MF91 */
{QMI_FIXED_INTF(0x19d2, 0x1428, 2)}, /* Telewell TW-LTE 4G v2 */
{QMI_FIXED_INTF(0x19d2, 0x1432, 3)}, /* ZTE ME3620 */
+ {QMI_FIXED_INTF(0x19d2, 0x1485, 5)}, /* ZTE MF286D */
{QMI_FIXED_INTF(0x19d2, 0x2002, 4)}, /* ZTE (Vodafone) K3765-Z */
{QMI_FIXED_INTF(0x2001, 0x7e16, 3)}, /* D-Link DWM-221 */
{QMI_FIXED_INTF(0x2001, 0x7e19, 4)}, /* D-Link DWM-221 B1 */

View File

@ -0,0 +1,40 @@
From 43f3f187e6f62ca40802afe39495c8a3e20b4bfa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
Date: Mon, 10 Jan 2022 01:50:50 +0100
Subject: [PATCH] PCI: aardvark: Replace custom PCIE_CORE_INT_* macros with
PCI_INTERRUPT_*
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Header file linux/pci.h defines enum pci_interrupt_pin with corresponding
PCI_INTERRUPT_* values.
Signed-off-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Marek Behún <kabel@kernel.org>
---
drivers/pci/controller/pci-aardvark.c | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -38,10 +38,6 @@
#define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN BIT(6)
#define PCIE_CORE_ERR_CAPCTL_ECRC_CHCK BIT(7)
#define PCIE_CORE_ERR_CAPCTL_ECRC_CHCK_RCV BIT(8)
-#define PCIE_CORE_INT_A_ASSERT_ENABLE 1
-#define PCIE_CORE_INT_B_ASSERT_ENABLE 2
-#define PCIE_CORE_INT_C_ASSERT_ENABLE 3
-#define PCIE_CORE_INT_D_ASSERT_ENABLE 4
/* PIO registers base address and register offsets */
#define PIO_BASE_ADDR 0x4000
#define PIO_CTRL (PIO_BASE_ADDR + 0x0)
@@ -959,7 +955,7 @@ static int advk_sw_pci_bridge_init(struc
bridge->conf.pref_mem_limit = cpu_to_le16(PCI_PREF_RANGE_TYPE_64);
/* Support interrupt A for MSI feature */
- bridge->conf.intpin = PCIE_CORE_INT_A_ASSERT_ENABLE;
+ bridge->conf.intpin = PCI_INTERRUPT_INTA;
/* Aardvark HW provides PCIe Capability structure in version 2 */
bridge->pcie_conf.cap = cpu_to_le16(2);

View File

@ -0,0 +1,57 @@
From a29a7d01cd778854e08108461cba321a63d98871 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
Date: Fri, 2 Jul 2021 16:39:47 +0200
Subject: [PATCH] PCI: aardvark: Fix reading MSI interrupt number
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
In advk_pcie_handle_msi() the authors expect that when bit i in the W1C
register PCIE_MSI_STATUS_REG is cleared, the PCIE_MSI_PAYLOAD_REG is
updated to contain the MSI number corresponding to index i.
Experiments show that this is not so, and instead PCIE_MSI_PAYLOAD_REG
always contains the number of the last received MSI, overall.
Do not read PCIE_MSI_PAYLOAD_REG register for determining MSI interrupt
number. Since Aardvark already forbids more than 32 interrupts and uses
own allocated hwirq numbers, the msi_idx already corresponds to the
received MSI number.
Fixes: 8c39d710363c ("PCI: aardvark: Add Aardvark PCI host controller driver")
Signed-off-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Marek Behún <kabel@kernel.org>
---
drivers/pci/controller/pci-aardvark.c | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -1386,7 +1386,7 @@ static void advk_pcie_remove_irq_domain(
static void advk_pcie_handle_msi(struct advk_pcie *pcie)
{
u32 msi_val, msi_mask, msi_status, msi_idx;
- u16 msi_data;
+ int virq;
msi_mask = advk_readl(pcie, PCIE_MSI_MASK_REG);
msi_val = advk_readl(pcie, PCIE_MSI_STATUS_REG);
@@ -1396,13 +1396,12 @@ static void advk_pcie_handle_msi(struct
if (!(BIT(msi_idx) & msi_status))
continue;
- /*
- * msi_idx contains bits [4:0] of the msi_data and msi_data
- * contains 16bit MSI interrupt number
- */
advk_writel(pcie, BIT(msi_idx), PCIE_MSI_STATUS_REG);
- msi_data = advk_readl(pcie, PCIE_MSI_PAYLOAD_REG) & PCIE_MSI_DATA_MASK;
- generic_handle_irq(msi_data);
+ virq = irq_find_mapping(pcie->msi_inner_domain, msi_idx);
+ if (virq)
+ generic_handle_irq(virq);
+ else
+ dev_err_ratelimited(&pcie->pdev->dev, "unexpected MSI 0x%02x\n", msi_idx);
}
advk_writel(pcie, PCIE_ISR0_MSI_INT_PENDING,

View File

@ -0,0 +1,72 @@
From bb03b126ea6c9e57177b537dd022246fa5dbef16 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
Date: Fri, 12 Feb 2021 16:24:07 +0100
Subject: [PATCH] PCI: aardvark: Fix support for MSI interrupts
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Aardvark hardware supports Multi-MSI and MSI_FLAG_MULTI_PCI_MSI is already
set for the MSI chip. But when allocating MSI interrupt numbers for
Multi-MSI, the numbers need to be properly aligned, otherwise endpoint
devices send MSI interrupt with incorrect numbers.
Fix this issue by using function bitmap_find_free_region() instead of
bitmap_find_next_zero_area().
To ensure that aligned MSI interrupt numbers are used by endpoint devices,
we cannot use Linux virtual irq numbers (as they are random and not
properly aligned). Instead we need to use the aligned hwirq numbers.
This change fixes receiving MSI interrupts on Armada 3720 boards and
allows using NVMe disks which use Multi-MSI feature with 3 interrupts.
Without this NVMe disks freeze booting as linux nvme-core.c is waiting
60s for an interrupt.
Signed-off-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Marek Behún <kabel@kernel.org>
---
drivers/pci/controller/pci-aardvark.c | 16 ++++++----------
1 file changed, 6 insertions(+), 10 deletions(-)
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -1182,7 +1182,7 @@ static void advk_msi_irq_compose_msi_msg
msg->address_lo = lower_32_bits(msi_msg);
msg->address_hi = upper_32_bits(msi_msg);
- msg->data = data->irq;
+ msg->data = data->hwirq;
}
static int advk_msi_set_affinity(struct irq_data *irq_data,
@@ -1199,15 +1199,11 @@ static int advk_msi_irq_domain_alloc(str
int hwirq, i;
mutex_lock(&pcie->msi_used_lock);
- hwirq = bitmap_find_next_zero_area(pcie->msi_used, MSI_IRQ_NUM,
- 0, nr_irqs, 0);
- if (hwirq >= MSI_IRQ_NUM) {
- mutex_unlock(&pcie->msi_used_lock);
- return -ENOSPC;
- }
-
- bitmap_set(pcie->msi_used, hwirq, nr_irqs);
+ hwirq = bitmap_find_free_region(pcie->msi_used, MSI_IRQ_NUM,
+ order_base_2(nr_irqs));
mutex_unlock(&pcie->msi_used_lock);
+ if (hwirq < 0)
+ return -ENOSPC;
for (i = 0; i < nr_irqs; i++)
irq_domain_set_info(domain, virq + i, hwirq + i,
@@ -1225,7 +1221,7 @@ static void advk_msi_irq_domain_free(str
struct advk_pcie *pcie = domain->host_data;
mutex_lock(&pcie->msi_used_lock);
- bitmap_clear(pcie->msi_used, d->hwirq, nr_irqs);
+ bitmap_release_region(pcie->msi_used, d->hwirq, order_base_2(nr_irqs));
mutex_unlock(&pcie->msi_used_lock);
}

View File

@ -0,0 +1,125 @@
From 0cd5141d1866afb23286fe90cd846441fe7aeb39 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
Date: Sat, 27 Mar 2021 14:44:11 +0100
Subject: [PATCH] PCI: aardvark: Rewrite IRQ code to chained IRQ handler
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Rewrite the code to use irq_set_chained_handler_and_data() handler with
chained_irq_enter() and chained_irq_exit() processing instead of using
devm_request_irq().
advk_pcie_irq_handler() reads IRQ status bits and calls other functions
based on which bits are set. These functions then read its own IRQ status
bits and calls other aardvark functions based on these bits. Finally
generic_handle_domain_irq() with translated linux IRQ numbers are called.
Signed-off-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Marek Behún <kabel@kernel.org>
---
drivers/pci/controller/pci-aardvark.c | 48 +++++++++++++++------------
1 file changed, 26 insertions(+), 22 deletions(-)
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -268,6 +268,7 @@ struct advk_pcie {
u32 actions;
} wins[OB_WIN_COUNT];
u8 wins_count;
+ int irq;
struct irq_domain *irq_domain;
struct irq_chip irq_chip;
raw_spinlock_t irq_lock;
@@ -1434,21 +1435,26 @@ static void advk_pcie_handle_int(struct
}
}
-static irqreturn_t advk_pcie_irq_handler(int irq, void *arg)
+static void advk_pcie_irq_handler(struct irq_desc *desc)
{
- struct advk_pcie *pcie = arg;
- u32 status;
+ struct advk_pcie *pcie = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ u32 val, mask, status;
- status = advk_readl(pcie, HOST_CTRL_INT_STATUS_REG);
- if (!(status & PCIE_IRQ_CORE_INT))
- return IRQ_NONE;
+ chained_irq_enter(chip, desc);
- advk_pcie_handle_int(pcie);
+ val = advk_readl(pcie, HOST_CTRL_INT_STATUS_REG);
+ mask = advk_readl(pcie, HOST_CTRL_INT_MASK_REG);
+ status = val & ((~mask) & PCIE_IRQ_ALL_MASK);
- /* Clear interrupt */
- advk_writel(pcie, PCIE_IRQ_CORE_INT, HOST_CTRL_INT_STATUS_REG);
+ if (status & PCIE_IRQ_CORE_INT) {
+ advk_pcie_handle_int(pcie);
- return IRQ_HANDLED;
+ /* Clear interrupt */
+ advk_writel(pcie, PCIE_IRQ_CORE_INT, HOST_CTRL_INT_STATUS_REG);
+ }
+
+ chained_irq_exit(chip, desc);
}
static void __maybe_unused advk_pcie_disable_phy(struct advk_pcie *pcie)
@@ -1515,7 +1521,7 @@ static int advk_pcie_probe(struct platfo
struct advk_pcie *pcie;
struct pci_host_bridge *bridge;
struct resource_entry *entry;
- int ret, irq;
+ int ret;
bridge = devm_pci_alloc_host_bridge(dev, sizeof(struct advk_pcie));
if (!bridge)
@@ -1601,17 +1607,9 @@ static int advk_pcie_probe(struct platfo
if (IS_ERR(pcie->base))
return PTR_ERR(pcie->base);
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
- return irq;
-
- ret = devm_request_irq(dev, irq, advk_pcie_irq_handler,
- IRQF_SHARED | IRQF_NO_THREAD, "advk-pcie",
- pcie);
- if (ret) {
- dev_err(dev, "Failed to register interrupt\n");
- return ret;
- }
+ pcie->irq = platform_get_irq(pdev, 0);
+ if (pcie->irq < 0)
+ return pcie->irq;
pcie->reset_gpio = devm_gpiod_get_from_of_node(dev, dev->of_node,
"reset-gpios", 0,
@@ -1660,11 +1658,14 @@ static int advk_pcie_probe(struct platfo
return ret;
}
+ irq_set_chained_handler_and_data(pcie->irq, advk_pcie_irq_handler, pcie);
+
bridge->sysdata = pcie;
bridge->ops = &advk_pcie_ops;
ret = pci_host_probe(bridge);
if (ret < 0) {
+ irq_set_chained_handler_and_data(pcie->irq, NULL, NULL);
advk_pcie_remove_msi_irq_domain(pcie);
advk_pcie_remove_irq_domain(pcie);
return ret;
@@ -1712,6 +1713,9 @@ static int advk_pcie_remove(struct platf
advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_REG);
advk_writel(pcie, PCIE_IRQ_ALL_MASK, HOST_CTRL_INT_STATUS_REG);
+ /* Remove IRQ handler */
+ irq_set_chained_handler_and_data(pcie->irq, NULL, NULL);
+
/* Remove IRQ domains */
advk_pcie_remove_msi_irq_domain(pcie);
advk_pcie_remove_irq_domain(pcie);

View File

@ -0,0 +1,31 @@
From 69c1f2c6f45a556361fd8e8d2d4eb20e2c8d3d95 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
Date: Thu, 18 Mar 2021 17:04:32 +0100
Subject: [PATCH] PCI: aardvark: Check return value of
generic_handle_domain_irq() when processing INTx IRQ
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
It is possible that we receive spurious INTx interrupt. Check for the
return value of generic_handle_domain_irq() when processing INTx IRQ.
Signed-off-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Marek Behún <kabel@kernel.org>
---
drivers/pci/controller/pci-aardvark.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -1431,7 +1431,9 @@ static void advk_pcie_handle_int(struct
advk_writel(pcie, PCIE_ISR1_INTX_ASSERT(i),
PCIE_ISR1_REG);
- generic_handle_domain_irq(pcie->irq_domain, i);
+ if (generic_handle_domain_irq(pcie->irq_domain, i) == -EINVAL)
+ dev_err_ratelimited(&pcie->pdev->dev, "unexpected INT%c IRQ\n",
+ (char)i + 'A');
}
}

View File

@ -0,0 +1,93 @@
From 5eb36a6b9508da442aac80f4df23e3951bbfa7aa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
Date: Mon, 10 Jan 2022 00:03:41 +0100
Subject: [PATCH] PCI: aardvark: Make MSI irq_chip structures static driver
structures
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Marc Zyngier says [1] that we should use struct irq_chip as a global
static struct in the driver. Even though the structure currently
contains a dynamic member (parent_device), Marc says [2] that he plans
to kill it and make the structure completely static.
Convert Aardvark's priv->msi_bottom_irq_chip and priv->msi_irq_chip to
static driver structure.
[1] https://lore.kernel.org/linux-pci/877dbcvngf.wl-maz@kernel.org/
[2] https://lore.kernel.org/linux-pci/874k6gvkhz.wl-maz@kernel.org/
Signed-off-by: Marek Behún <kabel@kernel.org>
---
drivers/pci/controller/pci-aardvark.c | 26 ++++++++++++--------------
1 file changed, 12 insertions(+), 14 deletions(-)
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -274,8 +274,6 @@ struct advk_pcie {
raw_spinlock_t irq_lock;
struct irq_domain *msi_domain;
struct irq_domain *msi_inner_domain;
- struct irq_chip msi_bottom_irq_chip;
- struct irq_chip msi_irq_chip;
struct msi_domain_info msi_domain_info;
DECLARE_BITMAP(msi_used, MSI_IRQ_NUM);
struct mutex msi_used_lock;
@@ -1192,6 +1190,12 @@ static int advk_msi_set_affinity(struct
return -EINVAL;
}
+static struct irq_chip advk_msi_bottom_irq_chip = {
+ .name = "MSI",
+ .irq_compose_msi_msg = advk_msi_irq_compose_msi_msg,
+ .irq_set_affinity = advk_msi_set_affinity,
+};
+
static int advk_msi_irq_domain_alloc(struct irq_domain *domain,
unsigned int virq,
unsigned int nr_irqs, void *args)
@@ -1208,7 +1212,7 @@ static int advk_msi_irq_domain_alloc(str
for (i = 0; i < nr_irqs; i++)
irq_domain_set_info(domain, virq + i, hwirq + i,
- &pcie->msi_bottom_irq_chip,
+ &advk_msi_bottom_irq_chip,
domain->host_data, handle_simple_irq,
NULL, NULL);
@@ -1278,29 +1282,23 @@ static const struct irq_domain_ops advk_
.xlate = irq_domain_xlate_onecell,
};
+static struct irq_chip advk_msi_irq_chip = {
+ .name = "advk-MSI",
+};
+
static int advk_pcie_init_msi_irq_domain(struct advk_pcie *pcie)
{
struct device *dev = &pcie->pdev->dev;
struct device_node *node = dev->of_node;
- struct irq_chip *bottom_ic, *msi_ic;
struct msi_domain_info *msi_di;
phys_addr_t msi_msg_phys;
mutex_init(&pcie->msi_used_lock);
- bottom_ic = &pcie->msi_bottom_irq_chip;
-
- bottom_ic->name = "MSI";
- bottom_ic->irq_compose_msi_msg = advk_msi_irq_compose_msi_msg;
- bottom_ic->irq_set_affinity = advk_msi_set_affinity;
-
- msi_ic = &pcie->msi_irq_chip;
- msi_ic->name = "advk-MSI";
-
msi_di = &pcie->msi_domain_info;
msi_di->flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
MSI_FLAG_MULTI_PCI_MSI;
- msi_di->chip = msi_ic;
+ msi_di->chip = &advk_msi_irq_chip;
msi_msg_phys = virt_to_phys(&pcie->msi_msg);

View File

@ -0,0 +1,64 @@
From c092ab8994f1f777054c0179a9deb40b87ee606f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
Date: Mon, 10 Jan 2022 00:10:46 +0100
Subject: [PATCH] PCI: aardvark: Make msi_domain_info structure a static driver
structure
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Make Aardvark's msi_domain_info structure into a private driver structure.
Domain info is same for every potential instatination of a controller.
Signed-off-by: Marek Behún <kabel@kernel.org>
---
drivers/pci/controller/pci-aardvark.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -274,7 +274,6 @@ struct advk_pcie {
raw_spinlock_t irq_lock;
struct irq_domain *msi_domain;
struct irq_domain *msi_inner_domain;
- struct msi_domain_info msi_domain_info;
DECLARE_BITMAP(msi_used, MSI_IRQ_NUM);
struct mutex msi_used_lock;
u16 msi_msg;
@@ -1286,20 +1285,20 @@ static struct irq_chip advk_msi_irq_chip
.name = "advk-MSI",
};
+static struct msi_domain_info advk_msi_domain_info = {
+ .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+ MSI_FLAG_MULTI_PCI_MSI,
+ .chip = &advk_msi_irq_chip,
+};
+
static int advk_pcie_init_msi_irq_domain(struct advk_pcie *pcie)
{
struct device *dev = &pcie->pdev->dev;
struct device_node *node = dev->of_node;
- struct msi_domain_info *msi_di;
phys_addr_t msi_msg_phys;
mutex_init(&pcie->msi_used_lock);
- msi_di = &pcie->msi_domain_info;
- msi_di->flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
- MSI_FLAG_MULTI_PCI_MSI;
- msi_di->chip = &advk_msi_irq_chip;
-
msi_msg_phys = virt_to_phys(&pcie->msi_msg);
advk_writel(pcie, lower_32_bits(msi_msg_phys),
@@ -1315,7 +1314,8 @@ static int advk_pcie_init_msi_irq_domain
pcie->msi_domain =
pci_msi_create_irq_domain(of_node_to_fwnode(node),
- msi_di, pcie->msi_inner_domain);
+ &advk_msi_domain_info,
+ pcie->msi_inner_domain);
if (!pcie->msi_domain) {
irq_domain_remove(pcie->msi_inner_domain);
return -ENOMEM;

View File

@ -0,0 +1,40 @@
From 59029739d42b439628e2f64f3d8f2db9be97deff Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
Date: Mon, 10 Jan 2022 00:15:17 +0100
Subject: [PATCH] PCI: aardvark: Use dev_fwnode() instead of
of_node_to_fwnode(dev->of_node)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Use simple
dev_fwnode(dev)
instead of
struct device_node *node = dev->of_node;
of_node_to_fwnode(node)
especially since the node variable is not used elsewhere in the function.
Signed-off-by: Marek Behún <kabel@kernel.org>
---
drivers/pci/controller/pci-aardvark.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -1294,7 +1294,6 @@ static struct msi_domain_info advk_msi_d
static int advk_pcie_init_msi_irq_domain(struct advk_pcie *pcie)
{
struct device *dev = &pcie->pdev->dev;
- struct device_node *node = dev->of_node;
phys_addr_t msi_msg_phys;
mutex_init(&pcie->msi_used_lock);
@@ -1313,7 +1312,7 @@ static int advk_pcie_init_msi_irq_domain
return -ENOMEM;
pcie->msi_domain =
- pci_msi_create_irq_domain(of_node_to_fwnode(node),
+ pci_msi_create_irq_domain(dev_fwnode(dev),
&advk_msi_domain_info,
pcie->msi_inner_domain);
if (!pcie->msi_domain) {

View File

@ -0,0 +1,44 @@
From 98feaf97bc64fc640a6c5b1394cd18fc7cd7dac8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
Date: Sun, 28 Mar 2021 14:34:49 +0200
Subject: [PATCH] PCI: aardvark: Refactor unmasking summary MSI interrupt
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Refactor the masking of ISR0/1 Sources and unmasking of summary MSI interrupt
so that it corresponds to the comments:
- first mask all ISR0/1
- then unmask all MSIs
- then unmask summary MSI interrupt
Signed-off-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Marek Behún <kabel@kernel.org>
---
drivers/pci/controller/pci-aardvark.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -571,15 +571,17 @@ static void advk_pcie_setup_hw(struct ad
advk_writel(pcie, PCIE_IRQ_ALL_MASK, HOST_CTRL_INT_STATUS_REG);
/* Disable All ISR0/1 Sources */
- reg = PCIE_ISR0_ALL_MASK;
- reg &= ~PCIE_ISR0_MSI_INT_PENDING;
- advk_writel(pcie, reg, PCIE_ISR0_MASK_REG);
-
+ advk_writel(pcie, PCIE_ISR0_ALL_MASK, PCIE_ISR0_MASK_REG);
advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_MASK_REG);
/* Unmask all MSIs */
advk_writel(pcie, ~(u32)PCIE_MSI_ALL_MASK, PCIE_MSI_MASK_REG);
+ /* Unmask summary MSI interrupt */
+ reg = advk_readl(pcie, PCIE_ISR0_MASK_REG);
+ reg &= ~PCIE_ISR0_MSI_INT_PENDING;
+ advk_writel(pcie, reg, PCIE_ISR0_MASK_REG);
+
/* Enable summary interrupt for GIC SPI source */
reg = PCIE_IRQ_ALL_MASK & (~PCIE_IRQ_ENABLE_INTS_MASK);
advk_writel(pcie, reg, HOST_CTRL_INT_MASK_REG);

View File

@ -0,0 +1,117 @@
From 7f353accca6e4a3222991c65b1a6801503973bd3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
Date: Fri, 2 Jul 2021 16:44:10 +0200
Subject: [PATCH] PCI: aardvark: Add support for masking MSI interrupts
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
We should not unmask MSIs at setup, but only when kernel asks for them
to be unmasked.
At setup, mask all MSIs, and implement IRQ chip callbacks for masking
and unmasking particular MSIs.
Signed-off-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Marek Behún <kabel@kernel.org>
---
drivers/pci/controller/pci-aardvark.c | 54 ++++++++++++++++++++++++---
1 file changed, 49 insertions(+), 5 deletions(-)
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -274,6 +274,7 @@ struct advk_pcie {
raw_spinlock_t irq_lock;
struct irq_domain *msi_domain;
struct irq_domain *msi_inner_domain;
+ raw_spinlock_t msi_irq_lock;
DECLARE_BITMAP(msi_used, MSI_IRQ_NUM);
struct mutex msi_used_lock;
u16 msi_msg;
@@ -570,12 +571,10 @@ static void advk_pcie_setup_hw(struct ad
advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_REG);
advk_writel(pcie, PCIE_IRQ_ALL_MASK, HOST_CTRL_INT_STATUS_REG);
- /* Disable All ISR0/1 Sources */
+ /* Disable All ISR0/1 and MSI Sources */
advk_writel(pcie, PCIE_ISR0_ALL_MASK, PCIE_ISR0_MASK_REG);
advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_MASK_REG);
-
- /* Unmask all MSIs */
- advk_writel(pcie, ~(u32)PCIE_MSI_ALL_MASK, PCIE_MSI_MASK_REG);
+ advk_writel(pcie, PCIE_MSI_ALL_MASK, PCIE_MSI_MASK_REG);
/* Unmask summary MSI interrupt */
reg = advk_readl(pcie, PCIE_ISR0_MASK_REG);
@@ -1191,10 +1190,52 @@ static int advk_msi_set_affinity(struct
return -EINVAL;
}
+static void advk_msi_irq_mask(struct irq_data *d)
+{
+ struct advk_pcie *pcie = d->domain->host_data;
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+ unsigned long flags;
+ u32 mask;
+
+ raw_spin_lock_irqsave(&pcie->msi_irq_lock, flags);
+ mask = advk_readl(pcie, PCIE_MSI_MASK_REG);
+ mask |= BIT(hwirq);
+ advk_writel(pcie, mask, PCIE_MSI_MASK_REG);
+ raw_spin_unlock_irqrestore(&pcie->msi_irq_lock, flags);
+}
+
+static void advk_msi_irq_unmask(struct irq_data *d)
+{
+ struct advk_pcie *pcie = d->domain->host_data;
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+ unsigned long flags;
+ u32 mask;
+
+ raw_spin_lock_irqsave(&pcie->msi_irq_lock, flags);
+ mask = advk_readl(pcie, PCIE_MSI_MASK_REG);
+ mask &= ~BIT(hwirq);
+ advk_writel(pcie, mask, PCIE_MSI_MASK_REG);
+ raw_spin_unlock_irqrestore(&pcie->msi_irq_lock, flags);
+}
+
+static void advk_msi_top_irq_mask(struct irq_data *d)
+{
+ pci_msi_mask_irq(d);
+ irq_chip_mask_parent(d);
+}
+
+static void advk_msi_top_irq_unmask(struct irq_data *d)
+{
+ pci_msi_unmask_irq(d);
+ irq_chip_unmask_parent(d);
+}
+
static struct irq_chip advk_msi_bottom_irq_chip = {
.name = "MSI",
.irq_compose_msi_msg = advk_msi_irq_compose_msi_msg,
.irq_set_affinity = advk_msi_set_affinity,
+ .irq_mask = advk_msi_irq_mask,
+ .irq_unmask = advk_msi_irq_unmask,
};
static int advk_msi_irq_domain_alloc(struct irq_domain *domain,
@@ -1284,7 +1325,9 @@ static const struct irq_domain_ops advk_
};
static struct irq_chip advk_msi_irq_chip = {
- .name = "advk-MSI",
+ .name = "advk-MSI",
+ .irq_mask = advk_msi_top_irq_mask,
+ .irq_unmask = advk_msi_top_irq_unmask,
};
static struct msi_domain_info advk_msi_domain_info = {
@@ -1298,6 +1341,7 @@ static int advk_pcie_init_msi_irq_domain
struct device *dev = &pcie->pdev->dev;
phys_addr_t msi_msg_phys;
+ raw_spin_lock_init(&pcie->msi_irq_lock);
mutex_init(&pcie->msi_used_lock);
msi_msg_phys = virt_to_phys(&pcie->msi_msg);

View File

@ -0,0 +1,91 @@
From fa73c200f181436eab859374657c53a73778d8ad Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
Date: Fri, 26 Mar 2021 17:35:44 +0100
Subject: [PATCH] PCI: aardvark: Fix setting MSI address
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
MSI address for receiving MSI interrupts needs to be correctly set before
enabling processing of MSI interrupts.
Move code for setting PCIE_MSI_ADDR_LOW_REG and PCIE_MSI_ADDR_HIGH_REG
from advk_pcie_init_msi_irq_domain() to advk_pcie_setup_hw(), before
enabling PCIE_CORE_CTRL2_MSI_ENABLE.
After this we can remove the now unused member msi_msg, which was used
only for MSI doorbell address. MSI address can be any address which cannot
be used to DMA to. So change it to the address of the main struct advk_pcie.
Fixes: 8c39d710363c ("PCI: aardvark: Add Aardvark PCI host controller driver")
Signed-off-by: Pali Rohár <pali@kernel.org>
Acked-by: Marc Zyngier <maz@kernel.org>
Signed-off-by: Marek Behún <kabel@kernel.org>
Cc: stable@vger.kernel.org # f21a8b1b6837 ("PCI: aardvark: Move to MSI handling using generic MSI support")
---
drivers/pci/controller/pci-aardvark.c | 21 +++++++++------------
1 file changed, 9 insertions(+), 12 deletions(-)
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -277,7 +277,6 @@ struct advk_pcie {
raw_spinlock_t msi_irq_lock;
DECLARE_BITMAP(msi_used, MSI_IRQ_NUM);
struct mutex msi_used_lock;
- u16 msi_msg;
int link_gen;
struct pci_bridge_emul bridge;
struct gpio_desc *reset_gpio;
@@ -472,6 +471,7 @@ static void advk_pcie_disable_ob_win(str
static void advk_pcie_setup_hw(struct advk_pcie *pcie)
{
+ phys_addr_t msi_addr;
u32 reg;
int i;
@@ -560,6 +560,11 @@ static void advk_pcie_setup_hw(struct ad
reg |= LANE_COUNT_1;
advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG);
+ /* Set MSI address */
+ msi_addr = virt_to_phys(pcie);
+ advk_writel(pcie, lower_32_bits(msi_addr), PCIE_MSI_ADDR_LOW_REG);
+ advk_writel(pcie, upper_32_bits(msi_addr), PCIE_MSI_ADDR_HIGH_REG);
+
/* Enable MSI */
reg = advk_readl(pcie, PCIE_CORE_CTRL2_REG);
reg |= PCIE_CORE_CTRL2_MSI_ENABLE;
@@ -1177,10 +1182,10 @@ static void advk_msi_irq_compose_msi_msg
struct msi_msg *msg)
{
struct advk_pcie *pcie = irq_data_get_irq_chip_data(data);
- phys_addr_t msi_msg = virt_to_phys(&pcie->msi_msg);
+ phys_addr_t msi_addr = virt_to_phys(pcie);
- msg->address_lo = lower_32_bits(msi_msg);
- msg->address_hi = upper_32_bits(msi_msg);
+ msg->address_lo = lower_32_bits(msi_addr);
+ msg->address_hi = upper_32_bits(msi_addr);
msg->data = data->hwirq;
}
@@ -1339,18 +1344,10 @@ static struct msi_domain_info advk_msi_d
static int advk_pcie_init_msi_irq_domain(struct advk_pcie *pcie)
{
struct device *dev = &pcie->pdev->dev;
- phys_addr_t msi_msg_phys;
raw_spin_lock_init(&pcie->msi_irq_lock);
mutex_init(&pcie->msi_used_lock);
- msi_msg_phys = virt_to_phys(&pcie->msi_msg);
-
- advk_writel(pcie, lower_32_bits(msi_msg_phys),
- PCIE_MSI_ADDR_LOW_REG);
- advk_writel(pcie, upper_32_bits(msi_msg_phys),
- PCIE_MSI_ADDR_HIGH_REG);
-
pcie->msi_inner_domain =
irq_domain_add_linear(NULL, MSI_IRQ_NUM,
&advk_msi_domain_ops, pcie);

View File

@ -0,0 +1,38 @@
From 735a4ac9782b96fbe1543c578aa8334364f21abd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
Date: Fri, 2 Apr 2021 14:05:24 +0200
Subject: [PATCH] PCI: aardvark: Enable MSI-X support
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
According to PCI 3.0 specification, sending both MSI and MSI-X interrupts
is done by DWORD memory write operation to doorbell message address. The
write operation for MSI has zero upper 16 bits and the MSI interrupt number
in the lower 16 bits, while the write operation for MSI-X contains a 32-bit
value from MSI-X table.
Since the driver only uses interrupt numbers from range 0..31, the upper
16 bits of the DWORD memory write operation to doorbell message address
are zero even for MSI-X interrupts. Thus we can enable MSI-X interrupts.
Testing proves that kernel can correctly receive MSI-X interrupts from PCIe
cards which supports both MSI and MSI-X interrupts.
Signed-off-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Marek Behún <kabel@kernel.org>
---
drivers/pci/controller/pci-aardvark.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -1337,7 +1337,7 @@ static struct irq_chip advk_msi_irq_chip
static struct msi_domain_info advk_msi_domain_info = {
.flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
- MSI_FLAG_MULTI_PCI_MSI,
+ MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX,
.chip = &advk_msi_irq_chip,
};

View File

@ -0,0 +1,100 @@
From 7f3e55a3890fa26d15e2e4e90213962d1a7f6df9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
Date: Fri, 12 Feb 2021 20:32:55 +0100
Subject: [PATCH] PCI: aardvark: Add support for ERR interrupt on emulated
bridge
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
ERR interrupt is triggered when corresponding bit is unmasked in both ISR0
and PCI_EXP_DEVCTL registers. Unmasking ERR bits in PCI_EXP_DEVCTL register
is not enough. This means that currently the ERR interrupt is never
triggered.
Unmask ERR bits in ISR0 register at driver probe time. ERR interrupt is not
triggered until ERR bits are unmasked also in PCI_EXP_DEVCTL register,
which is done by AER driver. So it is safe to unconditionally unmask all
ERR bits in aardvark probe.
Aardvark HW sets PCI_ERR_ROOT_AER_IRQ to zero and when corresponding bits
in ISR0 and PCI_EXP_DEVCTL are enabled, the HW triggers a generic interrupt
on GIC. Chain this interrupt to PCIe interrupt 0 with
generic_handle_domain_irq() to allow processing of ERR interrupts.
Signed-off-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Marek Behún <kabel@kernel.org>
---
drivers/pci/controller/pci-aardvark.c | 36 ++++++++++++++++++++++++++-
1 file changed, 35 insertions(+), 1 deletion(-)
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -98,6 +98,10 @@
#define PCIE_MSG_PM_PME_MASK BIT(7)
#define PCIE_ISR0_MASK_REG (CONTROL_BASE_ADDR + 0x44)
#define PCIE_ISR0_MSI_INT_PENDING BIT(24)
+#define PCIE_ISR0_CORR_ERR BIT(11)
+#define PCIE_ISR0_NFAT_ERR BIT(12)
+#define PCIE_ISR0_FAT_ERR BIT(13)
+#define PCIE_ISR0_ERR_MASK GENMASK(13, 11)
#define PCIE_ISR0_INTX_ASSERT(val) BIT(16 + (val))
#define PCIE_ISR0_INTX_DEASSERT(val) BIT(20 + (val))
#define PCIE_ISR0_ALL_MASK GENMASK(31, 0)
@@ -778,11 +782,15 @@ advk_pci_bridge_emul_base_conf_read(stru
case PCI_INTERRUPT_LINE: {
/*
* From the whole 32bit register we support reading from HW only
- * one bit: PCI_BRIDGE_CTL_BUS_RESET.
+ * two bits: PCI_BRIDGE_CTL_BUS_RESET and PCI_BRIDGE_CTL_SERR.
* Other bits are retrieved only from emulated config buffer.
*/
__le32 *cfgspace = (__le32 *)&bridge->conf;
u32 val = le32_to_cpu(cfgspace[PCI_INTERRUPT_LINE / 4]);
+ if (advk_readl(pcie, PCIE_ISR0_MASK_REG) & PCIE_ISR0_ERR_MASK)
+ val &= ~(PCI_BRIDGE_CTL_SERR << 16);
+ else
+ val |= PCI_BRIDGE_CTL_SERR << 16;
if (advk_readl(pcie, PCIE_CORE_CTRL1_REG) & HOT_RESET_GEN)
val |= PCI_BRIDGE_CTL_BUS_RESET << 16;
else
@@ -808,6 +816,19 @@ advk_pci_bridge_emul_base_conf_write(str
break;
case PCI_INTERRUPT_LINE:
+ /*
+ * According to Figure 6-3: Pseudo Logic Diagram for Error
+ * Message Controls in PCIe base specification, SERR# Enable bit
+ * in Bridge Control register enable receiving of ERR_* messages
+ */
+ if (mask & (PCI_BRIDGE_CTL_SERR << 16)) {
+ u32 val = advk_readl(pcie, PCIE_ISR0_MASK_REG);
+ if (new & (PCI_BRIDGE_CTL_SERR << 16))
+ val &= ~PCIE_ISR0_ERR_MASK;
+ else
+ val |= PCIE_ISR0_ERR_MASK;
+ advk_writel(pcie, val, PCIE_ISR0_MASK_REG);
+ }
if (mask & (PCI_BRIDGE_CTL_BUS_RESET << 16)) {
u32 val = advk_readl(pcie, PCIE_CORE_CTRL1_REG);
if (new & (PCI_BRIDGE_CTL_BUS_RESET << 16))
@@ -1459,6 +1480,19 @@ static void advk_pcie_handle_int(struct
isr1_mask = advk_readl(pcie, PCIE_ISR1_MASK_REG);
isr1_status = isr1_val & ((~isr1_mask) & PCIE_ISR1_ALL_MASK);
+ /* Process ERR interrupt */
+ if (isr0_status & PCIE_ISR0_ERR_MASK) {
+ advk_writel(pcie, PCIE_ISR0_ERR_MASK, PCIE_ISR0_REG);
+
+ /*
+ * Aardvark HW returns zero for PCI_ERR_ROOT_AER_IRQ, so use
+ * PCIe interrupt 0
+ */
+ virq = irq_find_mapping(pcie->irq_domain, 0);
+ if (generic_handle_irq(virq) == -EINVAL)
+ dev_err_ratelimited(&pcie->pdev->dev, "unhandled ERR IRQ\n");
+ }
+
/* Process MSI interrupts */
if (isr0_status & PCIE_ISR0_MSI_INT_PENDING)
advk_pcie_handle_msi(pcie);

View File

@ -0,0 +1,44 @@
From 5f354992eeef9a51c67796dc9f7f578d3584baa2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
Date: Wed, 8 Dec 2021 05:57:54 +0100
Subject: [PATCH] PCI: aardvark: Fix reading PCI_EXP_RTSTA_PME bit on emulated
bridge
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The emulated bridge returns incorrect value for PCI_EXP_RTSTA register
during readout in advk_pci_bridge_emul_pcie_conf_read() function: the
correct bit is BIT(16), but we are setting BIT(23), because the code
does
*value = (isr0 & PCIE_MSG_PM_PME_MASK) << 16
where
PCIE_MSG_PM_PME_MASK
is
BIT(7).
The code should probably have been something like
*value = (!!(isr0 & PCIE_MSG_PM_PME_MASK)) << 16,
but we are better of using an if() and using the proper macro for this
bit.
Fixes: 8a3ebd8de328 ("PCI: aardvark: Implement emulated root PCI bridge config space")
Signed-off-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Marek Behún <kabel@kernel.org>
---
drivers/pci/controller/pci-aardvark.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -867,7 +867,9 @@ advk_pci_bridge_emul_pcie_conf_read(stru
case PCI_EXP_RTSTA: {
u32 isr0 = advk_readl(pcie, PCIE_ISR0_REG);
u32 msglog = advk_readl(pcie, PCIE_MSG_LOG_REG);
- *value = (isr0 & PCIE_MSG_PM_PME_MASK) << 16 | (msglog >> 16);
+ *value = msglog >> 16;
+ if (isr0 & PCIE_MSG_PM_PME_MASK)
+ *value |= PCI_EXP_RTSTA_PME;
return PCI_BRIDGE_EMUL_HANDLED;
}

View File

@ -0,0 +1,52 @@
From 3fe0073d116d9902df08761c1cf0d733dd4c38fc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
Date: Wed, 8 Dec 2021 06:03:50 +0100
Subject: [PATCH] PCI: aardvark: Optimize writing PCI_EXP_RTCTL_PMEIE and
PCI_EXP_RTSTA_PME on emulated bridge
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
To optimize advk_pci_bridge_emul_pcie_conf_write() code, touch
PCIE_ISR0_REG and PCIE_ISR0_MASK_REG registers only when it is really
needed, when processing PCI_EXP_RTCTL_PMEIE and PCI_EXP_RTSTA_PME bits.
Signed-off-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Marek Behún <kabel@kernel.org>
---
drivers/pci/controller/pci-aardvark.c | 20 +++++++++++---------
1 file changed, 11 insertions(+), 9 deletions(-)
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -925,19 +925,21 @@ advk_pci_bridge_emul_pcie_conf_write(str
advk_pcie_wait_for_retrain(pcie);
break;
- case PCI_EXP_RTCTL: {
+ case PCI_EXP_RTCTL:
/* Only mask/unmask PME interrupt */
- u32 val = advk_readl(pcie, PCIE_ISR0_MASK_REG) &
- ~PCIE_MSG_PM_PME_MASK;
- if ((new & PCI_EXP_RTCTL_PMEIE) == 0)
- val |= PCIE_MSG_PM_PME_MASK;
- advk_writel(pcie, val, PCIE_ISR0_MASK_REG);
+ if (mask & PCI_EXP_RTCTL_PMEIE) {
+ u32 val = advk_readl(pcie, PCIE_ISR0_MASK_REG);
+ if (new & PCI_EXP_RTCTL_PMEIE)
+ val &= ~PCIE_MSG_PM_PME_MASK;
+ else
+ val |= PCIE_MSG_PM_PME_MASK;
+ advk_writel(pcie, val, PCIE_ISR0_MASK_REG);
+ }
break;
- }
case PCI_EXP_RTSTA:
- new = (new & PCI_EXP_RTSTA_PME) >> 9;
- advk_writel(pcie, new, PCIE_ISR0_REG);
+ if (new & PCI_EXP_RTSTA_PME)
+ advk_writel(pcie, PCIE_MSG_PM_PME_MASK, PCIE_ISR0_REG);
break;
case PCI_EXP_DEVCTL:

View File

@ -0,0 +1,47 @@
From 7acd8ef92e8789e10b5d736d73cea3b625087f26 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
Date: Wed, 8 Dec 2021 06:07:44 +0100
Subject: [PATCH] PCI: aardvark: Add support for PME interrupts
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Currently enabling PCI_EXP_RTSTA_PME bit in PCI_EXP_RTCTL register does
nothing. This is because PCIe PME driver expects to receive PCIe interrupt
defined in PCI_EXP_FLAGS_IRQ register, but aardvark hardware does not
trigger PCIe INTx/MSI interrupt for PME event, rather it triggers custom
aardvark interrupt which this driver is not processing yet.
Fix this issue by handling PME interrupt in advk_pcie_handle_int() and
chaining it to PCIe interrupt 0 with generic_handle_domain_irq() (since
aardvark sets PCI_EXP_FLAGS_IRQ to zero). With this change PCIe PME driver
finally starts receiving PME interrupt.
Signed-off-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Marek Behún <kabel@kernel.org>
---
drivers/pci/controller/pci-aardvark.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -1484,6 +1484,19 @@ static void advk_pcie_handle_int(struct
isr1_mask = advk_readl(pcie, PCIE_ISR1_MASK_REG);
isr1_status = isr1_val & ((~isr1_mask) & PCIE_ISR1_ALL_MASK);
+ /* Process PME interrupt */
+ if (isr0_status & PCIE_MSG_PM_PME_MASK) {
+ /*
+ * Do not clear PME interrupt bit in ISR0, it is cleared by IRQ
+ * receiver by writing to the PCI_EXP_RTSTA register of emulated
+ * root bridge. Aardvark HW returns zero for PCI_EXP_FLAGS_IRQ,
+ * so use PCIe interrupt 0.
+ */
+ virq = irq_find_mapping(pcie->irq_domain, 0);
+ if (generic_handle_irq(virq) == -EINVAL)
+ dev_err_ratelimited(&pcie->pdev->dev, "unhandled PME IRQ\n");
+ }
+
/* Process ERR interrupt */
if (isr0_status & PCIE_ISR0_ERR_MASK) {
advk_writel(pcie, PCIE_ISR0_ERR_MASK, PCIE_ISR0_REG);

View File

@ -0,0 +1,173 @@
From 68727b545332327b4c2f9c0f8d006be8970e7832 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
Date: Fri, 19 Feb 2021 14:22:22 +0100
Subject: [PATCH] PCI: aardvark: Fix support for PME requester on emulated
bridge
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Enable aardvark PME interrupt unconditionally by unmasking it and read PME
requester ID to emulated bridge config space immediately after receiving
interrupt.
PME requester ID is stored in the PCIE_MSG_LOG_REG register, which contains
the last inbound message. So when new inbound message is received by HW
(including non-PM), the content in PCIE_MSG_LOG_REG register is replaced by
a new value.
PCIe specification mandates that subsequent PMEs are kept pending until the
PME Status Register bit is cleared by software by writing a 1b.
Support for masking/unmasking PME interrupt on emulated bridge via
PCI_EXP_RTCTL_PMEIE bit is now implemented only in emulated bridge config
space, to ensure that we do not miss any aardvark PME interrupt.
Reading of PCI_EXP_RTCAP and PCI_EXP_RTSTA registers is simplified as final
value is now always stored into emulated bridge config space by the
interrupt handler, so there is no need to implement support for these
registers in read_pcie callback.
Clearing of W1C bit PCI_EXP_RTSTA_PME is now also simplified as it is done
by pci-bridge-emul.c code for emulated bridge config space. So there is no
need to implement support for clearing this bit in write_pcie callback.
Signed-off-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Marek Behún <kabel@kernel.org>
---
drivers/pci/controller/pci-aardvark.c | 94 +++++++++++++++------------
1 file changed, 52 insertions(+), 42 deletions(-)
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -590,6 +590,11 @@ static void advk_pcie_setup_hw(struct ad
reg &= ~PCIE_ISR0_MSI_INT_PENDING;
advk_writel(pcie, reg, PCIE_ISR0_MASK_REG);
+ /* Unmask PME interrupt for processing of PME requester */
+ reg = advk_readl(pcie, PCIE_ISR0_MASK_REG);
+ reg &= ~PCIE_MSG_PM_PME_MASK;
+ advk_writel(pcie, reg, PCIE_ISR0_MASK_REG);
+
/* Enable summary interrupt for GIC SPI source */
reg = PCIE_IRQ_ALL_MASK & (~PCIE_IRQ_ENABLE_INTS_MASK);
advk_writel(pcie, reg, HOST_CTRL_INT_MASK_REG);
@@ -856,22 +861,11 @@ advk_pci_bridge_emul_pcie_conf_read(stru
*value = PCI_EXP_SLTSTA_PDS << 16;
return PCI_BRIDGE_EMUL_HANDLED;
- case PCI_EXP_RTCTL: {
- u32 val = advk_readl(pcie, PCIE_ISR0_MASK_REG);
- *value = (val & PCIE_MSG_PM_PME_MASK) ? 0 : PCI_EXP_RTCTL_PMEIE;
- *value |= le16_to_cpu(bridge->pcie_conf.rootctl) & PCI_EXP_RTCTL_CRSSVE;
- *value |= PCI_EXP_RTCAP_CRSVIS << 16;
- return PCI_BRIDGE_EMUL_HANDLED;
- }
-
- case PCI_EXP_RTSTA: {
- u32 isr0 = advk_readl(pcie, PCIE_ISR0_REG);
- u32 msglog = advk_readl(pcie, PCIE_MSG_LOG_REG);
- *value = msglog >> 16;
- if (isr0 & PCIE_MSG_PM_PME_MASK)
- *value |= PCI_EXP_RTSTA_PME;
- return PCI_BRIDGE_EMUL_HANDLED;
- }
+ /*
+ * PCI_EXP_RTCTL and PCI_EXP_RTSTA are also supported, but do not need
+ * to be handled here, because their values are stored in emulated
+ * config space buffer, and we read them from there when needed.
+ */
case PCI_EXP_LNKCAP: {
u32 val = advk_readl(pcie, PCIE_CORE_PCIEXP_CAP + reg);
@@ -925,22 +919,19 @@ advk_pci_bridge_emul_pcie_conf_write(str
advk_pcie_wait_for_retrain(pcie);
break;
- case PCI_EXP_RTCTL:
- /* Only mask/unmask PME interrupt */
- if (mask & PCI_EXP_RTCTL_PMEIE) {
- u32 val = advk_readl(pcie, PCIE_ISR0_MASK_REG);
- if (new & PCI_EXP_RTCTL_PMEIE)
- val &= ~PCIE_MSG_PM_PME_MASK;
- else
- val |= PCIE_MSG_PM_PME_MASK;
- advk_writel(pcie, val, PCIE_ISR0_MASK_REG);
- }
+ case PCI_EXP_RTCTL: {
+ u16 rootctl = le16_to_cpu(bridge->pcie_conf.rootctl);
+ /* Only emulation of PMEIE and CRSSVE bits is provided */
+ rootctl &= PCI_EXP_RTCTL_PMEIE | PCI_EXP_RTCTL_CRSSVE;
+ bridge->pcie_conf.rootctl = cpu_to_le16(rootctl);
break;
+ }
- case PCI_EXP_RTSTA:
- if (new & PCI_EXP_RTSTA_PME)
- advk_writel(pcie, PCIE_MSG_PM_PME_MASK, PCIE_ISR0_REG);
- break;
+ /*
+ * PCI_EXP_RTSTA is also supported, but does not need to be handled
+ * here, because its value is stored in emulated config space buffer,
+ * and we write it there when needed.
+ */
case PCI_EXP_DEVCTL:
case PCI_EXP_DEVCTL2:
@@ -1445,6 +1436,34 @@ static void advk_pcie_remove_irq_domain(
irq_domain_remove(pcie->irq_domain);
}
+static void advk_pcie_handle_pme(struct advk_pcie *pcie)
+{
+ u32 requester = advk_readl(pcie, PCIE_MSG_LOG_REG) >> 16;
+ int virq;
+
+ advk_writel(pcie, PCIE_MSG_PM_PME_MASK, PCIE_ISR0_REG);
+
+ /*
+ * PCIE_MSG_LOG_REG contains the last inbound message, so store
+ * the requester ID only when PME was not asserted yet.
+ * Also do not trigger PME interrupt when PME is still asserted.
+ */
+ if (!(le32_to_cpu(pcie->bridge.pcie_conf.rootsta) & PCI_EXP_RTSTA_PME)) {
+ pcie->bridge.pcie_conf.rootsta = cpu_to_le32(requester | PCI_EXP_RTSTA_PME);
+
+ /*
+ * Trigger PME interrupt only if PMEIE bit in Root Control is set.
+ * Aardvark HW returns zero for PCI_EXP_FLAGS_IRQ, so use PCIe interrupt 0.
+ */
+ if (!(le16_to_cpu(pcie->bridge.pcie_conf.rootctl) & PCI_EXP_RTCTL_PMEIE))
+ return;
+
+ virq = irq_find_mapping(pcie->irq_domain, 0);
+ if (generic_handle_irq(virq) == -EINVAL)
+ dev_err_ratelimited(&pcie->pdev->dev, "unhandled PME IRQ\n");
+ }
+}
+
static void advk_pcie_handle_msi(struct advk_pcie *pcie)
{
u32 msi_val, msi_mask, msi_status, msi_idx;
@@ -1484,18 +1503,9 @@ static void advk_pcie_handle_int(struct
isr1_mask = advk_readl(pcie, PCIE_ISR1_MASK_REG);
isr1_status = isr1_val & ((~isr1_mask) & PCIE_ISR1_ALL_MASK);
- /* Process PME interrupt */
- if (isr0_status & PCIE_MSG_PM_PME_MASK) {
- /*
- * Do not clear PME interrupt bit in ISR0, it is cleared by IRQ
- * receiver by writing to the PCI_EXP_RTSTA register of emulated
- * root bridge. Aardvark HW returns zero for PCI_EXP_FLAGS_IRQ,
- * so use PCIe interrupt 0.
- */
- virq = irq_find_mapping(pcie->irq_domain, 0);
- if (generic_handle_irq(virq) == -EINVAL)
- dev_err_ratelimited(&pcie->pdev->dev, "unhandled PME IRQ\n");
- }
+ /* Process PME interrupt as the first one to do not miss PME requester id */
+ if (isr0_status & PCIE_MSG_PM_PME_MASK)
+ advk_pcie_handle_pme(pcie);
/* Process ERR interrupt */
if (isr0_status & PCIE_ISR0_ERR_MASK) {

View File

@ -0,0 +1,161 @@
From db305233136f5aa2444a8287a279384e8458c458 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
Date: Thu, 1 Apr 2021 20:12:48 +0200
Subject: [PATCH] PCI: aardvark: Use separate INTA interrupt for emulated root
bridge
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Emulated root bridge currently provides only one Legacy INTA interrupt
which is used for reporting PCIe PME and ERR events and handled by kernel
PCIe PME and AER drivers.
Aardvark HW reports these PME and ERR events separately, so there is no
need to mix real INTA interrupt and emulated INTA interrupt for PCIe PME
and AER drivers.
Register a new advk-RP (as in Root Port) irq chip and a new irq domain
for emulated root bridge and use this new separate irq domain for
providing INTA interrupt from emulated root bridge for PME and ERR events.
The real INTA interrupt from real devices is now separate.
A custom map_irq callback function on PCI host bridge structure is used to
allocate IRQ mapping for emulated root bridge from new irq domain. Original
callback of_irq_parse_and_map_pci() is used for all other devices as before.
Signed-off-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Marek Behún <kabel@kernel.org>
---
drivers/pci/controller/pci-aardvark.c | 69 ++++++++++++++++++++++++++-
1 file changed, 67 insertions(+), 2 deletions(-)
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -273,6 +273,7 @@ struct advk_pcie {
} wins[OB_WIN_COUNT];
u8 wins_count;
int irq;
+ struct irq_domain *rp_irq_domain;
struct irq_domain *irq_domain;
struct irq_chip irq_chip;
raw_spinlock_t irq_lock;
@@ -1436,6 +1437,44 @@ static void advk_pcie_remove_irq_domain(
irq_domain_remove(pcie->irq_domain);
}
+static struct irq_chip advk_rp_irq_chip = {
+ .name = "advk-RP",
+};
+
+static int advk_pcie_rp_irq_map(struct irq_domain *h,
+ unsigned int virq, irq_hw_number_t hwirq)
+{
+ struct advk_pcie *pcie = h->host_data;
+
+ irq_set_chip_and_handler(virq, &advk_rp_irq_chip, handle_simple_irq);
+ irq_set_chip_data(virq, pcie);
+
+ return 0;
+}
+
+static const struct irq_domain_ops advk_pcie_rp_irq_domain_ops = {
+ .map = advk_pcie_rp_irq_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static int advk_pcie_init_rp_irq_domain(struct advk_pcie *pcie)
+{
+ pcie->rp_irq_domain = irq_domain_add_linear(NULL, 1,
+ &advk_pcie_rp_irq_domain_ops,
+ pcie);
+ if (!pcie->rp_irq_domain) {
+ dev_err(&pcie->pdev->dev, "Failed to add Root Port IRQ domain\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void advk_pcie_remove_rp_irq_domain(struct advk_pcie *pcie)
+{
+ irq_domain_remove(pcie->rp_irq_domain);
+}
+
static void advk_pcie_handle_pme(struct advk_pcie *pcie)
{
u32 requester = advk_readl(pcie, PCIE_MSG_LOG_REG) >> 16;
@@ -1458,7 +1497,7 @@ static void advk_pcie_handle_pme(struct
if (!(le16_to_cpu(pcie->bridge.pcie_conf.rootctl) & PCI_EXP_RTCTL_PMEIE))
return;
- virq = irq_find_mapping(pcie->irq_domain, 0);
+ virq = irq_find_mapping(pcie->rp_irq_domain, 0);
if (generic_handle_irq(virq) == -EINVAL)
dev_err_ratelimited(&pcie->pdev->dev, "unhandled PME IRQ\n");
}
@@ -1515,7 +1554,7 @@ static void advk_pcie_handle_int(struct
* Aardvark HW returns zero for PCI_ERR_ROOT_AER_IRQ, so use
* PCIe interrupt 0
*/
- virq = irq_find_mapping(pcie->irq_domain, 0);
+ virq = irq_find_mapping(pcie->rp_irq_domain, 0);
if (generic_handle_irq(virq) == -EINVAL)
dev_err_ratelimited(&pcie->pdev->dev, "unhandled ERR IRQ\n");
}
@@ -1560,6 +1599,21 @@ static void advk_pcie_irq_handler(struct
chained_irq_exit(chip, desc);
}
+static int advk_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+ struct advk_pcie *pcie = dev->bus->sysdata;
+
+ /*
+ * Emulated root bridge has its own emulated irq chip and irq domain.
+ * Argument pin is the INTx pin (1=INTA, 2=INTB, 3=INTC, 4=INTD) and
+ * hwirq for irq_create_mapping() is indexed from zero.
+ */
+ if (pci_is_root_bus(dev->bus))
+ return irq_create_mapping(pcie->rp_irq_domain, pin - 1);
+ else
+ return of_irq_parse_and_map_pci(dev, slot, pin);
+}
+
static void __maybe_unused advk_pcie_disable_phy(struct advk_pcie *pcie)
{
phy_power_off(pcie->phy);
@@ -1761,14 +1815,24 @@ static int advk_pcie_probe(struct platfo
return ret;
}
+ ret = advk_pcie_init_rp_irq_domain(pcie);
+ if (ret) {
+ dev_err(dev, "Failed to initialize irq\n");
+ advk_pcie_remove_msi_irq_domain(pcie);
+ advk_pcie_remove_irq_domain(pcie);
+ return ret;
+ }
+
irq_set_chained_handler_and_data(pcie->irq, advk_pcie_irq_handler, pcie);
bridge->sysdata = pcie;
bridge->ops = &advk_pcie_ops;
+ bridge->map_irq = advk_pcie_map_irq;
ret = pci_host_probe(bridge);
if (ret < 0) {
irq_set_chained_handler_and_data(pcie->irq, NULL, NULL);
+ advk_pcie_remove_rp_irq_domain(pcie);
advk_pcie_remove_msi_irq_domain(pcie);
advk_pcie_remove_irq_domain(pcie);
return ret;
@@ -1820,6 +1884,7 @@ static int advk_pcie_remove(struct platf
irq_set_chained_handler_and_data(pcie->irq, NULL, NULL);
/* Remove IRQ domains */
+ advk_pcie_remove_rp_irq_domain(pcie);
advk_pcie_remove_msi_irq_domain(pcie);
advk_pcie_remove_irq_domain(pcie);

View File

@ -0,0 +1,29 @@
From 8c9eef96e24f34ff8b62b230700416b822691a37 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
Date: Thu, 1 Apr 2021 14:24:12 +0200
Subject: [PATCH] PCI: aardvark: Remove irq_mask_ack callback for INTx
interrupts
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Callback for irq_mask_ack is the same as for irq_mask. As there is no
special handling for irq_ack, there is no need to define irq_mask_ack too.
Signed-off-by: Pali Rohár <pali@kernel.org>
Acked-by: Marc Zyngier <maz@kernel.org>
Signed-off-by: Marek Behún <kabel@kernel.org>
---
drivers/pci/controller/pci-aardvark.c | 1 -
1 file changed, 1 deletion(-)
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -1415,7 +1415,6 @@ static int advk_pcie_init_irq_domain(str
}
irq_chip->irq_mask = advk_pcie_irq_mask;
- irq_chip->irq_mask_ack = advk_pcie_irq_mask;
irq_chip->irq_unmask = advk_pcie_irq_unmask;
pcie->irq_domain =

View File

@ -0,0 +1,27 @@
From dc01fca5a9d9c09ce9a3fb2bc2e7715c37ff3bd9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
Date: Thu, 1 Apr 2021 14:30:06 +0200
Subject: [PATCH] PCI: aardvark: Don't mask irq when mapping
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
By default, all Legacy INTx interrupts are masked, so there is no need to
mask this interrupt during irq_map callback.
Signed-off-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Marek Behún <kabel@kernel.org>
---
drivers/pci/controller/pci-aardvark.c | 1 -
1 file changed, 1 deletion(-)
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -1332,7 +1332,6 @@ static int advk_pcie_irq_map(struct irq_
{
struct advk_pcie *pcie = h->host_data;
- advk_pcie_irq_mask(irq_get_irq_data(virq));
irq_set_status_flags(virq, IRQ_LEVEL);
irq_set_chip_and_handler(virq, &pcie->irq_chip,
handle_level_irq);

View File

@ -0,0 +1,28 @@
From a511c99262ce19ee06908d27212b39ec4c5aeb17 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
Date: Wed, 8 Dec 2021 04:40:29 +0100
Subject: [PATCH] PCI: aardvark: Drop __maybe_unused from
advk_pcie_disable_phy()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This function is now always used in driver remove method, drop the
__maybe_unused attribute.
Signed-off-by: Marek Behún <kabel@kernel.org>
---
drivers/pci/controller/pci-aardvark.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -1612,7 +1612,7 @@ static int advk_pcie_map_irq(const struc
return of_irq_parse_and_map_pci(dev, slot, pin);
}
-static void __maybe_unused advk_pcie_disable_phy(struct advk_pcie *pcie)
+static void advk_pcie_disable_phy(struct advk_pcie *pcie)
{
phy_power_off(pcie->phy);
phy_exit(pcie->phy);

View File

@ -0,0 +1,35 @@
From bafda858364003a70b9cda84282f9761587f8033 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
Date: Mon, 10 Jan 2022 00:47:38 +0100
Subject: [PATCH] PCI: aardvark: Update comment about link going down after
link-up
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Update the comment about what happens when link goes down after we have
checked for link-up. If a PIO request is done while link-down, we have
a serious problem.
Signed-off-by: Marek Behún <kabel@kernel.org>
---
drivers/pci/controller/pci-aardvark.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -998,8 +998,12 @@ static bool advk_pcie_valid_device(struc
return false;
/*
- * If the link goes down after we check for link-up, nothing bad
- * happens but the config access times out.
+ * If the link goes down after we check for link-up, we have a problem:
+ * if a PIO request is executed while link-down, the whole controller
+ * gets stuck in a non-functional state, and even after link comes up
+ * again, PIO requests won't work anymore, and a reset of the whole PCIe
+ * controller is needed. Therefore we need to prevent sending PIO
+ * requests while the link is down.
*/
if (!pci_is_root_bus(bus) && !advk_pcie_link_up(pcie))
return false;

View File

@ -0,0 +1,102 @@
From 663b9f99bb35dbc0c7b685f71ee3668a60d31320 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
Date: Mon, 10 Jan 2022 02:02:00 +0100
Subject: [PATCH] PCI: aardvark: Make main irq_chip structure a static driver
structure
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Marc Zyngier says [1] that we should use struct irq_chip as a global
static struct in the driver. Even though the structure currently
contains a dynamic member (parent_device), Marc says [2] that he plans
to kill it and make the structure completely static.
We have already converted others irq_chip structures in this driver in
this way, but we omitted this one because the .name member is
dynamically created from device's name, and the name is displayed in
sysfs, so changing it would break sysfs ABI.
The rationale for changing the name (to "advk-INT") in spite of sysfs
ABI, and thus allowing to convert to a static structure, is that after
the other changes we made in this series, the IRQ chip is basically
something different: it no logner generates ERR and PME interrupts (they
are generated by emulated bridge's rp_irq_chip).
[1] https://lore.kernel.org/linux-pci/877dbcvngf.wl-maz@kernel.org/
[2] https://lore.kernel.org/linux-pci/874k6gvkhz.wl-maz@kernel.org/
Signed-off-by: Marek Behún <kabel@kernel.org>
---
drivers/pci/controller/pci-aardvark.c | 25 +++++++------------------
1 file changed, 7 insertions(+), 18 deletions(-)
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -275,7 +275,6 @@ struct advk_pcie {
int irq;
struct irq_domain *rp_irq_domain;
struct irq_domain *irq_domain;
- struct irq_chip irq_chip;
raw_spinlock_t irq_lock;
struct irq_domain *msi_domain;
struct irq_domain *msi_inner_domain;
@@ -1331,14 +1330,19 @@ static void advk_pcie_irq_unmask(struct
raw_spin_unlock_irqrestore(&pcie->irq_lock, flags);
}
+static struct irq_chip advk_irq_chip = {
+ .name = "advk-INT",
+ .irq_mask = advk_pcie_irq_mask,
+ .irq_unmask = advk_pcie_irq_unmask,
+};
+
static int advk_pcie_irq_map(struct irq_domain *h,
unsigned int virq, irq_hw_number_t hwirq)
{
struct advk_pcie *pcie = h->host_data;
irq_set_status_flags(virq, IRQ_LEVEL);
- irq_set_chip_and_handler(virq, &pcie->irq_chip,
- handle_level_irq);
+ irq_set_chip_and_handler(virq, &advk_irq_chip, handle_level_irq);
irq_set_chip_data(virq, pcie);
return 0;
@@ -1397,7 +1401,6 @@ static int advk_pcie_init_irq_domain(str
struct device *dev = &pcie->pdev->dev;
struct device_node *node = dev->of_node;
struct device_node *pcie_intc_node;
- struct irq_chip *irq_chip;
int ret = 0;
raw_spin_lock_init(&pcie->irq_lock);
@@ -1408,28 +1411,14 @@ static int advk_pcie_init_irq_domain(str
return -ENODEV;
}
- irq_chip = &pcie->irq_chip;
-
- irq_chip->name = devm_kasprintf(dev, GFP_KERNEL, "%s-irq",
- dev_name(dev));
- if (!irq_chip->name) {
- ret = -ENOMEM;
- goto out_put_node;
- }
-
- irq_chip->irq_mask = advk_pcie_irq_mask;
- irq_chip->irq_unmask = advk_pcie_irq_unmask;
-
pcie->irq_domain =
irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX,
&advk_pcie_irq_domain_ops, pcie);
if (!pcie->irq_domain) {
dev_err(dev, "Failed to get a INTx IRQ domain\n");
ret = -ENOMEM;
- goto out_put_node;
}
-out_put_node:
of_node_put(pcie_intc_node);
return ret;
}

View File

@ -0,0 +1,217 @@
From a719f7ba7fcba05d85801c6f0267f389a21627c1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
Date: Fri, 24 Sep 2021 13:03:02 +0200
Subject: [PATCH] phy: marvell: phy-mvebu-a3700-comphy: Remove port from driver
configuration
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Port number is encoded into argument for SMC call. It is zero for SATA,
PCIe and also both USB 3.0 PHYs. It is non-zero only for Ethernet PHY
(incorrectly called SGMII) on lane 0. Ethernet PHY on lane 1 also uses zero
port number.
So construct "port" bits for SMC call argument can be constructed directly
from PHY type and lane number.
Change driver code to always pass zero port number for non-ethernet PHYs
and for ethernet PHYs determinate port number from lane number. This
simplifies the driver.
As port number from DT PHY configuration is not used anymore, remove whole
driver code which parses it. This also simplifies the driver.
Signed-off-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Marek Behún <kabel@kernel.org>
Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/phy/marvell/phy-mvebu-a3700-comphy.c | 62 +++++++++-----------
1 file changed, 29 insertions(+), 33 deletions(-)
--- a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
+++ b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
@@ -20,7 +20,6 @@
#include <linux/platform_device.h>
#define MVEBU_A3700_COMPHY_LANES 3
-#define MVEBU_A3700_COMPHY_PORTS 2
/* COMPHY Fast SMC function identifiers */
#define COMPHY_SIP_POWER_ON 0x82000001
@@ -45,51 +44,47 @@
#define COMPHY_FW_NET(mode, idx, speed) (COMPHY_FW_MODE(mode) | \
((idx) << 8) | \
((speed) << 2))
-#define COMPHY_FW_PCIE(mode, idx, speed, width) (COMPHY_FW_NET(mode, idx, speed) | \
+#define COMPHY_FW_PCIE(mode, speed, width) (COMPHY_FW_NET(mode, 0, speed) | \
((width) << 18))
struct mvebu_a3700_comphy_conf {
unsigned int lane;
enum phy_mode mode;
int submode;
- unsigned int port;
u32 fw_mode;
};
-#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode, _port, _fw) \
+#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode, _fw) \
{ \
.lane = _lane, \
.mode = _mode, \
.submode = _smode, \
- .port = _port, \
.fw_mode = _fw, \
}
-#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode, _port, _fw) \
- MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA, _port, _fw)
+#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode, _fw) \
+ MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA, _fw)
-#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode, _port, _fw) \
- MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode, _port, _fw)
+#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode, _fw) \
+ MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode, _fw)
static const struct mvebu_a3700_comphy_conf mvebu_a3700_comphy_modes[] = {
/* lane 0 */
- MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS, 0,
+ MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS,
COMPHY_FW_MODE_USB3H),
- MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII, 1,
+ MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII,
COMPHY_FW_MODE_SGMII),
- MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX, 1,
+ MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX,
COMPHY_FW_MODE_2500BASEX),
/* lane 1 */
- MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE, 0,
- COMPHY_FW_MODE_PCIE),
- MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII, 0,
+ MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE, COMPHY_FW_MODE_PCIE),
+ MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII,
COMPHY_FW_MODE_SGMII),
- MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX, 0,
+ MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX,
COMPHY_FW_MODE_2500BASEX),
/* lane 2 */
- MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA, 0,
- COMPHY_FW_MODE_SATA),
- MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS, 0,
+ MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA, COMPHY_FW_MODE_SATA),
+ MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS,
COMPHY_FW_MODE_USB3H),
};
@@ -98,7 +93,6 @@ struct mvebu_a3700_comphy_lane {
unsigned int id;
enum phy_mode mode;
int submode;
- int port;
};
static int mvebu_a3700_comphy_smc(unsigned long function, unsigned long lane,
@@ -120,7 +114,7 @@ static int mvebu_a3700_comphy_smc(unsign
}
}
-static int mvebu_a3700_comphy_get_fw_mode(int lane, int port,
+static int mvebu_a3700_comphy_get_fw_mode(int lane,
enum phy_mode mode,
int submode)
{
@@ -132,7 +126,6 @@ static int mvebu_a3700_comphy_get_fw_mod
for (i = 0; i < n; i++) {
if (mvebu_a3700_comphy_modes[i].lane == lane &&
- mvebu_a3700_comphy_modes[i].port == port &&
mvebu_a3700_comphy_modes[i].mode == mode &&
mvebu_a3700_comphy_modes[i].submode == submode)
break;
@@ -153,7 +146,7 @@ static int mvebu_a3700_comphy_set_mode(s
if (submode == PHY_INTERFACE_MODE_1000BASEX)
submode = PHY_INTERFACE_MODE_SGMII;
- fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port, mode,
+ fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, mode,
submode);
if (fw_mode < 0) {
dev_err(lane->dev, "invalid COMPHY mode\n");
@@ -172,9 +165,10 @@ static int mvebu_a3700_comphy_power_on(s
struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
u32 fw_param;
int fw_mode;
+ int fw_port;
int ret;
- fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port,
+ fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id,
lane->mode, lane->submode);
if (fw_mode < 0) {
dev_err(lane->dev, "invalid COMPHY mode\n");
@@ -191,17 +185,18 @@ static int mvebu_a3700_comphy_power_on(s
fw_param = COMPHY_FW_MODE(fw_mode);
break;
case PHY_MODE_ETHERNET:
+ fw_port = (lane->id == 0) ? 1 : 0;
switch (lane->submode) {
case PHY_INTERFACE_MODE_SGMII:
dev_dbg(lane->dev, "set lane %d to SGMII mode\n",
lane->id);
- fw_param = COMPHY_FW_NET(fw_mode, lane->port,
+ fw_param = COMPHY_FW_NET(fw_mode, fw_port,
COMPHY_FW_SPEED_1_25G);
break;
case PHY_INTERFACE_MODE_2500BASEX:
dev_dbg(lane->dev, "set lane %d to 2500BASEX mode\n",
lane->id);
- fw_param = COMPHY_FW_NET(fw_mode, lane->port,
+ fw_param = COMPHY_FW_NET(fw_mode, fw_port,
COMPHY_FW_SPEED_3_125G);
break;
default:
@@ -212,8 +207,7 @@ static int mvebu_a3700_comphy_power_on(s
break;
case PHY_MODE_PCIE:
dev_dbg(lane->dev, "set lane %d to PCIe mode\n", lane->id);
- fw_param = COMPHY_FW_PCIE(fw_mode, lane->port,
- COMPHY_FW_SPEED_5G,
+ fw_param = COMPHY_FW_PCIE(fw_mode, COMPHY_FW_SPEED_5G,
phy->attrs.bus_width);
break;
default:
@@ -247,17 +241,20 @@ static struct phy *mvebu_a3700_comphy_xl
struct of_phandle_args *args)
{
struct mvebu_a3700_comphy_lane *lane;
+ unsigned int port;
struct phy *phy;
- if (WARN_ON(args->args[0] >= MVEBU_A3700_COMPHY_PORTS))
- return ERR_PTR(-EINVAL);
-
phy = of_phy_simple_xlate(dev, args);
if (IS_ERR(phy))
return phy;
lane = phy_get_drvdata(phy);
- lane->port = args->args[0];
+
+ port = args->args[0];
+ if (port != 0 && (port != 1 || lane->id != 0)) {
+ dev_err(lane->dev, "invalid port number %u\n", port);
+ return ERR_PTR(-EINVAL);
+ }
return phy;
}
@@ -302,7 +299,6 @@ static int mvebu_a3700_comphy_probe(stru
lane->mode = PHY_MODE_INVALID;
lane->submode = PHY_INTERFACE_MODE_NA;
lane->id = lane_id;
- lane->port = -1;
phy_set_drvdata(phy, lane);
}

View File

@ -0,0 +1,31 @@
From 66c51c39fd4bf05e99debf0e71de5704231c57dc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
Date: Thu, 23 Sep 2021 19:26:26 +0200
Subject: [PATCH] arm64: dts: marvell: armada-37xx: Add xtal clock to comphy
node
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Kernel driver phy-mvebu-a3700-comphy.c needs to know the rate of the
reference xtal clock. So add missing xtal clock source into comphy device
tree node. If the property is not present, the driver defaults to 25 MHz
xtal rate (which, as far as we know, is used by all the existing boards).
Signed-off-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Marek Behún <kabel@kernel.org>
---
arch/arm64/boot/dts/marvell/armada-37xx.dtsi | 2 ++
1 file changed, 2 insertions(+)
--- a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
@@ -265,6 +265,8 @@
"lane2_sata_usb3";
#address-cells = <1>;
#size-cells = <0>;
+ clocks = <&xtalclk>;
+ clock-names = "xtal";
comphy0: phy@0 {
reg = <0>;

View File

@ -0,0 +1,61 @@
From 750bb44dbbe9dfb4ba3e1f8a746b831b39ba3cd9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
Date: Thu, 23 Sep 2021 19:35:57 +0200
Subject: [PATCH] Revert "ata: ahci: mvebu: Make SATA PHY optional for Armada
3720"
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This reverts commit 45aefe3d2251e4e229d7662052739f96ad1d08d9.
Armada 3720 PHY driver (phy-mvebu-a3700-comphy.c) does not return
-EOPNOTSUPP from phy_power_on() callback anymore.
So remove AHCI_HFLAG_IGN_NOTSUPP_POWER_ON flag from Armada 3720 plat data.
AHCI_HFLAG_IGN_NOTSUPP_POWER_ON is not used by any other ahci driver, so
remove this flag completely.
Signed-off-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Marek Behún <kabel@kernel.org>
Acked-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/ata/ahci.h | 2 --
drivers/ata/ahci_mvebu.c | 2 +-
drivers/ata/libahci_platform.c | 2 +-
3 files changed, 2 insertions(+), 4 deletions(-)
--- a/drivers/ata/ahci.h
+++ b/drivers/ata/ahci.h
@@ -240,8 +240,6 @@ enum {
as default lpm_policy */
AHCI_HFLAG_SUSPEND_PHYS = (1 << 26), /* handle PHYs during
suspend/resume */
- AHCI_HFLAG_IGN_NOTSUPP_POWER_ON = (1 << 27), /* ignore -EOPNOTSUPP
- from phy_power_on() */
AHCI_HFLAG_NO_SXS = (1 << 28), /* SXS not supported */
/* ap->flags bits */
--- a/drivers/ata/ahci_mvebu.c
+++ b/drivers/ata/ahci_mvebu.c
@@ -227,7 +227,7 @@ static const struct ahci_mvebu_plat_data
static const struct ahci_mvebu_plat_data ahci_mvebu_armada_3700_plat_data = {
.plat_config = ahci_mvebu_armada_3700_config,
- .flags = AHCI_HFLAG_SUSPEND_PHYS | AHCI_HFLAG_IGN_NOTSUPP_POWER_ON,
+ .flags = AHCI_HFLAG_SUSPEND_PHYS,
};
static const struct of_device_id ahci_mvebu_of_match[] = {
--- a/drivers/ata/libahci_platform.c
+++ b/drivers/ata/libahci_platform.c
@@ -59,7 +59,7 @@ int ahci_platform_enable_phys(struct ahc
}
rc = phy_power_on(hpriv->phys[i]);
- if (rc && !(rc == -EOPNOTSUPP && (hpriv->flags & AHCI_HFLAG_IGN_NOTSUPP_POWER_ON))) {
+ if (rc) {
phy_exit(hpriv->phys[i]);
goto disable_phys;
}

View File

@ -0,0 +1,163 @@
From 9f0dfb279b1dd505d5e10b10e4a78a62030978d8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
Date: Thu, 23 Sep 2021 19:40:06 +0200
Subject: [PATCH] Revert "usb: host: xhci: mvebu: make USB 3.0 PHY optional for
Armada 3720"
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This reverts commit 3241929b67d28c83945d3191c6816a3271fd6b85.
Armada 3720 phy driver (phy-mvebu-a3700-comphy.c) does not return
-EOPNOTSUPP from phy_power_on() callback anymore.
So remove XHCI_SKIP_PHY_INIT flag from xhci_mvebu_a3700_plat_setup() and
then also whole xhci_mvebu_a3700_plat_setup() function which is there just
to handle -EOPNOTSUPP for XHCI_SKIP_PHY_INIT.
xhci plat_setup callback is not used by any other xhci plat driver, so
remove this callback completely.
Signed-off-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Marek Behún <kabel@kernel.org>
Acked-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/usb/host/xhci-mvebu.c | 42 -----------------------------------
drivers/usb/host/xhci-mvebu.h | 6 -----
drivers/usb/host/xhci-plat.c | 20 +----------------
drivers/usb/host/xhci-plat.h | 1 -
4 files changed, 1 insertion(+), 68 deletions(-)
--- a/drivers/usb/host/xhci-mvebu.c
+++ b/drivers/usb/host/xhci-mvebu.c
@@ -8,7 +8,6 @@
#include <linux/mbus.h>
#include <linux/of.h>
#include <linux/platform_device.h>
-#include <linux/phy/phy.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
@@ -74,47 +73,6 @@ int xhci_mvebu_mbus_init_quirk(struct us
return 0;
}
-
-int xhci_mvebu_a3700_plat_setup(struct usb_hcd *hcd)
-{
- struct xhci_hcd *xhci = hcd_to_xhci(hcd);
- struct device *dev = hcd->self.controller;
- struct phy *phy;
- int ret;
-
- /* Old bindings miss the PHY handle */
- phy = of_phy_get(dev->of_node, "usb3-phy");
- if (IS_ERR(phy) && PTR_ERR(phy) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
- else if (IS_ERR(phy))
- goto phy_out;
-
- ret = phy_init(phy);
- if (ret)
- goto phy_put;
-
- ret = phy_set_mode(phy, PHY_MODE_USB_HOST_SS);
- if (ret)
- goto phy_exit;
-
- ret = phy_power_on(phy);
- if (ret == -EOPNOTSUPP) {
- /* Skip initializatin of XHCI PHY when it is unsupported by firmware */
- dev_warn(dev, "PHY unsupported by firmware\n");
- xhci->quirks |= XHCI_SKIP_PHY_INIT;
- }
- if (ret)
- goto phy_exit;
-
- phy_power_off(phy);
-phy_exit:
- phy_exit(phy);
-phy_put:
- of_phy_put(phy);
-phy_out:
-
- return 0;
-}
int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd)
{
--- a/drivers/usb/host/xhci-mvebu.h
+++ b/drivers/usb/host/xhci-mvebu.h
@@ -12,18 +12,12 @@ struct usb_hcd;
#if IS_ENABLED(CONFIG_USB_XHCI_MVEBU)
int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd);
-int xhci_mvebu_a3700_plat_setup(struct usb_hcd *hcd);
int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd);
#else
static inline int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd)
{
return 0;
}
-
-static inline int xhci_mvebu_a3700_plat_setup(struct usb_hcd *hcd)
-{
- return 0;
-}
static inline int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd)
{
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -44,16 +44,6 @@ static void xhci_priv_plat_start(struct
priv->plat_start(hcd);
}
-static int xhci_priv_plat_setup(struct usb_hcd *hcd)
-{
- struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
-
- if (!priv->plat_setup)
- return 0;
-
- return priv->plat_setup(hcd);
-}
-
static int xhci_priv_init_quirk(struct usb_hcd *hcd)
{
struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
@@ -121,7 +111,6 @@ static const struct xhci_plat_priv xhci_
};
static const struct xhci_plat_priv xhci_plat_marvell_armada3700 = {
- .plat_setup = xhci_mvebu_a3700_plat_setup,
.init_quirk = xhci_mvebu_a3700_init_quirk,
};
@@ -341,14 +330,7 @@ static int xhci_plat_probe(struct platfo
hcd->tpl_support = of_usb_host_tpl_support(sysdev->of_node);
xhci->shared_hcd->tpl_support = hcd->tpl_support;
-
- if (priv) {
- ret = xhci_priv_plat_setup(hcd);
- if (ret)
- goto disable_usb_phy;
- }
-
- if ((xhci->quirks & XHCI_SKIP_PHY_INIT) || (priv && (priv->quirks & XHCI_SKIP_PHY_INIT)))
+ if (priv && (priv->quirks & XHCI_SKIP_PHY_INIT))
hcd->skip_phy_initialization = 1;
if (priv && (priv->quirks & XHCI_SG_TRB_CACHE_SIZE_QUIRK))
--- a/drivers/usb/host/xhci-plat.h
+++ b/drivers/usb/host/xhci-plat.h
@@ -13,7 +13,6 @@
struct xhci_plat_priv {
const char *firmware_name;
unsigned long long quirks;
- int (*plat_setup)(struct usb_hcd *);
void (*plat_start)(struct usb_hcd *);
int (*init_quirk)(struct usb_hcd *);
int (*suspend_quirk)(struct usb_hcd *);

View File

@ -0,0 +1,36 @@
From 9a352062b7e3857742389dff6f64393481dc755e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
Date: Thu, 23 Sep 2021 19:37:05 +0200
Subject: [PATCH] Revert "PCI: aardvark: Fix initialization with old Marvell's
Arm Trusted Firmware"
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This reverts commit b0c6ae0f8948a2be6bf4e8b4bbab9ca1343289b6.
Armada 3720 phy driver (phy-mvebu-a3700-comphy.c) does not return
-EOPNOTSUPP from phy_power_on() callback anymore.
So remove dead code which handles -EOPNOTSUPP return value.
Signed-off-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Marek Behún <kabel@kernel.org>
Acked-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/pci/controller/pci-aardvark.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -1629,9 +1629,7 @@ static int advk_pcie_enable_phy(struct a
}
ret = phy_power_on(pcie->phy);
- if (ret == -EOPNOTSUPP) {
- dev_warn(&pcie->pdev->dev, "PHY unsupported by firmware\n");
- } else if (ret) {
+ if (ret) {
phy_exit(pcie->phy);
return ret;
}