Drop all backport patches that are now included in kernel 6.6.
Signed-off-by: Weijie Gao <hackpascal@gmail.com>
+++ /dev/null
-From 8c20e2eb5f2a0175b774134685e4d7bd93e85ff8 Mon Sep 17 00:00:00 2001
-From: Yu Zhao <yuzhao@google.com>
-Date: Wed, 21 Dec 2022 21:18:59 -0700
-Subject: [PATCH 01/19] UPSTREAM: mm: multi-gen LRU: rename lru_gen_struct to
- lru_gen_folio
-
-Patch series "mm: multi-gen LRU: memcg LRU", v3.
-
-Overview
-========
-
-An memcg LRU is a per-node LRU of memcgs. It is also an LRU of LRUs,
-since each node and memcg combination has an LRU of folios (see
-mem_cgroup_lruvec()).
-
-Its goal is to improve the scalability of global reclaim, which is
-critical to system-wide memory overcommit in data centers. Note that
-memcg reclaim is currently out of scope.
-
-Its memory bloat is a pointer to each lruvec and negligible to each
-pglist_data. In terms of traversing memcgs during global reclaim, it
-improves the best-case complexity from O(n) to O(1) and does not affect
-the worst-case complexity O(n). Therefore, on average, it has a sublinear
-complexity in contrast to the current linear complexity.
-
-The basic structure of an memcg LRU can be understood by an analogy to
-the active/inactive LRU (of folios):
-1. It has the young and the old (generations), i.e., the counterparts
- to the active and the inactive;
-2. The increment of max_seq triggers promotion, i.e., the counterpart
- to activation;
-3. Other events trigger similar operations, e.g., offlining an memcg
- triggers demotion, i.e., the counterpart to deactivation.
-
-In terms of global reclaim, it has two distinct features:
-1. Sharding, which allows each thread to start at a random memcg (in
- the old generation) and improves parallelism;
-2. Eventual fairness, which allows direct reclaim to bail out at will
- and reduces latency without affecting fairness over some time.
-
-The commit message in patch 6 details the workflow:
-https://lore.kernel.org/r/20221222041905.2431096-7-yuzhao@google.com/
-
-The following is a simple test to quickly verify its effectiveness.
-
- Test design:
- 1. Create multiple memcgs.
- 2. Each memcg contains a job (fio).
- 3. All jobs access the same amount of memory randomly.
- 4. The system does not experience global memory pressure.
- 5. Periodically write to the root memory.reclaim.
-
- Desired outcome:
- 1. All memcgs have similar pgsteal counts, i.e., stddev(pgsteal)
- over mean(pgsteal) is close to 0%.
- 2. The total pgsteal is close to the total requested through
- memory.reclaim, i.e., sum(pgsteal) over sum(requested) is close
- to 100%.
-
- Actual outcome [1]:
- MGLRU off MGLRU on
- stddev(pgsteal) / mean(pgsteal) 75% 20%
- sum(pgsteal) / sum(requested) 425% 95%
-
- ####################################################################
- MEMCGS=128
-
- for ((memcg = 0; memcg < $MEMCGS; memcg++)); do
- mkdir /sys/fs/cgroup/memcg$memcg
- done
-
- start() {
- echo $BASHPID > /sys/fs/cgroup/memcg$memcg/cgroup.procs
-
- fio -name=memcg$memcg --numjobs=1 --ioengine=mmap \
- --filename=/dev/zero --size=1920M --rw=randrw \
- --rate=64m,64m --random_distribution=random \
- --fadvise_hint=0 --time_based --runtime=10h \
- --group_reporting --minimal
- }
-
- for ((memcg = 0; memcg < $MEMCGS; memcg++)); do
- start &
- done
-
- sleep 600
-
- for ((i = 0; i < 600; i++)); do
- echo 256m >/sys/fs/cgroup/memory.reclaim
- sleep 6
- done
-
- for ((memcg = 0; memcg < $MEMCGS; memcg++)); do
- grep "pgsteal " /sys/fs/cgroup/memcg$memcg/memory.stat
- done
- ####################################################################
-
-[1]: This was obtained from running the above script (touches less
- than 256GB memory) on an EPYC 7B13 with 512GB DRAM for over an
- hour.
-
-This patch (of 8):
-
-The new name lru_gen_folio will be more distinct from the coming
-lru_gen_memcg.
-
-Link: https://lkml.kernel.org/r/20221222041905.2431096-1-yuzhao@google.com
-Link: https://lkml.kernel.org/r/20221222041905.2431096-2-yuzhao@google.com
-Signed-off-by: Yu Zhao <yuzhao@google.com>
-Cc: Johannes Weiner <hannes@cmpxchg.org>
-Cc: Jonathan Corbet <corbet@lwn.net>
-Cc: Michael Larabel <Michael@MichaelLarabel.com>
-Cc: Michal Hocko <mhocko@kernel.org>
-Cc: Mike Rapoport <rppt@kernel.org>
-Cc: Roman Gushchin <roman.gushchin@linux.dev>
-Cc: Suren Baghdasaryan <surenb@google.com>
-Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
-Bug: 274865848
-(cherry picked from commit 391655fe08d1f942359a11148aa9aaf3f99d6d6f)
-Change-Id: I7df67e0e2435ba28f10eaa57d28d98b61a9210a6
-Signed-off-by: T.J. Mercier <tjmercier@google.com>
----
- include/linux/mm_inline.h | 4 ++--
- include/linux/mmzone.h | 6 +++---
- mm/vmscan.c | 34 +++++++++++++++++-----------------
- mm/workingset.c | 4 ++--
- 4 files changed, 24 insertions(+), 24 deletions(-)
-
---- a/include/linux/mm_inline.h
-+++ b/include/linux/mm_inline.h
-@@ -178,7 +178,7 @@ static inline void lru_gen_update_size(s
- int zone = folio_zonenum(folio);
- int delta = folio_nr_pages(folio);
- enum lru_list lru = type * LRU_INACTIVE_FILE;
-- struct lru_gen_struct *lrugen = &lruvec->lrugen;
-+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
-
- VM_WARN_ON_ONCE(old_gen != -1 && old_gen >= MAX_NR_GENS);
- VM_WARN_ON_ONCE(new_gen != -1 && new_gen >= MAX_NR_GENS);
-@@ -224,7 +224,7 @@ static inline bool lru_gen_add_folio(str
- int gen = folio_lru_gen(folio);
- int type = folio_is_file_lru(folio);
- int zone = folio_zonenum(folio);
-- struct lru_gen_struct *lrugen = &lruvec->lrugen;
-+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
-
- VM_WARN_ON_ONCE_FOLIO(gen != -1, folio);
-
---- a/include/linux/mmzone.h
-+++ b/include/linux/mmzone.h
-@@ -404,7 +404,7 @@ enum {
- * The number of pages in each generation is eventually consistent and therefore
- * can be transiently negative when reset_batch_size() is pending.
- */
--struct lru_gen_struct {
-+struct lru_gen_folio {
- /* the aging increments the youngest generation number */
- unsigned long max_seq;
- /* the eviction increments the oldest generation numbers */
-@@ -461,7 +461,7 @@ struct lru_gen_mm_state {
- struct lru_gen_mm_walk {
- /* the lruvec under reclaim */
- struct lruvec *lruvec;
-- /* unstable max_seq from lru_gen_struct */
-+ /* unstable max_seq from lru_gen_folio */
- unsigned long max_seq;
- /* the next address within an mm to scan */
- unsigned long next_addr;
-@@ -524,7 +524,7 @@ struct lruvec {
- unsigned long flags;
- #ifdef CONFIG_LRU_GEN
- /* evictable pages divided into generations */
-- struct lru_gen_struct lrugen;
-+ struct lru_gen_folio lrugen;
- /* to concurrently iterate lru_gen_mm_list */
- struct lru_gen_mm_state mm_state;
- #endif
---- a/mm/vmscan.c
-+++ b/mm/vmscan.c
-@@ -3190,7 +3190,7 @@ static int get_nr_gens(struct lruvec *lr
-
- static bool __maybe_unused seq_is_valid(struct lruvec *lruvec)
- {
-- /* see the comment on lru_gen_struct */
-+ /* see the comment on lru_gen_folio */
- return get_nr_gens(lruvec, LRU_GEN_FILE) >= MIN_NR_GENS &&
- get_nr_gens(lruvec, LRU_GEN_FILE) <= get_nr_gens(lruvec, LRU_GEN_ANON) &&
- get_nr_gens(lruvec, LRU_GEN_ANON) <= MAX_NR_GENS;
-@@ -3596,7 +3596,7 @@ struct ctrl_pos {
- static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier, int gain,
- struct ctrl_pos *pos)
- {
-- struct lru_gen_struct *lrugen = &lruvec->lrugen;
-+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
- int hist = lru_hist_from_seq(lrugen->min_seq[type]);
-
- pos->refaulted = lrugen->avg_refaulted[type][tier] +
-@@ -3611,7 +3611,7 @@ static void read_ctrl_pos(struct lruvec
- static void reset_ctrl_pos(struct lruvec *lruvec, int type, bool carryover)
- {
- int hist, tier;
-- struct lru_gen_struct *lrugen = &lruvec->lrugen;
-+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
- bool clear = carryover ? NR_HIST_GENS == 1 : NR_HIST_GENS > 1;
- unsigned long seq = carryover ? lrugen->min_seq[type] : lrugen->max_seq + 1;
-
-@@ -3688,7 +3688,7 @@ static int folio_update_gen(struct folio
- static int folio_inc_gen(struct lruvec *lruvec, struct folio *folio, bool reclaiming)
- {
- int type = folio_is_file_lru(folio);
-- struct lru_gen_struct *lrugen = &lruvec->lrugen;
-+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
- int new_gen, old_gen = lru_gen_from_seq(lrugen->min_seq[type]);
- unsigned long new_flags, old_flags = READ_ONCE(folio->flags);
-
-@@ -3733,7 +3733,7 @@ static void update_batch_size(struct lru
- static void reset_batch_size(struct lruvec *lruvec, struct lru_gen_mm_walk *walk)
- {
- int gen, type, zone;
-- struct lru_gen_struct *lrugen = &lruvec->lrugen;
-+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
-
- walk->batched = 0;
-
-@@ -4250,7 +4250,7 @@ static bool inc_min_seq(struct lruvec *l
- {
- int zone;
- int remaining = MAX_LRU_BATCH;
-- struct lru_gen_struct *lrugen = &lruvec->lrugen;
-+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
- int new_gen, old_gen = lru_gen_from_seq(lrugen->min_seq[type]);
-
- if (type == LRU_GEN_ANON && !can_swap)
-@@ -4286,7 +4286,7 @@ static bool try_to_inc_min_seq(struct lr
- {
- int gen, type, zone;
- bool success = false;
-- struct lru_gen_struct *lrugen = &lruvec->lrugen;
-+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
- DEFINE_MIN_SEQ(lruvec);
-
- VM_WARN_ON_ONCE(!seq_is_valid(lruvec));
-@@ -4307,7 +4307,7 @@ next:
- ;
- }
-
-- /* see the comment on lru_gen_struct */
-+ /* see the comment on lru_gen_folio */
- if (can_swap) {
- min_seq[LRU_GEN_ANON] = min(min_seq[LRU_GEN_ANON], min_seq[LRU_GEN_FILE]);
- min_seq[LRU_GEN_FILE] = max(min_seq[LRU_GEN_ANON], lrugen->min_seq[LRU_GEN_FILE]);
-@@ -4329,7 +4329,7 @@ static void inc_max_seq(struct lruvec *l
- {
- int prev, next;
- int type, zone;
-- struct lru_gen_struct *lrugen = &lruvec->lrugen;
-+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
-
- restart:
- spin_lock_irq(&lruvec->lru_lock);
-@@ -4389,7 +4389,7 @@ static bool try_to_inc_max_seq(struct lr
- bool success;
- struct lru_gen_mm_walk *walk;
- struct mm_struct *mm = NULL;
-- struct lru_gen_struct *lrugen = &lruvec->lrugen;
-+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
-
- VM_WARN_ON_ONCE(max_seq > READ_ONCE(lrugen->max_seq));
-
-@@ -4454,7 +4454,7 @@ static bool should_run_aging(struct lruv
- unsigned long old = 0;
- unsigned long young = 0;
- unsigned long total = 0;
-- struct lru_gen_struct *lrugen = &lruvec->lrugen;
-+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
- struct mem_cgroup *memcg = lruvec_memcg(lruvec);
-
- for (type = !can_swap; type < ANON_AND_FILE; type++) {
-@@ -4740,7 +4740,7 @@ static bool sort_folio(struct lruvec *lr
- int delta = folio_nr_pages(folio);
- int refs = folio_lru_refs(folio);
- int tier = lru_tier_from_refs(refs);
-- struct lru_gen_struct *lrugen = &lruvec->lrugen;
-+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
-
- VM_WARN_ON_ONCE_FOLIO(gen >= MAX_NR_GENS, folio);
-
-@@ -4848,7 +4848,7 @@ static int scan_folios(struct lruvec *lr
- int scanned = 0;
- int isolated = 0;
- int remaining = MAX_LRU_BATCH;
-- struct lru_gen_struct *lrugen = &lruvec->lrugen;
-+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
- struct mem_cgroup *memcg = lruvec_memcg(lruvec);
-
- VM_WARN_ON_ONCE(!list_empty(list));
-@@ -5249,7 +5249,7 @@ done:
-
- static bool __maybe_unused state_is_valid(struct lruvec *lruvec)
- {
-- struct lru_gen_struct *lrugen = &lruvec->lrugen;
-+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
-
- if (lrugen->enabled) {
- enum lru_list lru;
-@@ -5531,7 +5531,7 @@ static void lru_gen_seq_show_full(struct
- int i;
- int type, tier;
- int hist = lru_hist_from_seq(seq);
-- struct lru_gen_struct *lrugen = &lruvec->lrugen;
-+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
-
- for (tier = 0; tier < MAX_NR_TIERS; tier++) {
- seq_printf(m, " %10d", tier);
-@@ -5581,7 +5581,7 @@ static int lru_gen_seq_show(struct seq_f
- unsigned long seq;
- bool full = !debugfs_real_fops(m->file)->write;
- struct lruvec *lruvec = v;
-- struct lru_gen_struct *lrugen = &lruvec->lrugen;
-+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
- int nid = lruvec_pgdat(lruvec)->node_id;
- struct mem_cgroup *memcg = lruvec_memcg(lruvec);
- DEFINE_MAX_SEQ(lruvec);
-@@ -5835,7 +5835,7 @@ void lru_gen_init_lruvec(struct lruvec *
- {
- int i;
- int gen, type, zone;
-- struct lru_gen_struct *lrugen = &lruvec->lrugen;
-+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
-
- lrugen->max_seq = MIN_NR_GENS + 1;
- lrugen->enabled = lru_gen_enabled();
---- a/mm/workingset.c
-+++ b/mm/workingset.c
-@@ -223,7 +223,7 @@ static void *lru_gen_eviction(struct fol
- unsigned long token;
- unsigned long min_seq;
- struct lruvec *lruvec;
-- struct lru_gen_struct *lrugen;
-+ struct lru_gen_folio *lrugen;
- int type = folio_is_file_lru(folio);
- int delta = folio_nr_pages(folio);
- int refs = folio_lru_refs(folio);
-@@ -252,7 +252,7 @@ static void lru_gen_refault(struct folio
- unsigned long token;
- unsigned long min_seq;
- struct lruvec *lruvec;
-- struct lru_gen_struct *lrugen;
-+ struct lru_gen_folio *lrugen;
- struct mem_cgroup *memcg;
- struct pglist_data *pgdat;
- int type = folio_is_file_lru(folio);
+++ /dev/null
-From 14f9a7a15f3d1af351f30e0438fd747b7ac253b0 Mon Sep 17 00:00:00 2001
-From: Yu Zhao <yuzhao@google.com>
-Date: Wed, 21 Dec 2022 21:19:01 -0700
-Subject: [PATCH 03/19] UPSTREAM: mm: multi-gen LRU: remove eviction fairness
- safeguard
-
-Recall that the eviction consumes the oldest generation: first it
-bucket-sorts folios whose gen counters were updated by the aging and
-reclaims the rest; then it increments lrugen->min_seq.
-
-The current eviction fairness safeguard for global reclaim has a
-dilemma: when there are multiple eligible memcgs, should it continue
-or stop upon meeting the reclaim goal? If it continues, it overshoots
-and increases direct reclaim latency; if it stops, it loses fairness
-between memcgs it has taken memory away from and those it has yet to.
-
-With memcg LRU, the eviction, while ensuring eventual fairness, will
-stop upon meeting its goal. Therefore the current eviction fairness
-safeguard for global reclaim will not be needed.
-
-Note that memcg LRU only applies to global reclaim. For memcg reclaim,
-the eviction will continue, even if it is overshooting. This becomes
-unconditional due to code simplification.
-
-Link: https://lkml.kernel.org/r/20221222041905.2431096-4-yuzhao@google.com
-Signed-off-by: Yu Zhao <yuzhao@google.com>
-Cc: Johannes Weiner <hannes@cmpxchg.org>
-Cc: Jonathan Corbet <corbet@lwn.net>
-Cc: Michael Larabel <Michael@MichaelLarabel.com>
-Cc: Michal Hocko <mhocko@kernel.org>
-Cc: Mike Rapoport <rppt@kernel.org>
-Cc: Roman Gushchin <roman.gushchin@linux.dev>
-Cc: Suren Baghdasaryan <surenb@google.com>
-Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
-Bug: 274865848
-(cherry picked from commit a579086c99ed70cc4bfc104348dbe3dd8f2787e6)
-Change-Id: I08ac1b3c90e29cafd0566785aaa4bcdb5db7d22c
-Signed-off-by: T.J. Mercier <tjmercier@google.com>
----
- mm/vmscan.c | 81 +++++++++++++++--------------------------------------
- 1 file changed, 23 insertions(+), 58 deletions(-)
-
---- a/mm/vmscan.c
-+++ b/mm/vmscan.c
-@@ -448,6 +448,11 @@ static bool cgroup_reclaim(struct scan_c
- return sc->target_mem_cgroup;
- }
-
-+static bool global_reclaim(struct scan_control *sc)
-+{
-+ return !sc->target_mem_cgroup || mem_cgroup_is_root(sc->target_mem_cgroup);
-+}
-+
- /**
- * writeback_throttling_sane - is the usual dirty throttling mechanism available?
- * @sc: scan_control in question
-@@ -498,6 +503,11 @@ static bool cgroup_reclaim(struct scan_c
- return false;
- }
-
-+static bool global_reclaim(struct scan_control *sc)
-+{
-+ return true;
-+}
-+
- static bool writeback_throttling_sane(struct scan_control *sc)
- {
- return true;
-@@ -5005,8 +5015,7 @@ static int isolate_folios(struct lruvec
- return scanned;
- }
-
--static int evict_folios(struct lruvec *lruvec, struct scan_control *sc, int swappiness,
-- bool *need_swapping)
-+static int evict_folios(struct lruvec *lruvec, struct scan_control *sc, int swappiness)
- {
- int type;
- int scanned;
-@@ -5095,9 +5104,6 @@ retry:
- goto retry;
- }
-
-- if (need_swapping && type == LRU_GEN_ANON)
-- *need_swapping = true;
--
- return scanned;
- }
-
-@@ -5136,67 +5142,26 @@ done:
- return min_seq[!can_swap] + MIN_NR_GENS <= max_seq ? nr_to_scan : 0;
- }
-
--static bool should_abort_scan(struct lruvec *lruvec, unsigned long seq,
-- struct scan_control *sc, bool need_swapping)
-+static unsigned long get_nr_to_reclaim(struct scan_control *sc)
- {
-- int i;
-- DEFINE_MAX_SEQ(lruvec);
--
-- if (!current_is_kswapd()) {
-- /* age each memcg at most once to ensure fairness */
-- if (max_seq - seq > 1)
-- return true;
--
-- /* over-swapping can increase allocation latency */
-- if (sc->nr_reclaimed >= sc->nr_to_reclaim && need_swapping)
-- return true;
--
-- /* give this thread a chance to exit and free its memory */
-- if (fatal_signal_pending(current)) {
-- sc->nr_reclaimed += MIN_LRU_BATCH;
-- return true;
-- }
--
-- if (cgroup_reclaim(sc))
-- return false;
-- } else if (sc->nr_reclaimed - sc->last_reclaimed < sc->nr_to_reclaim)
-- return false;
--
-- /* keep scanning at low priorities to ensure fairness */
-- if (sc->priority > DEF_PRIORITY - 2)
-- return false;
--
-- /*
-- * A minimum amount of work was done under global memory pressure. For
-- * kswapd, it may be overshooting. For direct reclaim, the allocation
-- * may succeed if all suitable zones are somewhat safe. In either case,
-- * it's better to stop now, and restart later if necessary.
-- */
-- for (i = 0; i <= sc->reclaim_idx; i++) {
-- unsigned long wmark;
-- struct zone *zone = lruvec_pgdat(lruvec)->node_zones + i;
--
-- if (!managed_zone(zone))
-- continue;
--
-- wmark = current_is_kswapd() ? high_wmark_pages(zone) : low_wmark_pages(zone);
-- if (wmark > zone_page_state(zone, NR_FREE_PAGES))
-- return false;
-- }
-+ /* don't abort memcg reclaim to ensure fairness */
-+ if (!global_reclaim(sc))
-+ return -1;
-
-- sc->nr_reclaimed += MIN_LRU_BATCH;
-+ /* discount the previous progress for kswapd */
-+ if (current_is_kswapd())
-+ return sc->nr_to_reclaim + sc->last_reclaimed;
-
-- return true;
-+ return max(sc->nr_to_reclaim, compact_gap(sc->order));
- }
-
- static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
- {
- struct blk_plug plug;
- bool need_aging = false;
-- bool need_swapping = false;
- unsigned long scanned = 0;
- unsigned long reclaimed = sc->nr_reclaimed;
-- DEFINE_MAX_SEQ(lruvec);
-+ unsigned long nr_to_reclaim = get_nr_to_reclaim(sc);
-
- lru_add_drain();
-
-@@ -5220,7 +5185,7 @@ static void lru_gen_shrink_lruvec(struct
- if (!nr_to_scan)
- goto done;
-
-- delta = evict_folios(lruvec, sc, swappiness, &need_swapping);
-+ delta = evict_folios(lruvec, sc, swappiness);
- if (!delta)
- goto done;
-
-@@ -5228,7 +5193,7 @@ static void lru_gen_shrink_lruvec(struct
- if (scanned >= nr_to_scan)
- break;
-
-- if (should_abort_scan(lruvec, max_seq, sc, need_swapping))
-+ if (sc->nr_reclaimed >= nr_to_reclaim)
- break;
-
- cond_resched();
-@@ -5678,7 +5643,7 @@ static int run_eviction(struct lruvec *l
- if (sc->nr_reclaimed >= nr_to_reclaim)
- return 0;
-
-- if (!evict_folios(lruvec, sc, swappiness, NULL))
-+ if (!evict_folios(lruvec, sc, swappiness))
- return 0;
-
- cond_resched();
+++ /dev/null
-From f3c93d2e37a3c56593d7ccf4f4bcf1b58426fdd8 Mon Sep 17 00:00:00 2001
-From: Yu Zhao <yuzhao@google.com>
-Date: Wed, 21 Dec 2022 21:19:02 -0700
-Subject: [PATCH 04/19] BACKPORT: mm: multi-gen LRU: remove aging fairness
- safeguard
-
-Recall that the aging produces the youngest generation: first it scans
-for accessed folios and updates their gen counters; then it increments
-lrugen->max_seq.
-
-The current aging fairness safeguard for kswapd uses two passes to
-ensure the fairness to multiple eligible memcgs. On the first pass,
-which is shared with the eviction, it checks whether all eligible
-memcgs are low on cold folios. If so, it requires a second pass, on
-which it ages all those memcgs at the same time.
-
-With memcg LRU, the aging, while ensuring eventual fairness, will run
-when necessary. Therefore the current aging fairness safeguard for
-kswapd will not be needed.
-
-Note that memcg LRU only applies to global reclaim. For memcg reclaim,
-the aging can be unfair to different memcgs, i.e., their
-lrugen->max_seq can be incremented at different paces.
-
-Link: https://lkml.kernel.org/r/20221222041905.2431096-5-yuzhao@google.com
-Signed-off-by: Yu Zhao <yuzhao@google.com>
-Cc: Johannes Weiner <hannes@cmpxchg.org>
-Cc: Jonathan Corbet <corbet@lwn.net>
-Cc: Michael Larabel <Michael@MichaelLarabel.com>
-Cc: Michal Hocko <mhocko@kernel.org>
-Cc: Mike Rapoport <rppt@kernel.org>
-Cc: Roman Gushchin <roman.gushchin@linux.dev>
-Cc: Suren Baghdasaryan <surenb@google.com>
-Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
-Bug: 274865848
-(cherry picked from commit 7348cc91821b0cb24dfb00e578047f68299a50ab)
-[TJ: Resolved conflicts with older function signatures for
-min_cgroup_below_min / min_cgroup_below_low]
-Change-Id: I6e36ecfbaaefbc0a56d9a9d5d7cbe404ed7f57a5
-Signed-off-by: T.J. Mercier <tjmercier@google.com>
----
- mm/vmscan.c | 126 ++++++++++++++++++++++++----------------------------
- 1 file changed, 59 insertions(+), 67 deletions(-)
-
---- a/mm/vmscan.c
-+++ b/mm/vmscan.c
-@@ -136,7 +136,6 @@ struct scan_control {
-
- #ifdef CONFIG_LRU_GEN
- /* help kswapd make better choices among multiple memcgs */
-- unsigned int memcgs_need_aging:1;
- unsigned long last_reclaimed;
- #endif
-
-@@ -4457,7 +4456,7 @@ done:
- return true;
- }
-
--static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq, unsigned long *min_seq,
-+static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq,
- struct scan_control *sc, bool can_swap, unsigned long *nr_to_scan)
- {
- int gen, type, zone;
-@@ -4466,6 +4465,13 @@ static bool should_run_aging(struct lruv
- unsigned long total = 0;
- struct lru_gen_folio *lrugen = &lruvec->lrugen;
- struct mem_cgroup *memcg = lruvec_memcg(lruvec);
-+ DEFINE_MIN_SEQ(lruvec);
-+
-+ /* whether this lruvec is completely out of cold folios */
-+ if (min_seq[!can_swap] + MIN_NR_GENS > max_seq) {
-+ *nr_to_scan = 0;
-+ return true;
-+ }
-
- for (type = !can_swap; type < ANON_AND_FILE; type++) {
- unsigned long seq;
-@@ -4494,8 +4500,6 @@ static bool should_run_aging(struct lruv
- * stalls when the number of generations reaches MIN_NR_GENS. Hence, the
- * ideal number of generations is MIN_NR_GENS+1.
- */
-- if (min_seq[!can_swap] + MIN_NR_GENS > max_seq)
-- return true;
- if (min_seq[!can_swap] + MIN_NR_GENS < max_seq)
- return false;
-
-@@ -4514,40 +4518,54 @@ static bool should_run_aging(struct lruv
- return false;
- }
-
--static bool age_lruvec(struct lruvec *lruvec, struct scan_control *sc, unsigned long min_ttl)
-+static bool lruvec_is_sizable(struct lruvec *lruvec, struct scan_control *sc)
- {
-- bool need_aging;
-- unsigned long nr_to_scan;
-- int swappiness = get_swappiness(lruvec, sc);
-+ int gen, type, zone;
-+ unsigned long total = 0;
-+ bool can_swap = get_swappiness(lruvec, sc);
-+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
- struct mem_cgroup *memcg = lruvec_memcg(lruvec);
- DEFINE_MAX_SEQ(lruvec);
- DEFINE_MIN_SEQ(lruvec);
-
-- VM_WARN_ON_ONCE(sc->memcg_low_reclaim);
-+ for (type = !can_swap; type < ANON_AND_FILE; type++) {
-+ unsigned long seq;
-
-- mem_cgroup_calculate_protection(NULL, memcg);
-+ for (seq = min_seq[type]; seq <= max_seq; seq++) {
-+ gen = lru_gen_from_seq(seq);
-
-- if (mem_cgroup_below_min(memcg))
-- return false;
-+ for (zone = 0; zone < MAX_NR_ZONES; zone++)
-+ total += max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L);
-+ }
-+ }
-
-- need_aging = should_run_aging(lruvec, max_seq, min_seq, sc, swappiness, &nr_to_scan);
-+ /* whether the size is big enough to be helpful */
-+ return mem_cgroup_online(memcg) ? (total >> sc->priority) : total;
-+}
-
-- if (min_ttl) {
-- int gen = lru_gen_from_seq(min_seq[LRU_GEN_FILE]);
-- unsigned long birth = READ_ONCE(lruvec->lrugen.timestamps[gen]);
-+static bool lruvec_is_reclaimable(struct lruvec *lruvec, struct scan_control *sc,
-+ unsigned long min_ttl)
-+{
-+ int gen;
-+ unsigned long birth;
-+ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
-+ DEFINE_MIN_SEQ(lruvec);
-
-- if (time_is_after_jiffies(birth + min_ttl))
-- return false;
-+ VM_WARN_ON_ONCE(sc->memcg_low_reclaim);
-
-- /* the size is likely too small to be helpful */
-- if (!nr_to_scan && sc->priority != DEF_PRIORITY)
-- return false;
-- }
-+ /* see the comment on lru_gen_folio */
-+ gen = lru_gen_from_seq(min_seq[LRU_GEN_FILE]);
-+ birth = READ_ONCE(lruvec->lrugen.timestamps[gen]);
-
-- if (need_aging)
-- try_to_inc_max_seq(lruvec, max_seq, sc, swappiness, false);
-+ if (time_is_after_jiffies(birth + min_ttl))
-+ return false;
-
-- return true;
-+ if (!lruvec_is_sizable(lruvec, sc))
-+ return false;
-+
-+ mem_cgroup_calculate_protection(NULL, memcg);
-+
-+ return !mem_cgroup_below_min(memcg);
- }
-
- /* to protect the working set of the last N jiffies */
-@@ -4556,46 +4574,32 @@ static unsigned long lru_gen_min_ttl __r
- static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_control *sc)
- {
- struct mem_cgroup *memcg;
-- bool success = false;
- unsigned long min_ttl = READ_ONCE(lru_gen_min_ttl);
-
- VM_WARN_ON_ONCE(!current_is_kswapd());
-
- sc->last_reclaimed = sc->nr_reclaimed;
-
-- /*
-- * To reduce the chance of going into the aging path, which can be
-- * costly, optimistically skip it if the flag below was cleared in the
-- * eviction path. This improves the overall performance when multiple
-- * memcgs are available.
-- */
-- if (!sc->memcgs_need_aging) {
-- sc->memcgs_need_aging = true;
-+ /* check the order to exclude compaction-induced reclaim */
-+ if (!min_ttl || sc->order || sc->priority == DEF_PRIORITY)
- return;
-- }
--
-- set_mm_walk(pgdat);
-
- memcg = mem_cgroup_iter(NULL, NULL, NULL);
- do {
- struct lruvec *lruvec = mem_cgroup_lruvec(memcg, pgdat);
-
-- if (age_lruvec(lruvec, sc, min_ttl))
-- success = true;
-+ if (lruvec_is_reclaimable(lruvec, sc, min_ttl)) {
-+ mem_cgroup_iter_break(NULL, memcg);
-+ return;
-+ }
-
- cond_resched();
- } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL)));
-
-- clear_mm_walk();
--
-- /* check the order to exclude compaction-induced reclaim */
-- if (success || !min_ttl || sc->order)
-- return;
--
- /*
- * The main goal is to OOM kill if every generation from all memcgs is
- * younger than min_ttl. However, another possibility is all memcgs are
-- * either below min or empty.
-+ * either too small or below min.
- */
- if (mutex_trylock(&oom_lock)) {
- struct oom_control oc = {
-@@ -5113,33 +5117,27 @@ retry:
- * reclaim.
- */
- static unsigned long get_nr_to_scan(struct lruvec *lruvec, struct scan_control *sc,
-- bool can_swap, bool *need_aging)
-+ bool can_swap)
- {
- unsigned long nr_to_scan;
- struct mem_cgroup *memcg = lruvec_memcg(lruvec);
- DEFINE_MAX_SEQ(lruvec);
-- DEFINE_MIN_SEQ(lruvec);
-
- if (mem_cgroup_below_min(memcg) ||
- (mem_cgroup_below_low(memcg) && !sc->memcg_low_reclaim))
- return 0;
-
-- *need_aging = should_run_aging(lruvec, max_seq, min_seq, sc, can_swap, &nr_to_scan);
-- if (!*need_aging)
-+ if (!should_run_aging(lruvec, max_seq, sc, can_swap, &nr_to_scan))
- return nr_to_scan;
-
- /* skip the aging path at the default priority */
- if (sc->priority == DEF_PRIORITY)
-- goto done;
-+ return nr_to_scan;
-
-- /* leave the work to lru_gen_age_node() */
-- if (current_is_kswapd())
-- return 0;
-+ try_to_inc_max_seq(lruvec, max_seq, sc, can_swap, false);
-
-- if (try_to_inc_max_seq(lruvec, max_seq, sc, can_swap, false))
-- return nr_to_scan;
--done:
-- return min_seq[!can_swap] + MIN_NR_GENS <= max_seq ? nr_to_scan : 0;
-+ /* skip this lruvec as it's low on cold folios */
-+ return 0;
- }
-
- static unsigned long get_nr_to_reclaim(struct scan_control *sc)
-@@ -5158,9 +5156,7 @@ static unsigned long get_nr_to_reclaim(s
- static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
- {
- struct blk_plug plug;
-- bool need_aging = false;
- unsigned long scanned = 0;
-- unsigned long reclaimed = sc->nr_reclaimed;
- unsigned long nr_to_reclaim = get_nr_to_reclaim(sc);
-
- lru_add_drain();
-@@ -5181,13 +5177,13 @@ static void lru_gen_shrink_lruvec(struct
- else
- swappiness = 0;
-
-- nr_to_scan = get_nr_to_scan(lruvec, sc, swappiness, &need_aging);
-+ nr_to_scan = get_nr_to_scan(lruvec, sc, swappiness);
- if (!nr_to_scan)
-- goto done;
-+ break;
-
- delta = evict_folios(lruvec, sc, swappiness);
- if (!delta)
-- goto done;
-+ break;
-
- scanned += delta;
- if (scanned >= nr_to_scan)
-@@ -5199,10 +5195,6 @@ static void lru_gen_shrink_lruvec(struct
- cond_resched();
- }
-
-- /* see the comment in lru_gen_age_node() */
-- if (sc->nr_reclaimed - reclaimed >= MIN_LRU_BATCH && !need_aging)
-- sc->memcgs_need_aging = false;
--done:
- clear_mm_walk();
-
- blk_finish_plug(&plug);
+++ /dev/null
-From eca3858631e0cbad2ca6e40f788892749428e4cb Mon Sep 17 00:00:00 2001
-From: Yu Zhao <yuzhao@google.com>
-Date: Wed, 21 Dec 2022 21:19:03 -0700
-Subject: [PATCH 05/19] UPSTREAM: mm: multi-gen LRU: shuffle should_run_aging()
-
-Move should_run_aging() next to its only caller left.
-
-Link: https://lkml.kernel.org/r/20221222041905.2431096-6-yuzhao@google.com
-Cc: Johannes Weiner <hannes@cmpxchg.org>
-Cc: Jonathan Corbet <corbet@lwn.net>
-Cc: Michael Larabel <Michael@MichaelLarabel.com>
-Cc: Michal Hocko <mhocko@kernel.org>
-Cc: Mike Rapoport <rppt@kernel.org>
-Cc: Roman Gushchin <roman.gushchin@linux.dev>
-Cc: Suren Baghdasaryan <surenb@google.com>
-Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
-Bug: 274865848
-(cherry picked from commit 77d4459a4a1a472b7309e475f962dda87d950abd)
-Signed-off-by: T.J. Mercier <tjmercier@google.com>
-Change-Id: I3b0383fe16b93a783b4d8c0b3a0b325160392576
-Signed-off-by: Yu Zhao <yuzhao@google.com>
-Signed-off-by: T.J. Mercier <tjmercier@google.com>
----
- mm/vmscan.c | 124 ++++++++++++++++++++++++++--------------------------
- 1 file changed, 62 insertions(+), 62 deletions(-)
-
---- a/mm/vmscan.c
-+++ b/mm/vmscan.c
-@@ -4456,68 +4456,6 @@ done:
- return true;
- }
-
--static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq,
-- struct scan_control *sc, bool can_swap, unsigned long *nr_to_scan)
--{
-- int gen, type, zone;
-- unsigned long old = 0;
-- unsigned long young = 0;
-- unsigned long total = 0;
-- struct lru_gen_folio *lrugen = &lruvec->lrugen;
-- struct mem_cgroup *memcg = lruvec_memcg(lruvec);
-- DEFINE_MIN_SEQ(lruvec);
--
-- /* whether this lruvec is completely out of cold folios */
-- if (min_seq[!can_swap] + MIN_NR_GENS > max_seq) {
-- *nr_to_scan = 0;
-- return true;
-- }
--
-- for (type = !can_swap; type < ANON_AND_FILE; type++) {
-- unsigned long seq;
--
-- for (seq = min_seq[type]; seq <= max_seq; seq++) {
-- unsigned long size = 0;
--
-- gen = lru_gen_from_seq(seq);
--
-- for (zone = 0; zone < MAX_NR_ZONES; zone++)
-- size += max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L);
--
-- total += size;
-- if (seq == max_seq)
-- young += size;
-- else if (seq + MIN_NR_GENS == max_seq)
-- old += size;
-- }
-- }
--
-- /* try to scrape all its memory if this memcg was deleted */
-- *nr_to_scan = mem_cgroup_online(memcg) ? (total >> sc->priority) : total;
--
-- /*
-- * The aging tries to be lazy to reduce the overhead, while the eviction
-- * stalls when the number of generations reaches MIN_NR_GENS. Hence, the
-- * ideal number of generations is MIN_NR_GENS+1.
-- */
-- if (min_seq[!can_swap] + MIN_NR_GENS < max_seq)
-- return false;
--
-- /*
-- * It's also ideal to spread pages out evenly, i.e., 1/(MIN_NR_GENS+1)
-- * of the total number of pages for each generation. A reasonable range
-- * for this average portion is [1/MIN_NR_GENS, 1/(MIN_NR_GENS+2)]. The
-- * aging cares about the upper bound of hot pages, while the eviction
-- * cares about the lower bound of cold pages.
-- */
-- if (young * MIN_NR_GENS > total)
-- return true;
-- if (old * (MIN_NR_GENS + 2) < total)
-- return true;
--
-- return false;
--}
--
- static bool lruvec_is_sizable(struct lruvec *lruvec, struct scan_control *sc)
- {
- int gen, type, zone;
-@@ -5111,6 +5049,68 @@ retry:
- return scanned;
- }
-
-+static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq,
-+ struct scan_control *sc, bool can_swap, unsigned long *nr_to_scan)
-+{
-+ int gen, type, zone;
-+ unsigned long old = 0;
-+ unsigned long young = 0;
-+ unsigned long total = 0;
-+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
-+ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
-+ DEFINE_MIN_SEQ(lruvec);
-+
-+ /* whether this lruvec is completely out of cold folios */
-+ if (min_seq[!can_swap] + MIN_NR_GENS > max_seq) {
-+ *nr_to_scan = 0;
-+ return true;
-+ }
-+
-+ for (type = !can_swap; type < ANON_AND_FILE; type++) {
-+ unsigned long seq;
-+
-+ for (seq = min_seq[type]; seq <= max_seq; seq++) {
-+ unsigned long size = 0;
-+
-+ gen = lru_gen_from_seq(seq);
-+
-+ for (zone = 0; zone < MAX_NR_ZONES; zone++)
-+ size += max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L);
-+
-+ total += size;
-+ if (seq == max_seq)
-+ young += size;
-+ else if (seq + MIN_NR_GENS == max_seq)
-+ old += size;
-+ }
-+ }
-+
-+ /* try to scrape all its memory if this memcg was deleted */
-+ *nr_to_scan = mem_cgroup_online(memcg) ? (total >> sc->priority) : total;
-+
-+ /*
-+ * The aging tries to be lazy to reduce the overhead, while the eviction
-+ * stalls when the number of generations reaches MIN_NR_GENS. Hence, the
-+ * ideal number of generations is MIN_NR_GENS+1.
-+ */
-+ if (min_seq[!can_swap] + MIN_NR_GENS < max_seq)
-+ return false;
-+
-+ /*
-+ * It's also ideal to spread pages out evenly, i.e., 1/(MIN_NR_GENS+1)
-+ * of the total number of pages for each generation. A reasonable range
-+ * for this average portion is [1/MIN_NR_GENS, 1/(MIN_NR_GENS+2)]. The
-+ * aging cares about the upper bound of hot pages, while the eviction
-+ * cares about the lower bound of cold pages.
-+ */
-+ if (young * MIN_NR_GENS > total)
-+ return true;
-+ if (old * (MIN_NR_GENS + 2) < total)
-+ return true;
-+
-+ return false;
-+}
-+
- /*
- * For future optimizations:
- * 1. Defer try_to_inc_max_seq() to workqueues to reduce latency for memcg
+++ /dev/null
-From 8ee8571e47aa75221e5fbd4c9c7802fc4244c346 Mon Sep 17 00:00:00 2001
-From: Yu Zhao <yuzhao@google.com>
-Date: Wed, 21 Dec 2022 21:19:04 -0700
-Subject: [PATCH 06/19] BACKPORT: mm: multi-gen LRU: per-node lru_gen_folio
- lists
-
-For each node, memcgs are divided into two generations: the old and
-the young. For each generation, memcgs are randomly sharded into
-multiple bins to improve scalability. For each bin, an RCU hlist_nulls
-is virtually divided into three segments: the head, the tail and the
-default.
-
-An onlining memcg is added to the tail of a random bin in the old
-generation. The eviction starts at the head of a random bin in the old
-generation. The per-node memcg generation counter, whose reminder (mod
-2) indexes the old generation, is incremented when all its bins become
-empty.
-
-There are four operations:
-1. MEMCG_LRU_HEAD, which moves an memcg to the head of a random bin in
- its current generation (old or young) and updates its "seg" to
- "head";
-2. MEMCG_LRU_TAIL, which moves an memcg to the tail of a random bin in
- its current generation (old or young) and updates its "seg" to
- "tail";
-3. MEMCG_LRU_OLD, which moves an memcg to the head of a random bin in
- the old generation, updates its "gen" to "old" and resets its "seg"
- to "default";
-4. MEMCG_LRU_YOUNG, which moves an memcg to the tail of a random bin
- in the young generation, updates its "gen" to "young" and resets
- its "seg" to "default".
-
-The events that trigger the above operations are:
-1. Exceeding the soft limit, which triggers MEMCG_LRU_HEAD;
-2. The first attempt to reclaim an memcg below low, which triggers
- MEMCG_LRU_TAIL;
-3. The first attempt to reclaim an memcg below reclaimable size
- threshold, which triggers MEMCG_LRU_TAIL;
-4. The second attempt to reclaim an memcg below reclaimable size
- threshold, which triggers MEMCG_LRU_YOUNG;
-5. Attempting to reclaim an memcg below min, which triggers
- MEMCG_LRU_YOUNG;
-6. Finishing the aging on the eviction path, which triggers
- MEMCG_LRU_YOUNG;
-7. Offlining an memcg, which triggers MEMCG_LRU_OLD.
-
-Note that memcg LRU only applies to global reclaim, and the
-round-robin incrementing of their max_seq counters ensures the
-eventual fairness to all eligible memcgs. For memcg reclaim, it still
-relies on mem_cgroup_iter().
-
-Link: https://lkml.kernel.org/r/20221222041905.2431096-7-yuzhao@google.com
-Signed-off-by: Yu Zhao <yuzhao@google.com>
-Cc: Johannes Weiner <hannes@cmpxchg.org>
-Cc: Jonathan Corbet <corbet@lwn.net>
-Cc: Michael Larabel <Michael@MichaelLarabel.com>
-Cc: Michal Hocko <mhocko@kernel.org>
-Cc: Mike Rapoport <rppt@kernel.org>
-Cc: Roman Gushchin <roman.gushchin@linux.dev>
-Cc: Suren Baghdasaryan <surenb@google.com>
-Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
-Bug: 274865848
-(cherry picked from commit e4dde56cd208674ce899b47589f263499e5b8cdc)
-[TJ: Resolved conflicts with older function signatures for
-min_cgroup_below_min / min_cgroup_below_low and includes]
-Change-Id: Idc8a0f635e035d72dd911f807d1224cb47cbd655
-Signed-off-by: T.J. Mercier <tjmercier@google.com>
----
- include/linux/memcontrol.h | 10 +
- include/linux/mm_inline.h | 17 ++
- include/linux/mmzone.h | 117 +++++++++++-
- mm/memcontrol.c | 16 ++
- mm/page_alloc.c | 1 +
- mm/vmscan.c | 374 +++++++++++++++++++++++++++++++++----
- 6 files changed, 500 insertions(+), 35 deletions(-)
-
---- a/include/linux/memcontrol.h
-+++ b/include/linux/memcontrol.h
-@@ -795,6 +795,11 @@ static inline void obj_cgroup_put(struct
- percpu_ref_put(&objcg->refcnt);
- }
-
-+static inline bool mem_cgroup_tryget(struct mem_cgroup *memcg)
-+{
-+ return !memcg || css_tryget(&memcg->css);
-+}
-+
- static inline void mem_cgroup_put(struct mem_cgroup *memcg)
- {
- if (memcg)
-@@ -1295,6 +1300,11 @@ static inline void obj_cgroup_put(struct
- {
- }
-
-+static inline bool mem_cgroup_tryget(struct mem_cgroup *memcg)
-+{
-+ return true;
-+}
-+
- static inline void mem_cgroup_put(struct mem_cgroup *memcg)
- {
- }
---- a/include/linux/mm_inline.h
-+++ b/include/linux/mm_inline.h
-@@ -122,6 +122,18 @@ static inline bool lru_gen_in_fault(void
- return current->in_lru_fault;
- }
-
-+#ifdef CONFIG_MEMCG
-+static inline int lru_gen_memcg_seg(struct lruvec *lruvec)
-+{
-+ return READ_ONCE(lruvec->lrugen.seg);
-+}
-+#else
-+static inline int lru_gen_memcg_seg(struct lruvec *lruvec)
-+{
-+ return 0;
-+}
-+#endif
-+
- static inline int lru_gen_from_seq(unsigned long seq)
- {
- return seq % MAX_NR_GENS;
-@@ -302,6 +314,11 @@ static inline bool lru_gen_in_fault(void
- return false;
- }
-
-+static inline int lru_gen_memcg_seg(struct lruvec *lruvec)
-+{
-+ return 0;
-+}
-+
- static inline bool lru_gen_add_folio(struct lruvec *lruvec, struct folio *folio, bool reclaiming)
- {
- return false;
---- a/include/linux/mmzone.h
-+++ b/include/linux/mmzone.h
-@@ -7,6 +7,7 @@
-
- #include <linux/spinlock.h>
- #include <linux/list.h>
-+#include <linux/list_nulls.h>
- #include <linux/wait.h>
- #include <linux/bitops.h>
- #include <linux/cache.h>
-@@ -367,6 +368,15 @@ struct page_vma_mapped_walk;
- #define LRU_GEN_MASK ((BIT(LRU_GEN_WIDTH) - 1) << LRU_GEN_PGOFF)
- #define LRU_REFS_MASK ((BIT(LRU_REFS_WIDTH) - 1) << LRU_REFS_PGOFF)
-
-+/* see the comment on MEMCG_NR_GENS */
-+enum {
-+ MEMCG_LRU_NOP,
-+ MEMCG_LRU_HEAD,
-+ MEMCG_LRU_TAIL,
-+ MEMCG_LRU_OLD,
-+ MEMCG_LRU_YOUNG,
-+};
-+
- #ifdef CONFIG_LRU_GEN
-
- enum {
-@@ -426,6 +436,14 @@ struct lru_gen_folio {
- atomic_long_t refaulted[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS];
- /* whether the multi-gen LRU is enabled */
- bool enabled;
-+#ifdef CONFIG_MEMCG
-+ /* the memcg generation this lru_gen_folio belongs to */
-+ u8 gen;
-+ /* the list segment this lru_gen_folio belongs to */
-+ u8 seg;
-+ /* per-node lru_gen_folio list for global reclaim */
-+ struct hlist_nulls_node list;
-+#endif
- };
-
- enum {
-@@ -479,12 +497,87 @@ void lru_gen_init_lruvec(struct lruvec *
- void lru_gen_look_around(struct page_vma_mapped_walk *pvmw);
-
- #ifdef CONFIG_MEMCG
-+
-+/*
-+ * For each node, memcgs are divided into two generations: the old and the
-+ * young. For each generation, memcgs are randomly sharded into multiple bins
-+ * to improve scalability. For each bin, the hlist_nulls is virtually divided
-+ * into three segments: the head, the tail and the default.
-+ *
-+ * An onlining memcg is added to the tail of a random bin in the old generation.
-+ * The eviction starts at the head of a random bin in the old generation. The
-+ * per-node memcg generation counter, whose reminder (mod MEMCG_NR_GENS) indexes
-+ * the old generation, is incremented when all its bins become empty.
-+ *
-+ * There are four operations:
-+ * 1. MEMCG_LRU_HEAD, which moves an memcg to the head of a random bin in its
-+ * current generation (old or young) and updates its "seg" to "head";
-+ * 2. MEMCG_LRU_TAIL, which moves an memcg to the tail of a random bin in its
-+ * current generation (old or young) and updates its "seg" to "tail";
-+ * 3. MEMCG_LRU_OLD, which moves an memcg to the head of a random bin in the old
-+ * generation, updates its "gen" to "old" and resets its "seg" to "default";
-+ * 4. MEMCG_LRU_YOUNG, which moves an memcg to the tail of a random bin in the
-+ * young generation, updates its "gen" to "young" and resets its "seg" to
-+ * "default".
-+ *
-+ * The events that trigger the above operations are:
-+ * 1. Exceeding the soft limit, which triggers MEMCG_LRU_HEAD;
-+ * 2. The first attempt to reclaim an memcg below low, which triggers
-+ * MEMCG_LRU_TAIL;
-+ * 3. The first attempt to reclaim an memcg below reclaimable size threshold,
-+ * which triggers MEMCG_LRU_TAIL;
-+ * 4. The second attempt to reclaim an memcg below reclaimable size threshold,
-+ * which triggers MEMCG_LRU_YOUNG;
-+ * 5. Attempting to reclaim an memcg below min, which triggers MEMCG_LRU_YOUNG;
-+ * 6. Finishing the aging on the eviction path, which triggers MEMCG_LRU_YOUNG;
-+ * 7. Offlining an memcg, which triggers MEMCG_LRU_OLD.
-+ *
-+ * Note that memcg LRU only applies to global reclaim, and the round-robin
-+ * incrementing of their max_seq counters ensures the eventual fairness to all
-+ * eligible memcgs. For memcg reclaim, it still relies on mem_cgroup_iter().
-+ */
-+#define MEMCG_NR_GENS 2
-+#define MEMCG_NR_BINS 8
-+
-+struct lru_gen_memcg {
-+ /* the per-node memcg generation counter */
-+ unsigned long seq;
-+ /* each memcg has one lru_gen_folio per node */
-+ unsigned long nr_memcgs[MEMCG_NR_GENS];
-+ /* per-node lru_gen_folio list for global reclaim */
-+ struct hlist_nulls_head fifo[MEMCG_NR_GENS][MEMCG_NR_BINS];
-+ /* protects the above */
-+ spinlock_t lock;
-+};
-+
-+void lru_gen_init_pgdat(struct pglist_data *pgdat);
-+
- void lru_gen_init_memcg(struct mem_cgroup *memcg);
- void lru_gen_exit_memcg(struct mem_cgroup *memcg);
--#endif
-+void lru_gen_online_memcg(struct mem_cgroup *memcg);
-+void lru_gen_offline_memcg(struct mem_cgroup *memcg);
-+void lru_gen_release_memcg(struct mem_cgroup *memcg);
-+void lru_gen_rotate_memcg(struct lruvec *lruvec, int op);
-+
-+#else /* !CONFIG_MEMCG */
-+
-+#define MEMCG_NR_GENS 1
-+
-+struct lru_gen_memcg {
-+};
-+
-+static inline void lru_gen_init_pgdat(struct pglist_data *pgdat)
-+{
-+}
-+
-+#endif /* CONFIG_MEMCG */
-
- #else /* !CONFIG_LRU_GEN */
-
-+static inline void lru_gen_init_pgdat(struct pglist_data *pgdat)
-+{
-+}
-+
- static inline void lru_gen_init_lruvec(struct lruvec *lruvec)
- {
- }
-@@ -494,6 +587,7 @@ static inline void lru_gen_look_around(s
- }
-
- #ifdef CONFIG_MEMCG
-+
- static inline void lru_gen_init_memcg(struct mem_cgroup *memcg)
- {
- }
-@@ -501,7 +595,24 @@ static inline void lru_gen_init_memcg(st
- static inline void lru_gen_exit_memcg(struct mem_cgroup *memcg)
- {
- }
--#endif
-+
-+static inline void lru_gen_online_memcg(struct mem_cgroup *memcg)
-+{
-+}
-+
-+static inline void lru_gen_offline_memcg(struct mem_cgroup *memcg)
-+{
-+}
-+
-+static inline void lru_gen_release_memcg(struct mem_cgroup *memcg)
-+{
-+}
-+
-+static inline void lru_gen_rotate_memcg(struct lruvec *lruvec, int op)
-+{
-+}
-+
-+#endif /* CONFIG_MEMCG */
-
- #endif /* CONFIG_LRU_GEN */
-
-@@ -1219,6 +1330,8 @@ typedef struct pglist_data {
- #ifdef CONFIG_LRU_GEN
- /* kswap mm walk data */
- struct lru_gen_mm_walk mm_walk;
-+ /* lru_gen_folio list */
-+ struct lru_gen_memcg memcg_lru;
- #endif
-
- CACHELINE_PADDING(_pad2_);
---- a/mm/memcontrol.c
-+++ b/mm/memcontrol.c
-@@ -477,6 +477,16 @@ static void mem_cgroup_update_tree(struc
- struct mem_cgroup_per_node *mz;
- struct mem_cgroup_tree_per_node *mctz;
-
-+ if (lru_gen_enabled()) {
-+ struct lruvec *lruvec = &memcg->nodeinfo[nid]->lruvec;
-+
-+ /* see the comment on MEMCG_NR_GENS */
-+ if (soft_limit_excess(memcg) && lru_gen_memcg_seg(lruvec) != MEMCG_LRU_HEAD)
-+ lru_gen_rotate_memcg(lruvec, MEMCG_LRU_HEAD);
-+
-+ return;
-+ }
-+
- mctz = soft_limit_tree.rb_tree_per_node[nid];
- if (!mctz)
- return;
-@@ -3524,6 +3534,9 @@ unsigned long mem_cgroup_soft_limit_recl
- struct mem_cgroup_tree_per_node *mctz;
- unsigned long excess;
-
-+ if (lru_gen_enabled())
-+ return 0;
-+
- if (order > 0)
- return 0;
-
-@@ -5387,6 +5400,7 @@ static int mem_cgroup_css_online(struct
- if (unlikely(mem_cgroup_is_root(memcg)))
- queue_delayed_work(system_unbound_wq, &stats_flush_dwork,
- 2UL*HZ);
-+ lru_gen_online_memcg(memcg);
- return 0;
- offline_kmem:
- memcg_offline_kmem(memcg);
-@@ -5418,6 +5432,7 @@ static void mem_cgroup_css_offline(struc
- memcg_offline_kmem(memcg);
- reparent_shrinker_deferred(memcg);
- wb_memcg_offline(memcg);
-+ lru_gen_offline_memcg(memcg);
-
- drain_all_stock(memcg);
-
-@@ -5429,6 +5444,7 @@ static void mem_cgroup_css_released(stru
- struct mem_cgroup *memcg = mem_cgroup_from_css(css);
-
- invalidate_reclaim_iterators(memcg);
-+ lru_gen_release_memcg(memcg);
- }
-
- static void mem_cgroup_css_free(struct cgroup_subsys_state *css)
---- a/mm/page_alloc.c
-+++ b/mm/page_alloc.c
-@@ -7943,6 +7943,7 @@ static void __init free_area_init_node(i
- pgdat_set_deferred_range(pgdat);
-
- free_area_init_core(pgdat);
-+ lru_gen_init_pgdat(pgdat);
- }
-
- static void __init free_area_init_memoryless_node(int nid)
---- a/mm/vmscan.c
-+++ b/mm/vmscan.c
-@@ -54,6 +54,8 @@
- #include <linux/shmem_fs.h>
- #include <linux/ctype.h>
- #include <linux/debugfs.h>
-+#include <linux/rculist_nulls.h>
-+#include <linux/random.h>
-
- #include <asm/tlbflush.h>
- #include <asm/div64.h>
-@@ -134,11 +136,6 @@ struct scan_control {
- /* Always discard instead of demoting to lower tier memory */
- unsigned int no_demotion:1;
-
--#ifdef CONFIG_LRU_GEN
-- /* help kswapd make better choices among multiple memcgs */
-- unsigned long last_reclaimed;
--#endif
--
- /* Allocation order */
- s8 order;
-
-@@ -3160,6 +3157,9 @@ DEFINE_STATIC_KEY_ARRAY_FALSE(lru_gen_ca
- for ((type) = 0; (type) < ANON_AND_FILE; (type)++) \
- for ((zone) = 0; (zone) < MAX_NR_ZONES; (zone)++)
-
-+#define get_memcg_gen(seq) ((seq) % MEMCG_NR_GENS)
-+#define get_memcg_bin(bin) ((bin) % MEMCG_NR_BINS)
-+
- static struct lruvec *get_lruvec(struct mem_cgroup *memcg, int nid)
- {
- struct pglist_data *pgdat = NODE_DATA(nid);
-@@ -4442,8 +4442,7 @@ done:
- if (sc->priority <= DEF_PRIORITY - 2)
- wait_event_killable(lruvec->mm_state.wait,
- max_seq < READ_ONCE(lrugen->max_seq));
--
-- return max_seq < READ_ONCE(lrugen->max_seq);
-+ return false;
- }
-
- VM_WARN_ON_ONCE(max_seq != READ_ONCE(lrugen->max_seq));
-@@ -4516,8 +4515,6 @@ static void lru_gen_age_node(struct pgli
-
- VM_WARN_ON_ONCE(!current_is_kswapd());
-
-- sc->last_reclaimed = sc->nr_reclaimed;
--
- /* check the order to exclude compaction-induced reclaim */
- if (!min_ttl || sc->order || sc->priority == DEF_PRIORITY)
- return;
-@@ -5116,8 +5113,7 @@ static bool should_run_aging(struct lruv
- * 1. Defer try_to_inc_max_seq() to workqueues to reduce latency for memcg
- * reclaim.
- */
--static unsigned long get_nr_to_scan(struct lruvec *lruvec, struct scan_control *sc,
-- bool can_swap)
-+static long get_nr_to_scan(struct lruvec *lruvec, struct scan_control *sc, bool can_swap)
- {
- unsigned long nr_to_scan;
- struct mem_cgroup *memcg = lruvec_memcg(lruvec);
-@@ -5134,10 +5130,8 @@ static unsigned long get_nr_to_scan(stru
- if (sc->priority == DEF_PRIORITY)
- return nr_to_scan;
-
-- try_to_inc_max_seq(lruvec, max_seq, sc, can_swap, false);
--
- /* skip this lruvec as it's low on cold folios */
-- return 0;
-+ return try_to_inc_max_seq(lruvec, max_seq, sc, can_swap, false) ? -1 : 0;
- }
-
- static unsigned long get_nr_to_reclaim(struct scan_control *sc)
-@@ -5146,29 +5140,18 @@ static unsigned long get_nr_to_reclaim(s
- if (!global_reclaim(sc))
- return -1;
-
-- /* discount the previous progress for kswapd */
-- if (current_is_kswapd())
-- return sc->nr_to_reclaim + sc->last_reclaimed;
--
- return max(sc->nr_to_reclaim, compact_gap(sc->order));
- }
-
--static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
-+static bool try_to_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
- {
-- struct blk_plug plug;
-+ long nr_to_scan;
- unsigned long scanned = 0;
- unsigned long nr_to_reclaim = get_nr_to_reclaim(sc);
-
-- lru_add_drain();
--
-- blk_start_plug(&plug);
--
-- set_mm_walk(lruvec_pgdat(lruvec));
--
- while (true) {
- int delta;
- int swappiness;
-- unsigned long nr_to_scan;
-
- if (sc->may_swap)
- swappiness = get_swappiness(lruvec, sc);
-@@ -5178,7 +5161,7 @@ static void lru_gen_shrink_lruvec(struct
- swappiness = 0;
-
- nr_to_scan = get_nr_to_scan(lruvec, sc, swappiness);
-- if (!nr_to_scan)
-+ if (nr_to_scan <= 0)
- break;
-
- delta = evict_folios(lruvec, sc, swappiness);
-@@ -5195,10 +5178,251 @@ static void lru_gen_shrink_lruvec(struct
- cond_resched();
- }
-
-+ /* whether try_to_inc_max_seq() was successful */
-+ return nr_to_scan < 0;
-+}
-+
-+static int shrink_one(struct lruvec *lruvec, struct scan_control *sc)
-+{
-+ bool success;
-+ unsigned long scanned = sc->nr_scanned;
-+ unsigned long reclaimed = sc->nr_reclaimed;
-+ int seg = lru_gen_memcg_seg(lruvec);
-+ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
-+ struct pglist_data *pgdat = lruvec_pgdat(lruvec);
-+
-+ /* see the comment on MEMCG_NR_GENS */
-+ if (!lruvec_is_sizable(lruvec, sc))
-+ return seg != MEMCG_LRU_TAIL ? MEMCG_LRU_TAIL : MEMCG_LRU_YOUNG;
-+
-+ mem_cgroup_calculate_protection(NULL, memcg);
-+
-+ if (mem_cgroup_below_min(memcg))
-+ return MEMCG_LRU_YOUNG;
-+
-+ if (mem_cgroup_below_low(memcg)) {
-+ /* see the comment on MEMCG_NR_GENS */
-+ if (seg != MEMCG_LRU_TAIL)
-+ return MEMCG_LRU_TAIL;
-+
-+ memcg_memory_event(memcg, MEMCG_LOW);
-+ }
-+
-+ success = try_to_shrink_lruvec(lruvec, sc);
-+
-+ shrink_slab(sc->gfp_mask, pgdat->node_id, memcg, sc->priority);
-+
-+ if (!sc->proactive)
-+ vmpressure(sc->gfp_mask, memcg, false, sc->nr_scanned - scanned,
-+ sc->nr_reclaimed - reclaimed);
-+
-+ sc->nr_reclaimed += current->reclaim_state->reclaimed_slab;
-+ current->reclaim_state->reclaimed_slab = 0;
-+
-+ return success ? MEMCG_LRU_YOUNG : 0;
-+}
-+
-+#ifdef CONFIG_MEMCG
-+
-+static void shrink_many(struct pglist_data *pgdat, struct scan_control *sc)
-+{
-+ int gen;
-+ int bin;
-+ int first_bin;
-+ struct lruvec *lruvec;
-+ struct lru_gen_folio *lrugen;
-+ const struct hlist_nulls_node *pos;
-+ int op = 0;
-+ struct mem_cgroup *memcg = NULL;
-+ unsigned long nr_to_reclaim = get_nr_to_reclaim(sc);
-+
-+ bin = first_bin = get_random_u32_below(MEMCG_NR_BINS);
-+restart:
-+ gen = get_memcg_gen(READ_ONCE(pgdat->memcg_lru.seq));
-+
-+ rcu_read_lock();
-+
-+ hlist_nulls_for_each_entry_rcu(lrugen, pos, &pgdat->memcg_lru.fifo[gen][bin], list) {
-+ if (op)
-+ lru_gen_rotate_memcg(lruvec, op);
-+
-+ mem_cgroup_put(memcg);
-+
-+ lruvec = container_of(lrugen, struct lruvec, lrugen);
-+ memcg = lruvec_memcg(lruvec);
-+
-+ if (!mem_cgroup_tryget(memcg)) {
-+ op = 0;
-+ memcg = NULL;
-+ continue;
-+ }
-+
-+ rcu_read_unlock();
-+
-+ op = shrink_one(lruvec, sc);
-+
-+ if (sc->nr_reclaimed >= nr_to_reclaim)
-+ goto success;
-+
-+ rcu_read_lock();
-+ }
-+
-+ rcu_read_unlock();
-+
-+ /* restart if raced with lru_gen_rotate_memcg() */
-+ if (gen != get_nulls_value(pos))
-+ goto restart;
-+
-+ /* try the rest of the bins of the current generation */
-+ bin = get_memcg_bin(bin + 1);
-+ if (bin != first_bin)
-+ goto restart;
-+success:
-+ if (op)
-+ lru_gen_rotate_memcg(lruvec, op);
-+
-+ mem_cgroup_put(memcg);
-+}
-+
-+static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
-+{
-+ struct blk_plug plug;
-+
-+ VM_WARN_ON_ONCE(global_reclaim(sc));
-+
-+ lru_add_drain();
-+
-+ blk_start_plug(&plug);
-+
-+ set_mm_walk(lruvec_pgdat(lruvec));
-+
-+ if (try_to_shrink_lruvec(lruvec, sc))
-+ lru_gen_rotate_memcg(lruvec, MEMCG_LRU_YOUNG);
-+
-+ clear_mm_walk();
-+
-+ blk_finish_plug(&plug);
-+}
-+
-+#else /* !CONFIG_MEMCG */
-+
-+static void shrink_many(struct pglist_data *pgdat, struct scan_control *sc)
-+{
-+ BUILD_BUG();
-+}
-+
-+static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
-+{
-+ BUILD_BUG();
-+}
-+
-+#endif
-+
-+static void set_initial_priority(struct pglist_data *pgdat, struct scan_control *sc)
-+{
-+ int priority;
-+ unsigned long reclaimable;
-+ struct lruvec *lruvec = mem_cgroup_lruvec(NULL, pgdat);
-+
-+ if (sc->priority != DEF_PRIORITY || sc->nr_to_reclaim < MIN_LRU_BATCH)
-+ return;
-+ /*
-+ * Determine the initial priority based on ((total / MEMCG_NR_GENS) >>
-+ * priority) * reclaimed_to_scanned_ratio = nr_to_reclaim, where the
-+ * estimated reclaimed_to_scanned_ratio = inactive / total.
-+ */
-+ reclaimable = node_page_state(pgdat, NR_INACTIVE_FILE);
-+ if (get_swappiness(lruvec, sc))
-+ reclaimable += node_page_state(pgdat, NR_INACTIVE_ANON);
-+
-+ reclaimable /= MEMCG_NR_GENS;
-+
-+ /* round down reclaimable and round up sc->nr_to_reclaim */
-+ priority = fls_long(reclaimable) - 1 - fls_long(sc->nr_to_reclaim - 1);
-+
-+ sc->priority = clamp(priority, 0, DEF_PRIORITY);
-+}
-+
-+static void lru_gen_shrink_node(struct pglist_data *pgdat, struct scan_control *sc)
-+{
-+ struct blk_plug plug;
-+ unsigned long reclaimed = sc->nr_reclaimed;
-+
-+ VM_WARN_ON_ONCE(!global_reclaim(sc));
-+
-+ lru_add_drain();
-+
-+ blk_start_plug(&plug);
-+
-+ set_mm_walk(pgdat);
-+
-+ set_initial_priority(pgdat, sc);
-+
-+ if (current_is_kswapd())
-+ sc->nr_reclaimed = 0;
-+
-+ if (mem_cgroup_disabled())
-+ shrink_one(&pgdat->__lruvec, sc);
-+ else
-+ shrink_many(pgdat, sc);
-+
-+ if (current_is_kswapd())
-+ sc->nr_reclaimed += reclaimed;
-+
- clear_mm_walk();
-
- blk_finish_plug(&plug);
-+
-+ /* kswapd should never fail */
-+ pgdat->kswapd_failures = 0;
-+}
-+
-+#ifdef CONFIG_MEMCG
-+void lru_gen_rotate_memcg(struct lruvec *lruvec, int op)
-+{
-+ int seg;
-+ int old, new;
-+ int bin = get_random_u32_below(MEMCG_NR_BINS);
-+ struct pglist_data *pgdat = lruvec_pgdat(lruvec);
-+
-+ spin_lock(&pgdat->memcg_lru.lock);
-+
-+ VM_WARN_ON_ONCE(hlist_nulls_unhashed(&lruvec->lrugen.list));
-+
-+ seg = 0;
-+ new = old = lruvec->lrugen.gen;
-+
-+ /* see the comment on MEMCG_NR_GENS */
-+ if (op == MEMCG_LRU_HEAD)
-+ seg = MEMCG_LRU_HEAD;
-+ else if (op == MEMCG_LRU_TAIL)
-+ seg = MEMCG_LRU_TAIL;
-+ else if (op == MEMCG_LRU_OLD)
-+ new = get_memcg_gen(pgdat->memcg_lru.seq);
-+ else if (op == MEMCG_LRU_YOUNG)
-+ new = get_memcg_gen(pgdat->memcg_lru.seq + 1);
-+ else
-+ VM_WARN_ON_ONCE(true);
-+
-+ hlist_nulls_del_rcu(&lruvec->lrugen.list);
-+
-+ if (op == MEMCG_LRU_HEAD || op == MEMCG_LRU_OLD)
-+ hlist_nulls_add_head_rcu(&lruvec->lrugen.list, &pgdat->memcg_lru.fifo[new][bin]);
-+ else
-+ hlist_nulls_add_tail_rcu(&lruvec->lrugen.list, &pgdat->memcg_lru.fifo[new][bin]);
-+
-+ pgdat->memcg_lru.nr_memcgs[old]--;
-+ pgdat->memcg_lru.nr_memcgs[new]++;
-+
-+ lruvec->lrugen.gen = new;
-+ WRITE_ONCE(lruvec->lrugen.seg, seg);
-+
-+ if (!pgdat->memcg_lru.nr_memcgs[old] && old == get_memcg_gen(pgdat->memcg_lru.seq))
-+ WRITE_ONCE(pgdat->memcg_lru.seq, pgdat->memcg_lru.seq + 1);
-+
-+ spin_unlock(&pgdat->memcg_lru.lock);
- }
-+#endif
-
- /******************************************************************************
- * state change
-@@ -5656,11 +5880,11 @@ static int run_cmd(char cmd, int memcg_i
-
- if (!mem_cgroup_disabled()) {
- rcu_read_lock();
-+
- memcg = mem_cgroup_from_id(memcg_id);
--#ifdef CONFIG_MEMCG
-- if (memcg && !css_tryget(&memcg->css))
-+ if (!mem_cgroup_tryget(memcg))
- memcg = NULL;
--#endif
-+
- rcu_read_unlock();
-
- if (!memcg)
-@@ -5808,6 +6032,19 @@ void lru_gen_init_lruvec(struct lruvec *
- }
-
- #ifdef CONFIG_MEMCG
-+
-+void lru_gen_init_pgdat(struct pglist_data *pgdat)
-+{
-+ int i, j;
-+
-+ spin_lock_init(&pgdat->memcg_lru.lock);
-+
-+ for (i = 0; i < MEMCG_NR_GENS; i++) {
-+ for (j = 0; j < MEMCG_NR_BINS; j++)
-+ INIT_HLIST_NULLS_HEAD(&pgdat->memcg_lru.fifo[i][j], i);
-+ }
-+}
-+
- void lru_gen_init_memcg(struct mem_cgroup *memcg)
- {
- INIT_LIST_HEAD(&memcg->mm_list.fifo);
-@@ -5831,7 +6068,69 @@ void lru_gen_exit_memcg(struct mem_cgrou
- }
- }
- }
--#endif
-+
-+void lru_gen_online_memcg(struct mem_cgroup *memcg)
-+{
-+ int gen;
-+ int nid;
-+ int bin = get_random_u32_below(MEMCG_NR_BINS);
-+
-+ for_each_node(nid) {
-+ struct pglist_data *pgdat = NODE_DATA(nid);
-+ struct lruvec *lruvec = get_lruvec(memcg, nid);
-+
-+ spin_lock(&pgdat->memcg_lru.lock);
-+
-+ VM_WARN_ON_ONCE(!hlist_nulls_unhashed(&lruvec->lrugen.list));
-+
-+ gen = get_memcg_gen(pgdat->memcg_lru.seq);
-+
-+ hlist_nulls_add_tail_rcu(&lruvec->lrugen.list, &pgdat->memcg_lru.fifo[gen][bin]);
-+ pgdat->memcg_lru.nr_memcgs[gen]++;
-+
-+ lruvec->lrugen.gen = gen;
-+
-+ spin_unlock(&pgdat->memcg_lru.lock);
-+ }
-+}
-+
-+void lru_gen_offline_memcg(struct mem_cgroup *memcg)
-+{
-+ int nid;
-+
-+ for_each_node(nid) {
-+ struct lruvec *lruvec = get_lruvec(memcg, nid);
-+
-+ lru_gen_rotate_memcg(lruvec, MEMCG_LRU_OLD);
-+ }
-+}
-+
-+void lru_gen_release_memcg(struct mem_cgroup *memcg)
-+{
-+ int gen;
-+ int nid;
-+
-+ for_each_node(nid) {
-+ struct pglist_data *pgdat = NODE_DATA(nid);
-+ struct lruvec *lruvec = get_lruvec(memcg, nid);
-+
-+ spin_lock(&pgdat->memcg_lru.lock);
-+
-+ VM_WARN_ON_ONCE(hlist_nulls_unhashed(&lruvec->lrugen.list));
-+
-+ gen = lruvec->lrugen.gen;
-+
-+ hlist_nulls_del_rcu(&lruvec->lrugen.list);
-+ pgdat->memcg_lru.nr_memcgs[gen]--;
-+
-+ if (!pgdat->memcg_lru.nr_memcgs[gen] && gen == get_memcg_gen(pgdat->memcg_lru.seq))
-+ WRITE_ONCE(pgdat->memcg_lru.seq, pgdat->memcg_lru.seq + 1);
-+
-+ spin_unlock(&pgdat->memcg_lru.lock);
-+ }
-+}
-+
-+#endif /* CONFIG_MEMCG */
-
- static int __init init_lru_gen(void)
- {
-@@ -5858,6 +6157,10 @@ static void lru_gen_shrink_lruvec(struct
- {
- }
-
-+static void lru_gen_shrink_node(struct pglist_data *pgdat, struct scan_control *sc)
-+{
-+}
-+
- #endif /* CONFIG_LRU_GEN */
-
- static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
-@@ -5871,7 +6174,7 @@ static void shrink_lruvec(struct lruvec
- bool proportional_reclaim;
- struct blk_plug plug;
-
-- if (lru_gen_enabled()) {
-+ if (lru_gen_enabled() && !global_reclaim(sc)) {
- lru_gen_shrink_lruvec(lruvec, sc);
- return;
- }
-@@ -6114,6 +6417,11 @@ static void shrink_node(pg_data_t *pgdat
- struct lruvec *target_lruvec;
- bool reclaimable = false;
-
-+ if (lru_gen_enabled() && global_reclaim(sc)) {
-+ lru_gen_shrink_node(pgdat, sc);
-+ return;
-+ }
-+
- target_lruvec = mem_cgroup_lruvec(sc->target_mem_cgroup, pgdat);
-
- again:
+++ /dev/null
-From 11b14ee8cbbbebd8204609076a9327a1171cd253 Mon Sep 17 00:00:00 2001
-From: Yu Zhao <yuzhao@google.com>
-Date: Wed, 21 Dec 2022 21:19:05 -0700
-Subject: [PATCH 07/19] BACKPORT: mm: multi-gen LRU: clarify scan_control flags
-
-Among the flags in scan_control:
-1. sc->may_swap, which indicates swap constraint due to memsw.max, is
- supported as usual.
-2. sc->proactive, which indicates reclaim by memory.reclaim, may not
- opportunistically skip the aging path, since it is considered less
- latency sensitive.
-3. !(sc->gfp_mask & __GFP_IO), which indicates IO constraint, lowers
- swappiness to prioritize file LRU, since clean file folios are more
- likely to exist.
-4. sc->may_writepage and sc->may_unmap, which indicates opportunistic
- reclaim, are rejected, since unmapped clean folios are already
- prioritized. Scanning for more of them is likely futile and can
- cause high reclaim latency when there is a large number of memcgs.
-
-The rest are handled by the existing code.
-
-Link: https://lkml.kernel.org/r/20221222041905.2431096-8-yuzhao@google.com
-Signed-off-by: Yu Zhao <yuzhao@google.com>
-Cc: Johannes Weiner <hannes@cmpxchg.org>
-Cc: Jonathan Corbet <corbet@lwn.net>
-Cc: Michael Larabel <Michael@MichaelLarabel.com>
-Cc: Michal Hocko <mhocko@kernel.org>
-Cc: Mike Rapoport <rppt@kernel.org>
-Cc: Roman Gushchin <roman.gushchin@linux.dev>
-Cc: Suren Baghdasaryan <surenb@google.com>
-Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
-Bug: 274865848
-(cherry picked from commit e9d4e1ee788097484606c32122f146d802a9c5fb)
-[TJ: Resolved conflict with older function signature for min_cgroup_below_min, and over
-cdded861182142ac4488a4d64c571107aeb77f53 ("ANDROID: MGLRU: Don't skip anon reclaim if swap low")]
-Change-Id: Ic2e779eaf4e91a3921831b4e2fa10c740dc59d50
-Signed-off-by: T.J. Mercier <tjmercier@google.com>
----
- mm/vmscan.c | 55 +++++++++++++++++++++++++++--------------------------
- 1 file changed, 28 insertions(+), 27 deletions(-)
-
---- a/mm/vmscan.c
-+++ b/mm/vmscan.c
-@@ -3185,6 +3185,9 @@ static int get_swappiness(struct lruvec
- struct mem_cgroup *memcg = lruvec_memcg(lruvec);
- struct pglist_data *pgdat = lruvec_pgdat(lruvec);
-
-+ if (!sc->may_swap)
-+ return 0;
-+
- if (!can_demote(pgdat->node_id, sc) &&
- mem_cgroup_get_nr_swap_pages(memcg) < MIN_LRU_BATCH)
- return 0;
-@@ -4223,7 +4226,7 @@ static void walk_mm(struct lruvec *lruve
- } while (err == -EAGAIN);
- }
-
--static struct lru_gen_mm_walk *set_mm_walk(struct pglist_data *pgdat)
-+static struct lru_gen_mm_walk *set_mm_walk(struct pglist_data *pgdat, bool force_alloc)
- {
- struct lru_gen_mm_walk *walk = current->reclaim_state->mm_walk;
-
-@@ -4231,7 +4234,7 @@ static struct lru_gen_mm_walk *set_mm_wa
- VM_WARN_ON_ONCE(walk);
-
- walk = &pgdat->mm_walk;
-- } else if (!pgdat && !walk) {
-+ } else if (!walk && force_alloc) {
- VM_WARN_ON_ONCE(current_is_kswapd());
-
- walk = kzalloc(sizeof(*walk), __GFP_HIGH | __GFP_NOMEMALLOC | __GFP_NOWARN);
-@@ -4419,7 +4422,7 @@ static bool try_to_inc_max_seq(struct lr
- goto done;
- }
-
-- walk = set_mm_walk(NULL);
-+ walk = set_mm_walk(NULL, true);
- if (!walk) {
- success = iterate_mm_list_nowalk(lruvec, max_seq);
- goto done;
-@@ -4488,8 +4491,6 @@ static bool lruvec_is_reclaimable(struct
- struct mem_cgroup *memcg = lruvec_memcg(lruvec);
- DEFINE_MIN_SEQ(lruvec);
-
-- VM_WARN_ON_ONCE(sc->memcg_low_reclaim);
--
- /* see the comment on lru_gen_folio */
- gen = lru_gen_from_seq(min_seq[LRU_GEN_FILE]);
- birth = READ_ONCE(lruvec->lrugen.timestamps[gen]);
-@@ -4753,12 +4754,8 @@ static bool isolate_folio(struct lruvec
- {
- bool success;
-
-- /* unmapping inhibited */
-- if (!sc->may_unmap && folio_mapped(folio))
-- return false;
--
- /* swapping inhibited */
-- if (!(sc->may_writepage && (sc->gfp_mask & __GFP_IO)) &&
-+ if (!(sc->gfp_mask & __GFP_IO) &&
- (folio_test_dirty(folio) ||
- (folio_test_anon(folio) && !folio_test_swapcache(folio))))
- return false;
-@@ -4857,9 +4854,8 @@ static int scan_folios(struct lruvec *lr
- __count_vm_events(PGSCAN_ANON + type, isolated);
-
- /*
-- * There might not be eligible pages due to reclaim_idx, may_unmap and
-- * may_writepage. Check the remaining to prevent livelock if it's not
-- * making progress.
-+ * There might not be eligible folios due to reclaim_idx. Check the
-+ * remaining to prevent livelock if it's not making progress.
- */
- return isolated || !remaining ? scanned : 0;
- }
-@@ -5119,8 +5115,7 @@ static long get_nr_to_scan(struct lruvec
- struct mem_cgroup *memcg = lruvec_memcg(lruvec);
- DEFINE_MAX_SEQ(lruvec);
-
-- if (mem_cgroup_below_min(memcg) ||
-- (mem_cgroup_below_low(memcg) && !sc->memcg_low_reclaim))
-+ if (mem_cgroup_below_min(memcg))
- return 0;
-
- if (!should_run_aging(lruvec, max_seq, sc, can_swap, &nr_to_scan))
-@@ -5148,17 +5143,14 @@ static bool try_to_shrink_lruvec(struct
- long nr_to_scan;
- unsigned long scanned = 0;
- unsigned long nr_to_reclaim = get_nr_to_reclaim(sc);
-+ int swappiness = get_swappiness(lruvec, sc);
-+
-+ /* clean file folios are more likely to exist */
-+ if (swappiness && !(sc->gfp_mask & __GFP_IO))
-+ swappiness = 1;
-
- while (true) {
- int delta;
-- int swappiness;
--
-- if (sc->may_swap)
-- swappiness = get_swappiness(lruvec, sc);
-- else if (!cgroup_reclaim(sc) && get_swappiness(lruvec, sc))
-- swappiness = 1;
-- else
-- swappiness = 0;
-
- nr_to_scan = get_nr_to_scan(lruvec, sc, swappiness);
- if (nr_to_scan <= 0)
-@@ -5289,12 +5281,13 @@ static void lru_gen_shrink_lruvec(struct
- struct blk_plug plug;
-
- VM_WARN_ON_ONCE(global_reclaim(sc));
-+ VM_WARN_ON_ONCE(!sc->may_writepage || !sc->may_unmap);
-
- lru_add_drain();
-
- blk_start_plug(&plug);
-
-- set_mm_walk(lruvec_pgdat(lruvec));
-+ set_mm_walk(NULL, sc->proactive);
-
- if (try_to_shrink_lruvec(lruvec, sc))
- lru_gen_rotate_memcg(lruvec, MEMCG_LRU_YOUNG);
-@@ -5350,11 +5343,19 @@ static void lru_gen_shrink_node(struct p
-
- VM_WARN_ON_ONCE(!global_reclaim(sc));
-
-+ /*
-+ * Unmapped clean folios are already prioritized. Scanning for more of
-+ * them is likely futile and can cause high reclaim latency when there
-+ * is a large number of memcgs.
-+ */
-+ if (!sc->may_writepage || !sc->may_unmap)
-+ goto done;
-+
- lru_add_drain();
-
- blk_start_plug(&plug);
-
-- set_mm_walk(pgdat);
-+ set_mm_walk(pgdat, sc->proactive);
-
- set_initial_priority(pgdat, sc);
-
-@@ -5372,7 +5373,7 @@ static void lru_gen_shrink_node(struct p
- clear_mm_walk();
-
- blk_finish_plug(&plug);
--
-+done:
- /* kswapd should never fail */
- pgdat->kswapd_failures = 0;
- }
-@@ -5944,7 +5945,7 @@ static ssize_t lru_gen_seq_write(struct
- set_task_reclaim_state(current, &sc.reclaim_state);
- flags = memalloc_noreclaim_save();
- blk_start_plug(&plug);
-- if (!set_mm_walk(NULL)) {
-+ if (!set_mm_walk(NULL, true)) {
- err = -ENOMEM;
- goto done;
- }
+++ /dev/null
-From 25887d48dff860751a06caa4188bfaf6bfb6e4b2 Mon Sep 17 00:00:00 2001
-From: Yu Zhao <yuzhao@google.com>
-Date: Wed, 21 Dec 2022 21:19:06 -0700
-Subject: [PATCH 08/19] UPSTREAM: mm: multi-gen LRU: simplify
- arch_has_hw_pte_young() check
-
-Scanning page tables when hardware does not set the accessed bit has
-no real use cases.
-
-Link: https://lkml.kernel.org/r/20221222041905.2431096-9-yuzhao@google.com
-Signed-off-by: Yu Zhao <yuzhao@google.com>
-Cc: Johannes Weiner <hannes@cmpxchg.org>
-Cc: Jonathan Corbet <corbet@lwn.net>
-Cc: Michael Larabel <Michael@MichaelLarabel.com>
-Cc: Michal Hocko <mhocko@kernel.org>
-Cc: Mike Rapoport <rppt@kernel.org>
-Cc: Roman Gushchin <roman.gushchin@linux.dev>
-Cc: Suren Baghdasaryan <surenb@google.com>
-Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
-Bug: 274865848
-(cherry picked from commit f386e9314025ea99dae639ed2032560a92081430)
-Change-Id: I84d97ab665b4e3bb862a9bc7d72f50dea7191a6b
-Signed-off-by: T.J. Mercier <tjmercier@google.com>
----
- mm/vmscan.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/mm/vmscan.c
-+++ b/mm/vmscan.c
-@@ -4417,7 +4417,7 @@ static bool try_to_inc_max_seq(struct lr
- * handful of PTEs. Spreading the work out over a period of time usually
- * is less efficient, but it avoids bursty page faults.
- */
-- if (!force_scan && !(arch_has_hw_pte_young() && get_cap(LRU_GEN_MM_WALK))) {
-+ if (!arch_has_hw_pte_young() || !get_cap(LRU_GEN_MM_WALK)) {
- success = iterate_mm_list_nowalk(lruvec, max_seq);
- goto done;
- }
+++ /dev/null
-From 620b0ee94455e48d124414cd06d8a53f69fb6453 Mon Sep 17 00:00:00 2001
-From: Yu Zhao <yuzhao@google.com>
-Date: Mon, 13 Feb 2023 00:53:22 -0700
-Subject: [PATCH 09/19] UPSTREAM: mm: multi-gen LRU: avoid futile retries
-
-Recall that the per-node memcg LRU has two generations and they alternate
-when the last memcg (of a given node) is moved from one to the other.
-Each generation is also sharded into multiple bins to improve scalability.
-A reclaimer starts with a random bin (in the old generation) and, if it
-fails, it will retry, i.e., to try the rest of the bins.
-
-If a reclaimer fails with the last memcg, it should move this memcg to the
-young generation first, which causes the generations to alternate, and
-then retry. Otherwise, the retries will be futile because all other bins
-are empty.
-
-Link: https://lkml.kernel.org/r/20230213075322.1416966-1-yuzhao@google.com
-Fixes: e4dde56cd208 ("mm: multi-gen LRU: per-node lru_gen_folio lists")
-Signed-off-by: Yu Zhao <yuzhao@google.com>
-Reported-by: T.J. Mercier <tjmercier@google.com>
-Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
-Bug: 274865848
-(cherry picked from commit 9f550d78b40da21b4da515db4c37d8d7b12aa1a6)
-Change-Id: Ie92535676b005ec9e7987632b742fdde8d54436f
-Signed-off-by: T.J. Mercier <tjmercier@google.com>
----
- mm/vmscan.c | 25 +++++++++++++++----------
- 1 file changed, 15 insertions(+), 10 deletions(-)
-
---- a/mm/vmscan.c
-+++ b/mm/vmscan.c
-@@ -5218,18 +5218,20 @@ static int shrink_one(struct lruvec *lru
-
- static void shrink_many(struct pglist_data *pgdat, struct scan_control *sc)
- {
-+ int op;
- int gen;
- int bin;
- int first_bin;
- struct lruvec *lruvec;
- struct lru_gen_folio *lrugen;
-+ struct mem_cgroup *memcg;
- const struct hlist_nulls_node *pos;
-- int op = 0;
-- struct mem_cgroup *memcg = NULL;
- unsigned long nr_to_reclaim = get_nr_to_reclaim(sc);
-
- bin = first_bin = get_random_u32_below(MEMCG_NR_BINS);
- restart:
-+ op = 0;
-+ memcg = NULL;
- gen = get_memcg_gen(READ_ONCE(pgdat->memcg_lru.seq));
-
- rcu_read_lock();
-@@ -5253,14 +5255,22 @@ restart:
-
- op = shrink_one(lruvec, sc);
-
-- if (sc->nr_reclaimed >= nr_to_reclaim)
-- goto success;
--
- rcu_read_lock();
-+
-+ if (sc->nr_reclaimed >= nr_to_reclaim)
-+ break;
- }
-
- rcu_read_unlock();
-
-+ if (op)
-+ lru_gen_rotate_memcg(lruvec, op);
-+
-+ mem_cgroup_put(memcg);
-+
-+ if (sc->nr_reclaimed >= nr_to_reclaim)
-+ return;
-+
- /* restart if raced with lru_gen_rotate_memcg() */
- if (gen != get_nulls_value(pos))
- goto restart;
-@@ -5269,11 +5279,6 @@ restart:
- bin = get_memcg_bin(bin + 1);
- if (bin != first_bin)
- goto restart;
--success:
-- if (op)
-- lru_gen_rotate_memcg(lruvec, op);
--
-- mem_cgroup_put(memcg);
- }
-
- static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
+++ /dev/null
-From 70d216c71ff5c5b17dd1da6294f97b91fb6aba7a Mon Sep 17 00:00:00 2001
-From: Yu Zhao <yuzhao@google.com>
-Date: Fri, 30 Dec 2022 14:52:51 -0700
-Subject: [PATCH 10/19] UPSTREAM: mm: add vma_has_recency()
-
-Add vma_has_recency() to indicate whether a VMA may exhibit temporal
-locality that the LRU algorithm relies on.
-
-This function returns false for VMAs marked by VM_SEQ_READ or
-VM_RAND_READ. While the former flag indicates linear access, i.e., a
-special case of spatial locality, both flags indicate a lack of temporal
-locality, i.e., the reuse of an area within a relatively small duration.
-
-"Recency" is chosen over "locality" to avoid confusion between temporal
-and spatial localities.
-
-Before this patch, the active/inactive LRU only ignored the accessed bit
-from VMAs marked by VM_SEQ_READ. After this patch, the active/inactive
-LRU and MGLRU share the same logic: they both ignore the accessed bit if
-vma_has_recency() returns false.
-
-For the active/inactive LRU, the following fio test showed a [6, 8]%
-increase in IOPS when randomly accessing mapped files under memory
-pressure.
-
- kb=$(awk '/MemTotal/ { print $2 }' /proc/meminfo)
- kb=$((kb - 8*1024*1024))
-
- modprobe brd rd_nr=1 rd_size=$kb
- dd if=/dev/zero of=/dev/ram0 bs=1M
-
- mkfs.ext4 /dev/ram0
- mount /dev/ram0 /mnt/
- swapoff -a
-
- fio --name=test --directory=/mnt/ --ioengine=mmap --numjobs=8 \
- --size=8G --rw=randrw --time_based --runtime=10m \
- --group_reporting
-
-The discussion that led to this patch is here [1]. Additional test
-results are available in that thread.
-
-[1] https://lore.kernel.org/r/Y31s%2FK8T85jh05wH@google.com/
-
-Link: https://lkml.kernel.org/r/20221230215252.2628425-1-yuzhao@google.com
-Change-Id: I291dcb795197659e40e46539cd32b857677c34ad
-Signed-off-by: Yu Zhao <yuzhao@google.com>
-Cc: Alexander Viro <viro@zeniv.linux.org.uk>
-Cc: Andrea Righi <andrea.righi@canonical.com>
-Cc: Johannes Weiner <hannes@cmpxchg.org>
-Cc: Michael Larabel <Michael@MichaelLarabel.com>
-Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
-(cherry picked from commit 8788f6781486769d9598dcaedc3fe0eb12fc3e59)
-Bug: 274865848
-Signed-off-by: T.J. Mercier <tjmercier@google.com>
----
- include/linux/mm_inline.h | 8 ++++++++
- mm/memory.c | 7 +++----
- mm/rmap.c | 42 +++++++++++++++++----------------------
- mm/vmscan.c | 5 ++++-
- 4 files changed, 33 insertions(+), 29 deletions(-)
-
---- a/include/linux/mm_inline.h
-+++ b/include/linux/mm_inline.h
-@@ -600,4 +600,12 @@ pte_install_uffd_wp_if_needed(struct vm_
- #endif
- }
-
-+static inline bool vma_has_recency(struct vm_area_struct *vma)
-+{
-+ if (vma->vm_flags & (VM_SEQ_READ | VM_RAND_READ))
-+ return false;
-+
-+ return true;
-+}
-+
- #endif
---- a/mm/memory.c
-+++ b/mm/memory.c
-@@ -1445,8 +1445,7 @@ again:
- force_flush = 1;
- set_page_dirty(page);
- }
-- if (pte_young(ptent) &&
-- likely(!(vma->vm_flags & VM_SEQ_READ)))
-+ if (pte_young(ptent) && likely(vma_has_recency(vma)))
- mark_page_accessed(page);
- }
- rss[mm_counter(page)]--;
-@@ -5219,8 +5218,8 @@ static inline void mm_account_fault(stru
- #ifdef CONFIG_LRU_GEN
- static void lru_gen_enter_fault(struct vm_area_struct *vma)
- {
-- /* the LRU algorithm doesn't apply to sequential or random reads */
-- current->in_lru_fault = !(vma->vm_flags & (VM_SEQ_READ | VM_RAND_READ));
-+ /* the LRU algorithm only applies to accesses with recency */
-+ current->in_lru_fault = vma_has_recency(vma);
- }
-
- static void lru_gen_exit_fault(void)
---- a/mm/rmap.c
-+++ b/mm/rmap.c
-@@ -823,25 +823,14 @@ static bool folio_referenced_one(struct
- }
-
- if (pvmw.pte) {
-- if (lru_gen_enabled() && pte_young(*pvmw.pte) &&
-- !(vma->vm_flags & (VM_SEQ_READ | VM_RAND_READ))) {
-+ if (lru_gen_enabled() && pte_young(*pvmw.pte)) {
- lru_gen_look_around(&pvmw);
- referenced++;
- }
-
- if (ptep_clear_flush_young_notify(vma, address,
-- pvmw.pte)) {
-- /*
-- * Don't treat a reference through
-- * a sequentially read mapping as such.
-- * If the folio has been used in another mapping,
-- * we will catch it; if this other mapping is
-- * already gone, the unmap path will have set
-- * the referenced flag or activated the folio.
-- */
-- if (likely(!(vma->vm_flags & VM_SEQ_READ)))
-- referenced++;
-- }
-+ pvmw.pte))
-+ referenced++;
- } else if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) {
- if (pmdp_clear_flush_young_notify(vma, address,
- pvmw.pmd))
-@@ -875,7 +864,20 @@ static bool invalid_folio_referenced_vma
- struct folio_referenced_arg *pra = arg;
- struct mem_cgroup *memcg = pra->memcg;
-
-- if (!mm_match_cgroup(vma->vm_mm, memcg))
-+ /*
-+ * Ignore references from this mapping if it has no recency. If the
-+ * folio has been used in another mapping, we will catch it; if this
-+ * other mapping is already gone, the unmap path will have set the
-+ * referenced flag or activated the folio in zap_pte_range().
-+ */
-+ if (!vma_has_recency(vma))
-+ return true;
-+
-+ /*
-+ * If we are reclaiming on behalf of a cgroup, skip counting on behalf
-+ * of references from different cgroups.
-+ */
-+ if (memcg && !mm_match_cgroup(vma->vm_mm, memcg))
- return true;
-
- return false;
-@@ -906,6 +908,7 @@ int folio_referenced(struct folio *folio
- .arg = (void *)&pra,
- .anon_lock = folio_lock_anon_vma_read,
- .try_lock = true,
-+ .invalid_vma = invalid_folio_referenced_vma,
- };
-
- *vm_flags = 0;
-@@ -921,15 +924,6 @@ int folio_referenced(struct folio *folio
- return 1;
- }
-
-- /*
-- * If we are reclaiming on behalf of a cgroup, skip
-- * counting on behalf of references from different
-- * cgroups
-- */
-- if (memcg) {
-- rwc.invalid_vma = invalid_folio_referenced_vma;
-- }
--
- rmap_walk(folio, &rwc);
- *vm_flags = pra.vm_flags;
-
---- a/mm/vmscan.c
-+++ b/mm/vmscan.c
-@@ -3778,7 +3778,10 @@ static int should_skip_vma(unsigned long
- if (is_vm_hugetlb_page(vma))
- return true;
-
-- if (vma->vm_flags & (VM_LOCKED | VM_SPECIAL | VM_SEQ_READ | VM_RAND_READ))
-+ if (!vma_has_recency(vma))
-+ return true;
-+
-+ if (vma->vm_flags & (VM_LOCKED | VM_SPECIAL))
- return true;
-
- if (vma == get_gate_vma(vma->vm_mm))
+++ /dev/null
-From 9ca4e437a24dfc4ec6c362f319eb9850b9eca497 Mon Sep 17 00:00:00 2001
-From: Yu Zhao <yuzhao@google.com>
-Date: Fri, 30 Dec 2022 14:52:52 -0700
-Subject: [PATCH 11/19] UPSTREAM: mm: support POSIX_FADV_NOREUSE
-
-This patch adds POSIX_FADV_NOREUSE to vma_has_recency() so that the LRU
-algorithm can ignore access to mapped files marked by this flag.
-
-The advantages of POSIX_FADV_NOREUSE are:
-1. Unlike MADV_SEQUENTIAL and MADV_RANDOM, it does not alter the
- default readahead behavior.
-2. Unlike MADV_SEQUENTIAL and MADV_RANDOM, it does not split VMAs and
- therefore does not take mmap_lock.
-3. Unlike MADV_COLD, setting it has a negligible cost, regardless of
- how many pages it affects.
-
-Its limitations are:
-1. Like POSIX_FADV_RANDOM and POSIX_FADV_SEQUENTIAL, it currently does
- not support range. IOW, its scope is the entire file.
-2. It currently does not ignore access through file descriptors.
- Specifically, for the active/inactive LRU, given a file page shared
- by two users and one of them having set POSIX_FADV_NOREUSE on the
- file, this page will be activated upon the second user accessing
- it. This corner case can be covered by checking POSIX_FADV_NOREUSE
- before calling folio_mark_accessed() on the read path. But it is
- considered not worth the effort.
-
-There have been a few attempts to support POSIX_FADV_NOREUSE, e.g., [1].
-This time the goal is to fill a niche: a few desktop applications, e.g.,
-large file transferring and video encoding/decoding, want fast file
-streaming with mmap() rather than direct IO. Among those applications, an
-SVT-AV1 regression was reported when running with MGLRU [2]. The
-following test can reproduce that regression.
-
- kb=$(awk '/MemTotal/ { print $2 }' /proc/meminfo)
- kb=$((kb - 8*1024*1024))
-
- modprobe brd rd_nr=1 rd_size=$kb
- dd if=/dev/zero of=/dev/ram0 bs=1M
-
- mkfs.ext4 /dev/ram0
- mount /dev/ram0 /mnt/
- swapoff -a
-
- fallocate -l 8G /mnt/swapfile
- mkswap /mnt/swapfile
- swapon /mnt/swapfile
-
- wget http://ultravideo.cs.tut.fi/video/Bosphorus_3840x2160_120fps_420_8bit_YUV_Y4M.7z
- 7z e -o/mnt/ Bosphorus_3840x2160_120fps_420_8bit_YUV_Y4M.7z
- SvtAv1EncApp --preset 12 -w 3840 -h 2160 \
- -i /mnt/Bosphorus_3840x2160.y4m
-
-For MGLRU, the following change showed a [9-11]% increase in FPS,
-which makes it on par with the active/inactive LRU.
-
- patch Source/App/EncApp/EbAppMain.c <<EOF
- 31a32
- > #include <fcntl.h>
- 35d35
- < #include <fcntl.h> /* _O_BINARY */
- 117a118
- > posix_fadvise(config->mmap.fd, 0, 0, POSIX_FADV_NOREUSE);
- EOF
-
-[1] https://lore.kernel.org/r/1308923350-7932-1-git-send-email-andrea@betterlinux.com/
-[2] https://openbenchmarking.org/result/2209259-PTS-MGLRU8GB57
-
-Link: https://lkml.kernel.org/r/20221230215252.2628425-2-yuzhao@google.com
-Change-Id: I0b7f5f971d78014ea1ba44cee6a8ec902a4330d0
-Signed-off-by: Yu Zhao <yuzhao@google.com>
-Cc: Alexander Viro <viro@zeniv.linux.org.uk>
-Cc: Andrea Righi <andrea.righi@canonical.com>
-Cc: Johannes Weiner <hannes@cmpxchg.org>
-Cc: Michael Larabel <Michael@MichaelLarabel.com>
-Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
-(cherry picked from commit 17e810229cb3068b692fa078bd9b3a6527e0866a)
-Bug: 274865848
-Signed-off-by: T.J. Mercier <tjmercier@google.com>
----
- include/linux/fs.h | 2 ++
- include/linux/mm_inline.h | 3 +++
- mm/fadvise.c | 5 ++++-
- 3 files changed, 9 insertions(+), 1 deletion(-)
-
---- a/include/linux/fs.h
-+++ b/include/linux/fs.h
-@@ -166,6 +166,8 @@ typedef int (dio_iodone_t)(struct kiocb
- /* File supports DIRECT IO */
- #define FMODE_CAN_ODIRECT ((__force fmode_t)0x400000)
-
-+#define FMODE_NOREUSE ((__force fmode_t)0x800000)
-+
- /* File was opened by fanotify and shouldn't generate fanotify events */
- #define FMODE_NONOTIFY ((__force fmode_t)0x4000000)
-
---- a/include/linux/mm_inline.h
-+++ b/include/linux/mm_inline.h
-@@ -605,6 +605,9 @@ static inline bool vma_has_recency(struc
- if (vma->vm_flags & (VM_SEQ_READ | VM_RAND_READ))
- return false;
-
-+ if (vma->vm_file && (vma->vm_file->f_mode & FMODE_NOREUSE))
-+ return false;
-+
- return true;
- }
-
---- a/mm/fadvise.c
-+++ b/mm/fadvise.c
-@@ -80,7 +80,7 @@ int generic_fadvise(struct file *file, l
- case POSIX_FADV_NORMAL:
- file->f_ra.ra_pages = bdi->ra_pages;
- spin_lock(&file->f_lock);
-- file->f_mode &= ~FMODE_RANDOM;
-+ file->f_mode &= ~(FMODE_RANDOM | FMODE_NOREUSE);
- spin_unlock(&file->f_lock);
- break;
- case POSIX_FADV_RANDOM:
-@@ -107,6 +107,9 @@ int generic_fadvise(struct file *file, l
- force_page_cache_readahead(mapping, file, start_index, nrpages);
- break;
- case POSIX_FADV_NOREUSE:
-+ spin_lock(&file->f_lock);
-+ file->f_mode |= FMODE_NOREUSE;
-+ spin_unlock(&file->f_lock);
- break;
- case POSIX_FADV_DONTNEED:
- __filemap_fdatawrite_range(mapping, offset, endbyte,
+++ /dev/null
-From 1b5e4c317d80f4826eceb3781702d18d06b14394 Mon Sep 17 00:00:00 2001
-From: "T.J. Alumbaugh" <talumbau@google.com>
-Date: Wed, 18 Jan 2023 00:18:21 +0000
-Subject: [PATCH 12/19] UPSTREAM: mm: multi-gen LRU: section for working set
- protection
-
-Patch series "mm: multi-gen LRU: improve".
-
-This patch series improves a few MGLRU functions, collects related
-functions, and adds additional documentation.
-
-This patch (of 7):
-
-Add a section for working set protection in the code and the design doc.
-The admin doc already contains its usage.
-
-Link: https://lkml.kernel.org/r/20230118001827.1040870-1-talumbau@google.com
-Link: https://lkml.kernel.org/r/20230118001827.1040870-2-talumbau@google.com
-Change-Id: I65599075fd42951db7739a2ab7cee78516e157b3
-Signed-off-by: T.J. Alumbaugh <talumbau@google.com>
-Cc: Yu Zhao <yuzhao@google.com>
-Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
-(cherry picked from commit 7b8144e63d84716f16a1b929e0c7e03ae5c4d5c1)
-Bug: 274865848
-Signed-off-by: T.J. Mercier <tjmercier@google.com>
----
- Documentation/mm/multigen_lru.rst | 15 +++++++++++++++
- mm/vmscan.c | 4 ++++
- 2 files changed, 19 insertions(+)
-
---- a/Documentation/mm/multigen_lru.rst
-+++ b/Documentation/mm/multigen_lru.rst
-@@ -141,6 +141,21 @@ loop has detected outlying refaults from
- this end, the feedback loop uses the first tier as the baseline, for
- the reason stated earlier.
-
-+Working set protection
-+----------------------
-+Each generation is timestamped at birth. If ``lru_gen_min_ttl`` is
-+set, an ``lruvec`` is protected from the eviction when its oldest
-+generation was born within ``lru_gen_min_ttl`` milliseconds. In other
-+words, it prevents the working set of ``lru_gen_min_ttl`` milliseconds
-+from getting evicted. The OOM killer is triggered if this working set
-+cannot be kept in memory.
-+
-+This time-based approach has the following advantages:
-+
-+1. It is easier to configure because it is agnostic to applications
-+ and memory sizes.
-+2. It is more reliable because it is directly wired to the OOM killer.
-+
- Summary
- -------
- The multi-gen LRU can be disassembled into the following parts:
---- a/mm/vmscan.c
-+++ b/mm/vmscan.c
-@@ -4461,6 +4461,10 @@ done:
- return true;
- }
-
-+/******************************************************************************
-+ * working set protection
-+ ******************************************************************************/
-+
- static bool lruvec_is_sizable(struct lruvec *lruvec, struct scan_control *sc)
- {
- int gen, type, zone;
+++ /dev/null
-From 5ddf9d53d375e42af49b744bd7c2f8247c6bce15 Mon Sep 17 00:00:00 2001
-From: "T.J. Alumbaugh" <talumbau@google.com>
-Date: Wed, 18 Jan 2023 00:18:22 +0000
-Subject: [PATCH 13/19] UPSTREAM: mm: multi-gen LRU: section for rmap/PT walk
- feedback
-
-Add a section for lru_gen_look_around() in the code and the design doc.
-
-Link: https://lkml.kernel.org/r/20230118001827.1040870-3-talumbau@google.com
-Change-Id: I5097af63f61b3b69ec2abee6cdbdc33c296df213
-Signed-off-by: T.J. Alumbaugh <talumbau@google.com>
-Cc: Yu Zhao <yuzhao@google.com>
-Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
-(cherry picked from commit db19a43d9b3a8876552f00f656008206ef9a5efa)
-Bug: 274865848
-Signed-off-by: T.J. Mercier <tjmercier@google.com>
----
- Documentation/mm/multigen_lru.rst | 14 ++++++++++++++
- mm/vmscan.c | 4 ++++
- 2 files changed, 18 insertions(+)
-
---- a/Documentation/mm/multigen_lru.rst
-+++ b/Documentation/mm/multigen_lru.rst
-@@ -156,6 +156,20 @@ This time-based approach has the followi
- and memory sizes.
- 2. It is more reliable because it is directly wired to the OOM killer.
-
-+Rmap/PT walk feedback
-+---------------------
-+Searching the rmap for PTEs mapping each page on an LRU list (to test
-+and clear the accessed bit) can be expensive because pages from
-+different VMAs (PA space) are not cache friendly to the rmap (VA
-+space). For workloads mostly using mapped pages, searching the rmap
-+can incur the highest CPU cost in the reclaim path.
-+
-+``lru_gen_look_around()`` exploits spatial locality to reduce the
-+trips into the rmap. It scans the adjacent PTEs of a young PTE and
-+promotes hot pages. If the scan was done cacheline efficiently, it
-+adds the PMD entry pointing to the PTE table to the Bloom filter. This
-+forms a feedback loop between the eviction and the aging.
-+
- Summary
- -------
- The multi-gen LRU can be disassembled into the following parts:
---- a/mm/vmscan.c
-+++ b/mm/vmscan.c
-@@ -4555,6 +4555,10 @@ static void lru_gen_age_node(struct pgli
- }
- }
-
-+/******************************************************************************
-+ * rmap/PT walk feedback
-+ ******************************************************************************/
-+
- /*
- * This function exploits spatial locality when shrink_folio_list() walks the
- * rmap. It scans the adjacent PTEs of a young PTE and promotes hot pages. If
+++ /dev/null
-From 397624e12244ec038f51cb1f178ccb7a2ec562e5 Mon Sep 17 00:00:00 2001
-From: "T.J. Alumbaugh" <talumbau@google.com>
-Date: Wed, 18 Jan 2023 00:18:23 +0000
-Subject: [PATCH 14/19] UPSTREAM: mm: multi-gen LRU: section for Bloom filters
-
-Move Bloom filters code into a dedicated section. Improve the design doc
-to explain Bloom filter usage and connection between aging and eviction in
-their use.
-
-Link: https://lkml.kernel.org/r/20230118001827.1040870-4-talumbau@google.com
-Change-Id: I73e866f687c1ed9f5c8538086aa39408b79897db
-Signed-off-by: T.J. Alumbaugh <talumbau@google.com>
-Cc: Yu Zhao <yuzhao@google.com>
-Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
-(cherry picked from commit ccbbbb85945d8f0255aa9dbc1b617017e2294f2c)
-Bug: 274865848
-Signed-off-by: T.J. Mercier <tjmercier@google.com>
----
- Documentation/mm/multigen_lru.rst | 16 +++
- mm/vmscan.c | 180 +++++++++++++++---------------
- 2 files changed, 108 insertions(+), 88 deletions(-)
-
---- a/Documentation/mm/multigen_lru.rst
-+++ b/Documentation/mm/multigen_lru.rst
-@@ -170,6 +170,22 @@ promotes hot pages. If the scan was done
- adds the PMD entry pointing to the PTE table to the Bloom filter. This
- forms a feedback loop between the eviction and the aging.
-
-+Bloom Filters
-+-------------
-+Bloom filters are a space and memory efficient data structure for set
-+membership test, i.e., test if an element is not in the set or may be
-+in the set.
-+
-+In the eviction path, specifically, in ``lru_gen_look_around()``, if a
-+PMD has a sufficient number of hot pages, its address is placed in the
-+filter. In the aging path, set membership means that the PTE range
-+will be scanned for young pages.
-+
-+Note that Bloom filters are probabilistic on set membership. If a test
-+is false positive, the cost is an additional scan of a range of PTEs,
-+which may yield hot pages anyway. Parameters of the filter itself can
-+control the false positive rate in the limit.
-+
- Summary
- -------
- The multi-gen LRU can be disassembled into the following parts:
---- a/mm/vmscan.c
-+++ b/mm/vmscan.c
-@@ -3209,6 +3209,98 @@ static bool __maybe_unused seq_is_valid(
- }
-
- /******************************************************************************
-+ * Bloom filters
-+ ******************************************************************************/
-+
-+/*
-+ * Bloom filters with m=1<<15, k=2 and the false positive rates of ~1/5 when
-+ * n=10,000 and ~1/2 when n=20,000, where, conventionally, m is the number of
-+ * bits in a bitmap, k is the number of hash functions and n is the number of
-+ * inserted items.
-+ *
-+ * Page table walkers use one of the two filters to reduce their search space.
-+ * To get rid of non-leaf entries that no longer have enough leaf entries, the
-+ * aging uses the double-buffering technique to flip to the other filter each
-+ * time it produces a new generation. For non-leaf entries that have enough
-+ * leaf entries, the aging carries them over to the next generation in
-+ * walk_pmd_range(); the eviction also report them when walking the rmap
-+ * in lru_gen_look_around().
-+ *
-+ * For future optimizations:
-+ * 1. It's not necessary to keep both filters all the time. The spare one can be
-+ * freed after the RCU grace period and reallocated if needed again.
-+ * 2. And when reallocating, it's worth scaling its size according to the number
-+ * of inserted entries in the other filter, to reduce the memory overhead on
-+ * small systems and false positives on large systems.
-+ * 3. Jenkins' hash function is an alternative to Knuth's.
-+ */
-+#define BLOOM_FILTER_SHIFT 15
-+
-+static inline int filter_gen_from_seq(unsigned long seq)
-+{
-+ return seq % NR_BLOOM_FILTERS;
-+}
-+
-+static void get_item_key(void *item, int *key)
-+{
-+ u32 hash = hash_ptr(item, BLOOM_FILTER_SHIFT * 2);
-+
-+ BUILD_BUG_ON(BLOOM_FILTER_SHIFT * 2 > BITS_PER_TYPE(u32));
-+
-+ key[0] = hash & (BIT(BLOOM_FILTER_SHIFT) - 1);
-+ key[1] = hash >> BLOOM_FILTER_SHIFT;
-+}
-+
-+static bool test_bloom_filter(struct lruvec *lruvec, unsigned long seq, void *item)
-+{
-+ int key[2];
-+ unsigned long *filter;
-+ int gen = filter_gen_from_seq(seq);
-+
-+ filter = READ_ONCE(lruvec->mm_state.filters[gen]);
-+ if (!filter)
-+ return true;
-+
-+ get_item_key(item, key);
-+
-+ return test_bit(key[0], filter) && test_bit(key[1], filter);
-+}
-+
-+static void update_bloom_filter(struct lruvec *lruvec, unsigned long seq, void *item)
-+{
-+ int key[2];
-+ unsigned long *filter;
-+ int gen = filter_gen_from_seq(seq);
-+
-+ filter = READ_ONCE(lruvec->mm_state.filters[gen]);
-+ if (!filter)
-+ return;
-+
-+ get_item_key(item, key);
-+
-+ if (!test_bit(key[0], filter))
-+ set_bit(key[0], filter);
-+ if (!test_bit(key[1], filter))
-+ set_bit(key[1], filter);
-+}
-+
-+static void reset_bloom_filter(struct lruvec *lruvec, unsigned long seq)
-+{
-+ unsigned long *filter;
-+ int gen = filter_gen_from_seq(seq);
-+
-+ filter = lruvec->mm_state.filters[gen];
-+ if (filter) {
-+ bitmap_clear(filter, 0, BIT(BLOOM_FILTER_SHIFT));
-+ return;
-+ }
-+
-+ filter = bitmap_zalloc(BIT(BLOOM_FILTER_SHIFT),
-+ __GFP_HIGH | __GFP_NOMEMALLOC | __GFP_NOWARN);
-+ WRITE_ONCE(lruvec->mm_state.filters[gen], filter);
-+}
-+
-+/******************************************************************************
- * mm_struct list
- ******************************************************************************/
-
-@@ -3333,94 +3425,6 @@ void lru_gen_migrate_mm(struct mm_struct
- }
- #endif
-
--/*
-- * Bloom filters with m=1<<15, k=2 and the false positive rates of ~1/5 when
-- * n=10,000 and ~1/2 when n=20,000, where, conventionally, m is the number of
-- * bits in a bitmap, k is the number of hash functions and n is the number of
-- * inserted items.
-- *
-- * Page table walkers use one of the two filters to reduce their search space.
-- * To get rid of non-leaf entries that no longer have enough leaf entries, the
-- * aging uses the double-buffering technique to flip to the other filter each
-- * time it produces a new generation. For non-leaf entries that have enough
-- * leaf entries, the aging carries them over to the next generation in
-- * walk_pmd_range(); the eviction also report them when walking the rmap
-- * in lru_gen_look_around().
-- *
-- * For future optimizations:
-- * 1. It's not necessary to keep both filters all the time. The spare one can be
-- * freed after the RCU grace period and reallocated if needed again.
-- * 2. And when reallocating, it's worth scaling its size according to the number
-- * of inserted entries in the other filter, to reduce the memory overhead on
-- * small systems and false positives on large systems.
-- * 3. Jenkins' hash function is an alternative to Knuth's.
-- */
--#define BLOOM_FILTER_SHIFT 15
--
--static inline int filter_gen_from_seq(unsigned long seq)
--{
-- return seq % NR_BLOOM_FILTERS;
--}
--
--static void get_item_key(void *item, int *key)
--{
-- u32 hash = hash_ptr(item, BLOOM_FILTER_SHIFT * 2);
--
-- BUILD_BUG_ON(BLOOM_FILTER_SHIFT * 2 > BITS_PER_TYPE(u32));
--
-- key[0] = hash & (BIT(BLOOM_FILTER_SHIFT) - 1);
-- key[1] = hash >> BLOOM_FILTER_SHIFT;
--}
--
--static void reset_bloom_filter(struct lruvec *lruvec, unsigned long seq)
--{
-- unsigned long *filter;
-- int gen = filter_gen_from_seq(seq);
--
-- filter = lruvec->mm_state.filters[gen];
-- if (filter) {
-- bitmap_clear(filter, 0, BIT(BLOOM_FILTER_SHIFT));
-- return;
-- }
--
-- filter = bitmap_zalloc(BIT(BLOOM_FILTER_SHIFT),
-- __GFP_HIGH | __GFP_NOMEMALLOC | __GFP_NOWARN);
-- WRITE_ONCE(lruvec->mm_state.filters[gen], filter);
--}
--
--static void update_bloom_filter(struct lruvec *lruvec, unsigned long seq, void *item)
--{
-- int key[2];
-- unsigned long *filter;
-- int gen = filter_gen_from_seq(seq);
--
-- filter = READ_ONCE(lruvec->mm_state.filters[gen]);
-- if (!filter)
-- return;
--
-- get_item_key(item, key);
--
-- if (!test_bit(key[0], filter))
-- set_bit(key[0], filter);
-- if (!test_bit(key[1], filter))
-- set_bit(key[1], filter);
--}
--
--static bool test_bloom_filter(struct lruvec *lruvec, unsigned long seq, void *item)
--{
-- int key[2];
-- unsigned long *filter;
-- int gen = filter_gen_from_seq(seq);
--
-- filter = READ_ONCE(lruvec->mm_state.filters[gen]);
-- if (!filter)
-- return true;
--
-- get_item_key(item, key);
--
-- return test_bit(key[0], filter) && test_bit(key[1], filter);
--}
--
- static void reset_mm_stats(struct lruvec *lruvec, struct lru_gen_mm_walk *walk, bool last)
- {
- int i;
+++ /dev/null
-From 48c916b812652f9453be5bd45a703728926d41ca Mon Sep 17 00:00:00 2001
-From: "T.J. Alumbaugh" <talumbau@google.com>
-Date: Wed, 18 Jan 2023 00:18:24 +0000
-Subject: [PATCH 15/19] UPSTREAM: mm: multi-gen LRU: section for memcg LRU
-
-Move memcg LRU code into a dedicated section. Improve the design doc to
-outline its architecture.
-
-Link: https://lkml.kernel.org/r/20230118001827.1040870-5-talumbau@google.com
-Change-Id: Id252e420cff7a858acb098cf2b3642da5c40f602
-Signed-off-by: T.J. Alumbaugh <talumbau@google.com>
-Cc: Yu Zhao <yuzhao@google.com>
-Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
-(cherry picked from commit 36c7b4db7c942ae9e1b111f0c6b468c8b2e33842)
-Bug: 274865848
-Signed-off-by: T.J. Mercier <tjmercier@google.com>
----
- Documentation/mm/multigen_lru.rst | 33 +++-
- include/linux/mm_inline.h | 17 --
- include/linux/mmzone.h | 13 +-
- mm/memcontrol.c | 8 +-
- mm/vmscan.c | 250 +++++++++++++++++-------------
- 5 files changed, 178 insertions(+), 143 deletions(-)
-
---- a/Documentation/mm/multigen_lru.rst
-+++ b/Documentation/mm/multigen_lru.rst
-@@ -186,9 +186,40 @@ is false positive, the cost is an additi
- which may yield hot pages anyway. Parameters of the filter itself can
- control the false positive rate in the limit.
-
-+Memcg LRU
-+---------
-+An memcg LRU is a per-node LRU of memcgs. It is also an LRU of LRUs,
-+since each node and memcg combination has an LRU of folios (see
-+``mem_cgroup_lruvec()``). Its goal is to improve the scalability of
-+global reclaim, which is critical to system-wide memory overcommit in
-+data centers. Note that memcg LRU only applies to global reclaim.
-+
-+The basic structure of an memcg LRU can be understood by an analogy to
-+the active/inactive LRU (of folios):
-+
-+1. It has the young and the old (generations), i.e., the counterparts
-+ to the active and the inactive;
-+2. The increment of ``max_seq`` triggers promotion, i.e., the
-+ counterpart to activation;
-+3. Other events trigger similar operations, e.g., offlining an memcg
-+ triggers demotion, i.e., the counterpart to deactivation.
-+
-+In terms of global reclaim, it has two distinct features:
-+
-+1. Sharding, which allows each thread to start at a random memcg (in
-+ the old generation) and improves parallelism;
-+2. Eventual fairness, which allows direct reclaim to bail out at will
-+ and reduces latency without affecting fairness over some time.
-+
-+In terms of traversing memcgs during global reclaim, it improves the
-+best-case complexity from O(n) to O(1) and does not affect the
-+worst-case complexity O(n). Therefore, on average, it has a sublinear
-+complexity.
-+
- Summary
- -------
--The multi-gen LRU can be disassembled into the following parts:
-+The multi-gen LRU (of folios) can be disassembled into the following
-+parts:
-
- * Generations
- * Rmap walks
---- a/include/linux/mm_inline.h
-+++ b/include/linux/mm_inline.h
-@@ -122,18 +122,6 @@ static inline bool lru_gen_in_fault(void
- return current->in_lru_fault;
- }
-
--#ifdef CONFIG_MEMCG
--static inline int lru_gen_memcg_seg(struct lruvec *lruvec)
--{
-- return READ_ONCE(lruvec->lrugen.seg);
--}
--#else
--static inline int lru_gen_memcg_seg(struct lruvec *lruvec)
--{
-- return 0;
--}
--#endif
--
- static inline int lru_gen_from_seq(unsigned long seq)
- {
- return seq % MAX_NR_GENS;
-@@ -314,11 +302,6 @@ static inline bool lru_gen_in_fault(void
- return false;
- }
-
--static inline int lru_gen_memcg_seg(struct lruvec *lruvec)
--{
-- return 0;
--}
--
- static inline bool lru_gen_add_folio(struct lruvec *lruvec, struct folio *folio, bool reclaiming)
- {
- return false;
---- a/include/linux/mmzone.h
-+++ b/include/linux/mmzone.h
-@@ -368,15 +368,6 @@ struct page_vma_mapped_walk;
- #define LRU_GEN_MASK ((BIT(LRU_GEN_WIDTH) - 1) << LRU_GEN_PGOFF)
- #define LRU_REFS_MASK ((BIT(LRU_REFS_WIDTH) - 1) << LRU_REFS_PGOFF)
-
--/* see the comment on MEMCG_NR_GENS */
--enum {
-- MEMCG_LRU_NOP,
-- MEMCG_LRU_HEAD,
-- MEMCG_LRU_TAIL,
-- MEMCG_LRU_OLD,
-- MEMCG_LRU_YOUNG,
--};
--
- #ifdef CONFIG_LRU_GEN
-
- enum {
-@@ -557,7 +548,7 @@ void lru_gen_exit_memcg(struct mem_cgrou
- void lru_gen_online_memcg(struct mem_cgroup *memcg);
- void lru_gen_offline_memcg(struct mem_cgroup *memcg);
- void lru_gen_release_memcg(struct mem_cgroup *memcg);
--void lru_gen_rotate_memcg(struct lruvec *lruvec, int op);
-+void lru_gen_soft_reclaim(struct lruvec *lruvec);
-
- #else /* !CONFIG_MEMCG */
-
-@@ -608,7 +599,7 @@ static inline void lru_gen_release_memcg
- {
- }
-
--static inline void lru_gen_rotate_memcg(struct lruvec *lruvec, int op)
-+static inline void lru_gen_soft_reclaim(struct lruvec *lruvec)
- {
- }
-
---- a/mm/memcontrol.c
-+++ b/mm/memcontrol.c
-@@ -478,12 +478,8 @@ static void mem_cgroup_update_tree(struc
- struct mem_cgroup_tree_per_node *mctz;
-
- if (lru_gen_enabled()) {
-- struct lruvec *lruvec = &memcg->nodeinfo[nid]->lruvec;
--
-- /* see the comment on MEMCG_NR_GENS */
-- if (soft_limit_excess(memcg) && lru_gen_memcg_seg(lruvec) != MEMCG_LRU_HEAD)
-- lru_gen_rotate_memcg(lruvec, MEMCG_LRU_HEAD);
--
-+ if (soft_limit_excess(memcg))
-+ lru_gen_soft_reclaim(&memcg->nodeinfo[nid]->lruvec);
- return;
- }
-
---- a/mm/vmscan.c
-+++ b/mm/vmscan.c
-@@ -4692,6 +4692,148 @@ void lru_gen_look_around(struct page_vma
- }
-
- /******************************************************************************
-+ * memcg LRU
-+ ******************************************************************************/
-+
-+/* see the comment on MEMCG_NR_GENS */
-+enum {
-+ MEMCG_LRU_NOP,
-+ MEMCG_LRU_HEAD,
-+ MEMCG_LRU_TAIL,
-+ MEMCG_LRU_OLD,
-+ MEMCG_LRU_YOUNG,
-+};
-+
-+#ifdef CONFIG_MEMCG
-+
-+static int lru_gen_memcg_seg(struct lruvec *lruvec)
-+{
-+ return READ_ONCE(lruvec->lrugen.seg);
-+}
-+
-+static void lru_gen_rotate_memcg(struct lruvec *lruvec, int op)
-+{
-+ int seg;
-+ int old, new;
-+ int bin = get_random_u32_below(MEMCG_NR_BINS);
-+ struct pglist_data *pgdat = lruvec_pgdat(lruvec);
-+
-+ spin_lock(&pgdat->memcg_lru.lock);
-+
-+ VM_WARN_ON_ONCE(hlist_nulls_unhashed(&lruvec->lrugen.list));
-+
-+ seg = 0;
-+ new = old = lruvec->lrugen.gen;
-+
-+ /* see the comment on MEMCG_NR_GENS */
-+ if (op == MEMCG_LRU_HEAD)
-+ seg = MEMCG_LRU_HEAD;
-+ else if (op == MEMCG_LRU_TAIL)
-+ seg = MEMCG_LRU_TAIL;
-+ else if (op == MEMCG_LRU_OLD)
-+ new = get_memcg_gen(pgdat->memcg_lru.seq);
-+ else if (op == MEMCG_LRU_YOUNG)
-+ new = get_memcg_gen(pgdat->memcg_lru.seq + 1);
-+ else
-+ VM_WARN_ON_ONCE(true);
-+
-+ hlist_nulls_del_rcu(&lruvec->lrugen.list);
-+
-+ if (op == MEMCG_LRU_HEAD || op == MEMCG_LRU_OLD)
-+ hlist_nulls_add_head_rcu(&lruvec->lrugen.list, &pgdat->memcg_lru.fifo[new][bin]);
-+ else
-+ hlist_nulls_add_tail_rcu(&lruvec->lrugen.list, &pgdat->memcg_lru.fifo[new][bin]);
-+
-+ pgdat->memcg_lru.nr_memcgs[old]--;
-+ pgdat->memcg_lru.nr_memcgs[new]++;
-+
-+ lruvec->lrugen.gen = new;
-+ WRITE_ONCE(lruvec->lrugen.seg, seg);
-+
-+ if (!pgdat->memcg_lru.nr_memcgs[old] && old == get_memcg_gen(pgdat->memcg_lru.seq))
-+ WRITE_ONCE(pgdat->memcg_lru.seq, pgdat->memcg_lru.seq + 1);
-+
-+ spin_unlock(&pgdat->memcg_lru.lock);
-+}
-+
-+void lru_gen_online_memcg(struct mem_cgroup *memcg)
-+{
-+ int gen;
-+ int nid;
-+ int bin = get_random_u32_below(MEMCG_NR_BINS);
-+
-+ for_each_node(nid) {
-+ struct pglist_data *pgdat = NODE_DATA(nid);
-+ struct lruvec *lruvec = get_lruvec(memcg, nid);
-+
-+ spin_lock(&pgdat->memcg_lru.lock);
-+
-+ VM_WARN_ON_ONCE(!hlist_nulls_unhashed(&lruvec->lrugen.list));
-+
-+ gen = get_memcg_gen(pgdat->memcg_lru.seq);
-+
-+ hlist_nulls_add_tail_rcu(&lruvec->lrugen.list, &pgdat->memcg_lru.fifo[gen][bin]);
-+ pgdat->memcg_lru.nr_memcgs[gen]++;
-+
-+ lruvec->lrugen.gen = gen;
-+
-+ spin_unlock(&pgdat->memcg_lru.lock);
-+ }
-+}
-+
-+void lru_gen_offline_memcg(struct mem_cgroup *memcg)
-+{
-+ int nid;
-+
-+ for_each_node(nid) {
-+ struct lruvec *lruvec = get_lruvec(memcg, nid);
-+
-+ lru_gen_rotate_memcg(lruvec, MEMCG_LRU_OLD);
-+ }
-+}
-+
-+void lru_gen_release_memcg(struct mem_cgroup *memcg)
-+{
-+ int gen;
-+ int nid;
-+
-+ for_each_node(nid) {
-+ struct pglist_data *pgdat = NODE_DATA(nid);
-+ struct lruvec *lruvec = get_lruvec(memcg, nid);
-+
-+ spin_lock(&pgdat->memcg_lru.lock);
-+
-+ VM_WARN_ON_ONCE(hlist_nulls_unhashed(&lruvec->lrugen.list));
-+
-+ gen = lruvec->lrugen.gen;
-+
-+ hlist_nulls_del_rcu(&lruvec->lrugen.list);
-+ pgdat->memcg_lru.nr_memcgs[gen]--;
-+
-+ if (!pgdat->memcg_lru.nr_memcgs[gen] && gen == get_memcg_gen(pgdat->memcg_lru.seq))
-+ WRITE_ONCE(pgdat->memcg_lru.seq, pgdat->memcg_lru.seq + 1);
-+
-+ spin_unlock(&pgdat->memcg_lru.lock);
-+ }
-+}
-+
-+void lru_gen_soft_reclaim(struct lruvec *lruvec)
-+{
-+ /* see the comment on MEMCG_NR_GENS */
-+ if (lru_gen_memcg_seg(lruvec) != MEMCG_LRU_HEAD)
-+ lru_gen_rotate_memcg(lruvec, MEMCG_LRU_HEAD);
-+}
-+
-+#else /* !CONFIG_MEMCG */
-+
-+static int lru_gen_memcg_seg(struct lruvec *lruvec)
-+{
-+ return 0;
-+}
-+
-+#endif
-+
-+/******************************************************************************
- * the eviction
- ******************************************************************************/
-
-@@ -5398,53 +5540,6 @@ done:
- pgdat->kswapd_failures = 0;
- }
-
--#ifdef CONFIG_MEMCG
--void lru_gen_rotate_memcg(struct lruvec *lruvec, int op)
--{
-- int seg;
-- int old, new;
-- int bin = get_random_u32_below(MEMCG_NR_BINS);
-- struct pglist_data *pgdat = lruvec_pgdat(lruvec);
--
-- spin_lock(&pgdat->memcg_lru.lock);
--
-- VM_WARN_ON_ONCE(hlist_nulls_unhashed(&lruvec->lrugen.list));
--
-- seg = 0;
-- new = old = lruvec->lrugen.gen;
--
-- /* see the comment on MEMCG_NR_GENS */
-- if (op == MEMCG_LRU_HEAD)
-- seg = MEMCG_LRU_HEAD;
-- else if (op == MEMCG_LRU_TAIL)
-- seg = MEMCG_LRU_TAIL;
-- else if (op == MEMCG_LRU_OLD)
-- new = get_memcg_gen(pgdat->memcg_lru.seq);
-- else if (op == MEMCG_LRU_YOUNG)
-- new = get_memcg_gen(pgdat->memcg_lru.seq + 1);
-- else
-- VM_WARN_ON_ONCE(true);
--
-- hlist_nulls_del_rcu(&lruvec->lrugen.list);
--
-- if (op == MEMCG_LRU_HEAD || op == MEMCG_LRU_OLD)
-- hlist_nulls_add_head_rcu(&lruvec->lrugen.list, &pgdat->memcg_lru.fifo[new][bin]);
-- else
-- hlist_nulls_add_tail_rcu(&lruvec->lrugen.list, &pgdat->memcg_lru.fifo[new][bin]);
--
-- pgdat->memcg_lru.nr_memcgs[old]--;
-- pgdat->memcg_lru.nr_memcgs[new]++;
--
-- lruvec->lrugen.gen = new;
-- WRITE_ONCE(lruvec->lrugen.seg, seg);
--
-- if (!pgdat->memcg_lru.nr_memcgs[old] && old == get_memcg_gen(pgdat->memcg_lru.seq))
-- WRITE_ONCE(pgdat->memcg_lru.seq, pgdat->memcg_lru.seq + 1);
--
-- spin_unlock(&pgdat->memcg_lru.lock);
--}
--#endif
--
- /******************************************************************************
- * state change
- ******************************************************************************/
-@@ -6090,67 +6185,6 @@ void lru_gen_exit_memcg(struct mem_cgrou
- }
- }
-
--void lru_gen_online_memcg(struct mem_cgroup *memcg)
--{
-- int gen;
-- int nid;
-- int bin = get_random_u32_below(MEMCG_NR_BINS);
--
-- for_each_node(nid) {
-- struct pglist_data *pgdat = NODE_DATA(nid);
-- struct lruvec *lruvec = get_lruvec(memcg, nid);
--
-- spin_lock(&pgdat->memcg_lru.lock);
--
-- VM_WARN_ON_ONCE(!hlist_nulls_unhashed(&lruvec->lrugen.list));
--
-- gen = get_memcg_gen(pgdat->memcg_lru.seq);
--
-- hlist_nulls_add_tail_rcu(&lruvec->lrugen.list, &pgdat->memcg_lru.fifo[gen][bin]);
-- pgdat->memcg_lru.nr_memcgs[gen]++;
--
-- lruvec->lrugen.gen = gen;
--
-- spin_unlock(&pgdat->memcg_lru.lock);
-- }
--}
--
--void lru_gen_offline_memcg(struct mem_cgroup *memcg)
--{
-- int nid;
--
-- for_each_node(nid) {
-- struct lruvec *lruvec = get_lruvec(memcg, nid);
--
-- lru_gen_rotate_memcg(lruvec, MEMCG_LRU_OLD);
-- }
--}
--
--void lru_gen_release_memcg(struct mem_cgroup *memcg)
--{
-- int gen;
-- int nid;
--
-- for_each_node(nid) {
-- struct pglist_data *pgdat = NODE_DATA(nid);
-- struct lruvec *lruvec = get_lruvec(memcg, nid);
--
-- spin_lock(&pgdat->memcg_lru.lock);
--
-- VM_WARN_ON_ONCE(hlist_nulls_unhashed(&lruvec->lrugen.list));
--
-- gen = lruvec->lrugen.gen;
--
-- hlist_nulls_del_rcu(&lruvec->lrugen.list);
-- pgdat->memcg_lru.nr_memcgs[gen]--;
--
-- if (!pgdat->memcg_lru.nr_memcgs[gen] && gen == get_memcg_gen(pgdat->memcg_lru.seq))
-- WRITE_ONCE(pgdat->memcg_lru.seq, pgdat->memcg_lru.seq + 1);
--
-- spin_unlock(&pgdat->memcg_lru.lock);
-- }
--}
--
- #endif /* CONFIG_MEMCG */
-
- static int __init init_lru_gen(void)
+++ /dev/null
-From bec433f29537652ed054148edfd7e2183ddcf7c3 Mon Sep 17 00:00:00 2001
-From: "T.J. Alumbaugh" <talumbau@google.com>
-Date: Wed, 18 Jan 2023 00:18:25 +0000
-Subject: [PATCH 16/19] UPSTREAM: mm: multi-gen LRU: improve
- lru_gen_exit_memcg()
-
-Add warnings and poison ->next.
-
-Link: https://lkml.kernel.org/r/20230118001827.1040870-6-talumbau@google.com
-Change-Id: I53de9e04c1ae941e122b33cd45d2bbb5f34aae0c
-Signed-off-by: T.J. Alumbaugh <talumbau@google.com>
-Cc: Yu Zhao <yuzhao@google.com>
-Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
-(cherry picked from commit 37cc99979d04cca677c0ad5c0acd1149ec165d1b)
-Bug: 274865848
-Signed-off-by: T.J. Mercier <tjmercier@google.com>
----
- mm/vmscan.c | 5 +++++
- 1 file changed, 5 insertions(+)
-
---- a/mm/vmscan.c
-+++ b/mm/vmscan.c
-@@ -6172,12 +6172,17 @@ void lru_gen_exit_memcg(struct mem_cgrou
- int i;
- int nid;
-
-+ VM_WARN_ON_ONCE(!list_empty(&memcg->mm_list.fifo));
-+
- for_each_node(nid) {
- struct lruvec *lruvec = get_lruvec(memcg, nid);
-
-+ VM_WARN_ON_ONCE(lruvec->mm_state.nr_walkers);
- VM_WARN_ON_ONCE(memchr_inv(lruvec->lrugen.nr_pages, 0,
- sizeof(lruvec->lrugen.nr_pages)));
-
-+ lruvec->lrugen.list.next = LIST_POISON1;
-+
- for (i = 0; i < NR_BLOOM_FILTERS; i++) {
- bitmap_free(lruvec->mm_state.filters[i]);
- lruvec->mm_state.filters[i] = NULL;
+++ /dev/null
-From fc0e3b06e0f19917b7ecad7967a72f61d4743644 Mon Sep 17 00:00:00 2001
-From: "T.J. Alumbaugh" <talumbau@google.com>
-Date: Wed, 18 Jan 2023 00:18:26 +0000
-Subject: [PATCH 17/19] UPSTREAM: mm: multi-gen LRU: improve walk_pmd_range()
-
-Improve readability of walk_pmd_range() and walk_pmd_range_locked().
-
-Link: https://lkml.kernel.org/r/20230118001827.1040870-7-talumbau@google.com
-Change-Id: Ia084fbf53fe989673b7804ca8ca520af12d7d52a
-Signed-off-by: T.J. Alumbaugh <talumbau@google.com>
-Cc: Yu Zhao <yuzhao@google.com>
-Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
-(cherry picked from commit b5ff4133617d0eced35b685da0bd0929dd9fabb7)
-Bug: 274865848
-Signed-off-by: T.J. Mercier <tjmercier@google.com>
----
- mm/vmscan.c | 40 ++++++++++++++++++++--------------------
- 1 file changed, 20 insertions(+), 20 deletions(-)
-
---- a/mm/vmscan.c
-+++ b/mm/vmscan.c
-@@ -3980,8 +3980,8 @@ restart:
- }
-
- #if defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG)
--static void walk_pmd_range_locked(pud_t *pud, unsigned long next, struct vm_area_struct *vma,
-- struct mm_walk *args, unsigned long *bitmap, unsigned long *start)
-+static void walk_pmd_range_locked(pud_t *pud, unsigned long addr, struct vm_area_struct *vma,
-+ struct mm_walk *args, unsigned long *bitmap, unsigned long *first)
- {
- int i;
- pmd_t *pmd;
-@@ -3994,18 +3994,19 @@ static void walk_pmd_range_locked(pud_t
- VM_WARN_ON_ONCE(pud_leaf(*pud));
-
- /* try to batch at most 1+MIN_LRU_BATCH+1 entries */
-- if (*start == -1) {
-- *start = next;
-+ if (*first == -1) {
-+ *first = addr;
-+ bitmap_zero(bitmap, MIN_LRU_BATCH);
- return;
- }
-
-- i = next == -1 ? 0 : pmd_index(next) - pmd_index(*start);
-+ i = addr == -1 ? 0 : pmd_index(addr) - pmd_index(*first);
- if (i && i <= MIN_LRU_BATCH) {
- __set_bit(i - 1, bitmap);
- return;
- }
-
-- pmd = pmd_offset(pud, *start);
-+ pmd = pmd_offset(pud, *first);
-
- ptl = pmd_lockptr(args->mm, pmd);
- if (!spin_trylock(ptl))
-@@ -4016,15 +4017,16 @@ static void walk_pmd_range_locked(pud_t
- do {
- unsigned long pfn;
- struct folio *folio;
-- unsigned long addr = i ? (*start & PMD_MASK) + i * PMD_SIZE : *start;
-+
-+ /* don't round down the first address */
-+ addr = i ? (*first & PMD_MASK) + i * PMD_SIZE : *first;
-
- pfn = get_pmd_pfn(pmd[i], vma, addr);
- if (pfn == -1)
- goto next;
-
- if (!pmd_trans_huge(pmd[i])) {
-- if (arch_has_hw_nonleaf_pmd_young() &&
-- get_cap(LRU_GEN_NONLEAF_YOUNG))
-+ if (arch_has_hw_nonleaf_pmd_young() && get_cap(LRU_GEN_NONLEAF_YOUNG))
- pmdp_test_and_clear_young(vma, addr, pmd + i);
- goto next;
- }
-@@ -4053,12 +4055,11 @@ next:
- arch_leave_lazy_mmu_mode();
- spin_unlock(ptl);
- done:
-- *start = -1;
-- bitmap_zero(bitmap, MIN_LRU_BATCH);
-+ *first = -1;
- }
- #else
--static void walk_pmd_range_locked(pud_t *pud, unsigned long next, struct vm_area_struct *vma,
-- struct mm_walk *args, unsigned long *bitmap, unsigned long *start)
-+static void walk_pmd_range_locked(pud_t *pud, unsigned long addr, struct vm_area_struct *vma,
-+ struct mm_walk *args, unsigned long *bitmap, unsigned long *first)
- {
- }
- #endif
-@@ -4071,9 +4072,9 @@ static void walk_pmd_range(pud_t *pud, u
- unsigned long next;
- unsigned long addr;
- struct vm_area_struct *vma;
-- unsigned long pos = -1;
-+ unsigned long bitmap[BITS_TO_LONGS(MIN_LRU_BATCH)];
-+ unsigned long first = -1;
- struct lru_gen_mm_walk *walk = args->private;
-- unsigned long bitmap[BITS_TO_LONGS(MIN_LRU_BATCH)] = {};
-
- VM_WARN_ON_ONCE(pud_leaf(*pud));
-
-@@ -4115,18 +4116,17 @@ restart:
- if (pfn < pgdat->node_start_pfn || pfn >= pgdat_end_pfn(pgdat))
- continue;
-
-- walk_pmd_range_locked(pud, addr, vma, args, bitmap, &pos);
-+ walk_pmd_range_locked(pud, addr, vma, args, bitmap, &first);
- continue;
- }
- #endif
- walk->mm_stats[MM_NONLEAF_TOTAL]++;
-
-- if (arch_has_hw_nonleaf_pmd_young() &&
-- get_cap(LRU_GEN_NONLEAF_YOUNG)) {
-+ if (arch_has_hw_nonleaf_pmd_young() && get_cap(LRU_GEN_NONLEAF_YOUNG)) {
- if (!pmd_young(val))
- continue;
-
-- walk_pmd_range_locked(pud, addr, vma, args, bitmap, &pos);
-+ walk_pmd_range_locked(pud, addr, vma, args, bitmap, &first);
- }
-
- if (!walk->force_scan && !test_bloom_filter(walk->lruvec, walk->max_seq, pmd + i))
-@@ -4143,7 +4143,7 @@ restart:
- update_bloom_filter(walk->lruvec, walk->max_seq + 1, pmd + i);
- }
-
-- walk_pmd_range_locked(pud, -1, vma, args, bitmap, &pos);
-+ walk_pmd_range_locked(pud, -1, vma, args, bitmap, &first);
-
- if (i < PTRS_PER_PMD && get_next_vma(PUD_MASK, PMD_SIZE, args, &start, &end))
- goto restart;
+++ /dev/null
-From e604c3ccb4dfbdde2467fccef9bb36170a392695 Mon Sep 17 00:00:00 2001
-From: "T.J. Alumbaugh" <talumbau@google.com>
-Date: Wed, 18 Jan 2023 00:18:27 +0000
-Subject: [PATCH 18/19] UPSTREAM: mm: multi-gen LRU: simplify
- lru_gen_look_around()
-
-Update the folio generation in place with or without
-current->reclaim_state->mm_walk. The LRU lock is held for longer, if
-mm_walk is NULL and the number of folios to update is more than
-PAGEVEC_SIZE.
-
-This causes a measurable regression from the LRU lock contention during a
-microbencmark. But a tiny regression is not worth the complexity.
-
-Link: https://lkml.kernel.org/r/20230118001827.1040870-8-talumbau@google.com
-Change-Id: I9ce18b4f4062e6c1c13c98ece9422478eb8e1846
-Signed-off-by: T.J. Alumbaugh <talumbau@google.com>
-Cc: Yu Zhao <yuzhao@google.com>
-Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
-(cherry picked from commit abf086721a2f1e6897c57796f7268df1b194c750)
-Bug: 274865848
-Signed-off-by: T.J. Mercier <tjmercier@google.com>
----
- mm/vmscan.c | 73 +++++++++++++++++------------------------------------
- 1 file changed, 23 insertions(+), 50 deletions(-)
-
---- a/mm/vmscan.c
-+++ b/mm/vmscan.c
-@@ -4573,13 +4573,12 @@ static void lru_gen_age_node(struct pgli
- void lru_gen_look_around(struct page_vma_mapped_walk *pvmw)
- {
- int i;
-- pte_t *pte;
- unsigned long start;
- unsigned long end;
-- unsigned long addr;
- struct lru_gen_mm_walk *walk;
- int young = 0;
-- unsigned long bitmap[BITS_TO_LONGS(MIN_LRU_BATCH)] = {};
-+ pte_t *pte = pvmw->pte;
-+ unsigned long addr = pvmw->address;
- struct folio *folio = pfn_folio(pvmw->pfn);
- struct mem_cgroup *memcg = folio_memcg(folio);
- struct pglist_data *pgdat = folio_pgdat(folio);
-@@ -4596,25 +4595,28 @@ void lru_gen_look_around(struct page_vma
- /* avoid taking the LRU lock under the PTL when possible */
- walk = current->reclaim_state ? current->reclaim_state->mm_walk : NULL;
-
-- start = max(pvmw->address & PMD_MASK, pvmw->vma->vm_start);
-- end = min(pvmw->address | ~PMD_MASK, pvmw->vma->vm_end - 1) + 1;
-+ start = max(addr & PMD_MASK, pvmw->vma->vm_start);
-+ end = min(addr | ~PMD_MASK, pvmw->vma->vm_end - 1) + 1;
-
- if (end - start > MIN_LRU_BATCH * PAGE_SIZE) {
-- if (pvmw->address - start < MIN_LRU_BATCH * PAGE_SIZE / 2)
-+ if (addr - start < MIN_LRU_BATCH * PAGE_SIZE / 2)
- end = start + MIN_LRU_BATCH * PAGE_SIZE;
-- else if (end - pvmw->address < MIN_LRU_BATCH * PAGE_SIZE / 2)
-+ else if (end - addr < MIN_LRU_BATCH * PAGE_SIZE / 2)
- start = end - MIN_LRU_BATCH * PAGE_SIZE;
- else {
-- start = pvmw->address - MIN_LRU_BATCH * PAGE_SIZE / 2;
-- end = pvmw->address + MIN_LRU_BATCH * PAGE_SIZE / 2;
-+ start = addr - MIN_LRU_BATCH * PAGE_SIZE / 2;
-+ end = addr + MIN_LRU_BATCH * PAGE_SIZE / 2;
- }
- }
-
-- pte = pvmw->pte - (pvmw->address - start) / PAGE_SIZE;
-+ /* folio_update_gen() requires stable folio_memcg() */
-+ if (!mem_cgroup_trylock_pages(memcg))
-+ return;
-
-- rcu_read_lock();
- arch_enter_lazy_mmu_mode();
-
-+ pte -= (addr - start) / PAGE_SIZE;
-+
- for (i = 0, addr = start; addr != end; i++, addr += PAGE_SIZE) {
- unsigned long pfn;
-
-@@ -4639,56 +4641,27 @@ void lru_gen_look_around(struct page_vma
- !folio_test_swapcache(folio)))
- folio_mark_dirty(folio);
-
-+ if (walk) {
-+ old_gen = folio_update_gen(folio, new_gen);
-+ if (old_gen >= 0 && old_gen != new_gen)
-+ update_batch_size(walk, folio, old_gen, new_gen);
-+
-+ continue;
-+ }
-+
- old_gen = folio_lru_gen(folio);
- if (old_gen < 0)
- folio_set_referenced(folio);
- else if (old_gen != new_gen)
-- __set_bit(i, bitmap);
-+ folio_activate(folio);
- }
-
- arch_leave_lazy_mmu_mode();
-- rcu_read_unlock();
-+ mem_cgroup_unlock_pages();
-
- /* feedback from rmap walkers to page table walkers */
- if (suitable_to_scan(i, young))
- update_bloom_filter(lruvec, max_seq, pvmw->pmd);
--
-- if (!walk && bitmap_weight(bitmap, MIN_LRU_BATCH) < PAGEVEC_SIZE) {
-- for_each_set_bit(i, bitmap, MIN_LRU_BATCH) {
-- folio = pfn_folio(pte_pfn(pte[i]));
-- folio_activate(folio);
-- }
-- return;
-- }
--
-- /* folio_update_gen() requires stable folio_memcg() */
-- if (!mem_cgroup_trylock_pages(memcg))
-- return;
--
-- if (!walk) {
-- spin_lock_irq(&lruvec->lru_lock);
-- new_gen = lru_gen_from_seq(lruvec->lrugen.max_seq);
-- }
--
-- for_each_set_bit(i, bitmap, MIN_LRU_BATCH) {
-- folio = pfn_folio(pte_pfn(pte[i]));
-- if (folio_memcg_rcu(folio) != memcg)
-- continue;
--
-- old_gen = folio_update_gen(folio, new_gen);
-- if (old_gen < 0 || old_gen == new_gen)
-- continue;
--
-- if (walk)
-- update_batch_size(walk, folio, old_gen, new_gen);
-- else
-- lru_gen_update_size(lruvec, folio, old_gen, new_gen);
-- }
--
-- if (!walk)
-- spin_unlock_irq(&lruvec->lru_lock);
--
-- mem_cgroup_unlock_pages();
- }
-
- /******************************************************************************
+++ /dev/null
-From 418038c22452df38cde519cc8c662bb15139764a Mon Sep 17 00:00:00 2001
-From: Kalesh Singh <kaleshsingh@google.com>
-Date: Thu, 13 Apr 2023 14:43:26 -0700
-Subject: [PATCH 19/19] mm: Multi-gen LRU: remove wait_event_killable()
-
-Android 14 and later default to MGLRU [1] and field telemetry showed
-occasional long tail latency (>100ms) in the reclaim path.
-
-Tracing revealed priority inversion in the reclaim path. In
-try_to_inc_max_seq(), when high priority tasks were blocked on
-wait_event_killable(), the preemption of the low priority task to call
-wake_up_all() caused those high priority tasks to wait longer than
-necessary. In general, this problem is not different from others of its
-kind, e.g., one caused by mutex_lock(). However, it is specific to MGLRU
-because it introduced the new wait queue lruvec->mm_state.wait.
-
-The purpose of this new wait queue is to avoid the thundering herd
-problem. If many direct reclaimers rush into try_to_inc_max_seq(), only
-one can succeed, i.e., the one to wake up the rest, and the rest who
-failed might cause premature OOM kills if they do not wait. So far there
-is no evidence supporting this scenario, based on how often the wait has
-been hit. And this begs the question how useful the wait queue is in
-practice.
-
-Based on Minchan's recommendation, which is in line with his commit
-6d4675e60135 ("mm: don't be stuck to rmap lock on reclaim path") and the
-rest of the MGLRU code which also uses trylock when possible, remove the
-wait queue.
-
-[1] https://android-review.googlesource.com/q/I7ed7fbfd6ef9ce10053347528125dd98c39e50bf
-
-Link: https://lkml.kernel.org/r/20230413214326.2147568-1-kaleshsingh@google.com
-Fixes: bd74fdaea146 ("mm: multi-gen LRU: support page table walks")
-Signed-off-by: Kalesh Singh <kaleshsingh@google.com>
-Suggested-by: Minchan Kim <minchan@kernel.org>
-Reported-by: Wei Wang <wvw@google.com>
-Acked-by: Yu Zhao <yuzhao@google.com>
-Cc: Minchan Kim <minchan@kernel.org>
-Cc: Jan Alexander Steffens (heftig) <heftig@archlinux.org>
-Cc: Oleksandr Natalenko <oleksandr@natalenko.name>
-Cc: Suleiman Souhlal <suleiman@google.com>
-Cc: Suren Baghdasaryan <surenb@google.com>
-Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
----
- include/linux/mmzone.h | 8 +--
- mm/vmscan.c | 112 +++++++++++++++--------------------------
- 2 files changed, 42 insertions(+), 78 deletions(-)
-
---- a/include/linux/mmzone.h
-+++ b/include/linux/mmzone.h
-@@ -453,18 +453,14 @@ enum {
- struct lru_gen_mm_state {
- /* set to max_seq after each iteration */
- unsigned long seq;
-- /* where the current iteration continues (inclusive) */
-+ /* where the current iteration continues after */
- struct list_head *head;
-- /* where the last iteration ended (exclusive) */
-+ /* where the last iteration ended before */
- struct list_head *tail;
-- /* to wait for the last page table walker to finish */
-- struct wait_queue_head wait;
- /* Bloom filters flip after each iteration */
- unsigned long *filters[NR_BLOOM_FILTERS];
- /* the mm stats for debugging */
- unsigned long stats[NR_HIST_GENS][NR_MM_STATS];
-- /* the number of concurrent page table walkers */
-- int nr_walkers;
- };
-
- struct lru_gen_mm_walk {
---- a/mm/vmscan.c
-+++ b/mm/vmscan.c
-@@ -3371,18 +3371,13 @@ void lru_gen_del_mm(struct mm_struct *mm
- if (!lruvec)
- continue;
-
-- /* where the last iteration ended (exclusive) */
-+ /* where the current iteration continues after */
-+ if (lruvec->mm_state.head == &mm->lru_gen.list)
-+ lruvec->mm_state.head = lruvec->mm_state.head->prev;
-+
-+ /* where the last iteration ended before */
- if (lruvec->mm_state.tail == &mm->lru_gen.list)
- lruvec->mm_state.tail = lruvec->mm_state.tail->next;
--
-- /* where the current iteration continues (inclusive) */
-- if (lruvec->mm_state.head != &mm->lru_gen.list)
-- continue;
--
-- lruvec->mm_state.head = lruvec->mm_state.head->next;
-- /* the deletion ends the current iteration */
-- if (lruvec->mm_state.head == &mm_list->fifo)
-- WRITE_ONCE(lruvec->mm_state.seq, lruvec->mm_state.seq + 1);
- }
-
- list_del_init(&mm->lru_gen.list);
-@@ -3478,68 +3473,54 @@ static bool iterate_mm_list(struct lruve
- struct mm_struct **iter)
- {
- bool first = false;
-- bool last = true;
-+ bool last = false;
- struct mm_struct *mm = NULL;
- struct mem_cgroup *memcg = lruvec_memcg(lruvec);
- struct lru_gen_mm_list *mm_list = get_mm_list(memcg);
- struct lru_gen_mm_state *mm_state = &lruvec->mm_state;
-
- /*
-- * There are four interesting cases for this page table walker:
-- * 1. It tries to start a new iteration of mm_list with a stale max_seq;
-- * there is nothing left to do.
-- * 2. It's the first of the current generation, and it needs to reset
-- * the Bloom filter for the next generation.
-- * 3. It reaches the end of mm_list, and it needs to increment
-- * mm_state->seq; the iteration is done.
-- * 4. It's the last of the current generation, and it needs to reset the
-- * mm stats counters for the next generation.
-+ * mm_state->seq is incremented after each iteration of mm_list. There
-+ * are three interesting cases for this page table walker:
-+ * 1. It tries to start a new iteration with a stale max_seq: there is
-+ * nothing left to do.
-+ * 2. It started the next iteration: it needs to reset the Bloom filter
-+ * so that a fresh set of PTE tables can be recorded.
-+ * 3. It ended the current iteration: it needs to reset the mm stats
-+ * counters and tell its caller to increment max_seq.
- */
- spin_lock(&mm_list->lock);
-
- VM_WARN_ON_ONCE(mm_state->seq + 1 < walk->max_seq);
-- VM_WARN_ON_ONCE(*iter && mm_state->seq > walk->max_seq);
-- VM_WARN_ON_ONCE(*iter && !mm_state->nr_walkers);
-
-- if (walk->max_seq <= mm_state->seq) {
-- if (!*iter)
-- last = false;
-+ if (walk->max_seq <= mm_state->seq)
- goto done;
-- }
-
-- if (!mm_state->nr_walkers) {
-- VM_WARN_ON_ONCE(mm_state->head && mm_state->head != &mm_list->fifo);
-+ if (!mm_state->head)
-+ mm_state->head = &mm_list->fifo;
-
-- mm_state->head = mm_list->fifo.next;
-+ if (mm_state->head == &mm_list->fifo)
- first = true;
-- }
--
-- while (!mm && mm_state->head != &mm_list->fifo) {
-- mm = list_entry(mm_state->head, struct mm_struct, lru_gen.list);
-
-+ do {
- mm_state->head = mm_state->head->next;
-+ if (mm_state->head == &mm_list->fifo) {
-+ WRITE_ONCE(mm_state->seq, mm_state->seq + 1);
-+ last = true;
-+ break;
-+ }
-
- /* force scan for those added after the last iteration */
-- if (!mm_state->tail || mm_state->tail == &mm->lru_gen.list) {
-- mm_state->tail = mm_state->head;
-+ if (!mm_state->tail || mm_state->tail == mm_state->head) {
-+ mm_state->tail = mm_state->head->next;
- walk->force_scan = true;
- }
-
-+ mm = list_entry(mm_state->head, struct mm_struct, lru_gen.list);
- if (should_skip_mm(mm, walk))
- mm = NULL;
-- }
--
-- if (mm_state->head == &mm_list->fifo)
-- WRITE_ONCE(mm_state->seq, mm_state->seq + 1);
-+ } while (!mm);
- done:
-- if (*iter && !mm)
-- mm_state->nr_walkers--;
-- if (!*iter && mm)
-- mm_state->nr_walkers++;
--
-- if (mm_state->nr_walkers)
-- last = false;
--
- if (*iter || last)
- reset_mm_stats(lruvec, walk, last);
-
-@@ -3567,9 +3548,9 @@ static bool iterate_mm_list_nowalk(struc
-
- VM_WARN_ON_ONCE(mm_state->seq + 1 < max_seq);
-
-- if (max_seq > mm_state->seq && !mm_state->nr_walkers) {
-- VM_WARN_ON_ONCE(mm_state->head && mm_state->head != &mm_list->fifo);
--
-+ if (max_seq > mm_state->seq) {
-+ mm_state->head = NULL;
-+ mm_state->tail = NULL;
- WRITE_ONCE(mm_state->seq, mm_state->seq + 1);
- reset_mm_stats(lruvec, NULL, true);
- success = true;
-@@ -4172,10 +4153,6 @@ restart:
-
- walk_pmd_range(&val, addr, next, args);
-
-- /* a racy check to curtail the waiting time */
-- if (wq_has_sleeper(&walk->lruvec->mm_state.wait))
-- return 1;
--
- if (need_resched() || walk->batched >= MAX_LRU_BATCH) {
- end = (addr | ~PUD_MASK) + 1;
- goto done;
-@@ -4208,8 +4185,14 @@ static void walk_mm(struct lruvec *lruve
- walk->next_addr = FIRST_USER_ADDRESS;
-
- do {
-+ DEFINE_MAX_SEQ(lruvec);
-+
- err = -EBUSY;
-
-+ /* another thread might have called inc_max_seq() */
-+ if (walk->max_seq != max_seq)
-+ break;
-+
- /* folio_update_gen() requires stable folio_memcg() */
- if (!mem_cgroup_trylock_pages(memcg))
- break;
-@@ -4444,25 +4427,12 @@ static bool try_to_inc_max_seq(struct lr
- success = iterate_mm_list(lruvec, walk, &mm);
- if (mm)
- walk_mm(lruvec, mm, walk);
--
-- cond_resched();
- } while (mm);
- done:
-- if (!success) {
-- if (sc->priority <= DEF_PRIORITY - 2)
-- wait_event_killable(lruvec->mm_state.wait,
-- max_seq < READ_ONCE(lrugen->max_seq));
-- return false;
-- }
-+ if (success)
-+ inc_max_seq(lruvec, can_swap, force_scan);
-
-- VM_WARN_ON_ONCE(max_seq != READ_ONCE(lrugen->max_seq));
--
-- inc_max_seq(lruvec, can_swap, force_scan);
-- /* either this sees any waiters or they will see updated max_seq */
-- if (wq_has_sleeper(&lruvec->mm_state.wait))
-- wake_up_all(&lruvec->mm_state.wait);
--
-- return true;
-+ return success;
- }
-
- /******************************************************************************
-@@ -6117,7 +6087,6 @@ void lru_gen_init_lruvec(struct lruvec *
- INIT_LIST_HEAD(&lrugen->folios[gen][type][zone]);
-
- lruvec->mm_state.seq = MIN_NR_GENS;
-- init_waitqueue_head(&lruvec->mm_state.wait);
- }
-
- #ifdef CONFIG_MEMCG
-@@ -6150,7 +6119,6 @@ void lru_gen_exit_memcg(struct mem_cgrou
- for_each_node(nid) {
- struct lruvec *lruvec = get_lruvec(memcg, nid);
-
-- VM_WARN_ON_ONCE(lruvec->mm_state.nr_walkers);
- VM_WARN_ON_ONCE(memchr_inv(lruvec->lrugen.nr_pages, 0,
- sizeof(lruvec->lrugen.nr_pages)));
-
+++ /dev/null
-From e2192de59e457aef8d1f055a452131f0b3e5d097 Mon Sep 17 00:00:00 2001
-From: Johannes Berg <johannes.berg@intel.com>
-Date: Wed, 18 Jan 2023 14:26:53 +0100
-Subject: [PATCH] bitfield: add FIELD_PREP_CONST()
-
-Neither FIELD_PREP() nor *_encode_bits() can be used
-in constant contexts (such as initializers), but we
-don't want to define shift constants for all masks
-just for use in initializers, and having checks that
-the values fit is also useful.
-
-Therefore, add FIELD_PREP_CONST() which is a smaller
-version of FIELD_PREP() that can only take constant
-arguments and has less friendly (but not less strict)
-error checks, and expands to a constant value.
-
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-Link: https://lore.kernel.org/r/20230118142652.53f20593504b.Iaeea0aee77a6493d70e573b4aa55c91c00e01e4b@changeid
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
- include/linux/bitfield.h | 26 ++++++++++++++++++++++++++
- 1 file changed, 26 insertions(+)
-
---- a/include/linux/bitfield.h
-+++ b/include/linux/bitfield.h
-@@ -115,6 +115,32 @@
- ((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask); \
- })
-
-+#define __BF_CHECK_POW2(n) BUILD_BUG_ON_ZERO(((n) & ((n) - 1)) != 0)
-+
-+/**
-+ * FIELD_PREP_CONST() - prepare a constant bitfield element
-+ * @_mask: shifted mask defining the field's length and position
-+ * @_val: value to put in the field
-+ *
-+ * FIELD_PREP_CONST() masks and shifts up the value. The result should
-+ * be combined with other fields of the bitfield using logical OR.
-+ *
-+ * Unlike FIELD_PREP() this is a constant expression and can therefore
-+ * be used in initializers. Error checking is less comfortable for this
-+ * version, and non-constant masks cannot be used.
-+ */
-+#define FIELD_PREP_CONST(_mask, _val) \
-+ ( \
-+ /* mask must be non-zero */ \
-+ BUILD_BUG_ON_ZERO((_mask) == 0) + \
-+ /* check if value fits */ \
-+ BUILD_BUG_ON_ZERO(~((_mask) >> __bf_shf(_mask)) & (_val)) + \
-+ /* check if mask is contiguous */ \
-+ __BF_CHECK_POW2((_mask) + (1ULL << __bf_shf(_mask))) + \
-+ /* and create the value */ \
-+ (((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask)) \
-+ )
-+
- /**
- * FIELD_GET() - extract a bitfield element
- * @_mask: shifted mask defining the field's length and position
+++ /dev/null
-From 579aee9fc594af94c242068c011b0233563d4bbf Mon Sep 17 00:00:00 2001
-From: Stephen Rothwell <sfr@canb.auug.org.au>
-Date: Mon, 10 Oct 2022 16:57:21 +1100
-Subject: [PATCH] powerpc: suppress some linker warnings in recent linker
- versions
-
-This is a follow on from commit
-
- 0d362be5b142 ("Makefile: link with -z noexecstack --no-warn-rwx-segments")
-
-for arch/powerpc/boot to address wanrings like:
-
- ld: warning: opal-calls.o: missing .note.GNU-stack section implies executable stack
- ld: NOTE: This behaviour is deprecated and will be removed in a future version of the linker
- ld: warning: arch/powerpc/boot/zImage.epapr has a LOAD segment with RWX permissions
-
-This fixes issue https://github.com/linuxppc/issues/issues/417
-
-Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
-Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
-Link: https://lore.kernel.org/r/20221010165721.106267e6@canb.auug.org.au
----
- arch/powerpc/boot/wrapper | 15 ++++++++++++++-
- 1 file changed, 14 insertions(+), 1 deletion(-)
-
---- a/arch/powerpc/boot/wrapper
-+++ b/arch/powerpc/boot/wrapper
-@@ -215,6 +215,11 @@ ld_version()
- }'
- }
-
-+ld_is_lld()
-+{
-+ ${CROSS}ld -V 2>&1 | grep -q LLD
-+}
-+
- # Do not include PT_INTERP segment when linking pie. Non-pie linking
- # just ignores this option.
- LD_VERSION=$(${CROSS}ld --version | ld_version)
-@@ -223,6 +228,14 @@ if [ "$LD_VERSION" -ge "$LD_NO_DL_MIN_VE
- nodl="--no-dynamic-linker"
- fi
-
-+# suppress some warnings in recent ld versions
-+nowarn="-z noexecstack"
-+if ! ld_is_lld; then
-+ if [ "$LD_VERSION" -ge "$(echo 2.39 | ld_version)" ]; then
-+ nowarn="$nowarn --no-warn-rwx-segments"
-+ fi
-+fi
-+
- platformo=$object/"$platform".o
- lds=$object/zImage.lds
- ext=strip
-@@ -504,7 +517,7 @@ if [ "$platform" != "miboot" ]; then
- text_start="-Ttext $link_address"
- fi
- #link everything
-- ${CROSS}ld -m $format -T $lds $text_start $pie $nodl $rodynamic $notext -o "$ofile" $map \
-+ ${CROSS}ld -m $format -T $lds $text_start $pie $nodl $nowarn $rodynamic $notext -o "$ofile" $map \
- $platformo $tmp $object/wrapper.a
- rm $tmp
- fi
+++ /dev/null
-From 63db0cb35e1cb3b3c134906d1062f65513fdda2d Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
-Date: Tue, 4 Oct 2022 10:37:09 +0200
-Subject: [PATCH] mtd: core: simplify (a bit) code find partition-matching
- dynamic OF node
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-1. Don't hardcode "partition-" string twice
-2. Use simpler logic & use ->name to avoid of_property_read_string()
-3. Use mtd_get_of_node() helper
-
-Cc: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Link: https://lore.kernel.org/linux-mtd/20221004083710.27704-1-zajec5@gmail.com
----
- drivers/mtd/mtdcore.c | 16 +++++++---------
- 1 file changed, 7 insertions(+), 9 deletions(-)
-
---- a/drivers/mtd/mtdcore.c
-+++ b/drivers/mtd/mtdcore.c
-@@ -551,18 +551,16 @@ static void mtd_check_of_node(struct mtd
- struct device_node *partitions, *parent_dn, *mtd_dn = NULL;
- const char *pname, *prefix = "partition-";
- int plen, mtd_name_len, offset, prefix_len;
-- struct mtd_info *parent;
- bool found = false;
-
- /* Check if MTD already has a device node */
-- if (dev_of_node(&mtd->dev))
-+ if (mtd_get_of_node(mtd))
- return;
-
- /* Check if a partitions node exist */
- if (!mtd_is_partition(mtd))
- return;
-- parent = mtd->parent;
-- parent_dn = of_node_get(dev_of_node(&parent->dev));
-+ parent_dn = of_node_get(mtd_get_of_node(mtd->parent));
- if (!parent_dn)
- return;
-
-@@ -575,15 +573,15 @@ static void mtd_check_of_node(struct mtd
-
- /* Search if a partition is defined with the same name */
- for_each_child_of_node(partitions, mtd_dn) {
-- offset = 0;
--
- /* Skip partition with no/wrong prefix */
-- if (!of_node_name_prefix(mtd_dn, "partition-"))
-+ if (!of_node_name_prefix(mtd_dn, prefix))
- continue;
-
- /* Label have priority. Check that first */
-- if (of_property_read_string(mtd_dn, "label", &pname)) {
-- of_property_read_string(mtd_dn, "name", &pname);
-+ if (!of_property_read_string(mtd_dn, "label", &pname)) {
-+ offset = 0;
-+ } else {
-+ pname = mtd_dn->name;
- offset = prefix_len;
- }
-
+++ /dev/null
-From ddb8cefb7af288950447ca6eeeafb09977dab56f Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
-Date: Tue, 4 Oct 2022 10:37:10 +0200
-Subject: [PATCH] mtd: core: try to find OF node for every MTD partition
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-So far this feature was limited to the top-level "nvmem-cells" node.
-There are multiple parsers creating partitions and subpartitions
-dynamically. Extend that code to handle them too.
-
-This allows finding partition-* node for every MTD (sub)partition.
-
-Random example:
-
-partitions {
- compatible = "brcm,bcm947xx-cfe-partitions";
-
- partition-firmware {
- compatible = "brcm,trx";
-
- partition-loader {
- };
- };
-};
-
-Cc: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Link: https://lore.kernel.org/linux-mtd/20221004083710.27704-2-zajec5@gmail.com
----
- drivers/mtd/mtdcore.c | 18 ++++++------------
- 1 file changed, 6 insertions(+), 12 deletions(-)
-
---- a/drivers/mtd/mtdcore.c
-+++ b/drivers/mtd/mtdcore.c
-@@ -551,20 +551,22 @@ static void mtd_check_of_node(struct mtd
- struct device_node *partitions, *parent_dn, *mtd_dn = NULL;
- const char *pname, *prefix = "partition-";
- int plen, mtd_name_len, offset, prefix_len;
-- bool found = false;
-
- /* Check if MTD already has a device node */
- if (mtd_get_of_node(mtd))
- return;
-
-- /* Check if a partitions node exist */
- if (!mtd_is_partition(mtd))
- return;
-+
- parent_dn = of_node_get(mtd_get_of_node(mtd->parent));
- if (!parent_dn)
- return;
-
-- partitions = of_get_child_by_name(parent_dn, "partitions");
-+ if (mtd_is_partition(mtd->parent))
-+ partitions = of_node_get(parent_dn);
-+ else
-+ partitions = of_get_child_by_name(parent_dn, "partitions");
- if (!partitions)
- goto exit_parent;
-
-@@ -588,19 +590,11 @@ static void mtd_check_of_node(struct mtd
- plen = strlen(pname) - offset;
- if (plen == mtd_name_len &&
- !strncmp(mtd->name, pname + offset, plen)) {
-- found = true;
-+ mtd_set_of_node(mtd, mtd_dn);
- break;
- }
- }
-
-- if (!found)
-- goto exit_partitions;
--
-- /* Set of_node only for nvmem */
-- if (of_device_is_compatible(mtd_dn, "nvmem-cells"))
-- mtd_set_of_node(mtd, mtd_dn);
--
--exit_partitions:
- of_node_put(partitions);
- exit_parent:
- of_node_put(parent_dn);
+++ /dev/null
-From 26422ac78e9d8767bd4aabfbae616b15edbf6a1b Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
-Date: Sat, 22 Oct 2022 23:13:18 +0200
-Subject: [PATCH] mtd: core: set ROOT_DEV for partitions marked as rootfs in DT
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This adds support for "linux,rootfs" binding that is used to mark flash
-partition containing rootfs. It's useful for devices using device tree
-that don't have bootloader passing root info in cmdline.
-
-Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Link: https://lore.kernel.org/linux-mtd/20221022211318.32009-2-zajec5@gmail.com
----
- drivers/mtd/mtdcore.c | 12 ++++++++++++
- 1 file changed, 12 insertions(+)
-
---- a/drivers/mtd/mtdcore.c
-+++ b/drivers/mtd/mtdcore.c
-@@ -28,6 +28,7 @@
- #include <linux/leds.h>
- #include <linux/debugfs.h>
- #include <linux/nvmem-provider.h>
-+#include <linux/root_dev.h>
-
- #include <linux/mtd/mtd.h>
- #include <linux/mtd/partitions.h>
-@@ -737,6 +738,17 @@ int add_mtd_device(struct mtd_info *mtd)
- not->add(mtd);
-
- mutex_unlock(&mtd_table_mutex);
-+
-+ if (of_find_property(mtd_get_of_node(mtd), "linux,rootfs", NULL)) {
-+ if (IS_BUILTIN(CONFIG_MTD)) {
-+ pr_info("mtd: setting mtd%d (%s) as root device\n", mtd->index, mtd->name);
-+ ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, mtd->index);
-+ } else {
-+ pr_warn("mtd: can't set mtd%d (%s) as root device - mtd must be builtin\n",
-+ mtd->index, mtd->name);
-+ }
-+ }
-+
- /* We _know_ we aren't being removed, because
- our caller is still holding us here. So none
- of this try_ nonsense, and no bitching about it
+++ /dev/null
-From aec4d5f5ffd0f0092bd9dc21ea90e0bc237d4b74 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
-Date: Sat, 15 Oct 2022 11:29:50 +0200
-Subject: [PATCH] mtd: parsers: add TP-Link SafeLoader partitions table parser
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This parser deals with most TP-Link home routers. It reads info about
-partitions and registers them in the MTD subsystem.
-
-Example from TP-Link Archer C5 V2:
-
-spi-nor spi0.0: s25fl128s1 (16384 Kbytes)
-15 tplink-safeloader partitions found on MTD device spi0.0
-Creating 15 MTD partitions on "spi0.0":
-0x000000000000-0x000000040000 : "fs-uboot"
-0x000000040000-0x000000440000 : "os-image"
-0x000000440000-0x000000e40000 : "rootfs"
-0x000000e40000-0x000000e40200 : "default-mac"
-0x000000e40200-0x000000e40400 : "pin"
-0x000000e40400-0x000000e40600 : "product-info"
-0x000000e50000-0x000000e60000 : "partition-table"
-0x000000e60000-0x000000e60200 : "soft-version"
-0x000000e61000-0x000000e70000 : "support-list"
-0x000000e70000-0x000000e80000 : "profile"
-0x000000e80000-0x000000e90000 : "default-config"
-0x000000e90000-0x000000ee0000 : "user-config"
-0x000000ee0000-0x000000fe0000 : "log"
-0x000000fe0000-0x000000ff0000 : "radio_bk"
-0x000000ff0000-0x000001000000 : "radio"
-
-Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Link: https://lore.kernel.org/linux-mtd/20221015092950.27467-2-zajec5@gmail.com
----
- drivers/mtd/parsers/Kconfig | 15 +++
- drivers/mtd/parsers/Makefile | 1 +
- drivers/mtd/parsers/tplink_safeloader.c | 150 ++++++++++++++++++++++++
- 3 files changed, 166 insertions(+)
- create mode 100644 drivers/mtd/parsers/tplink_safeloader.c
-
---- a/drivers/mtd/parsers/Kconfig
-+++ b/drivers/mtd/parsers/Kconfig
-@@ -123,6 +123,21 @@ config MTD_AFS_PARTS
- for your particular device. It won't happen automatically. The
- 'physmap' map driver (CONFIG_MTD_PHYSMAP) does this, for example.
-
-+config MTD_PARSER_TPLINK_SAFELOADER
-+ tristate "TP-Link Safeloader partitions parser"
-+ depends on MTD && (ARCH_BCM_5301X || ATH79 || SOC_MT7620 || SOC_MT7621 || COMPILE_TEST)
-+ help
-+ TP-Link home routers use flash partitions to store various data. Info
-+ about flash space layout is stored in a partitions table using a
-+ custom ASCII-based format.
-+
-+ That format was first found in devices with SafeLoader bootloader and
-+ was named after it. Later it was adapted to CFE and U-Boot
-+ bootloaders.
-+
-+ This driver reads partitions table, parses it and creates MTD
-+ partitions.
-+
- config MTD_PARSER_TRX
- tristate "Parser for TRX format partitions"
- depends on MTD && (BCM47XX || ARCH_BCM_5301X || ARCH_MEDIATEK || RALINK || COMPILE_TEST)
---- a/drivers/mtd/parsers/Makefile
-+++ b/drivers/mtd/parsers/Makefile
-@@ -10,6 +10,7 @@ ofpart-$(CONFIG_MTD_OF_PARTS_BCM4908) +=
- ofpart-$(CONFIG_MTD_OF_PARTS_LINKSYS_NS)+= ofpart_linksys_ns.o
- obj-$(CONFIG_MTD_PARSER_IMAGETAG) += parser_imagetag.o
- obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
-+obj-$(CONFIG_MTD_PARSER_TPLINK_SAFELOADER) += tplink_safeloader.o
- obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o
- obj-$(CONFIG_MTD_SERCOMM_PARTS) += scpart.o
- obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o
---- /dev/null
-+++ b/drivers/mtd/parsers/tplink_safeloader.c
-@@ -0,0 +1,150 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+/*
-+ * Copyright © 2022 Rafał Miłecki <rafal@milecki.pl>
-+ */
-+
-+#include <linux/kernel.h>
-+#include <linux/module.h>
-+#include <linux/mtd/mtd.h>
-+#include <linux/mtd/partitions.h>
-+#include <linux/of.h>
-+#include <linux/slab.h>
-+
-+#define TPLINK_SAFELOADER_DATA_OFFSET 4
-+#define TPLINK_SAFELOADER_MAX_PARTS 32
-+
-+struct safeloader_cmn_header {
-+ __be32 size;
-+ uint32_t unused;
-+} __packed;
-+
-+static void *mtd_parser_tplink_safeloader_read_table(struct mtd_info *mtd)
-+{
-+ struct safeloader_cmn_header hdr;
-+ struct device_node *np;
-+ size_t bytes_read;
-+ size_t offset;
-+ size_t size;
-+ char *buf;
-+ int err;
-+
-+ np = mtd_get_of_node(mtd);
-+ if (mtd_is_partition(mtd))
-+ of_node_get(np);
-+ else
-+ np = of_get_child_by_name(np, "partitions");
-+
-+ if (of_property_read_u32(np, "partitions-table-offset", (u32 *)&offset)) {
-+ pr_err("Failed to get partitions table offset\n");
-+ goto err_put;
-+ }
-+
-+ err = mtd_read(mtd, offset, sizeof(hdr), &bytes_read, (uint8_t *)&hdr);
-+ if (err && !mtd_is_bitflip(err)) {
-+ pr_err("Failed to read from %s at 0x%zx\n", mtd->name, offset);
-+ goto err_put;
-+ }
-+
-+ size = be32_to_cpu(hdr.size);
-+
-+ buf = kmalloc(size + 1, GFP_KERNEL);
-+ if (!buf)
-+ goto err_put;
-+
-+ err = mtd_read(mtd, offset + sizeof(hdr), size, &bytes_read, buf);
-+ if (err && !mtd_is_bitflip(err)) {
-+ pr_err("Failed to read from %s at 0x%zx\n", mtd->name, offset + sizeof(hdr));
-+ goto err_kfree;
-+ }
-+
-+ buf[size] = '\0';
-+
-+ of_node_put(np);
-+
-+ return buf;
-+
-+err_kfree:
-+ kfree(buf);
-+err_put:
-+ of_node_put(np);
-+ return NULL;
-+}
-+
-+static int mtd_parser_tplink_safeloader_parse(struct mtd_info *mtd,
-+ const struct mtd_partition **pparts,
-+ struct mtd_part_parser_data *data)
-+{
-+ struct mtd_partition *parts;
-+ char name[65];
-+ size_t offset;
-+ size_t bytes;
-+ char *buf;
-+ int idx;
-+ int err;
-+
-+ parts = kcalloc(TPLINK_SAFELOADER_MAX_PARTS, sizeof(*parts), GFP_KERNEL);
-+ if (!parts) {
-+ err = -ENOMEM;
-+ goto err_out;
-+ }
-+
-+ buf = mtd_parser_tplink_safeloader_read_table(mtd);
-+ if (!buf) {
-+ err = -ENOENT;
-+ goto err_out;
-+ }
-+
-+ for (idx = 0, offset = TPLINK_SAFELOADER_DATA_OFFSET;
-+ idx < TPLINK_SAFELOADER_MAX_PARTS &&
-+ sscanf(buf + offset, "partition %64s base 0x%llx size 0x%llx%zn\n",
-+ name, &parts[idx].offset, &parts[idx].size, &bytes) == 3;
-+ idx++, offset += bytes + 1) {
-+ parts[idx].name = kstrdup(name, GFP_KERNEL);
-+ if (!parts[idx].name) {
-+ err = -ENOMEM;
-+ goto err_free;
-+ }
-+ }
-+
-+ if (idx == TPLINK_SAFELOADER_MAX_PARTS)
-+ pr_warn("Reached maximum number of partitions!\n");
-+
-+ kfree(buf);
-+
-+ *pparts = parts;
-+
-+ return idx;
-+
-+err_free:
-+ for (idx -= 1; idx >= 0; idx--)
-+ kfree(parts[idx].name);
-+err_out:
-+ return err;
-+};
-+
-+static void mtd_parser_tplink_safeloader_cleanup(const struct mtd_partition *pparts,
-+ int nr_parts)
-+{
-+ int i;
-+
-+ for (i = 0; i < nr_parts; i++)
-+ kfree(pparts[i].name);
-+
-+ kfree(pparts);
-+}
-+
-+static const struct of_device_id mtd_parser_tplink_safeloader_of_match_table[] = {
-+ { .compatible = "tplink,safeloader-partitions" },
-+ {},
-+};
-+MODULE_DEVICE_TABLE(of, mtd_parser_tplink_safeloader_of_match_table);
-+
-+static struct mtd_part_parser mtd_parser_tplink_safeloader = {
-+ .parse_fn = mtd_parser_tplink_safeloader_parse,
-+ .cleanup = mtd_parser_tplink_safeloader_cleanup,
-+ .name = "tplink-safeloader",
-+ .of_match_table = mtd_parser_tplink_safeloader_of_match_table,
-+};
-+module_mtd_part_parser(mtd_parser_tplink_safeloader);
-+
-+MODULE_LICENSE("GPL");
+++ /dev/null
-From ebed787a0becb9354f0a23620a5130cccd6c730c Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Thu, 19 Jan 2023 03:45:43 +0000
-Subject: [PATCH] mtd: spinand: macronix: use scratch buffer for DMA operation
-
-The mx35lf1ge4ab_get_eccsr() function uses an SPI DMA operation to
-read the eccsr, hence the buffer should not be on stack. Since commit
-380583227c0c7f ("spi: spi-mem: Add extra sanity checks on the op param")
-the kernel emmits a warning and blocks such operations.
-
-Use the scratch buffer to get eccsr instead of trying to directly read
-into a stack-allocated variable.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Dhruva Gole <d-gole@ti.com>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Link: https://lore.kernel.org/linux-mtd/Y8i85zM0u4XdM46z@makrotopia.org
----
- drivers/mtd/nand/spi/macronix.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
---- a/drivers/mtd/nand/spi/macronix.c
-+++ b/drivers/mtd/nand/spi/macronix.c
-@@ -83,9 +83,10 @@ static int mx35lf1ge4ab_ecc_get_status(s
- * in order to avoid forcing the wear-leveling layer to move
- * data around if it's not necessary.
- */
-- if (mx35lf1ge4ab_get_eccsr(spinand, &eccsr))
-+ if (mx35lf1ge4ab_get_eccsr(spinand, spinand->scratchbuf))
- return nanddev_get_ecc_conf(nand)->strength;
-
-+ eccsr = *spinand->scratchbuf;
- if (WARN_ON(eccsr > nanddev_get_ecc_conf(nand)->strength ||
- !eccsr))
- return nanddev_get_ecc_conf(nand)->strength;
+++ /dev/null
-From 1ecf9e390452e73a362ea7fbde8f3f0db83de856 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Thu, 22 Dec 2022 19:33:04 +0000
-Subject: [PATCH] mtd: ubi: wire-up parent MTD device
-
-Wire up the device parent pointer of UBI devices to their lower MTD
-device, typically an MTD partition or whole-chip device.
-
-The most noticeable change is that in sysfs, previously ubi devices
-would be could in /sys/devices/virtual/ubi while after this change they
-would be correctly attached to their parent MTD device, e.g.
-
-/sys/devices/platform/1100d000.spi/spi_master/spi1/spi1.0/mtd/mtd2/ubi0.
-
-Locating UBI devices using /sys/class/ubi/ of course still works as
-well.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: Richard Weinberger <richard@nod.at>
----
- drivers/mtd/ubi/build.c | 1 +
- 1 file changed, 1 insertion(+)
-
---- a/drivers/mtd/ubi/build.c
-+++ b/drivers/mtd/ubi/build.c
-@@ -929,6 +929,7 @@ int ubi_attach_mtd_dev(struct mtd_info *
- ubi->dev.release = dev_release;
- ubi->dev.class = &ubi_class;
- ubi->dev.groups = ubi_dev_groups;
-+ ubi->dev.parent = &mtd->dev;
-
- ubi->mtd = mtd;
- ubi->ubi_num = ubi_num;
+++ /dev/null
-From 05b8773ca33253ea562be145cf3145b05ef19f86 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Thu, 22 Dec 2022 19:33:31 +0000
-Subject: [PATCH] mtd: ubi: block: wire-up device parent
-
-ubiblock devices were previously only identifyable by their name, but
-not connected to their parent UBI volume device e.g. in sysfs.
-Properly parent ubiblock device as descendant of a UBI volume device
-to reflect device model hierachy.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: Richard Weinberger <richard@nod.at>
----
- drivers/mtd/ubi/block.c | 2 +-
- drivers/mtd/ubi/kapi.c | 1 +
- include/linux/mtd/ubi.h | 1 +
- 3 files changed, 3 insertions(+), 1 deletion(-)
-
---- a/drivers/mtd/ubi/block.c
-+++ b/drivers/mtd/ubi/block.c
-@@ -452,7 +452,7 @@ int ubiblock_create(struct ubi_volume_in
- list_add_tail(&dev->list, &ubiblock_devices);
-
- /* Must be the last step: anyone can call file ops from now on */
-- ret = add_disk(dev->gd);
-+ ret = device_add_disk(vi->dev, dev->gd, NULL);
- if (ret)
- goto out_destroy_wq;
-
---- a/drivers/mtd/ubi/kapi.c
-+++ b/drivers/mtd/ubi/kapi.c
-@@ -79,6 +79,7 @@ void ubi_do_get_volume_info(struct ubi_d
- vi->name_len = vol->name_len;
- vi->name = vol->name;
- vi->cdev = vol->cdev.dev;
-+ vi->dev = &vol->dev;
- }
-
- /**
---- a/include/linux/mtd/ubi.h
-+++ b/include/linux/mtd/ubi.h
-@@ -110,6 +110,7 @@ struct ubi_volume_info {
- int name_len;
- const char *name;
- dev_t cdev;
-+ struct device *dev;
- };
-
- /**
+++ /dev/null
-From 281f7a6c1a33fffcde32001bacbb4f672140fbf9 Mon Sep 17 00:00:00 2001
-From: Michael Walle <michael@walle.cc>
-Date: Wed, 8 Mar 2023 09:20:21 +0100
-Subject: [PATCH] mtd: core: prepare mtd_otp_nvmem_add() to handle
- -EPROBE_DEFER
-
-NVMEM soon will get the ability for nvmem layouts and these might
-not be ready when nvmem_register() is called and thus it might
-return -EPROBE_DEFER. Don't print the error message in this case.
-
-Signed-off-by: Michael Walle <michael@walle.cc>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Link: https://lore.kernel.org/linux-mtd/20230308082021.870459-4-michael@walle.cc
----
- drivers/mtd/mtdcore.c | 7 +++----
- 1 file changed, 3 insertions(+), 4 deletions(-)
-
---- a/drivers/mtd/mtdcore.c
-+++ b/drivers/mtd/mtdcore.c
-@@ -953,8 +953,8 @@ static int mtd_otp_nvmem_add(struct mtd_
- nvmem = mtd_otp_nvmem_register(mtd, "user-otp", size,
- mtd_nvmem_user_otp_reg_read);
- if (IS_ERR(nvmem)) {
-- dev_err(dev, "Failed to register OTP NVMEM device\n");
-- return PTR_ERR(nvmem);
-+ err = PTR_ERR(nvmem);
-+ goto err;
- }
- mtd->otp_user_nvmem = nvmem;
- }
-@@ -971,7 +971,6 @@ static int mtd_otp_nvmem_add(struct mtd_
- nvmem = mtd_otp_nvmem_register(mtd, "factory-otp", size,
- mtd_nvmem_fact_otp_reg_read);
- if (IS_ERR(nvmem)) {
-- dev_err(dev, "Failed to register OTP NVMEM device\n");
- err = PTR_ERR(nvmem);
- goto err;
- }
-@@ -983,7 +982,7 @@ static int mtd_otp_nvmem_add(struct mtd_
-
- err:
- nvmem_unregister(mtd->otp_user_nvmem);
-- return err;
-+ return dev_err_probe(dev, err, "Failed to register OTP NVMEM device\n");
- }
-
- /**
+++ /dev/null
-From 7390609b0121a1b982c5ecdfcd72dc328e5784ee Mon Sep 17 00:00:00 2001
-From: Michael Walle <michael@walle.cc>
-Date: Mon, 6 Feb 2023 13:43:42 +0000
-Subject: [PATCH] net: add helper eth_addr_add()
-
-Add a helper to add an offset to a ethernet address. This comes in handy
-if you have a base ethernet address for multiple interfaces.
-
-Signed-off-by: Michael Walle <michael@walle.cc>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Acked-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230206134356.839737-9-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- include/linux/etherdevice.h | 14 ++++++++++++++
- 1 file changed, 14 insertions(+)
-
---- a/include/linux/etherdevice.h
-+++ b/include/linux/etherdevice.h
-@@ -508,6 +508,20 @@ static inline void eth_addr_inc(u8 *addr
- }
-
- /**
-+ * eth_addr_add() - Add (or subtract) an offset to/from the given MAC address.
-+ *
-+ * @offset: Offset to add.
-+ * @addr: Pointer to a six-byte array containing Ethernet address to increment.
-+ */
-+static inline void eth_addr_add(u8 *addr, long offset)
-+{
-+ u64 u = ether_addr_to_u64(addr);
-+
-+ u += offset;
-+ u64_to_ether_addr(u, addr);
-+}
-+
-+/**
- * is_etherdev_addr - Tell if given Ethernet address belongs to the device.
- * @dev: Pointer to a device structure
- * @addr: Pointer to a six-byte array containing the Ethernet address
+++ /dev/null
-From d387e34fec407f881fdf165b5d7ec128ebff362f Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Tue, 19 Sep 2023 14:47:20 +0200
-Subject: [PATCH] net: sfp: add quirk for Fiberstone GPON-ONU-34-20BI
-
-Fiberstone GPON-ONU-34-20B can operate at 2500base-X, but report 1.2GBd
-NRZ in their EEPROM.
-
-The module also require the ignore tx fault fixup similar to Huawei MA5671A
-as it gets disabled on error messages with serial redirection enabled.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Link: https://lore.kernel.org/r/20230919124720.8210-1-ansuelsmth@gmail.com
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/phy/sfp.c | 5 +++++
- 1 file changed, 5 insertions(+)
-
---- a/drivers/net/phy/sfp.c
-+++ b/drivers/net/phy/sfp.c
-@@ -393,6 +393,11 @@ static const struct sfp_quirk sfp_quirks
- SFP_QUIRK("ALCATELLUCENT", "3FE46541AA", sfp_quirk_2500basex,
- sfp_fixup_long_startup),
-
-+ // Fiberstore GPON-ONU-34-20BI can operate at 2500base-X, but report 1.2GBd
-+ // NRZ in their EEPROM
-+ SFP_QUIRK("FS", "GPON-ONU-34-20BI", sfp_quirk_2500basex,
-+ sfp_fixup_ignore_tx_fault),
-+
- SFP_QUIRK_F("HALNy", "HL-GSFP", sfp_fixup_halny_gsfp),
-
- // HG MXPD-483II-F 2.5G supports 2500Base-X, but incorrectly reports
+++ /dev/null
-From 4e4aafcddbbfcdd6eed5780e190fcbfac8b4685a Mon Sep 17 00:00:00 2001
-From: Andrew Lunn <andrew@lunn.ch>
-Date: Mon, 9 Jan 2023 16:30:41 +0100
-Subject: [PATCH] net: mdio: Add dedicated C45 API to MDIO bus drivers
-
-Currently C22 and C45 transactions are mixed over a combined API calls
-which make use of a special bit in the reg address to indicate if a
-C45 transaction should be performed. This makes it impossible to know
-if the bus driver actually supports C45. Additionally, many C22 only
-drivers don't return -EOPNOTSUPP when asked to perform a C45
-transaction, they mistaking perform a C22 transaction.
-
-This is the first step to cleanly separate C22 from C45. To maintain
-backwards compatibility until all drivers which are capable of
-performing C45 are converted to this new API, the helper functions
-will fall back to the older API if the new API is not
-supported. Eventually this fallback will be removed.
-
-Signed-off-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: Michael Walle <michael@walle.cc>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/mdio_bus.c | 189 +++++++++++++++++++++++++++++++++++++
- include/linux/mdio.h | 39 ++++----
- include/linux/phy.h | 5 +
- 3 files changed, 214 insertions(+), 19 deletions(-)
-
---- a/drivers/net/phy/mdio_bus.c
-+++ b/drivers/net/phy/mdio_bus.c
-@@ -832,6 +832,100 @@ int __mdiobus_modify_changed(struct mii_
- EXPORT_SYMBOL_GPL(__mdiobus_modify_changed);
-
- /**
-+ * __mdiobus_c45_read - Unlocked version of the mdiobus_c45_read function
-+ * @bus: the mii_bus struct
-+ * @addr: the phy address
-+ * @devad: device address to read
-+ * @regnum: register number to read
-+ *
-+ * Read a MDIO bus register. Caller must hold the mdio bus lock.
-+ *
-+ * NOTE: MUST NOT be called from interrupt context.
-+ */
-+int __mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum)
-+{
-+ int retval;
-+
-+ lockdep_assert_held_once(&bus->mdio_lock);
-+
-+ if (bus->read_c45)
-+ retval = bus->read_c45(bus, addr, devad, regnum);
-+ else
-+ retval = bus->read(bus, addr, mdiobus_c45_addr(devad, regnum));
-+
-+ trace_mdio_access(bus, 1, addr, regnum, retval, retval);
-+ mdiobus_stats_acct(&bus->stats[addr], true, retval);
-+
-+ return retval;
-+}
-+EXPORT_SYMBOL(__mdiobus_c45_read);
-+
-+/**
-+ * __mdiobus_c45_write - Unlocked version of the mdiobus_write function
-+ * @bus: the mii_bus struct
-+ * @addr: the phy address
-+ * @devad: device address to read
-+ * @regnum: register number to write
-+ * @val: value to write to @regnum
-+ *
-+ * Write a MDIO bus register. Caller must hold the mdio bus lock.
-+ *
-+ * NOTE: MUST NOT be called from interrupt context.
-+ */
-+int __mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum,
-+ u16 val)
-+{
-+ int err;
-+
-+ lockdep_assert_held_once(&bus->mdio_lock);
-+
-+ if (bus->write_c45)
-+ err = bus->write_c45(bus, addr, devad, regnum, val);
-+ else
-+ err = bus->write(bus, addr, mdiobus_c45_addr(devad, regnum),
-+ val);
-+
-+ trace_mdio_access(bus, 0, addr, regnum, val, err);
-+ mdiobus_stats_acct(&bus->stats[addr], false, err);
-+
-+ return err;
-+}
-+EXPORT_SYMBOL(__mdiobus_c45_write);
-+
-+/**
-+ * __mdiobus_c45_modify_changed - Unlocked version of the mdiobus_modify function
-+ * @bus: the mii_bus struct
-+ * @addr: the phy address
-+ * @devad: device address to read
-+ * @regnum: register number to modify
-+ * @mask: bit mask of bits to clear
-+ * @set: bit mask of bits to set
-+ *
-+ * Read, modify, and if any change, write the register value back to the
-+ * device. Any error returns a negative number.
-+ *
-+ * NOTE: MUST NOT be called from interrupt context.
-+ */
-+static int __mdiobus_c45_modify_changed(struct mii_bus *bus, int addr,
-+ int devad, u32 regnum, u16 mask,
-+ u16 set)
-+{
-+ int new, ret;
-+
-+ ret = __mdiobus_c45_read(bus, addr, devad, regnum);
-+ if (ret < 0)
-+ return ret;
-+
-+ new = (ret & ~mask) | set;
-+ if (new == ret)
-+ return 0;
-+
-+ ret = __mdiobus_c45_write(bus, addr, devad, regnum, new);
-+
-+ return ret < 0 ? ret : 1;
-+}
-+
-+/**
- * mdiobus_read_nested - Nested version of the mdiobus_read function
- * @bus: the mii_bus struct
- * @addr: the phy address
-@@ -879,6 +973,29 @@ int mdiobus_read(struct mii_bus *bus, in
- EXPORT_SYMBOL(mdiobus_read);
-
- /**
-+ * mdiobus_c45_read - Convenience function for reading a given MII mgmt register
-+ * @bus: the mii_bus struct
-+ * @addr: the phy address
-+ * @devad: device address to read
-+ * @regnum: register number to read
-+ *
-+ * NOTE: MUST NOT be called from interrupt context,
-+ * because the bus read/write functions may wait for an interrupt
-+ * to conclude the operation.
-+ */
-+int mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum)
-+{
-+ int retval;
-+
-+ mutex_lock(&bus->mdio_lock);
-+ retval = __mdiobus_c45_read(bus, addr, devad, regnum);
-+ mutex_unlock(&bus->mdio_lock);
-+
-+ return retval;
-+}
-+EXPORT_SYMBOL(mdiobus_c45_read);
-+
-+/**
- * mdiobus_write_nested - Nested version of the mdiobus_write function
- * @bus: the mii_bus struct
- * @addr: the phy address
-@@ -928,6 +1045,31 @@ int mdiobus_write(struct mii_bus *bus, i
- EXPORT_SYMBOL(mdiobus_write);
-
- /**
-+ * mdiobus_c45_write - Convenience function for writing a given MII mgmt register
-+ * @bus: the mii_bus struct
-+ * @addr: the phy address
-+ * @devad: device address to read
-+ * @regnum: register number to write
-+ * @val: value to write to @regnum
-+ *
-+ * NOTE: MUST NOT be called from interrupt context,
-+ * because the bus read/write functions may wait for an interrupt
-+ * to conclude the operation.
-+ */
-+int mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum,
-+ u16 val)
-+{
-+ int err;
-+
-+ mutex_lock(&bus->mdio_lock);
-+ err = __mdiobus_c45_write(bus, addr, devad, regnum, val);
-+ mutex_unlock(&bus->mdio_lock);
-+
-+ return err;
-+}
-+EXPORT_SYMBOL(mdiobus_c45_write);
-+
-+/**
- * mdiobus_modify - Convenience function for modifying a given mdio device
- * register
- * @bus: the mii_bus struct
-@@ -949,6 +1091,30 @@ int mdiobus_modify(struct mii_bus *bus,
- EXPORT_SYMBOL_GPL(mdiobus_modify);
-
- /**
-+ * mdiobus_c45_modify - Convenience function for modifying a given mdio device
-+ * register
-+ * @bus: the mii_bus struct
-+ * @addr: the phy address
-+ * @devad: device address to read
-+ * @regnum: register number to write
-+ * @mask: bit mask of bits to clear
-+ * @set: bit mask of bits to set
-+ */
-+int mdiobus_c45_modify(struct mii_bus *bus, int addr, int devad, u32 regnum,
-+ u16 mask, u16 set)
-+{
-+ int err;
-+
-+ mutex_lock(&bus->mdio_lock);
-+ err = __mdiobus_c45_modify_changed(bus, addr, devad, regnum,
-+ mask, set);
-+ mutex_unlock(&bus->mdio_lock);
-+
-+ return err < 0 ? err : 0;
-+}
-+EXPORT_SYMBOL_GPL(mdiobus_c45_modify);
-+
-+/**
- * mdiobus_modify_changed - Convenience function for modifying a given mdio
- * device register and returning if it changed
- * @bus: the mii_bus struct
-@@ -971,6 +1137,29 @@ int mdiobus_modify_changed(struct mii_bu
- EXPORT_SYMBOL_GPL(mdiobus_modify_changed);
-
- /**
-+ * mdiobus_c45_modify_changed - Convenience function for modifying a given mdio
-+ * device register and returning if it changed
-+ * @bus: the mii_bus struct
-+ * @addr: the phy address
-+ * @devad: device address to read
-+ * @regnum: register number to write
-+ * @mask: bit mask of bits to clear
-+ * @set: bit mask of bits to set
-+ */
-+int mdiobus_c45_modify_changed(struct mii_bus *bus, int devad, int addr,
-+ u32 regnum, u16 mask, u16 set)
-+{
-+ int err;
-+
-+ mutex_lock(&bus->mdio_lock);
-+ err = __mdiobus_c45_modify_changed(bus, addr, devad, regnum, mask, set);
-+ mutex_unlock(&bus->mdio_lock);
-+
-+ return err;
-+}
-+EXPORT_SYMBOL_GPL(mdiobus_c45_modify_changed);
-+
-+/**
- * mdio_bus_match - determine if given MDIO driver supports the given
- * MDIO device
- * @dev: target MDIO device
---- a/include/linux/mdio.h
-+++ b/include/linux/mdio.h
-@@ -423,6 +423,17 @@ int mdiobus_modify(struct mii_bus *bus,
- u16 set);
- int mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum,
- u16 mask, u16 set);
-+int __mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum);
-+int mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum);
-+int __mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum,
-+ u16 val);
-+int mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum,
-+ u16 val);
-+int mdiobus_c45_modify(struct mii_bus *bus, int addr, int devad, u32 regnum,
-+ u16 mask, u16 set);
-+
-+int mdiobus_c45_modify_changed(struct mii_bus *bus, int addr, int devad,
-+ u32 regnum, u16 mask, u16 set);
-
- static inline int mdiodev_read(struct mdio_device *mdiodev, u32 regnum)
- {
-@@ -463,29 +474,19 @@ static inline u16 mdiobus_c45_devad(u32
- return FIELD_GET(MII_DEVADDR_C45_MASK, regnum);
- }
-
--static inline int __mdiobus_c45_read(struct mii_bus *bus, int prtad, int devad,
-- u16 regnum)
--{
-- return __mdiobus_read(bus, prtad, mdiobus_c45_addr(devad, regnum));
--}
--
--static inline int __mdiobus_c45_write(struct mii_bus *bus, int prtad, int devad,
-- u16 regnum, u16 val)
--{
-- return __mdiobus_write(bus, prtad, mdiobus_c45_addr(devad, regnum),
-- val);
--}
--
--static inline int mdiobus_c45_read(struct mii_bus *bus, int prtad, int devad,
-- u16 regnum)
-+static inline int mdiodev_c45_modify(struct mdio_device *mdiodev, int devad,
-+ u32 regnum, u16 mask, u16 set)
- {
-- return mdiobus_read(bus, prtad, mdiobus_c45_addr(devad, regnum));
-+ return mdiobus_c45_modify(mdiodev->bus, mdiodev->addr, devad, regnum,
-+ mask, set);
- }
-
--static inline int mdiobus_c45_write(struct mii_bus *bus, int prtad, int devad,
-- u16 regnum, u16 val)
-+static inline int mdiodev_c45_modify_changed(struct mdio_device *mdiodev,
-+ int devad, u32 regnum, u16 mask,
-+ u16 set)
- {
-- return mdiobus_write(bus, prtad, mdiobus_c45_addr(devad, regnum), val);
-+ return mdiobus_c45_modify_changed(mdiodev->bus, mdiodev->addr, devad,
-+ regnum, mask, set);
- }
-
- int mdiobus_register_device(struct mdio_device *mdiodev);
---- a/include/linux/phy.h
-+++ b/include/linux/phy.h
-@@ -364,6 +364,11 @@ struct mii_bus {
- int (*read)(struct mii_bus *bus, int addr, int regnum);
- /** @write: Perform a write transfer on the bus */
- int (*write)(struct mii_bus *bus, int addr, int regnum, u16 val);
-+ /** @read_c45: Perform a C45 read transfer on the bus */
-+ int (*read_c45)(struct mii_bus *bus, int addr, int devnum, int regnum);
-+ /** @write_c45: Perform a C45 write transfer on the bus */
-+ int (*write_c45)(struct mii_bus *bus, int addr, int devnum,
-+ int regnum, u16 val);
- /** @reset: Perform a reset of the bus */
- int (*reset)(struct mii_bus *bus);
-
+++ /dev/null
-From 9a0e95e34e9c0a713ddfd48c3a88a20d2bdfd514 Mon Sep 17 00:00:00 2001
-From: Gabor Juhos <j4g8y7@gmail.com>
-Date: Fri, 11 Aug 2023 13:10:07 +0200
-Subject: [PATCH] net: phy: Introduce PSGMII PHY interface mode
-
-The PSGMII interface is similar to QSGMII. The main difference
-is that the PSGMII interface combines five SGMII lines into a
-single link while in QSGMII only four lines are combined.
-
-Similarly to the QSGMII, this interface mode might also needs
-special handling within the MAC driver.
-
-It is commonly used by Qualcomm with their QCA807x PHY series and
-modern WiSoC-s.
-
-Add definitions for the PHY layer to allow to express this type
-of connection between the MAC and PHY.
-
-Signed-off-by: Gabor Juhos <j4g8y7@gmail.com>
-Signed-off-by: Robert Marko <robert.marko@sartura.hr>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- Documentation/networking/phy.rst | 4 ++++
- drivers/net/phy/phy-core.c | 2 ++
- drivers/net/phy/phylink.c | 3 +++
- include/linux/phy.h | 4 ++++
- 4 files changed, 13 insertions(+)
-
---- a/Documentation/networking/phy.rst
-+++ b/Documentation/networking/phy.rst
-@@ -323,6 +323,10 @@ Some of the interface modes are describe
- contrast with the 1000BASE-X phy mode used for Clause 38 and 39 PMDs, this
- interface mode has different autonegotiation and only supports full duplex.
-
-+``PHY_INTERFACE_MODE_PSGMII``
-+ This is the Penta SGMII mode, it is similar to QSGMII but it combines 5
-+ SGMII lines into a single link compared to 4 on QSGMII.
-+
- Pause frames / flow control
- ===========================
-
---- a/drivers/net/phy/phy-core.c
-+++ b/drivers/net/phy/phy-core.c
-@@ -140,6 +140,8 @@ int phy_interface_num_ports(phy_interfac
- case PHY_INTERFACE_MODE_QSGMII:
- case PHY_INTERFACE_MODE_QUSGMII:
- return 4;
-+ case PHY_INTERFACE_MODE_PSGMII:
-+ return 5;
- case PHY_INTERFACE_MODE_MAX:
- WARN_ONCE(1, "PHY_INTERFACE_MODE_MAX isn't a valid interface mode");
- return 0;
---- a/drivers/net/phy/phylink.c
-+++ b/drivers/net/phy/phylink.c
-@@ -187,6 +187,7 @@ static int phylink_interface_max_speed(p
- case PHY_INTERFACE_MODE_RGMII_RXID:
- case PHY_INTERFACE_MODE_RGMII_ID:
- case PHY_INTERFACE_MODE_RGMII:
-+ case PHY_INTERFACE_MODE_PSGMII:
- case PHY_INTERFACE_MODE_QSGMII:
- case PHY_INTERFACE_MODE_QUSGMII:
- case PHY_INTERFACE_MODE_SGMII:
-@@ -448,6 +449,7 @@ unsigned long phylink_get_capabilities(p
- case PHY_INTERFACE_MODE_RGMII_RXID:
- case PHY_INTERFACE_MODE_RGMII_ID:
- case PHY_INTERFACE_MODE_RGMII:
-+ case PHY_INTERFACE_MODE_PSGMII:
- case PHY_INTERFACE_MODE_QSGMII:
- case PHY_INTERFACE_MODE_QUSGMII:
- case PHY_INTERFACE_MODE_SGMII:
-@@ -814,6 +816,7 @@ static int phylink_parse_mode(struct phy
-
- switch (pl->link_config.interface) {
- case PHY_INTERFACE_MODE_SGMII:
-+ case PHY_INTERFACE_MODE_PSGMII:
- case PHY_INTERFACE_MODE_QSGMII:
- case PHY_INTERFACE_MODE_QUSGMII:
- case PHY_INTERFACE_MODE_RGMII:
---- a/include/linux/phy.h
-+++ b/include/linux/phy.h
-@@ -103,6 +103,7 @@ extern const int phy_10gbit_features_arr
- * @PHY_INTERFACE_MODE_XGMII: 10 gigabit media-independent interface
- * @PHY_INTERFACE_MODE_XLGMII:40 gigabit media-independent interface
- * @PHY_INTERFACE_MODE_MOCA: Multimedia over Coax
-+ * @PHY_INTERFACE_MODE_PSGMII: Penta SGMII
- * @PHY_INTERFACE_MODE_QSGMII: Quad SGMII
- * @PHY_INTERFACE_MODE_TRGMII: Turbo RGMII
- * @PHY_INTERFACE_MODE_100BASEX: 100 BaseX
-@@ -140,6 +141,7 @@ typedef enum {
- PHY_INTERFACE_MODE_XGMII,
- PHY_INTERFACE_MODE_XLGMII,
- PHY_INTERFACE_MODE_MOCA,
-+ PHY_INTERFACE_MODE_PSGMII,
- PHY_INTERFACE_MODE_QSGMII,
- PHY_INTERFACE_MODE_TRGMII,
- PHY_INTERFACE_MODE_100BASEX,
-@@ -247,6 +249,8 @@ static inline const char *phy_modes(phy_
- return "xlgmii";
- case PHY_INTERFACE_MODE_MOCA:
- return "moca";
-+ case PHY_INTERFACE_MODE_PSGMII:
-+ return "psgmii";
- case PHY_INTERFACE_MODE_QSGMII:
- return "qsgmii";
- case PHY_INTERFACE_MODE_TRGMII:
+++ /dev/null
-From a593a2fcfdfb92cfd0ffc54bc81b07e6bfaaaf46 Mon Sep 17 00:00:00 2001
-From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
-Date: Thu, 16 Mar 2023 14:08:26 +0200
-Subject: [PATCH] net: phy: at803x: Replace of_gpio.h with what indeed is used
-
-of_gpio.h in this driver is solely used as a proxy to other headers.
-This is incorrect usage of the of_gpio.h. Replace it .h with what
-indeed is used in the code.
-
-Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/at803x.c | 3 +--
- 1 file changed, 1 insertion(+), 2 deletions(-)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -13,12 +13,11 @@
- #include <linux/netdevice.h>
- #include <linux/etherdevice.h>
- #include <linux/ethtool_netlink.h>
--#include <linux/of_gpio.h>
- #include <linux/bitfield.h>
--#include <linux/gpio/consumer.h>
- #include <linux/regulator/of_regulator.h>
- #include <linux/regulator/driver.h>
- #include <linux/regulator/consumer.h>
-+#include <linux/of.h>
- #include <linux/phylink.h>
- #include <linux/sfp.h>
- #include <dt-bindings/net/qca-ar803x.h>
+++ /dev/null
-From 8b8bc13d89a7d23d14b0e041c73f037c9db997b1 Mon Sep 17 00:00:00 2001
-From: Luo Jie <quic_luoj@quicinc.com>
-Date: Sun, 16 Jul 2023 16:49:19 +0800
-Subject: [PATCH 1/6] net: phy: at803x: support qca8081
- genphy_c45_pma_read_abilities
-
-qca8081 PHY supports to use genphy_c45_pma_read_abilities for
-getting the PHY features supported except for the autoneg ability
-
-but autoneg ability exists in MDIO_STAT1 instead of MMD7.1, add it
-manually after calling genphy_c45_pma_read_abilities.
-
-Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/at803x.c | 28 ++++++++++++++++++----------
- 1 file changed, 18 insertions(+), 10 deletions(-)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -902,15 +902,6 @@ static int at803x_get_features(struct ph
- if (err)
- return err;
-
-- if (phydev->drv->phy_id == QCA8081_PHY_ID) {
-- err = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_NG_EXTABLE);
-- if (err < 0)
-- return err;
--
-- linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported,
-- err & MDIO_PMA_NG_EXTABLE_2_5GBT);
-- }
--
- if (phydev->drv->phy_id != ATH8031_PHY_ID)
- return 0;
-
-@@ -1996,6 +1987,23 @@ static int qca808x_cable_test_get_status
- return 0;
- }
-
-+static int qca808x_get_features(struct phy_device *phydev)
-+{
-+ int ret;
-+
-+ ret = genphy_c45_pma_read_abilities(phydev);
-+ if (ret)
-+ return ret;
-+
-+ /* The autoneg ability is not existed in bit3 of MMD7.1,
-+ * but it is supported by qca808x PHY, so we add it here
-+ * manually.
-+ */
-+ linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
-+
-+ return 0;
-+}
-+
- static struct phy_driver at803x_driver[] = {
- {
- /* Qualcomm Atheros AR8035 */
-@@ -2163,7 +2171,7 @@ static struct phy_driver at803x_driver[]
- .set_tunable = at803x_set_tunable,
- .set_wol = at803x_set_wol,
- .get_wol = at803x_get_wol,
-- .get_features = at803x_get_features,
-+ .get_features = qca808x_get_features,
- .config_aneg = at803x_config_aneg,
- .suspend = genphy_suspend,
- .resume = genphy_resume,
+++ /dev/null
-From f3db55ae860a82e1224a909072783ef850e5d228 Mon Sep 17 00:00:00 2001
-From: Luo Jie <quic_luoj@quicinc.com>
-Date: Sun, 16 Jul 2023 16:49:20 +0800
-Subject: [PATCH 2/6] net: phy: at803x: merge qca8081 slave seed function
-
-merge the seed enablement and seed value configuration into
-one function, since the random seed value is needed to be
-configured when the seed is enabled.
-
-Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/at803x.c | 29 +++++++++--------------------
- 1 file changed, 9 insertions(+), 20 deletions(-)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -1730,24 +1730,19 @@ static int qca808x_phy_fast_retrain_conf
- return 0;
- }
-
--static int qca808x_phy_ms_random_seed_set(struct phy_device *phydev)
--{
-- u16 seed_value = prandom_u32_max(QCA808X_MASTER_SLAVE_SEED_RANGE);
--
-- return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
-- QCA808X_MASTER_SLAVE_SEED_CFG,
-- FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value));
--}
--
- static int qca808x_phy_ms_seed_enable(struct phy_device *phydev, bool enable)
- {
-- u16 seed_enable = 0;
-+ u16 seed_value;
-
-- if (enable)
-- seed_enable = QCA808X_MASTER_SLAVE_SEED_ENABLE;
-+ if (!enable)
-+ return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
-+ QCA808X_MASTER_SLAVE_SEED_ENABLE, 0);
-
-+ seed_value = prandom_u32_max(QCA808X_MASTER_SLAVE_SEED_RANGE);
- return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
-- QCA808X_MASTER_SLAVE_SEED_ENABLE, seed_enable);
-+ QCA808X_MASTER_SLAVE_SEED_CFG | QCA808X_MASTER_SLAVE_SEED_ENABLE,
-+ FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value) |
-+ QCA808X_MASTER_SLAVE_SEED_ENABLE);
- }
-
- static int qca808x_config_init(struct phy_device *phydev)
-@@ -1771,12 +1766,7 @@ static int qca808x_config_init(struct ph
- if (ret)
- return ret;
-
-- /* Configure lower ramdom seed to make phy linked as slave mode */
-- ret = qca808x_phy_ms_random_seed_set(phydev);
-- if (ret)
-- return ret;
--
-- /* Enable seed */
-+ /* Enable seed and configure lower ramdom seed to make phy linked as slave mode */
- ret = qca808x_phy_ms_seed_enable(phydev, true);
- if (ret)
- return ret;
-@@ -1821,7 +1811,6 @@ static int qca808x_read_status(struct ph
- if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR) {
- qca808x_phy_ms_seed_enable(phydev, false);
- } else {
-- qca808x_phy_ms_random_seed_set(phydev);
- qca808x_phy_ms_seed_enable(phydev, true);
- }
- }
+++ /dev/null
-From 7cc3209558002d95c0d45a1276ba4f5f741eec42 Mon Sep 17 00:00:00 2001
-From: Luo Jie <quic_luoj@quicinc.com>
-Date: Sun, 16 Jul 2023 16:49:21 +0800
-Subject: [PATCH 3/6] net: phy: at803x: enable qca8081 slave seed conditionally
-
-qca8081 is the single port PHY, the slave prefer mode is used
-by default.
-
-if the phy master perfer mode is configured, the slave seed
-configuration should not be enabled, since the slave seed
-enablement is for making PHY linked as slave mode easily.
-
-disable slave seed if the master mode is preferred.
-
-Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/at803x.c | 25 ++++++++++++++++++++-----
- 1 file changed, 20 insertions(+), 5 deletions(-)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -1745,6 +1745,12 @@ static int qca808x_phy_ms_seed_enable(st
- QCA808X_MASTER_SLAVE_SEED_ENABLE);
- }
-
-+static bool qca808x_is_prefer_master(struct phy_device *phydev)
-+{
-+ return (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_FORCE) ||
-+ (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_PREFERRED);
-+}
-+
- static int qca808x_config_init(struct phy_device *phydev)
- {
- int ret;
-@@ -1766,11 +1772,17 @@ static int qca808x_config_init(struct ph
- if (ret)
- return ret;
-
-- /* Enable seed and configure lower ramdom seed to make phy linked as slave mode */
-- ret = qca808x_phy_ms_seed_enable(phydev, true);
-- if (ret)
-+ ret = genphy_read_master_slave(phydev);
-+ if (ret < 0)
- return ret;
-
-+ if (!qca808x_is_prefer_master(phydev)) {
-+ /* Enable seed and configure lower ramdom seed to make phy linked as slave mode */
-+ ret = qca808x_phy_ms_seed_enable(phydev, true);
-+ if (ret)
-+ return ret;
-+ }
-+
- /* Configure adc threshold as 100mv for the link 10M */
- return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD,
- QCA808X_ADC_THRESHOLD_MASK, QCA808X_ADC_THRESHOLD_100MV);
-@@ -1802,13 +1814,16 @@ static int qca808x_read_status(struct ph
- phydev->interface = PHY_INTERFACE_MODE_SGMII;
- } else {
- /* generate seed as a lower random value to make PHY linked as SLAVE easily,
-- * except for master/slave configuration fault detected.
-+ * except for master/slave configuration fault detected or the master mode
-+ * preferred.
-+ *
- * the reason for not putting this code into the function link_change_notify is
- * the corner case where the link partner is also the qca8081 PHY and the seed
- * value is configured as the same value, the link can't be up and no link change
- * occurs.
- */
-- if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR) {
-+ if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||
-+ qca808x_is_prefer_master(phydev)) {
- qca808x_phy_ms_seed_enable(phydev, false);
- } else {
- qca808x_phy_ms_seed_enable(phydev, true);
+++ /dev/null
-From fea7cfb83d1a2782e39cd101dd44ed2548539de5 Mon Sep 17 00:00:00 2001
-From: Luo Jie <quic_luoj@quicinc.com>
-Date: Sun, 16 Jul 2023 16:49:22 +0800
-Subject: [PATCH 4/6] net: phy: at803x: support qca8081 1G chip type
-
-The qca8081 1G chip version does not support 2.5 capability, which
-is distinguished from qca8081 2.5G chip according to the bit0 of
-register mmd7.0x901d, the 1G version chip also has the same PHY ID
-as the normal qca8081 2.5G chip.
-
-Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/at803x.c | 15 +++++++++++++++
- 1 file changed, 15 insertions(+)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -272,6 +272,10 @@
- #define QCA808X_CDT_STATUS_STAT_OPEN 2
- #define QCA808X_CDT_STATUS_STAT_SHORT 3
-
-+/* QCA808X 1G chip type */
-+#define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d
-+#define QCA808X_PHY_CHIP_TYPE_1G BIT(0)
-+
- MODULE_DESCRIPTION("Qualcomm Atheros AR803x and QCA808X PHY driver");
- MODULE_AUTHOR("Matus Ujhelyi");
- MODULE_LICENSE("GPL");
-@@ -2005,6 +2009,17 @@ static int qca808x_get_features(struct p
- */
- linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
-
-+ /* As for the qca8081 1G version chip, the 2500baseT ability is also
-+ * existed in the bit0 of MMD1.21, we need to remove it manually if
-+ * it is the qca8081 1G chip according to the bit0 of MMD7.0x901d.
-+ */
-+ ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE);
-+ if (ret < 0)
-+ return ret;
-+
-+ if (QCA808X_PHY_CHIP_TYPE_1G & ret)
-+ linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
-+
- return 0;
- }
-
+++ /dev/null
-From df9401ff3e6eeaa42bfb06761967f1b71f5afce7 Mon Sep 17 00:00:00 2001
-From: Luo Jie <quic_luoj@quicinc.com>
-Date: Sun, 16 Jul 2023 16:49:23 +0800
-Subject: [PATCH 5/6] net: phy: at803x: remove qca8081 1G fast retrain and
- slave seed config
-
-The fast retrain and slave seed configs are only applicable when the 2.5G
-ability is supported.
-
-Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/at803x.c | 50 +++++++++++++++++++++++++---------------
- 1 file changed, 32 insertions(+), 18 deletions(-)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -1755,6 +1755,11 @@ static bool qca808x_is_prefer_master(str
- (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_PREFERRED);
- }
-
-+static bool qca808x_has_fast_retrain_or_slave_seed(struct phy_device *phydev)
-+{
-+ return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
-+}
-+
- static int qca808x_config_init(struct phy_device *phydev)
- {
- int ret;
-@@ -1771,20 +1776,24 @@ static int qca808x_config_init(struct ph
- if (ret)
- return ret;
-
-- /* Config the fast retrain for the link 2500M */
-- ret = qca808x_phy_fast_retrain_config(phydev);
-- if (ret)
-- return ret;
--
-- ret = genphy_read_master_slave(phydev);
-- if (ret < 0)
-- return ret;
--
-- if (!qca808x_is_prefer_master(phydev)) {
-- /* Enable seed and configure lower ramdom seed to make phy linked as slave mode */
-- ret = qca808x_phy_ms_seed_enable(phydev, true);
-+ if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
-+ /* Config the fast retrain for the link 2500M */
-+ ret = qca808x_phy_fast_retrain_config(phydev);
- if (ret)
- return ret;
-+
-+ ret = genphy_read_master_slave(phydev);
-+ if (ret < 0)
-+ return ret;
-+
-+ if (!qca808x_is_prefer_master(phydev)) {
-+ /* Enable seed and configure lower ramdom seed to make phy
-+ * linked as slave mode.
-+ */
-+ ret = qca808x_phy_ms_seed_enable(phydev, true);
-+ if (ret)
-+ return ret;
-+ }
- }
-
- /* Configure adc threshold as 100mv for the link 10M */
-@@ -1826,11 +1835,13 @@ static int qca808x_read_status(struct ph
- * value is configured as the same value, the link can't be up and no link change
- * occurs.
- */
-- if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||
-- qca808x_is_prefer_master(phydev)) {
-- qca808x_phy_ms_seed_enable(phydev, false);
-- } else {
-- qca808x_phy_ms_seed_enable(phydev, true);
-+ if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
-+ if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||
-+ qca808x_is_prefer_master(phydev)) {
-+ qca808x_phy_ms_seed_enable(phydev, false);
-+ } else {
-+ qca808x_phy_ms_seed_enable(phydev, true);
-+ }
- }
- }
-
-@@ -1845,7 +1856,10 @@ static int qca808x_soft_reset(struct phy
- if (ret < 0)
- return ret;
-
-- return qca808x_phy_ms_seed_enable(phydev, true);
-+ if (qca808x_has_fast_retrain_or_slave_seed(phydev))
-+ ret = qca808x_phy_ms_seed_enable(phydev, true);
-+
-+ return ret;
- }
-
- static bool qca808x_cdt_fault_length_valid(int cdt_code)
+++ /dev/null
-From 723970affdd8766fa0d91cd34bf2ffc861538b5f Mon Sep 17 00:00:00 2001
-From: Luo Jie <quic_luoj@quicinc.com>
-Date: Sun, 16 Jul 2023 16:49:24 +0800
-Subject: [PATCH 6/6] net: phy: at803x: add qca8081 fifo reset on the link
- changed
-
-The qca8081 sgmii fifo needs to be reset on link down and
-released on the link up in case of any abnormal issue
-such as the packet blocked on the PHY.
-
-Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/at803x.c | 14 ++++++++++++++
- 1 file changed, 14 insertions(+)
-
---- a/drivers/net/phy/at803x.c
-+++ b/drivers/net/phy/at803x.c
-@@ -276,6 +276,9 @@
- #define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d
- #define QCA808X_PHY_CHIP_TYPE_1G BIT(0)
-
-+#define QCA8081_PHY_SERDES_MMD1_FIFO_CTRL 0x9072
-+#define QCA8081_PHY_FIFO_RSTN BIT(11)
-+
- MODULE_DESCRIPTION("Qualcomm Atheros AR803x and QCA808X PHY driver");
- MODULE_AUTHOR("Matus Ujhelyi");
- MODULE_LICENSE("GPL");
-@@ -2037,6 +2040,16 @@ static int qca808x_get_features(struct p
- return 0;
- }
-
-+static void qca808x_link_change_notify(struct phy_device *phydev)
-+{
-+ /* Assert interface sgmii fifo on link down, deassert it on link up,
-+ * the interface device address is always phy address added by 1.
-+ */
-+ mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1,
-+ MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL,
-+ QCA8081_PHY_FIFO_RSTN, phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
-+}
-+
- static struct phy_driver at803x_driver[] = {
- {
- /* Qualcomm Atheros AR8035 */
-@@ -2213,6 +2226,7 @@ static struct phy_driver at803x_driver[]
- .soft_reset = qca808x_soft_reset,
- .cable_test_start = qca808x_cable_test_start,
- .cable_test_get_status = qca808x_cable_test_get_status,
-+ .link_change_notify = qca808x_link_change_notify,
- }, };
-
- module_phy_driver(at803x_driver);
+++ /dev/null
-From 4765a9722e09765866e131ec31f7b9cf4c1f4854 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Sun, 19 Mar 2023 12:57:50 +0000
-Subject: [PATCH] net: pcs: add driver for MediaTek SGMII PCS
-
-The SGMII core found in several MediaTek SoCs is identical to what can
-also be found in MediaTek's MT7531 Ethernet switch IC.
-As this has not always been clear, both drivers developed different
-implementations to deal with the PCS.
-Recently Alexander Couzens pointed out this fact which lead to the
-development of this shared driver.
-
-Add a dedicated driver, mostly by copying the code now found in the
-Ethernet driver. The now redundant code will be removed by a follow-up
-commit.
-
-Suggested-by: Alexander Couzens <lynxis@fe80.eu>
-Suggested-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Tested-by: Frank Wunderlich <frank-w@public-files.de>
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- MAINTAINERS | 8 +
- drivers/net/pcs/Kconfig | 7 +
- drivers/net/pcs/Makefile | 1 +
- drivers/net/pcs/pcs-mtk-lynxi.c | 305 ++++++++++++++++++++++++++++++
- include/linux/pcs/pcs-mtk-lynxi.h | 13 ++
- 5 files changed, 334 insertions(+)
- create mode 100644 drivers/net/pcs/pcs-mtk-lynxi.c
- create mode 100644 include/linux/pcs/pcs-mtk-lynxi.h
-
---- a/MAINTAINERS
-+++ b/MAINTAINERS
-@@ -12928,6 +12928,14 @@ L: netdev@vger.kernel.org
- S: Maintained
- F: drivers/net/ethernet/mediatek/
-
-+MEDIATEK ETHERNET PCS DRIVER
-+M: Alexander Couzens <lynxis@fe80.eu>
-+M: Daniel Golle <daniel@makrotopia.org>
-+L: netdev@vger.kernel.org
-+S: Maintained
-+F: drivers/net/pcs/pcs-mtk-lynxi.c
-+F: include/linux/pcs/pcs-mtk-lynxi.h
-+
- MEDIATEK I2C CONTROLLER DRIVER
- M: Qii Wang <qii.wang@mediatek.com>
- L: linux-i2c@vger.kernel.org
---- a/drivers/net/pcs/Kconfig
-+++ b/drivers/net/pcs/Kconfig
-@@ -32,4 +32,11 @@ config PCS_ALTERA_TSE
- This module provides helper functions for the Altera Triple Speed
- Ethernet SGMII PCS, that can be found on the Intel Socfpga family.
-
-+config PCS_MTK_LYNXI
-+ tristate
-+ select REGMAP
-+ help
-+ This module provides helpers to phylink for managing the LynxI PCS
-+ which is part of MediaTek's SoC and Ethernet switch ICs.
-+
- endmenu
---- a/drivers/net/pcs/Makefile
-+++ b/drivers/net/pcs/Makefile
-@@ -7,3 +7,4 @@ obj-$(CONFIG_PCS_XPCS) += pcs_xpcs.o
- obj-$(CONFIG_PCS_LYNX) += pcs-lynx.o
- obj-$(CONFIG_PCS_RZN1_MIIC) += pcs-rzn1-miic.o
- obj-$(CONFIG_PCS_ALTERA_TSE) += pcs-altera-tse.o
-+obj-$(CONFIG_PCS_MTK_LYNXI) += pcs-mtk-lynxi.o
---- /dev/null
-+++ b/drivers/net/pcs/pcs-mtk-lynxi.c
-@@ -0,0 +1,305 @@
-+// SPDX-License-Identifier: GPL-2.0
-+// Copyright (c) 2018-2019 MediaTek Inc.
-+/* A library for MediaTek SGMII circuit
-+ *
-+ * Author: Sean Wang <sean.wang@mediatek.com>
-+ * Author: Alexander Couzens <lynxis@fe80.eu>
-+ * Author: Daniel Golle <daniel@makrotopia.org>
-+ *
-+ */
-+
-+#include <linux/mdio.h>
-+#include <linux/of.h>
-+#include <linux/pcs/pcs-mtk-lynxi.h>
-+#include <linux/phylink.h>
-+#include <linux/regmap.h>
-+
-+/* SGMII subsystem config registers */
-+/* BMCR (low 16) BMSR (high 16) */
-+#define SGMSYS_PCS_CONTROL_1 0x0
-+#define SGMII_BMCR GENMASK(15, 0)
-+#define SGMII_BMSR GENMASK(31, 16)
-+
-+#define SGMSYS_PCS_DEVICE_ID 0x4
-+#define SGMII_LYNXI_DEV_ID 0x4d544950
-+
-+#define SGMSYS_PCS_ADVERTISE 0x8
-+#define SGMII_ADVERTISE GENMASK(15, 0)
-+#define SGMII_LPA GENMASK(31, 16)
-+
-+#define SGMSYS_PCS_SCRATCH 0x14
-+#define SGMII_DEV_VERSION GENMASK(31, 16)
-+
-+/* Register to programmable link timer, the unit in 2 * 8ns */
-+#define SGMSYS_PCS_LINK_TIMER 0x18
-+#define SGMII_LINK_TIMER_MASK GENMASK(19, 0)
-+#define SGMII_LINK_TIMER_VAL(ns) FIELD_PREP(SGMII_LINK_TIMER_MASK, \
-+ ((ns) / 2 / 8))
-+
-+/* Register to control remote fault */
-+#define SGMSYS_SGMII_MODE 0x20
-+#define SGMII_IF_MODE_SGMII BIT(0)
-+#define SGMII_SPEED_DUPLEX_AN BIT(1)
-+#define SGMII_SPEED_MASK GENMASK(3, 2)
-+#define SGMII_SPEED_10 FIELD_PREP(SGMII_SPEED_MASK, 0)
-+#define SGMII_SPEED_100 FIELD_PREP(SGMII_SPEED_MASK, 1)
-+#define SGMII_SPEED_1000 FIELD_PREP(SGMII_SPEED_MASK, 2)
-+#define SGMII_DUPLEX_HALF BIT(4)
-+#define SGMII_REMOTE_FAULT_DIS BIT(8)
-+
-+/* Register to reset SGMII design */
-+#define SGMSYS_RESERVED_0 0x34
-+#define SGMII_SW_RESET BIT(0)
-+
-+/* Register to set SGMII speed, ANA RG_ Control Signals III */
-+#define SGMII_PHY_SPEED_MASK GENMASK(3, 2)
-+#define SGMII_PHY_SPEED_1_25G FIELD_PREP(SGMII_PHY_SPEED_MASK, 0)
-+#define SGMII_PHY_SPEED_3_125G FIELD_PREP(SGMII_PHY_SPEED_MASK, 1)
-+
-+/* Register to power up QPHY */
-+#define SGMSYS_QPHY_PWR_STATE_CTRL 0xe8
-+#define SGMII_PHYA_PWD BIT(4)
-+
-+/* Register to QPHY wrapper control */
-+#define SGMSYS_QPHY_WRAP_CTRL 0xec
-+#define SGMII_PN_SWAP_MASK GENMASK(1, 0)
-+#define SGMII_PN_SWAP_TX_RX (BIT(0) | BIT(1))
-+
-+/* struct mtk_pcs_lynxi - This structure holds each sgmii regmap andassociated
-+ * data
-+ * @regmap: The register map pointing at the range used to setup
-+ * SGMII modes
-+ * @dev: Pointer to device owning the PCS
-+ * @ana_rgc3: The offset of register ANA_RGC3 relative to regmap
-+ * @interface: Currently configured interface mode
-+ * @pcs: Phylink PCS structure
-+ * @flags: Flags indicating hardware properties
-+ */
-+struct mtk_pcs_lynxi {
-+ struct regmap *regmap;
-+ u32 ana_rgc3;
-+ phy_interface_t interface;
-+ struct phylink_pcs pcs;
-+ u32 flags;
-+};
-+
-+static struct mtk_pcs_lynxi *pcs_to_mtk_pcs_lynxi(struct phylink_pcs *pcs)
-+{
-+ return container_of(pcs, struct mtk_pcs_lynxi, pcs);
-+}
-+
-+static void mtk_pcs_lynxi_get_state(struct phylink_pcs *pcs,
-+ struct phylink_link_state *state)
-+{
-+ struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
-+ unsigned int bm, adv;
-+
-+ /* Read the BMSR and LPA */
-+ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm);
-+ regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv);
-+
-+ phylink_mii_c22_pcs_decode_state(state, FIELD_GET(SGMII_BMSR, bm),
-+ FIELD_GET(SGMII_LPA, adv));
-+}
-+
-+static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int mode,
-+ phy_interface_t interface,
-+ const unsigned long *advertising,
-+ bool permit_pause_to_mac)
-+{
-+ struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
-+ bool mode_changed = false, changed, use_an;
-+ unsigned int rgc3, sgm_mode, bmcr;
-+ int advertise, link_timer;
-+
-+ advertise = phylink_mii_c22_pcs_encode_advertisement(interface,
-+ advertising);
-+ if (advertise < 0)
-+ return advertise;
-+
-+ /* Clearing IF_MODE_BIT0 switches the PCS to BASE-X mode, and
-+ * we assume that fixes it's speed at bitrate = line rate (in
-+ * other words, 1000Mbps or 2500Mbps).
-+ */
-+ if (interface == PHY_INTERFACE_MODE_SGMII) {
-+ sgm_mode = SGMII_IF_MODE_SGMII;
-+ if (phylink_autoneg_inband(mode)) {
-+ sgm_mode |= SGMII_REMOTE_FAULT_DIS |
-+ SGMII_SPEED_DUPLEX_AN;
-+ use_an = true;
-+ } else {
-+ use_an = false;
-+ }
-+ } else if (phylink_autoneg_inband(mode)) {
-+ /* 1000base-X or 2500base-X autoneg */
-+ sgm_mode = SGMII_REMOTE_FAULT_DIS;
-+ use_an = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
-+ advertising);
-+ } else {
-+ /* 1000base-X or 2500base-X without autoneg */
-+ sgm_mode = 0;
-+ use_an = false;
-+ }
-+
-+ if (use_an)
-+ bmcr = BMCR_ANENABLE;
-+ else
-+ bmcr = 0;
-+
-+ if (mpcs->interface != interface) {
-+ link_timer = phylink_get_link_timer_ns(interface);
-+ if (link_timer < 0)
-+ return link_timer;
-+
-+ /* PHYA power down */
-+ regmap_set_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
-+ SGMII_PHYA_PWD);
-+
-+ /* Reset SGMII PCS state */
-+ regmap_set_bits(mpcs->regmap, SGMSYS_RESERVED_0,
-+ SGMII_SW_RESET);
-+
-+ if (mpcs->flags & MTK_SGMII_FLAG_PN_SWAP)
-+ regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_WRAP_CTRL,
-+ SGMII_PN_SWAP_MASK,
-+ SGMII_PN_SWAP_TX_RX);
-+
-+ if (interface == PHY_INTERFACE_MODE_2500BASEX)
-+ rgc3 = SGMII_PHY_SPEED_3_125G;
-+ else
-+ rgc3 = SGMII_PHY_SPEED_1_25G;
-+
-+ /* Configure the underlying interface speed */
-+ regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
-+ SGMII_PHY_SPEED_MASK, rgc3);
-+
-+ /* Setup the link timer */
-+ regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
-+ SGMII_LINK_TIMER_VAL(link_timer));
-+
-+ mpcs->interface = interface;
-+ mode_changed = true;
-+ }
-+
-+ /* Update the advertisement, noting whether it has changed */
-+ regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE,
-+ SGMII_ADVERTISE, advertise, &changed);
-+
-+ /* Update the sgmsys mode register */
-+ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
-+ SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN |
-+ SGMII_IF_MODE_SGMII, sgm_mode);
-+
-+ /* Update the BMCR */
-+ regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
-+ BMCR_ANENABLE, bmcr);
-+
-+ /* Release PHYA power down state
-+ * Only removing bit SGMII_PHYA_PWD isn't enough.
-+ * There are cases when the SGMII_PHYA_PWD register contains 0x9 which
-+ * prevents SGMII from working. The SGMII still shows link but no traffic
-+ * can flow. Writing 0x0 to the PHYA_PWD register fix the issue. 0x0 was
-+ * taken from a good working state of the SGMII interface.
-+ * Unknown how much the QPHY needs but it is racy without a sleep.
-+ * Tested on mt7622 & mt7986.
-+ */
-+ usleep_range(50, 100);
-+ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
-+
-+ return changed || mode_changed;
-+}
-+
-+static void mtk_pcs_lynxi_restart_an(struct phylink_pcs *pcs)
-+{
-+ struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
-+
-+ regmap_set_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, BMCR_ANRESTART);
-+}
-+
-+static void mtk_pcs_lynxi_link_up(struct phylink_pcs *pcs, unsigned int mode,
-+ phy_interface_t interface, int speed,
-+ int duplex)
-+{
-+ struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
-+ unsigned int sgm_mode;
-+
-+ if (!phylink_autoneg_inband(mode)) {
-+ /* Force the speed and duplex setting */
-+ if (speed == SPEED_10)
-+ sgm_mode = SGMII_SPEED_10;
-+ else if (speed == SPEED_100)
-+ sgm_mode = SGMII_SPEED_100;
-+ else
-+ sgm_mode = SGMII_SPEED_1000;
-+
-+ if (duplex != DUPLEX_FULL)
-+ sgm_mode |= SGMII_DUPLEX_HALF;
-+
-+ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
-+ SGMII_DUPLEX_HALF | SGMII_SPEED_MASK,
-+ sgm_mode);
-+ }
-+}
-+
-+static const struct phylink_pcs_ops mtk_pcs_lynxi_ops = {
-+ .pcs_get_state = mtk_pcs_lynxi_get_state,
-+ .pcs_config = mtk_pcs_lynxi_config,
-+ .pcs_an_restart = mtk_pcs_lynxi_restart_an,
-+ .pcs_link_up = mtk_pcs_lynxi_link_up,
-+};
-+
-+struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev,
-+ struct regmap *regmap, u32 ana_rgc3,
-+ u32 flags)
-+{
-+ struct mtk_pcs_lynxi *mpcs;
-+ u32 id, ver;
-+ int ret;
-+
-+ ret = regmap_read(regmap, SGMSYS_PCS_DEVICE_ID, &id);
-+ if (ret < 0)
-+ return NULL;
-+
-+ if (id != SGMII_LYNXI_DEV_ID) {
-+ dev_err(dev, "unknown PCS device id %08x\n", id);
-+ return NULL;
-+ }
-+
-+ ret = regmap_read(regmap, SGMSYS_PCS_SCRATCH, &ver);
-+ if (ret < 0)
-+ return NULL;
-+
-+ ver = FIELD_GET(SGMII_DEV_VERSION, ver);
-+ if (ver != 0x1) {
-+ dev_err(dev, "unknown PCS device version %04x\n", ver);
-+ return NULL;
-+ }
-+
-+ dev_dbg(dev, "MediaTek LynxI SGMII PCS (id 0x%08x, ver 0x%04x)\n", id,
-+ ver);
-+
-+ mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL);
-+ if (!mpcs)
-+ return NULL;
-+
-+ mpcs->ana_rgc3 = ana_rgc3;
-+ mpcs->regmap = regmap;
-+ mpcs->flags = flags;
-+ mpcs->pcs.ops = &mtk_pcs_lynxi_ops;
-+ mpcs->pcs.poll = true;
-+ mpcs->interface = PHY_INTERFACE_MODE_NA;
-+
-+ return &mpcs->pcs;
-+}
-+EXPORT_SYMBOL(mtk_pcs_lynxi_create);
-+
-+void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs)
-+{
-+ if (!pcs)
-+ return;
-+
-+ kfree(pcs_to_mtk_pcs_lynxi(pcs));
-+}
-+EXPORT_SYMBOL(mtk_pcs_lynxi_destroy);
-+
-+MODULE_LICENSE("GPL");
---- /dev/null
-+++ b/include/linux/pcs/pcs-mtk-lynxi.h
-@@ -0,0 +1,13 @@
-+/* SPDX-License-Identifier: GPL-2.0 */
-+#ifndef __LINUX_PCS_MTK_LYNXI_H
-+#define __LINUX_PCS_MTK_LYNXI_H
-+
-+#include <linux/phylink.h>
-+#include <linux/regmap.h>
-+
-+#define MTK_SGMII_FLAG_PN_SWAP BIT(0)
-+struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev,
-+ struct regmap *regmap,
-+ u32 ana_rgc3, u32 flags);
-+void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs);
-+#endif
+++ /dev/null
-From affa013f494486079c3c5ad2d00cebc41a3d7445 Mon Sep 17 00:00:00 2001
-From: Sean Anderson <sean.anderson@seco.com>
-Date: Mon, 17 Oct 2022 16:22:36 -0400
-Subject: [PATCH 01/21] net: fman: memac: Add serdes support
-
-This adds support for using a serdes which has to be configured. This is
-primarly in preparation for phylink conversion, which will then change the
-serdes mode dynamically.
-
-Signed-off-by: Sean Anderson <sean.anderson@seco.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- .../net/ethernet/freescale/fman/fman_memac.c | 49 ++++++++++++++++++-
- 1 file changed, 47 insertions(+), 2 deletions(-)
-
---- a/drivers/net/ethernet/freescale/fman/fman_memac.c
-+++ b/drivers/net/ethernet/freescale/fman/fman_memac.c
-@@ -13,6 +13,7 @@
- #include <linux/io.h>
- #include <linux/phy.h>
- #include <linux/phy_fixed.h>
-+#include <linux/phy/phy.h>
- #include <linux/of_mdio.h>
-
- /* PCS registers */
-@@ -324,6 +325,7 @@ struct fman_mac {
- void *fm;
- struct fman_rev_info fm_rev_info;
- bool basex_if;
-+ struct phy *serdes;
- struct phy_device *pcsphy;
- bool allmulti_enabled;
- };
-@@ -1203,17 +1205,56 @@ int memac_initialization(struct mac_devi
- }
- }
-
-+ memac->serdes = devm_of_phy_get(mac_dev->dev, mac_node, "serdes");
-+ err = PTR_ERR(memac->serdes);
-+ if (err == -ENODEV || err == -ENOSYS) {
-+ dev_dbg(mac_dev->dev, "could not get (optional) serdes\n");
-+ memac->serdes = NULL;
-+ } else if (IS_ERR(memac->serdes)) {
-+ dev_err_probe(mac_dev->dev, err, "could not get serdes\n");
-+ goto _return_fm_mac_free;
-+ } else {
-+ err = phy_init(memac->serdes);
-+ if (err) {
-+ dev_err_probe(mac_dev->dev, err,
-+ "could not initialize serdes\n");
-+ goto _return_fm_mac_free;
-+ }
-+
-+ err = phy_power_on(memac->serdes);
-+ if (err) {
-+ dev_err_probe(mac_dev->dev, err,
-+ "could not power on serdes\n");
-+ goto _return_phy_exit;
-+ }
-+
-+ if (memac->phy_if == PHY_INTERFACE_MODE_SGMII ||
-+ memac->phy_if == PHY_INTERFACE_MODE_1000BASEX ||
-+ memac->phy_if == PHY_INTERFACE_MODE_2500BASEX ||
-+ memac->phy_if == PHY_INTERFACE_MODE_QSGMII ||
-+ memac->phy_if == PHY_INTERFACE_MODE_XGMII) {
-+ err = phy_set_mode_ext(memac->serdes, PHY_MODE_ETHERNET,
-+ memac->phy_if);
-+ if (err) {
-+ dev_err_probe(mac_dev->dev, err,
-+ "could not set serdes mode to %s\n",
-+ phy_modes(memac->phy_if));
-+ goto _return_phy_power_off;
-+ }
-+ }
-+ }
-+
- if (!mac_dev->phy_node && of_phy_is_fixed_link(mac_node)) {
- struct phy_device *phy;
-
- err = of_phy_register_fixed_link(mac_node);
- if (err)
-- goto _return_fm_mac_free;
-+ goto _return_phy_power_off;
-
- fixed_link = kzalloc(sizeof(*fixed_link), GFP_KERNEL);
- if (!fixed_link) {
- err = -ENOMEM;
-- goto _return_fm_mac_free;
-+ goto _return_phy_power_off;
- }
-
- mac_dev->phy_node = of_node_get(mac_node);
-@@ -1242,6 +1283,10 @@ int memac_initialization(struct mac_devi
-
- goto _return;
-
-+_return_phy_power_off:
-+ phy_power_off(memac->serdes);
-+_return_phy_exit:
-+ phy_exit(memac->serdes);
- _return_fixed_link_free:
- kfree(fixed_link);
- _return_fm_mac_free:
+++ /dev/null
-From fe60e7154d3a35af975c5e6570d6ec31aab9a731 Mon Sep 17 00:00:00 2001
-From: Sean Anderson <sean.anderson@seco.com>
-Date: Mon, 17 Oct 2022 16:22:37 -0400
-Subject: [PATCH 02/21] net: fman: memac: Use lynx pcs driver
-
-Although not stated in the datasheet, as far as I can tell PCS for mEMACs
-is a "Lynx." By reusing the existing driver, we can remove the PCS
-management code from the memac driver. This requires calling some PCS
-functions manually which phylink would usually do for us, but we will let
-it do that soon.
-
-One problem is that we don't actually have a PCS for QSGMII. We pretend
-that each mEMAC's MDIO bus has four QSGMII PCSs, but this is not the case.
-Only the "base" mEMAC's MDIO bus has the four QSGMII PCSs. This is not an
-issue yet, because we never get the PCS state. However, it will be once the
-conversion to phylink is complete, since the links will appear to never
-come up. To get around this, we allow specifying multiple PCSs in pcsphy.
-This breaks backwards compatibility with old device trees, but only for
-QSGMII. IMO this is the only reasonable way to figure out what the actual
-QSGMII PCS is.
-
-Additionally, we now also support a separate XFI PCS. This can allow the
-SerDes driver to set different addresses for the SGMII and XFI PCSs so they
-can be accessed at the same time.
-
-Signed-off-by: Sean Anderson <sean.anderson@seco.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/ethernet/freescale/fman/Kconfig | 3 +
- .../net/ethernet/freescale/fman/fman_memac.c | 258 +++++++-----------
- 2 files changed, 105 insertions(+), 156 deletions(-)
-
---- a/drivers/net/ethernet/freescale/fman/Kconfig
-+++ b/drivers/net/ethernet/freescale/fman/Kconfig
-@@ -4,6 +4,9 @@ config FSL_FMAN
- depends on FSL_SOC || ARCH_LAYERSCAPE || COMPILE_TEST
- select GENERIC_ALLOCATOR
- select PHYLIB
-+ select PHYLINK
-+ select PCS
-+ select PCS_LYNX
- select CRC32
- default n
- help
---- a/drivers/net/ethernet/freescale/fman/fman_memac.c
-+++ b/drivers/net/ethernet/freescale/fman/fman_memac.c
-@@ -11,43 +11,12 @@
-
- #include <linux/slab.h>
- #include <linux/io.h>
-+#include <linux/pcs-lynx.h>
- #include <linux/phy.h>
- #include <linux/phy_fixed.h>
- #include <linux/phy/phy.h>
- #include <linux/of_mdio.h>
-
--/* PCS registers */
--#define MDIO_SGMII_CR 0x00
--#define MDIO_SGMII_DEV_ABIL_SGMII 0x04
--#define MDIO_SGMII_LINK_TMR_L 0x12
--#define MDIO_SGMII_LINK_TMR_H 0x13
--#define MDIO_SGMII_IF_MODE 0x14
--
--/* SGMII Control defines */
--#define SGMII_CR_AN_EN 0x1000
--#define SGMII_CR_RESTART_AN 0x0200
--#define SGMII_CR_FD 0x0100
--#define SGMII_CR_SPEED_SEL1_1G 0x0040
--#define SGMII_CR_DEF_VAL (SGMII_CR_AN_EN | SGMII_CR_FD | \
-- SGMII_CR_SPEED_SEL1_1G)
--
--/* SGMII Device Ability for SGMII defines */
--#define MDIO_SGMII_DEV_ABIL_SGMII_MODE 0x4001
--#define MDIO_SGMII_DEV_ABIL_BASEX_MODE 0x01A0
--
--/* Link timer define */
--#define LINK_TMR_L 0xa120
--#define LINK_TMR_H 0x0007
--#define LINK_TMR_L_BASEX 0xaf08
--#define LINK_TMR_H_BASEX 0x002f
--
--/* SGMII IF Mode defines */
--#define IF_MODE_USE_SGMII_AN 0x0002
--#define IF_MODE_SGMII_EN 0x0001
--#define IF_MODE_SGMII_SPEED_100M 0x0004
--#define IF_MODE_SGMII_SPEED_1G 0x0008
--#define IF_MODE_SGMII_DUPLEX_HALF 0x0010
--
- /* Num of additional exact match MAC adr regs */
- #define MEMAC_NUM_OF_PADDRS 7
-
-@@ -326,7 +295,9 @@ struct fman_mac {
- struct fman_rev_info fm_rev_info;
- bool basex_if;
- struct phy *serdes;
-- struct phy_device *pcsphy;
-+ struct phylink_pcs *sgmii_pcs;
-+ struct phylink_pcs *qsgmii_pcs;
-+ struct phylink_pcs *xfi_pcs;
- bool allmulti_enabled;
- };
-
-@@ -487,91 +458,22 @@ static u32 get_mac_addr_hash_code(u64 et
- return xor_val;
- }
-
--static void setup_sgmii_internal_phy(struct fman_mac *memac,
-- struct fixed_phy_status *fixed_link)
--{
-- u16 tmp_reg16;
--
-- if (WARN_ON(!memac->pcsphy))
-- return;
--
-- /* SGMII mode */
-- tmp_reg16 = IF_MODE_SGMII_EN;
-- if (!fixed_link)
-- /* AN enable */
-- tmp_reg16 |= IF_MODE_USE_SGMII_AN;
-- else {
-- switch (fixed_link->speed) {
-- case 10:
-- /* For 10M: IF_MODE[SPEED_10M] = 0 */
-- break;
-- case 100:
-- tmp_reg16 |= IF_MODE_SGMII_SPEED_100M;
-- break;
-- case 1000:
-- default:
-- tmp_reg16 |= IF_MODE_SGMII_SPEED_1G;
-- break;
-- }
-- if (!fixed_link->duplex)
-- tmp_reg16 |= IF_MODE_SGMII_DUPLEX_HALF;
-- }
-- phy_write(memac->pcsphy, MDIO_SGMII_IF_MODE, tmp_reg16);
--
-- /* Device ability according to SGMII specification */
-- tmp_reg16 = MDIO_SGMII_DEV_ABIL_SGMII_MODE;
-- phy_write(memac->pcsphy, MDIO_SGMII_DEV_ABIL_SGMII, tmp_reg16);
--
-- /* Adjust link timer for SGMII -
-- * According to Cisco SGMII specification the timer should be 1.6 ms.
-- * The link_timer register is configured in units of the clock.
-- * - When running as 1G SGMII, Serdes clock is 125 MHz, so
-- * unit = 1 / (125*10^6 Hz) = 8 ns.
-- * 1.6 ms in units of 8 ns = 1.6ms / 8ns = 2*10^5 = 0x30d40
-- * - When running as 2.5G SGMII, Serdes clock is 312.5 MHz, so
-- * unit = 1 / (312.5*10^6 Hz) = 3.2 ns.
-- * 1.6 ms in units of 3.2 ns = 1.6ms / 3.2ns = 5*10^5 = 0x7a120.
-- * Since link_timer value of 1G SGMII will be too short for 2.5 SGMII,
-- * we always set up here a value of 2.5 SGMII.
-- */
-- phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_H, LINK_TMR_H);
-- phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_L, LINK_TMR_L);
--
-- if (!fixed_link)
-- /* Restart AN */
-- tmp_reg16 = SGMII_CR_DEF_VAL | SGMII_CR_RESTART_AN;
-+static void setup_sgmii_internal(struct fman_mac *memac,
-+ struct phylink_pcs *pcs,
-+ struct fixed_phy_status *fixed_link)
-+{
-+ __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
-+ phy_interface_t iface = memac->basex_if ? PHY_INTERFACE_MODE_1000BASEX :
-+ PHY_INTERFACE_MODE_SGMII;
-+ unsigned int mode = fixed_link ? MLO_AN_FIXED : MLO_AN_INBAND;
-+
-+ linkmode_set_pause(advertising, true, true);
-+ pcs->ops->pcs_config(pcs, mode, iface, advertising, true);
-+ if (fixed_link)
-+ pcs->ops->pcs_link_up(pcs, mode, iface, fixed_link->speed,
-+ fixed_link->duplex);
- else
-- /* AN disabled */
-- tmp_reg16 = SGMII_CR_DEF_VAL & ~SGMII_CR_AN_EN;
-- phy_write(memac->pcsphy, 0x0, tmp_reg16);
--}
--
--static void setup_sgmii_internal_phy_base_x(struct fman_mac *memac)
--{
-- u16 tmp_reg16;
--
-- /* AN Device capability */
-- tmp_reg16 = MDIO_SGMII_DEV_ABIL_BASEX_MODE;
-- phy_write(memac->pcsphy, MDIO_SGMII_DEV_ABIL_SGMII, tmp_reg16);
--
-- /* Adjust link timer for SGMII -
-- * For Serdes 1000BaseX auto-negotiation the timer should be 10 ms.
-- * The link_timer register is configured in units of the clock.
-- * - When running as 1G SGMII, Serdes clock is 125 MHz, so
-- * unit = 1 / (125*10^6 Hz) = 8 ns.
-- * 10 ms in units of 8 ns = 10ms / 8ns = 1250000 = 0x1312d0
-- * - When running as 2.5G SGMII, Serdes clock is 312.5 MHz, so
-- * unit = 1 / (312.5*10^6 Hz) = 3.2 ns.
-- * 10 ms in units of 3.2 ns = 10ms / 3.2ns = 3125000 = 0x2faf08.
-- * Since link_timer value of 1G SGMII will be too short for 2.5 SGMII,
-- * we always set up here a value of 2.5 SGMII.
-- */
-- phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_H, LINK_TMR_H_BASEX);
-- phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_L, LINK_TMR_L_BASEX);
--
-- /* Restart AN */
-- tmp_reg16 = SGMII_CR_DEF_VAL | SGMII_CR_RESTART_AN;
-- phy_write(memac->pcsphy, 0x0, tmp_reg16);
-+ pcs->ops->pcs_an_restart(pcs);
- }
-
- static int check_init_parameters(struct fman_mac *memac)
-@@ -983,7 +885,6 @@ static int memac_set_exception(struct fm
- static int memac_init(struct fman_mac *memac)
- {
- struct memac_cfg *memac_drv_param;
-- u8 i;
- enet_addr_t eth_addr;
- bool slow_10g_if = false;
- struct fixed_phy_status *fixed_link = NULL;
-@@ -1036,32 +937,10 @@ static int memac_init(struct fman_mac *m
- iowrite32be(reg32, &memac->regs->command_config);
- }
-
-- if (memac->phy_if == PHY_INTERFACE_MODE_SGMII) {
-- /* Configure internal SGMII PHY */
-- if (memac->basex_if)
-- setup_sgmii_internal_phy_base_x(memac);
-- else
-- setup_sgmii_internal_phy(memac, fixed_link);
-- } else if (memac->phy_if == PHY_INTERFACE_MODE_QSGMII) {
-- /* Configure 4 internal SGMII PHYs */
-- for (i = 0; i < 4; i++) {
-- u8 qsmgii_phy_addr, phy_addr;
-- /* QSGMII PHY address occupies 3 upper bits of 5-bit
-- * phy_address; the lower 2 bits are used to extend
-- * register address space and access each one of 4
-- * ports inside QSGMII.
-- */
-- phy_addr = memac->pcsphy->mdio.addr;
-- qsmgii_phy_addr = (u8)((phy_addr << 2) | i);
-- memac->pcsphy->mdio.addr = qsmgii_phy_addr;
-- if (memac->basex_if)
-- setup_sgmii_internal_phy_base_x(memac);
-- else
-- setup_sgmii_internal_phy(memac, fixed_link);
--
-- memac->pcsphy->mdio.addr = phy_addr;
-- }
-- }
-+ if (memac->phy_if == PHY_INTERFACE_MODE_SGMII)
-+ setup_sgmii_internal(memac, memac->sgmii_pcs, fixed_link);
-+ else if (memac->phy_if == PHY_INTERFACE_MODE_QSGMII)
-+ setup_sgmii_internal(memac, memac->qsgmii_pcs, fixed_link);
-
- /* Max Frame Length */
- err = fman_set_mac_max_frame(memac->fm, memac->mac_id,
-@@ -1097,12 +976,25 @@ static int memac_init(struct fman_mac *m
- return 0;
- }
-
-+static void pcs_put(struct phylink_pcs *pcs)
-+{
-+ struct mdio_device *mdiodev;
-+
-+ if (IS_ERR_OR_NULL(pcs))
-+ return;
-+
-+ mdiodev = lynx_get_mdio_device(pcs);
-+ lynx_pcs_destroy(pcs);
-+ mdio_device_free(mdiodev);
-+}
-+
- static int memac_free(struct fman_mac *memac)
- {
- free_init_resources(memac);
-
-- if (memac->pcsphy)
-- put_device(&memac->pcsphy->mdio.dev);
-+ pcs_put(memac->sgmii_pcs);
-+ pcs_put(memac->qsgmii_pcs);
-+ pcs_put(memac->xfi_pcs);
-
- kfree(memac->memac_drv_param);
- kfree(memac);
-@@ -1153,12 +1045,31 @@ static struct fman_mac *memac_config(str
- return memac;
- }
-
-+static struct phylink_pcs *memac_pcs_create(struct device_node *mac_node,
-+ int index)
-+{
-+ struct device_node *node;
-+ struct mdio_device *mdiodev = NULL;
-+ struct phylink_pcs *pcs;
-+
-+ node = of_parse_phandle(mac_node, "pcsphy-handle", index);
-+ if (node && of_device_is_available(node))
-+ mdiodev = of_mdio_find_device(node);
-+ of_node_put(node);
-+
-+ if (!mdiodev)
-+ return ERR_PTR(-EPROBE_DEFER);
-+
-+ pcs = lynx_pcs_create(mdiodev);
-+ return pcs;
-+}
-+
- int memac_initialization(struct mac_device *mac_dev,
- struct device_node *mac_node,
- struct fman_mac_params *params)
- {
- int err;
-- struct device_node *phy_node;
-+ struct phylink_pcs *pcs;
- struct fixed_phy_status *fixed_link;
- struct fman_mac *memac;
-
-@@ -1188,23 +1099,58 @@ int memac_initialization(struct mac_devi
- memac = mac_dev->fman_mac;
- memac->memac_drv_param->max_frame_length = fman_get_max_frm();
- memac->memac_drv_param->reset_on_init = true;
-- if (memac->phy_if == PHY_INTERFACE_MODE_SGMII ||
-- memac->phy_if == PHY_INTERFACE_MODE_QSGMII) {
-- phy_node = of_parse_phandle(mac_node, "pcsphy-handle", 0);
-- if (!phy_node) {
-- pr_err("PCS PHY node is not available\n");
-- err = -EINVAL;
-+
-+ err = of_property_match_string(mac_node, "pcs-handle-names", "xfi");
-+ if (err >= 0) {
-+ memac->xfi_pcs = memac_pcs_create(mac_node, err);
-+ if (IS_ERR(memac->xfi_pcs)) {
-+ err = PTR_ERR(memac->xfi_pcs);
-+ dev_err_probe(mac_dev->dev, err, "missing xfi pcs\n");
- goto _return_fm_mac_free;
- }
-+ } else if (err != -EINVAL && err != -ENODATA) {
-+ goto _return_fm_mac_free;
-+ }
-
-- memac->pcsphy = of_phy_find_device(phy_node);
-- if (!memac->pcsphy) {
-- pr_err("of_phy_find_device (PCS PHY) failed\n");
-- err = -EINVAL;
-+ err = of_property_match_string(mac_node, "pcs-handle-names", "qsgmii");
-+ if (err >= 0) {
-+ memac->qsgmii_pcs = memac_pcs_create(mac_node, err);
-+ if (IS_ERR(memac->qsgmii_pcs)) {
-+ err = PTR_ERR(memac->qsgmii_pcs);
-+ dev_err_probe(mac_dev->dev, err,
-+ "missing qsgmii pcs\n");
- goto _return_fm_mac_free;
- }
-+ } else if (err != -EINVAL && err != -ENODATA) {
-+ goto _return_fm_mac_free;
-+ }
-+
-+ /* For compatibility, if pcs-handle-names is missing, we assume this
-+ * phy is the first one in pcsphy-handle
-+ */
-+ err = of_property_match_string(mac_node, "pcs-handle-names", "sgmii");
-+ if (err == -EINVAL || err == -ENODATA)
-+ pcs = memac_pcs_create(mac_node, 0);
-+ else if (err < 0)
-+ goto _return_fm_mac_free;
-+ else
-+ pcs = memac_pcs_create(mac_node, err);
-+
-+ if (!pcs) {
-+ dev_err(mac_dev->dev, "missing pcs\n");
-+ err = -ENOENT;
-+ goto _return_fm_mac_free;
- }
-
-+ /* If err is set here, it means that pcs-handle-names was missing above
-+ * (and therefore that xfi_pcs cannot be set). If we are defaulting to
-+ * XGMII, assume this is for XFI. Otherwise, assume it is for SGMII.
-+ */
-+ if (err && mac_dev->phy_if == PHY_INTERFACE_MODE_XGMII)
-+ memac->xfi_pcs = pcs;
-+ else
-+ memac->sgmii_pcs = pcs;
-+
- memac->serdes = devm_of_phy_get(mac_dev->dev, mac_node, "serdes");
- err = PTR_ERR(memac->serdes);
- if (err == -ENODEV || err == -ENOSYS) {
+++ /dev/null
-From 38e50fc3d43882a43115b4f1ca3eb88255163c5b Mon Sep 17 00:00:00 2001
-From: Sean Anderson <sean.anderson@seco.com>
-Date: Mon, 17 Oct 2022 16:22:38 -0400
-Subject: [PATCH 03/21] net: dpaa: Convert to phylink
-
-This converts DPAA to phylink. All macs are converted. This should work
-with no device tree modifications (including those made in this series),
-except for QSGMII (as noted previously).
-
-The mEMAC configuration is one of the tricker areas. I have tried to
-capture all the restrictions across the various models. Most of the time,
-we assume that if the serdes supports a mode or the phy-interface-mode
-specifies it, then we support it. The only place we can't do this is
-(RG)MII, since there's no serdes. In that case, we rely on a (new)
-devicetree property. There are also several cases where half-duplex is
-broken. Unfortunately, only a single compatible is used for the MAC, so we
-have to use the board compatible instead.
-
-The 10GEC conversion is very straightforward, since it only supports XAUI.
-There is generally nothing to configure.
-
-The dTSEC conversion is broadly similar to mEMAC, but is simpler because we
-don't support configuring the SerDes (though this can be easily added) and
-we don't have multiple PCSs. From what I can tell, there's nothing
-different in the driver or documentation between SGMII and 1000BASE-X
-except for the advertising. Similarly, I couldn't find anything about
-2500BASE-X. In both cases, I treat them like SGMII. These modes aren't used
-by any in-tree boards. Similarly, despite being mentioned in the driver, I
-couldn't find any documented SoCs which supported QSGMII. I have left it
-unimplemented for now.
-
-Signed-off-by: Sean Anderson <sean.anderson@seco.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/ethernet/freescale/dpaa/Kconfig | 4 +-
- .../net/ethernet/freescale/dpaa/dpaa_eth.c | 89 +--
- .../ethernet/freescale/dpaa/dpaa_ethtool.c | 90 +--
- drivers/net/ethernet/freescale/fman/Kconfig | 1 -
- .../net/ethernet/freescale/fman/fman_dtsec.c | 458 +++++++--------
- .../net/ethernet/freescale/fman/fman_mac.h | 10 -
- .../net/ethernet/freescale/fman/fman_memac.c | 547 +++++++++---------
- .../net/ethernet/freescale/fman/fman_tgec.c | 131 ++---
- drivers/net/ethernet/freescale/fman/mac.c | 168 +-----
- drivers/net/ethernet/freescale/fman/mac.h | 23 +-
- 10 files changed, 612 insertions(+), 909 deletions(-)
-
---- a/drivers/net/ethernet/freescale/dpaa/Kconfig
-+++ b/drivers/net/ethernet/freescale/dpaa/Kconfig
-@@ -2,8 +2,8 @@
- menuconfig FSL_DPAA_ETH
- tristate "DPAA Ethernet"
- depends on FSL_DPAA && FSL_FMAN
-- select PHYLIB
-- select FIXED_PHY
-+ select PHYLINK
-+ select PCS_LYNX
- help
- Data Path Acceleration Architecture Ethernet driver,
- supporting the Freescale QorIQ chips.
---- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
-+++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
-@@ -264,8 +264,19 @@ static int dpaa_netdev_init(struct net_d
- net_dev->needed_headroom = priv->tx_headroom;
- net_dev->watchdog_timeo = msecs_to_jiffies(tx_timeout);
-
-- mac_dev->net_dev = net_dev;
-+ /* The rest of the config is filled in by the mac device already */
-+ mac_dev->phylink_config.dev = &net_dev->dev;
-+ mac_dev->phylink_config.type = PHYLINK_NETDEV;
- mac_dev->update_speed = dpaa_eth_cgr_set_speed;
-+ mac_dev->phylink = phylink_create(&mac_dev->phylink_config,
-+ dev_fwnode(mac_dev->dev),
-+ mac_dev->phy_if,
-+ mac_dev->phylink_ops);
-+ if (IS_ERR(mac_dev->phylink)) {
-+ err = PTR_ERR(mac_dev->phylink);
-+ dev_err_probe(dev, err, "Could not create phylink\n");
-+ return err;
-+ }
-
- /* start without the RUNNING flag, phylib controls it later */
- netif_carrier_off(net_dev);
-@@ -273,6 +284,7 @@ static int dpaa_netdev_init(struct net_d
- err = register_netdev(net_dev);
- if (err < 0) {
- dev_err(dev, "register_netdev() = %d\n", err);
-+ phylink_destroy(mac_dev->phylink);
- return err;
- }
-
-@@ -295,8 +307,7 @@ static int dpaa_stop(struct net_device *
- */
- msleep(200);
-
-- if (mac_dev->phy_dev)
-- phy_stop(mac_dev->phy_dev);
-+ phylink_stop(mac_dev->phylink);
- mac_dev->disable(mac_dev->fman_mac);
-
- for (i = 0; i < ARRAY_SIZE(mac_dev->port); i++) {
-@@ -305,8 +316,7 @@ static int dpaa_stop(struct net_device *
- err = error;
- }
-
-- if (net_dev->phydev)
-- phy_disconnect(net_dev->phydev);
-+ phylink_disconnect_phy(mac_dev->phylink);
- net_dev->phydev = NULL;
-
- msleep(200);
-@@ -834,10 +844,10 @@ static int dpaa_eth_cgr_init(struct dpaa
-
- /* Set different thresholds based on the configured MAC speed.
- * This may turn suboptimal if the MAC is reconfigured at another
-- * speed, so MACs must call dpaa_eth_cgr_set_speed in their adjust_link
-+ * speed, so MACs must call dpaa_eth_cgr_set_speed in their link_up
- * callback.
- */
-- if (priv->mac_dev->if_support & SUPPORTED_10000baseT_Full)
-+ if (priv->mac_dev->phylink_config.mac_capabilities & MAC_10000FD)
- cs_th = DPAA_CS_THRESHOLD_10G;
- else
- cs_th = DPAA_CS_THRESHOLD_1G;
-@@ -866,7 +876,7 @@ out_error:
-
- static void dpaa_eth_cgr_set_speed(struct mac_device *mac_dev, int speed)
- {
-- struct net_device *net_dev = mac_dev->net_dev;
-+ struct net_device *net_dev = to_net_dev(mac_dev->phylink_config.dev);
- struct dpaa_priv *priv = netdev_priv(net_dev);
- struct qm_mcc_initcgr opts = { };
- u32 cs_th;
-@@ -2905,58 +2915,6 @@ static void dpaa_eth_napi_disable(struct
- }
- }
-
--static void dpaa_adjust_link(struct net_device *net_dev)
--{
-- struct mac_device *mac_dev;
-- struct dpaa_priv *priv;
--
-- priv = netdev_priv(net_dev);
-- mac_dev = priv->mac_dev;
-- mac_dev->adjust_link(mac_dev);
--}
--
--/* The Aquantia PHYs are capable of performing rate adaptation */
--#define PHY_VEND_AQUANTIA 0x03a1b400
--#define PHY_VEND_AQUANTIA2 0x31c31c00
--
--static int dpaa_phy_init(struct net_device *net_dev)
--{
-- __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
-- struct mac_device *mac_dev;
-- struct phy_device *phy_dev;
-- struct dpaa_priv *priv;
-- u32 phy_vendor;
--
-- priv = netdev_priv(net_dev);
-- mac_dev = priv->mac_dev;
--
-- phy_dev = of_phy_connect(net_dev, mac_dev->phy_node,
-- &dpaa_adjust_link, 0,
-- mac_dev->phy_if);
-- if (!phy_dev) {
-- netif_err(priv, ifup, net_dev, "init_phy() failed\n");
-- return -ENODEV;
-- }
--
-- phy_vendor = phy_dev->drv->phy_id & GENMASK(31, 10);
-- /* Unless the PHY is capable of rate adaptation */
-- if (mac_dev->phy_if != PHY_INTERFACE_MODE_XGMII ||
-- (phy_vendor != PHY_VEND_AQUANTIA &&
-- phy_vendor != PHY_VEND_AQUANTIA2)) {
-- /* remove any features not supported by the controller */
-- ethtool_convert_legacy_u32_to_link_mode(mask,
-- mac_dev->if_support);
-- linkmode_and(phy_dev->supported, phy_dev->supported, mask);
-- }
--
-- phy_support_asym_pause(phy_dev);
--
-- mac_dev->phy_dev = phy_dev;
-- net_dev->phydev = phy_dev;
--
-- return 0;
--}
--
- static int dpaa_open(struct net_device *net_dev)
- {
- struct mac_device *mac_dev;
-@@ -2967,7 +2925,8 @@ static int dpaa_open(struct net_device *
- mac_dev = priv->mac_dev;
- dpaa_eth_napi_enable(priv);
-
-- err = dpaa_phy_init(net_dev);
-+ err = phylink_of_phy_connect(mac_dev->phylink,
-+ mac_dev->dev->of_node, 0);
- if (err)
- goto phy_init_failed;
-
-@@ -2982,7 +2941,7 @@ static int dpaa_open(struct net_device *
- netif_err(priv, ifup, net_dev, "mac_dev->enable() = %d\n", err);
- goto mac_start_failed;
- }
-- phy_start(priv->mac_dev->phy_dev);
-+ phylink_start(mac_dev->phylink);
-
- netif_tx_start_all_queues(net_dev);
-
-@@ -2991,6 +2950,7 @@ static int dpaa_open(struct net_device *
- mac_start_failed:
- for (i = 0; i < ARRAY_SIZE(mac_dev->port); i++)
- fman_port_disable(mac_dev->port[i]);
-+ phylink_disconnect_phy(mac_dev->phylink);
-
- phy_init_failed:
- dpaa_eth_napi_disable(priv);
-@@ -3146,10 +3106,12 @@ static int dpaa_ts_ioctl(struct net_devi
- static int dpaa_ioctl(struct net_device *net_dev, struct ifreq *rq, int cmd)
- {
- int ret = -EINVAL;
-+ struct dpaa_priv *priv = netdev_priv(net_dev);
-
- if (cmd == SIOCGMIIREG) {
- if (net_dev->phydev)
-- return phy_mii_ioctl(net_dev->phydev, rq, cmd);
-+ return phylink_mii_ioctl(priv->mac_dev->phylink, rq,
-+ cmd);
- }
-
- if (cmd == SIOCSHWTSTAMP)
-@@ -3552,6 +3514,7 @@ static int dpaa_remove(struct platform_d
-
- dev_set_drvdata(dev, NULL);
- unregister_netdev(net_dev);
-+ phylink_destroy(priv->mac_dev->phylink);
-
- err = dpaa_fq_free(dev, &priv->dpaa_fq_list);
-
---- a/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c
-+++ b/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c
-@@ -54,27 +54,19 @@ static char dpaa_stats_global[][ETH_GSTR
- static int dpaa_get_link_ksettings(struct net_device *net_dev,
- struct ethtool_link_ksettings *cmd)
- {
-- if (!net_dev->phydev)
-- return 0;
-+ struct dpaa_priv *priv = netdev_priv(net_dev);
-+ struct mac_device *mac_dev = priv->mac_dev;
-
-- phy_ethtool_ksettings_get(net_dev->phydev, cmd);
--
-- return 0;
-+ return phylink_ethtool_ksettings_get(mac_dev->phylink, cmd);
- }
-
- static int dpaa_set_link_ksettings(struct net_device *net_dev,
- const struct ethtool_link_ksettings *cmd)
- {
-- int err;
--
-- if (!net_dev->phydev)
-- return -ENODEV;
-+ struct dpaa_priv *priv = netdev_priv(net_dev);
-+ struct mac_device *mac_dev = priv->mac_dev;
-
-- err = phy_ethtool_ksettings_set(net_dev->phydev, cmd);
-- if (err < 0)
-- netdev_err(net_dev, "phy_ethtool_ksettings_set() = %d\n", err);
--
-- return err;
-+ return phylink_ethtool_ksettings_set(mac_dev->phylink, cmd);
- }
-
- static void dpaa_get_drvinfo(struct net_device *net_dev,
-@@ -99,80 +91,28 @@ static void dpaa_set_msglevel(struct net
-
- static int dpaa_nway_reset(struct net_device *net_dev)
- {
-- int err;
--
-- if (!net_dev->phydev)
-- return -ENODEV;
-+ struct dpaa_priv *priv = netdev_priv(net_dev);
-+ struct mac_device *mac_dev = priv->mac_dev;
-
-- err = 0;
-- if (net_dev->phydev->autoneg) {
-- err = phy_start_aneg(net_dev->phydev);
-- if (err < 0)
-- netdev_err(net_dev, "phy_start_aneg() = %d\n",
-- err);
-- }
--
-- return err;
-+ return phylink_ethtool_nway_reset(mac_dev->phylink);
- }
-
- static void dpaa_get_pauseparam(struct net_device *net_dev,
- struct ethtool_pauseparam *epause)
- {
-- struct mac_device *mac_dev;
-- struct dpaa_priv *priv;
--
-- priv = netdev_priv(net_dev);
-- mac_dev = priv->mac_dev;
--
-- if (!net_dev->phydev)
-- return;
-+ struct dpaa_priv *priv = netdev_priv(net_dev);
-+ struct mac_device *mac_dev = priv->mac_dev;
-
-- epause->autoneg = mac_dev->autoneg_pause;
-- epause->rx_pause = mac_dev->rx_pause_active;
-- epause->tx_pause = mac_dev->tx_pause_active;
-+ phylink_ethtool_get_pauseparam(mac_dev->phylink, epause);
- }
-
- static int dpaa_set_pauseparam(struct net_device *net_dev,
- struct ethtool_pauseparam *epause)
- {
-- struct mac_device *mac_dev;
-- struct phy_device *phydev;
-- bool rx_pause, tx_pause;
-- struct dpaa_priv *priv;
-- int err;
--
-- priv = netdev_priv(net_dev);
-- mac_dev = priv->mac_dev;
--
-- phydev = net_dev->phydev;
-- if (!phydev) {
-- netdev_err(net_dev, "phy device not initialized\n");
-- return -ENODEV;
-- }
--
-- if (!phy_validate_pause(phydev, epause))
-- return -EINVAL;
--
-- /* The MAC should know how to handle PAUSE frame autonegotiation before
-- * adjust_link is triggered by a forced renegotiation of sym/asym PAUSE
-- * settings.
-- */
-- mac_dev->autoneg_pause = !!epause->autoneg;
-- mac_dev->rx_pause_req = !!epause->rx_pause;
-- mac_dev->tx_pause_req = !!epause->tx_pause;
--
-- /* Determine the sym/asym advertised PAUSE capabilities from the desired
-- * rx/tx pause settings.
-- */
--
-- phy_set_asym_pause(phydev, epause->rx_pause, epause->tx_pause);
--
-- fman_get_pause_cfg(mac_dev, &rx_pause, &tx_pause);
-- err = fman_set_mac_active_pause(mac_dev, rx_pause, tx_pause);
-- if (err < 0)
-- netdev_err(net_dev, "set_mac_active_pause() = %d\n", err);
-+ struct dpaa_priv *priv = netdev_priv(net_dev);
-+ struct mac_device *mac_dev = priv->mac_dev;
-
-- return err;
-+ return phylink_ethtool_set_pauseparam(mac_dev->phylink, epause);
- }
-
- static int dpaa_get_sset_count(struct net_device *net_dev, int type)
---- a/drivers/net/ethernet/freescale/fman/Kconfig
-+++ b/drivers/net/ethernet/freescale/fman/Kconfig
-@@ -3,7 +3,6 @@ config FSL_FMAN
- tristate "FMan support"
- depends on FSL_SOC || ARCH_LAYERSCAPE || COMPILE_TEST
- select GENERIC_ALLOCATOR
-- select PHYLIB
- select PHYLINK
- select PCS
- select PCS_LYNX
---- a/drivers/net/ethernet/freescale/fman/fman_dtsec.c
-+++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c
-@@ -17,6 +17,7 @@
- #include <linux/crc32.h>
- #include <linux/of_mdio.h>
- #include <linux/mii.h>
-+#include <linux/netdevice.h>
-
- /* TBI register addresses */
- #define MII_TBICON 0x11
-@@ -29,9 +30,6 @@
- #define TBICON_CLK_SELECT 0x0020 /* Clock select */
- #define TBICON_MI_MODE 0x0010 /* GMII mode (TBI if not set) */
-
--#define TBIANA_SGMII 0x4001
--#define TBIANA_1000X 0x01a0
--
- /* Interrupt Mask Register (IMASK) */
- #define DTSEC_IMASK_BREN 0x80000000
- #define DTSEC_IMASK_RXCEN 0x40000000
-@@ -92,9 +90,10 @@
-
- #define DTSEC_ECNTRL_GMIIM 0x00000040
- #define DTSEC_ECNTRL_TBIM 0x00000020
--#define DTSEC_ECNTRL_SGMIIM 0x00000002
- #define DTSEC_ECNTRL_RPM 0x00000010
- #define DTSEC_ECNTRL_R100M 0x00000008
-+#define DTSEC_ECNTRL_RMM 0x00000004
-+#define DTSEC_ECNTRL_SGMIIM 0x00000002
- #define DTSEC_ECNTRL_QSGMIIM 0x00000001
-
- #define TCTRL_TTSE 0x00000040
-@@ -318,7 +317,8 @@ struct fman_mac {
- void *fm;
- struct fman_rev_info fm_rev_info;
- bool basex_if;
-- struct phy_device *tbiphy;
-+ struct mdio_device *tbidev;
-+ struct phylink_pcs pcs;
- };
-
- static void set_dflts(struct dtsec_cfg *cfg)
-@@ -356,56 +356,14 @@ static int init(struct dtsec_regs __iome
- phy_interface_t iface, u16 iface_speed, u64 addr,
- u32 exception_mask, u8 tbi_addr)
- {
-- bool is_rgmii, is_sgmii, is_qsgmii;
- enet_addr_t eth_addr;
-- u32 tmp;
-+ u32 tmp = 0;
- int i;
-
- /* Soft reset */
- iowrite32be(MACCFG1_SOFT_RESET, ®s->maccfg1);
- iowrite32be(0, ®s->maccfg1);
-
-- /* dtsec_id2 */
-- tmp = ioread32be(®s->tsec_id2);
--
-- /* check RGMII support */
-- if (iface == PHY_INTERFACE_MODE_RGMII ||
-- iface == PHY_INTERFACE_MODE_RGMII_ID ||
-- iface == PHY_INTERFACE_MODE_RGMII_RXID ||
-- iface == PHY_INTERFACE_MODE_RGMII_TXID ||
-- iface == PHY_INTERFACE_MODE_RMII)
-- if (tmp & DTSEC_ID2_INT_REDUCED_OFF)
-- return -EINVAL;
--
-- if (iface == PHY_INTERFACE_MODE_SGMII ||
-- iface == PHY_INTERFACE_MODE_MII)
-- if (tmp & DTSEC_ID2_INT_REDUCED_OFF)
-- return -EINVAL;
--
-- is_rgmii = iface == PHY_INTERFACE_MODE_RGMII ||
-- iface == PHY_INTERFACE_MODE_RGMII_ID ||
-- iface == PHY_INTERFACE_MODE_RGMII_RXID ||
-- iface == PHY_INTERFACE_MODE_RGMII_TXID;
-- is_sgmii = iface == PHY_INTERFACE_MODE_SGMII;
-- is_qsgmii = iface == PHY_INTERFACE_MODE_QSGMII;
--
-- tmp = 0;
-- if (is_rgmii || iface == PHY_INTERFACE_MODE_GMII)
-- tmp |= DTSEC_ECNTRL_GMIIM;
-- if (is_sgmii)
-- tmp |= (DTSEC_ECNTRL_SGMIIM | DTSEC_ECNTRL_TBIM);
-- if (is_qsgmii)
-- tmp |= (DTSEC_ECNTRL_SGMIIM | DTSEC_ECNTRL_TBIM |
-- DTSEC_ECNTRL_QSGMIIM);
-- if (is_rgmii)
-- tmp |= DTSEC_ECNTRL_RPM;
-- if (iface_speed == SPEED_100)
-- tmp |= DTSEC_ECNTRL_R100M;
--
-- iowrite32be(tmp, ®s->ecntrl);
--
-- tmp = 0;
--
- if (cfg->tx_pause_time)
- tmp |= cfg->tx_pause_time;
- if (cfg->tx_pause_time_extd)
-@@ -446,17 +404,10 @@ static int init(struct dtsec_regs __iome
-
- tmp = 0;
-
-- if (iface_speed < SPEED_1000)
-- tmp |= MACCFG2_NIBBLE_MODE;
-- else if (iface_speed == SPEED_1000)
-- tmp |= MACCFG2_BYTE_MODE;
--
- tmp |= (cfg->preamble_len << MACCFG2_PREAMBLE_LENGTH_SHIFT) &
- MACCFG2_PREAMBLE_LENGTH_MASK;
- if (cfg->tx_pad_crc)
- tmp |= MACCFG2_PAD_CRC_EN;
-- /* Full Duplex */
-- tmp |= MACCFG2_FULL_DUPLEX;
- iowrite32be(tmp, ®s->maccfg2);
-
- tmp = (((cfg->non_back_to_back_ipg1 <<
-@@ -525,10 +476,6 @@ static void set_bucket(struct dtsec_regs
-
- static int check_init_parameters(struct fman_mac *dtsec)
- {
-- if (dtsec->max_speed >= SPEED_10000) {
-- pr_err("1G MAC driver supports 1G or lower speeds\n");
-- return -EINVAL;
-- }
- if ((dtsec->dtsec_drv_param)->rx_prepend >
- MAX_PACKET_ALIGNMENT) {
- pr_err("packetAlignmentPadding can't be > than %d\n",
-@@ -630,22 +577,10 @@ static int get_exception_flag(enum fman_
- return bit_mask;
- }
-
--static bool is_init_done(struct dtsec_cfg *dtsec_drv_params)
--{
-- /* Checks if dTSEC driver parameters were initialized */
-- if (!dtsec_drv_params)
-- return true;
--
-- return false;
--}
--
- static u16 dtsec_get_max_frame_length(struct fman_mac *dtsec)
- {
- struct dtsec_regs __iomem *regs = dtsec->regs;
-
-- if (is_init_done(dtsec->dtsec_drv_param))
-- return 0;
--
- return (u16)ioread32be(®s->maxfrm);
- }
-
-@@ -682,6 +617,7 @@ static void dtsec_isr(void *handle)
- dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_COL_RET_LMT);
- if (event & DTSEC_IMASK_XFUNEN) {
- /* FM_TX_LOCKUP_ERRATA_DTSEC6 Errata workaround */
-+ /* FIXME: This races with the rest of the driver! */
- if (dtsec->fm_rev_info.major == 2) {
- u32 tpkt1, tmp_reg1, tpkt2, tmp_reg2, i;
- /* a. Write 0x00E0_0C00 to DTSEC_ID
-@@ -814,6 +750,43 @@ static void free_init_resources(struct f
- dtsec->unicast_addr_hash = NULL;
- }
-
-+static struct fman_mac *pcs_to_dtsec(struct phylink_pcs *pcs)
-+{
-+ return container_of(pcs, struct fman_mac, pcs);
-+}
-+
-+static void dtsec_pcs_get_state(struct phylink_pcs *pcs,
-+ struct phylink_link_state *state)
-+{
-+ struct fman_mac *dtsec = pcs_to_dtsec(pcs);
-+
-+ phylink_mii_c22_pcs_get_state(dtsec->tbidev, state);
-+}
-+
-+static int dtsec_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
-+ phy_interface_t interface,
-+ const unsigned long *advertising,
-+ bool permit_pause_to_mac)
-+{
-+ struct fman_mac *dtsec = pcs_to_dtsec(pcs);
-+
-+ return phylink_mii_c22_pcs_config(dtsec->tbidev, mode, interface,
-+ advertising);
-+}
-+
-+static void dtsec_pcs_an_restart(struct phylink_pcs *pcs)
-+{
-+ struct fman_mac *dtsec = pcs_to_dtsec(pcs);
-+
-+ phylink_mii_c22_pcs_an_restart(dtsec->tbidev);
-+}
-+
-+static const struct phylink_pcs_ops dtsec_pcs_ops = {
-+ .pcs_get_state = dtsec_pcs_get_state,
-+ .pcs_config = dtsec_pcs_config,
-+ .pcs_an_restart = dtsec_pcs_an_restart,
-+};
-+
- static void graceful_start(struct fman_mac *dtsec)
- {
- struct dtsec_regs __iomem *regs = dtsec->regs;
-@@ -854,36 +827,11 @@ static void graceful_stop(struct fman_ma
-
- static int dtsec_enable(struct fman_mac *dtsec)
- {
-- struct dtsec_regs __iomem *regs = dtsec->regs;
-- u32 tmp;
--
-- if (!is_init_done(dtsec->dtsec_drv_param))
-- return -EINVAL;
--
-- /* Enable */
-- tmp = ioread32be(®s->maccfg1);
-- tmp |= MACCFG1_RX_EN | MACCFG1_TX_EN;
-- iowrite32be(tmp, ®s->maccfg1);
--
-- /* Graceful start - clear the graceful Rx/Tx stop bit */
-- graceful_start(dtsec);
--
- return 0;
- }
-
- static void dtsec_disable(struct fman_mac *dtsec)
- {
-- struct dtsec_regs __iomem *regs = dtsec->regs;
-- u32 tmp;
--
-- WARN_ON_ONCE(!is_init_done(dtsec->dtsec_drv_param));
--
-- /* Graceful stop - Assert the graceful Rx/Tx stop bit */
-- graceful_stop(dtsec);
--
-- tmp = ioread32be(®s->maccfg1);
-- tmp &= ~(MACCFG1_RX_EN | MACCFG1_TX_EN);
-- iowrite32be(tmp, ®s->maccfg1);
- }
-
- static int dtsec_set_tx_pause_frames(struct fman_mac *dtsec,
-@@ -894,11 +842,6 @@ static int dtsec_set_tx_pause_frames(str
- struct dtsec_regs __iomem *regs = dtsec->regs;
- u32 ptv = 0;
-
-- if (!is_init_done(dtsec->dtsec_drv_param))
-- return -EINVAL;
--
-- graceful_stop(dtsec);
--
- if (pause_time) {
- /* FM_BAD_TX_TS_IN_B_2_B_ERRATA_DTSEC_A003 Errata workaround */
- if (dtsec->fm_rev_info.major == 2 && pause_time <= 320) {
-@@ -919,8 +862,6 @@ static int dtsec_set_tx_pause_frames(str
- iowrite32be(ioread32be(®s->maccfg1) & ~MACCFG1_TX_FLOW,
- ®s->maccfg1);
-
-- graceful_start(dtsec);
--
- return 0;
- }
-
-@@ -929,11 +870,6 @@ static int dtsec_accept_rx_pause_frames(
- struct dtsec_regs __iomem *regs = dtsec->regs;
- u32 tmp;
-
-- if (!is_init_done(dtsec->dtsec_drv_param))
-- return -EINVAL;
--
-- graceful_stop(dtsec);
--
- tmp = ioread32be(®s->maccfg1);
- if (en)
- tmp |= MACCFG1_RX_FLOW;
-@@ -941,17 +877,125 @@ static int dtsec_accept_rx_pause_frames(
- tmp &= ~MACCFG1_RX_FLOW;
- iowrite32be(tmp, ®s->maccfg1);
-
-+ return 0;
-+}
-+
-+static struct phylink_pcs *dtsec_select_pcs(struct phylink_config *config,
-+ phy_interface_t iface)
-+{
-+ struct fman_mac *dtsec = fman_config_to_mac(config)->fman_mac;
-+
-+ switch (iface) {
-+ case PHY_INTERFACE_MODE_SGMII:
-+ case PHY_INTERFACE_MODE_1000BASEX:
-+ case PHY_INTERFACE_MODE_2500BASEX:
-+ return &dtsec->pcs;
-+ default:
-+ return NULL;
-+ }
-+}
-+
-+static void dtsec_mac_config(struct phylink_config *config, unsigned int mode,
-+ const struct phylink_link_state *state)
-+{
-+ struct mac_device *mac_dev = fman_config_to_mac(config);
-+ struct dtsec_regs __iomem *regs = mac_dev->fman_mac->regs;
-+ u32 tmp;
-+
-+ switch (state->interface) {
-+ case PHY_INTERFACE_MODE_RMII:
-+ tmp = DTSEC_ECNTRL_RMM;
-+ break;
-+ case PHY_INTERFACE_MODE_RGMII:
-+ case PHY_INTERFACE_MODE_RGMII_ID:
-+ case PHY_INTERFACE_MODE_RGMII_RXID:
-+ case PHY_INTERFACE_MODE_RGMII_TXID:
-+ tmp = DTSEC_ECNTRL_GMIIM | DTSEC_ECNTRL_RPM;
-+ break;
-+ case PHY_INTERFACE_MODE_SGMII:
-+ case PHY_INTERFACE_MODE_1000BASEX:
-+ case PHY_INTERFACE_MODE_2500BASEX:
-+ tmp = DTSEC_ECNTRL_TBIM | DTSEC_ECNTRL_SGMIIM;
-+ break;
-+ default:
-+ dev_warn(mac_dev->dev, "cannot configure dTSEC for %s\n",
-+ phy_modes(state->interface));
-+ return;
-+ }
-+
-+ iowrite32be(tmp, ®s->ecntrl);
-+}
-+
-+static void dtsec_link_up(struct phylink_config *config, struct phy_device *phy,
-+ unsigned int mode, phy_interface_t interface,
-+ int speed, int duplex, bool tx_pause, bool rx_pause)
-+{
-+ struct mac_device *mac_dev = fman_config_to_mac(config);
-+ struct fman_mac *dtsec = mac_dev->fman_mac;
-+ struct dtsec_regs __iomem *regs = dtsec->regs;
-+ u16 pause_time = tx_pause ? FSL_FM_PAUSE_TIME_ENABLE :
-+ FSL_FM_PAUSE_TIME_DISABLE;
-+ u32 tmp;
-+
-+ dtsec_set_tx_pause_frames(dtsec, 0, pause_time, 0);
-+ dtsec_accept_rx_pause_frames(dtsec, rx_pause);
-+
-+ tmp = ioread32be(®s->ecntrl);
-+ if (speed == SPEED_100)
-+ tmp |= DTSEC_ECNTRL_R100M;
-+ else
-+ tmp &= ~DTSEC_ECNTRL_R100M;
-+ iowrite32be(tmp, ®s->ecntrl);
-+
-+ tmp = ioread32be(®s->maccfg2);
-+ tmp &= ~(MACCFG2_NIBBLE_MODE | MACCFG2_BYTE_MODE | MACCFG2_FULL_DUPLEX);
-+ if (speed >= SPEED_1000)
-+ tmp |= MACCFG2_BYTE_MODE;
-+ else
-+ tmp |= MACCFG2_NIBBLE_MODE;
-+
-+ if (duplex == DUPLEX_FULL)
-+ tmp |= MACCFG2_FULL_DUPLEX;
-+
-+ iowrite32be(tmp, ®s->maccfg2);
-+
-+ mac_dev->update_speed(mac_dev, speed);
-+
-+ /* Enable */
-+ tmp = ioread32be(®s->maccfg1);
-+ tmp |= MACCFG1_RX_EN | MACCFG1_TX_EN;
-+ iowrite32be(tmp, ®s->maccfg1);
-+
-+ /* Graceful start - clear the graceful Rx/Tx stop bit */
- graceful_start(dtsec);
-+}
-
-- return 0;
-+static void dtsec_link_down(struct phylink_config *config, unsigned int mode,
-+ phy_interface_t interface)
-+{
-+ struct fman_mac *dtsec = fman_config_to_mac(config)->fman_mac;
-+ struct dtsec_regs __iomem *regs = dtsec->regs;
-+ u32 tmp;
-+
-+ /* Graceful stop - Assert the graceful Rx/Tx stop bit */
-+ graceful_stop(dtsec);
-+
-+ tmp = ioread32be(®s->maccfg1);
-+ tmp &= ~(MACCFG1_RX_EN | MACCFG1_TX_EN);
-+ iowrite32be(tmp, ®s->maccfg1);
- }
-
-+static const struct phylink_mac_ops dtsec_mac_ops = {
-+ .validate = phylink_generic_validate,
-+ .mac_select_pcs = dtsec_select_pcs,
-+ .mac_config = dtsec_mac_config,
-+ .mac_link_up = dtsec_link_up,
-+ .mac_link_down = dtsec_link_down,
-+};
-+
- static int dtsec_modify_mac_address(struct fman_mac *dtsec,
- const enet_addr_t *enet_addr)
- {
-- if (!is_init_done(dtsec->dtsec_drv_param))
-- return -EINVAL;
--
- graceful_stop(dtsec);
-
- /* Initialize MAC Station Address registers (1 & 2)
-@@ -975,9 +1019,6 @@ static int dtsec_add_hash_mac_address(st
- u32 crc = 0xFFFFFFFF;
- bool mcast, ghtx;
-
-- if (!is_init_done(dtsec->dtsec_drv_param))
-- return -EINVAL;
--
- addr = ENET_ADDR_TO_UINT64(*eth_addr);
-
- ghtx = (bool)((ioread32be(®s->rctrl) & RCTRL_GHTX) ? true : false);
-@@ -1037,9 +1078,6 @@ static int dtsec_set_allmulti(struct fma
- u32 tmp;
- struct dtsec_regs __iomem *regs = dtsec->regs;
-
-- if (!is_init_done(dtsec->dtsec_drv_param))
-- return -EINVAL;
--
- tmp = ioread32be(®s->rctrl);
- if (enable)
- tmp |= RCTRL_MPROM;
-@@ -1056,9 +1094,6 @@ static int dtsec_set_tstamp(struct fman_
- struct dtsec_regs __iomem *regs = dtsec->regs;
- u32 rctrl, tctrl;
-
-- if (!is_init_done(dtsec->dtsec_drv_param))
-- return -EINVAL;
--
- rctrl = ioread32be(®s->rctrl);
- tctrl = ioread32be(®s->tctrl);
-
-@@ -1087,9 +1122,6 @@ static int dtsec_del_hash_mac_address(st
- u32 crc = 0xFFFFFFFF;
- bool mcast, ghtx;
-
-- if (!is_init_done(dtsec->dtsec_drv_param))
-- return -EINVAL;
--
- addr = ENET_ADDR_TO_UINT64(*eth_addr);
-
- ghtx = (bool)((ioread32be(®s->rctrl) & RCTRL_GHTX) ? true : false);
-@@ -1153,9 +1185,6 @@ static int dtsec_set_promiscuous(struct
- struct dtsec_regs __iomem *regs = dtsec->regs;
- u32 tmp;
-
-- if (!is_init_done(dtsec->dtsec_drv_param))
-- return -EINVAL;
--
- /* Set unicast promiscuous */
- tmp = ioread32be(®s->rctrl);
- if (new_val)
-@@ -1177,90 +1206,12 @@ static int dtsec_set_promiscuous(struct
- return 0;
- }
-
--static int dtsec_adjust_link(struct fman_mac *dtsec, u16 speed)
--{
-- struct dtsec_regs __iomem *regs = dtsec->regs;
-- u32 tmp;
--
-- if (!is_init_done(dtsec->dtsec_drv_param))
-- return -EINVAL;
--
-- graceful_stop(dtsec);
--
-- tmp = ioread32be(®s->maccfg2);
--
-- /* Full Duplex */
-- tmp |= MACCFG2_FULL_DUPLEX;
--
-- tmp &= ~(MACCFG2_NIBBLE_MODE | MACCFG2_BYTE_MODE);
-- if (speed < SPEED_1000)
-- tmp |= MACCFG2_NIBBLE_MODE;
-- else if (speed == SPEED_1000)
-- tmp |= MACCFG2_BYTE_MODE;
-- iowrite32be(tmp, ®s->maccfg2);
--
-- tmp = ioread32be(®s->ecntrl);
-- if (speed == SPEED_100)
-- tmp |= DTSEC_ECNTRL_R100M;
-- else
-- tmp &= ~DTSEC_ECNTRL_R100M;
-- iowrite32be(tmp, ®s->ecntrl);
--
-- graceful_start(dtsec);
--
-- return 0;
--}
--
--static int dtsec_restart_autoneg(struct fman_mac *dtsec)
--{
-- u16 tmp_reg16;
--
-- if (!is_init_done(dtsec->dtsec_drv_param))
-- return -EINVAL;
--
-- tmp_reg16 = phy_read(dtsec->tbiphy, MII_BMCR);
--
-- tmp_reg16 &= ~(BMCR_SPEED100 | BMCR_SPEED1000);
-- tmp_reg16 |= (BMCR_ANENABLE | BMCR_ANRESTART |
-- BMCR_FULLDPLX | BMCR_SPEED1000);
--
-- phy_write(dtsec->tbiphy, MII_BMCR, tmp_reg16);
--
-- return 0;
--}
--
--static void adjust_link_dtsec(struct mac_device *mac_dev)
--{
-- struct phy_device *phy_dev = mac_dev->phy_dev;
-- struct fman_mac *fman_mac;
-- bool rx_pause, tx_pause;
-- int err;
--
-- fman_mac = mac_dev->fman_mac;
-- if (!phy_dev->link) {
-- dtsec_restart_autoneg(fman_mac);
--
-- return;
-- }
--
-- dtsec_adjust_link(fman_mac, phy_dev->speed);
-- mac_dev->update_speed(mac_dev, phy_dev->speed);
-- fman_get_pause_cfg(mac_dev, &rx_pause, &tx_pause);
-- err = fman_set_mac_active_pause(mac_dev, rx_pause, tx_pause);
-- if (err < 0)
-- dev_err(mac_dev->dev, "fman_set_mac_active_pause() = %d\n",
-- err);
--}
--
- static int dtsec_set_exception(struct fman_mac *dtsec,
- enum fman_mac_exceptions exception, bool enable)
- {
- struct dtsec_regs __iomem *regs = dtsec->regs;
- u32 bit_mask = 0;
-
-- if (!is_init_done(dtsec->dtsec_drv_param))
-- return -EINVAL;
--
- if (exception != FM_MAC_EX_1G_1588_TS_RX_ERR) {
- bit_mask = get_exception_flag(exception);
- if (bit_mask) {
-@@ -1310,12 +1261,9 @@ static int dtsec_init(struct fman_mac *d
- {
- struct dtsec_regs __iomem *regs = dtsec->regs;
- struct dtsec_cfg *dtsec_drv_param;
-- u16 max_frm_ln;
-+ u16 max_frm_ln, tbicon;
- int err;
-
-- if (is_init_done(dtsec->dtsec_drv_param))
-- return -EINVAL;
--
- if (DEFAULT_RESET_ON_INIT &&
- (fman_reset_mac(dtsec->fm, dtsec->mac_id) != 0)) {
- pr_err("Can't reset MAC!\n");
-@@ -1330,38 +1278,19 @@ static int dtsec_init(struct fman_mac *d
-
- err = init(dtsec->regs, dtsec_drv_param, dtsec->phy_if,
- dtsec->max_speed, dtsec->addr, dtsec->exceptions,
-- dtsec->tbiphy->mdio.addr);
-+ dtsec->tbidev->addr);
- if (err) {
- free_init_resources(dtsec);
- pr_err("DTSEC version doesn't support this i/f mode\n");
- return err;
- }
-
-- if (dtsec->phy_if == PHY_INTERFACE_MODE_SGMII) {
-- u16 tmp_reg16;
--
-- /* Configure the TBI PHY Control Register */
-- tmp_reg16 = TBICON_CLK_SELECT | TBICON_SOFT_RESET;
-- phy_write(dtsec->tbiphy, MII_TBICON, tmp_reg16);
--
-- tmp_reg16 = TBICON_CLK_SELECT;
-- phy_write(dtsec->tbiphy, MII_TBICON, tmp_reg16);
--
-- tmp_reg16 = (BMCR_RESET | BMCR_ANENABLE |
-- BMCR_FULLDPLX | BMCR_SPEED1000);
-- phy_write(dtsec->tbiphy, MII_BMCR, tmp_reg16);
--
-- if (dtsec->basex_if)
-- tmp_reg16 = TBIANA_1000X;
-- else
-- tmp_reg16 = TBIANA_SGMII;
-- phy_write(dtsec->tbiphy, MII_ADVERTISE, tmp_reg16);
-+ /* Configure the TBI PHY Control Register */
-+ tbicon = TBICON_CLK_SELECT | TBICON_SOFT_RESET;
-+ mdiodev_write(dtsec->tbidev, MII_TBICON, tbicon);
-
-- tmp_reg16 = (BMCR_ANENABLE | BMCR_ANRESTART |
-- BMCR_FULLDPLX | BMCR_SPEED1000);
--
-- phy_write(dtsec->tbiphy, MII_BMCR, tmp_reg16);
-- }
-+ tbicon = TBICON_CLK_SELECT;
-+ mdiodev_write(dtsec->tbidev, MII_TBICON, tbicon);
-
- /* Max Frame Length */
- max_frm_ln = (u16)ioread32be(®s->maxfrm);
-@@ -1406,6 +1335,8 @@ static int dtsec_free(struct fman_mac *d
-
- kfree(dtsec->dtsec_drv_param);
- dtsec->dtsec_drv_param = NULL;
-+ if (!IS_ERR_OR_NULL(dtsec->tbidev))
-+ put_device(&dtsec->tbidev->dev);
- kfree(dtsec);
-
- return 0;
-@@ -1434,7 +1365,6 @@ static struct fman_mac *dtsec_config(str
-
- dtsec->regs = mac_dev->vaddr;
- dtsec->addr = ENET_ADDR_TO_UINT64(mac_dev->addr);
-- dtsec->max_speed = params->max_speed;
- dtsec->phy_if = mac_dev->phy_if;
- dtsec->mac_id = params->mac_id;
- dtsec->exceptions = (DTSEC_IMASK_BREN |
-@@ -1457,7 +1387,6 @@ static struct fman_mac *dtsec_config(str
- dtsec->en_tsu_err_exception = dtsec->dtsec_drv_param->ptp_exception_en;
-
- dtsec->fm = params->fm;
-- dtsec->basex_if = params->basex_if;
-
- /* Save FMan revision */
- fman_get_revision(dtsec->fm, &dtsec->fm_rev_info);
-@@ -1476,18 +1405,18 @@ int dtsec_initialization(struct mac_devi
- int err;
- struct fman_mac *dtsec;
- struct device_node *phy_node;
-+ unsigned long capabilities;
-+ unsigned long *supported;
-
-+ mac_dev->phylink_ops = &dtsec_mac_ops;
- mac_dev->set_promisc = dtsec_set_promiscuous;
- mac_dev->change_addr = dtsec_modify_mac_address;
- mac_dev->add_hash_mac_addr = dtsec_add_hash_mac_address;
- mac_dev->remove_hash_mac_addr = dtsec_del_hash_mac_address;
-- mac_dev->set_tx_pause = dtsec_set_tx_pause_frames;
-- mac_dev->set_rx_pause = dtsec_accept_rx_pause_frames;
- mac_dev->set_exception = dtsec_set_exception;
- mac_dev->set_allmulti = dtsec_set_allmulti;
- mac_dev->set_tstamp = dtsec_set_tstamp;
- mac_dev->set_multi = fman_set_multi;
-- mac_dev->adjust_link = adjust_link_dtsec;
- mac_dev->enable = dtsec_enable;
- mac_dev->disable = dtsec_disable;
-
-@@ -1502,19 +1431,56 @@ int dtsec_initialization(struct mac_devi
- dtsec->dtsec_drv_param->tx_pad_crc = true;
-
- phy_node = of_parse_phandle(mac_node, "tbi-handle", 0);
-- if (!phy_node) {
-- pr_err("TBI PHY node is not available\n");
-+ if (!phy_node || of_device_is_available(phy_node)) {
-+ of_node_put(phy_node);
- err = -EINVAL;
-+ dev_err_probe(mac_dev->dev, err,
-+ "TBI PCS node is not available\n");
- goto _return_fm_mac_free;
- }
-
-- dtsec->tbiphy = of_phy_find_device(phy_node);
-- if (!dtsec->tbiphy) {
-- pr_err("of_phy_find_device (TBI PHY) failed\n");
-- err = -EINVAL;
-+ dtsec->tbidev = of_mdio_find_device(phy_node);
-+ of_node_put(phy_node);
-+ if (!dtsec->tbidev) {
-+ err = -EPROBE_DEFER;
-+ dev_err_probe(mac_dev->dev, err,
-+ "could not find mdiodev for PCS\n");
- goto _return_fm_mac_free;
- }
-- put_device(&dtsec->tbiphy->mdio.dev);
-+ dtsec->pcs.ops = &dtsec_pcs_ops;
-+ dtsec->pcs.poll = true;
-+
-+ supported = mac_dev->phylink_config.supported_interfaces;
-+
-+ /* FIXME: Can we use DTSEC_ID2_INT_FULL_OFF to determine if these are
-+ * supported? If not, we can determine support via the phy if SerDes
-+ * support is added.
-+ */
-+ if (mac_dev->phy_if == PHY_INTERFACE_MODE_SGMII ||
-+ mac_dev->phy_if == PHY_INTERFACE_MODE_1000BASEX) {
-+ __set_bit(PHY_INTERFACE_MODE_SGMII, supported);
-+ __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported);
-+ } else if (mac_dev->phy_if == PHY_INTERFACE_MODE_2500BASEX) {
-+ __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported);
-+ }
-+
-+ if (!(ioread32be(&dtsec->regs->tsec_id2) & DTSEC_ID2_INT_REDUCED_OFF)) {
-+ phy_interface_set_rgmii(supported);
-+
-+ /* DTSEC_ID2_INT_REDUCED_OFF indicates that the dTSEC supports
-+ * RMII and RGMII. However, the only SoCs which support RMII
-+ * are the P1017 and P1023. Avoid advertising this mode on
-+ * other SoCs. This is a bit of a moot point, since there's no
-+ * in-tree support for ethernet on these platforms...
-+ */
-+ if (of_machine_is_compatible("fsl,P1023") ||
-+ of_machine_is_compatible("fsl,P1023RDB"))
-+ __set_bit(PHY_INTERFACE_MODE_RMII, supported);
-+ }
-+
-+ capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE;
-+ capabilities |= MAC_10 | MAC_100 | MAC_1000FD | MAC_2500FD;
-+ mac_dev->phylink_config.mac_capabilities = capabilities;
-
- err = dtsec_init(dtsec);
- if (err < 0)
---- a/drivers/net/ethernet/freescale/fman/fman_mac.h
-+++ b/drivers/net/ethernet/freescale/fman/fman_mac.h
-@@ -170,20 +170,10 @@ struct fman_mac_params {
- * 0 - FM_MAX_NUM_OF_10G_MACS
- */
- u8 mac_id;
-- /* Note that the speed should indicate the maximum rate that
-- * this MAC should support rather than the actual speed;
-- */
-- u16 max_speed;
- /* A handle to the FM object this port related to */
- void *fm;
- fman_mac_exception_cb *event_cb; /* MDIO Events Callback Routine */
- fman_mac_exception_cb *exception_cb;/* Exception Callback Routine */
-- /* SGMII/QSGII interface with 1000BaseX auto-negotiation between MAC
-- * and phy or backplane; Note: 1000BaseX auto-negotiation relates only
-- * to interface between MAC and phy/backplane, SGMII phy can still
-- * synchronize with far-end phy at 10Mbps, 100Mbps or 1000Mbps
-- */
-- bool basex_if;
- };
-
- struct eth_hash_t {
---- a/drivers/net/ethernet/freescale/fman/fman_memac.c
-+++ b/drivers/net/ethernet/freescale/fman/fman_memac.c
-@@ -278,9 +278,6 @@ struct fman_mac {
- struct memac_regs __iomem *regs;
- /* MAC address of device */
- u64 addr;
-- /* Ethernet physical interface */
-- phy_interface_t phy_if;
-- u16 max_speed;
- struct mac_device *dev_id; /* device cookie used by the exception cbs */
- fman_mac_exception_cb *exception_cb;
- fman_mac_exception_cb *event_cb;
-@@ -293,12 +290,12 @@ struct fman_mac {
- struct memac_cfg *memac_drv_param;
- void *fm;
- struct fman_rev_info fm_rev_info;
-- bool basex_if;
- struct phy *serdes;
- struct phylink_pcs *sgmii_pcs;
- struct phylink_pcs *qsgmii_pcs;
- struct phylink_pcs *xfi_pcs;
- bool allmulti_enabled;
-+ bool rgmii_no_half_duplex;
- };
-
- static void add_addr_in_paddr(struct memac_regs __iomem *regs, const u8 *adr,
-@@ -356,7 +353,6 @@ static void set_exception(struct memac_r
- }
-
- static int init(struct memac_regs __iomem *regs, struct memac_cfg *cfg,
-- phy_interface_t phy_if, u16 speed, bool slow_10g_if,
- u32 exceptions)
- {
- u32 tmp;
-@@ -384,41 +380,6 @@ static int init(struct memac_regs __iome
- iowrite32be((u32)cfg->pause_quanta, ®s->pause_quanta[0]);
- iowrite32be((u32)0, ®s->pause_thresh[0]);
-
-- /* IF_MODE */
-- tmp = 0;
-- switch (phy_if) {
-- case PHY_INTERFACE_MODE_XGMII:
-- tmp |= IF_MODE_10G;
-- break;
-- case PHY_INTERFACE_MODE_MII:
-- tmp |= IF_MODE_MII;
-- break;
-- default:
-- tmp |= IF_MODE_GMII;
-- if (phy_if == PHY_INTERFACE_MODE_RGMII ||
-- phy_if == PHY_INTERFACE_MODE_RGMII_ID ||
-- phy_if == PHY_INTERFACE_MODE_RGMII_RXID ||
-- phy_if == PHY_INTERFACE_MODE_RGMII_TXID)
-- tmp |= IF_MODE_RGMII | IF_MODE_RGMII_AUTO;
-- }
-- iowrite32be(tmp, ®s->if_mode);
--
-- /* TX_FIFO_SECTIONS */
-- tmp = 0;
-- if (phy_if == PHY_INTERFACE_MODE_XGMII) {
-- if (slow_10g_if) {
-- tmp |= (TX_FIFO_SECTIONS_TX_AVAIL_SLOW_10G |
-- TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G);
-- } else {
-- tmp |= (TX_FIFO_SECTIONS_TX_AVAIL_10G |
-- TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G);
-- }
-- } else {
-- tmp |= (TX_FIFO_SECTIONS_TX_AVAIL_1G |
-- TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_1G);
-- }
-- iowrite32be(tmp, ®s->tx_fifo_sections);
--
- /* clear all pending events and set-up interrupts */
- iowrite32be(0xffffffff, ®s->ievent);
- set_exception(regs, exceptions, true);
-@@ -458,24 +419,6 @@ static u32 get_mac_addr_hash_code(u64 et
- return xor_val;
- }
-
--static void setup_sgmii_internal(struct fman_mac *memac,
-- struct phylink_pcs *pcs,
-- struct fixed_phy_status *fixed_link)
--{
-- __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
-- phy_interface_t iface = memac->basex_if ? PHY_INTERFACE_MODE_1000BASEX :
-- PHY_INTERFACE_MODE_SGMII;
-- unsigned int mode = fixed_link ? MLO_AN_FIXED : MLO_AN_INBAND;
--
-- linkmode_set_pause(advertising, true, true);
-- pcs->ops->pcs_config(pcs, mode, iface, advertising, true);
-- if (fixed_link)
-- pcs->ops->pcs_link_up(pcs, mode, iface, fixed_link->speed,
-- fixed_link->duplex);
-- else
-- pcs->ops->pcs_an_restart(pcs);
--}
--
- static int check_init_parameters(struct fman_mac *memac)
- {
- if (!memac->exception_cb) {
-@@ -581,41 +524,31 @@ static void free_init_resources(struct f
- memac->unicast_addr_hash = NULL;
- }
-
--static bool is_init_done(struct memac_cfg *memac_drv_params)
--{
-- /* Checks if mEMAC driver parameters were initialized */
-- if (!memac_drv_params)
-- return true;
--
-- return false;
--}
--
- static int memac_enable(struct fman_mac *memac)
- {
-- struct memac_regs __iomem *regs = memac->regs;
-- u32 tmp;
-+ int ret;
-
-- if (!is_init_done(memac->memac_drv_param))
-- return -EINVAL;
-+ ret = phy_init(memac->serdes);
-+ if (ret) {
-+ dev_err(memac->dev_id->dev,
-+ "could not initialize serdes: %pe\n", ERR_PTR(ret));
-+ return ret;
-+ }
-
-- tmp = ioread32be(®s->command_config);
-- tmp |= CMD_CFG_RX_EN | CMD_CFG_TX_EN;
-- iowrite32be(tmp, ®s->command_config);
-+ ret = phy_power_on(memac->serdes);
-+ if (ret) {
-+ dev_err(memac->dev_id->dev,
-+ "could not power on serdes: %pe\n", ERR_PTR(ret));
-+ phy_exit(memac->serdes);
-+ }
-
-- return 0;
-+ return ret;
- }
-
- static void memac_disable(struct fman_mac *memac)
--
- {
-- struct memac_regs __iomem *regs = memac->regs;
-- u32 tmp;
--
-- WARN_ON_ONCE(!is_init_done(memac->memac_drv_param));
--
-- tmp = ioread32be(®s->command_config);
-- tmp &= ~(CMD_CFG_RX_EN | CMD_CFG_TX_EN);
-- iowrite32be(tmp, ®s->command_config);
-+ phy_power_off(memac->serdes);
-+ phy_exit(memac->serdes);
- }
-
- static int memac_set_promiscuous(struct fman_mac *memac, bool new_val)
-@@ -623,9 +556,6 @@ static int memac_set_promiscuous(struct
- struct memac_regs __iomem *regs = memac->regs;
- u32 tmp;
-
-- if (!is_init_done(memac->memac_drv_param))
-- return -EINVAL;
--
- tmp = ioread32be(®s->command_config);
- if (new_val)
- tmp |= CMD_CFG_PROMIS_EN;
-@@ -637,73 +567,12 @@ static int memac_set_promiscuous(struct
- return 0;
- }
-
--static int memac_adjust_link(struct fman_mac *memac, u16 speed)
--{
-- struct memac_regs __iomem *regs = memac->regs;
-- u32 tmp;
--
-- if (!is_init_done(memac->memac_drv_param))
-- return -EINVAL;
--
-- tmp = ioread32be(®s->if_mode);
--
-- /* Set full duplex */
-- tmp &= ~IF_MODE_HD;
--
-- if (phy_interface_mode_is_rgmii(memac->phy_if)) {
-- /* Configure RGMII in manual mode */
-- tmp &= ~IF_MODE_RGMII_AUTO;
-- tmp &= ~IF_MODE_RGMII_SP_MASK;
-- /* Full duplex */
-- tmp |= IF_MODE_RGMII_FD;
--
-- switch (speed) {
-- case SPEED_1000:
-- tmp |= IF_MODE_RGMII_1000;
-- break;
-- case SPEED_100:
-- tmp |= IF_MODE_RGMII_100;
-- break;
-- case SPEED_10:
-- tmp |= IF_MODE_RGMII_10;
-- break;
-- default:
-- break;
-- }
-- }
--
-- iowrite32be(tmp, ®s->if_mode);
--
-- return 0;
--}
--
--static void adjust_link_memac(struct mac_device *mac_dev)
--{
-- struct phy_device *phy_dev = mac_dev->phy_dev;
-- struct fman_mac *fman_mac;
-- bool rx_pause, tx_pause;
-- int err;
--
-- fman_mac = mac_dev->fman_mac;
-- memac_adjust_link(fman_mac, phy_dev->speed);
-- mac_dev->update_speed(mac_dev, phy_dev->speed);
--
-- fman_get_pause_cfg(mac_dev, &rx_pause, &tx_pause);
-- err = fman_set_mac_active_pause(mac_dev, rx_pause, tx_pause);
-- if (err < 0)
-- dev_err(mac_dev->dev, "fman_set_mac_active_pause() = %d\n",
-- err);
--}
--
- static int memac_set_tx_pause_frames(struct fman_mac *memac, u8 priority,
- u16 pause_time, u16 thresh_time)
- {
- struct memac_regs __iomem *regs = memac->regs;
- u32 tmp;
-
-- if (!is_init_done(memac->memac_drv_param))
-- return -EINVAL;
--
- tmp = ioread32be(®s->tx_fifo_sections);
-
- GET_TX_EMPTY_DEFAULT_VALUE(tmp);
-@@ -738,9 +607,6 @@ static int memac_accept_rx_pause_frames(
- struct memac_regs __iomem *regs = memac->regs;
- u32 tmp;
-
-- if (!is_init_done(memac->memac_drv_param))
-- return -EINVAL;
--
- tmp = ioread32be(®s->command_config);
- if (en)
- tmp &= ~CMD_CFG_PAUSE_IGNORE;
-@@ -752,12 +618,175 @@ static int memac_accept_rx_pause_frames(
- return 0;
- }
-
-+static void memac_validate(struct phylink_config *config,
-+ unsigned long *supported,
-+ struct phylink_link_state *state)
-+{
-+ struct fman_mac *memac = fman_config_to_mac(config)->fman_mac;
-+ unsigned long caps = config->mac_capabilities;
-+
-+ if (phy_interface_mode_is_rgmii(state->interface) &&
-+ memac->rgmii_no_half_duplex)
-+ caps &= ~(MAC_10HD | MAC_100HD);
-+
-+ phylink_validate_mask_caps(supported, state, caps);
-+}
-+
-+/**
-+ * memac_if_mode() - Convert an interface mode into an IF_MODE config
-+ * @interface: A phy interface mode
-+ *
-+ * Return: A configuration word, suitable for programming into the lower bits
-+ * of %IF_MODE.
-+ */
-+static u32 memac_if_mode(phy_interface_t interface)
-+{
-+ switch (interface) {
-+ case PHY_INTERFACE_MODE_MII:
-+ return IF_MODE_MII;
-+ case PHY_INTERFACE_MODE_RGMII:
-+ case PHY_INTERFACE_MODE_RGMII_ID:
-+ case PHY_INTERFACE_MODE_RGMII_RXID:
-+ case PHY_INTERFACE_MODE_RGMII_TXID:
-+ return IF_MODE_GMII | IF_MODE_RGMII;
-+ case PHY_INTERFACE_MODE_SGMII:
-+ case PHY_INTERFACE_MODE_1000BASEX:
-+ case PHY_INTERFACE_MODE_QSGMII:
-+ return IF_MODE_GMII;
-+ case PHY_INTERFACE_MODE_10GBASER:
-+ return IF_MODE_10G;
-+ default:
-+ WARN_ON_ONCE(1);
-+ return 0;
-+ }
-+}
-+
-+static struct phylink_pcs *memac_select_pcs(struct phylink_config *config,
-+ phy_interface_t iface)
-+{
-+ struct fman_mac *memac = fman_config_to_mac(config)->fman_mac;
-+
-+ switch (iface) {
-+ case PHY_INTERFACE_MODE_SGMII:
-+ case PHY_INTERFACE_MODE_1000BASEX:
-+ return memac->sgmii_pcs;
-+ case PHY_INTERFACE_MODE_QSGMII:
-+ return memac->qsgmii_pcs;
-+ case PHY_INTERFACE_MODE_10GBASER:
-+ return memac->xfi_pcs;
-+ default:
-+ return NULL;
-+ }
-+}
-+
-+static int memac_prepare(struct phylink_config *config, unsigned int mode,
-+ phy_interface_t iface)
-+{
-+ struct fman_mac *memac = fman_config_to_mac(config)->fman_mac;
-+
-+ switch (iface) {
-+ case PHY_INTERFACE_MODE_SGMII:
-+ case PHY_INTERFACE_MODE_1000BASEX:
-+ case PHY_INTERFACE_MODE_QSGMII:
-+ case PHY_INTERFACE_MODE_10GBASER:
-+ return phy_set_mode_ext(memac->serdes, PHY_MODE_ETHERNET,
-+ iface);
-+ default:
-+ return 0;
-+ }
-+}
-+
-+static void memac_mac_config(struct phylink_config *config, unsigned int mode,
-+ const struct phylink_link_state *state)
-+{
-+ struct mac_device *mac_dev = fman_config_to_mac(config);
-+ struct memac_regs __iomem *regs = mac_dev->fman_mac->regs;
-+ u32 tmp = ioread32be(®s->if_mode);
-+
-+ tmp &= ~(IF_MODE_MASK | IF_MODE_RGMII);
-+ tmp |= memac_if_mode(state->interface);
-+ if (phylink_autoneg_inband(mode))
-+ tmp |= IF_MODE_RGMII_AUTO;
-+ iowrite32be(tmp, ®s->if_mode);
-+}
-+
-+static void memac_link_up(struct phylink_config *config, struct phy_device *phy,
-+ unsigned int mode, phy_interface_t interface,
-+ int speed, int duplex, bool tx_pause, bool rx_pause)
-+{
-+ struct mac_device *mac_dev = fman_config_to_mac(config);
-+ struct fman_mac *memac = mac_dev->fman_mac;
-+ struct memac_regs __iomem *regs = memac->regs;
-+ u32 tmp = memac_if_mode(interface);
-+ u16 pause_time = tx_pause ? FSL_FM_PAUSE_TIME_ENABLE :
-+ FSL_FM_PAUSE_TIME_DISABLE;
-+
-+ memac_set_tx_pause_frames(memac, 0, pause_time, 0);
-+ memac_accept_rx_pause_frames(memac, rx_pause);
-+
-+ if (duplex == DUPLEX_HALF)
-+ tmp |= IF_MODE_HD;
-+
-+ switch (speed) {
-+ case SPEED_1000:
-+ tmp |= IF_MODE_RGMII_1000;
-+ break;
-+ case SPEED_100:
-+ tmp |= IF_MODE_RGMII_100;
-+ break;
-+ case SPEED_10:
-+ tmp |= IF_MODE_RGMII_10;
-+ break;
-+ }
-+ iowrite32be(tmp, ®s->if_mode);
-+
-+ /* TODO: EEE? */
-+
-+ if (speed == SPEED_10000) {
-+ if (memac->fm_rev_info.major == 6 &&
-+ memac->fm_rev_info.minor == 4)
-+ tmp = TX_FIFO_SECTIONS_TX_AVAIL_SLOW_10G;
-+ else
-+ tmp = TX_FIFO_SECTIONS_TX_AVAIL_10G;
-+ tmp |= TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G;
-+ } else {
-+ tmp = TX_FIFO_SECTIONS_TX_AVAIL_1G |
-+ TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_1G;
-+ }
-+ iowrite32be(tmp, ®s->tx_fifo_sections);
-+
-+ mac_dev->update_speed(mac_dev, speed);
-+
-+ tmp = ioread32be(®s->command_config);
-+ tmp |= CMD_CFG_RX_EN | CMD_CFG_TX_EN;
-+ iowrite32be(tmp, ®s->command_config);
-+}
-+
-+static void memac_link_down(struct phylink_config *config, unsigned int mode,
-+ phy_interface_t interface)
-+{
-+ struct fman_mac *memac = fman_config_to_mac(config)->fman_mac;
-+ struct memac_regs __iomem *regs = memac->regs;
-+ u32 tmp;
-+
-+ /* TODO: graceful */
-+ tmp = ioread32be(®s->command_config);
-+ tmp &= ~(CMD_CFG_RX_EN | CMD_CFG_TX_EN);
-+ iowrite32be(tmp, ®s->command_config);
-+}
-+
-+static const struct phylink_mac_ops memac_mac_ops = {
-+ .validate = memac_validate,
-+ .mac_select_pcs = memac_select_pcs,
-+ .mac_prepare = memac_prepare,
-+ .mac_config = memac_mac_config,
-+ .mac_link_up = memac_link_up,
-+ .mac_link_down = memac_link_down,
-+};
-+
- static int memac_modify_mac_address(struct fman_mac *memac,
- const enet_addr_t *enet_addr)
- {
-- if (!is_init_done(memac->memac_drv_param))
-- return -EINVAL;
--
- add_addr_in_paddr(memac->regs, (const u8 *)(*enet_addr), 0);
-
- return 0;
-@@ -771,9 +800,6 @@ static int memac_add_hash_mac_address(st
- u32 hash;
- u64 addr;
-
-- if (!is_init_done(memac->memac_drv_param))
-- return -EINVAL;
--
- addr = ENET_ADDR_TO_UINT64(*eth_addr);
-
- if (!(addr & GROUP_ADDRESS)) {
-@@ -802,9 +828,6 @@ static int memac_set_allmulti(struct fma
- u32 entry;
- struct memac_regs __iomem *regs = memac->regs;
-
-- if (!is_init_done(memac->memac_drv_param))
-- return -EINVAL;
--
- if (enable) {
- for (entry = 0; entry < HASH_TABLE_SIZE; entry++)
- iowrite32be(entry | HASH_CTRL_MCAST_EN,
-@@ -834,9 +857,6 @@ static int memac_del_hash_mac_address(st
- u32 hash;
- u64 addr;
-
-- if (!is_init_done(memac->memac_drv_param))
-- return -EINVAL;
--
- addr = ENET_ADDR_TO_UINT64(*eth_addr);
-
- hash = get_mac_addr_hash_code(addr) & HASH_CTRL_ADDR_MASK;
-@@ -864,9 +884,6 @@ static int memac_set_exception(struct fm
- {
- u32 bit_mask = 0;
-
-- if (!is_init_done(memac->memac_drv_param))
-- return -EINVAL;
--
- bit_mask = get_exception_flag(exception);
- if (bit_mask) {
- if (enable)
-@@ -886,23 +903,15 @@ static int memac_init(struct fman_mac *m
- {
- struct memac_cfg *memac_drv_param;
- enet_addr_t eth_addr;
-- bool slow_10g_if = false;
-- struct fixed_phy_status *fixed_link = NULL;
- int err;
- u32 reg32 = 0;
-
-- if (is_init_done(memac->memac_drv_param))
-- return -EINVAL;
--
- err = check_init_parameters(memac);
- if (err)
- return err;
-
- memac_drv_param = memac->memac_drv_param;
-
-- if (memac->fm_rev_info.major == 6 && memac->fm_rev_info.minor == 4)
-- slow_10g_if = true;
--
- /* First, reset the MAC if desired. */
- if (memac_drv_param->reset_on_init) {
- err = reset(memac->regs);
-@@ -918,10 +927,7 @@ static int memac_init(struct fman_mac *m
- add_addr_in_paddr(memac->regs, (const u8 *)eth_addr, 0);
- }
-
-- fixed_link = memac_drv_param->fixed_link;
--
-- init(memac->regs, memac->memac_drv_param, memac->phy_if,
-- memac->max_speed, slow_10g_if, memac->exceptions);
-+ init(memac->regs, memac->memac_drv_param, memac->exceptions);
-
- /* FM_RX_FIFO_CORRUPT_ERRATA_10GMAC_A006320 errata workaround
- * Exists only in FMan 6.0 and 6.3.
-@@ -937,11 +943,6 @@ static int memac_init(struct fman_mac *m
- iowrite32be(reg32, &memac->regs->command_config);
- }
-
-- if (memac->phy_if == PHY_INTERFACE_MODE_SGMII)
-- setup_sgmii_internal(memac, memac->sgmii_pcs, fixed_link);
-- else if (memac->phy_if == PHY_INTERFACE_MODE_QSGMII)
-- setup_sgmii_internal(memac, memac->qsgmii_pcs, fixed_link);
--
- /* Max Frame Length */
- err = fman_set_mac_max_frame(memac->fm, memac->mac_id,
- memac_drv_param->max_frame_length);
-@@ -970,9 +971,6 @@ static int memac_init(struct fman_mac *m
- fman_register_intr(memac->fm, FMAN_MOD_MAC, memac->mac_id,
- FMAN_INTR_TYPE_NORMAL, memac_exception, memac);
-
-- kfree(memac_drv_param);
-- memac->memac_drv_param = NULL;
--
- return 0;
- }
-
-@@ -995,7 +993,6 @@ static int memac_free(struct fman_mac *m
- pcs_put(memac->sgmii_pcs);
- pcs_put(memac->qsgmii_pcs);
- pcs_put(memac->xfi_pcs);
--
- kfree(memac->memac_drv_param);
- kfree(memac);
-
-@@ -1028,8 +1025,6 @@ static struct fman_mac *memac_config(str
- memac->addr = ENET_ADDR_TO_UINT64(mac_dev->addr);
-
- memac->regs = mac_dev->vaddr;
-- memac->max_speed = params->max_speed;
-- memac->phy_if = mac_dev->phy_if;
- memac->mac_id = params->mac_id;
- memac->exceptions = (MEMAC_IMASK_TSECC_ER | MEMAC_IMASK_TECC_ER |
- MEMAC_IMASK_RECC_ER | MEMAC_IMASK_MGI);
-@@ -1037,7 +1032,6 @@ static struct fman_mac *memac_config(str
- memac->event_cb = params->event_cb;
- memac->dev_id = mac_dev;
- memac->fm = params->fm;
-- memac->basex_if = params->basex_if;
-
- /* Save FMan revision */
- fman_get_revision(memac->fm, &memac->fm_rev_info);
-@@ -1064,37 +1058,44 @@ static struct phylink_pcs *memac_pcs_cre
- return pcs;
- }
-
-+static bool memac_supports(struct mac_device *mac_dev, phy_interface_t iface)
-+{
-+ /* If there's no serdes device, assume that it's been configured for
-+ * whatever the default interface mode is.
-+ */
-+ if (!mac_dev->fman_mac->serdes)
-+ return mac_dev->phy_if == iface;
-+ /* Otherwise, ask the serdes */
-+ return !phy_validate(mac_dev->fman_mac->serdes, PHY_MODE_ETHERNET,
-+ iface, NULL);
-+}
-+
- int memac_initialization(struct mac_device *mac_dev,
- struct device_node *mac_node,
- struct fman_mac_params *params)
- {
- int err;
-+ struct device_node *fixed;
- struct phylink_pcs *pcs;
-- struct fixed_phy_status *fixed_link;
- struct fman_mac *memac;
-+ unsigned long capabilities;
-+ unsigned long *supported;
-
-+ mac_dev->phylink_ops = &memac_mac_ops;
- mac_dev->set_promisc = memac_set_promiscuous;
- mac_dev->change_addr = memac_modify_mac_address;
- mac_dev->add_hash_mac_addr = memac_add_hash_mac_address;
- mac_dev->remove_hash_mac_addr = memac_del_hash_mac_address;
-- mac_dev->set_tx_pause = memac_set_tx_pause_frames;
-- mac_dev->set_rx_pause = memac_accept_rx_pause_frames;
- mac_dev->set_exception = memac_set_exception;
- mac_dev->set_allmulti = memac_set_allmulti;
- mac_dev->set_tstamp = memac_set_tstamp;
- mac_dev->set_multi = fman_set_multi;
-- mac_dev->adjust_link = adjust_link_memac;
- mac_dev->enable = memac_enable;
- mac_dev->disable = memac_disable;
-
-- if (params->max_speed == SPEED_10000)
-- mac_dev->phy_if = PHY_INTERFACE_MODE_XGMII;
--
- mac_dev->fman_mac = memac_config(mac_dev, params);
-- if (!mac_dev->fman_mac) {
-- err = -EINVAL;
-- goto _return;
-- }
-+ if (!mac_dev->fman_mac)
-+ return -EINVAL;
-
- memac = mac_dev->fman_mac;
- memac->memac_drv_param->max_frame_length = fman_get_max_frm();
-@@ -1136,9 +1137,9 @@ int memac_initialization(struct mac_devi
- else
- pcs = memac_pcs_create(mac_node, err);
-
-- if (!pcs) {
-- dev_err(mac_dev->dev, "missing pcs\n");
-- err = -ENOENT;
-+ if (IS_ERR(pcs)) {
-+ err = PTR_ERR(pcs);
-+ dev_err_probe(mac_dev->dev, err, "missing pcs\n");
- goto _return_fm_mac_free;
- }
-
-@@ -1159,84 +1160,100 @@ int memac_initialization(struct mac_devi
- } else if (IS_ERR(memac->serdes)) {
- dev_err_probe(mac_dev->dev, err, "could not get serdes\n");
- goto _return_fm_mac_free;
-- } else {
-- err = phy_init(memac->serdes);
-- if (err) {
-- dev_err_probe(mac_dev->dev, err,
-- "could not initialize serdes\n");
-- goto _return_fm_mac_free;
-- }
--
-- err = phy_power_on(memac->serdes);
-- if (err) {
-- dev_err_probe(mac_dev->dev, err,
-- "could not power on serdes\n");
-- goto _return_phy_exit;
-- }
--
-- if (memac->phy_if == PHY_INTERFACE_MODE_SGMII ||
-- memac->phy_if == PHY_INTERFACE_MODE_1000BASEX ||
-- memac->phy_if == PHY_INTERFACE_MODE_2500BASEX ||
-- memac->phy_if == PHY_INTERFACE_MODE_QSGMII ||
-- memac->phy_if == PHY_INTERFACE_MODE_XGMII) {
-- err = phy_set_mode_ext(memac->serdes, PHY_MODE_ETHERNET,
-- memac->phy_if);
-- if (err) {
-- dev_err_probe(mac_dev->dev, err,
-- "could not set serdes mode to %s\n",
-- phy_modes(memac->phy_if));
-- goto _return_phy_power_off;
-- }
-- }
- }
-
-- if (!mac_dev->phy_node && of_phy_is_fixed_link(mac_node)) {
-- struct phy_device *phy;
--
-- err = of_phy_register_fixed_link(mac_node);
-- if (err)
-- goto _return_phy_power_off;
--
-- fixed_link = kzalloc(sizeof(*fixed_link), GFP_KERNEL);
-- if (!fixed_link) {
-- err = -ENOMEM;
-- goto _return_phy_power_off;
-- }
-+ /* The internal connection to the serdes is XGMII, but this isn't
-+ * really correct for the phy mode (which is the external connection).
-+ * However, this is how all older device trees say that they want
-+ * 10GBASE-R (aka XFI), so just convert it for them.
-+ */
-+ if (mac_dev->phy_if == PHY_INTERFACE_MODE_XGMII)
-+ mac_dev->phy_if = PHY_INTERFACE_MODE_10GBASER;
-
-- mac_dev->phy_node = of_node_get(mac_node);
-- phy = of_phy_find_device(mac_dev->phy_node);
-- if (!phy) {
-- err = -EINVAL;
-- of_node_put(mac_dev->phy_node);
-- goto _return_fixed_link_free;
-- }
-+ /* TODO: The following interface modes are supported by (some) hardware
-+ * but not by this driver:
-+ * - 1000BASE-KX
-+ * - 10GBASE-KR
-+ * - XAUI/HiGig
-+ */
-+ supported = mac_dev->phylink_config.supported_interfaces;
-
-- fixed_link->link = phy->link;
-- fixed_link->speed = phy->speed;
-- fixed_link->duplex = phy->duplex;
-- fixed_link->pause = phy->pause;
-- fixed_link->asym_pause = phy->asym_pause;
-+ /* Note that half duplex is only supported on 10/100M interfaces. */
-
-- put_device(&phy->mdio.dev);
-- memac->memac_drv_param->fixed_link = fixed_link;
-+ if (memac->sgmii_pcs &&
-+ (memac_supports(mac_dev, PHY_INTERFACE_MODE_SGMII) ||
-+ memac_supports(mac_dev, PHY_INTERFACE_MODE_1000BASEX))) {
-+ __set_bit(PHY_INTERFACE_MODE_SGMII, supported);
-+ __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported);
-+ }
-+
-+ if (memac->sgmii_pcs &&
-+ memac_supports(mac_dev, PHY_INTERFACE_MODE_2500BASEX))
-+ __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported);
-+
-+ if (memac->qsgmii_pcs &&
-+ memac_supports(mac_dev, PHY_INTERFACE_MODE_QSGMII))
-+ __set_bit(PHY_INTERFACE_MODE_QSGMII, supported);
-+ else if (mac_dev->phy_if == PHY_INTERFACE_MODE_QSGMII)
-+ dev_warn(mac_dev->dev, "no QSGMII pcs specified\n");
-+
-+ if (memac->xfi_pcs &&
-+ memac_supports(mac_dev, PHY_INTERFACE_MODE_10GBASER)) {
-+ __set_bit(PHY_INTERFACE_MODE_10GBASER, supported);
-+ } else {
-+ /* From what I can tell, no 10g macs support RGMII. */
-+ phy_interface_set_rgmii(supported);
-+ __set_bit(PHY_INTERFACE_MODE_MII, supported);
- }
-
-+ capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE | MAC_10 | MAC_100;
-+ capabilities |= MAC_1000FD | MAC_2500FD | MAC_10000FD;
-+
-+ /* These SoCs don't support half duplex at all; there's no different
-+ * FMan version or compatible, so we just have to check the machine
-+ * compatible instead
-+ */
-+ if (of_machine_is_compatible("fsl,ls1043a") ||
-+ of_machine_is_compatible("fsl,ls1046a") ||
-+ of_machine_is_compatible("fsl,B4QDS"))
-+ capabilities &= ~(MAC_10HD | MAC_100HD);
-+
-+ mac_dev->phylink_config.mac_capabilities = capabilities;
-+
-+ /* The T2080 and T4240 don't support half duplex RGMII. There is no
-+ * other way to identify these SoCs, so just use the machine
-+ * compatible.
-+ */
-+ if (of_machine_is_compatible("fsl,T2080QDS") ||
-+ of_machine_is_compatible("fsl,T2080RDB") ||
-+ of_machine_is_compatible("fsl,T2081QDS") ||
-+ of_machine_is_compatible("fsl,T4240QDS") ||
-+ of_machine_is_compatible("fsl,T4240RDB"))
-+ memac->rgmii_no_half_duplex = true;
-+
-+ /* Most boards should use MLO_AN_INBAND, but existing boards don't have
-+ * a managed property. Default to MLO_AN_INBAND if nothing else is
-+ * specified. We need to be careful and not enable this if we have a
-+ * fixed link or if we are using MII or RGMII, since those
-+ * configurations modes don't use in-band autonegotiation.
-+ */
-+ fixed = of_get_child_by_name(mac_node, "fixed-link");
-+ if (!fixed && !of_property_read_bool(mac_node, "fixed-link") &&
-+ !of_property_read_bool(mac_node, "managed") &&
-+ mac_dev->phy_if != PHY_INTERFACE_MODE_MII &&
-+ !phy_interface_mode_is_rgmii(mac_dev->phy_if))
-+ mac_dev->phylink_config.ovr_an_inband = true;
-+ of_node_put(fixed);
-+
- err = memac_init(mac_dev->fman_mac);
- if (err < 0)
-- goto _return_fixed_link_free;
-+ goto _return_fm_mac_free;
-
- dev_info(mac_dev->dev, "FMan MEMAC\n");
-
-- goto _return;
-+ return 0;
-
--_return_phy_power_off:
-- phy_power_off(memac->serdes);
--_return_phy_exit:
-- phy_exit(memac->serdes);
--_return_fixed_link_free:
-- kfree(fixed_link);
- _return_fm_mac_free:
- memac_free(mac_dev->fman_mac);
--_return:
- return err;
- }
---- a/drivers/net/ethernet/freescale/fman/fman_tgec.c
-+++ b/drivers/net/ethernet/freescale/fman/fman_tgec.c
-@@ -13,6 +13,7 @@
- #include <linux/bitrev.h>
- #include <linux/io.h>
- #include <linux/crc32.h>
-+#include <linux/netdevice.h>
-
- /* Transmit Inter-Packet Gap Length Register (TX_IPG_LENGTH) */
- #define TGEC_TX_IPG_LENGTH_MASK 0x000003ff
-@@ -243,10 +244,6 @@ static int init(struct tgec_regs __iomem
-
- static int check_init_parameters(struct fman_mac *tgec)
- {
-- if (tgec->max_speed < SPEED_10000) {
-- pr_err("10G MAC driver only support 10G speed\n");
-- return -EINVAL;
-- }
- if (!tgec->exception_cb) {
- pr_err("uninitialized exception_cb\n");
- return -EINVAL;
-@@ -384,40 +381,13 @@ static void free_init_resources(struct f
- tgec->unicast_addr_hash = NULL;
- }
-
--static bool is_init_done(struct tgec_cfg *cfg)
--{
-- /* Checks if tGEC driver parameters were initialized */
-- if (!cfg)
-- return true;
--
-- return false;
--}
--
- static int tgec_enable(struct fman_mac *tgec)
- {
-- struct tgec_regs __iomem *regs = tgec->regs;
-- u32 tmp;
--
-- if (!is_init_done(tgec->cfg))
-- return -EINVAL;
--
-- tmp = ioread32be(®s->command_config);
-- tmp |= CMD_CFG_RX_EN | CMD_CFG_TX_EN;
-- iowrite32be(tmp, ®s->command_config);
--
- return 0;
- }
-
- static void tgec_disable(struct fman_mac *tgec)
- {
-- struct tgec_regs __iomem *regs = tgec->regs;
-- u32 tmp;
--
-- WARN_ON_ONCE(!is_init_done(tgec->cfg));
--
-- tmp = ioread32be(®s->command_config);
-- tmp &= ~(CMD_CFG_RX_EN | CMD_CFG_TX_EN);
-- iowrite32be(tmp, ®s->command_config);
- }
-
- static int tgec_set_promiscuous(struct fman_mac *tgec, bool new_val)
-@@ -425,9 +395,6 @@ static int tgec_set_promiscuous(struct f
- struct tgec_regs __iomem *regs = tgec->regs;
- u32 tmp;
-
-- if (!is_init_done(tgec->cfg))
-- return -EINVAL;
--
- tmp = ioread32be(®s->command_config);
- if (new_val)
- tmp |= CMD_CFG_PROMIS_EN;
-@@ -444,9 +411,6 @@ static int tgec_set_tx_pause_frames(stru
- {
- struct tgec_regs __iomem *regs = tgec->regs;
-
-- if (!is_init_done(tgec->cfg))
-- return -EINVAL;
--
- iowrite32be((u32)pause_time, ®s->pause_quant);
-
- return 0;
-@@ -457,9 +421,6 @@ static int tgec_accept_rx_pause_frames(s
- struct tgec_regs __iomem *regs = tgec->regs;
- u32 tmp;
-
-- if (!is_init_done(tgec->cfg))
-- return -EINVAL;
--
- tmp = ioread32be(®s->command_config);
- if (!en)
- tmp |= CMD_CFG_PAUSE_IGNORE;
-@@ -470,12 +431,53 @@ static int tgec_accept_rx_pause_frames(s
- return 0;
- }
-
-+static void tgec_mac_config(struct phylink_config *config, unsigned int mode,
-+ const struct phylink_link_state *state)
-+{
-+}
-+
-+static void tgec_link_up(struct phylink_config *config, struct phy_device *phy,
-+ unsigned int mode, phy_interface_t interface,
-+ int speed, int duplex, bool tx_pause, bool rx_pause)
-+{
-+ struct mac_device *mac_dev = fman_config_to_mac(config);
-+ struct fman_mac *tgec = mac_dev->fman_mac;
-+ struct tgec_regs __iomem *regs = tgec->regs;
-+ u16 pause_time = tx_pause ? FSL_FM_PAUSE_TIME_ENABLE :
-+ FSL_FM_PAUSE_TIME_DISABLE;
-+ u32 tmp;
-+
-+ tgec_set_tx_pause_frames(tgec, 0, pause_time, 0);
-+ tgec_accept_rx_pause_frames(tgec, rx_pause);
-+ mac_dev->update_speed(mac_dev, speed);
-+
-+ tmp = ioread32be(®s->command_config);
-+ tmp |= CMD_CFG_RX_EN | CMD_CFG_TX_EN;
-+ iowrite32be(tmp, ®s->command_config);
-+}
-+
-+static void tgec_link_down(struct phylink_config *config, unsigned int mode,
-+ phy_interface_t interface)
-+{
-+ struct fman_mac *tgec = fman_config_to_mac(config)->fman_mac;
-+ struct tgec_regs __iomem *regs = tgec->regs;
-+ u32 tmp;
-+
-+ tmp = ioread32be(®s->command_config);
-+ tmp &= ~(CMD_CFG_RX_EN | CMD_CFG_TX_EN);
-+ iowrite32be(tmp, ®s->command_config);
-+}
-+
-+static const struct phylink_mac_ops tgec_mac_ops = {
-+ .validate = phylink_generic_validate,
-+ .mac_config = tgec_mac_config,
-+ .mac_link_up = tgec_link_up,
-+ .mac_link_down = tgec_link_down,
-+};
-+
- static int tgec_modify_mac_address(struct fman_mac *tgec,
- const enet_addr_t *p_enet_addr)
- {
-- if (!is_init_done(tgec->cfg))
-- return -EINVAL;
--
- tgec->addr = ENET_ADDR_TO_UINT64(*p_enet_addr);
- set_mac_address(tgec->regs, (const u8 *)(*p_enet_addr));
-
-@@ -490,9 +492,6 @@ static int tgec_add_hash_mac_address(str
- u32 crc = 0xFFFFFFFF, hash;
- u64 addr;
-
-- if (!is_init_done(tgec->cfg))
-- return -EINVAL;
--
- addr = ENET_ADDR_TO_UINT64(*eth_addr);
-
- if (!(addr & GROUP_ADDRESS)) {
-@@ -525,9 +524,6 @@ static int tgec_set_allmulti(struct fman
- u32 entry;
- struct tgec_regs __iomem *regs = tgec->regs;
-
-- if (!is_init_done(tgec->cfg))
-- return -EINVAL;
--
- if (enable) {
- for (entry = 0; entry < TGEC_HASH_TABLE_SIZE; entry++)
- iowrite32be(entry | TGEC_HASH_MCAST_EN,
-@@ -548,9 +544,6 @@ static int tgec_set_tstamp(struct fman_m
- struct tgec_regs __iomem *regs = tgec->regs;
- u32 tmp;
-
-- if (!is_init_done(tgec->cfg))
-- return -EINVAL;
--
- tmp = ioread32be(®s->command_config);
-
- if (enable)
-@@ -572,9 +565,6 @@ static int tgec_del_hash_mac_address(str
- u32 crc = 0xFFFFFFFF, hash;
- u64 addr;
-
-- if (!is_init_done(tgec->cfg))
-- return -EINVAL;
--
- addr = ((*(u64 *)eth_addr) >> 16);
-
- /* CRC calculation */
-@@ -601,22 +591,12 @@ static int tgec_del_hash_mac_address(str
- return 0;
- }
-
--static void tgec_adjust_link(struct mac_device *mac_dev)
--{
-- struct phy_device *phy_dev = mac_dev->phy_dev;
--
-- mac_dev->update_speed(mac_dev, phy_dev->speed);
--}
--
- static int tgec_set_exception(struct fman_mac *tgec,
- enum fman_mac_exceptions exception, bool enable)
- {
- struct tgec_regs __iomem *regs = tgec->regs;
- u32 bit_mask = 0;
-
-- if (!is_init_done(tgec->cfg))
-- return -EINVAL;
--
- bit_mask = get_exception_flag(exception);
- if (bit_mask) {
- if (enable)
-@@ -641,9 +621,6 @@ static int tgec_init(struct fman_mac *tg
- enet_addr_t eth_addr;
- int err;
-
-- if (is_init_done(tgec->cfg))
-- return -EINVAL;
--
- if (DEFAULT_RESET_ON_INIT &&
- (fman_reset_mac(tgec->fm, tgec->mac_id) != 0)) {
- pr_err("Can't reset MAC!\n");
-@@ -753,7 +730,6 @@ static struct fman_mac *tgec_config(stru
-
- tgec->regs = mac_dev->vaddr;
- tgec->addr = ENET_ADDR_TO_UINT64(mac_dev->addr);
-- tgec->max_speed = params->max_speed;
- tgec->mac_id = params->mac_id;
- tgec->exceptions = (TGEC_IMASK_MDIO_SCAN_EVENT |
- TGEC_IMASK_REM_FAULT |
-@@ -788,17 +764,15 @@ int tgec_initialization(struct mac_devic
- int err;
- struct fman_mac *tgec;
-
-+ mac_dev->phylink_ops = &tgec_mac_ops;
- mac_dev->set_promisc = tgec_set_promiscuous;
- mac_dev->change_addr = tgec_modify_mac_address;
- mac_dev->add_hash_mac_addr = tgec_add_hash_mac_address;
- mac_dev->remove_hash_mac_addr = tgec_del_hash_mac_address;
-- mac_dev->set_tx_pause = tgec_set_tx_pause_frames;
-- mac_dev->set_rx_pause = tgec_accept_rx_pause_frames;
- mac_dev->set_exception = tgec_set_exception;
- mac_dev->set_allmulti = tgec_set_allmulti;
- mac_dev->set_tstamp = tgec_set_tstamp;
- mac_dev->set_multi = fman_set_multi;
-- mac_dev->adjust_link = tgec_adjust_link;
- mac_dev->enable = tgec_enable;
- mac_dev->disable = tgec_disable;
-
-@@ -808,6 +782,19 @@ int tgec_initialization(struct mac_devic
- goto _return;
- }
-
-+ /* The internal connection to the serdes is XGMII, but this isn't
-+ * really correct for the phy mode (which is the external connection).
-+ * However, this is how all older device trees say that they want
-+ * XAUI, so just convert it for them.
-+ */
-+ if (mac_dev->phy_if == PHY_INTERFACE_MODE_XGMII)
-+ mac_dev->phy_if = PHY_INTERFACE_MODE_XAUI;
-+
-+ __set_bit(PHY_INTERFACE_MODE_XAUI,
-+ mac_dev->phylink_config.supported_interfaces);
-+ mac_dev->phylink_config.mac_capabilities =
-+ MAC_SYM_PAUSE | MAC_ASYM_PAUSE | MAC_10000FD;
-+
- tgec = mac_dev->fman_mac;
- tgec->cfg->max_frame_length = fman_get_max_frm();
- err = tgec_init(tgec);
---- a/drivers/net/ethernet/freescale/fman/mac.c
-+++ b/drivers/net/ethernet/freescale/fman/mac.c
-@@ -15,6 +15,7 @@
- #include <linux/phy.h>
- #include <linux/netdevice.h>
- #include <linux/phy_fixed.h>
-+#include <linux/phylink.h>
- #include <linux/etherdevice.h>
- #include <linux/libfdt_env.h>
-
-@@ -93,130 +94,8 @@ int fman_set_multi(struct net_device *ne
- return 0;
- }
-
--/**
-- * fman_set_mac_active_pause
-- * @mac_dev: A pointer to the MAC device
-- * @rx: Pause frame setting for RX
-- * @tx: Pause frame setting for TX
-- *
-- * Set the MAC RX/TX PAUSE frames settings
-- *
-- * Avoid redundant calls to FMD, if the MAC driver already contains the desired
-- * active PAUSE settings. Otherwise, the new active settings should be reflected
-- * in FMan.
-- *
-- * Return: 0 on success; Error code otherwise.
-- */
--int fman_set_mac_active_pause(struct mac_device *mac_dev, bool rx, bool tx)
--{
-- struct fman_mac *fman_mac = mac_dev->fman_mac;
-- int err = 0;
--
-- if (rx != mac_dev->rx_pause_active) {
-- err = mac_dev->set_rx_pause(fman_mac, rx);
-- if (likely(err == 0))
-- mac_dev->rx_pause_active = rx;
-- }
--
-- if (tx != mac_dev->tx_pause_active) {
-- u16 pause_time = (tx ? FSL_FM_PAUSE_TIME_ENABLE :
-- FSL_FM_PAUSE_TIME_DISABLE);
--
-- err = mac_dev->set_tx_pause(fman_mac, 0, pause_time, 0);
--
-- if (likely(err == 0))
-- mac_dev->tx_pause_active = tx;
-- }
--
-- return err;
--}
--EXPORT_SYMBOL(fman_set_mac_active_pause);
--
--/**
-- * fman_get_pause_cfg
-- * @mac_dev: A pointer to the MAC device
-- * @rx_pause: Return value for RX setting
-- * @tx_pause: Return value for TX setting
-- *
-- * Determine the MAC RX/TX PAUSE frames settings based on PHY
-- * autonegotiation or values set by eththool.
-- *
-- * Return: Pointer to FMan device.
-- */
--void fman_get_pause_cfg(struct mac_device *mac_dev, bool *rx_pause,
-- bool *tx_pause)
--{
-- struct phy_device *phy_dev = mac_dev->phy_dev;
-- u16 lcl_adv, rmt_adv;
-- u8 flowctrl;
--
-- *rx_pause = *tx_pause = false;
--
-- if (!phy_dev->duplex)
-- return;
--
-- /* If PAUSE autonegotiation is disabled, the TX/RX PAUSE settings
-- * are those set by ethtool.
-- */
-- if (!mac_dev->autoneg_pause) {
-- *rx_pause = mac_dev->rx_pause_req;
-- *tx_pause = mac_dev->tx_pause_req;
-- return;
-- }
--
-- /* Else if PAUSE autonegotiation is enabled, the TX/RX PAUSE
-- * settings depend on the result of the link negotiation.
-- */
--
-- /* get local capabilities */
-- lcl_adv = linkmode_adv_to_lcl_adv_t(phy_dev->advertising);
--
-- /* get link partner capabilities */
-- rmt_adv = 0;
-- if (phy_dev->pause)
-- rmt_adv |= LPA_PAUSE_CAP;
-- if (phy_dev->asym_pause)
-- rmt_adv |= LPA_PAUSE_ASYM;
--
-- /* Calculate TX/RX settings based on local and peer advertised
-- * symmetric/asymmetric PAUSE capabilities.
-- */
-- flowctrl = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv);
-- if (flowctrl & FLOW_CTRL_RX)
-- *rx_pause = true;
-- if (flowctrl & FLOW_CTRL_TX)
-- *tx_pause = true;
--}
--EXPORT_SYMBOL(fman_get_pause_cfg);
--
--#define DTSEC_SUPPORTED \
-- (SUPPORTED_10baseT_Half \
-- | SUPPORTED_10baseT_Full \
-- | SUPPORTED_100baseT_Half \
-- | SUPPORTED_100baseT_Full \
-- | SUPPORTED_Autoneg \
-- | SUPPORTED_Pause \
-- | SUPPORTED_Asym_Pause \
-- | SUPPORTED_FIBRE \
-- | SUPPORTED_MII)
--
- static DEFINE_MUTEX(eth_lock);
-
--static const u16 phy2speed[] = {
-- [PHY_INTERFACE_MODE_MII] = SPEED_100,
-- [PHY_INTERFACE_MODE_GMII] = SPEED_1000,
-- [PHY_INTERFACE_MODE_SGMII] = SPEED_1000,
-- [PHY_INTERFACE_MODE_TBI] = SPEED_1000,
-- [PHY_INTERFACE_MODE_RMII] = SPEED_100,
-- [PHY_INTERFACE_MODE_RGMII] = SPEED_1000,
-- [PHY_INTERFACE_MODE_RGMII_ID] = SPEED_1000,
-- [PHY_INTERFACE_MODE_RGMII_RXID] = SPEED_1000,
-- [PHY_INTERFACE_MODE_RGMII_TXID] = SPEED_1000,
-- [PHY_INTERFACE_MODE_RTBI] = SPEED_1000,
-- [PHY_INTERFACE_MODE_QSGMII] = SPEED_1000,
-- [PHY_INTERFACE_MODE_XGMII] = SPEED_10000
--};
--
- static struct platform_device *dpaa_eth_add_device(int fman_id,
- struct mac_device *mac_dev)
- {
-@@ -263,8 +142,8 @@ no_mem:
- }
-
- static const struct of_device_id mac_match[] = {
-- { .compatible = "fsl,fman-dtsec", .data = dtsec_initialization },
-- { .compatible = "fsl,fman-xgec", .data = tgec_initialization },
-+ { .compatible = "fsl,fman-dtsec", .data = dtsec_initialization },
-+ { .compatible = "fsl,fman-xgec", .data = tgec_initialization },
- { .compatible = "fsl,fman-memac", .data = memac_initialization },
- {}
- };
-@@ -295,6 +174,7 @@ static int mac_probe(struct platform_dev
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-+ platform_set_drvdata(_of_dev, mac_dev);
-
- /* Save private information */
- mac_dev->priv = priv;
-@@ -424,57 +304,21 @@ static int mac_probe(struct platform_dev
- }
- mac_dev->phy_if = phy_if;
-
-- priv->speed = phy2speed[mac_dev->phy_if];
-- params.max_speed = priv->speed;
-- mac_dev->if_support = DTSEC_SUPPORTED;
-- /* We don't support half-duplex in SGMII mode */
-- if (mac_dev->phy_if == PHY_INTERFACE_MODE_SGMII)
-- mac_dev->if_support &= ~(SUPPORTED_10baseT_Half |
-- SUPPORTED_100baseT_Half);
--
-- /* Gigabit support (no half-duplex) */
-- if (params.max_speed == 1000)
-- mac_dev->if_support |= SUPPORTED_1000baseT_Full;
--
-- /* The 10G interface only supports one mode */
-- if (mac_dev->phy_if == PHY_INTERFACE_MODE_XGMII)
-- mac_dev->if_support = SUPPORTED_10000baseT_Full;
--
-- /* Get the rest of the PHY information */
-- mac_dev->phy_node = of_parse_phandle(mac_node, "phy-handle", 0);
--
-- params.basex_if = false;
- params.mac_id = priv->cell_index;
- params.fm = (void *)priv->fman;
- params.exception_cb = mac_exception;
- params.event_cb = mac_exception;
-
- err = init(mac_dev, mac_node, ¶ms);
-- if (err < 0) {
-- dev_err(dev, "mac_dev->init() = %d\n", err);
-- of_node_put(mac_dev->phy_node);
-- return err;
-- }
--
-- /* pause frame autonegotiation enabled */
-- mac_dev->autoneg_pause = true;
--
-- /* By intializing the values to false, force FMD to enable PAUSE frames
-- * on RX and TX
-- */
-- mac_dev->rx_pause_req = true;
-- mac_dev->tx_pause_req = true;
-- mac_dev->rx_pause_active = false;
-- mac_dev->tx_pause_active = false;
-- err = fman_set_mac_active_pause(mac_dev, true, true);
- if (err < 0)
-- dev_err(dev, "fman_set_mac_active_pause() = %d\n", err);
-+ return err;
-
- if (!is_zero_ether_addr(mac_dev->addr))
- dev_info(dev, "FMan MAC address: %pM\n", mac_dev->addr);
-
- priv->eth_dev = dpaa_eth_add_device(fman_id, mac_dev);
- if (IS_ERR(priv->eth_dev)) {
-+ err = PTR_ERR(priv->eth_dev);
- dev_err(dev, "failed to add Ethernet platform device for MAC %d\n",
- priv->cell_index);
- priv->eth_dev = NULL;
---- a/drivers/net/ethernet/freescale/fman/mac.h
-+++ b/drivers/net/ethernet/freescale/fman/mac.h
-@@ -9,6 +9,7 @@
- #include <linux/device.h>
- #include <linux/if_ether.h>
- #include <linux/phy.h>
-+#include <linux/phylink.h>
- #include <linux/list.h>
-
- #include "fman_port.h"
-@@ -24,32 +25,22 @@ struct mac_device {
- struct resource *res;
- u8 addr[ETH_ALEN];
- struct fman_port *port[2];
-- u32 if_support;
-- struct phy_device *phy_dev;
-+ struct phylink *phylink;
-+ struct phylink_config phylink_config;
- phy_interface_t phy_if;
-- struct device_node *phy_node;
-- struct net_device *net_dev;
-
-- bool autoneg_pause;
-- bool rx_pause_req;
-- bool tx_pause_req;
-- bool rx_pause_active;
-- bool tx_pause_active;
- bool promisc;
- bool allmulti;
-
-+ const struct phylink_mac_ops *phylink_ops;
- int (*enable)(struct fman_mac *mac_dev);
- void (*disable)(struct fman_mac *mac_dev);
-- void (*adjust_link)(struct mac_device *mac_dev);
- int (*set_promisc)(struct fman_mac *mac_dev, bool enable);
- int (*change_addr)(struct fman_mac *mac_dev, const enet_addr_t *enet_addr);
- int (*set_allmulti)(struct fman_mac *mac_dev, bool enable);
- int (*set_tstamp)(struct fman_mac *mac_dev, bool enable);
- int (*set_multi)(struct net_device *net_dev,
- struct mac_device *mac_dev);
-- int (*set_rx_pause)(struct fman_mac *mac_dev, bool en);
-- int (*set_tx_pause)(struct fman_mac *mac_dev, u8 priority,
-- u16 pause_time, u16 thresh_time);
- int (*set_exception)(struct fman_mac *mac_dev,
- enum fman_mac_exceptions exception, bool enable);
- int (*add_hash_mac_addr)(struct fman_mac *mac_dev,
-@@ -63,6 +54,12 @@ struct mac_device {
- struct mac_priv_s *priv;
- };
-
-+static inline struct mac_device
-+*fman_config_to_mac(struct phylink_config *config)
-+{
-+ return container_of(config, struct mac_device, phylink_config);
-+}
-+
- struct dpaa_eth_data {
- struct mac_device *mac_dev;
- int mac_hw_id;
+++ /dev/null
-From bf4de031052fe7c5309e8956c342d4e5ce79038e Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Mon, 17 Oct 2022 16:22:35 -0400
-Subject: [PATCH 04/21] net: phylink: provide phylink_validate_mask_caps()
- helper
-
-Provide a helper that restricts the link modes according to the
-phylink capabilities.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-[rebased on net-next/master and added documentation]
-Signed-off-by: Sean Anderson <sean.anderson@seco.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/phylink.c | 41 +++++++++++++++++++++++++++------------
- include/linux/phylink.h | 3 +++
- 2 files changed, 32 insertions(+), 12 deletions(-)
-
---- a/drivers/net/phy/phylink.c
-+++ b/drivers/net/phy/phylink.c
-@@ -564,31 +564,48 @@ unsigned long phylink_get_capabilities(p
- EXPORT_SYMBOL_GPL(phylink_get_capabilities);
-
- /**
-- * phylink_generic_validate() - generic validate() callback implementation
-- * @config: a pointer to a &struct phylink_config.
-+ * phylink_validate_mask_caps() - Restrict link modes based on caps
- * @supported: ethtool bitmask for supported link modes.
-- * @state: a pointer to a &struct phylink_link_state.
-+ * @state: an (optional) pointer to a &struct phylink_link_state.
-+ * @mac_capabilities: bitmask of MAC capabilities
- *
-- * Generic implementation of the validate() callback that MAC drivers can
-- * use when they pass the range of supported interfaces and MAC capabilities.
-- * This makes use of phylink_get_linkmodes().
-+ * Calculate the supported link modes based on @mac_capabilities, and restrict
-+ * @supported and @state based on that. Use this function if your capabiliies
-+ * aren't constant, such as if they vary depending on the interface.
- */
--void phylink_generic_validate(struct phylink_config *config,
-- unsigned long *supported,
-- struct phylink_link_state *state)
-+void phylink_validate_mask_caps(unsigned long *supported,
-+ struct phylink_link_state *state,
-+ unsigned long mac_capabilities)
- {
- __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
- unsigned long caps;
-
- phylink_set_port_modes(mask);
- phylink_set(mask, Autoneg);
-- caps = phylink_get_capabilities(state->interface,
-- config->mac_capabilities,
-+ caps = phylink_get_capabilities(state->interface, mac_capabilities,
- state->rate_matching);
- phylink_caps_to_linkmodes(mask, caps);
-
- linkmode_and(supported, supported, mask);
-- linkmode_and(state->advertising, state->advertising, mask);
-+ if (state)
-+ linkmode_and(state->advertising, state->advertising, mask);
-+}
-+EXPORT_SYMBOL_GPL(phylink_validate_mask_caps);
-+
-+/**
-+ * phylink_generic_validate() - generic validate() callback implementation
-+ * @config: a pointer to a &struct phylink_config.
-+ * @supported: ethtool bitmask for supported link modes.
-+ * @state: a pointer to a &struct phylink_link_state.
-+ *
-+ * Generic implementation of the validate() callback that MAC drivers can
-+ * use when they pass the range of supported interfaces and MAC capabilities.
-+ */
-+void phylink_generic_validate(struct phylink_config *config,
-+ unsigned long *supported,
-+ struct phylink_link_state *state)
-+{
-+ phylink_validate_mask_caps(supported, state, config->mac_capabilities);
- }
- EXPORT_SYMBOL_GPL(phylink_generic_validate);
-
---- a/include/linux/phylink.h
-+++ b/include/linux/phylink.h
-@@ -558,6 +558,9 @@ void phylink_caps_to_linkmodes(unsigned
- unsigned long phylink_get_capabilities(phy_interface_t interface,
- unsigned long mac_capabilities,
- int rate_matching);
-+void phylink_validate_mask_caps(unsigned long *supported,
-+ struct phylink_link_state *state,
-+ unsigned long caps);
- void phylink_generic_validate(struct phylink_config *config,
- unsigned long *supported,
- struct phylink_link_state *state);
+++ /dev/null
-From 2bf7e4a68c42eed909f3c29582e1fb85cb157e35 Mon Sep 17 00:00:00 2001
-From: Jakub Kicinski <kuba@kernel.org>
-Date: Tue, 25 Oct 2022 11:51:26 -0700
-Subject: [PATCH 05/21] phylink: require valid state argument to
- phylink_validate_mask_caps()
-
-state is deferenced earlier in the function, the NULL check
-is pointless. Since we don't have any crash reports presumably
-it's safe to assume state is not NULL.
-
-Fixes: f392a1846489 ("net: phylink: provide phylink_validate_mask_caps() helper")
-Reviewed-by: Sean Anderson <sean.anderson@seco.com>
-Link: https://lore.kernel.org/r/20221025185126.1720553-1-kuba@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/phylink.c | 5 ++---
- 1 file changed, 2 insertions(+), 3 deletions(-)
-
---- a/drivers/net/phy/phylink.c
-+++ b/drivers/net/phy/phylink.c
-@@ -566,7 +566,7 @@ EXPORT_SYMBOL_GPL(phylink_get_capabiliti
- /**
- * phylink_validate_mask_caps() - Restrict link modes based on caps
- * @supported: ethtool bitmask for supported link modes.
-- * @state: an (optional) pointer to a &struct phylink_link_state.
-+ * @state: pointer to a &struct phylink_link_state.
- * @mac_capabilities: bitmask of MAC capabilities
- *
- * Calculate the supported link modes based on @mac_capabilities, and restrict
-@@ -587,8 +587,7 @@ void phylink_validate_mask_caps(unsigned
- phylink_caps_to_linkmodes(mask, caps);
-
- linkmode_and(supported, supported, mask);
-- if (state)
-- linkmode_and(state->advertising, state->advertising, mask);
-+ linkmode_and(state->advertising, state->advertising, mask);
- }
- EXPORT_SYMBOL_GPL(phylink_validate_mask_caps);
-
+++ /dev/null
-From f8fc363bf0c023e4736a0328174b4a24b44ab23a Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Thu, 27 Oct 2022 14:10:37 +0100
-Subject: [PATCH 06/21] net: phylink: add phylink_get_link_timer_ns() helper
-
-Add a helper to convert the PHY interface mode to the required link
-timer setting as stated by the appropriate standard. Inappropriate
-interface modes return an error.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- include/linux/phylink.h | 24 ++++++++++++++++++++++++
- 1 file changed, 24 insertions(+)
-
---- a/include/linux/phylink.h
-+++ b/include/linux/phylink.h
-@@ -617,6 +617,30 @@ int phylink_speed_up(struct phylink *pl)
-
- void phylink_set_port_modes(unsigned long *bits);
-
-+/**
-+ * phylink_get_link_timer_ns - return the PCS link timer value
-+ * @interface: link &typedef phy_interface_t mode
-+ *
-+ * Return the PCS link timer setting in nanoseconds for the PHY @interface
-+ * mode, or -EINVAL if not appropriate.
-+ */
-+static inline int phylink_get_link_timer_ns(phy_interface_t interface)
-+{
-+ switch (interface) {
-+ case PHY_INTERFACE_MODE_SGMII:
-+ case PHY_INTERFACE_MODE_QSGMII:
-+ case PHY_INTERFACE_MODE_USXGMII:
-+ return 1600000;
-+
-+ case PHY_INTERFACE_MODE_1000BASEX:
-+ case PHY_INTERFACE_MODE_2500BASEX:
-+ return 10000000;
-+
-+ default:
-+ return -EINVAL;
-+ }
-+}
-+
- void phylink_mii_c22_pcs_decode_state(struct phylink_link_state *state,
- u16 bmsr, u16 lpa);
- void phylink_mii_c22_pcs_get_state(struct mdio_device *pcs,
+++ /dev/null
-From b45b773a96b0e9e8d51e5d005485f4e376d6ce9a Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Fri, 4 Nov 2022 17:13:01 +0000
-Subject: [PATCH 07/21] net: remove explicit phylink_generic_validate()
- references
-
-Virtually all conventional network drivers are now converted to use
-phylink_generic_validate() - only DSA drivers and fman_memac remain,
-so lets remove the necessity for network drivers to explicitly set
-this member, and default to phylink_generic_validate() when unset.
-This is possible as .validate must currently be set.
-
-Any remaining instances that have not been addressed by this patch can
-be fixed up later.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Reviewed-by: Vladimir Oltean <vladimir.oltean@nxp.com>
-Link: https://lore.kernel.org/r/E1or0FZ-001tRa-DI@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/altera/altera_tse_main.c | 1 -
- drivers/net/ethernet/atheros/ag71xx.c | 1 -
- drivers/net/ethernet/cadence/macb_main.c | 1 -
- drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 1 -
- drivers/net/ethernet/freescale/enetc/enetc_pf.c | 1 -
- drivers/net/ethernet/freescale/fman/fman_dtsec.c | 1 -
- drivers/net/ethernet/freescale/fman/fman_tgec.c | 1 -
- drivers/net/ethernet/marvell/mvneta.c | 1 -
- drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 1 -
- drivers/net/ethernet/marvell/prestera/prestera_main.c | 1 -
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 1 -
- drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c | 1 -
- drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c | 1 -
- drivers/net/ethernet/mscc/ocelot_net.c | 1 -
- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 1 -
- drivers/net/ethernet/ti/am65-cpsw-nuss.c | 1 -
- drivers/net/ethernet/xilinx/xilinx_axienet_main.c | 1 -
- drivers/net/phy/phylink.c | 5 ++++-
- drivers/net/usb/asix_devices.c | 1 -
- include/linux/phylink.h | 5 +++++
- 20 files changed, 9 insertions(+), 19 deletions(-)
-
---- a/drivers/net/ethernet/altera/altera_tse_main.c
-+++ b/drivers/net/ethernet/altera/altera_tse_main.c
-@@ -1096,7 +1096,6 @@ static struct phylink_pcs *alt_tse_selec
- }
-
- static const struct phylink_mac_ops alt_tse_phylink_ops = {
-- .validate = phylink_generic_validate,
- .mac_an_restart = alt_tse_mac_an_restart,
- .mac_config = alt_tse_mac_config,
- .mac_link_down = alt_tse_mac_link_down,
---- a/drivers/net/ethernet/atheros/ag71xx.c
-+++ b/drivers/net/ethernet/atheros/ag71xx.c
-@@ -1086,7 +1086,6 @@ static void ag71xx_mac_link_up(struct ph
- }
-
- static const struct phylink_mac_ops ag71xx_phylink_mac_ops = {
-- .validate = phylink_generic_validate,
- .mac_config = ag71xx_mac_config,
- .mac_link_down = ag71xx_mac_link_down,
- .mac_link_up = ag71xx_mac_link_up,
---- a/drivers/net/ethernet/cadence/macb_main.c
-+++ b/drivers/net/ethernet/cadence/macb_main.c
-@@ -752,7 +752,6 @@ static struct phylink_pcs *macb_mac_sele
- }
-
- static const struct phylink_mac_ops macb_phylink_ops = {
-- .validate = phylink_generic_validate,
- .mac_select_pcs = macb_mac_select_pcs,
- .mac_config = macb_mac_config,
- .mac_link_down = macb_mac_link_down,
---- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
-+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
-@@ -235,7 +235,6 @@ static void dpaa2_mac_link_down(struct p
- }
-
- static const struct phylink_mac_ops dpaa2_mac_phylink_ops = {
-- .validate = phylink_generic_validate,
- .mac_select_pcs = dpaa2_mac_select_pcs,
- .mac_config = dpaa2_mac_config,
- .mac_link_up = dpaa2_mac_link_up,
---- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
-+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
-@@ -1111,7 +1111,6 @@ static void enetc_pl_mac_link_down(struc
- }
-
- static const struct phylink_mac_ops enetc_mac_phylink_ops = {
-- .validate = phylink_generic_validate,
- .mac_select_pcs = enetc_pl_mac_select_pcs,
- .mac_config = enetc_pl_mac_config,
- .mac_link_up = enetc_pl_mac_link_up,
---- a/drivers/net/ethernet/freescale/fman/fman_dtsec.c
-+++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c
-@@ -986,7 +986,6 @@ static void dtsec_link_down(struct phyli
- }
-
- static const struct phylink_mac_ops dtsec_mac_ops = {
-- .validate = phylink_generic_validate,
- .mac_select_pcs = dtsec_select_pcs,
- .mac_config = dtsec_mac_config,
- .mac_link_up = dtsec_link_up,
---- a/drivers/net/ethernet/freescale/fman/fman_tgec.c
-+++ b/drivers/net/ethernet/freescale/fman/fman_tgec.c
-@@ -469,7 +469,6 @@ static void tgec_link_down(struct phylin
- }
-
- static const struct phylink_mac_ops tgec_mac_ops = {
-- .validate = phylink_generic_validate,
- .mac_config = tgec_mac_config,
- .mac_link_up = tgec_link_up,
- .mac_link_down = tgec_link_down,
---- a/drivers/net/ethernet/marvell/mvneta.c
-+++ b/drivers/net/ethernet/marvell/mvneta.c
-@@ -4228,7 +4228,6 @@ static void mvneta_mac_link_up(struct ph
- }
-
- static const struct phylink_mac_ops mvneta_phylink_ops = {
-- .validate = phylink_generic_validate,
- .mac_select_pcs = mvneta_mac_select_pcs,
- .mac_prepare = mvneta_mac_prepare,
- .mac_config = mvneta_mac_config,
---- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
-+++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
-@@ -6633,7 +6633,6 @@ static void mvpp2_mac_link_down(struct p
- }
-
- static const struct phylink_mac_ops mvpp2_phylink_ops = {
-- .validate = phylink_generic_validate,
- .mac_select_pcs = mvpp2_select_pcs,
- .mac_prepare = mvpp2_mac_prepare,
- .mac_config = mvpp2_mac_config,
---- a/drivers/net/ethernet/marvell/prestera/prestera_main.c
-+++ b/drivers/net/ethernet/marvell/prestera/prestera_main.c
-@@ -360,7 +360,6 @@ static void prestera_pcs_an_restart(stru
- }
-
- static const struct phylink_mac_ops prestera_mac_ops = {
-- .validate = phylink_generic_validate,
- .mac_select_pcs = prestera_mac_select_pcs,
- .mac_config = prestera_mac_config,
- .mac_link_down = prestera_mac_link_down,
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -654,7 +654,6 @@ static void mtk_mac_link_up(struct phyli
- }
-
- static const struct phylink_mac_ops mtk_phylink_ops = {
-- .validate = phylink_generic_validate,
- .mac_select_pcs = mtk_mac_select_pcs,
- .mac_pcs_get_state = mtk_mac_pcs_get_state,
- .mac_config = mtk_mac_config,
---- a/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c
-+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c
-@@ -125,7 +125,6 @@ static void lan966x_pcs_aneg_restart(str
- }
-
- const struct phylink_mac_ops lan966x_phylink_mac_ops = {
-- .validate = phylink_generic_validate,
- .mac_select_pcs = lan966x_phylink_mac_select,
- .mac_config = lan966x_phylink_mac_config,
- .mac_prepare = lan966x_phylink_mac_prepare,
---- a/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c
-+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c
-@@ -138,7 +138,6 @@ const struct phylink_pcs_ops sparx5_phyl
- };
-
- const struct phylink_mac_ops sparx5_phylink_mac_ops = {
-- .validate = phylink_generic_validate,
- .mac_select_pcs = sparx5_phylink_mac_select_pcs,
- .mac_config = sparx5_phylink_mac_config,
- .mac_link_down = sparx5_phylink_mac_link_down,
---- a/drivers/net/ethernet/mscc/ocelot_net.c
-+++ b/drivers/net/ethernet/mscc/ocelot_net.c
-@@ -1737,7 +1737,6 @@ static void vsc7514_phylink_mac_link_up(
- }
-
- static const struct phylink_mac_ops ocelot_phylink_ops = {
-- .validate = phylink_generic_validate,
- .mac_config = vsc7514_phylink_mac_config,
- .mac_link_down = vsc7514_phylink_mac_link_down,
- .mac_link_up = vsc7514_phylink_mac_link_up,
---- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
-+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
-@@ -1090,7 +1090,6 @@ static void stmmac_mac_link_up(struct ph
- }
-
- static const struct phylink_mac_ops stmmac_phylink_mac_ops = {
-- .validate = phylink_generic_validate,
- .mac_select_pcs = stmmac_mac_select_pcs,
- .mac_config = stmmac_mac_config,
- .mac_link_down = stmmac_mac_link_down,
---- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c
-+++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c
-@@ -1493,7 +1493,6 @@ static void am65_cpsw_nuss_mac_link_up(s
- }
-
- static const struct phylink_mac_ops am65_cpsw_phylink_mac_ops = {
-- .validate = phylink_generic_validate,
- .mac_config = am65_cpsw_nuss_mac_config,
- .mac_link_down = am65_cpsw_nuss_mac_link_down,
- .mac_link_up = am65_cpsw_nuss_mac_link_up,
---- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
-+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
-@@ -1736,7 +1736,6 @@ static void axienet_mac_link_up(struct p
- }
-
- static const struct phylink_mac_ops axienet_phylink_ops = {
-- .validate = phylink_generic_validate,
- .mac_select_pcs = axienet_mac_select_pcs,
- .mac_config = axienet_mac_config,
- .mac_link_down = axienet_mac_link_down,
---- a/drivers/net/phy/phylink.c
-+++ b/drivers/net/phy/phylink.c
-@@ -651,7 +651,10 @@ static int phylink_validate_mac_and_pcs(
- }
-
- /* Then validate the link parameters with the MAC */
-- pl->mac_ops->validate(pl->config, supported, state);
-+ if (pl->mac_ops->validate)
-+ pl->mac_ops->validate(pl->config, supported, state);
-+ else
-+ phylink_generic_validate(pl->config, supported, state);
-
- return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
- }
---- a/drivers/net/usb/asix_devices.c
-+++ b/drivers/net/usb/asix_devices.c
-@@ -787,7 +787,6 @@ static void ax88772_mac_link_up(struct p
- }
-
- static const struct phylink_mac_ops ax88772_phylink_mac_ops = {
-- .validate = phylink_generic_validate,
- .mac_config = ax88772_mac_config,
- .mac_link_down = ax88772_mac_link_down,
- .mac_link_up = ax88772_mac_link_up,
---- a/include/linux/phylink.h
-+++ b/include/linux/phylink.h
-@@ -207,6 +207,11 @@ struct phylink_mac_ops {
- *
- * If the @state->interface mode is not supported, then the @supported
- * mask must be cleared.
-+ *
-+ * This member is optional; if not set, the generic validator will be
-+ * used making use of @config->mac_capabilities and
-+ * @config->supported_interfaces to determine which link modes are
-+ * supported.
- */
- void validate(struct phylink_config *config, unsigned long *supported,
- struct phylink_link_state *state);
+++ /dev/null
-From a90ac762d345890b40d88a1385a34a2449c2d75e Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Fri, 24 Mar 2023 09:23:42 +0000
-Subject: [PATCH] net: sfp: make sfp_bus_find_fwnode() take a const fwnode
-
-sfp_bus_find_fwnode() does not write to the fwnode, so let's make it
-const.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Reviewed-by: Simon Horman <simon.horman@corigine.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/sfp-bus.c | 2 +-
- include/linux/sfp.h | 5 +++--
- 2 files changed, 4 insertions(+), 3 deletions(-)
-
---- a/drivers/net/phy/sfp-bus.c
-+++ b/drivers/net/phy/sfp-bus.c
-@@ -603,7 +603,7 @@ static void sfp_upstream_clear(struct sf
- * - %-ENOMEM if we failed to allocate the bus.
- * - an error from the upstream's connect_phy() method.
- */
--struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode)
-+struct sfp_bus *sfp_bus_find_fwnode(const struct fwnode_handle *fwnode)
- {
- struct fwnode_reference_args ref;
- struct sfp_bus *bus;
---- a/include/linux/sfp.h
-+++ b/include/linux/sfp.h
-@@ -548,7 +548,7 @@ int sfp_get_module_eeprom_by_page(struct
- void sfp_upstream_start(struct sfp_bus *bus);
- void sfp_upstream_stop(struct sfp_bus *bus);
- void sfp_bus_put(struct sfp_bus *bus);
--struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode);
-+struct sfp_bus *sfp_bus_find_fwnode(const struct fwnode_handle *fwnode);
- int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream,
- const struct sfp_upstream_ops *ops);
- void sfp_bus_del_upstream(struct sfp_bus *bus);
-@@ -610,7 +610,8 @@ static inline void sfp_bus_put(struct sf
- {
- }
-
--static inline struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode)
-+static inline struct sfp_bus *
-+sfp_bus_find_fwnode(const struct fwnode_handle *fwnode)
- {
- return NULL;
- }
+++ /dev/null
-From ecec0ebbc6381a5a375f1cf10c4858f24e91e2ef Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Wed, 15 Mar 2023 14:46:49 +0000
-Subject: [PATCH] net: pcs: lynx: don't print an_enabled in pcs_get_state()
-
-an_enabled will be going away, and in any case, pcs_get_state() should
-not be updating this member. Remove the print.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Reviewed-by: Steen Hegelund <Steen.Hegelund@microchip.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/pcs/pcs-lynx.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
---- a/drivers/net/pcs/pcs-lynx.c
-+++ b/drivers/net/pcs/pcs-lynx.c
-@@ -115,11 +115,11 @@ static void lynx_pcs_get_state(struct ph
- }
-
- dev_dbg(&lynx->mdio->dev,
-- "mode=%s/%s/%s link=%u an_enabled=%u an_complete=%u\n",
-+ "mode=%s/%s/%s link=%u an_complete=%u\n",
- phy_modes(state->interface),
- phy_speed_to_str(state->speed),
- phy_duplex_to_str(state->duplex),
-- state->link, state->an_enabled, state->an_complete);
-+ state->link, state->an_complete);
- }
-
- static int lynx_pcs_config_giga(struct mdio_device *pcs, unsigned int mode,
+++ /dev/null
-From 99d0f3a1095f4c938b1665025c29411edafe8a01 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Tue, 21 Mar 2023 15:58:44 +0000
-Subject: [PATCH] net: dpaa2-mac: use Autoneg bit rather than an_enabled
-
-The Autoneg bit in the advertising bitmap and state->an_enabled are
-always identical. Thus, we will be removing state->an_enabled.
-
-Use the Autoneg bit in the advertising bitmap to indicate whether
-autonegotiation should be used, rather than using the an_enabled
-member which will be going away. This means we use the same condition
-as phylink_mii_c22_pcs_config().
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Reviewed-by: Simon Horman <simon.horman@corigine.com>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
---- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
-+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
-@@ -158,7 +158,8 @@ static void dpaa2_mac_config(struct phyl
- struct dpmac_link_state *dpmac_state = &mac->state;
- int err;
-
-- if (state->an_enabled)
-+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
-+ state->advertising))
- dpmac_state->options |= DPMAC_LINK_OPT_AUTONEG;
- else
- dpmac_state->options &= ~DPMAC_LINK_OPT_AUTONEG;
+++ /dev/null
-From 471c40bde606ec0b1ce8c616f7998739c7a783a6 Mon Sep 17 00:00:00 2001
-From: Ivan Bornyakov <i.bornyakov@metrotek.ru>
-Date: Fri, 10 Feb 2023 18:46:27 +0300
-Subject: [PATCH 10/21] net: phylink: support validated pause and autoneg in
- fixed-link
-
-In fixed-link setup phylink_parse_fixedlink() unconditionally sets
-Pause, Asym_Pause and Autoneg bits to "supported" bitmap, while MAC may
-not support these.
-
-This leads to ethtool reporting:
-
- > Supported pause frame use: Symmetric Receive-only
- > Supports auto-negotiation: Yes
-
-regardless of what is actually supported.
-
-Instead of unconditionally set Pause, Asym_Pause and Autoneg it is
-sensible to set them according to validated "supported" bitmap, i.e. the
-result of phylink_validate().
-
-Signed-off-by: Ivan Bornyakov <i.bornyakov@metrotek.ru>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/phylink.c | 17 ++++++++++++++---
- 1 file changed, 14 insertions(+), 3 deletions(-)
-
---- a/drivers/net/phy/phylink.c
-+++ b/drivers/net/phy/phylink.c
-@@ -709,6 +709,7 @@ static int phylink_parse_fixedlink(struc
- struct fwnode_handle *fwnode)
- {
- struct fwnode_handle *fixed_node;
-+ bool pause, asym_pause, autoneg;
- const struct phy_setting *s;
- struct gpio_desc *desc;
- u32 speed;
-@@ -781,13 +782,23 @@ static int phylink_parse_fixedlink(struc
- linkmode_copy(pl->link_config.advertising, pl->supported);
- phylink_validate(pl, pl->supported, &pl->link_config);
-
-+ pause = phylink_test(pl->supported, Pause);
-+ asym_pause = phylink_test(pl->supported, Asym_Pause);
-+ autoneg = phylink_test(pl->supported, Autoneg);
- s = phy_lookup_setting(pl->link_config.speed, pl->link_config.duplex,
- pl->supported, true);
- linkmode_zero(pl->supported);
- phylink_set(pl->supported, MII);
-- phylink_set(pl->supported, Pause);
-- phylink_set(pl->supported, Asym_Pause);
-- phylink_set(pl->supported, Autoneg);
-+
-+ if (pause)
-+ phylink_set(pl->supported, Pause);
-+
-+ if (asym_pause)
-+ phylink_set(pl->supported, Asym_Pause);
-+
-+ if (autoneg)
-+ phylink_set(pl->supported, Autoneg);
-+
- if (s) {
- __set_bit(s->bit, pl->supported);
- __set_bit(s->bit, pl->link_config.lp_advertising);
+++ /dev/null
-From 7211ffd70941933a7825a56cf480f07ee81c321c Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Tue, 21 Mar 2023 15:58:54 +0000
-Subject: [PATCH 11/21] net: phylink: remove an_enabled
-
-The Autoneg bit in the advertising bitmap and state->an_enabled are
-always identical. state->an_enabled is now no longer used by any
-drivers, so lets kill this duplication.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Reviewed-by: Simon Horman <simon.horman@corigine.com>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/phylink.c | 37 +++++++++++++++++--------------------
- include/linux/phylink.h | 2 --
- 2 files changed, 17 insertions(+), 22 deletions(-)
-
---- a/drivers/net/phy/phylink.c
-+++ b/drivers/net/phy/phylink.c
-@@ -841,7 +841,6 @@ static int phylink_parse_mode(struct phy
- phylink_set(pl->supported, Autoneg);
- phylink_set(pl->supported, Asym_Pause);
- phylink_set(pl->supported, Pause);
-- pl->link_config.an_enabled = true;
- pl->cfg_link_an_mode = MLO_AN_INBAND;
-
- switch (pl->link_config.interface) {
-@@ -944,9 +943,6 @@ static int phylink_parse_mode(struct phy
- "failed to validate link configuration for in-band status\n");
- return -EINVAL;
- }
--
-- /* Check if MAC/PCS also supports Autoneg. */
-- pl->link_config.an_enabled = phylink_test(pl->supported, Autoneg);
- }
-
- return 0;
-@@ -956,7 +952,8 @@ static void phylink_apply_manual_flow(st
- struct phylink_link_state *state)
- {
- /* If autoneg is disabled, pause AN is also disabled */
-- if (!state->an_enabled)
-+ if (!linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
-+ state->advertising))
- state->pause &= ~MLO_PAUSE_AN;
-
- /* Manual configuration of pause modes */
-@@ -996,21 +993,22 @@ static void phylink_mac_config(struct ph
- const struct phylink_link_state *state)
- {
- phylink_dbg(pl,
-- "%s: mode=%s/%s/%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n",
-+ "%s: mode=%s/%s/%s/%s/%s adv=%*pb pause=%02x link=%u\n",
- __func__, phylink_an_mode_str(pl->cur_link_an_mode),
- phy_modes(state->interface),
- phy_speed_to_str(state->speed),
- phy_duplex_to_str(state->duplex),
- phy_rate_matching_to_str(state->rate_matching),
- __ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising,
-- state->pause, state->link, state->an_enabled);
-+ state->pause, state->link);
-
- pl->mac_ops->mac_config(pl->config, pl->cur_link_an_mode, state);
- }
-
- static void phylink_mac_pcs_an_restart(struct phylink *pl)
- {
-- if (pl->link_config.an_enabled &&
-+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
-+ pl->link_config.advertising) &&
- phy_interface_mode_is_8023z(pl->link_config.interface) &&
- phylink_autoneg_inband(pl->cur_link_an_mode)) {
- if (pl->pcs)
-@@ -1137,9 +1135,9 @@ static void phylink_mac_pcs_get_state(st
- linkmode_copy(state->advertising, pl->link_config.advertising);
- linkmode_zero(state->lp_advertising);
- state->interface = pl->link_config.interface;
-- state->an_enabled = pl->link_config.an_enabled;
- state->rate_matching = pl->link_config.rate_matching;
-- if (state->an_enabled) {
-+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
-+ state->advertising)) {
- state->speed = SPEED_UNKNOWN;
- state->duplex = DUPLEX_UNKNOWN;
- state->pause = MLO_PAUSE_NONE;
-@@ -1531,7 +1529,6 @@ struct phylink *phylink_create(struct ph
- pl->link_config.pause = MLO_PAUSE_AN;
- pl->link_config.speed = SPEED_UNKNOWN;
- pl->link_config.duplex = DUPLEX_UNKNOWN;
-- pl->link_config.an_enabled = true;
- pl->mac_ops = mac_ops;
- __set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
- timer_setup(&pl->link_poll, phylink_fixed_poll, 0);
-@@ -2155,8 +2152,9 @@ static void phylink_get_ksettings(const
- kset->base.speed = state->speed;
- kset->base.duplex = state->duplex;
- }
-- kset->base.autoneg = state->an_enabled ? AUTONEG_ENABLE :
-- AUTONEG_DISABLE;
-+ kset->base.autoneg = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
-+ state->advertising) ?
-+ AUTONEG_ENABLE : AUTONEG_DISABLE;
- }
-
- /**
-@@ -2303,9 +2301,8 @@ int phylink_ethtool_ksettings_set(struct
- /* We have ruled out the case with a PHY attached, and the
- * fixed-link cases. All that is left are in-band links.
- */
-- config.an_enabled = kset->base.autoneg == AUTONEG_ENABLE;
- linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising,
-- config.an_enabled);
-+ kset->base.autoneg == AUTONEG_ENABLE);
-
- /* If this link is with an SFP, ensure that changes to advertised modes
- * also cause the associated interface to be selected such that the
-@@ -2339,13 +2336,14 @@ int phylink_ethtool_ksettings_set(struct
- }
-
- /* If autonegotiation is enabled, we must have an advertisement */
-- if (config.an_enabled && phylink_is_empty_linkmode(config.advertising))
-+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
-+ config.advertising) &&
-+ phylink_is_empty_linkmode(config.advertising))
- return -EINVAL;
-
- mutex_lock(&pl->state_mutex);
- pl->link_config.speed = config.speed;
- pl->link_config.duplex = config.duplex;
-- pl->link_config.an_enabled = config.an_enabled;
-
- if (pl->link_config.interface != config.interface) {
- /* The interface changed, e.g. 1000base-X <-> 2500base-X */
-@@ -2951,7 +2949,6 @@ static int phylink_sfp_config_phy(struct
- config.speed = SPEED_UNKNOWN;
- config.duplex = DUPLEX_UNKNOWN;
- config.pause = MLO_PAUSE_AN;
-- config.an_enabled = pl->link_config.an_enabled;
-
- /* Ignore errors if we're expecting a PHY to attach later */
- ret = phylink_validate(pl, support, &config);
-@@ -3020,7 +3017,6 @@ static int phylink_sfp_config_optical(st
- config.speed = SPEED_UNKNOWN;
- config.duplex = DUPLEX_UNKNOWN;
- config.pause = MLO_PAUSE_AN;
-- config.an_enabled = true;
-
- /* For all the interfaces that are supported, reduce the sfp_support
- * mask to only those link modes that can be supported.
-@@ -3354,7 +3350,8 @@ void phylink_mii_c22_pcs_decode_state(st
- /* If there is no link or autonegotiation is disabled, the LP advertisement
- * data is not meaningful, so don't go any further.
- */
-- if (!state->link || !state->an_enabled)
-+ if (!state->link || !linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
-+ state->advertising))
- return;
-
- switch (state->interface) {
---- a/include/linux/phylink.h
-+++ b/include/linux/phylink.h
-@@ -93,7 +93,6 @@ static inline bool phylink_autoneg_inban
- * the medium link mode (@speed and @duplex) and the speed/duplex of the phy
- * interface mode (@interface) are different.
- * @link: true if the link is up.
-- * @an_enabled: true if autonegotiation is enabled/desired.
- * @an_complete: true if autonegotiation has completed.
- */
- struct phylink_link_state {
-@@ -105,7 +104,6 @@ struct phylink_link_state {
- int pause;
- int rate_matching;
- unsigned int link:1;
-- unsigned int an_enabled:1;
- unsigned int an_complete:1;
- };
-
+++ /dev/null
-From a3555d1f5c208f0a63eafee77381f68d304a0512 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Fri, 12 May 2023 17:58:37 +0100
-Subject: [PATCH 12/21] net: phylink: constify fwnode arguments
-
-Both phylink_create() and phylink_fwnode_phy_connect() do not modify
-the fwnode argument that they are passed, so lets constify these.
-
-Reviewed-by: Simon Horman <simon.horman@corigine.com>
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/phylink.c | 11 ++++++-----
- include/linux/phylink.h | 9 +++++----
- 2 files changed, 11 insertions(+), 9 deletions(-)
-
---- a/drivers/net/phy/phylink.c
-+++ b/drivers/net/phy/phylink.c
-@@ -706,7 +706,7 @@ static int phylink_validate(struct phyli
- }
-
- static int phylink_parse_fixedlink(struct phylink *pl,
-- struct fwnode_handle *fwnode)
-+ const struct fwnode_handle *fwnode)
- {
- struct fwnode_handle *fixed_node;
- bool pause, asym_pause, autoneg;
-@@ -817,7 +817,8 @@ static int phylink_parse_fixedlink(struc
- return 0;
- }
-
--static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode)
-+static int phylink_parse_mode(struct phylink *pl,
-+ const struct fwnode_handle *fwnode)
- {
- struct fwnode_handle *dn;
- const char *managed;
-@@ -1440,7 +1441,7 @@ static void phylink_fixed_poll(struct ti
- static const struct sfp_upstream_ops sfp_phylink_ops;
-
- static int phylink_register_sfp(struct phylink *pl,
-- struct fwnode_handle *fwnode)
-+ const struct fwnode_handle *fwnode)
- {
- struct sfp_bus *bus;
- int ret;
-@@ -1479,7 +1480,7 @@ static int phylink_register_sfp(struct p
- * must use IS_ERR() to check for errors from this function.
- */
- struct phylink *phylink_create(struct phylink_config *config,
-- struct fwnode_handle *fwnode,
-+ const struct fwnode_handle *fwnode,
- phy_interface_t iface,
- const struct phylink_mac_ops *mac_ops)
- {
-@@ -1809,7 +1810,7 @@ EXPORT_SYMBOL_GPL(phylink_of_phy_connect
- * Returns 0 on success or a negative errno.
- */
- int phylink_fwnode_phy_connect(struct phylink *pl,
-- struct fwnode_handle *fwnode,
-+ const struct fwnode_handle *fwnode,
- u32 flags)
- {
- struct fwnode_handle *phy_fwnode;
---- a/include/linux/phylink.h
-+++ b/include/linux/phylink.h
-@@ -568,16 +568,17 @@ void phylink_generic_validate(struct phy
- unsigned long *supported,
- struct phylink_link_state *state);
-
--struct phylink *phylink_create(struct phylink_config *, struct fwnode_handle *,
-- phy_interface_t iface,
-- const struct phylink_mac_ops *mac_ops);
-+struct phylink *phylink_create(struct phylink_config *,
-+ const struct fwnode_handle *,
-+ phy_interface_t,
-+ const struct phylink_mac_ops *);
- void phylink_destroy(struct phylink *);
- bool phylink_expects_phy(struct phylink *pl);
-
- int phylink_connect_phy(struct phylink *, struct phy_device *);
- int phylink_of_phy_connect(struct phylink *, struct device_node *, u32 flags);
- int phylink_fwnode_phy_connect(struct phylink *pl,
-- struct fwnode_handle *fwnode,
-+ const struct fwnode_handle *fwnode,
- u32 flags);
- void phylink_disconnect_phy(struct phylink *);
-
+++ /dev/null
-From 4a0faa02d419a6728abef0f1d8a32d8c35ef95e6 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Fri, 24 Mar 2023 09:23:53 +0000
-Subject: [PATCH] net: phy: constify fwnode_get_phy_node() fwnode argument
-
-fwnode_get_phy_node() does not motify the fwnode structure, so make
-the argument const,
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Reviewed-by: Simon Horman <simon.horman@corigine.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/phy_device.c | 2 +-
- include/linux/phy.h | 2 +-
- 2 files changed, 2 insertions(+), 2 deletions(-)
-
---- a/drivers/net/phy/phy_device.c
-+++ b/drivers/net/phy/phy_device.c
-@@ -3003,7 +3003,7 @@ EXPORT_SYMBOL_GPL(device_phy_find_device
- * and "phy-device" are not supported in ACPI. DT supports all the three
- * named references to the phy node.
- */
--struct fwnode_handle *fwnode_get_phy_node(struct fwnode_handle *fwnode)
-+struct fwnode_handle *fwnode_get_phy_node(const struct fwnode_handle *fwnode)
- {
- struct fwnode_handle *phy_node;
-
---- a/include/linux/phy.h
-+++ b/include/linux/phy.h
-@@ -1473,7 +1473,7 @@ int fwnode_get_phy_id(struct fwnode_hand
- struct mdio_device *fwnode_mdio_find_device(struct fwnode_handle *fwnode);
- struct phy_device *fwnode_phy_find_device(struct fwnode_handle *phy_fwnode);
- struct phy_device *device_phy_find_device(struct device *dev);
--struct fwnode_handle *fwnode_get_phy_node(struct fwnode_handle *fwnode);
-+struct fwnode_handle *fwnode_get_phy_node(const struct fwnode_handle *fwnode);
- struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45);
- int phy_device_register(struct phy_device *phy);
- void phy_device_free(struct phy_device *phydev);
+++ /dev/null
-From cc73de0411f7d3cdd157564a78f7a39058420ff8 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Sat, 13 May 2023 22:03:45 +0100
-Subject: [PATCH 13/21] net: phylink: fix ksettings_set() ethtool call
-
-While testing a Fiberstore SFP-10G-T module (which uses 10GBASE-R with
-rate adaption) in a Clearfog platform (which can't do that) it was
-found that the PHYs advertisement was not limited according to the
-hosts capabilities when using ethtool to change it.
-
-Fix this by ensuring that we mask the advertisement with the computed
-support mask as the very first thing we do.
-
-Fixes: cbc1bb1e4689 ("net: phylink: simplify phy case for ksettings_set method")
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/phylink.c | 8 ++++----
- 1 file changed, 4 insertions(+), 4 deletions(-)
-
---- a/drivers/net/phy/phylink.c
-+++ b/drivers/net/phy/phylink.c
-@@ -2226,6 +2226,10 @@ int phylink_ethtool_ksettings_set(struct
-
- ASSERT_RTNL();
-
-+ /* Mask out unsupported advertisements */
-+ linkmode_and(config.advertising, kset->link_modes.advertising,
-+ pl->supported);
-+
- if (pl->phydev) {
- /* We can rely on phylib for this update; we also do not need
- * to update the pl->link_config settings:
-@@ -2250,10 +2254,6 @@ int phylink_ethtool_ksettings_set(struct
-
- config = pl->link_config;
-
-- /* Mask out unsupported advertisements */
-- linkmode_and(config.advertising, kset->link_modes.advertising,
-- pl->supported);
--
- /* FIXME: should we reject autoneg if phy/mac does not support it? */
- switch (kset->base.autoneg) {
- case AUTONEG_DISABLE:
+++ /dev/null
-From 0100d1c5789018ba77bf2f4fab3bd91ecece7b3b Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Wed, 17 May 2023 11:38:12 +0100
-Subject: [PATCH 14/21] net: sfp: add support for setting signalling rate
-
-Add support to the SFP layer to allow phylink to set the signalling
-rate for a SFP module. The rate given will be in units of kilo-baud
-(1000 baud).
-
-Reviewed-by: Simon Horman <simon.horman@corigine.com>
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/phylink.c | 24 ++++++++++++++++++++++++
- drivers/net/phy/sfp-bus.c | 20 ++++++++++++++++++++
- drivers/net/phy/sfp.c | 5 +++++
- drivers/net/phy/sfp.h | 1 +
- include/linux/sfp.h | 6 ++++++
- 5 files changed, 56 insertions(+)
-
---- a/drivers/net/phy/phylink.c
-+++ b/drivers/net/phy/phylink.c
-@@ -156,6 +156,23 @@ static const char *phylink_an_mode_str(u
- return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown";
- }
-
-+static unsigned int phylink_interface_signal_rate(phy_interface_t interface)
-+{
-+ switch (interface) {
-+ case PHY_INTERFACE_MODE_SGMII:
-+ case PHY_INTERFACE_MODE_1000BASEX: /* 1.25Mbd */
-+ return 1250;
-+ case PHY_INTERFACE_MODE_2500BASEX: /* 3.125Mbd */
-+ return 3125;
-+ case PHY_INTERFACE_MODE_5GBASER: /* 5.15625Mbd */
-+ return 5156;
-+ case PHY_INTERFACE_MODE_10GBASER: /* 10.3125Mbd */
-+ return 10313;
-+ default:
-+ return 0;
-+ }
-+}
-+
- /**
- * phylink_interface_max_speed() - get the maximum speed of a phy interface
- * @interface: phy interface mode defined by &typedef phy_interface_t
-@@ -1024,6 +1041,7 @@ static void phylink_major_config(struct
- {
- struct phylink_pcs *pcs = NULL;
- bool pcs_changed = false;
-+ unsigned int rate_kbd;
- int err;
-
- phylink_dbg(pl, "major config %s\n", phy_modes(state->interface));
-@@ -1083,6 +1101,12 @@ static void phylink_major_config(struct
- ERR_PTR(err));
- }
-
-+ if (pl->sfp_bus) {
-+ rate_kbd = phylink_interface_signal_rate(state->interface);
-+ if (rate_kbd)
-+ sfp_upstream_set_signal_rate(pl->sfp_bus, rate_kbd);
-+ }
-+
- phylink_pcs_poll_start(pl);
- }
-
---- a/drivers/net/phy/sfp-bus.c
-+++ b/drivers/net/phy/sfp-bus.c
-@@ -586,6 +586,26 @@ static void sfp_upstream_clear(struct sf
- }
-
- /**
-+ * sfp_upstream_set_signal_rate() - set data signalling rate
-+ * @bus: a pointer to the &struct sfp_bus structure for the sfp module
-+ * @rate_kbd: signalling rate in units of 1000 baud
-+ *
-+ * Configure the rate select settings on the SFP module for the signalling
-+ * rate (not the same as the data rate).
-+ *
-+ * Locks that may be held:
-+ * Phylink's state_mutex
-+ * rtnl lock
-+ * SFP's sm_mutex
-+ */
-+void sfp_upstream_set_signal_rate(struct sfp_bus *bus, unsigned int rate_kbd)
-+{
-+ if (bus->registered)
-+ bus->socket_ops->set_signal_rate(bus->sfp, rate_kbd);
-+}
-+EXPORT_SYMBOL_GPL(sfp_upstream_set_signal_rate);
-+
-+/**
- * sfp_bus_find_fwnode() - parse and locate the SFP bus from fwnode
- * @fwnode: firmware node for the parent device (MAC or PHY)
- *
---- a/drivers/net/phy/sfp.c
-+++ b/drivers/net/phy/sfp.c
-@@ -2474,6 +2474,10 @@ static void sfp_stop(struct sfp *sfp)
- sfp_sm_event(sfp, SFP_E_DEV_DOWN);
- }
-
-+static void sfp_set_signal_rate(struct sfp *sfp, unsigned int rate_kbd)
-+{
-+}
-+
- static int sfp_module_info(struct sfp *sfp, struct ethtool_modinfo *modinfo)
- {
- /* locking... and check module is present */
-@@ -2552,6 +2556,7 @@ static const struct sfp_socket_ops sfp_m
- .detach = sfp_detach,
- .start = sfp_start,
- .stop = sfp_stop,
-+ .set_signal_rate = sfp_set_signal_rate,
- .module_info = sfp_module_info,
- .module_eeprom = sfp_module_eeprom,
- .module_eeprom_by_page = sfp_module_eeprom_by_page,
---- a/drivers/net/phy/sfp.h
-+++ b/drivers/net/phy/sfp.h
-@@ -19,6 +19,7 @@ struct sfp_socket_ops {
- void (*detach)(struct sfp *sfp);
- void (*start)(struct sfp *sfp);
- void (*stop)(struct sfp *sfp);
-+ void (*set_signal_rate)(struct sfp *sfp, unsigned int rate_kbd);
- int (*module_info)(struct sfp *sfp, struct ethtool_modinfo *modinfo);
- int (*module_eeprom)(struct sfp *sfp, struct ethtool_eeprom *ee,
- u8 *data);
---- a/include/linux/sfp.h
-+++ b/include/linux/sfp.h
-@@ -547,6 +547,7 @@ int sfp_get_module_eeprom_by_page(struct
- struct netlink_ext_ack *extack);
- void sfp_upstream_start(struct sfp_bus *bus);
- void sfp_upstream_stop(struct sfp_bus *bus);
-+void sfp_upstream_set_signal_rate(struct sfp_bus *bus, unsigned int rate_kbd);
- void sfp_bus_put(struct sfp_bus *bus);
- struct sfp_bus *sfp_bus_find_fwnode(const struct fwnode_handle *fwnode);
- int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream,
-@@ -606,6 +607,11 @@ static inline void sfp_upstream_stop(str
- {
- }
-
-+static inline void sfp_upstream_set_signal_rate(struct sfp_bus *bus,
-+ unsigned int rate_kbd)
-+{
-+}
-+
- static inline void sfp_bus_put(struct sfp_bus *bus)
- {
- }
+++ /dev/null
-From b84acdb07222a701bfc6403b374249c86f97d18d Mon Sep 17 00:00:00 2001
-From: Russell King <rmk+kernel@armlinux.org.uk>
-Date: Fri, 19 May 2023 14:03:59 +0100
-Subject: [PATCH 15/21] net: phy: add helpers for comparing phy IDs
-
-There are several places which open code comparing PHY IDs. Provide a
-couple of helpers to assist with this, using a slightly simpler test
-than the original:
-
-- phy_id_compare() compares two arbitary PHY IDs and a mask of the
- significant bits in the ID.
-- phydev_id_compare() compares the bound phydev with the specified
- PHY ID, using the bound driver's mask.
-
-Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
-Reviewed-by: Simon Horman <simon.horman@corigine.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/micrel.c | 6 +++---
- drivers/net/phy/phy_device.c | 16 +++++++---------
- drivers/net/phy/phylink.c | 4 ++--
- include/linux/phy.h | 28 ++++++++++++++++++++++++++++
- 4 files changed, 40 insertions(+), 14 deletions(-)
-
---- a/drivers/net/phy/micrel.c
-+++ b/drivers/net/phy/micrel.c
-@@ -620,7 +620,7 @@ static int ksz8051_ksz8795_match_phy_dev
- {
- int ret;
-
-- if ((phydev->phy_id & MICREL_PHY_ID_MASK) != PHY_ID_KSZ8051)
-+ if (!phy_id_compare(phydev->phy_id, PHY_ID_KSZ8051, MICREL_PHY_ID_MASK))
- return 0;
-
- ret = phy_read(phydev, MII_BMSR);
-@@ -1455,7 +1455,7 @@ static int ksz9x31_cable_test_fault_leng
- *
- * distance to fault = (VCT_DATA - 22) * 4 / cable propagation velocity
- */
-- if ((phydev->phy_id & MICREL_PHY_ID_MASK) == PHY_ID_KSZ9131)
-+ if (phydev_id_compare(phydev, PHY_ID_KSZ9131))
- dt = clamp(dt - 22, 0, 255);
-
- return (dt * 400) / 10;
-@@ -1887,7 +1887,7 @@ static __always_inline int ksz886x_cable
- */
- dt = FIELD_GET(data_mask, status);
-
-- if ((phydev->phy_id & MICREL_PHY_ID_MASK) == PHY_ID_LAN8814)
-+ if (phydev_id_compare(phydev, PHY_ID_LAN8814))
- return ((dt - 22) * 800) / 10;
- else
- return (dt * 400) / 10;
---- a/drivers/net/phy/phy_device.c
-+++ b/drivers/net/phy/phy_device.c
-@@ -422,8 +422,7 @@ int phy_unregister_fixup(const char *bus
- fixup = list_entry(pos, struct phy_fixup, list);
-
- if ((!strcmp(fixup->bus_id, bus_id)) &&
-- ((fixup->phy_uid & phy_uid_mask) ==
-- (phy_uid & phy_uid_mask))) {
-+ phy_id_compare(fixup->phy_uid, phy_uid, phy_uid_mask)) {
- list_del(&fixup->list);
- kfree(fixup);
- ret = 0;
-@@ -459,8 +458,8 @@ static int phy_needs_fixup(struct phy_de
- if (strcmp(fixup->bus_id, PHY_ANY_ID) != 0)
- return 0;
-
-- if ((fixup->phy_uid & fixup->phy_uid_mask) !=
-- (phydev->phy_id & fixup->phy_uid_mask))
-+ if (!phy_id_compare(phydev->phy_id, fixup->phy_uid,
-+ fixup->phy_uid_mask))
- if (fixup->phy_uid != PHY_ANY_UID)
- return 0;
-
-@@ -507,15 +506,14 @@ static int phy_bus_match(struct device *
- if (phydev->c45_ids.device_ids[i] == 0xffffffff)
- continue;
-
-- if ((phydrv->phy_id & phydrv->phy_id_mask) ==
-- (phydev->c45_ids.device_ids[i] &
-- phydrv->phy_id_mask))
-+ if (phy_id_compare(phydev->c45_ids.device_ids[i],
-+ phydrv->phy_id, phydrv->phy_id_mask))
- return 1;
- }
- return 0;
- } else {
-- return (phydrv->phy_id & phydrv->phy_id_mask) ==
-- (phydev->phy_id & phydrv->phy_id_mask);
-+ return phy_id_compare(phydev->phy_id, phydrv->phy_id,
-+ phydrv->phy_id_mask);
- }
- }
-
---- a/drivers/net/phy/phylink.c
-+++ b/drivers/net/phy/phylink.c
-@@ -3151,8 +3151,8 @@ static void phylink_sfp_link_up(void *up
- */
- static bool phylink_phy_no_inband(struct phy_device *phy)
- {
-- return phy->is_c45 &&
-- (phy->c45_ids.device_ids[1] & 0xfffffff0) == 0xae025150;
-+ return phy->is_c45 && phy_id_compare(phy->c45_ids.device_ids[1],
-+ 0xae025150, 0xfffffff0);
- }
-
- static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy)
---- a/include/linux/phy.h
-+++ b/include/linux/phy.h
-@@ -993,6 +993,34 @@ struct phy_driver {
- #define PHY_ID_MATCH_MODEL(id) .phy_id = (id), .phy_id_mask = GENMASK(31, 4)
- #define PHY_ID_MATCH_VENDOR(id) .phy_id = (id), .phy_id_mask = GENMASK(31, 10)
-
-+/**
-+ * phy_id_compare - compare @id1 with @id2 taking account of @mask
-+ * @id1: first PHY ID
-+ * @id2: second PHY ID
-+ * @mask: the PHY ID mask, set bits are significant in matching
-+ *
-+ * Return true if the bits from @id1 and @id2 specified by @mask match.
-+ * This uses an equivalent test to (@id & @mask) == (@phy_id & @mask).
-+ */
-+static inline bool phy_id_compare(u32 id1, u32 id2, u32 mask)
-+{
-+ return !((id1 ^ id2) & mask);
-+}
-+
-+/**
-+ * phydev_id_compare - compare @id with the PHY's Clause 22 ID
-+ * @phydev: the PHY device
-+ * @id: the PHY ID to be matched
-+ *
-+ * Compare the @phydev clause 22 ID with the provided @id and return true or
-+ * false depending whether it matches, using the bound driver mask. The
-+ * @phydev must be bound to a driver.
-+ */
-+static inline bool phydev_id_compare(struct phy_device *phydev, u32 id)
-+{
-+ return phy_id_compare(id, phydev->phy_id, phydev->drv->phy_id_mask);
-+}
-+
- /* A Structure for boards to register fixups with the PHY Lib */
- struct phy_fixup {
- struct list_head list;
+++ /dev/null
-From 441e1e44301fc5762a06737f8ec04bf1ce3fb039 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Sat, 20 May 2023 11:41:42 +0100
-Subject: [PATCH 16/21] net: phylink: require supported_interfaces to be filled
-
-We have been requiring the supported_interfaces bitmap to be filled in
-by MAC drivers that have a mac_select_pcs() method. Now that all MAC
-drivers fill in the supported_interfaces bitmap, it is time to enforce
-this. We have already required supported_interfaces to be set in order
-for optical SFPs to be configured in commit f81fa96d8a6c ("net: phylink:
-use phy_interface_t bitmaps for optical modules").
-
-Refuse phylink creation if supported_interfaces is empty, and remove
-code to deal with cases where this mask is empty.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/E1q0K1u-006EIP-ET@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/phylink.c | 26 +++++++++++---------------
- 1 file changed, 11 insertions(+), 15 deletions(-)
-
---- a/drivers/net/phy/phylink.c
-+++ b/drivers/net/phy/phylink.c
-@@ -710,14 +710,11 @@ static int phylink_validate(struct phyli
- {
- const unsigned long *interfaces = pl->config->supported_interfaces;
-
-- if (!phy_interface_empty(interfaces)) {
-- if (state->interface == PHY_INTERFACE_MODE_NA)
-- return phylink_validate_mask(pl, supported, state,
-- interfaces);
-+ if (state->interface == PHY_INTERFACE_MODE_NA)
-+ return phylink_validate_mask(pl, supported, state, interfaces);
-
-- if (!test_bit(state->interface, interfaces))
-- return -EINVAL;
-- }
-+ if (!test_bit(state->interface, interfaces))
-+ return -EINVAL;
-
- return phylink_validate_mac_and_pcs(pl, supported, state);
- }
-@@ -1512,19 +1509,18 @@ struct phylink *phylink_create(struct ph
- struct phylink *pl;
- int ret;
-
-- if (mac_ops->mac_select_pcs &&
-- mac_ops->mac_select_pcs(config, PHY_INTERFACE_MODE_NA) !=
-- ERR_PTR(-EOPNOTSUPP))
-- using_mac_select_pcs = true;
--
- /* Validate the supplied configuration */
-- if (using_mac_select_pcs &&
-- phy_interface_empty(config->supported_interfaces)) {
-+ if (phy_interface_empty(config->supported_interfaces)) {
- dev_err(config->dev,
-- "phylink: error: empty supported_interfaces but mac_select_pcs() method present\n");
-+ "phylink: error: empty supported_interfaces\n");
- return ERR_PTR(-EINVAL);
- }
-
-+ if (mac_ops->mac_select_pcs &&
-+ mac_ops->mac_select_pcs(config, PHY_INTERFACE_MODE_NA) !=
-+ ERR_PTR(-EOPNOTSUPP))
-+ using_mac_select_pcs = true;
-+
- pl = kzalloc(sizeof(*pl), GFP_KERNEL);
- if (!pl)
- return ERR_PTR(-ENOMEM);
+++ /dev/null
-From 4b624a39f2ab523ca6a6ad9448fab1deb7b101e2 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Tue, 23 May 2023 11:15:53 +0100
-Subject: [PATCH 17/21] net: phylink: remove duplicated linkmode pause
- resolution
-
-Phylink had two chunks of code virtually the same for resolving the
-negotiated pause modes. Factor this down to one function.
-
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/phylink.c | 15 ++++-----------
- 1 file changed, 4 insertions(+), 11 deletions(-)
-
---- a/drivers/net/phy/phylink.c
-+++ b/drivers/net/phy/phylink.c
-@@ -976,11 +976,10 @@ static void phylink_apply_manual_flow(st
- state->pause = pl->link_config.pause;
- }
-
--static void phylink_resolve_flow(struct phylink_link_state *state)
-+static void phylink_resolve_an_pause(struct phylink_link_state *state)
- {
- bool tx_pause, rx_pause;
-
-- state->pause = MLO_PAUSE_NONE;
- if (state->duplex == DUPLEX_FULL) {
- linkmode_resolve_pause(state->advertising,
- state->lp_advertising,
-@@ -1192,7 +1191,8 @@ static void phylink_get_fixed_state(stru
- else if (pl->link_gpio)
- state->link = !!gpiod_get_value_cansleep(pl->link_gpio);
-
-- phylink_resolve_flow(state);
-+ state->pause = MLO_PAUSE_NONE;
-+ phylink_resolve_an_pause(state);
- }
-
- static void phylink_mac_initial_config(struct phylink *pl, bool force_restart)
-@@ -3215,7 +3215,6 @@ static const struct sfp_upstream_ops sfp
- static void phylink_decode_c37_word(struct phylink_link_state *state,
- uint16_t config_reg, int speed)
- {
-- bool tx_pause, rx_pause;
- int fd_bit;
-
- if (speed == SPEED_2500)
-@@ -3234,13 +3233,7 @@ static void phylink_decode_c37_word(stru
- state->link = false;
- }
-
-- linkmode_resolve_pause(state->advertising, state->lp_advertising,
-- &tx_pause, &rx_pause);
--
-- if (tx_pause)
-- state->pause |= MLO_PAUSE_TX;
-- if (rx_pause)
-- state->pause |= MLO_PAUSE_RX;
-+ phylink_resolve_an_pause(state);
- }
-
- static void phylink_decode_sgmii_word(struct phylink_link_state *state,
+++ /dev/null
-From aa8b6bd2b1f235b262bd27f317a0516f196c2c6a Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Tue, 23 May 2023 11:15:58 +0100
-Subject: [PATCH 18/21] net: phylink: add function to resolve clause 73
- negotiation
-
-Add a function to resolve clause 73 negotiation according to the
-priority resolution function described in clause 73.3.6.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/phylink.c | 39 +++++++++++++++++++++++++++++++++++++++
- include/linux/phylink.h | 2 ++
- 2 files changed, 41 insertions(+)
-
---- a/drivers/net/phy/phylink.c
-+++ b/drivers/net/phy/phylink.c
-@@ -3212,6 +3212,45 @@ static const struct sfp_upstream_ops sfp
-
- /* Helpers for MAC drivers */
-
-+static struct {
-+ int bit;
-+ int speed;
-+} phylink_c73_priority_resolution[] = {
-+ { ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, SPEED_100000 },
-+ { ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT, SPEED_100000 },
-+ /* 100GBASE-KP4 and 100GBASE-CR10 not supported */
-+ { ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, SPEED_40000 },
-+ { ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, SPEED_40000 },
-+ { ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, SPEED_10000 },
-+ { ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, SPEED_10000 },
-+ /* 5GBASE-KR not supported */
-+ { ETHTOOL_LINK_MODE_2500baseX_Full_BIT, SPEED_2500 },
-+ { ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, SPEED_1000 },
-+};
-+
-+void phylink_resolve_c73(struct phylink_link_state *state)
-+{
-+ int i;
-+
-+ for (i = 0; i < ARRAY_SIZE(phylink_c73_priority_resolution); i++) {
-+ int bit = phylink_c73_priority_resolution[i].bit;
-+ if (linkmode_test_bit(bit, state->advertising) &&
-+ linkmode_test_bit(bit, state->lp_advertising))
-+ break;
-+ }
-+
-+ if (i < ARRAY_SIZE(phylink_c73_priority_resolution)) {
-+ state->speed = phylink_c73_priority_resolution[i].speed;
-+ state->duplex = DUPLEX_FULL;
-+ } else {
-+ /* negotiation failure */
-+ state->link = false;
-+ }
-+
-+ phylink_resolve_an_pause(state);
-+}
-+EXPORT_SYMBOL_GPL(phylink_resolve_c73);
-+
- static void phylink_decode_c37_word(struct phylink_link_state *state,
- uint16_t config_reg, int speed)
- {
---- a/include/linux/phylink.h
-+++ b/include/linux/phylink.h
-@@ -656,6 +656,8 @@ int phylink_mii_c22_pcs_config(struct md
- const unsigned long *advertising);
- void phylink_mii_c22_pcs_an_restart(struct mdio_device *pcs);
-
-+void phylink_resolve_c73(struct phylink_link_state *state);
-+
- void phylink_mii_c45_pcs_get_state(struct mdio_device *pcs,
- struct phylink_link_state *state);
-
+++ /dev/null
-From 796d709363135a6bd6a8ccc07b509c939e5b855f Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Tue, 23 May 2023 16:31:50 +0100
-Subject: [PATCH 19/21] net: phylink: provide phylink_pcs_config() and
- phylink_pcs_link_up()
-
-Add two helper functions for calling PCS methods. phylink_pcs_config()
-allows us to handle PCS configuration specifics in one location, rather
-than the two call sites. phylink_pcs_link_up() gives us consistency.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Link: https://lore.kernel.org/r/E1q1TzK-007Exd-Rs@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/phylink.c | 53 ++++++++++++++++++++++++---------------
- 1 file changed, 33 insertions(+), 20 deletions(-)
-
---- a/drivers/net/phy/phylink.c
-+++ b/drivers/net/phy/phylink.c
-@@ -991,6 +991,25 @@ static void phylink_resolve_an_pause(str
- }
- }
-
-+static int phylink_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
-+ const struct phylink_link_state *state,
-+ bool permit_pause_to_mac)
-+{
-+ if (!pcs)
-+ return 0;
-+
-+ return pcs->ops->pcs_config(pcs, mode, state->interface,
-+ state->advertising, permit_pause_to_mac);
-+}
-+
-+static void phylink_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
-+ phy_interface_t interface, int speed,
-+ int duplex)
-+{
-+ if (pcs && pcs->ops->pcs_link_up)
-+ pcs->ops->pcs_link_up(pcs, mode, interface, speed, duplex);
-+}
-+
- static void phylink_pcs_poll_stop(struct phylink *pl)
- {
- if (pl->cfg_link_an_mode == MLO_AN_INBAND)
-@@ -1074,18 +1093,15 @@ static void phylink_major_config(struct
-
- phylink_mac_config(pl, state);
-
-- if (pl->pcs) {
-- err = pl->pcs->ops->pcs_config(pl->pcs, pl->cur_link_an_mode,
-- state->interface,
-- state->advertising,
-- !!(pl->link_config.pause &
-- MLO_PAUSE_AN));
-- if (err < 0)
-- phylink_err(pl, "pcs_config failed: %pe\n",
-- ERR_PTR(err));
-- if (err > 0)
-- restart = true;
-- }
-+ err = phylink_pcs_config(pl->pcs, pl->cur_link_an_mode, state,
-+ !!(pl->link_config.pause &
-+ MLO_PAUSE_AN));
-+ if (err < 0)
-+ phylink_err(pl, "pcs_config failed: %pe\n",
-+ ERR_PTR(err));
-+ else if (err > 0)
-+ restart = true;
-+
- if (restart)
- phylink_mac_pcs_an_restart(pl);
-
-@@ -1136,11 +1152,9 @@ static int phylink_change_inband_advert(
- * restart negotiation if the pcs_config() helper indicates that
- * the programmed advertisement has changed.
- */
-- ret = pl->pcs->ops->pcs_config(pl->pcs, pl->cur_link_an_mode,
-- pl->link_config.interface,
-- pl->link_config.advertising,
-- !!(pl->link_config.pause &
-- MLO_PAUSE_AN));
-+ ret = phylink_pcs_config(pl->pcs, pl->cur_link_an_mode,
-+ &pl->link_config,
-+ !!(pl->link_config.pause & MLO_PAUSE_AN));
- if (ret < 0)
- return ret;
-
-@@ -1272,9 +1286,8 @@ static void phylink_link_up(struct phyli
-
- pl->cur_interface = link_state.interface;
-
-- if (pl->pcs && pl->pcs->ops->pcs_link_up)
-- pl->pcs->ops->pcs_link_up(pl->pcs, pl->cur_link_an_mode,
-- pl->cur_interface, speed, duplex);
-+ phylink_pcs_link_up(pl->pcs, pl->cur_link_an_mode, pl->cur_interface,
-+ speed, duplex);
-
- pl->mac_ops->mac_link_up(pl->config, pl->phydev, pl->cur_link_an_mode,
- pl->cur_interface, speed, duplex,
+++ /dev/null
-From 11933aa76865621d8e82553c8f3bc07796a5aaa2 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Thu, 1 Jun 2023 10:12:06 +0100
-Subject: [PATCH 20/21] net: phylink: actually fix ksettings_set() ethtool call
-
-Raju Lakkaraju reported that the below commit caused a regression
-with Lan743x drivers and a 2.5G SFP. Sadly, this is because the commit
-was utterly wrong. Let's fix this properly by not moving the
-linkmode_and(), but instead copying the link ksettings and then
-modifying the advertising mask before passing the modified link
-ksettings to phylib.
-
-Fixes: df0acdc59b09 ("net: phylink: fix ksettings_set() ethtool call")
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Link: https://lore.kernel.org/r/E1q4eLm-00Ayxk-GZ@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/phylink.c | 15 ++++++++++-----
- 1 file changed, 10 insertions(+), 5 deletions(-)
-
---- a/drivers/net/phy/phylink.c
-+++ b/drivers/net/phy/phylink.c
-@@ -2259,11 +2259,13 @@ int phylink_ethtool_ksettings_set(struct
-
- ASSERT_RTNL();
-
-- /* Mask out unsupported advertisements */
-- linkmode_and(config.advertising, kset->link_modes.advertising,
-- pl->supported);
--
- if (pl->phydev) {
-+ struct ethtool_link_ksettings phy_kset = *kset;
-+
-+ linkmode_and(phy_kset.link_modes.advertising,
-+ phy_kset.link_modes.advertising,
-+ pl->supported);
-+
- /* We can rely on phylib for this update; we also do not need
- * to update the pl->link_config settings:
- * - the configuration returned via ksettings_get() will come
-@@ -2282,10 +2284,13 @@ int phylink_ethtool_ksettings_set(struct
- * the presence of a PHY, this should not be changed as that
- * should be determined from the media side advertisement.
- */
-- return phy_ethtool_ksettings_set(pl->phydev, kset);
-+ return phy_ethtool_ksettings_set(pl->phydev, &phy_kset);
- }
-
- config = pl->link_config;
-+ /* Mask out unsupported advertisements */
-+ linkmode_and(config.advertising, kset->link_modes.advertising,
-+ pl->supported);
-
- /* FIXME: should we reject autoneg if phy/mac does not support it? */
- switch (kset->base.autoneg) {
+++ /dev/null
-From 79b07c3e9c4a2272927be8848c26b372516e1958 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Fri, 16 Jun 2023 13:06:22 +0100
-Subject: [PATCH 21/21] net: phylink: add PCS negotiation mode
-
-PCS have to work out whether they should enable PCS negotiation by
-looking at the "mode" and "interface" arguments, and the Autoneg bit
-in the advertising mask.
-
-This leads to some complex logic, so lets pull that out into phylink
-and instead pass a "neg_mode" argument to the PCS configuration and
-link up methods, instead of the "mode" argument.
-
-In order to transition drivers, add a "neg_mode" flag to the phylink
-PCS structure to PCS can indicate whether they want to be passed the
-neg_mode or the old mode argument.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Link: https://lore.kernel.org/r/E1qA8De-00EaFA-Ht@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/phylink.c | 45 +++++++++++++----
- include/linux/phylink.h | 104 +++++++++++++++++++++++++++++++++++---
- 2 files changed, 132 insertions(+), 17 deletions(-)
-
---- a/drivers/net/phy/phylink.c
-+++ b/drivers/net/phy/phylink.c
-@@ -71,6 +71,7 @@ struct phylink {
- struct mutex state_mutex;
- struct phylink_link_state phy_state;
- struct work_struct resolve;
-+ unsigned int pcs_neg_mode;
-
- bool mac_link_dropped;
- bool using_mac_select_pcs;
-@@ -991,23 +992,23 @@ static void phylink_resolve_an_pause(str
- }
- }
-
--static int phylink_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
-+static int phylink_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
- const struct phylink_link_state *state,
- bool permit_pause_to_mac)
- {
- if (!pcs)
- return 0;
-
-- return pcs->ops->pcs_config(pcs, mode, state->interface,
-+ return pcs->ops->pcs_config(pcs, neg_mode, state->interface,
- state->advertising, permit_pause_to_mac);
- }
-
--static void phylink_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
-+static void phylink_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
- phy_interface_t interface, int speed,
- int duplex)
- {
- if (pcs && pcs->ops->pcs_link_up)
-- pcs->ops->pcs_link_up(pcs, mode, interface, speed, duplex);
-+ pcs->ops->pcs_link_up(pcs, neg_mode, interface, speed, duplex);
- }
-
- static void phylink_pcs_poll_stop(struct phylink *pl)
-@@ -1057,10 +1058,15 @@ static void phylink_major_config(struct
- struct phylink_pcs *pcs = NULL;
- bool pcs_changed = false;
- unsigned int rate_kbd;
-+ unsigned int neg_mode;
- int err;
-
- phylink_dbg(pl, "major config %s\n", phy_modes(state->interface));
-
-+ pl->pcs_neg_mode = phylink_pcs_neg_mode(pl->cur_link_an_mode,
-+ state->interface,
-+ state->advertising);
-+
- if (pl->using_mac_select_pcs) {
- pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
- if (IS_ERR(pcs)) {
-@@ -1093,9 +1099,12 @@ static void phylink_major_config(struct
-
- phylink_mac_config(pl, state);
-
-- err = phylink_pcs_config(pl->pcs, pl->cur_link_an_mode, state,
-- !!(pl->link_config.pause &
-- MLO_PAUSE_AN));
-+ neg_mode = pl->cur_link_an_mode;
-+ if (pl->pcs && pl->pcs->neg_mode)
-+ neg_mode = pl->pcs_neg_mode;
-+
-+ err = phylink_pcs_config(pl->pcs, neg_mode, state,
-+ !!(pl->link_config.pause & MLO_PAUSE_AN));
- if (err < 0)
- phylink_err(pl, "pcs_config failed: %pe\n",
- ERR_PTR(err));
-@@ -1130,6 +1139,7 @@ static void phylink_major_config(struct
- */
- static int phylink_change_inband_advert(struct phylink *pl)
- {
-+ unsigned int neg_mode;
- int ret;
-
- if (test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state))
-@@ -1148,12 +1158,20 @@ static int phylink_change_inband_advert(
- __ETHTOOL_LINK_MODE_MASK_NBITS, pl->link_config.advertising,
- pl->link_config.pause);
-
-+ /* Recompute the PCS neg mode */
-+ pl->pcs_neg_mode = phylink_pcs_neg_mode(pl->cur_link_an_mode,
-+ pl->link_config.interface,
-+ pl->link_config.advertising);
-+
-+ neg_mode = pl->cur_link_an_mode;
-+ if (pl->pcs->neg_mode)
-+ neg_mode = pl->pcs_neg_mode;
-+
- /* Modern PCS-based method; update the advert at the PCS, and
- * restart negotiation if the pcs_config() helper indicates that
- * the programmed advertisement has changed.
- */
-- ret = phylink_pcs_config(pl->pcs, pl->cur_link_an_mode,
-- &pl->link_config,
-+ ret = phylink_pcs_config(pl->pcs, neg_mode, &pl->link_config,
- !!(pl->link_config.pause & MLO_PAUSE_AN));
- if (ret < 0)
- return ret;
-@@ -1256,6 +1274,7 @@ static void phylink_link_up(struct phyli
- struct phylink_link_state link_state)
- {
- struct net_device *ndev = pl->netdev;
-+ unsigned int neg_mode;
- int speed, duplex;
- bool rx_pause;
-
-@@ -1286,8 +1305,12 @@ static void phylink_link_up(struct phyli
-
- pl->cur_interface = link_state.interface;
-
-- phylink_pcs_link_up(pl->pcs, pl->cur_link_an_mode, pl->cur_interface,
-- speed, duplex);
-+ neg_mode = pl->cur_link_an_mode;
-+ if (pl->pcs && pl->pcs->neg_mode)
-+ neg_mode = pl->pcs_neg_mode;
-+
-+ phylink_pcs_link_up(pl->pcs, neg_mode, pl->cur_interface, speed,
-+ duplex);
-
- pl->mac_ops->mac_link_up(pl->config, pl->phydev, pl->cur_link_an_mode,
- pl->cur_interface, speed, duplex,
---- a/include/linux/phylink.h
-+++ b/include/linux/phylink.h
-@@ -21,6 +21,24 @@ enum {
- MLO_AN_FIXED, /* Fixed-link mode */
- MLO_AN_INBAND, /* In-band protocol */
-
-+ /* PCS "negotiation" mode.
-+ * PHYLINK_PCS_NEG_NONE - protocol has no inband capability
-+ * PHYLINK_PCS_NEG_OUTBAND - some out of band or fixed link setting
-+ * PHYLINK_PCS_NEG_INBAND_DISABLED - inband mode disabled, e.g.
-+ * 1000base-X with autoneg off
-+ * PHYLINK_PCS_NEG_INBAND_ENABLED - inband mode enabled
-+ * Additionally, this can be tested using bitmasks:
-+ * PHYLINK_PCS_NEG_INBAND - inband mode selected
-+ * PHYLINK_PCS_NEG_ENABLED - negotiation mode enabled
-+ */
-+ PHYLINK_PCS_NEG_NONE = 0,
-+ PHYLINK_PCS_NEG_ENABLED = BIT(4),
-+ PHYLINK_PCS_NEG_OUTBAND = BIT(5),
-+ PHYLINK_PCS_NEG_INBAND = BIT(6),
-+ PHYLINK_PCS_NEG_INBAND_DISABLED = PHYLINK_PCS_NEG_INBAND,
-+ PHYLINK_PCS_NEG_INBAND_ENABLED = PHYLINK_PCS_NEG_INBAND |
-+ PHYLINK_PCS_NEG_ENABLED,
-+
- /* MAC_SYM_PAUSE and MAC_ASYM_PAUSE are used when configuring our
- * autonegotiation advertisement. They correspond to the PAUSE and
- * ASM_DIR bits defined by 802.3, respectively.
-@@ -80,6 +98,70 @@ static inline bool phylink_autoneg_inban
- }
-
- /**
-+ * phylink_pcs_neg_mode() - helper to determine PCS inband mode
-+ * @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND.
-+ * @interface: interface mode to be used
-+ * @advertising: adertisement ethtool link mode mask
-+ *
-+ * Determines the negotiation mode to be used by the PCS, and returns
-+ * one of:
-+ * %PHYLINK_PCS_NEG_NONE: interface mode does not support inband
-+ * %PHYLINK_PCS_NEG_OUTBAND: an out of band mode (e.g. reading the PHY)
-+ * will be used.
-+ * %PHYLINK_PCS_NEG_INBAND_DISABLED: inband mode selected but autoneg disabled
-+ * %PHYLINK_PCS_NEG_INBAND_ENABLED: inband mode selected and autoneg enabled
-+ *
-+ * Note: this is for cases where the PCS itself is involved in negotiation
-+ * (e.g. Clause 37, SGMII and similar) not Clause 73.
-+ */
-+static inline unsigned int phylink_pcs_neg_mode(unsigned int mode,
-+ phy_interface_t interface,
-+ const unsigned long *advertising)
-+{
-+ unsigned int neg_mode;
-+
-+ switch (interface) {
-+ case PHY_INTERFACE_MODE_SGMII:
-+ case PHY_INTERFACE_MODE_QSGMII:
-+ case PHY_INTERFACE_MODE_QUSGMII:
-+ case PHY_INTERFACE_MODE_USXGMII:
-+ /* These protocols are designed for use with a PHY which
-+ * communicates its negotiation result back to the MAC via
-+ * inband communication. Note: there exist PHYs that run
-+ * with SGMII but do not send the inband data.
-+ */
-+ if (!phylink_autoneg_inband(mode))
-+ neg_mode = PHYLINK_PCS_NEG_OUTBAND;
-+ else
-+ neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
-+ break;
-+
-+ case PHY_INTERFACE_MODE_1000BASEX:
-+ case PHY_INTERFACE_MODE_2500BASEX:
-+ /* 1000base-X is designed for use media-side for Fibre
-+ * connections, and thus the Autoneg bit needs to be
-+ * taken into account. We also do this for 2500base-X
-+ * as well, but drivers may not support this, so may
-+ * need to override this.
-+ */
-+ if (!phylink_autoneg_inband(mode))
-+ neg_mode = PHYLINK_PCS_NEG_OUTBAND;
-+ else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
-+ advertising))
-+ neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
-+ else
-+ neg_mode = PHYLINK_PCS_NEG_INBAND_DISABLED;
-+ break;
-+
-+ default:
-+ neg_mode = PHYLINK_PCS_NEG_NONE;
-+ break;
-+ }
-+
-+ return neg_mode;
-+}
-+
-+/**
- * struct phylink_link_state - link state structure
- * @advertising: ethtool bitmask containing advertised link modes
- * @lp_advertising: ethtool bitmask containing link partner advertised link
-@@ -436,6 +518,7 @@ struct phylink_pcs_ops;
- /**
- * struct phylink_pcs - PHYLINK PCS instance
- * @ops: a pointer to the &struct phylink_pcs_ops structure
-+ * @neg_mode: provide PCS neg mode via "mode" argument
- * @poll: poll the PCS for link changes
- *
- * This structure is designed to be embedded within the PCS private data,
-@@ -443,6 +526,7 @@ struct phylink_pcs_ops;
- */
- struct phylink_pcs {
- const struct phylink_pcs_ops *ops;
-+ bool neg_mode;
- bool poll;
- };
-
-@@ -460,12 +544,12 @@ struct phylink_pcs_ops {
- const struct phylink_link_state *state);
- void (*pcs_get_state)(struct phylink_pcs *pcs,
- struct phylink_link_state *state);
-- int (*pcs_config)(struct phylink_pcs *pcs, unsigned int mode,
-+ int (*pcs_config)(struct phylink_pcs *pcs, unsigned int neg_mode,
- phy_interface_t interface,
- const unsigned long *advertising,
- bool permit_pause_to_mac);
- void (*pcs_an_restart)(struct phylink_pcs *pcs);
-- void (*pcs_link_up)(struct phylink_pcs *pcs, unsigned int mode,
-+ void (*pcs_link_up)(struct phylink_pcs *pcs, unsigned int neg_mode,
- phy_interface_t interface, int speed, int duplex);
- };
-
-@@ -508,7 +592,7 @@ void pcs_get_state(struct phylink_pcs *p
- /**
- * pcs_config() - Configure the PCS mode and advertisement
- * @pcs: a pointer to a &struct phylink_pcs.
-- * @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND.
-+ * @neg_mode: link negotiation mode (see below)
- * @interface: interface mode to be used
- * @advertising: adertisement ethtool link mode mask
- * @permit_pause_to_mac: permit forwarding pause resolution to MAC
-@@ -526,8 +610,12 @@ void pcs_get_state(struct phylink_pcs *p
- * For 1000BASE-X, the advertisement should be programmed into the PCS.
- *
- * For most 10GBASE-R, there is no advertisement.
-+ *
-+ * The %neg_mode argument should be tested via the phylink_mode_*() family of
-+ * functions, or for PCS that set pcs->neg_mode true, should be tested
-+ * against the %PHYLINK_PCS_NEG_* definitions.
- */
--int pcs_config(struct phylink_pcs *pcs, unsigned int mode,
-+int pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
- phy_interface_t interface, const unsigned long *advertising,
- bool permit_pause_to_mac);
-
-@@ -543,7 +631,7 @@ void pcs_an_restart(struct phylink_pcs *
- /**
- * pcs_link_up() - program the PCS for the resolved link configuration
- * @pcs: a pointer to a &struct phylink_pcs.
-- * @mode: link autonegotiation mode
-+ * @neg_mode: link negotiation mode (see below)
- * @interface: link &typedef phy_interface_t mode
- * @speed: link speed
- * @duplex: link duplex
-@@ -552,8 +640,12 @@ void pcs_an_restart(struct phylink_pcs *
- * the resolved link parameters. For example, a PCS operating in SGMII
- * mode without in-band AN needs to be manually configured for the link
- * and duplex setting. Otherwise, this should be a no-op.
-+ *
-+ * The %mode argument should be tested via the phylink_mode_*() family of
-+ * functions, or for PCS that set pcs->neg_mode true, should be tested
-+ * against the %PHYLINK_PCS_NEG_* definitions.
- */
--void pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
-+void pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
- phy_interface_t interface, int speed, int duplex);
- #endif
-
+++ /dev/null
-From cdb08aa0473730315dbc088d5394e59622314034 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Fri, 16 Jun 2023 13:06:27 +0100
-Subject: [PATCH 1/2] net: phylink: convert phylink_mii_c22_pcs_config() to
- neg_mode
-
-Use phylink_pcs_neg_mode() for phylink_mii_c22_pcs_config(). This
-results in no functional change.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Link: https://lore.kernel.org/r/E1qA8Dj-00EaFG-Mt@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/phylink.c | 9 ++++-----
- 1 file changed, 4 insertions(+), 5 deletions(-)
-
---- a/drivers/net/phy/phylink.c
-+++ b/drivers/net/phy/phylink.c
-@@ -3558,6 +3558,7 @@ int phylink_mii_c22_pcs_config(struct md
- phy_interface_t interface,
- const unsigned long *advertising)
- {
-+ unsigned int neg_mode;
- bool changed = 0;
- u16 bmcr;
- int ret, adv;
-@@ -3571,15 +3572,13 @@ int phylink_mii_c22_pcs_config(struct md
- changed = ret;
- }
-
-- /* Ensure ISOLATE bit is disabled */
-- if (mode == MLO_AN_INBAND &&
-- (interface == PHY_INTERFACE_MODE_SGMII ||
-- interface == PHY_INTERFACE_MODE_QSGMII ||
-- linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, advertising)))
-+ neg_mode = phylink_pcs_neg_mode(mode, interface, advertising);
-+ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
- bmcr = BMCR_ANENABLE;
- else
- bmcr = 0;
-
-+ /* Configure the inband state. Ensure ISOLATE bit is disabled */
- ret = mdiodev_modify(pcs, MII_BMCR, BMCR_ANENABLE | BMCR_ISOLATE, bmcr);
- if (ret < 0)
- return ret;
+++ /dev/null
-From febf2aaf05641f3258cc30e072aff65cffc7c82c Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Fri, 16 Jun 2023 13:06:32 +0100
-Subject: [PATCH 2/2] net: phylink: pass neg_mode into
- phylink_mii_c22_pcs_config()
-
-Convert fman_dtsec, xilinx_axienet and pcs-lynx to pass the neg_mode
-into phylink_mii_c22_pcs_config(). Where appropriate, drivers are
-updated to have neg_mode passed into their pcs_config() and
-pcs_link_up() functions. For other drivers, we just hoist the call
-to phylink_pcs_neg_mode() to their pcs_config() method out of
-phylink_mii_c22_pcs_config().
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Link: https://lore.kernel.org/r/E1qA8Do-00EaFM-Ra@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- .../net/ethernet/freescale/fman/fman_dtsec.c | 7 ++++---
- .../net/ethernet/xilinx/xilinx_axienet_main.c | 6 ++++--
- drivers/net/pcs/pcs-lynx.c | 18 ++++++++++++------
- drivers/net/phy/phylink.c | 9 ++++-----
- include/linux/phylink.h | 5 +++--
- 5 files changed, 27 insertions(+), 18 deletions(-)
-
---- a/drivers/net/ethernet/freescale/fman/fman_dtsec.c
-+++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c
-@@ -763,15 +763,15 @@ static void dtsec_pcs_get_state(struct p
- phylink_mii_c22_pcs_get_state(dtsec->tbidev, state);
- }
-
--static int dtsec_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
-+static int dtsec_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
- phy_interface_t interface,
- const unsigned long *advertising,
- bool permit_pause_to_mac)
- {
- struct fman_mac *dtsec = pcs_to_dtsec(pcs);
-
-- return phylink_mii_c22_pcs_config(dtsec->tbidev, mode, interface,
-- advertising);
-+ return phylink_mii_c22_pcs_config(dtsec->tbidev, interface,
-+ advertising, neg_mode);
- }
-
- static void dtsec_pcs_an_restart(struct phylink_pcs *pcs)
-@@ -1447,6 +1447,7 @@ int dtsec_initialization(struct mac_devi
- goto _return_fm_mac_free;
- }
- dtsec->pcs.ops = &dtsec_pcs_ops;
-+ dtsec->pcs.neg_mode = true;
- dtsec->pcs.poll = true;
-
- supported = mac_dev->phylink_config.supported_interfaces;
---- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
-+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
-@@ -1631,7 +1631,7 @@ static void axienet_pcs_an_restart(struc
- phylink_mii_c22_pcs_an_restart(pcs_phy);
- }
-
--static int axienet_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
-+static int axienet_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
- phy_interface_t interface,
- const unsigned long *advertising,
- bool permit_pause_to_mac)
-@@ -1653,7 +1653,8 @@ static int axienet_pcs_config(struct phy
- }
- }
-
-- ret = phylink_mii_c22_pcs_config(pcs_phy, mode, interface, advertising);
-+ ret = phylink_mii_c22_pcs_config(pcs_phy, interface, advertising,
-+ neg_mode);
- if (ret < 0)
- netdev_warn(ndev, "Failed to configure PCS: %d\n", ret);
-
-@@ -2129,6 +2130,7 @@ static int axienet_probe(struct platform
- }
- of_node_put(np);
- lp->pcs.ops = &axienet_pcs_ops;
-+ lp->pcs.neg_mode = true;
- lp->pcs.poll = true;
- }
-
---- a/drivers/net/pcs/pcs-lynx.c
-+++ b/drivers/net/pcs/pcs-lynx.c
-@@ -122,9 +122,10 @@ static void lynx_pcs_get_state(struct ph
- state->link, state->an_complete);
- }
-
--static int lynx_pcs_config_giga(struct mdio_device *pcs, unsigned int mode,
-+static int lynx_pcs_config_giga(struct mdio_device *pcs,
- phy_interface_t interface,
-- const unsigned long *advertising)
-+ const unsigned long *advertising,
-+ unsigned int neg_mode)
- {
- u32 link_timer;
- u16 if_mode;
-@@ -137,8 +138,9 @@ static int lynx_pcs_config_giga(struct m
-
- if_mode = 0;
- } else {
-+ /* SGMII and QSGMII */
- if_mode = IF_MODE_SGMII_EN;
-- if (mode == MLO_AN_INBAND) {
-+ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
- if_mode |= IF_MODE_USE_SGMII_AN;
-
- /* Adjust link timer for SGMII */
-@@ -154,7 +156,8 @@ static int lynx_pcs_config_giga(struct m
- if (err)
- return err;
-
-- return phylink_mii_c22_pcs_config(pcs, mode, interface, advertising);
-+ return phylink_mii_c22_pcs_config(pcs, interface, advertising,
-+ neg_mode);
- }
-
- static int lynx_pcs_config_usxgmii(struct mdio_device *pcs, unsigned int mode,
-@@ -181,13 +184,16 @@ static int lynx_pcs_config(struct phylin
- bool permit)
- {
- struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs);
-+ unsigned int neg_mode;
-+
-+ neg_mode = phylink_pcs_neg_mode(mode, ifmode, advertising);
-
- switch (ifmode) {
- case PHY_INTERFACE_MODE_1000BASEX:
- case PHY_INTERFACE_MODE_SGMII:
- case PHY_INTERFACE_MODE_QSGMII:
-- return lynx_pcs_config_giga(lynx->mdio, mode, ifmode,
-- advertising);
-+ return lynx_pcs_config_giga(lynx->mdio, ifmode, advertising,
-+ neg_mode);
- case PHY_INTERFACE_MODE_2500BASEX:
- if (phylink_autoneg_inband(mode)) {
- dev_err(&lynx->mdio->dev,
---- a/drivers/net/phy/phylink.c
-+++ b/drivers/net/phy/phylink.c
-@@ -3545,20 +3545,20 @@ EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_en
- /**
- * phylink_mii_c22_pcs_config() - configure clause 22 PCS
- * @pcs: a pointer to a &struct mdio_device.
-- * @mode: link autonegotiation mode
- * @interface: the PHY interface mode being configured
- * @advertising: the ethtool advertisement mask
-+ * @neg_mode: PCS negotiation mode
- *
- * Configure a Clause 22 PCS PHY with the appropriate negotiation
- * parameters for the @mode, @interface and @advertising parameters.
- * Returns negative error number on failure, zero if the advertisement
- * has not changed, or positive if there is a change.
- */
--int phylink_mii_c22_pcs_config(struct mdio_device *pcs, unsigned int mode,
-+int phylink_mii_c22_pcs_config(struct mdio_device *pcs,
- phy_interface_t interface,
-- const unsigned long *advertising)
-+ const unsigned long *advertising,
-+ unsigned int neg_mode)
- {
-- unsigned int neg_mode;
- bool changed = 0;
- u16 bmcr;
- int ret, adv;
-@@ -3572,7 +3572,6 @@ int phylink_mii_c22_pcs_config(struct md
- changed = ret;
- }
-
-- neg_mode = phylink_pcs_neg_mode(mode, interface, advertising);
- if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
- bmcr = BMCR_ANENABLE;
- else
---- a/include/linux/phylink.h
-+++ b/include/linux/phylink.h
-@@ -743,9 +743,10 @@ void phylink_mii_c22_pcs_get_state(struc
- struct phylink_link_state *state);
- int phylink_mii_c22_pcs_encode_advertisement(phy_interface_t interface,
- const unsigned long *advertising);
--int phylink_mii_c22_pcs_config(struct mdio_device *pcs, unsigned int mode,
-+int phylink_mii_c22_pcs_config(struct mdio_device *pcs,
- phy_interface_t interface,
-- const unsigned long *advertising);
-+ const unsigned long *advertising,
-+ unsigned int neg_mode);
- void phylink_mii_c22_pcs_an_restart(struct mdio_device *pcs);
-
- void phylink_resolve_c73(struct phylink_link_state *state);
+++ /dev/null
-From 3b2de56a146f34e3f70a84cc3a1897064e445d16 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Fri, 16 Jun 2023 13:06:43 +0100
-Subject: [PATCH] net: pcs: lynxi: update PCS driver to use neg_mode
-
-Update the Lynxi PCS driver to use neg_mode rather than the mode
-argument. This ensures that the link_up() method will always program
-the speed and duplex when negotiation is disabled.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Link: https://lore.kernel.org/r/E1qA8Dz-00EaFY-5A@rmk-PC.armlinux.org.uk
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/pcs/pcs-mtk-lynxi.c | 39 ++++++++++++++-------------------
- 1 file changed, 16 insertions(+), 23 deletions(-)
-
---- a/drivers/net/pcs/pcs-mtk-lynxi.c
-+++ b/drivers/net/pcs/pcs-mtk-lynxi.c
-@@ -102,13 +102,13 @@ static void mtk_pcs_lynxi_get_state(stru
- FIELD_GET(SGMII_LPA, adv));
- }
-
--static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int mode,
-+static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int neg_mode,
- phy_interface_t interface,
- const unsigned long *advertising,
- bool permit_pause_to_mac)
- {
- struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
-- bool mode_changed = false, changed, use_an;
-+ bool mode_changed = false, changed;
- unsigned int rgc3, sgm_mode, bmcr;
- int advertise, link_timer;
-
-@@ -121,30 +121,22 @@ static int mtk_pcs_lynxi_config(struct p
- * we assume that fixes it's speed at bitrate = line rate (in
- * other words, 1000Mbps or 2500Mbps).
- */
-- if (interface == PHY_INTERFACE_MODE_SGMII) {
-+ if (interface == PHY_INTERFACE_MODE_SGMII)
- sgm_mode = SGMII_IF_MODE_SGMII;
-- if (phylink_autoneg_inband(mode)) {
-- sgm_mode |= SGMII_REMOTE_FAULT_DIS |
-- SGMII_SPEED_DUPLEX_AN;
-- use_an = true;
-- } else {
-- use_an = false;
-- }
-- } else if (phylink_autoneg_inband(mode)) {
-- /* 1000base-X or 2500base-X autoneg */
-- sgm_mode = SGMII_REMOTE_FAULT_DIS;
-- use_an = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
-- advertising);
-- } else {
-+ else
- /* 1000base-X or 2500base-X without autoneg */
- sgm_mode = 0;
-- use_an = false;
-- }
-
-- if (use_an)
-+ if (neg_mode & PHYLINK_PCS_NEG_INBAND)
-+ sgm_mode |= SGMII_REMOTE_FAULT_DIS;
-+
-+ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
-+ if (interface == PHY_INTERFACE_MODE_SGMII)
-+ sgm_mode |= SGMII_SPEED_DUPLEX_AN;
- bmcr = BMCR_ANENABLE;
-- else
-+ } else {
- bmcr = 0;
-+ }
-
- if (mpcs->interface != interface) {
- link_timer = phylink_get_link_timer_ns(interface);
-@@ -216,14 +208,15 @@ static void mtk_pcs_lynxi_restart_an(str
- regmap_set_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, BMCR_ANRESTART);
- }
-
--static void mtk_pcs_lynxi_link_up(struct phylink_pcs *pcs, unsigned int mode,
-+static void mtk_pcs_lynxi_link_up(struct phylink_pcs *pcs,
-+ unsigned int neg_mode,
- phy_interface_t interface, int speed,
- int duplex)
- {
- struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
- unsigned int sgm_mode;
-
-- if (!phylink_autoneg_inband(mode)) {
-+ if (neg_mode != PHYLINK_PCS_NEG_INBAND_ENABLED) {
- /* Force the speed and duplex setting */
- if (speed == SPEED_10)
- sgm_mode = SGMII_SPEED_10;
-@@ -286,6 +279,7 @@ struct phylink_pcs *mtk_pcs_lynxi_create
- mpcs->regmap = regmap;
- mpcs->flags = flags;
- mpcs->pcs.ops = &mtk_pcs_lynxi_ops;
-+ mpcs->pcs.neg_mode = true;
- mpcs->pcs.poll = true;
- mpcs->interface = PHY_INTERFACE_MODE_NA;
-
+++ /dev/null
-From 459fd2f11204c962e3153020f4f56748e0e10afb Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Tue, 21 Mar 2023 15:58:49 +0000
-Subject: [PATCH] net: pcs: xpcs: use Autoneg bit rather than an_enabled
-
-The Autoneg bit in the advertising bitmap and state->an_enabled are
-always identical. Thus, we will be removing state->an_enabled.
-
-Use the Autoneg bit in the advertising bitmap to indicate whether
-autonegotiation should be used, rather than using the an_enabled
-member which will be going away.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Reviewed-by: Simon Horman <simon.horman@corigine.com>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/pcs/pcs-xpcs.c | 10 +++++++---
- 1 file changed, 7 insertions(+), 3 deletions(-)
-
---- a/drivers/net/pcs/pcs-xpcs.c
-+++ b/drivers/net/pcs/pcs-xpcs.c
-@@ -931,6 +931,7 @@ static int xpcs_get_state_c73(struct dw_
- struct phylink_link_state *state,
- const struct xpcs_compat *compat)
- {
-+ bool an_enabled;
- int ret;
-
- /* Link needs to be read first ... */
-@@ -948,11 +949,13 @@ static int xpcs_get_state_c73(struct dw_
- return xpcs_do_config(xpcs, state->interface, MLO_AN_INBAND, NULL);
- }
-
-- if (state->an_enabled && xpcs_aneg_done_c73(xpcs, state, compat)) {
-+ an_enabled = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
-+ state->advertising);
-+ if (an_enabled && xpcs_aneg_done_c73(xpcs, state, compat)) {
- state->an_complete = true;
- xpcs_read_lpa_c73(xpcs, state);
- xpcs_resolve_lpa_c73(xpcs, state);
-- } else if (state->an_enabled) {
-+ } else if (an_enabled) {
- state->link = 0;
- } else if (state->link) {
- xpcs_resolve_pma(xpcs, state);
-@@ -1007,7 +1010,8 @@ static int xpcs_get_state_c37_1000basex(
- {
- int lpa, bmsr;
-
-- if (state->an_enabled) {
-+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
-+ state->advertising)) {
- /* Reset link state */
- state->link = false;
-
+++ /dev/null
-From 43fb622d91a9f408322735d2f736495c1009f575 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Tue, 9 May 2023 12:50:04 +0100
-Subject: [PATCH] net: pcs: xpcs: fix incorrect number of interfaces
-
-In synopsys_xpcs_compat[], the DW_XPCS_2500BASEX entry was setting
-the number of interfaces using the xpcs_2500basex_features array
-rather than xpcs_2500basex_interfaces. This causes us to overflow
-the array of interfaces. Fix this.
-
-Fixes: f27abde3042a ("net: pcs: add 2500BASEX support for Intel mGbE controller")
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Reviewed-by: Leon Romanovsky <leonro@nvidia.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/pcs/pcs-xpcs.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/net/pcs/pcs-xpcs.c
-+++ b/drivers/net/pcs/pcs-xpcs.c
-@@ -1211,7 +1211,7 @@ static const struct xpcs_compat synopsys
- [DW_XPCS_2500BASEX] = {
- .supported = xpcs_2500basex_features,
- .interface = xpcs_2500basex_interfaces,
-- .num_interfaces = ARRAY_SIZE(xpcs_2500basex_features),
-+ .num_interfaces = ARRAY_SIZE(xpcs_2500basex_interfaces),
- .an_mode = DW_2500BASEX,
- },
- };
+++ /dev/null
-From 1b9827ceab08450308f7971d6fd700ec88b6ce67 Mon Sep 17 00:00:00 2001
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Sat, 3 Dec 2022 14:20:37 +0100
-Subject: [PATCH 098/250] net: mtk_eth_soc: enable flow offload support for
- MT7986 SoC
-
-Since Wireless Ethernet Dispatcher is now available for mt7986 in mt76,
-enable hw flow support for MT7986 SoC.
-
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Link: https://lore.kernel.org/r/fdcaacd827938e6a8c4aa1ac2c13e46d2c08c821.1670072898.git.lorenzo@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 1 +
- 1 file changed, 1 insertion(+)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -4333,6 +4333,7 @@ static const struct mtk_soc_data mt7986_
- .hw_features = MTK_HW_FEATURES,
- .required_clks = MT7986_CLKS_BITMAP,
- .required_pctl = false,
-+ .offload_version = 2,
- .hash_offset = 4,
- .foe_entry_size = sizeof(struct mtk_foe_entry),
- .txrx = {
+++ /dev/null
-From: Sujuan Chen <sujuan.chen@mediatek.com>
-Date: Sat, 5 Nov 2022 23:36:18 +0100
-Subject: [PATCH] net: ethernet: mtk_wed: introduce wed mcu support
-
-Introduce WED mcu support used to configure WED WO chip.
-This is a preliminary patch in order to add RX Wireless
-Ethernet Dispatch available on MT7986 SoC.
-
-Tested-by: Daniel Golle <daniel@makrotopia.org>
-Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- create mode 100644 drivers/net/ethernet/mediatek/mtk_wed_mcu.c
- create mode 100644 drivers/net/ethernet/mediatek/mtk_wed_wo.h
-
---- a/drivers/net/ethernet/mediatek/Makefile
-+++ b/drivers/net/ethernet/mediatek/Makefile
-@@ -5,7 +5,7 @@
-
- obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o
- mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o mtk_ppe_offload.o
--mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed.o
-+mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed.o mtk_wed_mcu.o
- ifdef CONFIG_DEBUG_FS
- mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed_debugfs.o
- endif
---- /dev/null
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
-@@ -0,0 +1,359 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+/* Copyright (C) 2022 MediaTek Inc.
-+ *
-+ * Author: Lorenzo Bianconi <lorenzo@kernel.org>
-+ * Sujuan Chen <sujuan.chen@mediatek.com>
-+ */
-+
-+#include <linux/firmware.h>
-+#include <linux/of_address.h>
-+#include <linux/of_reserved_mem.h>
-+#include <linux/mfd/syscon.h>
-+#include <linux/soc/mediatek/mtk_wed.h>
-+
-+#include "mtk_wed_regs.h"
-+#include "mtk_wed_wo.h"
-+#include "mtk_wed.h"
-+
-+static u32 wo_r32(struct mtk_wed_wo *wo, u32 reg)
-+{
-+ return readl(wo->boot.addr + reg);
-+}
-+
-+static void wo_w32(struct mtk_wed_wo *wo, u32 reg, u32 val)
-+{
-+ writel(val, wo->boot.addr + reg);
-+}
-+
-+static struct sk_buff *
-+mtk_wed_mcu_msg_alloc(const void *data, int data_len)
-+{
-+ int length = sizeof(struct mtk_wed_mcu_hdr) + data_len;
-+ struct sk_buff *skb;
-+
-+ skb = alloc_skb(length, GFP_KERNEL);
-+ if (!skb)
-+ return NULL;
-+
-+ memset(skb->head, 0, length);
-+ skb_reserve(skb, sizeof(struct mtk_wed_mcu_hdr));
-+ if (data && data_len)
-+ skb_put_data(skb, data, data_len);
-+
-+ return skb;
-+}
-+
-+static struct sk_buff *
-+mtk_wed_mcu_get_response(struct mtk_wed_wo *wo, unsigned long expires)
-+{
-+ if (!time_is_after_jiffies(expires))
-+ return NULL;
-+
-+ wait_event_timeout(wo->mcu.wait, !skb_queue_empty(&wo->mcu.res_q),
-+ expires - jiffies);
-+ return skb_dequeue(&wo->mcu.res_q);
-+}
-+
-+void mtk_wed_mcu_rx_event(struct mtk_wed_wo *wo, struct sk_buff *skb)
-+{
-+ skb_queue_tail(&wo->mcu.res_q, skb);
-+ wake_up(&wo->mcu.wait);
-+}
-+
-+void mtk_wed_mcu_rx_unsolicited_event(struct mtk_wed_wo *wo,
-+ struct sk_buff *skb)
-+{
-+ struct mtk_wed_mcu_hdr *hdr = (struct mtk_wed_mcu_hdr *)skb->data;
-+
-+ switch (hdr->cmd) {
-+ case MTK_WED_WO_EVT_LOG_DUMP: {
-+ const char *msg = (const char *)(skb->data + sizeof(*hdr));
-+
-+ dev_notice(wo->hw->dev, "%s\n", msg);
-+ break;
-+ }
-+ case MTK_WED_WO_EVT_PROFILING: {
-+ struct mtk_wed_wo_log_info *info;
-+ u32 count = (skb->len - sizeof(*hdr)) / sizeof(*info);
-+ int i;
-+
-+ info = (struct mtk_wed_wo_log_info *)(skb->data + sizeof(*hdr));
-+ for (i = 0 ; i < count ; i++)
-+ dev_notice(wo->hw->dev,
-+ "SN:%u latency: total=%u, rro:%u, mod:%u\n",
-+ le32_to_cpu(info[i].sn),
-+ le32_to_cpu(info[i].total),
-+ le32_to_cpu(info[i].rro),
-+ le32_to_cpu(info[i].mod));
-+ break;
-+ }
-+ case MTK_WED_WO_EVT_RXCNT_INFO:
-+ break;
-+ default:
-+ break;
-+ }
-+
-+ dev_kfree_skb(skb);
-+}
-+
-+static int
-+mtk_wed_mcu_skb_send_msg(struct mtk_wed_wo *wo, struct sk_buff *skb,
-+ int id, int cmd, u16 *wait_seq, bool wait_resp)
-+{
-+ struct mtk_wed_mcu_hdr *hdr;
-+
-+ /* TODO: make it dynamic based on cmd */
-+ wo->mcu.timeout = 20 * HZ;
-+
-+ hdr = (struct mtk_wed_mcu_hdr *)skb_push(skb, sizeof(*hdr));
-+ hdr->cmd = cmd;
-+ hdr->length = cpu_to_le16(skb->len);
-+
-+ if (wait_resp && wait_seq) {
-+ u16 seq = ++wo->mcu.seq;
-+
-+ if (!seq)
-+ seq = ++wo->mcu.seq;
-+ *wait_seq = seq;
-+
-+ hdr->flag |= cpu_to_le16(MTK_WED_WARP_CMD_FLAG_NEED_RSP);
-+ hdr->seq = cpu_to_le16(seq);
-+ }
-+ if (id == MTK_WED_MODULE_ID_WO)
-+ hdr->flag |= cpu_to_le16(MTK_WED_WARP_CMD_FLAG_FROM_TO_WO);
-+
-+ dev_kfree_skb(skb);
-+ return 0;
-+}
-+
-+static int
-+mtk_wed_mcu_parse_response(struct mtk_wed_wo *wo, struct sk_buff *skb,
-+ int cmd, int seq)
-+{
-+ struct mtk_wed_mcu_hdr *hdr;
-+
-+ if (!skb) {
-+ dev_err(wo->hw->dev, "Message %08x (seq %d) timeout\n",
-+ cmd, seq);
-+ return -ETIMEDOUT;
-+ }
-+
-+ hdr = (struct mtk_wed_mcu_hdr *)skb->data;
-+ if (le16_to_cpu(hdr->seq) != seq)
-+ return -EAGAIN;
-+
-+ skb_pull(skb, sizeof(*hdr));
-+ switch (cmd) {
-+ case MTK_WED_WO_CMD_RXCNT_INFO:
-+ default:
-+ break;
-+ }
-+
-+ return 0;
-+}
-+
-+int mtk_wed_mcu_send_msg(struct mtk_wed_wo *wo, int id, int cmd,
-+ const void *data, int len, bool wait_resp)
-+{
-+ unsigned long expires;
-+ struct sk_buff *skb;
-+ u16 seq;
-+ int ret;
-+
-+ skb = mtk_wed_mcu_msg_alloc(data, len);
-+ if (!skb)
-+ return -ENOMEM;
-+
-+ mutex_lock(&wo->mcu.mutex);
-+
-+ ret = mtk_wed_mcu_skb_send_msg(wo, skb, id, cmd, &seq, wait_resp);
-+ if (ret || !wait_resp)
-+ goto unlock;
-+
-+ expires = jiffies + wo->mcu.timeout;
-+ do {
-+ skb = mtk_wed_mcu_get_response(wo, expires);
-+ ret = mtk_wed_mcu_parse_response(wo, skb, cmd, seq);
-+ dev_kfree_skb(skb);
-+ } while (ret == -EAGAIN);
-+
-+unlock:
-+ mutex_unlock(&wo->mcu.mutex);
-+
-+ return ret;
-+}
-+
-+static int
-+mtk_wed_get_memory_region(struct mtk_wed_wo *wo,
-+ struct mtk_wed_wo_memory_region *region)
-+{
-+ struct reserved_mem *rmem;
-+ struct device_node *np;
-+ int index;
-+
-+ index = of_property_match_string(wo->hw->node, "memory-region-names",
-+ region->name);
-+ if (index < 0)
-+ return index;
-+
-+ np = of_parse_phandle(wo->hw->node, "memory-region", index);
-+ if (!np)
-+ return -ENODEV;
-+
-+ rmem = of_reserved_mem_lookup(np);
-+ of_node_put(np);
-+
-+ if (!rmem)
-+ return -ENODEV;
-+
-+ region->phy_addr = rmem->base;
-+ region->size = rmem->size;
-+ region->addr = devm_ioremap(wo->hw->dev, region->phy_addr, region->size);
-+
-+ return !region->addr ? -EINVAL : 0;
-+}
-+
-+static int
-+mtk_wed_mcu_run_firmware(struct mtk_wed_wo *wo, const struct firmware *fw,
-+ struct mtk_wed_wo_memory_region *region)
-+{
-+ const u8 *first_region_ptr, *region_ptr, *trailer_ptr, *ptr = fw->data;
-+ const struct mtk_wed_fw_trailer *trailer;
-+ const struct mtk_wed_fw_region *fw_region;
-+
-+ trailer_ptr = fw->data + fw->size - sizeof(*trailer);
-+ trailer = (const struct mtk_wed_fw_trailer *)trailer_ptr;
-+ region_ptr = trailer_ptr - trailer->num_region * sizeof(*fw_region);
-+ first_region_ptr = region_ptr;
-+
-+ while (region_ptr < trailer_ptr) {
-+ u32 length;
-+
-+ fw_region = (const struct mtk_wed_fw_region *)region_ptr;
-+ length = le32_to_cpu(fw_region->len);
-+
-+ if (region->phy_addr != le32_to_cpu(fw_region->addr))
-+ goto next;
-+
-+ if (region->size < length)
-+ goto next;
-+
-+ if (first_region_ptr < ptr + length)
-+ goto next;
-+
-+ if (region->shared && region->consumed)
-+ return 0;
-+
-+ if (!region->shared || !region->consumed) {
-+ memcpy_toio(region->addr, ptr, length);
-+ region->consumed = true;
-+ return 0;
-+ }
-+next:
-+ region_ptr += sizeof(*fw_region);
-+ ptr += length;
-+ }
-+
-+ return -EINVAL;
-+}
-+
-+static int
-+mtk_wed_mcu_load_firmware(struct mtk_wed_wo *wo)
-+{
-+ static struct mtk_wed_wo_memory_region mem_region[] = {
-+ [MTK_WED_WO_REGION_EMI] = {
-+ .name = "wo-emi",
-+ },
-+ [MTK_WED_WO_REGION_ILM] = {
-+ .name = "wo-ilm",
-+ },
-+ [MTK_WED_WO_REGION_DATA] = {
-+ .name = "wo-data",
-+ .shared = true,
-+ },
-+ };
-+ const struct mtk_wed_fw_trailer *trailer;
-+ const struct firmware *fw;
-+ const char *fw_name;
-+ u32 val, boot_cr;
-+ int ret, i;
-+
-+ /* load firmware region metadata */
-+ for (i = 0; i < ARRAY_SIZE(mem_region); i++) {
-+ ret = mtk_wed_get_memory_region(wo, &mem_region[i]);
-+ if (ret)
-+ return ret;
-+ }
-+
-+ wo->boot.name = "wo-boot";
-+ ret = mtk_wed_get_memory_region(wo, &wo->boot);
-+ if (ret)
-+ return ret;
-+
-+ /* set dummy cr */
-+ wed_w32(wo->hw->wed_dev, MTK_WED_SCR0 + 4 * MTK_WED_DUMMY_CR_FWDL,
-+ wo->hw->index + 1);
-+
-+ /* load firmware */
-+ fw_name = wo->hw->index ? MT7986_FIRMWARE_WO1 : MT7986_FIRMWARE_WO0;
-+ ret = request_firmware(&fw, fw_name, wo->hw->dev);
-+ if (ret)
-+ return ret;
-+
-+ trailer = (void *)(fw->data + fw->size -
-+ sizeof(struct mtk_wed_fw_trailer));
-+ dev_info(wo->hw->dev,
-+ "MTK WED WO Firmware Version: %.10s, Build Time: %.15s\n",
-+ trailer->fw_ver, trailer->build_date);
-+ dev_info(wo->hw->dev, "MTK WED WO Chip ID %02x Region %d\n",
-+ trailer->chip_id, trailer->num_region);
-+
-+ for (i = 0; i < ARRAY_SIZE(mem_region); i++) {
-+ ret = mtk_wed_mcu_run_firmware(wo, fw, &mem_region[i]);
-+ if (ret)
-+ goto out;
-+ }
-+
-+ /* set the start address */
-+ boot_cr = wo->hw->index ? MTK_WO_MCU_CFG_LS_WA_BOOT_ADDR_ADDR
-+ : MTK_WO_MCU_CFG_LS_WM_BOOT_ADDR_ADDR;
-+ wo_w32(wo, boot_cr, mem_region[MTK_WED_WO_REGION_EMI].phy_addr >> 16);
-+ /* wo firmware reset */
-+ wo_w32(wo, MTK_WO_MCU_CFG_LS_WF_MCCR_CLR_ADDR, 0xc00);
-+
-+ val = wo_r32(wo, MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR);
-+ val |= wo->hw->index ? MTK_WO_MCU_CFG_LS_WF_WM_WA_WA_CPU_RSTB_MASK
-+ : MTK_WO_MCU_CFG_LS_WF_WM_WA_WM_CPU_RSTB_MASK;
-+ wo_w32(wo, MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR, val);
-+out:
-+ release_firmware(fw);
-+
-+ return ret;
-+}
-+
-+static u32
-+mtk_wed_mcu_read_fw_dl(struct mtk_wed_wo *wo)
-+{
-+ return wed_r32(wo->hw->wed_dev,
-+ MTK_WED_SCR0 + 4 * MTK_WED_DUMMY_CR_FWDL);
-+}
-+
-+int mtk_wed_mcu_init(struct mtk_wed_wo *wo)
-+{
-+ u32 val;
-+ int ret;
-+
-+ skb_queue_head_init(&wo->mcu.res_q);
-+ init_waitqueue_head(&wo->mcu.wait);
-+ mutex_init(&wo->mcu.mutex);
-+
-+ ret = mtk_wed_mcu_load_firmware(wo);
-+ if (ret)
-+ return ret;
-+
-+ return readx_poll_timeout(mtk_wed_mcu_read_fw_dl, wo, val, !val,
-+ 100, MTK_FW_DL_TIMEOUT);
-+}
-+
-+MODULE_FIRMWARE(MT7986_FIRMWARE_WO0);
-+MODULE_FIRMWARE(MT7986_FIRMWARE_WO1);
---- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
-@@ -152,6 +152,7 @@ struct mtk_wdma_desc {
-
- #define MTK_WED_RING_RX(_n) (0x400 + (_n) * 0x10)
-
-+#define MTK_WED_SCR0 0x3c0
- #define MTK_WED_WPDMA_INT_TRIGGER 0x504
- #define MTK_WED_WPDMA_INT_TRIGGER_RX_DONE BIT(1)
- #define MTK_WED_WPDMA_INT_TRIGGER_TX_DONE GENMASK(5, 4)
---- /dev/null
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h
-@@ -0,0 +1,150 @@
-+/* SPDX-License-Identifier: GPL-2.0-only */
-+/* Copyright (C) 2022 Lorenzo Bianconi <lorenzo@kernel.org> */
-+
-+#ifndef __MTK_WED_WO_H
-+#define __MTK_WED_WO_H
-+
-+#include <linux/skbuff.h>
-+#include <linux/netdevice.h>
-+
-+struct mtk_wed_hw;
-+
-+struct mtk_wed_mcu_hdr {
-+ /* DW0 */
-+ u8 version;
-+ u8 cmd;
-+ __le16 length;
-+
-+ /* DW1 */
-+ __le16 seq;
-+ __le16 flag;
-+
-+ /* DW2 */
-+ __le32 status;
-+
-+ /* DW3 */
-+ u8 rsv[20];
-+};
-+
-+struct mtk_wed_wo_log_info {
-+ __le32 sn;
-+ __le32 total;
-+ __le32 rro;
-+ __le32 mod;
-+};
-+
-+enum mtk_wed_wo_event {
-+ MTK_WED_WO_EVT_LOG_DUMP = 0x1,
-+ MTK_WED_WO_EVT_PROFILING = 0x2,
-+ MTK_WED_WO_EVT_RXCNT_INFO = 0x3,
-+};
-+
-+#define MTK_WED_MODULE_ID_WO 1
-+#define MTK_FW_DL_TIMEOUT 4000000 /* us */
-+#define MTK_WOCPU_TIMEOUT 2000000 /* us */
-+
-+enum {
-+ MTK_WED_WARP_CMD_FLAG_RSP = BIT(0),
-+ MTK_WED_WARP_CMD_FLAG_NEED_RSP = BIT(1),
-+ MTK_WED_WARP_CMD_FLAG_FROM_TO_WO = BIT(2),
-+};
-+
-+enum {
-+ MTK_WED_WO_REGION_EMI,
-+ MTK_WED_WO_REGION_ILM,
-+ MTK_WED_WO_REGION_DATA,
-+ MTK_WED_WO_REGION_BOOT,
-+ __MTK_WED_WO_REGION_MAX,
-+};
-+
-+enum mtk_wed_dummy_cr_idx {
-+ MTK_WED_DUMMY_CR_FWDL,
-+ MTK_WED_DUMMY_CR_WO_STATUS,
-+};
-+
-+#define MT7986_FIRMWARE_WO0 "mediatek/mt7986_wo_0.bin"
-+#define MT7986_FIRMWARE_WO1 "mediatek/mt7986_wo_1.bin"
-+
-+#define MTK_WO_MCU_CFG_LS_BASE 0
-+#define MTK_WO_MCU_CFG_LS_HW_VER_ADDR (MTK_WO_MCU_CFG_LS_BASE + 0x000)
-+#define MTK_WO_MCU_CFG_LS_FW_VER_ADDR (MTK_WO_MCU_CFG_LS_BASE + 0x004)
-+#define MTK_WO_MCU_CFG_LS_CFG_DBG1_ADDR (MTK_WO_MCU_CFG_LS_BASE + 0x00c)
-+#define MTK_WO_MCU_CFG_LS_CFG_DBG2_ADDR (MTK_WO_MCU_CFG_LS_BASE + 0x010)
-+#define MTK_WO_MCU_CFG_LS_WF_MCCR_ADDR (MTK_WO_MCU_CFG_LS_BASE + 0x014)
-+#define MTK_WO_MCU_CFG_LS_WF_MCCR_SET_ADDR (MTK_WO_MCU_CFG_LS_BASE + 0x018)
-+#define MTK_WO_MCU_CFG_LS_WF_MCCR_CLR_ADDR (MTK_WO_MCU_CFG_LS_BASE + 0x01c)
-+#define MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR (MTK_WO_MCU_CFG_LS_BASE + 0x050)
-+#define MTK_WO_MCU_CFG_LS_WM_BOOT_ADDR_ADDR (MTK_WO_MCU_CFG_LS_BASE + 0x060)
-+#define MTK_WO_MCU_CFG_LS_WA_BOOT_ADDR_ADDR (MTK_WO_MCU_CFG_LS_BASE + 0x064)
-+
-+#define MTK_WO_MCU_CFG_LS_WF_WM_WA_WM_CPU_RSTB_MASK BIT(5)
-+#define MTK_WO_MCU_CFG_LS_WF_WM_WA_WA_CPU_RSTB_MASK BIT(0)
-+
-+struct mtk_wed_wo_memory_region {
-+ const char *name;
-+ void __iomem *addr;
-+ phys_addr_t phy_addr;
-+ u32 size;
-+ bool shared:1;
-+ bool consumed:1;
-+};
-+
-+struct mtk_wed_fw_region {
-+ __le32 decomp_crc;
-+ __le32 decomp_len;
-+ __le32 decomp_blk_sz;
-+ u8 rsv0[4];
-+ __le32 addr;
-+ __le32 len;
-+ u8 feature_set;
-+ u8 rsv1[15];
-+} __packed;
-+
-+struct mtk_wed_fw_trailer {
-+ u8 chip_id;
-+ u8 eco_code;
-+ u8 num_region;
-+ u8 format_ver;
-+ u8 format_flag;
-+ u8 rsv[2];
-+ char fw_ver[10];
-+ char build_date[15];
-+ u32 crc;
-+};
-+
-+struct mtk_wed_wo {
-+ struct mtk_wed_hw *hw;
-+ struct mtk_wed_wo_memory_region boot;
-+
-+ struct {
-+ struct mutex mutex;
-+ int timeout;
-+ u16 seq;
-+
-+ struct sk_buff_head res_q;
-+ wait_queue_head_t wait;
-+ } mcu;
-+};
-+
-+static inline int
-+mtk_wed_mcu_check_msg(struct mtk_wed_wo *wo, struct sk_buff *skb)
-+{
-+ struct mtk_wed_mcu_hdr *hdr = (struct mtk_wed_mcu_hdr *)skb->data;
-+
-+ if (hdr->version)
-+ return -EINVAL;
-+
-+ if (skb->len < sizeof(*hdr) || skb->len != le16_to_cpu(hdr->length))
-+ return -EINVAL;
-+
-+ return 0;
-+}
-+
-+void mtk_wed_mcu_rx_event(struct mtk_wed_wo *wo, struct sk_buff *skb);
-+void mtk_wed_mcu_rx_unsolicited_event(struct mtk_wed_wo *wo,
-+ struct sk_buff *skb);
-+int mtk_wed_mcu_send_msg(struct mtk_wed_wo *wo, int id, int cmd,
-+ const void *data, int len, bool wait_resp);
-+int mtk_wed_mcu_init(struct mtk_wed_wo *wo);
-+
-+#endif /* __MTK_WED_WO_H */
---- a/include/linux/soc/mediatek/mtk_wed.h
-+++ b/include/linux/soc/mediatek/mtk_wed.h
-@@ -11,6 +11,35 @@
- struct mtk_wed_hw;
- struct mtk_wdma_desc;
-
-+enum mtk_wed_wo_cmd {
-+ MTK_WED_WO_CMD_WED_CFG,
-+ MTK_WED_WO_CMD_WED_RX_STAT,
-+ MTK_WED_WO_CMD_RRO_SER,
-+ MTK_WED_WO_CMD_DBG_INFO,
-+ MTK_WED_WO_CMD_DEV_INFO,
-+ MTK_WED_WO_CMD_BSS_INFO,
-+ MTK_WED_WO_CMD_STA_REC,
-+ MTK_WED_WO_CMD_DEV_INFO_DUMP,
-+ MTK_WED_WO_CMD_BSS_INFO_DUMP,
-+ MTK_WED_WO_CMD_STA_REC_DUMP,
-+ MTK_WED_WO_CMD_BA_INFO_DUMP,
-+ MTK_WED_WO_CMD_FBCMD_Q_DUMP,
-+ MTK_WED_WO_CMD_FW_LOG_CTRL,
-+ MTK_WED_WO_CMD_LOG_FLUSH,
-+ MTK_WED_WO_CMD_CHANGE_STATE,
-+ MTK_WED_WO_CMD_CPU_STATS_ENABLE,
-+ MTK_WED_WO_CMD_CPU_STATS_DUMP,
-+ MTK_WED_WO_CMD_EXCEPTION_INIT,
-+ MTK_WED_WO_CMD_PROF_CTRL,
-+ MTK_WED_WO_CMD_STA_BA_DUMP,
-+ MTK_WED_WO_CMD_BA_CTRL_DUMP,
-+ MTK_WED_WO_CMD_RXCNT_CTRL,
-+ MTK_WED_WO_CMD_RXCNT_INFO,
-+ MTK_WED_WO_CMD_SET_CAP,
-+ MTK_WED_WO_CMD_CCIF_RING_DUMP,
-+ MTK_WED_WO_CMD_WED_END
-+};
-+
- enum mtk_wed_bus_tye {
- MTK_WED_BUS_PCIE,
- MTK_WED_BUS_AXI,
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Sat, 5 Nov 2022 23:36:19 +0100
-Subject: [PATCH] net: ethernet: mtk_wed: introduce wed wo support
-
-Introduce WO chip support to mtk wed driver. MTK WED WO is used to
-implement RX Wireless Ethernet Dispatch and offload traffic received by
-wlan nic to the wired interface.
-
-Tested-by: Daniel Golle <daniel@makrotopia.org>
-Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- create mode 100644 drivers/net/ethernet/mediatek/mtk_wed_wo.c
-
---- a/drivers/net/ethernet/mediatek/Makefile
-+++ b/drivers/net/ethernet/mediatek/Makefile
-@@ -5,7 +5,7 @@
-
- obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o
- mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o mtk_ppe_offload.o
--mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed.o mtk_wed_mcu.o
-+mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed.o mtk_wed_mcu.o mtk_wed_wo.o
- ifdef CONFIG_DEBUG_FS
- mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed_debugfs.o
- endif
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -16,6 +16,7 @@
- #include "mtk_wed_regs.h"
- #include "mtk_wed.h"
- #include "mtk_ppe.h"
-+#include "mtk_wed_wo.h"
-
- #define MTK_PCIE_BASE(n) (0x1a143000 + (n) * 0x2000)
-
-@@ -355,6 +356,8 @@ mtk_wed_detach(struct mtk_wed_device *de
-
- mtk_wed_free_buffer(dev);
- mtk_wed_free_tx_rings(dev);
-+ if (hw->version != 1)
-+ mtk_wed_wo_deinit(hw);
-
- if (dev->wlan.bus_type == MTK_WED_BUS_PCIE) {
- struct device_node *wlan_node;
-@@ -878,9 +881,11 @@ mtk_wed_attach(struct mtk_wed_device *de
- }
-
- mtk_wed_hw_init_early(dev);
-- if (hw->hifsys)
-+ if (hw->version == 1)
- regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP,
- BIT(hw->index), 0);
-+ else
-+ ret = mtk_wed_wo_init(hw);
-
- out:
- mutex_unlock(&hw_lock);
---- a/drivers/net/ethernet/mediatek/mtk_wed.h
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.h
-@@ -10,6 +10,7 @@
- #include <linux/netdevice.h>
-
- struct mtk_eth;
-+struct mtk_wed_wo;
-
- struct mtk_wed_hw {
- struct device_node *node;
-@@ -22,6 +23,7 @@ struct mtk_wed_hw {
- struct regmap *mirror;
- struct dentry *debugfs_dir;
- struct mtk_wed_device *wed_dev;
-+ struct mtk_wed_wo *wed_wo;
- u32 debugfs_reg;
- u32 num_flows;
- u8 version;
---- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
-@@ -122,8 +122,7 @@ mtk_wed_mcu_skb_send_msg(struct mtk_wed_
- if (id == MTK_WED_MODULE_ID_WO)
- hdr->flag |= cpu_to_le16(MTK_WED_WARP_CMD_FLAG_FROM_TO_WO);
-
-- dev_kfree_skb(skb);
-- return 0;
-+ return mtk_wed_wo_queue_tx_skb(wo, &wo->q_tx, skb);
- }
-
- static int
---- /dev/null
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.c
-@@ -0,0 +1,508 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+/* Copyright (C) 2022 MediaTek Inc.
-+ *
-+ * Author: Lorenzo Bianconi <lorenzo@kernel.org>
-+ * Sujuan Chen <sujuan.chen@mediatek.com>
-+ */
-+
-+#include <linux/kernel.h>
-+#include <linux/dma-mapping.h>
-+#include <linux/of_platform.h>
-+#include <linux/interrupt.h>
-+#include <linux/of_address.h>
-+#include <linux/mfd/syscon.h>
-+#include <linux/of_irq.h>
-+#include <linux/bitfield.h>
-+
-+#include "mtk_wed.h"
-+#include "mtk_wed_regs.h"
-+#include "mtk_wed_wo.h"
-+
-+static u32
-+mtk_wed_mmio_r32(struct mtk_wed_wo *wo, u32 reg)
-+{
-+ u32 val;
-+
-+ if (regmap_read(wo->mmio.regs, reg, &val))
-+ val = ~0;
-+
-+ return val;
-+}
-+
-+static void
-+mtk_wed_mmio_w32(struct mtk_wed_wo *wo, u32 reg, u32 val)
-+{
-+ regmap_write(wo->mmio.regs, reg, val);
-+}
-+
-+static u32
-+mtk_wed_wo_get_isr(struct mtk_wed_wo *wo)
-+{
-+ u32 val = mtk_wed_mmio_r32(wo, MTK_WED_WO_CCIF_RCHNUM);
-+
-+ return val & MTK_WED_WO_CCIF_RCHNUM_MASK;
-+}
-+
-+static void
-+mtk_wed_wo_set_isr(struct mtk_wed_wo *wo, u32 mask)
-+{
-+ mtk_wed_mmio_w32(wo, MTK_WED_WO_CCIF_IRQ0_MASK, mask);
-+}
-+
-+static void
-+mtk_wed_wo_set_ack(struct mtk_wed_wo *wo, u32 mask)
-+{
-+ mtk_wed_mmio_w32(wo, MTK_WED_WO_CCIF_ACK, mask);
-+}
-+
-+static void
-+mtk_wed_wo_set_isr_mask(struct mtk_wed_wo *wo, u32 mask, u32 val, bool set)
-+{
-+ unsigned long flags;
-+
-+ spin_lock_irqsave(&wo->mmio.lock, flags);
-+ wo->mmio.irq_mask &= ~mask;
-+ wo->mmio.irq_mask |= val;
-+ if (set)
-+ mtk_wed_wo_set_isr(wo, wo->mmio.irq_mask);
-+ spin_unlock_irqrestore(&wo->mmio.lock, flags);
-+}
-+
-+static void
-+mtk_wed_wo_irq_enable(struct mtk_wed_wo *wo, u32 mask)
-+{
-+ mtk_wed_wo_set_isr_mask(wo, 0, mask, false);
-+ tasklet_schedule(&wo->mmio.irq_tasklet);
-+}
-+
-+static void
-+mtk_wed_wo_irq_disable(struct mtk_wed_wo *wo, u32 mask)
-+{
-+ mtk_wed_wo_set_isr_mask(wo, mask, 0, true);
-+}
-+
-+static void
-+mtk_wed_wo_kickout(struct mtk_wed_wo *wo)
-+{
-+ mtk_wed_mmio_w32(wo, MTK_WED_WO_CCIF_BUSY, 1 << MTK_WED_WO_TXCH_NUM);
-+ mtk_wed_mmio_w32(wo, MTK_WED_WO_CCIF_TCHNUM, MTK_WED_WO_TXCH_NUM);
-+}
-+
-+static void
-+mtk_wed_wo_queue_kick(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q,
-+ u32 val)
-+{
-+ wmb();
-+ mtk_wed_mmio_w32(wo, q->regs.cpu_idx, val);
-+}
-+
-+static void *
-+mtk_wed_wo_dequeue(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q, u32 *len,
-+ bool flush)
-+{
-+ int buf_len = SKB_WITH_OVERHEAD(q->buf_size);
-+ int index = (q->tail + 1) % q->n_desc;
-+ struct mtk_wed_wo_queue_entry *entry;
-+ struct mtk_wed_wo_queue_desc *desc;
-+ void *buf;
-+
-+ if (!q->queued)
-+ return NULL;
-+
-+ if (flush)
-+ q->desc[index].ctrl |= cpu_to_le32(MTK_WED_WO_CTL_DMA_DONE);
-+ else if (!(q->desc[index].ctrl & cpu_to_le32(MTK_WED_WO_CTL_DMA_DONE)))
-+ return NULL;
-+
-+ q->tail = index;
-+ q->queued--;
-+
-+ desc = &q->desc[index];
-+ entry = &q->entry[index];
-+ buf = entry->buf;
-+ if (len)
-+ *len = FIELD_GET(MTK_WED_WO_CTL_SD_LEN0,
-+ le32_to_cpu(READ_ONCE(desc->ctrl)));
-+ if (buf)
-+ dma_unmap_single(wo->hw->dev, entry->addr, buf_len,
-+ DMA_FROM_DEVICE);
-+ entry->buf = NULL;
-+
-+ return buf;
-+}
-+
-+static int
-+mtk_wed_wo_queue_refill(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q,
-+ gfp_t gfp, bool rx)
-+{
-+ enum dma_data_direction dir = rx ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
-+ int n_buf = 0;
-+
-+ spin_lock_bh(&q->lock);
-+ while (q->queued < q->n_desc) {
-+ void *buf = page_frag_alloc(&q->cache, q->buf_size, gfp);
-+ struct mtk_wed_wo_queue_entry *entry;
-+ dma_addr_t addr;
-+
-+ if (!buf)
-+ break;
-+
-+ addr = dma_map_single(wo->hw->dev, buf, q->buf_size, dir);
-+ if (unlikely(dma_mapping_error(wo->hw->dev, addr))) {
-+ skb_free_frag(buf);
-+ break;
-+ }
-+
-+ q->head = (q->head + 1) % q->n_desc;
-+ entry = &q->entry[q->head];
-+ entry->addr = addr;
-+ entry->len = q->buf_size;
-+ q->entry[q->head].buf = buf;
-+
-+ if (rx) {
-+ struct mtk_wed_wo_queue_desc *desc = &q->desc[q->head];
-+ u32 ctrl = MTK_WED_WO_CTL_LAST_SEC0 |
-+ FIELD_PREP(MTK_WED_WO_CTL_SD_LEN0,
-+ entry->len);
-+
-+ WRITE_ONCE(desc->buf0, cpu_to_le32(addr));
-+ WRITE_ONCE(desc->ctrl, cpu_to_le32(ctrl));
-+ }
-+ q->queued++;
-+ n_buf++;
-+ }
-+ spin_unlock_bh(&q->lock);
-+
-+ return n_buf;
-+}
-+
-+static void
-+mtk_wed_wo_rx_complete(struct mtk_wed_wo *wo)
-+{
-+ mtk_wed_wo_set_ack(wo, MTK_WED_WO_RXCH_INT_MASK);
-+ mtk_wed_wo_irq_enable(wo, MTK_WED_WO_RXCH_INT_MASK);
-+}
-+
-+static void
-+mtk_wed_wo_rx_run_queue(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q)
-+{
-+ for (;;) {
-+ struct mtk_wed_mcu_hdr *hdr;
-+ struct sk_buff *skb;
-+ void *data;
-+ u32 len;
-+
-+ data = mtk_wed_wo_dequeue(wo, q, &len, false);
-+ if (!data)
-+ break;
-+
-+ skb = build_skb(data, q->buf_size);
-+ if (!skb) {
-+ skb_free_frag(data);
-+ continue;
-+ }
-+
-+ __skb_put(skb, len);
-+ if (mtk_wed_mcu_check_msg(wo, skb)) {
-+ dev_kfree_skb(skb);
-+ continue;
-+ }
-+
-+ hdr = (struct mtk_wed_mcu_hdr *)skb->data;
-+ if (hdr->flag & cpu_to_le16(MTK_WED_WARP_CMD_FLAG_RSP))
-+ mtk_wed_mcu_rx_event(wo, skb);
-+ else
-+ mtk_wed_mcu_rx_unsolicited_event(wo, skb);
-+ }
-+
-+ if (mtk_wed_wo_queue_refill(wo, q, GFP_ATOMIC, true)) {
-+ u32 index = (q->head - 1) % q->n_desc;
-+
-+ mtk_wed_wo_queue_kick(wo, q, index);
-+ }
-+}
-+
-+static irqreturn_t
-+mtk_wed_wo_irq_handler(int irq, void *data)
-+{
-+ struct mtk_wed_wo *wo = data;
-+
-+ mtk_wed_wo_set_isr(wo, 0);
-+ tasklet_schedule(&wo->mmio.irq_tasklet);
-+
-+ return IRQ_HANDLED;
-+}
-+
-+static void mtk_wed_wo_irq_tasklet(struct tasklet_struct *t)
-+{
-+ struct mtk_wed_wo *wo = from_tasklet(wo, t, mmio.irq_tasklet);
-+ u32 intr, mask;
-+
-+ /* disable interrupts */
-+ mtk_wed_wo_set_isr(wo, 0);
-+
-+ intr = mtk_wed_wo_get_isr(wo);
-+ intr &= wo->mmio.irq_mask;
-+ mask = intr & (MTK_WED_WO_RXCH_INT_MASK | MTK_WED_WO_EXCEPTION_INT_MASK);
-+ mtk_wed_wo_irq_disable(wo, mask);
-+
-+ if (intr & MTK_WED_WO_RXCH_INT_MASK) {
-+ mtk_wed_wo_rx_run_queue(wo, &wo->q_rx);
-+ mtk_wed_wo_rx_complete(wo);
-+ }
-+}
-+
-+/* mtk wed wo hw queues */
-+
-+static int
-+mtk_wed_wo_queue_alloc(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q,
-+ int n_desc, int buf_size, int index,
-+ struct mtk_wed_wo_queue_regs *regs)
-+{
-+ spin_lock_init(&q->lock);
-+ q->regs = *regs;
-+ q->n_desc = n_desc;
-+ q->buf_size = buf_size;
-+
-+ q->desc = dmam_alloc_coherent(wo->hw->dev, n_desc * sizeof(*q->desc),
-+ &q->desc_dma, GFP_KERNEL);
-+ if (!q->desc)
-+ return -ENOMEM;
-+
-+ q->entry = devm_kzalloc(wo->hw->dev, n_desc * sizeof(*q->entry),
-+ GFP_KERNEL);
-+ if (!q->entry)
-+ return -ENOMEM;
-+
-+ return 0;
-+}
-+
-+static void
-+mtk_wed_wo_queue_free(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q)
-+{
-+ mtk_wed_mmio_w32(wo, q->regs.cpu_idx, 0);
-+ dma_free_coherent(wo->hw->dev, q->n_desc * sizeof(*q->desc), q->desc,
-+ q->desc_dma);
-+}
-+
-+static void
-+mtk_wed_wo_queue_tx_clean(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q)
-+{
-+ struct page *page;
-+ int i;
-+
-+ spin_lock_bh(&q->lock);
-+ for (i = 0; i < q->n_desc; i++) {
-+ struct mtk_wed_wo_queue_entry *entry = &q->entry[i];
-+
-+ dma_unmap_single(wo->hw->dev, entry->addr, entry->len,
-+ DMA_TO_DEVICE);
-+ skb_free_frag(entry->buf);
-+ entry->buf = NULL;
-+ }
-+ spin_unlock_bh(&q->lock);
-+
-+ if (!q->cache.va)
-+ return;
-+
-+ page = virt_to_page(q->cache.va);
-+ __page_frag_cache_drain(page, q->cache.pagecnt_bias);
-+ memset(&q->cache, 0, sizeof(q->cache));
-+}
-+
-+static void
-+mtk_wed_wo_queue_rx_clean(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q)
-+{
-+ struct page *page;
-+
-+ spin_lock_bh(&q->lock);
-+ for (;;) {
-+ void *buf = mtk_wed_wo_dequeue(wo, q, NULL, true);
-+
-+ if (!buf)
-+ break;
-+
-+ skb_free_frag(buf);
-+ }
-+ spin_unlock_bh(&q->lock);
-+
-+ if (!q->cache.va)
-+ return;
-+
-+ page = virt_to_page(q->cache.va);
-+ __page_frag_cache_drain(page, q->cache.pagecnt_bias);
-+ memset(&q->cache, 0, sizeof(q->cache));
-+}
-+
-+static void
-+mtk_wed_wo_queue_reset(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q)
-+{
-+ mtk_wed_mmio_w32(wo, q->regs.cpu_idx, 0);
-+ mtk_wed_mmio_w32(wo, q->regs.desc_base, q->desc_dma);
-+ mtk_wed_mmio_w32(wo, q->regs.ring_size, q->n_desc);
-+}
-+
-+int mtk_wed_wo_queue_tx_skb(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q,
-+ struct sk_buff *skb)
-+{
-+ struct mtk_wed_wo_queue_entry *entry;
-+ struct mtk_wed_wo_queue_desc *desc;
-+ int ret = 0, index;
-+ u32 ctrl;
-+
-+ spin_lock_bh(&q->lock);
-+
-+ q->tail = mtk_wed_mmio_r32(wo, q->regs.dma_idx);
-+ index = (q->head + 1) % q->n_desc;
-+ if (q->tail == index) {
-+ ret = -ENOMEM;
-+ goto out;
-+ }
-+
-+ entry = &q->entry[index];
-+ if (skb->len > entry->len) {
-+ ret = -ENOMEM;
-+ goto out;
-+ }
-+
-+ desc = &q->desc[index];
-+ q->head = index;
-+
-+ dma_sync_single_for_cpu(wo->hw->dev, entry->addr, skb->len,
-+ DMA_TO_DEVICE);
-+ memcpy(entry->buf, skb->data, skb->len);
-+ dma_sync_single_for_device(wo->hw->dev, entry->addr, skb->len,
-+ DMA_TO_DEVICE);
-+
-+ ctrl = FIELD_PREP(MTK_WED_WO_CTL_SD_LEN0, skb->len) |
-+ MTK_WED_WO_CTL_LAST_SEC0 | MTK_WED_WO_CTL_DMA_DONE;
-+ WRITE_ONCE(desc->buf0, cpu_to_le32(entry->addr));
-+ WRITE_ONCE(desc->ctrl, cpu_to_le32(ctrl));
-+
-+ mtk_wed_wo_queue_kick(wo, q, q->head);
-+ mtk_wed_wo_kickout(wo);
-+out:
-+ spin_unlock_bh(&q->lock);
-+
-+ dev_kfree_skb(skb);
-+
-+ return ret;
-+}
-+
-+static int
-+mtk_wed_wo_exception_init(struct mtk_wed_wo *wo)
-+{
-+ return 0;
-+}
-+
-+static int
-+mtk_wed_wo_hardware_init(struct mtk_wed_wo *wo)
-+{
-+ struct mtk_wed_wo_queue_regs regs;
-+ struct device_node *np;
-+ int ret;
-+
-+ np = of_parse_phandle(wo->hw->node, "mediatek,wo-ccif", 0);
-+ if (!np)
-+ return -ENODEV;
-+
-+ wo->mmio.regs = syscon_regmap_lookup_by_phandle(np, NULL);
-+ if (IS_ERR_OR_NULL(wo->mmio.regs))
-+ return PTR_ERR(wo->mmio.regs);
-+
-+ wo->mmio.irq = irq_of_parse_and_map(np, 0);
-+ wo->mmio.irq_mask = MTK_WED_WO_ALL_INT_MASK;
-+ spin_lock_init(&wo->mmio.lock);
-+ tasklet_setup(&wo->mmio.irq_tasklet, mtk_wed_wo_irq_tasklet);
-+
-+ ret = devm_request_irq(wo->hw->dev, wo->mmio.irq,
-+ mtk_wed_wo_irq_handler, IRQF_TRIGGER_HIGH,
-+ KBUILD_MODNAME, wo);
-+ if (ret)
-+ goto error;
-+
-+ regs.desc_base = MTK_WED_WO_CCIF_DUMMY1;
-+ regs.ring_size = MTK_WED_WO_CCIF_DUMMY2;
-+ regs.dma_idx = MTK_WED_WO_CCIF_SHADOW4;
-+ regs.cpu_idx = MTK_WED_WO_CCIF_DUMMY3;
-+
-+ ret = mtk_wed_wo_queue_alloc(wo, &wo->q_tx, MTK_WED_WO_RING_SIZE,
-+ MTK_WED_WO_CMD_LEN, MTK_WED_WO_TXCH_NUM,
-+ ®s);
-+ if (ret)
-+ goto error;
-+
-+ mtk_wed_wo_queue_refill(wo, &wo->q_tx, GFP_KERNEL, false);
-+ mtk_wed_wo_queue_reset(wo, &wo->q_tx);
-+
-+ regs.desc_base = MTK_WED_WO_CCIF_DUMMY5;
-+ regs.ring_size = MTK_WED_WO_CCIF_DUMMY6;
-+ regs.dma_idx = MTK_WED_WO_CCIF_SHADOW8;
-+ regs.cpu_idx = MTK_WED_WO_CCIF_DUMMY7;
-+
-+ ret = mtk_wed_wo_queue_alloc(wo, &wo->q_rx, MTK_WED_WO_RING_SIZE,
-+ MTK_WED_WO_CMD_LEN, MTK_WED_WO_RXCH_NUM,
-+ ®s);
-+ if (ret)
-+ goto error;
-+
-+ mtk_wed_wo_queue_refill(wo, &wo->q_rx, GFP_KERNEL, true);
-+ mtk_wed_wo_queue_reset(wo, &wo->q_rx);
-+
-+ /* rx queue irqmask */
-+ mtk_wed_wo_set_isr(wo, wo->mmio.irq_mask);
-+
-+ return 0;
-+
-+error:
-+ devm_free_irq(wo->hw->dev, wo->mmio.irq, wo);
-+
-+ return ret;
-+}
-+
-+static void
-+mtk_wed_wo_hw_deinit(struct mtk_wed_wo *wo)
-+{
-+ /* disable interrupts */
-+ mtk_wed_wo_set_isr(wo, 0);
-+
-+ tasklet_disable(&wo->mmio.irq_tasklet);
-+
-+ disable_irq(wo->mmio.irq);
-+ devm_free_irq(wo->hw->dev, wo->mmio.irq, wo);
-+
-+ mtk_wed_wo_queue_tx_clean(wo, &wo->q_tx);
-+ mtk_wed_wo_queue_rx_clean(wo, &wo->q_rx);
-+ mtk_wed_wo_queue_free(wo, &wo->q_tx);
-+ mtk_wed_wo_queue_free(wo, &wo->q_rx);
-+}
-+
-+int mtk_wed_wo_init(struct mtk_wed_hw *hw)
-+{
-+ struct mtk_wed_wo *wo;
-+ int ret;
-+
-+ wo = devm_kzalloc(hw->dev, sizeof(*wo), GFP_KERNEL);
-+ if (!wo)
-+ return -ENOMEM;
-+
-+ hw->wed_wo = wo;
-+ wo->hw = hw;
-+
-+ ret = mtk_wed_wo_hardware_init(wo);
-+ if (ret)
-+ return ret;
-+
-+ ret = mtk_wed_mcu_init(wo);
-+ if (ret)
-+ return ret;
-+
-+ return mtk_wed_wo_exception_init(wo);
-+}
-+
-+void mtk_wed_wo_deinit(struct mtk_wed_hw *hw)
-+{
-+ struct mtk_wed_wo *wo = hw->wed_wo;
-+
-+ mtk_wed_wo_hw_deinit(wo);
-+}
---- a/drivers/net/ethernet/mediatek/mtk_wed_wo.h
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h
-@@ -80,6 +80,54 @@ enum mtk_wed_dummy_cr_idx {
- #define MTK_WO_MCU_CFG_LS_WF_WM_WA_WM_CPU_RSTB_MASK BIT(5)
- #define MTK_WO_MCU_CFG_LS_WF_WM_WA_WA_CPU_RSTB_MASK BIT(0)
-
-+#define MTK_WED_WO_RING_SIZE 256
-+#define MTK_WED_WO_CMD_LEN 1504
-+
-+#define MTK_WED_WO_TXCH_NUM 0
-+#define MTK_WED_WO_RXCH_NUM 1
-+#define MTK_WED_WO_RXCH_WO_EXCEPTION 7
-+
-+#define MTK_WED_WO_TXCH_INT_MASK BIT(0)
-+#define MTK_WED_WO_RXCH_INT_MASK BIT(1)
-+#define MTK_WED_WO_EXCEPTION_INT_MASK BIT(7)
-+#define MTK_WED_WO_ALL_INT_MASK (MTK_WED_WO_RXCH_INT_MASK | \
-+ MTK_WED_WO_EXCEPTION_INT_MASK)
-+
-+#define MTK_WED_WO_CCIF_BUSY 0x004
-+#define MTK_WED_WO_CCIF_START 0x008
-+#define MTK_WED_WO_CCIF_TCHNUM 0x00c
-+#define MTK_WED_WO_CCIF_RCHNUM 0x010
-+#define MTK_WED_WO_CCIF_RCHNUM_MASK GENMASK(7, 0)
-+
-+#define MTK_WED_WO_CCIF_ACK 0x014
-+#define MTK_WED_WO_CCIF_IRQ0_MASK 0x018
-+#define MTK_WED_WO_CCIF_IRQ1_MASK 0x01c
-+#define MTK_WED_WO_CCIF_DUMMY1 0x020
-+#define MTK_WED_WO_CCIF_DUMMY2 0x024
-+#define MTK_WED_WO_CCIF_DUMMY3 0x028
-+#define MTK_WED_WO_CCIF_DUMMY4 0x02c
-+#define MTK_WED_WO_CCIF_SHADOW1 0x030
-+#define MTK_WED_WO_CCIF_SHADOW2 0x034
-+#define MTK_WED_WO_CCIF_SHADOW3 0x038
-+#define MTK_WED_WO_CCIF_SHADOW4 0x03c
-+#define MTK_WED_WO_CCIF_DUMMY5 0x050
-+#define MTK_WED_WO_CCIF_DUMMY6 0x054
-+#define MTK_WED_WO_CCIF_DUMMY7 0x058
-+#define MTK_WED_WO_CCIF_DUMMY8 0x05c
-+#define MTK_WED_WO_CCIF_SHADOW5 0x060
-+#define MTK_WED_WO_CCIF_SHADOW6 0x064
-+#define MTK_WED_WO_CCIF_SHADOW7 0x068
-+#define MTK_WED_WO_CCIF_SHADOW8 0x06c
-+
-+#define MTK_WED_WO_CTL_SD_LEN1 GENMASK(13, 0)
-+#define MTK_WED_WO_CTL_LAST_SEC1 BIT(14)
-+#define MTK_WED_WO_CTL_BURST BIT(15)
-+#define MTK_WED_WO_CTL_SD_LEN0_SHIFT 16
-+#define MTK_WED_WO_CTL_SD_LEN0 GENMASK(29, 16)
-+#define MTK_WED_WO_CTL_LAST_SEC0 BIT(30)
-+#define MTK_WED_WO_CTL_DMA_DONE BIT(31)
-+#define MTK_WED_WO_INFO_WINFO GENMASK(15, 0)
-+
- struct mtk_wed_wo_memory_region {
- const char *name;
- void __iomem *addr;
-@@ -112,10 +160,53 @@ struct mtk_wed_fw_trailer {
- u32 crc;
- };
-
-+struct mtk_wed_wo_queue_regs {
-+ u32 desc_base;
-+ u32 ring_size;
-+ u32 cpu_idx;
-+ u32 dma_idx;
-+};
-+
-+struct mtk_wed_wo_queue_desc {
-+ __le32 buf0;
-+ __le32 ctrl;
-+ __le32 buf1;
-+ __le32 info;
-+ __le32 reserved[4];
-+} __packed __aligned(32);
-+
-+struct mtk_wed_wo_queue_entry {
-+ dma_addr_t addr;
-+ void *buf;
-+ u32 len;
-+};
-+
-+struct mtk_wed_wo_queue {
-+ struct mtk_wed_wo_queue_regs regs;
-+
-+ struct page_frag_cache cache;
-+ spinlock_t lock;
-+
-+ struct mtk_wed_wo_queue_desc *desc;
-+ dma_addr_t desc_dma;
-+
-+ struct mtk_wed_wo_queue_entry *entry;
-+
-+ u16 head;
-+ u16 tail;
-+ int n_desc;
-+ int queued;
-+ int buf_size;
-+
-+};
-+
- struct mtk_wed_wo {
- struct mtk_wed_hw *hw;
- struct mtk_wed_wo_memory_region boot;
-
-+ struct mtk_wed_wo_queue q_tx;
-+ struct mtk_wed_wo_queue q_rx;
-+
- struct {
- struct mutex mutex;
- int timeout;
-@@ -124,6 +215,15 @@ struct mtk_wed_wo {
- struct sk_buff_head res_q;
- wait_queue_head_t wait;
- } mcu;
-+
-+ struct {
-+ struct regmap *regs;
-+
-+ spinlock_t lock;
-+ struct tasklet_struct irq_tasklet;
-+ int irq;
-+ u32 irq_mask;
-+ } mmio;
- };
-
- static inline int
-@@ -146,5 +246,9 @@ void mtk_wed_mcu_rx_unsolicited_event(st
- int mtk_wed_mcu_send_msg(struct mtk_wed_wo *wo, int id, int cmd,
- const void *data, int len, bool wait_resp);
- int mtk_wed_mcu_init(struct mtk_wed_wo *wo);
-+int mtk_wed_wo_init(struct mtk_wed_hw *hw);
-+void mtk_wed_wo_deinit(struct mtk_wed_hw *hw);
-+int mtk_wed_wo_queue_tx_skb(struct mtk_wed_wo *dev, struct mtk_wed_wo_queue *q,
-+ struct sk_buff *skb);
-
- #endif /* __MTK_WED_WO_H */
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Sat, 5 Nov 2022 23:36:20 +0100
-Subject: [PATCH] net: ethernet: mtk_wed: rename tx_wdma array in rx_wdma
-
-Rename tx_wdma queue array in rx_wdma since this is rx side of wdma soc.
-Moreover rename mtk_wed_wdma_ring_setup routine in
-mtk_wed_wdma_rx_ring_setup()
-
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -253,8 +253,8 @@ mtk_wed_free_tx_rings(struct mtk_wed_dev
-
- for (i = 0; i < ARRAY_SIZE(dev->tx_ring); i++)
- mtk_wed_free_ring(dev, &dev->tx_ring[i]);
-- for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++)
-- mtk_wed_free_ring(dev, &dev->tx_wdma[i]);
-+ for (i = 0; i < ARRAY_SIZE(dev->rx_wdma); i++)
-+ mtk_wed_free_ring(dev, &dev->rx_wdma[i]);
- }
-
- static void
-@@ -688,10 +688,10 @@ mtk_wed_ring_alloc(struct mtk_wed_device
- }
-
- static int
--mtk_wed_wdma_ring_setup(struct mtk_wed_device *dev, int idx, int size)
-+mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev, int idx, int size)
- {
- u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version;
-- struct mtk_wed_ring *wdma = &dev->tx_wdma[idx];
-+ struct mtk_wed_ring *wdma = &dev->rx_wdma[idx];
-
- if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, desc_size))
- return -ENOMEM;
-@@ -805,9 +805,9 @@ mtk_wed_start(struct mtk_wed_device *dev
- {
- int i;
-
-- for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++)
-- if (!dev->tx_wdma[i].desc)
-- mtk_wed_wdma_ring_setup(dev, i, 16);
-+ for (i = 0; i < ARRAY_SIZE(dev->rx_wdma); i++)
-+ if (!dev->rx_wdma[i].desc)
-+ mtk_wed_wdma_rx_ring_setup(dev, i, 16);
-
- mtk_wed_hw_init(dev);
- mtk_wed_configure_irq(dev, irq_mask);
-@@ -916,7 +916,7 @@ mtk_wed_tx_ring_setup(struct mtk_wed_dev
- sizeof(*ring->desc)))
- return -ENOMEM;
-
-- if (mtk_wed_wdma_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE))
-+ if (mtk_wed_wdma_rx_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE))
- return -ENOMEM;
-
- ring->reg_base = MTK_WED_RING_TX(idx);
---- a/include/linux/soc/mediatek/mtk_wed.h
-+++ b/include/linux/soc/mediatek/mtk_wed.h
-@@ -7,6 +7,7 @@
- #include <linux/pci.h>
-
- #define MTK_WED_TX_QUEUES 2
-+#define MTK_WED_RX_QUEUES 2
-
- struct mtk_wed_hw;
- struct mtk_wdma_desc;
-@@ -66,7 +67,7 @@ struct mtk_wed_device {
-
- struct mtk_wed_ring tx_ring[MTK_WED_TX_QUEUES];
- struct mtk_wed_ring txfree_ring;
-- struct mtk_wed_ring tx_wdma[MTK_WED_TX_QUEUES];
-+ struct mtk_wed_ring rx_wdma[MTK_WED_RX_QUEUES];
-
- struct {
- int size;
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Sat, 5 Nov 2022 23:36:21 +0100
-Subject: [PATCH] net: ethernet: mtk_wed: add configure wed wo support
-
-Enable RX Wireless Ethernet Dispatch available on MT7986 Soc.
-
-Tested-by: Daniel Golle <daniel@makrotopia.org>
-Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -9,6 +9,7 @@
- #include <linux/skbuff.h>
- #include <linux/of_platform.h>
- #include <linux/of_address.h>
-+#include <linux/of_reserved_mem.h>
- #include <linux/mfd/syscon.h>
- #include <linux/debugfs.h>
- #include <linux/soc/mediatek/mtk_wed.h>
-@@ -23,6 +24,7 @@
- #define MTK_WED_PKT_SIZE 1900
- #define MTK_WED_BUF_SIZE 2048
- #define MTK_WED_BUF_PER_PAGE (PAGE_SIZE / 2048)
-+#define MTK_WED_RX_RING_SIZE 1536
-
- #define MTK_WED_TX_RING_SIZE 2048
- #define MTK_WED_WDMA_RING_SIZE 1024
-@@ -31,6 +33,10 @@
- #define MTK_WED_PER_GROUP_PKT 128
-
- #define MTK_WED_FBUF_SIZE 128
-+#define MTK_WED_MIOD_CNT 16
-+#define MTK_WED_FB_CMD_CNT 1024
-+#define MTK_WED_RRO_QUE_CNT 8192
-+#define MTK_WED_MIOD_ENTRY_CNT 128
-
- static struct mtk_wed_hw *hw_list[2];
- static DEFINE_MUTEX(hw_lock);
-@@ -65,12 +71,76 @@ wdma_set(struct mtk_wed_device *dev, u32
- wdma_m32(dev, reg, 0, mask);
- }
-
-+static void
-+wdma_clr(struct mtk_wed_device *dev, u32 reg, u32 mask)
-+{
-+ wdma_m32(dev, reg, mask, 0);
-+}
-+
-+static u32
-+wifi_r32(struct mtk_wed_device *dev, u32 reg)
-+{
-+ return readl(dev->wlan.base + reg);
-+}
-+
-+static void
-+wifi_w32(struct mtk_wed_device *dev, u32 reg, u32 val)
-+{
-+ writel(val, dev->wlan.base + reg);
-+}
-+
- static u32
- mtk_wed_read_reset(struct mtk_wed_device *dev)
- {
- return wed_r32(dev, MTK_WED_RESET);
- }
-
-+static u32
-+mtk_wdma_read_reset(struct mtk_wed_device *dev)
-+{
-+ return wdma_r32(dev, MTK_WDMA_GLO_CFG);
-+}
-+
-+static void
-+mtk_wdma_rx_reset(struct mtk_wed_device *dev)
-+{
-+ u32 status, mask = MTK_WDMA_GLO_CFG_RX_DMA_BUSY;
-+ int i;
-+
-+ wdma_clr(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_RX_DMA_EN);
-+ if (readx_poll_timeout(mtk_wdma_read_reset, dev, status,
-+ !(status & mask), 0, 1000))
-+ dev_err(dev->hw->dev, "rx reset failed\n");
-+
-+ for (i = 0; i < ARRAY_SIZE(dev->rx_wdma); i++) {
-+ if (dev->rx_wdma[i].desc)
-+ continue;
-+
-+ wdma_w32(dev,
-+ MTK_WDMA_RING_RX(i) + MTK_WED_RING_OFS_CPU_IDX, 0);
-+ }
-+}
-+
-+static void
-+mtk_wdma_tx_reset(struct mtk_wed_device *dev)
-+{
-+ u32 status, mask = MTK_WDMA_GLO_CFG_TX_DMA_BUSY;
-+ int i;
-+
-+ wdma_clr(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_TX_DMA_EN);
-+ if (readx_poll_timeout(mtk_wdma_read_reset, dev, status,
-+ !(status & mask), 0, 1000))
-+ dev_err(dev->hw->dev, "tx reset failed\n");
-+
-+ for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++) {
-+ if (dev->tx_wdma[i].desc)
-+ continue;
-+
-+ wdma_w32(dev,
-+ MTK_WDMA_RING_TX(i) + MTK_WED_RING_OFS_CPU_IDX, 0);
-+ }
-+}
-+
- static void
- mtk_wed_reset(struct mtk_wed_device *dev, u32 mask)
- {
-@@ -82,6 +152,54 @@ mtk_wed_reset(struct mtk_wed_device *dev
- WARN_ON_ONCE(1);
- }
-
-+static u32
-+mtk_wed_wo_read_status(struct mtk_wed_device *dev)
-+{
-+ return wed_r32(dev, MTK_WED_SCR0 + 4 * MTK_WED_DUMMY_CR_WO_STATUS);
-+}
-+
-+static void
-+mtk_wed_wo_reset(struct mtk_wed_device *dev)
-+{
-+ struct mtk_wed_wo *wo = dev->hw->wed_wo;
-+ u8 state = MTK_WED_WO_STATE_DISABLE;
-+ void __iomem *reg;
-+ u32 val;
-+
-+ mtk_wdma_tx_reset(dev);
-+ mtk_wed_reset(dev, MTK_WED_RESET_WED);
-+
-+ mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO,
-+ MTK_WED_WO_CMD_CHANGE_STATE, &state,
-+ sizeof(state), false);
-+
-+ if (readx_poll_timeout(mtk_wed_wo_read_status, dev, val,
-+ val == MTK_WED_WOIF_DISABLE_DONE,
-+ 100, MTK_WOCPU_TIMEOUT))
-+ dev_err(dev->hw->dev, "failed to disable wed-wo\n");
-+
-+ reg = ioremap(MTK_WED_WO_CPU_MCUSYS_RESET_ADDR, 4);
-+
-+ val = readl(reg);
-+ switch (dev->hw->index) {
-+ case 0:
-+ val |= MTK_WED_WO_CPU_WO0_MCUSYS_RESET_MASK;
-+ writel(val, reg);
-+ val &= ~MTK_WED_WO_CPU_WO0_MCUSYS_RESET_MASK;
-+ writel(val, reg);
-+ break;
-+ case 1:
-+ val |= MTK_WED_WO_CPU_WO1_MCUSYS_RESET_MASK;
-+ writel(val, reg);
-+ val &= ~MTK_WED_WO_CPU_WO1_MCUSYS_RESET_MASK;
-+ writel(val, reg);
-+ break;
-+ default:
-+ break;
-+ }
-+ iounmap(reg);
-+}
-+
- static struct mtk_wed_hw *
- mtk_wed_assign(struct mtk_wed_device *dev)
- {
-@@ -116,7 +234,7 @@ out:
- }
-
- static int
--mtk_wed_buffer_alloc(struct mtk_wed_device *dev)
-+mtk_wed_tx_buffer_alloc(struct mtk_wed_device *dev)
- {
- struct mtk_wdma_desc *desc;
- dma_addr_t desc_phys;
-@@ -133,16 +251,16 @@ mtk_wed_buffer_alloc(struct mtk_wed_devi
- if (!page_list)
- return -ENOMEM;
-
-- dev->buf_ring.size = ring_size;
-- dev->buf_ring.pages = page_list;
-+ dev->tx_buf_ring.size = ring_size;
-+ dev->tx_buf_ring.pages = page_list;
-
- desc = dma_alloc_coherent(dev->hw->dev, ring_size * sizeof(*desc),
- &desc_phys, GFP_KERNEL);
- if (!desc)
- return -ENOMEM;
-
-- dev->buf_ring.desc = desc;
-- dev->buf_ring.desc_phys = desc_phys;
-+ dev->tx_buf_ring.desc = desc;
-+ dev->tx_buf_ring.desc_phys = desc_phys;
-
- for (i = 0, page_idx = 0; i < ring_size; i += MTK_WED_BUF_PER_PAGE) {
- dma_addr_t page_phys, buf_phys;
-@@ -203,10 +321,10 @@ mtk_wed_buffer_alloc(struct mtk_wed_devi
- }
-
- static void
--mtk_wed_free_buffer(struct mtk_wed_device *dev)
-+mtk_wed_free_tx_buffer(struct mtk_wed_device *dev)
- {
-- struct mtk_wdma_desc *desc = dev->buf_ring.desc;
-- void **page_list = dev->buf_ring.pages;
-+ struct mtk_wdma_desc *desc = dev->tx_buf_ring.desc;
-+ void **page_list = dev->tx_buf_ring.pages;
- int page_idx;
- int i;
-
-@@ -216,7 +334,8 @@ mtk_wed_free_buffer(struct mtk_wed_devic
- if (!desc)
- goto free_pagelist;
-
-- for (i = 0, page_idx = 0; i < dev->buf_ring.size; i += MTK_WED_BUF_PER_PAGE) {
-+ for (i = 0, page_idx = 0; i < dev->tx_buf_ring.size;
-+ i += MTK_WED_BUF_PER_PAGE) {
- void *page = page_list[page_idx++];
- dma_addr_t buf_addr;
-
-@@ -229,13 +348,59 @@ mtk_wed_free_buffer(struct mtk_wed_devic
- __free_page(page);
- }
-
-- dma_free_coherent(dev->hw->dev, dev->buf_ring.size * sizeof(*desc),
-- desc, dev->buf_ring.desc_phys);
-+ dma_free_coherent(dev->hw->dev, dev->tx_buf_ring.size * sizeof(*desc),
-+ desc, dev->tx_buf_ring.desc_phys);
-
- free_pagelist:
- kfree(page_list);
- }
-
-+static int
-+mtk_wed_rx_buffer_alloc(struct mtk_wed_device *dev)
-+{
-+ struct mtk_rxbm_desc *desc;
-+ dma_addr_t desc_phys;
-+
-+ dev->rx_buf_ring.size = dev->wlan.rx_nbuf;
-+ desc = dma_alloc_coherent(dev->hw->dev,
-+ dev->wlan.rx_nbuf * sizeof(*desc),
-+ &desc_phys, GFP_KERNEL);
-+ if (!desc)
-+ return -ENOMEM;
-+
-+ dev->rx_buf_ring.desc = desc;
-+ dev->rx_buf_ring.desc_phys = desc_phys;
-+ dev->wlan.init_rx_buf(dev, dev->wlan.rx_npkt);
-+
-+ return 0;
-+}
-+
-+static void
-+mtk_wed_free_rx_buffer(struct mtk_wed_device *dev)
-+{
-+ struct mtk_rxbm_desc *desc = dev->rx_buf_ring.desc;
-+
-+ if (!desc)
-+ return;
-+
-+ dev->wlan.release_rx_buf(dev);
-+ dma_free_coherent(dev->hw->dev, dev->rx_buf_ring.size * sizeof(*desc),
-+ desc, dev->rx_buf_ring.desc_phys);
-+}
-+
-+static void
-+mtk_wed_rx_buffer_hw_init(struct mtk_wed_device *dev)
-+{
-+ wed_w32(dev, MTK_WED_RX_BM_RX_DMAD,
-+ FIELD_PREP(MTK_WED_RX_BM_RX_DMAD_SDL0, dev->wlan.rx_size));
-+ wed_w32(dev, MTK_WED_RX_BM_BASE, dev->rx_buf_ring.desc_phys);
-+ wed_w32(dev, MTK_WED_RX_BM_INIT_PTR, MTK_WED_RX_BM_INIT_SW_TAIL |
-+ FIELD_PREP(MTK_WED_RX_BM_SW_TAIL, dev->wlan.rx_npkt));
-+ wed_w32(dev, MTK_WED_RX_BM_DYN_ALLOC_TH,
-+ FIELD_PREP(MTK_WED_RX_BM_DYN_ALLOC_TH_H, 0xffff));
-+ wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_BM_EN);
-+}
-+
- static void
- mtk_wed_free_ring(struct mtk_wed_device *dev, struct mtk_wed_ring *ring)
- {
-@@ -247,6 +412,13 @@ mtk_wed_free_ring(struct mtk_wed_device
- }
-
- static void
-+mtk_wed_free_rx_rings(struct mtk_wed_device *dev)
-+{
-+ mtk_wed_free_rx_buffer(dev);
-+ mtk_wed_free_ring(dev, &dev->rro.ring);
-+}
-+
-+static void
- mtk_wed_free_tx_rings(struct mtk_wed_device *dev)
- {
- int i;
-@@ -291,6 +463,38 @@ mtk_wed_set_512_support(struct mtk_wed_d
- }
- }
-
-+#define MTK_WFMDA_RX_DMA_EN BIT(2)
-+static void
-+mtk_wed_check_wfdma_rx_fill(struct mtk_wed_device *dev, int idx)
-+{
-+ u32 val;
-+ int i;
-+
-+ if (!(dev->rx_ring[idx].flags & MTK_WED_RING_CONFIGURED))
-+ return; /* queue is not configured by mt76 */
-+
-+ for (i = 0; i < 3; i++) {
-+ u32 cur_idx;
-+
-+ cur_idx = wed_r32(dev,
-+ MTK_WED_WPDMA_RING_RX_DATA(idx) +
-+ MTK_WED_RING_OFS_CPU_IDX);
-+ if (cur_idx == MTK_WED_RX_RING_SIZE - 1)
-+ break;
-+
-+ usleep_range(100000, 200000);
-+ }
-+
-+ if (i == 3) {
-+ dev_err(dev->hw->dev, "rx dma enable failed\n");
-+ return;
-+ }
-+
-+ val = wifi_r32(dev, dev->wlan.wpdma_rx_glo - dev->wlan.phy_base) |
-+ MTK_WFMDA_RX_DMA_EN;
-+ wifi_w32(dev, dev->wlan.wpdma_rx_glo - dev->wlan.phy_base, val);
-+}
-+
- static void
- mtk_wed_dma_disable(struct mtk_wed_device *dev)
- {
-@@ -304,20 +508,25 @@ mtk_wed_dma_disable(struct mtk_wed_devic
- MTK_WED_GLO_CFG_TX_DMA_EN |
- MTK_WED_GLO_CFG_RX_DMA_EN);
-
-- wdma_m32(dev, MTK_WDMA_GLO_CFG,
-+ wdma_clr(dev, MTK_WDMA_GLO_CFG,
- MTK_WDMA_GLO_CFG_TX_DMA_EN |
- MTK_WDMA_GLO_CFG_RX_INFO1_PRERES |
-- MTK_WDMA_GLO_CFG_RX_INFO2_PRERES, 0);
-+ MTK_WDMA_GLO_CFG_RX_INFO2_PRERES);
-
- if (dev->hw->version == 1) {
- regmap_write(dev->hw->mirror, dev->hw->index * 4, 0);
-- wdma_m32(dev, MTK_WDMA_GLO_CFG,
-- MTK_WDMA_GLO_CFG_RX_INFO3_PRERES, 0);
-+ wdma_clr(dev, MTK_WDMA_GLO_CFG,
-+ MTK_WDMA_GLO_CFG_RX_INFO3_PRERES);
- } else {
- wed_clr(dev, MTK_WED_WPDMA_GLO_CFG,
- MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_PKT_PROC |
- MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC);
-
-+ wed_clr(dev, MTK_WED_WPDMA_RX_D_GLO_CFG,
-+ MTK_WED_WPDMA_RX_D_RX_DRV_EN);
-+ wed_clr(dev, MTK_WED_WDMA_GLO_CFG,
-+ MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK);
-+
- mtk_wed_set_512_support(dev, false);
- }
- }
-@@ -338,6 +547,13 @@ mtk_wed_stop(struct mtk_wed_device *dev)
- wdma_w32(dev, MTK_WDMA_INT_MASK, 0);
- wdma_w32(dev, MTK_WDMA_INT_GRP2, 0);
- wed_w32(dev, MTK_WED_WPDMA_INT_MASK, 0);
-+
-+ if (dev->hw->version == 1)
-+ return;
-+
-+ wed_w32(dev, MTK_WED_EXT_INT_MASK1, 0);
-+ wed_w32(dev, MTK_WED_EXT_INT_MASK2, 0);
-+ wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_BM_EN);
- }
-
- static void
-@@ -353,11 +569,21 @@ mtk_wed_detach(struct mtk_wed_device *de
- wdma_w32(dev, MTK_WDMA_RESET_IDX, 0);
-
- mtk_wed_reset(dev, MTK_WED_RESET_WED);
-+ if (mtk_wed_get_rx_capa(dev)) {
-+ wdma_clr(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_TX_DMA_EN);
-+ wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_TX);
-+ wdma_w32(dev, MTK_WDMA_RESET_IDX, 0);
-+ }
-
-- mtk_wed_free_buffer(dev);
-+ mtk_wed_free_tx_buffer(dev);
- mtk_wed_free_tx_rings(dev);
-- if (hw->version != 1)
-+
-+ if (mtk_wed_get_rx_capa(dev)) {
-+ mtk_wed_wo_reset(dev);
-+ mtk_wed_free_rx_rings(dev);
- mtk_wed_wo_deinit(hw);
-+ mtk_wdma_rx_reset(dev);
-+ }
-
- if (dev->wlan.bus_type == MTK_WED_BUS_PCIE) {
- struct device_node *wlan_node;
-@@ -434,10 +660,12 @@ mtk_wed_set_wpdma(struct mtk_wed_device
- } else {
- mtk_wed_bus_init(dev);
-
-- wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_int);
-- wed_w32(dev, MTK_WED_WPDMA_CFG_INT_MASK, dev->wlan.wpdma_mask);
-- wed_w32(dev, MTK_WED_WPDMA_CFG_TX, dev->wlan.wpdma_tx);
-- wed_w32(dev, MTK_WED_WPDMA_CFG_TX_FREE, dev->wlan.wpdma_txfree);
-+ wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_int);
-+ wed_w32(dev, MTK_WED_WPDMA_CFG_INT_MASK, dev->wlan.wpdma_mask);
-+ wed_w32(dev, MTK_WED_WPDMA_CFG_TX, dev->wlan.wpdma_tx);
-+ wed_w32(dev, MTK_WED_WPDMA_CFG_TX_FREE, dev->wlan.wpdma_txfree);
-+ wed_w32(dev, MTK_WED_WPDMA_RX_GLO_CFG, dev->wlan.wpdma_rx_glo);
-+ wed_w32(dev, MTK_WED_WPDMA_RX_RING, dev->wlan.wpdma_rx);
- }
- }
-
-@@ -487,6 +715,132 @@ mtk_wed_hw_init_early(struct mtk_wed_dev
- }
- }
-
-+static int
-+mtk_wed_rro_ring_alloc(struct mtk_wed_device *dev, struct mtk_wed_ring *ring,
-+ int size)
-+{
-+ ring->desc = dma_alloc_coherent(dev->hw->dev,
-+ size * sizeof(*ring->desc),
-+ &ring->desc_phys, GFP_KERNEL);
-+ if (!ring->desc)
-+ return -ENOMEM;
-+
-+ ring->desc_size = sizeof(*ring->desc);
-+ ring->size = size;
-+ memset(ring->desc, 0, size);
-+
-+ return 0;
-+}
-+
-+#define MTK_WED_MIOD_COUNT (MTK_WED_MIOD_ENTRY_CNT * MTK_WED_MIOD_CNT)
-+static int
-+mtk_wed_rro_alloc(struct mtk_wed_device *dev)
-+{
-+ struct reserved_mem *rmem;
-+ struct device_node *np;
-+ int index;
-+
-+ index = of_property_match_string(dev->hw->node, "memory-region-names",
-+ "wo-dlm");
-+ if (index < 0)
-+ return index;
-+
-+ np = of_parse_phandle(dev->hw->node, "memory-region", index);
-+ if (!np)
-+ return -ENODEV;
-+
-+ rmem = of_reserved_mem_lookup(np);
-+ of_node_put(np);
-+
-+ if (!rmem)
-+ return -ENODEV;
-+
-+ dev->rro.miod_phys = rmem->base;
-+ dev->rro.fdbk_phys = MTK_WED_MIOD_COUNT + dev->rro.miod_phys;
-+
-+ return mtk_wed_rro_ring_alloc(dev, &dev->rro.ring,
-+ MTK_WED_RRO_QUE_CNT);
-+}
-+
-+static int
-+mtk_wed_rro_cfg(struct mtk_wed_device *dev)
-+{
-+ struct mtk_wed_wo *wo = dev->hw->wed_wo;
-+ struct {
-+ struct {
-+ __le32 base;
-+ __le32 cnt;
-+ __le32 unit;
-+ } ring[2];
-+ __le32 wed;
-+ u8 version;
-+ } req = {
-+ .ring[0] = {
-+ .base = cpu_to_le32(MTK_WED_WOCPU_VIEW_MIOD_BASE),
-+ .cnt = cpu_to_le32(MTK_WED_MIOD_CNT),
-+ .unit = cpu_to_le32(MTK_WED_MIOD_ENTRY_CNT),
-+ },
-+ .ring[1] = {
-+ .base = cpu_to_le32(MTK_WED_WOCPU_VIEW_MIOD_BASE +
-+ MTK_WED_MIOD_COUNT),
-+ .cnt = cpu_to_le32(MTK_WED_FB_CMD_CNT),
-+ .unit = cpu_to_le32(4),
-+ },
-+ };
-+
-+ return mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO,
-+ MTK_WED_WO_CMD_WED_CFG,
-+ &req, sizeof(req), true);
-+}
-+
-+static void
-+mtk_wed_rro_hw_init(struct mtk_wed_device *dev)
-+{
-+ wed_w32(dev, MTK_WED_RROQM_MIOD_CFG,
-+ FIELD_PREP(MTK_WED_RROQM_MIOD_MID_DW, 0x70 >> 2) |
-+ FIELD_PREP(MTK_WED_RROQM_MIOD_MOD_DW, 0x10 >> 2) |
-+ FIELD_PREP(MTK_WED_RROQM_MIOD_ENTRY_DW,
-+ MTK_WED_MIOD_ENTRY_CNT >> 2));
-+
-+ wed_w32(dev, MTK_WED_RROQM_MIOD_CTRL0, dev->rro.miod_phys);
-+ wed_w32(dev, MTK_WED_RROQM_MIOD_CTRL1,
-+ FIELD_PREP(MTK_WED_RROQM_MIOD_CNT, MTK_WED_MIOD_CNT));
-+ wed_w32(dev, MTK_WED_RROQM_FDBK_CTRL0, dev->rro.fdbk_phys);
-+ wed_w32(dev, MTK_WED_RROQM_FDBK_CTRL1,
-+ FIELD_PREP(MTK_WED_RROQM_FDBK_CNT, MTK_WED_FB_CMD_CNT));
-+ wed_w32(dev, MTK_WED_RROQM_FDBK_CTRL2, 0);
-+ wed_w32(dev, MTK_WED_RROQ_BASE_L, dev->rro.ring.desc_phys);
-+
-+ wed_set(dev, MTK_WED_RROQM_RST_IDX,
-+ MTK_WED_RROQM_RST_IDX_MIOD |
-+ MTK_WED_RROQM_RST_IDX_FDBK);
-+
-+ wed_w32(dev, MTK_WED_RROQM_RST_IDX, 0);
-+ wed_w32(dev, MTK_WED_RROQM_MIOD_CTRL2, MTK_WED_MIOD_CNT - 1);
-+ wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_RX_RRO_QM_EN);
-+}
-+
-+static void
-+mtk_wed_route_qm_hw_init(struct mtk_wed_device *dev)
-+{
-+ wed_w32(dev, MTK_WED_RESET, MTK_WED_RESET_RX_ROUTE_QM);
-+
-+ for (;;) {
-+ usleep_range(100, 200);
-+ if (!(wed_r32(dev, MTK_WED_RESET) & MTK_WED_RESET_RX_ROUTE_QM))
-+ break;
-+ }
-+
-+ /* configure RX_ROUTE_QM */
-+ wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_Q_RST);
-+ wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_TXDMAD_FPORT);
-+ wed_set(dev, MTK_WED_RTQM_GLO_CFG,
-+ FIELD_PREP(MTK_WED_RTQM_TXDMAD_FPORT, 0x3 + dev->hw->index));
-+ wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_Q_RST);
-+ /* enable RX_ROUTE_QM */
-+ wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_RX_ROUTE_QM_EN);
-+}
-+
- static void
- mtk_wed_hw_init(struct mtk_wed_device *dev)
- {
-@@ -498,11 +852,11 @@ mtk_wed_hw_init(struct mtk_wed_device *d
- wed_w32(dev, MTK_WED_TX_BM_CTRL,
- MTK_WED_TX_BM_CTRL_PAUSE |
- FIELD_PREP(MTK_WED_TX_BM_CTRL_VLD_GRP_NUM,
-- dev->buf_ring.size / 128) |
-+ dev->tx_buf_ring.size / 128) |
- FIELD_PREP(MTK_WED_TX_BM_CTRL_RSV_GRP_NUM,
- MTK_WED_TX_RING_SIZE / 256));
-
-- wed_w32(dev, MTK_WED_TX_BM_BASE, dev->buf_ring.desc_phys);
-+ wed_w32(dev, MTK_WED_TX_BM_BASE, dev->tx_buf_ring.desc_phys);
-
- wed_w32(dev, MTK_WED_TX_BM_BUF_LEN, MTK_WED_PKT_SIZE);
-
-@@ -529,9 +883,9 @@ mtk_wed_hw_init(struct mtk_wed_device *d
- wed_w32(dev, MTK_WED_TX_TKID_CTRL,
- MTK_WED_TX_TKID_CTRL_PAUSE |
- FIELD_PREP(MTK_WED_TX_TKID_CTRL_VLD_GRP_NUM,
-- dev->buf_ring.size / 128) |
-+ dev->tx_buf_ring.size / 128) |
- FIELD_PREP(MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM,
-- dev->buf_ring.size / 128));
-+ dev->tx_buf_ring.size / 128));
- wed_w32(dev, MTK_WED_TX_TKID_DYN_THR,
- FIELD_PREP(MTK_WED_TX_TKID_DYN_THR_LO, 0) |
- MTK_WED_TX_TKID_DYN_THR_HI);
-@@ -539,18 +893,28 @@ mtk_wed_hw_init(struct mtk_wed_device *d
-
- mtk_wed_reset(dev, MTK_WED_RESET_TX_BM);
-
-- if (dev->hw->version == 1)
-+ if (dev->hw->version == 1) {
- wed_set(dev, MTK_WED_CTRL,
- MTK_WED_CTRL_WED_TX_BM_EN |
- MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
-- else
-+ } else {
- wed_clr(dev, MTK_WED_TX_TKID_CTRL, MTK_WED_TX_TKID_CTRL_PAUSE);
-+ /* rx hw init */
-+ wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX,
-+ MTK_WED_WPDMA_RX_D_RST_CRX_IDX |
-+ MTK_WED_WPDMA_RX_D_RST_DRV_IDX);
-+ wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, 0);
-+
-+ mtk_wed_rx_buffer_hw_init(dev);
-+ mtk_wed_rro_hw_init(dev);
-+ mtk_wed_route_qm_hw_init(dev);
-+ }
-
- wed_clr(dev, MTK_WED_TX_BM_CTRL, MTK_WED_TX_BM_CTRL_PAUSE);
- }
-
- static void
--mtk_wed_ring_reset(struct mtk_wed_ring *ring, int size)
-+mtk_wed_ring_reset(struct mtk_wed_ring *ring, int size, bool tx)
- {
- void *head = (void *)ring->desc;
- int i;
-@@ -560,7 +924,10 @@ mtk_wed_ring_reset(struct mtk_wed_ring *
-
- desc = (struct mtk_wdma_desc *)(head + i * ring->desc_size);
- desc->buf0 = 0;
-- desc->ctrl = cpu_to_le32(MTK_WDMA_DESC_CTRL_DMA_DONE);
-+ if (tx)
-+ desc->ctrl = cpu_to_le32(MTK_WDMA_DESC_CTRL_DMA_DONE);
-+ else
-+ desc->ctrl = cpu_to_le32(MTK_WFDMA_DESC_CTRL_TO_HOST);
- desc->buf1 = 0;
- desc->info = 0;
- }
-@@ -616,7 +983,8 @@ mtk_wed_reset_dma(struct mtk_wed_device
- if (!dev->tx_ring[i].desc)
- continue;
-
-- mtk_wed_ring_reset(&dev->tx_ring[i], MTK_WED_TX_RING_SIZE);
-+ mtk_wed_ring_reset(&dev->tx_ring[i], MTK_WED_TX_RING_SIZE,
-+ true);
- }
-
- if (mtk_wed_poll_busy(dev))
-@@ -634,6 +1002,9 @@ mtk_wed_reset_dma(struct mtk_wed_device
- wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_RX);
- wdma_w32(dev, MTK_WDMA_RESET_IDX, 0);
-
-+ if (mtk_wed_get_rx_capa(dev))
-+ mtk_wdma_rx_reset(dev);
-+
- if (busy) {
- mtk_wed_reset(dev, MTK_WED_RESET_WDMA_INT_AGENT);
- mtk_wed_reset(dev, MTK_WED_RESET_WDMA_RX_DRV);
-@@ -668,12 +1039,11 @@ mtk_wed_reset_dma(struct mtk_wed_device
- MTK_WED_WPDMA_RESET_IDX_RX);
- wed_w32(dev, MTK_WED_WPDMA_RESET_IDX, 0);
- }
--
- }
-
- static int
- mtk_wed_ring_alloc(struct mtk_wed_device *dev, struct mtk_wed_ring *ring,
-- int size, u32 desc_size)
-+ int size, u32 desc_size, bool tx)
- {
- ring->desc = dma_alloc_coherent(dev->hw->dev, size * desc_size,
- &ring->desc_phys, GFP_KERNEL);
-@@ -682,7 +1052,7 @@ mtk_wed_ring_alloc(struct mtk_wed_device
-
- ring->desc_size = desc_size;
- ring->size = size;
-- mtk_wed_ring_reset(ring, size);
-+ mtk_wed_ring_reset(ring, size, tx);
-
- return 0;
- }
-@@ -691,9 +1061,14 @@ static int
- mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev, int idx, int size)
- {
- u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version;
-- struct mtk_wed_ring *wdma = &dev->rx_wdma[idx];
-+ struct mtk_wed_ring *wdma;
-
-- if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, desc_size))
-+ if (idx >= ARRAY_SIZE(dev->rx_wdma))
-+ return -EINVAL;
-+
-+ wdma = &dev->rx_wdma[idx];
-+ if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, desc_size,
-+ true))
- return -ENOMEM;
-
- wdma_w32(dev, MTK_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_BASE,
-@@ -710,6 +1085,60 @@ mtk_wed_wdma_rx_ring_setup(struct mtk_we
- return 0;
- }
-
-+static int
-+mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev, int idx, int size)
-+{
-+ u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version;
-+ struct mtk_wed_ring *wdma;
-+
-+ if (idx >= ARRAY_SIZE(dev->tx_wdma))
-+ return -EINVAL;
-+
-+ wdma = &dev->tx_wdma[idx];
-+ if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, desc_size,
-+ true))
-+ return -ENOMEM;
-+
-+ wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_BASE,
-+ wdma->desc_phys);
-+ wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_COUNT,
-+ size);
-+ wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_CPU_IDX, 0);
-+ wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_DMA_IDX, 0);
-+
-+ if (!idx) {
-+ wed_w32(dev, MTK_WED_WDMA_RING_TX + MTK_WED_RING_OFS_BASE,
-+ wdma->desc_phys);
-+ wed_w32(dev, MTK_WED_WDMA_RING_TX + MTK_WED_RING_OFS_COUNT,
-+ size);
-+ wed_w32(dev, MTK_WED_WDMA_RING_TX + MTK_WED_RING_OFS_CPU_IDX,
-+ 0);
-+ wed_w32(dev, MTK_WED_WDMA_RING_TX + MTK_WED_RING_OFS_DMA_IDX,
-+ 0);
-+ }
-+
-+ return 0;
-+}
-+
-+static void
-+mtk_wed_ppe_check(struct mtk_wed_device *dev, struct sk_buff *skb,
-+ u32 reason, u32 hash)
-+{
-+ struct mtk_eth *eth = dev->hw->eth;
-+ struct ethhdr *eh;
-+
-+ if (!skb)
-+ return;
-+
-+ if (reason != MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED)
-+ return;
-+
-+ skb_set_mac_header(skb, 0);
-+ eh = eth_hdr(skb);
-+ skb->protocol = eh->h_proto;
-+ mtk_ppe_check_skb(eth->ppe[dev->hw->index], skb, hash);
-+}
-+
- static void
- mtk_wed_configure_irq(struct mtk_wed_device *dev, u32 irq_mask)
- {
-@@ -732,6 +1161,8 @@ mtk_wed_configure_irq(struct mtk_wed_dev
-
- wed_clr(dev, MTK_WED_WDMA_INT_CTRL, wdma_mask);
- } else {
-+ wdma_mask |= FIELD_PREP(MTK_WDMA_INT_MASK_TX_DONE,
-+ GENMASK(1, 0));
- /* initail tx interrupt trigger */
- wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_TX,
- MTK_WED_WPDMA_INT_CTRL_TX0_DONE_EN |
-@@ -750,6 +1181,16 @@ mtk_wed_configure_irq(struct mtk_wed_dev
- FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_TRIG,
- dev->wlan.txfree_tbit));
-
-+ wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_RX,
-+ MTK_WED_WPDMA_INT_CTRL_RX0_EN |
-+ MTK_WED_WPDMA_INT_CTRL_RX0_CLR |
-+ MTK_WED_WPDMA_INT_CTRL_RX1_EN |
-+ MTK_WED_WPDMA_INT_CTRL_RX1_CLR |
-+ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RX0_DONE_TRIG,
-+ dev->wlan.rx_tbit[0]) |
-+ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RX1_DONE_TRIG,
-+ dev->wlan.rx_tbit[1]));
-+
- wed_w32(dev, MTK_WED_WDMA_INT_CLR, wdma_mask);
- wed_set(dev, MTK_WED_WDMA_INT_CTRL,
- FIELD_PREP(MTK_WED_WDMA_INT_CTRL_POLL_SRC_SEL,
-@@ -787,9 +1228,15 @@ mtk_wed_dma_enable(struct mtk_wed_device
- wdma_set(dev, MTK_WDMA_GLO_CFG,
- MTK_WDMA_GLO_CFG_RX_INFO3_PRERES);
- } else {
-+ int i;
-+
- wed_set(dev, MTK_WED_WPDMA_CTRL,
- MTK_WED_WPDMA_CTRL_SDL1_FIXED);
-
-+ wed_set(dev, MTK_WED_WDMA_GLO_CFG,
-+ MTK_WED_WDMA_GLO_CFG_TX_DRV_EN |
-+ MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK);
-+
- wed_set(dev, MTK_WED_WPDMA_GLO_CFG,
- MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_PKT_PROC |
- MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC);
-@@ -797,6 +1244,15 @@ mtk_wed_dma_enable(struct mtk_wed_device
- wed_clr(dev, MTK_WED_WPDMA_GLO_CFG,
- MTK_WED_WPDMA_GLO_CFG_TX_TKID_KEEP |
- MTK_WED_WPDMA_GLO_CFG_TX_DMAD_DW3_PREV);
-+
-+ wed_set(dev, MTK_WED_WPDMA_RX_D_GLO_CFG,
-+ MTK_WED_WPDMA_RX_D_RX_DRV_EN |
-+ FIELD_PREP(MTK_WED_WPDMA_RX_D_RXD_READ_LEN, 0x18) |
-+ FIELD_PREP(MTK_WED_WPDMA_RX_D_INIT_PHASE_RXEN_SEL,
-+ 0x2));
-+
-+ for (i = 0; i < MTK_WED_RX_QUEUES; i++)
-+ mtk_wed_check_wfdma_rx_fill(dev, i);
- }
- }
-
-@@ -822,7 +1278,19 @@ mtk_wed_start(struct mtk_wed_device *dev
- val |= BIT(0) | (BIT(1) * !!dev->hw->index);
- regmap_write(dev->hw->mirror, dev->hw->index * 4, val);
- } else {
-- mtk_wed_set_512_support(dev, true);
-+ /* driver set mid ready and only once */
-+ wed_w32(dev, MTK_WED_EXT_INT_MASK1,
-+ MTK_WED_EXT_INT_STATUS_WPDMA_MID_RDY);
-+ wed_w32(dev, MTK_WED_EXT_INT_MASK2,
-+ MTK_WED_EXT_INT_STATUS_WPDMA_MID_RDY);
-+
-+ wed_r32(dev, MTK_WED_EXT_INT_MASK1);
-+ wed_r32(dev, MTK_WED_EXT_INT_MASK2);
-+
-+ if (mtk_wed_rro_cfg(dev))
-+ return;
-+
-+ mtk_wed_set_512_support(dev, dev->wlan.wcid_512);
- }
-
- mtk_wed_dma_enable(dev);
-@@ -856,7 +1324,7 @@ mtk_wed_attach(struct mtk_wed_device *de
- if (!hw) {
- module_put(THIS_MODULE);
- ret = -ENODEV;
-- goto out;
-+ goto unlock;
- }
-
- device = dev->wlan.bus_type == MTK_WED_BUS_PCIE
-@@ -869,15 +1337,24 @@ mtk_wed_attach(struct mtk_wed_device *de
- dev->dev = hw->dev;
- dev->irq = hw->irq;
- dev->wdma_idx = hw->index;
-+ dev->version = hw->version;
-
- if (hw->eth->dma_dev == hw->eth->dev &&
- of_dma_is_coherent(hw->eth->dev->of_node))
- mtk_eth_set_dma_device(hw->eth, hw->dev);
-
-- ret = mtk_wed_buffer_alloc(dev);
-- if (ret) {
-- mtk_wed_detach(dev);
-+ ret = mtk_wed_tx_buffer_alloc(dev);
-+ if (ret)
- goto out;
-+
-+ if (mtk_wed_get_rx_capa(dev)) {
-+ ret = mtk_wed_rx_buffer_alloc(dev);
-+ if (ret)
-+ goto out;
-+
-+ ret = mtk_wed_rro_alloc(dev);
-+ if (ret)
-+ goto out;
- }
-
- mtk_wed_hw_init_early(dev);
-@@ -886,8 +1363,10 @@ mtk_wed_attach(struct mtk_wed_device *de
- BIT(hw->index), 0);
- else
- ret = mtk_wed_wo_init(hw);
--
- out:
-+ if (ret)
-+ mtk_wed_detach(dev);
-+unlock:
- mutex_unlock(&hw_lock);
-
- return ret;
-@@ -910,10 +1389,11 @@ mtk_wed_tx_ring_setup(struct mtk_wed_dev
- * WDMA RX.
- */
-
-- BUG_ON(idx >= ARRAY_SIZE(dev->tx_ring));
-+ if (WARN_ON(idx >= ARRAY_SIZE(dev->tx_ring)))
-+ return -EINVAL;
-
- if (mtk_wed_ring_alloc(dev, ring, MTK_WED_TX_RING_SIZE,
-- sizeof(*ring->desc)))
-+ sizeof(*ring->desc), true))
- return -ENOMEM;
-
- if (mtk_wed_wdma_rx_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE))
-@@ -960,6 +1440,37 @@ mtk_wed_txfree_ring_setup(struct mtk_wed
- return 0;
- }
-
-+static int
-+mtk_wed_rx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs)
-+{
-+ struct mtk_wed_ring *ring = &dev->rx_ring[idx];
-+
-+ if (WARN_ON(idx >= ARRAY_SIZE(dev->rx_ring)))
-+ return -EINVAL;
-+
-+ if (mtk_wed_ring_alloc(dev, ring, MTK_WED_RX_RING_SIZE,
-+ sizeof(*ring->desc), false))
-+ return -ENOMEM;
-+
-+ if (mtk_wed_wdma_tx_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE))
-+ return -ENOMEM;
-+
-+ ring->reg_base = MTK_WED_RING_RX_DATA(idx);
-+ ring->wpdma = regs;
-+ ring->flags |= MTK_WED_RING_CONFIGURED;
-+
-+ /* WPDMA -> WED */
-+ wpdma_rx_w32(dev, idx, MTK_WED_RING_OFS_BASE, ring->desc_phys);
-+ wpdma_rx_w32(dev, idx, MTK_WED_RING_OFS_COUNT, MTK_WED_RX_RING_SIZE);
-+
-+ wed_w32(dev, MTK_WED_WPDMA_RING_RX_DATA(idx) + MTK_WED_RING_OFS_BASE,
-+ ring->desc_phys);
-+ wed_w32(dev, MTK_WED_WPDMA_RING_RX_DATA(idx) + MTK_WED_RING_OFS_COUNT,
-+ MTK_WED_RX_RING_SIZE);
-+
-+ return 0;
-+}
-+
- static u32
- mtk_wed_irq_get(struct mtk_wed_device *dev, u32 mask)
- {
-@@ -1056,7 +1567,9 @@ void mtk_wed_add_hw(struct device_node *
- static const struct mtk_wed_ops wed_ops = {
- .attach = mtk_wed_attach,
- .tx_ring_setup = mtk_wed_tx_ring_setup,
-+ .rx_ring_setup = mtk_wed_rx_ring_setup,
- .txfree_ring_setup = mtk_wed_txfree_ring_setup,
-+ .msg_update = mtk_wed_mcu_msg_update,
- .start = mtk_wed_start,
- .stop = mtk_wed_stop,
- .reset_dma = mtk_wed_reset_dma,
-@@ -1065,6 +1578,7 @@ void mtk_wed_add_hw(struct device_node *
- .irq_get = mtk_wed_irq_get,
- .irq_set_mask = mtk_wed_irq_set_mask,
- .detach = mtk_wed_detach,
-+ .ppe_check = mtk_wed_ppe_check,
- };
- struct device_node *eth_np = eth->dev->of_node;
- struct platform_device *pdev;
---- a/drivers/net/ethernet/mediatek/mtk_wed.h
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.h
-@@ -87,6 +87,24 @@ wpdma_tx_w32(struct mtk_wed_device *dev,
- }
-
- static inline u32
-+wpdma_rx_r32(struct mtk_wed_device *dev, int ring, u32 reg)
-+{
-+ if (!dev->rx_ring[ring].wpdma)
-+ return 0;
-+
-+ return readl(dev->rx_ring[ring].wpdma + reg);
-+}
-+
-+static inline void
-+wpdma_rx_w32(struct mtk_wed_device *dev, int ring, u32 reg, u32 val)
-+{
-+ if (!dev->rx_ring[ring].wpdma)
-+ return;
-+
-+ writel(val, dev->rx_ring[ring].wpdma + reg);
-+}
-+
-+static inline u32
- wpdma_txfree_r32(struct mtk_wed_device *dev, u32 reg)
- {
- if (!dev->txfree_ring.wpdma)
-@@ -128,6 +146,7 @@ static inline int mtk_wed_flow_add(int i
- static inline void mtk_wed_flow_remove(int index)
- {
- }
-+
- #endif
-
- #ifdef CONFIG_DEBUG_FS
---- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
-@@ -10,6 +10,7 @@
- #include <linux/of_reserved_mem.h>
- #include <linux/mfd/syscon.h>
- #include <linux/soc/mediatek/mtk_wed.h>
-+#include <asm/unaligned.h>
-
- #include "mtk_wed_regs.h"
- #include "mtk_wed_wo.h"
-@@ -60,24 +61,37 @@ void mtk_wed_mcu_rx_event(struct mtk_wed
- wake_up(&wo->mcu.wait);
- }
-
-+static void
-+mtk_wed_update_rx_stats(struct mtk_wed_device *wed, struct sk_buff *skb)
-+{
-+ u32 count = get_unaligned_le32(skb->data);
-+ struct mtk_wed_wo_rx_stats *stats;
-+ int i;
-+
-+ if (count * sizeof(*stats) > skb->len - sizeof(u32))
-+ return;
-+
-+ stats = (struct mtk_wed_wo_rx_stats *)(skb->data + sizeof(u32));
-+ for (i = 0 ; i < count ; i++)
-+ wed->wlan.update_wo_rx_stats(wed, &stats[i]);
-+}
-+
- void mtk_wed_mcu_rx_unsolicited_event(struct mtk_wed_wo *wo,
- struct sk_buff *skb)
- {
- struct mtk_wed_mcu_hdr *hdr = (struct mtk_wed_mcu_hdr *)skb->data;
-
-- switch (hdr->cmd) {
-- case MTK_WED_WO_EVT_LOG_DUMP: {
-- const char *msg = (const char *)(skb->data + sizeof(*hdr));
-+ skb_pull(skb, sizeof(*hdr));
-
-- dev_notice(wo->hw->dev, "%s\n", msg);
-+ switch (hdr->cmd) {
-+ case MTK_WED_WO_EVT_LOG_DUMP:
-+ dev_notice(wo->hw->dev, "%s\n", skb->data);
- break;
-- }
- case MTK_WED_WO_EVT_PROFILING: {
-- struct mtk_wed_wo_log_info *info;
-- u32 count = (skb->len - sizeof(*hdr)) / sizeof(*info);
-+ struct mtk_wed_wo_log_info *info = (void *)skb->data;
-+ u32 count = skb->len / sizeof(*info);
- int i;
-
-- info = (struct mtk_wed_wo_log_info *)(skb->data + sizeof(*hdr));
- for (i = 0 ; i < count ; i++)
- dev_notice(wo->hw->dev,
- "SN:%u latency: total=%u, rro:%u, mod:%u\n",
-@@ -88,6 +102,7 @@ void mtk_wed_mcu_rx_unsolicited_event(st
- break;
- }
- case MTK_WED_WO_EVT_RXCNT_INFO:
-+ mtk_wed_update_rx_stats(wo->hw->wed_dev, skb);
- break;
- default:
- break;
-@@ -144,6 +159,8 @@ mtk_wed_mcu_parse_response(struct mtk_we
- skb_pull(skb, sizeof(*hdr));
- switch (cmd) {
- case MTK_WED_WO_CMD_RXCNT_INFO:
-+ mtk_wed_update_rx_stats(wo->hw->wed_dev, skb);
-+ break;
- default:
- break;
- }
-@@ -182,6 +199,18 @@ unlock:
- return ret;
- }
-
-+int mtk_wed_mcu_msg_update(struct mtk_wed_device *dev, int id, void *data,
-+ int len)
-+{
-+ struct mtk_wed_wo *wo = dev->hw->wed_wo;
-+
-+ if (dev->hw->version == 1)
-+ return 0;
-+
-+ return mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO, id, data, len,
-+ true);
-+}
-+
- static int
- mtk_wed_get_memory_region(struct mtk_wed_wo *wo,
- struct mtk_wed_wo_memory_region *region)
---- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
-@@ -4,6 +4,7 @@
- #ifndef __MTK_WED_REGS_H
- #define __MTK_WED_REGS_H
-
-+#define MTK_WFDMA_DESC_CTRL_TO_HOST BIT(8)
- #define MTK_WDMA_DESC_CTRL_LEN1 GENMASK(14, 0)
- #define MTK_WDMA_DESC_CTRL_LEN1_V2 GENMASK(13, 0)
- #define MTK_WDMA_DESC_CTRL_LAST_SEG1 BIT(15)
-@@ -28,6 +29,8 @@ struct mtk_wdma_desc {
- #define MTK_WED_RESET_WED_TX_DMA BIT(12)
- #define MTK_WED_RESET_WDMA_RX_DRV BIT(17)
- #define MTK_WED_RESET_WDMA_INT_AGENT BIT(19)
-+#define MTK_WED_RESET_RX_RRO_QM BIT(20)
-+#define MTK_WED_RESET_RX_ROUTE_QM BIT(21)
- #define MTK_WED_RESET_WED BIT(31)
-
- #define MTK_WED_CTRL 0x00c
-@@ -39,8 +42,12 @@ struct mtk_wdma_desc {
- #define MTK_WED_CTRL_WED_TX_BM_BUSY BIT(9)
- #define MTK_WED_CTRL_WED_TX_FREE_AGENT_EN BIT(10)
- #define MTK_WED_CTRL_WED_TX_FREE_AGENT_BUSY BIT(11)
--#define MTK_WED_CTRL_RESERVE_EN BIT(12)
--#define MTK_WED_CTRL_RESERVE_BUSY BIT(13)
-+#define MTK_WED_CTRL_WED_RX_BM_EN BIT(12)
-+#define MTK_WED_CTRL_WED_RX_BM_BUSY BIT(13)
-+#define MTK_WED_CTRL_RX_RRO_QM_EN BIT(14)
-+#define MTK_WED_CTRL_RX_RRO_QM_BUSY BIT(15)
-+#define MTK_WED_CTRL_RX_ROUTE_QM_EN BIT(16)
-+#define MTK_WED_CTRL_RX_ROUTE_QM_BUSY BIT(17)
- #define MTK_WED_CTRL_FINAL_DIDX_READ BIT(24)
- #define MTK_WED_CTRL_ETH_DMAD_FMT BIT(25)
- #define MTK_WED_CTRL_MIB_READ_CLEAR BIT(28)
-@@ -62,6 +69,9 @@ struct mtk_wdma_desc {
- #define MTK_WED_EXT_INT_STATUS_TX_DMA_R_RESP_ERR BIT(22)
- #define MTK_WED_EXT_INT_STATUS_TX_DMA_W_RESP_ERR BIT(23)
- #define MTK_WED_EXT_INT_STATUS_RX_DRV_DMA_RECYCLE BIT(24)
-+#define MTK_WED_EXT_INT_STATUS_RX_DRV_GET_BM_DMAD_SKIP BIT(25)
-+#define MTK_WED_EXT_INT_STATUS_WPDMA_RX_D_DRV_ERR BIT(26)
-+#define MTK_WED_EXT_INT_STATUS_WPDMA_MID_RDY BIT(27)
- #define MTK_WED_EXT_INT_STATUS_ERROR_MASK (MTK_WED_EXT_INT_STATUS_TF_LEN_ERR | \
- MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD | \
- MTK_WED_EXT_INT_STATUS_TKID_TITO_INVALID | \
-@@ -71,6 +81,8 @@ struct mtk_wdma_desc {
- MTK_WED_EXT_INT_STATUS_TX_DMA_R_RESP_ERR)
-
- #define MTK_WED_EXT_INT_MASK 0x028
-+#define MTK_WED_EXT_INT_MASK1 0x02c
-+#define MTK_WED_EXT_INT_MASK2 0x030
-
- #define MTK_WED_STATUS 0x060
- #define MTK_WED_STATUS_TX GENMASK(15, 8)
-@@ -151,6 +163,7 @@ struct mtk_wdma_desc {
- #define MTK_WED_RING_TX(_n) (0x300 + (_n) * 0x10)
-
- #define MTK_WED_RING_RX(_n) (0x400 + (_n) * 0x10)
-+#define MTK_WED_RING_RX_DATA(_n) (0x420 + (_n) * 0x10)
-
- #define MTK_WED_SCR0 0x3c0
- #define MTK_WED_WPDMA_INT_TRIGGER 0x504
-@@ -213,6 +226,12 @@ struct mtk_wdma_desc {
- #define MTK_WED_WPDMA_INT_CTRL_TX1_DONE_TRIG GENMASK(14, 10)
-
- #define MTK_WED_WPDMA_INT_CTRL_RX 0x534
-+#define MTK_WED_WPDMA_INT_CTRL_RX0_EN BIT(0)
-+#define MTK_WED_WPDMA_INT_CTRL_RX0_CLR BIT(1)
-+#define MTK_WED_WPDMA_INT_CTRL_RX0_DONE_TRIG GENMASK(6, 2)
-+#define MTK_WED_WPDMA_INT_CTRL_RX1_EN BIT(8)
-+#define MTK_WED_WPDMA_INT_CTRL_RX1_CLR BIT(9)
-+#define MTK_WED_WPDMA_INT_CTRL_RX1_DONE_TRIG GENMASK(14, 10)
-
- #define MTK_WED_WPDMA_INT_CTRL_TX_FREE 0x538
- #define MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_EN BIT(0)
-@@ -242,11 +261,34 @@ struct mtk_wdma_desc {
-
- #define MTK_WED_WPDMA_RING_TX(_n) (0x600 + (_n) * 0x10)
- #define MTK_WED_WPDMA_RING_RX(_n) (0x700 + (_n) * 0x10)
-+#define MTK_WED_WPDMA_RING_RX_DATA(_n) (0x730 + (_n) * 0x10)
-+
-+#define MTK_WED_WPDMA_RX_D_GLO_CFG 0x75c
-+#define MTK_WED_WPDMA_RX_D_RX_DRV_EN BIT(0)
-+#define MTK_WED_WPDMA_RX_D_INIT_PHASE_RXEN_SEL GENMASK(11, 7)
-+#define MTK_WED_WPDMA_RX_D_RXD_READ_LEN GENMASK(31, 24)
-+
-+#define MTK_WED_WPDMA_RX_D_RST_IDX 0x760
-+#define MTK_WED_WPDMA_RX_D_RST_CRX_IDX GENMASK(17, 16)
-+#define MTK_WED_WPDMA_RX_D_RST_DRV_IDX GENMASK(25, 24)
-+
-+#define MTK_WED_WPDMA_RX_GLO_CFG 0x76c
-+#define MTK_WED_WPDMA_RX_RING 0x770
-+
-+#define MTK_WED_WPDMA_RX_D_MIB(_n) (0x774 + (_n) * 4)
-+#define MTK_WED_WPDMA_RX_D_PROCESSED_MIB(_n) (0x784 + (_n) * 4)
-+#define MTK_WED_WPDMA_RX_D_COHERENT_MIB 0x78c
-+
-+#define MTK_WED_WDMA_RING_TX 0x800
-+
-+#define MTK_WED_WDMA_TX_MIB 0x810
-+
- #define MTK_WED_WDMA_RING_RX(_n) (0x900 + (_n) * 0x10)
- #define MTK_WED_WDMA_RX_THRES(_n) (0x940 + (_n) * 0x4)
-
- #define MTK_WED_WDMA_GLO_CFG 0xa04
- #define MTK_WED_WDMA_GLO_CFG_TX_DRV_EN BIT(0)
-+#define MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK BIT(1)
- #define MTK_WED_WDMA_GLO_CFG_RX_DRV_EN BIT(2)
- #define MTK_WED_WDMA_GLO_CFG_RX_DRV_BUSY BIT(3)
- #define MTK_WED_WDMA_GLO_CFG_BT_SIZE GENMASK(5, 4)
-@@ -291,6 +333,20 @@ struct mtk_wdma_desc {
- #define MTK_WED_WDMA_RX_RECYCLE_MIB(_n) (0xae8 + (_n) * 4)
- #define MTK_WED_WDMA_RX_PROCESSED_MIB(_n) (0xaf0 + (_n) * 4)
-
-+#define MTK_WED_RX_BM_RX_DMAD 0xd80
-+#define MTK_WED_RX_BM_RX_DMAD_SDL0 GENMASK(13, 0)
-+
-+#define MTK_WED_RX_BM_BASE 0xd84
-+#define MTK_WED_RX_BM_INIT_PTR 0xd88
-+#define MTK_WED_RX_BM_SW_TAIL GENMASK(15, 0)
-+#define MTK_WED_RX_BM_INIT_SW_TAIL BIT(16)
-+
-+#define MTK_WED_RX_PTR 0xd8c
-+
-+#define MTK_WED_RX_BM_DYN_ALLOC_TH 0xdb4
-+#define MTK_WED_RX_BM_DYN_ALLOC_TH_H GENMASK(31, 16)
-+#define MTK_WED_RX_BM_DYN_ALLOC_TH_L GENMASK(15, 0)
-+
- #define MTK_WED_RING_OFS_BASE 0x00
- #define MTK_WED_RING_OFS_COUNT 0x04
- #define MTK_WED_RING_OFS_CPU_IDX 0x08
-@@ -301,7 +357,9 @@ struct mtk_wdma_desc {
-
- #define MTK_WDMA_GLO_CFG 0x204
- #define MTK_WDMA_GLO_CFG_TX_DMA_EN BIT(0)
-+#define MTK_WDMA_GLO_CFG_TX_DMA_BUSY BIT(1)
- #define MTK_WDMA_GLO_CFG_RX_DMA_EN BIT(2)
-+#define MTK_WDMA_GLO_CFG_RX_DMA_BUSY BIT(3)
- #define MTK_WDMA_GLO_CFG_RX_INFO3_PRERES BIT(26)
- #define MTK_WDMA_GLO_CFG_RX_INFO2_PRERES BIT(27)
- #define MTK_WDMA_GLO_CFG_RX_INFO1_PRERES BIT(28)
-@@ -330,4 +388,70 @@ struct mtk_wdma_desc {
- /* DMA channel mapping */
- #define HIFSYS_DMA_AG_MAP 0x008
-
-+#define MTK_WED_RTQM_GLO_CFG 0xb00
-+#define MTK_WED_RTQM_BUSY BIT(1)
-+#define MTK_WED_RTQM_Q_RST BIT(2)
-+#define MTK_WED_RTQM_Q_DBG_BYPASS BIT(5)
-+#define MTK_WED_RTQM_TXDMAD_FPORT GENMASK(23, 20)
-+
-+#define MTK_WED_RTQM_R2H_MIB(_n) (0xb70 + (_n) * 0x4)
-+#define MTK_WED_RTQM_R2Q_MIB(_n) (0xb78 + (_n) * 0x4)
-+#define MTK_WED_RTQM_Q2N_MIB 0xb80
-+#define MTK_WED_RTQM_Q2H_MIB(_n) (0xb84 + (_n) * 0x4)
-+
-+#define MTK_WED_RTQM_Q2B_MIB 0xb8c
-+#define MTK_WED_RTQM_PFDBK_MIB 0xb90
-+
-+#define MTK_WED_RROQM_GLO_CFG 0xc04
-+#define MTK_WED_RROQM_RST_IDX 0xc08
-+#define MTK_WED_RROQM_RST_IDX_MIOD BIT(0)
-+#define MTK_WED_RROQM_RST_IDX_FDBK BIT(4)
-+
-+#define MTK_WED_RROQM_MIOD_CTRL0 0xc40
-+#define MTK_WED_RROQM_MIOD_CTRL1 0xc44
-+#define MTK_WED_RROQM_MIOD_CNT GENMASK(11, 0)
-+
-+#define MTK_WED_RROQM_MIOD_CTRL2 0xc48
-+#define MTK_WED_RROQM_MIOD_CTRL3 0xc4c
-+
-+#define MTK_WED_RROQM_FDBK_CTRL0 0xc50
-+#define MTK_WED_RROQM_FDBK_CTRL1 0xc54
-+#define MTK_WED_RROQM_FDBK_CNT GENMASK(11, 0)
-+
-+#define MTK_WED_RROQM_FDBK_CTRL2 0xc58
-+
-+#define MTK_WED_RROQ_BASE_L 0xc80
-+#define MTK_WED_RROQ_BASE_H 0xc84
-+
-+#define MTK_WED_RROQM_MIOD_CFG 0xc8c
-+#define MTK_WED_RROQM_MIOD_MID_DW GENMASK(5, 0)
-+#define MTK_WED_RROQM_MIOD_MOD_DW GENMASK(13, 8)
-+#define MTK_WED_RROQM_MIOD_ENTRY_DW GENMASK(22, 16)
-+
-+#define MTK_WED_RROQM_MID_MIB 0xcc0
-+#define MTK_WED_RROQM_MOD_MIB 0xcc4
-+#define MTK_WED_RROQM_MOD_COHERENT_MIB 0xcc8
-+#define MTK_WED_RROQM_FDBK_MIB 0xcd0
-+#define MTK_WED_RROQM_FDBK_COHERENT_MIB 0xcd4
-+#define MTK_WED_RROQM_FDBK_IND_MIB 0xce0
-+#define MTK_WED_RROQM_FDBK_ENQ_MIB 0xce4
-+#define MTK_WED_RROQM_FDBK_ANC_MIB 0xce8
-+#define MTK_WED_RROQM_FDBK_ANC2H_MIB 0xcec
-+
-+#define MTK_WED_RX_BM_RX_DMAD 0xd80
-+#define MTK_WED_RX_BM_BASE 0xd84
-+#define MTK_WED_RX_BM_INIT_PTR 0xd88
-+#define MTK_WED_RX_BM_PTR 0xd8c
-+#define MTK_WED_RX_BM_PTR_HEAD GENMASK(32, 16)
-+#define MTK_WED_RX_BM_PTR_TAIL GENMASK(15, 0)
-+
-+#define MTK_WED_RX_BM_BLEN 0xd90
-+#define MTK_WED_RX_BM_STS 0xd94
-+#define MTK_WED_RX_BM_INTF2 0xd98
-+#define MTK_WED_RX_BM_INTF 0xd9c
-+#define MTK_WED_RX_BM_ERR_STS 0xda8
-+
-+#define MTK_WED_WOCPU_VIEW_MIOD_BASE 0x8000
-+#define MTK_WED_PCIE_INT_MASK 0x0
-+
- #endif
---- a/drivers/net/ethernet/mediatek/mtk_wed_wo.h
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h
-@@ -49,6 +49,10 @@ enum {
- MTK_WED_WARP_CMD_FLAG_FROM_TO_WO = BIT(2),
- };
-
-+#define MTK_WED_WO_CPU_MCUSYS_RESET_ADDR 0x15194050
-+#define MTK_WED_WO_CPU_WO0_MCUSYS_RESET_MASK 0x20
-+#define MTK_WED_WO_CPU_WO1_MCUSYS_RESET_MASK 0x1
-+
- enum {
- MTK_WED_WO_REGION_EMI,
- MTK_WED_WO_REGION_ILM,
-@@ -57,6 +61,28 @@ enum {
- __MTK_WED_WO_REGION_MAX,
- };
-
-+enum mtk_wed_wo_state {
-+ MTK_WED_WO_STATE_UNDEFINED,
-+ MTK_WED_WO_STATE_INIT,
-+ MTK_WED_WO_STATE_ENABLE,
-+ MTK_WED_WO_STATE_DISABLE,
-+ MTK_WED_WO_STATE_HALT,
-+ MTK_WED_WO_STATE_GATING,
-+ MTK_WED_WO_STATE_SER_RESET,
-+ MTK_WED_WO_STATE_WF_RESET,
-+};
-+
-+enum mtk_wed_wo_done_state {
-+ MTK_WED_WOIF_UNDEFINED,
-+ MTK_WED_WOIF_DISABLE_DONE,
-+ MTK_WED_WOIF_TRIGGER_ENABLE,
-+ MTK_WED_WOIF_ENABLE_DONE,
-+ MTK_WED_WOIF_TRIGGER_GATING,
-+ MTK_WED_WOIF_GATING_DONE,
-+ MTK_WED_WOIF_TRIGGER_HALT,
-+ MTK_WED_WOIF_HALT_DONE,
-+};
-+
- enum mtk_wed_dummy_cr_idx {
- MTK_WED_DUMMY_CR_FWDL,
- MTK_WED_DUMMY_CR_WO_STATUS,
-@@ -245,6 +271,8 @@ void mtk_wed_mcu_rx_unsolicited_event(st
- struct sk_buff *skb);
- int mtk_wed_mcu_send_msg(struct mtk_wed_wo *wo, int id, int cmd,
- const void *data, int len, bool wait_resp);
-+int mtk_wed_mcu_msg_update(struct mtk_wed_device *dev, int id, void *data,
-+ int len);
- int mtk_wed_mcu_init(struct mtk_wed_wo *wo);
- int mtk_wed_wo_init(struct mtk_wed_hw *hw);
- void mtk_wed_wo_deinit(struct mtk_wed_hw *hw);
---- a/include/linux/soc/mediatek/mtk_wed.h
-+++ b/include/linux/soc/mediatek/mtk_wed.h
-@@ -5,10 +5,13 @@
- #include <linux/rcupdate.h>
- #include <linux/regmap.h>
- #include <linux/pci.h>
-+#include <linux/skbuff.h>
-
- #define MTK_WED_TX_QUEUES 2
- #define MTK_WED_RX_QUEUES 2
-
-+#define WED_WO_STA_REC 0x6
-+
- struct mtk_wed_hw;
- struct mtk_wdma_desc;
-
-@@ -41,21 +44,37 @@ enum mtk_wed_wo_cmd {
- MTK_WED_WO_CMD_WED_END
- };
-
-+struct mtk_rxbm_desc {
-+ __le32 buf0;
-+ __le32 token;
-+} __packed __aligned(4);
-+
- enum mtk_wed_bus_tye {
- MTK_WED_BUS_PCIE,
- MTK_WED_BUS_AXI,
- };
-
-+#define MTK_WED_RING_CONFIGURED BIT(0)
- struct mtk_wed_ring {
- struct mtk_wdma_desc *desc;
- dma_addr_t desc_phys;
- u32 desc_size;
- int size;
-+ u32 flags;
-
- u32 reg_base;
- void __iomem *wpdma;
- };
-
-+struct mtk_wed_wo_rx_stats {
-+ __le16 wlan_idx;
-+ __le16 tid;
-+ __le32 rx_pkt_cnt;
-+ __le32 rx_byte_cnt;
-+ __le32 rx_err_cnt;
-+ __le32 rx_drop_cnt;
-+};
-+
- struct mtk_wed_device {
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
- const struct mtk_wed_ops *ops;
-@@ -64,9 +83,12 @@ struct mtk_wed_device {
- bool init_done, running;
- int wdma_idx;
- int irq;
-+ u8 version;
-
- struct mtk_wed_ring tx_ring[MTK_WED_TX_QUEUES];
-+ struct mtk_wed_ring rx_ring[MTK_WED_RX_QUEUES];
- struct mtk_wed_ring txfree_ring;
-+ struct mtk_wed_ring tx_wdma[MTK_WED_TX_QUEUES];
- struct mtk_wed_ring rx_wdma[MTK_WED_RX_QUEUES];
-
- struct {
-@@ -74,7 +96,20 @@ struct mtk_wed_device {
- void **pages;
- struct mtk_wdma_desc *desc;
- dma_addr_t desc_phys;
-- } buf_ring;
-+ } tx_buf_ring;
-+
-+ struct {
-+ int size;
-+ struct page_frag_cache rx_page;
-+ struct mtk_rxbm_desc *desc;
-+ dma_addr_t desc_phys;
-+ } rx_buf_ring;
-+
-+ struct {
-+ struct mtk_wed_ring ring;
-+ dma_addr_t miod_phys;
-+ dma_addr_t fdbk_phys;
-+ } rro;
-
- /* filled by driver: */
- struct {
-@@ -83,22 +118,36 @@ struct mtk_wed_device {
- struct pci_dev *pci_dev;
- };
- enum mtk_wed_bus_tye bus_type;
-+ void __iomem *base;
-+ u32 phy_base;
-
- u32 wpdma_phys;
- u32 wpdma_int;
- u32 wpdma_mask;
- u32 wpdma_tx;
- u32 wpdma_txfree;
-+ u32 wpdma_rx_glo;
-+ u32 wpdma_rx;
-+
-+ bool wcid_512;
-
- u16 token_start;
- unsigned int nbuf;
-+ unsigned int rx_nbuf;
-+ unsigned int rx_npkt;
-+ unsigned int rx_size;
-
- u8 tx_tbit[MTK_WED_TX_QUEUES];
-+ u8 rx_tbit[MTK_WED_RX_QUEUES];
- u8 txfree_tbit;
-
- u32 (*init_buf)(void *ptr, dma_addr_t phys, int token_id);
- int (*offload_enable)(struct mtk_wed_device *wed);
- void (*offload_disable)(struct mtk_wed_device *wed);
-+ u32 (*init_rx_buf)(struct mtk_wed_device *wed, int size);
-+ void (*release_rx_buf)(struct mtk_wed_device *wed);
-+ void (*update_wo_rx_stats)(struct mtk_wed_device *wed,
-+ struct mtk_wed_wo_rx_stats *stats);
- } wlan;
- #endif
- };
-@@ -107,9 +156,15 @@ struct mtk_wed_ops {
- int (*attach)(struct mtk_wed_device *dev);
- int (*tx_ring_setup)(struct mtk_wed_device *dev, int ring,
- void __iomem *regs);
-+ int (*rx_ring_setup)(struct mtk_wed_device *dev, int ring,
-+ void __iomem *regs);
- int (*txfree_ring_setup)(struct mtk_wed_device *dev,
- void __iomem *regs);
-+ int (*msg_update)(struct mtk_wed_device *dev, int cmd_id,
-+ void *data, int len);
- void (*detach)(struct mtk_wed_device *dev);
-+ void (*ppe_check)(struct mtk_wed_device *dev, struct sk_buff *skb,
-+ u32 reason, u32 hash);
-
- void (*stop)(struct mtk_wed_device *dev);
- void (*start)(struct mtk_wed_device *dev, u32 irq_mask);
-@@ -144,6 +199,16 @@ mtk_wed_device_attach(struct mtk_wed_dev
- return ret;
- }
-
-+static inline bool
-+mtk_wed_get_rx_capa(struct mtk_wed_device *dev)
-+{
-+#ifdef CONFIG_NET_MEDIATEK_SOC_WED
-+ return dev->version != 1;
-+#else
-+ return false;
-+#endif
-+}
-+
- #ifdef CONFIG_NET_MEDIATEK_SOC_WED
- #define mtk_wed_device_active(_dev) !!(_dev)->ops
- #define mtk_wed_device_detach(_dev) (_dev)->ops->detach(_dev)
-@@ -160,6 +225,12 @@ mtk_wed_device_attach(struct mtk_wed_dev
- (_dev)->ops->irq_get(_dev, _mask)
- #define mtk_wed_device_irq_set_mask(_dev, _mask) \
- (_dev)->ops->irq_set_mask(_dev, _mask)
-+#define mtk_wed_device_rx_ring_setup(_dev, _ring, _regs) \
-+ (_dev)->ops->rx_ring_setup(_dev, _ring, _regs)
-+#define mtk_wed_device_ppe_check(_dev, _skb, _reason, _hash) \
-+ (_dev)->ops->ppe_check(_dev, _skb, _reason, _hash)
-+#define mtk_wed_device_update_msg(_dev, _id, _msg, _len) \
-+ (_dev)->ops->msg_update(_dev, _id, _msg, _len)
- #else
- static inline bool mtk_wed_device_active(struct mtk_wed_device *dev)
- {
-@@ -173,6 +244,9 @@ static inline bool mtk_wed_device_active
- #define mtk_wed_device_reg_write(_dev, _reg, _val) do {} while (0)
- #define mtk_wed_device_irq_get(_dev, _mask) 0
- #define mtk_wed_device_irq_set_mask(_dev, _mask) do {} while (0)
-+#define mtk_wed_device_rx_ring_setup(_dev, _ring, _regs) -ENODEV
-+#define mtk_wed_device_ppe_check(_dev, _skb, _reason, _hash) do {} while (0)
-+#define mtk_wed_device_update_msg(_dev, _id, _msg, _len) -ENODEV
- #endif
-
- #endif
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Sat, 5 Nov 2022 23:36:22 +0100
-Subject: [PATCH] net: ethernet: mtk_wed: add rx mib counters
-
-Introduce WED RX MIB counters support available on MT7986a SoC.
-
-Tested-by: Daniel Golle <daniel@makrotopia.org>
-Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
-@@ -2,6 +2,7 @@
- /* Copyright (C) 2021 Felix Fietkau <nbd@nbd.name> */
-
- #include <linux/seq_file.h>
-+#include <linux/soc/mediatek/mtk_wed.h>
- #include "mtk_wed.h"
- #include "mtk_wed_regs.h"
-
-@@ -18,6 +19,8 @@ enum {
- DUMP_TYPE_WDMA,
- DUMP_TYPE_WPDMA_TX,
- DUMP_TYPE_WPDMA_TXFREE,
-+ DUMP_TYPE_WPDMA_RX,
-+ DUMP_TYPE_WED_RRO,
- };
-
- #define DUMP_STR(_str) { _str, 0, DUMP_TYPE_STRING }
-@@ -36,6 +39,9 @@ enum {
-
- #define DUMP_WPDMA_TX_RING(_n) DUMP_RING("WPDMA_TX" #_n, 0, DUMP_TYPE_WPDMA_TX, _n)
- #define DUMP_WPDMA_TXFREE_RING DUMP_RING("WPDMA_RX1", 0, DUMP_TYPE_WPDMA_TXFREE)
-+#define DUMP_WPDMA_RX_RING(_n) DUMP_RING("WPDMA_RX" #_n, 0, DUMP_TYPE_WPDMA_RX, _n)
-+#define DUMP_WED_RRO_RING(_base)DUMP_RING("WED_RRO_MIOD", MTK_##_base, DUMP_TYPE_WED_RRO)
-+#define DUMP_WED_RRO_FDBK(_base)DUMP_RING("WED_RRO_FDBK", MTK_##_base, DUMP_TYPE_WED_RRO)
-
- static void
- print_reg_val(struct seq_file *s, const char *name, u32 val)
-@@ -57,6 +63,7 @@ dump_wed_regs(struct seq_file *s, struct
- cur > regs ? "\n" : "",
- cur->name);
- continue;
-+ case DUMP_TYPE_WED_RRO:
- case DUMP_TYPE_WED:
- val = wed_r32(dev, cur->offset);
- break;
-@@ -69,6 +76,9 @@ dump_wed_regs(struct seq_file *s, struct
- case DUMP_TYPE_WPDMA_TXFREE:
- val = wpdma_txfree_r32(dev, cur->offset);
- break;
-+ case DUMP_TYPE_WPDMA_RX:
-+ val = wpdma_rx_r32(dev, cur->base, cur->offset);
-+ break;
- }
- print_reg_val(s, cur->name, val);
- }
-@@ -132,6 +142,80 @@ wed_txinfo_show(struct seq_file *s, void
- }
- DEFINE_SHOW_ATTRIBUTE(wed_txinfo);
-
-+static int
-+wed_rxinfo_show(struct seq_file *s, void *data)
-+{
-+ static const struct reg_dump regs[] = {
-+ DUMP_STR("WPDMA RX"),
-+ DUMP_WPDMA_RX_RING(0),
-+ DUMP_WPDMA_RX_RING(1),
-+
-+ DUMP_STR("WPDMA RX"),
-+ DUMP_WED(WED_WPDMA_RX_D_MIB(0)),
-+ DUMP_WED_RING(WED_WPDMA_RING_RX_DATA(0)),
-+ DUMP_WED(WED_WPDMA_RX_D_PROCESSED_MIB(0)),
-+ DUMP_WED(WED_WPDMA_RX_D_MIB(1)),
-+ DUMP_WED_RING(WED_WPDMA_RING_RX_DATA(1)),
-+ DUMP_WED(WED_WPDMA_RX_D_PROCESSED_MIB(1)),
-+ DUMP_WED(WED_WPDMA_RX_D_COHERENT_MIB),
-+
-+ DUMP_STR("WED RX"),
-+ DUMP_WED_RING(WED_RING_RX_DATA(0)),
-+ DUMP_WED_RING(WED_RING_RX_DATA(1)),
-+
-+ DUMP_STR("WED RRO"),
-+ DUMP_WED_RRO_RING(WED_RROQM_MIOD_CTRL0),
-+ DUMP_WED(WED_RROQM_MID_MIB),
-+ DUMP_WED(WED_RROQM_MOD_MIB),
-+ DUMP_WED(WED_RROQM_MOD_COHERENT_MIB),
-+ DUMP_WED_RRO_FDBK(WED_RROQM_FDBK_CTRL0),
-+ DUMP_WED(WED_RROQM_FDBK_IND_MIB),
-+ DUMP_WED(WED_RROQM_FDBK_ENQ_MIB),
-+ DUMP_WED(WED_RROQM_FDBK_ANC_MIB),
-+ DUMP_WED(WED_RROQM_FDBK_ANC2H_MIB),
-+
-+ DUMP_STR("WED Route QM"),
-+ DUMP_WED(WED_RTQM_R2H_MIB(0)),
-+ DUMP_WED(WED_RTQM_R2Q_MIB(0)),
-+ DUMP_WED(WED_RTQM_Q2H_MIB(0)),
-+ DUMP_WED(WED_RTQM_R2H_MIB(1)),
-+ DUMP_WED(WED_RTQM_R2Q_MIB(1)),
-+ DUMP_WED(WED_RTQM_Q2H_MIB(1)),
-+ DUMP_WED(WED_RTQM_Q2N_MIB),
-+ DUMP_WED(WED_RTQM_Q2B_MIB),
-+ DUMP_WED(WED_RTQM_PFDBK_MIB),
-+
-+ DUMP_STR("WED WDMA TX"),
-+ DUMP_WED(WED_WDMA_TX_MIB),
-+ DUMP_WED_RING(WED_WDMA_RING_TX),
-+
-+ DUMP_STR("WDMA TX"),
-+ DUMP_WDMA(WDMA_GLO_CFG),
-+ DUMP_WDMA_RING(WDMA_RING_TX(0)),
-+ DUMP_WDMA_RING(WDMA_RING_TX(1)),
-+
-+ DUMP_STR("WED RX BM"),
-+ DUMP_WED(WED_RX_BM_BASE),
-+ DUMP_WED(WED_RX_BM_RX_DMAD),
-+ DUMP_WED(WED_RX_BM_PTR),
-+ DUMP_WED(WED_RX_BM_TKID_MIB),
-+ DUMP_WED(WED_RX_BM_BLEN),
-+ DUMP_WED(WED_RX_BM_STS),
-+ DUMP_WED(WED_RX_BM_INTF2),
-+ DUMP_WED(WED_RX_BM_INTF),
-+ DUMP_WED(WED_RX_BM_ERR_STS),
-+ };
-+ struct mtk_wed_hw *hw = s->private;
-+ struct mtk_wed_device *dev = hw->wed_dev;
-+
-+ if (!dev)
-+ return 0;
-+
-+ dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs));
-+
-+ return 0;
-+}
-+DEFINE_SHOW_ATTRIBUTE(wed_rxinfo);
-
- static int
- mtk_wed_reg_set(void *data, u64 val)
-@@ -175,4 +259,7 @@ void mtk_wed_hw_add_debugfs(struct mtk_w
- debugfs_create_u32("regidx", 0600, dir, &hw->debugfs_reg);
- debugfs_create_file_unsafe("regval", 0600, dir, hw, &fops_regval);
- debugfs_create_file_unsafe("txinfo", 0400, dir, hw, &wed_txinfo_fops);
-+ if (hw->version != 1)
-+ debugfs_create_file_unsafe("rxinfo", 0400, dir, hw,
-+ &wed_rxinfo_fops);
- }
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Thu, 17 Nov 2022 00:58:46 +0100
-Subject: [PATCH] net: ethernet: mtk_eth_soc: remove cpu_relax in
- mtk_pending_work
-
-Get rid of cpu_relax in mtk_pending_work routine since MTK_RESETTING is
-set only in mtk_pending_work() and it runs holding rtnl lock
-
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -3480,11 +3480,8 @@ static void mtk_pending_work(struct work
- rtnl_lock();
-
- dev_dbg(eth->dev, "[%s][%d] reset\n", __func__, __LINE__);
-+ set_bit(MTK_RESETTING, ð->state);
-
-- while (test_and_set_bit_lock(MTK_RESETTING, ð->state))
-- cpu_relax();
--
-- dev_dbg(eth->dev, "[%s][%d] mtk_stop starts\n", __func__, __LINE__);
- /* stop all devices to make sure that dma is properly shut down */
- for (i = 0; i < MTK_MAC_COUNT; i++) {
- if (!eth->netdev[i])
-@@ -3518,7 +3515,7 @@ static void mtk_pending_work(struct work
-
- dev_dbg(eth->dev, "[%s][%d] reset done\n", __func__, __LINE__);
-
-- clear_bit_unlock(MTK_RESETTING, ð->state);
-+ clear_bit(MTK_RESETTING, ð->state);
-
- rtnl_unlock();
- }
+++ /dev/null
-From: Sujuan Chen <sujuan.chen@mediatek.com>
-Date: Thu, 24 Nov 2022 11:18:14 +0800
-Subject: [PATCH] net: ethernet: mtk_wed: add wcid overwritten support for wed
- v1
-
-All wed versions should enable the wcid overwritten feature,
-since the wcid size is controlled by the wlan driver.
-
-Tested-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Co-developed-by: Bo Jiao <bo.jiao@mediatek.com>
-Signed-off-by: Bo Jiao <bo.jiao@mediatek.com>
-Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -526,9 +526,9 @@ mtk_wed_dma_disable(struct mtk_wed_devic
- MTK_WED_WPDMA_RX_D_RX_DRV_EN);
- wed_clr(dev, MTK_WED_WDMA_GLO_CFG,
- MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK);
--
-- mtk_wed_set_512_support(dev, false);
- }
-+
-+ mtk_wed_set_512_support(dev, false);
- }
-
- static void
-@@ -1290,9 +1290,10 @@ mtk_wed_start(struct mtk_wed_device *dev
- if (mtk_wed_rro_cfg(dev))
- return;
-
-- mtk_wed_set_512_support(dev, dev->wlan.wcid_512);
- }
-
-+ mtk_wed_set_512_support(dev, dev->wlan.wcid_512);
-+
- mtk_wed_dma_enable(dev);
- dev->running = true;
- }
-@@ -1358,11 +1359,13 @@ mtk_wed_attach(struct mtk_wed_device *de
- }
-
- mtk_wed_hw_init_early(dev);
-- if (hw->version == 1)
-+ if (hw->version == 1) {
- regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP,
- BIT(hw->index), 0);
-- else
-+ } else {
-+ dev->rev_id = wed_r32(dev, MTK_WED_REV_ID);
- ret = mtk_wed_wo_init(hw);
-+ }
- out:
- if (ret)
- mtk_wed_detach(dev);
---- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
-@@ -20,6 +20,8 @@ struct mtk_wdma_desc {
- __le32 info;
- } __packed __aligned(4);
-
-+#define MTK_WED_REV_ID 0x004
-+
- #define MTK_WED_RESET 0x008
- #define MTK_WED_RESET_TX_BM BIT(0)
- #define MTK_WED_RESET_TX_FREE_AGENT BIT(4)
---- a/include/linux/soc/mediatek/mtk_wed.h
-+++ b/include/linux/soc/mediatek/mtk_wed.h
-@@ -85,6 +85,9 @@ struct mtk_wed_device {
- int irq;
- u8 version;
-
-+ /* used by wlan driver */
-+ u32 rev_id;
-+
- struct mtk_wed_ring tx_ring[MTK_WED_TX_QUEUES];
- struct mtk_wed_ring rx_ring[MTK_WED_RX_QUEUES];
- struct mtk_wed_ring txfree_ring;
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Thu, 24 Nov 2022 16:22:51 +0100
-Subject: [PATCH] net: ethernet: mtk_wed: return status value in
- mtk_wdma_rx_reset
-
-Move MTK_WDMA_RESET_IDX configuration in mtk_wdma_rx_reset routine.
-Increase poll timeout to 10ms in order to be aligned with vendor sdk.
-This is a preliminary patch to add Wireless Ethernet Dispatcher reset
-support.
-
-Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -101,17 +101,21 @@ mtk_wdma_read_reset(struct mtk_wed_devic
- return wdma_r32(dev, MTK_WDMA_GLO_CFG);
- }
-
--static void
-+static int
- mtk_wdma_rx_reset(struct mtk_wed_device *dev)
- {
- u32 status, mask = MTK_WDMA_GLO_CFG_RX_DMA_BUSY;
-- int i;
-+ int i, ret;
-
- wdma_clr(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_RX_DMA_EN);
-- if (readx_poll_timeout(mtk_wdma_read_reset, dev, status,
-- !(status & mask), 0, 1000))
-+ ret = readx_poll_timeout(mtk_wdma_read_reset, dev, status,
-+ !(status & mask), 0, 10000);
-+ if (ret)
- dev_err(dev->hw->dev, "rx reset failed\n");
-
-+ wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_RX);
-+ wdma_w32(dev, MTK_WDMA_RESET_IDX, 0);
-+
- for (i = 0; i < ARRAY_SIZE(dev->rx_wdma); i++) {
- if (dev->rx_wdma[i].desc)
- continue;
-@@ -119,6 +123,8 @@ mtk_wdma_rx_reset(struct mtk_wed_device
- wdma_w32(dev,
- MTK_WDMA_RING_RX(i) + MTK_WED_RING_OFS_CPU_IDX, 0);
- }
-+
-+ return ret;
- }
-
- static void
-@@ -565,9 +571,7 @@ mtk_wed_detach(struct mtk_wed_device *de
-
- mtk_wed_stop(dev);
-
-- wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_RX);
-- wdma_w32(dev, MTK_WDMA_RESET_IDX, 0);
--
-+ mtk_wdma_rx_reset(dev);
- mtk_wed_reset(dev, MTK_WED_RESET_WED);
- if (mtk_wed_get_rx_capa(dev)) {
- wdma_clr(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_TX_DMA_EN);
-@@ -582,7 +586,6 @@ mtk_wed_detach(struct mtk_wed_device *de
- mtk_wed_wo_reset(dev);
- mtk_wed_free_rx_rings(dev);
- mtk_wed_wo_deinit(hw);
-- mtk_wdma_rx_reset(dev);
- }
-
- if (dev->wlan.bus_type == MTK_WED_BUS_PCIE) {
-@@ -999,11 +1002,7 @@ mtk_wed_reset_dma(struct mtk_wed_device
- wed_w32(dev, MTK_WED_RESET_IDX, 0);
- }
-
-- wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_RX);
-- wdma_w32(dev, MTK_WDMA_RESET_IDX, 0);
--
-- if (mtk_wed_get_rx_capa(dev))
-- mtk_wdma_rx_reset(dev);
-+ mtk_wdma_rx_reset(dev);
-
- if (busy) {
- mtk_wed_reset(dev, MTK_WED_RESET_WDMA_INT_AGENT);
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Thu, 24 Nov 2022 16:22:52 +0100
-Subject: [PATCH] net: ethernet: mtk_wed: move MTK_WDMA_RESET_IDX_TX
- configuration in mtk_wdma_tx_reset
-
-Remove duplicated code. Increase poll timeout to 10ms in order to be
-aligned with vendor sdk.
-This is a preliminary patch to add Wireless Ethernet Dispatcher reset
-support.
-
-Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -135,16 +135,15 @@ mtk_wdma_tx_reset(struct mtk_wed_device
-
- wdma_clr(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_TX_DMA_EN);
- if (readx_poll_timeout(mtk_wdma_read_reset, dev, status,
-- !(status & mask), 0, 1000))
-+ !(status & mask), 0, 10000))
- dev_err(dev->hw->dev, "tx reset failed\n");
-
-- for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++) {
-- if (dev->tx_wdma[i].desc)
-- continue;
-+ wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_TX);
-+ wdma_w32(dev, MTK_WDMA_RESET_IDX, 0);
-
-+ for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++)
- wdma_w32(dev,
- MTK_WDMA_RING_TX(i) + MTK_WED_RING_OFS_CPU_IDX, 0);
-- }
- }
-
- static void
-@@ -573,12 +572,6 @@ mtk_wed_detach(struct mtk_wed_device *de
-
- mtk_wdma_rx_reset(dev);
- mtk_wed_reset(dev, MTK_WED_RESET_WED);
-- if (mtk_wed_get_rx_capa(dev)) {
-- wdma_clr(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_TX_DMA_EN);
-- wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_TX);
-- wdma_w32(dev, MTK_WDMA_RESET_IDX, 0);
-- }
--
- mtk_wed_free_tx_buffer(dev);
- mtk_wed_free_tx_rings(dev);
-
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Thu, 24 Nov 2022 16:22:53 +0100
-Subject: [PATCH] net: ethernet: mtk_wed: update mtk_wed_stop
-
-Update mtk_wed_stop routine and rename old mtk_wed_stop() to
-mtk_wed_deinit(). This is a preliminary patch to add Wireless Ethernet
-Dispatcher reset support.
-
-Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -539,14 +539,8 @@ mtk_wed_dma_disable(struct mtk_wed_devic
- static void
- mtk_wed_stop(struct mtk_wed_device *dev)
- {
-- mtk_wed_dma_disable(dev);
- mtk_wed_set_ext_int(dev, false);
-
-- wed_clr(dev, MTK_WED_CTRL,
-- MTK_WED_CTRL_WDMA_INT_AGENT_EN |
-- MTK_WED_CTRL_WPDMA_INT_AGENT_EN |
-- MTK_WED_CTRL_WED_TX_BM_EN |
-- MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
- wed_w32(dev, MTK_WED_WPDMA_INT_TRIGGER, 0);
- wed_w32(dev, MTK_WED_WDMA_INT_TRIGGER, 0);
- wdma_w32(dev, MTK_WDMA_INT_MASK, 0);
-@@ -558,7 +552,27 @@ mtk_wed_stop(struct mtk_wed_device *dev)
-
- wed_w32(dev, MTK_WED_EXT_INT_MASK1, 0);
- wed_w32(dev, MTK_WED_EXT_INT_MASK2, 0);
-- wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_BM_EN);
-+}
-+
-+static void
-+mtk_wed_deinit(struct mtk_wed_device *dev)
-+{
-+ mtk_wed_stop(dev);
-+ mtk_wed_dma_disable(dev);
-+
-+ wed_clr(dev, MTK_WED_CTRL,
-+ MTK_WED_CTRL_WDMA_INT_AGENT_EN |
-+ MTK_WED_CTRL_WPDMA_INT_AGENT_EN |
-+ MTK_WED_CTRL_WED_TX_BM_EN |
-+ MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
-+
-+ if (dev->hw->version == 1)
-+ return;
-+
-+ wed_clr(dev, MTK_WED_CTRL,
-+ MTK_WED_CTRL_RX_ROUTE_QM_EN |
-+ MTK_WED_CTRL_WED_RX_BM_EN |
-+ MTK_WED_CTRL_RX_RRO_QM_EN);
- }
-
- static void
-@@ -568,7 +582,7 @@ mtk_wed_detach(struct mtk_wed_device *de
-
- mutex_lock(&hw_lock);
-
-- mtk_wed_stop(dev);
-+ mtk_wed_deinit(dev);
-
- mtk_wdma_rx_reset(dev);
- mtk_wed_reset(dev, MTK_WED_RESET_WED);
-@@ -670,7 +684,7 @@ mtk_wed_hw_init_early(struct mtk_wed_dev
- {
- u32 mask, set;
-
-- mtk_wed_stop(dev);
-+ mtk_wed_deinit(dev);
- mtk_wed_reset(dev, MTK_WED_RESET_WED);
- mtk_wed_set_wpdma(dev);
-
---- a/include/linux/soc/mediatek/mtk_wed.h
-+++ b/include/linux/soc/mediatek/mtk_wed.h
-@@ -234,6 +234,8 @@ mtk_wed_get_rx_capa(struct mtk_wed_devic
- (_dev)->ops->ppe_check(_dev, _skb, _reason, _hash)
- #define mtk_wed_device_update_msg(_dev, _id, _msg, _len) \
- (_dev)->ops->msg_update(_dev, _id, _msg, _len)
-+#define mtk_wed_device_stop(_dev) (_dev)->ops->stop(_dev)
-+#define mtk_wed_device_dma_reset(_dev) (_dev)->ops->reset_dma(_dev)
- #else
- static inline bool mtk_wed_device_active(struct mtk_wed_device *dev)
- {
-@@ -250,6 +252,8 @@ static inline bool mtk_wed_device_active
- #define mtk_wed_device_rx_ring_setup(_dev, _ring, _regs) -ENODEV
- #define mtk_wed_device_ppe_check(_dev, _skb, _reason, _hash) do {} while (0)
- #define mtk_wed_device_update_msg(_dev, _id, _msg, _len) -ENODEV
-+#define mtk_wed_device_stop(_dev) do {} while (0)
-+#define mtk_wed_device_dma_reset(_dev) do {} while (0)
- #endif
-
- #endif
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Thu, 24 Nov 2022 16:22:54 +0100
-Subject: [PATCH] net: ethernet: mtk_wed: add mtk_wed_rx_reset routine
-
-Introduce mtk_wed_rx_reset routine in order to reset rx DMA for Wireless
-Ethernet Dispatcher available on MT7986 SoC.
-
-Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -944,42 +944,130 @@ mtk_wed_ring_reset(struct mtk_wed_ring *
- }
-
- static u32
--mtk_wed_check_busy(struct mtk_wed_device *dev)
-+mtk_wed_check_busy(struct mtk_wed_device *dev, u32 reg, u32 mask)
- {
-- if (wed_r32(dev, MTK_WED_GLO_CFG) & MTK_WED_GLO_CFG_TX_DMA_BUSY)
-- return true;
--
-- if (wed_r32(dev, MTK_WED_WPDMA_GLO_CFG) &
-- MTK_WED_WPDMA_GLO_CFG_TX_DRV_BUSY)
-- return true;
--
-- if (wed_r32(dev, MTK_WED_CTRL) & MTK_WED_CTRL_WDMA_INT_AGENT_BUSY)
-- return true;
--
-- if (wed_r32(dev, MTK_WED_WDMA_GLO_CFG) &
-- MTK_WED_WDMA_GLO_CFG_RX_DRV_BUSY)
-- return true;
--
-- if (wdma_r32(dev, MTK_WDMA_GLO_CFG) &
-- MTK_WED_WDMA_GLO_CFG_RX_DRV_BUSY)
-- return true;
--
-- if (wed_r32(dev, MTK_WED_CTRL) &
-- (MTK_WED_CTRL_WED_TX_BM_BUSY | MTK_WED_CTRL_WED_TX_FREE_AGENT_BUSY))
-- return true;
--
-- return false;
-+ return !!(wed_r32(dev, reg) & mask);
- }
-
- static int
--mtk_wed_poll_busy(struct mtk_wed_device *dev)
-+mtk_wed_poll_busy(struct mtk_wed_device *dev, u32 reg, u32 mask)
- {
- int sleep = 15000;
- int timeout = 100 * sleep;
- u32 val;
-
- return read_poll_timeout(mtk_wed_check_busy, val, !val, sleep,
-- timeout, false, dev);
-+ timeout, false, dev, reg, mask);
-+}
-+
-+static int
-+mtk_wed_rx_reset(struct mtk_wed_device *dev)
-+{
-+ struct mtk_wed_wo *wo = dev->hw->wed_wo;
-+ u8 val = MTK_WED_WO_STATE_SER_RESET;
-+ int i, ret;
-+
-+ ret = mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO,
-+ MTK_WED_WO_CMD_CHANGE_STATE, &val,
-+ sizeof(val), true);
-+ if (ret)
-+ return ret;
-+
-+ wed_clr(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, MTK_WED_WPDMA_RX_D_RX_DRV_EN);
-+ ret = mtk_wed_poll_busy(dev, MTK_WED_WPDMA_RX_D_GLO_CFG,
-+ MTK_WED_WPDMA_RX_D_RX_DRV_BUSY);
-+ if (ret) {
-+ mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_INT_AGENT);
-+ mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_RX_D_DRV);
-+ } else {
-+ wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX,
-+ MTK_WED_WPDMA_RX_D_RST_CRX_IDX |
-+ MTK_WED_WPDMA_RX_D_RST_DRV_IDX);
-+
-+ wed_set(dev, MTK_WED_WPDMA_RX_D_GLO_CFG,
-+ MTK_WED_WPDMA_RX_D_RST_INIT_COMPLETE |
-+ MTK_WED_WPDMA_RX_D_FSM_RETURN_IDLE);
-+ wed_clr(dev, MTK_WED_WPDMA_RX_D_GLO_CFG,
-+ MTK_WED_WPDMA_RX_D_RST_INIT_COMPLETE |
-+ MTK_WED_WPDMA_RX_D_FSM_RETURN_IDLE);
-+
-+ wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, 0);
-+ }
-+
-+ /* reset rro qm */
-+ wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_RX_RRO_QM_EN);
-+ ret = mtk_wed_poll_busy(dev, MTK_WED_CTRL,
-+ MTK_WED_CTRL_RX_RRO_QM_BUSY);
-+ if (ret) {
-+ mtk_wed_reset(dev, MTK_WED_RESET_RX_RRO_QM);
-+ } else {
-+ wed_set(dev, MTK_WED_RROQM_RST_IDX,
-+ MTK_WED_RROQM_RST_IDX_MIOD |
-+ MTK_WED_RROQM_RST_IDX_FDBK);
-+ wed_w32(dev, MTK_WED_RROQM_RST_IDX, 0);
-+ }
-+
-+ /* reset route qm */
-+ wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_RX_ROUTE_QM_EN);
-+ ret = mtk_wed_poll_busy(dev, MTK_WED_CTRL,
-+ MTK_WED_CTRL_RX_ROUTE_QM_BUSY);
-+ if (ret)
-+ mtk_wed_reset(dev, MTK_WED_RESET_RX_ROUTE_QM);
-+ else
-+ wed_set(dev, MTK_WED_RTQM_GLO_CFG,
-+ MTK_WED_RTQM_Q_RST);
-+
-+ /* reset tx wdma */
-+ mtk_wdma_tx_reset(dev);
-+
-+ /* reset tx wdma drv */
-+ wed_clr(dev, MTK_WED_WDMA_GLO_CFG, MTK_WED_WDMA_GLO_CFG_TX_DRV_EN);
-+ mtk_wed_poll_busy(dev, MTK_WED_CTRL,
-+ MTK_WED_CTRL_WDMA_INT_AGENT_BUSY);
-+ mtk_wed_reset(dev, MTK_WED_RESET_WDMA_TX_DRV);
-+
-+ /* reset wed rx dma */
-+ ret = mtk_wed_poll_busy(dev, MTK_WED_GLO_CFG,
-+ MTK_WED_GLO_CFG_RX_DMA_BUSY);
-+ wed_clr(dev, MTK_WED_GLO_CFG, MTK_WED_GLO_CFG_RX_DMA_EN);
-+ if (ret) {
-+ mtk_wed_reset(dev, MTK_WED_RESET_WED_RX_DMA);
-+ } else {
-+ struct mtk_eth *eth = dev->hw->eth;
-+
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-+ wed_set(dev, MTK_WED_RESET_IDX,
-+ MTK_WED_RESET_IDX_RX_V2);
-+ else
-+ wed_set(dev, MTK_WED_RESET_IDX, MTK_WED_RESET_IDX_RX);
-+ wed_w32(dev, MTK_WED_RESET_IDX, 0);
-+ }
-+
-+ /* reset rx bm */
-+ wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_BM_EN);
-+ mtk_wed_poll_busy(dev, MTK_WED_CTRL,
-+ MTK_WED_CTRL_WED_RX_BM_BUSY);
-+ mtk_wed_reset(dev, MTK_WED_RESET_RX_BM);
-+
-+ /* wo change to enable state */
-+ val = MTK_WED_WO_STATE_ENABLE;
-+ ret = mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO,
-+ MTK_WED_WO_CMD_CHANGE_STATE, &val,
-+ sizeof(val), true);
-+ if (ret)
-+ return ret;
-+
-+ /* wed_rx_ring_reset */
-+ for (i = 0; i < ARRAY_SIZE(dev->rx_ring); i++) {
-+ if (!dev->rx_ring[i].desc)
-+ continue;
-+
-+ mtk_wed_ring_reset(&dev->rx_ring[i], MTK_WED_RX_RING_SIZE,
-+ false);
-+ }
-+ mtk_wed_free_rx_buffer(dev);
-+
-+ return 0;
- }
-
- static void
-@@ -997,19 +1085,23 @@ mtk_wed_reset_dma(struct mtk_wed_device
- true);
- }
-
-- if (mtk_wed_poll_busy(dev))
-- busy = mtk_wed_check_busy(dev);
--
-+ /* 1. reset WED tx DMA */
-+ wed_clr(dev, MTK_WED_GLO_CFG, MTK_WED_GLO_CFG_TX_DMA_EN);
-+ busy = mtk_wed_poll_busy(dev, MTK_WED_GLO_CFG,
-+ MTK_WED_GLO_CFG_TX_DMA_BUSY);
- if (busy) {
- mtk_wed_reset(dev, MTK_WED_RESET_WED_TX_DMA);
- } else {
-- wed_w32(dev, MTK_WED_RESET_IDX,
-- MTK_WED_RESET_IDX_TX |
-- MTK_WED_RESET_IDX_RX);
-+ wed_w32(dev, MTK_WED_RESET_IDX, MTK_WED_RESET_IDX_TX);
- wed_w32(dev, MTK_WED_RESET_IDX, 0);
- }
-
-- mtk_wdma_rx_reset(dev);
-+ /* 2. reset WDMA rx DMA */
-+ busy = !!mtk_wdma_rx_reset(dev);
-+ wed_clr(dev, MTK_WED_WDMA_GLO_CFG, MTK_WED_WDMA_GLO_CFG_RX_DRV_EN);
-+ if (!busy)
-+ busy = mtk_wed_poll_busy(dev, MTK_WED_WDMA_GLO_CFG,
-+ MTK_WED_WDMA_GLO_CFG_RX_DRV_BUSY);
-
- if (busy) {
- mtk_wed_reset(dev, MTK_WED_RESET_WDMA_INT_AGENT);
-@@ -1026,6 +1118,9 @@ mtk_wed_reset_dma(struct mtk_wed_device
- MTK_WED_WDMA_GLO_CFG_RST_INIT_COMPLETE);
- }
-
-+ /* 3. reset WED WPDMA tx */
-+ wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
-+
- for (i = 0; i < 100; i++) {
- val = wed_r32(dev, MTK_WED_TX_BM_INTF);
- if (FIELD_GET(MTK_WED_TX_BM_INTF_TKFIFO_FDEP, val) == 0x40)
-@@ -1033,8 +1128,19 @@ mtk_wed_reset_dma(struct mtk_wed_device
- }
-
- mtk_wed_reset(dev, MTK_WED_RESET_TX_FREE_AGENT);
-+ wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_TX_BM_EN);
- mtk_wed_reset(dev, MTK_WED_RESET_TX_BM);
-
-+ /* 4. reset WED WPDMA tx */
-+ busy = mtk_wed_poll_busy(dev, MTK_WED_WPDMA_GLO_CFG,
-+ MTK_WED_WPDMA_GLO_CFG_TX_DRV_BUSY);
-+ wed_clr(dev, MTK_WED_WPDMA_GLO_CFG,
-+ MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN |
-+ MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN);
-+ if (!busy)
-+ busy = mtk_wed_poll_busy(dev, MTK_WED_WPDMA_GLO_CFG,
-+ MTK_WED_WPDMA_GLO_CFG_RX_DRV_BUSY);
-+
- if (busy) {
- mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_INT_AGENT);
- mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_TX_DRV);
-@@ -1045,6 +1151,17 @@ mtk_wed_reset_dma(struct mtk_wed_device
- MTK_WED_WPDMA_RESET_IDX_RX);
- wed_w32(dev, MTK_WED_WPDMA_RESET_IDX, 0);
- }
-+
-+ dev->init_done = false;
-+ if (dev->hw->version == 1)
-+ return;
-+
-+ if (!busy) {
-+ wed_w32(dev, MTK_WED_RESET_IDX, MTK_WED_RESET_WPDMA_IDX_RX);
-+ wed_w32(dev, MTK_WED_RESET_IDX, 0);
-+ }
-+
-+ mtk_wed_rx_reset(dev);
- }
-
- static int
-@@ -1267,6 +1384,9 @@ mtk_wed_start(struct mtk_wed_device *dev
- {
- int i;
-
-+ if (mtk_wed_get_rx_capa(dev) && mtk_wed_rx_buffer_alloc(dev))
-+ return;
-+
- for (i = 0; i < ARRAY_SIZE(dev->rx_wdma); i++)
- if (!dev->rx_wdma[i].desc)
- mtk_wed_wdma_rx_ring_setup(dev, i, 16);
-@@ -1355,10 +1475,6 @@ mtk_wed_attach(struct mtk_wed_device *de
- goto out;
-
- if (mtk_wed_get_rx_capa(dev)) {
-- ret = mtk_wed_rx_buffer_alloc(dev);
-- if (ret)
-- goto out;
--
- ret = mtk_wed_rro_alloc(dev);
- if (ret)
- goto out;
---- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
-@@ -24,11 +24,15 @@ struct mtk_wdma_desc {
-
- #define MTK_WED_RESET 0x008
- #define MTK_WED_RESET_TX_BM BIT(0)
-+#define MTK_WED_RESET_RX_BM BIT(1)
- #define MTK_WED_RESET_TX_FREE_AGENT BIT(4)
- #define MTK_WED_RESET_WPDMA_TX_DRV BIT(8)
- #define MTK_WED_RESET_WPDMA_RX_DRV BIT(9)
-+#define MTK_WED_RESET_WPDMA_RX_D_DRV BIT(10)
- #define MTK_WED_RESET_WPDMA_INT_AGENT BIT(11)
- #define MTK_WED_RESET_WED_TX_DMA BIT(12)
-+#define MTK_WED_RESET_WED_RX_DMA BIT(13)
-+#define MTK_WED_RESET_WDMA_TX_DRV BIT(16)
- #define MTK_WED_RESET_WDMA_RX_DRV BIT(17)
- #define MTK_WED_RESET_WDMA_INT_AGENT BIT(19)
- #define MTK_WED_RESET_RX_RRO_QM BIT(20)
-@@ -158,6 +162,8 @@ struct mtk_wdma_desc {
- #define MTK_WED_RESET_IDX 0x20c
- #define MTK_WED_RESET_IDX_TX GENMASK(3, 0)
- #define MTK_WED_RESET_IDX_RX GENMASK(17, 16)
-+#define MTK_WED_RESET_IDX_RX_V2 GENMASK(7, 6)
-+#define MTK_WED_RESET_WPDMA_IDX_RX GENMASK(31, 30)
-
- #define MTK_WED_TX_MIB(_n) (0x2a0 + (_n) * 4)
- #define MTK_WED_RX_MIB(_n) (0x2e0 + (_n) * 4)
-@@ -267,6 +273,9 @@ struct mtk_wdma_desc {
-
- #define MTK_WED_WPDMA_RX_D_GLO_CFG 0x75c
- #define MTK_WED_WPDMA_RX_D_RX_DRV_EN BIT(0)
-+#define MTK_WED_WPDMA_RX_D_RX_DRV_BUSY BIT(1)
-+#define MTK_WED_WPDMA_RX_D_FSM_RETURN_IDLE BIT(3)
-+#define MTK_WED_WPDMA_RX_D_RST_INIT_COMPLETE BIT(4)
- #define MTK_WED_WPDMA_RX_D_INIT_PHASE_RXEN_SEL GENMASK(11, 7)
- #define MTK_WED_WPDMA_RX_D_RXD_READ_LEN GENMASK(31, 24)
-
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Thu, 24 Nov 2022 16:22:55 +0100
-Subject: [PATCH] net: ethernet: mtk_wed: add reset to tx_ring_setup callback
-
-Introduce reset parameter to mtk_wed_tx_ring_setup signature.
-This is a preliminary patch to add Wireless Ethernet Dispatcher reset
-support.
-
-Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -1181,7 +1181,8 @@ mtk_wed_ring_alloc(struct mtk_wed_device
- }
-
- static int
--mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev, int idx, int size)
-+mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev, int idx, int size,
-+ bool reset)
- {
- u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version;
- struct mtk_wed_ring *wdma;
-@@ -1190,8 +1191,8 @@ mtk_wed_wdma_rx_ring_setup(struct mtk_we
- return -EINVAL;
-
- wdma = &dev->rx_wdma[idx];
-- if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, desc_size,
-- true))
-+ if (!reset && mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE,
-+ desc_size, true))
- return -ENOMEM;
-
- wdma_w32(dev, MTK_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_BASE,
-@@ -1389,7 +1390,7 @@ mtk_wed_start(struct mtk_wed_device *dev
-
- for (i = 0; i < ARRAY_SIZE(dev->rx_wdma); i++)
- if (!dev->rx_wdma[i].desc)
-- mtk_wed_wdma_rx_ring_setup(dev, i, 16);
-+ mtk_wed_wdma_rx_ring_setup(dev, i, 16, false);
-
- mtk_wed_hw_init(dev);
- mtk_wed_configure_irq(dev, irq_mask);
-@@ -1498,7 +1499,8 @@ unlock:
- }
-
- static int
--mtk_wed_tx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs)
-+mtk_wed_tx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs,
-+ bool reset)
- {
- struct mtk_wed_ring *ring = &dev->tx_ring[idx];
-
-@@ -1517,11 +1519,12 @@ mtk_wed_tx_ring_setup(struct mtk_wed_dev
- if (WARN_ON(idx >= ARRAY_SIZE(dev->tx_ring)))
- return -EINVAL;
-
-- if (mtk_wed_ring_alloc(dev, ring, MTK_WED_TX_RING_SIZE,
-- sizeof(*ring->desc), true))
-+ if (!reset && mtk_wed_ring_alloc(dev, ring, MTK_WED_TX_RING_SIZE,
-+ sizeof(*ring->desc), true))
- return -ENOMEM;
-
-- if (mtk_wed_wdma_rx_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE))
-+ if (mtk_wed_wdma_rx_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE,
-+ reset))
- return -ENOMEM;
-
- ring->reg_base = MTK_WED_RING_TX(idx);
---- a/include/linux/soc/mediatek/mtk_wed.h
-+++ b/include/linux/soc/mediatek/mtk_wed.h
-@@ -158,7 +158,7 @@ struct mtk_wed_device {
- struct mtk_wed_ops {
- int (*attach)(struct mtk_wed_device *dev);
- int (*tx_ring_setup)(struct mtk_wed_device *dev, int ring,
-- void __iomem *regs);
-+ void __iomem *regs, bool reset);
- int (*rx_ring_setup)(struct mtk_wed_device *dev, int ring,
- void __iomem *regs);
- int (*txfree_ring_setup)(struct mtk_wed_device *dev,
-@@ -216,8 +216,8 @@ mtk_wed_get_rx_capa(struct mtk_wed_devic
- #define mtk_wed_device_active(_dev) !!(_dev)->ops
- #define mtk_wed_device_detach(_dev) (_dev)->ops->detach(_dev)
- #define mtk_wed_device_start(_dev, _mask) (_dev)->ops->start(_dev, _mask)
--#define mtk_wed_device_tx_ring_setup(_dev, _ring, _regs) \
-- (_dev)->ops->tx_ring_setup(_dev, _ring, _regs)
-+#define mtk_wed_device_tx_ring_setup(_dev, _ring, _regs, _reset) \
-+ (_dev)->ops->tx_ring_setup(_dev, _ring, _regs, _reset)
- #define mtk_wed_device_txfree_ring_setup(_dev, _regs) \
- (_dev)->ops->txfree_ring_setup(_dev, _regs)
- #define mtk_wed_device_reg_read(_dev, _reg) \
-@@ -243,7 +243,7 @@ static inline bool mtk_wed_device_active
- }
- #define mtk_wed_device_detach(_dev) do {} while (0)
- #define mtk_wed_device_start(_dev, _mask) do {} while (0)
--#define mtk_wed_device_tx_ring_setup(_dev, _ring, _regs) -ENODEV
-+#define mtk_wed_device_tx_ring_setup(_dev, _ring, _regs, _reset) -ENODEV
- #define mtk_wed_device_txfree_ring_setup(_dev, _ring, _regs) -ENODEV
- #define mtk_wed_device_reg_read(_dev, _reg) 0
- #define mtk_wed_device_reg_write(_dev, _reg, _val) do {} while (0)
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Thu, 1 Dec 2022 16:26:53 +0100
-Subject: [PATCH] net: ethernet: mtk_wed: fix sleep while atomic in
- mtk_wed_wo_queue_refill
-
-In order to fix the following sleep while atomic bug always alloc pages
-with GFP_ATOMIC in mtk_wed_wo_queue_refill since page_frag_alloc runs in
-spin_lock critical section.
-
-[ 9.049719] Hardware name: MediaTek MT7986a RFB (DT)
-[ 9.054665] Call trace:
-[ 9.057096] dump_backtrace+0x0/0x154
-[ 9.060751] show_stack+0x14/0x1c
-[ 9.064052] dump_stack_lvl+0x64/0x7c
-[ 9.067702] dump_stack+0x14/0x2c
-[ 9.071001] ___might_sleep+0xec/0x120
-[ 9.074736] __might_sleep+0x4c/0x9c
-[ 9.078296] __alloc_pages+0x184/0x2e4
-[ 9.082030] page_frag_alloc_align+0x98/0x1ac
-[ 9.086369] mtk_wed_wo_queue_refill+0x134/0x234
-[ 9.090974] mtk_wed_wo_init+0x174/0x2c0
-[ 9.094881] mtk_wed_attach+0x7c8/0x7e0
-[ 9.098701] mt7915_mmio_wed_init+0x1f0/0x3a0 [mt7915e]
-[ 9.103940] mt7915_pci_probe+0xec/0x3bc [mt7915e]
-[ 9.108727] pci_device_probe+0xac/0x13c
-[ 9.112638] really_probe.part.0+0x98/0x2f4
-[ 9.116807] __driver_probe_device+0x94/0x13c
-[ 9.121147] driver_probe_device+0x40/0x114
-[ 9.125314] __driver_attach+0x7c/0x180
-[ 9.129133] bus_for_each_dev+0x5c/0x90
-[ 9.132953] driver_attach+0x20/0x2c
-[ 9.136513] bus_add_driver+0x104/0x1fc
-[ 9.140333] driver_register+0x74/0x120
-[ 9.144153] __pci_register_driver+0x40/0x50
-[ 9.148407] mt7915_init+0x5c/0x1000 [mt7915e]
-[ 9.152848] do_one_initcall+0x40/0x25c
-[ 9.156669] do_init_module+0x44/0x230
-[ 9.160403] load_module+0x1f30/0x2750
-[ 9.164135] __do_sys_init_module+0x150/0x200
-[ 9.168475] __arm64_sys_init_module+0x18/0x20
-[ 9.172901] invoke_syscall.constprop.0+0x4c/0xe0
-[ 9.177589] do_el0_svc+0x48/0xe0
-[ 9.180889] el0_svc+0x14/0x50
-[ 9.183929] el0t_64_sync_handler+0x9c/0x120
-[ 9.188183] el0t_64_sync+0x158/0x15c
-
-Fixes: 799684448e3e ("net: ethernet: mtk_wed: introduce wed wo support")
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Reviewed-by: Pavan Chebbi <pavan.chebbi@broadcom.com>
-Link: https://lore.kernel.org/r/67ca94bdd3d9eaeb86e52b3050fbca0bcf7bb02f.1669908312.git.lorenzo@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed_wo.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.c
-@@ -133,17 +133,18 @@ mtk_wed_wo_dequeue(struct mtk_wed_wo *wo
-
- static int
- mtk_wed_wo_queue_refill(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q,
-- gfp_t gfp, bool rx)
-+ bool rx)
- {
- enum dma_data_direction dir = rx ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
- int n_buf = 0;
-
- spin_lock_bh(&q->lock);
- while (q->queued < q->n_desc) {
-- void *buf = page_frag_alloc(&q->cache, q->buf_size, gfp);
- struct mtk_wed_wo_queue_entry *entry;
- dma_addr_t addr;
-+ void *buf;
-
-+ buf = page_frag_alloc(&q->cache, q->buf_size, GFP_ATOMIC);
- if (!buf)
- break;
-
-@@ -215,7 +216,7 @@ mtk_wed_wo_rx_run_queue(struct mtk_wed_w
- mtk_wed_mcu_rx_unsolicited_event(wo, skb);
- }
-
-- if (mtk_wed_wo_queue_refill(wo, q, GFP_ATOMIC, true)) {
-+ if (mtk_wed_wo_queue_refill(wo, q, true)) {
- u32 index = (q->head - 1) % q->n_desc;
-
- mtk_wed_wo_queue_kick(wo, q, index);
-@@ -432,7 +433,7 @@ mtk_wed_wo_hardware_init(struct mtk_wed_
- if (ret)
- goto error;
-
-- mtk_wed_wo_queue_refill(wo, &wo->q_tx, GFP_KERNEL, false);
-+ mtk_wed_wo_queue_refill(wo, &wo->q_tx, false);
- mtk_wed_wo_queue_reset(wo, &wo->q_tx);
-
- regs.desc_base = MTK_WED_WO_CCIF_DUMMY5;
-@@ -446,7 +447,7 @@ mtk_wed_wo_hardware_init(struct mtk_wed_
- if (ret)
- goto error;
-
-- mtk_wed_wo_queue_refill(wo, &wo->q_rx, GFP_KERNEL, true);
-+ mtk_wed_wo_queue_refill(wo, &wo->q_rx, true);
- mtk_wed_wo_queue_reset(wo, &wo->q_rx);
-
- /* rx queue irqmask */
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Tue, 10 Jan 2023 10:31:26 +0100
-Subject: [PATCH] net: ethernet: mtk_wed: get rid of queue lock for rx queue
-
-Queue spinlock is currently held in mtk_wed_wo_queue_rx_clean and
-mtk_wed_wo_queue_refill routines for MTK Wireless Ethernet Dispatcher
-MCU rx queue. mtk_wed_wo_queue_refill() is running during initialization
-and in rx tasklet while mtk_wed_wo_queue_rx_clean() is running in
-mtk_wed_wo_hw_deinit() during hw de-init phase after rx tasklet has been
-disabled. Since mtk_wed_wo_queue_rx_clean and mtk_wed_wo_queue_refill
-routines can't run concurrently get rid of spinlock for mcu rx queue.
-
-Reviewed-by: Alexander Duyck <alexanderduyck@fb.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Link: https://lore.kernel.org/r/36ec3b729542ea60898471d890796f745479ba32.1673342990.git.lorenzo@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed_wo.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.c
-@@ -138,7 +138,6 @@ mtk_wed_wo_queue_refill(struct mtk_wed_w
- enum dma_data_direction dir = rx ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
- int n_buf = 0;
-
-- spin_lock_bh(&q->lock);
- while (q->queued < q->n_desc) {
- struct mtk_wed_wo_queue_entry *entry;
- dma_addr_t addr;
-@@ -172,7 +171,6 @@ mtk_wed_wo_queue_refill(struct mtk_wed_w
- q->queued++;
- n_buf++;
- }
-- spin_unlock_bh(&q->lock);
-
- return n_buf;
- }
-@@ -316,7 +314,6 @@ mtk_wed_wo_queue_rx_clean(struct mtk_wed
- {
- struct page *page;
-
-- spin_lock_bh(&q->lock);
- for (;;) {
- void *buf = mtk_wed_wo_dequeue(wo, q, NULL, true);
-
-@@ -325,7 +322,6 @@ mtk_wed_wo_queue_rx_clean(struct mtk_wed
-
- skb_free_frag(buf);
- }
-- spin_unlock_bh(&q->lock);
-
- if (!q->cache.va)
- return;
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Thu, 12 Jan 2023 10:21:29 +0100
-Subject: [PATCH] net: ethernet: mtk_wed: get rid of queue lock for tx queue
-
-Similar to MTK Wireless Ethernet Dispatcher (WED) MCU rx queue,
-we do not need to protect WED MCU tx queue with a spin lock since
-the tx queue is accessed in the two following routines:
-- mtk_wed_wo_queue_tx_skb():
- it is run at initialization and during mt7915 normal operation.
- Moreover MCU messages are serialized through MCU mutex.
-- mtk_wed_wo_queue_tx_clean():
- it runs just at mt7915 driver module unload when no more messages
- are sent to the MCU.
-
-Remove tx queue spinlock.
-
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Link: https://lore.kernel.org/r/7bd0337b2a13ab1a63673b7c03fd35206b3b284e.1673515140.git.lorenzo@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed_wo.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.c
-@@ -258,7 +258,6 @@ mtk_wed_wo_queue_alloc(struct mtk_wed_wo
- int n_desc, int buf_size, int index,
- struct mtk_wed_wo_queue_regs *regs)
- {
-- spin_lock_init(&q->lock);
- q->regs = *regs;
- q->n_desc = n_desc;
- q->buf_size = buf_size;
-@@ -290,7 +289,6 @@ mtk_wed_wo_queue_tx_clean(struct mtk_wed
- struct page *page;
- int i;
-
-- spin_lock_bh(&q->lock);
- for (i = 0; i < q->n_desc; i++) {
- struct mtk_wed_wo_queue_entry *entry = &q->entry[i];
-
-@@ -299,7 +297,6 @@ mtk_wed_wo_queue_tx_clean(struct mtk_wed
- skb_free_frag(entry->buf);
- entry->buf = NULL;
- }
-- spin_unlock_bh(&q->lock);
-
- if (!q->cache.va)
- return;
-@@ -347,8 +344,6 @@ int mtk_wed_wo_queue_tx_skb(struct mtk_w
- int ret = 0, index;
- u32 ctrl;
-
-- spin_lock_bh(&q->lock);
--
- q->tail = mtk_wed_mmio_r32(wo, q->regs.dma_idx);
- index = (q->head + 1) % q->n_desc;
- if (q->tail == index) {
-@@ -379,8 +374,6 @@ int mtk_wed_wo_queue_tx_skb(struct mtk_w
- mtk_wed_wo_queue_kick(wo, q, q->head);
- mtk_wed_wo_kickout(wo);
- out:
-- spin_unlock_bh(&q->lock);
--
- dev_kfree_skb(skb);
-
- return ret;
---- a/drivers/net/ethernet/mediatek/mtk_wed_wo.h
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h
-@@ -211,7 +211,6 @@ struct mtk_wed_wo_queue {
- struct mtk_wed_wo_queue_regs regs;
-
- struct page_frag_cache cache;
-- spinlock_t lock;
-
- struct mtk_wed_wo_queue_desc *desc;
- dma_addr_t desc_dma;
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Sat, 14 Jan 2023 18:01:28 +0100
-Subject: [PATCH] net: ethernet: mtk_eth_soc: introduce mtk_hw_reset utility
- routine
-
-This is a preliminary patch to add Wireless Ethernet Dispatcher reset
-support.
-
-Reviewed-by: Leon Romanovsky <leonro@nvidia.com>
-Tested-by: Daniel Golle <daniel@makrotopia.org>
-Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -3256,6 +3256,27 @@ static void mtk_set_mcr_max_rx(struct mt
- mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id));
- }
-
-+static void mtk_hw_reset(struct mtk_eth *eth)
-+{
-+ u32 val;
-+
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
-+ regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN, 0);
-+ val = RSTCTRL_PPE0_V2;
-+ } else {
-+ val = RSTCTRL_PPE0;
-+ }
-+
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
-+ val |= RSTCTRL_PPE1;
-+
-+ ethsys_reset(eth, RSTCTRL_ETH | RSTCTRL_FE | val);
-+
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-+ regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN,
-+ 0x3ffffff);
-+}
-+
- static int mtk_hw_init(struct mtk_eth *eth)
- {
- u32 dma_mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA |
-@@ -3295,22 +3316,9 @@ static int mtk_hw_init(struct mtk_eth *e
- return 0;
- }
-
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
-- regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN, 0);
-- val = RSTCTRL_PPE0_V2;
-- } else {
-- val = RSTCTRL_PPE0;
-- }
--
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
-- val |= RSTCTRL_PPE1;
--
-- ethsys_reset(eth, RSTCTRL_ETH | RSTCTRL_FE | val);
-+ mtk_hw_reset(eth);
-
- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
-- regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN,
-- 0x3ffffff);
--
- /* Set FE to PDMAv2 if necessary */
- val = mtk_r32(eth, MTK_FE_GLO_MISC);
- mtk_w32(eth, val | BIT(4), MTK_FE_GLO_MISC);
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Sat, 14 Jan 2023 18:01:29 +0100
-Subject: [PATCH] net: ethernet: mtk_eth_soc: introduce mtk_hw_warm_reset
- support
-
-Introduce mtk_hw_warm_reset utility routine. This is a preliminary patch
-to align reset procedure to vendor sdk and avoid to power down the chip
-during hw reset.
-
-Reviewed-by: Leon Romanovsky <leonro@nvidia.com>
-Tested-by: Daniel Golle <daniel@makrotopia.org>
-Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -3277,7 +3277,54 @@ static void mtk_hw_reset(struct mtk_eth
- 0x3ffffff);
- }
-
--static int mtk_hw_init(struct mtk_eth *eth)
-+static u32 mtk_hw_reset_read(struct mtk_eth *eth)
-+{
-+ u32 val;
-+
-+ regmap_read(eth->ethsys, ETHSYS_RSTCTRL, &val);
-+ return val;
-+}
-+
-+static void mtk_hw_warm_reset(struct mtk_eth *eth)
-+{
-+ u32 rst_mask, val;
-+
-+ regmap_update_bits(eth->ethsys, ETHSYS_RSTCTRL, RSTCTRL_FE,
-+ RSTCTRL_FE);
-+ if (readx_poll_timeout_atomic(mtk_hw_reset_read, eth, val,
-+ val & RSTCTRL_FE, 1, 1000)) {
-+ dev_err(eth->dev, "warm reset failed\n");
-+ mtk_hw_reset(eth);
-+ return;
-+ }
-+
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-+ rst_mask = RSTCTRL_ETH | RSTCTRL_PPE0_V2;
-+ else
-+ rst_mask = RSTCTRL_ETH | RSTCTRL_PPE0;
-+
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
-+ rst_mask |= RSTCTRL_PPE1;
-+
-+ regmap_update_bits(eth->ethsys, ETHSYS_RSTCTRL, rst_mask, rst_mask);
-+
-+ udelay(1);
-+ val = mtk_hw_reset_read(eth);
-+ if (!(val & rst_mask))
-+ dev_err(eth->dev, "warm reset stage0 failed %08x (%08x)\n",
-+ val, rst_mask);
-+
-+ rst_mask |= RSTCTRL_FE;
-+ regmap_update_bits(eth->ethsys, ETHSYS_RSTCTRL, rst_mask, ~rst_mask);
-+
-+ udelay(1);
-+ val = mtk_hw_reset_read(eth);
-+ if (val & rst_mask)
-+ dev_err(eth->dev, "warm reset stage1 failed %08x (%08x)\n",
-+ val, rst_mask);
-+}
-+
-+static int mtk_hw_init(struct mtk_eth *eth, bool reset)
- {
- u32 dma_mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA |
- ETHSYS_DMA_AG_MAP_PPE;
-@@ -3316,7 +3363,12 @@ static int mtk_hw_init(struct mtk_eth *e
- return 0;
- }
-
-- mtk_hw_reset(eth);
-+ msleep(100);
-+
-+ if (reset)
-+ mtk_hw_warm_reset(eth);
-+ else
-+ mtk_hw_reset(eth);
-
- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
- /* Set FE to PDMAv2 if necessary */
-@@ -3507,7 +3559,7 @@ static void mtk_pending_work(struct work
- if (eth->dev->pins)
- pinctrl_select_state(eth->dev->pins->p,
- eth->dev->pins->default_state);
-- mtk_hw_init(eth);
-+ mtk_hw_init(eth, true);
-
- /* restart DMA and enable IRQs */
- for (i = 0; i < MTK_MAC_COUNT; i++) {
-@@ -4109,7 +4161,7 @@ static int mtk_probe(struct platform_dev
- eth->msg_enable = netif_msg_init(mtk_msg_level, MTK_DEFAULT_MSG_ENABLE);
- INIT_WORK(ð->pending_work, mtk_pending_work);
-
-- err = mtk_hw_init(eth);
-+ err = mtk_hw_init(eth, false);
- if (err)
- goto err_wed_exit;
-
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Sat, 14 Jan 2023 18:01:30 +0100
-Subject: [PATCH] net: ethernet: mtk_eth_soc: align reset procedure to vendor
- sdk
-
-Avoid to power-down the ethernet chip during hw reset and align reset
-procedure to vendor sdk.
-
-Reviewed-by: Leon Romanovsky <leonro@nvidia.com>
-Tested-by: Daniel Golle <daniel@makrotopia.org>
-Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -2844,14 +2844,29 @@ static void mtk_dma_free(struct mtk_eth
- kfree(eth->scratch_head);
- }
-
-+static bool mtk_hw_reset_check(struct mtk_eth *eth)
-+{
-+ u32 val = mtk_r32(eth, MTK_INT_STATUS2);
-+
-+ return (val & MTK_FE_INT_FQ_EMPTY) || (val & MTK_FE_INT_RFIFO_UF) ||
-+ (val & MTK_FE_INT_RFIFO_OV) || (val & MTK_FE_INT_TSO_FAIL) ||
-+ (val & MTK_FE_INT_TSO_ALIGN) || (val & MTK_FE_INT_TSO_ILLEGAL);
-+}
-+
- static void mtk_tx_timeout(struct net_device *dev, unsigned int txqueue)
- {
- struct mtk_mac *mac = netdev_priv(dev);
- struct mtk_eth *eth = mac->hw;
-
-+ if (test_bit(MTK_RESETTING, ð->state))
-+ return;
-+
-+ if (!mtk_hw_reset_check(eth))
-+ return;
-+
- eth->netdev[mac->id]->stats.tx_errors++;
-- netif_err(eth, tx_err, dev,
-- "transmit timed out\n");
-+ netif_err(eth, tx_err, dev, "transmit timed out\n");
-+
- schedule_work(ð->pending_work);
- }
-
-@@ -3331,15 +3346,17 @@ static int mtk_hw_init(struct mtk_eth *e
- const struct mtk_reg_map *reg_map = eth->soc->reg_map;
- int i, val, ret;
-
-- if (test_and_set_bit(MTK_HW_INIT, ð->state))
-+ if (!reset && test_and_set_bit(MTK_HW_INIT, ð->state))
- return 0;
-
-- pm_runtime_enable(eth->dev);
-- pm_runtime_get_sync(eth->dev);
-+ if (!reset) {
-+ pm_runtime_enable(eth->dev);
-+ pm_runtime_get_sync(eth->dev);
-
-- ret = mtk_clk_enable(eth);
-- if (ret)
-- goto err_disable_pm;
-+ ret = mtk_clk_enable(eth);
-+ if (ret)
-+ goto err_disable_pm;
-+ }
-
- if (eth->ethsys)
- regmap_update_bits(eth->ethsys, ETHSYS_DMA_AG_MAP, dma_mask,
-@@ -3468,8 +3485,10 @@ static int mtk_hw_init(struct mtk_eth *e
- return 0;
-
- err_disable_pm:
-- pm_runtime_put_sync(eth->dev);
-- pm_runtime_disable(eth->dev);
-+ if (!reset) {
-+ pm_runtime_put_sync(eth->dev);
-+ pm_runtime_disable(eth->dev);
-+ }
-
- return ret;
- }
-@@ -3531,30 +3550,53 @@ static int mtk_do_ioctl(struct net_devic
- return -EOPNOTSUPP;
- }
-
-+static void mtk_prepare_for_reset(struct mtk_eth *eth)
-+{
-+ u32 val;
-+ int i;
-+
-+ /* disabe FE P3 and P4 */
-+ val = mtk_r32(eth, MTK_FE_GLO_CFG) | MTK_FE_LINK_DOWN_P3;
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
-+ val |= MTK_FE_LINK_DOWN_P4;
-+ mtk_w32(eth, val, MTK_FE_GLO_CFG);
-+
-+ /* adjust PPE configurations to prepare for reset */
-+ for (i = 0; i < ARRAY_SIZE(eth->ppe); i++)
-+ mtk_ppe_prepare_reset(eth->ppe[i]);
-+
-+ /* disable NETSYS interrupts */
-+ mtk_w32(eth, 0, MTK_FE_INT_ENABLE);
-+
-+ /* force link down GMAC */
-+ for (i = 0; i < 2; i++) {
-+ val = mtk_r32(eth, MTK_MAC_MCR(i)) & ~MAC_MCR_FORCE_LINK;
-+ mtk_w32(eth, val, MTK_MAC_MCR(i));
-+ }
-+}
-+
- static void mtk_pending_work(struct work_struct *work)
- {
- struct mtk_eth *eth = container_of(work, struct mtk_eth, pending_work);
-- int err, i;
- unsigned long restart = 0;
-+ u32 val;
-+ int i;
-
- rtnl_lock();
--
-- dev_dbg(eth->dev, "[%s][%d] reset\n", __func__, __LINE__);
- set_bit(MTK_RESETTING, ð->state);
-
-+ mtk_prepare_for_reset(eth);
-+
- /* stop all devices to make sure that dma is properly shut down */
- for (i = 0; i < MTK_MAC_COUNT; i++) {
-- if (!eth->netdev[i])
-+ if (!eth->netdev[i] || !netif_running(eth->netdev[i]))
- continue;
-+
- mtk_stop(eth->netdev[i]);
- __set_bit(i, &restart);
- }
-- dev_dbg(eth->dev, "[%s][%d] mtk_stop ends\n", __func__, __LINE__);
-
-- /* restart underlying hardware such as power, clock, pin mux
-- * and the connected phy
-- */
-- mtk_hw_deinit(eth);
-+ usleep_range(15000, 16000);
-
- if (eth->dev->pins)
- pinctrl_select_state(eth->dev->pins->p,
-@@ -3565,15 +3607,19 @@ static void mtk_pending_work(struct work
- for (i = 0; i < MTK_MAC_COUNT; i++) {
- if (!test_bit(i, &restart))
- continue;
-- err = mtk_open(eth->netdev[i]);
-- if (err) {
-+
-+ if (mtk_open(eth->netdev[i])) {
- netif_alert(eth, ifup, eth->netdev[i],
-- "Driver up/down cycle failed, closing device.\n");
-+ "Driver up/down cycle failed\n");
- dev_close(eth->netdev[i]);
- }
- }
-
-- dev_dbg(eth->dev, "[%s][%d] reset done\n", __func__, __LINE__);
-+ /* enabe FE P3 and P4 */
-+ val = mtk_r32(eth, MTK_FE_GLO_CFG) & ~MTK_FE_LINK_DOWN_P3;
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
-+ val &= ~MTK_FE_LINK_DOWN_P4;
-+ mtk_w32(eth, val, MTK_FE_GLO_CFG);
-
- clear_bit(MTK_RESETTING, ð->state);
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -72,12 +72,24 @@
- #define MTK_HW_LRO_REPLACE_DELTA 1000
- #define MTK_HW_LRO_SDL_REMAIN_ROOM 1522
-
-+/* Frame Engine Global Configuration */
-+#define MTK_FE_GLO_CFG 0x00
-+#define MTK_FE_LINK_DOWN_P3 BIT(11)
-+#define MTK_FE_LINK_DOWN_P4 BIT(12)
-+
- /* Frame Engine Global Reset Register */
- #define MTK_RST_GL 0x04
- #define RST_GL_PSE BIT(0)
-
- /* Frame Engine Interrupt Status Register */
- #define MTK_INT_STATUS2 0x08
-+#define MTK_FE_INT_ENABLE 0x0c
-+#define MTK_FE_INT_FQ_EMPTY BIT(8)
-+#define MTK_FE_INT_TSO_FAIL BIT(12)
-+#define MTK_FE_INT_TSO_ILLEGAL BIT(13)
-+#define MTK_FE_INT_TSO_ALIGN BIT(14)
-+#define MTK_FE_INT_RFIFO_OV BIT(18)
-+#define MTK_FE_INT_RFIFO_UF BIT(19)
- #define MTK_GDM1_AF BIT(28)
- #define MTK_GDM2_AF BIT(29)
-
---- a/drivers/net/ethernet/mediatek/mtk_ppe.c
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
-@@ -710,6 +710,33 @@ int mtk_foe_entry_idle_time(struct mtk_p
- return __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
- }
-
-+int mtk_ppe_prepare_reset(struct mtk_ppe *ppe)
-+{
-+ if (!ppe)
-+ return -EINVAL;
-+
-+ /* disable KA */
-+ ppe_clear(ppe, MTK_PPE_TB_CFG, MTK_PPE_TB_CFG_KEEPALIVE);
-+ ppe_clear(ppe, MTK_PPE_BIND_LMT1, MTK_PPE_NTU_KEEPALIVE);
-+ ppe_w32(ppe, MTK_PPE_KEEPALIVE, 0);
-+ usleep_range(10000, 11000);
-+
-+ /* set KA timer to maximum */
-+ ppe_set(ppe, MTK_PPE_BIND_LMT1, MTK_PPE_NTU_KEEPALIVE);
-+ ppe_w32(ppe, MTK_PPE_KEEPALIVE, 0xffffffff);
-+
-+ /* set KA tick select */
-+ ppe_set(ppe, MTK_PPE_TB_CFG, MTK_PPE_TB_TICK_SEL);
-+ ppe_set(ppe, MTK_PPE_TB_CFG, MTK_PPE_TB_CFG_KEEPALIVE);
-+ usleep_range(10000, 11000);
-+
-+ /* disable scan mode */
-+ ppe_clear(ppe, MTK_PPE_TB_CFG, MTK_PPE_TB_CFG_SCAN_MODE);
-+ usleep_range(10000, 11000);
-+
-+ return mtk_ppe_wait_busy(ppe);
-+}
-+
- struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
- int version, int index)
- {
---- a/drivers/net/ethernet/mediatek/mtk_ppe.h
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
-@@ -306,6 +306,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_
- void mtk_ppe_deinit(struct mtk_eth *eth);
- void mtk_ppe_start(struct mtk_ppe *ppe);
- int mtk_ppe_stop(struct mtk_ppe *ppe);
-+int mtk_ppe_prepare_reset(struct mtk_ppe *ppe);
-
- void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash);
-
---- a/drivers/net/ethernet/mediatek/mtk_ppe_regs.h
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe_regs.h
-@@ -58,6 +58,12 @@
- #define MTK_PPE_TB_CFG_SCAN_MODE GENMASK(17, 16)
- #define MTK_PPE_TB_CFG_HASH_DEBUG GENMASK(19, 18)
- #define MTK_PPE_TB_CFG_INFO_SEL BIT(20)
-+#define MTK_PPE_TB_TICK_SEL BIT(24)
-+
-+#define MTK_PPE_BIND_LMT1 0x230
-+#define MTK_PPE_NTU_KEEPALIVE GENMASK(23, 16)
-+
-+#define MTK_PPE_KEEPALIVE 0x234
-
- enum {
- MTK_PPE_SCAN_MODE_DISABLED,
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Sat, 14 Jan 2023 18:01:31 +0100
-Subject: [PATCH] net: ethernet: mtk_eth_soc: add dma checks to
- mtk_hw_reset_check
-
-Introduce mtk_hw_check_dma_hang routine to monitor possible dma hangs.
-
-Reviewed-by: Leon Romanovsky <leonro@nvidia.com>
-Tested-by: Daniel Golle <daniel@makrotopia.org>
-Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -50,6 +50,7 @@ static const struct mtk_reg_map mtk_reg_
- .delay_irq = 0x0a0c,
- .irq_status = 0x0a20,
- .irq_mask = 0x0a28,
-+ .adma_rx_dbg0 = 0x0a38,
- .int_grp = 0x0a50,
- },
- .qdma = {
-@@ -79,6 +80,8 @@ static const struct mtk_reg_map mtk_reg_
- [0] = 0x2800,
- [1] = 0x2c00,
- },
-+ .pse_iq_sta = 0x0110,
-+ .pse_oq_sta = 0x0118,
- };
-
- static const struct mtk_reg_map mt7628_reg_map = {
-@@ -109,6 +112,7 @@ static const struct mtk_reg_map mt7986_r
- .delay_irq = 0x620c,
- .irq_status = 0x6220,
- .irq_mask = 0x6228,
-+ .adma_rx_dbg0 = 0x6238,
- .int_grp = 0x6250,
- },
- .qdma = {
-@@ -138,6 +142,8 @@ static const struct mtk_reg_map mt7986_r
- [0] = 0x4800,
- [1] = 0x4c00,
- },
-+ .pse_iq_sta = 0x0180,
-+ .pse_oq_sta = 0x01a0,
- };
-
- /* strings used by ethtool */
-@@ -3339,6 +3345,102 @@ static void mtk_hw_warm_reset(struct mtk
- val, rst_mask);
- }
-
-+static bool mtk_hw_check_dma_hang(struct mtk_eth *eth)
-+{
-+ const struct mtk_reg_map *reg_map = eth->soc->reg_map;
-+ bool gmac1_tx, gmac2_tx, gdm1_tx, gdm2_tx;
-+ bool oq_hang, cdm1_busy, adma_busy;
-+ bool wtx_busy, cdm_full, oq_free;
-+ u32 wdidx, val, gdm1_fc, gdm2_fc;
-+ bool qfsm_hang, qfwd_hang;
-+ bool ret = false;
-+
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
-+ return false;
-+
-+ /* WDMA sanity checks */
-+ wdidx = mtk_r32(eth, reg_map->wdma_base[0] + 0xc);
-+
-+ val = mtk_r32(eth, reg_map->wdma_base[0] + 0x204);
-+ wtx_busy = FIELD_GET(MTK_TX_DMA_BUSY, val);
-+
-+ val = mtk_r32(eth, reg_map->wdma_base[0] + 0x230);
-+ cdm_full = !FIELD_GET(MTK_CDM_TXFIFO_RDY, val);
-+
-+ oq_free = (!(mtk_r32(eth, reg_map->pse_oq_sta) & GENMASK(24, 16)) &&
-+ !(mtk_r32(eth, reg_map->pse_oq_sta + 0x4) & GENMASK(8, 0)) &&
-+ !(mtk_r32(eth, reg_map->pse_oq_sta + 0x10) & GENMASK(24, 16)));
-+
-+ if (wdidx == eth->reset.wdidx && wtx_busy && cdm_full && oq_free) {
-+ if (++eth->reset.wdma_hang_count > 2) {
-+ eth->reset.wdma_hang_count = 0;
-+ ret = true;
-+ }
-+ goto out;
-+ }
-+
-+ /* QDMA sanity checks */
-+ qfsm_hang = !!mtk_r32(eth, reg_map->qdma.qtx_cfg + 0x234);
-+ qfwd_hang = !mtk_r32(eth, reg_map->qdma.qtx_cfg + 0x308);
-+
-+ gdm1_tx = FIELD_GET(GENMASK(31, 16), mtk_r32(eth, MTK_FE_GDM1_FSM)) > 0;
-+ gdm2_tx = FIELD_GET(GENMASK(31, 16), mtk_r32(eth, MTK_FE_GDM2_FSM)) > 0;
-+ gmac1_tx = FIELD_GET(GENMASK(31, 24), mtk_r32(eth, MTK_MAC_FSM(0))) != 1;
-+ gmac2_tx = FIELD_GET(GENMASK(31, 24), mtk_r32(eth, MTK_MAC_FSM(1))) != 1;
-+ gdm1_fc = mtk_r32(eth, reg_map->gdm1_cnt + 0x24);
-+ gdm2_fc = mtk_r32(eth, reg_map->gdm1_cnt + 0x64);
-+
-+ if (qfsm_hang && qfwd_hang &&
-+ ((gdm1_tx && gmac1_tx && gdm1_fc < 1) ||
-+ (gdm2_tx && gmac2_tx && gdm2_fc < 1))) {
-+ if (++eth->reset.qdma_hang_count > 2) {
-+ eth->reset.qdma_hang_count = 0;
-+ ret = true;
-+ }
-+ goto out;
-+ }
-+
-+ /* ADMA sanity checks */
-+ oq_hang = !!(mtk_r32(eth, reg_map->pse_oq_sta) & GENMASK(8, 0));
-+ cdm1_busy = !!(mtk_r32(eth, MTK_FE_CDM1_FSM) & GENMASK(31, 16));
-+ adma_busy = !(mtk_r32(eth, reg_map->pdma.adma_rx_dbg0) & GENMASK(4, 0)) &&
-+ !(mtk_r32(eth, reg_map->pdma.adma_rx_dbg0) & BIT(6));
-+
-+ if (oq_hang && cdm1_busy && adma_busy) {
-+ if (++eth->reset.adma_hang_count > 2) {
-+ eth->reset.adma_hang_count = 0;
-+ ret = true;
-+ }
-+ goto out;
-+ }
-+
-+ eth->reset.wdma_hang_count = 0;
-+ eth->reset.qdma_hang_count = 0;
-+ eth->reset.adma_hang_count = 0;
-+out:
-+ eth->reset.wdidx = wdidx;
-+
-+ return ret;
-+}
-+
-+static void mtk_hw_reset_monitor_work(struct work_struct *work)
-+{
-+ struct delayed_work *del_work = to_delayed_work(work);
-+ struct mtk_eth *eth = container_of(del_work, struct mtk_eth,
-+ reset.monitor_work);
-+
-+ if (test_bit(MTK_RESETTING, ð->state))
-+ goto out;
-+
-+ /* DMA stuck checks */
-+ if (mtk_hw_check_dma_hang(eth))
-+ schedule_work(ð->pending_work);
-+
-+out:
-+ schedule_delayed_work(ð->reset.monitor_work,
-+ MTK_DMA_MONITOR_TIMEOUT);
-+}
-+
- static int mtk_hw_init(struct mtk_eth *eth, bool reset)
- {
- u32 dma_mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA |
-@@ -3657,6 +3759,7 @@ static int mtk_cleanup(struct mtk_eth *e
- mtk_unreg_dev(eth);
- mtk_free_dev(eth);
- cancel_work_sync(ð->pending_work);
-+ cancel_delayed_work_sync(ð->reset.monitor_work);
-
- return 0;
- }
-@@ -4094,6 +4197,7 @@ static int mtk_probe(struct platform_dev
-
- eth->rx_dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE;
- INIT_WORK(ð->rx_dim.work, mtk_dim_rx);
-+ INIT_DELAYED_WORK(ð->reset.monitor_work, mtk_hw_reset_monitor_work);
-
- eth->tx_dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE;
- INIT_WORK(ð->tx_dim.work, mtk_dim_tx);
-@@ -4296,6 +4400,8 @@ static int mtk_probe(struct platform_dev
- netif_napi_add(ð->dummy_dev, ð->rx_napi, mtk_napi_rx);
-
- platform_set_drvdata(pdev, eth);
-+ schedule_delayed_work(ð->reset.monitor_work,
-+ MTK_DMA_MONITOR_TIMEOUT);
-
- return 0;
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -257,6 +257,8 @@
-
- #define MTK_RX_DONE_INT_V2 BIT(14)
-
-+#define MTK_CDM_TXFIFO_RDY BIT(7)
-+
- /* QDMA Interrupt grouping registers */
- #define MTK_RLS_DONE_INT BIT(0)
-
-@@ -542,6 +544,17 @@
- #define MT7628_SDM_RBCNT (MT7628_SDM_OFFSET + 0x10c)
- #define MT7628_SDM_CS_ERR (MT7628_SDM_OFFSET + 0x110)
-
-+#define MTK_FE_CDM1_FSM 0x220
-+#define MTK_FE_CDM2_FSM 0x224
-+#define MTK_FE_CDM3_FSM 0x238
-+#define MTK_FE_CDM4_FSM 0x298
-+#define MTK_FE_CDM5_FSM 0x318
-+#define MTK_FE_CDM6_FSM 0x328
-+#define MTK_FE_GDM1_FSM 0x228
-+#define MTK_FE_GDM2_FSM 0x22C
-+
-+#define MTK_MAC_FSM(x) (0x1010C + ((x) * 0x100))
-+
- struct mtk_rx_dma {
- unsigned int rxd1;
- unsigned int rxd2;
-@@ -938,6 +951,7 @@ struct mtk_reg_map {
- u32 delay_irq; /* delay interrupt */
- u32 irq_status; /* interrupt status */
- u32 irq_mask; /* interrupt mask */
-+ u32 adma_rx_dbg0;
- u32 int_grp;
- } pdma;
- struct {
-@@ -964,6 +978,8 @@ struct mtk_reg_map {
- u32 gdma_to_ppe;
- u32 ppe_base;
- u32 wdma_base[2];
-+ u32 pse_iq_sta;
-+ u32 pse_oq_sta;
- };
-
- /* struct mtk_eth_data - This is the structure holding all differences
-@@ -1006,6 +1022,8 @@ struct mtk_soc_data {
- } txrx;
- };
-
-+#define MTK_DMA_MONITOR_TIMEOUT msecs_to_jiffies(1000)
-+
- /* currently no SoC has more than 2 macs */
- #define MTK_MAX_DEVS 2
-
-@@ -1128,6 +1146,14 @@ struct mtk_eth {
- struct rhashtable flow_table;
-
- struct bpf_prog __rcu *prog;
-+
-+ struct {
-+ struct delayed_work monitor_work;
-+ u32 wdidx;
-+ u8 wdma_hang_count;
-+ u8 qdma_hang_count;
-+ u8 adma_hang_count;
-+ } reset;
- };
-
- /* struct mtk_mac - the structure that holds the info about the MACs of the
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Sat, 14 Jan 2023 18:01:32 +0100
-Subject: [PATCH] net: ethernet: mtk_wed: add reset/reset_complete callbacks
-
-Introduce reset and reset_complete wlan callback to schedule WLAN driver
-reset when ethernet/wed driver is resetting.
-
-Tested-by: Daniel Golle <daniel@makrotopia.org>
-Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -3688,6 +3688,11 @@ static void mtk_pending_work(struct work
- set_bit(MTK_RESETTING, ð->state);
-
- mtk_prepare_for_reset(eth);
-+ mtk_wed_fe_reset();
-+ /* Run again reset preliminary configuration in order to avoid any
-+ * possible race during FE reset since it can run releasing RTNL lock.
-+ */
-+ mtk_prepare_for_reset(eth);
-
- /* stop all devices to make sure that dma is properly shut down */
- for (i = 0; i < MTK_MAC_COUNT; i++) {
-@@ -3725,6 +3730,8 @@ static void mtk_pending_work(struct work
-
- clear_bit(MTK_RESETTING, ð->state);
-
-+ mtk_wed_fe_reset_complete();
-+
- rtnl_unlock();
- }
-
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -205,6 +205,48 @@ mtk_wed_wo_reset(struct mtk_wed_device *
- iounmap(reg);
- }
-
-+void mtk_wed_fe_reset(void)
-+{
-+ int i;
-+
-+ mutex_lock(&hw_lock);
-+
-+ for (i = 0; i < ARRAY_SIZE(hw_list); i++) {
-+ struct mtk_wed_hw *hw = hw_list[i];
-+ struct mtk_wed_device *dev = hw->wed_dev;
-+ int err;
-+
-+ if (!dev || !dev->wlan.reset)
-+ continue;
-+
-+ /* reset callback blocks until WLAN reset is completed */
-+ err = dev->wlan.reset(dev);
-+ if (err)
-+ dev_err(dev->dev, "wlan reset failed: %d\n", err);
-+ }
-+
-+ mutex_unlock(&hw_lock);
-+}
-+
-+void mtk_wed_fe_reset_complete(void)
-+{
-+ int i;
-+
-+ mutex_lock(&hw_lock);
-+
-+ for (i = 0; i < ARRAY_SIZE(hw_list); i++) {
-+ struct mtk_wed_hw *hw = hw_list[i];
-+ struct mtk_wed_device *dev = hw->wed_dev;
-+
-+ if (!dev || !dev->wlan.reset_complete)
-+ continue;
-+
-+ dev->wlan.reset_complete(dev);
-+ }
-+
-+ mutex_unlock(&hw_lock);
-+}
-+
- static struct mtk_wed_hw *
- mtk_wed_assign(struct mtk_wed_device *dev)
- {
---- a/drivers/net/ethernet/mediatek/mtk_wed.h
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.h
-@@ -128,6 +128,8 @@ void mtk_wed_add_hw(struct device_node *
- void mtk_wed_exit(void);
- int mtk_wed_flow_add(int index);
- void mtk_wed_flow_remove(int index);
-+void mtk_wed_fe_reset(void);
-+void mtk_wed_fe_reset_complete(void);
- #else
- static inline void
- mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
-@@ -147,6 +149,13 @@ static inline void mtk_wed_flow_remove(i
- {
- }
-
-+static inline void mtk_wed_fe_reset(void)
-+{
-+}
-+
-+static inline void mtk_wed_fe_reset_complete(void)
-+{
-+}
- #endif
-
- #ifdef CONFIG_DEBUG_FS
---- a/include/linux/soc/mediatek/mtk_wed.h
-+++ b/include/linux/soc/mediatek/mtk_wed.h
-@@ -151,6 +151,8 @@ struct mtk_wed_device {
- void (*release_rx_buf)(struct mtk_wed_device *wed);
- void (*update_wo_rx_stats)(struct mtk_wed_device *wed,
- struct mtk_wed_wo_rx_stats *stats);
-+ int (*reset)(struct mtk_wed_device *wed);
-+ void (*reset_complete)(struct mtk_wed_device *wed);
- } wlan;
- #endif
- };
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Mon, 5 Dec 2022 12:34:42 +0100
-Subject: [PATCH] net: ethernet: mtk_wed: add reset to rx_ring_setup callback
-
-This patch adds reset parameter to mtk_wed_rx_ring_setup signature
-in order to align rx_ring_setup callback to tx_ring_setup one introduced
-in 'commit 23dca7a90017 ("net: ethernet: mtk_wed: add reset to
-tx_ring_setup callback")'
-
-Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Reviewed-by: Leon Romanovsky <leonro@nvidia.com>
-Link: https://lore.kernel.org/r/29c6e7a5469e784406cf3e2920351d1207713d05.1670239984.git.lorenzo@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -1252,7 +1252,8 @@ mtk_wed_wdma_rx_ring_setup(struct mtk_we
- }
-
- static int
--mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev, int idx, int size)
-+mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev, int idx, int size,
-+ bool reset)
- {
- u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version;
- struct mtk_wed_ring *wdma;
-@@ -1261,8 +1262,8 @@ mtk_wed_wdma_tx_ring_setup(struct mtk_we
- return -EINVAL;
-
- wdma = &dev->tx_wdma[idx];
-- if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, desc_size,
-- true))
-+ if (!reset && mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE,
-+ desc_size, true))
- return -ENOMEM;
-
- wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_BASE,
-@@ -1272,6 +1273,9 @@ mtk_wed_wdma_tx_ring_setup(struct mtk_we
- wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_CPU_IDX, 0);
- wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_DMA_IDX, 0);
-
-+ if (reset)
-+ mtk_wed_ring_reset(wdma, MTK_WED_WDMA_RING_SIZE, true);
-+
- if (!idx) {
- wed_w32(dev, MTK_WED_WDMA_RING_TX + MTK_WED_RING_OFS_BASE,
- wdma->desc_phys);
-@@ -1611,18 +1615,20 @@ mtk_wed_txfree_ring_setup(struct mtk_wed
- }
-
- static int
--mtk_wed_rx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs)
-+mtk_wed_rx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs,
-+ bool reset)
- {
- struct mtk_wed_ring *ring = &dev->rx_ring[idx];
-
- if (WARN_ON(idx >= ARRAY_SIZE(dev->rx_ring)))
- return -EINVAL;
-
-- if (mtk_wed_ring_alloc(dev, ring, MTK_WED_RX_RING_SIZE,
-- sizeof(*ring->desc), false))
-+ if (!reset && mtk_wed_ring_alloc(dev, ring, MTK_WED_RX_RING_SIZE,
-+ sizeof(*ring->desc), false))
- return -ENOMEM;
-
-- if (mtk_wed_wdma_tx_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE))
-+ if (mtk_wed_wdma_tx_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE,
-+ reset))
- return -ENOMEM;
-
- ring->reg_base = MTK_WED_RING_RX_DATA(idx);
---- a/include/linux/soc/mediatek/mtk_wed.h
-+++ b/include/linux/soc/mediatek/mtk_wed.h
-@@ -162,7 +162,7 @@ struct mtk_wed_ops {
- int (*tx_ring_setup)(struct mtk_wed_device *dev, int ring,
- void __iomem *regs, bool reset);
- int (*rx_ring_setup)(struct mtk_wed_device *dev, int ring,
-- void __iomem *regs);
-+ void __iomem *regs, bool reset);
- int (*txfree_ring_setup)(struct mtk_wed_device *dev,
- void __iomem *regs);
- int (*msg_update)(struct mtk_wed_device *dev, int cmd_id,
-@@ -230,8 +230,8 @@ mtk_wed_get_rx_capa(struct mtk_wed_devic
- (_dev)->ops->irq_get(_dev, _mask)
- #define mtk_wed_device_irq_set_mask(_dev, _mask) \
- (_dev)->ops->irq_set_mask(_dev, _mask)
--#define mtk_wed_device_rx_ring_setup(_dev, _ring, _regs) \
-- (_dev)->ops->rx_ring_setup(_dev, _ring, _regs)
-+#define mtk_wed_device_rx_ring_setup(_dev, _ring, _regs, _reset) \
-+ (_dev)->ops->rx_ring_setup(_dev, _ring, _regs, _reset)
- #define mtk_wed_device_ppe_check(_dev, _skb, _reason, _hash) \
- (_dev)->ops->ppe_check(_dev, _skb, _reason, _hash)
- #define mtk_wed_device_update_msg(_dev, _id, _msg, _len) \
-@@ -251,7 +251,7 @@ static inline bool mtk_wed_device_active
- #define mtk_wed_device_reg_write(_dev, _reg, _val) do {} while (0)
- #define mtk_wed_device_irq_get(_dev, _mask) 0
- #define mtk_wed_device_irq_set_mask(_dev, _mask) do {} while (0)
--#define mtk_wed_device_rx_ring_setup(_dev, _ring, _regs) -ENODEV
-+#define mtk_wed_device_rx_ring_setup(_dev, _ring, _regs, _reset) -ENODEV
- #define mtk_wed_device_ppe_check(_dev, _skb, _reason, _hash) do {} while (0)
- #define mtk_wed_device_update_msg(_dev, _id, _msg, _len) -ENODEV
- #define mtk_wed_device_stop(_dev) do {} while (0)
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Thu, 27 Oct 2022 19:50:31 +0200
-Subject: [PATCH] net: ethernet: mtk_eth_soc: account for vlan in rx
- header length
-
-The network stack assumes that devices can handle an extra VLAN tag without
-increasing the MTU
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -29,7 +29,7 @@
- #define MTK_TX_DMA_BUF_LEN_V2 0xffff
- #define MTK_DMA_SIZE 512
- #define MTK_MAC_COUNT 2
--#define MTK_RX_ETH_HLEN (ETH_HLEN + ETH_FCS_LEN)
-+#define MTK_RX_ETH_HLEN (VLAN_ETH_HLEN + ETH_FCS_LEN)
- #define MTK_RX_HLEN (NET_SKB_PAD + MTK_RX_ETH_HLEN + NET_IP_ALIGN)
- #define MTK_DMA_DUMMY_DESC 0xffffffff
- #define MTK_DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | \
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Thu, 27 Oct 2022 19:53:57 +0200
-Subject: [PATCH] net: ethernet: mtk_eth_soc: increase tx ring side for
- QDMA devices
-
-In order to use the hardware traffic shaper feature, a larger tx ring is
-needed, especially for the scratch ring, which the hardware shaper uses to
-reorder packets.
-
-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
-@@ -944,7 +944,7 @@ static int mtk_init_fq_dma(struct mtk_et
- {
- const struct mtk_soc_data *soc = eth->soc;
- dma_addr_t phy_ring_tail;
-- int cnt = MTK_DMA_SIZE;
-+ int cnt = MTK_QDMA_RING_SIZE;
- dma_addr_t dma_addr;
- int i;
-
-@@ -2208,19 +2208,25 @@ static int mtk_tx_alloc(struct mtk_eth *
- struct mtk_tx_ring *ring = ð->tx_ring;
- int i, sz = soc->txrx.txd_size;
- struct mtk_tx_dma_v2 *txd;
-+ int ring_size;
-
-- ring->buf = kcalloc(MTK_DMA_SIZE, sizeof(*ring->buf),
-+ if (MTK_HAS_CAPS(soc->caps, MTK_QDMA))
-+ ring_size = MTK_QDMA_RING_SIZE;
-+ else
-+ ring_size = MTK_DMA_SIZE;
-+
-+ ring->buf = kcalloc(ring_size, sizeof(*ring->buf),
- GFP_KERNEL);
- if (!ring->buf)
- goto no_tx_mem;
-
-- ring->dma = dma_alloc_coherent(eth->dma_dev, MTK_DMA_SIZE * sz,
-+ ring->dma = dma_alloc_coherent(eth->dma_dev, ring_size * sz,
- &ring->phys, GFP_KERNEL);
- if (!ring->dma)
- goto no_tx_mem;
-
-- for (i = 0; i < MTK_DMA_SIZE; i++) {
-- int next = (i + 1) % MTK_DMA_SIZE;
-+ for (i = 0; i < ring_size; i++) {
-+ int next = (i + 1) % ring_size;
- u32 next_ptr = ring->phys + next * sz;
-
- txd = ring->dma + i * sz;
-@@ -2240,22 +2246,22 @@ static int mtk_tx_alloc(struct mtk_eth *
- * descriptors in ring->dma_pdma.
- */
- if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) {
-- ring->dma_pdma = dma_alloc_coherent(eth->dma_dev, MTK_DMA_SIZE * sz,
-+ ring->dma_pdma = dma_alloc_coherent(eth->dma_dev, ring_size * sz,
- &ring->phys_pdma, GFP_KERNEL);
- if (!ring->dma_pdma)
- goto no_tx_mem;
-
-- for (i = 0; i < MTK_DMA_SIZE; i++) {
-+ for (i = 0; i < ring_size; i++) {
- ring->dma_pdma[i].txd2 = TX_DMA_DESP2_DEF;
- ring->dma_pdma[i].txd4 = 0;
- }
- }
-
-- ring->dma_size = MTK_DMA_SIZE;
-- atomic_set(&ring->free_count, MTK_DMA_SIZE - 2);
-+ ring->dma_size = ring_size;
-+ atomic_set(&ring->free_count, ring_size - 2);
- ring->next_free = ring->dma;
- ring->last_free = (void *)txd;
-- ring->last_free_ptr = (u32)(ring->phys + ((MTK_DMA_SIZE - 1) * sz));
-+ ring->last_free_ptr = (u32)(ring->phys + ((ring_size - 1) * sz));
- ring->thresh = MAX_SKB_FRAGS;
-
- /* make sure that all changes to the dma ring are flushed before we
-@@ -2267,14 +2273,14 @@ static int mtk_tx_alloc(struct mtk_eth *
- mtk_w32(eth, ring->phys, soc->reg_map->qdma.ctx_ptr);
- mtk_w32(eth, ring->phys, soc->reg_map->qdma.dtx_ptr);
- mtk_w32(eth,
-- ring->phys + ((MTK_DMA_SIZE - 1) * sz),
-+ ring->phys + ((ring_size - 1) * sz),
- soc->reg_map->qdma.crx_ptr);
- mtk_w32(eth, ring->last_free_ptr, soc->reg_map->qdma.drx_ptr);
- mtk_w32(eth, (QDMA_RES_THRES << 8) | QDMA_RES_THRES,
- soc->reg_map->qdma.qtx_cfg);
- } else {
- mtk_w32(eth, ring->phys_pdma, MT7628_TX_BASE_PTR0);
-- mtk_w32(eth, MTK_DMA_SIZE, MT7628_TX_MAX_CNT0);
-+ mtk_w32(eth, ring_size, MT7628_TX_MAX_CNT0);
- mtk_w32(eth, 0, MT7628_TX_CTX_IDX0);
- mtk_w32(eth, MT7628_PST_DTX_IDX0, soc->reg_map->pdma.rst_idx);
- }
-@@ -2292,7 +2298,7 @@ static void mtk_tx_clean(struct mtk_eth
- int i;
-
- if (ring->buf) {
-- for (i = 0; i < MTK_DMA_SIZE; i++)
-+ for (i = 0; i < ring->dma_size; i++)
- mtk_tx_unmap(eth, &ring->buf[i], NULL, false);
- kfree(ring->buf);
- ring->buf = NULL;
-@@ -2300,14 +2306,14 @@ static void mtk_tx_clean(struct mtk_eth
-
- if (ring->dma) {
- dma_free_coherent(eth->dma_dev,
-- MTK_DMA_SIZE * soc->txrx.txd_size,
-+ ring->dma_size * soc->txrx.txd_size,
- ring->dma, ring->phys);
- ring->dma = NULL;
- }
-
- if (ring->dma_pdma) {
- dma_free_coherent(eth->dma_dev,
-- MTK_DMA_SIZE * soc->txrx.txd_size,
-+ ring->dma_size * soc->txrx.txd_size,
- ring->dma_pdma, ring->phys_pdma);
- ring->dma_pdma = NULL;
- }
-@@ -2832,7 +2838,7 @@ static void mtk_dma_free(struct mtk_eth
- netdev_reset_queue(eth->netdev[i]);
- if (eth->scratch_ring) {
- dma_free_coherent(eth->dma_dev,
-- MTK_DMA_SIZE * soc->txrx.txd_size,
-+ MTK_QDMA_RING_SIZE * soc->txrx.txd_size,
- eth->scratch_ring, eth->phy_scratch_ring);
- eth->scratch_ring = NULL;
- eth->phy_scratch_ring = 0;
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -27,6 +27,7 @@
- #define MTK_MAX_RX_LENGTH_2K 2048
- #define MTK_TX_DMA_BUF_LEN 0x3fff
- #define MTK_TX_DMA_BUF_LEN_V2 0xffff
-+#define MTK_QDMA_RING_SIZE 2048
- #define MTK_DMA_SIZE 512
- #define MTK_MAC_COUNT 2
- #define MTK_RX_ETH_HLEN (VLAN_ETH_HLEN + ETH_FCS_LEN)
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Fri, 4 Nov 2022 19:49:08 +0100
-Subject: [PATCH] net: ethernet: mtk_eth_soc: avoid port_mg assignment on
- MT7622 and newer
-
-On newer chips, this field is unused and contains some bits related to queue
-assignment. Initialize it to 0 in those cases.
-Fix offload_version on MT7621 and MT7623, which still need the previous value.
-
-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
-@@ -4479,7 +4479,7 @@ static const struct mtk_soc_data mt7621_
- .hw_features = MTK_HW_FEATURES,
- .required_clks = MT7621_CLKS_BITMAP,
- .required_pctl = false,
-- .offload_version = 2,
-+ .offload_version = 1,
- .hash_offset = 2,
- .foe_entry_size = sizeof(struct mtk_foe_entry) - 16,
- .txrx = {
-@@ -4518,7 +4518,7 @@ static const struct mtk_soc_data mt7623_
- .hw_features = MTK_HW_FEATURES,
- .required_clks = MT7623_CLKS_BITMAP,
- .required_pctl = true,
-- .offload_version = 2,
-+ .offload_version = 1,
- .hash_offset = 2,
- .foe_entry_size = sizeof(struct mtk_foe_entry) - 16,
- .txrx = {
---- a/drivers/net/ethernet/mediatek/mtk_ppe.c
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
-@@ -175,6 +175,8 @@ int mtk_foe_entry_prepare(struct mtk_eth
- val = FIELD_PREP(MTK_FOE_IB2_DEST_PORT_V2, pse_port) |
- FIELD_PREP(MTK_FOE_IB2_PORT_AG_V2, 0xf);
- } else {
-+ int port_mg = eth->soc->offload_version > 1 ? 0 : 0x3f;
-+
- val = FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_BIND) |
- FIELD_PREP(MTK_FOE_IB1_PACKET_TYPE, type) |
- FIELD_PREP(MTK_FOE_IB1_UDP, l4proto == IPPROTO_UDP) |
-@@ -182,7 +184,7 @@ int mtk_foe_entry_prepare(struct mtk_eth
- entry->ib1 = val;
-
- val = FIELD_PREP(MTK_FOE_IB2_DEST_PORT, pse_port) |
-- FIELD_PREP(MTK_FOE_IB2_PORT_MG, 0x3f) |
-+ FIELD_PREP(MTK_FOE_IB2_PORT_MG, port_mg) |
- FIELD_PREP(MTK_FOE_IB2_PORT_AG, 0x1f);
- }
-
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Thu, 27 Oct 2022 20:17:27 +0200
-Subject: [PATCH] net: ethernet: mtk_eth_soc: implement multi-queue
- support for per-port queues
-
-When sending traffic to multiple ports with different link speeds, queued
-packets to one port can drown out tx to other ports.
-In order to better handle transmission to multiple ports, use the hardware
-shaper feature to implement weighted fair queueing between ports.
-Weight and maximum rate are automatically adjusted based on the link speed
-of the port.
-The first 3 queues are unrestricted and reserved for non-DSA direct tx on
-GMAC ports. The following queues are automatically assigned by the MTK DSA
-tag driver based on the target port number.
-The PPE offload code configures the queues for offloaded traffic in the same
-way.
-This feature is only supported on devices supporting QDMA. All queues still
-share the same DMA ring and descriptor pool.
-
-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
-@@ -55,6 +55,7 @@ static const struct mtk_reg_map mtk_reg_
- },
- .qdma = {
- .qtx_cfg = 0x1800,
-+ .qtx_sch = 0x1804,
- .rx_ptr = 0x1900,
- .rx_cnt_cfg = 0x1904,
- .qcrx_ptr = 0x1908,
-@@ -62,6 +63,7 @@ static const struct mtk_reg_map mtk_reg_
- .rst_idx = 0x1a08,
- .delay_irq = 0x1a0c,
- .fc_th = 0x1a10,
-+ .tx_sch_rate = 0x1a14,
- .int_grp = 0x1a20,
- .hred = 0x1a44,
- .ctx_ptr = 0x1b00,
-@@ -117,6 +119,7 @@ static const struct mtk_reg_map mt7986_r
- },
- .qdma = {
- .qtx_cfg = 0x4400,
-+ .qtx_sch = 0x4404,
- .rx_ptr = 0x4500,
- .rx_cnt_cfg = 0x4504,
- .qcrx_ptr = 0x4508,
-@@ -134,6 +137,7 @@ static const struct mtk_reg_map mt7986_r
- .fq_tail = 0x4724,
- .fq_count = 0x4728,
- .fq_blen = 0x472c,
-+ .tx_sch_rate = 0x4798,
- },
- .gdm1_cnt = 0x1c00,
- .gdma_to_ppe = 0x3333,
-@@ -620,6 +624,75 @@ static void mtk_mac_link_down(struct phy
- mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id));
- }
-
-+static void mtk_set_queue_speed(struct mtk_eth *eth, unsigned int idx,
-+ int speed)
-+{
-+ const struct mtk_soc_data *soc = eth->soc;
-+ u32 ofs, val;
-+
-+ if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA))
-+ return;
-+
-+ val = MTK_QTX_SCH_MIN_RATE_EN |
-+ /* minimum: 10 Mbps */
-+ FIELD_PREP(MTK_QTX_SCH_MIN_RATE_MAN, 1) |
-+ FIELD_PREP(MTK_QTX_SCH_MIN_RATE_EXP, 4) |
-+ MTK_QTX_SCH_LEAKY_BUCKET_SIZE;
-+ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-+ val |= MTK_QTX_SCH_LEAKY_BUCKET_EN;
-+
-+ if (IS_ENABLED(CONFIG_SOC_MT7621)) {
-+ switch (speed) {
-+ case SPEED_10:
-+ val |= MTK_QTX_SCH_MAX_RATE_EN |
-+ FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, 103) |
-+ FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP, 2) |
-+ FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, 1);
-+ break;
-+ case SPEED_100:
-+ val |= MTK_QTX_SCH_MAX_RATE_EN |
-+ FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, 103) |
-+ FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP, 3);
-+ FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, 1);
-+ break;
-+ case SPEED_1000:
-+ val |= MTK_QTX_SCH_MAX_RATE_EN |
-+ FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, 105) |
-+ FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP, 4) |
-+ FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, 10);
-+ break;
-+ default:
-+ break;
-+ }
-+ } else {
-+ switch (speed) {
-+ case SPEED_10:
-+ val |= MTK_QTX_SCH_MAX_RATE_EN |
-+ FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, 1) |
-+ FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP, 4) |
-+ FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, 1);
-+ break;
-+ case SPEED_100:
-+ val |= MTK_QTX_SCH_MAX_RATE_EN |
-+ FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, 1) |
-+ FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP, 5);
-+ FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, 1);
-+ break;
-+ case SPEED_1000:
-+ val |= MTK_QTX_SCH_MAX_RATE_EN |
-+ FIELD_PREP(MTK_QTX_SCH_MAX_RATE_MAN, 10) |
-+ FIELD_PREP(MTK_QTX_SCH_MAX_RATE_EXP, 5) |
-+ FIELD_PREP(MTK_QTX_SCH_MAX_RATE_WEIGHT, 10);
-+ break;
-+ default:
-+ break;
-+ }
-+ }
-+
-+ ofs = MTK_QTX_OFFSET * idx;
-+ mtk_w32(eth, val, soc->reg_map->qdma.qtx_sch + ofs);
-+}
-+
- static void mtk_mac_link_up(struct phylink_config *config,
- struct phy_device *phy,
- unsigned int mode, phy_interface_t interface,
-@@ -645,6 +718,8 @@ static void mtk_mac_link_up(struct phyli
- break;
- }
-
-+ mtk_set_queue_speed(mac->hw, mac->id, speed);
-+
- /* Configure duplex */
- if (duplex == DUPLEX_FULL)
- mcr |= MAC_MCR_FORCE_DPX;
-@@ -1105,7 +1180,8 @@ static void mtk_tx_set_dma_desc_v1(struc
-
- WRITE_ONCE(desc->txd1, info->addr);
-
-- data = TX_DMA_SWC | TX_DMA_PLEN0(info->size);
-+ data = TX_DMA_SWC | TX_DMA_PLEN0(info->size) |
-+ FIELD_PREP(TX_DMA_PQID, info->qid);
- if (info->last)
- data |= TX_DMA_LS0;
- WRITE_ONCE(desc->txd3, data);
-@@ -1139,9 +1215,6 @@ static void mtk_tx_set_dma_desc_v2(struc
- data |= TX_DMA_LS0;
- WRITE_ONCE(desc->txd3, data);
-
-- if (!info->qid && mac->id)
-- info->qid = MTK_QDMA_GMAC2_QID;
--
- data = (mac->id + 1) << TX_DMA_FPORT_SHIFT_V2; /* forward port */
- data |= TX_DMA_SWC_V2 | QID_BITS_V2(info->qid);
- WRITE_ONCE(desc->txd4, data);
-@@ -1185,11 +1258,12 @@ static int mtk_tx_map(struct sk_buff *sk
- .gso = gso,
- .csum = skb->ip_summed == CHECKSUM_PARTIAL,
- .vlan = skb_vlan_tag_present(skb),
-- .qid = skb->mark & MTK_QDMA_TX_MASK,
-+ .qid = skb_get_queue_mapping(skb),
- .vlan_tci = skb_vlan_tag_get(skb),
- .first = true,
- .last = !skb_is_nonlinear(skb),
- };
-+ struct netdev_queue *txq;
- struct mtk_mac *mac = netdev_priv(dev);
- struct mtk_eth *eth = mac->hw;
- const struct mtk_soc_data *soc = eth->soc;
-@@ -1197,8 +1271,10 @@ static int mtk_tx_map(struct sk_buff *sk
- struct mtk_tx_dma *itxd_pdma, *txd_pdma;
- struct mtk_tx_buf *itx_buf, *tx_buf;
- int i, n_desc = 1;
-+ int queue = skb_get_queue_mapping(skb);
- int k = 0;
-
-+ txq = netdev_get_tx_queue(dev, queue);
- itxd = ring->next_free;
- itxd_pdma = qdma_to_pdma(ring, itxd);
- if (itxd == ring->last_free)
-@@ -1247,7 +1323,7 @@ static int mtk_tx_map(struct sk_buff *sk
- memset(&txd_info, 0, sizeof(struct mtk_tx_dma_desc_info));
- txd_info.size = min_t(unsigned int, frag_size,
- soc->txrx.dma_max_len);
-- txd_info.qid = skb->mark & MTK_QDMA_TX_MASK;
-+ txd_info.qid = queue;
- txd_info.last = i == skb_shinfo(skb)->nr_frags - 1 &&
- !(frag_size - txd_info.size);
- txd_info.addr = skb_frag_dma_map(eth->dma_dev, frag,
-@@ -1286,7 +1362,7 @@ static int mtk_tx_map(struct sk_buff *sk
- txd_pdma->txd2 |= TX_DMA_LS1;
- }
-
-- netdev_sent_queue(dev, skb->len);
-+ netdev_tx_sent_queue(txq, skb->len);
- skb_tx_timestamp(skb);
-
- ring->next_free = mtk_qdma_phys_to_virt(ring, txd->txd2);
-@@ -1298,8 +1374,7 @@ static int mtk_tx_map(struct sk_buff *sk
- wmb();
-
- if (MTK_HAS_CAPS(soc->caps, MTK_QDMA)) {
-- if (netif_xmit_stopped(netdev_get_tx_queue(dev, 0)) ||
-- !netdev_xmit_more())
-+ if (netif_xmit_stopped(txq) || !netdev_xmit_more())
- mtk_w32(eth, txd->txd2, soc->reg_map->qdma.ctx_ptr);
- } else {
- int next_idx;
-@@ -1368,7 +1443,7 @@ static void mtk_wake_queue(struct mtk_et
- for (i = 0; i < MTK_MAC_COUNT; i++) {
- if (!eth->netdev[i])
- continue;
-- netif_wake_queue(eth->netdev[i]);
-+ netif_tx_wake_all_queues(eth->netdev[i]);
- }
- }
-
-@@ -1392,7 +1467,7 @@ static netdev_tx_t mtk_start_xmit(struct
-
- tx_num = mtk_cal_txd_req(eth, skb);
- if (unlikely(atomic_read(&ring->free_count) <= tx_num)) {
-- netif_stop_queue(dev);
-+ netif_tx_stop_all_queues(dev);
- netif_err(eth, tx_queued, dev,
- "Tx Ring full when queue awake!\n");
- spin_unlock(ð->page_lock);
-@@ -1418,7 +1493,7 @@ static netdev_tx_t mtk_start_xmit(struct
- goto drop;
-
- if (unlikely(atomic_read(&ring->free_count) <= ring->thresh))
-- netif_stop_queue(dev);
-+ netif_tx_stop_all_queues(dev);
-
- spin_unlock(ð->page_lock);
-
-@@ -1585,10 +1660,12 @@ static int mtk_xdp_submit_frame(struct m
- struct skb_shared_info *sinfo = xdp_get_shared_info_from_frame(xdpf);
- const struct mtk_soc_data *soc = eth->soc;
- struct mtk_tx_ring *ring = ð->tx_ring;
-+ struct mtk_mac *mac = netdev_priv(dev);
- struct mtk_tx_dma_desc_info txd_info = {
- .size = xdpf->len,
- .first = true,
- .last = !xdp_frame_has_frags(xdpf),
-+ .qid = mac->id,
- };
- int err, index = 0, n_desc = 1, nr_frags;
- struct mtk_tx_buf *htx_buf, *tx_buf;
-@@ -1638,6 +1715,7 @@ static int mtk_xdp_submit_frame(struct m
- memset(&txd_info, 0, sizeof(struct mtk_tx_dma_desc_info));
- txd_info.size = skb_frag_size(&sinfo->frags[index]);
- txd_info.last = index + 1 == nr_frags;
-+ txd_info.qid = mac->id;
- data = skb_frag_address(&sinfo->frags[index]);
-
- index++;
-@@ -1992,8 +2070,46 @@ rx_done:
- return done;
- }
-
-+struct mtk_poll_state {
-+ struct netdev_queue *txq;
-+ unsigned int total;
-+ unsigned int done;
-+ unsigned int bytes;
-+};
-+
-+static void
-+mtk_poll_tx_done(struct mtk_eth *eth, struct mtk_poll_state *state, u8 mac,
-+ struct sk_buff *skb)
-+{
-+ struct netdev_queue *txq;
-+ struct net_device *dev;
-+ unsigned int bytes = skb->len;
-+
-+ state->total++;
-+ eth->tx_packets++;
-+ eth->tx_bytes += bytes;
-+
-+ dev = eth->netdev[mac];
-+ if (!dev)
-+ return;
-+
-+ txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb));
-+ if (state->txq == txq) {
-+ state->done++;
-+ state->bytes += bytes;
-+ return;
-+ }
-+
-+ if (state->txq)
-+ netdev_tx_completed_queue(state->txq, state->done, state->bytes);
-+
-+ state->txq = txq;
-+ state->done = 1;
-+ state->bytes = bytes;
-+}
-+
- static int mtk_poll_tx_qdma(struct mtk_eth *eth, int budget,
-- unsigned int *done, unsigned int *bytes)
-+ struct mtk_poll_state *state)
- {
- const struct mtk_reg_map *reg_map = eth->soc->reg_map;
- struct mtk_tx_ring *ring = ð->tx_ring;
-@@ -2025,12 +2141,9 @@ static int mtk_poll_tx_qdma(struct mtk_e
- break;
-
- if (tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) {
-- if (tx_buf->type == MTK_TYPE_SKB) {
-- struct sk_buff *skb = tx_buf->data;
-+ if (tx_buf->type == MTK_TYPE_SKB)
-+ mtk_poll_tx_done(eth, state, mac, tx_buf->data);
-
-- bytes[mac] += skb->len;
-- done[mac]++;
-- }
- budget--;
- }
- mtk_tx_unmap(eth, tx_buf, &bq, true);
-@@ -2049,7 +2162,7 @@ static int mtk_poll_tx_qdma(struct mtk_e
- }
-
- static int mtk_poll_tx_pdma(struct mtk_eth *eth, int budget,
-- unsigned int *done, unsigned int *bytes)
-+ struct mtk_poll_state *state)
- {
- struct mtk_tx_ring *ring = ð->tx_ring;
- struct mtk_tx_buf *tx_buf;
-@@ -2067,12 +2180,8 @@ static int mtk_poll_tx_pdma(struct mtk_e
- break;
-
- if (tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) {
-- if (tx_buf->type == MTK_TYPE_SKB) {
-- struct sk_buff *skb = tx_buf->data;
--
-- bytes[0] += skb->len;
-- done[0]++;
-- }
-+ if (tx_buf->type == MTK_TYPE_SKB)
-+ mtk_poll_tx_done(eth, state, 0, tx_buf->data);
- budget--;
- }
- mtk_tx_unmap(eth, tx_buf, &bq, true);
-@@ -2094,26 +2203,15 @@ static int mtk_poll_tx(struct mtk_eth *e
- {
- struct mtk_tx_ring *ring = ð->tx_ring;
- struct dim_sample dim_sample = {};
-- unsigned int done[MTK_MAX_DEVS];
-- unsigned int bytes[MTK_MAX_DEVS];
-- int total = 0, i;
--
-- memset(done, 0, sizeof(done));
-- memset(bytes, 0, sizeof(bytes));
-+ struct mtk_poll_state state = {};
-
- if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
-- budget = mtk_poll_tx_qdma(eth, budget, done, bytes);
-+ budget = mtk_poll_tx_qdma(eth, budget, &state);
- else
-- budget = mtk_poll_tx_pdma(eth, budget, done, bytes);
-+ budget = mtk_poll_tx_pdma(eth, budget, &state);
-
-- for (i = 0; i < MTK_MAC_COUNT; i++) {
-- if (!eth->netdev[i] || !done[i])
-- continue;
-- netdev_completed_queue(eth->netdev[i], done[i], bytes[i]);
-- total += done[i];
-- eth->tx_packets += done[i];
-- eth->tx_bytes += bytes[i];
-- }
-+ if (state.txq)
-+ netdev_tx_completed_queue(state.txq, state.done, state.bytes);
-
- dim_update_sample(eth->tx_events, eth->tx_packets, eth->tx_bytes,
- &dim_sample);
-@@ -2123,7 +2221,7 @@ static int mtk_poll_tx(struct mtk_eth *e
- (atomic_read(&ring->free_count) > ring->thresh))
- mtk_wake_queue(eth);
-
-- return total;
-+ return state.total;
- }
-
- static void mtk_handle_status_irq(struct mtk_eth *eth)
-@@ -2209,6 +2307,7 @@ static int mtk_tx_alloc(struct mtk_eth *
- int i, sz = soc->txrx.txd_size;
- struct mtk_tx_dma_v2 *txd;
- int ring_size;
-+ u32 ofs, val;
-
- if (MTK_HAS_CAPS(soc->caps, MTK_QDMA))
- ring_size = MTK_QDMA_RING_SIZE;
-@@ -2276,8 +2375,25 @@ static int mtk_tx_alloc(struct mtk_eth *
- ring->phys + ((ring_size - 1) * sz),
- soc->reg_map->qdma.crx_ptr);
- mtk_w32(eth, ring->last_free_ptr, soc->reg_map->qdma.drx_ptr);
-- mtk_w32(eth, (QDMA_RES_THRES << 8) | QDMA_RES_THRES,
-- soc->reg_map->qdma.qtx_cfg);
-+
-+ for (i = 0, ofs = 0; i < MTK_QDMA_NUM_QUEUES; i++) {
-+ val = (QDMA_RES_THRES << 8) | QDMA_RES_THRES;
-+ mtk_w32(eth, val, soc->reg_map->qdma.qtx_cfg + ofs);
-+
-+ val = MTK_QTX_SCH_MIN_RATE_EN |
-+ /* minimum: 10 Mbps */
-+ FIELD_PREP(MTK_QTX_SCH_MIN_RATE_MAN, 1) |
-+ FIELD_PREP(MTK_QTX_SCH_MIN_RATE_EXP, 4) |
-+ MTK_QTX_SCH_LEAKY_BUCKET_SIZE;
-+ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-+ val |= MTK_QTX_SCH_LEAKY_BUCKET_EN;
-+ mtk_w32(eth, val, soc->reg_map->qdma.qtx_sch + ofs);
-+ ofs += MTK_QTX_OFFSET;
-+ }
-+ val = MTK_QDMA_TX_SCH_MAX_WFQ | (MTK_QDMA_TX_SCH_MAX_WFQ << 16);
-+ mtk_w32(eth, val, soc->reg_map->qdma.tx_sch_rate);
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-+ mtk_w32(eth, val, soc->reg_map->qdma.tx_sch_rate + 4);
- } else {
- mtk_w32(eth, ring->phys_pdma, MT7628_TX_BASE_PTR0);
- mtk_w32(eth, ring_size, MT7628_TX_MAX_CNT0);
-@@ -2962,7 +3078,7 @@ static int mtk_start_dma(struct mtk_eth
- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
- val |= MTK_MUTLI_CNT | MTK_RESV_BUF |
- MTK_WCOMP_EN | MTK_DMAD_WR_WDONE |
-- MTK_CHK_DDONE_EN;
-+ MTK_CHK_DDONE_EN | MTK_LEAKY_BUCKET_EN;
- else
- val |= MTK_RX_BT_32DWORDS;
- mtk_w32(eth, val, reg_map->qdma.glo_cfg);
-@@ -3008,6 +3124,45 @@ static void mtk_gdm_config(struct mtk_et
- mtk_w32(eth, 0, MTK_RST_GL);
- }
-
-+static int mtk_device_event(struct notifier_block *n, unsigned long event, void *ptr)
-+{
-+ struct mtk_mac *mac = container_of(n, struct mtk_mac, device_notifier);
-+ struct mtk_eth *eth = mac->hw;
-+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
-+ struct ethtool_link_ksettings s;
-+ struct net_device *ldev;
-+ struct list_head *iter;
-+ struct dsa_port *dp;
-+
-+ if (event != NETDEV_CHANGE)
-+ return NOTIFY_DONE;
-+
-+ netdev_for_each_lower_dev(dev, ldev, iter) {
-+ if (netdev_priv(ldev) == mac)
-+ goto found;
-+ }
-+
-+ return NOTIFY_DONE;
-+
-+found:
-+ if (!dsa_slave_dev_check(dev))
-+ return NOTIFY_DONE;
-+
-+ if (__ethtool_get_link_ksettings(dev, &s))
-+ return NOTIFY_DONE;
-+
-+ if (s.base.speed == 0 || s.base.speed == ((__u32)-1))
-+ return NOTIFY_DONE;
-+
-+ dp = dsa_port_from_netdev(dev);
-+ if (dp->index >= MTK_QDMA_NUM_QUEUES)
-+ return NOTIFY_DONE;
-+
-+ mtk_set_queue_speed(eth, dp->index + 3, s.base.speed);
-+
-+ return NOTIFY_DONE;
-+}
-+
- static int mtk_open(struct net_device *dev)
- {
- struct mtk_mac *mac = netdev_priv(dev);
-@@ -3050,7 +3205,8 @@ static int mtk_open(struct net_device *d
- refcount_inc(ð->dma_refcnt);
-
- phylink_start(mac->phylink);
-- netif_start_queue(dev);
-+ netif_tx_start_all_queues(dev);
-+
- return 0;
- }
-
-@@ -3759,8 +3915,12 @@ static int mtk_unreg_dev(struct mtk_eth
- int i;
-
- for (i = 0; i < MTK_MAC_COUNT; i++) {
-+ struct mtk_mac *mac;
- if (!eth->netdev[i])
- continue;
-+ mac = netdev_priv(eth->netdev[i]);
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
-+ unregister_netdevice_notifier(&mac->device_notifier);
- unregister_netdev(eth->netdev[i]);
- }
-
-@@ -3977,6 +4137,23 @@ static int mtk_set_rxnfc(struct net_devi
- return ret;
- }
-
-+static u16 mtk_select_queue(struct net_device *dev, struct sk_buff *skb,
-+ struct net_device *sb_dev)
-+{
-+ struct mtk_mac *mac = netdev_priv(dev);
-+ unsigned int queue = 0;
-+
-+ if (netdev_uses_dsa(dev))
-+ queue = skb_get_queue_mapping(skb) + 3;
-+ else
-+ queue = mac->id;
-+
-+ if (queue >= dev->num_tx_queues)
-+ queue = 0;
-+
-+ return queue;
-+}
-+
- static const struct ethtool_ops mtk_ethtool_ops = {
- .get_link_ksettings = mtk_get_link_ksettings,
- .set_link_ksettings = mtk_set_link_ksettings,
-@@ -4011,6 +4188,7 @@ static const struct net_device_ops mtk_n
- .ndo_setup_tc = mtk_eth_setup_tc,
- .ndo_bpf = mtk_xdp,
- .ndo_xdp_xmit = mtk_xdp_xmit,
-+ .ndo_select_queue = mtk_select_queue,
- };
-
- static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
-@@ -4020,6 +4198,7 @@ static int mtk_add_mac(struct mtk_eth *e
- struct phylink *phylink;
- struct mtk_mac *mac;
- int id, err;
-+ int txqs = 1;
-
- if (!_id) {
- dev_err(eth->dev, "missing mac id\n");
-@@ -4037,7 +4216,10 @@ static int mtk_add_mac(struct mtk_eth *e
- return -EINVAL;
- }
-
-- eth->netdev[id] = alloc_etherdev(sizeof(*mac));
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
-+ txqs = MTK_QDMA_NUM_QUEUES;
-+
-+ eth->netdev[id] = alloc_etherdev_mqs(sizeof(*mac), txqs, 1);
- if (!eth->netdev[id]) {
- dev_err(eth->dev, "alloc_etherdev failed\n");
- return -ENOMEM;
-@@ -4145,6 +4327,11 @@ static int mtk_add_mac(struct mtk_eth *e
- else
- eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH_2K - MTK_RX_ETH_HLEN;
-
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
-+ mac->device_notifier.notifier_call = mtk_device_event;
-+ register_netdevice_notifier(&mac->device_notifier);
-+ }
-+
- return 0;
-
- free_netdev:
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -22,6 +22,7 @@
- #include <linux/bpf_trace.h>
- #include "mtk_ppe.h"
-
-+#define MTK_QDMA_NUM_QUEUES 16
- #define MTK_QDMA_PAGE_SIZE 2048
- #define MTK_MAX_RX_LENGTH 1536
- #define MTK_MAX_RX_LENGTH_2K 2048
-@@ -216,8 +217,26 @@
- #define MTK_RING_MAX_AGG_CNT_H ((MTK_HW_LRO_MAX_AGG_CNT >> 6) & 0x3)
-
- /* QDMA TX Queue Configuration Registers */
-+#define MTK_QTX_OFFSET 0x10
- #define QDMA_RES_THRES 4
-
-+/* QDMA Tx Queue Scheduler Configuration Registers */
-+#define MTK_QTX_SCH_TX_SEL BIT(31)
-+#define MTK_QTX_SCH_TX_SEL_V2 GENMASK(31, 30)
-+
-+#define MTK_QTX_SCH_LEAKY_BUCKET_EN BIT(30)
-+#define MTK_QTX_SCH_LEAKY_BUCKET_SIZE GENMASK(29, 28)
-+#define MTK_QTX_SCH_MIN_RATE_EN BIT(27)
-+#define MTK_QTX_SCH_MIN_RATE_MAN GENMASK(26, 20)
-+#define MTK_QTX_SCH_MIN_RATE_EXP GENMASK(19, 16)
-+#define MTK_QTX_SCH_MAX_RATE_WEIGHT GENMASK(15, 12)
-+#define MTK_QTX_SCH_MAX_RATE_EN BIT(11)
-+#define MTK_QTX_SCH_MAX_RATE_MAN GENMASK(10, 4)
-+#define MTK_QTX_SCH_MAX_RATE_EXP GENMASK(3, 0)
-+
-+/* QDMA TX Scheduler Rate Control Register */
-+#define MTK_QDMA_TX_SCH_MAX_WFQ BIT(15)
-+
- /* QDMA Global Configuration Register */
- #define MTK_RX_2B_OFFSET BIT(31)
- #define MTK_RX_BT_32DWORDS (3 << 11)
-@@ -236,6 +255,7 @@
- #define MTK_WCOMP_EN BIT(24)
- #define MTK_RESV_BUF (0x40 << 16)
- #define MTK_MUTLI_CNT (0x4 << 12)
-+#define MTK_LEAKY_BUCKET_EN BIT(11)
-
- /* QDMA Flow Control Register */
- #define FC_THRES_DROP_MODE BIT(20)
-@@ -266,8 +286,6 @@
- #define MTK_STAT_OFFSET 0x40
-
- /* QDMA TX NUM */
--#define MTK_QDMA_TX_NUM 16
--#define MTK_QDMA_TX_MASK (MTK_QDMA_TX_NUM - 1)
- #define QID_BITS_V2(x) (((x) & 0x3f) << 16)
- #define MTK_QDMA_GMAC2_QID 8
-
-@@ -297,6 +315,7 @@
- #define TX_DMA_PLEN0(x) (((x) & eth->soc->txrx.dma_max_len) << eth->soc->txrx.dma_len_offset)
- #define TX_DMA_PLEN1(x) ((x) & eth->soc->txrx.dma_max_len)
- #define TX_DMA_SWC BIT(14)
-+#define TX_DMA_PQID GENMASK(3, 0)
-
- /* PDMA on MT7628 */
- #define TX_DMA_DONE BIT(31)
-@@ -957,6 +976,7 @@ struct mtk_reg_map {
- } pdma;
- struct {
- u32 qtx_cfg; /* tx queue configuration */
-+ u32 qtx_sch; /* tx queue scheduler configuration */
- u32 rx_ptr; /* rx base pointer */
- u32 rx_cnt_cfg; /* rx max count configuration */
- u32 qcrx_ptr; /* rx cpu pointer */
-@@ -974,6 +994,7 @@ struct mtk_reg_map {
- u32 fq_tail; /* fq tail pointer */
- u32 fq_count; /* fq free page count */
- u32 fq_blen; /* fq free page buffer length */
-+ u32 tx_sch_rate; /* tx scheduler rate control registers */
- } qdma;
- u32 gdm1_cnt;
- u32 gdma_to_ppe;
-@@ -1177,6 +1198,7 @@ struct mtk_mac {
- __be32 hwlro_ip[MTK_MAX_LRO_IP_CNT];
- int hwlro_ip_cnt;
- unsigned int syscfg0;
-+ struct notifier_block device_notifier;
- };
-
- /* the struct describing the SoC. these are declared in the soc_xyz.c files */
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Fri, 28 Oct 2022 18:16:03 +0200
-Subject: [PATCH] net: dsa: tag_mtk: assign per-port queues
-
-Keeps traffic sent to the switch within link speed limits
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/dsa/tag_mtk.c
-+++ b/net/dsa/tag_mtk.c
-@@ -25,6 +25,8 @@ static struct sk_buff *mtk_tag_xmit(stru
- u8 xmit_tpid;
- u8 *mtk_tag;
-
-+ skb_set_queue_mapping(skb, dp->index);
-+
- /* Build the special tag after the MAC Source Address. If VLAN header
- * is present, it's required that VLAN header and special tag is
- * being combined. Only in this way we can allow the switch can parse
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Thu, 3 Nov 2022 17:49:44 +0100
-Subject: [PATCH] net: ethernet: mediatek: ppe: assign per-port queues
- for offloaded traffic
-
-Keeps traffic sent to the switch within link speed limits
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_ppe.c
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
-@@ -399,6 +399,24 @@ int mtk_foe_entry_set_wdma(struct mtk_et
- return 0;
- }
-
-+int mtk_foe_entry_set_queue(struct mtk_eth *eth, struct mtk_foe_entry *entry,
-+ unsigned int queue)
-+{
-+ u32 *ib2 = mtk_foe_entry_ib2(eth, entry);
-+
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
-+ *ib2 &= ~MTK_FOE_IB2_QID_V2;
-+ *ib2 |= FIELD_PREP(MTK_FOE_IB2_QID_V2, queue);
-+ *ib2 |= MTK_FOE_IB2_PSE_QOS_V2;
-+ } else {
-+ *ib2 &= ~MTK_FOE_IB2_QID;
-+ *ib2 |= FIELD_PREP(MTK_FOE_IB2_QID, queue);
-+ *ib2 |= MTK_FOE_IB2_PSE_QOS;
-+ }
-+
-+ return 0;
-+}
-+
- static bool
- mtk_flow_entry_match(struct mtk_eth *eth, struct mtk_flow_entry *entry,
- struct mtk_foe_entry *data)
---- a/drivers/net/ethernet/mediatek/mtk_ppe.h
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
-@@ -68,7 +68,9 @@ enum {
- #define MTK_FOE_IB2_DSCP GENMASK(31, 24)
-
- /* CONFIG_MEDIATEK_NETSYS_V2 */
-+#define MTK_FOE_IB2_QID_V2 GENMASK(6, 0)
- #define MTK_FOE_IB2_PORT_MG_V2 BIT(7)
-+#define MTK_FOE_IB2_PSE_QOS_V2 BIT(8)
- #define MTK_FOE_IB2_DEST_PORT_V2 GENMASK(12, 9)
- #define MTK_FOE_IB2_MULTICAST_V2 BIT(13)
- #define MTK_FOE_IB2_WDMA_WINFO_V2 BIT(19)
-@@ -351,6 +353,8 @@ int mtk_foe_entry_set_pppoe(struct mtk_e
- int sid);
- int mtk_foe_entry_set_wdma(struct mtk_eth *eth, struct mtk_foe_entry *entry,
- int wdma_idx, int txq, int bss, int wcid);
-+int mtk_foe_entry_set_queue(struct mtk_eth *eth, struct mtk_foe_entry *entry,
-+ unsigned int queue);
- 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);
---- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-@@ -188,7 +188,7 @@ mtk_flow_set_output_device(struct mtk_et
- int *wed_index)
- {
- struct mtk_wdma_info info = {};
-- int pse_port, dsa_port;
-+ int pse_port, dsa_port, queue;
-
- if (mtk_flow_get_wdma_info(dev, dest_mac, &info) == 0) {
- mtk_foe_entry_set_wdma(eth, foe, info.wdma_idx, info.queue,
-@@ -212,8 +212,6 @@ mtk_flow_set_output_device(struct mtk_et
- }
-
- dsa_port = mtk_flow_get_dsa_port(&dev);
-- if (dsa_port >= 0)
-- mtk_foe_entry_set_dsa(eth, foe, dsa_port);
-
- if (dev == eth->netdev[0])
- pse_port = 1;
-@@ -222,6 +220,14 @@ mtk_flow_set_output_device(struct mtk_et
- else
- return -EOPNOTSUPP;
-
-+ if (dsa_port >= 0) {
-+ mtk_foe_entry_set_dsa(eth, foe, dsa_port);
-+ queue = 3 + dsa_port;
-+ } else {
-+ queue = pse_port - 1;
-+ }
-+ mtk_foe_entry_set_queue(eth, foe, queue);
-+
- out:
- mtk_foe_entry_set_pse_port(eth, foe, pse_port);
-
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Tue, 8 Nov 2022 15:03:15 +0100
-Subject: [PATCH] net: dsa: add support for DSA rx offloading via
- metadata dst
-
-If a metadata dst is present with the type METADATA_HW_PORT_MUX on a dsa cpu
-port netdev, assume that it carries the port number and that there is no DSA
-tag present in the skb data.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/core/flow_dissector.c
-+++ b/net/core/flow_dissector.c
-@@ -971,12 +971,14 @@ bool __skb_flow_dissect(const struct net
- #if IS_ENABLED(CONFIG_NET_DSA)
- if (unlikely(skb->dev && netdev_uses_dsa(skb->dev) &&
- proto == htons(ETH_P_XDSA))) {
-+ struct metadata_dst *md_dst = skb_metadata_dst(skb);
- const struct dsa_device_ops *ops;
- int offset = 0;
-
- ops = skb->dev->dsa_ptr->tag_ops;
- /* Only DSA header taggers break flow dissection */
-- if (ops->needed_headroom) {
-+ if (ops->needed_headroom &&
-+ (!md_dst || md_dst->type != METADATA_HW_PORT_MUX)) {
- if (ops->flow_dissect)
- ops->flow_dissect(skb, &proto, &offset);
- else
---- a/net/dsa/dsa.c
-+++ b/net/dsa/dsa.c
-@@ -11,6 +11,7 @@
- #include <linux/netdevice.h>
- #include <linux/sysfs.h>
- #include <linux/ptp_classify.h>
-+#include <net/dst_metadata.h>
-
- #include "dsa_priv.h"
-
-@@ -216,6 +217,7 @@ static bool dsa_skb_defer_rx_timestamp(s
- static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev,
- struct packet_type *pt, struct net_device *unused)
- {
-+ struct metadata_dst *md_dst = skb_metadata_dst(skb);
- struct dsa_port *cpu_dp = dev->dsa_ptr;
- struct sk_buff *nskb = NULL;
- struct dsa_slave_priv *p;
-@@ -229,7 +231,22 @@ static int dsa_switch_rcv(struct sk_buff
- if (!skb)
- return 0;
-
-- nskb = cpu_dp->rcv(skb, dev);
-+ if (md_dst && md_dst->type == METADATA_HW_PORT_MUX) {
-+ unsigned int port = md_dst->u.port_info.port_id;
-+
-+ skb_dst_drop(skb);
-+ if (!skb_has_extensions(skb))
-+ skb->slow_gro = 0;
-+
-+ skb->dev = dsa_master_find_slave(dev, 0, port);
-+ if (likely(skb->dev)) {
-+ dsa_default_offload_fwd_mark(skb);
-+ nskb = skb;
-+ }
-+ } else {
-+ nskb = cpu_dp->rcv(skb, dev);
-+ }
-+
- if (!nskb) {
- kfree_skb(skb);
- return 0;
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Fri, 28 Oct 2022 11:01:12 +0200
-Subject: [PATCH] net: ethernet: mtk_eth_soc: fix VLAN rx hardware
- acceleration
-
-- enable VLAN untagging for PDMA rx
-- make it possible to disable the feature via ethtool
-- pass VLAN tag to the DSA driver
-- untag special tag on PDMA only if no non-DSA devices are in use
-- disable special tag untagging on 7986 for now, since it's not working yet
-
-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
-@@ -23,6 +23,7 @@
- #include <linux/jhash.h>
- #include <linux/bitfield.h>
- #include <net/dsa.h>
-+#include <net/dst_metadata.h>
-
- #include "mtk_eth_soc.h"
- #include "mtk_wed.h"
-@@ -2021,16 +2022,22 @@ static int mtk_poll_rx(struct napi_struc
- htons(RX_DMA_VPID(trxd.rxd4)),
- RX_DMA_VID(trxd.rxd4));
- } else if (trxd.rxd2 & RX_DMA_VTAG) {
-- __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
-+ __vlan_hwaccel_put_tag(skb, htons(RX_DMA_VPID(trxd.rxd3)),
- RX_DMA_VID(trxd.rxd3));
- }
-+ }
-+
-+ /* When using VLAN untagging in combination with DSA, the
-+ * hardware treats the MTK special tag as a VLAN and untags it.
-+ */
-+ if (skb_vlan_tag_present(skb) && netdev_uses_dsa(netdev)) {
-+ unsigned int port = ntohs(skb->vlan_proto) & GENMASK(2, 0);
-
-- /* If the device is attached to a dsa switch, the special
-- * tag inserted in VLAN field by hw switch can * be offloaded
-- * by RX HW VLAN offload. Clear vlan info.
-- */
-- if (netdev_uses_dsa(netdev))
-- __vlan_hwaccel_clear_tag(skb);
-+ if (port < ARRAY_SIZE(eth->dsa_meta) &&
-+ eth->dsa_meta[port])
-+ skb_dst_set_noref(skb, ð->dsa_meta[port]->dst);
-+
-+ __vlan_hwaccel_clear_tag(skb);
- }
-
- skb_record_rx_queue(skb, 0);
-@@ -2858,15 +2865,30 @@ static netdev_features_t mtk_fix_feature
-
- static int mtk_set_features(struct net_device *dev, netdev_features_t features)
- {
-- int err = 0;
-+ struct mtk_mac *mac = netdev_priv(dev);
-+ struct mtk_eth *eth = mac->hw;
-+ netdev_features_t diff = dev->features ^ features;
-+ int i;
-+
-+ if ((diff & NETIF_F_LRO) && !(features & NETIF_F_LRO))
-+ mtk_hwlro_netdev_disable(dev);
-
-- if (!((dev->features ^ features) & NETIF_F_LRO))
-+ /* Set RX VLAN offloading */
-+ if (!(diff & NETIF_F_HW_VLAN_CTAG_RX))
- return 0;
-
-- if (!(features & NETIF_F_LRO))
-- mtk_hwlro_netdev_disable(dev);
-+ mtk_w32(eth, !!(features & NETIF_F_HW_VLAN_CTAG_RX),
-+ MTK_CDMP_EG_CTRL);
-
-- return err;
-+ /* sync features with other MAC */
-+ for (i = 0; i < MTK_MAC_COUNT; i++) {
-+ if (!eth->netdev[i] || eth->netdev[i] == dev)
-+ continue;
-+ eth->netdev[i]->features &= ~NETIF_F_HW_VLAN_CTAG_RX;
-+ eth->netdev[i]->features |= features & NETIF_F_HW_VLAN_CTAG_RX;
-+ }
-+
-+ return 0;
- }
-
- /* wait for DMA to finish whatever it is doing before we start using it again */
-@@ -3163,11 +3185,45 @@ found:
- return NOTIFY_DONE;
- }
-
-+static bool mtk_uses_dsa(struct net_device *dev)
-+{
-+#if IS_ENABLED(CONFIG_NET_DSA)
-+ return netdev_uses_dsa(dev) &&
-+ dev->dsa_ptr->tag_ops->proto == DSA_TAG_PROTO_MTK;
-+#else
-+ return false;
-+#endif
-+}
-+
- static int mtk_open(struct net_device *dev)
- {
- struct mtk_mac *mac = netdev_priv(dev);
- struct mtk_eth *eth = mac->hw;
-- int err;
-+ int i, err;
-+
-+ if (mtk_uses_dsa(dev) && !eth->prog) {
-+ for (i = 0; i < ARRAY_SIZE(eth->dsa_meta); i++) {
-+ struct metadata_dst *md_dst = eth->dsa_meta[i];
-+
-+ if (md_dst)
-+ continue;
-+
-+ md_dst = metadata_dst_alloc(0, METADATA_HW_PORT_MUX,
-+ GFP_KERNEL);
-+ if (!md_dst)
-+ return -ENOMEM;
-+
-+ md_dst->u.port_info.port_id = i;
-+ eth->dsa_meta[i] = md_dst;
-+ }
-+ } else {
-+ /* Hardware special tag parsing needs to be disabled if at least
-+ * one MAC does not use DSA.
-+ */
-+ u32 val = mtk_r32(eth, MTK_CDMP_IG_CTRL);
-+ val &= ~MTK_CDMP_STAG_EN;
-+ mtk_w32(eth, val, MTK_CDMP_IG_CTRL);
-+ }
-
- err = phylink_of_phy_connect(mac->phylink, mac->of_node, 0);
- if (err) {
-@@ -3688,6 +3744,10 @@ static int mtk_hw_init(struct mtk_eth *e
- */
- val = mtk_r32(eth, MTK_CDMQ_IG_CTRL);
- mtk_w32(eth, val | MTK_CDMQ_STAG_EN, MTK_CDMQ_IG_CTRL);
-+ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
-+ val = mtk_r32(eth, MTK_CDMP_IG_CTRL);
-+ mtk_w32(eth, val | MTK_CDMP_STAG_EN, MTK_CDMP_IG_CTRL);
-+ }
-
- /* Enable RX VLan Offloading */
- mtk_w32(eth, 1, MTK_CDMP_EG_CTRL);
-@@ -3907,6 +3967,12 @@ static int mtk_free_dev(struct mtk_eth *
- free_netdev(eth->netdev[i]);
- }
-
-+ for (i = 0; i < ARRAY_SIZE(eth->dsa_meta); i++) {
-+ if (!eth->dsa_meta[i])
-+ break;
-+ metadata_dst_free(eth->dsa_meta[i]);
-+ }
-+
- return 0;
- }
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -22,6 +22,9 @@
- #include <linux/bpf_trace.h>
- #include "mtk_ppe.h"
-
-+#define MTK_MAX_DSA_PORTS 7
-+#define MTK_DSA_PORT_MASK GENMASK(2, 0)
-+
- #define MTK_QDMA_NUM_QUEUES 16
- #define MTK_QDMA_PAGE_SIZE 2048
- #define MTK_MAX_RX_LENGTH 1536
-@@ -105,6 +108,9 @@
- #define MTK_CDMQ_IG_CTRL 0x1400
- #define MTK_CDMQ_STAG_EN BIT(0)
-
-+/* CDMQ Exgress Control Register */
-+#define MTK_CDMQ_EG_CTRL 0x1404
-+
- /* CDMP Ingress Control Register */
- #define MTK_CDMP_IG_CTRL 0x400
- #define MTK_CDMP_STAG_EN BIT(0)
-@@ -1164,6 +1170,8 @@ struct mtk_eth {
-
- int ip_align;
-
-+ struct metadata_dst *dsa_meta[MTK_MAX_DSA_PORTS];
-+
- struct mtk_ppe *ppe[2];
- struct rhashtable flow_table;
-
+++ /dev/null
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Sat, 28 Jan 2023 12:42:32 +0300
-Subject: [PATCH] net: ethernet: mtk_eth_soc: disable hardware DSA untagging
- for second MAC
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-According to my tests on MT7621AT and MT7623NI SoCs, hardware DSA untagging
-won't work on the second MAC. Therefore, disable this feature when the
-second MAC of the MT7621 and MT7623 SoCs is being used.
-
-Fixes: 2d7605a72906 ("net: ethernet: mtk_eth_soc: enable hardware DSA untagging")
-Link: https://lore.kernel.org/netdev/6249fc14-b38a-c770-36b4-5af6d41c21d3@arinc9.com/
-Tested-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Link: https://lore.kernel.org/r/20230128094232.2451947-1-arinc.unal@arinc9.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -3201,7 +3201,8 @@ static int mtk_open(struct net_device *d
- struct mtk_eth *eth = mac->hw;
- int i, err;
-
-- if (mtk_uses_dsa(dev) && !eth->prog) {
-+ if ((mtk_uses_dsa(dev) && !eth->prog) &&
-+ !(mac->id == 1 && MTK_HAS_CAPS(eth->soc->caps, MTK_GMAC1_TRGMII))) {
- for (i = 0; i < ARRAY_SIZE(eth->dsa_meta); i++) {
- struct metadata_dst *md_dst = eth->dsa_meta[i];
-
-@@ -3218,7 +3219,8 @@ static int mtk_open(struct net_device *d
- }
- } else {
- /* Hardware special tag parsing needs to be disabled if at least
-- * one MAC does not use DSA.
-+ * one MAC does not use DSA, or the second MAC of the MT7621 and
-+ * MT7623 SoCs is being used.
- */
- u32 val = mtk_r32(eth, MTK_CDMP_IG_CTRL);
- val &= ~MTK_CDMP_STAG_EN;
+++ /dev/null
-From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
-Date: Sun, 5 Feb 2023 20:53:31 +0300
-Subject: [PATCH] net: ethernet: mtk_eth_soc: enable special tag when any MAC
- uses DSA
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The special tag is only enabled when the first MAC uses DSA. However, it
-must be enabled when any MAC uses DSA. Change the check accordingly.
-
-This fixes hardware DSA untagging not working on the second MAC of the
-MT7621 and MT7623 SoCs, and likely other SoCs too. Therefore, remove the
-check that disables hardware DSA untagging for the second MAC of the MT7621
-and MT7623 SoCs.
-
-Fixes: a1f47752fd62 ("net: ethernet: mtk_eth_soc: disable hardware DSA untagging for second MAC")
-Co-developed-by: Richard van Schagen <richard@routerhints.com>
-Signed-off-by: Richard van Schagen <richard@routerhints.com>
-Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -3136,7 +3136,7 @@ static void mtk_gdm_config(struct mtk_et
-
- val |= config;
-
-- if (!i && eth->netdev[0] && netdev_uses_dsa(eth->netdev[0]))
-+ if (eth->netdev[i] && netdev_uses_dsa(eth->netdev[i]))
- val |= MTK_GDMA_SPECIAL_TAG;
-
- mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i));
-@@ -3201,8 +3201,7 @@ static int mtk_open(struct net_device *d
- struct mtk_eth *eth = mac->hw;
- int i, err;
-
-- if ((mtk_uses_dsa(dev) && !eth->prog) &&
-- !(mac->id == 1 && MTK_HAS_CAPS(eth->soc->caps, MTK_GMAC1_TRGMII))) {
-+ if (mtk_uses_dsa(dev) && !eth->prog) {
- for (i = 0; i < ARRAY_SIZE(eth->dsa_meta); i++) {
- struct metadata_dst *md_dst = eth->dsa_meta[i];
-
-@@ -3219,8 +3218,7 @@ static int mtk_open(struct net_device *d
- }
- } else {
- /* Hardware special tag parsing needs to be disabled if at least
-- * one MAC does not use DSA, or the second MAC of the MT7621 and
-- * MT7623 SoCs is being used.
-+ * one MAC does not use DSA.
- */
- u32 val = mtk_r32(eth, MTK_CDMP_IG_CTRL);
- val &= ~MTK_CDMP_STAG_EN;
+++ /dev/null
-From: Vladimir Oltean <vladimir.oltean@nxp.com>
-Date: Tue, 7 Feb 2023 12:30:27 +0200
-Subject: [PATCH] net: ethernet: mtk_eth_soc: fix DSA TX tag hwaccel for switch
- port 0
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Arınç reports that on his MT7621AT Unielec U7621-06 board and MT7623NI
-Bananapi BPI-R2, packets received by the CPU over mt7530 switch port 0
-(of which this driver acts as the DSA master) are not processed
-correctly by software. More precisely, they arrive without a DSA tag
-(in packet or in the hwaccel area - skb_metadata_dst()), so DSA cannot
-demux them towards the switch's interface for port 0. Traffic from other
-ports receives a skb_metadata_dst() with the correct port and is demuxed
-properly.
-
-Looking at mtk_poll_rx(), it becomes apparent that this driver uses the
-skb vlan hwaccel area:
-
- union {
- u32 vlan_all;
- struct {
- __be16 vlan_proto;
- __u16 vlan_tci;
- };
- };
-
-as a temporary storage for the VLAN hwaccel tag, or the DSA hwaccel tag.
-If this is a DSA master it's a DSA hwaccel tag, and finally clears up
-the skb VLAN hwaccel header.
-
-I'm guessing that the problem is the (mis)use of API.
-skb_vlan_tag_present() looks like this:
-
- #define skb_vlan_tag_present(__skb) (!!(__skb)->vlan_all)
-
-So if both vlan_proto and vlan_tci are zeroes, skb_vlan_tag_present()
-returns precisely false. I don't know for sure what is the format of the
-DSA hwaccel tag, but I surely know that lowermost 3 bits of vlan_proto
-are 0 when receiving from port 0:
-
- unsigned int port = vlan_proto & GENMASK(2, 0);
-
-If the RX descriptor has no other bits set to non-zero values in
-RX_DMA_VTAG, then the call to __vlan_hwaccel_put_tag() will not, in
-fact, make the subsequent skb_vlan_tag_present() return true, because
-it's implemented like this:
-
-static inline void __vlan_hwaccel_put_tag(struct sk_buff *skb,
- __be16 vlan_proto, u16 vlan_tci)
-{
- skb->vlan_proto = vlan_proto;
- skb->vlan_tci = vlan_tci;
-}
-
-What we need to do to fix this problem (assuming this is the problem) is
-to stop using skb->vlan_all as temporary storage for driver affairs, and
-just create some local variables that serve the same purpose, but
-hopefully better. Instead of calling skb_vlan_tag_present(), let's look
-at a boolean has_hwaccel_tag which we set to true when the RX DMA
-descriptors have something. Disambiguate based on netdev_uses_dsa()
-whether this is a VLAN or DSA hwaccel tag, and only call
-__vlan_hwaccel_put_tag() if we're certain it's a VLAN tag.
-
-Arınç confirms that the treatment works, so this validates the
-assumption.
-
-Link: https://lore.kernel.org/netdev/704f3a72-fc9e-714a-db54-272e17612637@arinc9.com/
-Fixes: 2d7605a72906 ("net: ethernet: mtk_eth_soc: enable hardware DSA untagging")
-Reported-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Tested-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
-Reviewed-by: Felix Fietkau <nbd@nbd.name>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -1877,7 +1877,9 @@ static int mtk_poll_rx(struct napi_struc
-
- while (done < budget) {
- unsigned int pktlen, *rxdcsum;
-+ bool has_hwaccel_tag = false;
- struct net_device *netdev;
-+ u16 vlan_proto, vlan_tci;
- dma_addr_t dma_addr;
- u32 hash, reason;
- int mac = 0;
-@@ -2017,27 +2019,29 @@ static int mtk_poll_rx(struct napi_struc
-
- if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX) {
- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
-- if (trxd.rxd3 & RX_DMA_VTAG_V2)
-- __vlan_hwaccel_put_tag(skb,
-- htons(RX_DMA_VPID(trxd.rxd4)),
-- RX_DMA_VID(trxd.rxd4));
-+ if (trxd.rxd3 & RX_DMA_VTAG_V2) {
-+ vlan_proto = RX_DMA_VPID(trxd.rxd4);
-+ vlan_tci = RX_DMA_VID(trxd.rxd4);
-+ has_hwaccel_tag = true;
-+ }
- } else if (trxd.rxd2 & RX_DMA_VTAG) {
-- __vlan_hwaccel_put_tag(skb, htons(RX_DMA_VPID(trxd.rxd3)),
-- RX_DMA_VID(trxd.rxd3));
-+ vlan_proto = RX_DMA_VPID(trxd.rxd3);
-+ vlan_tci = RX_DMA_VID(trxd.rxd3);
-+ has_hwaccel_tag = true;
- }
- }
-
- /* When using VLAN untagging in combination with DSA, the
- * hardware treats the MTK special tag as a VLAN and untags it.
- */
-- if (skb_vlan_tag_present(skb) && netdev_uses_dsa(netdev)) {
-- unsigned int port = ntohs(skb->vlan_proto) & GENMASK(2, 0);
-+ if (has_hwaccel_tag && netdev_uses_dsa(netdev)) {
-+ unsigned int port = vlan_proto & GENMASK(2, 0);
-
- if (port < ARRAY_SIZE(eth->dsa_meta) &&
- eth->dsa_meta[port])
- skb_dst_set_noref(skb, ð->dsa_meta[port]->dst);
--
-- __vlan_hwaccel_clear_tag(skb);
-+ } else if (has_hwaccel_tag) {
-+ __vlan_hwaccel_put_tag(skb, htons(vlan_proto), vlan_tci);
- }
-
- skb_record_rx_queue(skb, 0);
+++ /dev/null
-From: Christophe JAILLET <christophe.jaillet@wanadoo.fr>
-Date: Sun, 12 Feb 2023 07:51:51 +0100
-Subject: [PATCH] net: ethernet: mtk_wed: No need to clear memory after a
- dma_alloc_coherent() call
-
-dma_alloc_coherent() already clears the allocated memory, there is no need
-to explicitly call memset().
-
-Moreover, it is likely that the size in the memset() is incorrect and
-should be "size * sizeof(*ring->desc)".
-
-Signed-off-by: Christophe JAILLET <christophe.jaillet@wanadoo.fr>
-Link: https://lore.kernel.org/r/d5acce7dd108887832c9719f62c7201b4c83b3fb.1676184599.git.christophe.jaillet@wanadoo.fr
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -779,7 +779,6 @@ mtk_wed_rro_ring_alloc(struct mtk_wed_de
-
- ring->desc_size = sizeof(*ring->desc);
- ring->size = size;
-- memset(ring->desc, 0, size);
-
- return 0;
- }
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Wed, 7 Dec 2022 15:04:54 +0100
-Subject: [PATCH] net: ethernet: mtk_wed: fix some possible NULL pointer
- dereferences
-
-Fix possible NULL pointer dereference in mtk_wed_detach routine checking
-wo pointer is properly allocated before running mtk_wed_wo_reset() and
-mtk_wed_wo_deinit().
-Even if it is just a theoretical issue at the moment check wo pointer is
-not NULL in mtk_wed_mcu_msg_update.
-Moreover, honor mtk_wed_mcu_send_msg return value in mtk_wed_wo_reset()
-
-Fixes: 799684448e3e ("net: ethernet: mtk_wed: introduce wed wo support")
-Fixes: 4c5de09eb0d0 ("net: ethernet: mtk_wed: add configure wed wo support")
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Reviewed-by: Leon Romanovsky <leonro@nvidia.com>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -174,9 +174,10 @@ mtk_wed_wo_reset(struct mtk_wed_device *
- mtk_wdma_tx_reset(dev);
- mtk_wed_reset(dev, MTK_WED_RESET_WED);
-
-- mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO,
-- MTK_WED_WO_CMD_CHANGE_STATE, &state,
-- sizeof(state), false);
-+ if (mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO,
-+ MTK_WED_WO_CMD_CHANGE_STATE, &state,
-+ sizeof(state), false))
-+ return;
-
- if (readx_poll_timeout(mtk_wed_wo_read_status, dev, val,
- val == MTK_WED_WOIF_DISABLE_DONE,
-@@ -632,9 +633,11 @@ mtk_wed_detach(struct mtk_wed_device *de
- mtk_wed_free_tx_rings(dev);
-
- if (mtk_wed_get_rx_capa(dev)) {
-- mtk_wed_wo_reset(dev);
-+ if (hw->wed_wo)
-+ mtk_wed_wo_reset(dev);
- mtk_wed_free_rx_rings(dev);
-- mtk_wed_wo_deinit(hw);
-+ if (hw->wed_wo)
-+ mtk_wed_wo_deinit(hw);
- }
-
- if (dev->wlan.bus_type == MTK_WED_BUS_PCIE) {
---- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
-@@ -207,6 +207,9 @@ int mtk_wed_mcu_msg_update(struct mtk_we
- if (dev->hw->version == 1)
- return 0;
-
-+ if (WARN_ON(!wo))
-+ return -ENODEV;
-+
- return mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO, id, data, len,
- true);
- }
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Wed, 7 Dec 2022 15:04:55 +0100
-Subject: [PATCH] net: ethernet: mtk_wed: fix possible deadlock if
- mtk_wed_wo_init fails
-
-Introduce __mtk_wed_detach() in order to avoid a deadlock in
-mtk_wed_attach routine if mtk_wed_wo_init fails since both
-mtk_wed_attach and mtk_wed_detach run holding hw_lock mutex.
-
-Fixes: 4c5de09eb0d0 ("net: ethernet: mtk_wed: add configure wed wo support")
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Reviewed-by: Leon Romanovsky <leonro@nvidia.com>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -619,12 +619,10 @@ mtk_wed_deinit(struct mtk_wed_device *de
- }
-
- static void
--mtk_wed_detach(struct mtk_wed_device *dev)
-+__mtk_wed_detach(struct mtk_wed_device *dev)
- {
- struct mtk_wed_hw *hw = dev->hw;
-
-- mutex_lock(&hw_lock);
--
- mtk_wed_deinit(dev);
-
- mtk_wdma_rx_reset(dev);
-@@ -657,6 +655,13 @@ mtk_wed_detach(struct mtk_wed_device *de
- module_put(THIS_MODULE);
-
- hw->wed_dev = NULL;
-+}
-+
-+static void
-+mtk_wed_detach(struct mtk_wed_device *dev)
-+{
-+ mutex_lock(&hw_lock);
-+ __mtk_wed_detach(dev);
- mutex_unlock(&hw_lock);
- }
-
-@@ -1538,8 +1543,10 @@ mtk_wed_attach(struct mtk_wed_device *de
- ret = mtk_wed_wo_init(hw);
- }
- out:
-- if (ret)
-- mtk_wed_detach(dev);
-+ if (ret) {
-+ dev_err(dev->hw->dev, "failed to attach wed device\n");
-+ __mtk_wed_detach(dev);
-+ }
- unlock:
- mutex_unlock(&hw_lock);
-
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Fri, 24 Mar 2023 14:56:58 +0100
-Subject: [PATCH] net: ethernet: mtk_eth_soc: fix tx throughput regression with
- direct 1G links
-
-Using the QDMA tx scheduler to throttle tx to line speed works fine for
-switch ports, but apparently caused a regression on non-switch ports.
-
-Based on a number of tests, it seems that this throttling can be safely
-dropped without re-introducing the issues on switch ports that the
-tx scheduling changes resolved.
-
-Link: https://lore.kernel.org/netdev/trinity-92c3826f-c2c8-40af-8339-bc6d0d3ffea4-1678213958520@3c-app-gmx-bs16/
-Fixes: f63959c7eec3 ("net: ethernet: mtk_eth_soc: implement multi-queue support for per-port queues")
-Reported-by: Frank Wunderlich <frank-w@public-files.de>
-Reported-by: Daniel Golle <daniel@makrotopia.org>
-Tested-by: Daniel Golle <daniel@makrotopia.org>
-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
-@@ -719,8 +719,6 @@ static void mtk_mac_link_up(struct phyli
- break;
- }
-
-- mtk_set_queue_speed(mac->hw, mac->id, speed);
--
- /* Configure duplex */
- if (duplex == DUPLEX_FULL)
- mcr |= MAC_MCR_FORCE_DPX;
+++ /dev/null
-From b6a709cb51f7bdc55c01cec886098a9753ce8c28 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Thu, 27 Oct 2022 14:10:42 +0100
-Subject: [PATCH 01/10] net: mtk_eth_soc: add definitions for PCS
-
-As a result of help from Frank Wunderlich to investigate and test, we
-know a bit more about the PCS on the Mediatek platforms. Update the
-definitions from this investigation.
-
-This PCS appears similar, but not identical to the Lynx PCS.
-
-Although not included in this patch, but for future reference, the PHY
-ID registers at offset 4 read as 0x4d544950 'MTIP'.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 13 ++++++++++---
- 1 file changed, 10 insertions(+), 3 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -504,8 +504,10 @@
- #define ETHSYS_DMA_AG_MAP_PPE BIT(2)
-
- /* SGMII subsystem config registers */
--/* Register to auto-negotiation restart */
-+/* BMCR (low 16) BMSR (high 16) */
- #define SGMSYS_PCS_CONTROL_1 0x0
-+#define SGMII_BMCR GENMASK(15, 0)
-+#define SGMII_BMSR GENMASK(31, 16)
- #define SGMII_AN_RESTART BIT(9)
- #define SGMII_ISOLATE BIT(10)
- #define SGMII_AN_ENABLE BIT(12)
-@@ -515,13 +517,18 @@
- #define SGMII_PCS_FAULT BIT(23)
- #define SGMII_AN_EXPANSION_CLR BIT(30)
-
-+#define SGMSYS_PCS_ADVERTISE 0x8
-+#define SGMII_ADVERTISE GENMASK(15, 0)
-+#define SGMII_LPA GENMASK(31, 16)
-+
- /* Register to programmable link timer, the unit in 2 * 8ns */
- #define SGMSYS_PCS_LINK_TIMER 0x18
--#define SGMII_LINK_TIMER_DEFAULT (0x186a0 & GENMASK(19, 0))
-+#define SGMII_LINK_TIMER_MASK GENMASK(19, 0)
-+#define SGMII_LINK_TIMER_DEFAULT (0x186a0 & SGMII_LINK_TIMER_MASK)
-
- /* Register to control remote fault */
- #define SGMSYS_SGMII_MODE 0x20
--#define SGMII_IF_MODE_BIT0 BIT(0)
-+#define SGMII_IF_MODE_SGMII BIT(0)
- #define SGMII_SPEED_DUPLEX_AN BIT(1)
- #define SGMII_SPEED_MASK GENMASK(3, 2)
- #define SGMII_SPEED_10 FIELD_PREP(SGMII_SPEED_MASK, 0)
+++ /dev/null
-From 5cf7797526ee81bea0f627bccaa3d887f48f53e0 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Thu, 27 Oct 2022 14:10:47 +0100
-Subject: [PATCH 02/10] net: mtk_eth_soc: eliminate unnecessary error handling
-
-The functions called by the pcs_config() method always return zero, so
-there is no point trying to handle an error from these functions. Make
-these functions void, eliminate the "err" variable and simply return
-zero from the pcs_config() function itself.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_sgmii.c | 18 ++++++------------
- 1 file changed, 6 insertions(+), 12 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
-+++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
-@@ -20,7 +20,7 @@ static struct mtk_pcs *pcs_to_mtk_pcs(st
- }
-
- /* For SGMII interface mode */
--static int mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs)
-+static void mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs)
- {
- unsigned int val;
-
-@@ -39,16 +39,13 @@ static int mtk_pcs_setup_mode_an(struct
- regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
- val &= ~SGMII_PHYA_PWD;
- regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val);
--
-- return 0;
--
- }
-
- /* For 1000BASE-X and 2500BASE-X interface modes, which operate at a
- * fixed speed.
- */
--static int mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs,
-- phy_interface_t interface)
-+static void mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs,
-+ phy_interface_t interface)
- {
- unsigned int val;
-
-@@ -73,8 +70,6 @@ static int mtk_pcs_setup_mode_force(stru
- regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
- val &= ~SGMII_PHYA_PWD;
- regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val);
--
-- return 0;
- }
-
- static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
-@@ -83,15 +78,14 @@ static int mtk_pcs_config(struct phylink
- bool permit_pause_to_mac)
- {
- struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
-- int err = 0;
-
- /* Setup SGMIISYS with the determined property */
- if (interface != PHY_INTERFACE_MODE_SGMII)
-- err = mtk_pcs_setup_mode_force(mpcs, interface);
-+ mtk_pcs_setup_mode_force(mpcs, interface);
- else if (phylink_autoneg_inband(mode))
-- err = mtk_pcs_setup_mode_an(mpcs);
-+ mtk_pcs_setup_mode_an(mpcs);
-
-- return err;
-+ return 0;
- }
-
- static void mtk_pcs_restart_an(struct phylink_pcs *pcs)
+++ /dev/null
-From c000dca098002da193b98099df051c9ead0cacb4 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Thu, 27 Oct 2022 14:10:52 +0100
-Subject: [PATCH 03/10] net: mtk_eth_soc: add pcs_get_state() implementation
-
-Add a pcs_get_state() implementation which uses the advertisements
-to compute the resulting link modes, and BMSR contents to determine
-negotiation and link status.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_sgmii.c | 15 +++++++++++++++
- 1 file changed, 15 insertions(+)
-
---- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
-+++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
-@@ -19,6 +19,20 @@ static struct mtk_pcs *pcs_to_mtk_pcs(st
- return container_of(pcs, struct mtk_pcs, pcs);
- }
-
-+static void mtk_pcs_get_state(struct phylink_pcs *pcs,
-+ struct phylink_link_state *state)
-+{
-+ struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
-+ unsigned int bm, adv;
-+
-+ /* Read the BMSR and LPA */
-+ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm);
-+ regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv);
-+
-+ phylink_mii_c22_pcs_decode_state(state, FIELD_GET(SGMII_BMSR, bm),
-+ FIELD_GET(SGMII_LPA, adv));
-+}
-+
- /* For SGMII interface mode */
- static void mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs)
- {
-@@ -117,6 +131,7 @@ static void mtk_pcs_link_up(struct phyli
- }
-
- static const struct phylink_pcs_ops mtk_pcs_ops = {
-+ .pcs_get_state = mtk_pcs_get_state,
- .pcs_config = mtk_pcs_config,
- .pcs_an_restart = mtk_pcs_restart_an,
- .pcs_link_up = mtk_pcs_link_up,
+++ /dev/null
-From 0d2351dc2768061689abd4de1529fa206bbd574e Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Thu, 27 Oct 2022 14:10:58 +0100
-Subject: [PATCH 04/10] net: mtk_eth_soc: convert mtk_sgmii to use
- regmap_update_bits()
-
-mtk_sgmii does a lot of read-modify-write operations, for which there
-is a specific regmap function. Use this function instead of open-coding
-the operations.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_sgmii.c | 61 ++++++++++-------------
- 1 file changed, 26 insertions(+), 35 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
-+++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
-@@ -36,23 +36,18 @@ static void mtk_pcs_get_state(struct phy
- /* For SGMII interface mode */
- static void mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs)
- {
-- unsigned int val;
--
- /* Setup the link timer and QPHY power up inside SGMIISYS */
- regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
- SGMII_LINK_TIMER_DEFAULT);
-
-- regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
-- val |= SGMII_REMOTE_FAULT_DIS;
-- regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
--
-- regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
-- val |= SGMII_AN_RESTART;
-- regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
--
-- regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
-- val &= ~SGMII_PHYA_PWD;
-- regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val);
-+ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
-+ SGMII_REMOTE_FAULT_DIS, SGMII_REMOTE_FAULT_DIS);
-+
-+ regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
-+ SGMII_AN_RESTART, SGMII_AN_RESTART);
-+
-+ regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
-+ SGMII_PHYA_PWD, 0);
- }
-
- /* For 1000BASE-X and 2500BASE-X interface modes, which operate at a
-@@ -61,29 +56,26 @@ static void mtk_pcs_setup_mode_an(struct
- static void mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs,
- phy_interface_t interface)
- {
-- unsigned int val;
-+ unsigned int rgc3;
-
-- regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val);
-- val &= ~RG_PHY_SPEED_MASK;
- if (interface == PHY_INTERFACE_MODE_2500BASEX)
-- val |= RG_PHY_SPEED_3_125G;
-- regmap_write(mpcs->regmap, mpcs->ana_rgc3, val);
-+ rgc3 = RG_PHY_SPEED_3_125G;
-+
-+ regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
-+ RG_PHY_SPEED_3_125G, rgc3);
-
- /* Disable SGMII AN */
-- regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
-- val &= ~SGMII_AN_ENABLE;
-- regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
-+ regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
-+ SGMII_AN_ENABLE, 0);
-
- /* Set the speed etc but leave the duplex unchanged */
-- regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
-- val &= SGMII_DUPLEX_FULL | ~SGMII_IF_MODE_MASK;
-- val |= SGMII_SPEED_1000;
-- regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
-+ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
-+ SGMII_IF_MODE_MASK & ~SGMII_DUPLEX_FULL,
-+ SGMII_SPEED_1000);
-
- /* Release PHYA power down state */
-- regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
-- val &= ~SGMII_PHYA_PWD;
-- regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val);
-+ regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
-+ SGMII_PHYA_PWD, 0);
- }
-
- static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
-@@ -105,29 +97,28 @@ static int mtk_pcs_config(struct phylink
- static void mtk_pcs_restart_an(struct phylink_pcs *pcs)
- {
- struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
-- unsigned int val;
-
-- regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
-- val |= SGMII_AN_RESTART;
-- regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
-+ regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
-+ SGMII_AN_RESTART, SGMII_AN_RESTART);
- }
-
- static void mtk_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
- phy_interface_t interface, int speed, int duplex)
- {
- struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
-- unsigned int val;
-+ unsigned int sgm_mode;
-
- if (!phy_interface_mode_is_8023z(interface))
- return;
-
- /* SGMII force duplex setting */
-- regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
-- val &= ~SGMII_DUPLEX_FULL;
- if (duplex == DUPLEX_FULL)
-- val |= SGMII_DUPLEX_FULL;
-+ sgm_mode = SGMII_DUPLEX_FULL;
-+ else
-+ sgm_mode = 0;
-
-- regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
-+ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
-+ SGMII_DUPLEX_FULL, sgm_mode);
- }
-
- static const struct phylink_pcs_ops mtk_pcs_ops = {
+++ /dev/null
-From 12198c3a410fe69843e335c1bbf6d4c2a4d48e4e Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Thu, 27 Oct 2022 14:11:03 +0100
-Subject: [PATCH 05/10] net: mtk_eth_soc: add out of band forcing of speed and
- duplex in pcs_link_up
-
-Add support for forcing the link speed and duplex setting in the
-pcs_link_up() method for out of band modes, which will be useful when
-we finish converting the pcs_config() method. Until then, we still have
-to force duplex for 802.3z modes to work correctly.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_sgmii.c | 28 ++++++++++++++---------
- 1 file changed, 17 insertions(+), 11 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
-+++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
-@@ -108,17 +108,23 @@ static void mtk_pcs_link_up(struct phyli
- struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
- unsigned int sgm_mode;
-
-- if (!phy_interface_mode_is_8023z(interface))
-- return;
-+ if (!phylink_autoneg_inband(mode) ||
-+ phy_interface_mode_is_8023z(interface)) {
-+ /* Force the speed and duplex setting */
-+ if (speed == SPEED_10)
-+ sgm_mode = SGMII_SPEED_10;
-+ else if (speed == SPEED_100)
-+ sgm_mode = SGMII_SPEED_100;
-+ else
-+ sgm_mode = SGMII_SPEED_1000;
-
-- /* SGMII force duplex setting */
-- if (duplex == DUPLEX_FULL)
-- sgm_mode = SGMII_DUPLEX_FULL;
-- else
-- sgm_mode = 0;
-+ if (duplex == DUPLEX_FULL)
-+ sgm_mode |= SGMII_DUPLEX_FULL;
-
-- regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
-- SGMII_DUPLEX_FULL, sgm_mode);
-+ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
-+ SGMII_DUPLEX_FULL | SGMII_SPEED_MASK,
-+ sgm_mode);
-+ }
- }
-
- static const struct phylink_pcs_ops mtk_pcs_ops = {
+++ /dev/null
-From 6f38fffe2179dd29612aea2c67c46ed6682b4e46 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Thu, 27 Oct 2022 14:11:08 +0100
-Subject: [PATCH 06/10] net: mtk_eth_soc: move PHY power up
-
-The PHY power up is common to both configuration paths, so move it into
-the parent function. We need to do this for all serdes modes.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_sgmii.c | 11 ++++-------
- 1 file changed, 4 insertions(+), 7 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
-+++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
-@@ -45,9 +45,6 @@ static void mtk_pcs_setup_mode_an(struct
-
- regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
- SGMII_AN_RESTART, SGMII_AN_RESTART);
--
-- regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
-- SGMII_PHYA_PWD, 0);
- }
-
- /* For 1000BASE-X and 2500BASE-X interface modes, which operate at a
-@@ -72,10 +69,6 @@ static void mtk_pcs_setup_mode_force(str
- regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
- SGMII_IF_MODE_MASK & ~SGMII_DUPLEX_FULL,
- SGMII_SPEED_1000);
--
-- /* Release PHYA power down state */
-- regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
-- SGMII_PHYA_PWD, 0);
- }
-
- static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
-@@ -91,6 +84,10 @@ static int mtk_pcs_config(struct phylink
- else if (phylink_autoneg_inband(mode))
- mtk_pcs_setup_mode_an(mpcs);
-
-+ /* Release PHYA power down state */
-+ regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
-+ SGMII_PHYA_PWD, 0);
-+
- return 0;
- }
-
+++ /dev/null
-From f752c0df13dfeb721c11d3debb79f08cf437344f Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Thu, 27 Oct 2022 14:11:13 +0100
-Subject: [PATCH 07/10] net: mtk_eth_soc: move interface speed selection
-
-Move the selection of the underlying interface speed to the pcs_config
-function, so we always program the interface speed.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_sgmii.c | 18 ++++++++++--------
- 1 file changed, 10 insertions(+), 8 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
-+++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
-@@ -53,14 +53,6 @@ static void mtk_pcs_setup_mode_an(struct
- static void mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs,
- phy_interface_t interface)
- {
-- unsigned int rgc3;
--
-- if (interface == PHY_INTERFACE_MODE_2500BASEX)
-- rgc3 = RG_PHY_SPEED_3_125G;
--
-- regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
-- RG_PHY_SPEED_3_125G, rgc3);
--
- /* Disable SGMII AN */
- regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
- SGMII_AN_ENABLE, 0);
-@@ -77,6 +69,16 @@ static int mtk_pcs_config(struct phylink
- bool permit_pause_to_mac)
- {
- struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
-+ unsigned int rgc3;
-+
-+ if (interface == PHY_INTERFACE_MODE_2500BASEX)
-+ rgc3 = RG_PHY_SPEED_3_125G;
-+ else
-+ rgc3 = 0;
-+
-+ /* Configure the underlying interface speed */
-+ regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
-+ RG_PHY_SPEED_3_125G, rgc3);
-
- /* Setup SGMIISYS with the determined property */
- if (interface != PHY_INTERFACE_MODE_SGMII)
+++ /dev/null
-From c125c66ea71b9377ae2478c4f1b87b180cc5c6ef Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Thu, 27 Oct 2022 14:11:18 +0100
-Subject: [PATCH 08/10] net: mtk_eth_soc: add advertisement programming
-
-Program the advertisement into the mtk PCS block.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_sgmii.c | 13 ++++++++++++-
- 1 file changed, 12 insertions(+), 1 deletion(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
-+++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
-@@ -70,16 +70,27 @@ static int mtk_pcs_config(struct phylink
- {
- struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
- unsigned int rgc3;
-+ int advertise;
-+ bool changed;
-
- if (interface == PHY_INTERFACE_MODE_2500BASEX)
- rgc3 = RG_PHY_SPEED_3_125G;
- else
- rgc3 = 0;
-
-+ advertise = phylink_mii_c22_pcs_encode_advertisement(interface,
-+ advertising);
-+ if (advertise < 0)
-+ return advertise;
-+
- /* Configure the underlying interface speed */
- regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
- RG_PHY_SPEED_3_125G, rgc3);
-
-+ /* Update the advertisement, noting whether it has changed */
-+ regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE,
-+ SGMII_ADVERTISE, advertise, &changed);
-+
- /* Setup SGMIISYS with the determined property */
- if (interface != PHY_INTERFACE_MODE_SGMII)
- mtk_pcs_setup_mode_force(mpcs, interface);
-@@ -90,7 +101,7 @@ static int mtk_pcs_config(struct phylink
- regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
- SGMII_PHYA_PWD, 0);
-
-- return 0;
-+ return changed;
- }
-
- static void mtk_pcs_restart_an(struct phylink_pcs *pcs)
+++ /dev/null
-From 3027d89f87707e7f3e5b683e0d37a32afb5bde96 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Thu, 27 Oct 2022 14:11:23 +0100
-Subject: [PATCH 09/10] net: mtk_eth_soc: move and correct link timer
- programming
-
-Program the link timer appropriately for the interface mode being
-used, using the newly introduced phylink helper that provides the
-nanosecond link timer interval.
-
-The intervals are 1.6ms for SGMII based protocols and 10ms for
-802.3z based protocols.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_sgmii.c | 13 ++++++++-----
- 1 file changed, 8 insertions(+), 5 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
-+++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
-@@ -36,10 +36,6 @@ static void mtk_pcs_get_state(struct phy
- /* For SGMII interface mode */
- static void mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs)
- {
-- /* Setup the link timer and QPHY power up inside SGMIISYS */
-- regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
-- SGMII_LINK_TIMER_DEFAULT);
--
- regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
- SGMII_REMOTE_FAULT_DIS, SGMII_REMOTE_FAULT_DIS);
-
-@@ -69,8 +65,8 @@ static int mtk_pcs_config(struct phylink
- bool permit_pause_to_mac)
- {
- struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
-+ int advertise, link_timer;
- unsigned int rgc3;
-- int advertise;
- bool changed;
-
- if (interface == PHY_INTERFACE_MODE_2500BASEX)
-@@ -83,6 +79,10 @@ static int mtk_pcs_config(struct phylink
- if (advertise < 0)
- return advertise;
-
-+ link_timer = phylink_get_link_timer_ns(interface);
-+ if (link_timer < 0)
-+ return link_timer;
-+
- /* Configure the underlying interface speed */
- regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
- RG_PHY_SPEED_3_125G, rgc3);
-@@ -91,6 +91,9 @@ static int mtk_pcs_config(struct phylink
- regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE,
- SGMII_ADVERTISE, advertise, &changed);
-
-+ /* Setup the link timer and QPHY power up inside SGMIISYS */
-+ regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, link_timer / 2 / 8);
-+
- /* Setup SGMIISYS with the determined property */
- if (interface != PHY_INTERFACE_MODE_SGMII)
- mtk_pcs_setup_mode_force(mpcs, interface);
+++ /dev/null
-From 81b0f12a2a8a1699a7d49c3995e5f71e4ec018e6 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Thu, 27 Oct 2022 14:11:28 +0100
-Subject: [PATCH 10/10] net: mtk_eth_soc: add support for in-band 802.3z
- negotiation
-
-As a result of help from Frank Wunderlich to investigate and test, we
-now know how to program this PCS for in-band 802.3z negotiation. Add
-support for this by moving the contents of the two functions into the
-common mtk_pcs_config() function and adding the register settings for
-802.3z negotiation.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_sgmii.c | 77 ++++++++++++-----------
- 1 file changed, 42 insertions(+), 35 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
-+++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
-@@ -33,41 +33,15 @@ static void mtk_pcs_get_state(struct phy
- FIELD_GET(SGMII_LPA, adv));
- }
-
--/* For SGMII interface mode */
--static void mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs)
--{
-- regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
-- SGMII_REMOTE_FAULT_DIS, SGMII_REMOTE_FAULT_DIS);
--
-- regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
-- SGMII_AN_RESTART, SGMII_AN_RESTART);
--}
--
--/* For 1000BASE-X and 2500BASE-X interface modes, which operate at a
-- * fixed speed.
-- */
--static void mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs,
-- phy_interface_t interface)
--{
-- /* Disable SGMII AN */
-- regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
-- SGMII_AN_ENABLE, 0);
--
-- /* Set the speed etc but leave the duplex unchanged */
-- regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
-- SGMII_IF_MODE_MASK & ~SGMII_DUPLEX_FULL,
-- SGMII_SPEED_1000);
--}
--
- static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
- phy_interface_t interface,
- const unsigned long *advertising,
- bool permit_pause_to_mac)
- {
- struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
-+ unsigned int rgc3, sgm_mode, bmcr;
- int advertise, link_timer;
-- unsigned int rgc3;
-- bool changed;
-+ bool changed, use_an;
-
- if (interface == PHY_INTERFACE_MODE_2500BASEX)
- rgc3 = RG_PHY_SPEED_3_125G;
-@@ -83,6 +57,37 @@ static int mtk_pcs_config(struct phylink
- if (link_timer < 0)
- return link_timer;
-
-+ /* Clearing IF_MODE_BIT0 switches the PCS to BASE-X mode, and
-+ * we assume that fixes it's speed at bitrate = line rate (in
-+ * other words, 1000Mbps or 2500Mbps).
-+ */
-+ if (interface == PHY_INTERFACE_MODE_SGMII) {
-+ sgm_mode = SGMII_IF_MODE_SGMII;
-+ if (phylink_autoneg_inband(mode)) {
-+ sgm_mode |= SGMII_REMOTE_FAULT_DIS |
-+ SGMII_SPEED_DUPLEX_AN;
-+ use_an = true;
-+ } else {
-+ use_an = false;
-+ }
-+ } else if (phylink_autoneg_inband(mode)) {
-+ /* 1000base-X or 2500base-X autoneg */
-+ sgm_mode = SGMII_REMOTE_FAULT_DIS;
-+ use_an = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
-+ advertising);
-+ } else {
-+ /* 1000base-X or 2500base-X without autoneg */
-+ sgm_mode = 0;
-+ use_an = false;
-+ }
-+
-+ if (use_an) {
-+ /* FIXME: Do we need to set AN_RESTART here? */
-+ bmcr = SGMII_AN_RESTART | SGMII_AN_ENABLE;
-+ } else {
-+ bmcr = 0;
-+ }
-+
- /* Configure the underlying interface speed */
- regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
- RG_PHY_SPEED_3_125G, rgc3);
-@@ -94,11 +99,14 @@ static int mtk_pcs_config(struct phylink
- /* Setup the link timer and QPHY power up inside SGMIISYS */
- regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, link_timer / 2 / 8);
-
-- /* Setup SGMIISYS with the determined property */
-- if (interface != PHY_INTERFACE_MODE_SGMII)
-- mtk_pcs_setup_mode_force(mpcs, interface);
-- else if (phylink_autoneg_inband(mode))
-- mtk_pcs_setup_mode_an(mpcs);
-+ /* Update the sgmsys mode register */
-+ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
-+ SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN |
-+ SGMII_IF_MODE_SGMII, sgm_mode);
-+
-+ /* Update the BMCR */
-+ regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
-+ SGMII_AN_RESTART | SGMII_AN_ENABLE, bmcr);
-
- /* Release PHYA power down state */
- regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
-@@ -121,8 +129,7 @@ static void mtk_pcs_link_up(struct phyli
- struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
- unsigned int sgm_mode;
-
-- if (!phylink_autoneg_inband(mode) ||
-- phy_interface_mode_is_8023z(interface)) {
-+ if (!phylink_autoneg_inband(mode)) {
- /* Force the speed and duplex setting */
- if (speed == SPEED_10)
- sgm_mode = SGMII_SPEED_10;
+++ /dev/null
-From 7ff82416de8295c61423ef6fd75f052d3837d2f7 Mon Sep 17 00:00:00 2001
-From: Alexander Couzens <lynxis@fe80.eu>
-Date: Wed, 1 Feb 2023 19:23:29 +0100
-Subject: [PATCH 11/13] net: mediatek: sgmii: ensure the SGMII PHY is powered
- down on configuration
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The code expect the PHY to be in power down which is only true after reset.
-Allow changes of the SGMII parameters more than once.
-
-Only power down when reconfiguring to avoid bouncing the link when there's
-no reason to - based on code from Russell King.
-
-There are cases when the SGMII_PHYA_PWD register contains 0x9 which
-prevents SGMII from working. The SGMII still shows link but no traffic
-can flow. Writing 0x0 to the PHYA_PWD register fix the issue. 0x0 was
-taken from a good working state of the SGMII interface.
-
-Fixes: 42c03844e93d ("net-next: mediatek: add support for MediaTek MT7622 SoC")
-Suggested-by: Russell King (Oracle) <linux@armlinux.org.uk>
-Signed-off-by: Alexander Couzens <lynxis@fe80.eu>
-[ bmork: rebased and squashed into one patch ]
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Bjørn Mork <bjorn@mork.no>
-Acked-by: Daniel Golle <daniel@makrotopia.org>
-Tested-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 2 ++
- drivers/net/ethernet/mediatek/mtk_sgmii.c | 39 +++++++++++++++------
- 2 files changed, 30 insertions(+), 11 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -1067,11 +1067,13 @@ struct mtk_soc_data {
- * @regmap: The register map pointing at the range used to setup
- * SGMII modes
- * @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap
-+ * @interface: Currently configured interface mode
- * @pcs: Phylink PCS structure
- */
- struct mtk_pcs {
- struct regmap *regmap;
- u32 ana_rgc3;
-+ phy_interface_t interface;
- struct phylink_pcs pcs;
- };
-
---- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
-+++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
-@@ -43,11 +43,6 @@ static int mtk_pcs_config(struct phylink
- int advertise, link_timer;
- bool changed, use_an;
-
-- if (interface == PHY_INTERFACE_MODE_2500BASEX)
-- rgc3 = RG_PHY_SPEED_3_125G;
-- else
-- rgc3 = 0;
--
- advertise = phylink_mii_c22_pcs_encode_advertisement(interface,
- advertising);
- if (advertise < 0)
-@@ -88,9 +83,22 @@ static int mtk_pcs_config(struct phylink
- bmcr = 0;
- }
-
-- /* Configure the underlying interface speed */
-- regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
-- RG_PHY_SPEED_3_125G, rgc3);
-+ if (mpcs->interface != interface) {
-+ /* PHYA power down */
-+ regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
-+ SGMII_PHYA_PWD, SGMII_PHYA_PWD);
-+
-+ if (interface == PHY_INTERFACE_MODE_2500BASEX)
-+ rgc3 = RG_PHY_SPEED_3_125G;
-+ else
-+ rgc3 = 0;
-+
-+ /* Configure the underlying interface speed */
-+ regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
-+ RG_PHY_SPEED_3_125G, rgc3);
-+
-+ mpcs->interface = interface;
-+ }
-
- /* Update the advertisement, noting whether it has changed */
- regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE,
-@@ -108,9 +116,17 @@ static int mtk_pcs_config(struct phylink
- regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
- SGMII_AN_RESTART | SGMII_AN_ENABLE, bmcr);
-
-- /* Release PHYA power down state */
-- regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
-- SGMII_PHYA_PWD, 0);
-+ /* Release PHYA power down state
-+ * Only removing bit SGMII_PHYA_PWD isn't enough.
-+ * There are cases when the SGMII_PHYA_PWD register contains 0x9 which
-+ * prevents SGMII from working. The SGMII still shows link but no traffic
-+ * can flow. Writing 0x0 to the PHYA_PWD register fix the issue. 0x0 was
-+ * taken from a good working state of the SGMII interface.
-+ * Unknown how much the QPHY needs but it is racy without a sleep.
-+ * Tested on mt7622 & mt7986.
-+ */
-+ usleep_range(50, 100);
-+ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
-
- return changed;
- }
-@@ -171,6 +187,7 @@ int mtk_sgmii_init(struct mtk_sgmii *ss,
- return PTR_ERR(ss->pcs[i].regmap);
-
- ss->pcs[i].pcs.ops = &mtk_pcs_ops;
-+ ss->pcs[i].interface = PHY_INTERFACE_MODE_NA;
- }
-
- return 0;
+++ /dev/null
-From 9d32637122de88f1ef614c29703f0e050cad342e Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= <bjorn@mork.no>
-Date: Wed, 1 Feb 2023 19:23:30 +0100
-Subject: [PATCH 12/13] net: mediatek: sgmii: fix duplex configuration
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The logic of the duplex bit is inverted. Setting it means half
-duplex, not full duplex.
-
-Fix and rename macro to avoid confusion.
-
-Fixes: 7e538372694b ("net: ethernet: mediatek: Re-add support SGMII")
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Bjørn Mork <bjorn@mork.no>
-Acked-by: Daniel Golle <daniel@makrotopia.org>
-Tested-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 2 +-
- drivers/net/ethernet/mediatek/mtk_sgmii.c | 6 +++---
- 2 files changed, 4 insertions(+), 4 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -534,7 +534,7 @@
- #define SGMII_SPEED_10 FIELD_PREP(SGMII_SPEED_MASK, 0)
- #define SGMII_SPEED_100 FIELD_PREP(SGMII_SPEED_MASK, 1)
- #define SGMII_SPEED_1000 FIELD_PREP(SGMII_SPEED_MASK, 2)
--#define SGMII_DUPLEX_FULL BIT(4)
-+#define SGMII_DUPLEX_HALF BIT(4)
- #define SGMII_IF_MODE_BIT5 BIT(5)
- #define SGMII_REMOTE_FAULT_DIS BIT(8)
- #define SGMII_CODE_SYNC_SET_VAL BIT(9)
---- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
-+++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
-@@ -154,11 +154,11 @@ static void mtk_pcs_link_up(struct phyli
- else
- sgm_mode = SGMII_SPEED_1000;
-
-- if (duplex == DUPLEX_FULL)
-- sgm_mode |= SGMII_DUPLEX_FULL;
-+ if (duplex != DUPLEX_FULL)
-+ sgm_mode |= SGMII_DUPLEX_HALF;
-
- regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
-- SGMII_DUPLEX_FULL | SGMII_SPEED_MASK,
-+ SGMII_DUPLEX_HALF | SGMII_SPEED_MASK,
- sgm_mode);
- }
- }
+++ /dev/null
-From 3337a6e04ddf2923a1bdcf3d31b3b52412bf82dd Mon Sep 17 00:00:00 2001
-From: Alexander Couzens <lynxis@fe80.eu>
-Date: Wed, 1 Feb 2023 19:23:31 +0100
-Subject: [PATCH 13/13] mtk_sgmii: enable PCS polling to allow SFP work
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Currently there is no IRQ handling (even the SGMII supports it).
-Enable polling to support SFP ports.
-
-Fixes: 14a44ab0330d ("net: mtk_eth_soc: partially convert to phylink_pcs")
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Alexander Couzens <lynxis@fe80.eu>
-[ bmork: changed "1" => "true" ]
-Signed-off-by: Bjørn Mork <bjorn@mork.no>
-Acked-by: Daniel Golle <daniel@makrotopia.org>
-Tested-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_sgmii.c | 1 +
- 1 file changed, 1 insertion(+)
-
---- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
-+++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
-@@ -187,6 +187,7 @@ int mtk_sgmii_init(struct mtk_sgmii *ss,
- return PTR_ERR(ss->pcs[i].regmap);
-
- ss->pcs[i].pcs.ops = &mtk_pcs_ops;
-+ ss->pcs[i].pcs.poll = true;
- ss->pcs[i].interface = PHY_INTERFACE_MODE_NA;
- }
-
+++ /dev/null
-From 611e2dabb4b3243d176739fd6a5a34d007fa3f86 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Tue, 14 Mar 2023 00:34:26 +0000
-Subject: [PATCH 1/2] net: ethernet: mtk_eth_soc: reset PCS state
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Reset the internal PCS state machine when changing interface mode.
-This prevents confusing the state machine when changing interface
-modes, e.g. from SGMII to 2500Base-X or vice-versa.
-
-Fixes: 7e538372694b ("net: ethernet: mediatek: Re-add support SGMII")
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Tested-by: Bjørn Mork <bjorn@mork.no>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 4 ++++
- drivers/net/ethernet/mediatek/mtk_sgmii.c | 4 ++++
- 2 files changed, 8 insertions(+)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -542,6 +542,10 @@
- #define SGMII_SEND_AN_ERROR_EN BIT(11)
- #define SGMII_IF_MODE_MASK GENMASK(5, 1)
-
-+/* Register to reset SGMII design */
-+#define SGMII_RESERVED_0 0x34
-+#define SGMII_SW_RESET BIT(0)
-+
- /* Register to set SGMII speed, ANA RG_ Control Signals III*/
- #define SGMSYS_ANA_RG_CS3 0x2028
- #define RG_PHY_SPEED_MASK (BIT(2) | BIT(3))
---- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
-+++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
-@@ -88,6 +88,10 @@ static int mtk_pcs_config(struct phylink
- regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
- SGMII_PHYA_PWD, SGMII_PHYA_PWD);
-
-+ /* Reset SGMII PCS state */
-+ regmap_update_bits(mpcs->regmap, SGMII_RESERVED_0,
-+ SGMII_SW_RESET, SGMII_SW_RESET);
-+
- if (interface == PHY_INTERFACE_MODE_2500BASEX)
- rgc3 = RG_PHY_SPEED_3_125G;
- else
+++ /dev/null
-From 6e933a804c7db8be64f367f33e63cd7dcc302ebb Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Tue, 14 Mar 2023 00:34:45 +0000
-Subject: [PATCH 2/2] net: ethernet: mtk_eth_soc: only write values if needed
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Only restart auto-negotiation and write link timer if actually
-necessary. This prevents losing the link in case of minor
-changes.
-
-Fixes: 7e538372694b ("net: ethernet: mediatek: Re-add support SGMII")
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Tested-by: Bjørn Mork <bjorn@mork.no>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/ethernet/mediatek/mtk_sgmii.c | 24 +++++++++++------------
- 1 file changed, 12 insertions(+), 12 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
-+++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
-@@ -38,20 +38,16 @@ static int mtk_pcs_config(struct phylink
- const unsigned long *advertising,
- bool permit_pause_to_mac)
- {
-+ bool mode_changed = false, changed, use_an;
- struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
- unsigned int rgc3, sgm_mode, bmcr;
- int advertise, link_timer;
-- bool changed, use_an;
-
- advertise = phylink_mii_c22_pcs_encode_advertisement(interface,
- advertising);
- if (advertise < 0)
- return advertise;
-
-- link_timer = phylink_get_link_timer_ns(interface);
-- if (link_timer < 0)
-- return link_timer;
--
- /* Clearing IF_MODE_BIT0 switches the PCS to BASE-X mode, and
- * we assume that fixes it's speed at bitrate = line rate (in
- * other words, 1000Mbps or 2500Mbps).
-@@ -77,13 +73,16 @@ static int mtk_pcs_config(struct phylink
- }
-
- if (use_an) {
-- /* FIXME: Do we need to set AN_RESTART here? */
-- bmcr = SGMII_AN_RESTART | SGMII_AN_ENABLE;
-+ bmcr = SGMII_AN_ENABLE;
- } else {
- bmcr = 0;
- }
-
- if (mpcs->interface != interface) {
-+ link_timer = phylink_get_link_timer_ns(interface);
-+ if (link_timer < 0)
-+ return link_timer;
-+
- /* PHYA power down */
- regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
- SGMII_PHYA_PWD, SGMII_PHYA_PWD);
-@@ -101,16 +100,17 @@ static int mtk_pcs_config(struct phylink
- regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
- RG_PHY_SPEED_3_125G, rgc3);
-
-+ /* Setup the link timer */
-+ regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, link_timer / 2 / 8);
-+
- mpcs->interface = interface;
-+ mode_changed = true;
- }
-
- /* Update the advertisement, noting whether it has changed */
- regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE,
- SGMII_ADVERTISE, advertise, &changed);
-
-- /* Setup the link timer and QPHY power up inside SGMIISYS */
-- regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, link_timer / 2 / 8);
--
- /* Update the sgmsys mode register */
- regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
- SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN |
-@@ -118,7 +118,7 @@ static int mtk_pcs_config(struct phylink
-
- /* Update the BMCR */
- regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
-- SGMII_AN_RESTART | SGMII_AN_ENABLE, bmcr);
-+ SGMII_AN_ENABLE, bmcr);
-
- /* Release PHYA power down state
- * Only removing bit SGMII_PHYA_PWD isn't enough.
-@@ -132,7 +132,7 @@ static int mtk_pcs_config(struct phylink
- usleep_range(50, 100);
- regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
-
-- return changed;
-+ return changed || mode_changed;
- }
-
- static void mtk_pcs_restart_an(struct phylink_pcs *pcs)
+++ /dev/null
-From f5d43ddd334b7c32fcaed9ba46afbd85cb467f1f Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Sun, 19 Mar 2023 12:56:28 +0000
-Subject: [PATCH] net: ethernet: mtk_eth_soc: add support for MT7981 SoC
-
-The MediaTek MT7981 SoC comes with two 1G/2.5G SGMII ports, just like
-MT7986.
-
-In addition MT7981 is equipped with a built-in 1000Base-T PHY which can
-be used with GMAC1.
-
-As many MT7981 boards make use of inverting SGMII signal polarity, add
-new device-tree attribute 'mediatek,pn_swap' to support them.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_path.c | 14 +++++++--
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 21 +++++++++++++
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 31 ++++++++++++++++++++
- drivers/net/ethernet/mediatek/mtk_sgmii.c | 10 +++++++
- 4 files changed, 73 insertions(+), 3 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_path.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_path.c
-@@ -96,12 +96,20 @@ static int set_mux_gmac2_gmac0_to_gephy(
-
- static int set_mux_u3_gmac2_to_qphy(struct mtk_eth *eth, int path)
- {
-- unsigned int val = 0;
-+ unsigned int val = 0, mask = 0, reg = 0;
- bool updated = true;
-
- switch (path) {
- case MTK_ETH_PATH_GMAC2_SGMII:
-- val = CO_QPHY_SEL;
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_U3_COPHY_V2)) {
-+ reg = USB_PHY_SWITCH_REG;
-+ val = SGMII_QPHY_SEL;
-+ mask = QPHY_SEL_MASK;
-+ } else {
-+ reg = INFRA_MISC2;
-+ val = CO_QPHY_SEL;
-+ mask = val;
-+ }
- break;
- default:
- updated = false;
-@@ -109,7 +117,7 @@ static int set_mux_u3_gmac2_to_qphy(stru
- }
-
- if (updated)
-- regmap_update_bits(eth->infra, INFRA_MISC2, CO_QPHY_SEL, val);
-+ regmap_update_bits(eth->infra, reg, mask, val);
-
- dev_dbg(eth->dev, "path %s in %s updated = %d\n",
- mtk_eth_path_name(path), __func__, updated);
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -4803,6 +4803,26 @@ static const struct mtk_soc_data mt7629_
- },
- };
-
-+static const struct mtk_soc_data mt7981_data = {
-+ .reg_map = &mt7986_reg_map,
-+ .ana_rgc3 = 0x128,
-+ .caps = MT7981_CAPS,
-+ .hw_features = MTK_HW_FEATURES,
-+ .required_clks = MT7981_CLKS_BITMAP,
-+ .required_pctl = false,
-+ .offload_version = 2,
-+ .hash_offset = 4,
-+ .foe_entry_size = sizeof(struct mtk_foe_entry),
-+ .txrx = {
-+ .txd_size = sizeof(struct mtk_tx_dma_v2),
-+ .rxd_size = sizeof(struct mtk_rx_dma_v2),
-+ .rx_irq_done_mask = MTK_RX_DONE_INT_V2,
-+ .rx_dma_l4_valid = RX_DMA_L4_VALID_V2,
-+ .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
-+ .dma_len_offset = 8,
-+ },
-+};
-+
- static const struct mtk_soc_data mt7986_data = {
- .reg_map = &mt7986_reg_map,
- .ana_rgc3 = 0x128,
-@@ -4845,6 +4865,7 @@ const struct of_device_id of_mtk_match[]
- { .compatible = "mediatek,mt7622-eth", .data = &mt7622_data},
- { .compatible = "mediatek,mt7623-eth", .data = &mt7623_data},
- { .compatible = "mediatek,mt7629-eth", .data = &mt7629_data},
-+ { .compatible = "mediatek,mt7981-eth", .data = &mt7981_data},
- { .compatible = "mediatek,mt7986-eth", .data = &mt7986_data},
- { .compatible = "ralink,rt5350-eth", .data = &rt5350_data},
- {},
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -556,11 +556,22 @@
- #define SGMSYS_QPHY_PWR_STATE_CTRL 0xe8
- #define SGMII_PHYA_PWD BIT(4)
-
-+/* Register to QPHY wrapper control */
-+#define SGMSYS_QPHY_WRAP_CTRL 0xec
-+#define SGMII_PN_SWAP_MASK GENMASK(1, 0)
-+#define SGMII_PN_SWAP_TX_RX (BIT(0) | BIT(1))
-+#define MTK_SGMII_FLAG_PN_SWAP BIT(0)
-+
- /* Infrasys subsystem config registers */
- #define INFRA_MISC2 0x70c
- #define CO_QPHY_SEL BIT(0)
- #define GEPHY_MAC_SEL BIT(1)
-
-+/* Top misc registers */
-+#define USB_PHY_SWITCH_REG 0x218
-+#define QPHY_SEL_MASK GENMASK(1, 0)
-+#define SGMII_QPHY_SEL 0x2
-+
- /* MT7628/88 specific stuff */
- #define MT7628_PDMA_OFFSET 0x0800
- #define MT7628_SDM_OFFSET 0x0c00
-@@ -741,6 +752,17 @@ enum mtk_clks_map {
- BIT(MTK_CLK_SGMII2_CDR_FB) | \
- BIT(MTK_CLK_SGMII_CK) | \
- BIT(MTK_CLK_ETH2PLL) | BIT(MTK_CLK_SGMIITOP))
-+#define MT7981_CLKS_BITMAP (BIT(MTK_CLK_FE) | BIT(MTK_CLK_GP2) | BIT(MTK_CLK_GP1) | \
-+ BIT(MTK_CLK_WOCPU0) | \
-+ BIT(MTK_CLK_SGMII_TX_250M) | \
-+ BIT(MTK_CLK_SGMII_RX_250M) | \
-+ BIT(MTK_CLK_SGMII_CDR_REF) | \
-+ BIT(MTK_CLK_SGMII_CDR_FB) | \
-+ BIT(MTK_CLK_SGMII2_TX_250M) | \
-+ BIT(MTK_CLK_SGMII2_RX_250M) | \
-+ BIT(MTK_CLK_SGMII2_CDR_REF) | \
-+ BIT(MTK_CLK_SGMII2_CDR_FB) | \
-+ BIT(MTK_CLK_SGMII_CK))
- #define MT7986_CLKS_BITMAP (BIT(MTK_CLK_FE) | BIT(MTK_CLK_GP2) | BIT(MTK_CLK_GP1) | \
- BIT(MTK_CLK_WOCPU1) | BIT(MTK_CLK_WOCPU0) | \
- BIT(MTK_CLK_SGMII_TX_250M) | \
-@@ -854,6 +876,7 @@ enum mkt_eth_capabilities {
- MTK_NETSYS_V2_BIT,
- MTK_SOC_MT7628_BIT,
- MTK_RSTCTRL_PPE1_BIT,
-+ MTK_U3_COPHY_V2_BIT,
-
- /* MUX BITS*/
- MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT,
-@@ -888,6 +911,7 @@ enum mkt_eth_capabilities {
- #define MTK_NETSYS_V2 BIT(MTK_NETSYS_V2_BIT)
- #define MTK_SOC_MT7628 BIT(MTK_SOC_MT7628_BIT)
- #define MTK_RSTCTRL_PPE1 BIT(MTK_RSTCTRL_PPE1_BIT)
-+#define MTK_U3_COPHY_V2 BIT(MTK_U3_COPHY_V2_BIT)
-
- #define MTK_ETH_MUX_GDM1_TO_GMAC1_ESW \
- BIT(MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT)
-@@ -960,6 +984,11 @@ enum mkt_eth_capabilities {
- MTK_MUX_U3_GMAC2_TO_QPHY | \
- MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA)
-
-+#define MT7981_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | MTK_GMAC2_GEPHY | \
-+ MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \
-+ MTK_MUX_U3_GMAC2_TO_QPHY | MTK_U3_COPHY_V2 | \
-+ MTK_NETSYS_V2 | MTK_RSTCTRL_PPE1)
-+
- #define MT7986_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | \
- MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \
- MTK_NETSYS_V2 | MTK_RSTCTRL_PPE1)
-@@ -1073,12 +1102,14 @@ struct mtk_soc_data {
- * @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap
- * @interface: Currently configured interface mode
- * @pcs: Phylink PCS structure
-+ * @flags: Flags indicating hardware properties
- */
- struct mtk_pcs {
- struct regmap *regmap;
- u32 ana_rgc3;
- phy_interface_t interface;
- struct phylink_pcs pcs;
-+ u32 flags;
- };
-
- /* struct mtk_sgmii - This is the structure holding sgmii regmap and its
---- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
-+++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
-@@ -87,6 +87,11 @@ static int mtk_pcs_config(struct phylink
- regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
- SGMII_PHYA_PWD, SGMII_PHYA_PWD);
-
-+ if (mpcs->flags & MTK_SGMII_FLAG_PN_SWAP)
-+ regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_WRAP_CTRL,
-+ SGMII_PN_SWAP_MASK,
-+ SGMII_PN_SWAP_TX_RX);
-+
- /* Reset SGMII PCS state */
- regmap_update_bits(mpcs->regmap, SGMII_RESERVED_0,
- SGMII_SW_RESET, SGMII_SW_RESET);
-@@ -186,6 +191,11 @@ int mtk_sgmii_init(struct mtk_sgmii *ss,
-
- ss->pcs[i].ana_rgc3 = ana_rgc3;
- ss->pcs[i].regmap = syscon_node_to_regmap(np);
-+
-+ ss->pcs[i].flags = 0;
-+ if (of_property_read_bool(np, "mediatek,pnswap"))
-+ ss->pcs[i].flags |= MTK_SGMII_FLAG_PN_SWAP;
-+
- of_node_put(np);
- if (IS_ERR(ss->pcs[i].regmap))
- return PTR_ERR(ss->pcs[i].regmap);
+++ /dev/null
-From c0a440031d4314d1023c1b87f43a4233634eebdb Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Sun, 19 Mar 2023 12:57:15 +0000
-Subject: [PATCH] net: ethernet: mtk_eth_soc: set MDIO bus clock frequency
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Set MDIO bus clock frequency and allow setting a custom maximum
-frequency from device tree.
-
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
-Tested-by: Bjørn Mork <bjorn@mork.no>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 21 +++++++++++++++++++++
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 7 +++++++
- 2 files changed, 28 insertions(+)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -744,8 +744,10 @@ static const struct phylink_mac_ops mtk_
-
- static int mtk_mdio_init(struct mtk_eth *eth)
- {
-+ unsigned int max_clk = 2500000, divider;
- struct device_node *mii_np;
- int ret;
-+ u32 val;
-
- mii_np = of_get_child_by_name(eth->dev->of_node, "mdio-bus");
- if (!mii_np) {
-@@ -772,6 +774,25 @@ static int mtk_mdio_init(struct mtk_eth
- eth->mii_bus->parent = eth->dev;
-
- snprintf(eth->mii_bus->id, MII_BUS_ID_SIZE, "%pOFn", mii_np);
-+
-+ if (!of_property_read_u32(mii_np, "clock-frequency", &val)) {
-+ if (val > MDC_MAX_FREQ || val < MDC_MAX_FREQ / MDC_MAX_DIVIDER) {
-+ dev_err(eth->dev, "MDIO clock frequency out of range");
-+ ret = -EINVAL;
-+ goto err_put_node;
-+ }
-+ max_clk = val;
-+ }
-+ divider = min_t(unsigned int, DIV_ROUND_UP(MDC_MAX_FREQ, max_clk), 63);
-+
-+ /* Configure MDC Divider */
-+ val = mtk_r32(eth, MTK_PPSC);
-+ val &= ~PPSC_MDC_CFG;
-+ val |= FIELD_PREP(PPSC_MDC_CFG, divider) | PPSC_MDC_TURBO;
-+ mtk_w32(eth, val, MTK_PPSC);
-+
-+ dev_dbg(eth->dev, "MDC is running on %d Hz\n", MDC_MAX_FREQ / divider);
-+
- ret = of_mdiobus_register(eth->mii_bus, mii_np);
-
- err_put_node:
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -363,6 +363,13 @@
- #define RX_DMA_VTAG_V2 BIT(0)
- #define RX_DMA_L4_VALID_V2 BIT(2)
-
-+/* PHY Polling and SMI Master Control registers */
-+#define MTK_PPSC 0x10000
-+#define PPSC_MDC_CFG GENMASK(29, 24)
-+#define PPSC_MDC_TURBO BIT(20)
-+#define MDC_MAX_FREQ 25000000
-+#define MDC_MAX_DIVIDER 63
-+
- /* PHY Indirect Access Control registers */
- #define MTK_PHY_IAC 0x10004
- #define PHY_IAC_ACCESS BIT(31)
+++ /dev/null
-From 2a3ec7ae313310c1092e4256208cc04d1958e469 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Sun, 19 Mar 2023 12:58:02 +0000
-Subject: [PATCH] net: ethernet: mtk_eth_soc: switch to external PCS driver
-
-Now that we got a PCS driver, use it and remove the now redundant
-PCS code and it's header macros from the Ethernet driver.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Tested-by: Frank Wunderlich <frank-w@public-files.de>
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/Kconfig | 2 +
- drivers/net/ethernet/mediatek/Makefile | 2 +-
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 61 +++++-
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 93 +--------
- drivers/net/ethernet/mediatek/mtk_sgmii.c | 217 --------------------
- 5 files changed, 56 insertions(+), 319 deletions(-)
- delete mode 100644 drivers/net/ethernet/mediatek/mtk_sgmii.c
-
---- a/drivers/net/ethernet/mediatek/Kconfig
-+++ b/drivers/net/ethernet/mediatek/Kconfig
-@@ -19,6 +19,8 @@ config NET_MEDIATEK_SOC
- select DIMLIB
- select PAGE_POOL
- select PAGE_POOL_STATS
-+ select PCS_MTK_LYNXI
-+ select REGMAP_MMIO
- help
- This driver supports the gigabit ethernet MACs in the
- MediaTek SoC family.
---- a/drivers/net/ethernet/mediatek/Makefile
-+++ b/drivers/net/ethernet/mediatek/Makefile
-@@ -4,7 +4,7 @@
- #
-
- obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o
--mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o mtk_ppe_offload.o
-+mtk_eth-y := mtk_eth_soc.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o mtk_ppe_offload.o
- mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed.o mtk_wed_mcu.o mtk_wed_wo.o
- ifdef CONFIG_DEBUG_FS
- mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed_debugfs.o
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -20,6 +20,7 @@
- #include <linux/interrupt.h>
- #include <linux/pinctrl/devinfo.h>
- #include <linux/phylink.h>
-+#include <linux/pcs/pcs-mtk-lynxi.h>
- #include <linux/jhash.h>
- #include <linux/bitfield.h>
- #include <net/dsa.h>
-@@ -400,7 +401,7 @@ static struct phylink_pcs *mtk_mac_selec
- sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ?
- 0 : mac->id;
-
-- return mtk_sgmii_select_pcs(eth->sgmii, sid);
-+ return eth->sgmii_pcs[sid];
- }
-
- return NULL;
-@@ -4016,8 +4017,17 @@ static int mtk_unreg_dev(struct mtk_eth
- return 0;
- }
-
-+static void mtk_sgmii_destroy(struct mtk_eth *eth)
-+{
-+ int i;
-+
-+ for (i = 0; i < MTK_MAX_DEVS; i++)
-+ mtk_pcs_lynxi_destroy(eth->sgmii_pcs[i]);
-+}
-+
- static int mtk_cleanup(struct mtk_eth *eth)
- {
-+ mtk_sgmii_destroy(eth);
- mtk_unreg_dev(eth);
- mtk_free_dev(eth);
- cancel_work_sync(ð->pending_work);
-@@ -4457,6 +4467,36 @@ void mtk_eth_set_dma_device(struct mtk_e
- rtnl_unlock();
- }
-
-+static int mtk_sgmii_init(struct mtk_eth *eth)
-+{
-+ struct device_node *np;
-+ struct regmap *regmap;
-+ u32 flags;
-+ int i;
-+
-+ for (i = 0; i < MTK_MAX_DEVS; i++) {
-+ np = of_parse_phandle(eth->dev->of_node, "mediatek,sgmiisys", i);
-+ if (!np)
-+ break;
-+
-+ regmap = syscon_node_to_regmap(np);
-+ flags = 0;
-+ if (of_property_read_bool(np, "mediatek,pnswap"))
-+ flags |= MTK_SGMII_FLAG_PN_SWAP;
-+
-+ of_node_put(np);
-+
-+ if (IS_ERR(regmap))
-+ return PTR_ERR(regmap);
-+
-+ eth->sgmii_pcs[i] = mtk_pcs_lynxi_create(eth->dev, regmap,
-+ eth->soc->ana_rgc3,
-+ flags);
-+ }
-+
-+ return 0;
-+}
-+
- static int mtk_probe(struct platform_device *pdev)
- {
- struct resource *res = NULL;
-@@ -4520,13 +4560,7 @@ static int mtk_probe(struct platform_dev
- }
-
- if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) {
-- eth->sgmii = devm_kzalloc(eth->dev, sizeof(*eth->sgmii),
-- GFP_KERNEL);
-- if (!eth->sgmii)
-- return -ENOMEM;
--
-- err = mtk_sgmii_init(eth->sgmii, pdev->dev.of_node,
-- eth->soc->ana_rgc3);
-+ err = mtk_sgmii_init(eth);
-
- if (err)
- return err;
-@@ -4537,14 +4571,17 @@ static int mtk_probe(struct platform_dev
- "mediatek,pctl");
- if (IS_ERR(eth->pctl)) {
- dev_err(&pdev->dev, "no pctl regmap found\n");
-- return PTR_ERR(eth->pctl);
-+ err = PTR_ERR(eth->pctl);
-+ goto err_destroy_sgmii;
- }
- }
-
- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-- if (!res)
-- return -EINVAL;
-+ if (!res) {
-+ err = -EINVAL;
-+ goto err_destroy_sgmii;
-+ }
- }
-
- if (eth->soc->offload_version) {
-@@ -4703,6 +4740,8 @@ err_deinit_hw:
- mtk_hw_deinit(eth);
- err_wed_exit:
- mtk_wed_exit();
-+err_destroy_sgmii:
-+ mtk_sgmii_destroy(eth);
-
- return err;
- }
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -510,65 +510,6 @@
- #define ETHSYS_DMA_AG_MAP_QDMA BIT(1)
- #define ETHSYS_DMA_AG_MAP_PPE BIT(2)
-
--/* SGMII subsystem config registers */
--/* BMCR (low 16) BMSR (high 16) */
--#define SGMSYS_PCS_CONTROL_1 0x0
--#define SGMII_BMCR GENMASK(15, 0)
--#define SGMII_BMSR GENMASK(31, 16)
--#define SGMII_AN_RESTART BIT(9)
--#define SGMII_ISOLATE BIT(10)
--#define SGMII_AN_ENABLE BIT(12)
--#define SGMII_LINK_STATYS BIT(18)
--#define SGMII_AN_ABILITY BIT(19)
--#define SGMII_AN_COMPLETE BIT(21)
--#define SGMII_PCS_FAULT BIT(23)
--#define SGMII_AN_EXPANSION_CLR BIT(30)
--
--#define SGMSYS_PCS_ADVERTISE 0x8
--#define SGMII_ADVERTISE GENMASK(15, 0)
--#define SGMII_LPA GENMASK(31, 16)
--
--/* Register to programmable link timer, the unit in 2 * 8ns */
--#define SGMSYS_PCS_LINK_TIMER 0x18
--#define SGMII_LINK_TIMER_MASK GENMASK(19, 0)
--#define SGMII_LINK_TIMER_DEFAULT (0x186a0 & SGMII_LINK_TIMER_MASK)
--
--/* Register to control remote fault */
--#define SGMSYS_SGMII_MODE 0x20
--#define SGMII_IF_MODE_SGMII BIT(0)
--#define SGMII_SPEED_DUPLEX_AN BIT(1)
--#define SGMII_SPEED_MASK GENMASK(3, 2)
--#define SGMII_SPEED_10 FIELD_PREP(SGMII_SPEED_MASK, 0)
--#define SGMII_SPEED_100 FIELD_PREP(SGMII_SPEED_MASK, 1)
--#define SGMII_SPEED_1000 FIELD_PREP(SGMII_SPEED_MASK, 2)
--#define SGMII_DUPLEX_HALF BIT(4)
--#define SGMII_IF_MODE_BIT5 BIT(5)
--#define SGMII_REMOTE_FAULT_DIS BIT(8)
--#define SGMII_CODE_SYNC_SET_VAL BIT(9)
--#define SGMII_CODE_SYNC_SET_EN BIT(10)
--#define SGMII_SEND_AN_ERROR_EN BIT(11)
--#define SGMII_IF_MODE_MASK GENMASK(5, 1)
--
--/* Register to reset SGMII design */
--#define SGMII_RESERVED_0 0x34
--#define SGMII_SW_RESET BIT(0)
--
--/* Register to set SGMII speed, ANA RG_ Control Signals III*/
--#define SGMSYS_ANA_RG_CS3 0x2028
--#define RG_PHY_SPEED_MASK (BIT(2) | BIT(3))
--#define RG_PHY_SPEED_1_25G 0x0
--#define RG_PHY_SPEED_3_125G BIT(2)
--
--/* Register to power up QPHY */
--#define SGMSYS_QPHY_PWR_STATE_CTRL 0xe8
--#define SGMII_PHYA_PWD BIT(4)
--
--/* Register to QPHY wrapper control */
--#define SGMSYS_QPHY_WRAP_CTRL 0xec
--#define SGMII_PN_SWAP_MASK GENMASK(1, 0)
--#define SGMII_PN_SWAP_TX_RX (BIT(0) | BIT(1))
--#define MTK_SGMII_FLAG_PN_SWAP BIT(0)
--
- /* Infrasys subsystem config registers */
- #define INFRA_MISC2 0x70c
- #define CO_QPHY_SEL BIT(0)
-@@ -1102,31 +1043,6 @@ struct mtk_soc_data {
- /* currently no SoC has more than 2 macs */
- #define MTK_MAX_DEVS 2
-
--/* struct mtk_pcs - This structure holds each sgmii regmap and associated
-- * data
-- * @regmap: The register map pointing at the range used to setup
-- * SGMII modes
-- * @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap
-- * @interface: Currently configured interface mode
-- * @pcs: Phylink PCS structure
-- * @flags: Flags indicating hardware properties
-- */
--struct mtk_pcs {
-- struct regmap *regmap;
-- u32 ana_rgc3;
-- phy_interface_t interface;
-- struct phylink_pcs pcs;
-- u32 flags;
--};
--
--/* struct mtk_sgmii - This is the structure holding sgmii regmap and its
-- * characteristics
-- * @pcs Array of individual PCS structures
-- */
--struct mtk_sgmii {
-- struct mtk_pcs pcs[MTK_MAX_DEVS];
--};
--
- /* struct mtk_eth - This is the main datasructure for holding the state
- * of the driver
- * @dev: The device pointer
-@@ -1146,6 +1062,7 @@ struct mtk_sgmii {
- * MII modes
- * @infra: The register map pointing at the range used to setup
- * SGMII and GePHY path
-+ * @sgmii_pcs: Pointers to mtk-pcs-lynxi phylink_pcs instances
- * @pctl: The register map pointing at the range used to setup
- * GMAC port drive/slew values
- * @dma_refcnt: track how many netdevs are using the DMA engine
-@@ -1186,8 +1103,8 @@ struct mtk_eth {
- u32 msg_enable;
- unsigned long sysclk;
- struct regmap *ethsys;
-- struct regmap *infra;
-- struct mtk_sgmii *sgmii;
-+ struct regmap *infra;
-+ struct phylink_pcs *sgmii_pcs[MTK_MAX_DEVS];
- struct regmap *pctl;
- bool hwlro;
- refcount_t dma_refcnt;
-@@ -1349,10 +1266,6 @@ void mtk_stats_update_mac(struct mtk_mac
- void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg);
- u32 mtk_r32(struct mtk_eth *eth, unsigned reg);
-
--struct phylink_pcs *mtk_sgmii_select_pcs(struct mtk_sgmii *ss, int id);
--int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *np,
-- u32 ana_rgc3);
--
- int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id);
- int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id);
- int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id);
---- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
-+++ /dev/null
-@@ -1,217 +0,0 @@
--// SPDX-License-Identifier: GPL-2.0
--// Copyright (c) 2018-2019 MediaTek Inc.
--
--/* A library for MediaTek SGMII circuit
-- *
-- * Author: Sean Wang <sean.wang@mediatek.com>
-- *
-- */
--
--#include <linux/mfd/syscon.h>
--#include <linux/of.h>
--#include <linux/phylink.h>
--#include <linux/regmap.h>
--
--#include "mtk_eth_soc.h"
--
--static struct mtk_pcs *pcs_to_mtk_pcs(struct phylink_pcs *pcs)
--{
-- return container_of(pcs, struct mtk_pcs, pcs);
--}
--
--static void mtk_pcs_get_state(struct phylink_pcs *pcs,
-- struct phylink_link_state *state)
--{
-- struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
-- unsigned int bm, adv;
--
-- /* Read the BMSR and LPA */
-- regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm);
-- regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv);
--
-- phylink_mii_c22_pcs_decode_state(state, FIELD_GET(SGMII_BMSR, bm),
-- FIELD_GET(SGMII_LPA, adv));
--}
--
--static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
-- phy_interface_t interface,
-- const unsigned long *advertising,
-- bool permit_pause_to_mac)
--{
-- bool mode_changed = false, changed, use_an;
-- struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
-- unsigned int rgc3, sgm_mode, bmcr;
-- int advertise, link_timer;
--
-- advertise = phylink_mii_c22_pcs_encode_advertisement(interface,
-- advertising);
-- if (advertise < 0)
-- return advertise;
--
-- /* Clearing IF_MODE_BIT0 switches the PCS to BASE-X mode, and
-- * we assume that fixes it's speed at bitrate = line rate (in
-- * other words, 1000Mbps or 2500Mbps).
-- */
-- if (interface == PHY_INTERFACE_MODE_SGMII) {
-- sgm_mode = SGMII_IF_MODE_SGMII;
-- if (phylink_autoneg_inband(mode)) {
-- sgm_mode |= SGMII_REMOTE_FAULT_DIS |
-- SGMII_SPEED_DUPLEX_AN;
-- use_an = true;
-- } else {
-- use_an = false;
-- }
-- } else if (phylink_autoneg_inband(mode)) {
-- /* 1000base-X or 2500base-X autoneg */
-- sgm_mode = SGMII_REMOTE_FAULT_DIS;
-- use_an = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
-- advertising);
-- } else {
-- /* 1000base-X or 2500base-X without autoneg */
-- sgm_mode = 0;
-- use_an = false;
-- }
--
-- if (use_an) {
-- bmcr = SGMII_AN_ENABLE;
-- } else {
-- bmcr = 0;
-- }
--
-- if (mpcs->interface != interface) {
-- link_timer = phylink_get_link_timer_ns(interface);
-- if (link_timer < 0)
-- return link_timer;
--
-- /* PHYA power down */
-- regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
-- SGMII_PHYA_PWD, SGMII_PHYA_PWD);
--
-- if (mpcs->flags & MTK_SGMII_FLAG_PN_SWAP)
-- regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_WRAP_CTRL,
-- SGMII_PN_SWAP_MASK,
-- SGMII_PN_SWAP_TX_RX);
--
-- /* Reset SGMII PCS state */
-- regmap_update_bits(mpcs->regmap, SGMII_RESERVED_0,
-- SGMII_SW_RESET, SGMII_SW_RESET);
--
-- if (interface == PHY_INTERFACE_MODE_2500BASEX)
-- rgc3 = RG_PHY_SPEED_3_125G;
-- else
-- rgc3 = 0;
--
-- /* Configure the underlying interface speed */
-- regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
-- RG_PHY_SPEED_3_125G, rgc3);
--
-- /* Setup the link timer */
-- regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, link_timer / 2 / 8);
--
-- mpcs->interface = interface;
-- mode_changed = true;
-- }
--
-- /* Update the advertisement, noting whether it has changed */
-- regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE,
-- SGMII_ADVERTISE, advertise, &changed);
--
-- /* Update the sgmsys mode register */
-- regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
-- SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN |
-- SGMII_IF_MODE_SGMII, sgm_mode);
--
-- /* Update the BMCR */
-- regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
-- SGMII_AN_ENABLE, bmcr);
--
-- /* Release PHYA power down state
-- * Only removing bit SGMII_PHYA_PWD isn't enough.
-- * There are cases when the SGMII_PHYA_PWD register contains 0x9 which
-- * prevents SGMII from working. The SGMII still shows link but no traffic
-- * can flow. Writing 0x0 to the PHYA_PWD register fix the issue. 0x0 was
-- * taken from a good working state of the SGMII interface.
-- * Unknown how much the QPHY needs but it is racy without a sleep.
-- * Tested on mt7622 & mt7986.
-- */
-- usleep_range(50, 100);
-- regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
--
-- return changed || mode_changed;
--}
--
--static void mtk_pcs_restart_an(struct phylink_pcs *pcs)
--{
-- struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
--
-- regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
-- SGMII_AN_RESTART, SGMII_AN_RESTART);
--}
--
--static void mtk_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
-- phy_interface_t interface, int speed, int duplex)
--{
-- struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
-- unsigned int sgm_mode;
--
-- if (!phylink_autoneg_inband(mode)) {
-- /* Force the speed and duplex setting */
-- if (speed == SPEED_10)
-- sgm_mode = SGMII_SPEED_10;
-- else if (speed == SPEED_100)
-- sgm_mode = SGMII_SPEED_100;
-- else
-- sgm_mode = SGMII_SPEED_1000;
--
-- if (duplex != DUPLEX_FULL)
-- sgm_mode |= SGMII_DUPLEX_HALF;
--
-- regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
-- SGMII_DUPLEX_HALF | SGMII_SPEED_MASK,
-- sgm_mode);
-- }
--}
--
--static const struct phylink_pcs_ops mtk_pcs_ops = {
-- .pcs_get_state = mtk_pcs_get_state,
-- .pcs_config = mtk_pcs_config,
-- .pcs_an_restart = mtk_pcs_restart_an,
-- .pcs_link_up = mtk_pcs_link_up,
--};
--
--int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3)
--{
-- struct device_node *np;
-- int i;
--
-- for (i = 0; i < MTK_MAX_DEVS; i++) {
-- np = of_parse_phandle(r, "mediatek,sgmiisys", i);
-- if (!np)
-- break;
--
-- ss->pcs[i].ana_rgc3 = ana_rgc3;
-- ss->pcs[i].regmap = syscon_node_to_regmap(np);
--
-- ss->pcs[i].flags = 0;
-- if (of_property_read_bool(np, "mediatek,pnswap"))
-- ss->pcs[i].flags |= MTK_SGMII_FLAG_PN_SWAP;
--
-- of_node_put(np);
-- if (IS_ERR(ss->pcs[i].regmap))
-- return PTR_ERR(ss->pcs[i].regmap);
--
-- ss->pcs[i].pcs.ops = &mtk_pcs_ops;
-- ss->pcs[i].pcs.poll = true;
-- ss->pcs[i].interface = PHY_INTERFACE_MODE_NA;
-- }
--
-- return 0;
--}
--
--struct phylink_pcs *mtk_sgmii_select_pcs(struct mtk_sgmii *ss, int id)
--{
-- if (!ss->pcs[id].regmap)
-- return NULL;
--
-- return &ss->pcs[id].pcs;
--}
+++ /dev/null
-From f5af7931d2a2cae66d0f9dad4ba517b1b00620b3 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Wed, 19 Apr 2023 19:07:23 +0100
-Subject: [PATCH] net: mtk_eth_soc: use WO firmware for MT7981
-
-In order to support wireless offloading on MT7981 we need to load the
-appropriate firmware. Recognize MT7981 and load mt7981_wo.bin.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
----
- drivers/net/ethernet/mediatek/mtk_wed_mcu.c | 7 ++++++-
- drivers/net/ethernet/mediatek/mtk_wed_wo.h | 1 +
- 2 files changed, 7 insertions(+), 1 deletion(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
-@@ -326,7 +326,11 @@ mtk_wed_mcu_load_firmware(struct mtk_wed
- wo->hw->index + 1);
-
- /* load firmware */
-- fw_name = wo->hw->index ? MT7986_FIRMWARE_WO1 : MT7986_FIRMWARE_WO0;
-+ if (of_device_is_compatible(wo->hw->node, "mediatek,mt7981-wed"))
-+ fw_name = MT7981_FIRMWARE_WO;
-+ else
-+ fw_name = wo->hw->index ? MT7986_FIRMWARE_WO1 : MT7986_FIRMWARE_WO0;
-+
- ret = request_firmware(&fw, fw_name, wo->hw->dev);
- if (ret)
- return ret;
-@@ -386,5 +390,6 @@ int mtk_wed_mcu_init(struct mtk_wed_wo *
- 100, MTK_FW_DL_TIMEOUT);
- }
-
-+MODULE_FIRMWARE(MT7981_FIRMWARE_WO);
- MODULE_FIRMWARE(MT7986_FIRMWARE_WO0);
- MODULE_FIRMWARE(MT7986_FIRMWARE_WO1);
---- a/drivers/net/ethernet/mediatek/mtk_wed_wo.h
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h
-@@ -88,6 +88,7 @@ enum mtk_wed_dummy_cr_idx {
- MTK_WED_DUMMY_CR_WO_STATUS,
- };
-
-+#define MT7981_FIRMWARE_WO "mediatek/mt7981_wo.bin"
- #define MT7986_FIRMWARE_WO0 "mediatek/mt7986_wo_0.bin"
- #define MT7986_FIRMWARE_WO1 "mediatek/mt7986_wo_1.bin"
-
+++ /dev/null
-From 7c83e28f10830aa5105c25eaabe890e3adac36aa Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Tue, 9 May 2023 03:20:06 +0200
-Subject: [PATCH] net: ethernet: mtk_eth_soc: fix NULL pointer dereference
-
-Check for NULL pointer to avoid kernel crashing in case of missing WO
-firmware in case only a single WEDv2 device has been initialized, e.g. on
-MT7981 which can connect just one wireless frontend.
-
-Fixes: 86ce0d09e424 ("net: ethernet: mtk_eth_soc: use WO firmware for MT7981")
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Simon Horman <simon.horman@corigine.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/ethernet/mediatek/mtk_wed.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -647,7 +647,7 @@ __mtk_wed_detach(struct mtk_wed_device *
- BIT(hw->index), BIT(hw->index));
- }
-
-- if (!hw_list[!hw->index]->wed_dev &&
-+ if ((!hw_list[!hw->index] || !hw_list[!hw->index]->wed_dev) &&
- hw->eth->dma_dev != hw->eth->dev)
- mtk_eth_set_dma_device(hw->eth, hw->eth->dev);
-
+++ /dev/null
-From f601293f37c4be618c5efaef85d2ee21f97e82e0 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Sun, 19 Mar 2023 12:57:35 +0000
-Subject: [PATCH 092/250] net: ethernet: mtk_eth_soc: ppe: add support for flow
- accounting
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The PPE units found in MT7622 and newer support packet and byte
-accounting of hw-offloaded flows. Add support for reading those counters
-as found in MediaTek's SDK[1].
-
-[1]: https://git01.mediatek.com/plugins/gitiles/openwrt/feeds/mtk-openwrt-feeds/+/bc6a6a375c800dc2b80e1a325a2c732d1737df92
-Tested-by: Bjørn Mork <bjorn@mork.no>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 8 +-
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 3 +
- drivers/net/ethernet/mediatek/mtk_ppe.c | 114 +++++++++++++++++-
- drivers/net/ethernet/mediatek/mtk_ppe.h | 25 +++-
- .../net/ethernet/mediatek/mtk_ppe_debugfs.c | 9 +-
- .../net/ethernet/mediatek/mtk_ppe_offload.c | 8 ++
- drivers/net/ethernet/mediatek/mtk_ppe_regs.h | 14 +++
- 7 files changed, 172 insertions(+), 9 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -4691,8 +4691,8 @@ static int mtk_probe(struct platform_dev
- for (i = 0; i < num_ppe; i++) {
- u32 ppe_addr = eth->soc->reg_map->ppe_base + i * 0x400;
-
-- eth->ppe[i] = mtk_ppe_init(eth, eth->base + ppe_addr,
-- eth->soc->offload_version, i);
-+ eth->ppe[i] = mtk_ppe_init(eth, eth->base + ppe_addr, i);
-+
- if (!eth->ppe[i]) {
- err = -ENOMEM;
- goto err_deinit_ppe;
-@@ -4816,6 +4816,7 @@ static const struct mtk_soc_data mt7622_
- .required_pctl = false,
- .offload_version = 2,
- .hash_offset = 2,
-+ .has_accounting = true,
- .foe_entry_size = sizeof(struct mtk_foe_entry) - 16,
- .txrx = {
- .txd_size = sizeof(struct mtk_tx_dma),
-@@ -4853,6 +4854,7 @@ static const struct mtk_soc_data mt7629_
- .hw_features = MTK_HW_FEATURES,
- .required_clks = MT7629_CLKS_BITMAP,
- .required_pctl = false,
-+ .has_accounting = true,
- .txrx = {
- .txd_size = sizeof(struct mtk_tx_dma),
- .rxd_size = sizeof(struct mtk_rx_dma),
-@@ -4873,6 +4875,7 @@ static const struct mtk_soc_data mt7981_
- .offload_version = 2,
- .hash_offset = 4,
- .foe_entry_size = sizeof(struct mtk_foe_entry),
-+ .has_accounting = true,
- .txrx = {
- .txd_size = sizeof(struct mtk_tx_dma_v2),
- .rxd_size = sizeof(struct mtk_rx_dma_v2),
-@@ -4893,6 +4896,7 @@ static const struct mtk_soc_data mt7986_
- .offload_version = 2,
- .hash_offset = 4,
- .foe_entry_size = sizeof(struct mtk_foe_entry),
-+ .has_accounting = true,
- .txrx = {
- .txd_size = sizeof(struct mtk_tx_dma_v2),
- .rxd_size = sizeof(struct mtk_rx_dma_v2),
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -1011,6 +1011,8 @@ struct mtk_reg_map {
- * the extra setup for those pins used by GMAC.
- * @hash_offset Flow table hash offset.
- * @foe_entry_size Foe table entry size.
-+ * @has_accounting Bool indicating support for accounting of
-+ * offloaded flows.
- * @txd_size Tx DMA descriptor size.
- * @rxd_size Rx DMA descriptor size.
- * @rx_irq_done_mask Rx irq done register mask.
-@@ -1028,6 +1030,7 @@ struct mtk_soc_data {
- u8 hash_offset;
- u16 foe_entry_size;
- netdev_features_t hw_features;
-+ bool has_accounting;
- struct {
- u32 txd_size;
- u32 rxd_size;
---- a/drivers/net/ethernet/mediatek/mtk_ppe.c
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
-@@ -74,6 +74,48 @@ static int mtk_ppe_wait_busy(struct mtk_
- return ret;
- }
-
-+static int mtk_ppe_mib_wait_busy(struct mtk_ppe *ppe)
-+{
-+ int ret;
-+ u32 val;
-+
-+ ret = readl_poll_timeout(ppe->base + MTK_PPE_MIB_SER_CR, val,
-+ !(val & MTK_PPE_MIB_SER_CR_ST),
-+ 20, MTK_PPE_WAIT_TIMEOUT_US);
-+
-+ if (ret)
-+ dev_err(ppe->dev, "MIB table busy");
-+
-+ return ret;
-+}
-+
-+static int mtk_mib_entry_read(struct mtk_ppe *ppe, u16 index, u64 *bytes, u64 *packets)
-+{
-+ u32 byte_cnt_low, byte_cnt_high, pkt_cnt_low, pkt_cnt_high;
-+ u32 val, cnt_r0, cnt_r1, cnt_r2;
-+ int ret;
-+
-+ val = FIELD_PREP(MTK_PPE_MIB_SER_CR_ADDR, index) | MTK_PPE_MIB_SER_CR_ST;
-+ ppe_w32(ppe, MTK_PPE_MIB_SER_CR, val);
-+
-+ ret = mtk_ppe_mib_wait_busy(ppe);
-+ if (ret)
-+ return ret;
-+
-+ cnt_r0 = readl(ppe->base + MTK_PPE_MIB_SER_R0);
-+ cnt_r1 = readl(ppe->base + MTK_PPE_MIB_SER_R1);
-+ cnt_r2 = readl(ppe->base + MTK_PPE_MIB_SER_R2);
-+
-+ byte_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R0_BYTE_CNT_LOW, cnt_r0);
-+ byte_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R1_BYTE_CNT_HIGH, cnt_r1);
-+ pkt_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R1_PKT_CNT_LOW, cnt_r1);
-+ pkt_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R2_PKT_CNT_HIGH, cnt_r2);
-+ *bytes = ((u64)byte_cnt_high << 32) | byte_cnt_low;
-+ *packets = (pkt_cnt_high << 16) | pkt_cnt_low;
-+
-+ return 0;
-+}
-+
- static void mtk_ppe_cache_clear(struct mtk_ppe *ppe)
- {
- ppe_set(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_CLEAR);
-@@ -459,6 +501,13 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp
- hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_INVALID);
- dma_wmb();
- mtk_ppe_cache_clear(ppe);
-+ if (ppe->accounting) {
-+ struct mtk_foe_accounting *acct;
-+
-+ acct = ppe->acct_table + entry->hash * sizeof(*acct);
-+ acct->packets = 0;
-+ acct->bytes = 0;
-+ }
- }
- entry->hash = 0xffff;
-
-@@ -566,6 +615,9 @@ __mtk_foe_entry_commit(struct mtk_ppe *p
- wmb();
- hwe->ib1 = entry->ib1;
-
-+ if (ppe->accounting)
-+ *mtk_foe_entry_ib2(eth, hwe) |= MTK_FOE_IB2_MIB_CNT;
-+
- dma_wmb();
-
- mtk_ppe_cache_clear(ppe);
-@@ -757,11 +809,39 @@ int mtk_ppe_prepare_reset(struct mtk_ppe
- return mtk_ppe_wait_busy(ppe);
- }
-
--struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
-- int version, int index)
-+struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index,
-+ struct mtk_foe_accounting *diff)
-+{
-+ struct mtk_foe_accounting *acct;
-+ int size = sizeof(struct mtk_foe_accounting);
-+ u64 bytes, packets;
-+
-+ if (!ppe->accounting)
-+ return NULL;
-+
-+ if (mtk_mib_entry_read(ppe, index, &bytes, &packets))
-+ return NULL;
-+
-+ acct = ppe->acct_table + index * size;
-+
-+ acct->bytes += bytes;
-+ acct->packets += packets;
-+
-+ if (diff) {
-+ diff->bytes = bytes;
-+ diff->packets = packets;
-+ }
-+
-+ return acct;
-+}
-+
-+struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index)
- {
-+ bool accounting = eth->soc->has_accounting;
- const struct mtk_soc_data *soc = eth->soc;
-+ struct mtk_foe_accounting *acct;
- struct device *dev = eth->dev;
-+ struct mtk_mib_entry *mib;
- struct mtk_ppe *ppe;
- u32 foe_flow_size;
- void *foe;
-@@ -778,7 +858,8 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_
- ppe->base = base;
- ppe->eth = eth;
- ppe->dev = dev;
-- ppe->version = version;
-+ ppe->version = eth->soc->offload_version;
-+ ppe->accounting = accounting;
-
- foe = dmam_alloc_coherent(ppe->dev,
- MTK_PPE_ENTRIES * soc->foe_entry_size,
-@@ -794,6 +875,23 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_
- if (!ppe->foe_flow)
- goto err_free_l2_flows;
-
-+ if (accounting) {
-+ mib = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*mib),
-+ &ppe->mib_phys, GFP_KERNEL);
-+ if (!mib)
-+ return NULL;
-+
-+ ppe->mib_table = mib;
-+
-+ acct = devm_kzalloc(dev, MTK_PPE_ENTRIES * sizeof(*acct),
-+ GFP_KERNEL);
-+
-+ if (!acct)
-+ return NULL;
-+
-+ ppe->acct_table = acct;
-+ }
-+
- mtk_ppe_debugfs_init(ppe, index);
-
- return ppe;
-@@ -923,6 +1021,16 @@ void mtk_ppe_start(struct mtk_ppe *ppe)
- ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT1, 0xcb777);
- ppe_w32(ppe, MTK_PPE_SBW_CTRL, 0x7f);
- }
-+
-+ if (ppe->accounting && ppe->mib_phys) {
-+ ppe_w32(ppe, MTK_PPE_MIB_TB_BASE, ppe->mib_phys);
-+ ppe_m32(ppe, MTK_PPE_MIB_CFG, MTK_PPE_MIB_CFG_EN,
-+ MTK_PPE_MIB_CFG_EN);
-+ ppe_m32(ppe, MTK_PPE_MIB_CFG, MTK_PPE_MIB_CFG_RD_CLR,
-+ MTK_PPE_MIB_CFG_RD_CLR);
-+ ppe_m32(ppe, MTK_PPE_MIB_CACHE_CTL, MTK_PPE_MIB_CACHE_CTL_EN,
-+ MTK_PPE_MIB_CFG_RD_CLR);
-+ }
- }
-
- int mtk_ppe_stop(struct mtk_ppe *ppe)
---- a/drivers/net/ethernet/mediatek/mtk_ppe.h
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
-@@ -57,6 +57,7 @@ enum {
- #define MTK_FOE_IB2_MULTICAST BIT(8)
-
- #define MTK_FOE_IB2_WDMA_QID2 GENMASK(13, 12)
-+#define MTK_FOE_IB2_MIB_CNT BIT(15)
- #define MTK_FOE_IB2_WDMA_DEVIDX BIT(16)
- #define MTK_FOE_IB2_WDMA_WINFO BIT(17)
-
-@@ -285,16 +286,34 @@ struct mtk_flow_entry {
- unsigned long cookie;
- };
-
-+struct mtk_mib_entry {
-+ u32 byt_cnt_l;
-+ u16 byt_cnt_h;
-+ u32 pkt_cnt_l;
-+ u8 pkt_cnt_h;
-+ u8 _rsv0;
-+ u32 _rsv1;
-+} __packed;
-+
-+struct mtk_foe_accounting {
-+ u64 bytes;
-+ u64 packets;
-+};
-+
- struct mtk_ppe {
- struct mtk_eth *eth;
- struct device *dev;
- void __iomem *base;
- int version;
- char dirname[5];
-+ bool accounting;
-
- void *foe_table;
- dma_addr_t foe_phys;
-
-+ struct mtk_mib_entry *mib_table;
-+ dma_addr_t mib_phys;
-+
- u16 foe_check_time[MTK_PPE_ENTRIES];
- struct hlist_head *foe_flow;
-
-@@ -303,8 +322,8 @@ struct mtk_ppe {
- void *acct_table;
- };
-
--struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
-- int version, int index);
-+struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index);
-+
- void mtk_ppe_deinit(struct mtk_eth *eth);
- void mtk_ppe_start(struct mtk_ppe *ppe);
- int mtk_ppe_stop(struct mtk_ppe *ppe);
-@@ -359,5 +378,7 @@ int mtk_foe_entry_commit(struct mtk_ppe
- 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, int index);
-+struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index,
-+ struct mtk_foe_accounting *diff);
-
- #endif
---- a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
-@@ -82,6 +82,7 @@ mtk_ppe_debugfs_foe_show(struct seq_file
- struct mtk_foe_entry *entry = mtk_foe_get_entry(ppe, i);
- struct mtk_foe_mac_info *l2;
- struct mtk_flow_addr_info ai = {};
-+ struct mtk_foe_accounting *acct;
- unsigned char h_source[ETH_ALEN];
- unsigned char h_dest[ETH_ALEN];
- int type, state;
-@@ -95,6 +96,8 @@ mtk_ppe_debugfs_foe_show(struct seq_file
- if (bind && state != MTK_FOE_STATE_BIND)
- continue;
-
-+ acct = mtk_foe_entry_get_mib(ppe, i, NULL);
-+
- type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
- seq_printf(m, "%05x %s %7s", i,
- mtk_foe_entry_state_str(state),
-@@ -153,9 +156,11 @@ mtk_ppe_debugfs_foe_show(struct seq_file
- *((__be16 *)&h_dest[4]) = htons(l2->dest_mac_lo);
-
- seq_printf(m, " eth=%pM->%pM etype=%04x"
-- " vlan=%d,%d ib1=%08x ib2=%08x\n",
-+ " vlan=%d,%d ib1=%08x ib2=%08x"
-+ " packets=%llu bytes=%llu\n",
- h_source, h_dest, ntohs(l2->etype),
-- l2->vlan1, l2->vlan2, entry->ib1, ib2);
-+ l2->vlan1, l2->vlan2, entry->ib1, ib2,
-+ acct ? acct->packets : 0, acct ? acct->bytes : 0);
- }
-
- return 0;
---- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-@@ -497,6 +497,7 @@ static int
- mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f)
- {
- struct mtk_flow_entry *entry;
-+ struct mtk_foe_accounting diff;
- u32 idle;
-
- entry = rhashtable_lookup(ð->flow_table, &f->cookie,
-@@ -507,6 +508,13 @@ mtk_flow_offload_stats(struct mtk_eth *e
- idle = mtk_foe_entry_idle_time(eth->ppe[entry->ppe_index], entry);
- f->stats.lastused = jiffies - idle * HZ;
-
-+ if (entry->hash != 0xFFFF &&
-+ mtk_foe_entry_get_mib(eth->ppe[entry->ppe_index], entry->hash,
-+ &diff)) {
-+ f->stats.pkts += diff.packets;
-+ f->stats.bytes += diff.bytes;
-+ }
-+
- return 0;
- }
-
---- a/drivers/net/ethernet/mediatek/mtk_ppe_regs.h
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe_regs.h
-@@ -149,6 +149,20 @@ enum {
-
- #define MTK_PPE_MIB_TB_BASE 0x338
-
-+#define MTK_PPE_MIB_SER_CR 0x33C
-+#define MTK_PPE_MIB_SER_CR_ST BIT(16)
-+#define MTK_PPE_MIB_SER_CR_ADDR GENMASK(13, 0)
-+
-+#define MTK_PPE_MIB_SER_R0 0x340
-+#define MTK_PPE_MIB_SER_R0_BYTE_CNT_LOW GENMASK(31, 0)
-+
-+#define MTK_PPE_MIB_SER_R1 0x344
-+#define MTK_PPE_MIB_SER_R1_PKT_CNT_LOW GENMASK(31, 16)
-+#define MTK_PPE_MIB_SER_R1_BYTE_CNT_HIGH GENMASK(15, 0)
-+
-+#define MTK_PPE_MIB_SER_R2 0x348
-+#define MTK_PPE_MIB_SER_R2_PKT_CNT_HIGH GENMASK(23, 0)
-+
- #define MTK_PPE_MIB_CACHE_CTL 0x350
- #define MTK_PPE_MIB_CACHE_CTL_EN BIT(0)
- #define MTK_PPE_MIB_CACHE_CTL_FLUSH BIT(2)
+++ /dev/null
-From 88a0fd5927b7c2c7aecd6dc747d898eb38043d2b Mon Sep 17 00:00:00 2001
-From: Felix Fietkau <nbd@nbd.name>
-Date: Thu, 20 Apr 2023 22:06:42 +0100
-Subject: [PATCH 093/250] net: mtk_eth_soc: mediatek: fix ppe flow accounting
- for v1 hardware
-
-Older chips (like MT7622) use a different bit in ib2 to enable hardware
-counter support. Add macros for both and select the appropriate bit.
-
-Fixes: 3fbe4d8c0e53 ("net: ethernet: mtk_eth_soc: ppe: add support for flow accounting")
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/ethernet/mediatek/mtk_ppe.c | 10 ++++++++--
- drivers/net/ethernet/mediatek/mtk_ppe.h | 3 ++-
- 2 files changed, 10 insertions(+), 3 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_ppe.c
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
-@@ -599,6 +599,7 @@ __mtk_foe_entry_commit(struct mtk_ppe *p
- struct mtk_eth *eth = ppe->eth;
- u16 timestamp = mtk_eth_timestamp(eth);
- struct mtk_foe_entry *hwe;
-+ u32 val;
-
- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
- entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP_V2;
-@@ -615,8 +616,13 @@ __mtk_foe_entry_commit(struct mtk_ppe *p
- wmb();
- hwe->ib1 = entry->ib1;
-
-- if (ppe->accounting)
-- *mtk_foe_entry_ib2(eth, hwe) |= MTK_FOE_IB2_MIB_CNT;
-+ if (ppe->accounting) {
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-+ val = MTK_FOE_IB2_MIB_CNT_V2;
-+ else
-+ val = MTK_FOE_IB2_MIB_CNT;
-+ *mtk_foe_entry_ib2(eth, hwe) |= val;
-+ }
-
- dma_wmb();
-
---- a/drivers/net/ethernet/mediatek/mtk_ppe.h
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
-@@ -55,9 +55,10 @@ enum {
- #define MTK_FOE_IB2_PSE_QOS BIT(4)
- #define MTK_FOE_IB2_DEST_PORT GENMASK(7, 5)
- #define MTK_FOE_IB2_MULTICAST BIT(8)
-+#define MTK_FOE_IB2_MIB_CNT BIT(10)
-
- #define MTK_FOE_IB2_WDMA_QID2 GENMASK(13, 12)
--#define MTK_FOE_IB2_MIB_CNT BIT(15)
-+#define MTK_FOE_IB2_MIB_CNT_V2 BIT(15)
- #define MTK_FOE_IB2_WDMA_DEVIDX BIT(16)
- #define MTK_FOE_IB2_WDMA_WINFO BIT(17)
-
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Sun, 20 Nov 2022 23:01:00 +0100
-Subject: [PATCH] net: ethernet: mtk_eth_soc: drop generic vlan rx offload,
- only use DSA untagging
-
-Through testing I found out that hardware vlan rx offload support seems to
-have some hardware issues. At least when using multiple MACs and when receiving
-tagged packets on the secondary MAC, the hardware can sometimes start to emit
-wrong tags on the first MAC as well.
-
-In order to avoid such issues, drop the feature configuration and use the
-offload feature only for DSA hardware untagging on MT7621/MT7622 devices which
-only use one MAC.
-
-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
-@@ -1897,9 +1897,7 @@ static int mtk_poll_rx(struct napi_struc
-
- while (done < budget) {
- unsigned int pktlen, *rxdcsum;
-- bool has_hwaccel_tag = false;
- struct net_device *netdev;
-- u16 vlan_proto, vlan_tci;
- dma_addr_t dma_addr;
- u32 hash, reason;
- int mac = 0;
-@@ -2034,36 +2032,21 @@ static int mtk_poll_rx(struct napi_struc
- skb_checksum_none_assert(skb);
- skb->protocol = eth_type_trans(skb, netdev);
-
-- if (reason == MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED)
-- mtk_ppe_check_skb(eth->ppe[0], skb, hash);
--
-- if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX) {
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
-- if (trxd.rxd3 & RX_DMA_VTAG_V2) {
-- vlan_proto = RX_DMA_VPID(trxd.rxd4);
-- vlan_tci = RX_DMA_VID(trxd.rxd4);
-- has_hwaccel_tag = true;
-- }
-- } else if (trxd.rxd2 & RX_DMA_VTAG) {
-- vlan_proto = RX_DMA_VPID(trxd.rxd3);
-- vlan_tci = RX_DMA_VID(trxd.rxd3);
-- has_hwaccel_tag = true;
-- }
-- }
--
- /* When using VLAN untagging in combination with DSA, the
- * hardware treats the MTK special tag as a VLAN and untags it.
- */
-- if (has_hwaccel_tag && netdev_uses_dsa(netdev)) {
-- unsigned int port = vlan_proto & GENMASK(2, 0);
-+ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) &&
-+ (trxd.rxd2 & RX_DMA_VTAG) && netdev_uses_dsa(netdev)) {
-+ unsigned int port = RX_DMA_VPID(trxd.rxd3) & GENMASK(2, 0);
-
- if (port < ARRAY_SIZE(eth->dsa_meta) &&
- eth->dsa_meta[port])
- skb_dst_set_noref(skb, ð->dsa_meta[port]->dst);
-- } else if (has_hwaccel_tag) {
-- __vlan_hwaccel_put_tag(skb, htons(vlan_proto), vlan_tci);
- }
-
-+ if (reason == MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED)
-+ mtk_ppe_check_skb(eth->ppe[0], skb, hash);
-+
- skb_record_rx_queue(skb, 0);
- napi_gro_receive(napi, skb);
-
-@@ -2889,29 +2872,11 @@ static netdev_features_t mtk_fix_feature
-
- static int mtk_set_features(struct net_device *dev, netdev_features_t features)
- {
-- struct mtk_mac *mac = netdev_priv(dev);
-- struct mtk_eth *eth = mac->hw;
- netdev_features_t diff = dev->features ^ features;
-- int i;
-
- if ((diff & NETIF_F_LRO) && !(features & NETIF_F_LRO))
- mtk_hwlro_netdev_disable(dev);
-
-- /* Set RX VLAN offloading */
-- if (!(diff & NETIF_F_HW_VLAN_CTAG_RX))
-- return 0;
--
-- mtk_w32(eth, !!(features & NETIF_F_HW_VLAN_CTAG_RX),
-- MTK_CDMP_EG_CTRL);
--
-- /* sync features with other MAC */
-- for (i = 0; i < MTK_MAC_COUNT; i++) {
-- if (!eth->netdev[i] || eth->netdev[i] == dev)
-- continue;
-- eth->netdev[i]->features &= ~NETIF_F_HW_VLAN_CTAG_RX;
-- eth->netdev[i]->features |= features & NETIF_F_HW_VLAN_CTAG_RX;
-- }
--
- return 0;
- }
-
-@@ -3225,30 +3190,6 @@ static int mtk_open(struct net_device *d
- struct mtk_eth *eth = mac->hw;
- int i, err;
-
-- if (mtk_uses_dsa(dev) && !eth->prog) {
-- for (i = 0; i < ARRAY_SIZE(eth->dsa_meta); i++) {
-- struct metadata_dst *md_dst = eth->dsa_meta[i];
--
-- if (md_dst)
-- continue;
--
-- md_dst = metadata_dst_alloc(0, METADATA_HW_PORT_MUX,
-- GFP_KERNEL);
-- if (!md_dst)
-- return -ENOMEM;
--
-- md_dst->u.port_info.port_id = i;
-- eth->dsa_meta[i] = md_dst;
-- }
-- } else {
-- /* Hardware special tag parsing needs to be disabled if at least
-- * one MAC does not use DSA.
-- */
-- u32 val = mtk_r32(eth, MTK_CDMP_IG_CTRL);
-- val &= ~MTK_CDMP_STAG_EN;
-- mtk_w32(eth, val, MTK_CDMP_IG_CTRL);
-- }
--
- err = phylink_of_phy_connect(mac->phylink, mac->of_node, 0);
- if (err) {
- netdev_err(dev, "%s: could not attach PHY: %d\n", __func__,
-@@ -3287,6 +3228,35 @@ static int mtk_open(struct net_device *d
- phylink_start(mac->phylink);
- netif_tx_start_all_queues(dev);
-
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-+ return 0;
-+
-+ if (mtk_uses_dsa(dev) && !eth->prog) {
-+ for (i = 0; i < ARRAY_SIZE(eth->dsa_meta); i++) {
-+ struct metadata_dst *md_dst = eth->dsa_meta[i];
-+
-+ if (md_dst)
-+ continue;
-+
-+ md_dst = metadata_dst_alloc(0, METADATA_HW_PORT_MUX,
-+ GFP_KERNEL);
-+ if (!md_dst)
-+ return -ENOMEM;
-+
-+ md_dst->u.port_info.port_id = i;
-+ eth->dsa_meta[i] = md_dst;
-+ }
-+ } else {
-+ /* Hardware special tag parsing needs to be disabled if at least
-+ * one MAC does not use DSA.
-+ */
-+ u32 val = mtk_r32(eth, MTK_CDMP_IG_CTRL);
-+ val &= ~MTK_CDMP_STAG_EN;
-+ mtk_w32(eth, val, MTK_CDMP_IG_CTRL);
-+
-+ mtk_w32(eth, 0, MTK_CDMP_EG_CTRL);
-+ }
-+
- return 0;
- }
-
-@@ -3771,10 +3741,9 @@ static int mtk_hw_init(struct mtk_eth *e
- if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
- val = mtk_r32(eth, MTK_CDMP_IG_CTRL);
- mtk_w32(eth, val | MTK_CDMP_STAG_EN, MTK_CDMP_IG_CTRL);
-- }
-
-- /* Enable RX VLan Offloading */
-- mtk_w32(eth, 1, MTK_CDMP_EG_CTRL);
-+ mtk_w32(eth, 1, MTK_CDMP_EG_CTRL);
-+ }
-
- /* set interrupt delays based on current Net DIM sample */
- mtk_dim_rx(ð->rx_dim.work);
-@@ -4414,7 +4383,7 @@ static int mtk_add_mac(struct mtk_eth *e
- eth->netdev[id]->hw_features |= NETIF_F_LRO;
-
- eth->netdev[id]->vlan_features = eth->soc->hw_features &
-- ~(NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX);
-+ ~NETIF_F_HW_VLAN_CTAG_TX;
- eth->netdev[id]->features |= eth->soc->hw_features;
- eth->netdev[id]->ethtool_ops = &mtk_ethtool_ops;
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -48,7 +48,6 @@
- #define MTK_HW_FEATURES (NETIF_F_IP_CSUM | \
- NETIF_F_RXCSUM | \
- NETIF_F_HW_VLAN_CTAG_TX | \
-- NETIF_F_HW_VLAN_CTAG_RX | \
- NETIF_F_SG | NETIF_F_TSO | \
- NETIF_F_TSO6 | \
- NETIF_F_IPV6_CSUM |\
+++ /dev/null
-From b804f765485109f9644cc05d1e8fc79ca6c6e4aa Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Wed, 19 Jul 2023 01:39:36 +0100
-Subject: [PATCH 094/250] net: ethernet: mtk_eth_soc: always
- mtk_get_ib1_pkt_type
-
-entries and bind debugfs files would display wrong data on NETSYS_V2 and
-later because instead of using mtk_get_ib1_pkt_type the driver would use
-MTK_FOE_IB1_PACKET_TYPE which corresponds to NETSYS_V1(.x) SoCs.
-Use mtk_get_ib1_pkt_type so entries and bind records display correctly.
-
-Fixes: 03a3180e5c09e ("net: ethernet: mtk_eth_soc: introduce flow offloading support for mt7986")
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Link: https://lore.kernel.org/r/c0ae03d0182f4d27b874cbdf0059bc972c317f3c.1689727134.git.daniel@makrotopia.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
-@@ -98,7 +98,7 @@ mtk_ppe_debugfs_foe_show(struct seq_file
-
- acct = mtk_foe_entry_get_mib(ppe, i, NULL);
-
-- type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
-+ type = mtk_get_ib1_pkt_type(ppe->eth, entry->ib1);
- seq_printf(m, "%05x %s %7s", i,
- mtk_foe_entry_state_str(state),
- mtk_foe_pkt_type_str(type));
+++ /dev/null
-From 5ea0e1312bcfebc06b5f91d1bb82b823d6395125 Mon Sep 17 00:00:00 2001
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Wed, 19 Jul 2023 12:29:49 +0200
-Subject: [PATCH 095/250] net: ethernet: mtk_ppe: add MTK_FOE_ENTRY_V{1,2}_SIZE
- macros
-
-Introduce MTK_FOE_ENTRY_V{1,2}_SIZE macros in order to make more
-explicit foe_entry size for different chipset revisions.
-
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Reviewed-by: Simon Horman <simon.horman@corigine.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 10 +++++-----
- drivers/net/ethernet/mediatek/mtk_ppe.h | 3 +++
- 2 files changed, 8 insertions(+), 5 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -4765,7 +4765,7 @@ static const struct mtk_soc_data mt7621_
- .required_pctl = false,
- .offload_version = 1,
- .hash_offset = 2,
-- .foe_entry_size = sizeof(struct mtk_foe_entry) - 16,
-+ .foe_entry_size = MTK_FOE_ENTRY_V1_SIZE,
- .txrx = {
- .txd_size = sizeof(struct mtk_tx_dma),
- .rxd_size = sizeof(struct mtk_rx_dma),
-@@ -4786,7 +4786,7 @@ static const struct mtk_soc_data mt7622_
- .offload_version = 2,
- .hash_offset = 2,
- .has_accounting = true,
-- .foe_entry_size = sizeof(struct mtk_foe_entry) - 16,
-+ .foe_entry_size = MTK_FOE_ENTRY_V1_SIZE,
- .txrx = {
- .txd_size = sizeof(struct mtk_tx_dma),
- .rxd_size = sizeof(struct mtk_rx_dma),
-@@ -4805,7 +4805,7 @@ static const struct mtk_soc_data mt7623_
- .required_pctl = true,
- .offload_version = 1,
- .hash_offset = 2,
-- .foe_entry_size = sizeof(struct mtk_foe_entry) - 16,
-+ .foe_entry_size = MTK_FOE_ENTRY_V1_SIZE,
- .txrx = {
- .txd_size = sizeof(struct mtk_tx_dma),
- .rxd_size = sizeof(struct mtk_rx_dma),
-@@ -4843,8 +4843,8 @@ static const struct mtk_soc_data mt7981_
- .required_pctl = false,
- .offload_version = 2,
- .hash_offset = 4,
-- .foe_entry_size = sizeof(struct mtk_foe_entry),
- .has_accounting = true,
-+ .foe_entry_size = MTK_FOE_ENTRY_V2_SIZE,
- .txrx = {
- .txd_size = sizeof(struct mtk_tx_dma_v2),
- .rxd_size = sizeof(struct mtk_rx_dma_v2),
-@@ -4864,8 +4864,8 @@ static const struct mtk_soc_data mt7986_
- .required_pctl = false,
- .offload_version = 2,
- .hash_offset = 4,
-- .foe_entry_size = sizeof(struct mtk_foe_entry),
- .has_accounting = true,
-+ .foe_entry_size = MTK_FOE_ENTRY_V2_SIZE,
- .txrx = {
- .txd_size = sizeof(struct mtk_tx_dma_v2),
- .rxd_size = sizeof(struct mtk_rx_dma_v2),
---- a/drivers/net/ethernet/mediatek/mtk_ppe.h
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
-@@ -216,6 +216,9 @@ struct mtk_foe_ipv6_6rd {
- struct mtk_foe_mac_info l2;
- };
-
-+#define MTK_FOE_ENTRY_V1_SIZE 80
-+#define MTK_FOE_ENTRY_V2_SIZE 96
-+
- struct mtk_foe_entry {
- u32 ib1;
-
+++ /dev/null
-From 8cfa2576d79f9379d167a8994f0fca935c07a8bc Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Sat, 22 Jul 2023 21:32:49 +0100
-Subject: [PATCH 096/250] net: ethernet: mtk_eth_soc: remove incorrect PLL
- configuration
-
-MT7623 GMAC0 attempts to configure the system clocking according to the
-required speed in the .mac_config callback for non-SGMII, non-baseX and
-non-TRGMII modes.
-
-state->speed setting has never been reliable in the .mac_config
-callback - there are cases where this is not the link speed,
-particularly via ethtool paths, so this has always been unreliable (as
-detailed in phylink's documentation.)
-
-There is the additional issue that mtk_gmac0_rgmii_adjust() will only
-be called if state->interface changes, which means it only configures
-the system clocking on the very first .mac_config call, which will be
-made when the network device is first brought up before any link is
-established.
-
-Essentially, this code is incredibly buggy, and probably never worked.
-
-Moreover, checking the in-kernel DT files, it seems no platform makes
-use of this code path.
-
-Therefore, let's remove it, and disable interface modes for port 0 that
-are not SGMII, 1000base-X, 2500base-X or TRGMII on the MT7623.
-
-Reviewed-by: Daniel Golle <daniel@makrotopia.org>
-Tested-by: Daniel Golle <daniel@makrotopia.org>
-Tested-by: Frank Wunderlich <frank-w@public-files.de>
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 54 ++++++---------------
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 1 +
- 2 files changed, 17 insertions(+), 38 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -352,7 +352,7 @@ static int mt7621_gmac0_rgmii_adjust(str
- }
-
- static void mtk_gmac0_rgmii_adjust(struct mtk_eth *eth,
-- phy_interface_t interface, int speed)
-+ phy_interface_t interface)
- {
- u32 val;
- int ret;
-@@ -366,26 +366,7 @@ static void mtk_gmac0_rgmii_adjust(struc
- return;
- }
-
-- val = (speed == SPEED_1000) ?
-- INTF_MODE_RGMII_1000 : INTF_MODE_RGMII_10_100;
-- mtk_w32(eth, val, INTF_MODE);
--
-- regmap_update_bits(eth->ethsys, ETHSYS_CLKCFG0,
-- ETHSYS_TRGMII_CLK_SEL362_5,
-- ETHSYS_TRGMII_CLK_SEL362_5);
--
-- val = (speed == SPEED_1000) ? 250000000 : 500000000;
-- ret = clk_set_rate(eth->clks[MTK_CLK_TRGPLL], val);
-- if (ret)
-- dev_err(eth->dev, "Failed to set trgmii pll: %d\n", ret);
--
-- val = (speed == SPEED_1000) ?
-- RCK_CTRL_RGMII_1000 : RCK_CTRL_RGMII_10_100;
-- mtk_w32(eth, val, TRGMII_RCK_CTRL);
--
-- val = (speed == SPEED_1000) ?
-- TCK_CTRL_RGMII_1000 : TCK_CTRL_RGMII_10_100;
-- mtk_w32(eth, val, TRGMII_TCK_CTRL);
-+ dev_err(eth->dev, "Missing PLL configuration, ethernet may not work\n");
- }
-
- static struct phylink_pcs *mtk_mac_select_pcs(struct phylink_config *config,
-@@ -471,17 +452,8 @@ static void mtk_mac_config(struct phylin
- state->interface))
- goto err_phy;
- } else {
-- /* FIXME: this is incorrect. Not only does it
-- * use state->speed (which is not guaranteed
-- * to be correct) but it also makes use of it
-- * in a code path that will only be reachable
-- * when the PHY interface mode changes, not
-- * when the speed changes. Consequently, RGMII
-- * is probably broken.
-- */
- mtk_gmac0_rgmii_adjust(mac->hw,
-- state->interface,
-- state->speed);
-+ state->interface);
-
- /* mt7623_pad_clk_setup */
- for (i = 0 ; i < NUM_TRGMII_CTRL; i++)
-@@ -4342,13 +4314,19 @@ static int mtk_add_mac(struct mtk_eth *e
- mac->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
- MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD;
-
-- __set_bit(PHY_INTERFACE_MODE_MII,
-- mac->phylink_config.supported_interfaces);
-- __set_bit(PHY_INTERFACE_MODE_GMII,
-- mac->phylink_config.supported_interfaces);
-+ /* MT7623 gmac0 is now missing its speed-specific PLL configuration
-+ * in its .mac_config method (since state->speed is not valid there.
-+ * Disable support for MII, GMII and RGMII.
-+ */
-+ if (!mac->hw->soc->disable_pll_modes || mac->id != 0) {
-+ __set_bit(PHY_INTERFACE_MODE_MII,
-+ mac->phylink_config.supported_interfaces);
-+ __set_bit(PHY_INTERFACE_MODE_GMII,
-+ mac->phylink_config.supported_interfaces);
-
-- if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_RGMII))
-- phy_interface_set_rgmii(mac->phylink_config.supported_interfaces);
-+ if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_RGMII))
-+ phy_interface_set_rgmii(mac->phylink_config.supported_interfaces);
-+ }
-
- if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_TRGMII) && !mac->id)
- __set_bit(PHY_INTERFACE_MODE_TRGMII,
-@@ -4806,6 +4784,7 @@ static const struct mtk_soc_data mt7623_
- .offload_version = 1,
- .hash_offset = 2,
- .foe_entry_size = MTK_FOE_ENTRY_V1_SIZE,
-+ .disable_pll_modes = true,
- .txrx = {
- .txd_size = sizeof(struct mtk_tx_dma),
- .rxd_size = sizeof(struct mtk_rx_dma),
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -1030,6 +1030,7 @@ struct mtk_soc_data {
- u16 foe_entry_size;
- netdev_features_t hw_features;
- bool has_accounting;
-+ bool disable_pll_modes;
- struct {
- u32 txd_size;
- u32 rxd_size;
+++ /dev/null
-From a4c2233b1e4359b6c64b6f9ba98c8718a11fffee Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Sat, 22 Jul 2023 21:32:54 +0100
-Subject: [PATCH 097/250] net: ethernet: mtk_eth_soc: remove mac_pcs_get_state
- and modernise
-
-Remove the .mac_pcs_get_state function, since as far as I can tell is
-never called - no DT appears to specify an in-band-status management
-nor SFP support for this driver.
-
-Removal of this, along with the previous patch to remove the incorrect
-clocking configuration, means that the driver becomes non-legacy, so
-we can remove the "legacy_pre_march2020" status from this driver.
-
-Reviewed-by: Daniel Golle <daniel@makrotopia.org>
-Tested-by: Daniel Golle <daniel@makrotopia.org>
-Tested-by: Frank Wunderlich <frank-w@public-files.de>
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 35 ---------------------
- 1 file changed, 35 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -555,38 +555,6 @@ static int mtk_mac_finish(struct phylink
- return 0;
- }
-
--static void mtk_mac_pcs_get_state(struct phylink_config *config,
-- struct phylink_link_state *state)
--{
-- struct mtk_mac *mac = container_of(config, struct mtk_mac,
-- phylink_config);
-- u32 pmsr = mtk_r32(mac->hw, MTK_MAC_MSR(mac->id));
--
-- state->link = (pmsr & MAC_MSR_LINK);
-- state->duplex = (pmsr & MAC_MSR_DPX) >> 1;
--
-- switch (pmsr & (MAC_MSR_SPEED_1000 | MAC_MSR_SPEED_100)) {
-- case 0:
-- state->speed = SPEED_10;
-- break;
-- case MAC_MSR_SPEED_100:
-- state->speed = SPEED_100;
-- break;
-- case MAC_MSR_SPEED_1000:
-- state->speed = SPEED_1000;
-- break;
-- default:
-- state->speed = SPEED_UNKNOWN;
-- break;
-- }
--
-- state->pause &= (MLO_PAUSE_RX | MLO_PAUSE_TX);
-- if (pmsr & MAC_MSR_RX_FC)
-- state->pause |= MLO_PAUSE_RX;
-- if (pmsr & MAC_MSR_TX_FC)
-- state->pause |= MLO_PAUSE_TX;
--}
--
- static void mtk_mac_link_down(struct phylink_config *config, unsigned int mode,
- phy_interface_t interface)
- {
-@@ -708,7 +676,6 @@ static void mtk_mac_link_up(struct phyli
-
- static const struct phylink_mac_ops mtk_phylink_ops = {
- .mac_select_pcs = mtk_mac_select_pcs,
-- .mac_pcs_get_state = mtk_mac_pcs_get_state,
- .mac_config = mtk_mac_config,
- .mac_finish = mtk_mac_finish,
- .mac_link_down = mtk_mac_link_down,
-@@ -4309,8 +4276,6 @@ static int mtk_add_mac(struct mtk_eth *e
-
- mac->phylink_config.dev = ð->netdev[id]->dev;
- mac->phylink_config.type = PHYLINK_NETDEV;
-- /* This driver makes use of state->speed in mac_config */
-- mac->phylink_config.legacy_pre_march2020 = true;
- mac->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
- MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD;
-
+++ /dev/null
-From 5d8d05fbf804b4485646d39551ac27452e45afd3 Mon Sep 17 00:00:00 2001
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Tue, 25 Jul 2023 01:52:02 +0100
-Subject: [PATCH 099/250] net: ethernet: mtk_eth_soc: add version in
- mtk_soc_data
-
-Introduce version field in mtk_soc_data data structure in order to
-make mtk_eth driver easier to maintain for chipset configuration
-codebase. Get rid of MTK_NETSYS_V2 bit in chip capabilities.
-This is a preliminary patch to introduce support for MT7988 SoC.
-
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Link: https://lore.kernel.org/r/e52fae302ca135436e5cdd26d38d87be2da63055.1690246066.git.daniel@makrotopia.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 55 +++++++++++--------
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 36 +++++++-----
- drivers/net/ethernet/mediatek/mtk_ppe.c | 18 +++---
- .../net/ethernet/mediatek/mtk_ppe_offload.c | 2 +-
- drivers/net/ethernet/mediatek/mtk_wed.c | 4 +-
- 5 files changed, 66 insertions(+), 49 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -580,7 +580,7 @@ static void mtk_set_queue_speed(struct m
- FIELD_PREP(MTK_QTX_SCH_MIN_RATE_MAN, 1) |
- FIELD_PREP(MTK_QTX_SCH_MIN_RATE_EXP, 4) |
- MTK_QTX_SCH_LEAKY_BUCKET_SIZE;
-- if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-+ if (mtk_is_netsys_v1(eth))
- val |= MTK_QTX_SCH_LEAKY_BUCKET_EN;
-
- if (IS_ENABLED(CONFIG_SOC_MT7621)) {
-@@ -955,7 +955,7 @@ static bool mtk_rx_get_desc(struct mtk_e
- rxd->rxd1 = READ_ONCE(dma_rxd->rxd1);
- rxd->rxd3 = READ_ONCE(dma_rxd->rxd3);
- rxd->rxd4 = READ_ONCE(dma_rxd->rxd4);
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
-+ if (mtk_is_netsys_v2_or_greater(eth)) {
- rxd->rxd5 = READ_ONCE(dma_rxd->rxd5);
- rxd->rxd6 = READ_ONCE(dma_rxd->rxd6);
- }
-@@ -1013,7 +1013,7 @@ static int mtk_init_fq_dma(struct mtk_et
-
- txd->txd3 = TX_DMA_PLEN0(MTK_QDMA_PAGE_SIZE);
- txd->txd4 = 0;
-- if (MTK_HAS_CAPS(soc->caps, MTK_NETSYS_V2)) {
-+ if (mtk_is_netsys_v2_or_greater(eth)) {
- txd->txd5 = 0;
- txd->txd6 = 0;
- txd->txd7 = 0;
-@@ -1204,7 +1204,7 @@ static void mtk_tx_set_dma_desc(struct n
- struct mtk_mac *mac = netdev_priv(dev);
- struct mtk_eth *eth = mac->hw;
-
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-+ if (mtk_is_netsys_v2_or_greater(eth))
- mtk_tx_set_dma_desc_v2(dev, txd, info);
- else
- mtk_tx_set_dma_desc_v1(dev, txd, info);
-@@ -1511,7 +1511,7 @@ static void mtk_update_rx_cpu_idx(struct
-
- static bool mtk_page_pool_enabled(struct mtk_eth *eth)
- {
-- return MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2);
-+ return eth->soc->version == 2;
- }
-
- static struct page_pool *mtk_create_page_pool(struct mtk_eth *eth,
-@@ -1853,7 +1853,7 @@ static int mtk_poll_rx(struct napi_struc
- break;
-
- /* find out which mac the packet come from. values start at 1 */
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-+ if (mtk_is_netsys_v2_or_greater(eth))
- mac = RX_DMA_GET_SPORT_V2(trxd.rxd5) - 1;
- else if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628) &&
- !(trxd.rxd4 & RX_DMA_SPECIAL_TAG))
-@@ -1949,7 +1949,7 @@ static int mtk_poll_rx(struct napi_struc
- skb->dev = netdev;
- bytes += skb->len;
-
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
-+ if (mtk_is_netsys_v2_or_greater(eth)) {
- reason = FIELD_GET(MTK_RXD5_PPE_CPU_REASON, trxd.rxd5);
- hash = trxd.rxd5 & MTK_RXD5_FOE_ENTRY;
- if (hash != MTK_RXD5_FOE_ENTRY)
-@@ -1974,8 +1974,8 @@ static int mtk_poll_rx(struct napi_struc
- /* When using VLAN untagging in combination with DSA, the
- * hardware treats the MTK special tag as a VLAN and untags it.
- */
-- if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) &&
-- (trxd.rxd2 & RX_DMA_VTAG) && netdev_uses_dsa(netdev)) {
-+ if (mtk_is_netsys_v1(eth) && (trxd.rxd2 & RX_DMA_VTAG) &&
-+ netdev_uses_dsa(netdev)) {
- unsigned int port = RX_DMA_VPID(trxd.rxd3) & GENMASK(2, 0);
-
- if (port < ARRAY_SIZE(eth->dsa_meta) &&
-@@ -2285,7 +2285,7 @@ static int mtk_tx_alloc(struct mtk_eth *
- txd->txd2 = next_ptr;
- txd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU;
- txd->txd4 = 0;
-- if (MTK_HAS_CAPS(soc->caps, MTK_NETSYS_V2)) {
-+ if (mtk_is_netsys_v2_or_greater(eth)) {
- txd->txd5 = 0;
- txd->txd6 = 0;
- txd->txd7 = 0;
-@@ -2338,14 +2338,14 @@ static int mtk_tx_alloc(struct mtk_eth *
- FIELD_PREP(MTK_QTX_SCH_MIN_RATE_MAN, 1) |
- FIELD_PREP(MTK_QTX_SCH_MIN_RATE_EXP, 4) |
- MTK_QTX_SCH_LEAKY_BUCKET_SIZE;
-- if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-+ if (mtk_is_netsys_v1(eth))
- val |= MTK_QTX_SCH_LEAKY_BUCKET_EN;
- mtk_w32(eth, val, soc->reg_map->qdma.qtx_sch + ofs);
- ofs += MTK_QTX_OFFSET;
- }
- val = MTK_QDMA_TX_SCH_MAX_WFQ | (MTK_QDMA_TX_SCH_MAX_WFQ << 16);
- mtk_w32(eth, val, soc->reg_map->qdma.tx_sch_rate);
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-+ if (mtk_is_netsys_v2_or_greater(eth))
- mtk_w32(eth, val, soc->reg_map->qdma.tx_sch_rate + 4);
- } else {
- mtk_w32(eth, ring->phys_pdma, MT7628_TX_BASE_PTR0);
-@@ -2474,7 +2474,7 @@ static int mtk_rx_alloc(struct mtk_eth *
-
- rxd->rxd3 = 0;
- rxd->rxd4 = 0;
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
-+ if (mtk_is_netsys_v2_or_greater(eth)) {
- rxd->rxd5 = 0;
- rxd->rxd6 = 0;
- rxd->rxd7 = 0;
-@@ -3025,7 +3025,7 @@ static int mtk_start_dma(struct mtk_eth
- MTK_TX_BT_32DWORDS | MTK_NDP_CO_PRO |
- MTK_RX_2B_OFFSET | MTK_TX_WB_DDONE;
-
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-+ if (mtk_is_netsys_v2_or_greater(eth))
- val |= MTK_MUTLI_CNT | MTK_RESV_BUF |
- MTK_WCOMP_EN | MTK_DMAD_WR_WDONE |
- MTK_CHK_DDONE_EN | MTK_LEAKY_BUCKET_EN;
-@@ -3167,7 +3167,7 @@ static int mtk_open(struct net_device *d
- phylink_start(mac->phylink);
- netif_tx_start_all_queues(dev);
-
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-+ if (mtk_is_netsys_v2_or_greater(eth))
- return 0;
-
- if (mtk_uses_dsa(dev) && !eth->prog) {
-@@ -3432,7 +3432,7 @@ static void mtk_hw_reset(struct mtk_eth
- {
- u32 val;
-
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
-+ if (mtk_is_netsys_v2_or_greater(eth)) {
- regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN, 0);
- val = RSTCTRL_PPE0_V2;
- } else {
-@@ -3444,7 +3444,7 @@ static void mtk_hw_reset(struct mtk_eth
-
- ethsys_reset(eth, RSTCTRL_ETH | RSTCTRL_FE | val);
-
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-+ if (mtk_is_netsys_v2_or_greater(eth))
- regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN,
- 0x3ffffff);
- }
-@@ -3470,7 +3470,7 @@ static void mtk_hw_warm_reset(struct mtk
- return;
- }
-
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-+ if (mtk_is_netsys_v2_or_greater(eth))
- rst_mask = RSTCTRL_ETH | RSTCTRL_PPE0_V2;
- else
- rst_mask = RSTCTRL_ETH | RSTCTRL_PPE0;
-@@ -3640,7 +3640,7 @@ static int mtk_hw_init(struct mtk_eth *e
- else
- mtk_hw_reset(eth);
-
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
-+ if (mtk_is_netsys_v2_or_greater(eth)) {
- /* Set FE to PDMAv2 if necessary */
- val = mtk_r32(eth, MTK_FE_GLO_MISC);
- mtk_w32(eth, val | BIT(4), MTK_FE_GLO_MISC);
-@@ -3677,7 +3677,7 @@ static int mtk_hw_init(struct mtk_eth *e
- */
- val = mtk_r32(eth, MTK_CDMQ_IG_CTRL);
- mtk_w32(eth, val | MTK_CDMQ_STAG_EN, MTK_CDMQ_IG_CTRL);
-- if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
-+ if (mtk_is_netsys_v1(eth)) {
- val = mtk_r32(eth, MTK_CDMP_IG_CTRL);
- mtk_w32(eth, val | MTK_CDMP_STAG_EN, MTK_CDMP_IG_CTRL);
-
-@@ -3699,7 +3699,7 @@ static int mtk_hw_init(struct mtk_eth *e
- mtk_w32(eth, eth->soc->txrx.rx_irq_done_mask, reg_map->qdma.int_grp + 4);
- mtk_w32(eth, 0x21021000, MTK_FE_INT_GRP);
-
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
-+ if (mtk_is_netsys_v2_or_greater(eth)) {
- /* PSE should not drop port8 and port9 packets from WDMA Tx */
- mtk_w32(eth, 0x00000300, PSE_DROP_CFG);
-
-@@ -4488,7 +4488,7 @@ static int mtk_probe(struct platform_dev
- }
- }
-
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
-+ if (mtk_is_netsys_v2_or_greater(eth)) {
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- err = -EINVAL;
-@@ -4596,9 +4596,8 @@ static int mtk_probe(struct platform_dev
- }
-
- if (eth->soc->offload_version) {
-- u32 num_ppe;
-+ u32 num_ppe = mtk_is_netsys_v2_or_greater(eth) ? 2 : 1;
-
-- num_ppe = MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) ? 2 : 1;
- num_ppe = min_t(u32, ARRAY_SIZE(eth->ppe), num_ppe);
- for (i = 0; i < num_ppe; i++) {
- u32 ppe_addr = eth->soc->reg_map->ppe_base + i * 0x400;
-@@ -4690,6 +4689,7 @@ static const struct mtk_soc_data mt2701_
- .hw_features = MTK_HW_FEATURES,
- .required_clks = MT7623_CLKS_BITMAP,
- .required_pctl = true,
-+ .version = 1,
- .txrx = {
- .txd_size = sizeof(struct mtk_tx_dma),
- .rxd_size = sizeof(struct mtk_rx_dma),
-@@ -4706,6 +4706,7 @@ static const struct mtk_soc_data mt7621_
- .hw_features = MTK_HW_FEATURES,
- .required_clks = MT7621_CLKS_BITMAP,
- .required_pctl = false,
-+ .version = 1,
- .offload_version = 1,
- .hash_offset = 2,
- .foe_entry_size = MTK_FOE_ENTRY_V1_SIZE,
-@@ -4726,6 +4727,7 @@ static const struct mtk_soc_data mt7622_
- .hw_features = MTK_HW_FEATURES,
- .required_clks = MT7622_CLKS_BITMAP,
- .required_pctl = false,
-+ .version = 1,
- .offload_version = 2,
- .hash_offset = 2,
- .has_accounting = true,
-@@ -4746,6 +4748,7 @@ static const struct mtk_soc_data mt7623_
- .hw_features = MTK_HW_FEATURES,
- .required_clks = MT7623_CLKS_BITMAP,
- .required_pctl = true,
-+ .version = 1,
- .offload_version = 1,
- .hash_offset = 2,
- .foe_entry_size = MTK_FOE_ENTRY_V1_SIZE,
-@@ -4768,6 +4771,7 @@ static const struct mtk_soc_data mt7629_
- .required_clks = MT7629_CLKS_BITMAP,
- .required_pctl = false,
- .has_accounting = true,
-+ .version = 1,
- .txrx = {
- .txd_size = sizeof(struct mtk_tx_dma),
- .rxd_size = sizeof(struct mtk_rx_dma),
-@@ -4785,6 +4789,7 @@ static const struct mtk_soc_data mt7981_
- .hw_features = MTK_HW_FEATURES,
- .required_clks = MT7981_CLKS_BITMAP,
- .required_pctl = false,
-+ .version = 2,
- .offload_version = 2,
- .hash_offset = 4,
- .has_accounting = true,
-@@ -4806,6 +4811,7 @@ static const struct mtk_soc_data mt7986_
- .hw_features = MTK_HW_FEATURES,
- .required_clks = MT7986_CLKS_BITMAP,
- .required_pctl = false,
-+ .version = 2,
- .offload_version = 2,
- .hash_offset = 4,
- .has_accounting = true,
-@@ -4826,6 +4832,7 @@ static const struct mtk_soc_data rt5350_
- .hw_features = MTK_HW_FEATURES_MT7628,
- .required_clks = MT7628_CLKS_BITMAP,
- .required_pctl = false,
-+ .version = 1,
- .txrx = {
- .txd_size = sizeof(struct mtk_tx_dma),
- .rxd_size = sizeof(struct mtk_rx_dma),
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -820,7 +820,6 @@ enum mkt_eth_capabilities {
- MTK_SHARED_INT_BIT,
- MTK_TRGMII_MT7621_CLK_BIT,
- MTK_QDMA_BIT,
-- MTK_NETSYS_V2_BIT,
- MTK_SOC_MT7628_BIT,
- MTK_RSTCTRL_PPE1_BIT,
- MTK_U3_COPHY_V2_BIT,
-@@ -855,7 +854,6 @@ enum mkt_eth_capabilities {
- #define MTK_SHARED_INT BIT(MTK_SHARED_INT_BIT)
- #define MTK_TRGMII_MT7621_CLK BIT(MTK_TRGMII_MT7621_CLK_BIT)
- #define MTK_QDMA BIT(MTK_QDMA_BIT)
--#define MTK_NETSYS_V2 BIT(MTK_NETSYS_V2_BIT)
- #define MTK_SOC_MT7628 BIT(MTK_SOC_MT7628_BIT)
- #define MTK_RSTCTRL_PPE1 BIT(MTK_RSTCTRL_PPE1_BIT)
- #define MTK_U3_COPHY_V2 BIT(MTK_U3_COPHY_V2_BIT)
-@@ -934,11 +932,11 @@ enum mkt_eth_capabilities {
- #define MT7981_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | MTK_GMAC2_GEPHY | \
- MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \
- MTK_MUX_U3_GMAC2_TO_QPHY | MTK_U3_COPHY_V2 | \
-- MTK_NETSYS_V2 | MTK_RSTCTRL_PPE1)
-+ MTK_RSTCTRL_PPE1)
-
- #define MT7986_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | \
- MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \
-- MTK_NETSYS_V2 | MTK_RSTCTRL_PPE1)
-+ MTK_RSTCTRL_PPE1)
-
- struct mtk_tx_dma_desc_info {
- dma_addr_t addr;
-@@ -1009,6 +1007,7 @@ struct mtk_reg_map {
- * @required_pctl A bool value to show whether the SoC requires
- * the extra setup for those pins used by GMAC.
- * @hash_offset Flow table hash offset.
-+ * @version SoC version.
- * @foe_entry_size Foe table entry size.
- * @has_accounting Bool indicating support for accounting of
- * offloaded flows.
-@@ -1027,6 +1026,7 @@ struct mtk_soc_data {
- bool required_pctl;
- u8 offload_version;
- u8 hash_offset;
-+ u8 version;
- u16 foe_entry_size;
- netdev_features_t hw_features;
- bool has_accounting;
-@@ -1183,6 +1183,16 @@ struct mtk_mac {
- /* the struct describing the SoC. these are declared in the soc_xyz.c files */
- extern const struct of_device_id of_mtk_match[];
-
-+static inline bool mtk_is_netsys_v1(struct mtk_eth *eth)
-+{
-+ return eth->soc->version == 1;
-+}
-+
-+static inline bool mtk_is_netsys_v2_or_greater(struct mtk_eth *eth)
-+{
-+ return eth->soc->version > 1;
-+}
-+
- static inline struct mtk_foe_entry *
- mtk_foe_get_entry(struct mtk_ppe *ppe, u16 hash)
- {
-@@ -1193,7 +1203,7 @@ mtk_foe_get_entry(struct mtk_ppe *ppe, u
-
- static inline u32 mtk_get_ib1_ts_mask(struct mtk_eth *eth)
- {
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-+ if (mtk_is_netsys_v2_or_greater(eth))
- return MTK_FOE_IB1_BIND_TIMESTAMP_V2;
-
- return MTK_FOE_IB1_BIND_TIMESTAMP;
-@@ -1201,7 +1211,7 @@ static inline u32 mtk_get_ib1_ts_mask(st
-
- static inline u32 mtk_get_ib1_ppoe_mask(struct mtk_eth *eth)
- {
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-+ if (mtk_is_netsys_v2_or_greater(eth))
- return MTK_FOE_IB1_BIND_PPPOE_V2;
-
- return MTK_FOE_IB1_BIND_PPPOE;
-@@ -1209,7 +1219,7 @@ static inline u32 mtk_get_ib1_ppoe_mask(
-
- static inline u32 mtk_get_ib1_vlan_tag_mask(struct mtk_eth *eth)
- {
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-+ if (mtk_is_netsys_v2_or_greater(eth))
- return MTK_FOE_IB1_BIND_VLAN_TAG_V2;
-
- return MTK_FOE_IB1_BIND_VLAN_TAG;
-@@ -1217,7 +1227,7 @@ static inline u32 mtk_get_ib1_vlan_tag_m
-
- static inline u32 mtk_get_ib1_vlan_layer_mask(struct mtk_eth *eth)
- {
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-+ if (mtk_is_netsys_v2_or_greater(eth))
- return MTK_FOE_IB1_BIND_VLAN_LAYER_V2;
-
- return MTK_FOE_IB1_BIND_VLAN_LAYER;
-@@ -1225,7 +1235,7 @@ static inline u32 mtk_get_ib1_vlan_layer
-
- static inline u32 mtk_prep_ib1_vlan_layer(struct mtk_eth *eth, u32 val)
- {
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-+ if (mtk_is_netsys_v2_or_greater(eth))
- return FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER_V2, val);
-
- return FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, val);
-@@ -1233,7 +1243,7 @@ static inline u32 mtk_prep_ib1_vlan_laye
-
- static inline u32 mtk_get_ib1_vlan_layer(struct mtk_eth *eth, u32 val)
- {
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-+ if (mtk_is_netsys_v2_or_greater(eth))
- return FIELD_GET(MTK_FOE_IB1_BIND_VLAN_LAYER_V2, val);
-
- return FIELD_GET(MTK_FOE_IB1_BIND_VLAN_LAYER, val);
-@@ -1241,7 +1251,7 @@ static inline u32 mtk_get_ib1_vlan_layer
-
- static inline u32 mtk_get_ib1_pkt_type_mask(struct mtk_eth *eth)
- {
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-+ if (mtk_is_netsys_v2_or_greater(eth))
- return MTK_FOE_IB1_PACKET_TYPE_V2;
-
- return MTK_FOE_IB1_PACKET_TYPE;
-@@ -1249,7 +1259,7 @@ static inline u32 mtk_get_ib1_pkt_type_m
-
- static inline u32 mtk_get_ib1_pkt_type(struct mtk_eth *eth, u32 val)
- {
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-+ if (mtk_is_netsys_v2_or_greater(eth))
- return FIELD_GET(MTK_FOE_IB1_PACKET_TYPE_V2, val);
-
- return FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, val);
-@@ -1257,7 +1267,7 @@ static inline u32 mtk_get_ib1_pkt_type(s
-
- static inline u32 mtk_get_ib2_multicast_mask(struct mtk_eth *eth)
- {
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-+ if (mtk_is_netsys_v2_or_greater(eth))
- return MTK_FOE_IB2_MULTICAST_V2;
-
- return MTK_FOE_IB2_MULTICAST;
---- a/drivers/net/ethernet/mediatek/mtk_ppe.c
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
-@@ -207,7 +207,7 @@ int mtk_foe_entry_prepare(struct mtk_eth
-
- memset(entry, 0, sizeof(*entry));
-
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
-+ if (mtk_is_netsys_v2_or_greater(eth)) {
- val = FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_BIND) |
- FIELD_PREP(MTK_FOE_IB1_PACKET_TYPE_V2, type) |
- FIELD_PREP(MTK_FOE_IB1_UDP, l4proto == IPPROTO_UDP) |
-@@ -271,7 +271,7 @@ int mtk_foe_entry_set_pse_port(struct mt
- u32 *ib2 = mtk_foe_entry_ib2(eth, entry);
- u32 val = *ib2;
-
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
-+ if (mtk_is_netsys_v2_or_greater(eth)) {
- val &= ~MTK_FOE_IB2_DEST_PORT_V2;
- val |= FIELD_PREP(MTK_FOE_IB2_DEST_PORT_V2, port);
- } else {
-@@ -422,7 +422,7 @@ int mtk_foe_entry_set_wdma(struct mtk_et
- struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry);
- u32 *ib2 = mtk_foe_entry_ib2(eth, entry);
-
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
-+ if (mtk_is_netsys_v2_or_greater(eth)) {
- *ib2 &= ~MTK_FOE_IB2_PORT_MG_V2;
- *ib2 |= FIELD_PREP(MTK_FOE_IB2_RX_IDX, txq) |
- MTK_FOE_IB2_WDMA_WINFO_V2;
-@@ -446,7 +446,7 @@ int mtk_foe_entry_set_queue(struct mtk_e
- {
- u32 *ib2 = mtk_foe_entry_ib2(eth, entry);
-
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
-+ if (mtk_is_netsys_v2_or_greater(eth)) {
- *ib2 &= ~MTK_FOE_IB2_QID_V2;
- *ib2 |= FIELD_PREP(MTK_FOE_IB2_QID_V2, queue);
- *ib2 |= MTK_FOE_IB2_PSE_QOS_V2;
-@@ -601,7 +601,7 @@ __mtk_foe_entry_commit(struct mtk_ppe *p
- struct mtk_foe_entry *hwe;
- u32 val;
-
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
-+ if (mtk_is_netsys_v2_or_greater(eth)) {
- entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP_V2;
- entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_TIMESTAMP_V2,
- timestamp);
-@@ -617,7 +617,7 @@ __mtk_foe_entry_commit(struct mtk_ppe *p
- hwe->ib1 = entry->ib1;
-
- if (ppe->accounting) {
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-+ if (mtk_is_netsys_v2_or_greater(eth))
- val = MTK_FOE_IB2_MIB_CNT_V2;
- else
- val = MTK_FOE_IB2_MIB_CNT;
-@@ -965,7 +965,7 @@ void mtk_ppe_start(struct mtk_ppe *ppe)
- MTK_PPE_SCAN_MODE_KEEPALIVE_AGE) |
- FIELD_PREP(MTK_PPE_TB_CFG_ENTRY_NUM,
- MTK_PPE_ENTRIES_SHIFT);
-- if (MTK_HAS_CAPS(ppe->eth->soc->caps, MTK_NETSYS_V2))
-+ if (mtk_is_netsys_v2_or_greater(ppe->eth))
- val |= MTK_PPE_TB_CFG_INFO_SEL;
- ppe_w32(ppe, MTK_PPE_TB_CFG, val);
-
-@@ -981,7 +981,7 @@ void mtk_ppe_start(struct mtk_ppe *ppe)
- MTK_PPE_FLOW_CFG_IP4_NAPT |
- MTK_PPE_FLOW_CFG_IP4_DSLITE |
- MTK_PPE_FLOW_CFG_IP4_NAT_FRAG;
-- if (MTK_HAS_CAPS(ppe->eth->soc->caps, MTK_NETSYS_V2))
-+ if (mtk_is_netsys_v2_or_greater(ppe->eth))
- val |= MTK_PPE_MD_TOAP_BYP_CRSN0 |
- MTK_PPE_MD_TOAP_BYP_CRSN1 |
- MTK_PPE_MD_TOAP_BYP_CRSN2 |
-@@ -1023,7 +1023,7 @@ void mtk_ppe_start(struct mtk_ppe *ppe)
-
- ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT, 0);
-
-- if (MTK_HAS_CAPS(ppe->eth->soc->caps, MTK_NETSYS_V2)) {
-+ if (mtk_is_netsys_v2_or_greater(ppe->eth)) {
- ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT1, 0xcb777);
- ppe_w32(ppe, MTK_PPE_SBW_CTRL, 0x7f);
- }
---- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-@@ -193,7 +193,7 @@ mtk_flow_set_output_device(struct mtk_et
- if (mtk_flow_get_wdma_info(dev, dest_mac, &info) == 0) {
- mtk_foe_entry_set_wdma(eth, foe, info.wdma_idx, info.queue,
- info.bss, info.wcid);
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
-+ if (mtk_is_netsys_v2_or_greater(eth)) {
- switch (info.wdma_idx) {
- case 0:
- pse_port = 8;
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -1084,7 +1084,7 @@ mtk_wed_rx_reset(struct mtk_wed_device *
- } else {
- struct mtk_eth *eth = dev->hw->eth;
-
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
-+ if (mtk_is_netsys_v2_or_greater(eth))
- wed_set(dev, MTK_WED_RESET_IDX,
- MTK_WED_RESET_IDX_RX_V2);
- else
-@@ -1806,7 +1806,7 @@ void mtk_wed_add_hw(struct device_node *
- hw->wdma = wdma;
- hw->index = index;
- hw->irq = irq;
-- hw->version = MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) ? 2 : 1;
-+ hw->version = mtk_is_netsys_v1(eth) ? 1 : 2;
-
- if (hw->version == 1) {
- hw->mirror = syscon_regmap_lookup_by_phandle(eth_np,
+++ /dev/null
-From f8fb8dbd158c585be7574faf92db7d614b6722ff Mon Sep 17 00:00:00 2001
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Tue, 25 Jul 2023 01:52:27 +0100
-Subject: [PATCH 100/250] net: ethernet: mtk_eth_soc: increase MAX_DEVS to 3
-
-This is a preliminary patch to add MT7988 SoC support since it runs 3
-macs instead of 2.
-
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Link: https://lore.kernel.org/r/3563e5fab367e7d79a7f1296fabaa5c20f202d7a.1690246066.git.daniel@makrotopia.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -1043,8 +1043,8 @@ struct mtk_soc_data {
-
- #define MTK_DMA_MONITOR_TIMEOUT msecs_to_jiffies(1000)
-
--/* currently no SoC has more than 2 macs */
--#define MTK_MAX_DEVS 2
-+/* currently no SoC has more than 3 macs */
-+#define MTK_MAX_DEVS 3
-
- /* struct mtk_eth - This is the main datasructure for holding the state
- * of the driver
+++ /dev/null
-From 856be974290f28d7943be2ac5a382c4139486196 Mon Sep 17 00:00:00 2001
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Tue, 25 Jul 2023 01:52:44 +0100
-Subject: [PATCH 101/250] net: ethernet: mtk_eth_soc: rely on MTK_MAX_DEVS and
- remove MTK_MAC_COUNT
-
-Get rid of MTK_MAC_COUNT since it is a duplicated of MTK_MAX_DEVS.
-
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Link: https://lore.kernel.org/r/1856f4266f2fc80677807b1bad867659e7b00c65.1690246066.git.daniel@makrotopia.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 49 ++++++++++++---------
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 1 -
- 2 files changed, 27 insertions(+), 23 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -881,7 +881,7 @@ static void mtk_stats_update(struct mtk_
- {
- int i;
-
-- for (i = 0; i < MTK_MAC_COUNT; i++) {
-+ for (i = 0; i < MTK_MAX_DEVS; i++) {
- if (!eth->mac[i] || !eth->mac[i]->hw_stats)
- continue;
- if (spin_trylock(ð->mac[i]->hw_stats->stats_lock)) {
-@@ -1386,7 +1386,7 @@ static int mtk_queue_stopped(struct mtk_
- {
- int i;
-
-- for (i = 0; i < MTK_MAC_COUNT; i++) {
-+ for (i = 0; i < MTK_MAX_DEVS; i++) {
- if (!eth->netdev[i])
- continue;
- if (netif_queue_stopped(eth->netdev[i]))
-@@ -1400,7 +1400,7 @@ static void mtk_wake_queue(struct mtk_et
- {
- int i;
-
-- for (i = 0; i < MTK_MAC_COUNT; i++) {
-+ for (i = 0; i < MTK_MAX_DEVS; i++) {
- if (!eth->netdev[i])
- continue;
- netif_tx_wake_all_queues(eth->netdev[i]);
-@@ -1859,7 +1859,7 @@ static int mtk_poll_rx(struct napi_struc
- !(trxd.rxd4 & RX_DMA_SPECIAL_TAG))
- mac = RX_DMA_GET_SPORT(trxd.rxd4) - 1;
-
-- if (unlikely(mac < 0 || mac >= MTK_MAC_COUNT ||
-+ if (unlikely(mac < 0 || mac >= MTK_MAX_DEVS ||
- !eth->netdev[mac]))
- goto release_desc;
-
-@@ -2899,7 +2899,7 @@ static void mtk_dma_free(struct mtk_eth
- const struct mtk_soc_data *soc = eth->soc;
- int i;
-
-- for (i = 0; i < MTK_MAC_COUNT; i++)
-+ for (i = 0; i < MTK_MAX_DEVS; i++)
- if (eth->netdev[i])
- netdev_reset_queue(eth->netdev[i]);
- if (eth->scratch_ring) {
-@@ -3053,8 +3053,13 @@ static void mtk_gdm_config(struct mtk_et
- if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
- return;
-
-- for (i = 0; i < MTK_MAC_COUNT; i++) {
-- u32 val = mtk_r32(eth, MTK_GDMA_FWD_CFG(i));
-+ for (i = 0; i < MTK_MAX_DEVS; i++) {
-+ u32 val;
-+
-+ if (!eth->netdev[i])
-+ continue;
-+
-+ val = mtk_r32(eth, MTK_GDMA_FWD_CFG(i));
-
- /* default setup the forward port to send frame to PDMA */
- val &= ~0xffff;
-@@ -3064,7 +3069,7 @@ static void mtk_gdm_config(struct mtk_et
-
- val |= config;
-
-- if (eth->netdev[i] && netdev_uses_dsa(eth->netdev[i]))
-+ if (netdev_uses_dsa(eth->netdev[i]))
- val |= MTK_GDMA_SPECIAL_TAG;
-
- mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i));
-@@ -3661,15 +3666,15 @@ static int mtk_hw_init(struct mtk_eth *e
- * up with the more appropriate value when mtk_mac_config call is being
- * invoked.
- */
-- for (i = 0; i < MTK_MAC_COUNT; i++) {
-+ for (i = 0; i < MTK_MAX_DEVS; i++) {
- struct net_device *dev = eth->netdev[i];
-
-- mtk_w32(eth, MAC_MCR_FORCE_LINK_DOWN, MTK_MAC_MCR(i));
-- if (dev) {
-- struct mtk_mac *mac = netdev_priv(dev);
-+ if (!dev)
-+ continue;
-
-- mtk_set_mcr_max_rx(mac, dev->mtu + MTK_RX_ETH_HLEN);
-- }
-+ mtk_w32(eth, MAC_MCR_FORCE_LINK_DOWN, MTK_MAC_MCR(i));
-+ mtk_set_mcr_max_rx(netdev_priv(dev),
-+ dev->mtu + MTK_RX_ETH_HLEN);
- }
-
- /* Indicates CDM to parse the MTK special tag from CPU
-@@ -3849,7 +3854,7 @@ static void mtk_pending_work(struct work
- mtk_prepare_for_reset(eth);
-
- /* stop all devices to make sure that dma is properly shut down */
-- for (i = 0; i < MTK_MAC_COUNT; i++) {
-+ for (i = 0; i < MTK_MAX_DEVS; i++) {
- if (!eth->netdev[i] || !netif_running(eth->netdev[i]))
- continue;
-
-@@ -3865,8 +3870,8 @@ static void mtk_pending_work(struct work
- mtk_hw_init(eth, true);
-
- /* restart DMA and enable IRQs */
-- for (i = 0; i < MTK_MAC_COUNT; i++) {
-- if (!test_bit(i, &restart))
-+ for (i = 0; i < MTK_MAX_DEVS; i++) {
-+ if (!eth->netdev[i] || !test_bit(i, &restart))
- continue;
-
- if (mtk_open(eth->netdev[i])) {
-@@ -3893,7 +3898,7 @@ static int mtk_free_dev(struct mtk_eth *
- {
- int i;
-
-- for (i = 0; i < MTK_MAC_COUNT; i++) {
-+ for (i = 0; i < MTK_MAX_DEVS; i++) {
- if (!eth->netdev[i])
- continue;
- free_netdev(eth->netdev[i]);
-@@ -3912,7 +3917,7 @@ static int mtk_unreg_dev(struct mtk_eth
- {
- int i;
-
-- for (i = 0; i < MTK_MAC_COUNT; i++) {
-+ for (i = 0; i < MTK_MAX_DEVS; i++) {
- struct mtk_mac *mac;
- if (!eth->netdev[i])
- continue;
-@@ -4213,7 +4218,7 @@ static int mtk_add_mac(struct mtk_eth *e
- }
-
- id = be32_to_cpup(_id);
-- if (id >= MTK_MAC_COUNT) {
-+ if (id >= MTK_MAX_DEVS) {
- dev_err(eth->dev, "%d is not a valid mac id\n", id);
- return -EINVAL;
- }
-@@ -4358,7 +4363,7 @@ void mtk_eth_set_dma_device(struct mtk_e
-
- rtnl_lock();
-
-- for (i = 0; i < MTK_MAC_COUNT; i++) {
-+ for (i = 0; i < MTK_MAX_DEVS; i++) {
- dev = eth->netdev[i];
-
- if (!dev || !(dev->flags & IFF_UP))
-@@ -4664,7 +4669,7 @@ static int mtk_remove(struct platform_de
- int i;
-
- /* stop all devices to make sure that dma is properly shut down */
-- for (i = 0; i < MTK_MAC_COUNT; i++) {
-+ for (i = 0; i < MTK_MAX_DEVS; i++) {
- if (!eth->netdev[i])
- continue;
- mtk_stop(eth->netdev[i]);
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -33,7 +33,6 @@
- #define MTK_TX_DMA_BUF_LEN_V2 0xffff
- #define MTK_QDMA_RING_SIZE 2048
- #define MTK_DMA_SIZE 512
--#define MTK_MAC_COUNT 2
- #define MTK_RX_ETH_HLEN (VLAN_ETH_HLEN + ETH_FCS_LEN)
- #define MTK_RX_HLEN (NET_SKB_PAD + MTK_RX_ETH_HLEN + NET_IP_ALIGN)
- #define MTK_DMA_DUMMY_DESC 0xffffffff
+++ /dev/null
-From a41d535855976838d246c079143c948dcf0f7931 Mon Sep 17 00:00:00 2001
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Tue, 25 Jul 2023 01:52:59 +0100
-Subject: [PATCH 102/250] net: ethernet: mtk_eth_soc: add NETSYS_V3 version
- support
-
-Introduce NETSYS_V3 chipset version support.
-This is a preliminary patch to introduce support for MT7988 SoC.
-
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Link: https://lore.kernel.org/r/0db2260910755d76fa48e303b9f9bdf4e5a82340.1690246066.git.daniel@makrotopia.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 105 ++++++++++++++------
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 48 +++++++--
- 2 files changed, 116 insertions(+), 37 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -861,17 +861,32 @@ void mtk_stats_update_mac(struct mtk_mac
- mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x20 + offs);
- hw_stats->rx_flow_control_packets +=
- mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x24 + offs);
-- hw_stats->tx_skip +=
-- mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x28 + offs);
-- hw_stats->tx_collisions +=
-- mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x2c + offs);
-- hw_stats->tx_bytes +=
-- mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x30 + offs);
-- stats = mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x34 + offs);
-- if (stats)
-- hw_stats->tx_bytes += (stats << 32);
-- hw_stats->tx_packets +=
-- mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x38 + offs);
-+
-+ if (mtk_is_netsys_v3_or_greater(eth)) {
-+ hw_stats->tx_skip +=
-+ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x50 + offs);
-+ hw_stats->tx_collisions +=
-+ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x54 + offs);
-+ hw_stats->tx_bytes +=
-+ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x40 + offs);
-+ stats = mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x44 + offs);
-+ if (stats)
-+ hw_stats->tx_bytes += (stats << 32);
-+ hw_stats->tx_packets +=
-+ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x48 + offs);
-+ } else {
-+ hw_stats->tx_skip +=
-+ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x28 + offs);
-+ hw_stats->tx_collisions +=
-+ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x2c + offs);
-+ hw_stats->tx_bytes +=
-+ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x30 + offs);
-+ stats = mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x34 + offs);
-+ if (stats)
-+ hw_stats->tx_bytes += (stats << 32);
-+ hw_stats->tx_packets +=
-+ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x38 + offs);
-+ }
- }
-
- u64_stats_update_end(&hw_stats->syncp);
-@@ -1175,7 +1190,10 @@ static void mtk_tx_set_dma_desc_v2(struc
- data |= TX_DMA_LS0;
- WRITE_ONCE(desc->txd3, data);
-
-- data = (mac->id + 1) << TX_DMA_FPORT_SHIFT_V2; /* forward port */
-+ if (mac->id == MTK_GMAC3_ID)
-+ data = PSE_GDM3_PORT;
-+ else
-+ data = (mac->id + 1) << TX_DMA_FPORT_SHIFT_V2; /* forward port */
- data |= TX_DMA_SWC_V2 | QID_BITS_V2(info->qid);
- WRITE_ONCE(desc->txd4, data);
-
-@@ -1186,6 +1204,8 @@ static void mtk_tx_set_dma_desc_v2(struc
- /* tx checksum offload */
- if (info->csum)
- data |= TX_DMA_CHKSUM_V2;
-+ if (mtk_is_netsys_v3_or_greater(eth) && netdev_uses_dsa(dev))
-+ data |= TX_DMA_SPTAG_V3;
- }
- WRITE_ONCE(desc->txd5, data);
-
-@@ -1251,8 +1271,7 @@ static int mtk_tx_map(struct sk_buff *sk
- mtk_tx_set_dma_desc(dev, itxd, &txd_info);
-
- itx_buf->flags |= MTK_TX_FLAGS_SINGLE0;
-- itx_buf->flags |= (!mac->id) ? MTK_TX_FLAGS_FPORT0 :
-- MTK_TX_FLAGS_FPORT1;
-+ itx_buf->mac_id = mac->id;
- setup_tx_buf(eth, itx_buf, itxd_pdma, txd_info.addr, txd_info.size,
- k++);
-
-@@ -1300,8 +1319,7 @@ static int mtk_tx_map(struct sk_buff *sk
- memset(tx_buf, 0, sizeof(*tx_buf));
- tx_buf->data = (void *)MTK_DMA_DUMMY_DESC;
- tx_buf->flags |= MTK_TX_FLAGS_PAGE0;
-- tx_buf->flags |= (!mac->id) ? MTK_TX_FLAGS_FPORT0 :
-- MTK_TX_FLAGS_FPORT1;
-+ tx_buf->mac_id = mac->id;
-
- setup_tx_buf(eth, tx_buf, txd_pdma, txd_info.addr,
- txd_info.size, k++);
-@@ -1603,7 +1621,7 @@ static int mtk_xdp_frame_map(struct mtk_
- }
- mtk_tx_set_dma_desc(dev, txd, txd_info);
-
-- tx_buf->flags |= !mac->id ? MTK_TX_FLAGS_FPORT0 : MTK_TX_FLAGS_FPORT1;
-+ tx_buf->mac_id = mac->id;
- tx_buf->type = dma_map ? MTK_TYPE_XDP_NDO : MTK_TYPE_XDP_TX;
- tx_buf->data = (void *)MTK_DMA_DUMMY_DESC;
-
-@@ -1853,11 +1871,24 @@ static int mtk_poll_rx(struct napi_struc
- break;
-
- /* find out which mac the packet come from. values start at 1 */
-- if (mtk_is_netsys_v2_or_greater(eth))
-- mac = RX_DMA_GET_SPORT_V2(trxd.rxd5) - 1;
-- else if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628) &&
-- !(trxd.rxd4 & RX_DMA_SPECIAL_TAG))
-+ if (mtk_is_netsys_v2_or_greater(eth)) {
-+ u32 val = RX_DMA_GET_SPORT_V2(trxd.rxd5);
-+
-+ switch (val) {
-+ case PSE_GDM1_PORT:
-+ case PSE_GDM2_PORT:
-+ mac = val - 1;
-+ break;
-+ case PSE_GDM3_PORT:
-+ mac = MTK_GMAC3_ID;
-+ break;
-+ default:
-+ break;
-+ }
-+ } else if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628) &&
-+ !(trxd.rxd4 & RX_DMA_SPECIAL_TAG)) {
- mac = RX_DMA_GET_SPORT(trxd.rxd4) - 1;
-+ }
-
- if (unlikely(mac < 0 || mac >= MTK_MAX_DEVS ||
- !eth->netdev[mac]))
-@@ -2079,7 +2110,6 @@ static int mtk_poll_tx_qdma(struct mtk_e
-
- while ((cpu != dma) && budget) {
- u32 next_cpu = desc->txd2;
-- int mac = 0;
-
- desc = mtk_qdma_phys_to_virt(ring, desc->txd2);
- if ((desc->txd3 & TX_DMA_OWNER_CPU) == 0)
-@@ -2087,15 +2117,13 @@ static int mtk_poll_tx_qdma(struct mtk_e
-
- tx_buf = mtk_desc_to_tx_buf(ring, desc,
- eth->soc->txrx.txd_size);
-- if (tx_buf->flags & MTK_TX_FLAGS_FPORT1)
-- mac = 1;
--
- if (!tx_buf->data)
- break;
-
- if (tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) {
- if (tx_buf->type == MTK_TYPE_SKB)
-- mtk_poll_tx_done(eth, state, mac, tx_buf->data);
-+ mtk_poll_tx_done(eth, state, tx_buf->mac_id,
-+ tx_buf->data);
-
- budget--;
- }
-@@ -3704,7 +3732,24 @@ static int mtk_hw_init(struct mtk_eth *e
- mtk_w32(eth, eth->soc->txrx.rx_irq_done_mask, reg_map->qdma.int_grp + 4);
- mtk_w32(eth, 0x21021000, MTK_FE_INT_GRP);
-
-- if (mtk_is_netsys_v2_or_greater(eth)) {
-+ if (mtk_is_netsys_v3_or_greater(eth)) {
-+ /* PSE should not drop port1, port8 and port9 packets */
-+ mtk_w32(eth, 0x00000302, PSE_DROP_CFG);
-+
-+ /* GDM and CDM Threshold */
-+ mtk_w32(eth, 0x00000707, MTK_CDMW0_THRES);
-+ mtk_w32(eth, 0x00000077, MTK_CDMW1_THRES);
-+
-+ /* Disable GDM1 RX CRC stripping */
-+ mtk_m32(eth, MTK_GDMA_STRP_CRC, 0, MTK_GDMA_FWD_CFG(0));
-+
-+ /* PSE GDM3 MIB counter has incorrect hw default values,
-+ * so the driver ought to read clear the values beforehand
-+ * in case ethtool retrieve wrong mib values.
-+ */
-+ for (i = 0; i < 0x80; i += 0x4)
-+ mtk_r32(eth, reg_map->gdm1_cnt + 0x100 + i);
-+ } else if (!mtk_is_netsys_v1(eth)) {
- /* PSE should not drop port8 and port9 packets from WDMA Tx */
- mtk_w32(eth, 0x00000300, PSE_DROP_CFG);
-
-@@ -4266,7 +4311,11 @@ static int mtk_add_mac(struct mtk_eth *e
- }
- spin_lock_init(&mac->hw_stats->stats_lock);
- u64_stats_init(&mac->hw_stats->syncp);
-- mac->hw_stats->reg_offset = id * MTK_STAT_OFFSET;
-+
-+ if (mtk_is_netsys_v3_or_greater(eth))
-+ mac->hw_stats->reg_offset = id * 0x80;
-+ else
-+ mac->hw_stats->reg_offset = id * 0x40;
-
- /* phylink create */
- err = of_get_phy_mode(np, &phy_mode);
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -122,6 +122,7 @@
- #define MTK_GDMA_ICS_EN BIT(22)
- #define MTK_GDMA_TCS_EN BIT(21)
- #define MTK_GDMA_UCS_EN BIT(20)
-+#define MTK_GDMA_STRP_CRC BIT(16)
- #define MTK_GDMA_TO_PDMA 0x0
- #define MTK_GDMA_DROP_ALL 0x7777
-
-@@ -287,8 +288,6 @@
- /* QDMA Interrupt grouping registers */
- #define MTK_RLS_DONE_INT BIT(0)
-
--#define MTK_STAT_OFFSET 0x40
--
- /* QDMA TX NUM */
- #define QID_BITS_V2(x) (((x) & 0x3f) << 16)
- #define MTK_QDMA_GMAC2_QID 8
-@@ -301,6 +300,8 @@
- #define TX_DMA_CHKSUM_V2 (0x7 << 28)
- #define TX_DMA_TSO_V2 BIT(31)
-
-+#define TX_DMA_SPTAG_V3 BIT(27)
-+
- /* QDMA V2 descriptor txd4 */
- #define TX_DMA_FPORT_SHIFT_V2 8
- #define TX_DMA_FPORT_MASK_V2 0xf
-@@ -634,12 +635,6 @@ enum mtk_tx_flags {
- */
- MTK_TX_FLAGS_SINGLE0 = 0x01,
- MTK_TX_FLAGS_PAGE0 = 0x02,
--
-- /* MTK_TX_FLAGS_FPORTx allows tracking which port the transmitted
-- * SKB out instead of looking up through hardware TX descriptor.
-- */
-- MTK_TX_FLAGS_FPORT0 = 0x04,
-- MTK_TX_FLAGS_FPORT1 = 0x08,
- };
-
- /* This enum allows us to identify how the clock is defined on the array of the
-@@ -725,6 +720,35 @@ enum mtk_dev_state {
- MTK_RESETTING
- };
-
-+/* PSE Port Definition */
-+enum mtk_pse_port {
-+ PSE_ADMA_PORT = 0,
-+ PSE_GDM1_PORT,
-+ PSE_GDM2_PORT,
-+ PSE_PPE0_PORT,
-+ PSE_PPE1_PORT,
-+ PSE_QDMA_TX_PORT,
-+ PSE_QDMA_RX_PORT,
-+ PSE_DROP_PORT,
-+ PSE_WDMA0_PORT,
-+ PSE_WDMA1_PORT,
-+ PSE_TDMA_PORT,
-+ PSE_NONE_PORT,
-+ PSE_PPE2_PORT,
-+ PSE_WDMA2_PORT,
-+ PSE_EIP197_PORT,
-+ PSE_GDM3_PORT,
-+ PSE_PORT_MAX
-+};
-+
-+/* GMAC Identifier */
-+enum mtk_gmac_id {
-+ MTK_GMAC1_ID = 0,
-+ MTK_GMAC2_ID,
-+ MTK_GMAC3_ID,
-+ MTK_GMAC_ID_MAX
-+};
-+
- enum mtk_tx_buf_type {
- MTK_TYPE_SKB,
- MTK_TYPE_XDP_TX,
-@@ -743,7 +767,8 @@ struct mtk_tx_buf {
- enum mtk_tx_buf_type type;
- void *data;
-
-- u32 flags;
-+ u16 mac_id;
-+ u16 flags;
- DEFINE_DMA_UNMAP_ADDR(dma_addr0);
- DEFINE_DMA_UNMAP_LEN(dma_len0);
- DEFINE_DMA_UNMAP_ADDR(dma_addr1);
-@@ -1192,6 +1217,11 @@ static inline bool mtk_is_netsys_v2_or_g
- return eth->soc->version > 1;
- }
-
-+static inline bool mtk_is_netsys_v3_or_greater(struct mtk_eth *eth)
-+{
-+ return eth->soc->version > 2;
-+}
-+
- static inline struct mtk_foe_entry *
- mtk_foe_get_entry(struct mtk_ppe *ppe, u16 hash)
- {
+++ /dev/null
-From db797ae0542220a98658229397da464c383c991c Mon Sep 17 00:00:00 2001
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Tue, 25 Jul 2023 01:53:13 +0100
-Subject: [PATCH 103/250] net: ethernet: mtk_eth_soc: convert caps in
- mtk_soc_data struct to u64
-
-This is a preliminary patch to introduce support for MT7988 SoC.
-
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Link: https://lore.kernel.org/r/9499ac3670b2fc5b444404b84e8a4a169beabbf2.1690246066.git.daniel@makrotopia.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_path.c | 22 ++++----
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 56 ++++++++++----------
- 2 files changed, 39 insertions(+), 39 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_path.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_path.c
-@@ -15,10 +15,10 @@
- struct mtk_eth_muxc {
- const char *name;
- int cap_bit;
-- int (*set_path)(struct mtk_eth *eth, int path);
-+ int (*set_path)(struct mtk_eth *eth, u64 path);
- };
-
--static const char *mtk_eth_path_name(int path)
-+static const char *mtk_eth_path_name(u64 path)
- {
- switch (path) {
- case MTK_ETH_PATH_GMAC1_RGMII:
-@@ -40,7 +40,7 @@ static const char *mtk_eth_path_name(int
- }
- }
-
--static int set_mux_gdm1_to_gmac1_esw(struct mtk_eth *eth, int path)
-+static int set_mux_gdm1_to_gmac1_esw(struct mtk_eth *eth, u64 path)
- {
- bool updated = true;
- u32 val, mask, set;
-@@ -71,7 +71,7 @@ static int set_mux_gdm1_to_gmac1_esw(str
- return 0;
- }
-
--static int set_mux_gmac2_gmac0_to_gephy(struct mtk_eth *eth, int path)
-+static int set_mux_gmac2_gmac0_to_gephy(struct mtk_eth *eth, u64 path)
- {
- unsigned int val = 0;
- bool updated = true;
-@@ -94,7 +94,7 @@ static int set_mux_gmac2_gmac0_to_gephy(
- return 0;
- }
-
--static int set_mux_u3_gmac2_to_qphy(struct mtk_eth *eth, int path)
-+static int set_mux_u3_gmac2_to_qphy(struct mtk_eth *eth, u64 path)
- {
- unsigned int val = 0, mask = 0, reg = 0;
- bool updated = true;
-@@ -125,7 +125,7 @@ static int set_mux_u3_gmac2_to_qphy(stru
- return 0;
- }
-
--static int set_mux_gmac1_gmac2_to_sgmii_rgmii(struct mtk_eth *eth, int path)
-+static int set_mux_gmac1_gmac2_to_sgmii_rgmii(struct mtk_eth *eth, u64 path)
- {
- unsigned int val = 0;
- bool updated = true;
-@@ -163,7 +163,7 @@ static int set_mux_gmac1_gmac2_to_sgmii_
- return 0;
- }
-
--static int set_mux_gmac12_to_gephy_sgmii(struct mtk_eth *eth, int path)
-+static int set_mux_gmac12_to_gephy_sgmii(struct mtk_eth *eth, u64 path)
- {
- unsigned int val = 0;
- bool updated = true;
-@@ -218,7 +218,7 @@ static const struct mtk_eth_muxc mtk_eth
- },
- };
-
--static int mtk_eth_mux_setup(struct mtk_eth *eth, int path)
-+static int mtk_eth_mux_setup(struct mtk_eth *eth, u64 path)
- {
- int i, err = 0;
-
-@@ -249,7 +249,7 @@ out:
-
- int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id)
- {
-- int path;
-+ u64 path;
-
- path = (mac_id == 0) ? MTK_ETH_PATH_GMAC1_SGMII :
- MTK_ETH_PATH_GMAC2_SGMII;
-@@ -260,7 +260,7 @@ int mtk_gmac_sgmii_path_setup(struct mtk
-
- int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id)
- {
-- int path = 0;
-+ u64 path = 0;
-
- if (mac_id == 1)
- path = MTK_ETH_PATH_GMAC2_GEPHY;
-@@ -274,7 +274,7 @@ int mtk_gmac_gephy_path_setup(struct mtk
-
- int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id)
- {
-- int path;
-+ u64 path;
-
- path = (mac_id == 0) ? MTK_ETH_PATH_GMAC1_RGMII :
- MTK_ETH_PATH_GMAC2_RGMII;
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -866,41 +866,41 @@ enum mkt_eth_capabilities {
- };
-
- /* Supported hardware group on SoCs */
--#define MTK_RGMII BIT(MTK_RGMII_BIT)
--#define MTK_TRGMII BIT(MTK_TRGMII_BIT)
--#define MTK_SGMII BIT(MTK_SGMII_BIT)
--#define MTK_ESW BIT(MTK_ESW_BIT)
--#define MTK_GEPHY BIT(MTK_GEPHY_BIT)
--#define MTK_MUX BIT(MTK_MUX_BIT)
--#define MTK_INFRA BIT(MTK_INFRA_BIT)
--#define MTK_SHARED_SGMII BIT(MTK_SHARED_SGMII_BIT)
--#define MTK_HWLRO BIT(MTK_HWLRO_BIT)
--#define MTK_SHARED_INT BIT(MTK_SHARED_INT_BIT)
--#define MTK_TRGMII_MT7621_CLK BIT(MTK_TRGMII_MT7621_CLK_BIT)
--#define MTK_QDMA BIT(MTK_QDMA_BIT)
--#define MTK_SOC_MT7628 BIT(MTK_SOC_MT7628_BIT)
--#define MTK_RSTCTRL_PPE1 BIT(MTK_RSTCTRL_PPE1_BIT)
--#define MTK_U3_COPHY_V2 BIT(MTK_U3_COPHY_V2_BIT)
-+#define MTK_RGMII BIT_ULL(MTK_RGMII_BIT)
-+#define MTK_TRGMII BIT_ULL(MTK_TRGMII_BIT)
-+#define MTK_SGMII BIT_ULL(MTK_SGMII_BIT)
-+#define MTK_ESW BIT_ULL(MTK_ESW_BIT)
-+#define MTK_GEPHY BIT_ULL(MTK_GEPHY_BIT)
-+#define MTK_MUX BIT_ULL(MTK_MUX_BIT)
-+#define MTK_INFRA BIT_ULL(MTK_INFRA_BIT)
-+#define MTK_SHARED_SGMII BIT_ULL(MTK_SHARED_SGMII_BIT)
-+#define MTK_HWLRO BIT_ULL(MTK_HWLRO_BIT)
-+#define MTK_SHARED_INT BIT_ULL(MTK_SHARED_INT_BIT)
-+#define MTK_TRGMII_MT7621_CLK BIT_ULL(MTK_TRGMII_MT7621_CLK_BIT)
-+#define MTK_QDMA BIT_ULL(MTK_QDMA_BIT)
-+#define MTK_SOC_MT7628 BIT_ULL(MTK_SOC_MT7628_BIT)
-+#define MTK_RSTCTRL_PPE1 BIT_ULL(MTK_RSTCTRL_PPE1_BIT)
-+#define MTK_U3_COPHY_V2 BIT_ULL(MTK_U3_COPHY_V2_BIT)
-
- #define MTK_ETH_MUX_GDM1_TO_GMAC1_ESW \
-- BIT(MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT)
-+ BIT_ULL(MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT)
- #define MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY \
-- BIT(MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT)
-+ BIT_ULL(MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT)
- #define MTK_ETH_MUX_U3_GMAC2_TO_QPHY \
-- BIT(MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT)
-+ BIT_ULL(MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT)
- #define MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII \
-- BIT(MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT)
-+ BIT_ULL(MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT)
- #define MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII \
-- BIT(MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_BIT)
-+ BIT_ULL(MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_BIT)
-
- /* Supported path present on SoCs */
--#define MTK_ETH_PATH_GMAC1_RGMII BIT(MTK_ETH_PATH_GMAC1_RGMII_BIT)
--#define MTK_ETH_PATH_GMAC1_TRGMII BIT(MTK_ETH_PATH_GMAC1_TRGMII_BIT)
--#define MTK_ETH_PATH_GMAC1_SGMII BIT(MTK_ETH_PATH_GMAC1_SGMII_BIT)
--#define MTK_ETH_PATH_GMAC2_RGMII BIT(MTK_ETH_PATH_GMAC2_RGMII_BIT)
--#define MTK_ETH_PATH_GMAC2_SGMII BIT(MTK_ETH_PATH_GMAC2_SGMII_BIT)
--#define MTK_ETH_PATH_GMAC2_GEPHY BIT(MTK_ETH_PATH_GMAC2_GEPHY_BIT)
--#define MTK_ETH_PATH_GDM1_ESW BIT(MTK_ETH_PATH_GDM1_ESW_BIT)
-+#define MTK_ETH_PATH_GMAC1_RGMII BIT_ULL(MTK_ETH_PATH_GMAC1_RGMII_BIT)
-+#define MTK_ETH_PATH_GMAC1_TRGMII BIT_ULL(MTK_ETH_PATH_GMAC1_TRGMII_BIT)
-+#define MTK_ETH_PATH_GMAC1_SGMII BIT_ULL(MTK_ETH_PATH_GMAC1_SGMII_BIT)
-+#define MTK_ETH_PATH_GMAC2_RGMII BIT_ULL(MTK_ETH_PATH_GMAC2_RGMII_BIT)
-+#define MTK_ETH_PATH_GMAC2_SGMII BIT_ULL(MTK_ETH_PATH_GMAC2_SGMII_BIT)
-+#define MTK_ETH_PATH_GMAC2_GEPHY BIT_ULL(MTK_ETH_PATH_GMAC2_GEPHY_BIT)
-+#define MTK_ETH_PATH_GDM1_ESW BIT_ULL(MTK_ETH_PATH_GDM1_ESW_BIT)
-
- #define MTK_GMAC1_RGMII (MTK_ETH_PATH_GMAC1_RGMII | MTK_RGMII)
- #define MTK_GMAC1_TRGMII (MTK_ETH_PATH_GMAC1_TRGMII | MTK_TRGMII)
-@@ -1045,7 +1045,7 @@ struct mtk_reg_map {
- struct mtk_soc_data {
- const struct mtk_reg_map *reg_map;
- u32 ana_rgc3;
-- u32 caps;
-+ u64 caps;
- u32 required_clks;
- bool required_pctl;
- u8 offload_version;
+++ /dev/null
-From a1c9f7d1d24e90294f6a6755b137fcf306851e93 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Tue, 25 Jul 2023 01:53:28 +0100
-Subject: [PATCH 104/250] net: ethernet: mtk_eth_soc: convert clock bitmap to
- u64
-
-The to-be-added MT7988 SoC adds many new clocks which need to be
-controlled by the Ethernet driver, which will result in their total
-number exceeding 32.
-Prepare by converting clock bitmaps into 64-bit types.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Link: https://lore.kernel.org/r/6960a39bb0078cf84d7642a9558e6a91c6cc9df3.1690246066.git.daniel@makrotopia.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 96 +++++++++++----------
- 1 file changed, 49 insertions(+), 47 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -666,54 +666,56 @@ enum mtk_clks_map {
- MTK_CLK_MAX
- };
-
--#define MT7623_CLKS_BITMAP (BIT(MTK_CLK_ETHIF) | BIT(MTK_CLK_ESW) | \
-- BIT(MTK_CLK_GP1) | BIT(MTK_CLK_GP2) | \
-- BIT(MTK_CLK_TRGPLL))
--#define MT7622_CLKS_BITMAP (BIT(MTK_CLK_ETHIF) | BIT(MTK_CLK_ESW) | \
-- BIT(MTK_CLK_GP0) | BIT(MTK_CLK_GP1) | \
-- BIT(MTK_CLK_GP2) | \
-- BIT(MTK_CLK_SGMII_TX_250M) | \
-- BIT(MTK_CLK_SGMII_RX_250M) | \
-- BIT(MTK_CLK_SGMII_CDR_REF) | \
-- BIT(MTK_CLK_SGMII_CDR_FB) | \
-- BIT(MTK_CLK_SGMII_CK) | \
-- BIT(MTK_CLK_ETH2PLL))
-+#define MT7623_CLKS_BITMAP (BIT_ULL(MTK_CLK_ETHIF) | BIT_ULL(MTK_CLK_ESW) | \
-+ BIT_ULL(MTK_CLK_GP1) | BIT_ULL(MTK_CLK_GP2) | \
-+ BIT_ULL(MTK_CLK_TRGPLL))
-+#define MT7622_CLKS_BITMAP (BIT_ULL(MTK_CLK_ETHIF) | BIT_ULL(MTK_CLK_ESW) | \
-+ BIT_ULL(MTK_CLK_GP0) | BIT_ULL(MTK_CLK_GP1) | \
-+ BIT_ULL(MTK_CLK_GP2) | \
-+ BIT_ULL(MTK_CLK_SGMII_TX_250M) | \
-+ BIT_ULL(MTK_CLK_SGMII_RX_250M) | \
-+ BIT_ULL(MTK_CLK_SGMII_CDR_REF) | \
-+ BIT_ULL(MTK_CLK_SGMII_CDR_FB) | \
-+ BIT_ULL(MTK_CLK_SGMII_CK) | \
-+ BIT_ULL(MTK_CLK_ETH2PLL))
- #define MT7621_CLKS_BITMAP (0)
- #define MT7628_CLKS_BITMAP (0)
--#define MT7629_CLKS_BITMAP (BIT(MTK_CLK_ETHIF) | BIT(MTK_CLK_ESW) | \
-- BIT(MTK_CLK_GP0) | BIT(MTK_CLK_GP1) | \
-- BIT(MTK_CLK_GP2) | BIT(MTK_CLK_FE) | \
-- BIT(MTK_CLK_SGMII_TX_250M) | \
-- BIT(MTK_CLK_SGMII_RX_250M) | \
-- BIT(MTK_CLK_SGMII_CDR_REF) | \
-- BIT(MTK_CLK_SGMII_CDR_FB) | \
-- BIT(MTK_CLK_SGMII2_TX_250M) | \
-- BIT(MTK_CLK_SGMII2_RX_250M) | \
-- BIT(MTK_CLK_SGMII2_CDR_REF) | \
-- BIT(MTK_CLK_SGMII2_CDR_FB) | \
-- BIT(MTK_CLK_SGMII_CK) | \
-- BIT(MTK_CLK_ETH2PLL) | BIT(MTK_CLK_SGMIITOP))
--#define MT7981_CLKS_BITMAP (BIT(MTK_CLK_FE) | BIT(MTK_CLK_GP2) | BIT(MTK_CLK_GP1) | \
-- BIT(MTK_CLK_WOCPU0) | \
-- BIT(MTK_CLK_SGMII_TX_250M) | \
-- BIT(MTK_CLK_SGMII_RX_250M) | \
-- BIT(MTK_CLK_SGMII_CDR_REF) | \
-- BIT(MTK_CLK_SGMII_CDR_FB) | \
-- BIT(MTK_CLK_SGMII2_TX_250M) | \
-- BIT(MTK_CLK_SGMII2_RX_250M) | \
-- BIT(MTK_CLK_SGMII2_CDR_REF) | \
-- BIT(MTK_CLK_SGMII2_CDR_FB) | \
-- BIT(MTK_CLK_SGMII_CK))
--#define MT7986_CLKS_BITMAP (BIT(MTK_CLK_FE) | BIT(MTK_CLK_GP2) | BIT(MTK_CLK_GP1) | \
-- BIT(MTK_CLK_WOCPU1) | BIT(MTK_CLK_WOCPU0) | \
-- BIT(MTK_CLK_SGMII_TX_250M) | \
-- BIT(MTK_CLK_SGMII_RX_250M) | \
-- BIT(MTK_CLK_SGMII_CDR_REF) | \
-- BIT(MTK_CLK_SGMII_CDR_FB) | \
-- BIT(MTK_CLK_SGMII2_TX_250M) | \
-- BIT(MTK_CLK_SGMII2_RX_250M) | \
-- BIT(MTK_CLK_SGMII2_CDR_REF) | \
-- BIT(MTK_CLK_SGMII2_CDR_FB))
-+#define MT7629_CLKS_BITMAP (BIT_ULL(MTK_CLK_ETHIF) | BIT_ULL(MTK_CLK_ESW) | \
-+ BIT_ULL(MTK_CLK_GP0) | BIT_ULL(MTK_CLK_GP1) | \
-+ BIT_ULL(MTK_CLK_GP2) | BIT_ULL(MTK_CLK_FE) | \
-+ BIT_ULL(MTK_CLK_SGMII_TX_250M) | \
-+ BIT_ULL(MTK_CLK_SGMII_RX_250M) | \
-+ BIT_ULL(MTK_CLK_SGMII_CDR_REF) | \
-+ BIT_ULL(MTK_CLK_SGMII_CDR_FB) | \
-+ BIT_ULL(MTK_CLK_SGMII2_TX_250M) | \
-+ BIT_ULL(MTK_CLK_SGMII2_RX_250M) | \
-+ BIT_ULL(MTK_CLK_SGMII2_CDR_REF) | \
-+ BIT_ULL(MTK_CLK_SGMII2_CDR_FB) | \
-+ BIT_ULL(MTK_CLK_SGMII_CK) | \
-+ BIT_ULL(MTK_CLK_ETH2PLL) | BIT_ULL(MTK_CLK_SGMIITOP))
-+#define MT7981_CLKS_BITMAP (BIT_ULL(MTK_CLK_FE) | BIT_ULL(MTK_CLK_GP2) | \
-+ BIT_ULL(MTK_CLK_GP1) | \
-+ BIT_ULL(MTK_CLK_WOCPU0) | \
-+ BIT_ULL(MTK_CLK_SGMII_TX_250M) | \
-+ BIT_ULL(MTK_CLK_SGMII_RX_250M) | \
-+ BIT_ULL(MTK_CLK_SGMII_CDR_REF) | \
-+ BIT_ULL(MTK_CLK_SGMII_CDR_FB) | \
-+ BIT_ULL(MTK_CLK_SGMII2_TX_250M) | \
-+ BIT_ULL(MTK_CLK_SGMII2_RX_250M) | \
-+ BIT_ULL(MTK_CLK_SGMII2_CDR_REF) | \
-+ BIT_ULL(MTK_CLK_SGMII2_CDR_FB) | \
-+ BIT_ULL(MTK_CLK_SGMII_CK))
-+#define MT7986_CLKS_BITMAP (BIT_ULL(MTK_CLK_FE) | BIT_ULL(MTK_CLK_GP2) | \
-+ BIT_ULL(MTK_CLK_GP1) | \
-+ BIT_ULL(MTK_CLK_WOCPU1) | BIT_ULL(MTK_CLK_WOCPU0) | \
-+ BIT_ULL(MTK_CLK_SGMII_TX_250M) | \
-+ BIT_ULL(MTK_CLK_SGMII_RX_250M) | \
-+ BIT_ULL(MTK_CLK_SGMII_CDR_REF) | \
-+ BIT_ULL(MTK_CLK_SGMII_CDR_FB) | \
-+ BIT_ULL(MTK_CLK_SGMII2_TX_250M) | \
-+ BIT_ULL(MTK_CLK_SGMII2_RX_250M) | \
-+ BIT_ULL(MTK_CLK_SGMII2_CDR_REF) | \
-+ BIT_ULL(MTK_CLK_SGMII2_CDR_FB))
-
- enum mtk_dev_state {
- MTK_HW_INIT,
-@@ -1046,7 +1048,7 @@ struct mtk_soc_data {
- const struct mtk_reg_map *reg_map;
- u32 ana_rgc3;
- u64 caps;
-- u32 required_clks;
-+ u64 required_clks;
- bool required_pctl;
- u8 offload_version;
- u8 hash_offset;
+++ /dev/null
-From 94f825a7eadfc8b4c8828efdb7705d9703f9c73e Mon Sep 17 00:00:00 2001
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Tue, 25 Jul 2023 01:57:42 +0100
-Subject: [PATCH 105/250] net: ethernet: mtk_eth_soc: add basic support for
- MT7988 SoC
-
-Introduce support for ethernet chip available in MT7988 SoC to
-mtk_eth_soc driver. As a first step support only the first GMAC which
-is hard-wired to the internal DSA switch having 4 built-in gigabit
-Ethernet PHYs.
-
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Link: https://lore.kernel.org/r/25c8377095b95d186872eeda7aa055da83e8f0ca.1690246605.git.daniel@makrotopia.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_path.c | 14 +-
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 201 +++++++++++++++++--
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 86 +++++++-
- 3 files changed, 273 insertions(+), 28 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_path.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_path.c
-@@ -43,7 +43,7 @@ static const char *mtk_eth_path_name(u64
- static int set_mux_gdm1_to_gmac1_esw(struct mtk_eth *eth, u64 path)
- {
- bool updated = true;
-- u32 val, mask, set;
-+ u32 mask, set, reg;
-
- switch (path) {
- case MTK_ETH_PATH_GMAC1_SGMII:
-@@ -59,11 +59,13 @@ static int set_mux_gdm1_to_gmac1_esw(str
- break;
- }
-
-- if (updated) {
-- val = mtk_r32(eth, MTK_MAC_MISC);
-- val = (val & mask) | set;
-- mtk_w32(eth, val, MTK_MAC_MISC);
-- }
-+ if (mtk_is_netsys_v3_or_greater(eth))
-+ reg = MTK_MAC_MISC_V3;
-+ else
-+ reg = MTK_MAC_MISC;
-+
-+ if (updated)
-+ mtk_m32(eth, mask, set, reg);
-
- dev_dbg(eth->dev, "path %s in %s updated = %d\n",
- mtk_eth_path_name(path), __func__, updated);
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -152,6 +152,54 @@ static const struct mtk_reg_map mt7986_r
- .pse_oq_sta = 0x01a0,
- };
-
-+static const struct mtk_reg_map mt7988_reg_map = {
-+ .tx_irq_mask = 0x461c,
-+ .tx_irq_status = 0x4618,
-+ .pdma = {
-+ .rx_ptr = 0x6900,
-+ .rx_cnt_cfg = 0x6904,
-+ .pcrx_ptr = 0x6908,
-+ .glo_cfg = 0x6a04,
-+ .rst_idx = 0x6a08,
-+ .delay_irq = 0x6a0c,
-+ .irq_status = 0x6a20,
-+ .irq_mask = 0x6a28,
-+ .adma_rx_dbg0 = 0x6a38,
-+ .int_grp = 0x6a50,
-+ },
-+ .qdma = {
-+ .qtx_cfg = 0x4400,
-+ .qtx_sch = 0x4404,
-+ .rx_ptr = 0x4500,
-+ .rx_cnt_cfg = 0x4504,
-+ .qcrx_ptr = 0x4508,
-+ .glo_cfg = 0x4604,
-+ .rst_idx = 0x4608,
-+ .delay_irq = 0x460c,
-+ .fc_th = 0x4610,
-+ .int_grp = 0x4620,
-+ .hred = 0x4644,
-+ .ctx_ptr = 0x4700,
-+ .dtx_ptr = 0x4704,
-+ .crx_ptr = 0x4710,
-+ .drx_ptr = 0x4714,
-+ .fq_head = 0x4720,
-+ .fq_tail = 0x4724,
-+ .fq_count = 0x4728,
-+ .fq_blen = 0x472c,
-+ .tx_sch_rate = 0x4798,
-+ },
-+ .gdm1_cnt = 0x1c00,
-+ .gdma_to_ppe = 0x3333,
-+ .ppe_base = 0x2000,
-+ .wdma_base = {
-+ [0] = 0x4800,
-+ [1] = 0x4c00,
-+ },
-+ .pse_iq_sta = 0x0180,
-+ .pse_oq_sta = 0x01a0,
-+};
-+
- /* strings used by ethtool */
- static const struct mtk_ethtool_stats {
- char str[ETH_GSTRING_LEN];
-@@ -179,10 +227,54 @@ static const struct mtk_ethtool_stats {
- };
-
- static const char * const mtk_clks_source_name[] = {
-- "ethif", "sgmiitop", "esw", "gp0", "gp1", "gp2", "fe", "trgpll",
-- "sgmii_tx250m", "sgmii_rx250m", "sgmii_cdr_ref", "sgmii_cdr_fb",
-- "sgmii2_tx250m", "sgmii2_rx250m", "sgmii2_cdr_ref", "sgmii2_cdr_fb",
-- "sgmii_ck", "eth2pll", "wocpu0", "wocpu1", "netsys0", "netsys1"
-+ "ethif",
-+ "sgmiitop",
-+ "esw",
-+ "gp0",
-+ "gp1",
-+ "gp2",
-+ "gp3",
-+ "xgp1",
-+ "xgp2",
-+ "xgp3",
-+ "crypto",
-+ "fe",
-+ "trgpll",
-+ "sgmii_tx250m",
-+ "sgmii_rx250m",
-+ "sgmii_cdr_ref",
-+ "sgmii_cdr_fb",
-+ "sgmii2_tx250m",
-+ "sgmii2_rx250m",
-+ "sgmii2_cdr_ref",
-+ "sgmii2_cdr_fb",
-+ "sgmii_ck",
-+ "eth2pll",
-+ "wocpu0",
-+ "wocpu1",
-+ "netsys0",
-+ "netsys1",
-+ "ethwarp_wocpu2",
-+ "ethwarp_wocpu1",
-+ "ethwarp_wocpu0",
-+ "top_usxgmii0_sel",
-+ "top_usxgmii1_sel",
-+ "top_sgm0_sel",
-+ "top_sgm1_sel",
-+ "top_xfi_phy0_xtal_sel",
-+ "top_xfi_phy1_xtal_sel",
-+ "top_eth_gmii_sel",
-+ "top_eth_refck_50m_sel",
-+ "top_eth_sys_200m_sel",
-+ "top_eth_sys_sel",
-+ "top_eth_xgmii_sel",
-+ "top_eth_mii_sel",
-+ "top_netsys_sel",
-+ "top_netsys_500m_sel",
-+ "top_netsys_pao_2x_sel",
-+ "top_netsys_sync_250m_sel",
-+ "top_netsys_ppefb_250m_sel",
-+ "top_netsys_warp_sel",
- };
-
- void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg)
-@@ -195,7 +287,7 @@ u32 mtk_r32(struct mtk_eth *eth, unsigne
- return __raw_readl(eth->base + reg);
- }
-
--static u32 mtk_m32(struct mtk_eth *eth, u32 mask, u32 set, unsigned reg)
-+u32 mtk_m32(struct mtk_eth *eth, u32 mask, u32 set, unsigned int reg)
- {
- u32 val;
-
-@@ -369,6 +461,19 @@ static void mtk_gmac0_rgmii_adjust(struc
- dev_err(eth->dev, "Missing PLL configuration, ethernet may not work\n");
- }
-
-+static void mtk_setup_bridge_switch(struct mtk_eth *eth)
-+{
-+ /* Force Port1 XGMAC Link Up */
-+ mtk_m32(eth, 0, MTK_XGMAC_FORCE_LINK(MTK_GMAC1_ID),
-+ MTK_XGMAC_STS(MTK_GMAC1_ID));
-+
-+ /* Adjust GSW bridge IPG to 11 */
-+ mtk_m32(eth, GSWTX_IPG_MASK | GSWRX_IPG_MASK,
-+ (GSW_IPG_11 << GSWTX_IPG_SHIFT) |
-+ (GSW_IPG_11 << GSWRX_IPG_SHIFT),
-+ MTK_GSW_CFG);
-+}
-+
- static struct phylink_pcs *mtk_mac_select_pcs(struct phylink_config *config,
- phy_interface_t interface)
- {
-@@ -438,6 +543,8 @@ static void mtk_mac_config(struct phylin
- goto init_err;
- }
- break;
-+ case PHY_INTERFACE_MODE_INTERNAL:
-+ break;
- default:
- goto err_phy;
- }
-@@ -515,6 +622,15 @@ static void mtk_mac_config(struct phylin
- return;
- }
-
-+ /* Setup gmac */
-+ if (mtk_is_netsys_v3_or_greater(eth) &&
-+ mac->interface == PHY_INTERFACE_MODE_INTERNAL) {
-+ mtk_w32(mac->hw, MTK_GDMA_XGDM_SEL, MTK_GDMA_EG_CTRL(mac->id));
-+ mtk_w32(mac->hw, MAC_MCR_FORCE_LINK_DOWN, MTK_MAC_MCR(mac->id));
-+
-+ mtk_setup_bridge_switch(eth);
-+ }
-+
- return;
-
- err_phy:
-@@ -725,11 +841,15 @@ static int mtk_mdio_init(struct mtk_eth
- }
- divider = min_t(unsigned int, DIV_ROUND_UP(MDC_MAX_FREQ, max_clk), 63);
-
-+ /* Configure MDC Turbo Mode */
-+ if (mtk_is_netsys_v3_or_greater(eth))
-+ mtk_m32(eth, 0, MISC_MDC_TURBO, MTK_MAC_MISC_V3);
-+
- /* Configure MDC Divider */
-- val = mtk_r32(eth, MTK_PPSC);
-- val &= ~PPSC_MDC_CFG;
-- val |= FIELD_PREP(PPSC_MDC_CFG, divider) | PPSC_MDC_TURBO;
-- mtk_w32(eth, val, MTK_PPSC);
-+ val = FIELD_PREP(PPSC_MDC_CFG, divider);
-+ if (!mtk_is_netsys_v3_or_greater(eth))
-+ val |= PPSC_MDC_TURBO;
-+ mtk_m32(eth, PPSC_MDC_CFG, val, MTK_PPSC);
-
- dev_dbg(eth->dev, "MDC is running on %d Hz\n", MDC_MAX_FREQ / divider);
-
-@@ -1190,10 +1310,19 @@ static void mtk_tx_set_dma_desc_v2(struc
- data |= TX_DMA_LS0;
- WRITE_ONCE(desc->txd3, data);
-
-- if (mac->id == MTK_GMAC3_ID)
-- data = PSE_GDM3_PORT;
-- else
-- data = (mac->id + 1) << TX_DMA_FPORT_SHIFT_V2; /* forward port */
-+ /* set forward port */
-+ switch (mac->id) {
-+ case MTK_GMAC1_ID:
-+ data = PSE_GDM1_PORT << TX_DMA_FPORT_SHIFT_V2;
-+ break;
-+ case MTK_GMAC2_ID:
-+ data = PSE_GDM2_PORT << TX_DMA_FPORT_SHIFT_V2;
-+ break;
-+ case MTK_GMAC3_ID:
-+ data = PSE_GDM3_PORT << TX_DMA_FPORT_SHIFT_V2;
-+ break;
-+ }
-+
- data |= TX_DMA_SWC_V2 | QID_BITS_V2(info->qid);
- WRITE_ONCE(desc->txd4, data);
-
-@@ -4360,6 +4489,17 @@ static int mtk_add_mac(struct mtk_eth *e
- mac->phylink_config.supported_interfaces);
- }
-
-+ if (mtk_is_netsys_v3_or_greater(mac->hw) &&
-+ MTK_HAS_CAPS(mac->hw->soc->caps, MTK_ESW_BIT) &&
-+ id == MTK_GMAC1_ID) {
-+ mac->phylink_config.mac_capabilities = MAC_ASYM_PAUSE |
-+ MAC_SYM_PAUSE |
-+ MAC_10000FD;
-+ phy_interface_zero(mac->phylink_config.supported_interfaces);
-+ __set_bit(PHY_INTERFACE_MODE_INTERNAL,
-+ mac->phylink_config.supported_interfaces);
-+ }
-+
- phylink = phylink_create(&mac->phylink_config,
- of_fwnode_handle(mac->of_node),
- phy_mode, &mtk_phylink_ops);
-@@ -4880,6 +5020,24 @@ static const struct mtk_soc_data mt7986_
- },
- };
-
-+static const struct mtk_soc_data mt7988_data = {
-+ .reg_map = &mt7988_reg_map,
-+ .ana_rgc3 = 0x128,
-+ .caps = MT7988_CAPS,
-+ .hw_features = MTK_HW_FEATURES,
-+ .required_clks = MT7988_CLKS_BITMAP,
-+ .required_pctl = false,
-+ .version = 3,
-+ .txrx = {
-+ .txd_size = sizeof(struct mtk_tx_dma_v2),
-+ .rxd_size = sizeof(struct mtk_rx_dma_v2),
-+ .rx_irq_done_mask = MTK_RX_DONE_INT_V2,
-+ .rx_dma_l4_valid = RX_DMA_L4_VALID_V2,
-+ .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
-+ .dma_len_offset = 8,
-+ },
-+};
-+
- static const struct mtk_soc_data rt5350_data = {
- .reg_map = &mt7628_reg_map,
- .caps = MT7628_CAPS,
-@@ -4898,14 +5056,15 @@ static const struct mtk_soc_data rt5350_
- };
-
- const struct of_device_id of_mtk_match[] = {
-- { .compatible = "mediatek,mt2701-eth", .data = &mt2701_data},
-- { .compatible = "mediatek,mt7621-eth", .data = &mt7621_data},
-- { .compatible = "mediatek,mt7622-eth", .data = &mt7622_data},
-- { .compatible = "mediatek,mt7623-eth", .data = &mt7623_data},
-- { .compatible = "mediatek,mt7629-eth", .data = &mt7629_data},
-- { .compatible = "mediatek,mt7981-eth", .data = &mt7981_data},
-- { .compatible = "mediatek,mt7986-eth", .data = &mt7986_data},
-- { .compatible = "ralink,rt5350-eth", .data = &rt5350_data},
-+ { .compatible = "mediatek,mt2701-eth", .data = &mt2701_data },
-+ { .compatible = "mediatek,mt7621-eth", .data = &mt7621_data },
-+ { .compatible = "mediatek,mt7622-eth", .data = &mt7622_data },
-+ { .compatible = "mediatek,mt7623-eth", .data = &mt7623_data },
-+ { .compatible = "mediatek,mt7629-eth", .data = &mt7629_data },
-+ { .compatible = "mediatek,mt7981-eth", .data = &mt7981_data },
-+ { .compatible = "mediatek,mt7986-eth", .data = &mt7986_data },
-+ { .compatible = "mediatek,mt7988-eth", .data = &mt7988_data },
-+ { .compatible = "ralink,rt5350-eth", .data = &rt5350_data },
- {},
- };
- MODULE_DEVICE_TABLE(of, of_mtk_match);
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -117,7 +117,8 @@
- #define MTK_CDMP_EG_CTRL 0x404
-
- /* GDM Exgress Control Register */
--#define MTK_GDMA_FWD_CFG(x) (0x500 + (x * 0x1000))
-+#define MTK_GDMA_FWD_CFG(x) ({ typeof(x) _x = (x); (_x == MTK_GMAC3_ID) ? \
-+ 0x540 : 0x500 + (_x * 0x1000); })
- #define MTK_GDMA_SPECIAL_TAG BIT(24)
- #define MTK_GDMA_ICS_EN BIT(22)
- #define MTK_GDMA_TCS_EN BIT(21)
-@@ -126,6 +127,11 @@
- #define MTK_GDMA_TO_PDMA 0x0
- #define MTK_GDMA_DROP_ALL 0x7777
-
-+/* GDM Egress Control Register */
-+#define MTK_GDMA_EG_CTRL(x) ({ typeof(x) _x = (x); (_x == MTK_GMAC3_ID) ? \
-+ 0x544 : 0x504 + (_x * 0x1000); })
-+#define MTK_GDMA_XGDM_SEL BIT(31)
-+
- /* Unicast Filter MAC Address Register - Low */
- #define MTK_GDMA_MAC_ADRL(x) (0x508 + (x * 0x1000))
-
-@@ -389,7 +395,26 @@
- #define PHY_IAC_TIMEOUT HZ
-
- #define MTK_MAC_MISC 0x1000c
-+#define MTK_MAC_MISC_V3 0x10010
- #define MTK_MUX_TO_ESW BIT(0)
-+#define MISC_MDC_TURBO BIT(4)
-+
-+/* XMAC status registers */
-+#define MTK_XGMAC_STS(x) (((x) == MTK_GMAC3_ID) ? 0x1001C : 0x1000C)
-+#define MTK_XGMAC_FORCE_LINK(x) (((x) == MTK_GMAC2_ID) ? BIT(31) : BIT(15))
-+#define MTK_USXGMII_PCS_LINK BIT(8)
-+#define MTK_XGMAC_RX_FC BIT(5)
-+#define MTK_XGMAC_TX_FC BIT(4)
-+#define MTK_USXGMII_PCS_MODE GENMASK(3, 1)
-+#define MTK_XGMAC_LINK_STS BIT(0)
-+
-+/* GSW bridge registers */
-+#define MTK_GSW_CFG (0x10080)
-+#define GSWTX_IPG_MASK GENMASK(19, 16)
-+#define GSWTX_IPG_SHIFT 16
-+#define GSWRX_IPG_MASK GENMASK(3, 0)
-+#define GSWRX_IPG_SHIFT 0
-+#define GSW_IPG_11 11
-
- /* Mac control registers */
- #define MTK_MAC_MCR(x) (0x10100 + (x * 0x100))
-@@ -647,6 +672,11 @@ enum mtk_clks_map {
- MTK_CLK_GP0,
- MTK_CLK_GP1,
- MTK_CLK_GP2,
-+ MTK_CLK_GP3,
-+ MTK_CLK_XGP1,
-+ MTK_CLK_XGP2,
-+ MTK_CLK_XGP3,
-+ MTK_CLK_CRYPTO,
- MTK_CLK_FE,
- MTK_CLK_TRGPLL,
- MTK_CLK_SGMII_TX_250M,
-@@ -663,6 +693,27 @@ enum mtk_clks_map {
- MTK_CLK_WOCPU1,
- MTK_CLK_NETSYS0,
- MTK_CLK_NETSYS1,
-+ MTK_CLK_ETHWARP_WOCPU2,
-+ MTK_CLK_ETHWARP_WOCPU1,
-+ MTK_CLK_ETHWARP_WOCPU0,
-+ MTK_CLK_TOP_USXGMII_SBUS_0_SEL,
-+ MTK_CLK_TOP_USXGMII_SBUS_1_SEL,
-+ MTK_CLK_TOP_SGM_0_SEL,
-+ MTK_CLK_TOP_SGM_1_SEL,
-+ MTK_CLK_TOP_XFI_PHY_0_XTAL_SEL,
-+ MTK_CLK_TOP_XFI_PHY_1_XTAL_SEL,
-+ MTK_CLK_TOP_ETH_GMII_SEL,
-+ MTK_CLK_TOP_ETH_REFCK_50M_SEL,
-+ MTK_CLK_TOP_ETH_SYS_200M_SEL,
-+ MTK_CLK_TOP_ETH_SYS_SEL,
-+ MTK_CLK_TOP_ETH_XGMII_SEL,
-+ MTK_CLK_TOP_ETH_MII_SEL,
-+ MTK_CLK_TOP_NETSYS_SEL,
-+ MTK_CLK_TOP_NETSYS_500M_SEL,
-+ MTK_CLK_TOP_NETSYS_PAO_2X_SEL,
-+ MTK_CLK_TOP_NETSYS_SYNC_250M_SEL,
-+ MTK_CLK_TOP_NETSYS_PPEFB_250M_SEL,
-+ MTK_CLK_TOP_NETSYS_WARP_SEL,
- MTK_CLK_MAX
- };
-
-@@ -716,6 +767,36 @@ enum mtk_clks_map {
- BIT_ULL(MTK_CLK_SGMII2_RX_250M) | \
- BIT_ULL(MTK_CLK_SGMII2_CDR_REF) | \
- BIT_ULL(MTK_CLK_SGMII2_CDR_FB))
-+#define MT7988_CLKS_BITMAP (BIT_ULL(MTK_CLK_FE) | BIT_ULL(MTK_CLK_ESW) | \
-+ BIT_ULL(MTK_CLK_GP1) | BIT_ULL(MTK_CLK_GP2) | \
-+ BIT_ULL(MTK_CLK_GP3) | BIT_ULL(MTK_CLK_XGP1) | \
-+ BIT_ULL(MTK_CLK_XGP2) | BIT_ULL(MTK_CLK_XGP3) | \
-+ BIT_ULL(MTK_CLK_CRYPTO) | \
-+ BIT_ULL(MTK_CLK_SGMII_TX_250M) | \
-+ BIT_ULL(MTK_CLK_SGMII_RX_250M) | \
-+ BIT_ULL(MTK_CLK_SGMII2_TX_250M) | \
-+ BIT_ULL(MTK_CLK_SGMII2_RX_250M) | \
-+ BIT_ULL(MTK_CLK_ETHWARP_WOCPU2) | \
-+ BIT_ULL(MTK_CLK_ETHWARP_WOCPU1) | \
-+ BIT_ULL(MTK_CLK_ETHWARP_WOCPU0) | \
-+ BIT_ULL(MTK_CLK_TOP_USXGMII_SBUS_0_SEL) | \
-+ BIT_ULL(MTK_CLK_TOP_USXGMII_SBUS_1_SEL) | \
-+ BIT_ULL(MTK_CLK_TOP_SGM_0_SEL) | \
-+ BIT_ULL(MTK_CLK_TOP_SGM_1_SEL) | \
-+ BIT_ULL(MTK_CLK_TOP_XFI_PHY_0_XTAL_SEL) | \
-+ BIT_ULL(MTK_CLK_TOP_XFI_PHY_1_XTAL_SEL) | \
-+ BIT_ULL(MTK_CLK_TOP_ETH_GMII_SEL) | \
-+ BIT_ULL(MTK_CLK_TOP_ETH_REFCK_50M_SEL) | \
-+ BIT_ULL(MTK_CLK_TOP_ETH_SYS_200M_SEL) | \
-+ BIT_ULL(MTK_CLK_TOP_ETH_SYS_SEL) | \
-+ BIT_ULL(MTK_CLK_TOP_ETH_XGMII_SEL) | \
-+ BIT_ULL(MTK_CLK_TOP_ETH_MII_SEL) | \
-+ BIT_ULL(MTK_CLK_TOP_NETSYS_SEL) | \
-+ BIT_ULL(MTK_CLK_TOP_NETSYS_500M_SEL) | \
-+ BIT_ULL(MTK_CLK_TOP_NETSYS_PAO_2X_SEL) | \
-+ BIT_ULL(MTK_CLK_TOP_NETSYS_SYNC_250M_SEL) | \
-+ BIT_ULL(MTK_CLK_TOP_NETSYS_PPEFB_250M_SEL) | \
-+ BIT_ULL(MTK_CLK_TOP_NETSYS_WARP_SEL))
-
- enum mtk_dev_state {
- MTK_HW_INIT,
-@@ -964,6 +1045,8 @@ enum mkt_eth_capabilities {
- MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \
- MTK_RSTCTRL_PPE1)
-
-+#define MT7988_CAPS (MTK_GDM1_ESW | MTK_QDMA | MTK_RSTCTRL_PPE1)
-+
- struct mtk_tx_dma_desc_info {
- dma_addr_t addr;
- u32 size;
-@@ -1309,6 +1392,7 @@ void mtk_stats_update_mac(struct mtk_mac
-
- void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg);
- u32 mtk_r32(struct mtk_eth *eth, unsigned reg);
-+u32 mtk_m32(struct mtk_eth *eth, u32 mask, u32 set, unsigned int reg);
-
- int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id);
- int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id);
+++ /dev/null
-From 38a7eb76220731eff40602cf433f24880be0a6c2 Mon Sep 17 00:00:00 2001
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Thu, 27 Jul 2023 09:02:26 +0200
-Subject: [PATCH 106/250] net: ethernet: mtk_eth_soc: enable page_pool support
- for MT7988 SoC
-
-In order to recycle pages, enable page_pool allocator for MT7988 SoC.
-
-Tested-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Link: https://lore.kernel.org/r/fd4e8693980e47385a543e7b002eec0b88bd09df.1690440675.git.lorenzo@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -1658,7 +1658,7 @@ static void mtk_update_rx_cpu_idx(struct
-
- static bool mtk_page_pool_enabled(struct mtk_eth *eth)
- {
-- return eth->soc->version == 2;
-+ return mtk_is_netsys_v2_or_greater(eth);
- }
-
- static struct page_pool *mtk_create_page_pool(struct mtk_eth *eth,
+++ /dev/null
-From 199e7d5a7f03dd377f3a7a458360dbedd71d50ba Mon Sep 17 00:00:00 2001
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Thu, 27 Jul 2023 09:07:28 +0200
-Subject: [PATCH 107/250] net: ethernet: mtk_eth_soc: enable nft hw
- flowtable_offload for MT7988 SoC
-
-Enable hw Packet Process Engine (PPE) for MT7988 SoC.
-
-Tested-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Link: https://lore.kernel.org/r/5e86341b0220a49620dadc02d77970de5ded9efc.1690441576.git.lorenzo@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 3 +++
- drivers/net/ethernet/mediatek/mtk_ppe.c | 19 +++++++++++++++----
- drivers/net/ethernet/mediatek/mtk_ppe.h | 19 ++++++++++++++++++-
- 3 files changed, 36 insertions(+), 5 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -5028,6 +5028,9 @@ static const struct mtk_soc_data mt7988_
- .required_clks = MT7988_CLKS_BITMAP,
- .required_pctl = false,
- .version = 3,
-+ .offload_version = 2,
-+ .hash_offset = 4,
-+ .foe_entry_size = MTK_FOE_ENTRY_V3_SIZE,
- .txrx = {
- .txd_size = sizeof(struct mtk_tx_dma_v2),
- .rxd_size = sizeof(struct mtk_rx_dma_v2),
---- a/drivers/net/ethernet/mediatek/mtk_ppe.c
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
-@@ -422,13 +422,22 @@ int mtk_foe_entry_set_wdma(struct mtk_et
- struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry);
- u32 *ib2 = mtk_foe_entry_ib2(eth, entry);
-
-- if (mtk_is_netsys_v2_or_greater(eth)) {
-+ switch (eth->soc->version) {
-+ case 3:
-+ *ib2 &= ~MTK_FOE_IB2_PORT_MG_V2;
-+ *ib2 |= FIELD_PREP(MTK_FOE_IB2_RX_IDX, txq) |
-+ MTK_FOE_IB2_WDMA_WINFO_V2;
-+ l2->w3info = FIELD_PREP(MTK_FOE_WINFO_WCID_V3, wcid) |
-+ FIELD_PREP(MTK_FOE_WINFO_BSS_V3, bss);
-+ break;
-+ case 2:
- *ib2 &= ~MTK_FOE_IB2_PORT_MG_V2;
- *ib2 |= FIELD_PREP(MTK_FOE_IB2_RX_IDX, txq) |
- MTK_FOE_IB2_WDMA_WINFO_V2;
- l2->winfo = FIELD_PREP(MTK_FOE_WINFO_WCID, wcid) |
- FIELD_PREP(MTK_FOE_WINFO_BSS, bss);
-- } else {
-+ break;
-+ default:
- *ib2 &= ~MTK_FOE_IB2_PORT_MG;
- *ib2 |= MTK_FOE_IB2_WDMA_WINFO;
- if (wdma_idx)
-@@ -436,6 +445,7 @@ int mtk_foe_entry_set_wdma(struct mtk_et
- 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);
-+ break;
- }
-
- return 0;
-@@ -950,8 +960,7 @@ void mtk_ppe_start(struct mtk_ppe *ppe)
- mtk_ppe_init_foe_table(ppe);
- ppe_w32(ppe, MTK_PPE_TB_BASE, ppe->foe_phys);
-
-- val = MTK_PPE_TB_CFG_ENTRY_80B |
-- MTK_PPE_TB_CFG_AGE_NON_L4 |
-+ val = MTK_PPE_TB_CFG_AGE_NON_L4 |
- MTK_PPE_TB_CFG_AGE_UNBIND |
- MTK_PPE_TB_CFG_AGE_TCP |
- MTK_PPE_TB_CFG_AGE_UDP |
-@@ -967,6 +976,8 @@ void mtk_ppe_start(struct mtk_ppe *ppe)
- MTK_PPE_ENTRIES_SHIFT);
- if (mtk_is_netsys_v2_or_greater(ppe->eth))
- val |= MTK_PPE_TB_CFG_INFO_SEL;
-+ if (!mtk_is_netsys_v3_or_greater(ppe->eth))
-+ val |= MTK_PPE_TB_CFG_ENTRY_80B;
- ppe_w32(ppe, MTK_PPE_TB_CFG, val);
-
- ppe_w32(ppe, MTK_PPE_IP_PROTO_CHK,
---- a/drivers/net/ethernet/mediatek/mtk_ppe.h
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
-@@ -85,6 +85,17 @@ enum {
- #define MTK_FOE_WINFO_BSS GENMASK(5, 0)
- #define MTK_FOE_WINFO_WCID GENMASK(15, 6)
-
-+#define MTK_FOE_WINFO_BSS_V3 GENMASK(23, 16)
-+#define MTK_FOE_WINFO_WCID_V3 GENMASK(15, 0)
-+
-+#define MTK_FOE_WINFO_PAO_USR_INFO GENMASK(15, 0)
-+#define MTK_FOE_WINFO_PAO_TID GENMASK(19, 16)
-+#define MTK_FOE_WINFO_PAO_IS_FIXEDRATE BIT(20)
-+#define MTK_FOE_WINFO_PAO_IS_PRIOR BIT(21)
-+#define MTK_FOE_WINFO_PAO_IS_SP BIT(22)
-+#define MTK_FOE_WINFO_PAO_HF BIT(23)
-+#define MTK_FOE_WINFO_PAO_AMSDU_EN BIT(24)
-+
- enum {
- MTK_FOE_STATE_INVALID,
- MTK_FOE_STATE_UNBIND,
-@@ -106,8 +117,13 @@ struct mtk_foe_mac_info {
- u16 pppoe_id;
- u16 src_mac_lo;
-
-+ /* netsys_v2 */
- u16 minfo;
- u16 winfo;
-+
-+ /* netsys_v3 */
-+ u32 w3info;
-+ u32 wpao;
- };
-
- /* software-only entry type */
-@@ -218,6 +234,7 @@ struct mtk_foe_ipv6_6rd {
-
- #define MTK_FOE_ENTRY_V1_SIZE 80
- #define MTK_FOE_ENTRY_V2_SIZE 96
-+#define MTK_FOE_ENTRY_V3_SIZE 128
-
- struct mtk_foe_entry {
- u32 ib1;
-@@ -228,7 +245,7 @@ struct mtk_foe_entry {
- struct mtk_foe_ipv4_dslite dslite;
- struct mtk_foe_ipv6 ipv6;
- struct mtk_foe_ipv6_6rd ipv6_6rd;
-- u32 data[23];
-+ u32 data[31];
- };
- };
-
+++ /dev/null
-From 0c024632c1e7ff69914329bfd87bec749b9c0aed Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Wed, 2 Aug 2023 04:31:09 +0100
-Subject: [PATCH 108/250] net: ethernet: mtk_eth_soc: support per-flow
- accounting on MT7988
-
-NETSYS_V3 uses 64 bits for each counters while older SoCs are using
-48/40 bits for each counter.
-Support reading per-flow byte and package counters on NETSYS_V3.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Link: https://lore.kernel.org/r/37a0928fa8c1253b197884c68ce1f54239421ac5.1690946442.git.daniel@makrotopia.org
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 1 +
- drivers/net/ethernet/mediatek/mtk_ppe.c | 21 +++++++++++++-------
- drivers/net/ethernet/mediatek/mtk_ppe_regs.h | 2 ++
- 3 files changed, 17 insertions(+), 7 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -5030,6 +5030,7 @@ static const struct mtk_soc_data mt7988_
- .version = 3,
- .offload_version = 2,
- .hash_offset = 4,
-+ .has_accounting = true,
- .foe_entry_size = MTK_FOE_ENTRY_V3_SIZE,
- .txrx = {
- .txd_size = sizeof(struct mtk_tx_dma_v2),
---- a/drivers/net/ethernet/mediatek/mtk_ppe.c
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
-@@ -91,7 +91,6 @@ static int mtk_ppe_mib_wait_busy(struct
-
- static int mtk_mib_entry_read(struct mtk_ppe *ppe, u16 index, u64 *bytes, u64 *packets)
- {
-- u32 byte_cnt_low, byte_cnt_high, pkt_cnt_low, pkt_cnt_high;
- u32 val, cnt_r0, cnt_r1, cnt_r2;
- int ret;
-
-@@ -106,12 +105,20 @@ static int mtk_mib_entry_read(struct mtk
- cnt_r1 = readl(ppe->base + MTK_PPE_MIB_SER_R1);
- cnt_r2 = readl(ppe->base + MTK_PPE_MIB_SER_R2);
-
-- byte_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R0_BYTE_CNT_LOW, cnt_r0);
-- byte_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R1_BYTE_CNT_HIGH, cnt_r1);
-- pkt_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R1_PKT_CNT_LOW, cnt_r1);
-- pkt_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R2_PKT_CNT_HIGH, cnt_r2);
-- *bytes = ((u64)byte_cnt_high << 32) | byte_cnt_low;
-- *packets = (pkt_cnt_high << 16) | pkt_cnt_low;
-+ if (mtk_is_netsys_v3_or_greater(ppe->eth)) {
-+ /* 64 bit for each counter */
-+ u32 cnt_r3 = readl(ppe->base + MTK_PPE_MIB_SER_R3);
-+ *bytes = ((u64)cnt_r1 << 32) | cnt_r0;
-+ *packets = ((u64)cnt_r3 << 32) | cnt_r2;
-+ } else {
-+ /* 48 bit byte counter, 40 bit packet counter */
-+ u32 byte_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R0_BYTE_CNT_LOW, cnt_r0);
-+ u32 byte_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R1_BYTE_CNT_HIGH, cnt_r1);
-+ u32 pkt_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R1_PKT_CNT_LOW, cnt_r1);
-+ u32 pkt_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R2_PKT_CNT_HIGH, cnt_r2);
-+ *bytes = ((u64)byte_cnt_high << 32) | byte_cnt_low;
-+ *packets = (pkt_cnt_high << 16) | pkt_cnt_low;
-+ }
-
- return 0;
- }
---- a/drivers/net/ethernet/mediatek/mtk_ppe_regs.h
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe_regs.h
-@@ -163,6 +163,8 @@ enum {
- #define MTK_PPE_MIB_SER_R2 0x348
- #define MTK_PPE_MIB_SER_R2_PKT_CNT_HIGH GENMASK(23, 0)
-
-+#define MTK_PPE_MIB_SER_R3 0x34c
-+
- #define MTK_PPE_MIB_CACHE_CTL 0x350
- #define MTK_PPE_MIB_CACHE_CTL_EN BIT(0)
- #define MTK_PPE_MIB_CACHE_CTL_FLUSH BIT(2)
+++ /dev/null
-From 3b12f42772c26869d60398c1710aa27b27cd945c Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Mon, 21 Aug 2023 17:12:44 +0100
-Subject: [PATCH 109/250] net: ethernet: mtk_eth_soc: fix NULL pointer on hw
- reset
-
-When a hardware reset is triggered on devices not initializing WED the
-calls to mtk_wed_fe_reset and mtk_wed_fe_reset_complete dereference a
-pointer on uninitialized stack memory.
-Break out of both functions in case a hw_list entry is 0.
-
-Fixes: 08a764a7c51b ("net: ethernet: mtk_wed: add reset/reset_complete callbacks")
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Link: https://lore.kernel.org/r/5465c1609b464cc7407ae1530c40821dcdf9d3e6.1692634266.git.daniel@makrotopia.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_wed.c | 12 ++++++++++--
- 1 file changed, 10 insertions(+), 2 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -214,9 +214,13 @@ void mtk_wed_fe_reset(void)
-
- for (i = 0; i < ARRAY_SIZE(hw_list); i++) {
- struct mtk_wed_hw *hw = hw_list[i];
-- struct mtk_wed_device *dev = hw->wed_dev;
-+ struct mtk_wed_device *dev;
- int err;
-
-+ if (!hw)
-+ break;
-+
-+ dev = hw->wed_dev;
- if (!dev || !dev->wlan.reset)
- continue;
-
-@@ -237,8 +241,12 @@ void mtk_wed_fe_reset_complete(void)
-
- for (i = 0; i < ARRAY_SIZE(hw_list); i++) {
- struct mtk_wed_hw *hw = hw_list[i];
-- struct mtk_wed_device *dev = hw->wed_dev;
-+ struct mtk_wed_device *dev;
-+
-+ if (!hw)
-+ break;
-
-+ dev = hw->wed_dev;
- if (!dev || !dev->wlan.reset_complete)
- continue;
-
+++ /dev/null
-From 489aea123d74a846ce746bfdb3efe1e7ad512e0d Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Tue, 22 Aug 2023 17:31:24 +0100
-Subject: [PATCH 110/250] net: ethernet: mtk_eth_soc: fix register definitions
- for MT7988
-
-More register macros need to be adjusted for the 3rd GMAC on MT7988.
-Account for added bit in SYSCFG0_SGMII_MASK.
-
-Fixes: 445eb6448ed3 ("net: ethernet: mtk_eth_soc: add basic support for MT7988 SoC")
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Simon Horman <horms@kernel.org>
-Link: https://lore.kernel.org/r/1c8da012e2ca80939906d85f314138c552139f0f.1692721443.git.daniel@makrotopia.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 8 +++++---
- 1 file changed, 5 insertions(+), 3 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -133,10 +133,12 @@
- #define MTK_GDMA_XGDM_SEL BIT(31)
-
- /* Unicast Filter MAC Address Register - Low */
--#define MTK_GDMA_MAC_ADRL(x) (0x508 + (x * 0x1000))
-+#define MTK_GDMA_MAC_ADRL(x) ({ typeof(x) _x = (x); (_x == MTK_GMAC3_ID) ? \
-+ 0x548 : 0x508 + (_x * 0x1000); })
-
- /* Unicast Filter MAC Address Register - High */
--#define MTK_GDMA_MAC_ADRH(x) (0x50C + (x * 0x1000))
-+#define MTK_GDMA_MAC_ADRH(x) ({ typeof(x) _x = (x); (_x == MTK_GMAC3_ID) ? \
-+ 0x54C : 0x50C + (_x * 0x1000); })
-
- /* FE global misc reg*/
- #define MTK_FE_GLO_MISC 0x124
-@@ -503,7 +505,7 @@
- #define ETHSYS_SYSCFG0 0x14
- #define SYSCFG0_GE_MASK 0x3
- #define SYSCFG0_GE_MODE(x, y) (x << (12 + (y * 2)))
--#define SYSCFG0_SGMII_MASK GENMASK(9, 8)
-+#define SYSCFG0_SGMII_MASK GENMASK(9, 7)
- #define SYSCFG0_SGMII_GMAC1 ((2 << 8) & SYSCFG0_SGMII_MASK)
- #define SYSCFG0_SGMII_GMAC2 ((3 << 8) & SYSCFG0_SGMII_MASK)
- #define SYSCFG0_SGMII_GMAC1_V2 BIT(9)
+++ /dev/null
-From 15a84d1c44ae8c1451c265ee60500588a24e8cd6 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Tue, 22 Aug 2023 17:32:03 +0100
-Subject: [PATCH 111/250] net: ethernet: mtk_eth_soc: add reset bits for MT7988
-
-Add bits needed to reset the frame engine on MT7988.
-
-Fixes: 445eb6448ed3 ("net: ethernet: mtk_eth_soc: add basic support for MT7988 SoC")
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Link: https://lore.kernel.org/r/89b6c38380e7a3800c1362aa7575600717bc7543.1692721443.git.daniel@makrotopia.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 76 +++++++++++++++------
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 16 +++--
- 2 files changed, 68 insertions(+), 24 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -3594,19 +3594,34 @@ static void mtk_hw_reset(struct mtk_eth
- {
- u32 val;
-
-- if (mtk_is_netsys_v2_or_greater(eth)) {
-+ if (mtk_is_netsys_v2_or_greater(eth))
- regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN, 0);
-+
-+ if (mtk_is_netsys_v3_or_greater(eth)) {
-+ val = RSTCTRL_PPE0_V3;
-+
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
-+ val |= RSTCTRL_PPE1_V3;
-+
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE2))
-+ val |= RSTCTRL_PPE2;
-+
-+ val |= RSTCTRL_WDMA0 | RSTCTRL_WDMA1 | RSTCTRL_WDMA2;
-+ } else if (mtk_is_netsys_v2_or_greater(eth)) {
- val = RSTCTRL_PPE0_V2;
-+
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
-+ val |= RSTCTRL_PPE1;
- } else {
- val = RSTCTRL_PPE0;
- }
-
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
-- val |= RSTCTRL_PPE1;
--
- ethsys_reset(eth, RSTCTRL_ETH | RSTCTRL_FE | val);
-
-- if (mtk_is_netsys_v2_or_greater(eth))
-+ if (mtk_is_netsys_v3_or_greater(eth))
-+ regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN,
-+ 0x6f8ff);
-+ else if (mtk_is_netsys_v2_or_greater(eth))
- regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN,
- 0x3ffffff);
- }
-@@ -3632,13 +3647,21 @@ static void mtk_hw_warm_reset(struct mtk
- return;
- }
-
-- if (mtk_is_netsys_v2_or_greater(eth))
-+ if (mtk_is_netsys_v3_or_greater(eth)) {
-+ rst_mask = RSTCTRL_ETH | RSTCTRL_PPE0_V3;
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
-+ rst_mask |= RSTCTRL_PPE1_V3;
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE2))
-+ rst_mask |= RSTCTRL_PPE2;
-+
-+ rst_mask |= RSTCTRL_WDMA0 | RSTCTRL_WDMA1 | RSTCTRL_WDMA2;
-+ } else if (mtk_is_netsys_v2_or_greater(eth)) {
- rst_mask = RSTCTRL_ETH | RSTCTRL_PPE0_V2;
-- else
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
-+ rst_mask |= RSTCTRL_PPE1;
-+ } else {
- rst_mask = RSTCTRL_ETH | RSTCTRL_PPE0;
--
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
-- rst_mask |= RSTCTRL_PPE1;
-+ }
-
- regmap_update_bits(eth->ethsys, ETHSYS_RSTCTRL, rst_mask, rst_mask);
-
-@@ -3990,11 +4013,17 @@ static void mtk_prepare_for_reset(struct
- u32 val;
- int i;
-
-- /* disabe FE P3 and P4 */
-- val = mtk_r32(eth, MTK_FE_GLO_CFG) | MTK_FE_LINK_DOWN_P3;
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
-- val |= MTK_FE_LINK_DOWN_P4;
-- mtk_w32(eth, val, MTK_FE_GLO_CFG);
-+ /* set FE PPE ports link down */
-+ for (i = MTK_GMAC1_ID;
-+ i <= (mtk_is_netsys_v3_or_greater(eth) ? MTK_GMAC3_ID : MTK_GMAC2_ID);
-+ i += 2) {
-+ val = mtk_r32(eth, MTK_FE_GLO_CFG(i)) | MTK_FE_LINK_DOWN_P(PSE_PPE0_PORT);
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
-+ val |= MTK_FE_LINK_DOWN_P(PSE_PPE1_PORT);
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE2))
-+ val |= MTK_FE_LINK_DOWN_P(PSE_PPE2_PORT);
-+ mtk_w32(eth, val, MTK_FE_GLO_CFG(i));
-+ }
-
- /* adjust PPE configurations to prepare for reset */
- for (i = 0; i < ARRAY_SIZE(eth->ppe); i++)
-@@ -4055,11 +4084,18 @@ static void mtk_pending_work(struct work
- }
- }
-
-- /* enabe FE P3 and P4 */
-- val = mtk_r32(eth, MTK_FE_GLO_CFG) & ~MTK_FE_LINK_DOWN_P3;
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
-- val &= ~MTK_FE_LINK_DOWN_P4;
-- mtk_w32(eth, val, MTK_FE_GLO_CFG);
-+ /* set FE PPE ports link up */
-+ for (i = MTK_GMAC1_ID;
-+ i <= (mtk_is_netsys_v3_or_greater(eth) ? MTK_GMAC3_ID : MTK_GMAC2_ID);
-+ i += 2) {
-+ val = mtk_r32(eth, MTK_FE_GLO_CFG(i)) & ~MTK_FE_LINK_DOWN_P(PSE_PPE0_PORT);
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
-+ val &= ~MTK_FE_LINK_DOWN_P(PSE_PPE1_PORT);
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE2))
-+ val &= ~MTK_FE_LINK_DOWN_P(PSE_PPE2_PORT);
-+
-+ mtk_w32(eth, val, MTK_FE_GLO_CFG(i));
-+ }
-
- clear_bit(MTK_RESETTING, ð->state);
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -76,9 +76,8 @@
- #define MTK_HW_LRO_SDL_REMAIN_ROOM 1522
-
- /* Frame Engine Global Configuration */
--#define MTK_FE_GLO_CFG 0x00
--#define MTK_FE_LINK_DOWN_P3 BIT(11)
--#define MTK_FE_LINK_DOWN_P4 BIT(12)
-+#define MTK_FE_GLO_CFG(x) (((x) == MTK_GMAC3_ID) ? 0x24 : 0x00)
-+#define MTK_FE_LINK_DOWN_P(x) BIT(((x) + 8) % 16)
-
- /* Frame Engine Global Reset Register */
- #define MTK_RST_GL 0x04
-@@ -522,9 +521,15 @@
- /* ethernet reset control register */
- #define ETHSYS_RSTCTRL 0x34
- #define RSTCTRL_FE BIT(6)
-+#define RSTCTRL_WDMA0 BIT(24)
-+#define RSTCTRL_WDMA1 BIT(25)
-+#define RSTCTRL_WDMA2 BIT(26)
- #define RSTCTRL_PPE0 BIT(31)
- #define RSTCTRL_PPE0_V2 BIT(30)
- #define RSTCTRL_PPE1 BIT(31)
-+#define RSTCTRL_PPE0_V3 BIT(29)
-+#define RSTCTRL_PPE1_V3 BIT(30)
-+#define RSTCTRL_PPE2 BIT(31)
- #define RSTCTRL_ETH BIT(23)
-
- /* ethernet reset check idle register */
-@@ -931,6 +936,7 @@ enum mkt_eth_capabilities {
- MTK_QDMA_BIT,
- MTK_SOC_MT7628_BIT,
- MTK_RSTCTRL_PPE1_BIT,
-+ MTK_RSTCTRL_PPE2_BIT,
- MTK_U3_COPHY_V2_BIT,
-
- /* MUX BITS*/
-@@ -965,6 +971,7 @@ enum mkt_eth_capabilities {
- #define MTK_QDMA BIT_ULL(MTK_QDMA_BIT)
- #define MTK_SOC_MT7628 BIT_ULL(MTK_SOC_MT7628_BIT)
- #define MTK_RSTCTRL_PPE1 BIT_ULL(MTK_RSTCTRL_PPE1_BIT)
-+#define MTK_RSTCTRL_PPE2 BIT_ULL(MTK_RSTCTRL_PPE2_BIT)
- #define MTK_U3_COPHY_V2 BIT_ULL(MTK_U3_COPHY_V2_BIT)
-
- #define MTK_ETH_MUX_GDM1_TO_GMAC1_ESW \
-@@ -1047,7 +1054,8 @@ enum mkt_eth_capabilities {
- MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \
- MTK_RSTCTRL_PPE1)
-
--#define MT7988_CAPS (MTK_GDM1_ESW | MTK_QDMA | MTK_RSTCTRL_PPE1)
-+#define MT7988_CAPS (MTK_GDM1_ESW | MTK_QDMA | MTK_RSTCTRL_PPE1 | \
-+ MTK_RSTCTRL_PPE2)
-
- struct mtk_tx_dma_desc_info {
- dma_addr_t addr;
+++ /dev/null
-From 25ce45fe40b574e5d7ffa407f7f2db03e7d5a910 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Tue, 22 Aug 2023 17:32:54 +0100
-Subject: [PATCH 112/250] net: ethernet: mtk_eth_soc: add support for in-SoC
- SRAM
-
-MT7981, MT7986 and MT7988 come with in-SoC SRAM dedicated for Ethernet
-DMA rings. Support using the SRAM without breaking existing device tree
-bindings, ie. only new SoC starting from MT7988 will have the SRAM
-declared as additional resource in device tree. For MT7981 and MT7986
-an offset on top of the main I/O base is used.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Link: https://lore.kernel.org/r/e45e0f230c63ad58869e8fe35b95a2fb8925b625.1692721443.git.daniel@makrotopia.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 88 ++++++++++++++++-----
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 12 ++-
- 2 files changed, 78 insertions(+), 22 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -1118,10 +1118,13 @@ static int mtk_init_fq_dma(struct mtk_et
- dma_addr_t dma_addr;
- int i;
-
-- eth->scratch_ring = dma_alloc_coherent(eth->dma_dev,
-- cnt * soc->txrx.txd_size,
-- ð->phy_scratch_ring,
-- GFP_KERNEL);
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SRAM))
-+ eth->scratch_ring = eth->sram_base;
-+ else
-+ eth->scratch_ring = dma_alloc_coherent(eth->dma_dev,
-+ cnt * soc->txrx.txd_size,
-+ ð->phy_scratch_ring,
-+ GFP_KERNEL);
- if (unlikely(!eth->scratch_ring))
- return -ENOMEM;
-
-@@ -2429,8 +2432,14 @@ static int mtk_tx_alloc(struct mtk_eth *
- if (!ring->buf)
- goto no_tx_mem;
-
-- ring->dma = dma_alloc_coherent(eth->dma_dev, ring_size * sz,
-- &ring->phys, GFP_KERNEL);
-+ if (MTK_HAS_CAPS(soc->caps, MTK_SRAM)) {
-+ ring->dma = eth->sram_base + ring_size * sz;
-+ ring->phys = eth->phy_scratch_ring + ring_size * (dma_addr_t)sz;
-+ } else {
-+ ring->dma = dma_alloc_coherent(eth->dma_dev, ring_size * sz,
-+ &ring->phys, GFP_KERNEL);
-+ }
-+
- if (!ring->dma)
- goto no_tx_mem;
-
-@@ -2529,8 +2538,7 @@ static void mtk_tx_clean(struct mtk_eth
- kfree(ring->buf);
- ring->buf = NULL;
- }
--
-- if (ring->dma) {
-+ if (!MTK_HAS_CAPS(soc->caps, MTK_SRAM) && ring->dma) {
- dma_free_coherent(eth->dma_dev,
- ring->dma_size * soc->txrx.txd_size,
- ring->dma, ring->phys);
-@@ -2549,9 +2557,14 @@ static int mtk_rx_alloc(struct mtk_eth *
- {
- const struct mtk_reg_map *reg_map = eth->soc->reg_map;
- struct mtk_rx_ring *ring;
-- int rx_data_len, rx_dma_size;
-+ int rx_data_len, rx_dma_size, tx_ring_size;
- int i;
-
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
-+ tx_ring_size = MTK_QDMA_RING_SIZE;
-+ else
-+ tx_ring_size = MTK_DMA_SIZE;
-+
- if (rx_flag == MTK_RX_FLAGS_QDMA) {
- if (ring_no)
- return -EINVAL;
-@@ -2586,9 +2599,20 @@ static int mtk_rx_alloc(struct mtk_eth *
- ring->page_pool = pp;
- }
-
-- ring->dma = dma_alloc_coherent(eth->dma_dev,
-- rx_dma_size * eth->soc->txrx.rxd_size,
-- &ring->phys, GFP_KERNEL);
-+ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SRAM) ||
-+ rx_flag != MTK_RX_FLAGS_NORMAL) {
-+ ring->dma = dma_alloc_coherent(eth->dma_dev,
-+ rx_dma_size * eth->soc->txrx.rxd_size,
-+ &ring->phys, GFP_KERNEL);
-+ } else {
-+ struct mtk_tx_ring *tx_ring = ð->tx_ring;
-+
-+ ring->dma = tx_ring->dma + tx_ring_size *
-+ eth->soc->txrx.txd_size * (ring_no + 1);
-+ ring->phys = tx_ring->phys + tx_ring_size *
-+ eth->soc->txrx.txd_size * (ring_no + 1);
-+ }
-+
- if (!ring->dma)
- return -ENOMEM;
-
-@@ -2673,7 +2697,7 @@ static int mtk_rx_alloc(struct mtk_eth *
- return 0;
- }
-
--static void mtk_rx_clean(struct mtk_eth *eth, struct mtk_rx_ring *ring)
-+static void mtk_rx_clean(struct mtk_eth *eth, struct mtk_rx_ring *ring, bool in_sram)
- {
- int i;
-
-@@ -2696,7 +2720,7 @@ static void mtk_rx_clean(struct mtk_eth
- ring->data = NULL;
- }
-
-- if (ring->dma) {
-+ if (!in_sram && ring->dma) {
- dma_free_coherent(eth->dma_dev,
- ring->dma_size * eth->soc->txrx.rxd_size,
- ring->dma, ring->phys);
-@@ -3059,7 +3083,7 @@ static void mtk_dma_free(struct mtk_eth
- for (i = 0; i < MTK_MAX_DEVS; i++)
- if (eth->netdev[i])
- netdev_reset_queue(eth->netdev[i]);
-- if (eth->scratch_ring) {
-+ if (!MTK_HAS_CAPS(soc->caps, MTK_SRAM) && eth->scratch_ring) {
- dma_free_coherent(eth->dma_dev,
- MTK_QDMA_RING_SIZE * soc->txrx.txd_size,
- eth->scratch_ring, eth->phy_scratch_ring);
-@@ -3067,13 +3091,13 @@ static void mtk_dma_free(struct mtk_eth
- eth->phy_scratch_ring = 0;
- }
- mtk_tx_clean(eth);
-- mtk_rx_clean(eth, ð->rx_ring[0]);
-- mtk_rx_clean(eth, ð->rx_ring_qdma);
-+ mtk_rx_clean(eth, ð->rx_ring[0], MTK_HAS_CAPS(soc->caps, MTK_SRAM));
-+ mtk_rx_clean(eth, ð->rx_ring_qdma, false);
-
- if (eth->hwlro) {
- mtk_hwlro_rx_uninit(eth);
- for (i = 1; i < MTK_MAX_RX_RING_NUM; i++)
-- mtk_rx_clean(eth, ð->rx_ring[i]);
-+ mtk_rx_clean(eth, ð->rx_ring[i], false);
- }
-
- kfree(eth->scratch_head);
-@@ -4641,7 +4665,7 @@ static int mtk_sgmii_init(struct mtk_eth
-
- static int mtk_probe(struct platform_device *pdev)
- {
-- struct resource *res = NULL;
-+ struct resource *res = NULL, *res_sram;
- struct device_node *mac_np;
- struct mtk_eth *eth;
- int err, i;
-@@ -4661,6 +4685,20 @@ static int mtk_probe(struct platform_dev
- if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
- eth->ip_align = NET_IP_ALIGN;
-
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SRAM)) {
-+ /* SRAM is actual memory and supports transparent access just like DRAM.
-+ * Hence we don't require __iomem being set and don't need to use accessor
-+ * functions to read from or write to SRAM.
-+ */
-+ if (mtk_is_netsys_v3_or_greater(eth)) {
-+ eth->sram_base = (void __force *)devm_platform_ioremap_resource(pdev, 1);
-+ if (IS_ERR(eth->sram_base))
-+ return PTR_ERR(eth->sram_base);
-+ } else {
-+ eth->sram_base = (void __force *)eth->base + MTK_ETH_SRAM_OFFSET;
-+ }
-+ }
-+
- spin_lock_init(ð->page_lock);
- spin_lock_init(ð->tx_irq_lock);
- spin_lock_init(ð->rx_irq_lock);
-@@ -4724,6 +4762,18 @@ static int mtk_probe(struct platform_dev
- err = -EINVAL;
- goto err_destroy_sgmii;
- }
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SRAM)) {
-+ if (mtk_is_netsys_v3_or_greater(eth)) {
-+ res_sram = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-+ if (!res_sram) {
-+ err = -EINVAL;
-+ goto err_destroy_sgmii;
-+ }
-+ eth->phy_scratch_ring = res_sram->start;
-+ } else {
-+ eth->phy_scratch_ring = res->start + MTK_ETH_SRAM_OFFSET;
-+ }
-+ }
- }
-
- if (eth->soc->offload_version) {
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -139,6 +139,9 @@
- #define MTK_GDMA_MAC_ADRH(x) ({ typeof(x) _x = (x); (_x == MTK_GMAC3_ID) ? \
- 0x54C : 0x50C + (_x * 0x1000); })
-
-+/* Internal SRAM offset */
-+#define MTK_ETH_SRAM_OFFSET 0x40000
-+
- /* FE global misc reg*/
- #define MTK_FE_GLO_MISC 0x124
-
-@@ -938,6 +941,7 @@ enum mkt_eth_capabilities {
- MTK_RSTCTRL_PPE1_BIT,
- MTK_RSTCTRL_PPE2_BIT,
- MTK_U3_COPHY_V2_BIT,
-+ MTK_SRAM_BIT,
-
- /* MUX BITS*/
- MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT,
-@@ -973,6 +977,7 @@ enum mkt_eth_capabilities {
- #define MTK_RSTCTRL_PPE1 BIT_ULL(MTK_RSTCTRL_PPE1_BIT)
- #define MTK_RSTCTRL_PPE2 BIT_ULL(MTK_RSTCTRL_PPE2_BIT)
- #define MTK_U3_COPHY_V2 BIT_ULL(MTK_U3_COPHY_V2_BIT)
-+#define MTK_SRAM BIT_ULL(MTK_SRAM_BIT)
-
- #define MTK_ETH_MUX_GDM1_TO_GMAC1_ESW \
- BIT_ULL(MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT)
-@@ -1048,14 +1053,14 @@ enum mkt_eth_capabilities {
- #define MT7981_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | MTK_GMAC2_GEPHY | \
- MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \
- MTK_MUX_U3_GMAC2_TO_QPHY | MTK_U3_COPHY_V2 | \
-- MTK_RSTCTRL_PPE1)
-+ MTK_RSTCTRL_PPE1 | MTK_SRAM)
-
- #define MT7986_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | \
- MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \
-- MTK_RSTCTRL_PPE1)
-+ MTK_RSTCTRL_PPE1 | MTK_SRAM)
-
- #define MT7988_CAPS (MTK_GDM1_ESW | MTK_QDMA | MTK_RSTCTRL_PPE1 | \
-- MTK_RSTCTRL_PPE2)
-+ MTK_RSTCTRL_PPE2 | MTK_SRAM)
-
- struct mtk_tx_dma_desc_info {
- dma_addr_t addr;
-@@ -1215,6 +1220,7 @@ struct mtk_eth {
- struct device *dev;
- struct device *dma_dev;
- void __iomem *base;
-+ void *sram_base;
- spinlock_t page_lock;
- spinlock_t tx_irq_lock;
- spinlock_t rx_irq_lock;
+++ /dev/null
-From 0b0d606eb9650fa01dd5621e072aa29a10544399 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Tue, 22 Aug 2023 17:33:12 +0100
-Subject: [PATCH 113/250] net: ethernet: mtk_eth_soc: support 36-bit DMA
- addressing on MT7988
-
-Systems having 4 GiB of RAM and more require DMA addressing beyond the
-current 32-bit limit. Starting from MT7988 the hardware now supports
-36-bit DMA addressing, let's use that new capability in the driver to
-avoid running into swiotlb on systems with 4 GiB of RAM or more.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Link: https://lore.kernel.org/r/95b919c98876c9e49761e44662e7c937479eecb8.1692721443.git.daniel@makrotopia.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 30 +++++++++++++++++++--
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 22 +++++++++++++--
- 2 files changed, 48 insertions(+), 4 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -1311,6 +1311,10 @@ static void mtk_tx_set_dma_desc_v2(struc
- data = TX_DMA_PLEN0(info->size);
- if (info->last)
- data |= TX_DMA_LS0;
-+
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_36BIT_DMA))
-+ data |= TX_DMA_PREP_ADDR64(info->addr);
-+
- WRITE_ONCE(desc->txd3, data);
-
- /* set forward port */
-@@ -1980,6 +1984,7 @@ static int mtk_poll_rx(struct napi_struc
- bool xdp_flush = false;
- int idx;
- struct sk_buff *skb;
-+ u64 addr64 = 0;
- u8 *data, *new_data;
- struct mtk_rx_dma_v2 *rxd, trxd;
- int done = 0, bytes = 0;
-@@ -2095,7 +2100,10 @@ static int mtk_poll_rx(struct napi_struc
- goto release_desc;
- }
-
-- dma_unmap_single(eth->dma_dev, trxd.rxd1,
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_36BIT_DMA))
-+ addr64 = RX_DMA_GET_ADDR64(trxd.rxd2);
-+
-+ dma_unmap_single(eth->dma_dev, ((u64)trxd.rxd1 | addr64),
- ring->buf_size, DMA_FROM_DEVICE);
-
- skb = build_skb(data, ring->frag_size);
-@@ -2161,6 +2169,9 @@ release_desc:
- else
- rxd->rxd2 = RX_DMA_PREP_PLEN0(ring->buf_size);
-
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_36BIT_DMA))
-+ rxd->rxd2 |= RX_DMA_PREP_ADDR64(dma_addr);
-+
- ring->calc_idx = idx;
- done++;
- }
-@@ -2653,6 +2664,9 @@ static int mtk_rx_alloc(struct mtk_eth *
- else
- rxd->rxd2 = RX_DMA_PREP_PLEN0(ring->buf_size);
-
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_36BIT_DMA))
-+ rxd->rxd2 |= RX_DMA_PREP_ADDR64(dma_addr);
-+
- rxd->rxd3 = 0;
- rxd->rxd4 = 0;
- if (mtk_is_netsys_v2_or_greater(eth)) {
-@@ -2699,6 +2713,7 @@ static int mtk_rx_alloc(struct mtk_eth *
-
- static void mtk_rx_clean(struct mtk_eth *eth, struct mtk_rx_ring *ring, bool in_sram)
- {
-+ u64 addr64 = 0;
- int i;
-
- if (ring->data && ring->dma) {
-@@ -2712,7 +2727,10 @@ static void mtk_rx_clean(struct mtk_eth
- if (!rxd->rxd1)
- continue;
-
-- dma_unmap_single(eth->dma_dev, rxd->rxd1,
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_36BIT_DMA))
-+ addr64 = RX_DMA_GET_ADDR64(rxd->rxd2);
-+
-+ dma_unmap_single(eth->dma_dev, ((u64)rxd->rxd1 | addr64),
- ring->buf_size, DMA_FROM_DEVICE);
- mtk_rx_put_buff(ring, ring->data[i], false);
- }
-@@ -4699,6 +4717,14 @@ static int mtk_probe(struct platform_dev
- }
- }
-
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_36BIT_DMA)) {
-+ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(36));
-+ if (err) {
-+ dev_err(&pdev->dev, "Wrong DMA config\n");
-+ return -EINVAL;
-+ }
-+ }
-+
- spin_lock_init(ð->page_lock);
- spin_lock_init(ð->tx_irq_lock);
- spin_lock_init(ð->rx_irq_lock);
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -331,6 +331,14 @@
- #define TX_DMA_PLEN1(x) ((x) & eth->soc->txrx.dma_max_len)
- #define TX_DMA_SWC BIT(14)
- #define TX_DMA_PQID GENMASK(3, 0)
-+#define TX_DMA_ADDR64_MASK GENMASK(3, 0)
-+#if IS_ENABLED(CONFIG_64BIT)
-+# define TX_DMA_GET_ADDR64(x) (((u64)FIELD_GET(TX_DMA_ADDR64_MASK, (x))) << 32)
-+# define TX_DMA_PREP_ADDR64(x) FIELD_PREP(TX_DMA_ADDR64_MASK, ((x) >> 32))
-+#else
-+# define TX_DMA_GET_ADDR64(x) (0)
-+# define TX_DMA_PREP_ADDR64(x) (0)
-+#endif
-
- /* PDMA on MT7628 */
- #define TX_DMA_DONE BIT(31)
-@@ -343,6 +351,14 @@
- #define RX_DMA_PREP_PLEN0(x) (((x) & eth->soc->txrx.dma_max_len) << eth->soc->txrx.dma_len_offset)
- #define RX_DMA_GET_PLEN0(x) (((x) >> eth->soc->txrx.dma_len_offset) & eth->soc->txrx.dma_max_len)
- #define RX_DMA_VTAG BIT(15)
-+#define RX_DMA_ADDR64_MASK GENMASK(3, 0)
-+#if IS_ENABLED(CONFIG_64BIT)
-+# define RX_DMA_GET_ADDR64(x) (((u64)FIELD_GET(RX_DMA_ADDR64_MASK, (x))) << 32)
-+# define RX_DMA_PREP_ADDR64(x) FIELD_PREP(RX_DMA_ADDR64_MASK, ((x) >> 32))
-+#else
-+# define RX_DMA_GET_ADDR64(x) (0)
-+# define RX_DMA_PREP_ADDR64(x) (0)
-+#endif
-
- /* QDMA descriptor rxd3 */
- #define RX_DMA_VID(x) ((x) & VLAN_VID_MASK)
-@@ -942,6 +958,7 @@ enum mkt_eth_capabilities {
- MTK_RSTCTRL_PPE2_BIT,
- MTK_U3_COPHY_V2_BIT,
- MTK_SRAM_BIT,
-+ MTK_36BIT_DMA_BIT,
-
- /* MUX BITS*/
- MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT,
-@@ -978,6 +995,7 @@ enum mkt_eth_capabilities {
- #define MTK_RSTCTRL_PPE2 BIT_ULL(MTK_RSTCTRL_PPE2_BIT)
- #define MTK_U3_COPHY_V2 BIT_ULL(MTK_U3_COPHY_V2_BIT)
- #define MTK_SRAM BIT_ULL(MTK_SRAM_BIT)
-+#define MTK_36BIT_DMA BIT_ULL(MTK_36BIT_DMA_BIT)
-
- #define MTK_ETH_MUX_GDM1_TO_GMAC1_ESW \
- BIT_ULL(MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT)
-@@ -1059,8 +1077,8 @@ enum mkt_eth_capabilities {
- MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \
- MTK_RSTCTRL_PPE1 | MTK_SRAM)
-
--#define MT7988_CAPS (MTK_GDM1_ESW | MTK_QDMA | MTK_RSTCTRL_PPE1 | \
-- MTK_RSTCTRL_PPE2 | MTK_SRAM)
-+#define MT7988_CAPS (MTK_36BIT_DMA | MTK_GDM1_ESW | MTK_QDMA | \
-+ MTK_RSTCTRL_PPE1 | MTK_RSTCTRL_PPE2 | MTK_SRAM)
-
- struct mtk_tx_dma_desc_info {
- dma_addr_t addr;
+++ /dev/null
-From e10a35abb3da12b812cfb6fc6137926a0c81e39a Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Sun, 10 Sep 2023 22:40:30 +0100
-Subject: [PATCH] net: ethernet: mtk_eth_soc: fix uninitialized variable
-
-Variable dma_addr in function mtk_poll_rx can be uninitialized on
-some of the error paths. In practise this doesn't matter, even random
-data present in uninitialized stack memory can safely be used in the
-way it happens in the error path.
-
-However, in order to make Smatch happy make sure the variable is
-always initialized.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 5 +++--
- 1 file changed, 3 insertions(+), 2 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -1988,11 +1988,11 @@ static int mtk_poll_rx(struct napi_struc
- u8 *data, *new_data;
- struct mtk_rx_dma_v2 *rxd, trxd;
- int done = 0, bytes = 0;
-+ dma_addr_t dma_addr = DMA_MAPPING_ERROR;
-
- while (done < budget) {
- unsigned int pktlen, *rxdcsum;
- struct net_device *netdev;
-- dma_addr_t dma_addr;
- u32 hash, reason;
- int mac = 0;
-
-@@ -2169,7 +2169,8 @@ release_desc:
- else
- rxd->rxd2 = RX_DMA_PREP_PLEN0(ring->buf_size);
-
-- if (MTK_HAS_CAPS(eth->soc->caps, MTK_36BIT_DMA))
-+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_36BIT_DMA) &&
-+ likely(dma_addr != DMA_MAPPING_ERROR))
- rxd->rxd2 |= RX_DMA_PREP_ADDR64(dma_addr);
-
- ring->calc_idx = idx;
+++ /dev/null
-From 5a124b1fd3e6cb15a943f0cdfe96aa8f6d3d2f39 Mon Sep 17 00:00:00 2001
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Sat, 9 Sep 2023 20:41:56 +0200
-Subject: [PATCH] net: ethernet: mtk_eth_soc: fix pse_port configuration for
- MT7988
-
-MT7988 SoC support 3 NICs. Fix pse_port configuration in
-mtk_flow_set_output_device routine if the traffic is offloaded to eth2.
-Rely on mtk_pse_port definitions.
-
-Fixes: 88efedf517e6 ("net: ethernet: mtk_eth_soc: enable nft hw flowtable_offload for MT7988 SoC")
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/ethernet/mediatek/mtk_ppe_offload.c | 6 ++++--
- 1 file changed, 4 insertions(+), 2 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-@@ -214,9 +214,11 @@ mtk_flow_set_output_device(struct mtk_et
- dsa_port = mtk_flow_get_dsa_port(&dev);
-
- if (dev == eth->netdev[0])
-- pse_port = 1;
-+ pse_port = PSE_GDM1_PORT;
- else if (dev == eth->netdev[1])
-- pse_port = 2;
-+ pse_port = PSE_GDM2_PORT;
-+ else if (dev == eth->netdev[2])
-+ pse_port = PSE_GDM3_PORT;
- else
- return -EOPNOTSUPP;
-
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Mon, 20 Mar 2023 11:44:30 +0100
-Subject: [PATCH] net: ethernet: mtk_eth_soc: add code for offloading flows
- from wlan devices
-
-WED version 2 (on MT7986 and later) can offload flows originating from wireless
-devices. In order to make that work, ndo_setup_tc needs to be implemented on
-the netdevs. This adds the required code to offload flows coming in from WED,
-while keeping track of the incoming wed index used for selecting the correct
-PPE device.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 3 +
- .../net/ethernet/mediatek/mtk_ppe_offload.c | 37 ++++---
- drivers/net/ethernet/mediatek/mtk_wed.c | 101 ++++++++++++++++++
- include/linux/soc/mediatek/mtk_wed.h | 6 ++
- 4 files changed, 133 insertions(+), 14 deletions(-)
-
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -1435,6 +1435,9 @@ 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);
-+int mtk_flow_offload_cmd(struct mtk_eth *eth, struct flow_cls_offload *cls,
-+ int ppe_index);
-+void mtk_flow_offload_cleanup(struct mtk_eth *eth, struct list_head *list);
- void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev);
-
-
---- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
-@@ -237,7 +237,8 @@ out:
- }
-
- static int
--mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
-+mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f,
-+ int ppe_index)
- {
- struct flow_rule *rule = flow_cls_offload_flow_rule(f);
- struct flow_action_entry *act;
-@@ -454,6 +455,7 @@ mtk_flow_offload_replace(struct mtk_eth
- entry->cookie = f->cookie;
- memcpy(&entry->data, &foe, sizeof(entry->data));
- entry->wed_index = wed_index;
-+ entry->ppe_index = ppe_index;
-
- err = mtk_foe_entry_commit(eth->ppe[entry->ppe_index], entry);
- if (err < 0)
-@@ -522,25 +524,15 @@ mtk_flow_offload_stats(struct mtk_eth *e
-
- static DEFINE_MUTEX(mtk_flow_offload_mutex);
-
--static int
--mtk_eth_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
-+int mtk_flow_offload_cmd(struct mtk_eth *eth, struct flow_cls_offload *cls,
-+ int ppe_index)
- {
-- struct flow_cls_offload *cls = type_data;
-- struct net_device *dev = cb_priv;
-- struct mtk_mac *mac = netdev_priv(dev);
-- struct mtk_eth *eth = mac->hw;
- int err;
-
-- if (!tc_can_offload(dev))
-- return -EOPNOTSUPP;
--
-- if (type != TC_SETUP_CLSFLOWER)
-- return -EOPNOTSUPP;
--
- mutex_lock(&mtk_flow_offload_mutex);
- switch (cls->command) {
- case FLOW_CLS_REPLACE:
-- err = mtk_flow_offload_replace(eth, cls);
-+ err = mtk_flow_offload_replace(eth, cls, ppe_index);
- break;
- case FLOW_CLS_DESTROY:
- err = mtk_flow_offload_destroy(eth, cls);
-@@ -558,6 +550,23 @@ mtk_eth_setup_tc_block_cb(enum tc_setup_
- }
-
- static int
-+mtk_eth_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
-+{
-+ struct flow_cls_offload *cls = type_data;
-+ struct net_device *dev = cb_priv;
-+ struct mtk_mac *mac = netdev_priv(dev);
-+ struct mtk_eth *eth = mac->hw;
-+
-+ if (!tc_can_offload(dev))
-+ return -EOPNOTSUPP;
-+
-+ if (type != TC_SETUP_CLSFLOWER)
-+ return -EOPNOTSUPP;
-+
-+ return mtk_flow_offload_cmd(eth, cls, 0);
-+}
-+
-+static int
- mtk_eth_setup_tc_block(struct net_device *dev, struct flow_block_offload *f)
- {
- struct mtk_mac *mac = netdev_priv(dev);
---- a/drivers/net/ethernet/mediatek/mtk_wed.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
-@@ -13,6 +13,8 @@
- #include <linux/mfd/syscon.h>
- #include <linux/debugfs.h>
- #include <linux/soc/mediatek/mtk_wed.h>
-+#include <net/flow_offload.h>
-+#include <net/pkt_cls.h>
- #include "mtk_eth_soc.h"
- #include "mtk_wed_regs.h"
- #include "mtk_wed.h"
-@@ -41,6 +43,11 @@
- static struct mtk_wed_hw *hw_list[2];
- static DEFINE_MUTEX(hw_lock);
-
-+struct mtk_wed_flow_block_priv {
-+ struct mtk_wed_hw *hw;
-+ struct net_device *dev;
-+};
-+
- static void
- wed_m32(struct mtk_wed_device *dev, u32 reg, u32 mask, u32 val)
- {
-@@ -1753,6 +1760,99 @@ out:
- mutex_unlock(&hw_lock);
- }
-
-+static int
-+mtk_wed_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
-+{
-+ struct mtk_wed_flow_block_priv *priv = cb_priv;
-+ struct flow_cls_offload *cls = type_data;
-+ struct mtk_wed_hw *hw = priv->hw;
-+
-+ if (!tc_can_offload(priv->dev))
-+ return -EOPNOTSUPP;
-+
-+ if (type != TC_SETUP_CLSFLOWER)
-+ return -EOPNOTSUPP;
-+
-+ return mtk_flow_offload_cmd(hw->eth, cls, hw->index);
-+}
-+
-+static int
-+mtk_wed_setup_tc_block(struct mtk_wed_hw *hw, struct net_device *dev,
-+ struct flow_block_offload *f)
-+{
-+ struct mtk_wed_flow_block_priv *priv;
-+ static LIST_HEAD(block_cb_list);
-+ struct flow_block_cb *block_cb;
-+ struct mtk_eth *eth = hw->eth;
-+ flow_setup_cb_t *cb;
-+
-+ if (!eth->soc->offload_version)
-+ return -EOPNOTSUPP;
-+
-+ if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
-+ return -EOPNOTSUPP;
-+
-+ cb = mtk_wed_setup_tc_block_cb;
-+ f->driver_block_list = &block_cb_list;
-+
-+ switch (f->command) {
-+ case FLOW_BLOCK_BIND:
-+ block_cb = flow_block_cb_lookup(f->block, cb, dev);
-+ if (block_cb) {
-+ flow_block_cb_incref(block_cb);
-+ return 0;
-+ }
-+
-+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
-+ if (!priv)
-+ return -ENOMEM;
-+
-+ priv->hw = hw;
-+ priv->dev = dev;
-+ block_cb = flow_block_cb_alloc(cb, dev, priv, NULL);
-+ if (IS_ERR(block_cb)) {
-+ kfree(priv);
-+ return PTR_ERR(block_cb);
-+ }
-+
-+ flow_block_cb_incref(block_cb);
-+ flow_block_cb_add(block_cb, f);
-+ list_add_tail(&block_cb->driver_list, &block_cb_list);
-+ return 0;
-+ case FLOW_BLOCK_UNBIND:
-+ block_cb = flow_block_cb_lookup(f->block, cb, dev);
-+ if (!block_cb)
-+ return -ENOENT;
-+
-+ if (!flow_block_cb_decref(block_cb)) {
-+ flow_block_cb_remove(block_cb, f);
-+ list_del(&block_cb->driver_list);
-+ kfree(block_cb->cb_priv);
-+ }
-+ return 0;
-+ default:
-+ return -EOPNOTSUPP;
-+ }
-+}
-+
-+static int
-+mtk_wed_setup_tc(struct mtk_wed_device *wed, struct net_device *dev,
-+ enum tc_setup_type type, void *type_data)
-+{
-+ struct mtk_wed_hw *hw = wed->hw;
-+
-+ if (hw->version < 2)
-+ return -EOPNOTSUPP;
-+
-+ switch (type) {
-+ case TC_SETUP_BLOCK:
-+ case TC_SETUP_FT:
-+ return mtk_wed_setup_tc_block(hw, dev, type_data);
-+ default:
-+ return -EOPNOTSUPP;
-+ }
-+}
-+
- void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
- void __iomem *wdma, phys_addr_t wdma_phy,
- int index)
-@@ -1772,6 +1872,7 @@ void mtk_wed_add_hw(struct device_node *
- .irq_set_mask = mtk_wed_irq_set_mask,
- .detach = mtk_wed_detach,
- .ppe_check = mtk_wed_ppe_check,
-+ .setup_tc = mtk_wed_setup_tc,
- };
- struct device_node *eth_np = eth->dev->of_node;
- struct platform_device *pdev;
---- a/include/linux/soc/mediatek/mtk_wed.h
-+++ b/include/linux/soc/mediatek/mtk_wed.h
-@@ -6,6 +6,7 @@
- #include <linux/regmap.h>
- #include <linux/pci.h>
- #include <linux/skbuff.h>
-+#include <linux/netdevice.h>
-
- #define MTK_WED_TX_QUEUES 2
- #define MTK_WED_RX_QUEUES 2
-@@ -180,6 +181,8 @@ struct mtk_wed_ops {
-
- u32 (*irq_get)(struct mtk_wed_device *dev, u32 mask);
- void (*irq_set_mask)(struct mtk_wed_device *dev, u32 mask);
-+ int (*setup_tc)(struct mtk_wed_device *wed, struct net_device *dev,
-+ enum tc_setup_type type, void *type_data);
- };
-
- extern const struct mtk_wed_ops __rcu *mtk_soc_wed_ops;
-@@ -238,6 +241,8 @@ mtk_wed_get_rx_capa(struct mtk_wed_devic
- (_dev)->ops->msg_update(_dev, _id, _msg, _len)
- #define mtk_wed_device_stop(_dev) (_dev)->ops->stop(_dev)
- #define mtk_wed_device_dma_reset(_dev) (_dev)->ops->reset_dma(_dev)
-+#define mtk_wed_device_setup_tc(_dev, _netdev, _type, _type_data) \
-+ (_dev)->ops->setup_tc(_dev, _netdev, _type, _type_data)
- #else
- static inline bool mtk_wed_device_active(struct mtk_wed_device *dev)
- {
-@@ -256,6 +261,7 @@ static inline bool mtk_wed_device_active
- #define mtk_wed_device_update_msg(_dev, _id, _msg, _len) -ENODEV
- #define mtk_wed_device_stop(_dev) do {} while (0)
- #define mtk_wed_device_dma_reset(_dev) do {} while (0)
-+#define mtk_wed_device_setup_tc(_dev, _netdev, _type, _type_data) -EOPNOTSUPP
- #endif
-
- #endif
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Mon, 20 Mar 2023 15:37:55 +0100
-Subject: [PATCH] net: ethernet: mediatek: mtk_ppe: prefer newly added l2
- flows over existing ones
-
-When a device is roaming between interfaces and a new flow entry is created,
-we should assume that its output device is more up to date than whatever
-entry existed already.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_ppe.c
-+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
-@@ -656,10 +656,20 @@ void mtk_foe_entry_clear(struct mtk_ppe
- static int
- mtk_foe_entry_commit_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
- {
-+ struct mtk_flow_entry *prev;
-+
- entry->type = MTK_FLOW_TYPE_L2;
-
-- return rhashtable_insert_fast(&ppe->l2_flows, &entry->l2_node,
-- mtk_flow_l2_ht_params);
-+ prev = rhashtable_lookup_get_insert_fast(&ppe->l2_flows, &entry->l2_node,
-+ mtk_flow_l2_ht_params);
-+ if (likely(!prev))
-+ return 0;
-+
-+ if (IS_ERR(prev))
-+ return PTR_ERR(prev);
-+
-+ return rhashtable_replace_fast(&ppe->l2_flows, &prev->l2_node,
-+ &entry->l2_node, mtk_flow_l2_ht_params);
- }
-
- int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Sun, 27 Aug 2023 19:31:41 +0200
-Subject: [PATCH] net: ethernet: mtk_wed: add some more info in wed_txinfo_show
- handler
-
-Add some new info in Wireless Ethernet Dispatcher wed_txinfo_show
-debugfs handler useful during debugging.
-
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Link: https://lore.kernel.org/r/3390292655d568180b73d2a25576f61aa63310e5.1693157377.git.lorenzo@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
-@@ -127,8 +127,17 @@ wed_txinfo_show(struct seq_file *s, void
- DUMP_WDMA_RING(WDMA_RING_RX(0)),
- DUMP_WDMA_RING(WDMA_RING_RX(1)),
-
-- DUMP_STR("TX FREE"),
-+ DUMP_STR("WED TX FREE"),
- DUMP_WED(WED_RX_MIB(0)),
-+ DUMP_WED_RING(WED_RING_RX(0)),
-+ DUMP_WED(WED_WPDMA_RX_COHERENT_MIB(0)),
-+ DUMP_WED(WED_RX_MIB(1)),
-+ DUMP_WED_RING(WED_RING_RX(1)),
-+ DUMP_WED(WED_WPDMA_RX_COHERENT_MIB(1)),
-+
-+ DUMP_STR("WED WPDMA TX FREE"),
-+ DUMP_WED_RING(WED_WPDMA_RING_RX(0)),
-+ DUMP_WED_RING(WED_WPDMA_RING_RX(1)),
- };
- struct mtk_wed_hw *hw = s->private;
- struct mtk_wed_device *dev = hw->wed_dev;
---- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
-@@ -266,6 +266,8 @@ struct mtk_wdma_desc {
-
- #define MTK_WED_WPDMA_TX_MIB(_n) (0x5a0 + (_n) * 4)
- #define MTK_WED_WPDMA_TX_COHERENT_MIB(_n) (0x5d0 + (_n) * 4)
-+#define MTK_WED_WPDMA_RX_MIB(_n) (0x5e0 + (_n) * 4)
-+#define MTK_WED_WPDMA_RX_COHERENT_MIB(_n) (0x5f0 + (_n) * 4)
-
- #define MTK_WED_WPDMA_RING_TX(_n) (0x600 + (_n) * 0x10)
- #define MTK_WED_WPDMA_RING_RX(_n) (0x700 + (_n) * 0x10)
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Sun, 27 Aug 2023 19:33:47 +0200
-Subject: [PATCH] net: ethernet: mtk_wed: minor change in wed_{tx,rx}info_show
-
-No functional changes, just cosmetic ones.
-
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Link: https://lore.kernel.org/r/71e046c72a978745f0435af265dda610aa9bfbcf.1693157578.git.lorenzo@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
-
---- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
-+++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
-@@ -84,7 +84,6 @@ dump_wed_regs(struct seq_file *s, struct
- }
- }
-
--
- static int
- wed_txinfo_show(struct seq_file *s, void *data)
- {
-@@ -142,10 +141,8 @@ wed_txinfo_show(struct seq_file *s, void
- struct mtk_wed_hw *hw = s->private;
- struct mtk_wed_device *dev = hw->wed_dev;
-
-- if (!dev)
-- return 0;
--
-- dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs));
-+ if (dev)
-+ dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs));
-
- return 0;
- }
-@@ -217,10 +214,8 @@ wed_rxinfo_show(struct seq_file *s, void
- struct mtk_wed_hw *hw = s->private;
- struct mtk_wed_device *dev = hw->wed_dev;
-
-- if (!dev)
-- return 0;
--
-- dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs));
-+ if (dev)
-+ dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs));
-
- return 0;
- }
+++ /dev/null
-From cfbd6de588ef659c198083205dc954a6d3ed2aec Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Thu, 29 Dec 2022 17:33:35 +0100
-Subject: [PATCH 4/5] net: dsa: qca8k: introduce single mii read/write lo/hi
-
-It may be useful to read/write just the lo or hi half of a reg.
-
-This is especially useful for phy poll with the use of mdio master.
-The mdio master reg is composed by the first 16 bit related to setup and
-the other half with the returned data or data to write.
-
-Refactor the mii function to permit single mii read/write of lo or hi
-half of the reg.
-
-Tested-by: Ronald Wahl <ronald.wahl@raritan.com>
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/qca/qca8k-8xxx.c | 106 ++++++++++++++++++++++++-------
- 1 file changed, 84 insertions(+), 22 deletions(-)
-
---- a/drivers/net/dsa/qca/qca8k-8xxx.c
-+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
-@@ -37,42 +37,104 @@ qca8k_split_addr(u32 regaddr, u16 *r1, u
- }
-
- static int
--qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val)
-+qca8k_mii_write_lo(struct mii_bus *bus, int phy_id, u32 regnum, u32 val)
- {
- int ret;
-+ u16 lo;
-
-- ret = bus->read(bus, phy_id, regnum);
-- if (ret >= 0) {
-- *val = ret;
-- ret = bus->read(bus, phy_id, regnum + 1);
-- *val |= ret << 16;
-- }
-+ lo = val & 0xffff;
-+ ret = bus->write(bus, phy_id, regnum, lo);
-+ if (ret < 0)
-+ dev_err_ratelimited(&bus->dev,
-+ "failed to write qca8k 32bit lo register\n");
-+
-+ return ret;
-+}
-
-- if (ret < 0) {
-+static int
-+qca8k_mii_write_hi(struct mii_bus *bus, int phy_id, u32 regnum, u32 val)
-+{
-+ int ret;
-+ u16 hi;
-+
-+ hi = (u16)(val >> 16);
-+ ret = bus->write(bus, phy_id, regnum, hi);
-+ if (ret < 0)
- dev_err_ratelimited(&bus->dev,
-- "failed to read qca8k 32bit register\n");
-- *val = 0;
-- return ret;
-- }
-+ "failed to write qca8k 32bit hi register\n");
-
-+ return ret;
-+}
-+
-+static int
-+qca8k_mii_read_lo(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val)
-+{
-+ int ret;
-+
-+ ret = bus->read(bus, phy_id, regnum);
-+ if (ret < 0)
-+ goto err;
-+
-+ *val = ret & 0xffff;
- return 0;
-+
-+err:
-+ dev_err_ratelimited(&bus->dev,
-+ "failed to read qca8k 32bit lo register\n");
-+ *val = 0;
-+
-+ return ret;
- }
-
--static void
--qca8k_mii_write32(struct mii_bus *bus, int phy_id, u32 regnum, u32 val)
-+static int
-+qca8k_mii_read_hi(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val)
- {
-- u16 lo, hi;
- int ret;
-
-- lo = val & 0xffff;
-- hi = (u16)(val >> 16);
-+ ret = bus->read(bus, phy_id, regnum);
-+ if (ret < 0)
-+ goto err;
-
-- ret = bus->write(bus, phy_id, regnum, lo);
-- if (ret >= 0)
-- ret = bus->write(bus, phy_id, regnum + 1, hi);
-+ *val = ret << 16;
-+ return 0;
-+
-+err:
-+ dev_err_ratelimited(&bus->dev,
-+ "failed to read qca8k 32bit hi register\n");
-+ *val = 0;
-+
-+ return ret;
-+}
-+
-+static int
-+qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val)
-+{
-+ u32 hi, lo;
-+ int ret;
-+
-+ *val = 0;
-+
-+ ret = qca8k_mii_read_lo(bus, phy_id, regnum, &lo);
- if (ret < 0)
-- dev_err_ratelimited(&bus->dev,
-- "failed to write qca8k 32bit register\n");
-+ goto err;
-+
-+ ret = qca8k_mii_read_hi(bus, phy_id, regnum + 1, &hi);
-+ if (ret < 0)
-+ goto err;
-+
-+ *val = lo | hi;
-+
-+err:
-+ return ret;
-+}
-+
-+static void
-+qca8k_mii_write32(struct mii_bus *bus, int phy_id, u32 regnum, u32 val)
-+{
-+ if (qca8k_mii_write_lo(bus, phy_id, regnum, val) < 0)
-+ return;
-+
-+ qca8k_mii_write_hi(bus, phy_id, regnum + 1, val);
- }
-
- static int
+++ /dev/null
-From a4165830ca237f2b3318faf62562bce8ce12a389 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Thu, 29 Dec 2022 17:33:36 +0100
-Subject: [PATCH 5/5] net: dsa: qca8k: improve mdio master read/write by using
- single lo/hi
-
-Improve mdio master read/write by using singe mii read/write lo/hi.
-
-In a read and write we need to poll the mdio master regs in a busy loop
-to check for a specific bit present in the upper half of the reg. We can
-ignore the other half since it won't contain useful data. This will save
-an additional useless read for each read and write operation.
-
-In a read operation the returned data is present in the mdio master reg
-lower half. We can ignore the other half since it won't contain useful
-data. This will save an additional useless read for each read operation.
-
-In a read operation it's needed to just set the hi half of the mdio
-master reg as the lo half will be replaced by the result. This will save
-an additional useless write for each read operation.
-
-Tested-by: Ronald Wahl <ronald.wahl@raritan.com>
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/qca/qca8k-8xxx.c | 12 ++++++------
- 1 file changed, 6 insertions(+), 6 deletions(-)
-
---- a/drivers/net/dsa/qca/qca8k-8xxx.c
-+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
-@@ -754,9 +754,9 @@ qca8k_mdio_busy_wait(struct mii_bus *bus
-
- qca8k_split_addr(reg, &r1, &r2, &page);
-
-- ret = read_poll_timeout(qca8k_mii_read32, ret1, !(val & mask), 0,
-+ ret = read_poll_timeout(qca8k_mii_read_hi, ret1, !(val & mask), 0,
- QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false,
-- bus, 0x10 | r2, r1, &val);
-+ bus, 0x10 | r2, r1 + 1, &val);
-
- /* Check if qca8k_read has failed for a different reason
- * before returnting -ETIMEDOUT
-@@ -798,7 +798,7 @@ qca8k_mdio_write(struct qca8k_priv *priv
-
- exit:
- /* even if the busy_wait timeouts try to clear the MASTER_EN */
-- qca8k_mii_write32(bus, 0x10 | r2, r1, 0);
-+ qca8k_mii_write_hi(bus, 0x10 | r2, r1 + 1, 0);
-
- mutex_unlock(&bus->mdio_lock);
-
-@@ -828,18 +828,18 @@ qca8k_mdio_read(struct qca8k_priv *priv,
- if (ret)
- goto exit;
-
-- qca8k_mii_write32(bus, 0x10 | r2, r1, val);
-+ qca8k_mii_write_hi(bus, 0x10 | r2, r1 + 1, val);
-
- ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL,
- QCA8K_MDIO_MASTER_BUSY);
- if (ret)
- goto exit;
-
-- ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val);
-+ ret = qca8k_mii_read_lo(bus, 0x10 | r2, r1, &val);
-
- exit:
- /* even if the busy_wait timeouts try to clear the MASTER_EN */
-- qca8k_mii_write32(bus, 0x10 | r2, r1, 0);
-+ qca8k_mii_write_hi(bus, 0x10 | r2, r1 + 1, 0);
-
- mutex_unlock(&bus->mdio_lock);
-
+++ /dev/null
-From e03cea60c3db8c6b011cc36ecef9281dff8377f3 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Wed, 25 Jan 2023 21:35:16 +0100
-Subject: [PATCH] net: dsa: qca8k: add QCA8K_ATU_TABLE_SIZE define for fdb
- access
-
-Add and use QCA8K_ATU_TABLE_SIZE instead of hardcoding the ATU size with
-a pure number and using sizeof on the array.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/qca/qca8k-common.c | 10 ++++++----
- drivers/net/dsa/qca/qca8k.h | 2 ++
- 2 files changed, 8 insertions(+), 4 deletions(-)
-
---- a/drivers/net/dsa/qca/qca8k-common.c
-+++ b/drivers/net/dsa/qca/qca8k-common.c
-@@ -150,11 +150,12 @@ static int qca8k_busy_wait(struct qca8k_
-
- static int qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb)
- {
-- u32 reg[3];
-+ u32 reg[QCA8K_ATU_TABLE_SIZE];
- int ret;
-
- /* load the ARL table into an array */
-- ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg));
-+ ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg,
-+ QCA8K_ATU_TABLE_SIZE * sizeof(u32));
- if (ret)
- return ret;
-
-@@ -178,7 +179,7 @@ static int qca8k_fdb_read(struct qca8k_p
- static void qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask,
- const u8 *mac, u8 aging)
- {
-- u32 reg[3] = { 0 };
-+ u32 reg[QCA8K_ATU_TABLE_SIZE] = { 0 };
-
- /* vid - 83:72 */
- reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid);
-@@ -195,7 +196,8 @@ static void qca8k_fdb_write(struct qca8k
- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]);
-
- /* load the array into the ARL table */
-- qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg));
-+ qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg,
-+ QCA8K_ATU_TABLE_SIZE * sizeof(u32));
- }
-
- static int qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd,
---- a/drivers/net/dsa/qca/qca8k.h
-+++ b/drivers/net/dsa/qca/qca8k.h
-@@ -148,6 +148,8 @@
- #define QCA8K_REG_IPV4_PRI_ADDR_MASK 0x474
-
- /* Lookup registers */
-+#define QCA8K_ATU_TABLE_SIZE 3 /* 12 bytes wide table / sizeof(u32) */
-+
- #define QCA8K_REG_ATU_DATA0 0x600
- #define QCA8K_ATU_ADDR2_MASK GENMASK(31, 24)
- #define QCA8K_ATU_ADDR3_MASK GENMASK(23, 16)
+++ /dev/null
-From c766e077d927e1775902c18827205ea2ade3a35d Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Wed, 25 Jan 2023 21:35:17 +0100
-Subject: [PATCH] net: dsa: qca8k: convert to regmap read/write API
-
-Convert qca8k to regmap read/write bulk API. The mgmt eth can write up
-to 32 bytes of data at times. Currently we use a custom function to do
-it but regmap now supports declaration of read/write bulk even without a
-bus.
-
-Drop the custom function and rework the regmap function to this new
-implementation.
-
-Rework the qca8k_fdb_read/write function to use the new
-regmap_bulk_read/write as the old qca8k_bulk_read/write are now dropped.
-
-Cc: Mark Brown <broonie@kernel.org>
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/qca/qca8k-8xxx.c | 92 ++++++++++++++++++++++++------
- drivers/net/dsa/qca/qca8k-common.c | 47 ++-------------
- drivers/net/dsa/qca/qca8k.h | 3 -
- 3 files changed, 77 insertions(+), 65 deletions(-)
-
---- a/drivers/net/dsa/qca/qca8k-8xxx.c
-+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
-@@ -425,16 +425,12 @@ qca8k_regmap_update_bits_eth(struct qca8
- }
-
- static int
--qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val)
-+qca8k_read_mii(struct qca8k_priv *priv, uint32_t reg, uint32_t *val)
- {
-- struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
- struct mii_bus *bus = priv->bus;
- u16 r1, r2, page;
- int ret;
-
-- if (!qca8k_read_eth(priv, reg, val, sizeof(*val)))
-- return 0;
--
- qca8k_split_addr(reg, &r1, &r2, &page);
-
- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
-@@ -451,16 +447,12 @@ exit:
- }
-
- static int
--qca8k_regmap_write(void *ctx, uint32_t reg, uint32_t val)
-+qca8k_write_mii(struct qca8k_priv *priv, uint32_t reg, uint32_t val)
- {
-- struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
- struct mii_bus *bus = priv->bus;
- u16 r1, r2, page;
- int ret;
-
-- if (!qca8k_write_eth(priv, reg, &val, sizeof(val)))
-- return 0;
--
- qca8k_split_addr(reg, &r1, &r2, &page);
-
- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
-@@ -477,17 +469,14 @@ exit:
- }
-
- static int
--qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask, uint32_t write_val)
-+qca8k_regmap_update_bits_mii(struct qca8k_priv *priv, uint32_t reg,
-+ uint32_t mask, uint32_t write_val)
- {
-- struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
- struct mii_bus *bus = priv->bus;
- u16 r1, r2, page;
- u32 val;
- int ret;
-
-- if (!qca8k_regmap_update_bits_eth(priv, reg, mask, write_val))
-- return 0;
--
- qca8k_split_addr(reg, &r1, &r2, &page);
-
- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
-@@ -510,17 +499,84 @@ exit:
- return ret;
- }
-
-+static int
-+qca8k_bulk_read(void *ctx, const void *reg_buf, size_t reg_len,
-+ void *val_buf, size_t val_len)
-+{
-+ int i, count = val_len / sizeof(u32), ret;
-+ u32 reg = *(u32 *)reg_buf & U16_MAX;
-+ struct qca8k_priv *priv = ctx;
-+
-+ if (priv->mgmt_master &&
-+ !qca8k_read_eth(priv, reg, val_buf, val_len))
-+ return 0;
-+
-+ /* loop count times and increment reg of 4 */
-+ for (i = 0; i < count; i++, reg += sizeof(u32)) {
-+ ret = qca8k_read_mii(priv, reg, val_buf + i);
-+ if (ret < 0)
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
-+static int
-+qca8k_bulk_gather_write(void *ctx, const void *reg_buf, size_t reg_len,
-+ const void *val_buf, size_t val_len)
-+{
-+ int i, count = val_len / sizeof(u32), ret;
-+ u32 reg = *(u32 *)reg_buf & U16_MAX;
-+ struct qca8k_priv *priv = ctx;
-+ u32 *val = (u32 *)val_buf;
-+
-+ if (priv->mgmt_master &&
-+ !qca8k_write_eth(priv, reg, val, val_len))
-+ return 0;
-+
-+ /* loop count times, increment reg of 4 and increment val ptr to
-+ * the next value
-+ */
-+ for (i = 0; i < count; i++, reg += sizeof(u32), val++) {
-+ ret = qca8k_write_mii(priv, reg, *val);
-+ if (ret < 0)
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
-+static int
-+qca8k_bulk_write(void *ctx, const void *data, size_t bytes)
-+{
-+ return qca8k_bulk_gather_write(ctx, data, sizeof(u16), data + sizeof(u16),
-+ bytes - sizeof(u16));
-+}
-+
-+static int
-+qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask, uint32_t write_val)
-+{
-+ struct qca8k_priv *priv = ctx;
-+
-+ if (!qca8k_regmap_update_bits_eth(priv, reg, mask, write_val))
-+ return 0;
-+
-+ return qca8k_regmap_update_bits_mii(priv, reg, mask, write_val);
-+}
-+
- static struct regmap_config qca8k_regmap_config = {
- .reg_bits = 16,
- .val_bits = 32,
- .reg_stride = 4,
- .max_register = 0x16ac, /* end MIB - Port6 range */
-- .reg_read = qca8k_regmap_read,
-- .reg_write = qca8k_regmap_write,
-+ .read = qca8k_bulk_read,
-+ .write = qca8k_bulk_write,
- .reg_update_bits = qca8k_regmap_update_bits,
- .rd_table = &qca8k_readable_table,
- .disable_locking = true, /* Locking is handled by qca8k read/write */
- .cache_type = REGCACHE_NONE, /* Explicitly disable CACHE */
-+ .max_raw_read = 32, /* mgmt eth can read/write up to 8 registers at time */
-+ .max_raw_write = 32,
- };
-
- static int
-@@ -2112,8 +2168,6 @@ static SIMPLE_DEV_PM_OPS(qca8k_pm_ops,
-
- static const struct qca8k_info_ops qca8xxx_ops = {
- .autocast_mib = qca8k_get_ethtool_stats_eth,
-- .read_eth = qca8k_read_eth,
-- .write_eth = qca8k_write_eth,
- };
-
- static const struct qca8k_match_data qca8327 = {
---- a/drivers/net/dsa/qca/qca8k-common.c
-+++ b/drivers/net/dsa/qca/qca8k-common.c
-@@ -101,45 +101,6 @@ const struct regmap_access_table qca8k_r
- .n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges),
- };
-
--/* TODO: remove these extra ops when we can support regmap bulk read/write */
--static int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
--{
-- int i, count = len / sizeof(u32), ret;
--
-- if (priv->mgmt_master && priv->info->ops->read_eth &&
-- !priv->info->ops->read_eth(priv, reg, val, len))
-- return 0;
--
-- for (i = 0; i < count; i++) {
-- ret = regmap_read(priv->regmap, reg + (i * 4), val + i);
-- if (ret < 0)
-- return ret;
-- }
--
-- return 0;
--}
--
--/* TODO: remove these extra ops when we can support regmap bulk read/write */
--static int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
--{
-- int i, count = len / sizeof(u32), ret;
-- u32 tmp;
--
-- if (priv->mgmt_master && priv->info->ops->write_eth &&
-- !priv->info->ops->write_eth(priv, reg, val, len))
-- return 0;
--
-- for (i = 0; i < count; i++) {
-- tmp = val[i];
--
-- ret = regmap_write(priv->regmap, reg + (i * 4), tmp);
-- if (ret < 0)
-- return ret;
-- }
--
-- return 0;
--}
--
- static int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)
- {
- u32 val;
-@@ -154,8 +115,8 @@ static int qca8k_fdb_read(struct qca8k_p
- int ret;
-
- /* load the ARL table into an array */
-- ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg,
-- QCA8K_ATU_TABLE_SIZE * sizeof(u32));
-+ ret = regmap_bulk_read(priv->regmap, QCA8K_REG_ATU_DATA0, reg,
-+ QCA8K_ATU_TABLE_SIZE);
- if (ret)
- return ret;
-
-@@ -196,8 +157,8 @@ static void qca8k_fdb_write(struct qca8k
- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]);
-
- /* load the array into the ARL table */
-- qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg,
-- QCA8K_ATU_TABLE_SIZE * sizeof(u32));
-+ regmap_bulk_write(priv->regmap, QCA8K_REG_ATU_DATA0, reg,
-+ QCA8K_ATU_TABLE_SIZE);
- }
-
- static int qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd,
---- a/drivers/net/dsa/qca/qca8k.h
-+++ b/drivers/net/dsa/qca/qca8k.h
-@@ -330,9 +330,6 @@ struct qca8k_priv;
-
- struct qca8k_info_ops {
- int (*autocast_mib)(struct dsa_switch *ds, int port, u64 *data);
-- /* TODO: remove these extra ops when we can support regmap bulk read/write */
-- int (*read_eth)(struct qca8k_priv *priv, u32 reg, u32 *val, int len);
-- int (*write_eth)(struct qca8k_priv *priv, u32 reg, u32 *val, int len);
- };
-
- struct qca8k_match_data {
+++ /dev/null
-From 2c39dd025da489cf87d26469d9f5ff19715324a0 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Mon, 24 Jul 2023 05:25:28 +0200
-Subject: [PATCH 1/4] net: dsa: qca8k: enable use_single_write for qca8xxx
-
-The qca8xxx switch supports 2 way to write reg values, a slow way using
-mdio and a fast way by sending specially crafted mgmt packet to
-read/write reg.
-
-The fast way can support up to 32 bytes of data as eth packet are used
-to send/receive.
-
-This correctly works for almost the entire regmap of the switch but with
-the use of some kernel selftests for dsa drivers it was found a funny
-and interesting hw defect/limitation.
-
-For some specific reg, bulk write won't work and will result in writing
-only part of the requested regs resulting in half data written. This was
-especially hard to track and discover due to the total strangeness of
-the problem and also by the specific regs where this occurs.
-
-This occurs in the specific regs of the ATU table, where multiple entry
-needs to be written to compose the entire entry.
-It was discovered that with a bulk write of 12 bytes on
-QCA8K_REG_ATU_DATA0 only QCA8K_REG_ATU_DATA0 and QCA8K_REG_ATU_DATA2
-were written, but QCA8K_REG_ATU_DATA1 was always zero.
-Tcpdump was used to make sure the specially crafted packet was correct
-and this was confirmed.
-
-The problem was hard to track as the lack of QCA8K_REG_ATU_DATA1
-resulted in an entry somehow possible as the first bytes of the mac
-address are set in QCA8K_REG_ATU_DATA0 and the entry type is set in
-QCA8K_REG_ATU_DATA2.
-
-Funlly enough writing QCA8K_REG_ATU_DATA1 results in the same problem
-with QCA8K_REG_ATU_DATA2 empty and QCA8K_REG_ATU_DATA1 and
-QCA8K_REG_ATU_FUNC correctly written.
-A speculation on the problem might be that there are some kind of
-indirection internally when accessing these regs and they can't be
-accessed all together, due to the fact that it's really a table mapped
-somewhere in the switch SRAM.
-
-Even more funny is the fact that every other reg was tested with all
-kind of combination and they are not affected by this problem. Read
-operation was also tested and always worked so it's not affected by this
-problem.
-
-The problem is not present if we limit writing a single reg at times.
-
-To handle this hardware defect, enable use_single_write so that bulk
-api can correctly split the write in multiple different operation
-effectively reverting to a non-bulk write.
-
-Cc: Mark Brown <broonie@kernel.org>
-Fixes: c766e077d927 ("net: dsa: qca8k: convert to regmap read/write API")
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Cc: stable@vger.kernel.org
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/qca/qca8k-8xxx.c | 7 +++++--
- 1 file changed, 5 insertions(+), 2 deletions(-)
-
---- a/drivers/net/dsa/qca/qca8k-8xxx.c
-+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
-@@ -575,8 +575,11 @@ static struct regmap_config qca8k_regmap
- .rd_table = &qca8k_readable_table,
- .disable_locking = true, /* Locking is handled by qca8k read/write */
- .cache_type = REGCACHE_NONE, /* Explicitly disable CACHE */
-- .max_raw_read = 32, /* mgmt eth can read/write up to 8 registers at time */
-- .max_raw_write = 32,
-+ .max_raw_read = 32, /* mgmt eth can read up to 8 registers at time */
-+ /* ATU regs suffer from a bug where some data are not correctly
-+ * written. Disable bulk write to correctly write ATU entry.
-+ */
-+ .use_single_write = true,
- };
-
- static int
+++ /dev/null
-From 23cfc7172e5297d0bee49ac6f6f8248d1cf0820d Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Sun, 30 Jul 2023 09:41:10 +0200
-Subject: [PATCH 1/4] net: dsa: qca8k: make learning configurable and keep off
- if standalone
-
-Address learning should initially be turned off by the driver for port
-operation in standalone mode, then the DSA core handles changes to it
-via ds->ops->port_bridge_flags().
-
-Currently this is not the case for qca8k where learning is enabled
-unconditionally in qca8k_setup for every user port.
-
-Handle ports configured in standalone mode by making the learning
-configurable and not enabling it by default.
-
-Implement .port_pre_bridge_flags and .port_bridge_flags dsa ops to
-enable learning for bridge that request it and tweak
-.port_stp_state_set to correctly disable learning when port is
-configured in standalone mode.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://lore.kernel.org/r/20230730074113.21889-2-ansuelsmth@gmail.com
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/dsa/qca/qca8k-8xxx.c | 7 +++--
- drivers/net/dsa/qca/qca8k-common.c | 48 ++++++++++++++++++++++++++++++
- drivers/net/dsa/qca/qca8k.h | 6 ++++
- 3 files changed, 58 insertions(+), 3 deletions(-)
-
---- a/drivers/net/dsa/qca/qca8k-8xxx.c
-+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
-@@ -1905,9 +1905,8 @@ qca8k_setup(struct dsa_switch *ds)
- if (ret)
- return ret;
-
-- /* Enable ARP Auto-learning by default */
-- ret = regmap_set_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i),
-- QCA8K_PORT_LOOKUP_LEARN);
-+ ret = regmap_clear_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i),
-+ QCA8K_PORT_LOOKUP_LEARN);
- if (ret)
- return ret;
-
-@@ -2013,6 +2012,8 @@ static const struct dsa_switch_ops qca8k
- .port_change_mtu = qca8k_port_change_mtu,
- .port_max_mtu = qca8k_port_max_mtu,
- .port_stp_state_set = qca8k_port_stp_state_set,
-+ .port_pre_bridge_flags = qca8k_port_pre_bridge_flags,
-+ .port_bridge_flags = qca8k_port_bridge_flags,
- .port_bridge_join = qca8k_port_bridge_join,
- .port_bridge_leave = qca8k_port_bridge_leave,
- .port_fast_age = qca8k_port_fast_age,
---- a/drivers/net/dsa/qca/qca8k-common.c
-+++ b/drivers/net/dsa/qca/qca8k-common.c
-@@ -565,9 +565,26 @@ int qca8k_get_mac_eee(struct dsa_switch
- return 0;
- }
-
-+static int qca8k_port_configure_learning(struct dsa_switch *ds, int port,
-+ bool learning)
-+{
-+ struct qca8k_priv *priv = ds->priv;
-+
-+ if (learning)
-+ return regmap_set_bits(priv->regmap,
-+ QCA8K_PORT_LOOKUP_CTRL(port),
-+ QCA8K_PORT_LOOKUP_LEARN);
-+ else
-+ return regmap_clear_bits(priv->regmap,
-+ QCA8K_PORT_LOOKUP_CTRL(port),
-+ QCA8K_PORT_LOOKUP_LEARN);
-+}
-+
- void qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
- {
-+ struct dsa_port *dp = dsa_to_port(ds, port);
- struct qca8k_priv *priv = ds->priv;
-+ bool learning = false;
- u32 stp_state;
-
- switch (state) {
-@@ -582,8 +599,11 @@ void qca8k_port_stp_state_set(struct dsa
- break;
- case BR_STATE_LEARNING:
- stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING;
-+ learning = dp->learning;
- break;
- case BR_STATE_FORWARDING:
-+ learning = dp->learning;
-+ fallthrough;
- default:
- stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD;
- break;
-@@ -591,6 +611,34 @@ void qca8k_port_stp_state_set(struct dsa
-
- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
- QCA8K_PORT_LOOKUP_STATE_MASK, stp_state);
-+
-+ qca8k_port_configure_learning(ds, port, learning);
-+}
-+
-+int qca8k_port_pre_bridge_flags(struct dsa_switch *ds, int port,
-+ struct switchdev_brport_flags flags,
-+ struct netlink_ext_ack *extack)
-+{
-+ if (flags.mask & ~BR_LEARNING)
-+ return -EINVAL;
-+
-+ return 0;
-+}
-+
-+int qca8k_port_bridge_flags(struct dsa_switch *ds, int port,
-+ struct switchdev_brport_flags flags,
-+ struct netlink_ext_ack *extack)
-+{
-+ int ret;
-+
-+ if (flags.mask & BR_LEARNING) {
-+ ret = qca8k_port_configure_learning(ds, port,
-+ flags.val & BR_LEARNING);
-+ if (ret)
-+ return ret;
-+ }
-+
-+ return 0;
- }
-
- int qca8k_port_bridge_join(struct dsa_switch *ds, int port,
---- a/drivers/net/dsa/qca/qca8k.h
-+++ b/drivers/net/dsa/qca/qca8k.h
-@@ -448,6 +448,12 @@ int qca8k_get_mac_eee(struct dsa_switch
-
- /* Common bridge function */
- void qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state);
-+int qca8k_port_pre_bridge_flags(struct dsa_switch *ds, int port,
-+ struct switchdev_brport_flags flags,
-+ struct netlink_ext_ack *extack);
-+int qca8k_port_bridge_flags(struct dsa_switch *ds, int port,
-+ struct switchdev_brport_flags flags,
-+ struct netlink_ext_ack *extack);
- int qca8k_port_bridge_join(struct dsa_switch *ds, int port,
- struct dsa_bridge bridge,
- bool *tx_fwd_offload,
+++ /dev/null
-From 18e8feae4a807994e4906d659116d249bfecd4c5 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Sun, 30 Jul 2023 09:41:11 +0200
-Subject: [PATCH 2/4] net: dsa: qca8k: limit user ports access to the first CPU
- port on setup
-
-In preparation for multi-CPU support, set CPU port LOOKUP MEMBER outside
-the port loop and setup the LOOKUP MEMBER mask for user ports only to
-the first CPU port.
-
-This is to handle flooding condition where every CPU port is set as
-target and prevent packet duplication for unknown frames from user ports.
-
-Secondary CPU port LOOKUP MEMBER mask will be setup later when
-port_change_master will be implemented.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Simon Horman <simon.horman@corigine.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-Link: https://lore.kernel.org/r/20230730074113.21889-3-ansuelsmth@gmail.com
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/dsa/qca/qca8k-8xxx.c | 14 ++++++--------
- 1 file changed, 6 insertions(+), 8 deletions(-)
-
---- a/drivers/net/dsa/qca/qca8k-8xxx.c
-+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
-@@ -1885,18 +1885,16 @@ qca8k_setup(struct dsa_switch *ds)
- if (ret)
- return ret;
-
-+ /* CPU port gets connected to all user ports of the switch */
-+ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(cpu_port),
-+ QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds));
-+ if (ret)
-+ return ret;
-+
- /* Setup connection between CPU port & user ports
- * Configure specific switch configuration for ports
- */
- for (i = 0; i < QCA8K_NUM_PORTS; i++) {
-- /* CPU port gets connected to all user ports of the switch */
-- if (dsa_is_cpu_port(ds, i)) {
-- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
-- QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds));
-- if (ret)
-- return ret;
-- }
--
- /* Individual user ports get connected to CPU port only */
- if (dsa_is_user_port(ds, i)) {
- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
+++ /dev/null
-From a9108b0712bf018dc69020864b21485b71b17dfc Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Sun, 30 Jul 2023 09:41:12 +0200
-Subject: [PATCH 3/4] net: dsa: qca8k: move qca8xxx hol fixup to separate
- function
-
-Move qca8xxx hol fixup to separate function to tidy things up and to
-permit using a more efficent loop in future patch.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://lore.kernel.org/r/20230730074113.21889-4-ansuelsmth@gmail.com
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/dsa/qca/qca8k-8xxx.c | 78 +++++++++++++++++---------------
- 1 file changed, 42 insertions(+), 36 deletions(-)
-
---- a/drivers/net/dsa/qca/qca8k-8xxx.c
-+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
-@@ -1795,6 +1795,46 @@ static int qca8k_connect_tag_protocol(st
- return 0;
- }
-
-+static void qca8k_setup_hol_fixup(struct qca8k_priv *priv, int port)
-+{
-+ u32 mask;
-+
-+ switch (port) {
-+ /* The 2 CPU port and port 5 requires some different
-+ * priority than any other ports.
-+ */
-+ case 0:
-+ case 5:
-+ case 6:
-+ mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) |
-+ QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) |
-+ QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x4) |
-+ QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x4) |
-+ QCA8K_PORT_HOL_CTRL0_EG_PRI4(0x6) |
-+ QCA8K_PORT_HOL_CTRL0_EG_PRI5(0x8) |
-+ QCA8K_PORT_HOL_CTRL0_EG_PORT(0x1e);
-+ break;
-+ default:
-+ mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) |
-+ QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) |
-+ QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x6) |
-+ QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) |
-+ QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19);
-+ }
-+ regmap_write(priv->regmap, QCA8K_REG_PORT_HOL_CTRL0(port), mask);
-+
-+ mask = QCA8K_PORT_HOL_CTRL1_ING(0x6) |
-+ QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN |
-+ QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN |
-+ QCA8K_PORT_HOL_CTRL1_WRED_EN;
-+ regmap_update_bits(priv->regmap, QCA8K_REG_PORT_HOL_CTRL1(port),
-+ QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK |
-+ QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN |
-+ QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN |
-+ QCA8K_PORT_HOL_CTRL1_WRED_EN,
-+ mask);
-+}
-+
- static int
- qca8k_setup(struct dsa_switch *ds)
- {
-@@ -1930,42 +1970,8 @@ qca8k_setup(struct dsa_switch *ds)
- * missing settings to improve switch stability under load condition.
- * This problem is limited to qca8337 and other qca8k switch are not affected.
- */
-- if (priv->switch_id == QCA8K_ID_QCA8337) {
-- switch (i) {
-- /* The 2 CPU port and port 5 requires some different
-- * priority than any other ports.
-- */
-- case 0:
-- case 5:
-- case 6:
-- mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) |
-- QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) |
-- QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x4) |
-- QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x4) |
-- QCA8K_PORT_HOL_CTRL0_EG_PRI4(0x6) |
-- QCA8K_PORT_HOL_CTRL0_EG_PRI5(0x8) |
-- QCA8K_PORT_HOL_CTRL0_EG_PORT(0x1e);
-- break;
-- default:
-- mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) |
-- QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) |
-- QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x6) |
-- QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) |
-- QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19);
-- }
-- qca8k_write(priv, QCA8K_REG_PORT_HOL_CTRL0(i), mask);
--
-- mask = QCA8K_PORT_HOL_CTRL1_ING(0x6) |
-- QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN |
-- QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN |
-- QCA8K_PORT_HOL_CTRL1_WRED_EN;
-- qca8k_rmw(priv, QCA8K_REG_PORT_HOL_CTRL1(i),
-- QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK |
-- QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN |
-- QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN |
-- QCA8K_PORT_HOL_CTRL1_WRED_EN,
-- mask);
-- }
-+ if (priv->switch_id == QCA8K_ID_QCA8337)
-+ qca8k_setup_hol_fixup(priv, i);
- }
-
- /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */
+++ /dev/null
-From 01e6f8ad8d26ced14b0cf288c42e55d03a7c5070 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Sun, 30 Jul 2023 09:41:13 +0200
-Subject: [PATCH 4/4] net: dsa: qca8k: use dsa_for_each macro instead of for
- loop
-
-Convert for loop to dsa_for_each macro to save some redundant write on
-unconnected/unused port and tidy things up.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://lore.kernel.org/r/20230730074113.21889-5-ansuelsmth@gmail.com
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/dsa/qca/qca8k-8xxx.c | 107 ++++++++++++++++---------------
- 1 file changed, 54 insertions(+), 53 deletions(-)
-
---- a/drivers/net/dsa/qca/qca8k-8xxx.c
-+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
-@@ -1839,7 +1839,8 @@ static int
- qca8k_setup(struct dsa_switch *ds)
- {
- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
-- int cpu_port, ret, i;
-+ struct dsa_port *dp;
-+ int cpu_port, ret;
- u32 mask;
-
- cpu_port = qca8k_find_cpu_port(ds);
-@@ -1890,27 +1891,27 @@ qca8k_setup(struct dsa_switch *ds)
- dev_warn(priv->dev, "mib init failed");
-
- /* Initial setup of all ports */
-- for (i = 0; i < QCA8K_NUM_PORTS; i++) {
-+ dsa_switch_for_each_port(dp, ds) {
- /* Disable forwarding by default on all ports */
-- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
-+ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(dp->index),
- QCA8K_PORT_LOOKUP_MEMBER, 0);
- if (ret)
- return ret;
-+ }
-
-- /* Enable QCA header mode on all cpu ports */
-- if (dsa_is_cpu_port(ds, i)) {
-- ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(i),
-- FIELD_PREP(QCA8K_PORT_HDR_CTRL_TX_MASK, QCA8K_PORT_HDR_CTRL_ALL) |
-- FIELD_PREP(QCA8K_PORT_HDR_CTRL_RX_MASK, QCA8K_PORT_HDR_CTRL_ALL));
-- if (ret) {
-- dev_err(priv->dev, "failed enabling QCA header mode");
-- return ret;
-- }
-+ /* Disable MAC by default on all user ports */
-+ dsa_switch_for_each_user_port(dp, ds)
-+ qca8k_port_set_status(priv, dp->index, 0);
-+
-+ /* Enable QCA header mode on all cpu ports */
-+ dsa_switch_for_each_cpu_port(dp, ds) {
-+ ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(dp->index),
-+ FIELD_PREP(QCA8K_PORT_HDR_CTRL_TX_MASK, QCA8K_PORT_HDR_CTRL_ALL) |
-+ FIELD_PREP(QCA8K_PORT_HDR_CTRL_RX_MASK, QCA8K_PORT_HDR_CTRL_ALL));
-+ if (ret) {
-+ dev_err(priv->dev, "failed enabling QCA header mode on port %d", dp->index);
-+ return ret;
- }
--
-- /* Disable MAC by default on all user ports */
-- if (dsa_is_user_port(ds, i))
-- qca8k_port_set_status(priv, i, 0);
- }
-
- /* Forward all unknown frames to CPU port for Linux processing
-@@ -1932,48 +1933,48 @@ qca8k_setup(struct dsa_switch *ds)
- return ret;
-
- /* Setup connection between CPU port & user ports
-- * Configure specific switch configuration for ports
-+ * Individual user ports get connected to CPU port only
- */
-- for (i = 0; i < QCA8K_NUM_PORTS; i++) {
-- /* Individual user ports get connected to CPU port only */
-- if (dsa_is_user_port(ds, i)) {
-- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
-- QCA8K_PORT_LOOKUP_MEMBER,
-- BIT(cpu_port));
-- if (ret)
-- return ret;
--
-- ret = regmap_clear_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i),
-- QCA8K_PORT_LOOKUP_LEARN);
-- if (ret)
-- return ret;
--
-- /* For port based vlans to work we need to set the
-- * default egress vid
-- */
-- ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i),
-- QCA8K_EGREES_VLAN_PORT_MASK(i),
-- QCA8K_EGREES_VLAN_PORT(i, QCA8K_PORT_VID_DEF));
-- if (ret)
-- return ret;
--
-- ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i),
-- QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) |
-- QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF));
-- if (ret)
-- return ret;
-- }
-+ dsa_switch_for_each_user_port(dp, ds) {
-+ u8 port = dp->index;
-
-- /* The port 5 of the qca8337 have some problem in flood condition. The
-- * original legacy driver had some specific buffer and priority settings
-- * for the different port suggested by the QCA switch team. Add this
-- * missing settings to improve switch stability under load condition.
-- * This problem is limited to qca8337 and other qca8k switch are not affected.
-+ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
-+ QCA8K_PORT_LOOKUP_MEMBER,
-+ BIT(cpu_port));
-+ if (ret)
-+ return ret;
-+
-+ ret = regmap_clear_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port),
-+ QCA8K_PORT_LOOKUP_LEARN);
-+ if (ret)
-+ return ret;
-+
-+ /* For port based vlans to work we need to set the
-+ * default egress vid
- */
-- if (priv->switch_id == QCA8K_ID_QCA8337)
-- qca8k_setup_hol_fixup(priv, i);
-+ ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port),
-+ QCA8K_EGREES_VLAN_PORT_MASK(port),
-+ QCA8K_EGREES_VLAN_PORT(port, QCA8K_PORT_VID_DEF));
-+ if (ret)
-+ return ret;
-+
-+ ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port),
-+ QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) |
-+ QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF));
-+ if (ret)
-+ return ret;
- }
-
-+ /* The port 5 of the qca8337 have some problem in flood condition. The
-+ * original legacy driver had some specific buffer and priority settings
-+ * for the different port suggested by the QCA switch team. Add this
-+ * missing settings to improve switch stability under load condition.
-+ * This problem is limited to qca8337 and other qca8k switch are not affected.
-+ */
-+ if (priv->switch_id == QCA8K_ID_QCA8337)
-+ dsa_switch_for_each_available_port(dp, ds)
-+ qca8k_setup_hol_fixup(priv, dp->index);
-+
- /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */
- if (priv->switch_id == QCA8K_ID_QCA8327) {
- mask = QCA8K_GLOBAL_FC_GOL_XON_THRES(288) |
+++ /dev/null
-From 5652d1741574eb89cc02576e50ee3e348bd6dd77 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
-Date: Wed, 4 Oct 2023 11:19:03 +0200
-Subject: [PATCH 1/2] net: dsa: qca8k: fix regmap bulk read/write methods on
- big endian systems
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Commit c766e077d927 ("net: dsa: qca8k: convert to regmap read/write
-API") introduced bulk read/write methods to qca8k's regmap.
-
-The regmap bulk read/write methods get the register address in a buffer
-passed as a void pointer parameter (the same buffer contains also the
-read/written values). The register address occupies only as many bytes
-as it requires at the beginning of this buffer. For example if the
-.reg_bits member in regmap_config is 16 (as is the case for this
-driver), the register address occupies only the first 2 bytes in this
-buffer, so it can be cast to u16.
-
-But the original commit implementing these bulk read/write methods cast
-the buffer to u32:
- u32 reg = *(u32 *)reg_buf & U16_MAX;
-taking the first 4 bytes. This works on little endian systems where the
-first 2 bytes of the buffer correspond to the low 16-bits, but it
-obviously cannot work on big endian systems.
-
-Fix this by casting the beginning of the buffer to u16 as
- u32 reg = *(u16 *)reg_buf;
-
-Fixes: c766e077d927 ("net: dsa: qca8k: convert to regmap read/write API")
-Signed-off-by: Marek Behún <kabel@kernel.org>
-Tested-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/qca/qca8k-8xxx.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
---- a/drivers/net/dsa/qca/qca8k-8xxx.c
-+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
-@@ -504,8 +504,8 @@ qca8k_bulk_read(void *ctx, const void *r
- void *val_buf, size_t val_len)
- {
- int i, count = val_len / sizeof(u32), ret;
-- u32 reg = *(u32 *)reg_buf & U16_MAX;
- struct qca8k_priv *priv = ctx;
-+ u32 reg = *(u16 *)reg_buf;
-
- if (priv->mgmt_master &&
- !qca8k_read_eth(priv, reg, val_buf, val_len))
-@@ -526,8 +526,8 @@ qca8k_bulk_gather_write(void *ctx, const
- const void *val_buf, size_t val_len)
- {
- int i, count = val_len / sizeof(u32), ret;
-- u32 reg = *(u32 *)reg_buf & U16_MAX;
- struct qca8k_priv *priv = ctx;
-+ u32 reg = *(u16 *)reg_buf;
- u32 *val = (u32 *)val_buf;
-
- if (priv->mgmt_master &&
+++ /dev/null
-From patchwork Thu Mar 9 10:57:44 2023
-Content-Type: text/plain; charset="utf-8"
-MIME-Version: 1.0
-Content-Transfer-Encoding: 8bit
-X-Patchwork-Submitter: Daniel Golle <daniel@makrotopia.org>
-X-Patchwork-Id: 13167235
-X-Patchwork-Delegate: kuba@kernel.org
-Return-Path: <netdev-owner@vger.kernel.org>
-Date: Thu, 9 Mar 2023 10:57:44 +0000
-From: Daniel Golle <daniel@makrotopia.org>
-To: netdev@vger.kernel.org, linux-mediatek@lists.infradead.org,
- linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org,
- Russell King <linux@armlinux.org.uk>,
- Heiner Kallweit <hkallweit1@gmail.com>,
- Lorenzo Bianconi <lorenzo@kernel.org>,
- Mark Lee <Mark-MC.Lee@mediatek.com>,
- John Crispin <john@phrozen.org>, Felix Fietkau <nbd@nbd.name>,
- AngeloGioacchino Del Regno
- <angelogioacchino.delregno@collabora.com>,
- Matthias Brugger <matthias.bgg@gmail.com>,
- DENG Qingfang <dqfext@gmail.com>,
- Landen Chao <Landen.Chao@mediatek.com>,
- Sean Wang <sean.wang@mediatek.com>,
- Paolo Abeni <pabeni@redhat.com>,
- Jakub Kicinski <kuba@kernel.org>,
- Eric Dumazet <edumazet@google.com>,
- "David S. Miller" <davem@davemloft.net>,
- Vladimir Oltean <olteanv@gmail.com>,
- Florian Fainelli <f.fainelli@gmail.com>,
- Andrew Lunn <andrew@lunn.ch>,
- Vladimir Oltean <vladimir.oltean@nxp.com>
-Cc: =?iso-8859-1?q?Bj=F8rn?= Mork <bjorn@mork.no>,
- Frank Wunderlich <frank-w@public-files.de>,
- Alexander Couzens <lynxis@fe80.eu>
-Subject: [PATCH net-next v13 11/16] net: dsa: mt7530: use external PCS driver
-Message-ID:
- <2ac2ee40d3b0e705461b50613fda6a7edfdbc4b3.1678357225.git.daniel@makrotopia.org>
-References: <cover.1678357225.git.daniel@makrotopia.org>
-MIME-Version: 1.0
-Content-Disposition: inline
-In-Reply-To: <cover.1678357225.git.daniel@makrotopia.org>
-Precedence: bulk
-List-ID: <netdev.vger.kernel.org>
-X-Mailing-List: netdev@vger.kernel.org
-X-Patchwork-Delegate: kuba@kernel.org
-
-Implement regmap access wrappers, for now only to be used by the
-pcs-mtk driver.
-Make use of external PCS driver and drop the reduntant implementation
-in mt7530.c.
-As a nice side effect the SGMII registers can now also more easily be
-inspected for debugging via /sys/kernel/debug/regmap.
-
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Tested-by: Bjørn Mork <bjorn@mork.no>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Tested-by: Frank Wunderlich <frank-w@public-files.de>
----
- drivers/net/dsa/Kconfig | 1 +
- drivers/net/dsa/mt7530.c | 277 ++++++++++-----------------------------
- drivers/net/dsa/mt7530.h | 47 +------
- 3 files changed, 71 insertions(+), 254 deletions(-)
-
---- a/drivers/net/dsa/Kconfig
-+++ b/drivers/net/dsa/Kconfig
-@@ -37,6 +37,7 @@ config NET_DSA_MT7530
- tristate "MediaTek MT753x and MT7621 Ethernet switch support"
- select NET_DSA_TAG_MTK
- select MEDIATEK_GE_PHY
-+ select PCS_MTK_LYNXI
- help
- This enables support for the MediaTek MT7530, MT7531, and MT7621
- Ethernet switch chips.
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -14,6 +14,7 @@
- #include <linux/of_mdio.h>
- #include <linux/of_net.h>
- #include <linux/of_platform.h>
-+#include <linux/pcs/pcs-mtk-lynxi.h>
- #include <linux/phylink.h>
- #include <linux/regmap.h>
- #include <linux/regulator/consumer.h>
-@@ -2615,128 +2616,11 @@ static int mt7531_rgmii_setup(struct mt7
- return 0;
- }
-
--static void mt7531_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
-- phy_interface_t interface, int speed, int duplex)
--{
-- struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv;
-- int port = pcs_to_mt753x_pcs(pcs)->port;
-- unsigned int val;
--
-- /* For adjusting speed and duplex of SGMII force mode. */
-- if (interface != PHY_INTERFACE_MODE_SGMII ||
-- phylink_autoneg_inband(mode))
-- return;
--
-- /* SGMII force mode setting */
-- val = mt7530_read(priv, MT7531_SGMII_MODE(port));
-- val &= ~MT7531_SGMII_IF_MODE_MASK;
--
-- switch (speed) {
-- case SPEED_10:
-- val |= MT7531_SGMII_FORCE_SPEED_10;
-- break;
-- case SPEED_100:
-- val |= MT7531_SGMII_FORCE_SPEED_100;
-- break;
-- case SPEED_1000:
-- val |= MT7531_SGMII_FORCE_SPEED_1000;
-- break;
-- }
--
-- /* MT7531 SGMII 1G force mode can only work in full duplex mode,
-- * no matter MT7531_SGMII_FORCE_HALF_DUPLEX is set or not.
-- *
-- * The speed check is unnecessary as the MAC capabilities apply
-- * this restriction. --rmk
-- */
-- if ((speed == SPEED_10 || speed == SPEED_100) &&
-- duplex != DUPLEX_FULL)
-- val |= MT7531_SGMII_FORCE_HALF_DUPLEX;
--
-- mt7530_write(priv, MT7531_SGMII_MODE(port), val);
--}
--
- static bool mt753x_is_mac_port(u32 port)
- {
- return (port == 5 || port == 6);
- }
-
--static int mt7531_sgmii_setup_mode_force(struct mt7530_priv *priv, u32 port,
-- phy_interface_t interface)
--{
-- u32 val;
--
-- if (!mt753x_is_mac_port(port))
-- return -EINVAL;
--
-- mt7530_set(priv, MT7531_QPHY_PWR_STATE_CTRL(port),
-- MT7531_SGMII_PHYA_PWD);
--
-- val = mt7530_read(priv, MT7531_PHYA_CTRL_SIGNAL3(port));
-- val &= ~MT7531_RG_TPHY_SPEED_MASK;
-- /* Setup 2.5 times faster clock for 2.5Gbps data speeds with 10B/8B
-- * encoding.
-- */
-- val |= (interface == PHY_INTERFACE_MODE_2500BASEX) ?
-- MT7531_RG_TPHY_SPEED_3_125G : MT7531_RG_TPHY_SPEED_1_25G;
-- mt7530_write(priv, MT7531_PHYA_CTRL_SIGNAL3(port), val);
--
-- mt7530_clear(priv, MT7531_PCS_CONTROL_1(port), MT7531_SGMII_AN_ENABLE);
--
-- /* MT7531 SGMII 1G and 2.5G force mode can only work in full duplex
-- * mode, no matter MT7531_SGMII_FORCE_HALF_DUPLEX is set or not.
-- */
-- mt7530_rmw(priv, MT7531_SGMII_MODE(port),
-- MT7531_SGMII_IF_MODE_MASK | MT7531_SGMII_REMOTE_FAULT_DIS,
-- MT7531_SGMII_FORCE_SPEED_1000);
--
-- mt7530_write(priv, MT7531_QPHY_PWR_STATE_CTRL(port), 0);
--
-- return 0;
--}
--
--static int mt7531_sgmii_setup_mode_an(struct mt7530_priv *priv, int port,
-- phy_interface_t interface)
--{
-- if (!mt753x_is_mac_port(port))
-- return -EINVAL;
--
-- mt7530_set(priv, MT7531_QPHY_PWR_STATE_CTRL(port),
-- MT7531_SGMII_PHYA_PWD);
--
-- mt7530_rmw(priv, MT7531_PHYA_CTRL_SIGNAL3(port),
-- MT7531_RG_TPHY_SPEED_MASK, MT7531_RG_TPHY_SPEED_1_25G);
--
-- mt7530_set(priv, MT7531_SGMII_MODE(port),
-- MT7531_SGMII_REMOTE_FAULT_DIS |
-- MT7531_SGMII_SPEED_DUPLEX_AN);
--
-- mt7530_rmw(priv, MT7531_PCS_SPEED_ABILITY(port),
-- MT7531_SGMII_TX_CONFIG_MASK, 1);
--
-- mt7530_set(priv, MT7531_PCS_CONTROL_1(port), MT7531_SGMII_AN_ENABLE);
--
-- mt7530_set(priv, MT7531_PCS_CONTROL_1(port), MT7531_SGMII_AN_RESTART);
--
-- mt7530_write(priv, MT7531_QPHY_PWR_STATE_CTRL(port), 0);
--
-- return 0;
--}
--
--static void mt7531_pcs_an_restart(struct phylink_pcs *pcs)
--{
-- struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv;
-- int port = pcs_to_mt753x_pcs(pcs)->port;
-- u32 val;
--
-- /* Only restart AN when AN is enabled */
-- val = mt7530_read(priv, MT7531_PCS_CONTROL_1(port));
-- if (val & MT7531_SGMII_AN_ENABLE) {
-- val |= MT7531_SGMII_AN_RESTART;
-- mt7530_write(priv, MT7531_PCS_CONTROL_1(port), val);
-- }
--}
--
- static int
- mt7531_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
- phy_interface_t interface)
-@@ -2759,11 +2643,11 @@ mt7531_mac_config(struct dsa_switch *ds,
- phydev = dp->slave->phydev;
- return mt7531_rgmii_setup(priv, port, interface, phydev);
- case PHY_INTERFACE_MODE_SGMII:
-- return mt7531_sgmii_setup_mode_an(priv, port, interface);
- case PHY_INTERFACE_MODE_NA:
- case PHY_INTERFACE_MODE_1000BASEX:
- case PHY_INTERFACE_MODE_2500BASEX:
-- return mt7531_sgmii_setup_mode_force(priv, port, interface);
-+ /* handled in SGMII PCS driver */
-+ return 0;
- default:
- return -EINVAL;
- }
-@@ -2788,11 +2672,11 @@ mt753x_phylink_mac_select_pcs(struct dsa
-
- switch (interface) {
- case PHY_INTERFACE_MODE_TRGMII:
-+ return &priv->pcs[port].pcs;
- case PHY_INTERFACE_MODE_SGMII:
- case PHY_INTERFACE_MODE_1000BASEX:
- case PHY_INTERFACE_MODE_2500BASEX:
-- return &priv->pcs[port].pcs;
--
-+ return priv->ports[port].sgmii_pcs;
- default:
- return NULL;
- }
-@@ -3033,86 +2917,6 @@ static void mt7530_pcs_get_state(struct
- state->pause |= MLO_PAUSE_TX;
- }
-
--static int
--mt7531_sgmii_pcs_get_state_an(struct mt7530_priv *priv, int port,
-- struct phylink_link_state *state)
--{
-- u32 status, val;
-- u16 config_reg;
--
-- status = mt7530_read(priv, MT7531_PCS_CONTROL_1(port));
-- state->link = !!(status & MT7531_SGMII_LINK_STATUS);
-- state->an_complete = !!(status & MT7531_SGMII_AN_COMPLETE);
-- if (state->interface == PHY_INTERFACE_MODE_SGMII &&
-- (status & MT7531_SGMII_AN_ENABLE)) {
-- val = mt7530_read(priv, MT7531_PCS_SPEED_ABILITY(port));
-- config_reg = val >> 16;
--
-- switch (config_reg & LPA_SGMII_SPD_MASK) {
-- case LPA_SGMII_1000:
-- state->speed = SPEED_1000;
-- break;
-- case LPA_SGMII_100:
-- state->speed = SPEED_100;
-- break;
-- case LPA_SGMII_10:
-- state->speed = SPEED_10;
-- break;
-- default:
-- dev_err(priv->dev, "invalid sgmii PHY speed\n");
-- state->link = false;
-- return -EINVAL;
-- }
--
-- if (config_reg & LPA_SGMII_FULL_DUPLEX)
-- state->duplex = DUPLEX_FULL;
-- else
-- state->duplex = DUPLEX_HALF;
-- }
--
-- return 0;
--}
--
--static void
--mt7531_sgmii_pcs_get_state_inband(struct mt7530_priv *priv, int port,
-- struct phylink_link_state *state)
--{
-- unsigned int val;
--
-- val = mt7530_read(priv, MT7531_PCS_CONTROL_1(port));
-- state->link = !!(val & MT7531_SGMII_LINK_STATUS);
-- if (!state->link)
-- return;
--
-- state->an_complete = state->link;
--
-- if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
-- state->speed = SPEED_2500;
-- else
-- state->speed = SPEED_1000;
--
-- state->duplex = DUPLEX_FULL;
-- state->pause = MLO_PAUSE_NONE;
--}
--
--static void mt7531_pcs_get_state(struct phylink_pcs *pcs,
-- struct phylink_link_state *state)
--{
-- struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv;
-- int port = pcs_to_mt753x_pcs(pcs)->port;
--
-- if (state->interface == PHY_INTERFACE_MODE_SGMII) {
-- mt7531_sgmii_pcs_get_state_an(priv, port, state);
-- return;
-- } else if ((state->interface == PHY_INTERFACE_MODE_1000BASEX) ||
-- (state->interface == PHY_INTERFACE_MODE_2500BASEX)) {
-- mt7531_sgmii_pcs_get_state_inband(priv, port, state);
-- return;
-- }
--
-- state->link = false;
--}
--
- static int mt753x_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
- phy_interface_t interface,
- const unsigned long *advertising,
-@@ -3132,18 +2936,57 @@ static const struct phylink_pcs_ops mt75
- .pcs_an_restart = mt7530_pcs_an_restart,
- };
-
--static const struct phylink_pcs_ops mt7531_pcs_ops = {
-- .pcs_validate = mt753x_pcs_validate,
-- .pcs_get_state = mt7531_pcs_get_state,
-- .pcs_config = mt753x_pcs_config,
-- .pcs_an_restart = mt7531_pcs_an_restart,
-- .pcs_link_up = mt7531_pcs_link_up,
-+static int mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val)
-+{
-+ struct mt7530_priv *priv = context;
-+
-+ *val = mt7530_read(priv, reg);
-+ return 0;
-+};
-+
-+static int mt7530_regmap_write(void *context, unsigned int reg, unsigned int val)
-+{
-+ struct mt7530_priv *priv = context;
-+
-+ mt7530_write(priv, reg, val);
-+ return 0;
-+};
-+
-+static int mt7530_regmap_update_bits(void *context, unsigned int reg,
-+ unsigned int mask, unsigned int val)
-+{
-+ struct mt7530_priv *priv = context;
-+
-+ mt7530_rmw(priv, reg, mask, val);
-+ return 0;
-+};
-+
-+static const struct regmap_bus mt7531_regmap_bus = {
-+ .reg_write = mt7530_regmap_write,
-+ .reg_read = mt7530_regmap_read,
-+ .reg_update_bits = mt7530_regmap_update_bits,
-+};
-+
-+#define MT7531_PCS_REGMAP_CONFIG(_name, _reg_base) \
-+ { \
-+ .name = _name, \
-+ .reg_bits = 16, \
-+ .val_bits = 32, \
-+ .reg_stride = 4, \
-+ .reg_base = _reg_base, \
-+ .max_register = 0x17c, \
-+ }
-+
-+static const struct regmap_config mt7531_pcs_config[] = {
-+ MT7531_PCS_REGMAP_CONFIG("port5", MT7531_SGMII_REG_BASE(5)),
-+ MT7531_PCS_REGMAP_CONFIG("port6", MT7531_SGMII_REG_BASE(6)),
- };
-
- static int
- mt753x_setup(struct dsa_switch *ds)
- {
- struct mt7530_priv *priv = ds->priv;
-+ struct regmap *regmap;
- int i, ret;
-
- /* Initialise the PCS devices */
-@@ -3151,8 +2994,6 @@ mt753x_setup(struct dsa_switch *ds)
- priv->pcs[i].pcs.ops = priv->info->pcs_ops;
- priv->pcs[i].priv = priv;
- priv->pcs[i].port = i;
-- if (mt753x_is_mac_port(i))
-- priv->pcs[i].pcs.poll = 1;
- }
-
- ret = priv->info->sw_setup(ds);
-@@ -3167,6 +3008,16 @@ mt753x_setup(struct dsa_switch *ds)
- if (ret && priv->irq)
- mt7530_free_irq_common(priv);
-
-+ if (priv->id == ID_MT7531)
-+ for (i = 0; i < 2; i++) {
-+ regmap = devm_regmap_init(ds->dev,
-+ &mt7531_regmap_bus, priv,
-+ &mt7531_pcs_config[i]);
-+ priv->ports[5 + i].sgmii_pcs =
-+ mtk_pcs_lynxi_create(ds->dev, regmap,
-+ MT7531_PHYA_CTRL_SIGNAL3, 0);
-+ }
-+
- return ret;
- }
-
-@@ -3258,7 +3109,7 @@ static const struct mt753x_info mt753x_t
- },
- [ID_MT7531] = {
- .id = ID_MT7531,
-- .pcs_ops = &mt7531_pcs_ops,
-+ .pcs_ops = &mt7530_pcs_ops,
- .sw_setup = mt7531_setup,
- .phy_read = mt7531_ind_phy_read,
- .phy_write = mt7531_ind_phy_write,
-@@ -3366,7 +3217,7 @@ static void
- mt7530_remove(struct mdio_device *mdiodev)
- {
- struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev);
-- int ret = 0;
-+ int ret = 0, i;
-
- if (!priv)
- return;
-@@ -3385,6 +3236,10 @@ mt7530_remove(struct mdio_device *mdiode
- mt7530_free_irq(priv);
-
- dsa_unregister_switch(priv->ds);
-+
-+ for (i = 0; i < 2; ++i)
-+ mtk_pcs_lynxi_destroy(priv->ports[5 + i].sgmii_pcs);
-+
- mutex_destroy(&priv->reg_mutex);
- }
-
---- a/drivers/net/dsa/mt7530.h
-+++ b/drivers/net/dsa/mt7530.h
-@@ -371,47 +371,8 @@ enum mt7530_vlan_port_acc_frm {
- CCR_TX_OCT_CNT_BAD)
-
- /* MT7531 SGMII register group */
--#define MT7531_SGMII_REG_BASE 0x5000
--#define MT7531_SGMII_REG(p, r) (MT7531_SGMII_REG_BASE + \
-- ((p) - 5) * 0x1000 + (r))
--
--/* Register forSGMII PCS_CONTROL_1 */
--#define MT7531_PCS_CONTROL_1(p) MT7531_SGMII_REG(p, 0x00)
--#define MT7531_SGMII_LINK_STATUS BIT(18)
--#define MT7531_SGMII_AN_ENABLE BIT(12)
--#define MT7531_SGMII_AN_RESTART BIT(9)
--#define MT7531_SGMII_AN_COMPLETE BIT(21)
--
--/* Register for SGMII PCS_SPPED_ABILITY */
--#define MT7531_PCS_SPEED_ABILITY(p) MT7531_SGMII_REG(p, 0x08)
--#define MT7531_SGMII_TX_CONFIG_MASK GENMASK(15, 0)
--#define MT7531_SGMII_TX_CONFIG BIT(0)
--
--/* Register for SGMII_MODE */
--#define MT7531_SGMII_MODE(p) MT7531_SGMII_REG(p, 0x20)
--#define MT7531_SGMII_REMOTE_FAULT_DIS BIT(8)
--#define MT7531_SGMII_IF_MODE_MASK GENMASK(5, 1)
--#define MT7531_SGMII_FORCE_DUPLEX BIT(4)
--#define MT7531_SGMII_FORCE_SPEED_MASK GENMASK(3, 2)
--#define MT7531_SGMII_FORCE_SPEED_1000 BIT(3)
--#define MT7531_SGMII_FORCE_SPEED_100 BIT(2)
--#define MT7531_SGMII_FORCE_SPEED_10 0
--#define MT7531_SGMII_SPEED_DUPLEX_AN BIT(1)
--
--enum mt7531_sgmii_force_duplex {
-- MT7531_SGMII_FORCE_FULL_DUPLEX = 0,
-- MT7531_SGMII_FORCE_HALF_DUPLEX = 0x10,
--};
--
--/* Fields of QPHY_PWR_STATE_CTRL */
--#define MT7531_QPHY_PWR_STATE_CTRL(p) MT7531_SGMII_REG(p, 0xe8)
--#define MT7531_SGMII_PHYA_PWD BIT(4)
--
--/* Values of SGMII SPEED */
--#define MT7531_PHYA_CTRL_SIGNAL3(p) MT7531_SGMII_REG(p, 0x128)
--#define MT7531_RG_TPHY_SPEED_MASK (BIT(2) | BIT(3))
--#define MT7531_RG_TPHY_SPEED_1_25G 0x0
--#define MT7531_RG_TPHY_SPEED_3_125G BIT(2)
-+#define MT7531_SGMII_REG_BASE(p) (0x5000 + ((p) - 5) * 0x1000)
-+#define MT7531_PHYA_CTRL_SIGNAL3 0x128
-
- /* Register for system reset */
- #define MT7530_SYS_CTRL 0x7000
-@@ -710,13 +671,13 @@ struct mt7530_fdb {
- * @pm: The matrix used to show all connections with the port.
- * @pvid: The VLAN specified is to be considered a PVID at ingress. Any
- * untagged frames will be assigned to the related VLAN.
-- * @vlan_filtering: The flags indicating whether the port that can recognize
-- * VLAN-tagged frames.
-+ * @sgmii_pcs: Pointer to PCS instance for SerDes ports
- */
- struct mt7530_port {
- bool enable;
- u32 pm;
- u16 pvid;
-+ struct phylink_pcs *sgmii_pcs;
- };
-
- /* Port 5 interface select definitions */
+++ /dev/null
-From c3552d3f85f06cf4b4818bd84c4fcc09d8d45165 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Mon, 3 Apr 2023 02:17:19 +0100
-Subject: [PATCH 01/13] net: dsa: mt7530: make some noise if register read
- fails
-
-Simply returning the negative error value instead of the read value
-doesn't seem like a good idea. Return 0 instead and add WARN_ON_ONCE(1)
-so this kind of error will not go unnoticed.
-
-Suggested-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/mt7530.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -224,9 +224,10 @@ mt7530_mii_read(struct mt7530_priv *priv
- /* MT7530 uses 31 as the pseudo port */
- ret = bus->write(bus, 0x1f, 0x1f, page);
- if (ret < 0) {
-+ WARN_ON_ONCE(1);
- dev_err(&bus->dev,
- "failed to read mt7530 register\n");
-- return ret;
-+ return 0;
- }
-
- lo = bus->read(bus, 0x1f, r);
+++ /dev/null
-From b896355fc4988216d4f38582d07add9252a795ae Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Mon, 3 Apr 2023 02:17:30 +0100
-Subject: [PATCH 02/13] net: dsa: mt7530: refactor SGMII PCS creation
-
-Instead of macro templates use a dedidated function and allocated
-regmap_config when creating the regmaps for the pcs-mtk-lynxi
-instances.
-This is in preparation to switching to use unlocked regmap accessors
-and have regmap's locking API handle locking for us.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/mt7530.c | 74 +++++++++++++++++++++++++++-------------
- 1 file changed, 50 insertions(+), 24 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -2968,26 +2968,56 @@ static const struct regmap_bus mt7531_re
- .reg_update_bits = mt7530_regmap_update_bits,
- };
-
--#define MT7531_PCS_REGMAP_CONFIG(_name, _reg_base) \
-- { \
-- .name = _name, \
-- .reg_bits = 16, \
-- .val_bits = 32, \
-- .reg_stride = 4, \
-- .reg_base = _reg_base, \
-- .max_register = 0x17c, \
-+static int
-+mt7531_create_sgmii(struct mt7530_priv *priv)
-+{
-+ struct regmap_config *mt7531_pcs_config[2];
-+ struct phylink_pcs *pcs;
-+ struct regmap *regmap;
-+ int i, ret = 0;
-+
-+ for (i = 0; i < 2; i++) {
-+ mt7531_pcs_config[i] = devm_kzalloc(priv->dev,
-+ sizeof(struct regmap_config),
-+ GFP_KERNEL);
-+ if (!mt7531_pcs_config[i]) {
-+ ret = -ENOMEM;
-+ break;
-+ }
-+
-+ mt7531_pcs_config[i]->name = i ? "port6" : "port5";
-+ mt7531_pcs_config[i]->reg_bits = 16;
-+ mt7531_pcs_config[i]->val_bits = 32;
-+ mt7531_pcs_config[i]->reg_stride = 4;
-+ mt7531_pcs_config[i]->reg_base = MT7531_SGMII_REG_BASE(5 + i);
-+ mt7531_pcs_config[i]->max_register = 0x17c;
-+
-+ regmap = devm_regmap_init(priv->dev,
-+ &mt7531_regmap_bus, priv,
-+ mt7531_pcs_config[i]);
-+ if (IS_ERR(regmap)) {
-+ ret = PTR_ERR(regmap);
-+ break;
-+ }
-+ pcs = mtk_pcs_lynxi_create(priv->dev, regmap,
-+ MT7531_PHYA_CTRL_SIGNAL3, 0);
-+ if (!pcs) {
-+ ret = -ENXIO;
-+ break;
-+ }
-+ priv->ports[5 + i].sgmii_pcs = pcs;
- }
-
--static const struct regmap_config mt7531_pcs_config[] = {
-- MT7531_PCS_REGMAP_CONFIG("port5", MT7531_SGMII_REG_BASE(5)),
-- MT7531_PCS_REGMAP_CONFIG("port6", MT7531_SGMII_REG_BASE(6)),
--};
-+ if (ret && i)
-+ mtk_pcs_lynxi_destroy(priv->ports[5].sgmii_pcs);
-+
-+ return ret;
-+}
-
- static int
- mt753x_setup(struct dsa_switch *ds)
- {
- struct mt7530_priv *priv = ds->priv;
-- struct regmap *regmap;
- int i, ret;
-
- /* Initialise the PCS devices */
-@@ -3009,15 +3039,11 @@ mt753x_setup(struct dsa_switch *ds)
- if (ret && priv->irq)
- mt7530_free_irq_common(priv);
-
-- if (priv->id == ID_MT7531)
-- for (i = 0; i < 2; i++) {
-- regmap = devm_regmap_init(ds->dev,
-- &mt7531_regmap_bus, priv,
-- &mt7531_pcs_config[i]);
-- priv->ports[5 + i].sgmii_pcs =
-- mtk_pcs_lynxi_create(ds->dev, regmap,
-- MT7531_PHYA_CTRL_SIGNAL3, 0);
-- }
-+ if (priv->id == ID_MT7531) {
-+ ret = mt7531_create_sgmii(priv);
-+ if (ret && priv->irq)
-+ mt7530_free_irq_common(priv);
-+ }
-
- return ret;
- }
+++ /dev/null
-From 33396408776385f3d2f6069646169a6b5b28e3b3 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Mon, 3 Apr 2023 02:17:40 +0100
-Subject: [PATCH 03/13] net: dsa: mt7530: use unlocked regmap accessors
-
-Instead of wrapping the locked register accessor functions, use the
-unlocked variants and add locking wrapper functions to let regmap
-handle the locking.
-
-This is a preparation towards being able to always use regmap to
-access switch registers instead of open-coded accessor functions.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/mt7530.c | 23 ++++++++++++++---------
- 1 file changed, 14 insertions(+), 9 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -2941,7 +2941,7 @@ static int mt7530_regmap_read(void *cont
- {
- struct mt7530_priv *priv = context;
-
-- *val = mt7530_read(priv, reg);
-+ *val = mt7530_mii_read(priv, reg);
- return 0;
- };
-
-@@ -2949,23 +2949,25 @@ static int mt7530_regmap_write(void *con
- {
- struct mt7530_priv *priv = context;
-
-- mt7530_write(priv, reg, val);
-+ mt7530_mii_write(priv, reg, val);
- return 0;
- };
-
--static int mt7530_regmap_update_bits(void *context, unsigned int reg,
-- unsigned int mask, unsigned int val)
-+static void
-+mt7530_mdio_regmap_lock(void *mdio_lock)
- {
-- struct mt7530_priv *priv = context;
-+ mutex_lock_nested(mdio_lock, MDIO_MUTEX_NESTED);
-+}
-
-- mt7530_rmw(priv, reg, mask, val);
-- return 0;
--};
-+static void
-+mt7530_mdio_regmap_unlock(void *mdio_lock)
-+{
-+ mutex_unlock(mdio_lock);
-+}
-
- static const struct regmap_bus mt7531_regmap_bus = {
- .reg_write = mt7530_regmap_write,
- .reg_read = mt7530_regmap_read,
-- .reg_update_bits = mt7530_regmap_update_bits,
- };
-
- static int
-@@ -2991,6 +2993,9 @@ mt7531_create_sgmii(struct mt7530_priv *
- mt7531_pcs_config[i]->reg_stride = 4;
- mt7531_pcs_config[i]->reg_base = MT7531_SGMII_REG_BASE(5 + i);
- mt7531_pcs_config[i]->max_register = 0x17c;
-+ mt7531_pcs_config[i]->lock = mt7530_mdio_regmap_lock;
-+ mt7531_pcs_config[i]->unlock = mt7530_mdio_regmap_unlock;
-+ mt7531_pcs_config[i]->lock_arg = &priv->bus->mdio_lock;
-
- regmap = devm_regmap_init(priv->dev,
- &mt7531_regmap_bus, priv,
+++ /dev/null
-From 743cba4345cb366248f9d375c6a9e50243dc0677 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Mon, 3 Apr 2023 02:17:52 +0100
-Subject: [PATCH 04/13] net: dsa: mt7530: use regmap to access switch register
- space
-
-Use regmap API to access the switch register space.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/mt7530.c | 99 ++++++++++++++++++++++++----------------
- drivers/net/dsa/mt7530.h | 2 +
- 2 files changed, 62 insertions(+), 39 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -183,9 +183,9 @@ core_clear(struct mt7530_priv *priv, u32
- }
-
- static int
--mt7530_mii_write(struct mt7530_priv *priv, u32 reg, u32 val)
-+mt7530_regmap_write(void *context, unsigned int reg, unsigned int val)
- {
-- struct mii_bus *bus = priv->bus;
-+ struct mii_bus *bus = context;
- u16 page, r, lo, hi;
- int ret;
-
-@@ -197,24 +197,34 @@ mt7530_mii_write(struct mt7530_priv *pri
- /* MT7530 uses 31 as the pseudo port */
- ret = bus->write(bus, 0x1f, 0x1f, page);
- if (ret < 0)
-- goto err;
-+ return ret;
-
- ret = bus->write(bus, 0x1f, r, lo);
- if (ret < 0)
-- goto err;
-+ return ret;
-
- ret = bus->write(bus, 0x1f, 0x10, hi);
--err:
-+ return ret;
-+}
-+
-+static int
-+mt7530_mii_write(struct mt7530_priv *priv, u32 reg, u32 val)
-+{
-+ int ret;
-+
-+ ret = regmap_write(priv->regmap, reg, val);
-+
- if (ret < 0)
-- dev_err(&bus->dev,
-+ dev_err(priv->dev,
- "failed to write mt7530 register\n");
-+
- return ret;
- }
-
--static u32
--mt7530_mii_read(struct mt7530_priv *priv, u32 reg)
-+static int
-+mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val)
- {
-- struct mii_bus *bus = priv->bus;
-+ struct mii_bus *bus = context;
- u16 page, r, lo, hi;
- int ret;
-
-@@ -223,17 +233,32 @@ mt7530_mii_read(struct mt7530_priv *priv
-
- /* MT7530 uses 31 as the pseudo port */
- ret = bus->write(bus, 0x1f, 0x1f, page);
-- if (ret < 0) {
-+ if (ret < 0)
-+ return ret;
-+
-+ lo = bus->read(bus, 0x1f, r);
-+ hi = bus->read(bus, 0x1f, 0x10);
-+
-+ *val = (hi << 16) | (lo & 0xffff);
-+
-+ return 0;
-+}
-+
-+static u32
-+mt7530_mii_read(struct mt7530_priv *priv, u32 reg)
-+{
-+ int ret;
-+ u32 val;
-+
-+ ret = regmap_read(priv->regmap, reg, &val);
-+ if (ret) {
- WARN_ON_ONCE(1);
-- dev_err(&bus->dev,
-+ dev_err(priv->dev,
- "failed to read mt7530 register\n");
- return 0;
- }
-
-- lo = bus->read(bus, 0x1f, r);
-- hi = bus->read(bus, 0x1f, 0x10);
--
-- return (hi << 16) | (lo & 0xffff);
-+ return val;
- }
-
- static void
-@@ -283,14 +308,10 @@ mt7530_rmw(struct mt7530_priv *priv, u32
- u32 mask, u32 set)
- {
- struct mii_bus *bus = priv->bus;
-- u32 val;
-
- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
-
-- val = mt7530_mii_read(priv, reg);
-- val &= ~mask;
-- val |= set;
-- mt7530_mii_write(priv, reg, val);
-+ regmap_update_bits(priv->regmap, reg, mask, set);
-
- mutex_unlock(&bus->mdio_lock);
- }
-@@ -298,7 +319,7 @@ mt7530_rmw(struct mt7530_priv *priv, u32
- static void
- mt7530_set(struct mt7530_priv *priv, u32 reg, u32 val)
- {
-- mt7530_rmw(priv, reg, 0, val);
-+ mt7530_rmw(priv, reg, val, val);
- }
-
- static void
-@@ -2937,22 +2958,6 @@ static const struct phylink_pcs_ops mt75
- .pcs_an_restart = mt7530_pcs_an_restart,
- };
-
--static int mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val)
--{
-- struct mt7530_priv *priv = context;
--
-- *val = mt7530_mii_read(priv, reg);
-- return 0;
--};
--
--static int mt7530_regmap_write(void *context, unsigned int reg, unsigned int val)
--{
-- struct mt7530_priv *priv = context;
--
-- mt7530_mii_write(priv, reg, val);
-- return 0;
--};
--
- static void
- mt7530_mdio_regmap_lock(void *mdio_lock)
- {
-@@ -2965,7 +2970,7 @@ mt7530_mdio_regmap_unlock(void *mdio_loc
- mutex_unlock(mdio_lock);
- }
-
--static const struct regmap_bus mt7531_regmap_bus = {
-+static const struct regmap_bus mt7530_regmap_bus = {
- .reg_write = mt7530_regmap_write,
- .reg_read = mt7530_regmap_read,
- };
-@@ -2998,7 +3003,7 @@ mt7531_create_sgmii(struct mt7530_priv *
- mt7531_pcs_config[i]->lock_arg = &priv->bus->mdio_lock;
-
- regmap = devm_regmap_init(priv->dev,
-- &mt7531_regmap_bus, priv,
-+ &mt7530_regmap_bus, priv->bus,
- mt7531_pcs_config[i]);
- if (IS_ERR(regmap)) {
- ret = PTR_ERR(regmap);
-@@ -3163,6 +3168,7 @@ MODULE_DEVICE_TABLE(of, mt7530_of_match)
- static int
- mt7530_probe(struct mdio_device *mdiodev)
- {
-+ static struct regmap_config *regmap_config;
- struct mt7530_priv *priv;
- struct device_node *dn;
-
-@@ -3242,6 +3248,21 @@ mt7530_probe(struct mdio_device *mdiodev
- mutex_init(&priv->reg_mutex);
- dev_set_drvdata(&mdiodev->dev, priv);
-
-+ regmap_config = devm_kzalloc(&mdiodev->dev, sizeof(*regmap_config),
-+ GFP_KERNEL);
-+ if (!regmap_config)
-+ return -ENOMEM;
-+
-+ regmap_config->reg_bits = 16;
-+ regmap_config->val_bits = 32;
-+ regmap_config->reg_stride = 4;
-+ regmap_config->max_register = MT7530_CREV;
-+ regmap_config->disable_locking = true;
-+ priv->regmap = devm_regmap_init(priv->dev, &mt7530_regmap_bus,
-+ priv->bus, regmap_config);
-+ if (IS_ERR(priv->regmap))
-+ return PTR_ERR(priv->regmap);
-+
- return dsa_register_switch(priv->ds);
- }
-
---- a/drivers/net/dsa/mt7530.h
-+++ b/drivers/net/dsa/mt7530.h
-@@ -754,6 +754,7 @@ struct mt753x_info {
- * @dev: The device pointer
- * @ds: The pointer to the dsa core structure
- * @bus: The bus used for the device and built-in PHY
-+ * @regmap: The regmap instance representing all switch registers
- * @rstc: The pointer to reset control used by MCM
- * @core_pwr: The power supplied into the core
- * @io_pwr: The power supplied into the I/O
-@@ -774,6 +775,7 @@ struct mt7530_priv {
- struct device *dev;
- struct dsa_switch *ds;
- struct mii_bus *bus;
-+ struct regmap *regmap;
- struct reset_control *rstc;
- struct regulator *core_pwr;
- struct regulator *io_pwr;
+++ /dev/null
-From f3cf1d06e2aef644b426c23b4bb570780b1f8d47 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Mon, 3 Apr 2023 02:18:04 +0100
-Subject: [PATCH 05/13] net: dsa: mt7530: move SGMII PCS creation to
- mt7530_probe function
-
-Move creating the SGMII PCS from mt753x_setup() to the more appropriate
-mt7530_probe() function.
-This is done also in preparation of moving all functions related to
-MDIO-connected MT753x switches to a separate module.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/mt7530.c | 13 +++++++------
- 1 file changed, 7 insertions(+), 6 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -3049,12 +3049,6 @@ mt753x_setup(struct dsa_switch *ds)
- if (ret && priv->irq)
- mt7530_free_irq_common(priv);
-
-- if (priv->id == ID_MT7531) {
-- ret = mt7531_create_sgmii(priv);
-- if (ret && priv->irq)
-- mt7530_free_irq_common(priv);
-- }
--
- return ret;
- }
-
-@@ -3171,6 +3165,7 @@ mt7530_probe(struct mdio_device *mdiodev
- static struct regmap_config *regmap_config;
- struct mt7530_priv *priv;
- struct device_node *dn;
-+ int ret;
-
- dn = mdiodev->dev.of_node;
-
-@@ -3263,6 +3258,12 @@ mt7530_probe(struct mdio_device *mdiodev
- if (IS_ERR(priv->regmap))
- return PTR_ERR(priv->regmap);
-
-+ if (priv->id == ID_MT7531) {
-+ ret = mt7531_create_sgmii(priv);
-+ if (ret)
-+ return ret;
-+ }
-+
- return dsa_register_switch(priv->ds);
- }
-
+++ /dev/null
-From e4729ae7c095c0c87794bff47ea43e35d69de986 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Mon, 3 Apr 2023 02:18:16 +0100
-Subject: [PATCH 06/13] net: dsa: mt7530: introduce mutex helpers
-
-As the MDIO bus lock only needs to be involved if actually operating
-on an MDIO-connected switch we will need to skip locking for built-in
-switches which are accessed via MMIO.
-Create helper functions which simplify that upcoming change.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/mt7530.c | 73 ++++++++++++++++++++--------------------
- 1 file changed, 36 insertions(+), 37 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -143,31 +143,40 @@ err:
- }
-
- static void
--core_write(struct mt7530_priv *priv, u32 reg, u32 val)
-+mt7530_mutex_lock(struct mt7530_priv *priv)
- {
-- struct mii_bus *bus = priv->bus;
-+ mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
-+}
-
-- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
-+static void
-+mt7530_mutex_unlock(struct mt7530_priv *priv)
-+{
-+ mutex_unlock(&priv->bus->mdio_lock);
-+}
-+
-+static void
-+core_write(struct mt7530_priv *priv, u32 reg, u32 val)
-+{
-+ mt7530_mutex_lock(priv);
-
- core_write_mmd_indirect(priv, reg, MDIO_MMD_VEND2, val);
-
-- mutex_unlock(&bus->mdio_lock);
-+ mt7530_mutex_unlock(priv);
- }
-
- static void
- core_rmw(struct mt7530_priv *priv, u32 reg, u32 mask, u32 set)
- {
-- struct mii_bus *bus = priv->bus;
- u32 val;
-
-- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
-+ mt7530_mutex_lock(priv);
-
- val = core_read_mmd_indirect(priv, reg, MDIO_MMD_VEND2);
- val &= ~mask;
- val |= set;
- core_write_mmd_indirect(priv, reg, MDIO_MMD_VEND2, val);
-
-- mutex_unlock(&bus->mdio_lock);
-+ mt7530_mutex_unlock(priv);
- }
-
- static void
-@@ -264,13 +273,11 @@ mt7530_mii_read(struct mt7530_priv *priv
- static void
- mt7530_write(struct mt7530_priv *priv, u32 reg, u32 val)
- {
-- struct mii_bus *bus = priv->bus;
--
-- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
-+ mt7530_mutex_lock(priv);
-
- mt7530_mii_write(priv, reg, val);
-
-- mutex_unlock(&bus->mdio_lock);
-+ mt7530_mutex_unlock(priv);
- }
-
- static u32
-@@ -282,14 +289,13 @@ _mt7530_unlocked_read(struct mt7530_dumm
- static u32
- _mt7530_read(struct mt7530_dummy_poll *p)
- {
-- struct mii_bus *bus = p->priv->bus;
- u32 val;
-
-- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
-+ mt7530_mutex_lock(p->priv);
-
- val = mt7530_mii_read(p->priv, p->reg);
-
-- mutex_unlock(&bus->mdio_lock);
-+ mt7530_mutex_unlock(p->priv);
-
- return val;
- }
-@@ -307,13 +313,11 @@ static void
- mt7530_rmw(struct mt7530_priv *priv, u32 reg,
- u32 mask, u32 set)
- {
-- struct mii_bus *bus = priv->bus;
--
-- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
-+ mt7530_mutex_lock(priv);
-
- regmap_update_bits(priv->regmap, reg, mask, set);
-
-- mutex_unlock(&bus->mdio_lock);
-+ mt7530_mutex_unlock(priv);
- }
-
- static void
-@@ -645,14 +649,13 @@ static int
- mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad,
- int regnum)
- {
-- struct mii_bus *bus = priv->bus;
- struct mt7530_dummy_poll p;
- u32 reg, val;
- int ret;
-
- INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
-
-- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
-+ mt7530_mutex_lock(priv);
-
- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
- !(val & MT7531_PHY_ACS_ST), 20, 100000);
-@@ -685,7 +688,7 @@ mt7531_ind_c45_phy_read(struct mt7530_pr
-
- ret = val & MT7531_MDIO_RW_DATA_MASK;
- out:
-- mutex_unlock(&bus->mdio_lock);
-+ mt7530_mutex_unlock(priv);
-
- return ret;
- }
-@@ -694,14 +697,13 @@ static int
- mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,
- int regnum, u32 data)
- {
-- struct mii_bus *bus = priv->bus;
- struct mt7530_dummy_poll p;
- u32 val, reg;
- int ret;
-
- INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
-
-- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
-+ mt7530_mutex_lock(priv);
-
- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
- !(val & MT7531_PHY_ACS_ST), 20, 100000);
-@@ -733,7 +735,7 @@ mt7531_ind_c45_phy_write(struct mt7530_p
- }
-
- out:
-- mutex_unlock(&bus->mdio_lock);
-+ mt7530_mutex_unlock(priv);
-
- return ret;
- }
-@@ -741,14 +743,13 @@ out:
- static int
- mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum)
- {
-- struct mii_bus *bus = priv->bus;
- struct mt7530_dummy_poll p;
- int ret;
- u32 val;
-
- INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
-
-- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
-+ mt7530_mutex_lock(priv);
-
- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
- !(val & MT7531_PHY_ACS_ST), 20, 100000);
-@@ -771,7 +772,7 @@ mt7531_ind_c22_phy_read(struct mt7530_pr
-
- ret = val & MT7531_MDIO_RW_DATA_MASK;
- out:
-- mutex_unlock(&bus->mdio_lock);
-+ mt7530_mutex_unlock(priv);
-
- return ret;
- }
-@@ -780,14 +781,13 @@ static int
- mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum,
- u16 data)
- {
-- struct mii_bus *bus = priv->bus;
- struct mt7530_dummy_poll p;
- int ret;
- u32 reg;
-
- INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
-
-- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
-+ mt7530_mutex_lock(priv);
-
- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg,
- !(reg & MT7531_PHY_ACS_ST), 20, 100000);
-@@ -809,7 +809,7 @@ mt7531_ind_c22_phy_write(struct mt7530_p
- }
-
- out:
-- mutex_unlock(&bus->mdio_lock);
-+ mt7530_mutex_unlock(priv);
-
- return ret;
- }
-@@ -1125,7 +1125,6 @@ static int
- mt7530_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
- {
- struct mt7530_priv *priv = ds->priv;
-- struct mii_bus *bus = priv->bus;
- int length;
- u32 val;
-
-@@ -1136,7 +1135,7 @@ mt7530_port_change_mtu(struct dsa_switch
- if (!dsa_is_cpu_port(ds, port))
- return 0;
-
-- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
-+ mt7530_mutex_lock(priv);
-
- val = mt7530_mii_read(priv, MT7530_GMACCR);
- val &= ~MAX_RX_PKT_LEN_MASK;
-@@ -1157,7 +1156,7 @@ mt7530_port_change_mtu(struct dsa_switch
-
- mt7530_mii_write(priv, MT7530_GMACCR, val);
-
-- mutex_unlock(&bus->mdio_lock);
-+ mt7530_mutex_unlock(priv);
-
- return 0;
- }
-@@ -1958,10 +1957,10 @@ mt7530_irq_thread_fn(int irq, void *dev_
- u32 val;
- int p;
-
-- mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
-+ mt7530_mutex_lock(priv);
- val = mt7530_mii_read(priv, MT7530_SYS_INT_STS);
- mt7530_mii_write(priv, MT7530_SYS_INT_STS, val);
-- mutex_unlock(&priv->bus->mdio_lock);
-+ mt7530_mutex_unlock(priv);
-
- for (p = 0; p < MT7530_NUM_PHYS; p++) {
- if (BIT(p) & val) {
-@@ -1997,7 +1996,7 @@ mt7530_irq_bus_lock(struct irq_data *d)
- {
- struct mt7530_priv *priv = irq_data_get_irq_chip_data(d);
-
-- mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
-+ mt7530_mutex_lock(priv);
- }
-
- static void
-@@ -2006,7 +2005,7 @@ mt7530_irq_bus_sync_unlock(struct irq_da
- struct mt7530_priv *priv = irq_data_get_irq_chip_data(d);
-
- mt7530_mii_write(priv, MT7530_SYS_INT_EN, priv->irq_enable);
-- mutex_unlock(&priv->bus->mdio_lock);
-+ mt7530_mutex_unlock(priv);
- }
-
- static struct irq_chip mt7530_irq_chip = {
+++ /dev/null
-From 0d7ae94a0c581f86939bebec0b6ccd66e640d1d8 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Mon, 3 Apr 2023 02:18:28 +0100
-Subject: [PATCH 07/13] net: dsa: mt7530: move p5_intf_modes() function to
- mt7530.c
-
-In preparation of splitting mt7530.c into a driver for MDIO-connected
-as well as MDIO-accessed built-in switches on one hand and MMIO-accessed
-built-in switches move the p5_inft_modes() function from mt7530.h to
-mt7530.c. The function is only needed there and will trigger a compiler
-warning about a defined but unused function otherwise when including
-mt7530.h in the to-be-introduced bus-specific drivers.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/mt7530.c | 18 ++++++++++++++++++
- drivers/net/dsa/mt7530.h | 18 ------------------
- 2 files changed, 18 insertions(+), 18 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -950,6 +950,24 @@ mt7530_set_ageing_time(struct dsa_switch
- return 0;
- }
-
-+static const char *p5_intf_modes(unsigned int p5_interface)
-+{
-+ switch (p5_interface) {
-+ case P5_DISABLED:
-+ return "DISABLED";
-+ case P5_INTF_SEL_PHY_P0:
-+ return "PHY P0";
-+ case P5_INTF_SEL_PHY_P4:
-+ return "PHY P4";
-+ case P5_INTF_SEL_GMAC5:
-+ return "GMAC5";
-+ case P5_INTF_SEL_GMAC5_SGMII:
-+ return "GMAC5_SGMII";
-+ default:
-+ return "unknown";
-+ }
-+}
-+
- static void mt7530_setup_port5(struct dsa_switch *ds, phy_interface_t interface)
- {
- struct mt7530_priv *priv = ds->priv;
---- a/drivers/net/dsa/mt7530.h
-+++ b/drivers/net/dsa/mt7530.h
-@@ -689,24 +689,6 @@ enum p5_interface_select {
- P5_INTF_SEL_GMAC5_SGMII,
- };
-
--static const char *p5_intf_modes(unsigned int p5_interface)
--{
-- switch (p5_interface) {
-- case P5_DISABLED:
-- return "DISABLED";
-- case P5_INTF_SEL_PHY_P0:
-- return "PHY P0";
-- case P5_INTF_SEL_PHY_P4:
-- return "PHY P4";
-- case P5_INTF_SEL_GMAC5:
-- return "GMAC5";
-- case P5_INTF_SEL_GMAC5_SGMII:
-- return "GMAC5_SGMII";
-- default:
-- return "unknown";
-- }
--}
--
- struct mt7530_priv;
-
- struct mt753x_pcs {
+++ /dev/null
-From 4d632005c90e253c000d0db73b7cdb9d8dc2e2dd Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Mon, 3 Apr 2023 02:18:39 +0100
-Subject: [PATCH 08/13] net: dsa: mt7530: introduce mt7530_probe_common helper
- function
-
-Move commonly used parts from mt7530_probe into new mt7530_probe_common
-helper function which will be used by both, mt7530_probe and the
-to-be-introduced mt7988_probe.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/mt7530.c | 98 ++++++++++++++++++++++------------------
- 1 file changed, 54 insertions(+), 44 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -3177,44 +3177,21 @@ static const struct of_device_id mt7530_
- MODULE_DEVICE_TABLE(of, mt7530_of_match);
-
- static int
--mt7530_probe(struct mdio_device *mdiodev)
-+mt7530_probe_common(struct mt7530_priv *priv)
- {
-- static struct regmap_config *regmap_config;
-- struct mt7530_priv *priv;
-- struct device_node *dn;
-- int ret;
-+ struct device *dev = priv->dev;
-
-- dn = mdiodev->dev.of_node;
--
-- priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL);
-- if (!priv)
-- return -ENOMEM;
--
-- priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL);
-+ priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL);
- if (!priv->ds)
- return -ENOMEM;
-
-- priv->ds->dev = &mdiodev->dev;
-+ priv->ds->dev = dev;
- priv->ds->num_ports = MT7530_NUM_PORTS;
-
-- /* Use medatek,mcm property to distinguish hardware type that would
-- * casues a little bit differences on power-on sequence.
-- */
-- priv->mcm = of_property_read_bool(dn, "mediatek,mcm");
-- if (priv->mcm) {
-- dev_info(&mdiodev->dev, "MT7530 adapts as multi-chip module\n");
--
-- priv->rstc = devm_reset_control_get(&mdiodev->dev, "mcm");
-- if (IS_ERR(priv->rstc)) {
-- dev_err(&mdiodev->dev, "Couldn't get our reset line\n");
-- return PTR_ERR(priv->rstc);
-- }
-- }
--
- /* Get the hardware identifier from the devicetree node.
- * We will need it for some of the clock and regulator setup.
- */
-- priv->info = of_device_get_match_data(&mdiodev->dev);
-+ priv->info = of_device_get_match_data(dev);
- if (!priv->info)
- return -EINVAL;
-
-@@ -3228,23 +3205,53 @@ mt7530_probe(struct mdio_device *mdiodev
- return -EINVAL;
-
- priv->id = priv->info->id;
-+ priv->dev = dev;
-+ priv->ds->priv = priv;
-+ priv->ds->ops = &mt7530_switch_ops;
-+ mutex_init(&priv->reg_mutex);
-+ dev_set_drvdata(dev, priv);
-
-- if (priv->id == ID_MT7530) {
-- priv->core_pwr = devm_regulator_get(&mdiodev->dev, "core");
-- if (IS_ERR(priv->core_pwr))
-- return PTR_ERR(priv->core_pwr);
-+ return 0;
-+}
-
-- priv->io_pwr = devm_regulator_get(&mdiodev->dev, "io");
-- if (IS_ERR(priv->io_pwr))
-- return PTR_ERR(priv->io_pwr);
-- }
-+static int
-+mt7530_probe(struct mdio_device *mdiodev)
-+{
-+ static struct regmap_config *regmap_config;
-+ struct mt7530_priv *priv;
-+ struct device_node *dn;
-+ int ret;
-+
-+ dn = mdiodev->dev.of_node;
-+
-+ priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL);
-+ if (!priv)
-+ return -ENOMEM;
-
-- /* Not MCM that indicates switch works as the remote standalone
-+ priv->bus = mdiodev->bus;
-+ priv->dev = &mdiodev->dev;
-+
-+ ret = mt7530_probe_common(priv);
-+ if (ret)
-+ return ret;
-+
-+ /* Use medatek,mcm property to distinguish hardware type that would
-+ * cause a little bit differences on power-on sequence.
-+ * Not MCM that indicates switch works as the remote standalone
- * integrated circuit so the GPIO pin would be used to complete
- * the reset, otherwise memory-mapped register accessing used
- * through syscon provides in the case of MCM.
- */
-- if (!priv->mcm) {
-+ priv->mcm = of_property_read_bool(dn, "mediatek,mcm");
-+ if (priv->mcm) {
-+ dev_info(&mdiodev->dev, "MT7530 adapts as multi-chip module\n");
-+
-+ priv->rstc = devm_reset_control_get(&mdiodev->dev, "mcm");
-+ if (IS_ERR(priv->rstc)) {
-+ dev_err(&mdiodev->dev, "Couldn't get our reset line\n");
-+ return PTR_ERR(priv->rstc);
-+ }
-+ } else {
- priv->reset = devm_gpiod_get_optional(&mdiodev->dev, "reset",
- GPIOD_OUT_LOW);
- if (IS_ERR(priv->reset)) {
-@@ -3253,12 +3260,15 @@ mt7530_probe(struct mdio_device *mdiodev
- }
- }
-
-- priv->bus = mdiodev->bus;
-- priv->dev = &mdiodev->dev;
-- priv->ds->priv = priv;
-- priv->ds->ops = &mt7530_switch_ops;
-- mutex_init(&priv->reg_mutex);
-- dev_set_drvdata(&mdiodev->dev, priv);
-+ if (priv->id == ID_MT7530) {
-+ priv->core_pwr = devm_regulator_get(&mdiodev->dev, "core");
-+ if (IS_ERR(priv->core_pwr))
-+ return PTR_ERR(priv->core_pwr);
-+
-+ priv->io_pwr = devm_regulator_get(&mdiodev->dev, "io");
-+ if (IS_ERR(priv->io_pwr))
-+ return PTR_ERR(priv->io_pwr);
-+ }
-
- regmap_config = devm_kzalloc(&mdiodev->dev, sizeof(*regmap_config),
- GFP_KERNEL);
+++ /dev/null
-From 69b838d2629e6b82bcd9e0ab3c1c03f46e5e01d3 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Mon, 3 Apr 2023 02:18:50 +0100
-Subject: [PATCH 09/13] net: dsa: mt7530: introduce mt7530_remove_common helper
- function
-
-Move commonly used parts from mt7530_remove into new
-mt7530_remove_common helper function which will be used by both,
-mt7530_remove and the to-be-introduced mt7988_remove.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/mt7530.c | 18 ++++++++++++------
- 1 file changed, 12 insertions(+), 6 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -3295,6 +3295,17 @@ mt7530_probe(struct mdio_device *mdiodev
- }
-
- static void
-+mt7530_remove_common(struct mt7530_priv *priv)
-+{
-+ if (priv->irq)
-+ mt7530_free_irq(priv);
-+
-+ dsa_unregister_switch(priv->ds);
-+
-+ mutex_destroy(&priv->reg_mutex);
-+}
-+
-+static void
- mt7530_remove(struct mdio_device *mdiodev)
- {
- struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev);
-@@ -3313,15 +3324,10 @@ mt7530_remove(struct mdio_device *mdiode
- dev_err(priv->dev, "Failed to disable io pwr: %d\n",
- ret);
-
-- if (priv->irq)
-- mt7530_free_irq(priv);
--
-- dsa_unregister_switch(priv->ds);
-+ mt7530_remove_common(priv);
-
- for (i = 0; i < 2; ++i)
- mtk_pcs_lynxi_destroy(priv->ports[5 + i].sgmii_pcs);
--
-- mutex_destroy(&priv->reg_mutex);
- }
-
- static void mt7530_shutdown(struct mdio_device *mdiodev)
+++ /dev/null
-From 8eceed6dbd74067dbf4d8e39f14734f4d2f35176 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Mon, 3 Apr 2023 02:19:13 +0100
-Subject: [PATCH 10/13] net: dsa: mt7530: introduce separate MDIO driver
-
-Split MT7530 switch driver into a common part and a part specific
-for MDIO connected switches and multi-chip modules.
-Move MDIO-specific functions to newly introduced mt7530-mdio.c while
-keeping the common parts in mt7530.c.
-Introduce new Kconfig symbol CONFIG_NET_DSA_MT7530_MDIO which is
-implied by CONFIG_NET_DSA_MT7530.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- MAINTAINERS | 1 +
- drivers/net/dsa/Kconfig | 16 +-
- drivers/net/dsa/Makefile | 1 +
- drivers/net/dsa/mt7530-mdio.c | 271 ++++++++++++++++++++++++++++++++++
- drivers/net/dsa/mt7530.c | 264 +--------------------------------
- drivers/net/dsa/mt7530.h | 6 +
- 6 files changed, 301 insertions(+), 258 deletions(-)
- create mode 100644 drivers/net/dsa/mt7530-mdio.c
-
---- a/MAINTAINERS
-+++ b/MAINTAINERS
-@@ -13062,6 +13062,7 @@ M: Landen Chao <Landen.Chao@mediatek.com
- M: DENG Qingfang <dqfext@gmail.com>
- L: netdev@vger.kernel.org
- S: Maintained
-+F: drivers/net/dsa/mt7530-mdio.c
- F: drivers/net/dsa/mt7530.*
- F: net/dsa/tag_mtk.c
-
---- a/drivers/net/dsa/Kconfig
-+++ b/drivers/net/dsa/Kconfig
-@@ -37,10 +37,22 @@ config NET_DSA_MT7530
- tristate "MediaTek MT753x and MT7621 Ethernet switch support"
- select NET_DSA_TAG_MTK
- select MEDIATEK_GE_PHY
-+ imply NET_DSA_MT7530_MDIO
-+ help
-+ This enables support for the MediaTek MT7530 and MT7531 Ethernet
-+ switch chips. Multi-chip module MT7530 in MT7621AT, MT7621DAT,
-+ MT7621ST and MT7623AI SoCs, and built-in switch in MT7988 SoC are
-+ supported as well.
-+
-+config NET_DSA_MT7530_MDIO
-+ tristate "MediaTek MT7530 MDIO interface driver"
-+ depends on NET_DSA_MT7530
- select PCS_MTK_LYNXI
- help
-- This enables support for the MediaTek MT7530, MT7531, and MT7621
-- Ethernet switch chips.
-+ This enables support for the MediaTek MT7530 and MT7531 switch
-+ chips which are connected via MDIO, as well as multi-chip
-+ module MT7530 which can be found in the MT7621AT, MT7621DAT,
-+ MT7621ST and MT7623AI SoCs.
-
- config NET_DSA_MV88E6060
- tristate "Marvell 88E6060 ethernet switch chip support"
---- a/drivers/net/dsa/Makefile
-+++ b/drivers/net/dsa/Makefile
-@@ -7,6 +7,7 @@ obj-$(CONFIG_FIXED_PHY) += dsa_loop_bdi
- endif
- obj-$(CONFIG_NET_DSA_LANTIQ_GSWIP) += lantiq_gswip.o
- obj-$(CONFIG_NET_DSA_MT7530) += mt7530.o
-+obj-$(CONFIG_NET_DSA_MT7530_MDIO) += mt7530-mdio.o
- obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o
- obj-$(CONFIG_NET_DSA_RZN1_A5PSW) += rzn1_a5psw.o
- obj-$(CONFIG_NET_DSA_SMSC_LAN9303) += lan9303-core.o
---- /dev/null
-+++ b/drivers/net/dsa/mt7530-mdio.c
-@@ -0,0 +1,271 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+
-+#include <linux/gpio/consumer.h>
-+#include <linux/mdio.h>
-+#include <linux/module.h>
-+#include <linux/pcs/pcs-mtk-lynxi.h>
-+#include <linux/of_irq.h>
-+#include <linux/of_mdio.h>
-+#include <linux/of_net.h>
-+#include <linux/of_platform.h>
-+#include <linux/regmap.h>
-+#include <linux/reset.h>
-+#include <linux/regulator/consumer.h>
-+#include <net/dsa.h>
-+
-+#include "mt7530.h"
-+
-+static int
-+mt7530_regmap_write(void *context, unsigned int reg, unsigned int val)
-+{
-+ struct mii_bus *bus = context;
-+ u16 page, r, lo, hi;
-+ int ret;
-+
-+ page = (reg >> 6) & 0x3ff;
-+ r = (reg >> 2) & 0xf;
-+ lo = val & 0xffff;
-+ hi = val >> 16;
-+
-+ /* MT7530 uses 31 as the pseudo port */
-+ ret = bus->write(bus, 0x1f, 0x1f, page);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = bus->write(bus, 0x1f, r, lo);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = bus->write(bus, 0x1f, 0x10, hi);
-+ return ret;
-+}
-+
-+static int
-+mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val)
-+{
-+ struct mii_bus *bus = context;
-+ u16 page, r, lo, hi;
-+ int ret;
-+
-+ page = (reg >> 6) & 0x3ff;
-+ r = (reg >> 2) & 0xf;
-+
-+ /* MT7530 uses 31 as the pseudo port */
-+ ret = bus->write(bus, 0x1f, 0x1f, page);
-+ if (ret < 0)
-+ return ret;
-+
-+ lo = bus->read(bus, 0x1f, r);
-+ hi = bus->read(bus, 0x1f, 0x10);
-+
-+ *val = (hi << 16) | (lo & 0xffff);
-+
-+ return 0;
-+}
-+
-+static void
-+mt7530_mdio_regmap_lock(void *mdio_lock)
-+{
-+ mutex_lock_nested(mdio_lock, MDIO_MUTEX_NESTED);
-+}
-+
-+static void
-+mt7530_mdio_regmap_unlock(void *mdio_lock)
-+{
-+ mutex_unlock(mdio_lock);
-+}
-+
-+static const struct regmap_bus mt7530_regmap_bus = {
-+ .reg_write = mt7530_regmap_write,
-+ .reg_read = mt7530_regmap_read,
-+};
-+
-+static int
-+mt7531_create_sgmii(struct mt7530_priv *priv)
-+{
-+ struct regmap_config *mt7531_pcs_config[2];
-+ struct phylink_pcs *pcs;
-+ struct regmap *regmap;
-+ int i, ret = 0;
-+
-+ for (i = 0; i < 2; i++) {
-+ mt7531_pcs_config[i] = devm_kzalloc(priv->dev,
-+ sizeof(struct regmap_config),
-+ GFP_KERNEL);
-+ if (!mt7531_pcs_config[i]) {
-+ ret = -ENOMEM;
-+ break;
-+ }
-+
-+ mt7531_pcs_config[i]->name = i ? "port6" : "port5";
-+ mt7531_pcs_config[i]->reg_bits = 16;
-+ mt7531_pcs_config[i]->val_bits = 32;
-+ mt7531_pcs_config[i]->reg_stride = 4;
-+ mt7531_pcs_config[i]->reg_base = MT7531_SGMII_REG_BASE(5 + i);
-+ mt7531_pcs_config[i]->max_register = 0x17c;
-+ mt7531_pcs_config[i]->lock = mt7530_mdio_regmap_lock;
-+ mt7531_pcs_config[i]->unlock = mt7530_mdio_regmap_unlock;
-+ mt7531_pcs_config[i]->lock_arg = &priv->bus->mdio_lock;
-+
-+ regmap = devm_regmap_init(priv->dev,
-+ &mt7530_regmap_bus, priv->bus,
-+ mt7531_pcs_config[i]);
-+ if (IS_ERR(regmap)) {
-+ ret = PTR_ERR(regmap);
-+ break;
-+ }
-+ pcs = mtk_pcs_lynxi_create(priv->dev, regmap,
-+ MT7531_PHYA_CTRL_SIGNAL3, 0);
-+ if (!pcs) {
-+ ret = -ENXIO;
-+ break;
-+ }
-+ priv->ports[5 + i].sgmii_pcs = pcs;
-+ }
-+
-+ if (ret && i)
-+ mtk_pcs_lynxi_destroy(priv->ports[5].sgmii_pcs);
-+
-+ return ret;
-+}
-+
-+static const struct of_device_id mt7530_of_match[] = {
-+ { .compatible = "mediatek,mt7621", .data = &mt753x_table[ID_MT7621], },
-+ { .compatible = "mediatek,mt7530", .data = &mt753x_table[ID_MT7530], },
-+ { .compatible = "mediatek,mt7531", .data = &mt753x_table[ID_MT7531], },
-+ { /* sentinel */ },
-+};
-+MODULE_DEVICE_TABLE(of, mt7530_of_match);
-+
-+static int
-+mt7530_probe(struct mdio_device *mdiodev)
-+{
-+ static struct regmap_config *regmap_config;
-+ struct mt7530_priv *priv;
-+ struct device_node *dn;
-+ int ret;
-+
-+ dn = mdiodev->dev.of_node;
-+
-+ priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL);
-+ if (!priv)
-+ return -ENOMEM;
-+
-+ priv->bus = mdiodev->bus;
-+ priv->dev = &mdiodev->dev;
-+
-+ ret = mt7530_probe_common(priv);
-+ if (ret)
-+ return ret;
-+
-+ /* Use medatek,mcm property to distinguish hardware type that would
-+ * cause a little bit differences on power-on sequence.
-+ * Not MCM that indicates switch works as the remote standalone
-+ * integrated circuit so the GPIO pin would be used to complete
-+ * the reset, otherwise memory-mapped register accessing used
-+ * through syscon provides in the case of MCM.
-+ */
-+ priv->mcm = of_property_read_bool(dn, "mediatek,mcm");
-+ if (priv->mcm) {
-+ dev_info(&mdiodev->dev, "MT7530 adapts as multi-chip module\n");
-+
-+ priv->rstc = devm_reset_control_get(&mdiodev->dev, "mcm");
-+ if (IS_ERR(priv->rstc)) {
-+ dev_err(&mdiodev->dev, "Couldn't get our reset line\n");
-+ return PTR_ERR(priv->rstc);
-+ }
-+ } else {
-+ priv->reset = devm_gpiod_get_optional(&mdiodev->dev, "reset",
-+ GPIOD_OUT_LOW);
-+ if (IS_ERR(priv->reset)) {
-+ dev_err(&mdiodev->dev, "Couldn't get our reset line\n");
-+ return PTR_ERR(priv->reset);
-+ }
-+ }
-+
-+ if (priv->id == ID_MT7530) {
-+ priv->core_pwr = devm_regulator_get(&mdiodev->dev, "core");
-+ if (IS_ERR(priv->core_pwr))
-+ return PTR_ERR(priv->core_pwr);
-+
-+ priv->io_pwr = devm_regulator_get(&mdiodev->dev, "io");
-+ if (IS_ERR(priv->io_pwr))
-+ return PTR_ERR(priv->io_pwr);
-+ }
-+
-+ regmap_config = devm_kzalloc(&mdiodev->dev, sizeof(*regmap_config),
-+ GFP_KERNEL);
-+ if (!regmap_config)
-+ return -ENOMEM;
-+
-+ regmap_config->reg_bits = 16;
-+ regmap_config->val_bits = 32;
-+ regmap_config->reg_stride = 4;
-+ regmap_config->max_register = MT7530_CREV;
-+ regmap_config->disable_locking = true;
-+ priv->regmap = devm_regmap_init(priv->dev, &mt7530_regmap_bus,
-+ priv->bus, regmap_config);
-+ if (IS_ERR(priv->regmap))
-+ return PTR_ERR(priv->regmap);
-+
-+ if (priv->id == ID_MT7531) {
-+ ret = mt7531_create_sgmii(priv);
-+ if (ret)
-+ return ret;
-+ }
-+
-+ return dsa_register_switch(priv->ds);
-+}
-+
-+static void
-+mt7530_remove(struct mdio_device *mdiodev)
-+{
-+ struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev);
-+ int ret = 0, i;
-+
-+ if (!priv)
-+ return;
-+
-+ ret = regulator_disable(priv->core_pwr);
-+ if (ret < 0)
-+ dev_err(priv->dev,
-+ "Failed to disable core power: %d\n", ret);
-+
-+ ret = regulator_disable(priv->io_pwr);
-+ if (ret < 0)
-+ dev_err(priv->dev, "Failed to disable io pwr: %d\n",
-+ ret);
-+
-+ mt7530_remove_common(priv);
-+
-+ for (i = 0; i < 2; ++i)
-+ mtk_pcs_lynxi_destroy(priv->ports[5 + i].sgmii_pcs);
-+}
-+
-+static void mt7530_shutdown(struct mdio_device *mdiodev)
-+{
-+ struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev);
-+
-+ if (!priv)
-+ return;
-+
-+ dsa_switch_shutdown(priv->ds);
-+
-+ dev_set_drvdata(&mdiodev->dev, NULL);
-+}
-+
-+static struct mdio_driver mt7530_mdio_driver = {
-+ .probe = mt7530_probe,
-+ .remove = mt7530_remove,
-+ .shutdown = mt7530_shutdown,
-+ .mdiodrv.driver = {
-+ .name = "mt7530-mdio",
-+ .of_match_table = mt7530_of_match,
-+ },
-+};
-+
-+mdio_module_driver(mt7530_mdio_driver);
-+
-+MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
-+MODULE_DESCRIPTION("Driver for Mediatek MT7530 Switch (MDIO)");
-+MODULE_LICENSE("GPL");
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -14,7 +14,6 @@
- #include <linux/of_mdio.h>
- #include <linux/of_net.h>
- #include <linux/of_platform.h>
--#include <linux/pcs/pcs-mtk-lynxi.h>
- #include <linux/phylink.h>
- #include <linux/regmap.h>
- #include <linux/regulator/consumer.h>
-@@ -192,31 +191,6 @@ core_clear(struct mt7530_priv *priv, u32
- }
-
- static int
--mt7530_regmap_write(void *context, unsigned int reg, unsigned int val)
--{
-- struct mii_bus *bus = context;
-- u16 page, r, lo, hi;
-- int ret;
--
-- page = (reg >> 6) & 0x3ff;
-- r = (reg >> 2) & 0xf;
-- lo = val & 0xffff;
-- hi = val >> 16;
--
-- /* MT7530 uses 31 as the pseudo port */
-- ret = bus->write(bus, 0x1f, 0x1f, page);
-- if (ret < 0)
-- return ret;
--
-- ret = bus->write(bus, 0x1f, r, lo);
-- if (ret < 0)
-- return ret;
--
-- ret = bus->write(bus, 0x1f, 0x10, hi);
-- return ret;
--}
--
--static int
- mt7530_mii_write(struct mt7530_priv *priv, u32 reg, u32 val)
- {
- int ret;
-@@ -230,29 +204,6 @@ mt7530_mii_write(struct mt7530_priv *pri
- return ret;
- }
-
--static int
--mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val)
--{
-- struct mii_bus *bus = context;
-- u16 page, r, lo, hi;
-- int ret;
--
-- page = (reg >> 6) & 0x3ff;
-- r = (reg >> 2) & 0xf;
--
-- /* MT7530 uses 31 as the pseudo port */
-- ret = bus->write(bus, 0x1f, 0x1f, page);
-- if (ret < 0)
-- return ret;
--
-- lo = bus->read(bus, 0x1f, r);
-- hi = bus->read(bus, 0x1f, 0x10);
--
-- *val = (hi << 16) | (lo & 0xffff);
--
-- return 0;
--}
--
- static u32
- mt7530_mii_read(struct mt7530_priv *priv, u32 reg)
- {
-@@ -2975,72 +2926,6 @@ static const struct phylink_pcs_ops mt75
- .pcs_an_restart = mt7530_pcs_an_restart,
- };
-
--static void
--mt7530_mdio_regmap_lock(void *mdio_lock)
--{
-- mutex_lock_nested(mdio_lock, MDIO_MUTEX_NESTED);
--}
--
--static void
--mt7530_mdio_regmap_unlock(void *mdio_lock)
--{
-- mutex_unlock(mdio_lock);
--}
--
--static const struct regmap_bus mt7530_regmap_bus = {
-- .reg_write = mt7530_regmap_write,
-- .reg_read = mt7530_regmap_read,
--};
--
--static int
--mt7531_create_sgmii(struct mt7530_priv *priv)
--{
-- struct regmap_config *mt7531_pcs_config[2];
-- struct phylink_pcs *pcs;
-- struct regmap *regmap;
-- int i, ret = 0;
--
-- for (i = 0; i < 2; i++) {
-- mt7531_pcs_config[i] = devm_kzalloc(priv->dev,
-- sizeof(struct regmap_config),
-- GFP_KERNEL);
-- if (!mt7531_pcs_config[i]) {
-- ret = -ENOMEM;
-- break;
-- }
--
-- mt7531_pcs_config[i]->name = i ? "port6" : "port5";
-- mt7531_pcs_config[i]->reg_bits = 16;
-- mt7531_pcs_config[i]->val_bits = 32;
-- mt7531_pcs_config[i]->reg_stride = 4;
-- mt7531_pcs_config[i]->reg_base = MT7531_SGMII_REG_BASE(5 + i);
-- mt7531_pcs_config[i]->max_register = 0x17c;
-- mt7531_pcs_config[i]->lock = mt7530_mdio_regmap_lock;
-- mt7531_pcs_config[i]->unlock = mt7530_mdio_regmap_unlock;
-- mt7531_pcs_config[i]->lock_arg = &priv->bus->mdio_lock;
--
-- regmap = devm_regmap_init(priv->dev,
-- &mt7530_regmap_bus, priv->bus,
-- mt7531_pcs_config[i]);
-- if (IS_ERR(regmap)) {
-- ret = PTR_ERR(regmap);
-- break;
-- }
-- pcs = mtk_pcs_lynxi_create(priv->dev, regmap,
-- MT7531_PHYA_CTRL_SIGNAL3, 0);
-- if (!pcs) {
-- ret = -ENXIO;
-- break;
-- }
-- priv->ports[5 + i].sgmii_pcs = pcs;
-- }
--
-- if (ret && i)
-- mtk_pcs_lynxi_destroy(priv->ports[5].sgmii_pcs);
--
-- return ret;
--}
--
- static int
- mt753x_setup(struct dsa_switch *ds)
- {
-@@ -3099,7 +2984,7 @@ static int mt753x_set_mac_eee(struct dsa
- return 0;
- }
-
--static const struct dsa_switch_ops mt7530_switch_ops = {
-+const struct dsa_switch_ops mt7530_switch_ops = {
- .get_tag_protocol = mtk_get_tag_protocol,
- .setup = mt753x_setup,
- .get_strings = mt7530_get_strings,
-@@ -3133,8 +3018,9 @@ static const struct dsa_switch_ops mt753
- .get_mac_eee = mt753x_get_mac_eee,
- .set_mac_eee = mt753x_set_mac_eee,
- };
-+EXPORT_SYMBOL_GPL(mt7530_switch_ops);
-
--static const struct mt753x_info mt753x_table[] = {
-+const struct mt753x_info mt753x_table[] = {
- [ID_MT7621] = {
- .id = ID_MT7621,
- .pcs_ops = &mt7530_pcs_ops,
-@@ -3167,16 +3053,9 @@ static const struct mt753x_info mt753x_t
- .mac_port_config = mt7531_mac_config,
- },
- };
-+EXPORT_SYMBOL_GPL(mt753x_table);
-
--static const struct of_device_id mt7530_of_match[] = {
-- { .compatible = "mediatek,mt7621", .data = &mt753x_table[ID_MT7621], },
-- { .compatible = "mediatek,mt7530", .data = &mt753x_table[ID_MT7530], },
-- { .compatible = "mediatek,mt7531", .data = &mt753x_table[ID_MT7531], },
-- { /* sentinel */ },
--};
--MODULE_DEVICE_TABLE(of, mt7530_of_match);
--
--static int
-+int
- mt7530_probe_common(struct mt7530_priv *priv)
- {
- struct device *dev = priv->dev;
-@@ -3213,88 +3092,9 @@ mt7530_probe_common(struct mt7530_priv *
-
- return 0;
- }
-+EXPORT_SYMBOL_GPL(mt7530_probe_common);
-
--static int
--mt7530_probe(struct mdio_device *mdiodev)
--{
-- static struct regmap_config *regmap_config;
-- struct mt7530_priv *priv;
-- struct device_node *dn;
-- int ret;
--
-- dn = mdiodev->dev.of_node;
--
-- priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL);
-- if (!priv)
-- return -ENOMEM;
--
-- priv->bus = mdiodev->bus;
-- priv->dev = &mdiodev->dev;
--
-- ret = mt7530_probe_common(priv);
-- if (ret)
-- return ret;
--
-- /* Use medatek,mcm property to distinguish hardware type that would
-- * cause a little bit differences on power-on sequence.
-- * Not MCM that indicates switch works as the remote standalone
-- * integrated circuit so the GPIO pin would be used to complete
-- * the reset, otherwise memory-mapped register accessing used
-- * through syscon provides in the case of MCM.
-- */
-- priv->mcm = of_property_read_bool(dn, "mediatek,mcm");
-- if (priv->mcm) {
-- dev_info(&mdiodev->dev, "MT7530 adapts as multi-chip module\n");
--
-- priv->rstc = devm_reset_control_get(&mdiodev->dev, "mcm");
-- if (IS_ERR(priv->rstc)) {
-- dev_err(&mdiodev->dev, "Couldn't get our reset line\n");
-- return PTR_ERR(priv->rstc);
-- }
-- } else {
-- priv->reset = devm_gpiod_get_optional(&mdiodev->dev, "reset",
-- GPIOD_OUT_LOW);
-- if (IS_ERR(priv->reset)) {
-- dev_err(&mdiodev->dev, "Couldn't get our reset line\n");
-- return PTR_ERR(priv->reset);
-- }
-- }
--
-- if (priv->id == ID_MT7530) {
-- priv->core_pwr = devm_regulator_get(&mdiodev->dev, "core");
-- if (IS_ERR(priv->core_pwr))
-- return PTR_ERR(priv->core_pwr);
--
-- priv->io_pwr = devm_regulator_get(&mdiodev->dev, "io");
-- if (IS_ERR(priv->io_pwr))
-- return PTR_ERR(priv->io_pwr);
-- }
--
-- regmap_config = devm_kzalloc(&mdiodev->dev, sizeof(*regmap_config),
-- GFP_KERNEL);
-- if (!regmap_config)
-- return -ENOMEM;
--
-- regmap_config->reg_bits = 16;
-- regmap_config->val_bits = 32;
-- regmap_config->reg_stride = 4;
-- regmap_config->max_register = MT7530_CREV;
-- regmap_config->disable_locking = true;
-- priv->regmap = devm_regmap_init(priv->dev, &mt7530_regmap_bus,
-- priv->bus, regmap_config);
-- if (IS_ERR(priv->regmap))
-- return PTR_ERR(priv->regmap);
--
-- if (priv->id == ID_MT7531) {
-- ret = mt7531_create_sgmii(priv);
-- if (ret)
-- return ret;
-- }
--
-- return dsa_register_switch(priv->ds);
--}
--
--static void
-+void
- mt7530_remove_common(struct mt7530_priv *priv)
- {
- if (priv->irq)
-@@ -3304,55 +3104,7 @@ mt7530_remove_common(struct mt7530_priv
-
- mutex_destroy(&priv->reg_mutex);
- }
--
--static void
--mt7530_remove(struct mdio_device *mdiodev)
--{
-- struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev);
-- int ret = 0, i;
--
-- if (!priv)
-- return;
--
-- ret = regulator_disable(priv->core_pwr);
-- if (ret < 0)
-- dev_err(priv->dev,
-- "Failed to disable core power: %d\n", ret);
--
-- ret = regulator_disable(priv->io_pwr);
-- if (ret < 0)
-- dev_err(priv->dev, "Failed to disable io pwr: %d\n",
-- ret);
--
-- mt7530_remove_common(priv);
--
-- for (i = 0; i < 2; ++i)
-- mtk_pcs_lynxi_destroy(priv->ports[5 + i].sgmii_pcs);
--}
--
--static void mt7530_shutdown(struct mdio_device *mdiodev)
--{
-- struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev);
--
-- if (!priv)
-- return;
--
-- dsa_switch_shutdown(priv->ds);
--
-- dev_set_drvdata(&mdiodev->dev, NULL);
--}
--
--static struct mdio_driver mt7530_mdio_driver = {
-- .probe = mt7530_probe,
-- .remove = mt7530_remove,
-- .shutdown = mt7530_shutdown,
-- .mdiodrv.driver = {
-- .name = "mt7530",
-- .of_match_table = mt7530_of_match,
-- },
--};
--
--mdio_module_driver(mt7530_mdio_driver);
-+EXPORT_SYMBOL_GPL(mt7530_remove_common);
-
- MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
- MODULE_DESCRIPTION("Driver for Mediatek MT7530 Switch");
---- a/drivers/net/dsa/mt7530.h
-+++ b/drivers/net/dsa/mt7530.h
-@@ -814,4 +814,10 @@ static inline void INIT_MT7530_DUMMY_POL
- p->reg = reg;
- }
-
-+int mt7530_probe_common(struct mt7530_priv *priv);
-+void mt7530_remove_common(struct mt7530_priv *priv);
-+
-+extern const struct dsa_switch_ops mt7530_switch_ops;
-+extern const struct mt753x_info mt753x_table[];
-+
- #endif /* __MT7530_H */
+++ /dev/null
-From a52cadbf76593f8fcb2f4f62cb006e3f2a22ad06 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Mon, 3 Apr 2023 02:19:28 +0100
-Subject: [PATCH 11/13] net: dsa: mt7530: skip locking if MDIO bus isn't
- present
-
-As MT7530 and MT7531 internally use 32-bit wide registers, each access
-to any register of the switch requires several operations on the MDIO
-bus. Hence if there is congruent access, e.g. due to PCS or PHY
-polling, this can mess up and interfere with another ongoing register
-access sequence.
-
-However, the MDIO bus mutex is only relevant for MDIO-connected
-switches. Prepare switches which have there registers directly mapped
-into the SoCs register space via MMIO which do not require such
-locking. There we can simply use regmap's default locking mechanism.
-
-Hence guard mutex operations to only be performed in case of MDIO
-connected switches.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/mt7530.c | 6 ++++--
- 1 file changed, 4 insertions(+), 2 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -144,13 +144,15 @@ err:
- static void
- mt7530_mutex_lock(struct mt7530_priv *priv)
- {
-- mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
-+ if (priv->bus)
-+ mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
- }
-
- static void
- mt7530_mutex_unlock(struct mt7530_priv *priv)
- {
-- mutex_unlock(&priv->bus->mdio_lock);
-+ if (priv->bus)
-+ mutex_unlock(&priv->bus->mdio_lock);
- }
-
- static void
+++ /dev/null
-From b361015763fedea439f13b336b15ef7bdf1f7d4f Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Mon, 3 Apr 2023 02:19:40 +0100
-Subject: [PATCH 12/13] net: dsa: mt7530: introduce driver for MT7988 built-in
- switch
-
-Add driver for the built-in Gigabit Ethernet switch which can be found
-in the MediaTek MT7988 SoC.
-
-The switch shares most of its design with MT7530 and MT7531, but has
-it's registers mapped into the SoCs register space rather than being
-connected externally or internally via MDIO.
-
-Introduce a new platform driver to support that.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- MAINTAINERS | 2 +
- drivers/net/dsa/Kconfig | 12 +++
- drivers/net/dsa/Makefile | 1 +
- drivers/net/dsa/mt7530-mmio.c | 101 +++++++++++++++++++++++++
- drivers/net/dsa/mt7530.c | 135 +++++++++++++++++++++++++++++++++-
- drivers/net/dsa/mt7530.h | 12 +--
- 6 files changed, 253 insertions(+), 10 deletions(-)
- create mode 100644 drivers/net/dsa/mt7530-mmio.c
-
---- a/MAINTAINERS
-+++ b/MAINTAINERS
-@@ -13060,9 +13060,11 @@ MEDIATEK SWITCH DRIVER
- M: Sean Wang <sean.wang@mediatek.com>
- M: Landen Chao <Landen.Chao@mediatek.com>
- M: DENG Qingfang <dqfext@gmail.com>
-+M: Daniel Golle <daniel@makrotopia.org>
- L: netdev@vger.kernel.org
- S: Maintained
- F: drivers/net/dsa/mt7530-mdio.c
-+F: drivers/net/dsa/mt7530-mmio.c
- F: drivers/net/dsa/mt7530.*
- F: net/dsa/tag_mtk.c
-
---- a/drivers/net/dsa/Kconfig
-+++ b/drivers/net/dsa/Kconfig
-@@ -38,6 +38,7 @@ config NET_DSA_MT7530
- select NET_DSA_TAG_MTK
- select MEDIATEK_GE_PHY
- imply NET_DSA_MT7530_MDIO
-+ imply NET_DSA_MT7530_MMIO
- help
- This enables support for the MediaTek MT7530 and MT7531 Ethernet
- switch chips. Multi-chip module MT7530 in MT7621AT, MT7621DAT,
-@@ -54,6 +55,17 @@ config NET_DSA_MT7530_MDIO
- module MT7530 which can be found in the MT7621AT, MT7621DAT,
- MT7621ST and MT7623AI SoCs.
-
-+config NET_DSA_MT7530_MMIO
-+ tristate "MediaTek MT7530 MMIO interface driver"
-+ depends on NET_DSA_MT7530
-+ depends on HAS_IOMEM
-+ help
-+ This enables support for the built-in Ethernet switch found
-+ in the MediaTek MT7988 SoC.
-+ The switch is a similar design as MT7531, but the switch registers
-+ are directly mapped into the SoCs register space rather than being
-+ accessible via MDIO.
-+
- config NET_DSA_MV88E6060
- tristate "Marvell 88E6060 ethernet switch chip support"
- select NET_DSA_TAG_TRAILER
---- a/drivers/net/dsa/Makefile
-+++ b/drivers/net/dsa/Makefile
-@@ -8,6 +8,7 @@ endif
- obj-$(CONFIG_NET_DSA_LANTIQ_GSWIP) += lantiq_gswip.o
- obj-$(CONFIG_NET_DSA_MT7530) += mt7530.o
- obj-$(CONFIG_NET_DSA_MT7530_MDIO) += mt7530-mdio.o
-+obj-$(CONFIG_NET_DSA_MT7530_MMIO) += mt7530-mmio.o
- obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o
- obj-$(CONFIG_NET_DSA_RZN1_A5PSW) += rzn1_a5psw.o
- obj-$(CONFIG_NET_DSA_SMSC_LAN9303) += lan9303-core.o
---- /dev/null
-+++ b/drivers/net/dsa/mt7530-mmio.c
-@@ -0,0 +1,101 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+
-+#include <linux/module.h>
-+#include <linux/of_platform.h>
-+#include <linux/regmap.h>
-+#include <linux/regulator/consumer.h>
-+#include <linux/reset.h>
-+#include <net/dsa.h>
-+
-+#include "mt7530.h"
-+
-+static const struct of_device_id mt7988_of_match[] = {
-+ { .compatible = "mediatek,mt7988-switch", .data = &mt753x_table[ID_MT7988], },
-+ { /* sentinel */ },
-+};
-+MODULE_DEVICE_TABLE(of, mt7988_of_match);
-+
-+static int
-+mt7988_probe(struct platform_device *pdev)
-+{
-+ static struct regmap_config *sw_regmap_config;
-+ struct mt7530_priv *priv;
-+ void __iomem *base_addr;
-+ int ret;
-+
-+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
-+ if (!priv)
-+ return -ENOMEM;
-+
-+ priv->bus = NULL;
-+ priv->dev = &pdev->dev;
-+
-+ ret = mt7530_probe_common(priv);
-+ if (ret)
-+ return ret;
-+
-+ priv->rstc = devm_reset_control_get(&pdev->dev, NULL);
-+ if (IS_ERR(priv->rstc)) {
-+ dev_err(&pdev->dev, "Couldn't get our reset line\n");
-+ return PTR_ERR(priv->rstc);
-+ }
-+
-+ base_addr = devm_platform_ioremap_resource(pdev, 0);
-+ if (IS_ERR(base_addr)) {
-+ dev_err(&pdev->dev, "cannot request I/O memory space\n");
-+ return -ENXIO;
-+ }
-+
-+ sw_regmap_config = devm_kzalloc(&pdev->dev, sizeof(*sw_regmap_config), GFP_KERNEL);
-+ if (!sw_regmap_config)
-+ return -ENOMEM;
-+
-+ sw_regmap_config->name = "switch";
-+ sw_regmap_config->reg_bits = 16;
-+ sw_regmap_config->val_bits = 32;
-+ sw_regmap_config->reg_stride = 4;
-+ sw_regmap_config->max_register = MT7530_CREV;
-+ priv->regmap = devm_regmap_init_mmio(&pdev->dev, base_addr, sw_regmap_config);
-+ if (IS_ERR(priv->regmap))
-+ return PTR_ERR(priv->regmap);
-+
-+ return dsa_register_switch(priv->ds);
-+}
-+
-+static int
-+mt7988_remove(struct platform_device *pdev)
-+{
-+ struct mt7530_priv *priv = platform_get_drvdata(pdev);
-+
-+ if (priv)
-+ mt7530_remove_common(priv);
-+
-+ return 0;
-+}
-+
-+static void mt7988_shutdown(struct platform_device *pdev)
-+{
-+ struct mt7530_priv *priv = platform_get_drvdata(pdev);
-+
-+ if (!priv)
-+ return;
-+
-+ dsa_switch_shutdown(priv->ds);
-+
-+ dev_set_drvdata(&pdev->dev, NULL);
-+}
-+
-+static struct platform_driver mt7988_platform_driver = {
-+ .probe = mt7988_probe,
-+ .remove = mt7988_remove,
-+ .shutdown = mt7988_shutdown,
-+ .driver = {
-+ .name = "mt7530-mmio",
-+ .of_match_table = mt7988_of_match,
-+ },
-+};
-+module_platform_driver(mt7988_platform_driver);
-+
-+MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
-+MODULE_DESCRIPTION("Driver for Mediatek MT7530 Switch (MMIO)");
-+MODULE_LICENSE("GPL");
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -2005,6 +2005,47 @@ static const struct irq_domain_ops mt753
- };
-
- static void
-+mt7988_irq_mask(struct irq_data *d)
-+{
-+ struct mt7530_priv *priv = irq_data_get_irq_chip_data(d);
-+
-+ priv->irq_enable &= ~BIT(d->hwirq);
-+ mt7530_mii_write(priv, MT7530_SYS_INT_EN, priv->irq_enable);
-+}
-+
-+static void
-+mt7988_irq_unmask(struct irq_data *d)
-+{
-+ struct mt7530_priv *priv = irq_data_get_irq_chip_data(d);
-+
-+ priv->irq_enable |= BIT(d->hwirq);
-+ mt7530_mii_write(priv, MT7530_SYS_INT_EN, priv->irq_enable);
-+}
-+
-+static struct irq_chip mt7988_irq_chip = {
-+ .name = KBUILD_MODNAME,
-+ .irq_mask = mt7988_irq_mask,
-+ .irq_unmask = mt7988_irq_unmask,
-+};
-+
-+static int
-+mt7988_irq_map(struct irq_domain *domain, unsigned int irq,
-+ irq_hw_number_t hwirq)
-+{
-+ irq_set_chip_data(irq, domain->host_data);
-+ irq_set_chip_and_handler(irq, &mt7988_irq_chip, handle_simple_irq);
-+ irq_set_nested_thread(irq, true);
-+ irq_set_noprobe(irq);
-+
-+ return 0;
-+}
-+
-+static const struct irq_domain_ops mt7988_irq_domain_ops = {
-+ .map = mt7988_irq_map,
-+ .xlate = irq_domain_xlate_onecell,
-+};
-+
-+static void
- mt7530_setup_mdio_irq(struct mt7530_priv *priv)
- {
- struct dsa_switch *ds = priv->ds;
-@@ -2038,8 +2079,15 @@ mt7530_setup_irq(struct mt7530_priv *pri
- return priv->irq ? : -EINVAL;
- }
-
-- priv->irq_domain = irq_domain_add_linear(np, MT7530_NUM_PHYS,
-- &mt7530_irq_domain_ops, priv);
-+ if (priv->id == ID_MT7988)
-+ priv->irq_domain = irq_domain_add_linear(np, MT7530_NUM_PHYS,
-+ &mt7988_irq_domain_ops,
-+ priv);
-+ else
-+ priv->irq_domain = irq_domain_add_linear(np, MT7530_NUM_PHYS,
-+ &mt7530_irq_domain_ops,
-+ priv);
-+
- if (!priv->irq_domain) {
- dev_err(dev, "failed to create IRQ domain\n");
- return -ENOMEM;
-@@ -2538,6 +2586,25 @@ static void mt7531_mac_port_get_caps(str
- }
- }
-
-+static void mt7988_mac_port_get_caps(struct dsa_switch *ds, int port,
-+ struct phylink_config *config)
-+{
-+ phy_interface_zero(config->supported_interfaces);
-+
-+ switch (port) {
-+ case 0 ... 4: /* Internal phy */
-+ __set_bit(PHY_INTERFACE_MODE_INTERNAL,
-+ config->supported_interfaces);
-+ break;
-+
-+ case 6:
-+ __set_bit(PHY_INTERFACE_MODE_INTERNAL,
-+ config->supported_interfaces);
-+ config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
-+ MAC_10000FD;
-+ }
-+}
-+
- static int
- mt753x_pad_setup(struct dsa_switch *ds, const struct phylink_link_state *state)
- {
-@@ -2614,6 +2681,17 @@ static bool mt753x_is_mac_port(u32 port)
- }
-
- static int
-+mt7988_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
-+ phy_interface_t interface)
-+{
-+ if (dsa_is_cpu_port(ds, port) &&
-+ interface == PHY_INTERFACE_MODE_INTERNAL)
-+ return 0;
-+
-+ return -EINVAL;
-+}
-+
-+static int
- mt7531_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
- phy_interface_t interface)
- {
-@@ -2683,7 +2761,8 @@ mt753x_phylink_mac_config(struct dsa_swi
-
- switch (port) {
- case 0 ... 4: /* Internal phy */
-- if (state->interface != PHY_INTERFACE_MODE_GMII)
-+ if (state->interface != PHY_INTERFACE_MODE_GMII &&
-+ state->interface != PHY_INTERFACE_MODE_INTERNAL)
- goto unsupported;
- break;
- case 5: /* 2nd cpu port with phy of port 0 or 4 / external phy */
-@@ -2761,7 +2840,8 @@ static void mt753x_phylink_mac_link_up(s
- /* MT753x MAC works in 1G full duplex mode for all up-clocked
- * variants.
- */
-- if (interface == PHY_INTERFACE_MODE_TRGMII ||
-+ if (interface == PHY_INTERFACE_MODE_INTERNAL ||
-+ interface == PHY_INTERFACE_MODE_TRGMII ||
- (phy_interface_mode_is_8023z(interface))) {
- speed = SPEED_1000;
- duplex = DUPLEX_FULL;
-@@ -2841,6 +2921,21 @@ mt7531_cpu_port_config(struct dsa_switch
- return 0;
- }
-
-+static int
-+mt7988_cpu_port_config(struct dsa_switch *ds, int port)
-+{
-+ struct mt7530_priv *priv = ds->priv;
-+
-+ mt7530_write(priv, MT7530_PMCR_P(port),
-+ PMCR_CPU_PORT_SETTING(priv->id));
-+
-+ mt753x_phylink_mac_link_up(ds, port, MLO_AN_FIXED,
-+ PHY_INTERFACE_MODE_INTERNAL, NULL,
-+ SPEED_10000, DUPLEX_FULL, true, true);
-+
-+ return 0;
-+}
-+
- static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port,
- struct phylink_config *config)
- {
-@@ -2986,6 +3081,27 @@ static int mt753x_set_mac_eee(struct dsa
- return 0;
- }
-
-+static int mt7988_pad_setup(struct dsa_switch *ds, phy_interface_t interface)
-+{
-+ return 0;
-+}
-+
-+static int mt7988_setup(struct dsa_switch *ds)
-+{
-+ struct mt7530_priv *priv = ds->priv;
-+
-+ /* Reset the switch */
-+ reset_control_assert(priv->rstc);
-+ usleep_range(20, 50);
-+ reset_control_deassert(priv->rstc);
-+ usleep_range(20, 50);
-+
-+ /* Reset the switch PHYs */
-+ mt7530_write(priv, MT7530_SYS_CTRL, SYS_CTRL_PHY_RST);
-+
-+ return mt7531_setup_common(ds);
-+}
-+
- const struct dsa_switch_ops mt7530_switch_ops = {
- .get_tag_protocol = mtk_get_tag_protocol,
- .setup = mt753x_setup,
-@@ -3054,6 +3170,17 @@ const struct mt753x_info mt753x_table[]
- .mac_port_get_caps = mt7531_mac_port_get_caps,
- .mac_port_config = mt7531_mac_config,
- },
-+ [ID_MT7988] = {
-+ .id = ID_MT7988,
-+ .pcs_ops = &mt7530_pcs_ops,
-+ .sw_setup = mt7988_setup,
-+ .phy_read = mt7531_ind_phy_read,
-+ .phy_write = mt7531_ind_phy_write,
-+ .pad_setup = mt7988_pad_setup,
-+ .cpu_port_config = mt7988_cpu_port_config,
-+ .mac_port_get_caps = mt7988_mac_port_get_caps,
-+ .mac_port_config = mt7988_mac_config,
-+ },
- };
- EXPORT_SYMBOL_GPL(mt753x_table);
-
---- a/drivers/net/dsa/mt7530.h
-+++ b/drivers/net/dsa/mt7530.h
-@@ -18,6 +18,7 @@ enum mt753x_id {
- ID_MT7530 = 0,
- ID_MT7621 = 1,
- ID_MT7531 = 2,
-+ ID_MT7988 = 3,
- };
-
- #define NUM_TRGMII_CTRL 5
-@@ -54,11 +55,11 @@ enum mt753x_id {
- #define MT7531_MIRROR_PORT_SET(x) (((x) & MIRROR_MASK) << 16)
- #define MT7531_CPU_PMAP_MASK GENMASK(7, 0)
-
--#define MT753X_MIRROR_REG(id) (((id) == ID_MT7531) ? \
-+#define MT753X_MIRROR_REG(id) ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \
- MT7531_CFC : MT7530_MFC)
--#define MT753X_MIRROR_EN(id) (((id) == ID_MT7531) ? \
-+#define MT753X_MIRROR_EN(id) ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \
- MT7531_MIRROR_EN : MIRROR_EN)
--#define MT753X_MIRROR_MASK(id) (((id) == ID_MT7531) ? \
-+#define MT753X_MIRROR_MASK(id) ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \
- MT7531_MIRROR_MASK : MIRROR_MASK)
-
- /* Registers for BPDU and PAE frame control*/
-@@ -302,9 +303,8 @@ enum mt7530_vlan_port_acc_frm {
- MT7531_FORCE_DPX | \
- MT7531_FORCE_RX_FC | \
- MT7531_FORCE_TX_FC)
--#define PMCR_FORCE_MODE_ID(id) (((id) == ID_MT7531) ? \
-- MT7531_FORCE_MODE : \
-- PMCR_FORCE_MODE)
-+#define PMCR_FORCE_MODE_ID(id) ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \
-+ MT7531_FORCE_MODE : PMCR_FORCE_MODE)
- #define PMCR_LINK_SETTINGS_MASK (PMCR_TX_EN | PMCR_FORCE_SPEED_1000 | \
- PMCR_RX_EN | PMCR_FORCE_SPEED_100 | \
- PMCR_TX_FC_EN | PMCR_RX_FC_EN | \
+++ /dev/null
-From eb1dd407b4be7ca38166a38c56c8edf52c6a399f Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Sun, 16 Apr 2023 13:08:14 +0100
-Subject: [PATCH 13/13] net: dsa: mt7530: fix support for MT7531BE
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-There are two variants of the MT7531 switch IC which got different
-features (and pins) regarding port 5:
- * MT7531AE: SGMII/1000Base-X/2500Base-X SerDes PCS
- * MT7531BE: RGMII
-
-Moving the creation of the SerDes PCS from mt753x_setup to mt7530_probe
-with commit 6de285229773 ("net: dsa: mt7530: move SGMII PCS creation
-to mt7530_probe function") works fine for MT7531AE which got two
-instances of mtk-pcs-lynxi, however, MT7531BE requires mt7531_pll_setup
-to setup clocks before the single PCS on port 6 (usually used as CPU
-port) starts to work and hence the PCS creation failed on MT7531BE.
-
-Fix this by introducing a pointer to mt7531_create_sgmii function in
-struct mt7530_priv and call it again at the end of mt753x_setup like it
-was before commit 6de285229773 ("net: dsa: mt7530: move SGMII PCS
-creation to mt7530_probe function").
-
-Fixes: 6de285229773 ("net: dsa: mt7530: move SGMII PCS creation to mt7530_probe function")
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Acked-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Link: https://lore.kernel.org/r/ZDvlLhhqheobUvOK@makrotopia.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/dsa/mt7530-mdio.c | 16 ++++++++--------
- drivers/net/dsa/mt7530.c | 6 ++++++
- drivers/net/dsa/mt7530.h | 4 ++--
- 3 files changed, 16 insertions(+), 10 deletions(-)
-
---- a/drivers/net/dsa/mt7530-mdio.c
-+++ b/drivers/net/dsa/mt7530-mdio.c
-@@ -81,14 +81,17 @@ static const struct regmap_bus mt7530_re
- };
-
- static int
--mt7531_create_sgmii(struct mt7530_priv *priv)
-+mt7531_create_sgmii(struct mt7530_priv *priv, bool dual_sgmii)
- {
-- struct regmap_config *mt7531_pcs_config[2];
-+ struct regmap_config *mt7531_pcs_config[2] = {};
- struct phylink_pcs *pcs;
- struct regmap *regmap;
- int i, ret = 0;
-
-- for (i = 0; i < 2; i++) {
-+ /* MT7531AE has two SGMII units for port 5 and port 6
-+ * MT7531BE has only one SGMII unit for port 6
-+ */
-+ for (i = dual_sgmii ? 0 : 1; i < 2; i++) {
- mt7531_pcs_config[i] = devm_kzalloc(priv->dev,
- sizeof(struct regmap_config),
- GFP_KERNEL);
-@@ -208,11 +211,8 @@ mt7530_probe(struct mdio_device *mdiodev
- if (IS_ERR(priv->regmap))
- return PTR_ERR(priv->regmap);
-
-- if (priv->id == ID_MT7531) {
-- ret = mt7531_create_sgmii(priv);
-- if (ret)
-- return ret;
-- }
-+ if (priv->id == ID_MT7531)
-+ priv->create_sgmii = mt7531_create_sgmii;
-
- return dsa_register_switch(priv->ds);
- }
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -3048,6 +3048,12 @@ mt753x_setup(struct dsa_switch *ds)
- if (ret && priv->irq)
- mt7530_free_irq_common(priv);
-
-+ if (priv->create_sgmii) {
-+ ret = priv->create_sgmii(priv, mt7531_dual_sgmii_supported(priv));
-+ if (ret && priv->irq)
-+ mt7530_free_irq(priv);
-+ }
-+
- return ret;
- }
-
---- a/drivers/net/dsa/mt7530.h
-+++ b/drivers/net/dsa/mt7530.h
-@@ -748,10 +748,10 @@ struct mt753x_info {
- * registers
- * @p6_interface Holding the current port 6 interface
- * @p5_intf_sel: Holding the current port 5 interface select
-- *
- * @irq: IRQ number of the switch
- * @irq_domain: IRQ domain of the switch irq_chip
- * @irq_enable: IRQ enable bits, synced to SYS_INT_EN
-+ * @create_sgmii: Pointer to function creating SGMII PCS instance(s)
- */
- struct mt7530_priv {
- struct device *dev;
-@@ -770,7 +770,6 @@ struct mt7530_priv {
- unsigned int p5_intf_sel;
- u8 mirror_rx;
- u8 mirror_tx;
--
- struct mt7530_port ports[MT7530_NUM_PORTS];
- struct mt753x_pcs pcs[MT7530_NUM_PORTS];
- /* protect among processes for registers access*/
-@@ -778,6 +777,7 @@ struct mt7530_priv {
- int irq;
- struct irq_domain *irq_domain;
- u32 irq_enable;
-+ int (*create_sgmii)(struct mt7530_priv *priv, bool dual_sgmii);
- };
-
- struct mt7530_hw_vlan_entry {
+++ /dev/null
-From 70479a40954cf353e87a486997a3477108c75aa9 Mon Sep 17 00:00:00 2001
-From: Frank <Frank.Sae@motor-comm.com>
-Date: Fri, 28 Oct 2022 17:26:21 +0800
-Subject: [PATCH] net: phy: Add driver for Motorcomm yt8521 gigabit ethernet
- phy
-
-Add a driver for the motorcomm yt8521 gigabit ethernet phy. We have verified
- the driver on StarFive VisionFive development board, which is developed by
- Shanghai StarFive Technology Co., Ltd.. On the board, yt8521 gigabit ethernet
- phy works in utp mode, RGMII interface, supports 1000M/100M/10M speeds, and
- wol(magic package).
-
-Signed-off-by: Frank <Frank.Sae@motor-comm.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- MAINTAINERS | 1 +
- drivers/net/phy/Kconfig | 2 +-
- drivers/net/phy/motorcomm.c | 1635 ++++++++++++++++++++++++++++++++++-
- 3 files changed, 1635 insertions(+), 3 deletions(-)
-
---- a/MAINTAINERS
-+++ b/MAINTAINERS
-@@ -13964,6 +13964,7 @@ F: include/uapi/linux/meye.h
-
- MOTORCOMM PHY DRIVER
- M: Peter Geis <pgwipeout@gmail.com>
-+M: Frank <Frank.Sae@motor-comm.com>
- L: netdev@vger.kernel.org
- S: Maintained
- F: drivers/net/phy/motorcomm.c
---- a/drivers/net/phy/Kconfig
-+++ b/drivers/net/phy/Kconfig
-@@ -257,7 +257,7 @@ config MOTORCOMM_PHY
- tristate "Motorcomm PHYs"
- help
- Enables support for Motorcomm network PHYs.
-- Currently supports the YT8511 gigabit PHY.
-+ Currently supports the YT8511, YT8521 Gigabit Ethernet PHYs.
-
- config NATIONAL_PHY
- tristate "National Semiconductor PHYs"
---- a/drivers/net/phy/motorcomm.c
-+++ b/drivers/net/phy/motorcomm.c
-@@ -1,15 +1,106 @@
- // SPDX-License-Identifier: GPL-2.0+
- /*
-- * Driver for Motorcomm PHYs
-+ * Motorcomm 8511/8521 PHY driver.
- *
- * Author: Peter Geis <pgwipeout@gmail.com>
-+ * Author: Frank <Frank.Sae@motor-comm.com>
- */
-
-+#include <linux/etherdevice.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/phy.h>
-
- #define PHY_ID_YT8511 0x0000010a
-+#define PHY_ID_YT8521 0x0000011A
-+
-+/* YT8521 Register Overview
-+ * UTP Register space | FIBER Register space
-+ * ------------------------------------------------------------
-+ * | UTP MII | FIBER MII |
-+ * | UTP MMD | |
-+ * | UTP Extended | FIBER Extended |
-+ * ------------------------------------------------------------
-+ * | Common Extended |
-+ * ------------------------------------------------------------
-+ */
-+
-+/* 0x10 ~ 0x15 , 0x1E and 0x1F are common MII registers of yt phy */
-+
-+/* Specific Function Control Register */
-+#define YTPHY_SPECIFIC_FUNCTION_CONTROL_REG 0x10
-+
-+/* 2b00 Manual MDI configuration
-+ * 2b01 Manual MDIX configuration
-+ * 2b10 Reserved
-+ * 2b11 Enable automatic crossover for all modes *default*
-+ */
-+#define YTPHY_SFCR_MDI_CROSSOVER_MODE_MASK (BIT(6) | BIT(5))
-+#define YTPHY_SFCR_CROSSOVER_EN BIT(3)
-+#define YTPHY_SFCR_SQE_TEST_EN BIT(2)
-+#define YTPHY_SFCR_POLARITY_REVERSAL_EN BIT(1)
-+#define YTPHY_SFCR_JABBER_DIS BIT(0)
-+
-+/* Specific Status Register */
-+#define YTPHY_SPECIFIC_STATUS_REG 0x11
-+#define YTPHY_SSR_SPEED_MODE_OFFSET 14
-+
-+#define YTPHY_SSR_SPEED_MODE_MASK (BIT(15) | BIT(14))
-+#define YTPHY_SSR_SPEED_10M 0x0
-+#define YTPHY_SSR_SPEED_100M 0x1
-+#define YTPHY_SSR_SPEED_1000M 0x2
-+#define YTPHY_SSR_DUPLEX_OFFSET 13
-+#define YTPHY_SSR_DUPLEX BIT(13)
-+#define YTPHY_SSR_PAGE_RECEIVED BIT(12)
-+#define YTPHY_SSR_SPEED_DUPLEX_RESOLVED BIT(11)
-+#define YTPHY_SSR_LINK BIT(10)
-+#define YTPHY_SSR_MDIX_CROSSOVER BIT(6)
-+#define YTPHY_SSR_DOWNGRADE BIT(5)
-+#define YTPHY_SSR_TRANSMIT_PAUSE BIT(3)
-+#define YTPHY_SSR_RECEIVE_PAUSE BIT(2)
-+#define YTPHY_SSR_POLARITY BIT(1)
-+#define YTPHY_SSR_JABBER BIT(0)
-+
-+/* Interrupt enable Register */
-+#define YTPHY_INTERRUPT_ENABLE_REG 0x12
-+#define YTPHY_IER_WOL BIT(6)
-+
-+/* Interrupt Status Register */
-+#define YTPHY_INTERRUPT_STATUS_REG 0x13
-+#define YTPHY_ISR_AUTONEG_ERR BIT(15)
-+#define YTPHY_ISR_SPEED_CHANGED BIT(14)
-+#define YTPHY_ISR_DUPLEX_CHANGED BIT(13)
-+#define YTPHY_ISR_PAGE_RECEIVED BIT(12)
-+#define YTPHY_ISR_LINK_FAILED BIT(11)
-+#define YTPHY_ISR_LINK_SUCCESSED BIT(10)
-+#define YTPHY_ISR_WOL BIT(6)
-+#define YTPHY_ISR_WIRESPEED_DOWNGRADE BIT(5)
-+#define YTPHY_ISR_SERDES_LINK_FAILED BIT(3)
-+#define YTPHY_ISR_SERDES_LINK_SUCCESSED BIT(2)
-+#define YTPHY_ISR_POLARITY_CHANGED BIT(1)
-+#define YTPHY_ISR_JABBER_HAPPENED BIT(0)
-+
-+/* Speed Auto Downgrade Control Register */
-+#define YTPHY_SPEED_AUTO_DOWNGRADE_CONTROL_REG 0x14
-+#define YTPHY_SADCR_SPEED_DOWNGRADE_EN BIT(5)
-+
-+/* If these bits are set to 3, the PHY attempts five times ( 3(set value) +
-+ * additional 2) before downgrading, default 0x3
-+ */
-+#define YTPHY_SADCR_SPEED_RETRY_LIMIT (0x3 << 2)
-+
-+/* Rx Error Counter Register */
-+#define YTPHY_RX_ERROR_COUNTER_REG 0x15
-+
-+/* Extended Register's Address Offset Register */
-+#define YTPHY_PAGE_SELECT 0x1E
-+
-+/* Extended Register's Data Register */
-+#define YTPHY_PAGE_DATA 0x1F
-+
-+/* FIBER Auto-Negotiation link partner ability */
-+#define YTPHY_FLPA_PAUSE (0x3 << 7)
-+#define YTPHY_FLPA_ASYM_PAUSE (0x2 << 7)
-
- #define YT8511_PAGE_SELECT 0x1e
- #define YT8511_PAGE 0x1f
-@@ -38,6 +129,352 @@
- #define YT8511_DELAY_FE_TX_EN (0xf << 12)
- #define YT8511_DELAY_FE_TX_DIS (0x2 << 12)
-
-+/* Extended register is different from MMD Register and MII Register.
-+ * We can use ytphy_read_ext/ytphy_write_ext/ytphy_modify_ext function to
-+ * operate extended register.
-+ * Extended Register start
-+ */
-+
-+/* Phy gmii clock gating Register */
-+#define YT8521_CLOCK_GATING_REG 0xC
-+#define YT8521_CGR_RX_CLK_EN BIT(12)
-+
-+#define YT8521_EXTREG_SLEEP_CONTROL1_REG 0x27
-+#define YT8521_ESC1R_SLEEP_SW BIT(15)
-+#define YT8521_ESC1R_PLLON_SLP BIT(14)
-+
-+/* Phy fiber Link timer cfg2 Register */
-+#define YT8521_LINK_TIMER_CFG2_REG 0xA5
-+#define YT8521_LTCR_EN_AUTOSEN BIT(15)
-+
-+/* 0xA000, 0xA001, 0xA003 ,and 0xA006 ~ 0xA00A are common ext registers
-+ * of yt8521 phy. There is no need to switch reg space when operating these
-+ * registers.
-+ */
-+
-+#define YT8521_REG_SPACE_SELECT_REG 0xA000
-+#define YT8521_RSSR_SPACE_MASK BIT(1)
-+#define YT8521_RSSR_FIBER_SPACE (0x1 << 1)
-+#define YT8521_RSSR_UTP_SPACE (0x0 << 1)
-+#define YT8521_RSSR_TO_BE_ARBITRATED (0xFF)
-+
-+#define YT8521_CHIP_CONFIG_REG 0xA001
-+#define YT8521_CCR_SW_RST BIT(15)
-+
-+#define YT8521_CCR_MODE_SEL_MASK (BIT(2) | BIT(1) | BIT(0))
-+#define YT8521_CCR_MODE_UTP_TO_RGMII 0
-+#define YT8521_CCR_MODE_FIBER_TO_RGMII 1
-+#define YT8521_CCR_MODE_UTP_FIBER_TO_RGMII 2
-+#define YT8521_CCR_MODE_UTP_TO_SGMII 3
-+#define YT8521_CCR_MODE_SGPHY_TO_RGMAC 4
-+#define YT8521_CCR_MODE_SGMAC_TO_RGPHY 5
-+#define YT8521_CCR_MODE_UTP_TO_FIBER_AUTO 6
-+#define YT8521_CCR_MODE_UTP_TO_FIBER_FORCE 7
-+
-+/* 3 phy polling modes,poll mode combines utp and fiber mode*/
-+#define YT8521_MODE_FIBER 0x1
-+#define YT8521_MODE_UTP 0x2
-+#define YT8521_MODE_POLL 0x3
-+
-+#define YT8521_RGMII_CONFIG1_REG 0xA003
-+
-+/* TX Gig-E Delay is bits 3:0, default 0x1
-+ * TX Fast-E Delay is bits 7:4, default 0xf
-+ * RX Delay is bits 13:10, default 0x0
-+ * Delay = 150ps * N
-+ * On = 2250ps, off = 0ps
-+ */
-+#define YT8521_RC1R_RX_DELAY_MASK (0xF << 10)
-+#define YT8521_RC1R_RX_DELAY_EN (0xF << 10)
-+#define YT8521_RC1R_RX_DELAY_DIS (0x0 << 10)
-+#define YT8521_RC1R_FE_TX_DELAY_MASK (0xF << 4)
-+#define YT8521_RC1R_FE_TX_DELAY_EN (0xF << 4)
-+#define YT8521_RC1R_FE_TX_DELAY_DIS (0x0 << 4)
-+#define YT8521_RC1R_GE_TX_DELAY_MASK (0xF << 0)
-+#define YT8521_RC1R_GE_TX_DELAY_EN (0xF << 0)
-+#define YT8521_RC1R_GE_TX_DELAY_DIS (0x0 << 0)
-+
-+#define YTPHY_MISC_CONFIG_REG 0xA006
-+#define YTPHY_MCR_FIBER_SPEED_MASK BIT(0)
-+#define YTPHY_MCR_FIBER_1000BX (0x1 << 0)
-+#define YTPHY_MCR_FIBER_100FX (0x0 << 0)
-+
-+/* WOL MAC ADDR: MACADDR2(highest), MACADDR1(middle), MACADDR0(lowest) */
-+#define YTPHY_WOL_MACADDR2_REG 0xA007
-+#define YTPHY_WOL_MACADDR1_REG 0xA008
-+#define YTPHY_WOL_MACADDR0_REG 0xA009
-+
-+#define YTPHY_WOL_CONFIG_REG 0xA00A
-+#define YTPHY_WCR_INTR_SEL BIT(6)
-+#define YTPHY_WCR_ENABLE BIT(3)
-+
-+/* 2b00 84ms
-+ * 2b01 168ms *default*
-+ * 2b10 336ms
-+ * 2b11 672ms
-+ */
-+#define YTPHY_WCR_PULSE_WIDTH_MASK (BIT(2) | BIT(1))
-+#define YTPHY_WCR_PULSE_WIDTH_672MS (BIT(2) | BIT(1))
-+
-+/* 1b0 Interrupt and WOL events is level triggered and active LOW *default*
-+ * 1b1 Interrupt and WOL events is pulse triggered and active LOW
-+ */
-+#define YTPHY_WCR_TYPE_PULSE BIT(0)
-+
-+/* Extended Register end */
-+
-+struct yt8521_priv {
-+ /* combo_advertising is used for case of YT8521 in combo mode,
-+ * this means that yt8521 may work in utp or fiber mode which depends
-+ * on which media is connected (YT8521_RSSR_TO_BE_ARBITRATED).
-+ */
-+ __ETHTOOL_DECLARE_LINK_MODE_MASK(combo_advertising);
-+
-+ /* YT8521_MODE_FIBER / YT8521_MODE_UTP / YT8521_MODE_POLL*/
-+ u8 polling_mode;
-+ u8 strap_mode; /* 8 working modes */
-+ /* current reg page of yt8521 phy:
-+ * YT8521_RSSR_UTP_SPACE
-+ * YT8521_RSSR_FIBER_SPACE
-+ * YT8521_RSSR_TO_BE_ARBITRATED
-+ */
-+ u8 reg_page;
-+};
-+
-+/**
-+ * ytphy_read_ext() - read a PHY's extended register
-+ * @phydev: a pointer to a &struct phy_device
-+ * @regnum: register number to read
-+ *
-+ * NOTE:The caller must have taken the MDIO bus lock.
-+ *
-+ * returns the value of regnum reg or negative error code
-+ */
-+static int ytphy_read_ext(struct phy_device *phydev, u16 regnum)
-+{
-+ int ret;
-+
-+ ret = __phy_write(phydev, YTPHY_PAGE_SELECT, regnum);
-+ if (ret < 0)
-+ return ret;
-+
-+ return __phy_read(phydev, YTPHY_PAGE_DATA);
-+}
-+
-+/**
-+ * ytphy_read_ext_with_lock() - read a PHY's extended register
-+ * @phydev: a pointer to a &struct phy_device
-+ * @regnum: register number to read
-+ *
-+ * returns the value of regnum reg or negative error code
-+ */
-+static int ytphy_read_ext_with_lock(struct phy_device *phydev, u16 regnum)
-+{
-+ int ret;
-+
-+ phy_lock_mdio_bus(phydev);
-+ ret = ytphy_read_ext(phydev, regnum);
-+ phy_unlock_mdio_bus(phydev);
-+
-+ return ret;
-+}
-+
-+/**
-+ * ytphy_write_ext() - write a PHY's extended register
-+ * @phydev: a pointer to a &struct phy_device
-+ * @regnum: register number to write
-+ * @val: value to write to @regnum
-+ *
-+ * NOTE:The caller must have taken the MDIO bus lock.
-+ *
-+ * returns 0 or negative error code
-+ */
-+static int ytphy_write_ext(struct phy_device *phydev, u16 regnum, u16 val)
-+{
-+ int ret;
-+
-+ ret = __phy_write(phydev, YTPHY_PAGE_SELECT, regnum);
-+ if (ret < 0)
-+ return ret;
-+
-+ return __phy_write(phydev, YTPHY_PAGE_DATA, val);
-+}
-+
-+/**
-+ * ytphy_write_ext_with_lock() - write a PHY's extended register
-+ * @phydev: a pointer to a &struct phy_device
-+ * @regnum: register number to write
-+ * @val: value to write to @regnum
-+ *
-+ * returns 0 or negative error code
-+ */
-+static int ytphy_write_ext_with_lock(struct phy_device *phydev, u16 regnum,
-+ u16 val)
-+{
-+ int ret;
-+
-+ phy_lock_mdio_bus(phydev);
-+ ret = ytphy_write_ext(phydev, regnum, val);
-+ phy_unlock_mdio_bus(phydev);
-+
-+ return ret;
-+}
-+
-+/**
-+ * ytphy_modify_ext() - bits modify a PHY's extended register
-+ * @phydev: a pointer to a &struct phy_device
-+ * @regnum: register number to write
-+ * @mask: bit mask of bits to clear
-+ * @set: bit mask of bits to set
-+ *
-+ * NOTE: Convenience function which allows a PHY's extended register to be
-+ * modified as new register value = (old register value & ~mask) | set.
-+ * The caller must have taken the MDIO bus lock.
-+ *
-+ * returns 0 or negative error code
-+ */
-+static int ytphy_modify_ext(struct phy_device *phydev, u16 regnum, u16 mask,
-+ u16 set)
-+{
-+ int ret;
-+
-+ ret = __phy_write(phydev, YTPHY_PAGE_SELECT, regnum);
-+ if (ret < 0)
-+ return ret;
-+
-+ return __phy_modify(phydev, YTPHY_PAGE_DATA, mask, set);
-+}
-+
-+/**
-+ * ytphy_modify_ext_with_lock() - bits modify a PHY's extended register
-+ * @phydev: a pointer to a &struct phy_device
-+ * @regnum: register number to write
-+ * @mask: bit mask of bits to clear
-+ * @set: bit mask of bits to set
-+ *
-+ * NOTE: Convenience function which allows a PHY's extended register to be
-+ * modified as new register value = (old register value & ~mask) | set.
-+ *
-+ * returns 0 or negative error code
-+ */
-+static int ytphy_modify_ext_with_lock(struct phy_device *phydev, u16 regnum,
-+ u16 mask, u16 set)
-+{
-+ int ret;
-+
-+ phy_lock_mdio_bus(phydev);
-+ ret = ytphy_modify_ext(phydev, regnum, mask, set);
-+ phy_unlock_mdio_bus(phydev);
-+
-+ return ret;
-+}
-+
-+/**
-+ * ytphy_get_wol() - report whether wake-on-lan is enabled
-+ * @phydev: a pointer to a &struct phy_device
-+ * @wol: a pointer to a &struct ethtool_wolinfo
-+ *
-+ * NOTE: YTPHY_WOL_CONFIG_REG is common ext reg.
-+ */
-+static void ytphy_get_wol(struct phy_device *phydev,
-+ struct ethtool_wolinfo *wol)
-+{
-+ int wol_config;
-+
-+ wol->supported = WAKE_MAGIC;
-+ wol->wolopts = 0;
-+
-+ wol_config = ytphy_read_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG);
-+ if (wol_config < 0)
-+ return;
-+
-+ if (wol_config & YTPHY_WCR_ENABLE)
-+ wol->wolopts |= WAKE_MAGIC;
-+}
-+
-+/**
-+ * ytphy_set_wol() - turn wake-on-lan on or off
-+ * @phydev: a pointer to a &struct phy_device
-+ * @wol: a pointer to a &struct ethtool_wolinfo
-+ *
-+ * NOTE: YTPHY_WOL_CONFIG_REG, YTPHY_WOL_MACADDR2_REG, YTPHY_WOL_MACADDR1_REG
-+ * and YTPHY_WOL_MACADDR0_REG are common ext reg. The
-+ * YTPHY_INTERRUPT_ENABLE_REG of UTP is special, fiber also use this register.
-+ *
-+ * returns 0 or negative errno code
-+ */
-+static int ytphy_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
-+{
-+ struct net_device *p_attached_dev;
-+ const u16 mac_addr_reg[] = {
-+ YTPHY_WOL_MACADDR2_REG,
-+ YTPHY_WOL_MACADDR1_REG,
-+ YTPHY_WOL_MACADDR0_REG,
-+ };
-+ const u8 *mac_addr;
-+ int old_page;
-+ int ret = 0;
-+ u16 mask;
-+ u16 val;
-+ u8 i;
-+
-+ if (wol->wolopts & WAKE_MAGIC) {
-+ p_attached_dev = phydev->attached_dev;
-+ if (!p_attached_dev)
-+ return -ENODEV;
-+
-+ mac_addr = (const u8 *)p_attached_dev->dev_addr;
-+ if (!is_valid_ether_addr(mac_addr))
-+ return -EINVAL;
-+
-+ /* lock mdio bus then switch to utp reg space */
-+ old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE);
-+ if (old_page < 0)
-+ goto err_restore_page;
-+
-+ /* Store the device address for the magic packet */
-+ for (i = 0; i < 3; i++) {
-+ ret = ytphy_write_ext(phydev, mac_addr_reg[i],
-+ ((mac_addr[i * 2] << 8)) |
-+ (mac_addr[i * 2 + 1]));
-+ if (ret < 0)
-+ goto err_restore_page;
-+ }
-+
-+ /* Enable WOL feature */
-+ mask = YTPHY_WCR_PULSE_WIDTH_MASK | YTPHY_WCR_INTR_SEL;
-+ val = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
-+ val |= YTPHY_WCR_TYPE_PULSE | YTPHY_WCR_PULSE_WIDTH_672MS;
-+ ret = ytphy_modify_ext(phydev, YTPHY_WOL_CONFIG_REG, mask, val);
-+ if (ret < 0)
-+ goto err_restore_page;
-+
-+ /* Enable WOL interrupt */
-+ ret = __phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, 0,
-+ YTPHY_IER_WOL);
-+ if (ret < 0)
-+ goto err_restore_page;
-+
-+ } else {
-+ old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE);
-+ if (old_page < 0)
-+ goto err_restore_page;
-+
-+ /* Disable WOL feature */
-+ mask = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
-+ ret = ytphy_modify_ext(phydev, YTPHY_WOL_CONFIG_REG, mask, 0);
-+
-+ /* Disable WOL interrupt */
-+ ret = __phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG,
-+ YTPHY_IER_WOL, 0);
-+ if (ret < 0)
-+ goto err_restore_page;
-+ }
-+
-+err_restore_page:
-+ return phy_restore_page(phydev, old_page, ret);
-+}
-+
- static int yt8511_read_page(struct phy_device *phydev)
- {
- return __phy_read(phydev, YT8511_PAGE_SELECT);
-@@ -111,6 +548,1181 @@ err_restore_page:
- return phy_restore_page(phydev, oldpage, ret);
- }
-
-+/**
-+ * yt8521_read_page() - read reg page
-+ * @phydev: a pointer to a &struct phy_device
-+ *
-+ * returns current reg space of yt8521 (YT8521_RSSR_FIBER_SPACE/
-+ * YT8521_RSSR_UTP_SPACE) or negative errno code
-+ */
-+static int yt8521_read_page(struct phy_device *phydev)
-+{
-+ int old_page;
-+
-+ old_page = ytphy_read_ext(phydev, YT8521_REG_SPACE_SELECT_REG);
-+ if (old_page < 0)
-+ return old_page;
-+
-+ if ((old_page & YT8521_RSSR_SPACE_MASK) == YT8521_RSSR_FIBER_SPACE)
-+ return YT8521_RSSR_FIBER_SPACE;
-+
-+ return YT8521_RSSR_UTP_SPACE;
-+};
-+
-+/**
-+ * yt8521_write_page() - write reg page
-+ * @phydev: a pointer to a &struct phy_device
-+ * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to write.
-+ *
-+ * returns 0 or negative errno code
-+ */
-+static int yt8521_write_page(struct phy_device *phydev, int page)
-+{
-+ int mask = YT8521_RSSR_SPACE_MASK;
-+ int set;
-+
-+ if ((page & YT8521_RSSR_SPACE_MASK) == YT8521_RSSR_FIBER_SPACE)
-+ set = YT8521_RSSR_FIBER_SPACE;
-+ else
-+ set = YT8521_RSSR_UTP_SPACE;
-+
-+ return ytphy_modify_ext(phydev, YT8521_REG_SPACE_SELECT_REG, mask, set);
-+};
-+
-+/**
-+ * yt8521_probe() - read chip config then set suitable polling_mode
-+ * @phydev: a pointer to a &struct phy_device
-+ *
-+ * returns 0 or negative errno code
-+ */
-+static int yt8521_probe(struct phy_device *phydev)
-+{
-+ struct device *dev = &phydev->mdio.dev;
-+ struct yt8521_priv *priv;
-+ int chip_config;
-+ int ret;
-+
-+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
-+ if (!priv)
-+ return -ENOMEM;
-+
-+ phydev->priv = priv;
-+
-+ chip_config = ytphy_read_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG);
-+ if (chip_config < 0)
-+ return chip_config;
-+
-+ priv->strap_mode = chip_config & YT8521_CCR_MODE_SEL_MASK;
-+ switch (priv->strap_mode) {
-+ case YT8521_CCR_MODE_FIBER_TO_RGMII:
-+ case YT8521_CCR_MODE_SGPHY_TO_RGMAC:
-+ case YT8521_CCR_MODE_SGMAC_TO_RGPHY:
-+ priv->polling_mode = YT8521_MODE_FIBER;
-+ priv->reg_page = YT8521_RSSR_FIBER_SPACE;
-+ phydev->port = PORT_FIBRE;
-+ break;
-+ case YT8521_CCR_MODE_UTP_FIBER_TO_RGMII:
-+ case YT8521_CCR_MODE_UTP_TO_FIBER_AUTO:
-+ case YT8521_CCR_MODE_UTP_TO_FIBER_FORCE:
-+ priv->polling_mode = YT8521_MODE_POLL;
-+ priv->reg_page = YT8521_RSSR_TO_BE_ARBITRATED;
-+ phydev->port = PORT_NONE;
-+ break;
-+ case YT8521_CCR_MODE_UTP_TO_SGMII:
-+ case YT8521_CCR_MODE_UTP_TO_RGMII:
-+ priv->polling_mode = YT8521_MODE_UTP;
-+ priv->reg_page = YT8521_RSSR_UTP_SPACE;
-+ phydev->port = PORT_TP;
-+ break;
-+ }
-+ /* set default reg space */
-+ if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) {
-+ ret = ytphy_write_ext_with_lock(phydev,
-+ YT8521_REG_SPACE_SELECT_REG,
-+ priv->reg_page);
-+ if (ret < 0)
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
-+/**
-+ * ytphy_utp_read_lpa() - read LPA then setup lp_advertising for utp
-+ * @phydev: a pointer to a &struct phy_device
-+ *
-+ * NOTE:The caller must have taken the MDIO bus lock.
-+ *
-+ * returns 0 or negative errno code
-+ */
-+static int ytphy_utp_read_lpa(struct phy_device *phydev)
-+{
-+ int lpa, lpagb;
-+
-+ if (phydev->autoneg == AUTONEG_ENABLE) {
-+ if (!phydev->autoneg_complete) {
-+ mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising,
-+ 0);
-+ mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, 0);
-+ return 0;
-+ }
-+
-+ if (phydev->is_gigabit_capable) {
-+ lpagb = __phy_read(phydev, MII_STAT1000);
-+ if (lpagb < 0)
-+ return lpagb;
-+
-+ if (lpagb & LPA_1000MSFAIL) {
-+ int adv = __phy_read(phydev, MII_CTRL1000);
-+
-+ if (adv < 0)
-+ return adv;
-+
-+ if (adv & CTL1000_ENABLE_MASTER)
-+ phydev_err(phydev, "Master/Slave resolution failed, maybe conflicting manual settings?\n");
-+ else
-+ phydev_err(phydev, "Master/Slave resolution failed\n");
-+ return -ENOLINK;
-+ }
-+
-+ mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising,
-+ lpagb);
-+ }
-+
-+ lpa = __phy_read(phydev, MII_LPA);
-+ if (lpa < 0)
-+ return lpa;
-+
-+ mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa);
-+ } else {
-+ linkmode_zero(phydev->lp_advertising);
-+ }
-+
-+ return 0;
-+}
-+
-+/**
-+ * yt8521_adjust_status() - update speed and duplex to phydev. when in fiber
-+ * mode, adjust speed and duplex.
-+ * @phydev: a pointer to a &struct phy_device
-+ * @status: yt8521 status read from YTPHY_SPECIFIC_STATUS_REG
-+ * @is_utp: false(yt8521 work in fiber mode) or true(yt8521 work in utp mode)
-+ *
-+ * NOTE:The caller must have taken the MDIO bus lock.
-+ *
-+ * returns 0
-+ */
-+static int yt8521_adjust_status(struct phy_device *phydev, int status,
-+ bool is_utp)
-+{
-+ int speed_mode, duplex;
-+ int speed;
-+ int err;
-+ int lpa;
-+
-+ if (is_utp)
-+ duplex = (status & YTPHY_SSR_DUPLEX) >> YTPHY_SSR_DUPLEX_OFFSET;
-+ else
-+ duplex = DUPLEX_FULL; /* for fiber, it always DUPLEX_FULL */
-+
-+ speed_mode = (status & YTPHY_SSR_SPEED_MODE_MASK) >>
-+ YTPHY_SSR_SPEED_MODE_OFFSET;
-+
-+ switch (speed_mode) {
-+ case YTPHY_SSR_SPEED_10M:
-+ if (is_utp)
-+ speed = SPEED_10;
-+ else
-+ /* for fiber, it will never run here, default to
-+ * SPEED_UNKNOWN
-+ */
-+ speed = SPEED_UNKNOWN;
-+ break;
-+ case YTPHY_SSR_SPEED_100M:
-+ speed = SPEED_100;
-+ break;
-+ case YTPHY_SSR_SPEED_1000M:
-+ speed = SPEED_1000;
-+ break;
-+ default:
-+ speed = SPEED_UNKNOWN;
-+ break;
-+ }
-+
-+ phydev->speed = speed;
-+ phydev->duplex = duplex;
-+
-+ if (is_utp) {
-+ err = ytphy_utp_read_lpa(phydev);
-+ if (err < 0)
-+ return err;
-+
-+ phy_resolve_aneg_pause(phydev);
-+ } else {
-+ lpa = __phy_read(phydev, MII_LPA);
-+ if (lpa < 0)
-+ return lpa;
-+
-+ /* only support 1000baseX Full */
-+ linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
-+ phydev->lp_advertising, lpa & LPA_1000XFULL);
-+
-+ if (!(lpa & YTPHY_FLPA_PAUSE)) {
-+ phydev->pause = 0;
-+ phydev->asym_pause = 0;
-+ } else if ((lpa & YTPHY_FLPA_ASYM_PAUSE)) {
-+ phydev->pause = 1;
-+ phydev->asym_pause = 1;
-+ } else {
-+ phydev->pause = 1;
-+ phydev->asym_pause = 0;
-+ }
-+ }
-+
-+ return 0;
-+}
-+
-+/**
-+ * yt8521_read_status_paged() - determines the speed and duplex of one page
-+ * @phydev: a pointer to a &struct phy_device
-+ * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to
-+ * operate.
-+ *
-+ * returns 1 (utp or fiber link),0 (no link) or negative errno code
-+ */
-+static int yt8521_read_status_paged(struct phy_device *phydev, int page)
-+{
-+ int fiber_latch_val;
-+ int fiber_curr_val;
-+ int old_page;
-+ int ret = 0;
-+ int status;
-+ int link;
-+
-+ linkmode_zero(phydev->lp_advertising);
-+ phydev->duplex = DUPLEX_UNKNOWN;
-+ phydev->speed = SPEED_UNKNOWN;
-+ phydev->asym_pause = 0;
-+ phydev->pause = 0;
-+
-+ /* YT8521 has two reg space (utp/fiber) for linkup with utp/fiber
-+ * respectively. but for utp/fiber combo mode, reg space should be
-+ * arbitrated based on media priority. by default, utp takes
-+ * priority. reg space should be properly set before read
-+ * YTPHY_SPECIFIC_STATUS_REG.
-+ */
-+
-+ page &= YT8521_RSSR_SPACE_MASK;
-+ old_page = phy_select_page(phydev, page);
-+ if (old_page < 0)
-+ goto err_restore_page;
-+
-+ /* Read YTPHY_SPECIFIC_STATUS_REG, which indicates the speed and duplex
-+ * of the PHY is actually using.
-+ */
-+ ret = __phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG);
-+ if (ret < 0)
-+ goto err_restore_page;
-+
-+ status = ret;
-+ link = !!(status & YTPHY_SSR_LINK);
-+
-+ /* When PHY is in fiber mode, speed transferred from 1000Mbps to
-+ * 100Mbps,there is not link down from YTPHY_SPECIFIC_STATUS_REG, so
-+ * we need check MII_BMSR to identify such case.
-+ */
-+ if (page == YT8521_RSSR_FIBER_SPACE) {
-+ ret = __phy_read(phydev, MII_BMSR);
-+ if (ret < 0)
-+ goto err_restore_page;
-+
-+ fiber_latch_val = ret;
-+ ret = __phy_read(phydev, MII_BMSR);
-+ if (ret < 0)
-+ goto err_restore_page;
-+
-+ fiber_curr_val = ret;
-+ if (link && fiber_latch_val != fiber_curr_val) {
-+ link = 0;
-+ phydev_info(phydev,
-+ "%s, fiber link down detect, latch = %04x, curr = %04x\n",
-+ __func__, fiber_latch_val, fiber_curr_val);
-+ }
-+ } else {
-+ /* Read autonegotiation status */
-+ ret = __phy_read(phydev, MII_BMSR);
-+ if (ret < 0)
-+ goto err_restore_page;
-+
-+ phydev->autoneg_complete = ret & BMSR_ANEGCOMPLETE ? 1 : 0;
-+ }
-+
-+ if (link) {
-+ if (page == YT8521_RSSR_UTP_SPACE)
-+ yt8521_adjust_status(phydev, status, true);
-+ else
-+ yt8521_adjust_status(phydev, status, false);
-+ }
-+ return phy_restore_page(phydev, old_page, link);
-+
-+err_restore_page:
-+ return phy_restore_page(phydev, old_page, ret);
-+}
-+
-+/**
-+ * yt8521_read_status() - determines the negotiated speed and duplex
-+ * @phydev: a pointer to a &struct phy_device
-+ *
-+ * returns 0 or negative errno code
-+ */
-+static int yt8521_read_status(struct phy_device *phydev)
-+{
-+ struct yt8521_priv *priv = phydev->priv;
-+ int link_fiber = 0;
-+ int link_utp;
-+ int link;
-+ int ret;
-+
-+ if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) {
-+ link = yt8521_read_status_paged(phydev, priv->reg_page);
-+ if (link < 0)
-+ return link;
-+ } else {
-+ /* when page is YT8521_RSSR_TO_BE_ARBITRATED, arbitration is
-+ * needed. by default, utp is higher priority.
-+ */
-+
-+ link_utp = yt8521_read_status_paged(phydev,
-+ YT8521_RSSR_UTP_SPACE);
-+ if (link_utp < 0)
-+ return link_utp;
-+
-+ if (!link_utp) {
-+ link_fiber = yt8521_read_status_paged(phydev,
-+ YT8521_RSSR_FIBER_SPACE);
-+ if (link_fiber < 0)
-+ return link_fiber;
-+ }
-+
-+ link = link_utp || link_fiber;
-+ }
-+
-+ if (link) {
-+ if (phydev->link == 0) {
-+ /* arbitrate reg space based on linkup media type. */
-+ if (priv->polling_mode == YT8521_MODE_POLL &&
-+ priv->reg_page == YT8521_RSSR_TO_BE_ARBITRATED) {
-+ if (link_fiber)
-+ priv->reg_page =
-+ YT8521_RSSR_FIBER_SPACE;
-+ else
-+ priv->reg_page = YT8521_RSSR_UTP_SPACE;
-+
-+ ret = ytphy_write_ext_with_lock(phydev,
-+ YT8521_REG_SPACE_SELECT_REG,
-+ priv->reg_page);
-+ if (ret < 0)
-+ return ret;
-+
-+ phydev->port = link_fiber ? PORT_FIBRE : PORT_TP;
-+
-+ phydev_info(phydev, "%s, link up, media: %s\n",
-+ __func__,
-+ (phydev->port == PORT_TP) ?
-+ "UTP" : "Fiber");
-+ }
-+ }
-+ phydev->link = 1;
-+ } else {
-+ if (phydev->link == 1) {
-+ phydev_info(phydev, "%s, link down, media: %s\n",
-+ __func__, (phydev->port == PORT_TP) ?
-+ "UTP" : "Fiber");
-+
-+ /* When in YT8521_MODE_POLL mode, need prepare for next
-+ * arbitration.
-+ */
-+ if (priv->polling_mode == YT8521_MODE_POLL) {
-+ priv->reg_page = YT8521_RSSR_TO_BE_ARBITRATED;
-+ phydev->port = PORT_NONE;
-+ }
-+ }
-+
-+ phydev->link = 0;
-+ }
-+
-+ return 0;
-+}
-+
-+/**
-+ * yt8521_modify_bmcr_paged - bits modify a PHY's BMCR register of one page
-+ * @phydev: the phy_device struct
-+ * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to operate
-+ * @mask: bit mask of bits to clear
-+ * @set: bit mask of bits to set
-+ *
-+ * NOTE: Convenience function which allows a PHY's BMCR register to be
-+ * modified as new register value = (old register value & ~mask) | set.
-+ * YT8521 has two space (utp/fiber) and three mode (utp/fiber/poll), each space
-+ * has MII_BMCR. poll mode combines utp and faber,so need do both.
-+ * If it is reset, it will wait for completion.
-+ *
-+ * returns 0 or negative errno code
-+ */
-+static int yt8521_modify_bmcr_paged(struct phy_device *phydev, int page,
-+ u16 mask, u16 set)
-+{
-+ int max_cnt = 500; /* the max wait time of reset ~ 500 ms */
-+ int old_page;
-+ int ret = 0;
-+
-+ old_page = phy_select_page(phydev, page & YT8521_RSSR_SPACE_MASK);
-+ if (old_page < 0)
-+ goto err_restore_page;
-+
-+ ret = __phy_modify(phydev, MII_BMCR, mask, set);
-+ if (ret < 0)
-+ goto err_restore_page;
-+
-+ /* If it is reset, need to wait for the reset to complete */
-+ if (set == BMCR_RESET) {
-+ while (max_cnt--) {
-+ usleep_range(1000, 1100);
-+ ret = __phy_read(phydev, MII_BMCR);
-+ if (ret < 0)
-+ goto err_restore_page;
-+
-+ if (!(ret & BMCR_RESET))
-+ return phy_restore_page(phydev, old_page, 0);
-+ }
-+ }
-+
-+err_restore_page:
-+ return phy_restore_page(phydev, old_page, ret);
-+}
-+
-+/**
-+ * yt8521_modify_utp_fiber_bmcr - bits modify a PHY's BMCR register
-+ * @phydev: the phy_device struct
-+ * @mask: bit mask of bits to clear
-+ * @set: bit mask of bits to set
-+ *
-+ * NOTE: Convenience function which allows a PHY's BMCR register to be
-+ * modified as new register value = (old register value & ~mask) | set.
-+ * YT8521 has two space (utp/fiber) and three mode (utp/fiber/poll), each space
-+ * has MII_BMCR. poll mode combines utp and faber,so need do both.
-+ *
-+ * returns 0 or negative errno code
-+ */
-+static int yt8521_modify_utp_fiber_bmcr(struct phy_device *phydev, u16 mask,
-+ u16 set)
-+{
-+ struct yt8521_priv *priv = phydev->priv;
-+ int ret;
-+
-+ if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) {
-+ ret = yt8521_modify_bmcr_paged(phydev, priv->reg_page, mask,
-+ set);
-+ if (ret < 0)
-+ return ret;
-+ } else {
-+ ret = yt8521_modify_bmcr_paged(phydev, YT8521_RSSR_UTP_SPACE,
-+ mask, set);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = yt8521_modify_bmcr_paged(phydev, YT8521_RSSR_FIBER_SPACE,
-+ mask, set);
-+ if (ret < 0)
-+ return ret;
-+ }
-+ return 0;
-+}
-+
-+/**
-+ * yt8521_soft_reset() - called to issue a PHY software reset
-+ * @phydev: a pointer to a &struct phy_device
-+ *
-+ * returns 0 or negative errno code
-+ */
-+static int yt8521_soft_reset(struct phy_device *phydev)
-+{
-+ return yt8521_modify_utp_fiber_bmcr(phydev, 0, BMCR_RESET);
-+}
-+
-+/**
-+ * yt8521_suspend() - suspend the hardware
-+ * @phydev: a pointer to a &struct phy_device
-+ *
-+ * returns 0 or negative errno code
-+ */
-+static int yt8521_suspend(struct phy_device *phydev)
-+{
-+ int wol_config;
-+
-+ /* YTPHY_WOL_CONFIG_REG is common ext reg */
-+ wol_config = ytphy_read_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG);
-+ if (wol_config < 0)
-+ return wol_config;
-+
-+ /* if wol enable, do nothing */
-+ if (wol_config & YTPHY_WCR_ENABLE)
-+ return 0;
-+
-+ return yt8521_modify_utp_fiber_bmcr(phydev, 0, BMCR_PDOWN);
-+}
-+
-+/**
-+ * yt8521_resume() - resume the hardware
-+ * @phydev: a pointer to a &struct phy_device
-+ *
-+ * returns 0 or negative errno code
-+ */
-+static int yt8521_resume(struct phy_device *phydev)
-+{
-+ int ret;
-+ int wol_config;
-+
-+ /* disable auto sleep */
-+ ret = ytphy_modify_ext_with_lock(phydev,
-+ YT8521_EXTREG_SLEEP_CONTROL1_REG,
-+ YT8521_ESC1R_SLEEP_SW, 0);
-+ if (ret < 0)
-+ return ret;
-+
-+ wol_config = ytphy_read_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG);
-+ if (wol_config < 0)
-+ return wol_config;
-+
-+ /* if wol enable, do nothing */
-+ if (wol_config & YTPHY_WCR_ENABLE)
-+ return 0;
-+
-+ return yt8521_modify_utp_fiber_bmcr(phydev, BMCR_PDOWN, 0);
-+}
-+
-+/**
-+ * yt8521_config_init() - called to initialize the PHY
-+ * @phydev: a pointer to a &struct phy_device
-+ *
-+ * returns 0 or negative errno code
-+ */
-+static int yt8521_config_init(struct phy_device *phydev)
-+{
-+ int old_page;
-+ int ret = 0;
-+ u16 val;
-+
-+ old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE);
-+ if (old_page < 0)
-+ goto err_restore_page;
-+
-+ switch (phydev->interface) {
-+ case PHY_INTERFACE_MODE_RGMII:
-+ val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_GE_TX_DELAY_DIS;
-+ val |= YT8521_RC1R_RX_DELAY_DIS;
-+ break;
-+ case PHY_INTERFACE_MODE_RGMII_RXID:
-+ val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_GE_TX_DELAY_DIS;
-+ val |= YT8521_RC1R_RX_DELAY_EN;
-+ break;
-+ case PHY_INTERFACE_MODE_RGMII_TXID:
-+ val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_GE_TX_DELAY_EN;
-+ val |= YT8521_RC1R_RX_DELAY_DIS;
-+ break;
-+ case PHY_INTERFACE_MODE_RGMII_ID:
-+ val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_GE_TX_DELAY_EN;
-+ val |= YT8521_RC1R_RX_DELAY_EN;
-+ break;
-+ case PHY_INTERFACE_MODE_SGMII:
-+ break;
-+ default: /* do not support other modes */
-+ ret = -EOPNOTSUPP;
-+ goto err_restore_page;
-+ }
-+
-+ /* set rgmii delay mode */
-+ if (phydev->interface != PHY_INTERFACE_MODE_SGMII) {
-+ ret = ytphy_modify_ext(phydev, YT8521_RGMII_CONFIG1_REG,
-+ (YT8521_RC1R_RX_DELAY_MASK |
-+ YT8521_RC1R_FE_TX_DELAY_MASK |
-+ YT8521_RC1R_GE_TX_DELAY_MASK),
-+ val);
-+ if (ret < 0)
-+ goto err_restore_page;
-+ }
-+
-+ /* disable auto sleep */
-+ ret = ytphy_modify_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1_REG,
-+ YT8521_ESC1R_SLEEP_SW, 0);
-+ if (ret < 0)
-+ goto err_restore_page;
-+
-+ /* enable RXC clock when no wire plug */
-+ ret = ytphy_modify_ext(phydev, YT8521_CLOCK_GATING_REG,
-+ YT8521_CGR_RX_CLK_EN, 0);
-+ if (ret < 0)
-+ goto err_restore_page;
-+
-+err_restore_page:
-+ return phy_restore_page(phydev, old_page, ret);
-+}
-+
-+/**
-+ * yt8521_prepare_fiber_features() - A small helper function that setup
-+ * fiber's features.
-+ * @phydev: a pointer to a &struct phy_device
-+ * @dst: a pointer to store fiber's features
-+ */
-+static void yt8521_prepare_fiber_features(struct phy_device *phydev,
-+ unsigned long *dst)
-+{
-+ linkmode_set_bit(ETHTOOL_LINK_MODE_100baseFX_Full_BIT, dst);
-+ linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, dst);
-+ linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, dst);
-+ linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, dst);
-+}
-+
-+/**
-+ * yt8521_fiber_setup_forced - configures/forces speed from @phydev
-+ * @phydev: target phy_device struct
-+ *
-+ * NOTE:The caller must have taken the MDIO bus lock.
-+ *
-+ * returns 0 or negative errno code
-+ */
-+static int yt8521_fiber_setup_forced(struct phy_device *phydev)
-+{
-+ u16 val;
-+ int ret;
-+
-+ if (phydev->speed == SPEED_1000)
-+ val = YTPHY_MCR_FIBER_1000BX;
-+ else if (phydev->speed == SPEED_100)
-+ val = YTPHY_MCR_FIBER_100FX;
-+ else
-+ return -EINVAL;
-+
-+ ret = __phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, 0);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* disable Fiber auto sensing */
-+ ret = ytphy_modify_ext(phydev, YT8521_LINK_TIMER_CFG2_REG,
-+ YT8521_LTCR_EN_AUTOSEN, 0);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = ytphy_modify_ext(phydev, YTPHY_MISC_CONFIG_REG,
-+ YTPHY_MCR_FIBER_SPEED_MASK, val);
-+ if (ret < 0)
-+ return ret;
-+
-+ return ytphy_modify_ext(phydev, YT8521_CHIP_CONFIG_REG,
-+ YT8521_CCR_SW_RST, 0);
-+}
-+
-+/**
-+ * ytphy_check_and_restart_aneg - Enable and restart auto-negotiation
-+ * @phydev: target phy_device struct
-+ * @restart: whether aneg restart is requested
-+ *
-+ * NOTE:The caller must have taken the MDIO bus lock.
-+ *
-+ * returns 0 or negative errno code
-+ */
-+static int ytphy_check_and_restart_aneg(struct phy_device *phydev, bool restart)
-+{
-+ int ret;
-+
-+ if (!restart) {
-+ /* Advertisement hasn't changed, but maybe aneg was never on to
-+ * begin with? Or maybe phy was isolated?
-+ */
-+ ret = __phy_read(phydev, MII_BMCR);
-+ if (ret < 0)
-+ return ret;
-+
-+ if (!(ret & BMCR_ANENABLE) || (ret & BMCR_ISOLATE))
-+ restart = true;
-+ }
-+ /* Enable and Restart Autonegotiation
-+ * Don't isolate the PHY if we're negotiating
-+ */
-+ if (restart)
-+ return __phy_modify(phydev, MII_BMCR, BMCR_ISOLATE,
-+ BMCR_ANENABLE | BMCR_ANRESTART);
-+
-+ return 0;
-+}
-+
-+/**
-+ * yt8521_fiber_config_aneg - restart auto-negotiation or write
-+ * YTPHY_MISC_CONFIG_REG.
-+ * @phydev: target phy_device struct
-+ *
-+ * NOTE:The caller must have taken the MDIO bus lock.
-+ *
-+ * returns 0 or negative errno code
-+ */
-+static int yt8521_fiber_config_aneg(struct phy_device *phydev)
-+{
-+ int err, changed = 0;
-+ int bmcr;
-+ u16 adv;
-+
-+ if (phydev->autoneg != AUTONEG_ENABLE)
-+ return yt8521_fiber_setup_forced(phydev);
-+
-+ /* enable Fiber auto sensing */
-+ err = ytphy_modify_ext(phydev, YT8521_LINK_TIMER_CFG2_REG,
-+ 0, YT8521_LTCR_EN_AUTOSEN);
-+ if (err < 0)
-+ return err;
-+
-+ err = ytphy_modify_ext(phydev, YT8521_CHIP_CONFIG_REG,
-+ YT8521_CCR_SW_RST, 0);
-+ if (err < 0)
-+ return err;
-+
-+ bmcr = __phy_read(phydev, MII_BMCR);
-+ if (bmcr < 0)
-+ return bmcr;
-+
-+ /* When it is coming from fiber forced mode, add bmcr power down
-+ * and power up to let aneg work fine.
-+ */
-+ if (!(bmcr & BMCR_ANENABLE)) {
-+ __phy_modify(phydev, MII_BMCR, 0, BMCR_PDOWN);
-+ usleep_range(1000, 1100);
-+ __phy_modify(phydev, MII_BMCR, BMCR_PDOWN, 0);
-+ }
-+
-+ adv = linkmode_adv_to_mii_adv_x(phydev->advertising,
-+ ETHTOOL_LINK_MODE_1000baseX_Full_BIT);
-+
-+ /* Setup fiber advertisement */
-+ err = __phy_modify_changed(phydev, MII_ADVERTISE,
-+ ADVERTISE_1000XHALF | ADVERTISE_1000XFULL |
-+ ADVERTISE_1000XPAUSE |
-+ ADVERTISE_1000XPSE_ASYM,
-+ adv);
-+ if (err < 0)
-+ return err;
-+
-+ if (err > 0)
-+ changed = 1;
-+
-+ return ytphy_check_and_restart_aneg(phydev, changed);
-+}
-+
-+/**
-+ * ytphy_setup_master_slave
-+ * @phydev: target phy_device struct
-+ *
-+ * NOTE: The caller must have taken the MDIO bus lock.
-+ *
-+ * returns 0 or negative errno code
-+ */
-+static int ytphy_setup_master_slave(struct phy_device *phydev)
-+{
-+ u16 ctl = 0;
-+
-+ if (!phydev->is_gigabit_capable)
-+ return 0;
-+
-+ switch (phydev->master_slave_set) {
-+ case MASTER_SLAVE_CFG_MASTER_PREFERRED:
-+ ctl |= CTL1000_PREFER_MASTER;
-+ break;
-+ case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
-+ break;
-+ case MASTER_SLAVE_CFG_MASTER_FORCE:
-+ ctl |= CTL1000_AS_MASTER;
-+ fallthrough;
-+ case MASTER_SLAVE_CFG_SLAVE_FORCE:
-+ ctl |= CTL1000_ENABLE_MASTER;
-+ break;
-+ case MASTER_SLAVE_CFG_UNKNOWN:
-+ case MASTER_SLAVE_CFG_UNSUPPORTED:
-+ return 0;
-+ default:
-+ phydev_warn(phydev, "Unsupported Master/Slave mode\n");
-+ return -EOPNOTSUPP;
-+ }
-+
-+ return __phy_modify_changed(phydev, MII_CTRL1000,
-+ (CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER |
-+ CTL1000_PREFER_MASTER), ctl);
-+}
-+
-+/**
-+ * ytphy_utp_config_advert - sanitize and advertise auto-negotiation parameters
-+ * @phydev: target phy_device struct
-+ *
-+ * NOTE: Writes MII_ADVERTISE with the appropriate values,
-+ * after sanitizing the values to make sure we only advertise
-+ * what is supported. Returns < 0 on error, 0 if the PHY's advertisement
-+ * hasn't changed, and > 0 if it has changed.
-+ * The caller must have taken the MDIO bus lock.
-+ *
-+ * returns 0 or negative errno code
-+ */
-+static int ytphy_utp_config_advert(struct phy_device *phydev)
-+{
-+ int err, bmsr, changed = 0;
-+ u32 adv;
-+
-+ /* Only allow advertising what this PHY supports */
-+ linkmode_and(phydev->advertising, phydev->advertising,
-+ phydev->supported);
-+
-+ adv = linkmode_adv_to_mii_adv_t(phydev->advertising);
-+
-+ /* Setup standard advertisement */
-+ err = __phy_modify_changed(phydev, MII_ADVERTISE,
-+ ADVERTISE_ALL | ADVERTISE_100BASE4 |
-+ ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM,
-+ adv);
-+ if (err < 0)
-+ return err;
-+ if (err > 0)
-+ changed = 1;
-+
-+ bmsr = __phy_read(phydev, MII_BMSR);
-+ if (bmsr < 0)
-+ return bmsr;
-+
-+ /* Per 802.3-2008, Section 22.2.4.2.16 Extended status all
-+ * 1000Mbits/sec capable PHYs shall have the BMSR_ESTATEN bit set to a
-+ * logical 1.
-+ */
-+ if (!(bmsr & BMSR_ESTATEN))
-+ return changed;
-+
-+ adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
-+
-+ err = __phy_modify_changed(phydev, MII_CTRL1000,
-+ ADVERTISE_1000FULL | ADVERTISE_1000HALF,
-+ adv);
-+ if (err < 0)
-+ return err;
-+ if (err > 0)
-+ changed = 1;
-+
-+ return changed;
-+}
-+
-+/**
-+ * ytphy_utp_config_aneg - restart auto-negotiation or write BMCR
-+ * @phydev: target phy_device struct
-+ * @changed: whether autoneg is requested
-+ *
-+ * NOTE: If auto-negotiation is enabled, we configure the
-+ * advertising, and then restart auto-negotiation. If it is not
-+ * enabled, then we write the BMCR.
-+ * The caller must have taken the MDIO bus lock.
-+ *
-+ * returns 0 or negative errno code
-+ */
-+static int ytphy_utp_config_aneg(struct phy_device *phydev, bool changed)
-+{
-+ int err;
-+ u16 ctl;
-+
-+ err = ytphy_setup_master_slave(phydev);
-+ if (err < 0)
-+ return err;
-+ else if (err)
-+ changed = true;
-+
-+ if (phydev->autoneg != AUTONEG_ENABLE) {
-+ /* configures/forces speed/duplex from @phydev */
-+
-+ ctl = mii_bmcr_encode_fixed(phydev->speed, phydev->duplex);
-+
-+ return __phy_modify(phydev, MII_BMCR, ~(BMCR_LOOPBACK |
-+ BMCR_ISOLATE | BMCR_PDOWN), ctl);
-+ }
-+
-+ err = ytphy_utp_config_advert(phydev);
-+ if (err < 0) /* error */
-+ return err;
-+ else if (err)
-+ changed = true;
-+
-+ return ytphy_check_and_restart_aneg(phydev, changed);
-+}
-+
-+/**
-+ * yt8521_config_aneg_paged() - switch reg space then call genphy_config_aneg
-+ * of one page
-+ * @phydev: a pointer to a &struct phy_device
-+ * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to
-+ * operate.
-+ *
-+ * returns 0 or negative errno code
-+ */
-+static int yt8521_config_aneg_paged(struct phy_device *phydev, int page)
-+{
-+ __ETHTOOL_DECLARE_LINK_MODE_MASK(fiber_supported);
-+ struct yt8521_priv *priv = phydev->priv;
-+ int old_page;
-+ int ret = 0;
-+
-+ page &= YT8521_RSSR_SPACE_MASK;
-+
-+ old_page = phy_select_page(phydev, page);
-+ if (old_page < 0)
-+ goto err_restore_page;
-+
-+ /* If reg_page is YT8521_RSSR_TO_BE_ARBITRATED,
-+ * phydev->advertising should be updated.
-+ */
-+ if (priv->reg_page == YT8521_RSSR_TO_BE_ARBITRATED) {
-+ linkmode_zero(fiber_supported);
-+ yt8521_prepare_fiber_features(phydev, fiber_supported);
-+
-+ /* prepare fiber_supported, then setup advertising. */
-+ if (page == YT8521_RSSR_FIBER_SPACE) {
-+ linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT,
-+ fiber_supported);
-+ linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
-+ fiber_supported);
-+ linkmode_and(phydev->advertising,
-+ priv->combo_advertising, fiber_supported);
-+ } else {
-+ /* ETHTOOL_LINK_MODE_Autoneg_BIT is also used in utp */
-+ linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
-+ fiber_supported);
-+ linkmode_andnot(phydev->advertising,
-+ priv->combo_advertising,
-+ fiber_supported);
-+ }
-+ }
-+
-+ if (page == YT8521_RSSR_FIBER_SPACE)
-+ ret = yt8521_fiber_config_aneg(phydev);
-+ else
-+ ret = ytphy_utp_config_aneg(phydev, false);
-+
-+err_restore_page:
-+ return phy_restore_page(phydev, old_page, ret);
-+}
-+
-+/**
-+ * yt8521_config_aneg() - change reg space then call yt8521_config_aneg_paged
-+ * @phydev: a pointer to a &struct phy_device
-+ *
-+ * returns 0 or negative errno code
-+ */
-+static int yt8521_config_aneg(struct phy_device *phydev)
-+{
-+ struct yt8521_priv *priv = phydev->priv;
-+ int ret;
-+
-+ if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) {
-+ ret = yt8521_config_aneg_paged(phydev, priv->reg_page);
-+ if (ret < 0)
-+ return ret;
-+ } else {
-+ /* If reg_page is YT8521_RSSR_TO_BE_ARBITRATED,
-+ * phydev->advertising need to be saved at first run.
-+ * Because it contains the advertising which supported by both
-+ * mac and yt8521(utp and fiber).
-+ */
-+ if (linkmode_empty(priv->combo_advertising)) {
-+ linkmode_copy(priv->combo_advertising,
-+ phydev->advertising);
-+ }
-+
-+ ret = yt8521_config_aneg_paged(phydev, YT8521_RSSR_UTP_SPACE);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = yt8521_config_aneg_paged(phydev, YT8521_RSSR_FIBER_SPACE);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* we don't known which will be link, so restore
-+ * phydev->advertising as default value.
-+ */
-+ linkmode_copy(phydev->advertising, priv->combo_advertising);
-+ }
-+ return 0;
-+}
-+
-+/**
-+ * yt8521_aneg_done_paged() - determines the auto negotiation result of one
-+ * page.
-+ * @phydev: a pointer to a &struct phy_device
-+ * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to
-+ * operate.
-+ *
-+ * returns 0(no link)or 1(fiber or utp link) or negative errno code
-+ */
-+static int yt8521_aneg_done_paged(struct phy_device *phydev, int page)
-+{
-+ int old_page;
-+ int ret = 0;
-+ int link;
-+
-+ old_page = phy_select_page(phydev, page & YT8521_RSSR_SPACE_MASK);
-+ if (old_page < 0)
-+ goto err_restore_page;
-+
-+ ret = __phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG);
-+ if (ret < 0)
-+ goto err_restore_page;
-+
-+ link = !!(ret & YTPHY_SSR_LINK);
-+ ret = link;
-+
-+err_restore_page:
-+ return phy_restore_page(phydev, old_page, ret);
-+}
-+
-+/**
-+ * yt8521_aneg_done() - determines the auto negotiation result
-+ * @phydev: a pointer to a &struct phy_device
-+ *
-+ * returns 0(no link)or 1(fiber or utp link) or negative errno code
-+ */
-+static int yt8521_aneg_done(struct phy_device *phydev)
-+{
-+ struct yt8521_priv *priv = phydev->priv;
-+ int link_fiber = 0;
-+ int link_utp;
-+ int link;
-+
-+ if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) {
-+ link = yt8521_aneg_done_paged(phydev, priv->reg_page);
-+ } else {
-+ link_utp = yt8521_aneg_done_paged(phydev,
-+ YT8521_RSSR_UTP_SPACE);
-+ if (link_utp < 0)
-+ return link_utp;
-+
-+ if (!link_utp) {
-+ link_fiber = yt8521_aneg_done_paged(phydev,
-+ YT8521_RSSR_FIBER_SPACE);
-+ if (link_fiber < 0)
-+ return link_fiber;
-+ }
-+ link = link_fiber || link_utp;
-+ phydev_info(phydev, "%s, link_fiber: %d, link_utp: %d\n",
-+ __func__, link_fiber, link_utp);
-+ }
-+
-+ return link;
-+}
-+
-+/**
-+ * ytphy_utp_read_abilities - read PHY abilities from Clause 22 registers
-+ * @phydev: target phy_device struct
-+ *
-+ * NOTE: Reads the PHY's abilities and populates
-+ * phydev->supported accordingly.
-+ * The caller must have taken the MDIO bus lock.
-+ *
-+ * returns 0 or negative errno code
-+ */
-+static int ytphy_utp_read_abilities(struct phy_device *phydev)
-+{
-+ int val;
-+
-+ linkmode_set_bit_array(phy_basic_ports_array,
-+ ARRAY_SIZE(phy_basic_ports_array),
-+ phydev->supported);
-+
-+ val = __phy_read(phydev, MII_BMSR);
-+ if (val < 0)
-+ return val;
-+
-+ linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported,
-+ val & BMSR_ANEGCAPABLE);
-+
-+ linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, phydev->supported,
-+ val & BMSR_100FULL);
-+ linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, phydev->supported,
-+ val & BMSR_100HALF);
-+ linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, phydev->supported,
-+ val & BMSR_10FULL);
-+ linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, phydev->supported,
-+ val & BMSR_10HALF);
-+
-+ if (val & BMSR_ESTATEN) {
-+ val = __phy_read(phydev, MII_ESTATUS);
-+ if (val < 0)
-+ return val;
-+
-+ linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
-+ phydev->supported, val & ESTATUS_1000_TFULL);
-+ linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
-+ phydev->supported, val & ESTATUS_1000_THALF);
-+ linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
-+ phydev->supported, val & ESTATUS_1000_XFULL);
-+ }
-+
-+ return 0;
-+}
-+
-+/**
-+ * yt8521_get_features_paged() - read supported link modes for one page
-+ * @phydev: a pointer to a &struct phy_device
-+ * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to
-+ * operate.
-+ *
-+ * returns 0 or negative errno code
-+ */
-+static int yt8521_get_features_paged(struct phy_device *phydev, int page)
-+{
-+ int old_page;
-+ int ret = 0;
-+
-+ page &= YT8521_RSSR_SPACE_MASK;
-+ old_page = phy_select_page(phydev, page);
-+ if (old_page < 0)
-+ goto err_restore_page;
-+
-+ if (page == YT8521_RSSR_FIBER_SPACE) {
-+ linkmode_zero(phydev->supported);
-+ yt8521_prepare_fiber_features(phydev, phydev->supported);
-+ } else {
-+ ret = ytphy_utp_read_abilities(phydev);
-+ if (ret < 0)
-+ goto err_restore_page;
-+ }
-+
-+err_restore_page:
-+ return phy_restore_page(phydev, old_page, ret);
-+}
-+
-+/**
-+ * yt8521_get_features - switch reg space then call yt8521_get_features_paged
-+ * @phydev: target phy_device struct
-+ *
-+ * returns 0 or negative errno code
-+ */
-+static int yt8521_get_features(struct phy_device *phydev)
-+{
-+ struct yt8521_priv *priv = phydev->priv;
-+ int ret;
-+
-+ if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) {
-+ ret = yt8521_get_features_paged(phydev, priv->reg_page);
-+ } else {
-+ ret = yt8521_get_features_paged(phydev,
-+ YT8521_RSSR_UTP_SPACE);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* add fiber's features to phydev->supported */
-+ yt8521_prepare_fiber_features(phydev, phydev->supported);
-+ }
-+ return ret;
-+}
-+
- static struct phy_driver motorcomm_phy_drvs[] = {
- {
- PHY_ID_MATCH_EXACT(PHY_ID_YT8511),
-@@ -121,16 +1733,35 @@ static struct phy_driver motorcomm_phy_d
- .read_page = yt8511_read_page,
- .write_page = yt8511_write_page,
- },
-+ {
-+ PHY_ID_MATCH_EXACT(PHY_ID_YT8521),
-+ .name = "YT8521 Gigabit Ethernet",
-+ .get_features = yt8521_get_features,
-+ .probe = yt8521_probe,
-+ .read_page = yt8521_read_page,
-+ .write_page = yt8521_write_page,
-+ .get_wol = ytphy_get_wol,
-+ .set_wol = ytphy_set_wol,
-+ .config_aneg = yt8521_config_aneg,
-+ .aneg_done = yt8521_aneg_done,
-+ .config_init = yt8521_config_init,
-+ .read_status = yt8521_read_status,
-+ .soft_reset = yt8521_soft_reset,
-+ .suspend = yt8521_suspend,
-+ .resume = yt8521_resume,
-+ },
- };
-
- module_phy_driver(motorcomm_phy_drvs);
-
--MODULE_DESCRIPTION("Motorcomm PHY driver");
-+MODULE_DESCRIPTION("Motorcomm 8511/8521 PHY driver");
- MODULE_AUTHOR("Peter Geis");
-+MODULE_AUTHOR("Frank");
- MODULE_LICENSE("GPL");
-
- static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
- { PHY_ID_MATCH_EXACT(PHY_ID_YT8511) },
-+ { PHY_ID_MATCH_EXACT(PHY_ID_YT8521) },
- { /* sentinal */ }
- };
-
+++ /dev/null
-From 4e0243e7128c9b25ea2739136076a95d6adaba5e Mon Sep 17 00:00:00 2001
-From: Frank <Frank.Sae@motor-comm.com>
-Date: Fri, 4 Nov 2022 16:44:41 +0800
-Subject: [PATCH] net: phy: fix yt8521 duplicated argument to & or |
-
-cocci warnings: (new ones prefixed by >>)
->> drivers/net/phy/motorcomm.c:1122:8-35: duplicated argument to & or |
- drivers/net/phy/motorcomm.c:1126:8-35: duplicated argument to & or |
- drivers/net/phy/motorcomm.c:1130:8-34: duplicated argument to & or |
- drivers/net/phy/motorcomm.c:1134:8-34: duplicated argument to & or |
-
- The second YT8521_RC1R_GE_TX_DELAY_xx should be YT8521_RC1R_FE_TX_DELAY_xx.
-
-Fixes: 70479a40954c ("net: phy: Add driver for Motorcomm yt8521 gigabit ethernet phy")
-Reported-by: kernel test robot <lkp@intel.com>
-Reported-by: Julia Lawall <julia.lawall@lip6.fr>
-Signed-off-by: Frank <Frank.Sae@motor-comm.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/motorcomm.c | 8 ++++----
- 1 file changed, 4 insertions(+), 4 deletions(-)
-
---- a/drivers/net/phy/motorcomm.c
-+++ b/drivers/net/phy/motorcomm.c
-@@ -1119,19 +1119,19 @@ static int yt8521_config_init(struct phy
-
- switch (phydev->interface) {
- case PHY_INTERFACE_MODE_RGMII:
-- val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_GE_TX_DELAY_DIS;
-+ val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_FE_TX_DELAY_DIS;
- val |= YT8521_RC1R_RX_DELAY_DIS;
- break;
- case PHY_INTERFACE_MODE_RGMII_RXID:
-- val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_GE_TX_DELAY_DIS;
-+ val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_FE_TX_DELAY_DIS;
- val |= YT8521_RC1R_RX_DELAY_EN;
- break;
- case PHY_INTERFACE_MODE_RGMII_TXID:
-- val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_GE_TX_DELAY_EN;
-+ val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_FE_TX_DELAY_EN;
- val |= YT8521_RC1R_RX_DELAY_DIS;
- break;
- case PHY_INTERFACE_MODE_RGMII_ID:
-- val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_GE_TX_DELAY_EN;
-+ val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_FE_TX_DELAY_EN;
- val |= YT8521_RC1R_RX_DELAY_EN;
- break;
- case PHY_INTERFACE_MODE_SGMII:
+++ /dev/null
-From 813abcd98fb1b2cccf850cdfa092a4bfc50b2363 Mon Sep 17 00:00:00 2001
-From: Frank <Frank.Sae@motor-comm.com>
-Date: Tue, 22 Nov 2022 16:42:32 +0800
-Subject: [PATCH] net: phy: add Motorcomm YT8531S phy id.
-
-We added patch for motorcomm.c to support YT8531S. This patch has
-been tested on AM335x platform which has one YT8531S interface
-card and passed all test cases.
-The tested cases indluding: YT8531S UTP function with support of
-10M/100M/1000M; YT8531S Fiber function with support of 100M/1000M;
-and YT8531S Combo function that supports auto detection of media type.
-
-Since most functions of YT8531S are similar to YT8521 and we reuse some
-codes for YT8521 in the patch file.
-
-Signed-off-by: Frank <Frank.Sae@motor-comm.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/Kconfig | 2 +-
- drivers/net/phy/motorcomm.c | 52 +++++++++++++++++++++++++++++++++----
- 2 files changed, 48 insertions(+), 6 deletions(-)
-
---- a/drivers/net/phy/Kconfig
-+++ b/drivers/net/phy/Kconfig
-@@ -257,7 +257,7 @@ config MOTORCOMM_PHY
- tristate "Motorcomm PHYs"
- help
- Enables support for Motorcomm network PHYs.
-- Currently supports the YT8511, YT8521 Gigabit Ethernet PHYs.
-+ Currently supports the YT8511, YT8521, YT8531S Gigabit Ethernet PHYs.
-
- config NATIONAL_PHY
- tristate "National Semiconductor PHYs"
---- a/drivers/net/phy/motorcomm.c
-+++ b/drivers/net/phy/motorcomm.c
-@@ -1,6 +1,6 @@
- // SPDX-License-Identifier: GPL-2.0+
- /*
-- * Motorcomm 8511/8521 PHY driver.
-+ * Motorcomm 8511/8521/8531S PHY driver.
- *
- * Author: Peter Geis <pgwipeout@gmail.com>
- * Author: Frank <Frank.Sae@motor-comm.com>
-@@ -12,9 +12,10 @@
- #include <linux/phy.h>
-
- #define PHY_ID_YT8511 0x0000010a
--#define PHY_ID_YT8521 0x0000011A
-+#define PHY_ID_YT8521 0x0000011A
-+#define PHY_ID_YT8531S 0x4F51E91A
-
--/* YT8521 Register Overview
-+/* YT8521/YT8531S Register Overview
- * UTP Register space | FIBER Register space
- * ------------------------------------------------------------
- * | UTP MII | FIBER MII |
-@@ -147,7 +148,7 @@
- #define YT8521_LINK_TIMER_CFG2_REG 0xA5
- #define YT8521_LTCR_EN_AUTOSEN BIT(15)
-
--/* 0xA000, 0xA001, 0xA003 ,and 0xA006 ~ 0xA00A are common ext registers
-+/* 0xA000, 0xA001, 0xA003, 0xA006 ~ 0xA00A and 0xA012 are common ext registers
- * of yt8521 phy. There is no need to switch reg space when operating these
- * registers.
- */
-@@ -221,6 +222,9 @@
- */
- #define YTPHY_WCR_TYPE_PULSE BIT(0)
-
-+#define YT8531S_SYNCE_CFG_REG 0xA012
-+#define YT8531S_SCR_SYNCE_ENABLE BIT(6)
-+
- /* Extended Register end */
-
- struct yt8521_priv {
-@@ -648,6 +652,26 @@ static int yt8521_probe(struct phy_devic
- }
-
- /**
-+ * yt8531s_probe() - read chip config then set suitable polling_mode
-+ * @phydev: a pointer to a &struct phy_device
-+ *
-+ * returns 0 or negative errno code
-+ */
-+static int yt8531s_probe(struct phy_device *phydev)
-+{
-+ int ret;
-+
-+ /* Disable SyncE clock output by default */
-+ ret = ytphy_modify_ext_with_lock(phydev, YT8531S_SYNCE_CFG_REG,
-+ YT8531S_SCR_SYNCE_ENABLE, 0);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* same as yt8521_probe */
-+ return yt8521_probe(phydev);
-+}
-+
-+/**
- * ytphy_utp_read_lpa() - read LPA then setup lp_advertising for utp
- * @phydev: a pointer to a &struct phy_device
- *
-@@ -1750,11 +1774,28 @@ static struct phy_driver motorcomm_phy_d
- .suspend = yt8521_suspend,
- .resume = yt8521_resume,
- },
-+ {
-+ PHY_ID_MATCH_EXACT(PHY_ID_YT8531S),
-+ .name = "YT8531S Gigabit Ethernet",
-+ .get_features = yt8521_get_features,
-+ .probe = yt8531s_probe,
-+ .read_page = yt8521_read_page,
-+ .write_page = yt8521_write_page,
-+ .get_wol = ytphy_get_wol,
-+ .set_wol = ytphy_set_wol,
-+ .config_aneg = yt8521_config_aneg,
-+ .aneg_done = yt8521_aneg_done,
-+ .config_init = yt8521_config_init,
-+ .read_status = yt8521_read_status,
-+ .soft_reset = yt8521_soft_reset,
-+ .suspend = yt8521_suspend,
-+ .resume = yt8521_resume,
-+ },
- };
-
- module_phy_driver(motorcomm_phy_drvs);
-
--MODULE_DESCRIPTION("Motorcomm 8511/8521 PHY driver");
-+MODULE_DESCRIPTION("Motorcomm 8511/8521/8531S PHY driver");
- MODULE_AUTHOR("Peter Geis");
- MODULE_AUTHOR("Frank");
- MODULE_LICENSE("GPL");
-@@ -1762,6 +1803,7 @@ MODULE_LICENSE("GPL");
- static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
- { PHY_ID_MATCH_EXACT(PHY_ID_YT8511) },
- { PHY_ID_MATCH_EXACT(PHY_ID_YT8521) },
-+ { PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) },
- { /* sentinal */ }
- };
-
+++ /dev/null
-From 4104a713204d62aca482eebb0c6226d82a0721eb Mon Sep 17 00:00:00 2001
-From: Frank Sae <Frank.Sae@motor-comm.com>
-Date: Sat, 28 Jan 2023 14:35:57 +0800
-Subject: [PATCH] net: phy: fix the spelling problem of Sentinel
-
-CHECK: 'sentinal' may be misspelled - perhaps 'sentinel'?
-
-Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/20230128063558.5850-1-Frank.Sae@motor-comm.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/motorcomm.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/net/phy/motorcomm.c
-+++ b/drivers/net/phy/motorcomm.c
-@@ -1804,7 +1804,7 @@ static const struct mdio_device_id __may
- { PHY_ID_MATCH_EXACT(PHY_ID_YT8511) },
- { PHY_ID_MATCH_EXACT(PHY_ID_YT8521) },
- { PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) },
-- { /* sentinal */ }
-+ { /* sentinel */ }
- };
-
- MODULE_DEVICE_TABLE(mdio, motorcomm_tbl);
+++ /dev/null
-From 3c1dc22162d673d595855d24f95200ed2643f88f Mon Sep 17 00:00:00 2001
-From: Frank Sae <Frank.Sae@motor-comm.com>
-Date: Sat, 28 Jan 2023 14:35:58 +0800
-Subject: [PATCH] net: phy: motorcomm: change the phy id of yt8521 and yt8531s
- to lowercase
-
-The phy id is usually defined in lower case.
-
-Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/20230128063558.5850-2-Frank.Sae@motor-comm.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/motorcomm.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
---- a/drivers/net/phy/motorcomm.c
-+++ b/drivers/net/phy/motorcomm.c
-@@ -12,8 +12,8 @@
- #include <linux/phy.h>
-
- #define PHY_ID_YT8511 0x0000010a
--#define PHY_ID_YT8521 0x0000011A
--#define PHY_ID_YT8531S 0x4F51E91A
-+#define PHY_ID_YT8521 0x0000011a
-+#define PHY_ID_YT8531S 0x4f51e91a
-
- /* YT8521/YT8531S Register Overview
- * UTP Register space | FIBER Register space
+++ /dev/null
-From 4869a146cd60fc8115230f0a45e15e534c531922 Mon Sep 17 00:00:00 2001
-From: Frank Sae <Frank.Sae@motor-comm.com>
-Date: Thu, 2 Feb 2023 11:00:34 +0800
-Subject: [PATCH] net: phy: Add BIT macro for Motorcomm yt8521/yt8531 gigabit
- ethernet phy
-
-Add BIT macro for Motorcomm yt8521/yt8531 gigabit ethernet phy.
- This is a preparatory patch. Add BIT macro for 0xA012 reg, and
- supplement for 0xA001 and 0xA003 reg. These will be used to support dts.
-
-Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/motorcomm.c | 55 ++++++++++++++++++++++++++++++++++---
- 1 file changed, 51 insertions(+), 4 deletions(-)
-
---- a/drivers/net/phy/motorcomm.c
-+++ b/drivers/net/phy/motorcomm.c
-@@ -161,6 +161,11 @@
-
- #define YT8521_CHIP_CONFIG_REG 0xA001
- #define YT8521_CCR_SW_RST BIT(15)
-+/* 1b0 disable 1.9ns rxc clock delay *default*
-+ * 1b1 enable 1.9ns rxc clock delay
-+ */
-+#define YT8521_CCR_RXC_DLY_EN BIT(8)
-+#define YT8521_CCR_RXC_DLY_1_900_NS 1900
-
- #define YT8521_CCR_MODE_SEL_MASK (BIT(2) | BIT(1) | BIT(0))
- #define YT8521_CCR_MODE_UTP_TO_RGMII 0
-@@ -178,22 +183,41 @@
- #define YT8521_MODE_POLL 0x3
-
- #define YT8521_RGMII_CONFIG1_REG 0xA003
--
-+/* 1b0 use original tx_clk_rgmii *default*
-+ * 1b1 use inverted tx_clk_rgmii.
-+ */
-+#define YT8521_RC1R_TX_CLK_SEL_INVERTED BIT(14)
- /* TX Gig-E Delay is bits 3:0, default 0x1
- * TX Fast-E Delay is bits 7:4, default 0xf
- * RX Delay is bits 13:10, default 0x0
- * Delay = 150ps * N
- * On = 2250ps, off = 0ps
- */
--#define YT8521_RC1R_RX_DELAY_MASK (0xF << 10)
-+#define YT8521_RC1R_RX_DELAY_MASK GENMASK(13, 10)
- #define YT8521_RC1R_RX_DELAY_EN (0xF << 10)
- #define YT8521_RC1R_RX_DELAY_DIS (0x0 << 10)
--#define YT8521_RC1R_FE_TX_DELAY_MASK (0xF << 4)
-+#define YT8521_RC1R_FE_TX_DELAY_MASK GENMASK(7, 4)
- #define YT8521_RC1R_FE_TX_DELAY_EN (0xF << 4)
- #define YT8521_RC1R_FE_TX_DELAY_DIS (0x0 << 4)
--#define YT8521_RC1R_GE_TX_DELAY_MASK (0xF << 0)
-+#define YT8521_RC1R_GE_TX_DELAY_MASK GENMASK(3, 0)
- #define YT8521_RC1R_GE_TX_DELAY_EN (0xF << 0)
- #define YT8521_RC1R_GE_TX_DELAY_DIS (0x0 << 0)
-+#define YT8521_RC1R_RGMII_0_000_NS 0
-+#define YT8521_RC1R_RGMII_0_150_NS 1
-+#define YT8521_RC1R_RGMII_0_300_NS 2
-+#define YT8521_RC1R_RGMII_0_450_NS 3
-+#define YT8521_RC1R_RGMII_0_600_NS 4
-+#define YT8521_RC1R_RGMII_0_750_NS 5
-+#define YT8521_RC1R_RGMII_0_900_NS 6
-+#define YT8521_RC1R_RGMII_1_050_NS 7
-+#define YT8521_RC1R_RGMII_1_200_NS 8
-+#define YT8521_RC1R_RGMII_1_350_NS 9
-+#define YT8521_RC1R_RGMII_1_500_NS 10
-+#define YT8521_RC1R_RGMII_1_650_NS 11
-+#define YT8521_RC1R_RGMII_1_800_NS 12
-+#define YT8521_RC1R_RGMII_1_950_NS 13
-+#define YT8521_RC1R_RGMII_2_100_NS 14
-+#define YT8521_RC1R_RGMII_2_250_NS 15
-
- #define YTPHY_MISC_CONFIG_REG 0xA006
- #define YTPHY_MCR_FIBER_SPEED_MASK BIT(0)
-@@ -222,6 +246,29 @@
- */
- #define YTPHY_WCR_TYPE_PULSE BIT(0)
-
-+#define YTPHY_SYNCE_CFG_REG 0xA012
-+#define YT8521_SCR_SYNCE_ENABLE BIT(5)
-+/* 1b0 output 25m clock
-+ * 1b1 output 125m clock *default*
-+ */
-+#define YT8521_SCR_CLK_FRE_SEL_125M BIT(3)
-+#define YT8521_SCR_CLK_SRC_MASK GENMASK(2, 1)
-+#define YT8521_SCR_CLK_SRC_PLL_125M 0
-+#define YT8521_SCR_CLK_SRC_UTP_RX 1
-+#define YT8521_SCR_CLK_SRC_SDS_RX 2
-+#define YT8521_SCR_CLK_SRC_REF_25M 3
-+#define YT8531_SCR_SYNCE_ENABLE BIT(6)
-+/* 1b0 output 25m clock *default*
-+ * 1b1 output 125m clock
-+ */
-+#define YT8531_SCR_CLK_FRE_SEL_125M BIT(4)
-+#define YT8531_SCR_CLK_SRC_MASK GENMASK(3, 1)
-+#define YT8531_SCR_CLK_SRC_PLL_125M 0
-+#define YT8531_SCR_CLK_SRC_UTP_RX 1
-+#define YT8531_SCR_CLK_SRC_SDS_RX 2
-+#define YT8531_SCR_CLK_SRC_CLOCK_FROM_DIGITAL 3
-+#define YT8531_SCR_CLK_SRC_REF_25M 4
-+#define YT8531_SCR_CLK_SRC_SSC_25M 5
- #define YT8531S_SYNCE_CFG_REG 0xA012
- #define YT8531S_SCR_SYNCE_ENABLE BIT(6)
-
+++ /dev/null
-From a6e68f0f8769f79c67cdcfb6302feecd36197dec Mon Sep 17 00:00:00 2001
-From: Frank Sae <Frank.Sae@motor-comm.com>
-Date: Thu, 2 Feb 2023 11:00:35 +0800
-Subject: [PATCH] net: phy: Add dts support for Motorcomm yt8521 gigabit
- ethernet phy
-
-Add dts support for Motorcomm yt8521 gigabit ethernet phy.
- Add ytphy_rgmii_clk_delay_config function to support dst config for
- the delay of rgmii clk. This funciont is common for yt8521, yt8531s
- and yt8531.
- This patch has been verified on AM335x platform.
-
-Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/motorcomm.c | 253 ++++++++++++++++++++++++++++--------
- 1 file changed, 199 insertions(+), 54 deletions(-)
-
---- a/drivers/net/phy/motorcomm.c
-+++ b/drivers/net/phy/motorcomm.c
-@@ -10,6 +10,7 @@
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/phy.h>
-+#include <linux/of.h>
-
- #define PHY_ID_YT8511 0x0000010a
- #define PHY_ID_YT8521 0x0000011a
-@@ -187,21 +188,9 @@
- * 1b1 use inverted tx_clk_rgmii.
- */
- #define YT8521_RC1R_TX_CLK_SEL_INVERTED BIT(14)
--/* TX Gig-E Delay is bits 3:0, default 0x1
-- * TX Fast-E Delay is bits 7:4, default 0xf
-- * RX Delay is bits 13:10, default 0x0
-- * Delay = 150ps * N
-- * On = 2250ps, off = 0ps
-- */
- #define YT8521_RC1R_RX_DELAY_MASK GENMASK(13, 10)
--#define YT8521_RC1R_RX_DELAY_EN (0xF << 10)
--#define YT8521_RC1R_RX_DELAY_DIS (0x0 << 10)
- #define YT8521_RC1R_FE_TX_DELAY_MASK GENMASK(7, 4)
--#define YT8521_RC1R_FE_TX_DELAY_EN (0xF << 4)
--#define YT8521_RC1R_FE_TX_DELAY_DIS (0x0 << 4)
- #define YT8521_RC1R_GE_TX_DELAY_MASK GENMASK(3, 0)
--#define YT8521_RC1R_GE_TX_DELAY_EN (0xF << 0)
--#define YT8521_RC1R_GE_TX_DELAY_DIS (0x0 << 0)
- #define YT8521_RC1R_RGMII_0_000_NS 0
- #define YT8521_RC1R_RGMII_0_150_NS 1
- #define YT8521_RC1R_RGMII_0_300_NS 2
-@@ -274,6 +263,10 @@
-
- /* Extended Register end */
-
-+#define YTPHY_DTS_OUTPUT_CLK_DIS 0
-+#define YTPHY_DTS_OUTPUT_CLK_25M 25000000
-+#define YTPHY_DTS_OUTPUT_CLK_125M 125000000
-+
- struct yt8521_priv {
- /* combo_advertising is used for case of YT8521 in combo mode,
- * this means that yt8521 may work in utp or fiber mode which depends
-@@ -641,6 +634,142 @@ static int yt8521_write_page(struct phy_
- };
-
- /**
-+ * struct ytphy_cfg_reg_map - map a config value to a register value
-+ * @cfg: value in device configuration
-+ * @reg: value in the register
-+ */
-+struct ytphy_cfg_reg_map {
-+ u32 cfg;
-+ u32 reg;
-+};
-+
-+static const struct ytphy_cfg_reg_map ytphy_rgmii_delays[] = {
-+ /* for tx delay / rx delay with YT8521_CCR_RXC_DLY_EN is not set. */
-+ { 0, YT8521_RC1R_RGMII_0_000_NS },
-+ { 150, YT8521_RC1R_RGMII_0_150_NS },
-+ { 300, YT8521_RC1R_RGMII_0_300_NS },
-+ { 450, YT8521_RC1R_RGMII_0_450_NS },
-+ { 600, YT8521_RC1R_RGMII_0_600_NS },
-+ { 750, YT8521_RC1R_RGMII_0_750_NS },
-+ { 900, YT8521_RC1R_RGMII_0_900_NS },
-+ { 1050, YT8521_RC1R_RGMII_1_050_NS },
-+ { 1200, YT8521_RC1R_RGMII_1_200_NS },
-+ { 1350, YT8521_RC1R_RGMII_1_350_NS },
-+ { 1500, YT8521_RC1R_RGMII_1_500_NS },
-+ { 1650, YT8521_RC1R_RGMII_1_650_NS },
-+ { 1800, YT8521_RC1R_RGMII_1_800_NS },
-+ { 1950, YT8521_RC1R_RGMII_1_950_NS }, /* default tx/rx delay */
-+ { 2100, YT8521_RC1R_RGMII_2_100_NS },
-+ { 2250, YT8521_RC1R_RGMII_2_250_NS },
-+
-+ /* only for rx delay with YT8521_CCR_RXC_DLY_EN is set. */
-+ { 0 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_0_000_NS },
-+ { 150 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_0_150_NS },
-+ { 300 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_0_300_NS },
-+ { 450 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_0_450_NS },
-+ { 600 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_0_600_NS },
-+ { 750 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_0_750_NS },
-+ { 900 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_0_900_NS },
-+ { 1050 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_1_050_NS },
-+ { 1200 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_1_200_NS },
-+ { 1350 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_1_350_NS },
-+ { 1500 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_1_500_NS },
-+ { 1650 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_1_650_NS },
-+ { 1800 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_1_800_NS },
-+ { 1950 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_1_950_NS },
-+ { 2100 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_2_100_NS },
-+ { 2250 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_2_250_NS }
-+};
-+
-+static u32 ytphy_get_delay_reg_value(struct phy_device *phydev,
-+ const char *prop_name,
-+ const struct ytphy_cfg_reg_map *tbl,
-+ int tb_size,
-+ u16 *rxc_dly_en,
-+ u32 dflt)
-+{
-+ struct device_node *node = phydev->mdio.dev.of_node;
-+ int tb_size_half = tb_size / 2;
-+ u32 val;
-+ int i;
-+
-+ if (of_property_read_u32(node, prop_name, &val))
-+ goto err_dts_val;
-+
-+ /* when rxc_dly_en is NULL, it is get the delay for tx, only half of
-+ * tb_size is valid.
-+ */
-+ if (!rxc_dly_en)
-+ tb_size = tb_size_half;
-+
-+ for (i = 0; i < tb_size; i++) {
-+ if (tbl[i].cfg == val) {
-+ if (rxc_dly_en && i < tb_size_half)
-+ *rxc_dly_en = 0;
-+ return tbl[i].reg;
-+ }
-+ }
-+
-+ phydev_warn(phydev, "Unsupported value %d for %s using default (%u)\n",
-+ val, prop_name, dflt);
-+
-+err_dts_val:
-+ /* when rxc_dly_en is not NULL, it is get the delay for rx.
-+ * The rx default in dts and ytphy_rgmii_clk_delay_config is 1950 ps,
-+ * so YT8521_CCR_RXC_DLY_EN should not be set.
-+ */
-+ if (rxc_dly_en)
-+ *rxc_dly_en = 0;
-+
-+ return dflt;
-+}
-+
-+static int ytphy_rgmii_clk_delay_config(struct phy_device *phydev)
-+{
-+ int tb_size = ARRAY_SIZE(ytphy_rgmii_delays);
-+ u16 rxc_dly_en = YT8521_CCR_RXC_DLY_EN;
-+ u32 rx_reg, tx_reg;
-+ u16 mask, val = 0;
-+ int ret;
-+
-+ rx_reg = ytphy_get_delay_reg_value(phydev, "rx-internal-delay-ps",
-+ ytphy_rgmii_delays, tb_size,
-+ &rxc_dly_en,
-+ YT8521_RC1R_RGMII_1_950_NS);
-+ tx_reg = ytphy_get_delay_reg_value(phydev, "tx-internal-delay-ps",
-+ ytphy_rgmii_delays, tb_size, NULL,
-+ YT8521_RC1R_RGMII_1_950_NS);
-+
-+ switch (phydev->interface) {
-+ case PHY_INTERFACE_MODE_RGMII:
-+ rxc_dly_en = 0;
-+ break;
-+ case PHY_INTERFACE_MODE_RGMII_RXID:
-+ val |= FIELD_PREP(YT8521_RC1R_RX_DELAY_MASK, rx_reg);
-+ break;
-+ case PHY_INTERFACE_MODE_RGMII_TXID:
-+ rxc_dly_en = 0;
-+ val |= FIELD_PREP(YT8521_RC1R_GE_TX_DELAY_MASK, tx_reg);
-+ break;
-+ case PHY_INTERFACE_MODE_RGMII_ID:
-+ val |= FIELD_PREP(YT8521_RC1R_RX_DELAY_MASK, rx_reg) |
-+ FIELD_PREP(YT8521_RC1R_GE_TX_DELAY_MASK, tx_reg);
-+ break;
-+ default: /* do not support other modes */
-+ return -EOPNOTSUPP;
-+ }
-+
-+ ret = ytphy_modify_ext(phydev, YT8521_CHIP_CONFIG_REG,
-+ YT8521_CCR_RXC_DLY_EN, rxc_dly_en);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* Generally, it is not necessary to adjust YT8521_RC1R_FE_TX_DELAY */
-+ mask = YT8521_RC1R_RX_DELAY_MASK | YT8521_RC1R_GE_TX_DELAY_MASK;
-+ return ytphy_modify_ext(phydev, YT8521_RGMII_CONFIG1_REG, mask, val);
-+}
-+
-+/**
- * yt8521_probe() - read chip config then set suitable polling_mode
- * @phydev: a pointer to a &struct phy_device
- *
-@@ -648,9 +777,12 @@ static int yt8521_write_page(struct phy_
- */
- static int yt8521_probe(struct phy_device *phydev)
- {
-+ struct device_node *node = phydev->mdio.dev.of_node;
- struct device *dev = &phydev->mdio.dev;
- struct yt8521_priv *priv;
- int chip_config;
-+ u16 mask, val;
-+ u32 freq;
- int ret;
-
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
-@@ -695,7 +827,45 @@ static int yt8521_probe(struct phy_devic
- return ret;
- }
-
-- return 0;
-+ if (of_property_read_u32(node, "motorcomm,clk-out-frequency-hz", &freq))
-+ freq = YTPHY_DTS_OUTPUT_CLK_DIS;
-+
-+ if (phydev->drv->phy_id == PHY_ID_YT8521) {
-+ switch (freq) {
-+ case YTPHY_DTS_OUTPUT_CLK_DIS:
-+ mask = YT8521_SCR_SYNCE_ENABLE;
-+ val = 0;
-+ break;
-+ case YTPHY_DTS_OUTPUT_CLK_25M:
-+ mask = YT8521_SCR_SYNCE_ENABLE |
-+ YT8521_SCR_CLK_SRC_MASK |
-+ YT8521_SCR_CLK_FRE_SEL_125M;
-+ val = YT8521_SCR_SYNCE_ENABLE |
-+ FIELD_PREP(YT8521_SCR_CLK_SRC_MASK,
-+ YT8521_SCR_CLK_SRC_REF_25M);
-+ break;
-+ case YTPHY_DTS_OUTPUT_CLK_125M:
-+ mask = YT8521_SCR_SYNCE_ENABLE |
-+ YT8521_SCR_CLK_SRC_MASK |
-+ YT8521_SCR_CLK_FRE_SEL_125M;
-+ val = YT8521_SCR_SYNCE_ENABLE |
-+ YT8521_SCR_CLK_FRE_SEL_125M |
-+ FIELD_PREP(YT8521_SCR_CLK_SRC_MASK,
-+ YT8521_SCR_CLK_SRC_PLL_125M);
-+ break;
-+ default:
-+ phydev_warn(phydev, "Freq err:%u\n", freq);
-+ return -EINVAL;
-+ }
-+ } else if (phydev->drv->phy_id == PHY_ID_YT8531S) {
-+ return 0;
-+ } else {
-+ phydev_warn(phydev, "PHY id err\n");
-+ return -EINVAL;
-+ }
-+
-+ return ytphy_modify_ext_with_lock(phydev, YTPHY_SYNCE_CFG_REG, mask,
-+ val);
- }
-
- /**
-@@ -1180,61 +1350,36 @@ static int yt8521_resume(struct phy_devi
- */
- static int yt8521_config_init(struct phy_device *phydev)
- {
-+ struct device_node *node = phydev->mdio.dev.of_node;
- int old_page;
- int ret = 0;
-- u16 val;
-
- old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE);
- if (old_page < 0)
- goto err_restore_page;
-
-- switch (phydev->interface) {
-- case PHY_INTERFACE_MODE_RGMII:
-- val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_FE_TX_DELAY_DIS;
-- val |= YT8521_RC1R_RX_DELAY_DIS;
-- break;
-- case PHY_INTERFACE_MODE_RGMII_RXID:
-- val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_FE_TX_DELAY_DIS;
-- val |= YT8521_RC1R_RX_DELAY_EN;
-- break;
-- case PHY_INTERFACE_MODE_RGMII_TXID:
-- val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_FE_TX_DELAY_EN;
-- val |= YT8521_RC1R_RX_DELAY_DIS;
-- break;
-- case PHY_INTERFACE_MODE_RGMII_ID:
-- val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_FE_TX_DELAY_EN;
-- val |= YT8521_RC1R_RX_DELAY_EN;
-- break;
-- case PHY_INTERFACE_MODE_SGMII:
-- break;
-- default: /* do not support other modes */
-- ret = -EOPNOTSUPP;
-- goto err_restore_page;
-- }
--
- /* set rgmii delay mode */
- if (phydev->interface != PHY_INTERFACE_MODE_SGMII) {
-- ret = ytphy_modify_ext(phydev, YT8521_RGMII_CONFIG1_REG,
-- (YT8521_RC1R_RX_DELAY_MASK |
-- YT8521_RC1R_FE_TX_DELAY_MASK |
-- YT8521_RC1R_GE_TX_DELAY_MASK),
-- val);
-+ ret = ytphy_rgmii_clk_delay_config(phydev);
- if (ret < 0)
- goto err_restore_page;
- }
-
-- /* disable auto sleep */
-- ret = ytphy_modify_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1_REG,
-- YT8521_ESC1R_SLEEP_SW, 0);
-- if (ret < 0)
-- goto err_restore_page;
--
-- /* enable RXC clock when no wire plug */
-- ret = ytphy_modify_ext(phydev, YT8521_CLOCK_GATING_REG,
-- YT8521_CGR_RX_CLK_EN, 0);
-- if (ret < 0)
-- goto err_restore_page;
-+ if (of_property_read_bool(node, "motorcomm,auto-sleep-disabled")) {
-+ /* disable auto sleep */
-+ ret = ytphy_modify_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1_REG,
-+ YT8521_ESC1R_SLEEP_SW, 0);
-+ if (ret < 0)
-+ goto err_restore_page;
-+ }
-
-+ if (of_property_read_bool(node, "motorcomm,keep-pll-enabled")) {
-+ /* enable RXC clock when no wire plug */
-+ ret = ytphy_modify_ext(phydev, YT8521_CLOCK_GATING_REG,
-+ YT8521_CGR_RX_CLK_EN, 0);
-+ if (ret < 0)
-+ goto err_restore_page;
-+ }
- err_restore_page:
- return phy_restore_page(phydev, old_page, ret);
- }
+++ /dev/null
-From 36152f87dda4af221b16258751451d9cd3d0fb0b Mon Sep 17 00:00:00 2001
-From: Frank Sae <Frank.Sae@motor-comm.com>
-Date: Thu, 2 Feb 2023 11:00:36 +0800
-Subject: [PATCH] net: phy: Add dts support for Motorcomm yt8531s gigabit
- ethernet phy
-
-Add dts support for Motorcomm yt8531s gigabit ethernet phy.
- Change yt8521_probe to support clk config of yt8531s. Becase
- yt8521_probe does the things which yt8531s is needed, so
- removed yt8531s function.
- This patch has been verified on AM335x platform with yt8531s board.
-
-Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/motorcomm.c | 51 ++++++++++++++++++++-----------------
- 1 file changed, 27 insertions(+), 24 deletions(-)
-
---- a/drivers/net/phy/motorcomm.c
-+++ b/drivers/net/phy/motorcomm.c
-@@ -258,8 +258,6 @@
- #define YT8531_SCR_CLK_SRC_CLOCK_FROM_DIGITAL 3
- #define YT8531_SCR_CLK_SRC_REF_25M 4
- #define YT8531_SCR_CLK_SRC_SSC_25M 5
--#define YT8531S_SYNCE_CFG_REG 0xA012
--#define YT8531S_SCR_SYNCE_ENABLE BIT(6)
-
- /* Extended Register end */
-
-@@ -858,7 +856,32 @@ static int yt8521_probe(struct phy_devic
- return -EINVAL;
- }
- } else if (phydev->drv->phy_id == PHY_ID_YT8531S) {
-- return 0;
-+ switch (freq) {
-+ case YTPHY_DTS_OUTPUT_CLK_DIS:
-+ mask = YT8531_SCR_SYNCE_ENABLE;
-+ val = 0;
-+ break;
-+ case YTPHY_DTS_OUTPUT_CLK_25M:
-+ mask = YT8531_SCR_SYNCE_ENABLE |
-+ YT8531_SCR_CLK_SRC_MASK |
-+ YT8531_SCR_CLK_FRE_SEL_125M;
-+ val = YT8531_SCR_SYNCE_ENABLE |
-+ FIELD_PREP(YT8531_SCR_CLK_SRC_MASK,
-+ YT8531_SCR_CLK_SRC_REF_25M);
-+ break;
-+ case YTPHY_DTS_OUTPUT_CLK_125M:
-+ mask = YT8531_SCR_SYNCE_ENABLE |
-+ YT8531_SCR_CLK_SRC_MASK |
-+ YT8531_SCR_CLK_FRE_SEL_125M;
-+ val = YT8531_SCR_SYNCE_ENABLE |
-+ YT8531_SCR_CLK_FRE_SEL_125M |
-+ FIELD_PREP(YT8531_SCR_CLK_SRC_MASK,
-+ YT8531_SCR_CLK_SRC_PLL_125M);
-+ break;
-+ default:
-+ phydev_warn(phydev, "Freq err:%u\n", freq);
-+ return -EINVAL;
-+ }
- } else {
- phydev_warn(phydev, "PHY id err\n");
- return -EINVAL;
-@@ -869,26 +892,6 @@ static int yt8521_probe(struct phy_devic
- }
-
- /**
-- * yt8531s_probe() - read chip config then set suitable polling_mode
-- * @phydev: a pointer to a &struct phy_device
-- *
-- * returns 0 or negative errno code
-- */
--static int yt8531s_probe(struct phy_device *phydev)
--{
-- int ret;
--
-- /* Disable SyncE clock output by default */
-- ret = ytphy_modify_ext_with_lock(phydev, YT8531S_SYNCE_CFG_REG,
-- YT8531S_SCR_SYNCE_ENABLE, 0);
-- if (ret < 0)
-- return ret;
--
-- /* same as yt8521_probe */
-- return yt8521_probe(phydev);
--}
--
--/**
- * ytphy_utp_read_lpa() - read LPA then setup lp_advertising for utp
- * @phydev: a pointer to a &struct phy_device
- *
-@@ -1970,7 +1973,7 @@ static struct phy_driver motorcomm_phy_d
- PHY_ID_MATCH_EXACT(PHY_ID_YT8531S),
- .name = "YT8531S Gigabit Ethernet",
- .get_features = yt8521_get_features,
-- .probe = yt8531s_probe,
-+ .probe = yt8521_probe,
- .read_page = yt8521_read_page,
- .write_page = yt8521_write_page,
- .get_wol = ytphy_get_wol,
+++ /dev/null
-From 4ac94f728a588e7096dd5010cd7141a309ea7805 Mon Sep 17 00:00:00 2001
-From: Frank Sae <Frank.Sae@motor-comm.com>
-Date: Thu, 2 Feb 2023 11:00:37 +0800
-Subject: [PATCH] net: phy: Add driver for Motorcomm yt8531 gigabit ethernet
- phy
-
-Add a driver for the motorcomm yt8531 gigabit ethernet phy. We have
- verified the driver on AM335x platform with yt8531 board. On the
- board, yt8531 gigabit ethernet phy works in utp mode, RGMII
- interface, supports 1000M/100M/10M speeds, and wol(magic package).
-
-Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/Kconfig | 2 +-
- drivers/net/phy/motorcomm.c | 208 +++++++++++++++++++++++++++++++++++-
- 2 files changed, 207 insertions(+), 3 deletions(-)
-
---- a/drivers/net/phy/Kconfig
-+++ b/drivers/net/phy/Kconfig
-@@ -257,7 +257,7 @@ config MOTORCOMM_PHY
- tristate "Motorcomm PHYs"
- help
- Enables support for Motorcomm network PHYs.
-- Currently supports the YT8511, YT8521, YT8531S Gigabit Ethernet PHYs.
-+ Currently supports YT85xx Gigabit Ethernet PHYs.
-
- config NATIONAL_PHY
- tristate "National Semiconductor PHYs"
---- a/drivers/net/phy/motorcomm.c
-+++ b/drivers/net/phy/motorcomm.c
-@@ -1,6 +1,6 @@
- // SPDX-License-Identifier: GPL-2.0+
- /*
-- * Motorcomm 8511/8521/8531S PHY driver.
-+ * Motorcomm 8511/8521/8531/8531S PHY driver.
- *
- * Author: Peter Geis <pgwipeout@gmail.com>
- * Author: Frank <Frank.Sae@motor-comm.com>
-@@ -14,6 +14,7 @@
-
- #define PHY_ID_YT8511 0x0000010a
- #define PHY_ID_YT8521 0x0000011a
-+#define PHY_ID_YT8531 0x4f51e91b
- #define PHY_ID_YT8531S 0x4f51e91a
-
- /* YT8521/YT8531S Register Overview
-@@ -517,6 +518,61 @@ err_restore_page:
- return phy_restore_page(phydev, old_page, ret);
- }
-
-+static int yt8531_set_wol(struct phy_device *phydev,
-+ struct ethtool_wolinfo *wol)
-+{
-+ const u16 mac_addr_reg[] = {
-+ YTPHY_WOL_MACADDR2_REG,
-+ YTPHY_WOL_MACADDR1_REG,
-+ YTPHY_WOL_MACADDR0_REG,
-+ };
-+ const u8 *mac_addr;
-+ u16 mask, val;
-+ int ret;
-+ u8 i;
-+
-+ if (wol->wolopts & WAKE_MAGIC) {
-+ mac_addr = phydev->attached_dev->dev_addr;
-+
-+ /* Store the device address for the magic packet */
-+ for (i = 0; i < 3; i++) {
-+ ret = ytphy_write_ext_with_lock(phydev, mac_addr_reg[i],
-+ ((mac_addr[i * 2] << 8)) |
-+ (mac_addr[i * 2 + 1]));
-+ if (ret < 0)
-+ return ret;
-+ }
-+
-+ /* Enable WOL feature */
-+ mask = YTPHY_WCR_PULSE_WIDTH_MASK | YTPHY_WCR_INTR_SEL;
-+ val = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
-+ val |= YTPHY_WCR_TYPE_PULSE | YTPHY_WCR_PULSE_WIDTH_672MS;
-+ ret = ytphy_modify_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG,
-+ mask, val);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* Enable WOL interrupt */
-+ ret = phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, 0,
-+ YTPHY_IER_WOL);
-+ if (ret < 0)
-+ return ret;
-+ } else {
-+ /* Disable WOL feature */
-+ mask = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
-+ ret = ytphy_modify_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG,
-+ mask, 0);
-+
-+ /* Disable WOL interrupt */
-+ ret = phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG,
-+ YTPHY_IER_WOL, 0);
-+ if (ret < 0)
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
- static int yt8511_read_page(struct phy_device *phydev)
- {
- return __phy_read(phydev, YT8511_PAGE_SELECT);
-@@ -767,6 +823,17 @@ static int ytphy_rgmii_clk_delay_config(
- return ytphy_modify_ext(phydev, YT8521_RGMII_CONFIG1_REG, mask, val);
- }
-
-+static int ytphy_rgmii_clk_delay_config_with_lock(struct phy_device *phydev)
-+{
-+ int ret;
-+
-+ phy_lock_mdio_bus(phydev);
-+ ret = ytphy_rgmii_clk_delay_config(phydev);
-+ phy_unlock_mdio_bus(phydev);
-+
-+ return ret;
-+}
-+
- /**
- * yt8521_probe() - read chip config then set suitable polling_mode
- * @phydev: a pointer to a &struct phy_device
-@@ -891,6 +958,43 @@ static int yt8521_probe(struct phy_devic
- val);
- }
-
-+static int yt8531_probe(struct phy_device *phydev)
-+{
-+ struct device_node *node = phydev->mdio.dev.of_node;
-+ u16 mask, val;
-+ u32 freq;
-+
-+ if (of_property_read_u32(node, "motorcomm,clk-out-frequency-hz", &freq))
-+ freq = YTPHY_DTS_OUTPUT_CLK_DIS;
-+
-+ switch (freq) {
-+ case YTPHY_DTS_OUTPUT_CLK_DIS:
-+ mask = YT8531_SCR_SYNCE_ENABLE;
-+ val = 0;
-+ break;
-+ case YTPHY_DTS_OUTPUT_CLK_25M:
-+ mask = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_SRC_MASK |
-+ YT8531_SCR_CLK_FRE_SEL_125M;
-+ val = YT8531_SCR_SYNCE_ENABLE |
-+ FIELD_PREP(YT8531_SCR_CLK_SRC_MASK,
-+ YT8531_SCR_CLK_SRC_REF_25M);
-+ break;
-+ case YTPHY_DTS_OUTPUT_CLK_125M:
-+ mask = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_SRC_MASK |
-+ YT8531_SCR_CLK_FRE_SEL_125M;
-+ val = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_FRE_SEL_125M |
-+ FIELD_PREP(YT8531_SCR_CLK_SRC_MASK,
-+ YT8531_SCR_CLK_SRC_PLL_125M);
-+ break;
-+ default:
-+ phydev_warn(phydev, "Freq err:%u\n", freq);
-+ return -EINVAL;
-+ }
-+
-+ return ytphy_modify_ext_with_lock(phydev, YTPHY_SYNCE_CFG_REG, mask,
-+ val);
-+}
-+
- /**
- * ytphy_utp_read_lpa() - read LPA then setup lp_advertising for utp
- * @phydev: a pointer to a &struct phy_device
-@@ -1387,6 +1491,94 @@ err_restore_page:
- return phy_restore_page(phydev, old_page, ret);
- }
-
-+static int yt8531_config_init(struct phy_device *phydev)
-+{
-+ struct device_node *node = phydev->mdio.dev.of_node;
-+ int ret;
-+
-+ ret = ytphy_rgmii_clk_delay_config_with_lock(phydev);
-+ if (ret < 0)
-+ return ret;
-+
-+ if (of_property_read_bool(node, "motorcomm,auto-sleep-disabled")) {
-+ /* disable auto sleep */
-+ ret = ytphy_modify_ext_with_lock(phydev,
-+ YT8521_EXTREG_SLEEP_CONTROL1_REG,
-+ YT8521_ESC1R_SLEEP_SW, 0);
-+ if (ret < 0)
-+ return ret;
-+ }
-+
-+ if (of_property_read_bool(node, "motorcomm,keep-pll-enabled")) {
-+ /* enable RXC clock when no wire plug */
-+ ret = ytphy_modify_ext_with_lock(phydev,
-+ YT8521_CLOCK_GATING_REG,
-+ YT8521_CGR_RX_CLK_EN, 0);
-+ if (ret < 0)
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
-+/**
-+ * yt8531_link_change_notify() - Adjust the tx clock direction according to
-+ * the current speed and dts config.
-+ * @phydev: a pointer to a &struct phy_device
-+ *
-+ * NOTE: This function is only used to adapt to VF2 with JH7110 SoC. Please
-+ * keep "motorcomm,tx-clk-adj-enabled" not exist in dts when the soc is not
-+ * JH7110.
-+ */
-+static void yt8531_link_change_notify(struct phy_device *phydev)
-+{
-+ struct device_node *node = phydev->mdio.dev.of_node;
-+ bool tx_clk_adj_enabled = false;
-+ bool tx_clk_1000_inverted;
-+ bool tx_clk_100_inverted;
-+ bool tx_clk_10_inverted;
-+ u16 val = 0;
-+ int ret;
-+
-+ if (of_property_read_bool(node, "motorcomm,tx-clk-adj-enabled"))
-+ tx_clk_adj_enabled = true;
-+
-+ if (!tx_clk_adj_enabled)
-+ return;
-+
-+ if (of_property_read_bool(node, "motorcomm,tx-clk-10-inverted"))
-+ tx_clk_10_inverted = true;
-+ if (of_property_read_bool(node, "motorcomm,tx-clk-100-inverted"))
-+ tx_clk_100_inverted = true;
-+ if (of_property_read_bool(node, "motorcomm,tx-clk-1000-inverted"))
-+ tx_clk_1000_inverted = true;
-+
-+ if (phydev->speed < 0)
-+ return;
-+
-+ switch (phydev->speed) {
-+ case SPEED_1000:
-+ if (tx_clk_1000_inverted)
-+ val = YT8521_RC1R_TX_CLK_SEL_INVERTED;
-+ break;
-+ case SPEED_100:
-+ if (tx_clk_100_inverted)
-+ val = YT8521_RC1R_TX_CLK_SEL_INVERTED;
-+ break;
-+ case SPEED_10:
-+ if (tx_clk_10_inverted)
-+ val = YT8521_RC1R_TX_CLK_SEL_INVERTED;
-+ break;
-+ default:
-+ return;
-+ }
-+
-+ ret = ytphy_modify_ext_with_lock(phydev, YT8521_RGMII_CONFIG1_REG,
-+ YT8521_RC1R_TX_CLK_SEL_INVERTED, val);
-+ if (ret < 0)
-+ phydev_warn(phydev, "Modify TX_CLK_SEL err:%d\n", ret);
-+}
-+
- /**
- * yt8521_prepare_fiber_features() - A small helper function that setup
- * fiber's features.
-@@ -1970,6 +2162,17 @@ static struct phy_driver motorcomm_phy_d
- .resume = yt8521_resume,
- },
- {
-+ PHY_ID_MATCH_EXACT(PHY_ID_YT8531),
-+ .name = "YT8531 Gigabit Ethernet",
-+ .probe = yt8531_probe,
-+ .config_init = yt8531_config_init,
-+ .suspend = genphy_suspend,
-+ .resume = genphy_resume,
-+ .get_wol = ytphy_get_wol,
-+ .set_wol = yt8531_set_wol,
-+ .link_change_notify = yt8531_link_change_notify,
-+ },
-+ {
- PHY_ID_MATCH_EXACT(PHY_ID_YT8531S),
- .name = "YT8531S Gigabit Ethernet",
- .get_features = yt8521_get_features,
-@@ -1990,7 +2193,7 @@ static struct phy_driver motorcomm_phy_d
-
- module_phy_driver(motorcomm_phy_drvs);
-
--MODULE_DESCRIPTION("Motorcomm 8511/8521/8531S PHY driver");
-+MODULE_DESCRIPTION("Motorcomm 8511/8521/8531/8531S PHY driver");
- MODULE_AUTHOR("Peter Geis");
- MODULE_AUTHOR("Frank");
- MODULE_LICENSE("GPL");
-@@ -1998,6 +2201,7 @@ MODULE_LICENSE("GPL");
- static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
- { PHY_ID_MATCH_EXACT(PHY_ID_YT8511) },
- { PHY_ID_MATCH_EXACT(PHY_ID_YT8521) },
-+ { PHY_ID_MATCH_EXACT(PHY_ID_YT8531) },
- { PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) },
- { /* sentinel */ }
- };
+++ /dev/null
-From 9753613f7399601f9bae6ee81e9d4274246c98ab Mon Sep 17 00:00:00 2001
-From: Dan Carpenter <error27@gmail.com>
-Date: Wed, 15 Feb 2023 07:21:47 +0300
-Subject: [PATCH] net: phy: motorcomm: uninitialized variables in
- yt8531_link_change_notify()
-
-These booleans are never set to false, but are just used without being
-initialized.
-
-Fixes: 4ac94f728a58 ("net: phy: Add driver for Motorcomm yt8531 gigabit ethernet phy")
-Signed-off-by: Dan Carpenter <error27@gmail.com>
-Reviewed-by: Frank Sae <Frank.Sae@motor-comm.com>
-Link: https://lore.kernel.org/r/Y+xd2yJet2ImHLoQ@kili
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/motorcomm.c | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
---- a/drivers/net/phy/motorcomm.c
-+++ b/drivers/net/phy/motorcomm.c
-@@ -1533,10 +1533,10 @@ static int yt8531_config_init(struct phy
- static void yt8531_link_change_notify(struct phy_device *phydev)
- {
- struct device_node *node = phydev->mdio.dev.of_node;
-+ bool tx_clk_1000_inverted = false;
-+ bool tx_clk_100_inverted = false;
-+ bool tx_clk_10_inverted = false;
- bool tx_clk_adj_enabled = false;
-- bool tx_clk_1000_inverted;
-- bool tx_clk_100_inverted;
-- bool tx_clk_10_inverted;
- u16 val = 0;
- int ret;
-
+++ /dev/null
-From 7a561e9351ae7e3fb1f08584d40b49c1e55dde60 Mon Sep 17 00:00:00 2001
-From: Samin Guo <samin.guo@starfivetech.com>
-Date: Thu, 20 Jul 2023 19:15:09 +0800
-Subject: [PATCH] net: phy: motorcomm: Add pad drive strength cfg support
-
-The motorcomm phy (YT8531) supports the ability to adjust the drive
-strength of the rx_clk/rx_data, and the default strength may not be
-suitable for all boards. So add configurable options to better match
-the boards.(e.g. StarFive VisionFive 2)
-
-When we configure the drive strength, we need to read the current
-LDO voltage value to ensure that it is a legal value at that LDO
-voltage.
-
-Reviewed-by: Hal Feng <hal.feng@starfivetech.com>
-Signed-off-by: Samin Guo <samin.guo@starfivetech.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/motorcomm.c | 118 ++++++++++++++++++++++++++++++++++++
- 1 file changed, 118 insertions(+)
-
---- a/drivers/net/phy/motorcomm.c
-+++ b/drivers/net/phy/motorcomm.c
-@@ -163,6 +163,10 @@
-
- #define YT8521_CHIP_CONFIG_REG 0xA001
- #define YT8521_CCR_SW_RST BIT(15)
-+#define YT8531_RGMII_LDO_VOL_MASK GENMASK(5, 4)
-+#define YT8531_LDO_VOL_3V3 0x0
-+#define YT8531_LDO_VOL_1V8 0x2
-+
- /* 1b0 disable 1.9ns rxc clock delay *default*
- * 1b1 enable 1.9ns rxc clock delay
- */
-@@ -236,6 +240,12 @@
- */
- #define YTPHY_WCR_TYPE_PULSE BIT(0)
-
-+#define YTPHY_PAD_DRIVE_STRENGTH_REG 0xA010
-+#define YT8531_RGMII_RXC_DS_MASK GENMASK(15, 13)
-+#define YT8531_RGMII_RXD_DS_HI_MASK BIT(12) /* Bit 2 of rxd_ds */
-+#define YT8531_RGMII_RXD_DS_LOW_MASK GENMASK(5, 4) /* Bit 1/0 of rxd_ds */
-+#define YT8531_RGMII_RX_DS_DEFAULT 0x3
-+
- #define YTPHY_SYNCE_CFG_REG 0xA012
- #define YT8521_SCR_SYNCE_ENABLE BIT(5)
- /* 1b0 output 25m clock
-@@ -835,6 +845,110 @@ static int ytphy_rgmii_clk_delay_config_
- }
-
- /**
-+ * struct ytphy_ldo_vol_map - map a current value to a register value
-+ * @vol: ldo voltage
-+ * @ds: value in the register
-+ * @cur: value in device configuration
-+ */
-+struct ytphy_ldo_vol_map {
-+ u32 vol;
-+ u32 ds;
-+ u32 cur;
-+};
-+
-+static const struct ytphy_ldo_vol_map yt8531_ldo_vol[] = {
-+ {.vol = YT8531_LDO_VOL_1V8, .ds = 0, .cur = 1200},
-+ {.vol = YT8531_LDO_VOL_1V8, .ds = 1, .cur = 2100},
-+ {.vol = YT8531_LDO_VOL_1V8, .ds = 2, .cur = 2700},
-+ {.vol = YT8531_LDO_VOL_1V8, .ds = 3, .cur = 2910},
-+ {.vol = YT8531_LDO_VOL_1V8, .ds = 4, .cur = 3110},
-+ {.vol = YT8531_LDO_VOL_1V8, .ds = 5, .cur = 3600},
-+ {.vol = YT8531_LDO_VOL_1V8, .ds = 6, .cur = 3970},
-+ {.vol = YT8531_LDO_VOL_1V8, .ds = 7, .cur = 4350},
-+ {.vol = YT8531_LDO_VOL_3V3, .ds = 0, .cur = 3070},
-+ {.vol = YT8531_LDO_VOL_3V3, .ds = 1, .cur = 4080},
-+ {.vol = YT8531_LDO_VOL_3V3, .ds = 2, .cur = 4370},
-+ {.vol = YT8531_LDO_VOL_3V3, .ds = 3, .cur = 4680},
-+ {.vol = YT8531_LDO_VOL_3V3, .ds = 4, .cur = 5020},
-+ {.vol = YT8531_LDO_VOL_3V3, .ds = 5, .cur = 5450},
-+ {.vol = YT8531_LDO_VOL_3V3, .ds = 6, .cur = 5740},
-+ {.vol = YT8531_LDO_VOL_3V3, .ds = 7, .cur = 6140},
-+};
-+
-+static u32 yt8531_get_ldo_vol(struct phy_device *phydev)
-+{
-+ u32 val;
-+
-+ val = ytphy_read_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG);
-+ val = FIELD_GET(YT8531_RGMII_LDO_VOL_MASK, val);
-+
-+ return val <= YT8531_LDO_VOL_1V8 ? val : YT8531_LDO_VOL_1V8;
-+}
-+
-+static int yt8531_get_ds_map(struct phy_device *phydev, u32 cur)
-+{
-+ u32 vol;
-+ int i;
-+
-+ vol = yt8531_get_ldo_vol(phydev);
-+ for (i = 0; i < ARRAY_SIZE(yt8531_ldo_vol); i++) {
-+ if (yt8531_ldo_vol[i].vol == vol && yt8531_ldo_vol[i].cur == cur)
-+ return yt8531_ldo_vol[i].ds;
-+ }
-+
-+ return -EINVAL;
-+}
-+
-+static int yt8531_set_ds(struct phy_device *phydev)
-+{
-+ struct device_node *node = phydev->mdio.dev.of_node;
-+ u32 ds_field_low, ds_field_hi, val;
-+ int ret, ds;
-+
-+ /* set rgmii rx clk driver strength */
-+ if (!of_property_read_u32(node, "motorcomm,rx-clk-drv-microamp", &val)) {
-+ ds = yt8531_get_ds_map(phydev, val);
-+ if (ds < 0)
-+ return dev_err_probe(&phydev->mdio.dev, ds,
-+ "No matching current value was found.\n");
-+ } else {
-+ ds = YT8531_RGMII_RX_DS_DEFAULT;
-+ }
-+
-+ ret = ytphy_modify_ext_with_lock(phydev,
-+ YTPHY_PAD_DRIVE_STRENGTH_REG,
-+ YT8531_RGMII_RXC_DS_MASK,
-+ FIELD_PREP(YT8531_RGMII_RXC_DS_MASK, ds));
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set rgmii rx data driver strength */
-+ if (!of_property_read_u32(node, "motorcomm,rx-data-drv-microamp", &val)) {
-+ ds = yt8531_get_ds_map(phydev, val);
-+ if (ds < 0)
-+ return dev_err_probe(&phydev->mdio.dev, ds,
-+ "No matching current value was found.\n");
-+ } else {
-+ ds = YT8531_RGMII_RX_DS_DEFAULT;
-+ }
-+
-+ ds_field_hi = FIELD_GET(BIT(2), ds);
-+ ds_field_hi = FIELD_PREP(YT8531_RGMII_RXD_DS_HI_MASK, ds_field_hi);
-+
-+ ds_field_low = FIELD_GET(GENMASK(1, 0), ds);
-+ ds_field_low = FIELD_PREP(YT8531_RGMII_RXD_DS_LOW_MASK, ds_field_low);
-+
-+ ret = ytphy_modify_ext_with_lock(phydev,
-+ YTPHY_PAD_DRIVE_STRENGTH_REG,
-+ YT8531_RGMII_RXD_DS_LOW_MASK | YT8531_RGMII_RXD_DS_HI_MASK,
-+ ds_field_low | ds_field_hi);
-+ if (ret < 0)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+/**
- * yt8521_probe() - read chip config then set suitable polling_mode
- * @phydev: a pointer to a &struct phy_device
- *
-@@ -1518,6 +1632,10 @@ static int yt8531_config_init(struct phy
- return ret;
- }
-
-+ ret = yt8531_set_ds(phydev);
-+ if (ret < 0)
-+ return ret;
-+
- return 0;
- }
-
+++ /dev/null
-From 90ef0a7b0622c62758b2638604927867775479ea Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Thu, 13 Jul 2023 09:42:07 +0100
-Subject: [PATCH] net: phylink: add pcs_enable()/pcs_disable() methods
-
-Add phylink PCS enable/disable callbacks that will allow us to place
-IEEE 802.3 register compliant PCS in power-down mode while not being
-used.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/phylink.c | 48 +++++++++++++++++++++++++++++++--------
- include/linux/phylink.h | 16 +++++++++++++
- 2 files changed, 55 insertions(+), 9 deletions(-)
-
---- a/drivers/net/phy/phylink.c
-+++ b/drivers/net/phy/phylink.c
-@@ -34,6 +34,10 @@ enum {
- PHYLINK_DISABLE_STOPPED,
- PHYLINK_DISABLE_LINK,
- PHYLINK_DISABLE_MAC_WOL,
-+
-+ PCS_STATE_DOWN = 0,
-+ PCS_STATE_STARTING,
-+ PCS_STATE_STARTED,
- };
-
- /**
-@@ -72,6 +76,7 @@ struct phylink {
- struct phylink_link_state phy_state;
- struct work_struct resolve;
- unsigned int pcs_neg_mode;
-+ unsigned int pcs_state;
-
- bool mac_link_dropped;
- bool using_mac_select_pcs;
-@@ -992,6 +997,22 @@ static void phylink_resolve_an_pause(str
- }
- }
-
-+static void phylink_pcs_disable(struct phylink_pcs *pcs)
-+{
-+ if (pcs && pcs->ops->pcs_disable)
-+ pcs->ops->pcs_disable(pcs);
-+}
-+
-+static int phylink_pcs_enable(struct phylink_pcs *pcs)
-+{
-+ int err = 0;
-+
-+ if (pcs && pcs->ops->pcs_enable)
-+ err = pcs->ops->pcs_enable(pcs);
-+
-+ return err;
-+}
-+
- static int phylink_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
- const struct phylink_link_state *state,
- bool permit_pause_to_mac)
-@@ -1094,11 +1115,17 @@ static void phylink_major_config(struct
- /* If we have a new PCS, switch to the new PCS after preparing the MAC
- * for the change.
- */
-- if (pcs_changed)
-+ if (pcs_changed) {
-+ phylink_pcs_disable(pl->pcs);
-+
- pl->pcs = pcs;
-+ }
-
- phylink_mac_config(pl, state);
-
-+ if (pl->pcs_state == PCS_STATE_STARTING || pcs_changed)
-+ phylink_pcs_enable(pl->pcs);
-+
- neg_mode = pl->cur_link_an_mode;
- if (pl->pcs && pl->pcs->neg_mode)
- neg_mode = pl->pcs_neg_mode;
-@@ -1586,6 +1613,7 @@ struct phylink *phylink_create(struct ph
- pl->link_config.pause = MLO_PAUSE_AN;
- pl->link_config.speed = SPEED_UNKNOWN;
- pl->link_config.duplex = DUPLEX_UNKNOWN;
-+ pl->pcs_state = PCS_STATE_DOWN;
- pl->mac_ops = mac_ops;
- __set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
- timer_setup(&pl->link_poll, phylink_fixed_poll, 0);
-@@ -1987,6 +2015,8 @@ void phylink_start(struct phylink *pl)
- if (pl->netdev)
- netif_carrier_off(pl->netdev);
-
-+ pl->pcs_state = PCS_STATE_STARTING;
-+
- /* Apply the link configuration to the MAC when starting. This allows
- * a fixed-link to start with the correct parameters, and also
- * ensures that we set the appropriate advertisement for Serdes links.
-@@ -1997,6 +2027,8 @@ void phylink_start(struct phylink *pl)
- */
- phylink_mac_initial_config(pl, true);
-
-+ pl->pcs_state = PCS_STATE_STARTED;
-+
- phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_STOPPED);
-
- if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->link_gpio) {
-@@ -2015,15 +2047,9 @@ void phylink_start(struct phylink *pl)
- poll = true;
- }
-
-- switch (pl->cfg_link_an_mode) {
-- case MLO_AN_FIXED:
-+ if (pl->cfg_link_an_mode == MLO_AN_FIXED)
- poll |= pl->config->poll_fixed_state;
-- break;
-- case MLO_AN_INBAND:
-- if (pl->pcs)
-- poll |= pl->pcs->poll;
-- break;
-- }
-+
- if (poll)
- mod_timer(&pl->link_poll, jiffies + HZ);
- if (pl->phydev)
-@@ -2060,6 +2086,10 @@ void phylink_stop(struct phylink *pl)
- }
-
- phylink_run_resolve_and_disable(pl, PHYLINK_DISABLE_STOPPED);
-+
-+ pl->pcs_state = PCS_STATE_DOWN;
-+
-+ phylink_pcs_disable(pl->pcs);
- }
- EXPORT_SYMBOL_GPL(phylink_stop);
-
---- a/include/linux/phylink.h
-+++ b/include/linux/phylink.h
-@@ -533,6 +533,8 @@ struct phylink_pcs {
- /**
- * struct phylink_pcs_ops - MAC PCS operations structure.
- * @pcs_validate: validate the link configuration.
-+ * @pcs_enable: enable the PCS.
-+ * @pcs_disable: disable the PCS.
- * @pcs_get_state: read the current MAC PCS link state from the hardware.
- * @pcs_config: configure the MAC PCS for the selected mode and state.
- * @pcs_an_restart: restart 802.3z BaseX autonegotiation.
-@@ -542,6 +544,8 @@ struct phylink_pcs {
- struct phylink_pcs_ops {
- int (*pcs_validate)(struct phylink_pcs *pcs, unsigned long *supported,
- const struct phylink_link_state *state);
-+ int (*pcs_enable)(struct phylink_pcs *pcs);
-+ void (*pcs_disable)(struct phylink_pcs *pcs);
- void (*pcs_get_state)(struct phylink_pcs *pcs,
- struct phylink_link_state *state);
- int (*pcs_config)(struct phylink_pcs *pcs, unsigned int neg_mode,
-@@ -572,6 +576,18 @@ int pcs_validate(struct phylink_pcs *pcs
- const struct phylink_link_state *state);
-
- /**
-+ * pcs_enable() - enable the PCS.
-+ * @pcs: a pointer to a &struct phylink_pcs.
-+ */
-+int pcs_enable(struct phylink_pcs *pcs);
-+
-+/**
-+ * pcs_disable() - disable the PCS.
-+ * @pcs: a pointer to a &struct phylink_pcs.
-+ */
-+void pcs_disable(struct phylink_pcs *pcs);
-+
-+/**
- * pcs_get_state() - Read the current inband link state from the hardware
- * @pcs: a pointer to a &struct phylink_pcs.
- * @state: a pointer to a &struct phylink_link_state.
+++ /dev/null
-From e4ccdfb78a47132f2d215658aab8902fc457c4b4 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Fri, 18 Aug 2023 04:07:46 +0100
-Subject: [PATCH 082/125] net: pcs: lynxi: implement pcs_disable op
-
-When switching from 10GBase-R/5GBase-R/USXGMII to one of the interface
-modes provided by mtk-pcs-lynxi we need to make sure to always perform
-a full configuration of the PHYA.
-
-Implement pcs_disable op which resets the stored interface mode to
-PHY_INTERFACE_MODE_NA to trigger a full reconfiguration once the LynxI
-PCS driver had previously been deselected in favor of another PCS
-driver such as the to-be-added driver for the USXGMII PCS found in
-MT7988.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Link: https://lore.kernel.org/r/f23d1a60d2c9d2fb72e32dcb0eaa5f7e867a3d68.1692327891.git.daniel@makrotopia.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/pcs/pcs-mtk-lynxi.c | 8 ++++++++
- 1 file changed, 8 insertions(+)
-
---- a/drivers/net/pcs/pcs-mtk-lynxi.c
-+++ b/drivers/net/pcs/pcs-mtk-lynxi.c
-@@ -234,11 +234,19 @@ static void mtk_pcs_lynxi_link_up(struct
- }
- }
-
-+static void mtk_pcs_lynxi_disable(struct phylink_pcs *pcs)
-+{
-+ struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
-+
-+ mpcs->interface = PHY_INTERFACE_MODE_NA;
-+}
-+
- static const struct phylink_pcs_ops mtk_pcs_lynxi_ops = {
- .pcs_get_state = mtk_pcs_lynxi_get_state,
- .pcs_config = mtk_pcs_lynxi_config,
- .pcs_an_restart = mtk_pcs_lynxi_restart_an,
- .pcs_link_up = mtk_pcs_lynxi_link_up,
-+ .pcs_disable = mtk_pcs_lynxi_disable,
- };
-
- struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev,
+++ /dev/null
-From: Andy Ren <andy.ren@getcruise.com>
-Date: Mon, 7 Nov 2022 09:42:42 -0800
-Subject: [PATCH] net/core: Allow live renaming when an interface is up
-
-Allow a network interface to be renamed when the interface
-is up.
-
-As described in the netconsole documentation [1], when netconsole is
-used as a built-in, it will bring up the specified interface as soon as
-possible. As a result, user space will not be able to rename the
-interface since the kernel disallows renaming of interfaces that are
-administratively up unless the 'IFF_LIVE_RENAME_OK' private flag was set
-by the kernel.
-
-The original solution [2] to this problem was to add a new parameter to
-the netconsole configuration parameters that allows renaming of
-the interface used by netconsole while it is administratively up.
-However, during the discussion that followed, it became apparent that we
-have no reason to keep the current restriction and instead we should
-allow user space to rename interfaces regardless of their administrative
-state:
-
-1. The restriction was put in place over 20 years ago when renaming was
-only possible via IOCTL and before rtnetlink started notifying user
-space about such changes like it does today.
-
-2. The 'IFF_LIVE_RENAME_OK' flag was added over 3 years ago in version
-5.2 and no regressions were reported.
-
-3. In-kernel listeners to 'NETDEV_CHANGENAME' do not seem to care about
-the administrative state of interface.
-
-Therefore, allow user space to rename running interfaces by removing the
-restriction and the associated 'IFF_LIVE_RENAME_OK' flag. Help in
-possible triage by emitting a message to the kernel log that an
-interface was renamed while UP.
-
-[1] https://www.kernel.org/doc/Documentation/networking/netconsole.rst
-[2] https://lore.kernel.org/netdev/20221102002420.2613004-1-andy.ren@getcruise.com/
-
-Signed-off-by: Andy Ren <andy.ren@getcruise.com>
-Reviewed-by: Ido Schimmel <idosch@nvidia.com>
-Reviewed-by: David Ahern <dsahern@kernel.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
-
---- a/include/linux/netdevice.h
-+++ b/include/linux/netdevice.h
-@@ -1691,7 +1691,6 @@ struct net_device_ops {
- * @IFF_FAILOVER: device is a failover master device
- * @IFF_FAILOVER_SLAVE: device is lower dev of a failover master device
- * @IFF_L3MDEV_RX_HANDLER: only invoke the rx handler of L3 master device
-- * @IFF_LIVE_RENAME_OK: rename is allowed while device is up and running
- * @IFF_TX_SKB_NO_LINEAR: device/driver is capable of xmitting frames with
- * skb_headlen(skb) == 0 (data starts from frag0)
- * @IFF_CHANGE_PROTO_DOWN: device supports setting carrier via IFLA_PROTO_DOWN
-@@ -1727,7 +1726,7 @@ enum netdev_priv_flags {
- IFF_FAILOVER = 1<<27,
- IFF_FAILOVER_SLAVE = 1<<28,
- IFF_L3MDEV_RX_HANDLER = 1<<29,
-- IFF_LIVE_RENAME_OK = 1<<30,
-+ /* was IFF_LIVE_RENAME_OK */
- IFF_TX_SKB_NO_LINEAR = BIT_ULL(31),
- IFF_CHANGE_PROTO_DOWN = BIT_ULL(32),
- };
-@@ -1762,7 +1761,6 @@ enum netdev_priv_flags {
- #define IFF_FAILOVER IFF_FAILOVER
- #define IFF_FAILOVER_SLAVE IFF_FAILOVER_SLAVE
- #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
-
- /* Specifies the type of the struct net_device::ml_priv pointer */
---- a/net/core/dev.c
-+++ b/net/core/dev.c
-@@ -1188,22 +1188,6 @@ int dev_change_name(struct net_device *d
-
- net = dev_net(dev);
-
-- /* Some auto-enslaved devices e.g. failover slaves are
-- * special, as userspace might rename the device after
-- * the interface had been brought up and running since
-- * the point kernel initiated auto-enslavement. Allow
-- * live name change even when these slave devices are
-- * up and running.
-- *
-- * Typically, users of these auto-enslaving devices
-- * don't actually care about slave name change, as
-- * they are supposed to operate on master interface
-- * directly.
-- */
-- if (dev->flags & IFF_UP &&
-- likely(!(dev->priv_flags & IFF_LIVE_RENAME_OK)))
-- return -EBUSY;
--
- down_write(&devnet_rename_sem);
-
- if (strncmp(newname, dev->name, IFNAMSIZ) == 0) {
-@@ -1220,7 +1204,8 @@ int dev_change_name(struct net_device *d
- }
-
- if (oldname[0] && !strchr(oldname, '%'))
-- netdev_info(dev, "renamed from %s\n", oldname);
-+ netdev_info(dev, "renamed from %s%s\n", oldname,
-+ dev->flags & IFF_UP ? " (while UP)" : "");
-
- old_assign_type = dev->name_assign_type;
- dev->name_assign_type = NET_NAME_RENAMED;
---- a/net/core/failover.c
-+++ b/net/core/failover.c
-@@ -80,14 +80,14 @@ static int failover_slave_register(struc
- goto err_upper_link;
- }
-
-- slave_dev->priv_flags |= (IFF_FAILOVER_SLAVE | IFF_LIVE_RENAME_OK);
-+ slave_dev->priv_flags |= IFF_FAILOVER_SLAVE;
-
- if (fops && fops->slave_register &&
- !fops->slave_register(slave_dev, failover_dev))
- return NOTIFY_OK;
-
- netdev_upper_dev_unlink(slave_dev, failover_dev);
-- slave_dev->priv_flags &= ~(IFF_FAILOVER_SLAVE | IFF_LIVE_RENAME_OK);
-+ slave_dev->priv_flags &= ~IFF_FAILOVER_SLAVE;
- err_upper_link:
- netdev_rx_handler_unregister(slave_dev);
- done:
-@@ -121,7 +121,7 @@ int failover_slave_unregister(struct net
-
- netdev_rx_handler_unregister(slave_dev);
- netdev_upper_dev_unlink(slave_dev, failover_dev);
-- slave_dev->priv_flags &= ~(IFF_FAILOVER_SLAVE | IFF_LIVE_RENAME_OK);
-+ slave_dev->priv_flags &= ~IFF_FAILOVER_SLAVE;
-
- if (fops && fops->slave_unregister &&
- !fops->slave_unregister(slave_dev, failover_dev))
+++ /dev/null
-From 69649ef8405320f81497f4757faac8234f61b167 Mon Sep 17 00:00:00 2001
-From: Bjørn Mork <bjorn@mork.no>
-Date: Fri, 6 Jan 2023 17:07:39 +0100
-Subject: [PATCH] cdc_ether: no need to blacklist any r8152 devices
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The r8152 driver does not need this anymore.
-
-Dropping blacklist entries adds optional support for these
-devices in ECM mode.
-
-The 8153 devices are handled by the r8153_ecm driver when
-in ECM mode, and must still be blacklisted here.
-
-Signed-off-by: Bjørn Mork <bjorn@mork.no>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/usb/cdc_ether.c | 114 ------------------------------------
- 1 file changed, 114 deletions(-)
-
---- a/drivers/net/usb/cdc_ether.c
-+++ b/drivers/net/usb/cdc_ether.c
-@@ -768,13 +768,6 @@ static const struct usb_device_id produc
- .driver_info = 0,
- },
-
--/* Realtek RTL8152 Based USB 2.0 Ethernet Adapters */
--{
-- USB_DEVICE_AND_INTERFACE_INFO(REALTEK_VENDOR_ID, 0x8152, USB_CLASS_COMM,
-- USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
-- .driver_info = 0,
--},
--
- /* Realtek RTL8153 Based USB 3.0 Ethernet Adapters */
- {
- USB_DEVICE_AND_INTERFACE_INFO(REALTEK_VENDOR_ID, 0x8153, USB_CLASS_COMM,
-@@ -782,119 +775,12 @@ static const struct usb_device_id produc
- .driver_info = 0,
- },
-
--/* Samsung USB Ethernet Adapters */
--{
-- USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, 0xa101, USB_CLASS_COMM,
-- USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
-- .driver_info = 0,
--},
--
--#if IS_ENABLED(CONFIG_USB_RTL8152)
--/* Linksys USB3GIGV1 Ethernet Adapter */
--{
-- USB_DEVICE_AND_INTERFACE_INFO(LINKSYS_VENDOR_ID, 0x0041, USB_CLASS_COMM,
-- USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
-- .driver_info = 0,
--},
--#endif
--
--/* Lenovo ThinkPad OneLink+ Dock (based on Realtek RTL8153) */
--{
-- USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x3054, USB_CLASS_COMM,
-- USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
-- .driver_info = 0,
--},
--
--/* ThinkPad USB-C Dock (based on Realtek RTL8153) */
--{
-- USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x3062, USB_CLASS_COMM,
-- USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
-- .driver_info = 0,
--},
--
--/* ThinkPad Thunderbolt 3 Dock (based on Realtek RTL8153) */
--{
-- USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x3069, USB_CLASS_COMM,
-- USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
-- .driver_info = 0,
--},
--
--/* ThinkPad Thunderbolt 3 Dock Gen 2 (based on Realtek RTL8153) */
--{
-- USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x3082, USB_CLASS_COMM,
-- USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
-- .driver_info = 0,
--},
--
--/* Lenovo Thinkpad USB 3.0 Ethernet Adapters (based on Realtek RTL8153) */
--{
-- USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x7205, USB_CLASS_COMM,
-- USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
-- .driver_info = 0,
--},
--
--/* Lenovo USB C to Ethernet Adapter (based on Realtek RTL8153) */
--{
-- USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x720c, USB_CLASS_COMM,
-- USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
-- .driver_info = 0,
--},
--
--/* Lenovo USB-C Travel Hub (based on Realtek RTL8153) */
--{
-- USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x7214, USB_CLASS_COMM,
-- USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
-- .driver_info = 0,
--},
--
- /* Lenovo Powered USB-C Travel Hub (4X90S92381, based on Realtek RTL8153) */
- {
- USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x721e, USB_CLASS_COMM,
- USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
- .driver_info = 0,
- },
--
--/* ThinkPad USB-C Dock Gen 2 (based on Realtek RTL8153) */
--{
-- USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0xa387, USB_CLASS_COMM,
-- USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
-- .driver_info = 0,
--},
--
--/* NVIDIA Tegra USB 3.0 Ethernet Adapters (based on Realtek RTL8153) */
--{
-- USB_DEVICE_AND_INTERFACE_INFO(NVIDIA_VENDOR_ID, 0x09ff, USB_CLASS_COMM,
-- USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
-- .driver_info = 0,
--},
--
--/* Microsoft Surface 2 dock (based on Realtek RTL8152) */
--{
-- USB_DEVICE_AND_INTERFACE_INFO(MICROSOFT_VENDOR_ID, 0x07ab, USB_CLASS_COMM,
-- USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
-- .driver_info = 0,
--},
--
--/* Microsoft Surface Ethernet Adapter (based on Realtek RTL8153) */
--{
-- USB_DEVICE_AND_INTERFACE_INFO(MICROSOFT_VENDOR_ID, 0x07c6, USB_CLASS_COMM,
-- USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
-- .driver_info = 0,
--},
--
--/* Microsoft Surface Ethernet Adapter (based on Realtek RTL8153B) */
--{
-- USB_DEVICE_AND_INTERFACE_INFO(MICROSOFT_VENDOR_ID, 0x0927, USB_CLASS_COMM,
-- USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
-- .driver_info = 0,
--},
--
--/* TP-LINK UE300 USB 3.0 Ethernet Adapters (based on Realtek RTL8153) */
--{
-- USB_DEVICE_AND_INTERFACE_INFO(TPLINK_VENDOR_ID, 0x0601, USB_CLASS_COMM,
-- USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
-- .driver_info = 0,
--},
-
- /* Aquantia AQtion USB to 5GbE Controller (based on AQC111U) */
- {
+++ /dev/null
-From 02767440e1dda9861a11ca1dbe0f19a760b1d5c2 Mon Sep 17 00:00:00 2001
-From: Hayes Wang <hayeswang@realtek.com>
-Date: Thu, 19 Jan 2023 15:40:43 +0800
-Subject: [PATCH] r8152: reduce the control transfer of rtl8152_get_version()
-
-Reduce the control transfer by moving calling rtl8152_get_version() in
-rtl8152_probe(). This could prevent from calling rtl8152_get_version()
-for unnecessary situations. For example, after setting config #2 for the
-device, there are two interfaces and rtl8152_probe() may be called
-twice. However, we don't need to call rtl8152_get_version() for this
-situation.
-
-Signed-off-by: Hayes Wang <hayeswang@realtek.com>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/usb/r8152.c | 9 +++++----
- 1 file changed, 5 insertions(+), 4 deletions(-)
-
---- a/drivers/net/usb/r8152.c
-+++ b/drivers/net/usb/r8152.c
-@@ -9638,20 +9638,21 @@ static int rtl8152_probe(struct usb_inte
- const struct usb_device_id *id)
- {
- struct usb_device *udev = interface_to_usbdev(intf);
-- u8 version = rtl8152_get_version(intf);
- struct r8152 *tp;
- struct net_device *netdev;
-+ u8 version;
- int ret;
-
-- if (version == RTL_VER_UNKNOWN)
-- return -ENODEV;
--
- if (intf->cur_altsetting->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC)
- return -ENODEV;
-
- if (!rtl_check_vendor_ok(intf))
- return -ENODEV;
-
-+ version = rtl8152_get_version(intf);
-+ if (version == RTL_VER_UNKNOWN)
-+ return -ENODEV;
-+
- usb_reset_device(udev);
- netdev = alloc_etherdev(sizeof(struct r8152));
- if (!netdev) {
+++ /dev/null
-From 5cc33f139e11b893ff6dc60d8a0ae865a65521ac Mon Sep 17 00:00:00 2001
-From: Douglas Anderson <dianders@chromium.org>
-Date: Thu, 6 Apr 2023 17:14:26 -0700
-Subject: [PATCH] r8152: Add __GFP_NOWARN to big allocations
-
-When memory is a little tight on my system, it's pretty easy to see
-warnings that look like this.
-
- ksoftirqd/0: page allocation failure: order:3, mode:0x40a20(GFP_ATOMIC|__GFP_COMP), nodemask=(null),cpuset=/,mems_allowed=0
- ...
- Call trace:
- dump_backtrace+0x0/0x1e8
- show_stack+0x20/0x2c
- dump_stack_lvl+0x60/0x78
- dump_stack+0x18/0x38
- warn_alloc+0x104/0x174
- __alloc_pages+0x588/0x67c
- alloc_rx_agg+0xa0/0x190 [r8152 ...]
- r8152_poll+0x270/0x760 [r8152 ...]
- __napi_poll+0x44/0x1ec
- net_rx_action+0x100/0x300
- __do_softirq+0xec/0x38c
- run_ksoftirqd+0x38/0xec
- smpboot_thread_fn+0xb8/0x248
- kthread+0x134/0x154
- ret_from_fork+0x10/0x20
-
-On a fragmented system it's normal that order 3 allocations will
-sometimes fail, especially atomic ones. The driver handles these
-failures fine and the WARN just creates spam in the logs for this
-case. The __GFP_NOWARN flag is exactly for this situation, so add it
-to the allocation.
-
-NOTE: my testing is on a 5.15 system, but there should be no reason
-that this would be fundamentally different on a mainline kernel.
-
-Signed-off-by: Douglas Anderson <dianders@chromium.org>
-Acked-by: Hayes Wang <hayeswang@realtek.com>
-Link: https://lore.kernel.org/r/20230406171411.1.I84dbef45786af440fd269b71e9436a96a8e7a152@changeid
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/usb/r8152.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/net/usb/r8152.c
-+++ b/drivers/net/usb/r8152.c
-@@ -1947,7 +1947,7 @@ static struct rx_agg *alloc_rx_agg(struc
- if (!rx_agg)
- return NULL;
-
-- rx_agg->page = alloc_pages(mflags | __GFP_COMP, order);
-+ rx_agg->page = alloc_pages(mflags | __GFP_COMP | __GFP_NOWARN, order);
- if (!rx_agg->page)
- goto free_rx;
-
+++ /dev/null
-From 57df0fb9d511f91202114813e90128d65c0589f0 Mon Sep 17 00:00:00 2001
-From: Hayes Wang <hayeswang@realtek.com>
-Date: Wed, 26 Jul 2023 11:08:07 +0800
-Subject: [PATCH] r8152: adjust generic_ocp_write function
-
-Reduce the control transfer if all bytes of first or the last DWORD are
-written.
-
-The original method is to split the control transfer into three parts
-(the first DWORD, middle continuous data, and the last DWORD). However,
-they could be combined if whole bytes of the first DWORD or last DWORD
-are written. That is, the first DWORD or the last DWORD could be combined
-with the middle continuous data, if the byte_en is 0xff.
-
-Signed-off-by: Hayes Wang <hayeswang@realtek.com>
-Link: https://lore.kernel.org/r/20230726030808.9093-418-nic_swsd@realtek.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/usb/r8152.c | 29 ++++++++++++++++++-----------
- 1 file changed, 18 insertions(+), 11 deletions(-)
-
---- a/drivers/net/usb/r8152.c
-+++ b/drivers/net/usb/r8152.c
-@@ -1313,16 +1313,24 @@ static int generic_ocp_write(struct r815
- byteen_end = byteen & BYTE_EN_END_MASK;
-
- byen = byteen_start | (byteen_start << 4);
-- ret = set_registers(tp, index, type | byen, 4, data);
-- if (ret < 0)
-- goto error1;
--
-- index += 4;
-- data += 4;
-- size -= 4;
-
-- if (size) {
-+ /* Split the first DWORD if the byte_en is not 0xff */
-+ if (byen != BYTE_EN_DWORD) {
-+ ret = set_registers(tp, index, type | byen, 4, data);
-+ if (ret < 0)
-+ goto error1;
-+
-+ index += 4;
-+ data += 4;
- size -= 4;
-+ }
-+
-+ if (size) {
-+ byen = byteen_end | (byteen_end >> 4);
-+
-+ /* Split the last DWORD if the byte_en is not 0xff */
-+ if (byen != BYTE_EN_DWORD)
-+ size -= 4;
-
- while (size) {
- if (size > limit) {
-@@ -1349,10 +1357,9 @@ static int generic_ocp_write(struct r815
- }
- }
-
-- byen = byteen_end | (byteen_end >> 4);
-- ret = set_registers(tp, index, type | byen, 4, data);
-- if (ret < 0)
-- goto error1;
-+ /* Set the last DWORD */
-+ if (byen != BYTE_EN_DWORD)
-+ ret = set_registers(tp, index, type | byen, 4, data);
- }
-
- error1:
+++ /dev/null
-From e5c266a61186b462c388c53a3564c375e72f2244 Mon Sep 17 00:00:00 2001
-From: Hayes Wang <hayeswang@realtek.com>
-Date: Wed, 26 Jul 2023 11:08:08 +0800
-Subject: [PATCH] r8152: set bp in bulk
-
-PLA_BP_0 ~ PLA_BP_15 (0xfc28 ~ 0xfc46) are continuous registers, so we
-could combine the control transfers into one control transfer.
-
-Signed-off-by: Hayes Wang <hayeswang@realtek.com>
-Link: https://lore.kernel.org/r/20230726030808.9093-419-nic_swsd@realtek.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/usb/r8152.c | 75 ++++++++++++++---------------------------
- 1 file changed, 25 insertions(+), 50 deletions(-)
-
---- a/drivers/net/usb/r8152.c
-+++ b/drivers/net/usb/r8152.c
-@@ -3990,29 +3990,10 @@ static void rtl_reset_bmu(struct r8152 *
- /* Clear the bp to stop the firmware before loading a new one */
- static void rtl_clear_bp(struct r8152 *tp, u16 type)
- {
-- switch (tp->version) {
-- case RTL_VER_01:
-- case RTL_VER_02:
-- case RTL_VER_07:
-- break;
-- case RTL_VER_03:
-- case RTL_VER_04:
-- case RTL_VER_05:
-- case RTL_VER_06:
-- ocp_write_byte(tp, type, PLA_BP_EN, 0);
-- break;
-- case RTL_VER_14:
-- ocp_write_word(tp, type, USB_BP2_EN, 0);
-+ u16 bp[16] = {0};
-+ u16 bp_num;
-
-- ocp_write_word(tp, type, USB_BP_8, 0);
-- ocp_write_word(tp, type, USB_BP_9, 0);
-- ocp_write_word(tp, type, USB_BP_10, 0);
-- ocp_write_word(tp, type, USB_BP_11, 0);
-- ocp_write_word(tp, type, USB_BP_12, 0);
-- ocp_write_word(tp, type, USB_BP_13, 0);
-- ocp_write_word(tp, type, USB_BP_14, 0);
-- ocp_write_word(tp, type, USB_BP_15, 0);
-- break;
-+ switch (tp->version) {
- case RTL_VER_08:
- case RTL_VER_09:
- case RTL_VER_10:
-@@ -4020,32 +4001,31 @@ static void rtl_clear_bp(struct r8152 *t
- case RTL_VER_12:
- case RTL_VER_13:
- case RTL_VER_15:
-- default:
- if (type == MCU_TYPE_USB) {
- ocp_write_word(tp, MCU_TYPE_USB, USB_BP2_EN, 0);
--
-- ocp_write_word(tp, MCU_TYPE_USB, USB_BP_8, 0);
-- ocp_write_word(tp, MCU_TYPE_USB, USB_BP_9, 0);
-- ocp_write_word(tp, MCU_TYPE_USB, USB_BP_10, 0);
-- ocp_write_word(tp, MCU_TYPE_USB, USB_BP_11, 0);
-- ocp_write_word(tp, MCU_TYPE_USB, USB_BP_12, 0);
-- ocp_write_word(tp, MCU_TYPE_USB, USB_BP_13, 0);
-- ocp_write_word(tp, MCU_TYPE_USB, USB_BP_14, 0);
-- ocp_write_word(tp, MCU_TYPE_USB, USB_BP_15, 0);
-- } else {
-- ocp_write_byte(tp, MCU_TYPE_PLA, PLA_BP_EN, 0);
-+ bp_num = 16;
-+ break;
- }
-+ fallthrough;
-+ case RTL_VER_03:
-+ case RTL_VER_04:
-+ case RTL_VER_05:
-+ case RTL_VER_06:
-+ ocp_write_byte(tp, type, PLA_BP_EN, 0);
-+ fallthrough;
-+ case RTL_VER_01:
-+ case RTL_VER_02:
-+ case RTL_VER_07:
-+ bp_num = 8;
-+ break;
-+ case RTL_VER_14:
-+ default:
-+ ocp_write_word(tp, type, USB_BP2_EN, 0);
-+ bp_num = 16;
- break;
- }
-
-- ocp_write_word(tp, type, PLA_BP_0, 0);
-- ocp_write_word(tp, type, PLA_BP_1, 0);
-- ocp_write_word(tp, type, PLA_BP_2, 0);
-- ocp_write_word(tp, type, PLA_BP_3, 0);
-- ocp_write_word(tp, type, PLA_BP_4, 0);
-- ocp_write_word(tp, type, PLA_BP_5, 0);
-- ocp_write_word(tp, type, PLA_BP_6, 0);
-- ocp_write_word(tp, type, PLA_BP_7, 0);
-+ generic_ocp_write(tp, PLA_BP_0, BYTE_EN_DWORD, bp_num << 1, bp, type);
-
- /* wait 3 ms to make sure the firmware is stopped */
- usleep_range(3000, 6000);
-@@ -5022,10 +5002,9 @@ static void rtl8152_fw_phy_nc_apply(stru
-
- static void rtl8152_fw_mac_apply(struct r8152 *tp, struct fw_mac *mac)
- {
-- u16 bp_en_addr, bp_index, type, bp_num, fw_ver_reg;
-+ u16 bp_en_addr, type, fw_ver_reg;
- u32 length;
- u8 *data;
-- int i;
-
- switch (__le32_to_cpu(mac->blk_hdr.type)) {
- case RTL_FW_PLA:
-@@ -5067,12 +5046,8 @@ static void rtl8152_fw_mac_apply(struct
- ocp_write_word(tp, type, __le16_to_cpu(mac->bp_ba_addr),
- __le16_to_cpu(mac->bp_ba_value));
-
-- bp_index = __le16_to_cpu(mac->bp_start);
-- bp_num = __le16_to_cpu(mac->bp_num);
-- for (i = 0; i < bp_num; i++) {
-- ocp_write_word(tp, type, bp_index, __le16_to_cpu(mac->bp[i]));
-- bp_index += 2;
-- }
-+ generic_ocp_write(tp, __le16_to_cpu(mac->bp_start), BYTE_EN_DWORD,
-+ __le16_to_cpu(mac->bp_num) << 1, mac->bp, type);
-
- bp_en_addr = __le16_to_cpu(mac->bp_en_addr);
- if (bp_en_addr)
+++ /dev/null
-From cf74eb5a5bc867258e7d0b0d1c3c4a60e1e3de2f Mon Sep 17 00:00:00 2001
-From: Jakub Kicinski <kuba@kernel.org>
-Date: Mon, 14 Aug 2023 08:35:21 -0700
-Subject: [PATCH] eth: r8152: try to use a normal budget
-
-Mario reports that loading r8152 on his system leads to a:
-
- netif_napi_add_weight() called with weight 256
-
-warning getting printed. We don't have any solid data
-on why such high budget was chosen, and it may cause
-stalls in processing other softirqs and rt threads.
-So try to switch back to the default (64) weight.
-
-If this slows down someone's system we should investigate
-which part of stopping starting the NAPI poll in this
-driver are expensive.
-
-Reported-by: Mario Limonciello <mario.limonciello@amd.com>
-Link: https://lore.kernel.org/all/0bfd445a-81f7-f702-08b0-bd5a72095e49@amd.com/
-Acked-by: Hayes Wang <hayeswang@realtek.com>
-Link: https://lore.kernel.org/r/20230814153521.2697982-1-kuba@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/usb/r8152.c | 3 +--
- 1 file changed, 1 insertion(+), 2 deletions(-)
-
---- a/drivers/net/usb/r8152.c
-+++ b/drivers/net/usb/r8152.c
-@@ -9784,8 +9784,7 @@ static int rtl8152_probe(struct usb_inte
-
- usb_set_intfdata(intf, tp);
-
-- netif_napi_add_weight(netdev, &tp->napi, r8152_poll,
-- tp->support_2500full ? 256 : 64);
-+ netif_napi_add(netdev, &tp->napi, r8152_poll);
-
- ret = register_netdev(netdev);
- if (ret != 0) {
+++ /dev/null
-From d9962b0d42029bcb40fe3c38bce06d1870fa4df4 Mon Sep 17 00:00:00 2001
-From: Douglas Anderson <dianders@chromium.org>
-Date: Fri, 20 Oct 2023 14:06:59 -0700
-Subject: [PATCH] r8152: Block future register access if register access fails
-
-Even though the functions to read/write registers can fail, most of
-the places in the r8152 driver that read/write register values don't
-check error codes. The lack of error code checking is problematic in
-at least two ways.
-
-The first problem is that the r8152 driver often uses code patterns
-similar to this:
- x = read_register()
- x = x | SOME_BIT;
- write_register(x);
-
-...with the above pattern, if the read_register() fails and returns
-garbage then we'll end up trying to write modified garbage back to the
-Realtek adapter. If the write_register() succeeds that's bad. Note
-that as of commit f53a7ad18959 ("r8152: Set memory to all 0xFFs on
-failed reg reads") the "garbage" returned by read_register() will at
-least be consistent garbage, but it is still garbage.
-
-It turns out that this problem is very serious. Writing garbage to
-some of the hardware registers on the Ethernet adapter can put the
-adapter in such a bad state that it needs to be power cycled (fully
-unplugged and plugged in again) before it can enumerate again.
-
-The second problem is that the r8152 driver generally has functions
-that are long sequences of register writes. Assuming everything will
-be OK if a random register write fails in the middle isn't a great
-assumption.
-
-One might wonder if the above two problems are real. You could ask if
-we would really have a successful write after a failed read. It turns
-out that the answer appears to be "yes, this can happen". In fact,
-we've seen at least two distinct failure modes where this happens.
-
-On a sc7180-trogdor Chromebook if you drop into kdb for a while and
-then resume, you can see:
-1. We get a "Tx timeout"
-2. The "Tx timeout" queues up a USB reset.
-3. In rtl8152_pre_reset() we try to reinit the hardware.
-4. The first several (2-9) register accesses fail with a timeout, then
- things recover.
-
-The above test case was actually fixed by the patch ("r8152: Increase
-USB control msg timeout to 5000ms as per spec") but at least shows
-that we really can see successful calls after failed ones.
-
-On a different (AMD) based Chromebook with a particular adapter, we
-found that during reboot tests we'd also sometimes get a transitory
-failure. In this case we saw -EPIPE being returned sometimes. Retrying
-worked, but retrying is not always safe for all register accesses
-since reading/writing some registers might have side effects (like
-registers that clear on read).
-
-Let's fully lock out all register access if a register access fails.
-When we do this, we'll try to queue up a USB reset and try to unlock
-register access after the reset. This is slightly tricker than it
-sounds since the r8152 driver has an optimized reset sequence that
-only works reliably after probe happens. In order to handle this, we
-avoid the optimized reset if probe didn't finish. Instead, we simply
-retry the probe routine in this case.
-
-When locking out access, we'll use the existing infrastructure that
-the driver was using when it detected we were unplugged. This keeps us
-from getting stuck in delay loops in some parts of the driver.
-
-Signed-off-by: Douglas Anderson <dianders@chromium.org>
-Reviewed-by: Grant Grundler <grundler@chromium.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/usb/r8152.c | 207 ++++++++++++++++++++++++++++++++++------
- 1 file changed, 176 insertions(+), 31 deletions(-)
-
---- a/drivers/net/usb/r8152.c
-+++ b/drivers/net/usb/r8152.c
-@@ -772,6 +772,9 @@ enum rtl8152_flags {
- SCHEDULE_TASKLET,
- GREEN_ETHERNET,
- RX_EPROTO,
-+ IN_PRE_RESET,
-+ PROBED_WITH_NO_ERRORS,
-+ PROBE_SHOULD_RETRY,
- };
-
- #define DEVICE_ID_LENOVO_USB_C_TRAVEL_HUB 0x721e
-@@ -952,6 +955,8 @@ struct r8152 {
- u8 version;
- u8 duplex;
- u8 autoneg;
-+
-+ unsigned int reg_access_reset_count;
- };
-
- /**
-@@ -1199,6 +1204,96 @@ static unsigned int agg_buf_sz = 16384;
-
- #define RTL_LIMITED_TSO_SIZE (size_to_mtu(agg_buf_sz) - sizeof(struct tx_desc))
-
-+/* If register access fails then we block access and issue a reset. If this
-+ * happens too many times in a row without a successful access then we stop
-+ * trying to reset and just leave access blocked.
-+ */
-+#define REGISTER_ACCESS_MAX_RESETS 3
-+
-+static void rtl_set_inaccessible(struct r8152 *tp)
-+{
-+ set_bit(RTL8152_INACCESSIBLE, &tp->flags);
-+ smp_mb__after_atomic();
-+}
-+
-+static void rtl_set_accessible(struct r8152 *tp)
-+{
-+ clear_bit(RTL8152_INACCESSIBLE, &tp->flags);
-+ smp_mb__after_atomic();
-+}
-+
-+static
-+int r8152_control_msg(struct r8152 *tp, unsigned int pipe, __u8 request,
-+ __u8 requesttype, __u16 value, __u16 index, void *data,
-+ __u16 size, const char *msg_tag)
-+{
-+ struct usb_device *udev = tp->udev;
-+ int ret;
-+
-+ if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
-+ return -ENODEV;
-+
-+ ret = usb_control_msg(udev, pipe, request, requesttype,
-+ value, index, data, size,
-+ USB_CTRL_GET_TIMEOUT);
-+
-+ /* No need to issue a reset to report an error if the USB device got
-+ * unplugged; just return immediately.
-+ */
-+ if (ret == -ENODEV)
-+ return ret;
-+
-+ /* If the write was successful then we're done */
-+ if (ret >= 0) {
-+ tp->reg_access_reset_count = 0;
-+ return ret;
-+ }
-+
-+ dev_err(&udev->dev,
-+ "Failed to %s %d bytes at %#06x/%#06x (%d)\n",
-+ msg_tag, size, value, index, ret);
-+
-+ /* Block all future register access until we reset. Much of the code
-+ * in the driver doesn't check for errors. Notably, many parts of the
-+ * driver do a read/modify/write of a register value without
-+ * confirming that the read succeeded. Writing back modified garbage
-+ * like this can fully wedge the adapter, requiring a power cycle.
-+ */
-+ rtl_set_inaccessible(tp);
-+
-+ /* If probe hasn't yet finished, then we'll request a retry of the
-+ * whole probe routine if we get any control transfer errors. We
-+ * never have to clear this bit since we free/reallocate the whole "tp"
-+ * structure if we retry probe.
-+ */
-+ if (!test_bit(PROBED_WITH_NO_ERRORS, &tp->flags)) {
-+ set_bit(PROBE_SHOULD_RETRY, &tp->flags);
-+ return ret;
-+ }
-+
-+ /* Failing to access registers in pre-reset is not surprising since we
-+ * wouldn't be resetting if things were behaving normally. The register
-+ * access we do in pre-reset isn't truly mandatory--we're just reusing
-+ * the disable() function and trying to be nice by powering the
-+ * adapter down before resetting it. Thus, if we're in pre-reset,
-+ * we'll return right away and not try to queue up yet another reset.
-+ * We know the post-reset is already coming.
-+ */
-+ if (test_bit(IN_PRE_RESET, &tp->flags))
-+ return ret;
-+
-+ if (tp->reg_access_reset_count < REGISTER_ACCESS_MAX_RESETS) {
-+ usb_queue_reset_device(tp->intf);
-+ tp->reg_access_reset_count++;
-+ } else if (tp->reg_access_reset_count == REGISTER_ACCESS_MAX_RESETS) {
-+ dev_err(&udev->dev,
-+ "Tried to reset %d times; giving up.\n",
-+ REGISTER_ACCESS_MAX_RESETS);
-+ }
-+
-+ return ret;
-+}
-+
- static
- int get_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void *data)
- {
-@@ -1209,9 +1304,10 @@ int get_registers(struct r8152 *tp, u16
- if (!tmp)
- return -ENOMEM;
-
-- ret = usb_control_msg(tp->udev, tp->pipe_ctrl_in,
-- RTL8152_REQ_GET_REGS, RTL8152_REQT_READ,
-- value, index, tmp, size, USB_CTRL_GET_TIMEOUT);
-+ ret = r8152_control_msg(tp, tp->pipe_ctrl_in,
-+ RTL8152_REQ_GET_REGS, RTL8152_REQT_READ,
-+ value, index, tmp, size, "read");
-+
- if (ret < 0)
- memset(data, 0xff, size);
- else
-@@ -1232,9 +1328,9 @@ int set_registers(struct r8152 *tp, u16
- if (!tmp)
- return -ENOMEM;
-
-- ret = usb_control_msg(tp->udev, tp->pipe_ctrl_out,
-- RTL8152_REQ_SET_REGS, RTL8152_REQT_WRITE,
-- value, index, tmp, size, USB_CTRL_SET_TIMEOUT);
-+ ret = r8152_control_msg(tp, tp->pipe_ctrl_out,
-+ RTL8152_REQ_SET_REGS, RTL8152_REQT_WRITE,
-+ value, index, tmp, size, "write");
-
- kfree(tmp);
-
-@@ -1243,10 +1339,8 @@ int set_registers(struct r8152 *tp, u16
-
- static void rtl_set_unplug(struct r8152 *tp)
- {
-- if (tp->udev->state == USB_STATE_NOTATTACHED) {
-- set_bit(RTL8152_INACCESSIBLE, &tp->flags);
-- smp_mb__after_atomic();
-- }
-+ if (tp->udev->state == USB_STATE_NOTATTACHED)
-+ rtl_set_inaccessible(tp);
- }
-
- static int generic_ocp_read(struct r8152 *tp, u16 index, u16 size,
-@@ -8275,7 +8369,7 @@ static int rtl8152_pre_reset(struct usb_
- struct r8152 *tp = usb_get_intfdata(intf);
- struct net_device *netdev;
-
-- if (!tp)
-+ if (!tp || !test_bit(PROBED_WITH_NO_ERRORS, &tp->flags))
- return 0;
-
- netdev = tp->netdev;
-@@ -8290,7 +8384,9 @@ static int rtl8152_pre_reset(struct usb_
- napi_disable(&tp->napi);
- if (netif_carrier_ok(netdev)) {
- mutex_lock(&tp->control);
-+ set_bit(IN_PRE_RESET, &tp->flags);
- tp->rtl_ops.disable(tp);
-+ clear_bit(IN_PRE_RESET, &tp->flags);
- mutex_unlock(&tp->control);
- }
-
-@@ -8303,9 +8399,11 @@ static int rtl8152_post_reset(struct usb
- struct net_device *netdev;
- struct sockaddr sa;
-
-- if (!tp)
-+ if (!tp || !test_bit(PROBED_WITH_NO_ERRORS, &tp->flags))
- return 0;
-
-+ rtl_set_accessible(tp);
-+
- /* reset the MAC address in case of policy change */
- if (determine_ethernet_addr(tp, &sa) >= 0) {
- rtnl_lock();
-@@ -9507,17 +9605,29 @@ static u8 __rtl_get_hw_ver(struct usb_de
- __le32 *tmp;
- u8 version;
- int ret;
-+ int i;
-
- tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
- if (!tmp)
- return 0;
-
-- ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
-- RTL8152_REQ_GET_REGS, RTL8152_REQT_READ,
-- PLA_TCR0, MCU_TYPE_PLA, tmp, sizeof(*tmp),
-- USB_CTRL_GET_TIMEOUT);
-- if (ret > 0)
-- ocp_data = (__le32_to_cpu(*tmp) >> 16) & VERSION_MASK;
-+ /* Retry up to 3 times in case there is a transitory error. We do this
-+ * since retrying a read of the version is always safe and this
-+ * function doesn't take advantage of r8152_control_msg().
-+ */
-+ for (i = 0; i < 3; i++) {
-+ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
-+ RTL8152_REQ_GET_REGS, RTL8152_REQT_READ,
-+ PLA_TCR0, MCU_TYPE_PLA, tmp, sizeof(*tmp),
-+ USB_CTRL_GET_TIMEOUT);
-+ if (ret > 0) {
-+ ocp_data = (__le32_to_cpu(*tmp) >> 16) & VERSION_MASK;
-+ break;
-+ }
-+ }
-+
-+ if (i != 0 && ret > 0)
-+ dev_warn(&udev->dev, "Needed %d retries to read version\n", i);
-
- kfree(tmp);
-
-@@ -9616,25 +9726,14 @@ static bool rtl8152_supports_lenovo_macp
- return 0;
- }
-
--static int rtl8152_probe(struct usb_interface *intf,
-- const struct usb_device_id *id)
-+static int rtl8152_probe_once(struct usb_interface *intf,
-+ const struct usb_device_id *id, u8 version)
- {
- struct usb_device *udev = interface_to_usbdev(intf);
- struct r8152 *tp;
- struct net_device *netdev;
-- u8 version;
- int ret;
-
-- if (intf->cur_altsetting->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC)
-- return -ENODEV;
--
-- if (!rtl_check_vendor_ok(intf))
-- return -ENODEV;
--
-- version = rtl8152_get_version(intf);
-- if (version == RTL_VER_UNKNOWN)
-- return -ENODEV;
--
- usb_reset_device(udev);
- netdev = alloc_etherdev(sizeof(struct r8152));
- if (!netdev) {
-@@ -9797,10 +9896,20 @@ static int rtl8152_probe(struct usb_inte
- else
- device_set_wakeup_enable(&udev->dev, false);
-
-+ /* If we saw a control transfer error while probing then we may
-+ * want to try probe() again. Consider this an error.
-+ */
-+ if (test_bit(PROBE_SHOULD_RETRY, &tp->flags))
-+ goto out2;
-+
-+ set_bit(PROBED_WITH_NO_ERRORS, &tp->flags);
- netif_info(tp, probe, netdev, "%s\n", DRIVER_VERSION);
-
- return 0;
-
-+out2:
-+ unregister_netdev(netdev);
-+
- out1:
- tasklet_kill(&tp->tx_tl);
- cancel_delayed_work_sync(&tp->hw_phy_work);
-@@ -9809,10 +9918,46 @@ out1:
- rtl8152_release_firmware(tp);
- usb_set_intfdata(intf, NULL);
- out:
-+ if (test_bit(PROBE_SHOULD_RETRY, &tp->flags))
-+ ret = -EAGAIN;
-+
- free_netdev(netdev);
- return ret;
- }
-
-+#define RTL8152_PROBE_TRIES 3
-+
-+static int rtl8152_probe(struct usb_interface *intf,
-+ const struct usb_device_id *id)
-+{
-+ u8 version;
-+ int ret;
-+ int i;
-+
-+ if (intf->cur_altsetting->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC)
-+ return -ENODEV;
-+
-+ if (!rtl_check_vendor_ok(intf))
-+ return -ENODEV;
-+
-+ version = rtl8152_get_version(intf);
-+ if (version == RTL_VER_UNKNOWN)
-+ return -ENODEV;
-+
-+ for (i = 0; i < RTL8152_PROBE_TRIES; i++) {
-+ ret = rtl8152_probe_once(intf, id, version);
-+ if (ret != -EAGAIN)
-+ break;
-+ }
-+ if (ret == -EAGAIN) {
-+ dev_err(&intf->dev,
-+ "r8152 failed probe after %d tries; giving up\n", i);
-+ return -ENODEV;
-+ }
-+
-+ return ret;
-+}
-+
- static void rtl8152_disconnect(struct usb_interface *intf)
- {
- struct r8152 *tp = usb_get_intfdata(intf);
+++ /dev/null
-From 66eee612a1ba39f9a76a9ace4a34d012044767fb Mon Sep 17 00:00:00 2001
-From: Hayes Wang <hayeswang@realtek.com>
-Date: Tue, 26 Sep 2023 19:17:13 +0800
-Subject: [PATCH] r8152: break the loop when the budget is exhausted
-
-[ Upstream commit 2cf51f931797d9a47e75d999d0993a68cbd2a560 ]
-
-A bulk transfer of the USB may contain many packets. And, the total
-number of the packets in the bulk transfer may be more than budget.
-
-Originally, only budget packets would be handled by napi_gro_receive(),
-and the other packets would be queued in the driver for next schedule.
-
-This patch would break the loop about getting next bulk transfer, when
-the budget is exhausted. That is, only the current bulk transfer would
-be handled, and the other bulk transfers would be queued for next
-schedule. Besides, the packets which are more than the budget in the
-current bulk trasnfer would be still queued in the driver, as the
-original method.
-
-In addition, a bulk transfer wouldn't contain more than 400 packets, so
-the check of queue length is unnecessary. Therefore, I replace it with
-WARN_ON_ONCE().
-
-Fixes: cf74eb5a5bc8 ("eth: r8152: try to use a normal budget")
-Signed-off-by: Hayes Wang <hayeswang@realtek.com>
-Link: https://lore.kernel.org/r/20230926111714.9448-433-nic_swsd@realtek.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- drivers/net/usb/r8152.c | 18 +++++++++++++-----
- 1 file changed, 13 insertions(+), 5 deletions(-)
-
---- a/drivers/net/usb/r8152.c
-+++ b/drivers/net/usb/r8152.c
-@@ -2542,7 +2542,7 @@ static int rx_bottom(struct r8152 *tp, i
- }
- }
-
-- if (list_empty(&tp->rx_done))
-+ if (list_empty(&tp->rx_done) || work_done >= budget)
- goto out1;
-
- clear_bit(RX_EPROTO, &tp->flags);
-@@ -2558,6 +2558,15 @@ static int rx_bottom(struct r8152 *tp, i
- struct urb *urb;
- u8 *rx_data;
-
-+ /* A bulk transfer of USB may contain may packets, so the
-+ * total packets may more than the budget. Deal with all
-+ * packets in current bulk transfer, and stop to handle the
-+ * next bulk transfer until next schedule, if budget is
-+ * exhausted.
-+ */
-+ if (work_done >= budget)
-+ break;
-+
- list_del_init(cursor);
-
- agg = list_entry(cursor, struct rx_agg, list);
-@@ -2577,9 +2586,7 @@ static int rx_bottom(struct r8152 *tp, i
- unsigned int pkt_len, rx_frag_head_sz;
- struct sk_buff *skb;
-
-- /* limit the skb numbers for rx_queue */
-- if (unlikely(skb_queue_len(&tp->rx_queue) >= 1000))
-- break;
-+ WARN_ON_ONCE(skb_queue_len(&tp->rx_queue) >= 1000);
-
- pkt_len = le32_to_cpu(rx_desc->opts1) & RX_LEN_MASK;
- if (pkt_len < ETH_ZLEN)
-@@ -2657,9 +2664,10 @@ submit:
- }
- }
-
-+ /* Splice the remained list back to rx_done for next schedule */
- if (!list_empty(&rx_queue)) {
- spin_lock_irqsave(&tp->rx_lock, flags);
-- list_splice_tail(&rx_queue, &tp->rx_done);
-+ list_splice(&rx_queue, &tp->rx_done);
- spin_unlock_irqrestore(&tp->rx_lock, flags);
- }
-
+++ /dev/null
-From 1b0fce8c8e69485e49a7d34aac3d4c2a2aa15d62 Mon Sep 17 00:00:00 2001
-From: Davide Tronchin <davide.tronchin.94@gmail.com>
-Date: Thu, 29 Jun 2023 12:37:36 +0200
-Subject: [PATCH] net: usb: cdc_ether: add u-blox 0x1313 composition.
-
-Add CDC-ECM support for LARA-R6 01B.
-
-The new LARA-R6 product variant identified by the "01B" string can be
-configured (by AT interface) in three different USB modes:
-* Default mode (Vendor ID: 0x1546 Product ID: 0x1311) with 4 serial
-interfaces
-* RmNet mode (Vendor ID: 0x1546 Product ID: 0x1312) with 4 serial
-interfaces and 1 RmNet virtual network interface
-* CDC-ECM mode (Vendor ID: 0x1546 Product ID: 0x1313) with 4 serial
-interface and 1 CDC-ECM virtual network interface
-The first 4 interfaces of all the 3 configurations (default, RmNet, ECM)
-are the same.
-
-In CDC-ECM mode LARA-R6 01B exposes the following interfaces:
-If 0: Diagnostic
-If 1: AT parser
-If 2: AT parser
-If 3: AT parset/alternative functions
-If 4: CDC-ECM interface
-
-Signed-off-by: Davide Tronchin <davide.tronchin.94@gmail.com>
-Reviewed-by: Simon Horman <simon.horman@corigine.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/usb/cdc_ether.c | 6 ++++++
- 1 file changed, 6 insertions(+)
-
---- a/drivers/net/usb/cdc_ether.c
-+++ b/drivers/net/usb/cdc_ether.c
-@@ -879,6 +879,12 @@ static const struct usb_device_id produc
- USB_CDC_PROTO_NONE),
- .driver_info = (unsigned long)&wwan_info,
- }, {
-+ /* U-blox LARA-R6 01B */
-+ USB_DEVICE_AND_INTERFACE_INFO(UBLOX_VENDOR_ID, 0x1313, USB_CLASS_COMM,
-+ USB_CDC_SUBCLASS_ETHERNET,
-+ USB_CDC_PROTO_NONE),
-+ .driver_info = (unsigned long)&wwan_info,
-+}, {
- /* ZTE modules */
- USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, USB_CLASS_COMM,
- USB_CDC_SUBCLASS_ETHERNET,
+++ /dev/null
-From 156a5bb89ca6f3edd2be0bfd0de15e575442927e Mon Sep 17 00:00:00 2001
-From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
-Date: Tue, 3 Jan 2023 15:12:47 +0200
-Subject: [PATCH] leds: Move led_init_default_state_get() to the global header
-
-There are users inside and outside LED framework that have implemented
-a local copy of led_init_default_state_get(). In order to deduplicate
-that, as the first step move the declaration from LED header to the
-global one.
-
-Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
-Signed-off-by: Lee Jones <lee@kernel.org>
-Link: https://lore.kernel.org/r/20230103131256.33894-3-andriy.shevchenko@linux.intel.com
----
- drivers/leds/leds.h | 1 -
- include/linux/leds.h | 2 ++
- 2 files changed, 2 insertions(+), 1 deletion(-)
-
---- a/drivers/leds/leds.h
-+++ b/drivers/leds/leds.h
-@@ -27,7 +27,6 @@ ssize_t led_trigger_read(struct file *fi
- ssize_t led_trigger_write(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr, char *buf,
- loff_t pos, size_t count);
--enum led_default_state led_init_default_state_get(struct fwnode_handle *fwnode);
-
- extern struct rw_semaphore leds_list_lock;
- extern struct list_head leds_list;
---- a/include/linux/leds.h
-+++ b/include/linux/leds.h
-@@ -63,6 +63,8 @@ struct led_init_data {
- bool devname_mandatory;
- };
-
-+enum led_default_state led_init_default_state_get(struct fwnode_handle *fwnode);
-+
- struct led_hw_trigger_type {
- int dummy;
- };
+++ /dev/null
-From 3e8b4d6277fd19d98c817576954dd6a4ff3caa2b Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Mon, 17 Apr 2023 17:17:23 +0200
-Subject: [PATCH 1/9] net: dsa: qca8k: move qca8k_port_to_phy() to header
-
-Move qca8k_port_to_phy() to qca8k header as it's useful for future
-reference in Switch LEDs module since the same logic is applied to get
-the right index of the switch port.
-Make it inline as it's simple function that just decrease the port.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Reviewed-by: Michal Kubiak <michal.kubiak@intel.com>
-Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/qca/qca8k-8xxx.c | 15 ---------------
- drivers/net/dsa/qca/qca8k.h | 14 ++++++++++++++
- 2 files changed, 14 insertions(+), 15 deletions(-)
-
---- a/drivers/net/dsa/qca/qca8k-8xxx.c
-+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
-@@ -789,21 +789,6 @@ err_clear_skb:
- return ret;
- }
-
--static u32
--qca8k_port_to_phy(int port)
--{
-- /* From Andrew Lunn:
-- * Port 0 has no internal phy.
-- * Port 1 has an internal PHY at MDIO address 0.
-- * Port 2 has an internal PHY at MDIO address 1.
-- * ...
-- * Port 5 has an internal PHY at MDIO address 4.
-- * Port 6 has no internal PHY.
-- */
--
-- return port - 1;
--}
--
- static int
- qca8k_mdio_busy_wait(struct mii_bus *bus, u32 reg, u32 mask)
- {
---- a/drivers/net/dsa/qca/qca8k.h
-+++ b/drivers/net/dsa/qca/qca8k.h
-@@ -421,6 +421,20 @@ struct qca8k_fdb {
- u8 mac[6];
- };
-
-+static inline u32 qca8k_port_to_phy(int port)
-+{
-+ /* From Andrew Lunn:
-+ * Port 0 has no internal phy.
-+ * Port 1 has an internal PHY at MDIO address 0.
-+ * Port 2 has an internal PHY at MDIO address 1.
-+ * ...
-+ * Port 5 has an internal PHY at MDIO address 4.
-+ * Port 6 has no internal PHY.
-+ */
-+
-+ return port - 1;
-+}
-+
- /* Common setup function */
- extern const struct qca8k_mib_desc ar8327_mib[];
- extern const struct regmap_access_table qca8k_readable_table;
+++ /dev/null
-From 1e264f9d2918b5737023c44a23ae04def1095210 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Mon, 17 Apr 2023 17:17:24 +0200
-Subject: [PATCH 2/9] net: dsa: qca8k: add LEDs basic support
-
-Add LEDs basic support for qca8k Switch Family by adding basic
-brightness_set() support.
-
-Since these LEDs refelect port status, the default label is set to
-":port". DT binding should describe the color and function of the
-LEDs using standard LEDs api.
-Each LED always have the device name as prefix. The device name is
-composed from the mii bus id and the PHY addr resulting in example
-names like:
-- qca8k-0.0:00:amber:lan
-- qca8k-0.0:00:white:lan
-- qca8k-0.0:01:amber:lan
-- qca8k-0.0:01:white:lan
-
-These LEDs supports only blocking variant of the brightness_set()
-function since they can sleep during access of the switch leds to set
-the brightness.
-
-While at it add to the qca8k header file each mode defined by the Switch
-Documentation for future use.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/qca/Kconfig | 8 ++
- drivers/net/dsa/qca/Makefile | 3 +
- drivers/net/dsa/qca/qca8k-8xxx.c | 5 +
- drivers/net/dsa/qca/qca8k-leds.c | 239 +++++++++++++++++++++++++++++++
- drivers/net/dsa/qca/qca8k.h | 60 ++++++++
- drivers/net/dsa/qca/qca8k_leds.h | 16 +++
- 6 files changed, 331 insertions(+)
- create mode 100644 drivers/net/dsa/qca/qca8k-leds.c
- create mode 100644 drivers/net/dsa/qca/qca8k_leds.h
-
---- a/drivers/net/dsa/qca/Kconfig
-+++ b/drivers/net/dsa/qca/Kconfig
-@@ -15,3 +15,11 @@ config NET_DSA_QCA8K
- help
- This enables support for the Qualcomm Atheros QCA8K Ethernet
- switch chips.
-+
-+config NET_DSA_QCA8K_LEDS_SUPPORT
-+ bool "Qualcomm Atheros QCA8K Ethernet switch family LEDs support"
-+ depends on NET_DSA_QCA8K
-+ depends on LEDS_CLASS
-+ help
-+ This enabled support for LEDs present on the Qualcomm Atheros
-+ QCA8K Ethernet switch chips.
---- a/drivers/net/dsa/qca/Makefile
-+++ b/drivers/net/dsa/qca/Makefile
-@@ -2,3 +2,6 @@
- obj-$(CONFIG_NET_DSA_AR9331) += ar9331.o
- obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o
- qca8k-y += qca8k-common.o qca8k-8xxx.o
-+ifdef CONFIG_NET_DSA_QCA8K_LEDS_SUPPORT
-+qca8k-y += qca8k-leds.o
-+endif
---- a/drivers/net/dsa/qca/qca8k-8xxx.c
-+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
-@@ -22,6 +22,7 @@
- #include <linux/dsa/tag_qca.h>
-
- #include "qca8k.h"
-+#include "qca8k_leds.h"
-
- static void
- qca8k_split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)
-@@ -1851,6 +1852,10 @@ qca8k_setup(struct dsa_switch *ds)
- if (ret)
- return ret;
-
-+ ret = qca8k_setup_led_ctrl(priv);
-+ if (ret)
-+ return ret;
-+
- qca8k_setup_pcs(priv, &priv->pcs_port_0, 0);
- qca8k_setup_pcs(priv, &priv->pcs_port_6, 6);
-
---- /dev/null
-+++ b/drivers/net/dsa/qca/qca8k-leds.c
-@@ -0,0 +1,239 @@
-+// SPDX-License-Identifier: GPL-2.0
-+#include <linux/regmap.h>
-+#include <net/dsa.h>
-+
-+#include "qca8k.h"
-+#include "qca8k_leds.h"
-+
-+static int
-+qca8k_get_enable_led_reg(int port_num, int led_num, struct qca8k_led_pattern_en *reg_info)
-+{
-+ switch (port_num) {
-+ case 0:
-+ reg_info->reg = QCA8K_LED_CTRL_REG(led_num);
-+ reg_info->shift = QCA8K_LED_PHY0123_CONTROL_RULE_SHIFT;
-+ break;
-+ case 1:
-+ case 2:
-+ case 3:
-+ /* Port 123 are controlled on a different reg */
-+ reg_info->reg = QCA8K_LED_CTRL3_REG;
-+ reg_info->shift = QCA8K_LED_PHY123_PATTERN_EN_SHIFT(port_num, led_num);
-+ break;
-+ case 4:
-+ reg_info->reg = QCA8K_LED_CTRL_REG(led_num);
-+ reg_info->shift = QCA8K_LED_PHY4_CONTROL_RULE_SHIFT;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ return 0;
-+}
-+
-+static int
-+qca8k_led_brightness_set(struct qca8k_led *led,
-+ enum led_brightness brightness)
-+{
-+ struct qca8k_led_pattern_en reg_info;
-+ struct qca8k_priv *priv = led->priv;
-+ u32 mask, val;
-+
-+ qca8k_get_enable_led_reg(led->port_num, led->led_num, ®_info);
-+
-+ val = QCA8K_LED_ALWAYS_OFF;
-+ if (brightness)
-+ val = QCA8K_LED_ALWAYS_ON;
-+
-+ /* HW regs to control brightness is special and port 1-2-3
-+ * are placed in a different reg.
-+ *
-+ * To control port 0 brightness:
-+ * - the 2 bit (15, 14) of:
-+ * - QCA8K_LED_CTRL0_REG for led1
-+ * - QCA8K_LED_CTRL1_REG for led2
-+ * - QCA8K_LED_CTRL2_REG for led3
-+ *
-+ * To control port 4:
-+ * - the 2 bit (31, 30) of:
-+ * - QCA8K_LED_CTRL0_REG for led1
-+ * - QCA8K_LED_CTRL1_REG for led2
-+ * - QCA8K_LED_CTRL2_REG for led3
-+ *
-+ * To control port 1:
-+ * - the 2 bit at (9, 8) of QCA8K_LED_CTRL3_REG are used for led1
-+ * - the 2 bit at (11, 10) of QCA8K_LED_CTRL3_REG are used for led2
-+ * - the 2 bit at (13, 12) of QCA8K_LED_CTRL3_REG are used for led3
-+ *
-+ * To control port 2:
-+ * - the 2 bit at (15, 14) of QCA8K_LED_CTRL3_REG are used for led1
-+ * - the 2 bit at (17, 16) of QCA8K_LED_CTRL3_REG are used for led2
-+ * - the 2 bit at (19, 18) of QCA8K_LED_CTRL3_REG are used for led3
-+ *
-+ * To control port 3:
-+ * - the 2 bit at (21, 20) of QCA8K_LED_CTRL3_REG are used for led1
-+ * - the 2 bit at (23, 22) of QCA8K_LED_CTRL3_REG are used for led2
-+ * - the 2 bit at (25, 24) of QCA8K_LED_CTRL3_REG are used for led3
-+ *
-+ * To abstract this and have less code, we use the port and led numm
-+ * to calculate the shift and the correct reg due to this problem of
-+ * not having a 1:1 map of LED with the regs.
-+ */
-+ if (led->port_num == 0 || led->port_num == 4) {
-+ mask = QCA8K_LED_PATTERN_EN_MASK;
-+ val <<= QCA8K_LED_PATTERN_EN_SHIFT;
-+ } else {
-+ mask = QCA8K_LED_PHY123_PATTERN_EN_MASK;
-+ }
-+
-+ return regmap_update_bits(priv->regmap, reg_info.reg,
-+ mask << reg_info.shift,
-+ val << reg_info.shift);
-+}
-+
-+static int
-+qca8k_cled_brightness_set_blocking(struct led_classdev *ldev,
-+ enum led_brightness brightness)
-+{
-+ struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev);
-+
-+ return qca8k_led_brightness_set(led, brightness);
-+}
-+
-+static enum led_brightness
-+qca8k_led_brightness_get(struct qca8k_led *led)
-+{
-+ struct qca8k_led_pattern_en reg_info;
-+ struct qca8k_priv *priv = led->priv;
-+ u32 val;
-+ int ret;
-+
-+ qca8k_get_enable_led_reg(led->port_num, led->led_num, ®_info);
-+
-+ ret = regmap_read(priv->regmap, reg_info.reg, &val);
-+ if (ret)
-+ return 0;
-+
-+ val >>= reg_info.shift;
-+
-+ if (led->port_num == 0 || led->port_num == 4) {
-+ val &= QCA8K_LED_PATTERN_EN_MASK;
-+ val >>= QCA8K_LED_PATTERN_EN_SHIFT;
-+ } else {
-+ val &= QCA8K_LED_PHY123_PATTERN_EN_MASK;
-+ }
-+
-+ /* Assume brightness ON only when the LED is set to always ON */
-+ return val == QCA8K_LED_ALWAYS_ON;
-+}
-+
-+static int
-+qca8k_parse_port_leds(struct qca8k_priv *priv, struct fwnode_handle *port, int port_num)
-+{
-+ struct fwnode_handle *led = NULL, *leds = NULL;
-+ struct led_init_data init_data = { };
-+ struct dsa_switch *ds = priv->ds;
-+ enum led_default_state state;
-+ struct qca8k_led *port_led;
-+ int led_num, led_index;
-+ int ret;
-+
-+ leds = fwnode_get_named_child_node(port, "leds");
-+ if (!leds) {
-+ dev_dbg(priv->dev, "No Leds node specified in device tree for port %d!\n",
-+ port_num);
-+ return 0;
-+ }
-+
-+ fwnode_for_each_child_node(leds, led) {
-+ /* Reg represent the led number of the port.
-+ * Each port can have at most 3 leds attached
-+ * Commonly:
-+ * 1. is gigabit led
-+ * 2. is mbit led
-+ * 3. additional status led
-+ */
-+ if (fwnode_property_read_u32(led, "reg", &led_num))
-+ continue;
-+
-+ if (led_num >= QCA8K_LED_PORT_COUNT) {
-+ dev_warn(priv->dev, "Invalid LED reg %d defined for port %d",
-+ led_num, port_num);
-+ continue;
-+ }
-+
-+ led_index = QCA8K_LED_PORT_INDEX(port_num, led_num);
-+
-+ port_led = &priv->ports_led[led_index];
-+ port_led->port_num = port_num;
-+ port_led->led_num = led_num;
-+ port_led->priv = priv;
-+
-+ state = led_init_default_state_get(led);
-+ switch (state) {
-+ case LEDS_DEFSTATE_ON:
-+ port_led->cdev.brightness = 1;
-+ qca8k_led_brightness_set(port_led, 1);
-+ break;
-+ case LEDS_DEFSTATE_KEEP:
-+ port_led->cdev.brightness =
-+ qca8k_led_brightness_get(port_led);
-+ break;
-+ default:
-+ port_led->cdev.brightness = 0;
-+ qca8k_led_brightness_set(port_led, 0);
-+ }
-+
-+ port_led->cdev.max_brightness = 1;
-+ port_led->cdev.brightness_set_blocking = qca8k_cled_brightness_set_blocking;
-+ init_data.default_label = ":port";
-+ init_data.fwnode = led;
-+ init_data.devname_mandatory = true;
-+ init_data.devicename = kasprintf(GFP_KERNEL, "%s:0%d", ds->slave_mii_bus->id,
-+ port_num);
-+ if (!init_data.devicename)
-+ return -ENOMEM;
-+
-+ ret = devm_led_classdev_register_ext(priv->dev, &port_led->cdev, &init_data);
-+ if (ret)
-+ dev_warn(priv->dev, "Failed to init LED %d for port %d", led_num, port_num);
-+
-+ kfree(init_data.devicename);
-+ }
-+
-+ return 0;
-+}
-+
-+int
-+qca8k_setup_led_ctrl(struct qca8k_priv *priv)
-+{
-+ struct fwnode_handle *ports, *port;
-+ int port_num;
-+ int ret;
-+
-+ ports = device_get_named_child_node(priv->dev, "ports");
-+ if (!ports) {
-+ dev_info(priv->dev, "No ports node specified in device tree!");
-+ return 0;
-+ }
-+
-+ fwnode_for_each_child_node(ports, port) {
-+ if (fwnode_property_read_u32(port, "reg", &port_num))
-+ continue;
-+
-+ /* Skip checking for CPU port 0 and CPU port 6 as not supported */
-+ if (port_num == 0 || port_num == 6)
-+ continue;
-+
-+ /* Each port can have at most 3 different leds attached.
-+ * Switch port starts from 0 to 6, but port 0 and 6 are CPU
-+ * port. The port index needs to be decreased by one to identify
-+ * the correct port for LED setup.
-+ */
-+ ret = qca8k_parse_port_leds(priv, port, qca8k_port_to_phy(port_num));
-+ if (ret)
-+ return ret;
-+ }
-+
-+ return 0;
-+}
---- a/drivers/net/dsa/qca/qca8k.h
-+++ b/drivers/net/dsa/qca/qca8k.h
-@@ -11,6 +11,7 @@
- #include <linux/delay.h>
- #include <linux/regmap.h>
- #include <linux/gpio.h>
-+#include <linux/leds.h>
- #include <linux/dsa/tag_qca.h>
-
- #define QCA8K_ETHERNET_MDIO_PRIORITY 7
-@@ -85,6 +86,51 @@
- #define QCA8K_MDIO_MASTER_DATA(x) FIELD_PREP(QCA8K_MDIO_MASTER_DATA_MASK, x)
- #define QCA8K_MDIO_MASTER_MAX_PORTS 5
- #define QCA8K_MDIO_MASTER_MAX_REG 32
-+
-+/* LED control register */
-+#define QCA8K_LED_PORT_COUNT 3
-+#define QCA8K_LED_COUNT ((QCA8K_NUM_PORTS - QCA8K_NUM_CPU_PORTS) * QCA8K_LED_PORT_COUNT)
-+#define QCA8K_LED_RULE_COUNT 6
-+#define QCA8K_LED_RULE_MAX 11
-+#define QCA8K_LED_PORT_INDEX(_phy, _led) (((_phy) * QCA8K_LED_PORT_COUNT) + (_led))
-+
-+#define QCA8K_LED_PHY123_PATTERN_EN_SHIFT(_phy, _led) ((((_phy) - 1) * 6) + 8 + (2 * (_led)))
-+#define QCA8K_LED_PHY123_PATTERN_EN_MASK GENMASK(1, 0)
-+
-+#define QCA8K_LED_PHY0123_CONTROL_RULE_SHIFT 0
-+#define QCA8K_LED_PHY4_CONTROL_RULE_SHIFT 16
-+
-+#define QCA8K_LED_CTRL_REG(_i) (0x050 + (_i) * 4)
-+#define QCA8K_LED_CTRL0_REG 0x50
-+#define QCA8K_LED_CTRL1_REG 0x54
-+#define QCA8K_LED_CTRL2_REG 0x58
-+#define QCA8K_LED_CTRL3_REG 0x5C
-+#define QCA8K_LED_CTRL_SHIFT(_i) (((_i) % 2) * 16)
-+#define QCA8K_LED_CTRL_MASK GENMASK(15, 0)
-+#define QCA8K_LED_RULE_MASK GENMASK(13, 0)
-+#define QCA8K_LED_BLINK_FREQ_MASK GENMASK(1, 0)
-+#define QCA8K_LED_BLINK_FREQ_SHITF 0
-+#define QCA8K_LED_BLINK_2HZ 0
-+#define QCA8K_LED_BLINK_4HZ 1
-+#define QCA8K_LED_BLINK_8HZ 2
-+#define QCA8K_LED_BLINK_AUTO 3
-+#define QCA8K_LED_LINKUP_OVER_MASK BIT(2)
-+#define QCA8K_LED_TX_BLINK_MASK BIT(4)
-+#define QCA8K_LED_RX_BLINK_MASK BIT(5)
-+#define QCA8K_LED_COL_BLINK_MASK BIT(7)
-+#define QCA8K_LED_LINK_10M_EN_MASK BIT(8)
-+#define QCA8K_LED_LINK_100M_EN_MASK BIT(9)
-+#define QCA8K_LED_LINK_1000M_EN_MASK BIT(10)
-+#define QCA8K_LED_POWER_ON_LIGHT_MASK BIT(11)
-+#define QCA8K_LED_HALF_DUPLEX_MASK BIT(12)
-+#define QCA8K_LED_FULL_DUPLEX_MASK BIT(13)
-+#define QCA8K_LED_PATTERN_EN_MASK GENMASK(15, 14)
-+#define QCA8K_LED_PATTERN_EN_SHIFT 14
-+#define QCA8K_LED_ALWAYS_OFF 0
-+#define QCA8K_LED_ALWAYS_BLINK_4HZ 1
-+#define QCA8K_LED_ALWAYS_ON 2
-+#define QCA8K_LED_RULE_CONTROLLED 3
-+
- #define QCA8K_GOL_MAC_ADDR0 0x60
- #define QCA8K_GOL_MAC_ADDR1 0x64
- #define QCA8K_MAX_FRAME_SIZE 0x78
-@@ -382,6 +428,19 @@ struct qca8k_pcs {
- int port;
- };
-
-+struct qca8k_led_pattern_en {
-+ u32 reg;
-+ u8 shift;
-+};
-+
-+struct qca8k_led {
-+ u8 port_num;
-+ u8 led_num;
-+ u16 old_rule;
-+ struct qca8k_priv *priv;
-+ struct led_classdev cdev;
-+};
-+
- struct qca8k_priv {
- u8 switch_id;
- u8 switch_revision;
-@@ -406,6 +465,7 @@ struct qca8k_priv {
- struct qca8k_pcs pcs_port_0;
- struct qca8k_pcs pcs_port_6;
- const struct qca8k_match_data *info;
-+ struct qca8k_led ports_led[QCA8K_LED_COUNT];
- };
-
- struct qca8k_mib_desc {
---- /dev/null
-+++ b/drivers/net/dsa/qca/qca8k_leds.h
-@@ -0,0 +1,16 @@
-+/* SPDX-License-Identifier: GPL-2.0-only */
-+
-+#ifndef __QCA8K_LEDS_H
-+#define __QCA8K_LEDS_H
-+
-+/* Leds Support function */
-+#ifdef CONFIG_NET_DSA_QCA8K_LEDS_SUPPORT
-+int qca8k_setup_led_ctrl(struct qca8k_priv *priv);
-+#else
-+static inline int qca8k_setup_led_ctrl(struct qca8k_priv *priv)
-+{
-+ return 0;
-+}
-+#endif
-+
-+#endif /* __QCA8K_LEDS_H */
+++ /dev/null
-From 91acadcc6e599dfc62717abcdad58a459cfb1684 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Mon, 17 Apr 2023 17:17:25 +0200
-Subject: [PATCH 3/9] net: dsa: qca8k: add LEDs blink_set() support
-
-Add LEDs blink_set() support to qca8k Switch Family.
-These LEDs support hw accellerated blinking at a fixed rate
-of 4Hz.
-
-Reject any other value since not supported by the LEDs switch.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Acked-by: Pavel Machek <pavel@ucw.cz>
-Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/qca/qca8k-leds.c | 38 ++++++++++++++++++++++++++++++++
- 1 file changed, 38 insertions(+)
-
---- a/drivers/net/dsa/qca/qca8k-leds.c
-+++ b/drivers/net/dsa/qca/qca8k-leds.c
-@@ -128,6 +128,43 @@ qca8k_led_brightness_get(struct qca8k_le
- }
-
- static int
-+qca8k_cled_blink_set(struct led_classdev *ldev,
-+ unsigned long *delay_on,
-+ unsigned long *delay_off)
-+{
-+ struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev);
-+ u32 mask, val = QCA8K_LED_ALWAYS_BLINK_4HZ;
-+ struct qca8k_led_pattern_en reg_info;
-+ struct qca8k_priv *priv = led->priv;
-+
-+ if (*delay_on == 0 && *delay_off == 0) {
-+ *delay_on = 125;
-+ *delay_off = 125;
-+ }
-+
-+ if (*delay_on != 125 || *delay_off != 125) {
-+ /* The hardware only supports blinking at 4Hz. Fall back
-+ * to software implementation in other cases.
-+ */
-+ return -EINVAL;
-+ }
-+
-+ qca8k_get_enable_led_reg(led->port_num, led->led_num, ®_info);
-+
-+ if (led->port_num == 0 || led->port_num == 4) {
-+ mask = QCA8K_LED_PATTERN_EN_MASK;
-+ val <<= QCA8K_LED_PATTERN_EN_SHIFT;
-+ } else {
-+ mask = QCA8K_LED_PHY123_PATTERN_EN_MASK;
-+ }
-+
-+ regmap_update_bits(priv->regmap, reg_info.reg, mask << reg_info.shift,
-+ val << reg_info.shift);
-+
-+ return 0;
-+}
-+
-+static int
- qca8k_parse_port_leds(struct qca8k_priv *priv, struct fwnode_handle *port, int port_num)
- {
- struct fwnode_handle *led = NULL, *leds = NULL;
-@@ -186,6 +223,7 @@ qca8k_parse_port_leds(struct qca8k_priv
-
- port_led->cdev.max_brightness = 1;
- port_led->cdev.brightness_set_blocking = qca8k_cled_brightness_set_blocking;
-+ port_led->cdev.blink_set = qca8k_cled_blink_set;
- init_data.default_label = ":port";
- init_data.fwnode = led;
- init_data.devname_mandatory = true;
+++ /dev/null
-From e5029edd53937a29801ef507cee12e657ff31ea9 Mon Sep 17 00:00:00 2001
-From: Andrew Lunn <andrew@lunn.ch>
-Date: Mon, 17 Apr 2023 17:17:26 +0200
-Subject: [PATCH 4/9] leds: Provide stubs for when CLASS_LED & NEW_LEDS are
- disabled
-
-Provide stubs for devm_led_classdev_register_ext() and
-led_init_default_state_get() so that LED drivers embedded within other
-drivers such as PHYs and Ethernet switches still build when LEDS_CLASS
-or NEW_LEDS are disabled. This also helps with Kconfig dependencies,
-which are somewhat hairy for phylib and mdio and only get worse when
-adding a dependency on LED_CLASS.
-
-Signed-off-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- include/linux/leds.h | 18 ++++++++++++++++++
- 1 file changed, 18 insertions(+)
-
---- a/include/linux/leds.h
-+++ b/include/linux/leds.h
-@@ -63,7 +63,15 @@ struct led_init_data {
- bool devname_mandatory;
- };
-
-+#if IS_ENABLED(CONFIG_NEW_LEDS)
- enum led_default_state led_init_default_state_get(struct fwnode_handle *fwnode);
-+#else
-+static inline enum led_default_state
-+led_init_default_state_get(struct fwnode_handle *fwnode)
-+{
-+ return LEDS_DEFSTATE_OFF;
-+}
-+#endif
-
- struct led_hw_trigger_type {
- int dummy;
-@@ -198,9 +206,19 @@ static inline int led_classdev_register(
- return led_classdev_register_ext(parent, led_cdev, NULL);
- }
-
-+#if IS_ENABLED(CONFIG_LEDS_CLASS)
- int devm_led_classdev_register_ext(struct device *parent,
- struct led_classdev *led_cdev,
- struct led_init_data *init_data);
-+#else
-+static inline int
-+devm_led_classdev_register_ext(struct device *parent,
-+ struct led_classdev *led_cdev,
-+ struct led_init_data *init_data)
-+{
-+ return 0;
-+}
-+#endif
-
- static inline int devm_led_classdev_register(struct device *parent,
- struct led_classdev *led_cdev)
+++ /dev/null
-From 01e5b728e9e43ae444e0369695a5f72209906464 Mon Sep 17 00:00:00 2001
-From: Andrew Lunn <andrew@lunn.ch>
-Date: Mon, 17 Apr 2023 17:17:27 +0200
-Subject: [PATCH 5/9] net: phy: Add a binding for PHY LEDs
-
-Define common binding parsing for all PHY drivers with LEDs using
-phylib. Parse the DT as part of the phy_probe and add LEDs to the
-linux LED class infrastructure. For the moment, provide a dummy
-brightness function, which will later be replaced with a call into the
-PHY driver. This allows testing since the LED core might otherwise
-reject an LED whose brightness cannot be set.
-
-Add a dependency on LED_CLASS. It either needs to be built in, or not
-enabled, since a modular build can result in linker errors.
-
-Signed-off-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/Kconfig | 1 +
- drivers/net/phy/phy_device.c | 76 ++++++++++++++++++++++++++++++++++++
- include/linux/phy.h | 16 ++++++++
- 3 files changed, 93 insertions(+)
-
---- a/drivers/net/phy/Kconfig
-+++ b/drivers/net/phy/Kconfig
-@@ -18,6 +18,7 @@ menuconfig PHYLIB
- depends on NETDEVICES
- select MDIO_DEVICE
- select MDIO_DEVRES
-+ depends on LEDS_CLASS || LEDS_CLASS=n
- help
- Ethernet controllers are usually attached to PHY
- devices. This option provides infrastructure for
---- a/drivers/net/phy/phy_device.c
-+++ b/drivers/net/phy/phy_device.c
-@@ -19,10 +19,12 @@
- #include <linux/interrupt.h>
- #include <linux/io.h>
- #include <linux/kernel.h>
-+#include <linux/list.h>
- #include <linux/mdio.h>
- #include <linux/mii.h>
- #include <linux/mm.h>
- #include <linux/module.h>
-+#include <linux/of.h>
- #include <linux/netdevice.h>
- #include <linux/phy.h>
- #include <linux/phy_led_triggers.h>
-@@ -642,6 +644,7 @@ struct phy_device *phy_device_create(str
- device_initialize(&mdiodev->dev);
-
- dev->state = PHY_DOWN;
-+ INIT_LIST_HEAD(&dev->leds);
-
- mutex_init(&dev->lock);
- INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);
-@@ -3035,6 +3038,74 @@ static bool phy_drv_supports_irq(struct
- return phydrv->config_intr && phydrv->handle_interrupt;
- }
-
-+/* Dummy implementation until calls into PHY driver are added */
-+static int phy_led_set_brightness(struct led_classdev *led_cdev,
-+ enum led_brightness value)
-+{
-+ return 0;
-+}
-+
-+static int of_phy_led(struct phy_device *phydev,
-+ struct device_node *led)
-+{
-+ struct device *dev = &phydev->mdio.dev;
-+ struct led_init_data init_data = {};
-+ struct led_classdev *cdev;
-+ struct phy_led *phyled;
-+ int err;
-+
-+ phyled = devm_kzalloc(dev, sizeof(*phyled), GFP_KERNEL);
-+ if (!phyled)
-+ return -ENOMEM;
-+
-+ cdev = &phyled->led_cdev;
-+
-+ err = of_property_read_u8(led, "reg", &phyled->index);
-+ if (err)
-+ return err;
-+
-+ cdev->brightness_set_blocking = phy_led_set_brightness;
-+ cdev->max_brightness = 1;
-+ init_data.devicename = dev_name(&phydev->mdio.dev);
-+ init_data.fwnode = of_fwnode_handle(led);
-+ init_data.devname_mandatory = true;
-+
-+ err = devm_led_classdev_register_ext(dev, cdev, &init_data);
-+ if (err)
-+ return err;
-+
-+ list_add(&phyled->list, &phydev->leds);
-+
-+ return 0;
-+}
-+
-+static int of_phy_leds(struct phy_device *phydev)
-+{
-+ struct device_node *node = phydev->mdio.dev.of_node;
-+ struct device_node *leds, *led;
-+ int err;
-+
-+ if (!IS_ENABLED(CONFIG_OF_MDIO))
-+ return 0;
-+
-+ if (!node)
-+ return 0;
-+
-+ leds = of_get_child_by_name(node, "leds");
-+ if (!leds)
-+ return 0;
-+
-+ for_each_available_child_of_node(leds, led) {
-+ err = of_phy_led(phydev, led);
-+ if (err) {
-+ of_node_put(led);
-+ return err;
-+ }
-+ }
-+
-+ return 0;
-+}
-+
- /**
- * fwnode_mdio_find_device - Given a fwnode, find the mdio_device
- * @fwnode: pointer to the mdio_device's fwnode
-@@ -3213,6 +3284,11 @@ static int phy_probe(struct device *dev)
- /* Set the state to READY by default */
- phydev->state = PHY_READY;
-
-+ /* Get the LEDs from the device tree, and instantiate standard
-+ * LEDs for them.
-+ */
-+ err = of_phy_leds(phydev);
-+
- out:
- /* Re-assert the reset signal on error */
- if (err)
---- a/include/linux/phy.h
-+++ b/include/linux/phy.h
-@@ -14,6 +14,7 @@
- #include <linux/compiler.h>
- #include <linux/spinlock.h>
- #include <linux/ethtool.h>
-+#include <linux/leds.h>
- #include <linux/linkmode.h>
- #include <linux/netlink.h>
- #include <linux/mdio.h>
-@@ -606,6 +607,7 @@ struct macsec_ops;
- * @phy_num_led_triggers: Number of triggers in @phy_led_triggers
- * @led_link_trigger: LED trigger for link up/down
- * @last_triggered: last LED trigger for link speed
-+ * @leds: list of PHY LED structures
- * @master_slave_set: User requested master/slave configuration
- * @master_slave_get: Current master/slave advertisement
- * @master_slave_state: Current master/slave configuration
-@@ -698,6 +700,7 @@ struct phy_device {
-
- struct phy_led_trigger *led_link_trigger;
- #endif
-+ struct list_head leds;
-
- /*
- * Interrupt number for this PHY
-@@ -772,6 +775,19 @@ struct phy_tdr_config {
- #define PHY_PAIR_ALL -1
-
- /**
-+ * struct phy_led: An LED driven by the PHY
-+ *
-+ * @list: List of LEDs
-+ * @led_cdev: Standard LED class structure
-+ * @index: Number of the LED
-+ */
-+struct phy_led {
-+ struct list_head list;
-+ struct led_classdev led_cdev;
-+ u8 index;
-+};
-+
-+/**
- * struct phy_driver - Driver structure for a particular PHY type
- *
- * @mdiodrv: Data common to all MDIO devices
+++ /dev/null
-From 684818189b04b095b34964ed4a3ea5249a840eab Mon Sep 17 00:00:00 2001
-From: Andrew Lunn <andrew@lunn.ch>
-Date: Mon, 17 Apr 2023 17:17:28 +0200
-Subject: [PATCH 6/9] net: phy: phy_device: Call into the PHY driver to set LED
- brightness
-
-Linux LEDs can be software controlled via the brightness file in /sys.
-LED drivers need to implement a brightness_set function which the core
-will call. Implement an intermediary in phy_device, which will call
-into the phy driver if it implements the necessary function.
-
-Signed-off-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/phy_device.c | 15 ++++++++++++---
- include/linux/phy.h | 13 +++++++++++++
- 2 files changed, 25 insertions(+), 3 deletions(-)
-
---- a/drivers/net/phy/phy_device.c
-+++ b/drivers/net/phy/phy_device.c
-@@ -3038,11 +3038,18 @@ static bool phy_drv_supports_irq(struct
- return phydrv->config_intr && phydrv->handle_interrupt;
- }
-
--/* Dummy implementation until calls into PHY driver are added */
- static int phy_led_set_brightness(struct led_classdev *led_cdev,
- enum led_brightness value)
- {
-- return 0;
-+ struct phy_led *phyled = to_phy_led(led_cdev);
-+ struct phy_device *phydev = phyled->phydev;
-+ int err;
-+
-+ mutex_lock(&phydev->lock);
-+ err = phydev->drv->led_brightness_set(phydev, phyled->index, value);
-+ mutex_unlock(&phydev->lock);
-+
-+ return err;
- }
-
- static int of_phy_led(struct phy_device *phydev,
-@@ -3059,12 +3066,14 @@ static int of_phy_led(struct phy_device
- return -ENOMEM;
-
- cdev = &phyled->led_cdev;
-+ phyled->phydev = phydev;
-
- err = of_property_read_u8(led, "reg", &phyled->index);
- if (err)
- return err;
-
-- cdev->brightness_set_blocking = phy_led_set_brightness;
-+ if (phydev->drv->led_brightness_set)
-+ cdev->brightness_set_blocking = phy_led_set_brightness;
- cdev->max_brightness = 1;
- init_data.devicename = dev_name(&phydev->mdio.dev);
- init_data.fwnode = of_fwnode_handle(led);
---- a/include/linux/phy.h
-+++ b/include/linux/phy.h
-@@ -778,15 +778,19 @@ struct phy_tdr_config {
- * struct phy_led: An LED driven by the PHY
- *
- * @list: List of LEDs
-+ * @phydev: PHY this LED is attached to
- * @led_cdev: Standard LED class structure
- * @index: Number of the LED
- */
- struct phy_led {
- struct list_head list;
-+ struct phy_device *phydev;
- struct led_classdev led_cdev;
- u8 index;
- };
-
-+#define to_phy_led(d) container_of(d, struct phy_led, led_cdev)
-+
- /**
- * struct phy_driver - Driver structure for a particular PHY type
- *
-@@ -1001,6 +1005,15 @@ struct phy_driver {
- int (*get_sqi)(struct phy_device *dev);
- /** @get_sqi_max: Get the maximum signal quality indication */
- int (*get_sqi_max)(struct phy_device *dev);
-+
-+ /**
-+ * @led_brightness_set: Set a PHY LED brightness. Index
-+ * indicates which of the PHYs led should be set. Value
-+ * follows the standard LED class meaning, e.g. LED_OFF,
-+ * LED_HALF, LED_FULL.
-+ */
-+ int (*led_brightness_set)(struct phy_device *dev,
-+ u8 index, enum led_brightness value);
- };
- #define to_phy_driver(d) container_of(to_mdio_common_driver(d), \
- struct phy_driver, mdiodrv)
+++ /dev/null
-From 2d3960e58ef7c83fe1dbf952f056b9e906cb6df8 Mon Sep 17 00:00:00 2001
-From: Andrew Lunn <andrew@lunn.ch>
-Date: Mon, 17 Apr 2023 17:17:29 +0200
-Subject: [PATCH 7/9] net: phy: marvell: Add software control of the LEDs
-
-Add a brightness function, so the LEDs can be controlled from
-software using the standard Linux LED infrastructure.
-
-Signed-off-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/marvell.c | 45 ++++++++++++++++++++++++++++++++++-----
- 1 file changed, 40 insertions(+), 5 deletions(-)
-
---- a/drivers/net/phy/marvell.c
-+++ b/drivers/net/phy/marvell.c
-@@ -144,11 +144,13 @@
- /* WOL Event Interrupt Enable */
- #define MII_88E1318S_PHY_CSIER_WOL_EIE BIT(7)
-
--/* LED Timer Control Register */
--#define MII_88E1318S_PHY_LED_TCR 0x12
--#define MII_88E1318S_PHY_LED_TCR_FORCE_INT BIT(15)
--#define MII_88E1318S_PHY_LED_TCR_INTn_ENABLE BIT(7)
--#define MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW BIT(11)
-+#define MII_88E1318S_PHY_LED_FUNC 0x10
-+#define MII_88E1318S_PHY_LED_FUNC_OFF (0x8)
-+#define MII_88E1318S_PHY_LED_FUNC_ON (0x9)
-+#define MII_88E1318S_PHY_LED_TCR 0x12
-+#define MII_88E1318S_PHY_LED_TCR_FORCE_INT BIT(15)
-+#define MII_88E1318S_PHY_LED_TCR_INTn_ENABLE BIT(7)
-+#define MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW BIT(11)
-
- /* Magic Packet MAC address registers */
- #define MII_88E1318S_PHY_MAGIC_PACKET_WORD2 0x17
-@@ -2832,6 +2834,34 @@ static int marvell_hwmon_probe(struct ph
- }
- #endif
-
-+static int m88e1318_led_brightness_set(struct phy_device *phydev,
-+ u8 index, enum led_brightness value)
-+{
-+ int reg;
-+
-+ reg = phy_read_paged(phydev, MII_MARVELL_LED_PAGE,
-+ MII_88E1318S_PHY_LED_FUNC);
-+ if (reg < 0)
-+ return reg;
-+
-+ switch (index) {
-+ case 0:
-+ case 1:
-+ case 2:
-+ reg &= ~(0xf << (4 * index));
-+ if (value == LED_OFF)
-+ reg |= MII_88E1318S_PHY_LED_FUNC_OFF << (4 * index);
-+ else
-+ reg |= MII_88E1318S_PHY_LED_FUNC_ON << (4 * index);
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ return phy_write_paged(phydev, MII_MARVELL_LED_PAGE,
-+ MII_88E1318S_PHY_LED_FUNC, reg);
-+}
-+
- static int marvell_probe(struct phy_device *phydev)
- {
- struct marvell_priv *priv;
-@@ -3081,6 +3111,7 @@ static struct phy_driver marvell_drivers
- .get_sset_count = marvell_get_sset_count,
- .get_strings = marvell_get_strings,
- .get_stats = marvell_get_stats,
-+ .led_brightness_set = m88e1318_led_brightness_set,
- },
- {
- .phy_id = MARVELL_PHY_ID_88E1145,
-@@ -3187,6 +3218,7 @@ static struct phy_driver marvell_drivers
- .cable_test_start = marvell_vct7_cable_test_start,
- .cable_test_tdr_start = marvell_vct5_cable_test_tdr_start,
- .cable_test_get_status = marvell_vct7_cable_test_get_status,
-+ .led_brightness_set = m88e1318_led_brightness_set,
- },
- {
- .phy_id = MARVELL_PHY_ID_88E1540,
-@@ -3213,6 +3245,7 @@ static struct phy_driver marvell_drivers
- .cable_test_start = marvell_vct7_cable_test_start,
- .cable_test_tdr_start = marvell_vct5_cable_test_tdr_start,
- .cable_test_get_status = marvell_vct7_cable_test_get_status,
-+ .led_brightness_set = m88e1318_led_brightness_set,
- },
- {
- .phy_id = MARVELL_PHY_ID_88E1545,
-@@ -3239,6 +3272,7 @@ static struct phy_driver marvell_drivers
- .cable_test_start = marvell_vct7_cable_test_start,
- .cable_test_tdr_start = marvell_vct5_cable_test_tdr_start,
- .cable_test_get_status = marvell_vct7_cable_test_get_status,
-+ .led_brightness_set = m88e1318_led_brightness_set,
- },
- {
- .phy_id = MARVELL_PHY_ID_88E3016,
-@@ -3380,6 +3414,7 @@ static struct phy_driver marvell_drivers
- .get_stats = marvell_get_stats,
- .get_tunable = m88e1540_get_tunable,
- .set_tunable = m88e1540_set_tunable,
-+ .led_brightness_set = m88e1318_led_brightness_set,
- },
- };
-
+++ /dev/null
-From 4e901018432e38eab35d2a352661ce4727795be1 Mon Sep 17 00:00:00 2001
-From: Andrew Lunn <andrew@lunn.ch>
-Date: Mon, 17 Apr 2023 17:17:30 +0200
-Subject: [PATCH 8/9] net: phy: phy_device: Call into the PHY driver to set LED
- blinking
-
-Linux LEDs can be requested to perform hardware accelerated
-blinking. Pass this to the PHY driver, if it implements the op.
-
-Signed-off-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/phy_device.c | 18 ++++++++++++++++++
- include/linux/phy.h | 12 ++++++++++++
- 2 files changed, 30 insertions(+)
-
---- a/drivers/net/phy/phy_device.c
-+++ b/drivers/net/phy/phy_device.c
-@@ -3052,6 +3052,22 @@ static int phy_led_set_brightness(struct
- return err;
- }
-
-+static int phy_led_blink_set(struct led_classdev *led_cdev,
-+ unsigned long *delay_on,
-+ unsigned long *delay_off)
-+{
-+ struct phy_led *phyled = to_phy_led(led_cdev);
-+ struct phy_device *phydev = phyled->phydev;
-+ int err;
-+
-+ mutex_lock(&phydev->lock);
-+ err = phydev->drv->led_blink_set(phydev, phyled->index,
-+ delay_on, delay_off);
-+ mutex_unlock(&phydev->lock);
-+
-+ return err;
-+}
-+
- static int of_phy_led(struct phy_device *phydev,
- struct device_node *led)
- {
-@@ -3074,6 +3090,8 @@ static int of_phy_led(struct phy_device
-
- if (phydev->drv->led_brightness_set)
- cdev->brightness_set_blocking = phy_led_set_brightness;
-+ if (phydev->drv->led_blink_set)
-+ cdev->blink_set = phy_led_blink_set;
- cdev->max_brightness = 1;
- init_data.devicename = dev_name(&phydev->mdio.dev);
- init_data.fwnode = of_fwnode_handle(led);
---- a/include/linux/phy.h
-+++ b/include/linux/phy.h
-@@ -1014,6 +1014,18 @@ struct phy_driver {
- */
- int (*led_brightness_set)(struct phy_device *dev,
- u8 index, enum led_brightness value);
-+
-+ /**
-+ * @led_blink_set: Set a PHY LED brightness. Index indicates
-+ * which of the PHYs led should be configured to blink. Delays
-+ * are in milliseconds and if both are zero then a sensible
-+ * default should be chosen. The call should adjust the
-+ * timings in that case and if it can't match the values
-+ * specified exactly.
-+ */
-+ int (*led_blink_set)(struct phy_device *dev, u8 index,
-+ unsigned long *delay_on,
-+ unsigned long *delay_off);
- };
- #define to_phy_driver(d) container_of(to_mdio_common_driver(d), \
- struct phy_driver, mdiodrv)
+++ /dev/null
-From ea9e86485decb2ac1750005bd96c166c9b780406 Mon Sep 17 00:00:00 2001
-From: Andrew Lunn <andrew@lunn.ch>
-Date: Mon, 17 Apr 2023 17:17:31 +0200
-Subject: [PATCH 9/9] net: phy: marvell: Implement led_blink_set()
-
-The Marvell PHY can blink the LEDs, simple on/off. All LEDs blink at
-the same rate, and the reset default is 84ms per blink, which is
-around 12Hz.
-
-Signed-off-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/marvell.c | 36 ++++++++++++++++++++++++++++++++++++
- 1 file changed, 36 insertions(+)
-
---- a/drivers/net/phy/marvell.c
-+++ b/drivers/net/phy/marvell.c
-@@ -147,6 +147,8 @@
- #define MII_88E1318S_PHY_LED_FUNC 0x10
- #define MII_88E1318S_PHY_LED_FUNC_OFF (0x8)
- #define MII_88E1318S_PHY_LED_FUNC_ON (0x9)
-+#define MII_88E1318S_PHY_LED_FUNC_HI_Z (0xa)
-+#define MII_88E1318S_PHY_LED_FUNC_BLINK (0xb)
- #define MII_88E1318S_PHY_LED_TCR 0x12
- #define MII_88E1318S_PHY_LED_TCR_FORCE_INT BIT(15)
- #define MII_88E1318S_PHY_LED_TCR_INTn_ENABLE BIT(7)
-@@ -2862,6 +2864,35 @@ static int m88e1318_led_brightness_set(s
- MII_88E1318S_PHY_LED_FUNC, reg);
- }
-
-+static int m88e1318_led_blink_set(struct phy_device *phydev, u8 index,
-+ unsigned long *delay_on,
-+ unsigned long *delay_off)
-+{
-+ int reg;
-+
-+ reg = phy_read_paged(phydev, MII_MARVELL_LED_PAGE,
-+ MII_88E1318S_PHY_LED_FUNC);
-+ if (reg < 0)
-+ return reg;
-+
-+ switch (index) {
-+ case 0:
-+ case 1:
-+ case 2:
-+ reg &= ~(0xf << (4 * index));
-+ reg |= MII_88E1318S_PHY_LED_FUNC_BLINK << (4 * index);
-+ /* Reset default is 84ms */
-+ *delay_on = 84 / 2;
-+ *delay_off = 84 / 2;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ return phy_write_paged(phydev, MII_MARVELL_LED_PAGE,
-+ MII_88E1318S_PHY_LED_FUNC, reg);
-+}
-+
- static int marvell_probe(struct phy_device *phydev)
- {
- struct marvell_priv *priv;
-@@ -3112,6 +3143,7 @@ static struct phy_driver marvell_drivers
- .get_strings = marvell_get_strings,
- .get_stats = marvell_get_stats,
- .led_brightness_set = m88e1318_led_brightness_set,
-+ .led_blink_set = m88e1318_led_blink_set,
- },
- {
- .phy_id = MARVELL_PHY_ID_88E1145,
-@@ -3219,6 +3251,7 @@ static struct phy_driver marvell_drivers
- .cable_test_tdr_start = marvell_vct5_cable_test_tdr_start,
- .cable_test_get_status = marvell_vct7_cable_test_get_status,
- .led_brightness_set = m88e1318_led_brightness_set,
-+ .led_blink_set = m88e1318_led_blink_set,
- },
- {
- .phy_id = MARVELL_PHY_ID_88E1540,
-@@ -3246,6 +3279,7 @@ static struct phy_driver marvell_drivers
- .cable_test_tdr_start = marvell_vct5_cable_test_tdr_start,
- .cable_test_get_status = marvell_vct7_cable_test_get_status,
- .led_brightness_set = m88e1318_led_brightness_set,
-+ .led_blink_set = m88e1318_led_blink_set,
- },
- {
- .phy_id = MARVELL_PHY_ID_88E1545,
-@@ -3273,6 +3307,7 @@ static struct phy_driver marvell_drivers
- .cable_test_tdr_start = marvell_vct5_cable_test_tdr_start,
- .cable_test_get_status = marvell_vct7_cable_test_get_status,
- .led_brightness_set = m88e1318_led_brightness_set,
-+ .led_blink_set = m88e1318_led_blink_set,
- },
- {
- .phy_id = MARVELL_PHY_ID_88E3016,
-@@ -3415,6 +3450,7 @@ static struct phy_driver marvell_drivers
- .get_tunable = m88e1540_get_tunable,
- .set_tunable = m88e1540_set_tunable,
- .led_brightness_set = m88e1318_led_brightness_set,
-+ .led_blink_set = m88e1318_led_blink_set,
- },
- };
-
+++ /dev/null
-From 4774ad841bef97cc51df90195338c5b2573dd4cb Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Sun, 23 Apr 2023 19:28:00 +0200
-Subject: [PATCH] net: phy: marvell: Fix inconsistent indenting in
- led_blink_set
-
-Fix inconsistent indeinting in m88e1318_led_blink_set reported by kernel
-test robot, probably done by the presence of an if condition dropped in
-later revision of the same code.
-
-Reported-by: kernel test robot <lkp@intel.com>
-Link: https://lore.kernel.org/oe-kbuild-all/202304240007.0VEX8QYG-lkp@intel.com/
-Fixes: ea9e86485dec ("net: phy: marvell: Implement led_blink_set()")
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/20230423172800.3470-1-ansuelsmth@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/marvell.c | 8 ++++----
- 1 file changed, 4 insertions(+), 4 deletions(-)
-
---- a/drivers/net/phy/marvell.c
-+++ b/drivers/net/phy/marvell.c
-@@ -2880,10 +2880,10 @@ static int m88e1318_led_blink_set(struct
- case 1:
- case 2:
- reg &= ~(0xf << (4 * index));
-- reg |= MII_88E1318S_PHY_LED_FUNC_BLINK << (4 * index);
-- /* Reset default is 84ms */
-- *delay_on = 84 / 2;
-- *delay_off = 84 / 2;
-+ reg |= MII_88E1318S_PHY_LED_FUNC_BLINK << (4 * index);
-+ /* Reset default is 84ms */
-+ *delay_on = 84 / 2;
-+ *delay_off = 84 / 2;
- break;
- default:
- return -EINVAL;
+++ /dev/null
-From e2f24cb1b5daf9a4f6f3ba574c1fa74aab9807a4 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Wed, 19 Apr 2023 23:07:40 +0200
-Subject: [PATCH 2/5] leds: trigger: netdev: Drop NETDEV_LED_MODE_LINKUP from
- mode
-
-Putting NETDEV_LED_MODE_LINKUP in the same list of the netdev trigger
-modes is wrong as it's used to set the link state of the device and not
-to set a blink mode as it's done by NETDEV_LED_LINK, NETDEV_LED_TX and
-NETDEV_LED_RX. It's also wrong to put this state in the same bitmap of the
-netdev trigger mode and should be external to it.
-
-Drop NETDEV_LED_MODE_LINKUP from mode list and convert to a simple bool
-that will be true or false based on the carrier link. No functional
-change intended.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: Lee Jones <lee@kernel.org>
-Link: https://lore.kernel.org/r/20230419210743.3594-3-ansuelsmth@gmail.com
----
- drivers/leds/trigger/ledtrig-netdev.c | 19 ++++++++-----------
- 1 file changed, 8 insertions(+), 11 deletions(-)
-
---- a/drivers/leds/trigger/ledtrig-netdev.c
-+++ b/drivers/leds/trigger/ledtrig-netdev.c
-@@ -50,10 +50,10 @@ struct led_netdev_data {
- unsigned int last_activity;
-
- unsigned long mode;
-+ bool carrier_link_up;
- #define NETDEV_LED_LINK 0
- #define NETDEV_LED_TX 1
- #define NETDEV_LED_RX 2
--#define NETDEV_LED_MODE_LINKUP 3
- };
-
- enum netdev_led_attr {
-@@ -73,9 +73,9 @@ static void set_baseline_state(struct le
- if (!led_cdev->blink_brightness)
- led_cdev->blink_brightness = led_cdev->max_brightness;
-
-- if (!test_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode))
-+ if (!trigger_data->carrier_link_up) {
- led_set_brightness(led_cdev, LED_OFF);
-- else {
-+ } else {
- if (test_bit(NETDEV_LED_LINK, &trigger_data->mode))
- led_set_brightness(led_cdev,
- led_cdev->blink_brightness);
-@@ -131,10 +131,9 @@ static ssize_t device_name_store(struct
- trigger_data->net_dev =
- dev_get_by_name(&init_net, trigger_data->device_name);
-
-- clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
-+ trigger_data->carrier_link_up = false;
- if (trigger_data->net_dev != NULL)
-- if (netif_carrier_ok(trigger_data->net_dev))
-- set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
-+ trigger_data->carrier_link_up = netif_carrier_ok(trigger_data->net_dev);
-
- trigger_data->last_activity = 0;
-
-@@ -315,11 +314,10 @@ static int netdev_trig_notify(struct not
-
- spin_lock_bh(&trigger_data->lock);
-
-- clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
-+ trigger_data->carrier_link_up = false;
- switch (evt) {
- case NETDEV_CHANGENAME:
-- if (netif_carrier_ok(dev))
-- set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
-+ trigger_data->carrier_link_up = netif_carrier_ok(dev);
- fallthrough;
- case NETDEV_REGISTER:
- if (trigger_data->net_dev)
-@@ -333,8 +331,7 @@ static int netdev_trig_notify(struct not
- break;
- case NETDEV_UP:
- case NETDEV_CHANGE:
-- if (netif_carrier_ok(dev))
-- set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
-+ trigger_data->carrier_link_up = netif_carrier_ok(dev);
- break;
- }
-
+++ /dev/null
-From bdec9cb83936e0ac4cb87fed5b49fad0175f7dec Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Wed, 19 Apr 2023 23:07:41 +0200
-Subject: [PATCH 3/5] leds: trigger: netdev: Rename add namespace to netdev
- trigger enum modes
-
-Rename NETDEV trigger enum modes to a more symbolic name and add a
-namespace to them.
-
-Also add __TRIGGER_NETDEV_MAX to identify the max modes of the netdev
-trigger.
-
-This is a cleanup to drop the define and no behaviour change are
-intended.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: Lee Jones <lee@kernel.org>
-Link: https://lore.kernel.org/r/20230419210743.3594-4-ansuelsmth@gmail.com
----
- drivers/leds/trigger/ledtrig-netdev.c | 58 ++++++++++++---------------
- 1 file changed, 25 insertions(+), 33 deletions(-)
-
---- a/drivers/leds/trigger/ledtrig-netdev.c
-+++ b/drivers/leds/trigger/ledtrig-netdev.c
-@@ -51,15 +51,15 @@ struct led_netdev_data {
-
- unsigned long mode;
- bool carrier_link_up;
--#define NETDEV_LED_LINK 0
--#define NETDEV_LED_TX 1
--#define NETDEV_LED_RX 2
- };
-
--enum netdev_led_attr {
-- NETDEV_ATTR_LINK,
-- NETDEV_ATTR_TX,
-- NETDEV_ATTR_RX
-+enum led_trigger_netdev_modes {
-+ TRIGGER_NETDEV_LINK = 0,
-+ TRIGGER_NETDEV_TX,
-+ TRIGGER_NETDEV_RX,
-+
-+ /* Keep last */
-+ __TRIGGER_NETDEV_MAX,
- };
-
- static void set_baseline_state(struct led_netdev_data *trigger_data)
-@@ -76,7 +76,7 @@ static void set_baseline_state(struct le
- if (!trigger_data->carrier_link_up) {
- led_set_brightness(led_cdev, LED_OFF);
- } else {
-- if (test_bit(NETDEV_LED_LINK, &trigger_data->mode))
-+ if (test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode))
- led_set_brightness(led_cdev,
- led_cdev->blink_brightness);
- else
-@@ -85,8 +85,8 @@ static void set_baseline_state(struct le
- /* If we are looking for RX/TX start periodically
- * checking stats
- */
-- if (test_bit(NETDEV_LED_TX, &trigger_data->mode) ||
-- test_bit(NETDEV_LED_RX, &trigger_data->mode))
-+ if (test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) ||
-+ test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode))
- schedule_delayed_work(&trigger_data->work, 0);
- }
- }
-@@ -146,20 +146,16 @@ static ssize_t device_name_store(struct
- static DEVICE_ATTR_RW(device_name);
-
- static ssize_t netdev_led_attr_show(struct device *dev, char *buf,
-- enum netdev_led_attr attr)
-+ enum led_trigger_netdev_modes attr)
- {
- struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
- int bit;
-
- switch (attr) {
-- case NETDEV_ATTR_LINK:
-- bit = NETDEV_LED_LINK;
-- break;
-- case NETDEV_ATTR_TX:
-- bit = NETDEV_LED_TX;
-- break;
-- case NETDEV_ATTR_RX:
-- bit = NETDEV_LED_RX;
-+ case TRIGGER_NETDEV_LINK:
-+ case TRIGGER_NETDEV_TX:
-+ case TRIGGER_NETDEV_RX:
-+ bit = attr;
- break;
- default:
- return -EINVAL;
-@@ -169,7 +165,7 @@ static ssize_t netdev_led_attr_show(stru
- }
-
- static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
-- size_t size, enum netdev_led_attr attr)
-+ size_t size, enum led_trigger_netdev_modes attr)
- {
- struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
- unsigned long state;
-@@ -181,14 +177,10 @@ static ssize_t netdev_led_attr_store(str
- return ret;
-
- switch (attr) {
-- case NETDEV_ATTR_LINK:
-- bit = NETDEV_LED_LINK;
-- break;
-- case NETDEV_ATTR_TX:
-- bit = NETDEV_LED_TX;
-- break;
-- case NETDEV_ATTR_RX:
-- bit = NETDEV_LED_RX;
-+ case TRIGGER_NETDEV_LINK:
-+ case TRIGGER_NETDEV_TX:
-+ case TRIGGER_NETDEV_RX:
-+ bit = attr;
- break;
- default:
- return -EINVAL;
-@@ -360,21 +352,21 @@ static void netdev_trig_work(struct work
- }
-
- /* If we are not looking for RX/TX then return */
-- if (!test_bit(NETDEV_LED_TX, &trigger_data->mode) &&
-- !test_bit(NETDEV_LED_RX, &trigger_data->mode))
-+ if (!test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) &&
-+ !test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode))
- return;
-
- dev_stats = dev_get_stats(trigger_data->net_dev, &temp);
- new_activity =
-- (test_bit(NETDEV_LED_TX, &trigger_data->mode) ?
-+ (test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) ?
- dev_stats->tx_packets : 0) +
-- (test_bit(NETDEV_LED_RX, &trigger_data->mode) ?
-+ (test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode) ?
- dev_stats->rx_packets : 0);
-
- if (trigger_data->last_activity != new_activity) {
- led_stop_software_blink(trigger_data->led_cdev);
-
-- invert = test_bit(NETDEV_LED_LINK, &trigger_data->mode);
-+ invert = test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode);
- interval = jiffies_to_msecs(
- atomic_read(&trigger_data->interval));
- /* base state is ON (link present) */
+++ /dev/null
-From 164b67d53476a9d114be85c885bd31f783835be4 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Wed, 19 Apr 2023 23:07:42 +0200
-Subject: [PATCH 4/5] leds: trigger: netdev: Convert device attr to macro
-
-Convert link tx and rx device attr to a common macro to reduce common
-code and in preparation for additional attr.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: Lee Jones <lee@kernel.org>
-Link: https://lore.kernel.org/r/20230419210743.3594-5-ansuelsmth@gmail.com
----
- drivers/leds/trigger/ledtrig-netdev.c | 57 ++++++++-------------------
- 1 file changed, 16 insertions(+), 41 deletions(-)
-
---- a/drivers/leds/trigger/ledtrig-netdev.c
-+++ b/drivers/leds/trigger/ledtrig-netdev.c
-@@ -198,47 +198,22 @@ static ssize_t netdev_led_attr_store(str
- return size;
- }
-
--static ssize_t link_show(struct device *dev,
-- struct device_attribute *attr, char *buf)
--{
-- return netdev_led_attr_show(dev, buf, NETDEV_ATTR_LINK);
--}
--
--static ssize_t link_store(struct device *dev,
-- struct device_attribute *attr, const char *buf, size_t size)
--{
-- return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_LINK);
--}
--
--static DEVICE_ATTR_RW(link);
--
--static ssize_t tx_show(struct device *dev,
-- struct device_attribute *attr, char *buf)
--{
-- return netdev_led_attr_show(dev, buf, NETDEV_ATTR_TX);
--}
--
--static ssize_t tx_store(struct device *dev,
-- struct device_attribute *attr, const char *buf, size_t size)
--{
-- return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_TX);
--}
--
--static DEVICE_ATTR_RW(tx);
--
--static ssize_t rx_show(struct device *dev,
-- struct device_attribute *attr, char *buf)
--{
-- return netdev_led_attr_show(dev, buf, NETDEV_ATTR_RX);
--}
--
--static ssize_t rx_store(struct device *dev,
-- struct device_attribute *attr, const char *buf, size_t size)
--{
-- return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_RX);
--}
--
--static DEVICE_ATTR_RW(rx);
-+#define DEFINE_NETDEV_TRIGGER(trigger_name, trigger) \
-+ static ssize_t trigger_name##_show(struct device *dev, \
-+ struct device_attribute *attr, char *buf) \
-+ { \
-+ return netdev_led_attr_show(dev, buf, trigger); \
-+ } \
-+ static ssize_t trigger_name##_store(struct device *dev, \
-+ struct device_attribute *attr, const char *buf, size_t size) \
-+ { \
-+ return netdev_led_attr_store(dev, buf, size, trigger); \
-+ } \
-+ static DEVICE_ATTR_RW(trigger_name)
-+
-+DEFINE_NETDEV_TRIGGER(link, TRIGGER_NETDEV_LINK);
-+DEFINE_NETDEV_TRIGGER(tx, TRIGGER_NETDEV_TX);
-+DEFINE_NETDEV_TRIGGER(rx, TRIGGER_NETDEV_RX);
-
- static ssize_t interval_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+++ /dev/null
-From d1b9e1391ab2dc80e9db87fe8b2de015c651e4c9 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Wed, 19 Apr 2023 23:07:43 +0200
-Subject: [PATCH 5/5] leds: trigger: netdev: Use mutex instead of spinlocks
-
-Some LEDs may require to sleep while doing some operation like setting
-brightness and other cleanup.
-
-For this reason, using a spinlock will cause a sleep under spinlock
-warning.
-
-It should be safe to convert this to a sleepable lock since:
-- sysfs read/write can sleep
-- netdev_trig_work is a work queue and can sleep
-- netdev _trig_notify can sleep
-
-The spinlock was used when brightness didn't support sleeping, but this
-changed and now it supported with brightness_set_blocking().
-
-Convert to mutex lock to permit sleeping using brightness_set_blocking().
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: Lee Jones <lee@kernel.org>
-Link: https://lore.kernel.org/r/20230419210743.3594-6-ansuelsmth@gmail.com
----
- drivers/leds/trigger/ledtrig-netdev.c | 18 +++++++++---------
- 1 file changed, 9 insertions(+), 9 deletions(-)
-
---- a/drivers/leds/trigger/ledtrig-netdev.c
-+++ b/drivers/leds/trigger/ledtrig-netdev.c
-@@ -20,7 +20,7 @@
- #include <linux/list.h>
- #include <linux/module.h>
- #include <linux/netdevice.h>
--#include <linux/spinlock.h>
-+#include <linux/mutex.h>
- #include <linux/timer.h>
- #include "../leds.h"
-
-@@ -37,7 +37,7 @@
- */
-
- struct led_netdev_data {
-- spinlock_t lock;
-+ struct mutex lock;
-
- struct delayed_work work;
- struct notifier_block notifier;
-@@ -97,9 +97,9 @@ static ssize_t device_name_show(struct d
- struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
- ssize_t len;
-
-- spin_lock_bh(&trigger_data->lock);
-+ mutex_lock(&trigger_data->lock);
- len = sprintf(buf, "%s\n", trigger_data->device_name);
-- spin_unlock_bh(&trigger_data->lock);
-+ mutex_unlock(&trigger_data->lock);
-
- return len;
- }
-@@ -115,7 +115,7 @@ static ssize_t device_name_store(struct
-
- cancel_delayed_work_sync(&trigger_data->work);
-
-- spin_lock_bh(&trigger_data->lock);
-+ mutex_lock(&trigger_data->lock);
-
- if (trigger_data->net_dev) {
- dev_put(trigger_data->net_dev);
-@@ -138,7 +138,7 @@ static ssize_t device_name_store(struct
- trigger_data->last_activity = 0;
-
- set_baseline_state(trigger_data);
-- spin_unlock_bh(&trigger_data->lock);
-+ mutex_unlock(&trigger_data->lock);
-
- return size;
- }
-@@ -279,7 +279,7 @@ static int netdev_trig_notify(struct not
-
- cancel_delayed_work_sync(&trigger_data->work);
-
-- spin_lock_bh(&trigger_data->lock);
-+ mutex_lock(&trigger_data->lock);
-
- trigger_data->carrier_link_up = false;
- switch (evt) {
-@@ -304,7 +304,7 @@ static int netdev_trig_notify(struct not
-
- set_baseline_state(trigger_data);
-
-- spin_unlock_bh(&trigger_data->lock);
-+ mutex_unlock(&trigger_data->lock);
-
- return NOTIFY_DONE;
- }
-@@ -365,7 +365,7 @@ static int netdev_trig_activate(struct l
- if (!trigger_data)
- return -ENOMEM;
-
-- spin_lock_init(&trigger_data->lock);
-+ mutex_init(&trigger_data->lock);
-
- trigger_data->notifier.notifier_call = netdev_trig_notify;
- trigger_data->notifier.priority = 10;
+++ /dev/null
-From ed554d3f945179c5b159bddfad7be34b403fe11a Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Mon, 29 May 2023 18:32:31 +0200
-Subject: [PATCH 01/13] leds: add APIs for LEDs hw control
-
-Add an option to permit LED driver to declare support for a specific
-trigger to use hw control and setup the LED to blink based on specific
-provided modes.
-
-Add APIs for LEDs hw control. These functions will be used to activate
-hardware control where a LED will use the provided flags, from an
-unique defined supported trigger, to setup the LED to be driven by
-hardware.
-
-Add hw_control_is_supported() to ask the LED driver if the requested
-mode by the trigger are supported and the LED can be setup to follow
-the requested modes.
-
-Deactivate hardware blink control by setting brightness to LED_OFF via
-the brightness_set() callback.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- include/linux/leds.h | 37 +++++++++++++++++++++++++++++++++++++
- 1 file changed, 37 insertions(+)
-
---- a/include/linux/leds.h
-+++ b/include/linux/leds.h
-@@ -164,6 +164,43 @@ struct led_classdev {
-
- /* LEDs that have private triggers have this set */
- struct led_hw_trigger_type *trigger_type;
-+
-+ /* Unique trigger name supported by LED set in hw control mode */
-+ const char *hw_control_trigger;
-+ /*
-+ * Check if the LED driver supports the requested mode provided by the
-+ * defined supported trigger to setup the LED to hw control mode.
-+ *
-+ * Return 0 on success. Return -EOPNOTSUPP when the passed flags are not
-+ * supported and software fallback needs to be used.
-+ * Return a negative error number on any other case for check fail due
-+ * to various reason like device not ready or timeouts.
-+ */
-+ int (*hw_control_is_supported)(struct led_classdev *led_cdev,
-+ unsigned long flags);
-+ /*
-+ * Activate hardware control, LED driver will use the provided flags
-+ * from the supported trigger and setup the LED to be driven by hardware
-+ * following the requested mode from the trigger flags.
-+ * Deactivate hardware blink control by setting brightness to LED_OFF via
-+ * the brightness_set() callback.
-+ *
-+ * Return 0 on success, a negative error number on flags apply fail.
-+ */
-+ int (*hw_control_set)(struct led_classdev *led_cdev,
-+ unsigned long flags);
-+ /*
-+ * Get from the LED driver the current mode that the LED is set in hw
-+ * control mode and put them in flags.
-+ * Trigger can use this to get the initial state of a LED already set in
-+ * hardware blink control.
-+ *
-+ * Return 0 on success, a negative error number on failing parsing the
-+ * initial mode. Error from this function is NOT FATAL as the device
-+ * may be in a not supported initial state by the attached LED trigger.
-+ */
-+ int (*hw_control_get)(struct led_classdev *led_cdev,
-+ unsigned long *flags);
- #endif
-
- #ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
+++ /dev/null
-From 052c38eb17e866c5b4cd43924e7a5e20167b55c0 Mon Sep 17 00:00:00 2001
-From: Andrew Lunn <andrew@lunn.ch>
-Date: Mon, 29 May 2023 18:32:32 +0200
-Subject: [PATCH 02/13] leds: add API to get attached device for LED hw control
-
-Some specific LED triggers blink the LED based on events from a device
-or subsystem.
-For example, an LED could be blinked to indicate a network device is
-receiving packets, or a disk is reading blocks. To correctly enable and
-request the hw control of the LED, the trigger has to check if the
-network interface or block device configured via a /sys/class/led file
-match the one the LED driver provide for hw control for.
-
-Provide an API call to get the device which the LED blinks for.
-
-Signed-off-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- include/linux/leds.h | 6 ++++++
- 1 file changed, 6 insertions(+)
-
---- a/include/linux/leds.h
-+++ b/include/linux/leds.h
-@@ -201,6 +201,12 @@ struct led_classdev {
- */
- int (*hw_control_get)(struct led_classdev *led_cdev,
- unsigned long *flags);
-+ /*
-+ * Get the device this LED blinks in response to.
-+ * e.g. for a PHY LED, it is the network device. If the LED is
-+ * not yet associated to a device, return NULL.
-+ */
-+ struct device *(*hw_control_get_device)(struct led_classdev *led_cdev);
- #endif
-
- #ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
+++ /dev/null
-From 8aa2fd7b66980ecd2e45e95af61cf7eafede1211 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Mon, 29 May 2023 18:32:33 +0200
-Subject: [PATCH 03/13] Documentation: leds: leds-class: Document new Hardware
- driven LEDs APIs
-
-Document new Hardware driven LEDs APIs.
-
-Some LEDs can be programmed to be driven by hardware. This is not
-limited to blink but also to turn off or on autonomously.
-To support this feature, a LED needs to implement various additional
-ops and needs to declare specific support for the supported triggers.
-
-Add documentation for each required value and API to make hw control
-possible and implementable by both LEDs and triggers.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- Documentation/leds/leds-class.rst | 81 +++++++++++++++++++++++++++++++
- 1 file changed, 81 insertions(+)
-
---- a/Documentation/leds/leds-class.rst
-+++ b/Documentation/leds/leds-class.rst
-@@ -169,6 +169,87 @@ Setting the brightness to zero with brig
- should completely turn off the LED and cancel the previously programmed
- hardware blinking function, if any.
-
-+Hardware driven LEDs
-+====================
-+
-+Some LEDs can be programmed to be driven by hardware. This is not
-+limited to blink but also to turn off or on autonomously.
-+To support this feature, a LED needs to implement various additional
-+ops and needs to declare specific support for the supported triggers.
-+
-+With hw control we refer to the LED driven by hardware.
-+
-+LED driver must define the following value to support hw control:
-+
-+ - hw_control_trigger:
-+ unique trigger name supported by the LED in hw control
-+ mode.
-+
-+LED driver must implement the following API to support hw control:
-+ - hw_control_is_supported:
-+ check if the flags passed by the supported trigger can
-+ be parsed and activate hw control on the LED.
-+
-+ Return 0 if the passed flags mask is supported and
-+ can be set with hw_control_set().
-+
-+ If the passed flags mask is not supported -EOPNOTSUPP
-+ must be returned, the LED trigger will use software
-+ fallback in this case.
-+
-+ Return a negative error in case of any other error like
-+ device not ready or timeouts.
-+
-+ - hw_control_set:
-+ activate hw control. LED driver will use the provided
-+ flags passed from the supported trigger, parse them to
-+ a set of mode and setup the LED to be driven by hardware
-+ following the requested modes.
-+
-+ Set LED_OFF via the brightness_set to deactivate hw control.
-+
-+ Return 0 on success, a negative error number on failing to
-+ apply flags.
-+
-+ - hw_control_get:
-+ get active modes from a LED already in hw control, parse
-+ them and set in flags the current active flags for the
-+ supported trigger.
-+
-+ Return 0 on success, a negative error number on failing
-+ parsing the initial mode.
-+ Error from this function is NOT FATAL as the device may
-+ be in a not supported initial state by the attached LED
-+ trigger.
-+
-+ - hw_control_get_device:
-+ return the device associated with the LED driver in
-+ hw control. A trigger might use this to match the
-+ returned device from this function with a configured
-+ device for the trigger as the source for blinking
-+ events and correctly enable hw control.
-+ (example a netdev trigger configured to blink for a
-+ particular dev match the returned dev from get_device
-+ to set hw control)
-+
-+ Returns a pointer to a struct device or NULL if nothing
-+ is currently attached.
-+
-+LED driver can activate additional modes by default to workaround the
-+impossibility of supporting each different mode on the supported trigger.
-+Examples are hardcoding the blink speed to a set interval, enable special
-+feature like bypassing blink if some requirements are not met.
-+
-+A trigger should first check if the hw control API are supported by the LED
-+driver and check if the trigger is supported to verify if hw control is possible,
-+use hw_control_is_supported to check if the flags are supported and only at
-+the end use hw_control_set to activate hw control.
-+
-+A trigger can use hw_control_get to check if a LED is already in hw control
-+and init their flags.
-+
-+When the LED is in hw control, no software blink is possible and doing so
-+will effectively disable hw control.
-
- Known Issues
- ============
+++ /dev/null
-From 28a6a2ef18ad840a390d519840c303b03040961c Mon Sep 17 00:00:00 2001
-From: Andrew Lunn <andrew@lunn.ch>
-Date: Mon, 29 May 2023 18:32:34 +0200
-Subject: [PATCH 04/13] leds: trigger: netdev: refactor code setting device
- name
-
-Move the code into a helper, ready for it to be called at
-other times. No intended behaviour change.
-
-Signed-off-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/leds/trigger/ledtrig-netdev.c | 29 ++++++++++++++++++---------
- 1 file changed, 20 insertions(+), 9 deletions(-)
-
---- a/drivers/leds/trigger/ledtrig-netdev.c
-+++ b/drivers/leds/trigger/ledtrig-netdev.c
-@@ -104,15 +104,9 @@ static ssize_t device_name_show(struct d
- return len;
- }
-
--static ssize_t device_name_store(struct device *dev,
-- struct device_attribute *attr, const char *buf,
-- size_t size)
-+static int set_device_name(struct led_netdev_data *trigger_data,
-+ const char *name, size_t size)
- {
-- struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
--
-- if (size >= IFNAMSIZ)
-- return -EINVAL;
--
- cancel_delayed_work_sync(&trigger_data->work);
-
- mutex_lock(&trigger_data->lock);
-@@ -122,7 +116,7 @@ static ssize_t device_name_store(struct
- trigger_data->net_dev = NULL;
- }
-
-- memcpy(trigger_data->device_name, buf, size);
-+ memcpy(trigger_data->device_name, name, size);
- trigger_data->device_name[size] = 0;
- if (size > 0 && trigger_data->device_name[size - 1] == '\n')
- trigger_data->device_name[size - 1] = 0;
-@@ -140,6 +134,23 @@ static ssize_t device_name_store(struct
- set_baseline_state(trigger_data);
- mutex_unlock(&trigger_data->lock);
-
-+ return 0;
-+}
-+
-+static ssize_t device_name_store(struct device *dev,
-+ struct device_attribute *attr, const char *buf,
-+ size_t size)
-+{
-+ struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
-+ int ret;
-+
-+ if (size >= IFNAMSIZ)
-+ return -EINVAL;
-+
-+ ret = set_device_name(trigger_data, buf, size);
-+
-+ if (ret < 0)
-+ return ret;
- return size;
- }
-
+++ /dev/null
-From 4fd1b6d47a7a38e81fdc6f8be2ccd4216b3f93db Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Mon, 29 May 2023 18:32:35 +0200
-Subject: [PATCH 05/13] leds: trigger: netdev: introduce check for possible hw
- control
-
-Introduce function to check if the requested mode can use hw control in
-preparation for hw control support. Currently everything is handled in
-software so can_hw_control will always return false.
-
-Add knob with the new value hw_control in trigger_data struct to
-set hw control possible. Useful for future implementation to implement
-in set_baseline_state() the required function to set the requested mode
-using LEDs hw control ops and in other function to reject set if hw
-control is currently active.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/leds/trigger/ledtrig-netdev.c | 8 ++++++++
- 1 file changed, 8 insertions(+)
-
---- a/drivers/leds/trigger/ledtrig-netdev.c
-+++ b/drivers/leds/trigger/ledtrig-netdev.c
-@@ -51,6 +51,7 @@ struct led_netdev_data {
-
- unsigned long mode;
- bool carrier_link_up;
-+ bool hw_control;
- };
-
- enum led_trigger_netdev_modes {
-@@ -91,6 +92,11 @@ static void set_baseline_state(struct le
- }
- }
-
-+static bool can_hw_control(struct led_netdev_data *trigger_data)
-+{
-+ return false;
-+}
-+
- static ssize_t device_name_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
-@@ -204,6 +210,8 @@ static ssize_t netdev_led_attr_store(str
- else
- clear_bit(bit, &trigger_data->mode);
-
-+ trigger_data->hw_control = can_hw_control(trigger_data);
-+
- set_baseline_state(trigger_data);
-
- return size;
+++ /dev/null
-From 6352f25f9fadba59d5df2ba7139495759ccc81d5 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Mon, 29 May 2023 18:32:36 +0200
-Subject: [PATCH 06/13] leds: trigger: netdev: add basic check for hw control
- support
-
-Add basic check for hw control support. Check if the required API are
-defined and check if the defined trigger supported in hw control for the
-LED driver match netdev.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/leds/trigger/ledtrig-netdev.c | 14 ++++++++++++++
- 1 file changed, 14 insertions(+)
-
---- a/drivers/leds/trigger/ledtrig-netdev.c
-+++ b/drivers/leds/trigger/ledtrig-netdev.c
-@@ -92,8 +92,22 @@ static void set_baseline_state(struct le
- }
- }
-
-+static bool supports_hw_control(struct led_classdev *led_cdev)
-+{
-+ if (!led_cdev->hw_control_get || !led_cdev->hw_control_set ||
-+ !led_cdev->hw_control_is_supported)
-+ return false;
-+
-+ return !strcmp(led_cdev->hw_control_trigger, led_cdev->trigger->name);
-+}
-+
- static bool can_hw_control(struct led_netdev_data *trigger_data)
- {
-+ struct led_classdev *led_cdev = trigger_data->led_cdev;
-+
-+ if (!supports_hw_control(led_cdev))
-+ return false;
-+
- return false;
- }
-
+++ /dev/null
-From c84c80c7388f887b10dafd70fc55bc6c5fe9fa5a Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Mon, 29 May 2023 18:32:37 +0200
-Subject: [PATCH 07/13] leds: trigger: netdev: reject interval store for
- hw_control
-
-Reject interval store with hw_control enabled. It's are currently not
-supported and MUST be set to the default value with hw control enabled.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/leds/trigger/ledtrig-netdev.c | 3 +++
- 1 file changed, 3 insertions(+)
-
---- a/drivers/leds/trigger/ledtrig-netdev.c
-+++ b/drivers/leds/trigger/ledtrig-netdev.c
-@@ -265,6 +265,9 @@ static ssize_t interval_store(struct dev
- unsigned long value;
- int ret;
-
-+ if (trigger_data->hw_control)
-+ return -EINVAL;
-+
- ret = kstrtoul(buf, 0, &value);
- if (ret)
- return ret;
+++ /dev/null
-From 7c145a34ba6e380616af93262fcab9fc7261d851 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Mon, 29 May 2023 18:32:38 +0200
-Subject: [PATCH 08/13] leds: trigger: netdev: add support for LED hw control
-
-Add support for LED hw control for the netdev trigger.
-
-The trigger on calling set_baseline_state to configure a new mode, will
-do various check to verify if hw control can be used for the requested
-mode in can_hw_control() function.
-
-It will first check if the LED driver supports hw control for the netdev
-trigger, then will use hw_control_is_supported() and finally will call
-hw_control_set() to apply the requested mode.
-
-To use such mode, interval MUST be set to the default value and net_dev
-MUST be set. If one of these 2 value are not valid, hw control will
-never be used and normal software fallback is used.
-
-The default interval value is moved to a define to make sure they are
-always synced.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/leds/trigger/ledtrig-netdev.c | 43 +++++++++++++++++++++++++--
- 1 file changed, 41 insertions(+), 2 deletions(-)
-
---- a/drivers/leds/trigger/ledtrig-netdev.c
-+++ b/drivers/leds/trigger/ledtrig-netdev.c
-@@ -24,6 +24,8 @@
- #include <linux/timer.h>
- #include "../leds.h"
-
-+#define NETDEV_LED_DEFAULT_INTERVAL 50
-+
- /*
- * Configurable sysfs attributes:
- *
-@@ -68,6 +70,13 @@ static void set_baseline_state(struct le
- int current_brightness;
- struct led_classdev *led_cdev = trigger_data->led_cdev;
-
-+ /* Already validated, hw control is possible with the requested mode */
-+ if (trigger_data->hw_control) {
-+ led_cdev->hw_control_set(led_cdev, trigger_data->mode);
-+
-+ return;
-+ }
-+
- current_brightness = led_cdev->brightness;
- if (current_brightness)
- led_cdev->blink_brightness = current_brightness;
-@@ -103,12 +112,42 @@ static bool supports_hw_control(struct l
-
- static bool can_hw_control(struct led_netdev_data *trigger_data)
- {
-+ unsigned long default_interval = msecs_to_jiffies(NETDEV_LED_DEFAULT_INTERVAL);
-+ unsigned int interval = atomic_read(&trigger_data->interval);
- struct led_classdev *led_cdev = trigger_data->led_cdev;
-+ int ret;
-
- if (!supports_hw_control(led_cdev))
- return false;
-
-- return false;
-+ /*
-+ * Interval must be set to the default
-+ * value. Any different value is rejected if in hw
-+ * control.
-+ */
-+ if (interval != default_interval)
-+ return false;
-+
-+ /*
-+ * net_dev must be set with hw control, otherwise no
-+ * blinking can be happening and there is nothing to
-+ * offloaded.
-+ */
-+ if (!trigger_data->net_dev)
-+ return false;
-+
-+ /* Check if the requested mode is supported */
-+ ret = led_cdev->hw_control_is_supported(led_cdev, trigger_data->mode);
-+ /* Fall back to software blinking if not supported */
-+ if (ret == -EOPNOTSUPP)
-+ return false;
-+ if (ret) {
-+ dev_warn(led_cdev->dev,
-+ "Current mode check failed with error %d\n", ret);
-+ return false;
-+ }
-+
-+ return true;
- }
-
- static ssize_t device_name_show(struct device *dev,
-@@ -413,7 +452,7 @@ static int netdev_trig_activate(struct l
- trigger_data->device_name[0] = 0;
-
- trigger_data->mode = 0;
-- atomic_set(&trigger_data->interval, msecs_to_jiffies(50));
-+ atomic_set(&trigger_data->interval, msecs_to_jiffies(NETDEV_LED_DEFAULT_INTERVAL));
- trigger_data->last_activity = 0;
-
- led_set_trigger_data(led_cdev, trigger_data);
+++ /dev/null
-From 33ec0b53befff2c0a7f3aa19ff08556d60585d6b Mon Sep 17 00:00:00 2001
-From: Andrew Lunn <andrew@lunn.ch>
-Date: Mon, 29 May 2023 18:32:39 +0200
-Subject: [PATCH 09/13] leds: trigger: netdev: validate configured netdev
-
-The netdev which the LED should blink for is configurable in
-/sys/class/led/foo/device_name. Ensure when offloading that the
-configured netdev is the same as the netdev the LED is associated
-with. If it is not, only perform software blinking.
-
-Signed-off-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/leds/trigger/ledtrig-netdev.c | 24 ++++++++++++++++++++++--
- 1 file changed, 22 insertions(+), 2 deletions(-)
-
---- a/drivers/leds/trigger/ledtrig-netdev.c
-+++ b/drivers/leds/trigger/ledtrig-netdev.c
-@@ -110,6 +110,24 @@ static bool supports_hw_control(struct l
- return !strcmp(led_cdev->hw_control_trigger, led_cdev->trigger->name);
- }
-
-+/*
-+ * Validate the configured netdev is the same as the one associated with
-+ * the LED driver in hw control.
-+ */
-+static bool validate_net_dev(struct led_classdev *led_cdev,
-+ struct net_device *net_dev)
-+{
-+ struct device *dev = led_cdev->hw_control_get_device(led_cdev);
-+ struct net_device *ndev;
-+
-+ if (!dev)
-+ return false;
-+
-+ ndev = to_net_dev(dev);
-+
-+ return ndev == net_dev;
-+}
-+
- static bool can_hw_control(struct led_netdev_data *trigger_data)
- {
- unsigned long default_interval = msecs_to_jiffies(NETDEV_LED_DEFAULT_INTERVAL);
-@@ -131,9 +149,11 @@ static bool can_hw_control(struct led_ne
- /*
- * net_dev must be set with hw control, otherwise no
- * blinking can be happening and there is nothing to
-- * offloaded.
-+ * offloaded. Additionally, for hw control to be
-+ * valid, the configured netdev must be the same as
-+ * netdev associated to the LED.
- */
-- if (!trigger_data->net_dev)
-+ if (!validate_net_dev(led_cdev, trigger_data->net_dev))
- return false;
-
- /* Check if the requested mode is supported */
+++ /dev/null
-From 0316cc5629d15880dd3f097d221c55ca648bcd61 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Mon, 29 May 2023 18:32:40 +0200
-Subject: [PATCH 10/13] leds: trigger: netdev: init mode if hw control already
- active
-
-On netdev trigger activation, hw control may be already active by
-default. If this is the case and a device is actually provided by
-hw_control_get_device(), init the already active mode and set the
-bool to hw_control bool to true to reflect the already set mode in the
-trigger_data.
-
-Co-developed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/leds/trigger/ledtrig-netdev.c | 17 +++++++++++++++++
- 1 file changed, 17 insertions(+)
-
---- a/drivers/leds/trigger/ledtrig-netdev.c
-+++ b/drivers/leds/trigger/ledtrig-netdev.c
-@@ -454,6 +454,8 @@ static void netdev_trig_work(struct work
- static int netdev_trig_activate(struct led_classdev *led_cdev)
- {
- struct led_netdev_data *trigger_data;
-+ unsigned long mode;
-+ struct device *dev;
- int rc;
-
- trigger_data = kzalloc(sizeof(struct led_netdev_data), GFP_KERNEL);
-@@ -475,6 +477,21 @@ static int netdev_trig_activate(struct l
- atomic_set(&trigger_data->interval, msecs_to_jiffies(NETDEV_LED_DEFAULT_INTERVAL));
- trigger_data->last_activity = 0;
-
-+ /* Check if hw control is active by default on the LED.
-+ * Init already enabled mode in hw control.
-+ */
-+ if (supports_hw_control(led_cdev) &&
-+ !led_cdev->hw_control_get(led_cdev, &mode)) {
-+ dev = led_cdev->hw_control_get_device(led_cdev);
-+ if (dev) {
-+ const char *name = dev_name(dev);
-+
-+ set_device_name(trigger_data, name, strlen(name));
-+ trigger_data->hw_control = true;
-+ trigger_data->mode = mode;
-+ }
-+ }
-+
- led_set_trigger_data(led_cdev, trigger_data);
-
- rc = register_netdevice_notifier(&trigger_data->notifier);
+++ /dev/null
-From 947acacab5ea151291b861cdfbde16ff5cf1b08c Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Mon, 29 May 2023 18:32:41 +0200
-Subject: [PATCH 11/13] leds: trigger: netdev: expose netdev trigger modes in
- linux include
-
-Expose netdev trigger modes to make them accessible by LED driver that
-will support netdev trigger for hw control.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/leds/trigger/ledtrig-netdev.c | 9 ---------
- include/linux/leds.h | 10 ++++++++++
- 2 files changed, 10 insertions(+), 9 deletions(-)
-
---- a/drivers/leds/trigger/ledtrig-netdev.c
-+++ b/drivers/leds/trigger/ledtrig-netdev.c
-@@ -56,15 +56,6 @@ struct led_netdev_data {
- bool hw_control;
- };
-
--enum led_trigger_netdev_modes {
-- TRIGGER_NETDEV_LINK = 0,
-- TRIGGER_NETDEV_TX,
-- TRIGGER_NETDEV_RX,
--
-- /* Keep last */
-- __TRIGGER_NETDEV_MAX,
--};
--
- static void set_baseline_state(struct led_netdev_data *trigger_data)
- {
- int current_brightness;
---- a/include/linux/leds.h
-+++ b/include/linux/leds.h
-@@ -527,6 +527,16 @@ static inline void *led_get_trigger_data
-
- #endif /* CONFIG_LEDS_TRIGGERS */
-
-+/* Trigger specific enum */
-+enum led_trigger_netdev_modes {
-+ TRIGGER_NETDEV_LINK = 0,
-+ TRIGGER_NETDEV_TX,
-+ TRIGGER_NETDEV_RX,
-+
-+ /* Keep last */
-+ __TRIGGER_NETDEV_MAX,
-+};
-+
- /* Trigger specific functions */
- #ifdef CONFIG_LEDS_TRIGGER_DISK
- void ledtrig_disk_activity(bool write);
+++ /dev/null
-From e0256648c831af13cbfe4a1787327fcec01c2807 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Mon, 29 May 2023 18:32:42 +0200
-Subject: [PATCH 12/13] net: dsa: qca8k: implement hw_control ops
-
-Implement hw_control ops to drive Switch LEDs based on hardware events.
-
-Netdev trigger is the declared supported trigger for hw control
-operation and supports the following mode:
-- tx
-- rx
-
-When hw_control_set is called, LEDs are set to follow the requested
-mode.
-Each LEDs will blink at 4Hz by default.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/qca/qca8k-leds.c | 154 +++++++++++++++++++++++++++++++
- 1 file changed, 154 insertions(+)
-
---- a/drivers/net/dsa/qca/qca8k-leds.c
-+++ b/drivers/net/dsa/qca/qca8k-leds.c
-@@ -32,6 +32,43 @@ qca8k_get_enable_led_reg(int port_num, i
- }
-
- static int
-+qca8k_get_control_led_reg(int port_num, int led_num, struct qca8k_led_pattern_en *reg_info)
-+{
-+ reg_info->reg = QCA8K_LED_CTRL_REG(led_num);
-+
-+ /* 6 total control rule:
-+ * 3 control rules for phy0-3 that applies to all their leds
-+ * 3 control rules for phy4
-+ */
-+ if (port_num == 4)
-+ reg_info->shift = QCA8K_LED_PHY4_CONTROL_RULE_SHIFT;
-+ else
-+ reg_info->shift = QCA8K_LED_PHY0123_CONTROL_RULE_SHIFT;
-+
-+ return 0;
-+}
-+
-+static int
-+qca8k_parse_netdev(unsigned long rules, u32 *offload_trigger)
-+{
-+ /* Parsing specific to netdev trigger */
-+ if (test_bit(TRIGGER_NETDEV_TX, &rules))
-+ *offload_trigger |= QCA8K_LED_TX_BLINK_MASK;
-+ if (test_bit(TRIGGER_NETDEV_RX, &rules))
-+ *offload_trigger |= QCA8K_LED_RX_BLINK_MASK;
-+
-+ if (rules && !*offload_trigger)
-+ return -EOPNOTSUPP;
-+
-+ /* Enable some default rule by default to the requested mode:
-+ * - Blink at 4Hz by default
-+ */
-+ *offload_trigger |= QCA8K_LED_BLINK_4HZ;
-+
-+ return 0;
-+}
-+
-+static int
- qca8k_led_brightness_set(struct qca8k_led *led,
- enum led_brightness brightness)
- {
-@@ -165,6 +202,119 @@ qca8k_cled_blink_set(struct led_classdev
- }
-
- static int
-+qca8k_cled_trigger_offload(struct led_classdev *ldev, bool enable)
-+{
-+ struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev);
-+
-+ struct qca8k_led_pattern_en reg_info;
-+ struct qca8k_priv *priv = led->priv;
-+ u32 mask, val = QCA8K_LED_ALWAYS_OFF;
-+
-+ qca8k_get_enable_led_reg(led->port_num, led->led_num, ®_info);
-+
-+ if (enable)
-+ val = QCA8K_LED_RULE_CONTROLLED;
-+
-+ if (led->port_num == 0 || led->port_num == 4) {
-+ mask = QCA8K_LED_PATTERN_EN_MASK;
-+ val <<= QCA8K_LED_PATTERN_EN_SHIFT;
-+ } else {
-+ mask = QCA8K_LED_PHY123_PATTERN_EN_MASK;
-+ }
-+
-+ return regmap_update_bits(priv->regmap, reg_info.reg, mask << reg_info.shift,
-+ val << reg_info.shift);
-+}
-+
-+static bool
-+qca8k_cled_hw_control_status(struct led_classdev *ldev)
-+{
-+ struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev);
-+
-+ struct qca8k_led_pattern_en reg_info;
-+ struct qca8k_priv *priv = led->priv;
-+ u32 val;
-+
-+ qca8k_get_enable_led_reg(led->port_num, led->led_num, ®_info);
-+
-+ regmap_read(priv->regmap, reg_info.reg, &val);
-+
-+ val >>= reg_info.shift;
-+
-+ if (led->port_num == 0 || led->port_num == 4) {
-+ val &= QCA8K_LED_PATTERN_EN_MASK;
-+ val >>= QCA8K_LED_PATTERN_EN_SHIFT;
-+ } else {
-+ val &= QCA8K_LED_PHY123_PATTERN_EN_MASK;
-+ }
-+
-+ return val == QCA8K_LED_RULE_CONTROLLED;
-+}
-+
-+static int
-+qca8k_cled_hw_control_is_supported(struct led_classdev *ldev, unsigned long rules)
-+{
-+ u32 offload_trigger = 0;
-+
-+ return qca8k_parse_netdev(rules, &offload_trigger);
-+}
-+
-+static int
-+qca8k_cled_hw_control_set(struct led_classdev *ldev, unsigned long rules)
-+{
-+ struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev);
-+ struct qca8k_led_pattern_en reg_info;
-+ struct qca8k_priv *priv = led->priv;
-+ u32 offload_trigger = 0;
-+ int ret;
-+
-+ ret = qca8k_parse_netdev(rules, &offload_trigger);
-+ if (ret)
-+ return ret;
-+
-+ ret = qca8k_cled_trigger_offload(ldev, true);
-+ if (ret)
-+ return ret;
-+
-+ qca8k_get_control_led_reg(led->port_num, led->led_num, ®_info);
-+
-+ return regmap_update_bits(priv->regmap, reg_info.reg,
-+ QCA8K_LED_RULE_MASK << reg_info.shift,
-+ offload_trigger << reg_info.shift);
-+}
-+
-+static int
-+qca8k_cled_hw_control_get(struct led_classdev *ldev, unsigned long *rules)
-+{
-+ struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev);
-+ struct qca8k_led_pattern_en reg_info;
-+ struct qca8k_priv *priv = led->priv;
-+ u32 val;
-+ int ret;
-+
-+ /* With hw control not active return err */
-+ if (!qca8k_cled_hw_control_status(ldev))
-+ return -EINVAL;
-+
-+ qca8k_get_control_led_reg(led->port_num, led->led_num, ®_info);
-+
-+ ret = regmap_read(priv->regmap, reg_info.reg, &val);
-+ if (ret)
-+ return ret;
-+
-+ val >>= reg_info.shift;
-+ val &= QCA8K_LED_RULE_MASK;
-+
-+ /* Parsing specific to netdev trigger */
-+ if (val & QCA8K_LED_TX_BLINK_MASK)
-+ set_bit(TRIGGER_NETDEV_TX, rules);
-+ if (val & QCA8K_LED_RX_BLINK_MASK)
-+ set_bit(TRIGGER_NETDEV_RX, rules);
-+
-+ return 0;
-+}
-+
-+static int
- qca8k_parse_port_leds(struct qca8k_priv *priv, struct fwnode_handle *port, int port_num)
- {
- struct fwnode_handle *led = NULL, *leds = NULL;
-@@ -224,6 +374,10 @@ qca8k_parse_port_leds(struct qca8k_priv
- port_led->cdev.max_brightness = 1;
- port_led->cdev.brightness_set_blocking = qca8k_cled_brightness_set_blocking;
- port_led->cdev.blink_set = qca8k_cled_blink_set;
-+ port_led->cdev.hw_control_is_supported = qca8k_cled_hw_control_is_supported;
-+ port_led->cdev.hw_control_set = qca8k_cled_hw_control_set;
-+ port_led->cdev.hw_control_get = qca8k_cled_hw_control_get;
-+ port_led->cdev.hw_control_trigger = "netdev";
- init_data.default_label = ":port";
- init_data.fwnode = led;
- init_data.devname_mandatory = true;
+++ /dev/null
-From 4f53c27f772e27e4cf4e5507d6f4d5980002cb6a Mon Sep 17 00:00:00 2001
-From: Andrew Lunn <andrew@lunn.ch>
-Date: Mon, 29 May 2023 18:32:43 +0200
-Subject: [PATCH 13/13] net: dsa: qca8k: add op to get ports netdev
-
-In order that the LED trigger can blink the switch MAC ports LED, it
-needs to know the netdev associated to the port. Add the callback to
-return the struct device of the netdev.
-
-Add an helper function qca8k_phy_to_port() to convert the phy back to
-dsa_port index, as we reference LED port based on the internal PHY
-index and needs to be converted back.
-
-Signed-off-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/dsa/qca/qca8k-leds.c | 27 +++++++++++++++++++++++++++
- 1 file changed, 27 insertions(+)
-
---- a/drivers/net/dsa/qca/qca8k-leds.c
-+++ b/drivers/net/dsa/qca/qca8k-leds.c
-@@ -5,6 +5,18 @@
- #include "qca8k.h"
- #include "qca8k_leds.h"
-
-+static u32 qca8k_phy_to_port(int phy)
-+{
-+ /* Internal PHY 0 has port at index 1.
-+ * Internal PHY 1 has port at index 2.
-+ * Internal PHY 2 has port at index 3.
-+ * Internal PHY 3 has port at index 4.
-+ * Internal PHY 4 has port at index 5.
-+ */
-+
-+ return phy + 1;
-+}
-+
- static int
- qca8k_get_enable_led_reg(int port_num, int led_num, struct qca8k_led_pattern_en *reg_info)
- {
-@@ -314,6 +326,20 @@ qca8k_cled_hw_control_get(struct led_cla
- return 0;
- }
-
-+static struct device *qca8k_cled_hw_control_get_device(struct led_classdev *ldev)
-+{
-+ struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev);
-+ struct qca8k_priv *priv = led->priv;
-+ struct dsa_port *dp;
-+
-+ dp = dsa_to_port(priv->ds, qca8k_phy_to_port(led->port_num));
-+ if (!dp)
-+ return NULL;
-+ if (dp->slave)
-+ return &dp->slave->dev;
-+ return NULL;
-+}
-+
- static int
- qca8k_parse_port_leds(struct qca8k_priv *priv, struct fwnode_handle *port, int port_num)
- {
-@@ -377,6 +403,7 @@ qca8k_parse_port_leds(struct qca8k_priv
- port_led->cdev.hw_control_is_supported = qca8k_cled_hw_control_is_supported;
- port_led->cdev.hw_control_set = qca8k_cled_hw_control_set;
- port_led->cdev.hw_control_get = qca8k_cled_hw_control_get;
-+ port_led->cdev.hw_control_get_device = qca8k_cled_hw_control_get_device;
- port_led->cdev.hw_control_trigger = "netdev";
- init_data.default_label = ":port";
- init_data.fwnode = led;
+++ /dev/null
-From d5e01266e7f5fa12400d4c8aa4e86fe89dcc61e9 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Mon, 19 Jun 2023 22:46:58 +0200
-Subject: [PATCH 1/3] leds: trigger: netdev: add additional specific link speed
- mode
-
-Add additional modes for specific link speed. Use ethtool APIs to get the
-current link speed and enable the LED accordingly. Under netdev event
-handler the rtnl lock is already held and is not needed to be set to
-access ethtool APIs.
-
-This is especially useful for PHY and Switch that supports LEDs hw
-control for specific link speed. (example scenario a PHY that have 2 LED
-connected one green and one orange where the green is turned on with
-1000mbps speed and orange is turned on with 10mpbs speed)
-
-On mode set from sysfs we check if we have enabled split link speed mode
-and reject enabling generic link mode to prevent wrong and redundant
-configuration.
-
-Rework logic on the set baseline state to support these new modes to
-select if we need to turn on or off the LED.
-
-Add additional modes:
-- link_10: Turn on LED when link speed is 10mbps
-- link_100: Turn on LED when link speed is 100mbps
-- link_1000: Turn on LED when link speed is 1000mbps
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Acked-by: Lee Jones <lee@kernel.org>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/leds/trigger/ledtrig-netdev.c | 80 +++++++++++++++++++++++----
- include/linux/leds.h | 3 +
- 2 files changed, 73 insertions(+), 10 deletions(-)
-
---- a/drivers/leds/trigger/ledtrig-netdev.c
-+++ b/drivers/leds/trigger/ledtrig-netdev.c
-@@ -13,6 +13,7 @@
- #include <linux/atomic.h>
- #include <linux/ctype.h>
- #include <linux/device.h>
-+#include <linux/ethtool.h>
- #include <linux/init.h>
- #include <linux/jiffies.h>
- #include <linux/kernel.h>
-@@ -21,6 +22,7 @@
- #include <linux/module.h>
- #include <linux/netdevice.h>
- #include <linux/mutex.h>
-+#include <linux/rtnetlink.h>
- #include <linux/timer.h>
- #include "../leds.h"
-
-@@ -52,6 +54,8 @@ struct led_netdev_data {
- unsigned int last_activity;
-
- unsigned long mode;
-+ int link_speed;
-+
- bool carrier_link_up;
- bool hw_control;
- };
-@@ -77,7 +81,24 @@ static void set_baseline_state(struct le
- if (!trigger_data->carrier_link_up) {
- led_set_brightness(led_cdev, LED_OFF);
- } else {
-+ bool blink_on = false;
-+
- if (test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode))
-+ blink_on = true;
-+
-+ if (test_bit(TRIGGER_NETDEV_LINK_10, &trigger_data->mode) &&
-+ trigger_data->link_speed == SPEED_10)
-+ blink_on = true;
-+
-+ if (test_bit(TRIGGER_NETDEV_LINK_100, &trigger_data->mode) &&
-+ trigger_data->link_speed == SPEED_100)
-+ blink_on = true;
-+
-+ if (test_bit(TRIGGER_NETDEV_LINK_1000, &trigger_data->mode) &&
-+ trigger_data->link_speed == SPEED_1000)
-+ blink_on = true;
-+
-+ if (blink_on)
- led_set_brightness(led_cdev,
- led_cdev->blink_brightness);
- else
-@@ -161,6 +182,18 @@ static bool can_hw_control(struct led_ne
- return true;
- }
-
-+static void get_device_state(struct led_netdev_data *trigger_data)
-+{
-+ struct ethtool_link_ksettings cmd;
-+
-+ trigger_data->carrier_link_up = netif_carrier_ok(trigger_data->net_dev);
-+ if (!trigger_data->carrier_link_up)
-+ return;
-+
-+ if (!__ethtool_get_link_ksettings(trigger_data->net_dev, &cmd))
-+ trigger_data->link_speed = cmd.base.speed;
-+}
-+
- static ssize_t device_name_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
-@@ -196,8 +229,12 @@ static int set_device_name(struct led_ne
- dev_get_by_name(&init_net, trigger_data->device_name);
-
- trigger_data->carrier_link_up = false;
-- if (trigger_data->net_dev != NULL)
-- trigger_data->carrier_link_up = netif_carrier_ok(trigger_data->net_dev);
-+ trigger_data->link_speed = SPEED_UNKNOWN;
-+ if (trigger_data->net_dev != NULL) {
-+ rtnl_lock();
-+ get_device_state(trigger_data);
-+ rtnl_unlock();
-+ }
-
- trigger_data->last_activity = 0;
-
-@@ -234,6 +271,9 @@ static ssize_t netdev_led_attr_show(stru
-
- switch (attr) {
- case TRIGGER_NETDEV_LINK:
-+ case TRIGGER_NETDEV_LINK_10:
-+ case TRIGGER_NETDEV_LINK_100:
-+ case TRIGGER_NETDEV_LINK_1000:
- case TRIGGER_NETDEV_TX:
- case TRIGGER_NETDEV_RX:
- bit = attr;
-@@ -249,7 +289,7 @@ static ssize_t netdev_led_attr_store(str
- size_t size, enum led_trigger_netdev_modes attr)
- {
- struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
-- unsigned long state;
-+ unsigned long state, mode = trigger_data->mode;
- int ret;
- int bit;
-
-@@ -259,6 +299,9 @@ static ssize_t netdev_led_attr_store(str
-
- switch (attr) {
- case TRIGGER_NETDEV_LINK:
-+ case TRIGGER_NETDEV_LINK_10:
-+ case TRIGGER_NETDEV_LINK_100:
-+ case TRIGGER_NETDEV_LINK_1000:
- case TRIGGER_NETDEV_TX:
- case TRIGGER_NETDEV_RX:
- bit = attr;
-@@ -267,13 +310,20 @@ static ssize_t netdev_led_attr_store(str
- return -EINVAL;
- }
-
-- cancel_delayed_work_sync(&trigger_data->work);
--
- if (state)
-- set_bit(bit, &trigger_data->mode);
-+ set_bit(bit, &mode);
- else
-- clear_bit(bit, &trigger_data->mode);
-+ clear_bit(bit, &mode);
-+
-+ if (test_bit(TRIGGER_NETDEV_LINK, &mode) &&
-+ (test_bit(TRIGGER_NETDEV_LINK_10, &mode) ||
-+ test_bit(TRIGGER_NETDEV_LINK_100, &mode) ||
-+ test_bit(TRIGGER_NETDEV_LINK_1000, &mode)))
-+ return -EINVAL;
-+
-+ cancel_delayed_work_sync(&trigger_data->work);
-
-+ trigger_data->mode = mode;
- trigger_data->hw_control = can_hw_control(trigger_data);
-
- set_baseline_state(trigger_data);
-@@ -295,6 +345,9 @@ static ssize_t netdev_led_attr_store(str
- static DEVICE_ATTR_RW(trigger_name)
-
- DEFINE_NETDEV_TRIGGER(link, TRIGGER_NETDEV_LINK);
-+DEFINE_NETDEV_TRIGGER(link_10, TRIGGER_NETDEV_LINK_10);
-+DEFINE_NETDEV_TRIGGER(link_100, TRIGGER_NETDEV_LINK_100);
-+DEFINE_NETDEV_TRIGGER(link_1000, TRIGGER_NETDEV_LINK_1000);
- DEFINE_NETDEV_TRIGGER(tx, TRIGGER_NETDEV_TX);
- DEFINE_NETDEV_TRIGGER(rx, TRIGGER_NETDEV_RX);
-
-@@ -338,6 +391,9 @@ static DEVICE_ATTR_RW(interval);
- static struct attribute *netdev_trig_attrs[] = {
- &dev_attr_device_name.attr,
- &dev_attr_link.attr,
-+ &dev_attr_link_10.attr,
-+ &dev_attr_link_100.attr,
-+ &dev_attr_link_1000.attr,
- &dev_attr_rx.attr,
- &dev_attr_tx.attr,
- &dev_attr_interval.attr,
-@@ -368,9 +424,10 @@ static int netdev_trig_notify(struct not
- mutex_lock(&trigger_data->lock);
-
- trigger_data->carrier_link_up = false;
-+ trigger_data->link_speed = SPEED_UNKNOWN;
- switch (evt) {
- case NETDEV_CHANGENAME:
-- trigger_data->carrier_link_up = netif_carrier_ok(dev);
-+ get_device_state(trigger_data);
- fallthrough;
- case NETDEV_REGISTER:
- if (trigger_data->net_dev)
-@@ -384,7 +441,7 @@ static int netdev_trig_notify(struct not
- break;
- case NETDEV_UP:
- case NETDEV_CHANGE:
-- trigger_data->carrier_link_up = netif_carrier_ok(dev);
-+ get_device_state(trigger_data);
- break;
- }
-
-@@ -427,7 +484,10 @@ static void netdev_trig_work(struct work
- if (trigger_data->last_activity != new_activity) {
- led_stop_software_blink(trigger_data->led_cdev);
-
-- invert = test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode);
-+ invert = test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode) ||
-+ test_bit(TRIGGER_NETDEV_LINK_10, &trigger_data->mode) ||
-+ test_bit(TRIGGER_NETDEV_LINK_100, &trigger_data->mode) ||
-+ test_bit(TRIGGER_NETDEV_LINK_1000, &trigger_data->mode);
- interval = jiffies_to_msecs(
- atomic_read(&trigger_data->interval));
- /* base state is ON (link present) */
---- a/include/linux/leds.h
-+++ b/include/linux/leds.h
-@@ -530,6 +530,9 @@ static inline void *led_get_trigger_data
- /* Trigger specific enum */
- enum led_trigger_netdev_modes {
- TRIGGER_NETDEV_LINK = 0,
-+ TRIGGER_NETDEV_LINK_10,
-+ TRIGGER_NETDEV_LINK_100,
-+ TRIGGER_NETDEV_LINK_1000,
- TRIGGER_NETDEV_TX,
- TRIGGER_NETDEV_RX,
-
+++ /dev/null
-From f22f95b9ff1551c9bab13104131929f33d51f23f Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Mon, 19 Jun 2023 22:46:59 +0200
-Subject: [PATCH 2/3] leds: trigger: netdev: add additional specific link
- duplex mode
-
-Add additional modes for specific link duplex. Use ethtool APIs to get the
-current link duplex and enable the LED accordingly. Under netdev event
-handler the rtnl lock is already held and is not needed to be set to
-access ethtool APIs.
-
-This is especially useful for PHY and Switch that supports LEDs hw
-control for specific link duplex.
-
-Add additional modes:
-- half_duplex: Turn on LED when link is half duplex
-- full_duplex: Turn on LED when link is full duplex
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Acked-by: Lee Jones <lee@kernel.org>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/leds/trigger/ledtrig-netdev.c | 27 +++++++++++++++++++++++++--
- include/linux/leds.h | 2 ++
- 2 files changed, 27 insertions(+), 2 deletions(-)
-
---- a/drivers/leds/trigger/ledtrig-netdev.c
-+++ b/drivers/leds/trigger/ledtrig-netdev.c
-@@ -55,6 +55,7 @@ struct led_netdev_data {
-
- unsigned long mode;
- int link_speed;
-+ u8 duplex;
-
- bool carrier_link_up;
- bool hw_control;
-@@ -98,6 +99,14 @@ static void set_baseline_state(struct le
- trigger_data->link_speed == SPEED_1000)
- blink_on = true;
-
-+ if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) &&
-+ trigger_data->duplex == DUPLEX_HALF)
-+ blink_on = true;
-+
-+ if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &trigger_data->mode) &&
-+ trigger_data->duplex == DUPLEX_FULL)
-+ blink_on = true;
-+
- if (blink_on)
- led_set_brightness(led_cdev,
- led_cdev->blink_brightness);
-@@ -190,8 +199,10 @@ static void get_device_state(struct led_
- if (!trigger_data->carrier_link_up)
- return;
-
-- if (!__ethtool_get_link_ksettings(trigger_data->net_dev, &cmd))
-+ if (!__ethtool_get_link_ksettings(trigger_data->net_dev, &cmd)) {
- trigger_data->link_speed = cmd.base.speed;
-+ trigger_data->duplex = cmd.base.duplex;
-+ }
- }
-
- static ssize_t device_name_show(struct device *dev,
-@@ -230,6 +241,7 @@ static int set_device_name(struct led_ne
-
- trigger_data->carrier_link_up = false;
- trigger_data->link_speed = SPEED_UNKNOWN;
-+ trigger_data->duplex = DUPLEX_UNKNOWN;
- if (trigger_data->net_dev != NULL) {
- rtnl_lock();
- get_device_state(trigger_data);
-@@ -274,6 +286,8 @@ static ssize_t netdev_led_attr_show(stru
- case TRIGGER_NETDEV_LINK_10:
- case TRIGGER_NETDEV_LINK_100:
- case TRIGGER_NETDEV_LINK_1000:
-+ case TRIGGER_NETDEV_HALF_DUPLEX:
-+ case TRIGGER_NETDEV_FULL_DUPLEX:
- case TRIGGER_NETDEV_TX:
- case TRIGGER_NETDEV_RX:
- bit = attr;
-@@ -302,6 +316,8 @@ static ssize_t netdev_led_attr_store(str
- case TRIGGER_NETDEV_LINK_10:
- case TRIGGER_NETDEV_LINK_100:
- case TRIGGER_NETDEV_LINK_1000:
-+ case TRIGGER_NETDEV_HALF_DUPLEX:
-+ case TRIGGER_NETDEV_FULL_DUPLEX:
- case TRIGGER_NETDEV_TX:
- case TRIGGER_NETDEV_RX:
- bit = attr;
-@@ -348,6 +364,8 @@ DEFINE_NETDEV_TRIGGER(link, TRIGGER_NETD
- DEFINE_NETDEV_TRIGGER(link_10, TRIGGER_NETDEV_LINK_10);
- DEFINE_NETDEV_TRIGGER(link_100, TRIGGER_NETDEV_LINK_100);
- DEFINE_NETDEV_TRIGGER(link_1000, TRIGGER_NETDEV_LINK_1000);
-+DEFINE_NETDEV_TRIGGER(half_duplex, TRIGGER_NETDEV_HALF_DUPLEX);
-+DEFINE_NETDEV_TRIGGER(full_duplex, TRIGGER_NETDEV_FULL_DUPLEX);
- DEFINE_NETDEV_TRIGGER(tx, TRIGGER_NETDEV_TX);
- DEFINE_NETDEV_TRIGGER(rx, TRIGGER_NETDEV_RX);
-
-@@ -394,6 +412,8 @@ static struct attribute *netdev_trig_att
- &dev_attr_link_10.attr,
- &dev_attr_link_100.attr,
- &dev_attr_link_1000.attr,
-+ &dev_attr_full_duplex.attr,
-+ &dev_attr_half_duplex.attr,
- &dev_attr_rx.attr,
- &dev_attr_tx.attr,
- &dev_attr_interval.attr,
-@@ -425,6 +445,7 @@ static int netdev_trig_notify(struct not
-
- trigger_data->carrier_link_up = false;
- trigger_data->link_speed = SPEED_UNKNOWN;
-+ trigger_data->duplex = DUPLEX_UNKNOWN;
- switch (evt) {
- case NETDEV_CHANGENAME:
- get_device_state(trigger_data);
-@@ -487,7 +508,9 @@ static void netdev_trig_work(struct work
- invert = test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode) ||
- test_bit(TRIGGER_NETDEV_LINK_10, &trigger_data->mode) ||
- test_bit(TRIGGER_NETDEV_LINK_100, &trigger_data->mode) ||
-- test_bit(TRIGGER_NETDEV_LINK_1000, &trigger_data->mode);
-+ test_bit(TRIGGER_NETDEV_LINK_1000, &trigger_data->mode) ||
-+ test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) ||
-+ test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &trigger_data->mode);
- interval = jiffies_to_msecs(
- atomic_read(&trigger_data->interval));
- /* base state is ON (link present) */
---- a/include/linux/leds.h
-+++ b/include/linux/leds.h
-@@ -533,6 +533,8 @@ enum led_trigger_netdev_modes {
- TRIGGER_NETDEV_LINK_10,
- TRIGGER_NETDEV_LINK_100,
- TRIGGER_NETDEV_LINK_1000,
-+ TRIGGER_NETDEV_HALF_DUPLEX,
-+ TRIGGER_NETDEV_FULL_DUPLEX,
- TRIGGER_NETDEV_TX,
- TRIGGER_NETDEV_RX,
-
+++ /dev/null
-From b655892ffd6d89b0c7407e099c40dbde82ee3f03 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Mon, 19 Jun 2023 22:47:00 +0200
-Subject: [PATCH 3/3] leds: trigger: netdev: expose hw_control status via sysfs
-
-Expose hw_control status via sysfs for the netdev trigger to give
-userspace better understanding of the current state of the trigger and
-the LED.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Reviewed-by: Kalesh AP <kalesh-anakkur.purayil@broadcom.com>
-Acked-by: Lee Jones <lee@kernel.org>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/leds/trigger/ledtrig-netdev.c | 11 +++++++++++
- 1 file changed, 11 insertions(+)
-
---- a/drivers/leds/trigger/ledtrig-netdev.c
-+++ b/drivers/leds/trigger/ledtrig-netdev.c
-@@ -406,6 +406,16 @@ static ssize_t interval_store(struct dev
-
- static DEVICE_ATTR_RW(interval);
-
-+static ssize_t hw_control_show(struct device *dev,
-+ struct device_attribute *attr, char *buf)
-+{
-+ struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
-+
-+ return sprintf(buf, "%d\n", trigger_data->hw_control);
-+}
-+
-+static DEVICE_ATTR_RO(hw_control);
-+
- static struct attribute *netdev_trig_attrs[] = {
- &dev_attr_device_name.attr,
- &dev_attr_link.attr,
-@@ -417,6 +427,7 @@ static struct attribute *netdev_trig_att
- &dev_attr_rx.attr,
- &dev_attr_tx.attr,
- &dev_attr_interval.attr,
-+ &dev_attr_hw_control.attr,
- NULL
- };
- ATTRIBUTE_GROUPS(netdev_trig);
+++ /dev/null
-From 2555f35a4f428a9bfdf09aa0459dbfdf59a24a9a Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Wed, 21 Jun 2023 11:54:09 +0200
-Subject: [PATCH] net: dsa: qca8k: add support for additional modes for netdev
- trigger
-
-The QCA8K switch supports additional modes that can be handled in
-hardware for the LED netdev trigger.
-
-Add these additional modes to further support the Switch LEDs and
-offload more blink modes.
-
-Add additional modes:
-- link_10
-- link_100
-- link_1000
-- half_duplex
-- full_duplex
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
-Link: https://lore.kernel.org/r/20230621095409.25859-1-ansuelsmth@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/dsa/qca/qca8k-leds.c | 20 ++++++++++++++++++++
- 1 file changed, 20 insertions(+)
-
---- a/drivers/net/dsa/qca/qca8k-leds.c
-+++ b/drivers/net/dsa/qca/qca8k-leds.c
-@@ -68,6 +68,16 @@ qca8k_parse_netdev(unsigned long rules,
- *offload_trigger |= QCA8K_LED_TX_BLINK_MASK;
- if (test_bit(TRIGGER_NETDEV_RX, &rules))
- *offload_trigger |= QCA8K_LED_RX_BLINK_MASK;
-+ if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
-+ *offload_trigger |= QCA8K_LED_LINK_10M_EN_MASK;
-+ if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
-+ *offload_trigger |= QCA8K_LED_LINK_100M_EN_MASK;
-+ if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
-+ *offload_trigger |= QCA8K_LED_LINK_1000M_EN_MASK;
-+ if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))
-+ *offload_trigger |= QCA8K_LED_HALF_DUPLEX_MASK;
-+ if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))
-+ *offload_trigger |= QCA8K_LED_FULL_DUPLEX_MASK;
-
- if (rules && !*offload_trigger)
- return -EOPNOTSUPP;
-@@ -322,6 +332,16 @@ qca8k_cled_hw_control_get(struct led_cla
- set_bit(TRIGGER_NETDEV_TX, rules);
- if (val & QCA8K_LED_RX_BLINK_MASK)
- set_bit(TRIGGER_NETDEV_RX, rules);
-+ if (val & QCA8K_LED_LINK_10M_EN_MASK)
-+ set_bit(TRIGGER_NETDEV_LINK_10, rules);
-+ if (val & QCA8K_LED_LINK_100M_EN_MASK)
-+ set_bit(TRIGGER_NETDEV_LINK_100, rules);
-+ if (val & QCA8K_LED_LINK_1000M_EN_MASK)
-+ set_bit(TRIGGER_NETDEV_LINK_1000, rules);
-+ if (val & QCA8K_LED_HALF_DUPLEX_MASK)
-+ set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);
-+ if (val & QCA8K_LED_FULL_DUPLEX_MASK)
-+ set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);
-
- return 0;
- }
+++ /dev/null
-From 4f86eb098e18fd0f032877dfa1a7e8c1503ca409 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Alexis=20Lothor=C3=A9?= <alexis.lothore@bootlin.com>
-Date: Mon, 29 May 2023 10:02:41 +0200
-Subject: [PATCH 1/6] net: dsa: mv88e6xxx: pass directly chip structure to
- mv88e6xxx_phy_is_internal
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Since this function is a simple helper, we do not need to pass a full
-dsa_switch structure, we can directly pass the mv88e6xxx_chip structure.
-Doing so will allow to share this function with any other function
-not manipulating dsa_switch structure but needing info about number of
-internal phys
-
-Signed-off-by: Alexis Lothoré <alexis.lothore@bootlin.com>
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/dsa/mv88e6xxx/chip.c | 10 ++++------
- 1 file changed, 4 insertions(+), 6 deletions(-)
-
---- a/drivers/net/dsa/mv88e6xxx/chip.c
-+++ b/drivers/net/dsa/mv88e6xxx/chip.c
-@@ -470,10 +470,8 @@ restore_link:
- return err;
- }
-
--static int mv88e6xxx_phy_is_internal(struct dsa_switch *ds, int port)
-+static int mv88e6xxx_phy_is_internal(struct mv88e6xxx_chip *chip, int port)
- {
-- struct mv88e6xxx_chip *chip = ds->priv;
--
- return port < chip->info->num_internal_phys;
- }
-
-@@ -591,7 +589,7 @@ static void mv88e6095_phylink_get_caps(s
-
- config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100;
-
-- if (mv88e6xxx_phy_is_internal(chip->ds, port)) {
-+ if (mv88e6xxx_phy_is_internal(chip, port)) {
- __set_bit(PHY_INTERFACE_MODE_MII, config->supported_interfaces);
- } else {
- if (cmode < ARRAY_SIZE(mv88e6185_phy_interface_modes) &&
-@@ -839,7 +837,7 @@ static void mv88e6xxx_get_caps(struct ds
- chip->info->ops->phylink_get_caps(chip, port, config);
- mv88e6xxx_reg_unlock(chip);
-
-- if (mv88e6xxx_phy_is_internal(ds, port)) {
-+ if (mv88e6xxx_phy_is_internal(chip, port)) {
- __set_bit(PHY_INTERFACE_MODE_INTERNAL,
- config->supported_interfaces);
- /* Internal ports with no phy-mode need GMII for PHYLIB */
-@@ -860,7 +858,7 @@ static void mv88e6xxx_mac_config(struct
-
- mv88e6xxx_reg_lock(chip);
-
-- if (mode != MLO_AN_PHY || !mv88e6xxx_phy_is_internal(ds, port)) {
-+ if (mode != MLO_AN_PHY || !mv88e6xxx_phy_is_internal(chip, port)) {
- /* In inband mode, the link may come up at any time while the
- * link is not forced down. Force the link down while we
- * reconfigure the interface mode.
+++ /dev/null
-From 73cbfad9296eed004992806e056db5b48583ca41 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Alexis=20Lothor=C3=A9?= <alexis.lothore@bootlin.com>
-Date: Mon, 29 May 2023 10:02:42 +0200
-Subject: [PATCH 2/6] net: dsa: mv88e6xxx: use mv88e6xxx_phy_is_internal in
- mv88e6xxx_port_ppu_updates
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Make sure to use existing helper to get internal PHYs count instead of
-redoing it manually
-
-Signed-off-by: Alexis Lothoré <alexis.lothore@bootlin.com>
-Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/dsa/mv88e6xxx/chip.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/net/dsa/mv88e6xxx/chip.c
-+++ b/drivers/net/dsa/mv88e6xxx/chip.c
-@@ -484,7 +484,7 @@ static int mv88e6xxx_port_ppu_updates(st
- * report whether the port is internal.
- */
- if (chip->info->family == MV88E6XXX_FAMILY_6250)
-- return port < chip->info->num_internal_phys;
-+ return mv88e6xxx_phy_is_internal(chip, port);
-
- err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®);
- if (err) {
+++ /dev/null
-From 1414d30660d201f515a9d877571ceea9ca190b6a Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Alexis=20Lothor=C3=A9?= <alexis.lothore@bootlin.com>
-Date: Mon, 29 May 2023 10:02:43 +0200
-Subject: [PATCH 3/6] net: dsa: mv88e6xxx: add field to specify internal phys
- layout
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-mv88e6xxx currently assumes that switch equipped with internal phys have
-those phys mapped contiguously starting from port 0 (see
-mv88e6xxx_phy_is_internal). However, some switches have internal PHYs but
-NOT starting from port 0. For example 88e6393X, 88E6193X and 88E6191X have
-integrated PHYs available on ports 1 to 8
-To properly support this offset, add a new field to allow specifying an
-internal PHYs layout. If field is not set, default layout is assumed (start
-at port 0)
-
-Signed-off-by: Alexis Lothoré <alexis.lothore@bootlin.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/dsa/mv88e6xxx/chip.c | 4 +++-
- drivers/net/dsa/mv88e6xxx/chip.h | 5 +++++
- drivers/net/dsa/mv88e6xxx/global2.c | 5 ++++-
- 3 files changed, 12 insertions(+), 2 deletions(-)
-
---- a/drivers/net/dsa/mv88e6xxx/chip.c
-+++ b/drivers/net/dsa/mv88e6xxx/chip.c
-@@ -472,7 +472,9 @@ restore_link:
-
- static int mv88e6xxx_phy_is_internal(struct mv88e6xxx_chip *chip, int port)
- {
-- return port < chip->info->num_internal_phys;
-+ return port >= chip->info->internal_phys_offset &&
-+ port < chip->info->num_internal_phys +
-+ chip->info->internal_phys_offset;
- }
-
- static int mv88e6xxx_port_ppu_updates(struct mv88e6xxx_chip *chip, int port)
---- a/drivers/net/dsa/mv88e6xxx/chip.h
-+++ b/drivers/net/dsa/mv88e6xxx/chip.h
-@@ -167,6 +167,11 @@ struct mv88e6xxx_info {
-
- /* Supports PTP */
- bool ptp_support;
-+
-+ /* Internal PHY start index. 0 means that internal PHYs range starts at
-+ * port 0, 1 means internal PHYs range starts at port 1, etc
-+ */
-+ unsigned int internal_phys_offset;
- };
-
- struct mv88e6xxx_atu_entry {
---- a/drivers/net/dsa/mv88e6xxx/global2.c
-+++ b/drivers/net/dsa/mv88e6xxx/global2.c
-@@ -1185,8 +1185,11 @@ int mv88e6xxx_g2_irq_mdio_setup(struct m
- struct mii_bus *bus)
- {
- int phy, irq, err, err_phy;
-+ int phy_start = chip->info->internal_phys_offset;
-+ int phy_end = chip->info->internal_phys_offset +
-+ chip->info->num_internal_phys;
-
-- for (phy = 0; phy < chip->info->num_internal_phys; phy++) {
-+ for (phy = phy_start; phy < phy_end; phy++) {
- irq = irq_find_mapping(chip->g2_irq.domain, phy);
- if (irq < 0) {
- err = irq;
+++ /dev/null
-From eb8c75f82a6711387f3b9e03e28923f3e75a761b Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Alexis=20Lothor=C3=A9?= <alexis.lothore@bootlin.com>
-Date: Mon, 29 May 2023 10:02:44 +0200
-Subject: [PATCH 4/6] net: dsa: mv88e6xxx: fix 88E6393X family internal phys
- layout
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-88E6393X/88E6193X/88E6191X switches have in fact 8 internal PHYs, but those
-are not present starting at port 0: supported ports go from 1 to 8
-
-Signed-off-by: Alexis Lothoré <alexis.lothore@bootlin.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/dsa/mv88e6xxx/chip.c | 9 ++++++---
- 1 file changed, 6 insertions(+), 3 deletions(-)
-
---- a/drivers/net/dsa/mv88e6xxx/chip.c
-+++ b/drivers/net/dsa/mv88e6xxx/chip.c
-@@ -5944,7 +5944,8 @@ static const struct mv88e6xxx_info mv88e
- .name = "Marvell 88E6191X",
- .num_databases = 4096,
- .num_ports = 11, /* 10 + Z80 */
-- .num_internal_phys = 9,
-+ .num_internal_phys = 8,
-+ .internal_phys_offset = 1,
- .max_vid = 8191,
- .max_sid = 63,
- .port_base_addr = 0x0,
-@@ -5967,7 +5968,8 @@ static const struct mv88e6xxx_info mv88e
- .name = "Marvell 88E6193X",
- .num_databases = 4096,
- .num_ports = 11, /* 10 + Z80 */
-- .num_internal_phys = 9,
-+ .num_internal_phys = 8,
-+ .internal_phys_offset = 1,
- .max_vid = 8191,
- .max_sid = 63,
- .port_base_addr = 0x0,
-@@ -6286,7 +6288,8 @@ static const struct mv88e6xxx_info mv88e
- .name = "Marvell 88E6393X",
- .num_databases = 4096,
- .num_ports = 11, /* 10 + Z80 */
-- .num_internal_phys = 9,
-+ .num_internal_phys = 8,
-+ .internal_phys_offset = 1,
- .max_vid = 8191,
- .max_sid = 63,
- .port_base_addr = 0x0,
+++ /dev/null
-From cef945452c8468efce75ba0dc8420510a5b84af9 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Alexis=20Lothor=C3=A9?= <alexis.lothore@bootlin.com>
-Date: Mon, 29 May 2023 10:02:45 +0200
-Subject: [PATCH 5/6] net: dsa: mv88e6xxx: pass mv88e6xxx_chip structure to
- port_max_speed_mode
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Some switches families have minor differences on supported link speed for
-ports. Instead of redefining a new port_max_speed_mode for each different
-configuration, allow to pass mv88e6xxx_chip structure to allow
-differentiating those chips by known chip id
-
-Signed-off-by: Alexis Lothoré <alexis.lothore@bootlin.com>
-Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/dsa/mv88e6xxx/chip.c | 2 +-
- drivers/net/dsa/mv88e6xxx/chip.h | 3 ++-
- drivers/net/dsa/mv88e6xxx/port.c | 12 ++++++++----
- drivers/net/dsa/mv88e6xxx/port.h | 12 ++++++++----
- 4 files changed, 19 insertions(+), 10 deletions(-)
-
---- a/drivers/net/dsa/mv88e6xxx/chip.c
-+++ b/drivers/net/dsa/mv88e6xxx/chip.c
-@@ -3328,7 +3328,7 @@ static int mv88e6xxx_setup_port(struct m
- caps = pl_config.mac_capabilities;
-
- if (chip->info->ops->port_max_speed_mode)
-- mode = chip->info->ops->port_max_speed_mode(port);
-+ mode = chip->info->ops->port_max_speed_mode(chip, port);
- else
- mode = PHY_INTERFACE_MODE_NA;
-
---- a/drivers/net/dsa/mv88e6xxx/chip.h
-+++ b/drivers/net/dsa/mv88e6xxx/chip.h
-@@ -508,7 +508,8 @@ struct mv88e6xxx_ops {
- int speed, int duplex);
-
- /* What interface mode should be used for maximum speed? */
-- phy_interface_t (*port_max_speed_mode)(int port);
-+ phy_interface_t (*port_max_speed_mode)(struct mv88e6xxx_chip *chip,
-+ int port);
-
- int (*port_tag_remap)(struct mv88e6xxx_chip *chip, int port);
-
---- a/drivers/net/dsa/mv88e6xxx/port.c
-+++ b/drivers/net/dsa/mv88e6xxx/port.c
-@@ -342,7 +342,8 @@ int mv88e6341_port_set_speed_duplex(stru
- duplex);
- }
-
--phy_interface_t mv88e6341_port_max_speed_mode(int port)
-+phy_interface_t mv88e6341_port_max_speed_mode(struct mv88e6xxx_chip *chip,
-+ int port)
- {
- if (port == 5)
- return PHY_INTERFACE_MODE_2500BASEX;
-@@ -381,7 +382,8 @@ int mv88e6390_port_set_speed_duplex(stru
- duplex);
- }
-
--phy_interface_t mv88e6390_port_max_speed_mode(int port)
-+phy_interface_t mv88e6390_port_max_speed_mode(struct mv88e6xxx_chip *chip,
-+ int port)
- {
- if (port == 9 || port == 10)
- return PHY_INTERFACE_MODE_2500BASEX;
-@@ -403,7 +405,8 @@ int mv88e6390x_port_set_speed_duplex(str
- duplex);
- }
-
--phy_interface_t mv88e6390x_port_max_speed_mode(int port)
-+phy_interface_t mv88e6390x_port_max_speed_mode(struct mv88e6xxx_chip *chip,
-+ int port)
- {
- if (port == 9 || port == 10)
- return PHY_INTERFACE_MODE_XAUI;
-@@ -500,7 +503,8 @@ int mv88e6393x_port_set_speed_duplex(str
- return 0;
- }
-
--phy_interface_t mv88e6393x_port_max_speed_mode(int port)
-+phy_interface_t mv88e6393x_port_max_speed_mode(struct mv88e6xxx_chip *chip,
-+ int port)
- {
- if (port == 0 || port == 9 || port == 10)
- return PHY_INTERFACE_MODE_10GBASER;
---- a/drivers/net/dsa/mv88e6xxx/port.h
-+++ b/drivers/net/dsa/mv88e6xxx/port.h
-@@ -359,10 +359,14 @@ int mv88e6390x_port_set_speed_duplex(str
- int mv88e6393x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
- int speed, int duplex);
-
--phy_interface_t mv88e6341_port_max_speed_mode(int port);
--phy_interface_t mv88e6390_port_max_speed_mode(int port);
--phy_interface_t mv88e6390x_port_max_speed_mode(int port);
--phy_interface_t mv88e6393x_port_max_speed_mode(int port);
-+phy_interface_t mv88e6341_port_max_speed_mode(struct mv88e6xxx_chip *chip,
-+ int port);
-+phy_interface_t mv88e6390_port_max_speed_mode(struct mv88e6xxx_chip *chip,
-+ int port);
-+phy_interface_t mv88e6390x_port_max_speed_mode(struct mv88e6xxx_chip *chip,
-+ int port);
-+phy_interface_t mv88e6393x_port_max_speed_mode(struct mv88e6xxx_chip *chip,
-+ int port);
-
- int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state);
-
+++ /dev/null
-From 23680321789863bab2d60af507858ce50ff9f56a Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Alexis=20Lothor=C3=A9?= <alexis.lothore@bootlin.com>
-Date: Mon, 29 May 2023 10:02:46 +0200
-Subject: [PATCH 6/6] net: dsa: mv88e6xxx: enable support for 88E6361 switch
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Marvell 88E6361 is an 8-port switch derived from the
-88E6393X/88E9193X/88E6191X switches family. It can benefit from the
-existing mv88e6xxx driver by simply adding the proper switch description in
-the driver. Main differences with other switches from this
-family are:
-- 8 ports exposed (instead of 11): ports 1, 2 and 8 not available
-- No 5GBase-x nor SFI/USXGMII support
-
-Signed-off-by: Alexis Lothoré <alexis.lothore@bootlin.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/dsa/mv88e6xxx/chip.c | 42 ++++++++++++++++++++++++++++----
- drivers/net/dsa/mv88e6xxx/chip.h | 3 ++-
- drivers/net/dsa/mv88e6xxx/port.c | 14 ++++++++---
- drivers/net/dsa/mv88e6xxx/port.h | 1 +
- 4 files changed, 51 insertions(+), 9 deletions(-)
-
---- a/drivers/net/dsa/mv88e6xxx/chip.c
-+++ b/drivers/net/dsa/mv88e6xxx/chip.c
-@@ -797,6 +797,8 @@ static void mv88e6393x_phylink_get_caps(
- unsigned long *supported = config->supported_interfaces;
- bool is_6191x =
- chip->info->prod_num == MV88E6XXX_PORT_SWITCH_ID_PROD_6191X;
-+ bool is_6361 =
-+ chip->info->prod_num == MV88E6XXX_PORT_SWITCH_ID_PROD_6361;
-
- mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported);
-
-@@ -811,13 +813,17 @@ static void mv88e6393x_phylink_get_caps(
- /* 6191X supports >1G modes only on port 10 */
- if (!is_6191x || port == 10) {
- __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported);
-- __set_bit(PHY_INTERFACE_MODE_5GBASER, supported);
-- __set_bit(PHY_INTERFACE_MODE_10GBASER, supported);
-+ config->mac_capabilities |= MAC_2500FD;
-+
-+ /* 6361 only supports up to 2500BaseX */
-+ if (!is_6361) {
-+ __set_bit(PHY_INTERFACE_MODE_5GBASER, supported);
-+ __set_bit(PHY_INTERFACE_MODE_10GBASER, supported);
-+ config->mac_capabilities |= MAC_5000FD |
-+ MAC_10000FD;
-+ }
- /* FIXME: USXGMII is not supported yet */
- /* __set_bit(PHY_INTERFACE_MODE_USXGMII, supported); */
--
-- config->mac_capabilities |= MAC_2500FD | MAC_5000FD |
-- MAC_10000FD;
- }
- }
-
-@@ -6231,6 +6237,32 @@ static const struct mv88e6xxx_info mv88e
- .ptp_support = true,
- .ops = &mv88e6352_ops,
- },
-+ [MV88E6361] = {
-+ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6361,
-+ .family = MV88E6XXX_FAMILY_6393,
-+ .name = "Marvell 88E6361",
-+ .num_databases = 4096,
-+ .num_macs = 16384,
-+ .num_ports = 11,
-+ /* Ports 1, 2 and 8 are not routed */
-+ .invalid_port_mask = BIT(1) | BIT(2) | BIT(8),
-+ .num_internal_phys = 5,
-+ .internal_phys_offset = 3,
-+ .max_vid = 4095,
-+ .max_sid = 63,
-+ .port_base_addr = 0x0,
-+ .phy_base_addr = 0x0,
-+ .global1_addr = 0x1b,
-+ .global2_addr = 0x1c,
-+ .age_time_coeff = 3750,
-+ .g1_irqs = 10,
-+ .g2_irqs = 14,
-+ .atu_move_port_mask = 0x1f,
-+ .pvt = true,
-+ .multi_chip = true,
-+ .ptp_support = true,
-+ .ops = &mv88e6393x_ops,
-+ },
- [MV88E6390] = {
- .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6390,
- .family = MV88E6XXX_FAMILY_6390,
---- a/drivers/net/dsa/mv88e6xxx/chip.h
-+++ b/drivers/net/dsa/mv88e6xxx/chip.h
-@@ -82,6 +82,7 @@ enum mv88e6xxx_model {
- MV88E6350,
- MV88E6351,
- MV88E6352,
-+ MV88E6361,
- MV88E6390,
- MV88E6390X,
- MV88E6393X,
-@@ -100,7 +101,7 @@ enum mv88e6xxx_family {
- MV88E6XXX_FAMILY_6351, /* 6171 6175 6350 6351 */
- MV88E6XXX_FAMILY_6352, /* 6172 6176 6240 6352 */
- MV88E6XXX_FAMILY_6390, /* 6190 6190X 6191 6290 6390 6390X */
-- MV88E6XXX_FAMILY_6393, /* 6191X 6193X 6393X */
-+ MV88E6XXX_FAMILY_6393, /* 6191X 6193X 6361 6393X */
- };
-
- /**
---- a/drivers/net/dsa/mv88e6xxx/port.c
-+++ b/drivers/net/dsa/mv88e6xxx/port.c
-@@ -424,6 +424,10 @@ int mv88e6393x_port_set_speed_duplex(str
- u16 reg, ctrl;
- int err;
-
-+ if (chip->info->prod_num == MV88E6XXX_PORT_SWITCH_ID_PROD_6361 &&
-+ speed > 2500)
-+ return -EOPNOTSUPP;
-+
- if (speed == 200 && port != 0)
- return -EOPNOTSUPP;
-
-@@ -506,10 +510,14 @@ int mv88e6393x_port_set_speed_duplex(str
- phy_interface_t mv88e6393x_port_max_speed_mode(struct mv88e6xxx_chip *chip,
- int port)
- {
-- if (port == 0 || port == 9 || port == 10)
-- return PHY_INTERFACE_MODE_10GBASER;
-
-- return PHY_INTERFACE_MODE_NA;
-+ if (port != 0 && port != 9 && port != 10)
-+ return PHY_INTERFACE_MODE_NA;
-+
-+ if (chip->info->prod_num == MV88E6XXX_PORT_SWITCH_ID_PROD_6361)
-+ return PHY_INTERFACE_MODE_2500BASEX;
-+
-+ return PHY_INTERFACE_MODE_10GBASER;
- }
-
- static int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
---- a/drivers/net/dsa/mv88e6xxx/port.h
-+++ b/drivers/net/dsa/mv88e6xxx/port.h
-@@ -133,6 +133,7 @@
- #define MV88E6XXX_PORT_SWITCH_ID_PROD_6220 0x2200
- #define MV88E6XXX_PORT_SWITCH_ID_PROD_6240 0x2400
- #define MV88E6XXX_PORT_SWITCH_ID_PROD_6250 0x2500
-+#define MV88E6XXX_PORT_SWITCH_ID_PROD_6361 0x2610
- #define MV88E6XXX_PORT_SWITCH_ID_PROD_6290 0x2900
- #define MV88E6XXX_PORT_SWITCH_ID_PROD_6321 0x3100
- #define MV88E6XXX_PORT_SWITCH_ID_PROD_6141 0x3400
+++ /dev/null
-From fbfc4ca465a1f8d81bf2d67d95bf7fc67c3cf0c2 Mon Sep 17 00:00:00 2001
-From: Patrick Delaunay <patrick.delaunay@foss.st.com>
-Date: Fri, 18 Nov 2022 06:39:20 +0000
-Subject: [PATCH] nvmem: stm32: move STM32MP15_BSEC_NUM_LOWER in config
-
-Support STM32MP15_BSEC_NUM_LOWER in stm32 romem config to prepare
-the next SoC in STM32MP family.
-
-Signed-off-by: Patrick Delaunay <patrick.delaunay@foss.st.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20221118063932.6418-2-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/stm32-romem.c | 21 ++++++++++++++++-----
- 1 file changed, 16 insertions(+), 5 deletions(-)
-
---- a/drivers/nvmem/stm32-romem.c
-+++ b/drivers/nvmem/stm32-romem.c
-@@ -22,16 +22,15 @@
- /* shadow registers offest */
- #define STM32MP15_BSEC_DATA0 0x200
-
--/* 32 (x 32-bits) lower shadow registers */
--#define STM32MP15_BSEC_NUM_LOWER 32
--
- struct stm32_romem_cfg {
- int size;
-+ u8 lower;
- };
-
- struct stm32_romem_priv {
- void __iomem *base;
- struct nvmem_config cfg;
-+ u8 lower;
- };
-
- static int stm32_romem_read(void *context, unsigned int offset, void *buf,
-@@ -85,7 +84,7 @@ static int stm32_bsec_read(void *context
- for (i = roffset; (i < roffset + rbytes); i += 4) {
- u32 otp = i >> 2;
-
-- if (otp < STM32MP15_BSEC_NUM_LOWER) {
-+ if (otp < priv->lower) {
- /* read lower data from shadow registers */
- val = readl_relaxed(
- priv->base + STM32MP15_BSEC_DATA0 + i);
-@@ -159,6 +158,8 @@ static int stm32_romem_probe(struct plat
- priv->cfg.priv = priv;
- priv->cfg.owner = THIS_MODULE;
-
-+ priv->lower = 0;
-+
- cfg = (const struct stm32_romem_cfg *)
- of_match_device(dev->driver->of_match_table, dev)->data;
- if (!cfg) {
-@@ -167,6 +168,7 @@ static int stm32_romem_probe(struct plat
- priv->cfg.reg_read = stm32_romem_read;
- } else {
- priv->cfg.size = cfg->size;
-+ priv->lower = cfg->lower;
- priv->cfg.reg_read = stm32_bsec_read;
- priv->cfg.reg_write = stm32_bsec_write;
- }
-@@ -174,8 +176,17 @@ static int stm32_romem_probe(struct plat
- return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &priv->cfg));
- }
-
-+/*
-+ * STM32MP15 BSEC OTP regions: 4096 OTP bits (with 3072 effective bits)
-+ * => 96 x 32-bits data words
-+ * - Lower: 1K bits, 2:1 redundancy, incremental bit programming
-+ * => 32 (x 32-bits) lower shadow registers = words 0 to 31
-+ * - Upper: 2K bits, ECC protection, word programming only
-+ * => 64 (x 32-bits) = words 32 to 95
-+ */
- static const struct stm32_romem_cfg stm32mp15_bsec_cfg = {
-- .size = 384, /* 96 x 32-bits data words */
-+ .size = 384,
-+ .lower = 32,
- };
-
- static const struct of_device_id stm32_romem_of_match[] = {
+++ /dev/null
-From d61784e6410f3df2028e6eb91b06ffed37a660e0 Mon Sep 17 00:00:00 2001
-From: Patrick Delaunay <patrick.delaunay@foss.st.com>
-Date: Fri, 18 Nov 2022 06:39:21 +0000
-Subject: [PATCH] nvmem: stm32: add warning when upper OTPs are updated
-
-As the upper OTPs are ECC protected, they support only one 32 bits word
-programming.
-For a second modification of this word, these ECC become invalid and
-this OTP will be no more accessible, the shadowed value is invalid.
-
-This patch adds a warning to indicate an upper OTP update, because this
-operation is dangerous as OTP is not locked by the driver after the first
-update to avoid a second update.
-
-Signed-off-by: Patrick Delaunay <patrick.delaunay@foss.st.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20221118063932.6418-3-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/stm32-romem.c | 3 +++
- 1 file changed, 3 insertions(+)
-
---- a/drivers/nvmem/stm32-romem.c
-+++ b/drivers/nvmem/stm32-romem.c
-@@ -132,6 +132,9 @@ static int stm32_bsec_write(void *contex
- }
- }
-
-+ if (offset + bytes >= priv->lower * 4)
-+ dev_warn(dev, "Update of upper OTPs with ECC protection (word programming, only once)\n");
-+
- return 0;
- }
-
+++ /dev/null
-From a3816a7d7c097c1da46aad5f5d1e229b607dce04 Mon Sep 17 00:00:00 2001
-From: Patrick Delaunay <patrick.delaunay@foss.st.com>
-Date: Fri, 18 Nov 2022 06:39:22 +0000
-Subject: [PATCH] nvmem: stm32: add nvmem type attribute
-
-Inform NVMEM framework of type attribute for stm32-romem as NVMEM_TYPE_OTP
-so userspace is able to know how the data is stored in BSEC.
-
-Signed-off-by: Patrick Delaunay <patrick.delaunay@foss.st.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20221118063932.6418-4-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/stm32-romem.c | 1 +
- 1 file changed, 1 insertion(+)
-
---- a/drivers/nvmem/stm32-romem.c
-+++ b/drivers/nvmem/stm32-romem.c
-@@ -160,6 +160,7 @@ static int stm32_romem_probe(struct plat
- priv->cfg.dev = dev;
- priv->cfg.priv = priv;
- priv->cfg.owner = THIS_MODULE;
-+ priv->cfg.type = NVMEM_TYPE_OTP;
-
- priv->lower = 0;
-
+++ /dev/null
-From 06aac0e11960a7ddccc1888326b5906d017e0f24 Mon Sep 17 00:00:00 2001
-From: Jiangshan Yi <yijiangshan@kylinos.cn>
-Date: Fri, 18 Nov 2022 06:39:24 +0000
-Subject: [PATCH] nvmem: stm32: fix spelling typo in comment
-
-Fix spelling typo in comment.
-
-Reported-by: k2ci <kernel-bot@kylinos.cn>
-Signed-off-by: Jiangshan Yi <yijiangshan@kylinos.cn>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20221118063932.6418-6-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/stm32-romem.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/nvmem/stm32-romem.c
-+++ b/drivers/nvmem/stm32-romem.c
-@@ -19,7 +19,7 @@
- #define STM32_SMC_WRITE_SHADOW 0x03
- #define STM32_SMC_READ_OTP 0x04
-
--/* shadow registers offest */
-+/* shadow registers offset */
- #define STM32MP15_BSEC_DATA0 0x200
-
- struct stm32_romem_cfg {
+++ /dev/null
-From fb817c4ef63e8cfb6e77ae4a2875ae854c80708f Mon Sep 17 00:00:00 2001
-From: Colin Ian King <colin.i.king@gmail.com>
-Date: Fri, 18 Nov 2022 06:39:26 +0000
-Subject: [PATCH] nvmem: Kconfig: Fix spelling mistake "controlls" ->
- "controls"
-
-There is a spelling mistake in a Kconfig description. Fix it.
-
-Signed-off-by: Colin Ian King <colin.i.king@gmail.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20221118063932.6418-8-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/Kconfig | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/nvmem/Kconfig
-+++ b/drivers/nvmem/Kconfig
-@@ -164,7 +164,7 @@ config NVMEM_MICROCHIP_OTPC
- depends on ARCH_AT91 || COMPILE_TEST
- help
- This driver enable the OTP controller available on Microchip SAMA7G5
-- SoCs. It controlls the access to the OTP memory connected to it.
-+ SoCs. It controls the access to the OTP memory connected to it.
-
- config NVMEM_MTK_EFUSE
- tristate "Mediatek SoCs EFUSE support"
+++ /dev/null
-From ada84d07af6097b2addd18262668ce6cb9e15206 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
-Date: Fri, 18 Nov 2022 06:39:27 +0000
-Subject: [PATCH] nvmem: u-boot-env: add Broadcom format support
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Broadcom uses U-Boot for a lot of their bcmbca familiy chipsets. They
-decided to store U-Boot environment data inside U-Boot partition and to
-use a custom header (with "uEnv" magic and env data length).
-
-Add support for Broadcom's specific binding and their custom format.
-
-Ref: 6b0584c19d87 ("dt-bindings: nvmem: u-boot,env: add Broadcom's variant binding")
-Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20221118063932.6418-9-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/u-boot-env.c | 14 ++++++++++++++
- 1 file changed, 14 insertions(+)
-
---- a/drivers/nvmem/u-boot-env.c
-+++ b/drivers/nvmem/u-boot-env.c
-@@ -16,6 +16,7 @@
- enum u_boot_env_format {
- U_BOOT_FORMAT_SINGLE,
- U_BOOT_FORMAT_REDUNDANT,
-+ U_BOOT_FORMAT_BROADCOM,
- };
-
- struct u_boot_env {
-@@ -40,6 +41,13 @@ struct u_boot_env_image_redundant {
- uint8_t data[];
- } __packed;
-
-+struct u_boot_env_image_broadcom {
-+ __le32 magic;
-+ __le32 len;
-+ __le32 crc32;
-+ uint8_t data[0];
-+} __packed;
-+
- static int u_boot_env_read(void *context, unsigned int offset, void *val,
- size_t bytes)
- {
-@@ -138,6 +146,11 @@ static int u_boot_env_parse(struct u_boo
- crc32_data_offset = offsetof(struct u_boot_env_image_redundant, data);
- data_offset = offsetof(struct u_boot_env_image_redundant, data);
- break;
-+ case U_BOOT_FORMAT_BROADCOM:
-+ crc32_offset = offsetof(struct u_boot_env_image_broadcom, crc32);
-+ crc32_data_offset = offsetof(struct u_boot_env_image_broadcom, data);
-+ data_offset = offsetof(struct u_boot_env_image_broadcom, data);
-+ break;
- }
- crc32 = le32_to_cpu(*(__le32 *)(buf + crc32_offset));
- crc32_data_len = priv->mtd->size - crc32_data_offset;
-@@ -202,6 +215,7 @@ static const struct of_device_id u_boot_
- { .compatible = "u-boot,env", .data = (void *)U_BOOT_FORMAT_SINGLE, },
- { .compatible = "u-boot,env-redundant-bool", .data = (void *)U_BOOT_FORMAT_REDUNDANT, },
- { .compatible = "u-boot,env-redundant-count", .data = (void *)U_BOOT_FORMAT_REDUNDANT, },
-+ { .compatible = "brcm,env", .data = (void *)U_BOOT_FORMAT_BROADCOM, },
- {},
- };
-
+++ /dev/null
-From 2e8dc541ae207349b51c65391be625ffe1f86e0c Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Mon, 6 Feb 2023 13:43:41 +0000
-Subject: [PATCH] nvmem: core: remove spurious white space
-
-Remove a spurious white space in for the ida_alloc() call.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230206134356.839737-8-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/core.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -764,7 +764,7 @@ struct nvmem_device *nvmem_register(cons
- if (!nvmem)
- return ERR_PTR(-ENOMEM);
-
-- rval = ida_alloc(&nvmem_ida, GFP_KERNEL);
-+ rval = ida_alloc(&nvmem_ida, GFP_KERNEL);
- if (rval < 0) {
- kfree(nvmem);
- return ERR_PTR(rval);
+++ /dev/null
-From 5d8e6e6c10a3d37486d263b16ddc15991a7e4a88 Mon Sep 17 00:00:00 2001
-From: Michael Walle <michael@walle.cc>
-Date: Mon, 6 Feb 2023 13:43:46 +0000
-Subject: [PATCH] nvmem: core: add an index parameter to the cell
-
-Sometimes a cell can represend multiple values. For example, a base
-ethernet address stored in the NVMEM can be expanded into multiple
-discreet ones by adding an offset.
-
-For this use case, introduce an index parameter which is then used to
-distiguish between values. This parameter will then be passed to the
-post process hook which can then use it to create different values
-during reading.
-
-At the moment, there is only support for the device tree path. You can
-add the index to the phandle, e.g.
-
- &net {
- nvmem-cells = <&base_mac_address 2>;
- nvmem-cell-names = "mac-address";
- };
-
- &nvmem_provider {
- base_mac_address: base-mac-address@0 {
- #nvmem-cell-cells = <1>;
- reg = <0 6>;
- };
- };
-
-Signed-off-by: Michael Walle <michael@walle.cc>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230206134356.839737-13-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/core.c | 37 ++++++++++++++++++++++++----------
- drivers/nvmem/imx-ocotp.c | 4 ++--
- include/linux/nvmem-provider.h | 4 ++--
- 3 files changed, 30 insertions(+), 15 deletions(-)
-
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -60,6 +60,7 @@ struct nvmem_cell_entry {
- struct nvmem_cell {
- struct nvmem_cell_entry *entry;
- const char *id;
-+ int index;
- };
-
- static DEFINE_MUTEX(nvmem_mutex);
-@@ -1122,7 +1123,8 @@ struct nvmem_device *devm_nvmem_device_g
- }
- EXPORT_SYMBOL_GPL(devm_nvmem_device_get);
-
--static struct nvmem_cell *nvmem_create_cell(struct nvmem_cell_entry *entry, const char *id)
-+static struct nvmem_cell *nvmem_create_cell(struct nvmem_cell_entry *entry,
-+ const char *id, int index)
- {
- struct nvmem_cell *cell;
- const char *name = NULL;
-@@ -1141,6 +1143,7 @@ static struct nvmem_cell *nvmem_create_c
-
- cell->id = name;
- cell->entry = entry;
-+ cell->index = index;
-
- return cell;
- }
-@@ -1179,7 +1182,7 @@ nvmem_cell_get_from_lookup(struct device
- __nvmem_device_put(nvmem);
- cell = ERR_PTR(-ENOENT);
- } else {
-- cell = nvmem_create_cell(cell_entry, con_id);
-+ cell = nvmem_create_cell(cell_entry, con_id, 0);
- if (IS_ERR(cell))
- __nvmem_device_put(nvmem);
- }
-@@ -1227,15 +1230,27 @@ struct nvmem_cell *of_nvmem_cell_get(str
- struct nvmem_device *nvmem;
- struct nvmem_cell_entry *cell_entry;
- struct nvmem_cell *cell;
-+ struct of_phandle_args cell_spec;
- int index = 0;
-+ int cell_index = 0;
-+ int ret;
-
- /* if cell name exists, find index to the name */
- if (id)
- index = of_property_match_string(np, "nvmem-cell-names", id);
-
-- cell_np = of_parse_phandle(np, "nvmem-cells", index);
-- if (!cell_np)
-- return ERR_PTR(-ENOENT);
-+ ret = of_parse_phandle_with_optional_args(np, "nvmem-cells",
-+ "#nvmem-cell-cells",
-+ index, &cell_spec);
-+ if (ret)
-+ return ERR_PTR(ret);
-+
-+ if (cell_spec.args_count > 1)
-+ return ERR_PTR(-EINVAL);
-+
-+ cell_np = cell_spec.np;
-+ if (cell_spec.args_count)
-+ cell_index = cell_spec.args[0];
-
- nvmem_np = of_get_parent(cell_np);
- if (!nvmem_np) {
-@@ -1257,7 +1272,7 @@ struct nvmem_cell *of_nvmem_cell_get(str
- return ERR_PTR(-ENOENT);
- }
-
-- cell = nvmem_create_cell(cell_entry, id);
-+ cell = nvmem_create_cell(cell_entry, id, cell_index);
- if (IS_ERR(cell))
- __nvmem_device_put(nvmem);
-
-@@ -1410,8 +1425,8 @@ static void nvmem_shift_read_buffer_in_p
- }
-
- static int __nvmem_cell_read(struct nvmem_device *nvmem,
-- struct nvmem_cell_entry *cell,
-- void *buf, size_t *len, const char *id)
-+ struct nvmem_cell_entry *cell,
-+ void *buf, size_t *len, const char *id, int index)
- {
- int rc;
-
-@@ -1425,7 +1440,7 @@ static int __nvmem_cell_read(struct nvme
- nvmem_shift_read_buffer_in_place(cell, buf);
-
- if (nvmem->cell_post_process) {
-- rc = nvmem->cell_post_process(nvmem->priv, id,
-+ rc = nvmem->cell_post_process(nvmem->priv, id, index,
- cell->offset, buf, cell->bytes);
- if (rc)
- return rc;
-@@ -1460,7 +1475,7 @@ void *nvmem_cell_read(struct nvmem_cell
- if (!buf)
- return ERR_PTR(-ENOMEM);
-
-- rc = __nvmem_cell_read(nvmem, cell->entry, buf, len, cell->id);
-+ rc = __nvmem_cell_read(nvmem, cell->entry, buf, len, cell->id, cell->index);
- if (rc) {
- kfree(buf);
- return ERR_PTR(rc);
-@@ -1773,7 +1788,7 @@ ssize_t nvmem_device_cell_read(struct nv
- if (rc)
- return rc;
-
-- rc = __nvmem_cell_read(nvmem, &cell, buf, &len, NULL);
-+ rc = __nvmem_cell_read(nvmem, &cell, buf, &len, NULL, 0);
- if (rc)
- return rc;
-
---- a/drivers/nvmem/imx-ocotp.c
-+++ b/drivers/nvmem/imx-ocotp.c
-@@ -222,8 +222,8 @@ read_end:
- return ret;
- }
-
--static int imx_ocotp_cell_pp(void *context, const char *id, unsigned int offset,
-- void *data, size_t bytes)
-+static int imx_ocotp_cell_pp(void *context, const char *id, int index,
-+ unsigned int offset, void *data, size_t bytes)
- {
- struct ocotp_priv *priv = context;
-
---- a/include/linux/nvmem-provider.h
-+++ b/include/linux/nvmem-provider.h
-@@ -20,8 +20,8 @@ typedef int (*nvmem_reg_read_t)(void *pr
- typedef int (*nvmem_reg_write_t)(void *priv, unsigned int offset,
- void *val, size_t bytes);
- /* used for vendor specific post processing of cell data */
--typedef int (*nvmem_cell_post_process_t)(void *priv, const char *id, unsigned int offset,
-- void *buf, size_t bytes);
-+typedef int (*nvmem_cell_post_process_t)(void *priv, const char *id, int index,
-+ unsigned int offset, void *buf, size_t bytes);
-
- enum nvmem_type {
- NVMEM_TYPE_UNKNOWN = 0,
+++ /dev/null
-From fbd03d27776c6121a483921601418e3c8f0ff37e Mon Sep 17 00:00:00 2001
-From: Michael Walle <michael@walle.cc>
-Date: Mon, 6 Feb 2023 13:43:47 +0000
-Subject: [PATCH] nvmem: core: move struct nvmem_cell_info to nvmem-provider.h
-
-struct nvmem_cell_info is used to describe a cell. Thus this should
-really be in the nvmem-provider's header. There are two (unused) nvmem
-access methods which use the nvmem_cell_info to describe the cell to be
-accesses. One can argue, that they will create a cell before accessing,
-thus they are both a provider and a consumer.
-
-struct nvmem_cell_info will get used more and more by nvmem-providers,
-don't force them to also include the consumer header, although they are
-not.
-
-Signed-off-by: Michael Walle <michael@walle.cc>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230206134356.839737-14-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- include/linux/nvmem-consumer.h | 10 +---------
- include/linux/nvmem-provider.h | 19 ++++++++++++++++++-
- 2 files changed, 19 insertions(+), 10 deletions(-)
-
---- a/include/linux/nvmem-consumer.h
-+++ b/include/linux/nvmem-consumer.h
-@@ -18,15 +18,7 @@ struct device_node;
- /* consumer cookie */
- struct nvmem_cell;
- struct nvmem_device;
--
--struct nvmem_cell_info {
-- const char *name;
-- unsigned int offset;
-- unsigned int bytes;
-- unsigned int bit_offset;
-- unsigned int nbits;
-- struct device_node *np;
--};
-+struct nvmem_cell_info;
-
- /**
- * struct nvmem_cell_lookup - cell lookup entry
---- a/include/linux/nvmem-provider.h
-+++ b/include/linux/nvmem-provider.h
-@@ -14,7 +14,6 @@
- #include <linux/gpio/consumer.h>
-
- struct nvmem_device;
--struct nvmem_cell_info;
- typedef int (*nvmem_reg_read_t)(void *priv, unsigned int offset,
- void *val, size_t bytes);
- typedef int (*nvmem_reg_write_t)(void *priv, unsigned int offset,
-@@ -48,6 +47,24 @@ struct nvmem_keepout {
- };
-
- /**
-+ * struct nvmem_cell_info - NVMEM cell description
-+ * @name: Name.
-+ * @offset: Offset within the NVMEM device.
-+ * @bytes: Length of the cell.
-+ * @bit_offset: Bit offset if cell is smaller than a byte.
-+ * @nbits: Number of bits.
-+ * @np: Optional device_node pointer.
-+ */
-+struct nvmem_cell_info {
-+ const char *name;
-+ unsigned int offset;
-+ unsigned int bytes;
-+ unsigned int bit_offset;
-+ unsigned int nbits;
-+ struct device_node *np;
-+};
-+
-+/**
- * struct nvmem_config - NVMEM device configuration
- *
- * @dev: Parent device.
+++ /dev/null
-From cc5bdd323dde6494623f3ffe3a5b887fa21cd375 Mon Sep 17 00:00:00 2001
-From: Michael Walle <michael@walle.cc>
-Date: Mon, 6 Feb 2023 13:43:48 +0000
-Subject: [PATCH] nvmem: core: drop the removal of the cells in
- nvmem_add_cells()
-
-If nvmem_add_cells() fails, the whole nvmem_register() will fail
-and the cells will then be removed anyway. This is a preparation
-to introduce a nvmem_add_one_cell() which can then be used by
-nvmem_add_cells().
-
-This is then the same to what nvmem_add_cells_from_table() and
-nvmem_add_cells_from_of() do.
-
-Signed-off-by: Michael Walle <michael@walle.cc>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230206134356.839737-15-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/core.c | 14 ++++----------
- 1 file changed, 4 insertions(+), 10 deletions(-)
-
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -515,7 +515,7 @@ static int nvmem_add_cells(struct nvmem_
- int ncells)
- {
- struct nvmem_cell_entry **cells;
-- int i, rval;
-+ int i, rval = 0;
-
- cells = kcalloc(ncells, sizeof(*cells), GFP_KERNEL);
- if (!cells)
-@@ -525,28 +525,22 @@ static int nvmem_add_cells(struct nvmem_
- cells[i] = kzalloc(sizeof(**cells), GFP_KERNEL);
- if (!cells[i]) {
- rval = -ENOMEM;
-- goto err;
-+ goto out;
- }
-
- rval = nvmem_cell_info_to_nvmem_cell_entry(nvmem, &info[i], cells[i]);
- if (rval) {
- kfree(cells[i]);
-- goto err;
-+ goto out;
- }
-
- nvmem_cell_entry_add(cells[i]);
- }
-
-+out:
- /* remove tmp array */
- kfree(cells);
-
-- return 0;
--err:
-- while (i--)
-- nvmem_cell_entry_drop(cells[i]);
--
-- kfree(cells);
--
- return rval;
- }
-
+++ /dev/null
-From 2ded6830d376d5e7bf43d59f7f7fdf1a59abc676 Mon Sep 17 00:00:00 2001
-From: Michael Walle <michael@walle.cc>
-Date: Mon, 6 Feb 2023 13:43:49 +0000
-Subject: [PATCH] nvmem: core: add nvmem_add_one_cell()
-
-Add a new function to add exactly one cell. This will be used by the
-nvmem layout drivers to add custom cells. In contrast to the
-nvmem_add_cells(), this has the advantage that we don't have to assemble
-a list of cells on runtime.
-
-Signed-off-by: Michael Walle <michael@walle.cc>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230206134356.839737-16-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/core.c | 59 ++++++++++++++++++++--------------
- include/linux/nvmem-provider.h | 8 +++++
- 2 files changed, 43 insertions(+), 24 deletions(-)
-
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -502,6 +502,36 @@ static int nvmem_cell_info_to_nvmem_cell
- }
-
- /**
-+ * nvmem_add_one_cell() - Add one cell information to an nvmem device
-+ *
-+ * @nvmem: nvmem device to add cells to.
-+ * @info: nvmem cell info to add to the device
-+ *
-+ * Return: 0 or negative error code on failure.
-+ */
-+int nvmem_add_one_cell(struct nvmem_device *nvmem,
-+ const struct nvmem_cell_info *info)
-+{
-+ struct nvmem_cell_entry *cell;
-+ int rval;
-+
-+ cell = kzalloc(sizeof(*cell), GFP_KERNEL);
-+ if (!cell)
-+ return -ENOMEM;
-+
-+ rval = nvmem_cell_info_to_nvmem_cell_entry(nvmem, info, cell);
-+ if (rval) {
-+ kfree(cell);
-+ return rval;
-+ }
-+
-+ nvmem_cell_entry_add(cell);
-+
-+ return 0;
-+}
-+EXPORT_SYMBOL_GPL(nvmem_add_one_cell);
-+
-+/**
- * nvmem_add_cells() - Add cell information to an nvmem device
- *
- * @nvmem: nvmem device to add cells to.
-@@ -514,34 +544,15 @@ static int nvmem_add_cells(struct nvmem_
- const struct nvmem_cell_info *info,
- int ncells)
- {
-- struct nvmem_cell_entry **cells;
-- int i, rval = 0;
--
-- cells = kcalloc(ncells, sizeof(*cells), GFP_KERNEL);
-- if (!cells)
-- return -ENOMEM;
-+ int i, rval;
-
- for (i = 0; i < ncells; i++) {
-- cells[i] = kzalloc(sizeof(**cells), GFP_KERNEL);
-- if (!cells[i]) {
-- rval = -ENOMEM;
-- goto out;
-- }
--
-- rval = nvmem_cell_info_to_nvmem_cell_entry(nvmem, &info[i], cells[i]);
-- if (rval) {
-- kfree(cells[i]);
-- goto out;
-- }
--
-- nvmem_cell_entry_add(cells[i]);
-+ rval = nvmem_add_one_cell(nvmem, &info[i]);
-+ if (rval)
-+ return rval;
- }
-
--out:
-- /* remove tmp array */
-- kfree(cells);
--
-- return rval;
-+ return 0;
- }
-
- /**
---- a/include/linux/nvmem-provider.h
-+++ b/include/linux/nvmem-provider.h
-@@ -153,6 +153,9 @@ struct nvmem_device *devm_nvmem_register
- void nvmem_add_cell_table(struct nvmem_cell_table *table);
- void nvmem_del_cell_table(struct nvmem_cell_table *table);
-
-+int nvmem_add_one_cell(struct nvmem_device *nvmem,
-+ const struct nvmem_cell_info *info);
-+
- #else
-
- static inline struct nvmem_device *nvmem_register(const struct nvmem_config *c)
-@@ -170,6 +173,11 @@ devm_nvmem_register(struct device *dev,
-
- static inline void nvmem_add_cell_table(struct nvmem_cell_table *table) {}
- static inline void nvmem_del_cell_table(struct nvmem_cell_table *table) {}
-+static inline int nvmem_add_one_cell(struct nvmem_device *nvmem,
-+ const struct nvmem_cell_info *info)
-+{
-+ return -EOPNOTSUPP;
-+}
-
- #endif /* CONFIG_NVMEM */
- #endif /* ifndef _LINUX_NVMEM_PROVIDER_H */
+++ /dev/null
-From 50014d659617dc58780a5d31ceb76c82779a9d8b Mon Sep 17 00:00:00 2001
-From: Michael Walle <michael@walle.cc>
-Date: Mon, 6 Feb 2023 13:43:50 +0000
-Subject: [PATCH] nvmem: core: use nvmem_add_one_cell() in
- nvmem_add_cells_from_of()
-
-Convert nvmem_add_cells_from_of() to use the new nvmem_add_one_cell().
-This will remove duplicate code and it will make it possible to add a
-hook to a nvmem layout in between, which can change fields before the
-cell is finally added.
-
-Signed-off-by: Michael Walle <michael@walle.cc>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230206134356.839737-17-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/core.c | 45 ++++++++++++++------------------------------
- 1 file changed, 14 insertions(+), 31 deletions(-)
-
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -688,15 +688,14 @@ static int nvmem_validate_keepouts(struc
-
- static int nvmem_add_cells_from_of(struct nvmem_device *nvmem)
- {
-- struct device_node *parent, *child;
- struct device *dev = &nvmem->dev;
-- struct nvmem_cell_entry *cell;
-+ struct device_node *child;
- const __be32 *addr;
-- int len;
-+ int len, ret;
-
-- parent = dev->of_node;
-+ for_each_child_of_node(dev->of_node, child) {
-+ struct nvmem_cell_info info = {0};
-
-- for_each_child_of_node(parent, child) {
- addr = of_get_property(child, "reg", &len);
- if (!addr)
- continue;
-@@ -706,40 +705,24 @@ static int nvmem_add_cells_from_of(struc
- return -EINVAL;
- }
-
-- cell = kzalloc(sizeof(*cell), GFP_KERNEL);
-- if (!cell) {
-- of_node_put(child);
-- return -ENOMEM;
-- }
--
-- cell->nvmem = nvmem;
-- cell->offset = be32_to_cpup(addr++);
-- cell->bytes = be32_to_cpup(addr);
-- cell->name = kasprintf(GFP_KERNEL, "%pOFn", child);
-+ info.offset = be32_to_cpup(addr++);
-+ info.bytes = be32_to_cpup(addr);
-+ info.name = kasprintf(GFP_KERNEL, "%pOFn", child);
-
- addr = of_get_property(child, "bits", &len);
- if (addr && len == (2 * sizeof(u32))) {
-- cell->bit_offset = be32_to_cpup(addr++);
-- cell->nbits = be32_to_cpup(addr);
-+ info.bit_offset = be32_to_cpup(addr++);
-+ info.nbits = be32_to_cpup(addr);
- }
-
-- if (cell->nbits)
-- cell->bytes = DIV_ROUND_UP(
-- cell->nbits + cell->bit_offset,
-- BITS_PER_BYTE);
--
-- if (!IS_ALIGNED(cell->offset, nvmem->stride)) {
-- dev_err(dev, "cell %s unaligned to nvmem stride %d\n",
-- cell->name, nvmem->stride);
-- /* Cells already added will be freed later. */
-- kfree_const(cell->name);
-- kfree(cell);
-+ info.np = of_node_get(child);
-+
-+ ret = nvmem_add_one_cell(nvmem, &info);
-+ kfree(info.name);
-+ if (ret) {
- of_node_put(child);
-- return -EINVAL;
-+ return ret;
- }
--
-- cell->np = of_node_get(child);
-- nvmem_cell_entry_add(cell);
- }
-
- return 0;
+++ /dev/null
-From 6a0bc3522e746025e2d9a63ab2cb5d7062c2d39c Mon Sep 17 00:00:00 2001
-From: Patrick Delaunay <patrick.delaunay@foss.st.com>
-Date: Mon, 6 Feb 2023 13:43:51 +0000
-Subject: [PATCH] nvmem: stm32: add OP-TEE support for STM32MP13x
-
-For boot with OP-TEE on STM32MP13, the communication with the secure
-world no more use STMicroelectronics SMC but communication with the
-STM32MP BSEC TA, for data access (read/write) or lock operation:
-- all the request are sent to OP-TEE trusted application,
-- for upper OTP with ECC protection and with word programming only
- each OTP are permanently locked when programmed to avoid ECC error
- on the second write operation
-
-Signed-off-by: Patrick Delaunay <patrick.delaunay@foss.st.com>
-Reviewed-by: Etienne Carriere <etienne.carriere@linaro.org>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230206134356.839737-18-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/Kconfig | 11 +
- drivers/nvmem/Makefile | 1 +
- drivers/nvmem/stm32-bsec-optee-ta.c | 298 ++++++++++++++++++++++++++++
- drivers/nvmem/stm32-bsec-optee-ta.h | 80 ++++++++
- drivers/nvmem/stm32-romem.c | 54 ++++-
- 5 files changed, 441 insertions(+), 3 deletions(-)
- create mode 100644 drivers/nvmem/stm32-bsec-optee-ta.c
- create mode 100644 drivers/nvmem/stm32-bsec-optee-ta.h
-
---- a/drivers/nvmem/Kconfig
-+++ b/drivers/nvmem/Kconfig
-@@ -290,9 +290,20 @@ config NVMEM_SPRD_EFUSE
- This driver can also be built as a module. If so, the module
- will be called nvmem-sprd-efuse.
-
-+config NVMEM_STM32_BSEC_OPTEE_TA
-+ bool "STM32MP BSEC OP-TEE TA support for nvmem-stm32-romem driver"
-+ depends on OPTEE
-+ help
-+ Say y here to enable the accesses to STM32MP SoC OTPs by the OP-TEE
-+ trusted application STM32MP BSEC.
-+
-+ This library is a used by stm32-romem driver or included in the module
-+ called nvmem-stm32-romem.
-+
- config NVMEM_STM32_ROMEM
- tristate "STMicroelectronics STM32 factory-programmed memory support"
- depends on ARCH_STM32 || COMPILE_TEST
-+ imply NVMEM_STM32_BSEC_OPTEE_TA
- help
- Say y here to enable read-only access for STMicroelectronics STM32
- factory-programmed memory area.
---- a/drivers/nvmem/Makefile
-+++ b/drivers/nvmem/Makefile
-@@ -61,6 +61,7 @@ obj-$(CONFIG_NVMEM_SPRD_EFUSE) += nvmem
- nvmem_sprd_efuse-y := sprd-efuse.o
- obj-$(CONFIG_NVMEM_STM32_ROMEM) += nvmem_stm32_romem.o
- nvmem_stm32_romem-y := stm32-romem.o
-+nvmem_stm32_romem-$(CONFIG_NVMEM_STM32_BSEC_OPTEE_TA) += stm32-bsec-optee-ta.o
- obj-$(CONFIG_NVMEM_SUNPLUS_OCOTP) += nvmem_sunplus_ocotp.o
- nvmem_sunplus_ocotp-y := sunplus-ocotp.o
- obj-$(CONFIG_NVMEM_SUNXI_SID) += nvmem_sunxi_sid.o
---- /dev/null
-+++ b/drivers/nvmem/stm32-bsec-optee-ta.c
-@@ -0,0 +1,298 @@
-+// SPDX-License-Identifier: GPL-2.0-or-later
-+/*
-+ * OP-TEE STM32MP BSEC PTA interface, used by STM32 ROMEM driver
-+ *
-+ * Copyright (C) 2022, STMicroelectronics - All Rights Reserved
-+ */
-+
-+#include <linux/tee_drv.h>
-+
-+#include "stm32-bsec-optee-ta.h"
-+
-+/*
-+ * Read OTP memory
-+ *
-+ * [in] value[0].a OTP start offset in byte
-+ * [in] value[0].b Access type (0:shadow, 1:fuse, 2:lock)
-+ * [out] memref[1].buffer Output buffer to store read values
-+ * [out] memref[1].size Size of OTP to be read
-+ *
-+ * Return codes:
-+ * TEE_SUCCESS - Invoke command success
-+ * TEE_ERROR_BAD_PARAMETERS - Incorrect input param
-+ * TEE_ERROR_ACCESS_DENIED - OTP not accessible by caller
-+ */
-+#define PTA_BSEC_READ_MEM 0x0
-+
-+/*
-+ * Write OTP memory
-+ *
-+ * [in] value[0].a OTP start offset in byte
-+ * [in] value[0].b Access type (0:shadow, 1:fuse, 2:lock)
-+ * [in] memref[1].buffer Input buffer to read values
-+ * [in] memref[1].size Size of OTP to be written
-+ *
-+ * Return codes:
-+ * TEE_SUCCESS - Invoke command success
-+ * TEE_ERROR_BAD_PARAMETERS - Incorrect input param
-+ * TEE_ERROR_ACCESS_DENIED - OTP not accessible by caller
-+ */
-+#define PTA_BSEC_WRITE_MEM 0x1
-+
-+/* value of PTA_BSEC access type = value[in] b */
-+#define SHADOW_ACCESS 0
-+#define FUSE_ACCESS 1
-+#define LOCK_ACCESS 2
-+
-+/* Bitfield definition for LOCK status */
-+#define LOCK_PERM BIT(30)
-+
-+/* OP-TEE STM32MP BSEC TA UUID */
-+static const uuid_t stm32mp_bsec_ta_uuid =
-+ UUID_INIT(0x94cf71ad, 0x80e6, 0x40b5,
-+ 0xa7, 0xc6, 0x3d, 0xc5, 0x01, 0xeb, 0x28, 0x03);
-+
-+/*
-+ * Check whether this driver supports the BSEC TA in the TEE instance
-+ * represented by the params (ver/data) to this function.
-+ */
-+static int stm32_bsec_optee_ta_match(struct tee_ioctl_version_data *ver,
-+ const void *data)
-+{
-+ /* Currently this driver only supports GP compliant, OP-TEE based TA */
-+ if ((ver->impl_id == TEE_IMPL_ID_OPTEE) &&
-+ (ver->gen_caps & TEE_GEN_CAP_GP))
-+ return 1;
-+ else
-+ return 0;
-+}
-+
-+/* Open a session to OP-TEE for STM32MP BSEC TA */
-+static int stm32_bsec_ta_open_session(struct tee_context *ctx, u32 *id)
-+{
-+ struct tee_ioctl_open_session_arg sess_arg;
-+ int rc;
-+
-+ memset(&sess_arg, 0, sizeof(sess_arg));
-+ export_uuid(sess_arg.uuid, &stm32mp_bsec_ta_uuid);
-+ sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL;
-+ sess_arg.num_params = 0;
-+
-+ rc = tee_client_open_session(ctx, &sess_arg, NULL);
-+ if ((rc < 0) || (sess_arg.ret != 0)) {
-+ pr_err("%s: tee_client_open_session failed err:%#x, ret:%#x\n",
-+ __func__, sess_arg.ret, rc);
-+ if (!rc)
-+ rc = -EINVAL;
-+ } else {
-+ *id = sess_arg.session;
-+ }
-+
-+ return rc;
-+}
-+
-+/* close a session to OP-TEE for STM32MP BSEC TA */
-+static void stm32_bsec_ta_close_session(void *ctx, u32 id)
-+{
-+ tee_client_close_session(ctx, id);
-+}
-+
-+/* stm32_bsec_optee_ta_open() - initialize the STM32MP BSEC TA */
-+int stm32_bsec_optee_ta_open(struct tee_context **ctx)
-+{
-+ struct tee_context *tee_ctx;
-+ u32 session_id;
-+ int rc;
-+
-+ /* Open context with TEE driver */
-+ tee_ctx = tee_client_open_context(NULL, stm32_bsec_optee_ta_match, NULL, NULL);
-+ if (IS_ERR(tee_ctx)) {
-+ rc = PTR_ERR(tee_ctx);
-+ if (rc == -ENOENT)
-+ return -EPROBE_DEFER;
-+ pr_err("%s: tee_client_open_context failed (%d)\n", __func__, rc);
-+
-+ return rc;
-+ }
-+
-+ /* Check STM32MP BSEC TA presence */
-+ rc = stm32_bsec_ta_open_session(tee_ctx, &session_id);
-+ if (rc) {
-+ tee_client_close_context(tee_ctx);
-+ return rc;
-+ }
-+
-+ stm32_bsec_ta_close_session(tee_ctx, session_id);
-+
-+ *ctx = tee_ctx;
-+
-+ return 0;
-+}
-+
-+/* stm32_bsec_optee_ta_open() - release the PTA STM32MP BSEC TA */
-+void stm32_bsec_optee_ta_close(void *ctx)
-+{
-+ tee_client_close_context(ctx);
-+}
-+
-+/* stm32_bsec_optee_ta_read() - nvmem read access using PTA client driver */
-+int stm32_bsec_optee_ta_read(struct tee_context *ctx, unsigned int offset,
-+ void *buf, size_t bytes)
-+{
-+ struct tee_shm *shm;
-+ struct tee_ioctl_invoke_arg arg;
-+ struct tee_param param[2];
-+ u8 *shm_buf;
-+ u32 start, num_bytes;
-+ int ret;
-+ u32 session_id;
-+
-+ ret = stm32_bsec_ta_open_session(ctx, &session_id);
-+ if (ret)
-+ return ret;
-+
-+ memset(&arg, 0, sizeof(arg));
-+ memset(¶m, 0, sizeof(param));
-+
-+ arg.func = PTA_BSEC_READ_MEM;
-+ arg.session = session_id;
-+ arg.num_params = 2;
-+
-+ /* align access on 32bits */
-+ start = ALIGN_DOWN(offset, 4);
-+ num_bytes = round_up(offset + bytes - start, 4);
-+ param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
-+ param[0].u.value.a = start;
-+ param[0].u.value.b = SHADOW_ACCESS;
-+
-+ shm = tee_shm_alloc_kernel_buf(ctx, num_bytes);
-+ if (IS_ERR(shm)) {
-+ ret = PTR_ERR(shm);
-+ goto out_tee_session;
-+ }
-+
-+ param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;
-+ param[1].u.memref.shm = shm;
-+ param[1].u.memref.size = num_bytes;
-+
-+ ret = tee_client_invoke_func(ctx, &arg, param);
-+ if (ret < 0 || arg.ret != 0) {
-+ pr_err("TA_BSEC invoke failed TEE err:%#x, ret:%#x\n",
-+ arg.ret, ret);
-+ if (!ret)
-+ ret = -EIO;
-+ }
-+ if (!ret) {
-+ shm_buf = tee_shm_get_va(shm, 0);
-+ if (IS_ERR(shm_buf)) {
-+ ret = PTR_ERR(shm_buf);
-+ pr_err("tee_shm_get_va failed for transmit (%d)\n", ret);
-+ } else {
-+ /* read data from 32 bits aligned buffer */
-+ memcpy(buf, &shm_buf[offset % 4], bytes);
-+ }
-+ }
-+
-+ tee_shm_free(shm);
-+
-+out_tee_session:
-+ stm32_bsec_ta_close_session(ctx, session_id);
-+
-+ return ret;
-+}
-+
-+/* stm32_bsec_optee_ta_write() - nvmem write access using PTA client driver */
-+int stm32_bsec_optee_ta_write(struct tee_context *ctx, unsigned int lower,
-+ unsigned int offset, void *buf, size_t bytes)
-+{ struct tee_shm *shm;
-+ struct tee_ioctl_invoke_arg arg;
-+ struct tee_param param[2];
-+ u8 *shm_buf;
-+ int ret;
-+ u32 session_id;
-+
-+ ret = stm32_bsec_ta_open_session(ctx, &session_id);
-+ if (ret)
-+ return ret;
-+
-+ /* Allow only writing complete 32-bits aligned words */
-+ if ((bytes % 4) || (offset % 4))
-+ return -EINVAL;
-+
-+ memset(&arg, 0, sizeof(arg));
-+ memset(¶m, 0, sizeof(param));
-+
-+ arg.func = PTA_BSEC_WRITE_MEM;
-+ arg.session = session_id;
-+ arg.num_params = 2;
-+
-+ param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
-+ param[0].u.value.a = offset;
-+ param[0].u.value.b = FUSE_ACCESS;
-+
-+ shm = tee_shm_alloc_kernel_buf(ctx, bytes);
-+ if (IS_ERR(shm)) {
-+ ret = PTR_ERR(shm);
-+ goto out_tee_session;
-+ }
-+
-+ param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
-+ param[1].u.memref.shm = shm;
-+ param[1].u.memref.size = bytes;
-+
-+ shm_buf = tee_shm_get_va(shm, 0);
-+ if (IS_ERR(shm_buf)) {
-+ ret = PTR_ERR(shm_buf);
-+ pr_err("tee_shm_get_va failed for transmit (%d)\n", ret);
-+ tee_shm_free(shm);
-+
-+ goto out_tee_session;
-+ }
-+
-+ memcpy(shm_buf, buf, bytes);
-+
-+ ret = tee_client_invoke_func(ctx, &arg, param);
-+ if (ret < 0 || arg.ret != 0) {
-+ pr_err("TA_BSEC invoke failed TEE err:%#x, ret:%#x\n", arg.ret, ret);
-+ if (!ret)
-+ ret = -EIO;
-+ }
-+ pr_debug("Write OTPs %d to %zu, ret=%d\n", offset / 4, (offset + bytes) / 4, ret);
-+
-+ /* Lock the upper OTPs with ECC protection, word programming only */
-+ if (!ret && ((offset + bytes) >= (lower * 4))) {
-+ u32 start, nb_lock;
-+ u32 *lock = (u32 *)shm_buf;
-+ int i;
-+
-+ /*
-+ * don't lock the lower OTPs, no ECC protection and incremental
-+ * bit programming, a second write is allowed
-+ */
-+ start = max_t(u32, offset, lower * 4);
-+ nb_lock = (offset + bytes - start) / 4;
-+
-+ param[0].u.value.a = start;
-+ param[0].u.value.b = LOCK_ACCESS;
-+ param[1].u.memref.size = nb_lock * 4;
-+
-+ for (i = 0; i < nb_lock; i++)
-+ lock[i] = LOCK_PERM;
-+
-+ ret = tee_client_invoke_func(ctx, &arg, param);
-+ if (ret < 0 || arg.ret != 0) {
-+ pr_err("TA_BSEC invoke failed TEE err:%#x, ret:%#x\n", arg.ret, ret);
-+ if (!ret)
-+ ret = -EIO;
-+ }
-+ pr_debug("Lock upper OTPs %d to %d, ret=%d\n",
-+ start / 4, start / 4 + nb_lock, ret);
-+ }
-+
-+ tee_shm_free(shm);
-+
-+out_tee_session:
-+ stm32_bsec_ta_close_session(ctx, session_id);
-+
-+ return ret;
-+}
---- /dev/null
-+++ b/drivers/nvmem/stm32-bsec-optee-ta.h
-@@ -0,0 +1,80 @@
-+/* SPDX-License-Identifier: GPL-2.0-or-later */
-+/*
-+ * OP-TEE STM32MP BSEC PTA interface, used by STM32 ROMEM driver
-+ *
-+ * Copyright (C) 2022, STMicroelectronics - All Rights Reserved
-+ */
-+
-+#if IS_ENABLED(CONFIG_NVMEM_STM32_BSEC_OPTEE_TA)
-+/**
-+ * stm32_bsec_optee_ta_open() - initialize the STM32 BSEC TA
-+ * @ctx: the OP-TEE context on success
-+ *
-+ * Return:
-+ * On success, 0. On failure, -errno.
-+ */
-+int stm32_bsec_optee_ta_open(struct tee_context **ctx);
-+
-+/**
-+ * stm32_bsec_optee_ta_close() - release the STM32 BSEC TA
-+ * @ctx: the OP-TEE context
-+ *
-+ * This function used to clean the OP-TEE resources initialized in
-+ * stm32_bsec_optee_ta_open(); it can be used as callback to
-+ * devm_add_action_or_reset()
-+ */
-+void stm32_bsec_optee_ta_close(void *ctx);
-+
-+/**
-+ * stm32_bsec_optee_ta_read() - nvmem read access using TA client driver
-+ * @ctx: the OP-TEE context provided by stm32_bsec_optee_ta_open
-+ * @offset: nvmem offset
-+ * @buf: buffer to fill with nvem values
-+ * @bytes: number of bytes to read
-+ *
-+ * Return:
-+ * On success, 0. On failure, -errno.
-+ */
-+int stm32_bsec_optee_ta_read(struct tee_context *ctx, unsigned int offset,
-+ void *buf, size_t bytes);
-+
-+/**
-+ * stm32_bsec_optee_ta_write() - nvmem write access using TA client driver
-+ * @ctx: the OP-TEE context provided by stm32_bsec_optee_ta_open
-+ * @lower: number of lower OTP, not protected by ECC
-+ * @offset: nvmem offset
-+ * @buf: buffer with nvem values
-+ * @bytes: number of bytes to write
-+ *
-+ * Return:
-+ * On success, 0. On failure, -errno.
-+ */
-+int stm32_bsec_optee_ta_write(struct tee_context *ctx, unsigned int lower,
-+ unsigned int offset, void *buf, size_t bytes);
-+
-+#else
-+
-+static inline int stm32_bsec_optee_ta_open(struct tee_context **ctx)
-+{
-+ return -EOPNOTSUPP;
-+}
-+
-+static inline void stm32_bsec_optee_ta_close(void *ctx)
-+{
-+}
-+
-+static inline int stm32_bsec_optee_ta_read(struct tee_context *ctx,
-+ unsigned int offset, void *buf,
-+ size_t bytes)
-+{
-+ return -EOPNOTSUPP;
-+}
-+
-+static inline int stm32_bsec_optee_ta_write(struct tee_context *ctx,
-+ unsigned int lower,
-+ unsigned int offset, void *buf,
-+ size_t bytes)
-+{
-+ return -EOPNOTSUPP;
-+}
-+#endif /* CONFIG_NVMEM_STM32_BSEC_OPTEE_TA */
---- a/drivers/nvmem/stm32-romem.c
-+++ b/drivers/nvmem/stm32-romem.c
-@@ -11,6 +11,9 @@
- #include <linux/module.h>
- #include <linux/nvmem-provider.h>
- #include <linux/of_device.h>
-+#include <linux/tee_drv.h>
-+
-+#include "stm32-bsec-optee-ta.h"
-
- /* BSEC secure service access from non-secure */
- #define STM32_SMC_BSEC 0x82001003
-@@ -25,12 +28,14 @@
- struct stm32_romem_cfg {
- int size;
- u8 lower;
-+ bool ta;
- };
-
- struct stm32_romem_priv {
- void __iomem *base;
- struct nvmem_config cfg;
- u8 lower;
-+ struct tee_context *ctx;
- };
-
- static int stm32_romem_read(void *context, unsigned int offset, void *buf,
-@@ -138,12 +143,29 @@ static int stm32_bsec_write(void *contex
- return 0;
- }
-
-+static int stm32_bsec_pta_read(void *context, unsigned int offset, void *buf,
-+ size_t bytes)
-+{
-+ struct stm32_romem_priv *priv = context;
-+
-+ return stm32_bsec_optee_ta_read(priv->ctx, offset, buf, bytes);
-+}
-+
-+static int stm32_bsec_pta_write(void *context, unsigned int offset, void *buf,
-+ size_t bytes)
-+{
-+ struct stm32_romem_priv *priv = context;
-+
-+ return stm32_bsec_optee_ta_write(priv->ctx, priv->lower, offset, buf, bytes);
-+}
-+
- static int stm32_romem_probe(struct platform_device *pdev)
- {
- const struct stm32_romem_cfg *cfg;
- struct device *dev = &pdev->dev;
- struct stm32_romem_priv *priv;
- struct resource *res;
-+ int rc;
-
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
-@@ -173,15 +195,31 @@ static int stm32_romem_probe(struct plat
- } else {
- priv->cfg.size = cfg->size;
- priv->lower = cfg->lower;
-- priv->cfg.reg_read = stm32_bsec_read;
-- priv->cfg.reg_write = stm32_bsec_write;
-+ if (cfg->ta) {
-+ rc = stm32_bsec_optee_ta_open(&priv->ctx);
-+ /* wait for OP-TEE client driver to be up and ready */
-+ if (rc)
-+ return rc;
-+ }
-+ if (priv->ctx) {
-+ rc = devm_add_action_or_reset(dev, stm32_bsec_optee_ta_close, priv->ctx);
-+ if (rc) {
-+ dev_err(dev, "devm_add_action_or_reset() failed (%d)\n", rc);
-+ return rc;
-+ }
-+ priv->cfg.reg_read = stm32_bsec_pta_read;
-+ priv->cfg.reg_write = stm32_bsec_pta_write;
-+ } else {
-+ priv->cfg.reg_read = stm32_bsec_read;
-+ priv->cfg.reg_write = stm32_bsec_write;
-+ }
- }
-
- return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &priv->cfg));
- }
-
- /*
-- * STM32MP15 BSEC OTP regions: 4096 OTP bits (with 3072 effective bits)
-+ * STM32MP15/13 BSEC OTP regions: 4096 OTP bits (with 3072 effective bits)
- * => 96 x 32-bits data words
- * - Lower: 1K bits, 2:1 redundancy, incremental bit programming
- * => 32 (x 32-bits) lower shadow registers = words 0 to 31
-@@ -191,6 +229,13 @@ static int stm32_romem_probe(struct plat
- static const struct stm32_romem_cfg stm32mp15_bsec_cfg = {
- .size = 384,
- .lower = 32,
-+ .ta = false,
-+};
-+
-+static const struct stm32_romem_cfg stm32mp13_bsec_cfg = {
-+ .size = 384,
-+ .lower = 32,
-+ .ta = true,
- };
-
- static const struct of_device_id stm32_romem_of_match[] = {
-@@ -198,7 +243,10 @@ static const struct of_device_id stm32_r
- .compatible = "st,stm32mp15-bsec",
- .data = (void *)&stm32mp15_bsec_cfg,
- }, {
-+ .compatible = "st,stm32mp13-bsec",
-+ .data = (void *)&stm32mp13_bsec_cfg,
- },
-+ { /* sentinel */ },
- };
- MODULE_DEVICE_TABLE(of, stm32_romem_of_match);
-
+++ /dev/null
-From df2f34ef1d924125ffaf29dfdaf7cdbd3183c321 Mon Sep 17 00:00:00 2001
-From: Patrick Delaunay <patrick.delaunay@foss.st.com>
-Date: Mon, 6 Feb 2023 13:43:52 +0000
-Subject: [PATCH] nvmem: stm32: detect bsec pta presence for STM32MP15x
-
-On STM32MP15x SoC, the SMC backend is optional when OP-TEE is used;
-the PTA BSEC should be used as it is done on STM32MP13x platform,
-but the BSEC SMC can be also used: it is a legacy mode in OP-TEE,
-not recommended but used in previous OP-TEE firmware.
-
-The presence of OP-TEE is dynamically detected in STM32MP15x device tree
-and the supported NVMEM backend is dynamically detected:
-- PTA with stm32_bsec_pta_find
-- SMC with stm32_bsec_check
-
-With OP-TEE but without PTA and SMC detection, the probe is deferred for
-STM32MP15x devices.
-
-On STM32MP13x platform, only the PTA is supported with cfg->ta = true
-and this detection is skipped.
-
-Signed-off-by: Patrick Delaunay <patrick.delaunay@foss.st.com>
-Reviewed-by: Etienne Carriere <etienne.carriere@linaro.org>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230206134356.839737-19-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/stm32-romem.c | 38 +++++++++++++++++++++++++++++++++----
- 1 file changed, 34 insertions(+), 4 deletions(-)
-
---- a/drivers/nvmem/stm32-romem.c
-+++ b/drivers/nvmem/stm32-romem.c
-@@ -159,6 +159,31 @@ static int stm32_bsec_pta_write(void *co
- return stm32_bsec_optee_ta_write(priv->ctx, priv->lower, offset, buf, bytes);
- }
-
-+static bool stm32_bsec_smc_check(void)
-+{
-+ u32 val;
-+ int ret;
-+
-+ /* check that the OP-TEE support the BSEC SMC (legacy mode) */
-+ ret = stm32_bsec_smc(STM32_SMC_READ_SHADOW, 0, 0, &val);
-+
-+ return !ret;
-+}
-+
-+static bool optee_presence_check(void)
-+{
-+ struct device_node *np;
-+ bool tee_detected = false;
-+
-+ /* check that the OP-TEE node is present and available. */
-+ np = of_find_compatible_node(NULL, NULL, "linaro,optee-tz");
-+ if (np && of_device_is_available(np))
-+ tee_detected = true;
-+ of_node_put(np);
-+
-+ return tee_detected;
-+}
-+
- static int stm32_romem_probe(struct platform_device *pdev)
- {
- const struct stm32_romem_cfg *cfg;
-@@ -195,11 +220,16 @@ static int stm32_romem_probe(struct plat
- } else {
- priv->cfg.size = cfg->size;
- priv->lower = cfg->lower;
-- if (cfg->ta) {
-+ if (cfg->ta || optee_presence_check()) {
- rc = stm32_bsec_optee_ta_open(&priv->ctx);
-- /* wait for OP-TEE client driver to be up and ready */
-- if (rc)
-- return rc;
-+ if (rc) {
-+ /* wait for OP-TEE client driver to be up and ready */
-+ if (rc == -EPROBE_DEFER)
-+ return -EPROBE_DEFER;
-+ /* BSEC PTA is required or SMC not supported */
-+ if (cfg->ta || !stm32_bsec_smc_check())
-+ return rc;
-+ }
- }
- if (priv->ctx) {
- rc = devm_add_action_or_reset(dev, stm32_bsec_optee_ta_close, priv->ctx);
+++ /dev/null
-From 3e5ac22aa564026e99defc3a8e02082521a5b231 Mon Sep 17 00:00:00 2001
-From: Randy Dunlap <rdunlap@infradead.org>
-Date: Mon, 6 Feb 2023 13:43:53 +0000
-Subject: [PATCH] nvmem: rave-sp-eeprm: fix kernel-doc bad line warning
-
-Convert an empty line to " *" to avoid a kernel-doc warning:
-
-drivers/nvmem/rave-sp-eeprom.c:48: warning: bad line:
-
-Signed-off-by: Randy Dunlap <rdunlap@infradead.org>
-Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Cc: Andrey Vostrikov <andrey.vostrikov@cogentembedded.com>
-Cc: Nikita Yushchenko <nikita.yoush@cogentembedded.com>
-Cc: Andrey Smirnov <andrew.smirnov@gmail.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230206134356.839737-20-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/rave-sp-eeprom.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/nvmem/rave-sp-eeprom.c
-+++ b/drivers/nvmem/rave-sp-eeprom.c
-@@ -45,7 +45,7 @@ enum rave_sp_eeprom_header_size {
- * @type: Access type (see enum rave_sp_eeprom_access_type)
- * @success: Success flag (Success = 1, Failure = 0)
- * @data: Read data
--
-+ *
- * Note this structure corresponds to RSP_*_EEPROM payload from RAVE
- * SP ICD
- */
+++ /dev/null
-From eb7dda20f42a9137e9ee53d5ed3b743d49338cb5 Mon Sep 17 00:00:00 2001
-From: Johan Hovold <johan+linaro@kernel.org>
-Date: Mon, 6 Feb 2023 13:43:54 +0000
-Subject: [PATCH] nvmem: qcom-spmi-sdam: register at device init time
-
-There are currently no in-tree users of the Qualcomm SDAM nvmem driver
-and there is generally no point in registering a driver that can be
-built as a module at subsys init time.
-
-Register the driver at the normal device init time instead and let
-driver core sort out the probe order.
-
-Signed-off-by: Johan Hovold <johan+linaro@kernel.org>
-Reviewed-by: Bjorn Andersson <andersson@kernel.org>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230206134356.839737-21-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/qcom-spmi-sdam.c | 13 +------------
- 1 file changed, 1 insertion(+), 12 deletions(-)
-
---- a/drivers/nvmem/qcom-spmi-sdam.c
-+++ b/drivers/nvmem/qcom-spmi-sdam.c
-@@ -175,18 +175,7 @@ static struct platform_driver sdam_drive
- },
- .probe = sdam_probe,
- };
--
--static int __init sdam_init(void)
--{
-- return platform_driver_register(&sdam_driver);
--}
--subsys_initcall(sdam_init);
--
--static void __exit sdam_exit(void)
--{
-- return platform_driver_unregister(&sdam_driver);
--}
--module_exit(sdam_exit);
-+module_platform_driver(sdam_driver);
-
- MODULE_DESCRIPTION("QCOM SPMI SDAM driver");
- MODULE_LICENSE("GPL v2");
+++ /dev/null
-From 1dc7e37bb0ec1c997fac82031332a38c7610352f Mon Sep 17 00:00:00 2001
-From: Arnd Bergmann <arnd@arndb.de>
-Date: Mon, 6 Feb 2023 13:43:56 +0000
-Subject: [PATCH] nvmem: stm32: fix OPTEE dependency
-
-The stm32 nvmem driver fails to link as built-in when OPTEE
-is a loadable module:
-
-aarch64-linux-ld: drivers/nvmem/stm32-bsec-optee-ta.o: in function `stm32_bsec:
-stm32-bsec-optee-ta.c:(.text+0xc8): undefined reference to `tee_client_open_session'
-aarch64-linux-ld: drivers/nvmem/stm32-bsec-optee-ta.o: in function `stm32_bsec:
-stm32-bsec-optee-ta.c:(.text+0x1fc): undefined reference to `tee_client_open_context'
-
-Change the CONFIG_NVMEM_STM32_ROMEM definition so it can only
-be built-in if OPTEE is either built-in or disabled, and
-make NVMEM_STM32_BSEC_OPTEE_TA a hidden symbol instead.
-
-Signed-off-by: Arnd Bergmann <arnd@arndb.de>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230206134356.839737-23-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/Kconfig | 5 ++---
- 1 file changed, 2 insertions(+), 3 deletions(-)
-
---- a/drivers/nvmem/Kconfig
-+++ b/drivers/nvmem/Kconfig
-@@ -291,8 +291,7 @@ config NVMEM_SPRD_EFUSE
- will be called nvmem-sprd-efuse.
-
- config NVMEM_STM32_BSEC_OPTEE_TA
-- bool "STM32MP BSEC OP-TEE TA support for nvmem-stm32-romem driver"
-- depends on OPTEE
-+ def_bool NVMEM_STM32_ROMEM && OPTEE
- help
- Say y here to enable the accesses to STM32MP SoC OTPs by the OP-TEE
- trusted application STM32MP BSEC.
-@@ -303,7 +302,7 @@ config NVMEM_STM32_BSEC_OPTEE_TA
- config NVMEM_STM32_ROMEM
- tristate "STMicroelectronics STM32 factory-programmed memory support"
- depends on ARCH_STM32 || COMPILE_TEST
-- imply NVMEM_STM32_BSEC_OPTEE_TA
-+ depends on OPTEE || !OPTEE
- help
- Say y here to enable read-only access for STMicroelectronics STM32
- factory-programmed memory area.
+++ /dev/null
-From 4d70c74659d9746502b23d055dba03d1d28ec388 Mon Sep 17 00:00:00 2001
-From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
-Date: Wed, 30 Nov 2022 15:48:35 +0200
-Subject: [PATCH] i915: Move list_count() to list.h as list_count_nodes() for
- broader use
-
-Some of the existing users, and definitely will be new ones, want to
-count existing nodes in the list. Provide a generic API for that by
-moving code from i915 to list.h.
-
-Reviewed-by: Lucas De Marchi <lucas.demarchi@intel.com>
-Acked-by: Jani Nikula <jani.nikula@intel.com>
-Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
-Link: https://lore.kernel.org/r/20221130134838.23805-1-andriy.shevchenko@linux.intel.com
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/gpu/drm/i915/gt/intel_engine_cs.c | 15 ++-------------
- include/linux/list.h | 15 +++++++++++++++
- 2 files changed, 17 insertions(+), 13 deletions(-)
-
---- a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c
-+++ b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c
-@@ -4154,17 +4154,6 @@ void intel_execlists_show_requests(struc
- spin_unlock_irqrestore(&sched_engine->lock, flags);
- }
-
--static unsigned long list_count(struct list_head *list)
--{
-- struct list_head *pos;
-- unsigned long count = 0;
--
-- list_for_each(pos, list)
-- count++;
--
-- return count;
--}
--
- void intel_execlists_dump_active_requests(struct intel_engine_cs *engine,
- struct i915_request *hung_rq,
- struct drm_printer *m)
-@@ -4175,8 +4164,8 @@ void intel_execlists_dump_active_request
-
- intel_engine_dump_active_requests(&engine->sched_engine->requests, hung_rq, m);
-
-- drm_printf(m, "\tOn hold?: %lu\n",
-- list_count(&engine->sched_engine->hold));
-+ drm_printf(m, "\tOn hold?: %zu\n",
-+ list_count_nodes(&engine->sched_engine->hold));
-
- spin_unlock_irqrestore(&engine->sched_engine->lock, flags);
- }
---- a/include/linux/list.h
-+++ b/include/linux/list.h
-@@ -656,6 +656,21 @@ static inline void list_splice_tail_init
- pos = n, n = pos->prev)
-
- /**
-+ * list_count_nodes - count nodes in the list
-+ * @head: the head for your list.
-+ */
-+static inline size_t list_count_nodes(struct list_head *head)
-+{
-+ struct list_head *pos;
-+ size_t count = 0;
-+
-+ list_for_each(pos, head)
-+ count++;
-+
-+ return count;
-+}
-+
-+/**
- * list_entry_is_head - test if the entry points to the head of the list
- * @pos: the type * to cursor
- * @head: the head for your list.
+++ /dev/null
-From bcd1fe07def0f070eb5f31594620aaee6f81d31a Mon Sep 17 00:00:00 2001
-From: Nick Alcock <nick.alcock@oracle.com>
-Date: Tue, 4 Apr 2023 18:21:11 +0100
-Subject: [PATCH] nvmem: xilinx: zynqmp: make modular
-
-This driver has a MODULE_LICENSE but is not tristate so cannot be
-built as a module, unlike all its peers: make it modular to match.
-
-Signed-off-by: Nick Alcock <nick.alcock@oracle.com>
-Suggested-by: Michal Simek <michal.simek@amd.com>
-Cc: Luis Chamberlain <mcgrof@kernel.org>
-Cc: linux-modules@vger.kernel.org
-Cc: linux-kernel@vger.kernel.org
-Cc: Hitomi Hasegawa <hasegawa-hitomi@fujitsu.com>
-Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Cc: Michal Simek <michal.simek@xilinx.com>
-Cc: linux-arm-kernel@lists.infradead.org
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230404172148.82422-4-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/Kconfig | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/nvmem/Kconfig
-+++ b/drivers/nvmem/Kconfig
-@@ -368,7 +368,7 @@ config NVMEM_VF610_OCOTP
- be called nvmem-vf610-ocotp.
-
- config NVMEM_ZYNQMP
-- bool "Xilinx ZYNQMP SoC nvmem firmware support"
-+ tristate "Xilinx ZYNQMP SoC nvmem firmware support"
- depends on ARCH_ZYNQMP
- help
- This is a driver to access hardware related data like
+++ /dev/null
-From 266570f496b90dea8fda893c2cf7c28d63ae2bd9 Mon Sep 17 00:00:00 2001
-From: Michael Walle <michael@walle.cc>
-Date: Tue, 4 Apr 2023 18:21:21 +0100
-Subject: [PATCH] nvmem: core: introduce NVMEM layouts
-
-NVMEM layouts are used to generate NVMEM cells during runtime. Think of
-an EEPROM with a well-defined conent. For now, the content can be
-described by a device tree or a board file. But this only works if the
-offsets and lengths are static and don't change. One could also argue
-that putting the layout of the EEPROM in the device tree is the wrong
-place. Instead, the device tree should just have a specific compatible
-string.
-
-Right now there are two use cases:
- (1) The NVMEM cell needs special processing. E.g. if it only specifies
- a base MAC address offset and you need to add an offset, or it
- needs to parse a MAC from ASCII format or some proprietary format.
- (Post processing of cells is added in a later commit).
- (2) u-boot environment parsing. The cells don't have a particular
- offset but it needs parsing the content to determine the offsets
- and length.
-
-Co-developed-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Signed-off-by: Michael Walle <michael@walle.cc>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230404172148.82422-14-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- Documentation/driver-api/nvmem.rst | 15 ++++
- drivers/nvmem/Kconfig | 4 +
- drivers/nvmem/Makefile | 1 +
- drivers/nvmem/core.c | 120 +++++++++++++++++++++++++++++
- drivers/nvmem/layouts/Kconfig | 5 ++
- drivers/nvmem/layouts/Makefile | 4 +
- include/linux/nvmem-consumer.h | 7 ++
- include/linux/nvmem-provider.h | 51 ++++++++++++
- 8 files changed, 207 insertions(+)
- create mode 100644 drivers/nvmem/layouts/Kconfig
- create mode 100644 drivers/nvmem/layouts/Makefile
-
---- a/Documentation/driver-api/nvmem.rst
-+++ b/Documentation/driver-api/nvmem.rst
-@@ -185,3 +185,18 @@ ex::
- =====================
-
- See Documentation/devicetree/bindings/nvmem/nvmem.txt
-+
-+8. NVMEM layouts
-+================
-+
-+NVMEM layouts are yet another mechanism to create cells. With the device
-+tree binding it is possible to specify simple cells by using an offset
-+and a length. Sometimes, the cells doesn't have a static offset, but
-+the content is still well defined, e.g. tag-length-values. In this case,
-+the NVMEM device content has to be first parsed and the cells need to
-+be added accordingly. Layouts let you read the content of the NVMEM device
-+and let you add cells dynamically.
-+
-+Another use case for layouts is the post processing of cells. With layouts,
-+it is possible to associate a custom post processing hook to a cell. It
-+even possible to add this hook to cells not created by the layout itself.
---- a/drivers/nvmem/Kconfig
-+++ b/drivers/nvmem/Kconfig
-@@ -21,6 +21,10 @@ config NVMEM_SYSFS
- This interface is mostly used by userspace applications to
- read/write directly into nvmem.
-
-+# Layouts
-+
-+source "drivers/nvmem/layouts/Kconfig"
-+
- # Devices
-
- config NVMEM_APPLE_EFUSES
---- a/drivers/nvmem/Makefile
-+++ b/drivers/nvmem/Makefile
-@@ -5,6 +5,7 @@
-
- obj-$(CONFIG_NVMEM) += nvmem_core.o
- nvmem_core-y := core.o
-+obj-y += layouts/
-
- # Devices
- obj-$(CONFIG_NVMEM_APPLE_EFUSES) += nvmem-apple-efuses.o
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -40,6 +40,7 @@ struct nvmem_device {
- nvmem_reg_write_t reg_write;
- nvmem_cell_post_process_t cell_post_process;
- struct gpio_desc *wp_gpio;
-+ struct nvmem_layout *layout;
- void *priv;
- };
-
-@@ -74,6 +75,9 @@ static LIST_HEAD(nvmem_lookup_list);
-
- static BLOCKING_NOTIFIER_HEAD(nvmem_notifier);
-
-+static DEFINE_SPINLOCK(nvmem_layout_lock);
-+static LIST_HEAD(nvmem_layouts);
-+
- static int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset,
- void *val, size_t bytes)
- {
-@@ -728,6 +732,101 @@ static int nvmem_add_cells_from_of(struc
- return 0;
- }
-
-+int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner)
-+{
-+ layout->owner = owner;
-+
-+ spin_lock(&nvmem_layout_lock);
-+ list_add(&layout->node, &nvmem_layouts);
-+ spin_unlock(&nvmem_layout_lock);
-+
-+ return 0;
-+}
-+EXPORT_SYMBOL_GPL(__nvmem_layout_register);
-+
-+void nvmem_layout_unregister(struct nvmem_layout *layout)
-+{
-+ spin_lock(&nvmem_layout_lock);
-+ list_del(&layout->node);
-+ spin_unlock(&nvmem_layout_lock);
-+}
-+EXPORT_SYMBOL_GPL(nvmem_layout_unregister);
-+
-+static struct nvmem_layout *nvmem_layout_get(struct nvmem_device *nvmem)
-+{
-+ struct device_node *layout_np, *np = nvmem->dev.of_node;
-+ struct nvmem_layout *l, *layout = NULL;
-+
-+ layout_np = of_get_child_by_name(np, "nvmem-layout");
-+ if (!layout_np)
-+ return NULL;
-+
-+ spin_lock(&nvmem_layout_lock);
-+
-+ list_for_each_entry(l, &nvmem_layouts, node) {
-+ if (of_match_node(l->of_match_table, layout_np)) {
-+ if (try_module_get(l->owner))
-+ layout = l;
-+
-+ break;
-+ }
-+ }
-+
-+ spin_unlock(&nvmem_layout_lock);
-+ of_node_put(layout_np);
-+
-+ return layout;
-+}
-+
-+static void nvmem_layout_put(struct nvmem_layout *layout)
-+{
-+ if (layout)
-+ module_put(layout->owner);
-+}
-+
-+static int nvmem_add_cells_from_layout(struct nvmem_device *nvmem)
-+{
-+ struct nvmem_layout *layout = nvmem->layout;
-+ int ret;
-+
-+ if (layout && layout->add_cells) {
-+ ret = layout->add_cells(&nvmem->dev, nvmem, layout);
-+ if (ret)
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
-+#if IS_ENABLED(CONFIG_OF)
-+/**
-+ * of_nvmem_layout_get_container() - Get OF node to layout container.
-+ *
-+ * @nvmem: nvmem device.
-+ *
-+ * Return: a node pointer with refcount incremented or NULL if no
-+ * container exists. Use of_node_put() on it when done.
-+ */
-+struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
-+{
-+ return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout");
-+}
-+EXPORT_SYMBOL_GPL(of_nvmem_layout_get_container);
-+#endif
-+
-+const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem,
-+ struct nvmem_layout *layout)
-+{
-+ struct device_node __maybe_unused *layout_np;
-+ const struct of_device_id *match;
-+
-+ layout_np = of_nvmem_layout_get_container(nvmem);
-+ match = of_match_node(layout->of_match_table, layout_np);
-+
-+ return match ? match->data : NULL;
-+}
-+EXPORT_SYMBOL_GPL(nvmem_layout_get_match_data);
-+
- /**
- * nvmem_register() - Register a nvmem device for given nvmem_config.
- * Also creates a binary entry in /sys/bus/nvmem/devices/dev-name/nvmem
-@@ -834,6 +933,12 @@ struct nvmem_device *nvmem_register(cons
- goto err_put_device;
- }
-
-+ /*
-+ * If the driver supplied a layout by config->layout, the module
-+ * pointer will be NULL and nvmem_layout_put() will be a noop.
-+ */
-+ nvmem->layout = config->layout ?: nvmem_layout_get(nvmem);
-+
- if (config->cells) {
- rval = nvmem_add_cells(nvmem, config->cells, config->ncells);
- if (rval)
-@@ -854,12 +959,17 @@ struct nvmem_device *nvmem_register(cons
- if (rval)
- goto err_remove_cells;
-
-+ rval = nvmem_add_cells_from_layout(nvmem);
-+ if (rval)
-+ goto err_remove_cells;
-+
- blocking_notifier_call_chain(&nvmem_notifier, NVMEM_ADD, nvmem);
-
- return nvmem;
-
- err_remove_cells:
- nvmem_device_remove_all_cells(nvmem);
-+ nvmem_layout_put(nvmem->layout);
- if (config->compat)
- nvmem_sysfs_remove_compat(nvmem, config);
- err_put_device:
-@@ -881,6 +991,7 @@ static void nvmem_device_release(struct
- device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom);
-
- nvmem_device_remove_all_cells(nvmem);
-+ nvmem_layout_put(nvmem->layout);
- device_unregister(&nvmem->dev);
- }
-
-@@ -1246,6 +1357,15 @@ struct nvmem_cell *of_nvmem_cell_get(str
- return ERR_PTR(-EINVAL);
- }
-
-+ /* nvmem layouts produce cells within the nvmem-layout container */
-+ if (of_node_name_eq(nvmem_np, "nvmem-layout")) {
-+ nvmem_np = of_get_next_parent(nvmem_np);
-+ if (!nvmem_np) {
-+ of_node_put(cell_np);
-+ return ERR_PTR(-EINVAL);
-+ }
-+ }
-+
- nvmem = __nvmem_device_get(nvmem_np, device_match_of_node);
- of_node_put(nvmem_np);
- if (IS_ERR(nvmem)) {
---- /dev/null
-+++ b/drivers/nvmem/layouts/Kconfig
-@@ -0,0 +1,5 @@
-+# SPDX-License-Identifier: GPL-2.0
-+
-+menu "Layout Types"
-+
-+endmenu
---- /dev/null
-+++ b/drivers/nvmem/layouts/Makefile
-@@ -0,0 +1,4 @@
-+# SPDX-License-Identifier: GPL-2.0
-+#
-+# Makefile for nvmem layouts.
-+#
---- a/include/linux/nvmem-consumer.h
-+++ b/include/linux/nvmem-consumer.h
-@@ -239,6 +239,7 @@ struct nvmem_cell *of_nvmem_cell_get(str
- const char *id);
- struct nvmem_device *of_nvmem_device_get(struct device_node *np,
- const char *name);
-+struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem);
- #else
- static inline struct nvmem_cell *of_nvmem_cell_get(struct device_node *np,
- const char *id)
-@@ -251,6 +252,12 @@ static inline struct nvmem_device *of_nv
- {
- return ERR_PTR(-EOPNOTSUPP);
- }
-+
-+static inline struct device_node *
-+of_nvmem_layout_get_container(struct nvmem_device *nvmem)
-+{
-+ return ERR_PTR(-EOPNOTSUPP);
-+}
- #endif /* CONFIG_NVMEM && CONFIG_OF */
-
- #endif /* ifndef _LINUX_NVMEM_CONSUMER_H */
---- a/include/linux/nvmem-provider.h
-+++ b/include/linux/nvmem-provider.h
-@@ -88,6 +88,7 @@ struct nvmem_cell_info {
- * @stride: Minimum read/write access stride.
- * @priv: User context passed to read/write callbacks.
- * @ignore_wp: Write Protect pin is managed by the provider.
-+ * @layout: Fixed layout associated with this nvmem device.
- *
- * Note: A default "nvmem<id>" name will be assigned to the device if
- * no name is specified in its configuration. In such case "<id>" is
-@@ -109,6 +110,7 @@ struct nvmem_config {
- bool read_only;
- bool root_only;
- bool ignore_wp;
-+ struct nvmem_layout *layout;
- struct device_node *of_node;
- bool no_of_node;
- nvmem_reg_read_t reg_read;
-@@ -142,6 +144,33 @@ struct nvmem_cell_table {
- struct list_head node;
- };
-
-+/**
-+ * struct nvmem_layout - NVMEM layout definitions
-+ *
-+ * @name: Layout name.
-+ * @of_match_table: Open firmware match table.
-+ * @add_cells: Will be called if a nvmem device is found which
-+ * has this layout. The function will add layout
-+ * specific cells with nvmem_add_one_cell().
-+ * @owner: Pointer to struct module.
-+ * @node: List node.
-+ *
-+ * A nvmem device can hold a well defined structure which can just be
-+ * evaluated during runtime. For example a TLV list, or a list of "name=val"
-+ * pairs. A nvmem layout can parse the nvmem device and add appropriate
-+ * cells.
-+ */
-+struct nvmem_layout {
-+ const char *name;
-+ const struct of_device_id *of_match_table;
-+ int (*add_cells)(struct device *dev, struct nvmem_device *nvmem,
-+ struct nvmem_layout *layout);
-+
-+ /* private */
-+ struct module *owner;
-+ struct list_head node;
-+};
-+
- #if IS_ENABLED(CONFIG_NVMEM)
-
- struct nvmem_device *nvmem_register(const struct nvmem_config *cfg);
-@@ -156,6 +185,14 @@ void nvmem_del_cell_table(struct nvmem_c
- int nvmem_add_one_cell(struct nvmem_device *nvmem,
- const struct nvmem_cell_info *info);
-
-+int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner);
-+#define nvmem_layout_register(layout) \
-+ __nvmem_layout_register(layout, THIS_MODULE)
-+void nvmem_layout_unregister(struct nvmem_layout *layout);
-+
-+const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem,
-+ struct nvmem_layout *layout);
-+
- #else
-
- static inline struct nvmem_device *nvmem_register(const struct nvmem_config *c)
-@@ -179,5 +216,19 @@ static inline int nvmem_add_one_cell(str
- return -EOPNOTSUPP;
- }
-
-+static inline int nvmem_layout_register(struct nvmem_layout *layout)
-+{
-+ return -EOPNOTSUPP;
-+}
-+
-+static inline void nvmem_layout_unregister(struct nvmem_layout *layout) {}
-+
-+static inline const void *
-+nvmem_layout_get_match_data(struct nvmem_device *nvmem,
-+ struct nvmem_layout *layout)
-+{
-+ return NULL;
-+}
-+
- #endif /* CONFIG_NVMEM */
- #endif /* ifndef _LINUX_NVMEM_PROVIDER_H */
+++ /dev/null
-From 6468a6f45148fb5e95c86b4efebf63f9abcd2137 Mon Sep 17 00:00:00 2001
-From: Miquel Raynal <miquel.raynal@bootlin.com>
-Date: Tue, 4 Apr 2023 18:21:22 +0100
-Subject: [PATCH] nvmem: core: handle the absence of expected layouts
-
-Make nvmem_layout_get() return -EPROBE_DEFER while the expected layout
-is not available. This condition cannot be triggered today as nvmem
-layout drivers are initialed as part of an early init call, but soon
-these drivers will be converted into modules and be initialized with a
-standard priority, so the unavailability of the drivers might become a
-reality that must be taken care of.
-
-Let's anticipate this by telling the caller the layout might not yet be
-available. A probe deferral is requested in this case.
-
-Please note this does not affect any nvmem device not using layouts,
-because an early check against the "nvmem-layout" container presence
-will return NULL in this case.
-
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Tested-by: Michael Walle <michael@walle.cc>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230404172148.82422-15-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/core.c | 10 +++++++++-
- 1 file changed, 9 insertions(+), 1 deletion(-)
-
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -755,7 +755,7 @@ EXPORT_SYMBOL_GPL(nvmem_layout_unregiste
- static struct nvmem_layout *nvmem_layout_get(struct nvmem_device *nvmem)
- {
- struct device_node *layout_np, *np = nvmem->dev.of_node;
-- struct nvmem_layout *l, *layout = NULL;
-+ struct nvmem_layout *l, *layout = ERR_PTR(-EPROBE_DEFER);
-
- layout_np = of_get_child_by_name(np, "nvmem-layout");
- if (!layout_np)
-@@ -938,6 +938,13 @@ struct nvmem_device *nvmem_register(cons
- * pointer will be NULL and nvmem_layout_put() will be a noop.
- */
- nvmem->layout = config->layout ?: nvmem_layout_get(nvmem);
-+ if (IS_ERR(nvmem->layout)) {
-+ rval = PTR_ERR(nvmem->layout);
-+ nvmem->layout = NULL;
-+
-+ if (rval == -EPROBE_DEFER)
-+ goto err_teardown_compat;
-+ }
-
- if (config->cells) {
- rval = nvmem_add_cells(nvmem, config->cells, config->ncells);
-@@ -970,6 +977,7 @@ struct nvmem_device *nvmem_register(cons
- err_remove_cells:
- nvmem_device_remove_all_cells(nvmem);
- nvmem_layout_put(nvmem->layout);
-+err_teardown_compat:
- if (config->compat)
- nvmem_sysfs_remove_compat(nvmem, config);
- err_put_device:
+++ /dev/null
-From b1c37bec1ccfe5ccab72bc0ddc0dfa45c43e2de2 Mon Sep 17 00:00:00 2001
-From: Miquel Raynal <miquel.raynal@bootlin.com>
-Date: Tue, 4 Apr 2023 18:21:23 +0100
-Subject: [PATCH] nvmem: core: request layout modules loading
-
-When a storage device like an eeprom or an mtd device probes, it
-registers an nvmem device if the nvmem subsystem has been enabled (bool
-symbol). During nvmem registration, if the device is using layouts to
-expose dynamic nvmem cells, the core will first try to get a reference
-over the layout driver callbacks. In practice there is not relationship
-that can be described between the storage driver and the nvmem
-layout. So there is no way we can enforce both drivers will be built-in
-or both will be modules. If the storage device driver is built-in but
-the layout is built as a module, instead of badly failing with an
-endless probe deferral loop, lets just make a modprobe call in case the
-driver was made available in an initramfs with
-of_device_node_request_module(), and offer a fully functional system to
-the user.
-
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Tested-by: Michael Walle <michael@walle.cc>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230404172148.82422-16-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/core.c | 8 ++++++++
- 1 file changed, 8 insertions(+)
-
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -17,6 +17,7 @@
- #include <linux/nvmem-provider.h>
- #include <linux/gpio/consumer.h>
- #include <linux/of.h>
-+#include <linux/of_device.h>
- #include <linux/slab.h>
-
- struct nvmem_device {
-@@ -761,6 +762,13 @@ static struct nvmem_layout *nvmem_layout
- if (!layout_np)
- return NULL;
-
-+ /*
-+ * In case the nvmem device was built-in while the layout was built as a
-+ * module, we shall manually request the layout driver loading otherwise
-+ * we'll never have any match.
-+ */
-+ of_request_module(layout_np);
-+
- spin_lock(&nvmem_layout_lock);
-
- list_for_each_entry(l, &nvmem_layouts, node) {
+++ /dev/null
-From 345ec382cd4b736c36e01f155d08c913b225b736 Mon Sep 17 00:00:00 2001
-From: Michael Walle <michael@walle.cc>
-Date: Tue, 4 Apr 2023 18:21:24 +0100
-Subject: [PATCH] nvmem: core: add per-cell post processing
-
-Instead of relying on the name the consumer is using for the cell, like
-it is done for the nvmem .cell_post_process configuration parameter,
-provide a per-cell post processing hook. This can then be populated by
-the NVMEM provider (or the NVMEM layout) when adding the cell.
-
-Signed-off-by: Michael Walle <michael@walle.cc>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230404172148.82422-17-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/core.c | 17 +++++++++++++++++
- include/linux/nvmem-provider.h | 3 +++
- 2 files changed, 20 insertions(+)
-
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -54,6 +54,7 @@ struct nvmem_cell_entry {
- int bytes;
- int bit_offset;
- int nbits;
-+ nvmem_cell_post_process_t read_post_process;
- struct device_node *np;
- struct nvmem_device *nvmem;
- struct list_head node;
-@@ -470,6 +471,7 @@ static int nvmem_cell_info_to_nvmem_cell
- cell->offset = info->offset;
- cell->bytes = info->bytes;
- cell->name = info->name;
-+ cell->read_post_process = info->read_post_process;
-
- cell->bit_offset = info->bit_offset;
- cell->nbits = info->nbits;
-@@ -1563,6 +1565,13 @@ static int __nvmem_cell_read(struct nvme
- if (cell->bit_offset || cell->nbits)
- nvmem_shift_read_buffer_in_place(cell, buf);
-
-+ if (cell->read_post_process) {
-+ rc = cell->read_post_process(nvmem->priv, id, index,
-+ cell->offset, buf, cell->bytes);
-+ if (rc)
-+ return rc;
-+ }
-+
- if (nvmem->cell_post_process) {
- rc = nvmem->cell_post_process(nvmem->priv, id, index,
- cell->offset, buf, cell->bytes);
-@@ -1671,6 +1680,14 @@ static int __nvmem_cell_entry_write(stru
- (cell->bit_offset == 0 && len != cell->bytes))
- return -EINVAL;
-
-+ /*
-+ * Any cells which have a read_post_process hook are read-only because
-+ * we cannot reverse the operation and it might affect other cells,
-+ * too.
-+ */
-+ if (cell->read_post_process)
-+ return -EINVAL;
-+
- if (cell->bit_offset || cell->nbits) {
- buf = nvmem_cell_prepare_write_buffer(cell, buf, len);
- if (IS_ERR(buf))
---- a/include/linux/nvmem-provider.h
-+++ b/include/linux/nvmem-provider.h
-@@ -54,6 +54,8 @@ struct nvmem_keepout {
- * @bit_offset: Bit offset if cell is smaller than a byte.
- * @nbits: Number of bits.
- * @np: Optional device_node pointer.
-+ * @read_post_process: Callback for optional post processing of cell data
-+ * on reads.
- */
- struct nvmem_cell_info {
- const char *name;
-@@ -62,6 +64,7 @@ struct nvmem_cell_info {
- unsigned int bit_offset;
- unsigned int nbits;
- struct device_node *np;
-+ nvmem_cell_post_process_t read_post_process;
- };
-
- /**
+++ /dev/null
-From de12c9691501ccba41a154c223869f82be4c12fd Mon Sep 17 00:00:00 2001
-From: Michael Walle <michael@walle.cc>
-Date: Tue, 4 Apr 2023 18:21:25 +0100
-Subject: [PATCH] nvmem: core: allow to modify a cell before adding it
-
-Provide a way to modify a cell before it will get added. This is useful
-to attach a custom post processing hook via a layout.
-
-Signed-off-by: Michael Walle <michael@walle.cc>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230404172148.82422-18-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/core.c | 4 ++++
- include/linux/nvmem-provider.h | 5 +++++
- 2 files changed, 9 insertions(+)
-
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -695,6 +695,7 @@ static int nvmem_validate_keepouts(struc
-
- static int nvmem_add_cells_from_of(struct nvmem_device *nvmem)
- {
-+ struct nvmem_layout *layout = nvmem->layout;
- struct device *dev = &nvmem->dev;
- struct device_node *child;
- const __be32 *addr;
-@@ -724,6 +725,9 @@ static int nvmem_add_cells_from_of(struc
-
- info.np = of_node_get(child);
-
-+ if (layout && layout->fixup_cell_info)
-+ layout->fixup_cell_info(nvmem, layout, &info);
-+
- ret = nvmem_add_one_cell(nvmem, &info);
- kfree(info.name);
- if (ret) {
---- a/include/linux/nvmem-provider.h
-+++ b/include/linux/nvmem-provider.h
-@@ -155,6 +155,8 @@ struct nvmem_cell_table {
- * @add_cells: Will be called if a nvmem device is found which
- * has this layout. The function will add layout
- * specific cells with nvmem_add_one_cell().
-+ * @fixup_cell_info: Will be called before a cell is added. Can be
-+ * used to modify the nvmem_cell_info.
- * @owner: Pointer to struct module.
- * @node: List node.
- *
-@@ -168,6 +170,9 @@ struct nvmem_layout {
- const struct of_device_id *of_match_table;
- int (*add_cells)(struct device *dev, struct nvmem_device *nvmem,
- struct nvmem_layout *layout);
-+ void (*fixup_cell_info)(struct nvmem_device *nvmem,
-+ struct nvmem_layout *layout,
-+ struct nvmem_cell_info *cell);
-
- /* private */
- struct module *owner;
+++ /dev/null
-From 6c56a82d7895a213a43182a5d01a21a906a79847 Mon Sep 17 00:00:00 2001
-From: Michael Walle <michael@walle.cc>
-Date: Tue, 4 Apr 2023 18:21:26 +0100
-Subject: [PATCH] nvmem: imx-ocotp: replace global post processing with layouts
-
-In preparation of retiring the global post processing hook change this
-driver to use layouts. The layout will be supplied during registration
-and will be used to add the post processing hook to all added cells.
-
-Signed-off-by: Michael Walle <michael@walle.cc>
-Tested-by: Michael Walle <michael@walle.cc> # on kontron-pitx-imx8m
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230404172148.82422-19-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/imx-ocotp.c | 30 +++++++++++++++++++-----------
- 1 file changed, 19 insertions(+), 11 deletions(-)
-
---- a/drivers/nvmem/imx-ocotp.c
-+++ b/drivers/nvmem/imx-ocotp.c
-@@ -225,18 +225,13 @@ read_end:
- static int imx_ocotp_cell_pp(void *context, const char *id, int index,
- unsigned int offset, void *data, size_t bytes)
- {
-- struct ocotp_priv *priv = context;
-+ u8 *buf = data;
-+ int i;
-
- /* Deal with some post processing of nvmem cell data */
-- if (id && !strcmp(id, "mac-address")) {
-- if (priv->params->reverse_mac_address) {
-- u8 *buf = data;
-- int i;
--
-- for (i = 0; i < bytes/2; i++)
-- swap(buf[i], buf[bytes - i - 1]);
-- }
-- }
-+ if (id && !strcmp(id, "mac-address"))
-+ for (i = 0; i < bytes / 2; i++)
-+ swap(buf[i], buf[bytes - i - 1]);
-
- return 0;
- }
-@@ -488,7 +483,6 @@ static struct nvmem_config imx_ocotp_nvm
- .stride = 1,
- .reg_read = imx_ocotp_read,
- .reg_write = imx_ocotp_write,
-- .cell_post_process = imx_ocotp_cell_pp,
- };
-
- static const struct ocotp_params imx6q_params = {
-@@ -595,6 +589,17 @@ static const struct of_device_id imx_oco
- };
- MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids);
-
-+static void imx_ocotp_fixup_cell_info(struct nvmem_device *nvmem,
-+ struct nvmem_layout *layout,
-+ struct nvmem_cell_info *cell)
-+{
-+ cell->read_post_process = imx_ocotp_cell_pp;
-+}
-+
-+struct nvmem_layout imx_ocotp_layout = {
-+ .fixup_cell_info = imx_ocotp_fixup_cell_info,
-+};
-+
- static int imx_ocotp_probe(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
-@@ -619,6 +624,9 @@ static int imx_ocotp_probe(struct platfo
- imx_ocotp_nvmem_config.size = 4 * priv->params->nregs;
- imx_ocotp_nvmem_config.dev = dev;
- imx_ocotp_nvmem_config.priv = priv;
-+ if (priv->params->reverse_mac_address)
-+ imx_ocotp_nvmem_config.layout = &imx_ocotp_layout;
-+
- priv->config = &imx_ocotp_nvmem_config;
-
- clk_prepare_enable(priv->clk);
+++ /dev/null
-From 011e40a166fdaa65fb9946b7cd91efec85b70dbb Mon Sep 17 00:00:00 2001
-From: Michael Walle <michael@walle.cc>
-Date: Tue, 4 Apr 2023 18:21:27 +0100
-Subject: [PATCH] nvmem: cell: drop global cell_post_process
-
-There are no users anymore for the global cell_post_process callback
-anymore. New users should use proper nvmem layouts.
-
-Signed-off-by: Michael Walle <michael@walle.cc>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230404172148.82422-20-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/core.c | 9 ---------
- include/linux/nvmem-provider.h | 2 --
- 2 files changed, 11 deletions(-)
-
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -39,7 +39,6 @@ struct nvmem_device {
- unsigned int nkeepout;
- nvmem_reg_read_t reg_read;
- nvmem_reg_write_t reg_write;
-- nvmem_cell_post_process_t cell_post_process;
- struct gpio_desc *wp_gpio;
- struct nvmem_layout *layout;
- void *priv;
-@@ -903,7 +902,6 @@ struct nvmem_device *nvmem_register(cons
- nvmem->type = config->type;
- nvmem->reg_read = config->reg_read;
- nvmem->reg_write = config->reg_write;
-- nvmem->cell_post_process = config->cell_post_process;
- nvmem->keepout = config->keepout;
- nvmem->nkeepout = config->nkeepout;
- if (config->of_node)
-@@ -1575,13 +1573,6 @@ static int __nvmem_cell_read(struct nvme
- if (rc)
- return rc;
- }
--
-- if (nvmem->cell_post_process) {
-- rc = nvmem->cell_post_process(nvmem->priv, id, index,
-- cell->offset, buf, cell->bytes);
-- if (rc)
-- return rc;
-- }
-
- if (len)
- *len = cell->bytes;
---- a/include/linux/nvmem-provider.h
-+++ b/include/linux/nvmem-provider.h
-@@ -85,7 +85,6 @@ struct nvmem_cell_info {
- * @no_of_node: Device should not use the parent's of_node even if it's !NULL.
- * @reg_read: Callback to read data.
- * @reg_write: Callback to write data.
-- * @cell_post_process: Callback for vendor specific post processing of cell data
- * @size: Device size.
- * @word_size: Minimum read/write access granularity.
- * @stride: Minimum read/write access stride.
-@@ -118,7 +117,6 @@ struct nvmem_config {
- bool no_of_node;
- nvmem_reg_read_t reg_read;
- nvmem_reg_write_t reg_write;
-- nvmem_cell_post_process_t cell_post_process;
- int size;
- int word_size;
- int stride;
+++ /dev/null
-From 8a134fd9f9323f4c39ec27055b3d3723cfb5c1e9 Mon Sep 17 00:00:00 2001
-From: Michael Walle <michael@walle.cc>
-Date: Tue, 4 Apr 2023 18:21:28 +0100
-Subject: [PATCH] nvmem: core: provide own priv pointer in post process
- callback
-
-It doesn't make any more sense to have a opaque pointer set up by the
-nvmem device. Usually, the layout isn't associated with a particular
-nvmem device. Instead, let the caller who set the post process callback
-provide the priv pointer.
-
-Signed-off-by: Michael Walle <michael@walle.cc>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230404172148.82422-21-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/core.c | 4 +++-
- include/linux/nvmem-provider.h | 5 ++++-
- 2 files changed, 7 insertions(+), 2 deletions(-)
-
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -54,6 +54,7 @@ struct nvmem_cell_entry {
- int bit_offset;
- int nbits;
- nvmem_cell_post_process_t read_post_process;
-+ void *priv;
- struct device_node *np;
- struct nvmem_device *nvmem;
- struct list_head node;
-@@ -471,6 +472,7 @@ static int nvmem_cell_info_to_nvmem_cell
- cell->bytes = info->bytes;
- cell->name = info->name;
- cell->read_post_process = info->read_post_process;
-+ cell->priv = info->priv;
-
- cell->bit_offset = info->bit_offset;
- cell->nbits = info->nbits;
-@@ -1568,7 +1570,7 @@ static int __nvmem_cell_read(struct nvme
- nvmem_shift_read_buffer_in_place(cell, buf);
-
- if (cell->read_post_process) {
-- rc = cell->read_post_process(nvmem->priv, id, index,
-+ rc = cell->read_post_process(cell->priv, id, index,
- cell->offset, buf, cell->bytes);
- if (rc)
- return rc;
---- a/include/linux/nvmem-provider.h
-+++ b/include/linux/nvmem-provider.h
-@@ -20,7 +20,8 @@ typedef int (*nvmem_reg_write_t)(void *p
- void *val, size_t bytes);
- /* used for vendor specific post processing of cell data */
- typedef int (*nvmem_cell_post_process_t)(void *priv, const char *id, int index,
-- unsigned int offset, void *buf, size_t bytes);
-+ unsigned int offset, void *buf,
-+ size_t bytes);
-
- enum nvmem_type {
- NVMEM_TYPE_UNKNOWN = 0,
-@@ -56,6 +57,7 @@ struct nvmem_keepout {
- * @np: Optional device_node pointer.
- * @read_post_process: Callback for optional post processing of cell data
- * on reads.
-+ * @priv: Opaque data passed to the read_post_process hook.
- */
- struct nvmem_cell_info {
- const char *name;
-@@ -65,6 +67,7 @@ struct nvmem_cell_info {
- unsigned int nbits;
- struct device_node *np;
- nvmem_cell_post_process_t read_post_process;
-+ void *priv;
- };
-
- /**
+++ /dev/null
-From d9fae023fe86069750092fc1c2f3a73e2fb18512 Mon Sep 17 00:00:00 2001
-From: Michael Walle <michael@walle.cc>
-Date: Tue, 4 Apr 2023 18:21:29 +0100
-Subject: [PATCH] nvmem: layouts: sl28vpd: Add new layout driver
-
-This layout applies to the VPD of the Kontron sl28 boards. The VPD only
-contains a base MAC address. Therefore, we have to add an individual
-offset to it. This is done by taking the second argument of the nvmem
-phandle into account. Also this let us checking the VPD version and the
-checksum.
-
-Signed-off-by: Michael Walle <michael@walle.cc>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230404172148.82422-22-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/layouts/Kconfig | 9 ++
- drivers/nvmem/layouts/Makefile | 2 +
- drivers/nvmem/layouts/sl28vpd.c | 165 ++++++++++++++++++++++++++++++++
- 3 files changed, 176 insertions(+)
- create mode 100644 drivers/nvmem/layouts/sl28vpd.c
-
---- a/drivers/nvmem/layouts/Kconfig
-+++ b/drivers/nvmem/layouts/Kconfig
-@@ -2,4 +2,13 @@
-
- menu "Layout Types"
-
-+config NVMEM_LAYOUT_SL28_VPD
-+ tristate "Kontron sl28 VPD layout support"
-+ select CRC8
-+ help
-+ Say Y here if you want to support the VPD layout of the Kontron
-+ SMARC-sAL28 boards.
-+
-+ If unsure, say N.
-+
- endmenu
---- a/drivers/nvmem/layouts/Makefile
-+++ b/drivers/nvmem/layouts/Makefile
-@@ -2,3 +2,5 @@
- #
- # Makefile for nvmem layouts.
- #
-+
-+obj-$(CONFIG_NVMEM_LAYOUT_SL28_VPD) += sl28vpd.o
---- /dev/null
-+++ b/drivers/nvmem/layouts/sl28vpd.c
-@@ -0,0 +1,165 @@
-+// SPDX-License-Identifier: GPL-2.0
-+
-+#include <linux/crc8.h>
-+#include <linux/etherdevice.h>
-+#include <linux/nvmem-consumer.h>
-+#include <linux/nvmem-provider.h>
-+#include <linux/of.h>
-+#include <uapi/linux/if_ether.h>
-+
-+#define SL28VPD_MAGIC 'V'
-+
-+struct sl28vpd_header {
-+ u8 magic;
-+ u8 version;
-+} __packed;
-+
-+struct sl28vpd_v1 {
-+ struct sl28vpd_header header;
-+ char serial_number[15];
-+ u8 base_mac_address[ETH_ALEN];
-+ u8 crc8;
-+} __packed;
-+
-+static int sl28vpd_mac_address_pp(void *priv, const char *id, int index,
-+ unsigned int offset, void *buf,
-+ size_t bytes)
-+{
-+ if (bytes != ETH_ALEN)
-+ return -EINVAL;
-+
-+ if (index < 0)
-+ return -EINVAL;
-+
-+ if (!is_valid_ether_addr(buf))
-+ return -EINVAL;
-+
-+ eth_addr_add(buf, index);
-+
-+ return 0;
-+}
-+
-+static const struct nvmem_cell_info sl28vpd_v1_entries[] = {
-+ {
-+ .name = "serial-number",
-+ .offset = offsetof(struct sl28vpd_v1, serial_number),
-+ .bytes = sizeof_field(struct sl28vpd_v1, serial_number),
-+ },
-+ {
-+ .name = "base-mac-address",
-+ .offset = offsetof(struct sl28vpd_v1, base_mac_address),
-+ .bytes = sizeof_field(struct sl28vpd_v1, base_mac_address),
-+ .read_post_process = sl28vpd_mac_address_pp,
-+ },
-+};
-+
-+static int sl28vpd_v1_check_crc(struct device *dev, struct nvmem_device *nvmem)
-+{
-+ struct sl28vpd_v1 data_v1;
-+ u8 table[CRC8_TABLE_SIZE];
-+ int ret;
-+ u8 crc;
-+
-+ crc8_populate_msb(table, 0x07);
-+
-+ ret = nvmem_device_read(nvmem, 0, sizeof(data_v1), &data_v1);
-+ if (ret < 0)
-+ return ret;
-+ else if (ret != sizeof(data_v1))
-+ return -EIO;
-+
-+ crc = crc8(table, (void *)&data_v1, sizeof(data_v1) - 1, 0);
-+
-+ if (crc != data_v1.crc8) {
-+ dev_err(dev,
-+ "Checksum is invalid (got %02x, expected %02x).\n",
-+ crc, data_v1.crc8);
-+ return -EINVAL;
-+ }
-+
-+ return 0;
-+}
-+
-+static int sl28vpd_add_cells(struct device *dev, struct nvmem_device *nvmem,
-+ struct nvmem_layout *layout)
-+{
-+ const struct nvmem_cell_info *pinfo;
-+ struct nvmem_cell_info info = {0};
-+ struct device_node *layout_np;
-+ struct sl28vpd_header hdr;
-+ int ret, i;
-+
-+ /* check header */
-+ ret = nvmem_device_read(nvmem, 0, sizeof(hdr), &hdr);
-+ if (ret < 0)
-+ return ret;
-+ else if (ret != sizeof(hdr))
-+ return -EIO;
-+
-+ if (hdr.magic != SL28VPD_MAGIC) {
-+ dev_err(dev, "Invalid magic value (%02x)\n", hdr.magic);
-+ return -EINVAL;
-+ }
-+
-+ if (hdr.version != 1) {
-+ dev_err(dev, "Version %d is unsupported.\n", hdr.version);
-+ return -EINVAL;
-+ }
-+
-+ ret = sl28vpd_v1_check_crc(dev, nvmem);
-+ if (ret)
-+ return ret;
-+
-+ layout_np = of_nvmem_layout_get_container(nvmem);
-+ if (!layout_np)
-+ return -ENOENT;
-+
-+ for (i = 0; i < ARRAY_SIZE(sl28vpd_v1_entries); i++) {
-+ pinfo = &sl28vpd_v1_entries[i];
-+
-+ info.name = pinfo->name;
-+ info.offset = pinfo->offset;
-+ info.bytes = pinfo->bytes;
-+ info.read_post_process = pinfo->read_post_process;
-+ info.np = of_get_child_by_name(layout_np, pinfo->name);
-+
-+ ret = nvmem_add_one_cell(nvmem, &info);
-+ if (ret) {
-+ of_node_put(layout_np);
-+ return ret;
-+ }
-+ }
-+
-+ of_node_put(layout_np);
-+
-+ return 0;
-+}
-+
-+static const struct of_device_id sl28vpd_of_match_table[] = {
-+ { .compatible = "kontron,sl28-vpd" },
-+ {},
-+};
-+MODULE_DEVICE_TABLE(of, sl28vpd_of_match_table);
-+
-+struct nvmem_layout sl28vpd_layout = {
-+ .name = "sl28-vpd",
-+ .of_match_table = sl28vpd_of_match_table,
-+ .add_cells = sl28vpd_add_cells,
-+};
-+
-+static int __init sl28vpd_init(void)
-+{
-+ return nvmem_layout_register(&sl28vpd_layout);
-+}
-+
-+static void __exit sl28vpd_exit(void)
-+{
-+ nvmem_layout_unregister(&sl28vpd_layout);
-+}
-+
-+module_init(sl28vpd_init);
-+module_exit(sl28vpd_exit);
-+
-+MODULE_LICENSE("GPL");
-+MODULE_AUTHOR("Michael Walle <michael@walle.cc>");
-+MODULE_DESCRIPTION("NVMEM layout driver for the VPD of Kontron sl28 boards");
+++ /dev/null
-From d3c0d12f6474216bf386101e2449cc73e5c5b61d Mon Sep 17 00:00:00 2001
-From: Miquel Raynal <miquel.raynal@bootlin.com>
-Date: Tue, 4 Apr 2023 18:21:31 +0100
-Subject: [PATCH] nvmem: layouts: onie-tlv: Add new layout driver
-
-This layout applies on top of any non volatile storage device containing
-an ONIE table factory flashed. This table follows the tlv
-(type-length-value) organization described in the link below. We cannot
-afford using regular parsers because the content of these tables is
-manufacturer specific and must be dynamically discovered.
-
-Link: https://opencomputeproject.github.io/onie/design-spec/hw_requirements.html
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230404172148.82422-24-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/layouts/Kconfig | 9 ++
- drivers/nvmem/layouts/Makefile | 1 +
- drivers/nvmem/layouts/onie-tlv.c | 257 +++++++++++++++++++++++++++++++
- 3 files changed, 267 insertions(+)
- create mode 100644 drivers/nvmem/layouts/onie-tlv.c
-
---- a/drivers/nvmem/layouts/Kconfig
-+++ b/drivers/nvmem/layouts/Kconfig
-@@ -11,4 +11,13 @@ config NVMEM_LAYOUT_SL28_VPD
-
- If unsure, say N.
-
-+config NVMEM_LAYOUT_ONIE_TLV
-+ tristate "ONIE tlv support"
-+ select CRC32
-+ help
-+ Say Y here if you want to support the Open Compute Project ONIE
-+ Type-Length-Value standard table.
-+
-+ If unsure, say N.
-+
- endmenu
---- a/drivers/nvmem/layouts/Makefile
-+++ b/drivers/nvmem/layouts/Makefile
-@@ -4,3 +4,4 @@
- #
-
- obj-$(CONFIG_NVMEM_LAYOUT_SL28_VPD) += sl28vpd.o
-+obj-$(CONFIG_NVMEM_LAYOUT_ONIE_TLV) += onie-tlv.o
---- /dev/null
-+++ b/drivers/nvmem/layouts/onie-tlv.c
-@@ -0,0 +1,257 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+/*
-+ * ONIE tlv NVMEM cells provider
-+ *
-+ * Copyright (C) 2022 Open Compute Group ONIE
-+ * Author: Miquel Raynal <miquel.raynal@bootlin.com>
-+ * Based on the nvmem driver written by: Vadym Kochan <vadym.kochan@plvision.eu>
-+ * Inspired by the first layout written by: Rafał Miłecki <rafal@milecki.pl>
-+ */
-+
-+#include <linux/crc32.h>
-+#include <linux/etherdevice.h>
-+#include <linux/nvmem-consumer.h>
-+#include <linux/nvmem-provider.h>
-+#include <linux/of.h>
-+
-+#define ONIE_TLV_MAX_LEN 2048
-+#define ONIE_TLV_CRC_FIELD_SZ 6
-+#define ONIE_TLV_CRC_SZ 4
-+#define ONIE_TLV_HDR_ID "TlvInfo"
-+
-+struct onie_tlv_hdr {
-+ u8 id[8];
-+ u8 version;
-+ __be16 data_len;
-+} __packed;
-+
-+struct onie_tlv {
-+ u8 type;
-+ u8 len;
-+} __packed;
-+
-+static const char *onie_tlv_cell_name(u8 type)
-+{
-+ switch (type) {
-+ case 0x21:
-+ return "product-name";
-+ case 0x22:
-+ return "part-number";
-+ case 0x23:
-+ return "serial-number";
-+ case 0x24:
-+ return "mac-address";
-+ case 0x25:
-+ return "manufacture-date";
-+ case 0x26:
-+ return "device-version";
-+ case 0x27:
-+ return "label-revision";
-+ case 0x28:
-+ return "platform-name";
-+ case 0x29:
-+ return "onie-version";
-+ case 0x2A:
-+ return "num-macs";
-+ case 0x2B:
-+ return "manufacturer";
-+ case 0x2C:
-+ return "country-code";
-+ case 0x2D:
-+ return "vendor";
-+ case 0x2E:
-+ return "diag-version";
-+ case 0x2F:
-+ return "service-tag";
-+ case 0xFD:
-+ return "vendor-extension";
-+ case 0xFE:
-+ return "crc32";
-+ default:
-+ break;
-+ }
-+
-+ return NULL;
-+}
-+
-+static int onie_tlv_mac_read_cb(void *priv, const char *id, int index,
-+ unsigned int offset, void *buf,
-+ size_t bytes)
-+{
-+ eth_addr_add(buf, index);
-+
-+ return 0;
-+}
-+
-+static nvmem_cell_post_process_t onie_tlv_read_cb(u8 type, u8 *buf)
-+{
-+ switch (type) {
-+ case 0x24:
-+ return &onie_tlv_mac_read_cb;
-+ default:
-+ break;
-+ }
-+
-+ return NULL;
-+}
-+
-+static int onie_tlv_add_cells(struct device *dev, struct nvmem_device *nvmem,
-+ size_t data_len, u8 *data)
-+{
-+ struct nvmem_cell_info cell = {};
-+ struct device_node *layout;
-+ struct onie_tlv tlv;
-+ unsigned int hdr_len = sizeof(struct onie_tlv_hdr);
-+ unsigned int offset = 0;
-+ int ret;
-+
-+ layout = of_nvmem_layout_get_container(nvmem);
-+ if (!layout)
-+ return -ENOENT;
-+
-+ while (offset < data_len) {
-+ memcpy(&tlv, data + offset, sizeof(tlv));
-+ if (offset + tlv.len >= data_len) {
-+ dev_err(dev, "Out of bounds field (0x%x bytes at 0x%x)\n",
-+ tlv.len, hdr_len + offset);
-+ break;
-+ }
-+
-+ cell.name = onie_tlv_cell_name(tlv.type);
-+ if (!cell.name)
-+ continue;
-+
-+ cell.offset = hdr_len + offset + sizeof(tlv.type) + sizeof(tlv.len);
-+ cell.bytes = tlv.len;
-+ cell.np = of_get_child_by_name(layout, cell.name);
-+ cell.read_post_process = onie_tlv_read_cb(tlv.type, data + offset + sizeof(tlv));
-+
-+ ret = nvmem_add_one_cell(nvmem, &cell);
-+ if (ret) {
-+ of_node_put(layout);
-+ return ret;
-+ }
-+
-+ offset += sizeof(tlv) + tlv.len;
-+ }
-+
-+ of_node_put(layout);
-+
-+ return 0;
-+}
-+
-+static bool onie_tlv_hdr_is_valid(struct device *dev, struct onie_tlv_hdr *hdr)
-+{
-+ if (memcmp(hdr->id, ONIE_TLV_HDR_ID, sizeof(hdr->id))) {
-+ dev_err(dev, "Invalid header\n");
-+ return false;
-+ }
-+
-+ if (hdr->version != 0x1) {
-+ dev_err(dev, "Invalid version number\n");
-+ return false;
-+ }
-+
-+ return true;
-+}
-+
-+static bool onie_tlv_crc_is_valid(struct device *dev, size_t table_len, u8 *table)
-+{
-+ struct onie_tlv crc_hdr;
-+ u32 read_crc, calc_crc;
-+ __be32 crc_be;
-+
-+ memcpy(&crc_hdr, table + table_len - ONIE_TLV_CRC_FIELD_SZ, sizeof(crc_hdr));
-+ if (crc_hdr.type != 0xfe || crc_hdr.len != ONIE_TLV_CRC_SZ) {
-+ dev_err(dev, "Invalid CRC field\n");
-+ return false;
-+ }
-+
-+ /* The table contains a JAMCRC, which is XOR'ed compared to the original
-+ * CRC32 implementation as known in the Ethernet world.
-+ */
-+ memcpy(&crc_be, table + table_len - ONIE_TLV_CRC_SZ, ONIE_TLV_CRC_SZ);
-+ read_crc = be32_to_cpu(crc_be);
-+ calc_crc = crc32(~0, table, table_len - ONIE_TLV_CRC_SZ) ^ 0xFFFFFFFF;
-+ if (read_crc != calc_crc) {
-+ dev_err(dev, "Invalid CRC read: 0x%08x, expected: 0x%08x\n",
-+ read_crc, calc_crc);
-+ return false;
-+ }
-+
-+ return true;
-+}
-+
-+static int onie_tlv_parse_table(struct device *dev, struct nvmem_device *nvmem,
-+ struct nvmem_layout *layout)
-+{
-+ struct onie_tlv_hdr hdr;
-+ size_t table_len, data_len, hdr_len;
-+ u8 *table, *data;
-+ int ret;
-+
-+ ret = nvmem_device_read(nvmem, 0, sizeof(hdr), &hdr);
-+ if (ret < 0)
-+ return ret;
-+
-+ if (!onie_tlv_hdr_is_valid(dev, &hdr)) {
-+ dev_err(dev, "Invalid ONIE TLV header\n");
-+ return -EINVAL;
-+ }
-+
-+ hdr_len = sizeof(hdr.id) + sizeof(hdr.version) + sizeof(hdr.data_len);
-+ data_len = be16_to_cpu(hdr.data_len);
-+ table_len = hdr_len + data_len;
-+ if (table_len > ONIE_TLV_MAX_LEN) {
-+ dev_err(dev, "Invalid ONIE TLV data length\n");
-+ return -EINVAL;
-+ }
-+
-+ table = devm_kmalloc(dev, table_len, GFP_KERNEL);
-+ if (!table)
-+ return -ENOMEM;
-+
-+ ret = nvmem_device_read(nvmem, 0, table_len, table);
-+ if (ret != table_len)
-+ return ret;
-+
-+ if (!onie_tlv_crc_is_valid(dev, table_len, table))
-+ return -EINVAL;
-+
-+ data = table + hdr_len;
-+ ret = onie_tlv_add_cells(dev, nvmem, data_len, data);
-+ if (ret)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+static const struct of_device_id onie_tlv_of_match_table[] = {
-+ { .compatible = "onie,tlv-layout", },
-+ {},
-+};
-+MODULE_DEVICE_TABLE(of, onie_tlv_of_match_table);
-+
-+static struct nvmem_layout onie_tlv_layout = {
-+ .name = "ONIE tlv layout",
-+ .of_match_table = onie_tlv_of_match_table,
-+ .add_cells = onie_tlv_parse_table,
-+};
-+
-+static int __init onie_tlv_init(void)
-+{
-+ return nvmem_layout_register(&onie_tlv_layout);
-+}
-+
-+static void __exit onie_tlv_exit(void)
-+{
-+ nvmem_layout_unregister(&onie_tlv_layout);
-+}
-+
-+module_init(onie_tlv_init);
-+module_exit(onie_tlv_exit);
-+
-+MODULE_LICENSE("GPL");
-+MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
-+MODULE_DESCRIPTION("NVMEM layout driver for Onie TLV table parsing");
-+MODULE_ALIAS("NVMEM layout driver for Onie TLV table parsing");
+++ /dev/null
-From a4fb434ef96ace5af758ca2c52c3a3f8f3abc87c Mon Sep 17 00:00:00 2001
-From: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
-Date: Tue, 4 Apr 2023 18:21:34 +0100
-Subject: [PATCH] nvmem: stm32-romem: mark OF related data as maybe unused
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The driver can be compile tested with !CONFIG_OF making certain data
-unused:
-
- drivers/nvmem/stm32-romem.c:271:34: error: ‘stm32_romem_of_match’ defined but not used [-Werror=unused-const-variable=]
-
-Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230404172148.82422-27-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/stm32-romem.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/nvmem/stm32-romem.c
-+++ b/drivers/nvmem/stm32-romem.c
-@@ -268,7 +268,7 @@ static const struct stm32_romem_cfg stm3
- .ta = true,
- };
-
--static const struct of_device_id stm32_romem_of_match[] = {
-+static const struct of_device_id stm32_romem_of_match[] __maybe_unused = {
- { .compatible = "st,stm32f4-otp", }, {
- .compatible = "st,stm32mp15-bsec",
- .data = (void *)&stm32mp15_bsec_cfg,
+++ /dev/null
-From de6e05097f7db066afb0ad4c88b730949f7b7749 Mon Sep 17 00:00:00 2001
-From: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
-Date: Tue, 4 Apr 2023 18:21:35 +0100
-Subject: [PATCH] nvmem: mtk-efuse: Support postprocessing for GPU speed
- binning data
-
-On some MediaTek SoCs GPU speed binning data is available for read
-in the SoC's eFuse array but it has a format that is incompatible
-with what the OPP API expects, as we read a number from 0 to 7 but
-opp-supported-hw is expecting a bitmask to enable an OPP entry:
-being what we read limited to 0-7, it's straightforward to simply
-convert the value to BIT(value) as a post-processing action.
-
-So, introduce post-processing support and enable it by evaluating
-the newly introduced platform data's `uses_post_processing` member,
-currently enabled only for MT8186.
-
-Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230404172148.82422-28-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/mtk-efuse.c | 53 +++++++++++++++++++++++++++++++++++++--
- 1 file changed, 51 insertions(+), 2 deletions(-)
-
---- a/drivers/nvmem/mtk-efuse.c
-+++ b/drivers/nvmem/mtk-efuse.c
-@@ -10,6 +10,11 @@
- #include <linux/io.h>
- #include <linux/nvmem-provider.h>
- #include <linux/platform_device.h>
-+#include <linux/property.h>
-+
-+struct mtk_efuse_pdata {
-+ bool uses_post_processing;
-+};
-
- struct mtk_efuse_priv {
- void __iomem *base;
-@@ -29,6 +34,37 @@ static int mtk_reg_read(void *context,
- return 0;
- }
-
-+static int mtk_efuse_gpu_speedbin_pp(void *context, const char *id, int index,
-+ unsigned int offset, void *data, size_t bytes)
-+{
-+ u8 *val = data;
-+
-+ if (val[0] < 8)
-+ val[0] = BIT(val[0]);
-+
-+ return 0;
-+}
-+
-+static void mtk_efuse_fixup_cell_info(struct nvmem_device *nvmem,
-+ struct nvmem_layout *layout,
-+ struct nvmem_cell_info *cell)
-+{
-+ size_t sz = strlen(cell->name);
-+
-+ /*
-+ * On some SoCs, the GPU speedbin is not read as bitmask but as
-+ * a number with range [0-7] (max 3 bits): post process to use
-+ * it in OPP tables to describe supported-hw.
-+ */
-+ if (cell->nbits <= 3 &&
-+ strncmp(cell->name, "gpu-speedbin", min(sz, strlen("gpu-speedbin"))) == 0)
-+ cell->read_post_process = mtk_efuse_gpu_speedbin_pp;
-+}
-+
-+static struct nvmem_layout mtk_efuse_layout = {
-+ .fixup_cell_info = mtk_efuse_fixup_cell_info,
-+};
-+
- static int mtk_efuse_probe(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
-@@ -36,6 +72,7 @@ static int mtk_efuse_probe(struct platfo
- struct nvmem_device *nvmem;
- struct nvmem_config econfig = {};
- struct mtk_efuse_priv *priv;
-+ const struct mtk_efuse_pdata *pdata;
-
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
-@@ -45,20 +82,32 @@ static int mtk_efuse_probe(struct platfo
- if (IS_ERR(priv->base))
- return PTR_ERR(priv->base);
-
-+ pdata = device_get_match_data(dev);
- econfig.stride = 1;
- econfig.word_size = 1;
- econfig.reg_read = mtk_reg_read;
- econfig.size = resource_size(res);
- econfig.priv = priv;
- econfig.dev = dev;
-+ if (pdata->uses_post_processing)
-+ econfig.layout = &mtk_efuse_layout;
- nvmem = devm_nvmem_register(dev, &econfig);
-
- return PTR_ERR_OR_ZERO(nvmem);
- }
-
-+static const struct mtk_efuse_pdata mtk_mt8186_efuse_pdata = {
-+ .uses_post_processing = true,
-+};
-+
-+static const struct mtk_efuse_pdata mtk_efuse_pdata = {
-+ .uses_post_processing = false,
-+};
-+
- static const struct of_device_id mtk_efuse_of_match[] = {
-- { .compatible = "mediatek,mt8173-efuse",},
-- { .compatible = "mediatek,efuse",},
-+ { .compatible = "mediatek,mt8173-efuse", .data = &mtk_efuse_pdata },
-+ { .compatible = "mediatek,mt8186-efuse", .data = &mtk_mt8186_efuse_pdata },
-+ { .compatible = "mediatek,efuse", .data = &mtk_efuse_pdata },
- {/* sentinel */},
- };
- MODULE_DEVICE_TABLE(of, mtk_efuse_of_match);
+++ /dev/null
-From 1dc552fa33cf98af3e784dbc0500da93cae3b24a Mon Sep 17 00:00:00 2001
-From: Yang Li <yang.lee@linux.alibaba.com>
-Date: Tue, 4 Apr 2023 18:21:38 +0100
-Subject: [PATCH] nvmem: bcm-ocotp: Use devm_platform_ioremap_resource()
-
-According to commit 7945f929f1a7 ("drivers: provide
-devm_platform_ioremap_resource()"), convert platform_get_resource(),
-devm_ioremap_resource() to a single call to use
-devm_platform_ioremap_resource(), as this is exactly what this function
-does.
-
-Signed-off-by: Yang Li <yang.lee@linux.alibaba.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230404172148.82422-31-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/bcm-ocotp.c | 4 +---
- 1 file changed, 1 insertion(+), 3 deletions(-)
-
---- a/drivers/nvmem/bcm-ocotp.c
-+++ b/drivers/nvmem/bcm-ocotp.c
-@@ -244,7 +244,6 @@ MODULE_DEVICE_TABLE(acpi, bcm_otpc_acpi_
- static int bcm_otpc_probe(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
-- struct resource *res;
- struct otpc_priv *priv;
- struct nvmem_device *nvmem;
- int err;
-@@ -259,8 +258,7 @@ static int bcm_otpc_probe(struct platfor
- return -ENODEV;
-
- /* Get OTP base address register. */
-- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-- priv->base = devm_ioremap_resource(dev, res);
-+ priv->base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(priv->base)) {
- dev_err(dev, "unable to map I/O memory\n");
- return PTR_ERR(priv->base);
+++ /dev/null
-From 649409990d2e93fac657be7c6960c28a2c601d65 Mon Sep 17 00:00:00 2001
-From: Yang Li <yang.lee@linux.alibaba.com>
-Date: Tue, 4 Apr 2023 18:21:39 +0100
-Subject: [PATCH] nvmem: nintendo-otp: Use devm_platform_ioremap_resource()
-
-According to commit 7945f929f1a7 ("drivers: provide
-devm_platform_ioremap_resource()"), convert platform_get_resource(),
-devm_ioremap_resource() to a single call to use
-devm_platform_ioremap_resource(), as this is exactly what this function
-does.
-
-Signed-off-by: Yang Li <yang.lee@linux.alibaba.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230404172148.82422-32-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/nintendo-otp.c | 4 +---
- 1 file changed, 1 insertion(+), 3 deletions(-)
-
---- a/drivers/nvmem/nintendo-otp.c
-+++ b/drivers/nvmem/nintendo-otp.c
-@@ -76,7 +76,6 @@ static int nintendo_otp_probe(struct pla
- struct device *dev = &pdev->dev;
- const struct of_device_id *of_id =
- of_match_device(nintendo_otp_of_table, dev);
-- struct resource *res;
- struct nvmem_device *nvmem;
- struct nintendo_otp_priv *priv;
-
-@@ -92,8 +91,7 @@ static int nintendo_otp_probe(struct pla
- if (!priv)
- return -ENOMEM;
-
-- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-- priv->regs = devm_ioremap_resource(dev, res);
-+ priv->regs = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(priv->regs))
- return PTR_ERR(priv->regs);
-
+++ /dev/null
-From c2367aa60d5e34d48582362c6de34b4131d92be7 Mon Sep 17 00:00:00 2001
-From: Yang Li <yang.lee@linux.alibaba.com>
-Date: Tue, 4 Apr 2023 18:21:40 +0100
-Subject: [PATCH] nvmem: vf610-ocotp: Use
- devm_platform_get_and_ioremap_resource()
-
-According to commit 890cc39a8799 ("drivers: provide
-devm_platform_get_and_ioremap_resource()"), convert
-platform_get_resource(), devm_ioremap_resource() to a single
-call to devm_platform_get_and_ioremap_resource(), as this is exactly
-what this function does.
-
-Signed-off-by: Yang Li <yang.lee@linux.alibaba.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230404172148.82422-33-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/vf610-ocotp.c | 3 +--
- 1 file changed, 1 insertion(+), 2 deletions(-)
-
---- a/drivers/nvmem/vf610-ocotp.c
-+++ b/drivers/nvmem/vf610-ocotp.c
-@@ -219,8 +219,7 @@ static int vf610_ocotp_probe(struct plat
- if (!ocotp_dev)
- return -ENOMEM;
-
-- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-- ocotp_dev->base = devm_ioremap_resource(dev, res);
-+ ocotp_dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
- if (IS_ERR(ocotp_dev->base))
- return PTR_ERR(ocotp_dev->base);
-
+++ /dev/null
-From 55d4980ce55b6bb4be66877de4dbec513911b988 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
-Date: Tue, 4 Apr 2023 18:21:42 +0100
-Subject: [PATCH] nvmem: core: support specifying both: cell raw data & post
- read lengths
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Callback .read_post_process() is designed to modify raw cell content
-before providing it to the consumer. So far we were dealing with
-modifications that didn't affect cell size (length). In some cases
-however cell content needs to be reformatted and resized.
-
-It's required e.g. to provide properly formatted MAC address in case
-it's stored in a non-binary format (e.g. using ASCII).
-
-There were few discussions how to optimally handle that. Following
-possible solutions were considered:
-1. Allow .read_post_process() to realloc (resize) content buffer
-2. Allow .read_post_process() to adjust (decrease) just buffer length
-3. Register NVMEM cells using post-read sizes
-
-The preferred solution was the last one. The problem is that simply
-adjusting "bytes" in NVMEM providers would result in core code NOT
-passing whole raw data to .read_post_process() callbacks. It means
-callback functions couldn't do their job without somehow manually
-reading original cell content on their own.
-
-This patch deals with that by registering NVMEM cells with both lengths:
-raw content one and post read one. It allows:
-1. Core code to read whole raw cell content
-2. Callbacks to return content they want
-
-Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230404172148.82422-35-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/core.c | 11 +++++++----
- include/linux/nvmem-provider.h | 2 ++
- 2 files changed, 9 insertions(+), 4 deletions(-)
-
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -50,6 +50,7 @@ struct nvmem_device {
- struct nvmem_cell_entry {
- const char *name;
- int offset;
-+ size_t raw_len;
- int bytes;
- int bit_offset;
- int nbits;
-@@ -469,6 +470,7 @@ static int nvmem_cell_info_to_nvmem_cell
- {
- cell->nvmem = nvmem;
- cell->offset = info->offset;
-+ cell->raw_len = info->raw_len ?: info->bytes;
- cell->bytes = info->bytes;
- cell->name = info->name;
- cell->read_post_process = info->read_post_process;
-@@ -1560,7 +1562,7 @@ static int __nvmem_cell_read(struct nvme
- {
- int rc;
-
-- rc = nvmem_reg_read(nvmem, cell->offset, buf, cell->bytes);
-+ rc = nvmem_reg_read(nvmem, cell->offset, buf, cell->raw_len);
-
- if (rc)
- return rc;
-@@ -1571,7 +1573,7 @@ static int __nvmem_cell_read(struct nvme
-
- if (cell->read_post_process) {
- rc = cell->read_post_process(cell->priv, id, index,
-- cell->offset, buf, cell->bytes);
-+ cell->offset, buf, cell->raw_len);
- if (rc)
- return rc;
- }
-@@ -1594,14 +1596,15 @@ static int __nvmem_cell_read(struct nvme
- */
- void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len)
- {
-- struct nvmem_device *nvmem = cell->entry->nvmem;
-+ struct nvmem_cell_entry *entry = cell->entry;
-+ struct nvmem_device *nvmem = entry->nvmem;
- u8 *buf;
- int rc;
-
- if (!nvmem)
- return ERR_PTR(-EINVAL);
-
-- buf = kzalloc(cell->entry->bytes, GFP_KERNEL);
-+ buf = kzalloc(max_t(size_t, entry->raw_len, entry->bytes), GFP_KERNEL);
- if (!buf)
- return ERR_PTR(-ENOMEM);
-
---- a/include/linux/nvmem-provider.h
-+++ b/include/linux/nvmem-provider.h
-@@ -51,6 +51,7 @@ struct nvmem_keepout {
- * struct nvmem_cell_info - NVMEM cell description
- * @name: Name.
- * @offset: Offset within the NVMEM device.
-+ * @raw_len: Length of raw data (without post processing).
- * @bytes: Length of the cell.
- * @bit_offset: Bit offset if cell is smaller than a byte.
- * @nbits: Number of bits.
-@@ -62,6 +63,7 @@ struct nvmem_keepout {
- struct nvmem_cell_info {
- const char *name;
- unsigned int offset;
-+ size_t raw_len;
- unsigned int bytes;
- unsigned int bit_offset;
- unsigned int nbits;
+++ /dev/null
-From c49f1a8af6bcf6d18576bca898f8083ca4b129e1 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
-Date: Tue, 4 Apr 2023 18:21:43 +0100
-Subject: [PATCH] nvmem: u-boot-env: post-process "ethaddr" env variable
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-U-Boot environment variables are stored in ASCII format so "ethaddr"
-requires parsing into binary to make it work with Ethernet interfaces.
-
-This includes support for indexes to support #nvmem-cell-cells = <1>.
-
-Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230404172148.82422-36-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/Kconfig | 1 +
- drivers/nvmem/u-boot-env.c | 26 ++++++++++++++++++++++++++
- 2 files changed, 27 insertions(+)
-
---- a/drivers/nvmem/Kconfig
-+++ b/drivers/nvmem/Kconfig
-@@ -340,6 +340,7 @@ config NVMEM_U_BOOT_ENV
- tristate "U-Boot environment variables support"
- depends on OF && MTD
- select CRC32
-+ select GENERIC_NET_UTILS
- help
- U-Boot stores its setup as environment variables. This driver adds
- support for verifying & exporting such data. It also exposes variables
---- a/drivers/nvmem/u-boot-env.c
-+++ b/drivers/nvmem/u-boot-env.c
-@@ -4,6 +4,8 @@
- */
-
- #include <linux/crc32.h>
-+#include <linux/etherdevice.h>
-+#include <linux/if_ether.h>
- #include <linux/mod_devicetable.h>
- #include <linux/module.h>
- #include <linux/mtd/mtd.h>
-@@ -70,6 +72,25 @@ static int u_boot_env_read(void *context
- return 0;
- }
-
-+static int u_boot_env_read_post_process_ethaddr(void *context, const char *id, int index,
-+ unsigned int offset, void *buf, size_t bytes)
-+{
-+ u8 mac[ETH_ALEN];
-+
-+ if (bytes != 3 * ETH_ALEN - 1)
-+ return -EINVAL;
-+
-+ if (!mac_pton(buf, mac))
-+ return -EINVAL;
-+
-+ if (index)
-+ eth_addr_add(mac, index);
-+
-+ ether_addr_copy(buf, mac);
-+
-+ return 0;
-+}
-+
- static int u_boot_env_add_cells(struct u_boot_env *priv, uint8_t *buf,
- size_t data_offset, size_t data_len)
- {
-@@ -101,6 +122,11 @@ static int u_boot_env_add_cells(struct u
- priv->cells[idx].offset = data_offset + value - data;
- priv->cells[idx].bytes = strlen(value);
- priv->cells[idx].np = of_get_child_by_name(dev->of_node, priv->cells[idx].name);
-+ if (!strcmp(var, "ethaddr")) {
-+ priv->cells[idx].raw_len = strlen(value);
-+ priv->cells[idx].bytes = ETH_ALEN;
-+ priv->cells[idx].read_post_process = u_boot_env_read_post_process_ethaddr;
-+ }
- }
-
- if (WARN_ON(idx != priv->ncells))
+++ /dev/null
-From 814c978f02db17f16e6aa2efa2a929372f06da09 Mon Sep 17 00:00:00 2001
-From: Miquel Raynal <miquel.raynal@bootlin.com>
-Date: Tue, 4 Apr 2023 18:21:44 +0100
-Subject: [PATCH] nvmem: Add macro to register nvmem layout drivers
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Provide a module_nvmem_layout_driver() macro at the end of the
-nvmem-provider.h header to reduce the boilerplate when registering nvmem
-layout drivers.
-
-Suggested-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Acked-by: Rafał Miłecki <rafal@milecki.pl>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230404172148.82422-37-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- include/linux/nvmem-provider.h | 6 ++++++
- 1 file changed, 6 insertions(+)
-
---- a/include/linux/nvmem-provider.h
-+++ b/include/linux/nvmem-provider.h
-@@ -9,6 +9,7 @@
- #ifndef _LINUX_NVMEM_PROVIDER_H
- #define _LINUX_NVMEM_PROVIDER_H
-
-+#include <linux/device/driver.h>
- #include <linux/err.h>
- #include <linux/errno.h>
- #include <linux/gpio/consumer.h>
-@@ -242,4 +243,9 @@ nvmem_layout_get_match_data(struct nvmem
- }
-
- #endif /* CONFIG_NVMEM */
-+
-+#define module_nvmem_layout_driver(__layout_driver) \
-+ module_driver(__layout_driver, nvmem_layout_register, \
-+ nvmem_layout_unregister)
-+
- #endif /* ifndef _LINUX_NVMEM_PROVIDER_H */
+++ /dev/null
-From 0abdf99fe0c86252ba274703425f8d543d7e7f0d Mon Sep 17 00:00:00 2001
-From: Miquel Raynal <miquel.raynal@bootlin.com>
-Date: Tue, 4 Apr 2023 18:21:45 +0100
-Subject: [PATCH] nvmem: layouts: sl28vpd: Use module_nvmem_layout_driver()
-
-Stop open-coding the module init/exit functions. Use the
-module_nvmem_layout_driver() instead.
-
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230404172148.82422-38-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/layouts/sl28vpd.c | 14 +-------------
- 1 file changed, 1 insertion(+), 13 deletions(-)
-
---- a/drivers/nvmem/layouts/sl28vpd.c
-+++ b/drivers/nvmem/layouts/sl28vpd.c
-@@ -146,19 +146,7 @@ struct nvmem_layout sl28vpd_layout = {
- .of_match_table = sl28vpd_of_match_table,
- .add_cells = sl28vpd_add_cells,
- };
--
--static int __init sl28vpd_init(void)
--{
-- return nvmem_layout_register(&sl28vpd_layout);
--}
--
--static void __exit sl28vpd_exit(void)
--{
-- nvmem_layout_unregister(&sl28vpd_layout);
--}
--
--module_init(sl28vpd_init);
--module_exit(sl28vpd_exit);
-+module_nvmem_layout_driver(sl28vpd_layout);
-
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("Michael Walle <michael@walle.cc>");
+++ /dev/null
-From d119eb38faab61125aaa4f63c74eef61585cf34c Mon Sep 17 00:00:00 2001
-From: Miquel Raynal <miquel.raynal@bootlin.com>
-Date: Tue, 4 Apr 2023 18:21:46 +0100
-Subject: [PATCH] nvmem: layouts: onie-tlv: Use module_nvmem_layout_driver()
-
-Stop open-coding the module init/exit functions. Use the
-module_nvmem_layout_driver() instead.
-
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230404172148.82422-39-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/layouts/onie-tlv.c | 14 +-------------
- 1 file changed, 1 insertion(+), 13 deletions(-)
-
---- a/drivers/nvmem/layouts/onie-tlv.c
-+++ b/drivers/nvmem/layouts/onie-tlv.c
-@@ -237,19 +237,7 @@ static struct nvmem_layout onie_tlv_layo
- .of_match_table = onie_tlv_of_match_table,
- .add_cells = onie_tlv_parse_table,
- };
--
--static int __init onie_tlv_init(void)
--{
-- return nvmem_layout_register(&onie_tlv_layout);
--}
--
--static void __exit onie_tlv_exit(void)
--{
-- nvmem_layout_unregister(&onie_tlv_layout);
--}
--
--module_init(onie_tlv_init);
--module_exit(onie_tlv_exit);
-+module_nvmem_layout_driver(onie_tlv_layout);
-
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
+++ /dev/null
-From 6b13e4b6a9a45028ac730e550380077df1845912 Mon Sep 17 00:00:00 2001
-From: Miquel Raynal <miquel.raynal@bootlin.com>
-Date: Tue, 4 Apr 2023 18:21:47 +0100
-Subject: [PATCH] nvmem: layouts: onie-tlv: Drop wrong module alias
-
-The MODULE_ALIAS macro is misused here as it carries the
-description. There is currently no relevant alias to provide so let's
-just drop it.
-
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230404172148.82422-40-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/layouts/onie-tlv.c | 1 -
- 1 file changed, 1 deletion(-)
-
---- a/drivers/nvmem/layouts/onie-tlv.c
-+++ b/drivers/nvmem/layouts/onie-tlv.c
-@@ -242,4 +242,3 @@ module_nvmem_layout_driver(onie_tlv_layo
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
- MODULE_DESCRIPTION("NVMEM layout driver for Onie TLV table parsing");
--MODULE_ALIAS("NVMEM layout driver for Onie TLV table parsing");
+++ /dev/null
-From a8642cd11635a35a5f1dc31857887900d6610778 Mon Sep 17 00:00:00 2001
-From: Tom Rix <trix@redhat.com>
-Date: Tue, 4 Apr 2023 18:21:48 +0100
-Subject: [PATCH] nvmem: layouts: sl28vpd: set varaiable sl28vpd_layout
- storage-class-specifier to static
-
-smatch reports
-drivers/nvmem/layouts/sl28vpd.c:144:21: warning: symbol
- 'sl28vpd_layout' was not declared. Should it be static?
-
-This variable is only used in one file so it should be static.
-
-Signed-off-by: Tom Rix <trix@redhat.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230404172148.82422-41-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/layouts/sl28vpd.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/nvmem/layouts/sl28vpd.c
-+++ b/drivers/nvmem/layouts/sl28vpd.c
-@@ -141,7 +141,7 @@ static const struct of_device_id sl28vpd
- };
- MODULE_DEVICE_TABLE(of, sl28vpd_of_match_table);
-
--struct nvmem_layout sl28vpd_layout = {
-+static struct nvmem_layout sl28vpd_layout = {
- .name = "sl28-vpd",
- .of_match_table = sl28vpd_of_match_table,
- .add_cells = sl28vpd_add_cells,
+++ /dev/null
-From a5be5ce0e25439fae3cd42e3d775979547926812 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
-Date: Thu, 3 Nov 2022 09:25:29 +0100
-Subject: [PATCH] firmware/nvram: bcm47xx: support init from IO memory
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Provide NVMEM content to the NVRAM driver from a simple
-memory resource. This is necessary to use NVRAM in a memory-
-mapped flash device. Patch taken from OpenWrts development
-tree.
-
-This patch makes it possible to use memory-mapped NVRAM
-on the D-Link DWL-8610AP and the D-Link DIR-890L.
-
-Cc: Hauke Mehrtens <hauke@hauke-m.de>
-Cc: linux-mips@vger.kernel.org
-Cc: Florian Fainelli <f.fainelli@gmail.com>
-Cc: bcm-kernel-feedback-list@broadcom.com
-Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
-Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
-[Added an export for modules potentially using the init symbol]
-Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
-Link: https://lore.kernel.org/r/20221103082529.359084-1-linus.walleij@linaro.org
-Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
----
- drivers/firmware/broadcom/bcm47xx_nvram.c | 18 ++++++++++++++++++
- drivers/nvmem/brcm_nvram.c | 3 +++
- include/linux/bcm47xx_nvram.h | 6 ++++++
- 3 files changed, 27 insertions(+)
-
---- a/drivers/firmware/broadcom/bcm47xx_nvram.c
-+++ b/drivers/firmware/broadcom/bcm47xx_nvram.c
-@@ -110,6 +110,24 @@ found:
- return 0;
- }
-
-+int bcm47xx_nvram_init_from_iomem(void __iomem *nvram_start, size_t res_size)
-+{
-+ if (nvram_len) {
-+ pr_warn("nvram already initialized\n");
-+ return -EEXIST;
-+ }
-+
-+ if (!bcm47xx_nvram_is_valid(nvram_start)) {
-+ pr_err("No valid NVRAM found\n");
-+ return -ENOENT;
-+ }
-+
-+ bcm47xx_nvram_copy(nvram_start, res_size);
-+
-+ return 0;
-+}
-+EXPORT_SYMBOL_GPL(bcm47xx_nvram_init_from_iomem);
-+
- /*
- * On bcm47xx we need access to the NVRAM very early, so we can't use mtd
- * subsystem to access flash. We can't even use platform device / driver to
---- a/drivers/nvmem/brcm_nvram.c
-+++ b/drivers/nvmem/brcm_nvram.c
-@@ -3,6 +3,7 @@
- * Copyright (C) 2021 Rafał Miłecki <rafal@milecki.pl>
- */
-
-+#include <linux/bcm47xx_nvram.h>
- #include <linux/io.h>
- #include <linux/mod_devicetable.h>
- #include <linux/module.h>
-@@ -139,6 +140,8 @@ static int brcm_nvram_probe(struct platf
- if (err)
- return err;
-
-+ bcm47xx_nvram_init_from_iomem(priv->base, resource_size(res));
-+
- config.dev = dev;
- config.cells = priv->cells;
- config.ncells = priv->ncells;
---- a/include/linux/bcm47xx_nvram.h
-+++ b/include/linux/bcm47xx_nvram.h
-@@ -11,6 +11,7 @@
- #include <linux/vmalloc.h>
-
- #ifdef CONFIG_BCM47XX_NVRAM
-+int bcm47xx_nvram_init_from_iomem(void __iomem *nvram_start, size_t res_size);
- int bcm47xx_nvram_init_from_mem(u32 base, u32 lim);
- int bcm47xx_nvram_getenv(const char *name, char *val, size_t val_len);
- int bcm47xx_nvram_gpio_pin(const char *name);
-@@ -20,6 +21,11 @@ static inline void bcm47xx_nvram_release
- vfree(nvram);
- };
- #else
-+static inline int bcm47xx_nvram_init_from_iomem(void __iomem *nvram_start,
-+ size_t res_size)
-+{
-+ return -ENOTSUPP;
-+}
- static inline int bcm47xx_nvram_init_from_mem(u32 base, u32 lim)
- {
- return -ENOTSUPP;
+++ /dev/null
-From eebc6573ad940b62a87776db3917e912b4f52d78 Mon Sep 17 00:00:00 2001
-From: Tom Rix <trix@redhat.com>
-Date: Sun, 11 Jun 2023 15:03:05 +0100
-Subject: [PATCH] nvmem: imx-ocotp: set varaiable imx_ocotp_layout
- storage-class-specifier to static
-
-smatch reports
-drivers/nvmem/imx-ocotp.c:599:21: warning: symbol
- 'imx_ocotp_layout' was not declared. Should it be static?
-
-This variable is only used in one file so should be static.
-
-Signed-off-by: Tom Rix <trix@redhat.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Message-ID: <20230611140330.154222-2-srinivas.kandagatla@linaro.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/imx-ocotp.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/nvmem/imx-ocotp.c
-+++ b/drivers/nvmem/imx-ocotp.c
-@@ -596,7 +596,7 @@ static void imx_ocotp_fixup_cell_info(st
- cell->read_post_process = imx_ocotp_cell_pp;
- }
-
--struct nvmem_layout imx_ocotp_layout = {
-+static struct nvmem_layout imx_ocotp_layout = {
- .fixup_cell_info = imx_ocotp_fixup_cell_info,
- };
-
+++ /dev/null
-From 8a00fc606312c68b98add8fe8e6f7a013ce29e78 Mon Sep 17 00:00:00 2001
-From: Alexander Stein <alexander.stein@ew.tq-group.com>
-Date: Sun, 11 Jun 2023 15:03:06 +0100
-Subject: [PATCH] nvmem: imx-ocotp: Reverse MAC addresses on all i.MX derivates
-
-Not just i.MX8M, but all i.MX6/7 (and subtypes) need to reverse the
-MAC address read from fuses. Exceptions are i.MX6SLL and i.MX7ULP which
-do not support ethernet at all.
-
-Fixes: d0221a780cbc ("nvmem: imx-ocotp: add support for post processing")
-Signed-off-by: Alexander Stein <alexander.stein@ew.tq-group.com>
-Tested-by: Richard Leitner <richard.leitner@skidata.com> # imx6q
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Message-ID: <20230611140330.154222-3-srinivas.kandagatla@linaro.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/imx-ocotp.c | 8 +-------
- 1 file changed, 1 insertion(+), 7 deletions(-)
-
---- a/drivers/nvmem/imx-ocotp.c
-+++ b/drivers/nvmem/imx-ocotp.c
-@@ -97,7 +97,6 @@ struct ocotp_params {
- unsigned int bank_address_words;
- void (*set_timing)(struct ocotp_priv *priv);
- struct ocotp_ctrl_reg ctrl;
-- bool reverse_mac_address;
- };
-
- static int imx_ocotp_wait_for_busy(struct ocotp_priv *priv, u32 flags)
-@@ -545,7 +544,6 @@ static const struct ocotp_params imx8mq_
- .bank_address_words = 0,
- .set_timing = imx_ocotp_set_imx6_timing,
- .ctrl = IMX_OCOTP_BM_CTRL_DEFAULT,
-- .reverse_mac_address = true,
- };
-
- static const struct ocotp_params imx8mm_params = {
-@@ -553,7 +551,6 @@ static const struct ocotp_params imx8mm_
- .bank_address_words = 0,
- .set_timing = imx_ocotp_set_imx6_timing,
- .ctrl = IMX_OCOTP_BM_CTRL_DEFAULT,
-- .reverse_mac_address = true,
- };
-
- static const struct ocotp_params imx8mn_params = {
-@@ -561,7 +558,6 @@ static const struct ocotp_params imx8mn_
- .bank_address_words = 0,
- .set_timing = imx_ocotp_set_imx6_timing,
- .ctrl = IMX_OCOTP_BM_CTRL_DEFAULT,
-- .reverse_mac_address = true,
- };
-
- static const struct ocotp_params imx8mp_params = {
-@@ -569,7 +565,6 @@ static const struct ocotp_params imx8mp_
- .bank_address_words = 0,
- .set_timing = imx_ocotp_set_imx6_timing,
- .ctrl = IMX_OCOTP_BM_CTRL_8MP,
-- .reverse_mac_address = true,
- };
-
- static const struct of_device_id imx_ocotp_dt_ids[] = {
-@@ -624,8 +619,7 @@ static int imx_ocotp_probe(struct platfo
- imx_ocotp_nvmem_config.size = 4 * priv->params->nregs;
- imx_ocotp_nvmem_config.dev = dev;
- imx_ocotp_nvmem_config.priv = priv;
-- if (priv->params->reverse_mac_address)
-- imx_ocotp_nvmem_config.layout = &imx_ocotp_layout;
-+ imx_ocotp_nvmem_config.layout = &imx_ocotp_layout;
-
- priv->config = &imx_ocotp_nvmem_config;
-
+++ /dev/null
-From 73bcd133c910bff3b6d3b3834d0d14be9444e90a Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
-Date: Sun, 11 Jun 2023 15:03:08 +0100
-Subject: [PATCH] nvmem: brcm_nvram: add .read_post_process() for MACs
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-1. Parse ASCII MAC format into byte based
-2. Calculate relative addresses based on index argument
-
-Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Message-ID: <20230611140330.154222-5-srinivas.kandagatla@linaro.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/Kconfig | 1 +
- drivers/nvmem/brcm_nvram.c | 28 ++++++++++++++++++++++++++++
- 2 files changed, 29 insertions(+)
-
---- a/drivers/nvmem/Kconfig
-+++ b/drivers/nvmem/Kconfig
-@@ -55,6 +55,7 @@ config NVMEM_BRCM_NVRAM
- tristate "Broadcom's NVRAM support"
- depends on ARCH_BCM_5301X || COMPILE_TEST
- depends on HAS_IOMEM
-+ select GENERIC_NET_UTILS
- help
- This driver provides support for Broadcom's NVRAM that can be accessed
- using I/O mapping.
---- a/drivers/nvmem/brcm_nvram.c
-+++ b/drivers/nvmem/brcm_nvram.c
-@@ -4,6 +4,8 @@
- */
-
- #include <linux/bcm47xx_nvram.h>
-+#include <linux/etherdevice.h>
-+#include <linux/if_ether.h>
- #include <linux/io.h>
- #include <linux/mod_devicetable.h>
- #include <linux/module.h>
-@@ -42,6 +44,25 @@ static int brcm_nvram_read(void *context
- return 0;
- }
-
-+static int brcm_nvram_read_post_process_macaddr(void *context, const char *id, int index,
-+ unsigned int offset, void *buf, size_t bytes)
-+{
-+ u8 mac[ETH_ALEN];
-+
-+ if (bytes != 3 * ETH_ALEN - 1)
-+ return -EINVAL;
-+
-+ if (!mac_pton(buf, mac))
-+ return -EINVAL;
-+
-+ if (index)
-+ eth_addr_add(mac, index);
-+
-+ ether_addr_copy(buf, mac);
-+
-+ return 0;
-+}
-+
- static int brcm_nvram_add_cells(struct brcm_nvram *priv, uint8_t *data,
- size_t len)
- {
-@@ -75,6 +96,13 @@ static int brcm_nvram_add_cells(struct b
- priv->cells[idx].offset = value - (char *)data;
- priv->cells[idx].bytes = strlen(value);
- priv->cells[idx].np = of_get_child_by_name(dev->of_node, priv->cells[idx].name);
-+ if (!strcmp(var, "et0macaddr") ||
-+ !strcmp(var, "et1macaddr") ||
-+ !strcmp(var, "et2macaddr")) {
-+ priv->cells[idx].raw_len = strlen(value);
-+ priv->cells[idx].bytes = ETH_ALEN;
-+ priv->cells[idx].read_post_process = brcm_nvram_read_post_process_macaddr;
-+ }
- }
-
- return 0;
+++ /dev/null
-From 8dc61364164e79e44c07fa2ac0a7b6939f00d5db Mon Sep 17 00:00:00 2001
-From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
-Date: Sun, 11 Jun 2023 15:03:13 +0100
-Subject: [PATCH] nvmem: rockchip-otp: Add clks and reg_read to rockchip_data
-
-In preparation to support new Rockchip OTP memory devices with different
-clock configurations and register layout, extend rockchip_data struct
-with the related members: clks, num_clks, reg_read.
-
-Additionally, to avoid managing redundant driver data, drop num_clks
-member from rockchip_otp struct and update all references to point to
-the equivalent member in rockchip_data.
-
-Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
-Tested-by: Vincent Legoll <vincent.legoll@gmail.com>
-Reviewed-by: Heiko Stuebner <heiko@sntech.de>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Message-ID: <20230611140330.154222-10-srinivas.kandagatla@linaro.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/rockchip-otp.c | 79 ++++++++++++++++++++++--------------
- 1 file changed, 49 insertions(+), 30 deletions(-)
-
---- a/drivers/nvmem/rockchip-otp.c
-+++ b/drivers/nvmem/rockchip-otp.c
-@@ -54,21 +54,19 @@
-
- #define OTPC_TIMEOUT 10000
-
-+struct rockchip_data {
-+ int size;
-+ const char * const *clks;
-+ int num_clks;
-+ nvmem_reg_read_t reg_read;
-+};
-+
- struct rockchip_otp {
- struct device *dev;
- void __iomem *base;
-- struct clk_bulk_data *clks;
-- int num_clks;
-+ struct clk_bulk_data *clks;
- struct reset_control *rst;
--};
--
--/* list of required clocks */
--static const char * const rockchip_otp_clocks[] = {
-- "otp", "apb_pclk", "phy",
--};
--
--struct rockchip_data {
-- int size;
-+ const struct rockchip_data *data;
- };
-
- static int rockchip_otp_reset(struct rockchip_otp *otp)
-@@ -132,29 +130,23 @@ static int rockchip_otp_ecc_enable(struc
- return ret;
- }
-
--static int rockchip_otp_read(void *context, unsigned int offset,
-- void *val, size_t bytes)
-+static int px30_otp_read(void *context, unsigned int offset,
-+ void *val, size_t bytes)
- {
- struct rockchip_otp *otp = context;
- u8 *buf = val;
-- int ret = 0;
--
-- ret = clk_bulk_prepare_enable(otp->num_clks, otp->clks);
-- if (ret < 0) {
-- dev_err(otp->dev, "failed to prepare/enable clks\n");
-- return ret;
-- }
-+ int ret;
-
- ret = rockchip_otp_reset(otp);
- if (ret) {
- dev_err(otp->dev, "failed to reset otp phy\n");
-- goto disable_clks;
-+ return ret;
- }
-
- ret = rockchip_otp_ecc_enable(otp, false);
- if (ret < 0) {
- dev_err(otp->dev, "rockchip_otp_ecc_enable err\n");
-- goto disable_clks;
-+ return ret;
- }
-
- writel(OTPC_USE_USER | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
-@@ -174,8 +166,28 @@ static int rockchip_otp_read(void *conte
-
- read_end:
- writel(0x0 | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
--disable_clks:
-- clk_bulk_disable_unprepare(otp->num_clks, otp->clks);
-+
-+ return ret;
-+}
-+
-+static int rockchip_otp_read(void *context, unsigned int offset,
-+ void *val, size_t bytes)
-+{
-+ struct rockchip_otp *otp = context;
-+ int ret;
-+
-+ if (!otp->data || !otp->data->reg_read)
-+ return -EINVAL;
-+
-+ ret = clk_bulk_prepare_enable(otp->data->num_clks, otp->clks);
-+ if (ret < 0) {
-+ dev_err(otp->dev, "failed to prepare/enable clks\n");
-+ return ret;
-+ }
-+
-+ ret = otp->data->reg_read(context, offset, val, bytes);
-+
-+ clk_bulk_disable_unprepare(otp->data->num_clks, otp->clks);
-
- return ret;
- }
-@@ -189,8 +201,15 @@ static struct nvmem_config otp_config =
- .reg_read = rockchip_otp_read,
- };
-
-+static const char * const px30_otp_clocks[] = {
-+ "otp", "apb_pclk", "phy",
-+};
-+
- static const struct rockchip_data px30_data = {
- .size = 0x40,
-+ .clks = px30_otp_clocks,
-+ .num_clks = ARRAY_SIZE(px30_otp_clocks),
-+ .reg_read = px30_otp_read,
- };
-
- static const struct of_device_id rockchip_otp_match[] = {
-@@ -225,21 +244,21 @@ static int rockchip_otp_probe(struct pla
- if (!otp)
- return -ENOMEM;
-
-+ otp->data = data;
- otp->dev = dev;
- otp->base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(otp->base))
- return PTR_ERR(otp->base);
-
-- otp->num_clks = ARRAY_SIZE(rockchip_otp_clocks);
-- otp->clks = devm_kcalloc(dev, otp->num_clks,
-- sizeof(*otp->clks), GFP_KERNEL);
-+ otp->clks = devm_kcalloc(dev, data->num_clks, sizeof(*otp->clks),
-+ GFP_KERNEL);
- if (!otp->clks)
- return -ENOMEM;
-
-- for (i = 0; i < otp->num_clks; ++i)
-- otp->clks[i].id = rockchip_otp_clocks[i];
-+ for (i = 0; i < data->num_clks; ++i)
-+ otp->clks[i].id = data->clks[i];
-
-- ret = devm_clk_bulk_get(dev, otp->num_clks, otp->clks);
-+ ret = devm_clk_bulk_get(dev, data->num_clks, otp->clks);
- if (ret)
- return ret;
-
+++ /dev/null
-From 30fd21cfb1e64ef20035559a8246f5fbf682c40e Mon Sep 17 00:00:00 2001
-From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
-Date: Sun, 11 Jun 2023 15:03:14 +0100
-Subject: [PATCH] nvmem: rockchip-otp: Generalize rockchip_otp_wait_status()
-
-In preparation to support additional Rockchip OTP memory devices with
-different register layout, generalize rockchip_otp_wait_status() to
-accept a new parameter for specifying the offset of the status register.
-
-Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
-Tested-by: Vincent Legoll <vincent.legoll@gmail.com>
-Reviewed-by: Heiko Stuebner <heiko@sntech.de>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Message-ID: <20230611140330.154222-11-srinivas.kandagatla@linaro.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/rockchip-otp.c | 11 ++++++-----
- 1 file changed, 6 insertions(+), 5 deletions(-)
-
---- a/drivers/nvmem/rockchip-otp.c
-+++ b/drivers/nvmem/rockchip-otp.c
-@@ -90,18 +90,19 @@ static int rockchip_otp_reset(struct roc
- return 0;
- }
-
--static int rockchip_otp_wait_status(struct rockchip_otp *otp, u32 flag)
-+static int rockchip_otp_wait_status(struct rockchip_otp *otp,
-+ unsigned int reg, u32 flag)
- {
- u32 status = 0;
- int ret;
-
-- ret = readl_poll_timeout_atomic(otp->base + OTPC_INT_STATUS, status,
-+ ret = readl_poll_timeout_atomic(otp->base + reg, status,
- (status & flag), 1, OTPC_TIMEOUT);
- if (ret)
- return ret;
-
- /* clean int status */
-- writel(flag, otp->base + OTPC_INT_STATUS);
-+ writel(flag, otp->base + reg);
-
- return 0;
- }
-@@ -123,7 +124,7 @@ static int rockchip_otp_ecc_enable(struc
-
- writel(SBPI_ENABLE_MASK | SBPI_ENABLE, otp->base + OTPC_SBPI_CTRL);
-
-- ret = rockchip_otp_wait_status(otp, OTPC_SBPI_DONE);
-+ ret = rockchip_otp_wait_status(otp, OTPC_INT_STATUS, OTPC_SBPI_DONE);
- if (ret < 0)
- dev_err(otp->dev, "timeout during ecc_enable\n");
-
-@@ -156,7 +157,7 @@ static int px30_otp_read(void *context,
- otp->base + OTPC_USER_ADDR);
- writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK,
- otp->base + OTPC_USER_ENABLE);
-- ret = rockchip_otp_wait_status(otp, OTPC_USER_DONE);
-+ ret = rockchip_otp_wait_status(otp, OTPC_INT_STATUS, OTPC_USER_DONE);
- if (ret < 0) {
- dev_err(otp->dev, "timeout during read setup\n");
- goto read_end;
+++ /dev/null
-From d325c9dd2b6e94040ca722ddcadcd6af358dd2be Mon Sep 17 00:00:00 2001
-From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
-Date: Sun, 11 Jun 2023 15:03:15 +0100
-Subject: [PATCH] nvmem: rockchip-otp: Use
- devm_reset_control_array_get_exclusive()
-
-In preparation to support new Rockchip OTP memory devices having
-specific reset configurations, switch devm_reset_control_get() to
-devm_reset_control_array_get_exclusive().
-
-Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
-Tested-by: Vincent Legoll <vincent.legoll@gmail.com>
-Reviewed-by: Heiko Stuebner <heiko@sntech.de>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Message-ID: <20230611140330.154222-12-srinivas.kandagatla@linaro.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/rockchip-otp.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/nvmem/rockchip-otp.c
-+++ b/drivers/nvmem/rockchip-otp.c
-@@ -263,7 +263,7 @@ static int rockchip_otp_probe(struct pla
- if (ret)
- return ret;
-
-- otp->rst = devm_reset_control_get(dev, "phy");
-+ otp->rst = devm_reset_control_array_get_exclusive(dev);
- if (IS_ERR(otp->rst))
- return PTR_ERR(otp->rst);
-
+++ /dev/null
-From 912517345b867a69542dc9f5c2cc3e9d8beaccf5 Mon Sep 17 00:00:00 2001
-From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
-Date: Sun, 11 Jun 2023 15:03:16 +0100
-Subject: [PATCH] nvmem: rockchip-otp: Improve probe error handling
-
-Enhance error handling in the probe function by making use of
-dev_err_probe(), which ensures the error code is always printed, in
-addition to the specified error message.
-
-Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
-Tested-by: Vincent Legoll <vincent.legoll@gmail.com>
-Reviewed-by: Heiko Stuebner <heiko@sntech.de>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Message-ID: <20230611140330.154222-13-srinivas.kandagatla@linaro.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/rockchip-otp.c | 21 ++++++++++++---------
- 1 file changed, 12 insertions(+), 9 deletions(-)
-
---- a/drivers/nvmem/rockchip-otp.c
-+++ b/drivers/nvmem/rockchip-otp.c
-@@ -235,10 +235,8 @@ static int rockchip_otp_probe(struct pla
- int ret, i;
-
- data = of_device_get_match_data(dev);
-- if (!data) {
-- dev_err(dev, "failed to get match data\n");
-- return -EINVAL;
-- }
-+ if (!data)
-+ return dev_err_probe(dev, -EINVAL, "failed to get match data\n");
-
- otp = devm_kzalloc(&pdev->dev, sizeof(struct rockchip_otp),
- GFP_KERNEL);
-@@ -249,7 +247,8 @@ static int rockchip_otp_probe(struct pla
- otp->dev = dev;
- otp->base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(otp->base))
-- return PTR_ERR(otp->base);
-+ return dev_err_probe(dev, PTR_ERR(otp->base),
-+ "failed to ioremap resource\n");
-
- otp->clks = devm_kcalloc(dev, data->num_clks, sizeof(*otp->clks),
- GFP_KERNEL);
-@@ -261,18 +260,22 @@ static int rockchip_otp_probe(struct pla
-
- ret = devm_clk_bulk_get(dev, data->num_clks, otp->clks);
- if (ret)
-- return ret;
-+ return dev_err_probe(dev, ret, "failed to get clocks\n");
-
- otp->rst = devm_reset_control_array_get_exclusive(dev);
- if (IS_ERR(otp->rst))
-- return PTR_ERR(otp->rst);
-+ return dev_err_probe(dev, PTR_ERR(otp->rst),
-+ "failed to get resets\n");
-
- otp_config.size = data->size;
- otp_config.priv = otp;
- otp_config.dev = dev;
-- nvmem = devm_nvmem_register(dev, &otp_config);
-
-- return PTR_ERR_OR_ZERO(nvmem);
-+ nvmem = devm_nvmem_register(dev, &otp_config);
-+ if (IS_ERR(nvmem))
-+ return dev_err_probe(dev, PTR_ERR(nvmem),
-+ "failed to register nvmem device\n");
-+ return 0;
- }
-
- static struct platform_driver rockchip_otp_driver = {
+++ /dev/null
-From 8ab099fafbbc8c9607c399d21a774784a6cb8b45 Mon Sep 17 00:00:00 2001
-From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
-Date: Sun, 11 Jun 2023 15:03:17 +0100
-Subject: [PATCH] nvmem: rockchip-otp: Add support for RK3588
-
-Add support for the OTP memory device found on the Rockchip RK3588 SoC.
-
-While here, remove the unnecessary 'void *' casts in the OF device ID
-table.
-
-Co-developed-by: Finley Xiao <finley.xiao@rock-chips.com>
-Signed-off-by: Finley Xiao <finley.xiao@rock-chips.com>
-Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
-Tested-by: Vincent Legoll <vincent.legoll@gmail.com>
-Reviewed-by: Heiko Stuebner <heiko@sntech.de>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Message-ID: <20230611140330.154222-14-srinivas.kandagatla@linaro.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/rockchip-otp.c | 78 +++++++++++++++++++++++++++++++++++-
- 1 file changed, 76 insertions(+), 2 deletions(-)
-
---- a/drivers/nvmem/rockchip-otp.c
-+++ b/drivers/nvmem/rockchip-otp.c
-@@ -54,6 +54,19 @@
-
- #define OTPC_TIMEOUT 10000
-
-+/* RK3588 Register */
-+#define RK3588_OTPC_AUTO_CTRL 0x04
-+#define RK3588_OTPC_AUTO_EN 0x08
-+#define RK3588_OTPC_INT_ST 0x84
-+#define RK3588_OTPC_DOUT0 0x20
-+#define RK3588_NO_SECURE_OFFSET 0x300
-+#define RK3588_NBYTES 4
-+#define RK3588_BURST_NUM 1
-+#define RK3588_BURST_SHIFT 8
-+#define RK3588_ADDR_SHIFT 16
-+#define RK3588_AUTO_EN BIT(0)
-+#define RK3588_RD_DONE BIT(1)
-+
- struct rockchip_data {
- int size;
- const char * const *clks;
-@@ -171,6 +184,52 @@ read_end:
- return ret;
- }
-
-+static int rk3588_otp_read(void *context, unsigned int offset,
-+ void *val, size_t bytes)
-+{
-+ struct rockchip_otp *otp = context;
-+ unsigned int addr_start, addr_end, addr_len;
-+ int ret, i = 0;
-+ u32 data;
-+ u8 *buf;
-+
-+ addr_start = round_down(offset, RK3588_NBYTES) / RK3588_NBYTES;
-+ addr_end = round_up(offset + bytes, RK3588_NBYTES) / RK3588_NBYTES;
-+ addr_len = addr_end - addr_start;
-+ addr_start += RK3588_NO_SECURE_OFFSET;
-+
-+ buf = kzalloc(array_size(addr_len, RK3588_NBYTES), GFP_KERNEL);
-+ if (!buf)
-+ return -ENOMEM;
-+
-+ while (addr_len--) {
-+ writel((addr_start << RK3588_ADDR_SHIFT) |
-+ (RK3588_BURST_NUM << RK3588_BURST_SHIFT),
-+ otp->base + RK3588_OTPC_AUTO_CTRL);
-+ writel(RK3588_AUTO_EN, otp->base + RK3588_OTPC_AUTO_EN);
-+
-+ ret = rockchip_otp_wait_status(otp, RK3588_OTPC_INT_ST,
-+ RK3588_RD_DONE);
-+ if (ret < 0) {
-+ dev_err(otp->dev, "timeout during read setup\n");
-+ goto read_end;
-+ }
-+
-+ data = readl(otp->base + RK3588_OTPC_DOUT0);
-+ memcpy(&buf[i], &data, RK3588_NBYTES);
-+
-+ i += RK3588_NBYTES;
-+ addr_start++;
-+ }
-+
-+ memcpy(val, buf + offset % RK3588_NBYTES, bytes);
-+
-+read_end:
-+ kfree(buf);
-+
-+ return ret;
-+}
-+
- static int rockchip_otp_read(void *context, unsigned int offset,
- void *val, size_t bytes)
- {
-@@ -213,14 +272,29 @@ static const struct rockchip_data px30_d
- .reg_read = px30_otp_read,
- };
-
-+static const char * const rk3588_otp_clocks[] = {
-+ "otp", "apb_pclk", "phy", "arb",
-+};
-+
-+static const struct rockchip_data rk3588_data = {
-+ .size = 0x400,
-+ .clks = rk3588_otp_clocks,
-+ .num_clks = ARRAY_SIZE(rk3588_otp_clocks),
-+ .reg_read = rk3588_otp_read,
-+};
-+
- static const struct of_device_id rockchip_otp_match[] = {
- {
- .compatible = "rockchip,px30-otp",
-- .data = (void *)&px30_data,
-+ .data = &px30_data,
- },
- {
- .compatible = "rockchip,rk3308-otp",
-- .data = (void *)&px30_data,
-+ .data = &px30_data,
-+ },
-+ {
-+ .compatible = "rockchip,rk3588-otp",
-+ .data = &rk3588_data,
- },
- { /* sentinel */ },
- };
+++ /dev/null
-From 9734408969e978a1c0d5d752be63dd638288e374 Mon Sep 17 00:00:00 2001
-From: Michal Simek <michal.simek@amd.com>
-Date: Sun, 11 Jun 2023 15:03:23 +0100
-Subject: [PATCH] nvmem: zynqmp: Switch @xilinx.com emails to @amd.com
-
-@xilinx.com is still working but better to switch to new amd.com after
-AMD/Xilinx acquisition.
-
-Signed-off-by: Michal Simek <michal.simek@amd.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Message-ID: <20230611140330.154222-20-srinivas.kandagatla@linaro.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/zynqmp_nvmem.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/nvmem/zynqmp_nvmem.c
-+++ b/drivers/nvmem/zynqmp_nvmem.c
-@@ -76,6 +76,6 @@ static struct platform_driver zynqmp_nvm
-
- module_platform_driver(zynqmp_nvmem_driver);
-
--MODULE_AUTHOR("Michal Simek <michal.simek@xilinx.com>, Nava kishore Manne <navam@xilinx.com>");
-+MODULE_AUTHOR("Michal Simek <michal.simek@amd.com>, Nava kishore Manne <nava.kishore.manne@amd.com>");
- MODULE_DESCRIPTION("ZynqMP NVMEM driver");
- MODULE_LICENSE("GPL");
+++ /dev/null
-From 22e9e6fcfb5042cb6d6c7874c459b034800092f1 Mon Sep 17 00:00:00 2001
-From: Peng Fan <peng.fan@nxp.com>
-Date: Sun, 11 Jun 2023 15:03:25 +0100
-Subject: [PATCH] nvmem: imx: support i.MX93 OCOTP
-
-Add i.MX93 OCOTP support. i.MX93 OCOTP has two parts: Fuse shadow
-block(fsb) and fuse managed by ELE. The FSB part could be directly
-accessed with MMIO, the ELE could only be accessed with ELE API.
-
-Currently the ELE API is not ready, so NULL function callback is used,
-but it was tested with downstream ELE API.
-
-Signed-off-by: Peng Fan <peng.fan@nxp.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Message-ID: <20230611140330.154222-22-srinivas.kandagatla@linaro.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/Kconfig | 9 ++
- drivers/nvmem/Makefile | 2 +
- drivers/nvmem/imx-ocotp-ele.c | 175 ++++++++++++++++++++++++++++++++++
- 3 files changed, 186 insertions(+)
- create mode 100644 drivers/nvmem/imx-ocotp-ele.c
-
---- a/drivers/nvmem/Kconfig
-+++ b/drivers/nvmem/Kconfig
-@@ -83,6 +83,15 @@ config NVMEM_IMX_OCOTP
- This driver can also be built as a module. If so, the module
- will be called nvmem-imx-ocotp.
-
-+config NVMEM_IMX_OCOTP_ELE
-+ tristate "i.MX On-Chip OTP Controller support"
-+ depends on ARCH_MXC || COMPILE_TEST
-+ depends on HAS_IOMEM
-+ depends on OF
-+ help
-+ This is a driver for the On-Chip OTP Controller (OCOTP)
-+ available on i.MX SoCs which has ELE.
-+
- config NVMEM_IMX_OCOTP_SCU
- tristate "i.MX8 SCU On-Chip OTP Controller support"
- depends on IMX_SCU
---- a/drivers/nvmem/Makefile
-+++ b/drivers/nvmem/Makefile
-@@ -18,6 +18,8 @@ obj-$(CONFIG_NVMEM_IMX_IIM) += nvmem-im
- nvmem-imx-iim-y := imx-iim.o
- obj-$(CONFIG_NVMEM_IMX_OCOTP) += nvmem-imx-ocotp.o
- nvmem-imx-ocotp-y := imx-ocotp.o
-+obj-$(CONFIG_NVMEM_IMX_OCOTP_ELE) += nvmem-imx-ocotp-ele.o
-+nvmem-imx-ocotp-ele-y := imx-ocotp-ele.o
- obj-$(CONFIG_NVMEM_IMX_OCOTP_SCU) += nvmem-imx-ocotp-scu.o
- nvmem-imx-ocotp-scu-y := imx-ocotp-scu.o
- obj-$(CONFIG_NVMEM_JZ4780_EFUSE) += nvmem_jz4780_efuse.o
---- /dev/null
-+++ b/drivers/nvmem/imx-ocotp-ele.c
-@@ -0,0 +1,175 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+/*
-+ * i.MX9 OCOTP fusebox driver
-+ *
-+ * Copyright 2023 NXP
-+ */
-+
-+#include <linux/device.h>
-+#include <linux/io.h>
-+#include <linux/module.h>
-+#include <linux/nvmem-provider.h>
-+#include <linux/of_device.h>
-+#include <linux/platform_device.h>
-+#include <linux/slab.h>
-+
-+enum fuse_type {
-+ FUSE_FSB = 1,
-+ FUSE_ELE = 2,
-+ FUSE_INVALID = -1
-+};
-+
-+struct ocotp_map_entry {
-+ u32 start; /* start word */
-+ u32 num; /* num words */
-+ enum fuse_type type;
-+};
-+
-+struct ocotp_devtype_data {
-+ u32 reg_off;
-+ char *name;
-+ u32 size;
-+ u32 num_entry;
-+ u32 flag;
-+ nvmem_reg_read_t reg_read;
-+ struct ocotp_map_entry entry[];
-+};
-+
-+struct imx_ocotp_priv {
-+ struct device *dev;
-+ void __iomem *base;
-+ struct nvmem_config config;
-+ struct mutex lock;
-+ const struct ocotp_devtype_data *data;
-+};
-+
-+static enum fuse_type imx_ocotp_fuse_type(void *context, u32 index)
-+{
-+ struct imx_ocotp_priv *priv = context;
-+ const struct ocotp_devtype_data *data = priv->data;
-+ u32 start, end;
-+ int i;
-+
-+ for (i = 0; i < data->num_entry; i++) {
-+ start = data->entry[i].start;
-+ end = data->entry[i].start + data->entry[i].num;
-+
-+ if (index >= start && index < end)
-+ return data->entry[i].type;
-+ }
-+
-+ return FUSE_INVALID;
-+}
-+
-+static int imx_ocotp_reg_read(void *context, unsigned int offset, void *val, size_t bytes)
-+{
-+ struct imx_ocotp_priv *priv = context;
-+ void __iomem *reg = priv->base + priv->data->reg_off;
-+ u32 count, index, num_bytes;
-+ enum fuse_type type;
-+ u32 *buf;
-+ void *p;
-+ int i;
-+
-+ index = offset;
-+ num_bytes = round_up(bytes, 4);
-+ count = num_bytes >> 2;
-+
-+ if (count > ((priv->data->size >> 2) - index))
-+ count = (priv->data->size >> 2) - index;
-+
-+ p = kzalloc(num_bytes, GFP_KERNEL);
-+ if (!p)
-+ return -ENOMEM;
-+
-+ mutex_lock(&priv->lock);
-+
-+ buf = p;
-+
-+ for (i = index; i < (index + count); i++) {
-+ type = imx_ocotp_fuse_type(context, i);
-+ if (type == FUSE_INVALID || type == FUSE_ELE) {
-+ *buf++ = 0;
-+ continue;
-+ }
-+
-+ *buf++ = readl_relaxed(reg + (i << 2));
-+ }
-+
-+ memcpy(val, (u8 *)p, bytes);
-+
-+ mutex_unlock(&priv->lock);
-+
-+ kfree(p);
-+
-+ return 0;
-+};
-+
-+static int imx_ele_ocotp_probe(struct platform_device *pdev)
-+{
-+ struct device *dev = &pdev->dev;
-+ struct imx_ocotp_priv *priv;
-+ struct nvmem_device *nvmem;
-+
-+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
-+ if (!priv)
-+ return -ENOMEM;
-+
-+ priv->data = of_device_get_match_data(dev);
-+
-+ priv->base = devm_platform_ioremap_resource(pdev, 0);
-+ if (IS_ERR(priv->base))
-+ return PTR_ERR(priv->base);
-+
-+ priv->config.dev = dev;
-+ priv->config.name = "ELE-OCOTP";
-+ priv->config.id = NVMEM_DEVID_AUTO;
-+ priv->config.owner = THIS_MODULE;
-+ priv->config.size = priv->data->size;
-+ priv->config.reg_read = priv->data->reg_read;
-+ priv->config.word_size = 4;
-+ priv->config.stride = 1;
-+ priv->config.priv = priv;
-+ priv->config.read_only = true;
-+ mutex_init(&priv->lock);
-+
-+ nvmem = devm_nvmem_register(dev, &priv->config);
-+ if (IS_ERR(nvmem))
-+ return PTR_ERR(nvmem);
-+
-+ return 0;
-+}
-+
-+static const struct ocotp_devtype_data imx93_ocotp_data = {
-+ .reg_off = 0x8000,
-+ .reg_read = imx_ocotp_reg_read,
-+ .size = 2048,
-+ .num_entry = 6,
-+ .entry = {
-+ { 0, 52, FUSE_FSB },
-+ { 63, 1, FUSE_ELE},
-+ { 128, 16, FUSE_ELE },
-+ { 182, 1, FUSE_ELE },
-+ { 188, 1, FUSE_ELE },
-+ { 312, 200, FUSE_FSB }
-+ },
-+};
-+
-+static const struct of_device_id imx_ele_ocotp_dt_ids[] = {
-+ { .compatible = "fsl,imx93-ocotp", .data = &imx93_ocotp_data, },
-+ {},
-+};
-+MODULE_DEVICE_TABLE(of, imx_ele_ocotp_dt_ids);
-+
-+static struct platform_driver imx_ele_ocotp_driver = {
-+ .driver = {
-+ .name = "imx_ele_ocotp",
-+ .of_match_table = imx_ele_ocotp_dt_ids,
-+ },
-+ .probe = imx_ele_ocotp_probe,
-+};
-+module_platform_driver(imx_ele_ocotp_driver);
-+
-+MODULE_DESCRIPTION("i.MX OCOTP/ELE driver");
-+MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
-+MODULE_LICENSE("GPL");
+++ /dev/null
-From 27f699e578b1a72df89dfa3bc42e093a01dc8d10 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
-Date: Sun, 11 Jun 2023 15:03:29 +0100
-Subject: [PATCH] nvmem: core: add support for fixed cells *layout*
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This adds support for the "fixed-layout" NVMEM layout binding. It allows
-defining NVMEM cells in a layout DT node named "nvmem-layout".
-
-While NVMEM subsystem supports layout drivers it has been discussed that
-"fixed-layout" may actually be supperted internally. It's because:
-1. It's a very basic layout
-2. It allows sharing code with legacy syntax parsing
-3. It's safer for soc_device_match() due to -EPROBE_DEFER
-4. This will make the syntax transition easier
-
-Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
-Reviewed-by: Michael Walle <michael@walle.cc>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Message-ID: <20230611140330.154222-26-srinivas.kandagatla@linaro.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/core.c | 32 +++++++++++++++++++++++++++++---
- 1 file changed, 29 insertions(+), 3 deletions(-)
-
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -696,7 +696,7 @@ static int nvmem_validate_keepouts(struc
- return 0;
- }
-
--static int nvmem_add_cells_from_of(struct nvmem_device *nvmem)
-+static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np)
- {
- struct nvmem_layout *layout = nvmem->layout;
- struct device *dev = &nvmem->dev;
-@@ -704,7 +704,7 @@ static int nvmem_add_cells_from_of(struc
- const __be32 *addr;
- int len, ret;
-
-- for_each_child_of_node(dev->of_node, child) {
-+ for_each_child_of_node(np, child) {
- struct nvmem_cell_info info = {0};
-
- addr = of_get_property(child, "reg", &len);
-@@ -742,6 +742,28 @@ static int nvmem_add_cells_from_of(struc
- return 0;
- }
-
-+static int nvmem_add_cells_from_legacy_of(struct nvmem_device *nvmem)
-+{
-+ return nvmem_add_cells_from_dt(nvmem, nvmem->dev.of_node);
-+}
-+
-+static int nvmem_add_cells_from_fixed_layout(struct nvmem_device *nvmem)
-+{
-+ struct device_node *layout_np;
-+ int err = 0;
-+
-+ layout_np = of_nvmem_layout_get_container(nvmem);
-+ if (!layout_np)
-+ return 0;
-+
-+ if (of_device_is_compatible(layout_np, "fixed-layout"))
-+ err = nvmem_add_cells_from_dt(nvmem, layout_np);
-+
-+ of_node_put(layout_np);
-+
-+ return err;
-+}
-+
- int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner)
- {
- layout->owner = owner;
-@@ -972,7 +994,7 @@ struct nvmem_device *nvmem_register(cons
- if (rval)
- goto err_remove_cells;
-
-- rval = nvmem_add_cells_from_of(nvmem);
-+ rval = nvmem_add_cells_from_legacy_of(nvmem);
- if (rval)
- goto err_remove_cells;
-
-@@ -982,6 +1004,10 @@ struct nvmem_device *nvmem_register(cons
- if (rval)
- goto err_remove_cells;
-
-+ rval = nvmem_add_cells_from_fixed_layout(nvmem);
-+ if (rval)
-+ goto err_remove_cells;
-+
- rval = nvmem_add_cells_from_layout(nvmem);
- if (rval)
- goto err_remove_cells;
+++ /dev/null
-From 9ccfcbeb8f32ff89e99b36cb9cdebaa0d1b44ed1 Mon Sep 17 00:00:00 2001
-From: Yangtao Li <frank.li@vivo.com>
-Date: Wed, 23 Aug 2023 14:27:24 +0100
-Subject: [PATCH] nvmem: sunxi_sid: Convert to devm_platform_ioremap_resource()
-
-Use devm_platform_ioremap_resource() to simplify code.
-
-Signed-off-by: Yangtao Li <frank.li@vivo.com>
-Acked-by: Jernej Skrabec <jernej.skrabec@gmail.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230823132744.350618-3-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/sunxi_sid.c | 4 +---
- 1 file changed, 1 insertion(+), 3 deletions(-)
-
---- a/drivers/nvmem/sunxi_sid.c
-+++ b/drivers/nvmem/sunxi_sid.c
-@@ -125,7 +125,6 @@ static int sun8i_sid_read_by_reg(void *c
- static int sunxi_sid_probe(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
-- struct resource *res;
- struct nvmem_config *nvmem_cfg;
- struct nvmem_device *nvmem;
- struct sunxi_sid *sid;
-@@ -142,8 +141,7 @@ static int sunxi_sid_probe(struct platfo
- return -EINVAL;
- sid->value_offset = cfg->value_offset;
-
-- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-- sid->base = devm_ioremap_resource(dev, res);
-+ sid->base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(sid->base))
- return PTR_ERR(sid->base);
-
+++ /dev/null
-From cfadd0e7d9225566f320bc4dc716682be910be6c Mon Sep 17 00:00:00 2001
-From: Yangtao Li <frank.li@vivo.com>
-Date: Wed, 23 Aug 2023 14:27:25 +0100
-Subject: [PATCH] nvmem: brcm_nvram: Use
- devm_platform_get_and_ioremap_resource()
-
-Convert platform_get_resource(), devm_ioremap_resource() to a single
-call to devm_platform_get_and_ioremap_resource(), as this is exactly
-what this function does.
-
-Signed-off-by: Yangtao Li <frank.li@vivo.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230823132744.350618-4-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/brcm_nvram.c | 3 +--
- 1 file changed, 1 insertion(+), 2 deletions(-)
-
---- a/drivers/nvmem/brcm_nvram.c
-+++ b/drivers/nvmem/brcm_nvram.c
-@@ -159,8 +159,7 @@ static int brcm_nvram_probe(struct platf
- return -ENOMEM;
- priv->dev = dev;
-
-- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-- priv->base = devm_ioremap_resource(dev, res);
-+ priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
- if (IS_ERR(priv->base))
- return PTR_ERR(priv->base);
-
+++ /dev/null
-From 0b49178e2b6b4aac3c7fa3ce8d8c02208a13b988 Mon Sep 17 00:00:00 2001
-From: Yangtao Li <frank.li@vivo.com>
-Date: Wed, 23 Aug 2023 14:27:26 +0100
-Subject: [PATCH] nvmem: lpc18xx_otp: Convert to
- devm_platform_ioremap_resource()
-
-Use devm_platform_ioremap_resource() to simplify code.
-
-Signed-off-by: Yangtao Li <frank.li@vivo.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230823132744.350618-5-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/lpc18xx_otp.c | 4 +---
- 1 file changed, 1 insertion(+), 3 deletions(-)
-
---- a/drivers/nvmem/lpc18xx_otp.c
-+++ b/drivers/nvmem/lpc18xx_otp.c
-@@ -68,14 +68,12 @@ static int lpc18xx_otp_probe(struct plat
- {
- struct nvmem_device *nvmem;
- struct lpc18xx_otp *otp;
-- struct resource *res;
-
- otp = devm_kzalloc(&pdev->dev, sizeof(*otp), GFP_KERNEL);
- if (!otp)
- return -ENOMEM;
-
-- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-- otp->base = devm_ioremap_resource(&pdev->dev, res);
-+ otp->base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(otp->base))
- return PTR_ERR(otp->base);
-
+++ /dev/null
-From 0a223a097709b99a0ba738d6be5b4f52c04ffb64 Mon Sep 17 00:00:00 2001
-From: Yangtao Li <frank.li@vivo.com>
-Date: Wed, 23 Aug 2023 14:27:27 +0100
-Subject: [PATCH] nvmem: meson-mx-efuse: Convert to
- devm_platform_ioremap_resource()
-
-Use devm_platform_ioremap_resource() to simplify code.
-
-Signed-off-by: Yangtao Li <frank.li@vivo.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230823132744.350618-6-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/meson-mx-efuse.c | 4 +---
- 1 file changed, 1 insertion(+), 3 deletions(-)
-
---- a/drivers/nvmem/meson-mx-efuse.c
-+++ b/drivers/nvmem/meson-mx-efuse.c
-@@ -194,7 +194,6 @@ static int meson_mx_efuse_probe(struct p
- {
- const struct meson_mx_efuse_platform_data *drvdata;
- struct meson_mx_efuse *efuse;
-- struct resource *res;
-
- drvdata = of_device_get_match_data(&pdev->dev);
- if (!drvdata)
-@@ -204,8 +203,7 @@ static int meson_mx_efuse_probe(struct p
- if (!efuse)
- return -ENOMEM;
-
-- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-- efuse->base = devm_ioremap_resource(&pdev->dev, res);
-+ efuse->base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(efuse->base))
- return PTR_ERR(efuse->base);
-
+++ /dev/null
-From 94904db28db49ac8fbb2a273d25156db26a3a985 Mon Sep 17 00:00:00 2001
-From: Yangtao Li <frank.li@vivo.com>
-Date: Wed, 23 Aug 2023 14:27:28 +0100
-Subject: [PATCH] nvmem: rockchip-efuse: Use
- devm_platform_get_and_ioremap_resource()
-
-Convert platform_get_resource(), devm_ioremap_resource() to a single
-call to devm_platform_get_and_ioremap_resource(), as this is exactly
-what this function does.
-
-Signed-off-by: Yangtao Li <frank.li@vivo.com>
-Reviewed-by: Heiko Stuebner <heiko@sntech.de>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230823132744.350618-7-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/rockchip-efuse.c | 3 +--
- 1 file changed, 1 insertion(+), 2 deletions(-)
-
---- a/drivers/nvmem/rockchip-efuse.c
-+++ b/drivers/nvmem/rockchip-efuse.c
-@@ -267,8 +267,7 @@ static int rockchip_efuse_probe(struct p
- if (!efuse)
- return -ENOMEM;
-
-- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-- efuse->base = devm_ioremap_resource(dev, res);
-+ efuse->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
- if (IS_ERR(efuse->base))
- return PTR_ERR(efuse->base);
-
+++ /dev/null
-From 0a4a8c0d238fec1fa4b85591524ef42ad261cb97 Mon Sep 17 00:00:00 2001
-From: Yangtao Li <frank.li@vivo.com>
-Date: Wed, 23 Aug 2023 14:27:29 +0100
-Subject: [PATCH] nvmem: stm32-romem: Use
- devm_platform_get_and_ioremap_resource()
-
-Convert platform_get_resource(), devm_ioremap_resource() to a single
-call to devm_platform_get_and_ioremap_resource(), as this is exactly
-what this function does.
-
-Signed-off-by: Yangtao Li <frank.li@vivo.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230823132744.350618-8-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/stm32-romem.c | 3 +--
- 1 file changed, 1 insertion(+), 2 deletions(-)
-
---- a/drivers/nvmem/stm32-romem.c
-+++ b/drivers/nvmem/stm32-romem.c
-@@ -196,8 +196,7 @@ static int stm32_romem_probe(struct plat
- if (!priv)
- return -ENOMEM;
-
-- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-- priv->base = devm_ioremap_resource(dev, res);
-+ priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
- if (IS_ERR(priv->base))
- return PTR_ERR(priv->base);
-
+++ /dev/null
-From 0bc0d6dc2a9a05ae6729b4622f09782d9f230815 Mon Sep 17 00:00:00 2001
-From: Yangtao Li <frank.li@vivo.com>
-Date: Wed, 23 Aug 2023 14:27:30 +0100
-Subject: [PATCH] nvmem: qfprom: do some cleanup
-
-Use devm_platform_ioremap_resource() and
-devm_platform_get_and_ioremap_resource() to simplify code.
-BTW convert to use dev_err_probe() instead of open it.
-
-Signed-off-by: Yangtao Li <frank.li@vivo.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230823132744.350618-9-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/qfprom.c | 17 +++++------------
- 1 file changed, 5 insertions(+), 12 deletions(-)
-
---- a/drivers/nvmem/qfprom.c
-+++ b/drivers/nvmem/qfprom.c
-@@ -374,8 +374,7 @@ static int qfprom_probe(struct platform_
- return -ENOMEM;
-
- /* The corrected section is always provided */
-- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-- priv->qfpcorrected = devm_ioremap_resource(dev, res);
-+ priv->qfpcorrected = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
- if (IS_ERR(priv->qfpcorrected))
- return PTR_ERR(priv->qfpcorrected);
-
-@@ -402,12 +401,10 @@ static int qfprom_probe(struct platform_
- priv->qfpraw = devm_ioremap_resource(dev, res);
- if (IS_ERR(priv->qfpraw))
- return PTR_ERR(priv->qfpraw);
-- res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
-- priv->qfpconf = devm_ioremap_resource(dev, res);
-+ priv->qfpconf = devm_platform_ioremap_resource(pdev, 2);
- if (IS_ERR(priv->qfpconf))
- return PTR_ERR(priv->qfpconf);
-- res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
-- priv->qfpsecurity = devm_ioremap_resource(dev, res);
-+ priv->qfpsecurity = devm_platform_ioremap_resource(pdev, 3);
- if (IS_ERR(priv->qfpsecurity))
- return PTR_ERR(priv->qfpsecurity);
-
-@@ -427,12 +424,8 @@ static int qfprom_probe(struct platform_
- return PTR_ERR(priv->vcc);
-
- priv->secclk = devm_clk_get(dev, "core");
-- if (IS_ERR(priv->secclk)) {
-- ret = PTR_ERR(priv->secclk);
-- if (ret != -EPROBE_DEFER)
-- dev_err(dev, "Error getting clock: %d\n", ret);
-- return ret;
-- }
-+ if (IS_ERR(priv->secclk))
-+ return dev_err_probe(dev, PTR_ERR(priv->secclk), "Error getting clock\n");
-
- /* Only enable writing if we have SoC data. */
- if (priv->soc_data)
+++ /dev/null
-From 6ac41c556e22a0d7d267c9b9d48681d73af4b368 Mon Sep 17 00:00:00 2001
-From: Yangtao Li <frank.li@vivo.com>
-Date: Wed, 23 Aug 2023 14:27:31 +0100
-Subject: [PATCH] nvmem: uniphier: Use devm_platform_get_and_ioremap_resource()
-
-Convert platform_get_resource(), devm_ioremap_resource() to a single
-call to devm_platform_get_and_ioremap_resource(), as this is exactly
-what this function does.
-
-Signed-off-by: Yangtao Li <frank.li@vivo.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230823132744.350618-10-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/uniphier-efuse.c | 3 +--
- 1 file changed, 1 insertion(+), 2 deletions(-)
-
---- a/drivers/nvmem/uniphier-efuse.c
-+++ b/drivers/nvmem/uniphier-efuse.c
-@@ -41,8 +41,7 @@ static int uniphier_efuse_probe(struct p
- if (!priv)
- return -ENOMEM;
-
-- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-- priv->base = devm_ioremap_resource(dev, res);
-+ priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
- if (IS_ERR(priv->base))
- return PTR_ERR(priv->base);
-
+++ /dev/null
-From c8efcf7a86ebf2ff48584d270b3070a7075bc345 Mon Sep 17 00:00:00 2001
-From: Richard Alpe <richard@bit42.se>
-Date: Mon, 10 Apr 2023 10:20:51 +0200
-Subject: [PATCH] nvmem: add new NXP QorIQ eFuse driver
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Add SFP (Security Fuse Processor) read support for NXP (Freescale)
-QorIQ series SOC's.
-
-This patch adds support for the T1023 SOC using the SFP offset from
-the existing T1023 device tree. In theory this should also work for
-T1024, T1014 and T1013 which uses the same SFP base offset.
-
-Signed-off-by: Richard Alpe <richard@bit42.se>
-Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
----
- drivers/nvmem/Kconfig | 12 ++++++
- drivers/nvmem/Makefile | 2 +
- drivers/nvmem/qoriq-efuse.c | 78 +++++++++++++++++++++++++++++++++++++
- 3 files changed, 92 insertions(+)
- create mode 100644 drivers/nvmem/qoriq-efuse.c
-
---- a/drivers/nvmem/Kconfig
-+++ b/drivers/nvmem/Kconfig
-@@ -392,4 +392,16 @@ config NVMEM_ZYNQMP
-
- If sure, say yes. If unsure, say no.
-
-+config NVMEM_QORIQ_EFUSE
-+ tristate "NXP QorIQ eFuse support"
-+ depends on PPC_85xx || COMPILE_TEST
-+ depends on HAS_IOMEM
-+ help
-+ This driver provides read support for the eFuses (SFP) on NXP QorIQ
-+ series SoC's. This includes secure boot settings, the globally unique
-+ NXP ID 'FUIDR' and the OEM unique ID 'OUIDR'.
-+
-+ This driver can also be built as a module. If so, the module
-+ will be called nvmem_qoriq_efuse.
-+
- endif
---- a/drivers/nvmem/Makefile
-+++ b/drivers/nvmem/Makefile
-@@ -77,3 +77,5 @@ obj-$(CONFIG_NVMEM_VF610_OCOTP) += nvme
- nvmem-vf610-ocotp-y := vf610-ocotp.o
- obj-$(CONFIG_NVMEM_ZYNQMP) += nvmem_zynqmp_nvmem.o
- nvmem_zynqmp_nvmem-y := zynqmp_nvmem.o
-+obj-$(CONFIG_NVMEM_QORIQ_EFUSE) += nvmem-qoriq-efuse.o
-+nvmem-qoriq-efuse-y := qoriq-efuse.o
---- /dev/null
-+++ b/drivers/nvmem/qoriq-efuse.c
-@@ -0,0 +1,78 @@
-+// SPDX-License-Identifier: GPL-2.0
-+/*
-+ * Copyright (C) 2023 Westermo Network Technologies AB
-+ */
-+
-+#include <linux/device.h>
-+#include <linux/io.h>
-+#include <linux/module.h>
-+#include <linux/mod_devicetable.h>
-+#include <linux/nvmem-provider.h>
-+#include <linux/platform_device.h>
-+
-+struct qoriq_efuse_priv {
-+ void __iomem *base;
-+};
-+
-+static int qoriq_efuse_read(void *context, unsigned int offset, void *val,
-+ size_t bytes)
-+{
-+ struct qoriq_efuse_priv *priv = context;
-+
-+ /* .stride = 4 so offset is guaranteed to be aligned */
-+ __ioread32_copy(val, priv->base + offset, bytes / 4);
-+
-+ /* Ignore trailing bytes (there shouldn't be any) */
-+
-+ return 0;
-+}
-+
-+static int qoriq_efuse_probe(struct platform_device *pdev)
-+{
-+ struct nvmem_config config = {
-+ .dev = &pdev->dev,
-+ .read_only = true,
-+ .reg_read = qoriq_efuse_read,
-+ .stride = sizeof(u32),
-+ .word_size = sizeof(u32),
-+ .name = "qoriq_efuse_read",
-+ .id = NVMEM_DEVID_AUTO,
-+ .root_only = true,
-+ };
-+ struct qoriq_efuse_priv *priv;
-+ struct nvmem_device *nvmem;
-+ struct resource *res;
-+
-+ priv = devm_kzalloc(config.dev, sizeof(*priv), GFP_KERNEL);
-+ if (!priv)
-+ return -ENOMEM;
-+
-+ priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
-+ if (IS_ERR(priv->base))
-+ return PTR_ERR(priv->base);
-+
-+ config.size = resource_size(res);
-+ config.priv = priv;
-+ nvmem = devm_nvmem_register(config.dev, &config);
-+
-+ return PTR_ERR_OR_ZERO(nvmem);
-+}
-+
-+static const struct of_device_id qoriq_efuse_of_match[] = {
-+ { .compatible = "fsl,t1023-sfp", },
-+ {/* sentinel */},
-+};
-+MODULE_DEVICE_TABLE(of, qoriq_efuse_of_match);
-+
-+static struct platform_driver qoriq_efuse_driver = {
-+ .probe = qoriq_efuse_probe,
-+ .driver = {
-+ .name = "qoriq-efuse",
-+ .of_match_table = qoriq_efuse_of_match,
-+ },
-+};
-+module_platform_driver(qoriq_efuse_driver);
-+
-+MODULE_AUTHOR("Richard Alpe <richard.alpe@bit42.se>");
-+MODULE_DESCRIPTION("NXP QorIQ Security Fuse Processor (SFP) Reader");
-+MODULE_LICENSE("GPL");
+++ /dev/null
-From 9d53d595f688c9837e88a919229cc61a165c7b9e Mon Sep 17 00:00:00 2001
-From: Diederik de Haas <didi.debian@cknow.org>
-Date: Mon, 24 Jul 2023 13:36:22 +0200
-Subject: [PATCH] nvmem: Kconfig: Fix typo "drive" -> "driver"
-
-Fix typo where "driver" was meant instead of "drive".
-While at it, also capitalize "OTP".
-
-Signed-off-by: Diederik de Haas <didi.debian@cknow.org>
-Reviewed-by: Heiko Stuebner <heiko@sntech.de>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
----
- drivers/nvmem/Kconfig | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
---- a/drivers/nvmem/Kconfig
-+++ b/drivers/nvmem/Kconfig
-@@ -247,7 +247,7 @@ config NVMEM_ROCKCHIP_EFUSE
- depends on ARCH_ROCKCHIP || COMPILE_TEST
- depends on HAS_IOMEM
- help
-- This is a simple drive to dump specified values of Rockchip SoC
-+ This is a simple driver to dump specified values of Rockchip SoC
- from eFuse, such as cpu-leakage.
-
- This driver can also be built as a module. If so, the module
-@@ -258,8 +258,8 @@ config NVMEM_ROCKCHIP_OTP
- depends on ARCH_ROCKCHIP || COMPILE_TEST
- depends on HAS_IOMEM
- help
-- This is a simple drive to dump specified values of Rockchip SoC
-- from otp, such as cpu-leakage.
-+ This is a simple driver to dump specified values of Rockchip SoC
-+ from OTP, such as cpu-leakage.
-
- This driver can also be built as a module. If so, the module
- will be called nvmem_rockchip_otp.
+++ /dev/null
-From 0a9ec38c47c1ca4528aa058e2b9ea61901a7e632 Mon Sep 17 00:00:00 2001
-From: Komal Bajaj <quic_kbajaj@quicinc.com>
-Date: Tue, 1 Aug 2023 12:10:25 +0530
-Subject: [PATCH] nvmem: sec-qfprom: Add Qualcomm secure QFPROM support
-
-For some of the Qualcomm SoC's, it is possible that
-some of the fuse regions or entire qfprom region is
-protected from non-secure access. In such situations,
-the OS will have to use secure calls to read the region.
-With that motivation, add secure qfprom driver.
-
-Signed-off-by: Komal Bajaj <quic_kbajaj@quicinc.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
----
- drivers/nvmem/Kconfig | 13 ++++++
- drivers/nvmem/Makefile | 2 +
- drivers/nvmem/sec-qfprom.c | 96 ++++++++++++++++++++++++++++++++++++++
- 3 files changed, 111 insertions(+)
- create mode 100644 drivers/nvmem/sec-qfprom.c
-
---- a/drivers/nvmem/Kconfig
-+++ b/drivers/nvmem/Kconfig
-@@ -226,6 +226,19 @@ config NVMEM_QCOM_QFPROM
- This driver can also be built as a module. If so, the module
- will be called nvmem_qfprom.
-
-+config NVMEM_QCOM_SEC_QFPROM
-+ tristate "QCOM SECURE QFPROM Support"
-+ depends on ARCH_QCOM || COMPILE_TEST
-+ depends on HAS_IOMEM
-+ depends on OF
-+ select QCOM_SCM
-+ help
-+ Say y here to enable secure QFPROM support. The secure QFPROM provides access
-+ functions for QFPROM data to rest of the drivers via nvmem interface.
-+
-+ This driver can also be built as a module. If so, the module will be called
-+ nvmem_sec_qfprom.
-+
- config NVMEM_RAVE_SP_EEPROM
- tristate "Rave SP EEPROM Support"
- depends on RAVE_SP_CORE
---- a/drivers/nvmem/Makefile
-+++ b/drivers/nvmem/Makefile
-@@ -46,6 +46,8 @@ obj-$(CONFIG_NVMEM_NINTENDO_OTP) += nvme
- nvmem-nintendo-otp-y := nintendo-otp.o
- obj-$(CONFIG_NVMEM_QCOM_QFPROM) += nvmem_qfprom.o
- nvmem_qfprom-y := qfprom.o
-+obj-$(CONFIG_NVMEM_QCOM_SEC_QFPROM) += nvmem_sec_qfprom.o
-+nvmem_sec_qfprom-y := sec-qfprom.o
- obj-$(CONFIG_NVMEM_RAVE_SP_EEPROM) += nvmem-rave-sp-eeprom.o
- nvmem-rave-sp-eeprom-y := rave-sp-eeprom.o
- obj-$(CONFIG_NVMEM_RMEM) += nvmem-rmem.o
---- /dev/null
-+++ b/drivers/nvmem/sec-qfprom.c
-@@ -0,0 +1,96 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+/*
-+ * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved.
-+ */
-+
-+#include <linux/qcom_scm.h>
-+#include <linux/mod_devicetable.h>
-+#include <linux/nvmem-provider.h>
-+#include <linux/platform_device.h>
-+#include <linux/pm_runtime.h>
-+
-+/**
-+ * struct sec_qfprom - structure holding secure qfprom attributes
-+ *
-+ * @base: starting physical address for secure qfprom corrected address space.
-+ * @dev: qfprom device structure.
-+ */
-+struct sec_qfprom {
-+ phys_addr_t base;
-+ struct device *dev;
-+};
-+
-+static int sec_qfprom_reg_read(void *context, unsigned int reg, void *_val, size_t bytes)
-+{
-+ struct sec_qfprom *priv = context;
-+ unsigned int i;
-+ u8 *val = _val;
-+ u32 read_val;
-+ u8 *tmp;
-+
-+ for (i = 0; i < bytes; i++, reg++) {
-+ if (i == 0 || reg % 4 == 0) {
-+ if (qcom_scm_io_readl(priv->base + (reg & ~3), &read_val)) {
-+ dev_err(priv->dev, "Couldn't access fuse register\n");
-+ return -EINVAL;
-+ }
-+ tmp = (u8 *)&read_val;
-+ }
-+
-+ val[i] = tmp[reg & 3];
-+ }
-+
-+ return 0;
-+}
-+
-+static int sec_qfprom_probe(struct platform_device *pdev)
-+{
-+ struct nvmem_config econfig = {
-+ .name = "sec-qfprom",
-+ .stride = 1,
-+ .word_size = 1,
-+ .id = NVMEM_DEVID_AUTO,
-+ .reg_read = sec_qfprom_reg_read,
-+ };
-+ struct device *dev = &pdev->dev;
-+ struct nvmem_device *nvmem;
-+ struct sec_qfprom *priv;
-+ struct resource *res;
-+
-+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
-+ if (!priv)
-+ return -ENOMEM;
-+
-+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-+ if (!res)
-+ return -EINVAL;
-+
-+ priv->base = res->start;
-+
-+ econfig.size = resource_size(res);
-+ econfig.dev = dev;
-+ econfig.priv = priv;
-+
-+ priv->dev = dev;
-+
-+ nvmem = devm_nvmem_register(dev, &econfig);
-+
-+ return PTR_ERR_OR_ZERO(nvmem);
-+}
-+
-+static const struct of_device_id sec_qfprom_of_match[] = {
-+ { .compatible = "qcom,sec-qfprom" },
-+ {/* sentinel */},
-+};
-+MODULE_DEVICE_TABLE(of, sec_qfprom_of_match);
-+
-+static struct platform_driver qfprom_driver = {
-+ .probe = sec_qfprom_probe,
-+ .driver = {
-+ .name = "qcom_sec_qfprom",
-+ .of_match_table = sec_qfprom_of_match,
-+ },
-+};
-+module_platform_driver(qfprom_driver);
-+MODULE_DESCRIPTION("Qualcomm Secure QFPROM driver");
-+MODULE_LICENSE("GPL");
+++ /dev/null
-From c32f2186acc9abb4d766361255d7ddf07d15eeb2 Mon Sep 17 00:00:00 2001
-From: Atul Raut <rauji.raut@gmail.com>
-Date: Sun, 30 Jul 2023 15:39:15 -0700
-Subject: [PATCH] nvmem: u-boot-env:: Replace zero-length array with
- DECLARE_FLEX_ARRAY() helper
-
-We are moving toward replacing zero-length arrays with C99 flexible-array
-members since they are deprecated. Therefore, the new DECLARE_FLEX_ARRAY()
-helper macro should be used to replace the zero-length array declaration.
-
-This fixes warnings such as:
-./drivers/nvmem/u-boot-env.c:50:9-13: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays)
-
-Signed-off-by: Atul Raut <rauji.raut@gmail.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
----
- drivers/nvmem/u-boot-env.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/nvmem/u-boot-env.c
-+++ b/drivers/nvmem/u-boot-env.c
-@@ -47,7 +47,7 @@ struct u_boot_env_image_broadcom {
- __le32 magic;
- __le32 len;
- __le32 crc32;
-- uint8_t data[0];
-+ DECLARE_FLEX_ARRAY(uint8_t, data);
- } __packed;
-
- static int u_boot_env_read(void *context, unsigned int offset, void *val,
+++ /dev/null
-From 104af6a5b199eb4dc7970d1304aef38ac5a6ed54 Mon Sep 17 00:00:00 2001
-From: Miquel Raynal <miquel.raynal@bootlin.com>
-Date: Tue, 8 Aug 2023 08:29:26 +0200
-Subject: [PATCH] nvmem: core: Create all cells before adding the nvmem device
-
-Let's pack all the cells creation in one place, so they are all created
-before we add the nvmem device.
-
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Reviewed-by: Michael Walle <michael@walle.cc>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
----
- drivers/nvmem/core.c | 10 +++++-----
- 1 file changed, 5 insertions(+), 5 deletions(-)
-
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -998,17 +998,17 @@ struct nvmem_device *nvmem_register(cons
- if (rval)
- goto err_remove_cells;
-
-- dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name);
--
-- rval = device_add(&nvmem->dev);
-+ rval = nvmem_add_cells_from_fixed_layout(nvmem);
- if (rval)
- goto err_remove_cells;
-
-- rval = nvmem_add_cells_from_fixed_layout(nvmem);
-+ rval = nvmem_add_cells_from_layout(nvmem);
- if (rval)
- goto err_remove_cells;
-
-- rval = nvmem_add_cells_from_layout(nvmem);
-+ dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name);
-+
-+ rval = device_add(&nvmem->dev);
- if (rval)
- goto err_remove_cells;
-
+++ /dev/null
-From 6c7f48ea2e663b679aa8e60d8d8e1e6306a644f9 Mon Sep 17 00:00:00 2001
-From: Miquel Raynal <miquel.raynal@bootlin.com>
-Date: Tue, 8 Aug 2023 08:29:27 +0200
-Subject: [PATCH] nvmem: core: Return NULL when no nvmem layout is found
-
-Currently, of_nvmem_layout_get_container() returns NULL on error, or an
-error pointer if either CONFIG_NVMEM or CONFIG_OF is turned off. We
-should likely avoid this kind of mix for two reasons: to clarify the
-intend and anyway fix the !CONFIG_OF which will likely always if we use
-this helper somewhere else. Let's just return NULL when no layout is
-found, we don't need an error value here.
-
-Link: https://staticthinking.wordpress.com/2022/08/01/mixing-error-pointers-and-null/
-Fixes: 266570f496b9 ("nvmem: core: introduce NVMEM layouts")
-Reported-by: kernel test robot <lkp@intel.com>
-Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
-Closes: https://lore.kernel.org/r/202308030002.DnSFOrMB-lkp@intel.com/
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Reviewed-by: Michael Walle <michael@walle.cc>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
----
- include/linux/nvmem-consumer.h | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/include/linux/nvmem-consumer.h
-+++ b/include/linux/nvmem-consumer.h
-@@ -256,7 +256,7 @@ static inline struct nvmem_device *of_nv
- static inline struct device_node *
- of_nvmem_layout_get_container(struct nvmem_device *nvmem)
- {
-- return ERR_PTR(-EOPNOTSUPP);
-+ return NULL;
- }
- #endif /* CONFIG_NVMEM && CONFIG_OF */
-
+++ /dev/null
-From b8257f61b4ddac6d7d0e19a5a4e8b07afb3b4ed3 Mon Sep 17 00:00:00 2001
-From: Miquel Raynal <miquel.raynal@bootlin.com>
-Date: Tue, 8 Aug 2023 08:29:28 +0200
-Subject: [PATCH] nvmem: core: Do not open-code existing functions
-
-Use of_nvmem_layout_get_container() instead of hardcoding it.
-
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Reviewed-by: Michael Walle <michael@walle.cc>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
----
- drivers/nvmem/core.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -786,10 +786,10 @@ EXPORT_SYMBOL_GPL(nvmem_layout_unregiste
-
- static struct nvmem_layout *nvmem_layout_get(struct nvmem_device *nvmem)
- {
-- struct device_node *layout_np, *np = nvmem->dev.of_node;
-+ struct device_node *layout_np;
- struct nvmem_layout *l, *layout = ERR_PTR(-EPROBE_DEFER);
-
-- layout_np = of_get_child_by_name(np, "nvmem-layout");
-+ layout_np = of_nvmem_layout_get_container(nvmem);
- if (!layout_np)
- return NULL;
-
+++ /dev/null
-From 0991afbe4b1805e7f0113ef10d7c5f0698a739e4 Mon Sep 17 00:00:00 2001
-From: Miquel Raynal <miquel.raynal@bootlin.com>
-Date: Tue, 8 Aug 2023 08:29:29 +0200
-Subject: [PATCH] nvmem: core: Notify when a new layout is registered
-
-Tell listeners a new layout was introduced and is now available.
-
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
----
- drivers/nvmem/core.c | 4 ++++
- include/linux/nvmem-consumer.h | 2 ++
- 2 files changed, 6 insertions(+)
-
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -772,12 +772,16 @@ int __nvmem_layout_register(struct nvmem
- list_add(&layout->node, &nvmem_layouts);
- spin_unlock(&nvmem_layout_lock);
-
-+ blocking_notifier_call_chain(&nvmem_notifier, NVMEM_LAYOUT_ADD, layout);
-+
- return 0;
- }
- EXPORT_SYMBOL_GPL(__nvmem_layout_register);
-
- void nvmem_layout_unregister(struct nvmem_layout *layout)
- {
-+ blocking_notifier_call_chain(&nvmem_notifier, NVMEM_LAYOUT_REMOVE, layout);
-+
- spin_lock(&nvmem_layout_lock);
- list_del(&layout->node);
- spin_unlock(&nvmem_layout_lock);
---- a/include/linux/nvmem-consumer.h
-+++ b/include/linux/nvmem-consumer.h
-@@ -43,6 +43,8 @@ enum {
- NVMEM_REMOVE,
- NVMEM_CELL_ADD,
- NVMEM_CELL_REMOVE,
-+ NVMEM_LAYOUT_ADD,
-+ NVMEM_LAYOUT_REMOVE,
- };
-
- #if IS_ENABLED(CONFIG_NVMEM)
+++ /dev/null
-From a75b58a46423cfd9b1f73581f4bd2ac2ae743996 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
-Date: Wed, 2 Aug 2023 18:07:45 +0200
-Subject: [PATCH 1/6] leds: turris-omnia: Use sysfs_emit() instead of sprintf()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Use the dedicated sysfs_emit() function instead of sprintf() in sysfs
-attribute accessor brightness_show().
-
-Signed-off-by: Marek Behún <kabel@kernel.org>
-Link: https://lore.kernel.org/r/20230802160748.11208-4-kabel@kernel.org
-Signed-off-by: Lee Jones <lee@kernel.org>
----
- drivers/leds/leds-turris-omnia.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/leds/leds-turris-omnia.c
-+++ b/drivers/leds/leds-turris-omnia.c
-@@ -194,7 +194,7 @@ static ssize_t brightness_show(struct de
- if (ret < 0)
- return ret;
-
-- return sprintf(buf, "%d\n", ret);
-+ return sysfs_emit(buf, "%d\n", ret);
- }
-
- static ssize_t brightness_store(struct device *dev, struct device_attribute *a,
+++ /dev/null
-From b7c1e53751cb3990153084f31c41f25fde3b629c Mon Sep 17 00:00:00 2001
-From: Miquel Raynal <miquel.raynal@bootlin.com>
-Date: Fri, 24 Nov 2023 20:38:14 +0100
-Subject: [PATCH] nvmem: Do not expect fixed layouts to grab a layout driver
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Two series lived in parallel for some time, which led to this situation:
-- The nvmem-layout container is used for dynamic layouts
-- We now expect fixed layouts to also use the nvmem-layout container but
-this does not require any additional driver, the support is built-in the
-nvmem core.
-
-Ensure we don't refuse to probe for wrong reasons.
-
-Fixes: 27f699e578b1 ("nvmem: core: add support for fixed cells *layout*")
-Cc: stable@vger.kernel.org
-Reported-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Tested-by: Rafał Miłecki <rafal@milecki.pl>
-Tested-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
-Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
-
-Link: https://lore.kernel.org/r/20231124193814.360552-1-miquel.raynal@bootlin.com
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/core.c | 6 ++++++
- 1 file changed, 6 insertions(+)
-
---- a/drivers/nvmem/core.c
-+++ b/drivers/nvmem/core.c
-@@ -797,6 +797,12 @@ static struct nvmem_layout *nvmem_layout
- if (!layout_np)
- return NULL;
-
-+ /* Fixed layouts don't have a matching driver */
-+ if (of_device_is_compatible(layout_np, "fixed-layout")) {
-+ of_node_put(layout_np);
-+ return NULL;
-+ }
-+
- /*
- * In case the nvmem device was built-in while the layout was built as a
- * module, we shall manually request the layout driver loading otherwise
+++ /dev/null
-From 1e37bf84afacd5ba17b7a13a18ca2bc78aff05c0 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
-Date: Fri, 15 Dec 2023 11:13:58 +0000
-Subject: [PATCH] nvmem: brcm_nvram: store a copy of NVRAM content
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This driver uses MMIO access for reading NVRAM from a flash device.
-Underneath there is a flash controller that reads data and provides
-mapping window.
-
-Using MMIO interface affects controller configuration and may break real
-controller driver. It was reported by multiple users of devices with
-NVRAM stored on NAND.
-
-Modify driver to read & cache NVRAM content during init and use that
-copy to provide NVMEM data when requested. On NAND flashes due to their
-alignment NVRAM partitions can be quite big (1 MiB and more) while
-actual NVRAM content stays quite small (usually 16 to 32 KiB). To avoid
-allocating so much memory check for actual data length.
-
-Link: https://lore.kernel.org/linux-mtd/CACna6rwf3_9QVjYcM+847biTX=K0EoWXuXcSMkJO1Vy_5vmVqA@mail.gmail.com/
-Fixes: 3fef9ed0627a ("nvmem: brcm_nvram: new driver exposing Broadcom's NVRAM")
-Cc: <Stable@vger.kernel.org>
-Cc: Arınç ÜNAL <arinc.unal@arinc9.com>
-Cc: Florian Fainelli <florian.fainelli@broadcom.com>
-Cc: Scott Branden <scott.branden@broadcom.com>
-Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
-Acked-by: Arınç ÜNAL <arinc.unal@arinc9.com>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20231215111358.316727-3-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/nvmem/brcm_nvram.c | 134 ++++++++++++++++++++++++++-----------
- 1 file changed, 94 insertions(+), 40 deletions(-)
-
---- a/drivers/nvmem/brcm_nvram.c
-+++ b/drivers/nvmem/brcm_nvram.c
-@@ -17,9 +17,23 @@
-
- #define NVRAM_MAGIC "FLSH"
-
-+/**
-+ * struct brcm_nvram - driver state internal struct
-+ *
-+ * @dev: NVMEM device pointer
-+ * @nvmem_size: Size of the whole space available for NVRAM
-+ * @data: NVRAM data copy stored to avoid poking underlaying flash controller
-+ * @data_len: NVRAM data size
-+ * @padding_byte: Padding value used to fill remaining space
-+ * @cells: Array of discovered NVMEM cells
-+ * @ncells: Number of elements in cells
-+ */
- struct brcm_nvram {
- struct device *dev;
-- void __iomem *base;
-+ size_t nvmem_size;
-+ uint8_t *data;
-+ size_t data_len;
-+ uint8_t padding_byte;
- struct nvmem_cell_info *cells;
- int ncells;
- };
-@@ -36,10 +50,47 @@ static int brcm_nvram_read(void *context
- size_t bytes)
- {
- struct brcm_nvram *priv = context;
-- u8 *dst = val;
-+ size_t to_copy;
-+
-+ if (offset + bytes > priv->data_len)
-+ to_copy = max_t(ssize_t, (ssize_t)priv->data_len - offset, 0);
-+ else
-+ to_copy = bytes;
-+
-+ memcpy(val, priv->data + offset, to_copy);
-+
-+ memset((uint8_t *)val + to_copy, priv->padding_byte, bytes - to_copy);
-+
-+ return 0;
-+}
-+
-+static int brcm_nvram_copy_data(struct brcm_nvram *priv, struct platform_device *pdev)
-+{
-+ struct resource *res;
-+ void __iomem *base;
-+
-+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
-+ if (IS_ERR(base))
-+ return PTR_ERR(base);
-+
-+ priv->nvmem_size = resource_size(res);
-+
-+ priv->padding_byte = readb(base + priv->nvmem_size - 1);
-+ for (priv->data_len = priv->nvmem_size;
-+ priv->data_len;
-+ priv->data_len--) {
-+ if (readb(base + priv->data_len - 1) != priv->padding_byte)
-+ break;
-+ }
-+ WARN(priv->data_len > SZ_128K, "Unexpected (big) NVRAM size: %zu B\n", priv->data_len);
-
-- while (bytes--)
-- *dst++ = readb(priv->base + offset++);
-+ priv->data = devm_kzalloc(priv->dev, priv->data_len, GFP_KERNEL);
-+ if (!priv->data)
-+ return -ENOMEM;
-+
-+ memcpy_fromio(priv->data, base, priv->data_len);
-+
-+ bcm47xx_nvram_init_from_iomem(base, priv->data_len);
-
- return 0;
- }
-@@ -67,8 +118,13 @@ static int brcm_nvram_add_cells(struct b
- size_t len)
- {
- struct device *dev = priv->dev;
-- char *var, *value, *eq;
-+ char *var, *value;
-+ uint8_t tmp;
- int idx;
-+ int err = 0;
-+
-+ tmp = priv->data[len - 1];
-+ priv->data[len - 1] = '\0';
-
- priv->ncells = 0;
- for (var = data + sizeof(struct brcm_nvram_header);
-@@ -78,67 +134,68 @@ static int brcm_nvram_add_cells(struct b
- }
-
- priv->cells = devm_kcalloc(dev, priv->ncells, sizeof(*priv->cells), GFP_KERNEL);
-- if (!priv->cells)
-- return -ENOMEM;
-+ if (!priv->cells) {
-+ err = -ENOMEM;
-+ goto out;
-+ }
-
- for (var = data + sizeof(struct brcm_nvram_header), idx = 0;
- var < (char *)data + len && *var;
- var = value + strlen(value) + 1, idx++) {
-+ char *eq, *name;
-+
- eq = strchr(var, '=');
- if (!eq)
- break;
- *eq = '\0';
-+ name = devm_kstrdup(dev, var, GFP_KERNEL);
-+ *eq = '=';
-+ if (!name) {
-+ err = -ENOMEM;
-+ goto out;
-+ }
- value = eq + 1;
-
-- priv->cells[idx].name = devm_kstrdup(dev, var, GFP_KERNEL);
-- if (!priv->cells[idx].name)
-- return -ENOMEM;
-+ priv->cells[idx].name = name;
- priv->cells[idx].offset = value - (char *)data;
- priv->cells[idx].bytes = strlen(value);
- priv->cells[idx].np = of_get_child_by_name(dev->of_node, priv->cells[idx].name);
-- if (!strcmp(var, "et0macaddr") ||
-- !strcmp(var, "et1macaddr") ||
-- !strcmp(var, "et2macaddr")) {
-+ if (!strcmp(name, "et0macaddr") ||
-+ !strcmp(name, "et1macaddr") ||
-+ !strcmp(name, "et2macaddr")) {
- priv->cells[idx].raw_len = strlen(value);
- priv->cells[idx].bytes = ETH_ALEN;
- priv->cells[idx].read_post_process = brcm_nvram_read_post_process_macaddr;
- }
- }
-
-- return 0;
-+out:
-+ priv->data[len - 1] = tmp;
-+ return err;
- }
-
- static int brcm_nvram_parse(struct brcm_nvram *priv)
- {
-+ struct brcm_nvram_header *header = (struct brcm_nvram_header *)priv->data;
- struct device *dev = priv->dev;
-- struct brcm_nvram_header header;
-- uint8_t *data;
- size_t len;
- int err;
-
-- memcpy_fromio(&header, priv->base, sizeof(header));
--
-- if (memcmp(header.magic, NVRAM_MAGIC, 4)) {
-+ if (memcmp(header->magic, NVRAM_MAGIC, 4)) {
- dev_err(dev, "Invalid NVRAM magic\n");
- return -EINVAL;
- }
-
-- len = le32_to_cpu(header.len);
--
-- data = kzalloc(len, GFP_KERNEL);
-- if (!data)
-- return -ENOMEM;
--
-- memcpy_fromio(data, priv->base, len);
-- data[len - 1] = '\0';
--
-- err = brcm_nvram_add_cells(priv, data, len);
-- if (err) {
-- dev_err(dev, "Failed to add cells: %d\n", err);
-- return err;
-+ len = le32_to_cpu(header->len);
-+ if (len > priv->nvmem_size) {
-+ dev_err(dev, "NVRAM length (%zd) exceeds mapped size (%zd)\n", len,
-+ priv->nvmem_size);
-+ return -EINVAL;
- }
-
-- kfree(data);
-+ err = brcm_nvram_add_cells(priv, priv->data, len);
-+ if (err)
-+ dev_err(dev, "Failed to add cells: %d\n", err);
-
- return 0;
- }
-@@ -150,7 +207,6 @@ static int brcm_nvram_probe(struct platf
- .reg_read = brcm_nvram_read,
- };
- struct device *dev = &pdev->dev;
-- struct resource *res;
- struct brcm_nvram *priv;
- int err;
-
-@@ -159,21 +215,19 @@ static int brcm_nvram_probe(struct platf
- return -ENOMEM;
- priv->dev = dev;
-
-- priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
-- if (IS_ERR(priv->base))
-- return PTR_ERR(priv->base);
-+ err = brcm_nvram_copy_data(priv, pdev);
-+ if (err)
-+ return err;
-
- err = brcm_nvram_parse(priv);
- if (err)
- return err;
-
-- bcm47xx_nvram_init_from_iomem(priv->base, resource_size(res));
--
- config.dev = dev;
- config.cells = priv->cells;
- config.ncells = priv->ncells;
- config.priv = priv;
-- config.size = resource_size(res);
-+ config.size = priv->nvmem_size;
-
- return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config));
- }
+++ /dev/null
-From 4bb7aac70b5d8a4bddf4ee0791b834f9f56883d2 Mon Sep 17 00:00:00 2001
-From: Arnd Bergmann <arnd@arndb.de>
-Date: Thu, 20 Apr 2023 10:45:51 +0200
-Subject: [PATCH] net: phy: fix circular LEDS_CLASS dependencies
-
-The CONFIG_PHYLIB symbol is selected by a number of device drivers that
-need PHY support, but it now has a dependency on CONFIG_LEDS_CLASS,
-which may not be enabled, causing build failures.
-
-Avoid the risk of missing and circular dependencies by guarding the
-phylib LED support itself in another Kconfig symbol that can only be
-enabled if the dependency is met.
-
-This could be made a hidden symbol and always enabled when both CONFIG_OF
-and CONFIG_LEDS_CLASS are reachable from the phylib, but there may be an
-advantage in having users see this option when they have a misconfigured
-kernel without built-in LED support.
-
-Fixes: 01e5b728e9e4 ("net: phy: Add a binding for PHY LEDs")
-Signed-off-by: Arnd Bergmann <arnd@arndb.de>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Link: https://lore.kernel.org/r/20230420084624.3005701-1-arnd@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/Kconfig | 9 ++++++++-
- drivers/net/phy/phy_device.c | 3 ++-
- 2 files changed, 10 insertions(+), 2 deletions(-)
-
---- a/drivers/net/phy/Kconfig
-+++ b/drivers/net/phy/Kconfig
-@@ -18,7 +18,6 @@ menuconfig PHYLIB
- depends on NETDEVICES
- select MDIO_DEVICE
- select MDIO_DEVRES
-- depends on LEDS_CLASS || LEDS_CLASS=n
- help
- Ethernet controllers are usually attached to PHY
- devices. This option provides infrastructure for
-@@ -45,6 +44,14 @@ config LED_TRIGGER_PHY
- <Speed in megabits>Mbps OR <Speed in gigabits>Gbps OR link
- for any speed known to the PHY.
-
-+config PHYLIB_LEDS
-+ bool "Support probing LEDs from device tree"
-+ depends on LEDS_CLASS=y || LEDS_CLASS=PHYLIB
-+ depends on OF
-+ default y
-+ help
-+ When LED class support is enabled, phylib can automatically
-+ probe LED setting from device tree.
-
- config FIXED_PHY
- tristate "MDIO Bus/PHY emulation with fixed speed/link PHYs"
---- a/drivers/net/phy/phy_device.c
-+++ b/drivers/net/phy/phy_device.c
-@@ -3314,7 +3314,8 @@ static int phy_probe(struct device *dev)
- /* Get the LEDs from the device tree, and instantiate standard
- * LEDs for them.
- */
-- err = of_phy_leds(phydev);
-+ if (IS_ENABLED(CONFIG_PHYLIB_LEDS))
-+ err = of_phy_leds(phydev);
-
- out:
- /* Re-assert the reset signal on error */
+++ /dev/null
-From aed8fdad2152d946add50bec00a6b07c457bdcdf Mon Sep 17 00:00:00 2001
-From: Alexander Stein <alexander.stein@ew.tq-group.com>
-Date: Mon, 24 Apr 2023 16:16:48 +0200
-Subject: [PATCH] net: phy: Fix reading LED reg property
-
-'reg' is always encoded in 32 bits, thus it has to be read using the
-function with the corresponding bit width.
-
-Fixes: 01e5b728e9e4 ("net: phy: Add a binding for PHY LEDs")
-Signed-off-by: Alexander Stein <alexander.stein@ew.tq-group.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
-Link: https://lore.kernel.org/r/20230424141648.317944-1-alexander.stein@ew.tq-group.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/phy_device.c | 6 +++++-
- 1 file changed, 5 insertions(+), 1 deletion(-)
-
---- a/drivers/net/phy/phy_device.c
-+++ b/drivers/net/phy/phy_device.c
-@@ -3075,6 +3075,7 @@ static int of_phy_led(struct phy_device
- struct led_init_data init_data = {};
- struct led_classdev *cdev;
- struct phy_led *phyled;
-+ u32 index;
- int err;
-
- phyled = devm_kzalloc(dev, sizeof(*phyled), GFP_KERNEL);
-@@ -3084,10 +3085,13 @@ static int of_phy_led(struct phy_device
- cdev = &phyled->led_cdev;
- phyled->phydev = phydev;
-
-- err = of_property_read_u8(led, "reg", &phyled->index);
-+ err = of_property_read_u32(led, "reg", &index);
- if (err)
- return err;
-+ if (index > U8_MAX)
-+ return -EINVAL;
-
-+ phyled->index = index;
- if (phydev->drv->led_brightness_set)
- cdev->brightness_set_blocking = phy_led_set_brightness;
- if (phydev->drv->led_blink_set)
+++ /dev/null
-From c938ab4da0eb1620ae3243b0b24c572ddfc318fc Mon Sep 17 00:00:00 2001
-From: Andrew Lunn <andrew@lunn.ch>
-Date: Sat, 17 Jun 2023 17:55:00 +0200
-Subject: [PATCH] net: phy: Manual remove LEDs to ensure correct ordering
-
-If the core is left to remove the LEDs via devm_, it is performed too
-late, after the PHY driver is removed from the PHY. This results in
-dereferencing a NULL pointer when the LED core tries to turn the LED
-off before destroying the LED.
-
-Manually unregister the LEDs at a safe point in phy_remove.
-
-Cc: stable@vger.kernel.org
-Reported-by: Florian Fainelli <f.fainelli@gmail.com>
-Suggested-by: Florian Fainelli <f.fainelli@gmail.com>
-Fixes: 01e5b728e9e4 ("net: phy: Add a binding for PHY LEDs")
-Signed-off-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/net/phy/phy_device.c | 15 ++++++++++++++-
- 1 file changed, 14 insertions(+), 1 deletion(-)
-
---- a/drivers/net/phy/phy_device.c
-+++ b/drivers/net/phy/phy_device.c
-@@ -3068,6 +3068,15 @@ static int phy_led_blink_set(struct led_
- return err;
- }
-
-+static void phy_leds_unregister(struct phy_device *phydev)
-+{
-+ struct phy_led *phyled;
-+
-+ list_for_each_entry(phyled, &phydev->leds, list) {
-+ led_classdev_unregister(&phyled->led_cdev);
-+ }
-+}
-+
- static int of_phy_led(struct phy_device *phydev,
- struct device_node *led)
- {
-@@ -3101,7 +3110,7 @@ static int of_phy_led(struct phy_device
- init_data.fwnode = of_fwnode_handle(led);
- init_data.devname_mandatory = true;
-
-- err = devm_led_classdev_register_ext(dev, cdev, &init_data);
-+ err = led_classdev_register_ext(dev, cdev, &init_data);
- if (err)
- return err;
-
-@@ -3130,6 +3139,7 @@ static int of_phy_leds(struct phy_device
- err = of_phy_led(phydev, led);
- if (err) {
- of_node_put(led);
-+ phy_leds_unregister(phydev);
- return err;
- }
- }
-@@ -3335,6 +3345,9 @@ static int phy_remove(struct device *dev
-
- cancel_delayed_work_sync(&phydev->state_queue);
-
-+ if (IS_ENABLED(CONFIG_PHYLIB_LEDS))
-+ phy_leds_unregister(phydev);
-+
- phydev->state = PHY_DOWN;
-
- sfp_bus_del_upstream(phydev->sfp_bus);
+++ /dev/null
-From af7320ecae0ce646fd2c4a88341a3fbc243553da Mon Sep 17 00:00:00 2001
-From: Yang Li <yang.lee@linux.alibaba.com>
-Date: Thu, 11 May 2023 15:08:20 +0800
-Subject: [PATCH] leds: trigger: netdev: Remove NULL check before dev_{put,
- hold}
-
-The call netdev_{put, hold} of dev_{put, hold} will check NULL,
-so there is no need to check before using dev_{put, hold},
-remove it to silence the warnings:
-
-./drivers/leds/trigger/ledtrig-netdev.c:291:3-10: WARNING: NULL check before dev_{put, hold} functions is not needed.
-./drivers/leds/trigger/ledtrig-netdev.c:401:2-9: WARNING: NULL check before dev_{put, hold} functions is not needed.
-
-Reported-by: Abaci Robot <abaci@linux.alibaba.com>
-Closes: https://bugzilla.openanolis.cn/show_bug.cgi?id=4929
-Signed-off-by: Yang Li <yang.lee@linux.alibaba.com>
-Link: https://lore.kernel.org/r/20230511070820.52731-1-yang.lee@linux.alibaba.com
-Signed-off-by: Lee Jones <lee@kernel.org>
----
- drivers/leds/trigger/ledtrig-netdev.c | 6 ++----
- 1 file changed, 2 insertions(+), 4 deletions(-)
-
---- a/drivers/leds/trigger/ledtrig-netdev.c
-+++ b/drivers/leds/trigger/ledtrig-netdev.c
-@@ -462,8 +462,7 @@ static int netdev_trig_notify(struct not
- get_device_state(trigger_data);
- fallthrough;
- case NETDEV_REGISTER:
-- if (trigger_data->net_dev)
-- dev_put(trigger_data->net_dev);
-+ dev_put(trigger_data->net_dev);
- dev_hold(dev);
- trigger_data->net_dev = dev;
- break;
-@@ -594,8 +593,7 @@ static void netdev_trig_deactivate(struc
-
- cancel_delayed_work_sync(&trigger_data->work);
-
-- if (trigger_data->net_dev)
-- dev_put(trigger_data->net_dev);
-+ dev_put(trigger_data->net_dev);
-
- kfree(trigger_data);
- }
+++ /dev/null
-From 97c5209b3d374a25ebdb4c2ea9e9c1b121768da0 Mon Sep 17 00:00:00 2001
-From: Dan Carpenter <dan.carpenter@linaro.org>
-Date: Wed, 14 Jun 2023 10:03:59 +0300
-Subject: [PATCH] leds: trigger: netdev: uninitialized variable in
- netdev_trig_activate()
-
-The qca8k_cled_hw_control_get() function which implements ->hw_control_get
-sets the appropriate bits but does not clear them. This leads to an
-uninitialized variable bug. Fix this by setting mode to zero at the
-start.
-
-Fixes: e0256648c831 ("net: dsa: qca8k: implement hw_control ops")
-Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Acked-by: Lee Jones <lee@kernel.org>
-Signed-off-by: David S. Miller <davem@davemloft.net>
----
- drivers/leds/trigger/ledtrig-netdev.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/leds/trigger/ledtrig-netdev.c
-+++ b/drivers/leds/trigger/ledtrig-netdev.c
-@@ -538,7 +538,7 @@ static void netdev_trig_work(struct work
- static int netdev_trig_activate(struct led_classdev *led_cdev)
- {
- struct led_netdev_data *trigger_data;
-- unsigned long mode;
-+ unsigned long mode = 0;
- struct device *dev;
- int rc;
-
+++ /dev/null
-From 7df1f14c04cbb1950e79c19793420f87227c3e80 Mon Sep 17 00:00:00 2001
-From: Andrew Lunn <andrew@lunn.ch>
-Date: Tue, 8 Aug 2023 23:04:33 +0200
-Subject: [PATCH 1/4] led: trig: netdev: Fix requesting offload device
-
-When the netdev trigger is activates, it tries to determine what
-device the LED blinks for, and what the current blink mode is.
-
-The documentation for hw_control_get() says:
-
- * Return 0 on success, a negative error number on failing parsing the
- * initial mode. Error from this function is NOT FATAL as the device
- * may be in a not supported initial state by the attached LED trigger.
- */
-
-For the Marvell PHY and the Armada 370-rd board, the initial LED blink
-mode is not supported by the trigger, so it returns an error. This
-resulted in not getting the device the LED is blinking for. As a
-result, the device is unknown and offloaded is never performed.
-
-Change to condition to always get the device if offloading is
-supported, and reduce the scope of testing for an error from
-hw_control_get() to skip setting trigger internal state if there is an
-error.
-
-Reviewed-by: Simon Horman <simon.horman@corigine.com>
-Signed-off-by: Andrew Lunn <andrew@lunn.ch>
-Tested-by: Daniel Golle <daniel@makrotopia.org>
-Link: https://lore.kernel.org/r/20230808210436.838995-2-andrew@lunn.ch
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/leds/trigger/ledtrig-netdev.c | 8 +++++---
- 1 file changed, 5 insertions(+), 3 deletions(-)
-
---- a/drivers/leds/trigger/ledtrig-netdev.c
-+++ b/drivers/leds/trigger/ledtrig-netdev.c
-@@ -564,15 +564,17 @@ static int netdev_trig_activate(struct l
- /* Check if hw control is active by default on the LED.
- * Init already enabled mode in hw control.
- */
-- if (supports_hw_control(led_cdev) &&
-- !led_cdev->hw_control_get(led_cdev, &mode)) {
-+ if (supports_hw_control(led_cdev)) {
- dev = led_cdev->hw_control_get_device(led_cdev);
- if (dev) {
- const char *name = dev_name(dev);
-
- set_device_name(trigger_data, name, strlen(name));
- trigger_data->hw_control = true;
-- trigger_data->mode = mode;
-+
-+ rc = led_cdev->hw_control_get(led_cdev, &mode);
-+ if (!rc)
-+ trigger_data->mode = mode;
- }
- }
-
+++ /dev/null
-From 1dcc03c9a7a824a31eaaecdfaa03542fb25feb6c Mon Sep 17 00:00:00 2001
-From: Andrew Lunn <andrew@lunn.ch>
-Date: Tue, 8 Aug 2023 23:04:34 +0200
-Subject: [PATCH 2/4] net: phy: phy_device: Call into the PHY driver to set LED
- offload
-
-Linux LEDs can be requested to perform hardware accelerated blinking
-to indicate link, RX, TX etc. Pass the rules for blinking to the PHY
-driver, if it implements the ops needed to determine if a given
-pattern can be offloaded, to offload it, and what the current offload
-is. Additionally implement the op needed to get what device the LED is
-for.
-
-Reviewed-by: Simon Horman <simon.horman@corigine.com>
-Signed-off-by: Andrew Lunn <andrew@lunn.ch>
-Tested-by: Daniel Golle <daniel@makrotopia.org>
-Link: https://lore.kernel.org/r/20230808210436.838995-3-andrew@lunn.ch
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/phy_device.c | 68 ++++++++++++++++++++++++++++++++++++
- include/linux/phy.h | 33 +++++++++++++++++
- 2 files changed, 101 insertions(+)
-
---- a/drivers/net/phy/phy_device.c
-+++ b/drivers/net/phy/phy_device.c
-@@ -3068,6 +3068,61 @@ static int phy_led_blink_set(struct led_
- return err;
- }
-
-+static __maybe_unused struct device *
-+phy_led_hw_control_get_device(struct led_classdev *led_cdev)
-+{
-+ struct phy_led *phyled = to_phy_led(led_cdev);
-+ struct phy_device *phydev = phyled->phydev;
-+
-+ if (phydev->attached_dev)
-+ return &phydev->attached_dev->dev;
-+ return NULL;
-+}
-+
-+static int __maybe_unused
-+phy_led_hw_control_get(struct led_classdev *led_cdev,
-+ unsigned long *rules)
-+{
-+ struct phy_led *phyled = to_phy_led(led_cdev);
-+ struct phy_device *phydev = phyled->phydev;
-+ int err;
-+
-+ mutex_lock(&phydev->lock);
-+ err = phydev->drv->led_hw_control_get(phydev, phyled->index, rules);
-+ mutex_unlock(&phydev->lock);
-+
-+ return err;
-+}
-+
-+static int __maybe_unused
-+phy_led_hw_control_set(struct led_classdev *led_cdev,
-+ unsigned long rules)
-+{
-+ struct phy_led *phyled = to_phy_led(led_cdev);
-+ struct phy_device *phydev = phyled->phydev;
-+ int err;
-+
-+ mutex_lock(&phydev->lock);
-+ err = phydev->drv->led_hw_control_set(phydev, phyled->index, rules);
-+ mutex_unlock(&phydev->lock);
-+
-+ return err;
-+}
-+
-+static __maybe_unused int phy_led_hw_is_supported(struct led_classdev *led_cdev,
-+ unsigned long rules)
-+{
-+ struct phy_led *phyled = to_phy_led(led_cdev);
-+ struct phy_device *phydev = phyled->phydev;
-+ int err;
-+
-+ mutex_lock(&phydev->lock);
-+ err = phydev->drv->led_hw_is_supported(phydev, phyled->index, rules);
-+ mutex_unlock(&phydev->lock);
-+
-+ return err;
-+}
-+
- static void phy_leds_unregister(struct phy_device *phydev)
- {
- struct phy_led *phyled;
-@@ -3105,6 +3160,19 @@ static int of_phy_led(struct phy_device
- cdev->brightness_set_blocking = phy_led_set_brightness;
- if (phydev->drv->led_blink_set)
- cdev->blink_set = phy_led_blink_set;
-+
-+#ifdef CONFIG_LEDS_TRIGGERS
-+ if (phydev->drv->led_hw_is_supported &&
-+ phydev->drv->led_hw_control_set &&
-+ phydev->drv->led_hw_control_get) {
-+ cdev->hw_control_is_supported = phy_led_hw_is_supported;
-+ cdev->hw_control_set = phy_led_hw_control_set;
-+ cdev->hw_control_get = phy_led_hw_control_get;
-+ cdev->hw_control_trigger = "netdev";
-+ }
-+
-+ cdev->hw_control_get_device = phy_led_hw_control_get_device;
-+#endif
- cdev->max_brightness = 1;
- init_data.devicename = dev_name(&phydev->mdio.dev);
- init_data.fwnode = of_fwnode_handle(led);
---- a/include/linux/phy.h
-+++ b/include/linux/phy.h
-@@ -1026,6 +1026,39 @@ struct phy_driver {
- int (*led_blink_set)(struct phy_device *dev, u8 index,
- unsigned long *delay_on,
- unsigned long *delay_off);
-+ /**
-+ * @led_hw_is_supported: Can the HW support the given rules.
-+ * @dev: PHY device which has the LED
-+ * @index: Which LED of the PHY device
-+ * @rules The core is interested in these rules
-+ *
-+ * Return 0 if yes, -EOPNOTSUPP if not, or an error code.
-+ */
-+ int (*led_hw_is_supported)(struct phy_device *dev, u8 index,
-+ unsigned long rules);
-+ /**
-+ * @led_hw_control_set: Set the HW to control the LED
-+ * @dev: PHY device which has the LED
-+ * @index: Which LED of the PHY device
-+ * @rules The rules used to control the LED
-+ *
-+ * Returns 0, or a an error code.
-+ */
-+ int (*led_hw_control_set)(struct phy_device *dev, u8 index,
-+ unsigned long rules);
-+ /**
-+ * @led_hw_control_get: Get how the HW is controlling the LED
-+ * @dev: PHY device which has the LED
-+ * @index: Which LED of the PHY device
-+ * @rules Pointer to the rules used to control the LED
-+ *
-+ * Set *@rules to how the HW is currently blinking. Returns 0
-+ * on success, or a error code if the current blinking cannot
-+ * be represented in rules, or some other error happens.
-+ */
-+ int (*led_hw_control_get)(struct phy_device *dev, u8 index,
-+ unsigned long *rules);
-+
- };
- #define to_phy_driver(d) container_of(to_mdio_common_driver(d), \
- struct phy_driver, mdiodrv)
+++ /dev/null
-From 460b0b648fab24f576c481424e0de5479ffb9786 Mon Sep 17 00:00:00 2001
-From: Andrew Lunn <andrew@lunn.ch>
-Date: Tue, 8 Aug 2023 23:04:35 +0200
-Subject: [PATCH 3/4] net: phy: marvell: Add support for offloading LED
- blinking
-
-Add the code needed to indicate if a given blinking pattern can be
-offloaded, to offload a pattern and to try to return the current
-pattern.
-
-Reviewed-by: Simon Horman <simon.horman@corigine.com>
-Signed-off-by: Andrew Lunn <andrew@lunn.ch>
-Tested-by: Daniel Golle <daniel@makrotopia.org>
-Link: https://lore.kernel.org/r/20230808210436.838995-4-andrew@lunn.ch
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/net/phy/marvell.c | 281 ++++++++++++++++++++++++++++++++++++++
- 1 file changed, 281 insertions(+)
-
---- a/drivers/net/phy/marvell.c
-+++ b/drivers/net/phy/marvell.c
-@@ -2893,6 +2893,272 @@ static int m88e1318_led_blink_set(struct
- MII_88E1318S_PHY_LED_FUNC, reg);
- }
-
-+struct marvell_led_rules {
-+ int mode;
-+ unsigned long rules;
-+};
-+
-+static const struct marvell_led_rules marvell_led0[] = {
-+ {
-+ .mode = 0,
-+ .rules = BIT(TRIGGER_NETDEV_LINK),
-+ },
-+ {
-+ .mode = 1,
-+ .rules = (BIT(TRIGGER_NETDEV_LINK) |
-+ BIT(TRIGGER_NETDEV_RX) |
-+ BIT(TRIGGER_NETDEV_TX)),
-+ },
-+ {
-+ .mode = 3,
-+ .rules = (BIT(TRIGGER_NETDEV_RX) |
-+ BIT(TRIGGER_NETDEV_TX)),
-+ },
-+ {
-+ .mode = 4,
-+ .rules = (BIT(TRIGGER_NETDEV_RX) |
-+ BIT(TRIGGER_NETDEV_TX)),
-+ },
-+ {
-+ .mode = 5,
-+ .rules = BIT(TRIGGER_NETDEV_TX),
-+ },
-+ {
-+ .mode = 6,
-+ .rules = BIT(TRIGGER_NETDEV_LINK),
-+ },
-+ {
-+ .mode = 7,
-+ .rules = BIT(TRIGGER_NETDEV_LINK_1000),
-+ },
-+ {
-+ .mode = 8,
-+ .rules = 0,
-+ },
-+};
-+
-+static const struct marvell_led_rules marvell_led1[] = {
-+ {
-+ .mode = 1,
-+ .rules = (BIT(TRIGGER_NETDEV_LINK) |
-+ BIT(TRIGGER_NETDEV_RX) |
-+ BIT(TRIGGER_NETDEV_TX)),
-+ },
-+ {
-+ .mode = 2,
-+ .rules = (BIT(TRIGGER_NETDEV_LINK) |
-+ BIT(TRIGGER_NETDEV_RX)),
-+ },
-+ {
-+ .mode = 3,
-+ .rules = (BIT(TRIGGER_NETDEV_RX) |
-+ BIT(TRIGGER_NETDEV_TX)),
-+ },
-+ {
-+ .mode = 4,
-+ .rules = (BIT(TRIGGER_NETDEV_RX) |
-+ BIT(TRIGGER_NETDEV_TX)),
-+ },
-+ {
-+ .mode = 6,
-+ .rules = (BIT(TRIGGER_NETDEV_LINK_100) |
-+ BIT(TRIGGER_NETDEV_LINK_1000)),
-+ },
-+ {
-+ .mode = 7,
-+ .rules = BIT(TRIGGER_NETDEV_LINK_100),
-+ },
-+ {
-+ .mode = 8,
-+ .rules = 0,
-+ },
-+};
-+
-+static const struct marvell_led_rules marvell_led2[] = {
-+ {
-+ .mode = 0,
-+ .rules = BIT(TRIGGER_NETDEV_LINK),
-+ },
-+ {
-+ .mode = 1,
-+ .rules = (BIT(TRIGGER_NETDEV_LINK) |
-+ BIT(TRIGGER_NETDEV_RX) |
-+ BIT(TRIGGER_NETDEV_TX)),
-+ },
-+ {
-+ .mode = 3,
-+ .rules = (BIT(TRIGGER_NETDEV_RX) |
-+ BIT(TRIGGER_NETDEV_TX)),
-+ },
-+ {
-+ .mode = 4,
-+ .rules = (BIT(TRIGGER_NETDEV_RX) |
-+ BIT(TRIGGER_NETDEV_TX)),
-+ },
-+ {
-+ .mode = 5,
-+ .rules = BIT(TRIGGER_NETDEV_TX),
-+ },
-+ {
-+ .mode = 6,
-+ .rules = (BIT(TRIGGER_NETDEV_LINK_10) |
-+ BIT(TRIGGER_NETDEV_LINK_1000)),
-+ },
-+ {
-+ .mode = 7,
-+ .rules = BIT(TRIGGER_NETDEV_LINK_10),
-+ },
-+ {
-+ .mode = 8,
-+ .rules = 0,
-+ },
-+};
-+
-+static int marvell_find_led_mode(unsigned long rules,
-+ const struct marvell_led_rules *marvell_rules,
-+ int count,
-+ int *mode)
-+{
-+ int i;
-+
-+ for (i = 0; i < count; i++) {
-+ if (marvell_rules[i].rules == rules) {
-+ *mode = marvell_rules[i].mode;
-+ return 0;
-+ }
-+ }
-+ return -EOPNOTSUPP;
-+}
-+
-+static int marvell_get_led_mode(u8 index, unsigned long rules, int *mode)
-+{
-+ int ret;
-+
-+ switch (index) {
-+ case 0:
-+ ret = marvell_find_led_mode(rules, marvell_led0,
-+ ARRAY_SIZE(marvell_led0), mode);
-+ break;
-+ case 1:
-+ ret = marvell_find_led_mode(rules, marvell_led1,
-+ ARRAY_SIZE(marvell_led1), mode);
-+ break;
-+ case 2:
-+ ret = marvell_find_led_mode(rules, marvell_led2,
-+ ARRAY_SIZE(marvell_led2), mode);
-+ break;
-+ default:
-+ ret = -EINVAL;
-+ }
-+
-+ return ret;
-+}
-+
-+static int marvell_find_led_rules(unsigned long *rules,
-+ const struct marvell_led_rules *marvell_rules,
-+ int count,
-+ int mode)
-+{
-+ int i;
-+
-+ for (i = 0; i < count; i++) {
-+ if (marvell_rules[i].mode == mode) {
-+ *rules = marvell_rules[i].rules;
-+ return 0;
-+ }
-+ }
-+ return -EOPNOTSUPP;
-+}
-+
-+static int marvell_get_led_rules(u8 index, unsigned long *rules, int mode)
-+{
-+ int ret;
-+
-+ switch (index) {
-+ case 0:
-+ ret = marvell_find_led_rules(rules, marvell_led0,
-+ ARRAY_SIZE(marvell_led0), mode);
-+ break;
-+ case 1:
-+ ret = marvell_find_led_rules(rules, marvell_led1,
-+ ARRAY_SIZE(marvell_led1), mode);
-+ break;
-+ case 2:
-+ ret = marvell_find_led_rules(rules, marvell_led2,
-+ ARRAY_SIZE(marvell_led2), mode);
-+ break;
-+ default:
-+ ret = -EOPNOTSUPP;
-+ }
-+
-+ return ret;
-+}
-+
-+static int m88e1318_led_hw_is_supported(struct phy_device *phydev, u8 index,
-+ unsigned long rules)
-+{
-+ int mode, ret;
-+
-+ switch (index) {
-+ case 0:
-+ case 1:
-+ case 2:
-+ ret = marvell_get_led_mode(index, rules, &mode);
-+ break;
-+ default:
-+ ret = -EINVAL;
-+ }
-+
-+ return ret;
-+}
-+
-+static int m88e1318_led_hw_control_set(struct phy_device *phydev, u8 index,
-+ unsigned long rules)
-+{
-+ int mode, ret, reg;
-+
-+ switch (index) {
-+ case 0:
-+ case 1:
-+ case 2:
-+ ret = marvell_get_led_mode(index, rules, &mode);
-+ break;
-+ default:
-+ ret = -EINVAL;
-+ }
-+
-+ if (ret < 0)
-+ return ret;
-+
-+ reg = phy_read_paged(phydev, MII_MARVELL_LED_PAGE,
-+ MII_88E1318S_PHY_LED_FUNC);
-+ if (reg < 0)
-+ return reg;
-+
-+ reg &= ~(0xf << (4 * index));
-+ reg |= mode << (4 * index);
-+ return phy_write_paged(phydev, MII_MARVELL_LED_PAGE,
-+ MII_88E1318S_PHY_LED_FUNC, reg);
-+}
-+
-+static int m88e1318_led_hw_control_get(struct phy_device *phydev, u8 index,
-+ unsigned long *rules)
-+{
-+ int mode, reg;
-+
-+ if (index > 2)
-+ return -EINVAL;
-+
-+ reg = phy_read_paged(phydev, MII_MARVELL_LED_PAGE,
-+ MII_88E1318S_PHY_LED_FUNC);
-+ if (reg < 0)
-+ return reg;
-+
-+ mode = (reg >> (4 * index)) & 0xf;
-+
-+ return marvell_get_led_rules(index, rules, mode);
-+}
-+
- static int marvell_probe(struct phy_device *phydev)
- {
- struct marvell_priv *priv;
-@@ -3144,6 +3410,9 @@ static struct phy_driver marvell_drivers
- .get_stats = marvell_get_stats,
- .led_brightness_set = m88e1318_led_brightness_set,
- .led_blink_set = m88e1318_led_blink_set,
-+ .led_hw_is_supported = m88e1318_led_hw_is_supported,
-+ .led_hw_control_set = m88e1318_led_hw_control_set,
-+ .led_hw_control_get = m88e1318_led_hw_control_get,
- },
- {
- .phy_id = MARVELL_PHY_ID_88E1145,
-@@ -3252,6 +3521,9 @@ static struct phy_driver marvell_drivers
- .cable_test_get_status = marvell_vct7_cable_test_get_status,
- .led_brightness_set = m88e1318_led_brightness_set,
- .led_blink_set = m88e1318_led_blink_set,
-+ .led_hw_is_supported = m88e1318_led_hw_is_supported,
-+ .led_hw_control_set = m88e1318_led_hw_control_set,
-+ .led_hw_control_get = m88e1318_led_hw_control_get,
- },
- {
- .phy_id = MARVELL_PHY_ID_88E1540,
-@@ -3280,6 +3552,9 @@ static struct phy_driver marvell_drivers
- .cable_test_get_status = marvell_vct7_cable_test_get_status,
- .led_brightness_set = m88e1318_led_brightness_set,
- .led_blink_set = m88e1318_led_blink_set,
-+ .led_hw_is_supported = m88e1318_led_hw_is_supported,
-+ .led_hw_control_set = m88e1318_led_hw_control_set,
-+ .led_hw_control_get = m88e1318_led_hw_control_get,
- },
- {
- .phy_id = MARVELL_PHY_ID_88E1545,
-@@ -3308,6 +3583,9 @@ static struct phy_driver marvell_drivers
- .cable_test_get_status = marvell_vct7_cable_test_get_status,
- .led_brightness_set = m88e1318_led_brightness_set,
- .led_blink_set = m88e1318_led_blink_set,
-+ .led_hw_is_supported = m88e1318_led_hw_is_supported,
-+ .led_hw_control_set = m88e1318_led_hw_control_set,
-+ .led_hw_control_get = m88e1318_led_hw_control_get,
- },
- {
- .phy_id = MARVELL_PHY_ID_88E3016,
-@@ -3451,6 +3729,9 @@ static struct phy_driver marvell_drivers
- .set_tunable = m88e1540_set_tunable,
- .led_brightness_set = m88e1318_led_brightness_set,
- .led_blink_set = m88e1318_led_blink_set,
-+ .led_hw_is_supported = m88e1318_led_hw_is_supported,
-+ .led_hw_control_set = m88e1318_led_hw_control_set,
-+ .led_hw_control_get = m88e1318_led_hw_control_get,
- },
- };
-
+++ /dev/null
-From e8fbcc47a8e935f36f044d85f21a99acecbd7bfb Mon Sep 17 00:00:00 2001
-From: Andrew Lunn <andrew@lunn.ch>
-Date: Tue, 8 Aug 2023 23:04:36 +0200
-Subject: [PATCH 4/4] leds: trig-netdev: Disable offload on deactivation of
- trigger
-
-Ensure that the offloading of blinking is stopped when the trigger is
-deactivated. Calling led_set_brightness() is documented as stopping
-offload and setting the LED to a constant brightness.
-
-Suggested-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: Andrew Lunn <andrew@lunn.ch>
-Reviewed-by: Simon Horman <simon.horman@corigine.com>
-Tested-by: Daniel Golle <daniel@makrotopia.org>
-Link: https://lore.kernel.org/r/20230808210436.838995-5-andrew@lunn.ch
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/leds/trigger/ledtrig-netdev.c | 2 ++
- 1 file changed, 2 insertions(+)
-
---- a/drivers/leds/trigger/ledtrig-netdev.c
-+++ b/drivers/leds/trigger/ledtrig-netdev.c
-@@ -595,6 +595,8 @@ static void netdev_trig_deactivate(struc
-
- cancel_delayed_work_sync(&trigger_data->work);
-
-+ led_set_brightness(led_cdev, LED_OFF);
-+
- dev_put(trigger_data->net_dev);
-
- kfree(trigger_data);
+++ /dev/null
-From c5d264d4b527c96ae8903376a4b195df47b05203 Mon Sep 17 00:00:00 2001
-From: Michael Walle <michael@walle.cc>
-Date: Mon, 6 Feb 2023 13:43:43 +0000
-Subject: [PATCH] of: base: add of_parse_phandle_with_optional_args()
-
-Add a new variant of the of_parse_phandle_with_args() which treats the
-cells name as optional. If it's missing, it is assumed that the phandle
-has no arguments.
-
-Up until now, a nvmem node didn't have any arguments, so all the device
-trees haven't any '#*-cells' property. But there is a need for an
-additional argument for the phandle, for which we need a '#*-cells'
-property. Therefore, we need to support nvmem nodes with and without
-this property.
-
-Signed-off-by: Michael Walle <michael@walle.cc>
-Reviewed-by: Rob Herring <robh@kernel.org>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230206134356.839737-10-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- include/linux/of.h | 25 +++++++++++++++++++++++++
- 1 file changed, 25 insertions(+)
-
---- a/include/linux/of.h
-+++ b/include/linux/of.h
-@@ -1009,6 +1009,31 @@ static inline int of_parse_phandle_with_
- }
-
- /**
-+ * of_parse_phandle_with_optional_args() - Find a node pointed by phandle in a list
-+ * @np: pointer to a device tree node containing a list
-+ * @list_name: property name that contains a list
-+ * @cells_name: property name that specifies phandles' arguments count
-+ * @index: index of a phandle to parse out
-+ * @out_args: optional pointer to output arguments structure (will be filled)
-+ *
-+ * Same as of_parse_phandle_with_args() except that if the cells_name property
-+ * is not found, cell_count of 0 is assumed.
-+ *
-+ * This is used to useful, if you have a phandle which didn't have arguments
-+ * before and thus doesn't have a '#*-cells' property but is now migrated to
-+ * having arguments while retaining backwards compatibility.
-+ */
-+static inline int of_parse_phandle_with_optional_args(const struct device_node *np,
-+ const char *list_name,
-+ const char *cells_name,
-+ int index,
-+ struct of_phandle_args *out_args)
-+{
-+ return __of_parse_phandle_with_args(np, list_name, cells_name,
-+ 0, index, out_args);
-+}
-+
-+/**
- * of_property_count_u8_elems - Count the number of u8 elements in a property
- *
- * @np: device node from which the property value is to be read.
+++ /dev/null
-From ff24fed10ba414d19579e26e60b126fad2f2bb07 Mon Sep 17 00:00:00 2001
-From: Michael Walle <michael@walle.cc>
-Date: Mon, 6 Feb 2023 13:43:44 +0000
-Subject: [PATCH] of: property: make #.*-cells optional for simple props
-
-Sometimes, future bindings for phandles will get additional arguments.
-Thus the target node of the phandle will need a new #.*-cells property.
-To be backwards compatible, this needs to be optional.
-
-Prepare the DEFINE_SIMPLE_PROPS() to handle the cells name as optional.
-
-Signed-off-by: Michael Walle <michael@walle.cc>
-Tested-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Reviewed-by: Rob Herring <robh@kernel.org>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230206134356.839737-11-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/of/property.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
---- a/drivers/of/property.c
-+++ b/drivers/of/property.c
-@@ -1146,8 +1146,8 @@ static struct device_node *parse_prop_ce
- if (strcmp(prop_name, list_name))
- return NULL;
-
-- if (of_parse_phandle_with_args(np, list_name, cells_name, index,
-- &sup_args))
-+ if (__of_parse_phandle_with_args(np, list_name, cells_name, 0, index,
-+ &sup_args))
- return NULL;
-
- return sup_args.np;
+++ /dev/null
-From e2d8172043d2e50df19fcd59c11e5593de8188d7 Mon Sep 17 00:00:00 2001
-From: Michael Walle <michael@walle.cc>
-Date: Mon, 6 Feb 2023 13:43:45 +0000
-Subject: [PATCH] of: property: add #nvmem-cell-cells property
-
-Bindings describe the new '#nvmem-cell-cells' property. Now that the
-arguments count property is optional, we just add this property to the
-nvmem-cells.
-
-Signed-off-by: Michael Walle <michael@walle.cc>
-Tested-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Reviewed-by: Rob Herring <robh@kernel.org>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230206134356.839737-12-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/of/property.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/of/property.c
-+++ b/drivers/of/property.c
-@@ -1251,7 +1251,7 @@ DEFINE_SIMPLE_PROP(dmas, "dmas", "#dma-c
- DEFINE_SIMPLE_PROP(power_domains, "power-domains", "#power-domain-cells")
- DEFINE_SIMPLE_PROP(hwlocks, "hwlocks", "#hwlock-cells")
- DEFINE_SIMPLE_PROP(extcon, "extcon", NULL)
--DEFINE_SIMPLE_PROP(nvmem_cells, "nvmem-cells", NULL)
-+DEFINE_SIMPLE_PROP(nvmem_cells, "nvmem-cells", "#nvmem-cell-cells")
- DEFINE_SIMPLE_PROP(phys, "phys", "#phy-cells")
- DEFINE_SIMPLE_PROP(wakeup_parent, "wakeup-parent", NULL)
- DEFINE_SIMPLE_PROP(pinctrl0, "pinctrl-0", NULL)
+++ /dev/null
-From 553bd29700145e1849698985e9800f14e967da49 Mon Sep 17 00:00:00 2001
-From: Alexander Stein <alexander.stein@ew.tq-group.com>
-Date: Tue, 7 Feb 2023 12:05:29 +0100
-Subject: [PATCH] of: device: Ignore modalias of reused nodes
-
-If of_node is reused, do not use that node's modalias. This will hide
-the name of the actual device. This is rather prominent in USB glue
-drivers creating a platform device for the host controller.
-
-Signed-off-by: Alexander Stein <alexander.stein@ew.tq-group.com>
-Reviewed-by: Rob Herring <robh@kernel.org>
-Link: https://lore.kernel.org/r/20230207110531.1060252-2-alexander.stein@ew.tq-group.com
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/of/device.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
---- a/drivers/of/device.c
-+++ b/drivers/of/device.c
-@@ -256,7 +256,7 @@ static ssize_t of_device_get_modalias(st
- ssize_t csize;
- ssize_t tsize;
-
-- if ((!dev) || (!dev->of_node))
-+ if ((!dev) || (!dev->of_node) || dev->of_node_reused)
- return -ENODEV;
-
- /* Name & Type */
-@@ -379,7 +379,7 @@ int of_device_uevent_modalias(struct dev
- {
- int sl;
-
-- if ((!dev) || (!dev->of_node))
-+ if ((!dev) || (!dev->of_node) || dev->of_node_reused)
- return -ENODEV;
-
- /* Devicetree modalias is tricky, we add it in 2 steps */
+++ /dev/null
-From 2295bed9bebe8d1eef276194fed5b5fbe89c5363 Mon Sep 17 00:00:00 2001
-From: Alexander Stein <alexander.stein@ew.tq-group.com>
-Date: Tue, 7 Feb 2023 12:05:30 +0100
-Subject: [PATCH] of: device: Do not ignore error code in
- of_device_uevent_modalias
-
-of_device_get_modalias might return an error code, propagate that one.
-Otherwise the negative, signed integer is propagated to unsigned integer
-for the comparison resulting in a huge 'sl' size.
-
-Signed-off-by: Alexander Stein <alexander.stein@ew.tq-group.com>
-Reviewed-by: Rob Herring <robh@kernel.org>
-Link: https://lore.kernel.org/r/20230207110531.1060252-3-alexander.stein@ew.tq-group.com
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/of/device.c | 2 ++
- 1 file changed, 2 insertions(+)
-
---- a/drivers/of/device.c
-+++ b/drivers/of/device.c
-@@ -388,6 +388,8 @@ int of_device_uevent_modalias(struct dev
-
- sl = of_device_get_modalias(dev, &env->buf[env->buflen-1],
- sizeof(env->buf) - env->buflen);
-+ if (sl < 0)
-+ return sl;
- if (sl >= (sizeof(env->buf) - env->buflen))
- return -ENOMEM;
- env->buflen += sl;
+++ /dev/null
-From 5c3d15e127ebfc0754cd18def7124633b6d46672 Mon Sep 17 00:00:00 2001
-From: Miquel Raynal <miquel.raynal@bootlin.com>
-Date: Tue, 4 Apr 2023 18:21:15 +0100
-Subject: [PATCH] of: Update of_device_get_modalias()
-
-This function only needs a "struct device_node" to work, but for
-convenience the author (and only user) of this helper did use a "struct
-device" and put it in device.c.
-
-Let's convert this helper to take a "struct device node" instead. This
-change asks for two additional changes: renaming it "of_modalias()"
-to fit the current naming, and moving it outside of device.c which will
-be done in a follow-up commit.
-
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Reviewed-by: Rob Herring <robh@kernel.org>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230404172148.82422-8-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/of/device.c | 29 +++++++++++++++++------------
- 1 file changed, 17 insertions(+), 12 deletions(-)
-
---- a/drivers/of/device.c
-+++ b/drivers/of/device.c
-@@ -248,7 +248,7 @@ const void *of_device_get_match_data(con
- }
- EXPORT_SYMBOL(of_device_get_match_data);
-
--static ssize_t of_device_get_modalias(struct device *dev, char *str, ssize_t len)
-+static ssize_t of_modalias(const struct device_node *np, char *str, ssize_t len)
- {
- const char *compat;
- char *c;
-@@ -256,19 +256,16 @@ static ssize_t of_device_get_modalias(st
- ssize_t csize;
- ssize_t tsize;
-
-- if ((!dev) || (!dev->of_node) || dev->of_node_reused)
-- return -ENODEV;
--
- /* Name & Type */
- /* %p eats all alphanum characters, so %c must be used here */
-- csize = snprintf(str, len, "of:N%pOFn%c%s", dev->of_node, 'T',
-- of_node_get_device_type(dev->of_node));
-+ csize = snprintf(str, len, "of:N%pOFn%c%s", np, 'T',
-+ of_node_get_device_type(np));
- tsize = csize;
- len -= csize;
- if (str)
- str += csize;
-
-- of_property_for_each_string(dev->of_node, "compatible", p, compat) {
-+ of_property_for_each_string(np, "compatible", p, compat) {
- csize = strlen(compat) + 1;
- tsize += csize;
- if (csize > len)
-@@ -293,7 +290,10 @@ int of_device_request_module(struct devi
- ssize_t size;
- int ret;
-
-- size = of_device_get_modalias(dev, NULL, 0);
-+ if (!dev || !dev->of_node)
-+ return -ENODEV;
-+
-+ size = of_modalias(dev->of_node, NULL, 0);
- if (size < 0)
- return size;
-
-@@ -304,7 +304,7 @@ int of_device_request_module(struct devi
- if (!str)
- return -ENOMEM;
-
-- of_device_get_modalias(dev, str, size);
-+ of_modalias(dev->of_node, str, size);
- str[size - 1] = '\0';
- ret = request_module(str);
- kfree(str);
-@@ -321,7 +321,12 @@ EXPORT_SYMBOL_GPL(of_device_request_modu
- */
- ssize_t of_device_modalias(struct device *dev, char *str, ssize_t len)
- {
-- ssize_t sl = of_device_get_modalias(dev, str, len - 2);
-+ ssize_t sl;
-+
-+ if (!dev || !dev->of_node || dev->of_node_reused)
-+ return -ENODEV;
-+
-+ sl = of_modalias(dev->of_node, str, len - 2);
- if (sl < 0)
- return sl;
- if (sl > len - 2)
-@@ -386,8 +391,8 @@ int of_device_uevent_modalias(struct dev
- if (add_uevent_var(env, "MODALIAS="))
- return -ENOMEM;
-
-- sl = of_device_get_modalias(dev, &env->buf[env->buflen-1],
-- sizeof(env->buf) - env->buflen);
-+ sl = of_modalias(dev->of_node, &env->buf[env->buflen-1],
-+ sizeof(env->buf) - env->buflen);
- if (sl < 0)
- return sl;
- if (sl >= (sizeof(env->buf) - env->buflen))
+++ /dev/null
-From 673aa1ed1c9b6710bf24e3f0957d85e2f46c77db Mon Sep 17 00:00:00 2001
-From: Miquel Raynal <miquel.raynal@bootlin.com>
-Date: Tue, 4 Apr 2023 18:21:16 +0100
-Subject: [PATCH] of: Rename of_modalias_node()
-
-This helper does not produce a real modalias, but tries to get the
-"product" compatible part of the "vendor,product" compatibles only. It
-is far from creating a purely useful modalias string and does not seem
-to be used like that directly anyway, so let's try to give this helper a
-more meaningful name before moving there a real modalias helper (already
-existing under of/device.c).
-
-Also update the various documentations to refer to the strings as
-"aliases" rather than "modaliases" which has a real meaning in the Linux
-kernel.
-
-There is no functional change.
-
-Cc: Rafael J. Wysocki <rafael@kernel.org>
-Cc: Len Brown <lenb@kernel.org>
-Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
-Cc: Maxime Ripard <mripard@kernel.org>
-Cc: Thomas Zimmermann <tzimmermann@suse.de>
-Cc: Sebastian Reichel <sre@kernel.org>
-Cc: Wolfram Sang <wsa@kernel.org>
-Cc: Mark Brown <broonie@kernel.org>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Reviewed-by: Rob Herring <robh@kernel.org>
-Acked-by: Mark Brown <broonie@kernel.org>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Acked-by: Sebastian Reichel <sre@kernel.org>
-Link: https://lore.kernel.org/r/20230404172148.82422-9-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/acpi/bus.c | 7 ++++---
- drivers/gpu/drm/drm_mipi_dsi.c | 2 +-
- drivers/hsi/hsi_core.c | 2 +-
- drivers/i2c/busses/i2c-powermac.c | 2 +-
- drivers/i2c/i2c-core-of.c | 2 +-
- drivers/of/base.c | 18 +++++++++++-------
- drivers/spi/spi.c | 4 ++--
- include/linux/of.h | 3 ++-
- 8 files changed, 23 insertions(+), 17 deletions(-)
-
---- a/drivers/acpi/bus.c
-+++ b/drivers/acpi/bus.c
-@@ -806,9 +806,10 @@ static bool acpi_of_modalias(struct acpi
- * @modalias: Pointer to buffer that modalias value will be copied into
- * @len: Length of modalias buffer
- *
-- * This is a counterpart of of_modalias_node() for struct acpi_device objects.
-- * If there is a compatible string for @adev, it will be copied to @modalias
-- * with the vendor prefix stripped; otherwise, @default_id will be used.
-+ * This is a counterpart of of_alias_from_compatible() for struct acpi_device
-+ * objects. If there is a compatible string for @adev, it will be copied to
-+ * @modalias with the vendor prefix stripped; otherwise, @default_id will be
-+ * used.
- */
- void acpi_set_modalias(struct acpi_device *adev, const char *default_id,
- char *modalias, size_t len)
---- a/drivers/gpu/drm/drm_mipi_dsi.c
-+++ b/drivers/gpu/drm/drm_mipi_dsi.c
-@@ -160,7 +160,7 @@ of_mipi_dsi_device_add(struct mipi_dsi_h
- int ret;
- u32 reg;
-
-- if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
-+ if (of_alias_from_compatible(node, info.type, sizeof(info.type)) < 0) {
- drm_err(host, "modalias failure on %pOF\n", node);
- return ERR_PTR(-EINVAL);
- }
---- a/drivers/hsi/hsi_core.c
-+++ b/drivers/hsi/hsi_core.c
-@@ -207,7 +207,7 @@ static void hsi_add_client_from_dt(struc
- if (!cl)
- return;
-
-- err = of_modalias_node(client, name, sizeof(name));
-+ err = of_alias_from_compatible(client, name, sizeof(name));
- if (err)
- goto err;
-
---- a/drivers/i2c/busses/i2c-powermac.c
-+++ b/drivers/i2c/busses/i2c-powermac.c
-@@ -284,7 +284,7 @@ static bool i2c_powermac_get_type(struct
- */
-
- /* First try proper modalias */
-- if (of_modalias_node(node, tmp, sizeof(tmp)) >= 0) {
-+ if (of_alias_from_compatible(node, tmp, sizeof(tmp)) >= 0) {
- snprintf(type, type_size, "MAC,%s", tmp);
- return true;
- }
---- a/drivers/i2c/i2c-core-of.c
-+++ b/drivers/i2c/i2c-core-of.c
-@@ -27,7 +27,7 @@ int of_i2c_get_board_info(struct device
-
- memset(info, 0, sizeof(*info));
-
-- if (of_modalias_node(node, info->type, sizeof(info->type)) < 0) {
-+ if (of_alias_from_compatible(node, info->type, sizeof(info->type)) < 0) {
- dev_err(dev, "of_i2c: modalias failure on %pOF\n", node);
- return -EINVAL;
- }
---- a/drivers/of/base.c
-+++ b/drivers/of/base.c
-@@ -1208,19 +1208,23 @@ struct device_node *of_find_matching_nod
- EXPORT_SYMBOL(of_find_matching_node_and_match);
-
- /**
-- * of_modalias_node - Lookup appropriate modalias for a device node
-+ * of_alias_from_compatible - Lookup appropriate alias for a device node
-+ * depending on compatible
- * @node: pointer to a device tree node
-- * @modalias: Pointer to buffer that modalias value will be copied into
-- * @len: Length of modalias value
-+ * @alias: Pointer to buffer that alias value will be copied into
-+ * @len: Length of alias value
- *
- * Based on the value of the compatible property, this routine will attempt
-- * to choose an appropriate modalias value for a particular device tree node.
-+ * to choose an appropriate alias value for a particular device tree node.
- * It does this by stripping the manufacturer prefix (as delimited by a ',')
- * from the first entry in the compatible list property.
- *
-+ * Note: The matching on just the "product" side of the compatible is a relic
-+ * from I2C and SPI. Please do not add any new user.
-+ *
- * Return: This routine returns 0 on success, <0 on failure.
- */
--int of_modalias_node(struct device_node *node, char *modalias, int len)
-+int of_alias_from_compatible(const struct device_node *node, char *alias, int len)
- {
- const char *compatible, *p;
- int cplen;
-@@ -1229,10 +1233,10 @@ int of_modalias_node(struct device_node
- if (!compatible || strlen(compatible) > cplen)
- return -ENODEV;
- p = strchr(compatible, ',');
-- strscpy(modalias, p ? p + 1 : compatible, len);
-+ strscpy(alias, p ? p + 1 : compatible, len);
- return 0;
- }
--EXPORT_SYMBOL_GPL(of_modalias_node);
-+EXPORT_SYMBOL_GPL(of_alias_from_compatible);
-
- /**
- * of_find_node_by_phandle - Find a node given a phandle
---- a/drivers/spi/spi.c
-+++ b/drivers/spi/spi.c
-@@ -2330,8 +2330,8 @@ of_register_spi_device(struct spi_contro
- }
-
- /* Select device driver */
-- rc = of_modalias_node(nc, spi->modalias,
-- sizeof(spi->modalias));
-+ rc = of_alias_from_compatible(nc, spi->modalias,
-+ sizeof(spi->modalias));
- if (rc < 0) {
- dev_err(&ctlr->dev, "cannot find modalias for %pOF\n", nc);
- goto err_out;
---- a/include/linux/of.h
-+++ b/include/linux/of.h
-@@ -362,7 +362,8 @@ extern int of_n_addr_cells(struct device
- extern int of_n_size_cells(struct device_node *np);
- extern const struct of_device_id *of_match_node(
- const struct of_device_id *matches, const struct device_node *node);
--extern int of_modalias_node(struct device_node *node, char *modalias, int len);
-+extern int of_alias_from_compatible(const struct device_node *node, char *alias,
-+ int len);
- extern void of_print_phandle_args(const char *msg, const struct of_phandle_args *args);
- extern int __of_parse_phandle_with_args(const struct device_node *np,
- const char *list_name, const char *cells_name, int cell_count,
+++ /dev/null
-From bd7a7ed774afd1a4174df34227626c95573be517 Mon Sep 17 00:00:00 2001
-From: Miquel Raynal <miquel.raynal@bootlin.com>
-Date: Tue, 4 Apr 2023 18:21:17 +0100
-Subject: [PATCH] of: Move of_modalias() to module.c
-
-Create a specific .c file for OF related module handling.
-Move of_modalias() inside as a first step.
-
-The helper is exposed through of.h even though it is only used by core
-files because the users from device.c will soon be split into an OF-only
-helper in module.c as well as a device-oriented inline helper in
-of_device.h. Putting this helper in of_private.h would require to
-include of_private.h from of_device.h, which is not acceptable.
-
-Suggested-by: Rob Herring <robh+dt@kernel.org>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Reviewed-by: Rob Herring <robh@kernel.org>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230404172148.82422-10-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/of/Makefile | 2 +-
- drivers/of/device.c | 37 -------------------------------------
- drivers/of/module.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
- include/linux/of.h | 9 +++++++++
- 4 files changed, 54 insertions(+), 38 deletions(-)
- create mode 100644 drivers/of/module.c
-
---- a/drivers/of/Makefile
-+++ b/drivers/of/Makefile
-@@ -1,5 +1,5 @@
- # SPDX-License-Identifier: GPL-2.0
--obj-y = base.o device.o platform.o property.o
-+obj-y = base.o device.o module.o platform.o property.o
- obj-$(CONFIG_OF_KOBJ) += kobj.o
- obj-$(CONFIG_OF_DYNAMIC) += dynamic.o
- obj-$(CONFIG_OF_FLATTREE) += fdt.o
---- a/drivers/of/device.c
-+++ b/drivers/of/device.c
-@@ -1,5 +1,4 @@
- // SPDX-License-Identifier: GPL-2.0
--#include <linux/string.h>
- #include <linux/kernel.h>
- #include <linux/of.h>
- #include <linux/of_device.h>
-@@ -248,42 +247,6 @@ const void *of_device_get_match_data(con
- }
- EXPORT_SYMBOL(of_device_get_match_data);
-
--static ssize_t of_modalias(const struct device_node *np, char *str, ssize_t len)
--{
-- const char *compat;
-- char *c;
-- struct property *p;
-- ssize_t csize;
-- ssize_t tsize;
--
-- /* Name & Type */
-- /* %p eats all alphanum characters, so %c must be used here */
-- csize = snprintf(str, len, "of:N%pOFn%c%s", np, 'T',
-- of_node_get_device_type(np));
-- tsize = csize;
-- len -= csize;
-- if (str)
-- str += csize;
--
-- of_property_for_each_string(np, "compatible", p, compat) {
-- csize = strlen(compat) + 1;
-- tsize += csize;
-- if (csize > len)
-- continue;
--
-- csize = snprintf(str, len, "C%s", compat);
-- for (c = str; c; ) {
-- c = strchr(c, ' ');
-- if (c)
-- *c++ = '_';
-- }
-- len -= csize;
-- str += csize;
-- }
--
-- return tsize;
--}
--
- int of_device_request_module(struct device *dev)
- {
- char *str;
---- /dev/null
-+++ b/drivers/of/module.c
-@@ -0,0 +1,44 @@
-+// SPDX-License-Identifier: GPL-2.0
-+/*
-+ * Linux kernel module helpers.
-+ */
-+
-+#include <linux/of.h>
-+#include <linux/slab.h>
-+#include <linux/string.h>
-+
-+ssize_t of_modalias(const struct device_node *np, char *str, ssize_t len)
-+{
-+ const char *compat;
-+ char *c;
-+ struct property *p;
-+ ssize_t csize;
-+ ssize_t tsize;
-+
-+ /* Name & Type */
-+ /* %p eats all alphanum characters, so %c must be used here */
-+ csize = snprintf(str, len, "of:N%pOFn%c%s", np, 'T',
-+ of_node_get_device_type(np));
-+ tsize = csize;
-+ len -= csize;
-+ if (str)
-+ str += csize;
-+
-+ of_property_for_each_string(np, "compatible", p, compat) {
-+ csize = strlen(compat) + 1;
-+ tsize += csize;
-+ if (csize > len)
-+ continue;
-+
-+ csize = snprintf(str, len, "C%s", compat);
-+ for (c = str; c; ) {
-+ c = strchr(c, ' ');
-+ if (c)
-+ *c++ = '_';
-+ }
-+ len -= csize;
-+ str += csize;
-+ }
-+
-+ return tsize;
-+}
---- a/include/linux/of.h
-+++ b/include/linux/of.h
-@@ -374,6 +374,9 @@ extern int of_parse_phandle_with_args_ma
- extern int of_count_phandle_with_args(const struct device_node *np,
- const char *list_name, const char *cells_name);
-
-+/* module functions */
-+extern ssize_t of_modalias(const struct device_node *np, char *str, ssize_t len);
-+
- /* phandle iterator functions */
- extern int of_phandle_iterator_init(struct of_phandle_iterator *it,
- const struct device_node *np,
-@@ -731,6 +734,12 @@ static inline int of_count_phandle_with_
- return -ENOSYS;
- }
-
-+static inline ssize_t of_modalias(const struct device_node *np, char *str,
-+ ssize_t len)
-+{
-+ return -ENODEV;
-+}
-+
- static inline int of_phandle_iterator_init(struct of_phandle_iterator *it,
- const struct device_node *np,
- const char *list_name,
+++ /dev/null
-From e6506f06d5e82765666902ccf9e9162f3e31d518 Mon Sep 17 00:00:00 2001
-From: Miquel Raynal <miquel.raynal@bootlin.com>
-Date: Tue, 4 Apr 2023 18:21:18 +0100
-Subject: [PATCH] of: Move the request module helper logic to module.c
-
-Depending on device.c for pure OF handling is considered
-backwards. Let's extract the content of of_device_request_module() to
-have the real logic under module.c.
-
-The next step will be to convert users of of_device_request_module() to
-use the new helper.
-
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Reviewed-by: Rob Herring <robh@kernel.org>
-Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-Link: https://lore.kernel.org/r/20230404172148.82422-11-srinivas.kandagatla@linaro.org
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/of/device.c | 25 ++-----------------------
- drivers/of/module.c | 30 ++++++++++++++++++++++++++++++
- include/linux/of.h | 6 ++++++
- 3 files changed, 38 insertions(+), 23 deletions(-)
-
---- a/drivers/of/device.c
-+++ b/drivers/of/device.c
-@@ -8,7 +8,6 @@
- #include <linux/dma-direct.h> /* for bus_dma_region */
- #include <linux/dma-map-ops.h>
- #include <linux/init.h>
--#include <linux/module.h>
- #include <linux/mod_devicetable.h>
- #include <linux/slab.h>
- #include <linux/platform_device.h>
-@@ -249,30 +248,10 @@ EXPORT_SYMBOL(of_device_get_match_data);
-
- int of_device_request_module(struct device *dev)
- {
-- char *str;
-- ssize_t size;
-- int ret;
--
-- if (!dev || !dev->of_node)
-+ if (!dev)
- return -ENODEV;
-
-- size = of_modalias(dev->of_node, NULL, 0);
-- if (size < 0)
-- return size;
--
-- /* Reserve an additional byte for the trailing '\0' */
-- size++;
--
-- str = kmalloc(size, GFP_KERNEL);
-- if (!str)
-- return -ENOMEM;
--
-- of_modalias(dev->of_node, str, size);
-- str[size - 1] = '\0';
-- ret = request_module(str);
-- kfree(str);
--
-- return ret;
-+ return of_request_module(dev->of_node);
- }
- EXPORT_SYMBOL_GPL(of_device_request_module);
-
---- a/drivers/of/module.c
-+++ b/drivers/of/module.c
-@@ -4,6 +4,7 @@
- */
-
- #include <linux/of.h>
-+#include <linux/module.h>
- #include <linux/slab.h>
- #include <linux/string.h>
-
-@@ -42,3 +43,32 @@ ssize_t of_modalias(const struct device_
-
- return tsize;
- }
-+
-+int of_request_module(const struct device_node *np)
-+{
-+ char *str;
-+ ssize_t size;
-+ int ret;
-+
-+ if (!np)
-+ return -ENODEV;
-+
-+ size = of_modalias(np, NULL, 0);
-+ if (size < 0)
-+ return size;
-+
-+ /* Reserve an additional byte for the trailing '\0' */
-+ size++;
-+
-+ str = kmalloc(size, GFP_KERNEL);
-+ if (!str)
-+ return -ENOMEM;
-+
-+ of_modalias(np, str, size);
-+ str[size - 1] = '\0';
-+ ret = request_module(str);
-+ kfree(str);
-+
-+ return ret;
-+}
-+EXPORT_SYMBOL_GPL(of_request_module);
---- a/include/linux/of.h
-+++ b/include/linux/of.h
-@@ -376,6 +376,7 @@ extern int of_count_phandle_with_args(co
-
- /* module functions */
- extern ssize_t of_modalias(const struct device_node *np, char *str, ssize_t len);
-+extern int of_request_module(const struct device_node *np);
-
- /* phandle iterator functions */
- extern int of_phandle_iterator_init(struct of_phandle_iterator *it,
-@@ -739,6 +740,11 @@ static inline ssize_t of_modalias(const
- {
- return -ENODEV;
- }
-+
-+static inline int of_request_module(const struct device_node *np)
-+{
-+ return -ENODEV;
-+}
-
- static inline int of_phandle_iterator_init(struct of_phandle_iterator *it,
- const struct device_node *np,
+++ /dev/null
-From 77faa07c185c969e742cbb3e6aa487a11b0b616c Mon Sep 17 00:00:00 2001
-From: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
-Date: Tue, 30 Aug 2022 09:57:42 +0300
-Subject: [PATCH] dt-bindings: arm: qcom: document qcom,msm-id and
- qcom,board-id
-
-The top level qcom,msm-id and qcom,board-id properties are utilized by
-bootloaders on Qualcomm MSM platforms to determine which device tree
-should be used and passed to the kernel.
-
-The commit b32e592d3c28 ("devicetree: bindings: Document qcom board
-compatible format") from 2015 was a consensus during discussion about
-upstreaming qcom,msm-id and qcom,board-id fields. There are however still
-problems with that consensus:
-1. It was reached 7 years ago but it turned out its implementation did
- not reach all possible products.
-
-2. Initially additional tool (dtbTool) was needed for parsing these
- fields to create a QCDT image consisting of multiple DTBs, later the
- bootloaders were improved and they use these qcom,msm-id and
- qcom,board-id properties directly.
-
-3. Extracting relevant information from the board compatible requires
- this additional tool (dtbTool), which makes the build process more
- complicated and not easily reproducible (DTBs are modified after the
- kernel build).
-
-4. Some versions of Qualcomm bootloaders expect these properties even
- when booting with a single DTB. The community is stuck with these
- bootloaders thus they require properties in the DTBs.
-
-Since several upstreamed Qualcomm SoC-based boards require these
-properties to properly boot and the properties are reportedly used by
-bootloaders, document them along with the bindings header with constants
-used by: bootloader, some DTS and socinfo driver.
-
-Link: https://lore.kernel.org/r/a3c932d1-a102-ce18-deea-18cbbd05ecab@linaro.org/
-Co-developed-by: Kumar Gala <galak@codeaurora.org>
-Signed-off-by: Kumar Gala <galak@codeaurora.org>
-Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
-Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
-Reviewed-by: Rob Herring <robh@kernel.org>
-Signed-off-by: Bjorn Andersson <andersson@kernel.org>
-Link: https://lore.kernel.org/r/20220830065744.161163-2-krzysztof.kozlowski@linaro.org
----
- include/dt-bindings/arm/qcom,ids.h | 155 +++++++++++++++++++++++++++++
- 1 file changed, 155 insertions(+)
- create mode 100644 include/dt-bindings/arm/qcom,ids.h
-
---- /dev/null
-+++ b/include/dt-bindings/arm/qcom,ids.h
-@@ -0,0 +1,155 @@
-+/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */
-+/*
-+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
-+ * Copyright (c) 2022 Linaro Ltd
-+ * Author: Krzysztof Kozlowski <krzk@kernel.org> based on previous work of Kumar Gala.
-+ */
-+#ifndef _DT_BINDINGS_ARM_QCOM_IDS_H
-+#define _DT_BINDINGS_ARM_QCOM_IDS_H
-+
-+/*
-+ * The MSM chipset and hardware revision used by Qualcomm bootloaders, DTS for
-+ * older chipsets (qcom,msm-id) and in socinfo driver:
-+ */
-+#define QCOM_ID_MSM8960 87
-+#define QCOM_ID_APQ8064 109
-+#define QCOM_ID_MSM8660A 122
-+#define QCOM_ID_MSM8260A 123
-+#define QCOM_ID_APQ8060A 124
-+#define QCOM_ID_MSM8974 126
-+#define QCOM_ID_MPQ8064 130
-+#define QCOM_ID_MSM8960AB 138
-+#define QCOM_ID_APQ8060AB 139
-+#define QCOM_ID_MSM8260AB 140
-+#define QCOM_ID_MSM8660AB 141
-+#define QCOM_ID_MSM8626 145
-+#define QCOM_ID_MSM8610 147
-+#define QCOM_ID_APQ8064AB 153
-+#define QCOM_ID_MSM8226 158
-+#define QCOM_ID_MSM8526 159
-+#define QCOM_ID_MSM8110 161
-+#define QCOM_ID_MSM8210 162
-+#define QCOM_ID_MSM8810 163
-+#define QCOM_ID_MSM8212 164
-+#define QCOM_ID_MSM8612 165
-+#define QCOM_ID_MSM8112 166
-+#define QCOM_ID_MSM8225Q 168
-+#define QCOM_ID_MSM8625Q 169
-+#define QCOM_ID_MSM8125Q 170
-+#define QCOM_ID_APQ8064AA 172
-+#define QCOM_ID_APQ8084 178
-+#define QCOM_ID_APQ8074 184
-+#define QCOM_ID_MSM8274 185
-+#define QCOM_ID_MSM8674 186
-+#define QCOM_ID_MSM8974PRO_AC 194
-+#define QCOM_ID_MSM8126 198
-+#define QCOM_ID_APQ8026 199
-+#define QCOM_ID_MSM8926 200
-+#define QCOM_ID_MSM8326 205
-+#define QCOM_ID_MSM8916 206
-+#define QCOM_ID_MSM8994 207
-+#define QCOM_ID_APQ8074PRO_AA 208
-+#define QCOM_ID_APQ8074PRO_AB 209
-+#define QCOM_ID_APQ8074PRO_AC 210
-+#define QCOM_ID_MSM8274PRO_AA 211
-+#define QCOM_ID_MSM8274PRO_AB 212
-+#define QCOM_ID_MSM8274PRO_AC 213
-+#define QCOM_ID_MSM8674PRO_AA 214
-+#define QCOM_ID_MSM8674PRO_AB 215
-+#define QCOM_ID_MSM8674PRO_AC 216
-+#define QCOM_ID_MSM8974PRO_AA 217
-+#define QCOM_ID_MSM8974PRO_AB 218
-+#define QCOM_ID_APQ8028 219
-+#define QCOM_ID_MSM8128 220
-+#define QCOM_ID_MSM8228 221
-+#define QCOM_ID_MSM8528 222
-+#define QCOM_ID_MSM8628 223
-+#define QCOM_ID_MSM8928 224
-+#define QCOM_ID_MSM8510 225
-+#define QCOM_ID_MSM8512 226
-+#define QCOM_ID_MSM8936 233
-+#define QCOM_ID_MSM8939 239
-+#define QCOM_ID_APQ8036 240
-+#define QCOM_ID_APQ8039 241
-+#define QCOM_ID_MSM8996 246
-+#define QCOM_ID_APQ8016 247
-+#define QCOM_ID_MSM8216 248
-+#define QCOM_ID_MSM8116 249
-+#define QCOM_ID_MSM8616 250
-+#define QCOM_ID_MSM8992 251
-+#define QCOM_ID_APQ8094 253
-+#define QCOM_ID_MDM9607 290
-+#define QCOM_ID_APQ8096 291
-+#define QCOM_ID_MSM8998 292
-+#define QCOM_ID_MSM8953 293
-+#define QCOM_ID_MDM8207 296
-+#define QCOM_ID_MDM9207 297
-+#define QCOM_ID_MDM9307 298
-+#define QCOM_ID_MDM9628 299
-+#define QCOM_ID_APQ8053 304
-+#define QCOM_ID_MSM8996SG 305
-+#define QCOM_ID_MSM8996AU 310
-+#define QCOM_ID_APQ8096AU 311
-+#define QCOM_ID_APQ8096SG 312
-+#define QCOM_ID_SDM660 317
-+#define QCOM_ID_SDM630 318
-+#define QCOM_ID_APQ8098 319
-+#define QCOM_ID_SDM845 321
-+#define QCOM_ID_MDM9206 322
-+#define QCOM_ID_IPQ8074 323
-+#define QCOM_ID_SDA660 324
-+#define QCOM_ID_SDM658 325
-+#define QCOM_ID_SDA658 326
-+#define QCOM_ID_SDA630 327
-+#define QCOM_ID_SDM450 338
-+#define QCOM_ID_SDA845 341
-+#define QCOM_ID_IPQ8072 342
-+#define QCOM_ID_IPQ8076 343
-+#define QCOM_ID_IPQ8078 344
-+#define QCOM_ID_SDM636 345
-+#define QCOM_ID_SDA636 346
-+#define QCOM_ID_SDM632 349
-+#define QCOM_ID_SDA632 350
-+#define QCOM_ID_SDA450 351
-+#define QCOM_ID_SM8250 356
-+#define QCOM_ID_IPQ8070 375
-+#define QCOM_ID_IPQ8071 376
-+#define QCOM_ID_IPQ8072A 389
-+#define QCOM_ID_IPQ8074A 390
-+#define QCOM_ID_IPQ8076A 391
-+#define QCOM_ID_IPQ8078A 392
-+#define QCOM_ID_SM6125 394
-+#define QCOM_ID_IPQ8070A 395
-+#define QCOM_ID_IPQ8071A 396
-+#define QCOM_ID_IPQ6018 402
-+#define QCOM_ID_IPQ6028 403
-+#define QCOM_ID_IPQ6000 421
-+#define QCOM_ID_IPQ6010 422
-+#define QCOM_ID_SC7180 425
-+#define QCOM_ID_SM6350 434
-+#define QCOM_ID_SM8350 439
-+#define QCOM_ID_SC8280XP 449
-+#define QCOM_ID_IPQ6005 453
-+#define QCOM_ID_QRB5165 455
-+#define QCOM_ID_SM8450 457
-+#define QCOM_ID_SM7225 459
-+#define QCOM_ID_SA8295P 460
-+#define QCOM_ID_SA8540P 461
-+#define QCOM_ID_SM8450_2 480
-+#define QCOM_ID_SM8450_3 482
-+#define QCOM_ID_SC7280 487
-+#define QCOM_ID_SC7180P 495
-+#define QCOM_ID_SM6375 507
-+
-+/*
-+ * The board type and revision information, used by Qualcomm bootloaders and
-+ * DTS for older chipsets (qcom,board-id):
-+ */
-+#define QCOM_BOARD_ID(a, major, minor) \
-+ (((major & 0xff) << 16) | ((minor & 0xff) << 8) | QCOM_BOARD_ID_##a)
-+
-+#define QCOM_BOARD_ID_MTP 8
-+#define QCOM_BOARD_ID_DRAGONBOARD 10
-+#define QCOM_BOARD_ID_SBC 24
-+
-+#endif /* _DT_BINDINGS_ARM_QCOM_IDS_H */
+++ /dev/null
-From 7cbff3c3f867ff3b24de674f44ca03f54e416a37 Mon Sep 17 00:00:00 2001
-From: Robert Marko <robimarko@gmail.com>
-Date: Sat, 31 Dec 2022 00:27:42 +0100
-Subject: [PATCH] soc: qcom: socinfo: move SMEM item struct and defines to a
- header
-
-Move SMEM item struct and related defines to a header in order to be able
-to reuse them in the Qualcomm NVMEM CPUFreq driver instead of duplicating
-them.
-
-Signed-off-by: Robert Marko <robimarko@gmail.com>
-Reviewed-by: Konrad Dybcio <konrad.dybcio@linaro.org>
-Signed-off-by: Bjorn Andersson <andersson@kernel.org>
-Link: https://lore.kernel.org/r/20230526204802.3081168-1-robimarko@gmail.com
----
- drivers/soc/qcom/socinfo.c | 58 +--------------------------
- include/linux/soc/qcom/socinfo.h | 67 ++++++++++++++++++++++++++++++++
- 2 files changed, 68 insertions(+), 57 deletions(-)
- create mode 100644 include/linux/soc/qcom/socinfo.h
-
---- a/drivers/soc/qcom/socinfo.c
-+++ b/drivers/soc/qcom/socinfo.c
-@@ -11,6 +11,7 @@
- #include <linux/random.h>
- #include <linux/slab.h>
- #include <linux/soc/qcom/smem.h>
-+#include <linux/soc/qcom/socinfo.h>
- #include <linux/string.h>
- #include <linux/sys_soc.h>
- #include <linux/types.h>
-@@ -25,15 +26,6 @@
- #define SOCINFO_MINOR(ver) ((ver) & 0xffff)
- #define SOCINFO_VERSION(maj, min) ((((maj) & 0xffff) << 16)|((min) & 0xffff))
-
--#define SMEM_SOCINFO_BUILD_ID_LENGTH 32
--#define SMEM_SOCINFO_CHIP_ID_LENGTH 32
--
--/*
-- * SMEM item id, used to acquire handles to respective
-- * SMEM region.
-- */
--#define SMEM_HW_SW_BUILD_ID 137
--
- #ifdef CONFIG_DEBUG_FS
- #define SMEM_IMAGE_VERSION_BLOCKS_COUNT 32
- #define SMEM_IMAGE_VERSION_SIZE 4096
-@@ -116,54 +108,6 @@ static const char *const pmic_models[] =
- };
- #endif /* CONFIG_DEBUG_FS */
-
--/* Socinfo SMEM item structure */
--struct socinfo {
-- __le32 fmt;
-- __le32 id;
-- __le32 ver;
-- char build_id[SMEM_SOCINFO_BUILD_ID_LENGTH];
-- /* Version 2 */
-- __le32 raw_id;
-- __le32 raw_ver;
-- /* Version 3 */
-- __le32 hw_plat;
-- /* Version 4 */
-- __le32 plat_ver;
-- /* Version 5 */
-- __le32 accessory_chip;
-- /* Version 6 */
-- __le32 hw_plat_subtype;
-- /* Version 7 */
-- __le32 pmic_model;
-- __le32 pmic_die_rev;
-- /* Version 8 */
-- __le32 pmic_model_1;
-- __le32 pmic_die_rev_1;
-- __le32 pmic_model_2;
-- __le32 pmic_die_rev_2;
-- /* Version 9 */
-- __le32 foundry_id;
-- /* Version 10 */
-- __le32 serial_num;
-- /* Version 11 */
-- __le32 num_pmics;
-- __le32 pmic_array_offset;
-- /* Version 12 */
-- __le32 chip_family;
-- __le32 raw_device_family;
-- __le32 raw_device_num;
-- /* Version 13 */
-- __le32 nproduct_id;
-- char chip_id[SMEM_SOCINFO_CHIP_ID_LENGTH];
-- /* Version 14 */
-- __le32 num_clusters;
-- __le32 ncluster_array_offset;
-- __le32 num_defective_parts;
-- __le32 ndefective_parts_array_offset;
-- /* Version 15 */
-- __le32 nmodem_supported;
--};
--
- #ifdef CONFIG_DEBUG_FS
- struct socinfo_params {
- u32 raw_device_family;
---- /dev/null
-+++ b/include/linux/soc/qcom/socinfo.h
-@@ -0,0 +1,67 @@
-+// SPDX-License-Identifier: GPL-2.0
-+/*
-+ * Copyright (c) 2009-2017, The Linux Foundation. All rights reserved.
-+ * Copyright (c) 2017-2019, Linaro Ltd.
-+ */
-+
-+#ifndef __QCOM_SOCINFO_H__
-+#define __QCOM_SOCINFO_H__
-+
-+/*
-+ * SMEM item id, used to acquire handles to respective
-+ * SMEM region.
-+ */
-+#define SMEM_HW_SW_BUILD_ID 137
-+
-+#define SMEM_SOCINFO_BUILD_ID_LENGTH 32
-+#define SMEM_SOCINFO_CHIP_ID_LENGTH 32
-+
-+/* Socinfo SMEM item structure */
-+struct socinfo {
-+ __le32 fmt;
-+ __le32 id;
-+ __le32 ver;
-+ char build_id[SMEM_SOCINFO_BUILD_ID_LENGTH];
-+ /* Version 2 */
-+ __le32 raw_id;
-+ __le32 raw_ver;
-+ /* Version 3 */
-+ __le32 hw_plat;
-+ /* Version 4 */
-+ __le32 plat_ver;
-+ /* Version 5 */
-+ __le32 accessory_chip;
-+ /* Version 6 */
-+ __le32 hw_plat_subtype;
-+ /* Version 7 */
-+ __le32 pmic_model;
-+ __le32 pmic_die_rev;
-+ /* Version 8 */
-+ __le32 pmic_model_1;
-+ __le32 pmic_die_rev_1;
-+ __le32 pmic_model_2;
-+ __le32 pmic_die_rev_2;
-+ /* Version 9 */
-+ __le32 foundry_id;
-+ /* Version 10 */
-+ __le32 serial_num;
-+ /* Version 11 */
-+ __le32 num_pmics;
-+ __le32 pmic_array_offset;
-+ /* Version 12 */
-+ __le32 chip_family;
-+ __le32 raw_device_family;
-+ __le32 raw_device_num;
-+ /* Version 13 */
-+ __le32 nproduct_id;
-+ char chip_id[SMEM_SOCINFO_CHIP_ID_LENGTH];
-+ /* Version 14 */
-+ __le32 num_clusters;
-+ __le32 ncluster_array_offset;
-+ __le32 num_defective_parts;
-+ __le32 ndefective_parts_array_offset;
-+ /* Version 15 */
-+ __le32 nmodem_supported;
-+};
-+
-+#endif
+++ /dev/null
-From 9f1bbff157a69db7684f5da2f73b2325c638a90e Mon Sep 17 00:00:00 2001
-From: Robert Marko <robimarko@gmail.com>
-Date: Fri, 26 May 2023 22:47:59 +0200
-Subject: [PATCH] soc: qcom: smem: Switch to EXPORT_SYMBOL_GPL()
-
-SMEM has been GPL licensed from the start, and there is no reason to use
-EXPORT_SYMBOL() so switch to the GPL version.
-
-Signed-off-by: Robert Marko <robimarko@gmail.com>
-Reviewed-by: Konrad Dybcio <konrad.dybcio@linaro.org>
-Reviewed-by: Trilok Soni <quic_tsoni@quicinc.com>
-Signed-off-by: Bjorn Andersson <andersson@kernel.org>
-Link: https://lore.kernel.org/r/20230526204802.3081168-2-robimarko@gmail.com
----
- drivers/soc/qcom/smem.c | 8 ++++----
- 1 file changed, 4 insertions(+), 4 deletions(-)
-
---- a/drivers/soc/qcom/smem.c
-+++ b/drivers/soc/qcom/smem.c
-@@ -500,7 +500,7 @@ int qcom_smem_alloc(unsigned host, unsig
-
- return ret;
- }
--EXPORT_SYMBOL(qcom_smem_alloc);
-+EXPORT_SYMBOL_GPL(qcom_smem_alloc);
-
- static void *qcom_smem_get_global(struct qcom_smem *smem,
- unsigned item,
-@@ -674,7 +674,7 @@ void *qcom_smem_get(unsigned host, unsig
- return ptr;
-
- }
--EXPORT_SYMBOL(qcom_smem_get);
-+EXPORT_SYMBOL_GPL(qcom_smem_get);
-
- /**
- * qcom_smem_get_free_space() - retrieve amount of free space in a partition
-@@ -719,7 +719,7 @@ int qcom_smem_get_free_space(unsigned ho
-
- return ret;
- }
--EXPORT_SYMBOL(qcom_smem_get_free_space);
-+EXPORT_SYMBOL_GPL(qcom_smem_get_free_space);
-
- static bool addr_in_range(void __iomem *base, size_t size, void *addr)
- {
-@@ -770,7 +770,7 @@ phys_addr_t qcom_smem_virt_to_phys(void
-
- return 0;
- }
--EXPORT_SYMBOL(qcom_smem_virt_to_phys);
-+EXPORT_SYMBOL_GPL(qcom_smem_virt_to_phys);
-
- static int qcom_smem_get_sbl_version(struct qcom_smem *smem)
- {
+++ /dev/null
-From c3ecf2602a32d9b9e5fc997076c0d2836495c085 Mon Sep 17 00:00:00 2001
-From: Robert Marko <robimarko@gmail.com>
-Date: Fri, 26 May 2023 22:48:00 +0200
-Subject: [PATCH] soc: qcom: smem: introduce qcom_smem_get_soc_id()
-
-Introduce a helper to return the SoC SMEM ID, which is used to identify the
-exact SoC model as there may be differences in the same SoC family.
-
-Currently, cpufreq-nvmem does this completely in the driver and there has
-been more interest expresed for other drivers to use this information so
-lets expose a common helper to prevent redoing it in individual drivers
-since this field is present on every SMEM table version.
-
-Signed-off-by: Robert Marko <robimarko@gmail.com>
-Reviewed-by: Konrad Dybcio <konrad.dybcio@linaro.org>
-Signed-off-by: Bjorn Andersson <andersson@kernel.org>
-Link: https://lore.kernel.org/r/20230526204802.3081168-3-robimarko@gmail.com
----
- drivers/soc/qcom/smem.c | 23 +++++++++++++++++++++++
- include/linux/soc/qcom/smem.h | 2 ++
- 2 files changed, 25 insertions(+)
-
---- a/drivers/soc/qcom/smem.c
-+++ b/drivers/soc/qcom/smem.c
-@@ -14,6 +14,7 @@
- #include <linux/sizes.h>
- #include <linux/slab.h>
- #include <linux/soc/qcom/smem.h>
-+#include <linux/soc/qcom/socinfo.h>
-
- /*
- * The Qualcomm shared memory system is a allocate only heap structure that
-@@ -772,6 +773,28 @@ phys_addr_t qcom_smem_virt_to_phys(void
- }
- EXPORT_SYMBOL_GPL(qcom_smem_virt_to_phys);
-
-+/**
-+ * qcom_smem_get_soc_id() - return the SoC ID
-+ * @id: On success, we return the SoC ID here.
-+ *
-+ * Look up SoC ID from HW/SW build ID and return it.
-+ *
-+ * Return: 0 on success, negative errno on failure.
-+ */
-+int qcom_smem_get_soc_id(u32 *id)
-+{
-+ struct socinfo *info;
-+
-+ info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID, NULL);
-+ if (IS_ERR(info))
-+ return PTR_ERR(info);
-+
-+ *id = __le32_to_cpu(info->id);
-+
-+ return 0;
-+}
-+EXPORT_SYMBOL_GPL(qcom_smem_get_soc_id);
-+
- static int qcom_smem_get_sbl_version(struct qcom_smem *smem)
- {
- struct smem_header *header;
---- a/include/linux/soc/qcom/smem.h
-+++ b/include/linux/soc/qcom/smem.h
-@@ -11,4 +11,6 @@ int qcom_smem_get_free_space(unsigned ho
-
- phys_addr_t qcom_smem_virt_to_phys(void *p);
-
-+int qcom_smem_get_soc_id(u32 *id);
-+
- #endif
+++ /dev/null
-From 2b8634d1468ff498cc91b6adf993c27ae6fa079d Mon Sep 17 00:00:00 2001
-From: Robert Marko <robimarko@gmail.com>
-Date: Fri, 26 May 2023 22:48:01 +0200
-Subject: [PATCH] cpufreq: qcom-nvmem: use SoC ID-s from bindings
-
-SMEM SoC ID-s are now stored in DT bindings so lets use those instead of
-defining them in the driver again.
-
-Signed-off-by: Robert Marko <robimarko@gmail.com>
-Reviewed-by: Konrad Dybcio <konrad.dybcio@linaro.org>
-Reviewed-by: Bjorn Andersson <bjorn.andersson@linaro.org>
-Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
-Signed-off-by: Bjorn Andersson <andersson@kernel.org>
-Link: https://lore.kernel.org/r/20230526204802.3081168-4-robimarko@gmail.com
----
- drivers/cpufreq/qcom-cpufreq-nvmem.c | 15 +++++----------
- 1 file changed, 5 insertions(+), 10 deletions(-)
-
---- a/drivers/cpufreq/qcom-cpufreq-nvmem.c
-+++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c
-@@ -31,12 +31,7 @@
-
- #define MSM_ID_SMEM 137
-
--enum _msm_id {
-- MSM8996V3 = 0xF6ul,
-- APQ8096V3 = 0x123ul,
-- MSM8996SG = 0x131ul,
-- APQ8096SG = 0x138ul,
--};
-+#include <dt-bindings/arm/qcom,ids.h>
-
- enum _msm8996_version {
- MSM8996_V3,
-@@ -154,12 +149,12 @@ static enum _msm8996_version qcom_cpufre
- msm_id++;
-
- switch ((enum _msm_id)*msm_id) {
-- case MSM8996V3:
-- case APQ8096V3:
-+ case QCOM_ID_MSM8996:
-+ case QCOM_ID_APQ8096:
- version = MSM8996_V3;
- break;
-- case MSM8996SG:
-- case APQ8096SG:
-+ case QCOM_ID_MSM8996SG:
-+ case QCOM_ID_APQ8096SG:
- version = MSM8996_SG;
- break;
- default:
+++ /dev/null
-From e7992615acacc27baeec310197108143afc77337 Mon Sep 17 00:00:00 2001
-From: Robert Marko <robimarko@gmail.com>
-Date: Fri, 26 May 2023 22:48:02 +0200
-Subject: [PATCH] cpufreq: qcom-nvmem: use helper to get SMEM SoC ID
-
-Now that SMEM exports a helper to get the SMEM SoC ID lets utilize it.
-Currently qcom_cpufreq_get_msm_id() is encoding the returned SMEM SoC ID
-into an enum, however there is no reason to do so and we can just match
-directly on the SMEM SoC ID as returned by qcom_smem_get_soc_id().
-
-Signed-off-by: Robert Marko <robimarko@gmail.com>
-Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
-Reviewed-by: Konrad Dybcio <konrad.dybcio@linaro.org>
-Signed-off-by: Bjorn Andersson <andersson@kernel.org>
-Link: https://lore.kernel.org/r/20230526204802.3081168-5-robimarko@gmail.com
----
- drivers/cpufreq/qcom-cpufreq-nvmem.c | 56 +++++-----------------------
- 1 file changed, 10 insertions(+), 46 deletions(-)
-
---- a/drivers/cpufreq/qcom-cpufreq-nvmem.c
-+++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c
-@@ -29,16 +29,8 @@
- #include <linux/slab.h>
- #include <linux/soc/qcom/smem.h>
-
--#define MSM_ID_SMEM 137
--
- #include <dt-bindings/arm/qcom,ids.h>
-
--enum _msm8996_version {
-- MSM8996_V3,
-- MSM8996_SG,
-- NUM_OF_MSM8996_VERSIONS,
--};
--
- struct qcom_cpufreq_drv;
-
- struct qcom_cpufreq_match_data {
-@@ -135,60 +127,32 @@ static void get_krait_bin_format_b(struc
- dev_dbg(cpu_dev, "PVS version: %d\n", *pvs_ver);
- }
-
--static enum _msm8996_version qcom_cpufreq_get_msm_id(void)
--{
-- size_t len;
-- u32 *msm_id;
-- enum _msm8996_version version;
--
-- msm_id = qcom_smem_get(QCOM_SMEM_HOST_ANY, MSM_ID_SMEM, &len);
-- if (IS_ERR(msm_id))
-- return NUM_OF_MSM8996_VERSIONS;
--
-- /* The first 4 bytes are format, next to them is the actual msm-id */
-- msm_id++;
--
-- switch ((enum _msm_id)*msm_id) {
-- case QCOM_ID_MSM8996:
-- case QCOM_ID_APQ8096:
-- version = MSM8996_V3;
-- break;
-- case QCOM_ID_MSM8996SG:
-- case QCOM_ID_APQ8096SG:
-- version = MSM8996_SG;
-- break;
-- default:
-- version = NUM_OF_MSM8996_VERSIONS;
-- }
--
-- return version;
--}
--
- static int qcom_cpufreq_kryo_name_version(struct device *cpu_dev,
- struct nvmem_cell *speedbin_nvmem,
- char **pvs_name,
- struct qcom_cpufreq_drv *drv)
- {
- size_t len;
-+ u32 msm_id;
- u8 *speedbin;
-- enum _msm8996_version msm8996_version;
-+ int ret;
- *pvs_name = NULL;
-
-- msm8996_version = qcom_cpufreq_get_msm_id();
-- if (NUM_OF_MSM8996_VERSIONS == msm8996_version) {
-- dev_err(cpu_dev, "Not Snapdragon 820/821!");
-- return -ENODEV;
-- }
-+ ret = qcom_smem_get_soc_id(&msm_id);
-+ if (ret)
-+ return ret;
-
- speedbin = nvmem_cell_read(speedbin_nvmem, &len);
- if (IS_ERR(speedbin))
- return PTR_ERR(speedbin);
-
-- switch (msm8996_version) {
-- case MSM8996_V3:
-+ switch (msm_id) {
-+ case QCOM_ID_MSM8996:
-+ case QCOM_ID_APQ8096:
- drv->versions = 1 << (unsigned int)(*speedbin);
- break;
-- case MSM8996_SG:
-+ case QCOM_ID_MSM8996SG:
-+ case QCOM_ID_APQ8096SG:
- drv->versions = 1 << ((unsigned int)(*speedbin) + 4);
- break;
- default:
+++ /dev/null
-From fe2b1226656afae56702d1d84c6900f6b67df297 Mon Sep 17 00:00:00 2001
-From: Heiner Kallweit <hkallweit1@gmail.com>
-Date: Fri, 1 Dec 2023 11:23:22 +0100
-Subject: [PATCH] leds: trigger: netdev: fix RTNL handling to prevent potential
- deadlock
-
-When working on LED support for r8169 I got the following lockdep
-warning. Easiest way to prevent this scenario seems to be to take
-the RTNL lock before the trigger_data lock in set_device_name().
-
-======================================================
-WARNING: possible circular locking dependency detected
-6.7.0-rc2-next-20231124+ #2 Not tainted
-------------------------------------------------------
-bash/383 is trying to acquire lock:
-ffff888103aa1c68 (&trigger_data->lock){+.+.}-{3:3}, at: netdev_trig_notify+0xec/0x190 [ledtrig_netdev]
-
-but task is already holding lock:
-ffffffff8cddf808 (rtnl_mutex){+.+.}-{3:3}, at: rtnl_lock+0x12/0x20
-
-which lock already depends on the new lock.
-
-the existing dependency chain (in reverse order) is:
-
--> #1 (rtnl_mutex){+.+.}-{3:3}:
- __mutex_lock+0x9b/0xb50
- mutex_lock_nested+0x16/0x20
- rtnl_lock+0x12/0x20
- set_device_name+0xa9/0x120 [ledtrig_netdev]
- netdev_trig_activate+0x1a1/0x230 [ledtrig_netdev]
- led_trigger_set+0x172/0x2c0
- led_trigger_write+0xf1/0x140
- sysfs_kf_bin_write+0x5d/0x80
- kernfs_fop_write_iter+0x15d/0x210
- vfs_write+0x1f0/0x510
- ksys_write+0x6c/0xf0
- __x64_sys_write+0x14/0x20
- do_syscall_64+0x3f/0xf0
- entry_SYSCALL_64_after_hwframe+0x6c/0x74
-
--> #0 (&trigger_data->lock){+.+.}-{3:3}:
- __lock_acquire+0x1459/0x25a0
- lock_acquire+0xc8/0x2d0
- __mutex_lock+0x9b/0xb50
- mutex_lock_nested+0x16/0x20
- netdev_trig_notify+0xec/0x190 [ledtrig_netdev]
- call_netdevice_register_net_notifiers+0x5a/0x100
- register_netdevice_notifier+0x85/0x120
- netdev_trig_activate+0x1d4/0x230 [ledtrig_netdev]
- led_trigger_set+0x172/0x2c0
- led_trigger_write+0xf1/0x140
- sysfs_kf_bin_write+0x5d/0x80
- kernfs_fop_write_iter+0x15d/0x210
- vfs_write+0x1f0/0x510
- ksys_write+0x6c/0xf0
- __x64_sys_write+0x14/0x20
- do_syscall_64+0x3f/0xf0
- entry_SYSCALL_64_after_hwframe+0x6c/0x74
-
-other info that might help us debug this:
-
- Possible unsafe locking scenario:
-
- CPU0 CPU1
- ---- ----
- lock(rtnl_mutex);
- lock(&trigger_data->lock);
- lock(rtnl_mutex);
- lock(&trigger_data->lock);
-
- *** DEADLOCK ***
-
-8 locks held by bash/383:
- #0: ffff888103ff33f0 (sb_writers#3){.+.+}-{0:0}, at: ksys_write+0x6c/0xf0
- #1: ffff888103aa1e88 (&of->mutex){+.+.}-{3:3}, at: kernfs_fop_write_iter+0x114/0x210
- #2: ffff8881036f1890 (kn->active#82){.+.+}-{0:0}, at: kernfs_fop_write_iter+0x11d/0x210
- #3: ffff888108e2c358 (&led_cdev->led_access){+.+.}-{3:3}, at: led_trigger_write+0x30/0x140
- #4: ffffffff8cdd9e10 (triggers_list_lock){++++}-{3:3}, at: led_trigger_write+0x75/0x140
- #5: ffff888108e2c270 (&led_cdev->trigger_lock){++++}-{3:3}, at: led_trigger_write+0xe3/0x140
- #6: ffffffff8cdde3d0 (pernet_ops_rwsem){++++}-{3:3}, at: register_netdevice_notifier+0x1c/0x120
- #7: ffffffff8cddf808 (rtnl_mutex){+.+.}-{3:3}, at: rtnl_lock+0x12/0x20
-
-stack backtrace:
-CPU: 0 PID: 383 Comm: bash Not tainted 6.7.0-rc2-next-20231124+ #2
-Hardware name: Default string Default string/Default string, BIOS ADLN.M6.SODIMM.ZB.CY.015 08/08/2023
-Call Trace:
- <TASK>
- dump_stack_lvl+0x5c/0xd0
- dump_stack+0x10/0x20
- print_circular_bug+0x2dd/0x410
- check_noncircular+0x131/0x150
- __lock_acquire+0x1459/0x25a0
- lock_acquire+0xc8/0x2d0
- ? netdev_trig_notify+0xec/0x190 [ledtrig_netdev]
- __mutex_lock+0x9b/0xb50
- ? netdev_trig_notify+0xec/0x190 [ledtrig_netdev]
- ? __this_cpu_preempt_check+0x13/0x20
- ? netdev_trig_notify+0xec/0x190 [ledtrig_netdev]
- ? __cancel_work_timer+0x11c/0x1b0
- ? __mutex_lock+0x123/0xb50
- mutex_lock_nested+0x16/0x20
- ? mutex_lock_nested+0x16/0x20
- netdev_trig_notify+0xec/0x190 [ledtrig_netdev]
- call_netdevice_register_net_notifiers+0x5a/0x100
- register_netdevice_notifier+0x85/0x120
- netdev_trig_activate+0x1d4/0x230 [ledtrig_netdev]
- led_trigger_set+0x172/0x2c0
- ? preempt_count_add+0x49/0xc0
- led_trigger_write+0xf1/0x140
- sysfs_kf_bin_write+0x5d/0x80
- kernfs_fop_write_iter+0x15d/0x210
- vfs_write+0x1f0/0x510
- ksys_write+0x6c/0xf0
- __x64_sys_write+0x14/0x20
- do_syscall_64+0x3f/0xf0
- entry_SYSCALL_64_after_hwframe+0x6c/0x74
-RIP: 0033:0x7f269055d034
-Code: c7 00 16 00 00 00 b8 ff ff ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 f3 0f 1e fa 80 3d 35 c3 0d 00 00 74 13 b8 01 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 54 c3 0f 1f 00 48 83 ec 28 48 89 54 24 18 48
-RSP: 002b:00007ffddb7ef748 EFLAGS: 00000202 ORIG_RAX: 0000000000000001
-RAX: ffffffffffffffda RBX: 0000000000000007 RCX: 00007f269055d034
-RDX: 0000000000000007 RSI: 000055bf5f4af3c0 RDI: 0000000000000001
-RBP: 000055bf5f4af3c0 R08: 0000000000000073 R09: 0000000000000001
-R10: 0000000000000000 R11: 0000000000000202 R12: 0000000000000007
-R13: 00007f26906325c0 R14: 00007f269062ff20 R15: 0000000000000000
- </TASK>
-
-Fixes: d5e01266e7f5 ("leds: trigger: netdev: add additional specific link speed mode")
-Cc: stable@vger.kernel.org
-Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Acked-by: Lee Jones <lee@kernel.org>
-Link: https://lore.kernel.org/r/fb5c8294-2a10-4bf5-8f10-3d2b77d2757e@gmail.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- drivers/leds/trigger/ledtrig-netdev.c | 11 +++++++----
- 1 file changed, 7 insertions(+), 4 deletions(-)
-
---- a/drivers/leds/trigger/ledtrig-netdev.c
-+++ b/drivers/leds/trigger/ledtrig-netdev.c
-@@ -235,6 +235,11 @@ static int set_device_name(struct led_ne
- {
- cancel_delayed_work_sync(&trigger_data->work);
-
-+ /*
-+ * Take RTNL lock before trigger_data lock to prevent potential
-+ * deadlock with netdev notifier registration.
-+ */
-+ rtnl_lock();
- mutex_lock(&trigger_data->lock);
-
- if (trigger_data->net_dev) {
-@@ -254,16 +259,14 @@ static int set_device_name(struct led_ne
- trigger_data->carrier_link_up = false;
- trigger_data->link_speed = SPEED_UNKNOWN;
- trigger_data->duplex = DUPLEX_UNKNOWN;
-- if (trigger_data->net_dev != NULL) {
-- rtnl_lock();
-+ if (trigger_data->net_dev)
- get_device_state(trigger_data);
-- rtnl_unlock();
-- }
-
- trigger_data->last_activity = 0;
-
- set_baseline_state(trigger_data);
- mutex_unlock(&trigger_data->lock);
-+ rtnl_unlock();
-
- return 0;
- }
+++ /dev/null
-From 9b78d919632b7149d311aaad5a977e4b48b10321 Mon Sep 17 00:00:00 2001
-From: Paolo Abeni <pabeni@redhat.com>
-Date: Wed, 26 Apr 2023 10:15:31 +0200
-Subject: [PATCH] net: phy: hide the PHYLIB_LEDS knob
-
-commit 4bb7aac70b5d ("net: phy: fix circular LEDS_CLASS dependencies")
-solved a build failure, but introduces a new config knob with a default
-'y' value: PHYLIB_LEDS.
-
-The latter is against the current new config policy. The exception
-was raised to allow the user to catch bad configurations without led
-support.
-
-Anyway the current definition of PHYLIB_LEDS does not fit the above
-goal: if LEDS_CLASS is disabled, the new config will be available
-only with PHYLIB disabled, too.
-
-Hide the mentioned config, to preserve the randconfig testing done so
-far, while respecting the mentioned policy.
-
-Suggested-by: Andrew Lunn <andrew@lunn.ch>
-Suggested-by: Arnd Bergmann <arnd@arndb.de>
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
-Link: https://lore.kernel.org/r/d82489be8ed911c383c3447e9abf469995ccf39a.1682496488.git.pabeni@redhat.com
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/phy/Kconfig | 4 +---
- 1 file changed, 1 insertion(+), 3 deletions(-)
-
---- a/drivers/net/phy/Kconfig
-+++ b/drivers/net/phy/Kconfig
-@@ -45,10 +45,8 @@ config LED_TRIGGER_PHY
- for any speed known to the PHY.
-
- config PHYLIB_LEDS
-- bool "Support probing LEDs from device tree"
-+ def_bool OF
- depends on LEDS_CLASS=y || LEDS_CLASS=PHYLIB
-- depends on OF
-- default y
- help
- When LED class support is enabled, phylib can automatically
- probe LED setting from device tree.
+++ /dev/null
-From dbf70fc204d2fbb0d8ad8f42038a60846502efda Mon Sep 17 00:00:00 2001
-From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-Date: Mon, 10 Oct 2022 13:51:09 +0300
-Subject: [PATCH] mtd: spinand: winbond: fix flash identification
-
-Winbond uses 3 bytes to identify flash: vendor_id, dev_id_0, dev_id_1,
-but current driver uses only first 2 bytes of it for devices
-identification. As result Winbond W25N02KV flash (id_bytes: EF, AA, 22)
-is identified as W25N01GV (id_bytes: EF, AA, 21).
-
-Fix this by adding missed identification bytes.
-
-Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-Reviewed-by: Frieder Schrempf <frieder.schrempf@kontron.de>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Link: https://lore.kernel.org/linux-mtd/20221010105110.446674-1-mikhail.kshevetskiy@iopsys.eu
----
- drivers/mtd/nand/spi/winbond.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
---- a/drivers/mtd/nand/spi/winbond.c
-+++ b/drivers/mtd/nand/spi/winbond.c
-@@ -76,7 +76,7 @@ static int w25m02gv_select_target(struct
-
- static const struct spinand_info winbond_spinand_table[] = {
- SPINAND_INFO("W25M02GV",
-- SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab),
-+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab, 0x21),
- NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 2),
- NAND_ECCREQ(1, 512),
- SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-@@ -86,7 +86,7 @@ static const struct spinand_info winbond
- SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
- SPINAND_SELECT_TARGET(w25m02gv_select_target)),
- SPINAND_INFO("W25N01GV",
-- SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa),
-+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x21),
- NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
- NAND_ECCREQ(1, 512),
- SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+++ /dev/null
-From 6154c7a583483d7b69f53bea868efdc369edd563 Mon Sep 17 00:00:00 2001
-From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-Date: Mon, 10 Oct 2022 13:51:10 +0300
-Subject: [PATCH] mtd: spinand: winbond: add Winbond W25N02KV flash support
-
-Add support of Winbond W25N02KV flash
-
-Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-Reviewed-by: Frieder Schrempf <frieder.schrempf@kontron.de>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Link: https://lore.kernel.org/linux-mtd/20221010105110.446674-2-mikhail.kshevetskiy@iopsys.eu
----
- drivers/mtd/nand/spi/winbond.c | 75 ++++++++++++++++++++++++++++++++++
- 1 file changed, 75 insertions(+)
-
---- a/drivers/mtd/nand/spi/winbond.c
-+++ b/drivers/mtd/nand/spi/winbond.c
-@@ -74,6 +74,72 @@ static int w25m02gv_select_target(struct
- return spi_mem_exec_op(spinand->spimem, &op);
- }
-
-+static int w25n02kv_ooblayout_ecc(struct mtd_info *mtd, int section,
-+ struct mtd_oob_region *region)
-+{
-+ if (section > 3)
-+ return -ERANGE;
-+
-+ region->offset = 64 + (16 * section);
-+ region->length = 13;
-+
-+ return 0;
-+}
-+
-+static int w25n02kv_ooblayout_free(struct mtd_info *mtd, int section,
-+ struct mtd_oob_region *region)
-+{
-+ if (section > 3)
-+ return -ERANGE;
-+
-+ region->offset = (16 * section) + 2;
-+ region->length = 14;
-+
-+ return 0;
-+}
-+
-+static const struct mtd_ooblayout_ops w25n02kv_ooblayout = {
-+ .ecc = w25n02kv_ooblayout_ecc,
-+ .free = w25n02kv_ooblayout_free,
-+};
-+
-+static int w25n02kv_ecc_get_status(struct spinand_device *spinand,
-+ u8 status)
-+{
-+ struct nand_device *nand = spinand_to_nand(spinand);
-+ u8 mbf = 0;
-+ struct spi_mem_op op = SPINAND_GET_FEATURE_OP(0x30, &mbf);
-+
-+ switch (status & STATUS_ECC_MASK) {
-+ case STATUS_ECC_NO_BITFLIPS:
-+ return 0;
-+
-+ case STATUS_ECC_UNCOR_ERROR:
-+ return -EBADMSG;
-+
-+ case STATUS_ECC_HAS_BITFLIPS:
-+ /*
-+ * Let's try to retrieve the real maximum number of bitflips
-+ * in order to avoid forcing the wear-leveling layer to move
-+ * data around if it's not necessary.
-+ */
-+ if (spi_mem_exec_op(spinand->spimem, &op))
-+ return nanddev_get_ecc_conf(nand)->strength;
-+
-+ mbf >>= 4;
-+
-+ if (WARN_ON(mbf > nanddev_get_ecc_conf(nand)->strength || !mbf))
-+ return nanddev_get_ecc_conf(nand)->strength;
-+
-+ return mbf;
-+
-+ default:
-+ break;
-+ }
-+
-+ return -EINVAL;
-+}
-+
- static const struct spinand_info winbond_spinand_table[] = {
- SPINAND_INFO("W25M02GV",
- SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab, 0x21),
-@@ -94,6 +160,15 @@ static const struct spinand_info winbond
- &update_cache_variants),
- 0,
- SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)),
-+ SPINAND_INFO("W25N02KV",
-+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x22),
-+ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
-+ NAND_ECCREQ(8, 512),
-+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-+ &write_cache_variants,
-+ &update_cache_variants),
-+ 0,
-+ SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)),
- };
-
- static int winbond_spinand_init(struct spinand_device *spinand)
+++ /dev/null
-From f5a05060670a4d8d6523afc7963eb559c2e3615f Mon Sep 17 00:00:00 2001
-From: Olivier Maignial <olivier.maignial@hotmail.fr>
-Date: Fri, 23 Jun 2023 17:33:37 +0200
-Subject: [PATCH] mtd: spinand: winbond: Fix ecc_get_status
-
-Reading ECC status is failing.
-
-w25n02kv_ecc_get_status() is using on-stack buffer for
-SPINAND_GET_FEATURE_OP() output. It is not suitable for
-DMA needs of spi-mem.
-
-Fix this by using the spi-mem operations dedicated buffer
-spinand->scratchbuf.
-
-See
-spinand->scratchbuf:
-https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/mtd/spinand.h?h=v6.3#n418
-spi_mem_check_op():
-https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/spi/spi-mem.c?h=v6.3#n199
-
-Fixes: 6154c7a58348 ("mtd: spinand: winbond: add Winbond W25N02KV flash support")
-Cc: stable@vger.kernel.org
-Signed-off-by: Olivier Maignial <olivier.maignial@hotmail.fr>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Link: https://lore.kernel.org/linux-mtd/DB4P250MB1032EDB9E36B764A33769039FE23A@DB4P250MB1032.EURP250.PROD.OUTLOOK.COM
----
- drivers/mtd/nand/spi/winbond.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
---- a/drivers/mtd/nand/spi/winbond.c
-+++ b/drivers/mtd/nand/spi/winbond.c
-@@ -108,7 +108,7 @@ static int w25n02kv_ecc_get_status(struc
- {
- struct nand_device *nand = spinand_to_nand(spinand);
- u8 mbf = 0;
-- struct spi_mem_op op = SPINAND_GET_FEATURE_OP(0x30, &mbf);
-+ struct spi_mem_op op = SPINAND_GET_FEATURE_OP(0x30, spinand->scratchbuf);
-
- switch (status & STATUS_ECC_MASK) {
- case STATUS_ECC_NO_BITFLIPS:
-@@ -126,7 +126,7 @@ static int w25n02kv_ecc_get_status(struc
- if (spi_mem_exec_op(spinand->spimem, &op))
- return nanddev_get_ecc_conf(nand)->strength;
-
-- mbf >>= 4;
-+ mbf = *(spinand->scratchbuf) >> 4;
-
- if (WARN_ON(mbf > nanddev_get_ecc_conf(nand)->strength || !mbf))
- return nanddev_get_ecc_conf(nand)->strength;