From: Weijie Gao Date: Wed, 3 Jan 2024 11:57:28 +0000 (+0800) Subject: generic: copy backport, hack, pending patch and config from 6.1 to 6.6 X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=8a9273d51ede02fe66816ad2132198d592b1491e;p=openwrt%2Fstaging%2Fldir.git generic: copy backport, hack, pending patch and config from 6.1 to 6.6 Copy backport, hack, pending patch and config from 6.1 to 6.6. Signed-off-by: Weijie Gao --- diff --git a/target/linux/generic/backport-6.6/020-v6.3-01-UPSTREAM-mm-multi-gen-LRU-rename-lru_gen_struct-to-l.patch b/target/linux/generic/backport-6.6/020-v6.3-01-UPSTREAM-mm-multi-gen-LRU-rename-lru_gen_struct-to-l.patch new file mode 100644 index 0000000000..fe32acc985 --- /dev/null +++ b/target/linux/generic/backport-6.6/020-v6.3-01-UPSTREAM-mm-multi-gen-LRU-rename-lru_gen_struct-to-l.patch @@ -0,0 +1,352 @@ +From 8c20e2eb5f2a0175b774134685e4d7bd93e85ff8 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +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 +Cc: Johannes Weiner +Cc: Jonathan Corbet +Cc: Michael Larabel +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Roman Gushchin +Cc: Suren Baghdasaryan +Signed-off-by: Andrew Morton +Bug: 274865848 +(cherry picked from commit 391655fe08d1f942359a11148aa9aaf3f99d6d6f) +Change-Id: I7df67e0e2435ba28f10eaa57d28d98b61a9210a6 +Signed-off-by: T.J. Mercier +--- + 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); diff --git a/target/linux/generic/backport-6.6/020-v6.3-03-UPSTREAM-mm-multi-gen-LRU-remove-eviction-fairness-s.patch b/target/linux/generic/backport-6.6/020-v6.3-03-UPSTREAM-mm-multi-gen-LRU-remove-eviction-fairness-s.patch new file mode 100644 index 0000000000..e5ad78b61d --- /dev/null +++ b/target/linux/generic/backport-6.6/020-v6.3-03-UPSTREAM-mm-multi-gen-LRU-remove-eviction-fairness-s.patch @@ -0,0 +1,192 @@ +From 14f9a7a15f3d1af351f30e0438fd747b7ac253b0 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +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 +Cc: Johannes Weiner +Cc: Jonathan Corbet +Cc: Michael Larabel +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Roman Gushchin +Cc: Suren Baghdasaryan +Signed-off-by: Andrew Morton +Bug: 274865848 +(cherry picked from commit a579086c99ed70cc4bfc104348dbe3dd8f2787e6) +Change-Id: I08ac1b3c90e29cafd0566785aaa4bcdb5db7d22c +Signed-off-by: T.J. Mercier +--- + 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(); diff --git a/target/linux/generic/backport-6.6/020-v6.3-04-BACKPORT-mm-multi-gen-LRU-remove-aging-fairness-safe.patch b/target/linux/generic/backport-6.6/020-v6.3-04-BACKPORT-mm-multi-gen-LRU-remove-aging-fairness-safe.patch new file mode 100644 index 0000000000..cb349abcdb --- /dev/null +++ b/target/linux/generic/backport-6.6/020-v6.3-04-BACKPORT-mm-multi-gen-LRU-remove-aging-fairness-safe.patch @@ -0,0 +1,294 @@ +From f3c93d2e37a3c56593d7ccf4f4bcf1b58426fdd8 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +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 +Cc: Johannes Weiner +Cc: Jonathan Corbet +Cc: Michael Larabel +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Roman Gushchin +Cc: Suren Baghdasaryan +Signed-off-by: Andrew Morton +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 +--- + 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); diff --git a/target/linux/generic/backport-6.6/020-v6.3-05-UPSTREAM-mm-multi-gen-LRU-shuffle-should_run_aging.patch b/target/linux/generic/backport-6.6/020-v6.3-05-UPSTREAM-mm-multi-gen-LRU-shuffle-should_run_aging.patch new file mode 100644 index 0000000000..42caab7c37 --- /dev/null +++ b/target/linux/generic/backport-6.6/020-v6.3-05-UPSTREAM-mm-multi-gen-LRU-shuffle-should_run_aging.patch @@ -0,0 +1,166 @@ +From eca3858631e0cbad2ca6e40f788892749428e4cb Mon Sep 17 00:00:00 2001 +From: Yu Zhao +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 +Cc: Jonathan Corbet +Cc: Michael Larabel +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Roman Gushchin +Cc: Suren Baghdasaryan +Signed-off-by: Andrew Morton +Bug: 274865848 +(cherry picked from commit 77d4459a4a1a472b7309e475f962dda87d950abd) +Signed-off-by: T.J. Mercier +Change-Id: I3b0383fe16b93a783b4d8c0b3a0b325160392576 +Signed-off-by: Yu Zhao +Signed-off-by: T.J. Mercier +--- + 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 diff --git a/target/linux/generic/backport-6.6/020-v6.3-06-BACKPORT-mm-multi-gen-LRU-per-node-lru_gen_folio-lis.patch b/target/linux/generic/backport-6.6/020-v6.3-06-BACKPORT-mm-multi-gen-LRU-per-node-lru_gen_folio-lis.patch new file mode 100644 index 0000000000..99ec42fe48 --- /dev/null +++ b/target/linux/generic/backport-6.6/020-v6.3-06-BACKPORT-mm-multi-gen-LRU-per-node-lru_gen_folio-lis.patch @@ -0,0 +1,876 @@ +From 8ee8571e47aa75221e5fbd4c9c7802fc4244c346 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +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 +Cc: Johannes Weiner +Cc: Jonathan Corbet +Cc: Michael Larabel +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Roman Gushchin +Cc: Suren Baghdasaryan +Signed-off-by: Andrew Morton +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 +--- + 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 + #include ++#include + #include + #include + #include +@@ -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 + #include + #include ++#include ++#include + + #include + #include +@@ -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: diff --git a/target/linux/generic/backport-6.6/020-v6.3-07-BACKPORT-mm-multi-gen-LRU-clarify-scan_control-flags.patch b/target/linux/generic/backport-6.6/020-v6.3-07-BACKPORT-mm-multi-gen-LRU-clarify-scan_control-flags.patch new file mode 100644 index 0000000000..d60ddb9dcc --- /dev/null +++ b/target/linux/generic/backport-6.6/020-v6.3-07-BACKPORT-mm-multi-gen-LRU-clarify-scan_control-flags.patch @@ -0,0 +1,202 @@ +From 11b14ee8cbbbebd8204609076a9327a1171cd253 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +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 +Cc: Johannes Weiner +Cc: Jonathan Corbet +Cc: Michael Larabel +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Roman Gushchin +Cc: Suren Baghdasaryan +Signed-off-by: Andrew Morton +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 +--- + 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; + } diff --git a/target/linux/generic/backport-6.6/020-v6.3-08-UPSTREAM-mm-multi-gen-LRU-simplify-arch_has_hw_pte_y.patch b/target/linux/generic/backport-6.6/020-v6.3-08-UPSTREAM-mm-multi-gen-LRU-simplify-arch_has_hw_pte_y.patch new file mode 100644 index 0000000000..3cbc84f8b8 --- /dev/null +++ b/target/linux/generic/backport-6.6/020-v6.3-08-UPSTREAM-mm-multi-gen-LRU-simplify-arch_has_hw_pte_y.patch @@ -0,0 +1,38 @@ +From 25887d48dff860751a06caa4188bfaf6bfb6e4b2 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +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 +Cc: Johannes Weiner +Cc: Jonathan Corbet +Cc: Michael Larabel +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Roman Gushchin +Cc: Suren Baghdasaryan +Signed-off-by: Andrew Morton +Bug: 274865848 +(cherry picked from commit f386e9314025ea99dae639ed2032560a92081430) +Change-Id: I84d97ab665b4e3bb862a9bc7d72f50dea7191a6b +Signed-off-by: T.J. Mercier +--- + 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; + } diff --git a/target/linux/generic/backport-6.6/020-v6.3-09-UPSTREAM-mm-multi-gen-LRU-avoid-futile-retries.patch b/target/linux/generic/backport-6.6/020-v6.3-09-UPSTREAM-mm-multi-gen-LRU-avoid-futile-retries.patch new file mode 100644 index 0000000000..c1ad1c538e --- /dev/null +++ b/target/linux/generic/backport-6.6/020-v6.3-09-UPSTREAM-mm-multi-gen-LRU-avoid-futile-retries.patch @@ -0,0 +1,92 @@ +From 620b0ee94455e48d124414cd06d8a53f69fb6453 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +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 +Reported-by: T.J. Mercier +Signed-off-by: Andrew Morton +Bug: 274865848 +(cherry picked from commit 9f550d78b40da21b4da515db4c37d8d7b12aa1a6) +Change-Id: Ie92535676b005ec9e7987632b742fdde8d54436f +Signed-off-by: T.J. Mercier +--- + 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) diff --git a/target/linux/generic/backport-6.6/020-v6.3-10-UPSTREAM-mm-add-vma_has_recency.patch b/target/linux/generic/backport-6.6/020-v6.3-10-UPSTREAM-mm-add-vma_has_recency.patch new file mode 100644 index 0000000000..67fe4f96ec --- /dev/null +++ b/target/linux/generic/backport-6.6/020-v6.3-10-UPSTREAM-mm-add-vma_has_recency.patch @@ -0,0 +1,191 @@ +From 70d216c71ff5c5b17dd1da6294f97b91fb6aba7a Mon Sep 17 00:00:00 2001 +From: Yu Zhao +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 +Cc: Alexander Viro +Cc: Andrea Righi +Cc: Johannes Weiner +Cc: Michael Larabel +Signed-off-by: Andrew Morton +(cherry picked from commit 8788f6781486769d9598dcaedc3fe0eb12fc3e59) +Bug: 274865848 +Signed-off-by: T.J. Mercier +--- + 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)) diff --git a/target/linux/generic/backport-6.6/020-v6.3-11-UPSTREAM-mm-support-POSIX_FADV_NOREUSE.patch b/target/linux/generic/backport-6.6/020-v6.3-11-UPSTREAM-mm-support-POSIX_FADV_NOREUSE.patch new file mode 100644 index 0000000000..f9c39be920 --- /dev/null +++ b/target/linux/generic/backport-6.6/020-v6.3-11-UPSTREAM-mm-support-POSIX_FADV_NOREUSE.patch @@ -0,0 +1,129 @@ +From 9ca4e437a24dfc4ec6c362f319eb9850b9eca497 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +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 < #include + 35d35 + < #include /* _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 +Cc: Alexander Viro +Cc: Andrea Righi +Cc: Johannes Weiner +Cc: Michael Larabel +Signed-off-by: Andrew Morton +(cherry picked from commit 17e810229cb3068b692fa078bd9b3a6527e0866a) +Bug: 274865848 +Signed-off-by: T.J. Mercier +--- + 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, diff --git a/target/linux/generic/backport-6.6/020-v6.3-12-UPSTREAM-mm-multi-gen-LRU-section-for-working-set-pr.patch b/target/linux/generic/backport-6.6/020-v6.3-12-UPSTREAM-mm-multi-gen-LRU-section-for-working-set-pr.patch new file mode 100644 index 0000000000..bac2e3e344 --- /dev/null +++ b/target/linux/generic/backport-6.6/020-v6.3-12-UPSTREAM-mm-multi-gen-LRU-section-for-working-set-pr.patch @@ -0,0 +1,67 @@ +From 1b5e4c317d80f4826eceb3781702d18d06b14394 Mon Sep 17 00:00:00 2001 +From: "T.J. Alumbaugh" +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 +Cc: Yu Zhao +Signed-off-by: Andrew Morton +(cherry picked from commit 7b8144e63d84716f16a1b929e0c7e03ae5c4d5c1) +Bug: 274865848 +Signed-off-by: T.J. Mercier +--- + 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; diff --git a/target/linux/generic/backport-6.6/020-v6.3-13-UPSTREAM-mm-multi-gen-LRU-section-for-rmap-PT-walk-f.patch b/target/linux/generic/backport-6.6/020-v6.3-13-UPSTREAM-mm-multi-gen-LRU-section-for-rmap-PT-walk-f.patch new file mode 100644 index 0000000000..c28a1c5d5b --- /dev/null +++ b/target/linux/generic/backport-6.6/020-v6.3-13-UPSTREAM-mm-multi-gen-LRU-section-for-rmap-PT-walk-f.patch @@ -0,0 +1,57 @@ +From 5ddf9d53d375e42af49b744bd7c2f8247c6bce15 Mon Sep 17 00:00:00 2001 +From: "T.J. Alumbaugh" +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 +Cc: Yu Zhao +Signed-off-by: Andrew Morton +(cherry picked from commit db19a43d9b3a8876552f00f656008206ef9a5efa) +Bug: 274865848 +Signed-off-by: T.J. Mercier +--- + 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 diff --git a/target/linux/generic/backport-6.6/020-v6.3-14-UPSTREAM-mm-multi-gen-LRU-section-for-Bloom-filters.patch b/target/linux/generic/backport-6.6/020-v6.3-14-UPSTREAM-mm-multi-gen-LRU-section-for-Bloom-filters.patch new file mode 100644 index 0000000000..a7dfb5ffe7 --- /dev/null +++ b/target/linux/generic/backport-6.6/020-v6.3-14-UPSTREAM-mm-multi-gen-LRU-section-for-Bloom-filters.patch @@ -0,0 +1,243 @@ +From 397624e12244ec038f51cb1f178ccb7a2ec562e5 Mon Sep 17 00:00:00 2001 +From: "T.J. Alumbaugh" +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 +Cc: Yu Zhao +Signed-off-by: Andrew Morton +(cherry picked from commit ccbbbb85945d8f0255aa9dbc1b617017e2294f2c) +Bug: 274865848 +Signed-off-by: T.J. Mercier +--- + 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; diff --git a/target/linux/generic/backport-6.6/020-v6.3-15-UPSTREAM-mm-multi-gen-LRU-section-for-memcg-LRU.patch b/target/linux/generic/backport-6.6/020-v6.3-15-UPSTREAM-mm-multi-gen-LRU-section-for-memcg-LRU.patch new file mode 100644 index 0000000000..101a0a3757 --- /dev/null +++ b/target/linux/generic/backport-6.6/020-v6.3-15-UPSTREAM-mm-multi-gen-LRU-section-for-memcg-LRU.patch @@ -0,0 +1,427 @@ +From 48c916b812652f9453be5bd45a703728926d41ca Mon Sep 17 00:00:00 2001 +From: "T.J. Alumbaugh" +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 +Cc: Yu Zhao +Signed-off-by: Andrew Morton +(cherry picked from commit 36c7b4db7c942ae9e1b111f0c6b468c8b2e33842) +Bug: 274865848 +Signed-off-by: T.J. Mercier +--- + 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) diff --git a/target/linux/generic/backport-6.6/020-v6.3-16-UPSTREAM-mm-multi-gen-LRU-improve-lru_gen_exit_memcg.patch b/target/linux/generic/backport-6.6/020-v6.3-16-UPSTREAM-mm-multi-gen-LRU-improve-lru_gen_exit_memcg.patch new file mode 100644 index 0000000000..1ee766f861 --- /dev/null +++ b/target/linux/generic/backport-6.6/020-v6.3-16-UPSTREAM-mm-multi-gen-LRU-improve-lru_gen_exit_memcg.patch @@ -0,0 +1,40 @@ +From bec433f29537652ed054148edfd7e2183ddcf7c3 Mon Sep 17 00:00:00 2001 +From: "T.J. Alumbaugh" +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 +Cc: Yu Zhao +Signed-off-by: Andrew Morton +(cherry picked from commit 37cc99979d04cca677c0ad5c0acd1149ec165d1b) +Bug: 274865848 +Signed-off-by: T.J. Mercier +--- + 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; diff --git a/target/linux/generic/backport-6.6/020-v6.3-17-UPSTREAM-mm-multi-gen-LRU-improve-walk_pmd_range.patch b/target/linux/generic/backport-6.6/020-v6.3-17-UPSTREAM-mm-multi-gen-LRU-improve-walk_pmd_range.patch new file mode 100644 index 0000000000..2273977dc9 --- /dev/null +++ b/target/linux/generic/backport-6.6/020-v6.3-17-UPSTREAM-mm-multi-gen-LRU-improve-walk_pmd_range.patch @@ -0,0 +1,135 @@ +From fc0e3b06e0f19917b7ecad7967a72f61d4743644 Mon Sep 17 00:00:00 2001 +From: "T.J. Alumbaugh" +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 +Cc: Yu Zhao +Signed-off-by: Andrew Morton +(cherry picked from commit b5ff4133617d0eced35b685da0bd0929dd9fabb7) +Bug: 274865848 +Signed-off-by: T.J. Mercier +--- + 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; diff --git a/target/linux/generic/backport-6.6/020-v6.3-18-UPSTREAM-mm-multi-gen-LRU-simplify-lru_gen_look_arou.patch b/target/linux/generic/backport-6.6/020-v6.3-18-UPSTREAM-mm-multi-gen-LRU-simplify-lru_gen_look_arou.patch new file mode 100644 index 0000000000..856199574a --- /dev/null +++ b/target/linux/generic/backport-6.6/020-v6.3-18-UPSTREAM-mm-multi-gen-LRU-simplify-lru_gen_look_arou.patch @@ -0,0 +1,148 @@ +From e604c3ccb4dfbdde2467fccef9bb36170a392695 Mon Sep 17 00:00:00 2001 +From: "T.J. Alumbaugh" +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 +Cc: Yu Zhao +Signed-off-by: Andrew Morton +(cherry picked from commit abf086721a2f1e6897c57796f7268df1b194c750) +Bug: 274865848 +Signed-off-by: T.J. Mercier +--- + 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(); + } + + /****************************************************************************** diff --git a/target/linux/generic/backport-6.6/020-v6.4-19-mm-Multi-gen-LRU-remove-wait_event_killable.patch b/target/linux/generic/backport-6.6/020-v6.4-19-mm-Multi-gen-LRU-remove-wait_event_killable.patch new file mode 100644 index 0000000000..1b0459cdb9 --- /dev/null +++ b/target/linux/generic/backport-6.6/020-v6.4-19-mm-Multi-gen-LRU-remove-wait_event_killable.patch @@ -0,0 +1,273 @@ +From 418038c22452df38cde519cc8c662bb15139764a Mon Sep 17 00:00:00 2001 +From: Kalesh Singh +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 +Suggested-by: Minchan Kim +Reported-by: Wei Wang +Acked-by: Yu Zhao +Cc: Minchan Kim +Cc: Jan Alexander Steffens (heftig) +Cc: Oleksandr Natalenko +Cc: Suleiman Souhlal +Cc: Suren Baghdasaryan +Signed-off-by: Andrew Morton +--- + 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))); + diff --git a/target/linux/generic/backport-6.6/200-v6.3-bitfield-add-FIELD_PREP_CONST.patch b/target/linux/generic/backport-6.6/200-v6.3-bitfield-add-FIELD_PREP_CONST.patch new file mode 100644 index 0000000000..2e6d881fe9 --- /dev/null +++ b/target/linux/generic/backport-6.6/200-v6.3-bitfield-add-FIELD_PREP_CONST.patch @@ -0,0 +1,58 @@ +From e2192de59e457aef8d1f055a452131f0b3e5d097 Mon Sep 17 00:00:00 2001 +From: Johannes Berg +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 +Link: https://lore.kernel.org/r/20230118142652.53f20593504b.Iaeea0aee77a6493d70e573b4aa55c91c00e01e4b@changeid +Signed-off-by: Johannes Berg +--- + 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 diff --git a/target/linux/generic/backport-6.6/300-v6.2-powerpc-suppress-some-linker-warnings-in-recent-link.patch b/target/linux/generic/backport-6.6/300-v6.2-powerpc-suppress-some-linker-warnings-in-recent-link.patch new file mode 100644 index 0000000000..d8d0cf9555 --- /dev/null +++ b/target/linux/generic/backport-6.6/300-v6.2-powerpc-suppress-some-linker-warnings-in-recent-link.patch @@ -0,0 +1,63 @@ +From 579aee9fc594af94c242068c011b0233563d4bbf Mon Sep 17 00:00:00 2001 +From: Stephen Rothwell +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 +Signed-off-by: Michael Ellerman +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 diff --git a/target/linux/generic/backport-6.6/406-v6.2-0001-mtd-core-simplify-a-bit-code-find-partition-matching.patch b/target/linux/generic/backport-6.6/406-v6.2-0001-mtd-core-simplify-a-bit-code-find-partition-matching.patch new file mode 100644 index 0000000000..eb6be5ed00 --- /dev/null +++ b/target/linux/generic/backport-6.6/406-v6.2-0001-mtd-core-simplify-a-bit-code-find-partition-matching.patch @@ -0,0 +1,65 @@ +From 63db0cb35e1cb3b3c134906d1062f65513fdda2d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +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 +Signed-off-by: Rafał Miłecki +Signed-off-by: Miquel Raynal +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; + } + diff --git a/target/linux/generic/backport-6.6/406-v6.2-0002-mtd-core-try-to-find-OF-node-for-every-MTD-partition.patch b/target/linux/generic/backport-6.6/406-v6.2-0002-mtd-core-try-to-find-OF-node-for-every-MTD-partition.patch new file mode 100644 index 0000000000..f8d3a370d4 --- /dev/null +++ b/target/linux/generic/backport-6.6/406-v6.2-0002-mtd-core-try-to-find-OF-node-for-every-MTD-partition.patch @@ -0,0 +1,84 @@ +From ddb8cefb7af288950447ca6eeeafb09977dab56f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +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 +Signed-off-by: Rafał Miłecki +Signed-off-by: Miquel Raynal +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); diff --git a/target/linux/generic/backport-6.6/408-v6.2-mtd-core-set-ROOT_DEV-for-partitions-marked-as-rootf.patch b/target/linux/generic/backport-6.6/408-v6.2-mtd-core-set-ROOT_DEV-for-partitions-marked-as-rootf.patch new file mode 100644 index 0000000000..5a5e11c8f7 --- /dev/null +++ b/target/linux/generic/backport-6.6/408-v6.2-mtd-core-set-ROOT_DEV-for-partitions-marked-as-rootf.patch @@ -0,0 +1,47 @@ +From 26422ac78e9d8767bd4aabfbae616b15edbf6a1b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +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 +Signed-off-by: Miquel Raynal +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 + #include + #include ++#include + + #include + #include +@@ -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 diff --git a/target/linux/generic/backport-6.6/421-v6.2-mtd-parsers-add-TP-Link-SafeLoader-partitions-table-.patch b/target/linux/generic/backport-6.6/421-v6.2-mtd-parsers-add-TP-Link-SafeLoader-partitions-table-.patch new file mode 100644 index 0000000000..e570564a0b --- /dev/null +++ b/target/linux/generic/backport-6.6/421-v6.2-mtd-parsers-add-TP-Link-SafeLoader-partitions-table-.patch @@ -0,0 +1,229 @@ +From aec4d5f5ffd0f0092bd9dc21ea90e0bc237d4b74 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +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 +Signed-off-by: Miquel Raynal +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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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"); diff --git a/target/linux/generic/backport-6.6/423-v6.3-mtd-spinand-macronix-use-scratch-buffer-for-DMA-oper.patch b/target/linux/generic/backport-6.6/423-v6.3-mtd-spinand-macronix-use-scratch-buffer-for-DMA-oper.patch new file mode 100644 index 0000000000..7dbc271725 --- /dev/null +++ b/target/linux/generic/backport-6.6/423-v6.3-mtd-spinand-macronix-use-scratch-buffer-for-DMA-oper.patch @@ -0,0 +1,35 @@ +From ebed787a0becb9354f0a23620a5130cccd6c730c Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Reviewed-by: Dhruva Gole +Signed-off-by: Miquel Raynal +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; diff --git a/target/linux/generic/backport-6.6/424-v6.3-mtd-ubi-wire-up-parent-MTD-device.patch b/target/linux/generic/backport-6.6/424-v6.3-mtd-ubi-wire-up-parent-MTD-device.patch new file mode 100644 index 0000000000..657404196d --- /dev/null +++ b/target/linux/generic/backport-6.6/424-v6.3-mtd-ubi-wire-up-parent-MTD-device.patch @@ -0,0 +1,33 @@ +From 1ecf9e390452e73a362ea7fbde8f3f0db83de856 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Signed-off-by: Richard Weinberger +--- + 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; diff --git a/target/linux/generic/backport-6.6/425-v6.3-mtd-ubi-block-wire-up-device-parent.patch b/target/linux/generic/backport-6.6/425-v6.3-mtd-ubi-block-wire-up-device-parent.patch new file mode 100644 index 0000000000..48bf986118 --- /dev/null +++ b/target/linux/generic/backport-6.6/425-v6.3-mtd-ubi-block-wire-up-device-parent.patch @@ -0,0 +1,49 @@ +From 05b8773ca33253ea562be145cf3145b05ef19f86 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Signed-off-by: Richard Weinberger +--- + 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; + }; + + /** diff --git a/target/linux/generic/backport-6.6/426-v6.4-0004-mtd-core-prepare-mtd_otp_nvmem_add-to-handle-EPROBE_.patch b/target/linux/generic/backport-6.6/426-v6.4-0004-mtd-core-prepare-mtd_otp_nvmem_add-to-handle-EPROBE_.patch new file mode 100644 index 0000000000..9ddda420ac --- /dev/null +++ b/target/linux/generic/backport-6.6/426-v6.4-0004-mtd-core-prepare-mtd_otp_nvmem_add-to-handle-EPROBE_.patch @@ -0,0 +1,47 @@ +From 281f7a6c1a33fffcde32001bacbb4f672140fbf9 Mon Sep 17 00:00:00 2001 +From: Michael Walle +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 +Signed-off-by: Miquel Raynal +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"); + } + + /** diff --git a/target/linux/generic/backport-6.6/611-v6.3-net-add-helper-eth_addr_add.patch b/target/linux/generic/backport-6.6/611-v6.3-net-add-helper-eth_addr_add.patch new file mode 100644 index 0000000000..28b7b4383e --- /dev/null +++ b/target/linux/generic/backport-6.6/611-v6.3-net-add-helper-eth_addr_add.patch @@ -0,0 +1,41 @@ +From 7390609b0121a1b982c5ecdfcd72dc328e5784ee Mon Sep 17 00:00:00 2001 +From: Michael Walle +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 +Reviewed-by: Andrew Lunn +Acked-by: Jakub Kicinski +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230206134356.839737-9-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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 diff --git a/target/linux/generic/backport-6.6/701-net-next-net-sfp-add-quirk-for-Fiberstone-GPON-ONU-34-20BI.patch b/target/linux/generic/backport-6.6/701-net-next-net-sfp-add-quirk-for-Fiberstone-GPON-ONU-34-20BI.patch new file mode 100644 index 0000000000..56e14c5c0a --- /dev/null +++ b/target/linux/generic/backport-6.6/701-net-next-net-sfp-add-quirk-for-Fiberstone-GPON-ONU-34-20BI.patch @@ -0,0 +1,32 @@ +From d387e34fec407f881fdf165b5d7ec128ebff362f Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Link: https://lore.kernel.org/r/20230919124720.8210-1-ansuelsmth@gmail.com +Signed-off-by: Paolo Abeni +--- + 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 diff --git a/target/linux/generic/backport-6.6/702-01-v6.7-net-phy-aquantia-move-to-separate-directory.patch b/target/linux/generic/backport-6.6/702-01-v6.7-net-phy-aquantia-move-to-separate-directory.patch new file mode 100644 index 0000000000..0cad6c53d4 --- /dev/null +++ b/target/linux/generic/backport-6.6/702-01-v6.7-net-phy-aquantia-move-to-separate-directory.patch @@ -0,0 +1,2306 @@ +From d2213db3f49bce8e7a87c8de05b9a091f78f654e Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 14 Nov 2023 15:08:41 +0100 +Subject: [PATCH 1/3] net: phy: aquantia: move to separate directory + +Move aquantia PHY driver to separate driectory in preparation for +firmware loading support to keep things tidy. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/Kconfig | 5 +---- + drivers/net/phy/Makefile | 6 +----- + drivers/net/phy/aquantia/Kconfig | 5 +++++ + drivers/net/phy/aquantia/Makefile | 6 ++++++ + drivers/net/phy/{ => aquantia}/aquantia.h | 0 + drivers/net/phy/{ => aquantia}/aquantia_hwmon.c | 0 + drivers/net/phy/{ => aquantia}/aquantia_main.c | 0 + 7 files changed, 13 insertions(+), 9 deletions(-) + create mode 100644 drivers/net/phy/aquantia/Kconfig + create mode 100644 drivers/net/phy/aquantia/Makefile + rename drivers/net/phy/{ => aquantia}/aquantia.h (100%) + rename drivers/net/phy/{ => aquantia}/aquantia_hwmon.c (100%) + rename drivers/net/phy/{ => aquantia}/aquantia_main.c (100%) + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -90,10 +90,7 @@ config ADIN1100_PHY + Currently supports the: + - ADIN1100 - Robust,Industrial, Low Power 10BASE-T1L Ethernet PHY + +-config AQUANTIA_PHY +- tristate "Aquantia PHYs" +- help +- Currently supports the Aquantia AQ1202, AQ2104, AQR105, AQR405 ++source "drivers/net/phy/aquantia/Kconfig" + + config AX88796B_PHY + tristate "Asix PHYs" +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -33,11 +33,7 @@ obj-y += $(sfp-obj-y) $(sfp-obj-m) + obj-$(CONFIG_ADIN_PHY) += adin.o + obj-$(CONFIG_ADIN1100_PHY) += adin1100.o + obj-$(CONFIG_AMD_PHY) += amd.o +-aquantia-objs += aquantia_main.o +-ifdef CONFIG_HWMON +-aquantia-objs += aquantia_hwmon.o +-endif +-obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o ++obj-$(CONFIG_AQUANTIA_PHY) += aquantia/ + obj-$(CONFIG_AT803X_PHY) += at803x.o + obj-$(CONFIG_AX88796B_PHY) += ax88796b.o + obj-$(CONFIG_BCM54140_PHY) += bcm54140.o +--- /dev/null ++++ b/drivers/net/phy/aquantia/Kconfig +@@ -0,0 +1,5 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++config AQUANTIA_PHY ++ tristate "Aquantia PHYs" ++ help ++ Currently supports the Aquantia AQ1202, AQ2104, AQR105, AQR405 +--- /dev/null ++++ b/drivers/net/phy/aquantia/Makefile +@@ -0,0 +1,6 @@ ++# SPDX-License-Identifier: GPL-2.0 ++aquantia-objs += aquantia_main.o ++ifdef CONFIG_HWMON ++aquantia-objs += aquantia_hwmon.o ++endif ++obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o +--- a/drivers/net/phy/aquantia.h ++++ /dev/null +@@ -1,16 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0 */ +-/* HWMON driver for Aquantia PHY +- * +- * Author: Nikita Yushchenko +- * Author: Andrew Lunn +- * Author: Heiner Kallweit +- */ +- +-#include +-#include +- +-#if IS_REACHABLE(CONFIG_HWMON) +-int aqr_hwmon_probe(struct phy_device *phydev); +-#else +-static inline int aqr_hwmon_probe(struct phy_device *phydev) { return 0; } +-#endif +--- /dev/null ++++ b/drivers/net/phy/aquantia/aquantia.h +@@ -0,0 +1,16 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* HWMON driver for Aquantia PHY ++ * ++ * Author: Nikita Yushchenko ++ * Author: Andrew Lunn ++ * Author: Heiner Kallweit ++ */ ++ ++#include ++#include ++ ++#if IS_REACHABLE(CONFIG_HWMON) ++int aqr_hwmon_probe(struct phy_device *phydev); ++#else ++static inline int aqr_hwmon_probe(struct phy_device *phydev) { return 0; } ++#endif +--- /dev/null ++++ b/drivers/net/phy/aquantia/aquantia_hwmon.c +@@ -0,0 +1,250 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* HWMON driver for Aquantia PHY ++ * ++ * Author: Nikita Yushchenko ++ * Author: Andrew Lunn ++ * Author: Heiner Kallweit ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "aquantia.h" ++ ++/* Vendor specific 1, MDIO_MMD_VEND2 */ ++#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL 0xc421 ++#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL 0xc422 ++#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN 0xc423 ++#define VEND1_THERMAL_PROV_LOW_TEMP_WARN 0xc424 ++#define VEND1_THERMAL_STAT1 0xc820 ++#define VEND1_THERMAL_STAT2 0xc821 ++#define VEND1_THERMAL_STAT2_VALID BIT(0) ++#define VEND1_GENERAL_STAT1 0xc830 ++#define VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL BIT(14) ++#define VEND1_GENERAL_STAT1_LOW_TEMP_FAIL BIT(13) ++#define VEND1_GENERAL_STAT1_HIGH_TEMP_WARN BIT(12) ++#define VEND1_GENERAL_STAT1_LOW_TEMP_WARN BIT(11) ++ ++#if IS_REACHABLE(CONFIG_HWMON) ++ ++static umode_t aqr_hwmon_is_visible(const void *data, ++ enum hwmon_sensor_types type, ++ u32 attr, int channel) ++{ ++ if (type != hwmon_temp) ++ return 0; ++ ++ switch (attr) { ++ case hwmon_temp_input: ++ case hwmon_temp_min_alarm: ++ case hwmon_temp_max_alarm: ++ case hwmon_temp_lcrit_alarm: ++ case hwmon_temp_crit_alarm: ++ return 0444; ++ case hwmon_temp_min: ++ case hwmon_temp_max: ++ case hwmon_temp_lcrit: ++ case hwmon_temp_crit: ++ return 0644; ++ default: ++ return 0; ++ } ++} ++ ++static int aqr_hwmon_get(struct phy_device *phydev, int reg, long *value) ++{ ++ int temp = phy_read_mmd(phydev, MDIO_MMD_VEND1, reg); ++ ++ if (temp < 0) ++ return temp; ++ ++ /* 16 bit value is 2's complement with LSB = 1/256th degree Celsius */ ++ *value = (s16)temp * 1000 / 256; ++ ++ return 0; ++} ++ ++static int aqr_hwmon_set(struct phy_device *phydev, int reg, long value) ++{ ++ int temp; ++ ++ if (value >= 128000 || value < -128000) ++ return -ERANGE; ++ ++ temp = value * 256 / 1000; ++ ++ /* temp is in s16 range and we're interested in lower 16 bits only */ ++ return phy_write_mmd(phydev, MDIO_MMD_VEND1, reg, (u16)temp); ++} ++ ++static int aqr_hwmon_test_bit(struct phy_device *phydev, int reg, int bit) ++{ ++ int val = phy_read_mmd(phydev, MDIO_MMD_VEND1, reg); ++ ++ if (val < 0) ++ return val; ++ ++ return !!(val & bit); ++} ++ ++static int aqr_hwmon_status1(struct phy_device *phydev, int bit, long *value) ++{ ++ int val = aqr_hwmon_test_bit(phydev, VEND1_GENERAL_STAT1, bit); ++ ++ if (val < 0) ++ return val; ++ ++ *value = val; ++ ++ return 0; ++} ++ ++static int aqr_hwmon_read(struct device *dev, enum hwmon_sensor_types type, ++ u32 attr, int channel, long *value) ++{ ++ struct phy_device *phydev = dev_get_drvdata(dev); ++ int reg; ++ ++ if (type != hwmon_temp) ++ return -EOPNOTSUPP; ++ ++ switch (attr) { ++ case hwmon_temp_input: ++ reg = aqr_hwmon_test_bit(phydev, VEND1_THERMAL_STAT2, ++ VEND1_THERMAL_STAT2_VALID); ++ if (reg < 0) ++ return reg; ++ if (!reg) ++ return -EBUSY; ++ ++ return aqr_hwmon_get(phydev, VEND1_THERMAL_STAT1, value); ++ ++ case hwmon_temp_lcrit: ++ return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_LOW_TEMP_FAIL, ++ value); ++ case hwmon_temp_min: ++ return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_LOW_TEMP_WARN, ++ value); ++ case hwmon_temp_max: ++ return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_WARN, ++ value); ++ case hwmon_temp_crit: ++ return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_FAIL, ++ value); ++ case hwmon_temp_lcrit_alarm: ++ return aqr_hwmon_status1(phydev, ++ VEND1_GENERAL_STAT1_LOW_TEMP_FAIL, ++ value); ++ case hwmon_temp_min_alarm: ++ return aqr_hwmon_status1(phydev, ++ VEND1_GENERAL_STAT1_LOW_TEMP_WARN, ++ value); ++ case hwmon_temp_max_alarm: ++ return aqr_hwmon_status1(phydev, ++ VEND1_GENERAL_STAT1_HIGH_TEMP_WARN, ++ value); ++ case hwmon_temp_crit_alarm: ++ return aqr_hwmon_status1(phydev, ++ VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL, ++ value); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++static int aqr_hwmon_write(struct device *dev, enum hwmon_sensor_types type, ++ u32 attr, int channel, long value) ++{ ++ struct phy_device *phydev = dev_get_drvdata(dev); ++ ++ if (type != hwmon_temp) ++ return -EOPNOTSUPP; ++ ++ switch (attr) { ++ case hwmon_temp_lcrit: ++ return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_LOW_TEMP_FAIL, ++ value); ++ case hwmon_temp_min: ++ return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_LOW_TEMP_WARN, ++ value); ++ case hwmon_temp_max: ++ return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_WARN, ++ value); ++ case hwmon_temp_crit: ++ return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_FAIL, ++ value); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++static const struct hwmon_ops aqr_hwmon_ops = { ++ .is_visible = aqr_hwmon_is_visible, ++ .read = aqr_hwmon_read, ++ .write = aqr_hwmon_write, ++}; ++ ++static u32 aqr_hwmon_chip_config[] = { ++ HWMON_C_REGISTER_TZ, ++ 0, ++}; ++ ++static const struct hwmon_channel_info aqr_hwmon_chip = { ++ .type = hwmon_chip, ++ .config = aqr_hwmon_chip_config, ++}; ++ ++static u32 aqr_hwmon_temp_config[] = { ++ HWMON_T_INPUT | ++ HWMON_T_MAX | HWMON_T_MIN | ++ HWMON_T_MAX_ALARM | HWMON_T_MIN_ALARM | ++ HWMON_T_CRIT | HWMON_T_LCRIT | ++ HWMON_T_CRIT_ALARM | HWMON_T_LCRIT_ALARM, ++ 0, ++}; ++ ++static const struct hwmon_channel_info aqr_hwmon_temp = { ++ .type = hwmon_temp, ++ .config = aqr_hwmon_temp_config, ++}; ++ ++static const struct hwmon_channel_info *aqr_hwmon_info[] = { ++ &aqr_hwmon_chip, ++ &aqr_hwmon_temp, ++ NULL, ++}; ++ ++static const struct hwmon_chip_info aqr_hwmon_chip_info = { ++ .ops = &aqr_hwmon_ops, ++ .info = aqr_hwmon_info, ++}; ++ ++int aqr_hwmon_probe(struct phy_device *phydev) ++{ ++ struct device *dev = &phydev->mdio.dev; ++ struct device *hwmon_dev; ++ char *hwmon_name; ++ int i, j; ++ ++ hwmon_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); ++ if (!hwmon_name) ++ return -ENOMEM; ++ ++ for (i = j = 0; hwmon_name[i]; i++) { ++ if (isalnum(hwmon_name[i])) { ++ if (i != j) ++ hwmon_name[j] = hwmon_name[i]; ++ j++; ++ } ++ } ++ hwmon_name[j] = '\0'; ++ ++ hwmon_dev = devm_hwmon_device_register_with_info(dev, hwmon_name, ++ phydev, &aqr_hwmon_chip_info, NULL); ++ ++ return PTR_ERR_OR_ZERO(hwmon_dev); ++} ++ ++#endif +--- /dev/null ++++ b/drivers/net/phy/aquantia/aquantia_main.c +@@ -0,0 +1,842 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Driver for Aquantia PHY ++ * ++ * Author: Shaohui Xie ++ * ++ * Copyright 2015 Freescale Semiconductor, Inc. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "aquantia.h" ++ ++#define PHY_ID_AQ1202 0x03a1b445 ++#define PHY_ID_AQ2104 0x03a1b460 ++#define PHY_ID_AQR105 0x03a1b4a2 ++#define PHY_ID_AQR106 0x03a1b4d0 ++#define PHY_ID_AQR107 0x03a1b4e0 ++#define PHY_ID_AQCS109 0x03a1b5c2 ++#define PHY_ID_AQR405 0x03a1b4b0 ++#define PHY_ID_AQR113C 0x31c31c12 ++ ++#define MDIO_PHYXS_VEND_IF_STATUS 0xe812 ++#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK GENMASK(7, 3) ++#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR 0 ++#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_KX 1 ++#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI 2 ++#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII 3 ++#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_XAUI 4 ++#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII 6 ++#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_RXAUI 7 ++#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII 10 ++ ++#define MDIO_AN_VEND_PROV 0xc400 ++#define MDIO_AN_VEND_PROV_1000BASET_FULL BIT(15) ++#define MDIO_AN_VEND_PROV_1000BASET_HALF BIT(14) ++#define MDIO_AN_VEND_PROV_5000BASET_FULL BIT(11) ++#define MDIO_AN_VEND_PROV_2500BASET_FULL BIT(10) ++#define MDIO_AN_VEND_PROV_DOWNSHIFT_EN BIT(4) ++#define MDIO_AN_VEND_PROV_DOWNSHIFT_MASK GENMASK(3, 0) ++#define MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT 4 ++ ++#define MDIO_AN_TX_VEND_STATUS1 0xc800 ++#define MDIO_AN_TX_VEND_STATUS1_RATE_MASK GENMASK(3, 1) ++#define MDIO_AN_TX_VEND_STATUS1_10BASET 0 ++#define MDIO_AN_TX_VEND_STATUS1_100BASETX 1 ++#define MDIO_AN_TX_VEND_STATUS1_1000BASET 2 ++#define MDIO_AN_TX_VEND_STATUS1_10GBASET 3 ++#define MDIO_AN_TX_VEND_STATUS1_2500BASET 4 ++#define MDIO_AN_TX_VEND_STATUS1_5000BASET 5 ++#define MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX BIT(0) ++ ++#define MDIO_AN_TX_VEND_INT_STATUS1 0xcc00 ++#define MDIO_AN_TX_VEND_INT_STATUS1_DOWNSHIFT BIT(1) ++ ++#define MDIO_AN_TX_VEND_INT_STATUS2 0xcc01 ++#define MDIO_AN_TX_VEND_INT_STATUS2_MASK BIT(0) ++ ++#define MDIO_AN_TX_VEND_INT_MASK2 0xd401 ++#define MDIO_AN_TX_VEND_INT_MASK2_LINK BIT(0) ++ ++#define MDIO_AN_RX_LP_STAT1 0xe820 ++#define MDIO_AN_RX_LP_STAT1_1000BASET_FULL BIT(15) ++#define MDIO_AN_RX_LP_STAT1_1000BASET_HALF BIT(14) ++#define MDIO_AN_RX_LP_STAT1_SHORT_REACH BIT(13) ++#define MDIO_AN_RX_LP_STAT1_AQRATE_DOWNSHIFT BIT(12) ++#define MDIO_AN_RX_LP_STAT1_AQ_PHY BIT(2) ++ ++#define MDIO_AN_RX_LP_STAT4 0xe823 ++#define MDIO_AN_RX_LP_STAT4_FW_MAJOR GENMASK(15, 8) ++#define MDIO_AN_RX_LP_STAT4_FW_MINOR GENMASK(7, 0) ++ ++#define MDIO_AN_RX_VEND_STAT3 0xe832 ++#define MDIO_AN_RX_VEND_STAT3_AFR BIT(0) ++ ++/* MDIO_MMD_C22EXT */ ++#define MDIO_C22EXT_STAT_SGMII_RX_GOOD_FRAMES 0xd292 ++#define MDIO_C22EXT_STAT_SGMII_RX_BAD_FRAMES 0xd294 ++#define MDIO_C22EXT_STAT_SGMII_RX_FALSE_CARRIER 0xd297 ++#define MDIO_C22EXT_STAT_SGMII_TX_GOOD_FRAMES 0xd313 ++#define MDIO_C22EXT_STAT_SGMII_TX_BAD_FRAMES 0xd315 ++#define MDIO_C22EXT_STAT_SGMII_TX_FALSE_CARRIER 0xd317 ++#define MDIO_C22EXT_STAT_SGMII_TX_COLLISIONS 0xd318 ++#define MDIO_C22EXT_STAT_SGMII_TX_LINE_COLLISIONS 0xd319 ++#define MDIO_C22EXT_STAT_SGMII_TX_FRAME_ALIGN_ERR 0xd31a ++#define MDIO_C22EXT_STAT_SGMII_TX_RUNT_FRAMES 0xd31b ++ ++/* Vendor specific 1, MDIO_MMD_VEND1 */ ++#define VEND1_GLOBAL_FW_ID 0x0020 ++#define VEND1_GLOBAL_FW_ID_MAJOR GENMASK(15, 8) ++#define VEND1_GLOBAL_FW_ID_MINOR GENMASK(7, 0) ++ ++#define VEND1_GLOBAL_GEN_STAT2 0xc831 ++#define VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG BIT(15) ++ ++/* The following registers all have similar layouts; first the registers... */ ++#define VEND1_GLOBAL_CFG_10M 0x0310 ++#define VEND1_GLOBAL_CFG_100M 0x031b ++#define VEND1_GLOBAL_CFG_1G 0x031c ++#define VEND1_GLOBAL_CFG_2_5G 0x031d ++#define VEND1_GLOBAL_CFG_5G 0x031e ++#define VEND1_GLOBAL_CFG_10G 0x031f ++/* ...and now the fields */ ++#define VEND1_GLOBAL_CFG_RATE_ADAPT GENMASK(8, 7) ++#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE 0 ++#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX 1 ++#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE 2 ++ ++#define VEND1_GLOBAL_RSVD_STAT1 0xc885 ++#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID GENMASK(7, 4) ++#define VEND1_GLOBAL_RSVD_STAT1_PROV_ID GENMASK(3, 0) ++ ++#define VEND1_GLOBAL_RSVD_STAT9 0xc88d ++#define VEND1_GLOBAL_RSVD_STAT9_MODE GENMASK(7, 0) ++#define VEND1_GLOBAL_RSVD_STAT9_1000BT2 0x23 ++ ++#define VEND1_GLOBAL_INT_STD_STATUS 0xfc00 ++#define VEND1_GLOBAL_INT_VEND_STATUS 0xfc01 ++ ++#define VEND1_GLOBAL_INT_STD_MASK 0xff00 ++#define VEND1_GLOBAL_INT_STD_MASK_PMA1 BIT(15) ++#define VEND1_GLOBAL_INT_STD_MASK_PMA2 BIT(14) ++#define VEND1_GLOBAL_INT_STD_MASK_PCS1 BIT(13) ++#define VEND1_GLOBAL_INT_STD_MASK_PCS2 BIT(12) ++#define VEND1_GLOBAL_INT_STD_MASK_PCS3 BIT(11) ++#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1 BIT(10) ++#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2 BIT(9) ++#define VEND1_GLOBAL_INT_STD_MASK_AN1 BIT(8) ++#define VEND1_GLOBAL_INT_STD_MASK_AN2 BIT(7) ++#define VEND1_GLOBAL_INT_STD_MASK_GBE BIT(6) ++#define VEND1_GLOBAL_INT_STD_MASK_ALL BIT(0) ++ ++#define VEND1_GLOBAL_INT_VEND_MASK 0xff01 ++#define VEND1_GLOBAL_INT_VEND_MASK_PMA BIT(15) ++#define VEND1_GLOBAL_INT_VEND_MASK_PCS BIT(14) ++#define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS BIT(13) ++#define VEND1_GLOBAL_INT_VEND_MASK_AN BIT(12) ++#define VEND1_GLOBAL_INT_VEND_MASK_GBE BIT(11) ++#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1 BIT(2) ++#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2 BIT(1) ++#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 BIT(0) ++ ++/* Sleep and timeout for checking if the Processor-Intensive ++ * MDIO operation is finished ++ */ ++#define AQR107_OP_IN_PROG_SLEEP 1000 ++#define AQR107_OP_IN_PROG_TIMEOUT 100000 ++ ++struct aqr107_hw_stat { ++ const char *name; ++ int reg; ++ int size; ++}; ++ ++#define SGMII_STAT(n, r, s) { n, MDIO_C22EXT_STAT_SGMII_ ## r, s } ++static const struct aqr107_hw_stat aqr107_hw_stats[] = { ++ SGMII_STAT("sgmii_rx_good_frames", RX_GOOD_FRAMES, 26), ++ SGMII_STAT("sgmii_rx_bad_frames", RX_BAD_FRAMES, 26), ++ SGMII_STAT("sgmii_rx_false_carrier_events", RX_FALSE_CARRIER, 8), ++ SGMII_STAT("sgmii_tx_good_frames", TX_GOOD_FRAMES, 26), ++ SGMII_STAT("sgmii_tx_bad_frames", TX_BAD_FRAMES, 26), ++ SGMII_STAT("sgmii_tx_false_carrier_events", TX_FALSE_CARRIER, 8), ++ SGMII_STAT("sgmii_tx_collisions", TX_COLLISIONS, 8), ++ SGMII_STAT("sgmii_tx_line_collisions", TX_LINE_COLLISIONS, 8), ++ SGMII_STAT("sgmii_tx_frame_alignment_err", TX_FRAME_ALIGN_ERR, 16), ++ SGMII_STAT("sgmii_tx_runt_frames", TX_RUNT_FRAMES, 22), ++}; ++#define AQR107_SGMII_STAT_SZ ARRAY_SIZE(aqr107_hw_stats) ++ ++struct aqr107_priv { ++ u64 sgmii_stats[AQR107_SGMII_STAT_SZ]; ++}; ++ ++static int aqr107_get_sset_count(struct phy_device *phydev) ++{ ++ return AQR107_SGMII_STAT_SZ; ++} ++ ++static void aqr107_get_strings(struct phy_device *phydev, u8 *data) ++{ ++ int i; ++ ++ for (i = 0; i < AQR107_SGMII_STAT_SZ; i++) ++ strscpy(data + i * ETH_GSTRING_LEN, aqr107_hw_stats[i].name, ++ ETH_GSTRING_LEN); ++} ++ ++static u64 aqr107_get_stat(struct phy_device *phydev, int index) ++{ ++ const struct aqr107_hw_stat *stat = aqr107_hw_stats + index; ++ int len_l = min(stat->size, 16); ++ int len_h = stat->size - len_l; ++ u64 ret; ++ int val; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_C22EXT, stat->reg); ++ if (val < 0) ++ return U64_MAX; ++ ++ ret = val & GENMASK(len_l - 1, 0); ++ if (len_h) { ++ val = phy_read_mmd(phydev, MDIO_MMD_C22EXT, stat->reg + 1); ++ if (val < 0) ++ return U64_MAX; ++ ++ ret += (val & GENMASK(len_h - 1, 0)) << 16; ++ } ++ ++ return ret; ++} ++ ++static void aqr107_get_stats(struct phy_device *phydev, ++ struct ethtool_stats *stats, u64 *data) ++{ ++ struct aqr107_priv *priv = phydev->priv; ++ u64 val; ++ int i; ++ ++ for (i = 0; i < AQR107_SGMII_STAT_SZ; i++) { ++ val = aqr107_get_stat(phydev, i); ++ if (val == U64_MAX) ++ phydev_err(phydev, "Reading HW Statistics failed for %s\n", ++ aqr107_hw_stats[i].name); ++ else ++ priv->sgmii_stats[i] += val; ++ ++ data[i] = priv->sgmii_stats[i]; ++ } ++} ++ ++static int aqr_config_aneg(struct phy_device *phydev) ++{ ++ bool changed = false; ++ u16 reg; ++ int ret; ++ ++ if (phydev->autoneg == AUTONEG_DISABLE) ++ return genphy_c45_pma_setup_forced(phydev); ++ ++ ret = genphy_c45_an_config_aneg(phydev); ++ if (ret < 0) ++ return ret; ++ if (ret > 0) ++ changed = true; ++ ++ /* Clause 45 has no standardized support for 1000BaseT, therefore ++ * use vendor registers for this mode. ++ */ ++ reg = 0; ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, ++ phydev->advertising)) ++ reg |= MDIO_AN_VEND_PROV_1000BASET_FULL; ++ ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, ++ phydev->advertising)) ++ reg |= MDIO_AN_VEND_PROV_1000BASET_HALF; ++ ++ /* Handle the case when the 2.5G and 5G speeds are not advertised */ ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, ++ phydev->advertising)) ++ reg |= MDIO_AN_VEND_PROV_2500BASET_FULL; ++ ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, ++ phydev->advertising)) ++ reg |= MDIO_AN_VEND_PROV_5000BASET_FULL; ++ ++ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV, ++ MDIO_AN_VEND_PROV_1000BASET_HALF | ++ MDIO_AN_VEND_PROV_1000BASET_FULL | ++ MDIO_AN_VEND_PROV_2500BASET_FULL | ++ MDIO_AN_VEND_PROV_5000BASET_FULL, reg); ++ if (ret < 0) ++ return ret; ++ if (ret > 0) ++ changed = true; ++ ++ return genphy_c45_check_and_restart_aneg(phydev, changed); ++} ++ ++static int aqr_config_intr(struct phy_device *phydev) ++{ ++ bool en = phydev->interrupts == PHY_INTERRUPT_ENABLED; ++ int err; ++ ++ if (en) { ++ /* Clear any pending interrupts before enabling them */ ++ err = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_STATUS2); ++ if (err < 0) ++ return err; ++ } ++ ++ err = phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_MASK2, ++ en ? MDIO_AN_TX_VEND_INT_MASK2_LINK : 0); ++ if (err < 0) ++ return err; ++ ++ err = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_INT_STD_MASK, ++ en ? VEND1_GLOBAL_INT_STD_MASK_ALL : 0); ++ if (err < 0) ++ return err; ++ ++ err = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_INT_VEND_MASK, ++ en ? VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 | ++ VEND1_GLOBAL_INT_VEND_MASK_AN : 0); ++ if (err < 0) ++ return err; ++ ++ if (!en) { ++ /* Clear any pending interrupts after we have disabled them */ ++ err = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_STATUS2); ++ if (err < 0) ++ return err; ++ } ++ ++ return 0; ++} ++ ++static irqreturn_t aqr_handle_interrupt(struct phy_device *phydev) ++{ ++ int irq_status; ++ ++ irq_status = phy_read_mmd(phydev, MDIO_MMD_AN, ++ MDIO_AN_TX_VEND_INT_STATUS2); ++ if (irq_status < 0) { ++ phy_error(phydev); ++ return IRQ_NONE; ++ } ++ ++ if (!(irq_status & MDIO_AN_TX_VEND_INT_STATUS2_MASK)) ++ return IRQ_NONE; ++ ++ phy_trigger_machine(phydev); ++ ++ return IRQ_HANDLED; ++} ++ ++static int aqr_read_status(struct phy_device *phydev) ++{ ++ int val; ++ ++ if (phydev->autoneg == AUTONEG_ENABLE) { ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_LP_STAT1); ++ if (val < 0) ++ return val; ++ ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, ++ phydev->lp_advertising, ++ val & MDIO_AN_RX_LP_STAT1_1000BASET_FULL); ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, ++ phydev->lp_advertising, ++ val & MDIO_AN_RX_LP_STAT1_1000BASET_HALF); ++ } ++ ++ return genphy_c45_read_status(phydev); ++} ++ ++static int aqr107_read_rate(struct phy_device *phydev) ++{ ++ u32 config_reg; ++ int val; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_STATUS1); ++ if (val < 0) ++ return val; ++ ++ if (val & MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX) ++ phydev->duplex = DUPLEX_FULL; ++ else ++ phydev->duplex = DUPLEX_HALF; ++ ++ switch (FIELD_GET(MDIO_AN_TX_VEND_STATUS1_RATE_MASK, val)) { ++ case MDIO_AN_TX_VEND_STATUS1_10BASET: ++ phydev->speed = SPEED_10; ++ config_reg = VEND1_GLOBAL_CFG_10M; ++ break; ++ case MDIO_AN_TX_VEND_STATUS1_100BASETX: ++ phydev->speed = SPEED_100; ++ config_reg = VEND1_GLOBAL_CFG_100M; ++ break; ++ case MDIO_AN_TX_VEND_STATUS1_1000BASET: ++ phydev->speed = SPEED_1000; ++ config_reg = VEND1_GLOBAL_CFG_1G; ++ break; ++ case MDIO_AN_TX_VEND_STATUS1_2500BASET: ++ phydev->speed = SPEED_2500; ++ config_reg = VEND1_GLOBAL_CFG_2_5G; ++ break; ++ case MDIO_AN_TX_VEND_STATUS1_5000BASET: ++ phydev->speed = SPEED_5000; ++ config_reg = VEND1_GLOBAL_CFG_5G; ++ break; ++ case MDIO_AN_TX_VEND_STATUS1_10GBASET: ++ phydev->speed = SPEED_10000; ++ config_reg = VEND1_GLOBAL_CFG_10G; ++ break; ++ default: ++ phydev->speed = SPEED_UNKNOWN; ++ return 0; ++ } ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, config_reg); ++ if (val < 0) ++ return val; ++ ++ if (FIELD_GET(VEND1_GLOBAL_CFG_RATE_ADAPT, val) == ++ VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE) ++ phydev->rate_matching = RATE_MATCH_PAUSE; ++ else ++ phydev->rate_matching = RATE_MATCH_NONE; ++ ++ return 0; ++} ++ ++static int aqr107_read_status(struct phy_device *phydev) ++{ ++ int val, ret; ++ ++ ret = aqr_read_status(phydev); ++ if (ret) ++ return ret; ++ ++ if (!phydev->link || phydev->autoneg == AUTONEG_DISABLE) ++ return 0; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_PHYXS, MDIO_PHYXS_VEND_IF_STATUS); ++ if (val < 0) ++ return val; ++ ++ switch (FIELD_GET(MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK, val)) { ++ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR: ++ phydev->interface = PHY_INTERFACE_MODE_10GKR; ++ break; ++ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KX: ++ phydev->interface = PHY_INTERFACE_MODE_1000BASEKX; ++ break; ++ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI: ++ phydev->interface = PHY_INTERFACE_MODE_10GBASER; ++ break; ++ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII: ++ phydev->interface = PHY_INTERFACE_MODE_USXGMII; ++ break; ++ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XAUI: ++ phydev->interface = PHY_INTERFACE_MODE_XAUI; ++ break; ++ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII: ++ phydev->interface = PHY_INTERFACE_MODE_SGMII; ++ break; ++ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_RXAUI: ++ phydev->interface = PHY_INTERFACE_MODE_RXAUI; ++ break; ++ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII: ++ phydev->interface = PHY_INTERFACE_MODE_2500BASEX; ++ break; ++ default: ++ phydev->interface = PHY_INTERFACE_MODE_NA; ++ break; ++ } ++ ++ /* Read possibly downshifted rate from vendor register */ ++ return aqr107_read_rate(phydev); ++} ++ ++static int aqr107_get_downshift(struct phy_device *phydev, u8 *data) ++{ ++ int val, cnt, enable; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV); ++ if (val < 0) ++ return val; ++ ++ enable = FIELD_GET(MDIO_AN_VEND_PROV_DOWNSHIFT_EN, val); ++ cnt = FIELD_GET(MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, val); ++ ++ *data = enable && cnt ? cnt : DOWNSHIFT_DEV_DISABLE; ++ ++ return 0; ++} ++ ++static int aqr107_set_downshift(struct phy_device *phydev, u8 cnt) ++{ ++ int val = 0; ++ ++ if (!FIELD_FIT(MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, cnt)) ++ return -E2BIG; ++ ++ if (cnt != DOWNSHIFT_DEV_DISABLE) { ++ val = MDIO_AN_VEND_PROV_DOWNSHIFT_EN; ++ val |= FIELD_PREP(MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, cnt); ++ } ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV, ++ MDIO_AN_VEND_PROV_DOWNSHIFT_EN | ++ MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, val); ++} ++ ++static int aqr107_get_tunable(struct phy_device *phydev, ++ struct ethtool_tunable *tuna, void *data) ++{ ++ switch (tuna->id) { ++ case ETHTOOL_PHY_DOWNSHIFT: ++ return aqr107_get_downshift(phydev, data); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++static int aqr107_set_tunable(struct phy_device *phydev, ++ struct ethtool_tunable *tuna, const void *data) ++{ ++ switch (tuna->id) { ++ case ETHTOOL_PHY_DOWNSHIFT: ++ return aqr107_set_downshift(phydev, *(const u8 *)data); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++/* If we configure settings whilst firmware is still initializing the chip, ++ * then these settings may be overwritten. Therefore make sure chip ++ * initialization has completed. Use presence of the firmware ID as ++ * indicator for initialization having completed. ++ * The chip also provides a "reset completed" bit, but it's cleared after ++ * read. Therefore function would time out if called again. ++ */ ++static int aqr107_wait_reset_complete(struct phy_device *phydev) ++{ ++ int val; ++ ++ return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, ++ VEND1_GLOBAL_FW_ID, val, val != 0, ++ 20000, 2000000, false); ++} ++ ++static void aqr107_chip_info(struct phy_device *phydev) ++{ ++ u8 fw_major, fw_minor, build_id, prov_id; ++ int val; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_FW_ID); ++ if (val < 0) ++ return; ++ ++ fw_major = FIELD_GET(VEND1_GLOBAL_FW_ID_MAJOR, val); ++ fw_minor = FIELD_GET(VEND1_GLOBAL_FW_ID_MINOR, val); ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_RSVD_STAT1); ++ if (val < 0) ++ return; ++ ++ build_id = FIELD_GET(VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID, val); ++ prov_id = FIELD_GET(VEND1_GLOBAL_RSVD_STAT1_PROV_ID, val); ++ ++ phydev_dbg(phydev, "FW %u.%u, Build %u, Provisioning %u\n", ++ fw_major, fw_minor, build_id, prov_id); ++} ++ ++static int aqr107_config_init(struct phy_device *phydev) ++{ ++ int ret; ++ ++ /* Check that the PHY interface type is compatible */ ++ if (phydev->interface != PHY_INTERFACE_MODE_SGMII && ++ phydev->interface != PHY_INTERFACE_MODE_1000BASEKX && ++ phydev->interface != PHY_INTERFACE_MODE_2500BASEX && ++ phydev->interface != PHY_INTERFACE_MODE_XGMII && ++ phydev->interface != PHY_INTERFACE_MODE_USXGMII && ++ phydev->interface != PHY_INTERFACE_MODE_10GKR && ++ phydev->interface != PHY_INTERFACE_MODE_10GBASER && ++ phydev->interface != PHY_INTERFACE_MODE_XAUI && ++ phydev->interface != PHY_INTERFACE_MODE_RXAUI) ++ return -ENODEV; ++ ++ WARN(phydev->interface == PHY_INTERFACE_MODE_XGMII, ++ "Your devicetree is out of date, please update it. The AQR107 family doesn't support XGMII, maybe you mean USXGMII.\n"); ++ ++ ret = aqr107_wait_reset_complete(phydev); ++ if (!ret) ++ aqr107_chip_info(phydev); ++ ++ return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT); ++} ++ ++static int aqcs109_config_init(struct phy_device *phydev) ++{ ++ int ret; ++ ++ /* Check that the PHY interface type is compatible */ ++ if (phydev->interface != PHY_INTERFACE_MODE_SGMII && ++ phydev->interface != PHY_INTERFACE_MODE_2500BASEX) ++ return -ENODEV; ++ ++ ret = aqr107_wait_reset_complete(phydev); ++ if (!ret) ++ aqr107_chip_info(phydev); ++ ++ /* AQCS109 belongs to a chip family partially supporting 10G and 5G. ++ * PMA speed ability bits are the same for all members of the family, ++ * AQCS109 however supports speeds up to 2.5G only. ++ */ ++ phy_set_max_speed(phydev, SPEED_2500); ++ ++ return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT); ++} ++ ++static void aqr107_link_change_notify(struct phy_device *phydev) ++{ ++ u8 fw_major, fw_minor; ++ bool downshift, short_reach, afr; ++ int mode, val; ++ ++ if (phydev->state != PHY_RUNNING || phydev->autoneg == AUTONEG_DISABLE) ++ return; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_LP_STAT1); ++ /* call failed or link partner is no Aquantia PHY */ ++ if (val < 0 || !(val & MDIO_AN_RX_LP_STAT1_AQ_PHY)) ++ return; ++ ++ short_reach = val & MDIO_AN_RX_LP_STAT1_SHORT_REACH; ++ downshift = val & MDIO_AN_RX_LP_STAT1_AQRATE_DOWNSHIFT; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_LP_STAT4); ++ if (val < 0) ++ return; ++ ++ fw_major = FIELD_GET(MDIO_AN_RX_LP_STAT4_FW_MAJOR, val); ++ fw_minor = FIELD_GET(MDIO_AN_RX_LP_STAT4_FW_MINOR, val); ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_VEND_STAT3); ++ if (val < 0) ++ return; ++ ++ afr = val & MDIO_AN_RX_VEND_STAT3_AFR; ++ ++ phydev_dbg(phydev, "Link partner is Aquantia PHY, FW %u.%u%s%s%s\n", ++ fw_major, fw_minor, ++ short_reach ? ", short reach mode" : "", ++ downshift ? ", fast-retrain downshift advertised" : "", ++ afr ? ", fast reframe advertised" : ""); ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_RSVD_STAT9); ++ if (val < 0) ++ return; ++ ++ mode = FIELD_GET(VEND1_GLOBAL_RSVD_STAT9_MODE, val); ++ if (mode == VEND1_GLOBAL_RSVD_STAT9_1000BT2) ++ phydev_info(phydev, "Aquantia 1000Base-T2 mode active\n"); ++} ++ ++static int aqr107_wait_processor_intensive_op(struct phy_device *phydev) ++{ ++ int val, err; ++ ++ /* The datasheet notes to wait at least 1ms after issuing a ++ * processor intensive operation before checking. ++ * We cannot use the 'sleep_before_read' parameter of read_poll_timeout ++ * because that just determines the maximum time slept, not the minimum. ++ */ ++ usleep_range(1000, 5000); ++ ++ err = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, ++ VEND1_GLOBAL_GEN_STAT2, val, ++ !(val & VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG), ++ AQR107_OP_IN_PROG_SLEEP, ++ AQR107_OP_IN_PROG_TIMEOUT, false); ++ if (err) { ++ phydev_err(phydev, "timeout: processor-intensive MDIO operation\n"); ++ return err; ++ } ++ ++ return 0; ++} ++ ++static int aqr107_get_rate_matching(struct phy_device *phydev, ++ phy_interface_t iface) ++{ ++ if (iface == PHY_INTERFACE_MODE_10GBASER || ++ iface == PHY_INTERFACE_MODE_2500BASEX || ++ iface == PHY_INTERFACE_MODE_NA) ++ return RATE_MATCH_PAUSE; ++ return RATE_MATCH_NONE; ++} ++ ++static int aqr107_suspend(struct phy_device *phydev) ++{ ++ int err; ++ ++ err = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MDIO_CTRL1, ++ MDIO_CTRL1_LPOWER); ++ if (err) ++ return err; ++ ++ return aqr107_wait_processor_intensive_op(phydev); ++} ++ ++static int aqr107_resume(struct phy_device *phydev) ++{ ++ int err; ++ ++ err = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MDIO_CTRL1, ++ MDIO_CTRL1_LPOWER); ++ if (err) ++ return err; ++ ++ return aqr107_wait_processor_intensive_op(phydev); ++} ++ ++static int aqr107_probe(struct phy_device *phydev) ++{ ++ phydev->priv = devm_kzalloc(&phydev->mdio.dev, ++ sizeof(struct aqr107_priv), GFP_KERNEL); ++ if (!phydev->priv) ++ return -ENOMEM; ++ ++ return aqr_hwmon_probe(phydev); ++} ++ ++static struct phy_driver aqr_driver[] = { ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQ1202), ++ .name = "Aquantia AQ1202", ++ .config_aneg = aqr_config_aneg, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr_read_status, ++}, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQ2104), ++ .name = "Aquantia AQ2104", ++ .config_aneg = aqr_config_aneg, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr_read_status, ++}, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR105), ++ .name = "Aquantia AQR105", ++ .config_aneg = aqr_config_aneg, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr_read_status, ++ .suspend = aqr107_suspend, ++ .resume = aqr107_resume, ++}, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR106), ++ .name = "Aquantia AQR106", ++ .config_aneg = aqr_config_aneg, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr_read_status, ++}, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR107), ++ .name = "Aquantia AQR107", ++ .probe = aqr107_probe, ++ .get_rate_matching = aqr107_get_rate_matching, ++ .config_init = aqr107_config_init, ++ .config_aneg = aqr_config_aneg, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr107_read_status, ++ .get_tunable = aqr107_get_tunable, ++ .set_tunable = aqr107_set_tunable, ++ .suspend = aqr107_suspend, ++ .resume = aqr107_resume, ++ .get_sset_count = aqr107_get_sset_count, ++ .get_strings = aqr107_get_strings, ++ .get_stats = aqr107_get_stats, ++ .link_change_notify = aqr107_link_change_notify, ++}, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQCS109), ++ .name = "Aquantia AQCS109", ++ .probe = aqr107_probe, ++ .get_rate_matching = aqr107_get_rate_matching, ++ .config_init = aqcs109_config_init, ++ .config_aneg = aqr_config_aneg, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr107_read_status, ++ .get_tunable = aqr107_get_tunable, ++ .set_tunable = aqr107_set_tunable, ++ .suspend = aqr107_suspend, ++ .resume = aqr107_resume, ++ .get_sset_count = aqr107_get_sset_count, ++ .get_strings = aqr107_get_strings, ++ .get_stats = aqr107_get_stats, ++ .link_change_notify = aqr107_link_change_notify, ++}, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR405), ++ .name = "Aquantia AQR405", ++ .config_aneg = aqr_config_aneg, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr_read_status, ++}, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR113C), ++ .name = "Aquantia AQR113C", ++ .probe = aqr107_probe, ++ .get_rate_matching = aqr107_get_rate_matching, ++ .config_init = aqr107_config_init, ++ .config_aneg = aqr_config_aneg, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr107_read_status, ++ .get_tunable = aqr107_get_tunable, ++ .set_tunable = aqr107_set_tunable, ++ .suspend = aqr107_suspend, ++ .resume = aqr107_resume, ++ .get_sset_count = aqr107_get_sset_count, ++ .get_strings = aqr107_get_strings, ++ .get_stats = aqr107_get_stats, ++ .link_change_notify = aqr107_link_change_notify, ++}, ++}; ++ ++module_phy_driver(aqr_driver); ++ ++static struct mdio_device_id __maybe_unused aqr_tbl[] = { ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQ1202) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQ2104) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR105) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR106) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR107) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) }, ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(mdio, aqr_tbl); ++ ++MODULE_DESCRIPTION("Aquantia PHY driver"); ++MODULE_AUTHOR("Shaohui Xie "); ++MODULE_LICENSE("GPL v2"); +--- a/drivers/net/phy/aquantia_hwmon.c ++++ /dev/null +@@ -1,250 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-/* HWMON driver for Aquantia PHY +- * +- * Author: Nikita Yushchenko +- * Author: Andrew Lunn +- * Author: Heiner Kallweit +- */ +- +-#include +-#include +-#include +-#include +- +-#include "aquantia.h" +- +-/* Vendor specific 1, MDIO_MMD_VEND2 */ +-#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL 0xc421 +-#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL 0xc422 +-#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN 0xc423 +-#define VEND1_THERMAL_PROV_LOW_TEMP_WARN 0xc424 +-#define VEND1_THERMAL_STAT1 0xc820 +-#define VEND1_THERMAL_STAT2 0xc821 +-#define VEND1_THERMAL_STAT2_VALID BIT(0) +-#define VEND1_GENERAL_STAT1 0xc830 +-#define VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL BIT(14) +-#define VEND1_GENERAL_STAT1_LOW_TEMP_FAIL BIT(13) +-#define VEND1_GENERAL_STAT1_HIGH_TEMP_WARN BIT(12) +-#define VEND1_GENERAL_STAT1_LOW_TEMP_WARN BIT(11) +- +-#if IS_REACHABLE(CONFIG_HWMON) +- +-static umode_t aqr_hwmon_is_visible(const void *data, +- enum hwmon_sensor_types type, +- u32 attr, int channel) +-{ +- if (type != hwmon_temp) +- return 0; +- +- switch (attr) { +- case hwmon_temp_input: +- case hwmon_temp_min_alarm: +- case hwmon_temp_max_alarm: +- case hwmon_temp_lcrit_alarm: +- case hwmon_temp_crit_alarm: +- return 0444; +- case hwmon_temp_min: +- case hwmon_temp_max: +- case hwmon_temp_lcrit: +- case hwmon_temp_crit: +- return 0644; +- default: +- return 0; +- } +-} +- +-static int aqr_hwmon_get(struct phy_device *phydev, int reg, long *value) +-{ +- int temp = phy_read_mmd(phydev, MDIO_MMD_VEND1, reg); +- +- if (temp < 0) +- return temp; +- +- /* 16 bit value is 2's complement with LSB = 1/256th degree Celsius */ +- *value = (s16)temp * 1000 / 256; +- +- return 0; +-} +- +-static int aqr_hwmon_set(struct phy_device *phydev, int reg, long value) +-{ +- int temp; +- +- if (value >= 128000 || value < -128000) +- return -ERANGE; +- +- temp = value * 256 / 1000; +- +- /* temp is in s16 range and we're interested in lower 16 bits only */ +- return phy_write_mmd(phydev, MDIO_MMD_VEND1, reg, (u16)temp); +-} +- +-static int aqr_hwmon_test_bit(struct phy_device *phydev, int reg, int bit) +-{ +- int val = phy_read_mmd(phydev, MDIO_MMD_VEND1, reg); +- +- if (val < 0) +- return val; +- +- return !!(val & bit); +-} +- +-static int aqr_hwmon_status1(struct phy_device *phydev, int bit, long *value) +-{ +- int val = aqr_hwmon_test_bit(phydev, VEND1_GENERAL_STAT1, bit); +- +- if (val < 0) +- return val; +- +- *value = val; +- +- return 0; +-} +- +-static int aqr_hwmon_read(struct device *dev, enum hwmon_sensor_types type, +- u32 attr, int channel, long *value) +-{ +- struct phy_device *phydev = dev_get_drvdata(dev); +- int reg; +- +- if (type != hwmon_temp) +- return -EOPNOTSUPP; +- +- switch (attr) { +- case hwmon_temp_input: +- reg = aqr_hwmon_test_bit(phydev, VEND1_THERMAL_STAT2, +- VEND1_THERMAL_STAT2_VALID); +- if (reg < 0) +- return reg; +- if (!reg) +- return -EBUSY; +- +- return aqr_hwmon_get(phydev, VEND1_THERMAL_STAT1, value); +- +- case hwmon_temp_lcrit: +- return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_LOW_TEMP_FAIL, +- value); +- case hwmon_temp_min: +- return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_LOW_TEMP_WARN, +- value); +- case hwmon_temp_max: +- return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_WARN, +- value); +- case hwmon_temp_crit: +- return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_FAIL, +- value); +- case hwmon_temp_lcrit_alarm: +- return aqr_hwmon_status1(phydev, +- VEND1_GENERAL_STAT1_LOW_TEMP_FAIL, +- value); +- case hwmon_temp_min_alarm: +- return aqr_hwmon_status1(phydev, +- VEND1_GENERAL_STAT1_LOW_TEMP_WARN, +- value); +- case hwmon_temp_max_alarm: +- return aqr_hwmon_status1(phydev, +- VEND1_GENERAL_STAT1_HIGH_TEMP_WARN, +- value); +- case hwmon_temp_crit_alarm: +- return aqr_hwmon_status1(phydev, +- VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL, +- value); +- default: +- return -EOPNOTSUPP; +- } +-} +- +-static int aqr_hwmon_write(struct device *dev, enum hwmon_sensor_types type, +- u32 attr, int channel, long value) +-{ +- struct phy_device *phydev = dev_get_drvdata(dev); +- +- if (type != hwmon_temp) +- return -EOPNOTSUPP; +- +- switch (attr) { +- case hwmon_temp_lcrit: +- return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_LOW_TEMP_FAIL, +- value); +- case hwmon_temp_min: +- return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_LOW_TEMP_WARN, +- value); +- case hwmon_temp_max: +- return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_WARN, +- value); +- case hwmon_temp_crit: +- return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_FAIL, +- value); +- default: +- return -EOPNOTSUPP; +- } +-} +- +-static const struct hwmon_ops aqr_hwmon_ops = { +- .is_visible = aqr_hwmon_is_visible, +- .read = aqr_hwmon_read, +- .write = aqr_hwmon_write, +-}; +- +-static u32 aqr_hwmon_chip_config[] = { +- HWMON_C_REGISTER_TZ, +- 0, +-}; +- +-static const struct hwmon_channel_info aqr_hwmon_chip = { +- .type = hwmon_chip, +- .config = aqr_hwmon_chip_config, +-}; +- +-static u32 aqr_hwmon_temp_config[] = { +- HWMON_T_INPUT | +- HWMON_T_MAX | HWMON_T_MIN | +- HWMON_T_MAX_ALARM | HWMON_T_MIN_ALARM | +- HWMON_T_CRIT | HWMON_T_LCRIT | +- HWMON_T_CRIT_ALARM | HWMON_T_LCRIT_ALARM, +- 0, +-}; +- +-static const struct hwmon_channel_info aqr_hwmon_temp = { +- .type = hwmon_temp, +- .config = aqr_hwmon_temp_config, +-}; +- +-static const struct hwmon_channel_info *aqr_hwmon_info[] = { +- &aqr_hwmon_chip, +- &aqr_hwmon_temp, +- NULL, +-}; +- +-static const struct hwmon_chip_info aqr_hwmon_chip_info = { +- .ops = &aqr_hwmon_ops, +- .info = aqr_hwmon_info, +-}; +- +-int aqr_hwmon_probe(struct phy_device *phydev) +-{ +- struct device *dev = &phydev->mdio.dev; +- struct device *hwmon_dev; +- char *hwmon_name; +- int i, j; +- +- hwmon_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); +- if (!hwmon_name) +- return -ENOMEM; +- +- for (i = j = 0; hwmon_name[i]; i++) { +- if (isalnum(hwmon_name[i])) { +- if (i != j) +- hwmon_name[j] = hwmon_name[i]; +- j++; +- } +- } +- hwmon_name[j] = '\0'; +- +- hwmon_dev = devm_hwmon_device_register_with_info(dev, hwmon_name, +- phydev, &aqr_hwmon_chip_info, NULL); +- +- return PTR_ERR_OR_ZERO(hwmon_dev); +-} +- +-#endif +--- a/drivers/net/phy/aquantia_main.c ++++ /dev/null +@@ -1,842 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-/* +- * Driver for Aquantia PHY +- * +- * Author: Shaohui Xie +- * +- * Copyright 2015 Freescale Semiconductor, Inc. +- */ +- +-#include +-#include +-#include +-#include +-#include +- +-#include "aquantia.h" +- +-#define PHY_ID_AQ1202 0x03a1b445 +-#define PHY_ID_AQ2104 0x03a1b460 +-#define PHY_ID_AQR105 0x03a1b4a2 +-#define PHY_ID_AQR106 0x03a1b4d0 +-#define PHY_ID_AQR107 0x03a1b4e0 +-#define PHY_ID_AQCS109 0x03a1b5c2 +-#define PHY_ID_AQR405 0x03a1b4b0 +-#define PHY_ID_AQR113C 0x31c31c12 +- +-#define MDIO_PHYXS_VEND_IF_STATUS 0xe812 +-#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK GENMASK(7, 3) +-#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR 0 +-#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_KX 1 +-#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI 2 +-#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII 3 +-#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_XAUI 4 +-#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII 6 +-#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_RXAUI 7 +-#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII 10 +- +-#define MDIO_AN_VEND_PROV 0xc400 +-#define MDIO_AN_VEND_PROV_1000BASET_FULL BIT(15) +-#define MDIO_AN_VEND_PROV_1000BASET_HALF BIT(14) +-#define MDIO_AN_VEND_PROV_5000BASET_FULL BIT(11) +-#define MDIO_AN_VEND_PROV_2500BASET_FULL BIT(10) +-#define MDIO_AN_VEND_PROV_DOWNSHIFT_EN BIT(4) +-#define MDIO_AN_VEND_PROV_DOWNSHIFT_MASK GENMASK(3, 0) +-#define MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT 4 +- +-#define MDIO_AN_TX_VEND_STATUS1 0xc800 +-#define MDIO_AN_TX_VEND_STATUS1_RATE_MASK GENMASK(3, 1) +-#define MDIO_AN_TX_VEND_STATUS1_10BASET 0 +-#define MDIO_AN_TX_VEND_STATUS1_100BASETX 1 +-#define MDIO_AN_TX_VEND_STATUS1_1000BASET 2 +-#define MDIO_AN_TX_VEND_STATUS1_10GBASET 3 +-#define MDIO_AN_TX_VEND_STATUS1_2500BASET 4 +-#define MDIO_AN_TX_VEND_STATUS1_5000BASET 5 +-#define MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX BIT(0) +- +-#define MDIO_AN_TX_VEND_INT_STATUS1 0xcc00 +-#define MDIO_AN_TX_VEND_INT_STATUS1_DOWNSHIFT BIT(1) +- +-#define MDIO_AN_TX_VEND_INT_STATUS2 0xcc01 +-#define MDIO_AN_TX_VEND_INT_STATUS2_MASK BIT(0) +- +-#define MDIO_AN_TX_VEND_INT_MASK2 0xd401 +-#define MDIO_AN_TX_VEND_INT_MASK2_LINK BIT(0) +- +-#define MDIO_AN_RX_LP_STAT1 0xe820 +-#define MDIO_AN_RX_LP_STAT1_1000BASET_FULL BIT(15) +-#define MDIO_AN_RX_LP_STAT1_1000BASET_HALF BIT(14) +-#define MDIO_AN_RX_LP_STAT1_SHORT_REACH BIT(13) +-#define MDIO_AN_RX_LP_STAT1_AQRATE_DOWNSHIFT BIT(12) +-#define MDIO_AN_RX_LP_STAT1_AQ_PHY BIT(2) +- +-#define MDIO_AN_RX_LP_STAT4 0xe823 +-#define MDIO_AN_RX_LP_STAT4_FW_MAJOR GENMASK(15, 8) +-#define MDIO_AN_RX_LP_STAT4_FW_MINOR GENMASK(7, 0) +- +-#define MDIO_AN_RX_VEND_STAT3 0xe832 +-#define MDIO_AN_RX_VEND_STAT3_AFR BIT(0) +- +-/* MDIO_MMD_C22EXT */ +-#define MDIO_C22EXT_STAT_SGMII_RX_GOOD_FRAMES 0xd292 +-#define MDIO_C22EXT_STAT_SGMII_RX_BAD_FRAMES 0xd294 +-#define MDIO_C22EXT_STAT_SGMII_RX_FALSE_CARRIER 0xd297 +-#define MDIO_C22EXT_STAT_SGMII_TX_GOOD_FRAMES 0xd313 +-#define MDIO_C22EXT_STAT_SGMII_TX_BAD_FRAMES 0xd315 +-#define MDIO_C22EXT_STAT_SGMII_TX_FALSE_CARRIER 0xd317 +-#define MDIO_C22EXT_STAT_SGMII_TX_COLLISIONS 0xd318 +-#define MDIO_C22EXT_STAT_SGMII_TX_LINE_COLLISIONS 0xd319 +-#define MDIO_C22EXT_STAT_SGMII_TX_FRAME_ALIGN_ERR 0xd31a +-#define MDIO_C22EXT_STAT_SGMII_TX_RUNT_FRAMES 0xd31b +- +-/* Vendor specific 1, MDIO_MMD_VEND1 */ +-#define VEND1_GLOBAL_FW_ID 0x0020 +-#define VEND1_GLOBAL_FW_ID_MAJOR GENMASK(15, 8) +-#define VEND1_GLOBAL_FW_ID_MINOR GENMASK(7, 0) +- +-#define VEND1_GLOBAL_GEN_STAT2 0xc831 +-#define VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG BIT(15) +- +-/* The following registers all have similar layouts; first the registers... */ +-#define VEND1_GLOBAL_CFG_10M 0x0310 +-#define VEND1_GLOBAL_CFG_100M 0x031b +-#define VEND1_GLOBAL_CFG_1G 0x031c +-#define VEND1_GLOBAL_CFG_2_5G 0x031d +-#define VEND1_GLOBAL_CFG_5G 0x031e +-#define VEND1_GLOBAL_CFG_10G 0x031f +-/* ...and now the fields */ +-#define VEND1_GLOBAL_CFG_RATE_ADAPT GENMASK(8, 7) +-#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE 0 +-#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX 1 +-#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE 2 +- +-#define VEND1_GLOBAL_RSVD_STAT1 0xc885 +-#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID GENMASK(7, 4) +-#define VEND1_GLOBAL_RSVD_STAT1_PROV_ID GENMASK(3, 0) +- +-#define VEND1_GLOBAL_RSVD_STAT9 0xc88d +-#define VEND1_GLOBAL_RSVD_STAT9_MODE GENMASK(7, 0) +-#define VEND1_GLOBAL_RSVD_STAT9_1000BT2 0x23 +- +-#define VEND1_GLOBAL_INT_STD_STATUS 0xfc00 +-#define VEND1_GLOBAL_INT_VEND_STATUS 0xfc01 +- +-#define VEND1_GLOBAL_INT_STD_MASK 0xff00 +-#define VEND1_GLOBAL_INT_STD_MASK_PMA1 BIT(15) +-#define VEND1_GLOBAL_INT_STD_MASK_PMA2 BIT(14) +-#define VEND1_GLOBAL_INT_STD_MASK_PCS1 BIT(13) +-#define VEND1_GLOBAL_INT_STD_MASK_PCS2 BIT(12) +-#define VEND1_GLOBAL_INT_STD_MASK_PCS3 BIT(11) +-#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1 BIT(10) +-#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2 BIT(9) +-#define VEND1_GLOBAL_INT_STD_MASK_AN1 BIT(8) +-#define VEND1_GLOBAL_INT_STD_MASK_AN2 BIT(7) +-#define VEND1_GLOBAL_INT_STD_MASK_GBE BIT(6) +-#define VEND1_GLOBAL_INT_STD_MASK_ALL BIT(0) +- +-#define VEND1_GLOBAL_INT_VEND_MASK 0xff01 +-#define VEND1_GLOBAL_INT_VEND_MASK_PMA BIT(15) +-#define VEND1_GLOBAL_INT_VEND_MASK_PCS BIT(14) +-#define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS BIT(13) +-#define VEND1_GLOBAL_INT_VEND_MASK_AN BIT(12) +-#define VEND1_GLOBAL_INT_VEND_MASK_GBE BIT(11) +-#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1 BIT(2) +-#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2 BIT(1) +-#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 BIT(0) +- +-/* Sleep and timeout for checking if the Processor-Intensive +- * MDIO operation is finished +- */ +-#define AQR107_OP_IN_PROG_SLEEP 1000 +-#define AQR107_OP_IN_PROG_TIMEOUT 100000 +- +-struct aqr107_hw_stat { +- const char *name; +- int reg; +- int size; +-}; +- +-#define SGMII_STAT(n, r, s) { n, MDIO_C22EXT_STAT_SGMII_ ## r, s } +-static const struct aqr107_hw_stat aqr107_hw_stats[] = { +- SGMII_STAT("sgmii_rx_good_frames", RX_GOOD_FRAMES, 26), +- SGMII_STAT("sgmii_rx_bad_frames", RX_BAD_FRAMES, 26), +- SGMII_STAT("sgmii_rx_false_carrier_events", RX_FALSE_CARRIER, 8), +- SGMII_STAT("sgmii_tx_good_frames", TX_GOOD_FRAMES, 26), +- SGMII_STAT("sgmii_tx_bad_frames", TX_BAD_FRAMES, 26), +- SGMII_STAT("sgmii_tx_false_carrier_events", TX_FALSE_CARRIER, 8), +- SGMII_STAT("sgmii_tx_collisions", TX_COLLISIONS, 8), +- SGMII_STAT("sgmii_tx_line_collisions", TX_LINE_COLLISIONS, 8), +- SGMII_STAT("sgmii_tx_frame_alignment_err", TX_FRAME_ALIGN_ERR, 16), +- SGMII_STAT("sgmii_tx_runt_frames", TX_RUNT_FRAMES, 22), +-}; +-#define AQR107_SGMII_STAT_SZ ARRAY_SIZE(aqr107_hw_stats) +- +-struct aqr107_priv { +- u64 sgmii_stats[AQR107_SGMII_STAT_SZ]; +-}; +- +-static int aqr107_get_sset_count(struct phy_device *phydev) +-{ +- return AQR107_SGMII_STAT_SZ; +-} +- +-static void aqr107_get_strings(struct phy_device *phydev, u8 *data) +-{ +- int i; +- +- for (i = 0; i < AQR107_SGMII_STAT_SZ; i++) +- strscpy(data + i * ETH_GSTRING_LEN, aqr107_hw_stats[i].name, +- ETH_GSTRING_LEN); +-} +- +-static u64 aqr107_get_stat(struct phy_device *phydev, int index) +-{ +- const struct aqr107_hw_stat *stat = aqr107_hw_stats + index; +- int len_l = min(stat->size, 16); +- int len_h = stat->size - len_l; +- u64 ret; +- int val; +- +- val = phy_read_mmd(phydev, MDIO_MMD_C22EXT, stat->reg); +- if (val < 0) +- return U64_MAX; +- +- ret = val & GENMASK(len_l - 1, 0); +- if (len_h) { +- val = phy_read_mmd(phydev, MDIO_MMD_C22EXT, stat->reg + 1); +- if (val < 0) +- return U64_MAX; +- +- ret += (val & GENMASK(len_h - 1, 0)) << 16; +- } +- +- return ret; +-} +- +-static void aqr107_get_stats(struct phy_device *phydev, +- struct ethtool_stats *stats, u64 *data) +-{ +- struct aqr107_priv *priv = phydev->priv; +- u64 val; +- int i; +- +- for (i = 0; i < AQR107_SGMII_STAT_SZ; i++) { +- val = aqr107_get_stat(phydev, i); +- if (val == U64_MAX) +- phydev_err(phydev, "Reading HW Statistics failed for %s\n", +- aqr107_hw_stats[i].name); +- else +- priv->sgmii_stats[i] += val; +- +- data[i] = priv->sgmii_stats[i]; +- } +-} +- +-static int aqr_config_aneg(struct phy_device *phydev) +-{ +- bool changed = false; +- u16 reg; +- int ret; +- +- if (phydev->autoneg == AUTONEG_DISABLE) +- return genphy_c45_pma_setup_forced(phydev); +- +- ret = genphy_c45_an_config_aneg(phydev); +- if (ret < 0) +- return ret; +- if (ret > 0) +- changed = true; +- +- /* Clause 45 has no standardized support for 1000BaseT, therefore +- * use vendor registers for this mode. +- */ +- reg = 0; +- if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, +- phydev->advertising)) +- reg |= MDIO_AN_VEND_PROV_1000BASET_FULL; +- +- if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, +- phydev->advertising)) +- reg |= MDIO_AN_VEND_PROV_1000BASET_HALF; +- +- /* Handle the case when the 2.5G and 5G speeds are not advertised */ +- if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, +- phydev->advertising)) +- reg |= MDIO_AN_VEND_PROV_2500BASET_FULL; +- +- if (linkmode_test_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, +- phydev->advertising)) +- reg |= MDIO_AN_VEND_PROV_5000BASET_FULL; +- +- ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV, +- MDIO_AN_VEND_PROV_1000BASET_HALF | +- MDIO_AN_VEND_PROV_1000BASET_FULL | +- MDIO_AN_VEND_PROV_2500BASET_FULL | +- MDIO_AN_VEND_PROV_5000BASET_FULL, reg); +- if (ret < 0) +- return ret; +- if (ret > 0) +- changed = true; +- +- return genphy_c45_check_and_restart_aneg(phydev, changed); +-} +- +-static int aqr_config_intr(struct phy_device *phydev) +-{ +- bool en = phydev->interrupts == PHY_INTERRUPT_ENABLED; +- int err; +- +- if (en) { +- /* Clear any pending interrupts before enabling them */ +- err = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_STATUS2); +- if (err < 0) +- return err; +- } +- +- err = phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_MASK2, +- en ? MDIO_AN_TX_VEND_INT_MASK2_LINK : 0); +- if (err < 0) +- return err; +- +- err = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_INT_STD_MASK, +- en ? VEND1_GLOBAL_INT_STD_MASK_ALL : 0); +- if (err < 0) +- return err; +- +- err = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_INT_VEND_MASK, +- en ? VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 | +- VEND1_GLOBAL_INT_VEND_MASK_AN : 0); +- if (err < 0) +- return err; +- +- if (!en) { +- /* Clear any pending interrupts after we have disabled them */ +- err = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_STATUS2); +- if (err < 0) +- return err; +- } +- +- return 0; +-} +- +-static irqreturn_t aqr_handle_interrupt(struct phy_device *phydev) +-{ +- int irq_status; +- +- irq_status = phy_read_mmd(phydev, MDIO_MMD_AN, +- MDIO_AN_TX_VEND_INT_STATUS2); +- if (irq_status < 0) { +- phy_error(phydev); +- return IRQ_NONE; +- } +- +- if (!(irq_status & MDIO_AN_TX_VEND_INT_STATUS2_MASK)) +- return IRQ_NONE; +- +- phy_trigger_machine(phydev); +- +- return IRQ_HANDLED; +-} +- +-static int aqr_read_status(struct phy_device *phydev) +-{ +- int val; +- +- if (phydev->autoneg == AUTONEG_ENABLE) { +- val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_LP_STAT1); +- if (val < 0) +- return val; +- +- linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, +- phydev->lp_advertising, +- val & MDIO_AN_RX_LP_STAT1_1000BASET_FULL); +- linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, +- phydev->lp_advertising, +- val & MDIO_AN_RX_LP_STAT1_1000BASET_HALF); +- } +- +- return genphy_c45_read_status(phydev); +-} +- +-static int aqr107_read_rate(struct phy_device *phydev) +-{ +- u32 config_reg; +- int val; +- +- val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_STATUS1); +- if (val < 0) +- return val; +- +- if (val & MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX) +- phydev->duplex = DUPLEX_FULL; +- else +- phydev->duplex = DUPLEX_HALF; +- +- switch (FIELD_GET(MDIO_AN_TX_VEND_STATUS1_RATE_MASK, val)) { +- case MDIO_AN_TX_VEND_STATUS1_10BASET: +- phydev->speed = SPEED_10; +- config_reg = VEND1_GLOBAL_CFG_10M; +- break; +- case MDIO_AN_TX_VEND_STATUS1_100BASETX: +- phydev->speed = SPEED_100; +- config_reg = VEND1_GLOBAL_CFG_100M; +- break; +- case MDIO_AN_TX_VEND_STATUS1_1000BASET: +- phydev->speed = SPEED_1000; +- config_reg = VEND1_GLOBAL_CFG_1G; +- break; +- case MDIO_AN_TX_VEND_STATUS1_2500BASET: +- phydev->speed = SPEED_2500; +- config_reg = VEND1_GLOBAL_CFG_2_5G; +- break; +- case MDIO_AN_TX_VEND_STATUS1_5000BASET: +- phydev->speed = SPEED_5000; +- config_reg = VEND1_GLOBAL_CFG_5G; +- break; +- case MDIO_AN_TX_VEND_STATUS1_10GBASET: +- phydev->speed = SPEED_10000; +- config_reg = VEND1_GLOBAL_CFG_10G; +- break; +- default: +- phydev->speed = SPEED_UNKNOWN; +- return 0; +- } +- +- val = phy_read_mmd(phydev, MDIO_MMD_VEND1, config_reg); +- if (val < 0) +- return val; +- +- if (FIELD_GET(VEND1_GLOBAL_CFG_RATE_ADAPT, val) == +- VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE) +- phydev->rate_matching = RATE_MATCH_PAUSE; +- else +- phydev->rate_matching = RATE_MATCH_NONE; +- +- return 0; +-} +- +-static int aqr107_read_status(struct phy_device *phydev) +-{ +- int val, ret; +- +- ret = aqr_read_status(phydev); +- if (ret) +- return ret; +- +- if (!phydev->link || phydev->autoneg == AUTONEG_DISABLE) +- return 0; +- +- val = phy_read_mmd(phydev, MDIO_MMD_PHYXS, MDIO_PHYXS_VEND_IF_STATUS); +- if (val < 0) +- return val; +- +- switch (FIELD_GET(MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK, val)) { +- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR: +- phydev->interface = PHY_INTERFACE_MODE_10GKR; +- break; +- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KX: +- phydev->interface = PHY_INTERFACE_MODE_1000BASEKX; +- break; +- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI: +- phydev->interface = PHY_INTERFACE_MODE_10GBASER; +- break; +- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII: +- phydev->interface = PHY_INTERFACE_MODE_USXGMII; +- break; +- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XAUI: +- phydev->interface = PHY_INTERFACE_MODE_XAUI; +- break; +- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII: +- phydev->interface = PHY_INTERFACE_MODE_SGMII; +- break; +- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_RXAUI: +- phydev->interface = PHY_INTERFACE_MODE_RXAUI; +- break; +- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII: +- phydev->interface = PHY_INTERFACE_MODE_2500BASEX; +- break; +- default: +- phydev->interface = PHY_INTERFACE_MODE_NA; +- break; +- } +- +- /* Read possibly downshifted rate from vendor register */ +- return aqr107_read_rate(phydev); +-} +- +-static int aqr107_get_downshift(struct phy_device *phydev, u8 *data) +-{ +- int val, cnt, enable; +- +- val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV); +- if (val < 0) +- return val; +- +- enable = FIELD_GET(MDIO_AN_VEND_PROV_DOWNSHIFT_EN, val); +- cnt = FIELD_GET(MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, val); +- +- *data = enable && cnt ? cnt : DOWNSHIFT_DEV_DISABLE; +- +- return 0; +-} +- +-static int aqr107_set_downshift(struct phy_device *phydev, u8 cnt) +-{ +- int val = 0; +- +- if (!FIELD_FIT(MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, cnt)) +- return -E2BIG; +- +- if (cnt != DOWNSHIFT_DEV_DISABLE) { +- val = MDIO_AN_VEND_PROV_DOWNSHIFT_EN; +- val |= FIELD_PREP(MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, cnt); +- } +- +- return phy_modify_mmd(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV, +- MDIO_AN_VEND_PROV_DOWNSHIFT_EN | +- MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, val); +-} +- +-static int aqr107_get_tunable(struct phy_device *phydev, +- struct ethtool_tunable *tuna, void *data) +-{ +- switch (tuna->id) { +- case ETHTOOL_PHY_DOWNSHIFT: +- return aqr107_get_downshift(phydev, data); +- default: +- return -EOPNOTSUPP; +- } +-} +- +-static int aqr107_set_tunable(struct phy_device *phydev, +- struct ethtool_tunable *tuna, const void *data) +-{ +- switch (tuna->id) { +- case ETHTOOL_PHY_DOWNSHIFT: +- return aqr107_set_downshift(phydev, *(const u8 *)data); +- default: +- return -EOPNOTSUPP; +- } +-} +- +-/* If we configure settings whilst firmware is still initializing the chip, +- * then these settings may be overwritten. Therefore make sure chip +- * initialization has completed. Use presence of the firmware ID as +- * indicator for initialization having completed. +- * The chip also provides a "reset completed" bit, but it's cleared after +- * read. Therefore function would time out if called again. +- */ +-static int aqr107_wait_reset_complete(struct phy_device *phydev) +-{ +- int val; +- +- return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, +- VEND1_GLOBAL_FW_ID, val, val != 0, +- 20000, 2000000, false); +-} +- +-static void aqr107_chip_info(struct phy_device *phydev) +-{ +- u8 fw_major, fw_minor, build_id, prov_id; +- int val; +- +- val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_FW_ID); +- if (val < 0) +- return; +- +- fw_major = FIELD_GET(VEND1_GLOBAL_FW_ID_MAJOR, val); +- fw_minor = FIELD_GET(VEND1_GLOBAL_FW_ID_MINOR, val); +- +- val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_RSVD_STAT1); +- if (val < 0) +- return; +- +- build_id = FIELD_GET(VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID, val); +- prov_id = FIELD_GET(VEND1_GLOBAL_RSVD_STAT1_PROV_ID, val); +- +- phydev_dbg(phydev, "FW %u.%u, Build %u, Provisioning %u\n", +- fw_major, fw_minor, build_id, prov_id); +-} +- +-static int aqr107_config_init(struct phy_device *phydev) +-{ +- int ret; +- +- /* Check that the PHY interface type is compatible */ +- if (phydev->interface != PHY_INTERFACE_MODE_SGMII && +- phydev->interface != PHY_INTERFACE_MODE_1000BASEKX && +- phydev->interface != PHY_INTERFACE_MODE_2500BASEX && +- phydev->interface != PHY_INTERFACE_MODE_XGMII && +- phydev->interface != PHY_INTERFACE_MODE_USXGMII && +- phydev->interface != PHY_INTERFACE_MODE_10GKR && +- phydev->interface != PHY_INTERFACE_MODE_10GBASER && +- phydev->interface != PHY_INTERFACE_MODE_XAUI && +- phydev->interface != PHY_INTERFACE_MODE_RXAUI) +- return -ENODEV; +- +- WARN(phydev->interface == PHY_INTERFACE_MODE_XGMII, +- "Your devicetree is out of date, please update it. The AQR107 family doesn't support XGMII, maybe you mean USXGMII.\n"); +- +- ret = aqr107_wait_reset_complete(phydev); +- if (!ret) +- aqr107_chip_info(phydev); +- +- return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT); +-} +- +-static int aqcs109_config_init(struct phy_device *phydev) +-{ +- int ret; +- +- /* Check that the PHY interface type is compatible */ +- if (phydev->interface != PHY_INTERFACE_MODE_SGMII && +- phydev->interface != PHY_INTERFACE_MODE_2500BASEX) +- return -ENODEV; +- +- ret = aqr107_wait_reset_complete(phydev); +- if (!ret) +- aqr107_chip_info(phydev); +- +- /* AQCS109 belongs to a chip family partially supporting 10G and 5G. +- * PMA speed ability bits are the same for all members of the family, +- * AQCS109 however supports speeds up to 2.5G only. +- */ +- phy_set_max_speed(phydev, SPEED_2500); +- +- return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT); +-} +- +-static void aqr107_link_change_notify(struct phy_device *phydev) +-{ +- u8 fw_major, fw_minor; +- bool downshift, short_reach, afr; +- int mode, val; +- +- if (phydev->state != PHY_RUNNING || phydev->autoneg == AUTONEG_DISABLE) +- return; +- +- val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_LP_STAT1); +- /* call failed or link partner is no Aquantia PHY */ +- if (val < 0 || !(val & MDIO_AN_RX_LP_STAT1_AQ_PHY)) +- return; +- +- short_reach = val & MDIO_AN_RX_LP_STAT1_SHORT_REACH; +- downshift = val & MDIO_AN_RX_LP_STAT1_AQRATE_DOWNSHIFT; +- +- val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_LP_STAT4); +- if (val < 0) +- return; +- +- fw_major = FIELD_GET(MDIO_AN_RX_LP_STAT4_FW_MAJOR, val); +- fw_minor = FIELD_GET(MDIO_AN_RX_LP_STAT4_FW_MINOR, val); +- +- val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_VEND_STAT3); +- if (val < 0) +- return; +- +- afr = val & MDIO_AN_RX_VEND_STAT3_AFR; +- +- phydev_dbg(phydev, "Link partner is Aquantia PHY, FW %u.%u%s%s%s\n", +- fw_major, fw_minor, +- short_reach ? ", short reach mode" : "", +- downshift ? ", fast-retrain downshift advertised" : "", +- afr ? ", fast reframe advertised" : ""); +- +- val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_RSVD_STAT9); +- if (val < 0) +- return; +- +- mode = FIELD_GET(VEND1_GLOBAL_RSVD_STAT9_MODE, val); +- if (mode == VEND1_GLOBAL_RSVD_STAT9_1000BT2) +- phydev_info(phydev, "Aquantia 1000Base-T2 mode active\n"); +-} +- +-static int aqr107_wait_processor_intensive_op(struct phy_device *phydev) +-{ +- int val, err; +- +- /* The datasheet notes to wait at least 1ms after issuing a +- * processor intensive operation before checking. +- * We cannot use the 'sleep_before_read' parameter of read_poll_timeout +- * because that just determines the maximum time slept, not the minimum. +- */ +- usleep_range(1000, 5000); +- +- err = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, +- VEND1_GLOBAL_GEN_STAT2, val, +- !(val & VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG), +- AQR107_OP_IN_PROG_SLEEP, +- AQR107_OP_IN_PROG_TIMEOUT, false); +- if (err) { +- phydev_err(phydev, "timeout: processor-intensive MDIO operation\n"); +- return err; +- } +- +- return 0; +-} +- +-static int aqr107_get_rate_matching(struct phy_device *phydev, +- phy_interface_t iface) +-{ +- if (iface == PHY_INTERFACE_MODE_10GBASER || +- iface == PHY_INTERFACE_MODE_2500BASEX || +- iface == PHY_INTERFACE_MODE_NA) +- return RATE_MATCH_PAUSE; +- return RATE_MATCH_NONE; +-} +- +-static int aqr107_suspend(struct phy_device *phydev) +-{ +- int err; +- +- err = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MDIO_CTRL1, +- MDIO_CTRL1_LPOWER); +- if (err) +- return err; +- +- return aqr107_wait_processor_intensive_op(phydev); +-} +- +-static int aqr107_resume(struct phy_device *phydev) +-{ +- int err; +- +- err = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MDIO_CTRL1, +- MDIO_CTRL1_LPOWER); +- if (err) +- return err; +- +- return aqr107_wait_processor_intensive_op(phydev); +-} +- +-static int aqr107_probe(struct phy_device *phydev) +-{ +- phydev->priv = devm_kzalloc(&phydev->mdio.dev, +- sizeof(struct aqr107_priv), GFP_KERNEL); +- if (!phydev->priv) +- return -ENOMEM; +- +- return aqr_hwmon_probe(phydev); +-} +- +-static struct phy_driver aqr_driver[] = { +-{ +- PHY_ID_MATCH_MODEL(PHY_ID_AQ1202), +- .name = "Aquantia AQ1202", +- .config_aneg = aqr_config_aneg, +- .config_intr = aqr_config_intr, +- .handle_interrupt = aqr_handle_interrupt, +- .read_status = aqr_read_status, +-}, +-{ +- PHY_ID_MATCH_MODEL(PHY_ID_AQ2104), +- .name = "Aquantia AQ2104", +- .config_aneg = aqr_config_aneg, +- .config_intr = aqr_config_intr, +- .handle_interrupt = aqr_handle_interrupt, +- .read_status = aqr_read_status, +-}, +-{ +- PHY_ID_MATCH_MODEL(PHY_ID_AQR105), +- .name = "Aquantia AQR105", +- .config_aneg = aqr_config_aneg, +- .config_intr = aqr_config_intr, +- .handle_interrupt = aqr_handle_interrupt, +- .read_status = aqr_read_status, +- .suspend = aqr107_suspend, +- .resume = aqr107_resume, +-}, +-{ +- PHY_ID_MATCH_MODEL(PHY_ID_AQR106), +- .name = "Aquantia AQR106", +- .config_aneg = aqr_config_aneg, +- .config_intr = aqr_config_intr, +- .handle_interrupt = aqr_handle_interrupt, +- .read_status = aqr_read_status, +-}, +-{ +- PHY_ID_MATCH_MODEL(PHY_ID_AQR107), +- .name = "Aquantia AQR107", +- .probe = aqr107_probe, +- .get_rate_matching = aqr107_get_rate_matching, +- .config_init = aqr107_config_init, +- .config_aneg = aqr_config_aneg, +- .config_intr = aqr_config_intr, +- .handle_interrupt = aqr_handle_interrupt, +- .read_status = aqr107_read_status, +- .get_tunable = aqr107_get_tunable, +- .set_tunable = aqr107_set_tunable, +- .suspend = aqr107_suspend, +- .resume = aqr107_resume, +- .get_sset_count = aqr107_get_sset_count, +- .get_strings = aqr107_get_strings, +- .get_stats = aqr107_get_stats, +- .link_change_notify = aqr107_link_change_notify, +-}, +-{ +- PHY_ID_MATCH_MODEL(PHY_ID_AQCS109), +- .name = "Aquantia AQCS109", +- .probe = aqr107_probe, +- .get_rate_matching = aqr107_get_rate_matching, +- .config_init = aqcs109_config_init, +- .config_aneg = aqr_config_aneg, +- .config_intr = aqr_config_intr, +- .handle_interrupt = aqr_handle_interrupt, +- .read_status = aqr107_read_status, +- .get_tunable = aqr107_get_tunable, +- .set_tunable = aqr107_set_tunable, +- .suspend = aqr107_suspend, +- .resume = aqr107_resume, +- .get_sset_count = aqr107_get_sset_count, +- .get_strings = aqr107_get_strings, +- .get_stats = aqr107_get_stats, +- .link_change_notify = aqr107_link_change_notify, +-}, +-{ +- PHY_ID_MATCH_MODEL(PHY_ID_AQR405), +- .name = "Aquantia AQR405", +- .config_aneg = aqr_config_aneg, +- .config_intr = aqr_config_intr, +- .handle_interrupt = aqr_handle_interrupt, +- .read_status = aqr_read_status, +-}, +-{ +- PHY_ID_MATCH_MODEL(PHY_ID_AQR113C), +- .name = "Aquantia AQR113C", +- .probe = aqr107_probe, +- .get_rate_matching = aqr107_get_rate_matching, +- .config_init = aqr107_config_init, +- .config_aneg = aqr_config_aneg, +- .config_intr = aqr_config_intr, +- .handle_interrupt = aqr_handle_interrupt, +- .read_status = aqr107_read_status, +- .get_tunable = aqr107_get_tunable, +- .set_tunable = aqr107_set_tunable, +- .suspend = aqr107_suspend, +- .resume = aqr107_resume, +- .get_sset_count = aqr107_get_sset_count, +- .get_strings = aqr107_get_strings, +- .get_stats = aqr107_get_stats, +- .link_change_notify = aqr107_link_change_notify, +-}, +-}; +- +-module_phy_driver(aqr_driver); +- +-static struct mdio_device_id __maybe_unused aqr_tbl[] = { +- { PHY_ID_MATCH_MODEL(PHY_ID_AQ1202) }, +- { PHY_ID_MATCH_MODEL(PHY_ID_AQ2104) }, +- { PHY_ID_MATCH_MODEL(PHY_ID_AQR105) }, +- { PHY_ID_MATCH_MODEL(PHY_ID_AQR106) }, +- { PHY_ID_MATCH_MODEL(PHY_ID_AQR107) }, +- { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) }, +- { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) }, +- { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) }, +- { } +-}; +- +-MODULE_DEVICE_TABLE(mdio, aqr_tbl); +- +-MODULE_DESCRIPTION("Aquantia PHY driver"); +-MODULE_AUTHOR("Shaohui Xie "); +-MODULE_LICENSE("GPL v2"); diff --git a/target/linux/generic/backport-6.6/702-02-v6.7-net-phy-aquantia-move-MMD_VEND-define-to-header.patch b/target/linux/generic/backport-6.6/702-02-v6.7-net-phy-aquantia-move-MMD_VEND-define-to-header.patch new file mode 100644 index 0000000000..2b94522723 --- /dev/null +++ b/target/linux/generic/backport-6.6/702-02-v6.7-net-phy-aquantia-move-MMD_VEND-define-to-header.patch @@ -0,0 +1,183 @@ +From e1fbfa4a995d42e02e22b0dff2f8b4fdee1504b3 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 14 Nov 2023 15:08:42 +0100 +Subject: [PATCH 2/3] net: phy: aquantia: move MMD_VEND define to header + +Move MMD_VEND define to header to clean things up and in preparation for +firmware loading support that require some define placed in +aquantia_main. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/aquantia/aquantia.h | 69 +++++++++++++++++++++++ + drivers/net/phy/aquantia/aquantia_hwmon.c | 14 ----- + drivers/net/phy/aquantia/aquantia_main.c | 55 ------------------ + 3 files changed, 69 insertions(+), 69 deletions(-) + +--- a/drivers/net/phy/aquantia/aquantia.h ++++ b/drivers/net/phy/aquantia/aquantia.h +@@ -9,6 +9,75 @@ + #include + #include + ++/* Vendor specific 1, MDIO_MMD_VEND1 */ ++#define VEND1_GLOBAL_FW_ID 0x0020 ++#define VEND1_GLOBAL_FW_ID_MAJOR GENMASK(15, 8) ++#define VEND1_GLOBAL_FW_ID_MINOR GENMASK(7, 0) ++ ++/* The following registers all have similar layouts; first the registers... */ ++#define VEND1_GLOBAL_CFG_10M 0x0310 ++#define VEND1_GLOBAL_CFG_100M 0x031b ++#define VEND1_GLOBAL_CFG_1G 0x031c ++#define VEND1_GLOBAL_CFG_2_5G 0x031d ++#define VEND1_GLOBAL_CFG_5G 0x031e ++#define VEND1_GLOBAL_CFG_10G 0x031f ++/* ...and now the fields */ ++#define VEND1_GLOBAL_CFG_RATE_ADAPT GENMASK(8, 7) ++#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE 0 ++#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX 1 ++#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE 2 ++ ++/* Vendor specific 1, MDIO_MMD_VEND2 */ ++#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL 0xc421 ++#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL 0xc422 ++#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN 0xc423 ++#define VEND1_THERMAL_PROV_LOW_TEMP_WARN 0xc424 ++#define VEND1_THERMAL_STAT1 0xc820 ++#define VEND1_THERMAL_STAT2 0xc821 ++#define VEND1_THERMAL_STAT2_VALID BIT(0) ++#define VEND1_GENERAL_STAT1 0xc830 ++#define VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL BIT(14) ++#define VEND1_GENERAL_STAT1_LOW_TEMP_FAIL BIT(13) ++#define VEND1_GENERAL_STAT1_HIGH_TEMP_WARN BIT(12) ++#define VEND1_GENERAL_STAT1_LOW_TEMP_WARN BIT(11) ++ ++#define VEND1_GLOBAL_GEN_STAT2 0xc831 ++#define VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG BIT(15) ++ ++#define VEND1_GLOBAL_RSVD_STAT1 0xc885 ++#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID GENMASK(7, 4) ++#define VEND1_GLOBAL_RSVD_STAT1_PROV_ID GENMASK(3, 0) ++ ++#define VEND1_GLOBAL_RSVD_STAT9 0xc88d ++#define VEND1_GLOBAL_RSVD_STAT9_MODE GENMASK(7, 0) ++#define VEND1_GLOBAL_RSVD_STAT9_1000BT2 0x23 ++ ++#define VEND1_GLOBAL_INT_STD_STATUS 0xfc00 ++#define VEND1_GLOBAL_INT_VEND_STATUS 0xfc01 ++ ++#define VEND1_GLOBAL_INT_STD_MASK 0xff00 ++#define VEND1_GLOBAL_INT_STD_MASK_PMA1 BIT(15) ++#define VEND1_GLOBAL_INT_STD_MASK_PMA2 BIT(14) ++#define VEND1_GLOBAL_INT_STD_MASK_PCS1 BIT(13) ++#define VEND1_GLOBAL_INT_STD_MASK_PCS2 BIT(12) ++#define VEND1_GLOBAL_INT_STD_MASK_PCS3 BIT(11) ++#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1 BIT(10) ++#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2 BIT(9) ++#define VEND1_GLOBAL_INT_STD_MASK_AN1 BIT(8) ++#define VEND1_GLOBAL_INT_STD_MASK_AN2 BIT(7) ++#define VEND1_GLOBAL_INT_STD_MASK_GBE BIT(6) ++#define VEND1_GLOBAL_INT_STD_MASK_ALL BIT(0) ++ ++#define VEND1_GLOBAL_INT_VEND_MASK 0xff01 ++#define VEND1_GLOBAL_INT_VEND_MASK_PMA BIT(15) ++#define VEND1_GLOBAL_INT_VEND_MASK_PCS BIT(14) ++#define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS BIT(13) ++#define VEND1_GLOBAL_INT_VEND_MASK_AN BIT(12) ++#define VEND1_GLOBAL_INT_VEND_MASK_GBE BIT(11) ++#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1 BIT(2) ++#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2 BIT(1) ++#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 BIT(0) ++ + #if IS_REACHABLE(CONFIG_HWMON) + int aqr_hwmon_probe(struct phy_device *phydev); + #else +--- a/drivers/net/phy/aquantia/aquantia_hwmon.c ++++ b/drivers/net/phy/aquantia/aquantia_hwmon.c +@@ -13,20 +13,6 @@ + + #include "aquantia.h" + +-/* Vendor specific 1, MDIO_MMD_VEND2 */ +-#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL 0xc421 +-#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL 0xc422 +-#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN 0xc423 +-#define VEND1_THERMAL_PROV_LOW_TEMP_WARN 0xc424 +-#define VEND1_THERMAL_STAT1 0xc820 +-#define VEND1_THERMAL_STAT2 0xc821 +-#define VEND1_THERMAL_STAT2_VALID BIT(0) +-#define VEND1_GENERAL_STAT1 0xc830 +-#define VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL BIT(14) +-#define VEND1_GENERAL_STAT1_LOW_TEMP_FAIL BIT(13) +-#define VEND1_GENERAL_STAT1_HIGH_TEMP_WARN BIT(12) +-#define VEND1_GENERAL_STAT1_LOW_TEMP_WARN BIT(11) +- + #if IS_REACHABLE(CONFIG_HWMON) + + static umode_t aqr_hwmon_is_visible(const void *data, +--- a/drivers/net/phy/aquantia/aquantia_main.c ++++ b/drivers/net/phy/aquantia/aquantia_main.c +@@ -89,61 +89,6 @@ + #define MDIO_C22EXT_STAT_SGMII_TX_FRAME_ALIGN_ERR 0xd31a + #define MDIO_C22EXT_STAT_SGMII_TX_RUNT_FRAMES 0xd31b + +-/* Vendor specific 1, MDIO_MMD_VEND1 */ +-#define VEND1_GLOBAL_FW_ID 0x0020 +-#define VEND1_GLOBAL_FW_ID_MAJOR GENMASK(15, 8) +-#define VEND1_GLOBAL_FW_ID_MINOR GENMASK(7, 0) +- +-#define VEND1_GLOBAL_GEN_STAT2 0xc831 +-#define VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG BIT(15) +- +-/* The following registers all have similar layouts; first the registers... */ +-#define VEND1_GLOBAL_CFG_10M 0x0310 +-#define VEND1_GLOBAL_CFG_100M 0x031b +-#define VEND1_GLOBAL_CFG_1G 0x031c +-#define VEND1_GLOBAL_CFG_2_5G 0x031d +-#define VEND1_GLOBAL_CFG_5G 0x031e +-#define VEND1_GLOBAL_CFG_10G 0x031f +-/* ...and now the fields */ +-#define VEND1_GLOBAL_CFG_RATE_ADAPT GENMASK(8, 7) +-#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE 0 +-#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX 1 +-#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE 2 +- +-#define VEND1_GLOBAL_RSVD_STAT1 0xc885 +-#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID GENMASK(7, 4) +-#define VEND1_GLOBAL_RSVD_STAT1_PROV_ID GENMASK(3, 0) +- +-#define VEND1_GLOBAL_RSVD_STAT9 0xc88d +-#define VEND1_GLOBAL_RSVD_STAT9_MODE GENMASK(7, 0) +-#define VEND1_GLOBAL_RSVD_STAT9_1000BT2 0x23 +- +-#define VEND1_GLOBAL_INT_STD_STATUS 0xfc00 +-#define VEND1_GLOBAL_INT_VEND_STATUS 0xfc01 +- +-#define VEND1_GLOBAL_INT_STD_MASK 0xff00 +-#define VEND1_GLOBAL_INT_STD_MASK_PMA1 BIT(15) +-#define VEND1_GLOBAL_INT_STD_MASK_PMA2 BIT(14) +-#define VEND1_GLOBAL_INT_STD_MASK_PCS1 BIT(13) +-#define VEND1_GLOBAL_INT_STD_MASK_PCS2 BIT(12) +-#define VEND1_GLOBAL_INT_STD_MASK_PCS3 BIT(11) +-#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1 BIT(10) +-#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2 BIT(9) +-#define VEND1_GLOBAL_INT_STD_MASK_AN1 BIT(8) +-#define VEND1_GLOBAL_INT_STD_MASK_AN2 BIT(7) +-#define VEND1_GLOBAL_INT_STD_MASK_GBE BIT(6) +-#define VEND1_GLOBAL_INT_STD_MASK_ALL BIT(0) +- +-#define VEND1_GLOBAL_INT_VEND_MASK 0xff01 +-#define VEND1_GLOBAL_INT_VEND_MASK_PMA BIT(15) +-#define VEND1_GLOBAL_INT_VEND_MASK_PCS BIT(14) +-#define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS BIT(13) +-#define VEND1_GLOBAL_INT_VEND_MASK_AN BIT(12) +-#define VEND1_GLOBAL_INT_VEND_MASK_GBE BIT(11) +-#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1 BIT(2) +-#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2 BIT(1) +-#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 BIT(0) +- + /* Sleep and timeout for checking if the Processor-Intensive + * MDIO operation is finished + */ diff --git a/target/linux/generic/backport-6.6/702-03-v6.7-net-phy-aquantia-add-firmware-load-support.patch b/target/linux/generic/backport-6.6/702-03-v6.7-net-phy-aquantia-add-firmware-load-support.patch new file mode 100644 index 0000000000..aa52b3baa6 --- /dev/null +++ b/target/linux/generic/backport-6.6/702-03-v6.7-net-phy-aquantia-add-firmware-load-support.patch @@ -0,0 +1,504 @@ +From e93984ebc1c82bd34f7a1b3391efaceee0a8ae96 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Tue, 14 Nov 2023 15:08:43 +0100 +Subject: [PATCH 3/3] net: phy: aquantia: add firmware load support + +Aquantia PHY-s require firmware to be loaded before they start operating. +It can be automatically loaded in case when there is a SPI-NOR connected +to Aquantia PHY-s or can be loaded from the host via MDIO. + +This patch adds support for loading the firmware via MDIO as in most cases +there is no SPI-NOR being used to save on cost. +Firmware loading code itself is ported from mainline U-boot with cleanups. + +The firmware has mixed values both in big and little endian. +PHY core itself is big-endian but it expects values to be in little-endian. +The firmware is little-endian but CRC-16 value for it is stored at the end +of firmware in big-endian. + +It seems the PHY does the conversion internally from firmware that is +little-endian to the PHY that is big-endian on using the mailbox +but mailbox returns a big-endian CRC-16 to verify the written data +integrity. + +Co-developed-by: Christian Marangi +Signed-off-by: Robert Marko +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/aquantia/Kconfig | 1 + + drivers/net/phy/aquantia/Makefile | 2 +- + drivers/net/phy/aquantia/aquantia.h | 32 ++ + drivers/net/phy/aquantia/aquantia_firmware.c | 370 +++++++++++++++++++ + drivers/net/phy/aquantia/aquantia_main.c | 6 + + 5 files changed, 410 insertions(+), 1 deletion(-) + create mode 100644 drivers/net/phy/aquantia/aquantia_firmware.c + +--- a/drivers/net/phy/aquantia/Kconfig ++++ b/drivers/net/phy/aquantia/Kconfig +@@ -1,5 +1,6 @@ + # SPDX-License-Identifier: GPL-2.0-only + config AQUANTIA_PHY + tristate "Aquantia PHYs" ++ select CRC_CCITT + help + Currently supports the Aquantia AQ1202, AQ2104, AQR105, AQR405 +--- a/drivers/net/phy/aquantia/Makefile ++++ b/drivers/net/phy/aquantia/Makefile +@@ -1,5 +1,5 @@ + # SPDX-License-Identifier: GPL-2.0 +-aquantia-objs += aquantia_main.o ++aquantia-objs += aquantia_main.o aquantia_firmware.o + ifdef CONFIG_HWMON + aquantia-objs += aquantia_hwmon.o + endif +--- a/drivers/net/phy/aquantia/aquantia.h ++++ b/drivers/net/phy/aquantia/aquantia.h +@@ -10,10 +10,35 @@ + #include + + /* Vendor specific 1, MDIO_MMD_VEND1 */ ++#define VEND1_GLOBAL_SC 0x0 ++#define VEND1_GLOBAL_SC_SOFT_RESET BIT(15) ++#define VEND1_GLOBAL_SC_LOW_POWER BIT(11) ++ + #define VEND1_GLOBAL_FW_ID 0x0020 + #define VEND1_GLOBAL_FW_ID_MAJOR GENMASK(15, 8) + #define VEND1_GLOBAL_FW_ID_MINOR GENMASK(7, 0) + ++#define VEND1_GLOBAL_MAILBOX_INTERFACE1 0x0200 ++#define VEND1_GLOBAL_MAILBOX_INTERFACE1_EXECUTE BIT(15) ++#define VEND1_GLOBAL_MAILBOX_INTERFACE1_WRITE BIT(14) ++#define VEND1_GLOBAL_MAILBOX_INTERFACE1_CRC_RESET BIT(12) ++#define VEND1_GLOBAL_MAILBOX_INTERFACE1_BUSY BIT(8) ++ ++#define VEND1_GLOBAL_MAILBOX_INTERFACE2 0x0201 ++#define VEND1_GLOBAL_MAILBOX_INTERFACE3 0x0202 ++#define VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR_MASK GENMASK(15, 0) ++#define VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR(x) FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR_MASK, (u16)((x) >> 16)) ++#define VEND1_GLOBAL_MAILBOX_INTERFACE4 0x0203 ++#define VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR_MASK GENMASK(15, 2) ++#define VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR(x) FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR_MASK, (u16)(x)) ++ ++#define VEND1_GLOBAL_MAILBOX_INTERFACE5 0x0204 ++#define VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA_MASK GENMASK(15, 0) ++#define VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA(x) FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA_MASK, (u16)((x) >> 16)) ++#define VEND1_GLOBAL_MAILBOX_INTERFACE6 0x0205 ++#define VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA_MASK GENMASK(15, 0) ++#define VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA(x) FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA_MASK, (u16)(x)) ++ + /* The following registers all have similar layouts; first the registers... */ + #define VEND1_GLOBAL_CFG_10M 0x0310 + #define VEND1_GLOBAL_CFG_100M 0x031b +@@ -28,6 +53,11 @@ + #define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE 2 + + /* Vendor specific 1, MDIO_MMD_VEND2 */ ++#define VEND1_GLOBAL_CONTROL2 0xc001 ++#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_RST BIT(15) ++#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD BIT(6) ++#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL BIT(0) ++ + #define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL 0xc421 + #define VEND1_THERMAL_PROV_LOW_TEMP_FAIL 0xc422 + #define VEND1_THERMAL_PROV_HIGH_TEMP_WARN 0xc423 +@@ -83,3 +113,5 @@ int aqr_hwmon_probe(struct phy_device *p + #else + static inline int aqr_hwmon_probe(struct phy_device *phydev) { return 0; } + #endif ++ ++int aqr_firmware_load(struct phy_device *phydev); +--- /dev/null ++++ b/drivers/net/phy/aquantia/aquantia_firmware.c +@@ -0,0 +1,370 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "aquantia.h" ++ ++#define UP_RESET_SLEEP 100 ++ ++/* addresses of memory segments in the phy */ ++#define DRAM_BASE_ADDR 0x3FFE0000 ++#define IRAM_BASE_ADDR 0x40000000 ++ ++/* firmware image format constants */ ++#define VERSION_STRING_SIZE 0x40 ++#define VERSION_STRING_OFFSET 0x0200 ++/* primary offset is written at an offset from the start of the fw blob */ ++#define PRIMARY_OFFSET_OFFSET 0x8 ++/* primary offset needs to be then added to a base offset */ ++#define PRIMARY_OFFSET_SHIFT 12 ++#define PRIMARY_OFFSET(x) ((x) << PRIMARY_OFFSET_SHIFT) ++#define HEADER_OFFSET 0x300 ++ ++struct aqr_fw_header { ++ u32 padding; ++ u8 iram_offset[3]; ++ u8 iram_size[3]; ++ u8 dram_offset[3]; ++ u8 dram_size[3]; ++} __packed; ++ ++enum aqr_fw_src { ++ AQR_FW_SRC_NVMEM = 0, ++ AQR_FW_SRC_FS, ++}; ++ ++static const char * const aqr_fw_src_string[] = { ++ [AQR_FW_SRC_NVMEM] = "NVMEM", ++ [AQR_FW_SRC_FS] = "FS", ++}; ++ ++/* AQR firmware doesn't have fixed offsets for iram and dram section ++ * but instead provide an header with the offset to use on reading ++ * and parsing the firmware. ++ * ++ * AQR firmware can't be trusted and each offset is validated to be ++ * not negative and be in the size of the firmware itself. ++ */ ++static bool aqr_fw_validate_get(size_t size, size_t offset, size_t get_size) ++{ ++ return offset + get_size <= size; ++} ++ ++static int aqr_fw_get_be16(const u8 *data, size_t offset, size_t size, u16 *value) ++{ ++ if (!aqr_fw_validate_get(size, offset, sizeof(u16))) ++ return -EINVAL; ++ ++ *value = get_unaligned_be16(data + offset); ++ ++ return 0; ++} ++ ++static int aqr_fw_get_le16(const u8 *data, size_t offset, size_t size, u16 *value) ++{ ++ if (!aqr_fw_validate_get(size, offset, sizeof(u16))) ++ return -EINVAL; ++ ++ *value = get_unaligned_le16(data + offset); ++ ++ return 0; ++} ++ ++static int aqr_fw_get_le24(const u8 *data, size_t offset, size_t size, u32 *value) ++{ ++ if (!aqr_fw_validate_get(size, offset, sizeof(u8) * 3)) ++ return -EINVAL; ++ ++ *value = get_unaligned_le24(data + offset); ++ ++ return 0; ++} ++ ++/* load data into the phy's memory */ ++static int aqr_fw_load_memory(struct phy_device *phydev, u32 addr, ++ const u8 *data, size_t len) ++{ ++ u16 crc = 0, up_crc; ++ size_t pos; ++ ++ /* PHY expect addr in LE */ ++ addr = (__force u32)cpu_to_le32(addr); ++ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLOBAL_MAILBOX_INTERFACE1, ++ VEND1_GLOBAL_MAILBOX_INTERFACE1_CRC_RESET); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLOBAL_MAILBOX_INTERFACE3, ++ VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR(addr)); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLOBAL_MAILBOX_INTERFACE4, ++ VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR(addr)); ++ ++ /* We assume and enforce the size to be word aligned. ++ * If a firmware that is not word aligned is found, please report upstream. ++ */ ++ for (pos = 0; pos < len; pos += sizeof(u32)) { ++ u32 word; ++ ++ /* FW data is always stored in little-endian */ ++ word = get_unaligned((const u32 *)(data + pos)); ++ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE5, ++ VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA(word)); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE6, ++ VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA(word)); ++ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE1, ++ VEND1_GLOBAL_MAILBOX_INTERFACE1_EXECUTE | ++ VEND1_GLOBAL_MAILBOX_INTERFACE1_WRITE); ++ ++ /* calculate CRC as we load data to the mailbox. ++ * We convert word to big-endian as PHY is BE and mailbox will ++ * return a BE CRC. ++ */ ++ word = (__force u32)cpu_to_be32(word); ++ crc = crc_ccitt_false(crc, (u8 *)&word, sizeof(word)); ++ } ++ ++ up_crc = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE2); ++ if (crc != up_crc) { ++ phydev_err(phydev, "CRC mismatch: calculated 0x%04x PHY 0x%04x\n", ++ crc, up_crc); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int aqr_fw_boot(struct phy_device *phydev, const u8 *data, size_t size, ++ enum aqr_fw_src fw_src) ++{ ++ u16 calculated_crc, read_crc, read_primary_offset; ++ u32 iram_offset = 0, iram_size = 0; ++ u32 dram_offset = 0, dram_size = 0; ++ char version[VERSION_STRING_SIZE]; ++ u32 primary_offset = 0; ++ int ret; ++ ++ /* extract saved CRC at the end of the fw ++ * CRC is saved in big-endian as PHY is BE ++ */ ++ ret = aqr_fw_get_be16(data, size - sizeof(u16), size, &read_crc); ++ if (ret) { ++ phydev_err(phydev, "bad firmware CRC in firmware\n"); ++ return ret; ++ } ++ calculated_crc = crc_ccitt_false(0, data, size - sizeof(u16)); ++ if (read_crc != calculated_crc) { ++ phydev_err(phydev, "bad firmware CRC: file 0x%04x calculated 0x%04x\n", ++ read_crc, calculated_crc); ++ return -EINVAL; ++ } ++ ++ /* Get the primary offset to extract DRAM and IRAM sections. */ ++ ret = aqr_fw_get_le16(data, PRIMARY_OFFSET_OFFSET, size, &read_primary_offset); ++ if (ret) { ++ phydev_err(phydev, "bad primary offset in firmware\n"); ++ return ret; ++ } ++ primary_offset = PRIMARY_OFFSET(read_primary_offset); ++ ++ /* Find the DRAM and IRAM sections within the firmware file. ++ * Make sure the fw_header is correctly in the firmware. ++ */ ++ if (!aqr_fw_validate_get(size, primary_offset + HEADER_OFFSET, ++ sizeof(struct aqr_fw_header))) { ++ phydev_err(phydev, "bad fw_header in firmware\n"); ++ return -EINVAL; ++ } ++ ++ /* offset are in LE and values needs to be converted to cpu endian */ ++ ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET + ++ offsetof(struct aqr_fw_header, iram_offset), ++ size, &iram_offset); ++ if (ret) { ++ phydev_err(phydev, "bad iram offset in firmware\n"); ++ return ret; ++ } ++ ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET + ++ offsetof(struct aqr_fw_header, iram_size), ++ size, &iram_size); ++ if (ret) { ++ phydev_err(phydev, "invalid iram size in firmware\n"); ++ return ret; ++ } ++ ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET + ++ offsetof(struct aqr_fw_header, dram_offset), ++ size, &dram_offset); ++ if (ret) { ++ phydev_err(phydev, "bad dram offset in firmware\n"); ++ return ret; ++ } ++ ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET + ++ offsetof(struct aqr_fw_header, dram_size), ++ size, &dram_size); ++ if (ret) { ++ phydev_err(phydev, "invalid dram size in firmware\n"); ++ return ret; ++ } ++ ++ /* Increment the offset with the primary offset. ++ * Validate iram/dram offset and size. ++ */ ++ iram_offset += primary_offset; ++ if (iram_size % sizeof(u32)) { ++ phydev_err(phydev, "iram size if not aligned to word size. Please report this upstream!\n"); ++ return -EINVAL; ++ } ++ if (!aqr_fw_validate_get(size, iram_offset, iram_size)) { ++ phydev_err(phydev, "invalid iram offset for iram size\n"); ++ return -EINVAL; ++ } ++ ++ dram_offset += primary_offset; ++ if (dram_size % sizeof(u32)) { ++ phydev_err(phydev, "dram size if not aligned to word size. Please report this upstream!\n"); ++ return -EINVAL; ++ } ++ if (!aqr_fw_validate_get(size, dram_offset, dram_size)) { ++ phydev_err(phydev, "invalid iram offset for iram size\n"); ++ return -EINVAL; ++ } ++ ++ phydev_dbg(phydev, "primary %d IRAM offset=%d size=%d DRAM offset=%d size=%d\n", ++ primary_offset, iram_offset, iram_size, dram_offset, dram_size); ++ ++ if (!aqr_fw_validate_get(size, dram_offset + VERSION_STRING_OFFSET, ++ VERSION_STRING_SIZE)) { ++ phydev_err(phydev, "invalid version in firmware\n"); ++ return -EINVAL; ++ } ++ strscpy(version, (char *)data + dram_offset + VERSION_STRING_OFFSET, ++ VERSION_STRING_SIZE); ++ if (version[0] == '\0') { ++ phydev_err(phydev, "invalid version in firmware\n"); ++ return -EINVAL; ++ } ++ phydev_info(phydev, "loading firmware version '%s' from '%s'\n", version, ++ aqr_fw_src_string[fw_src]); ++ ++ /* stall the microcprocessor */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2, ++ VEND1_GLOBAL_CONTROL2_UP_RUN_STALL | VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD); ++ ++ phydev_dbg(phydev, "loading DRAM 0x%08x from offset=%d size=%d\n", ++ DRAM_BASE_ADDR, dram_offset, dram_size); ++ ret = aqr_fw_load_memory(phydev, DRAM_BASE_ADDR, data + dram_offset, ++ dram_size); ++ if (ret) ++ return ret; ++ ++ phydev_dbg(phydev, "loading IRAM 0x%08x from offset=%d size=%d\n", ++ IRAM_BASE_ADDR, iram_offset, iram_size); ++ ret = aqr_fw_load_memory(phydev, IRAM_BASE_ADDR, data + iram_offset, ++ iram_size); ++ if (ret) ++ return ret; ++ ++ /* make sure soft reset and low power mode are clear */ ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_SC, ++ VEND1_GLOBAL_SC_SOFT_RESET | VEND1_GLOBAL_SC_LOW_POWER); ++ ++ /* Release the microprocessor. UP_RESET must be held for 100 usec. */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2, ++ VEND1_GLOBAL_CONTROL2_UP_RUN_STALL | ++ VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD | ++ VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_RST); ++ usleep_range(UP_RESET_SLEEP, UP_RESET_SLEEP * 2); ++ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2, ++ VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD); ++ ++ return 0; ++} ++ ++static int aqr_firmware_load_nvmem(struct phy_device *phydev) ++{ ++ struct nvmem_cell *cell; ++ size_t size; ++ u8 *buf; ++ int ret; ++ ++ cell = nvmem_cell_get(&phydev->mdio.dev, "firmware"); ++ if (IS_ERR(cell)) ++ return PTR_ERR(cell); ++ ++ buf = nvmem_cell_read(cell, &size); ++ if (IS_ERR(buf)) { ++ ret = PTR_ERR(buf); ++ goto exit; ++ } ++ ++ ret = aqr_fw_boot(phydev, buf, size, AQR_FW_SRC_NVMEM); ++ if (ret) ++ phydev_err(phydev, "firmware loading failed: %d\n", ret); ++ ++ kfree(buf); ++exit: ++ nvmem_cell_put(cell); ++ ++ return ret; ++} ++ ++static int aqr_firmware_load_fs(struct phy_device *phydev) ++{ ++ struct device *dev = &phydev->mdio.dev; ++ const struct firmware *fw; ++ const char *fw_name; ++ int ret; ++ ++ ret = of_property_read_string(dev->of_node, "firmware-name", ++ &fw_name); ++ if (ret) ++ return ret; ++ ++ ret = request_firmware(&fw, fw_name, dev); ++ if (ret) { ++ phydev_err(phydev, "failed to find FW file %s (%d)\n", ++ fw_name, ret); ++ return ret; ++ } ++ ++ ret = aqr_fw_boot(phydev, fw->data, fw->size, AQR_FW_SRC_FS); ++ if (ret) ++ phydev_err(phydev, "firmware loading failed: %d\n", ret); ++ ++ release_firmware(fw); ++ ++ return ret; ++} ++ ++int aqr_firmware_load(struct phy_device *phydev) ++{ ++ int ret; ++ ++ /* Check if the firmware is not already loaded by pooling ++ * the current version returned by the PHY. If 0 is returned, ++ * no firmware is loaded. ++ */ ++ ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_FW_ID); ++ if (ret > 0) ++ goto exit; ++ ++ ret = aqr_firmware_load_nvmem(phydev); ++ if (!ret) ++ goto exit; ++ ++ ret = aqr_firmware_load_fs(phydev); ++ if (ret) ++ return ret; ++ ++exit: ++ return 0; ++} +--- a/drivers/net/phy/aquantia/aquantia_main.c ++++ b/drivers/net/phy/aquantia/aquantia_main.c +@@ -656,11 +656,17 @@ static int aqr107_resume(struct phy_devi + + static int aqr107_probe(struct phy_device *phydev) + { ++ int ret; ++ + phydev->priv = devm_kzalloc(&phydev->mdio.dev, + sizeof(struct aqr107_priv), GFP_KERNEL); + if (!phydev->priv) + return -ENOMEM; + ++ ret = aqr_firmware_load(phydev); ++ if (ret) ++ return ret; ++ + return aqr_hwmon_probe(phydev); + } + diff --git a/target/linux/generic/backport-6.6/703-v6.3-net-mdio-Add-dedicated-C45-API-to-MDIO-bus-drivers.patch b/target/linux/generic/backport-6.6/703-v6.3-net-mdio-Add-dedicated-C45-API-to-MDIO-bus-drivers.patch new file mode 100644 index 0000000000..ff186742ba --- /dev/null +++ b/target/linux/generic/backport-6.6/703-v6.3-net-mdio-Add-dedicated-C45-API-to-MDIO-bus-drivers.patch @@ -0,0 +1,326 @@ +From 4e4aafcddbbfcdd6eed5780e190fcbfac8b4685a Mon Sep 17 00:00:00 2001 +From: Andrew Lunn +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 +Signed-off-by: Michael Walle +Signed-off-by: Jakub Kicinski +--- + 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); + diff --git a/target/linux/generic/backport-6.6/704-v6.6-net-phy-Introduce-PSGMII-PHY-interface-mode.patch b/target/linux/generic/backport-6.6/704-v6.6-net-phy-Introduce-PSGMII-PHY-interface-mode.patch new file mode 100644 index 0000000000..80210e6da1 --- /dev/null +++ b/target/linux/generic/backport-6.6/704-v6.6-net-phy-Introduce-PSGMII-PHY-interface-mode.patch @@ -0,0 +1,105 @@ +From 9a0e95e34e9c0a713ddfd48c3a88a20d2bdfd514 Mon Sep 17 00:00:00 2001 +From: Gabor Juhos +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 +Signed-off-by: Robert Marko +Signed-off-by: David S. Miller +--- + 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: diff --git a/target/linux/generic/backport-6.6/705-v6.4-net-phy-at803x-Replace-of_gpio.h-with-what-indeed-is.patch b/target/linux/generic/backport-6.6/705-v6.4-net-phy-at803x-Replace-of_gpio.h-with-what-indeed-is.patch new file mode 100644 index 0000000000..7cacb71b18 --- /dev/null +++ b/target/linux/generic/backport-6.6/705-v6.4-net-phy-at803x-Replace-of_gpio.h-with-what-indeed-is.patch @@ -0,0 +1,32 @@ +From a593a2fcfdfb92cfd0ffc54bc81b07e6bfaaaf46 Mon Sep 17 00:00:00 2001 +From: Andy Shevchenko +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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 + #include + #include +-#include + #include +-#include + #include + #include + #include ++#include + #include + #include + #include diff --git a/target/linux/generic/backport-6.6/706-v6.6-01-net-phy-at803x-support-qca8081-genphy_c45_pma_read_a.patch b/target/linux/generic/backport-6.6/706-v6.6-01-net-phy-at803x-support-qca8081-genphy_c45_pma_read_a.patch new file mode 100644 index 0000000000..dab4928960 --- /dev/null +++ b/target/linux/generic/backport-6.6/706-v6.6-01-net-phy-at803x-support-qca8081-genphy_c45_pma_read_a.patch @@ -0,0 +1,70 @@ +From 8b8bc13d89a7d23d14b0e041c73f037c9db997b1 Mon Sep 17 00:00:00 2001 +From: Luo Jie +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 +Reviewed-by: Russell King (Oracle) +Signed-off-by: David S. Miller +--- + 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, diff --git a/target/linux/generic/backport-6.6/706-v6.6-02-net-phy-at803x-merge-qca8081-slave-seed-function.patch b/target/linux/generic/backport-6.6/706-v6.6-02-net-phy-at803x-merge-qca8081-slave-seed-function.patch new file mode 100644 index 0000000000..28874068ce --- /dev/null +++ b/target/linux/generic/backport-6.6/706-v6.6-02-net-phy-at803x-merge-qca8081-slave-seed-function.patch @@ -0,0 +1,73 @@ +From f3db55ae860a82e1224a909072783ef850e5d228 Mon Sep 17 00:00:00 2001 +From: Luo Jie +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 +Reviewed-by: Russell King (Oracle) +Signed-off-by: David S. Miller +--- + 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); + } + } diff --git a/target/linux/generic/backport-6.6/706-v6.6-03-net-phy-at803x-enable-qca8081-slave-seed-conditional.patch b/target/linux/generic/backport-6.6/706-v6.6-03-net-phy-at803x-enable-qca8081-slave-seed-conditional.patch new file mode 100644 index 0000000000..b91d2cab30 --- /dev/null +++ b/target/linux/generic/backport-6.6/706-v6.6-03-net-phy-at803x-enable-qca8081-slave-seed-conditional.patch @@ -0,0 +1,76 @@ +From 7cc3209558002d95c0d45a1276ba4f5f741eec42 Mon Sep 17 00:00:00 2001 +From: Luo Jie +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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); diff --git a/target/linux/generic/backport-6.6/706-v6.6-04-net-phy-at803x-support-qca8081-1G-chip-type.patch b/target/linux/generic/backport-6.6/706-v6.6-04-net-phy-at803x-support-qca8081-1G-chip-type.patch new file mode 100644 index 0000000000..b8db89d853 --- /dev/null +++ b/target/linux/generic/backport-6.6/706-v6.6-04-net-phy-at803x-support-qca8081-1G-chip-type.patch @@ -0,0 +1,48 @@ +From fea7cfb83d1a2782e39cd101dd44ed2548539de5 Mon Sep 17 00:00:00 2001 +From: Luo Jie +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 +Reviewed-by: Russell King (Oracle) +Signed-off-by: David S. Miller +--- + 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; + } + diff --git a/target/linux/generic/backport-6.6/706-v6.6-05-net-phy-at803x-remove-qca8081-1G-fast-retrain-and-sl.patch b/target/linux/generic/backport-6.6/706-v6.6-05-net-phy-at803x-remove-qca8081-1G-fast-retrain-and-sl.patch new file mode 100644 index 0000000000..b54a86e5f5 --- /dev/null +++ b/target/linux/generic/backport-6.6/706-v6.6-05-net-phy-at803x-remove-qca8081-1G-fast-retrain-and-sl.patch @@ -0,0 +1,98 @@ +From df9401ff3e6eeaa42bfb06761967f1b71f5afce7 Mon Sep 17 00:00:00 2001 +From: Luo Jie +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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) diff --git a/target/linux/generic/backport-6.6/706-v6.6-06-net-phy-at803x-add-qca8081-fifo-reset-on-the-link-ch.patch b/target/linux/generic/backport-6.6/706-v6.6-06-net-phy-at803x-add-qca8081-fifo-reset-on-the-link-ch.patch new file mode 100644 index 0000000000..e382cf7876 --- /dev/null +++ b/target/linux/generic/backport-6.6/706-v6.6-06-net-phy-at803x-add-qca8081-fifo-reset-on-the-link-ch.patch @@ -0,0 +1,55 @@ +From 723970affdd8766fa0d91cd34bf2ffc861538b5f Mon Sep 17 00:00:00 2001 +From: Luo Jie +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 +Reviewed-by: Andrew Lunn +Reviewed-by: Russell King (Oracle) +Signed-off-by: David S. Miller +--- + 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); diff --git a/target/linux/generic/backport-6.6/707-v6.8-02-net-phy-at803x-move-disable-WOL-to-specific-at8031-p.patch b/target/linux/generic/backport-6.6/707-v6.8-02-net-phy-at803x-move-disable-WOL-to-specific-at8031-p.patch new file mode 100644 index 0000000000..eb9172b1cc --- /dev/null +++ b/target/linux/generic/backport-6.6/707-v6.8-02-net-phy-at803x-move-disable-WOL-to-specific-at8031-p.patch @@ -0,0 +1,69 @@ +From 6a3b8c573b5a152a6aa7a0b54c5e18b84c6ba6f5 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 8 Dec 2023 15:51:49 +0100 +Subject: [PATCH 02/13] net: phy: at803x: move disable WOL to specific at8031 + probe + +Move the WOL disable call to specific at8031 probe to make at803x_probe +more generic and drop extra check for PHY ID. + +Keep the same previous behaviour by first calling at803x_probe and then +disabling WOL. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 27 +++++++++++++++++---------- + 1 file changed, 17 insertions(+), 10 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -886,15 +886,6 @@ static int at803x_probe(struct phy_devic + priv->is_fiber = true; + break; + } +- +- /* Disable WoL in 1588 register which is enabled +- * by default +- */ +- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, +- AT803X_PHY_MMD3_WOL_CTRL, +- AT803X_WOL_EN, 0); +- if (ret) +- return ret; + } + + return 0; +@@ -1591,6 +1582,22 @@ static int at803x_cable_test_start(struc + return 0; + } + ++static int at8031_probe(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = at803x_probe(phydev); ++ if (ret) ++ return ret; ++ ++ /* Disable WoL in 1588 register which is enabled ++ * by default ++ */ ++ return phy_modify_mmd(phydev, MDIO_MMD_PCS, ++ AT803X_PHY_MMD3_WOL_CTRL, ++ AT803X_WOL_EN, 0); ++} ++ + static int qca83xx_config_init(struct phy_device *phydev) + { + u8 switch_revision; +@@ -2092,7 +2099,7 @@ static struct phy_driver at803x_driver[] + PHY_ID_MATCH_EXACT(ATH8031_PHY_ID), + .name = "Qualcomm Atheros AR8031/AR8033", + .flags = PHY_POLL_CABLE_TEST, +- .probe = at803x_probe, ++ .probe = at8031_probe, + .config_init = at803x_config_init, + .config_aneg = at803x_config_aneg, + .soft_reset = genphy_soft_reset, diff --git a/target/linux/generic/backport-6.6/707-v6.8-03-net-phy-at803x-raname-hw_stats-functions-to-qca83xx-.patch b/target/linux/generic/backport-6.6/707-v6.8-03-net-phy-at803x-raname-hw_stats-functions-to-qca83xx-.patch new file mode 100644 index 0000000000..5a9d1764f1 --- /dev/null +++ b/target/linux/generic/backport-6.6/707-v6.8-03-net-phy-at803x-raname-hw_stats-functions-to-qca83xx-.patch @@ -0,0 +1,129 @@ +From 07b1ad83b9ed6db1735ba10baf67b7a565ac0cef Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 8 Dec 2023 15:51:50 +0100 +Subject: [PATCH 03/13] net: phy: at803x: raname hw_stats functions to qca83xx + specific name + +The function and the struct related to hw_stats were specific to qca83xx +PHY but were called following the convention in the driver of calling +everything with at803x prefix. + +To better organize the code, rename these function a more specific name +to better describe that they are specific to 83xx PHY family. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 44 ++++++++++++++++++++-------------------- + 1 file changed, 22 insertions(+), 22 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -295,7 +295,7 @@ struct at803x_hw_stat { + enum stat_access_type access_type; + }; + +-static struct at803x_hw_stat at803x_hw_stats[] = { ++static struct at803x_hw_stat qca83xx_hw_stats[] = { + { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY}, + { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY}, + { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD}, +@@ -311,7 +311,7 @@ struct at803x_priv { + bool is_1000basex; + struct regulator_dev *vddio_rdev; + struct regulator_dev *vddh_rdev; +- u64 stats[ARRAY_SIZE(at803x_hw_stats)]; ++ u64 stats[ARRAY_SIZE(qca83xx_hw_stats)]; + }; + + struct at803x_context { +@@ -529,24 +529,24 @@ static void at803x_get_wol(struct phy_de + wol->wolopts |= WAKE_MAGIC; + } + +-static int at803x_get_sset_count(struct phy_device *phydev) ++static int qca83xx_get_sset_count(struct phy_device *phydev) + { +- return ARRAY_SIZE(at803x_hw_stats); ++ return ARRAY_SIZE(qca83xx_hw_stats); + } + +-static void at803x_get_strings(struct phy_device *phydev, u8 *data) ++static void qca83xx_get_strings(struct phy_device *phydev, u8 *data) + { + int i; + +- for (i = 0; i < ARRAY_SIZE(at803x_hw_stats); i++) { ++ for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) { + strscpy(data + i * ETH_GSTRING_LEN, +- at803x_hw_stats[i].string, ETH_GSTRING_LEN); ++ qca83xx_hw_stats[i].string, ETH_GSTRING_LEN); + } + } + +-static u64 at803x_get_stat(struct phy_device *phydev, int i) ++static u64 qca83xx_get_stat(struct phy_device *phydev, int i) + { +- struct at803x_hw_stat stat = at803x_hw_stats[i]; ++ struct at803x_hw_stat stat = qca83xx_hw_stats[i]; + struct at803x_priv *priv = phydev->priv; + int val; + u64 ret; +@@ -567,13 +567,13 @@ static u64 at803x_get_stat(struct phy_de + return ret; + } + +-static void at803x_get_stats(struct phy_device *phydev, +- struct ethtool_stats *stats, u64 *data) ++static void qca83xx_get_stats(struct phy_device *phydev, ++ struct ethtool_stats *stats, u64 *data) + { + int i; + +- for (i = 0; i < ARRAY_SIZE(at803x_hw_stats); i++) +- data[i] = at803x_get_stat(phydev, i); ++ for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) ++ data[i] = qca83xx_get_stat(phydev, i); + } + + static int at803x_suspend(struct phy_device *phydev) +@@ -2175,9 +2175,9 @@ static struct phy_driver at803x_driver[] + .flags = PHY_IS_INTERNAL, + .config_init = qca83xx_config_init, + .soft_reset = genphy_soft_reset, +- .get_sset_count = at803x_get_sset_count, +- .get_strings = at803x_get_strings, +- .get_stats = at803x_get_stats, ++ .get_sset_count = qca83xx_get_sset_count, ++ .get_strings = qca83xx_get_strings, ++ .get_stats = qca83xx_get_stats, + .suspend = qca83xx_suspend, + .resume = qca83xx_resume, + }, { +@@ -2191,9 +2191,9 @@ static struct phy_driver at803x_driver[] + .flags = PHY_IS_INTERNAL, + .config_init = qca83xx_config_init, + .soft_reset = genphy_soft_reset, +- .get_sset_count = at803x_get_sset_count, +- .get_strings = at803x_get_strings, +- .get_stats = at803x_get_stats, ++ .get_sset_count = qca83xx_get_sset_count, ++ .get_strings = qca83xx_get_strings, ++ .get_stats = qca83xx_get_stats, + .suspend = qca83xx_suspend, + .resume = qca83xx_resume, + }, { +@@ -2207,9 +2207,9 @@ static struct phy_driver at803x_driver[] + .flags = PHY_IS_INTERNAL, + .config_init = qca83xx_config_init, + .soft_reset = genphy_soft_reset, +- .get_sset_count = at803x_get_sset_count, +- .get_strings = at803x_get_strings, +- .get_stats = at803x_get_stats, ++ .get_sset_count = qca83xx_get_sset_count, ++ .get_strings = qca83xx_get_strings, ++ .get_stats = qca83xx_get_stats, + .suspend = qca83xx_suspend, + .resume = qca83xx_resume, + }, { diff --git a/target/linux/generic/backport-6.6/707-v6.8-04-net-phy-at803x-move-qca83xx-specific-check-in-dedica.patch b/target/linux/generic/backport-6.6/707-v6.8-04-net-phy-at803x-move-qca83xx-specific-check-in-dedica.patch new file mode 100644 index 0000000000..f09142c5ed --- /dev/null +++ b/target/linux/generic/backport-6.6/707-v6.8-04-net-phy-at803x-move-qca83xx-specific-check-in-dedica.patch @@ -0,0 +1,155 @@ +From d43cff3f82336c0bd965ea552232d9f4ddac71a6 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 8 Dec 2023 15:51:51 +0100 +Subject: [PATCH 04/13] net: phy: at803x: move qca83xx specific check in + dedicated functions + +Rework qca83xx specific check to dedicated function to tidy things up +and drop useless phy_id check. + +Also drop an useless link_change_notify for QCA8337 as it did nothing an +returned early. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 68 ++++++++++++++++++++++------------------ + 1 file changed, 37 insertions(+), 31 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -1623,27 +1623,26 @@ static int qca83xx_config_init(struct ph + break; + } + ++ /* Following original QCA sourcecode set port to prefer master */ ++ phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER); ++ ++ return 0; ++} ++ ++static int qca8327_config_init(struct phy_device *phydev) ++{ + /* QCA8327 require DAC amplitude adjustment for 100m set to +6%. + * Disable on init and enable only with 100m speed following + * qca original source code. + */ +- if (phydev->drv->phy_id == QCA8327_A_PHY_ID || +- phydev->drv->phy_id == QCA8327_B_PHY_ID) +- at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, +- QCA8327_DEBUG_MANU_CTRL_EN, 0); ++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, ++ QCA8327_DEBUG_MANU_CTRL_EN, 0); + +- /* Following original QCA sourcecode set port to prefer master */ +- phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER); +- +- return 0; ++ return qca83xx_config_init(phydev); + } + + static void qca83xx_link_change_notify(struct phy_device *phydev) + { +- /* QCA8337 doesn't require DAC Amplitude adjustement */ +- if (phydev->drv->phy_id == QCA8337_PHY_ID) +- return; +- + /* Set DAC Amplitude adjustment to +6% for 100m on link running */ + if (phydev->state == PHY_RUNNING) { + if (phydev->speed == SPEED_100) +@@ -1686,19 +1685,6 @@ static int qca83xx_resume(struct phy_dev + + static int qca83xx_suspend(struct phy_device *phydev) + { +- u16 mask = 0; +- +- /* Only QCA8337 support actual suspend. +- * QCA8327 cause port unreliability when phy suspend +- * is set. +- */ +- if (phydev->drv->phy_id == QCA8337_PHY_ID) { +- genphy_suspend(phydev); +- } else { +- mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX); +- phy_modify(phydev, MII_BMCR, mask, 0); +- } +- + at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_GREEN, + AT803X_DEBUG_GATE_CLK_IN1000, 0); + +@@ -1709,6 +1695,27 @@ static int qca83xx_suspend(struct phy_de + return 0; + } + ++static int qca8337_suspend(struct phy_device *phydev) ++{ ++ /* Only QCA8337 support actual suspend. */ ++ genphy_suspend(phydev); ++ ++ return qca83xx_suspend(phydev); ++} ++ ++static int qca8327_suspend(struct phy_device *phydev) ++{ ++ u16 mask = 0; ++ ++ /* QCA8327 cause port unreliability when phy suspend ++ * is set. ++ */ ++ mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX); ++ phy_modify(phydev, MII_BMCR, mask, 0); ++ ++ return qca83xx_suspend(phydev); ++} ++ + static int qca808x_phy_fast_retrain_config(struct phy_device *phydev) + { + int ret; +@@ -2170,7 +2177,6 @@ static struct phy_driver at803x_driver[] + .phy_id_mask = QCA8K_PHY_ID_MASK, + .name = "Qualcomm Atheros 8337 internal PHY", + /* PHY_GBIT_FEATURES */ +- .link_change_notify = qca83xx_link_change_notify, + .probe = at803x_probe, + .flags = PHY_IS_INTERNAL, + .config_init = qca83xx_config_init, +@@ -2178,7 +2184,7 @@ static struct phy_driver at803x_driver[] + .get_sset_count = qca83xx_get_sset_count, + .get_strings = qca83xx_get_strings, + .get_stats = qca83xx_get_stats, +- .suspend = qca83xx_suspend, ++ .suspend = qca8337_suspend, + .resume = qca83xx_resume, + }, { + /* QCA8327-A from switch QCA8327-AL1A */ +@@ -2189,12 +2195,12 @@ static struct phy_driver at803x_driver[] + .link_change_notify = qca83xx_link_change_notify, + .probe = at803x_probe, + .flags = PHY_IS_INTERNAL, +- .config_init = qca83xx_config_init, ++ .config_init = qca8327_config_init, + .soft_reset = genphy_soft_reset, + .get_sset_count = qca83xx_get_sset_count, + .get_strings = qca83xx_get_strings, + .get_stats = qca83xx_get_stats, +- .suspend = qca83xx_suspend, ++ .suspend = qca8327_suspend, + .resume = qca83xx_resume, + }, { + /* QCA8327-B from switch QCA8327-BL1A */ +@@ -2205,12 +2211,12 @@ static struct phy_driver at803x_driver[] + .link_change_notify = qca83xx_link_change_notify, + .probe = at803x_probe, + .flags = PHY_IS_INTERNAL, +- .config_init = qca83xx_config_init, ++ .config_init = qca8327_config_init, + .soft_reset = genphy_soft_reset, + .get_sset_count = qca83xx_get_sset_count, + .get_strings = qca83xx_get_strings, + .get_stats = qca83xx_get_stats, +- .suspend = qca83xx_suspend, ++ .suspend = qca8327_suspend, + .resume = qca83xx_resume, + }, { + /* Qualcomm QCA8081 */ diff --git a/target/linux/generic/backport-6.6/707-v6.8-05-net-phy-at803x-move-specific-DT-option-for-at8031-to.patch b/target/linux/generic/backport-6.6/707-v6.8-05-net-phy-at803x-move-specific-DT-option-for-at8031-to.patch new file mode 100644 index 0000000000..a5cc67a8c8 --- /dev/null +++ b/target/linux/generic/backport-6.6/707-v6.8-05-net-phy-at803x-move-specific-DT-option-for-at8031-to.patch @@ -0,0 +1,94 @@ +From 900eef75cc5018e149c52fe305c9c3fe424c52a7 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 8 Dec 2023 15:51:52 +0100 +Subject: [PATCH 05/13] net: phy: at803x: move specific DT option for at8031 to + specific probe + +Move specific DT options for at8031 to specific probe to tidy things up +and make at803x_parse_dt more generic. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 55 ++++++++++++++++++++++------------------ + 1 file changed, 31 insertions(+), 24 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -825,30 +825,6 @@ static int at803x_parse_dt(struct phy_de + } + } + +- /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping +- * options. +- */ +- if (phydev->drv->phy_id == ATH8031_PHY_ID) { +- if (of_property_read_bool(node, "qca,keep-pll-enabled")) +- priv->flags |= AT803X_KEEP_PLL_ENABLED; +- +- ret = at8031_register_regulators(phydev); +- if (ret < 0) +- return ret; +- +- ret = devm_regulator_get_enable_optional(&phydev->mdio.dev, +- "vddio"); +- if (ret) { +- phydev_err(phydev, "failed to get VDDIO regulator\n"); +- return ret; +- } +- +- /* Only AR8031/8033 support 1000Base-X for SFP modules */ +- ret = phy_sfp_probe(phydev, &at803x_sfp_ops); +- if (ret < 0) +- return ret; +- } +- + return 0; + } + +@@ -1582,6 +1558,30 @@ static int at803x_cable_test_start(struc + return 0; + } + ++static int at8031_parse_dt(struct phy_device *phydev) ++{ ++ struct device_node *node = phydev->mdio.dev.of_node; ++ struct at803x_priv *priv = phydev->priv; ++ int ret; ++ ++ if (of_property_read_bool(node, "qca,keep-pll-enabled")) ++ priv->flags |= AT803X_KEEP_PLL_ENABLED; ++ ++ ret = at8031_register_regulators(phydev); ++ if (ret < 0) ++ return ret; ++ ++ ret = devm_regulator_get_enable_optional(&phydev->mdio.dev, ++ "vddio"); ++ if (ret) { ++ phydev_err(phydev, "failed to get VDDIO regulator\n"); ++ return ret; ++ } ++ ++ /* Only AR8031/8033 support 1000Base-X for SFP modules */ ++ return phy_sfp_probe(phydev, &at803x_sfp_ops); ++} ++ + static int at8031_probe(struct phy_device *phydev) + { + int ret; +@@ -1590,6 +1590,13 @@ static int at8031_probe(struct phy_devic + if (ret) + return ret; + ++ /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping ++ * options. ++ */ ++ ret = at8031_parse_dt(phydev); ++ if (ret) ++ return ret; ++ + /* Disable WoL in 1588 register which is enabled + * by default + */ diff --git a/target/linux/generic/backport-6.6/707-v6.8-06-net-phy-at803x-move-specific-at8031-probe-mode-check.patch b/target/linux/generic/backport-6.6/707-v6.8-06-net-phy-at803x-move-specific-at8031-probe-mode-check.patch new file mode 100644 index 0000000000..9e10e000e5 --- /dev/null +++ b/target/linux/generic/backport-6.6/707-v6.8-06-net-phy-at803x-move-specific-at8031-probe-mode-check.patch @@ -0,0 +1,78 @@ +From 25d2ba94005fac18fe68878cddff59a67e115554 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 8 Dec 2023 15:51:53 +0100 +Subject: [PATCH 06/13] net: phy: at803x: move specific at8031 probe mode check + to dedicated probe + +Move specific at8031 probe mode check to dedicated probe to make +at803x_probe more generic and keep code tidy. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 39 +++++++++++++++++++-------------------- + 1 file changed, 19 insertions(+), 20 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -844,26 +844,6 @@ static int at803x_probe(struct phy_devic + if (ret) + return ret; + +- if (phydev->drv->phy_id == ATH8031_PHY_ID) { +- int ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG); +- int mode_cfg; +- +- if (ccr < 0) +- return ccr; +- mode_cfg = ccr & AT803X_MODE_CFG_MASK; +- +- switch (mode_cfg) { +- case AT803X_MODE_CFG_BX1000_RGMII_50OHM: +- case AT803X_MODE_CFG_BX1000_RGMII_75OHM: +- priv->is_1000basex = true; +- fallthrough; +- case AT803X_MODE_CFG_FX100_RGMII_50OHM: +- case AT803X_MODE_CFG_FX100_RGMII_75OHM: +- priv->is_fiber = true; +- break; +- } +- } +- + return 0; + } + +@@ -1584,6 +1564,9 @@ static int at8031_parse_dt(struct phy_de + + static int at8031_probe(struct phy_device *phydev) + { ++ struct at803x_priv *priv = phydev->priv; ++ int mode_cfg; ++ int ccr; + int ret; + + ret = at803x_probe(phydev); +@@ -1597,6 +1580,22 @@ static int at8031_probe(struct phy_devic + if (ret) + return ret; + ++ ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG); ++ if (ccr < 0) ++ return ccr; ++ mode_cfg = ccr & AT803X_MODE_CFG_MASK; ++ ++ switch (mode_cfg) { ++ case AT803X_MODE_CFG_BX1000_RGMII_50OHM: ++ case AT803X_MODE_CFG_BX1000_RGMII_75OHM: ++ priv->is_1000basex = true; ++ fallthrough; ++ case AT803X_MODE_CFG_FX100_RGMII_50OHM: ++ case AT803X_MODE_CFG_FX100_RGMII_75OHM: ++ priv->is_fiber = true; ++ break; ++ } ++ + /* Disable WoL in 1588 register which is enabled + * by default + */ diff --git a/target/linux/generic/backport-6.6/707-v6.8-07-net-phy-at803x-move-specific-at8031-config_init-to-d.patch b/target/linux/generic/backport-6.6/707-v6.8-07-net-phy-at803x-move-specific-at8031-config_init-to-d.patch new file mode 100644 index 0000000000..c9ef6df827 --- /dev/null +++ b/target/linux/generic/backport-6.6/707-v6.8-07-net-phy-at803x-move-specific-at8031-config_init-to-d.patch @@ -0,0 +1,86 @@ +From 3ae3bc426eaf57ca8f53d75777d9a5ef779bc7b7 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 8 Dec 2023 15:51:54 +0100 +Subject: [PATCH 07/13] net: phy: at803x: move specific at8031 config_init to + dedicated function + +Move specific at8031 config_init to dedicated function to make +at803x_config_init more generic and tidy things up. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 45 ++++++++++++++++++++++------------------ + 1 file changed, 25 insertions(+), 20 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -951,27 +951,8 @@ static int at803x_hibernation_mode_confi + + static int at803x_config_init(struct phy_device *phydev) + { +- struct at803x_priv *priv = phydev->priv; + int ret; + +- if (phydev->drv->phy_id == ATH8031_PHY_ID) { +- /* Some bootloaders leave the fiber page selected. +- * Switch to the appropriate page (fiber or copper), as otherwise we +- * read the PHY capabilities from the wrong page. +- */ +- phy_lock_mdio_bus(phydev); +- ret = at803x_write_page(phydev, +- priv->is_fiber ? AT803X_PAGE_FIBER : +- AT803X_PAGE_COPPER); +- phy_unlock_mdio_bus(phydev); +- if (ret) +- return ret; +- +- ret = at8031_pll_config(phydev); +- if (ret < 0) +- return ret; +- } +- + /* The RX and TX delay default is: + * after HW reset: RX delay enabled and TX delay disabled + * after SW reset: RX delay enabled, while TX delay retains the +@@ -1604,6 +1585,30 @@ static int at8031_probe(struct phy_devic + AT803X_WOL_EN, 0); + } + ++static int at8031_config_init(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ int ret; ++ ++ /* Some bootloaders leave the fiber page selected. ++ * Switch to the appropriate page (fiber or copper), as otherwise we ++ * read the PHY capabilities from the wrong page. ++ */ ++ phy_lock_mdio_bus(phydev); ++ ret = at803x_write_page(phydev, ++ priv->is_fiber ? AT803X_PAGE_FIBER : ++ AT803X_PAGE_COPPER); ++ phy_unlock_mdio_bus(phydev); ++ if (ret) ++ return ret; ++ ++ ret = at8031_pll_config(phydev); ++ if (ret < 0) ++ return ret; ++ ++ return at803x_config_init(phydev); ++} ++ + static int qca83xx_config_init(struct phy_device *phydev) + { + u8 switch_revision; +@@ -2113,7 +2118,7 @@ static struct phy_driver at803x_driver[] + .name = "Qualcomm Atheros AR8031/AR8033", + .flags = PHY_POLL_CABLE_TEST, + .probe = at8031_probe, +- .config_init = at803x_config_init, ++ .config_init = at8031_config_init, + .config_aneg = at803x_config_aneg, + .soft_reset = genphy_soft_reset, + .set_wol = at803x_set_wol, diff --git a/target/linux/generic/backport-6.6/707-v6.8-08-net-phy-at803x-move-specific-at8031-WOL-bits-to-dedi.patch b/target/linux/generic/backport-6.6/707-v6.8-08-net-phy-at803x-move-specific-at8031-WOL-bits-to-dedi.patch new file mode 100644 index 0000000000..6746a4d43c --- /dev/null +++ b/target/linux/generic/backport-6.6/707-v6.8-08-net-phy-at803x-move-specific-at8031-WOL-bits-to-dedi.patch @@ -0,0 +1,92 @@ +From 27b89c9dc1b0393090d68d651b82f30ad2696baa Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 8 Dec 2023 15:51:55 +0100 +Subject: [PATCH 08/13] net: phy: at803x: move specific at8031 WOL bits to + dedicated function + +Move specific at8031 WOL enable/disable to dedicated function to make +at803x_set_wol more generic. + +This is needed in preparation for PHY driver split as qca8081 share the +same function to toggle WOL settings. + +In this new implementation WOL module in at8031 is enabled after the +generic interrupt is setup. This should not cause any problem as the +WOL_INT has a separate implementation and only relay on MAC bits. + +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 42 ++++++++++++++++++++++++---------------- + 1 file changed, 25 insertions(+), 17 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -466,27 +466,11 @@ static int at803x_set_wol(struct phy_dev + phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i], + mac[(i * 2) + 1] | (mac[(i * 2)] << 8)); + +- /* Enable WOL function for 1588 */ +- if (phydev->drv->phy_id == ATH8031_PHY_ID) { +- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, +- AT803X_PHY_MMD3_WOL_CTRL, +- 0, AT803X_WOL_EN); +- if (ret) +- return ret; +- } + /* Enable WOL interrupt */ + ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL); + if (ret) + return ret; + } else { +- /* Disable WoL function for 1588 */ +- if (phydev->drv->phy_id == ATH8031_PHY_ID) { +- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, +- AT803X_PHY_MMD3_WOL_CTRL, +- AT803X_WOL_EN, 0); +- if (ret) +- return ret; +- } + /* Disable WOL interrupt */ + ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0); + if (ret) +@@ -1609,6 +1593,30 @@ static int at8031_config_init(struct phy + return at803x_config_init(phydev); + } + ++static int at8031_set_wol(struct phy_device *phydev, ++ struct ethtool_wolinfo *wol) ++{ ++ int ret; ++ ++ /* First setup MAC address and enable WOL interrupt */ ++ ret = at803x_set_wol(phydev, wol); ++ if (ret) ++ return ret; ++ ++ if (wol->wolopts & WAKE_MAGIC) ++ /* Enable WOL function for 1588 */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, ++ AT803X_PHY_MMD3_WOL_CTRL, ++ 0, AT803X_WOL_EN); ++ else ++ /* Disable WoL function for 1588 */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, ++ AT803X_PHY_MMD3_WOL_CTRL, ++ AT803X_WOL_EN, 0); ++ ++ return ret; ++} ++ + static int qca83xx_config_init(struct phy_device *phydev) + { + u8 switch_revision; +@@ -2121,7 +2129,7 @@ static struct phy_driver at803x_driver[] + .config_init = at8031_config_init, + .config_aneg = at803x_config_aneg, + .soft_reset = genphy_soft_reset, +- .set_wol = at803x_set_wol, ++ .set_wol = at8031_set_wol, + .get_wol = at803x_get_wol, + .suspend = at803x_suspend, + .resume = at803x_resume, diff --git a/target/linux/generic/backport-6.6/707-v6.8-09-net-phy-at803x-move-specific-at8031-config_intr-to-d.patch b/target/linux/generic/backport-6.6/707-v6.8-09-net-phy-at803x-move-specific-at8031-config_intr-to-d.patch new file mode 100644 index 0000000000..0d284c3d24 --- /dev/null +++ b/target/linux/generic/backport-6.6/707-v6.8-09-net-phy-at803x-move-specific-at8031-config_intr-to-d.patch @@ -0,0 +1,78 @@ +From 30dd62191d3dd97c08f7f9dc9ce77ffab457e4fb Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 8 Dec 2023 15:51:56 +0100 +Subject: [PATCH 09/13] net: phy: at803x: move specific at8031 config_intr to + dedicated function + +Move specific at8031 config_intr bits to dedicated function to make +at803x_config_initr more generic. + +This is needed in preparation for PHY driver split as qca8081 share the +same function to setup interrupts. + +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 30 ++++++++++++++++++++++++------ + 1 file changed, 24 insertions(+), 6 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -990,7 +990,6 @@ static int at803x_ack_interrupt(struct p + + static int at803x_config_intr(struct phy_device *phydev) + { +- struct at803x_priv *priv = phydev->priv; + int err; + int value; + +@@ -1007,10 +1006,6 @@ static int at803x_config_intr(struct phy + value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED; + value |= AT803X_INTR_ENABLE_LINK_FAIL; + value |= AT803X_INTR_ENABLE_LINK_SUCCESS; +- if (priv->is_fiber) { +- value |= AT803X_INTR_ENABLE_LINK_FAIL_BX; +- value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX; +- } + + err = phy_write(phydev, AT803X_INTR_ENABLE, value); + } else { +@@ -1617,6 +1612,29 @@ static int at8031_set_wol(struct phy_dev + return ret; + } + ++static int at8031_config_intr(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ int err, value = 0; ++ ++ if (phydev->interrupts == PHY_INTERRUPT_ENABLED && ++ priv->is_fiber) { ++ /* Clear any pending interrupts */ ++ err = at803x_ack_interrupt(phydev); ++ if (err) ++ return err; ++ ++ value |= AT803X_INTR_ENABLE_LINK_FAIL_BX; ++ value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX; ++ ++ err = phy_set_bits(phydev, AT803X_INTR_ENABLE, value); ++ if (err) ++ return err; ++ } ++ ++ return at803x_config_intr(phydev); ++} ++ + static int qca83xx_config_init(struct phy_device *phydev) + { + u8 switch_revision; +@@ -2137,7 +2155,7 @@ static struct phy_driver at803x_driver[] + .write_page = at803x_write_page, + .get_features = at803x_get_features, + .read_status = at803x_read_status, +- .config_intr = at803x_config_intr, ++ .config_intr = at8031_config_intr, + .handle_interrupt = at803x_handle_interrupt, + .get_tunable = at803x_get_tunable, + .set_tunable = at803x_set_tunable, diff --git a/target/linux/generic/backport-6.6/707-v6.8-10-net-phy-at803x-make-at8031-related-DT-functions-name.patch b/target/linux/generic/backport-6.6/707-v6.8-10-net-phy-at803x-make-at8031-related-DT-functions-name.patch new file mode 100644 index 0000000000..210949ea81 --- /dev/null +++ b/target/linux/generic/backport-6.6/707-v6.8-10-net-phy-at803x-make-at8031-related-DT-functions-name.patch @@ -0,0 +1,78 @@ +From a5ab9d8e7ae0da8328ac1637a9755311508dc8ab Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 8 Dec 2023 15:51:57 +0100 +Subject: [PATCH 10/13] net: phy: at803x: make at8031 related DT functions name + more specific + +Rename at8031 related DT function name to a more specific name +referencing they are only related to at8031 and not to the generic +at803x PHY family. + +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -583,7 +583,7 @@ static int at803x_resume(struct phy_devi + return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0); + } + +-static int at803x_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev, ++static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) + { + struct phy_device *phydev = rdev_get_drvdata(rdev); +@@ -596,7 +596,7 @@ static int at803x_rgmii_reg_set_voltage_ + AT803X_DEBUG_RGMII_1V8, 0); + } + +-static int at803x_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev) ++static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev) + { + struct phy_device *phydev = rdev_get_drvdata(rdev); + int val; +@@ -610,8 +610,8 @@ static int at803x_rgmii_reg_get_voltage_ + + static const struct regulator_ops vddio_regulator_ops = { + .list_voltage = regulator_list_voltage_table, +- .set_voltage_sel = at803x_rgmii_reg_set_voltage_sel, +- .get_voltage_sel = at803x_rgmii_reg_get_voltage_sel, ++ .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel, ++ .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel, + }; + + static const unsigned int vddio_voltage_table[] = { +@@ -666,7 +666,7 @@ static int at8031_register_regulators(st + return 0; + } + +-static int at803x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) ++static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) + { + struct phy_device *phydev = upstream; + __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support); +@@ -710,10 +710,10 @@ static int at803x_sfp_insert(void *upstr + return 0; + } + +-static const struct sfp_upstream_ops at803x_sfp_ops = { ++static const struct sfp_upstream_ops at8031_sfp_ops = { + .attach = phy_sfp_attach, + .detach = phy_sfp_detach, +- .module_insert = at803x_sfp_insert, ++ .module_insert = at8031_sfp_insert, + }; + + static int at803x_parse_dt(struct phy_device *phydev) +@@ -1519,7 +1519,7 @@ static int at8031_parse_dt(struct phy_de + } + + /* Only AR8031/8033 support 1000Base-X for SFP modules */ +- return phy_sfp_probe(phydev, &at803x_sfp_ops); ++ return phy_sfp_probe(phydev, &at8031_sfp_ops); + } + + static int at8031_probe(struct phy_device *phydev) diff --git a/target/linux/generic/backport-6.6/707-v6.8-11-net-phy-at803x-move-at8031-functions-in-dedicated-se.patch b/target/linux/generic/backport-6.6/707-v6.8-11-net-phy-at803x-move-at8031-functions-in-dedicated-se.patch new file mode 100644 index 0000000000..5856b59751 --- /dev/null +++ b/target/linux/generic/backport-6.6/707-v6.8-11-net-phy-at803x-move-at8031-functions-in-dedicated-se.patch @@ -0,0 +1,297 @@ +From f932a6dc8bae0dae9645b5b1b4c65aed8a8acb2a Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 8 Dec 2023 15:51:58 +0100 +Subject: [PATCH 11/13] net: phy: at803x: move at8031 functions in dedicated + section + +Move at8031 functions in dedicated section with dedicated at8031 +parse_dt and probe. + +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 266 +++++++++++++++++++-------------------- + 1 file changed, 133 insertions(+), 133 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -583,139 +583,6 @@ static int at803x_resume(struct phy_devi + return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0); + } + +-static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev, +- unsigned int selector) +-{ +- struct phy_device *phydev = rdev_get_drvdata(rdev); +- +- if (selector) +- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, +- 0, AT803X_DEBUG_RGMII_1V8); +- else +- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, +- AT803X_DEBUG_RGMII_1V8, 0); +-} +- +-static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev) +-{ +- struct phy_device *phydev = rdev_get_drvdata(rdev); +- int val; +- +- val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F); +- if (val < 0) +- return val; +- +- return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0; +-} +- +-static const struct regulator_ops vddio_regulator_ops = { +- .list_voltage = regulator_list_voltage_table, +- .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel, +- .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel, +-}; +- +-static const unsigned int vddio_voltage_table[] = { +- 1500000, +- 1800000, +-}; +- +-static const struct regulator_desc vddio_desc = { +- .name = "vddio", +- .of_match = of_match_ptr("vddio-regulator"), +- .n_voltages = ARRAY_SIZE(vddio_voltage_table), +- .volt_table = vddio_voltage_table, +- .ops = &vddio_regulator_ops, +- .type = REGULATOR_VOLTAGE, +- .owner = THIS_MODULE, +-}; +- +-static const struct regulator_ops vddh_regulator_ops = { +-}; +- +-static const struct regulator_desc vddh_desc = { +- .name = "vddh", +- .of_match = of_match_ptr("vddh-regulator"), +- .n_voltages = 1, +- .fixed_uV = 2500000, +- .ops = &vddh_regulator_ops, +- .type = REGULATOR_VOLTAGE, +- .owner = THIS_MODULE, +-}; +- +-static int at8031_register_regulators(struct phy_device *phydev) +-{ +- struct at803x_priv *priv = phydev->priv; +- struct device *dev = &phydev->mdio.dev; +- struct regulator_config config = { }; +- +- config.dev = dev; +- config.driver_data = phydev; +- +- priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config); +- if (IS_ERR(priv->vddio_rdev)) { +- phydev_err(phydev, "failed to register VDDIO regulator\n"); +- return PTR_ERR(priv->vddio_rdev); +- } +- +- priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config); +- if (IS_ERR(priv->vddh_rdev)) { +- phydev_err(phydev, "failed to register VDDH regulator\n"); +- return PTR_ERR(priv->vddh_rdev); +- } +- +- return 0; +-} +- +-static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) +-{ +- struct phy_device *phydev = upstream; +- __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support); +- __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); +- DECLARE_PHY_INTERFACE_MASK(interfaces); +- phy_interface_t iface; +- +- linkmode_zero(phy_support); +- phylink_set(phy_support, 1000baseX_Full); +- phylink_set(phy_support, 1000baseT_Full); +- phylink_set(phy_support, Autoneg); +- phylink_set(phy_support, Pause); +- phylink_set(phy_support, Asym_Pause); +- +- linkmode_zero(sfp_support); +- sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces); +- /* Some modules support 10G modes as well as others we support. +- * Mask out non-supported modes so the correct interface is picked. +- */ +- linkmode_and(sfp_support, phy_support, sfp_support); +- +- if (linkmode_empty(sfp_support)) { +- dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n"); +- return -EINVAL; +- } +- +- iface = sfp_select_interface(phydev->sfp_bus, sfp_support); +- +- /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes +- * interface for use with SFP modules. +- * However, some copper modules detected as having a preferred SGMII +- * interface do default to and function in 1000Base-X mode, so just +- * print a warning and allow such modules, as they may have some chance +- * of working. +- */ +- if (iface == PHY_INTERFACE_MODE_SGMII) +- dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n"); +- else if (iface != PHY_INTERFACE_MODE_1000BASEX) +- return -EINVAL; +- +- return 0; +-} +- +-static const struct sfp_upstream_ops at8031_sfp_ops = { +- .attach = phy_sfp_attach, +- .detach = phy_sfp_detach, +- .module_insert = at8031_sfp_insert, +-}; +- + static int at803x_parse_dt(struct phy_device *phydev) + { + struct device_node *node = phydev->mdio.dev.of_node; +@@ -1498,6 +1365,139 @@ static int at803x_cable_test_start(struc + return 0; + } + ++static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev, ++ unsigned int selector) ++{ ++ struct phy_device *phydev = rdev_get_drvdata(rdev); ++ ++ if (selector) ++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, ++ 0, AT803X_DEBUG_RGMII_1V8); ++ else ++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, ++ AT803X_DEBUG_RGMII_1V8, 0); ++} ++ ++static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev) ++{ ++ struct phy_device *phydev = rdev_get_drvdata(rdev); ++ int val; ++ ++ val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F); ++ if (val < 0) ++ return val; ++ ++ return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0; ++} ++ ++static const struct regulator_ops vddio_regulator_ops = { ++ .list_voltage = regulator_list_voltage_table, ++ .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel, ++ .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel, ++}; ++ ++static const unsigned int vddio_voltage_table[] = { ++ 1500000, ++ 1800000, ++}; ++ ++static const struct regulator_desc vddio_desc = { ++ .name = "vddio", ++ .of_match = of_match_ptr("vddio-regulator"), ++ .n_voltages = ARRAY_SIZE(vddio_voltage_table), ++ .volt_table = vddio_voltage_table, ++ .ops = &vddio_regulator_ops, ++ .type = REGULATOR_VOLTAGE, ++ .owner = THIS_MODULE, ++}; ++ ++static const struct regulator_ops vddh_regulator_ops = { ++}; ++ ++static const struct regulator_desc vddh_desc = { ++ .name = "vddh", ++ .of_match = of_match_ptr("vddh-regulator"), ++ .n_voltages = 1, ++ .fixed_uV = 2500000, ++ .ops = &vddh_regulator_ops, ++ .type = REGULATOR_VOLTAGE, ++ .owner = THIS_MODULE, ++}; ++ ++static int at8031_register_regulators(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ struct device *dev = &phydev->mdio.dev; ++ struct regulator_config config = { }; ++ ++ config.dev = dev; ++ config.driver_data = phydev; ++ ++ priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config); ++ if (IS_ERR(priv->vddio_rdev)) { ++ phydev_err(phydev, "failed to register VDDIO regulator\n"); ++ return PTR_ERR(priv->vddio_rdev); ++ } ++ ++ priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config); ++ if (IS_ERR(priv->vddh_rdev)) { ++ phydev_err(phydev, "failed to register VDDH regulator\n"); ++ return PTR_ERR(priv->vddh_rdev); ++ } ++ ++ return 0; ++} ++ ++static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) ++{ ++ struct phy_device *phydev = upstream; ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support); ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); ++ DECLARE_PHY_INTERFACE_MASK(interfaces); ++ phy_interface_t iface; ++ ++ linkmode_zero(phy_support); ++ phylink_set(phy_support, 1000baseX_Full); ++ phylink_set(phy_support, 1000baseT_Full); ++ phylink_set(phy_support, Autoneg); ++ phylink_set(phy_support, Pause); ++ phylink_set(phy_support, Asym_Pause); ++ ++ linkmode_zero(sfp_support); ++ sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces); ++ /* Some modules support 10G modes as well as others we support. ++ * Mask out non-supported modes so the correct interface is picked. ++ */ ++ linkmode_and(sfp_support, phy_support, sfp_support); ++ ++ if (linkmode_empty(sfp_support)) { ++ dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n"); ++ return -EINVAL; ++ } ++ ++ iface = sfp_select_interface(phydev->sfp_bus, sfp_support); ++ ++ /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes ++ * interface for use with SFP modules. ++ * However, some copper modules detected as having a preferred SGMII ++ * interface do default to and function in 1000Base-X mode, so just ++ * print a warning and allow such modules, as they may have some chance ++ * of working. ++ */ ++ if (iface == PHY_INTERFACE_MODE_SGMII) ++ dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n"); ++ else if (iface != PHY_INTERFACE_MODE_1000BASEX) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static const struct sfp_upstream_ops at8031_sfp_ops = { ++ .attach = phy_sfp_attach, ++ .detach = phy_sfp_detach, ++ .module_insert = at8031_sfp_insert, ++}; ++ + static int at8031_parse_dt(struct phy_device *phydev) + { + struct device_node *node = phydev->mdio.dev.of_node; diff --git a/target/linux/generic/backport-6.6/707-v6.8-12-net-phy-at803x-move-at8035-specific-DT-parse-to-dedi.patch b/target/linux/generic/backport-6.6/707-v6.8-12-net-phy-at803x-move-at8035-specific-DT-parse-to-dedi.patch new file mode 100644 index 0000000000..0a822c6d37 --- /dev/null +++ b/target/linux/generic/backport-6.6/707-v6.8-12-net-phy-at803x-move-at8035-specific-DT-parse-to-dedi.patch @@ -0,0 +1,114 @@ +From 21a2802a8365cfa82cc02187c1f95136d85592ad Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 8 Dec 2023 15:51:59 +0100 +Subject: [PATCH 12/13] net: phy: at803x: move at8035 specific DT parse to + dedicated probe + +Move at8035 specific DT parse for clock out frequency to dedicated probe +to make at803x probe function more generic. + +This is to tidy code and no behaviour change are intended. + +Detection logic is changed, we check if the clk 25m mask is set and if +it's not zero, we assume the qca,clk-out-frequency property is set. + +The property is checked in the generic at803x_parse_dt called by +at803x_probe. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 60 +++++++++++++++++++++++++++------------- + 1 file changed, 41 insertions(+), 19 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -638,23 +638,6 @@ static int at803x_parse_dt(struct phy_de + + priv->clk_25m_reg |= FIELD_PREP(AT803X_CLK_OUT_MASK, sel); + priv->clk_25m_mask |= AT803X_CLK_OUT_MASK; +- +- /* Fixup for the AR8030/AR8035. This chip has another mask and +- * doesn't support the DSP reference. Eg. the lowest bit of the +- * mask. The upper two bits select the same frequencies. Mask +- * the lowest bit here. +- * +- * Warning: +- * There was no datasheet for the AR8030 available so this is +- * just a guess. But the AR8035 is listed as pin compatible +- * to the AR8030 so there might be a good chance it works on +- * the AR8030 too. +- */ +- if (phydev->drv->phy_id == ATH8030_PHY_ID || +- phydev->drv->phy_id == ATH8035_PHY_ID) { +- priv->clk_25m_reg &= AT8035_CLK_OUT_MASK; +- priv->clk_25m_mask &= AT8035_CLK_OUT_MASK; +- } + } + + ret = of_property_read_u32(node, "qca,clk-out-strength", &strength); +@@ -1635,6 +1618,45 @@ static int at8031_config_intr(struct phy + return at803x_config_intr(phydev); + } + ++static int at8035_parse_dt(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ ++ /* Mask is set by the generic at803x_parse_dt ++ * if property is set. Assume property is set ++ * with the mask not zero. ++ */ ++ if (priv->clk_25m_mask) { ++ /* Fixup for the AR8030/AR8035. This chip has another mask and ++ * doesn't support the DSP reference. Eg. the lowest bit of the ++ * mask. The upper two bits select the same frequencies. Mask ++ * the lowest bit here. ++ * ++ * Warning: ++ * There was no datasheet for the AR8030 available so this is ++ * just a guess. But the AR8035 is listed as pin compatible ++ * to the AR8030 so there might be a good chance it works on ++ * the AR8030 too. ++ */ ++ priv->clk_25m_reg &= AT8035_CLK_OUT_MASK; ++ priv->clk_25m_mask &= AT8035_CLK_OUT_MASK; ++ } ++ ++ return 0; ++} ++ ++/* AR8030 and AR8035 shared the same special mask for clk_25m */ ++static int at8035_probe(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = at803x_probe(phydev); ++ if (ret) ++ return ret; ++ ++ return at8035_parse_dt(phydev); ++} ++ + static int qca83xx_config_init(struct phy_device *phydev) + { + u8 switch_revision; +@@ -2107,7 +2129,7 @@ static struct phy_driver at803x_driver[] + PHY_ID_MATCH_EXACT(ATH8035_PHY_ID), + .name = "Qualcomm Atheros AR8035", + .flags = PHY_POLL_CABLE_TEST, +- .probe = at803x_probe, ++ .probe = at8035_probe, + .config_aneg = at803x_config_aneg, + .config_init = at803x_config_init, + .soft_reset = genphy_soft_reset, +@@ -2128,7 +2150,7 @@ static struct phy_driver at803x_driver[] + .phy_id = ATH8030_PHY_ID, + .name = "Qualcomm Atheros AR8030", + .phy_id_mask = AT8030_PHY_ID_MASK, +- .probe = at803x_probe, ++ .probe = at8035_probe, + .config_init = at803x_config_init, + .link_change_notify = at803x_link_change_notify, + .set_wol = at803x_set_wol, diff --git a/target/linux/generic/backport-6.6/707-v6.8-13-net-phy-at803x-drop-specific-PHY-ID-check-from-cable.patch b/target/linux/generic/backport-6.6/707-v6.8-13-net-phy-at803x-drop-specific-PHY-ID-check-from-cable.patch new file mode 100644 index 0000000000..e1ba6ec2d0 --- /dev/null +++ b/target/linux/generic/backport-6.6/707-v6.8-13-net-phy-at803x-drop-specific-PHY-ID-check-from-cable.patch @@ -0,0 +1,219 @@ +From ef9df47b449e32e06501a11272809be49019bdb6 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 8 Dec 2023 15:52:00 +0100 +Subject: [PATCH 13/13] net: phy: at803x: drop specific PHY ID check from cable + test functions + +Drop specific PHY ID check for cable test functions for at803x. This is +done to make functions more generic. While at it better describe what +the functions does by using more symbolic function names. + +PHYs that requires to set additional reg are moved to specific function +calling the more generic one. + +cdt_start and cdt_wait_for_completion are changed to take an additional +arg to pass specific values specific to the PHY. + +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 95 +++++++++++++++++++++------------------- + 1 file changed, 50 insertions(+), 45 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -1222,31 +1222,16 @@ static int at803x_cdt_fault_length(u16 s + return (dt * 824) / 10; + } + +-static int at803x_cdt_start(struct phy_device *phydev, int pair) ++static int at803x_cdt_start(struct phy_device *phydev, ++ u32 cdt_start) + { +- u16 cdt; +- +- /* qca8081 takes the different bit 15 to enable CDT test */ +- if (phydev->drv->phy_id == QCA8081_PHY_ID) +- cdt = QCA808X_CDT_ENABLE_TEST | +- QCA808X_CDT_LENGTH_UNIT | +- QCA808X_CDT_INTER_CHECK_DIS; +- else +- cdt = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) | +- AT803X_CDT_ENABLE_TEST; +- +- return phy_write(phydev, AT803X_CDT, cdt); ++ return phy_write(phydev, AT803X_CDT, cdt_start); + } + +-static int at803x_cdt_wait_for_completion(struct phy_device *phydev) ++static int at803x_cdt_wait_for_completion(struct phy_device *phydev, ++ u32 cdt_en) + { + int val, ret; +- u16 cdt_en; +- +- if (phydev->drv->phy_id == QCA8081_PHY_ID) +- cdt_en = QCA808X_CDT_ENABLE_TEST; +- else +- cdt_en = AT803X_CDT_ENABLE_TEST; + + /* One test run takes about 25ms */ + ret = phy_read_poll_timeout(phydev, AT803X_CDT, val, +@@ -1266,11 +1251,13 @@ static int at803x_cable_test_one_pair(st + }; + int ret, val; + +- ret = at803x_cdt_start(phydev, pair); ++ val = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) | ++ AT803X_CDT_ENABLE_TEST; ++ ret = at803x_cdt_start(phydev, val); + if (ret) + return ret; + +- ret = at803x_cdt_wait_for_completion(phydev); ++ ret = at803x_cdt_wait_for_completion(phydev, AT803X_CDT_ENABLE_TEST); + if (ret) + return ret; + +@@ -1292,19 +1279,11 @@ static int at803x_cable_test_one_pair(st + } + + static int at803x_cable_test_get_status(struct phy_device *phydev, +- bool *finished) ++ bool *finished, unsigned long pair_mask) + { +- unsigned long pair_mask; + int retries = 20; + int pair, ret; + +- if (phydev->phy_id == ATH9331_PHY_ID || +- phydev->phy_id == ATH8032_PHY_ID || +- phydev->phy_id == QCA9561_PHY_ID) +- pair_mask = 0x3; +- else +- pair_mask = 0xf; +- + *finished = false; + + /* According to the datasheet the CDT can be performed when +@@ -1331,7 +1310,7 @@ static int at803x_cable_test_get_status( + return 0; + } + +-static int at803x_cable_test_start(struct phy_device *phydev) ++static void at803x_cable_test_autoneg(struct phy_device *phydev) + { + /* Enable auto-negotiation, but advertise no capabilities, no link + * will be established. A restart of the auto-negotiation is not +@@ -1339,11 +1318,11 @@ static int at803x_cable_test_start(struc + */ + phy_write(phydev, MII_BMCR, BMCR_ANENABLE); + phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA); +- if (phydev->phy_id != ATH9331_PHY_ID && +- phydev->phy_id != ATH8032_PHY_ID && +- phydev->phy_id != QCA9561_PHY_ID) +- phy_write(phydev, MII_CTRL1000, 0); ++} + ++static int at803x_cable_test_start(struct phy_device *phydev) ++{ ++ at803x_cable_test_autoneg(phydev); + /* we do all the (time consuming) work later */ + return 0; + } +@@ -1618,6 +1597,29 @@ static int at8031_config_intr(struct phy + return at803x_config_intr(phydev); + } + ++/* AR8031 and AR8035 share the same cable test get status reg */ ++static int at8031_cable_test_get_status(struct phy_device *phydev, ++ bool *finished) ++{ ++ return at803x_cable_test_get_status(phydev, finished, 0xf); ++} ++ ++/* AR8031 and AR8035 share the same cable test start logic */ ++static int at8031_cable_test_start(struct phy_device *phydev) ++{ ++ at803x_cable_test_autoneg(phydev); ++ phy_write(phydev, MII_CTRL1000, 0); ++ /* we do all the (time consuming) work later */ ++ return 0; ++} ++ ++/* AR8032, AR9331 and QCA9561 share the same cable test get status reg */ ++static int at8032_cable_test_get_status(struct phy_device *phydev, ++ bool *finished) ++{ ++ return at803x_cable_test_get_status(phydev, finished, 0x3); ++} ++ + static int at8035_parse_dt(struct phy_device *phydev) + { + struct at803x_priv *priv = phydev->priv; +@@ -2041,11 +2043,14 @@ static int qca808x_cable_test_get_status + + *finished = false; + +- ret = at803x_cdt_start(phydev, 0); ++ val = QCA808X_CDT_ENABLE_TEST | ++ QCA808X_CDT_LENGTH_UNIT | ++ QCA808X_CDT_INTER_CHECK_DIS; ++ ret = at803x_cdt_start(phydev, val); + if (ret) + return ret; + +- ret = at803x_cdt_wait_for_completion(phydev); ++ ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST); + if (ret) + return ret; + +@@ -2143,8 +2148,8 @@ static struct phy_driver at803x_driver[] + .handle_interrupt = at803x_handle_interrupt, + .get_tunable = at803x_get_tunable, + .set_tunable = at803x_set_tunable, +- .cable_test_start = at803x_cable_test_start, +- .cable_test_get_status = at803x_cable_test_get_status, ++ .cable_test_start = at8031_cable_test_start, ++ .cable_test_get_status = at8031_cable_test_get_status, + }, { + /* Qualcomm Atheros AR8030 */ + .phy_id = ATH8030_PHY_ID, +@@ -2181,8 +2186,8 @@ static struct phy_driver at803x_driver[] + .handle_interrupt = at803x_handle_interrupt, + .get_tunable = at803x_get_tunable, + .set_tunable = at803x_set_tunable, +- .cable_test_start = at803x_cable_test_start, +- .cable_test_get_status = at803x_cable_test_get_status, ++ .cable_test_start = at8031_cable_test_start, ++ .cable_test_get_status = at8031_cable_test_get_status, + }, { + /* Qualcomm Atheros AR8032 */ + PHY_ID_MATCH_EXACT(ATH8032_PHY_ID), +@@ -2197,7 +2202,7 @@ static struct phy_driver at803x_driver[] + .config_intr = at803x_config_intr, + .handle_interrupt = at803x_handle_interrupt, + .cable_test_start = at803x_cable_test_start, +- .cable_test_get_status = at803x_cable_test_get_status, ++ .cable_test_get_status = at8032_cable_test_get_status, + }, { + /* ATHEROS AR9331 */ + PHY_ID_MATCH_EXACT(ATH9331_PHY_ID), +@@ -2210,7 +2215,7 @@ static struct phy_driver at803x_driver[] + .config_intr = at803x_config_intr, + .handle_interrupt = at803x_handle_interrupt, + .cable_test_start = at803x_cable_test_start, +- .cable_test_get_status = at803x_cable_test_get_status, ++ .cable_test_get_status = at8032_cable_test_get_status, + .read_status = at803x_read_status, + .soft_reset = genphy_soft_reset, + .config_aneg = at803x_config_aneg, +@@ -2226,7 +2231,7 @@ static struct phy_driver at803x_driver[] + .config_intr = at803x_config_intr, + .handle_interrupt = at803x_handle_interrupt, + .cable_test_start = at803x_cable_test_start, +- .cable_test_get_status = at803x_cable_test_get_status, ++ .cable_test_get_status = at8032_cable_test_get_status, + .read_status = at803x_read_status, + .soft_reset = genphy_soft_reset, + .config_aneg = at803x_config_aneg, diff --git a/target/linux/generic/backport-6.6/708-v6.8-01-net-phy-at803x-move-specific-qca808x-config_aneg-to-.patch b/target/linux/generic/backport-6.6/708-v6.8-01-net-phy-at803x-move-specific-qca808x-config_aneg-to-.patch new file mode 100644 index 0000000000..0bb3673c02 --- /dev/null +++ b/target/linux/generic/backport-6.6/708-v6.8-01-net-phy-at803x-move-specific-qca808x-config_aneg-to-.patch @@ -0,0 +1,116 @@ +From 8e732f1c6f2dc5e18f766d0f1b11df9db2dd044a Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 14 Dec 2023 01:44:31 +0100 +Subject: [PATCH 1/2] net: phy: at803x: move specific qca808x config_aneg to + dedicated function + +Move specific qca808x config_aneg to dedicated function to permit easier +split of qca808x portion from at803x driver. + +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 66 ++++++++++++++++++++++++---------------- + 1 file changed, 40 insertions(+), 26 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -1045,9 +1045,8 @@ static int at803x_config_mdix(struct phy + FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val)); + } + +-static int at803x_config_aneg(struct phy_device *phydev) ++static int at803x_prepare_config_aneg(struct phy_device *phydev) + { +- struct at803x_priv *priv = phydev->priv; + int ret; + + ret = at803x_config_mdix(phydev, phydev->mdix_ctrl); +@@ -1064,33 +1063,22 @@ static int at803x_config_aneg(struct phy + return ret; + } + +- if (priv->is_1000basex) +- return genphy_c37_config_aneg(phydev); +- +- /* Do not restart auto-negotiation by setting ret to 0 defautly, +- * when calling __genphy_config_aneg later. +- */ +- ret = 0; +- +- if (phydev->drv->phy_id == QCA8081_PHY_ID) { +- int phy_ctrl = 0; ++ return 0; ++} + +- /* The reg MII_BMCR also needs to be configured for force mode, the +- * genphy_config_aneg is also needed. +- */ +- if (phydev->autoneg == AUTONEG_DISABLE) +- genphy_c45_pma_setup_forced(phydev); ++static int at803x_config_aneg(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ int ret; + +- if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising)) +- phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G; ++ ret = at803x_prepare_config_aneg(phydev); ++ if (ret) ++ return ret; + +- ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, +- MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl); +- if (ret < 0) +- return ret; +- } ++ if (priv->is_1000basex) ++ return genphy_c37_config_aneg(phydev); + +- return __genphy_config_aneg(phydev, ret); ++ return genphy_config_aneg(phydev); + } + + static int at803x_get_downshift(struct phy_device *phydev, u8 *d) +@@ -2118,6 +2106,32 @@ static int qca808x_get_features(struct p + return 0; + } + ++static int qca808x_config_aneg(struct phy_device *phydev) ++{ ++ int phy_ctrl = 0; ++ int ret; ++ ++ ret = at803x_prepare_config_aneg(phydev); ++ if (ret) ++ return ret; ++ ++ /* The reg MII_BMCR also needs to be configured for force mode, the ++ * genphy_config_aneg is also needed. ++ */ ++ if (phydev->autoneg == AUTONEG_DISABLE) ++ genphy_c45_pma_setup_forced(phydev); ++ ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising)) ++ phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G; ++ ++ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, ++ MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl); ++ if (ret < 0) ++ return ret; ++ ++ return __genphy_config_aneg(phydev, ret); ++} ++ + static void qca808x_link_change_notify(struct phy_device *phydev) + { + /* Assert interface sgmii fifo on link down, deassert it on link up, +@@ -2295,7 +2309,7 @@ static struct phy_driver at803x_driver[] + .set_wol = at803x_set_wol, + .get_wol = at803x_get_wol, + .get_features = qca808x_get_features, +- .config_aneg = at803x_config_aneg, ++ .config_aneg = qca808x_config_aneg, + .suspend = genphy_suspend, + .resume = genphy_resume, + .read_status = qca808x_read_status, diff --git a/target/linux/generic/backport-6.6/708-v6.8-02-net-phy-at803x-make-read-specific-status-function-mo.patch b/target/linux/generic/backport-6.6/708-v6.8-02-net-phy-at803x-make-read-specific-status-function-mo.patch new file mode 100644 index 0000000000..54ca3bdfc8 --- /dev/null +++ b/target/linux/generic/backport-6.6/708-v6.8-02-net-phy-at803x-make-read-specific-status-function-mo.patch @@ -0,0 +1,97 @@ +From 38eb804e8458ba181a03a0498ce4bf84eebd1931 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 14 Dec 2023 01:44:32 +0100 +Subject: [PATCH 2/2] net: phy: at803x: make read specific status function more + generic + +Rework read specific status function to be more generic. The function +apply different speed mask based on the PHY ID. Make it more generic by +adding an additional arg to pass the specific speed (ss) mask and use +the provided mask to parse the speed value. + +This is needed to permit an easier deatch of qca808x code from the +at803x driver. + +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 26 ++++++++++++++++++-------- + 1 file changed, 18 insertions(+), 8 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -301,6 +301,11 @@ static struct at803x_hw_stat qca83xx_hw_ + { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD}, + }; + ++struct at803x_ss_mask { ++ u16 speed_mask; ++ u8 speed_shift; ++}; ++ + struct at803x_priv { + int flags; + u16 clk_25m_reg; +@@ -921,7 +926,8 @@ static void at803x_link_change_notify(st + } + } + +-static int at803x_read_specific_status(struct phy_device *phydev) ++static int at803x_read_specific_status(struct phy_device *phydev, ++ struct at803x_ss_mask ss_mask) + { + int ss; + +@@ -940,11 +946,8 @@ static int at803x_read_specific_status(s + if (sfc < 0) + return sfc; + +- /* qca8081 takes the different bits for speed value from at803x */ +- if (phydev->drv->phy_id == QCA8081_PHY_ID) +- speed = FIELD_GET(QCA808X_SS_SPEED_MASK, ss); +- else +- speed = FIELD_GET(AT803X_SS_SPEED_MASK, ss); ++ speed = ss & ss_mask.speed_mask; ++ speed >>= ss_mask.speed_shift; + + switch (speed) { + case AT803X_SS_SPEED_10: +@@ -989,6 +992,7 @@ static int at803x_read_specific_status(s + static int at803x_read_status(struct phy_device *phydev) + { + struct at803x_priv *priv = phydev->priv; ++ struct at803x_ss_mask ss_mask = { 0 }; + int err, old_link = phydev->link; + + if (priv->is_1000basex) +@@ -1012,7 +1016,9 @@ static int at803x_read_status(struct phy + if (err < 0) + return err; + +- err = at803x_read_specific_status(phydev); ++ ss_mask.speed_mask = AT803X_SS_SPEED_MASK; ++ ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK); ++ err = at803x_read_specific_status(phydev, ss_mask); + if (err < 0) + return err; + +@@ -1869,6 +1875,7 @@ static int qca808x_config_init(struct ph + + static int qca808x_read_status(struct phy_device *phydev) + { ++ struct at803x_ss_mask ss_mask = { 0 }; + int ret; + + ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT); +@@ -1882,7 +1889,10 @@ static int qca808x_read_status(struct ph + if (ret) + return ret; + +- ret = at803x_read_specific_status(phydev); ++ /* qca8081 takes the different bits for speed value from at803x */ ++ ss_mask.speed_mask = QCA808X_SS_SPEED_MASK; ++ ss_mask.speed_shift = __bf_shf(QCA808X_SS_SPEED_MASK); ++ ret = at803x_read_specific_status(phydev, ss_mask); + if (ret < 0) + return ret; + diff --git a/target/linux/generic/backport-6.6/709-v6.8-01-net-phy-at803x-remove-extra-space-after-cast.patch b/target/linux/generic/backport-6.6/709-v6.8-01-net-phy-at803x-remove-extra-space-after-cast.patch new file mode 100644 index 0000000000..8097a33e49 --- /dev/null +++ b/target/linux/generic/backport-6.6/709-v6.8-01-net-phy-at803x-remove-extra-space-after-cast.patch @@ -0,0 +1,27 @@ +From fc9d7264ddc32eaa647d6bfcdc25cdf9f786fde0 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 18 Dec 2023 00:27:39 +0100 +Subject: [PATCH 1/2] net: phy: at803x: remove extra space after cast + +Remove extra space after cast as reported by checkpatch to keep code +clean. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20231217232739.27065-1-ansuelsmth@gmail.com +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/at803x.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -462,7 +462,7 @@ static int at803x_set_wol(struct phy_dev + if (!ndev) + return -ENODEV; + +- mac = (const u8 *) ndev->dev_addr; ++ mac = (const u8 *)ndev->dev_addr; + + if (!is_valid_ether_addr(mac)) + return -EINVAL; diff --git a/target/linux/generic/backport-6.6/709-v6.8-02-net-phy-at803x-replace-msleep-1-with-usleep_range.patch b/target/linux/generic/backport-6.6/709-v6.8-02-net-phy-at803x-replace-msleep-1-with-usleep_range.patch new file mode 100644 index 0000000000..b8d4591087 --- /dev/null +++ b/target/linux/generic/backport-6.6/709-v6.8-02-net-phy-at803x-replace-msleep-1-with-usleep_range.patch @@ -0,0 +1,38 @@ +From 3ab5720881a924fb6405d9e6a3b09f1026467c47 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 18 Dec 2023 00:25:08 +0100 +Subject: [PATCH 2/2] net: phy: at803x: replace msleep(1) with usleep_range + +Replace msleep(1) with usleep_range as suggested by timers-howto guide. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20231217232508.26470-1-ansuelsmth@gmail.com +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/at803x.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -916,9 +916,9 @@ static void at803x_link_change_notify(st + at803x_context_save(phydev, &context); + + phy_device_reset(phydev, 1); +- msleep(1); ++ usleep_range(1000, 2000); + phy_device_reset(phydev, 0); +- msleep(1); ++ usleep_range(1000, 2000); + + at803x_context_restore(phydev, &context); + +@@ -1733,7 +1733,7 @@ static int qca83xx_resume(struct phy_dev + if (ret) + return ret; + +- msleep(1); ++ usleep_range(1000, 2000); + + return 0; + } diff --git a/target/linux/generic/backport-6.6/710-v6.8-net-phy-at803x-better-align-function-varibles-to-ope.patch b/target/linux/generic/backport-6.6/710-v6.8-net-phy-at803x-better-align-function-varibles-to-ope.patch new file mode 100644 index 0000000000..776b7bb43f --- /dev/null +++ b/target/linux/generic/backport-6.6/710-v6.8-net-phy-at803x-better-align-function-varibles-to-ope.patch @@ -0,0 +1,152 @@ +From 7961ef1fa10ec35ad6923fb5751877116e4b035b Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 19 Dec 2023 21:21:24 +0100 +Subject: [PATCH] net: phy: at803x: better align function varibles to open + parenthesis + +Better align function variables to open parenthesis as suggested by +checkpatch script for qca808x function to make code cleaner. + +For cable_test_get_status function some additional rework was needed to +handle too long functions. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 67 ++++++++++++++++++++++------------------ + 1 file changed, 37 insertions(+), 30 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -1781,27 +1781,27 @@ static int qca808x_phy_fast_retrain_conf + return ret; + + phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1, +- QCA808X_TOP_OPTION1_DATA); ++ QCA808X_TOP_OPTION1_DATA); + phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB, +- QCA808X_MSE_THRESHOLD_20DB_VALUE); ++ QCA808X_MSE_THRESHOLD_20DB_VALUE); + phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB, +- QCA808X_MSE_THRESHOLD_17DB_VALUE); ++ QCA808X_MSE_THRESHOLD_17DB_VALUE); + phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB, +- QCA808X_MSE_THRESHOLD_27DB_VALUE); ++ QCA808X_MSE_THRESHOLD_27DB_VALUE); + phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB, +- QCA808X_MSE_THRESHOLD_28DB_VALUE); ++ QCA808X_MSE_THRESHOLD_28DB_VALUE); + phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1, +- QCA808X_MMD3_DEBUG_1_VALUE); ++ QCA808X_MMD3_DEBUG_1_VALUE); + phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4, +- QCA808X_MMD3_DEBUG_4_VALUE); ++ QCA808X_MMD3_DEBUG_4_VALUE); + phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5, +- QCA808X_MMD3_DEBUG_5_VALUE); ++ QCA808X_MMD3_DEBUG_5_VALUE); + phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3, +- QCA808X_MMD3_DEBUG_3_VALUE); ++ QCA808X_MMD3_DEBUG_3_VALUE); + phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6, +- QCA808X_MMD3_DEBUG_6_VALUE); ++ QCA808X_MMD3_DEBUG_6_VALUE); + phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2, +- QCA808X_MMD3_DEBUG_2_VALUE); ++ QCA808X_MMD3_DEBUG_2_VALUE); + + return 0; + } +@@ -1838,13 +1838,14 @@ static int qca808x_config_init(struct ph + + /* Active adc&vga on 802.3az for the link 1000M and 100M */ + ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7, +- QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN); ++ QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN); + if (ret) + return ret; + + /* Adjust the threshold on 802.3az for the link 1000M */ + ret = phy_write_mmd(phydev, MDIO_MMD_PCS, +- QCA808X_PHY_MMD3_AZ_TRAINING_CTRL, QCA808X_MMD3_AZ_TRAINING_VAL); ++ QCA808X_PHY_MMD3_AZ_TRAINING_CTRL, ++ QCA808X_MMD3_AZ_TRAINING_VAL); + if (ret) + return ret; + +@@ -1870,7 +1871,8 @@ static int qca808x_config_init(struct ph + + /* 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); ++ QCA808X_ADC_THRESHOLD_MASK, ++ QCA808X_ADC_THRESHOLD_100MV); + } + + static int qca808x_read_status(struct phy_device *phydev) +@@ -1883,7 +1885,7 @@ static int qca808x_read_status(struct ph + return ret; + + linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising, +- ret & MDIO_AN_10GBT_STAT_LP2_5G); ++ ret & MDIO_AN_10GBT_STAT_LP2_5G); + + ret = genphy_read_status(phydev); + if (ret) +@@ -1913,7 +1915,7 @@ static int qca808x_read_status(struct ph + */ + if (qca808x_has_fast_retrain_or_slave_seed(phydev)) { + if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR || +- qca808x_is_prefer_master(phydev)) { ++ qca808x_is_prefer_master(phydev)) { + qca808x_phy_ms_seed_enable(phydev, false); + } else { + qca808x_phy_ms_seed_enable(phydev, true); +@@ -2070,18 +2072,22 @@ static int qca808x_cable_test_get_status + ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D, + qca808x_cable_test_result_trans(pair_d)); + +- if (qca808x_cdt_fault_length_valid(pair_a)) +- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A, +- qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A)); +- if (qca808x_cdt_fault_length_valid(pair_b)) +- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B, +- qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B)); +- if (qca808x_cdt_fault_length_valid(pair_c)) +- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C, +- qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C)); +- if (qca808x_cdt_fault_length_valid(pair_d)) +- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D, +- qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D)); ++ if (qca808x_cdt_fault_length_valid(pair_a)) { ++ val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A); ++ ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A, val); ++ } ++ if (qca808x_cdt_fault_length_valid(pair_b)) { ++ val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B); ++ ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B, val); ++ } ++ if (qca808x_cdt_fault_length_valid(pair_c)) { ++ val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C); ++ ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C, val); ++ } ++ if (qca808x_cdt_fault_length_valid(pair_d)) { ++ val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D); ++ ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D, val); ++ } + + *finished = true; + +@@ -2148,8 +2154,9 @@ static void qca808x_link_change_notify(s + * 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); ++ 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[] = { diff --git a/target/linux/generic/backport-6.6/711-v6.8-01-net-phy-at803x-generalize-cdt-fault-length-function.patch b/target/linux/generic/backport-6.6/711-v6.8-01-net-phy-at803x-generalize-cdt-fault-length-function.patch new file mode 100644 index 0000000000..d7196da46e --- /dev/null +++ b/target/linux/generic/backport-6.6/711-v6.8-01-net-phy-at803x-generalize-cdt-fault-length-function.patch @@ -0,0 +1,62 @@ +From 22eb276098da820d9440fad22901f9b74ed4d659 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 4 Jan 2024 22:30:38 +0100 +Subject: [PATCH 1/4] net: phy: at803x: generalize cdt fault length function + +Generalize cable test fault length function since they all base on the +same magic values (already reverse engineered to understand the meaning +of it) to have consistenct values on every PHY. + +Signed-off-by: Christian Marangi +Reviewed-by: Simon Horman +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 13 ++++++------- + 1 file changed, 6 insertions(+), 7 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -1192,10 +1192,8 @@ static bool at803x_cdt_fault_length_vali + return false; + } + +-static int at803x_cdt_fault_length(u16 status) ++static int at803x_cdt_fault_length(int dt) + { +- int dt; +- + /* According to the datasheet the distance to the fault is + * DELTA_TIME * 0.824 meters. + * +@@ -1211,8 +1209,6 @@ static int at803x_cdt_fault_length(u16 s + * With a VF of 0.69 we get the factor 0.824 mentioned in the + * datasheet. + */ +- dt = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, status); +- + return (dt * 824) / 10; + } + +@@ -1265,9 +1261,11 @@ static int at803x_cable_test_one_pair(st + ethnl_cable_test_result(phydev, ethtool_pair[pair], + at803x_cable_test_result_trans(val)); + +- if (at803x_cdt_fault_length_valid(val)) ++ if (at803x_cdt_fault_length_valid(val)) { ++ val = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, val); + ethnl_cable_test_fault_length(phydev, ethtool_pair[pair], + at803x_cdt_fault_length(val)); ++ } + + return 1; + } +@@ -1992,7 +1990,8 @@ static int qca808x_cdt_fault_length(stru + if (val < 0) + return val; + +- return (FIELD_GET(QCA808X_CDT_DIAG_LENGTH, val) * 824) / 10; ++ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH, val); ++ return at803x_cdt_fault_length(val); + } + + static int qca808x_cable_test_start(struct phy_device *phydev) diff --git a/target/linux/generic/backport-6.6/711-v6.8-02-net-phy-at803x-refactor-qca808x-cable-test-get-statu.patch b/target/linux/generic/backport-6.6/711-v6.8-02-net-phy-at803x-refactor-qca808x-cable-test-get-statu.patch new file mode 100644 index 0000000000..5d38fe7e91 --- /dev/null +++ b/target/linux/generic/backport-6.6/711-v6.8-02-net-phy-at803x-refactor-qca808x-cable-test-get-statu.patch @@ -0,0 +1,118 @@ +From e0e9ada1df6133513249861c1d91c1dbefd9383b Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 4 Jan 2024 22:30:39 +0100 +Subject: [PATCH 2/4] net: phy: at803x: refactor qca808x cable test get status + function + +Refactor qca808x cable test get status function to remove code +duplication and clean things up. + +The same logic is applied to each pair hence it can be generalized and +moved to a common function. + +Signed-off-by: Christian Marangi +Reviewed-by: Simon Horman +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 80 ++++++++++++++++++++++++---------------- + 1 file changed, 49 insertions(+), 31 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -2035,10 +2035,43 @@ static int qca808x_cable_test_start(stru + return 0; + } + ++static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair, ++ u16 status) ++{ ++ u16 pair_code; ++ int length; ++ ++ switch (pair) { ++ case ETHTOOL_A_CABLE_PAIR_A: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status); ++ break; ++ case ETHTOOL_A_CABLE_PAIR_B: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status); ++ break; ++ case ETHTOOL_A_CABLE_PAIR_C: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status); ++ break; ++ case ETHTOOL_A_CABLE_PAIR_D: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ ethnl_cable_test_result(phydev, pair, ++ qca808x_cable_test_result_trans(pair_code)); ++ ++ if (qca808x_cdt_fault_length_valid(pair_code)) { ++ length = qca808x_cdt_fault_length(phydev, pair); ++ ethnl_cable_test_fault_length(phydev, pair, length); ++ } ++ ++ return 0; ++} ++ + static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished) + { + int ret, val; +- int pair_a, pair_b, pair_c, pair_d; + + *finished = false; + +@@ -2057,36 +2090,21 @@ static int qca808x_cable_test_get_status + if (val < 0) + return val; + +- pair_a = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, val); +- pair_b = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, val); +- pair_c = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, val); +- pair_d = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, val); +- +- ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A, +- qca808x_cable_test_result_trans(pair_a)); +- ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_B, +- qca808x_cable_test_result_trans(pair_b)); +- ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_C, +- qca808x_cable_test_result_trans(pair_c)); +- ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D, +- qca808x_cable_test_result_trans(pair_d)); +- +- if (qca808x_cdt_fault_length_valid(pair_a)) { +- val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A); +- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A, val); +- } +- if (qca808x_cdt_fault_length_valid(pair_b)) { +- val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B); +- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B, val); +- } +- if (qca808x_cdt_fault_length_valid(pair_c)) { +- val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C); +- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C, val); +- } +- if (qca808x_cdt_fault_length_valid(pair_d)) { +- val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D); +- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D, val); +- } ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val); ++ if (ret) ++ return ret; + + *finished = true; + diff --git a/target/linux/generic/backport-6.6/711-v6.8-03-net-phy-at803x-add-support-for-cdt-cross-short-test-.patch b/target/linux/generic/backport-6.6/711-v6.8-03-net-phy-at803x-add-support-for-cdt-cross-short-test-.patch new file mode 100644 index 0000000000..d5793860a9 --- /dev/null +++ b/target/linux/generic/backport-6.6/711-v6.8-03-net-phy-at803x-add-support-for-cdt-cross-short-test-.patch @@ -0,0 +1,182 @@ +From ea73e5ea442ee2aade67b1fb1233ccb3cbea2ceb Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 4 Jan 2024 22:30:40 +0100 +Subject: [PATCH 3/4] net: phy: at803x: add support for cdt cross short test + for qca808x + +QCA808x PHY Family supports Cable Diagnostic Test also for Cross Pair +Short. + +Add all the define to make enable and support these additional tests. + +Cross Short test was previously disabled by default, this is now changed +and enabled by default. In this mode, the mask changed a bit and length +is shifted based on the fault condition. + +Signed-off-by: Christian Marangi +Reviewed-by: Simon Horman +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 86 ++++++++++++++++++++++++++++++++-------- + 1 file changed, 69 insertions(+), 17 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -254,6 +254,7 @@ + + #define QCA808X_CDT_ENABLE_TEST BIT(15) + #define QCA808X_CDT_INTER_CHECK_DIS BIT(13) ++#define QCA808X_CDT_STATUS BIT(11) + #define QCA808X_CDT_LENGTH_UNIT BIT(10) + + #define QCA808X_MMD3_CDT_STATUS 0x8064 +@@ -261,16 +262,44 @@ + #define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066 + #define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067 + #define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068 +-#define QCA808X_CDT_DIAG_LENGTH GENMASK(7, 0) ++#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8) ++#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0) + + #define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12) + #define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8) + #define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4) + #define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0) +-#define QCA808X_CDT_STATUS_STAT_FAIL 0 +-#define QCA808X_CDT_STATUS_STAT_NORMAL 1 +-#define QCA808X_CDT_STATUS_STAT_OPEN 2 +-#define QCA808X_CDT_STATUS_STAT_SHORT 3 ++ ++#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0) ++#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0) ++#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1) ++#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2) ++#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3) ++ ++#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2) ++#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1) ++#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2) ++#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3) ++ ++/* NORMAL are MDI with type set to 0 */ ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1 ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ ++ QCA808X_CDT_STATUS_STAT_MDI1) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ ++ QCA808X_CDT_STATUS_STAT_MDI1) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2 ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ ++ QCA808X_CDT_STATUS_STAT_MDI2) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ ++ QCA808X_CDT_STATUS_STAT_MDI2) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3 ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ ++ QCA808X_CDT_STATUS_STAT_MDI3) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ ++ QCA808X_CDT_STATUS_STAT_MDI3) ++ ++/* Added for reference of existence but should be handled by wait_for_completion already */ ++#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3)) + + /* QCA808X 1G chip type */ + #define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d +@@ -1941,8 +1970,17 @@ static int qca808x_soft_reset(struct phy + static bool qca808x_cdt_fault_length_valid(int cdt_code) + { + switch (cdt_code) { +- case QCA808X_CDT_STATUS_STAT_SHORT: +- case QCA808X_CDT_STATUS_STAT_OPEN: ++ case QCA808X_CDT_STATUS_STAT_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: + return true; + default: + return false; +@@ -1954,17 +1992,28 @@ static int qca808x_cable_test_result_tra + switch (cdt_code) { + case QCA808X_CDT_STATUS_STAT_NORMAL: + return ETHTOOL_A_CABLE_RESULT_CODE_OK; +- case QCA808X_CDT_STATUS_STAT_SHORT: ++ case QCA808X_CDT_STATUS_STAT_SAME_SHORT: + return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; +- case QCA808X_CDT_STATUS_STAT_OPEN: ++ case QCA808X_CDT_STATUS_STAT_SAME_OPEN: + return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: ++ return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT; + case QCA808X_CDT_STATUS_STAT_FAIL: + default: + return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; + } + } + +-static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair) ++static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair, ++ int result) + { + int val; + u32 cdt_length_reg = 0; +@@ -1990,7 +2039,11 @@ static int qca808x_cdt_fault_length(stru + if (val < 0) + return val; + +- val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH, val); ++ if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT) ++ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val); ++ else ++ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val); ++ + return at803x_cdt_fault_length(val); + } + +@@ -2038,8 +2091,8 @@ static int qca808x_cable_test_start(stru + static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair, + u16 status) + { ++ int length, result; + u16 pair_code; +- int length; + + switch (pair) { + case ETHTOOL_A_CABLE_PAIR_A: +@@ -2058,11 +2111,11 @@ static int qca808x_cable_test_get_pair_s + return -EINVAL; + } + +- ethnl_cable_test_result(phydev, pair, +- qca808x_cable_test_result_trans(pair_code)); ++ result = qca808x_cable_test_result_trans(pair_code); ++ ethnl_cable_test_result(phydev, pair, result); + + if (qca808x_cdt_fault_length_valid(pair_code)) { +- length = qca808x_cdt_fault_length(phydev, pair); ++ length = qca808x_cdt_fault_length(phydev, pair, result); + ethnl_cable_test_fault_length(phydev, pair, length); + } + +@@ -2076,8 +2129,7 @@ static int qca808x_cable_test_get_status + *finished = false; + + val = QCA808X_CDT_ENABLE_TEST | +- QCA808X_CDT_LENGTH_UNIT | +- QCA808X_CDT_INTER_CHECK_DIS; ++ QCA808X_CDT_LENGTH_UNIT; + ret = at803x_cdt_start(phydev, val); + if (ret) + return ret; diff --git a/target/linux/generic/backport-6.6/711-v6.8-04-net-phy-at803x-make-read_status-more-generic.patch b/target/linux/generic/backport-6.6/711-v6.8-04-net-phy-at803x-make-read_status-more-generic.patch new file mode 100644 index 0000000000..c146e502fe --- /dev/null +++ b/target/linux/generic/backport-6.6/711-v6.8-04-net-phy-at803x-make-read_status-more-generic.patch @@ -0,0 +1,62 @@ +From c34d9452d4e5d98a655d7b625e85466320885416 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 4 Jan 2024 22:30:41 +0100 +Subject: [PATCH 4/4] net: phy: at803x: make read_status more generic + +Make read_status more generic in preparation on moving it to shared +library as other PHY Family Driver will have the exact same +implementation. + +The only specific part was a check for AR8031/33 if 1000basex was used. +The check is moved to a dedicated function specific for those PHYs. + +Signed-off-by: Christian Marangi +Reviewed-by: Simon Horman +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 17 ++++++++++++----- + 1 file changed, 12 insertions(+), 5 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -1020,13 +1020,9 @@ static int at803x_read_specific_status(s + + static int at803x_read_status(struct phy_device *phydev) + { +- struct at803x_priv *priv = phydev->priv; + struct at803x_ss_mask ss_mask = { 0 }; + int err, old_link = phydev->link; + +- if (priv->is_1000basex) +- return genphy_c37_read_status(phydev); +- + /* Update the link, but return if there was an error */ + err = genphy_update_link(phydev); + if (err) +@@ -1618,6 +1614,17 @@ static int at8031_config_intr(struct phy + return at803x_config_intr(phydev); + } + ++/* AR8031 and AR8033 share the same read status logic */ ++static int at8031_read_status(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ ++ if (priv->is_1000basex) ++ return genphy_c37_read_status(phydev); ++ ++ return at803x_read_status(phydev); ++} ++ + /* AR8031 and AR8035 share the same cable test get status reg */ + static int at8031_cable_test_get_status(struct phy_device *phydev, + bool *finished) +@@ -2281,7 +2288,7 @@ static struct phy_driver at803x_driver[] + .read_page = at803x_read_page, + .write_page = at803x_write_page, + .get_features = at803x_get_features, +- .read_status = at803x_read_status, ++ .read_status = at8031_read_status, + .config_intr = at8031_config_intr, + .handle_interrupt = at803x_handle_interrupt, + .get_tunable = at803x_get_tunable, diff --git a/target/linux/generic/backport-6.6/712-v6.9-net-phy-at803x-add-LED-support-for-qca808x.patch b/target/linux/generic/backport-6.6/712-v6.9-net-phy-at803x-add-LED-support-for-qca808x.patch new file mode 100644 index 0000000000..36675e7588 --- /dev/null +++ b/target/linux/generic/backport-6.6/712-v6.9-net-phy-at803x-add-LED-support-for-qca808x.patch @@ -0,0 +1,408 @@ +From 7196062b64ee470b91015f3d2e82d225948258ea Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 25 Jan 2024 21:37:01 +0100 +Subject: [PATCH 5/5] net: phy: at803x: add LED support for qca808x + +Add LED support for QCA8081 PHY. + +Documentation for this LEDs PHY is very scarce even with NDA access +to Documentation for OEMs. Only the blink pattern are documented and are +very confusing most of the time. No documentation is present about +forcing the LED on/off or to always blink. + +Those settings were reversed by poking the regs and trying to find the +correct bits to trigger these modes. Some bits mode are not clear and +maybe the documentation option are not 100% correct. For the sake of LED +support the reversed option are enough to add support for current LED +APIs. + +Supported HW control modes are: +- tx +- rx +- link_10 +- link_100 +- link_1000 +- link_2500 +- half_duplex +- full_duplex + +Also add support for LED polarity set to set LED polarity to active +high or low. QSDK sets this value to high by default but PHY reset value +doesn't have this enabled by default. + +QSDK also sets 2 additional bits but their usage is not clear, info about +this is added in the header. It was verified that for correct function +of the LED if active high is needed, only BIT 6 is needed. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20240125203702.4552-6-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/at803x.c | 327 +++++++++++++++++++++++++++++++++++++++ + 1 file changed, 327 insertions(+) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -301,6 +301,87 @@ + /* Added for reference of existence but should be handled by wait_for_completion already */ + #define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3)) + ++#define QCA808X_MMD7_LED_GLOBAL 0x8073 ++#define QCA808X_LED_BLINK_1 GENMASK(11, 6) ++#define QCA808X_LED_BLINK_2 GENMASK(5, 0) ++/* Values are the same for both BLINK_1 and BLINK_2 */ ++#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3) ++#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0) ++#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1) ++#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2) ++#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3) ++#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4) ++#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5) ++#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6) ++#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7) ++#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0) ++#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0) ++#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1) ++#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2) ++#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3) ++#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4) ++#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5) ++#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6) ++#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7) ++ ++#define QCA808X_MMD7_LED2_CTRL 0x8074 ++#define QCA808X_MMD7_LED2_FORCE_CTRL 0x8075 ++#define QCA808X_MMD7_LED1_CTRL 0x8076 ++#define QCA808X_MMD7_LED1_FORCE_CTRL 0x8077 ++#define QCA808X_MMD7_LED0_CTRL 0x8078 ++#define QCA808X_MMD7_LED_CTRL(x) (0x8078 - ((x) * 2)) ++ ++/* LED hw control pattern is the same for every LED */ ++#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0) ++#define QCA808X_LED_SPEED2500_ON BIT(15) ++#define QCA808X_LED_SPEED2500_BLINK BIT(14) ++/* Follow blink trigger even if duplex or speed condition doesn't match */ ++#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13) ++#define QCA808X_LED_FULL_DUPLEX_ON BIT(12) ++#define QCA808X_LED_HALF_DUPLEX_ON BIT(11) ++#define QCA808X_LED_TX_BLINK BIT(10) ++#define QCA808X_LED_RX_BLINK BIT(9) ++#define QCA808X_LED_TX_ON_10MS BIT(8) ++#define QCA808X_LED_RX_ON_10MS BIT(7) ++#define QCA808X_LED_SPEED1000_ON BIT(6) ++#define QCA808X_LED_SPEED100_ON BIT(5) ++#define QCA808X_LED_SPEED10_ON BIT(4) ++#define QCA808X_LED_COLLISION_BLINK BIT(3) ++#define QCA808X_LED_SPEED1000_BLINK BIT(2) ++#define QCA808X_LED_SPEED100_BLINK BIT(1) ++#define QCA808X_LED_SPEED10_BLINK BIT(0) ++ ++#define QCA808X_MMD7_LED0_FORCE_CTRL 0x8079 ++#define QCA808X_MMD7_LED_FORCE_CTRL(x) (0x8079 - ((x) * 2)) ++ ++/* LED force ctrl is the same for every LED ++ * No documentation exist for this, not even internal one ++ * with NDA as QCOM gives only info about configuring ++ * hw control pattern rules and doesn't indicate any way ++ * to force the LED to specific mode. ++ * These define comes from reverse and testing and maybe ++ * lack of some info or some info are not entirely correct. ++ * For the basic LED control and hw control these finding ++ * are enough to support LED control in all the required APIs. ++ * ++ * On doing some comparison with implementation with qca807x, ++ * it was found that it's 1:1 equal to it and confirms all the ++ * reverse done. It was also found further specification with the ++ * force mode and the blink modes. ++ */ ++#define QCA808X_LED_FORCE_EN BIT(15) ++#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13) ++#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3) ++#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2) ++#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1) ++#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0) ++ ++#define QCA808X_MMD7_LED_POLARITY_CTRL 0x901a ++/* QSDK sets by default 0x46 to this reg that sets BIT 6 for ++ * LED to active high. It's not clear what BIT 3 and BIT 4 does. ++ */ ++#define QCA808X_LED_ACTIVE_HIGH BIT(6) ++ + /* QCA808X 1G chip type */ + #define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d + #define QCA808X_PHY_CHIP_TYPE_1G BIT(0) +@@ -346,6 +427,7 @@ struct at803x_priv { + struct regulator_dev *vddio_rdev; + struct regulator_dev *vddh_rdev; + u64 stats[ARRAY_SIZE(qca83xx_hw_stats)]; ++ int led_polarity_mode; + }; + + struct at803x_context { +@@ -706,6 +788,9 @@ static int at803x_probe(struct phy_devic + if (!priv) + return -ENOMEM; + ++ /* Init LED polarity mode to -1 */ ++ priv->led_polarity_mode = -1; ++ + phydev->priv = priv; + + ret = at803x_parse_dt(phydev); +@@ -2235,6 +2320,242 @@ static void qca808x_link_change_notify(s + phydev->link ? QCA8081_PHY_FIFO_RSTN : 0); + } + ++static int qca808x_led_parse_netdev(struct phy_device *phydev, unsigned long rules, ++ u16 *offload_trigger) ++{ ++ /* Parsing specific to netdev trigger */ ++ if (test_bit(TRIGGER_NETDEV_TX, &rules)) ++ *offload_trigger |= QCA808X_LED_TX_BLINK; ++ if (test_bit(TRIGGER_NETDEV_RX, &rules)) ++ *offload_trigger |= QCA808X_LED_RX_BLINK; ++ if (test_bit(TRIGGER_NETDEV_LINK_10, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED10_ON; ++ if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED100_ON; ++ if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED1000_ON; ++ if (test_bit(TRIGGER_NETDEV_LINK_2500, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED2500_ON; ++ if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules)) ++ *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON; ++ if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules)) ++ *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON; ++ ++ if (rules && !*offload_trigger) ++ return -EOPNOTSUPP; ++ ++ /* Enable BLINK_CHECK_BYPASS by default to make the LED ++ * blink even with duplex or speed mode not enabled. ++ */ ++ *offload_trigger |= QCA808X_LED_BLINK_CHECK_BYPASS; ++ ++ return 0; ++} ++ ++static int qca808x_led_hw_control_enable(struct phy_device *phydev, u8 index) ++{ ++ u16 reg; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_FORCE_CTRL(index); ++ ++ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_FORCE_EN); ++} ++ ++static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ u16 offload_trigger = 0; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ return qca808x_led_parse_netdev(phydev, rules, &offload_trigger); ++} ++ ++static int qca808x_led_hw_control_set(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ u16 reg, offload_trigger = 0; ++ int ret; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_CTRL(index); ++ ++ ret = qca808x_led_parse_netdev(phydev, rules, &offload_trigger); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_led_hw_control_enable(phydev, index); ++ if (ret) ++ return ret; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_PATTERN_MASK, ++ offload_trigger); ++} ++ ++static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index) ++{ ++ u16 reg; ++ int val; ++ ++ if (index > 2) ++ return false; ++ ++ reg = QCA808X_MMD7_LED_FORCE_CTRL(index); ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); ++ ++ return !(val & QCA808X_LED_FORCE_EN); ++} ++ ++static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index, ++ unsigned long *rules) ++{ ++ u16 reg; ++ int val; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ /* Check if we have hw control enabled */ ++ if (qca808x_led_hw_control_status(phydev, index)) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_CTRL(index); ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); ++ if (val & QCA808X_LED_TX_BLINK) ++ set_bit(TRIGGER_NETDEV_TX, rules); ++ if (val & QCA808X_LED_RX_BLINK) ++ set_bit(TRIGGER_NETDEV_RX, rules); ++ if (val & QCA808X_LED_SPEED10_ON) ++ set_bit(TRIGGER_NETDEV_LINK_10, rules); ++ if (val & QCA808X_LED_SPEED100_ON) ++ set_bit(TRIGGER_NETDEV_LINK_100, rules); ++ if (val & QCA808X_LED_SPEED1000_ON) ++ set_bit(TRIGGER_NETDEV_LINK_1000, rules); ++ if (val & QCA808X_LED_SPEED2500_ON) ++ set_bit(TRIGGER_NETDEV_LINK_2500, rules); ++ if (val & QCA808X_LED_HALF_DUPLEX_ON) ++ set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules); ++ if (val & QCA808X_LED_FULL_DUPLEX_ON) ++ set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules); ++ ++ return 0; ++} ++ ++static int qca808x_led_hw_control_reset(struct phy_device *phydev, u8 index) ++{ ++ u16 reg; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_CTRL(index); ++ ++ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_PATTERN_MASK); ++} ++ ++static int qca808x_led_brightness_set(struct phy_device *phydev, ++ u8 index, enum led_brightness value) ++{ ++ u16 reg; ++ int ret; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ if (!value) { ++ ret = qca808x_led_hw_control_reset(phydev, index); ++ if (ret) ++ return ret; ++ } ++ ++ reg = QCA808X_MMD7_LED_FORCE_CTRL(index); ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, ++ QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON : ++ QCA808X_LED_FORCE_OFF); ++} ++ ++static int qca808x_led_blink_set(struct phy_device *phydev, u8 index, ++ unsigned long *delay_on, ++ unsigned long *delay_off) ++{ ++ int ret; ++ u16 reg; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_FORCE_CTRL(index); ++ ++ /* Set blink to 50% off, 50% on at 4Hz by default */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL, ++ QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK, ++ QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50); ++ if (ret) ++ return ret; ++ ++ /* We use BLINK_1 for normal blinking */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, ++ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1); ++ if (ret) ++ return ret; ++ ++ /* We set blink to 4Hz, aka 250ms */ ++ *delay_on = 250 / 2; ++ *delay_off = 250 / 2; ++ ++ return 0; ++} ++ ++static int qca808x_led_polarity_set(struct phy_device *phydev, int index, ++ unsigned long modes) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ bool active_low = false; ++ u32 mode; ++ ++ for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) { ++ switch (mode) { ++ case PHY_LED_ACTIVE_LOW: ++ active_low = true; ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ /* PHY polarity is global and can't be set per LED. ++ * To detect this, check if last requested polarity mode ++ * match the new one. ++ */ ++ if (priv->led_polarity_mode >= 0 && ++ priv->led_polarity_mode != active_low) { ++ phydev_err(phydev, "PHY polarity is global. Mismatched polarity on different LED\n"); ++ return -EINVAL; ++ } ++ ++ /* Save the last PHY polarity mode */ ++ priv->led_polarity_mode = active_low; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_AN, ++ QCA808X_MMD7_LED_POLARITY_CTRL, ++ QCA808X_LED_ACTIVE_HIGH, ++ active_low ? 0 : QCA808X_LED_ACTIVE_HIGH); ++} ++ + static struct phy_driver at803x_driver[] = { + { + /* Qualcomm Atheros AR8035 */ +@@ -2411,6 +2732,12 @@ static struct phy_driver at803x_driver[] + .cable_test_start = qca808x_cable_test_start, + .cable_test_get_status = qca808x_cable_test_get_status, + .link_change_notify = qca808x_link_change_notify, ++ .led_brightness_set = qca808x_led_brightness_set, ++ .led_blink_set = qca808x_led_blink_set, ++ .led_hw_is_supported = qca808x_led_hw_is_supported, ++ .led_hw_control_set = qca808x_led_hw_control_set, ++ .led_hw_control_get = qca808x_led_hw_control_get, ++ .led_polarity_set = qca808x_led_polarity_set, + }, }; + + module_phy_driver(at803x_driver); diff --git a/target/linux/generic/backport-6.6/713-v6.9-01-net-phy-move-at803x-PHY-driver-to-dedicated-director.patch b/target/linux/generic/backport-6.6/713-v6.9-01-net-phy-move-at803x-PHY-driver-to-dedicated-director.patch new file mode 100644 index 0000000000..8c9babea7b --- /dev/null +++ b/target/linux/generic/backport-6.6/713-v6.9-01-net-phy-move-at803x-PHY-driver-to-dedicated-director.patch @@ -0,0 +1,5598 @@ +From 9e56ff53b4115875667760445b028357848b4748 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 29 Jan 2024 15:15:19 +0100 +Subject: [PATCH 1/5] net: phy: move at803x PHY driver to dedicated directory + +In preparation for addition of other Qcom PHY and to tidy things up, +move the at803x PHY driver to dedicated directory. + +The same order in the Kconfig selection is saved. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20240129141600.2592-2-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/Kconfig | 7 +------ + drivers/net/phy/Makefile | 2 +- + drivers/net/phy/qcom/Kconfig | 7 +++++++ + drivers/net/phy/qcom/Makefile | 2 ++ + drivers/net/phy/{ => qcom}/at803x.c | 0 + 5 files changed, 11 insertions(+), 7 deletions(-) + create mode 100644 drivers/net/phy/qcom/Kconfig + create mode 100644 drivers/net/phy/qcom/Makefile + rename drivers/net/phy/{ => qcom}/at803x.c (100%) + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -277,12 +277,7 @@ config NXP_TJA11XX_PHY + help + Currently supports the NXP TJA1100 and TJA1101 PHY. + +-config AT803X_PHY +- tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs" +- depends on REGULATOR +- help +- Currently supports the AR8030, AR8031, AR8033, AR8035 and internal +- QCA8337(Internal qca8k PHY) model ++source "drivers/net/phy/qcom/Kconfig" + + config QSEMI_PHY + tristate "Quality Semiconductor PHYs" +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -34,7 +34,6 @@ obj-$(CONFIG_ADIN_PHY) += adin.o + obj-$(CONFIG_ADIN1100_PHY) += adin1100.o + obj-$(CONFIG_AMD_PHY) += amd.o + obj-$(CONFIG_AQUANTIA_PHY) += aquantia/ +-obj-$(CONFIG_AT803X_PHY) += at803x.o + obj-$(CONFIG_AX88796B_PHY) += ax88796b.o + obj-$(CONFIG_BCM54140_PHY) += bcm54140.o + obj-$(CONFIG_BCM63XX_PHY) += bcm63xx.o +@@ -75,6 +74,7 @@ obj-$(CONFIG_MOTORCOMM_PHY) += motorcomm + obj-$(CONFIG_NATIONAL_PHY) += national.o + obj-$(CONFIG_NXP_C45_TJA11XX_PHY) += nxp-c45-tja11xx.o + obj-$(CONFIG_NXP_TJA11XX_PHY) += nxp-tja11xx.o ++obj-y += qcom/ + obj-$(CONFIG_QSEMI_PHY) += qsemi.o + obj-$(CONFIG_REALTEK_PHY) += realtek.o + obj-$(CONFIG_RENESAS_PHY) += uPD60620.o +--- /dev/null ++++ b/drivers/net/phy/qcom/Kconfig +@@ -0,0 +1,7 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++config AT803X_PHY ++ tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs" ++ depends on REGULATOR ++ help ++ Currently supports the AR8030, AR8031, AR8033, AR8035 and internal ++ QCA8337(Internal qca8k PHY) model +--- /dev/null ++++ b/drivers/net/phy/qcom/Makefile +@@ -0,0 +1,2 @@ ++# SPDX-License-Identifier: GPL-2.0 ++obj-$(CONFIG_AT803X_PHY) += at803x.o +--- a/drivers/net/phy/at803x.c ++++ /dev/null +@@ -1,2759 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0+ +-/* +- * drivers/net/phy/at803x.c +- * +- * Driver for Qualcomm Atheros AR803x PHY +- * +- * Author: Matus Ujhelyi +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define AT803X_SPECIFIC_FUNCTION_CONTROL 0x10 +-#define AT803X_SFC_ASSERT_CRS BIT(11) +-#define AT803X_SFC_FORCE_LINK BIT(10) +-#define AT803X_SFC_MDI_CROSSOVER_MODE_M GENMASK(6, 5) +-#define AT803X_SFC_AUTOMATIC_CROSSOVER 0x3 +-#define AT803X_SFC_MANUAL_MDIX 0x1 +-#define AT803X_SFC_MANUAL_MDI 0x0 +-#define AT803X_SFC_SQE_TEST BIT(2) +-#define AT803X_SFC_POLARITY_REVERSAL BIT(1) +-#define AT803X_SFC_DISABLE_JABBER BIT(0) +- +-#define AT803X_SPECIFIC_STATUS 0x11 +-#define AT803X_SS_SPEED_MASK GENMASK(15, 14) +-#define AT803X_SS_SPEED_1000 2 +-#define AT803X_SS_SPEED_100 1 +-#define AT803X_SS_SPEED_10 0 +-#define AT803X_SS_DUPLEX BIT(13) +-#define AT803X_SS_SPEED_DUPLEX_RESOLVED BIT(11) +-#define AT803X_SS_MDIX BIT(6) +- +-#define QCA808X_SS_SPEED_MASK GENMASK(9, 7) +-#define QCA808X_SS_SPEED_2500 4 +- +-#define AT803X_INTR_ENABLE 0x12 +-#define AT803X_INTR_ENABLE_AUTONEG_ERR BIT(15) +-#define AT803X_INTR_ENABLE_SPEED_CHANGED BIT(14) +-#define AT803X_INTR_ENABLE_DUPLEX_CHANGED BIT(13) +-#define AT803X_INTR_ENABLE_PAGE_RECEIVED BIT(12) +-#define AT803X_INTR_ENABLE_LINK_FAIL BIT(11) +-#define AT803X_INTR_ENABLE_LINK_SUCCESS BIT(10) +-#define AT803X_INTR_ENABLE_LINK_FAIL_BX BIT(8) +-#define AT803X_INTR_ENABLE_LINK_SUCCESS_BX BIT(7) +-#define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE BIT(5) +-#define AT803X_INTR_ENABLE_POLARITY_CHANGED BIT(1) +-#define AT803X_INTR_ENABLE_WOL BIT(0) +- +-#define AT803X_INTR_STATUS 0x13 +- +-#define AT803X_SMART_SPEED 0x14 +-#define AT803X_SMART_SPEED_ENABLE BIT(5) +-#define AT803X_SMART_SPEED_RETRY_LIMIT_MASK GENMASK(4, 2) +-#define AT803X_SMART_SPEED_BYPASS_TIMER BIT(1) +-#define AT803X_CDT 0x16 +-#define AT803X_CDT_MDI_PAIR_MASK GENMASK(9, 8) +-#define AT803X_CDT_ENABLE_TEST BIT(0) +-#define AT803X_CDT_STATUS 0x1c +-#define AT803X_CDT_STATUS_STAT_NORMAL 0 +-#define AT803X_CDT_STATUS_STAT_SHORT 1 +-#define AT803X_CDT_STATUS_STAT_OPEN 2 +-#define AT803X_CDT_STATUS_STAT_FAIL 3 +-#define AT803X_CDT_STATUS_STAT_MASK GENMASK(9, 8) +-#define AT803X_CDT_STATUS_DELTA_TIME_MASK GENMASK(7, 0) +-#define AT803X_LED_CONTROL 0x18 +- +-#define AT803X_PHY_MMD3_WOL_CTRL 0x8012 +-#define AT803X_WOL_EN BIT(5) +-#define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C +-#define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B +-#define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A +-#define AT803X_REG_CHIP_CONFIG 0x1f +-#define AT803X_BT_BX_REG_SEL 0x8000 +- +-#define AT803X_DEBUG_ADDR 0x1D +-#define AT803X_DEBUG_DATA 0x1E +- +-#define AT803X_MODE_CFG_MASK 0x0F +-#define AT803X_MODE_CFG_BASET_RGMII 0x00 +-#define AT803X_MODE_CFG_BASET_SGMII 0x01 +-#define AT803X_MODE_CFG_BX1000_RGMII_50OHM 0x02 +-#define AT803X_MODE_CFG_BX1000_RGMII_75OHM 0x03 +-#define AT803X_MODE_CFG_BX1000_CONV_50OHM 0x04 +-#define AT803X_MODE_CFG_BX1000_CONV_75OHM 0x05 +-#define AT803X_MODE_CFG_FX100_RGMII_50OHM 0x06 +-#define AT803X_MODE_CFG_FX100_CONV_50OHM 0x07 +-#define AT803X_MODE_CFG_RGMII_AUTO_MDET 0x0B +-#define AT803X_MODE_CFG_FX100_RGMII_75OHM 0x0E +-#define AT803X_MODE_CFG_FX100_CONV_75OHM 0x0F +- +-#define AT803X_PSSR 0x11 /*PHY-Specific Status Register*/ +-#define AT803X_PSSR_MR_AN_COMPLETE 0x0200 +- +-#define AT803X_DEBUG_ANALOG_TEST_CTRL 0x00 +-#define QCA8327_DEBUG_MANU_CTRL_EN BIT(2) +-#define QCA8337_DEBUG_MANU_CTRL_EN GENMASK(3, 2) +-#define AT803X_DEBUG_RX_CLK_DLY_EN BIT(15) +- +-#define AT803X_DEBUG_SYSTEM_CTRL_MODE 0x05 +-#define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8) +- +-#define AT803X_DEBUG_REG_HIB_CTRL 0x0b +-#define AT803X_DEBUG_HIB_CTRL_SEL_RST_80U BIT(10) +-#define AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE BIT(13) +-#define AT803X_DEBUG_HIB_CTRL_PS_HIB_EN BIT(15) +- +-#define AT803X_DEBUG_REG_3C 0x3C +- +-#define AT803X_DEBUG_REG_GREEN 0x3D +-#define AT803X_DEBUG_GATE_CLK_IN1000 BIT(6) +- +-#define AT803X_DEBUG_REG_1F 0x1F +-#define AT803X_DEBUG_PLL_ON BIT(2) +-#define AT803X_DEBUG_RGMII_1V8 BIT(3) +- +-#define MDIO_AZ_DEBUG 0x800D +- +-/* AT803x supports either the XTAL input pad, an internal PLL or the +- * DSP as clock reference for the clock output pad. The XTAL reference +- * is only used for 25 MHz output, all other frequencies need the PLL. +- * The DSP as a clock reference is used in synchronous ethernet +- * applications. +- * +- * By default the PLL is only enabled if there is a link. Otherwise +- * the PHY will go into low power state and disabled the PLL. You can +- * set the PLL_ON bit (see debug register 0x1f) to keep the PLL always +- * enabled. +- */ +-#define AT803X_MMD7_CLK25M 0x8016 +-#define AT803X_CLK_OUT_MASK GENMASK(4, 2) +-#define AT803X_CLK_OUT_25MHZ_XTAL 0 +-#define AT803X_CLK_OUT_25MHZ_DSP 1 +-#define AT803X_CLK_OUT_50MHZ_PLL 2 +-#define AT803X_CLK_OUT_50MHZ_DSP 3 +-#define AT803X_CLK_OUT_62_5MHZ_PLL 4 +-#define AT803X_CLK_OUT_62_5MHZ_DSP 5 +-#define AT803X_CLK_OUT_125MHZ_PLL 6 +-#define AT803X_CLK_OUT_125MHZ_DSP 7 +- +-/* The AR8035 has another mask which is compatible with the AR8031/AR8033 mask +- * but doesn't support choosing between XTAL/PLL and DSP. +- */ +-#define AT8035_CLK_OUT_MASK GENMASK(4, 3) +- +-#define AT803X_CLK_OUT_STRENGTH_MASK GENMASK(8, 7) +-#define AT803X_CLK_OUT_STRENGTH_FULL 0 +-#define AT803X_CLK_OUT_STRENGTH_HALF 1 +-#define AT803X_CLK_OUT_STRENGTH_QUARTER 2 +- +-#define AT803X_DEFAULT_DOWNSHIFT 5 +-#define AT803X_MIN_DOWNSHIFT 2 +-#define AT803X_MAX_DOWNSHIFT 9 +- +-#define AT803X_MMD3_SMARTEEE_CTL1 0x805b +-#define AT803X_MMD3_SMARTEEE_CTL2 0x805c +-#define AT803X_MMD3_SMARTEEE_CTL3 0x805d +-#define AT803X_MMD3_SMARTEEE_CTL3_LPI_EN BIT(8) +- +-#define ATH9331_PHY_ID 0x004dd041 +-#define ATH8030_PHY_ID 0x004dd076 +-#define ATH8031_PHY_ID 0x004dd074 +-#define ATH8032_PHY_ID 0x004dd023 +-#define ATH8035_PHY_ID 0x004dd072 +-#define AT8030_PHY_ID_MASK 0xffffffef +- +-#define QCA8081_PHY_ID 0x004dd101 +- +-#define QCA8327_A_PHY_ID 0x004dd033 +-#define QCA8327_B_PHY_ID 0x004dd034 +-#define QCA8337_PHY_ID 0x004dd036 +-#define QCA9561_PHY_ID 0x004dd042 +-#define QCA8K_PHY_ID_MASK 0xffffffff +- +-#define QCA8K_DEVFLAGS_REVISION_MASK GENMASK(2, 0) +- +-#define AT803X_PAGE_FIBER 0 +-#define AT803X_PAGE_COPPER 1 +- +-/* don't turn off internal PLL */ +-#define AT803X_KEEP_PLL_ENABLED BIT(0) +-#define AT803X_DISABLE_SMARTEEE BIT(1) +- +-/* disable hibernation mode */ +-#define AT803X_DISABLE_HIBERNATION_MODE BIT(2) +- +-/* ADC threshold */ +-#define QCA808X_PHY_DEBUG_ADC_THRESHOLD 0x2c80 +-#define QCA808X_ADC_THRESHOLD_MASK GENMASK(7, 0) +-#define QCA808X_ADC_THRESHOLD_80MV 0 +-#define QCA808X_ADC_THRESHOLD_100MV 0xf0 +-#define QCA808X_ADC_THRESHOLD_200MV 0x0f +-#define QCA808X_ADC_THRESHOLD_300MV 0xff +- +-/* CLD control */ +-#define QCA808X_PHY_MMD3_ADDR_CLD_CTRL7 0x8007 +-#define QCA808X_8023AZ_AFE_CTRL_MASK GENMASK(8, 4) +-#define QCA808X_8023AZ_AFE_EN 0x90 +- +-/* AZ control */ +-#define QCA808X_PHY_MMD3_AZ_TRAINING_CTRL 0x8008 +-#define QCA808X_MMD3_AZ_TRAINING_VAL 0x1c32 +- +-#define QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB 0x8014 +-#define QCA808X_MSE_THRESHOLD_20DB_VALUE 0x529 +- +-#define QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB 0x800E +-#define QCA808X_MSE_THRESHOLD_17DB_VALUE 0x341 +- +-#define QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB 0x801E +-#define QCA808X_MSE_THRESHOLD_27DB_VALUE 0x419 +- +-#define QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB 0x8020 +-#define QCA808X_MSE_THRESHOLD_28DB_VALUE 0x341 +- +-#define QCA808X_PHY_MMD7_TOP_OPTION1 0x901c +-#define QCA808X_TOP_OPTION1_DATA 0x0 +- +-#define QCA808X_PHY_MMD3_DEBUG_1 0xa100 +-#define QCA808X_MMD3_DEBUG_1_VALUE 0x9203 +-#define QCA808X_PHY_MMD3_DEBUG_2 0xa101 +-#define QCA808X_MMD3_DEBUG_2_VALUE 0x48ad +-#define QCA808X_PHY_MMD3_DEBUG_3 0xa103 +-#define QCA808X_MMD3_DEBUG_3_VALUE 0x1698 +-#define QCA808X_PHY_MMD3_DEBUG_4 0xa105 +-#define QCA808X_MMD3_DEBUG_4_VALUE 0x8001 +-#define QCA808X_PHY_MMD3_DEBUG_5 0xa106 +-#define QCA808X_MMD3_DEBUG_5_VALUE 0x1111 +-#define QCA808X_PHY_MMD3_DEBUG_6 0xa011 +-#define QCA808X_MMD3_DEBUG_6_VALUE 0x5f85 +- +-/* master/slave seed config */ +-#define QCA808X_PHY_DEBUG_LOCAL_SEED 9 +-#define QCA808X_MASTER_SLAVE_SEED_ENABLE BIT(1) +-#define QCA808X_MASTER_SLAVE_SEED_CFG GENMASK(12, 2) +-#define QCA808X_MASTER_SLAVE_SEED_RANGE 0x32 +- +-/* Hibernation yields lower power consumpiton in contrast with normal operation mode. +- * when the copper cable is unplugged, the PHY enters into hibernation mode in about 10s. +- */ +-#define QCA808X_DBG_AN_TEST 0xb +-#define QCA808X_HIBERNATION_EN BIT(15) +- +-#define QCA808X_CDT_ENABLE_TEST BIT(15) +-#define QCA808X_CDT_INTER_CHECK_DIS BIT(13) +-#define QCA808X_CDT_STATUS BIT(11) +-#define QCA808X_CDT_LENGTH_UNIT BIT(10) +- +-#define QCA808X_MMD3_CDT_STATUS 0x8064 +-#define QCA808X_MMD3_CDT_DIAG_PAIR_A 0x8065 +-#define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066 +-#define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067 +-#define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068 +-#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8) +-#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0) +- +-#define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12) +-#define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8) +-#define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4) +-#define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0) +- +-#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0) +-#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0) +-#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1) +-#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2) +-#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3) +- +-#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2) +-#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1) +-#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2) +-#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3) +- +-/* NORMAL are MDI with type set to 0 */ +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1 +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ +- QCA808X_CDT_STATUS_STAT_MDI1) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ +- QCA808X_CDT_STATUS_STAT_MDI1) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2 +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ +- QCA808X_CDT_STATUS_STAT_MDI2) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ +- QCA808X_CDT_STATUS_STAT_MDI2) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3 +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ +- QCA808X_CDT_STATUS_STAT_MDI3) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ +- QCA808X_CDT_STATUS_STAT_MDI3) +- +-/* Added for reference of existence but should be handled by wait_for_completion already */ +-#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3)) +- +-#define QCA808X_MMD7_LED_GLOBAL 0x8073 +-#define QCA808X_LED_BLINK_1 GENMASK(11, 6) +-#define QCA808X_LED_BLINK_2 GENMASK(5, 0) +-/* Values are the same for both BLINK_1 and BLINK_2 */ +-#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3) +-#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0) +-#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1) +-#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2) +-#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3) +-#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4) +-#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5) +-#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6) +-#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7) +-#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0) +-#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0) +-#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1) +-#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2) +-#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3) +-#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4) +-#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5) +-#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6) +-#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7) +- +-#define QCA808X_MMD7_LED2_CTRL 0x8074 +-#define QCA808X_MMD7_LED2_FORCE_CTRL 0x8075 +-#define QCA808X_MMD7_LED1_CTRL 0x8076 +-#define QCA808X_MMD7_LED1_FORCE_CTRL 0x8077 +-#define QCA808X_MMD7_LED0_CTRL 0x8078 +-#define QCA808X_MMD7_LED_CTRL(x) (0x8078 - ((x) * 2)) +- +-/* LED hw control pattern is the same for every LED */ +-#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0) +-#define QCA808X_LED_SPEED2500_ON BIT(15) +-#define QCA808X_LED_SPEED2500_BLINK BIT(14) +-/* Follow blink trigger even if duplex or speed condition doesn't match */ +-#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13) +-#define QCA808X_LED_FULL_DUPLEX_ON BIT(12) +-#define QCA808X_LED_HALF_DUPLEX_ON BIT(11) +-#define QCA808X_LED_TX_BLINK BIT(10) +-#define QCA808X_LED_RX_BLINK BIT(9) +-#define QCA808X_LED_TX_ON_10MS BIT(8) +-#define QCA808X_LED_RX_ON_10MS BIT(7) +-#define QCA808X_LED_SPEED1000_ON BIT(6) +-#define QCA808X_LED_SPEED100_ON BIT(5) +-#define QCA808X_LED_SPEED10_ON BIT(4) +-#define QCA808X_LED_COLLISION_BLINK BIT(3) +-#define QCA808X_LED_SPEED1000_BLINK BIT(2) +-#define QCA808X_LED_SPEED100_BLINK BIT(1) +-#define QCA808X_LED_SPEED10_BLINK BIT(0) +- +-#define QCA808X_MMD7_LED0_FORCE_CTRL 0x8079 +-#define QCA808X_MMD7_LED_FORCE_CTRL(x) (0x8079 - ((x) * 2)) +- +-/* LED force ctrl is the same for every LED +- * No documentation exist for this, not even internal one +- * with NDA as QCOM gives only info about configuring +- * hw control pattern rules and doesn't indicate any way +- * to force the LED to specific mode. +- * These define comes from reverse and testing and maybe +- * lack of some info or some info are not entirely correct. +- * For the basic LED control and hw control these finding +- * are enough to support LED control in all the required APIs. +- * +- * On doing some comparison with implementation with qca807x, +- * it was found that it's 1:1 equal to it and confirms all the +- * reverse done. It was also found further specification with the +- * force mode and the blink modes. +- */ +-#define QCA808X_LED_FORCE_EN BIT(15) +-#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13) +-#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3) +-#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2) +-#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1) +-#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0) +- +-#define QCA808X_MMD7_LED_POLARITY_CTRL 0x901a +-/* QSDK sets by default 0x46 to this reg that sets BIT 6 for +- * LED to active high. It's not clear what BIT 3 and BIT 4 does. +- */ +-#define QCA808X_LED_ACTIVE_HIGH BIT(6) +- +-/* QCA808X 1G chip type */ +-#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"); +- +-enum stat_access_type { +- PHY, +- MMD +-}; +- +-struct at803x_hw_stat { +- const char *string; +- u8 reg; +- u32 mask; +- enum stat_access_type access_type; +-}; +- +-static struct at803x_hw_stat qca83xx_hw_stats[] = { +- { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY}, +- { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY}, +- { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD}, +-}; +- +-struct at803x_ss_mask { +- u16 speed_mask; +- u8 speed_shift; +-}; +- +-struct at803x_priv { +- int flags; +- u16 clk_25m_reg; +- u16 clk_25m_mask; +- u8 smarteee_lpi_tw_1g; +- u8 smarteee_lpi_tw_100m; +- bool is_fiber; +- bool is_1000basex; +- struct regulator_dev *vddio_rdev; +- struct regulator_dev *vddh_rdev; +- u64 stats[ARRAY_SIZE(qca83xx_hw_stats)]; +- int led_polarity_mode; +-}; +- +-struct at803x_context { +- u16 bmcr; +- u16 advertise; +- u16 control1000; +- u16 int_enable; +- u16 smart_speed; +- u16 led_control; +-}; +- +-static int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data) +-{ +- int ret; +- +- ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); +- if (ret < 0) +- return ret; +- +- return phy_write(phydev, AT803X_DEBUG_DATA, data); +-} +- +-static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg) +-{ +- int ret; +- +- ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); +- if (ret < 0) +- return ret; +- +- return phy_read(phydev, AT803X_DEBUG_DATA); +-} +- +-static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, +- u16 clear, u16 set) +-{ +- u16 val; +- int ret; +- +- ret = at803x_debug_reg_read(phydev, reg); +- if (ret < 0) +- return ret; +- +- val = ret & 0xffff; +- val &= ~clear; +- val |= set; +- +- return phy_write(phydev, AT803X_DEBUG_DATA, val); +-} +- +-static int at803x_write_page(struct phy_device *phydev, int page) +-{ +- int mask; +- int set; +- +- if (page == AT803X_PAGE_COPPER) { +- set = AT803X_BT_BX_REG_SEL; +- mask = 0; +- } else { +- set = 0; +- mask = AT803X_BT_BX_REG_SEL; +- } +- +- return __phy_modify(phydev, AT803X_REG_CHIP_CONFIG, mask, set); +-} +- +-static int at803x_read_page(struct phy_device *phydev) +-{ +- int ccr = __phy_read(phydev, AT803X_REG_CHIP_CONFIG); +- +- if (ccr < 0) +- return ccr; +- +- if (ccr & AT803X_BT_BX_REG_SEL) +- return AT803X_PAGE_COPPER; +- +- return AT803X_PAGE_FIBER; +-} +- +-static int at803x_enable_rx_delay(struct phy_device *phydev) +-{ +- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0, +- AT803X_DEBUG_RX_CLK_DLY_EN); +-} +- +-static int at803x_enable_tx_delay(struct phy_device *phydev) +-{ +- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0, +- AT803X_DEBUG_TX_CLK_DLY_EN); +-} +- +-static int at803x_disable_rx_delay(struct phy_device *phydev) +-{ +- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, +- AT803X_DEBUG_RX_CLK_DLY_EN, 0); +-} +- +-static int at803x_disable_tx_delay(struct phy_device *phydev) +-{ +- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, +- AT803X_DEBUG_TX_CLK_DLY_EN, 0); +-} +- +-/* save relevant PHY registers to private copy */ +-static void at803x_context_save(struct phy_device *phydev, +- struct at803x_context *context) +-{ +- context->bmcr = phy_read(phydev, MII_BMCR); +- context->advertise = phy_read(phydev, MII_ADVERTISE); +- context->control1000 = phy_read(phydev, MII_CTRL1000); +- context->int_enable = phy_read(phydev, AT803X_INTR_ENABLE); +- context->smart_speed = phy_read(phydev, AT803X_SMART_SPEED); +- context->led_control = phy_read(phydev, AT803X_LED_CONTROL); +-} +- +-/* restore relevant PHY registers from private copy */ +-static void at803x_context_restore(struct phy_device *phydev, +- const struct at803x_context *context) +-{ +- phy_write(phydev, MII_BMCR, context->bmcr); +- phy_write(phydev, MII_ADVERTISE, context->advertise); +- phy_write(phydev, MII_CTRL1000, context->control1000); +- phy_write(phydev, AT803X_INTR_ENABLE, context->int_enable); +- phy_write(phydev, AT803X_SMART_SPEED, context->smart_speed); +- phy_write(phydev, AT803X_LED_CONTROL, context->led_control); +-} +- +-static int at803x_set_wol(struct phy_device *phydev, +- struct ethtool_wolinfo *wol) +-{ +- int ret, irq_enabled; +- +- if (wol->wolopts & WAKE_MAGIC) { +- struct net_device *ndev = phydev->attached_dev; +- const u8 *mac; +- unsigned int i; +- static const unsigned int offsets[] = { +- AT803X_LOC_MAC_ADDR_32_47_OFFSET, +- AT803X_LOC_MAC_ADDR_16_31_OFFSET, +- AT803X_LOC_MAC_ADDR_0_15_OFFSET, +- }; +- +- if (!ndev) +- return -ENODEV; +- +- mac = (const u8 *)ndev->dev_addr; +- +- if (!is_valid_ether_addr(mac)) +- return -EINVAL; +- +- for (i = 0; i < 3; i++) +- phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i], +- mac[(i * 2) + 1] | (mac[(i * 2)] << 8)); +- +- /* Enable WOL interrupt */ +- ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL); +- if (ret) +- return ret; +- } else { +- /* Disable WOL interrupt */ +- ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0); +- if (ret) +- return ret; +- } +- +- /* Clear WOL status */ +- ret = phy_read(phydev, AT803X_INTR_STATUS); +- if (ret < 0) +- return ret; +- +- /* Check if there are other interrupts except for WOL triggered when PHY is +- * in interrupt mode, only the interrupts enabled by AT803X_INTR_ENABLE can +- * be passed up to the interrupt PIN. +- */ +- irq_enabled = phy_read(phydev, AT803X_INTR_ENABLE); +- if (irq_enabled < 0) +- return irq_enabled; +- +- irq_enabled &= ~AT803X_INTR_ENABLE_WOL; +- if (ret & irq_enabled && !phy_polling_mode(phydev)) +- phy_trigger_machine(phydev); +- +- return 0; +-} +- +-static void at803x_get_wol(struct phy_device *phydev, +- struct ethtool_wolinfo *wol) +-{ +- int value; +- +- wol->supported = WAKE_MAGIC; +- wol->wolopts = 0; +- +- value = phy_read(phydev, AT803X_INTR_ENABLE); +- if (value < 0) +- return; +- +- if (value & AT803X_INTR_ENABLE_WOL) +- wol->wolopts |= WAKE_MAGIC; +-} +- +-static int qca83xx_get_sset_count(struct phy_device *phydev) +-{ +- return ARRAY_SIZE(qca83xx_hw_stats); +-} +- +-static void qca83xx_get_strings(struct phy_device *phydev, u8 *data) +-{ +- int i; +- +- for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) { +- strscpy(data + i * ETH_GSTRING_LEN, +- qca83xx_hw_stats[i].string, ETH_GSTRING_LEN); +- } +-} +- +-static u64 qca83xx_get_stat(struct phy_device *phydev, int i) +-{ +- struct at803x_hw_stat stat = qca83xx_hw_stats[i]; +- struct at803x_priv *priv = phydev->priv; +- int val; +- u64 ret; +- +- if (stat.access_type == MMD) +- val = phy_read_mmd(phydev, MDIO_MMD_PCS, stat.reg); +- else +- val = phy_read(phydev, stat.reg); +- +- if (val < 0) { +- ret = U64_MAX; +- } else { +- val = val & stat.mask; +- priv->stats[i] += val; +- ret = priv->stats[i]; +- } +- +- return ret; +-} +- +-static void qca83xx_get_stats(struct phy_device *phydev, +- struct ethtool_stats *stats, u64 *data) +-{ +- int i; +- +- for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) +- data[i] = qca83xx_get_stat(phydev, i); +-} +- +-static int at803x_suspend(struct phy_device *phydev) +-{ +- int value; +- int wol_enabled; +- +- value = phy_read(phydev, AT803X_INTR_ENABLE); +- wol_enabled = value & AT803X_INTR_ENABLE_WOL; +- +- if (wol_enabled) +- value = BMCR_ISOLATE; +- else +- value = BMCR_PDOWN; +- +- phy_modify(phydev, MII_BMCR, 0, value); +- +- return 0; +-} +- +-static int at803x_resume(struct phy_device *phydev) +-{ +- return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0); +-} +- +-static int at803x_parse_dt(struct phy_device *phydev) +-{ +- struct device_node *node = phydev->mdio.dev.of_node; +- struct at803x_priv *priv = phydev->priv; +- u32 freq, strength, tw; +- unsigned int sel; +- int ret; +- +- if (!IS_ENABLED(CONFIG_OF_MDIO)) +- return 0; +- +- if (of_property_read_bool(node, "qca,disable-smarteee")) +- priv->flags |= AT803X_DISABLE_SMARTEEE; +- +- if (of_property_read_bool(node, "qca,disable-hibernation-mode")) +- priv->flags |= AT803X_DISABLE_HIBERNATION_MODE; +- +- if (!of_property_read_u32(node, "qca,smarteee-tw-us-1g", &tw)) { +- if (!tw || tw > 255) { +- phydev_err(phydev, "invalid qca,smarteee-tw-us-1g\n"); +- return -EINVAL; +- } +- priv->smarteee_lpi_tw_1g = tw; +- } +- +- if (!of_property_read_u32(node, "qca,smarteee-tw-us-100m", &tw)) { +- if (!tw || tw > 255) { +- phydev_err(phydev, "invalid qca,smarteee-tw-us-100m\n"); +- return -EINVAL; +- } +- priv->smarteee_lpi_tw_100m = tw; +- } +- +- ret = of_property_read_u32(node, "qca,clk-out-frequency", &freq); +- if (!ret) { +- switch (freq) { +- case 25000000: +- sel = AT803X_CLK_OUT_25MHZ_XTAL; +- break; +- case 50000000: +- sel = AT803X_CLK_OUT_50MHZ_PLL; +- break; +- case 62500000: +- sel = AT803X_CLK_OUT_62_5MHZ_PLL; +- break; +- case 125000000: +- sel = AT803X_CLK_OUT_125MHZ_PLL; +- break; +- default: +- phydev_err(phydev, "invalid qca,clk-out-frequency\n"); +- return -EINVAL; +- } +- +- priv->clk_25m_reg |= FIELD_PREP(AT803X_CLK_OUT_MASK, sel); +- priv->clk_25m_mask |= AT803X_CLK_OUT_MASK; +- } +- +- ret = of_property_read_u32(node, "qca,clk-out-strength", &strength); +- if (!ret) { +- priv->clk_25m_mask |= AT803X_CLK_OUT_STRENGTH_MASK; +- switch (strength) { +- case AR803X_STRENGTH_FULL: +- priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_FULL; +- break; +- case AR803X_STRENGTH_HALF: +- priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_HALF; +- break; +- case AR803X_STRENGTH_QUARTER: +- priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_QUARTER; +- break; +- default: +- phydev_err(phydev, "invalid qca,clk-out-strength\n"); +- return -EINVAL; +- } +- } +- +- return 0; +-} +- +-static int at803x_probe(struct phy_device *phydev) +-{ +- struct device *dev = &phydev->mdio.dev; +- struct at803x_priv *priv; +- int ret; +- +- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- /* Init LED polarity mode to -1 */ +- priv->led_polarity_mode = -1; +- +- phydev->priv = priv; +- +- ret = at803x_parse_dt(phydev); +- if (ret) +- return ret; +- +- return 0; +-} +- +-static int at803x_get_features(struct phy_device *phydev) +-{ +- struct at803x_priv *priv = phydev->priv; +- int err; +- +- err = genphy_read_abilities(phydev); +- if (err) +- return err; +- +- if (phydev->drv->phy_id != ATH8031_PHY_ID) +- return 0; +- +- /* AR8031/AR8033 have different status registers +- * for copper and fiber operation. However, the +- * extended status register is the same for both +- * operation modes. +- * +- * As a result of that, ESTATUS_1000_XFULL is set +- * to 1 even when operating in copper TP mode. +- * +- * Remove this mode from the supported link modes +- * when not operating in 1000BaseX mode. +- */ +- if (!priv->is_1000basex) +- linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, +- phydev->supported); +- +- return 0; +-} +- +-static int at803x_smarteee_config(struct phy_device *phydev) +-{ +- struct at803x_priv *priv = phydev->priv; +- u16 mask = 0, val = 0; +- int ret; +- +- if (priv->flags & AT803X_DISABLE_SMARTEEE) +- return phy_modify_mmd(phydev, MDIO_MMD_PCS, +- AT803X_MMD3_SMARTEEE_CTL3, +- AT803X_MMD3_SMARTEEE_CTL3_LPI_EN, 0); +- +- if (priv->smarteee_lpi_tw_1g) { +- mask |= 0xff00; +- val |= priv->smarteee_lpi_tw_1g << 8; +- } +- if (priv->smarteee_lpi_tw_100m) { +- mask |= 0x00ff; +- val |= priv->smarteee_lpi_tw_100m; +- } +- if (!mask) +- return 0; +- +- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL1, +- mask, val); +- if (ret) +- return ret; +- +- return phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL3, +- AT803X_MMD3_SMARTEEE_CTL3_LPI_EN, +- AT803X_MMD3_SMARTEEE_CTL3_LPI_EN); +-} +- +-static int at803x_clk_out_config(struct phy_device *phydev) +-{ +- struct at803x_priv *priv = phydev->priv; +- +- if (!priv->clk_25m_mask) +- return 0; +- +- return phy_modify_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M, +- priv->clk_25m_mask, priv->clk_25m_reg); +-} +- +-static int at8031_pll_config(struct phy_device *phydev) +-{ +- struct at803x_priv *priv = phydev->priv; +- +- /* The default after hardware reset is PLL OFF. After a soft reset, the +- * values are retained. +- */ +- if (priv->flags & AT803X_KEEP_PLL_ENABLED) +- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, +- 0, AT803X_DEBUG_PLL_ON); +- else +- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, +- AT803X_DEBUG_PLL_ON, 0); +-} +- +-static int at803x_hibernation_mode_config(struct phy_device *phydev) +-{ +- struct at803x_priv *priv = phydev->priv; +- +- /* The default after hardware reset is hibernation mode enabled. After +- * software reset, the value is retained. +- */ +- if (!(priv->flags & AT803X_DISABLE_HIBERNATION_MODE)) +- return 0; +- +- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL, +- AT803X_DEBUG_HIB_CTRL_PS_HIB_EN, 0); +-} +- +-static int at803x_config_init(struct phy_device *phydev) +-{ +- int ret; +- +- /* The RX and TX delay default is: +- * after HW reset: RX delay enabled and TX delay disabled +- * after SW reset: RX delay enabled, while TX delay retains the +- * value before reset. +- */ +- if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || +- phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) +- ret = at803x_enable_rx_delay(phydev); +- else +- ret = at803x_disable_rx_delay(phydev); +- if (ret < 0) +- return ret; +- +- if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || +- phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) +- ret = at803x_enable_tx_delay(phydev); +- else +- ret = at803x_disable_tx_delay(phydev); +- if (ret < 0) +- return ret; +- +- ret = at803x_smarteee_config(phydev); +- if (ret < 0) +- return ret; +- +- ret = at803x_clk_out_config(phydev); +- if (ret < 0) +- return ret; +- +- ret = at803x_hibernation_mode_config(phydev); +- if (ret < 0) +- return ret; +- +- /* Ar803x extended next page bit is enabled by default. Cisco +- * multigig switches read this bit and attempt to negotiate 10Gbps +- * rates even if the next page bit is disabled. This is incorrect +- * behaviour but we still need to accommodate it. XNP is only needed +- * for 10Gbps support, so disable XNP. +- */ +- return phy_modify(phydev, MII_ADVERTISE, MDIO_AN_CTRL1_XNP, 0); +-} +- +-static int at803x_ack_interrupt(struct phy_device *phydev) +-{ +- int err; +- +- err = phy_read(phydev, AT803X_INTR_STATUS); +- +- return (err < 0) ? err : 0; +-} +- +-static int at803x_config_intr(struct phy_device *phydev) +-{ +- int err; +- int value; +- +- value = phy_read(phydev, AT803X_INTR_ENABLE); +- +- if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { +- /* Clear any pending interrupts */ +- err = at803x_ack_interrupt(phydev); +- if (err) +- return err; +- +- value |= AT803X_INTR_ENABLE_AUTONEG_ERR; +- value |= AT803X_INTR_ENABLE_SPEED_CHANGED; +- value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED; +- value |= AT803X_INTR_ENABLE_LINK_FAIL; +- value |= AT803X_INTR_ENABLE_LINK_SUCCESS; +- +- err = phy_write(phydev, AT803X_INTR_ENABLE, value); +- } else { +- err = phy_write(phydev, AT803X_INTR_ENABLE, 0); +- if (err) +- return err; +- +- /* Clear any pending interrupts */ +- err = at803x_ack_interrupt(phydev); +- } +- +- return err; +-} +- +-static irqreturn_t at803x_handle_interrupt(struct phy_device *phydev) +-{ +- int irq_status, int_enabled; +- +- irq_status = phy_read(phydev, AT803X_INTR_STATUS); +- if (irq_status < 0) { +- phy_error(phydev); +- return IRQ_NONE; +- } +- +- /* Read the current enabled interrupts */ +- int_enabled = phy_read(phydev, AT803X_INTR_ENABLE); +- if (int_enabled < 0) { +- phy_error(phydev); +- return IRQ_NONE; +- } +- +- /* See if this was one of our enabled interrupts */ +- if (!(irq_status & int_enabled)) +- return IRQ_NONE; +- +- phy_trigger_machine(phydev); +- +- return IRQ_HANDLED; +-} +- +-static void at803x_link_change_notify(struct phy_device *phydev) +-{ +- /* +- * Conduct a hardware reset for AT8030 every time a link loss is +- * signalled. This is necessary to circumvent a hardware bug that +- * occurs when the cable is unplugged while TX packets are pending +- * in the FIFO. In such cases, the FIFO enters an error mode it +- * cannot recover from by software. +- */ +- if (phydev->state == PHY_NOLINK && phydev->mdio.reset_gpio) { +- struct at803x_context context; +- +- at803x_context_save(phydev, &context); +- +- phy_device_reset(phydev, 1); +- usleep_range(1000, 2000); +- phy_device_reset(phydev, 0); +- usleep_range(1000, 2000); +- +- at803x_context_restore(phydev, &context); +- +- phydev_dbg(phydev, "%s(): phy was reset\n", __func__); +- } +-} +- +-static int at803x_read_specific_status(struct phy_device *phydev, +- struct at803x_ss_mask ss_mask) +-{ +- int ss; +- +- /* Read the AT8035 PHY-Specific Status register, which indicates the +- * speed and duplex that the PHY is actually using, irrespective of +- * whether we are in autoneg mode or not. +- */ +- ss = phy_read(phydev, AT803X_SPECIFIC_STATUS); +- if (ss < 0) +- return ss; +- +- if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) { +- int sfc, speed; +- +- sfc = phy_read(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL); +- if (sfc < 0) +- return sfc; +- +- speed = ss & ss_mask.speed_mask; +- speed >>= ss_mask.speed_shift; +- +- switch (speed) { +- case AT803X_SS_SPEED_10: +- phydev->speed = SPEED_10; +- break; +- case AT803X_SS_SPEED_100: +- phydev->speed = SPEED_100; +- break; +- case AT803X_SS_SPEED_1000: +- phydev->speed = SPEED_1000; +- break; +- case QCA808X_SS_SPEED_2500: +- phydev->speed = SPEED_2500; +- break; +- } +- if (ss & AT803X_SS_DUPLEX) +- phydev->duplex = DUPLEX_FULL; +- else +- phydev->duplex = DUPLEX_HALF; +- +- if (ss & AT803X_SS_MDIX) +- phydev->mdix = ETH_TP_MDI_X; +- else +- phydev->mdix = ETH_TP_MDI; +- +- switch (FIELD_GET(AT803X_SFC_MDI_CROSSOVER_MODE_M, sfc)) { +- case AT803X_SFC_MANUAL_MDI: +- phydev->mdix_ctrl = ETH_TP_MDI; +- break; +- case AT803X_SFC_MANUAL_MDIX: +- phydev->mdix_ctrl = ETH_TP_MDI_X; +- break; +- case AT803X_SFC_AUTOMATIC_CROSSOVER: +- phydev->mdix_ctrl = ETH_TP_MDI_AUTO; +- break; +- } +- } +- +- return 0; +-} +- +-static int at803x_read_status(struct phy_device *phydev) +-{ +- struct at803x_ss_mask ss_mask = { 0 }; +- int err, old_link = phydev->link; +- +- /* Update the link, but return if there was an error */ +- err = genphy_update_link(phydev); +- if (err) +- return err; +- +- /* why bother the PHY if nothing can have changed */ +- if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) +- return 0; +- +- phydev->speed = SPEED_UNKNOWN; +- phydev->duplex = DUPLEX_UNKNOWN; +- phydev->pause = 0; +- phydev->asym_pause = 0; +- +- err = genphy_read_lpa(phydev); +- if (err < 0) +- return err; +- +- ss_mask.speed_mask = AT803X_SS_SPEED_MASK; +- ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK); +- err = at803x_read_specific_status(phydev, ss_mask); +- if (err < 0) +- return err; +- +- if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) +- phy_resolve_aneg_pause(phydev); +- +- return 0; +-} +- +-static int at803x_config_mdix(struct phy_device *phydev, u8 ctrl) +-{ +- u16 val; +- +- switch (ctrl) { +- case ETH_TP_MDI: +- val = AT803X_SFC_MANUAL_MDI; +- break; +- case ETH_TP_MDI_X: +- val = AT803X_SFC_MANUAL_MDIX; +- break; +- case ETH_TP_MDI_AUTO: +- val = AT803X_SFC_AUTOMATIC_CROSSOVER; +- break; +- default: +- return 0; +- } +- +- return phy_modify_changed(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL, +- AT803X_SFC_MDI_CROSSOVER_MODE_M, +- FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val)); +-} +- +-static int at803x_prepare_config_aneg(struct phy_device *phydev) +-{ +- int ret; +- +- ret = at803x_config_mdix(phydev, phydev->mdix_ctrl); +- if (ret < 0) +- return ret; +- +- /* Changes of the midx bits are disruptive to the normal operation; +- * therefore any changes to these registers must be followed by a +- * software reset to take effect. +- */ +- if (ret == 1) { +- ret = genphy_soft_reset(phydev); +- if (ret < 0) +- return ret; +- } +- +- return 0; +-} +- +-static int at803x_config_aneg(struct phy_device *phydev) +-{ +- struct at803x_priv *priv = phydev->priv; +- int ret; +- +- ret = at803x_prepare_config_aneg(phydev); +- if (ret) +- return ret; +- +- if (priv->is_1000basex) +- return genphy_c37_config_aneg(phydev); +- +- return genphy_config_aneg(phydev); +-} +- +-static int at803x_get_downshift(struct phy_device *phydev, u8 *d) +-{ +- int val; +- +- val = phy_read(phydev, AT803X_SMART_SPEED); +- if (val < 0) +- return val; +- +- if (val & AT803X_SMART_SPEED_ENABLE) +- *d = FIELD_GET(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, val) + 2; +- else +- *d = DOWNSHIFT_DEV_DISABLE; +- +- return 0; +-} +- +-static int at803x_set_downshift(struct phy_device *phydev, u8 cnt) +-{ +- u16 mask, set; +- int ret; +- +- switch (cnt) { +- case DOWNSHIFT_DEV_DEFAULT_COUNT: +- cnt = AT803X_DEFAULT_DOWNSHIFT; +- fallthrough; +- case AT803X_MIN_DOWNSHIFT ... AT803X_MAX_DOWNSHIFT: +- set = AT803X_SMART_SPEED_ENABLE | +- AT803X_SMART_SPEED_BYPASS_TIMER | +- FIELD_PREP(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, cnt - 2); +- mask = AT803X_SMART_SPEED_RETRY_LIMIT_MASK; +- break; +- case DOWNSHIFT_DEV_DISABLE: +- set = 0; +- mask = AT803X_SMART_SPEED_ENABLE | +- AT803X_SMART_SPEED_BYPASS_TIMER; +- break; +- default: +- return -EINVAL; +- } +- +- ret = phy_modify_changed(phydev, AT803X_SMART_SPEED, mask, set); +- +- /* After changing the smart speed settings, we need to perform a +- * software reset, use phy_init_hw() to make sure we set the +- * reapply any values which might got lost during software reset. +- */ +- if (ret == 1) +- ret = phy_init_hw(phydev); +- +- return ret; +-} +- +-static int at803x_get_tunable(struct phy_device *phydev, +- struct ethtool_tunable *tuna, void *data) +-{ +- switch (tuna->id) { +- case ETHTOOL_PHY_DOWNSHIFT: +- return at803x_get_downshift(phydev, data); +- default: +- return -EOPNOTSUPP; +- } +-} +- +-static int at803x_set_tunable(struct phy_device *phydev, +- struct ethtool_tunable *tuna, const void *data) +-{ +- switch (tuna->id) { +- case ETHTOOL_PHY_DOWNSHIFT: +- return at803x_set_downshift(phydev, *(const u8 *)data); +- default: +- return -EOPNOTSUPP; +- } +-} +- +-static int at803x_cable_test_result_trans(u16 status) +-{ +- switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) { +- case AT803X_CDT_STATUS_STAT_NORMAL: +- return ETHTOOL_A_CABLE_RESULT_CODE_OK; +- case AT803X_CDT_STATUS_STAT_SHORT: +- return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; +- case AT803X_CDT_STATUS_STAT_OPEN: +- return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; +- case AT803X_CDT_STATUS_STAT_FAIL: +- default: +- return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; +- } +-} +- +-static bool at803x_cdt_test_failed(u16 status) +-{ +- return FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status) == +- AT803X_CDT_STATUS_STAT_FAIL; +-} +- +-static bool at803x_cdt_fault_length_valid(u16 status) +-{ +- switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) { +- case AT803X_CDT_STATUS_STAT_OPEN: +- case AT803X_CDT_STATUS_STAT_SHORT: +- return true; +- } +- return false; +-} +- +-static int at803x_cdt_fault_length(int dt) +-{ +- /* According to the datasheet the distance to the fault is +- * DELTA_TIME * 0.824 meters. +- * +- * The author suspect the correct formula is: +- * +- * fault_distance = DELTA_TIME * (c * VF) / 125MHz / 2 +- * +- * where c is the speed of light, VF is the velocity factor of +- * the twisted pair cable, 125MHz the counter frequency and +- * we need to divide by 2 because the hardware will measure the +- * round trip time to the fault and back to the PHY. +- * +- * With a VF of 0.69 we get the factor 0.824 mentioned in the +- * datasheet. +- */ +- return (dt * 824) / 10; +-} +- +-static int at803x_cdt_start(struct phy_device *phydev, +- u32 cdt_start) +-{ +- return phy_write(phydev, AT803X_CDT, cdt_start); +-} +- +-static int at803x_cdt_wait_for_completion(struct phy_device *phydev, +- u32 cdt_en) +-{ +- int val, ret; +- +- /* One test run takes about 25ms */ +- ret = phy_read_poll_timeout(phydev, AT803X_CDT, val, +- !(val & cdt_en), +- 30000, 100000, true); +- +- return ret < 0 ? ret : 0; +-} +- +-static int at803x_cable_test_one_pair(struct phy_device *phydev, int pair) +-{ +- static const int ethtool_pair[] = { +- ETHTOOL_A_CABLE_PAIR_A, +- ETHTOOL_A_CABLE_PAIR_B, +- ETHTOOL_A_CABLE_PAIR_C, +- ETHTOOL_A_CABLE_PAIR_D, +- }; +- int ret, val; +- +- val = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) | +- AT803X_CDT_ENABLE_TEST; +- ret = at803x_cdt_start(phydev, val); +- if (ret) +- return ret; +- +- ret = at803x_cdt_wait_for_completion(phydev, AT803X_CDT_ENABLE_TEST); +- if (ret) +- return ret; +- +- val = phy_read(phydev, AT803X_CDT_STATUS); +- if (val < 0) +- return val; +- +- if (at803x_cdt_test_failed(val)) +- return 0; +- +- ethnl_cable_test_result(phydev, ethtool_pair[pair], +- at803x_cable_test_result_trans(val)); +- +- if (at803x_cdt_fault_length_valid(val)) { +- val = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, val); +- ethnl_cable_test_fault_length(phydev, ethtool_pair[pair], +- at803x_cdt_fault_length(val)); +- } +- +- return 1; +-} +- +-static int at803x_cable_test_get_status(struct phy_device *phydev, +- bool *finished, unsigned long pair_mask) +-{ +- int retries = 20; +- int pair, ret; +- +- *finished = false; +- +- /* According to the datasheet the CDT can be performed when +- * there is no link partner or when the link partner is +- * auto-negotiating. Starting the test will restart the AN +- * automatically. It seems that doing this repeatedly we will +- * get a slot where our link partner won't disturb our +- * measurement. +- */ +- while (pair_mask && retries--) { +- for_each_set_bit(pair, &pair_mask, 4) { +- ret = at803x_cable_test_one_pair(phydev, pair); +- if (ret < 0) +- return ret; +- if (ret) +- clear_bit(pair, &pair_mask); +- } +- if (pair_mask) +- msleep(250); +- } +- +- *finished = true; +- +- return 0; +-} +- +-static void at803x_cable_test_autoneg(struct phy_device *phydev) +-{ +- /* Enable auto-negotiation, but advertise no capabilities, no link +- * will be established. A restart of the auto-negotiation is not +- * required, because the cable test will automatically break the link. +- */ +- phy_write(phydev, MII_BMCR, BMCR_ANENABLE); +- phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA); +-} +- +-static int at803x_cable_test_start(struct phy_device *phydev) +-{ +- at803x_cable_test_autoneg(phydev); +- /* we do all the (time consuming) work later */ +- return 0; +-} +- +-static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev, +- unsigned int selector) +-{ +- struct phy_device *phydev = rdev_get_drvdata(rdev); +- +- if (selector) +- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, +- 0, AT803X_DEBUG_RGMII_1V8); +- else +- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, +- AT803X_DEBUG_RGMII_1V8, 0); +-} +- +-static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev) +-{ +- struct phy_device *phydev = rdev_get_drvdata(rdev); +- int val; +- +- val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F); +- if (val < 0) +- return val; +- +- return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0; +-} +- +-static const struct regulator_ops vddio_regulator_ops = { +- .list_voltage = regulator_list_voltage_table, +- .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel, +- .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel, +-}; +- +-static const unsigned int vddio_voltage_table[] = { +- 1500000, +- 1800000, +-}; +- +-static const struct regulator_desc vddio_desc = { +- .name = "vddio", +- .of_match = of_match_ptr("vddio-regulator"), +- .n_voltages = ARRAY_SIZE(vddio_voltage_table), +- .volt_table = vddio_voltage_table, +- .ops = &vddio_regulator_ops, +- .type = REGULATOR_VOLTAGE, +- .owner = THIS_MODULE, +-}; +- +-static const struct regulator_ops vddh_regulator_ops = { +-}; +- +-static const struct regulator_desc vddh_desc = { +- .name = "vddh", +- .of_match = of_match_ptr("vddh-regulator"), +- .n_voltages = 1, +- .fixed_uV = 2500000, +- .ops = &vddh_regulator_ops, +- .type = REGULATOR_VOLTAGE, +- .owner = THIS_MODULE, +-}; +- +-static int at8031_register_regulators(struct phy_device *phydev) +-{ +- struct at803x_priv *priv = phydev->priv; +- struct device *dev = &phydev->mdio.dev; +- struct regulator_config config = { }; +- +- config.dev = dev; +- config.driver_data = phydev; +- +- priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config); +- if (IS_ERR(priv->vddio_rdev)) { +- phydev_err(phydev, "failed to register VDDIO regulator\n"); +- return PTR_ERR(priv->vddio_rdev); +- } +- +- priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config); +- if (IS_ERR(priv->vddh_rdev)) { +- phydev_err(phydev, "failed to register VDDH regulator\n"); +- return PTR_ERR(priv->vddh_rdev); +- } +- +- return 0; +-} +- +-static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) +-{ +- struct phy_device *phydev = upstream; +- __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support); +- __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); +- DECLARE_PHY_INTERFACE_MASK(interfaces); +- phy_interface_t iface; +- +- linkmode_zero(phy_support); +- phylink_set(phy_support, 1000baseX_Full); +- phylink_set(phy_support, 1000baseT_Full); +- phylink_set(phy_support, Autoneg); +- phylink_set(phy_support, Pause); +- phylink_set(phy_support, Asym_Pause); +- +- linkmode_zero(sfp_support); +- sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces); +- /* Some modules support 10G modes as well as others we support. +- * Mask out non-supported modes so the correct interface is picked. +- */ +- linkmode_and(sfp_support, phy_support, sfp_support); +- +- if (linkmode_empty(sfp_support)) { +- dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n"); +- return -EINVAL; +- } +- +- iface = sfp_select_interface(phydev->sfp_bus, sfp_support); +- +- /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes +- * interface for use with SFP modules. +- * However, some copper modules detected as having a preferred SGMII +- * interface do default to and function in 1000Base-X mode, so just +- * print a warning and allow such modules, as they may have some chance +- * of working. +- */ +- if (iface == PHY_INTERFACE_MODE_SGMII) +- dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n"); +- else if (iface != PHY_INTERFACE_MODE_1000BASEX) +- return -EINVAL; +- +- return 0; +-} +- +-static const struct sfp_upstream_ops at8031_sfp_ops = { +- .attach = phy_sfp_attach, +- .detach = phy_sfp_detach, +- .module_insert = at8031_sfp_insert, +-}; +- +-static int at8031_parse_dt(struct phy_device *phydev) +-{ +- struct device_node *node = phydev->mdio.dev.of_node; +- struct at803x_priv *priv = phydev->priv; +- int ret; +- +- if (of_property_read_bool(node, "qca,keep-pll-enabled")) +- priv->flags |= AT803X_KEEP_PLL_ENABLED; +- +- ret = at8031_register_regulators(phydev); +- if (ret < 0) +- return ret; +- +- ret = devm_regulator_get_enable_optional(&phydev->mdio.dev, +- "vddio"); +- if (ret) { +- phydev_err(phydev, "failed to get VDDIO regulator\n"); +- return ret; +- } +- +- /* Only AR8031/8033 support 1000Base-X for SFP modules */ +- return phy_sfp_probe(phydev, &at8031_sfp_ops); +-} +- +-static int at8031_probe(struct phy_device *phydev) +-{ +- struct at803x_priv *priv = phydev->priv; +- int mode_cfg; +- int ccr; +- int ret; +- +- ret = at803x_probe(phydev); +- if (ret) +- return ret; +- +- /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping +- * options. +- */ +- ret = at8031_parse_dt(phydev); +- if (ret) +- return ret; +- +- ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG); +- if (ccr < 0) +- return ccr; +- mode_cfg = ccr & AT803X_MODE_CFG_MASK; +- +- switch (mode_cfg) { +- case AT803X_MODE_CFG_BX1000_RGMII_50OHM: +- case AT803X_MODE_CFG_BX1000_RGMII_75OHM: +- priv->is_1000basex = true; +- fallthrough; +- case AT803X_MODE_CFG_FX100_RGMII_50OHM: +- case AT803X_MODE_CFG_FX100_RGMII_75OHM: +- priv->is_fiber = true; +- break; +- } +- +- /* Disable WoL in 1588 register which is enabled +- * by default +- */ +- return phy_modify_mmd(phydev, MDIO_MMD_PCS, +- AT803X_PHY_MMD3_WOL_CTRL, +- AT803X_WOL_EN, 0); +-} +- +-static int at8031_config_init(struct phy_device *phydev) +-{ +- struct at803x_priv *priv = phydev->priv; +- int ret; +- +- /* Some bootloaders leave the fiber page selected. +- * Switch to the appropriate page (fiber or copper), as otherwise we +- * read the PHY capabilities from the wrong page. +- */ +- phy_lock_mdio_bus(phydev); +- ret = at803x_write_page(phydev, +- priv->is_fiber ? AT803X_PAGE_FIBER : +- AT803X_PAGE_COPPER); +- phy_unlock_mdio_bus(phydev); +- if (ret) +- return ret; +- +- ret = at8031_pll_config(phydev); +- if (ret < 0) +- return ret; +- +- return at803x_config_init(phydev); +-} +- +-static int at8031_set_wol(struct phy_device *phydev, +- struct ethtool_wolinfo *wol) +-{ +- int ret; +- +- /* First setup MAC address and enable WOL interrupt */ +- ret = at803x_set_wol(phydev, wol); +- if (ret) +- return ret; +- +- if (wol->wolopts & WAKE_MAGIC) +- /* Enable WOL function for 1588 */ +- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, +- AT803X_PHY_MMD3_WOL_CTRL, +- 0, AT803X_WOL_EN); +- else +- /* Disable WoL function for 1588 */ +- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, +- AT803X_PHY_MMD3_WOL_CTRL, +- AT803X_WOL_EN, 0); +- +- return ret; +-} +- +-static int at8031_config_intr(struct phy_device *phydev) +-{ +- struct at803x_priv *priv = phydev->priv; +- int err, value = 0; +- +- if (phydev->interrupts == PHY_INTERRUPT_ENABLED && +- priv->is_fiber) { +- /* Clear any pending interrupts */ +- err = at803x_ack_interrupt(phydev); +- if (err) +- return err; +- +- value |= AT803X_INTR_ENABLE_LINK_FAIL_BX; +- value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX; +- +- err = phy_set_bits(phydev, AT803X_INTR_ENABLE, value); +- if (err) +- return err; +- } +- +- return at803x_config_intr(phydev); +-} +- +-/* AR8031 and AR8033 share the same read status logic */ +-static int at8031_read_status(struct phy_device *phydev) +-{ +- struct at803x_priv *priv = phydev->priv; +- +- if (priv->is_1000basex) +- return genphy_c37_read_status(phydev); +- +- return at803x_read_status(phydev); +-} +- +-/* AR8031 and AR8035 share the same cable test get status reg */ +-static int at8031_cable_test_get_status(struct phy_device *phydev, +- bool *finished) +-{ +- return at803x_cable_test_get_status(phydev, finished, 0xf); +-} +- +-/* AR8031 and AR8035 share the same cable test start logic */ +-static int at8031_cable_test_start(struct phy_device *phydev) +-{ +- at803x_cable_test_autoneg(phydev); +- phy_write(phydev, MII_CTRL1000, 0); +- /* we do all the (time consuming) work later */ +- return 0; +-} +- +-/* AR8032, AR9331 and QCA9561 share the same cable test get status reg */ +-static int at8032_cable_test_get_status(struct phy_device *phydev, +- bool *finished) +-{ +- return at803x_cable_test_get_status(phydev, finished, 0x3); +-} +- +-static int at8035_parse_dt(struct phy_device *phydev) +-{ +- struct at803x_priv *priv = phydev->priv; +- +- /* Mask is set by the generic at803x_parse_dt +- * if property is set. Assume property is set +- * with the mask not zero. +- */ +- if (priv->clk_25m_mask) { +- /* Fixup for the AR8030/AR8035. This chip has another mask and +- * doesn't support the DSP reference. Eg. the lowest bit of the +- * mask. The upper two bits select the same frequencies. Mask +- * the lowest bit here. +- * +- * Warning: +- * There was no datasheet for the AR8030 available so this is +- * just a guess. But the AR8035 is listed as pin compatible +- * to the AR8030 so there might be a good chance it works on +- * the AR8030 too. +- */ +- priv->clk_25m_reg &= AT8035_CLK_OUT_MASK; +- priv->clk_25m_mask &= AT8035_CLK_OUT_MASK; +- } +- +- return 0; +-} +- +-/* AR8030 and AR8035 shared the same special mask for clk_25m */ +-static int at8035_probe(struct phy_device *phydev) +-{ +- int ret; +- +- ret = at803x_probe(phydev); +- if (ret) +- return ret; +- +- return at8035_parse_dt(phydev); +-} +- +-static int qca83xx_config_init(struct phy_device *phydev) +-{ +- u8 switch_revision; +- +- switch_revision = phydev->dev_flags & QCA8K_DEVFLAGS_REVISION_MASK; +- +- switch (switch_revision) { +- case 1: +- /* For 100M waveform */ +- at803x_debug_reg_write(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0x02ea); +- /* Turn on Gigabit clock */ +- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x68a0); +- break; +- +- case 2: +- phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0); +- fallthrough; +- case 4: +- phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_AZ_DEBUG, 0x803f); +- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x6860); +- at803x_debug_reg_write(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0x2c46); +- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3C, 0x6000); +- break; +- } +- +- /* Following original QCA sourcecode set port to prefer master */ +- phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER); +- +- return 0; +-} +- +-static int qca8327_config_init(struct phy_device *phydev) +-{ +- /* QCA8327 require DAC amplitude adjustment for 100m set to +6%. +- * Disable on init and enable only with 100m speed following +- * qca original source code. +- */ +- at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, +- QCA8327_DEBUG_MANU_CTRL_EN, 0); +- +- return qca83xx_config_init(phydev); +-} +- +-static void qca83xx_link_change_notify(struct phy_device *phydev) +-{ +- /* Set DAC Amplitude adjustment to +6% for 100m on link running */ +- if (phydev->state == PHY_RUNNING) { +- if (phydev->speed == SPEED_100) +- at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, +- QCA8327_DEBUG_MANU_CTRL_EN, +- QCA8327_DEBUG_MANU_CTRL_EN); +- } else { +- /* Reset DAC Amplitude adjustment */ +- at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, +- QCA8327_DEBUG_MANU_CTRL_EN, 0); +- } +-} +- +-static int qca83xx_resume(struct phy_device *phydev) +-{ +- int ret, val; +- +- /* Skip reset if not suspended */ +- if (!phydev->suspended) +- return 0; +- +- /* Reinit the port, reset values set by suspend */ +- qca83xx_config_init(phydev); +- +- /* Reset the port on port resume */ +- phy_set_bits(phydev, MII_BMCR, BMCR_RESET | BMCR_ANENABLE); +- +- /* On resume from suspend the switch execute a reset and +- * restart auto-negotiation. Wait for reset to complete. +- */ +- ret = phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET), +- 50000, 600000, true); +- if (ret) +- return ret; +- +- usleep_range(1000, 2000); +- +- return 0; +-} +- +-static int qca83xx_suspend(struct phy_device *phydev) +-{ +- at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_GREEN, +- AT803X_DEBUG_GATE_CLK_IN1000, 0); +- +- at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL, +- AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE | +- AT803X_DEBUG_HIB_CTRL_SEL_RST_80U, 0); +- +- return 0; +-} +- +-static int qca8337_suspend(struct phy_device *phydev) +-{ +- /* Only QCA8337 support actual suspend. */ +- genphy_suspend(phydev); +- +- return qca83xx_suspend(phydev); +-} +- +-static int qca8327_suspend(struct phy_device *phydev) +-{ +- u16 mask = 0; +- +- /* QCA8327 cause port unreliability when phy suspend +- * is set. +- */ +- mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX); +- phy_modify(phydev, MII_BMCR, mask, 0); +- +- return qca83xx_suspend(phydev); +-} +- +-static int qca808x_phy_fast_retrain_config(struct phy_device *phydev) +-{ +- int ret; +- +- /* Enable fast retrain */ +- ret = genphy_c45_fast_retrain(phydev, true); +- if (ret) +- return ret; +- +- phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1, +- QCA808X_TOP_OPTION1_DATA); +- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB, +- QCA808X_MSE_THRESHOLD_20DB_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB, +- QCA808X_MSE_THRESHOLD_17DB_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB, +- QCA808X_MSE_THRESHOLD_27DB_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB, +- QCA808X_MSE_THRESHOLD_28DB_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1, +- QCA808X_MMD3_DEBUG_1_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4, +- QCA808X_MMD3_DEBUG_4_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5, +- QCA808X_MMD3_DEBUG_5_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3, +- QCA808X_MMD3_DEBUG_3_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6, +- QCA808X_MMD3_DEBUG_6_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2, +- QCA808X_MMD3_DEBUG_2_VALUE); +- +- return 0; +-} +- +-static int qca808x_phy_ms_seed_enable(struct phy_device *phydev, bool enable) +-{ +- u16 seed_value; +- +- 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_CFG | QCA808X_MASTER_SLAVE_SEED_ENABLE, +- FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value) | +- 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 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; +- +- /* Active adc&vga on 802.3az for the link 1000M and 100M */ +- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7, +- QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN); +- if (ret) +- return ret; +- +- /* Adjust the threshold on 802.3az for the link 1000M */ +- ret = phy_write_mmd(phydev, MDIO_MMD_PCS, +- QCA808X_PHY_MMD3_AZ_TRAINING_CTRL, +- QCA808X_MMD3_AZ_TRAINING_VAL); +- if (ret) +- return ret; +- +- 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 */ +- return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD, +- QCA808X_ADC_THRESHOLD_MASK, +- QCA808X_ADC_THRESHOLD_100MV); +-} +- +-static int qca808x_read_status(struct phy_device *phydev) +-{ +- struct at803x_ss_mask ss_mask = { 0 }; +- int ret; +- +- ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT); +- if (ret < 0) +- return ret; +- +- linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising, +- ret & MDIO_AN_10GBT_STAT_LP2_5G); +- +- ret = genphy_read_status(phydev); +- if (ret) +- return ret; +- +- /* qca8081 takes the different bits for speed value from at803x */ +- ss_mask.speed_mask = QCA808X_SS_SPEED_MASK; +- ss_mask.speed_shift = __bf_shf(QCA808X_SS_SPEED_MASK); +- ret = at803x_read_specific_status(phydev, ss_mask); +- if (ret < 0) +- return ret; +- +- if (phydev->link) { +- if (phydev->speed == SPEED_2500) +- phydev->interface = PHY_INTERFACE_MODE_2500BASEX; +- else +- 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 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 (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); +- } +- } +- } +- +- return 0; +-} +- +-static int qca808x_soft_reset(struct phy_device *phydev) +-{ +- int ret; +- +- ret = genphy_soft_reset(phydev); +- if (ret < 0) +- return ret; +- +- 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) +-{ +- switch (cdt_code) { +- case QCA808X_CDT_STATUS_STAT_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: +- return true; +- default: +- return false; +- } +-} +- +-static int qca808x_cable_test_result_trans(int cdt_code) +-{ +- switch (cdt_code) { +- case QCA808X_CDT_STATUS_STAT_NORMAL: +- return ETHTOOL_A_CABLE_RESULT_CODE_OK; +- case QCA808X_CDT_STATUS_STAT_SAME_SHORT: +- return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; +- case QCA808X_CDT_STATUS_STAT_SAME_OPEN: +- return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: +- return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT; +- case QCA808X_CDT_STATUS_STAT_FAIL: +- default: +- return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; +- } +-} +- +-static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair, +- int result) +-{ +- int val; +- u32 cdt_length_reg = 0; +- +- switch (pair) { +- case ETHTOOL_A_CABLE_PAIR_A: +- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A; +- break; +- case ETHTOOL_A_CABLE_PAIR_B: +- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B; +- break; +- case ETHTOOL_A_CABLE_PAIR_C: +- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C; +- break; +- case ETHTOOL_A_CABLE_PAIR_D: +- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D; +- break; +- default: +- return -EINVAL; +- } +- +- val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg); +- if (val < 0) +- return val; +- +- if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT) +- val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val); +- else +- val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val); +- +- return at803x_cdt_fault_length(val); +-} +- +-static int qca808x_cable_test_start(struct phy_device *phydev) +-{ +- int ret; +- +- /* perform CDT with the following configs: +- * 1. disable hibernation. +- * 2. force PHY working in MDI mode. +- * 3. for PHY working in 1000BaseT. +- * 4. configure the threshold. +- */ +- +- ret = at803x_debug_reg_mask(phydev, QCA808X_DBG_AN_TEST, QCA808X_HIBERNATION_EN, 0); +- if (ret < 0) +- return ret; +- +- ret = at803x_config_mdix(phydev, ETH_TP_MDI); +- if (ret < 0) +- return ret; +- +- /* Force 1000base-T needs to configure PMA/PMD and MII_BMCR */ +- phydev->duplex = DUPLEX_FULL; +- phydev->speed = SPEED_1000; +- ret = genphy_c45_pma_setup_forced(phydev); +- if (ret < 0) +- return ret; +- +- ret = genphy_setup_forced(phydev); +- if (ret < 0) +- return ret; +- +- /* configure the thresholds for open, short, pair ok test */ +- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8074, 0xc040); +- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8076, 0xc040); +- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8077, 0xa060); +- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8078, 0xc050); +- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807a, 0xc060); +- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807e, 0xb060); +- +- return 0; +-} +- +-static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair, +- u16 status) +-{ +- int length, result; +- u16 pair_code; +- +- switch (pair) { +- case ETHTOOL_A_CABLE_PAIR_A: +- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status); +- break; +- case ETHTOOL_A_CABLE_PAIR_B: +- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status); +- break; +- case ETHTOOL_A_CABLE_PAIR_C: +- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status); +- break; +- case ETHTOOL_A_CABLE_PAIR_D: +- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status); +- break; +- default: +- return -EINVAL; +- } +- +- result = qca808x_cable_test_result_trans(pair_code); +- ethnl_cable_test_result(phydev, pair, result); +- +- if (qca808x_cdt_fault_length_valid(pair_code)) { +- length = qca808x_cdt_fault_length(phydev, pair, result); +- ethnl_cable_test_fault_length(phydev, pair, length); +- } +- +- return 0; +-} +- +-static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished) +-{ +- int ret, val; +- +- *finished = false; +- +- val = QCA808X_CDT_ENABLE_TEST | +- QCA808X_CDT_LENGTH_UNIT; +- ret = at803x_cdt_start(phydev, val); +- if (ret) +- return ret; +- +- ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST); +- if (ret) +- return ret; +- +- val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS); +- if (val < 0) +- return val; +- +- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val); +- if (ret) +- return ret; +- +- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val); +- if (ret) +- return ret; +- +- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val); +- if (ret) +- return ret; +- +- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val); +- if (ret) +- return ret; +- +- *finished = true; +- +- 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); +- +- /* 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; +-} +- +-static int qca808x_config_aneg(struct phy_device *phydev) +-{ +- int phy_ctrl = 0; +- int ret; +- +- ret = at803x_prepare_config_aneg(phydev); +- if (ret) +- return ret; +- +- /* The reg MII_BMCR also needs to be configured for force mode, the +- * genphy_config_aneg is also needed. +- */ +- if (phydev->autoneg == AUTONEG_DISABLE) +- genphy_c45_pma_setup_forced(phydev); +- +- if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising)) +- phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G; +- +- ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, +- MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl); +- if (ret < 0) +- return ret; +- +- return __genphy_config_aneg(phydev, ret); +-} +- +-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 int qca808x_led_parse_netdev(struct phy_device *phydev, unsigned long rules, +- u16 *offload_trigger) +-{ +- /* Parsing specific to netdev trigger */ +- if (test_bit(TRIGGER_NETDEV_TX, &rules)) +- *offload_trigger |= QCA808X_LED_TX_BLINK; +- if (test_bit(TRIGGER_NETDEV_RX, &rules)) +- *offload_trigger |= QCA808X_LED_RX_BLINK; +- if (test_bit(TRIGGER_NETDEV_LINK_10, &rules)) +- *offload_trigger |= QCA808X_LED_SPEED10_ON; +- if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) +- *offload_trigger |= QCA808X_LED_SPEED100_ON; +- if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) +- *offload_trigger |= QCA808X_LED_SPEED1000_ON; +- if (test_bit(TRIGGER_NETDEV_LINK_2500, &rules)) +- *offload_trigger |= QCA808X_LED_SPEED2500_ON; +- if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules)) +- *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON; +- if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules)) +- *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON; +- +- if (rules && !*offload_trigger) +- return -EOPNOTSUPP; +- +- /* Enable BLINK_CHECK_BYPASS by default to make the LED +- * blink even with duplex or speed mode not enabled. +- */ +- *offload_trigger |= QCA808X_LED_BLINK_CHECK_BYPASS; +- +- return 0; +-} +- +-static int qca808x_led_hw_control_enable(struct phy_device *phydev, u8 index) +-{ +- u16 reg; +- +- if (index > 2) +- return -EINVAL; +- +- reg = QCA808X_MMD7_LED_FORCE_CTRL(index); +- +- return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, +- QCA808X_LED_FORCE_EN); +-} +- +-static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index, +- unsigned long rules) +-{ +- u16 offload_trigger = 0; +- +- if (index > 2) +- return -EINVAL; +- +- return qca808x_led_parse_netdev(phydev, rules, &offload_trigger); +-} +- +-static int qca808x_led_hw_control_set(struct phy_device *phydev, u8 index, +- unsigned long rules) +-{ +- u16 reg, offload_trigger = 0; +- int ret; +- +- if (index > 2) +- return -EINVAL; +- +- reg = QCA808X_MMD7_LED_CTRL(index); +- +- ret = qca808x_led_parse_netdev(phydev, rules, &offload_trigger); +- if (ret) +- return ret; +- +- ret = qca808x_led_hw_control_enable(phydev, index); +- if (ret) +- return ret; +- +- return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, +- QCA808X_LED_PATTERN_MASK, +- offload_trigger); +-} +- +-static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index) +-{ +- u16 reg; +- int val; +- +- if (index > 2) +- return false; +- +- reg = QCA808X_MMD7_LED_FORCE_CTRL(index); +- +- val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); +- +- return !(val & QCA808X_LED_FORCE_EN); +-} +- +-static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index, +- unsigned long *rules) +-{ +- u16 reg; +- int val; +- +- if (index > 2) +- return -EINVAL; +- +- /* Check if we have hw control enabled */ +- if (qca808x_led_hw_control_status(phydev, index)) +- return -EINVAL; +- +- reg = QCA808X_MMD7_LED_CTRL(index); +- +- val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); +- if (val & QCA808X_LED_TX_BLINK) +- set_bit(TRIGGER_NETDEV_TX, rules); +- if (val & QCA808X_LED_RX_BLINK) +- set_bit(TRIGGER_NETDEV_RX, rules); +- if (val & QCA808X_LED_SPEED10_ON) +- set_bit(TRIGGER_NETDEV_LINK_10, rules); +- if (val & QCA808X_LED_SPEED100_ON) +- set_bit(TRIGGER_NETDEV_LINK_100, rules); +- if (val & QCA808X_LED_SPEED1000_ON) +- set_bit(TRIGGER_NETDEV_LINK_1000, rules); +- if (val & QCA808X_LED_SPEED2500_ON) +- set_bit(TRIGGER_NETDEV_LINK_2500, rules); +- if (val & QCA808X_LED_HALF_DUPLEX_ON) +- set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules); +- if (val & QCA808X_LED_FULL_DUPLEX_ON) +- set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules); +- +- return 0; +-} +- +-static int qca808x_led_hw_control_reset(struct phy_device *phydev, u8 index) +-{ +- u16 reg; +- +- if (index > 2) +- return -EINVAL; +- +- reg = QCA808X_MMD7_LED_CTRL(index); +- +- return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, +- QCA808X_LED_PATTERN_MASK); +-} +- +-static int qca808x_led_brightness_set(struct phy_device *phydev, +- u8 index, enum led_brightness value) +-{ +- u16 reg; +- int ret; +- +- if (index > 2) +- return -EINVAL; +- +- if (!value) { +- ret = qca808x_led_hw_control_reset(phydev, index); +- if (ret) +- return ret; +- } +- +- reg = QCA808X_MMD7_LED_FORCE_CTRL(index); +- +- return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, +- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, +- QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON : +- QCA808X_LED_FORCE_OFF); +-} +- +-static int qca808x_led_blink_set(struct phy_device *phydev, u8 index, +- unsigned long *delay_on, +- unsigned long *delay_off) +-{ +- int ret; +- u16 reg; +- +- if (index > 2) +- return -EINVAL; +- +- reg = QCA808X_MMD7_LED_FORCE_CTRL(index); +- +- /* Set blink to 50% off, 50% on at 4Hz by default */ +- ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL, +- QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK, +- QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50); +- if (ret) +- return ret; +- +- /* We use BLINK_1 for normal blinking */ +- ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg, +- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, +- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1); +- if (ret) +- return ret; +- +- /* We set blink to 4Hz, aka 250ms */ +- *delay_on = 250 / 2; +- *delay_off = 250 / 2; +- +- return 0; +-} +- +-static int qca808x_led_polarity_set(struct phy_device *phydev, int index, +- unsigned long modes) +-{ +- struct at803x_priv *priv = phydev->priv; +- bool active_low = false; +- u32 mode; +- +- for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) { +- switch (mode) { +- case PHY_LED_ACTIVE_LOW: +- active_low = true; +- break; +- default: +- return -EINVAL; +- } +- } +- +- /* PHY polarity is global and can't be set per LED. +- * To detect this, check if last requested polarity mode +- * match the new one. +- */ +- if (priv->led_polarity_mode >= 0 && +- priv->led_polarity_mode != active_low) { +- phydev_err(phydev, "PHY polarity is global. Mismatched polarity on different LED\n"); +- return -EINVAL; +- } +- +- /* Save the last PHY polarity mode */ +- priv->led_polarity_mode = active_low; +- +- return phy_modify_mmd(phydev, MDIO_MMD_AN, +- QCA808X_MMD7_LED_POLARITY_CTRL, +- QCA808X_LED_ACTIVE_HIGH, +- active_low ? 0 : QCA808X_LED_ACTIVE_HIGH); +-} +- +-static struct phy_driver at803x_driver[] = { +-{ +- /* Qualcomm Atheros AR8035 */ +- PHY_ID_MATCH_EXACT(ATH8035_PHY_ID), +- .name = "Qualcomm Atheros AR8035", +- .flags = PHY_POLL_CABLE_TEST, +- .probe = at8035_probe, +- .config_aneg = at803x_config_aneg, +- .config_init = at803x_config_init, +- .soft_reset = genphy_soft_reset, +- .set_wol = at803x_set_wol, +- .get_wol = at803x_get_wol, +- .suspend = at803x_suspend, +- .resume = at803x_resume, +- /* PHY_GBIT_FEATURES */ +- .read_status = at803x_read_status, +- .config_intr = at803x_config_intr, +- .handle_interrupt = at803x_handle_interrupt, +- .get_tunable = at803x_get_tunable, +- .set_tunable = at803x_set_tunable, +- .cable_test_start = at8031_cable_test_start, +- .cable_test_get_status = at8031_cable_test_get_status, +-}, { +- /* Qualcomm Atheros AR8030 */ +- .phy_id = ATH8030_PHY_ID, +- .name = "Qualcomm Atheros AR8030", +- .phy_id_mask = AT8030_PHY_ID_MASK, +- .probe = at8035_probe, +- .config_init = at803x_config_init, +- .link_change_notify = at803x_link_change_notify, +- .set_wol = at803x_set_wol, +- .get_wol = at803x_get_wol, +- .suspend = at803x_suspend, +- .resume = at803x_resume, +- /* PHY_BASIC_FEATURES */ +- .config_intr = at803x_config_intr, +- .handle_interrupt = at803x_handle_interrupt, +-}, { +- /* Qualcomm Atheros AR8031/AR8033 */ +- PHY_ID_MATCH_EXACT(ATH8031_PHY_ID), +- .name = "Qualcomm Atheros AR8031/AR8033", +- .flags = PHY_POLL_CABLE_TEST, +- .probe = at8031_probe, +- .config_init = at8031_config_init, +- .config_aneg = at803x_config_aneg, +- .soft_reset = genphy_soft_reset, +- .set_wol = at8031_set_wol, +- .get_wol = at803x_get_wol, +- .suspend = at803x_suspend, +- .resume = at803x_resume, +- .read_page = at803x_read_page, +- .write_page = at803x_write_page, +- .get_features = at803x_get_features, +- .read_status = at8031_read_status, +- .config_intr = at8031_config_intr, +- .handle_interrupt = at803x_handle_interrupt, +- .get_tunable = at803x_get_tunable, +- .set_tunable = at803x_set_tunable, +- .cable_test_start = at8031_cable_test_start, +- .cable_test_get_status = at8031_cable_test_get_status, +-}, { +- /* Qualcomm Atheros AR8032 */ +- PHY_ID_MATCH_EXACT(ATH8032_PHY_ID), +- .name = "Qualcomm Atheros AR8032", +- .probe = at803x_probe, +- .flags = PHY_POLL_CABLE_TEST, +- .config_init = at803x_config_init, +- .link_change_notify = at803x_link_change_notify, +- .suspend = at803x_suspend, +- .resume = at803x_resume, +- /* PHY_BASIC_FEATURES */ +- .config_intr = at803x_config_intr, +- .handle_interrupt = at803x_handle_interrupt, +- .cable_test_start = at803x_cable_test_start, +- .cable_test_get_status = at8032_cable_test_get_status, +-}, { +- /* ATHEROS AR9331 */ +- PHY_ID_MATCH_EXACT(ATH9331_PHY_ID), +- .name = "Qualcomm Atheros AR9331 built-in PHY", +- .probe = at803x_probe, +- .suspend = at803x_suspend, +- .resume = at803x_resume, +- .flags = PHY_POLL_CABLE_TEST, +- /* PHY_BASIC_FEATURES */ +- .config_intr = at803x_config_intr, +- .handle_interrupt = at803x_handle_interrupt, +- .cable_test_start = at803x_cable_test_start, +- .cable_test_get_status = at8032_cable_test_get_status, +- .read_status = at803x_read_status, +- .soft_reset = genphy_soft_reset, +- .config_aneg = at803x_config_aneg, +-}, { +- /* Qualcomm Atheros QCA9561 */ +- PHY_ID_MATCH_EXACT(QCA9561_PHY_ID), +- .name = "Qualcomm Atheros QCA9561 built-in PHY", +- .probe = at803x_probe, +- .suspend = at803x_suspend, +- .resume = at803x_resume, +- .flags = PHY_POLL_CABLE_TEST, +- /* PHY_BASIC_FEATURES */ +- .config_intr = at803x_config_intr, +- .handle_interrupt = at803x_handle_interrupt, +- .cable_test_start = at803x_cable_test_start, +- .cable_test_get_status = at8032_cable_test_get_status, +- .read_status = at803x_read_status, +- .soft_reset = genphy_soft_reset, +- .config_aneg = at803x_config_aneg, +-}, { +- /* QCA8337 */ +- .phy_id = QCA8337_PHY_ID, +- .phy_id_mask = QCA8K_PHY_ID_MASK, +- .name = "Qualcomm Atheros 8337 internal PHY", +- /* PHY_GBIT_FEATURES */ +- .probe = at803x_probe, +- .flags = PHY_IS_INTERNAL, +- .config_init = qca83xx_config_init, +- .soft_reset = genphy_soft_reset, +- .get_sset_count = qca83xx_get_sset_count, +- .get_strings = qca83xx_get_strings, +- .get_stats = qca83xx_get_stats, +- .suspend = qca8337_suspend, +- .resume = qca83xx_resume, +-}, { +- /* QCA8327-A from switch QCA8327-AL1A */ +- .phy_id = QCA8327_A_PHY_ID, +- .phy_id_mask = QCA8K_PHY_ID_MASK, +- .name = "Qualcomm Atheros 8327-A internal PHY", +- /* PHY_GBIT_FEATURES */ +- .link_change_notify = qca83xx_link_change_notify, +- .probe = at803x_probe, +- .flags = PHY_IS_INTERNAL, +- .config_init = qca8327_config_init, +- .soft_reset = genphy_soft_reset, +- .get_sset_count = qca83xx_get_sset_count, +- .get_strings = qca83xx_get_strings, +- .get_stats = qca83xx_get_stats, +- .suspend = qca8327_suspend, +- .resume = qca83xx_resume, +-}, { +- /* QCA8327-B from switch QCA8327-BL1A */ +- .phy_id = QCA8327_B_PHY_ID, +- .phy_id_mask = QCA8K_PHY_ID_MASK, +- .name = "Qualcomm Atheros 8327-B internal PHY", +- /* PHY_GBIT_FEATURES */ +- .link_change_notify = qca83xx_link_change_notify, +- .probe = at803x_probe, +- .flags = PHY_IS_INTERNAL, +- .config_init = qca8327_config_init, +- .soft_reset = genphy_soft_reset, +- .get_sset_count = qca83xx_get_sset_count, +- .get_strings = qca83xx_get_strings, +- .get_stats = qca83xx_get_stats, +- .suspend = qca8327_suspend, +- .resume = qca83xx_resume, +-}, { +- /* Qualcomm QCA8081 */ +- PHY_ID_MATCH_EXACT(QCA8081_PHY_ID), +- .name = "Qualcomm QCA8081", +- .flags = PHY_POLL_CABLE_TEST, +- .probe = at803x_probe, +- .config_intr = at803x_config_intr, +- .handle_interrupt = at803x_handle_interrupt, +- .get_tunable = at803x_get_tunable, +- .set_tunable = at803x_set_tunable, +- .set_wol = at803x_set_wol, +- .get_wol = at803x_get_wol, +- .get_features = qca808x_get_features, +- .config_aneg = qca808x_config_aneg, +- .suspend = genphy_suspend, +- .resume = genphy_resume, +- .read_status = qca808x_read_status, +- .config_init = qca808x_config_init, +- .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, +- .led_brightness_set = qca808x_led_brightness_set, +- .led_blink_set = qca808x_led_blink_set, +- .led_hw_is_supported = qca808x_led_hw_is_supported, +- .led_hw_control_set = qca808x_led_hw_control_set, +- .led_hw_control_get = qca808x_led_hw_control_get, +- .led_polarity_set = qca808x_led_polarity_set, +-}, }; +- +-module_phy_driver(at803x_driver); +- +-static struct mdio_device_id __maybe_unused atheros_tbl[] = { +- { ATH8030_PHY_ID, AT8030_PHY_ID_MASK }, +- { PHY_ID_MATCH_EXACT(ATH8031_PHY_ID) }, +- { PHY_ID_MATCH_EXACT(ATH8032_PHY_ID) }, +- { PHY_ID_MATCH_EXACT(ATH8035_PHY_ID) }, +- { PHY_ID_MATCH_EXACT(ATH9331_PHY_ID) }, +- { PHY_ID_MATCH_EXACT(QCA8337_PHY_ID) }, +- { PHY_ID_MATCH_EXACT(QCA8327_A_PHY_ID) }, +- { PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID) }, +- { PHY_ID_MATCH_EXACT(QCA9561_PHY_ID) }, +- { PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) }, +- { } +-}; +- +-MODULE_DEVICE_TABLE(mdio, atheros_tbl); +--- /dev/null ++++ b/drivers/net/phy/qcom/at803x.c +@@ -0,0 +1,2759 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * drivers/net/phy/at803x.c ++ * ++ * Driver for Qualcomm Atheros AR803x PHY ++ * ++ * Author: Matus Ujhelyi ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define AT803X_SPECIFIC_FUNCTION_CONTROL 0x10 ++#define AT803X_SFC_ASSERT_CRS BIT(11) ++#define AT803X_SFC_FORCE_LINK BIT(10) ++#define AT803X_SFC_MDI_CROSSOVER_MODE_M GENMASK(6, 5) ++#define AT803X_SFC_AUTOMATIC_CROSSOVER 0x3 ++#define AT803X_SFC_MANUAL_MDIX 0x1 ++#define AT803X_SFC_MANUAL_MDI 0x0 ++#define AT803X_SFC_SQE_TEST BIT(2) ++#define AT803X_SFC_POLARITY_REVERSAL BIT(1) ++#define AT803X_SFC_DISABLE_JABBER BIT(0) ++ ++#define AT803X_SPECIFIC_STATUS 0x11 ++#define AT803X_SS_SPEED_MASK GENMASK(15, 14) ++#define AT803X_SS_SPEED_1000 2 ++#define AT803X_SS_SPEED_100 1 ++#define AT803X_SS_SPEED_10 0 ++#define AT803X_SS_DUPLEX BIT(13) ++#define AT803X_SS_SPEED_DUPLEX_RESOLVED BIT(11) ++#define AT803X_SS_MDIX BIT(6) ++ ++#define QCA808X_SS_SPEED_MASK GENMASK(9, 7) ++#define QCA808X_SS_SPEED_2500 4 ++ ++#define AT803X_INTR_ENABLE 0x12 ++#define AT803X_INTR_ENABLE_AUTONEG_ERR BIT(15) ++#define AT803X_INTR_ENABLE_SPEED_CHANGED BIT(14) ++#define AT803X_INTR_ENABLE_DUPLEX_CHANGED BIT(13) ++#define AT803X_INTR_ENABLE_PAGE_RECEIVED BIT(12) ++#define AT803X_INTR_ENABLE_LINK_FAIL BIT(11) ++#define AT803X_INTR_ENABLE_LINK_SUCCESS BIT(10) ++#define AT803X_INTR_ENABLE_LINK_FAIL_BX BIT(8) ++#define AT803X_INTR_ENABLE_LINK_SUCCESS_BX BIT(7) ++#define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE BIT(5) ++#define AT803X_INTR_ENABLE_POLARITY_CHANGED BIT(1) ++#define AT803X_INTR_ENABLE_WOL BIT(0) ++ ++#define AT803X_INTR_STATUS 0x13 ++ ++#define AT803X_SMART_SPEED 0x14 ++#define AT803X_SMART_SPEED_ENABLE BIT(5) ++#define AT803X_SMART_SPEED_RETRY_LIMIT_MASK GENMASK(4, 2) ++#define AT803X_SMART_SPEED_BYPASS_TIMER BIT(1) ++#define AT803X_CDT 0x16 ++#define AT803X_CDT_MDI_PAIR_MASK GENMASK(9, 8) ++#define AT803X_CDT_ENABLE_TEST BIT(0) ++#define AT803X_CDT_STATUS 0x1c ++#define AT803X_CDT_STATUS_STAT_NORMAL 0 ++#define AT803X_CDT_STATUS_STAT_SHORT 1 ++#define AT803X_CDT_STATUS_STAT_OPEN 2 ++#define AT803X_CDT_STATUS_STAT_FAIL 3 ++#define AT803X_CDT_STATUS_STAT_MASK GENMASK(9, 8) ++#define AT803X_CDT_STATUS_DELTA_TIME_MASK GENMASK(7, 0) ++#define AT803X_LED_CONTROL 0x18 ++ ++#define AT803X_PHY_MMD3_WOL_CTRL 0x8012 ++#define AT803X_WOL_EN BIT(5) ++#define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C ++#define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B ++#define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A ++#define AT803X_REG_CHIP_CONFIG 0x1f ++#define AT803X_BT_BX_REG_SEL 0x8000 ++ ++#define AT803X_DEBUG_ADDR 0x1D ++#define AT803X_DEBUG_DATA 0x1E ++ ++#define AT803X_MODE_CFG_MASK 0x0F ++#define AT803X_MODE_CFG_BASET_RGMII 0x00 ++#define AT803X_MODE_CFG_BASET_SGMII 0x01 ++#define AT803X_MODE_CFG_BX1000_RGMII_50OHM 0x02 ++#define AT803X_MODE_CFG_BX1000_RGMII_75OHM 0x03 ++#define AT803X_MODE_CFG_BX1000_CONV_50OHM 0x04 ++#define AT803X_MODE_CFG_BX1000_CONV_75OHM 0x05 ++#define AT803X_MODE_CFG_FX100_RGMII_50OHM 0x06 ++#define AT803X_MODE_CFG_FX100_CONV_50OHM 0x07 ++#define AT803X_MODE_CFG_RGMII_AUTO_MDET 0x0B ++#define AT803X_MODE_CFG_FX100_RGMII_75OHM 0x0E ++#define AT803X_MODE_CFG_FX100_CONV_75OHM 0x0F ++ ++#define AT803X_PSSR 0x11 /*PHY-Specific Status Register*/ ++#define AT803X_PSSR_MR_AN_COMPLETE 0x0200 ++ ++#define AT803X_DEBUG_ANALOG_TEST_CTRL 0x00 ++#define QCA8327_DEBUG_MANU_CTRL_EN BIT(2) ++#define QCA8337_DEBUG_MANU_CTRL_EN GENMASK(3, 2) ++#define AT803X_DEBUG_RX_CLK_DLY_EN BIT(15) ++ ++#define AT803X_DEBUG_SYSTEM_CTRL_MODE 0x05 ++#define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8) ++ ++#define AT803X_DEBUG_REG_HIB_CTRL 0x0b ++#define AT803X_DEBUG_HIB_CTRL_SEL_RST_80U BIT(10) ++#define AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE BIT(13) ++#define AT803X_DEBUG_HIB_CTRL_PS_HIB_EN BIT(15) ++ ++#define AT803X_DEBUG_REG_3C 0x3C ++ ++#define AT803X_DEBUG_REG_GREEN 0x3D ++#define AT803X_DEBUG_GATE_CLK_IN1000 BIT(6) ++ ++#define AT803X_DEBUG_REG_1F 0x1F ++#define AT803X_DEBUG_PLL_ON BIT(2) ++#define AT803X_DEBUG_RGMII_1V8 BIT(3) ++ ++#define MDIO_AZ_DEBUG 0x800D ++ ++/* AT803x supports either the XTAL input pad, an internal PLL or the ++ * DSP as clock reference for the clock output pad. The XTAL reference ++ * is only used for 25 MHz output, all other frequencies need the PLL. ++ * The DSP as a clock reference is used in synchronous ethernet ++ * applications. ++ * ++ * By default the PLL is only enabled if there is a link. Otherwise ++ * the PHY will go into low power state and disabled the PLL. You can ++ * set the PLL_ON bit (see debug register 0x1f) to keep the PLL always ++ * enabled. ++ */ ++#define AT803X_MMD7_CLK25M 0x8016 ++#define AT803X_CLK_OUT_MASK GENMASK(4, 2) ++#define AT803X_CLK_OUT_25MHZ_XTAL 0 ++#define AT803X_CLK_OUT_25MHZ_DSP 1 ++#define AT803X_CLK_OUT_50MHZ_PLL 2 ++#define AT803X_CLK_OUT_50MHZ_DSP 3 ++#define AT803X_CLK_OUT_62_5MHZ_PLL 4 ++#define AT803X_CLK_OUT_62_5MHZ_DSP 5 ++#define AT803X_CLK_OUT_125MHZ_PLL 6 ++#define AT803X_CLK_OUT_125MHZ_DSP 7 ++ ++/* The AR8035 has another mask which is compatible with the AR8031/AR8033 mask ++ * but doesn't support choosing between XTAL/PLL and DSP. ++ */ ++#define AT8035_CLK_OUT_MASK GENMASK(4, 3) ++ ++#define AT803X_CLK_OUT_STRENGTH_MASK GENMASK(8, 7) ++#define AT803X_CLK_OUT_STRENGTH_FULL 0 ++#define AT803X_CLK_OUT_STRENGTH_HALF 1 ++#define AT803X_CLK_OUT_STRENGTH_QUARTER 2 ++ ++#define AT803X_DEFAULT_DOWNSHIFT 5 ++#define AT803X_MIN_DOWNSHIFT 2 ++#define AT803X_MAX_DOWNSHIFT 9 ++ ++#define AT803X_MMD3_SMARTEEE_CTL1 0x805b ++#define AT803X_MMD3_SMARTEEE_CTL2 0x805c ++#define AT803X_MMD3_SMARTEEE_CTL3 0x805d ++#define AT803X_MMD3_SMARTEEE_CTL3_LPI_EN BIT(8) ++ ++#define ATH9331_PHY_ID 0x004dd041 ++#define ATH8030_PHY_ID 0x004dd076 ++#define ATH8031_PHY_ID 0x004dd074 ++#define ATH8032_PHY_ID 0x004dd023 ++#define ATH8035_PHY_ID 0x004dd072 ++#define AT8030_PHY_ID_MASK 0xffffffef ++ ++#define QCA8081_PHY_ID 0x004dd101 ++ ++#define QCA8327_A_PHY_ID 0x004dd033 ++#define QCA8327_B_PHY_ID 0x004dd034 ++#define QCA8337_PHY_ID 0x004dd036 ++#define QCA9561_PHY_ID 0x004dd042 ++#define QCA8K_PHY_ID_MASK 0xffffffff ++ ++#define QCA8K_DEVFLAGS_REVISION_MASK GENMASK(2, 0) ++ ++#define AT803X_PAGE_FIBER 0 ++#define AT803X_PAGE_COPPER 1 ++ ++/* don't turn off internal PLL */ ++#define AT803X_KEEP_PLL_ENABLED BIT(0) ++#define AT803X_DISABLE_SMARTEEE BIT(1) ++ ++/* disable hibernation mode */ ++#define AT803X_DISABLE_HIBERNATION_MODE BIT(2) ++ ++/* ADC threshold */ ++#define QCA808X_PHY_DEBUG_ADC_THRESHOLD 0x2c80 ++#define QCA808X_ADC_THRESHOLD_MASK GENMASK(7, 0) ++#define QCA808X_ADC_THRESHOLD_80MV 0 ++#define QCA808X_ADC_THRESHOLD_100MV 0xf0 ++#define QCA808X_ADC_THRESHOLD_200MV 0x0f ++#define QCA808X_ADC_THRESHOLD_300MV 0xff ++ ++/* CLD control */ ++#define QCA808X_PHY_MMD3_ADDR_CLD_CTRL7 0x8007 ++#define QCA808X_8023AZ_AFE_CTRL_MASK GENMASK(8, 4) ++#define QCA808X_8023AZ_AFE_EN 0x90 ++ ++/* AZ control */ ++#define QCA808X_PHY_MMD3_AZ_TRAINING_CTRL 0x8008 ++#define QCA808X_MMD3_AZ_TRAINING_VAL 0x1c32 ++ ++#define QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB 0x8014 ++#define QCA808X_MSE_THRESHOLD_20DB_VALUE 0x529 ++ ++#define QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB 0x800E ++#define QCA808X_MSE_THRESHOLD_17DB_VALUE 0x341 ++ ++#define QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB 0x801E ++#define QCA808X_MSE_THRESHOLD_27DB_VALUE 0x419 ++ ++#define QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB 0x8020 ++#define QCA808X_MSE_THRESHOLD_28DB_VALUE 0x341 ++ ++#define QCA808X_PHY_MMD7_TOP_OPTION1 0x901c ++#define QCA808X_TOP_OPTION1_DATA 0x0 ++ ++#define QCA808X_PHY_MMD3_DEBUG_1 0xa100 ++#define QCA808X_MMD3_DEBUG_1_VALUE 0x9203 ++#define QCA808X_PHY_MMD3_DEBUG_2 0xa101 ++#define QCA808X_MMD3_DEBUG_2_VALUE 0x48ad ++#define QCA808X_PHY_MMD3_DEBUG_3 0xa103 ++#define QCA808X_MMD3_DEBUG_3_VALUE 0x1698 ++#define QCA808X_PHY_MMD3_DEBUG_4 0xa105 ++#define QCA808X_MMD3_DEBUG_4_VALUE 0x8001 ++#define QCA808X_PHY_MMD3_DEBUG_5 0xa106 ++#define QCA808X_MMD3_DEBUG_5_VALUE 0x1111 ++#define QCA808X_PHY_MMD3_DEBUG_6 0xa011 ++#define QCA808X_MMD3_DEBUG_6_VALUE 0x5f85 ++ ++/* master/slave seed config */ ++#define QCA808X_PHY_DEBUG_LOCAL_SEED 9 ++#define QCA808X_MASTER_SLAVE_SEED_ENABLE BIT(1) ++#define QCA808X_MASTER_SLAVE_SEED_CFG GENMASK(12, 2) ++#define QCA808X_MASTER_SLAVE_SEED_RANGE 0x32 ++ ++/* Hibernation yields lower power consumpiton in contrast with normal operation mode. ++ * when the copper cable is unplugged, the PHY enters into hibernation mode in about 10s. ++ */ ++#define QCA808X_DBG_AN_TEST 0xb ++#define QCA808X_HIBERNATION_EN BIT(15) ++ ++#define QCA808X_CDT_ENABLE_TEST BIT(15) ++#define QCA808X_CDT_INTER_CHECK_DIS BIT(13) ++#define QCA808X_CDT_STATUS BIT(11) ++#define QCA808X_CDT_LENGTH_UNIT BIT(10) ++ ++#define QCA808X_MMD3_CDT_STATUS 0x8064 ++#define QCA808X_MMD3_CDT_DIAG_PAIR_A 0x8065 ++#define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066 ++#define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067 ++#define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068 ++#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8) ++#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0) ++ ++#define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12) ++#define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8) ++#define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4) ++#define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0) ++ ++#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0) ++#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0) ++#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1) ++#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2) ++#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3) ++ ++#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2) ++#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1) ++#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2) ++#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3) ++ ++/* NORMAL are MDI with type set to 0 */ ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1 ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ ++ QCA808X_CDT_STATUS_STAT_MDI1) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ ++ QCA808X_CDT_STATUS_STAT_MDI1) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2 ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ ++ QCA808X_CDT_STATUS_STAT_MDI2) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ ++ QCA808X_CDT_STATUS_STAT_MDI2) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3 ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ ++ QCA808X_CDT_STATUS_STAT_MDI3) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ ++ QCA808X_CDT_STATUS_STAT_MDI3) ++ ++/* Added for reference of existence but should be handled by wait_for_completion already */ ++#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3)) ++ ++#define QCA808X_MMD7_LED_GLOBAL 0x8073 ++#define QCA808X_LED_BLINK_1 GENMASK(11, 6) ++#define QCA808X_LED_BLINK_2 GENMASK(5, 0) ++/* Values are the same for both BLINK_1 and BLINK_2 */ ++#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3) ++#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0) ++#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1) ++#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2) ++#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3) ++#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4) ++#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5) ++#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6) ++#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7) ++#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0) ++#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0) ++#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1) ++#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2) ++#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3) ++#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4) ++#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5) ++#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6) ++#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7) ++ ++#define QCA808X_MMD7_LED2_CTRL 0x8074 ++#define QCA808X_MMD7_LED2_FORCE_CTRL 0x8075 ++#define QCA808X_MMD7_LED1_CTRL 0x8076 ++#define QCA808X_MMD7_LED1_FORCE_CTRL 0x8077 ++#define QCA808X_MMD7_LED0_CTRL 0x8078 ++#define QCA808X_MMD7_LED_CTRL(x) (0x8078 - ((x) * 2)) ++ ++/* LED hw control pattern is the same for every LED */ ++#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0) ++#define QCA808X_LED_SPEED2500_ON BIT(15) ++#define QCA808X_LED_SPEED2500_BLINK BIT(14) ++/* Follow blink trigger even if duplex or speed condition doesn't match */ ++#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13) ++#define QCA808X_LED_FULL_DUPLEX_ON BIT(12) ++#define QCA808X_LED_HALF_DUPLEX_ON BIT(11) ++#define QCA808X_LED_TX_BLINK BIT(10) ++#define QCA808X_LED_RX_BLINK BIT(9) ++#define QCA808X_LED_TX_ON_10MS BIT(8) ++#define QCA808X_LED_RX_ON_10MS BIT(7) ++#define QCA808X_LED_SPEED1000_ON BIT(6) ++#define QCA808X_LED_SPEED100_ON BIT(5) ++#define QCA808X_LED_SPEED10_ON BIT(4) ++#define QCA808X_LED_COLLISION_BLINK BIT(3) ++#define QCA808X_LED_SPEED1000_BLINK BIT(2) ++#define QCA808X_LED_SPEED100_BLINK BIT(1) ++#define QCA808X_LED_SPEED10_BLINK BIT(0) ++ ++#define QCA808X_MMD7_LED0_FORCE_CTRL 0x8079 ++#define QCA808X_MMD7_LED_FORCE_CTRL(x) (0x8079 - ((x) * 2)) ++ ++/* LED force ctrl is the same for every LED ++ * No documentation exist for this, not even internal one ++ * with NDA as QCOM gives only info about configuring ++ * hw control pattern rules and doesn't indicate any way ++ * to force the LED to specific mode. ++ * These define comes from reverse and testing and maybe ++ * lack of some info or some info are not entirely correct. ++ * For the basic LED control and hw control these finding ++ * are enough to support LED control in all the required APIs. ++ * ++ * On doing some comparison with implementation with qca807x, ++ * it was found that it's 1:1 equal to it and confirms all the ++ * reverse done. It was also found further specification with the ++ * force mode and the blink modes. ++ */ ++#define QCA808X_LED_FORCE_EN BIT(15) ++#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13) ++#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3) ++#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2) ++#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1) ++#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0) ++ ++#define QCA808X_MMD7_LED_POLARITY_CTRL 0x901a ++/* QSDK sets by default 0x46 to this reg that sets BIT 6 for ++ * LED to active high. It's not clear what BIT 3 and BIT 4 does. ++ */ ++#define QCA808X_LED_ACTIVE_HIGH BIT(6) ++ ++/* QCA808X 1G chip type */ ++#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"); ++ ++enum stat_access_type { ++ PHY, ++ MMD ++}; ++ ++struct at803x_hw_stat { ++ const char *string; ++ u8 reg; ++ u32 mask; ++ enum stat_access_type access_type; ++}; ++ ++static struct at803x_hw_stat qca83xx_hw_stats[] = { ++ { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY}, ++ { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY}, ++ { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD}, ++}; ++ ++struct at803x_ss_mask { ++ u16 speed_mask; ++ u8 speed_shift; ++}; ++ ++struct at803x_priv { ++ int flags; ++ u16 clk_25m_reg; ++ u16 clk_25m_mask; ++ u8 smarteee_lpi_tw_1g; ++ u8 smarteee_lpi_tw_100m; ++ bool is_fiber; ++ bool is_1000basex; ++ struct regulator_dev *vddio_rdev; ++ struct regulator_dev *vddh_rdev; ++ u64 stats[ARRAY_SIZE(qca83xx_hw_stats)]; ++ int led_polarity_mode; ++}; ++ ++struct at803x_context { ++ u16 bmcr; ++ u16 advertise; ++ u16 control1000; ++ u16 int_enable; ++ u16 smart_speed; ++ u16 led_control; ++}; ++ ++static int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data) ++{ ++ int ret; ++ ++ ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); ++ if (ret < 0) ++ return ret; ++ ++ return phy_write(phydev, AT803X_DEBUG_DATA, data); ++} ++ ++static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg) ++{ ++ int ret; ++ ++ ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); ++ if (ret < 0) ++ return ret; ++ ++ return phy_read(phydev, AT803X_DEBUG_DATA); ++} ++ ++static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, ++ u16 clear, u16 set) ++{ ++ u16 val; ++ int ret; ++ ++ ret = at803x_debug_reg_read(phydev, reg); ++ if (ret < 0) ++ return ret; ++ ++ val = ret & 0xffff; ++ val &= ~clear; ++ val |= set; ++ ++ return phy_write(phydev, AT803X_DEBUG_DATA, val); ++} ++ ++static int at803x_write_page(struct phy_device *phydev, int page) ++{ ++ int mask; ++ int set; ++ ++ if (page == AT803X_PAGE_COPPER) { ++ set = AT803X_BT_BX_REG_SEL; ++ mask = 0; ++ } else { ++ set = 0; ++ mask = AT803X_BT_BX_REG_SEL; ++ } ++ ++ return __phy_modify(phydev, AT803X_REG_CHIP_CONFIG, mask, set); ++} ++ ++static int at803x_read_page(struct phy_device *phydev) ++{ ++ int ccr = __phy_read(phydev, AT803X_REG_CHIP_CONFIG); ++ ++ if (ccr < 0) ++ return ccr; ++ ++ if (ccr & AT803X_BT_BX_REG_SEL) ++ return AT803X_PAGE_COPPER; ++ ++ return AT803X_PAGE_FIBER; ++} ++ ++static int at803x_enable_rx_delay(struct phy_device *phydev) ++{ ++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0, ++ AT803X_DEBUG_RX_CLK_DLY_EN); ++} ++ ++static int at803x_enable_tx_delay(struct phy_device *phydev) ++{ ++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0, ++ AT803X_DEBUG_TX_CLK_DLY_EN); ++} ++ ++static int at803x_disable_rx_delay(struct phy_device *phydev) ++{ ++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, ++ AT803X_DEBUG_RX_CLK_DLY_EN, 0); ++} ++ ++static int at803x_disable_tx_delay(struct phy_device *phydev) ++{ ++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, ++ AT803X_DEBUG_TX_CLK_DLY_EN, 0); ++} ++ ++/* save relevant PHY registers to private copy */ ++static void at803x_context_save(struct phy_device *phydev, ++ struct at803x_context *context) ++{ ++ context->bmcr = phy_read(phydev, MII_BMCR); ++ context->advertise = phy_read(phydev, MII_ADVERTISE); ++ context->control1000 = phy_read(phydev, MII_CTRL1000); ++ context->int_enable = phy_read(phydev, AT803X_INTR_ENABLE); ++ context->smart_speed = phy_read(phydev, AT803X_SMART_SPEED); ++ context->led_control = phy_read(phydev, AT803X_LED_CONTROL); ++} ++ ++/* restore relevant PHY registers from private copy */ ++static void at803x_context_restore(struct phy_device *phydev, ++ const struct at803x_context *context) ++{ ++ phy_write(phydev, MII_BMCR, context->bmcr); ++ phy_write(phydev, MII_ADVERTISE, context->advertise); ++ phy_write(phydev, MII_CTRL1000, context->control1000); ++ phy_write(phydev, AT803X_INTR_ENABLE, context->int_enable); ++ phy_write(phydev, AT803X_SMART_SPEED, context->smart_speed); ++ phy_write(phydev, AT803X_LED_CONTROL, context->led_control); ++} ++ ++static int at803x_set_wol(struct phy_device *phydev, ++ struct ethtool_wolinfo *wol) ++{ ++ int ret, irq_enabled; ++ ++ if (wol->wolopts & WAKE_MAGIC) { ++ struct net_device *ndev = phydev->attached_dev; ++ const u8 *mac; ++ unsigned int i; ++ static const unsigned int offsets[] = { ++ AT803X_LOC_MAC_ADDR_32_47_OFFSET, ++ AT803X_LOC_MAC_ADDR_16_31_OFFSET, ++ AT803X_LOC_MAC_ADDR_0_15_OFFSET, ++ }; ++ ++ if (!ndev) ++ return -ENODEV; ++ ++ mac = (const u8 *)ndev->dev_addr; ++ ++ if (!is_valid_ether_addr(mac)) ++ return -EINVAL; ++ ++ for (i = 0; i < 3; i++) ++ phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i], ++ mac[(i * 2) + 1] | (mac[(i * 2)] << 8)); ++ ++ /* Enable WOL interrupt */ ++ ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL); ++ if (ret) ++ return ret; ++ } else { ++ /* Disable WOL interrupt */ ++ ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0); ++ if (ret) ++ return ret; ++ } ++ ++ /* Clear WOL status */ ++ ret = phy_read(phydev, AT803X_INTR_STATUS); ++ if (ret < 0) ++ return ret; ++ ++ /* Check if there are other interrupts except for WOL triggered when PHY is ++ * in interrupt mode, only the interrupts enabled by AT803X_INTR_ENABLE can ++ * be passed up to the interrupt PIN. ++ */ ++ irq_enabled = phy_read(phydev, AT803X_INTR_ENABLE); ++ if (irq_enabled < 0) ++ return irq_enabled; ++ ++ irq_enabled &= ~AT803X_INTR_ENABLE_WOL; ++ if (ret & irq_enabled && !phy_polling_mode(phydev)) ++ phy_trigger_machine(phydev); ++ ++ return 0; ++} ++ ++static void at803x_get_wol(struct phy_device *phydev, ++ struct ethtool_wolinfo *wol) ++{ ++ int value; ++ ++ wol->supported = WAKE_MAGIC; ++ wol->wolopts = 0; ++ ++ value = phy_read(phydev, AT803X_INTR_ENABLE); ++ if (value < 0) ++ return; ++ ++ if (value & AT803X_INTR_ENABLE_WOL) ++ wol->wolopts |= WAKE_MAGIC; ++} ++ ++static int qca83xx_get_sset_count(struct phy_device *phydev) ++{ ++ return ARRAY_SIZE(qca83xx_hw_stats); ++} ++ ++static void qca83xx_get_strings(struct phy_device *phydev, u8 *data) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) { ++ strscpy(data + i * ETH_GSTRING_LEN, ++ qca83xx_hw_stats[i].string, ETH_GSTRING_LEN); ++ } ++} ++ ++static u64 qca83xx_get_stat(struct phy_device *phydev, int i) ++{ ++ struct at803x_hw_stat stat = qca83xx_hw_stats[i]; ++ struct at803x_priv *priv = phydev->priv; ++ int val; ++ u64 ret; ++ ++ if (stat.access_type == MMD) ++ val = phy_read_mmd(phydev, MDIO_MMD_PCS, stat.reg); ++ else ++ val = phy_read(phydev, stat.reg); ++ ++ if (val < 0) { ++ ret = U64_MAX; ++ } else { ++ val = val & stat.mask; ++ priv->stats[i] += val; ++ ret = priv->stats[i]; ++ } ++ ++ return ret; ++} ++ ++static void qca83xx_get_stats(struct phy_device *phydev, ++ struct ethtool_stats *stats, u64 *data) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) ++ data[i] = qca83xx_get_stat(phydev, i); ++} ++ ++static int at803x_suspend(struct phy_device *phydev) ++{ ++ int value; ++ int wol_enabled; ++ ++ value = phy_read(phydev, AT803X_INTR_ENABLE); ++ wol_enabled = value & AT803X_INTR_ENABLE_WOL; ++ ++ if (wol_enabled) ++ value = BMCR_ISOLATE; ++ else ++ value = BMCR_PDOWN; ++ ++ phy_modify(phydev, MII_BMCR, 0, value); ++ ++ return 0; ++} ++ ++static int at803x_resume(struct phy_device *phydev) ++{ ++ return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0); ++} ++ ++static int at803x_parse_dt(struct phy_device *phydev) ++{ ++ struct device_node *node = phydev->mdio.dev.of_node; ++ struct at803x_priv *priv = phydev->priv; ++ u32 freq, strength, tw; ++ unsigned int sel; ++ int ret; ++ ++ if (!IS_ENABLED(CONFIG_OF_MDIO)) ++ return 0; ++ ++ if (of_property_read_bool(node, "qca,disable-smarteee")) ++ priv->flags |= AT803X_DISABLE_SMARTEEE; ++ ++ if (of_property_read_bool(node, "qca,disable-hibernation-mode")) ++ priv->flags |= AT803X_DISABLE_HIBERNATION_MODE; ++ ++ if (!of_property_read_u32(node, "qca,smarteee-tw-us-1g", &tw)) { ++ if (!tw || tw > 255) { ++ phydev_err(phydev, "invalid qca,smarteee-tw-us-1g\n"); ++ return -EINVAL; ++ } ++ priv->smarteee_lpi_tw_1g = tw; ++ } ++ ++ if (!of_property_read_u32(node, "qca,smarteee-tw-us-100m", &tw)) { ++ if (!tw || tw > 255) { ++ phydev_err(phydev, "invalid qca,smarteee-tw-us-100m\n"); ++ return -EINVAL; ++ } ++ priv->smarteee_lpi_tw_100m = tw; ++ } ++ ++ ret = of_property_read_u32(node, "qca,clk-out-frequency", &freq); ++ if (!ret) { ++ switch (freq) { ++ case 25000000: ++ sel = AT803X_CLK_OUT_25MHZ_XTAL; ++ break; ++ case 50000000: ++ sel = AT803X_CLK_OUT_50MHZ_PLL; ++ break; ++ case 62500000: ++ sel = AT803X_CLK_OUT_62_5MHZ_PLL; ++ break; ++ case 125000000: ++ sel = AT803X_CLK_OUT_125MHZ_PLL; ++ break; ++ default: ++ phydev_err(phydev, "invalid qca,clk-out-frequency\n"); ++ return -EINVAL; ++ } ++ ++ priv->clk_25m_reg |= FIELD_PREP(AT803X_CLK_OUT_MASK, sel); ++ priv->clk_25m_mask |= AT803X_CLK_OUT_MASK; ++ } ++ ++ ret = of_property_read_u32(node, "qca,clk-out-strength", &strength); ++ if (!ret) { ++ priv->clk_25m_mask |= AT803X_CLK_OUT_STRENGTH_MASK; ++ switch (strength) { ++ case AR803X_STRENGTH_FULL: ++ priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_FULL; ++ break; ++ case AR803X_STRENGTH_HALF: ++ priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_HALF; ++ break; ++ case AR803X_STRENGTH_QUARTER: ++ priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_QUARTER; ++ break; ++ default: ++ phydev_err(phydev, "invalid qca,clk-out-strength\n"); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++static int at803x_probe(struct phy_device *phydev) ++{ ++ struct device *dev = &phydev->mdio.dev; ++ struct at803x_priv *priv; ++ int ret; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ /* Init LED polarity mode to -1 */ ++ priv->led_polarity_mode = -1; ++ ++ phydev->priv = priv; ++ ++ ret = at803x_parse_dt(phydev); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int at803x_get_features(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ int err; ++ ++ err = genphy_read_abilities(phydev); ++ if (err) ++ return err; ++ ++ if (phydev->drv->phy_id != ATH8031_PHY_ID) ++ return 0; ++ ++ /* AR8031/AR8033 have different status registers ++ * for copper and fiber operation. However, the ++ * extended status register is the same for both ++ * operation modes. ++ * ++ * As a result of that, ESTATUS_1000_XFULL is set ++ * to 1 even when operating in copper TP mode. ++ * ++ * Remove this mode from the supported link modes ++ * when not operating in 1000BaseX mode. ++ */ ++ if (!priv->is_1000basex) ++ linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, ++ phydev->supported); ++ ++ return 0; ++} ++ ++static int at803x_smarteee_config(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ u16 mask = 0, val = 0; ++ int ret; ++ ++ if (priv->flags & AT803X_DISABLE_SMARTEEE) ++ return phy_modify_mmd(phydev, MDIO_MMD_PCS, ++ AT803X_MMD3_SMARTEEE_CTL3, ++ AT803X_MMD3_SMARTEEE_CTL3_LPI_EN, 0); ++ ++ if (priv->smarteee_lpi_tw_1g) { ++ mask |= 0xff00; ++ val |= priv->smarteee_lpi_tw_1g << 8; ++ } ++ if (priv->smarteee_lpi_tw_100m) { ++ mask |= 0x00ff; ++ val |= priv->smarteee_lpi_tw_100m; ++ } ++ if (!mask) ++ return 0; ++ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL1, ++ mask, val); ++ if (ret) ++ return ret; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL3, ++ AT803X_MMD3_SMARTEEE_CTL3_LPI_EN, ++ AT803X_MMD3_SMARTEEE_CTL3_LPI_EN); ++} ++ ++static int at803x_clk_out_config(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ ++ if (!priv->clk_25m_mask) ++ return 0; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M, ++ priv->clk_25m_mask, priv->clk_25m_reg); ++} ++ ++static int at8031_pll_config(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ ++ /* The default after hardware reset is PLL OFF. After a soft reset, the ++ * values are retained. ++ */ ++ if (priv->flags & AT803X_KEEP_PLL_ENABLED) ++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, ++ 0, AT803X_DEBUG_PLL_ON); ++ else ++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, ++ AT803X_DEBUG_PLL_ON, 0); ++} ++ ++static int at803x_hibernation_mode_config(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ ++ /* The default after hardware reset is hibernation mode enabled. After ++ * software reset, the value is retained. ++ */ ++ if (!(priv->flags & AT803X_DISABLE_HIBERNATION_MODE)) ++ return 0; ++ ++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL, ++ AT803X_DEBUG_HIB_CTRL_PS_HIB_EN, 0); ++} ++ ++static int at803x_config_init(struct phy_device *phydev) ++{ ++ int ret; ++ ++ /* The RX and TX delay default is: ++ * after HW reset: RX delay enabled and TX delay disabled ++ * after SW reset: RX delay enabled, while TX delay retains the ++ * value before reset. ++ */ ++ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || ++ phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) ++ ret = at803x_enable_rx_delay(phydev); ++ else ++ ret = at803x_disable_rx_delay(phydev); ++ if (ret < 0) ++ return ret; ++ ++ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || ++ phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) ++ ret = at803x_enable_tx_delay(phydev); ++ else ++ ret = at803x_disable_tx_delay(phydev); ++ if (ret < 0) ++ return ret; ++ ++ ret = at803x_smarteee_config(phydev); ++ if (ret < 0) ++ return ret; ++ ++ ret = at803x_clk_out_config(phydev); ++ if (ret < 0) ++ return ret; ++ ++ ret = at803x_hibernation_mode_config(phydev); ++ if (ret < 0) ++ return ret; ++ ++ /* Ar803x extended next page bit is enabled by default. Cisco ++ * multigig switches read this bit and attempt to negotiate 10Gbps ++ * rates even if the next page bit is disabled. This is incorrect ++ * behaviour but we still need to accommodate it. XNP is only needed ++ * for 10Gbps support, so disable XNP. ++ */ ++ return phy_modify(phydev, MII_ADVERTISE, MDIO_AN_CTRL1_XNP, 0); ++} ++ ++static int at803x_ack_interrupt(struct phy_device *phydev) ++{ ++ int err; ++ ++ err = phy_read(phydev, AT803X_INTR_STATUS); ++ ++ return (err < 0) ? err : 0; ++} ++ ++static int at803x_config_intr(struct phy_device *phydev) ++{ ++ int err; ++ int value; ++ ++ value = phy_read(phydev, AT803X_INTR_ENABLE); ++ ++ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { ++ /* Clear any pending interrupts */ ++ err = at803x_ack_interrupt(phydev); ++ if (err) ++ return err; ++ ++ value |= AT803X_INTR_ENABLE_AUTONEG_ERR; ++ value |= AT803X_INTR_ENABLE_SPEED_CHANGED; ++ value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED; ++ value |= AT803X_INTR_ENABLE_LINK_FAIL; ++ value |= AT803X_INTR_ENABLE_LINK_SUCCESS; ++ ++ err = phy_write(phydev, AT803X_INTR_ENABLE, value); ++ } else { ++ err = phy_write(phydev, AT803X_INTR_ENABLE, 0); ++ if (err) ++ return err; ++ ++ /* Clear any pending interrupts */ ++ err = at803x_ack_interrupt(phydev); ++ } ++ ++ return err; ++} ++ ++static irqreturn_t at803x_handle_interrupt(struct phy_device *phydev) ++{ ++ int irq_status, int_enabled; ++ ++ irq_status = phy_read(phydev, AT803X_INTR_STATUS); ++ if (irq_status < 0) { ++ phy_error(phydev); ++ return IRQ_NONE; ++ } ++ ++ /* Read the current enabled interrupts */ ++ int_enabled = phy_read(phydev, AT803X_INTR_ENABLE); ++ if (int_enabled < 0) { ++ phy_error(phydev); ++ return IRQ_NONE; ++ } ++ ++ /* See if this was one of our enabled interrupts */ ++ if (!(irq_status & int_enabled)) ++ return IRQ_NONE; ++ ++ phy_trigger_machine(phydev); ++ ++ return IRQ_HANDLED; ++} ++ ++static void at803x_link_change_notify(struct phy_device *phydev) ++{ ++ /* ++ * Conduct a hardware reset for AT8030 every time a link loss is ++ * signalled. This is necessary to circumvent a hardware bug that ++ * occurs when the cable is unplugged while TX packets are pending ++ * in the FIFO. In such cases, the FIFO enters an error mode it ++ * cannot recover from by software. ++ */ ++ if (phydev->state == PHY_NOLINK && phydev->mdio.reset_gpio) { ++ struct at803x_context context; ++ ++ at803x_context_save(phydev, &context); ++ ++ phy_device_reset(phydev, 1); ++ usleep_range(1000, 2000); ++ phy_device_reset(phydev, 0); ++ usleep_range(1000, 2000); ++ ++ at803x_context_restore(phydev, &context); ++ ++ phydev_dbg(phydev, "%s(): phy was reset\n", __func__); ++ } ++} ++ ++static int at803x_read_specific_status(struct phy_device *phydev, ++ struct at803x_ss_mask ss_mask) ++{ ++ int ss; ++ ++ /* Read the AT8035 PHY-Specific Status register, which indicates the ++ * speed and duplex that the PHY is actually using, irrespective of ++ * whether we are in autoneg mode or not. ++ */ ++ ss = phy_read(phydev, AT803X_SPECIFIC_STATUS); ++ if (ss < 0) ++ return ss; ++ ++ if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) { ++ int sfc, speed; ++ ++ sfc = phy_read(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL); ++ if (sfc < 0) ++ return sfc; ++ ++ speed = ss & ss_mask.speed_mask; ++ speed >>= ss_mask.speed_shift; ++ ++ switch (speed) { ++ case AT803X_SS_SPEED_10: ++ phydev->speed = SPEED_10; ++ break; ++ case AT803X_SS_SPEED_100: ++ phydev->speed = SPEED_100; ++ break; ++ case AT803X_SS_SPEED_1000: ++ phydev->speed = SPEED_1000; ++ break; ++ case QCA808X_SS_SPEED_2500: ++ phydev->speed = SPEED_2500; ++ break; ++ } ++ if (ss & AT803X_SS_DUPLEX) ++ phydev->duplex = DUPLEX_FULL; ++ else ++ phydev->duplex = DUPLEX_HALF; ++ ++ if (ss & AT803X_SS_MDIX) ++ phydev->mdix = ETH_TP_MDI_X; ++ else ++ phydev->mdix = ETH_TP_MDI; ++ ++ switch (FIELD_GET(AT803X_SFC_MDI_CROSSOVER_MODE_M, sfc)) { ++ case AT803X_SFC_MANUAL_MDI: ++ phydev->mdix_ctrl = ETH_TP_MDI; ++ break; ++ case AT803X_SFC_MANUAL_MDIX: ++ phydev->mdix_ctrl = ETH_TP_MDI_X; ++ break; ++ case AT803X_SFC_AUTOMATIC_CROSSOVER: ++ phydev->mdix_ctrl = ETH_TP_MDI_AUTO; ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++static int at803x_read_status(struct phy_device *phydev) ++{ ++ struct at803x_ss_mask ss_mask = { 0 }; ++ int err, old_link = phydev->link; ++ ++ /* Update the link, but return if there was an error */ ++ err = genphy_update_link(phydev); ++ if (err) ++ return err; ++ ++ /* why bother the PHY if nothing can have changed */ ++ if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) ++ return 0; ++ ++ phydev->speed = SPEED_UNKNOWN; ++ phydev->duplex = DUPLEX_UNKNOWN; ++ phydev->pause = 0; ++ phydev->asym_pause = 0; ++ ++ err = genphy_read_lpa(phydev); ++ if (err < 0) ++ return err; ++ ++ ss_mask.speed_mask = AT803X_SS_SPEED_MASK; ++ ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK); ++ err = at803x_read_specific_status(phydev, ss_mask); ++ if (err < 0) ++ return err; ++ ++ if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) ++ phy_resolve_aneg_pause(phydev); ++ ++ return 0; ++} ++ ++static int at803x_config_mdix(struct phy_device *phydev, u8 ctrl) ++{ ++ u16 val; ++ ++ switch (ctrl) { ++ case ETH_TP_MDI: ++ val = AT803X_SFC_MANUAL_MDI; ++ break; ++ case ETH_TP_MDI_X: ++ val = AT803X_SFC_MANUAL_MDIX; ++ break; ++ case ETH_TP_MDI_AUTO: ++ val = AT803X_SFC_AUTOMATIC_CROSSOVER; ++ break; ++ default: ++ return 0; ++ } ++ ++ return phy_modify_changed(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL, ++ AT803X_SFC_MDI_CROSSOVER_MODE_M, ++ FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val)); ++} ++ ++static int at803x_prepare_config_aneg(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = at803x_config_mdix(phydev, phydev->mdix_ctrl); ++ if (ret < 0) ++ return ret; ++ ++ /* Changes of the midx bits are disruptive to the normal operation; ++ * therefore any changes to these registers must be followed by a ++ * software reset to take effect. ++ */ ++ if (ret == 1) { ++ ret = genphy_soft_reset(phydev); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int at803x_config_aneg(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ int ret; ++ ++ ret = at803x_prepare_config_aneg(phydev); ++ if (ret) ++ return ret; ++ ++ if (priv->is_1000basex) ++ return genphy_c37_config_aneg(phydev); ++ ++ return genphy_config_aneg(phydev); ++} ++ ++static int at803x_get_downshift(struct phy_device *phydev, u8 *d) ++{ ++ int val; ++ ++ val = phy_read(phydev, AT803X_SMART_SPEED); ++ if (val < 0) ++ return val; ++ ++ if (val & AT803X_SMART_SPEED_ENABLE) ++ *d = FIELD_GET(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, val) + 2; ++ else ++ *d = DOWNSHIFT_DEV_DISABLE; ++ ++ return 0; ++} ++ ++static int at803x_set_downshift(struct phy_device *phydev, u8 cnt) ++{ ++ u16 mask, set; ++ int ret; ++ ++ switch (cnt) { ++ case DOWNSHIFT_DEV_DEFAULT_COUNT: ++ cnt = AT803X_DEFAULT_DOWNSHIFT; ++ fallthrough; ++ case AT803X_MIN_DOWNSHIFT ... AT803X_MAX_DOWNSHIFT: ++ set = AT803X_SMART_SPEED_ENABLE | ++ AT803X_SMART_SPEED_BYPASS_TIMER | ++ FIELD_PREP(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, cnt - 2); ++ mask = AT803X_SMART_SPEED_RETRY_LIMIT_MASK; ++ break; ++ case DOWNSHIFT_DEV_DISABLE: ++ set = 0; ++ mask = AT803X_SMART_SPEED_ENABLE | ++ AT803X_SMART_SPEED_BYPASS_TIMER; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ ret = phy_modify_changed(phydev, AT803X_SMART_SPEED, mask, set); ++ ++ /* After changing the smart speed settings, we need to perform a ++ * software reset, use phy_init_hw() to make sure we set the ++ * reapply any values which might got lost during software reset. ++ */ ++ if (ret == 1) ++ ret = phy_init_hw(phydev); ++ ++ return ret; ++} ++ ++static int at803x_get_tunable(struct phy_device *phydev, ++ struct ethtool_tunable *tuna, void *data) ++{ ++ switch (tuna->id) { ++ case ETHTOOL_PHY_DOWNSHIFT: ++ return at803x_get_downshift(phydev, data); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++static int at803x_set_tunable(struct phy_device *phydev, ++ struct ethtool_tunable *tuna, const void *data) ++{ ++ switch (tuna->id) { ++ case ETHTOOL_PHY_DOWNSHIFT: ++ return at803x_set_downshift(phydev, *(const u8 *)data); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++static int at803x_cable_test_result_trans(u16 status) ++{ ++ switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) { ++ case AT803X_CDT_STATUS_STAT_NORMAL: ++ return ETHTOOL_A_CABLE_RESULT_CODE_OK; ++ case AT803X_CDT_STATUS_STAT_SHORT: ++ return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; ++ case AT803X_CDT_STATUS_STAT_OPEN: ++ return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; ++ case AT803X_CDT_STATUS_STAT_FAIL: ++ default: ++ return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; ++ } ++} ++ ++static bool at803x_cdt_test_failed(u16 status) ++{ ++ return FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status) == ++ AT803X_CDT_STATUS_STAT_FAIL; ++} ++ ++static bool at803x_cdt_fault_length_valid(u16 status) ++{ ++ switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) { ++ case AT803X_CDT_STATUS_STAT_OPEN: ++ case AT803X_CDT_STATUS_STAT_SHORT: ++ return true; ++ } ++ return false; ++} ++ ++static int at803x_cdt_fault_length(int dt) ++{ ++ /* According to the datasheet the distance to the fault is ++ * DELTA_TIME * 0.824 meters. ++ * ++ * The author suspect the correct formula is: ++ * ++ * fault_distance = DELTA_TIME * (c * VF) / 125MHz / 2 ++ * ++ * where c is the speed of light, VF is the velocity factor of ++ * the twisted pair cable, 125MHz the counter frequency and ++ * we need to divide by 2 because the hardware will measure the ++ * round trip time to the fault and back to the PHY. ++ * ++ * With a VF of 0.69 we get the factor 0.824 mentioned in the ++ * datasheet. ++ */ ++ return (dt * 824) / 10; ++} ++ ++static int at803x_cdt_start(struct phy_device *phydev, ++ u32 cdt_start) ++{ ++ return phy_write(phydev, AT803X_CDT, cdt_start); ++} ++ ++static int at803x_cdt_wait_for_completion(struct phy_device *phydev, ++ u32 cdt_en) ++{ ++ int val, ret; ++ ++ /* One test run takes about 25ms */ ++ ret = phy_read_poll_timeout(phydev, AT803X_CDT, val, ++ !(val & cdt_en), ++ 30000, 100000, true); ++ ++ return ret < 0 ? ret : 0; ++} ++ ++static int at803x_cable_test_one_pair(struct phy_device *phydev, int pair) ++{ ++ static const int ethtool_pair[] = { ++ ETHTOOL_A_CABLE_PAIR_A, ++ ETHTOOL_A_CABLE_PAIR_B, ++ ETHTOOL_A_CABLE_PAIR_C, ++ ETHTOOL_A_CABLE_PAIR_D, ++ }; ++ int ret, val; ++ ++ val = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) | ++ AT803X_CDT_ENABLE_TEST; ++ ret = at803x_cdt_start(phydev, val); ++ if (ret) ++ return ret; ++ ++ ret = at803x_cdt_wait_for_completion(phydev, AT803X_CDT_ENABLE_TEST); ++ if (ret) ++ return ret; ++ ++ val = phy_read(phydev, AT803X_CDT_STATUS); ++ if (val < 0) ++ return val; ++ ++ if (at803x_cdt_test_failed(val)) ++ return 0; ++ ++ ethnl_cable_test_result(phydev, ethtool_pair[pair], ++ at803x_cable_test_result_trans(val)); ++ ++ if (at803x_cdt_fault_length_valid(val)) { ++ val = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, val); ++ ethnl_cable_test_fault_length(phydev, ethtool_pair[pair], ++ at803x_cdt_fault_length(val)); ++ } ++ ++ return 1; ++} ++ ++static int at803x_cable_test_get_status(struct phy_device *phydev, ++ bool *finished, unsigned long pair_mask) ++{ ++ int retries = 20; ++ int pair, ret; ++ ++ *finished = false; ++ ++ /* According to the datasheet the CDT can be performed when ++ * there is no link partner or when the link partner is ++ * auto-negotiating. Starting the test will restart the AN ++ * automatically. It seems that doing this repeatedly we will ++ * get a slot where our link partner won't disturb our ++ * measurement. ++ */ ++ while (pair_mask && retries--) { ++ for_each_set_bit(pair, &pair_mask, 4) { ++ ret = at803x_cable_test_one_pair(phydev, pair); ++ if (ret < 0) ++ return ret; ++ if (ret) ++ clear_bit(pair, &pair_mask); ++ } ++ if (pair_mask) ++ msleep(250); ++ } ++ ++ *finished = true; ++ ++ return 0; ++} ++ ++static void at803x_cable_test_autoneg(struct phy_device *phydev) ++{ ++ /* Enable auto-negotiation, but advertise no capabilities, no link ++ * will be established. A restart of the auto-negotiation is not ++ * required, because the cable test will automatically break the link. ++ */ ++ phy_write(phydev, MII_BMCR, BMCR_ANENABLE); ++ phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA); ++} ++ ++static int at803x_cable_test_start(struct phy_device *phydev) ++{ ++ at803x_cable_test_autoneg(phydev); ++ /* we do all the (time consuming) work later */ ++ return 0; ++} ++ ++static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev, ++ unsigned int selector) ++{ ++ struct phy_device *phydev = rdev_get_drvdata(rdev); ++ ++ if (selector) ++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, ++ 0, AT803X_DEBUG_RGMII_1V8); ++ else ++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, ++ AT803X_DEBUG_RGMII_1V8, 0); ++} ++ ++static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev) ++{ ++ struct phy_device *phydev = rdev_get_drvdata(rdev); ++ int val; ++ ++ val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F); ++ if (val < 0) ++ return val; ++ ++ return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0; ++} ++ ++static const struct regulator_ops vddio_regulator_ops = { ++ .list_voltage = regulator_list_voltage_table, ++ .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel, ++ .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel, ++}; ++ ++static const unsigned int vddio_voltage_table[] = { ++ 1500000, ++ 1800000, ++}; ++ ++static const struct regulator_desc vddio_desc = { ++ .name = "vddio", ++ .of_match = of_match_ptr("vddio-regulator"), ++ .n_voltages = ARRAY_SIZE(vddio_voltage_table), ++ .volt_table = vddio_voltage_table, ++ .ops = &vddio_regulator_ops, ++ .type = REGULATOR_VOLTAGE, ++ .owner = THIS_MODULE, ++}; ++ ++static const struct regulator_ops vddh_regulator_ops = { ++}; ++ ++static const struct regulator_desc vddh_desc = { ++ .name = "vddh", ++ .of_match = of_match_ptr("vddh-regulator"), ++ .n_voltages = 1, ++ .fixed_uV = 2500000, ++ .ops = &vddh_regulator_ops, ++ .type = REGULATOR_VOLTAGE, ++ .owner = THIS_MODULE, ++}; ++ ++static int at8031_register_regulators(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ struct device *dev = &phydev->mdio.dev; ++ struct regulator_config config = { }; ++ ++ config.dev = dev; ++ config.driver_data = phydev; ++ ++ priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config); ++ if (IS_ERR(priv->vddio_rdev)) { ++ phydev_err(phydev, "failed to register VDDIO regulator\n"); ++ return PTR_ERR(priv->vddio_rdev); ++ } ++ ++ priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config); ++ if (IS_ERR(priv->vddh_rdev)) { ++ phydev_err(phydev, "failed to register VDDH regulator\n"); ++ return PTR_ERR(priv->vddh_rdev); ++ } ++ ++ return 0; ++} ++ ++static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) ++{ ++ struct phy_device *phydev = upstream; ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support); ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); ++ DECLARE_PHY_INTERFACE_MASK(interfaces); ++ phy_interface_t iface; ++ ++ linkmode_zero(phy_support); ++ phylink_set(phy_support, 1000baseX_Full); ++ phylink_set(phy_support, 1000baseT_Full); ++ phylink_set(phy_support, Autoneg); ++ phylink_set(phy_support, Pause); ++ phylink_set(phy_support, Asym_Pause); ++ ++ linkmode_zero(sfp_support); ++ sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces); ++ /* Some modules support 10G modes as well as others we support. ++ * Mask out non-supported modes so the correct interface is picked. ++ */ ++ linkmode_and(sfp_support, phy_support, sfp_support); ++ ++ if (linkmode_empty(sfp_support)) { ++ dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n"); ++ return -EINVAL; ++ } ++ ++ iface = sfp_select_interface(phydev->sfp_bus, sfp_support); ++ ++ /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes ++ * interface for use with SFP modules. ++ * However, some copper modules detected as having a preferred SGMII ++ * interface do default to and function in 1000Base-X mode, so just ++ * print a warning and allow such modules, as they may have some chance ++ * of working. ++ */ ++ if (iface == PHY_INTERFACE_MODE_SGMII) ++ dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n"); ++ else if (iface != PHY_INTERFACE_MODE_1000BASEX) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static const struct sfp_upstream_ops at8031_sfp_ops = { ++ .attach = phy_sfp_attach, ++ .detach = phy_sfp_detach, ++ .module_insert = at8031_sfp_insert, ++}; ++ ++static int at8031_parse_dt(struct phy_device *phydev) ++{ ++ struct device_node *node = phydev->mdio.dev.of_node; ++ struct at803x_priv *priv = phydev->priv; ++ int ret; ++ ++ if (of_property_read_bool(node, "qca,keep-pll-enabled")) ++ priv->flags |= AT803X_KEEP_PLL_ENABLED; ++ ++ ret = at8031_register_regulators(phydev); ++ if (ret < 0) ++ return ret; ++ ++ ret = devm_regulator_get_enable_optional(&phydev->mdio.dev, ++ "vddio"); ++ if (ret) { ++ phydev_err(phydev, "failed to get VDDIO regulator\n"); ++ return ret; ++ } ++ ++ /* Only AR8031/8033 support 1000Base-X for SFP modules */ ++ return phy_sfp_probe(phydev, &at8031_sfp_ops); ++} ++ ++static int at8031_probe(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ int mode_cfg; ++ int ccr; ++ int ret; ++ ++ ret = at803x_probe(phydev); ++ if (ret) ++ return ret; ++ ++ /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping ++ * options. ++ */ ++ ret = at8031_parse_dt(phydev); ++ if (ret) ++ return ret; ++ ++ ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG); ++ if (ccr < 0) ++ return ccr; ++ mode_cfg = ccr & AT803X_MODE_CFG_MASK; ++ ++ switch (mode_cfg) { ++ case AT803X_MODE_CFG_BX1000_RGMII_50OHM: ++ case AT803X_MODE_CFG_BX1000_RGMII_75OHM: ++ priv->is_1000basex = true; ++ fallthrough; ++ case AT803X_MODE_CFG_FX100_RGMII_50OHM: ++ case AT803X_MODE_CFG_FX100_RGMII_75OHM: ++ priv->is_fiber = true; ++ break; ++ } ++ ++ /* Disable WoL in 1588 register which is enabled ++ * by default ++ */ ++ return phy_modify_mmd(phydev, MDIO_MMD_PCS, ++ AT803X_PHY_MMD3_WOL_CTRL, ++ AT803X_WOL_EN, 0); ++} ++ ++static int at8031_config_init(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ int ret; ++ ++ /* Some bootloaders leave the fiber page selected. ++ * Switch to the appropriate page (fiber or copper), as otherwise we ++ * read the PHY capabilities from the wrong page. ++ */ ++ phy_lock_mdio_bus(phydev); ++ ret = at803x_write_page(phydev, ++ priv->is_fiber ? AT803X_PAGE_FIBER : ++ AT803X_PAGE_COPPER); ++ phy_unlock_mdio_bus(phydev); ++ if (ret) ++ return ret; ++ ++ ret = at8031_pll_config(phydev); ++ if (ret < 0) ++ return ret; ++ ++ return at803x_config_init(phydev); ++} ++ ++static int at8031_set_wol(struct phy_device *phydev, ++ struct ethtool_wolinfo *wol) ++{ ++ int ret; ++ ++ /* First setup MAC address and enable WOL interrupt */ ++ ret = at803x_set_wol(phydev, wol); ++ if (ret) ++ return ret; ++ ++ if (wol->wolopts & WAKE_MAGIC) ++ /* Enable WOL function for 1588 */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, ++ AT803X_PHY_MMD3_WOL_CTRL, ++ 0, AT803X_WOL_EN); ++ else ++ /* Disable WoL function for 1588 */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, ++ AT803X_PHY_MMD3_WOL_CTRL, ++ AT803X_WOL_EN, 0); ++ ++ return ret; ++} ++ ++static int at8031_config_intr(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ int err, value = 0; ++ ++ if (phydev->interrupts == PHY_INTERRUPT_ENABLED && ++ priv->is_fiber) { ++ /* Clear any pending interrupts */ ++ err = at803x_ack_interrupt(phydev); ++ if (err) ++ return err; ++ ++ value |= AT803X_INTR_ENABLE_LINK_FAIL_BX; ++ value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX; ++ ++ err = phy_set_bits(phydev, AT803X_INTR_ENABLE, value); ++ if (err) ++ return err; ++ } ++ ++ return at803x_config_intr(phydev); ++} ++ ++/* AR8031 and AR8033 share the same read status logic */ ++static int at8031_read_status(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ ++ if (priv->is_1000basex) ++ return genphy_c37_read_status(phydev); ++ ++ return at803x_read_status(phydev); ++} ++ ++/* AR8031 and AR8035 share the same cable test get status reg */ ++static int at8031_cable_test_get_status(struct phy_device *phydev, ++ bool *finished) ++{ ++ return at803x_cable_test_get_status(phydev, finished, 0xf); ++} ++ ++/* AR8031 and AR8035 share the same cable test start logic */ ++static int at8031_cable_test_start(struct phy_device *phydev) ++{ ++ at803x_cable_test_autoneg(phydev); ++ phy_write(phydev, MII_CTRL1000, 0); ++ /* we do all the (time consuming) work later */ ++ return 0; ++} ++ ++/* AR8032, AR9331 and QCA9561 share the same cable test get status reg */ ++static int at8032_cable_test_get_status(struct phy_device *phydev, ++ bool *finished) ++{ ++ return at803x_cable_test_get_status(phydev, finished, 0x3); ++} ++ ++static int at8035_parse_dt(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ ++ /* Mask is set by the generic at803x_parse_dt ++ * if property is set. Assume property is set ++ * with the mask not zero. ++ */ ++ if (priv->clk_25m_mask) { ++ /* Fixup for the AR8030/AR8035. This chip has another mask and ++ * doesn't support the DSP reference. Eg. the lowest bit of the ++ * mask. The upper two bits select the same frequencies. Mask ++ * the lowest bit here. ++ * ++ * Warning: ++ * There was no datasheet for the AR8030 available so this is ++ * just a guess. But the AR8035 is listed as pin compatible ++ * to the AR8030 so there might be a good chance it works on ++ * the AR8030 too. ++ */ ++ priv->clk_25m_reg &= AT8035_CLK_OUT_MASK; ++ priv->clk_25m_mask &= AT8035_CLK_OUT_MASK; ++ } ++ ++ return 0; ++} ++ ++/* AR8030 and AR8035 shared the same special mask for clk_25m */ ++static int at8035_probe(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = at803x_probe(phydev); ++ if (ret) ++ return ret; ++ ++ return at8035_parse_dt(phydev); ++} ++ ++static int qca83xx_config_init(struct phy_device *phydev) ++{ ++ u8 switch_revision; ++ ++ switch_revision = phydev->dev_flags & QCA8K_DEVFLAGS_REVISION_MASK; ++ ++ switch (switch_revision) { ++ case 1: ++ /* For 100M waveform */ ++ at803x_debug_reg_write(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0x02ea); ++ /* Turn on Gigabit clock */ ++ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x68a0); ++ break; ++ ++ case 2: ++ phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0); ++ fallthrough; ++ case 4: ++ phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_AZ_DEBUG, 0x803f); ++ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x6860); ++ at803x_debug_reg_write(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0x2c46); ++ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3C, 0x6000); ++ break; ++ } ++ ++ /* Following original QCA sourcecode set port to prefer master */ ++ phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER); ++ ++ return 0; ++} ++ ++static int qca8327_config_init(struct phy_device *phydev) ++{ ++ /* QCA8327 require DAC amplitude adjustment for 100m set to +6%. ++ * Disable on init and enable only with 100m speed following ++ * qca original source code. ++ */ ++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, ++ QCA8327_DEBUG_MANU_CTRL_EN, 0); ++ ++ return qca83xx_config_init(phydev); ++} ++ ++static void qca83xx_link_change_notify(struct phy_device *phydev) ++{ ++ /* Set DAC Amplitude adjustment to +6% for 100m on link running */ ++ if (phydev->state == PHY_RUNNING) { ++ if (phydev->speed == SPEED_100) ++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, ++ QCA8327_DEBUG_MANU_CTRL_EN, ++ QCA8327_DEBUG_MANU_CTRL_EN); ++ } else { ++ /* Reset DAC Amplitude adjustment */ ++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, ++ QCA8327_DEBUG_MANU_CTRL_EN, 0); ++ } ++} ++ ++static int qca83xx_resume(struct phy_device *phydev) ++{ ++ int ret, val; ++ ++ /* Skip reset if not suspended */ ++ if (!phydev->suspended) ++ return 0; ++ ++ /* Reinit the port, reset values set by suspend */ ++ qca83xx_config_init(phydev); ++ ++ /* Reset the port on port resume */ ++ phy_set_bits(phydev, MII_BMCR, BMCR_RESET | BMCR_ANENABLE); ++ ++ /* On resume from suspend the switch execute a reset and ++ * restart auto-negotiation. Wait for reset to complete. ++ */ ++ ret = phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET), ++ 50000, 600000, true); ++ if (ret) ++ return ret; ++ ++ usleep_range(1000, 2000); ++ ++ return 0; ++} ++ ++static int qca83xx_suspend(struct phy_device *phydev) ++{ ++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_GREEN, ++ AT803X_DEBUG_GATE_CLK_IN1000, 0); ++ ++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL, ++ AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE | ++ AT803X_DEBUG_HIB_CTRL_SEL_RST_80U, 0); ++ ++ return 0; ++} ++ ++static int qca8337_suspend(struct phy_device *phydev) ++{ ++ /* Only QCA8337 support actual suspend. */ ++ genphy_suspend(phydev); ++ ++ return qca83xx_suspend(phydev); ++} ++ ++static int qca8327_suspend(struct phy_device *phydev) ++{ ++ u16 mask = 0; ++ ++ /* QCA8327 cause port unreliability when phy suspend ++ * is set. ++ */ ++ mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX); ++ phy_modify(phydev, MII_BMCR, mask, 0); ++ ++ return qca83xx_suspend(phydev); ++} ++ ++static int qca808x_phy_fast_retrain_config(struct phy_device *phydev) ++{ ++ int ret; ++ ++ /* Enable fast retrain */ ++ ret = genphy_c45_fast_retrain(phydev, true); ++ if (ret) ++ return ret; ++ ++ phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1, ++ QCA808X_TOP_OPTION1_DATA); ++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB, ++ QCA808X_MSE_THRESHOLD_20DB_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB, ++ QCA808X_MSE_THRESHOLD_17DB_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB, ++ QCA808X_MSE_THRESHOLD_27DB_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB, ++ QCA808X_MSE_THRESHOLD_28DB_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1, ++ QCA808X_MMD3_DEBUG_1_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4, ++ QCA808X_MMD3_DEBUG_4_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5, ++ QCA808X_MMD3_DEBUG_5_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3, ++ QCA808X_MMD3_DEBUG_3_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6, ++ QCA808X_MMD3_DEBUG_6_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2, ++ QCA808X_MMD3_DEBUG_2_VALUE); ++ ++ return 0; ++} ++ ++static int qca808x_phy_ms_seed_enable(struct phy_device *phydev, bool enable) ++{ ++ u16 seed_value; ++ ++ 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_CFG | QCA808X_MASTER_SLAVE_SEED_ENABLE, ++ FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value) | ++ 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 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; ++ ++ /* Active adc&vga on 802.3az for the link 1000M and 100M */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7, ++ QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN); ++ if (ret) ++ return ret; ++ ++ /* Adjust the threshold on 802.3az for the link 1000M */ ++ ret = phy_write_mmd(phydev, MDIO_MMD_PCS, ++ QCA808X_PHY_MMD3_AZ_TRAINING_CTRL, ++ QCA808X_MMD3_AZ_TRAINING_VAL); ++ if (ret) ++ return ret; ++ ++ 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 */ ++ return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD, ++ QCA808X_ADC_THRESHOLD_MASK, ++ QCA808X_ADC_THRESHOLD_100MV); ++} ++ ++static int qca808x_read_status(struct phy_device *phydev) ++{ ++ struct at803x_ss_mask ss_mask = { 0 }; ++ int ret; ++ ++ ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT); ++ if (ret < 0) ++ return ret; ++ ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising, ++ ret & MDIO_AN_10GBT_STAT_LP2_5G); ++ ++ ret = genphy_read_status(phydev); ++ if (ret) ++ return ret; ++ ++ /* qca8081 takes the different bits for speed value from at803x */ ++ ss_mask.speed_mask = QCA808X_SS_SPEED_MASK; ++ ss_mask.speed_shift = __bf_shf(QCA808X_SS_SPEED_MASK); ++ ret = at803x_read_specific_status(phydev, ss_mask); ++ if (ret < 0) ++ return ret; ++ ++ if (phydev->link) { ++ if (phydev->speed == SPEED_2500) ++ phydev->interface = PHY_INTERFACE_MODE_2500BASEX; ++ else ++ 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 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 (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); ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++static int qca808x_soft_reset(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = genphy_soft_reset(phydev); ++ if (ret < 0) ++ return ret; ++ ++ 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) ++{ ++ switch (cdt_code) { ++ case QCA808X_CDT_STATUS_STAT_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static int qca808x_cable_test_result_trans(int cdt_code) ++{ ++ switch (cdt_code) { ++ case QCA808X_CDT_STATUS_STAT_NORMAL: ++ return ETHTOOL_A_CABLE_RESULT_CODE_OK; ++ case QCA808X_CDT_STATUS_STAT_SAME_SHORT: ++ return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; ++ case QCA808X_CDT_STATUS_STAT_SAME_OPEN: ++ return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: ++ return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT; ++ case QCA808X_CDT_STATUS_STAT_FAIL: ++ default: ++ return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; ++ } ++} ++ ++static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair, ++ int result) ++{ ++ int val; ++ u32 cdt_length_reg = 0; ++ ++ switch (pair) { ++ case ETHTOOL_A_CABLE_PAIR_A: ++ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A; ++ break; ++ case ETHTOOL_A_CABLE_PAIR_B: ++ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B; ++ break; ++ case ETHTOOL_A_CABLE_PAIR_C: ++ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C; ++ break; ++ case ETHTOOL_A_CABLE_PAIR_D: ++ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg); ++ if (val < 0) ++ return val; ++ ++ if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT) ++ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val); ++ else ++ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val); ++ ++ return at803x_cdt_fault_length(val); ++} ++ ++static int qca808x_cable_test_start(struct phy_device *phydev) ++{ ++ int ret; ++ ++ /* perform CDT with the following configs: ++ * 1. disable hibernation. ++ * 2. force PHY working in MDI mode. ++ * 3. for PHY working in 1000BaseT. ++ * 4. configure the threshold. ++ */ ++ ++ ret = at803x_debug_reg_mask(phydev, QCA808X_DBG_AN_TEST, QCA808X_HIBERNATION_EN, 0); ++ if (ret < 0) ++ return ret; ++ ++ ret = at803x_config_mdix(phydev, ETH_TP_MDI); ++ if (ret < 0) ++ return ret; ++ ++ /* Force 1000base-T needs to configure PMA/PMD and MII_BMCR */ ++ phydev->duplex = DUPLEX_FULL; ++ phydev->speed = SPEED_1000; ++ ret = genphy_c45_pma_setup_forced(phydev); ++ if (ret < 0) ++ return ret; ++ ++ ret = genphy_setup_forced(phydev); ++ if (ret < 0) ++ return ret; ++ ++ /* configure the thresholds for open, short, pair ok test */ ++ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8074, 0xc040); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8076, 0xc040); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8077, 0xa060); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8078, 0xc050); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807a, 0xc060); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807e, 0xb060); ++ ++ return 0; ++} ++ ++static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair, ++ u16 status) ++{ ++ int length, result; ++ u16 pair_code; ++ ++ switch (pair) { ++ case ETHTOOL_A_CABLE_PAIR_A: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status); ++ break; ++ case ETHTOOL_A_CABLE_PAIR_B: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status); ++ break; ++ case ETHTOOL_A_CABLE_PAIR_C: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status); ++ break; ++ case ETHTOOL_A_CABLE_PAIR_D: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ result = qca808x_cable_test_result_trans(pair_code); ++ ethnl_cable_test_result(phydev, pair, result); ++ ++ if (qca808x_cdt_fault_length_valid(pair_code)) { ++ length = qca808x_cdt_fault_length(phydev, pair, result); ++ ethnl_cable_test_fault_length(phydev, pair, length); ++ } ++ ++ return 0; ++} ++ ++static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished) ++{ ++ int ret, val; ++ ++ *finished = false; ++ ++ val = QCA808X_CDT_ENABLE_TEST | ++ QCA808X_CDT_LENGTH_UNIT; ++ ret = at803x_cdt_start(phydev, val); ++ if (ret) ++ return ret; ++ ++ ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST); ++ if (ret) ++ return ret; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS); ++ if (val < 0) ++ return val; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val); ++ if (ret) ++ return ret; ++ ++ *finished = true; ++ ++ 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); ++ ++ /* 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; ++} ++ ++static int qca808x_config_aneg(struct phy_device *phydev) ++{ ++ int phy_ctrl = 0; ++ int ret; ++ ++ ret = at803x_prepare_config_aneg(phydev); ++ if (ret) ++ return ret; ++ ++ /* The reg MII_BMCR also needs to be configured for force mode, the ++ * genphy_config_aneg is also needed. ++ */ ++ if (phydev->autoneg == AUTONEG_DISABLE) ++ genphy_c45_pma_setup_forced(phydev); ++ ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising)) ++ phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G; ++ ++ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, ++ MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl); ++ if (ret < 0) ++ return ret; ++ ++ return __genphy_config_aneg(phydev, ret); ++} ++ ++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 int qca808x_led_parse_netdev(struct phy_device *phydev, unsigned long rules, ++ u16 *offload_trigger) ++{ ++ /* Parsing specific to netdev trigger */ ++ if (test_bit(TRIGGER_NETDEV_TX, &rules)) ++ *offload_trigger |= QCA808X_LED_TX_BLINK; ++ if (test_bit(TRIGGER_NETDEV_RX, &rules)) ++ *offload_trigger |= QCA808X_LED_RX_BLINK; ++ if (test_bit(TRIGGER_NETDEV_LINK_10, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED10_ON; ++ if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED100_ON; ++ if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED1000_ON; ++ if (test_bit(TRIGGER_NETDEV_LINK_2500, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED2500_ON; ++ if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules)) ++ *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON; ++ if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules)) ++ *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON; ++ ++ if (rules && !*offload_trigger) ++ return -EOPNOTSUPP; ++ ++ /* Enable BLINK_CHECK_BYPASS by default to make the LED ++ * blink even with duplex or speed mode not enabled. ++ */ ++ *offload_trigger |= QCA808X_LED_BLINK_CHECK_BYPASS; ++ ++ return 0; ++} ++ ++static int qca808x_led_hw_control_enable(struct phy_device *phydev, u8 index) ++{ ++ u16 reg; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_FORCE_CTRL(index); ++ ++ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_FORCE_EN); ++} ++ ++static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ u16 offload_trigger = 0; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ return qca808x_led_parse_netdev(phydev, rules, &offload_trigger); ++} ++ ++static int qca808x_led_hw_control_set(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ u16 reg, offload_trigger = 0; ++ int ret; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_CTRL(index); ++ ++ ret = qca808x_led_parse_netdev(phydev, rules, &offload_trigger); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_led_hw_control_enable(phydev, index); ++ if (ret) ++ return ret; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_PATTERN_MASK, ++ offload_trigger); ++} ++ ++static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index) ++{ ++ u16 reg; ++ int val; ++ ++ if (index > 2) ++ return false; ++ ++ reg = QCA808X_MMD7_LED_FORCE_CTRL(index); ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); ++ ++ return !(val & QCA808X_LED_FORCE_EN); ++} ++ ++static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index, ++ unsigned long *rules) ++{ ++ u16 reg; ++ int val; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ /* Check if we have hw control enabled */ ++ if (qca808x_led_hw_control_status(phydev, index)) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_CTRL(index); ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); ++ if (val & QCA808X_LED_TX_BLINK) ++ set_bit(TRIGGER_NETDEV_TX, rules); ++ if (val & QCA808X_LED_RX_BLINK) ++ set_bit(TRIGGER_NETDEV_RX, rules); ++ if (val & QCA808X_LED_SPEED10_ON) ++ set_bit(TRIGGER_NETDEV_LINK_10, rules); ++ if (val & QCA808X_LED_SPEED100_ON) ++ set_bit(TRIGGER_NETDEV_LINK_100, rules); ++ if (val & QCA808X_LED_SPEED1000_ON) ++ set_bit(TRIGGER_NETDEV_LINK_1000, rules); ++ if (val & QCA808X_LED_SPEED2500_ON) ++ set_bit(TRIGGER_NETDEV_LINK_2500, rules); ++ if (val & QCA808X_LED_HALF_DUPLEX_ON) ++ set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules); ++ if (val & QCA808X_LED_FULL_DUPLEX_ON) ++ set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules); ++ ++ return 0; ++} ++ ++static int qca808x_led_hw_control_reset(struct phy_device *phydev, u8 index) ++{ ++ u16 reg; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_CTRL(index); ++ ++ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_PATTERN_MASK); ++} ++ ++static int qca808x_led_brightness_set(struct phy_device *phydev, ++ u8 index, enum led_brightness value) ++{ ++ u16 reg; ++ int ret; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ if (!value) { ++ ret = qca808x_led_hw_control_reset(phydev, index); ++ if (ret) ++ return ret; ++ } ++ ++ reg = QCA808X_MMD7_LED_FORCE_CTRL(index); ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, ++ QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON : ++ QCA808X_LED_FORCE_OFF); ++} ++ ++static int qca808x_led_blink_set(struct phy_device *phydev, u8 index, ++ unsigned long *delay_on, ++ unsigned long *delay_off) ++{ ++ int ret; ++ u16 reg; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_FORCE_CTRL(index); ++ ++ /* Set blink to 50% off, 50% on at 4Hz by default */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL, ++ QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK, ++ QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50); ++ if (ret) ++ return ret; ++ ++ /* We use BLINK_1 for normal blinking */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, ++ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1); ++ if (ret) ++ return ret; ++ ++ /* We set blink to 4Hz, aka 250ms */ ++ *delay_on = 250 / 2; ++ *delay_off = 250 / 2; ++ ++ return 0; ++} ++ ++static int qca808x_led_polarity_set(struct phy_device *phydev, int index, ++ unsigned long modes) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ bool active_low = false; ++ u32 mode; ++ ++ for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) { ++ switch (mode) { ++ case PHY_LED_ACTIVE_LOW: ++ active_low = true; ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ /* PHY polarity is global and can't be set per LED. ++ * To detect this, check if last requested polarity mode ++ * match the new one. ++ */ ++ if (priv->led_polarity_mode >= 0 && ++ priv->led_polarity_mode != active_low) { ++ phydev_err(phydev, "PHY polarity is global. Mismatched polarity on different LED\n"); ++ return -EINVAL; ++ } ++ ++ /* Save the last PHY polarity mode */ ++ priv->led_polarity_mode = active_low; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_AN, ++ QCA808X_MMD7_LED_POLARITY_CTRL, ++ QCA808X_LED_ACTIVE_HIGH, ++ active_low ? 0 : QCA808X_LED_ACTIVE_HIGH); ++} ++ ++static struct phy_driver at803x_driver[] = { ++{ ++ /* Qualcomm Atheros AR8035 */ ++ PHY_ID_MATCH_EXACT(ATH8035_PHY_ID), ++ .name = "Qualcomm Atheros AR8035", ++ .flags = PHY_POLL_CABLE_TEST, ++ .probe = at8035_probe, ++ .config_aneg = at803x_config_aneg, ++ .config_init = at803x_config_init, ++ .soft_reset = genphy_soft_reset, ++ .set_wol = at803x_set_wol, ++ .get_wol = at803x_get_wol, ++ .suspend = at803x_suspend, ++ .resume = at803x_resume, ++ /* PHY_GBIT_FEATURES */ ++ .read_status = at803x_read_status, ++ .config_intr = at803x_config_intr, ++ .handle_interrupt = at803x_handle_interrupt, ++ .get_tunable = at803x_get_tunable, ++ .set_tunable = at803x_set_tunable, ++ .cable_test_start = at8031_cable_test_start, ++ .cable_test_get_status = at8031_cable_test_get_status, ++}, { ++ /* Qualcomm Atheros AR8030 */ ++ .phy_id = ATH8030_PHY_ID, ++ .name = "Qualcomm Atheros AR8030", ++ .phy_id_mask = AT8030_PHY_ID_MASK, ++ .probe = at8035_probe, ++ .config_init = at803x_config_init, ++ .link_change_notify = at803x_link_change_notify, ++ .set_wol = at803x_set_wol, ++ .get_wol = at803x_get_wol, ++ .suspend = at803x_suspend, ++ .resume = at803x_resume, ++ /* PHY_BASIC_FEATURES */ ++ .config_intr = at803x_config_intr, ++ .handle_interrupt = at803x_handle_interrupt, ++}, { ++ /* Qualcomm Atheros AR8031/AR8033 */ ++ PHY_ID_MATCH_EXACT(ATH8031_PHY_ID), ++ .name = "Qualcomm Atheros AR8031/AR8033", ++ .flags = PHY_POLL_CABLE_TEST, ++ .probe = at8031_probe, ++ .config_init = at8031_config_init, ++ .config_aneg = at803x_config_aneg, ++ .soft_reset = genphy_soft_reset, ++ .set_wol = at8031_set_wol, ++ .get_wol = at803x_get_wol, ++ .suspend = at803x_suspend, ++ .resume = at803x_resume, ++ .read_page = at803x_read_page, ++ .write_page = at803x_write_page, ++ .get_features = at803x_get_features, ++ .read_status = at8031_read_status, ++ .config_intr = at8031_config_intr, ++ .handle_interrupt = at803x_handle_interrupt, ++ .get_tunable = at803x_get_tunable, ++ .set_tunable = at803x_set_tunable, ++ .cable_test_start = at8031_cable_test_start, ++ .cable_test_get_status = at8031_cable_test_get_status, ++}, { ++ /* Qualcomm Atheros AR8032 */ ++ PHY_ID_MATCH_EXACT(ATH8032_PHY_ID), ++ .name = "Qualcomm Atheros AR8032", ++ .probe = at803x_probe, ++ .flags = PHY_POLL_CABLE_TEST, ++ .config_init = at803x_config_init, ++ .link_change_notify = at803x_link_change_notify, ++ .suspend = at803x_suspend, ++ .resume = at803x_resume, ++ /* PHY_BASIC_FEATURES */ ++ .config_intr = at803x_config_intr, ++ .handle_interrupt = at803x_handle_interrupt, ++ .cable_test_start = at803x_cable_test_start, ++ .cable_test_get_status = at8032_cable_test_get_status, ++}, { ++ /* ATHEROS AR9331 */ ++ PHY_ID_MATCH_EXACT(ATH9331_PHY_ID), ++ .name = "Qualcomm Atheros AR9331 built-in PHY", ++ .probe = at803x_probe, ++ .suspend = at803x_suspend, ++ .resume = at803x_resume, ++ .flags = PHY_POLL_CABLE_TEST, ++ /* PHY_BASIC_FEATURES */ ++ .config_intr = at803x_config_intr, ++ .handle_interrupt = at803x_handle_interrupt, ++ .cable_test_start = at803x_cable_test_start, ++ .cable_test_get_status = at8032_cable_test_get_status, ++ .read_status = at803x_read_status, ++ .soft_reset = genphy_soft_reset, ++ .config_aneg = at803x_config_aneg, ++}, { ++ /* Qualcomm Atheros QCA9561 */ ++ PHY_ID_MATCH_EXACT(QCA9561_PHY_ID), ++ .name = "Qualcomm Atheros QCA9561 built-in PHY", ++ .probe = at803x_probe, ++ .suspend = at803x_suspend, ++ .resume = at803x_resume, ++ .flags = PHY_POLL_CABLE_TEST, ++ /* PHY_BASIC_FEATURES */ ++ .config_intr = at803x_config_intr, ++ .handle_interrupt = at803x_handle_interrupt, ++ .cable_test_start = at803x_cable_test_start, ++ .cable_test_get_status = at8032_cable_test_get_status, ++ .read_status = at803x_read_status, ++ .soft_reset = genphy_soft_reset, ++ .config_aneg = at803x_config_aneg, ++}, { ++ /* QCA8337 */ ++ .phy_id = QCA8337_PHY_ID, ++ .phy_id_mask = QCA8K_PHY_ID_MASK, ++ .name = "Qualcomm Atheros 8337 internal PHY", ++ /* PHY_GBIT_FEATURES */ ++ .probe = at803x_probe, ++ .flags = PHY_IS_INTERNAL, ++ .config_init = qca83xx_config_init, ++ .soft_reset = genphy_soft_reset, ++ .get_sset_count = qca83xx_get_sset_count, ++ .get_strings = qca83xx_get_strings, ++ .get_stats = qca83xx_get_stats, ++ .suspend = qca8337_suspend, ++ .resume = qca83xx_resume, ++}, { ++ /* QCA8327-A from switch QCA8327-AL1A */ ++ .phy_id = QCA8327_A_PHY_ID, ++ .phy_id_mask = QCA8K_PHY_ID_MASK, ++ .name = "Qualcomm Atheros 8327-A internal PHY", ++ /* PHY_GBIT_FEATURES */ ++ .link_change_notify = qca83xx_link_change_notify, ++ .probe = at803x_probe, ++ .flags = PHY_IS_INTERNAL, ++ .config_init = qca8327_config_init, ++ .soft_reset = genphy_soft_reset, ++ .get_sset_count = qca83xx_get_sset_count, ++ .get_strings = qca83xx_get_strings, ++ .get_stats = qca83xx_get_stats, ++ .suspend = qca8327_suspend, ++ .resume = qca83xx_resume, ++}, { ++ /* QCA8327-B from switch QCA8327-BL1A */ ++ .phy_id = QCA8327_B_PHY_ID, ++ .phy_id_mask = QCA8K_PHY_ID_MASK, ++ .name = "Qualcomm Atheros 8327-B internal PHY", ++ /* PHY_GBIT_FEATURES */ ++ .link_change_notify = qca83xx_link_change_notify, ++ .probe = at803x_probe, ++ .flags = PHY_IS_INTERNAL, ++ .config_init = qca8327_config_init, ++ .soft_reset = genphy_soft_reset, ++ .get_sset_count = qca83xx_get_sset_count, ++ .get_strings = qca83xx_get_strings, ++ .get_stats = qca83xx_get_stats, ++ .suspend = qca8327_suspend, ++ .resume = qca83xx_resume, ++}, { ++ /* Qualcomm QCA8081 */ ++ PHY_ID_MATCH_EXACT(QCA8081_PHY_ID), ++ .name = "Qualcomm QCA8081", ++ .flags = PHY_POLL_CABLE_TEST, ++ .probe = at803x_probe, ++ .config_intr = at803x_config_intr, ++ .handle_interrupt = at803x_handle_interrupt, ++ .get_tunable = at803x_get_tunable, ++ .set_tunable = at803x_set_tunable, ++ .set_wol = at803x_set_wol, ++ .get_wol = at803x_get_wol, ++ .get_features = qca808x_get_features, ++ .config_aneg = qca808x_config_aneg, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .read_status = qca808x_read_status, ++ .config_init = qca808x_config_init, ++ .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, ++ .led_brightness_set = qca808x_led_brightness_set, ++ .led_blink_set = qca808x_led_blink_set, ++ .led_hw_is_supported = qca808x_led_hw_is_supported, ++ .led_hw_control_set = qca808x_led_hw_control_set, ++ .led_hw_control_get = qca808x_led_hw_control_get, ++ .led_polarity_set = qca808x_led_polarity_set, ++}, }; ++ ++module_phy_driver(at803x_driver); ++ ++static struct mdio_device_id __maybe_unused atheros_tbl[] = { ++ { ATH8030_PHY_ID, AT8030_PHY_ID_MASK }, ++ { PHY_ID_MATCH_EXACT(ATH8031_PHY_ID) }, ++ { PHY_ID_MATCH_EXACT(ATH8032_PHY_ID) }, ++ { PHY_ID_MATCH_EXACT(ATH8035_PHY_ID) }, ++ { PHY_ID_MATCH_EXACT(ATH9331_PHY_ID) }, ++ { PHY_ID_MATCH_EXACT(QCA8337_PHY_ID) }, ++ { PHY_ID_MATCH_EXACT(QCA8327_A_PHY_ID) }, ++ { PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID) }, ++ { PHY_ID_MATCH_EXACT(QCA9561_PHY_ID) }, ++ { PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) }, ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(mdio, atheros_tbl); diff --git a/target/linux/generic/backport-6.6/713-v6.9-02-net-phy-qcom-create-and-move-functions-to-shared-lib.patch b/target/linux/generic/backport-6.6/713-v6.9-02-net-phy-qcom-create-and-move-functions-to-shared-lib.patch new file mode 100644 index 0000000000..7d0e1f4a28 --- /dev/null +++ b/target/linux/generic/backport-6.6/713-v6.9-02-net-phy-qcom-create-and-move-functions-to-shared-lib.patch @@ -0,0 +1,243 @@ +From 6fb760972c49490b03f3db2ad64cf30bdd28c54a Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 29 Jan 2024 15:15:20 +0100 +Subject: [PATCH 2/5] net: phy: qcom: create and move functions to shared + library + +Create and move functions to shared library in preparation for qca83xx +PHY Family to be detached from at803x driver. + +Only the shared defines are moved to the shared qcom.h header. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20240129141600.2592-3-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/qcom/Kconfig | 4 ++ + drivers/net/phy/qcom/Makefile | 1 + + drivers/net/phy/qcom/at803x.c | 69 +---------------------------- + drivers/net/phy/qcom/qcom-phy-lib.c | 53 ++++++++++++++++++++++ + drivers/net/phy/qcom/qcom.h | 34 ++++++++++++++ + 5 files changed, 94 insertions(+), 67 deletions(-) + create mode 100644 drivers/net/phy/qcom/qcom-phy-lib.c + create mode 100644 drivers/net/phy/qcom/qcom.h + +--- a/drivers/net/phy/qcom/Kconfig ++++ b/drivers/net/phy/qcom/Kconfig +@@ -1,6 +1,10 @@ + # SPDX-License-Identifier: GPL-2.0-only ++config QCOM_NET_PHYLIB ++ tristate ++ + config AT803X_PHY + tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs" ++ select QCOM_NET_PHYLIB + depends on REGULATOR + help + Currently supports the AR8030, AR8031, AR8033, AR8035 and internal +--- a/drivers/net/phy/qcom/Makefile ++++ b/drivers/net/phy/qcom/Makefile +@@ -1,2 +1,3 @@ + # SPDX-License-Identifier: GPL-2.0 ++obj-$(CONFIG_QCOM_NET_PHYLIB) += qcom-phy-lib.o + obj-$(CONFIG_AT803X_PHY) += at803x.o +--- a/drivers/net/phy/qcom/at803x.c ++++ b/drivers/net/phy/qcom/at803x.c +@@ -22,6 +22,8 @@ + #include + #include + ++#include "qcom.h" ++ + #define AT803X_SPECIFIC_FUNCTION_CONTROL 0x10 + #define AT803X_SFC_ASSERT_CRS BIT(11) + #define AT803X_SFC_FORCE_LINK BIT(10) +@@ -84,9 +86,6 @@ + #define AT803X_REG_CHIP_CONFIG 0x1f + #define AT803X_BT_BX_REG_SEL 0x8000 + +-#define AT803X_DEBUG_ADDR 0x1D +-#define AT803X_DEBUG_DATA 0x1E +- + #define AT803X_MODE_CFG_MASK 0x0F + #define AT803X_MODE_CFG_BASET_RGMII 0x00 + #define AT803X_MODE_CFG_BASET_SGMII 0x01 +@@ -103,19 +102,6 @@ + #define AT803X_PSSR 0x11 /*PHY-Specific Status Register*/ + #define AT803X_PSSR_MR_AN_COMPLETE 0x0200 + +-#define AT803X_DEBUG_ANALOG_TEST_CTRL 0x00 +-#define QCA8327_DEBUG_MANU_CTRL_EN BIT(2) +-#define QCA8337_DEBUG_MANU_CTRL_EN GENMASK(3, 2) +-#define AT803X_DEBUG_RX_CLK_DLY_EN BIT(15) +- +-#define AT803X_DEBUG_SYSTEM_CTRL_MODE 0x05 +-#define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8) +- +-#define AT803X_DEBUG_REG_HIB_CTRL 0x0b +-#define AT803X_DEBUG_HIB_CTRL_SEL_RST_80U BIT(10) +-#define AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE BIT(13) +-#define AT803X_DEBUG_HIB_CTRL_PS_HIB_EN BIT(15) +- + #define AT803X_DEBUG_REG_3C 0x3C + + #define AT803X_DEBUG_REG_GREEN 0x3D +@@ -393,18 +379,6 @@ MODULE_DESCRIPTION("Qualcomm Atheros AR8 + MODULE_AUTHOR("Matus Ujhelyi"); + MODULE_LICENSE("GPL"); + +-enum stat_access_type { +- PHY, +- MMD +-}; +- +-struct at803x_hw_stat { +- const char *string; +- u8 reg; +- u32 mask; +- enum stat_access_type access_type; +-}; +- + static struct at803x_hw_stat qca83xx_hw_stats[] = { + { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY}, + { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY}, +@@ -439,45 +413,6 @@ struct at803x_context { + u16 led_control; + }; + +-static int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data) +-{ +- int ret; +- +- ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); +- if (ret < 0) +- return ret; +- +- return phy_write(phydev, AT803X_DEBUG_DATA, data); +-} +- +-static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg) +-{ +- int ret; +- +- ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); +- if (ret < 0) +- return ret; +- +- return phy_read(phydev, AT803X_DEBUG_DATA); +-} +- +-static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, +- u16 clear, u16 set) +-{ +- u16 val; +- int ret; +- +- ret = at803x_debug_reg_read(phydev, reg); +- if (ret < 0) +- return ret; +- +- val = ret & 0xffff; +- val &= ~clear; +- val |= set; +- +- return phy_write(phydev, AT803X_DEBUG_DATA, val); +-} +- + static int at803x_write_page(struct phy_device *phydev, int page) + { + int mask; +--- /dev/null ++++ b/drivers/net/phy/qcom/qcom-phy-lib.c +@@ -0,0 +1,53 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++#include ++#include ++ ++#include "qcom.h" ++ ++MODULE_DESCRIPTION("Qualcomm PHY driver Common Functions"); ++MODULE_AUTHOR("Matus Ujhelyi"); ++MODULE_AUTHOR("Christian Marangi "); ++MODULE_LICENSE("GPL"); ++ ++int at803x_debug_reg_read(struct phy_device *phydev, u16 reg) ++{ ++ int ret; ++ ++ ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); ++ if (ret < 0) ++ return ret; ++ ++ return phy_read(phydev, AT803X_DEBUG_DATA); ++} ++EXPORT_SYMBOL_GPL(at803x_debug_reg_read); ++ ++int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, ++ u16 clear, u16 set) ++{ ++ u16 val; ++ int ret; ++ ++ ret = at803x_debug_reg_read(phydev, reg); ++ if (ret < 0) ++ return ret; ++ ++ val = ret & 0xffff; ++ val &= ~clear; ++ val |= set; ++ ++ return phy_write(phydev, AT803X_DEBUG_DATA, val); ++} ++EXPORT_SYMBOL_GPL(at803x_debug_reg_mask); ++ ++int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data) ++{ ++ int ret; ++ ++ ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); ++ if (ret < 0) ++ return ret; ++ ++ return phy_write(phydev, AT803X_DEBUG_DATA, data); ++} ++EXPORT_SYMBOL_GPL(at803x_debug_reg_write); +--- /dev/null ++++ b/drivers/net/phy/qcom/qcom.h +@@ -0,0 +1,34 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++ ++#define AT803X_DEBUG_ADDR 0x1D ++#define AT803X_DEBUG_DATA 0x1E ++ ++#define AT803X_DEBUG_ANALOG_TEST_CTRL 0x00 ++#define QCA8327_DEBUG_MANU_CTRL_EN BIT(2) ++#define QCA8337_DEBUG_MANU_CTRL_EN GENMASK(3, 2) ++#define AT803X_DEBUG_RX_CLK_DLY_EN BIT(15) ++ ++#define AT803X_DEBUG_SYSTEM_CTRL_MODE 0x05 ++#define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8) ++ ++#define AT803X_DEBUG_REG_HIB_CTRL 0x0b ++#define AT803X_DEBUG_HIB_CTRL_SEL_RST_80U BIT(10) ++#define AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE BIT(13) ++#define AT803X_DEBUG_HIB_CTRL_PS_HIB_EN BIT(15) ++ ++enum stat_access_type { ++ PHY, ++ MMD ++}; ++ ++struct at803x_hw_stat { ++ const char *string; ++ u8 reg; ++ u32 mask; ++ enum stat_access_type access_type; ++}; ++ ++int at803x_debug_reg_read(struct phy_device *phydev, u16 reg); ++int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, ++ u16 clear, u16 set); ++int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data); diff --git a/target/linux/generic/backport-6.6/713-v6.9-03-net-phy-qcom-deatch-qca83xx-PHY-driver-from-at803x.patch b/target/linux/generic/backport-6.6/713-v6.9-03-net-phy-qcom-deatch-qca83xx-PHY-driver-from-at803x.patch new file mode 100644 index 0000000000..6ac09dcb9a --- /dev/null +++ b/target/linux/generic/backport-6.6/713-v6.9-03-net-phy-qcom-deatch-qca83xx-PHY-driver-from-at803x.patch @@ -0,0 +1,638 @@ +From 2e45d404d99d43bb7127b74b5dea8818df64996c Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 29 Jan 2024 15:15:21 +0100 +Subject: [PATCH 3/5] net: phy: qcom: deatch qca83xx PHY driver from at803x + +Deatch qca83xx PHY driver from at803x. + +The QCA83xx PHYs implement specific function and doesn't use generic +at803x so it can be detached from the driver and moved to a dedicated +one. + +Probe function and priv struct is reimplemented to allocate and use +only the qca83xx specific data. Unused data from at803x PHY driver +are dropped from at803x priv struct. + +This is to make slimmer PHY drivers instead of including lots of bloat +that would never be used in specific SoC. + +A new Kconfig flag QCA83XX_PHY is introduced to compile the new +introduced PHY driver. + +As the Kconfig name starts with Qualcomm the same order is kept. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20240129141600.2592-4-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/qcom/Kconfig | 11 +- + drivers/net/phy/qcom/Makefile | 1 + + drivers/net/phy/qcom/at803x.c | 235 ---------------------------- + drivers/net/phy/qcom/qca83xx.c | 275 +++++++++++++++++++++++++++++++++ + 4 files changed, 284 insertions(+), 238 deletions(-) + create mode 100644 drivers/net/phy/qcom/qca83xx.c + +--- a/drivers/net/phy/qcom/Kconfig ++++ b/drivers/net/phy/qcom/Kconfig +@@ -3,9 +3,14 @@ config QCOM_NET_PHYLIB + tristate + + config AT803X_PHY +- tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs" ++ tristate "Qualcomm Atheros AR803X PHYs" + select QCOM_NET_PHYLIB + depends on REGULATOR + help +- Currently supports the AR8030, AR8031, AR8033, AR8035 and internal +- QCA8337(Internal qca8k PHY) model ++ Currently supports the AR8030, AR8031, AR8033, AR8035 model ++ ++config QCA83XX_PHY ++ tristate "Qualcomm Atheros QCA833x PHYs" ++ select QCOM_NET_PHYLIB ++ help ++ Currently supports the internal QCA8337(Internal qca8k PHY) model +--- a/drivers/net/phy/qcom/Makefile ++++ b/drivers/net/phy/qcom/Makefile +@@ -1,3 +1,4 @@ + # SPDX-License-Identifier: GPL-2.0 + obj-$(CONFIG_QCOM_NET_PHYLIB) += qcom-phy-lib.o + obj-$(CONFIG_AT803X_PHY) += at803x.o ++obj-$(CONFIG_QCA83XX_PHY) += qca83xx.o +--- a/drivers/net/phy/qcom/at803x.c ++++ b/drivers/net/phy/qcom/at803x.c +@@ -102,17 +102,10 @@ + #define AT803X_PSSR 0x11 /*PHY-Specific Status Register*/ + #define AT803X_PSSR_MR_AN_COMPLETE 0x0200 + +-#define AT803X_DEBUG_REG_3C 0x3C +- +-#define AT803X_DEBUG_REG_GREEN 0x3D +-#define AT803X_DEBUG_GATE_CLK_IN1000 BIT(6) +- + #define AT803X_DEBUG_REG_1F 0x1F + #define AT803X_DEBUG_PLL_ON BIT(2) + #define AT803X_DEBUG_RGMII_1V8 BIT(3) + +-#define MDIO_AZ_DEBUG 0x800D +- + /* AT803x supports either the XTAL input pad, an internal PLL or the + * DSP as clock reference for the clock output pad. The XTAL reference + * is only used for 25 MHz output, all other frequencies need the PLL. +@@ -163,13 +156,7 @@ + + #define QCA8081_PHY_ID 0x004dd101 + +-#define QCA8327_A_PHY_ID 0x004dd033 +-#define QCA8327_B_PHY_ID 0x004dd034 +-#define QCA8337_PHY_ID 0x004dd036 + #define QCA9561_PHY_ID 0x004dd042 +-#define QCA8K_PHY_ID_MASK 0xffffffff +- +-#define QCA8K_DEVFLAGS_REVISION_MASK GENMASK(2, 0) + + #define AT803X_PAGE_FIBER 0 + #define AT803X_PAGE_COPPER 1 +@@ -379,12 +366,6 @@ MODULE_DESCRIPTION("Qualcomm Atheros AR8 + MODULE_AUTHOR("Matus Ujhelyi"); + MODULE_LICENSE("GPL"); + +-static struct at803x_hw_stat qca83xx_hw_stats[] = { +- { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY}, +- { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY}, +- { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD}, +-}; +- + struct at803x_ss_mask { + u16 speed_mask; + u8 speed_shift; +@@ -400,7 +381,6 @@ struct at803x_priv { + bool is_1000basex; + struct regulator_dev *vddio_rdev; + struct regulator_dev *vddh_rdev; +- u64 stats[ARRAY_SIZE(qca83xx_hw_stats)]; + int led_polarity_mode; + }; + +@@ -564,53 +544,6 @@ static void at803x_get_wol(struct phy_de + wol->wolopts |= WAKE_MAGIC; + } + +-static int qca83xx_get_sset_count(struct phy_device *phydev) +-{ +- return ARRAY_SIZE(qca83xx_hw_stats); +-} +- +-static void qca83xx_get_strings(struct phy_device *phydev, u8 *data) +-{ +- int i; +- +- for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) { +- strscpy(data + i * ETH_GSTRING_LEN, +- qca83xx_hw_stats[i].string, ETH_GSTRING_LEN); +- } +-} +- +-static u64 qca83xx_get_stat(struct phy_device *phydev, int i) +-{ +- struct at803x_hw_stat stat = qca83xx_hw_stats[i]; +- struct at803x_priv *priv = phydev->priv; +- int val; +- u64 ret; +- +- if (stat.access_type == MMD) +- val = phy_read_mmd(phydev, MDIO_MMD_PCS, stat.reg); +- else +- val = phy_read(phydev, stat.reg); +- +- if (val < 0) { +- ret = U64_MAX; +- } else { +- val = val & stat.mask; +- priv->stats[i] += val; +- ret = priv->stats[i]; +- } +- +- return ret; +-} +- +-static void qca83xx_get_stats(struct phy_device *phydev, +- struct ethtool_stats *stats, u64 *data) +-{ +- int i; +- +- for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) +- data[i] = qca83xx_get_stat(phydev, i); +-} +- + static int at803x_suspend(struct phy_device *phydev) + { + int value; +@@ -1707,124 +1640,6 @@ static int at8035_probe(struct phy_devic + return at8035_parse_dt(phydev); + } + +-static int qca83xx_config_init(struct phy_device *phydev) +-{ +- u8 switch_revision; +- +- switch_revision = phydev->dev_flags & QCA8K_DEVFLAGS_REVISION_MASK; +- +- switch (switch_revision) { +- case 1: +- /* For 100M waveform */ +- at803x_debug_reg_write(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0x02ea); +- /* Turn on Gigabit clock */ +- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x68a0); +- break; +- +- case 2: +- phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0); +- fallthrough; +- case 4: +- phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_AZ_DEBUG, 0x803f); +- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x6860); +- at803x_debug_reg_write(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0x2c46); +- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3C, 0x6000); +- break; +- } +- +- /* Following original QCA sourcecode set port to prefer master */ +- phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER); +- +- return 0; +-} +- +-static int qca8327_config_init(struct phy_device *phydev) +-{ +- /* QCA8327 require DAC amplitude adjustment for 100m set to +6%. +- * Disable on init and enable only with 100m speed following +- * qca original source code. +- */ +- at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, +- QCA8327_DEBUG_MANU_CTRL_EN, 0); +- +- return qca83xx_config_init(phydev); +-} +- +-static void qca83xx_link_change_notify(struct phy_device *phydev) +-{ +- /* Set DAC Amplitude adjustment to +6% for 100m on link running */ +- if (phydev->state == PHY_RUNNING) { +- if (phydev->speed == SPEED_100) +- at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, +- QCA8327_DEBUG_MANU_CTRL_EN, +- QCA8327_DEBUG_MANU_CTRL_EN); +- } else { +- /* Reset DAC Amplitude adjustment */ +- at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, +- QCA8327_DEBUG_MANU_CTRL_EN, 0); +- } +-} +- +-static int qca83xx_resume(struct phy_device *phydev) +-{ +- int ret, val; +- +- /* Skip reset if not suspended */ +- if (!phydev->suspended) +- return 0; +- +- /* Reinit the port, reset values set by suspend */ +- qca83xx_config_init(phydev); +- +- /* Reset the port on port resume */ +- phy_set_bits(phydev, MII_BMCR, BMCR_RESET | BMCR_ANENABLE); +- +- /* On resume from suspend the switch execute a reset and +- * restart auto-negotiation. Wait for reset to complete. +- */ +- ret = phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET), +- 50000, 600000, true); +- if (ret) +- return ret; +- +- usleep_range(1000, 2000); +- +- return 0; +-} +- +-static int qca83xx_suspend(struct phy_device *phydev) +-{ +- at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_GREEN, +- AT803X_DEBUG_GATE_CLK_IN1000, 0); +- +- at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL, +- AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE | +- AT803X_DEBUG_HIB_CTRL_SEL_RST_80U, 0); +- +- return 0; +-} +- +-static int qca8337_suspend(struct phy_device *phydev) +-{ +- /* Only QCA8337 support actual suspend. */ +- genphy_suspend(phydev); +- +- return qca83xx_suspend(phydev); +-} +- +-static int qca8327_suspend(struct phy_device *phydev) +-{ +- u16 mask = 0; +- +- /* QCA8327 cause port unreliability when phy suspend +- * is set. +- */ +- mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX); +- phy_modify(phydev, MII_BMCR, mask, 0); +- +- return qca83xx_suspend(phydev); +-} +- + static int qca808x_phy_fast_retrain_config(struct phy_device *phydev) + { + int ret; +@@ -2599,53 +2414,6 @@ static struct phy_driver at803x_driver[] + .soft_reset = genphy_soft_reset, + .config_aneg = at803x_config_aneg, + }, { +- /* QCA8337 */ +- .phy_id = QCA8337_PHY_ID, +- .phy_id_mask = QCA8K_PHY_ID_MASK, +- .name = "Qualcomm Atheros 8337 internal PHY", +- /* PHY_GBIT_FEATURES */ +- .probe = at803x_probe, +- .flags = PHY_IS_INTERNAL, +- .config_init = qca83xx_config_init, +- .soft_reset = genphy_soft_reset, +- .get_sset_count = qca83xx_get_sset_count, +- .get_strings = qca83xx_get_strings, +- .get_stats = qca83xx_get_stats, +- .suspend = qca8337_suspend, +- .resume = qca83xx_resume, +-}, { +- /* QCA8327-A from switch QCA8327-AL1A */ +- .phy_id = QCA8327_A_PHY_ID, +- .phy_id_mask = QCA8K_PHY_ID_MASK, +- .name = "Qualcomm Atheros 8327-A internal PHY", +- /* PHY_GBIT_FEATURES */ +- .link_change_notify = qca83xx_link_change_notify, +- .probe = at803x_probe, +- .flags = PHY_IS_INTERNAL, +- .config_init = qca8327_config_init, +- .soft_reset = genphy_soft_reset, +- .get_sset_count = qca83xx_get_sset_count, +- .get_strings = qca83xx_get_strings, +- .get_stats = qca83xx_get_stats, +- .suspend = qca8327_suspend, +- .resume = qca83xx_resume, +-}, { +- /* QCA8327-B from switch QCA8327-BL1A */ +- .phy_id = QCA8327_B_PHY_ID, +- .phy_id_mask = QCA8K_PHY_ID_MASK, +- .name = "Qualcomm Atheros 8327-B internal PHY", +- /* PHY_GBIT_FEATURES */ +- .link_change_notify = qca83xx_link_change_notify, +- .probe = at803x_probe, +- .flags = PHY_IS_INTERNAL, +- .config_init = qca8327_config_init, +- .soft_reset = genphy_soft_reset, +- .get_sset_count = qca83xx_get_sset_count, +- .get_strings = qca83xx_get_strings, +- .get_stats = qca83xx_get_stats, +- .suspend = qca8327_suspend, +- .resume = qca83xx_resume, +-}, { + /* Qualcomm QCA8081 */ + PHY_ID_MATCH_EXACT(QCA8081_PHY_ID), + .name = "Qualcomm QCA8081", +@@ -2683,9 +2451,6 @@ static struct mdio_device_id __maybe_unu + { PHY_ID_MATCH_EXACT(ATH8032_PHY_ID) }, + { PHY_ID_MATCH_EXACT(ATH8035_PHY_ID) }, + { PHY_ID_MATCH_EXACT(ATH9331_PHY_ID) }, +- { PHY_ID_MATCH_EXACT(QCA8337_PHY_ID) }, +- { PHY_ID_MATCH_EXACT(QCA8327_A_PHY_ID) }, +- { PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID) }, + { PHY_ID_MATCH_EXACT(QCA9561_PHY_ID) }, + { PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) }, + { } +--- /dev/null ++++ b/drivers/net/phy/qcom/qca83xx.c +@@ -0,0 +1,275 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++ ++#include ++#include ++ ++#include "qcom.h" ++ ++#define AT803X_DEBUG_REG_3C 0x3C ++ ++#define AT803X_DEBUG_REG_GREEN 0x3D ++#define AT803X_DEBUG_GATE_CLK_IN1000 BIT(6) ++ ++#define MDIO_AZ_DEBUG 0x800D ++ ++#define QCA8327_A_PHY_ID 0x004dd033 ++#define QCA8327_B_PHY_ID 0x004dd034 ++#define QCA8337_PHY_ID 0x004dd036 ++#define QCA8K_PHY_ID_MASK 0xffffffff ++ ++#define QCA8K_DEVFLAGS_REVISION_MASK GENMASK(2, 0) ++ ++static struct at803x_hw_stat qca83xx_hw_stats[] = { ++ { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY}, ++ { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY}, ++ { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD}, ++}; ++ ++struct qca83xx_priv { ++ u64 stats[ARRAY_SIZE(qca83xx_hw_stats)]; ++}; ++ ++MODULE_DESCRIPTION("Qualcomm Atheros QCA83XX PHY driver"); ++MODULE_AUTHOR("Matus Ujhelyi"); ++MODULE_AUTHOR("Christian Marangi "); ++MODULE_LICENSE("GPL"); ++ ++static int qca83xx_get_sset_count(struct phy_device *phydev) ++{ ++ return ARRAY_SIZE(qca83xx_hw_stats); ++} ++ ++static void qca83xx_get_strings(struct phy_device *phydev, u8 *data) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) { ++ strscpy(data + i * ETH_GSTRING_LEN, ++ qca83xx_hw_stats[i].string, ETH_GSTRING_LEN); ++ } ++} ++ ++static u64 qca83xx_get_stat(struct phy_device *phydev, int i) ++{ ++ struct at803x_hw_stat stat = qca83xx_hw_stats[i]; ++ struct qca83xx_priv *priv = phydev->priv; ++ int val; ++ u64 ret; ++ ++ if (stat.access_type == MMD) ++ val = phy_read_mmd(phydev, MDIO_MMD_PCS, stat.reg); ++ else ++ val = phy_read(phydev, stat.reg); ++ ++ if (val < 0) { ++ ret = U64_MAX; ++ } else { ++ val = val & stat.mask; ++ priv->stats[i] += val; ++ ret = priv->stats[i]; ++ } ++ ++ return ret; ++} ++ ++static void qca83xx_get_stats(struct phy_device *phydev, ++ struct ethtool_stats *stats, u64 *data) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) ++ data[i] = qca83xx_get_stat(phydev, i); ++} ++ ++static int qca83xx_probe(struct phy_device *phydev) ++{ ++ struct device *dev = &phydev->mdio.dev; ++ struct qca83xx_priv *priv; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ phydev->priv = priv; ++ ++ return 0; ++} ++ ++static int qca83xx_config_init(struct phy_device *phydev) ++{ ++ u8 switch_revision; ++ ++ switch_revision = phydev->dev_flags & QCA8K_DEVFLAGS_REVISION_MASK; ++ ++ switch (switch_revision) { ++ case 1: ++ /* For 100M waveform */ ++ at803x_debug_reg_write(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0x02ea); ++ /* Turn on Gigabit clock */ ++ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x68a0); ++ break; ++ ++ case 2: ++ phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0); ++ fallthrough; ++ case 4: ++ phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_AZ_DEBUG, 0x803f); ++ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x6860); ++ at803x_debug_reg_write(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0x2c46); ++ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3C, 0x6000); ++ break; ++ } ++ ++ /* Following original QCA sourcecode set port to prefer master */ ++ phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER); ++ ++ return 0; ++} ++ ++static int qca8327_config_init(struct phy_device *phydev) ++{ ++ /* QCA8327 require DAC amplitude adjustment for 100m set to +6%. ++ * Disable on init and enable only with 100m speed following ++ * qca original source code. ++ */ ++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, ++ QCA8327_DEBUG_MANU_CTRL_EN, 0); ++ ++ return qca83xx_config_init(phydev); ++} ++ ++static void qca83xx_link_change_notify(struct phy_device *phydev) ++{ ++ /* Set DAC Amplitude adjustment to +6% for 100m on link running */ ++ if (phydev->state == PHY_RUNNING) { ++ if (phydev->speed == SPEED_100) ++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, ++ QCA8327_DEBUG_MANU_CTRL_EN, ++ QCA8327_DEBUG_MANU_CTRL_EN); ++ } else { ++ /* Reset DAC Amplitude adjustment */ ++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, ++ QCA8327_DEBUG_MANU_CTRL_EN, 0); ++ } ++} ++ ++static int qca83xx_resume(struct phy_device *phydev) ++{ ++ int ret, val; ++ ++ /* Skip reset if not suspended */ ++ if (!phydev->suspended) ++ return 0; ++ ++ /* Reinit the port, reset values set by suspend */ ++ qca83xx_config_init(phydev); ++ ++ /* Reset the port on port resume */ ++ phy_set_bits(phydev, MII_BMCR, BMCR_RESET | BMCR_ANENABLE); ++ ++ /* On resume from suspend the switch execute a reset and ++ * restart auto-negotiation. Wait for reset to complete. ++ */ ++ ret = phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET), ++ 50000, 600000, true); ++ if (ret) ++ return ret; ++ ++ usleep_range(1000, 2000); ++ ++ return 0; ++} ++ ++static int qca83xx_suspend(struct phy_device *phydev) ++{ ++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_GREEN, ++ AT803X_DEBUG_GATE_CLK_IN1000, 0); ++ ++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL, ++ AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE | ++ AT803X_DEBUG_HIB_CTRL_SEL_RST_80U, 0); ++ ++ return 0; ++} ++ ++static int qca8337_suspend(struct phy_device *phydev) ++{ ++ /* Only QCA8337 support actual suspend. */ ++ genphy_suspend(phydev); ++ ++ return qca83xx_suspend(phydev); ++} ++ ++static int qca8327_suspend(struct phy_device *phydev) ++{ ++ u16 mask = 0; ++ ++ /* QCA8327 cause port unreliability when phy suspend ++ * is set. ++ */ ++ mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX); ++ phy_modify(phydev, MII_BMCR, mask, 0); ++ ++ return qca83xx_suspend(phydev); ++} ++ ++static struct phy_driver qca83xx_driver[] = { ++{ ++ /* QCA8337 */ ++ .phy_id = QCA8337_PHY_ID, ++ .phy_id_mask = QCA8K_PHY_ID_MASK, ++ .name = "Qualcomm Atheros 8337 internal PHY", ++ /* PHY_GBIT_FEATURES */ ++ .probe = qca83xx_probe, ++ .flags = PHY_IS_INTERNAL, ++ .config_init = qca83xx_config_init, ++ .soft_reset = genphy_soft_reset, ++ .get_sset_count = qca83xx_get_sset_count, ++ .get_strings = qca83xx_get_strings, ++ .get_stats = qca83xx_get_stats, ++ .suspend = qca8337_suspend, ++ .resume = qca83xx_resume, ++}, { ++ /* QCA8327-A from switch QCA8327-AL1A */ ++ .phy_id = QCA8327_A_PHY_ID, ++ .phy_id_mask = QCA8K_PHY_ID_MASK, ++ .name = "Qualcomm Atheros 8327-A internal PHY", ++ /* PHY_GBIT_FEATURES */ ++ .link_change_notify = qca83xx_link_change_notify, ++ .probe = qca83xx_probe, ++ .flags = PHY_IS_INTERNAL, ++ .config_init = qca8327_config_init, ++ .soft_reset = genphy_soft_reset, ++ .get_sset_count = qca83xx_get_sset_count, ++ .get_strings = qca83xx_get_strings, ++ .get_stats = qca83xx_get_stats, ++ .suspend = qca8327_suspend, ++ .resume = qca83xx_resume, ++}, { ++ /* QCA8327-B from switch QCA8327-BL1A */ ++ .phy_id = QCA8327_B_PHY_ID, ++ .phy_id_mask = QCA8K_PHY_ID_MASK, ++ .name = "Qualcomm Atheros 8327-B internal PHY", ++ /* PHY_GBIT_FEATURES */ ++ .link_change_notify = qca83xx_link_change_notify, ++ .probe = qca83xx_probe, ++ .flags = PHY_IS_INTERNAL, ++ .config_init = qca8327_config_init, ++ .soft_reset = genphy_soft_reset, ++ .get_sset_count = qca83xx_get_sset_count, ++ .get_strings = qca83xx_get_strings, ++ .get_stats = qca83xx_get_stats, ++ .suspend = qca8327_suspend, ++ .resume = qca83xx_resume, ++}, }; ++ ++module_phy_driver(qca83xx_driver); ++ ++static struct mdio_device_id __maybe_unused qca83xx_tbl[] = { ++ { PHY_ID_MATCH_EXACT(QCA8337_PHY_ID) }, ++ { PHY_ID_MATCH_EXACT(QCA8327_A_PHY_ID) }, ++ { PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID) }, ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(mdio, qca83xx_tbl); diff --git a/target/linux/generic/backport-6.6/713-v6.9-04-net-phy-qcom-move-additional-functions-to-shared-lib.patch b/target/linux/generic/backport-6.6/713-v6.9-04-net-phy-qcom-move-additional-functions-to-shared-lib.patch new file mode 100644 index 0000000000..9c43ad13b4 --- /dev/null +++ b/target/linux/generic/backport-6.6/713-v6.9-04-net-phy-qcom-move-additional-functions-to-shared-lib.patch @@ -0,0 +1,1014 @@ +From 249d2b80e4db0e38503ed0ec2af6c7401bc099b9 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 29 Jan 2024 15:15:22 +0100 +Subject: [PATCH 4/5] net: phy: qcom: move additional functions to shared + library + +Move additional functions to shared library in preparation for qca808x +PHY Family to be detached from at803x driver. + +Only the shared defines are moved to the shared qcom.h header. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20240129141600.2592-5-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/qcom/at803x.c | 426 +--------------------------- + drivers/net/phy/qcom/qcom-phy-lib.c | 376 ++++++++++++++++++++++++ + drivers/net/phy/qcom/qcom.h | 86 ++++++ + 3 files changed, 463 insertions(+), 425 deletions(-) + +--- a/drivers/net/phy/qcom/at803x.c ++++ b/drivers/net/phy/qcom/at803x.c +@@ -24,65 +24,11 @@ + + #include "qcom.h" + +-#define AT803X_SPECIFIC_FUNCTION_CONTROL 0x10 +-#define AT803X_SFC_ASSERT_CRS BIT(11) +-#define AT803X_SFC_FORCE_LINK BIT(10) +-#define AT803X_SFC_MDI_CROSSOVER_MODE_M GENMASK(6, 5) +-#define AT803X_SFC_AUTOMATIC_CROSSOVER 0x3 +-#define AT803X_SFC_MANUAL_MDIX 0x1 +-#define AT803X_SFC_MANUAL_MDI 0x0 +-#define AT803X_SFC_SQE_TEST BIT(2) +-#define AT803X_SFC_POLARITY_REVERSAL BIT(1) +-#define AT803X_SFC_DISABLE_JABBER BIT(0) +- +-#define AT803X_SPECIFIC_STATUS 0x11 +-#define AT803X_SS_SPEED_MASK GENMASK(15, 14) +-#define AT803X_SS_SPEED_1000 2 +-#define AT803X_SS_SPEED_100 1 +-#define AT803X_SS_SPEED_10 0 +-#define AT803X_SS_DUPLEX BIT(13) +-#define AT803X_SS_SPEED_DUPLEX_RESOLVED BIT(11) +-#define AT803X_SS_MDIX BIT(6) +- +-#define QCA808X_SS_SPEED_MASK GENMASK(9, 7) +-#define QCA808X_SS_SPEED_2500 4 +- +-#define AT803X_INTR_ENABLE 0x12 +-#define AT803X_INTR_ENABLE_AUTONEG_ERR BIT(15) +-#define AT803X_INTR_ENABLE_SPEED_CHANGED BIT(14) +-#define AT803X_INTR_ENABLE_DUPLEX_CHANGED BIT(13) +-#define AT803X_INTR_ENABLE_PAGE_RECEIVED BIT(12) +-#define AT803X_INTR_ENABLE_LINK_FAIL BIT(11) +-#define AT803X_INTR_ENABLE_LINK_SUCCESS BIT(10) +-#define AT803X_INTR_ENABLE_LINK_FAIL_BX BIT(8) +-#define AT803X_INTR_ENABLE_LINK_SUCCESS_BX BIT(7) +-#define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE BIT(5) +-#define AT803X_INTR_ENABLE_POLARITY_CHANGED BIT(1) +-#define AT803X_INTR_ENABLE_WOL BIT(0) +- +-#define AT803X_INTR_STATUS 0x13 +- +-#define AT803X_SMART_SPEED 0x14 +-#define AT803X_SMART_SPEED_ENABLE BIT(5) +-#define AT803X_SMART_SPEED_RETRY_LIMIT_MASK GENMASK(4, 2) +-#define AT803X_SMART_SPEED_BYPASS_TIMER BIT(1) +-#define AT803X_CDT 0x16 +-#define AT803X_CDT_MDI_PAIR_MASK GENMASK(9, 8) +-#define AT803X_CDT_ENABLE_TEST BIT(0) +-#define AT803X_CDT_STATUS 0x1c +-#define AT803X_CDT_STATUS_STAT_NORMAL 0 +-#define AT803X_CDT_STATUS_STAT_SHORT 1 +-#define AT803X_CDT_STATUS_STAT_OPEN 2 +-#define AT803X_CDT_STATUS_STAT_FAIL 3 +-#define AT803X_CDT_STATUS_STAT_MASK GENMASK(9, 8) +-#define AT803X_CDT_STATUS_DELTA_TIME_MASK GENMASK(7, 0) + #define AT803X_LED_CONTROL 0x18 + + #define AT803X_PHY_MMD3_WOL_CTRL 0x8012 + #define AT803X_WOL_EN BIT(5) +-#define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C +-#define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B +-#define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A ++ + #define AT803X_REG_CHIP_CONFIG 0x1f + #define AT803X_BT_BX_REG_SEL 0x8000 + +@@ -138,10 +84,6 @@ + #define AT803X_CLK_OUT_STRENGTH_HALF 1 + #define AT803X_CLK_OUT_STRENGTH_QUARTER 2 + +-#define AT803X_DEFAULT_DOWNSHIFT 5 +-#define AT803X_MIN_DOWNSHIFT 2 +-#define AT803X_MAX_DOWNSHIFT 9 +- + #define AT803X_MMD3_SMARTEEE_CTL1 0x805b + #define AT803X_MMD3_SMARTEEE_CTL2 0x805c + #define AT803X_MMD3_SMARTEEE_CTL3 0x805d +@@ -366,11 +308,6 @@ MODULE_DESCRIPTION("Qualcomm Atheros AR8 + MODULE_AUTHOR("Matus Ujhelyi"); + MODULE_LICENSE("GPL"); + +-struct at803x_ss_mask { +- u16 speed_mask; +- u8 speed_shift; +-}; +- + struct at803x_priv { + int flags; + u16 clk_25m_reg; +@@ -470,80 +407,6 @@ static void at803x_context_restore(struc + phy_write(phydev, AT803X_LED_CONTROL, context->led_control); + } + +-static int at803x_set_wol(struct phy_device *phydev, +- struct ethtool_wolinfo *wol) +-{ +- int ret, irq_enabled; +- +- if (wol->wolopts & WAKE_MAGIC) { +- struct net_device *ndev = phydev->attached_dev; +- const u8 *mac; +- unsigned int i; +- static const unsigned int offsets[] = { +- AT803X_LOC_MAC_ADDR_32_47_OFFSET, +- AT803X_LOC_MAC_ADDR_16_31_OFFSET, +- AT803X_LOC_MAC_ADDR_0_15_OFFSET, +- }; +- +- if (!ndev) +- return -ENODEV; +- +- mac = (const u8 *)ndev->dev_addr; +- +- if (!is_valid_ether_addr(mac)) +- return -EINVAL; +- +- for (i = 0; i < 3; i++) +- phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i], +- mac[(i * 2) + 1] | (mac[(i * 2)] << 8)); +- +- /* Enable WOL interrupt */ +- ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL); +- if (ret) +- return ret; +- } else { +- /* Disable WOL interrupt */ +- ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0); +- if (ret) +- return ret; +- } +- +- /* Clear WOL status */ +- ret = phy_read(phydev, AT803X_INTR_STATUS); +- if (ret < 0) +- return ret; +- +- /* Check if there are other interrupts except for WOL triggered when PHY is +- * in interrupt mode, only the interrupts enabled by AT803X_INTR_ENABLE can +- * be passed up to the interrupt PIN. +- */ +- irq_enabled = phy_read(phydev, AT803X_INTR_ENABLE); +- if (irq_enabled < 0) +- return irq_enabled; +- +- irq_enabled &= ~AT803X_INTR_ENABLE_WOL; +- if (ret & irq_enabled && !phy_polling_mode(phydev)) +- phy_trigger_machine(phydev); +- +- return 0; +-} +- +-static void at803x_get_wol(struct phy_device *phydev, +- struct ethtool_wolinfo *wol) +-{ +- int value; +- +- wol->supported = WAKE_MAGIC; +- wol->wolopts = 0; +- +- value = phy_read(phydev, AT803X_INTR_ENABLE); +- if (value < 0) +- return; +- +- if (value & AT803X_INTR_ENABLE_WOL) +- wol->wolopts |= WAKE_MAGIC; +-} +- + static int at803x_suspend(struct phy_device *phydev) + { + int value; +@@ -816,73 +679,6 @@ static int at803x_config_init(struct phy + return phy_modify(phydev, MII_ADVERTISE, MDIO_AN_CTRL1_XNP, 0); + } + +-static int at803x_ack_interrupt(struct phy_device *phydev) +-{ +- int err; +- +- err = phy_read(phydev, AT803X_INTR_STATUS); +- +- return (err < 0) ? err : 0; +-} +- +-static int at803x_config_intr(struct phy_device *phydev) +-{ +- int err; +- int value; +- +- value = phy_read(phydev, AT803X_INTR_ENABLE); +- +- if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { +- /* Clear any pending interrupts */ +- err = at803x_ack_interrupt(phydev); +- if (err) +- return err; +- +- value |= AT803X_INTR_ENABLE_AUTONEG_ERR; +- value |= AT803X_INTR_ENABLE_SPEED_CHANGED; +- value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED; +- value |= AT803X_INTR_ENABLE_LINK_FAIL; +- value |= AT803X_INTR_ENABLE_LINK_SUCCESS; +- +- err = phy_write(phydev, AT803X_INTR_ENABLE, value); +- } else { +- err = phy_write(phydev, AT803X_INTR_ENABLE, 0); +- if (err) +- return err; +- +- /* Clear any pending interrupts */ +- err = at803x_ack_interrupt(phydev); +- } +- +- return err; +-} +- +-static irqreturn_t at803x_handle_interrupt(struct phy_device *phydev) +-{ +- int irq_status, int_enabled; +- +- irq_status = phy_read(phydev, AT803X_INTR_STATUS); +- if (irq_status < 0) { +- phy_error(phydev); +- return IRQ_NONE; +- } +- +- /* Read the current enabled interrupts */ +- int_enabled = phy_read(phydev, AT803X_INTR_ENABLE); +- if (int_enabled < 0) { +- phy_error(phydev); +- return IRQ_NONE; +- } +- +- /* See if this was one of our enabled interrupts */ +- if (!(irq_status & int_enabled)) +- return IRQ_NONE; +- +- phy_trigger_machine(phydev); +- +- return IRQ_HANDLED; +-} +- + static void at803x_link_change_notify(struct phy_device *phydev) + { + /* +@@ -908,69 +704,6 @@ static void at803x_link_change_notify(st + } + } + +-static int at803x_read_specific_status(struct phy_device *phydev, +- struct at803x_ss_mask ss_mask) +-{ +- int ss; +- +- /* Read the AT8035 PHY-Specific Status register, which indicates the +- * speed and duplex that the PHY is actually using, irrespective of +- * whether we are in autoneg mode or not. +- */ +- ss = phy_read(phydev, AT803X_SPECIFIC_STATUS); +- if (ss < 0) +- return ss; +- +- if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) { +- int sfc, speed; +- +- sfc = phy_read(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL); +- if (sfc < 0) +- return sfc; +- +- speed = ss & ss_mask.speed_mask; +- speed >>= ss_mask.speed_shift; +- +- switch (speed) { +- case AT803X_SS_SPEED_10: +- phydev->speed = SPEED_10; +- break; +- case AT803X_SS_SPEED_100: +- phydev->speed = SPEED_100; +- break; +- case AT803X_SS_SPEED_1000: +- phydev->speed = SPEED_1000; +- break; +- case QCA808X_SS_SPEED_2500: +- phydev->speed = SPEED_2500; +- break; +- } +- if (ss & AT803X_SS_DUPLEX) +- phydev->duplex = DUPLEX_FULL; +- else +- phydev->duplex = DUPLEX_HALF; +- +- if (ss & AT803X_SS_MDIX) +- phydev->mdix = ETH_TP_MDI_X; +- else +- phydev->mdix = ETH_TP_MDI; +- +- switch (FIELD_GET(AT803X_SFC_MDI_CROSSOVER_MODE_M, sfc)) { +- case AT803X_SFC_MANUAL_MDI: +- phydev->mdix_ctrl = ETH_TP_MDI; +- break; +- case AT803X_SFC_MANUAL_MDIX: +- phydev->mdix_ctrl = ETH_TP_MDI_X; +- break; +- case AT803X_SFC_AUTOMATIC_CROSSOVER: +- phydev->mdix_ctrl = ETH_TP_MDI_AUTO; +- break; +- } +- } +- +- return 0; +-} +- + static int at803x_read_status(struct phy_device *phydev) + { + struct at803x_ss_mask ss_mask = { 0 }; +@@ -1006,50 +739,6 @@ static int at803x_read_status(struct phy + return 0; + } + +-static int at803x_config_mdix(struct phy_device *phydev, u8 ctrl) +-{ +- u16 val; +- +- switch (ctrl) { +- case ETH_TP_MDI: +- val = AT803X_SFC_MANUAL_MDI; +- break; +- case ETH_TP_MDI_X: +- val = AT803X_SFC_MANUAL_MDIX; +- break; +- case ETH_TP_MDI_AUTO: +- val = AT803X_SFC_AUTOMATIC_CROSSOVER; +- break; +- default: +- return 0; +- } +- +- return phy_modify_changed(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL, +- AT803X_SFC_MDI_CROSSOVER_MODE_M, +- FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val)); +-} +- +-static int at803x_prepare_config_aneg(struct phy_device *phydev) +-{ +- int ret; +- +- ret = at803x_config_mdix(phydev, phydev->mdix_ctrl); +- if (ret < 0) +- return ret; +- +- /* Changes of the midx bits are disruptive to the normal operation; +- * therefore any changes to these registers must be followed by a +- * software reset to take effect. +- */ +- if (ret == 1) { +- ret = genphy_soft_reset(phydev); +- if (ret < 0) +- return ret; +- } +- +- return 0; +-} +- + static int at803x_config_aneg(struct phy_device *phydev) + { + struct at803x_priv *priv = phydev->priv; +@@ -1065,80 +754,6 @@ static int at803x_config_aneg(struct phy + return genphy_config_aneg(phydev); + } + +-static int at803x_get_downshift(struct phy_device *phydev, u8 *d) +-{ +- int val; +- +- val = phy_read(phydev, AT803X_SMART_SPEED); +- if (val < 0) +- return val; +- +- if (val & AT803X_SMART_SPEED_ENABLE) +- *d = FIELD_GET(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, val) + 2; +- else +- *d = DOWNSHIFT_DEV_DISABLE; +- +- return 0; +-} +- +-static int at803x_set_downshift(struct phy_device *phydev, u8 cnt) +-{ +- u16 mask, set; +- int ret; +- +- switch (cnt) { +- case DOWNSHIFT_DEV_DEFAULT_COUNT: +- cnt = AT803X_DEFAULT_DOWNSHIFT; +- fallthrough; +- case AT803X_MIN_DOWNSHIFT ... AT803X_MAX_DOWNSHIFT: +- set = AT803X_SMART_SPEED_ENABLE | +- AT803X_SMART_SPEED_BYPASS_TIMER | +- FIELD_PREP(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, cnt - 2); +- mask = AT803X_SMART_SPEED_RETRY_LIMIT_MASK; +- break; +- case DOWNSHIFT_DEV_DISABLE: +- set = 0; +- mask = AT803X_SMART_SPEED_ENABLE | +- AT803X_SMART_SPEED_BYPASS_TIMER; +- break; +- default: +- return -EINVAL; +- } +- +- ret = phy_modify_changed(phydev, AT803X_SMART_SPEED, mask, set); +- +- /* After changing the smart speed settings, we need to perform a +- * software reset, use phy_init_hw() to make sure we set the +- * reapply any values which might got lost during software reset. +- */ +- if (ret == 1) +- ret = phy_init_hw(phydev); +- +- return ret; +-} +- +-static int at803x_get_tunable(struct phy_device *phydev, +- struct ethtool_tunable *tuna, void *data) +-{ +- switch (tuna->id) { +- case ETHTOOL_PHY_DOWNSHIFT: +- return at803x_get_downshift(phydev, data); +- default: +- return -EOPNOTSUPP; +- } +-} +- +-static int at803x_set_tunable(struct phy_device *phydev, +- struct ethtool_tunable *tuna, const void *data) +-{ +- switch (tuna->id) { +- case ETHTOOL_PHY_DOWNSHIFT: +- return at803x_set_downshift(phydev, *(const u8 *)data); +- default: +- return -EOPNOTSUPP; +- } +-} +- + static int at803x_cable_test_result_trans(u16 status) + { + switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) { +@@ -1170,45 +785,6 @@ static bool at803x_cdt_fault_length_vali + return false; + } + +-static int at803x_cdt_fault_length(int dt) +-{ +- /* According to the datasheet the distance to the fault is +- * DELTA_TIME * 0.824 meters. +- * +- * The author suspect the correct formula is: +- * +- * fault_distance = DELTA_TIME * (c * VF) / 125MHz / 2 +- * +- * where c is the speed of light, VF is the velocity factor of +- * the twisted pair cable, 125MHz the counter frequency and +- * we need to divide by 2 because the hardware will measure the +- * round trip time to the fault and back to the PHY. +- * +- * With a VF of 0.69 we get the factor 0.824 mentioned in the +- * datasheet. +- */ +- return (dt * 824) / 10; +-} +- +-static int at803x_cdt_start(struct phy_device *phydev, +- u32 cdt_start) +-{ +- return phy_write(phydev, AT803X_CDT, cdt_start); +-} +- +-static int at803x_cdt_wait_for_completion(struct phy_device *phydev, +- u32 cdt_en) +-{ +- int val, ret; +- +- /* One test run takes about 25ms */ +- ret = phy_read_poll_timeout(phydev, AT803X_CDT, val, +- !(val & cdt_en), +- 30000, 100000, true); +- +- return ret < 0 ? ret : 0; +-} +- + static int at803x_cable_test_one_pair(struct phy_device *phydev, int pair) + { + static const int ethtool_pair[] = { +--- a/drivers/net/phy/qcom/qcom-phy-lib.c ++++ b/drivers/net/phy/qcom/qcom-phy-lib.c +@@ -3,6 +3,9 @@ + #include + #include + ++#include ++#include ++ + #include "qcom.h" + + MODULE_DESCRIPTION("Qualcomm PHY driver Common Functions"); +@@ -51,3 +54,376 @@ int at803x_debug_reg_write(struct phy_de + return phy_write(phydev, AT803X_DEBUG_DATA, data); + } + EXPORT_SYMBOL_GPL(at803x_debug_reg_write); ++ ++int at803x_set_wol(struct phy_device *phydev, ++ struct ethtool_wolinfo *wol) ++{ ++ int ret, irq_enabled; ++ ++ if (wol->wolopts & WAKE_MAGIC) { ++ struct net_device *ndev = phydev->attached_dev; ++ const u8 *mac; ++ unsigned int i; ++ static const unsigned int offsets[] = { ++ AT803X_LOC_MAC_ADDR_32_47_OFFSET, ++ AT803X_LOC_MAC_ADDR_16_31_OFFSET, ++ AT803X_LOC_MAC_ADDR_0_15_OFFSET, ++ }; ++ ++ if (!ndev) ++ return -ENODEV; ++ ++ mac = (const u8 *)ndev->dev_addr; ++ ++ if (!is_valid_ether_addr(mac)) ++ return -EINVAL; ++ ++ for (i = 0; i < 3; i++) ++ phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i], ++ mac[(i * 2) + 1] | (mac[(i * 2)] << 8)); ++ ++ /* Enable WOL interrupt */ ++ ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL); ++ if (ret) ++ return ret; ++ } else { ++ /* Disable WOL interrupt */ ++ ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0); ++ if (ret) ++ return ret; ++ } ++ ++ /* Clear WOL status */ ++ ret = phy_read(phydev, AT803X_INTR_STATUS); ++ if (ret < 0) ++ return ret; ++ ++ /* Check if there are other interrupts except for WOL triggered when PHY is ++ * in interrupt mode, only the interrupts enabled by AT803X_INTR_ENABLE can ++ * be passed up to the interrupt PIN. ++ */ ++ irq_enabled = phy_read(phydev, AT803X_INTR_ENABLE); ++ if (irq_enabled < 0) ++ return irq_enabled; ++ ++ irq_enabled &= ~AT803X_INTR_ENABLE_WOL; ++ if (ret & irq_enabled && !phy_polling_mode(phydev)) ++ phy_trigger_machine(phydev); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(at803x_set_wol); ++ ++void at803x_get_wol(struct phy_device *phydev, ++ struct ethtool_wolinfo *wol) ++{ ++ int value; ++ ++ wol->supported = WAKE_MAGIC; ++ wol->wolopts = 0; ++ ++ value = phy_read(phydev, AT803X_INTR_ENABLE); ++ if (value < 0) ++ return; ++ ++ if (value & AT803X_INTR_ENABLE_WOL) ++ wol->wolopts |= WAKE_MAGIC; ++} ++EXPORT_SYMBOL_GPL(at803x_get_wol); ++ ++int at803x_ack_interrupt(struct phy_device *phydev) ++{ ++ int err; ++ ++ err = phy_read(phydev, AT803X_INTR_STATUS); ++ ++ return (err < 0) ? err : 0; ++} ++EXPORT_SYMBOL_GPL(at803x_ack_interrupt); ++ ++int at803x_config_intr(struct phy_device *phydev) ++{ ++ int err; ++ int value; ++ ++ value = phy_read(phydev, AT803X_INTR_ENABLE); ++ ++ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { ++ /* Clear any pending interrupts */ ++ err = at803x_ack_interrupt(phydev); ++ if (err) ++ return err; ++ ++ value |= AT803X_INTR_ENABLE_AUTONEG_ERR; ++ value |= AT803X_INTR_ENABLE_SPEED_CHANGED; ++ value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED; ++ value |= AT803X_INTR_ENABLE_LINK_FAIL; ++ value |= AT803X_INTR_ENABLE_LINK_SUCCESS; ++ ++ err = phy_write(phydev, AT803X_INTR_ENABLE, value); ++ } else { ++ err = phy_write(phydev, AT803X_INTR_ENABLE, 0); ++ if (err) ++ return err; ++ ++ /* Clear any pending interrupts */ ++ err = at803x_ack_interrupt(phydev); ++ } ++ ++ return err; ++} ++EXPORT_SYMBOL_GPL(at803x_config_intr); ++ ++irqreturn_t at803x_handle_interrupt(struct phy_device *phydev) ++{ ++ int irq_status, int_enabled; ++ ++ irq_status = phy_read(phydev, AT803X_INTR_STATUS); ++ if (irq_status < 0) { ++ phy_error(phydev); ++ return IRQ_NONE; ++ } ++ ++ /* Read the current enabled interrupts */ ++ int_enabled = phy_read(phydev, AT803X_INTR_ENABLE); ++ if (int_enabled < 0) { ++ phy_error(phydev); ++ return IRQ_NONE; ++ } ++ ++ /* See if this was one of our enabled interrupts */ ++ if (!(irq_status & int_enabled)) ++ return IRQ_NONE; ++ ++ phy_trigger_machine(phydev); ++ ++ return IRQ_HANDLED; ++} ++EXPORT_SYMBOL_GPL(at803x_handle_interrupt); ++ ++int at803x_read_specific_status(struct phy_device *phydev, ++ struct at803x_ss_mask ss_mask) ++{ ++ int ss; ++ ++ /* Read the AT8035 PHY-Specific Status register, which indicates the ++ * speed and duplex that the PHY is actually using, irrespective of ++ * whether we are in autoneg mode or not. ++ */ ++ ss = phy_read(phydev, AT803X_SPECIFIC_STATUS); ++ if (ss < 0) ++ return ss; ++ ++ if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) { ++ int sfc, speed; ++ ++ sfc = phy_read(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL); ++ if (sfc < 0) ++ return sfc; ++ ++ speed = ss & ss_mask.speed_mask; ++ speed >>= ss_mask.speed_shift; ++ ++ switch (speed) { ++ case AT803X_SS_SPEED_10: ++ phydev->speed = SPEED_10; ++ break; ++ case AT803X_SS_SPEED_100: ++ phydev->speed = SPEED_100; ++ break; ++ case AT803X_SS_SPEED_1000: ++ phydev->speed = SPEED_1000; ++ break; ++ case QCA808X_SS_SPEED_2500: ++ phydev->speed = SPEED_2500; ++ break; ++ } ++ if (ss & AT803X_SS_DUPLEX) ++ phydev->duplex = DUPLEX_FULL; ++ else ++ phydev->duplex = DUPLEX_HALF; ++ ++ if (ss & AT803X_SS_MDIX) ++ phydev->mdix = ETH_TP_MDI_X; ++ else ++ phydev->mdix = ETH_TP_MDI; ++ ++ switch (FIELD_GET(AT803X_SFC_MDI_CROSSOVER_MODE_M, sfc)) { ++ case AT803X_SFC_MANUAL_MDI: ++ phydev->mdix_ctrl = ETH_TP_MDI; ++ break; ++ case AT803X_SFC_MANUAL_MDIX: ++ phydev->mdix_ctrl = ETH_TP_MDI_X; ++ break; ++ case AT803X_SFC_AUTOMATIC_CROSSOVER: ++ phydev->mdix_ctrl = ETH_TP_MDI_AUTO; ++ break; ++ } ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(at803x_read_specific_status); ++ ++int at803x_config_mdix(struct phy_device *phydev, u8 ctrl) ++{ ++ u16 val; ++ ++ switch (ctrl) { ++ case ETH_TP_MDI: ++ val = AT803X_SFC_MANUAL_MDI; ++ break; ++ case ETH_TP_MDI_X: ++ val = AT803X_SFC_MANUAL_MDIX; ++ break; ++ case ETH_TP_MDI_AUTO: ++ val = AT803X_SFC_AUTOMATIC_CROSSOVER; ++ break; ++ default: ++ return 0; ++ } ++ ++ return phy_modify_changed(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL, ++ AT803X_SFC_MDI_CROSSOVER_MODE_M, ++ FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val)); ++} ++EXPORT_SYMBOL_GPL(at803x_config_mdix); ++ ++int at803x_prepare_config_aneg(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = at803x_config_mdix(phydev, phydev->mdix_ctrl); ++ if (ret < 0) ++ return ret; ++ ++ /* Changes of the midx bits are disruptive to the normal operation; ++ * therefore any changes to these registers must be followed by a ++ * software reset to take effect. ++ */ ++ if (ret == 1) { ++ ret = genphy_soft_reset(phydev); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(at803x_prepare_config_aneg); ++ ++static int at803x_get_downshift(struct phy_device *phydev, u8 *d) ++{ ++ int val; ++ ++ val = phy_read(phydev, AT803X_SMART_SPEED); ++ if (val < 0) ++ return val; ++ ++ if (val & AT803X_SMART_SPEED_ENABLE) ++ *d = FIELD_GET(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, val) + 2; ++ else ++ *d = DOWNSHIFT_DEV_DISABLE; ++ ++ return 0; ++} ++ ++static int at803x_set_downshift(struct phy_device *phydev, u8 cnt) ++{ ++ u16 mask, set; ++ int ret; ++ ++ switch (cnt) { ++ case DOWNSHIFT_DEV_DEFAULT_COUNT: ++ cnt = AT803X_DEFAULT_DOWNSHIFT; ++ fallthrough; ++ case AT803X_MIN_DOWNSHIFT ... AT803X_MAX_DOWNSHIFT: ++ set = AT803X_SMART_SPEED_ENABLE | ++ AT803X_SMART_SPEED_BYPASS_TIMER | ++ FIELD_PREP(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, cnt - 2); ++ mask = AT803X_SMART_SPEED_RETRY_LIMIT_MASK; ++ break; ++ case DOWNSHIFT_DEV_DISABLE: ++ set = 0; ++ mask = AT803X_SMART_SPEED_ENABLE | ++ AT803X_SMART_SPEED_BYPASS_TIMER; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ ret = phy_modify_changed(phydev, AT803X_SMART_SPEED, mask, set); ++ ++ /* After changing the smart speed settings, we need to perform a ++ * software reset, use phy_init_hw() to make sure we set the ++ * reapply any values which might got lost during software reset. ++ */ ++ if (ret == 1) ++ ret = phy_init_hw(phydev); ++ ++ return ret; ++} ++ ++int at803x_get_tunable(struct phy_device *phydev, ++ struct ethtool_tunable *tuna, void *data) ++{ ++ switch (tuna->id) { ++ case ETHTOOL_PHY_DOWNSHIFT: ++ return at803x_get_downshift(phydev, data); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++EXPORT_SYMBOL_GPL(at803x_get_tunable); ++ ++int at803x_set_tunable(struct phy_device *phydev, ++ struct ethtool_tunable *tuna, const void *data) ++{ ++ switch (tuna->id) { ++ case ETHTOOL_PHY_DOWNSHIFT: ++ return at803x_set_downshift(phydev, *(const u8 *)data); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++EXPORT_SYMBOL_GPL(at803x_set_tunable); ++ ++int at803x_cdt_fault_length(int dt) ++{ ++ /* According to the datasheet the distance to the fault is ++ * DELTA_TIME * 0.824 meters. ++ * ++ * The author suspect the correct formula is: ++ * ++ * fault_distance = DELTA_TIME * (c * VF) / 125MHz / 2 ++ * ++ * where c is the speed of light, VF is the velocity factor of ++ * the twisted pair cable, 125MHz the counter frequency and ++ * we need to divide by 2 because the hardware will measure the ++ * round trip time to the fault and back to the PHY. ++ * ++ * With a VF of 0.69 we get the factor 0.824 mentioned in the ++ * datasheet. ++ */ ++ return (dt * 824) / 10; ++} ++EXPORT_SYMBOL_GPL(at803x_cdt_fault_length); ++ ++int at803x_cdt_start(struct phy_device *phydev, u32 cdt_start) ++{ ++ return phy_write(phydev, AT803X_CDT, cdt_start); ++} ++EXPORT_SYMBOL_GPL(at803x_cdt_start); ++ ++int at803x_cdt_wait_for_completion(struct phy_device *phydev, ++ u32 cdt_en) ++{ ++ int val, ret; ++ ++ /* One test run takes about 25ms */ ++ ret = phy_read_poll_timeout(phydev, AT803X_CDT, val, ++ !(val & cdt_en), ++ 30000, 100000, true); ++ ++ return ret < 0 ? ret : 0; ++} ++EXPORT_SYMBOL_GPL(at803x_cdt_wait_for_completion); +--- a/drivers/net/phy/qcom/qcom.h ++++ b/drivers/net/phy/qcom/qcom.h +@@ -1,5 +1,63 @@ + /* SPDX-License-Identifier: GPL-2.0 */ + ++#define AT803X_SPECIFIC_FUNCTION_CONTROL 0x10 ++#define AT803X_SFC_ASSERT_CRS BIT(11) ++#define AT803X_SFC_FORCE_LINK BIT(10) ++#define AT803X_SFC_MDI_CROSSOVER_MODE_M GENMASK(6, 5) ++#define AT803X_SFC_AUTOMATIC_CROSSOVER 0x3 ++#define AT803X_SFC_MANUAL_MDIX 0x1 ++#define AT803X_SFC_MANUAL_MDI 0x0 ++#define AT803X_SFC_SQE_TEST BIT(2) ++#define AT803X_SFC_POLARITY_REVERSAL BIT(1) ++#define AT803X_SFC_DISABLE_JABBER BIT(0) ++ ++#define AT803X_SPECIFIC_STATUS 0x11 ++#define AT803X_SS_SPEED_MASK GENMASK(15, 14) ++#define AT803X_SS_SPEED_1000 2 ++#define AT803X_SS_SPEED_100 1 ++#define AT803X_SS_SPEED_10 0 ++#define AT803X_SS_DUPLEX BIT(13) ++#define AT803X_SS_SPEED_DUPLEX_RESOLVED BIT(11) ++#define AT803X_SS_MDIX BIT(6) ++ ++#define QCA808X_SS_SPEED_MASK GENMASK(9, 7) ++#define QCA808X_SS_SPEED_2500 4 ++ ++#define AT803X_INTR_ENABLE 0x12 ++#define AT803X_INTR_ENABLE_AUTONEG_ERR BIT(15) ++#define AT803X_INTR_ENABLE_SPEED_CHANGED BIT(14) ++#define AT803X_INTR_ENABLE_DUPLEX_CHANGED BIT(13) ++#define AT803X_INTR_ENABLE_PAGE_RECEIVED BIT(12) ++#define AT803X_INTR_ENABLE_LINK_FAIL BIT(11) ++#define AT803X_INTR_ENABLE_LINK_SUCCESS BIT(10) ++#define AT803X_INTR_ENABLE_LINK_FAIL_BX BIT(8) ++#define AT803X_INTR_ENABLE_LINK_SUCCESS_BX BIT(7) ++#define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE BIT(5) ++#define AT803X_INTR_ENABLE_POLARITY_CHANGED BIT(1) ++#define AT803X_INTR_ENABLE_WOL BIT(0) ++ ++#define AT803X_INTR_STATUS 0x13 ++ ++#define AT803X_SMART_SPEED 0x14 ++#define AT803X_SMART_SPEED_ENABLE BIT(5) ++#define AT803X_SMART_SPEED_RETRY_LIMIT_MASK GENMASK(4, 2) ++#define AT803X_SMART_SPEED_BYPASS_TIMER BIT(1) ++ ++#define AT803X_CDT 0x16 ++#define AT803X_CDT_MDI_PAIR_MASK GENMASK(9, 8) ++#define AT803X_CDT_ENABLE_TEST BIT(0) ++#define AT803X_CDT_STATUS 0x1c ++#define AT803X_CDT_STATUS_STAT_NORMAL 0 ++#define AT803X_CDT_STATUS_STAT_SHORT 1 ++#define AT803X_CDT_STATUS_STAT_OPEN 2 ++#define AT803X_CDT_STATUS_STAT_FAIL 3 ++#define AT803X_CDT_STATUS_STAT_MASK GENMASK(9, 8) ++#define AT803X_CDT_STATUS_DELTA_TIME_MASK GENMASK(7, 0) ++ ++#define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C ++#define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B ++#define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A ++ + #define AT803X_DEBUG_ADDR 0x1D + #define AT803X_DEBUG_DATA 0x1E + +@@ -16,6 +74,10 @@ + #define AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE BIT(13) + #define AT803X_DEBUG_HIB_CTRL_PS_HIB_EN BIT(15) + ++#define AT803X_DEFAULT_DOWNSHIFT 5 ++#define AT803X_MIN_DOWNSHIFT 2 ++#define AT803X_MAX_DOWNSHIFT 9 ++ + enum stat_access_type { + PHY, + MMD +@@ -28,7 +90,31 @@ struct at803x_hw_stat { + enum stat_access_type access_type; + }; + ++struct at803x_ss_mask { ++ u16 speed_mask; ++ u8 speed_shift; ++}; ++ + int at803x_debug_reg_read(struct phy_device *phydev, u16 reg); + int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, + u16 clear, u16 set); + int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data); ++int at803x_set_wol(struct phy_device *phydev, ++ struct ethtool_wolinfo *wol); ++void at803x_get_wol(struct phy_device *phydev, ++ struct ethtool_wolinfo *wol); ++int at803x_ack_interrupt(struct phy_device *phydev); ++int at803x_config_intr(struct phy_device *phydev); ++irqreturn_t at803x_handle_interrupt(struct phy_device *phydev); ++int at803x_read_specific_status(struct phy_device *phydev, ++ struct at803x_ss_mask ss_mask); ++int at803x_config_mdix(struct phy_device *phydev, u8 ctrl); ++int at803x_prepare_config_aneg(struct phy_device *phydev); ++int at803x_get_tunable(struct phy_device *phydev, ++ struct ethtool_tunable *tuna, void *data); ++int at803x_set_tunable(struct phy_device *phydev, ++ struct ethtool_tunable *tuna, const void *data); ++int at803x_cdt_fault_length(int dt); ++int at803x_cdt_start(struct phy_device *phydev, u32 cdt_start); ++int at803x_cdt_wait_for_completion(struct phy_device *phydev, ++ u32 cdt_en); diff --git a/target/linux/generic/backport-6.6/713-v6.9-05-net-phy-qcom-detach-qca808x-PHY-driver-from-at803x.patch b/target/linux/generic/backport-6.6/713-v6.9-05-net-phy-qcom-detach-qca808x-PHY-driver-from-at803x.patch new file mode 100644 index 0000000000..597dcea4c0 --- /dev/null +++ b/target/linux/generic/backport-6.6/713-v6.9-05-net-phy-qcom-detach-qca808x-PHY-driver-from-at803x.patch @@ -0,0 +1,1936 @@ +From c89414adf2ec7cd9e7080c419aa5847f1db1009c Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 29 Jan 2024 15:15:23 +0100 +Subject: [PATCH 5/5] net: phy: qcom: detach qca808x PHY driver from at803x + +Almost all the QCA8081 PHY driver OPs are specific and only some of them +use the generic at803x. + +To make the at803x code slimmer, move all the specific qca808x regs and +functions to a dedicated PHY driver. + +Probe function and priv struct is reworked to allocate and use only the +qca808x specific data. Unused data from at803x PHY driver are dropped +from at803x priv struct. + +Also a new Kconfig is introduced QCA808X_PHY, to compile the newly +introduced PHY driver for QCA8081 PHY. + +As the Kconfig name starts with Qualcomm the same order is kept. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20240129141600.2592-6-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/qcom/Kconfig | 6 + + drivers/net/phy/qcom/Makefile | 1 + + drivers/net/phy/qcom/at803x.c | 897 +------------------------------ + drivers/net/phy/qcom/qca808x.c | 934 +++++++++++++++++++++++++++++++++ + 4 files changed, 942 insertions(+), 896 deletions(-) + create mode 100644 drivers/net/phy/qcom/qca808x.c + +--- a/drivers/net/phy/qcom/Kconfig ++++ b/drivers/net/phy/qcom/Kconfig +@@ -14,3 +14,9 @@ config QCA83XX_PHY + select QCOM_NET_PHYLIB + help + Currently supports the internal QCA8337(Internal qca8k PHY) model ++ ++config QCA808X_PHY ++ tristate "Qualcomm QCA808x PHYs" ++ select QCOM_NET_PHYLIB ++ help ++ Currently supports the QCA8081 model +--- a/drivers/net/phy/qcom/Makefile ++++ b/drivers/net/phy/qcom/Makefile +@@ -2,3 +2,4 @@ + obj-$(CONFIG_QCOM_NET_PHYLIB) += qcom-phy-lib.o + obj-$(CONFIG_AT803X_PHY) += at803x.o + obj-$(CONFIG_QCA83XX_PHY) += qca83xx.o ++obj-$(CONFIG_QCA808X_PHY) += qca808x.o +--- a/drivers/net/phy/qcom/at803x.c ++++ b/drivers/net/phy/qcom/at803x.c +@@ -96,8 +96,6 @@ + #define ATH8035_PHY_ID 0x004dd072 + #define AT8030_PHY_ID_MASK 0xffffffef + +-#define QCA8081_PHY_ID 0x004dd101 +- + #define QCA9561_PHY_ID 0x004dd042 + + #define AT803X_PAGE_FIBER 0 +@@ -110,201 +108,7 @@ + /* disable hibernation mode */ + #define AT803X_DISABLE_HIBERNATION_MODE BIT(2) + +-/* ADC threshold */ +-#define QCA808X_PHY_DEBUG_ADC_THRESHOLD 0x2c80 +-#define QCA808X_ADC_THRESHOLD_MASK GENMASK(7, 0) +-#define QCA808X_ADC_THRESHOLD_80MV 0 +-#define QCA808X_ADC_THRESHOLD_100MV 0xf0 +-#define QCA808X_ADC_THRESHOLD_200MV 0x0f +-#define QCA808X_ADC_THRESHOLD_300MV 0xff +- +-/* CLD control */ +-#define QCA808X_PHY_MMD3_ADDR_CLD_CTRL7 0x8007 +-#define QCA808X_8023AZ_AFE_CTRL_MASK GENMASK(8, 4) +-#define QCA808X_8023AZ_AFE_EN 0x90 +- +-/* AZ control */ +-#define QCA808X_PHY_MMD3_AZ_TRAINING_CTRL 0x8008 +-#define QCA808X_MMD3_AZ_TRAINING_VAL 0x1c32 +- +-#define QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB 0x8014 +-#define QCA808X_MSE_THRESHOLD_20DB_VALUE 0x529 +- +-#define QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB 0x800E +-#define QCA808X_MSE_THRESHOLD_17DB_VALUE 0x341 +- +-#define QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB 0x801E +-#define QCA808X_MSE_THRESHOLD_27DB_VALUE 0x419 +- +-#define QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB 0x8020 +-#define QCA808X_MSE_THRESHOLD_28DB_VALUE 0x341 +- +-#define QCA808X_PHY_MMD7_TOP_OPTION1 0x901c +-#define QCA808X_TOP_OPTION1_DATA 0x0 +- +-#define QCA808X_PHY_MMD3_DEBUG_1 0xa100 +-#define QCA808X_MMD3_DEBUG_1_VALUE 0x9203 +-#define QCA808X_PHY_MMD3_DEBUG_2 0xa101 +-#define QCA808X_MMD3_DEBUG_2_VALUE 0x48ad +-#define QCA808X_PHY_MMD3_DEBUG_3 0xa103 +-#define QCA808X_MMD3_DEBUG_3_VALUE 0x1698 +-#define QCA808X_PHY_MMD3_DEBUG_4 0xa105 +-#define QCA808X_MMD3_DEBUG_4_VALUE 0x8001 +-#define QCA808X_PHY_MMD3_DEBUG_5 0xa106 +-#define QCA808X_MMD3_DEBUG_5_VALUE 0x1111 +-#define QCA808X_PHY_MMD3_DEBUG_6 0xa011 +-#define QCA808X_MMD3_DEBUG_6_VALUE 0x5f85 +- +-/* master/slave seed config */ +-#define QCA808X_PHY_DEBUG_LOCAL_SEED 9 +-#define QCA808X_MASTER_SLAVE_SEED_ENABLE BIT(1) +-#define QCA808X_MASTER_SLAVE_SEED_CFG GENMASK(12, 2) +-#define QCA808X_MASTER_SLAVE_SEED_RANGE 0x32 +- +-/* Hibernation yields lower power consumpiton in contrast with normal operation mode. +- * when the copper cable is unplugged, the PHY enters into hibernation mode in about 10s. +- */ +-#define QCA808X_DBG_AN_TEST 0xb +-#define QCA808X_HIBERNATION_EN BIT(15) +- +-#define QCA808X_CDT_ENABLE_TEST BIT(15) +-#define QCA808X_CDT_INTER_CHECK_DIS BIT(13) +-#define QCA808X_CDT_STATUS BIT(11) +-#define QCA808X_CDT_LENGTH_UNIT BIT(10) +- +-#define QCA808X_MMD3_CDT_STATUS 0x8064 +-#define QCA808X_MMD3_CDT_DIAG_PAIR_A 0x8065 +-#define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066 +-#define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067 +-#define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068 +-#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8) +-#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0) +- +-#define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12) +-#define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8) +-#define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4) +-#define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0) +- +-#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0) +-#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0) +-#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1) +-#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2) +-#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3) +- +-#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2) +-#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1) +-#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2) +-#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3) +- +-/* NORMAL are MDI with type set to 0 */ +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1 +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ +- QCA808X_CDT_STATUS_STAT_MDI1) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ +- QCA808X_CDT_STATUS_STAT_MDI1) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2 +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ +- QCA808X_CDT_STATUS_STAT_MDI2) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ +- QCA808X_CDT_STATUS_STAT_MDI2) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3 +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ +- QCA808X_CDT_STATUS_STAT_MDI3) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ +- QCA808X_CDT_STATUS_STAT_MDI3) +- +-/* Added for reference of existence but should be handled by wait_for_completion already */ +-#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3)) +- +-#define QCA808X_MMD7_LED_GLOBAL 0x8073 +-#define QCA808X_LED_BLINK_1 GENMASK(11, 6) +-#define QCA808X_LED_BLINK_2 GENMASK(5, 0) +-/* Values are the same for both BLINK_1 and BLINK_2 */ +-#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3) +-#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0) +-#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1) +-#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2) +-#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3) +-#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4) +-#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5) +-#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6) +-#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7) +-#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0) +-#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0) +-#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1) +-#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2) +-#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3) +-#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4) +-#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5) +-#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6) +-#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7) +- +-#define QCA808X_MMD7_LED2_CTRL 0x8074 +-#define QCA808X_MMD7_LED2_FORCE_CTRL 0x8075 +-#define QCA808X_MMD7_LED1_CTRL 0x8076 +-#define QCA808X_MMD7_LED1_FORCE_CTRL 0x8077 +-#define QCA808X_MMD7_LED0_CTRL 0x8078 +-#define QCA808X_MMD7_LED_CTRL(x) (0x8078 - ((x) * 2)) +- +-/* LED hw control pattern is the same for every LED */ +-#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0) +-#define QCA808X_LED_SPEED2500_ON BIT(15) +-#define QCA808X_LED_SPEED2500_BLINK BIT(14) +-/* Follow blink trigger even if duplex or speed condition doesn't match */ +-#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13) +-#define QCA808X_LED_FULL_DUPLEX_ON BIT(12) +-#define QCA808X_LED_HALF_DUPLEX_ON BIT(11) +-#define QCA808X_LED_TX_BLINK BIT(10) +-#define QCA808X_LED_RX_BLINK BIT(9) +-#define QCA808X_LED_TX_ON_10MS BIT(8) +-#define QCA808X_LED_RX_ON_10MS BIT(7) +-#define QCA808X_LED_SPEED1000_ON BIT(6) +-#define QCA808X_LED_SPEED100_ON BIT(5) +-#define QCA808X_LED_SPEED10_ON BIT(4) +-#define QCA808X_LED_COLLISION_BLINK BIT(3) +-#define QCA808X_LED_SPEED1000_BLINK BIT(2) +-#define QCA808X_LED_SPEED100_BLINK BIT(1) +-#define QCA808X_LED_SPEED10_BLINK BIT(0) +- +-#define QCA808X_MMD7_LED0_FORCE_CTRL 0x8079 +-#define QCA808X_MMD7_LED_FORCE_CTRL(x) (0x8079 - ((x) * 2)) +- +-/* LED force ctrl is the same for every LED +- * No documentation exist for this, not even internal one +- * with NDA as QCOM gives only info about configuring +- * hw control pattern rules and doesn't indicate any way +- * to force the LED to specific mode. +- * These define comes from reverse and testing and maybe +- * lack of some info or some info are not entirely correct. +- * For the basic LED control and hw control these finding +- * are enough to support LED control in all the required APIs. +- * +- * On doing some comparison with implementation with qca807x, +- * it was found that it's 1:1 equal to it and confirms all the +- * reverse done. It was also found further specification with the +- * force mode and the blink modes. +- */ +-#define QCA808X_LED_FORCE_EN BIT(15) +-#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13) +-#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3) +-#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2) +-#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1) +-#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0) +- +-#define QCA808X_MMD7_LED_POLARITY_CTRL 0x901a +-/* QSDK sets by default 0x46 to this reg that sets BIT 6 for +- * LED to active high. It's not clear what BIT 3 and BIT 4 does. +- */ +-#define QCA808X_LED_ACTIVE_HIGH BIT(6) +- +-/* QCA808X 1G chip type */ +-#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_DESCRIPTION("Qualcomm Atheros AR803x PHY driver"); + MODULE_AUTHOR("Matus Ujhelyi"); + MODULE_LICENSE("GPL"); + +@@ -318,7 +122,6 @@ struct at803x_priv { + bool is_1000basex; + struct regulator_dev *vddio_rdev; + struct regulator_dev *vddh_rdev; +- int led_polarity_mode; + }; + + struct at803x_context { +@@ -519,9 +322,6 @@ static int at803x_probe(struct phy_devic + if (!priv) + return -ENOMEM; + +- /* Init LED polarity mode to -1 */ +- priv->led_polarity_mode = -1; +- + phydev->priv = priv; + + ret = at803x_parse_dt(phydev); +@@ -1216,672 +1016,6 @@ static int at8035_probe(struct phy_devic + return at8035_parse_dt(phydev); + } + +-static int qca808x_phy_fast_retrain_config(struct phy_device *phydev) +-{ +- int ret; +- +- /* Enable fast retrain */ +- ret = genphy_c45_fast_retrain(phydev, true); +- if (ret) +- return ret; +- +- phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1, +- QCA808X_TOP_OPTION1_DATA); +- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB, +- QCA808X_MSE_THRESHOLD_20DB_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB, +- QCA808X_MSE_THRESHOLD_17DB_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB, +- QCA808X_MSE_THRESHOLD_27DB_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB, +- QCA808X_MSE_THRESHOLD_28DB_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1, +- QCA808X_MMD3_DEBUG_1_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4, +- QCA808X_MMD3_DEBUG_4_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5, +- QCA808X_MMD3_DEBUG_5_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3, +- QCA808X_MMD3_DEBUG_3_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6, +- QCA808X_MMD3_DEBUG_6_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2, +- QCA808X_MMD3_DEBUG_2_VALUE); +- +- return 0; +-} +- +-static int qca808x_phy_ms_seed_enable(struct phy_device *phydev, bool enable) +-{ +- u16 seed_value; +- +- 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_CFG | QCA808X_MASTER_SLAVE_SEED_ENABLE, +- FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value) | +- 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 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; +- +- /* Active adc&vga on 802.3az for the link 1000M and 100M */ +- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7, +- QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN); +- if (ret) +- return ret; +- +- /* Adjust the threshold on 802.3az for the link 1000M */ +- ret = phy_write_mmd(phydev, MDIO_MMD_PCS, +- QCA808X_PHY_MMD3_AZ_TRAINING_CTRL, +- QCA808X_MMD3_AZ_TRAINING_VAL); +- if (ret) +- return ret; +- +- 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 */ +- return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD, +- QCA808X_ADC_THRESHOLD_MASK, +- QCA808X_ADC_THRESHOLD_100MV); +-} +- +-static int qca808x_read_status(struct phy_device *phydev) +-{ +- struct at803x_ss_mask ss_mask = { 0 }; +- int ret; +- +- ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT); +- if (ret < 0) +- return ret; +- +- linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising, +- ret & MDIO_AN_10GBT_STAT_LP2_5G); +- +- ret = genphy_read_status(phydev); +- if (ret) +- return ret; +- +- /* qca8081 takes the different bits for speed value from at803x */ +- ss_mask.speed_mask = QCA808X_SS_SPEED_MASK; +- ss_mask.speed_shift = __bf_shf(QCA808X_SS_SPEED_MASK); +- ret = at803x_read_specific_status(phydev, ss_mask); +- if (ret < 0) +- return ret; +- +- if (phydev->link) { +- if (phydev->speed == SPEED_2500) +- phydev->interface = PHY_INTERFACE_MODE_2500BASEX; +- else +- 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 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 (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); +- } +- } +- } +- +- return 0; +-} +- +-static int qca808x_soft_reset(struct phy_device *phydev) +-{ +- int ret; +- +- ret = genphy_soft_reset(phydev); +- if (ret < 0) +- return ret; +- +- 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) +-{ +- switch (cdt_code) { +- case QCA808X_CDT_STATUS_STAT_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: +- return true; +- default: +- return false; +- } +-} +- +-static int qca808x_cable_test_result_trans(int cdt_code) +-{ +- switch (cdt_code) { +- case QCA808X_CDT_STATUS_STAT_NORMAL: +- return ETHTOOL_A_CABLE_RESULT_CODE_OK; +- case QCA808X_CDT_STATUS_STAT_SAME_SHORT: +- return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; +- case QCA808X_CDT_STATUS_STAT_SAME_OPEN: +- return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: +- return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT; +- case QCA808X_CDT_STATUS_STAT_FAIL: +- default: +- return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; +- } +-} +- +-static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair, +- int result) +-{ +- int val; +- u32 cdt_length_reg = 0; +- +- switch (pair) { +- case ETHTOOL_A_CABLE_PAIR_A: +- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A; +- break; +- case ETHTOOL_A_CABLE_PAIR_B: +- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B; +- break; +- case ETHTOOL_A_CABLE_PAIR_C: +- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C; +- break; +- case ETHTOOL_A_CABLE_PAIR_D: +- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D; +- break; +- default: +- return -EINVAL; +- } +- +- val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg); +- if (val < 0) +- return val; +- +- if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT) +- val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val); +- else +- val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val); +- +- return at803x_cdt_fault_length(val); +-} +- +-static int qca808x_cable_test_start(struct phy_device *phydev) +-{ +- int ret; +- +- /* perform CDT with the following configs: +- * 1. disable hibernation. +- * 2. force PHY working in MDI mode. +- * 3. for PHY working in 1000BaseT. +- * 4. configure the threshold. +- */ +- +- ret = at803x_debug_reg_mask(phydev, QCA808X_DBG_AN_TEST, QCA808X_HIBERNATION_EN, 0); +- if (ret < 0) +- return ret; +- +- ret = at803x_config_mdix(phydev, ETH_TP_MDI); +- if (ret < 0) +- return ret; +- +- /* Force 1000base-T needs to configure PMA/PMD and MII_BMCR */ +- phydev->duplex = DUPLEX_FULL; +- phydev->speed = SPEED_1000; +- ret = genphy_c45_pma_setup_forced(phydev); +- if (ret < 0) +- return ret; +- +- ret = genphy_setup_forced(phydev); +- if (ret < 0) +- return ret; +- +- /* configure the thresholds for open, short, pair ok test */ +- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8074, 0xc040); +- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8076, 0xc040); +- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8077, 0xa060); +- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8078, 0xc050); +- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807a, 0xc060); +- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807e, 0xb060); +- +- return 0; +-} +- +-static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair, +- u16 status) +-{ +- int length, result; +- u16 pair_code; +- +- switch (pair) { +- case ETHTOOL_A_CABLE_PAIR_A: +- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status); +- break; +- case ETHTOOL_A_CABLE_PAIR_B: +- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status); +- break; +- case ETHTOOL_A_CABLE_PAIR_C: +- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status); +- break; +- case ETHTOOL_A_CABLE_PAIR_D: +- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status); +- break; +- default: +- return -EINVAL; +- } +- +- result = qca808x_cable_test_result_trans(pair_code); +- ethnl_cable_test_result(phydev, pair, result); +- +- if (qca808x_cdt_fault_length_valid(pair_code)) { +- length = qca808x_cdt_fault_length(phydev, pair, result); +- ethnl_cable_test_fault_length(phydev, pair, length); +- } +- +- return 0; +-} +- +-static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished) +-{ +- int ret, val; +- +- *finished = false; +- +- val = QCA808X_CDT_ENABLE_TEST | +- QCA808X_CDT_LENGTH_UNIT; +- ret = at803x_cdt_start(phydev, val); +- if (ret) +- return ret; +- +- ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST); +- if (ret) +- return ret; +- +- val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS); +- if (val < 0) +- return val; +- +- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val); +- if (ret) +- return ret; +- +- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val); +- if (ret) +- return ret; +- +- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val); +- if (ret) +- return ret; +- +- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val); +- if (ret) +- return ret; +- +- *finished = true; +- +- 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); +- +- /* 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; +-} +- +-static int qca808x_config_aneg(struct phy_device *phydev) +-{ +- int phy_ctrl = 0; +- int ret; +- +- ret = at803x_prepare_config_aneg(phydev); +- if (ret) +- return ret; +- +- /* The reg MII_BMCR also needs to be configured for force mode, the +- * genphy_config_aneg is also needed. +- */ +- if (phydev->autoneg == AUTONEG_DISABLE) +- genphy_c45_pma_setup_forced(phydev); +- +- if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising)) +- phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G; +- +- ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, +- MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl); +- if (ret < 0) +- return ret; +- +- return __genphy_config_aneg(phydev, ret); +-} +- +-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 int qca808x_led_parse_netdev(struct phy_device *phydev, unsigned long rules, +- u16 *offload_trigger) +-{ +- /* Parsing specific to netdev trigger */ +- if (test_bit(TRIGGER_NETDEV_TX, &rules)) +- *offload_trigger |= QCA808X_LED_TX_BLINK; +- if (test_bit(TRIGGER_NETDEV_RX, &rules)) +- *offload_trigger |= QCA808X_LED_RX_BLINK; +- if (test_bit(TRIGGER_NETDEV_LINK_10, &rules)) +- *offload_trigger |= QCA808X_LED_SPEED10_ON; +- if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) +- *offload_trigger |= QCA808X_LED_SPEED100_ON; +- if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) +- *offload_trigger |= QCA808X_LED_SPEED1000_ON; +- if (test_bit(TRIGGER_NETDEV_LINK_2500, &rules)) +- *offload_trigger |= QCA808X_LED_SPEED2500_ON; +- if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules)) +- *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON; +- if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules)) +- *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON; +- +- if (rules && !*offload_trigger) +- return -EOPNOTSUPP; +- +- /* Enable BLINK_CHECK_BYPASS by default to make the LED +- * blink even with duplex or speed mode not enabled. +- */ +- *offload_trigger |= QCA808X_LED_BLINK_CHECK_BYPASS; +- +- return 0; +-} +- +-static int qca808x_led_hw_control_enable(struct phy_device *phydev, u8 index) +-{ +- u16 reg; +- +- if (index > 2) +- return -EINVAL; +- +- reg = QCA808X_MMD7_LED_FORCE_CTRL(index); +- +- return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, +- QCA808X_LED_FORCE_EN); +-} +- +-static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index, +- unsigned long rules) +-{ +- u16 offload_trigger = 0; +- +- if (index > 2) +- return -EINVAL; +- +- return qca808x_led_parse_netdev(phydev, rules, &offload_trigger); +-} +- +-static int qca808x_led_hw_control_set(struct phy_device *phydev, u8 index, +- unsigned long rules) +-{ +- u16 reg, offload_trigger = 0; +- int ret; +- +- if (index > 2) +- return -EINVAL; +- +- reg = QCA808X_MMD7_LED_CTRL(index); +- +- ret = qca808x_led_parse_netdev(phydev, rules, &offload_trigger); +- if (ret) +- return ret; +- +- ret = qca808x_led_hw_control_enable(phydev, index); +- if (ret) +- return ret; +- +- return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, +- QCA808X_LED_PATTERN_MASK, +- offload_trigger); +-} +- +-static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index) +-{ +- u16 reg; +- int val; +- +- if (index > 2) +- return false; +- +- reg = QCA808X_MMD7_LED_FORCE_CTRL(index); +- +- val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); +- +- return !(val & QCA808X_LED_FORCE_EN); +-} +- +-static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index, +- unsigned long *rules) +-{ +- u16 reg; +- int val; +- +- if (index > 2) +- return -EINVAL; +- +- /* Check if we have hw control enabled */ +- if (qca808x_led_hw_control_status(phydev, index)) +- return -EINVAL; +- +- reg = QCA808X_MMD7_LED_CTRL(index); +- +- val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); +- if (val & QCA808X_LED_TX_BLINK) +- set_bit(TRIGGER_NETDEV_TX, rules); +- if (val & QCA808X_LED_RX_BLINK) +- set_bit(TRIGGER_NETDEV_RX, rules); +- if (val & QCA808X_LED_SPEED10_ON) +- set_bit(TRIGGER_NETDEV_LINK_10, rules); +- if (val & QCA808X_LED_SPEED100_ON) +- set_bit(TRIGGER_NETDEV_LINK_100, rules); +- if (val & QCA808X_LED_SPEED1000_ON) +- set_bit(TRIGGER_NETDEV_LINK_1000, rules); +- if (val & QCA808X_LED_SPEED2500_ON) +- set_bit(TRIGGER_NETDEV_LINK_2500, rules); +- if (val & QCA808X_LED_HALF_DUPLEX_ON) +- set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules); +- if (val & QCA808X_LED_FULL_DUPLEX_ON) +- set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules); +- +- return 0; +-} +- +-static int qca808x_led_hw_control_reset(struct phy_device *phydev, u8 index) +-{ +- u16 reg; +- +- if (index > 2) +- return -EINVAL; +- +- reg = QCA808X_MMD7_LED_CTRL(index); +- +- return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, +- QCA808X_LED_PATTERN_MASK); +-} +- +-static int qca808x_led_brightness_set(struct phy_device *phydev, +- u8 index, enum led_brightness value) +-{ +- u16 reg; +- int ret; +- +- if (index > 2) +- return -EINVAL; +- +- if (!value) { +- ret = qca808x_led_hw_control_reset(phydev, index); +- if (ret) +- return ret; +- } +- +- reg = QCA808X_MMD7_LED_FORCE_CTRL(index); +- +- return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, +- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, +- QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON : +- QCA808X_LED_FORCE_OFF); +-} +- +-static int qca808x_led_blink_set(struct phy_device *phydev, u8 index, +- unsigned long *delay_on, +- unsigned long *delay_off) +-{ +- int ret; +- u16 reg; +- +- if (index > 2) +- return -EINVAL; +- +- reg = QCA808X_MMD7_LED_FORCE_CTRL(index); +- +- /* Set blink to 50% off, 50% on at 4Hz by default */ +- ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL, +- QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK, +- QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50); +- if (ret) +- return ret; +- +- /* We use BLINK_1 for normal blinking */ +- ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg, +- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, +- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1); +- if (ret) +- return ret; +- +- /* We set blink to 4Hz, aka 250ms */ +- *delay_on = 250 / 2; +- *delay_off = 250 / 2; +- +- return 0; +-} +- +-static int qca808x_led_polarity_set(struct phy_device *phydev, int index, +- unsigned long modes) +-{ +- struct at803x_priv *priv = phydev->priv; +- bool active_low = false; +- u32 mode; +- +- for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) { +- switch (mode) { +- case PHY_LED_ACTIVE_LOW: +- active_low = true; +- break; +- default: +- return -EINVAL; +- } +- } +- +- /* PHY polarity is global and can't be set per LED. +- * To detect this, check if last requested polarity mode +- * match the new one. +- */ +- if (priv->led_polarity_mode >= 0 && +- priv->led_polarity_mode != active_low) { +- phydev_err(phydev, "PHY polarity is global. Mismatched polarity on different LED\n"); +- return -EINVAL; +- } +- +- /* Save the last PHY polarity mode */ +- priv->led_polarity_mode = active_low; +- +- return phy_modify_mmd(phydev, MDIO_MMD_AN, +- QCA808X_MMD7_LED_POLARITY_CTRL, +- QCA808X_LED_ACTIVE_HIGH, +- active_low ? 0 : QCA808X_LED_ACTIVE_HIGH); +-} +- + static struct phy_driver at803x_driver[] = { + { + /* Qualcomm Atheros AR8035 */ +@@ -1989,34 +1123,6 @@ static struct phy_driver at803x_driver[] + .read_status = at803x_read_status, + .soft_reset = genphy_soft_reset, + .config_aneg = at803x_config_aneg, +-}, { +- /* Qualcomm QCA8081 */ +- PHY_ID_MATCH_EXACT(QCA8081_PHY_ID), +- .name = "Qualcomm QCA8081", +- .flags = PHY_POLL_CABLE_TEST, +- .probe = at803x_probe, +- .config_intr = at803x_config_intr, +- .handle_interrupt = at803x_handle_interrupt, +- .get_tunable = at803x_get_tunable, +- .set_tunable = at803x_set_tunable, +- .set_wol = at803x_set_wol, +- .get_wol = at803x_get_wol, +- .get_features = qca808x_get_features, +- .config_aneg = qca808x_config_aneg, +- .suspend = genphy_suspend, +- .resume = genphy_resume, +- .read_status = qca808x_read_status, +- .config_init = qca808x_config_init, +- .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, +- .led_brightness_set = qca808x_led_brightness_set, +- .led_blink_set = qca808x_led_blink_set, +- .led_hw_is_supported = qca808x_led_hw_is_supported, +- .led_hw_control_set = qca808x_led_hw_control_set, +- .led_hw_control_get = qca808x_led_hw_control_get, +- .led_polarity_set = qca808x_led_polarity_set, + }, }; + + module_phy_driver(at803x_driver); +@@ -2028,7 +1134,6 @@ static struct mdio_device_id __maybe_unu + { PHY_ID_MATCH_EXACT(ATH8035_PHY_ID) }, + { PHY_ID_MATCH_EXACT(ATH9331_PHY_ID) }, + { PHY_ID_MATCH_EXACT(QCA9561_PHY_ID) }, +- { PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) }, + { } + }; + +--- /dev/null ++++ b/drivers/net/phy/qcom/qca808x.c +@@ -0,0 +1,934 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++ ++#include ++#include ++#include ++ ++#include "qcom.h" ++ ++/* ADC threshold */ ++#define QCA808X_PHY_DEBUG_ADC_THRESHOLD 0x2c80 ++#define QCA808X_ADC_THRESHOLD_MASK GENMASK(7, 0) ++#define QCA808X_ADC_THRESHOLD_80MV 0 ++#define QCA808X_ADC_THRESHOLD_100MV 0xf0 ++#define QCA808X_ADC_THRESHOLD_200MV 0x0f ++#define QCA808X_ADC_THRESHOLD_300MV 0xff ++ ++/* CLD control */ ++#define QCA808X_PHY_MMD3_ADDR_CLD_CTRL7 0x8007 ++#define QCA808X_8023AZ_AFE_CTRL_MASK GENMASK(8, 4) ++#define QCA808X_8023AZ_AFE_EN 0x90 ++ ++/* AZ control */ ++#define QCA808X_PHY_MMD3_AZ_TRAINING_CTRL 0x8008 ++#define QCA808X_MMD3_AZ_TRAINING_VAL 0x1c32 ++ ++#define QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB 0x8014 ++#define QCA808X_MSE_THRESHOLD_20DB_VALUE 0x529 ++ ++#define QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB 0x800E ++#define QCA808X_MSE_THRESHOLD_17DB_VALUE 0x341 ++ ++#define QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB 0x801E ++#define QCA808X_MSE_THRESHOLD_27DB_VALUE 0x419 ++ ++#define QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB 0x8020 ++#define QCA808X_MSE_THRESHOLD_28DB_VALUE 0x341 ++ ++#define QCA808X_PHY_MMD7_TOP_OPTION1 0x901c ++#define QCA808X_TOP_OPTION1_DATA 0x0 ++ ++#define QCA808X_PHY_MMD3_DEBUG_1 0xa100 ++#define QCA808X_MMD3_DEBUG_1_VALUE 0x9203 ++#define QCA808X_PHY_MMD3_DEBUG_2 0xa101 ++#define QCA808X_MMD3_DEBUG_2_VALUE 0x48ad ++#define QCA808X_PHY_MMD3_DEBUG_3 0xa103 ++#define QCA808X_MMD3_DEBUG_3_VALUE 0x1698 ++#define QCA808X_PHY_MMD3_DEBUG_4 0xa105 ++#define QCA808X_MMD3_DEBUG_4_VALUE 0x8001 ++#define QCA808X_PHY_MMD3_DEBUG_5 0xa106 ++#define QCA808X_MMD3_DEBUG_5_VALUE 0x1111 ++#define QCA808X_PHY_MMD3_DEBUG_6 0xa011 ++#define QCA808X_MMD3_DEBUG_6_VALUE 0x5f85 ++ ++/* master/slave seed config */ ++#define QCA808X_PHY_DEBUG_LOCAL_SEED 9 ++#define QCA808X_MASTER_SLAVE_SEED_ENABLE BIT(1) ++#define QCA808X_MASTER_SLAVE_SEED_CFG GENMASK(12, 2) ++#define QCA808X_MASTER_SLAVE_SEED_RANGE 0x32 ++ ++/* Hibernation yields lower power consumpiton in contrast with normal operation mode. ++ * when the copper cable is unplugged, the PHY enters into hibernation mode in about 10s. ++ */ ++#define QCA808X_DBG_AN_TEST 0xb ++#define QCA808X_HIBERNATION_EN BIT(15) ++ ++#define QCA808X_CDT_ENABLE_TEST BIT(15) ++#define QCA808X_CDT_INTER_CHECK_DIS BIT(13) ++#define QCA808X_CDT_STATUS BIT(11) ++#define QCA808X_CDT_LENGTH_UNIT BIT(10) ++ ++#define QCA808X_MMD3_CDT_STATUS 0x8064 ++#define QCA808X_MMD3_CDT_DIAG_PAIR_A 0x8065 ++#define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066 ++#define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067 ++#define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068 ++#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8) ++#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0) ++ ++#define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12) ++#define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8) ++#define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4) ++#define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0) ++ ++#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0) ++#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0) ++#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1) ++#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2) ++#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3) ++ ++#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2) ++#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1) ++#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2) ++#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3) ++ ++/* NORMAL are MDI with type set to 0 */ ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1 ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ ++ QCA808X_CDT_STATUS_STAT_MDI1) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ ++ QCA808X_CDT_STATUS_STAT_MDI1) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2 ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ ++ QCA808X_CDT_STATUS_STAT_MDI2) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ ++ QCA808X_CDT_STATUS_STAT_MDI2) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3 ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ ++ QCA808X_CDT_STATUS_STAT_MDI3) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ ++ QCA808X_CDT_STATUS_STAT_MDI3) ++ ++/* Added for reference of existence but should be handled by wait_for_completion already */ ++#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3)) ++ ++#define QCA808X_MMD7_LED_GLOBAL 0x8073 ++#define QCA808X_LED_BLINK_1 GENMASK(11, 6) ++#define QCA808X_LED_BLINK_2 GENMASK(5, 0) ++/* Values are the same for both BLINK_1 and BLINK_2 */ ++#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3) ++#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0) ++#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1) ++#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2) ++#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3) ++#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4) ++#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5) ++#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6) ++#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7) ++#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0) ++#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0) ++#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1) ++#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2) ++#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3) ++#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4) ++#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5) ++#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6) ++#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7) ++ ++#define QCA808X_MMD7_LED2_CTRL 0x8074 ++#define QCA808X_MMD7_LED2_FORCE_CTRL 0x8075 ++#define QCA808X_MMD7_LED1_CTRL 0x8076 ++#define QCA808X_MMD7_LED1_FORCE_CTRL 0x8077 ++#define QCA808X_MMD7_LED0_CTRL 0x8078 ++#define QCA808X_MMD7_LED_CTRL(x) (0x8078 - ((x) * 2)) ++ ++/* LED hw control pattern is the same for every LED */ ++#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0) ++#define QCA808X_LED_SPEED2500_ON BIT(15) ++#define QCA808X_LED_SPEED2500_BLINK BIT(14) ++/* Follow blink trigger even if duplex or speed condition doesn't match */ ++#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13) ++#define QCA808X_LED_FULL_DUPLEX_ON BIT(12) ++#define QCA808X_LED_HALF_DUPLEX_ON BIT(11) ++#define QCA808X_LED_TX_BLINK BIT(10) ++#define QCA808X_LED_RX_BLINK BIT(9) ++#define QCA808X_LED_TX_ON_10MS BIT(8) ++#define QCA808X_LED_RX_ON_10MS BIT(7) ++#define QCA808X_LED_SPEED1000_ON BIT(6) ++#define QCA808X_LED_SPEED100_ON BIT(5) ++#define QCA808X_LED_SPEED10_ON BIT(4) ++#define QCA808X_LED_COLLISION_BLINK BIT(3) ++#define QCA808X_LED_SPEED1000_BLINK BIT(2) ++#define QCA808X_LED_SPEED100_BLINK BIT(1) ++#define QCA808X_LED_SPEED10_BLINK BIT(0) ++ ++#define QCA808X_MMD7_LED0_FORCE_CTRL 0x8079 ++#define QCA808X_MMD7_LED_FORCE_CTRL(x) (0x8079 - ((x) * 2)) ++ ++/* LED force ctrl is the same for every LED ++ * No documentation exist for this, not even internal one ++ * with NDA as QCOM gives only info about configuring ++ * hw control pattern rules and doesn't indicate any way ++ * to force the LED to specific mode. ++ * These define comes from reverse and testing and maybe ++ * lack of some info or some info are not entirely correct. ++ * For the basic LED control and hw control these finding ++ * are enough to support LED control in all the required APIs. ++ * ++ * On doing some comparison with implementation with qca807x, ++ * it was found that it's 1:1 equal to it and confirms all the ++ * reverse done. It was also found further specification with the ++ * force mode and the blink modes. ++ */ ++#define QCA808X_LED_FORCE_EN BIT(15) ++#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13) ++#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3) ++#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2) ++#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1) ++#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0) ++ ++#define QCA808X_MMD7_LED_POLARITY_CTRL 0x901a ++/* QSDK sets by default 0x46 to this reg that sets BIT 6 for ++ * LED to active high. It's not clear what BIT 3 and BIT 4 does. ++ */ ++#define QCA808X_LED_ACTIVE_HIGH BIT(6) ++ ++/* QCA808X 1G chip type */ ++#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) ++ ++#define QCA8081_PHY_ID 0x004dd101 ++ ++MODULE_DESCRIPTION("Qualcomm Atheros QCA808X PHY driver"); ++MODULE_AUTHOR("Matus Ujhelyi"); ++MODULE_LICENSE("GPL"); ++ ++struct qca808x_priv { ++ int led_polarity_mode; ++}; ++ ++static int qca808x_phy_fast_retrain_config(struct phy_device *phydev) ++{ ++ int ret; ++ ++ /* Enable fast retrain */ ++ ret = genphy_c45_fast_retrain(phydev, true); ++ if (ret) ++ return ret; ++ ++ phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1, ++ QCA808X_TOP_OPTION1_DATA); ++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB, ++ QCA808X_MSE_THRESHOLD_20DB_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB, ++ QCA808X_MSE_THRESHOLD_17DB_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB, ++ QCA808X_MSE_THRESHOLD_27DB_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB, ++ QCA808X_MSE_THRESHOLD_28DB_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1, ++ QCA808X_MMD3_DEBUG_1_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4, ++ QCA808X_MMD3_DEBUG_4_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5, ++ QCA808X_MMD3_DEBUG_5_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3, ++ QCA808X_MMD3_DEBUG_3_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6, ++ QCA808X_MMD3_DEBUG_6_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2, ++ QCA808X_MMD3_DEBUG_2_VALUE); ++ ++ return 0; ++} ++ ++static int qca808x_phy_ms_seed_enable(struct phy_device *phydev, bool enable) ++{ ++ u16 seed_value; ++ ++ 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_CFG | QCA808X_MASTER_SLAVE_SEED_ENABLE, ++ FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value) | ++ 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 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_probe(struct phy_device *phydev) ++{ ++ struct device *dev = &phydev->mdio.dev; ++ struct qca808x_priv *priv; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ /* Init LED polarity mode to -1 */ ++ priv->led_polarity_mode = -1; ++ ++ phydev->priv = priv; ++ ++ return 0; ++} ++ ++static int qca808x_config_init(struct phy_device *phydev) ++{ ++ int ret; ++ ++ /* Active adc&vga on 802.3az for the link 1000M and 100M */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7, ++ QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN); ++ if (ret) ++ return ret; ++ ++ /* Adjust the threshold on 802.3az for the link 1000M */ ++ ret = phy_write_mmd(phydev, MDIO_MMD_PCS, ++ QCA808X_PHY_MMD3_AZ_TRAINING_CTRL, ++ QCA808X_MMD3_AZ_TRAINING_VAL); ++ if (ret) ++ return ret; ++ ++ 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 */ ++ return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD, ++ QCA808X_ADC_THRESHOLD_MASK, ++ QCA808X_ADC_THRESHOLD_100MV); ++} ++ ++static int qca808x_read_status(struct phy_device *phydev) ++{ ++ struct at803x_ss_mask ss_mask = { 0 }; ++ int ret; ++ ++ ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT); ++ if (ret < 0) ++ return ret; ++ ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising, ++ ret & MDIO_AN_10GBT_STAT_LP2_5G); ++ ++ ret = genphy_read_status(phydev); ++ if (ret) ++ return ret; ++ ++ /* qca8081 takes the different bits for speed value from at803x */ ++ ss_mask.speed_mask = QCA808X_SS_SPEED_MASK; ++ ss_mask.speed_shift = __bf_shf(QCA808X_SS_SPEED_MASK); ++ ret = at803x_read_specific_status(phydev, ss_mask); ++ if (ret < 0) ++ return ret; ++ ++ if (phydev->link) { ++ if (phydev->speed == SPEED_2500) ++ phydev->interface = PHY_INTERFACE_MODE_2500BASEX; ++ else ++ 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 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 (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); ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++static int qca808x_soft_reset(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = genphy_soft_reset(phydev); ++ if (ret < 0) ++ return ret; ++ ++ 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) ++{ ++ switch (cdt_code) { ++ case QCA808X_CDT_STATUS_STAT_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static int qca808x_cable_test_result_trans(int cdt_code) ++{ ++ switch (cdt_code) { ++ case QCA808X_CDT_STATUS_STAT_NORMAL: ++ return ETHTOOL_A_CABLE_RESULT_CODE_OK; ++ case QCA808X_CDT_STATUS_STAT_SAME_SHORT: ++ return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; ++ case QCA808X_CDT_STATUS_STAT_SAME_OPEN: ++ return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: ++ return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT; ++ case QCA808X_CDT_STATUS_STAT_FAIL: ++ default: ++ return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; ++ } ++} ++ ++static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair, ++ int result) ++{ ++ int val; ++ u32 cdt_length_reg = 0; ++ ++ switch (pair) { ++ case ETHTOOL_A_CABLE_PAIR_A: ++ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A; ++ break; ++ case ETHTOOL_A_CABLE_PAIR_B: ++ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B; ++ break; ++ case ETHTOOL_A_CABLE_PAIR_C: ++ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C; ++ break; ++ case ETHTOOL_A_CABLE_PAIR_D: ++ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg); ++ if (val < 0) ++ return val; ++ ++ if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT) ++ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val); ++ else ++ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val); ++ ++ return at803x_cdt_fault_length(val); ++} ++ ++static int qca808x_cable_test_start(struct phy_device *phydev) ++{ ++ int ret; ++ ++ /* perform CDT with the following configs: ++ * 1. disable hibernation. ++ * 2. force PHY working in MDI mode. ++ * 3. for PHY working in 1000BaseT. ++ * 4. configure the threshold. ++ */ ++ ++ ret = at803x_debug_reg_mask(phydev, QCA808X_DBG_AN_TEST, QCA808X_HIBERNATION_EN, 0); ++ if (ret < 0) ++ return ret; ++ ++ ret = at803x_config_mdix(phydev, ETH_TP_MDI); ++ if (ret < 0) ++ return ret; ++ ++ /* Force 1000base-T needs to configure PMA/PMD and MII_BMCR */ ++ phydev->duplex = DUPLEX_FULL; ++ phydev->speed = SPEED_1000; ++ ret = genphy_c45_pma_setup_forced(phydev); ++ if (ret < 0) ++ return ret; ++ ++ ret = genphy_setup_forced(phydev); ++ if (ret < 0) ++ return ret; ++ ++ /* configure the thresholds for open, short, pair ok test */ ++ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8074, 0xc040); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8076, 0xc040); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8077, 0xa060); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8078, 0xc050); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807a, 0xc060); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807e, 0xb060); ++ ++ return 0; ++} ++ ++static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair, ++ u16 status) ++{ ++ int length, result; ++ u16 pair_code; ++ ++ switch (pair) { ++ case ETHTOOL_A_CABLE_PAIR_A: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status); ++ break; ++ case ETHTOOL_A_CABLE_PAIR_B: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status); ++ break; ++ case ETHTOOL_A_CABLE_PAIR_C: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status); ++ break; ++ case ETHTOOL_A_CABLE_PAIR_D: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ result = qca808x_cable_test_result_trans(pair_code); ++ ethnl_cable_test_result(phydev, pair, result); ++ ++ if (qca808x_cdt_fault_length_valid(pair_code)) { ++ length = qca808x_cdt_fault_length(phydev, pair, result); ++ ethnl_cable_test_fault_length(phydev, pair, length); ++ } ++ ++ return 0; ++} ++ ++static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished) ++{ ++ int ret, val; ++ ++ *finished = false; ++ ++ val = QCA808X_CDT_ENABLE_TEST | ++ QCA808X_CDT_LENGTH_UNIT; ++ ret = at803x_cdt_start(phydev, val); ++ if (ret) ++ return ret; ++ ++ ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST); ++ if (ret) ++ return ret; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS); ++ if (val < 0) ++ return val; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val); ++ if (ret) ++ return ret; ++ ++ *finished = true; ++ ++ 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); ++ ++ /* 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; ++} ++ ++static int qca808x_config_aneg(struct phy_device *phydev) ++{ ++ int phy_ctrl = 0; ++ int ret; ++ ++ ret = at803x_prepare_config_aneg(phydev); ++ if (ret) ++ return ret; ++ ++ /* The reg MII_BMCR also needs to be configured for force mode, the ++ * genphy_config_aneg is also needed. ++ */ ++ if (phydev->autoneg == AUTONEG_DISABLE) ++ genphy_c45_pma_setup_forced(phydev); ++ ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising)) ++ phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G; ++ ++ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, ++ MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl); ++ if (ret < 0) ++ return ret; ++ ++ return __genphy_config_aneg(phydev, ret); ++} ++ ++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 int qca808x_led_parse_netdev(struct phy_device *phydev, unsigned long rules, ++ u16 *offload_trigger) ++{ ++ /* Parsing specific to netdev trigger */ ++ if (test_bit(TRIGGER_NETDEV_TX, &rules)) ++ *offload_trigger |= QCA808X_LED_TX_BLINK; ++ if (test_bit(TRIGGER_NETDEV_RX, &rules)) ++ *offload_trigger |= QCA808X_LED_RX_BLINK; ++ if (test_bit(TRIGGER_NETDEV_LINK_10, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED10_ON; ++ if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED100_ON; ++ if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED1000_ON; ++ if (test_bit(TRIGGER_NETDEV_LINK_2500, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED2500_ON; ++ if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules)) ++ *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON; ++ if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules)) ++ *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON; ++ ++ if (rules && !*offload_trigger) ++ return -EOPNOTSUPP; ++ ++ /* Enable BLINK_CHECK_BYPASS by default to make the LED ++ * blink even with duplex or speed mode not enabled. ++ */ ++ *offload_trigger |= QCA808X_LED_BLINK_CHECK_BYPASS; ++ ++ return 0; ++} ++ ++static int qca808x_led_hw_control_enable(struct phy_device *phydev, u8 index) ++{ ++ u16 reg; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_FORCE_CTRL(index); ++ ++ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_FORCE_EN); ++} ++ ++static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ u16 offload_trigger = 0; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ return qca808x_led_parse_netdev(phydev, rules, &offload_trigger); ++} ++ ++static int qca808x_led_hw_control_set(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ u16 reg, offload_trigger = 0; ++ int ret; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_CTRL(index); ++ ++ ret = qca808x_led_parse_netdev(phydev, rules, &offload_trigger); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_led_hw_control_enable(phydev, index); ++ if (ret) ++ return ret; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_PATTERN_MASK, ++ offload_trigger); ++} ++ ++static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index) ++{ ++ u16 reg; ++ int val; ++ ++ if (index > 2) ++ return false; ++ ++ reg = QCA808X_MMD7_LED_FORCE_CTRL(index); ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); ++ ++ return !(val & QCA808X_LED_FORCE_EN); ++} ++ ++static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index, ++ unsigned long *rules) ++{ ++ u16 reg; ++ int val; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ /* Check if we have hw control enabled */ ++ if (qca808x_led_hw_control_status(phydev, index)) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_CTRL(index); ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); ++ if (val & QCA808X_LED_TX_BLINK) ++ set_bit(TRIGGER_NETDEV_TX, rules); ++ if (val & QCA808X_LED_RX_BLINK) ++ set_bit(TRIGGER_NETDEV_RX, rules); ++ if (val & QCA808X_LED_SPEED10_ON) ++ set_bit(TRIGGER_NETDEV_LINK_10, rules); ++ if (val & QCA808X_LED_SPEED100_ON) ++ set_bit(TRIGGER_NETDEV_LINK_100, rules); ++ if (val & QCA808X_LED_SPEED1000_ON) ++ set_bit(TRIGGER_NETDEV_LINK_1000, rules); ++ if (val & QCA808X_LED_SPEED2500_ON) ++ set_bit(TRIGGER_NETDEV_LINK_2500, rules); ++ if (val & QCA808X_LED_HALF_DUPLEX_ON) ++ set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules); ++ if (val & QCA808X_LED_FULL_DUPLEX_ON) ++ set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules); ++ ++ return 0; ++} ++ ++static int qca808x_led_hw_control_reset(struct phy_device *phydev, u8 index) ++{ ++ u16 reg; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_CTRL(index); ++ ++ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_PATTERN_MASK); ++} ++ ++static int qca808x_led_brightness_set(struct phy_device *phydev, ++ u8 index, enum led_brightness value) ++{ ++ u16 reg; ++ int ret; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ if (!value) { ++ ret = qca808x_led_hw_control_reset(phydev, index); ++ if (ret) ++ return ret; ++ } ++ ++ reg = QCA808X_MMD7_LED_FORCE_CTRL(index); ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, ++ QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON : ++ QCA808X_LED_FORCE_OFF); ++} ++ ++static int qca808x_led_blink_set(struct phy_device *phydev, u8 index, ++ unsigned long *delay_on, ++ unsigned long *delay_off) ++{ ++ int ret; ++ u16 reg; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_FORCE_CTRL(index); ++ ++ /* Set blink to 50% off, 50% on at 4Hz by default */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL, ++ QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK, ++ QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50); ++ if (ret) ++ return ret; ++ ++ /* We use BLINK_1 for normal blinking */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, ++ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1); ++ if (ret) ++ return ret; ++ ++ /* We set blink to 4Hz, aka 250ms */ ++ *delay_on = 250 / 2; ++ *delay_off = 250 / 2; ++ ++ return 0; ++} ++ ++static int qca808x_led_polarity_set(struct phy_device *phydev, int index, ++ unsigned long modes) ++{ ++ struct qca808x_priv *priv = phydev->priv; ++ bool active_low = false; ++ u32 mode; ++ ++ for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) { ++ switch (mode) { ++ case PHY_LED_ACTIVE_LOW: ++ active_low = true; ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ /* PHY polarity is global and can't be set per LED. ++ * To detect this, check if last requested polarity mode ++ * match the new one. ++ */ ++ if (priv->led_polarity_mode >= 0 && ++ priv->led_polarity_mode != active_low) { ++ phydev_err(phydev, "PHY polarity is global. Mismatched polarity on different LED\n"); ++ return -EINVAL; ++ } ++ ++ /* Save the last PHY polarity mode */ ++ priv->led_polarity_mode = active_low; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_AN, ++ QCA808X_MMD7_LED_POLARITY_CTRL, ++ QCA808X_LED_ACTIVE_HIGH, ++ active_low ? 0 : QCA808X_LED_ACTIVE_HIGH); ++} ++ ++static struct phy_driver qca808x_driver[] = { ++{ ++ /* Qualcomm QCA8081 */ ++ PHY_ID_MATCH_EXACT(QCA8081_PHY_ID), ++ .name = "Qualcomm QCA8081", ++ .flags = PHY_POLL_CABLE_TEST, ++ .probe = qca808x_probe, ++ .config_intr = at803x_config_intr, ++ .handle_interrupt = at803x_handle_interrupt, ++ .get_tunable = at803x_get_tunable, ++ .set_tunable = at803x_set_tunable, ++ .set_wol = at803x_set_wol, ++ .get_wol = at803x_get_wol, ++ .get_features = qca808x_get_features, ++ .config_aneg = qca808x_config_aneg, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .read_status = qca808x_read_status, ++ .config_init = qca808x_config_init, ++ .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, ++ .led_brightness_set = qca808x_led_brightness_set, ++ .led_blink_set = qca808x_led_blink_set, ++ .led_hw_is_supported = qca808x_led_hw_is_supported, ++ .led_hw_control_set = qca808x_led_hw_control_set, ++ .led_hw_control_get = qca808x_led_hw_control_get, ++ .led_polarity_set = qca808x_led_polarity_set, ++}, }; ++ ++module_phy_driver(qca808x_driver); ++ ++static struct mdio_device_id __maybe_unused qca808x_tbl[] = { ++ { PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) }, ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(mdio, qca808x_tbl); diff --git a/target/linux/generic/backport-6.6/714-net-pcs-add-driver-for-MediaTek-SGMII-PCS.patch b/target/linux/generic/backport-6.6/714-net-pcs-add-driver-for-MediaTek-SGMII-PCS.patch new file mode 100644 index 0000000000..d56a142451 --- /dev/null +++ b/target/linux/generic/backport-6.6/714-net-pcs-add-driver-for-MediaTek-SGMII-PCS.patch @@ -0,0 +1,394 @@ +From 4765a9722e09765866e131ec31f7b9cf4c1f4854 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Suggested-by: Russell King (Oracle) +Signed-off-by: Daniel Golle +Tested-by: Frank Wunderlich +Reviewed-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + 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 ++M: Daniel Golle ++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 + 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 ++ * Author: Alexander Couzens ++ * Author: Daniel Golle ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++/* 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 ++#include ++ ++#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 diff --git a/target/linux/generic/backport-6.6/714-v6.8-01-net-phy-make-addr-type-u8-in-phy_package_shared-stru.patch b/target/linux/generic/backport-6.6/714-v6.8-01-net-phy-make-addr-type-u8-in-phy_package_shared-stru.patch new file mode 100644 index 0000000000..6decc3430b --- /dev/null +++ b/target/linux/generic/backport-6.6/714-v6.8-01-net-phy-make-addr-type-u8-in-phy_package_shared-stru.patch @@ -0,0 +1,28 @@ +From ebb30ccbbdbd6fae5177b676da4f4ac92bb4f635 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 15 Dec 2023 14:15:31 +0100 +Subject: [PATCH 1/4] net: phy: make addr type u8 in phy_package_shared struct + +Switch addr type in phy_package_shared struct to u8. + +The value is already checked to be non negative and to be less than +PHY_MAX_ADDR, hence u8 is better suited than using int. + +Signed-off-by: Christian Marangi +Reviewed-by: Russell King (Oracle) +Signed-off-by: David S. Miller +--- + include/linux/phy.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -330,7 +330,7 @@ struct mdio_bus_stats { + * phy_package_leave(). + */ + struct phy_package_shared { +- int addr; ++ u8 addr; + refcount_t refcnt; + unsigned long flags; + size_t priv_size; diff --git a/target/linux/generic/backport-6.6/714-v6.8-02-net-phy-extend-PHY-package-API-to-support-multiple-g.patch b/target/linux/generic/backport-6.6/714-v6.8-02-net-phy-extend-PHY-package-API-to-support-multiple-g.patch new file mode 100644 index 0000000000..0cf01df3d3 --- /dev/null +++ b/target/linux/generic/backport-6.6/714-v6.8-02-net-phy-extend-PHY-package-API-to-support-multiple-g.patch @@ -0,0 +1,341 @@ +From 9eea577eb1155fe4a183bc5e7bf269b0b2e7a6ba Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 15 Dec 2023 14:15:32 +0100 +Subject: [PATCH 2/4] net: phy: extend PHY package API to support multiple + global address + +Current API for PHY package are limited to single address to configure +global settings for the PHY package. + +It was found that some PHY package (for example the qca807x, a PHY +package that is shipped with a bundle of 5 PHY) requires multiple PHY +address to configure global settings. An example scenario is a PHY that +have a dedicated PHY for PSGMII/serdes calibrarion and have a specific +PHY in the package where the global PHY mode is set and affects every +other PHY in the package. + +Change the API in the following way: +- Change phy_package_join() to take the base addr of the PHY package + instead of the global PHY addr. +- Make __/phy_package_write/read() require an additional arg that + select what global PHY address to use by passing the offset from the + base addr passed on phy_package_join(). + +Each user of this API is updated to follow this new implementation +following a pattern where an enum is defined to declare the offset of the +addr. + +We also drop the check if shared is defined as any user of the +phy_package_read/write is expected to use phy_package_join first. Misuse +of this will correctly trigger a kernel panic for NULL pointer +exception. + +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/phy/bcm54140.c | 16 ++++++-- + drivers/net/phy/mscc/mscc.h | 5 +++ + drivers/net/phy/mscc/mscc_main.c | 4 +- + drivers/net/phy/phy_device.c | 35 +++++++++-------- + include/linux/phy.h | 64 +++++++++++++++++++++----------- + 5 files changed, 80 insertions(+), 44 deletions(-) + +--- a/drivers/net/phy/bcm54140.c ++++ b/drivers/net/phy/bcm54140.c +@@ -128,6 +128,10 @@ + #define BCM54140_DEFAULT_DOWNSHIFT 5 + #define BCM54140_MAX_DOWNSHIFT 9 + ++enum bcm54140_global_phy { ++ BCM54140_BASE_ADDR = 0, ++}; ++ + struct bcm54140_priv { + int port; + int base_addr; +@@ -429,11 +433,13 @@ static int bcm54140_base_read_rdb(struct + int ret; + + phy_lock_mdio_bus(phydev); +- ret = __phy_package_write(phydev, MII_BCM54XX_RDB_ADDR, rdb); ++ ret = __phy_package_write(phydev, BCM54140_BASE_ADDR, ++ MII_BCM54XX_RDB_ADDR, rdb); + if (ret < 0) + goto out; + +- ret = __phy_package_read(phydev, MII_BCM54XX_RDB_DATA); ++ ret = __phy_package_read(phydev, BCM54140_BASE_ADDR, ++ MII_BCM54XX_RDB_DATA); + + out: + phy_unlock_mdio_bus(phydev); +@@ -446,11 +452,13 @@ static int bcm54140_base_write_rdb(struc + int ret; + + phy_lock_mdio_bus(phydev); +- ret = __phy_package_write(phydev, MII_BCM54XX_RDB_ADDR, rdb); ++ ret = __phy_package_write(phydev, BCM54140_BASE_ADDR, ++ MII_BCM54XX_RDB_ADDR, rdb); + if (ret < 0) + goto out; + +- ret = __phy_package_write(phydev, MII_BCM54XX_RDB_DATA, val); ++ ret = __phy_package_write(phydev, BCM54140_BASE_ADDR, ++ MII_BCM54XX_RDB_DATA, val); + + out: + phy_unlock_mdio_bus(phydev); +--- a/drivers/net/phy/mscc/mscc.h ++++ b/drivers/net/phy/mscc/mscc.h +@@ -414,6 +414,11 @@ struct vsc8531_private { + * gpio_lock: used for PHC operations. Common for all PHYs as the load/save GPIO + * is shared. + */ ++ ++enum vsc85xx_global_phy { ++ VSC88XX_BASE_ADDR = 0, ++}; ++ + struct vsc85xx_shared_private { + struct mutex gpio_lock; + }; +--- a/drivers/net/phy/mscc/mscc_main.c ++++ b/drivers/net/phy/mscc/mscc_main.c +@@ -700,7 +700,7 @@ int phy_base_write(struct phy_device *ph + dump_stack(); + } + +- return __phy_package_write(phydev, regnum, val); ++ return __phy_package_write(phydev, VSC88XX_BASE_ADDR, regnum, val); + } + + /* phydev->bus->mdio_lock should be locked when using this function */ +@@ -711,7 +711,7 @@ int phy_base_read(struct phy_device *phy + dump_stack(); + } + +- return __phy_package_read(phydev, regnum); ++ return __phy_package_read(phydev, VSC88XX_BASE_ADDR, regnum); + } + + u32 vsc85xx_csr_read(struct phy_device *phydev, +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -1602,20 +1602,22 @@ EXPORT_SYMBOL_GPL(phy_driver_is_genphy_1 + /** + * phy_package_join - join a common PHY group + * @phydev: target phy_device struct +- * @addr: cookie and PHY address for global register access ++ * @base_addr: cookie and base PHY address of PHY package for offset ++ * calculation of global register access + * @priv_size: if non-zero allocate this amount of bytes for private data + * + * This joins a PHY group and provides a shared storage for all phydevs in + * this group. This is intended to be used for packages which contain + * more than one PHY, for example a quad PHY transceiver. + * +- * The addr parameter serves as a cookie which has to have the same value +- * for all members of one group and as a PHY address to access generic +- * registers of a PHY package. Usually, one of the PHY addresses of the +- * different PHYs in the package provides access to these global registers. ++ * The base_addr parameter serves as cookie which has to have the same values ++ * for all members of one group and as the base PHY address of the PHY package ++ * for offset calculation to access generic registers of a PHY package. ++ * Usually, one of the PHY addresses of the different PHYs in the package ++ * provides access to these global registers. + * The address which is given here, will be used in the phy_package_read() +- * and phy_package_write() convenience functions. If your PHY doesn't have +- * global registers you can just pick any of the PHY addresses. ++ * and phy_package_write() convenience functions as base and added to the ++ * passed offset in those functions. + * + * This will set the shared pointer of the phydev to the shared storage. + * If this is the first call for a this cookie the shared storage will be +@@ -1625,17 +1627,17 @@ EXPORT_SYMBOL_GPL(phy_driver_is_genphy_1 + * Returns < 1 on error, 0 on success. Esp. calling phy_package_join() + * with the same cookie but a different priv_size is an error. + */ +-int phy_package_join(struct phy_device *phydev, int addr, size_t priv_size) ++int phy_package_join(struct phy_device *phydev, int base_addr, size_t priv_size) + { + struct mii_bus *bus = phydev->mdio.bus; + struct phy_package_shared *shared; + int ret; + +- if (addr < 0 || addr >= PHY_MAX_ADDR) ++ if (base_addr < 0 || base_addr >= PHY_MAX_ADDR) + return -EINVAL; + + mutex_lock(&bus->shared_lock); +- shared = bus->shared[addr]; ++ shared = bus->shared[base_addr]; + if (!shared) { + ret = -ENOMEM; + shared = kzalloc(sizeof(*shared), GFP_KERNEL); +@@ -1647,9 +1649,9 @@ int phy_package_join(struct phy_device * + goto err_free; + shared->priv_size = priv_size; + } +- shared->addr = addr; ++ shared->base_addr = base_addr; + refcount_set(&shared->refcnt, 1); +- bus->shared[addr] = shared; ++ bus->shared[base_addr] = shared; + } else { + ret = -EINVAL; + if (priv_size && priv_size != shared->priv_size) +@@ -1687,7 +1689,7 @@ void phy_package_leave(struct phy_device + return; + + if (refcount_dec_and_mutex_lock(&shared->refcnt, &bus->shared_lock)) { +- bus->shared[shared->addr] = NULL; ++ bus->shared[shared->base_addr] = NULL; + mutex_unlock(&bus->shared_lock); + kfree(shared->priv); + kfree(shared); +@@ -1706,7 +1708,8 @@ static void devm_phy_package_leave(struc + * devm_phy_package_join - resource managed phy_package_join() + * @dev: device that is registering this PHY package + * @phydev: target phy_device struct +- * @addr: cookie and PHY address for global register access ++ * @base_addr: cookie and base PHY address of PHY package for offset ++ * calculation of global register access + * @priv_size: if non-zero allocate this amount of bytes for private data + * + * Managed phy_package_join(). Shared storage fetched by this function, +@@ -1714,7 +1717,7 @@ static void devm_phy_package_leave(struc + * phy_package_join() for more information. + */ + int devm_phy_package_join(struct device *dev, struct phy_device *phydev, +- int addr, size_t priv_size) ++ int base_addr, size_t priv_size) + { + struct phy_device **ptr; + int ret; +@@ -1724,7 +1727,7 @@ int devm_phy_package_join(struct device + if (!ptr) + return -ENOMEM; + +- ret = phy_package_join(phydev, addr, priv_size); ++ ret = phy_package_join(phydev, base_addr, priv_size); + + if (!ret) { + *ptr = phydev; +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -319,7 +319,8 @@ struct mdio_bus_stats { + + /** + * struct phy_package_shared - Shared information in PHY packages +- * @addr: Common PHY address used to combine PHYs in one package ++ * @base_addr: Base PHY address of PHY package used to combine PHYs ++ * in one package and for offset calculation of phy_package_read/write + * @refcnt: Number of PHYs connected to this shared data + * @flags: Initialization of PHY package + * @priv_size: Size of the shared private data @priv +@@ -330,7 +331,7 @@ struct mdio_bus_stats { + * phy_package_leave(). + */ + struct phy_package_shared { +- u8 addr; ++ u8 base_addr; + refcount_t refcnt; + unsigned long flags; + size_t priv_size; +@@ -1763,10 +1764,10 @@ int phy_ethtool_get_link_ksettings(struc + int phy_ethtool_set_link_ksettings(struct net_device *ndev, + const struct ethtool_link_ksettings *cmd); + int phy_ethtool_nway_reset(struct net_device *ndev); +-int phy_package_join(struct phy_device *phydev, int addr, size_t priv_size); ++int phy_package_join(struct phy_device *phydev, int base_addr, size_t priv_size); + void phy_package_leave(struct phy_device *phydev); + int devm_phy_package_join(struct device *dev, struct phy_device *phydev, +- int addr, size_t priv_size); ++ int base_addr, size_t priv_size); + + #if IS_ENABLED(CONFIG_PHYLIB) + int __init mdio_bus_init(void); +@@ -1778,46 +1779,65 @@ int phy_ethtool_get_sset_count(struct ph + int phy_ethtool_get_stats(struct phy_device *phydev, + struct ethtool_stats *stats, u64 *data); + +-static inline int phy_package_read(struct phy_device *phydev, u32 regnum) ++static inline int phy_package_address(struct phy_device *phydev, ++ unsigned int addr_offset) + { + struct phy_package_shared *shared = phydev->shared; ++ u8 base_addr = shared->base_addr; + +- if (!shared) ++ if (addr_offset >= PHY_MAX_ADDR - base_addr) + return -EIO; + +- return mdiobus_read(phydev->mdio.bus, shared->addr, regnum); ++ /* we know that addr will be in the range 0..31 and thus the ++ * implicit cast to a signed int is not a problem. ++ */ ++ return base_addr + addr_offset; + } + +-static inline int __phy_package_read(struct phy_device *phydev, u32 regnum) ++static inline int phy_package_read(struct phy_device *phydev, ++ unsigned int addr_offset, u32 regnum) + { +- struct phy_package_shared *shared = phydev->shared; ++ int addr = phy_package_address(phydev, addr_offset); + +- if (!shared) +- return -EIO; ++ if (addr < 0) ++ return addr; ++ ++ return mdiobus_read(phydev->mdio.bus, addr, regnum); ++} ++ ++static inline int __phy_package_read(struct phy_device *phydev, ++ unsigned int addr_offset, u32 regnum) ++{ ++ int addr = phy_package_address(phydev, addr_offset); ++ ++ if (addr < 0) ++ return addr; + +- return __mdiobus_read(phydev->mdio.bus, shared->addr, regnum); ++ return __mdiobus_read(phydev->mdio.bus, addr, regnum); + } + + static inline int phy_package_write(struct phy_device *phydev, +- u32 regnum, u16 val) ++ unsigned int addr_offset, u32 regnum, ++ u16 val) + { +- struct phy_package_shared *shared = phydev->shared; ++ int addr = phy_package_address(phydev, addr_offset); + +- if (!shared) +- return -EIO; ++ if (addr < 0) ++ return addr; + +- return mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val); ++ return mdiobus_write(phydev->mdio.bus, addr, regnum, val); + } + + static inline int __phy_package_write(struct phy_device *phydev, +- u32 regnum, u16 val) ++ unsigned int addr_offset, u32 regnum, ++ u16 val) + { +- struct phy_package_shared *shared = phydev->shared; ++ int addr = phy_package_address(phydev, addr_offset); + +- if (!shared) +- return -EIO; ++ if (addr < 0) ++ return addr; + +- return __mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val); ++ return __mdiobus_write(phydev->mdio.bus, addr, regnum, val); + } + + static inline bool __phy_package_set_once(struct phy_device *phydev, diff --git a/target/linux/generic/backport-6.6/714-v6.8-03-net-phy-restructure-__phy_write-read_mmd-to-helper-a.patch b/target/linux/generic/backport-6.6/714-v6.8-03-net-phy-restructure-__phy_write-read_mmd-to-helper-a.patch new file mode 100644 index 0000000000..4a17d46453 --- /dev/null +++ b/target/linux/generic/backport-6.6/714-v6.8-03-net-phy-restructure-__phy_write-read_mmd-to-helper-a.patch @@ -0,0 +1,116 @@ +From 028672bd1d73cf65249a420c1de75e8d2acd2f6a Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 15 Dec 2023 14:15:33 +0100 +Subject: [PATCH 3/4] net: phy: restructure __phy_write/read_mmd to helper and + phydev user + +Restructure phy_write_mmd and phy_read_mmd to implement generic helper +for direct mdiobus access for mmd and use these helper for phydev user. + +This is needed in preparation of PHY package API that requires generic +access to the mdiobus and are deatched from phydev struct but instead +access them based on PHY package base_addr and offsets. + +Signed-off-by: Christian Marangi +Reviewed-by: Russell King (Oracle) +Signed-off-by: David S. Miller +--- + drivers/net/phy/phy-core.c | 64 ++++++++++++++++++-------------------- + 1 file changed, 30 insertions(+), 34 deletions(-) + +--- a/drivers/net/phy/phy-core.c ++++ b/drivers/net/phy/phy-core.c +@@ -528,6 +528,28 @@ static void mmd_phy_indirect(struct mii_ + devad | MII_MMD_CTRL_NOINCR); + } + ++static int mmd_phy_read(struct mii_bus *bus, int phy_addr, bool is_c45, ++ int devad, u32 regnum) ++{ ++ if (is_c45) ++ return __mdiobus_c45_read(bus, phy_addr, devad, regnum); ++ ++ mmd_phy_indirect(bus, phy_addr, devad, regnum); ++ /* Read the content of the MMD's selected register */ ++ return __mdiobus_read(bus, phy_addr, MII_MMD_DATA); ++} ++ ++static int mmd_phy_write(struct mii_bus *bus, int phy_addr, bool is_c45, ++ int devad, u32 regnum, u16 val) ++{ ++ if (is_c45) ++ return __mdiobus_c45_write(bus, phy_addr, devad, regnum, val); ++ ++ mmd_phy_indirect(bus, phy_addr, devad, regnum); ++ /* Write the data into MMD's selected register */ ++ return __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val); ++} ++ + /** + * __phy_read_mmd - Convenience function for reading a register + * from an MMD on a given PHY. +@@ -539,26 +561,14 @@ static void mmd_phy_indirect(struct mii_ + */ + int __phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum) + { +- int val; +- + if (regnum > (u16)~0 || devad > 32) + return -EINVAL; + +- if (phydev->drv && phydev->drv->read_mmd) { +- val = phydev->drv->read_mmd(phydev, devad, regnum); +- } else if (phydev->is_c45) { +- val = __mdiobus_c45_read(phydev->mdio.bus, phydev->mdio.addr, +- devad, regnum); +- } else { +- struct mii_bus *bus = phydev->mdio.bus; +- int phy_addr = phydev->mdio.addr; +- +- mmd_phy_indirect(bus, phy_addr, devad, regnum); +- +- /* Read the content of the MMD's selected register */ +- val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA); +- } +- return val; ++ if (phydev->drv && phydev->drv->read_mmd) ++ return phydev->drv->read_mmd(phydev, devad, regnum); ++ ++ return mmd_phy_read(phydev->mdio.bus, phydev->mdio.addr, ++ phydev->is_c45, devad, regnum); + } + EXPORT_SYMBOL(__phy_read_mmd); + +@@ -595,28 +605,14 @@ EXPORT_SYMBOL(phy_read_mmd); + */ + int __phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val) + { +- int ret; +- + if (regnum > (u16)~0 || devad > 32) + return -EINVAL; + +- if (phydev->drv && phydev->drv->write_mmd) { +- ret = phydev->drv->write_mmd(phydev, devad, regnum, val); +- } else if (phydev->is_c45) { +- ret = __mdiobus_c45_write(phydev->mdio.bus, phydev->mdio.addr, +- devad, regnum, val); +- } else { +- struct mii_bus *bus = phydev->mdio.bus; +- int phy_addr = phydev->mdio.addr; ++ if (phydev->drv && phydev->drv->write_mmd) ++ return phydev->drv->write_mmd(phydev, devad, regnum, val); + +- mmd_phy_indirect(bus, phy_addr, devad, regnum); +- +- /* Write the data into MMD's selected register */ +- __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val); +- +- ret = 0; +- } +- return ret; ++ return mmd_phy_write(phydev->mdio.bus, phydev->mdio.addr, ++ phydev->is_c45, devad, regnum, val); + } + EXPORT_SYMBOL(__phy_write_mmd); + diff --git a/target/linux/generic/backport-6.6/714-v6.8-04-net-phy-add-support-for-PHY-package-MMD-read-write.patch b/target/linux/generic/backport-6.6/714-v6.8-04-net-phy-add-support-for-PHY-package-MMD-read-write.patch new file mode 100644 index 0000000000..a628a37929 --- /dev/null +++ b/target/linux/generic/backport-6.6/714-v6.8-04-net-phy-add-support-for-PHY-package-MMD-read-write.patch @@ -0,0 +1,196 @@ +From d63710fc0f1a501fd75a7025e3070a96ffa1645f Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 15 Dec 2023 14:15:34 +0100 +Subject: [PATCH 4/4] net: phy: add support for PHY package MMD read/write + +Some PHY in PHY package may require to read/write MMD regs to correctly +configure the PHY package. + +Add support for these additional required function in both lock and no +lock variant. + +It's assumed that the entire PHY package is either C22 or C45. We use +C22 or C45 way of writing/reading to mmd regs based on the passed phydev +whether it's C22 or C45. + +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/phy/phy-core.c | 140 +++++++++++++++++++++++++++++++++++++ + include/linux/phy.h | 16 +++++ + 2 files changed, 156 insertions(+) + +--- a/drivers/net/phy/phy-core.c ++++ b/drivers/net/phy/phy-core.c +@@ -639,6 +639,146 @@ int phy_write_mmd(struct phy_device *phy + EXPORT_SYMBOL(phy_write_mmd); + + /** ++ * __phy_package_read_mmd - read MMD reg relative to PHY package base addr ++ * @phydev: The phy_device struct ++ * @addr_offset: The offset to be added to PHY package base_addr ++ * @devad: The MMD to read from ++ * @regnum: The register on the MMD to read ++ * ++ * Convenience helper for reading a register of an MMD on a given PHY ++ * using the PHY package base address. The base address is added to ++ * the addr_offset value. ++ * ++ * Same calling rules as for __phy_read(); ++ * ++ * NOTE: It's assumed that the entire PHY package is either C22 or C45. ++ */ ++int __phy_package_read_mmd(struct phy_device *phydev, ++ unsigned int addr_offset, int devad, ++ u32 regnum) ++{ ++ int addr = phy_package_address(phydev, addr_offset); ++ ++ if (addr < 0) ++ return addr; ++ ++ if (regnum > (u16)~0 || devad > 32) ++ return -EINVAL; ++ ++ return mmd_phy_read(phydev->mdio.bus, addr, phydev->is_c45, devad, ++ regnum); ++} ++EXPORT_SYMBOL(__phy_package_read_mmd); ++ ++/** ++ * phy_package_read_mmd - read MMD reg relative to PHY package base addr ++ * @phydev: The phy_device struct ++ * @addr_offset: The offset to be added to PHY package base_addr ++ * @devad: The MMD to read from ++ * @regnum: The register on the MMD to read ++ * ++ * Convenience helper for reading a register of an MMD on a given PHY ++ * using the PHY package base address. The base address is added to ++ * the addr_offset value. ++ * ++ * Same calling rules as for phy_read(); ++ * ++ * NOTE: It's assumed that the entire PHY package is either C22 or C45. ++ */ ++int phy_package_read_mmd(struct phy_device *phydev, ++ unsigned int addr_offset, int devad, ++ u32 regnum) ++{ ++ int addr = phy_package_address(phydev, addr_offset); ++ int val; ++ ++ if (addr < 0) ++ return addr; ++ ++ if (regnum > (u16)~0 || devad > 32) ++ return -EINVAL; ++ ++ phy_lock_mdio_bus(phydev); ++ val = mmd_phy_read(phydev->mdio.bus, addr, phydev->is_c45, devad, ++ regnum); ++ phy_unlock_mdio_bus(phydev); ++ ++ return val; ++} ++EXPORT_SYMBOL(phy_package_read_mmd); ++ ++/** ++ * __phy_package_write_mmd - write MMD reg relative to PHY package base addr ++ * @phydev: The phy_device struct ++ * @addr_offset: The offset to be added to PHY package base_addr ++ * @devad: The MMD to write to ++ * @regnum: The register on the MMD to write ++ * @val: value to write to @regnum ++ * ++ * Convenience helper for writing a register of an MMD on a given PHY ++ * using the PHY package base address. The base address is added to ++ * the addr_offset value. ++ * ++ * Same calling rules as for __phy_write(); ++ * ++ * NOTE: It's assumed that the entire PHY package is either C22 or C45. ++ */ ++int __phy_package_write_mmd(struct phy_device *phydev, ++ unsigned int addr_offset, int devad, ++ u32 regnum, u16 val) ++{ ++ int addr = phy_package_address(phydev, addr_offset); ++ ++ if (addr < 0) ++ return addr; ++ ++ if (regnum > (u16)~0 || devad > 32) ++ return -EINVAL; ++ ++ return mmd_phy_write(phydev->mdio.bus, addr, phydev->is_c45, devad, ++ regnum, val); ++} ++EXPORT_SYMBOL(__phy_package_write_mmd); ++ ++/** ++ * phy_package_write_mmd - write MMD reg relative to PHY package base addr ++ * @phydev: The phy_device struct ++ * @addr_offset: The offset to be added to PHY package base_addr ++ * @devad: The MMD to write to ++ * @regnum: The register on the MMD to write ++ * @val: value to write to @regnum ++ * ++ * Convenience helper for writing a register of an MMD on a given PHY ++ * using the PHY package base address. The base address is added to ++ * the addr_offset value. ++ * ++ * Same calling rules as for phy_write(); ++ * ++ * NOTE: It's assumed that the entire PHY package is either C22 or C45. ++ */ ++int phy_package_write_mmd(struct phy_device *phydev, ++ unsigned int addr_offset, int devad, ++ u32 regnum, u16 val) ++{ ++ int addr = phy_package_address(phydev, addr_offset); ++ int ret; ++ ++ if (addr < 0) ++ return addr; ++ ++ if (regnum > (u16)~0 || devad > 32) ++ return -EINVAL; ++ ++ phy_lock_mdio_bus(phydev); ++ ret = mmd_phy_write(phydev->mdio.bus, addr, phydev->is_c45, devad, ++ regnum, val); ++ phy_unlock_mdio_bus(phydev); ++ ++ return ret; ++} ++EXPORT_SYMBOL(phy_package_write_mmd); ++ ++/** + * phy_modify_changed - Function for modifying a PHY register + * @phydev: the phy_device struct + * @regnum: register number to modify +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -1840,6 +1840,22 @@ static inline int __phy_package_write(st + return __mdiobus_write(phydev->mdio.bus, addr, regnum, val); + } + ++int __phy_package_read_mmd(struct phy_device *phydev, ++ unsigned int addr_offset, int devad, ++ u32 regnum); ++ ++int phy_package_read_mmd(struct phy_device *phydev, ++ unsigned int addr_offset, int devad, ++ u32 regnum); ++ ++int __phy_package_write_mmd(struct phy_device *phydev, ++ unsigned int addr_offset, int devad, ++ u32 regnum, u16 val); ++ ++int phy_package_write_mmd(struct phy_device *phydev, ++ unsigned int addr_offset, int devad, ++ u32 regnum, u16 val); ++ + static inline bool __phy_package_set_once(struct phy_device *phydev, + unsigned int b) + { diff --git a/target/linux/generic/backport-6.6/715-01-v6.2-net-fman-memac-Add-serdes-support.patch b/target/linux/generic/backport-6.6/715-01-v6.2-net-fman-memac-Add-serdes-support.patch new file mode 100644 index 0000000000..2886123f2d --- /dev/null +++ b/target/linux/generic/backport-6.6/715-01-v6.2-net-fman-memac-Add-serdes-support.patch @@ -0,0 +1,103 @@ +From affa013f494486079c3c5ad2d00cebc41a3d7445 Mon Sep 17 00:00:00 2001 +From: Sean Anderson +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 +Signed-off-by: David S. Miller +--- + .../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 + #include + #include ++#include + #include + + /* 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: diff --git a/target/linux/generic/backport-6.6/715-02-v6.2-net-fman-memac-Use-lynx-pcs-driver.patch b/target/linux/generic/backport-6.6/715-02-v6.2-net-fman-memac-Use-lynx-pcs-driver.patch new file mode 100644 index 0000000000..873debc080 --- /dev/null +++ b/target/linux/generic/backport-6.6/715-02-v6.2-net-fman-memac-Use-lynx-pcs-driver.patch @@ -0,0 +1,384 @@ +From fe60e7154d3a35af975c5e6570d6ec31aab9a731 Mon Sep 17 00:00:00 2001 +From: Sean Anderson +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 +Signed-off-by: David S. Miller +--- + 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 + #include ++#include + #include + #include + #include + #include + +-/* 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) { diff --git a/target/linux/generic/backport-6.6/715-03-v6.2-net-dpaa-Convert-to-phylink.patch b/target/linux/generic/backport-6.6/715-03-v6.2-net-dpaa-Convert-to-phylink.patch new file mode 100644 index 0000000000..63b651bb2d --- /dev/null +++ b/target/linux/generic/backport-6.6/715-03-v6.2-net-dpaa-Convert-to-phylink.patch @@ -0,0 +1,2451 @@ +From 38e50fc3d43882a43115b4f1ca3eb88255163c5b Mon Sep 17 00:00:00 2001 +From: Sean Anderson +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 +Signed-off-by: David S. Miller +--- + 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 + #include + #include ++#include + + /* 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 + #include + #include ++#include + + /* 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 + #include + #include ++#include + #include + #include + +@@ -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 + #include + #include ++#include + #include + + #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; diff --git a/target/linux/generic/backport-6.6/715-04-v6.2-net-phylink-provide-phylink_validate_mask_caps-helpe.patch b/target/linux/generic/backport-6.6/715-04-v6.2-net-phylink-provide-phylink_validate_mask_caps-helpe.patch new file mode 100644 index 0000000000..06c348b1cd --- /dev/null +++ b/target/linux/generic/backport-6.6/715-04-v6.2-net-phylink-provide-phylink_validate_mask_caps-helpe.patch @@ -0,0 +1,93 @@ +From bf4de031052fe7c5309e8956c342d4e5ce79038e Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +[rebased on net-next/master and added documentation] +Signed-off-by: Sean Anderson +Signed-off-by: David S. Miller +--- + 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); diff --git a/target/linux/generic/backport-6.6/715-05-v6.2-phylink-require-valid-state-argument-to-phylink_vali.patch b/target/linux/generic/backport-6.6/715-05-v6.2-phylink-require-valid-state-argument-to-phylink_vali.patch new file mode 100644 index 0000000000..e3a1dda688 --- /dev/null +++ b/target/linux/generic/backport-6.6/715-05-v6.2-phylink-require-valid-state-argument-to-phylink_vali.patch @@ -0,0 +1,39 @@ +From 2bf7e4a68c42eed909f3c29582e1fb85cb157e35 Mon Sep 17 00:00:00 2001 +From: Jakub Kicinski +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 +Link: https://lore.kernel.org/r/20221025185126.1720553-1-kuba@kernel.org +Signed-off-by: Jakub Kicinski +--- + 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); + diff --git a/target/linux/generic/backport-6.6/715-06-v6.2-net-phylink-add-phylink_get_link_timer_ns-helper.patch b/target/linux/generic/backport-6.6/715-06-v6.2-net-phylink-add-phylink_get_link_timer_ns-helper.patch new file mode 100644 index 0000000000..c217ed87b5 --- /dev/null +++ b/target/linux/generic/backport-6.6/715-06-v6.2-net-phylink-add-phylink_get_link_timer_ns-helper.patch @@ -0,0 +1,48 @@ +From f8fc363bf0c023e4736a0328174b4a24b44ab23a Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Signed-off-by: Jakub Kicinski +--- + 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, diff --git a/target/linux/generic/backport-6.6/715-07-v6.2-net-remove-explicit-phylink_generic_validate-referen.patch b/target/linux/generic/backport-6.6/715-07-v6.2-net-remove-explicit-phylink_generic_validate-referen.patch new file mode 100644 index 0000000000..28154af920 --- /dev/null +++ b/target/linux/generic/backport-6.6/715-07-v6.2-net-remove-explicit-phylink_generic_validate-referen.patch @@ -0,0 +1,250 @@ +From b45b773a96b0e9e8d51e5d005485f4e376d6ce9a Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Reviewed-by: Vladimir Oltean +Link: https://lore.kernel.org/r/E1or0FZ-001tRa-DI@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + 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); diff --git a/target/linux/generic/backport-6.6/715-08-net-sfp-make-sfp_bus_find_fwnode-take-a-const-fwnode.patch b/target/linux/generic/backport-6.6/715-08-net-sfp-make-sfp_bus_find_fwnode-take-a-const-fwnode.patch new file mode 100644 index 0000000000..37d82b2cd7 --- /dev/null +++ b/target/linux/generic/backport-6.6/715-08-net-sfp-make-sfp_bus_find_fwnode-take-a-const-fwnode.patch @@ -0,0 +1,48 @@ +From a90ac762d345890b40d88a1385a34a2449c2d75e Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Reviewed-by: Simon Horman +Signed-off-by: David S. Miller +--- + 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; + } diff --git a/target/linux/generic/backport-6.6/715-09-v6.4-net-pcs-lynx-don-t-print-an_enabled-in-pcs_get_state.patch b/target/linux/generic/backport-6.6/715-09-v6.4-net-pcs-lynx-don-t-print-an_enabled-in-pcs_get_state.patch new file mode 100644 index 0000000000..290cb8d161 --- /dev/null +++ b/target/linux/generic/backport-6.6/715-09-v6.4-net-pcs-lynx-don-t-print-an_enabled-in-pcs_get_state.patch @@ -0,0 +1,31 @@ +From ecec0ebbc6381a5a375f1cf10c4858f24e91e2ef Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Reviewed-by: Steen Hegelund +Signed-off-by: David S. Miller +--- + 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, diff --git a/target/linux/generic/backport-6.6/715-10-v6.4-net-dpaa2-mac-use-Autoneg-bit-rather-than-an_enabled.patch b/target/linux/generic/backport-6.6/715-10-v6.4-net-dpaa2-mac-use-Autoneg-bit-rather-than-an_enabled.patch new file mode 100644 index 0000000000..38ea265476 --- /dev/null +++ b/target/linux/generic/backport-6.6/715-10-v6.4-net-dpaa2-mac-use-Autoneg-bit-rather-than-an_enabled.patch @@ -0,0 +1,32 @@ +From 99d0f3a1095f4c938b1665025c29411edafe8a01 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Reviewed-by: Simon Horman +Signed-off-by: Jakub Kicinski +--- + 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; diff --git a/target/linux/generic/backport-6.6/715-11-v6.4-net-phylink-support-validated-pause-and-autoneg-in-f.patch b/target/linux/generic/backport-6.6/715-11-v6.4-net-phylink-support-validated-pause-and-autoneg-in-f.patch new file mode 100644 index 0000000000..cb9c411cfb --- /dev/null +++ b/target/linux/generic/backport-6.6/715-11-v6.4-net-phylink-support-validated-pause-and-autoneg-in-f.patch @@ -0,0 +1,64 @@ +From 471c40bde606ec0b1ce8c616f7998739c7a783a6 Mon Sep 17 00:00:00 2001 +From: Ivan Bornyakov +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 +Signed-off-by: David S. Miller +--- + 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); diff --git a/target/linux/generic/backport-6.6/715-12-v6.4-net-phylink-remove-an_enabled.patch b/target/linux/generic/backport-6.6/715-12-v6.4-net-phylink-remove-an_enabled.patch new file mode 100644 index 0000000000..03b4f9d0c4 --- /dev/null +++ b/target/linux/generic/backport-6.6/715-12-v6.4-net-phylink-remove-an_enabled.patch @@ -0,0 +1,177 @@ +From 7211ffd70941933a7825a56cf480f07ee81c321c Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Reviewed-by: Simon Horman +Signed-off-by: Jakub Kicinski +--- + 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; + }; + diff --git a/target/linux/generic/backport-6.6/715-13-v6.5-net-phylink-constify-fwnode-arguments.patch b/target/linux/generic/backport-6.6/715-13-v6.5-net-phylink-constify-fwnode-arguments.patch new file mode 100644 index 0000000000..c06a367b8b --- /dev/null +++ b/target/linux/generic/backport-6.6/715-13-v6.5-net-phylink-constify-fwnode-arguments.patch @@ -0,0 +1,88 @@ +From a3555d1f5c208f0a63eafee77381f68d304a0512 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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 +Signed-off-by: Russell King (Oracle) +Signed-off-by: David S. Miller +--- + 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 *); + diff --git a/target/linux/generic/backport-6.6/715-14-v6.3-net-phy-constify-fwnode_get_phy_node-fwnode-argument.patch b/target/linux/generic/backport-6.6/715-14-v6.3-net-phy-constify-fwnode_get_phy_node-fwnode-argument.patch new file mode 100644 index 0000000000..2649634dc7 --- /dev/null +++ b/target/linux/generic/backport-6.6/715-14-v6.3-net-phy-constify-fwnode_get_phy_node-fwnode-argument.patch @@ -0,0 +1,38 @@ +From 4a0faa02d419a6728abef0f1d8a32d8c35ef95e6 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Reviewed-by: Simon Horman +Signed-off-by: David S. Miller +--- + 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); diff --git a/target/linux/generic/backport-6.6/715-15-v6.4-net-phylink-fix-ksettings_set-ethtool-call.patch b/target/linux/generic/backport-6.6/715-15-v6.4-net-phylink-fix-ksettings_set-ethtool-call.patch new file mode 100644 index 0000000000..5eba18b026 --- /dev/null +++ b/target/linux/generic/backport-6.6/715-15-v6.4-net-phylink-fix-ksettings_set-ethtool-call.patch @@ -0,0 +1,44 @@ +From cc73de0411f7d3cdd157564a78f7a39058420ff8 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Signed-off-by: David S. Miller +--- + 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: diff --git a/target/linux/generic/backport-6.6/715-16-v6.5-net-sfp-add-support-for-setting-signalling-rate.patch b/target/linux/generic/backport-6.6/715-16-v6.5-net-sfp-add-support-for-setting-signalling-rate.patch new file mode 100644 index 0000000000..79de6122b7 --- /dev/null +++ b/target/linux/generic/backport-6.6/715-16-v6.5-net-sfp-add-support-for-setting-signalling-rate.patch @@ -0,0 +1,149 @@ +From 0100d1c5789018ba77bf2f4fab3bd91ecece7b3b Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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 +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + 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) + { + } diff --git a/target/linux/generic/backport-6.6/715-17-v6.5-net-phy-add-helpers-for-comparing-phy-IDs.patch b/target/linux/generic/backport-6.6/715-17-v6.5-net-phy-add-helpers-for-comparing-phy-IDs.patch new file mode 100644 index 0000000000..8a694c86da --- /dev/null +++ b/target/linux/generic/backport-6.6/715-17-v6.5-net-phy-add-helpers-for-comparing-phy-IDs.patch @@ -0,0 +1,147 @@ +From b84acdb07222a701bfc6403b374249c86f97d18d Mon Sep 17 00:00:00 2001 +From: Russell King +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 +Reviewed-by: Simon Horman +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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; diff --git a/target/linux/generic/backport-6.6/715-18-v6.5-net-phylink-require-supported_interfaces-to-be-fille.patch b/target/linux/generic/backport-6.6/715-18-v6.5-net-phylink-require-supported_interfaces-to-be-fille.patch new file mode 100644 index 0000000000..5970355a6c --- /dev/null +++ b/target/linux/generic/backport-6.6/715-18-v6.5-net-phylink-require-supported_interfaces-to-be-fille.patch @@ -0,0 +1,71 @@ +From 441e1e44301fc5762a06737f8ec04bf1ce3fb039 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/E1q0K1u-006EIP-ET@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + 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); diff --git a/target/linux/generic/backport-6.6/715-19-v6.5-net-phylink-remove-duplicated-linkmode-pause-resolut.patch b/target/linux/generic/backport-6.6/715-19-v6.5-net-phylink-remove-duplicated-linkmode-pause-resolut.patch new file mode 100644 index 0000000000..3a26b4b600 --- /dev/null +++ b/target/linux/generic/backport-6.6/715-19-v6.5-net-phylink-remove-duplicated-linkmode-pause-resolut.patch @@ -0,0 +1,64 @@ +From 4b624a39f2ab523ca6a6ad9448fab1deb7b101e2 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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 +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + 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, diff --git a/target/linux/generic/backport-6.6/715-20-v6.5-net-phylink-add-function-to-resolve-clause-73-negoti.patch b/target/linux/generic/backport-6.6/715-20-v6.5-net-phylink-add-function-to-resolve-clause-73-negoti.patch new file mode 100644 index 0000000000..2b2634f80c --- /dev/null +++ b/target/linux/generic/backport-6.6/715-20-v6.5-net-phylink-add-function-to-resolve-clause-73-negoti.patch @@ -0,0 +1,76 @@ +From aa8b6bd2b1f235b262bd27f317a0516f196c2c6a Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Reviewed-by: Andrew Lunn +Signed-off-by: Jakub Kicinski +--- + 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); + diff --git a/target/linux/generic/backport-6.6/715-21-v6.5-net-phylink-provide-phylink_pcs_config-and-phylink_p.patch b/target/linux/generic/backport-6.6/715-21-v6.5-net-phylink-provide-phylink_pcs_config-and-phylink_p.patch new file mode 100644 index 0000000000..eea99a5d78 --- /dev/null +++ b/target/linux/generic/backport-6.6/715-21-v6.5-net-phylink-provide-phylink_pcs_config-and-phylink_p.patch @@ -0,0 +1,100 @@ +From 796d709363135a6bd6a8ccc07b509c939e5b855f Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Link: https://lore.kernel.org/r/E1q1TzK-007Exd-Rs@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + 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, diff --git a/target/linux/generic/backport-6.6/715-23-v6.4-net-phylink-actually-fix-ksettings_set-ethtool-call.patch b/target/linux/generic/backport-6.6/715-23-v6.4-net-phylink-actually-fix-ksettings_set-ethtool-call.patch new file mode 100644 index 0000000000..2f7f7a5737 --- /dev/null +++ b/target/linux/generic/backport-6.6/715-23-v6.4-net-phylink-actually-fix-ksettings_set-ethtool-call.patch @@ -0,0 +1,55 @@ +From 11933aa76865621d8e82553c8f3bc07796a5aaa2 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Link: https://lore.kernel.org/r/E1q4eLm-00Ayxk-GZ@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + 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) { diff --git a/target/linux/generic/backport-6.6/715-24-v6.5-net-phylink-add-PCS-negotiation-mode.patch b/target/linux/generic/backport-6.6/715-24-v6.5-net-phylink-add-PCS-negotiation-mode.patch new file mode 100644 index 0000000000..e1a8539aae --- /dev/null +++ b/target/linux/generic/backport-6.6/715-24-v6.5-net-phylink-add-PCS-negotiation-mode.patch @@ -0,0 +1,324 @@ +From 79b07c3e9c4a2272927be8848c26b372516e1958 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Link: https://lore.kernel.org/r/E1qA8De-00EaFA-Ht@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + 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 + diff --git a/target/linux/generic/backport-6.6/715-25-v6.5-net-phylink-convert-phylink_mii_c22_pcs_config-to-ne.patch b/target/linux/generic/backport-6.6/715-25-v6.5-net-phylink-convert-phylink_mii_c22_pcs_config-to-ne.patch new file mode 100644 index 0000000000..473e9d5836 --- /dev/null +++ b/target/linux/generic/backport-6.6/715-25-v6.5-net-phylink-convert-phylink_mii_c22_pcs_config-to-ne.patch @@ -0,0 +1,45 @@ +From cdb08aa0473730315dbc088d5394e59622314034 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Link: https://lore.kernel.org/r/E1qA8Dj-00EaFG-Mt@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + 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; diff --git a/target/linux/generic/backport-6.6/715-26-v6.5-net-phylink-pass-neg_mode-into-phylink_mii_c22_pcs_c.patch b/target/linux/generic/backport-6.6/715-26-v6.5-net-phylink-pass-neg_mode-into-phylink_mii_c22_pcs_c.patch new file mode 100644 index 0000000000..5572850e95 --- /dev/null +++ b/target/linux/generic/backport-6.6/715-26-v6.5-net-phylink-pass-neg_mode-into-phylink_mii_c22_pcs_c.patch @@ -0,0 +1,187 @@ +From febf2aaf05641f3258cc30e072aff65cffc7c82c Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Link: https://lore.kernel.org/r/E1qA8Do-00EaFM-Ra@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + .../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); diff --git a/target/linux/generic/backport-6.6/715-27-v6.5-net-pcs-lynxi-update-PCS-driver-to-use-neg_mode.patch b/target/linux/generic/backport-6.6/715-27-v6.5-net-pcs-lynxi-update-PCS-driver-to-use-neg_mode.patch new file mode 100644 index 0000000000..5e0128766c --- /dev/null +++ b/target/linux/generic/backport-6.6/715-27-v6.5-net-pcs-lynxi-update-PCS-driver-to-use-neg_mode.patch @@ -0,0 +1,101 @@ +From 3b2de56a146f34e3f70a84cc3a1897064e445d16 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Link: https://lore.kernel.org/r/E1qA8Dz-00EaFY-5A@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + 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; + diff --git a/target/linux/generic/backport-6.6/715-28-v6.4-net-pcs-xpcs-use-Autoneg-bit-rather-than-an_enabled.patch b/target/linux/generic/backport-6.6/715-28-v6.4-net-pcs-xpcs-use-Autoneg-bit-rather-than-an_enabled.patch new file mode 100644 index 0000000000..3dd22d2916 --- /dev/null +++ b/target/linux/generic/backport-6.6/715-28-v6.4-net-pcs-xpcs-use-Autoneg-bit-rather-than-an_enabled.patch @@ -0,0 +1,55 @@ +From 459fd2f11204c962e3153020f4f56748e0e10afb Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Reviewed-by: Simon Horman +Signed-off-by: Jakub Kicinski +--- + 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; + diff --git a/target/linux/generic/backport-6.6/715-29-v6.4-net-pcs-xpcs-fix-incorrect-number-of-interfaces.patch b/target/linux/generic/backport-6.6/715-29-v6.4-net-pcs-xpcs-fix-incorrect-number-of-interfaces.patch new file mode 100644 index 0000000000..7cae8515fa --- /dev/null +++ b/target/linux/generic/backport-6.6/715-29-v6.4-net-pcs-xpcs-fix-incorrect-number-of-interfaces.patch @@ -0,0 +1,30 @@ +From 43fb622d91a9f408322735d2f736495c1009f575 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Reviewed-by: Andrew Lunn +Reviewed-by: Leon Romanovsky +Signed-off-by: David S. Miller +--- + 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, + }, + }; diff --git a/target/linux/generic/backport-6.6/715-v6.9-01-net-phy-qcom-qca808x-fix-logic-error-in-LED-brightne.patch b/target/linux/generic/backport-6.6/715-v6.9-01-net-phy-qcom-qca808x-fix-logic-error-in-LED-brightne.patch new file mode 100644 index 0000000000..0a7b5358f1 --- /dev/null +++ b/target/linux/generic/backport-6.6/715-v6.9-01-net-phy-qcom-qca808x-fix-logic-error-in-LED-brightne.patch @@ -0,0 +1,36 @@ +From f2ec98566775dd4341ec1dcf93aa5859c60de826 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 1 Feb 2024 14:46:00 +0100 +Subject: [PATCH 1/2] net: phy: qcom: qca808x: fix logic error in LED + brightness set + +In switching to using phy_modify_mmd and a more short version of the +LED ON/OFF condition in later revision, it was made a logic error where + +value ? QCA808X_LED_FORCE_ON : QCA808X_LED_FORCE_OFF is always true as +value is always OR with QCA808X_LED_FORCE_EN due to missing () +resulting in the testing condition being QCA808X_LED_FORCE_EN | value. + +Add the () to apply the correct condition and restore correct +functionality of the brightness ON/OFF. + +Fixes: 7196062b64ee ("net: phy: at803x: add LED support for qca808x") +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/phy/qcom/qca808x.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/net/phy/qcom/qca808x.c ++++ b/drivers/net/phy/qcom/qca808x.c +@@ -820,8 +820,8 @@ static int qca808x_led_brightness_set(st + + return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, + QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, +- QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON : +- QCA808X_LED_FORCE_OFF); ++ QCA808X_LED_FORCE_EN | (value ? QCA808X_LED_FORCE_ON : ++ QCA808X_LED_FORCE_OFF)); + } + + static int qca808x_led_blink_set(struct phy_device *phydev, u8 index, diff --git a/target/linux/generic/backport-6.6/715-v6.9-02-net-phy-qcom-qca808x-default-to-LED-active-High-if-n.patch b/target/linux/generic/backport-6.6/715-v6.9-02-net-phy-qcom-qca808x-default-to-LED-active-High-if-n.patch new file mode 100644 index 0000000000..e32ed7f777 --- /dev/null +++ b/target/linux/generic/backport-6.6/715-v6.9-02-net-phy-qcom-qca808x-default-to-LED-active-High-if-n.patch @@ -0,0 +1,41 @@ +From f203c8c77c7616c099647636f4c67d59a45fe8a2 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 1 Feb 2024 14:46:01 +0100 +Subject: [PATCH 2/2] net: phy: qcom: qca808x: default to LED active High if + not set + +qca808x PHY provide support for the led_polarity_set OP to configure +and apply the active-low property but on PHY reset, the Active High bit +is not set resulting in the LED driven as active-low. + +To fix this, check if active-low is not set in DT and enable Active High +polarity by default to restore correct funcionality of the LED. + +Fixes: 7196062b64ee ("net: phy: at803x: add LED support for qca808x") +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/phy/qcom/qca808x.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/drivers/net/phy/qcom/qca808x.c ++++ b/drivers/net/phy/qcom/qca808x.c +@@ -290,8 +290,18 @@ static int qca808x_probe(struct phy_devi + + static int qca808x_config_init(struct phy_device *phydev) + { ++ struct qca808x_priv *priv = phydev->priv; + int ret; + ++ /* Default to LED Active High if active-low not in DT */ ++ if (priv->led_polarity_mode == -1) { ++ ret = phy_set_bits_mmd(phydev, MDIO_MMD_AN, ++ QCA808X_MMD7_LED_POLARITY_CTRL, ++ QCA808X_LED_ACTIVE_HIGH); ++ if (ret) ++ return ret; ++ } ++ + /* Active adc&vga on 802.3az for the link 1000M and 100M */ + ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7, + QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN); diff --git a/target/linux/generic/backport-6.6/716-v6.9-02-net-phy-add-support-for-scanning-PHY-in-PHY-packages.patch b/target/linux/generic/backport-6.6/716-v6.9-02-net-phy-add-support-for-scanning-PHY-in-PHY-packages.patch new file mode 100644 index 0000000000..b355031aa5 --- /dev/null +++ b/target/linux/generic/backport-6.6/716-v6.9-02-net-phy-add-support-for-scanning-PHY-in-PHY-packages.patch @@ -0,0 +1,211 @@ +From 385ef48f468696d6d172eb367656a3466fa0408d Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 6 Feb 2024 18:31:05 +0100 +Subject: [PATCH 02/10] net: phy: add support for scanning PHY in PHY packages + nodes + +Add support for scanning PHY in PHY package nodes. PHY packages nodes +are just container for actual PHY on the MDIO bus. + +Their PHY address defined in the PHY package node are absolute and +reflect the address on the MDIO bus. + +mdio_bus.c and of_mdio.c is updated to now support and parse also +PHY package subnode by checking if the node name match +"ethernet-phy-package". + +As PHY package reg is mandatory and each PHY in the PHY package must +have a reg, every invalid PHY Package node is ignored and will be +skipped by the autoscan fallback. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/mdio/of_mdio.c | 79 +++++++++++++++++++++++++++----------- + drivers/net/phy/mdio_bus.c | 44 +++++++++++++++++---- + 2 files changed, 92 insertions(+), 31 deletions(-) + +--- a/drivers/net/mdio/of_mdio.c ++++ b/drivers/net/mdio/of_mdio.c +@@ -138,6 +138,53 @@ bool of_mdiobus_child_is_phy(struct devi + } + EXPORT_SYMBOL(of_mdiobus_child_is_phy); + ++static int __of_mdiobus_parse_phys(struct mii_bus *mdio, struct device_node *np, ++ bool *scanphys) ++{ ++ struct device_node *child; ++ int addr, rc = 0; ++ ++ /* Loop over the child nodes and register a phy_device for each phy */ ++ for_each_available_child_of_node(np, child) { ++ if (of_node_name_eq(child, "ethernet-phy-package")) { ++ /* Ignore invalid ethernet-phy-package node */ ++ if (!of_find_property(child, "reg", NULL)) ++ continue; ++ ++ rc = __of_mdiobus_parse_phys(mdio, child, NULL); ++ if (rc && rc != -ENODEV) ++ goto exit; ++ ++ continue; ++ } ++ ++ addr = of_mdio_parse_addr(&mdio->dev, child); ++ if (addr < 0) { ++ /* Skip scanning for invalid ethernet-phy-package node */ ++ if (scanphys) ++ *scanphys = true; ++ continue; ++ } ++ ++ if (of_mdiobus_child_is_phy(child)) ++ rc = of_mdiobus_register_phy(mdio, child, addr); ++ else ++ rc = of_mdiobus_register_device(mdio, child, addr); ++ ++ if (rc == -ENODEV) ++ dev_err(&mdio->dev, ++ "MDIO device at address %d is missing.\n", ++ addr); ++ else if (rc) ++ goto exit; ++ } ++ ++ return 0; ++exit: ++ of_node_put(child); ++ return rc; ++} ++ + /** + * __of_mdiobus_register - Register mii_bus and create PHYs from the device tree + * @mdio: pointer to mii_bus structure +@@ -179,33 +226,18 @@ int __of_mdiobus_register(struct mii_bus + return rc; + + /* Loop over the child nodes and register a phy_device for each phy */ +- for_each_available_child_of_node(np, child) { +- addr = of_mdio_parse_addr(&mdio->dev, child); +- if (addr < 0) { +- scanphys = true; +- continue; +- } +- +- if (of_mdiobus_child_is_phy(child)) +- rc = of_mdiobus_register_phy(mdio, child, addr); +- else +- rc = of_mdiobus_register_device(mdio, child, addr); +- +- if (rc == -ENODEV) +- dev_err(&mdio->dev, +- "MDIO device at address %d is missing.\n", +- addr); +- else if (rc) +- goto unregister; +- } ++ rc = __of_mdiobus_parse_phys(mdio, np, &scanphys); ++ if (rc) ++ goto unregister; + + if (!scanphys) + return 0; + + /* auto scan for PHYs with empty reg property */ + for_each_available_child_of_node(np, child) { +- /* Skip PHYs with reg property set */ +- if (of_find_property(child, "reg", NULL)) ++ /* Skip PHYs with reg property set or ethernet-phy-package node */ ++ if (of_find_property(child, "reg", NULL) || ++ of_node_name_eq(child, "ethernet-phy-package")) + continue; + + for (addr = 0; addr < PHY_MAX_ADDR; addr++) { +@@ -226,15 +258,16 @@ int __of_mdiobus_register(struct mii_bus + if (!rc) + break; + if (rc != -ENODEV) +- goto unregister; ++ goto put_unregister; + } + } + } + + return 0; + +-unregister: ++put_unregister: + of_node_put(child); ++unregister: + mdiobus_unregister(mdio); + return rc; + } +--- a/drivers/net/phy/mdio_bus.c ++++ b/drivers/net/phy/mdio_bus.c +@@ -448,19 +448,34 @@ EXPORT_SYMBOL(of_mdio_find_bus); + * found, set the of_node pointer for the mdio device. This allows + * auto-probed phy devices to be supplied with information passed in + * via DT. ++ * If a PHY package is found, PHY is searched also there. + */ +-static void of_mdiobus_link_mdiodev(struct mii_bus *bus, +- struct mdio_device *mdiodev) ++static int of_mdiobus_find_phy(struct device *dev, struct mdio_device *mdiodev, ++ struct device_node *np) + { +- struct device *dev = &mdiodev->dev; + struct device_node *child; + +- if (dev->of_node || !bus->dev.of_node) +- return; +- +- for_each_available_child_of_node(bus->dev.of_node, child) { ++ for_each_available_child_of_node(np, child) { + int addr; + ++ if (of_node_name_eq(child, "ethernet-phy-package")) { ++ /* Validate PHY package reg presence */ ++ if (!of_find_property(child, "reg", NULL)) { ++ of_node_put(child); ++ return -EINVAL; ++ } ++ ++ if (!of_mdiobus_find_phy(dev, mdiodev, child)) { ++ /* The refcount for the PHY package will be ++ * incremented later when PHY join the Package. ++ */ ++ of_node_put(child); ++ return 0; ++ } ++ ++ continue; ++ } ++ + addr = of_mdio_parse_addr(dev, child); + if (addr < 0) + continue; +@@ -470,9 +485,22 @@ static void of_mdiobus_link_mdiodev(stru + /* The refcount on "child" is passed to the mdio + * device. Do _not_ use of_node_put(child) here. + */ +- return; ++ return 0; + } + } ++ ++ return -ENODEV; ++} ++ ++static void of_mdiobus_link_mdiodev(struct mii_bus *bus, ++ struct mdio_device *mdiodev) ++{ ++ struct device *dev = &mdiodev->dev; ++ ++ if (dev->of_node || !bus->dev.of_node) ++ return; ++ ++ of_mdiobus_find_phy(dev, mdiodev, bus->dev.of_node); + } + #else /* !IS_ENABLED(CONFIG_OF_MDIO) */ + static inline void of_mdiobus_link_mdiodev(struct mii_bus *mdio, diff --git a/target/linux/generic/backport-6.6/716-v6.9-03-net-phy-add-devm-of_phy_package_join-helper.patch b/target/linux/generic/backport-6.6/716-v6.9-03-net-phy-add-devm-of_phy_package_join-helper.patch new file mode 100644 index 0000000000..3c7bf6c132 --- /dev/null +++ b/target/linux/generic/backport-6.6/716-v6.9-03-net-phy-add-devm-of_phy_package_join-helper.patch @@ -0,0 +1,185 @@ +From 471e8fd3afcef5a9f9089f0bd21965ad9ba35c91 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 6 Feb 2024 18:31:06 +0100 +Subject: [PATCH 03/10] net: phy: add devm/of_phy_package_join helper + +Add devm/of_phy_package_join helper to join PHYs in a PHY package. These +are variant of the manual phy_package_join with the difference that +these will use DT nodes to derive the base_addr instead of manually +passing an hardcoded value. + +An additional value is added in phy_package_shared, "np" to reference +the PHY package node pointer in specific PHY driver probe_once and +config_init_once functions to make use of additional specific properties +defined in the PHY package node in DT. + +The np value is filled only with of_phy_package_join if a valid PHY +package node is found. A valid PHY package node must have the node name +set to "ethernet-phy-package". + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/phy_device.c | 96 ++++++++++++++++++++++++++++++++++++ + include/linux/phy.h | 6 +++ + 2 files changed, 102 insertions(+) + +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -1648,6 +1648,7 @@ int phy_package_join(struct phy_device * + shared->priv_size = priv_size; + } + shared->base_addr = base_addr; ++ shared->np = NULL; + refcount_set(&shared->refcnt, 1); + bus->shared[base_addr] = shared; + } else { +@@ -1671,6 +1672,63 @@ err_unlock: + EXPORT_SYMBOL_GPL(phy_package_join); + + /** ++ * of_phy_package_join - join a common PHY group in PHY package ++ * @phydev: target phy_device struct ++ * @priv_size: if non-zero allocate this amount of bytes for private data ++ * ++ * This is a variant of phy_package_join for PHY package defined in DT. ++ * ++ * The parent node of the @phydev is checked as a valid PHY package node ++ * structure (by matching the node name "ethernet-phy-package") and the ++ * base_addr for the PHY package is passed to phy_package_join. ++ * ++ * With this configuration the shared struct will also have the np value ++ * filled to use additional DT defined properties in PHY specific ++ * probe_once and config_init_once PHY package OPs. ++ * ++ * Returns < 0 on error, 0 on success. Esp. calling phy_package_join() ++ * with the same cookie but a different priv_size is an error. Or a parent ++ * node is not detected or is not valid or doesn't match the expected node ++ * name for PHY package. ++ */ ++int of_phy_package_join(struct phy_device *phydev, size_t priv_size) ++{ ++ struct device_node *node = phydev->mdio.dev.of_node; ++ struct device_node *package_node; ++ u32 base_addr; ++ int ret; ++ ++ if (!node) ++ return -EINVAL; ++ ++ package_node = of_get_parent(node); ++ if (!package_node) ++ return -EINVAL; ++ ++ if (!of_node_name_eq(package_node, "ethernet-phy-package")) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ if (of_property_read_u32(package_node, "reg", &base_addr)) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ ret = phy_package_join(phydev, base_addr, priv_size); ++ if (ret) ++ goto exit; ++ ++ phydev->shared->np = package_node; ++ ++ return 0; ++exit: ++ of_node_put(package_node); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(of_phy_package_join); ++ ++/** + * phy_package_leave - leave a common PHY group + * @phydev: target phy_device struct + * +@@ -1686,6 +1744,10 @@ void phy_package_leave(struct phy_device + if (!shared) + return; + ++ /* Decrease the node refcount on leave if present */ ++ if (shared->np) ++ of_node_put(shared->np); ++ + if (refcount_dec_and_mutex_lock(&shared->refcnt, &bus->shared_lock)) { + bus->shared[shared->base_addr] = NULL; + mutex_unlock(&bus->shared_lock); +@@ -1739,6 +1801,40 @@ int devm_phy_package_join(struct device + EXPORT_SYMBOL_GPL(devm_phy_package_join); + + /** ++ * devm_of_phy_package_join - resource managed of_phy_package_join() ++ * @dev: device that is registering this PHY package ++ * @phydev: target phy_device struct ++ * @priv_size: if non-zero allocate this amount of bytes for private data ++ * ++ * Managed of_phy_package_join(). Shared storage fetched by this function, ++ * phy_package_leave() is automatically called on driver detach. See ++ * of_phy_package_join() for more information. ++ */ ++int devm_of_phy_package_join(struct device *dev, struct phy_device *phydev, ++ size_t priv_size) ++{ ++ struct phy_device **ptr; ++ int ret; ++ ++ ptr = devres_alloc(devm_phy_package_leave, sizeof(*ptr), ++ GFP_KERNEL); ++ if (!ptr) ++ return -ENOMEM; ++ ++ ret = of_phy_package_join(phydev, priv_size); ++ ++ if (!ret) { ++ *ptr = phydev; ++ devres_add(dev, ptr); ++ } else { ++ devres_free(ptr); ++ } ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(devm_of_phy_package_join); ++ ++/** + * phy_detach - detach a PHY device from its network device + * @phydev: target phy_device struct + * +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -321,6 +321,7 @@ struct mdio_bus_stats { + * struct phy_package_shared - Shared information in PHY packages + * @base_addr: Base PHY address of PHY package used to combine PHYs + * in one package and for offset calculation of phy_package_read/write ++ * @np: Pointer to the Device Node if PHY package defined in DT + * @refcnt: Number of PHYs connected to this shared data + * @flags: Initialization of PHY package + * @priv_size: Size of the shared private data @priv +@@ -332,6 +333,8 @@ struct mdio_bus_stats { + */ + struct phy_package_shared { + u8 base_addr; ++ /* With PHY package defined in DT this points to the PHY package node */ ++ struct device_node *np; + refcount_t refcnt; + unsigned long flags; + size_t priv_size; +@@ -1793,9 +1796,12 @@ int phy_ethtool_set_link_ksettings(struc + const struct ethtool_link_ksettings *cmd); + int phy_ethtool_nway_reset(struct net_device *ndev); + int phy_package_join(struct phy_device *phydev, int base_addr, size_t priv_size); ++int of_phy_package_join(struct phy_device *phydev, size_t priv_size); + void phy_package_leave(struct phy_device *phydev); + int devm_phy_package_join(struct device *dev, struct phy_device *phydev, + int base_addr, size_t priv_size); ++int devm_of_phy_package_join(struct device *dev, struct phy_device *phydev, ++ size_t priv_size); + + #if IS_ENABLED(CONFIG_PHYLIB) + int __init mdio_bus_init(void); diff --git a/target/linux/generic/backport-6.6/716-v6.9-04-net-phy-qcom-move-more-function-to-shared-library.patch b/target/linux/generic/backport-6.6/716-v6.9-04-net-phy-qcom-move-more-function-to-shared-library.patch new file mode 100644 index 0000000000..e935725630 --- /dev/null +++ b/target/linux/generic/backport-6.6/716-v6.9-04-net-phy-qcom-move-more-function-to-shared-library.patch @@ -0,0 +1,583 @@ +From 737eb75a815f9c08dcbb6631db57f4f4b0540a5b Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 6 Feb 2024 18:31:07 +0100 +Subject: [PATCH 04/10] net: phy: qcom: move more function to shared library + +Move more function to shared library in preparation for introduction of +new PHY Family qca807x that will make use of both functions from at803x +and qca808x as it's a transition PHY with some implementation of at803x +and some from the new qca808x. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/qcom/at803x.c | 35 ----- + drivers/net/phy/qcom/qca808x.c | 205 ---------------------------- + drivers/net/phy/qcom/qcom-phy-lib.c | 193 ++++++++++++++++++++++++++ + drivers/net/phy/qcom/qcom.h | 51 +++++++ + 4 files changed, 244 insertions(+), 240 deletions(-) + +--- a/drivers/net/phy/qcom/at803x.c ++++ b/drivers/net/phy/qcom/at803x.c +@@ -504,41 +504,6 @@ static void at803x_link_change_notify(st + } + } + +-static int at803x_read_status(struct phy_device *phydev) +-{ +- struct at803x_ss_mask ss_mask = { 0 }; +- int err, old_link = phydev->link; +- +- /* Update the link, but return if there was an error */ +- err = genphy_update_link(phydev); +- if (err) +- return err; +- +- /* why bother the PHY if nothing can have changed */ +- if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) +- return 0; +- +- phydev->speed = SPEED_UNKNOWN; +- phydev->duplex = DUPLEX_UNKNOWN; +- phydev->pause = 0; +- phydev->asym_pause = 0; +- +- err = genphy_read_lpa(phydev); +- if (err < 0) +- return err; +- +- ss_mask.speed_mask = AT803X_SS_SPEED_MASK; +- ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK); +- err = at803x_read_specific_status(phydev, ss_mask); +- if (err < 0) +- return err; +- +- if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) +- phy_resolve_aneg_pause(phydev); +- +- return 0; +-} +- + static int at803x_config_aneg(struct phy_device *phydev) + { + struct at803x_priv *priv = phydev->priv; +--- a/drivers/net/phy/qcom/qca808x.c ++++ b/drivers/net/phy/qcom/qca808x.c +@@ -2,7 +2,6 @@ + + #include + #include +-#include + + #include "qcom.h" + +@@ -63,55 +62,6 @@ + #define QCA808X_DBG_AN_TEST 0xb + #define QCA808X_HIBERNATION_EN BIT(15) + +-#define QCA808X_CDT_ENABLE_TEST BIT(15) +-#define QCA808X_CDT_INTER_CHECK_DIS BIT(13) +-#define QCA808X_CDT_STATUS BIT(11) +-#define QCA808X_CDT_LENGTH_UNIT BIT(10) +- +-#define QCA808X_MMD3_CDT_STATUS 0x8064 +-#define QCA808X_MMD3_CDT_DIAG_PAIR_A 0x8065 +-#define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066 +-#define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067 +-#define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068 +-#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8) +-#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0) +- +-#define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12) +-#define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8) +-#define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4) +-#define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0) +- +-#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0) +-#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0) +-#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1) +-#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2) +-#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3) +- +-#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2) +-#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1) +-#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2) +-#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3) +- +-/* NORMAL are MDI with type set to 0 */ +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1 +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ +- QCA808X_CDT_STATUS_STAT_MDI1) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ +- QCA808X_CDT_STATUS_STAT_MDI1) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2 +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ +- QCA808X_CDT_STATUS_STAT_MDI2) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ +- QCA808X_CDT_STATUS_STAT_MDI2) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3 +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ +- QCA808X_CDT_STATUS_STAT_MDI3) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ +- QCA808X_CDT_STATUS_STAT_MDI3) +- +-/* Added for reference of existence but should be handled by wait_for_completion already */ +-#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3)) +- + #define QCA808X_MMD7_LED_GLOBAL 0x8073 + #define QCA808X_LED_BLINK_1 GENMASK(11, 6) + #define QCA808X_LED_BLINK_2 GENMASK(5, 0) +@@ -406,86 +356,6 @@ static int qca808x_soft_reset(struct phy + return ret; + } + +-static bool qca808x_cdt_fault_length_valid(int cdt_code) +-{ +- switch (cdt_code) { +- case QCA808X_CDT_STATUS_STAT_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: +- return true; +- default: +- return false; +- } +-} +- +-static int qca808x_cable_test_result_trans(int cdt_code) +-{ +- switch (cdt_code) { +- case QCA808X_CDT_STATUS_STAT_NORMAL: +- return ETHTOOL_A_CABLE_RESULT_CODE_OK; +- case QCA808X_CDT_STATUS_STAT_SAME_SHORT: +- return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; +- case QCA808X_CDT_STATUS_STAT_SAME_OPEN: +- return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: +- return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT; +- case QCA808X_CDT_STATUS_STAT_FAIL: +- default: +- return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; +- } +-} +- +-static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair, +- int result) +-{ +- int val; +- u32 cdt_length_reg = 0; +- +- switch (pair) { +- case ETHTOOL_A_CABLE_PAIR_A: +- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A; +- break; +- case ETHTOOL_A_CABLE_PAIR_B: +- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B; +- break; +- case ETHTOOL_A_CABLE_PAIR_C: +- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C; +- break; +- case ETHTOOL_A_CABLE_PAIR_D: +- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D; +- break; +- default: +- return -EINVAL; +- } +- +- val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg); +- if (val < 0) +- return val; +- +- if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT) +- val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val); +- else +- val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val); +- +- return at803x_cdt_fault_length(val); +-} +- + static int qca808x_cable_test_start(struct phy_device *phydev) + { + int ret; +@@ -526,81 +396,6 @@ static int qca808x_cable_test_start(stru + + return 0; + } +- +-static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair, +- u16 status) +-{ +- int length, result; +- u16 pair_code; +- +- switch (pair) { +- case ETHTOOL_A_CABLE_PAIR_A: +- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status); +- break; +- case ETHTOOL_A_CABLE_PAIR_B: +- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status); +- break; +- case ETHTOOL_A_CABLE_PAIR_C: +- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status); +- break; +- case ETHTOOL_A_CABLE_PAIR_D: +- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status); +- break; +- default: +- return -EINVAL; +- } +- +- result = qca808x_cable_test_result_trans(pair_code); +- ethnl_cable_test_result(phydev, pair, result); +- +- if (qca808x_cdt_fault_length_valid(pair_code)) { +- length = qca808x_cdt_fault_length(phydev, pair, result); +- ethnl_cable_test_fault_length(phydev, pair, length); +- } +- +- return 0; +-} +- +-static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished) +-{ +- int ret, val; +- +- *finished = false; +- +- val = QCA808X_CDT_ENABLE_TEST | +- QCA808X_CDT_LENGTH_UNIT; +- ret = at803x_cdt_start(phydev, val); +- if (ret) +- return ret; +- +- ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST); +- if (ret) +- return ret; +- +- val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS); +- if (val < 0) +- return val; +- +- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val); +- if (ret) +- return ret; +- +- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val); +- if (ret) +- return ret; +- +- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val); +- if (ret) +- return ret; +- +- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val); +- if (ret) +- return ret; +- +- *finished = true; +- +- return 0; +-} + + static int qca808x_get_features(struct phy_device *phydev) + { +--- a/drivers/net/phy/qcom/qcom-phy-lib.c ++++ b/drivers/net/phy/qcom/qcom-phy-lib.c +@@ -5,6 +5,7 @@ + + #include + #include ++#include + + #include "qcom.h" + +@@ -311,6 +312,42 @@ int at803x_prepare_config_aneg(struct ph + } + EXPORT_SYMBOL_GPL(at803x_prepare_config_aneg); + ++int at803x_read_status(struct phy_device *phydev) ++{ ++ struct at803x_ss_mask ss_mask = { 0 }; ++ int err, old_link = phydev->link; ++ ++ /* Update the link, but return if there was an error */ ++ err = genphy_update_link(phydev); ++ if (err) ++ return err; ++ ++ /* why bother the PHY if nothing can have changed */ ++ if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) ++ return 0; ++ ++ phydev->speed = SPEED_UNKNOWN; ++ phydev->duplex = DUPLEX_UNKNOWN; ++ phydev->pause = 0; ++ phydev->asym_pause = 0; ++ ++ err = genphy_read_lpa(phydev); ++ if (err < 0) ++ return err; ++ ++ ss_mask.speed_mask = AT803X_SS_SPEED_MASK; ++ ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK); ++ err = at803x_read_specific_status(phydev, ss_mask); ++ if (err < 0) ++ return err; ++ ++ if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) ++ phy_resolve_aneg_pause(phydev); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(at803x_read_status); ++ + static int at803x_get_downshift(struct phy_device *phydev, u8 *d) + { + int val; +@@ -427,3 +464,159 @@ int at803x_cdt_wait_for_completion(struc + return ret < 0 ? ret : 0; + } + EXPORT_SYMBOL_GPL(at803x_cdt_wait_for_completion); ++ ++static bool qca808x_cdt_fault_length_valid(int cdt_code) ++{ ++ switch (cdt_code) { ++ case QCA808X_CDT_STATUS_STAT_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static int qca808x_cable_test_result_trans(int cdt_code) ++{ ++ switch (cdt_code) { ++ case QCA808X_CDT_STATUS_STAT_NORMAL: ++ return ETHTOOL_A_CABLE_RESULT_CODE_OK; ++ case QCA808X_CDT_STATUS_STAT_SAME_SHORT: ++ return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; ++ case QCA808X_CDT_STATUS_STAT_SAME_OPEN: ++ return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: ++ return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT; ++ case QCA808X_CDT_STATUS_STAT_FAIL: ++ default: ++ return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; ++ } ++} ++ ++static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair, ++ int result) ++{ ++ int val; ++ u32 cdt_length_reg = 0; ++ ++ switch (pair) { ++ case ETHTOOL_A_CABLE_PAIR_A: ++ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A; ++ break; ++ case ETHTOOL_A_CABLE_PAIR_B: ++ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B; ++ break; ++ case ETHTOOL_A_CABLE_PAIR_C: ++ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C; ++ break; ++ case ETHTOOL_A_CABLE_PAIR_D: ++ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg); ++ if (val < 0) ++ return val; ++ ++ if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT) ++ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val); ++ else ++ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val); ++ ++ return at803x_cdt_fault_length(val); ++} ++ ++static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair, ++ u16 status) ++{ ++ int length, result; ++ u16 pair_code; ++ ++ switch (pair) { ++ case ETHTOOL_A_CABLE_PAIR_A: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status); ++ break; ++ case ETHTOOL_A_CABLE_PAIR_B: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status); ++ break; ++ case ETHTOOL_A_CABLE_PAIR_C: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status); ++ break; ++ case ETHTOOL_A_CABLE_PAIR_D: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ result = qca808x_cable_test_result_trans(pair_code); ++ ethnl_cable_test_result(phydev, pair, result); ++ ++ if (qca808x_cdt_fault_length_valid(pair_code)) { ++ length = qca808x_cdt_fault_length(phydev, pair, result); ++ ethnl_cable_test_fault_length(phydev, pair, length); ++ } ++ ++ return 0; ++} ++ ++int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished) ++{ ++ int ret, val; ++ ++ *finished = false; ++ ++ val = QCA808X_CDT_ENABLE_TEST | ++ QCA808X_CDT_LENGTH_UNIT; ++ ret = at803x_cdt_start(phydev, val); ++ if (ret) ++ return ret; ++ ++ ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST); ++ if (ret) ++ return ret; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS); ++ if (val < 0) ++ return val; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val); ++ if (ret) ++ return ret; ++ ++ *finished = true; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(qca808x_cable_test_get_status); +--- a/drivers/net/phy/qcom/qcom.h ++++ b/drivers/net/phy/qcom/qcom.h +@@ -54,6 +54,55 @@ + #define AT803X_CDT_STATUS_STAT_MASK GENMASK(9, 8) + #define AT803X_CDT_STATUS_DELTA_TIME_MASK GENMASK(7, 0) + ++#define QCA808X_CDT_ENABLE_TEST BIT(15) ++#define QCA808X_CDT_INTER_CHECK_DIS BIT(13) ++#define QCA808X_CDT_STATUS BIT(11) ++#define QCA808X_CDT_LENGTH_UNIT BIT(10) ++ ++#define QCA808X_MMD3_CDT_STATUS 0x8064 ++#define QCA808X_MMD3_CDT_DIAG_PAIR_A 0x8065 ++#define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066 ++#define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067 ++#define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068 ++#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8) ++#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0) ++ ++#define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12) ++#define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8) ++#define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4) ++#define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0) ++ ++#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0) ++#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0) ++#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1) ++#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2) ++#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3) ++ ++#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2) ++#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1) ++#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2) ++#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3) ++ ++/* NORMAL are MDI with type set to 0 */ ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1 ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ ++ QCA808X_CDT_STATUS_STAT_MDI1) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ ++ QCA808X_CDT_STATUS_STAT_MDI1) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2 ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ ++ QCA808X_CDT_STATUS_STAT_MDI2) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ ++ QCA808X_CDT_STATUS_STAT_MDI2) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3 ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ ++ QCA808X_CDT_STATUS_STAT_MDI3) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ ++ QCA808X_CDT_STATUS_STAT_MDI3) ++ ++/* Added for reference of existence but should be handled by wait_for_completion already */ ++#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3)) ++ + #define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C + #define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B + #define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A +@@ -110,6 +159,7 @@ int at803x_read_specific_status(struct p + struct at803x_ss_mask ss_mask); + int at803x_config_mdix(struct phy_device *phydev, u8 ctrl); + int at803x_prepare_config_aneg(struct phy_device *phydev); ++int at803x_read_status(struct phy_device *phydev); + int at803x_get_tunable(struct phy_device *phydev, + struct ethtool_tunable *tuna, void *data); + int at803x_set_tunable(struct phy_device *phydev, +@@ -118,3 +168,4 @@ int at803x_cdt_fault_length(int dt); + int at803x_cdt_start(struct phy_device *phydev, u32 cdt_start); + int at803x_cdt_wait_for_completion(struct phy_device *phydev, + u32 cdt_en); ++int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished); diff --git a/target/linux/generic/backport-6.6/716-v6.9-06-net-phy-provide-whether-link-has-changed-in-c37_read.patch b/target/linux/generic/backport-6.6/716-v6.9-06-net-phy-provide-whether-link-has-changed-in-c37_read.patch new file mode 100644 index 0000000000..acaa4a644e --- /dev/null +++ b/target/linux/generic/backport-6.6/716-v6.9-06-net-phy-provide-whether-link-has-changed-in-c37_read.patch @@ -0,0 +1,100 @@ +From 9b1d5e055508393561e26bd1720f4c2639b03b1a Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 6 Feb 2024 18:31:09 +0100 +Subject: [PATCH 06/10] net: phy: provide whether link has changed in + c37_read_status + +Some PHY driver might require additional regs call after +genphy_c37_read_status() is called. + +Expand genphy_c37_read_status to provide a bool wheather the link has +changed or not to permit PHY driver to skip additional regs call if +nothing has changed. + +Every user of genphy_c37_read_status() is updated with the new +additional bool. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/broadcom.c | 3 ++- + drivers/net/phy/phy_device.c | 11 +++++++++-- + drivers/net/phy/qcom/at803x.c | 3 ++- + include/linux/phy.h | 2 +- + 4 files changed, 14 insertions(+), 5 deletions(-) + +--- a/drivers/net/phy/broadcom.c ++++ b/drivers/net/phy/broadcom.c +@@ -609,10 +609,11 @@ static int bcm54616s_config_aneg(struct + static int bcm54616s_read_status(struct phy_device *phydev) + { + struct bcm54616s_phy_priv *priv = phydev->priv; ++ bool changed; + int err; + + if (priv->mode_1000bx_en) +- err = genphy_c37_read_status(phydev); ++ err = genphy_c37_read_status(phydev, &changed); + else + err = genphy_read_status(phydev); + +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -2549,12 +2549,15 @@ EXPORT_SYMBOL(genphy_read_status); + /** + * genphy_c37_read_status - check the link status and update current link state + * @phydev: target phy_device struct ++ * @changed: pointer where to store if link changed + * + * Description: Check the link, then figure out the current state + * by comparing what we advertise with what the link partner + * advertises. This function is for Clause 37 1000Base-X mode. ++ * ++ * If link has changed, @changed is set to true, false otherwise. + */ +-int genphy_c37_read_status(struct phy_device *phydev) ++int genphy_c37_read_status(struct phy_device *phydev, bool *changed) + { + int lpa, err, old_link = phydev->link; + +@@ -2564,9 +2567,13 @@ int genphy_c37_read_status(struct phy_de + return err; + + /* why bother the PHY if nothing can have changed */ +- if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) ++ if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) { ++ *changed = false; + return 0; ++ } + ++ /* Signal link has changed */ ++ *changed = true; + phydev->duplex = DUPLEX_UNKNOWN; + phydev->pause = 0; + phydev->asym_pause = 0; +--- a/drivers/net/phy/qcom/at803x.c ++++ b/drivers/net/phy/qcom/at803x.c +@@ -912,9 +912,10 @@ static int at8031_config_intr(struct phy + static int at8031_read_status(struct phy_device *phydev) + { + struct at803x_priv *priv = phydev->priv; ++ bool changed; + + if (priv->is_1000basex) +- return genphy_c37_read_status(phydev); ++ return genphy_c37_read_status(phydev, &changed); + + return at803x_read_status(phydev); + } +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -1688,7 +1688,7 @@ int genphy_write_mmd_unsupported(struct + + /* Clause 37 */ + int genphy_c37_config_aneg(struct phy_device *phydev); +-int genphy_c37_read_status(struct phy_device *phydev); ++int genphy_c37_read_status(struct phy_device *phydev, bool *changed); + + /* Clause 45 PHY */ + int genphy_c45_restart_aneg(struct phy_device *phydev); diff --git a/target/linux/generic/backport-6.6/716-v6.9-07-net-phy-qcom-add-support-for-QCA807x-PHY-Family.patch b/target/linux/generic/backport-6.6/716-v6.9-07-net-phy-qcom-add-support-for-QCA807x-PHY-Family.patch new file mode 100644 index 0000000000..bbf0f76d68 --- /dev/null +++ b/target/linux/generic/backport-6.6/716-v6.9-07-net-phy-qcom-add-support-for-QCA807x-PHY-Family.patch @@ -0,0 +1,668 @@ +From d1cb613efbd3cd7d0c000167816beb3f248f5eb8 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Tue, 6 Feb 2024 18:31:10 +0100 +Subject: [PATCH 07/10] net: phy: qcom: add support for QCA807x PHY Family + +This adds driver for the Qualcomm QCA8072 and QCA8075 PHY-s. + +They are 2 or 5 port IEEE 802.3 clause 22 compliant 10BASE-Te, +100BASE-TX and 1000BASE-T PHY-s. + +They feature 2 SerDes, one for PSGMII or QSGMII connection with +MAC, while second one is SGMII for connection to MAC or fiber. + +Both models have a combo port that supports 1000BASE-X and +100BASE-FX fiber. + +PHY package can be configured in 3 mode following this table: + + First Serdes mode Second Serdes mode +Option 1 PSGMII for copper Disabled + ports 0-4 +Option 2 PSGMII for copper 1000BASE-X / 100BASE-FX + ports 0-4 +Option 3 QSGMII for copper SGMII for + ports 0-3 copper port 4 + +Each PHY inside of QCA807x series has 4 digitally controlled +output only pins that natively drive LED-s. +But some vendors used these to driver generic LED-s controlled +by userspace, so lets enable registering each PHY as GPIO +controller and add driver for it. + +These are commonly used in Qualcomm IPQ40xx, IPQ60xx and IPQ807x +boards. + +Co-developed-by: Christian Marangi +Signed-off-by: Robert Marko +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/qcom/Kconfig | 8 + + drivers/net/phy/qcom/Makefile | 1 + + drivers/net/phy/qcom/qca807x.c | 597 +++++++++++++++++++++++++++++++++ + 3 files changed, 606 insertions(+) + create mode 100644 drivers/net/phy/qcom/qca807x.c + +--- a/drivers/net/phy/qcom/Kconfig ++++ b/drivers/net/phy/qcom/Kconfig +@@ -20,3 +20,11 @@ config QCA808X_PHY + select QCOM_NET_PHYLIB + help + Currently supports the QCA8081 model ++ ++config QCA807X_PHY ++ tristate "Qualcomm QCA807x PHYs" ++ select QCOM_NET_PHYLIB ++ depends on OF_MDIO ++ help ++ Currently supports the Qualcomm QCA8072, QCA8075 and the PSGMII ++ control PHY. +--- a/drivers/net/phy/qcom/Makefile ++++ b/drivers/net/phy/qcom/Makefile +@@ -3,3 +3,4 @@ obj-$(CONFIG_QCOM_NET_PHYLIB) += qcom-ph + obj-$(CONFIG_AT803X_PHY) += at803x.o + obj-$(CONFIG_QCA83XX_PHY) += qca83xx.o + obj-$(CONFIG_QCA808X_PHY) += qca808x.o ++obj-$(CONFIG_QCA807X_PHY) += qca807x.o +--- /dev/null ++++ b/drivers/net/phy/qcom/qca807x.c +@@ -0,0 +1,597 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Copyright (c) 2023 Sartura Ltd. ++ * ++ * Author: Robert Marko ++ * Christian Marangi ++ * ++ * Qualcomm QCA8072 and QCA8075 PHY driver ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "qcom.h" ++ ++#define QCA807X_CHIP_CONFIGURATION 0x1f ++#define QCA807X_BT_BX_REG_SEL BIT(15) ++#define QCA807X_BT_BX_REG_SEL_FIBER 0 ++#define QCA807X_BT_BX_REG_SEL_COPPER 1 ++#define QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK GENMASK(3, 0) ++#define QCA807X_CHIP_CONFIGURATION_MODE_QSGMII_SGMII 4 ++#define QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER 3 ++#define QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_ALL_COPPER 0 ++ ++#define QCA807X_MEDIA_SELECT_STATUS 0x1a ++#define QCA807X_MEDIA_DETECTED_COPPER BIT(5) ++#define QCA807X_MEDIA_DETECTED_1000_BASE_X BIT(4) ++#define QCA807X_MEDIA_DETECTED_100_BASE_FX BIT(3) ++ ++#define QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION 0x807e ++#define QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN BIT(0) ++ ++#define QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH 0x801a ++#define QCA807X_CONTROL_DAC_MASK GENMASK(2, 0) ++/* List of tweaks enabled by this bit: ++ * - With both FULL amplitude and FULL bias current: bias current ++ * is set to half. ++ * - With only DSP amplitude: bias current is set to half and ++ * is set to 1/4 with cable < 10m. ++ * - With DSP bias current (included both DSP amplitude and ++ * DSP bias current): bias current is half the detected current ++ * with cable < 10m. ++ */ ++#define QCA807X_CONTROL_DAC_BIAS_CURRENT_TWEAK BIT(2) ++#define QCA807X_CONTROL_DAC_DSP_BIAS_CURRENT BIT(1) ++#define QCA807X_CONTROL_DAC_DSP_AMPLITUDE BIT(0) ++ ++#define QCA807X_MMD7_LED_100N_1 0x8074 ++#define QCA807X_MMD7_LED_100N_2 0x8075 ++#define QCA807X_MMD7_LED_1000N_1 0x8076 ++#define QCA807X_MMD7_LED_1000N_2 0x8077 ++ ++#define QCA807X_MMD7_LED_CTRL(x) (0x8074 + ((x) * 2)) ++#define QCA807X_MMD7_LED_FORCE_CTRL(x) (0x8075 + ((x) * 2)) ++ ++#define QCA807X_GPIO_FORCE_EN BIT(15) ++#define QCA807X_GPIO_FORCE_MODE_MASK GENMASK(14, 13) ++ ++#define QCA807X_FUNCTION_CONTROL 0x10 ++#define QCA807X_FC_MDI_CROSSOVER_MODE_MASK GENMASK(6, 5) ++#define QCA807X_FC_MDI_CROSSOVER_AUTO 3 ++#define QCA807X_FC_MDI_CROSSOVER_MANUAL_MDIX 1 ++#define QCA807X_FC_MDI_CROSSOVER_MANUAL_MDI 0 ++ ++/* PQSGMII Analog PHY specific */ ++#define PQSGMII_CTRL_REG 0x0 ++#define PQSGMII_ANALOG_SW_RESET BIT(6) ++#define PQSGMII_DRIVE_CONTROL_1 0xb ++#define PQSGMII_TX_DRIVER_MASK GENMASK(7, 4) ++#define PQSGMII_TX_DRIVER_140MV 0x0 ++#define PQSGMII_TX_DRIVER_160MV 0x1 ++#define PQSGMII_TX_DRIVER_180MV 0x2 ++#define PQSGMII_TX_DRIVER_200MV 0x3 ++#define PQSGMII_TX_DRIVER_220MV 0x4 ++#define PQSGMII_TX_DRIVER_240MV 0x5 ++#define PQSGMII_TX_DRIVER_260MV 0x6 ++#define PQSGMII_TX_DRIVER_280MV 0x7 ++#define PQSGMII_TX_DRIVER_300MV 0x8 ++#define PQSGMII_TX_DRIVER_320MV 0x9 ++#define PQSGMII_TX_DRIVER_400MV 0xa ++#define PQSGMII_TX_DRIVER_500MV 0xb ++#define PQSGMII_TX_DRIVER_600MV 0xc ++#define PQSGMII_MODE_CTRL 0x6d ++#define PQSGMII_MODE_CTRL_AZ_WORKAROUND_MASK BIT(0) ++#define PQSGMII_MMD3_SERDES_CONTROL 0x805a ++ ++#define PHY_ID_QCA8072 0x004dd0b2 ++#define PHY_ID_QCA8075 0x004dd0b1 ++ ++#define QCA807X_COMBO_ADDR_OFFSET 4 ++#define QCA807X_PQSGMII_ADDR_OFFSET 5 ++#define SERDES_RESET_SLEEP 100 ++ ++enum qca807x_global_phy { ++ QCA807X_COMBO_ADDR = 4, ++ QCA807X_PQSGMII_ADDR = 5, ++}; ++ ++struct qca807x_shared_priv { ++ unsigned int package_mode; ++ u32 tx_drive_strength; ++}; ++ ++struct qca807x_gpio_priv { ++ struct phy_device *phy; ++}; ++ ++struct qca807x_priv { ++ bool dac_full_amplitude; ++ bool dac_full_bias_current; ++ bool dac_disable_bias_current_tweak; ++}; ++ ++static int qca807x_cable_test_start(struct phy_device *phydev) ++{ ++ /* we do all the (time consuming) work later */ ++ return 0; ++} ++ ++#ifdef CONFIG_GPIOLIB ++static int qca807x_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) ++{ ++ return GPIO_LINE_DIRECTION_OUT; ++} ++ ++static int qca807x_gpio_get(struct gpio_chip *gc, unsigned int offset) ++{ ++ struct qca807x_gpio_priv *priv = gpiochip_get_data(gc); ++ u16 reg; ++ int val; ++ ++ reg = QCA807X_MMD7_LED_FORCE_CTRL(offset); ++ val = phy_read_mmd(priv->phy, MDIO_MMD_AN, reg); ++ ++ return FIELD_GET(QCA807X_GPIO_FORCE_MODE_MASK, val); ++} ++ ++static void qca807x_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) ++{ ++ struct qca807x_gpio_priv *priv = gpiochip_get_data(gc); ++ u16 reg; ++ int val; ++ ++ reg = QCA807X_MMD7_LED_FORCE_CTRL(offset); ++ ++ val = phy_read_mmd(priv->phy, MDIO_MMD_AN, reg); ++ val &= ~QCA807X_GPIO_FORCE_MODE_MASK; ++ val |= QCA807X_GPIO_FORCE_EN; ++ val |= FIELD_PREP(QCA807X_GPIO_FORCE_MODE_MASK, value); ++ ++ phy_write_mmd(priv->phy, MDIO_MMD_AN, reg, val); ++} ++ ++static int qca807x_gpio_dir_out(struct gpio_chip *gc, unsigned int offset, int value) ++{ ++ qca807x_gpio_set(gc, offset, value); ++ ++ return 0; ++} ++ ++static int qca807x_gpio(struct phy_device *phydev) ++{ ++ struct device *dev = &phydev->mdio.dev; ++ struct qca807x_gpio_priv *priv; ++ struct gpio_chip *gc; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->phy = phydev; ++ ++ gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL); ++ if (!gc) ++ return -ENOMEM; ++ ++ gc->label = dev_name(dev); ++ gc->base = -1; ++ gc->ngpio = 2; ++ gc->parent = dev; ++ gc->owner = THIS_MODULE; ++ gc->can_sleep = true; ++ gc->get_direction = qca807x_gpio_get_direction; ++ gc->direction_output = qca807x_gpio_dir_out; ++ gc->get = qca807x_gpio_get; ++ gc->set = qca807x_gpio_set; ++ ++ return devm_gpiochip_add_data(dev, gc, priv); ++} ++#endif ++ ++static int qca807x_read_fiber_status(struct phy_device *phydev) ++{ ++ bool changed; ++ int ss, err; ++ ++ err = genphy_c37_read_status(phydev, &changed); ++ if (err || !changed) ++ return err; ++ ++ /* Read the QCA807x PHY-Specific Status register fiber page, ++ * which indicates the speed and duplex that the PHY is actually ++ * using, irrespective of whether we are in autoneg mode or not. ++ */ ++ ss = phy_read(phydev, AT803X_SPECIFIC_STATUS); ++ if (ss < 0) ++ return ss; ++ ++ phydev->speed = SPEED_UNKNOWN; ++ phydev->duplex = DUPLEX_UNKNOWN; ++ if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) { ++ switch (FIELD_GET(AT803X_SS_SPEED_MASK, ss)) { ++ case AT803X_SS_SPEED_100: ++ phydev->speed = SPEED_100; ++ break; ++ case AT803X_SS_SPEED_1000: ++ phydev->speed = SPEED_1000; ++ break; ++ } ++ ++ if (ss & AT803X_SS_DUPLEX) ++ phydev->duplex = DUPLEX_FULL; ++ else ++ phydev->duplex = DUPLEX_HALF; ++ } ++ ++ return 0; ++} ++ ++static int qca807x_read_status(struct phy_device *phydev) ++{ ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported)) { ++ switch (phydev->port) { ++ case PORT_FIBRE: ++ return qca807x_read_fiber_status(phydev); ++ case PORT_TP: ++ return at803x_read_status(phydev); ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ return at803x_read_status(phydev); ++} ++ ++static int qca807x_phy_package_probe_once(struct phy_device *phydev) ++{ ++ struct phy_package_shared *shared = phydev->shared; ++ struct qca807x_shared_priv *priv = shared->priv; ++ unsigned int tx_drive_strength; ++ const char *package_mode_name; ++ ++ /* Default to 600mw if not defined */ ++ if (of_property_read_u32(shared->np, "qcom,tx-drive-strength-milliwatt", ++ &tx_drive_strength)) ++ tx_drive_strength = 600; ++ ++ switch (tx_drive_strength) { ++ case 140: ++ priv->tx_drive_strength = PQSGMII_TX_DRIVER_140MV; ++ break; ++ case 160: ++ priv->tx_drive_strength = PQSGMII_TX_DRIVER_160MV; ++ break; ++ case 180: ++ priv->tx_drive_strength = PQSGMII_TX_DRIVER_180MV; ++ break; ++ case 200: ++ priv->tx_drive_strength = PQSGMII_TX_DRIVER_200MV; ++ break; ++ case 220: ++ priv->tx_drive_strength = PQSGMII_TX_DRIVER_220MV; ++ break; ++ case 240: ++ priv->tx_drive_strength = PQSGMII_TX_DRIVER_240MV; ++ break; ++ case 260: ++ priv->tx_drive_strength = PQSGMII_TX_DRIVER_260MV; ++ break; ++ case 280: ++ priv->tx_drive_strength = PQSGMII_TX_DRIVER_280MV; ++ break; ++ case 300: ++ priv->tx_drive_strength = PQSGMII_TX_DRIVER_300MV; ++ break; ++ case 320: ++ priv->tx_drive_strength = PQSGMII_TX_DRIVER_320MV; ++ break; ++ case 400: ++ priv->tx_drive_strength = PQSGMII_TX_DRIVER_400MV; ++ break; ++ case 500: ++ priv->tx_drive_strength = PQSGMII_TX_DRIVER_500MV; ++ break; ++ case 600: ++ priv->tx_drive_strength = PQSGMII_TX_DRIVER_600MV; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ priv->package_mode = PHY_INTERFACE_MODE_NA; ++ if (!of_property_read_string(shared->np, "qcom,package-mode", ++ &package_mode_name)) { ++ if (!strcasecmp(package_mode_name, ++ phy_modes(PHY_INTERFACE_MODE_PSGMII))) ++ priv->package_mode = PHY_INTERFACE_MODE_PSGMII; ++ else if (!strcasecmp(package_mode_name, ++ phy_modes(PHY_INTERFACE_MODE_QSGMII))) ++ priv->package_mode = PHY_INTERFACE_MODE_QSGMII; ++ else ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int qca807x_phy_package_config_init_once(struct phy_device *phydev) ++{ ++ struct phy_package_shared *shared = phydev->shared; ++ struct qca807x_shared_priv *priv = shared->priv; ++ int val, ret; ++ ++ phy_lock_mdio_bus(phydev); ++ ++ /* Set correct PHY package mode */ ++ val = __phy_package_read(phydev, QCA807X_COMBO_ADDR, ++ QCA807X_CHIP_CONFIGURATION); ++ val &= ~QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK; ++ /* package_mode can be QSGMII or PSGMII and we validate ++ * this in probe_once. ++ * With package_mode to NA, we default to PSGMII. ++ */ ++ switch (priv->package_mode) { ++ case PHY_INTERFACE_MODE_QSGMII: ++ val |= QCA807X_CHIP_CONFIGURATION_MODE_QSGMII_SGMII; ++ break; ++ case PHY_INTERFACE_MODE_PSGMII: ++ default: ++ val |= QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_ALL_COPPER; ++ } ++ ret = __phy_package_write(phydev, QCA807X_COMBO_ADDR, ++ QCA807X_CHIP_CONFIGURATION, val); ++ if (ret) ++ goto exit; ++ ++ /* After mode change Serdes reset is required */ ++ val = __phy_package_read(phydev, QCA807X_PQSGMII_ADDR, ++ PQSGMII_CTRL_REG); ++ val &= ~PQSGMII_ANALOG_SW_RESET; ++ ret = __phy_package_write(phydev, QCA807X_PQSGMII_ADDR, ++ PQSGMII_CTRL_REG, val); ++ if (ret) ++ goto exit; ++ ++ msleep(SERDES_RESET_SLEEP); ++ ++ val = __phy_package_read(phydev, QCA807X_PQSGMII_ADDR, ++ PQSGMII_CTRL_REG); ++ val |= PQSGMII_ANALOG_SW_RESET; ++ ret = __phy_package_write(phydev, QCA807X_PQSGMII_ADDR, ++ PQSGMII_CTRL_REG, val); ++ if (ret) ++ goto exit; ++ ++ /* Workaround to enable AZ transmitting ability */ ++ val = __phy_package_read_mmd(phydev, QCA807X_PQSGMII_ADDR, ++ MDIO_MMD_PMAPMD, PQSGMII_MODE_CTRL); ++ val &= ~PQSGMII_MODE_CTRL_AZ_WORKAROUND_MASK; ++ ret = __phy_package_write_mmd(phydev, QCA807X_PQSGMII_ADDR, ++ MDIO_MMD_PMAPMD, PQSGMII_MODE_CTRL, val); ++ if (ret) ++ goto exit; ++ ++ /* Set PQSGMII TX AMP strength */ ++ val = __phy_package_read(phydev, QCA807X_PQSGMII_ADDR, ++ PQSGMII_DRIVE_CONTROL_1); ++ val &= ~PQSGMII_TX_DRIVER_MASK; ++ val |= FIELD_PREP(PQSGMII_TX_DRIVER_MASK, priv->tx_drive_strength); ++ ret = __phy_package_write(phydev, QCA807X_PQSGMII_ADDR, ++ PQSGMII_DRIVE_CONTROL_1, val); ++ if (ret) ++ goto exit; ++ ++ /* Prevent PSGMII going into hibernation via PSGMII self test */ ++ val = __phy_package_read_mmd(phydev, QCA807X_COMBO_ADDR, ++ MDIO_MMD_PCS, PQSGMII_MMD3_SERDES_CONTROL); ++ val &= ~BIT(1); ++ ret = __phy_package_write_mmd(phydev, QCA807X_COMBO_ADDR, ++ MDIO_MMD_PCS, PQSGMII_MMD3_SERDES_CONTROL, val); ++ ++exit: ++ phy_unlock_mdio_bus(phydev); ++ ++ return ret; ++} ++ ++static int qca807x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) ++{ ++ struct phy_device *phydev = upstream; ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, }; ++ phy_interface_t iface; ++ int ret; ++ DECLARE_PHY_INTERFACE_MASK(interfaces); ++ ++ sfp_parse_support(phydev->sfp_bus, id, support, interfaces); ++ iface = sfp_select_interface(phydev->sfp_bus, support); ++ ++ dev_info(&phydev->mdio.dev, "%s SFP module inserted\n", phy_modes(iface)); ++ ++ switch (iface) { ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_100BASEX: ++ /* Set PHY mode to PSGMII combo (1/4 copper + combo ports) mode */ ++ ret = phy_modify(phydev, ++ QCA807X_CHIP_CONFIGURATION, ++ QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK, ++ QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER); ++ /* Enable fiber mode autodection (1000Base-X or 100Base-FX) */ ++ ret = phy_set_bits_mmd(phydev, ++ MDIO_MMD_AN, ++ QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION, ++ QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN); ++ /* Select fiber page */ ++ ret = phy_clear_bits(phydev, ++ QCA807X_CHIP_CONFIGURATION, ++ QCA807X_BT_BX_REG_SEL); ++ ++ phydev->port = PORT_FIBRE; ++ break; ++ default: ++ dev_err(&phydev->mdio.dev, "Incompatible SFP module inserted\n"); ++ return -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static void qca807x_sfp_remove(void *upstream) ++{ ++ struct phy_device *phydev = upstream; ++ ++ /* Select copper page */ ++ phy_set_bits(phydev, ++ QCA807X_CHIP_CONFIGURATION, ++ QCA807X_BT_BX_REG_SEL); ++ ++ phydev->port = PORT_TP; ++} ++ ++static const struct sfp_upstream_ops qca807x_sfp_ops = { ++ .attach = phy_sfp_attach, ++ .detach = phy_sfp_detach, ++ .module_insert = qca807x_sfp_insert, ++ .module_remove = qca807x_sfp_remove, ++}; ++ ++static int qca807x_probe(struct phy_device *phydev) ++{ ++ struct device_node *node = phydev->mdio.dev.of_node; ++ struct qca807x_shared_priv *shared_priv; ++ struct device *dev = &phydev->mdio.dev; ++ struct phy_package_shared *shared; ++ struct qca807x_priv *priv; ++ int ret; ++ ++ ret = devm_of_phy_package_join(dev, phydev, sizeof(*shared_priv)); ++ if (ret) ++ return ret; ++ ++ if (phy_package_probe_once(phydev)) { ++ ret = qca807x_phy_package_probe_once(phydev); ++ if (ret) ++ return ret; ++ } ++ ++ shared = phydev->shared; ++ shared_priv = shared->priv; ++ ++ /* Make sure PHY follow PHY package mode if enforced */ ++ if (shared_priv->package_mode != PHY_INTERFACE_MODE_NA && ++ phydev->interface != shared_priv->package_mode) ++ return -EINVAL; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->dac_full_amplitude = of_property_read_bool(node, "qcom,dac-full-amplitude"); ++ priv->dac_full_bias_current = of_property_read_bool(node, "qcom,dac-full-bias-current"); ++ priv->dac_disable_bias_current_tweak = of_property_read_bool(node, ++ "qcom,dac-disable-bias-current-tweak"); ++ ++ if (IS_ENABLED(CONFIG_GPIOLIB)) { ++ /* Do not register a GPIO controller unless flagged for it */ ++ if (of_property_read_bool(node, "gpio-controller")) { ++ ret = qca807x_gpio(phydev); ++ if (ret) ++ return ret; ++ } ++ } ++ ++ /* Attach SFP bus on combo port*/ ++ if (phy_read(phydev, QCA807X_CHIP_CONFIGURATION)) { ++ ret = phy_sfp_probe(phydev, &qca807x_sfp_ops); ++ if (ret) ++ return ret; ++ linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported); ++ linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->advertising); ++ } ++ ++ phydev->priv = priv; ++ ++ return 0; ++} ++ ++static int qca807x_config_init(struct phy_device *phydev) ++{ ++ struct qca807x_priv *priv = phydev->priv; ++ u16 control_dac; ++ int ret; ++ ++ if (phy_package_init_once(phydev)) { ++ ret = qca807x_phy_package_config_init_once(phydev); ++ if (ret) ++ return ret; ++ } ++ ++ control_dac = phy_read_mmd(phydev, MDIO_MMD_AN, ++ QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH); ++ control_dac &= ~QCA807X_CONTROL_DAC_MASK; ++ if (!priv->dac_full_amplitude) ++ control_dac |= QCA807X_CONTROL_DAC_DSP_AMPLITUDE; ++ if (!priv->dac_full_amplitude) ++ control_dac |= QCA807X_CONTROL_DAC_DSP_BIAS_CURRENT; ++ if (!priv->dac_disable_bias_current_tweak) ++ control_dac |= QCA807X_CONTROL_DAC_BIAS_CURRENT_TWEAK; ++ return phy_write_mmd(phydev, MDIO_MMD_AN, ++ QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH, ++ control_dac); ++} ++ ++static struct phy_driver qca807x_drivers[] = { ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_QCA8072), ++ .name = "Qualcomm QCA8072", ++ .flags = PHY_POLL_CABLE_TEST, ++ /* PHY_GBIT_FEATURES */ ++ .probe = qca807x_probe, ++ .config_init = qca807x_config_init, ++ .read_status = qca807x_read_status, ++ .config_intr = at803x_config_intr, ++ .handle_interrupt = at803x_handle_interrupt, ++ .soft_reset = genphy_soft_reset, ++ .get_tunable = at803x_get_tunable, ++ .set_tunable = at803x_set_tunable, ++ .resume = genphy_resume, ++ .suspend = genphy_suspend, ++ .cable_test_start = qca807x_cable_test_start, ++ .cable_test_get_status = qca808x_cable_test_get_status, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_QCA8075), ++ .name = "Qualcomm QCA8075", ++ .flags = PHY_POLL_CABLE_TEST, ++ /* PHY_GBIT_FEATURES */ ++ .probe = qca807x_probe, ++ .config_init = qca807x_config_init, ++ .read_status = qca807x_read_status, ++ .config_intr = at803x_config_intr, ++ .handle_interrupt = at803x_handle_interrupt, ++ .soft_reset = genphy_soft_reset, ++ .get_tunable = at803x_get_tunable, ++ .set_tunable = at803x_set_tunable, ++ .resume = genphy_resume, ++ .suspend = genphy_suspend, ++ .cable_test_start = qca807x_cable_test_start, ++ .cable_test_get_status = qca808x_cable_test_get_status, ++ }, ++}; ++module_phy_driver(qca807x_drivers); ++ ++static struct mdio_device_id __maybe_unused qca807x_tbl[] = { ++ { PHY_ID_MATCH_EXACT(PHY_ID_QCA8072) }, ++ { PHY_ID_MATCH_EXACT(PHY_ID_QCA8075) }, ++ { } ++}; ++ ++MODULE_AUTHOR("Robert Marko "); ++MODULE_AUTHOR("Christian Marangi "); ++MODULE_DESCRIPTION("Qualcomm QCA807x PHY driver"); ++MODULE_DEVICE_TABLE(mdio, qca807x_tbl); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/backport-6.6/716-v6.9-08-net-phy-qcom-move-common-qca808x-LED-define-to-share.patch b/target/linux/generic/backport-6.6/716-v6.9-08-net-phy-qcom-move-common-qca808x-LED-define-to-share.patch new file mode 100644 index 0000000000..cf4d74e8c5 --- /dev/null +++ b/target/linux/generic/backport-6.6/716-v6.9-08-net-phy-qcom-move-common-qca808x-LED-define-to-share.patch @@ -0,0 +1,179 @@ +From ee9d9807bee0e6af8ca2a4db6f0d1dc0e5b41f44 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 6 Feb 2024 18:31:11 +0100 +Subject: [PATCH 08/10] net: phy: qcom: move common qca808x LED define to + shared header + +The LED implementation of qca808x and qca807x is the same but qca807x +supports also Fiber port and have different hw control bits for Fiber +port. + +In preparation for qca807x introduction, move all the common define to +shared header. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/qcom/qca808x.c | 65 ---------------------------------- + drivers/net/phy/qcom/qcom.h | 65 ++++++++++++++++++++++++++++++++++ + 2 files changed, 65 insertions(+), 65 deletions(-) + +--- a/drivers/net/phy/qcom/qca808x.c ++++ b/drivers/net/phy/qcom/qca808x.c +@@ -62,29 +62,6 @@ + #define QCA808X_DBG_AN_TEST 0xb + #define QCA808X_HIBERNATION_EN BIT(15) + +-#define QCA808X_MMD7_LED_GLOBAL 0x8073 +-#define QCA808X_LED_BLINK_1 GENMASK(11, 6) +-#define QCA808X_LED_BLINK_2 GENMASK(5, 0) +-/* Values are the same for both BLINK_1 and BLINK_2 */ +-#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3) +-#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0) +-#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1) +-#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2) +-#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3) +-#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4) +-#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5) +-#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6) +-#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7) +-#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0) +-#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0) +-#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1) +-#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2) +-#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3) +-#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4) +-#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5) +-#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6) +-#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7) +- + #define QCA808X_MMD7_LED2_CTRL 0x8074 + #define QCA808X_MMD7_LED2_FORCE_CTRL 0x8075 + #define QCA808X_MMD7_LED1_CTRL 0x8076 +@@ -92,51 +69,9 @@ + #define QCA808X_MMD7_LED0_CTRL 0x8078 + #define QCA808X_MMD7_LED_CTRL(x) (0x8078 - ((x) * 2)) + +-/* LED hw control pattern is the same for every LED */ +-#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0) +-#define QCA808X_LED_SPEED2500_ON BIT(15) +-#define QCA808X_LED_SPEED2500_BLINK BIT(14) +-/* Follow blink trigger even if duplex or speed condition doesn't match */ +-#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13) +-#define QCA808X_LED_FULL_DUPLEX_ON BIT(12) +-#define QCA808X_LED_HALF_DUPLEX_ON BIT(11) +-#define QCA808X_LED_TX_BLINK BIT(10) +-#define QCA808X_LED_RX_BLINK BIT(9) +-#define QCA808X_LED_TX_ON_10MS BIT(8) +-#define QCA808X_LED_RX_ON_10MS BIT(7) +-#define QCA808X_LED_SPEED1000_ON BIT(6) +-#define QCA808X_LED_SPEED100_ON BIT(5) +-#define QCA808X_LED_SPEED10_ON BIT(4) +-#define QCA808X_LED_COLLISION_BLINK BIT(3) +-#define QCA808X_LED_SPEED1000_BLINK BIT(2) +-#define QCA808X_LED_SPEED100_BLINK BIT(1) +-#define QCA808X_LED_SPEED10_BLINK BIT(0) +- + #define QCA808X_MMD7_LED0_FORCE_CTRL 0x8079 + #define QCA808X_MMD7_LED_FORCE_CTRL(x) (0x8079 - ((x) * 2)) + +-/* LED force ctrl is the same for every LED +- * No documentation exist for this, not even internal one +- * with NDA as QCOM gives only info about configuring +- * hw control pattern rules and doesn't indicate any way +- * to force the LED to specific mode. +- * These define comes from reverse and testing and maybe +- * lack of some info or some info are not entirely correct. +- * For the basic LED control and hw control these finding +- * are enough to support LED control in all the required APIs. +- * +- * On doing some comparison with implementation with qca807x, +- * it was found that it's 1:1 equal to it and confirms all the +- * reverse done. It was also found further specification with the +- * force mode and the blink modes. +- */ +-#define QCA808X_LED_FORCE_EN BIT(15) +-#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13) +-#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3) +-#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2) +-#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1) +-#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0) +- + #define QCA808X_MMD7_LED_POLARITY_CTRL 0x901a + /* QSDK sets by default 0x46 to this reg that sets BIT 6 for + * LED to active high. It's not clear what BIT 3 and BIT 4 does. +--- a/drivers/net/phy/qcom/qcom.h ++++ b/drivers/net/phy/qcom/qcom.h +@@ -103,6 +103,71 @@ + /* Added for reference of existence but should be handled by wait_for_completion already */ + #define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3)) + ++#define QCA808X_MMD7_LED_GLOBAL 0x8073 ++#define QCA808X_LED_BLINK_1 GENMASK(11, 6) ++#define QCA808X_LED_BLINK_2 GENMASK(5, 0) ++/* Values are the same for both BLINK_1 and BLINK_2 */ ++#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3) ++#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0) ++#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1) ++#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2) ++#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3) ++#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4) ++#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5) ++#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6) ++#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7) ++#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0) ++#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0) ++#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1) ++#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2) ++#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3) ++#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4) ++#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5) ++#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6) ++#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7) ++ ++/* LED hw control pattern is the same for every LED */ ++#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0) ++#define QCA808X_LED_SPEED2500_ON BIT(15) ++#define QCA808X_LED_SPEED2500_BLINK BIT(14) ++/* Follow blink trigger even if duplex or speed condition doesn't match */ ++#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13) ++#define QCA808X_LED_FULL_DUPLEX_ON BIT(12) ++#define QCA808X_LED_HALF_DUPLEX_ON BIT(11) ++#define QCA808X_LED_TX_BLINK BIT(10) ++#define QCA808X_LED_RX_BLINK BIT(9) ++#define QCA808X_LED_TX_ON_10MS BIT(8) ++#define QCA808X_LED_RX_ON_10MS BIT(7) ++#define QCA808X_LED_SPEED1000_ON BIT(6) ++#define QCA808X_LED_SPEED100_ON BIT(5) ++#define QCA808X_LED_SPEED10_ON BIT(4) ++#define QCA808X_LED_COLLISION_BLINK BIT(3) ++#define QCA808X_LED_SPEED1000_BLINK BIT(2) ++#define QCA808X_LED_SPEED100_BLINK BIT(1) ++#define QCA808X_LED_SPEED10_BLINK BIT(0) ++ ++/* LED force ctrl is the same for every LED ++ * No documentation exist for this, not even internal one ++ * with NDA as QCOM gives only info about configuring ++ * hw control pattern rules and doesn't indicate any way ++ * to force the LED to specific mode. ++ * These define comes from reverse and testing and maybe ++ * lack of some info or some info are not entirely correct. ++ * For the basic LED control and hw control these finding ++ * are enough to support LED control in all the required APIs. ++ * ++ * On doing some comparison with implementation with qca807x, ++ * it was found that it's 1:1 equal to it and confirms all the ++ * reverse done. It was also found further specification with the ++ * force mode and the blink modes. ++ */ ++#define QCA808X_LED_FORCE_EN BIT(15) ++#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13) ++#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3) ++#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2) ++#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1) ++#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0) ++ + #define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C + #define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B + #define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A diff --git a/target/linux/generic/backport-6.6/716-v6.9-09-net-phy-qcom-generalize-some-qca808x-LED-functions.patch b/target/linux/generic/backport-6.6/716-v6.9-09-net-phy-qcom-generalize-some-qca808x-LED-functions.patch new file mode 100644 index 0000000000..da73c1d3b8 --- /dev/null +++ b/target/linux/generic/backport-6.6/716-v6.9-09-net-phy-qcom-generalize-some-qca808x-LED-functions.patch @@ -0,0 +1,172 @@ +From 47b930d0dd437af927145dba50a2e2ea1ba97c67 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 6 Feb 2024 18:31:12 +0100 +Subject: [PATCH 09/10] net: phy: qcom: generalize some qca808x LED functions + +Generalize some qca808x LED functions in preparation for qca807x LED +support. + +The LED implementation of qca808x and qca807x is the same but qca807x +supports also Fiber port and have different hw control bits for Fiber +port. To limit code duplication introduce micro functions that takes reg +instead of LED index to tweak all the supported LED modes. + +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/phy/qcom/qca808x.c | 38 +++----------------- + drivers/net/phy/qcom/qcom-phy-lib.c | 54 +++++++++++++++++++++++++++++ + drivers/net/phy/qcom/qcom.h | 7 ++++ + 3 files changed, 65 insertions(+), 34 deletions(-) + +--- a/drivers/net/phy/qcom/qca808x.c ++++ b/drivers/net/phy/qcom/qca808x.c +@@ -437,9 +437,7 @@ static int qca808x_led_hw_control_enable + return -EINVAL; + + reg = QCA808X_MMD7_LED_FORCE_CTRL(index); +- +- return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, +- QCA808X_LED_FORCE_EN); ++ return qca808x_led_reg_hw_control_enable(phydev, reg); + } + + static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index, +@@ -480,16 +478,12 @@ static int qca808x_led_hw_control_set(st + static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index) + { + u16 reg; +- int val; + + if (index > 2) + return false; + + reg = QCA808X_MMD7_LED_FORCE_CTRL(index); +- +- val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); +- +- return !(val & QCA808X_LED_FORCE_EN); ++ return qca808x_led_reg_hw_control_status(phydev, reg); + } + + static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index, +@@ -557,44 +551,20 @@ static int qca808x_led_brightness_set(st + } + + reg = QCA808X_MMD7_LED_FORCE_CTRL(index); +- +- return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, +- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, +- QCA808X_LED_FORCE_EN | (value ? QCA808X_LED_FORCE_ON : +- QCA808X_LED_FORCE_OFF)); ++ return qca808x_led_reg_brightness_set(phydev, reg, value); + } + + static int qca808x_led_blink_set(struct phy_device *phydev, u8 index, + unsigned long *delay_on, + unsigned long *delay_off) + { +- int ret; + u16 reg; + + if (index > 2) + return -EINVAL; + + reg = QCA808X_MMD7_LED_FORCE_CTRL(index); +- +- /* Set blink to 50% off, 50% on at 4Hz by default */ +- ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL, +- QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK, +- QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50); +- if (ret) +- return ret; +- +- /* We use BLINK_1 for normal blinking */ +- ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg, +- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, +- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1); +- if (ret) +- return ret; +- +- /* We set blink to 4Hz, aka 250ms */ +- *delay_on = 250 / 2; +- *delay_off = 250 / 2; +- +- return 0; ++ return qca808x_led_reg_blink_set(phydev, reg, delay_on, delay_off); + } + + static int qca808x_led_polarity_set(struct phy_device *phydev, int index, +--- a/drivers/net/phy/qcom/qcom-phy-lib.c ++++ b/drivers/net/phy/qcom/qcom-phy-lib.c +@@ -620,3 +620,57 @@ int qca808x_cable_test_get_status(struct + return 0; + } + EXPORT_SYMBOL_GPL(qca808x_cable_test_get_status); ++ ++int qca808x_led_reg_hw_control_enable(struct phy_device *phydev, u16 reg) ++{ ++ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_FORCE_EN); ++} ++EXPORT_SYMBOL_GPL(qca808x_led_reg_hw_control_enable); ++ ++bool qca808x_led_reg_hw_control_status(struct phy_device *phydev, u16 reg) ++{ ++ int val; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); ++ return !(val & QCA808X_LED_FORCE_EN); ++} ++EXPORT_SYMBOL_GPL(qca808x_led_reg_hw_control_status); ++ ++int qca808x_led_reg_brightness_set(struct phy_device *phydev, ++ u16 reg, enum led_brightness value) ++{ ++ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, ++ QCA808X_LED_FORCE_EN | (value ? QCA808X_LED_FORCE_ON : ++ QCA808X_LED_FORCE_OFF)); ++} ++EXPORT_SYMBOL_GPL(qca808x_led_reg_brightness_set); ++ ++int qca808x_led_reg_blink_set(struct phy_device *phydev, u16 reg, ++ unsigned long *delay_on, ++ unsigned long *delay_off) ++{ ++ int ret; ++ ++ /* Set blink to 50% off, 50% on at 4Hz by default */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL, ++ QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK, ++ QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50); ++ if (ret) ++ return ret; ++ ++ /* We use BLINK_1 for normal blinking */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, ++ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1); ++ if (ret) ++ return ret; ++ ++ /* We set blink to 4Hz, aka 250ms */ ++ *delay_on = 250 / 2; ++ *delay_off = 250 / 2; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(qca808x_led_reg_blink_set); +--- a/drivers/net/phy/qcom/qcom.h ++++ b/drivers/net/phy/qcom/qcom.h +@@ -234,3 +234,10 @@ int at803x_cdt_start(struct phy_device * + int at803x_cdt_wait_for_completion(struct phy_device *phydev, + u32 cdt_en); + int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished); ++int qca808x_led_reg_hw_control_enable(struct phy_device *phydev, u16 reg); ++bool qca808x_led_reg_hw_control_status(struct phy_device *phydev, u16 reg); ++int qca808x_led_reg_brightness_set(struct phy_device *phydev, ++ u16 reg, enum led_brightness value); ++int qca808x_led_reg_blink_set(struct phy_device *phydev, u16 reg, ++ unsigned long *delay_on, ++ unsigned long *delay_off); diff --git a/target/linux/generic/backport-6.6/716-v6.9-10-net-phy-qca807x-add-support-for-configurable-LED.patch b/target/linux/generic/backport-6.6/716-v6.9-10-net-phy-qca807x-add-support-for-configurable-LED.patch new file mode 100644 index 0000000000..3bd36f6ffe --- /dev/null +++ b/target/linux/generic/backport-6.6/716-v6.9-10-net-phy-qca807x-add-support-for-configurable-LED.patch @@ -0,0 +1,326 @@ +From f508a226b517a6a8afd78a317de46bc83e3e3d51 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 6 Feb 2024 18:31:13 +0100 +Subject: [PATCH 10/10] net: phy: qca807x: add support for configurable LED + +QCA8072/5 have up to 2 LEDs attached for PHY. + +LEDs can be configured to be ON/hw blink or be set to HW control. + +Hw blink mode is set to blink at 4Hz or 250ms. + +PHY can support both copper (TP) or fiber (FIBRE) kind and supports +different HW control modes based on the port type. + +HW control modes supported for netdev trigger for copper ports are: +- LINK_10 +- LINK_100 +- LINK_1000 +- TX +- RX +- FULL_DUPLEX +- HALF_DUPLEX + +HW control modes supported for netdev trigger for fiber ports are: +- LINK_100 +- LINK_1000 +- TX +- RX +- FULL_DUPLEX +- HALF_DUPLEX + +LED support conflicts with GPIO controller feature and must be disabled +if gpio-controller is used for the PHY. + +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/phy/qcom/qca807x.c | 256 ++++++++++++++++++++++++++++++++- + 1 file changed, 254 insertions(+), 2 deletions(-) + +--- a/drivers/net/phy/qcom/qca807x.c ++++ b/drivers/net/phy/qcom/qca807x.c +@@ -57,8 +57,18 @@ + #define QCA807X_MMD7_LED_CTRL(x) (0x8074 + ((x) * 2)) + #define QCA807X_MMD7_LED_FORCE_CTRL(x) (0x8075 + ((x) * 2)) + +-#define QCA807X_GPIO_FORCE_EN BIT(15) +-#define QCA807X_GPIO_FORCE_MODE_MASK GENMASK(14, 13) ++/* LED hw control pattern for fiber port */ ++#define QCA807X_LED_FIBER_PATTERN_MASK GENMASK(11, 1) ++#define QCA807X_LED_FIBER_TXACT_BLK_EN BIT(10) ++#define QCA807X_LED_FIBER_RXACT_BLK_EN BIT(9) ++#define QCA807X_LED_FIBER_FDX_ON_EN BIT(6) ++#define QCA807X_LED_FIBER_HDX_ON_EN BIT(5) ++#define QCA807X_LED_FIBER_1000BX_ON_EN BIT(2) ++#define QCA807X_LED_FIBER_100FX_ON_EN BIT(1) ++ ++/* Some device repurpose the LED as GPIO out */ ++#define QCA807X_GPIO_FORCE_EN QCA808X_LED_FORCE_EN ++#define QCA807X_GPIO_FORCE_MODE_MASK QCA808X_LED_FORCE_MODE_MASK + + #define QCA807X_FUNCTION_CONTROL 0x10 + #define QCA807X_FC_MDI_CROSSOVER_MODE_MASK GENMASK(6, 5) +@@ -121,6 +131,233 @@ static int qca807x_cable_test_start(stru + return 0; + } + ++static int qca807x_led_parse_netdev(struct phy_device *phydev, unsigned long rules, ++ u16 *offload_trigger) ++{ ++ /* Parsing specific to netdev trigger */ ++ switch (phydev->port) { ++ case PORT_TP: ++ if (test_bit(TRIGGER_NETDEV_TX, &rules)) ++ *offload_trigger |= QCA808X_LED_TX_BLINK; ++ if (test_bit(TRIGGER_NETDEV_RX, &rules)) ++ *offload_trigger |= QCA808X_LED_RX_BLINK; ++ if (test_bit(TRIGGER_NETDEV_LINK_10, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED10_ON; ++ if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED100_ON; ++ if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED1000_ON; ++ if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules)) ++ *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON; ++ if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules)) ++ *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON; ++ break; ++ case PORT_FIBRE: ++ if (test_bit(TRIGGER_NETDEV_TX, &rules)) ++ *offload_trigger |= QCA807X_LED_FIBER_TXACT_BLK_EN; ++ if (test_bit(TRIGGER_NETDEV_RX, &rules)) ++ *offload_trigger |= QCA807X_LED_FIBER_RXACT_BLK_EN; ++ if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) ++ *offload_trigger |= QCA807X_LED_FIBER_100FX_ON_EN; ++ if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) ++ *offload_trigger |= QCA807X_LED_FIBER_1000BX_ON_EN; ++ if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules)) ++ *offload_trigger |= QCA807X_LED_FIBER_HDX_ON_EN; ++ if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules)) ++ *offload_trigger |= QCA807X_LED_FIBER_FDX_ON_EN; ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ if (rules && !*offload_trigger) ++ return -EOPNOTSUPP; ++ ++ return 0; ++} ++ ++static int qca807x_led_hw_control_enable(struct phy_device *phydev, u8 index) ++{ ++ u16 reg; ++ ++ if (index > 1) ++ return -EINVAL; ++ ++ reg = QCA807X_MMD7_LED_FORCE_CTRL(index); ++ return qca808x_led_reg_hw_control_enable(phydev, reg); ++} ++ ++static int qca807x_led_hw_is_supported(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ u16 offload_trigger = 0; ++ ++ if (index > 1) ++ return -EINVAL; ++ ++ return qca807x_led_parse_netdev(phydev, rules, &offload_trigger); ++} ++ ++static int qca807x_led_hw_control_set(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ u16 reg, mask, offload_trigger = 0; ++ int ret; ++ ++ if (index > 1) ++ return -EINVAL; ++ ++ ret = qca807x_led_parse_netdev(phydev, rules, &offload_trigger); ++ if (ret) ++ return ret; ++ ++ ret = qca807x_led_hw_control_enable(phydev, index); ++ if (ret) ++ return ret; ++ ++ switch (phydev->port) { ++ case PORT_TP: ++ reg = QCA807X_MMD7_LED_CTRL(index); ++ mask = QCA808X_LED_PATTERN_MASK; ++ break; ++ case PORT_FIBRE: ++ /* HW control pattern bits are in LED FORCE reg */ ++ reg = QCA807X_MMD7_LED_FORCE_CTRL(index); ++ mask = QCA807X_LED_FIBER_PATTERN_MASK; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, mask, ++ offload_trigger); ++} ++ ++static bool qca807x_led_hw_control_status(struct phy_device *phydev, u8 index) ++{ ++ u16 reg; ++ ++ if (index > 1) ++ return false; ++ ++ reg = QCA807X_MMD7_LED_FORCE_CTRL(index); ++ return qca808x_led_reg_hw_control_status(phydev, reg); ++} ++ ++static int qca807x_led_hw_control_get(struct phy_device *phydev, u8 index, ++ unsigned long *rules) ++{ ++ u16 reg; ++ int val; ++ ++ if (index > 1) ++ return -EINVAL; ++ ++ /* Check if we have hw control enabled */ ++ if (qca807x_led_hw_control_status(phydev, index)) ++ return -EINVAL; ++ ++ /* Parsing specific to netdev trigger */ ++ switch (phydev->port) { ++ case PORT_TP: ++ reg = QCA807X_MMD7_LED_CTRL(index); ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); ++ if (val & QCA808X_LED_TX_BLINK) ++ set_bit(TRIGGER_NETDEV_TX, rules); ++ if (val & QCA808X_LED_RX_BLINK) ++ set_bit(TRIGGER_NETDEV_RX, rules); ++ if (val & QCA808X_LED_SPEED10_ON) ++ set_bit(TRIGGER_NETDEV_LINK_10, rules); ++ if (val & QCA808X_LED_SPEED100_ON) ++ set_bit(TRIGGER_NETDEV_LINK_100, rules); ++ if (val & QCA808X_LED_SPEED1000_ON) ++ set_bit(TRIGGER_NETDEV_LINK_1000, rules); ++ if (val & QCA808X_LED_HALF_DUPLEX_ON) ++ set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules); ++ if (val & QCA808X_LED_FULL_DUPLEX_ON) ++ set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules); ++ break; ++ case PORT_FIBRE: ++ /* HW control pattern bits are in LED FORCE reg */ ++ reg = QCA807X_MMD7_LED_FORCE_CTRL(index); ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); ++ if (val & QCA807X_LED_FIBER_TXACT_BLK_EN) ++ set_bit(TRIGGER_NETDEV_TX, rules); ++ if (val & QCA807X_LED_FIBER_RXACT_BLK_EN) ++ set_bit(TRIGGER_NETDEV_RX, rules); ++ if (val & QCA807X_LED_FIBER_100FX_ON_EN) ++ set_bit(TRIGGER_NETDEV_LINK_100, rules); ++ if (val & QCA807X_LED_FIBER_1000BX_ON_EN) ++ set_bit(TRIGGER_NETDEV_LINK_1000, rules); ++ if (val & QCA807X_LED_FIBER_HDX_ON_EN) ++ set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules); ++ if (val & QCA807X_LED_FIBER_FDX_ON_EN) ++ set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int qca807x_led_hw_control_reset(struct phy_device *phydev, u8 index) ++{ ++ u16 reg, mask; ++ ++ if (index > 1) ++ return -EINVAL; ++ ++ switch (phydev->port) { ++ case PORT_TP: ++ reg = QCA807X_MMD7_LED_CTRL(index); ++ mask = QCA808X_LED_PATTERN_MASK; ++ break; ++ case PORT_FIBRE: ++ /* HW control pattern bits are in LED FORCE reg */ ++ reg = QCA807X_MMD7_LED_FORCE_CTRL(index); ++ mask = QCA807X_LED_FIBER_PATTERN_MASK; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, mask); ++} ++ ++static int qca807x_led_brightness_set(struct phy_device *phydev, ++ u8 index, enum led_brightness value) ++{ ++ u16 reg; ++ int ret; ++ ++ if (index > 1) ++ return -EINVAL; ++ ++ /* If we are setting off the LED reset any hw control rule */ ++ if (!value) { ++ ret = qca807x_led_hw_control_reset(phydev, index); ++ if (ret) ++ return ret; ++ } ++ ++ reg = QCA807X_MMD7_LED_FORCE_CTRL(index); ++ return qca808x_led_reg_brightness_set(phydev, reg, value); ++} ++ ++static int qca807x_led_blink_set(struct phy_device *phydev, u8 index, ++ unsigned long *delay_on, ++ unsigned long *delay_off) ++{ ++ u16 reg; ++ ++ if (index > 1) ++ return -EINVAL; ++ ++ reg = QCA807X_MMD7_LED_FORCE_CTRL(index); ++ return qca808x_led_reg_blink_set(phydev, reg, delay_on, delay_off); ++} ++ + #ifdef CONFIG_GPIOLIB + static int qca807x_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) + { +@@ -496,6 +733,16 @@ static int qca807x_probe(struct phy_devi + "qcom,dac-disable-bias-current-tweak"); + + if (IS_ENABLED(CONFIG_GPIOLIB)) { ++ /* Make sure we don't have mixed leds node and gpio-controller ++ * to prevent registering leds and having gpio-controller usage ++ * conflicting with them. ++ */ ++ if (of_find_property(node, "leds", NULL) && ++ of_find_property(node, "gpio-controller", NULL)) { ++ phydev_err(phydev, "Invalid property detected. LEDs and gpio-controller are mutually exclusive."); ++ return -EINVAL; ++ } ++ + /* Do not register a GPIO controller unless flagged for it */ + if (of_property_read_bool(node, "gpio-controller")) { + ret = qca807x_gpio(phydev); +@@ -580,6 +827,11 @@ static struct phy_driver qca807x_drivers + .suspend = genphy_suspend, + .cable_test_start = qca807x_cable_test_start, + .cable_test_get_status = qca808x_cable_test_get_status, ++ .led_brightness_set = qca807x_led_brightness_set, ++ .led_blink_set = qca807x_led_blink_set, ++ .led_hw_is_supported = qca807x_led_hw_is_supported, ++ .led_hw_control_set = qca807x_led_hw_control_set, ++ .led_hw_control_get = qca807x_led_hw_control_get, + }, + }; + module_phy_driver(qca807x_drivers); diff --git a/target/linux/generic/backport-6.6/717-v6.9-net-phy-qca807x-move-interface-mode-check-to-.config.patch b/target/linux/generic/backport-6.6/717-v6.9-net-phy-qca807x-move-interface-mode-check-to-.config.patch new file mode 100644 index 0000000000..53652c38fc --- /dev/null +++ b/target/linux/generic/backport-6.6/717-v6.9-net-phy-qca807x-move-interface-mode-check-to-.config.patch @@ -0,0 +1,51 @@ +From 3be0d950b62852a693182cb678948f481de02825 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Mon, 12 Feb 2024 12:49:34 +0100 +Subject: [PATCH] net: phy: qca807x: move interface mode check to + .config_init_once + +Currently, we are checking whether the PHY package mode matches the +individual PHY interface modes at PHY package probe time, but at that time +we only know the PHY package mode and not the individual PHY interface +modes as of_get_phy_mode() that populates it will only get called once the +netdev to which PHY-s are attached to is being probed and thus this check +will always fail and return -EINVAL. + +So, lets move this check to .config_init_once as at that point individual +PHY interface modes should be populated. + +Fixes: d1cb613efbd3 ("net: phy: qcom: add support for QCA807x PHY Family") +Signed-off-by: Robert Marko +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20240212115043.1725918-1-robimarko@gmail.com +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/qcom/qca807x.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +--- a/drivers/net/phy/qcom/qca807x.c ++++ b/drivers/net/phy/qcom/qca807x.c +@@ -562,6 +562,11 @@ static int qca807x_phy_package_config_in + struct qca807x_shared_priv *priv = shared->priv; + int val, ret; + ++ /* Make sure PHY follow PHY package mode if enforced */ ++ if (priv->package_mode != PHY_INTERFACE_MODE_NA && ++ phydev->interface != priv->package_mode) ++ return -EINVAL; ++ + phy_lock_mdio_bus(phydev); + + /* Set correct PHY package mode */ +@@ -718,11 +723,6 @@ static int qca807x_probe(struct phy_devi + shared = phydev->shared; + shared_priv = shared->priv; + +- /* Make sure PHY follow PHY package mode if enforced */ +- if (shared_priv->package_mode != PHY_INTERFACE_MODE_NA && +- phydev->interface != shared_priv->package_mode) +- return -EINVAL; +- + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; diff --git a/target/linux/generic/backport-6.6/720-v6.9-net-mdio-ipq4019-add-support-for-clock-frequency-pro.patch b/target/linux/generic/backport-6.6/720-v6.9-net-mdio-ipq4019-add-support-for-clock-frequency-pro.patch new file mode 100644 index 0000000000..e6a240dbda --- /dev/null +++ b/target/linux/generic/backport-6.6/720-v6.9-net-mdio-ipq4019-add-support-for-clock-frequency-pro.patch @@ -0,0 +1,205 @@ +From bdce82e960d1205d118662f575cec39379984e34 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 31 Jan 2024 03:26:04 +0100 +Subject: [PATCH] net: mdio: ipq4019: add support for clock-frequency property + +The IPQ4019 MDIO internally divide the clock feed by AHB based on the +MDIO_MODE reg. On reset or power up, the default value for the +divider is 0xff that reflect the divider set to /256. + +This makes the MDC run at a very low rate, that is, considering AHB is +always fixed to 100Mhz, a value of 390KHz. + +This hasn't have been a problem as MDIO wasn't used for time sensitive +operation, it is now that on IPQ807x is usually mounted with PHY that +requires MDIO to load their firmware (example Aquantia PHY). + +To handle this problem and permit to set the correct designed MDC +frequency for the SoC add support for the standard "clock-frequency" +property for the MDIO node. + +The divider supports value from /1 to /256 and the common value are to +set it to /16 to reflect 6.25Mhz or to /8 on newer platform to reflect +12.5Mhz. + +To scan if the requested rate is supported by the divider, loop with +each supported divider and stop when the requested rate match the final +rate with the current divider. An error is returned if the rate doesn't +match any value. + +On MDIO reset, the divider is restored to the requested value to prevent +any kind of downclocking caused by the divider reverting to a default +value. + +To follow 802.3 spec of 2.5MHz of default value, if divider is set at +/256 and "clock-frequency" is not set in DT, assume nobody set the +divider and try to find the closest MDC rate to 2.5MHz. (in the case of +AHB set to 100MHz, it's 1.5625MHz) + +While at is also document other bits of the MDIO_MODE reg to have a +clear idea of what is actually applied there. + +Documentation of some BITs is skipped as they are marked as reserved and +their usage is not clear (RES 11:9 GENPHY 16:13 RES1 19:17) + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/mdio/mdio-ipq4019.c | 109 ++++++++++++++++++++++++++++++-- + 1 file changed, 103 insertions(+), 6 deletions(-) + +--- a/drivers/net/mdio/mdio-ipq4019.c ++++ b/drivers/net/mdio/mdio-ipq4019.c +@@ -14,6 +14,20 @@ + #include + + #define MDIO_MODE_REG 0x40 ++#define MDIO_MODE_MDC_MODE BIT(12) ++/* 0 = Clause 22, 1 = Clause 45 */ ++#define MDIO_MODE_C45 BIT(8) ++#define MDIO_MODE_DIV_MASK GENMASK(7, 0) ++#define MDIO_MODE_DIV(x) FIELD_PREP(MDIO_MODE_DIV_MASK, (x) - 1) ++#define MDIO_MODE_DIV_1 0x0 ++#define MDIO_MODE_DIV_2 0x1 ++#define MDIO_MODE_DIV_4 0x3 ++#define MDIO_MODE_DIV_8 0x7 ++#define MDIO_MODE_DIV_16 0xf ++#define MDIO_MODE_DIV_32 0x1f ++#define MDIO_MODE_DIV_64 0x3f ++#define MDIO_MODE_DIV_128 0x7f ++#define MDIO_MODE_DIV_256 0xff + #define MDIO_ADDR_REG 0x44 + #define MDIO_DATA_WRITE_REG 0x48 + #define MDIO_DATA_READ_REG 0x4c +@@ -26,9 +40,6 @@ + #define MDIO_CMD_ACCESS_CODE_C45_WRITE 1 + #define MDIO_CMD_ACCESS_CODE_C45_READ 2 + +-/* 0 = Clause 22, 1 = Clause 45 */ +-#define MDIO_MODE_C45 BIT(8) +- + #define IPQ4019_MDIO_TIMEOUT 10000 + #define IPQ4019_MDIO_SLEEP 10 + +@@ -41,6 +52,7 @@ struct ipq4019_mdio_data { + void __iomem *membase; + void __iomem *eth_ldo_rdy; + struct clk *mdio_clk; ++ unsigned int mdc_rate; + }; + + static int ipq4019_mdio_wait_busy(struct mii_bus *bus) +@@ -179,6 +191,38 @@ static int ipq4019_mdio_write(struct mii + return 0; + } + ++static int ipq4019_mdio_set_div(struct ipq4019_mdio_data *priv) ++{ ++ unsigned long ahb_rate; ++ int div; ++ u32 val; ++ ++ /* If we don't have a clock for AHB use the fixed value */ ++ ahb_rate = IPQ_MDIO_CLK_RATE; ++ if (priv->mdio_clk) ++ ahb_rate = clk_get_rate(priv->mdio_clk); ++ ++ /* MDC rate is ahb_rate/(MDIO_MODE_DIV + 1) ++ * While supported, internal documentation doesn't ++ * assure correct functionality of the MDIO bus ++ * with divider of 1, 2 or 4. ++ */ ++ for (div = 8; div <= 256; div *= 2) { ++ /* The requested rate is supported by the div */ ++ if (priv->mdc_rate == DIV_ROUND_UP(ahb_rate, div)) { ++ val = readl(priv->membase + MDIO_MODE_REG); ++ val &= ~MDIO_MODE_DIV_MASK; ++ val |= MDIO_MODE_DIV(div); ++ writel(val, priv->membase + MDIO_MODE_REG); ++ ++ return 0; ++ } ++ } ++ ++ /* The requested rate is not supported */ ++ return -EINVAL; ++} ++ + static int ipq_mdio_reset(struct mii_bus *bus) + { + struct ipq4019_mdio_data *priv = bus->priv; +@@ -201,10 +245,58 @@ static int ipq_mdio_reset(struct mii_bus + return ret; + + ret = clk_prepare_enable(priv->mdio_clk); +- if (ret == 0) +- mdelay(10); ++ if (ret) ++ return ret; ++ ++ mdelay(10); + +- return ret; ++ /* Restore MDC rate */ ++ return ipq4019_mdio_set_div(priv); ++} ++ ++static void ipq4019_mdio_select_mdc_rate(struct platform_device *pdev, ++ struct ipq4019_mdio_data *priv) ++{ ++ unsigned long ahb_rate; ++ int div; ++ u32 val; ++ ++ /* MDC rate defined in DT, we don't have to decide a default value */ ++ if (!of_property_read_u32(pdev->dev.of_node, "clock-frequency", ++ &priv->mdc_rate)) ++ return; ++ ++ /* If we don't have a clock for AHB use the fixed value */ ++ ahb_rate = IPQ_MDIO_CLK_RATE; ++ if (priv->mdio_clk) ++ ahb_rate = clk_get_rate(priv->mdio_clk); ++ ++ /* Check what is the current div set */ ++ val = readl(priv->membase + MDIO_MODE_REG); ++ div = FIELD_GET(MDIO_MODE_DIV_MASK, val); ++ ++ /* div is not set to the default value of /256 ++ * Probably someone changed that (bootloader, other drivers) ++ * Keep this and don't overwrite it. ++ */ ++ if (div != MDIO_MODE_DIV_256) { ++ priv->mdc_rate = DIV_ROUND_UP(ahb_rate, div + 1); ++ return; ++ } ++ ++ /* If div is /256 assume nobody have set this value and ++ * try to find one MDC rate that is close the 802.3 spec of ++ * 2.5MHz ++ */ ++ for (div = 256; div >= 8; div /= 2) { ++ /* Stop as soon as we found a divider that ++ * reached the closest value to 2.5MHz ++ */ ++ if (DIV_ROUND_UP(ahb_rate, div) > 2500000) ++ break; ++ ++ priv->mdc_rate = DIV_ROUND_UP(ahb_rate, div); ++ } + } + + static int ipq4019_mdio_probe(struct platform_device *pdev) +@@ -228,6 +320,11 @@ static int ipq4019_mdio_probe(struct pla + if (IS_ERR(priv->mdio_clk)) + return PTR_ERR(priv->mdio_clk); + ++ ipq4019_mdio_select_mdc_rate(pdev, priv); ++ ret = ipq4019_mdio_set_div(priv); ++ if (ret) ++ return ret; ++ + /* The platform resource is provided on the chipset IPQ5018 */ + /* This resource is optional */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); diff --git a/target/linux/generic/backport-6.6/721-v6.7-net-phy-aquantia-drop-wrong-endianness-conversion-fo.patch b/target/linux/generic/backport-6.6/721-v6.7-net-phy-aquantia-drop-wrong-endianness-conversion-fo.patch new file mode 100644 index 0000000000..d32a7edf93 --- /dev/null +++ b/target/linux/generic/backport-6.6/721-v6.7-net-phy-aquantia-drop-wrong-endianness-conversion-fo.patch @@ -0,0 +1,92 @@ +From 7edce370d87a23e8ed46af5b76a9fef1e341b67b Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 28 Nov 2023 14:59:28 +0100 +Subject: [PATCH] net: phy: aquantia: drop wrong endianness conversion for addr + and CRC + +On further testing on BE target with kernel test robot, it was notice +that the endianness conversion for addr and CRC in fw_load_memory was +wrong. + +Drop the cpu_to_le32 conversion for addr load as it's not needed. + +Use get_unaligned_le32 instead of get_unaligned for FW data word load to +correctly convert data in the correct order to follow system endian. + +Also drop the cpu_to_be32 for CRC calculation as it's wrong and would +cause different CRC on BE system. +The loaded word is swapped internally and MAILBOX calculates the CRC on +the swapped word. To correctly calculate the CRC to be later matched +with the one from MAILBOX, use an u8 struct and swap the word there to +keep the same order on both LE and BE for crc_ccitt_false function. +Also add additional comments on how the CRC verification for the loaded +section works. + +CRC is calculated as we load the section and verified with the MAILBOX +only after the entire section is loaded to skip additional slowdown by +loop the section data again. + +Reported-by: kernel test robot +Closes: https://lore.kernel.org/oe-kbuild-all/202311210414.sEJZjlcD-lkp@intel.com/ +Fixes: e93984ebc1c8 ("net: phy: aquantia: add firmware load support") +Tested-by: Robert Marko # ipq8072 LE device +Signed-off-by: Christian Marangi +Link: https://lore.kernel.org/r/20231128135928.9841-1-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/aquantia/aquantia_firmware.c | 24 ++++++++++++-------- + 1 file changed, 14 insertions(+), 10 deletions(-) + +--- a/drivers/net/phy/aquantia/aquantia_firmware.c ++++ b/drivers/net/phy/aquantia/aquantia_firmware.c +@@ -93,9 +93,6 @@ static int aqr_fw_load_memory(struct phy + u16 crc = 0, up_crc; + size_t pos; + +- /* PHY expect addr in LE */ +- addr = (__force u32)cpu_to_le32(addr); +- + phy_write_mmd(phydev, MDIO_MMD_VEND1, + VEND1_GLOBAL_MAILBOX_INTERFACE1, + VEND1_GLOBAL_MAILBOX_INTERFACE1_CRC_RESET); +@@ -110,10 +107,11 @@ static int aqr_fw_load_memory(struct phy + * If a firmware that is not word aligned is found, please report upstream. + */ + for (pos = 0; pos < len; pos += sizeof(u32)) { ++ u8 crc_data[4]; + u32 word; + + /* FW data is always stored in little-endian */ +- word = get_unaligned((const u32 *)(data + pos)); ++ word = get_unaligned_le32((const u32 *)(data + pos)); + + phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE5, + VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA(word)); +@@ -124,15 +122,21 @@ static int aqr_fw_load_memory(struct phy + VEND1_GLOBAL_MAILBOX_INTERFACE1_EXECUTE | + VEND1_GLOBAL_MAILBOX_INTERFACE1_WRITE); + +- /* calculate CRC as we load data to the mailbox. +- * We convert word to big-endian as PHY is BE and mailbox will +- * return a BE CRC. ++ /* Word is swapped internally and MAILBOX CRC is calculated ++ * using big-endian order. Mimic what the PHY does to have a ++ * matching CRC... + */ +- word = (__force u32)cpu_to_be32(word); +- crc = crc_ccitt_false(crc, (u8 *)&word, sizeof(word)); +- } ++ crc_data[0] = word >> 24; ++ crc_data[1] = word >> 16; ++ crc_data[2] = word >> 8; ++ crc_data[3] = word; + ++ /* ...calculate CRC as we load data... */ ++ crc = crc_ccitt_false(crc, crc_data, sizeof(crc_data)); ++ } ++ /* ...gets CRC from MAILBOX after we have loaded the entire section... */ + up_crc = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE2); ++ /* ...and make sure it does match our calculated CRC */ + if (crc != up_crc) { + phydev_err(phydev, "CRC mismatch: calculated 0x%04x PHY 0x%04x\n", + crc, up_crc); diff --git a/target/linux/generic/backport-6.6/724-v6.2-net-ethernet-mtk_eth_soc-enable-flow-offloading-supp.patch b/target/linux/generic/backport-6.6/724-v6.2-net-ethernet-mtk_eth_soc-enable-flow-offloading-supp.patch new file mode 100644 index 0000000000..816aa67787 --- /dev/null +++ b/target/linux/generic/backport-6.6/724-v6.2-net-ethernet-mtk_eth_soc-enable-flow-offloading-supp.patch @@ -0,0 +1,26 @@ +From 1b9827ceab08450308f7971d6fd700ec88b6ce67 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +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 +Link: https://lore.kernel.org/r/fdcaacd827938e6a8c4aa1ac2c13e46d2c08c821.1670072898.git.lorenzo@kernel.org +Signed-off-by: Jakub Kicinski +--- + 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 = { diff --git a/target/linux/generic/backport-6.6/729-01-v6.1-net-ethernet-mtk_wed-introduce-wed-mcu-support.patch b/target/linux/generic/backport-6.6/729-01-v6.1-net-ethernet-mtk_wed-introduce-wed-mcu-support.patch new file mode 100644 index 0000000000..c48613929d --- /dev/null +++ b/target/linux/generic/backport-6.6/729-01-v6.1-net-ethernet-mtk_wed-introduce-wed-mcu-support.patch @@ -0,0 +1,591 @@ +From: Sujuan Chen +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 +Co-developed-by: Lorenzo Bianconi +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Sujuan Chen +Signed-off-by: David S. Miller +--- + 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 ++ * Sujuan Chen ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#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 */ ++ ++#ifndef __MTK_WED_WO_H ++#define __MTK_WED_WO_H ++ ++#include ++#include ++ ++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, diff --git a/target/linux/generic/backport-6.6/729-02-v6.1-net-ethernet-mtk_wed-introduce-wed-wo-support.patch b/target/linux/generic/backport-6.6/729-02-v6.1-net-ethernet-mtk_wed-introduce-wed-wo-support.patch new file mode 100644 index 0000000000..fd5f45df2a --- /dev/null +++ b/target/linux/generic/backport-6.6/729-02-v6.1-net-ethernet-mtk_wed-introduce-wed-wo-support.patch @@ -0,0 +1,737 @@ +From: Lorenzo Bianconi +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 +Co-developed-by: Sujuan Chen +Signed-off-by: Sujuan Chen +Signed-off-by: Lorenzo Bianconi +Signed-off-by: David S. Miller +--- + 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 + + 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 ++ * Sujuan Chen ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 */ diff --git a/target/linux/generic/backport-6.6/729-03-v6.1-net-ethernet-mtk_wed-rename-tx_wdma-array-in-rx_wdma.patch b/target/linux/generic/backport-6.6/729-03-v6.1-net-ethernet-mtk_wed-rename-tx_wdma-array-in-rx_wdma.patch new file mode 100644 index 0000000000..a002a5f851 --- /dev/null +++ b/target/linux/generic/backport-6.6/729-03-v6.1-net-ethernet-mtk_wed-rename-tx_wdma-array-in-rx_wdma.patch @@ -0,0 +1,79 @@ +From: Lorenzo Bianconi +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 +Signed-off-by: David S. Miller +--- + +--- 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 + + #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; diff --git a/target/linux/generic/backport-6.6/729-04-v6.1-net-ethernet-mtk_wed-add-configure-wed-wo-support.patch b/target/linux/generic/backport-6.6/729-04-v6.1-net-ethernet-mtk_wed-add-configure-wed-wo-support.patch new file mode 100644 index 0000000000..eca29739b4 --- /dev/null +++ b/target/linux/generic/backport-6.6/729-04-v6.1-net-ethernet-mtk_wed-add-configure-wed-wo-support.patch @@ -0,0 +1,1521 @@ +From: Lorenzo Bianconi +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 +Co-developed-by: Sujuan Chen +Signed-off-by: Sujuan Chen +Signed-off-by: Lorenzo Bianconi +Signed-off-by: David S. Miller +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -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 + #include + #include ++#include + + #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 + #include + #include ++#include + + #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 diff --git a/target/linux/generic/backport-6.6/729-05-v6.1-net-ethernet-mtk_wed-add-rx-mib-counters.patch b/target/linux/generic/backport-6.6/729-05-v6.1-net-ethernet-mtk_wed-add-rx-mib-counters.patch new file mode 100644 index 0000000000..bb1066dece --- /dev/null +++ b/target/linux/generic/backport-6.6/729-05-v6.1-net-ethernet-mtk_wed-add-rx-mib-counters.patch @@ -0,0 +1,149 @@ +From: Lorenzo Bianconi +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 +Co-developed-by: Sujuan Chen +Signed-off-by: Sujuan Chen +Signed-off-by: Lorenzo Bianconi +Signed-off-by: David S. Miller +--- + +--- 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 */ + + #include ++#include + #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); + } diff --git a/target/linux/generic/backport-6.6/729-07-v6.1-net-ethernet-mtk_eth_soc-remove-cpu_relax-in-mtk_pen.patch b/target/linux/generic/backport-6.6/729-07-v6.1-net-ethernet-mtk_eth_soc-remove-cpu_relax-in-mtk_pen.patch new file mode 100644 index 0000000000..cefe1eefff --- /dev/null +++ b/target/linux/generic/backport-6.6/729-07-v6.1-net-ethernet-mtk_eth_soc-remove-cpu_relax-in-mtk_pen.patch @@ -0,0 +1,36 @@ +From: Lorenzo Bianconi +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 +Signed-off-by: David S. Miller +--- + +--- 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(); + } diff --git a/target/linux/generic/backport-6.6/729-09-v6.2-net-ethernet-mtk_wed-add-wcid-overwritten-support-fo.patch b/target/linux/generic/backport-6.6/729-09-v6.2-net-ethernet-mtk_wed-add-wcid-overwritten-support-fo.patch new file mode 100644 index 0000000000..117ccc0902 --- /dev/null +++ b/target/linux/generic/backport-6.6/729-09-v6.2-net-ethernet-mtk_wed-add-wcid-overwritten-support-fo.patch @@ -0,0 +1,80 @@ +From: Sujuan Chen +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 +Co-developed-by: Bo Jiao +Signed-off-by: Bo Jiao +Signed-off-by: Sujuan Chen +Signed-off-by: David S. Miller +--- + +--- 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; diff --git a/target/linux/generic/backport-6.6/729-10-v6.2-net-ethernet-mtk_wed-return-status-value-in-mtk_wdma.patch b/target/linux/generic/backport-6.6/729-10-v6.2-net-ethernet-mtk_wed-return-status-value-in-mtk_wdma.patch new file mode 100644 index 0000000000..ec58c3fc57 --- /dev/null +++ b/target/linux/generic/backport-6.6/729-10-v6.2-net-ethernet-mtk_wed-return-status-value-in-mtk_wdma.patch @@ -0,0 +1,85 @@ +From: Lorenzo Bianconi +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 +Signed-off-by: Sujuan Chen +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + +--- 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); diff --git a/target/linux/generic/backport-6.6/729-11-v6.2-net-ethernet-mtk_wed-move-MTK_WDMA_RESET_IDX_TX-conf.patch b/target/linux/generic/backport-6.6/729-11-v6.2-net-ethernet-mtk_wed-move-MTK_WDMA_RESET_IDX_TX-conf.patch new file mode 100644 index 0000000000..10c1732c96 --- /dev/null +++ b/target/linux/generic/backport-6.6/729-11-v6.2-net-ethernet-mtk_wed-move-MTK_WDMA_RESET_IDX_TX-conf.patch @@ -0,0 +1,52 @@ +From: Lorenzo Bianconi +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 +Signed-off-by: Sujuan Chen +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + +--- 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); + diff --git a/target/linux/generic/backport-6.6/729-12-v6.2-net-ethernet-mtk_wed-update-mtk_wed_stop.patch b/target/linux/generic/backport-6.6/729-12-v6.2-net-ethernet-mtk_wed-update-mtk_wed_stop.patch new file mode 100644 index 0000000000..f4e842d515 --- /dev/null +++ b/target/linux/generic/backport-6.6/729-12-v6.2-net-ethernet-mtk_wed-update-mtk_wed_stop.patch @@ -0,0 +1,98 @@ +From: Lorenzo Bianconi +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 +Signed-off-by: Sujuan Chen +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + +--- 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 diff --git a/target/linux/generic/backport-6.6/729-13-v6.2-net-ethernet-mtk_wed-add-mtk_wed_rx_reset-routine.patch b/target/linux/generic/backport-6.6/729-13-v6.2-net-ethernet-mtk_wed-add-mtk_wed_rx_reset-routine.patch new file mode 100644 index 0000000000..a0fc9da99e --- /dev/null +++ b/target/linux/generic/backport-6.6/729-13-v6.2-net-ethernet-mtk_wed-add-mtk_wed_rx_reset-routine.patch @@ -0,0 +1,309 @@ +From: Lorenzo Bianconi +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 +Signed-off-by: Sujuan Chen +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + +--- 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) + diff --git a/target/linux/generic/backport-6.6/729-14-v6.2-net-ethernet-mtk_wed-add-reset-to-tx_ring_setup-call.patch b/target/linux/generic/backport-6.6/729-14-v6.2-net-ethernet-mtk_wed-add-reset-to-tx_ring_setup-call.patch new file mode 100644 index 0000000000..4404971cc7 --- /dev/null +++ b/target/linux/generic/backport-6.6/729-14-v6.2-net-ethernet-mtk_wed-add-reset-to-tx_ring_setup-call.patch @@ -0,0 +1,103 @@ +From: Lorenzo Bianconi +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 +Signed-off-by: Sujuan Chen +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + +--- 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) diff --git a/target/linux/generic/backport-6.6/729-15-v6.2-net-ethernet-mtk_wed-fix-sleep-while-atomic-in-mtk_w.patch b/target/linux/generic/backport-6.6/729-15-v6.2-net-ethernet-mtk_wed-fix-sleep-while-atomic-in-mtk_w.patch new file mode 100644 index 0000000000..f9b11326b1 --- /dev/null +++ b/target/linux/generic/backport-6.6/729-15-v6.2-net-ethernet-mtk_wed-fix-sleep-while-atomic-in-mtk_w.patch @@ -0,0 +1,103 @@ +From: Lorenzo Bianconi +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 +Reviewed-by: Pavan Chebbi +Link: https://lore.kernel.org/r/67ca94bdd3d9eaeb86e52b3050fbca0bcf7bb02f.1669908312.git.lorenzo@kernel.org +Signed-off-by: Jakub Kicinski +--- + +--- 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 */ diff --git a/target/linux/generic/backport-6.6/729-16-v6.3-net-ethernet-mtk_wed-get-rid-of-queue-lock-for-rx-qu.patch b/target/linux/generic/backport-6.6/729-16-v6.3-net-ethernet-mtk_wed-get-rid-of-queue-lock-for-rx-qu.patch new file mode 100644 index 0000000000..fa6f56dbe7 --- /dev/null +++ b/target/linux/generic/backport-6.6/729-16-v6.3-net-ethernet-mtk_wed-get-rid-of-queue-lock-for-rx-qu.patch @@ -0,0 +1,52 @@ +From: Lorenzo Bianconi +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 +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/r/36ec3b729542ea60898471d890796f745479ba32.1673342990.git.lorenzo@kernel.org +Signed-off-by: Jakub Kicinski +--- + +--- 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; diff --git a/target/linux/generic/backport-6.6/729-17-v6.3-net-ethernet-mtk_wed-get-rid-of-queue-lock-for-tx-qu.patch b/target/linux/generic/backport-6.6/729-17-v6.3-net-ethernet-mtk_wed-get-rid-of-queue-lock-for-tx-qu.patch new file mode 100644 index 0000000000..9b1e4c3250 --- /dev/null +++ b/target/linux/generic/backport-6.6/729-17-v6.3-net-ethernet-mtk_wed-get-rid-of-queue-lock-for-tx-qu.patch @@ -0,0 +1,75 @@ +From: Lorenzo Bianconi +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 +Link: https://lore.kernel.org/r/7bd0337b2a13ab1a63673b7c03fd35206b3b284e.1673515140.git.lorenzo@kernel.org +Signed-off-by: Jakub Kicinski +--- + +--- 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; diff --git a/target/linux/generic/backport-6.6/729-18-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_reset-util.patch b/target/linux/generic/backport-6.6/729-18-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_reset-util.patch new file mode 100644 index 0000000000..c91861a8f1 --- /dev/null +++ b/target/linux/generic/backport-6.6/729-18-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_reset-util.patch @@ -0,0 +1,70 @@ +From: Lorenzo Bianconi +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 +Tested-by: Daniel Golle +Co-developed-by: Sujuan Chen +Signed-off-by: Sujuan Chen +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + +--- 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); diff --git a/target/linux/generic/backport-6.6/729-19-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_warm_reset.patch b/target/linux/generic/backport-6.6/729-19-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_warm_reset.patch new file mode 100644 index 0000000000..6597eb5b74 --- /dev/null +++ b/target/linux/generic/backport-6.6/729-19-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_warm_reset.patch @@ -0,0 +1,107 @@ +From: Lorenzo Bianconi +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 +Tested-by: Daniel Golle +Co-developed-by: Sujuan Chen +Signed-off-by: Sujuan Chen +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + +--- 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; + diff --git a/target/linux/generic/backport-6.6/729-20-v6.3-net-ethernet-mtk_eth_soc-align-reset-procedure-to-ve.patch b/target/linux/generic/backport-6.6/729-20-v6.3-net-ethernet-mtk_eth_soc-align-reset-procedure-to-ve.patch new file mode 100644 index 0000000000..55ab19f4c8 --- /dev/null +++ b/target/linux/generic/backport-6.6/729-20-v6.3-net-ethernet-mtk_eth_soc-align-reset-procedure-to-ve.patch @@ -0,0 +1,262 @@ +From: Lorenzo Bianconi +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 +Tested-by: Daniel Golle +Co-developed-by: Sujuan Chen +Signed-off-by: Sujuan Chen +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + +--- 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, diff --git a/target/linux/generic/backport-6.6/729-21-v6.3-net-ethernet-mtk_eth_soc-add-dma-checks-to-mtk_hw_re.patch b/target/linux/generic/backport-6.6/729-21-v6.3-net-ethernet-mtk_eth_soc-add-dma-checks-to-mtk_hw_re.patch new file mode 100644 index 0000000000..d5a7c0eba2 --- /dev/null +++ b/target/linux/generic/backport-6.6/729-21-v6.3-net-ethernet-mtk_eth_soc-add-dma-checks-to-mtk_hw_re.patch @@ -0,0 +1,249 @@ +From: Lorenzo Bianconi +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 +Tested-by: Daniel Golle +Co-developed-by: Sujuan Chen +Signed-off-by: Sujuan Chen +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + +--- 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 diff --git a/target/linux/generic/backport-6.6/729-22-v6.3-net-ethernet-mtk_wed-add-reset-reset_complete-callba.patch b/target/linux/generic/backport-6.6/729-22-v6.3-net-ethernet-mtk_wed-add-reset-reset_complete-callba.patch new file mode 100644 index 0000000000..c21d094ae8 --- /dev/null +++ b/target/linux/generic/backport-6.6/729-22-v6.3-net-ethernet-mtk_wed-add-reset-reset_complete-callba.patch @@ -0,0 +1,124 @@ +From: Lorenzo Bianconi +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 +Co-developed-by: Sujuan Chen +Signed-off-by: Sujuan Chen +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + +--- 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 + }; diff --git a/target/linux/generic/backport-6.6/729-23-v6.3-net-ethernet-mtk_wed-add-reset-to-rx_ring_setup-call.patch b/target/linux/generic/backport-6.6/729-23-v6.3-net-ethernet-mtk_wed-add-reset-to-rx_ring_setup-call.patch new file mode 100644 index 0000000000..c63628da99 --- /dev/null +++ b/target/linux/generic/backport-6.6/729-23-v6.3-net-ethernet-mtk_wed-add-reset-to-rx_ring_setup-call.patch @@ -0,0 +1,106 @@ +From: Lorenzo Bianconi +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 +Signed-off-by: Sujuan Chen +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Leon Romanovsky +Link: https://lore.kernel.org/r/29c6e7a5469e784406cf3e2920351d1207713d05.1670239984.git.lorenzo@kernel.org +Signed-off-by: Jakub Kicinski +--- + +--- 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) diff --git a/target/linux/generic/backport-6.6/730-01-v6.3-net-ethernet-mtk_eth_soc-account-for-vlan-in-rx-head.patch b/target/linux/generic/backport-6.6/730-01-v6.3-net-ethernet-mtk_eth_soc-account-for-vlan-in-rx-head.patch new file mode 100644 index 0000000000..45af898cf0 --- /dev/null +++ b/target/linux/generic/backport-6.6/730-01-v6.3-net-ethernet-mtk_eth_soc-account-for-vlan-in-rx-head.patch @@ -0,0 +1,22 @@ +From: Felix Fietkau +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 +--- + +--- 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 | \ diff --git a/target/linux/generic/backport-6.6/730-02-v6.3-net-ethernet-mtk_eth_soc-increase-tx-ring-side-for-Q.patch b/target/linux/generic/backport-6.6/730-02-v6.3-net-ethernet-mtk_eth_soc-increase-tx-ring-side-for-Q.patch new file mode 100644 index 0000000000..046a581224 --- /dev/null +++ b/target/linux/generic/backport-6.6/730-02-v6.3-net-ethernet-mtk_eth_soc-increase-tx-ring-side-for-Q.patch @@ -0,0 +1,143 @@ +From: Felix Fietkau +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 +--- + +--- 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) diff --git a/target/linux/generic/backport-6.6/730-03-v6.3-net-ethernet-mtk_eth_soc-avoid-port_mg-assignment-on.patch b/target/linux/generic/backport-6.6/730-03-v6.3-net-ethernet-mtk_eth_soc-avoid-port_mg-assignment-on.patch new file mode 100644 index 0000000000..7e879ca1d5 --- /dev/null +++ b/target/linux/generic/backport-6.6/730-03-v6.3-net-ethernet-mtk_eth_soc-avoid-port_mg-assignment-on.patch @@ -0,0 +1,52 @@ +From: Felix Fietkau +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 +--- + +--- 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); + } + diff --git a/target/linux/generic/backport-6.6/730-04-v6.3-net-ethernet-mtk_eth_soc-implement-multi-queue-suppo.patch b/target/linux/generic/backport-6.6/730-04-v6.3-net-ethernet-mtk_eth_soc-implement-multi-queue-suppo.patch new file mode 100644 index 0000000000..8ceba7831e --- /dev/null +++ b/target/linux/generic/backport-6.6/730-04-v6.3-net-ethernet-mtk_eth_soc-implement-multi-queue-suppo.patch @@ -0,0 +1,654 @@ +From: Felix Fietkau +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 +--- + +--- 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 + #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 */ diff --git a/target/linux/generic/backport-6.6/730-05-v6.3-net-dsa-tag_mtk-assign-per-port-queues.patch b/target/linux/generic/backport-6.6/730-05-v6.3-net-dsa-tag_mtk-assign-per-port-queues.patch new file mode 100644 index 0000000000..186df4bdc9 --- /dev/null +++ b/target/linux/generic/backport-6.6/730-05-v6.3-net-dsa-tag_mtk-assign-per-port-queues.patch @@ -0,0 +1,20 @@ +From: Felix Fietkau +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 +--- + +--- 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 diff --git a/target/linux/generic/backport-6.6/730-06-v6.3-net-ethernet-mediatek-ppe-assign-per-port-queues-for.patch b/target/linux/generic/backport-6.6/730-06-v6.3-net-ethernet-mediatek-ppe-assign-per-port-queues-for.patch new file mode 100644 index 0000000000..d29177eee7 --- /dev/null +++ b/target/linux/generic/backport-6.6/730-06-v6.3-net-ethernet-mediatek-ppe-assign-per-port-queues-for.patch @@ -0,0 +1,93 @@ +From: Felix Fietkau +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 +--- + +--- 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); + diff --git a/target/linux/generic/backport-6.6/730-08-v6.3-net-dsa-add-support-for-DSA-rx-offloading-via-metada.patch b/target/linux/generic/backport-6.6/730-08-v6.3-net-dsa-add-support-for-DSA-rx-offloading-via-metada.patch new file mode 100644 index 0000000000..6b7f3d6018 --- /dev/null +++ b/target/linux/generic/backport-6.6/730-08-v6.3-net-dsa-add-support-for-DSA-rx-offloading-via-metada.patch @@ -0,0 +1,72 @@ +From: Felix Fietkau +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 +--- + +--- 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 + #include + #include ++#include + + #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; diff --git a/target/linux/generic/backport-6.6/730-09-v6.3-net-ethernet-mtk_eth_soc-fix-VLAN-rx-hardware-accele.patch b/target/linux/generic/backport-6.6/730-09-v6.3-net-ethernet-mtk_eth_soc-fix-VLAN-rx-hardware-accele.patch new file mode 100644 index 0000000000..b8e3452f30 --- /dev/null +++ b/target/linux/generic/backport-6.6/730-09-v6.3-net-ethernet-mtk_eth_soc-fix-VLAN-rx-hardware-accele.patch @@ -0,0 +1,192 @@ +From: Felix Fietkau +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 +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + + #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 + #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; + diff --git a/target/linux/generic/backport-6.6/730-12-v6.3-net-ethernet-mtk_eth_soc-disable-hardware-DSA-untagg.patch b/target/linux/generic/backport-6.6/730-12-v6.3-net-ethernet-mtk_eth_soc-disable-hardware-DSA-untagg.patch new file mode 100644 index 0000000000..a88df2b8e3 --- /dev/null +++ b/target/linux/generic/backport-6.6/730-12-v6.3-net-ethernet-mtk_eth_soc-disable-hardware-DSA-untagg.patch @@ -0,0 +1,42 @@ +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +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 +Signed-off-by: Arınç ÜNAL +Link: https://lore.kernel.org/r/20230128094232.2451947-1-arinc.unal@arinc9.com +Signed-off-by: Jakub Kicinski +--- + +--- 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; diff --git a/target/linux/generic/backport-6.6/730-13-v6.3-net-ethernet-mtk_eth_soc-enable-special-tag-when-any.patch b/target/linux/generic/backport-6.6/730-13-v6.3-net-ethernet-mtk_eth_soc-enable-special-tag-when-any.patch new file mode 100644 index 0000000000..8da728b9e9 --- /dev/null +++ b/target/linux/generic/backport-6.6/730-13-v6.3-net-ethernet-mtk_eth_soc-enable-special-tag-when-any.patch @@ -0,0 +1,54 @@ +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +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 +Signed-off-by: Richard van Schagen +Signed-off-by: Arınç ÜNAL +Signed-off-by: David S. Miller +--- + +--- 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; diff --git a/target/linux/generic/backport-6.6/730-14-v6.3-net-ethernet-mtk_eth_soc-fix-DSA-TX-tag-hwaccel-for-.patch b/target/linux/generic/backport-6.6/730-14-v6.3-net-ethernet-mtk_eth_soc-fix-DSA-TX-tag-hwaccel-for-.patch new file mode 100644 index 0000000000..51cd572ab2 --- /dev/null +++ b/target/linux/generic/backport-6.6/730-14-v6.3-net-ethernet-mtk_eth_soc-fix-DSA-TX-tag-hwaccel-for-.patch @@ -0,0 +1,129 @@ +From: Vladimir Oltean +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 +Tested-by: Arınç ÜNAL +Signed-off-by: Vladimir Oltean +Reviewed-by: Felix Fietkau +Signed-off-by: David S. Miller +--- + +--- 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); diff --git a/target/linux/generic/backport-6.6/730-15-v6.3-net-ethernet-mtk_wed-No-need-to-clear-memory-after-a.patch b/target/linux/generic/backport-6.6/730-15-v6.3-net-ethernet-mtk_wed-No-need-to-clear-memory-after-a.patch new file mode 100644 index 0000000000..a3bb1c5db7 --- /dev/null +++ b/target/linux/generic/backport-6.6/730-15-v6.3-net-ethernet-mtk_wed-No-need-to-clear-memory-after-a.patch @@ -0,0 +1,26 @@ +From: Christophe JAILLET +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 +Link: https://lore.kernel.org/r/d5acce7dd108887832c9719f62c7201b4c83b3fb.1676184599.git.christophe.jaillet@wanadoo.fr +Signed-off-by: Jakub Kicinski +--- + +--- 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; + } diff --git a/target/linux/generic/backport-6.6/730-16-v6.3-net-ethernet-mtk_wed-fix-some-possible-NULL-pointer-.patch b/target/linux/generic/backport-6.6/730-16-v6.3-net-ethernet-mtk_wed-fix-some-possible-NULL-pointer-.patch new file mode 100644 index 0000000000..e043a681da --- /dev/null +++ b/target/linux/generic/backport-6.6/730-16-v6.3-net-ethernet-mtk_wed-fix-some-possible-NULL-pointer-.patch @@ -0,0 +1,61 @@ +From: Lorenzo Bianconi +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 +Reviewed-by: Leon Romanovsky +Signed-off-by: Jakub Kicinski +--- + +--- 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); + } diff --git a/target/linux/generic/backport-6.6/730-17-v6.3-net-ethernet-mtk_wed-fix-possible-deadlock-if-mtk_we.patch b/target/linux/generic/backport-6.6/730-17-v6.3-net-ethernet-mtk_wed-fix-possible-deadlock-if-mtk_we.patch new file mode 100644 index 0000000000..0afe7106e5 --- /dev/null +++ b/target/linux/generic/backport-6.6/730-17-v6.3-net-ethernet-mtk_wed-fix-possible-deadlock-if-mtk_we.patch @@ -0,0 +1,58 @@ +From: Lorenzo Bianconi +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 +Reviewed-by: Leon Romanovsky +Signed-off-by: Jakub Kicinski +--- + +--- 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); + diff --git a/target/linux/generic/backport-6.6/730-18-v6.3-net-ethernet-mtk_eth_soc-fix-tx-throughput-regressio.patch b/target/linux/generic/backport-6.6/730-18-v6.3-net-ethernet-mtk_eth_soc-fix-tx-throughput-regressio.patch new file mode 100644 index 0000000000..ca5b6b3a3e --- /dev/null +++ b/target/linux/generic/backport-6.6/730-18-v6.3-net-ethernet-mtk_eth_soc-fix-tx-throughput-regressio.patch @@ -0,0 +1,31 @@ +From: Felix Fietkau +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 +Reported-by: Daniel Golle +Tested-by: Daniel Golle +Signed-off-by: Felix Fietkau +--- + +--- 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; diff --git a/target/linux/generic/backport-6.6/733-v6.2-02-net-mtk_eth_soc-add-definitions-for-PCS.patch b/target/linux/generic/backport-6.6/733-v6.2-02-net-mtk_eth_soc-add-definitions-for-PCS.patch new file mode 100644 index 0000000000..850b806410 --- /dev/null +++ b/target/linux/generic/backport-6.6/733-v6.2-02-net-mtk_eth_soc-add-definitions-for-PCS.patch @@ -0,0 +1,55 @@ +From b6a709cb51f7bdc55c01cec886098a9753ce8c28 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Signed-off-by: Jakub Kicinski +--- + 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) diff --git a/target/linux/generic/backport-6.6/733-v6.2-03-net-mtk_eth_soc-eliminate-unnecessary-error-handling.patch b/target/linux/generic/backport-6.6/733-v6.2-03-net-mtk_eth_soc-eliminate-unnecessary-error-handling.patch new file mode 100644 index 0000000000..4ea428c9d6 --- /dev/null +++ b/target/linux/generic/backport-6.6/733-v6.2-03-net-mtk_eth_soc-eliminate-unnecessary-error-handling.patch @@ -0,0 +1,74 @@ +From 5cf7797526ee81bea0f627bccaa3d887f48f53e0 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Signed-off-by: Jakub Kicinski +--- + 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) diff --git a/target/linux/generic/backport-6.6/733-v6.2-04-net-mtk_eth_soc-add-pcs_get_state-implementation.patch b/target/linux/generic/backport-6.6/733-v6.2-04-net-mtk_eth_soc-add-pcs_get_state-implementation.patch new file mode 100644 index 0000000000..64a4a72fa6 --- /dev/null +++ b/target/linux/generic/backport-6.6/733-v6.2-04-net-mtk_eth_soc-add-pcs_get_state-implementation.patch @@ -0,0 +1,46 @@ +From c000dca098002da193b98099df051c9ead0cacb4 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Signed-off-by: Jakub Kicinski +--- + 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, diff --git a/target/linux/generic/backport-6.6/733-v6.2-05-net-mtk_eth_soc-convert-mtk_sgmii-to-use-regmap_upda.patch b/target/linux/generic/backport-6.6/733-v6.2-05-net-mtk_eth_soc-convert-mtk_sgmii-to-use-regmap_upda.patch new file mode 100644 index 0000000000..24610fe11e --- /dev/null +++ b/target/linux/generic/backport-6.6/733-v6.2-05-net-mtk_eth_soc-convert-mtk_sgmii-to-use-regmap_upda.patch @@ -0,0 +1,130 @@ +From 0d2351dc2768061689abd4de1529fa206bbd574e Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Signed-off-by: Jakub Kicinski +--- + 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 = { diff --git a/target/linux/generic/backport-6.6/733-v6.2-06-net-mtk_eth_soc-add-out-of-band-forcing-of-speed-and.patch b/target/linux/generic/backport-6.6/733-v6.2-06-net-mtk_eth_soc-add-out-of-band-forcing-of-speed-and.patch new file mode 100644 index 0000000000..ba76ca40ff --- /dev/null +++ b/target/linux/generic/backport-6.6/733-v6.2-06-net-mtk_eth_soc-add-out-of-band-forcing-of-speed-and.patch @@ -0,0 +1,52 @@ +From 12198c3a410fe69843e335c1bbf6d4c2a4d48e4e Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Signed-off-by: Jakub Kicinski +--- + 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 = { diff --git a/target/linux/generic/backport-6.6/733-v6.2-07-net-mtk_eth_soc-move-PHY-power-up.patch b/target/linux/generic/backport-6.6/733-v6.2-07-net-mtk_eth_soc-move-PHY-power-up.patch new file mode 100644 index 0000000000..b76e159275 --- /dev/null +++ b/target/linux/generic/backport-6.6/733-v6.2-07-net-mtk_eth_soc-move-PHY-power-up.patch @@ -0,0 +1,48 @@ +From 6f38fffe2179dd29612aea2c67c46ed6682b4e46 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Signed-off-by: Jakub Kicinski +--- + 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; + } + diff --git a/target/linux/generic/backport-6.6/733-v6.2-08-net-mtk_eth_soc-move-interface-speed-selection.patch b/target/linux/generic/backport-6.6/733-v6.2-08-net-mtk_eth_soc-move-interface-speed-selection.patch new file mode 100644 index 0000000000..cd9f0699b3 --- /dev/null +++ b/target/linux/generic/backport-6.6/733-v6.2-08-net-mtk_eth_soc-move-interface-speed-selection.patch @@ -0,0 +1,48 @@ +From f752c0df13dfeb721c11d3debb79f08cf437344f Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Signed-off-by: Jakub Kicinski +--- + 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) diff --git a/target/linux/generic/backport-6.6/733-v6.2-09-net-mtk_eth_soc-add-advertisement-programming.patch b/target/linux/generic/backport-6.6/733-v6.2-09-net-mtk_eth_soc-add-advertisement-programming.patch new file mode 100644 index 0000000000..f08358e963 --- /dev/null +++ b/target/linux/generic/backport-6.6/733-v6.2-09-net-mtk_eth_soc-add-advertisement-programming.patch @@ -0,0 +1,52 @@ +From c125c66ea71b9377ae2478c4f1b87b180cc5c6ef Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Signed-off-by: Jakub Kicinski +--- + 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) diff --git a/target/linux/generic/backport-6.6/733-v6.2-10-net-mtk_eth_soc-move-and-correct-link-timer-programm.patch b/target/linux/generic/backport-6.6/733-v6.2-10-net-mtk_eth_soc-move-and-correct-link-timer-programm.patch new file mode 100644 index 0000000000..602d52c6f4 --- /dev/null +++ b/target/linux/generic/backport-6.6/733-v6.2-10-net-mtk_eth_soc-move-and-correct-link-timer-programm.patch @@ -0,0 +1,63 @@ +From 3027d89f87707e7f3e5b683e0d37a32afb5bde96 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Signed-off-by: Jakub Kicinski +--- + 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); diff --git a/target/linux/generic/backport-6.6/733-v6.2-11-net-mtk_eth_soc-add-support-for-in-band-802.3z-negot.patch b/target/linux/generic/backport-6.6/733-v6.2-11-net-mtk_eth_soc-add-support-for-in-band-802.3z-negot.patch new file mode 100644 index 0000000000..0e9a0535a7 --- /dev/null +++ b/target/linux/generic/backport-6.6/733-v6.2-11-net-mtk_eth_soc-add-support-for-in-band-802.3z-negot.patch @@ -0,0 +1,132 @@ +From 81b0f12a2a8a1699a7d49c3995e5f71e4ec018e6 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Signed-off-by: Jakub Kicinski +--- + 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; diff --git a/target/linux/generic/backport-6.6/733-v6.2-12-net-mediatek-sgmii-ensure-the-SGMII-PHY-is-powered-d.patch b/target/linux/generic/backport-6.6/733-v6.2-12-net-mediatek-sgmii-ensure-the-SGMII-PHY-is-powered-d.patch new file mode 100644 index 0000000000..a02c583deb --- /dev/null +++ b/target/linux/generic/backport-6.6/733-v6.2-12-net-mediatek-sgmii-ensure-the-SGMII-PHY-is-powered-d.patch @@ -0,0 +1,119 @@ +From 7ff82416de8295c61423ef6fd75f052d3837d2f7 Mon Sep 17 00:00:00 2001 +From: Alexander Couzens +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) +Signed-off-by: Alexander Couzens +[ bmork: rebased and squashed into one patch ] +Reviewed-by: Russell King (Oracle) +Signed-off-by: Bjørn Mork +Acked-by: Daniel Golle +Tested-by: Daniel Golle +Signed-off-by: Jakub Kicinski +--- + 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; diff --git a/target/linux/generic/backport-6.6/733-v6.2-13-net-mediatek-sgmii-fix-duplex-configuration.patch b/target/linux/generic/backport-6.6/733-v6.2-13-net-mediatek-sgmii-fix-duplex-configuration.patch new file mode 100644 index 0000000000..a06298c0a9 --- /dev/null +++ b/target/linux/generic/backport-6.6/733-v6.2-13-net-mediatek-sgmii-fix-duplex-configuration.patch @@ -0,0 +1,52 @@ +From 9d32637122de88f1ef614c29703f0e050cad342e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= +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) +Signed-off-by: Bjørn Mork +Acked-by: Daniel Golle +Tested-by: Daniel Golle +Signed-off-by: Jakub Kicinski +--- + 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); + } + } diff --git a/target/linux/generic/backport-6.6/733-v6.2-14-mtk_sgmii-enable-PCS-polling-to-allow-SFP-work.patch b/target/linux/generic/backport-6.6/733-v6.2-14-mtk_sgmii-enable-PCS-polling-to-allow-SFP-work.patch new file mode 100644 index 0000000000..56d7a1348f --- /dev/null +++ b/target/linux/generic/backport-6.6/733-v6.2-14-mtk_sgmii-enable-PCS-polling-to-allow-SFP-work.patch @@ -0,0 +1,33 @@ +From 3337a6e04ddf2923a1bdcf3d31b3b52412bf82dd Mon Sep 17 00:00:00 2001 +From: Alexander Couzens +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) +Signed-off-by: Alexander Couzens +[ bmork: changed "1" => "true" ] +Signed-off-by: Bjørn Mork +Acked-by: Daniel Golle +Tested-by: Daniel Golle +Signed-off-by: Jakub Kicinski +--- + 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; + } + diff --git a/target/linux/generic/backport-6.6/733-v6.3-15-net-ethernet-mtk_eth_soc-reset-PCS-state.patch b/target/linux/generic/backport-6.6/733-v6.3-15-net-ethernet-mtk_eth_soc-reset-PCS-state.patch new file mode 100644 index 0000000000..6acc62d4ab --- /dev/null +++ b/target/linux/generic/backport-6.6/733-v6.3-15-net-ethernet-mtk_eth_soc-reset-PCS-state.patch @@ -0,0 +1,48 @@ +From 611e2dabb4b3243d176739fd6a5a34d007fa3f86 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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) +Tested-by: Bjørn Mork +Signed-off-by: Daniel Golle +Signed-off-by: David S. Miller +--- + 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 diff --git a/target/linux/generic/backport-6.6/733-v6.3-16-net-ethernet-mtk_eth_soc-only-write-values-if-needed.patch b/target/linux/generic/backport-6.6/733-v6.3-16-net-ethernet-mtk_eth_soc-only-write-values-if-needed.patch new file mode 100644 index 0000000000..0fabeea20c --- /dev/null +++ b/target/linux/generic/backport-6.6/733-v6.3-16-net-ethernet-mtk_eth_soc-only-write-values-if-needed.patch @@ -0,0 +1,103 @@ +From 6e933a804c7db8be64f367f33e63cd7dcc302ebb Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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) +Tested-by: Bjørn Mork +Signed-off-by: Daniel Golle +Signed-off-by: David S. Miller +--- + 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) diff --git a/target/linux/generic/backport-6.6/733-v6.3-18-net-ethernet-mtk_eth_soc-add-support-for-MT7981.patch b/target/linux/generic/backport-6.6/733-v6.3-18-net-ethernet-mtk_eth_soc-add-support-for-MT7981.patch new file mode 100644 index 0000000000..a1247218b0 --- /dev/null +++ b/target/linux/generic/backport-6.6/733-v6.3-18-net-ethernet-mtk_eth_soc-add-support-for-MT7981.patch @@ -0,0 +1,206 @@ +From f5d43ddd334b7c32fcaed9ba46afbd85cb467f1f Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Signed-off-by: Jakub Kicinski +--- + 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); diff --git a/target/linux/generic/backport-6.6/733-v6.3-19-net-ethernet-mtk_eth_soc-set-MDIO-bus-clock-frequenc.patch b/target/linux/generic/backport-6.6/733-v6.3-19-net-ethernet-mtk_eth_soc-set-MDIO-bus-clock-frequenc.patch new file mode 100644 index 0000000000..1cb1f40538 --- /dev/null +++ b/target/linux/generic/backport-6.6/733-v6.3-19-net-ethernet-mtk_eth_soc-set-MDIO-bus-clock-frequenc.patch @@ -0,0 +1,76 @@ +From c0a440031d4314d1023c1b87f43a4233634eebdb Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Reviewed-by: Florian Fainelli +Tested-by: Bjørn Mork +Signed-off-by: Daniel Golle +Signed-off-by: Jakub Kicinski +--- + 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) diff --git a/target/linux/generic/backport-6.6/733-v6.3-20-net-ethernet-mtk_eth_soc-switch-to-external-PCS-driv.patch b/target/linux/generic/backport-6.6/733-v6.3-20-net-ethernet-mtk_eth_soc-switch-to-external-PCS-driv.patch new file mode 100644 index 0000000000..110944658d --- /dev/null +++ b/target/linux/generic/backport-6.6/733-v6.3-20-net-ethernet-mtk_eth_soc-switch-to-external-PCS-driv.patch @@ -0,0 +1,512 @@ +From 2a3ec7ae313310c1092e4256208cc04d1958e469 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Tested-by: Frank Wunderlich +Reviewed-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + 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 + #include + #include ++#include + #include + #include + #include +@@ -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 +- * +- */ +- +-#include +-#include +-#include +-#include +- +-#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; +-} diff --git a/target/linux/generic/backport-6.6/733-v6.4-21-net-mtk_eth_soc-use-WO-firmware-for-MT7981.patch b/target/linux/generic/backport-6.6/733-v6.4-21-net-mtk_eth_soc-use-WO-firmware-for-MT7981.patch new file mode 100644 index 0000000000..9ce2735951 --- /dev/null +++ b/target/linux/generic/backport-6.6/733-v6.4-21-net-mtk_eth_soc-use-WO-firmware-for-MT7981.patch @@ -0,0 +1,46 @@ +From f5af7931d2a2cae66d0f9dad4ba517b1b00620b3 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +--- + 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" + diff --git a/target/linux/generic/backport-6.6/733-v6.4-22-net-ethernet-mtk_eth_soc-fix-NULL-pointer-dereferenc.patch b/target/linux/generic/backport-6.6/733-v6.4-22-net-ethernet-mtk_eth_soc-fix-NULL-pointer-dereferenc.patch new file mode 100644 index 0000000000..d715c4aa68 --- /dev/null +++ b/target/linux/generic/backport-6.6/733-v6.4-22-net-ethernet-mtk_eth_soc-fix-NULL-pointer-dereferenc.patch @@ -0,0 +1,28 @@ +From 7c83e28f10830aa5105c25eaabe890e3adac36aa Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Reviewed-by: Simon Horman +Signed-off-by: David S. Miller +--- + 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); + diff --git a/target/linux/generic/backport-6.6/733-v6.4-23-net-ethernet-mtk_eth_soc-ppe-add-support-for-flow-ac.patch b/target/linux/generic/backport-6.6/733-v6.4-23-net-ethernet-mtk_eth_soc-ppe-add-support-for-flow-ac.patch new file mode 100644 index 0000000000..93eaffa19e --- /dev/null +++ b/target/linux/generic/backport-6.6/733-v6.4-23-net-ethernet-mtk_eth_soc-ppe-add-support-for-flow-ac.patch @@ -0,0 +1,403 @@ +From f601293f37c4be618c5efaef85d2ee21f97e82e0 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Signed-off-by: Daniel Golle +Signed-off-by: Jakub Kicinski +--- + 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) diff --git a/target/linux/generic/backport-6.6/733-v6.4-24-net-ethernet-mediatek-fix-ppe-flow-accounting-for-v1.patch b/target/linux/generic/backport-6.6/733-v6.4-24-net-ethernet-mediatek-fix-ppe-flow-accounting-for-v1.patch new file mode 100644 index 0000000000..64352426ae --- /dev/null +++ b/target/linux/generic/backport-6.6/733-v6.4-24-net-ethernet-mediatek-fix-ppe-flow-accounting-for-v1.patch @@ -0,0 +1,58 @@ +From 88a0fd5927b7c2c7aecd6dc747d898eb38043d2b Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +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 +Signed-off-by: Daniel Golle +Signed-off-by: David S. Miller +--- + 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) + diff --git a/target/linux/generic/backport-6.6/733-v6.4-25-net-ethernet-mtk_eth_soc-drop-generic-vlan-rx-offloa.patch b/target/linux/generic/backport-6.6/733-v6.4-25-net-ethernet-mtk_eth_soc-drop-generic-vlan-rx-offloa.patch new file mode 100644 index 0000000000..217e517c3a --- /dev/null +++ b/target/linux/generic/backport-6.6/733-v6.4-25-net-ethernet-mtk_eth_soc-drop-generic-vlan-rx-offloa.patch @@ -0,0 +1,201 @@ +From: Felix Fietkau +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 +--- + +--- 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 |\ diff --git a/target/linux/generic/backport-6.6/733-v6.5-26-net-ethernet-mtk_eth_soc-always-mtk_get_ib1_pkt_type.patch b/target/linux/generic/backport-6.6/733-v6.5-26-net-ethernet-mtk_eth_soc-always-mtk_get_ib1_pkt_type.patch new file mode 100644 index 0000000000..2704faec12 --- /dev/null +++ b/target/linux/generic/backport-6.6/733-v6.5-26-net-ethernet-mtk_eth_soc-always-mtk_get_ib1_pkt_type.patch @@ -0,0 +1,31 @@ +From b804f765485109f9644cc05d1e8fc79ca6c6e4aa Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Acked-by: Lorenzo Bianconi +Link: https://lore.kernel.org/r/c0ae03d0182f4d27b874cbdf0059bc972c317f3c.1689727134.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + 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)); diff --git a/target/linux/generic/backport-6.6/750-v6.5-01-net-ethernet-mtk_ppe-add-MTK_FOE_ENTRY_V-1-2-_SIZE-m.patch b/target/linux/generic/backport-6.6/750-v6.5-01-net-ethernet-mtk_ppe-add-MTK_FOE_ENTRY_V-1-2-_SIZE-m.patch new file mode 100644 index 0000000000..d7d1c08fce --- /dev/null +++ b/target/linux/generic/backport-6.6/750-v6.5-01-net-ethernet-mtk_ppe-add-MTK_FOE_ENTRY_V-1-2-_SIZE-m.patch @@ -0,0 +1,78 @@ +From 5ea0e1312bcfebc06b5f91d1bb82b823d6395125 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +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 +Reviewed-by: Simon Horman +Signed-off-by: David S. Miller +--- + 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; + diff --git a/target/linux/generic/backport-6.6/750-v6.5-02-net-ethernet-mtk_eth_soc-remove-incorrect-PLL-config.patch b/target/linux/generic/backport-6.6/750-v6.5-02-net-ethernet-mtk_eth_soc-remove-incorrect-PLL-config.patch new file mode 100644 index 0000000000..fb54f404b2 --- /dev/null +++ b/target/linux/generic/backport-6.6/750-v6.5-02-net-ethernet-mtk_eth_soc-remove-incorrect-PLL-config.patch @@ -0,0 +1,141 @@ +From 8cfa2576d79f9379d167a8994f0fca935c07a8bc Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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 +Tested-by: Daniel Golle +Tested-by: Frank Wunderlich +Signed-off-by: Russell King (Oracle) +Signed-off-by: Paolo Abeni +--- + 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; diff --git a/target/linux/generic/backport-6.6/750-v6.5-03-net-ethernet-mtk_eth_soc-remove-mac_pcs_get_state-an.patch b/target/linux/generic/backport-6.6/750-v6.5-03-net-ethernet-mtk_eth_soc-remove-mac_pcs_get_state-an.patch new file mode 100644 index 0000000000..293066fa9a --- /dev/null +++ b/target/linux/generic/backport-6.6/750-v6.5-03-net-ethernet-mtk_eth_soc-remove-mac_pcs_get_state-an.patch @@ -0,0 +1,81 @@ +From a4c2233b1e4359b6c64b6f9ba98c8718a11fffee Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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 +Tested-by: Daniel Golle +Tested-by: Frank Wunderlich +Signed-off-by: Russell King (Oracle) +Signed-off-by: Paolo Abeni +--- + 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; + diff --git a/target/linux/generic/backport-6.6/750-v6.5-05-net-ethernet-mtk_eth_soc-add-version-in-mtk_soc_data.patch b/target/linux/generic/backport-6.6/750-v6.5-05-net-ethernet-mtk_eth_soc-add-version-in-mtk_soc_data.patch new file mode 100644 index 0000000000..25c87b0415 --- /dev/null +++ b/target/linux/generic/backport-6.6/750-v6.5-05-net-ethernet-mtk_eth_soc-add-version-in-mtk_soc_data.patch @@ -0,0 +1,550 @@ +From 5d8d05fbf804b4485646d39551ac27452e45afd3 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +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 +Signed-off-by: Daniel Golle +Link: https://lore.kernel.org/r/e52fae302ca135436e5cdd26d38d87be2da63055.1690246066.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + 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, diff --git a/target/linux/generic/backport-6.6/750-v6.5-06-net-ethernet-mtk_eth_soc-increase-MAX_DEVS-to-3.patch b/target/linux/generic/backport-6.6/750-v6.5-06-net-ethernet-mtk_eth_soc-increase-MAX_DEVS-to-3.patch new file mode 100644 index 0000000000..7b945bb869 --- /dev/null +++ b/target/linux/generic/backport-6.6/750-v6.5-06-net-ethernet-mtk_eth_soc-increase-MAX_DEVS-to-3.patch @@ -0,0 +1,29 @@ +From f8fb8dbd158c585be7574faf92db7d614b6722ff Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +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 +Signed-off-by: Daniel Golle +Link: https://lore.kernel.org/r/3563e5fab367e7d79a7f1296fabaa5c20f202d7a.1690246066.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + 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 diff --git a/target/linux/generic/backport-6.6/750-v6.5-07-net-ethernet-mtk_eth_soc-rely-on-MTK_MAX_DEVS-and-re.patch b/target/linux/generic/backport-6.6/750-v6.5-07-net-ethernet-mtk_eth_soc-rely-on-MTK_MAX_DEVS-and-re.patch new file mode 100644 index 0000000000..8071658313 --- /dev/null +++ b/target/linux/generic/backport-6.6/750-v6.5-07-net-ethernet-mtk_eth_soc-rely-on-MTK_MAX_DEVS-and-re.patch @@ -0,0 +1,186 @@ +From 856be974290f28d7943be2ac5a382c4139486196 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +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 +Signed-off-by: Daniel Golle +Link: https://lore.kernel.org/r/1856f4266f2fc80677807b1bad867659e7b00c65.1690246066.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + 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 diff --git a/target/linux/generic/backport-6.6/750-v6.5-08-net-ethernet-mtk_eth_soc-add-NETSYS_V3-version-suppo.patch b/target/linux/generic/backport-6.6/750-v6.5-08-net-ethernet-mtk_eth_soc-add-NETSYS_V3-version-suppo.patch new file mode 100644 index 0000000000..1a9b31f526 --- /dev/null +++ b/target/linux/generic/backport-6.6/750-v6.5-08-net-ethernet-mtk_eth_soc-add-NETSYS_V3-version-suppo.patch @@ -0,0 +1,307 @@ +From a41d535855976838d246c079143c948dcf0f7931 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +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 +Signed-off-by: Daniel Golle +Link: https://lore.kernel.org/r/0db2260910755d76fa48e303b9f9bdf4e5a82340.1690246066.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + 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) + { diff --git a/target/linux/generic/backport-6.6/750-v6.5-09-net-ethernet-mtk_eth_soc-convert-caps-in-mtk_soc_dat.patch b/target/linux/generic/backport-6.6/750-v6.5-09-net-ethernet-mtk_eth_soc-convert-caps-in-mtk_soc_dat.patch new file mode 100644 index 0000000000..7a0b285f2c --- /dev/null +++ b/target/linux/generic/backport-6.6/750-v6.5-09-net-ethernet-mtk_eth_soc-convert-caps-in-mtk_soc_dat.patch @@ -0,0 +1,193 @@ +From db797ae0542220a98658229397da464c383c991c Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +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 +Signed-off-by: Daniel Golle +Link: https://lore.kernel.org/r/9499ac3670b2fc5b444404b84e8a4a169beabbf2.1690246066.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + 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; diff --git a/target/linux/generic/backport-6.6/750-v6.5-10-net-ethernet-mtk_eth_soc-convert-clock-bitmap-to-u64.patch b/target/linux/generic/backport-6.6/750-v6.5-10-net-ethernet-mtk_eth_soc-convert-clock-bitmap-to-u64.patch new file mode 100644 index 0000000000..5947d385f2 --- /dev/null +++ b/target/linux/generic/backport-6.6/750-v6.5-10-net-ethernet-mtk_eth_soc-convert-clock-bitmap-to-u64.patch @@ -0,0 +1,132 @@ +From a1c9f7d1d24e90294f6a6755b137fcf306851e93 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Link: https://lore.kernel.org/r/6960a39bb0078cf84d7642a9558e6a91c6cc9df3.1690246066.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + 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; diff --git a/target/linux/generic/backport-6.6/750-v6.5-11-net-ethernet-mtk_eth_soc-add-basic-support-for-MT798.patch b/target/linux/generic/backport-6.6/750-v6.5-11-net-ethernet-mtk_eth_soc-add-basic-support-for-MT798.patch new file mode 100644 index 0000000000..8c24321dd4 --- /dev/null +++ b/target/linux/generic/backport-6.6/750-v6.5-11-net-ethernet-mtk_eth_soc-add-basic-support-for-MT798.patch @@ -0,0 +1,477 @@ +From 94f825a7eadfc8b4c8828efdb7705d9703f9c73e Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +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 +Signed-off-by: Daniel Golle +Link: https://lore.kernel.org/r/25c8377095b95d186872eeda7aa055da83e8f0ca.1690246605.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + 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); diff --git a/target/linux/generic/backport-6.6/750-v6.5-12-net-ethernet-mtk_eth_soc-enable-page_pool-support-fo.patch b/target/linux/generic/backport-6.6/750-v6.5-12-net-ethernet-mtk_eth_soc-enable-page_pool-support-fo.patch new file mode 100644 index 0000000000..3dc4662d1a --- /dev/null +++ b/target/linux/generic/backport-6.6/750-v6.5-12-net-ethernet-mtk_eth_soc-enable-page_pool-support-fo.patch @@ -0,0 +1,27 @@ +From 38a7eb76220731eff40602cf433f24880be0a6c2 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +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 +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/r/fd4e8693980e47385a543e7b002eec0b88bd09df.1690440675.git.lorenzo@kernel.org +Signed-off-by: Jakub Kicinski +--- + 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, diff --git a/target/linux/generic/backport-6.6/750-v6.5-13-net-ethernet-mtk_eth_soc-enable-nft-hw-flowtable_off.patch b/target/linux/generic/backport-6.6/750-v6.5-13-net-ethernet-mtk_eth_soc-enable-nft-hw-flowtable_off.patch new file mode 100644 index 0000000000..32f26d7d27 --- /dev/null +++ b/target/linux/generic/backport-6.6/750-v6.5-13-net-ethernet-mtk_eth_soc-enable-nft-hw-flowtable_off.patch @@ -0,0 +1,135 @@ +From 199e7d5a7f03dd377f3a7a458360dbedd71d50ba Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +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 +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/r/5e86341b0220a49620dadc02d77970de5ded9efc.1690441576.git.lorenzo@kernel.org +Signed-off-by: Jakub Kicinski +--- + 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]; + }; + }; + diff --git a/target/linux/generic/backport-6.6/750-v6.5-14-net-ethernet-mtk_eth_soc-support-per-flow-accounting.patch b/target/linux/generic/backport-6.6/750-v6.5-14-net-ethernet-mtk_eth_soc-support-per-flow-accounting.patch new file mode 100644 index 0000000000..876bdd5dd3 --- /dev/null +++ b/target/linux/generic/backport-6.6/750-v6.5-14-net-ethernet-mtk_eth_soc-support-per-flow-accounting.patch @@ -0,0 +1,78 @@ +From 0c024632c1e7ff69914329bfd87bec749b9c0aed Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Reviewed-by: Simon Horman +Link: https://lore.kernel.org/r/37a0928fa8c1253b197884c68ce1f54239421ac5.1690946442.git.daniel@makrotopia.org +Signed-off-by: Paolo Abeni +--- + 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) diff --git a/target/linux/generic/backport-6.6/750-v6.5-15-net-ethernet-mtk_eth_soc-fix-NULL-pointer-on-hw-rese.patch b/target/linux/generic/backport-6.6/750-v6.5-15-net-ethernet-mtk_eth_soc-fix-NULL-pointer-on-hw-rese.patch new file mode 100644 index 0000000000..6f1639a572 --- /dev/null +++ b/target/linux/generic/backport-6.6/750-v6.5-15-net-ethernet-mtk_eth_soc-fix-NULL-pointer-on-hw-rese.patch @@ -0,0 +1,52 @@ +From 3b12f42772c26869d60398c1710aa27b27cd945c Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Reviewed-by: Simon Horman +Acked-by: Lorenzo Bianconi +Link: https://lore.kernel.org/r/5465c1609b464cc7407ae1530c40821dcdf9d3e6.1692634266.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + 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; + diff --git a/target/linux/generic/backport-6.6/750-v6.5-16-net-ethernet-mtk_eth_soc-fix-register-definitions-fo.patch b/target/linux/generic/backport-6.6/750-v6.5-16-net-ethernet-mtk_eth_soc-fix-register-definitions-fo.patch new file mode 100644 index 0000000000..2190761fdd --- /dev/null +++ b/target/linux/generic/backport-6.6/750-v6.5-16-net-ethernet-mtk_eth_soc-fix-register-definitions-fo.patch @@ -0,0 +1,44 @@ +From 489aea123d74a846ce746bfdb3efe1e7ad512e0d Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Reviewed-by: Simon Horman +Link: https://lore.kernel.org/r/1c8da012e2ca80939906d85f314138c552139f0f.1692721443.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + 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) diff --git a/target/linux/generic/backport-6.6/750-v6.5-17-net-ethernet-mtk_eth_soc-add-reset-bits-for-MT7988.patch b/target/linux/generic/backport-6.6/750-v6.5-17-net-ethernet-mtk_eth_soc-add-reset-bits-for-MT7988.patch new file mode 100644 index 0000000000..05a18364d6 --- /dev/null +++ b/target/linux/generic/backport-6.6/750-v6.5-17-net-ethernet-mtk_eth_soc-add-reset-bits-for-MT7988.patch @@ -0,0 +1,188 @@ +From 15a84d1c44ae8c1451c265ee60500588a24e8cd6 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Link: https://lore.kernel.org/r/89b6c38380e7a3800c1362aa7575600717bc7543.1692721443.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + 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; diff --git a/target/linux/generic/backport-6.6/750-v6.5-18-net-ethernet-mtk_eth_soc-add-support-for-in-SoC-SRAM.patch b/target/linux/generic/backport-6.6/750-v6.5-18-net-ethernet-mtk_eth_soc-add-support-for-in-SoC-SRAM.patch new file mode 100644 index 0000000000..74ac8dc898 --- /dev/null +++ b/target/linux/generic/backport-6.6/750-v6.5-18-net-ethernet-mtk_eth_soc-add-support-for-in-SoC-SRAM.patch @@ -0,0 +1,254 @@ +From 25ce45fe40b574e5d7ffa407f7f2db03e7d5a910 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Link: https://lore.kernel.org/r/e45e0f230c63ad58869e8fe35b95a2fb8925b625.1692721443.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + 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; diff --git a/target/linux/generic/backport-6.6/750-v6.5-19-net-ethernet-mtk_eth_soc-support-36-bit-DMA-addressi.patch b/target/linux/generic/backport-6.6/750-v6.5-19-net-ethernet-mtk_eth_soc-support-36-bit-DMA-addressi.patch new file mode 100644 index 0000000000..1584dfd07c --- /dev/null +++ b/target/linux/generic/backport-6.6/750-v6.5-19-net-ethernet-mtk_eth_soc-support-36-bit-DMA-addressi.patch @@ -0,0 +1,166 @@ +From 0b0d606eb9650fa01dd5621e072aa29a10544399 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Link: https://lore.kernel.org/r/95b919c98876c9e49761e44662e7c937479eecb8.1692721443.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + 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; diff --git a/target/linux/generic/backport-6.6/750-v6.5-20-net-ethernet-mtk_eth_soc-fix-uninitialized-variable.patch b/target/linux/generic/backport-6.6/750-v6.5-20-net-ethernet-mtk_eth_soc-fix-uninitialized-variable.patch new file mode 100644 index 0000000000..5b27458eb8 --- /dev/null +++ b/target/linux/generic/backport-6.6/750-v6.5-20-net-ethernet-mtk_eth_soc-fix-uninitialized-variable.patch @@ -0,0 +1,44 @@ +From e10a35abb3da12b812cfb6fc6137926a0c81e39a Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Signed-off-by: David S. Miller +--- + 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; diff --git a/target/linux/generic/backport-6.6/750-v6.5-21-net-ethernet-mtk_eth_soc-fix-pse_port-configuration-.patch b/target/linux/generic/backport-6.6/750-v6.5-21-net-ethernet-mtk_eth_soc-fix-pse_port-configuration-.patch new file mode 100644 index 0000000000..ac3e3a3e67 --- /dev/null +++ b/target/linux/generic/backport-6.6/750-v6.5-21-net-ethernet-mtk_eth_soc-fix-pse_port-configuration-.patch @@ -0,0 +1,33 @@ +From 5a124b1fd3e6cb15a943f0cdfe96aa8f6d3d2f39 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +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 +Signed-off-by: David S. Miller +--- + 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; + diff --git a/target/linux/generic/backport-6.6/751-01-v6.4-net-ethernet-mtk_eth_soc-add-code-for-offloading-flo.patch b/target/linux/generic/backport-6.6/751-01-v6.4-net-ethernet-mtk_eth_soc-add-code-for-offloading-flo.patch new file mode 100644 index 0000000000..46da5b283f --- /dev/null +++ b/target/linux/generic/backport-6.6/751-01-v6.4-net-ethernet-mtk_eth_soc-add-code-for-offloading-flo.patch @@ -0,0 +1,271 @@ +From: Felix Fietkau +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 +--- + 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 + #include + #include ++#include ++#include + #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 + #include + #include ++#include + + #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 diff --git a/target/linux/generic/backport-6.6/751-02-v6.4-net-ethernet-mediatek-mtk_ppe-prefer-newly-added-l2-.patch b/target/linux/generic/backport-6.6/751-02-v6.4-net-ethernet-mediatek-mtk_ppe-prefer-newly-added-l2-.patch new file mode 100644 index 0000000000..b1796641a3 --- /dev/null +++ b/target/linux/generic/backport-6.6/751-02-v6.4-net-ethernet-mediatek-mtk_ppe-prefer-newly-added-l2-.patch @@ -0,0 +1,37 @@ +From: Felix Fietkau +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 +--- + +--- 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) diff --git a/target/linux/generic/backport-6.6/751-03-v6.4-net-ethernet-mtk_eth_soc-improve-keeping-track-of-of.patch b/target/linux/generic/backport-6.6/751-03-v6.4-net-ethernet-mtk_eth_soc-improve-keeping-track-of-of.patch new file mode 100644 index 0000000000..8b8a8e11c7 --- /dev/null +++ b/target/linux/generic/backport-6.6/751-03-v6.4-net-ethernet-mtk_eth_soc-improve-keeping-track-of-of.patch @@ -0,0 +1,334 @@ +From: Felix Fietkau +Date: Thu, 23 Mar 2023 10:24:11 +0100 +Subject: [PATCH] net: ethernet: mtk_eth_soc: improve keeping track of + offloaded flows + +Unify tracking of L2 and L3 flows. Use the generic list field in struct +mtk_foe_entry for tracking L2 subflows. Preparation for improving +flow accounting support. + +Signed-off-by: Felix Fietkau +--- + drivers/net/ethernet/mediatek/mtk_ppe.c | 162 ++++++++++++------------ + drivers/net/ethernet/mediatek/mtk_ppe.h | 15 +-- + 2 files changed, 86 insertions(+), 91 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_ppe.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c +@@ -476,42 +476,43 @@ int mtk_foe_entry_set_queue(struct mtk_e + return 0; + } + ++static int ++mtk_flow_entry_match_len(struct mtk_eth *eth, struct mtk_foe_entry *entry) ++{ ++ int type = mtk_get_ib1_pkt_type(eth, entry->ib1); ++ ++ if (type > MTK_PPE_PKT_TYPE_IPV4_DSLITE) ++ return offsetof(struct mtk_foe_entry, ipv6._rsv); ++ else ++ return offsetof(struct mtk_foe_entry, ipv4.ib2); ++} ++ + static bool + mtk_flow_entry_match(struct mtk_eth *eth, struct mtk_flow_entry *entry, +- struct mtk_foe_entry *data) ++ struct mtk_foe_entry *data, int len) + { +- int type, len; +- + if ((data->ib1 ^ entry->data.ib1) & MTK_FOE_IB1_UDP) + return false; + +- type = mtk_get_ib1_pkt_type(eth, entry->data.ib1); +- if (type > MTK_PPE_PKT_TYPE_IPV4_DSLITE) +- len = offsetof(struct mtk_foe_entry, ipv6._rsv); +- else +- len = offsetof(struct mtk_foe_entry, ipv4.ib2); +- + return !memcmp(&entry->data.data, &data->data, len - 4); + } + + static void +-__mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) ++__mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry, ++ bool set_state) + { +- struct hlist_head *head; + struct hlist_node *tmp; + + if (entry->type == MTK_FLOW_TYPE_L2) { + rhashtable_remove_fast(&ppe->l2_flows, &entry->l2_node, + mtk_flow_l2_ht_params); + +- head = &entry->l2_flows; +- hlist_for_each_entry_safe(entry, tmp, head, l2_data.list) +- __mtk_foe_entry_clear(ppe, entry); ++ hlist_for_each_entry_safe(entry, tmp, &entry->l2_flows, l2_list) ++ __mtk_foe_entry_clear(ppe, entry, set_state); + return; + } + +- hlist_del_init(&entry->list); +- if (entry->hash != 0xffff) { ++ if (entry->hash != 0xffff && set_state) { + struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, entry->hash); + + hwe->ib1 &= ~MTK_FOE_IB1_STATE; +@@ -531,7 +532,8 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp + if (entry->type != MTK_FLOW_TYPE_L2_SUBFLOW) + return; + +- hlist_del_init(&entry->l2_data.list); ++ hlist_del_init(&entry->l2_list); ++ hlist_del_init(&entry->list); + kfree(entry); + } + +@@ -547,66 +549,55 @@ static int __mtk_foe_entry_idle_time(str + return now - timestamp; + } + ++static bool ++mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) ++{ ++ struct mtk_foe_entry foe = {}; ++ struct mtk_foe_entry *hwe; ++ u16 hash = entry->hash; ++ int len; ++ ++ if (hash == 0xffff) ++ return false; ++ ++ hwe = mtk_foe_get_entry(ppe, hash); ++ len = mtk_flow_entry_match_len(ppe->eth, &entry->data); ++ memcpy(&foe, hwe, len); ++ ++ if (!mtk_flow_entry_match(ppe->eth, entry, &foe, len) || ++ FIELD_GET(MTK_FOE_IB1_STATE, foe.ib1) != MTK_FOE_STATE_BIND) ++ return false; ++ ++ entry->data.ib1 = foe.ib1; ++ ++ return true; ++} ++ + static void + mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + { + u32 ib1_ts_mask = mtk_get_ib1_ts_mask(ppe->eth); + struct mtk_flow_entry *cur; +- struct mtk_foe_entry *hwe; + struct hlist_node *tmp; + int idle; + + idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1); +- hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_data.list) { ++ hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_list) { + int cur_idle; +- u32 ib1; +- +- hwe = mtk_foe_get_entry(ppe, cur->hash); +- ib1 = READ_ONCE(hwe->ib1); + +- if (FIELD_GET(MTK_FOE_IB1_STATE, ib1) != MTK_FOE_STATE_BIND) { +- cur->hash = 0xffff; +- __mtk_foe_entry_clear(ppe, cur); ++ if (!mtk_flow_entry_update(ppe, cur)) { ++ __mtk_foe_entry_clear(ppe, entry, false); + continue; + } + +- cur_idle = __mtk_foe_entry_idle_time(ppe, ib1); ++ cur_idle = __mtk_foe_entry_idle_time(ppe, cur->data.ib1); + if (cur_idle >= idle) + continue; + + idle = cur_idle; + entry->data.ib1 &= ~ib1_ts_mask; +- entry->data.ib1 |= hwe->ib1 & ib1_ts_mask; +- } +-} +- +-static void +-mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) +-{ +- struct mtk_foe_entry foe = {}; +- struct mtk_foe_entry *hwe; +- +- spin_lock_bh(&ppe_lock); +- +- if (entry->type == MTK_FLOW_TYPE_L2) { +- mtk_flow_entry_update_l2(ppe, entry); +- goto out; ++ entry->data.ib1 |= cur->data.ib1 & ib1_ts_mask; + } +- +- if (entry->hash == 0xffff) +- goto out; +- +- hwe = mtk_foe_get_entry(ppe, entry->hash); +- memcpy(&foe, hwe, ppe->eth->soc->foe_entry_size); +- if (!mtk_flow_entry_match(ppe->eth, entry, &foe)) { +- entry->hash = 0xffff; +- goto out; +- } +- +- entry->data.ib1 = foe.ib1; +- +-out: +- spin_unlock_bh(&ppe_lock); + } + + static void +@@ -649,7 +640,8 @@ __mtk_foe_entry_commit(struct mtk_ppe *p + void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + { + spin_lock_bh(&ppe_lock); +- __mtk_foe_entry_clear(ppe, entry); ++ __mtk_foe_entry_clear(ppe, entry, true); ++ hlist_del_init(&entry->list); + spin_unlock_bh(&ppe_lock); + } + +@@ -696,8 +688,8 @@ mtk_foe_entry_commit_subflow(struct mtk_ + { + const struct mtk_soc_data *soc = ppe->eth->soc; + struct mtk_flow_entry *flow_info; +- struct mtk_foe_entry foe = {}, *hwe; + struct mtk_foe_mac_info *l2; ++ struct mtk_foe_entry *hwe; + u32 ib1_mask = mtk_get_ib1_pkt_type_mask(ppe->eth) | MTK_FOE_IB1_UDP; + int type; + +@@ -705,30 +697,30 @@ mtk_foe_entry_commit_subflow(struct mtk_ + if (!flow_info) + return; + +- flow_info->l2_data.base_flow = entry; + flow_info->type = MTK_FLOW_TYPE_L2_SUBFLOW; + flow_info->hash = hash; + hlist_add_head(&flow_info->list, + &ppe->foe_flow[hash / soc->hash_offset]); +- hlist_add_head(&flow_info->l2_data.list, &entry->l2_flows); ++ hlist_add_head(&flow_info->l2_list, &entry->l2_flows); + + hwe = mtk_foe_get_entry(ppe, hash); +- memcpy(&foe, hwe, soc->foe_entry_size); +- foe.ib1 &= ib1_mask; +- foe.ib1 |= entry->data.ib1 & ~ib1_mask; ++ memcpy(&flow_info->data, hwe, soc->foe_entry_size); ++ flow_info->data.ib1 &= ib1_mask; ++ flow_info->data.ib1 |= entry->data.ib1 & ~ib1_mask; + +- l2 = mtk_foe_entry_l2(ppe->eth, &foe); ++ l2 = mtk_foe_entry_l2(ppe->eth, &flow_info->data); + memcpy(l2, &entry->data.bridge.l2, sizeof(*l2)); + +- type = mtk_get_ib1_pkt_type(ppe->eth, foe.ib1); ++ type = mtk_get_ib1_pkt_type(ppe->eth, flow_info->data.ib1); + if (type == MTK_PPE_PKT_TYPE_IPV4_HNAPT) +- memcpy(&foe.ipv4.new, &foe.ipv4.orig, sizeof(foe.ipv4.new)); ++ memcpy(&flow_info->data.ipv4.new, &flow_info->data.ipv4.orig, ++ sizeof(flow_info->data.ipv4.new)); + else if (type >= MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T && l2->etype == ETH_P_IP) + l2->etype = ETH_P_IPV6; + +- *mtk_foe_entry_ib2(ppe->eth, &foe) = entry->data.bridge.ib2; ++ *mtk_foe_entry_ib2(ppe->eth, &flow_info->data) = entry->data.bridge.ib2; + +- __mtk_foe_entry_commit(ppe, &foe, hash); ++ __mtk_foe_entry_commit(ppe, &flow_info->data, hash); + } + + void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash) +@@ -738,9 +730,11 @@ void __mtk_ppe_check_skb(struct mtk_ppe + struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, hash); + struct mtk_flow_entry *entry; + struct mtk_foe_bridge key = {}; ++ struct mtk_foe_entry foe = {}; + struct hlist_node *n; + struct ethhdr *eh; + bool found = false; ++ int entry_len; + u8 *tag; + + spin_lock_bh(&ppe_lock); +@@ -748,20 +742,14 @@ void __mtk_ppe_check_skb(struct mtk_ppe + if (FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) == MTK_FOE_STATE_BIND) + goto out; + +- hlist_for_each_entry_safe(entry, n, head, list) { +- if (entry->type == MTK_FLOW_TYPE_L2_SUBFLOW) { +- if (unlikely(FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) == +- MTK_FOE_STATE_BIND)) +- continue; +- +- entry->hash = 0xffff; +- __mtk_foe_entry_clear(ppe, entry); +- continue; +- } ++ entry_len = mtk_flow_entry_match_len(ppe->eth, hwe); ++ memcpy(&foe, hwe, entry_len); + +- if (found || !mtk_flow_entry_match(ppe->eth, entry, hwe)) { ++ hlist_for_each_entry_safe(entry, n, head, list) { ++ if (found || ++ !mtk_flow_entry_match(ppe->eth, entry, &foe, entry_len)) { + if (entry->hash != 0xffff) +- entry->hash = 0xffff; ++ __mtk_foe_entry_clear(ppe, entry, false); + continue; + } + +@@ -810,9 +798,17 @@ out: + + int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + { +- mtk_flow_entry_update(ppe, entry); ++ int idle; ++ ++ spin_lock_bh(&ppe_lock); ++ if (entry->type == MTK_FLOW_TYPE_L2) ++ mtk_flow_entry_update_l2(ppe, entry); ++ else ++ mtk_flow_entry_update(ppe, entry); ++ idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1); ++ spin_unlock_bh(&ppe_lock); + +- return __mtk_foe_entry_idle_time(ppe, entry->data.ib1); ++ return idle; + } + + int mtk_ppe_prepare_reset(struct mtk_ppe *ppe) +--- a/drivers/net/ethernet/mediatek/mtk_ppe.h ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h +@@ -286,7 +286,12 @@ enum { + + struct mtk_flow_entry { + union { +- struct hlist_node list; ++ /* regular flows + L2 subflows */ ++ struct { ++ struct hlist_node list; ++ struct hlist_node l2_list; ++ }; ++ /* L2 flows */ + struct { + struct rhash_head l2_node; + struct hlist_head l2_flows; +@@ -296,13 +301,7 @@ struct mtk_flow_entry { + s8 wed_index; + u8 ppe_index; + u16 hash; +- union { +- struct mtk_foe_entry data; +- struct { +- struct mtk_flow_entry *base_flow; +- struct hlist_node list; +- } l2_data; +- }; ++ struct mtk_foe_entry data; + struct rhash_head node; + unsigned long cookie; + }; diff --git a/target/linux/generic/backport-6.6/751-04-v6.4-net-ethernet-mediatek-fix-ppe-flow-accounting-for-L2.patch b/target/linux/generic/backport-6.6/751-04-v6.4-net-ethernet-mediatek-fix-ppe-flow-accounting-for-L2.patch new file mode 100644 index 0000000000..e20f94f1d4 --- /dev/null +++ b/target/linux/generic/backport-6.6/751-04-v6.4-net-ethernet-mediatek-fix-ppe-flow-accounting-for-L2.patch @@ -0,0 +1,342 @@ +From: Felix Fietkau +Date: Thu, 23 Mar 2023 11:05:22 +0100 +Subject: [PATCH] net: ethernet: mediatek: fix ppe flow accounting for L2 + flows + +For L2 flows, the packet/byte counters should report the sum of the +counters of their subflows, both current and expired. +In order to make this work, change the way that accounting data is tracked. +Reset counters when a flow enters bind. Once it expires (or enters unbind), +store the last counter value in struct mtk_flow_entry. + +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ethernet/mediatek/mtk_ppe.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c +@@ -79,9 +79,9 @@ static int mtk_ppe_mib_wait_busy(struct + 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); ++ ret = readl_poll_timeout_atomic(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"); +@@ -89,17 +89,31 @@ static int mtk_ppe_mib_wait_busy(struct + return ret; + } + +-static int mtk_mib_entry_read(struct mtk_ppe *ppe, u16 index, u64 *bytes, u64 *packets) ++static inline struct mtk_foe_accounting * ++mtk_ppe_acct_data(struct mtk_ppe *ppe, u16 index) ++{ ++ if (!ppe->acct_table) ++ return NULL; ++ ++ return ppe->acct_table + index * sizeof(struct mtk_foe_accounting); ++} ++ ++struct mtk_foe_accounting *mtk_ppe_mib_entry_read(struct mtk_ppe *ppe, u16 index) + { + u32 val, cnt_r0, cnt_r1, cnt_r2; ++ struct mtk_foe_accounting *acct; + 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); + ++ acct = mtk_ppe_acct_data(ppe, index); ++ if (!acct) ++ return NULL; ++ + ret = mtk_ppe_mib_wait_busy(ppe); + if (ret) +- return ret; ++ return acct; + + cnt_r0 = readl(ppe->base + MTK_PPE_MIB_SER_R0); + cnt_r1 = readl(ppe->base + MTK_PPE_MIB_SER_R1); +@@ -108,19 +122,19 @@ static int mtk_mib_entry_read(struct mtk + 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; ++ acct->bytes += ((u64)cnt_r1 << 32) | cnt_r0; ++ acct->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; ++ acct->bytes += ((u64)byte_cnt_high << 32) | byte_cnt_low; ++ acct->packets += (pkt_cnt_high << 16) | pkt_cnt_low; + } + +- return 0; ++ return acct; + } + + static void mtk_ppe_cache_clear(struct mtk_ppe *ppe) +@@ -519,13 +533,6 @@ __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; + +@@ -550,11 +557,14 @@ static int __mtk_foe_entry_idle_time(str + } + + static bool +-mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) ++mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry, ++ u64 *packets, u64 *bytes) + { ++ struct mtk_foe_accounting *acct; + struct mtk_foe_entry foe = {}; + struct mtk_foe_entry *hwe; + u16 hash = entry->hash; ++ bool ret = false; + int len; + + if (hash == 0xffff) +@@ -565,18 +575,35 @@ mtk_flow_entry_update(struct mtk_ppe *pp + memcpy(&foe, hwe, len); + + if (!mtk_flow_entry_match(ppe->eth, entry, &foe, len) || +- FIELD_GET(MTK_FOE_IB1_STATE, foe.ib1) != MTK_FOE_STATE_BIND) +- return false; ++ FIELD_GET(MTK_FOE_IB1_STATE, foe.ib1) != MTK_FOE_STATE_BIND) { ++ acct = mtk_ppe_acct_data(ppe, hash); ++ if (acct) { ++ entry->prev_packets += acct->packets; ++ entry->prev_bytes += acct->bytes; ++ } ++ ++ goto out; ++ } + + entry->data.ib1 = foe.ib1; ++ acct = mtk_ppe_mib_entry_read(ppe, hash); ++ ret = true; ++ ++out: ++ if (acct) { ++ *packets += acct->packets; ++ *bytes += acct->bytes; ++ } + +- return true; ++ return ret; + } + + static void + mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + { + u32 ib1_ts_mask = mtk_get_ib1_ts_mask(ppe->eth); ++ u64 *packets = &entry->packets; ++ u64 *bytes = &entry->bytes; + struct mtk_flow_entry *cur; + struct hlist_node *tmp; + int idle; +@@ -585,7 +612,9 @@ mtk_flow_entry_update_l2(struct mtk_ppe + hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_list) { + int cur_idle; + +- if (!mtk_flow_entry_update(ppe, cur)) { ++ if (!mtk_flow_entry_update(ppe, cur, packets, bytes)) { ++ entry->prev_packets += cur->prev_packets; ++ entry->prev_bytes += cur->prev_bytes; + __mtk_foe_entry_clear(ppe, entry, false); + continue; + } +@@ -600,10 +629,29 @@ mtk_flow_entry_update_l2(struct mtk_ppe + } + } + ++void mtk_foe_entry_get_stats(struct mtk_ppe *ppe, struct mtk_flow_entry *entry, ++ int *idle) ++{ ++ entry->packets = entry->prev_packets; ++ entry->bytes = entry->prev_bytes; ++ ++ spin_lock_bh(&ppe_lock); ++ ++ if (entry->type == MTK_FLOW_TYPE_L2) ++ mtk_flow_entry_update_l2(ppe, entry); ++ else ++ mtk_flow_entry_update(ppe, entry, &entry->packets, &entry->bytes); ++ ++ *idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1); ++ ++ spin_unlock_bh(&ppe_lock); ++} ++ + static void + __mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry, + u16 hash) + { ++ struct mtk_foe_accounting *acct; + struct mtk_eth *eth = ppe->eth; + u16 timestamp = mtk_eth_timestamp(eth); + struct mtk_foe_entry *hwe; +@@ -634,6 +682,12 @@ __mtk_foe_entry_commit(struct mtk_ppe *p + + dma_wmb(); + ++ acct = mtk_ppe_mib_entry_read(ppe, hash); ++ if (acct) { ++ acct->packets = 0; ++ acct->bytes = 0; ++ } ++ + mtk_ppe_cache_clear(ppe); + } + +@@ -796,21 +850,6 @@ out: + spin_unlock_bh(&ppe_lock); + } + +-int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) +-{ +- int idle; +- +- spin_lock_bh(&ppe_lock); +- if (entry->type == MTK_FLOW_TYPE_L2) +- mtk_flow_entry_update_l2(ppe, entry); +- else +- mtk_flow_entry_update(ppe, entry); +- idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1); +- spin_unlock_bh(&ppe_lock); +- +- return idle; +-} +- + int mtk_ppe_prepare_reset(struct mtk_ppe *ppe) + { + if (!ppe) +@@ -838,32 +877,6 @@ int mtk_ppe_prepare_reset(struct mtk_ppe + return mtk_ppe_wait_busy(ppe); + } + +-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; +--- a/drivers/net/ethernet/mediatek/mtk_ppe.h ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h +@@ -304,6 +304,8 @@ struct mtk_flow_entry { + struct mtk_foe_entry data; + struct rhash_head node; + unsigned long cookie; ++ u64 prev_packets, prev_bytes; ++ u64 packets, bytes; + }; + + struct mtk_mib_entry { +@@ -348,6 +350,7 @@ 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); ++struct mtk_foe_accounting *mtk_ppe_mib_entry_read(struct mtk_ppe *ppe, u16 index); + + void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash); + +@@ -396,9 +399,8 @@ int mtk_foe_entry_set_queue(struct mtk_e + 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); + 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); ++void mtk_foe_entry_get_stats(struct mtk_ppe *ppe, struct mtk_flow_entry *entry, ++ int *idle); + + #endif +--- a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c +@@ -96,7 +96,7 @@ 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); ++ acct = mtk_ppe_mib_entry_read(ppe, i); + + type = mtk_get_ib1_pkt_type(ppe->eth, entry->ib1); + seq_printf(m, "%05x %s %7s", i, +--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +@@ -501,24 +501,21 @@ 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; ++ u64 packets, bytes; ++ int idle; + + entry = rhashtable_lookup(ð->flow_table, &f->cookie, + mtk_flow_ht_params); + if (!entry) + return -ENOENT; + +- idle = mtk_foe_entry_idle_time(eth->ppe[entry->ppe_index], entry); ++ packets = entry->packets; ++ bytes = entry->bytes; ++ mtk_foe_entry_get_stats(eth->ppe[entry->ppe_index], entry, &idle); ++ f->stats.pkts += entry->packets - packets; ++ f->stats.bytes += entry->bytes - bytes; + 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; + } + diff --git a/target/linux/generic/backport-6.6/752-01-v6.6-net-ethernet-mtk_wed-add-some-more-info-in-wed_txinf.patch b/target/linux/generic/backport-6.6/752-01-v6.6-net-ethernet-mtk_wed-add-some-more-info-in-wed_txinf.patch new file mode 100644 index 0000000000..a224b62624 --- /dev/null +++ b/target/linux/generic/backport-6.6/752-01-v6.6-net-ethernet-mtk_wed-add-some-more-info-in-wed_txinf.patch @@ -0,0 +1,45 @@ +From: Lorenzo Bianconi +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 +Link: https://lore.kernel.org/r/3390292655d568180b73d2a25576f61aa63310e5.1693157377.git.lorenzo@kernel.org +Signed-off-by: Jakub Kicinski +--- + +--- 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) diff --git a/target/linux/generic/backport-6.6/752-02-v6.6-net-ethernet-mtk_wed-minor-change-in-wed_-tx-rx-info.patch b/target/linux/generic/backport-6.6/752-02-v6.6-net-ethernet-mtk_wed-minor-change-in-wed_-tx-rx-info.patch new file mode 100644 index 0000000000..df6edfdf94 --- /dev/null +++ b/target/linux/generic/backport-6.6/752-02-v6.6-net-ethernet-mtk_wed-minor-change-in-wed_-tx-rx-info.patch @@ -0,0 +1,47 @@ +From: Lorenzo Bianconi +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 +Link: https://lore.kernel.org/r/71e046c72a978745f0435af265dda610aa9bfbcf.1693157578.git.lorenzo@kernel.org +Signed-off-by: Jakub Kicinski +--- + +--- 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; + } diff --git a/target/linux/generic/backport-6.6/752-03-v6.6-net-ethernet-mtk_eth_soc-rely-on-mtk_pse_port-defini.patch b/target/linux/generic/backport-6.6/752-03-v6.6-net-ethernet-mtk_eth_soc-rely-on-mtk_pse_port-defini.patch new file mode 100644 index 0000000000..0bf9dea24f --- /dev/null +++ b/target/linux/generic/backport-6.6/752-03-v6.6-net-ethernet-mtk_eth_soc-rely-on-mtk_pse_port-defini.patch @@ -0,0 +1,29 @@ +From: Lorenzo Bianconi +Date: Tue, 12 Sep 2023 10:22:56 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: rely on mtk_pse_port definitions + in mtk_flow_set_output_device + +Similar to ethernet ports, rely on mtk_pse_port definitions for +pse wdma ports as well. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://lore.kernel.org/r/b86bdb717e963e3246c1dec5f736c810703cf056.1694506814.git.lorenzo@kernel.org +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +@@ -196,10 +196,10 @@ mtk_flow_set_output_device(struct mtk_et + if (mtk_is_netsys_v2_or_greater(eth)) { + switch (info.wdma_idx) { + case 0: +- pse_port = 8; ++ pse_port = PSE_WDMA0_PORT; + break; + case 1: +- pse_port = 9; ++ pse_port = PSE_WDMA1_PORT; + break; + default: + return -EINVAL; diff --git a/target/linux/generic/backport-6.6/752-04-v6.6-net-ethernet-mtk_wed-check-update_wo_rx_stats-in-mtk.patch b/target/linux/generic/backport-6.6/752-04-v6.6-net-ethernet-mtk_wed-check-update_wo_rx_stats-in-mtk.patch new file mode 100644 index 0000000000..c99e1334d4 --- /dev/null +++ b/target/linux/generic/backport-6.6/752-04-v6.6-net-ethernet-mtk_wed-check-update_wo_rx_stats-in-mtk.patch @@ -0,0 +1,26 @@ +From: Lorenzo Bianconi +Date: Tue, 12 Sep 2023 10:28:00 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: check update_wo_rx_stats in + mtk_wed_update_rx_stats() + +Check if update_wo_rx_stats function pointer is properly set in +mtk_wed_update_rx_stats routine before accessing it. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://lore.kernel.org/r/b0d233386e059bccb59f18f69afb79a7806e5ded.1694507226.git.lorenzo@kernel.org +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c +@@ -68,6 +68,9 @@ mtk_wed_update_rx_stats(struct mtk_wed_d + struct mtk_wed_wo_rx_stats *stats; + int i; + ++ if (!wed->wlan.update_wo_rx_stats) ++ return; ++ + if (count * sizeof(*stats) > skb->len - sizeof(u32)) + return; + diff --git a/target/linux/generic/backport-6.6/752-05-v6.7-net-ethernet-mtk_wed-do-not-assume-offload-callbacks.patch b/target/linux/generic/backport-6.6/752-05-v6.7-net-ethernet-mtk_wed-do-not-assume-offload-callbacks.patch new file mode 100644 index 0000000000..cd7fb92e20 --- /dev/null +++ b/target/linux/generic/backport-6.6/752-05-v6.7-net-ethernet-mtk_wed-do-not-assume-offload-callbacks.patch @@ -0,0 +1,68 @@ +From: Lorenzo Bianconi +Date: Wed, 13 Sep 2023 20:42:47 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: do not assume offload callbacks are + always set + +Check if wlan.offload_enable and wlan.offload_disable callbacks are set +in mtk_wed_flow_add/mtk_wed_flow_remove since mt7996 will not rely +on them. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Signed-off-by: David S. Miller +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -1712,19 +1712,20 @@ mtk_wed_irq_set_mask(struct mtk_wed_devi + int mtk_wed_flow_add(int index) + { + struct mtk_wed_hw *hw = hw_list[index]; +- int ret; ++ int ret = 0; + +- if (!hw || !hw->wed_dev) +- return -ENODEV; ++ mutex_lock(&hw_lock); + +- if (hw->num_flows) { +- hw->num_flows++; +- return 0; ++ if (!hw || !hw->wed_dev) { ++ ret = -ENODEV; ++ goto out; + } + +- mutex_lock(&hw_lock); +- if (!hw->wed_dev) { +- ret = -ENODEV; ++ if (!hw->wed_dev->wlan.offload_enable) ++ goto out; ++ ++ if (hw->num_flows) { ++ hw->num_flows++; + goto out; + } + +@@ -1743,14 +1744,15 @@ void mtk_wed_flow_remove(int index) + { + struct mtk_wed_hw *hw = hw_list[index]; + +- if (!hw) +- return; ++ mutex_lock(&hw_lock); + +- if (--hw->num_flows) +- return; ++ if (!hw || !hw->wed_dev) ++ goto out; + +- mutex_lock(&hw_lock); +- if (!hw->wed_dev) ++ if (!hw->wed_dev->wlan.offload_disable) ++ goto out; ++ ++ if (--hw->num_flows) + goto out; + + hw->wed_dev->wlan.offload_disable(hw->wed_dev); diff --git a/target/linux/generic/backport-6.6/752-06-v6.7-net-ethernet-mtk_wed-introduce-versioning-utility-ro.patch b/target/linux/generic/backport-6.6/752-06-v6.7-net-ethernet-mtk_wed-introduce-versioning-utility-ro.patch new file mode 100644 index 0000000000..2948188650 --- /dev/null +++ b/target/linux/generic/backport-6.6/752-06-v6.7-net-ethernet-mtk_wed-introduce-versioning-utility-ro.patch @@ -0,0 +1,232 @@ +From: Lorenzo Bianconi +Date: Mon, 18 Sep 2023 12:29:05 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: introduce versioning utility routines + +Similar to mtk_eth_soc, introduce the following wed versioning +utility routines: +- mtk_wed_is_v1 +- mtk_wed_is_v2 + +This is a preliminary patch to introduce WED support for MT7988 SoC + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -277,7 +277,7 @@ mtk_wed_assign(struct mtk_wed_device *de + if (!hw->wed_dev) + goto out; + +- if (hw->version == 1) ++ if (mtk_wed_is_v1(hw)) + return NULL; + + /* MT7986 WED devices do not have any pcie slot restrictions */ +@@ -358,7 +358,7 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_d + desc->buf0 = cpu_to_le32(buf_phys); + desc->buf1 = cpu_to_le32(buf_phys + txd_size); + +- if (dev->hw->version == 1) ++ if (mtk_wed_is_v1(dev->hw)) + ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, txd_size) | + FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1, + MTK_WED_BUF_SIZE - txd_size) | +@@ -497,7 +497,7 @@ mtk_wed_set_ext_int(struct mtk_wed_devic + { + u32 mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK; + +- if (dev->hw->version == 1) ++ if (mtk_wed_is_v1(dev->hw)) + mask |= MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR; + else + mask |= MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH | +@@ -576,7 +576,7 @@ mtk_wed_dma_disable(struct mtk_wed_devic + MTK_WDMA_GLO_CFG_RX_INFO1_PRERES | + MTK_WDMA_GLO_CFG_RX_INFO2_PRERES); + +- if (dev->hw->version == 1) { ++ if (mtk_wed_is_v1(dev->hw)) { + regmap_write(dev->hw->mirror, dev->hw->index * 4, 0); + wdma_clr(dev, MTK_WDMA_GLO_CFG, + MTK_WDMA_GLO_CFG_RX_INFO3_PRERES); +@@ -605,7 +605,7 @@ mtk_wed_stop(struct mtk_wed_device *dev) + wdma_w32(dev, MTK_WDMA_INT_GRP2, 0); + wed_w32(dev, MTK_WED_WPDMA_INT_MASK, 0); + +- if (dev->hw->version == 1) ++ if (mtk_wed_is_v1(dev->hw)) + return; + + wed_w32(dev, MTK_WED_EXT_INT_MASK1, 0); +@@ -624,7 +624,7 @@ mtk_wed_deinit(struct mtk_wed_device *de + MTK_WED_CTRL_WED_TX_BM_EN | + MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); + +- if (dev->hw->version == 1) ++ if (mtk_wed_is_v1(dev->hw)) + return; + + wed_clr(dev, MTK_WED_CTRL, +@@ -730,7 +730,7 @@ mtk_wed_bus_init(struct mtk_wed_device * + static void + mtk_wed_set_wpdma(struct mtk_wed_device *dev) + { +- if (dev->hw->version == 1) { ++ if (mtk_wed_is_v1(dev->hw)) { + wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_phys); + } else { + mtk_wed_bus_init(dev); +@@ -761,7 +761,7 @@ mtk_wed_hw_init_early(struct mtk_wed_dev + MTK_WED_WDMA_GLO_CFG_IDLE_DMAD_SUPPLY; + wed_m32(dev, MTK_WED_WDMA_GLO_CFG, mask, set); + +- if (dev->hw->version == 1) { ++ if (mtk_wed_is_v1(dev->hw)) { + u32 offset = dev->hw->index ? 0x04000400 : 0; + + wdma_set(dev, MTK_WDMA_GLO_CFG, +@@ -934,7 +934,7 @@ mtk_wed_hw_init(struct mtk_wed_device *d + + wed_w32(dev, MTK_WED_TX_BM_BUF_LEN, MTK_WED_PKT_SIZE); + +- if (dev->hw->version == 1) { ++ if (mtk_wed_is_v1(dev->hw)) { + wed_w32(dev, MTK_WED_TX_BM_TKID, + FIELD_PREP(MTK_WED_TX_BM_TKID_START, + dev->wlan.token_start) | +@@ -967,7 +967,7 @@ mtk_wed_hw_init(struct mtk_wed_device *d + + mtk_wed_reset(dev, MTK_WED_RESET_TX_BM); + +- if (dev->hw->version == 1) { ++ if (mtk_wed_is_v1(dev->hw)) { + wed_set(dev, MTK_WED_CTRL, + MTK_WED_CTRL_WED_TX_BM_EN | + MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); +@@ -1217,7 +1217,7 @@ mtk_wed_reset_dma(struct mtk_wed_device + } + + dev->init_done = false; +- if (dev->hw->version == 1) ++ if (mtk_wed_is_v1(dev->hw)) + return; + + if (!busy) { +@@ -1343,7 +1343,7 @@ mtk_wed_configure_irq(struct mtk_wed_dev + MTK_WED_CTRL_WED_TX_BM_EN | + MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); + +- if (dev->hw->version == 1) { ++ if (mtk_wed_is_v1(dev->hw)) { + wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, + MTK_WED_PCIE_INT_TRIGGER_STATUS); + +@@ -1416,7 +1416,7 @@ mtk_wed_dma_enable(struct mtk_wed_device + MTK_WDMA_GLO_CFG_RX_INFO1_PRERES | + MTK_WDMA_GLO_CFG_RX_INFO2_PRERES); + +- if (dev->hw->version == 1) { ++ if (mtk_wed_is_v1(dev->hw)) { + wdma_set(dev, MTK_WDMA_GLO_CFG, + MTK_WDMA_GLO_CFG_RX_INFO3_PRERES); + } else { +@@ -1465,7 +1465,7 @@ mtk_wed_start(struct mtk_wed_device *dev + + mtk_wed_set_ext_int(dev, true); + +- if (dev->hw->version == 1) { ++ if (mtk_wed_is_v1(dev->hw)) { + u32 val = dev->wlan.wpdma_phys | MTK_PCIE_MIRROR_MAP_EN | + FIELD_PREP(MTK_PCIE_MIRROR_MAP_WED_ID, + dev->hw->index); +@@ -1550,7 +1550,7 @@ mtk_wed_attach(struct mtk_wed_device *de + } + + mtk_wed_hw_init_early(dev); +- if (hw->version == 1) { ++ if (mtk_wed_is_v1(hw)) { + regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP, + BIT(hw->index), 0); + } else { +@@ -1618,7 +1618,7 @@ static int + mtk_wed_txfree_ring_setup(struct mtk_wed_device *dev, void __iomem *regs) + { + struct mtk_wed_ring *ring = &dev->txfree_ring; +- int i, index = dev->hw->version == 1; ++ int i, index = mtk_wed_is_v1(dev->hw); + + /* + * For txfree event handling, the same DMA ring is shared between WED +@@ -1676,7 +1676,7 @@ mtk_wed_irq_get(struct mtk_wed_device *d + { + u32 val, ext_mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK; + +- if (dev->hw->version == 1) ++ if (mtk_wed_is_v1(dev->hw)) + ext_mask |= MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR; + else + ext_mask |= MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH | +@@ -1843,7 +1843,7 @@ mtk_wed_setup_tc(struct mtk_wed_device * + { + struct mtk_wed_hw *hw = wed->hw; + +- if (hw->version < 2) ++ if (mtk_wed_is_v1(hw)) + return -EOPNOTSUPP; + + switch (type) { +@@ -1917,9 +1917,9 @@ void mtk_wed_add_hw(struct device_node * + hw->wdma = wdma; + hw->index = index; + hw->irq = irq; +- hw->version = mtk_is_netsys_v1(eth) ? 1 : 2; ++ hw->version = eth->soc->version; + +- if (hw->version == 1) { ++ if (mtk_wed_is_v1(hw)) { + hw->mirror = syscon_regmap_lookup_by_phandle(eth_np, + "mediatek,pcie-mirror"); + hw->hifsys = syscon_regmap_lookup_by_phandle(eth_np, +--- a/drivers/net/ethernet/mediatek/mtk_wed.h ++++ b/drivers/net/ethernet/mediatek/mtk_wed.h +@@ -40,6 +40,16 @@ struct mtk_wdma_info { + }; + + #ifdef CONFIG_NET_MEDIATEK_SOC_WED ++static inline bool mtk_wed_is_v1(struct mtk_wed_hw *hw) ++{ ++ return hw->version == 1; ++} ++ ++static inline bool mtk_wed_is_v2(struct mtk_wed_hw *hw) ++{ ++ return hw->version == 2; ++} ++ + static inline void + wed_w32(struct mtk_wed_device *dev, u32 reg, u32 val) + { +--- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c +@@ -263,7 +263,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) ++ if (!mtk_wed_is_v1(hw)) + debugfs_create_file_unsafe("rxinfo", 0400, dir, hw, + &wed_rxinfo_fops); + } +--- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c +@@ -207,7 +207,7 @@ int mtk_wed_mcu_msg_update(struct mtk_we + { + struct mtk_wed_wo *wo = dev->hw->wed_wo; + +- if (dev->hw->version == 1) ++ if (mtk_wed_is_v1(dev->hw)) + return 0; + + if (WARN_ON(!wo)) diff --git a/target/linux/generic/backport-6.6/752-07-v6.7-net-ethernet-mtk_wed-do-not-configure-rx-offload-if-.patch b/target/linux/generic/backport-6.6/752-07-v6.7-net-ethernet-mtk_wed-do-not-configure-rx-offload-if-.patch new file mode 100644 index 0000000000..bc34aa33a9 --- /dev/null +++ b/target/linux/generic/backport-6.6/752-07-v6.7-net-ethernet-mtk_wed-do-not-configure-rx-offload-if-.patch @@ -0,0 +1,234 @@ +From: Lorenzo Bianconi +Date: Mon, 18 Sep 2023 12:29:06 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: do not configure rx offload if not + supported + +Check if rx offload is supported running mtk_wed_get_rx_capa routine +before configuring it. This is a preliminary patch to introduce Wireless +Ethernet Dispatcher (WED) support for MT7988 SoC. + +Co-developed-by: Sujuan Chen +Signed-off-by: Sujuan Chen +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -605,7 +605,7 @@ mtk_wed_stop(struct mtk_wed_device *dev) + wdma_w32(dev, MTK_WDMA_INT_GRP2, 0); + wed_w32(dev, MTK_WED_WPDMA_INT_MASK, 0); + +- if (mtk_wed_is_v1(dev->hw)) ++ if (!mtk_wed_get_rx_capa(dev)) + return; + + wed_w32(dev, MTK_WED_EXT_INT_MASK1, 0); +@@ -732,16 +732,21 @@ mtk_wed_set_wpdma(struct mtk_wed_device + { + if (mtk_wed_is_v1(dev->hw)) { + wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_phys); +- } 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_RX_GLO_CFG, dev->wlan.wpdma_rx_glo); +- wed_w32(dev, MTK_WED_WPDMA_RX_RING, dev->wlan.wpdma_rx); ++ return; + } ++ ++ 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); ++ ++ if (!mtk_wed_get_rx_capa(dev)) ++ return; ++ ++ 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); + } + + static void +@@ -973,15 +978,17 @@ mtk_wed_hw_init(struct mtk_wed_device *d + MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); + } 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); ++ if (mtk_wed_get_rx_capa(dev)) { ++ /* 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); +@@ -1353,8 +1360,6 @@ 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 | +@@ -1373,15 +1378,20 @@ 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])); ++ if (mtk_wed_get_rx_capa(dev)) { ++ 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])); ++ ++ wdma_mask |= FIELD_PREP(MTK_WDMA_INT_MASK_TX_DONE, ++ GENMASK(1, 0)); ++ } + + wed_w32(dev, MTK_WED_WDMA_INT_CLR, wdma_mask); + wed_set(dev, MTK_WED_WDMA_INT_CTRL, +@@ -1400,6 +1410,8 @@ mtk_wed_configure_irq(struct mtk_wed_dev + static void + mtk_wed_dma_enable(struct mtk_wed_device *dev) + { ++ int i; ++ + wed_set(dev, MTK_WED_WPDMA_INT_CTRL, MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV); + + wed_set(dev, MTK_WED_GLO_CFG, +@@ -1419,33 +1431,33 @@ mtk_wed_dma_enable(struct mtk_wed_device + if (mtk_wed_is_v1(dev->hw)) { + wdma_set(dev, MTK_WDMA_GLO_CFG, + MTK_WDMA_GLO_CFG_RX_INFO3_PRERES); +- } else { +- int i; ++ return; ++ } + +- wed_set(dev, MTK_WED_WPDMA_CTRL, +- MTK_WED_WPDMA_CTRL_SDL1_FIXED); ++ wed_set(dev, MTK_WED_WPDMA_CTRL, ++ MTK_WED_WPDMA_CTRL_SDL1_FIXED); ++ 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); ++ 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_WDMA_GLO_CFG, +- MTK_WED_WDMA_GLO_CFG_TX_DRV_EN | +- MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK); ++ if (!mtk_wed_get_rx_capa(dev)) ++ return; + +- 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); +- +- 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_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_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)); ++ 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); +- } ++ for (i = 0; i < MTK_WED_RX_QUEUES; i++) ++ mtk_wed_check_wfdma_rx_fill(dev, i); + } + + static void +@@ -1472,7 +1484,7 @@ 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 { ++ } else if (mtk_wed_get_rx_capa(dev)) { + /* driver set mid ready and only once */ + wed_w32(dev, MTK_WED_EXT_INT_MASK1, + MTK_WED_EXT_INT_STATUS_WPDMA_MID_RDY); +@@ -1484,7 +1496,6 @@ 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); +@@ -1550,13 +1561,14 @@ mtk_wed_attach(struct mtk_wed_device *de + } + + mtk_wed_hw_init_early(dev); +- if (mtk_wed_is_v1(hw)) { ++ if (mtk_wed_is_v1(hw)) + 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); ++ ++ if (mtk_wed_get_rx_capa(dev)) + ret = mtk_wed_wo_init(hw); +- } + out: + if (ret) { + dev_err(dev->hw->dev, "failed to attach wed device\n"); +--- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c +@@ -207,7 +207,7 @@ int mtk_wed_mcu_msg_update(struct mtk_we + { + struct mtk_wed_wo *wo = dev->hw->wed_wo; + +- if (mtk_wed_is_v1(dev->hw)) ++ if (!mtk_wed_get_rx_capa(dev)) + return 0; + + if (WARN_ON(!wo)) diff --git a/target/linux/generic/backport-6.6/752-08-v6.7-net-ethernet-mtk_wed-rename-mtk_rxbm_desc-in-mtk_wed.patch b/target/linux/generic/backport-6.6/752-08-v6.7-net-ethernet-mtk_wed-rename-mtk_rxbm_desc-in-mtk_wed.patch new file mode 100644 index 0000000000..d83434fb2c --- /dev/null +++ b/target/linux/generic/backport-6.6/752-08-v6.7-net-ethernet-mtk_wed-rename-mtk_rxbm_desc-in-mtk_wed.patch @@ -0,0 +1,52 @@ +From: Lorenzo Bianconi +Date: Mon, 18 Sep 2023 12:29:07 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: rename mtk_rxbm_desc in + mtk_wed_bm_desc + +Rename mtk_rxbm_desc structure in mtk_wed_bm_desc since it will be used +even on tx side by MT7988 SoC. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -421,7 +421,7 @@ free_pagelist: + static int + mtk_wed_rx_buffer_alloc(struct mtk_wed_device *dev) + { +- struct mtk_rxbm_desc *desc; ++ struct mtk_wed_bm_desc *desc; + dma_addr_t desc_phys; + + dev->rx_buf_ring.size = dev->wlan.rx_nbuf; +@@ -441,7 +441,7 @@ mtk_wed_rx_buffer_alloc(struct mtk_wed_d + static void + mtk_wed_free_rx_buffer(struct mtk_wed_device *dev) + { +- struct mtk_rxbm_desc *desc = dev->rx_buf_ring.desc; ++ struct mtk_wed_bm_desc *desc = dev->rx_buf_ring.desc; + + if (!desc) + return; +--- a/include/linux/soc/mediatek/mtk_wed.h ++++ b/include/linux/soc/mediatek/mtk_wed.h +@@ -45,7 +45,7 @@ enum mtk_wed_wo_cmd { + MTK_WED_WO_CMD_WED_END + }; + +-struct mtk_rxbm_desc { ++struct mtk_wed_bm_desc { + __le32 buf0; + __le32 token; + } __packed __aligned(4); +@@ -105,7 +105,7 @@ struct mtk_wed_device { + struct { + int size; + struct page_frag_cache rx_page; +- struct mtk_rxbm_desc *desc; ++ struct mtk_wed_bm_desc *desc; + dma_addr_t desc_phys; + } rx_buf_ring; + diff --git a/target/linux/generic/backport-6.6/752-09-v6.7-net-ethernet-mtk_wed-introduce-mtk_wed_buf-structure.patch b/target/linux/generic/backport-6.6/752-09-v6.7-net-ethernet-mtk_wed-introduce-mtk_wed_buf-structure.patch new file mode 100644 index 0000000000..8000a8759e --- /dev/null +++ b/target/linux/generic/backport-6.6/752-09-v6.7-net-ethernet-mtk_wed-introduce-mtk_wed_buf-structure.patch @@ -0,0 +1,87 @@ +From: Lorenzo Bianconi +Date: Mon, 18 Sep 2023 12:29:08 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: introduce mtk_wed_buf structure + +Introduce mtk_wed_buf structure to store both virtual and physical +addresses allocated in mtk_wed_tx_buffer_alloc() routine. This is a +preliminary patch to add WED support for MT7988 SoC since it relies on a +different dma descriptor layout not storing page dma addresses. + +Co-developed-by: Sujuan Chen +Signed-off-by: Sujuan Chen +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -299,9 +299,9 @@ out: + static int + mtk_wed_tx_buffer_alloc(struct mtk_wed_device *dev) + { ++ struct mtk_wed_buf *page_list; + struct mtk_wdma_desc *desc; + dma_addr_t desc_phys; +- void **page_list; + int token = dev->wlan.token_start; + int ring_size; + int n_pages; +@@ -342,7 +342,8 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_d + return -ENOMEM; + } + +- page_list[page_idx++] = page; ++ page_list[page_idx].p = page; ++ page_list[page_idx++].phy_addr = page_phys; + dma_sync_single_for_cpu(dev->hw->dev, page_phys, PAGE_SIZE, + DMA_BIDIRECTIONAL); + +@@ -386,8 +387,8 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_d + static void + mtk_wed_free_tx_buffer(struct mtk_wed_device *dev) + { ++ struct mtk_wed_buf *page_list = dev->tx_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; + +@@ -399,13 +400,12 @@ mtk_wed_free_tx_buffer(struct mtk_wed_de + + 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; ++ dma_addr_t buf_addr = page_list[page_idx].phy_addr; ++ void *page = page_list[page_idx++].p; + + if (!page) + break; + +- buf_addr = le32_to_cpu(desc[i].buf0); + dma_unmap_page(dev->hw->dev, buf_addr, PAGE_SIZE, + DMA_BIDIRECTIONAL); + __free_page(page); +--- a/include/linux/soc/mediatek/mtk_wed.h ++++ b/include/linux/soc/mediatek/mtk_wed.h +@@ -76,6 +76,11 @@ struct mtk_wed_wo_rx_stats { + __le32 rx_drop_cnt; + }; + ++struct mtk_wed_buf { ++ void *p; ++ dma_addr_t phy_addr; ++}; ++ + struct mtk_wed_device { + #ifdef CONFIG_NET_MEDIATEK_SOC_WED + const struct mtk_wed_ops *ops; +@@ -97,7 +102,7 @@ struct mtk_wed_device { + + struct { + int size; +- void **pages; ++ struct mtk_wed_buf *pages; + struct mtk_wdma_desc *desc; + dma_addr_t desc_phys; + } tx_buf_ring; diff --git a/target/linux/generic/backport-6.6/752-10-v6.7-net-ethernet-mtk_wed-move-mem_region-array-out-of-mt.patch b/target/linux/generic/backport-6.6/752-10-v6.7-net-ethernet-mtk_wed-move-mem_region-array-out-of-mt.patch new file mode 100644 index 0000000000..98d782b1d0 --- /dev/null +++ b/target/linux/generic/backport-6.6/752-10-v6.7-net-ethernet-mtk_wed-move-mem_region-array-out-of-mt.patch @@ -0,0 +1,88 @@ +From: Lorenzo Bianconi +Date: Mon, 18 Sep 2023 12:29:09 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: move mem_region array out of + mtk_wed_mcu_load_firmware + +Remove mtk_wed_wo_memory_region boot structure in mtk_wed_wo. +This is a preliminary patch to introduce WED support for MT7988 SoC. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c +@@ -16,14 +16,30 @@ + #include "mtk_wed_wo.h" + #include "mtk_wed.h" + ++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, ++ }, ++ [MTK_WED_WO_REGION_BOOT] = { ++ .name = "wo-boot", ++ }, ++}; ++ + static u32 wo_r32(struct mtk_wed_wo *wo, u32 reg) + { +- return readl(wo->boot.addr + reg); ++ return readl(mem_region[MTK_WED_WO_REGION_BOOT].addr + reg); + } + + static void wo_w32(struct mtk_wed_wo *wo, u32 reg, u32 val) + { +- writel(val, wo->boot.addr + reg); ++ writel(val, mem_region[MTK_WED_WO_REGION_BOOT].addr + reg); + } + + static struct sk_buff * +@@ -294,18 +310,6 @@ next: + 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; +@@ -319,11 +323,6 @@ mtk_wed_mcu_load_firmware(struct mtk_wed + 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); +--- a/drivers/net/ethernet/mediatek/mtk_wed_wo.h ++++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h +@@ -228,7 +228,6 @@ struct mtk_wed_wo_queue { + + 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; diff --git a/target/linux/generic/backport-6.6/752-11-v6.7-net-ethernet-mtk_wed-make-memory-region-optional.patch b/target/linux/generic/backport-6.6/752-11-v6.7-net-ethernet-mtk_wed-make-memory-region-optional.patch new file mode 100644 index 0000000000..48b0d02049 --- /dev/null +++ b/target/linux/generic/backport-6.6/752-11-v6.7-net-ethernet-mtk_wed-make-memory-region-optional.patch @@ -0,0 +1,71 @@ +From: Lorenzo Bianconi +Date: Mon, 18 Sep 2023 12:29:10 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: make memory region optional + +Make mtk_wed_wo_memory_region optionals. +This is a preliminary patch to introduce Wireless Ethernet Dispatcher +support for MT7988 SoC since MT7988 WED fw image will have a different +layout. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c +@@ -234,19 +234,13 @@ int mtk_wed_mcu_msg_update(struct mtk_we + } + + static int +-mtk_wed_get_memory_region(struct mtk_wed_wo *wo, ++mtk_wed_get_memory_region(struct mtk_wed_hw *hw, int index, + 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); ++ np = of_parse_phandle(hw->node, "memory-region", index); + if (!np) + return -ENODEV; + +@@ -258,7 +252,7 @@ mtk_wed_get_memory_region(struct mtk_wed + + region->phy_addr = rmem->base; + region->size = rmem->size; +- region->addr = devm_ioremap(wo->hw->dev, region->phy_addr, region->size); ++ region->addr = devm_ioremap(hw->dev, region->phy_addr, region->size); + + return !region->addr ? -EINVAL : 0; + } +@@ -271,6 +265,9 @@ mtk_wed_mcu_run_firmware(struct mtk_wed_ + const struct mtk_wed_fw_trailer *trailer; + const struct mtk_wed_fw_region *fw_region; + ++ if (!region->phy_addr || !region->size) ++ return 0; ++ + 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); +@@ -318,7 +315,13 @@ mtk_wed_mcu_load_firmware(struct mtk_wed + + /* load firmware region metadata */ + for (i = 0; i < ARRAY_SIZE(mem_region); i++) { +- ret = mtk_wed_get_memory_region(wo, &mem_region[i]); ++ int index = of_property_match_string(wo->hw->node, ++ "memory-region-names", ++ mem_region[i].name); ++ if (index < 0) ++ continue; ++ ++ ret = mtk_wed_get_memory_region(wo->hw, index, &mem_region[i]); + if (ret) + return ret; + } diff --git a/target/linux/generic/backport-6.6/752-13-v6.7-net-ethernet-mtk_wed-add-mtk_wed_soc_data-structure.patch b/target/linux/generic/backport-6.6/752-13-v6.7-net-ethernet-mtk_wed-add-mtk_wed_soc_data-structure.patch new file mode 100644 index 0000000000..c43114fb5b --- /dev/null +++ b/target/linux/generic/backport-6.6/752-13-v6.7-net-ethernet-mtk_wed-add-mtk_wed_soc_data-structure.patch @@ -0,0 +1,217 @@ +From: Lorenzo Bianconi +Date: Mon, 18 Sep 2023 12:29:12 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: add mtk_wed_soc_data structure + +Introduce mtk_wed_soc_data utility structure to contain per-SoC +definitions. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -48,6 +48,26 @@ struct mtk_wed_flow_block_priv { + struct net_device *dev; + }; + ++static const struct mtk_wed_soc_data mt7622_data = { ++ .regmap = { ++ .tx_bm_tkid = 0x088, ++ .wpdma_rx_ring0 = 0x770, ++ .reset_idx_tx_mask = GENMASK(3, 0), ++ .reset_idx_rx_mask = GENMASK(17, 16), ++ }, ++ .wdma_desc_size = sizeof(struct mtk_wdma_desc), ++}; ++ ++static const struct mtk_wed_soc_data mt7986_data = { ++ .regmap = { ++ .tx_bm_tkid = 0x0c8, ++ .wpdma_rx_ring0 = 0x770, ++ .reset_idx_tx_mask = GENMASK(1, 0), ++ .reset_idx_rx_mask = GENMASK(7, 6), ++ }, ++ .wdma_desc_size = 2 * sizeof(struct mtk_wdma_desc), ++}; ++ + static void + wed_m32(struct mtk_wed_device *dev, u32 reg, u32 mask, u32 val) + { +@@ -746,7 +766,7 @@ mtk_wed_set_wpdma(struct mtk_wed_device + return; + + 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); ++ wed_w32(dev, dev->hw->soc->regmap.wpdma_rx_ring0, dev->wlan.wpdma_rx); + } + + static void +@@ -940,22 +960,10 @@ mtk_wed_hw_init(struct mtk_wed_device *d + wed_w32(dev, MTK_WED_TX_BM_BUF_LEN, MTK_WED_PKT_SIZE); + + if (mtk_wed_is_v1(dev->hw)) { +- wed_w32(dev, MTK_WED_TX_BM_TKID, +- FIELD_PREP(MTK_WED_TX_BM_TKID_START, +- dev->wlan.token_start) | +- FIELD_PREP(MTK_WED_TX_BM_TKID_END, +- dev->wlan.token_start + +- dev->wlan.nbuf - 1)); + wed_w32(dev, MTK_WED_TX_BM_DYN_THR, + FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO, 1) | + MTK_WED_TX_BM_DYN_THR_HI); + } else { +- wed_w32(dev, MTK_WED_TX_BM_TKID_V2, +- FIELD_PREP(MTK_WED_TX_BM_TKID_START, +- dev->wlan.token_start) | +- FIELD_PREP(MTK_WED_TX_BM_TKID_END, +- dev->wlan.token_start + +- dev->wlan.nbuf - 1)); + wed_w32(dev, MTK_WED_TX_BM_DYN_THR, + FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO_V2, 0) | + MTK_WED_TX_BM_DYN_THR_HI_V2); +@@ -970,6 +978,11 @@ mtk_wed_hw_init(struct mtk_wed_device *d + MTK_WED_TX_TKID_DYN_THR_HI); + } + ++ wed_w32(dev, dev->hw->soc->regmap.tx_bm_tkid, ++ FIELD_PREP(MTK_WED_TX_BM_TKID_START, dev->wlan.token_start) | ++ FIELD_PREP(MTK_WED_TX_BM_TKID_END, ++ dev->wlan.token_start + dev->wlan.nbuf - 1)); ++ + mtk_wed_reset(dev, MTK_WED_RESET_TX_BM); + + if (mtk_wed_is_v1(dev->hw)) { +@@ -1104,13 +1117,8 @@ mtk_wed_rx_reset(struct mtk_wed_device * + if (ret) { + mtk_wed_reset(dev, MTK_WED_RESET_WED_RX_DMA); + } else { +- struct mtk_eth *eth = dev->hw->eth; +- +- if (mtk_is_netsys_v2_or_greater(eth)) +- 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_set(dev, MTK_WED_RESET_IDX, ++ dev->hw->soc->regmap.reset_idx_rx_mask); + wed_w32(dev, MTK_WED_RESET_IDX, 0); + } + +@@ -1163,7 +1171,8 @@ mtk_wed_reset_dma(struct mtk_wed_device + 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); ++ wed_w32(dev, MTK_WED_RESET_IDX, ++ dev->hw->soc->regmap.reset_idx_tx_mask); + wed_w32(dev, MTK_WED_RESET_IDX, 0); + } + +@@ -1255,7 +1264,6 @@ static int + 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; + + if (idx >= ARRAY_SIZE(dev->rx_wdma)) +@@ -1263,7 +1271,7 @@ mtk_wed_wdma_rx_ring_setup(struct mtk_we + + wdma = &dev->rx_wdma[idx]; + if (!reset && mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, +- desc_size, true)) ++ dev->hw->soc->wdma_desc_size, true)) + return -ENOMEM; + + wdma_w32(dev, MTK_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_BASE, +@@ -1284,7 +1292,6 @@ static int + 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; + + if (idx >= ARRAY_SIZE(dev->tx_wdma)) +@@ -1292,7 +1299,7 @@ mtk_wed_wdma_tx_ring_setup(struct mtk_we + + wdma = &dev->tx_wdma[idx]; + if (!reset && mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, +- desc_size, true)) ++ dev->hw->soc->wdma_desc_size, true)) + return -ENOMEM; + + wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_BASE, +@@ -1931,7 +1938,12 @@ void mtk_wed_add_hw(struct device_node * + hw->irq = irq; + hw->version = eth->soc->version; + +- if (mtk_wed_is_v1(hw)) { ++ switch (hw->version) { ++ case 2: ++ hw->soc = &mt7986_data; ++ break; ++ default: ++ case 1: + hw->mirror = syscon_regmap_lookup_by_phandle(eth_np, + "mediatek,pcie-mirror"); + hw->hifsys = syscon_regmap_lookup_by_phandle(eth_np, +@@ -1945,6 +1957,8 @@ void mtk_wed_add_hw(struct device_node * + regmap_write(hw->mirror, 0, 0); + regmap_write(hw->mirror, 4, 0); + } ++ hw->soc = &mt7622_data; ++ break; + } + + mtk_wed_hw_add_debugfs(hw); +--- a/drivers/net/ethernet/mediatek/mtk_wed.h ++++ b/drivers/net/ethernet/mediatek/mtk_wed.h +@@ -12,7 +12,18 @@ + struct mtk_eth; + struct mtk_wed_wo; + ++struct mtk_wed_soc_data { ++ struct { ++ u32 tx_bm_tkid; ++ u32 wpdma_rx_ring0; ++ u32 reset_idx_tx_mask; ++ u32 reset_idx_rx_mask; ++ } regmap; ++ u32 wdma_desc_size; ++}; ++ + struct mtk_wed_hw { ++ const struct mtk_wed_soc_data *soc; + struct device_node *node; + struct mtk_eth *eth; + struct regmap *regs; +--- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h ++++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h +@@ -100,8 +100,6 @@ struct mtk_wdma_desc { + + #define MTK_WED_TX_BM_BASE 0x084 + +-#define MTK_WED_TX_BM_TKID 0x088 +-#define MTK_WED_TX_BM_TKID_V2 0x0c8 + #define MTK_WED_TX_BM_TKID_START GENMASK(15, 0) + #define MTK_WED_TX_BM_TKID_END GENMASK(31, 16) + +@@ -160,9 +158,6 @@ struct mtk_wdma_desc { + #define MTK_WED_GLO_CFG_RX_2B_OFFSET BIT(31) + + #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) +@@ -286,7 +281,6 @@ struct mtk_wdma_desc { + #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) diff --git a/target/linux/generic/backport-6.6/752-14-v6.7-net-ethernet-mtk_wed-introduce-WED-support-for-MT798.patch b/target/linux/generic/backport-6.6/752-14-v6.7-net-ethernet-mtk_wed-introduce-WED-support-for-MT798.patch new file mode 100644 index 0000000000..f874899c5b --- /dev/null +++ b/target/linux/generic/backport-6.6/752-14-v6.7-net-ethernet-mtk_wed-introduce-WED-support-for-MT798.patch @@ -0,0 +1,1280 @@ +From: Sujuan Chen +Date: Mon, 18 Sep 2023 12:29:13 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: introduce WED support for MT7988 + +Similar to MT7986 and MT7622, enable Wireless Ethernet Ditpatcher for +MT7988 in order to offload traffic forwarded from LAN/WLAN to WLAN/LAN + +Co-developed-by: Lorenzo Bianconi +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Sujuan Chen +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -195,6 +195,7 @@ static const struct mtk_reg_map mt7988_r + .wdma_base = { + [0] = 0x4800, + [1] = 0x4c00, ++ [2] = 0x5000, + }, + .pse_iq_sta = 0x0180, + .pse_oq_sta = 0x01a0, +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -1132,7 +1132,7 @@ struct mtk_reg_map { + u32 gdm1_cnt; + u32 gdma_to_ppe; + u32 ppe_base; +- u32 wdma_base[2]; ++ u32 wdma_base[3]; + u32 pse_iq_sta; + u32 pse_oq_sta; + }; +--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +@@ -201,6 +201,9 @@ mtk_flow_set_output_device(struct mtk_et + case 1: + pse_port = PSE_WDMA1_PORT; + break; ++ case 2: ++ pse_port = PSE_WDMA2_PORT; ++ break; + default: + return -EINVAL; + } +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -16,17 +16,19 @@ + #include + #include + #include "mtk_eth_soc.h" +-#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) + +-#define MTK_WED_PKT_SIZE 1900 ++#define MTK_WED_PKT_SIZE 1920 + #define MTK_WED_BUF_SIZE 2048 ++#define MTK_WED_PAGE_BUF_SIZE 128 + #define MTK_WED_BUF_PER_PAGE (PAGE_SIZE / 2048) ++#define MTK_WED_RX_PAGE_BUF_PER_PAGE (PAGE_SIZE / 128) + #define MTK_WED_RX_RING_SIZE 1536 ++#define MTK_WED_RX_PG_BM_CNT 8192 + + #define MTK_WED_TX_RING_SIZE 2048 + #define MTK_WED_WDMA_RING_SIZE 1024 +@@ -40,7 +42,10 @@ + #define MTK_WED_RRO_QUE_CNT 8192 + #define MTK_WED_MIOD_ENTRY_CNT 128 + +-static struct mtk_wed_hw *hw_list[2]; ++#define MTK_WED_TX_BM_DMA_SIZE 65536 ++#define MTK_WED_TX_BM_PKT_CNT 32768 ++ ++static struct mtk_wed_hw *hw_list[3]; + static DEFINE_MUTEX(hw_lock); + + struct mtk_wed_flow_block_priv { +@@ -55,6 +60,7 @@ static const struct mtk_wed_soc_data mt7 + .reset_idx_tx_mask = GENMASK(3, 0), + .reset_idx_rx_mask = GENMASK(17, 16), + }, ++ .tx_ring_desc_size = sizeof(struct mtk_wdma_desc), + .wdma_desc_size = sizeof(struct mtk_wdma_desc), + }; + +@@ -65,6 +71,18 @@ static const struct mtk_wed_soc_data mt7 + .reset_idx_tx_mask = GENMASK(1, 0), + .reset_idx_rx_mask = GENMASK(7, 6), + }, ++ .tx_ring_desc_size = sizeof(struct mtk_wdma_desc), ++ .wdma_desc_size = 2 * sizeof(struct mtk_wdma_desc), ++}; ++ ++static const struct mtk_wed_soc_data mt7988_data = { ++ .regmap = { ++ .tx_bm_tkid = 0x0c8, ++ .wpdma_rx_ring0 = 0x7d0, ++ .reset_idx_tx_mask = GENMASK(1, 0), ++ .reset_idx_rx_mask = GENMASK(7, 6), ++ }, ++ .tx_ring_desc_size = sizeof(struct mtk_wed_bm_desc), + .wdma_desc_size = 2 * sizeof(struct mtk_wdma_desc), + }; + +@@ -319,33 +337,38 @@ out: + static int + mtk_wed_tx_buffer_alloc(struct mtk_wed_device *dev) + { ++ u32 desc_size = dev->hw->soc->tx_ring_desc_size; ++ int i, page_idx = 0, n_pages, ring_size; ++ int token = dev->wlan.token_start; + struct mtk_wed_buf *page_list; +- struct mtk_wdma_desc *desc; + dma_addr_t desc_phys; +- int token = dev->wlan.token_start; +- int ring_size; +- int n_pages; +- int i, page_idx; ++ void *desc_ptr; + +- ring_size = dev->wlan.nbuf & ~(MTK_WED_BUF_PER_PAGE - 1); +- n_pages = ring_size / MTK_WED_BUF_PER_PAGE; ++ if (!mtk_wed_is_v3_or_greater(dev->hw)) { ++ ring_size = dev->wlan.nbuf & ~(MTK_WED_BUF_PER_PAGE - 1); ++ dev->tx_buf_ring.size = ring_size; ++ } else { ++ dev->tx_buf_ring.size = MTK_WED_TX_BM_DMA_SIZE; ++ ring_size = MTK_WED_TX_BM_PKT_CNT; ++ } ++ n_pages = dev->tx_buf_ring.size / MTK_WED_BUF_PER_PAGE; + + page_list = kcalloc(n_pages, sizeof(*page_list), GFP_KERNEL); + if (!page_list) + return -ENOMEM; + +- 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) ++ desc_ptr = dma_alloc_coherent(dev->hw->dev, ++ dev->tx_buf_ring.size * desc_size, ++ &desc_phys, GFP_KERNEL); ++ if (!desc_ptr) + return -ENOMEM; + +- dev->tx_buf_ring.desc = desc; ++ dev->tx_buf_ring.desc = desc_ptr; + dev->tx_buf_ring.desc_phys = desc_phys; + +- for (i = 0, page_idx = 0; i < ring_size; i += MTK_WED_BUF_PER_PAGE) { ++ for (i = 0; i < ring_size; i += MTK_WED_BUF_PER_PAGE) { + dma_addr_t page_phys, buf_phys; + struct page *page; + void *buf; +@@ -371,28 +394,31 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_d + buf_phys = page_phys; + + for (s = 0; s < MTK_WED_BUF_PER_PAGE; s++) { +- u32 txd_size; +- u32 ctrl; +- +- txd_size = dev->wlan.init_buf(buf, buf_phys, token++); ++ struct mtk_wdma_desc *desc = desc_ptr; + + desc->buf0 = cpu_to_le32(buf_phys); +- desc->buf1 = cpu_to_le32(buf_phys + txd_size); ++ if (!mtk_wed_is_v3_or_greater(dev->hw)) { ++ u32 txd_size, ctrl; + +- if (mtk_wed_is_v1(dev->hw)) +- ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, txd_size) | +- FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1, +- MTK_WED_BUF_SIZE - txd_size) | +- MTK_WDMA_DESC_CTRL_LAST_SEG1; +- else +- ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, txd_size) | +- FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1_V2, +- MTK_WED_BUF_SIZE - txd_size) | +- MTK_WDMA_DESC_CTRL_LAST_SEG0; +- desc->ctrl = cpu_to_le32(ctrl); +- desc->info = 0; +- desc++; ++ txd_size = dev->wlan.init_buf(buf, buf_phys, ++ token++); ++ desc->buf1 = cpu_to_le32(buf_phys + txd_size); ++ ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, txd_size); ++ if (mtk_wed_is_v1(dev->hw)) ++ ctrl |= MTK_WDMA_DESC_CTRL_LAST_SEG1 | ++ FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1, ++ MTK_WED_BUF_SIZE - txd_size); ++ else ++ ctrl |= MTK_WDMA_DESC_CTRL_LAST_SEG0 | ++ FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1_V2, ++ MTK_WED_BUF_SIZE - txd_size); ++ desc->ctrl = cpu_to_le32(ctrl); ++ desc->info = 0; ++ } else { ++ desc->ctrl = cpu_to_le32(token << 16); ++ } + ++ desc_ptr += desc_size; + buf += MTK_WED_BUF_SIZE; + buf_phys += MTK_WED_BUF_SIZE; + } +@@ -408,31 +434,31 @@ static void + mtk_wed_free_tx_buffer(struct mtk_wed_device *dev) + { + struct mtk_wed_buf *page_list = dev->tx_buf_ring.pages; +- struct mtk_wdma_desc *desc = dev->tx_buf_ring.desc; +- int page_idx; +- int i; ++ struct mtk_wed_hw *hw = dev->hw; ++ int i, page_idx = 0; + + if (!page_list) + return; + +- if (!desc) ++ if (!dev->tx_buf_ring.desc) + goto free_pagelist; + +- for (i = 0, page_idx = 0; i < dev->tx_buf_ring.size; +- i += MTK_WED_BUF_PER_PAGE) { +- dma_addr_t buf_addr = page_list[page_idx].phy_addr; ++ for (i = 0; i < dev->tx_buf_ring.size; i += MTK_WED_BUF_PER_PAGE) { ++ dma_addr_t page_phy = page_list[page_idx].phy_addr; + void *page = page_list[page_idx++].p; + + if (!page) + break; + +- dma_unmap_page(dev->hw->dev, buf_addr, PAGE_SIZE, ++ dma_unmap_page(dev->hw->dev, page_phy, PAGE_SIZE, + DMA_BIDIRECTIONAL); + __free_page(page); + } + +- dma_free_coherent(dev->hw->dev, dev->tx_buf_ring.size * sizeof(*desc), +- desc, dev->tx_buf_ring.desc_phys); ++ dma_free_coherent(dev->hw->dev, ++ dev->tx_buf_ring.size * hw->soc->tx_ring_desc_size, ++ dev->tx_buf_ring.desc, ++ dev->tx_buf_ring.desc_phys); + + free_pagelist: + kfree(page_list); +@@ -517,13 +543,23 @@ mtk_wed_set_ext_int(struct mtk_wed_devic + { + u32 mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK; + +- if (mtk_wed_is_v1(dev->hw)) ++ switch (dev->hw->version) { ++ case 1: + mask |= MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR; +- else ++ break; ++ case 2: + mask |= MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH | + MTK_WED_EXT_INT_STATUS_RX_FBUF_HI_TH | + MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT | + MTK_WED_EXT_INT_STATUS_TX_DMA_W_RESP_ERR; ++ break; ++ case 3: ++ mask = MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT | ++ MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD; ++ break; ++ default: ++ break; ++ } + + if (!dev->hw->num_flows) + mask &= ~MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD; +@@ -535,6 +571,9 @@ mtk_wed_set_ext_int(struct mtk_wed_devic + static void + mtk_wed_set_512_support(struct mtk_wed_device *dev, bool enable) + { ++ if (!mtk_wed_is_v2(dev->hw)) ++ return; ++ + if (enable) { + wed_w32(dev, MTK_WED_TXDP_CTRL, MTK_WED_TXDP_DW9_OVERWR); + wed_w32(dev, MTK_WED_TXP_DW1, +@@ -609,6 +648,14 @@ 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); ++ ++ if (mtk_wed_is_v3_or_greater(dev->hw) && ++ mtk_wed_get_rx_capa(dev)) { ++ wdma_clr(dev, MTK_WDMA_PREF_TX_CFG, ++ MTK_WDMA_PREF_TX_CFG_PREF_EN); ++ wdma_clr(dev, MTK_WDMA_PREF_RX_CFG, ++ MTK_WDMA_PREF_RX_CFG_PREF_EN); ++ } + } + + mtk_wed_set_512_support(dev, false); +@@ -651,6 +698,14 @@ mtk_wed_deinit(struct mtk_wed_device *de + MTK_WED_CTRL_RX_ROUTE_QM_EN | + MTK_WED_CTRL_WED_RX_BM_EN | + MTK_WED_CTRL_RX_RRO_QM_EN); ++ ++ if (mtk_wed_is_v3_or_greater(dev->hw)) { ++ wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_TX_AMSDU_EN); ++ wed_clr(dev, MTK_WED_RESET, MTK_WED_RESET_TX_AMSDU); ++ wed_clr(dev, MTK_WED_PCIE_INT_CTRL, ++ MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA | ++ MTK_WED_PCIE_INT_CTRL_MSK_IRQ_FILTER); ++ } + } + + static void +@@ -700,21 +755,37 @@ mtk_wed_detach(struct mtk_wed_device *de + mutex_unlock(&hw_lock); + } + +-#define PCIE_BASE_ADDR0 0x11280000 + static void + mtk_wed_bus_init(struct mtk_wed_device *dev) + { + switch (dev->wlan.bus_type) { + case MTK_WED_BUS_PCIE: { + struct device_node *np = dev->hw->eth->dev->of_node; +- struct regmap *regs; + +- regs = syscon_regmap_lookup_by_phandle(np, +- "mediatek,wed-pcie"); +- if (IS_ERR(regs)) +- break; ++ if (mtk_wed_is_v2(dev->hw)) { ++ struct regmap *regs; ++ ++ regs = syscon_regmap_lookup_by_phandle(np, ++ "mediatek,wed-pcie"); ++ if (IS_ERR(regs)) ++ break; + +- regmap_update_bits(regs, 0, BIT(0), BIT(0)); ++ regmap_update_bits(regs, 0, BIT(0), BIT(0)); ++ } ++ ++ if (dev->wlan.msi) { ++ wed_w32(dev, MTK_WED_PCIE_CFG_INTM, ++ dev->hw->pcie_base | 0xc08); ++ wed_w32(dev, MTK_WED_PCIE_CFG_BASE, ++ dev->hw->pcie_base | 0xc04); ++ wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, BIT(8)); ++ } else { ++ wed_w32(dev, MTK_WED_PCIE_CFG_INTM, ++ dev->hw->pcie_base | 0x180); ++ wed_w32(dev, MTK_WED_PCIE_CFG_BASE, ++ dev->hw->pcie_base | 0x184); ++ wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, BIT(24)); ++ } + + wed_w32(dev, MTK_WED_PCIE_INT_CTRL, + FIELD_PREP(MTK_WED_PCIE_INT_CTRL_POLL_EN, 2)); +@@ -722,19 +793,9 @@ mtk_wed_bus_init(struct mtk_wed_device * + /* pcie interrupt control: pola/source selection */ + wed_set(dev, MTK_WED_PCIE_INT_CTRL, + MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA | +- FIELD_PREP(MTK_WED_PCIE_INT_CTRL_SRC_SEL, 1)); +- wed_r32(dev, MTK_WED_PCIE_INT_CTRL); +- +- wed_w32(dev, MTK_WED_PCIE_CFG_INTM, PCIE_BASE_ADDR0 | 0x180); +- wed_w32(dev, MTK_WED_PCIE_CFG_BASE, PCIE_BASE_ADDR0 | 0x184); +- +- /* pcie interrupt status trigger register */ +- wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, BIT(24)); +- wed_r32(dev, MTK_WED_PCIE_INT_TRIGGER); +- +- /* pola setting */ +- wed_set(dev, MTK_WED_PCIE_INT_CTRL, +- MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA); ++ MTK_WED_PCIE_INT_CTRL_MSK_IRQ_FILTER | ++ FIELD_PREP(MTK_WED_PCIE_INT_CTRL_SRC_SEL, ++ dev->hw->index)); + break; + } + case MTK_WED_BUS_AXI: +@@ -772,18 +833,19 @@ mtk_wed_set_wpdma(struct mtk_wed_device + static void + mtk_wed_hw_init_early(struct mtk_wed_device *dev) + { +- u32 mask, set; ++ u32 set = FIELD_PREP(MTK_WED_WDMA_GLO_CFG_BT_SIZE, 2); ++ u32 mask = MTK_WED_WDMA_GLO_CFG_BT_SIZE; + + mtk_wed_deinit(dev); + mtk_wed_reset(dev, MTK_WED_RESET_WED); + mtk_wed_set_wpdma(dev); + +- mask = MTK_WED_WDMA_GLO_CFG_BT_SIZE | +- MTK_WED_WDMA_GLO_CFG_DYNAMIC_DMAD_RECYCLE | +- MTK_WED_WDMA_GLO_CFG_RX_DIS_FSM_AUTO_IDLE; +- set = FIELD_PREP(MTK_WED_WDMA_GLO_CFG_BT_SIZE, 2) | +- MTK_WED_WDMA_GLO_CFG_DYNAMIC_SKIP_DMAD_PREP | +- MTK_WED_WDMA_GLO_CFG_IDLE_DMAD_SUPPLY; ++ if (!mtk_wed_is_v3_or_greater(dev->hw)) { ++ mask |= MTK_WED_WDMA_GLO_CFG_DYNAMIC_DMAD_RECYCLE | ++ MTK_WED_WDMA_GLO_CFG_RX_DIS_FSM_AUTO_IDLE; ++ set |= MTK_WED_WDMA_GLO_CFG_DYNAMIC_SKIP_DMAD_PREP | ++ MTK_WED_WDMA_GLO_CFG_IDLE_DMAD_SUPPLY; ++ } + wed_m32(dev, MTK_WED_WDMA_GLO_CFG, mask, set); + + if (mtk_wed_is_v1(dev->hw)) { +@@ -931,11 +993,18 @@ mtk_wed_route_qm_hw_init(struct mtk_wed_ + } + + /* 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); ++ if (mtk_wed_is_v2(dev->hw)) { ++ 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); ++ } else { ++ wed_set(dev, MTK_WED_RTQM_ENQ_CFG0, ++ FIELD_PREP(MTK_WED_RTQM_ENQ_CFG_TXDMAD_FPORT, ++ 0x3 + dev->hw->index)); ++ } + /* enable RX_ROUTE_QM */ + wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_RX_ROUTE_QM_EN); + } +@@ -948,22 +1017,30 @@ mtk_wed_hw_init(struct mtk_wed_device *d + + dev->init_done = true; + mtk_wed_set_ext_int(dev, false); +- 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->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->tx_buf_ring.desc_phys); +- + wed_w32(dev, MTK_WED_TX_BM_BUF_LEN, MTK_WED_PKT_SIZE); + + if (mtk_wed_is_v1(dev->hw)) { ++ 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->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_DYN_THR, + FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO, 1) | + MTK_WED_TX_BM_DYN_THR_HI); +- } else { ++ } else if (mtk_wed_is_v2(dev->hw)) { ++ 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->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_TKID_DYN_THR, ++ FIELD_PREP(MTK_WED_TX_TKID_DYN_THR_LO, 0) | ++ MTK_WED_TX_TKID_DYN_THR_HI); + wed_w32(dev, MTK_WED_TX_BM_DYN_THR, + FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO_V2, 0) | + MTK_WED_TX_BM_DYN_THR_HI_V2); +@@ -973,9 +1050,6 @@ mtk_wed_hw_init(struct mtk_wed_device *d + dev->tx_buf_ring.size / 128) | + FIELD_PREP(MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM, + 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); + } + + wed_w32(dev, dev->hw->soc->regmap.tx_bm_tkid, +@@ -985,26 +1059,62 @@ mtk_wed_hw_init(struct mtk_wed_device *d + + mtk_wed_reset(dev, MTK_WED_RESET_TX_BM); + ++ if (mtk_wed_is_v3_or_greater(dev->hw)) { ++ /* switch to new bm architecture */ ++ wed_clr(dev, MTK_WED_TX_BM_CTRL, ++ MTK_WED_TX_BM_CTRL_LEGACY_EN); ++ ++ wed_w32(dev, MTK_WED_TX_TKID_CTRL, ++ MTK_WED_TX_TKID_CTRL_PAUSE | ++ FIELD_PREP(MTK_WED_TX_TKID_CTRL_VLD_GRP_NUM_V3, ++ dev->wlan.nbuf / 128) | ++ FIELD_PREP(MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM_V3, ++ dev->wlan.nbuf / 128)); ++ /* return SKBID + SDP back to bm */ ++ wed_set(dev, MTK_WED_TX_TKID_CTRL, ++ MTK_WED_TX_TKID_CTRL_FREE_FORMAT); ++ ++ wed_w32(dev, MTK_WED_TX_BM_INIT_PTR, ++ MTK_WED_TX_BM_PKT_CNT | ++ MTK_WED_TX_BM_INIT_SW_TAIL_IDX); ++ } ++ + if (mtk_wed_is_v1(dev->hw)) { + wed_set(dev, MTK_WED_CTRL, + MTK_WED_CTRL_WED_TX_BM_EN | + MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); +- } else { +- wed_clr(dev, MTK_WED_TX_TKID_CTRL, MTK_WED_TX_TKID_CTRL_PAUSE); +- if (mtk_wed_get_rx_capa(dev)) { +- /* 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); +- } ++ } else if (mtk_wed_get_rx_capa(dev)) { ++ /* 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); ++ ++ /* reset prefetch index of ring */ ++ wed_set(dev, MTK_WED_WPDMA_RX_D_PREF_RX0_SIDX, ++ MTK_WED_WPDMA_RX_D_PREF_SIDX_IDX_CLR); ++ wed_clr(dev, MTK_WED_WPDMA_RX_D_PREF_RX0_SIDX, ++ MTK_WED_WPDMA_RX_D_PREF_SIDX_IDX_CLR); ++ ++ wed_set(dev, MTK_WED_WPDMA_RX_D_PREF_RX1_SIDX, ++ MTK_WED_WPDMA_RX_D_PREF_SIDX_IDX_CLR); ++ wed_clr(dev, MTK_WED_WPDMA_RX_D_PREF_RX1_SIDX, ++ MTK_WED_WPDMA_RX_D_PREF_SIDX_IDX_CLR); ++ ++ /* reset prefetch FIFO of ring */ ++ wed_set(dev, MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG, ++ MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG_R0_CLR | ++ MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG_R1_CLR); ++ wed_w32(dev, MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG, 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); ++ if (!mtk_wed_is_v1(dev->hw)) ++ wed_clr(dev, MTK_WED_TX_TKID_CTRL, MTK_WED_TX_TKID_CTRL_PAUSE); + } + + static void +@@ -1302,6 +1412,24 @@ mtk_wed_wdma_tx_ring_setup(struct mtk_we + dev->hw->soc->wdma_desc_size, true)) + return -ENOMEM; + ++ if (mtk_wed_is_v3_or_greater(dev->hw)) { ++ struct mtk_wdma_desc *desc = wdma->desc; ++ int i; ++ ++ for (i = 0; i < MTK_WED_WDMA_RING_SIZE; i++) { ++ desc->buf0 = 0; ++ desc->ctrl = cpu_to_le32(MTK_WDMA_DESC_CTRL_DMA_DONE); ++ desc->buf1 = 0; ++ desc->info = cpu_to_le32(MTK_WDMA_TXD0_DESC_INFO_DMA_DONE); ++ desc++; ++ desc->buf0 = 0; ++ desc->ctrl = cpu_to_le32(MTK_WDMA_DESC_CTRL_DMA_DONE); ++ desc->buf1 = 0; ++ desc->info = cpu_to_le32(MTK_WDMA_TXD1_DESC_INFO_DMA_DONE); ++ desc++; ++ } ++ } ++ + 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, +@@ -1367,6 +1495,9 @@ mtk_wed_configure_irq(struct mtk_wed_dev + + wed_clr(dev, MTK_WED_WDMA_INT_CTRL, wdma_mask); + } else { ++ if (mtk_wed_is_v3_or_greater(dev->hw)) ++ wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_TX_TKID_ALI_EN); ++ + /* initail tx interrupt trigger */ + wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_TX, + MTK_WED_WPDMA_INT_CTRL_TX0_DONE_EN | +@@ -1419,33 +1550,60 @@ mtk_wed_dma_enable(struct mtk_wed_device + { + int i; + +- wed_set(dev, MTK_WED_WPDMA_INT_CTRL, MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV); ++ if (!mtk_wed_is_v3_or_greater(dev->hw)) { ++ wed_set(dev, MTK_WED_WPDMA_INT_CTRL, ++ MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV); ++ wed_set(dev, MTK_WED_WPDMA_GLO_CFG, ++ MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN | ++ MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN); ++ wdma_set(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); ++ wed_set(dev, MTK_WED_WPDMA_CTRL, MTK_WED_WPDMA_CTRL_SDL1_FIXED); ++ } else { ++ wed_set(dev, MTK_WED_WPDMA_GLO_CFG, ++ MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN | ++ MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN | ++ MTK_WED_WPDMA_GLO_CFG_RX_DDONE2_WR); ++ wdma_set(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_TX_DMA_EN); ++ } + + wed_set(dev, MTK_WED_GLO_CFG, + MTK_WED_GLO_CFG_TX_DMA_EN | + MTK_WED_GLO_CFG_RX_DMA_EN); +- wed_set(dev, MTK_WED_WPDMA_GLO_CFG, +- MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN | +- MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN); ++ + wed_set(dev, MTK_WED_WDMA_GLO_CFG, + MTK_WED_WDMA_GLO_CFG_RX_DRV_EN); + +- wdma_set(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); +- + if (mtk_wed_is_v1(dev->hw)) { + wdma_set(dev, MTK_WDMA_GLO_CFG, + MTK_WDMA_GLO_CFG_RX_INFO3_PRERES); + return; + } + +- wed_set(dev, MTK_WED_WPDMA_CTRL, +- MTK_WED_WPDMA_CTRL_SDL1_FIXED); + 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); ++ ++ if (mtk_wed_is_v3_or_greater(dev->hw)) { ++ wed_set(dev, MTK_WED_WDMA_RX_PREF_CFG, ++ FIELD_PREP(MTK_WED_WDMA_RX_PREF_BURST_SIZE, 0x10) | ++ FIELD_PREP(MTK_WED_WDMA_RX_PREF_LOW_THRES, 0x8)); ++ wed_clr(dev, MTK_WED_WDMA_RX_PREF_CFG, ++ MTK_WED_WDMA_RX_PREF_DDONE2_EN); ++ wed_set(dev, MTK_WED_WDMA_RX_PREF_CFG, MTK_WED_WDMA_RX_PREF_EN); ++ ++ wed_clr(dev, MTK_WED_WPDMA_GLO_CFG, ++ MTK_WED_WPDMA_GLO_CFG_TX_DDONE_CHK_LAST); ++ wed_set(dev, MTK_WED_WPDMA_GLO_CFG, ++ MTK_WED_WPDMA_GLO_CFG_TX_DDONE_CHK | ++ MTK_WED_WPDMA_GLO_CFG_RX_DRV_EVENT_PKT_FMT_CHK | ++ MTK_WED_WPDMA_GLO_CFG_RX_DRV_UNS_VER_FORCE_4); ++ ++ wdma_set(dev, MTK_WDMA_PREF_RX_CFG, MTK_WDMA_PREF_RX_CFG_PREF_EN); ++ } ++ + 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); +@@ -1457,11 +1615,22 @@ mtk_wed_dma_enable(struct mtk_wed_device + MTK_WED_WDMA_GLO_CFG_TX_DRV_EN | + MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK); + ++ wed_clr(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, MTK_WED_WPDMA_RX_D_RXD_READ_LEN); + 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)); ++ FIELD_PREP(MTK_WED_WPDMA_RX_D_INIT_PHASE_RXEN_SEL, 0x2)); ++ ++ if (mtk_wed_is_v3_or_greater(dev->hw)) { ++ wed_set(dev, MTK_WED_WPDMA_RX_D_PREF_CFG, ++ MTK_WED_WPDMA_RX_D_PREF_EN | ++ FIELD_PREP(MTK_WED_WPDMA_RX_D_PREF_BURST_SIZE, 0x10) | ++ FIELD_PREP(MTK_WED_WPDMA_RX_D_PREF_LOW_THRES, 0x8)); ++ ++ wed_set(dev, MTK_WED_RRO_RX_D_CFG(2), MTK_WED_RRO_RX_D_DRV_EN); ++ wdma_set(dev, MTK_WDMA_PREF_TX_CFG, MTK_WDMA_PREF_TX_CFG_PREF_EN); ++ wdma_set(dev, MTK_WDMA_WRBK_TX_CFG, MTK_WDMA_WRBK_TX_CFG_WRBK_EN); ++ } + + for (i = 0; i < MTK_WED_RX_QUEUES; i++) + mtk_wed_check_wfdma_rx_fill(dev, i); +@@ -1501,6 +1670,12 @@ mtk_wed_start(struct mtk_wed_device *dev + wed_r32(dev, MTK_WED_EXT_INT_MASK1); + wed_r32(dev, MTK_WED_EXT_INT_MASK2); + ++ if (mtk_wed_is_v3_or_greater(dev->hw)) { ++ wed_w32(dev, MTK_WED_EXT_INT_MASK3, ++ MTK_WED_EXT_INT_STATUS_WPDMA_MID_RDY); ++ wed_r32(dev, MTK_WED_EXT_INT_MASK3); ++ } ++ + if (mtk_wed_rro_cfg(dev)) + return; + } +@@ -1552,6 +1727,7 @@ mtk_wed_attach(struct mtk_wed_device *de + dev->irq = hw->irq; + dev->wdma_idx = hw->index; + dev->version = hw->version; ++ dev->hw->pcie_base = mtk_wed_get_pcie_base(dev); + + if (hw->eth->dma_dev == hw->eth->dev && + of_dma_is_coherent(hw->eth->dev->of_node)) +@@ -1619,6 +1795,23 @@ mtk_wed_tx_ring_setup(struct mtk_wed_dev + ring->reg_base = MTK_WED_RING_TX(idx); + ring->wpdma = regs; + ++ if (mtk_wed_is_v3_or_greater(dev->hw) && idx == 1) { ++ /* reset prefetch index */ ++ wed_set(dev, MTK_WED_WDMA_RX_PREF_CFG, ++ MTK_WED_WDMA_RX_PREF_RX0_SIDX_CLR | ++ MTK_WED_WDMA_RX_PREF_RX1_SIDX_CLR); ++ ++ wed_clr(dev, MTK_WED_WDMA_RX_PREF_CFG, ++ MTK_WED_WDMA_RX_PREF_RX0_SIDX_CLR | ++ MTK_WED_WDMA_RX_PREF_RX1_SIDX_CLR); ++ ++ /* reset prefetch FIFO */ ++ wed_w32(dev, MTK_WED_WDMA_RX_PREF_FIFO_CFG, ++ MTK_WED_WDMA_RX_PREF_FIFO_RX0_CLR | ++ MTK_WED_WDMA_RX_PREF_FIFO_RX1_CLR); ++ wed_w32(dev, MTK_WED_WDMA_RX_PREF_FIFO_CFG, 0); ++ } ++ + /* WED -> WPDMA */ + wpdma_tx_w32(dev, idx, MTK_WED_RING_OFS_BASE, ring->desc_phys); + wpdma_tx_w32(dev, idx, MTK_WED_RING_OFS_COUNT, MTK_WED_TX_RING_SIZE); +@@ -1693,15 +1886,13 @@ mtk_wed_rx_ring_setup(struct mtk_wed_dev + static u32 + mtk_wed_irq_get(struct mtk_wed_device *dev, u32 mask) + { +- u32 val, ext_mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK; ++ u32 val, ext_mask; + +- if (mtk_wed_is_v1(dev->hw)) +- ext_mask |= MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR; ++ if (mtk_wed_is_v3_or_greater(dev->hw)) ++ ext_mask = MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT | ++ MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD; + else +- ext_mask |= MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH | +- MTK_WED_EXT_INT_STATUS_RX_FBUF_HI_TH | +- MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT | +- MTK_WED_EXT_INT_STATUS_TX_DMA_W_RESP_ERR; ++ ext_mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK; + + val = wed_r32(dev, MTK_WED_EXT_INT_STATUS); + wed_w32(dev, MTK_WED_EXT_INT_STATUS, val); +@@ -1942,6 +2133,9 @@ void mtk_wed_add_hw(struct device_node * + case 2: + hw->soc = &mt7986_data; + break; ++ case 3: ++ hw->soc = &mt7988_data; ++ break; + default: + case 1: + hw->mirror = syscon_regmap_lookup_by_phandle(eth_np, +--- a/drivers/net/ethernet/mediatek/mtk_wed.h ++++ b/drivers/net/ethernet/mediatek/mtk_wed.h +@@ -9,6 +9,8 @@ + #include + #include + ++#include "mtk_wed_regs.h" ++ + struct mtk_eth; + struct mtk_wed_wo; + +@@ -19,6 +21,7 @@ struct mtk_wed_soc_data { + u32 reset_idx_tx_mask; + u32 reset_idx_rx_mask; + } regmap; ++ u32 tx_ring_desc_size; + u32 wdma_desc_size; + }; + +@@ -35,6 +38,7 @@ struct mtk_wed_hw { + struct dentry *debugfs_dir; + struct mtk_wed_device *wed_dev; + struct mtk_wed_wo *wed_wo; ++ u32 pcie_base; + u32 debugfs_reg; + u32 num_flows; + u8 version; +@@ -61,6 +65,16 @@ static inline bool mtk_wed_is_v2(struct + return hw->version == 2; + } + ++static inline bool mtk_wed_is_v3(struct mtk_wed_hw *hw) ++{ ++ return hw->version == 3; ++} ++ ++static inline bool mtk_wed_is_v3_or_greater(struct mtk_wed_hw *hw) ++{ ++ return hw->version > 2; ++} ++ + static inline void + wed_w32(struct mtk_wed_device *dev, u32 reg, u32 val) + { +@@ -143,6 +157,21 @@ wpdma_txfree_w32(struct mtk_wed_device * + writel(val, dev->txfree_ring.wpdma + reg); + } + ++static inline u32 mtk_wed_get_pcie_base(struct mtk_wed_device *dev) ++{ ++ if (!mtk_wed_is_v3_or_greater(dev->hw)) ++ return MTK_WED_PCIE_BASE; ++ ++ switch (dev->hw->index) { ++ case 1: ++ return MTK_WED_PCIE_BASE1; ++ case 2: ++ return MTK_WED_PCIE_BASE2; ++ default: ++ return MTK_WED_PCIE_BASE0; ++ } ++} ++ + void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth, + void __iomem *wdma, phys_addr_t wdma_phy, + int index); +--- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c +@@ -331,10 +331,22 @@ mtk_wed_mcu_load_firmware(struct mtk_wed + wo->hw->index + 1); + + /* load firmware */ +- 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; ++ switch (wo->hw->version) { ++ case 2: ++ 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; ++ break; ++ case 3: ++ fw_name = wo->hw->index ? MT7988_FIRMWARE_WO1 ++ : MT7988_FIRMWARE_WO0; ++ break; ++ default: ++ return -EINVAL; ++ } + + ret = request_firmware(&fw, fw_name, wo->hw->dev); + if (ret) +@@ -355,15 +367,16 @@ mtk_wed_mcu_load_firmware(struct mtk_wed + } + + /* 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; ++ if (!mtk_wed_is_v3_or_greater(wo->hw) && wo->hw->index) ++ boot_cr = MTK_WO_MCU_CFG_LS_WA_BOOT_ADDR_ADDR; ++ else ++ boot_cr = 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; ++ val = wo_r32(wo, MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR) | ++ 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); +@@ -398,3 +411,5 @@ int mtk_wed_mcu_init(struct mtk_wed_wo * + MODULE_FIRMWARE(MT7981_FIRMWARE_WO); + MODULE_FIRMWARE(MT7986_FIRMWARE_WO0); + MODULE_FIRMWARE(MT7986_FIRMWARE_WO1); ++MODULE_FIRMWARE(MT7988_FIRMWARE_WO0); ++MODULE_FIRMWARE(MT7988_FIRMWARE_WO1); +--- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h ++++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h +@@ -13,6 +13,9 @@ + #define MTK_WDMA_DESC_CTRL_LAST_SEG0 BIT(30) + #define MTK_WDMA_DESC_CTRL_DMA_DONE BIT(31) + ++#define MTK_WDMA_TXD0_DESC_INFO_DMA_DONE BIT(29) ++#define MTK_WDMA_TXD1_DESC_INFO_DMA_DONE BIT(31) ++ + struct mtk_wdma_desc { + __le32 buf0; + __le32 ctrl; +@@ -37,6 +40,7 @@ struct mtk_wdma_desc { + #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_TX_AMSDU BIT(22) + #define MTK_WED_RESET_WED BIT(31) + + #define MTK_WED_CTRL 0x00c +@@ -44,6 +48,9 @@ struct mtk_wdma_desc { + #define MTK_WED_CTRL_WPDMA_INT_AGENT_BUSY BIT(1) + #define MTK_WED_CTRL_WDMA_INT_AGENT_EN BIT(2) + #define MTK_WED_CTRL_WDMA_INT_AGENT_BUSY BIT(3) ++#define MTK_WED_CTRL_WED_RX_IND_CMD_EN BIT(5) ++#define MTK_WED_CTRL_WED_RX_PG_BM_EN BIT(6) ++#define MTK_WED_CTRL_WED_RX_PG_BM_BUSY BIT(7) + #define MTK_WED_CTRL_WED_TX_BM_EN BIT(8) + #define MTK_WED_CTRL_WED_TX_BM_BUSY BIT(9) + #define MTK_WED_CTRL_WED_TX_FREE_AGENT_EN BIT(10) +@@ -54,9 +61,14 @@ struct mtk_wdma_desc { + #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_TX_TKID_ALI_EN BIT(20) ++#define MTK_WED_CTRL_TX_TKID_ALI_BUSY BIT(21) ++#define MTK_WED_CTRL_TX_AMSDU_EN BIT(22) ++#define MTK_WED_CTRL_TX_AMSDU_BUSY BIT(23) + #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) ++#define MTK_WED_CTRL_FLD_MIB_RD_CLR BIT(28) + + #define MTK_WED_EXT_INT_STATUS 0x020 + #define MTK_WED_EXT_INT_STATUS_TF_LEN_ERR BIT(0) +@@ -89,6 +101,7 @@ struct mtk_wdma_desc { + #define MTK_WED_EXT_INT_MASK 0x028 + #define MTK_WED_EXT_INT_MASK1 0x02c + #define MTK_WED_EXT_INT_MASK2 0x030 ++#define MTK_WED_EXT_INT_MASK3 0x034 + + #define MTK_WED_STATUS 0x060 + #define MTK_WED_STATUS_TX GENMASK(15, 8) +@@ -96,9 +109,14 @@ struct mtk_wdma_desc { + #define MTK_WED_TX_BM_CTRL 0x080 + #define MTK_WED_TX_BM_CTRL_VLD_GRP_NUM GENMASK(6, 0) + #define MTK_WED_TX_BM_CTRL_RSV_GRP_NUM GENMASK(22, 16) ++#define MTK_WED_TX_BM_CTRL_LEGACY_EN BIT(26) ++#define MTK_WED_TX_TKID_CTRL_FREE_FORMAT BIT(27) + #define MTK_WED_TX_BM_CTRL_PAUSE BIT(28) + + #define MTK_WED_TX_BM_BASE 0x084 ++#define MTK_WED_TX_BM_INIT_PTR 0x088 ++#define MTK_WED_TX_BM_SW_TAIL_IDX GENMASK(16, 0) ++#define MTK_WED_TX_BM_INIT_SW_TAIL_IDX BIT(16) + + #define MTK_WED_TX_BM_TKID_START GENMASK(15, 0) + #define MTK_WED_TX_BM_TKID_END GENMASK(31, 16) +@@ -122,6 +140,9 @@ struct mtk_wdma_desc { + #define MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM GENMASK(22, 16) + #define MTK_WED_TX_TKID_CTRL_PAUSE BIT(28) + ++#define MTK_WED_TX_TKID_CTRL_VLD_GRP_NUM_V3 GENMASK(7, 0) ++#define MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM_V3 GENMASK(23, 16) ++ + #define MTK_WED_TX_TKID_DYN_THR 0x0e0 + #define MTK_WED_TX_TKID_DYN_THR_LO GENMASK(6, 0) + #define MTK_WED_TX_TKID_DYN_THR_HI GENMASK(22, 16) +@@ -199,12 +220,15 @@ struct mtk_wdma_desc { + #define MTK_WED_WPDMA_GLO_CFG_RX_DRV_R1_PKT_PROC BIT(5) + #define MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC BIT(6) + #define MTK_WED_WPDMA_GLO_CFG_RX_DRV_R1_CRX_SYNC BIT(7) +-#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_EVENT_PKT_FMT_VER GENMASK(18, 16) ++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_EVENT_PKT_FMT_VER GENMASK(15, 12) ++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_UNS_VER_FORCE_4 BIT(18) + #define MTK_WED_WPDMA_GLO_CFG_RX_DRV_UNSUPPORT_FMT BIT(19) +-#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_UEVENT_PKT_FMT_CHK BIT(20) ++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_EVENT_PKT_FMT_CHK BIT(20) + #define MTK_WED_WPDMA_GLO_CFG_RX_DDONE2_WR BIT(21) + #define MTK_WED_WPDMA_GLO_CFG_TX_TKID_KEEP BIT(24) ++#define MTK_WED_WPDMA_GLO_CFG_TX_DDONE_CHK_LAST BIT(25) + #define MTK_WED_WPDMA_GLO_CFG_TX_DMAD_DW3_PREV BIT(28) ++#define MTK_WED_WPDMA_GLO_CFG_TX_DDONE_CHK BIT(30) + + #define MTK_WED_WPDMA_RESET_IDX 0x50c + #define MTK_WED_WPDMA_RESET_IDX_TX GENMASK(3, 0) +@@ -250,9 +274,10 @@ struct mtk_wdma_desc { + #define MTK_WED_PCIE_INT_TRIGGER_STATUS BIT(16) + + #define MTK_WED_PCIE_INT_CTRL 0x57c +-#define MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA BIT(20) +-#define MTK_WED_PCIE_INT_CTRL_SRC_SEL GENMASK(17, 16) + #define MTK_WED_PCIE_INT_CTRL_POLL_EN GENMASK(13, 12) ++#define MTK_WED_PCIE_INT_CTRL_SRC_SEL GENMASK(17, 16) ++#define MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA BIT(20) ++#define MTK_WED_PCIE_INT_CTRL_MSK_IRQ_FILTER BIT(21) + + #define MTK_WED_WPDMA_CFG_BASE 0x580 + #define MTK_WED_WPDMA_CFG_INT_MASK 0x584 +@@ -286,6 +311,20 @@ struct mtk_wdma_desc { + #define MTK_WED_WPDMA_RX_D_PROCESSED_MIB(_n) (0x784 + (_n) * 4) + #define MTK_WED_WPDMA_RX_D_COHERENT_MIB 0x78c + ++#define MTK_WED_WPDMA_RX_D_PREF_CFG 0x7b4 ++#define MTK_WED_WPDMA_RX_D_PREF_EN BIT(0) ++#define MTK_WED_WPDMA_RX_D_PREF_BURST_SIZE GENMASK(12, 8) ++#define MTK_WED_WPDMA_RX_D_PREF_LOW_THRES GENMASK(21, 16) ++ ++#define MTK_WED_WPDMA_RX_D_PREF_RX0_SIDX 0x7b8 ++#define MTK_WED_WPDMA_RX_D_PREF_SIDX_IDX_CLR BIT(15) ++ ++#define MTK_WED_WPDMA_RX_D_PREF_RX1_SIDX 0x7bc ++ ++#define MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG 0x7c0 ++#define MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG_R0_CLR BIT(0) ++#define MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG_R1_CLR BIT(16) ++ + #define MTK_WED_WDMA_RING_TX 0x800 + + #define MTK_WED_WDMA_TX_MIB 0x810 +@@ -293,6 +332,18 @@ struct mtk_wdma_desc { + #define MTK_WED_WDMA_RING_RX(_n) (0x900 + (_n) * 0x10) + #define MTK_WED_WDMA_RX_THRES(_n) (0x940 + (_n) * 0x4) + ++#define MTK_WED_WDMA_RX_PREF_CFG 0x950 ++#define MTK_WED_WDMA_RX_PREF_EN BIT(0) ++#define MTK_WED_WDMA_RX_PREF_BURST_SIZE GENMASK(12, 8) ++#define MTK_WED_WDMA_RX_PREF_LOW_THRES GENMASK(21, 16) ++#define MTK_WED_WDMA_RX_PREF_RX0_SIDX_CLR BIT(24) ++#define MTK_WED_WDMA_RX_PREF_RX1_SIDX_CLR BIT(25) ++#define MTK_WED_WDMA_RX_PREF_DDONE2_EN BIT(26) ++ ++#define MTK_WED_WDMA_RX_PREF_FIFO_CFG 0x95C ++#define MTK_WED_WDMA_RX_PREF_FIFO_RX0_CLR BIT(0) ++#define MTK_WED_WDMA_RX_PREF_FIFO_RX1_CLR BIT(16) ++ + #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) +@@ -325,6 +376,7 @@ struct mtk_wdma_desc { + #define MTK_WED_WDMA_INT_TRIGGER_RX_DONE GENMASK(17, 16) + + #define MTK_WED_WDMA_INT_CTRL 0xa2c ++#define MTK_WED_WDMA_INT_POLL_PRD GENMASK(7, 0) + #define MTK_WED_WDMA_INT_CTRL_POLL_SRC_SEL GENMASK(17, 16) + + #define MTK_WED_WDMA_CFG_BASE 0xaa0 +@@ -388,6 +440,18 @@ struct mtk_wdma_desc { + #define MTK_WDMA_INT_GRP1 0x250 + #define MTK_WDMA_INT_GRP2 0x254 + ++#define MTK_WDMA_PREF_TX_CFG 0x2d0 ++#define MTK_WDMA_PREF_TX_CFG_PREF_EN BIT(0) ++ ++#define MTK_WDMA_PREF_RX_CFG 0x2dc ++#define MTK_WDMA_PREF_RX_CFG_PREF_EN BIT(0) ++ ++#define MTK_WDMA_WRBK_TX_CFG 0x300 ++#define MTK_WDMA_WRBK_TX_CFG_WRBK_EN BIT(30) ++ ++#define MTK_WDMA_WRBK_RX_CFG 0x344 ++#define MTK_WDMA_WRBK_RX_CFG_WRBK_EN BIT(30) ++ + #define MTK_PCIE_MIRROR_MAP(n) ((n) ? 0x4 : 0x0) + #define MTK_PCIE_MIRROR_MAP_EN BIT(0) + #define MTK_PCIE_MIRROR_MAP_WED_ID BIT(1) +@@ -401,6 +465,30 @@ struct mtk_wdma_desc { + #define MTK_WED_RTQM_Q_DBG_BYPASS BIT(5) + #define MTK_WED_RTQM_TXDMAD_FPORT GENMASK(23, 20) + ++#define MTK_WED_RTQM_IGRS0_I2HW_DMAD_CNT 0xb1c ++#define MTK_WED_RTQM_IGRS0_I2H_DMAD_CNT(_n) (0xb20 + (_n) * 0x4) ++#define MTK_WED_RTQM_IGRS0_I2HW_PKT_CNT 0xb28 ++#define MTK_WED_RTQM_IGRS0_I2H_PKT_CNT(_n) (0xb2c + (_n) * 0x4) ++#define MTK_WED_RTQM_IGRS0_FDROP_CNT 0xb34 ++ ++#define MTK_WED_RTQM_IGRS1_I2HW_DMAD_CNT 0xb44 ++#define MTK_WED_RTQM_IGRS1_I2H_DMAD_CNT(_n) (0xb48 + (_n) * 0x4) ++#define MTK_WED_RTQM_IGRS1_I2HW_PKT_CNT 0xb50 ++#define MTK_WED_RTQM_IGRS1_I2H_PKT_CNT(_n) (0xb54 + (_n) * 0x4) ++#define MTK_WED_RTQM_IGRS1_FDROP_CNT 0xb5c ++ ++#define MTK_WED_RTQM_IGRS2_I2HW_DMAD_CNT 0xb6c ++#define MTK_WED_RTQM_IGRS2_I2H_DMAD_CNT(_n) (0xb70 + (_n) * 0x4) ++#define MTK_WED_RTQM_IGRS2_I2HW_PKT_CNT 0xb78 ++#define MTK_WED_RTQM_IGRS2_I2H_PKT_CNT(_n) (0xb7c + (_n) * 0x4) ++#define MTK_WED_RTQM_IGRS2_FDROP_CNT 0xb84 ++ ++#define MTK_WED_RTQM_IGRS3_I2HW_DMAD_CNT 0xb94 ++#define MTK_WED_RTQM_IGRS3_I2H_DMAD_CNT(_n) (0xb98 + (_n) * 0x4) ++#define MTK_WED_RTQM_IGRS3_I2HW_PKT_CNT 0xba0 ++#define MTK_WED_RTQM_IGRS3_I2H_PKT_CNT(_n) (0xba4 + (_n) * 0x4) ++#define MTK_WED_RTQM_IGRS3_FDROP_CNT 0xbac ++ + #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 +@@ -409,6 +497,24 @@ struct mtk_wdma_desc { + #define MTK_WED_RTQM_Q2B_MIB 0xb8c + #define MTK_WED_RTQM_PFDBK_MIB 0xb90 + ++#define MTK_WED_RTQM_ENQ_CFG0 0xbb8 ++#define MTK_WED_RTQM_ENQ_CFG_TXDMAD_FPORT GENMASK(15, 12) ++ ++#define MTK_WED_RTQM_FDROP_MIB 0xb84 ++#define MTK_WED_RTQM_ENQ_I2Q_DMAD_CNT 0xbbc ++#define MTK_WED_RTQM_ENQ_I2N_DMAD_CNT 0xbc0 ++#define MTK_WED_RTQM_ENQ_I2Q_PKT_CNT 0xbc4 ++#define MTK_WED_RTQM_ENQ_I2N_PKT_CNT 0xbc8 ++#define MTK_WED_RTQM_ENQ_USED_ENTRY_CNT 0xbcc ++#define MTK_WED_RTQM_ENQ_ERR_CNT 0xbd0 ++ ++#define MTK_WED_RTQM_DEQ_DMAD_CNT 0xbd8 ++#define MTK_WED_RTQM_DEQ_Q2I_DMAD_CNT 0xbdc ++#define MTK_WED_RTQM_DEQ_PKT_CNT 0xbe0 ++#define MTK_WED_RTQM_DEQ_Q2I_PKT_CNT 0xbe4 ++#define MTK_WED_RTQM_DEQ_USED_PFDBK_CNT 0xbe8 ++#define MTK_WED_RTQM_DEQ_ERR_CNT 0xbec ++ + #define MTK_WED_RROQM_GLO_CFG 0xc04 + #define MTK_WED_RROQM_RST_IDX 0xc08 + #define MTK_WED_RROQM_RST_IDX_MIOD BIT(0) +@@ -458,7 +564,116 @@ struct mtk_wdma_desc { + #define MTK_WED_RX_BM_INTF 0xd9c + #define MTK_WED_RX_BM_ERR_STS 0xda8 + ++#define MTK_RRO_IND_CMD_SIGNATURE 0xe00 ++#define MTK_RRO_IND_CMD_DMA_IDX GENMASK(11, 0) ++#define MTK_RRO_IND_CMD_MAGIC_CNT GENMASK(30, 28) ++ ++#define MTK_WED_IND_CMD_RX_CTRL0 0xe04 ++#define MTK_WED_IND_CMD_PROC_IDX GENMASK(11, 0) ++#define MTK_WED_IND_CMD_PREFETCH_FREE_CNT GENMASK(19, 16) ++#define MTK_WED_IND_CMD_MAGIC_CNT GENMASK(30, 28) ++ ++#define MTK_WED_IND_CMD_RX_CTRL1 0xe08 ++#define MTK_WED_IND_CMD_RX_CTRL2 0xe0c ++#define MTK_WED_IND_CMD_MAX_CNT GENMASK(11, 0) ++#define MTK_WED_IND_CMD_BASE_M GENMASK(19, 16) ++ ++#define MTK_WED_RRO_CFG0 0xe10 ++#define MTK_WED_RRO_CFG1 0xe14 ++#define MTK_WED_RRO_CFG1_MAX_WIN_SZ GENMASK(31, 29) ++#define MTK_WED_RRO_CFG1_ACK_SN_BASE_M GENMASK(19, 16) ++#define MTK_WED_RRO_CFG1_PARTICL_SE_ID GENMASK(11, 0) ++ ++#define MTK_WED_ADDR_ELEM_CFG0 0xe18 ++#define MTK_WED_ADDR_ELEM_CFG1 0xe1c ++#define MTK_WED_ADDR_ELEM_PREFETCH_FREE_CNT GENMASK(19, 16) ++ ++#define MTK_WED_ADDR_ELEM_TBL_CFG 0xe20 ++#define MTK_WED_ADDR_ELEM_TBL_OFFSET GENMASK(6, 0) ++#define MTK_WED_ADDR_ELEM_TBL_RD_RDY BIT(28) ++#define MTK_WED_ADDR_ELEM_TBL_WR_RDY BIT(29) ++#define MTK_WED_ADDR_ELEM_TBL_RD BIT(30) ++#define MTK_WED_ADDR_ELEM_TBL_WR BIT(31) ++ ++#define MTK_WED_RADDR_ELEM_TBL_WDATA 0xe24 ++#define MTK_WED_RADDR_ELEM_TBL_RDATA 0xe28 ++ ++#define MTK_WED_PN_CHECK_CFG 0xe30 ++#define MTK_WED_PN_CHECK_SE_ID GENMASK(11, 0) ++#define MTK_WED_PN_CHECK_RD_RDY BIT(28) ++#define MTK_WED_PN_CHECK_WR_RDY BIT(29) ++#define MTK_WED_PN_CHECK_RD BIT(30) ++#define MTK_WED_PN_CHECK_WR BIT(31) ++ ++#define MTK_WED_PN_CHECK_WDATA_M 0xe38 ++#define MTK_WED_PN_CHECK_IS_FIRST BIT(17) ++ ++#define MTK_WED_RRO_MSDU_PG_RING_CFG(_n) (0xe44 + (_n) * 0x8) ++ ++#define MTK_WED_RRO_MSDU_PG_RING2_CFG 0xe58 ++#define MTK_WED_RRO_MSDU_PG_DRV_CLR BIT(26) ++#define MTK_WED_RRO_MSDU_PG_DRV_EN BIT(31) ++ ++#define MTK_WED_RRO_MSDU_PG_CTRL0(_n) (0xe5c + (_n) * 0xc) ++#define MTK_WED_RRO_MSDU_PG_CTRL1(_n) (0xe60 + (_n) * 0xc) ++#define MTK_WED_RRO_MSDU_PG_CTRL2(_n) (0xe64 + (_n) * 0xc) ++ ++#define MTK_WED_RRO_RX_D_RX(_n) (0xe80 + (_n) * 0x10) ++ ++#define MTK_WED_RRO_RX_MAGIC_CNT BIT(13) ++ ++#define MTK_WED_RRO_RX_D_CFG(_n) (0xea0 + (_n) * 0x4) ++#define MTK_WED_RRO_RX_D_DRV_CLR BIT(26) ++#define MTK_WED_RRO_RX_D_DRV_EN BIT(31) ++ ++#define MTK_WED_RRO_PG_BM_RX_DMAM 0xeb0 ++#define MTK_WED_RRO_PG_BM_RX_SDL0 GENMASK(13, 0) ++ ++#define MTK_WED_RRO_PG_BM_BASE 0xeb4 ++#define MTK_WED_RRO_PG_BM_INIT_PTR 0xeb8 ++#define MTK_WED_RRO_PG_BM_SW_TAIL_IDX GENMASK(15, 0) ++#define MTK_WED_RRO_PG_BM_INIT_SW_TAIL_IDX BIT(16) ++ ++#define MTK_WED_WPDMA_INT_CTRL_RRO_RX 0xeec ++#define MTK_WED_WPDMA_INT_CTRL_RRO_RX0_EN BIT(0) ++#define MTK_WED_WPDMA_INT_CTRL_RRO_RX0_CLR BIT(1) ++#define MTK_WED_WPDMA_INT_CTRL_RRO_RX0_DONE_TRIG GENMASK(6, 2) ++#define MTK_WED_WPDMA_INT_CTRL_RRO_RX1_EN BIT(8) ++#define MTK_WED_WPDMA_INT_CTRL_RRO_RX1_CLR BIT(9) ++#define MTK_WED_WPDMA_INT_CTRL_RRO_RX1_DONE_TRIG GENMASK(14, 10) ++ ++#define MTK_WED_WPDMA_INT_CTRL_RRO_MSDU_PG 0xef4 ++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG0_EN BIT(0) ++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG0_CLR BIT(1) ++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG0_DONE_TRIG GENMASK(6, 2) ++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG1_EN BIT(8) ++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG1_CLR BIT(9) ++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG1_DONE_TRIG GENMASK(14, 10) ++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_EN BIT(16) ++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_CLR BIT(17) ++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_DONE_TRIG GENMASK(22, 18) ++ ++#define MTK_WED_RX_IND_CMD_CNT0 0xf20 ++#define MTK_WED_RX_IND_CMD_DBG_CNT_EN BIT(31) ++ ++#define MTK_WED_RX_IND_CMD_CNT(_n) (0xf20 + (_n) * 0x4) ++#define MTK_WED_IND_CMD_MAGIC_CNT_FAIL_CNT GENMASK(15, 0) ++ ++#define MTK_WED_RX_ADDR_ELEM_CNT(_n) (0xf48 + (_n) * 0x4) ++#define MTK_WED_ADDR_ELEM_SIG_FAIL_CNT GENMASK(15, 0) ++#define MTK_WED_ADDR_ELEM_FIRST_SIG_FAIL_CNT GENMASK(31, 16) ++#define MTK_WED_ADDR_ELEM_ACKSN_CNT GENMASK(27, 0) ++ ++#define MTK_WED_RX_MSDU_PG_CNT(_n) (0xf5c + (_n) * 0x4) ++ ++#define MTK_WED_RX_PN_CHK_CNT 0xf70 ++#define MTK_WED_PN_CHK_FAIL_CNT GENMASK(15, 0) ++ + #define MTK_WED_WOCPU_VIEW_MIOD_BASE 0x8000 + #define MTK_WED_PCIE_INT_MASK 0x0 + ++#define MTK_WED_PCIE_BASE 0x11280000 ++#define MTK_WED_PCIE_BASE0 0x11300000 ++#define MTK_WED_PCIE_BASE1 0x11310000 ++#define MTK_WED_PCIE_BASE2 0x11290000 + #endif +--- a/drivers/net/ethernet/mediatek/mtk_wed_wo.h ++++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h +@@ -91,6 +91,8 @@ enum mtk_wed_dummy_cr_idx { + #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" ++#define MT7988_FIRMWARE_WO0 "mediatek/mt7988_wo_0.bin" ++#define MT7988_FIRMWARE_WO1 "mediatek/mt7988_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) +--- a/include/linux/soc/mediatek/mtk_wed.h ++++ b/include/linux/soc/mediatek/mtk_wed.h +@@ -139,6 +139,8 @@ struct mtk_wed_device { + u32 wpdma_rx; + + bool wcid_512; ++ bool hw_rro; ++ bool msi; + + u16 token_start; + unsigned int nbuf; +@@ -212,10 +214,12 @@ mtk_wed_device_attach(struct mtk_wed_dev + return ret; + } + +-static inline bool +-mtk_wed_get_rx_capa(struct mtk_wed_device *dev) ++static inline bool mtk_wed_get_rx_capa(struct mtk_wed_device *dev) + { + #ifdef CONFIG_NET_MEDIATEK_SOC_WED ++ if (dev->version == 3) ++ return dev->wlan.hw_rro; ++ + return dev->version != 1; + #else + return false; diff --git a/target/linux/generic/backport-6.6/752-15-v6.7-net-ethernet-mtk_wed-refactor-mtk_wed_check_wfdma_rx.patch b/target/linux/generic/backport-6.6/752-15-v6.7-net-ethernet-mtk_wed-refactor-mtk_wed_check_wfdma_rx.patch new file mode 100644 index 0000000000..e91ae69d08 --- /dev/null +++ b/target/linux/generic/backport-6.6/752-15-v6.7-net-ethernet-mtk_wed-refactor-mtk_wed_check_wfdma_rx.patch @@ -0,0 +1,95 @@ +From: Lorenzo Bianconi +Date: Mon, 18 Sep 2023 12:29:14 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: refactor mtk_wed_check_wfdma_rx_fill + routine + +Refactor mtk_wed_check_wfdma_rx_fill() in order to be reused adding HW +receive offload support for MT7988 SoC. + +Co-developed-by: Sujuan Chen +Signed-off-by: Sujuan Chen +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -585,22 +585,15 @@ 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) ++static int ++mtk_wed_check_wfdma_rx_fill(struct mtk_wed_device *dev, ++ struct mtk_wed_ring *ring) + { +- 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; ++ u32 cur_idx = readl(ring->wpdma + MTK_WED_RING_OFS_CPU_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; + +@@ -609,12 +602,10 @@ mtk_wed_check_wfdma_rx_fill(struct mtk_w + + if (i == 3) { + dev_err(dev->hw->dev, "rx dma enable failed\n"); +- return; ++ return -ETIMEDOUT; + } + +- 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); ++ return 0; + } + + static void +@@ -1545,6 +1536,7 @@ mtk_wed_configure_irq(struct mtk_wed_dev + wed_w32(dev, MTK_WED_INT_MASK, irq_mask); + } + ++#define MTK_WFMDA_RX_DMA_EN BIT(2) + static void + mtk_wed_dma_enable(struct mtk_wed_device *dev) + { +@@ -1632,8 +1624,26 @@ mtk_wed_dma_enable(struct mtk_wed_device + wdma_set(dev, MTK_WDMA_WRBK_TX_CFG, MTK_WDMA_WRBK_TX_CFG_WRBK_EN); + } + +- for (i = 0; i < MTK_WED_RX_QUEUES; i++) +- mtk_wed_check_wfdma_rx_fill(dev, i); ++ for (i = 0; i < MTK_WED_RX_QUEUES; i++) { ++ struct mtk_wed_ring *ring = &dev->rx_ring[i]; ++ u32 val; ++ ++ if (!(ring->flags & MTK_WED_RING_CONFIGURED)) ++ continue; /* queue is not configured by mt76 */ ++ ++ if (mtk_wed_check_wfdma_rx_fill(dev, ring)) { ++ dev_err(dev->hw->dev, ++ "rx_ring(%d) dma enable failed\n", i); ++ continue; ++ } ++ ++ 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 diff --git a/target/linux/generic/backport-6.6/752-16-v6.7-net-ethernet-mtk_wed-introduce-partial-AMSDU-offload.patch b/target/linux/generic/backport-6.6/752-16-v6.7-net-ethernet-mtk_wed-introduce-partial-AMSDU-offload.patch new file mode 100644 index 0000000000..6534d73d8e --- /dev/null +++ b/target/linux/generic/backport-6.6/752-16-v6.7-net-ethernet-mtk_wed-introduce-partial-AMSDU-offload.patch @@ -0,0 +1,465 @@ +From: Sujuan Chen +Date: Mon, 18 Sep 2023 12:29:15 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: introduce partial AMSDU offload + support for MT7988 + +Introduce partial AMSDU offload support for MT7988 SoC in order to merge +in hw packets belonging to the same AMSDU before passing them to the +WLAN nic. + +Co-developed-by: Lorenzo Bianconi +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Sujuan Chen +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_ppe.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c +@@ -438,7 +438,8 @@ int mtk_foe_entry_set_pppoe(struct mtk_e + } + + 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 wdma_idx, int txq, int bss, int wcid, ++ bool amsdu_en) + { + struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry); + u32 *ib2 = mtk_foe_entry_ib2(eth, entry); +@@ -450,6 +451,7 @@ int mtk_foe_entry_set_wdma(struct mtk_et + MTK_FOE_IB2_WDMA_WINFO_V2; + l2->w3info = FIELD_PREP(MTK_FOE_WINFO_WCID_V3, wcid) | + FIELD_PREP(MTK_FOE_WINFO_BSS_V3, bss); ++ l2->amsdu = FIELD_PREP(MTK_FOE_WINFO_AMSDU_EN, amsdu_en); + break; + case 2: + *ib2 &= ~MTK_FOE_IB2_PORT_MG_V2; +--- a/drivers/net/ethernet/mediatek/mtk_ppe.h ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h +@@ -88,13 +88,13 @@ enum { + #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) ++#define MTK_FOE_WINFO_AMSDU_USR_INFO GENMASK(15, 0) ++#define MTK_FOE_WINFO_AMSDU_TID GENMASK(19, 16) ++#define MTK_FOE_WINFO_AMSDU_IS_FIXEDRATE BIT(20) ++#define MTK_FOE_WINFO_AMSDU_IS_PRIOR BIT(21) ++#define MTK_FOE_WINFO_AMSDU_IS_SP BIT(22) ++#define MTK_FOE_WINFO_AMSDU_HF BIT(23) ++#define MTK_FOE_WINFO_AMSDU_EN BIT(24) + + enum { + MTK_FOE_STATE_INVALID, +@@ -123,7 +123,7 @@ struct mtk_foe_mac_info { + + /* netsys_v3 */ + u32 w3info; +- u32 wpao; ++ u32 amsdu; + }; + + /* software-only entry type */ +@@ -394,7 +394,8 @@ int mtk_foe_entry_set_vlan(struct mtk_et + int mtk_foe_entry_set_pppoe(struct mtk_eth *eth, struct mtk_foe_entry *entry, + 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 wdma_idx, int txq, int bss, int wcid, ++ bool amsdu_en); + 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); +--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +@@ -111,6 +111,7 @@ mtk_flow_get_wdma_info(struct net_device + info->queue = path->mtk_wdma.queue; + info->bss = path->mtk_wdma.bss; + info->wcid = path->mtk_wdma.wcid; ++ info->amsdu = path->mtk_wdma.amsdu; + + return 0; + } +@@ -192,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); ++ info.bss, info.wcid, info.amsdu); + if (mtk_is_netsys_v2_or_greater(eth)) { + switch (info.wdma_idx) { + case 0: +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -29,6 +29,8 @@ + #define MTK_WED_RX_PAGE_BUF_PER_PAGE (PAGE_SIZE / 128) + #define MTK_WED_RX_RING_SIZE 1536 + #define MTK_WED_RX_PG_BM_CNT 8192 ++#define MTK_WED_AMSDU_BUF_SIZE (PAGE_SIZE << 4) ++#define MTK_WED_AMSDU_NPAGES 32 + + #define MTK_WED_TX_RING_SIZE 2048 + #define MTK_WED_WDMA_RING_SIZE 1024 +@@ -172,6 +174,23 @@ mtk_wdma_rx_reset(struct mtk_wed_device + return ret; + } + ++static u32 ++mtk_wed_check_busy(struct mtk_wed_device *dev, u32 reg, u32 mask) ++{ ++ return !!(wed_r32(dev, reg) & mask); ++} ++ ++static int ++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, reg, mask); ++} ++ + static void + mtk_wdma_tx_reset(struct mtk_wed_device *dev) + { +@@ -335,6 +354,118 @@ out: + } + + static int ++mtk_wed_amsdu_buffer_alloc(struct mtk_wed_device *dev) ++{ ++ struct mtk_wed_hw *hw = dev->hw; ++ struct mtk_wed_amsdu *wed_amsdu; ++ int i; ++ ++ if (!mtk_wed_is_v3_or_greater(hw)) ++ return 0; ++ ++ wed_amsdu = devm_kcalloc(hw->dev, MTK_WED_AMSDU_NPAGES, ++ sizeof(*wed_amsdu), GFP_KERNEL); ++ if (!wed_amsdu) ++ return -ENOMEM; ++ ++ for (i = 0; i < MTK_WED_AMSDU_NPAGES; i++) { ++ void *ptr; ++ ++ /* each segment is 64K */ ++ ptr = (void *)__get_free_pages(GFP_KERNEL | __GFP_NOWARN | ++ __GFP_ZERO | __GFP_COMP | ++ GFP_DMA32, ++ get_order(MTK_WED_AMSDU_BUF_SIZE)); ++ if (!ptr) ++ goto error; ++ ++ wed_amsdu[i].txd = ptr; ++ wed_amsdu[i].txd_phy = dma_map_single(hw->dev, ptr, ++ MTK_WED_AMSDU_BUF_SIZE, ++ DMA_TO_DEVICE); ++ if (dma_mapping_error(hw->dev, wed_amsdu[i].txd_phy)) ++ goto error; ++ } ++ dev->hw->wed_amsdu = wed_amsdu; ++ ++ return 0; ++ ++error: ++ for (i--; i >= 0; i--) ++ dma_unmap_single(hw->dev, wed_amsdu[i].txd_phy, ++ MTK_WED_AMSDU_BUF_SIZE, DMA_TO_DEVICE); ++ return -ENOMEM; ++} ++ ++static void ++mtk_wed_amsdu_free_buffer(struct mtk_wed_device *dev) ++{ ++ struct mtk_wed_amsdu *wed_amsdu = dev->hw->wed_amsdu; ++ int i; ++ ++ if (!wed_amsdu) ++ return; ++ ++ for (i = 0; i < MTK_WED_AMSDU_NPAGES; i++) { ++ dma_unmap_single(dev->hw->dev, wed_amsdu[i].txd_phy, ++ MTK_WED_AMSDU_BUF_SIZE, DMA_TO_DEVICE); ++ free_pages((unsigned long)wed_amsdu[i].txd, ++ get_order(MTK_WED_AMSDU_BUF_SIZE)); ++ } ++} ++ ++static int ++mtk_wed_amsdu_init(struct mtk_wed_device *dev) ++{ ++ struct mtk_wed_amsdu *wed_amsdu = dev->hw->wed_amsdu; ++ int i, ret; ++ ++ if (!wed_amsdu) ++ return 0; ++ ++ for (i = 0; i < MTK_WED_AMSDU_NPAGES; i++) ++ wed_w32(dev, MTK_WED_AMSDU_HIFTXD_BASE_L(i), ++ wed_amsdu[i].txd_phy); ++ ++ /* init all sta parameter */ ++ wed_w32(dev, MTK_WED_AMSDU_STA_INFO_INIT, MTK_WED_AMSDU_STA_RMVL | ++ MTK_WED_AMSDU_STA_WTBL_HDRT_MODE | ++ FIELD_PREP(MTK_WED_AMSDU_STA_MAX_AMSDU_LEN, ++ dev->wlan.amsdu_max_len >> 8) | ++ FIELD_PREP(MTK_WED_AMSDU_STA_MAX_AMSDU_NUM, ++ dev->wlan.amsdu_max_subframes)); ++ ++ wed_w32(dev, MTK_WED_AMSDU_STA_INFO, MTK_WED_AMSDU_STA_INFO_DO_INIT); ++ ++ ret = mtk_wed_poll_busy(dev, MTK_WED_AMSDU_STA_INFO, ++ MTK_WED_AMSDU_STA_INFO_DO_INIT); ++ if (ret) { ++ dev_err(dev->hw->dev, "amsdu initialization failed\n"); ++ return ret; ++ } ++ ++ /* init partial amsdu offload txd src */ ++ wed_set(dev, MTK_WED_AMSDU_HIFTXD_CFG, ++ FIELD_PREP(MTK_WED_AMSDU_HIFTXD_SRC, dev->hw->index)); ++ ++ /* init qmem */ ++ wed_set(dev, MTK_WED_AMSDU_PSE, MTK_WED_AMSDU_PSE_RESET); ++ ret = mtk_wed_poll_busy(dev, MTK_WED_MON_AMSDU_QMEM_STS1, BIT(29)); ++ if (ret) { ++ pr_info("%s: amsdu qmem initialization failed\n", __func__); ++ return ret; ++ } ++ ++ /* eagle E1 PCIE1 tx ring 22 flow control issue */ ++ if (dev->wlan.id == 0x7991) ++ wed_clr(dev, MTK_WED_AMSDU_FIFO, MTK_WED_AMSDU_IS_PRIOR0_RING); ++ ++ wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_TX_AMSDU_EN); ++ ++ return 0; ++} ++ ++static int + mtk_wed_tx_buffer_alloc(struct mtk_wed_device *dev) + { + u32 desc_size = dev->hw->soc->tx_ring_desc_size; +@@ -708,6 +839,7 @@ __mtk_wed_detach(struct mtk_wed_device * + + mtk_wdma_rx_reset(dev); + mtk_wed_reset(dev, MTK_WED_RESET_WED); ++ mtk_wed_amsdu_free_buffer(dev); + mtk_wed_free_tx_buffer(dev); + mtk_wed_free_tx_rings(dev); + +@@ -1128,23 +1260,6 @@ mtk_wed_ring_reset(struct mtk_wed_ring * + } + } + +-static u32 +-mtk_wed_check_busy(struct mtk_wed_device *dev, u32 reg, u32 mask) +-{ +- return !!(wed_r32(dev, reg) & mask); +-} +- +-static int +-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, reg, mask); +-} +- + static int + mtk_wed_rx_reset(struct mtk_wed_device *dev) + { +@@ -1691,6 +1806,7 @@ mtk_wed_start(struct mtk_wed_device *dev + } + + mtk_wed_set_512_support(dev, dev->wlan.wcid_512); ++ mtk_wed_amsdu_init(dev); + + mtk_wed_dma_enable(dev); + dev->running = true; +@@ -1747,6 +1863,10 @@ mtk_wed_attach(struct mtk_wed_device *de + if (ret) + goto out; + ++ ret = mtk_wed_amsdu_buffer_alloc(dev); ++ if (ret) ++ goto out; ++ + if (mtk_wed_get_rx_capa(dev)) { + ret = mtk_wed_rro_alloc(dev); + if (ret) +--- a/drivers/net/ethernet/mediatek/mtk_wed.h ++++ b/drivers/net/ethernet/mediatek/mtk_wed.h +@@ -25,6 +25,11 @@ struct mtk_wed_soc_data { + u32 wdma_desc_size; + }; + ++struct mtk_wed_amsdu { ++ void *txd; ++ dma_addr_t txd_phy; ++}; ++ + struct mtk_wed_hw { + const struct mtk_wed_soc_data *soc; + struct device_node *node; +@@ -38,6 +43,7 @@ struct mtk_wed_hw { + struct dentry *debugfs_dir; + struct mtk_wed_device *wed_dev; + struct mtk_wed_wo *wed_wo; ++ struct mtk_wed_amsdu *wed_amsdu; + u32 pcie_base; + u32 debugfs_reg; + u32 num_flows; +@@ -52,6 +58,7 @@ struct mtk_wdma_info { + u8 queue; + u16 wcid; + u8 bss; ++ u8 amsdu; + }; + + #ifdef CONFIG_NET_MEDIATEK_SOC_WED +--- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h ++++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h +@@ -672,6 +672,82 @@ struct mtk_wdma_desc { + #define MTK_WED_WOCPU_VIEW_MIOD_BASE 0x8000 + #define MTK_WED_PCIE_INT_MASK 0x0 + ++#define MTK_WED_AMSDU_FIFO 0x1800 ++#define MTK_WED_AMSDU_IS_PRIOR0_RING BIT(10) ++ ++#define MTK_WED_AMSDU_STA_INFO 0x01810 ++#define MTK_WED_AMSDU_STA_INFO_DO_INIT BIT(0) ++#define MTK_WED_AMSDU_STA_INFO_SET_INIT BIT(1) ++ ++#define MTK_WED_AMSDU_STA_INFO_INIT 0x01814 ++#define MTK_WED_AMSDU_STA_WTBL_HDRT_MODE BIT(0) ++#define MTK_WED_AMSDU_STA_RMVL BIT(1) ++#define MTK_WED_AMSDU_STA_MAX_AMSDU_LEN GENMASK(7, 2) ++#define MTK_WED_AMSDU_STA_MAX_AMSDU_NUM GENMASK(11, 8) ++ ++#define MTK_WED_AMSDU_HIFTXD_BASE_L(_n) (0x1980 + (_n) * 0x4) ++ ++#define MTK_WED_AMSDU_PSE 0x1910 ++#define MTK_WED_AMSDU_PSE_RESET BIT(16) ++ ++#define MTK_WED_AMSDU_HIFTXD_CFG 0x1968 ++#define MTK_WED_AMSDU_HIFTXD_SRC GENMASK(16, 15) ++ ++#define MTK_WED_MON_AMSDU_FIFO_DMAD 0x1a34 ++ ++#define MTK_WED_MON_AMSDU_ENG_DMAD(_n) (0x1a80 + (_n) * 0x50) ++#define MTK_WED_MON_AMSDU_ENG_QFPL(_n) (0x1a84 + (_n) * 0x50) ++#define MTK_WED_MON_AMSDU_ENG_QENI(_n) (0x1a88 + (_n) * 0x50) ++#define MTK_WED_MON_AMSDU_ENG_QENO(_n) (0x1a8c + (_n) * 0x50) ++#define MTK_WED_MON_AMSDU_ENG_MERG(_n) (0x1a90 + (_n) * 0x50) ++ ++#define MTK_WED_MON_AMSDU_ENG_CNT8(_n) (0x1a94 + (_n) * 0x50) ++#define MTK_WED_AMSDU_ENG_MAX_QGPP_CNT GENMASK(10, 0) ++#define MTK_WED_AMSDU_ENG_MAX_PL_CNT GENMASK(27, 16) ++ ++#define MTK_WED_MON_AMSDU_ENG_CNT9(_n) (0x1a98 + (_n) * 0x50) ++#define MTK_WED_AMSDU_ENG_CUR_ENTRY GENMASK(10, 0) ++#define MTK_WED_AMSDU_ENG_MAX_BUF_MERGED GENMASK(20, 16) ++#define MTK_WED_AMSDU_ENG_MAX_MSDU_MERGED GENMASK(28, 24) ++ ++#define MTK_WED_MON_AMSDU_QMEM_STS1 0x1e04 ++ ++#define MTK_WED_MON_AMSDU_QMEM_CNT(_n) (0x1e0c + (_n) * 0x4) ++#define MTK_WED_AMSDU_QMEM_FQ_CNT GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_SP_QCNT GENMASK(11, 0) ++#define MTK_WED_AMSDU_QMEM_TID0_QCNT GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_TID1_QCNT GENMASK(11, 0) ++#define MTK_WED_AMSDU_QMEM_TID2_QCNT GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_TID3_QCNT GENMASK(11, 0) ++#define MTK_WED_AMSDU_QMEM_TID4_QCNT GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_TID5_QCNT GENMASK(11, 0) ++#define MTK_WED_AMSDU_QMEM_TID6_QCNT GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_TID7_QCNT GENMASK(11, 0) ++ ++#define MTK_WED_MON_AMSDU_QMEM_PTR(_n) (0x1e20 + (_n) * 0x4) ++#define MTK_WED_AMSDU_QMEM_FQ_HEAD GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_SP_QHEAD GENMASK(11, 0) ++#define MTK_WED_AMSDU_QMEM_TID0_QHEAD GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_TID1_QHEAD GENMASK(11, 0) ++#define MTK_WED_AMSDU_QMEM_TID2_QHEAD GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_TID3_QHEAD GENMASK(11, 0) ++#define MTK_WED_AMSDU_QMEM_TID4_QHEAD GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_TID5_QHEAD GENMASK(11, 0) ++#define MTK_WED_AMSDU_QMEM_TID6_QHEAD GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_TID7_QHEAD GENMASK(11, 0) ++#define MTK_WED_AMSDU_QMEM_FQ_TAIL GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_SP_QTAIL GENMASK(11, 0) ++#define MTK_WED_AMSDU_QMEM_TID0_QTAIL GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_TID1_QTAIL GENMASK(11, 0) ++#define MTK_WED_AMSDU_QMEM_TID2_QTAIL GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_TID3_QTAIL GENMASK(11, 0) ++#define MTK_WED_AMSDU_QMEM_TID4_QTAIL GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_TID5_QTAIL GENMASK(11, 0) ++#define MTK_WED_AMSDU_QMEM_TID6_QTAIL GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_TID7_QTAIL GENMASK(11, 0) ++ ++#define MTK_WED_MON_AMSDU_HIFTXD_FETCH_MSDU(_n) (0x1ec4 + (_n) * 0x4) ++ + #define MTK_WED_PCIE_BASE 0x11280000 + #define MTK_WED_PCIE_BASE0 0x11300000 + #define MTK_WED_PCIE_BASE1 0x11310000 +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -928,6 +928,7 @@ struct net_device_path { + u8 queue; + u16 wcid; + u8 bss; ++ u8 amsdu; + } mtk_wdma; + }; + }; +--- a/include/linux/soc/mediatek/mtk_wed.h ++++ b/include/linux/soc/mediatek/mtk_wed.h +@@ -129,6 +129,7 @@ struct mtk_wed_device { + enum mtk_wed_bus_tye bus_type; + void __iomem *base; + u32 phy_base; ++ u32 id; + + u32 wpdma_phys; + u32 wpdma_int; +@@ -147,10 +148,12 @@ struct mtk_wed_device { + unsigned int rx_nbuf; + unsigned int rx_npkt; + unsigned int rx_size; ++ unsigned int amsdu_max_len; + + u8 tx_tbit[MTK_WED_TX_QUEUES]; + u8 rx_tbit[MTK_WED_RX_QUEUES]; + u8 txfree_tbit; ++ u8 amsdu_max_subframes; + + u32 (*init_buf)(void *ptr, dma_addr_t phys, int token_id); + int (*offload_enable)(struct mtk_wed_device *wed); +@@ -224,6 +227,15 @@ static inline bool mtk_wed_get_rx_capa(s + #else + return false; + #endif ++} ++ ++static inline bool mtk_wed_is_amsdu_supported(struct mtk_wed_device *dev) ++{ ++#ifdef CONFIG_NET_MEDIATEK_SOC_WED ++ return dev->version == 3; ++#else ++ return false; ++#endif + } + + #ifdef CONFIG_NET_MEDIATEK_SOC_WED diff --git a/target/linux/generic/backport-6.6/752-17-v6.7-net-ethernet-mtk_wed-introduce-hw_rro-support-for-MT.patch b/target/linux/generic/backport-6.6/752-17-v6.7-net-ethernet-mtk_wed-introduce-hw_rro-support-for-MT.patch new file mode 100644 index 0000000000..0cf4c18875 --- /dev/null +++ b/target/linux/generic/backport-6.6/752-17-v6.7-net-ethernet-mtk_wed-introduce-hw_rro-support-for-MT.patch @@ -0,0 +1,483 @@ +From: Sujuan Chen +Date: Mon, 18 Sep 2023 12:29:16 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: introduce hw_rro support for MT7988 + +MT7988 SoC support 802.11 receive reordering offload in hw while +MT7986 SoC implements it through the firmware running on the mcu. + +Co-developed-by: Lorenzo Bianconi +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Sujuan Chen +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -26,7 +26,7 @@ + #define MTK_WED_BUF_SIZE 2048 + #define MTK_WED_PAGE_BUF_SIZE 128 + #define MTK_WED_BUF_PER_PAGE (PAGE_SIZE / 2048) +-#define MTK_WED_RX_PAGE_BUF_PER_PAGE (PAGE_SIZE / 128) ++#define MTK_WED_RX_BUF_PER_PAGE (PAGE_SIZE / MTK_WED_PAGE_BUF_SIZE) + #define MTK_WED_RX_RING_SIZE 1536 + #define MTK_WED_RX_PG_BM_CNT 8192 + #define MTK_WED_AMSDU_BUF_SIZE (PAGE_SIZE << 4) +@@ -596,6 +596,68 @@ free_pagelist: + } + + static int ++mtk_wed_hwrro_buffer_alloc(struct mtk_wed_device *dev) ++{ ++ int n_pages = MTK_WED_RX_PG_BM_CNT / MTK_WED_RX_BUF_PER_PAGE; ++ struct mtk_wed_buf *page_list; ++ struct mtk_wed_bm_desc *desc; ++ dma_addr_t desc_phys; ++ int i, page_idx = 0; ++ ++ if (!dev->wlan.hw_rro) ++ return 0; ++ ++ page_list = kcalloc(n_pages, sizeof(*page_list), GFP_KERNEL); ++ if (!page_list) ++ return -ENOMEM; ++ ++ dev->hw_rro.size = dev->wlan.rx_nbuf & ~(MTK_WED_BUF_PER_PAGE - 1); ++ dev->hw_rro.pages = page_list; ++ desc = dma_alloc_coherent(dev->hw->dev, ++ dev->wlan.rx_nbuf * sizeof(*desc), ++ &desc_phys, GFP_KERNEL); ++ if (!desc) ++ return -ENOMEM; ++ ++ dev->hw_rro.desc = desc; ++ dev->hw_rro.desc_phys = desc_phys; ++ ++ for (i = 0; i < MTK_WED_RX_PG_BM_CNT; i += MTK_WED_RX_BUF_PER_PAGE) { ++ dma_addr_t page_phys, buf_phys; ++ struct page *page; ++ int s; ++ ++ page = __dev_alloc_page(GFP_KERNEL); ++ if (!page) ++ return -ENOMEM; ++ ++ page_phys = dma_map_page(dev->hw->dev, page, 0, PAGE_SIZE, ++ DMA_BIDIRECTIONAL); ++ if (dma_mapping_error(dev->hw->dev, page_phys)) { ++ __free_page(page); ++ return -ENOMEM; ++ } ++ ++ page_list[page_idx].p = page; ++ page_list[page_idx++].phy_addr = page_phys; ++ dma_sync_single_for_cpu(dev->hw->dev, page_phys, PAGE_SIZE, ++ DMA_BIDIRECTIONAL); ++ ++ buf_phys = page_phys; ++ for (s = 0; s < MTK_WED_RX_BUF_PER_PAGE; s++) { ++ desc->buf0 = cpu_to_le32(buf_phys); ++ buf_phys += MTK_WED_PAGE_BUF_SIZE; ++ desc++; ++ } ++ ++ dma_sync_single_for_device(dev->hw->dev, page_phys, PAGE_SIZE, ++ DMA_BIDIRECTIONAL); ++ } ++ ++ return 0; ++} ++ ++static int + mtk_wed_rx_buffer_alloc(struct mtk_wed_device *dev) + { + struct mtk_wed_bm_desc *desc; +@@ -612,7 +674,42 @@ mtk_wed_rx_buffer_alloc(struct mtk_wed_d + dev->rx_buf_ring.desc_phys = desc_phys; + dev->wlan.init_rx_buf(dev, dev->wlan.rx_npkt); + +- return 0; ++ return mtk_wed_hwrro_buffer_alloc(dev); ++} ++ ++static void ++mtk_wed_hwrro_free_buffer(struct mtk_wed_device *dev) ++{ ++ struct mtk_wed_buf *page_list = dev->hw_rro.pages; ++ struct mtk_wed_bm_desc *desc = dev->hw_rro.desc; ++ int i, page_idx = 0; ++ ++ if (!dev->wlan.hw_rro) ++ return; ++ ++ if (!page_list) ++ return; ++ ++ if (!desc) ++ goto free_pagelist; ++ ++ for (i = 0; i < MTK_WED_RX_PG_BM_CNT; i += MTK_WED_RX_BUF_PER_PAGE) { ++ dma_addr_t buf_addr = page_list[page_idx].phy_addr; ++ void *page = page_list[page_idx++].p; ++ ++ if (!page) ++ break; ++ ++ dma_unmap_page(dev->hw->dev, buf_addr, PAGE_SIZE, ++ DMA_BIDIRECTIONAL); ++ __free_page(page); ++ } ++ ++ dma_free_coherent(dev->hw->dev, dev->hw_rro.size * sizeof(*desc), ++ desc, dev->hw_rro.desc_phys); ++ ++free_pagelist: ++ kfree(page_list); + } + + static void +@@ -626,6 +723,28 @@ mtk_wed_free_rx_buffer(struct mtk_wed_de + 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); ++ ++ mtk_wed_hwrro_free_buffer(dev); ++} ++ ++static void ++mtk_wed_hwrro_init(struct mtk_wed_device *dev) ++{ ++ if (!mtk_wed_get_rx_capa(dev) || !dev->wlan.hw_rro) ++ return; ++ ++ wed_set(dev, MTK_WED_RRO_PG_BM_RX_DMAM, ++ FIELD_PREP(MTK_WED_RRO_PG_BM_RX_SDL0, 128)); ++ ++ wed_w32(dev, MTK_WED_RRO_PG_BM_BASE, dev->hw_rro.desc_phys); ++ ++ wed_w32(dev, MTK_WED_RRO_PG_BM_INIT_PTR, ++ MTK_WED_RRO_PG_BM_INIT_SW_TAIL_IDX | ++ FIELD_PREP(MTK_WED_RRO_PG_BM_SW_TAIL_IDX, ++ MTK_WED_RX_PG_BM_CNT)); ++ ++ /* enable rx_page_bm to fetch dmad */ ++ wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_PG_BM_EN); + } + + static void +@@ -639,6 +758,8 @@ mtk_wed_rx_buffer_hw_init(struct mtk_wed + 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); ++ ++ mtk_wed_hwrro_init(dev); + } + + static void +@@ -934,6 +1055,8 @@ mtk_wed_bus_init(struct mtk_wed_device * + static void + mtk_wed_set_wpdma(struct mtk_wed_device *dev) + { ++ int i; ++ + if (mtk_wed_is_v1(dev->hw)) { + wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_phys); + return; +@@ -951,6 +1074,15 @@ mtk_wed_set_wpdma(struct mtk_wed_device + + wed_w32(dev, MTK_WED_WPDMA_RX_GLO_CFG, dev->wlan.wpdma_rx_glo); + wed_w32(dev, dev->hw->soc->regmap.wpdma_rx_ring0, dev->wlan.wpdma_rx); ++ ++ if (!dev->wlan.hw_rro) ++ return; ++ ++ wed_w32(dev, MTK_WED_RRO_RX_D_CFG(0), dev->wlan.wpdma_rx_rro[0]); ++ wed_w32(dev, MTK_WED_RRO_RX_D_CFG(1), dev->wlan.wpdma_rx_rro[1]); ++ for (i = 0; i < MTK_WED_RX_PAGE_QUEUES; i++) ++ wed_w32(dev, MTK_WED_RRO_MSDU_PG_RING_CFG(i), ++ dev->wlan.wpdma_rx_pg + i * 0x10); + } + + static void +@@ -1762,6 +1894,165 @@ mtk_wed_dma_enable(struct mtk_wed_device + } + + static void ++mtk_wed_start_hw_rro(struct mtk_wed_device *dev, u32 irq_mask, bool reset) ++{ ++ int i; ++ ++ wed_w32(dev, MTK_WED_WPDMA_INT_MASK, irq_mask); ++ wed_w32(dev, MTK_WED_INT_MASK, irq_mask); ++ ++ if (!mtk_wed_get_rx_capa(dev) || !dev->wlan.hw_rro) ++ return; ++ ++ wed_set(dev, MTK_WED_RRO_RX_D_CFG(2), MTK_WED_RRO_MSDU_PG_DRV_CLR); ++ wed_w32(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG, ++ MTK_WED_RRO_MSDU_PG_DRV_CLR); ++ ++ wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_RRO_RX, ++ MTK_WED_WPDMA_INT_CTRL_RRO_RX0_EN | ++ MTK_WED_WPDMA_INT_CTRL_RRO_RX0_CLR | ++ MTK_WED_WPDMA_INT_CTRL_RRO_RX1_EN | ++ MTK_WED_WPDMA_INT_CTRL_RRO_RX1_CLR | ++ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_RX0_DONE_TRIG, ++ dev->wlan.rro_rx_tbit[0]) | ++ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_RX1_DONE_TRIG, ++ dev->wlan.rro_rx_tbit[1])); ++ ++ wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_RRO_MSDU_PG, ++ MTK_WED_WPDMA_INT_CTRL_RRO_PG0_EN | ++ MTK_WED_WPDMA_INT_CTRL_RRO_PG0_CLR | ++ MTK_WED_WPDMA_INT_CTRL_RRO_PG1_EN | ++ MTK_WED_WPDMA_INT_CTRL_RRO_PG1_CLR | ++ MTK_WED_WPDMA_INT_CTRL_RRO_PG2_EN | ++ MTK_WED_WPDMA_INT_CTRL_RRO_PG2_CLR | ++ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_PG0_DONE_TRIG, ++ dev->wlan.rx_pg_tbit[0]) | ++ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_PG1_DONE_TRIG, ++ dev->wlan.rx_pg_tbit[1]) | ++ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_PG2_DONE_TRIG, ++ dev->wlan.rx_pg_tbit[2])); ++ ++ /* RRO_MSDU_PG_RING2_CFG1_FLD_DRV_EN should be enabled after ++ * WM FWDL completed, otherwise RRO_MSDU_PG ring may broken ++ */ ++ wed_set(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG, ++ MTK_WED_RRO_MSDU_PG_DRV_EN); ++ ++ for (i = 0; i < MTK_WED_RX_QUEUES; i++) { ++ struct mtk_wed_ring *ring = &dev->rx_rro_ring[i]; ++ ++ if (!(ring->flags & MTK_WED_RING_CONFIGURED)) ++ continue; ++ ++ if (mtk_wed_check_wfdma_rx_fill(dev, ring)) ++ dev_err(dev->hw->dev, ++ "rx_rro_ring(%d) initialization failed\n", i); ++ } ++ ++ for (i = 0; i < MTK_WED_RX_PAGE_QUEUES; i++) { ++ struct mtk_wed_ring *ring = &dev->rx_page_ring[i]; ++ ++ if (!(ring->flags & MTK_WED_RING_CONFIGURED)) ++ continue; ++ ++ if (mtk_wed_check_wfdma_rx_fill(dev, ring)) ++ dev_err(dev->hw->dev, ++ "rx_page_ring(%d) initialization failed\n", i); ++ } ++} ++ ++static void ++mtk_wed_rro_rx_ring_setup(struct mtk_wed_device *dev, int idx, ++ void __iomem *regs) ++{ ++ struct mtk_wed_ring *ring = &dev->rx_rro_ring[idx]; ++ ++ ring->wpdma = regs; ++ wed_w32(dev, MTK_WED_RRO_RX_D_RX(idx) + MTK_WED_RING_OFS_BASE, ++ readl(regs)); ++ wed_w32(dev, MTK_WED_RRO_RX_D_RX(idx) + MTK_WED_RING_OFS_COUNT, ++ readl(regs + MTK_WED_RING_OFS_COUNT)); ++ ring->flags |= MTK_WED_RING_CONFIGURED; ++} ++ ++static void ++mtk_wed_msdu_pg_rx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs) ++{ ++ struct mtk_wed_ring *ring = &dev->rx_page_ring[idx]; ++ ++ ring->wpdma = regs; ++ wed_w32(dev, MTK_WED_RRO_MSDU_PG_CTRL0(idx) + MTK_WED_RING_OFS_BASE, ++ readl(regs)); ++ wed_w32(dev, MTK_WED_RRO_MSDU_PG_CTRL0(idx) + MTK_WED_RING_OFS_COUNT, ++ readl(regs + MTK_WED_RING_OFS_COUNT)); ++ ring->flags |= MTK_WED_RING_CONFIGURED; ++} ++ ++static int ++mtk_wed_ind_rx_ring_setup(struct mtk_wed_device *dev, void __iomem *regs) ++{ ++ struct mtk_wed_ring *ring = &dev->ind_cmd_ring; ++ u32 val = readl(regs + MTK_WED_RING_OFS_COUNT); ++ int i, count = 0; ++ ++ ring->wpdma = regs; ++ wed_w32(dev, MTK_WED_IND_CMD_RX_CTRL1 + MTK_WED_RING_OFS_BASE, ++ readl(regs) & 0xfffffff0); ++ ++ wed_w32(dev, MTK_WED_IND_CMD_RX_CTRL1 + MTK_WED_RING_OFS_COUNT, ++ readl(regs + MTK_WED_RING_OFS_COUNT)); ++ ++ /* ack sn cr */ ++ wed_w32(dev, MTK_WED_RRO_CFG0, dev->wlan.phy_base + ++ dev->wlan.ind_cmd.ack_sn_addr); ++ wed_w32(dev, MTK_WED_RRO_CFG1, ++ FIELD_PREP(MTK_WED_RRO_CFG1_MAX_WIN_SZ, ++ dev->wlan.ind_cmd.win_size) | ++ FIELD_PREP(MTK_WED_RRO_CFG1_PARTICL_SE_ID, ++ dev->wlan.ind_cmd.particular_sid)); ++ ++ /* particular session addr element */ ++ wed_w32(dev, MTK_WED_ADDR_ELEM_CFG0, ++ dev->wlan.ind_cmd.particular_se_phys); ++ ++ for (i = 0; i < dev->wlan.ind_cmd.se_group_nums; i++) { ++ wed_w32(dev, MTK_WED_RADDR_ELEM_TBL_WDATA, ++ dev->wlan.ind_cmd.addr_elem_phys[i] >> 4); ++ wed_w32(dev, MTK_WED_ADDR_ELEM_TBL_CFG, ++ MTK_WED_ADDR_ELEM_TBL_WR | (i & 0x7f)); ++ ++ val = wed_r32(dev, MTK_WED_ADDR_ELEM_TBL_CFG); ++ while (!(val & MTK_WED_ADDR_ELEM_TBL_WR_RDY) && count++ < 100) ++ val = wed_r32(dev, MTK_WED_ADDR_ELEM_TBL_CFG); ++ if (count >= 100) ++ dev_err(dev->hw->dev, ++ "write ba session base failed\n"); ++ } ++ ++ /* pn check init */ ++ for (i = 0; i < dev->wlan.ind_cmd.particular_sid; i++) { ++ wed_w32(dev, MTK_WED_PN_CHECK_WDATA_M, ++ MTK_WED_PN_CHECK_IS_FIRST); ++ ++ wed_w32(dev, MTK_WED_PN_CHECK_CFG, MTK_WED_PN_CHECK_WR | ++ FIELD_PREP(MTK_WED_PN_CHECK_SE_ID, i)); ++ ++ count = 0; ++ val = wed_r32(dev, MTK_WED_PN_CHECK_CFG); ++ while (!(val & MTK_WED_PN_CHECK_WR_RDY) && count++ < 100) ++ val = wed_r32(dev, MTK_WED_PN_CHECK_CFG); ++ if (count >= 100) ++ dev_err(dev->hw->dev, ++ "session(%d) initialization failed\n", i); ++ } ++ ++ wed_w32(dev, MTK_WED_RX_IND_CMD_CNT0, MTK_WED_RX_IND_CMD_DBG_CNT_EN); ++ wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_IND_CMD_EN); ++ ++ return 0; ++} ++ ++static void + mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask) + { + int i; +@@ -2215,6 +2506,10 @@ void mtk_wed_add_hw(struct device_node * + .detach = mtk_wed_detach, + .ppe_check = mtk_wed_ppe_check, + .setup_tc = mtk_wed_setup_tc, ++ .start_hw_rro = mtk_wed_start_hw_rro, ++ .rro_rx_ring_setup = mtk_wed_rro_rx_ring_setup, ++ .msdu_pg_rx_ring_setup = mtk_wed_msdu_pg_rx_ring_setup, ++ .ind_rx_ring_setup = mtk_wed_ind_rx_ring_setup, + }; + 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 +@@ -10,6 +10,7 @@ + + #define MTK_WED_TX_QUEUES 2 + #define MTK_WED_RX_QUEUES 2 ++#define MTK_WED_RX_PAGE_QUEUES 3 + + #define WED_WO_STA_REC 0x6 + +@@ -99,6 +100,9 @@ struct mtk_wed_device { + 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 mtk_wed_ring rx_rro_ring[MTK_WED_RX_QUEUES]; ++ struct mtk_wed_ring rx_page_ring[MTK_WED_RX_PAGE_QUEUES]; ++ struct mtk_wed_ring ind_cmd_ring; + + struct { + int size; +@@ -120,6 +124,13 @@ struct mtk_wed_device { + dma_addr_t fdbk_phys; + } rro; + ++ struct { ++ int size; ++ struct mtk_wed_buf *pages; ++ struct mtk_wed_bm_desc *desc; ++ dma_addr_t desc_phys; ++ } hw_rro; ++ + /* filled by driver: */ + struct { + union { +@@ -138,6 +149,8 @@ struct mtk_wed_device { + u32 wpdma_txfree; + u32 wpdma_rx_glo; + u32 wpdma_rx; ++ u32 wpdma_rx_rro[MTK_WED_RX_QUEUES]; ++ u32 wpdma_rx_pg; + + bool wcid_512; + bool hw_rro; +@@ -152,9 +165,20 @@ struct mtk_wed_device { + + u8 tx_tbit[MTK_WED_TX_QUEUES]; + u8 rx_tbit[MTK_WED_RX_QUEUES]; ++ u8 rro_rx_tbit[MTK_WED_RX_QUEUES]; ++ u8 rx_pg_tbit[MTK_WED_RX_PAGE_QUEUES]; + u8 txfree_tbit; + u8 amsdu_max_subframes; + ++ struct { ++ u8 se_group_nums; ++ u16 win_size; ++ u16 particular_sid; ++ u32 ack_sn_addr; ++ dma_addr_t particular_se_phys; ++ dma_addr_t addr_elem_phys[1024]; ++ } ind_cmd; ++ + 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); +@@ -193,6 +217,14 @@ struct mtk_wed_ops { + 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); ++ void (*start_hw_rro)(struct mtk_wed_device *dev, u32 irq_mask, ++ bool reset); ++ void (*rro_rx_ring_setup)(struct mtk_wed_device *dev, int ring, ++ void __iomem *regs); ++ void (*msdu_pg_rx_ring_setup)(struct mtk_wed_device *dev, int ring, ++ void __iomem *regs); ++ int (*ind_rx_ring_setup)(struct mtk_wed_device *dev, ++ void __iomem *regs); + }; + + extern const struct mtk_wed_ops __rcu *mtk_soc_wed_ops; +@@ -264,6 +296,15 @@ static inline bool mtk_wed_is_amsdu_supp + #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) ++#define mtk_wed_device_start_hw_rro(_dev, _mask, _reset) \ ++ (_dev)->ops->start_hw_rro(_dev, _mask, _reset) ++#define mtk_wed_device_rro_rx_ring_setup(_dev, _ring, _regs) \ ++ (_dev)->ops->rro_rx_ring_setup(_dev, _ring, _regs) ++#define mtk_wed_device_msdu_pg_rx_ring_setup(_dev, _ring, _regs) \ ++ (_dev)->ops->msdu_pg_rx_ring_setup(_dev, _ring, _regs) ++#define mtk_wed_device_ind_rx_ring_setup(_dev, _regs) \ ++ (_dev)->ops->ind_rx_ring_setup(_dev, _regs) ++ + #else + static inline bool mtk_wed_device_active(struct mtk_wed_device *dev) + { +@@ -283,6 +324,10 @@ static inline bool mtk_wed_device_active + #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 ++#define mtk_wed_device_start_hw_rro(_dev, _mask, _reset) do {} while (0) ++#define mtk_wed_device_rro_rx_ring_setup(_dev, _ring, _regs) -ENODEV ++#define mtk_wed_device_msdu_pg_rx_ring_setup(_dev, _ring, _regs) -ENODEV ++#define mtk_wed_device_ind_rx_ring_setup(_dev, _regs) -ENODEV + #endif + + #endif diff --git a/target/linux/generic/backport-6.6/752-18-v6.7-net-ethernet-mtk_wed-debugfs-move-wed_v2-specific-re.patch b/target/linux/generic/backport-6.6/752-18-v6.7-net-ethernet-mtk_wed-debugfs-move-wed_v2-specific-re.patch new file mode 100644 index 0000000000..5ea43a4445 --- /dev/null +++ b/target/linux/generic/backport-6.6/752-18-v6.7-net-ethernet-mtk_wed-debugfs-move-wed_v2-specific-re.patch @@ -0,0 +1,78 @@ +From: Lorenzo Bianconi +Date: Mon, 18 Sep 2023 12:29:17 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: debugfs: move wed_v2 specific regs + out of regs array + +Move specific WED2.0 debugfs entries out of regs array. This is a +preliminary patch to introduce WED 3.0 debugfs info. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c +@@ -151,7 +151,7 @@ DEFINE_SHOW_ATTRIBUTE(wed_txinfo); + static int + wed_rxinfo_show(struct seq_file *s, void *data) + { +- static const struct reg_dump regs[] = { ++ static const struct reg_dump regs_common[] = { + DUMP_STR("WPDMA RX"), + DUMP_WPDMA_RX_RING(0), + DUMP_WPDMA_RX_RING(1), +@@ -169,7 +169,7 @@ wed_rxinfo_show(struct seq_file *s, void + DUMP_WED_RING(WED_RING_RX_DATA(0)), + DUMP_WED_RING(WED_RING_RX_DATA(1)), + +- DUMP_STR("WED RRO"), ++ DUMP_STR("WED WO RRO"), + DUMP_WED_RRO_RING(WED_RROQM_MIOD_CTRL0), + DUMP_WED(WED_RROQM_MID_MIB), + DUMP_WED(WED_RROQM_MOD_MIB), +@@ -180,17 +180,6 @@ wed_rxinfo_show(struct seq_file *s, void + 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), +@@ -211,11 +200,25 @@ wed_rxinfo_show(struct seq_file *s, void + DUMP_WED(WED_RX_BM_INTF), + DUMP_WED(WED_RX_BM_ERR_STS), + }; ++ static const struct reg_dump regs_wed_v2[] = { ++ 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), ++ }; + struct mtk_wed_hw *hw = s->private; + struct mtk_wed_device *dev = hw->wed_dev; + +- if (dev) +- dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs)); ++ if (dev) { ++ dump_wed_regs(s, dev, regs_common, ARRAY_SIZE(regs_common)); ++ dump_wed_regs(s, dev, regs_wed_v2, ARRAY_SIZE(regs_wed_v2)); ++ } + + return 0; + } diff --git a/target/linux/generic/backport-6.6/752-19-v6.7-net-ethernet-mtk_wed-debugfs-add-WED-3.0-debugfs-ent.patch b/target/linux/generic/backport-6.6/752-19-v6.7-net-ethernet-mtk_wed-debugfs-add-WED-3.0-debugfs-ent.patch new file mode 100644 index 0000000000..f491d2fd80 --- /dev/null +++ b/target/linux/generic/backport-6.6/752-19-v6.7-net-ethernet-mtk_wed-debugfs-add-WED-3.0-debugfs-ent.patch @@ -0,0 +1,432 @@ +From: Sujuan Chen +Date: Mon, 18 Sep 2023 12:29:18 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: debugfs: add WED 3.0 debugfs entries + +Introduce WED3.0 debugfs entries useful for debugging. + +Co-developed-by: Lorenzo Bianconi +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Sujuan Chen +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c +@@ -11,6 +11,7 @@ struct reg_dump { + u16 offset; + u8 type; + u8 base; ++ u32 mask; + }; + + enum { +@@ -25,6 +26,8 @@ enum { + + #define DUMP_STR(_str) { _str, 0, DUMP_TYPE_STRING } + #define DUMP_REG(_reg, ...) { #_reg, MTK_##_reg, __VA_ARGS__ } ++#define DUMP_REG_MASK(_reg, _mask) \ ++ { #_mask, MTK_##_reg, DUMP_TYPE_WED, 0, MTK_##_mask } + #define DUMP_RING(_prefix, _base, ...) \ + { _prefix " BASE", _base, __VA_ARGS__ }, \ + { _prefix " CNT", _base + 0x4, __VA_ARGS__ }, \ +@@ -32,6 +35,7 @@ enum { + { _prefix " DIDX", _base + 0xc, __VA_ARGS__ } + + #define DUMP_WED(_reg) DUMP_REG(_reg, DUMP_TYPE_WED) ++#define DUMP_WED_MASK(_reg, _mask) DUMP_REG_MASK(_reg, _mask) + #define DUMP_WED_RING(_base) DUMP_RING(#_base, MTK_##_base, DUMP_TYPE_WED) + + #define DUMP_WDMA(_reg) DUMP_REG(_reg, DUMP_TYPE_WDMA) +@@ -212,12 +216,58 @@ wed_rxinfo_show(struct seq_file *s, void + DUMP_WED(WED_RTQM_Q2B_MIB), + DUMP_WED(WED_RTQM_PFDBK_MIB), + }; ++ static const struct reg_dump regs_wed_v3[] = { ++ DUMP_STR("WED RX RRO DATA"), ++ DUMP_WED_RING(WED_RRO_RX_D_RX(0)), ++ DUMP_WED_RING(WED_RRO_RX_D_RX(1)), ++ ++ DUMP_STR("WED RX MSDU PAGE"), ++ DUMP_WED_RING(WED_RRO_MSDU_PG_CTRL0(0)), ++ DUMP_WED_RING(WED_RRO_MSDU_PG_CTRL0(1)), ++ DUMP_WED_RING(WED_RRO_MSDU_PG_CTRL0(2)), ++ ++ DUMP_STR("WED RX IND CMD"), ++ DUMP_WED(WED_IND_CMD_RX_CTRL1), ++ DUMP_WED_MASK(WED_IND_CMD_RX_CTRL2, WED_IND_CMD_MAX_CNT), ++ DUMP_WED_MASK(WED_IND_CMD_RX_CTRL0, WED_IND_CMD_PROC_IDX), ++ DUMP_WED_MASK(RRO_IND_CMD_SIGNATURE, RRO_IND_CMD_DMA_IDX), ++ DUMP_WED_MASK(WED_IND_CMD_RX_CTRL0, WED_IND_CMD_MAGIC_CNT), ++ DUMP_WED_MASK(RRO_IND_CMD_SIGNATURE, RRO_IND_CMD_MAGIC_CNT), ++ DUMP_WED_MASK(WED_IND_CMD_RX_CTRL0, ++ WED_IND_CMD_PREFETCH_FREE_CNT), ++ DUMP_WED_MASK(WED_RRO_CFG1, WED_RRO_CFG1_PARTICL_SE_ID), ++ ++ DUMP_STR("WED ADDR ELEM"), ++ DUMP_WED(WED_ADDR_ELEM_CFG0), ++ DUMP_WED_MASK(WED_ADDR_ELEM_CFG1, ++ WED_ADDR_ELEM_PREFETCH_FREE_CNT), ++ ++ DUMP_STR("WED Route QM"), ++ DUMP_WED(WED_RTQM_ENQ_I2Q_DMAD_CNT), ++ DUMP_WED(WED_RTQM_ENQ_I2N_DMAD_CNT), ++ DUMP_WED(WED_RTQM_ENQ_I2Q_PKT_CNT), ++ DUMP_WED(WED_RTQM_ENQ_I2N_PKT_CNT), ++ DUMP_WED(WED_RTQM_ENQ_USED_ENTRY_CNT), ++ DUMP_WED(WED_RTQM_ENQ_ERR_CNT), ++ ++ DUMP_WED(WED_RTQM_DEQ_DMAD_CNT), ++ DUMP_WED(WED_RTQM_DEQ_Q2I_DMAD_CNT), ++ DUMP_WED(WED_RTQM_DEQ_PKT_CNT), ++ DUMP_WED(WED_RTQM_DEQ_Q2I_PKT_CNT), ++ DUMP_WED(WED_RTQM_DEQ_USED_PFDBK_CNT), ++ DUMP_WED(WED_RTQM_DEQ_ERR_CNT), ++ }; + struct mtk_wed_hw *hw = s->private; + struct mtk_wed_device *dev = hw->wed_dev; + + if (dev) { + dump_wed_regs(s, dev, regs_common, ARRAY_SIZE(regs_common)); +- dump_wed_regs(s, dev, regs_wed_v2, ARRAY_SIZE(regs_wed_v2)); ++ if (mtk_wed_is_v2(hw)) ++ dump_wed_regs(s, dev, ++ regs_wed_v2, ARRAY_SIZE(regs_wed_v2)); ++ else ++ dump_wed_regs(s, dev, ++ regs_wed_v3, ARRAY_SIZE(regs_wed_v3)); + } + + return 0; +@@ -225,6 +275,314 @@ wed_rxinfo_show(struct seq_file *s, void + DEFINE_SHOW_ATTRIBUTE(wed_rxinfo); + + static int ++wed_amsdu_show(struct seq_file *s, void *data) ++{ ++ static const struct reg_dump regs[] = { ++ DUMP_STR("WED AMDSU INFO"), ++ DUMP_WED(WED_MON_AMSDU_FIFO_DMAD), ++ ++ DUMP_STR("WED AMDSU ENG0 INFO"), ++ DUMP_WED(WED_MON_AMSDU_ENG_DMAD(0)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QFPL(0)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENI(0)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENO(0)), ++ DUMP_WED(WED_MON_AMSDU_ENG_MERG(0)), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(0), ++ WED_AMSDU_ENG_MAX_PL_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(0), ++ WED_AMSDU_ENG_MAX_QGPP_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(0), ++ WED_AMSDU_ENG_CUR_ENTRY), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(0), ++ WED_AMSDU_ENG_MAX_BUF_MERGED), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(0), ++ WED_AMSDU_ENG_MAX_MSDU_MERGED), ++ ++ DUMP_STR("WED AMDSU ENG1 INFO"), ++ DUMP_WED(WED_MON_AMSDU_ENG_DMAD(1)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QFPL(1)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENI(1)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENO(1)), ++ DUMP_WED(WED_MON_AMSDU_ENG_MERG(1)), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(1), ++ WED_AMSDU_ENG_MAX_PL_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(1), ++ WED_AMSDU_ENG_MAX_QGPP_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(1), ++ WED_AMSDU_ENG_CUR_ENTRY), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2), ++ WED_AMSDU_ENG_MAX_BUF_MERGED), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2), ++ WED_AMSDU_ENG_MAX_MSDU_MERGED), ++ ++ DUMP_STR("WED AMDSU ENG2 INFO"), ++ DUMP_WED(WED_MON_AMSDU_ENG_DMAD(2)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QFPL(2)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENI(2)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENO(2)), ++ DUMP_WED(WED_MON_AMSDU_ENG_MERG(2)), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(2), ++ WED_AMSDU_ENG_MAX_PL_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(2), ++ WED_AMSDU_ENG_MAX_QGPP_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2), ++ WED_AMSDU_ENG_CUR_ENTRY), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2), ++ WED_AMSDU_ENG_MAX_BUF_MERGED), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2), ++ WED_AMSDU_ENG_MAX_MSDU_MERGED), ++ ++ DUMP_STR("WED AMDSU ENG3 INFO"), ++ DUMP_WED(WED_MON_AMSDU_ENG_DMAD(3)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QFPL(3)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENI(3)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENO(3)), ++ DUMP_WED(WED_MON_AMSDU_ENG_MERG(3)), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(3), ++ WED_AMSDU_ENG_MAX_PL_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(3), ++ WED_AMSDU_ENG_MAX_QGPP_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(3), ++ WED_AMSDU_ENG_CUR_ENTRY), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(3), ++ WED_AMSDU_ENG_MAX_BUF_MERGED), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(3), ++ WED_AMSDU_ENG_MAX_MSDU_MERGED), ++ ++ DUMP_STR("WED AMDSU ENG4 INFO"), ++ DUMP_WED(WED_MON_AMSDU_ENG_DMAD(4)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QFPL(4)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENI(4)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENO(4)), ++ DUMP_WED(WED_MON_AMSDU_ENG_MERG(4)), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(4), ++ WED_AMSDU_ENG_MAX_PL_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(4), ++ WED_AMSDU_ENG_MAX_QGPP_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(4), ++ WED_AMSDU_ENG_CUR_ENTRY), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(4), ++ WED_AMSDU_ENG_MAX_BUF_MERGED), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(4), ++ WED_AMSDU_ENG_MAX_MSDU_MERGED), ++ ++ DUMP_STR("WED AMDSU ENG5 INFO"), ++ DUMP_WED(WED_MON_AMSDU_ENG_DMAD(5)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QFPL(5)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENI(5)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENO(5)), ++ DUMP_WED(WED_MON_AMSDU_ENG_MERG(5)), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(5), ++ WED_AMSDU_ENG_MAX_PL_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(5), ++ WED_AMSDU_ENG_MAX_QGPP_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(5), ++ WED_AMSDU_ENG_CUR_ENTRY), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(5), ++ WED_AMSDU_ENG_MAX_BUF_MERGED), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(5), ++ WED_AMSDU_ENG_MAX_MSDU_MERGED), ++ ++ DUMP_STR("WED AMDSU ENG6 INFO"), ++ DUMP_WED(WED_MON_AMSDU_ENG_DMAD(6)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QFPL(6)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENI(6)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENO(6)), ++ DUMP_WED(WED_MON_AMSDU_ENG_MERG(6)), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(6), ++ WED_AMSDU_ENG_MAX_PL_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(6), ++ WED_AMSDU_ENG_MAX_QGPP_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(6), ++ WED_AMSDU_ENG_CUR_ENTRY), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(6), ++ WED_AMSDU_ENG_MAX_BUF_MERGED), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(6), ++ WED_AMSDU_ENG_MAX_MSDU_MERGED), ++ ++ DUMP_STR("WED AMDSU ENG7 INFO"), ++ DUMP_WED(WED_MON_AMSDU_ENG_DMAD(7)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QFPL(7)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENI(7)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENO(7)), ++ DUMP_WED(WED_MON_AMSDU_ENG_MERG(7)), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(7), ++ WED_AMSDU_ENG_MAX_PL_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(7), ++ WED_AMSDU_ENG_MAX_QGPP_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(7), ++ WED_AMSDU_ENG_CUR_ENTRY), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(7), ++ WED_AMSDU_ENG_MAX_BUF_MERGED), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(4), ++ WED_AMSDU_ENG_MAX_MSDU_MERGED), ++ ++ DUMP_STR("WED AMDSU ENG8 INFO"), ++ DUMP_WED(WED_MON_AMSDU_ENG_DMAD(8)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QFPL(8)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENI(8)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENO(8)), ++ DUMP_WED(WED_MON_AMSDU_ENG_MERG(8)), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(8), ++ WED_AMSDU_ENG_MAX_PL_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(8), ++ WED_AMSDU_ENG_MAX_QGPP_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(8), ++ WED_AMSDU_ENG_CUR_ENTRY), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(8), ++ WED_AMSDU_ENG_MAX_BUF_MERGED), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(8), ++ WED_AMSDU_ENG_MAX_MSDU_MERGED), ++ ++ DUMP_STR("WED QMEM INFO"), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(0), WED_AMSDU_QMEM_FQ_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(0), WED_AMSDU_QMEM_SP_QCNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(1), WED_AMSDU_QMEM_TID0_QCNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(1), WED_AMSDU_QMEM_TID1_QCNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(2), WED_AMSDU_QMEM_TID2_QCNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(2), WED_AMSDU_QMEM_TID3_QCNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(3), WED_AMSDU_QMEM_TID4_QCNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(3), WED_AMSDU_QMEM_TID5_QCNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(4), WED_AMSDU_QMEM_TID6_QCNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(4), WED_AMSDU_QMEM_TID7_QCNT), ++ ++ DUMP_STR("WED QMEM HEAD INFO"), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(0), WED_AMSDU_QMEM_FQ_HEAD), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(0), WED_AMSDU_QMEM_SP_QHEAD), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(1), WED_AMSDU_QMEM_TID0_QHEAD), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(1), WED_AMSDU_QMEM_TID1_QHEAD), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(2), WED_AMSDU_QMEM_TID2_QHEAD), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(2), WED_AMSDU_QMEM_TID3_QHEAD), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(3), WED_AMSDU_QMEM_TID4_QHEAD), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(3), WED_AMSDU_QMEM_TID5_QHEAD), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(4), WED_AMSDU_QMEM_TID6_QHEAD), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(4), WED_AMSDU_QMEM_TID7_QHEAD), ++ ++ DUMP_STR("WED QMEM TAIL INFO"), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(5), WED_AMSDU_QMEM_FQ_TAIL), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(5), WED_AMSDU_QMEM_SP_QTAIL), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(6), WED_AMSDU_QMEM_TID0_QTAIL), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(6), WED_AMSDU_QMEM_TID1_QTAIL), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(7), WED_AMSDU_QMEM_TID2_QTAIL), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(7), WED_AMSDU_QMEM_TID3_QTAIL), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(8), WED_AMSDU_QMEM_TID4_QTAIL), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(8), WED_AMSDU_QMEM_TID5_QTAIL), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(9), WED_AMSDU_QMEM_TID6_QTAIL), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(9), WED_AMSDU_QMEM_TID7_QTAIL), ++ ++ DUMP_STR("WED HIFTXD MSDU INFO"), ++ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(1)), ++ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(2)), ++ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(3)), ++ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(4)), ++ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(5)), ++ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(6)), ++ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(7)), ++ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(8)), ++ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(9)), ++ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(10)), ++ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(11)), ++ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(12)), ++ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(13)), ++ }; ++ struct mtk_wed_hw *hw = s->private; ++ struct mtk_wed_device *dev = hw->wed_dev; ++ ++ if (dev) ++ dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs)); ++ ++ return 0; ++} ++DEFINE_SHOW_ATTRIBUTE(wed_amsdu); ++ ++static int ++wed_rtqm_show(struct seq_file *s, void *data) ++{ ++ static const struct reg_dump regs[] = { ++ DUMP_STR("WED Route QM IGRS0(N2H + Recycle)"), ++ DUMP_WED(WED_RTQM_IGRS0_I2HW_DMAD_CNT), ++ DUMP_WED(WED_RTQM_IGRS0_I2H_DMAD_CNT(0)), ++ DUMP_WED(WED_RTQM_IGRS0_I2H_DMAD_CNT(1)), ++ DUMP_WED(WED_RTQM_IGRS0_I2HW_PKT_CNT), ++ DUMP_WED(WED_RTQM_IGRS0_I2H_PKT_CNT(0)), ++ DUMP_WED(WED_RTQM_IGRS0_I2H_PKT_CNT(0)), ++ DUMP_WED(WED_RTQM_IGRS0_FDROP_CNT), ++ ++ DUMP_STR("WED Route QM IGRS1(Legacy)"), ++ DUMP_WED(WED_RTQM_IGRS1_I2HW_DMAD_CNT), ++ DUMP_WED(WED_RTQM_IGRS1_I2H_DMAD_CNT(0)), ++ DUMP_WED(WED_RTQM_IGRS1_I2H_DMAD_CNT(1)), ++ DUMP_WED(WED_RTQM_IGRS1_I2HW_PKT_CNT), ++ DUMP_WED(WED_RTQM_IGRS1_I2H_PKT_CNT(0)), ++ DUMP_WED(WED_RTQM_IGRS1_I2H_PKT_CNT(1)), ++ DUMP_WED(WED_RTQM_IGRS1_FDROP_CNT), ++ ++ DUMP_STR("WED Route QM IGRS2(RRO3.0)"), ++ DUMP_WED(WED_RTQM_IGRS2_I2HW_DMAD_CNT), ++ DUMP_WED(WED_RTQM_IGRS2_I2H_DMAD_CNT(0)), ++ DUMP_WED(WED_RTQM_IGRS2_I2H_DMAD_CNT(1)), ++ DUMP_WED(WED_RTQM_IGRS2_I2HW_PKT_CNT), ++ DUMP_WED(WED_RTQM_IGRS2_I2H_PKT_CNT(0)), ++ DUMP_WED(WED_RTQM_IGRS2_I2H_PKT_CNT(1)), ++ DUMP_WED(WED_RTQM_IGRS2_FDROP_CNT), ++ ++ DUMP_STR("WED Route QM IGRS3(DEBUG)"), ++ DUMP_WED(WED_RTQM_IGRS2_I2HW_DMAD_CNT), ++ DUMP_WED(WED_RTQM_IGRS3_I2H_DMAD_CNT(0)), ++ DUMP_WED(WED_RTQM_IGRS3_I2H_DMAD_CNT(1)), ++ DUMP_WED(WED_RTQM_IGRS3_I2HW_PKT_CNT), ++ DUMP_WED(WED_RTQM_IGRS3_I2H_PKT_CNT(0)), ++ DUMP_WED(WED_RTQM_IGRS3_I2H_PKT_CNT(1)), ++ DUMP_WED(WED_RTQM_IGRS3_FDROP_CNT), ++ }; ++ struct mtk_wed_hw *hw = s->private; ++ struct mtk_wed_device *dev = hw->wed_dev; ++ ++ if (dev) ++ dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs)); ++ ++ return 0; ++} ++DEFINE_SHOW_ATTRIBUTE(wed_rtqm); ++ ++static int ++wed_rro_show(struct seq_file *s, void *data) ++{ ++ static const struct reg_dump regs[] = { ++ DUMP_STR("RRO/IND CMD CNT"), ++ DUMP_WED(WED_RX_IND_CMD_CNT(1)), ++ DUMP_WED(WED_RX_IND_CMD_CNT(2)), ++ DUMP_WED(WED_RX_IND_CMD_CNT(3)), ++ DUMP_WED(WED_RX_IND_CMD_CNT(4)), ++ DUMP_WED(WED_RX_IND_CMD_CNT(5)), ++ DUMP_WED(WED_RX_IND_CMD_CNT(6)), ++ DUMP_WED(WED_RX_IND_CMD_CNT(7)), ++ DUMP_WED(WED_RX_IND_CMD_CNT(8)), ++ DUMP_WED_MASK(WED_RX_IND_CMD_CNT(9), ++ WED_IND_CMD_MAGIC_CNT_FAIL_CNT), ++ ++ DUMP_WED(WED_RX_ADDR_ELEM_CNT(0)), ++ DUMP_WED_MASK(WED_RX_ADDR_ELEM_CNT(1), ++ WED_ADDR_ELEM_SIG_FAIL_CNT), ++ DUMP_WED(WED_RX_MSDU_PG_CNT(1)), ++ DUMP_WED(WED_RX_MSDU_PG_CNT(2)), ++ DUMP_WED(WED_RX_MSDU_PG_CNT(3)), ++ DUMP_WED(WED_RX_MSDU_PG_CNT(4)), ++ DUMP_WED(WED_RX_MSDU_PG_CNT(5)), ++ DUMP_WED_MASK(WED_RX_PN_CHK_CNT, ++ WED_PN_CHK_FAIL_CNT), ++ }; ++ struct mtk_wed_hw *hw = s->private; ++ struct mtk_wed_device *dev = hw->wed_dev; ++ ++ if (dev) ++ dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs)); ++ ++ return 0; ++} ++DEFINE_SHOW_ATTRIBUTE(wed_rro); ++ ++static int + mtk_wed_reg_set(void *data, u64 val) + { + struct mtk_wed_hw *hw = data; +@@ -266,7 +624,16 @@ 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 (!mtk_wed_is_v1(hw)) ++ if (!mtk_wed_is_v1(hw)) { + debugfs_create_file_unsafe("rxinfo", 0400, dir, hw, + &wed_rxinfo_fops); ++ if (mtk_wed_is_v3_or_greater(hw)) { ++ debugfs_create_file_unsafe("amsdu", 0400, dir, hw, ++ &wed_amsdu_fops); ++ debugfs_create_file_unsafe("rtqm", 0400, dir, hw, ++ &wed_rtqm_fops); ++ debugfs_create_file_unsafe("rro", 0400, dir, hw, ++ &wed_rro_fops); ++ } ++ } + } diff --git a/target/linux/generic/backport-6.6/752-20-v6.7-net-ethernet-mtk_wed-add-wed-3.0-reset-support.patch b/target/linux/generic/backport-6.6/752-20-v6.7-net-ethernet-mtk_wed-add-wed-3.0-reset-support.patch new file mode 100644 index 0000000000..aaaabf05e8 --- /dev/null +++ b/target/linux/generic/backport-6.6/752-20-v6.7-net-ethernet-mtk_wed-add-wed-3.0-reset-support.patch @@ -0,0 +1,587 @@ +From: Sujuan Chen +Date: Mon, 18 Sep 2023 12:29:19 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: add wed 3.0 reset support + +Introduce support for resetting Wireless Ethernet Dispatcher 3.0 +available on MT988 SoC. + +Co-developed-by: Lorenzo Bianconi +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Sujuan Chen +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -148,6 +148,90 @@ mtk_wdma_read_reset(struct mtk_wed_devic + return wdma_r32(dev, MTK_WDMA_GLO_CFG); + } + ++static void ++mtk_wdma_v3_rx_reset(struct mtk_wed_device *dev) ++{ ++ u32 status; ++ ++ if (!mtk_wed_is_v3_or_greater(dev->hw)) ++ return; ++ ++ wdma_clr(dev, MTK_WDMA_PREF_TX_CFG, MTK_WDMA_PREF_TX_CFG_PREF_EN); ++ wdma_clr(dev, MTK_WDMA_PREF_RX_CFG, MTK_WDMA_PREF_RX_CFG_PREF_EN); ++ ++ if (read_poll_timeout(wdma_r32, status, ++ !(status & MTK_WDMA_PREF_TX_CFG_PREF_BUSY), ++ 0, 10000, false, dev, MTK_WDMA_PREF_TX_CFG)) ++ dev_err(dev->hw->dev, "rx reset failed\n"); ++ ++ if (read_poll_timeout(wdma_r32, status, ++ !(status & MTK_WDMA_PREF_RX_CFG_PREF_BUSY), ++ 0, 10000, false, dev, MTK_WDMA_PREF_RX_CFG)) ++ dev_err(dev->hw->dev, "rx reset failed\n"); ++ ++ wdma_clr(dev, MTK_WDMA_WRBK_TX_CFG, MTK_WDMA_WRBK_TX_CFG_WRBK_EN); ++ wdma_clr(dev, MTK_WDMA_WRBK_RX_CFG, MTK_WDMA_WRBK_RX_CFG_WRBK_EN); ++ ++ if (read_poll_timeout(wdma_r32, status, ++ !(status & MTK_WDMA_WRBK_TX_CFG_WRBK_BUSY), ++ 0, 10000, false, dev, MTK_WDMA_WRBK_TX_CFG)) ++ dev_err(dev->hw->dev, "rx reset failed\n"); ++ ++ if (read_poll_timeout(wdma_r32, status, ++ !(status & MTK_WDMA_WRBK_RX_CFG_WRBK_BUSY), ++ 0, 10000, false, dev, MTK_WDMA_WRBK_RX_CFG)) ++ dev_err(dev->hw->dev, "rx reset failed\n"); ++ ++ /* prefetch FIFO */ ++ wdma_w32(dev, MTK_WDMA_PREF_RX_FIFO_CFG, ++ MTK_WDMA_PREF_RX_FIFO_CFG_RING0_CLEAR | ++ MTK_WDMA_PREF_RX_FIFO_CFG_RING1_CLEAR); ++ wdma_clr(dev, MTK_WDMA_PREF_RX_FIFO_CFG, ++ MTK_WDMA_PREF_RX_FIFO_CFG_RING0_CLEAR | ++ MTK_WDMA_PREF_RX_FIFO_CFG_RING1_CLEAR); ++ ++ /* core FIFO */ ++ wdma_w32(dev, MTK_WDMA_XDMA_RX_FIFO_CFG, ++ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_PAR_FIFO_CLEAR | ++ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_CMD_FIFO_CLEAR | ++ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_DMAD_FIFO_CLEAR | ++ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_ARR_FIFO_CLEAR | ++ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_LEN_FIFO_CLEAR | ++ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_WID_FIFO_CLEAR | ++ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_BID_FIFO_CLEAR); ++ wdma_clr(dev, MTK_WDMA_XDMA_RX_FIFO_CFG, ++ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_PAR_FIFO_CLEAR | ++ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_CMD_FIFO_CLEAR | ++ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_DMAD_FIFO_CLEAR | ++ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_ARR_FIFO_CLEAR | ++ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_LEN_FIFO_CLEAR | ++ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_WID_FIFO_CLEAR | ++ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_BID_FIFO_CLEAR); ++ ++ /* writeback FIFO */ ++ wdma_w32(dev, MTK_WDMA_WRBK_RX_FIFO_CFG(0), ++ MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR); ++ wdma_w32(dev, MTK_WDMA_WRBK_RX_FIFO_CFG(1), ++ MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR); ++ ++ wdma_clr(dev, MTK_WDMA_WRBK_RX_FIFO_CFG(0), ++ MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR); ++ wdma_clr(dev, MTK_WDMA_WRBK_RX_FIFO_CFG(1), ++ MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR); ++ ++ /* prefetch ring status */ ++ wdma_w32(dev, MTK_WDMA_PREF_SIDX_CFG, ++ MTK_WDMA_PREF_SIDX_CFG_RX_RING_CLEAR); ++ wdma_clr(dev, MTK_WDMA_PREF_SIDX_CFG, ++ MTK_WDMA_PREF_SIDX_CFG_RX_RING_CLEAR); ++ ++ /* writeback ring status */ ++ wdma_w32(dev, MTK_WDMA_WRBK_SIDX_CFG, ++ MTK_WDMA_WRBK_SIDX_CFG_RX_RING_CLEAR); ++ wdma_clr(dev, MTK_WDMA_WRBK_SIDX_CFG, ++ MTK_WDMA_WRBK_SIDX_CFG_RX_RING_CLEAR); ++} ++ + static int + mtk_wdma_rx_reset(struct mtk_wed_device *dev) + { +@@ -160,6 +244,7 @@ mtk_wdma_rx_reset(struct mtk_wed_device + if (ret) + dev_err(dev->hw->dev, "rx reset failed\n"); + ++ mtk_wdma_v3_rx_reset(dev); + wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_RX); + wdma_w32(dev, MTK_WDMA_RESET_IDX, 0); + +@@ -192,6 +277,84 @@ mtk_wed_poll_busy(struct mtk_wed_device + } + + static void ++mtk_wdma_v3_tx_reset(struct mtk_wed_device *dev) ++{ ++ u32 status; ++ ++ if (!mtk_wed_is_v3_or_greater(dev->hw)) ++ return; ++ ++ wdma_clr(dev, MTK_WDMA_PREF_TX_CFG, MTK_WDMA_PREF_TX_CFG_PREF_EN); ++ wdma_clr(dev, MTK_WDMA_PREF_RX_CFG, MTK_WDMA_PREF_RX_CFG_PREF_EN); ++ ++ if (read_poll_timeout(wdma_r32, status, ++ !(status & MTK_WDMA_PREF_TX_CFG_PREF_BUSY), ++ 0, 10000, false, dev, MTK_WDMA_PREF_TX_CFG)) ++ dev_err(dev->hw->dev, "tx reset failed\n"); ++ ++ if (read_poll_timeout(wdma_r32, status, ++ !(status & MTK_WDMA_PREF_RX_CFG_PREF_BUSY), ++ 0, 10000, false, dev, MTK_WDMA_PREF_RX_CFG)) ++ dev_err(dev->hw->dev, "tx reset failed\n"); ++ ++ wdma_clr(dev, MTK_WDMA_WRBK_TX_CFG, MTK_WDMA_WRBK_TX_CFG_WRBK_EN); ++ wdma_clr(dev, MTK_WDMA_WRBK_RX_CFG, MTK_WDMA_WRBK_RX_CFG_WRBK_EN); ++ ++ if (read_poll_timeout(wdma_r32, status, ++ !(status & MTK_WDMA_WRBK_TX_CFG_WRBK_BUSY), ++ 0, 10000, false, dev, MTK_WDMA_WRBK_TX_CFG)) ++ dev_err(dev->hw->dev, "tx reset failed\n"); ++ ++ if (read_poll_timeout(wdma_r32, status, ++ !(status & MTK_WDMA_WRBK_RX_CFG_WRBK_BUSY), ++ 0, 10000, false, dev, MTK_WDMA_WRBK_RX_CFG)) ++ dev_err(dev->hw->dev, "tx reset failed\n"); ++ ++ /* prefetch FIFO */ ++ wdma_w32(dev, MTK_WDMA_PREF_TX_FIFO_CFG, ++ MTK_WDMA_PREF_TX_FIFO_CFG_RING0_CLEAR | ++ MTK_WDMA_PREF_TX_FIFO_CFG_RING1_CLEAR); ++ wdma_clr(dev, MTK_WDMA_PREF_TX_FIFO_CFG, ++ MTK_WDMA_PREF_TX_FIFO_CFG_RING0_CLEAR | ++ MTK_WDMA_PREF_TX_FIFO_CFG_RING1_CLEAR); ++ ++ /* core FIFO */ ++ wdma_w32(dev, MTK_WDMA_XDMA_TX_FIFO_CFG, ++ MTK_WDMA_XDMA_TX_FIFO_CFG_TX_PAR_FIFO_CLEAR | ++ MTK_WDMA_XDMA_TX_FIFO_CFG_TX_CMD_FIFO_CLEAR | ++ MTK_WDMA_XDMA_TX_FIFO_CFG_TX_DMAD_FIFO_CLEAR | ++ MTK_WDMA_XDMA_TX_FIFO_CFG_TX_ARR_FIFO_CLEAR); ++ wdma_clr(dev, MTK_WDMA_XDMA_TX_FIFO_CFG, ++ MTK_WDMA_XDMA_TX_FIFO_CFG_TX_PAR_FIFO_CLEAR | ++ MTK_WDMA_XDMA_TX_FIFO_CFG_TX_CMD_FIFO_CLEAR | ++ MTK_WDMA_XDMA_TX_FIFO_CFG_TX_DMAD_FIFO_CLEAR | ++ MTK_WDMA_XDMA_TX_FIFO_CFG_TX_ARR_FIFO_CLEAR); ++ ++ /* writeback FIFO */ ++ wdma_w32(dev, MTK_WDMA_WRBK_TX_FIFO_CFG(0), ++ MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR); ++ wdma_w32(dev, MTK_WDMA_WRBK_TX_FIFO_CFG(1), ++ MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR); ++ ++ wdma_clr(dev, MTK_WDMA_WRBK_TX_FIFO_CFG(0), ++ MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR); ++ wdma_clr(dev, MTK_WDMA_WRBK_TX_FIFO_CFG(1), ++ MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR); ++ ++ /* prefetch ring status */ ++ wdma_w32(dev, MTK_WDMA_PREF_SIDX_CFG, ++ MTK_WDMA_PREF_SIDX_CFG_TX_RING_CLEAR); ++ wdma_clr(dev, MTK_WDMA_PREF_SIDX_CFG, ++ MTK_WDMA_PREF_SIDX_CFG_TX_RING_CLEAR); ++ ++ /* writeback ring status */ ++ wdma_w32(dev, MTK_WDMA_WRBK_SIDX_CFG, ++ MTK_WDMA_WRBK_SIDX_CFG_TX_RING_CLEAR); ++ wdma_clr(dev, MTK_WDMA_WRBK_SIDX_CFG, ++ MTK_WDMA_WRBK_SIDX_CFG_TX_RING_CLEAR); ++} ++ ++static void + mtk_wdma_tx_reset(struct mtk_wed_device *dev) + { + u32 status, mask = MTK_WDMA_GLO_CFG_TX_DMA_BUSY; +@@ -202,6 +365,7 @@ mtk_wdma_tx_reset(struct mtk_wed_device + !(status & mask), 0, 10000)) + dev_err(dev->hw->dev, "tx reset failed\n"); + ++ mtk_wdma_v3_tx_reset(dev); + wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_TX); + wdma_w32(dev, MTK_WDMA_RESET_IDX, 0); + +@@ -1405,13 +1569,33 @@ mtk_wed_rx_reset(struct mtk_wed_device * + if (ret) + return ret; + ++ if (dev->wlan.hw_rro) { ++ wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_IND_CMD_EN); ++ mtk_wed_poll_busy(dev, MTK_WED_RRO_RX_HW_STS, ++ MTK_WED_RX_IND_CMD_BUSY); ++ mtk_wed_reset(dev, MTK_WED_RESET_RRO_RX_TO_PG); ++ } ++ + 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_is_v3_or_greater(dev->hw)) ++ ret = mtk_wed_poll_busy(dev, MTK_WED_WPDMA_RX_D_PREF_CFG, ++ MTK_WED_WPDMA_RX_D_PREF_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 { ++ if (mtk_wed_is_v3_or_greater(dev->hw)) { ++ /* 1.a. disable prefetch HW */ ++ wed_clr(dev, MTK_WED_WPDMA_RX_D_PREF_CFG, ++ MTK_WED_WPDMA_RX_D_PREF_EN); ++ mtk_wed_poll_busy(dev, MTK_WED_WPDMA_RX_D_PREF_CFG, ++ MTK_WED_WPDMA_RX_D_PREF_BUSY); ++ wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, ++ MTK_WED_WPDMA_RX_D_RST_DRV_IDX_ALL); ++ } ++ + 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); +@@ -1439,23 +1623,52 @@ mtk_wed_rx_reset(struct mtk_wed_device * + wed_w32(dev, MTK_WED_RROQM_RST_IDX, 0); + } + ++ if (dev->wlan.hw_rro) { ++ /* disable rro msdu page drv */ ++ wed_clr(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG, ++ MTK_WED_RRO_MSDU_PG_DRV_EN); ++ ++ /* disable rro data drv */ ++ wed_clr(dev, MTK_WED_RRO_RX_D_CFG(2), MTK_WED_RRO_RX_D_DRV_EN); ++ ++ /* rro msdu page drv reset */ ++ wed_w32(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG, ++ MTK_WED_RRO_MSDU_PG_DRV_CLR); ++ mtk_wed_poll_busy(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG, ++ MTK_WED_RRO_MSDU_PG_DRV_CLR); ++ ++ /* rro data drv reset */ ++ wed_w32(dev, MTK_WED_RRO_RX_D_CFG(2), ++ MTK_WED_RRO_RX_D_DRV_CLR); ++ mtk_wed_poll_busy(dev, MTK_WED_RRO_RX_D_CFG(2), ++ MTK_WED_RRO_RX_D_DRV_CLR); ++ } ++ + /* 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) ++ 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); ++ } else if (mtk_wed_is_v3_or_greater(dev->hw)) { ++ wed_set(dev, MTK_WED_RTQM_RST, BIT(0)); ++ wed_clr(dev, MTK_WED_RTQM_RST, BIT(0)); ++ 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); ++ if (mtk_wed_is_v3_or_greater(dev->hw)) ++ mtk_wed_poll_busy(dev, MTK_WED_WPDMA_STATUS, ++ MTK_WED_WPDMA_STATUS_TX_DRV); ++ else ++ 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 */ +@@ -1476,6 +1689,14 @@ mtk_wed_rx_reset(struct mtk_wed_device * + MTK_WED_CTRL_WED_RX_BM_BUSY); + mtk_wed_reset(dev, MTK_WED_RESET_RX_BM); + ++ if (dev->wlan.hw_rro) { ++ wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_PG_BM_EN); ++ mtk_wed_poll_busy(dev, MTK_WED_CTRL, ++ MTK_WED_CTRL_WED_RX_PG_BM_BUSY); ++ wed_set(dev, MTK_WED_RESET, MTK_WED_RESET_RX_PG_BM); ++ wed_clr(dev, MTK_WED_RESET, MTK_WED_RESET_RX_PG_BM); ++ } ++ + /* wo change to enable state */ + val = MTK_WED_WO_STATE_ENABLE; + ret = mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO, +@@ -1493,6 +1714,7 @@ mtk_wed_rx_reset(struct mtk_wed_device * + false); + } + mtk_wed_free_rx_buffer(dev); ++ mtk_wed_hwrro_free_buffer(dev); + + return 0; + } +@@ -1526,15 +1748,41 @@ mtk_wed_reset_dma(struct mtk_wed_device + + /* 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 (mtk_wed_is_v3_or_greater(dev->hw)) { ++ val = MTK_WED_WDMA_GLO_CFG_RX_DIS_FSM_AUTO_IDLE | ++ wed_r32(dev, MTK_WED_WDMA_GLO_CFG); ++ val &= ~MTK_WED_WDMA_GLO_CFG_RX_DRV_EN; ++ wed_w32(dev, MTK_WED_WDMA_GLO_CFG, val); ++ } else { ++ 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_is_v3_or_greater(dev->hw)) ++ busy = mtk_wed_poll_busy(dev, MTK_WED_WDMA_RX_PREF_CFG, ++ MTK_WED_WDMA_RX_PREF_BUSY); + + if (busy) { + mtk_wed_reset(dev, MTK_WED_RESET_WDMA_INT_AGENT); + mtk_wed_reset(dev, MTK_WED_RESET_WDMA_RX_DRV); + } else { ++ if (mtk_wed_is_v3_or_greater(dev->hw)) { ++ /* 1.a. disable prefetch HW */ ++ wed_clr(dev, MTK_WED_WDMA_RX_PREF_CFG, ++ MTK_WED_WDMA_RX_PREF_EN); ++ mtk_wed_poll_busy(dev, MTK_WED_WDMA_RX_PREF_CFG, ++ MTK_WED_WDMA_RX_PREF_BUSY); ++ wed_clr(dev, MTK_WED_WDMA_RX_PREF_CFG, ++ MTK_WED_WDMA_RX_PREF_DDONE2_EN); ++ ++ /* 2. Reset dma index */ ++ wed_w32(dev, MTK_WED_WDMA_RESET_IDX, ++ MTK_WED_WDMA_RESET_IDX_RX_ALL); ++ } ++ + wed_w32(dev, MTK_WED_WDMA_RESET_IDX, + MTK_WED_WDMA_RESET_IDX_RX | MTK_WED_WDMA_RESET_IDX_DRV); + wed_w32(dev, MTK_WED_WDMA_RESET_IDX, 0); +@@ -1550,8 +1798,13 @@ mtk_wed_reset_dma(struct mtk_wed_device + 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) ++ if (mtk_wed_is_v1(dev->hw)) ++ val = FIELD_GET(MTK_WED_TX_BM_INTF_TKFIFO_FDEP, ++ wed_r32(dev, MTK_WED_TX_BM_INTF)); ++ else ++ val = FIELD_GET(MTK_WED_TX_TKID_INTF_TKFIFO_FDEP, ++ wed_r32(dev, MTK_WED_TX_TKID_INTF)); ++ if (val == 0x40) + break; + } + +@@ -1573,6 +1826,8 @@ mtk_wed_reset_dma(struct mtk_wed_device + mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_INT_AGENT); + mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_TX_DRV); + mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_RX_DRV); ++ if (mtk_wed_is_v3_or_greater(dev->hw)) ++ wed_w32(dev, MTK_WED_RX1_CTRL2, 0); + } else { + wed_w32(dev, MTK_WED_WPDMA_RESET_IDX, + MTK_WED_WPDMA_RESET_IDX_TX | +@@ -1589,7 +1844,14 @@ mtk_wed_reset_dma(struct mtk_wed_device + wed_w32(dev, MTK_WED_RESET_IDX, 0); + } + +- mtk_wed_rx_reset(dev); ++ if (mtk_wed_is_v3_or_greater(dev->hw)) { ++ /* reset amsdu engine */ ++ wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_TX_AMSDU_EN); ++ mtk_wed_reset(dev, MTK_WED_RESET_TX_AMSDU); ++ } ++ ++ if (mtk_wed_get_rx_capa(dev)) ++ mtk_wed_rx_reset(dev); + } + + static int +@@ -1841,6 +2103,7 @@ mtk_wed_dma_enable(struct mtk_wed_device + MTK_WED_WPDMA_GLO_CFG_RX_DRV_UNS_VER_FORCE_4); + + wdma_set(dev, MTK_WDMA_PREF_RX_CFG, MTK_WDMA_PREF_RX_CFG_PREF_EN); ++ wdma_set(dev, MTK_WDMA_WRBK_RX_CFG, MTK_WDMA_WRBK_RX_CFG_WRBK_EN); + } + + wed_clr(dev, MTK_WED_WPDMA_GLO_CFG, +@@ -1904,6 +2167,12 @@ mtk_wed_start_hw_rro(struct mtk_wed_devi + if (!mtk_wed_get_rx_capa(dev) || !dev->wlan.hw_rro) + return; + ++ if (reset) { ++ wed_set(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG, ++ MTK_WED_RRO_MSDU_PG_DRV_EN); ++ return; ++ } ++ + wed_set(dev, MTK_WED_RRO_RX_D_CFG(2), MTK_WED_RRO_MSDU_PG_DRV_CLR); + wed_w32(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG, + MTK_WED_RRO_MSDU_PG_DRV_CLR); +--- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h ++++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h +@@ -28,6 +28,8 @@ 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_RX_PG_BM BIT(2) ++#define MTK_WED_RESET_RRO_RX_TO_PG BIT(3) + #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) +@@ -106,6 +108,9 @@ struct mtk_wdma_desc { + #define MTK_WED_STATUS 0x060 + #define MTK_WED_STATUS_TX GENMASK(15, 8) + ++#define MTK_WED_WPDMA_STATUS 0x068 ++#define MTK_WED_WPDMA_STATUS_TX_DRV GENMASK(15, 8) ++ + #define MTK_WED_TX_BM_CTRL 0x080 + #define MTK_WED_TX_BM_CTRL_VLD_GRP_NUM GENMASK(6, 0) + #define MTK_WED_TX_BM_CTRL_RSV_GRP_NUM GENMASK(22, 16) +@@ -140,6 +145,9 @@ struct mtk_wdma_desc { + #define MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM GENMASK(22, 16) + #define MTK_WED_TX_TKID_CTRL_PAUSE BIT(28) + ++#define MTK_WED_TX_TKID_INTF 0x0dc ++#define MTK_WED_TX_TKID_INTF_TKFIFO_FDEP GENMASK(25, 16) ++ + #define MTK_WED_TX_TKID_CTRL_VLD_GRP_NUM_V3 GENMASK(7, 0) + #define MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM_V3 GENMASK(23, 16) + +@@ -190,6 +198,7 @@ struct mtk_wdma_desc { + #define MTK_WED_RING_RX_DATA(_n) (0x420 + (_n) * 0x10) + + #define MTK_WED_SCR0 0x3c0 ++#define MTK_WED_RX1_CTRL2 0x418 + #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) +@@ -303,6 +312,7 @@ struct mtk_wdma_desc { + + #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_ALL BIT(20) + #define MTK_WED_WPDMA_RX_D_RST_DRV_IDX GENMASK(25, 24) + + #define MTK_WED_WPDMA_RX_GLO_CFG 0x76c +@@ -313,6 +323,7 @@ struct mtk_wdma_desc { + + #define MTK_WED_WPDMA_RX_D_PREF_CFG 0x7b4 + #define MTK_WED_WPDMA_RX_D_PREF_EN BIT(0) ++#define MTK_WED_WPDMA_RX_D_PREF_BUSY BIT(1) + #define MTK_WED_WPDMA_RX_D_PREF_BURST_SIZE GENMASK(12, 8) + #define MTK_WED_WPDMA_RX_D_PREF_LOW_THRES GENMASK(21, 16) + +@@ -334,11 +345,13 @@ struct mtk_wdma_desc { + + #define MTK_WED_WDMA_RX_PREF_CFG 0x950 + #define MTK_WED_WDMA_RX_PREF_EN BIT(0) ++#define MTK_WED_WDMA_RX_PREF_BUSY BIT(1) + #define MTK_WED_WDMA_RX_PREF_BURST_SIZE GENMASK(12, 8) + #define MTK_WED_WDMA_RX_PREF_LOW_THRES GENMASK(21, 16) + #define MTK_WED_WDMA_RX_PREF_RX0_SIDX_CLR BIT(24) + #define MTK_WED_WDMA_RX_PREF_RX1_SIDX_CLR BIT(25) + #define MTK_WED_WDMA_RX_PREF_DDONE2_EN BIT(26) ++#define MTK_WED_WDMA_RX_PREF_DDONE2_BUSY BIT(27) + + #define MTK_WED_WDMA_RX_PREF_FIFO_CFG 0x95C + #define MTK_WED_WDMA_RX_PREF_FIFO_RX0_CLR BIT(0) +@@ -367,6 +380,7 @@ struct mtk_wdma_desc { + + #define MTK_WED_WDMA_RESET_IDX 0xa08 + #define MTK_WED_WDMA_RESET_IDX_RX GENMASK(17, 16) ++#define MTK_WED_WDMA_RESET_IDX_RX_ALL BIT(20) + #define MTK_WED_WDMA_RESET_IDX_DRV GENMASK(25, 24) + + #define MTK_WED_WDMA_INT_CLR 0xa24 +@@ -437,21 +451,62 @@ struct mtk_wdma_desc { + #define MTK_WDMA_INT_MASK_RX_DELAY BIT(30) + #define MTK_WDMA_INT_MASK_RX_COHERENT BIT(31) + ++#define MTK_WDMA_XDMA_TX_FIFO_CFG 0x238 ++#define MTK_WDMA_XDMA_TX_FIFO_CFG_TX_PAR_FIFO_CLEAR BIT(0) ++#define MTK_WDMA_XDMA_TX_FIFO_CFG_TX_CMD_FIFO_CLEAR BIT(4) ++#define MTK_WDMA_XDMA_TX_FIFO_CFG_TX_DMAD_FIFO_CLEAR BIT(8) ++#define MTK_WDMA_XDMA_TX_FIFO_CFG_TX_ARR_FIFO_CLEAR BIT(12) ++ ++#define MTK_WDMA_XDMA_RX_FIFO_CFG 0x23c ++#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_PAR_FIFO_CLEAR BIT(0) ++#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_CMD_FIFO_CLEAR BIT(4) ++#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_DMAD_FIFO_CLEAR BIT(8) ++#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_ARR_FIFO_CLEAR BIT(12) ++#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_LEN_FIFO_CLEAR BIT(15) ++#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_WID_FIFO_CLEAR BIT(18) ++#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_BID_FIFO_CLEAR BIT(21) ++ + #define MTK_WDMA_INT_GRP1 0x250 + #define MTK_WDMA_INT_GRP2 0x254 + + #define MTK_WDMA_PREF_TX_CFG 0x2d0 + #define MTK_WDMA_PREF_TX_CFG_PREF_EN BIT(0) ++#define MTK_WDMA_PREF_TX_CFG_PREF_BUSY BIT(1) + + #define MTK_WDMA_PREF_RX_CFG 0x2dc + #define MTK_WDMA_PREF_RX_CFG_PREF_EN BIT(0) ++#define MTK_WDMA_PREF_RX_CFG_PREF_BUSY BIT(1) ++ ++#define MTK_WDMA_PREF_RX_FIFO_CFG 0x2e0 ++#define MTK_WDMA_PREF_RX_FIFO_CFG_RING0_CLEAR BIT(0) ++#define MTK_WDMA_PREF_RX_FIFO_CFG_RING1_CLEAR BIT(16) ++ ++#define MTK_WDMA_PREF_TX_FIFO_CFG 0x2d4 ++#define MTK_WDMA_PREF_TX_FIFO_CFG_RING0_CLEAR BIT(0) ++#define MTK_WDMA_PREF_TX_FIFO_CFG_RING1_CLEAR BIT(16) ++ ++#define MTK_WDMA_PREF_SIDX_CFG 0x2e4 ++#define MTK_WDMA_PREF_SIDX_CFG_TX_RING_CLEAR GENMASK(3, 0) ++#define MTK_WDMA_PREF_SIDX_CFG_RX_RING_CLEAR GENMASK(5, 4) + + #define MTK_WDMA_WRBK_TX_CFG 0x300 ++#define MTK_WDMA_WRBK_TX_CFG_WRBK_BUSY BIT(0) + #define MTK_WDMA_WRBK_TX_CFG_WRBK_EN BIT(30) + ++#define MTK_WDMA_WRBK_TX_FIFO_CFG(_n) (0x304 + (_n) * 0x4) ++#define MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR BIT(0) ++ + #define MTK_WDMA_WRBK_RX_CFG 0x344 ++#define MTK_WDMA_WRBK_RX_CFG_WRBK_BUSY BIT(0) + #define MTK_WDMA_WRBK_RX_CFG_WRBK_EN BIT(30) + ++#define MTK_WDMA_WRBK_RX_FIFO_CFG(_n) (0x348 + (_n) * 0x4) ++#define MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR BIT(0) ++ ++#define MTK_WDMA_WRBK_SIDX_CFG 0x388 ++#define MTK_WDMA_WRBK_SIDX_CFG_TX_RING_CLEAR GENMASK(3, 0) ++#define MTK_WDMA_WRBK_SIDX_CFG_RX_RING_CLEAR GENMASK(5, 4) ++ + #define MTK_PCIE_MIRROR_MAP(n) ((n) ? 0x4 : 0x0) + #define MTK_PCIE_MIRROR_MAP_EN BIT(0) + #define MTK_PCIE_MIRROR_MAP_WED_ID BIT(1) +@@ -465,6 +520,8 @@ struct mtk_wdma_desc { + #define MTK_WED_RTQM_Q_DBG_BYPASS BIT(5) + #define MTK_WED_RTQM_TXDMAD_FPORT GENMASK(23, 20) + ++#define MTK_WED_RTQM_RST 0xb04 ++ + #define MTK_WED_RTQM_IGRS0_I2HW_DMAD_CNT 0xb1c + #define MTK_WED_RTQM_IGRS0_I2H_DMAD_CNT(_n) (0xb20 + (_n) * 0x4) + #define MTK_WED_RTQM_IGRS0_I2HW_PKT_CNT 0xb28 +@@ -653,6 +710,9 @@ struct mtk_wdma_desc { + #define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_CLR BIT(17) + #define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_DONE_TRIG GENMASK(22, 18) + ++#define MTK_WED_RRO_RX_HW_STS 0xf00 ++#define MTK_WED_RX_IND_CMD_BUSY GENMASK(31, 0) ++ + #define MTK_WED_RX_IND_CMD_CNT0 0xf20 + #define MTK_WED_RX_IND_CMD_DBG_CNT_EN BIT(31) + diff --git a/target/linux/generic/backport-6.6/760-v6.9-net-phy-aquantia-add-AQR111-and-AQR111B0-PHY-ID.patch b/target/linux/generic/backport-6.6/760-v6.9-net-phy-aquantia-add-AQR111-and-AQR111B0-PHY-ID.patch new file mode 100644 index 0000000000..32ac37e8d0 --- /dev/null +++ b/target/linux/generic/backport-6.6/760-v6.9-net-phy-aquantia-add-AQR111-and-AQR111B0-PHY-ID.patch @@ -0,0 +1,102 @@ +From 038ba1dc4e54d51d953f5618d8eb5dd39bd9de25 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 13 Feb 2024 14:35:51 +0100 +Subject: [PATCH] net: phy: aquantia: add AQR111 and AQR111B0 PHY ID + +Add Aquantia AQR111 and AQR111B0 PHY ID. These PHY advertise 10G speed +but actually supports up to 5G speed, hence some manual fixup is needed. + +The Aquantia AQR111B0 PHY is just a variant of the AQR111 with smaller +chip size. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20240213133558.1836-1-ansuelsmth@gmail.com +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/aquantia/aquantia_main.c | 52 ++++++++++++++++++++++++ + 1 file changed, 52 insertions(+) + +--- a/drivers/net/phy/aquantia/aquantia_main.c ++++ b/drivers/net/phy/aquantia/aquantia_main.c +@@ -22,6 +22,8 @@ + #define PHY_ID_AQR107 0x03a1b4e0 + #define PHY_ID_AQCS109 0x03a1b5c2 + #define PHY_ID_AQR405 0x03a1b4b0 ++#define PHY_ID_AQR111 0x03a1b610 ++#define PHY_ID_AQR111B0 0x03a1b612 + #define PHY_ID_AQR113C 0x31c31c12 + + #define MDIO_PHYXS_VEND_IF_STATUS 0xe812 +@@ -670,6 +672,16 @@ static int aqr107_probe(struct phy_devic + return aqr_hwmon_probe(phydev); + } + ++static int aqr111_config_init(struct phy_device *phydev) ++{ ++ /* AQR111 reports supporting speed up to 10G, ++ * however only speeds up to 5G are supported. ++ */ ++ phy_set_max_speed(phydev, SPEED_5000); ++ ++ return aqr107_config_init(phydev); ++} ++ + static struct phy_driver aqr_driver[] = { + { + PHY_ID_MATCH_MODEL(PHY_ID_AQ1202), +@@ -744,6 +756,44 @@ static struct phy_driver aqr_driver[] = + .link_change_notify = aqr107_link_change_notify, + }, + { ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR111), ++ .name = "Aquantia AQR111", ++ .probe = aqr107_probe, ++ .get_rate_matching = aqr107_get_rate_matching, ++ .config_init = aqr111_config_init, ++ .config_aneg = aqr_config_aneg, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr107_read_status, ++ .get_tunable = aqr107_get_tunable, ++ .set_tunable = aqr107_set_tunable, ++ .suspend = aqr107_suspend, ++ .resume = aqr107_resume, ++ .get_sset_count = aqr107_get_sset_count, ++ .get_strings = aqr107_get_strings, ++ .get_stats = aqr107_get_stats, ++ .link_change_notify = aqr107_link_change_notify, ++}, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR111B0), ++ .name = "Aquantia AQR111B0", ++ .probe = aqr107_probe, ++ .get_rate_matching = aqr107_get_rate_matching, ++ .config_init = aqr111_config_init, ++ .config_aneg = aqr_config_aneg, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr107_read_status, ++ .get_tunable = aqr107_get_tunable, ++ .set_tunable = aqr107_set_tunable, ++ .suspend = aqr107_suspend, ++ .resume = aqr107_resume, ++ .get_sset_count = aqr107_get_sset_count, ++ .get_strings = aqr107_get_strings, ++ .get_stats = aqr107_get_stats, ++ .link_change_notify = aqr107_link_change_notify, ++}, ++{ + PHY_ID_MATCH_MODEL(PHY_ID_AQR405), + .name = "Aquantia AQR405", + .config_aneg = aqr_config_aneg, +@@ -782,6 +832,8 @@ static struct mdio_device_id __maybe_unu + { PHY_ID_MATCH_MODEL(PHY_ID_AQR107) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR111) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR111B0) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) }, + { } + }; diff --git a/target/linux/generic/backport-6.6/761-v6.9-net-phy-aquantia-add-AQR113-PHY-ID.patch b/target/linux/generic/backport-6.6/761-v6.9-net-phy-aquantia-add-AQR113-PHY-ID.patch new file mode 100644 index 0000000000..073bbbd49d --- /dev/null +++ b/target/linux/generic/backport-6.6/761-v6.9-net-phy-aquantia-add-AQR113-PHY-ID.patch @@ -0,0 +1,60 @@ +From 71b605d32017e5b8d257db7344bc2f8e8fcc973e Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 15 Feb 2024 16:30:05 +0100 +Subject: [PATCH] net: phy: aquantia: add AQR113 PHY ID + +Add Aquantia AQR113 PHY ID. Aquantia AQR113 is just a chip size variant of +the already supported AQR133C where the only difference is the PHY ID +and the hw chip size. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/aquantia/aquantia_main.c | 21 +++++++++++++++++++++ + 1 file changed, 21 insertions(+) + +--- a/drivers/net/phy/aquantia/aquantia_main.c ++++ b/drivers/net/phy/aquantia/aquantia_main.c +@@ -24,6 +24,7 @@ + #define PHY_ID_AQR405 0x03a1b4b0 + #define PHY_ID_AQR111 0x03a1b610 + #define PHY_ID_AQR111B0 0x03a1b612 ++#define PHY_ID_AQR113 0x31c31c40 + #define PHY_ID_AQR113C 0x31c31c12 + + #define MDIO_PHYXS_VEND_IF_STATUS 0xe812 +@@ -802,6 +803,25 @@ static struct phy_driver aqr_driver[] = + .read_status = aqr_read_status, + }, + { ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR113), ++ .name = "Aquantia AQR113", ++ .probe = aqr107_probe, ++ .get_rate_matching = aqr107_get_rate_matching, ++ .config_init = aqr107_config_init, ++ .config_aneg = aqr_config_aneg, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr107_read_status, ++ .get_tunable = aqr107_get_tunable, ++ .set_tunable = aqr107_set_tunable, ++ .suspend = aqr107_suspend, ++ .resume = aqr107_resume, ++ .get_sset_count = aqr107_get_sset_count, ++ .get_strings = aqr107_get_strings, ++ .get_stats = aqr107_get_stats, ++ .link_change_notify = aqr107_link_change_notify, ++}, ++{ + PHY_ID_MATCH_MODEL(PHY_ID_AQR113C), + .name = "Aquantia AQR113C", + .probe = aqr107_probe, +@@ -834,6 +854,7 @@ static struct mdio_device_id __maybe_unu + { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR111) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR111B0) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR113) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) }, + { } + }; diff --git a/target/linux/generic/backport-6.6/762-v6.9-net-phy-aquantia-add-AQR813-PHY-ID.patch b/target/linux/generic/backport-6.6/762-v6.9-net-phy-aquantia-add-AQR813-PHY-ID.patch new file mode 100644 index 0000000000..965b8c91b2 --- /dev/null +++ b/target/linux/generic/backport-6.6/762-v6.9-net-phy-aquantia-add-AQR813-PHY-ID.patch @@ -0,0 +1,59 @@ +From 6d47302a3f0ba31445478d518d98bd55918bc8ab Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 15 Feb 2024 22:43:30 +0100 +Subject: [PATCH] net: phy: aquantia: add AQR813 PHY ID + +Aquantia AQR813 is the Octal Port variant of the AQR113. Add PHY ID for +it to provide support for it. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/aquantia/aquantia_main.c | 21 +++++++++++++++++++++ + 1 file changed, 21 insertions(+) + +--- a/drivers/net/phy/aquantia/aquantia_main.c ++++ b/drivers/net/phy/aquantia/aquantia_main.c +@@ -26,6 +26,7 @@ + #define PHY_ID_AQR111B0 0x03a1b612 + #define PHY_ID_AQR113 0x31c31c40 + #define PHY_ID_AQR113C 0x31c31c12 ++#define PHY_ID_AQR813 0x31c31cb2 + + #define MDIO_PHYXS_VEND_IF_STATUS 0xe812 + #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK GENMASK(7, 3) +@@ -840,6 +841,25 @@ static struct phy_driver aqr_driver[] = + .get_stats = aqr107_get_stats, + .link_change_notify = aqr107_link_change_notify, + }, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR813), ++ .name = "Aquantia AQR813", ++ .probe = aqr107_probe, ++ .get_rate_matching = aqr107_get_rate_matching, ++ .config_init = aqr107_config_init, ++ .config_aneg = aqr_config_aneg, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr107_read_status, ++ .get_tunable = aqr107_get_tunable, ++ .set_tunable = aqr107_set_tunable, ++ .suspend = aqr107_suspend, ++ .resume = aqr107_resume, ++ .get_sset_count = aqr107_get_sset_count, ++ .get_strings = aqr107_get_strings, ++ .get_stats = aqr107_get_stats, ++ .link_change_notify = aqr107_link_change_notify, ++}, + }; + + module_phy_driver(aqr_driver); +@@ -856,6 +876,7 @@ static struct mdio_device_id __maybe_unu + { PHY_ID_MATCH_MODEL(PHY_ID_AQR111B0) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR113) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR813) }, + { } + }; + diff --git a/target/linux/generic/backport-6.6/770-net-introduce-napi_is_scheduled-helper.patch b/target/linux/generic/backport-6.6/770-net-introduce-napi_is_scheduled-helper.patch new file mode 100644 index 0000000000..789b93e9f9 --- /dev/null +++ b/target/linux/generic/backport-6.6/770-net-introduce-napi_is_scheduled-helper.patch @@ -0,0 +1,96 @@ +From 7f3eb2174512fe6c9c0f062e96eccb0d3cc6d5cd Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 18 Oct 2023 14:35:47 +0200 +Subject: [PATCH] net: introduce napi_is_scheduled helper + +We currently have napi_if_scheduled_mark_missed that can be used to +check if napi is scheduled but that does more thing than simply checking +it and return a bool. Some driver already implement custom function to +check if napi is scheduled. + +Drop these custom function and introduce napi_is_scheduled that simply +check if napi is scheduled atomically. + +Update any driver and code that implement a similar check and instead +use this new helper. + +Signed-off-by: Christian Marangi +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/chelsio/cxgb3/sge.c | 8 -------- + drivers/net/wireless/realtek/rtw89/core.c | 2 +- + include/linux/netdevice.h | 23 +++++++++++++++++++++++ + net/core/dev.c | 2 +- + 4 files changed, 25 insertions(+), 10 deletions(-) + +--- a/drivers/net/ethernet/chelsio/cxgb3/sge.c ++++ b/drivers/net/ethernet/chelsio/cxgb3/sge.c +@@ -2507,14 +2507,6 @@ static int napi_rx_handler(struct napi_s + return work_done; + } + +-/* +- * Returns true if the device is already scheduled for polling. +- */ +-static inline int napi_is_scheduled(struct napi_struct *napi) +-{ +- return test_bit(NAPI_STATE_SCHED, &napi->state); +-} +- + /** + * process_pure_responses - process pure responses from a response queue + * @adap: the adapter +--- a/drivers/net/wireless/realtek/rtw89/core.c ++++ b/drivers/net/wireless/realtek/rtw89/core.c +@@ -1479,7 +1479,7 @@ static void rtw89_core_rx_to_mac80211(st + struct napi_struct *napi = &rtwdev->napi; + + /* In low power mode, napi isn't scheduled. Receive it to netif. */ +- if (unlikely(!test_bit(NAPI_STATE_SCHED, &napi->state))) ++ if (unlikely(!napi_is_scheduled(napi))) + napi = NULL; + + rtw89_core_hw_to_sband_rate(rx_status); +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -468,6 +468,29 @@ static inline bool napi_prefer_busy_poll + return test_bit(NAPI_STATE_PREFER_BUSY_POLL, &n->state); + } + ++/** ++ * napi_is_scheduled - test if NAPI is scheduled ++ * @n: NAPI context ++ * ++ * This check is "best-effort". With no locking implemented, ++ * a NAPI can be scheduled or terminate right after this check ++ * and produce not precise results. ++ * ++ * NAPI_STATE_SCHED is an internal state, napi_is_scheduled ++ * should not be used normally and napi_schedule should be ++ * used instead. ++ * ++ * Use only if the driver really needs to check if a NAPI ++ * is scheduled for example in the context of delayed timer ++ * that can be skipped if a NAPI is already scheduled. ++ * ++ * Return True if NAPI is scheduled, False otherwise. ++ */ ++static inline bool napi_is_scheduled(struct napi_struct *n) ++{ ++ return test_bit(NAPI_STATE_SCHED, &n->state); ++} ++ + bool napi_schedule_prep(struct napi_struct *n); + + /** +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -6533,7 +6533,7 @@ static int __napi_poll(struct napi_struc + * accidentally calling ->poll() when NAPI is not scheduled. + */ + work = 0; +- if (test_bit(NAPI_STATE_SCHED, &n->state)) { ++ if (napi_is_scheduled(n)) { + work = n->poll(n, weight); + trace_napi_poll(n, work, weight); + } diff --git a/target/linux/generic/backport-6.6/771-v6.7-01-net-stmmac-improve-TX-timer-arm-logic.patch b/target/linux/generic/backport-6.6/771-v6.7-01-net-stmmac-improve-TX-timer-arm-logic.patch new file mode 100644 index 0000000000..aa0d730bc8 --- /dev/null +++ b/target/linux/generic/backport-6.6/771-v6.7-01-net-stmmac-improve-TX-timer-arm-logic.patch @@ -0,0 +1,77 @@ +From 2d1a42cf7f77cda54dbbee18d00b1200e7bc22aa Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 18 Oct 2023 14:35:48 +0200 +Subject: [PATCH 1/3] net: stmmac: improve TX timer arm logic + +There is currently a problem with the TX timer getting armed multiple +unnecessary times causing big performance regression on some device that +suffer from heavy handling of hrtimer rearm. + +The use of the TX timer is an old implementation that predates the napi +implementation and the interrupt enable/disable handling. + +Due to stmmac being a very old code, the TX timer was never evaluated +again with this new implementation and was kept there causing +performance regression. The performance regression started to appear +with kernel version 4.19 with 8fce33317023 ("net: stmmac: Rework coalesce +timer and fix multi-queue races") where the timer was reduced to 1ms +causing it to be armed 40 times more than before. + +Decreasing the timer made the problem more present and caused the +regression in the other of 600-700mbps on some device (regression where +this was notice is ipq806x). + +The problem is in the fact that handling the hrtimer on some target is +expensive and recent kernel made the timer armed much more times. +A solution that was proposed was reverting the hrtimer change and use +mod_timer but such solution would still hide the real problem in the +current implementation. + +To fix the regression, apply some additional logic and skip arming the +timer when not needed. + +Arm the timer ONLY if a napi is not already scheduled. Running the timer +is redundant since the same function (stmmac_tx_clean) will run in the +napi TX poll. Also try to cancel any timer if a napi is scheduled to +prevent redundant run of TX call. + +With the following new logic the original performance are restored while +keeping using the hrtimer. + +Signed-off-by: Christian Marangi +Signed-off-by: Paolo Abeni +--- + .../net/ethernet/stmicro/stmmac/stmmac_main.c | 18 +++++++++++++++--- + 1 file changed, 15 insertions(+), 3 deletions(-) + +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -2974,13 +2974,25 @@ static void stmmac_tx_timer_arm(struct s + { + struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + u32 tx_coal_timer = priv->tx_coal_timer[queue]; ++ struct stmmac_channel *ch; ++ struct napi_struct *napi; + + if (!tx_coal_timer) + return; + +- hrtimer_start(&tx_q->txtimer, +- STMMAC_COAL_TIMER(tx_coal_timer), +- HRTIMER_MODE_REL); ++ ch = &priv->channel[tx_q->queue_index]; ++ napi = tx_q->xsk_pool ? &ch->rxtx_napi : &ch->tx_napi; ++ ++ /* Arm timer only if napi is not already scheduled. ++ * Try to cancel any timer if napi is scheduled, timer will be armed ++ * again in the next scheduled napi. ++ */ ++ if (unlikely(!napi_is_scheduled(napi))) ++ hrtimer_start(&tx_q->txtimer, ++ STMMAC_COAL_TIMER(tx_coal_timer), ++ HRTIMER_MODE_REL); ++ else ++ hrtimer_try_to_cancel(&tx_q->txtimer); + } + + /** diff --git a/target/linux/generic/backport-6.6/771-v6.7-02-net-stmmac-move-TX-timer-arm-after-DMA-enable.patch b/target/linux/generic/backport-6.6/771-v6.7-02-net-stmmac-move-TX-timer-arm-after-DMA-enable.patch new file mode 100644 index 0000000000..95dd24881b --- /dev/null +++ b/target/linux/generic/backport-6.6/771-v6.7-02-net-stmmac-move-TX-timer-arm-after-DMA-enable.patch @@ -0,0 +1,96 @@ +From a594166387fe08e6f5a32130c400249a35b298f9 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 18 Oct 2023 14:35:49 +0200 +Subject: [PATCH 2/3] net: stmmac: move TX timer arm after DMA enable + +Move TX timer arm call after DMA interrupt is enabled again. + +The TX timer arm function changed logic and now is skipped if a napi is +already scheduled. By moving the TX timer arm call after DMA is enabled, +we permit to correctly skip if a DMA interrupt has been fired and a napi +has been scheduled again. + +Signed-off-by: Christian Marangi +Signed-off-by: Paolo Abeni +--- + .../net/ethernet/stmicro/stmmac/stmmac_main.c | 22 +++++++++++++++---- + 1 file changed, 18 insertions(+), 4 deletions(-) + +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -2527,9 +2527,13 @@ static void stmmac_bump_dma_threshold(st + * @priv: driver private structure + * @budget: napi budget limiting this functions packet handling + * @queue: TX queue index ++ * @pending_packets: signal to arm the TX coal timer + * Description: it reclaims the transmit resources after transmission completes. ++ * If some packets still needs to be handled, due to TX coalesce, set ++ * pending_packets to true to make NAPI arm the TX coal timer. + */ +-static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue) ++static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue, ++ bool *pending_packets) + { + struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + unsigned int bytes_compl = 0, pkts_compl = 0; +@@ -2692,7 +2696,7 @@ static int stmmac_tx_clean(struct stmmac + + /* We still have pending packets, let's call for a new scheduling */ + if (tx_q->dirty_tx != tx_q->cur_tx) +- stmmac_tx_timer_arm(priv, queue); ++ *pending_packets = true; + + __netif_tx_unlock_bh(netdev_get_tx_queue(priv->dev, queue)); + +@@ -5488,12 +5492,13 @@ static int stmmac_napi_poll_tx(struct na + struct stmmac_channel *ch = + container_of(napi, struct stmmac_channel, tx_napi); + struct stmmac_priv *priv = ch->priv_data; ++ bool pending_packets = false; + u32 chan = ch->index; + int work_done; + + priv->xstats.napi_poll++; + +- work_done = stmmac_tx_clean(priv, budget, chan); ++ work_done = stmmac_tx_clean(priv, budget, chan, &pending_packets); + work_done = min(work_done, budget); + + if (work_done < budget && napi_complete_done(napi, work_done)) { +@@ -5504,6 +5509,10 @@ static int stmmac_napi_poll_tx(struct na + spin_unlock_irqrestore(&ch->lock, flags); + } + ++ /* TX still have packet to handle, check if we need to arm tx timer */ ++ if (pending_packets) ++ stmmac_tx_timer_arm(priv, chan); ++ + return work_done; + } + +@@ -5512,12 +5521,13 @@ static int stmmac_napi_poll_rxtx(struct + struct stmmac_channel *ch = + container_of(napi, struct stmmac_channel, rxtx_napi); + struct stmmac_priv *priv = ch->priv_data; ++ bool tx_pending_packets = false; + int rx_done, tx_done, rxtx_done; + u32 chan = ch->index; + + priv->xstats.napi_poll++; + +- tx_done = stmmac_tx_clean(priv, budget, chan); ++ tx_done = stmmac_tx_clean(priv, budget, chan, &tx_pending_packets); + tx_done = min(tx_done, budget); + + rx_done = stmmac_rx_zc(priv, budget, chan); +@@ -5542,6 +5552,10 @@ static int stmmac_napi_poll_rxtx(struct + spin_unlock_irqrestore(&ch->lock, flags); + } + ++ /* TX still have packet to handle, check if we need to arm tx timer */ ++ if (tx_pending_packets) ++ stmmac_tx_timer_arm(priv, chan); ++ + return min(rxtx_done, budget - 1); + } + diff --git a/target/linux/generic/backport-6.6/771-v6.7-03-net-stmmac-increase-TX-coalesce-timer-to-5ms.patch b/target/linux/generic/backport-6.6/771-v6.7-03-net-stmmac-increase-TX-coalesce-timer-to-5ms.patch new file mode 100644 index 0000000000..bce54eba4f --- /dev/null +++ b/target/linux/generic/backport-6.6/771-v6.7-03-net-stmmac-increase-TX-coalesce-timer-to-5ms.patch @@ -0,0 +1,38 @@ +From 039550960a2235cfe2dfaa773df9f98f8da31a0c Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 18 Oct 2023 14:35:50 +0200 +Subject: [PATCH 3/3] net: stmmac: increase TX coalesce timer to 5ms + +Commit 8fce33317023 ("net: stmmac: Rework coalesce timer and fix +multi-queue races") decreased the TX coalesce timer from 40ms to 1ms. + +This caused some performance regression on some target (regression was +reported at least on ipq806x) in the order of 600mbps dropping from +gigabit handling to only 200mbps. + +The problem was identified in the TX timer getting armed too much time. +While this was fixed and improved in another commit, performance can be +improved even further by increasing the timer delay a bit moving from +1ms to 5ms. + +The value is a good balance between battery saving by prevending too +much interrupt to be generated and permitting good performance for +internet oriented devices. + +Signed-off-by: Christian Marangi +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/stmicro/stmmac/common.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/ethernet/stmicro/stmmac/common.h ++++ b/drivers/net/ethernet/stmicro/stmmac/common.h +@@ -287,7 +287,7 @@ struct stmmac_safety_stats { + #define MIN_DMA_RIWT 0x10 + #define DEF_DMA_RIWT 0xa0 + /* Tx coalesce parameters */ +-#define STMMAC_COAL_TX_TIMER 1000 ++#define STMMAC_COAL_TX_TIMER 5000 + #define STMMAC_MAX_COAL_TX_TICK 100000 + #define STMMAC_TX_MAX_FRAMES 256 + #define STMMAC_TX_FRAMES 25 diff --git a/target/linux/generic/backport-6.6/777-v6.2-04-net-dsa-qca8k-introduce-single-mii-read-write-lo-hi.patch b/target/linux/generic/backport-6.6/777-v6.2-04-net-dsa-qca8k-introduce-single-mii-read-write-lo-hi.patch new file mode 100644 index 0000000000..c0320ad6f9 --- /dev/null +++ b/target/linux/generic/backport-6.6/777-v6.2-04-net-dsa-qca8k-introduce-single-mii-read-write-lo-hi.patch @@ -0,0 +1,150 @@ +From cfbd6de588ef659c198083205dc954a6d3ed2aec Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + 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 diff --git a/target/linux/generic/backport-6.6/777-v6.2-05-net-dsa-qca8k-improve-mdio-master-read-write-by-usin.patch b/target/linux/generic/backport-6.6/777-v6.2-05-net-dsa-qca8k-improve-mdio-master-read-write-by-usin.patch new file mode 100644 index 0000000000..dcafad0fd5 --- /dev/null +++ b/target/linux/generic/backport-6.6/777-v6.2-05-net-dsa-qca8k-improve-mdio-master-read-write-by-usin.patch @@ -0,0 +1,73 @@ +From a4165830ca237f2b3318faf62562bce8ce12a389 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + 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); + diff --git a/target/linux/generic/backport-6.6/778-v6.3-01-net-dsa-qca8k-add-QCA8K_ATU_TABLE_SIZE-define-for-fd.patch b/target/linux/generic/backport-6.6/778-v6.3-01-net-dsa-qca8k-add-QCA8K_ATU_TABLE_SIZE-define-for-fd.patch new file mode 100644 index 0000000000..9331fd536d --- /dev/null +++ b/target/linux/generic/backport-6.6/778-v6.3-01-net-dsa-qca8k-add-QCA8K_ATU_TABLE_SIZE-define-for-fd.patch @@ -0,0 +1,63 @@ +From e03cea60c3db8c6b011cc36ecef9281dff8377f3 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Signed-off-by: David S. Miller +--- + 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) diff --git a/target/linux/generic/backport-6.6/778-v6.3-02-net-dsa-qca8k-convert-to-regmap-read-write-API.patch b/target/linux/generic/backport-6.6/778-v6.3-02-net-dsa-qca8k-convert-to-regmap-read-write-API.patch new file mode 100644 index 0000000000..b8f8071b0a --- /dev/null +++ b/target/linux/generic/backport-6.6/778-v6.3-02-net-dsa-qca8k-convert-to-regmap-read-write-API.patch @@ -0,0 +1,261 @@ +From c766e077d927e1775902c18827205ea2ade3a35d Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + 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 { diff --git a/target/linux/generic/backport-6.6/779-v6.5-net-dsa-qca8k-enable-use_single_write-for-qca8xxx.patch b/target/linux/generic/backport-6.6/779-v6.5-net-dsa-qca8k-enable-use_single_write-for-qca8xxx.patch new file mode 100644 index 0000000000..3619440f07 --- /dev/null +++ b/target/linux/generic/backport-6.6/779-v6.5-net-dsa-qca8k-enable-use_single_write-for-qca8xxx.patch @@ -0,0 +1,78 @@ +From 2c39dd025da489cf87d26469d9f5ff19715324a0 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Fixes: c766e077d927 ("net: dsa: qca8k: convert to regmap read/write API") +Signed-off-by: Christian Marangi +Cc: stable@vger.kernel.org +Signed-off-by: David S. Miller +--- + 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 diff --git a/target/linux/generic/backport-6.6/780-v6.6-01-net-dsa-qca8k-make-learning-configurable-and-keep-of.patch b/target/linux/generic/backport-6.6/780-v6.6-01-net-dsa-qca8k-make-learning-configurable-and-keep-of.patch new file mode 100644 index 0000000000..d31789370e --- /dev/null +++ b/target/linux/generic/backport-6.6/780-v6.6-01-net-dsa-qca8k-make-learning-configurable-and-keep-of.patch @@ -0,0 +1,146 @@ +From 23cfc7172e5297d0bee49ac6f6f8248d1cf0820d Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Reviewed-by: Vladimir Oltean +Reviewed-by: Florian Fainelli +Link: https://lore.kernel.org/r/20230730074113.21889-2-ansuelsmth@gmail.com +Signed-off-by: Paolo Abeni +--- + 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, diff --git a/target/linux/generic/backport-6.6/780-v6.6-02-net-dsa-qca8k-limit-user-ports-access-to-the-first-C.patch b/target/linux/generic/backport-6.6/780-v6.6-02-net-dsa-qca8k-limit-user-ports-access-to-the-first-C.patch new file mode 100644 index 0000000000..4b457f67de --- /dev/null +++ b/target/linux/generic/backport-6.6/780-v6.6-02-net-dsa-qca8k-limit-user-ports-access-to-the-first-C.patch @@ -0,0 +1,53 @@ +From 18e8feae4a807994e4906d659116d249bfecd4c5 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Reviewed-by: Simon Horman +Reviewed-by: Florian Fainelli +Reviewed-by: Vladimir Oltean +Link: https://lore.kernel.org/r/20230730074113.21889-3-ansuelsmth@gmail.com +Signed-off-by: Paolo Abeni +--- + 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), diff --git a/target/linux/generic/backport-6.6/780-v6.6-03-net-dsa-qca8k-move-qca8xxx-hol-fixup-to-separate-fun.patch b/target/linux/generic/backport-6.6/780-v6.6-03-net-dsa-qca8k-move-qca8xxx-hol-fixup-to-separate-fun.patch new file mode 100644 index 0000000000..f556628b5b --- /dev/null +++ b/target/linux/generic/backport-6.6/780-v6.6-03-net-dsa-qca8k-move-qca8xxx-hol-fixup-to-separate-fun.patch @@ -0,0 +1,111 @@ +From a9108b0712bf018dc69020864b21485b71b17dfc Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Reviewed-by: Florian Fainelli +Link: https://lore.kernel.org/r/20230730074113.21889-4-ansuelsmth@gmail.com +Signed-off-by: Paolo Abeni +--- + 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 */ diff --git a/target/linux/generic/backport-6.6/780-v6.6-04-net-dsa-qca8k-use-dsa_for_each-macro-instead-of-for-.patch b/target/linux/generic/backport-6.6/780-v6.6-04-net-dsa-qca8k-use-dsa_for_each-macro-instead-of-for-.patch new file mode 100644 index 0000000000..faa0142ca9 --- /dev/null +++ b/target/linux/generic/backport-6.6/780-v6.6-04-net-dsa-qca8k-use-dsa_for_each-macro-instead-of-for-.patch @@ -0,0 +1,158 @@ +From 01e6f8ad8d26ced14b0cf288c42e55d03a7c5070 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Reviewed-by: Florian Fainelli +Link: https://lore.kernel.org/r/20230730074113.21889-5-ansuelsmth@gmail.com +Signed-off-by: Paolo Abeni +--- + 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) | diff --git a/target/linux/generic/backport-6.6/781-v6.6-01-net-dsa-qca8k-fix-regmap-bulk-read-write-methods-on-.patch b/target/linux/generic/backport-6.6/781-v6.6-01-net-dsa-qca8k-fix-regmap-bulk-read-write-methods-on-.patch new file mode 100644 index 0000000000..632f422d1f --- /dev/null +++ b/target/linux/generic/backport-6.6/781-v6.6-01-net-dsa-qca8k-fix-regmap-bulk-read-write-methods-on-.patch @@ -0,0 +1,61 @@ +From 5652d1741574eb89cc02576e50ee3e348bd6dd77 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= +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 +Tested-by: Christian Marangi +Reviewed-by: Christian Marangi +Signed-off-by: David S. Miller +--- + 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 && diff --git a/target/linux/generic/backport-6.6/788-v6.3-net-dsa-mt7530-use-external-PCS-driver.patch b/target/linux/generic/backport-6.6/788-v6.3-net-dsa-mt7530-use-external-PCS-driver.patch new file mode 100644 index 0000000000..8010076fc0 --- /dev/null +++ b/target/linux/generic/backport-6.6/788-v6.3-net-dsa-mt7530-use-external-PCS-driver.patch @@ -0,0 +1,514 @@ +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 +X-Patchwork-Id: 13167235 +X-Patchwork-Delegate: kuba@kernel.org +Return-Path: +Date: Thu, 9 Mar 2023 10:57:44 +0000 +From: Daniel Golle +To: netdev@vger.kernel.org, linux-mediatek@lists.infradead.org, + linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, + Russell King , + Heiner Kallweit , + Lorenzo Bianconi , + Mark Lee , + John Crispin , Felix Fietkau , + AngeloGioacchino Del Regno + , + Matthias Brugger , + DENG Qingfang , + Landen Chao , + Sean Wang , + Paolo Abeni , + Jakub Kicinski , + Eric Dumazet , + "David S. Miller" , + Vladimir Oltean , + Florian Fainelli , + Andrew Lunn , + Vladimir Oltean +Cc: =?iso-8859-1?q?Bj=F8rn?= Mork , + Frank Wunderlich , + Alexander Couzens +Subject: [PATCH net-next v13 11/16] net: dsa: mt7530: use external PCS driver +Message-ID: + <2ac2ee40d3b0e705461b50613fda6a7edfdbc4b3.1678357225.git.daniel@makrotopia.org> +References: +MIME-Version: 1.0 +Content-Disposition: inline +In-Reply-To: +Precedence: bulk +List-ID: +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) +Tested-by: Bjørn Mork +Signed-off-by: Daniel Golle +Tested-by: Frank Wunderlich +--- + 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 + #include + #include ++#include + #include + #include + #include +@@ -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 */ diff --git a/target/linux/generic/backport-6.6/790-v6.4-0001-net-dsa-mt7530-make-some-noise-if-register-read-fail.patch b/target/linux/generic/backport-6.6/790-v6.4-0001-net-dsa-mt7530-make-some-noise-if-register-read-fail.patch new file mode 100644 index 0000000000..4d024b063a --- /dev/null +++ b/target/linux/generic/backport-6.6/790-v6.4-0001-net-dsa-mt7530-make-some-noise-if-register-read-fail.patch @@ -0,0 +1,32 @@ +From c3552d3f85f06cf4b4818bd84c4fcc09d8d45165 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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); diff --git a/target/linux/generic/backport-6.6/790-v6.4-0002-net-dsa-mt7530-refactor-SGMII-PCS-creation.patch b/target/linux/generic/backport-6.6/790-v6.4-0002-net-dsa-mt7530-refactor-SGMII-PCS-creation.patch new file mode 100644 index 0000000000..5667449296 --- /dev/null +++ b/target/linux/generic/backport-6.6/790-v6.4-0002-net-dsa-mt7530-refactor-SGMII-PCS-creation.patch @@ -0,0 +1,111 @@ +From b896355fc4988216d4f38582d07add9252a795ae Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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; + } diff --git a/target/linux/generic/backport-6.6/790-v6.4-0003-net-dsa-mt7530-use-unlocked-regmap-accessors.patch b/target/linux/generic/backport-6.6/790-v6.4-0003-net-dsa-mt7530-use-unlocked-regmap-accessors.patch new file mode 100644 index 0000000000..3b4689fb19 --- /dev/null +++ b/target/linux/generic/backport-6.6/790-v6.4-0003-net-dsa-mt7530-use-unlocked-regmap-accessors.patch @@ -0,0 +1,74 @@ +From 33396408776385f3d2f6069646169a6b5b28e3b3 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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, diff --git a/target/linux/generic/backport-6.6/790-v6.4-0004-net-dsa-mt7530-use-regmap-to-access-switch-register-.patch b/target/linux/generic/backport-6.6/790-v6.4-0004-net-dsa-mt7530-use-regmap-to-access-switch-register-.patch new file mode 100644 index 0000000000..04033f14f4 --- /dev/null +++ b/target/linux/generic/backport-6.6/790-v6.4-0004-net-dsa-mt7530-use-regmap-to-access-switch-register-.patch @@ -0,0 +1,224 @@ +From 743cba4345cb366248f9d375c6a9e50243dc0677 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Signed-off-by: David S. Miller +--- + 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; diff --git a/target/linux/generic/backport-6.6/790-v6.4-0005-net-dsa-mt7530-move-SGMII-PCS-creation-to-mt7530_pro.patch b/target/linux/generic/backport-6.6/790-v6.4-0005-net-dsa-mt7530-move-SGMII-PCS-creation-to-mt7530_pro.patch new file mode 100644 index 0000000000..6c5bebdd80 --- /dev/null +++ b/target/linux/generic/backport-6.6/790-v6.4-0005-net-dsa-mt7530-move-SGMII-PCS-creation-to-mt7530_pro.patch @@ -0,0 +1,54 @@ +From f3cf1d06e2aef644b426c23b4bb570780b1f8d47 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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); + } + diff --git a/target/linux/generic/backport-6.6/790-v6.4-0006-net-dsa-mt7530-introduce-mutex-helpers.patch b/target/linux/generic/backport-6.6/790-v6.4-0006-net-dsa-mt7530-introduce-mutex-helpers.patch new file mode 100644 index 0000000000..a8933d2cf4 --- /dev/null +++ b/target/linux/generic/backport-6.6/790-v6.4-0006-net-dsa-mt7530-introduce-mutex-helpers.patch @@ -0,0 +1,273 @@ +From e4729ae7c095c0c87794bff47ea43e35d69de986 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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 = { diff --git a/target/linux/generic/backport-6.6/790-v6.4-0007-net-dsa-mt7530-move-p5_intf_modes-function-to-mt7530.patch b/target/linux/generic/backport-6.6/790-v6.4-0007-net-dsa-mt7530-move-p5_intf_modes-function-to-mt7530.patch new file mode 100644 index 0000000000..6c68dc0c4f --- /dev/null +++ b/target/linux/generic/backport-6.6/790-v6.4-0007-net-dsa-mt7530-move-p5_intf_modes-function-to-mt7530.patch @@ -0,0 +1,75 @@ +From 0d7ae94a0c581f86939bebec0b6ccd66e640d1d8 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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 { diff --git a/target/linux/generic/backport-6.6/790-v6.4-0008-net-dsa-mt7530-introduce-mt7530_probe_common-helper-.patch b/target/linux/generic/backport-6.6/790-v6.4-0008-net-dsa-mt7530-introduce-mt7530_probe_common-helper-.patch new file mode 100644 index 0000000000..dc4fcb6aa1 --- /dev/null +++ b/target/linux/generic/backport-6.6/790-v6.4-0008-net-dsa-mt7530-introduce-mt7530_probe_common-helper-.patch @@ -0,0 +1,155 @@ +From 4d632005c90e253c000d0db73b7cdb9d8dc2e2dd Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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); diff --git a/target/linux/generic/backport-6.6/790-v6.4-0009-net-dsa-mt7530-introduce-mt7530_remove_common-helper.patch b/target/linux/generic/backport-6.6/790-v6.4-0009-net-dsa-mt7530-introduce-mt7530_remove_common-helper.patch new file mode 100644 index 0000000000..5df859d2df --- /dev/null +++ b/target/linux/generic/backport-6.6/790-v6.4-0009-net-dsa-mt7530-introduce-mt7530_remove_common-helper.patch @@ -0,0 +1,54 @@ +From 69b838d2629e6b82bcd9e0ab3c1c03f46e5e01d3 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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) diff --git a/target/linux/generic/backport-6.6/790-v6.4-0010-net-dsa-mt7530-introduce-separate-MDIO-driver.patch b/target/linux/generic/backport-6.6/790-v6.4-0010-net-dsa-mt7530-introduce-separate-MDIO-driver.patch new file mode 100644 index 0000000000..d203711884 --- /dev/null +++ b/target/linux/generic/backport-6.6/790-v6.4-0010-net-dsa-mt7530-introduce-separate-MDIO-driver.patch @@ -0,0 +1,691 @@ +From 8eceed6dbd74067dbf4d8e39f14734f4d2f35176 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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 + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 "); ++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 + #include + #include +-#include + #include + #include + #include +@@ -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 "); + 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 */ diff --git a/target/linux/generic/backport-6.6/790-v6.4-0011-net-dsa-mt7530-skip-locking-if-MDIO-bus-isn-t-presen.patch b/target/linux/generic/backport-6.6/790-v6.4-0011-net-dsa-mt7530-skip-locking-if-MDIO-bus-isn-t-presen.patch new file mode 100644 index 0000000000..01011ed1a0 --- /dev/null +++ b/target/linux/generic/backport-6.6/790-v6.4-0011-net-dsa-mt7530-skip-locking-if-MDIO-bus-isn-t-presen.patch @@ -0,0 +1,47 @@ +From a52cadbf76593f8fcb2f4f62cb006e3f2a22ad06 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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 diff --git a/target/linux/generic/backport-6.6/790-v6.4-0012-net-dsa-mt7530-introduce-driver-for-MT7988-built-in-.patch b/target/linux/generic/backport-6.6/790-v6.4-0012-net-dsa-mt7530-introduce-driver-for-MT7988-built-in-.patch new file mode 100644 index 0000000000..63bf0e77fa --- /dev/null +++ b/target/linux/generic/backport-6.6/790-v6.4-0012-net-dsa-mt7530-introduce-driver-for-MT7988-built-in-.patch @@ -0,0 +1,421 @@ +From b361015763fedea439f13b336b15ef7bdf1f7d4f Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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 + M: Landen Chao + M: DENG Qingfang ++M: Daniel Golle + 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 ++#include ++#include ++#include ++#include ++#include ++ ++#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 "); ++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 | \ diff --git a/target/linux/generic/backport-6.6/790-v6.4-0013-net-dsa-mt7530-fix-support-for-MT7531BE.patch b/target/linux/generic/backport-6.6/790-v6.4-0013-net-dsa-mt7530-fix-support-for-MT7531BE.patch new file mode 100644 index 0000000000..5b5f25e7af --- /dev/null +++ b/target/linux/generic/backport-6.6/790-v6.4-0013-net-dsa-mt7530-fix-support-for-MT7531BE.patch @@ -0,0 +1,118 @@ +From eb1dd407b4be7ca38166a38c56c8edf52c6a399f Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Acked-by: Arınç ÜNAL +Link: https://lore.kernel.org/r/ZDvlLhhqheobUvOK@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + 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 { diff --git a/target/linux/generic/backport-6.6/791-v6.2-01-net-phy-Add-driver-for-Motorcomm-yt8521-gigabit-ethernet.patch b/target/linux/generic/backport-6.6/791-v6.2-01-net-phy-Add-driver-for-Motorcomm-yt8521-gigabit-ethernet.patch new file mode 100644 index 0000000000..4cdd67cdcc --- /dev/null +++ b/target/linux/generic/backport-6.6/791-v6.2-01-net-phy-Add-driver-for-Motorcomm-yt8521-gigabit-ethernet.patch @@ -0,0 +1,1724 @@ +From 70479a40954cf353e87a486997a3477108c75aa9 Mon Sep 17 00:00:00 2001 +From: Frank +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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 ++M: Frank + 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 ++ * Author: Frank + */ + ++#include + #include + #include + #include + + #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 */ } + }; + diff --git a/target/linux/generic/backport-6.6/791-v6.2-02-net-phy-fix-yt8521-duplicated-argument-to-or.patch b/target/linux/generic/backport-6.6/791-v6.2-02-net-phy-fix-yt8521-duplicated-argument-to-or.patch new file mode 100644 index 0000000000..cce71c8d84 --- /dev/null +++ b/target/linux/generic/backport-6.6/791-v6.2-02-net-phy-fix-yt8521-duplicated-argument-to-or.patch @@ -0,0 +1,49 @@ +From 4e0243e7128c9b25ea2739136076a95d6adaba5e Mon Sep 17 00:00:00 2001 +From: Frank +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 +Reported-by: Julia Lawall +Signed-off-by: Frank +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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: diff --git a/target/linux/generic/backport-6.6/791-v6.2-03-net-phy-add-Motorcomm-YT8531S-phy-id.patch b/target/linux/generic/backport-6.6/791-v6.2-03-net-phy-add-Motorcomm-YT8531S-phy-id.patch new file mode 100644 index 0000000000..d22cc69425 --- /dev/null +++ b/target/linux/generic/backport-6.6/791-v6.2-03-net-phy-add-Motorcomm-YT8531S-phy-id.patch @@ -0,0 +1,140 @@ +From 813abcd98fb1b2cccf850cdfa092a4bfc50b2363 Mon Sep 17 00:00:00 2001 +From: Frank +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 +Signed-off-by: David S. Miller +--- + 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 + * Author: Frank +@@ -12,9 +12,10 @@ + #include + + #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 */ } + }; + diff --git a/target/linux/generic/backport-6.6/791-v6.3-04-net-phy-fix-the-spelling-problem-of-Sentinel.patch b/target/linux/generic/backport-6.6/791-v6.3-04-net-phy-fix-the-spelling-problem-of-Sentinel.patch new file mode 100644 index 0000000000..94fc32aadb --- /dev/null +++ b/target/linux/generic/backport-6.6/791-v6.3-04-net-phy-fix-the-spelling-problem-of-Sentinel.patch @@ -0,0 +1,26 @@ +From 4104a713204d62aca482eebb0c6226d82a0721eb Mon Sep 17 00:00:00 2001 +From: Frank Sae +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 +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20230128063558.5850-1-Frank.Sae@motor-comm.com +Signed-off-by: Jakub Kicinski +--- + 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); diff --git a/target/linux/generic/backport-6.6/791-v6.3-05-net-phy-motorcomm-change-the-phy-id-of-yt8521-and-yt8531s.patch b/target/linux/generic/backport-6.6/791-v6.3-05-net-phy-motorcomm-change-the-phy-id-of-yt8521-and-yt8531s.patch new file mode 100644 index 0000000000..076fa82d26 --- /dev/null +++ b/target/linux/generic/backport-6.6/791-v6.3-05-net-phy-motorcomm-change-the-phy-id-of-yt8521-and-yt8531s.patch @@ -0,0 +1,29 @@ +From 3c1dc22162d673d595855d24f95200ed2643f88f Mon Sep 17 00:00:00 2001 +From: Frank Sae +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 +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20230128063558.5850-2-Frank.Sae@motor-comm.com +Signed-off-by: Jakub Kicinski +--- + 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 + + #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 diff --git a/target/linux/generic/backport-6.6/791-v6.3-06-net-phy-Add-BIT-macro-for-Motorcomm-yt8521-yt8531-gigabit.patch b/target/linux/generic/backport-6.6/791-v6.3-06-net-phy-Add-BIT-macro-for-Motorcomm-yt8521-yt8531-gigabit.patch new file mode 100644 index 0000000000..ba9a6ab4cc --- /dev/null +++ b/target/linux/generic/backport-6.6/791-v6.3-06-net-phy-Add-BIT-macro-for-Motorcomm-yt8521-yt8531-gigabit.patch @@ -0,0 +1,107 @@ +From 4869a146cd60fc8115230f0a45e15e534c531922 Mon Sep 17 00:00:00 2001 +From: Frank Sae +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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) + diff --git a/target/linux/generic/backport-6.6/791-v6.3-07-net-phy-Add-dts-support-for-Motorcomm-yt8521-gigabit.patch b/target/linux/generic/backport-6.6/791-v6.3-07-net-phy-Add-dts-support-for-Motorcomm-yt8521-gigabit.patch new file mode 100644 index 0000000000..6d89fae84c --- /dev/null +++ b/target/linux/generic/backport-6.6/791-v6.3-07-net-phy-Add-dts-support-for-Motorcomm-yt8521-gigabit.patch @@ -0,0 +1,343 @@ +From a6e68f0f8769f79c67cdcfb6302feecd36197dec Mon Sep 17 00:00:00 2001 +From: Frank Sae +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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 + #include + #include ++#include + + #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); + } diff --git a/target/linux/generic/backport-6.6/791-v6.3-08-net-phy-Add-dts-support-for-Motorcomm-yt8531s-gigabit.patch b/target/linux/generic/backport-6.6/791-v6.3-08-net-phy-Add-dts-support-for-Motorcomm-yt8531s-gigabit.patch new file mode 100644 index 0000000000..86fc04695c --- /dev/null +++ b/target/linux/generic/backport-6.6/791-v6.3-08-net-phy-Add-dts-support-for-Motorcomm-yt8531s-gigabit.patch @@ -0,0 +1,100 @@ +From 36152f87dda4af221b16258751451d9cd3d0fb0b Mon Sep 17 00:00:00 2001 +From: Frank Sae +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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, diff --git a/target/linux/generic/backport-6.6/791-v6.3-09-net-phy-Add-driver-for-Motorcomm-yt8531-gigabit-ethernet.patch b/target/linux/generic/backport-6.6/791-v6.3-09-net-phy-Add-driver-for-Motorcomm-yt8531-gigabit-ethernet.patch new file mode 100644 index 0000000000..60eea4fa47 --- /dev/null +++ b/target/linux/generic/backport-6.6/791-v6.3-09-net-phy-Add-driver-for-Motorcomm-yt8531-gigabit-ethernet.patch @@ -0,0 +1,302 @@ +From 4ac94f728a588e7096dd5010cd7141a309ea7805 Mon Sep 17 00:00:00 2001 +From: Frank Sae +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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 + * Author: Frank +@@ -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 */ } + }; diff --git a/target/linux/generic/backport-6.6/791-v6.3-10-net-phy-motorcomm-uninitialized-variables-in.patch b/target/linux/generic/backport-6.6/791-v6.3-10-net-phy-motorcomm-uninitialized-variables-in.patch new file mode 100644 index 0000000000..29ae86dbbc --- /dev/null +++ b/target/linux/generic/backport-6.6/791-v6.3-10-net-phy-motorcomm-uninitialized-variables-in.patch @@ -0,0 +1,34 @@ +From 9753613f7399601f9bae6ee81e9d4274246c98ab Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +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 +Reviewed-by: Frank Sae +Link: https://lore.kernel.org/r/Y+xd2yJet2ImHLoQ@kili +Signed-off-by: Jakub Kicinski +--- + 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; + diff --git a/target/linux/generic/backport-6.6/791-v6.6-11-net-phy-motorcomm-Add-pad-drive-strength-cfg-support.patch b/target/linux/generic/backport-6.6/791-v6.6-11-net-phy-motorcomm-Add-pad-drive-strength-cfg-support.patch new file mode 100644 index 0000000000..010ca9b68a --- /dev/null +++ b/target/linux/generic/backport-6.6/791-v6.6-11-net-phy-motorcomm-Add-pad-drive-strength-cfg-support.patch @@ -0,0 +1,170 @@ +From 7a561e9351ae7e3fb1f08584d40b49c1e55dde60 Mon Sep 17 00:00:00 2001 +From: Samin Guo +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 +Signed-off-by: Samin Guo +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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; + } + diff --git a/target/linux/generic/backport-6.6/792-v6.6-net-phylink-add-pcs_enable-pcs_disable-methods.patch b/target/linux/generic/backport-6.6/792-v6.6-net-phylink-add-pcs_enable-pcs_disable-methods.patch new file mode 100644 index 0000000000..f82c8fc622 --- /dev/null +++ b/target/linux/generic/backport-6.6/792-v6.6-net-phylink-add-pcs_enable-pcs_disable-methods.patch @@ -0,0 +1,173 @@ +From 90ef0a7b0622c62758b2638604927867775479ea Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Signed-off-by: David S. Miller +--- + 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. diff --git a/target/linux/generic/backport-6.6/793-v6.6-net-pcs-lynxi-implement-pcs_disable-op.patch b/target/linux/generic/backport-6.6/793-v6.6-net-pcs-lynxi-implement-pcs_disable-op.patch new file mode 100644 index 0000000000..6b6369761a --- /dev/null +++ b/target/linux/generic/backport-6.6/793-v6.6-net-pcs-lynxi-implement-pcs_disable-op.patch @@ -0,0 +1,44 @@ +From e4ccdfb78a47132f2d215658aab8902fc457c4b4 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Link: https://lore.kernel.org/r/f23d1a60d2c9d2fb72e32dcb0eaa5f7e867a3d68.1692327891.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + 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, diff --git a/target/linux/generic/backport-6.6/794-v6.2-net-core-Allow-live-renaming-when-an-interface-is-up.patch b/target/linux/generic/backport-6.6/794-v6.2-net-core-Allow-live-renaming-when-an-interface-is-up.patch new file mode 100644 index 0000000000..c4141eee93 --- /dev/null +++ b/target/linux/generic/backport-6.6/794-v6.2-net-core-Allow-live-renaming-when-an-interface-is-up.patch @@ -0,0 +1,136 @@ +From: Andy Ren +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 +Reviewed-by: Ido Schimmel +Reviewed-by: David Ahern +Signed-off-by: David S. Miller +--- + +--- 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)) diff --git a/target/linux/generic/backport-6.6/795-v6.3-02-cdc_ether-no-need-to-blacklist-any-r8152-devices.patch b/target/linux/generic/backport-6.6/795-v6.3-02-cdc_ether-no-need-to-blacklist-any-r8152-devices.patch new file mode 100644 index 0000000000..17131c16d3 --- /dev/null +++ b/target/linux/generic/backport-6.6/795-v6.3-02-cdc_ether-no-need-to-blacklist-any-r8152-devices.patch @@ -0,0 +1,158 @@ +From 69649ef8405320f81497f4757faac8234f61b167 Mon Sep 17 00:00:00 2001 +From: Bjørn Mork +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 +Signed-off-by: David S. Miller +--- + 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) */ + { diff --git a/target/linux/generic/backport-6.6/795-v6.3-05-r8152-reduce-the-control-transfer-of-rtl8152_get_ver.patch b/target/linux/generic/backport-6.6/795-v6.3-05-r8152-reduce-the-control-transfer-of-rtl8152_get_ver.patch new file mode 100644 index 0000000000..565fbb3074 --- /dev/null +++ b/target/linux/generic/backport-6.6/795-v6.3-05-r8152-reduce-the-control-transfer-of-rtl8152_get_ver.patch @@ -0,0 +1,46 @@ +From 02767440e1dda9861a11ca1dbe0f19a760b1d5c2 Mon Sep 17 00:00:00 2001 +From: Hayes Wang +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 +Signed-off-by: Jakub Kicinski +--- + 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) { diff --git a/target/linux/generic/backport-6.6/795-v6.3-06-r8152-Add-__GFP_NOWARN-to-big-allocations.patch b/target/linux/generic/backport-6.6/795-v6.3-06-r8152-Add-__GFP_NOWARN-to-big-allocations.patch new file mode 100644 index 0000000000..cdabca36d2 --- /dev/null +++ b/target/linux/generic/backport-6.6/795-v6.3-06-r8152-Add-__GFP_NOWARN-to-big-allocations.patch @@ -0,0 +1,55 @@ +From 5cc33f139e11b893ff6dc60d8a0ae865a65521ac Mon Sep 17 00:00:00 2001 +From: Douglas Anderson +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 +Acked-by: Hayes Wang +Link: https://lore.kernel.org/r/20230406171411.1.I84dbef45786af440fd269b71e9436a96a8e7a152@changeid +Signed-off-by: Jakub Kicinski +--- + 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; + diff --git a/target/linux/generic/backport-6.6/795-v6.6-08-r8152-adjust-generic_ocp_write-function.patch b/target/linux/generic/backport-6.6/795-v6.6-08-r8152-adjust-generic_ocp_write-function.patch new file mode 100644 index 0000000000..3ba79d6cc6 --- /dev/null +++ b/target/linux/generic/backport-6.6/795-v6.6-08-r8152-adjust-generic_ocp_write-function.patch @@ -0,0 +1,70 @@ +From 57df0fb9d511f91202114813e90128d65c0589f0 Mon Sep 17 00:00:00 2001 +From: Hayes Wang +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 +Link: https://lore.kernel.org/r/20230726030808.9093-418-nic_swsd@realtek.com +Signed-off-by: Jakub Kicinski +--- + 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: diff --git a/target/linux/generic/backport-6.6/795-v6.6-09-r8152-set-bp-in-bulk.patch b/target/linux/generic/backport-6.6/795-v6.6-09-r8152-set-bp-in-bulk.patch new file mode 100644 index 0000000000..bc70c5af02 --- /dev/null +++ b/target/linux/generic/backport-6.6/795-v6.6-09-r8152-set-bp-in-bulk.patch @@ -0,0 +1,129 @@ +From e5c266a61186b462c388c53a3564c375e72f2244 Mon Sep 17 00:00:00 2001 +From: Hayes Wang +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 +Link: https://lore.kernel.org/r/20230726030808.9093-419-nic_swsd@realtek.com +Signed-off-by: Jakub Kicinski +--- + 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) diff --git a/target/linux/generic/backport-6.6/795-v6.6-10-eth-r8152-try-to-use-a-normal-budget.patch b/target/linux/generic/backport-6.6/795-v6.6-10-eth-r8152-try-to-use-a-normal-budget.patch new file mode 100644 index 0000000000..d7fdcdb2c6 --- /dev/null +++ b/target/linux/generic/backport-6.6/795-v6.6-10-eth-r8152-try-to-use-a-normal-budget.patch @@ -0,0 +1,39 @@ +From cf74eb5a5bc867258e7d0b0d1c3c4a60e1e3de2f Mon Sep 17 00:00:00 2001 +From: Jakub Kicinski +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 +Link: https://lore.kernel.org/all/0bfd445a-81f7-f702-08b0-bd5a72095e49@amd.com/ +Acked-by: Hayes Wang +Link: https://lore.kernel.org/r/20230814153521.2697982-1-kuba@kernel.org +Signed-off-by: Jakub Kicinski +--- + 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) { diff --git a/target/linux/generic/backport-6.6/795-v6.6-13-r8152-Block-future-register-access-if-register-acces.patch b/target/linux/generic/backport-6.6/795-v6.6-13-r8152-Block-future-register-access-if-register-acces.patch new file mode 100644 index 0000000000..8901767be5 --- /dev/null +++ b/target/linux/generic/backport-6.6/795-v6.6-13-r8152-Block-future-register-access-if-register-acces.patch @@ -0,0 +1,398 @@ +From d9962b0d42029bcb40fe3c38bce06d1870fa4df4 Mon Sep 17 00:00:00 2001 +From: Douglas Anderson +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 +Reviewed-by: Grant Grundler +Signed-off-by: David S. Miller +--- + 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); diff --git a/target/linux/generic/backport-6.6/795-v6.6-14-r8152-break-the-loop-when-the-budget-is-exhausted.patch b/target/linux/generic/backport-6.6/795-v6.6-14-r8152-break-the-loop-when-the-budget-is-exhausted.patch new file mode 100644 index 0000000000..7bbd1be820 --- /dev/null +++ b/target/linux/generic/backport-6.6/795-v6.6-14-r8152-break-the-loop-when-the-budget-is-exhausted.patch @@ -0,0 +1,83 @@ +From 66eee612a1ba39f9a76a9ace4a34d012044767fb Mon Sep 17 00:00:00 2001 +From: Hayes Wang +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 +Link: https://lore.kernel.org/r/20230926111714.9448-433-nic_swsd@realtek.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + 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); + } + diff --git a/target/linux/generic/backport-6.6/795-v6.6-15-net-usb-cdc_ether-add-u-blox-0x1313-composition.patch b/target/linux/generic/backport-6.6/795-v6.6-15-net-usb-cdc_ether-add-u-blox-0x1313-composition.patch new file mode 100644 index 0000000000..c858152067 --- /dev/null +++ b/target/linux/generic/backport-6.6/795-v6.6-15-net-usb-cdc_ether-add-u-blox-0x1313-composition.patch @@ -0,0 +1,47 @@ +From 1b0fce8c8e69485e49a7d34aac3d4c2a2aa15d62 Mon Sep 17 00:00:00 2001 +From: Davide Tronchin +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 +Reviewed-by: Simon Horman +Signed-off-by: David S. Miller +--- + 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, diff --git a/target/linux/generic/backport-6.6/795-v6.7-16-r8152-use-napi_gro_frags.patch b/target/linux/generic/backport-6.6/795-v6.7-16-r8152-use-napi_gro_frags.patch new file mode 100644 index 0000000000..3c9680a279 --- /dev/null +++ b/target/linux/generic/backport-6.6/795-v6.7-16-r8152-use-napi_gro_frags.patch @@ -0,0 +1,122 @@ +From 788d30daa8f97f06166b6a63f0e51f2a4c2f036a Mon Sep 17 00:00:00 2001 +From: Hayes Wang +Date: Tue, 26 Sep 2023 19:17:14 +0800 +Subject: [PATCH] r8152: use napi_gro_frags + +Use napi_gro_frags() for the skb of fragments when the work_done is less +than budget. + +Signed-off-by: Hayes Wang +Link: https://lore.kernel.org/r/20230926111714.9448-434-nic_swsd@realtek.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/usb/r8152.c | 67 ++++++++++++++++++++++++++++++----------- + 1 file changed, 50 insertions(+), 17 deletions(-) + +--- a/drivers/net/usb/r8152.c ++++ b/drivers/net/usb/r8152.c +@@ -2583,8 +2583,9 @@ static int rx_bottom(struct r8152 *tp, i + while (urb->actual_length > len_used) { + struct net_device *netdev = tp->netdev; + struct net_device_stats *stats = &netdev->stats; +- unsigned int pkt_len, rx_frag_head_sz; ++ unsigned int pkt_len, rx_frag_head_sz, len; + struct sk_buff *skb; ++ bool use_frags; + + WARN_ON_ONCE(skb_queue_len(&tp->rx_queue) >= 1000); + +@@ -2597,45 +2598,77 @@ static int rx_bottom(struct r8152 *tp, i + break; + + pkt_len -= ETH_FCS_LEN; ++ len = pkt_len; + rx_data += sizeof(struct rx_desc); + +- if (!agg_free || tp->rx_copybreak > pkt_len) +- rx_frag_head_sz = pkt_len; ++ if (!agg_free || tp->rx_copybreak > len) ++ use_frags = false; + else +- rx_frag_head_sz = tp->rx_copybreak; ++ use_frags = true; ++ ++ if (use_frags) { ++ /* If the budget is exhausted, the packet ++ * would be queued in the driver. That is, ++ * napi_gro_frags() wouldn't be called, so ++ * we couldn't use napi_get_frags(). ++ */ ++ if (work_done >= budget) { ++ rx_frag_head_sz = tp->rx_copybreak; ++ skb = napi_alloc_skb(napi, ++ rx_frag_head_sz); ++ } else { ++ rx_frag_head_sz = 0; ++ skb = napi_get_frags(napi); ++ } ++ } else { ++ rx_frag_head_sz = 0; ++ skb = napi_alloc_skb(napi, len); ++ } + +- skb = napi_alloc_skb(napi, rx_frag_head_sz); + if (!skb) { + stats->rx_dropped++; + goto find_next_rx; + } + + skb->ip_summed = r8152_rx_csum(tp, rx_desc); +- memcpy(skb->data, rx_data, rx_frag_head_sz); +- skb_put(skb, rx_frag_head_sz); +- pkt_len -= rx_frag_head_sz; +- rx_data += rx_frag_head_sz; +- if (pkt_len) { ++ rtl_rx_vlan_tag(rx_desc, skb); ++ ++ if (use_frags) { ++ if (rx_frag_head_sz) { ++ memcpy(skb->data, rx_data, ++ rx_frag_head_sz); ++ skb_put(skb, rx_frag_head_sz); ++ len -= rx_frag_head_sz; ++ rx_data += rx_frag_head_sz; ++ skb->protocol = eth_type_trans(skb, ++ netdev); ++ } ++ + skb_add_rx_frag(skb, 0, agg->page, + agg_offset(agg, rx_data), +- pkt_len, +- SKB_DATA_ALIGN(pkt_len)); ++ len, SKB_DATA_ALIGN(len)); + get_page(agg->page); ++ } else { ++ memcpy(skb->data, rx_data, len); ++ skb_put(skb, len); ++ skb->protocol = eth_type_trans(skb, netdev); + } + +- skb->protocol = eth_type_trans(skb, netdev); +- rtl_rx_vlan_tag(rx_desc, skb); + if (work_done < budget) { ++ if (use_frags) ++ napi_gro_frags(napi); ++ else ++ napi_gro_receive(napi, skb); ++ + work_done++; + stats->rx_packets++; +- stats->rx_bytes += skb->len; +- napi_gro_receive(napi, skb); ++ stats->rx_bytes += pkt_len; + } else { + __skb_queue_tail(&tp->rx_queue, skb); + } + + find_next_rx: +- rx_data = rx_agg_align(rx_data + pkt_len + ETH_FCS_LEN); ++ rx_data = rx_agg_align(rx_data + len + ETH_FCS_LEN); + rx_desc = (struct rx_desc *)rx_data; + len_used = agg_offset(agg, rx_data); + len_used += sizeof(struct rx_desc); diff --git a/target/linux/generic/backport-6.6/800-v6.3-leds-Move-led_init_default_state_get-to-the-global-h.patch b/target/linux/generic/backport-6.6/800-v6.3-leds-Move-led_init_default_state_get-to-the-global-h.patch new file mode 100644 index 0000000000..592111fb95 --- /dev/null +++ b/target/linux/generic/backport-6.6/800-v6.3-leds-Move-led_init_default_state_get-to-the-global-h.patch @@ -0,0 +1,39 @@ +From 156a5bb89ca6f3edd2be0bfd0de15e575442927e Mon Sep 17 00:00:00 2001 +From: Andy Shevchenko +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 +Signed-off-by: Lee Jones +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; + }; diff --git a/target/linux/generic/backport-6.6/801-v6.4-01-net-dsa-qca8k-move-qca8k_port_to_phy-to-header.patch b/target/linux/generic/backport-6.6/801-v6.4-01-net-dsa-qca8k-move-qca8k_port_to_phy-to-header.patch new file mode 100644 index 0000000000..f6a025fa12 --- /dev/null +++ b/target/linux/generic/backport-6.6/801-v6.4-01-net-dsa-qca8k-move-qca8k_port_to_phy-to-header.patch @@ -0,0 +1,67 @@ +From 3e8b4d6277fd19d98c817576954dd6a4ff3caa2b Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Reviewed-by: Andrew Lunn +Reviewed-by: Michal Kubiak +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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; diff --git a/target/linux/generic/backport-6.6/801-v6.4-02-net-dsa-qca8k-add-LEDs-basic-support.patch b/target/linux/generic/backport-6.6/801-v6.4-02-net-dsa-qca8k-add-LEDs-basic-support.patch new file mode 100644 index 0000000000..409fe9c7a1 --- /dev/null +++ b/target/linux/generic/backport-6.6/801-v6.4-02-net-dsa-qca8k-add-LEDs-basic-support.patch @@ -0,0 +1,435 @@ +From 1e264f9d2918b5737023c44a23ae04def1095210 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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 + + #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 ++#include ++ ++#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 + #include + #include ++#include + #include + + #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 */ diff --git a/target/linux/generic/backport-6.6/801-v6.4-03-net-dsa-qca8k-add-LEDs-blink_set-support.patch b/target/linux/generic/backport-6.6/801-v6.4-03-net-dsa-qca8k-add-LEDs-blink_set-support.patch new file mode 100644 index 0000000000..231c4156df --- /dev/null +++ b/target/linux/generic/backport-6.6/801-v6.4-03-net-dsa-qca8k-add-LEDs-blink_set-support.patch @@ -0,0 +1,74 @@ +From 91acadcc6e599dfc62717abcdad58a459cfb1684 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Reviewed-by: Andrew Lunn +Acked-by: Pavel Machek +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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; diff --git a/target/linux/generic/backport-6.6/801-v6.4-04-leds-Provide-stubs-for-when-CLASS_LED-NEW_LEDS-are-d.patch b/target/linux/generic/backport-6.6/801-v6.4-04-leds-Provide-stubs-for-when-CLASS_LED-NEW_LEDS-are-d.patch new file mode 100644 index 0000000000..bc905b4468 --- /dev/null +++ b/target/linux/generic/backport-6.6/801-v6.4-04-leds-Provide-stubs-for-when-CLASS_LED-NEW_LEDS-are-d.patch @@ -0,0 +1,59 @@ +From e5029edd53937a29801ef507cee12e657ff31ea9 Mon Sep 17 00:00:00 2001 +From: Andrew Lunn +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 +Signed-off-by: Christian Marangi +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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) diff --git a/target/linux/generic/backport-6.6/801-v6.4-05-net-phy-Add-a-binding-for-PHY-LEDs.patch b/target/linux/generic/backport-6.6/801-v6.4-05-net-phy-Add-a-binding-for-PHY-LEDs.patch new file mode 100644 index 0000000000..0d2c0bcd83 --- /dev/null +++ b/target/linux/generic/backport-6.6/801-v6.4-05-net-phy-Add-a-binding-for-PHY-LEDs.patch @@ -0,0 +1,191 @@ +From 01e5b728e9e43ae444e0369695a5f72209906464 Mon Sep 17 00:00:00 2001 +From: Andrew Lunn +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 +Signed-off-by: Christian Marangi +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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 + #include + #include ++#include + #include + #include + #include + #include ++#include + #include + #include + #include +@@ -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 + #include + #include ++#include + #include + #include + #include +@@ -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 diff --git a/target/linux/generic/backport-6.6/801-v6.4-06-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch b/target/linux/generic/backport-6.6/801-v6.4-06-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch new file mode 100644 index 0000000000..4873c40a77 --- /dev/null +++ b/target/linux/generic/backport-6.6/801-v6.4-06-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch @@ -0,0 +1,97 @@ +From 684818189b04b095b34964ed4a3ea5249a840eab Mon Sep 17 00:00:00 2001 +From: Andrew Lunn +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 +Signed-off-by: Christian Marangi +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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) diff --git a/target/linux/generic/backport-6.6/801-v6.4-07-net-phy-marvell-Add-software-control-of-the-LEDs.patch b/target/linux/generic/backport-6.6/801-v6.4-07-net-phy-marvell-Add-software-control-of-the-LEDs.patch new file mode 100644 index 0000000000..5114c0e6da --- /dev/null +++ b/target/linux/generic/backport-6.6/801-v6.4-07-net-phy-marvell-Add-software-control-of-the-LEDs.patch @@ -0,0 +1,112 @@ +From 2d3960e58ef7c83fe1dbf952f056b9e906cb6df8 Mon Sep 17 00:00:00 2001 +From: Andrew Lunn +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 +Signed-off-by: Christian Marangi +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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, + }, + }; + diff --git a/target/linux/generic/backport-6.6/801-v6.4-08-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch b/target/linux/generic/backport-6.6/801-v6.4-08-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch new file mode 100644 index 0000000000..00bdcc5468 --- /dev/null +++ b/target/linux/generic/backport-6.6/801-v6.4-08-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch @@ -0,0 +1,73 @@ +From 4e901018432e38eab35d2a352661ce4727795be1 Mon Sep 17 00:00:00 2001 +From: Andrew Lunn +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 +Signed-off-by: Christian Marangi +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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) diff --git a/target/linux/generic/backport-6.6/801-v6.4-09-net-phy-marvell-Implement-led_blink_set.patch b/target/linux/generic/backport-6.6/801-v6.4-09-net-phy-marvell-Implement-led_blink_set.patch new file mode 100644 index 0000000000..8d5081a43c --- /dev/null +++ b/target/linux/generic/backport-6.6/801-v6.4-09-net-phy-marvell-Implement-led_blink_set.patch @@ -0,0 +1,104 @@ +From ea9e86485decb2ac1750005bd96c166c9b780406 Mon Sep 17 00:00:00 2001 +From: Andrew Lunn +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 +Signed-off-by: Christian Marangi +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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, + }, + }; + diff --git a/target/linux/generic/backport-6.6/802-v6.4-net-phy-marvell-Fix-inconsistent-indenting-in-led_bl.patch b/target/linux/generic/backport-6.6/802-v6.4-net-phy-marvell-Fix-inconsistent-indenting-in-led_bl.patch new file mode 100644 index 0000000000..56df3f095e --- /dev/null +++ b/target/linux/generic/backport-6.6/802-v6.4-net-phy-marvell-Fix-inconsistent-indenting-in-led_bl.patch @@ -0,0 +1,38 @@ +From 4774ad841bef97cc51df90195338c5b2573dd4cb Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +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 +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20230423172800.3470-1-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + 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; diff --git a/target/linux/generic/backport-6.6/803-v6.5-02-leds-trigger-netdev-Drop-NETDEV_LED_MODE_LINKUP-from.patch b/target/linux/generic/backport-6.6/803-v6.5-02-leds-trigger-netdev-Drop-NETDEV_LED_MODE_LINKUP-from.patch new file mode 100644 index 0000000000..3170c26058 --- /dev/null +++ b/target/linux/generic/backport-6.6/803-v6.5-02-leds-trigger-netdev-Drop-NETDEV_LED_MODE_LINKUP-from.patch @@ -0,0 +1,87 @@ +From e2f24cb1b5daf9a4f6f3ba574c1fa74aab9807a4 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Reviewed-by: Andrew Lunn +Signed-off-by: Lee Jones +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; + } + diff --git a/target/linux/generic/backport-6.6/803-v6.5-03-leds-trigger-netdev-Rename-add-namespace-to-netdev-t.patch b/target/linux/generic/backport-6.6/803-v6.5-03-leds-trigger-netdev-Rename-add-namespace-to-netdev-t.patch new file mode 100644 index 0000000000..19cc1d7c9e --- /dev/null +++ b/target/linux/generic/backport-6.6/803-v6.5-03-leds-trigger-netdev-Rename-add-namespace-to-netdev-t.patch @@ -0,0 +1,149 @@ +From bdec9cb83936e0ac4cb87fed5b49fad0175f7dec Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Reviewed-by: Andrew Lunn +Signed-off-by: Lee Jones +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) */ diff --git a/target/linux/generic/backport-6.6/803-v6.5-04-leds-trigger-netdev-Convert-device-attr-to-macro.patch b/target/linux/generic/backport-6.6/803-v6.5-04-leds-trigger-netdev-Convert-device-attr-to-macro.patch new file mode 100644 index 0000000000..3b45951f57 --- /dev/null +++ b/target/linux/generic/backport-6.6/803-v6.5-04-leds-trigger-netdev-Convert-device-attr-to-macro.patch @@ -0,0 +1,82 @@ +From 164b67d53476a9d114be85c885bd31f783835be4 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Reviewed-by: Andrew Lunn +Signed-off-by: Lee Jones +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) diff --git a/target/linux/generic/backport-6.6/803-v6.5-05-leds-trigger-netdev-Use-mutex-instead-of-spinlocks.patch b/target/linux/generic/backport-6.6/803-v6.5-05-leds-trigger-netdev-Use-mutex-instead-of-spinlocks.patch new file mode 100644 index 0000000000..180bee9612 --- /dev/null +++ b/target/linux/generic/backport-6.6/803-v6.5-05-leds-trigger-netdev-Use-mutex-instead-of-spinlocks.patch @@ -0,0 +1,106 @@ +From d1b9e1391ab2dc80e9db87fe8b2de015c651e4c9 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Reviewed-by: Andrew Lunn +Signed-off-by: Lee Jones +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 + #include + #include +-#include ++#include + #include + #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; diff --git a/target/linux/generic/backport-6.6/804-v6.5-01-leds-add-APIs-for-LEDs-hw-control.patch b/target/linux/generic/backport-6.6/804-v6.5-01-leds-add-APIs-for-LEDs-hw-control.patch new file mode 100644 index 0000000000..ac18611733 --- /dev/null +++ b/target/linux/generic/backport-6.6/804-v6.5-01-leds-add-APIs-for-LEDs-hw-control.patch @@ -0,0 +1,74 @@ +From ed554d3f945179c5b159bddfad7be34b403fe11a Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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 diff --git a/target/linux/generic/backport-6.6/804-v6.5-02-leds-add-API-to-get-attached-device-for-LED-hw-contr.patch b/target/linux/generic/backport-6.6/804-v6.5-02-leds-add-API-to-get-attached-device-for-LED-hw-contr.patch new file mode 100644 index 0000000000..1a221727a0 --- /dev/null +++ b/target/linux/generic/backport-6.6/804-v6.5-02-leds-add-API-to-get-attached-device-for-LED-hw-contr.patch @@ -0,0 +1,37 @@ +From 052c38eb17e866c5b4cd43924e7a5e20167b55c0 Mon Sep 17 00:00:00 2001 +From: Andrew Lunn +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 +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + 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 diff --git a/target/linux/generic/backport-6.6/804-v6.5-03-Documentation-leds-leds-class-Document-new-Hardware-.patch b/target/linux/generic/backport-6.6/804-v6.5-03-Documentation-leds-leds-class-Document-new-Hardware-.patch new file mode 100644 index 0000000000..af9fb7fdc3 --- /dev/null +++ b/target/linux/generic/backport-6.6/804-v6.5-03-Documentation-leds-leds-class-Document-new-Hardware-.patch @@ -0,0 +1,113 @@ +From 8aa2fd7b66980ecd2e45e95af61cf7eafede1211 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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 + ============ diff --git a/target/linux/generic/backport-6.6/804-v6.5-04-leds-trigger-netdev-refactor-code-setting-device-nam.patch b/target/linux/generic/backport-6.6/804-v6.5-04-leds-trigger-netdev-refactor-code-setting-device-nam.patch new file mode 100644 index 0000000000..3c804c0b41 --- /dev/null +++ b/target/linux/generic/backport-6.6/804-v6.5-04-leds-trigger-netdev-refactor-code-setting-device-nam.patch @@ -0,0 +1,69 @@ +From 28a6a2ef18ad840a390d519840c303b03040961c Mon Sep 17 00:00:00 2001 +From: Andrew Lunn +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 +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + 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; + } + diff --git a/target/linux/generic/backport-6.6/804-v6.5-05-leds-trigger-netdev-introduce-check-for-possible-hw-.patch b/target/linux/generic/backport-6.6/804-v6.5-05-leds-trigger-netdev-introduce-check-for-possible-hw-.patch new file mode 100644 index 0000000000..284b519482 --- /dev/null +++ b/target/linux/generic/backport-6.6/804-v6.5-05-leds-trigger-netdev-introduce-check-for-possible-hw-.patch @@ -0,0 +1,54 @@ +From 4fd1b6d47a7a38e81fdc6f8be2ccd4216b3f93db Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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; diff --git a/target/linux/generic/backport-6.6/804-v6.5-06-leds-trigger-netdev-add-basic-check-for-hw-control-s.patch b/target/linux/generic/backport-6.6/804-v6.5-06-leds-trigger-netdev-add-basic-check-for-hw-control-s.patch new file mode 100644 index 0000000000..09759bc623 --- /dev/null +++ b/target/linux/generic/backport-6.6/804-v6.5-06-leds-trigger-netdev-add-basic-check-for-hw-control-s.patch @@ -0,0 +1,42 @@ +From 6352f25f9fadba59d5df2ba7139495759ccc81d5 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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; + } + diff --git a/target/linux/generic/backport-6.6/804-v6.5-07-leds-trigger-netdev-reject-interval-store-for-hw_con.patch b/target/linux/generic/backport-6.6/804-v6.5-07-leds-trigger-netdev-reject-interval-store-for-hw_con.patch new file mode 100644 index 0000000000..6634906800 --- /dev/null +++ b/target/linux/generic/backport-6.6/804-v6.5-07-leds-trigger-netdev-reject-interval-store-for-hw_con.patch @@ -0,0 +1,28 @@ +From c84c80c7388f887b10dafd70fc55bc6c5fe9fa5a Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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; diff --git a/target/linux/generic/backport-6.6/804-v6.5-08-leds-trigger-netdev-add-support-for-LED-hw-control.patch b/target/linux/generic/backport-6.6/804-v6.5-08-leds-trigger-netdev-add-support-for-LED-hw-control.patch new file mode 100644 index 0000000000..52faa4809b --- /dev/null +++ b/target/linux/generic/backport-6.6/804-v6.5-08-leds-trigger-netdev-add-support-for-LED-hw-control.patch @@ -0,0 +1,107 @@ +From 7c145a34ba6e380616af93262fcab9fc7261d851 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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 + #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); diff --git a/target/linux/generic/backport-6.6/804-v6.5-09-leds-trigger-netdev-validate-configured-netdev.patch b/target/linux/generic/backport-6.6/804-v6.5-09-leds-trigger-netdev-validate-configured-netdev.patch new file mode 100644 index 0000000000..c129ffa4f5 --- /dev/null +++ b/target/linux/generic/backport-6.6/804-v6.5-09-leds-trigger-netdev-validate-configured-netdev.patch @@ -0,0 +1,58 @@ +From 33ec0b53befff2c0a7f3aa19ff08556d60585d6b Mon Sep 17 00:00:00 2001 +From: Andrew Lunn +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 +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + 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 */ diff --git a/target/linux/generic/backport-6.6/804-v6.5-10-leds-trigger-netdev-init-mode-if-hw-control-already-.patch b/target/linux/generic/backport-6.6/804-v6.5-10-leds-trigger-netdev-init-mode-if-hw-control-already-.patch new file mode 100644 index 0000000000..e20594c99a --- /dev/null +++ b/target/linux/generic/backport-6.6/804-v6.5-10-leds-trigger-netdev-init-mode-if-hw-control-already-.patch @@ -0,0 +1,53 @@ +From 0316cc5629d15880dd3f097d221c55ca648bcd61 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Signed-off-by: Andrew Lunn +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + 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); diff --git a/target/linux/generic/backport-6.6/804-v6.5-11-leds-trigger-netdev-expose-netdev-trigger-modes-in-l.patch b/target/linux/generic/backport-6.6/804-v6.5-11-leds-trigger-netdev-expose-netdev-trigger-modes-in-l.patch new file mode 100644 index 0000000000..70aed850d1 --- /dev/null +++ b/target/linux/generic/backport-6.6/804-v6.5-11-leds-trigger-netdev-expose-netdev-trigger-modes-in-l.patch @@ -0,0 +1,54 @@ +From 947acacab5ea151291b861cdfbde16ff5cf1b08c Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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); diff --git a/target/linux/generic/backport-6.6/804-v6.5-12-net-dsa-qca8k-implement-hw_control-ops.patch b/target/linux/generic/backport-6.6/804-v6.5-12-net-dsa-qca8k-implement-hw_control-ops.patch new file mode 100644 index 0000000000..ad76d89b7b --- /dev/null +++ b/target/linux/generic/backport-6.6/804-v6.5-12-net-dsa-qca8k-implement-hw_control-ops.patch @@ -0,0 +1,200 @@ +From e0256648c831af13cbfe4a1787327fcec01c2807 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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; diff --git a/target/linux/generic/backport-6.6/804-v6.5-13-net-dsa-qca8k-add-op-to-get-ports-netdev.patch b/target/linux/generic/backport-6.6/804-v6.5-13-net-dsa-qca8k-add-op-to-get-ports-netdev.patch new file mode 100644 index 0000000000..feb6b9e1e4 --- /dev/null +++ b/target/linux/generic/backport-6.6/804-v6.5-13-net-dsa-qca8k-add-op-to-get-ports-netdev.patch @@ -0,0 +1,70 @@ +From 4f53c27f772e27e4cf4e5507d6f4d5980002cb6a Mon Sep 17 00:00:00 2001 +From: Andrew Lunn +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 +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + 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; diff --git a/target/linux/generic/backport-6.6/805-v6.5-01-leds-trigger-netdev-add-additional-specific-link-spe.patch b/target/linux/generic/backport-6.6/805-v6.5-01-leds-trigger-netdev-add-additional-specific-link-spe.patch new file mode 100644 index 0000000000..1c564b3897 --- /dev/null +++ b/target/linux/generic/backport-6.6/805-v6.5-01-leds-trigger-netdev-add-additional-specific-link-spe.patch @@ -0,0 +1,242 @@ +From d5e01266e7f5fa12400d4c8aa4e86fe89dcc61e9 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Reviewed-by: Andrew Lunn +Acked-by: Lee Jones +Signed-off-by: Jakub Kicinski +--- + 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 + #include + #include ++#include + #include + #include + #include +@@ -21,6 +22,7 @@ + #include + #include + #include ++#include + #include + #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, + diff --git a/target/linux/generic/backport-6.6/805-v6.5-02-leds-trigger-netdev-add-additional-specific-link-dup.patch b/target/linux/generic/backport-6.6/805-v6.5-02-leds-trigger-netdev-add-additional-specific-link-dup.patch new file mode 100644 index 0000000000..a5ab461828 --- /dev/null +++ b/target/linux/generic/backport-6.6/805-v6.5-02-leds-trigger-netdev-add-additional-specific-link-dup.patch @@ -0,0 +1,138 @@ +From f22f95b9ff1551c9bab13104131929f33d51f23f Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Reviewed-by: Andrew Lunn +Acked-by: Lee Jones +Signed-off-by: Jakub Kicinski +--- + 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, + diff --git a/target/linux/generic/backport-6.6/805-v6.5-03-leds-trigger-netdev-expose-hw_control-status-via-sys.patch b/target/linux/generic/backport-6.6/805-v6.5-03-leds-trigger-netdev-expose-hw_control-status-via-sys.patch new file mode 100644 index 0000000000..67528cafe0 --- /dev/null +++ b/target/linux/generic/backport-6.6/805-v6.5-03-leds-trigger-netdev-expose-hw_control-status-via-sys.patch @@ -0,0 +1,45 @@ +From b655892ffd6d89b0c7407e099c40dbde82ee3f03 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Reviewed-by: Andrew Lunn +Reviewed-by: Kalesh AP +Acked-by: Lee Jones +Signed-off-by: Jakub Kicinski +--- + 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); diff --git a/target/linux/generic/backport-6.6/806-v6.5-net-dsa-qca8k-add-support-for-additional-modes-for-n.patch b/target/linux/generic/backport-6.6/806-v6.5-net-dsa-qca8k-add-support-for-additional-modes-for-n.patch new file mode 100644 index 0000000000..ac322e1939 --- /dev/null +++ b/target/linux/generic/backport-6.6/806-v6.5-net-dsa-qca8k-add-support-for-additional-modes-for-n.patch @@ -0,0 +1,64 @@ +From 2555f35a4f428a9bfdf09aa0459dbfdf59a24a9a Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Reviewed-by: Andrew Lunn +Reviewed-by: Florian Fainelli +Link: https://lore.kernel.org/r/20230621095409.25859-1-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + 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; + } diff --git a/target/linux/generic/backport-6.6/807-v6.5-01-net-dsa-mv88e6xxx-pass-directly-chip-structure-to-mv.patch b/target/linux/generic/backport-6.6/807-v6.5-01-net-dsa-mv88e6xxx-pass-directly-chip-structure-to-mv.patch new file mode 100644 index 0000000000..58777cd280 --- /dev/null +++ b/target/linux/generic/backport-6.6/807-v6.5-01-net-dsa-mv88e6xxx-pass-directly-chip-structure-to-mv.patch @@ -0,0 +1,64 @@ +From 4f86eb098e18fd0f032877dfa1a7e8c1503ca409 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Alexis=20Lothor=C3=A9?= +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é +Reviewed-by: Russell King (Oracle) +Reviewed-by: Florian Fainelli +Signed-off-by: Jakub Kicinski +--- + 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. diff --git a/target/linux/generic/backport-6.6/807-v6.5-02-net-dsa-mv88e6xxx-use-mv88e6xxx_phy_is_internal-in-m.patch b/target/linux/generic/backport-6.6/807-v6.5-02-net-dsa-mv88e6xxx-use-mv88e6xxx_phy_is_internal-in-m.patch new file mode 100644 index 0000000000..75a21a931d --- /dev/null +++ b/target/linux/generic/backport-6.6/807-v6.5-02-net-dsa-mv88e6xxx-use-mv88e6xxx_phy_is_internal-in-m.patch @@ -0,0 +1,31 @@ +From 73cbfad9296eed004992806e056db5b48583ca41 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Alexis=20Lothor=C3=A9?= +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é +Reviewed-by: Russell King (Oracle) +Reviewed-by: Florian Fainelli +Signed-off-by: Jakub Kicinski +--- + 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) { diff --git a/target/linux/generic/backport-6.6/807-v6.5-03-net-dsa-mv88e6xxx-add-field-to-specify-internal-phys.patch b/target/linux/generic/backport-6.6/807-v6.5-03-net-dsa-mv88e6xxx-add-field-to-specify-internal-phys.patch new file mode 100644 index 0000000000..e24dca819b --- /dev/null +++ b/target/linux/generic/backport-6.6/807-v6.5-03-net-dsa-mv88e6xxx-add-field-to-specify-internal-phys.patch @@ -0,0 +1,69 @@ +From 1414d30660d201f515a9d877571ceea9ca190b6a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Alexis=20Lothor=C3=A9?= +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é +Reviewed-by: Andrew Lunn +Signed-off-by: Jakub Kicinski +--- + 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; diff --git a/target/linux/generic/backport-6.6/807-v6.5-04-net-dsa-mv88e6xxx-fix-88E6393X-family-internal-phys-.patch b/target/linux/generic/backport-6.6/807-v6.5-04-net-dsa-mv88e6xxx-fix-88E6393X-family-internal-phys-.patch new file mode 100644 index 0000000000..12ea3ebda0 --- /dev/null +++ b/target/linux/generic/backport-6.6/807-v6.5-04-net-dsa-mv88e6xxx-fix-88E6393X-family-internal-phys-.patch @@ -0,0 +1,52 @@ +From eb8c75f82a6711387f3b9e03e28923f3e75a761b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Alexis=20Lothor=C3=A9?= +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é +Reviewed-by: Andrew Lunn +Reviewed-by: Florian Fainelli +Signed-off-by: Jakub Kicinski +--- + 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, diff --git a/target/linux/generic/backport-6.6/807-v6.5-05-net-dsa-mv88e6xxx-pass-mv88e6xxx_chip-structure-to-p.patch b/target/linux/generic/backport-6.6/807-v6.5-05-net-dsa-mv88e6xxx-pass-mv88e6xxx_chip-structure-to-p.patch new file mode 100644 index 0000000000..72dfcee82c --- /dev/null +++ b/target/linux/generic/backport-6.6/807-v6.5-05-net-dsa-mv88e6xxx-pass-mv88e6xxx_chip-structure-to-p.patch @@ -0,0 +1,110 @@ +From cef945452c8468efce75ba0dc8420510a5b84af9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Alexis=20Lothor=C3=A9?= +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é +Reviewed-by: Florian Fainelli +Signed-off-by: Jakub Kicinski +--- + 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); + diff --git a/target/linux/generic/backport-6.6/807-v6.5-06-net-dsa-mv88e6xxx-enable-support-for-88E6361-switch.patch b/target/linux/generic/backport-6.6/807-v6.5-06-net-dsa-mv88e6xxx-enable-support-for-88E6361-switch.patch new file mode 100644 index 0000000000..dc6d5497f2 --- /dev/null +++ b/target/linux/generic/backport-6.6/807-v6.5-06-net-dsa-mv88e6xxx-enable-support-for-88E6361-switch.patch @@ -0,0 +1,153 @@ +From 23680321789863bab2d60af507858ce50ff9f56a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Alexis=20Lothor=C3=A9?= +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é +Reviewed-by: Andrew Lunn +Signed-off-by: Jakub Kicinski +--- + 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 diff --git a/target/linux/generic/backport-6.6/808-v6.2-0001-nvmem-stm32-move-STM32MP15_BSEC_NUM_LOWER-in-config.patch b/target/linux/generic/backport-6.6/808-v6.2-0001-nvmem-stm32-move-STM32MP15_BSEC_NUM_LOWER-in-config.patch new file mode 100644 index 0000000000..33759632eb --- /dev/null +++ b/target/linux/generic/backport-6.6/808-v6.2-0001-nvmem-stm32-move-STM32MP15_BSEC_NUM_LOWER-in-config.patch @@ -0,0 +1,82 @@ +From fbfc4ca465a1f8d81bf2d67d95bf7fc67c3cf0c2 Mon Sep 17 00:00:00 2001 +From: Patrick Delaunay +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20221118063932.6418-2-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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[] = { diff --git a/target/linux/generic/backport-6.6/808-v6.2-0002-nvmem-stm32-add-warning-when-upper-OTPs-are-updated.patch b/target/linux/generic/backport-6.6/808-v6.2-0002-nvmem-stm32-add-warning-when-upper-OTPs-are-updated.patch new file mode 100644 index 0000000000..5791df2606 --- /dev/null +++ b/target/linux/generic/backport-6.6/808-v6.2-0002-nvmem-stm32-add-warning-when-upper-OTPs-are-updated.patch @@ -0,0 +1,34 @@ +From d61784e6410f3df2028e6eb91b06ffed37a660e0 Mon Sep 17 00:00:00 2001 +From: Patrick Delaunay +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20221118063932.6418-3-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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; + } + diff --git a/target/linux/generic/backport-6.6/808-v6.2-0003-nvmem-stm32-add-nvmem-type-attribute.patch b/target/linux/generic/backport-6.6/808-v6.2-0003-nvmem-stm32-add-nvmem-type-attribute.patch new file mode 100644 index 0000000000..b83ad69c6b --- /dev/null +++ b/target/linux/generic/backport-6.6/808-v6.2-0003-nvmem-stm32-add-nvmem-type-attribute.patch @@ -0,0 +1,26 @@ +From a3816a7d7c097c1da46aad5f5d1e229b607dce04 Mon Sep 17 00:00:00 2001 +From: Patrick Delaunay +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20221118063932.6418-4-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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; + diff --git a/target/linux/generic/backport-6.6/808-v6.2-0004-nvmem-stm32-fix-spelling-typo-in-comment.patch b/target/linux/generic/backport-6.6/808-v6.2-0004-nvmem-stm32-fix-spelling-typo-in-comment.patch new file mode 100644 index 0000000000..52ba1e65b5 --- /dev/null +++ b/target/linux/generic/backport-6.6/808-v6.2-0004-nvmem-stm32-fix-spelling-typo-in-comment.patch @@ -0,0 +1,27 @@ +From 06aac0e11960a7ddccc1888326b5906d017e0f24 Mon Sep 17 00:00:00 2001 +From: Jiangshan Yi +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 +Signed-off-by: Jiangshan Yi +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20221118063932.6418-6-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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 { diff --git a/target/linux/generic/backport-6.6/808-v6.2-0005-nvmem-Kconfig-Fix-spelling-mistake-controlls-control.patch b/target/linux/generic/backport-6.6/808-v6.2-0005-nvmem-Kconfig-Fix-spelling-mistake-controlls-control.patch new file mode 100644 index 0000000000..8f024f4c1a --- /dev/null +++ b/target/linux/generic/backport-6.6/808-v6.2-0005-nvmem-Kconfig-Fix-spelling-mistake-controlls-control.patch @@ -0,0 +1,27 @@ +From fb817c4ef63e8cfb6e77ae4a2875ae854c80708f Mon Sep 17 00:00:00 2001 +From: Colin Ian King +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20221118063932.6418-8-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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" diff --git a/target/linux/generic/backport-6.6/808-v6.2-0006-nvmem-u-boot-env-add-Broadcom-format-support.patch b/target/linux/generic/backport-6.6/808-v6.2-0006-nvmem-u-boot-env-add-Broadcom-format-support.patch new file mode 100644 index 0000000000..861386ad31 --- /dev/null +++ b/target/linux/generic/backport-6.6/808-v6.2-0006-nvmem-u-boot-env-add-Broadcom-format-support.patch @@ -0,0 +1,67 @@ +From ada84d07af6097b2addd18262668ce6cb9e15206 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20221118063932.6418-9-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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, }, + {}, + }; + diff --git a/target/linux/generic/backport-6.6/809-v6.3-0001-nvmem-core-remove-spurious-white-space.patch b/target/linux/generic/backport-6.6/809-v6.3-0001-nvmem-core-remove-spurious-white-space.patch new file mode 100644 index 0000000000..01400fd490 --- /dev/null +++ b/target/linux/generic/backport-6.6/809-v6.3-0001-nvmem-core-remove-spurious-white-space.patch @@ -0,0 +1,26 @@ +From 2e8dc541ae207349b51c65391be625ffe1f86e0c Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +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) +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230206134356.839737-8-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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); diff --git a/target/linux/generic/backport-6.6/809-v6.3-0002-nvmem-core-add-an-index-parameter-to-the-cell.patch b/target/linux/generic/backport-6.6/809-v6.3-0002-nvmem-core-add-an-index-parameter-to-the-cell.patch new file mode 100644 index 0000000000..454d3bf0ed --- /dev/null +++ b/target/linux/generic/backport-6.6/809-v6.3-0002-nvmem-core-add-an-index-parameter-to-the-cell.patch @@ -0,0 +1,180 @@ +From 5d8e6e6c10a3d37486d263b16ddc15991a7e4a88 Mon Sep 17 00:00:00 2001 +From: Michael Walle +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230206134356.839737-13-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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, diff --git a/target/linux/generic/backport-6.6/809-v6.3-0003-nvmem-core-move-struct-nvmem_cell_info-to-nvmem-prov.patch b/target/linux/generic/backport-6.6/809-v6.3-0003-nvmem-core-move-struct-nvmem_cell_info-to-nvmem-prov.patch new file mode 100644 index 0000000000..f3829b3e17 --- /dev/null +++ b/target/linux/generic/backport-6.6/809-v6.3-0003-nvmem-core-move-struct-nvmem_cell_info-to-nvmem-prov.patch @@ -0,0 +1,78 @@ +From fbd03d27776c6121a483921601418e3c8f0ff37e Mon Sep 17 00:00:00 2001 +From: Michael Walle +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230206134356.839737-14-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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 + + 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. diff --git a/target/linux/generic/backport-6.6/809-v6.3-0004-nvmem-core-drop-the-removal-of-the-cells-in-nvmem_ad.patch b/target/linux/generic/backport-6.6/809-v6.3-0004-nvmem-core-drop-the-removal-of-the-cells-in-nvmem_ad.patch new file mode 100644 index 0000000000..8f996eab34 --- /dev/null +++ b/target/linux/generic/backport-6.6/809-v6.3-0004-nvmem-core-drop-the-removal-of-the-cells-in-nvmem_ad.patch @@ -0,0 +1,65 @@ +From cc5bdd323dde6494623f3ffe3a5b887fa21cd375 Mon Sep 17 00:00:00 2001 +From: Michael Walle +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230206134356.839737-15-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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; + } + diff --git a/target/linux/generic/backport-6.6/809-v6.3-0005-nvmem-core-add-nvmem_add_one_cell.patch b/target/linux/generic/backport-6.6/809-v6.3-0005-nvmem-core-add-nvmem_add_one_cell.patch new file mode 100644 index 0000000000..711ce229b2 --- /dev/null +++ b/target/linux/generic/backport-6.6/809-v6.3-0005-nvmem-core-add-nvmem_add_one_cell.patch @@ -0,0 +1,122 @@ +From 2ded6830d376d5e7bf43d59f7f7fdf1a59abc676 Mon Sep 17 00:00:00 2001 +From: Michael Walle +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230206134356.839737-16-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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 */ diff --git a/target/linux/generic/backport-6.6/809-v6.3-0006-nvmem-core-use-nvmem_add_one_cell-in-nvmem_add_cells.patch b/target/linux/generic/backport-6.6/809-v6.3-0006-nvmem-core-use-nvmem_add_one_cell-in-nvmem_add_cells.patch new file mode 100644 index 0000000000..e1791e5c83 --- /dev/null +++ b/target/linux/generic/backport-6.6/809-v6.3-0006-nvmem-core-use-nvmem_add_one_cell-in-nvmem_add_cells.patch @@ -0,0 +1,93 @@ +From 50014d659617dc58780a5d31ceb76c82779a9d8b Mon Sep 17 00:00:00 2001 +From: Michael Walle +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230206134356.839737-17-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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; diff --git a/target/linux/generic/backport-6.6/809-v6.3-0007-nvmem-stm32-add-OP-TEE-support-for-STM32MP13x.patch b/target/linux/generic/backport-6.6/809-v6.3-0007-nvmem-stm32-add-OP-TEE-support-for-STM32MP13x.patch new file mode 100644 index 0000000000..172a78b76a --- /dev/null +++ b/target/linux/generic/backport-6.6/809-v6.3-0007-nvmem-stm32-add-OP-TEE-support-for-STM32MP13x.patch @@ -0,0 +1,562 @@ +From 6a0bc3522e746025e2d9a63ab2cb5d7062c2d39c Mon Sep 17 00:00:00 2001 +From: Patrick Delaunay +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 +Reviewed-by: Etienne Carriere +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230206134356.839737-18-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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 ++ ++#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 + #include + #include ++#include ++ ++#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); + diff --git a/target/linux/generic/backport-6.6/809-v6.3-0008-nvmem-stm32-detect-bsec-pta-presence-for-STM32MP15x.patch b/target/linux/generic/backport-6.6/809-v6.3-0008-nvmem-stm32-detect-bsec-pta-presence-for-STM32MP15x.patch new file mode 100644 index 0000000000..cea8e93858 --- /dev/null +++ b/target/linux/generic/backport-6.6/809-v6.3-0008-nvmem-stm32-detect-bsec-pta-presence-for-STM32MP15x.patch @@ -0,0 +1,85 @@ +From df2f34ef1d924125ffaf29dfdaf7cdbd3183c321 Mon Sep 17 00:00:00 2001 +From: Patrick Delaunay +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 +Reviewed-by: Etienne Carriere +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230206134356.839737-19-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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); diff --git a/target/linux/generic/backport-6.6/809-v6.3-0009-nvmem-rave-sp-eeprm-fix-kernel-doc-bad-line-warning.patch b/target/linux/generic/backport-6.6/809-v6.3-0009-nvmem-rave-sp-eeprm-fix-kernel-doc-bad-line-warning.patch new file mode 100644 index 0000000000..9d6275a737 --- /dev/null +++ b/target/linux/generic/backport-6.6/809-v6.3-0009-nvmem-rave-sp-eeprm-fix-kernel-doc-bad-line-warning.patch @@ -0,0 +1,32 @@ +From 3e5ac22aa564026e99defc3a8e02082521a5b231 Mon Sep 17 00:00:00 2001 +From: Randy Dunlap +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 +Cc: Srinivas Kandagatla +Cc: Andrey Vostrikov +Cc: Nikita Yushchenko +Cc: Andrey Smirnov +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230206134356.839737-20-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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 + */ diff --git a/target/linux/generic/backport-6.6/809-v6.3-0010-nvmem-qcom-spmi-sdam-register-at-device-init-time.patch b/target/linux/generic/backport-6.6/809-v6.3-0010-nvmem-qcom-spmi-sdam-register-at-device-init-time.patch new file mode 100644 index 0000000000..1ab9e609d3 --- /dev/null +++ b/target/linux/generic/backport-6.6/809-v6.3-0010-nvmem-qcom-spmi-sdam-register-at-device-init-time.patch @@ -0,0 +1,43 @@ +From eb7dda20f42a9137e9ee53d5ed3b743d49338cb5 Mon Sep 17 00:00:00 2001 +From: Johan Hovold +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 +Reviewed-by: Bjorn Andersson +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230206134356.839737-21-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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"); diff --git a/target/linux/generic/backport-6.6/809-v6.3-0011-nvmem-stm32-fix-OPTEE-dependency.patch b/target/linux/generic/backport-6.6/809-v6.3-0011-nvmem-stm32-fix-OPTEE-dependency.patch new file mode 100644 index 0000000000..dcf704c6ff --- /dev/null +++ b/target/linux/generic/backport-6.6/809-v6.3-0011-nvmem-stm32-fix-OPTEE-dependency.patch @@ -0,0 +1,46 @@ +From 1dc7e37bb0ec1c997fac82031332a38c7610352f Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230206134356.839737-23-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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. diff --git a/target/linux/generic/backport-6.6/810-v6.3-i915-Move-list_count-to-list.h-as-list_count_nodes-f.patch b/target/linux/generic/backport-6.6/810-v6.3-i915-Move-list_count-to-list.h-as-list_count_nodes-f.patch new file mode 100644 index 0000000000..60a90136c9 --- /dev/null +++ b/target/linux/generic/backport-6.6/810-v6.3-i915-Move-list_count-to-list.h-as-list_count_nodes-f.patch @@ -0,0 +1,75 @@ +From 4d70c74659d9746502b23d055dba03d1d28ec388 Mon Sep 17 00:00:00 2001 +From: Andy Shevchenko +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 +Acked-by: Jani Nikula +Signed-off-by: Andy Shevchenko +Link: https://lore.kernel.org/r/20221130134838.23805-1-andriy.shevchenko@linux.intel.com +Signed-off-by: Greg Kroah-Hartman +--- + 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. diff --git a/target/linux/generic/backport-6.6/811-v6.4-0001-nvmem-xilinx-zynqmp-make-modular.patch b/target/linux/generic/backport-6.6/811-v6.4-0001-nvmem-xilinx-zynqmp-make-modular.patch new file mode 100644 index 0000000000..8328e87c0a --- /dev/null +++ b/target/linux/generic/backport-6.6/811-v6.4-0001-nvmem-xilinx-zynqmp-make-modular.patch @@ -0,0 +1,35 @@ +From bcd1fe07def0f070eb5f31594620aaee6f81d31a Mon Sep 17 00:00:00 2001 +From: Nick Alcock +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 +Suggested-by: Michal Simek +Cc: Luis Chamberlain +Cc: linux-modules@vger.kernel.org +Cc: linux-kernel@vger.kernel.org +Cc: Hitomi Hasegawa +Cc: Srinivas Kandagatla +Cc: Michal Simek +Cc: linux-arm-kernel@lists.infradead.org +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230404172148.82422-4-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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 diff --git a/target/linux/generic/backport-6.6/811-v6.4-0002-nvmem-core-introduce-NVMEM-layouts.patch b/target/linux/generic/backport-6.6/811-v6.4-0002-nvmem-core-introduce-NVMEM-layouts.patch new file mode 100644 index 0000000000..94cd23c18a --- /dev/null +++ b/target/linux/generic/backport-6.6/811-v6.4-0002-nvmem-core-introduce-NVMEM-layouts.patch @@ -0,0 +1,387 @@ +From 266570f496b90dea8fda893c2cf7c28d63ae2bd9 Mon Sep 17 00:00:00 2001 +From: Michael Walle +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 +Signed-off-by: Miquel Raynal +Signed-off-by: Michael Walle +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230404172148.82422-14-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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" name will be assigned to the device if + * no name is specified in its configuration. In such case "" 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 */ diff --git a/target/linux/generic/backport-6.6/811-v6.4-0003-nvmem-core-handle-the-absence-of-expected-layouts.patch b/target/linux/generic/backport-6.6/811-v6.4-0003-nvmem-core-handle-the-absence-of-expected-layouts.patch new file mode 100644 index 0000000000..6fa7b6382d --- /dev/null +++ b/target/linux/generic/backport-6.6/811-v6.4-0003-nvmem-core-handle-the-absence-of-expected-layouts.patch @@ -0,0 +1,61 @@ +From 6468a6f45148fb5e95c86b4efebf63f9abcd2137 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Tested-by: Michael Walle +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230404172148.82422-15-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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: diff --git a/target/linux/generic/backport-6.6/811-v6.4-0004-nvmem-core-request-layout-modules-loading.patch b/target/linux/generic/backport-6.6/811-v6.4-0004-nvmem-core-request-layout-modules-loading.patch new file mode 100644 index 0000000000..b9341666f9 --- /dev/null +++ b/target/linux/generic/backport-6.6/811-v6.4-0004-nvmem-core-request-layout-modules-loading.patch @@ -0,0 +1,52 @@ +From b1c37bec1ccfe5ccab72bc0ddc0dfa45c43e2de2 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Tested-by: Michael Walle +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230404172148.82422-16-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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 + #include + #include ++#include + #include + + 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) { diff --git a/target/linux/generic/backport-6.6/811-v6.4-0005-nvmem-core-add-per-cell-post-processing.patch b/target/linux/generic/backport-6.6/811-v6.4-0005-nvmem-core-add-per-cell-post-processing.patch new file mode 100644 index 0000000000..53628cd4e4 --- /dev/null +++ b/target/linux/generic/backport-6.6/811-v6.4-0005-nvmem-core-add-per-cell-post-processing.patch @@ -0,0 +1,86 @@ +From 345ec382cd4b736c36e01f155d08c913b225b736 Mon Sep 17 00:00:00 2001 +From: Michael Walle +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 +Signed-off-by: Miquel Raynal +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230404172148.82422-17-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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; + }; + + /** diff --git a/target/linux/generic/backport-6.6/811-v6.4-0006-nvmem-core-allow-to-modify-a-cell-before-adding-it.patch b/target/linux/generic/backport-6.6/811-v6.4-0006-nvmem-core-allow-to-modify-a-cell-before-adding-it.patch new file mode 100644 index 0000000000..32990148c8 --- /dev/null +++ b/target/linux/generic/backport-6.6/811-v6.4-0006-nvmem-core-allow-to-modify-a-cell-before-adding-it.patch @@ -0,0 +1,59 @@ +From de12c9691501ccba41a154c223869f82be4c12fd Mon Sep 17 00:00:00 2001 +From: Michael Walle +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 +Signed-off-by: Miquel Raynal +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230404172148.82422-18-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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; diff --git a/target/linux/generic/backport-6.6/811-v6.4-0007-nvmem-imx-ocotp-replace-global-post-processing-with-.patch b/target/linux/generic/backport-6.6/811-v6.4-0007-nvmem-imx-ocotp-replace-global-post-processing-with-.patch new file mode 100644 index 0000000000..2a5fa618ea --- /dev/null +++ b/target/linux/generic/backport-6.6/811-v6.4-0007-nvmem-imx-ocotp-replace-global-post-processing-with-.patch @@ -0,0 +1,81 @@ +From 6c56a82d7895a213a43182a5d01a21a906a79847 Mon Sep 17 00:00:00 2001 +From: Michael Walle +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 +Tested-by: Michael Walle # on kontron-pitx-imx8m +Signed-off-by: Miquel Raynal +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230404172148.82422-19-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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); diff --git a/target/linux/generic/backport-6.6/811-v6.4-0008-nvmem-cell-drop-global-cell_post_process.patch b/target/linux/generic/backport-6.6/811-v6.4-0008-nvmem-cell-drop-global-cell_post_process.patch new file mode 100644 index 0000000000..eac202b882 --- /dev/null +++ b/target/linux/generic/backport-6.6/811-v6.4-0008-nvmem-cell-drop-global-cell_post_process.patch @@ -0,0 +1,68 @@ +From 011e40a166fdaa65fb9946b7cd91efec85b70dbb Mon Sep 17 00:00:00 2001 +From: Michael Walle +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 +Signed-off-by: Miquel Raynal +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230404172148.82422-20-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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; diff --git a/target/linux/generic/backport-6.6/811-v6.4-0009-nvmem-core-provide-own-priv-pointer-in-post-process-.patch b/target/linux/generic/backport-6.6/811-v6.4-0009-nvmem-core-provide-own-priv-pointer-in-post-process-.patch new file mode 100644 index 0000000000..46b30a2ed9 --- /dev/null +++ b/target/linux/generic/backport-6.6/811-v6.4-0009-nvmem-core-provide-own-priv-pointer-in-post-process-.patch @@ -0,0 +1,76 @@ +From 8a134fd9f9323f4c39ec27055b3d3723cfb5c1e9 Mon Sep 17 00:00:00 2001 +From: Michael Walle +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 +Signed-off-by: Miquel Raynal +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230404172148.82422-21-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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; + }; + + /** diff --git a/target/linux/generic/backport-6.6/811-v6.4-0010-nvmem-layouts-sl28vpd-Add-new-layout-driver.patch b/target/linux/generic/backport-6.6/811-v6.4-0010-nvmem-layouts-sl28vpd-Add-new-layout-driver.patch new file mode 100644 index 0000000000..7d97658b60 --- /dev/null +++ b/target/linux/generic/backport-6.6/811-v6.4-0010-nvmem-layouts-sl28vpd-Add-new-layout-driver.patch @@ -0,0 +1,215 @@ +From d9fae023fe86069750092fc1c2f3a73e2fb18512 Mon Sep 17 00:00:00 2001 +From: Michael Walle +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 +Signed-off-by: Miquel Raynal +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230404172148.82422-22-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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 ++#include ++#include ++#include ++#include ++#include ++ ++#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 "); ++MODULE_DESCRIPTION("NVMEM layout driver for the VPD of Kontron sl28 boards"); diff --git a/target/linux/generic/backport-6.6/811-v6.4-0011-nvmem-layouts-onie-tlv-Add-new-layout-driver.patch b/target/linux/generic/backport-6.6/811-v6.4-0011-nvmem-layouts-onie-tlv-Add-new-layout-driver.patch new file mode 100644 index 0000000000..ca8b4bc069 --- /dev/null +++ b/target/linux/generic/backport-6.6/811-v6.4-0011-nvmem-layouts-onie-tlv-Add-new-layout-driver.patch @@ -0,0 +1,306 @@ +From d3c0d12f6474216bf386101e2449cc73e5c5b61d Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230404172148.82422-24-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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 ++ * Based on the nvmem driver written by: Vadym Kochan ++ * Inspired by the first layout written by: Rafał Miłecki ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#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 "); ++MODULE_DESCRIPTION("NVMEM layout driver for Onie TLV table parsing"); ++MODULE_ALIAS("NVMEM layout driver for Onie TLV table parsing"); diff --git a/target/linux/generic/backport-6.6/811-v6.4-0012-nvmem-stm32-romem-mark-OF-related-data-as-maybe-unus.patch b/target/linux/generic/backport-6.6/811-v6.4-0012-nvmem-stm32-romem-mark-OF-related-data-as-maybe-unus.patch new file mode 100644 index 0000000000..94a0911d73 --- /dev/null +++ b/target/linux/generic/backport-6.6/811-v6.4-0012-nvmem-stm32-romem-mark-OF-related-data-as-maybe-unus.patch @@ -0,0 +1,32 @@ +From a4fb434ef96ace5af758ca2c52c3a3f8f3abc87c Mon Sep 17 00:00:00 2001 +From: Krzysztof Kozlowski +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230404172148.82422-27-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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, diff --git a/target/linux/generic/backport-6.6/811-v6.4-0013-nvmem-mtk-efuse-Support-postprocessing-for-GPU-speed.patch b/target/linux/generic/backport-6.6/811-v6.4-0013-nvmem-mtk-efuse-Support-postprocessing-for-GPU-speed.patch new file mode 100644 index 0000000000..abda402bdd --- /dev/null +++ b/target/linux/generic/backport-6.6/811-v6.4-0013-nvmem-mtk-efuse-Support-postprocessing-for-GPU-speed.patch @@ -0,0 +1,120 @@ +From de6e05097f7db066afb0ad4c88b730949f7b7749 Mon Sep 17 00:00:00 2001 +From: AngeloGioacchino Del Regno +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230404172148.82422-28-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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 + #include + #include ++#include ++ ++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); diff --git a/target/linux/generic/backport-6.6/811-v6.4-0014-nvmem-bcm-ocotp-Use-devm_platform_ioremap_resource.patch b/target/linux/generic/backport-6.6/811-v6.4-0014-nvmem-bcm-ocotp-Use-devm_platform_ioremap_resource.patch new file mode 100644 index 0000000000..200eb1928f --- /dev/null +++ b/target/linux/generic/backport-6.6/811-v6.4-0014-nvmem-bcm-ocotp-Use-devm_platform_ioremap_resource.patch @@ -0,0 +1,39 @@ +From 1dc552fa33cf98af3e784dbc0500da93cae3b24a Mon Sep 17 00:00:00 2001 +From: Yang Li +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230404172148.82422-31-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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); diff --git a/target/linux/generic/backport-6.6/811-v6.4-0015-nvmem-nintendo-otp-Use-devm_platform_ioremap_resourc.patch b/target/linux/generic/backport-6.6/811-v6.4-0015-nvmem-nintendo-otp-Use-devm_platform_ioremap_resourc.patch new file mode 100644 index 0000000000..890dacd08d --- /dev/null +++ b/target/linux/generic/backport-6.6/811-v6.4-0015-nvmem-nintendo-otp-Use-devm_platform_ioremap_resourc.patch @@ -0,0 +1,39 @@ +From 649409990d2e93fac657be7c6960c28a2c601d65 Mon Sep 17 00:00:00 2001 +From: Yang Li +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230404172148.82422-32-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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); + diff --git a/target/linux/generic/backport-6.6/811-v6.4-0016-nvmem-vf610-ocotp-Use-devm_platform_get_and_ioremap_.patch b/target/linux/generic/backport-6.6/811-v6.4-0016-nvmem-vf610-ocotp-Use-devm_platform_get_and_ioremap_.patch new file mode 100644 index 0000000000..3f5d3c1ad4 --- /dev/null +++ b/target/linux/generic/backport-6.6/811-v6.4-0016-nvmem-vf610-ocotp-Use-devm_platform_get_and_ioremap_.patch @@ -0,0 +1,32 @@ +From c2367aa60d5e34d48582362c6de34b4131d92be7 Mon Sep 17 00:00:00 2001 +From: Yang Li +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230404172148.82422-33-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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); + diff --git a/target/linux/generic/backport-6.6/811-v6.4-0017-nvmem-core-support-specifying-both-cell-raw-data-pos.patch b/target/linux/generic/backport-6.6/811-v6.4-0017-nvmem-core-support-specifying-both-cell-raw-data-pos.patch new file mode 100644 index 0000000000..eeb407e9bb --- /dev/null +++ b/target/linux/generic/backport-6.6/811-v6.4-0017-nvmem-core-support-specifying-both-cell-raw-data-pos.patch @@ -0,0 +1,115 @@ +From 55d4980ce55b6bb4be66877de4dbec513911b988 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230404172148.82422-35-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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; diff --git a/target/linux/generic/backport-6.6/811-v6.4-0018-nvmem-u-boot-env-post-process-ethaddr-env-variable.patch b/target/linux/generic/backport-6.6/811-v6.4-0018-nvmem-u-boot-env-post-process-ethaddr-env-variable.patch new file mode 100644 index 0000000000..adde0ffc4b --- /dev/null +++ b/target/linux/generic/backport-6.6/811-v6.4-0018-nvmem-u-boot-env-post-process-ethaddr-env-variable.patch @@ -0,0 +1,81 @@ +From c49f1a8af6bcf6d18576bca898f8083ca4b129e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230404172148.82422-36-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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 ++#include ++#include + #include + #include + #include +@@ -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)) diff --git a/target/linux/generic/backport-6.6/811-v6.4-0019-nvmem-Add-macro-to-register-nvmem-layout-drivers.patch b/target/linux/generic/backport-6.6/811-v6.4-0019-nvmem-Add-macro-to-register-nvmem-layout-drivers.patch new file mode 100644 index 0000000000..7c6fe22b5f --- /dev/null +++ b/target/linux/generic/backport-6.6/811-v6.4-0019-nvmem-Add-macro-to-register-nvmem-layout-drivers.patch @@ -0,0 +1,42 @@ +From 814c978f02db17f16e6aa2efa2a929372f06da09 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Signed-off-by: Miquel Raynal +Acked-by: Rafał Miłecki +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230404172148.82422-37-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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 + #include + #include + #include +@@ -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 */ diff --git a/target/linux/generic/backport-6.6/811-v6.4-0020-nvmem-layouts-sl28vpd-Use-module_nvmem_layout_driver.patch b/target/linux/generic/backport-6.6/811-v6.4-0020-nvmem-layouts-sl28vpd-Use-module_nvmem_layout_driver.patch new file mode 100644 index 0000000000..06646dd68b --- /dev/null +++ b/target/linux/generic/backport-6.6/811-v6.4-0020-nvmem-layouts-sl28vpd-Use-module_nvmem_layout_driver.patch @@ -0,0 +1,39 @@ +From 0abdf99fe0c86252ba274703425f8d543d7e7f0d Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230404172148.82422-38-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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 "); diff --git a/target/linux/generic/backport-6.6/811-v6.4-0021-nvmem-layouts-onie-tlv-Use-module_nvmem_layout_drive.patch b/target/linux/generic/backport-6.6/811-v6.4-0021-nvmem-layouts-onie-tlv-Use-module_nvmem_layout_drive.patch new file mode 100644 index 0000000000..826f4378c2 --- /dev/null +++ b/target/linux/generic/backport-6.6/811-v6.4-0021-nvmem-layouts-onie-tlv-Use-module_nvmem_layout_drive.patch @@ -0,0 +1,39 @@ +From d119eb38faab61125aaa4f63c74eef61585cf34c Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230404172148.82422-39-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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 "); diff --git a/target/linux/generic/backport-6.6/811-v6.4-0022-nvmem-layouts-onie-tlv-Drop-wrong-module-alias.patch b/target/linux/generic/backport-6.6/811-v6.4-0022-nvmem-layouts-onie-tlv-Drop-wrong-module-alias.patch new file mode 100644 index 0000000000..f20db85ceb --- /dev/null +++ b/target/linux/generic/backport-6.6/811-v6.4-0022-nvmem-layouts-onie-tlv-Drop-wrong-module-alias.patch @@ -0,0 +1,24 @@ +From 6b13e4b6a9a45028ac730e550380077df1845912 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230404172148.82422-40-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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 "); + MODULE_DESCRIPTION("NVMEM layout driver for Onie TLV table parsing"); +-MODULE_ALIAS("NVMEM layout driver for Onie TLV table parsing"); diff --git a/target/linux/generic/backport-6.6/811-v6.4-0023-nvmem-layouts-sl28vpd-set-varaiable-sl28vpd_layout-s.patch b/target/linux/generic/backport-6.6/811-v6.4-0023-nvmem-layouts-sl28vpd-set-varaiable-sl28vpd_layout-s.patch new file mode 100644 index 0000000000..5cf847b57a --- /dev/null +++ b/target/linux/generic/backport-6.6/811-v6.4-0023-nvmem-layouts-sl28vpd-set-varaiable-sl28vpd_layout-s.patch @@ -0,0 +1,31 @@ +From a8642cd11635a35a5f1dc31857887900d6610778 Mon Sep 17 00:00:00 2001 +From: Tom Rix +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230404172148.82422-41-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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, diff --git a/target/linux/generic/backport-6.6/812-v6.2-firmware-nvram-bcm47xx-support-init-from-IO-memory.patch b/target/linux/generic/backport-6.6/812-v6.2-firmware-nvram-bcm47xx-support-init-from-IO-memory.patch new file mode 100644 index 0000000000..13ee2d4229 --- /dev/null +++ b/target/linux/generic/backport-6.6/812-v6.2-firmware-nvram-bcm47xx-support-init-from-IO-memory.patch @@ -0,0 +1,100 @@ +From a5be5ce0e25439fae3cd42e3d775979547926812 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +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 +Cc: linux-mips@vger.kernel.org +Cc: Florian Fainelli +Cc: bcm-kernel-feedback-list@broadcom.com +Cc: Thomas Bogendoerfer +Signed-off-by: Rafał Miłecki +[Added an export for modules potentially using the init symbol] +Signed-off-by: Linus Walleij +Link: https://lore.kernel.org/r/20221103082529.359084-1-linus.walleij@linaro.org +Signed-off-by: Florian Fainelli +--- + 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 + */ + ++#include + #include + #include + #include +@@ -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 + + #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; diff --git a/target/linux/generic/backport-6.6/813-v6.5-0001-nvmem-imx-ocotp-set-varaiable-imx_ocotp_layout-stora.patch b/target/linux/generic/backport-6.6/813-v6.5-0001-nvmem-imx-ocotp-set-varaiable-imx_ocotp_layout-stora.patch new file mode 100644 index 0000000000..38cfccd5ef --- /dev/null +++ b/target/linux/generic/backport-6.6/813-v6.5-0001-nvmem-imx-ocotp-set-varaiable-imx_ocotp_layout-stora.patch @@ -0,0 +1,31 @@ +From eebc6573ad940b62a87776db3917e912b4f52d78 Mon Sep 17 00:00:00 2001 +From: Tom Rix +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 +Signed-off-by: Srinivas Kandagatla +Message-ID: <20230611140330.154222-2-srinivas.kandagatla@linaro.org> +Signed-off-by: Greg Kroah-Hartman +--- + 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, + }; + diff --git a/target/linux/generic/backport-6.6/813-v6.5-0002-nvmem-imx-ocotp-Reverse-MAC-addresses-on-all-i.MX-de.patch b/target/linux/generic/backport-6.6/813-v6.5-0002-nvmem-imx-ocotp-Reverse-MAC-addresses-on-all-i.MX-de.patch new file mode 100644 index 0000000000..7523e5ebf6 --- /dev/null +++ b/target/linux/generic/backport-6.6/813-v6.5-0002-nvmem-imx-ocotp-Reverse-MAC-addresses-on-all-i.MX-de.patch @@ -0,0 +1,71 @@ +From 8a00fc606312c68b98add8fe8e6f7a013ce29e78 Mon Sep 17 00:00:00 2001 +From: Alexander Stein +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 +Tested-by: Richard Leitner # imx6q +Signed-off-by: Srinivas Kandagatla +Message-ID: <20230611140330.154222-3-srinivas.kandagatla@linaro.org> +Signed-off-by: Greg Kroah-Hartman +--- + 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; + diff --git a/target/linux/generic/backport-6.6/813-v6.5-0003-nvmem-brcm_nvram-add-.read_post_process-for-MACs.patch b/target/linux/generic/backport-6.6/813-v6.5-0003-nvmem-brcm_nvram-add-.read_post_process-for-MACs.patch new file mode 100644 index 0000000000..fb4d346a95 --- /dev/null +++ b/target/linux/generic/backport-6.6/813-v6.5-0003-nvmem-brcm_nvram-add-.read_post_process-for-MACs.patch @@ -0,0 +1,81 @@ +From 73bcd133c910bff3b6d3b3834d0d14be9444e90a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +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 +Signed-off-by: Srinivas Kandagatla +Message-ID: <20230611140330.154222-5-srinivas.kandagatla@linaro.org> +Signed-off-by: Greg Kroah-Hartman +--- + 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 ++#include ++#include + #include + #include + #include +@@ -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; diff --git a/target/linux/generic/backport-6.6/813-v6.5-0004-nvmem-rockchip-otp-Add-clks-and-reg_read-to-rockchip.patch b/target/linux/generic/backport-6.6/813-v6.5-0004-nvmem-rockchip-otp-Add-clks-and-reg_read-to-rockchip.patch new file mode 100644 index 0000000000..3b6e6e57f5 --- /dev/null +++ b/target/linux/generic/backport-6.6/813-v6.5-0004-nvmem-rockchip-otp-Add-clks-and-reg_read-to-rockchip.patch @@ -0,0 +1,166 @@ +From 8dc61364164e79e44c07fa2ac0a7b6939f00d5db Mon Sep 17 00:00:00 2001 +From: Cristian Ciocaltea +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 +Tested-by: Vincent Legoll +Reviewed-by: Heiko Stuebner +Signed-off-by: Srinivas Kandagatla +Message-ID: <20230611140330.154222-10-srinivas.kandagatla@linaro.org> +Signed-off-by: Greg Kroah-Hartman +--- + 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; + diff --git a/target/linux/generic/backport-6.6/813-v6.5-0005-nvmem-rockchip-otp-Generalize-rockchip_otp_wait_stat.patch b/target/linux/generic/backport-6.6/813-v6.5-0005-nvmem-rockchip-otp-Generalize-rockchip_otp_wait_stat.patch new file mode 100644 index 0000000000..b5b66cfc5a --- /dev/null +++ b/target/linux/generic/backport-6.6/813-v6.5-0005-nvmem-rockchip-otp-Generalize-rockchip_otp_wait_stat.patch @@ -0,0 +1,62 @@ +From 30fd21cfb1e64ef20035559a8246f5fbf682c40e Mon Sep 17 00:00:00 2001 +From: Cristian Ciocaltea +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 +Tested-by: Vincent Legoll +Reviewed-by: Heiko Stuebner +Signed-off-by: Srinivas Kandagatla +Message-ID: <20230611140330.154222-11-srinivas.kandagatla@linaro.org> +Signed-off-by: Greg Kroah-Hartman +--- + 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; diff --git a/target/linux/generic/backport-6.6/813-v6.5-0006-nvmem-rockchip-otp-Use-devm_reset_control_array_get_.patch b/target/linux/generic/backport-6.6/813-v6.5-0006-nvmem-rockchip-otp-Use-devm_reset_control_array_get_.patch new file mode 100644 index 0000000000..3a17479d95 --- /dev/null +++ b/target/linux/generic/backport-6.6/813-v6.5-0006-nvmem-rockchip-otp-Use-devm_reset_control_array_get_.patch @@ -0,0 +1,31 @@ +From d325c9dd2b6e94040ca722ddcadcd6af358dd2be Mon Sep 17 00:00:00 2001 +From: Cristian Ciocaltea +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 +Tested-by: Vincent Legoll +Reviewed-by: Heiko Stuebner +Signed-off-by: Srinivas Kandagatla +Message-ID: <20230611140330.154222-12-srinivas.kandagatla@linaro.org> +Signed-off-by: Greg Kroah-Hartman +--- + 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); + diff --git a/target/linux/generic/backport-6.6/813-v6.5-0007-nvmem-rockchip-otp-Improve-probe-error-handling.patch b/target/linux/generic/backport-6.6/813-v6.5-0007-nvmem-rockchip-otp-Improve-probe-error-handling.patch new file mode 100644 index 0000000000..37cb927b10 --- /dev/null +++ b/target/linux/generic/backport-6.6/813-v6.5-0007-nvmem-rockchip-otp-Improve-probe-error-handling.patch @@ -0,0 +1,71 @@ +From 912517345b867a69542dc9f5c2cc3e9d8beaccf5 Mon Sep 17 00:00:00 2001 +From: Cristian Ciocaltea +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 +Tested-by: Vincent Legoll +Reviewed-by: Heiko Stuebner +Signed-off-by: Srinivas Kandagatla +Message-ID: <20230611140330.154222-13-srinivas.kandagatla@linaro.org> +Signed-off-by: Greg Kroah-Hartman +--- + 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 = { diff --git a/target/linux/generic/backport-6.6/813-v6.5-0008-nvmem-rockchip-otp-Add-support-for-RK3588.patch b/target/linux/generic/backport-6.6/813-v6.5-0008-nvmem-rockchip-otp-Add-support-for-RK3588.patch new file mode 100644 index 0000000000..c1e2231c9e --- /dev/null +++ b/target/linux/generic/backport-6.6/813-v6.5-0008-nvmem-rockchip-otp-Add-support-for-RK3588.patch @@ -0,0 +1,129 @@ +From 8ab099fafbbc8c9607c399d21a774784a6cb8b45 Mon Sep 17 00:00:00 2001 +From: Cristian Ciocaltea +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 +Signed-off-by: Finley Xiao +Signed-off-by: Cristian Ciocaltea +Tested-by: Vincent Legoll +Reviewed-by: Heiko Stuebner +Signed-off-by: Srinivas Kandagatla +Message-ID: <20230611140330.154222-14-srinivas.kandagatla@linaro.org> +Signed-off-by: Greg Kroah-Hartman +--- + 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 */ }, + }; diff --git a/target/linux/generic/backport-6.6/813-v6.5-0009-nvmem-zynqmp-Switch-xilinx.com-emails-to-amd.com.patch b/target/linux/generic/backport-6.6/813-v6.5-0009-nvmem-zynqmp-Switch-xilinx.com-emails-to-amd.com.patch new file mode 100644 index 0000000000..220e3e9a05 --- /dev/null +++ b/target/linux/generic/backport-6.6/813-v6.5-0009-nvmem-zynqmp-Switch-xilinx.com-emails-to-amd.com.patch @@ -0,0 +1,26 @@ +From 9734408969e978a1c0d5d752be63dd638288e374 Mon Sep 17 00:00:00 2001 +From: Michal Simek +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 +Signed-off-by: Srinivas Kandagatla +Message-ID: <20230611140330.154222-20-srinivas.kandagatla@linaro.org> +Signed-off-by: Greg Kroah-Hartman +--- + 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 , Nava kishore Manne "); ++MODULE_AUTHOR("Michal Simek , Nava kishore Manne "); + MODULE_DESCRIPTION("ZynqMP NVMEM driver"); + MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/backport-6.6/813-v6.5-0010-nvmem-imx-support-i.MX93-OCOTP.patch b/target/linux/generic/backport-6.6/813-v6.5-0010-nvmem-imx-support-i.MX93-OCOTP.patch new file mode 100644 index 0000000000..f8e6be4241 --- /dev/null +++ b/target/linux/generic/backport-6.6/813-v6.5-0010-nvmem-imx-support-i.MX93-OCOTP.patch @@ -0,0 +1,230 @@ +From 22e9e6fcfb5042cb6d6c7874c459b034800092f1 Mon Sep 17 00:00:00 2001 +From: Peng Fan +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 +Signed-off-by: Srinivas Kandagatla +Message-ID: <20230611140330.154222-22-srinivas.kandagatla@linaro.org> +Signed-off-by: Greg Kroah-Hartman +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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 "); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/backport-6.6/813-v6.5-0011-nvmem-core-add-support-for-fixed-cells-layout.patch b/target/linux/generic/backport-6.6/813-v6.5-0011-nvmem-core-add-support-for-fixed-cells-layout.patch new file mode 100644 index 0000000000..59b2f9fa2c --- /dev/null +++ b/target/linux/generic/backport-6.6/813-v6.5-0011-nvmem-core-add-support-for-fixed-cells-layout.patch @@ -0,0 +1,96 @@ +From 27f699e578b1a72df89dfa3bc42e093a01dc8d10 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +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 +Reviewed-by: Michael Walle +Signed-off-by: Srinivas Kandagatla +Message-ID: <20230611140330.154222-26-srinivas.kandagatla@linaro.org> +Signed-off-by: Greg Kroah-Hartman +--- + 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; diff --git a/target/linux/generic/backport-6.6/814-v6.6-0001-nvmem-sunxi_sid-Convert-to-devm_platform_ioremap_res.patch b/target/linux/generic/backport-6.6/814-v6.6-0001-nvmem-sunxi_sid-Convert-to-devm_platform_ioremap_res.patch new file mode 100644 index 0000000000..bf7a816bb2 --- /dev/null +++ b/target/linux/generic/backport-6.6/814-v6.6-0001-nvmem-sunxi_sid-Convert-to-devm_platform_ioremap_res.patch @@ -0,0 +1,36 @@ +From 9ccfcbeb8f32ff89e99b36cb9cdebaa0d1b44ed1 Mon Sep 17 00:00:00 2001 +From: Yangtao Li +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 +Acked-by: Jernej Skrabec +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230823132744.350618-3-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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); + diff --git a/target/linux/generic/backport-6.6/814-v6.6-0002-nvmem-brcm_nvram-Use-devm_platform_get_and_ioremap_r.patch b/target/linux/generic/backport-6.6/814-v6.6-0002-nvmem-brcm_nvram-Use-devm_platform_get_and_ioremap_r.patch new file mode 100644 index 0000000000..f142d735de --- /dev/null +++ b/target/linux/generic/backport-6.6/814-v6.6-0002-nvmem-brcm_nvram-Use-devm_platform_get_and_ioremap_r.patch @@ -0,0 +1,30 @@ +From cfadd0e7d9225566f320bc4dc716682be910be6c Mon Sep 17 00:00:00 2001 +From: Yangtao Li +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230823132744.350618-4-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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); + diff --git a/target/linux/generic/backport-6.6/814-v6.6-0003-nvmem-lpc18xx_otp-Convert-to-devm_platform_ioremap_r.patch b/target/linux/generic/backport-6.6/814-v6.6-0003-nvmem-lpc18xx_otp-Convert-to-devm_platform_ioremap_r.patch new file mode 100644 index 0000000000..0395bbf8bc --- /dev/null +++ b/target/linux/generic/backport-6.6/814-v6.6-0003-nvmem-lpc18xx_otp-Convert-to-devm_platform_ioremap_r.patch @@ -0,0 +1,34 @@ +From 0b49178e2b6b4aac3c7fa3ce8d8c02208a13b988 Mon Sep 17 00:00:00 2001 +From: Yangtao Li +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230823132744.350618-5-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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); + diff --git a/target/linux/generic/backport-6.6/814-v6.6-0004-nvmem-meson-mx-efuse-Convert-to-devm_platform_iorema.patch b/target/linux/generic/backport-6.6/814-v6.6-0004-nvmem-meson-mx-efuse-Convert-to-devm_platform_iorema.patch new file mode 100644 index 0000000000..da077eb4b7 --- /dev/null +++ b/target/linux/generic/backport-6.6/814-v6.6-0004-nvmem-meson-mx-efuse-Convert-to-devm_platform_iorema.patch @@ -0,0 +1,36 @@ +From 0a223a097709b99a0ba738d6be5b4f52c04ffb64 Mon Sep 17 00:00:00 2001 +From: Yangtao Li +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230823132744.350618-6-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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); + diff --git a/target/linux/generic/backport-6.6/814-v6.6-0005-nvmem-rockchip-efuse-Use-devm_platform_get_and_iorem.patch b/target/linux/generic/backport-6.6/814-v6.6-0005-nvmem-rockchip-efuse-Use-devm_platform_get_and_iorem.patch new file mode 100644 index 0000000000..dc144ddfbd --- /dev/null +++ b/target/linux/generic/backport-6.6/814-v6.6-0005-nvmem-rockchip-efuse-Use-devm_platform_get_and_iorem.patch @@ -0,0 +1,31 @@ +From 94904db28db49ac8fbb2a273d25156db26a3a985 Mon Sep 17 00:00:00 2001 +From: Yangtao Li +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 +Reviewed-by: Heiko Stuebner +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230823132744.350618-7-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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); + diff --git a/target/linux/generic/backport-6.6/814-v6.6-0006-nvmem-stm32-romem-Use-devm_platform_get_and_ioremap_.patch b/target/linux/generic/backport-6.6/814-v6.6-0006-nvmem-stm32-romem-Use-devm_platform_get_and_ioremap_.patch new file mode 100644 index 0000000000..99e20939cc --- /dev/null +++ b/target/linux/generic/backport-6.6/814-v6.6-0006-nvmem-stm32-romem-Use-devm_platform_get_and_ioremap_.patch @@ -0,0 +1,30 @@ +From 0a4a8c0d238fec1fa4b85591524ef42ad261cb97 Mon Sep 17 00:00:00 2001 +From: Yangtao Li +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230823132744.350618-8-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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); + diff --git a/target/linux/generic/backport-6.6/814-v6.6-0007-nvmem-qfprom-do-some-cleanup.patch b/target/linux/generic/backport-6.6/814-v6.6-0007-nvmem-qfprom-do-some-cleanup.patch new file mode 100644 index 0000000000..6d93752e27 --- /dev/null +++ b/target/linux/generic/backport-6.6/814-v6.6-0007-nvmem-qfprom-do-some-cleanup.patch @@ -0,0 +1,59 @@ +From 0bc0d6dc2a9a05ae6729b4622f09782d9f230815 Mon Sep 17 00:00:00 2001 +From: Yangtao Li +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230823132744.350618-9-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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) diff --git a/target/linux/generic/backport-6.6/814-v6.6-0008-nvmem-uniphier-Use-devm_platform_get_and_ioremap_res.patch b/target/linux/generic/backport-6.6/814-v6.6-0008-nvmem-uniphier-Use-devm_platform_get_and_ioremap_res.patch new file mode 100644 index 0000000000..3e328a4c99 --- /dev/null +++ b/target/linux/generic/backport-6.6/814-v6.6-0008-nvmem-uniphier-Use-devm_platform_get_and_ioremap_res.patch @@ -0,0 +1,29 @@ +From 6ac41c556e22a0d7d267c9b9d48681d73af4b368 Mon Sep 17 00:00:00 2001 +From: Yangtao Li +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230823132744.350618-10-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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); + diff --git a/target/linux/generic/backport-6.6/814-v6.6-0009-nvmem-add-new-NXP-QorIQ-eFuse-driver.patch b/target/linux/generic/backport-6.6/814-v6.6-0009-nvmem-add-new-NXP-QorIQ-eFuse-driver.patch new file mode 100644 index 0000000000..acbe18e270 --- /dev/null +++ b/target/linux/generic/backport-6.6/814-v6.6-0009-nvmem-add-new-NXP-QorIQ-eFuse-driver.patch @@ -0,0 +1,133 @@ +From c8efcf7a86ebf2ff48584d270b3070a7075bc345 Mon Sep 17 00:00:00 2001 +From: Richard Alpe +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 +Reviewed-by: Niklas Söderlund +Signed-off-by: Srinivas Kandagatla +--- + 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 ++#include ++#include ++#include ++#include ++#include ++ ++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 "); ++MODULE_DESCRIPTION("NXP QorIQ Security Fuse Processor (SFP) Reader"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/backport-6.6/814-v6.6-0011-nvmem-Kconfig-Fix-typo-drive-driver.patch b/target/linux/generic/backport-6.6/814-v6.6-0011-nvmem-Kconfig-Fix-typo-drive-driver.patch new file mode 100644 index 0000000000..67f30e34a2 --- /dev/null +++ b/target/linux/generic/backport-6.6/814-v6.6-0011-nvmem-Kconfig-Fix-typo-drive-driver.patch @@ -0,0 +1,37 @@ +From 9d53d595f688c9837e88a919229cc61a165c7b9e Mon Sep 17 00:00:00 2001 +From: Diederik de Haas +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 +Reviewed-by: Heiko Stuebner +Signed-off-by: Srinivas Kandagatla +--- + 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. diff --git a/target/linux/generic/backport-6.6/814-v6.6-0012-nvmem-sec-qfprom-Add-Qualcomm-secure-QFPROM-support.patch b/target/linux/generic/backport-6.6/814-v6.6-0012-nvmem-sec-qfprom-Add-Qualcomm-secure-QFPROM-support.patch new file mode 100644 index 0000000000..af4a2926dc --- /dev/null +++ b/target/linux/generic/backport-6.6/814-v6.6-0012-nvmem-sec-qfprom-Add-Qualcomm-secure-QFPROM-support.patch @@ -0,0 +1,152 @@ +From 0a9ec38c47c1ca4528aa058e2b9ea61901a7e632 Mon Sep 17 00:00:00 2001 +From: Komal Bajaj +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 +Signed-off-by: Srinivas Kandagatla +--- + 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 ++#include ++#include ++#include ++#include ++ ++/** ++ * 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"); diff --git a/target/linux/generic/backport-6.6/814-v6.6-0013-nvmem-u-boot-env-Replace-zero-length-array-with-DECL.patch b/target/linux/generic/backport-6.6/814-v6.6-0013-nvmem-u-boot-env-Replace-zero-length-array-with-DECL.patch new file mode 100644 index 0000000000..dab8ec2c24 --- /dev/null +++ b/target/linux/generic/backport-6.6/814-v6.6-0013-nvmem-u-boot-env-Replace-zero-length-array-with-DECL.patch @@ -0,0 +1,30 @@ +From c32f2186acc9abb4d766361255d7ddf07d15eeb2 Mon Sep 17 00:00:00 2001 +From: Atul Raut +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 +Signed-off-by: Srinivas Kandagatla +--- + 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, diff --git a/target/linux/generic/backport-6.6/814-v6.6-0014-nvmem-core-Create-all-cells-before-adding-the-nvmem-.patch b/target/linux/generic/backport-6.6/814-v6.6-0014-nvmem-core-Create-all-cells-before-adding-the-nvmem-.patch new file mode 100644 index 0000000000..f9532f39c3 --- /dev/null +++ b/target/linux/generic/backport-6.6/814-v6.6-0014-nvmem-core-Create-all-cells-before-adding-the-nvmem-.patch @@ -0,0 +1,40 @@ +From 104af6a5b199eb4dc7970d1304aef38ac5a6ed54 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Reviewed-by: Michael Walle +Signed-off-by: Srinivas Kandagatla +--- + 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; + diff --git a/target/linux/generic/backport-6.6/814-v6.6-0015-nvmem-core-Return-NULL-when-no-nvmem-layout-is-found.patch b/target/linux/generic/backport-6.6/814-v6.6-0015-nvmem-core-Return-NULL-when-no-nvmem-layout-is-found.patch new file mode 100644 index 0000000000..8f64d0c28b --- /dev/null +++ b/target/linux/generic/backport-6.6/814-v6.6-0015-nvmem-core-Return-NULL-when-no-nvmem-layout-is-found.patch @@ -0,0 +1,35 @@ +From 6c7f48ea2e663b679aa8e60d8d8e1e6306a644f9 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Reported-by: Dan Carpenter +Closes: https://lore.kernel.org/r/202308030002.DnSFOrMB-lkp@intel.com/ +Signed-off-by: Miquel Raynal +Reviewed-by: Michael Walle +Signed-off-by: Srinivas Kandagatla +--- + 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 */ + diff --git a/target/linux/generic/backport-6.6/814-v6.6-0016-nvmem-core-Do-not-open-code-existing-functions.patch b/target/linux/generic/backport-6.6/814-v6.6-0016-nvmem-core-Do-not-open-code-existing-functions.patch new file mode 100644 index 0000000000..28d8bba194 --- /dev/null +++ b/target/linux/generic/backport-6.6/814-v6.6-0016-nvmem-core-Do-not-open-code-existing-functions.patch @@ -0,0 +1,29 @@ +From b8257f61b4ddac6d7d0e19a5a4e8b07afb3b4ed3 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Reviewed-by: Michael Walle +Signed-off-by: Srinivas Kandagatla +--- + 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; + diff --git a/target/linux/generic/backport-6.6/814-v6.6-0017-nvmem-core-Notify-when-a-new-layout-is-registered.patch b/target/linux/generic/backport-6.6/814-v6.6-0017-nvmem-core-Notify-when-a-new-layout-is-registered.patch new file mode 100644 index 0000000000..b62a0e18da --- /dev/null +++ b/target/linux/generic/backport-6.6/814-v6.6-0017-nvmem-core-Notify-when-a-new-layout-is-registered.patch @@ -0,0 +1,44 @@ +From 0991afbe4b1805e7f0113ef10d7c5f0698a739e4 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Signed-off-by: Srinivas Kandagatla +--- + 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) diff --git a/target/linux/generic/backport-6.6/815-v6.6-1-leds-turris-omnia-Use-sysfs_emit-instead-of-sprintf.patch b/target/linux/generic/backport-6.6/815-v6.6-1-leds-turris-omnia-Use-sysfs_emit-instead-of-sprintf.patch new file mode 100644 index 0000000000..e17be439b2 --- /dev/null +++ b/target/linux/generic/backport-6.6/815-v6.6-1-leds-turris-omnia-Use-sysfs_emit-instead-of-sprintf.patch @@ -0,0 +1,29 @@ +From a75b58a46423cfd9b1f73581f4bd2ac2ae743996 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= +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 +Link: https://lore.kernel.org/r/20230802160748.11208-4-kabel@kernel.org +Signed-off-by: Lee Jones +--- + 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, diff --git a/target/linux/generic/backport-6.6/815-v6.7-2-leds-turris-omnia-Make-set_brightness-more-efficient.patch b/target/linux/generic/backport-6.6/815-v6.7-2-leds-turris-omnia-Make-set_brightness-more-efficient.patch new file mode 100644 index 0000000000..d84ad67125 --- /dev/null +++ b/target/linux/generic/backport-6.6/815-v6.7-2-leds-turris-omnia-Make-set_brightness-more-efficient.patch @@ -0,0 +1,207 @@ +From a64c3c1357275b1fd61bc9734f638cdb5d8a8bbb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= +Date: Mon, 18 Sep 2023 18:11:02 +0200 +Subject: [PATCH 4/6] leds: turris-omnia: Make set_brightness() more efficient +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Implement caching of the LED color and state values that are sent to MCU +in order to make the set_brightness() operation more efficient by +avoiding I2C transactions which are not needed. + +On Turris Omnia's MCU, which acts as the RGB LED controller, each LED +has a RGB color, and a ON/OFF state, which are configurable via I2C +commands CMD_LED_COLOR and CMD_LED_STATE. + +The CMD_LED_COLOR command sends 5 bytes and the CMD_LED_STATE command 2 +bytes over the I2C bus, which operates at 100 kHz. With I2C overhead +this allows ~1670 color changing commands and ~3200 state changing +commands per second (or around 1000 color + state changes per second). +This may seem more than enough, but the issue is that the I2C bus is +shared with another peripheral, the MCU. The MCU exposes an interrupt +interface, and it can trigger hundreds of interrupts per second. Each +time, we need to read the interrupt state register over this I2C bus. +Whenever we are sending a LED color/state changing command, the +interrupt reading is waiting. + +Currently, every time LED brightness or LED multi intensity is changed, +we send a CMD_LED_STATE command, and if the computed color (brightness +adjusted multi_intensity) is non-zero, we also send a CMD_LED_COLOR +command. + +Consider for example the situation when we have a netdev trigger enabled +for a LED. The netdev trigger does not change the LED color, only the +brightness (either to 0 or to currently configured brightness), and so +there is no need to send the CMD_LED_COLOR command. But each change of +brightness to 0 sends one CMD_LED_STATE command, and each change of +brightness to max_brightness sends one CMD_LED_STATE command and one +CMD_LED_COLOR command: + set_brightness(0) -> CMD_LED_STATE + set_brightness(255) -> CMD_LED_STATE + CMD_LED_COLOR + (unnecessary) + +We can avoid the unnecessary I2C transactions if we cache the values of +state and color that are sent to the controller. If the color does not +change from the one previously sent, there is no need to do the +CMD_LED_COLOR I2C transaction, and if the state does not change, there +is no need to do the CMD_LED_STATE transaction. + +Because we need to make sure that our cached values are consistent with +the controller state, add explicit setting of the LED color to white at +probe time (this is the default setting when MCU resets, but does not +necessarily need to be the case, for example if U-Boot played with the +LED colors). + +Signed-off-by: Marek Behún +Link: https://lore.kernel.org/r/20230918161104.20860-3-kabel@kernel.org +Signed-off-by: Lee Jones +--- + drivers/leds/leds-turris-omnia.c | 96 ++++++++++++++++++++++++++------ + 1 file changed, 78 insertions(+), 18 deletions(-) + +--- a/drivers/leds/leds-turris-omnia.c ++++ b/drivers/leds/leds-turris-omnia.c +@@ -30,6 +30,8 @@ + struct omnia_led { + struct led_classdev_mc mc_cdev; + struct mc_subled subled_info[OMNIA_LED_NUM_CHANNELS]; ++ u8 cached_channels[OMNIA_LED_NUM_CHANNELS]; ++ bool on; + int reg; + }; + +@@ -72,36 +74,82 @@ static int omnia_cmd_read_u8(const struc + return -EIO; + } + ++static int omnia_led_send_color_cmd(const struct i2c_client *client, ++ struct omnia_led *led) ++{ ++ char cmd[5]; ++ int ret; ++ ++ cmd[0] = CMD_LED_COLOR; ++ cmd[1] = led->reg; ++ cmd[2] = led->subled_info[0].brightness; ++ cmd[3] = led->subled_info[1].brightness; ++ cmd[4] = led->subled_info[2].brightness; ++ ++ /* Send the color change command */ ++ ret = i2c_master_send(client, cmd, 5); ++ if (ret < 0) ++ return ret; ++ ++ /* Cache the RGB channel brightnesses */ ++ for (int i = 0; i < OMNIA_LED_NUM_CHANNELS; ++i) ++ led->cached_channels[i] = led->subled_info[i].brightness; ++ ++ return 0; ++} ++ ++/* Determine if the computed RGB channels are different from the cached ones */ ++static bool omnia_led_channels_changed(struct omnia_led *led) ++{ ++ for (int i = 0; i < OMNIA_LED_NUM_CHANNELS; ++i) ++ if (led->subled_info[i].brightness != led->cached_channels[i]) ++ return true; ++ ++ return false; ++} ++ + static int omnia_led_brightness_set_blocking(struct led_classdev *cdev, + enum led_brightness brightness) + { + struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev); + struct omnia_leds *leds = dev_get_drvdata(cdev->dev->parent); + struct omnia_led *led = to_omnia_led(mc_cdev); +- u8 buf[5], state; +- int ret; ++ int err = 0; + + mutex_lock(&leds->lock); + +- led_mc_calc_color_components(&led->mc_cdev, brightness); ++ /* ++ * Only recalculate RGB brightnesses from intensities if brightness is ++ * non-zero. Otherwise we won't be using them and we can save ourselves ++ * some software divisions (Omnia's CPU does not implement the division ++ * instruction). ++ */ ++ if (brightness) { ++ led_mc_calc_color_components(mc_cdev, brightness); ++ ++ /* ++ * Send color command only if brightness is non-zero and the RGB ++ * channel brightnesses changed. ++ */ ++ if (omnia_led_channels_changed(led)) ++ err = omnia_led_send_color_cmd(leds->client, led); ++ } + +- buf[0] = CMD_LED_COLOR; +- buf[1] = led->reg; +- buf[2] = mc_cdev->subled_info[0].brightness; +- buf[3] = mc_cdev->subled_info[1].brightness; +- buf[4] = mc_cdev->subled_info[2].brightness; +- +- state = CMD_LED_STATE_LED(led->reg); +- if (buf[2] || buf[3] || buf[4]) +- state |= CMD_LED_STATE_ON; +- +- ret = omnia_cmd_write_u8(leds->client, CMD_LED_STATE, state); +- if (ret >= 0 && (state & CMD_LED_STATE_ON)) +- ret = i2c_master_send(leds->client, buf, 5); ++ /* Send on/off state change only if (bool)brightness changed */ ++ if (!err && !brightness != !led->on) { ++ u8 state = CMD_LED_STATE_LED(led->reg); ++ ++ if (brightness) ++ state |= CMD_LED_STATE_ON; ++ ++ err = omnia_cmd_write_u8(leds->client, CMD_LED_STATE, state); ++ if (!err) ++ led->on = !!brightness; ++ } + + mutex_unlock(&leds->lock); + +- return ret; ++ return err; + } + + static int omnia_led_register(struct i2c_client *client, struct omnia_led *led, +@@ -129,11 +177,15 @@ static int omnia_led_register(struct i2c + } + + led->subled_info[0].color_index = LED_COLOR_ID_RED; +- led->subled_info[0].channel = 0; + led->subled_info[1].color_index = LED_COLOR_ID_GREEN; +- led->subled_info[1].channel = 1; + led->subled_info[2].color_index = LED_COLOR_ID_BLUE; +- led->subled_info[2].channel = 2; ++ ++ /* Initial color is white */ ++ for (int i = 0; i < OMNIA_LED_NUM_CHANNELS; ++i) { ++ led->subled_info[i].intensity = 255; ++ led->subled_info[i].brightness = 255; ++ led->subled_info[i].channel = i; ++ } + + led->mc_cdev.subled_info = led->subled_info; + led->mc_cdev.num_colors = OMNIA_LED_NUM_CHANNELS; +@@ -162,6 +214,14 @@ static int omnia_led_register(struct i2c + return ret; + } + ++ /* Set initial color and cache it */ ++ ret = omnia_led_send_color_cmd(client, led); ++ if (ret < 0) { ++ dev_err(dev, "Cannot set LED %pOF initial color: %i\n", np, ++ ret); ++ return ret; ++ } ++ + ret = devm_led_classdev_multicolor_register_ext(dev, &led->mc_cdev, + &init_data); + if (ret < 0) { diff --git a/target/linux/generic/backport-6.6/815-v6.7-3-leds-turris-omnia-Support-HW-controlled-mode-via-pri.patch b/target/linux/generic/backport-6.6/815-v6.7-3-leds-turris-omnia-Support-HW-controlled-mode-via-pri.patch new file mode 100644 index 0000000000..00773ab0f6 --- /dev/null +++ b/target/linux/generic/backport-6.6/815-v6.7-3-leds-turris-omnia-Support-HW-controlled-mode-via-pri.patch @@ -0,0 +1,202 @@ +From e965e0f6de60874fc0a0caed9a9e0122999e0c7b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= +Date: Mon, 18 Sep 2023 18:11:03 +0200 +Subject: [PATCH 5/6] leds: turris-omnia: Support HW controlled mode via + private trigger +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add support for enabling MCU controlled mode of the Turris Omnia LEDs +via a LED private trigger called "omnia-mcu". Recall that private LED +triggers will only be listed in the sysfs trigger file for LEDs that +support them (currently there is no user of this mechanism). + +When in MCU controlled mode, the user can still set LED color, but the +blinking is done by MCU, which does different things for different LEDs: +- WAN LED is blinked according to the LED[0] pin of the WAN PHY +- LAN LEDs are blinked according to the LED[0] output of the + corresponding port of the LAN switch +- PCIe LEDs are blinked according to the logical OR of the MiniPCIe port + LED pins + +In the future I want to make the netdev trigger to transparently offload +the blinking to the HW if user sets compatible settings for the netdev +trigger (for LEDs associated with network devices). +There was some work on this already, and hopefully we will be able to +complete it sometime, but for now there are still multiple blockers for +this, and even if there weren't, we still would not be able to configure +HW controlled mode for the LEDs associated with MiniPCIe ports. + +In the meantime let's support HW controlled mode via the private LED +trigger mechanism. If, in the future, we manage to complete the netdev +trigger offloading, we can still keep this private trigger for backwards +compatibility, if needed. + +We also set "omnia-mcu" to cdev->default_trigger, so that the MCU keeps +control until the user first wants to take over it. If a different +default trigger is specified in device-tree via the +'linux,default-trigger' property, LED class will overwrite +cdev->default_trigger, and so the DT property will be respected. + +Signed-off-by: Marek Behún +Link: https://lore.kernel.org/r/20230918161104.20860-4-kabel@kernel.org +Signed-off-by: Lee Jones +--- + drivers/leds/Kconfig | 1 + + drivers/leds/leds-turris-omnia.c | 98 +++++++++++++++++++++++++++++--- + 2 files changed, 91 insertions(+), 8 deletions(-) + +--- a/drivers/leds/Kconfig ++++ b/drivers/leds/Kconfig +@@ -164,6 +164,7 @@ config LEDS_TURRIS_OMNIA + depends on I2C + depends on MACH_ARMADA_38X || COMPILE_TEST + depends on OF ++ select LEDS_TRIGGERS + help + This option enables basic support for the LEDs found on the front + side of CZ.NIC's Turris Omnia router. There are 12 RGB LEDs on the +--- a/drivers/leds/leds-turris-omnia.c ++++ b/drivers/leds/leds-turris-omnia.c +@@ -31,7 +31,7 @@ struct omnia_led { + struct led_classdev_mc mc_cdev; + struct mc_subled subled_info[OMNIA_LED_NUM_CHANNELS]; + u8 cached_channels[OMNIA_LED_NUM_CHANNELS]; +- bool on; ++ bool on, hwtrig; + int reg; + }; + +@@ -120,12 +120,14 @@ static int omnia_led_brightness_set_bloc + + /* + * Only recalculate RGB brightnesses from intensities if brightness is +- * non-zero. Otherwise we won't be using them and we can save ourselves +- * some software divisions (Omnia's CPU does not implement the division +- * instruction). ++ * non-zero (if it is zero and the LED is in HW blinking mode, we use ++ * max_brightness as brightness). Otherwise we won't be using them and ++ * we can save ourselves some software divisions (Omnia's CPU does not ++ * implement the division instruction). + */ +- if (brightness) { +- led_mc_calc_color_components(mc_cdev, brightness); ++ if (brightness || led->hwtrig) { ++ led_mc_calc_color_components(mc_cdev, brightness ?: ++ cdev->max_brightness); + + /* + * Send color command only if brightness is non-zero and the RGB +@@ -135,8 +137,11 @@ static int omnia_led_brightness_set_bloc + err = omnia_led_send_color_cmd(leds->client, led); + } + +- /* Send on/off state change only if (bool)brightness changed */ +- if (!err && !brightness != !led->on) { ++ /* ++ * Send on/off state change only if (bool)brightness changed and the LED ++ * is not being blinked by HW. ++ */ ++ if (!err && !led->hwtrig && !brightness != !led->on) { + u8 state = CMD_LED_STATE_LED(led->reg); + + if (brightness) +@@ -152,6 +157,71 @@ static int omnia_led_brightness_set_bloc + return err; + } + ++static struct led_hw_trigger_type omnia_hw_trigger_type; ++ ++static int omnia_hwtrig_activate(struct led_classdev *cdev) ++{ ++ struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev); ++ struct omnia_leds *leds = dev_get_drvdata(cdev->dev->parent); ++ struct omnia_led *led = to_omnia_led(mc_cdev); ++ int err = 0; ++ ++ mutex_lock(&leds->lock); ++ ++ if (!led->on) { ++ /* ++ * If the LED is off (brightness was set to 0), the last ++ * configured color was not necessarily sent to the MCU. ++ * Recompute with max_brightness and send if needed. ++ */ ++ led_mc_calc_color_components(mc_cdev, cdev->max_brightness); ++ ++ if (omnia_led_channels_changed(led)) ++ err = omnia_led_send_color_cmd(leds->client, led); ++ } ++ ++ if (!err) { ++ /* Put the LED into MCU controlled mode */ ++ err = omnia_cmd_write_u8(leds->client, CMD_LED_MODE, ++ CMD_LED_MODE_LED(led->reg)); ++ if (!err) ++ led->hwtrig = true; ++ } ++ ++ mutex_unlock(&leds->lock); ++ ++ return err; ++} ++ ++static void omnia_hwtrig_deactivate(struct led_classdev *cdev) ++{ ++ struct omnia_leds *leds = dev_get_drvdata(cdev->dev->parent); ++ struct omnia_led *led = to_omnia_led(lcdev_to_mccdev(cdev)); ++ int err; ++ ++ mutex_lock(&leds->lock); ++ ++ led->hwtrig = false; ++ ++ /* Put the LED into software mode */ ++ err = omnia_cmd_write_u8(leds->client, CMD_LED_MODE, ++ CMD_LED_MODE_LED(led->reg) | ++ CMD_LED_MODE_USER); ++ ++ mutex_unlock(&leds->lock); ++ ++ if (err < 0) ++ dev_err(cdev->dev, "Cannot put LED to software mode: %i\n", ++ err); ++} ++ ++static struct led_trigger omnia_hw_trigger = { ++ .name = "omnia-mcu", ++ .activate = omnia_hwtrig_activate, ++ .deactivate = omnia_hwtrig_deactivate, ++ .trigger_type = &omnia_hw_trigger_type, ++}; ++ + static int omnia_led_register(struct i2c_client *client, struct omnia_led *led, + struct device_node *np) + { +@@ -195,6 +265,12 @@ static int omnia_led_register(struct i2c + cdev = &led->mc_cdev.led_cdev; + cdev->max_brightness = 255; + cdev->brightness_set_blocking = omnia_led_brightness_set_blocking; ++ cdev->trigger_type = &omnia_hw_trigger_type; ++ /* ++ * Use the omnia-mcu trigger as the default trigger. It may be rewritten ++ * by LED class from the linux,default-trigger property. ++ */ ++ cdev->default_trigger = omnia_hw_trigger.name; + + /* put the LED into software mode */ + ret = omnia_cmd_write_u8(client, CMD_LED_MODE, +@@ -309,6 +385,12 @@ static int omnia_leds_probe(struct i2c_c + + mutex_init(&leds->lock); + ++ ret = devm_led_trigger_register(dev, &omnia_hw_trigger); ++ if (ret < 0) { ++ dev_err(dev, "Cannot register private LED trigger: %d\n", ret); ++ return ret; ++ } ++ + led = &leds->leds[0]; + for_each_available_child_of_node(np, child) { + ret = omnia_led_register(client, led, child); diff --git a/target/linux/generic/backport-6.6/815-v6.7-4-leds-turris-omnia-Add-support-for-enabling-disabling.patch b/target/linux/generic/backport-6.6/815-v6.7-4-leds-turris-omnia-Add-support-for-enabling-disabling.patch new file mode 100644 index 0000000000..e98397922d --- /dev/null +++ b/target/linux/generic/backport-6.6/815-v6.7-4-leds-turris-omnia-Add-support-for-enabling-disabling.patch @@ -0,0 +1,244 @@ +From 0efb3f9609d3de5a7d8c31e3835d7eb3e6adce79 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= +Date: Mon, 18 Sep 2023 18:11:04 +0200 +Subject: [PATCH 6/6] leds: turris-omnia: Add support for enabling/disabling HW + gamma correction +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If the MCU on Turris Omnia is running newer firmware versions, the LED +controller supports RGB gamma correction (and enables it by default for +newer boards). + +Determine whether the gamma correction setting feature is supported and +add the ability to set it via sysfs attribute file. + +Signed-off-by: Marek Behún +Link: https://lore.kernel.org/r/20230918161104.20860-5-kabel@kernel.org +Signed-off-by: Lee Jones +--- + .../sysfs-class-led-driver-turris-omnia | 14 ++ + drivers/leds/leds-turris-omnia.c | 137 +++++++++++++++--- + 2 files changed, 134 insertions(+), 17 deletions(-) + +--- a/Documentation/ABI/testing/sysfs-class-led-driver-turris-omnia ++++ b/Documentation/ABI/testing/sysfs-class-led-driver-turris-omnia +@@ -12,3 +12,17 @@ Description: (RW) On the front panel of + able to change this setting from software. + + Format: %i ++ ++What: /sys/class/leds//device/gamma_correction ++Date: August 2023 ++KernelVersion: 6.6 ++Contact: Marek Behún ++Description: (RW) Newer versions of the microcontroller firmware of the ++ Turris Omnia router support gamma correction for the RGB LEDs. ++ This feature can be enabled/disabled by writing to this file. ++ ++ If the feature is not supported because the MCU firmware is too ++ old, the file always reads as 0, and writing to the file results ++ in the EOPNOTSUPP error. ++ ++ Format: %i +--- a/drivers/leds/leds-turris-omnia.c ++++ b/drivers/leds/leds-turris-omnia.c +@@ -15,17 +15,30 @@ + #define OMNIA_BOARD_LEDS 12 + #define OMNIA_LED_NUM_CHANNELS 3 + +-#define CMD_LED_MODE 3 +-#define CMD_LED_MODE_LED(l) ((l) & 0x0f) +-#define CMD_LED_MODE_USER 0x10 +- +-#define CMD_LED_STATE 4 +-#define CMD_LED_STATE_LED(l) ((l) & 0x0f) +-#define CMD_LED_STATE_ON 0x10 +- +-#define CMD_LED_COLOR 5 +-#define CMD_LED_SET_BRIGHTNESS 7 +-#define CMD_LED_GET_BRIGHTNESS 8 ++/* MCU controller commands at I2C address 0x2a */ ++#define OMNIA_MCU_I2C_ADDR 0x2a ++ ++#define CMD_GET_STATUS_WORD 0x01 ++#define STS_FEATURES_SUPPORTED BIT(2) ++ ++#define CMD_GET_FEATURES 0x10 ++#define FEAT_LED_GAMMA_CORRECTION BIT(5) ++ ++/* LED controller commands at I2C address 0x2b */ ++#define CMD_LED_MODE 0x03 ++#define CMD_LED_MODE_LED(l) ((l) & 0x0f) ++#define CMD_LED_MODE_USER 0x10 ++ ++#define CMD_LED_STATE 0x04 ++#define CMD_LED_STATE_LED(l) ((l) & 0x0f) ++#define CMD_LED_STATE_ON 0x10 ++ ++#define CMD_LED_COLOR 0x05 ++#define CMD_LED_SET_BRIGHTNESS 0x07 ++#define CMD_LED_GET_BRIGHTNESS 0x08 ++ ++#define CMD_SET_GAMMA_CORRECTION 0x30 ++#define CMD_GET_GAMMA_CORRECTION 0x31 + + struct omnia_led { + struct led_classdev_mc mc_cdev; +@@ -40,6 +53,7 @@ struct omnia_led { + struct omnia_leds { + struct i2c_client *client; + struct mutex lock; ++ bool has_gamma_correction; + struct omnia_led leds[]; + }; + +@@ -50,30 +64,42 @@ static int omnia_cmd_write_u8(const stru + return i2c_master_send(client, buf, sizeof(buf)); + } + +-static int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd) ++static int omnia_cmd_read_raw(struct i2c_adapter *adapter, u8 addr, u8 cmd, ++ void *reply, size_t len) + { + struct i2c_msg msgs[2]; +- u8 reply; + int ret; + +- msgs[0].addr = client->addr; ++ msgs[0].addr = addr; + msgs[0].flags = 0; + msgs[0].len = 1; + msgs[0].buf = &cmd; +- msgs[1].addr = client->addr; ++ msgs[1].addr = addr; + msgs[1].flags = I2C_M_RD; +- msgs[1].len = 1; +- msgs[1].buf = &reply; ++ msgs[1].len = len; ++ msgs[1].buf = reply; + +- ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); ++ ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs)); + if (likely(ret == ARRAY_SIZE(msgs))) +- return reply; ++ return len; + else if (ret < 0) + return ret; + else + return -EIO; + } + ++static int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd) ++{ ++ u8 reply; ++ int ret; ++ ++ ret = omnia_cmd_read_raw(client->adapter, client->addr, cmd, &reply, 1); ++ if (ret < 0) ++ return ret; ++ ++ return reply; ++} ++ + static int omnia_led_send_color_cmd(const struct i2c_client *client, + struct omnia_led *led) + { +@@ -352,12 +378,74 @@ static ssize_t brightness_store(struct d + } + static DEVICE_ATTR_RW(brightness); + ++static ssize_t gamma_correction_show(struct device *dev, ++ struct device_attribute *a, char *buf) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct omnia_leds *leds = i2c_get_clientdata(client); ++ int ret; ++ ++ if (leds->has_gamma_correction) { ++ ret = omnia_cmd_read_u8(client, CMD_GET_GAMMA_CORRECTION); ++ if (ret < 0) ++ return ret; ++ } else { ++ ret = 0; ++ } ++ ++ return sysfs_emit(buf, "%d\n", !!ret); ++} ++ ++static ssize_t gamma_correction_store(struct device *dev, ++ struct device_attribute *a, ++ const char *buf, size_t count) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct omnia_leds *leds = i2c_get_clientdata(client); ++ bool val; ++ int ret; ++ ++ if (!leds->has_gamma_correction) ++ return -EOPNOTSUPP; ++ ++ if (kstrtobool(buf, &val) < 0) ++ return -EINVAL; ++ ++ ret = omnia_cmd_write_u8(client, CMD_SET_GAMMA_CORRECTION, val); ++ ++ return ret < 0 ? ret : count; ++} ++static DEVICE_ATTR_RW(gamma_correction); ++ + static struct attribute *omnia_led_controller_attrs[] = { + &dev_attr_brightness.attr, ++ &dev_attr_gamma_correction.attr, + NULL, + }; + ATTRIBUTE_GROUPS(omnia_led_controller); + ++static int omnia_mcu_get_features(const struct i2c_client *client) ++{ ++ u16 reply; ++ int err; ++ ++ err = omnia_cmd_read_raw(client->adapter, OMNIA_MCU_I2C_ADDR, ++ CMD_GET_STATUS_WORD, &reply, sizeof(reply)); ++ if (err < 0) ++ return err; ++ ++ /* Check whether MCU firmware supports the CMD_GET_FEAUTRES command */ ++ if (!(le16_to_cpu(reply) & STS_FEATURES_SUPPORTED)) ++ return 0; ++ ++ err = omnia_cmd_read_raw(client->adapter, OMNIA_MCU_I2C_ADDR, ++ CMD_GET_FEATURES, &reply, sizeof(reply)); ++ if (err < 0) ++ return err; ++ ++ return le16_to_cpu(reply); ++} ++ + static int omnia_leds_probe(struct i2c_client *client, + const struct i2c_device_id *id) + { +@@ -383,6 +471,21 @@ static int omnia_leds_probe(struct i2c_c + leds->client = client; + i2c_set_clientdata(client, leds); + ++ ret = omnia_mcu_get_features(client); ++ if (ret < 0) { ++ dev_err(dev, "Cannot determine MCU supported features: %d\n", ++ ret); ++ return ret; ++ } ++ ++ leds->has_gamma_correction = ret & FEAT_LED_GAMMA_CORRECTION; ++ if (!leds->has_gamma_correction) { ++ dev_info(dev, ++ "Your board's MCU firmware does not support the LED gamma correction feature.\n"); ++ dev_info(dev, ++ "Consider upgrading MCU firmware with the omnia-mcutool utility.\n"); ++ } ++ + mutex_init(&leds->lock); + + ret = devm_led_trigger_register(dev, &omnia_hw_trigger); diff --git a/target/linux/generic/backport-6.6/815-v6.7-5-leds-turris-omnia-Fix-brightness-setting-and-trigger.patch b/target/linux/generic/backport-6.6/815-v6.7-5-leds-turris-omnia-Fix-brightness-setting-and-trigger.patch new file mode 100644 index 0000000000..b0cebdcf14 --- /dev/null +++ b/target/linux/generic/backport-6.6/815-v6.7-5-leds-turris-omnia-Fix-brightness-setting-and-trigger.patch @@ -0,0 +1,167 @@ +From ffec49d391c5f0195360912b216aa24dbc9b53c8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= +Date: Mon, 16 Oct 2023 16:15:38 +0200 +Subject: [PATCH] leds: turris-omnia: Fix brightness setting and trigger + activating +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +I have improperly refactored commits + 4d5ed2621c24 ("leds: turris-omnia: Make set_brightness() more efficient") +and + aaf38273cf76 ("leds: turris-omnia: Support HW controlled mode via private trigger") +after Lee requested a change in API semantics of the new functions I +introduced in commit + 28350bc0ac77 ("leds: turris-omnia: Do not use SMBUS calls"). + +Before the change, the function omnia_cmd_write_u8() returned 0 on +success, and afterwards it returned a positive value (number of bytes +written). The latter version was applied, but the following commits did +not properly account for this change. + +This results in non-functional LED's .brightness_set_blocking() and +trigger's .activate() methods. + +The main reasoning behind the semantics change was that read/write +methods should return the number of read/written bytes on success. +It was pointed to me [1] that this is not always true (for example the +regmap API does not do so), and since the driver never uses this number +of read/written bytes information, I decided to fix this issue by +changing the functions to the original semantics (return 0 on success). + +[1] https://lore.kernel.org/linux-gpio/ZQnn+Gi0xVlsGCYA@smile.fi.intel.com/ + +Fixes: 28350bc0ac77 ("leds: turris-omnia: Do not use SMBUS calls") +Signed-off-by: Marek Behún +--- + drivers/leds/leds-turris-omnia.c | 37 +++++++++++++++++--------------- + 1 file changed, 20 insertions(+), 17 deletions(-) + +--- a/drivers/leds/leds-turris-omnia.c ++++ b/drivers/leds/leds-turris-omnia.c +@@ -60,8 +60,11 @@ struct omnia_leds { + static int omnia_cmd_write_u8(const struct i2c_client *client, u8 cmd, u8 val) + { + u8 buf[2] = { cmd, val }; ++ int ret; ++ ++ ret = i2c_master_send(client, buf, sizeof(buf)); + +- return i2c_master_send(client, buf, sizeof(buf)); ++ return ret < 0 ? ret : 0; + } + + static int omnia_cmd_read_raw(struct i2c_adapter *adapter, u8 addr, u8 cmd, +@@ -81,7 +84,7 @@ static int omnia_cmd_read_raw(struct i2c + + ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs)); + if (likely(ret == ARRAY_SIZE(msgs))) +- return len; ++ return 0; + else if (ret < 0) + return ret; + else +@@ -91,11 +94,11 @@ static int omnia_cmd_read_raw(struct i2c + static int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd) + { + u8 reply; +- int ret; ++ int err; + +- ret = omnia_cmd_read_raw(client->adapter, client->addr, cmd, &reply, 1); +- if (ret < 0) +- return ret; ++ err = omnia_cmd_read_raw(client->adapter, client->addr, cmd, &reply, 1); ++ if (err) ++ return err; + + return reply; + } +@@ -236,7 +239,7 @@ static void omnia_hwtrig_deactivate(stru + + mutex_unlock(&leds->lock); + +- if (err < 0) ++ if (err) + dev_err(cdev->dev, "Cannot put LED to software mode: %i\n", + err); + } +@@ -302,7 +305,7 @@ static int omnia_led_register(struct i2c + ret = omnia_cmd_write_u8(client, CMD_LED_MODE, + CMD_LED_MODE_LED(led->reg) | + CMD_LED_MODE_USER); +- if (ret < 0) { ++ if (ret) { + dev_err(dev, "Cannot set LED %pOF to software mode: %i\n", np, + ret); + return ret; +@@ -311,7 +314,7 @@ static int omnia_led_register(struct i2c + /* disable the LED */ + ret = omnia_cmd_write_u8(client, CMD_LED_STATE, + CMD_LED_STATE_LED(led->reg)); +- if (ret < 0) { ++ if (ret) { + dev_err(dev, "Cannot set LED %pOF brightness: %i\n", np, ret); + return ret; + } +@@ -364,7 +367,7 @@ static ssize_t brightness_store(struct d + { + struct i2c_client *client = to_i2c_client(dev); + unsigned long brightness; +- int ret; ++ int err; + + if (kstrtoul(buf, 10, &brightness)) + return -EINVAL; +@@ -372,9 +375,9 @@ static ssize_t brightness_store(struct d + if (brightness > 100) + return -EINVAL; + +- ret = omnia_cmd_write_u8(client, CMD_LED_SET_BRIGHTNESS, brightness); ++ err = omnia_cmd_write_u8(client, CMD_LED_SET_BRIGHTNESS, brightness); + +- return ret < 0 ? ret : count; ++ return err ?: count; + } + static DEVICE_ATTR_RW(brightness); + +@@ -403,7 +406,7 @@ static ssize_t gamma_correction_store(st + struct i2c_client *client = to_i2c_client(dev); + struct omnia_leds *leds = i2c_get_clientdata(client); + bool val; +- int ret; ++ int err; + + if (!leds->has_gamma_correction) + return -EOPNOTSUPP; +@@ -411,9 +414,9 @@ static ssize_t gamma_correction_store(st + if (kstrtobool(buf, &val) < 0) + return -EINVAL; + +- ret = omnia_cmd_write_u8(client, CMD_SET_GAMMA_CORRECTION, val); ++ err = omnia_cmd_write_u8(client, CMD_SET_GAMMA_CORRECTION, val); + +- return ret < 0 ? ret : count; ++ return err ?: count; + } + static DEVICE_ATTR_RW(gamma_correction); + +@@ -431,7 +434,7 @@ static int omnia_mcu_get_features(const + + err = omnia_cmd_read_raw(client->adapter, OMNIA_MCU_I2C_ADDR, + CMD_GET_STATUS_WORD, &reply, sizeof(reply)); +- if (err < 0) ++ if (err) + return err; + + /* Check whether MCU firmware supports the CMD_GET_FEAUTRES command */ +@@ -440,7 +443,7 @@ static int omnia_mcu_get_features(const + + err = omnia_cmd_read_raw(client->adapter, OMNIA_MCU_I2C_ADDR, + CMD_GET_FEATURES, &reply, sizeof(reply)); +- if (err < 0) ++ if (err) + return err; + + return le16_to_cpu(reply); diff --git a/target/linux/generic/backport-6.6/816-v6.7-0001-nvmem-qfprom-Mark-core-clk-as-optional.patch b/target/linux/generic/backport-6.6/816-v6.7-0001-nvmem-qfprom-Mark-core-clk-as-optional.patch new file mode 100644 index 0000000000..66d4028140 --- /dev/null +++ b/target/linux/generic/backport-6.6/816-v6.7-0001-nvmem-qfprom-Mark-core-clk-as-optional.patch @@ -0,0 +1,37 @@ +From 16724d6ea40a2c9315f5a0d81005dfa4d7a6da24 Mon Sep 17 00:00:00 2001 +From: Luca Weiss +Date: Fri, 20 Oct 2023 11:55:40 +0100 +Subject: [PATCH] nvmem: qfprom: Mark core clk as optional + +On some platforms like sc7280 on non-ChromeOS devices the core clock +cannot be touched by Linux so we cannot provide it. Mark it as optional +as accessing qfprom for reading works without it but we still prohibit +writing if we cannot provide the clock. + +Signed-off-by: Luca Weiss +Reviewed-by: Douglas Anderson +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231020105545.216052-2-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/qfprom.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/nvmem/qfprom.c ++++ b/drivers/nvmem/qfprom.c +@@ -423,12 +423,12 @@ static int qfprom_probe(struct platform_ + if (IS_ERR(priv->vcc)) + return PTR_ERR(priv->vcc); + +- priv->secclk = devm_clk_get(dev, "core"); ++ priv->secclk = devm_clk_get_optional(dev, "core"); + 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) ++ /* Only enable writing if we have SoC data and a valid clock */ ++ if (priv->soc_data && priv->secclk) + econfig.reg_write = qfprom_reg_write; + } + diff --git a/target/linux/generic/backport-6.6/816-v6.7-0002-nvmem-add-explicit-config-option-to-read-old-syntax-.patch b/target/linux/generic/backport-6.6/816-v6.7-0002-nvmem-add-explicit-config-option-to-read-old-syntax-.patch new file mode 100644 index 0000000000..35b15776fb --- /dev/null +++ b/target/linux/generic/backport-6.6/816-v6.7-0002-nvmem-add-explicit-config-option-to-read-old-syntax-.patch @@ -0,0 +1,330 @@ +From 2cc3b37f5b6df8189d55d0e812d9658ce256dfec Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Fri, 20 Oct 2023 11:55:41 +0100 +Subject: [PATCH] nvmem: add explicit config option to read old syntax fixed OF + cells +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Binding for fixed NVMEM cells defined directly as NVMEM device subnodes +has been deprecated. It has been replaced by the "fixed-layout" NVMEM +layout binding. + +New syntax is meant to be clearer and should help avoiding imprecise +bindings. + +NVMEM subsystem already supports the new binding. It should be a good +idea to limit support for old syntax to existing drivers that actually +support & use it (we can't break backward compatibility!). That way we +additionally encourage new bindings & drivers to ignore deprecated +binding. + +It wasn't clear (to me) if rtc and w1 code actually uses old syntax +fixed cells. I enabled them to don't risk any breakage. + +Signed-off-by: Rafał Miłecki +[for meson-{efuse,mx-efuse}.c] +Acked-by: Martin Blumenstingl +[for mtk-efuse.c, nvmem/core.c, nvmem-provider.h] +Reviewed-by: AngeloGioacchino Del Regno +[MT8192, MT8195 Chromebooks] +Tested-by: AngeloGioacchino Del Regno +[for microchip-otpc.c] +Reviewed-by: Claudiu Beznea +[SAMA7G5-EK] +Tested-by: Claudiu Beznea +Acked-by: Jernej Skrabec +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231020105545.216052-3-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/mtd/mtdcore.c | 2 ++ + drivers/nvmem/apple-efuses.c | 1 + + drivers/nvmem/core.c | 8 +++++--- + drivers/nvmem/imx-ocotp-scu.c | 1 + + drivers/nvmem/imx-ocotp.c | 1 + + drivers/nvmem/meson-efuse.c | 1 + + drivers/nvmem/meson-mx-efuse.c | 1 + + drivers/nvmem/microchip-otpc.c | 1 + + drivers/nvmem/mtk-efuse.c | 1 + + drivers/nvmem/qcom-spmi-sdam.c | 1 + + drivers/nvmem/qfprom.c | 1 + + drivers/nvmem/rave-sp-eeprom.c | 1 + + drivers/nvmem/rockchip-efuse.c | 1 + + drivers/nvmem/sc27xx-efuse.c | 1 + + drivers/nvmem/sec-qfprom.c | 1 + + drivers/nvmem/sprd-efuse.c | 1 + + drivers/nvmem/stm32-romem.c | 1 + + drivers/nvmem/sunplus-ocotp.c | 1 + + drivers/nvmem/sunxi_sid.c | 1 + + drivers/nvmem/uniphier-efuse.c | 1 + + drivers/nvmem/zynqmp_nvmem.c | 1 + + drivers/rtc/nvmem.c | 1 + + drivers/w1/slaves/w1_ds250x.c | 1 + + include/linux/nvmem-provider.h | 2 ++ + 24 files changed, 30 insertions(+), 3 deletions(-) + +--- a/drivers/mtd/mtdcore.c ++++ b/drivers/mtd/mtdcore.c +@@ -523,6 +523,7 @@ static int mtd_nvmem_add(struct mtd_info + config.dev = &mtd->dev; + config.name = dev_name(&mtd->dev); + config.owner = THIS_MODULE; ++ config.add_legacy_fixed_of_cells = of_device_is_compatible(node, "nvmem-cells"); + config.reg_read = mtd_nvmem_reg_read; + config.size = mtd->size; + config.word_size = 1; +@@ -891,6 +892,7 @@ static struct nvmem_device *mtd_otp_nvme + config.name = compatible; + config.id = NVMEM_DEVID_AUTO; + config.owner = THIS_MODULE; ++ config.add_legacy_fixed_of_cells = true; + config.type = NVMEM_TYPE_OTP; + config.root_only = true; + config.ignore_wp = true; +--- a/drivers/nvmem/apple-efuses.c ++++ b/drivers/nvmem/apple-efuses.c +@@ -36,6 +36,7 @@ static int apple_efuses_probe(struct pla + struct resource *res; + struct nvmem_config config = { + .dev = &pdev->dev, ++ .add_legacy_fixed_of_cells = true, + .read_only = true, + .reg_read = apple_efuses_read, + .stride = sizeof(u32), +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -998,9 +998,11 @@ struct nvmem_device *nvmem_register(cons + if (rval) + goto err_remove_cells; + +- rval = nvmem_add_cells_from_legacy_of(nvmem); +- if (rval) +- goto err_remove_cells; ++ if (config->add_legacy_fixed_of_cells) { ++ rval = nvmem_add_cells_from_legacy_of(nvmem); ++ if (rval) ++ goto err_remove_cells; ++ } + + rval = nvmem_add_cells_from_fixed_layout(nvmem); + if (rval) +--- a/drivers/nvmem/imx-ocotp-scu.c ++++ b/drivers/nvmem/imx-ocotp-scu.c +@@ -220,6 +220,7 @@ static int imx_scu_ocotp_write(void *con + + static struct nvmem_config imx_scu_ocotp_nvmem_config = { + .name = "imx-scu-ocotp", ++ .add_legacy_fixed_of_cells = true, + .read_only = false, + .word_size = 4, + .stride = 1, +--- a/drivers/nvmem/imx-ocotp.c ++++ b/drivers/nvmem/imx-ocotp.c +@@ -616,6 +616,7 @@ static int imx_ocotp_probe(struct platfo + return PTR_ERR(priv->clk); + + priv->params = of_device_get_match_data(&pdev->dev); ++ imx_ocotp_nvmem_config.add_legacy_fixed_of_cells = true; + imx_ocotp_nvmem_config.size = 4 * priv->params->nregs; + imx_ocotp_nvmem_config.dev = dev; + imx_ocotp_nvmem_config.priv = priv; +--- a/drivers/nvmem/meson-efuse.c ++++ b/drivers/nvmem/meson-efuse.c +@@ -93,6 +93,7 @@ static int meson_efuse_probe(struct plat + + econfig->dev = dev; + econfig->name = dev_name(dev); ++ econfig->add_legacy_fixed_of_cells = true; + econfig->stride = 1; + econfig->word_size = 1; + econfig->reg_read = meson_efuse_read; +--- a/drivers/nvmem/meson-mx-efuse.c ++++ b/drivers/nvmem/meson-mx-efuse.c +@@ -211,6 +211,7 @@ static int meson_mx_efuse_probe(struct p + efuse->config.owner = THIS_MODULE; + efuse->config.dev = &pdev->dev; + efuse->config.priv = efuse; ++ efuse->config.add_legacy_fixed_of_cells = true; + efuse->config.stride = drvdata->word_size; + efuse->config.word_size = drvdata->word_size; + efuse->config.size = SZ_512; +--- a/drivers/nvmem/microchip-otpc.c ++++ b/drivers/nvmem/microchip-otpc.c +@@ -261,6 +261,7 @@ static int mchp_otpc_probe(struct platfo + return ret; + + mchp_nvmem_config.dev = otpc->dev; ++ mchp_nvmem_config.add_legacy_fixed_of_cells = true; + mchp_nvmem_config.size = size; + mchp_nvmem_config.priv = otpc; + nvmem = devm_nvmem_register(&pdev->dev, &mchp_nvmem_config); +--- a/drivers/nvmem/mtk-efuse.c ++++ b/drivers/nvmem/mtk-efuse.c +@@ -83,6 +83,7 @@ static int mtk_efuse_probe(struct platfo + return PTR_ERR(priv->base); + + pdata = device_get_match_data(dev); ++ econfig.add_legacy_fixed_of_cells = true; + econfig.stride = 1; + econfig.word_size = 1; + econfig.reg_read = mtk_reg_read; +--- a/drivers/nvmem/qcom-spmi-sdam.c ++++ b/drivers/nvmem/qcom-spmi-sdam.c +@@ -142,6 +142,7 @@ static int sdam_probe(struct platform_de + sdam->sdam_config.name = "spmi_sdam"; + sdam->sdam_config.id = NVMEM_DEVID_AUTO; + sdam->sdam_config.owner = THIS_MODULE; ++ sdam->sdam_config.add_legacy_fixed_of_cells = true; + sdam->sdam_config.stride = 1; + sdam->sdam_config.word_size = 1; + sdam->sdam_config.reg_read = sdam_read; +--- a/drivers/nvmem/qfprom.c ++++ b/drivers/nvmem/qfprom.c +@@ -357,6 +357,7 @@ static int qfprom_probe(struct platform_ + { + struct nvmem_config econfig = { + .name = "qfprom", ++ .add_legacy_fixed_of_cells = true, + .stride = 1, + .word_size = 1, + .id = NVMEM_DEVID_AUTO, +--- a/drivers/nvmem/rave-sp-eeprom.c ++++ b/drivers/nvmem/rave-sp-eeprom.c +@@ -328,6 +328,7 @@ static int rave_sp_eeprom_probe(struct p + of_property_read_string(np, "zii,eeprom-name", &config.name); + config.priv = eeprom; + config.dev = dev; ++ config.add_legacy_fixed_of_cells = true; + config.size = size; + config.reg_read = rave_sp_eeprom_reg_read; + config.reg_write = rave_sp_eeprom_reg_write; +--- a/drivers/nvmem/rockchip-efuse.c ++++ b/drivers/nvmem/rockchip-efuse.c +@@ -205,6 +205,7 @@ static int rockchip_rk3399_efuse_read(vo + + static struct nvmem_config econfig = { + .name = "rockchip-efuse", ++ .add_legacy_fixed_of_cells = true, + .stride = 1, + .word_size = 1, + .read_only = true, +--- a/drivers/nvmem/sc27xx-efuse.c ++++ b/drivers/nvmem/sc27xx-efuse.c +@@ -248,6 +248,7 @@ static int sc27xx_efuse_probe(struct pla + econfig.reg_read = sc27xx_efuse_read; + econfig.priv = efuse; + econfig.dev = &pdev->dev; ++ econfig.add_legacy_fixed_of_cells = true; + nvmem = devm_nvmem_register(&pdev->dev, &econfig); + if (IS_ERR(nvmem)) { + dev_err(&pdev->dev, "failed to register nvmem config\n"); +--- a/drivers/nvmem/sec-qfprom.c ++++ b/drivers/nvmem/sec-qfprom.c +@@ -47,6 +47,7 @@ static int sec_qfprom_probe(struct platf + { + struct nvmem_config econfig = { + .name = "sec-qfprom", ++ .add_legacy_fixed_of_cells = true, + .stride = 1, + .word_size = 1, + .id = NVMEM_DEVID_AUTO, +--- a/drivers/nvmem/sprd-efuse.c ++++ b/drivers/nvmem/sprd-efuse.c +@@ -408,6 +408,7 @@ static int sprd_efuse_probe(struct platf + econfig.read_only = false; + econfig.name = "sprd-efuse"; + econfig.size = efuse->data->blk_nums * SPRD_EFUSE_BLOCK_WIDTH; ++ econfig.add_legacy_fixed_of_cells = true; + econfig.reg_read = sprd_efuse_read; + econfig.reg_write = sprd_efuse_write; + econfig.priv = efuse; +--- a/drivers/nvmem/stm32-romem.c ++++ b/drivers/nvmem/stm32-romem.c +@@ -207,6 +207,7 @@ static int stm32_romem_probe(struct plat + priv->cfg.priv = priv; + priv->cfg.owner = THIS_MODULE; + priv->cfg.type = NVMEM_TYPE_OTP; ++ priv->cfg.add_legacy_fixed_of_cells = true; + + priv->lower = 0; + +--- a/drivers/nvmem/sunplus-ocotp.c ++++ b/drivers/nvmem/sunplus-ocotp.c +@@ -145,6 +145,7 @@ disable_clk: + + static struct nvmem_config sp_ocotp_nvmem_config = { + .name = "sp-ocotp", ++ .add_legacy_fixed_of_cells = true, + .read_only = true, + .word_size = 1, + .size = QAC628_OTP_SIZE, +--- a/drivers/nvmem/sunxi_sid.c ++++ b/drivers/nvmem/sunxi_sid.c +@@ -154,6 +154,7 @@ static int sunxi_sid_probe(struct platfo + nvmem_cfg->dev = dev; + nvmem_cfg->name = "sunxi-sid"; + nvmem_cfg->type = NVMEM_TYPE_OTP; ++ nvmem_cfg->add_legacy_fixed_of_cells = true; + nvmem_cfg->read_only = true; + nvmem_cfg->size = cfg->size; + nvmem_cfg->word_size = 1; +--- a/drivers/nvmem/uniphier-efuse.c ++++ b/drivers/nvmem/uniphier-efuse.c +@@ -52,6 +52,7 @@ static int uniphier_efuse_probe(struct p + econfig.size = resource_size(res); + econfig.priv = priv; + econfig.dev = dev; ++ econfig.add_legacy_fixed_of_cells = true; + nvmem = devm_nvmem_register(dev, &econfig); + + return PTR_ERR_OR_ZERO(nvmem); +--- a/drivers/nvmem/zynqmp_nvmem.c ++++ b/drivers/nvmem/zynqmp_nvmem.c +@@ -58,6 +58,7 @@ static int zynqmp_nvmem_probe(struct pla + + priv->dev = dev; + econfig.dev = dev; ++ econfig.add_legacy_fixed_of_cells = true; + econfig.reg_read = zynqmp_nvmem_read; + econfig.priv = priv; + +--- a/drivers/rtc/nvmem.c ++++ b/drivers/rtc/nvmem.c +@@ -21,6 +21,7 @@ int devm_rtc_nvmem_register(struct rtc_d + + nvmem_config->dev = dev; + nvmem_config->owner = rtc->owner; ++ nvmem_config->add_legacy_fixed_of_cells = true; + nvmem = devm_nvmem_register(dev, nvmem_config); + if (IS_ERR(nvmem)) + dev_err(dev, "failed to register nvmem device for RTC\n"); +--- a/drivers/w1/slaves/w1_ds250x.c ++++ b/drivers/w1/slaves/w1_ds250x.c +@@ -168,6 +168,7 @@ static int w1_eprom_add_slave(struct w1_ + struct nvmem_device *nvmem; + struct nvmem_config nvmem_cfg = { + .dev = &sl->dev, ++ .add_legacy_fixed_of_cells = true, + .reg_read = w1_nvmem_read, + .type = NVMEM_TYPE_OTP, + .read_only = true, +--- a/include/linux/nvmem-provider.h ++++ b/include/linux/nvmem-provider.h +@@ -82,6 +82,7 @@ struct nvmem_cell_info { + * @owner: Pointer to exporter module. Used for refcounting. + * @cells: Optional array of pre-defined NVMEM cells. + * @ncells: Number of elements in cells. ++ * @add_legacy_fixed_of_cells: Read fixed NVMEM cells from old OF syntax. + * @keepout: Optional array of keepout ranges (sorted ascending by start). + * @nkeepout: Number of elements in the keepout array. + * @type: Type of the nvmem storage +@@ -112,6 +113,7 @@ struct nvmem_config { + struct module *owner; + const struct nvmem_cell_info *cells; + int ncells; ++ bool add_legacy_fixed_of_cells; + const struct nvmem_keepout *keepout; + unsigned int nkeepout; + enum nvmem_type type; diff --git a/target/linux/generic/backport-6.6/816-v6.7-0003-nvmem-Use-device_get_match_data.patch b/target/linux/generic/backport-6.6/816-v6.7-0003-nvmem-Use-device_get_match_data.patch new file mode 100644 index 0000000000..84c0293982 --- /dev/null +++ b/target/linux/generic/backport-6.6/816-v6.7-0003-nvmem-Use-device_get_match_data.patch @@ -0,0 +1,77 @@ +From 0720219f4d34a88a9badb4de70cfad7585687d48 Mon Sep 17 00:00:00 2001 +From: Rob Herring +Date: Fri, 20 Oct 2023 11:55:45 +0100 +Subject: [PATCH] nvmem: Use device_get_match_data() + +Use preferred device_get_match_data() instead of of_match_device() to +get the driver match data. With this, adjust the includes to explicitly +include the correct headers. + +Signed-off-by: Rob Herring +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231020105545.216052-7-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/mxs-ocotp.c | 10 ++++------ + drivers/nvmem/stm32-romem.c | 7 ++++--- + 2 files changed, 8 insertions(+), 9 deletions(-) + +--- a/drivers/nvmem/mxs-ocotp.c ++++ b/drivers/nvmem/mxs-ocotp.c +@@ -13,8 +13,9 @@ + #include + #include + #include +-#include ++#include + #include ++#include + #include + #include + +@@ -140,11 +141,10 @@ static int mxs_ocotp_probe(struct platfo + struct device *dev = &pdev->dev; + const struct mxs_data *data; + struct mxs_ocotp *otp; +- const struct of_device_id *match; + int ret; + +- match = of_match_device(dev->driver->of_match_table, dev); +- if (!match || !match->data) ++ data = device_get_match_data(dev); ++ if (!data) + return -EINVAL; + + otp = devm_kzalloc(dev, sizeof(*otp), GFP_KERNEL); +@@ -169,8 +169,6 @@ static int mxs_ocotp_probe(struct platfo + if (ret) + return ret; + +- data = match->data; +- + ocotp_config.size = data->size; + ocotp_config.priv = otp; + ocotp_config.dev = dev; +--- a/drivers/nvmem/stm32-romem.c ++++ b/drivers/nvmem/stm32-romem.c +@@ -10,7 +10,9 @@ + #include + #include + #include +-#include ++#include ++#include ++#include + #include + + #include "stm32-bsec-optee-ta.h" +@@ -211,8 +213,7 @@ static int stm32_romem_probe(struct plat + + priv->lower = 0; + +- cfg = (const struct stm32_romem_cfg *) +- of_match_device(dev->driver->of_match_table, dev)->data; ++ cfg = device_get_match_data(dev); + if (!cfg) { + priv->cfg.read_only = true; + priv->cfg.size = resource_size(res); diff --git a/target/linux/generic/backport-6.6/816-v6.7-0004-Revert-nvmem-add-new-config-option.patch b/target/linux/generic/backport-6.6/816-v6.7-0004-Revert-nvmem-add-new-config-option.patch new file mode 100644 index 0000000000..7d80ad37f1 --- /dev/null +++ b/target/linux/generic/backport-6.6/816-v6.7-0004-Revert-nvmem-add-new-config-option.patch @@ -0,0 +1,77 @@ +From f4cf4e5db331a5ce69e3f0b21d322cac0f4e4b5d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Mon, 23 Oct 2023 12:27:59 +0200 +Subject: [PATCH] Revert "nvmem: add new config option" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This reverts commit 517f14d9cf3533d5ab4fded195ab6f80a92e378f. + +Config option "no_of_node" is no longer needed since adding a more +explicit and targeted option "add_legacy_fixed_of_cells". + +That "no_of_node" config option was needed *earlier* to help mtd's case. + +DT nodes of MTD partitions (that are also NVMEM devices) may contain +subnodes. Those SHOULD NOT be treated as NVMEM fixed cells. + +To prevent NVMEM core code from parsing subnodes a "no_of_node" option +was added (and set to true in mtd) to make for_each_child_of_node() in +NVMEM a no-op. That was a bit hacky because it was messing with +"of_node" pointer to achieve some side-effect. + +With the introduction of "add_legacy_fixed_of_cells" config option +things got more explicit. MTD subsystem simply tells NVMEM when to look +for fixed cells and there is no need to hack "of_node" pointer anymore. + +Signed-off-by: Rafał Miłecki +Reviewed-by: Miquel Raynal +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231023102759.31529-1-zajec5@gmail.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/mtd/mtdcore.c | 1 - + drivers/nvmem/core.c | 2 +- + include/linux/nvmem-provider.h | 2 -- + 3 files changed, 1 insertion(+), 4 deletions(-) + +--- a/drivers/mtd/mtdcore.c ++++ b/drivers/mtd/mtdcore.c +@@ -531,7 +531,6 @@ static int mtd_nvmem_add(struct mtd_info + config.read_only = true; + config.root_only = true; + config.ignore_wp = true; +- config.no_of_node = !of_device_is_compatible(node, "nvmem-cells"); + config.priv = mtd; + + mtd->nvmem = nvmem_register(&config); +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -936,7 +936,7 @@ struct nvmem_device *nvmem_register(cons + nvmem->nkeepout = config->nkeepout; + if (config->of_node) + nvmem->dev.of_node = config->of_node; +- else if (!config->no_of_node) ++ else + nvmem->dev.of_node = config->dev->of_node; + + switch (config->id) { +--- a/include/linux/nvmem-provider.h ++++ b/include/linux/nvmem-provider.h +@@ -89,7 +89,6 @@ struct nvmem_cell_info { + * @read_only: Device is read-only. + * @root_only: Device is accessibly to root only. + * @of_node: If given, this will be used instead of the parent's of_node. +- * @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. + * @size: Device size. +@@ -122,7 +121,6 @@ struct nvmem_config { + bool ignore_wp; + struct nvmem_layout *layout; + struct device_node *of_node; +- bool no_of_node; + nvmem_reg_read_t reg_read; + nvmem_reg_write_t reg_write; + int size; diff --git a/target/linux/generic/backport-6.6/816-v6.7-0005-nvmem-Do-not-expect-fixed-layouts-to-grab-a-layout-d.patch b/target/linux/generic/backport-6.6/816-v6.7-0005-nvmem-Do-not-expect-fixed-layouts-to-grab-a-layout-d.patch new file mode 100644 index 0000000000..bd5ceaabf7 --- /dev/null +++ b/target/linux/generic/backport-6.6/816-v6.7-0005-nvmem-Do-not-expect-fixed-layouts-to-grab-a-layout-d.patch @@ -0,0 +1,45 @@ +From b7c1e53751cb3990153084f31c41f25fde3b629c Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Signed-off-by: Miquel Raynal +Tested-by: Rafał Miłecki +Tested-by: Luca Ceresoli +Reviewed-by: Luca Ceresoli + +Link: https://lore.kernel.org/r/20231124193814.360552-1-miquel.raynal@bootlin.com +Signed-off-by: Greg Kroah-Hartman +--- + 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 diff --git a/target/linux/generic/backport-6.6/816-v6.7-0006-nvmem-brcm_nvram-store-a-copy-of-NVRAM-content.patch b/target/linux/generic/backport-6.6/816-v6.7-0006-nvmem-brcm_nvram-store-a-copy-of-NVRAM-content.patch new file mode 100644 index 0000000000..d49a20599d --- /dev/null +++ b/target/linux/generic/backport-6.6/816-v6.7-0006-nvmem-brcm_nvram-store-a-copy-of-NVRAM-content.patch @@ -0,0 +1,261 @@ +From 1e37bf84afacd5ba17b7a13a18ca2bc78aff05c0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +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: +Cc: Arınç ÜNAL +Cc: Florian Fainelli +Cc: Scott Branden +Signed-off-by: Rafał Miłecki +Acked-by: Arınç ÜNAL +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111358.316727-3-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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)); + } diff --git a/target/linux/generic/backport-6.6/818-v6.8-of-device-Export-of_device_make_bus_id.patch b/target/linux/generic/backport-6.6/818-v6.8-of-device-Export-of_device_make_bus_id.patch new file mode 100644 index 0000000000..564fe9822d --- /dev/null +++ b/target/linux/generic/backport-6.6/818-v6.8-of-device-Export-of_device_make_bus_id.patch @@ -0,0 +1,140 @@ +From 7f38b70042fcaa49219045bd1a9a2836e27a58ac Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +Date: Fri, 15 Dec 2023 11:15:27 +0000 +Subject: [PATCH] of: device: Export of_device_make_bus_id() + +This helper is really handy to create unique device names based on their +device tree path, we may need it outside of the OF core (in the NVMEM +subsystem) so let's export it. As this helper has nothing patform +specific, let's move it to of/device.c instead of of/platform.c so we +can add its prototype to of_device.h. + +Signed-off-by: Miquel Raynal +Acked-by: Rob Herring +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-2-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/of/device.c | 41 +++++++++++++++++++++++++++++++++++++++ + drivers/of/platform.c | 40 -------------------------------------- + include/linux/of_device.h | 6 ++++++ + 3 files changed, 47 insertions(+), 40 deletions(-) + +--- a/drivers/of/device.c ++++ b/drivers/of/device.c +@@ -395,3 +395,44 @@ int of_device_uevent_modalias(struct dev + return 0; + } + EXPORT_SYMBOL_GPL(of_device_uevent_modalias); ++ ++/** ++ * of_device_make_bus_id - Use the device node data to assign a unique name ++ * @dev: pointer to device structure that is linked to a device tree node ++ * ++ * This routine will first try using the translated bus address to ++ * derive a unique name. If it cannot, then it will prepend names from ++ * parent nodes until a unique name can be derived. ++ */ ++void of_device_make_bus_id(struct device *dev) ++{ ++ struct device_node *node = dev->of_node; ++ const __be32 *reg; ++ u64 addr; ++ u32 mask; ++ ++ /* Construct the name, using parent nodes if necessary to ensure uniqueness */ ++ while (node->parent) { ++ /* ++ * If the address can be translated, then that is as much ++ * uniqueness as we need. Make it the first component and return ++ */ ++ reg = of_get_property(node, "reg", NULL); ++ if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) { ++ if (!of_property_read_u32(node, "mask", &mask)) ++ dev_set_name(dev, dev_name(dev) ? "%llx.%x.%pOFn:%s" : "%llx.%x.%pOFn", ++ addr, ffs(mask) - 1, node, dev_name(dev)); ++ ++ else ++ dev_set_name(dev, dev_name(dev) ? "%llx.%pOFn:%s" : "%llx.%pOFn", ++ addr, node, dev_name(dev)); ++ return; ++ } ++ ++ /* format arguments only used if dev_name() resolves to NULL */ ++ dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s", ++ kbasename(node->full_name), dev_name(dev)); ++ node = node->parent; ++ } ++} ++EXPORT_SYMBOL_GPL(of_device_make_bus_id); +--- a/drivers/of/platform.c ++++ b/drivers/of/platform.c +@@ -64,46 +64,6 @@ EXPORT_SYMBOL(of_find_device_by_node); + */ + + /** +- * of_device_make_bus_id - Use the device node data to assign a unique name +- * @dev: pointer to device structure that is linked to a device tree node +- * +- * This routine will first try using the translated bus address to +- * derive a unique name. If it cannot, then it will prepend names from +- * parent nodes until a unique name can be derived. +- */ +-static void of_device_make_bus_id(struct device *dev) +-{ +- struct device_node *node = dev->of_node; +- const __be32 *reg; +- u64 addr; +- u32 mask; +- +- /* Construct the name, using parent nodes if necessary to ensure uniqueness */ +- while (node->parent) { +- /* +- * If the address can be translated, then that is as much +- * uniqueness as we need. Make it the first component and return +- */ +- reg = of_get_property(node, "reg", NULL); +- if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) { +- if (!of_property_read_u32(node, "mask", &mask)) +- dev_set_name(dev, dev_name(dev) ? "%llx.%x.%pOFn:%s" : "%llx.%x.%pOFn", +- addr, ffs(mask) - 1, node, dev_name(dev)); +- +- else +- dev_set_name(dev, dev_name(dev) ? "%llx.%pOFn:%s" : "%llx.%pOFn", +- addr, node, dev_name(dev)); +- return; +- } +- +- /* format arguments only used if dev_name() resolves to NULL */ +- dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s", +- kbasename(node->full_name), dev_name(dev)); +- node = node->parent; +- } +-} +- +-/** + * of_device_alloc - Allocate and initialize an of_device + * @np: device node to assign to device + * @bus_id: Name to assign to the device. May be null to use default name. +--- a/include/linux/of_device.h ++++ b/include/linux/of_device.h +@@ -56,6 +56,9 @@ static inline int of_dma_configure(struc + { + return of_dma_configure_id(dev, np, force_dma, NULL); + } ++ ++void of_device_make_bus_id(struct device *dev); ++ + #else /* CONFIG_OF */ + + static inline int of_driver_match_device(struct device *dev, +@@ -113,6 +116,9 @@ static inline int of_dma_configure(struc + { + return 0; + } ++ ++static inline void of_device_make_bus_id(struct device *dev) {} ++ + #endif /* CONFIG_OF */ + + #endif /* _LINUX_OF_DEVICE_H */ diff --git a/target/linux/generic/backport-6.6/819-v6.8-0001-nvmem-Move-of_nvmem_layout_get_container-in-another-.patch b/target/linux/generic/backport-6.6/819-v6.8-0001-nvmem-Move-of_nvmem_layout_get_container-in-another-.patch new file mode 100644 index 0000000000..2093fac8a1 --- /dev/null +++ b/target/linux/generic/backport-6.6/819-v6.8-0001-nvmem-Move-of_nvmem_layout_get_container-in-another-.patch @@ -0,0 +1,95 @@ +From 4a1a40233b4a9fc159a5c7a27dc34c5c7bc5be55 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +Date: Fri, 15 Dec 2023 11:15:28 +0000 +Subject: [PATCH] nvmem: Move of_nvmem_layout_get_container() in another header + +nvmem-consumer.h is included by consumer devices, extracting data from +NVMEM devices whereas nvmem-provider.h is included by devices providing +NVMEM content. + +The only users of of_nvmem_layout_get_container() outside of the core +are layout drivers, so better move its prototype to nvmem-provider.h. + +While we do so, we also move the kdoc associated with the function to +the header rather than the .c file. + +Signed-off-by: Miquel Raynal +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-3-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 8 -------- + include/linux/nvmem-consumer.h | 7 ------- + include/linux/nvmem-provider.h | 21 +++++++++++++++++++++ + 3 files changed, 21 insertions(+), 15 deletions(-) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -848,14 +848,6 @@ static int nvmem_add_cells_from_layout(s + } + + #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"); +--- a/include/linux/nvmem-consumer.h ++++ b/include/linux/nvmem-consumer.h +@@ -241,7 +241,6 @@ 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) +@@ -254,12 +253,6 @@ 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 NULL; +-} + #endif /* CONFIG_NVMEM && CONFIG_OF */ + + #endif /* ifndef _LINUX_NVMEM_CONSUMER_H */ +--- a/include/linux/nvmem-provider.h ++++ b/include/linux/nvmem-provider.h +@@ -244,6 +244,27 @@ nvmem_layout_get_match_data(struct nvmem + + #endif /* CONFIG_NVMEM */ + ++#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF) ++ ++/** ++ * of_nvmem_layout_get_container() - Get OF node of 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); ++ ++#else /* CONFIG_NVMEM && CONFIG_OF */ ++ ++static inline struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem) ++{ ++ return NULL; ++} ++ ++#endif /* CONFIG_NVMEM && CONFIG_OF */ ++ + #define module_nvmem_layout_driver(__layout_driver) \ + module_driver(__layout_driver, nvmem_layout_register, \ + nvmem_layout_unregister) diff --git a/target/linux/generic/backport-6.6/819-v6.8-0002-nvmem-Create-a-header-for-internal-sharing.patch b/target/linux/generic/backport-6.6/819-v6.8-0002-nvmem-Create-a-header-for-internal-sharing.patch new file mode 100644 index 0000000000..e722109f91 --- /dev/null +++ b/target/linux/generic/backport-6.6/819-v6.8-0002-nvmem-Create-a-header-for-internal-sharing.patch @@ -0,0 +1,91 @@ +From ec9c08a1cb8dc5e8e003f95f5f62de41dde235bb Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +Date: Fri, 15 Dec 2023 11:15:29 +0000 +Subject: [PATCH] nvmem: Create a header for internal sharing + +Before adding all the NVMEM layout bus infrastructure to the core, let's +move the main nvmem_device structure in an internal header, only +available to the core. This way all the additional code can be added in +a dedicated file in order to keep the current core file tidy. + +Signed-off-by: Miquel Raynal +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-4-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 24 +----------------------- + drivers/nvmem/internals.h | 35 +++++++++++++++++++++++++++++++++++ + 2 files changed, 36 insertions(+), 23 deletions(-) + create mode 100644 drivers/nvmem/internals.h + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -20,29 +20,7 @@ + #include + #include + +-struct nvmem_device { +- struct module *owner; +- struct device dev; +- int stride; +- int word_size; +- int id; +- struct kref refcnt; +- size_t size; +- bool read_only; +- bool root_only; +- int flags; +- enum nvmem_type type; +- struct bin_attribute eeprom; +- struct device *base_dev; +- struct list_head cells; +- const struct nvmem_keepout *keepout; +- unsigned int nkeepout; +- nvmem_reg_read_t reg_read; +- nvmem_reg_write_t reg_write; +- struct gpio_desc *wp_gpio; +- struct nvmem_layout *layout; +- void *priv; +-}; ++#include "internals.h" + + #define to_nvmem_device(d) container_of(d, struct nvmem_device, dev) + +--- /dev/null ++++ b/drivers/nvmem/internals.h +@@ -0,0 +1,35 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++ ++#ifndef _LINUX_NVMEM_INTERNALS_H ++#define _LINUX_NVMEM_INTERNALS_H ++ ++#include ++#include ++#include ++ ++struct nvmem_device { ++ struct module *owner; ++ struct device dev; ++ struct list_head node; ++ int stride; ++ int word_size; ++ int id; ++ struct kref refcnt; ++ size_t size; ++ bool read_only; ++ bool root_only; ++ int flags; ++ enum nvmem_type type; ++ struct bin_attribute eeprom; ++ struct device *base_dev; ++ struct list_head cells; ++ const struct nvmem_keepout *keepout; ++ unsigned int nkeepout; ++ nvmem_reg_read_t reg_read; ++ nvmem_reg_write_t reg_write; ++ struct gpio_desc *wp_gpio; ++ struct nvmem_layout *layout; ++ void *priv; ++}; ++ ++#endif /* ifndef _LINUX_NVMEM_INTERNALS_H */ diff --git a/target/linux/generic/backport-6.6/819-v6.8-0003-nvmem-Simplify-the-add_cells-hook.patch b/target/linux/generic/backport-6.6/819-v6.8-0003-nvmem-Simplify-the-add_cells-hook.patch new file mode 100644 index 0000000000..db2d8c1b46 --- /dev/null +++ b/target/linux/generic/backport-6.6/819-v6.8-0003-nvmem-Simplify-the-add_cells-hook.patch @@ -0,0 +1,79 @@ +From 1b7c298a4ecbc28cc6ee94005734bff55eb83d22 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +Date: Fri, 15 Dec 2023 11:15:30 +0000 +Subject: [PATCH] nvmem: Simplify the ->add_cells() hook + +The layout entry is not used and will anyway be made useless by the new +layout bus infrastructure coming next, so drop it. While at it, clarify +the kdoc entry. + +Signed-off-by: Miquel Raynal +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-5-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 2 +- + drivers/nvmem/layouts/onie-tlv.c | 3 +-- + drivers/nvmem/layouts/sl28vpd.c | 3 +-- + include/linux/nvmem-provider.h | 8 +++----- + 4 files changed, 6 insertions(+), 10 deletions(-) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -817,7 +817,7 @@ static int nvmem_add_cells_from_layout(s + int ret; + + if (layout && layout->add_cells) { +- ret = layout->add_cells(&nvmem->dev, nvmem, layout); ++ ret = layout->add_cells(&nvmem->dev, nvmem); + if (ret) + return ret; + } +--- a/drivers/nvmem/layouts/onie-tlv.c ++++ b/drivers/nvmem/layouts/onie-tlv.c +@@ -182,8 +182,7 @@ static bool onie_tlv_crc_is_valid(struct + return true; + } + +-static int onie_tlv_parse_table(struct device *dev, struct nvmem_device *nvmem, +- struct nvmem_layout *layout) ++static int onie_tlv_parse_table(struct device *dev, struct nvmem_device *nvmem) + { + struct onie_tlv_hdr hdr; + size_t table_len, data_len, hdr_len; +--- a/drivers/nvmem/layouts/sl28vpd.c ++++ b/drivers/nvmem/layouts/sl28vpd.c +@@ -80,8 +80,7 @@ static int sl28vpd_v1_check_crc(struct d + return 0; + } + +-static int sl28vpd_add_cells(struct device *dev, struct nvmem_device *nvmem, +- struct nvmem_layout *layout) ++static int sl28vpd_add_cells(struct device *dev, struct nvmem_device *nvmem) + { + const struct nvmem_cell_info *pinfo; + struct nvmem_cell_info info = {0}; +--- a/include/linux/nvmem-provider.h ++++ b/include/linux/nvmem-provider.h +@@ -156,9 +156,8 @@ struct nvmem_cell_table { + * + * @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(). ++ * @add_cells: Called to populate the layout using ++ * 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. +@@ -172,8 +171,7 @@ struct nvmem_cell_table { + 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); ++ int (*add_cells)(struct device *dev, struct nvmem_device *nvmem); + void (*fixup_cell_info)(struct nvmem_device *nvmem, + struct nvmem_layout *layout, + struct nvmem_cell_info *cell); diff --git a/target/linux/generic/backport-6.6/819-v6.8-0004-nvmem-Move-and-rename-fixup_cell_info.patch b/target/linux/generic/backport-6.6/819-v6.8-0004-nvmem-Move-and-rename-fixup_cell_info.patch new file mode 100644 index 0000000000..65aa37f834 --- /dev/null +++ b/target/linux/generic/backport-6.6/819-v6.8-0004-nvmem-Move-and-rename-fixup_cell_info.patch @@ -0,0 +1,169 @@ +From 1172460e716784ac7e1049a537bdca8edbf97360 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +Date: Fri, 15 Dec 2023 11:15:31 +0000 +Subject: [PATCH] nvmem: Move and rename ->fixup_cell_info() + +This hook is meant to be used by any provider and instantiating a layout +just for this is useless. Let's instead move this hook to the nvmem +device and add it to the config structure to be easily shared by the +providers. + +While at moving this hook, rename it ->fixup_dt_cell_info() to clarify +its main intended purpose. + +Signed-off-by: Miquel Raynal +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-6-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 6 +++--- + drivers/nvmem/imx-ocotp.c | 11 +++-------- + drivers/nvmem/internals.h | 2 ++ + drivers/nvmem/mtk-efuse.c | 11 +++-------- + include/linux/nvmem-provider.h | 9 ++++----- + 5 files changed, 15 insertions(+), 24 deletions(-) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -676,7 +676,6 @@ static int nvmem_validate_keepouts(struc + + 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; + struct device_node *child; + const __be32 *addr; +@@ -706,8 +705,8 @@ static int nvmem_add_cells_from_dt(struc + + info.np = of_node_get(child); + +- if (layout && layout->fixup_cell_info) +- layout->fixup_cell_info(nvmem, layout, &info); ++ if (nvmem->fixup_dt_cell_info) ++ nvmem->fixup_dt_cell_info(nvmem, &info); + + ret = nvmem_add_one_cell(nvmem, &info); + kfree(info.name); +@@ -896,6 +895,7 @@ struct nvmem_device *nvmem_register(cons + + kref_init(&nvmem->refcnt); + INIT_LIST_HEAD(&nvmem->cells); ++ nvmem->fixup_dt_cell_info = config->fixup_dt_cell_info; + + nvmem->owner = config->owner; + if (!nvmem->owner && config->dev->driver) +--- a/drivers/nvmem/imx-ocotp.c ++++ b/drivers/nvmem/imx-ocotp.c +@@ -584,17 +584,12 @@ 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) ++static void imx_ocotp_fixup_dt_cell_info(struct nvmem_device *nvmem, ++ struct nvmem_cell_info *cell) + { + cell->read_post_process = imx_ocotp_cell_pp; + } + +-static 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; +@@ -620,7 +615,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; +- imx_ocotp_nvmem_config.layout = &imx_ocotp_layout; ++ imx_ocotp_nvmem_config.fixup_dt_cell_info = &imx_ocotp_fixup_dt_cell_info; + + priv->config = &imx_ocotp_nvmem_config; + +--- a/drivers/nvmem/internals.h ++++ b/drivers/nvmem/internals.h +@@ -23,6 +23,8 @@ struct nvmem_device { + struct bin_attribute eeprom; + struct device *base_dev; + struct list_head cells; ++ void (*fixup_dt_cell_info)(struct nvmem_device *nvmem, ++ struct nvmem_cell_info *cell); + const struct nvmem_keepout *keepout; + unsigned int nkeepout; + nvmem_reg_read_t reg_read; +--- a/drivers/nvmem/mtk-efuse.c ++++ b/drivers/nvmem/mtk-efuse.c +@@ -45,9 +45,8 @@ static int mtk_efuse_gpu_speedbin_pp(voi + return 0; + } + +-static void mtk_efuse_fixup_cell_info(struct nvmem_device *nvmem, +- struct nvmem_layout *layout, +- struct nvmem_cell_info *cell) ++static void mtk_efuse_fixup_dt_cell_info(struct nvmem_device *nvmem, ++ struct nvmem_cell_info *cell) + { + size_t sz = strlen(cell->name); + +@@ -61,10 +60,6 @@ static void mtk_efuse_fixup_cell_info(st + 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; +@@ -91,7 +86,7 @@ static int mtk_efuse_probe(struct platfo + econfig.priv = priv; + econfig.dev = dev; + if (pdata->uses_post_processing) +- econfig.layout = &mtk_efuse_layout; ++ econfig.fixup_dt_cell_info = &mtk_efuse_fixup_dt_cell_info; + nvmem = devm_nvmem_register(dev, &econfig); + + return PTR_ERR_OR_ZERO(nvmem); +--- a/include/linux/nvmem-provider.h ++++ b/include/linux/nvmem-provider.h +@@ -83,6 +83,8 @@ struct nvmem_cell_info { + * @cells: Optional array of pre-defined NVMEM cells. + * @ncells: Number of elements in cells. + * @add_legacy_fixed_of_cells: Read fixed NVMEM cells from old OF syntax. ++ * @fixup_dt_cell_info: Will be called before a cell is added. Can be ++ * used to modify the nvmem_cell_info. + * @keepout: Optional array of keepout ranges (sorted ascending by start). + * @nkeepout: Number of elements in the keepout array. + * @type: Type of the nvmem storage +@@ -113,6 +115,8 @@ struct nvmem_config { + const struct nvmem_cell_info *cells; + int ncells; + bool add_legacy_fixed_of_cells; ++ void (*fixup_dt_cell_info)(struct nvmem_device *nvmem, ++ struct nvmem_cell_info *cell); + const struct nvmem_keepout *keepout; + unsigned int nkeepout; + enum nvmem_type type; +@@ -158,8 +162,6 @@ struct nvmem_cell_table { + * @of_match_table: Open firmware match table. + * @add_cells: Called to populate the layout using + * 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. + * +@@ -172,9 +174,6 @@ struct nvmem_layout { + const char *name; + const struct of_device_id *of_match_table; + int (*add_cells)(struct device *dev, struct nvmem_device *nvmem); +- void (*fixup_cell_info)(struct nvmem_device *nvmem, +- struct nvmem_layout *layout, +- struct nvmem_cell_info *cell); + + /* private */ + struct module *owner; diff --git a/target/linux/generic/backport-6.6/819-v6.8-0005-nvmem-core-Rework-layouts-to-become-regular-devices.patch b/target/linux/generic/backport-6.6/819-v6.8-0005-nvmem-core-Rework-layouts-to-become-regular-devices.patch new file mode 100644 index 0000000000..1881332340 --- /dev/null +++ b/target/linux/generic/backport-6.6/819-v6.8-0005-nvmem-core-Rework-layouts-to-become-regular-devices.patch @@ -0,0 +1,763 @@ +From fc29fd821d9ac2ae3d32a722fac39ce874efb883 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +Date: Fri, 15 Dec 2023 11:15:32 +0000 +Subject: [PATCH] nvmem: core: Rework layouts to become regular devices +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Current layout support was initially written without modules support in +mind. When the requirement for module support rose, the existing base +was improved to adopt modularization support, but kind of a design flaw +was introduced. With the existing implementation, when a storage device +registers into NVMEM, the core tries to hook a layout (if any) and +populates its cells immediately. This means, if the hardware description +expects a layout to be hooked up, but no driver was provided for that, +the storage medium will fail to probe and try later from +scratch. Even if we consider that the hardware description shall be +correct, we could still probe the storage device (especially if it +contains the rootfs). + +One way to overcome this situation is to consider the layouts as +devices, and leverage the native notifier mechanism. When a new NVMEM +device is registered, we can populate its nvmem-layout child, if any, +and wait for the matching to be done in order to get the cells (the +waiting can be easily done with the NVMEM notifiers). If the layout +driver is compiled as a module, it should automatically be loaded. This +way, there is no strong order to enforce, any NVMEM device creation +or NVMEM layout driver insertion will be observed as a new event which +may lead to the creation of additional cells, without disturbing the +probes with costly (and sometimes endless) deferrals. + +In order to achieve that goal we create a new bus for the nvmem-layouts +with minimal logic to match nvmem-layout devices with nvmem-layout +drivers. All this infrastructure code is created in the layouts.c file. + +Signed-off-by: Miquel Raynal +Tested-by: Rafał Miłecki +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-7-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/Kconfig | 1 + + drivers/nvmem/Makefile | 2 + + drivers/nvmem/core.c | 170 ++++++++++---------------- + drivers/nvmem/internals.h | 21 ++++ + drivers/nvmem/layouts.c | 201 +++++++++++++++++++++++++++++++ + drivers/nvmem/layouts/Kconfig | 8 ++ + drivers/nvmem/layouts/onie-tlv.c | 24 +++- + drivers/nvmem/layouts/sl28vpd.c | 24 +++- + include/linux/nvmem-provider.h | 38 +++--- + 9 files changed, 354 insertions(+), 135 deletions(-) + create mode 100644 drivers/nvmem/layouts.c + +--- a/drivers/nvmem/Kconfig ++++ b/drivers/nvmem/Kconfig +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0-only + menuconfig NVMEM + bool "NVMEM Support" ++ imply NVMEM_LAYOUTS + help + Support for NVMEM(Non Volatile Memory) devices like EEPROM, EFUSES... + +--- a/drivers/nvmem/Makefile ++++ b/drivers/nvmem/Makefile +@@ -5,6 +5,8 @@ + + obj-$(CONFIG_NVMEM) += nvmem_core.o + nvmem_core-y := core.o ++obj-$(CONFIG_NVMEM_LAYOUTS) += nvmem_layouts.o ++nvmem_layouts-y := layouts.o + obj-y += layouts/ + + # Devices +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -56,9 +56,6 @@ 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) + { +@@ -741,97 +738,22 @@ static int nvmem_add_cells_from_fixed_la + return err; + } + +-int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner) ++int nvmem_layout_register(struct nvmem_layout *layout) + { +- layout->owner = owner; +- +- spin_lock(&nvmem_layout_lock); +- list_add(&layout->node, &nvmem_layouts); +- spin_unlock(&nvmem_layout_lock); +- +- blocking_notifier_call_chain(&nvmem_notifier, NVMEM_LAYOUT_ADD, layout); ++ if (!layout->add_cells) ++ return -EINVAL; + +- return 0; ++ /* Populate the cells */ ++ return layout->add_cells(&layout->nvmem->dev, layout->nvmem); + } +-EXPORT_SYMBOL_GPL(__nvmem_layout_register); ++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); ++ /* Keep the API even with an empty stub in case we need it later */ + } + EXPORT_SYMBOL_GPL(nvmem_layout_unregister); + +-static struct nvmem_layout *nvmem_layout_get(struct nvmem_device *nvmem) +-{ +- struct device_node *layout_np; +- struct nvmem_layout *l, *layout = ERR_PTR(-EPROBE_DEFER); +- +- layout_np = of_nvmem_layout_get_container(nvmem); +- 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 +- * we'll never have any match. +- */ +- of_request_module(layout_np); +- +- 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); +- if (ret) +- return ret; +- } +- +- return 0; +-} +- +-#if IS_ENABLED(CONFIG_OF) +-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) + { +@@ -839,7 +761,7 @@ const void *nvmem_layout_get_match_data( + const struct of_device_id *match; + + layout_np = of_nvmem_layout_get_container(nvmem); +- match = of_match_node(layout->of_match_table, layout_np); ++ match = of_match_node(layout->dev.driver->of_match_table, layout_np); + + return match ? match->data : NULL; + } +@@ -951,19 +873,6 @@ 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 (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); + if (rval) +@@ -984,24 +893,24 @@ 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; +- + dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name); + + rval = device_add(&nvmem->dev); + if (rval) + goto err_remove_cells; + ++ rval = nvmem_populate_layout(nvmem); ++ if (rval) ++ goto err_remove_dev; ++ + blocking_notifier_call_chain(&nvmem_notifier, NVMEM_ADD, nvmem); + + return nvmem; + ++err_remove_dev: ++ device_del(&nvmem->dev); + 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: +@@ -1023,7 +932,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); ++ nvmem_destroy_layout(nvmem); + device_unregister(&nvmem->dev); + } + +@@ -1325,6 +1234,12 @@ nvmem_cell_get_from_lookup(struct device + return cell; + } + ++static void nvmem_layout_module_put(struct nvmem_device *nvmem) ++{ ++ if (nvmem->layout && nvmem->layout->dev.driver) ++ module_put(nvmem->layout->dev.driver->owner); ++} ++ + #if IS_ENABLED(CONFIG_OF) + static struct nvmem_cell_entry * + nvmem_find_cell_entry_by_node(struct nvmem_device *nvmem, struct device_node *np) +@@ -1343,6 +1258,18 @@ nvmem_find_cell_entry_by_node(struct nvm + return cell; + } + ++static int nvmem_layout_module_get_optional(struct nvmem_device *nvmem) ++{ ++ if (!nvmem->layout) ++ return 0; ++ ++ if (!nvmem->layout->dev.driver || ++ !try_module_get(nvmem->layout->dev.driver->owner)) ++ return -EPROBE_DEFER; ++ ++ return 0; ++} ++ + /** + * of_nvmem_cell_get() - Get a nvmem cell from given device node and cell id + * +@@ -1405,16 +1332,29 @@ struct nvmem_cell *of_nvmem_cell_get(str + return ERR_CAST(nvmem); + } + ++ ret = nvmem_layout_module_get_optional(nvmem); ++ if (ret) { ++ of_node_put(cell_np); ++ __nvmem_device_put(nvmem); ++ return ERR_PTR(ret); ++ } ++ + cell_entry = nvmem_find_cell_entry_by_node(nvmem, cell_np); + of_node_put(cell_np); + if (!cell_entry) { + __nvmem_device_put(nvmem); +- return ERR_PTR(-ENOENT); ++ nvmem_layout_module_put(nvmem); ++ if (nvmem->layout) ++ return ERR_PTR(-EPROBE_DEFER); ++ else ++ return ERR_PTR(-ENOENT); + } + + cell = nvmem_create_cell(cell_entry, id, cell_index); +- if (IS_ERR(cell)) ++ if (IS_ERR(cell)) { + __nvmem_device_put(nvmem); ++ nvmem_layout_module_put(nvmem); ++ } + + return cell; + } +@@ -1528,6 +1468,7 @@ void nvmem_cell_put(struct nvmem_cell *c + + kfree(cell); + __nvmem_device_put(nvmem); ++ nvmem_layout_module_put(nvmem); + } + EXPORT_SYMBOL_GPL(nvmem_cell_put); + +@@ -2105,11 +2046,22 @@ EXPORT_SYMBOL_GPL(nvmem_dev_name); + + static int __init nvmem_init(void) + { +- return bus_register(&nvmem_bus_type); ++ int ret; ++ ++ ret = bus_register(&nvmem_bus_type); ++ if (ret) ++ return ret; ++ ++ ret = nvmem_layout_bus_register(); ++ if (ret) ++ bus_unregister(&nvmem_bus_type); ++ ++ return ret; + } + + static void __exit nvmem_exit(void) + { ++ nvmem_layout_bus_unregister(); + bus_unregister(&nvmem_bus_type); + } + +--- a/drivers/nvmem/internals.h ++++ b/drivers/nvmem/internals.h +@@ -34,4 +34,25 @@ struct nvmem_device { + void *priv; + }; + ++#if IS_ENABLED(CONFIG_OF) ++int nvmem_layout_bus_register(void); ++void nvmem_layout_bus_unregister(void); ++int nvmem_populate_layout(struct nvmem_device *nvmem); ++void nvmem_destroy_layout(struct nvmem_device *nvmem); ++#else /* CONFIG_OF */ ++static inline int nvmem_layout_bus_register(void) ++{ ++ return 0; ++} ++ ++static inline void nvmem_layout_bus_unregister(void) {} ++ ++static inline int nvmem_populate_layout(struct nvmem_device *nvmem) ++{ ++ return 0; ++} ++ ++static inline void nvmem_destroy_layout(struct nvmem_device *nvmem) { } ++#endif /* CONFIG_OF */ ++ + #endif /* ifndef _LINUX_NVMEM_INTERNALS_H */ +--- /dev/null ++++ b/drivers/nvmem/layouts.c +@@ -0,0 +1,201 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * NVMEM layout bus handling ++ * ++ * Copyright (C) 2023 Bootlin ++ * Author: Miquel Raynal ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "internals.h" ++ ++#define to_nvmem_layout_driver(drv) \ ++ (container_of((drv), struct nvmem_layout_driver, driver)) ++#define to_nvmem_layout_device(_dev) \ ++ container_of((_dev), struct nvmem_layout, dev) ++ ++static int nvmem_layout_bus_match(struct device *dev, struct device_driver *drv) ++{ ++ return of_driver_match_device(dev, drv); ++} ++ ++static int nvmem_layout_bus_probe(struct device *dev) ++{ ++ struct nvmem_layout_driver *drv = to_nvmem_layout_driver(dev->driver); ++ struct nvmem_layout *layout = to_nvmem_layout_device(dev); ++ ++ if (!drv->probe || !drv->remove) ++ return -EINVAL; ++ ++ return drv->probe(layout); ++} ++ ++static void nvmem_layout_bus_remove(struct device *dev) ++{ ++ struct nvmem_layout_driver *drv = to_nvmem_layout_driver(dev->driver); ++ struct nvmem_layout *layout = to_nvmem_layout_device(dev); ++ ++ return drv->remove(layout); ++} ++ ++static struct bus_type nvmem_layout_bus_type = { ++ .name = "nvmem-layout", ++ .match = nvmem_layout_bus_match, ++ .probe = nvmem_layout_bus_probe, ++ .remove = nvmem_layout_bus_remove, ++}; ++ ++int nvmem_layout_driver_register(struct nvmem_layout_driver *drv) ++{ ++ drv->driver.bus = &nvmem_layout_bus_type; ++ ++ return driver_register(&drv->driver); ++} ++EXPORT_SYMBOL_GPL(nvmem_layout_driver_register); ++ ++void nvmem_layout_driver_unregister(struct nvmem_layout_driver *drv) ++{ ++ driver_unregister(&drv->driver); ++} ++EXPORT_SYMBOL_GPL(nvmem_layout_driver_unregister); ++ ++static void nvmem_layout_release_device(struct device *dev) ++{ ++ struct nvmem_layout *layout = to_nvmem_layout_device(dev); ++ ++ of_node_put(layout->dev.of_node); ++ kfree(layout); ++} ++ ++static int nvmem_layout_create_device(struct nvmem_device *nvmem, ++ struct device_node *np) ++{ ++ struct nvmem_layout *layout; ++ struct device *dev; ++ int ret; ++ ++ layout = kzalloc(sizeof(*layout), GFP_KERNEL); ++ if (!layout) ++ return -ENOMEM; ++ ++ /* Create a bidirectional link */ ++ layout->nvmem = nvmem; ++ nvmem->layout = layout; ++ ++ /* Device model registration */ ++ dev = &layout->dev; ++ device_initialize(dev); ++ dev->parent = &nvmem->dev; ++ dev->bus = &nvmem_layout_bus_type; ++ dev->release = nvmem_layout_release_device; ++ dev->coherent_dma_mask = DMA_BIT_MASK(32); ++ dev->dma_mask = &dev->coherent_dma_mask; ++ device_set_node(dev, of_fwnode_handle(of_node_get(np))); ++ of_device_make_bus_id(dev); ++ of_msi_configure(dev, dev->of_node); ++ ++ ret = device_add(dev); ++ if (ret) { ++ put_device(dev); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static const struct of_device_id of_nvmem_layout_skip_table[] = { ++ { .compatible = "fixed-layout", }, ++ {} ++}; ++ ++static int nvmem_layout_bus_populate(struct nvmem_device *nvmem, ++ struct device_node *layout_dn) ++{ ++ int ret; ++ ++ /* Make sure it has a compatible property */ ++ if (!of_get_property(layout_dn, "compatible", NULL)) { ++ pr_debug("%s() - skipping %pOF, no compatible prop\n", ++ __func__, layout_dn); ++ return 0; ++ } ++ ++ /* Fixed layouts are parsed manually somewhere else for now */ ++ if (of_match_node(of_nvmem_layout_skip_table, layout_dn)) { ++ pr_debug("%s() - skipping %pOF node\n", __func__, layout_dn); ++ return 0; ++ } ++ ++ if (of_node_check_flag(layout_dn, OF_POPULATED_BUS)) { ++ pr_debug("%s() - skipping %pOF, already populated\n", ++ __func__, layout_dn); ++ ++ return 0; ++ } ++ ++ /* NVMEM layout buses expect only a single device representing the layout */ ++ ret = nvmem_layout_create_device(nvmem, layout_dn); ++ if (ret) ++ return ret; ++ ++ of_node_set_flag(layout_dn, OF_POPULATED_BUS); ++ ++ return 0; ++} ++ ++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); ++ ++/* ++ * Returns the number of devices populated, 0 if the operation was not relevant ++ * for this nvmem device, an error code otherwise. ++ */ ++int nvmem_populate_layout(struct nvmem_device *nvmem) ++{ ++ struct device_node *layout_dn; ++ int ret; ++ ++ layout_dn = of_nvmem_layout_get_container(nvmem); ++ if (!layout_dn) ++ return 0; ++ ++ /* Populate the layout device */ ++ device_links_supplier_sync_state_pause(); ++ ret = nvmem_layout_bus_populate(nvmem, layout_dn); ++ device_links_supplier_sync_state_resume(); ++ ++ of_node_put(layout_dn); ++ return ret; ++} ++ ++void nvmem_destroy_layout(struct nvmem_device *nvmem) ++{ ++ struct device *dev; ++ ++ if (!nvmem->layout) ++ return; ++ ++ dev = &nvmem->layout->dev; ++ of_node_clear_flag(dev->of_node, OF_POPULATED_BUS); ++ device_unregister(dev); ++} ++ ++int nvmem_layout_bus_register(void) ++{ ++ return bus_register(&nvmem_layout_bus_type); ++} ++ ++void nvmem_layout_bus_unregister(void) ++{ ++ bus_unregister(&nvmem_layout_bus_type); ++} +--- a/drivers/nvmem/layouts/Kconfig ++++ b/drivers/nvmem/layouts/Kconfig +@@ -1,5 +1,11 @@ + # SPDX-License-Identifier: GPL-2.0 + ++config NVMEM_LAYOUTS ++ bool ++ depends on OF ++ ++if NVMEM_LAYOUTS ++ + menu "Layout Types" + + config NVMEM_LAYOUT_SL28_VPD +@@ -21,3 +27,5 @@ config NVMEM_LAYOUT_ONIE_TLV + If unsure, say N. + + endmenu ++ ++endif +--- a/drivers/nvmem/layouts/onie-tlv.c ++++ b/drivers/nvmem/layouts/onie-tlv.c +@@ -225,16 +225,32 @@ static int onie_tlv_parse_table(struct d + return 0; + } + ++static int onie_tlv_probe(struct nvmem_layout *layout) ++{ ++ layout->add_cells = onie_tlv_parse_table; ++ ++ return nvmem_layout_register(layout); ++} ++ ++static void onie_tlv_remove(struct nvmem_layout *layout) ++{ ++ nvmem_layout_unregister(layout); ++} ++ + 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 struct nvmem_layout_driver onie_tlv_layout = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "onie-tlv-layout", ++ .of_match_table = onie_tlv_of_match_table, ++ }, ++ .probe = onie_tlv_probe, ++ .remove = onie_tlv_remove, + }; + module_nvmem_layout_driver(onie_tlv_layout); + +--- a/drivers/nvmem/layouts/sl28vpd.c ++++ b/drivers/nvmem/layouts/sl28vpd.c +@@ -134,16 +134,32 @@ static int sl28vpd_add_cells(struct devi + return 0; + } + ++static int sl28vpd_probe(struct nvmem_layout *layout) ++{ ++ layout->add_cells = sl28vpd_add_cells; ++ ++ return nvmem_layout_register(layout); ++} ++ ++static void sl28vpd_remove(struct nvmem_layout *layout) ++{ ++ nvmem_layout_unregister(layout); ++} ++ + static const struct of_device_id sl28vpd_of_match_table[] = { + { .compatible = "kontron,sl28-vpd" }, + {}, + }; + MODULE_DEVICE_TABLE(of, sl28vpd_of_match_table); + +-static struct nvmem_layout sl28vpd_layout = { +- .name = "sl28-vpd", +- .of_match_table = sl28vpd_of_match_table, +- .add_cells = sl28vpd_add_cells, ++static struct nvmem_layout_driver sl28vpd_layout = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "kontron-sl28vpd-layout", ++ .of_match_table = sl28vpd_of_match_table, ++ }, ++ .probe = sl28vpd_probe, ++ .remove = sl28vpd_remove, + }; + module_nvmem_layout_driver(sl28vpd_layout); + +--- 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 + #include + #include + #include +@@ -158,12 +159,11 @@ struct nvmem_cell_table { + /** + * struct nvmem_layout - NVMEM layout definitions + * +- * @name: Layout name. +- * @of_match_table: Open firmware match table. +- * @add_cells: Called to populate the layout using +- * nvmem_add_one_cell(). +- * @owner: Pointer to struct module. +- * @node: List node. ++ * @dev: Device-model layout device. ++ * @nvmem: The underlying NVMEM device ++ * @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(). + * + * 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" +@@ -171,13 +171,15 @@ struct nvmem_cell_table { + * cells. + */ + struct nvmem_layout { +- const char *name; +- const struct of_device_id *of_match_table; ++ struct device dev; ++ struct nvmem_device *nvmem; + int (*add_cells)(struct device *dev, struct nvmem_device *nvmem); ++}; + +- /* private */ +- struct module *owner; +- struct list_head node; ++struct nvmem_layout_driver { ++ struct device_driver driver; ++ int (*probe)(struct nvmem_layout *layout); ++ void (*remove)(struct nvmem_layout *layout); + }; + + #if IS_ENABLED(CONFIG_NVMEM) +@@ -194,11 +196,15 @@ 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) ++int nvmem_layout_register(struct nvmem_layout *layout); + void nvmem_layout_unregister(struct nvmem_layout *layout); + ++int nvmem_layout_driver_register(struct nvmem_layout_driver *drv); ++void nvmem_layout_driver_unregister(struct nvmem_layout_driver *drv); ++#define module_nvmem_layout_driver(__nvmem_layout_driver) \ ++ module_driver(__nvmem_layout_driver, nvmem_layout_driver_register, \ ++ nvmem_layout_driver_unregister) ++ + const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem, + struct nvmem_layout *layout); + +@@ -262,8 +268,4 @@ static inline struct device_node *of_nvm + + #endif /* CONFIG_NVMEM && CONFIG_OF */ + +-#define module_nvmem_layout_driver(__layout_driver) \ +- module_driver(__layout_driver, nvmem_layout_register, \ +- nvmem_layout_unregister) +- + #endif /* ifndef _LINUX_NVMEM_PROVIDER_H */ diff --git a/target/linux/generic/backport-6.6/819-v6.8-0006-nvmem-core-Expose-cells-through-sysfs.patch b/target/linux/generic/backport-6.6/819-v6.8-0006-nvmem-core-Expose-cells-through-sysfs.patch new file mode 100644 index 0000000000..89872bec2e --- /dev/null +++ b/target/linux/generic/backport-6.6/819-v6.8-0006-nvmem-core-Expose-cells-through-sysfs.patch @@ -0,0 +1,240 @@ +From 0331c611949fffdf486652450901a4dc52bc5cca Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +Date: Fri, 15 Dec 2023 11:15:34 +0000 +Subject: [PATCH] nvmem: core: Expose cells through sysfs +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The binary content of nvmem devices is available to the user so in the +easiest cases, finding the content of a cell is rather easy as it is +just a matter of looking at a known and fixed offset. However, nvmem +layouts have been recently introduced to cope with more advanced +situations, where the offset and size of the cells is not known in +advance or is dynamic. When using layouts, more advanced parsers are +used by the kernel in order to give direct access to the content of each +cell, regardless of its position/size in the underlying +device. Unfortunately, these information are not accessible by users, +unless by fully re-implementing the parser logic in userland. + +Let's expose the cells and their content through sysfs to avoid these +situations. Of course the relevant NVMEM sysfs Kconfig option must be +enabled for this support to be available. + +Not all nvmem devices expose cells. Indeed, the .bin_attrs attribute +group member will be filled at runtime only when relevant and will +remain empty otherwise. In this case, as the cells attribute group will +be empty, it will not lead to any additional folder/file creation. + +Exposed cells are read-only. There is, in practice, everything in the +core to support a write path, but as I don't see any need for that, I +prefer to keep the interface simple (and probably safer). The interface +is documented as being in the "testing" state which means we can later +add a write attribute if though relevant. + +Signed-off-by: Miquel Raynal +Tested-by: Rafał Miłecki +Tested-by: Chen-Yu Tsai +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-9-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 135 +++++++++++++++++++++++++++++++++++++- + drivers/nvmem/internals.h | 1 + + 2 files changed, 135 insertions(+), 1 deletion(-) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -300,6 +300,43 @@ static umode_t nvmem_bin_attr_is_visible + return nvmem_bin_attr_get_umode(nvmem); + } + ++static struct nvmem_cell *nvmem_create_cell(struct nvmem_cell_entry *entry, ++ const char *id, int index); ++ ++static ssize_t nvmem_cell_attr_read(struct file *filp, struct kobject *kobj, ++ struct bin_attribute *attr, char *buf, ++ loff_t pos, size_t count) ++{ ++ struct nvmem_cell_entry *entry; ++ struct nvmem_cell *cell = NULL; ++ size_t cell_sz, read_len; ++ void *content; ++ ++ entry = attr->private; ++ cell = nvmem_create_cell(entry, entry->name, 0); ++ if (IS_ERR(cell)) ++ return PTR_ERR(cell); ++ ++ if (!cell) ++ return -EINVAL; ++ ++ content = nvmem_cell_read(cell, &cell_sz); ++ if (IS_ERR(content)) { ++ read_len = PTR_ERR(content); ++ goto destroy_cell; ++ } ++ ++ read_len = min_t(unsigned int, cell_sz - pos, count); ++ memcpy(buf, content + pos, read_len); ++ kfree(content); ++ ++destroy_cell: ++ kfree_const(cell->id); ++ kfree(cell); ++ ++ return read_len; ++} ++ + /* default read/write permissions */ + static struct bin_attribute bin_attr_rw_nvmem = { + .attr = { +@@ -321,11 +358,21 @@ static const struct attribute_group nvme + .is_bin_visible = nvmem_bin_attr_is_visible, + }; + ++/* Cell attributes will be dynamically allocated */ ++static struct attribute_group nvmem_cells_group = { ++ .name = "cells", ++}; ++ + static const struct attribute_group *nvmem_dev_groups[] = { + &nvmem_bin_group, + NULL, + }; + ++static const struct attribute_group *nvmem_cells_groups[] = { ++ &nvmem_cells_group, ++ NULL, ++}; ++ + static struct bin_attribute bin_attr_nvmem_eeprom_compat = { + .attr = { + .name = "eeprom", +@@ -381,6 +428,68 @@ static void nvmem_sysfs_remove_compat(st + device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); + } + ++static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem) ++{ ++ struct bin_attribute **cells_attrs, *attrs; ++ struct nvmem_cell_entry *entry; ++ unsigned int ncells = 0, i = 0; ++ int ret = 0; ++ ++ mutex_lock(&nvmem_mutex); ++ ++ if (list_empty(&nvmem->cells) || nvmem->sysfs_cells_populated) { ++ nvmem_cells_group.bin_attrs = NULL; ++ goto unlock_mutex; ++ } ++ ++ /* Allocate an array of attributes with a sentinel */ ++ ncells = list_count_nodes(&nvmem->cells); ++ cells_attrs = devm_kcalloc(&nvmem->dev, ncells + 1, ++ sizeof(struct bin_attribute *), GFP_KERNEL); ++ if (!cells_attrs) { ++ ret = -ENOMEM; ++ goto unlock_mutex; ++ } ++ ++ attrs = devm_kcalloc(&nvmem->dev, ncells, sizeof(struct bin_attribute), GFP_KERNEL); ++ if (!attrs) { ++ ret = -ENOMEM; ++ goto unlock_mutex; ++ } ++ ++ /* Initialize each attribute to take the name and size of the cell */ ++ list_for_each_entry(entry, &nvmem->cells, node) { ++ sysfs_bin_attr_init(&attrs[i]); ++ attrs[i].attr.name = devm_kasprintf(&nvmem->dev, GFP_KERNEL, ++ "%s@%x", entry->name, ++ entry->offset); ++ attrs[i].attr.mode = 0444; ++ attrs[i].size = entry->bytes; ++ attrs[i].read = &nvmem_cell_attr_read; ++ attrs[i].private = entry; ++ if (!attrs[i].attr.name) { ++ ret = -ENOMEM; ++ goto unlock_mutex; ++ } ++ ++ cells_attrs[i] = &attrs[i]; ++ i++; ++ } ++ ++ nvmem_cells_group.bin_attrs = cells_attrs; ++ ++ ret = devm_device_add_groups(&nvmem->dev, nvmem_cells_groups); ++ if (ret) ++ goto unlock_mutex; ++ ++ nvmem->sysfs_cells_populated = true; ++ ++unlock_mutex: ++ mutex_unlock(&nvmem_mutex); ++ ++ return ret; ++} ++ + #else /* CONFIG_NVMEM_SYSFS */ + + static int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem, +@@ -740,11 +849,25 @@ static int nvmem_add_cells_from_fixed_la + + int nvmem_layout_register(struct nvmem_layout *layout) + { ++ int ret; ++ + if (!layout->add_cells) + return -EINVAL; + + /* Populate the cells */ +- return layout->add_cells(&layout->nvmem->dev, layout->nvmem); ++ ret = layout->add_cells(&layout->nvmem->dev, layout->nvmem); ++ if (ret) ++ return ret; ++ ++#ifdef CONFIG_NVMEM_SYSFS ++ ret = nvmem_populate_sysfs_cells(layout->nvmem); ++ if (ret) { ++ nvmem_device_remove_all_cells(layout->nvmem); ++ return ret; ++ } ++#endif ++ ++ return 0; + } + EXPORT_SYMBOL_GPL(nvmem_layout_register); + +@@ -903,10 +1026,20 @@ struct nvmem_device *nvmem_register(cons + if (rval) + goto err_remove_dev; + ++#ifdef CONFIG_NVMEM_SYSFS ++ rval = nvmem_populate_sysfs_cells(nvmem); ++ if (rval) ++ goto err_destroy_layout; ++#endif ++ + blocking_notifier_call_chain(&nvmem_notifier, NVMEM_ADD, nvmem); + + return nvmem; + ++#ifdef CONFIG_NVMEM_SYSFS ++err_destroy_layout: ++ nvmem_destroy_layout(nvmem); ++#endif + err_remove_dev: + device_del(&nvmem->dev); + err_remove_cells: +--- a/drivers/nvmem/internals.h ++++ b/drivers/nvmem/internals.h +@@ -32,6 +32,7 @@ struct nvmem_device { + struct gpio_desc *wp_gpio; + struct nvmem_layout *layout; + void *priv; ++ bool sysfs_cells_populated; + }; + + #if IS_ENABLED(CONFIG_OF) diff --git a/target/linux/generic/backport-6.6/819-v6.8-0007-nvmem-stm32-add-support-for-STM32MP25-BSEC-to-contro.patch b/target/linux/generic/backport-6.6/819-v6.8-0007-nvmem-stm32-add-support-for-STM32MP25-BSEC-to-contro.patch new file mode 100644 index 0000000000..f686222f88 --- /dev/null +++ b/target/linux/generic/backport-6.6/819-v6.8-0007-nvmem-stm32-add-support-for-STM32MP25-BSEC-to-contro.patch @@ -0,0 +1,65 @@ +From f0ac5b23039610619ca4a4805528553ecb6bc815 Mon Sep 17 00:00:00 2001 +From: Patrick Delaunay +Date: Fri, 15 Dec 2023 11:15:36 +0000 +Subject: [PATCH] nvmem: stm32: add support for STM32MP25 BSEC to control OTP + data + +On STM32MP25, OTP area may be read/written by using BSEC (boot, security +and OTP control). The BSEC internal peripheral is only managed by the +secure world. + +The 12 Kbits of OTP (effective) are organized into the following regions: +- lower OTP (OTP0 to OTP127) = 4096 lower OTP bits, + bitwise (1-bit) programmable +- mid OTP (OTP128 to OTP255) = 4096 middle OTP bits, + bulk (32-bit) programmable +- upper OTP (OTP256 to OTP383) = 4096 upper OTP bits, + bulk (32-bit) programmable, + only accessible when BSEC is in closed state. + +As HWKEY and ECIES key are only accessible by ROM code; +only 368 OTP words are managed in this driver (OTP0 to OTP267). + +This patch adds the STM32MP25 configuration for reading and writing +the OTP data using the OP-TEE BSEC TA services. + +Signed-off-by: Patrick Delaunay +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-11-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/stm32-romem.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +--- a/drivers/nvmem/stm32-romem.c ++++ b/drivers/nvmem/stm32-romem.c +@@ -269,6 +269,19 @@ static const struct stm32_romem_cfg stm3 + .ta = true, + }; + ++/* ++ * STM32MP25 BSEC OTP: 3 regions of 32-bits data words ++ * lower OTP (OTP0 to OTP127), bitwise (1-bit) programmable ++ * mid OTP (OTP128 to OTP255), bulk (32-bit) programmable ++ * upper OTP (OTP256 to OTP383), bulk (32-bit) programmable ++ * but no access to HWKEY and ECIES key: limited at OTP367 ++ */ ++static const struct stm32_romem_cfg stm32mp25_bsec_cfg = { ++ .size = 368 * 4, ++ .lower = 127, ++ .ta = true, ++}; ++ + static const struct of_device_id stm32_romem_of_match[] __maybe_unused = { + { .compatible = "st,stm32f4-otp", }, { + .compatible = "st,stm32mp15-bsec", +@@ -276,6 +289,9 @@ static const struct of_device_id stm32_r + }, { + .compatible = "st,stm32mp13-bsec", + .data = (void *)&stm32mp13_bsec_cfg, ++ }, { ++ .compatible = "st,stm32mp25-bsec", ++ .data = (void *)&stm32mp25_bsec_cfg, + }, + { /* sentinel */ }, + }; diff --git a/target/linux/generic/backport-6.6/819-v6.8-0008-nvmem-layouts-refactor-.add_cells-callback-arguments.patch b/target/linux/generic/backport-6.6/819-v6.8-0008-nvmem-layouts-refactor-.add_cells-callback-arguments.patch new file mode 100644 index 0000000000..1bf3ba35b6 --- /dev/null +++ b/target/linux/generic/backport-6.6/819-v6.8-0008-nvmem-layouts-refactor-.add_cells-callback-arguments.patch @@ -0,0 +1,94 @@ +From 401df0d4f4098ecc9c5278da2f50756d62e5b37d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Tue, 19 Dec 2023 13:01:03 +0100 +Subject: [PATCH] nvmem: layouts: refactor .add_cells() callback arguments +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Simply pass whole "struct nvmem_layout" instead of single variables. +There is nothing in "struct nvmem_layout" that we have to hide from +layout drivers. They also access it during .probe() and .remove(). + +Thanks to this change: + +1. API gets more consistent + All layouts drivers callbacks get the same argument + +2. Layouts get correct device + Before this change NVMEM core code was passing NVMEM device instead + of layout device. That resulted in: + * Confusing prints + * Calling devm_*() helpers on wrong device + * Helpers like of_device_get_match_data() dereferencing NULLs + +3. It gets possible to get match data + First of all nvmem_layout_get_match_data() requires passing "struct + nvmem_layout" which .add_cells() callback didn't have before this. It + doesn't matter much as it's rather useless now anyway (and will be + dropped). + What's more important however is that of_device_get_match_data() can + be used now thanks to owning a proper device pointer. + +Signed-off-by: Rafał Miłecki +Reviewed-by: Miquel Raynal +Reviewed-by: Michael Walle +Link: https://lore.kernel.org/r/20231219120104.3422-1-zajec5@gmail.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 2 +- + drivers/nvmem/layouts/onie-tlv.c | 4 +++- + drivers/nvmem/layouts/sl28vpd.c | 4 +++- + include/linux/nvmem-provider.h | 2 +- + 4 files changed, 8 insertions(+), 4 deletions(-) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -855,7 +855,7 @@ int nvmem_layout_register(struct nvmem_l + return -EINVAL; + + /* Populate the cells */ +- ret = layout->add_cells(&layout->nvmem->dev, layout->nvmem); ++ ret = layout->add_cells(layout); + if (ret) + return ret; + +--- a/drivers/nvmem/layouts/onie-tlv.c ++++ b/drivers/nvmem/layouts/onie-tlv.c +@@ -182,8 +182,10 @@ static bool onie_tlv_crc_is_valid(struct + return true; + } + +-static int onie_tlv_parse_table(struct device *dev, struct nvmem_device *nvmem) ++static int onie_tlv_parse_table(struct nvmem_layout *layout) + { ++ struct nvmem_device *nvmem = layout->nvmem; ++ struct device *dev = &layout->dev; + struct onie_tlv_hdr hdr; + size_t table_len, data_len, hdr_len; + u8 *table, *data; +--- a/drivers/nvmem/layouts/sl28vpd.c ++++ b/drivers/nvmem/layouts/sl28vpd.c +@@ -80,8 +80,10 @@ static int sl28vpd_v1_check_crc(struct d + return 0; + } + +-static int sl28vpd_add_cells(struct device *dev, struct nvmem_device *nvmem) ++static int sl28vpd_add_cells(struct nvmem_layout *layout) + { ++ struct nvmem_device *nvmem = layout->nvmem; ++ struct device *dev = &layout->dev; + const struct nvmem_cell_info *pinfo; + struct nvmem_cell_info info = {0}; + struct device_node *layout_np; +--- a/include/linux/nvmem-provider.h ++++ b/include/linux/nvmem-provider.h +@@ -173,7 +173,7 @@ struct nvmem_cell_table { + struct nvmem_layout { + struct device dev; + struct nvmem_device *nvmem; +- int (*add_cells)(struct device *dev, struct nvmem_device *nvmem); ++ int (*add_cells)(struct nvmem_layout *layout); + }; + + struct nvmem_layout_driver { diff --git a/target/linux/generic/backport-6.6/819-v6.8-0009-nvmem-drop-nvmem_layout_get_match_data.patch b/target/linux/generic/backport-6.6/819-v6.8-0009-nvmem-drop-nvmem_layout_get_match_data.patch new file mode 100644 index 0000000000..514b5f2de5 --- /dev/null +++ b/target/linux/generic/backport-6.6/819-v6.8-0009-nvmem-drop-nvmem_layout_get_match_data.patch @@ -0,0 +1,72 @@ +From 43f60e3fb62edc7bd8891de8779fb422f4ae23ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Tue, 19 Dec 2023 13:01:04 +0100 +Subject: [PATCH] nvmem: drop nvmem_layout_get_match_data() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Thanks for layouts refactoring we now have "struct device" associated +with layout. Also its OF pointer points directly to the "nvmem-layout" +DT node. + +All it takes to get match data is a generic of_device_get_match_data(). + +Signed-off-by: Rafał Miłecki +Reviewed-by: Miquel Raynal +Reviewed-by: Michael Walle +Link: https://lore.kernel.org/r/20231219120104.3422-2-zajec5@gmail.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 13 ------------- + include/linux/nvmem-provider.h | 10 ---------- + 2 files changed, 23 deletions(-) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -877,19 +877,6 @@ void nvmem_layout_unregister(struct nvme + } + EXPORT_SYMBOL_GPL(nvmem_layout_unregister); + +-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->dev.driver->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 +--- a/include/linux/nvmem-provider.h ++++ b/include/linux/nvmem-provider.h +@@ -205,9 +205,6 @@ void nvmem_layout_driver_unregister(stru + module_driver(__nvmem_layout_driver, nvmem_layout_driver_register, \ + nvmem_layout_driver_unregister) + +-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) +@@ -238,13 +235,6 @@ static inline int nvmem_layout_register( + + 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 */ + + #if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF) diff --git a/target/linux/generic/backport-6.6/819-v6.8-0010-nvmem-core-add-nvmem_dev_size-helper.patch b/target/linux/generic/backport-6.6/819-v6.8-0010-nvmem-core-add-nvmem_dev_size-helper.patch new file mode 100644 index 0000000000..aa0bbaa0c5 --- /dev/null +++ b/target/linux/generic/backport-6.6/819-v6.8-0010-nvmem-core-add-nvmem_dev_size-helper.patch @@ -0,0 +1,53 @@ +From 33cf42e68efc8ff529a7eee08a4f0ba8c8d0a207 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Thu, 21 Dec 2023 18:34:17 +0100 +Subject: [PATCH] nvmem: core: add nvmem_dev_size() helper +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This is required by layouts that need to read whole NVMEM content. It's +especially useful for NVMEM devices without hardcoded layout (like +U-Boot environment data block). + +Signed-off-by: Rafał Miłecki +Reviewed-by: Miquel Raynal +Link: https://lore.kernel.org/r/20231221173421.13737-2-zajec5@gmail.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 13 +++++++++++++ + include/linux/nvmem-consumer.h | 1 + + 2 files changed, 14 insertions(+) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -2164,6 +2164,19 @@ const char *nvmem_dev_name(struct nvmem_ + } + EXPORT_SYMBOL_GPL(nvmem_dev_name); + ++/** ++ * nvmem_dev_size() - Get the size of a given nvmem device. ++ * ++ * @nvmem: nvmem device. ++ * ++ * Return: size of the nvmem device. ++ */ ++size_t nvmem_dev_size(struct nvmem_device *nvmem) ++{ ++ return nvmem->size; ++} ++EXPORT_SYMBOL_GPL(nvmem_dev_size); ++ + static int __init nvmem_init(void) + { + int ret; +--- a/include/linux/nvmem-consumer.h ++++ b/include/linux/nvmem-consumer.h +@@ -81,6 +81,7 @@ int nvmem_device_cell_write(struct nvmem + struct nvmem_cell_info *info, void *buf); + + const char *nvmem_dev_name(struct nvmem_device *nvmem); ++size_t nvmem_dev_size(struct nvmem_device *nvmem); + + void nvmem_add_cell_lookups(struct nvmem_cell_lookup *entries, + size_t nentries); diff --git a/target/linux/generic/backport-6.6/819-v6.8-0011-nvmem-u-boot-env-use-nvmem_add_one_cell-nvmem-subsys.patch b/target/linux/generic/backport-6.6/819-v6.8-0011-nvmem-u-boot-env-use-nvmem_add_one_cell-nvmem-subsys.patch new file mode 100644 index 0000000000..fc826f3f7e --- /dev/null +++ b/target/linux/generic/backport-6.6/819-v6.8-0011-nvmem-u-boot-env-use-nvmem_add_one_cell-nvmem-subsys.patch @@ -0,0 +1,126 @@ +From 7c8979b42b1a9c5604f431ba804928e55919263c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Thu, 21 Dec 2023 18:34:18 +0100 +Subject: [PATCH] nvmem: u-boot-env: use nvmem_add_one_cell() nvmem subsystem + helper +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Simplify adding NVMEM cells. + +Signed-off-by: Rafał Miłecki +Reviewed-by: Miquel Raynal +Link: https://lore.kernel.org/r/20231221173421.13737-3-zajec5@gmail.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/u-boot-env.c | 55 +++++++++++++++----------------------- + 1 file changed, 21 insertions(+), 34 deletions(-) + +--- a/drivers/nvmem/u-boot-env.c ++++ b/drivers/nvmem/u-boot-env.c +@@ -23,13 +23,10 @@ enum u_boot_env_format { + + struct u_boot_env { + struct device *dev; ++ struct nvmem_device *nvmem; + enum u_boot_env_format format; + + struct mtd_info *mtd; +- +- /* Cells */ +- struct nvmem_cell_info *cells; +- int ncells; + }; + + struct u_boot_env_image_single { +@@ -94,43 +91,36 @@ static int u_boot_env_read_post_process_ + static int u_boot_env_add_cells(struct u_boot_env *priv, uint8_t *buf, + size_t data_offset, size_t data_len) + { ++ struct nvmem_device *nvmem = priv->nvmem; + struct device *dev = priv->dev; + char *data = buf + data_offset; + char *var, *value, *eq; +- int idx; +- +- priv->ncells = 0; +- for (var = data; var < data + data_len && *var; var += strlen(var) + 1) +- priv->ncells++; +- +- priv->cells = devm_kcalloc(dev, priv->ncells, sizeof(*priv->cells), GFP_KERNEL); +- if (!priv->cells) +- return -ENOMEM; + +- for (var = data, idx = 0; ++ for (var = data; + var < data + data_len && *var; +- var = value + strlen(value) + 1, idx++) { ++ var = value + strlen(value) + 1) { ++ struct nvmem_cell_info info = {}; ++ + eq = strchr(var, '='); + if (!eq) + break; + *eq = '\0'; + value = eq + 1; + +- priv->cells[idx].name = devm_kstrdup(dev, var, GFP_KERNEL); +- if (!priv->cells[idx].name) ++ info.name = devm_kstrdup(dev, var, GFP_KERNEL); ++ if (!info.name) + return -ENOMEM; +- 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); ++ info.offset = data_offset + value - data; ++ info.bytes = strlen(value); ++ info.np = of_get_child_by_name(dev->of_node, info.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; ++ info.raw_len = strlen(value); ++ info.bytes = ETH_ALEN; ++ info.read_post_process = u_boot_env_read_post_process_ethaddr; + } +- } + +- if (WARN_ON(idx != priv->ncells)) +- priv->ncells = idx; ++ nvmem_add_one_cell(nvmem, &info); ++ } + + return 0; + } +@@ -209,7 +199,6 @@ static int u_boot_env_probe(struct platf + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct u_boot_env *priv; +- int err; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) +@@ -224,17 +213,15 @@ static int u_boot_env_probe(struct platf + return PTR_ERR(priv->mtd); + } + +- err = u_boot_env_parse(priv); +- if (err) +- return err; +- + config.dev = dev; +- config.cells = priv->cells; +- config.ncells = priv->ncells; + config.priv = priv; + config.size = priv->mtd->size; + +- return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config)); ++ priv->nvmem = devm_nvmem_register(dev, &config); ++ if (IS_ERR(priv->nvmem)) ++ return PTR_ERR(priv->nvmem); ++ ++ return u_boot_env_parse(priv); + } + + static const struct of_device_id u_boot_env_of_match_table[] = { diff --git a/target/linux/generic/backport-6.6/819-v6.8-0012-nvmem-u-boot-env-use-nvmem-device-helpers.patch b/target/linux/generic/backport-6.6/819-v6.8-0012-nvmem-u-boot-env-use-nvmem-device-helpers.patch new file mode 100644 index 0000000000..70abc7cf14 --- /dev/null +++ b/target/linux/generic/backport-6.6/819-v6.8-0012-nvmem-u-boot-env-use-nvmem-device-helpers.patch @@ -0,0 +1,81 @@ +From a832556d23c5a11115f300011a5874d6107a0d62 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Thu, 21 Dec 2023 18:34:19 +0100 +Subject: [PATCH] nvmem: u-boot-env: use nvmem device helpers +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Use nvmem_dev_size() and nvmem_device_read() to make this driver less +mtd dependent. + +Signed-off-by: Rafał Miłecki +Reviewed-by: Miquel Raynal +Link: https://lore.kernel.org/r/20231221173421.13737-4-zajec5@gmail.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/u-boot-env.c | 23 +++++++++++++++-------- + 1 file changed, 15 insertions(+), 8 deletions(-) + +--- a/drivers/nvmem/u-boot-env.c ++++ b/drivers/nvmem/u-boot-env.c +@@ -127,27 +127,34 @@ static int u_boot_env_add_cells(struct u + + static int u_boot_env_parse(struct u_boot_env *priv) + { ++ struct nvmem_device *nvmem = priv->nvmem; + struct device *dev = priv->dev; + size_t crc32_data_offset; + size_t crc32_data_len; + size_t crc32_offset; + size_t data_offset; + size_t data_len; ++ size_t dev_size; + uint32_t crc32; + uint32_t calc; +- size_t bytes; + uint8_t *buf; ++ int bytes; + int err; + +- buf = kcalloc(1, priv->mtd->size, GFP_KERNEL); ++ dev_size = nvmem_dev_size(nvmem); ++ ++ buf = kcalloc(1, dev_size, GFP_KERNEL); + if (!buf) { + err = -ENOMEM; + goto err_out; + } + +- err = mtd_read(priv->mtd, 0, priv->mtd->size, &bytes, buf); +- if ((err && !mtd_is_bitflip(err)) || bytes != priv->mtd->size) { +- dev_err(dev, "Failed to read from mtd: %d\n", err); ++ bytes = nvmem_device_read(nvmem, 0, dev_size, buf); ++ if (bytes < 0) { ++ err = bytes; ++ goto err_kfree; ++ } else if (bytes != dev_size) { ++ err = -EIO; + goto err_kfree; + } + +@@ -169,8 +176,8 @@ static int u_boot_env_parse(struct u_boo + break; + } + crc32 = le32_to_cpu(*(__le32 *)(buf + crc32_offset)); +- crc32_data_len = priv->mtd->size - crc32_data_offset; +- data_len = priv->mtd->size - data_offset; ++ crc32_data_len = dev_size - crc32_data_offset; ++ data_len = dev_size - data_offset; + + calc = crc32(~0, buf + crc32_data_offset, crc32_data_len) ^ ~0L; + if (calc != crc32) { +@@ -179,7 +186,7 @@ static int u_boot_env_parse(struct u_boo + goto err_kfree; + } + +- buf[priv->mtd->size - 1] = '\0'; ++ buf[dev_size - 1] = '\0'; + err = u_boot_env_add_cells(priv, buf, data_offset, data_len); + if (err) + dev_err(dev, "Failed to add cells: %d\n", err); diff --git a/target/linux/generic/backport-6.6/819-v6.8-0013-nvmem-u-boot-env-improve-coding-style.patch b/target/linux/generic/backport-6.6/819-v6.8-0013-nvmem-u-boot-env-improve-coding-style.patch new file mode 100644 index 0000000000..273cfed874 --- /dev/null +++ b/target/linux/generic/backport-6.6/819-v6.8-0013-nvmem-u-boot-env-improve-coding-style.patch @@ -0,0 +1,62 @@ +From 6bafe07c930676d6430be471310958070816a595 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Thu, 21 Dec 2023 18:34:20 +0100 +Subject: [PATCH] nvmem: u-boot-env: improve coding style +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +1. Prefer kzalloc() over kcalloc() + See memory-allocation.rst which says: "to be on the safe side it's + best to use routines that set memory to zero, like kzalloc()" +2. Drop dev_err() for u_boot_env_add_cells() fail + It can fail only on -ENOMEM. We don't want to print error then. +3. Add extra "crc32_addr" variable + It makes code reading header's crc32 easier to understand / review. + +Signed-off-by: Rafał Miłecki +Reviewed-by: Miquel Raynal +Link: https://lore.kernel.org/r/20231221173421.13737-5-zajec5@gmail.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/u-boot-env.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/nvmem/u-boot-env.c ++++ b/drivers/nvmem/u-boot-env.c +@@ -132,6 +132,7 @@ static int u_boot_env_parse(struct u_boo + size_t crc32_data_offset; + size_t crc32_data_len; + size_t crc32_offset; ++ __le32 *crc32_addr; + size_t data_offset; + size_t data_len; + size_t dev_size; +@@ -143,7 +144,7 @@ static int u_boot_env_parse(struct u_boo + + dev_size = nvmem_dev_size(nvmem); + +- buf = kcalloc(1, dev_size, GFP_KERNEL); ++ buf = kzalloc(dev_size, GFP_KERNEL); + if (!buf) { + err = -ENOMEM; + goto err_out; +@@ -175,7 +176,8 @@ static int u_boot_env_parse(struct u_boo + data_offset = offsetof(struct u_boot_env_image_broadcom, data); + break; + } +- crc32 = le32_to_cpu(*(__le32 *)(buf + crc32_offset)); ++ crc32_addr = (__le32 *)(buf + crc32_offset); ++ crc32 = le32_to_cpu(*crc32_addr); + crc32_data_len = dev_size - crc32_data_offset; + data_len = dev_size - data_offset; + +@@ -188,8 +190,6 @@ static int u_boot_env_parse(struct u_boo + + buf[dev_size - 1] = '\0'; + err = u_boot_env_add_cells(priv, buf, data_offset, data_len); +- if (err) +- dev_err(dev, "Failed to add cells: %d\n", err); + + err_kfree: + kfree(buf); diff --git a/target/linux/generic/backport-6.6/820-v6.4-net-phy-fix-circular-LEDS_CLASS-dependencies.patch b/target/linux/generic/backport-6.6/820-v6.4-net-phy-fix-circular-LEDS_CLASS-dependencies.patch new file mode 100644 index 0000000000..3b68403690 --- /dev/null +++ b/target/linux/generic/backport-6.6/820-v6.4-net-phy-fix-circular-LEDS_CLASS-dependencies.patch @@ -0,0 +1,65 @@ +From 4bb7aac70b5d8a4bddf4ee0791b834f9f56883d2 Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann +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 +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20230420084624.3005701-1-arnd@kernel.org +Signed-off-by: Jakub Kicinski +--- + 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 + Mbps OR 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 */ diff --git a/target/linux/generic/backport-6.6/821-v6.4-net-phy-Fix-reading-LED-reg-property.patch b/target/linux/generic/backport-6.6/821-v6.4-net-phy-Fix-reading-LED-reg-property.patch new file mode 100644 index 0000000000..622bb9e94a --- /dev/null +++ b/target/linux/generic/backport-6.6/821-v6.4-net-phy-Fix-reading-LED-reg-property.patch @@ -0,0 +1,43 @@ +From aed8fdad2152d946add50bec00a6b07c457bdcdf Mon Sep 17 00:00:00 2001 +From: Alexander Stein +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 +Reviewed-by: Andrew Lunn +Reviewed-by: Florian Fainelli +Link: https://lore.kernel.org/r/20230424141648.317944-1-alexander.stein@ew.tq-group.com +Signed-off-by: Jakub Kicinski +--- + 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) diff --git a/target/linux/generic/backport-6.6/822-v6.4-net-phy-Manual-remove-LEDs-to-ensure-correct-orderin.patch b/target/linux/generic/backport-6.6/822-v6.4-net-phy-Manual-remove-LEDs-to-ensure-correct-orderin.patch new file mode 100644 index 0000000000..80197e963b --- /dev/null +++ b/target/linux/generic/backport-6.6/822-v6.4-net-phy-Manual-remove-LEDs-to-ensure-correct-orderin.patch @@ -0,0 +1,67 @@ +From c938ab4da0eb1620ae3243b0b24c572ddfc318fc Mon Sep 17 00:00:00 2001 +From: Andrew Lunn +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 +Suggested-by: Florian Fainelli +Fixes: 01e5b728e9e4 ("net: phy: Add a binding for PHY LEDs") +Signed-off-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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); diff --git a/target/linux/generic/backport-6.6/824-v6.5-leds-trigger-netdev-Remove-NULL-check-before-dev_-pu.patch b/target/linux/generic/backport-6.6/824-v6.5-leds-trigger-netdev-Remove-NULL-check-before-dev_-pu.patch new file mode 100644 index 0000000000..609115b04a --- /dev/null +++ b/target/linux/generic/backport-6.6/824-v6.5-leds-trigger-netdev-Remove-NULL-check-before-dev_-pu.patch @@ -0,0 +1,44 @@ +From af7320ecae0ce646fd2c4a88341a3fbc243553da Mon Sep 17 00:00:00 2001 +From: Yang Li +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 +Closes: https://bugzilla.openanolis.cn/show_bug.cgi?id=4929 +Signed-off-by: Yang Li +Link: https://lore.kernel.org/r/20230511070820.52731-1-yang.lee@linux.alibaba.com +Signed-off-by: Lee Jones +--- + 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); + } diff --git a/target/linux/generic/backport-6.6/825-v6.5-leds-trigger-netdev-uninitialized-variable-in-netdev.patch b/target/linux/generic/backport-6.6/825-v6.5-leds-trigger-netdev-uninitialized-variable-in-netdev.patch new file mode 100644 index 0000000000..2f7a6ced90 --- /dev/null +++ b/target/linux/generic/backport-6.6/825-v6.5-leds-trigger-netdev-uninitialized-variable-in-netdev.patch @@ -0,0 +1,31 @@ +From 97c5209b3d374a25ebdb4c2ea9e9c1b121768da0 Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +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 +Reviewed-by: Andrew Lunn +Acked-by: Lee Jones +Signed-off-by: David S. Miller +--- + 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; + diff --git a/target/linux/generic/backport-6.6/826-v6.6-01-led-trig-netdev-Fix-requesting-offload-device.patch b/target/linux/generic/backport-6.6/826-v6.6-01-led-trig-netdev-Fix-requesting-offload-device.patch new file mode 100644 index 0000000000..dedf84c64b --- /dev/null +++ b/target/linux/generic/backport-6.6/826-v6.6-01-led-trig-netdev-Fix-requesting-offload-device.patch @@ -0,0 +1,57 @@ +From 7df1f14c04cbb1950e79c19793420f87227c3e80 Mon Sep 17 00:00:00 2001 +From: Andrew Lunn +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 +Signed-off-by: Andrew Lunn +Tested-by: Daniel Golle +Link: https://lore.kernel.org/r/20230808210436.838995-2-andrew@lunn.ch +Signed-off-by: Jakub Kicinski +--- + 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; + } + } + diff --git a/target/linux/generic/backport-6.6/826-v6.6-02-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch b/target/linux/generic/backport-6.6/826-v6.6-02-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch new file mode 100644 index 0000000000..505513a53f --- /dev/null +++ b/target/linux/generic/backport-6.6/826-v6.6-02-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch @@ -0,0 +1,149 @@ +From 1dcc03c9a7a824a31eaaecdfaa03542fb25feb6c Mon Sep 17 00:00:00 2001 +From: Andrew Lunn +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 +Signed-off-by: Andrew Lunn +Tested-by: Daniel Golle +Link: https://lore.kernel.org/r/20230808210436.838995-3-andrew@lunn.ch +Signed-off-by: Jakub Kicinski +--- + 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) diff --git a/target/linux/generic/backport-6.6/826-v6.6-03-net-phy-marvell-Add-support-for-offloading-LED-blink.patch b/target/linux/generic/backport-6.6/826-v6.6-03-net-phy-marvell-Add-support-for-offloading-LED-blink.patch new file mode 100644 index 0000000000..1300583b66 --- /dev/null +++ b/target/linux/generic/backport-6.6/826-v6.6-03-net-phy-marvell-Add-support-for-offloading-LED-blink.patch @@ -0,0 +1,344 @@ +From 460b0b648fab24f576c481424e0de5479ffb9786 Mon Sep 17 00:00:00 2001 +From: Andrew Lunn +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 +Signed-off-by: Andrew Lunn +Tested-by: Daniel Golle +Link: https://lore.kernel.org/r/20230808210436.838995-4-andrew@lunn.ch +Signed-off-by: Jakub Kicinski +--- + 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, + }, + }; + diff --git a/target/linux/generic/backport-6.6/826-v6.6-04-leds-trig-netdev-Disable-offload-on-deactivation-of-.patch b/target/linux/generic/backport-6.6/826-v6.6-04-leds-trig-netdev-Disable-offload-on-deactivation-of-.patch new file mode 100644 index 0000000000..d6a69aa9ca --- /dev/null +++ b/target/linux/generic/backport-6.6/826-v6.6-04-leds-trig-netdev-Disable-offload-on-deactivation-of-.patch @@ -0,0 +1,31 @@ +From e8fbcc47a8e935f36f044d85f21a99acecbd7bfb Mon Sep 17 00:00:00 2001 +From: Andrew Lunn +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 +Signed-off-by: Andrew Lunn +Reviewed-by: Simon Horman +Tested-by: Daniel Golle +Link: https://lore.kernel.org/r/20230808210436.838995-5-andrew@lunn.ch +Signed-off-by: Jakub Kicinski +--- + 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); diff --git a/target/linux/generic/backport-6.6/827-v6.3-0001-of-base-add-of_parse_phandle_with_optional_args.patch b/target/linux/generic/backport-6.6/827-v6.3-0001-of-base-add-of_parse_phandle_with_optional_args.patch new file mode 100644 index 0000000000..f568c3f6ce --- /dev/null +++ b/target/linux/generic/backport-6.6/827-v6.3-0001-of-base-add-of_parse_phandle_with_optional_args.patch @@ -0,0 +1,58 @@ +From c5d264d4b527c96ae8903376a4b195df47b05203 Mon Sep 17 00:00:00 2001 +From: Michael Walle +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 +Reviewed-by: Rob Herring +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230206134356.839737-10-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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. diff --git a/target/linux/generic/backport-6.6/827-v6.3-0002-of-property-make-.-cells-optional-for-simple-props.patch b/target/linux/generic/backport-6.6/827-v6.3-0002-of-property-make-.-cells-optional-for-simple-props.patch new file mode 100644 index 0000000000..66c29126b6 --- /dev/null +++ b/target/linux/generic/backport-6.6/827-v6.3-0002-of-property-make-.-cells-optional-for-simple-props.patch @@ -0,0 +1,34 @@ +From ff24fed10ba414d19579e26e60b126fad2f2bb07 Mon Sep 17 00:00:00 2001 +From: Michael Walle +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 +Tested-by: Miquel Raynal +Reviewed-by: Rob Herring +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230206134356.839737-11-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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; diff --git a/target/linux/generic/backport-6.6/827-v6.3-0003-of-property-add-nvmem-cell-cells-property.patch b/target/linux/generic/backport-6.6/827-v6.3-0003-of-property-add-nvmem-cell-cells-property.patch new file mode 100644 index 0000000000..07354d7da7 --- /dev/null +++ b/target/linux/generic/backport-6.6/827-v6.3-0003-of-property-add-nvmem-cell-cells-property.patch @@ -0,0 +1,30 @@ +From e2d8172043d2e50df19fcd59c11e5593de8188d7 Mon Sep 17 00:00:00 2001 +From: Michael Walle +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 +Tested-by: Miquel Raynal +Reviewed-by: Rob Herring +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230206134356.839737-12-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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) diff --git a/target/linux/generic/backport-6.6/827-v6.3-0004-of-device-Ignore-modalias-of-reused-nodes.patch b/target/linux/generic/backport-6.6/827-v6.3-0004-of-device-Ignore-modalias-of-reused-nodes.patch new file mode 100644 index 0000000000..8dc370618e --- /dev/null +++ b/target/linux/generic/backport-6.6/827-v6.3-0004-of-device-Ignore-modalias-of-reused-nodes.patch @@ -0,0 +1,37 @@ +From 553bd29700145e1849698985e9800f14e967da49 Mon Sep 17 00:00:00 2001 +From: Alexander Stein +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 +Reviewed-by: Rob Herring +Link: https://lore.kernel.org/r/20230207110531.1060252-2-alexander.stein@ew.tq-group.com +Signed-off-by: Greg Kroah-Hartman +--- + 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 */ diff --git a/target/linux/generic/backport-6.6/827-v6.3-0005-of-device-Do-not-ignore-error-code-in-of_device_ueve.patch b/target/linux/generic/backport-6.6/827-v6.3-0005-of-device-Do-not-ignore-error-code-in-of_device_ueve.patch new file mode 100644 index 0000000000..dcdc2313ba --- /dev/null +++ b/target/linux/generic/backport-6.6/827-v6.3-0005-of-device-Do-not-ignore-error-code-in-of_device_ueve.patch @@ -0,0 +1,29 @@ +From 2295bed9bebe8d1eef276194fed5b5fbe89c5363 Mon Sep 17 00:00:00 2001 +From: Alexander Stein +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 +Reviewed-by: Rob Herring +Link: https://lore.kernel.org/r/20230207110531.1060252-3-alexander.stein@ew.tq-group.com +Signed-off-by: Greg Kroah-Hartman +--- + 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; diff --git a/target/linux/generic/backport-6.6/828-v6.4-0002-of-Update-of_device_get_modalias.patch b/target/linux/generic/backport-6.6/828-v6.4-0002-of-Update-of_device_get_modalias.patch new file mode 100644 index 0000000000..280ed9085c --- /dev/null +++ b/target/linux/generic/backport-6.6/828-v6.4-0002-of-Update-of_device_get_modalias.patch @@ -0,0 +1,103 @@ +From 5c3d15e127ebfc0754cd18def7124633b6d46672 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Reviewed-by: Rob Herring +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230404172148.82422-8-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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)) diff --git a/target/linux/generic/backport-6.6/828-v6.4-0003-of-Rename-of_modalias_node.patch b/target/linux/generic/backport-6.6/828-v6.4-0003-of-Rename-of_modalias_node.patch new file mode 100644 index 0000000000..671556fbaa --- /dev/null +++ b/target/linux/generic/backport-6.6/828-v6.4-0003-of-Rename-of_modalias_node.patch @@ -0,0 +1,173 @@ +From 673aa1ed1c9b6710bf24e3f0957d85e2f46c77db Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Cc: Len Brown +Cc: Maarten Lankhorst +Cc: Maxime Ripard +Cc: Thomas Zimmermann +Cc: Sebastian Reichel +Cc: Wolfram Sang +Cc: Mark Brown +Signed-off-by: Miquel Raynal +Reviewed-by: Rob Herring +Acked-by: Mark Brown +Signed-off-by: Srinivas Kandagatla +Acked-by: Sebastian Reichel +Link: https://lore.kernel.org/r/20230404172148.82422-9-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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, diff --git a/target/linux/generic/backport-6.6/828-v6.4-0004-of-Move-of_modalias-to-module.c.patch b/target/linux/generic/backport-6.6/828-v6.4-0004-of-Move-of_modalias-to-module.c.patch new file mode 100644 index 0000000000..39a84161a2 --- /dev/null +++ b/target/linux/generic/backport-6.6/828-v6.4-0004-of-Move-of_modalias-to-module.c.patch @@ -0,0 +1,160 @@ +From bd7a7ed774afd1a4174df34227626c95573be517 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Signed-off-by: Miquel Raynal +Reviewed-by: Rob Herring +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230404172148.82422-10-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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 + #include + #include + #include +@@ -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 ++#include ++#include ++ ++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, diff --git a/target/linux/generic/backport-6.6/828-v6.4-0005-of-Move-the-request-module-helper-logic-to-module.c.patch b/target/linux/generic/backport-6.6/828-v6.4-0005-of-Move-the-request-module-helper-logic-to-module.c.patch new file mode 100644 index 0000000000..046c1df561 --- /dev/null +++ b/target/linux/generic/backport-6.6/828-v6.4-0005-of-Move-the-request-module-helper-logic-to-module.c.patch @@ -0,0 +1,131 @@ +From e6506f06d5e82765666902ccf9e9162f3e31d518 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Reviewed-by: Rob Herring +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230404172148.82422-11-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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 /* for bus_dma_region */ + #include + #include +-#include + #include + #include + #include +@@ -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 ++#include + #include + #include + +@@ -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, diff --git a/target/linux/generic/backport-6.6/830-00-v6.2-dt-bindings-arm-qcom-document-qcom-msm-id-and-qcom-b.patch b/target/linux/generic/backport-6.6/830-00-v6.2-dt-bindings-arm-qcom-document-qcom-msm-id-and-qcom-b.patch new file mode 100644 index 0000000000..3319f431ba --- /dev/null +++ b/target/linux/generic/backport-6.6/830-00-v6.2-dt-bindings-arm-qcom-document-qcom-msm-id-and-qcom-b.patch @@ -0,0 +1,207 @@ +From 77faa07c185c969e742cbb3e6aa487a11b0b616c Mon Sep 17 00:00:00 2001 +From: Krzysztof Kozlowski +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 +Signed-off-by: Kumar Gala +Signed-off-by: Krzysztof Kozlowski +Reviewed-by: Dmitry Baryshkov +Reviewed-by: Rob Herring +Signed-off-by: Bjorn Andersson +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 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 */ diff --git a/target/linux/generic/backport-6.6/830-01-v6.5-soc-qcom-socinfo-move-SMEM-item-struct-and-defines-t.patch b/target/linux/generic/backport-6.6/830-01-v6.5-soc-qcom-socinfo-move-SMEM-item-struct-and-defines-t.patch new file mode 100644 index 0000000000..394087a27a --- /dev/null +++ b/target/linux/generic/backport-6.6/830-01-v6.5-soc-qcom-socinfo-move-SMEM-item-struct-and-defines-t.patch @@ -0,0 +1,171 @@ +From 7cbff3c3f867ff3b24de674f44ca03f54e416a37 Mon Sep 17 00:00:00 2001 +From: Robert Marko +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 +Reviewed-by: Konrad Dybcio +Signed-off-by: Bjorn Andersson +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 + #include + #include ++#include + #include + #include + #include +@@ -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 diff --git a/target/linux/generic/backport-6.6/830-02-v6.5-soc-qcom-smem-Switch-to-EXPORT_SYMBOL_GPL.patch b/target/linux/generic/backport-6.6/830-02-v6.5-soc-qcom-smem-Switch-to-EXPORT_SYMBOL_GPL.patch new file mode 100644 index 0000000000..7c7c3f3635 --- /dev/null +++ b/target/linux/generic/backport-6.6/830-02-v6.5-soc-qcom-smem-Switch-to-EXPORT_SYMBOL_GPL.patch @@ -0,0 +1,55 @@ +From 9f1bbff157a69db7684f5da2f73b2325c638a90e Mon Sep 17 00:00:00 2001 +From: Robert Marko +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 +Reviewed-by: Konrad Dybcio +Reviewed-by: Trilok Soni +Signed-off-by: Bjorn Andersson +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) + { diff --git a/target/linux/generic/backport-6.6/830-03-v6.5-soc-qcom-smem-introduce-qcom_smem_get_soc_id.patch b/target/linux/generic/backport-6.6/830-03-v6.5-soc-qcom-smem-introduce-qcom_smem_get_soc_id.patch new file mode 100644 index 0000000000..4a816bb0f9 --- /dev/null +++ b/target/linux/generic/backport-6.6/830-03-v6.5-soc-qcom-smem-introduce-qcom_smem_get_soc_id.patch @@ -0,0 +1,70 @@ +From c3ecf2602a32d9b9e5fc997076c0d2836495c085 Mon Sep 17 00:00:00 2001 +From: Robert Marko +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 +Reviewed-by: Konrad Dybcio +Signed-off-by: Bjorn Andersson +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 + #include + #include ++#include + + /* + * 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 diff --git a/target/linux/generic/backport-6.6/830-04-v6.5-cpufreq-qcom-nvmem-use-SoC-ID-s-from-bindings.patch b/target/linux/generic/backport-6.6/830-04-v6.5-cpufreq-qcom-nvmem-use-SoC-ID-s-from-bindings.patch new file mode 100644 index 0000000000..9560122ccd --- /dev/null +++ b/target/linux/generic/backport-6.6/830-04-v6.5-cpufreq-qcom-nvmem-use-SoC-ID-s-from-bindings.patch @@ -0,0 +1,51 @@ +From 2b8634d1468ff498cc91b6adf993c27ae6fa079d Mon Sep 17 00:00:00 2001 +From: Robert Marko +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 +Reviewed-by: Konrad Dybcio +Reviewed-by: Bjorn Andersson +Acked-by: Viresh Kumar +Signed-off-by: Bjorn Andersson +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 + + 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: diff --git a/target/linux/generic/backport-6.6/830-05-v6.5-cpufreq-qcom-nvmem-use-helper-to-get-SMEM-SoC-ID.patch b/target/linux/generic/backport-6.6/830-05-v6.5-cpufreq-qcom-nvmem-use-helper-to-get-SMEM-SoC-ID.patch new file mode 100644 index 0000000000..4f37d672ba --- /dev/null +++ b/target/linux/generic/backport-6.6/830-05-v6.5-cpufreq-qcom-nvmem-use-helper-to-get-SMEM-SoC-ID.patch @@ -0,0 +1,109 @@ +From e7992615acacc27baeec310197108143afc77337 Mon Sep 17 00:00:00 2001 +From: Robert Marko +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 +Acked-by: Viresh Kumar +Reviewed-by: Konrad Dybcio +Signed-off-by: Bjorn Andersson +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 + #include + +-#define MSM_ID_SMEM 137 +- + #include + +-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: diff --git a/target/linux/generic/backport-6.6/831-v6.7-rtc-rtc7301-Support-byte-addressed-IO.patch b/target/linux/generic/backport-6.6/831-v6.7-rtc-rtc7301-Support-byte-addressed-IO.patch new file mode 100644 index 0000000000..ddda6e4e78 --- /dev/null +++ b/target/linux/generic/backport-6.6/831-v6.7-rtc-rtc7301-Support-byte-addressed-IO.patch @@ -0,0 +1,93 @@ +From edd25a77e69b7c546c28077e5dffe72c54c0afe8 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Thu, 21 Sep 2023 22:18:12 +0200 +Subject: [PATCH 2/4] rtc: rtc7301: Support byte-addressed IO + +The old RTC7301 driver in OpenWrt used byte access, but the +current mainline Linux driver uses 32bit word access. + +Make this configurable using device properties using the +standard property "reg-io-width" in e.g. device tree. + +This is needed for the USRobotics USR8200 which has the +chip connected using byte accesses. + +Debugging and testing by Howard Harte. + +Signed-off-by: Linus Walleij +--- + drivers/rtc/rtc-r7301.c | 35 +++++++++++++++++++++++++++++++++-- + 1 file changed, 33 insertions(+), 2 deletions(-) + +--- a/drivers/rtc/rtc-r7301.c ++++ b/drivers/rtc/rtc-r7301.c +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -55,12 +56,23 @@ struct rtc7301_priv { + u8 bank; + }; + +-static const struct regmap_config rtc7301_regmap_config = { ++/* ++ * When the device is memory-mapped, some platforms pack the registers into ++ * 32-bit access using the lower 8 bits at each 4-byte stride, while others ++ * expose them as simply consecutive bytes. ++ */ ++static const struct regmap_config rtc7301_regmap_32_config = { + .reg_bits = 32, + .val_bits = 8, + .reg_stride = 4, + }; + ++static const struct regmap_config rtc7301_regmap_8_config = { ++ .reg_bits = 8, ++ .val_bits = 8, ++ .reg_stride = 1, ++}; ++ + static u8 rtc7301_read(struct rtc7301_priv *priv, unsigned int reg) + { + int reg_stride = regmap_get_reg_stride(priv->regmap); +@@ -356,7 +368,9 @@ static int __init rtc7301_rtc_probe(stru + void __iomem *regs; + struct rtc7301_priv *priv; + struct rtc_device *rtc; ++ static const struct regmap_config *mapconf; + int ret; ++ u32 val; + + priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) +@@ -366,8 +380,25 @@ static int __init rtc7301_rtc_probe(stru + if (IS_ERR(regs)) + return PTR_ERR(regs); + ++ ret = device_property_read_u32(&dev->dev, "reg-io-width", &val); ++ if (ret) ++ /* Default to 32bit accesses */ ++ val = 4; ++ ++ switch (val) { ++ case 1: ++ mapconf = &rtc7301_regmap_8_config; ++ break; ++ case 4: ++ mapconf = &rtc7301_regmap_32_config; ++ break; ++ default: ++ dev_err(&dev->dev, "invalid reg-io-width %d\n", val); ++ return -EINVAL; ++ } ++ + priv->regmap = devm_regmap_init_mmio(&dev->dev, regs, +- &rtc7301_regmap_config); ++ mapconf); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + diff --git a/target/linux/generic/backport-6.6/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch b/target/linux/generic/backport-6.6/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch new file mode 100644 index 0000000000..2969462838 --- /dev/null +++ b/target/linux/generic/backport-6.6/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch @@ -0,0 +1,82 @@ +From 49e5663b505070424e18099841943f34342aa405 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Sun, 24 Sep 2023 01:09:01 +0200 +Subject: [PATCH] net: phy: amd: Support the Altima AMI101L + +The Altima AC101L is obviously compatible with the AMD PHY, +as seen by reading the datasheet. + +Datasheet: https://docs.broadcom.com/doc/AC101L-DS05-405-RDS.pdf + +Signed-off-by: Linus Walleij +--- + drivers/net/phy/Kconfig | 4 ++-- + drivers/net/phy/amd.c | 33 +++++++++++++++++++++++---------- + 2 files changed, 25 insertions(+), 12 deletions(-) + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -72,9 +72,9 @@ config SFP + comment "MII PHY device drivers" + + config AMD_PHY +- tristate "AMD PHYs" ++ tristate "AMD and Altima PHYs" + help +- Currently supports the am79c874 ++ Currently supports the AMD am79c874 and Altima AC101L. + + config MESON_GXL_PHY + tristate "Amlogic Meson GXL Internal PHY" +--- a/drivers/net/phy/amd.c ++++ b/drivers/net/phy/amd.c +@@ -13,6 +13,7 @@ + #include + #include + ++#define PHY_ID_AC101L 0x00225520 + #define PHY_ID_AM79C874 0x0022561b + + #define MII_AM79C_IR 17 /* Interrupt Status/Control Register */ +@@ -87,19 +88,31 @@ static irqreturn_t am79c_handle_interrup + return IRQ_HANDLED; + } + +-static struct phy_driver am79c_driver[] = { { +- .phy_id = PHY_ID_AM79C874, +- .name = "AM79C874", +- .phy_id_mask = 0xfffffff0, +- /* PHY_BASIC_FEATURES */ +- .config_init = am79c_config_init, +- .config_intr = am79c_config_intr, +- .handle_interrupt = am79c_handle_interrupt, +-} }; ++static struct phy_driver am79c_drivers[] = { ++ { ++ .phy_id = PHY_ID_AM79C874, ++ .name = "AM79C874", ++ .phy_id_mask = 0xfffffff0, ++ /* PHY_BASIC_FEATURES */ ++ .config_init = am79c_config_init, ++ .config_intr = am79c_config_intr, ++ .handle_interrupt = am79c_handle_interrupt, ++ }, ++ { ++ .phy_id = PHY_ID_AC101L, ++ .name = "AC101L", ++ .phy_id_mask = 0xfffffff0, ++ /* PHY_BASIC_FEATURES */ ++ .config_init = am79c_config_init, ++ .config_intr = am79c_config_intr, ++ .handle_interrupt = am79c_handle_interrupt, ++ }, ++}; + +-module_phy_driver(am79c_driver); ++module_phy_driver(am79c_drivers); + + static struct mdio_device_id __maybe_unused amd_tbl[] = { ++ { PHY_ID_AC101L, 0xfffffff0 }, + { PHY_ID_AM79C874, 0xfffffff0 }, + { } + }; diff --git a/target/linux/generic/backport-6.6/833-v6.8-leds-core-Add-more-colors-from-DT-bindings-to-led_co.patch.patch b/target/linux/generic/backport-6.6/833-v6.8-leds-core-Add-more-colors-from-DT-bindings-to-led_co.patch.patch new file mode 100644 index 0000000000..b71df6fa57 --- /dev/null +++ b/target/linux/generic/backport-6.6/833-v6.8-leds-core-Add-more-colors-from-DT-bindings-to-led_co.patch.patch @@ -0,0 +1,29 @@ +From a067943129b4ec6b835e02cfd5fbef01093c1471 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sun, 8 Oct 2023 16:40:13 +0200 +Subject: [PATCH] leds: core: Add more colors from DT bindings to led_colors + +The colors are already part of DT bindings. Make sure the kernel is +able to convert them to strings. + +Signed-off-by: Ondrej Jirman +Link: https://lore.kernel.org/r/20231008144014.1180334-1-megi@xff.cz +Signed-off-by: Lee Jones +--- + drivers/leds/led-core.c | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/drivers/leds/led-core.c ++++ b/drivers/leds/led-core.c +@@ -36,6 +36,11 @@ const char * const led_colors[LED_COLOR_ + [LED_COLOR_ID_IR] = "ir", + [LED_COLOR_ID_MULTI] = "multicolor", + [LED_COLOR_ID_RGB] = "rgb", ++ [LED_COLOR_ID_PURPLE] = "purple", ++ [LED_COLOR_ID_ORANGE] = "orange", ++ [LED_COLOR_ID_PINK] = "pink", ++ [LED_COLOR_ID_CYAN] = "cyan", ++ [LED_COLOR_ID_LIME] = "lime", + }; + EXPORT_SYMBOL_GPL(led_colors); + diff --git a/target/linux/generic/backport-6.6/834-v6.8-leds-trigger-netdev-Extend-speeds-up-to-10G.patch b/target/linux/generic/backport-6.6/834-v6.8-leds-trigger-netdev-Extend-speeds-up-to-10G.patch new file mode 100644 index 0000000000..1c8e014a1a --- /dev/null +++ b/target/linux/generic/backport-6.6/834-v6.8-leds-trigger-netdev-Extend-speeds-up-to-10G.patch @@ -0,0 +1,111 @@ +From bc8e1da69a68d9871773b657d18400a7941cbdef Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 28 Nov 2023 04:00:10 +0000 +Subject: [PATCH] leds: trigger: netdev: Extend speeds up to 10G + +Add 2.5G, 5G and 10G as available speeds to the netdev LED trigger. + +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/99e7d3304c6bba7f4863a4a80764a869855f2085.1701143925.git.daniel@makrotopia.org +Signed-off-by: Lee Jones +--- + drivers/leds/trigger/ledtrig-netdev.c | 32 ++++++++++++++++++++++++++- + include/linux/leds.h | 3 +++ + 2 files changed, 34 insertions(+), 1 deletion(-) + +--- a/drivers/leds/trigger/ledtrig-netdev.c ++++ b/drivers/leds/trigger/ledtrig-netdev.c +@@ -99,6 +99,18 @@ static void set_baseline_state(struct le + trigger_data->link_speed == SPEED_1000) + blink_on = true; + ++ if (test_bit(TRIGGER_NETDEV_LINK_2500, &trigger_data->mode) && ++ trigger_data->link_speed == SPEED_2500) ++ blink_on = true; ++ ++ if (test_bit(TRIGGER_NETDEV_LINK_5000, &trigger_data->mode) && ++ trigger_data->link_speed == SPEED_5000) ++ blink_on = true; ++ ++ if (test_bit(TRIGGER_NETDEV_LINK_10000, &trigger_data->mode) && ++ trigger_data->link_speed == SPEED_10000) ++ blink_on = true; ++ + if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) && + trigger_data->duplex == DUPLEX_HALF) + blink_on = true; +@@ -286,6 +298,9 @@ 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_LINK_2500: ++ case TRIGGER_NETDEV_LINK_5000: ++ case TRIGGER_NETDEV_LINK_10000: + case TRIGGER_NETDEV_HALF_DUPLEX: + case TRIGGER_NETDEV_FULL_DUPLEX: + case TRIGGER_NETDEV_TX: +@@ -316,6 +331,9 @@ 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_LINK_2500: ++ case TRIGGER_NETDEV_LINK_5000: ++ case TRIGGER_NETDEV_LINK_10000: + case TRIGGER_NETDEV_HALF_DUPLEX: + case TRIGGER_NETDEV_FULL_DUPLEX: + case TRIGGER_NETDEV_TX: +@@ -334,7 +352,10 @@ static ssize_t netdev_led_attr_store(str + 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))) ++ test_bit(TRIGGER_NETDEV_LINK_1000, &mode) || ++ test_bit(TRIGGER_NETDEV_LINK_2500, &mode) || ++ test_bit(TRIGGER_NETDEV_LINK_5000, &mode) || ++ test_bit(TRIGGER_NETDEV_LINK_10000, &mode))) + return -EINVAL; + + cancel_delayed_work_sync(&trigger_data->work); +@@ -364,6 +385,9 @@ 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(link_2500, TRIGGER_NETDEV_LINK_2500); ++DEFINE_NETDEV_TRIGGER(link_5000, TRIGGER_NETDEV_LINK_5000); ++DEFINE_NETDEV_TRIGGER(link_10000, TRIGGER_NETDEV_LINK_10000); + 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); +@@ -422,6 +446,9 @@ static struct attribute *netdev_trig_att + &dev_attr_link_10.attr, + &dev_attr_link_100.attr, + &dev_attr_link_1000.attr, ++ &dev_attr_link_2500.attr, ++ &dev_attr_link_5000.attr, ++ &dev_attr_link_10000.attr, + &dev_attr_full_duplex.attr, + &dev_attr_half_duplex.attr, + &dev_attr_rx.attr, +@@ -519,6 +546,9 @@ static void netdev_trig_work(struct work + 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_2500, &trigger_data->mode) || ++ test_bit(TRIGGER_NETDEV_LINK_5000, &trigger_data->mode) || ++ test_bit(TRIGGER_NETDEV_LINK_10000, &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( +--- a/include/linux/leds.h ++++ b/include/linux/leds.h +@@ -533,6 +533,9 @@ enum led_trigger_netdev_modes { + TRIGGER_NETDEV_LINK_10, + TRIGGER_NETDEV_LINK_100, + TRIGGER_NETDEV_LINK_1000, ++ TRIGGER_NETDEV_LINK_2500, ++ TRIGGER_NETDEV_LINK_5000, ++ TRIGGER_NETDEV_LINK_10000, + TRIGGER_NETDEV_HALF_DUPLEX, + TRIGGER_NETDEV_FULL_DUPLEX, + TRIGGER_NETDEV_TX, diff --git a/target/linux/generic/backport-6.6/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch b/target/linux/generic/backport-6.6/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch new file mode 100644 index 0000000000..0182e6d1a2 --- /dev/null +++ b/target/linux/generic/backport-6.6/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch @@ -0,0 +1,98 @@ +From 7ae215ee7bb855f13c80565470fc7f67db4ba82f Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 25 Jan 2024 21:36:59 +0100 +Subject: [PATCH 3/5] net: phy: add support for PHY LEDs polarity modes + +Add support for PHY LEDs polarity modes. Some PHY require LED to be set +to active low to be turned ON. Adds support for this by declaring +active-low property in DT. + +PHY driver needs to declare .led_polarity_set() to configure LED +polarity modes. Function will pass the index with the LED index and a +bitmap with all the required modes to set. + +Current supported modes are: +- active-low with the flag PHY_LED_ACTIVE_LOW. LED is set to active-low + to turn it ON. +- inactive-high-impedance with the flag PHY_LED_INACTIVE_HIGH_IMPEDANCE. + LED is set to high impedance to turn it OFF. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20240125203702.4552-4-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phy_device.c | 16 ++++++++++++++++ + include/linux/phy.h | 22 ++++++++++++++++++++++ + 2 files changed, 38 insertions(+) + +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -3138,6 +3138,7 @@ static int of_phy_led(struct phy_device + struct device *dev = &phydev->mdio.dev; + struct led_init_data init_data = {}; + struct led_classdev *cdev; ++ unsigned long modes = 0; + struct phy_led *phyled; + u32 index; + int err; +@@ -3155,6 +3156,21 @@ static int of_phy_led(struct phy_device + if (index > U8_MAX) + return -EINVAL; + ++ if (of_property_read_bool(led, "active-low")) ++ set_bit(PHY_LED_ACTIVE_LOW, &modes); ++ if (of_property_read_bool(led, "inactive-high-impedance")) ++ set_bit(PHY_LED_INACTIVE_HIGH_IMPEDANCE, &modes); ++ ++ if (modes) { ++ /* Return error if asked to set polarity modes but not supported */ ++ if (!phydev->drv->led_polarity_set) ++ return -EINVAL; ++ ++ err = phydev->drv->led_polarity_set(phydev, index, modes); ++ if (err) ++ return err; ++ } ++ + phyled->index = index; + if (phydev->drv->led_brightness_set) + cdev->brightness_set_blocking = phy_led_set_brightness; +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -791,6 +791,15 @@ struct phy_led { + + #define to_phy_led(d) container_of(d, struct phy_led, led_cdev) + ++/* Modes for PHY LED configuration */ ++enum phy_led_modes { ++ PHY_LED_ACTIVE_LOW = 0, ++ PHY_LED_INACTIVE_HIGH_IMPEDANCE = 1, ++ ++ /* keep it last */ ++ __PHY_LED_MODES_NUM, ++}; ++ + /** + * struct phy_driver - Driver structure for a particular PHY type + * +@@ -1059,6 +1068,19 @@ struct phy_driver { + int (*led_hw_control_get)(struct phy_device *dev, u8 index, + unsigned long *rules); + ++ /** ++ * @led_polarity_set: Set the LED polarity modes ++ * @dev: PHY device which has the LED ++ * @index: Which LED of the PHY device ++ * @modes: bitmap of LED polarity modes ++ * ++ * Configure LED with all the required polarity modes in @modes ++ * to make it correctly turn ON or OFF. ++ * ++ * Returns 0, or an error code. ++ */ ++ int (*led_polarity_set)(struct phy_device *dev, int index, ++ unsigned long modes); + }; + #define to_phy_driver(d) container_of(to_mdio_common_driver(d), \ + struct phy_driver, mdiodrv) diff --git a/target/linux/generic/backport-6.6/836-v6.7-leds-trigger-netdev-fix-RTNL-handling-to-prevent-pot.patch b/target/linux/generic/backport-6.6/836-v6.7-leds-trigger-netdev-fix-RTNL-handling-to-prevent-pot.patch new file mode 100644 index 0000000000..cbb9172032 --- /dev/null +++ b/target/linux/generic/backport-6.6/836-v6.7-leds-trigger-netdev-fix-RTNL-handling-to-prevent-pot.patch @@ -0,0 +1,170 @@ +From fe2b1226656afae56702d1d84c6900f6b67df297 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +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: + + 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 + + +Fixes: d5e01266e7f5 ("leds: trigger: netdev: add additional specific link speed mode") +Cc: stable@vger.kernel.org +Signed-off-by: Heiner Kallweit +Reviewed-by: Andrew Lunn +Acked-by: Lee Jones +Link: https://lore.kernel.org/r/fb5c8294-2a10-4bf5-8f10-3d2b77d2757e@gmail.com +Signed-off-by: Jakub Kicinski +--- + 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; + } diff --git a/target/linux/generic/backport-6.6/837-v6.4-net-phy-hide-the-PHYLIB_LEDS-knob.patch b/target/linux/generic/backport-6.6/837-v6.4-net-phy-hide-the-PHYLIB_LEDS-knob.patch new file mode 100644 index 0000000000..51b4bbbfcb --- /dev/null +++ b/target/linux/generic/backport-6.6/837-v6.4-net-phy-hide-the-PHYLIB_LEDS-knob.patch @@ -0,0 +1,43 @@ +From 9b78d919632b7149d311aaad5a977e4b48b10321 Mon Sep 17 00:00:00 2001 +From: Paolo Abeni +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 +Suggested-by: Arnd Bergmann +Signed-off-by: Paolo Abeni +Link: https://lore.kernel.org/r/d82489be8ed911c383c3447e9abf469995ccf39a.1682496488.git.pabeni@redhat.com +Signed-off-by: Paolo Abeni +--- + 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. diff --git a/target/linux/generic/backport-6.6/838-v6.9-leds-trigger-netdev-Fix-kernel-panic-on-interface-re.patch b/target/linux/generic/backport-6.6/838-v6.9-leds-trigger-netdev-Fix-kernel-panic-on-interface-re.patch new file mode 100644 index 0000000000..8d391678ff --- /dev/null +++ b/target/linux/generic/backport-6.6/838-v6.9-leds-trigger-netdev-Fix-kernel-panic-on-interface-re.patch @@ -0,0 +1,55 @@ +From 12ce20e02e532f101b725d71c52a36c5cc8ad1e6 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sun, 4 Feb 2024 00:54:01 +0100 +Subject: [PATCH] leds: trigger: netdev: Fix kernel panic on interface rename + trig notify + +Commit d5e01266e7f5 ("leds: trigger: netdev: add additional specific link +speed mode") in the various changes, reworked the way to set the LINKUP +mode in commit cee4bd16c319 ("leds: trigger: netdev: Recheck +NETDEV_LED_MODE_LINKUP on dev rename") and moved it to a generic function. + +This changed the logic where, in the previous implementation the dev +from the trigger event was used to check if the carrier was ok, but in +the new implementation with the generic function, the dev in +trigger_data is used instead. + +This is problematic and cause a possible kernel panic due to the fact +that the dev in the trigger_data still reference the old one as the +new one (passed from the trigger event) still has to be hold and saved +in the trigger_data struct (done in the NETDEV_REGISTER case). + +On calling of get_device_state(), an invalid net_dev is used and this +cause a kernel panic. + +To handle this correctly, move the call to get_device_state() after the +new net_dev is correctly set in trigger_data (in the NETDEV_REGISTER +case) and correctly parse the new dev. + +Fixes: d5e01266e7f5 ("leds: trigger: netdev: add additional specific link speed mode") +Cc: stable@vger.kernel.org +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20240203235413.1146-1-ansuelsmth@gmail.com +Signed-off-by: Lee Jones +--- + drivers/leds/trigger/ledtrig-netdev.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/leds/trigger/ledtrig-netdev.c ++++ b/drivers/leds/trigger/ledtrig-netdev.c +@@ -489,12 +489,12 @@ static int netdev_trig_notify(struct not + trigger_data->duplex = DUPLEX_UNKNOWN; + switch (evt) { + case NETDEV_CHANGENAME: +- get_device_state(trigger_data); +- fallthrough; + case NETDEV_REGISTER: + dev_put(trigger_data->net_dev); + dev_hold(dev); + trigger_data->net_dev = dev; ++ if (evt == NETDEV_CHANGENAME) ++ get_device_state(trigger_data); + break; + case NETDEV_UNREGISTER: + dev_put(trigger_data->net_dev); diff --git a/target/linux/generic/backport-6.6/890-v6.2-mtd-spinand-winbond-fix-flash-detection.patch b/target/linux/generic/backport-6.6/890-v6.2-mtd-spinand-winbond-fix-flash-detection.patch new file mode 100644 index 0000000000..38fbc3a3d7 --- /dev/null +++ b/target/linux/generic/backport-6.6/890-v6.2-mtd-spinand-winbond-fix-flash-detection.patch @@ -0,0 +1,40 @@ +From dbf70fc204d2fbb0d8ad8f42038a60846502efda Mon Sep 17 00:00:00 2001 +From: Mikhail Kshevetskiy +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 +Reviewed-by: Frieder Schrempf +Signed-off-by: Miquel Raynal +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, diff --git a/target/linux/generic/backport-6.6/891-v6.2-mtd-spinand-winbond-add-W25N02KV.patch b/target/linux/generic/backport-6.6/891-v6.2-mtd-spinand-winbond-add-W25N02KV.patch new file mode 100644 index 0000000000..d75a1acc57 --- /dev/null +++ b/target/linux/generic/backport-6.6/891-v6.2-mtd-spinand-winbond-add-W25N02KV.patch @@ -0,0 +1,106 @@ +From 6154c7a583483d7b69f53bea868efdc369edd563 Mon Sep 17 00:00:00 2001 +From: Mikhail Kshevetskiy +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 +Reviewed-by: Frieder Schrempf +Signed-off-by: Miquel Raynal +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) diff --git a/target/linux/generic/backport-6.6/892-v6.5-mtd-spinand-winbond-Fix-ecc_get_status.patch b/target/linux/generic/backport-6.6/892-v6.5-mtd-spinand-winbond-Fix-ecc_get_status.patch new file mode 100644 index 0000000000..2f408f5a30 --- /dev/null +++ b/target/linux/generic/backport-6.6/892-v6.5-mtd-spinand-winbond-Fix-ecc_get_status.patch @@ -0,0 +1,49 @@ +From f5a05060670a4d8d6523afc7963eb559c2e3615f Mon Sep 17 00:00:00 2001 +From: Olivier Maignial +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 +Signed-off-by: Miquel Raynal +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; diff --git a/target/linux/generic/backport-6.6/894-v6.8-net-ethtool-implement-ethtool_puts.patch b/target/linux/generic/backport-6.6/894-v6.8-net-ethtool-implement-ethtool_puts.patch new file mode 100644 index 0000000000..5094a6d774 --- /dev/null +++ b/target/linux/generic/backport-6.6/894-v6.8-net-ethtool-implement-ethtool_puts.patch @@ -0,0 +1,139 @@ +From mboxrd@z Thu Jan 1 00:00:00 1970 +Authentication-Results: smtp.subspace.kernel.org; + dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="sMUeie/T" +Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) + by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 84BB8D6D + for ; Wed, 6 Dec 2023 15:16:16 -0800 (PST) +Received: by mail-yb1-xb49.google.com with SMTP id 3f1490d57ef6-db5416d0fccso403298276.1 + for ; Wed, 06 Dec 2023 15:16:16 -0800 (PST) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=google.com; s=20230601; t=1701904575; x=1702509375; darn=vger.kernel.org; + h=cc:to:from:subject:message-id:references:mime-version:in-reply-to + :date:from:to:cc:subject:date:message-id:reply-to; + bh=/7eYcPC4ZNNyPcPPs0B5tDplF0arxw3r0vINNNou0rY=; + b=sMUeie/TxdytzC0EyT11QWi1TqTtiv7KCTs1F2vLmUUvPKNA3+1MHFo8ECW+0gQuDE + FGrgdZKGK5mXQgkF0N3JiSLvKO8tpQOIB57JLCG5IVy5dr2vVv0ExU3Dag2Cc4oBIBIO + w/cH95O1oPlvluIpATmAsxenVr7mFomU63BqYiRGLaEhWeb2hJ636GO8lubtsDfdFFoi + GPOL2tQwV93VnqmywBBpFaNAULN0UoCFhfkKv5prvpkXq19sWI7zyorVZ+rdTYem5m4T + dXsDaLXPtC3Dh2JOad1duSQIah/wCHYYUcV3IoFhwj2y0Uk/TTCrnZPORweSADcEy6Ho + vDrA== +X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=1e100.net; s=20230601; t=1701904575; x=1702509375; + h=cc:to:from:subject:message-id:references:mime-version:in-reply-to + :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; + bh=/7eYcPC4ZNNyPcPPs0B5tDplF0arxw3r0vINNNou0rY=; + b=Dmc6aSntxPlxAk72zVO1G9WoZnFtLolxENlLscYYAHG3VE+PQ8gGN2rPzcGoKb2Btb + 4b0PvjOzSlPQyghahdhdlz04RtAeeGG/MkfNiYjFql5OifIoovb51kroiPYrVsa7Ps7Y + +Pxug0+NPdTm5s9TNz940ZKl3GRME8UTmVxpWJRX03XMOqb6Wgsh2SK9ahXKc4yRsi62 + 3a3J72WmmSgvimxwM/99fXwvoUQpiv2J1xCoqc1Ng4q4qSuZvzmHN7ZTGaUhLxOqLeLK + 3W4RKHW6rZ7UjppuB6I3NXW+D344By2rdKp1sRXpjdQ0GS3YUcvlRETcJBXJudHfQP5Y + CLOw== +X-Gm-Message-State: AOJu0YzdCTLdwny+N99zeMgyKqFsEZhfIhL2cbgKA6zC1U/OLkxxRLoM + XrYVBC9DmxCGmP4o+M/Z/kHUew/9faHlCiLGxw== +X-Google-Smtp-Source: AGHT+IFRXxBV6JuX5Cl/k2o1+WKkCwkR8j20MJSkmoGCedPAtqFttH8OVh1/6vdfnq8MPN++A2h89peZQhyG8OsJ8A== +X-Received: from jstitt-linux1.c.googlers.com ([fda3:e722:ac3:cc00:2b:ff92:c0a8:23b5]) + (user=justinstitt job=sendgmr) by 2002:a25:dac7:0:b0:da0:3117:f35 with SMTP + id n190-20020a25dac7000000b00da031170f35mr28652ybf.3.1701904575576; Wed, 06 + Dec 2023 15:16:15 -0800 (PST) +Date: Wed, 06 Dec 2023 23:16:10 +0000 +In-Reply-To: <20231206-ethtool_puts_impl-v5-0-5a2528e17bf8@google.com> +Precedence: bulk +X-Mailing-List: bpf@vger.kernel.org +List-Id: +List-Subscribe: +List-Unsubscribe: +Mime-Version: 1.0 +References: <20231206-ethtool_puts_impl-v5-0-5a2528e17bf8@google.com> +X-Developer-Key: i=justinstitt@google.com; a=ed25519; pk=tC3hNkJQTpNX/gLKxTNQKDmiQl6QjBNCGKJINqAdJsE= +X-Developer-Signature: v=1; a=ed25519-sha256; t=1701904573; l=1840; + i=justinstitt@google.com; s=20230717; h=from:subject:message-id; + bh=UMdetIL2ZsPIkSodqhw2fM21NHJVjCu0lRImFuNhVoM=; b=a8rMnXfVVQ5gsxHWG4WRMwOLxZgflqXZtNuKx26vv4DwYvvCtCiYjl3f1frOjV/Ul2kaxq5g/ + b/UOv678JKCDASVokxG5GJifAnU7/kqRxdhcwfRkrD8RUfcsmiZOfyF +X-Mailer: b4 0.12.3 +Message-ID: <20231206-ethtool_puts_impl-v5-1-5a2528e17bf8@google.com> +Subject: [PATCH net-next v5 1/3] ethtool: Implement ethtool_puts() +From: justinstitt@google.com +To: "David S. Miller" , Eric Dumazet , + Jakub Kicinski , Paolo Abeni , Shay Agroskin , + Arthur Kiyanovski , David Arinzon , Noam Dagan , + Saeed Bishara , Rasesh Mody , + Sudarsana Kalluru , GR-Linux-NIC-Dev@marvell.com, + Dimitris Michailidis , Yisen Zhuang , + Salil Mehta , Jesse Brandeburg , + Tony Nguyen , Louis Peens , + Shannon Nelson , Brett Creeley , drivers@pensando.io, + "K. Y. Srinivasan" , Haiyang Zhang , Wei Liu , + Dexuan Cui , Ronak Doshi , + VMware PV-Drivers Reviewers , Andy Whitcroft , Joe Perches , + Dwaipayan Ray , Lukas Bulwahn , + Hauke Mehrtens , Andrew Lunn , + Florian Fainelli , Vladimir Oltean , + "=?utf-8?q?Ar=C4=B1n=C3=A7_=C3=9CNAL?=" , Daniel Golle , + Landen Chao , DENG Qingfang , + Sean Wang , Matthias Brugger , + AngeloGioacchino Del Regno , + Linus Walleij , + "=?utf-8?q?Alvin_=C5=A0ipraga?=" , Wei Fang , + Shenwei Wang , Clark Wang , + NXP Linux Team , Lars Povlsen , + Steen Hegelund , Daniel Machon , + UNGLinuxDriver@microchip.com, Jiawen Wu , + Mengyuan Lou , Heiner Kallweit , + Russell King , Alexei Starovoitov , + Daniel Borkmann , Jesper Dangaard Brouer , + John Fastabend +Cc: linux-kernel@vger.kernel.org, netdev@vger.kernel.org, + Nick Desaulniers , Nathan Chancellor , + Kees Cook , intel-wired-lan@lists.osuosl.org, + oss-drivers@corigine.com, linux-hyperv@vger.kernel.org, + linux-arm-kernel@lists.infradead.org, linux-mediatek@lists.infradead.org, + bpf@vger.kernel.org, Justin Stitt +Content-Type: text/plain; charset="utf-8" + +Use strscpy() to implement ethtool_puts(). + +Functionally the same as ethtool_sprintf() when it's used with two +arguments or with just "%s" format specifier. + +Signed-off-by: Justin Stitt +--- + include/linux/ethtool.h | 13 +++++++++++++ + net/ethtool/ioctl.c | 7 +++++++ + 2 files changed, 20 insertions(+) + +--- a/include/linux/ethtool.h ++++ b/include/linux/ethtool.h +@@ -843,4 +843,17 @@ int ethtool_get_phc_vclocks(struct net_d + * next string. + */ + extern __printf(2, 3) void ethtool_sprintf(u8 **data, const char *fmt, ...); ++ ++/** ++ * ethtool_puts - Write string to ethtool string data ++ * @data: Pointer to a pointer to the start of string to update ++ * @str: String to write ++ * ++ * Write string to *data without a trailing newline. Update *data ++ * to point at start of next string. ++ * ++ * Prefer this function to ethtool_sprintf() when given only ++ * two arguments or if @fmt is just "%s". ++ */ ++extern void ethtool_puts(u8 **data, const char *str); + #endif /* _LINUX_ETHTOOL_H */ +--- a/net/ethtool/ioctl.c ++++ b/net/ethtool/ioctl.c +@@ -1974,6 +1974,13 @@ __printf(2, 3) void ethtool_sprintf(u8 * + } + EXPORT_SYMBOL(ethtool_sprintf); + ++void ethtool_puts(u8 **data, const char *str) ++{ ++ strscpy(*data, str, ETH_GSTRING_LEN); ++ *data += ETH_GSTRING_LEN; ++} ++EXPORT_SYMBOL(ethtool_puts); ++ + static int ethtool_phys_id(struct net_device *dev, void __user *useraddr) + { + struct ethtool_value id; diff --git a/target/linux/generic/config-6.6 b/target/linux/generic/config-6.6 new file mode 100644 index 0000000000..34a6503ce0 --- /dev/null +++ b/target/linux/generic/config-6.6 @@ -0,0 +1,7936 @@ +# CONFIG_104_QUAD_8 is not set +CONFIG_32BIT=y +CONFIG_64BIT_TIME=y +# CONFIG_6LOWPAN is not set +# CONFIG_6LOWPAN_DEBUGFS is not set +# CONFIG_6PACK is not set +# CONFIG_8139CP is not set +# CONFIG_8139TOO is not set +# CONFIG_9P_FS is not set +# CONFIG_AB3100_CORE is not set +# CONFIG_AB8500_CORE is not set +# CONFIG_ABP060MG is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_ACCESSIBILITY is not set +# CONFIG_ACENIC is not set +# CONFIG_ACERHDF is not set +# CONFIG_ACER_WIRELESS is not set +# CONFIG_ACORN_PARTITION is not set +# CONFIG_ACPI_ALS is not set +# CONFIG_ACPI_APEI is not set +# CONFIG_ACPI_APEI_PCIEAER is not set +# CONFIG_ACPI_BUTTON is not set +# CONFIG_ACPI_CONFIGFS is not set +# CONFIG_ACPI_CUSTOM_METHOD is not set +# CONFIG_ACPI_EXTLOG is not set +# CONFIG_ACPI_HED is not set +# CONFIG_ACPI_NFIT is not set +# CONFIG_ACPI_REDUCED_HARDWARE_ONLY is not set +# CONFIG_ACPI_TABLE_UPGRADE is not set +# CONFIG_ACPI_VIDEO is not set +# CONFIG_AD2S1200 is not set +# CONFIG_AD2S1210 is not set +# CONFIG_AD2S90 is not set +# CONFIG_AD3552R is not set +# CONFIG_AD5064 is not set +# CONFIG_AD5110 is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_AD5272 is not set +# CONFIG_AD5360 is not set +# CONFIG_AD5380 is not set +# CONFIG_AD5421 is not set +# CONFIG_AD5446 is not set +# CONFIG_AD5449 is not set +# CONFIG_AD5504 is not set +# CONFIG_AD5592R is not set +# CONFIG_AD5593R is not set +# CONFIG_AD5624R_SPI is not set +# CONFIG_AD5686 is not set +# CONFIG_AD5686_SPI is not set +# CONFIG_AD5696_I2C is not set +# CONFIG_AD5755 is not set +# CONFIG_AD5758 is not set +# CONFIG_AD5761 is not set +# CONFIG_AD5764 is not set +# CONFIG_AD5766 is not set +# CONFIG_AD5770R is not set +# CONFIG_AD5791 is not set +# CONFIG_AD5933 is not set +# CONFIG_AD7091R5 is not set +# CONFIG_AD7124 is not set +# CONFIG_AD7150 is not set +# CONFIG_AD7152 is not set +# CONFIG_AD7192 is not set +# CONFIG_AD7266 is not set +# CONFIG_AD7280 is not set +# CONFIG_AD7291 is not set +# CONFIG_AD7292 is not set +# CONFIG_AD7293 is not set +# CONFIG_AD7298 is not set +# CONFIG_AD7303 is not set +# CONFIG_AD74413R is not set +# CONFIG_AD7476 is not set +# CONFIG_AD7606 is not set +# CONFIG_AD7606_IFACE_PARALLEL is not set +# CONFIG_AD7606_IFACE_SPI is not set +# CONFIG_AD7746 is not set +# CONFIG_AD7766 is not set +# CONFIG_AD7768_1 is not set +# CONFIG_AD7780 is not set +# CONFIG_AD7791 is not set +# CONFIG_AD7793 is not set +# CONFIG_AD7816 is not set +# CONFIG_AD7887 is not set +# CONFIG_AD7923 is not set +# CONFIG_AD7949 is not set +# CONFIG_AD799X is not set +# CONFIG_AD8366 is not set +# CONFIG_AD8801 is not set +# CONFIG_AD9467 is not set +# CONFIG_AD9523 is not set +# CONFIG_AD9832 is not set +# CONFIG_AD9834 is not set +# CONFIG_ADA4250 is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_ADE7854 is not set +# CONFIG_ADF4350 is not set +# CONFIG_ADF4371 is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADIN1100_PHY is not set +# CONFIG_ADIN1110 is not set +# CONFIG_ADIN_PHY is not set +# CONFIG_ADIS16080 is not set +# CONFIG_ADIS16130 is not set +# CONFIG_ADIS16136 is not set +# CONFIG_ADIS16201 is not set +# CONFIG_ADIS16203 is not set +# CONFIG_ADIS16209 is not set +# CONFIG_ADIS16240 is not set +# CONFIG_ADIS16260 is not set +# CONFIG_ADIS16400 is not set +# CONFIG_ADIS16460 is not set +# CONFIG_ADIS16475 is not set +# CONFIG_ADIS16480 is not set +# CONFIG_ADI_AXI_ADC is not set +# CONFIG_ADJD_S311 is not set +# CONFIG_ADM6996_PHY is not set +# CONFIG_ADM8211 is not set +# CONFIG_ADMV1013 is not set +# CONFIG_ADMV1014 is not set +# CONFIG_ADMV4420 is not set +# CONFIG_ADMV8818 is not set +# CONFIG_ADRF6780 is not set +# CONFIG_ADT7316 is not set +# CONFIG_ADUX1020 is not set +CONFIG_ADVISE_SYSCALLS=y +# CONFIG_ADXL313_I2C is not set +# CONFIG_ADXL313_SPI is not set +# CONFIG_ADXL345_I2C is not set +# CONFIG_ADXL345_SPI is not set +# CONFIG_ADXL355_I2C is not set +# CONFIG_ADXL355_SPI is not set +# CONFIG_ADXL367_I2C is not set +# CONFIG_ADXL367_SPI is not set +# CONFIG_ADXL372_I2C is not set +# CONFIG_ADXL372_SPI is not set +# CONFIG_ADXRS290 is not set +# CONFIG_ADXRS450 is not set +CONFIG_AEABI=y +# CONFIG_AFE4403 is not set +# CONFIG_AFE4404 is not set +# CONFIG_AFFS_FS is not set +# CONFIG_AFS_DEBUG_CURSOR is not set +# CONFIG_AFS_FS is not set +# CONFIG_AF_KCM is not set +# CONFIG_AF_RXRPC is not set +# CONFIG_AF_RXRPC_INJECT_LOSS is not set +# CONFIG_AF_RXRPC_IPV6 is not set +CONFIG_AF_UNIX_OOB=y +# CONFIG_AGP is not set +# CONFIG_AHCI_BRCM is not set +# CONFIG_AHCI_CEVA is not set +# CONFIG_AHCI_DWC is not set +# CONFIG_AHCI_IMX is not set +# CONFIG_AHCI_MVEBU is not set +# CONFIG_AHCI_QORIQ is not set +# CONFIG_AHCI_XGENE is not set +CONFIG_AIO=y +# CONFIG_AIR_EN8811H_PHY is not set +# CONFIG_AIRO is not set +# CONFIG_AIRO_CS is not set +# CONFIG_AIX_PARTITION is not set +# CONFIG_AK09911 is not set +# CONFIG_AK8974 is not set +# CONFIG_AK8975 is not set +# CONFIG_AL3010 is not set +# CONFIG_AL3320A is not set +# CONFIG_ALIM7101_WDT is not set +CONFIG_ALLOW_DEV_COREDUMP=y +# CONFIG_ALTERA_MBOX is not set +# CONFIG_ALTERA_MSGDMA is not set +# CONFIG_ALTERA_STAPL is not set +# CONFIG_ALTERA_TSE is not set +# CONFIG_ALX is not set +# CONFIG_AL_FIC is not set +# CONFIG_AM2315 is not set +# CONFIG_AM335X_PHY_USB is not set +# CONFIG_AMBA_PL08X is not set +# CONFIG_AMD8111_ETH is not set +# CONFIG_AMD_MEM_ENCRYPT is not set +# CONFIG_AMD_PHY is not set +# CONFIG_AMD_XGBE is not set +# CONFIG_AMD_XGBE_HAVE_ECC is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_AMILO_RFKILL is not set +# CONFIG_AMPERE_ERRATUM_AC03_CPU_38 is not set +# CONFIG_AMT is not set +# CONFIG_ANDROID is not set +# CONFIG_ANDROID_BINDER_IPC is not set +CONFIG_ANON_INODES=y +# CONFIG_ANON_VMA_NAME is not set +# CONFIG_APDS9300 is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_APDS9960 is not set +# CONFIG_APM8018X is not set +# CONFIG_APM_EMULATION is not set +# CONFIG_APPLE_GMUX is not set +# CONFIG_APPLE_MFI_FASTCHARGE is not set +# CONFIG_APPLE_PROPERTIES is not set +# CONFIG_APPLICOM is not set +# CONFIG_AQTION is not set +# CONFIG_AQUANTIA_PHY is not set +# CONFIG_AR5523 is not set +# CONFIG_AR7 is not set +# CONFIG_AR8216_PHY is not set +# CONFIG_AR8216_PHY_LEDS is not set +# CONFIG_ARCH_ACTIONS is not set +# CONFIG_ARCH_AGILEX is not set +# CONFIG_ARCH_AIROHA is not set +# CONFIG_ARCH_ALPINE is not set +# CONFIG_ARCH_APPLE is not set +# CONFIG_ARCH_ARTPEC is not set +# CONFIG_ARCH_ASPEED is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_AXXIA is not set +# CONFIG_ARCH_BCM is not set +# CONFIG_ARCH_BCM2835 is not set +# CONFIG_ARCH_BCM4908 is not set +# CONFIG_ARCH_BCMBCA is not set +# CONFIG_ARCH_BCM_21664 is not set +# CONFIG_ARCH_BCM_23550 is not set +# CONFIG_ARCH_BCM_281XX is not set +# CONFIG_ARCH_BCM_5301X is not set +# CONFIG_ARCH_BCM_53573 is not set +# CONFIG_ARCH_BCM_63XX is not set +# CONFIG_ARCH_BCM_CYGNUS is not set +# CONFIG_ARCH_BCM_HR2 is not set +# CONFIG_ARCH_BCM_IPROC is not set +# CONFIG_ARCH_BCM_NSP is not set +# CONFIG_ARCH_BERLIN is not set +CONFIG_ARCH_BINFMT_ELF_STATE=y +# CONFIG_ARCH_BITMAIN is not set +# CONFIG_ARCH_BRCMSTB is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CNS3XXX is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_DIGICOLOR is not set +# CONFIG_ARCH_DMA_ADDR_T_64BIT is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_EXYNOS is not set +CONFIG_ARCH_FLATMEM_ENABLE=y +# CONFIG_ARCH_FOOTBRIDGE is not set +CONFIG_ARCH_FORCE_MAX_ORDER=11 +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_HI3xxx is not set +# CONFIG_ARCH_HIGHBANK is not set +# CONFIG_ARCH_HISI is not set +# CONFIG_ARCH_HPE is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_INTEL_SOCFPGA is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_K3 is not set +# CONFIG_ARCH_KEEMBAY is not set +# CONFIG_ARCH_KEYSTONE is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_LAYERSCAPE is not set +# CONFIG_ARCH_LG1K is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_MEDIATEK is not set +# CONFIG_ARCH_MESON is not set +# CONFIG_ARCH_MILBEAUT is not set +CONFIG_ARCH_MMAP_RND_BITS=8 +CONFIG_ARCH_MMAP_RND_BITS_MAX=16 +CONFIG_ARCH_MMAP_RND_BITS_MIN=8 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=16 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=8 +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_MSTARV7 is not set +# CONFIG_ARCH_MULTIPLATFORM is not set +# CONFIG_ARCH_MULTI_V6 is not set +# CONFIG_ARCH_MULTI_V7 is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_MVEBU is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_MXS is not set +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_NPCM is not set +# CONFIG_ARCH_NSPIRE is not set +# CONFIG_ARCH_NXP is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_ARCH_OMAP1 is not set +# CONFIG_ARCH_OMAP2 is not set +# CONFIG_ARCH_OMAP2PLUS is not set +# CONFIG_ARCH_OMAP3 is not set +# CONFIG_ARCH_OMAP4 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_OXNAS is not set +# CONFIG_ARCH_PICOXCELL is not set +# CONFIG_ARCH_PRIMA2 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_QCOM is not set +# CONFIG_ARCH_RANDOM is not set +# CONFIG_ARCH_RDA is not set +# CONFIG_ARCH_REALTEK is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_RENESAS is not set +# CONFIG_ARCH_ROCKCHIP is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_S32 is not set +# CONFIG_ARCH_S3C24XX is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_SEATTLE is not set +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_SIRF is not set +# CONFIG_ARCH_SOCFPGA is not set +# CONFIG_ARCH_SPARX5 is not set +# CONFIG_ARCH_SPRD is not set +# CONFIG_ARCH_STI is not set +# CONFIG_ARCH_STM32 is not set +# CONFIG_ARCH_STRATIX10 is not set +# CONFIG_ARCH_SUNPLUS is not set +# CONFIG_ARCH_SUNXI is not set +# CONFIG_ARCH_SYNQUACER is not set +# CONFIG_ARCH_TANGO is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_THUNDER is not set +# CONFIG_ARCH_THUNDER2 is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_UNIPHIER is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_VIRT is not set +# CONFIG_ARCH_VISCONTI is not set +# CONFIG_ARCH_VT8500 is not set +# CONFIG_ARCH_VULCAN is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_WANTS_THP_SWAP is not set +# CONFIG_ARCH_WM8505 is not set +# CONFIG_ARCH_WM8750 is not set +# CONFIG_ARCH_WM8850 is not set +# CONFIG_ARCH_XGENE is not set +# CONFIG_ARCH_ZX is not set +# CONFIG_ARCH_ZYNQ is not set +# CONFIG_ARCH_ZYNQMP is not set +# CONFIG_ARCNET is not set +# CONFIG_ARC_EMAC is not set +# CONFIG_ARC_IRQ_NO_AUTOSAVE is not set +# CONFIG_ARM64_16K_PAGES is not set +# CONFIG_ARM64_64K_PAGES is not set +# CONFIG_ARM64_AMU_EXTN is not set +# CONFIG_ARM64_BTI is not set +# CONFIG_ARM64_CRYPTO is not set +# CONFIG_ARM64_E0PD is not set +# CONFIG_ARM64_ERRATUM_1024718 is not set +# CONFIG_ARM64_ERRATUM_1165522 is not set +# CONFIG_ARM64_ERRATUM_1286807 is not set +# CONFIG_ARM64_ERRATUM_1319367 is not set +# CONFIG_ARM64_ERRATUM_1418040 is not set +# CONFIG_ARM64_ERRATUM_1463225 is not set +# CONFIG_ARM64_ERRATUM_1508412 is not set +# CONFIG_ARM64_ERRATUM_1530923 is not set +# CONFIG_ARM64_ERRATUM_1542419 is not set +# CONFIG_ARM64_ERRATUM_1742098 is not set +# CONFIG_ARM64_ERRATUM_2051678 is not set +# CONFIG_ARM64_ERRATUM_2054223 is not set +# CONFIG_ARM64_ERRATUM_2067961 is not set +# CONFIG_ARM64_ERRATUM_2077057 is not set +# CONFIG_ARM64_ERRATUM_2441007 is not set +# CONFIG_ARM64_ERRATUM_2441009 is not set +# CONFIG_ARM64_ERRATUM_2658417 is not set +# CONFIG_ARM64_ERRATUM_819472 is not set +# CONFIG_ARM64_ERRATUM_824069 is not set +# CONFIG_ARM64_ERRATUM_826319 is not set +# CONFIG_ARM64_ERRATUM_827319 is not set +# CONFIG_ARM64_ERRATUM_832075 is not set +# CONFIG_ARM64_ERRATUM_834220 is not set +# CONFIG_ARM64_ERRATUM_843419 is not set +# CONFIG_ARM64_ERRATUM_845719 is not set +# CONFIG_ARM64_ERRATUM_858921 is not set +# CONFIG_ARM64_ERRATUM_2966298 is not set +# CONFIG_ARM64_HW_AFDBM is not set +# CONFIG_ARM64_LSE_ATOMICS is not set +CONFIG_ARM64_MODULE_PLTS=y +# CONFIG_ARM64_MTE is not set +# CONFIG_ARM64_PAN is not set +# CONFIG_ARM64_PMEM is not set +# CONFIG_ARM64_PSEUDO_NMI is not set +# CONFIG_ARM64_PTDUMP_DEBUGFS is not set +# CONFIG_ARM64_PTR_AUTH is not set +# CONFIG_ARM64_RANDOMIZE_TEXT_OFFSET is not set +# CONFIG_ARM64_RAS_EXTN is not set +# CONFIG_ARM64_RELOC_TEST is not set +# CONFIG_ARM64_SME is not set +# CONFIG_ARM64_SVE is not set +CONFIG_ARM64_SW_TTBR0_PAN=y +# CONFIG_ARM64_TLB_RANGE is not set +# CONFIG_ARM64_UAO is not set +# CONFIG_ARM64_USE_LSE_ATOMICS is not set +# CONFIG_ARM64_VA_BITS_48 is not set +# CONFIG_ARM64_VHE is not set +# CONFIG_ARM_APPENDED_DTB is not set +# CONFIG_ARM_ARCH_TIMER is not set +# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set +# CONFIG_ARM_BIG_LITTLE_CPUFREQ is not set +# CONFIG_ARM_CCI is not set +# CONFIG_ARM_CCI400_PMU is not set +# CONFIG_ARM_CCI5xx_PMU is not set +# CONFIG_ARM_CCI_PMU is not set +# CONFIG_ARM_CCN is not set +# CONFIG_ARM_CMN is not set +# CONFIG_ARM_CPUIDLE is not set +CONFIG_ARM_CPU_TOPOLOGY=y +# CONFIG_ARM_CRYPTO is not set +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +# CONFIG_ARM_DSU_PMU is not set +# CONFIG_ARM_ERRATA_326103 is not set +# CONFIG_ARM_ERRATA_364296 is not set +# CONFIG_ARM_ERRATA_411920 is not set +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_ARM_ERRATA_458693 is not set +# CONFIG_ARM_ERRATA_460075 is not set +# CONFIG_ARM_ERRATA_643719 is not set +# CONFIG_ARM_ERRATA_720789 is not set +# CONFIG_ARM_ERRATA_742230 is not set +# CONFIG_ARM_ERRATA_742231 is not set +# CONFIG_ARM_ERRATA_743622 is not set +# CONFIG_ARM_ERRATA_751472 is not set +# CONFIG_ARM_ERRATA_754322 is not set +# CONFIG_ARM_ERRATA_754327 is not set +# CONFIG_ARM_ERRATA_764319 is not set +# CONFIG_ARM_ERRATA_764369 is not set +# CONFIG_ARM_ERRATA_773022 is not set +# CONFIG_ARM_ERRATA_775420 is not set +# CONFIG_ARM_ERRATA_798181 is not set +# CONFIG_ARM_ERRATA_814220 is not set +# CONFIG_ARM_ERRATA_818325_852422 is not set +# CONFIG_ARM_ERRATA_821420 is not set +# CONFIG_ARM_ERRATA_825619 is not set +# CONFIG_ARM_ERRATA_852421 is not set +# CONFIG_ARM_ERRATA_852423 is not set +# CONFIG_ARM_ERRATA_857271 is not set +# CONFIG_ARM_ERRATA_857272 is not set +# CONFIG_ARM_FFA_TRANSPORT is not set +CONFIG_ARM_GIC_MAX_NR=1 +# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set +# CONFIG_ARM_KPROBES_TEST is not set +# CONFIG_ARM_LPAE is not set +# CONFIG_ARM_MEDIATEK_CPUFREQ_HW is not set +# CONFIG_ARM_MHU is not set +CONFIG_ARM_MODULE_PLTS=y +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +# CONFIG_ARM_PSCI is not set +# CONFIG_ARM_PSCI_CHECKER is not set +# CONFIG_ARM_PSCI_CPUIDLE is not set +# CONFIG_ARM_PTDUMP_DEBUGFS is not set +# CONFIG_ARM_SBSA_WATCHDOG is not set +# CONFIG_ARM_SCMI_PROTOCOL is not set +# CONFIG_ARM_SCPI_PROTOCOL is not set +# CONFIG_ARM_SDE_INTERFACE is not set +# CONFIG_ARM_SMCCC_SOC_ID is not set +# CONFIG_ARM_SMC_WATCHDOG is not set +# CONFIG_ARM_SP805_WATCHDOG is not set +# CONFIG_ARM_SPE_PMU is not set +# CONFIG_ARM_THUMBEE is not set +# CONFIG_ARM_TIMER_SP804 is not set +# CONFIG_ARM_UNWIND is not set +# CONFIG_ARM_VIRT_EXT is not set +# CONFIG_AS3935 is not set +# CONFIG_AS73211 is not set +# CONFIG_ASM9260_TIMER is not set +# CONFIG_ASN1 is not set +# CONFIG_ASUS_LAPTOP is not set +# CONFIG_ASUS_WIRELESS is not set +# CONFIG_ASYMMETRIC_KEY_TYPE is not set +# CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE is not set +# CONFIG_ASYNC_RAID6_TEST is not set +# CONFIG_ASYNC_TX_DMA is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_AT803X_PHY is not set +# CONFIG_AT91_SAMA5D2_ADC is not set +# CONFIG_ATA is not set +# CONFIG_ATAGS is not set +CONFIG_ATAGS_PROC=y +# CONFIG_ATALK is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_ATA_ACPI is not set +CONFIG_ATA_BMDMA=y +# CONFIG_ATA_FORCE is not set +# CONFIG_ATA_GENERIC is not set +# CONFIG_ATA_LEDS is not set +# CONFIG_ATA_NONSTANDARD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_ATA_PIIX is not set +CONFIG_ATA_SFF=y +# CONFIG_ATA_VERBOSE_ERROR is not set +# CONFIG_ATH10K is not set +# CONFIG_ATH25 is not set +# CONFIG_ATH5K is not set +# CONFIG_ATH6KL is not set +# CONFIG_ATH79 is not set +# CONFIG_ATH9K is not set +# CONFIG_ATH9K_HTC is not set +# CONFIG_ATH_DEBUG is not set +# CONFIG_ATL1 is not set +# CONFIG_ATL1C is not set +# CONFIG_ATL1E is not set +# CONFIG_ATL2 is not set +# CONFIG_ATLAS_EZO_SENSOR is not set +# CONFIG_ATLAS_PH_SENSOR is not set +# CONFIG_ATM is not set +# CONFIG_ATMEL is not set +# CONFIG_ATMEL_PIT is not set +# CONFIG_ATMEL_SSC is not set +# CONFIG_ATM_AMBASSADOR is not set +# CONFIG_ATM_BR2684 is not set +CONFIG_ATM_BR2684_IPFILTER=y +# CONFIG_ATM_CLIP is not set +CONFIG_ATM_CLIP_NO_ICMP=y +# CONFIG_ATM_DRIVERS is not set +# CONFIG_ATM_DUMMY is not set +# CONFIG_ATM_ENI is not set +# CONFIG_ATM_FIRESTREAM is not set +# CONFIG_ATM_FORE200E is not set +# CONFIG_ATM_HE is not set +# CONFIG_ATM_HORIZON is not set +# CONFIG_ATM_IA is not set +# CONFIG_ATM_IDT77252 is not set +# CONFIG_ATM_LANAI is not set +# CONFIG_ATM_LANE is not set +# CONFIG_ATM_MPOA is not set +# CONFIG_ATM_NICSTAR is not set +# CONFIG_ATM_SOLOS is not set +# CONFIG_ATM_TCP is not set +# CONFIG_ATM_ZATM is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_ATP is not set +# CONFIG_AUDIT is not set +# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set +# CONFIG_AURORA_NB8800 is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTO_ZRELADDR is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_AX25 is not set +# CONFIG_AX25_DAMA_SLAVE is not set +# CONFIG_AX88796 is not set +# CONFIG_AX88796B_PHY is not set +# CONFIG_AXP20X_ADC is not set +# CONFIG_AXP20X_POWER is not set +# CONFIG_AXP288_ADC is not set +# CONFIG_AXP288_FUEL_GAUGE is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_B44 is not set +# CONFIG_B53 is not set +# CONFIG_B53_MDIO_DRIVER is not set +# CONFIG_B53_MMAP_DRIVER is not set +# CONFIG_B53_SERDES is not set +# CONFIG_B53_SPI_DRIVER is not set +# CONFIG_B53_SRAB_DRIVER is not set +# CONFIG_BACKLIGHT_ADP8860 is not set +# CONFIG_BACKLIGHT_ADP8870 is not set +# CONFIG_BACKLIGHT_APPLE is not set +# CONFIG_BACKLIGHT_ARCXCNN is not set +# CONFIG_BACKLIGHT_BD6107 is not set +# CONFIG_BACKLIGHT_CLASS_DEVICE is not set +# CONFIG_BACKLIGHT_GENERIC is not set +# CONFIG_BACKLIGHT_GPIO is not set +# CONFIG_BACKLIGHT_KTD253 is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_BACKLIGHT_LED is not set +# CONFIG_BACKLIGHT_LM3630A is not set +# CONFIG_BACKLIGHT_LM3639 is not set +# CONFIG_BACKLIGHT_LP855X is not set +# CONFIG_BACKLIGHT_LV5207LP is not set +# CONFIG_BACKLIGHT_PANDORA is not set +# CONFIG_BACKLIGHT_PM8941_WLED is not set +# CONFIG_BACKLIGHT_PWM is not set +# CONFIG_BACKLIGHT_QCOM_WLED is not set +# CONFIG_BACKLIGHT_RPI is not set +# CONFIG_BACKLIGHT_SAHARA is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_BACKTRACE_VERBOSE is not set +# CONFIG_BAREUDP is not set +CONFIG_BASE_FULL=y +CONFIG_BASE_SMALL=0 +# CONFIG_BATMAN_ADV is not set +# CONFIG_BATTERY_BQ27XXX is not set +# CONFIG_BATTERY_BQ27XXX_HDQ is not set +# CONFIG_BATTERY_CW2015 is not set +# CONFIG_BATTERY_DS2760 is not set +# CONFIG_BATTERY_DS2780 is not set +# CONFIG_BATTERY_DS2781 is not set +# CONFIG_BATTERY_DS2782 is not set +# CONFIG_BATTERY_GAUGE_LTC2941 is not set +# CONFIG_BATTERY_GOLDFISH is not set +# CONFIG_BATTERY_LEGO_EV3 is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_BATTERY_MAX1721X is not set +# CONFIG_BATTERY_RT5033 is not set +# CONFIG_BATTERY_SAMSUNG_SDI is not set +# CONFIG_BATTERY_SBS is not set +# CONFIG_BATTERY_UG3105 is not set +# CONFIG_BAYCOM_EPP is not set +# CONFIG_BAYCOM_PAR is not set +# CONFIG_BAYCOM_SER_FDX is not set +# CONFIG_BAYCOM_SER_HDX is not set +# CONFIG_BCACHE is not set +# CONFIG_BCM2712_MIP is not set +# CONFIG_BCM47XX is not set +# CONFIG_BCM54140_PHY is not set +# CONFIG_BCM63XX is not set +# CONFIG_BCM63XX_PHY is not set +# CONFIG_BCM7038_L1_IRQ is not set +# CONFIG_BCM7038_WDT is not set +# CONFIG_BCM7120_L2_IRQ is not set +# CONFIG_BCM7XXX_PHY is not set +# CONFIG_BCM84881_PHY is not set +# CONFIG_BCM87XX_PHY is not set +# CONFIG_BCMA is not set +# CONFIG_BCMA_DRIVER_GPIO is not set +CONFIG_BCMA_POSSIBLE=y +# CONFIG_BCMGENET is not set +# CONFIG_BCM_IPROC_ADC is not set +# CONFIG_BCM_KONA_USB2_PHY is not set +# CONFIG_BCM_SBA_RAID is not set +# CONFIG_BCM_VK is not set +# CONFIG_BDI_SWITCH is not set +# CONFIG_BE2ISCSI is not set +# CONFIG_BE2NET is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_BGMAC is not set +# CONFIG_BH1750 is not set +# CONFIG_BH1780 is not set +# CONFIG_BIG_KEYS is not set +# CONFIG_BIG_LITTLE is not set +CONFIG_BINARY_PRINTF=y +# CONFIG_BINFMT_AOUT is not set +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_ELF_FDPIC is not set +# CONFIG_BINFMT_FLAT is not set +# CONFIG_BINFMT_MISC is not set +CONFIG_BINFMT_SCRIPT=y +CONFIG_BITREVERSE=y +# CONFIG_BLK_CGROUP_IOCOST is not set +# CONFIG_BLK_CGROUP_IOLATENCY is not set +# CONFIG_BLK_CGROUP_IOPRIO is not set +# CONFIG_BLK_CMDLINE_PARSER is not set +# CONFIG_BLK_DEBUG_FS is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_BLK_DEV_4DRIVES is not set +# CONFIG_BLK_DEV_AEC62XX is not set +# CONFIG_BLK_DEV_ALI14XX is not set +# CONFIG_BLK_DEV_ALI15X3 is not set +# CONFIG_BLK_DEV_AMD74XX is not set +# CONFIG_BLK_DEV_ATIIXP is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_CMD64X is not set +# CONFIG_BLK_DEV_COW_COMMON is not set +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_CS5520 is not set +# CONFIG_BLK_DEV_CS5530 is not set +# CONFIG_BLK_DEV_CS5535 is not set +# CONFIG_BLK_DEV_CS5536 is not set +# CONFIG_BLK_DEV_CY82C693 is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_DELKIN is not set +# CONFIG_BLK_DEV_DM is not set +# CONFIG_BLK_DEV_DRBD is not set +# CONFIG_BLK_DEV_DTC2278 is not set +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_GENERIC is not set +# CONFIG_BLK_DEV_HPT366 is not set +# CONFIG_BLK_DEV_HT6560B is not set +# CONFIG_BLK_DEV_IDEACPI is not set +# CONFIG_BLK_DEV_IDECD is not set +# CONFIG_BLK_DEV_IDECS is not set +# CONFIG_BLK_DEV_IDEPCI is not set +# CONFIG_BLK_DEV_IDEPNP is not set +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDE_AU1XXX is not set +# CONFIG_BLK_DEV_IDE_SATA is not set +CONFIG_BLK_DEV_INITRD=y +# CONFIG_BLK_DEV_INTEGRITY is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_BLK_DEV_IT8172 is not set +# CONFIG_BLK_DEV_IT8213 is not set +# CONFIG_BLK_DEV_IT821X is not set +# CONFIG_BLK_DEV_JMICRON is not set +# CONFIG_BLK_DEV_LOOP is not set +CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 +# CONFIG_BLK_DEV_MD is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_NS87415 is not set +# CONFIG_BLK_DEV_NULL_BLK is not set +# CONFIG_BLK_DEV_NVME is not set +# CONFIG_BLK_DEV_OFFBOARD is not set +# CONFIG_BLK_DEV_OPTI621 is not set +# CONFIG_BLK_DEV_PCIESSD_MTIP32XX is not set +# CONFIG_BLK_DEV_PDC202XX_NEW is not set +# CONFIG_BLK_DEV_PDC202XX_OLD is not set +# CONFIG_BLK_DEV_PIIX is not set +# CONFIG_BLK_DEV_PLATFORM is not set +# CONFIG_BLK_DEV_PMEM is not set +# CONFIG_BLK_DEV_QD65XX is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_BLK_DEV_RBD is not set +# CONFIG_BLK_DEV_RSXX is not set +# CONFIG_BLK_DEV_RZ1000 is not set +# CONFIG_BLK_DEV_SC1200 is not set +# CONFIG_BLK_DEV_SD is not set +# CONFIG_BLK_DEV_SIIMAGE is not set +# CONFIG_BLK_DEV_SIS5513 is not set +# CONFIG_BLK_DEV_SKD is not set +# CONFIG_BLK_DEV_SL82C105 is not set +# CONFIG_BLK_DEV_SLC90E66 is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_BLK_DEV_SVWKS is not set +# CONFIG_BLK_DEV_SX8 is not set +# CONFIG_BLK_DEV_TC86C001 is not set +# CONFIG_BLK_DEV_THROTTLING is not set +# CONFIG_BLK_DEV_TRIFLEX is not set +# CONFIG_BLK_DEV_TRM290 is not set +# CONFIG_BLK_DEV_UBLK is not set +# CONFIG_BLK_DEV_UMC8672 is not set +# CONFIG_BLK_DEV_UMEM is not set +# CONFIG_BLK_DEV_VIA82CXXX is not set +# CONFIG_BLK_DEV_ZONED is not set +# CONFIG_BLK_INLINE_ENCRYPTION is not set +# CONFIG_BLK_NVMEM is not set +# CONFIG_BLK_SED_OPAL is not set +# CONFIG_BLK_WBT is not set +CONFIG_BLOCK=y +# CONFIG_BLOCK_LEGACY_AUTOLOAD is not set +# CONFIG_BMA180 is not set +# CONFIG_BMA220 is not set +# CONFIG_BMA400 is not set +# CONFIG_BMC150_ACCEL is not set +# CONFIG_BMC150_MAGN is not set +# CONFIG_BMC150_MAGN_I2C is not set +# CONFIG_BMC150_MAGN_SPI is not set +# CONFIG_BME680 is not set +# CONFIG_BMG160 is not set +# CONFIG_BMI088_ACCEL is not set +# CONFIG_BMI160_I2C is not set +# CONFIG_BMI160_SPI is not set +# CONFIG_BMIPS_GENERIC is not set +# CONFIG_BMP280 is not set +# CONFIG_BNA is not set +# CONFIG_BNX2 is not set +# CONFIG_BNX2X is not set +# CONFIG_BNX2X_SRIOV is not set +# CONFIG_BNXT is not set +# CONFIG_BONDING is not set +# CONFIG_BOOKE_WDT is not set +CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT=3 +# CONFIG_BOOTPARAM_HARDLOCKUP_PANIC is not set +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set +# CONFIG_BOOTTIME_TRACING is not set +# CONFIG_BOOT_CONFIG is not set +# CONFIG_BOOT_PRINTK_DELAY is not set +CONFIG_BOOT_RAW=y +# CONFIG_BOSCH_BNO055_I2C is not set +# CONFIG_BOSCH_BNO055_SERIAL is not set +# CONFIG_BOUNCE is not set +CONFIG_BPF=y +# CONFIG_BPFILTER is not set +CONFIG_BPF_JIT=y +# CONFIG_BPF_JIT_ALWAYS_ON is not set +CONFIG_BPF_JIT_DEFAULT_ON=y +# CONFIG_BPF_PRELOAD is not set +# CONFIG_BPF_STREAM_PARSER is not set +CONFIG_BPF_SYSCALL=y +CONFIG_BPF_UNPRIV_DEFAULT_OFF=y +# CONFIG_BPQETHER is not set +CONFIG_BQL=y +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_BRCMFMAC is not set +# CONFIG_BRCMSMAC is not set +# CONFIG_BRCMSTB_GISB_ARB is not set +# CONFIG_BRCMSTB_L2_IRQ is not set +CONFIG_BRIDGE=y +# CONFIG_BRIDGE_CFM is not set +# CONFIG_BRIDGE_EBT_802_3 is not set +# CONFIG_BRIDGE_EBT_AMONG is not set +# CONFIG_BRIDGE_EBT_ARP is not set +# CONFIG_BRIDGE_EBT_ARPREPLY is not set +# CONFIG_BRIDGE_EBT_BROUTE is not set +# CONFIG_BRIDGE_EBT_DNAT is not set +# CONFIG_BRIDGE_EBT_IP is not set +# CONFIG_BRIDGE_EBT_IP6 is not set +# CONFIG_BRIDGE_EBT_LIMIT is not set +# CONFIG_BRIDGE_EBT_LOG is not set +# CONFIG_BRIDGE_EBT_MARK is not set +# CONFIG_BRIDGE_EBT_MARK_T is not set +# CONFIG_BRIDGE_EBT_NFLOG is not set +# CONFIG_BRIDGE_EBT_PKTTYPE is not set +# CONFIG_BRIDGE_EBT_REDIRECT is not set +# CONFIG_BRIDGE_EBT_SNAT is not set +# CONFIG_BRIDGE_EBT_STP is not set +# CONFIG_BRIDGE_EBT_T_FILTER is not set +# CONFIG_BRIDGE_EBT_T_NAT is not set +# CONFIG_BRIDGE_EBT_VLAN is not set +CONFIG_BRIDGE_IGMP_SNOOPING=y +# CONFIG_BRIDGE_MRP is not set +# CONFIG_BRIDGE_NETFILTER is not set +# CONFIG_BRIDGE_NF_EBTABLES is not set +CONFIG_BRIDGE_VLAN_FILTERING=y +# CONFIG_BROADCOM_PHY is not set +CONFIG_BROKEN_ON_SMP=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +# CONFIG_BT is not set +# CONFIG_BTRFS_ASSERT is not set +# CONFIG_BTRFS_DEBUG is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_BTRFS_FS_POSIX_ACL is not set +# CONFIG_BTRFS_FS_REF_VERIFY is not set +# CONFIG_BTRFS_FS_RUN_SANITY_TESTS is not set +# CONFIG_BT_AOSPEXT is not set +# CONFIG_BT_ATH3K is not set +# CONFIG_BT_BNEP is not set +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +# CONFIG_BT_BREDR is not set +# CONFIG_BT_CMTP is not set +# CONFIG_BT_FEATURE_DEBUG is not set +# CONFIG_BT_HCIBCM203X is not set +# CONFIG_BT_HCIBFUSB is not set +# CONFIG_BT_HCIBLUECARD is not set +# CONFIG_BT_HCIBPA10X is not set +# CONFIG_BT_HCIBT3C is not set +# CONFIG_BT_HCIBTSDIO is not set +# CONFIG_BT_HCIBTUSB is not set +# CONFIG_BT_HCIBTUSB_AUTOSUSPEND is not set +# CONFIG_BT_HCIBTUSB_MTK is not set +# CONFIG_BT_HCIBTUSB_RTL is not set +# CONFIG_BT_HCIDTL1 is not set +# CONFIG_BT_HCIUART is not set +# CONFIG_BT_HCIUART_3WIRE is not set +# CONFIG_BT_HCIUART_AG6XX is not set +# CONFIG_BT_HCIUART_ATH3K is not set +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_H4=y +# CONFIG_BT_HCIUART_LL is not set +# CONFIG_BT_HCIUART_MRVL is not set +# CONFIG_BT_HCIUART_QCA is not set +# CONFIG_BT_HCIUART_RTL is not set +# CONFIG_BT_HCIVHCI is not set +# CONFIG_BT_HIDP is not set +# CONFIG_BT_HS is not set +# CONFIG_BT_LE is not set +# CONFIG_BT_LEDS is not set +# CONFIG_BT_MRVL is not set +# CONFIG_BT_MSFTEXT is not set +# CONFIG_BT_MTKSDIO is not set +# CONFIG_BT_MTKUART is not set +# CONFIG_BT_RFCOMM is not set +CONFIG_BT_RFCOMM_TTY=y +# CONFIG_BT_SELFTEST is not set +# CONFIG_BT_VIRTIO is not set +CONFIG_BUG=y +# CONFIG_BUG_ON_DATA_CORRUPTION is not set +CONFIG_BUILDTIME_EXTABLE_SORT=y +CONFIG_BUILDTIME_TABLE_SORT=y +# CONFIG_BUILD_BIN2C is not set +CONFIG_BUILD_SALT="" +# CONFIG_C2PORT is not set +CONFIG_CACHE_L2X0_PMU=y +# CONFIG_CADENCE_WATCHDOG is not set +# CONFIG_CAIF is not set +# CONFIG_CAN is not set +# CONFIG_CAN_BCM is not set +# CONFIG_CAN_CAN327 is not set +# CONFIG_CAN_CTUCANFD_PCI is not set +# CONFIG_CAN_CTUCANFD_PLATFORM is not set +# CONFIG_CAN_DEBUG_DEVICES is not set +# CONFIG_CAN_DEV is not set +# CONFIG_CAN_ESD_USB is not set +# CONFIG_CAN_ETAS_ES58X is not set +# CONFIG_CAN_GS_USB is not set +# CONFIG_CAN_GW is not set +# CONFIG_CAN_HI311X is not set +# CONFIG_CAN_IFI_CANFD is not set +# CONFIG_CAN_ISOTP is not set +# CONFIG_CAN_J1939 is not set +# CONFIG_CAN_KVASER_PCIEFD is not set +# CONFIG_CAN_MCBA_USB is not set +# CONFIG_CAN_MCP251XFD is not set +# CONFIG_CAN_M_CAN is not set +# CONFIG_CAN_NETLINK is not set +# CONFIG_CAN_PEAK_PCIEFD is not set +# CONFIG_CAN_RAW is not set +# CONFIG_CAN_RCAR is not set +# CONFIG_CAN_RCAR_CANFD is not set +# CONFIG_CAN_SLCAN is not set +# CONFIG_CAN_SUN4I is not set +# CONFIG_CAN_UCAN is not set +# CONFIG_CAN_VCAN is not set +# CONFIG_CAN_VXCAN is not set +# CONFIG_CAPI_AVM is not set +# CONFIG_CAPI_EICON is not set +# CONFIG_CAPI_TRACE is not set +CONFIG_CARDBUS=y +# CONFIG_CARDMAN_4000 is not set +# CONFIG_CARDMAN_4040 is not set +# CONFIG_CARL9170 is not set +# CONFIG_CASSINI is not set +# CONFIG_CAVIUM_CPT is not set +# CONFIG_CAVIUM_ERRATUM_22375 is not set +# CONFIG_CAVIUM_ERRATUM_23144 is not set +# CONFIG_CAVIUM_ERRATUM_23154 is not set +# CONFIG_CAVIUM_ERRATUM_27456 is not set +# CONFIG_CAVIUM_ERRATUM_30115 is not set +# CONFIG_CAVIUM_OCTEON_SOC is not set +# CONFIG_CAVIUM_PTP is not set +# CONFIG_CAVIUM_TX2_ERRATUM_219 is not set +# CONFIG_CB710_CORE is not set +# CONFIG_CC10001_ADC is not set +# CONFIG_CCS811 is not set +CONFIG_CC_CAN_LINK=y +CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_CEPH_FS is not set +# CONFIG_CEPH_LIB is not set +# CONFIG_CFG80211 is not set +# CONFIG_CFG80211_CERTIFICATION_ONUS is not set +CONFIG_CFG80211_HEADERS=y +# CONFIG_CGROUPS is not set +# CONFIG_CGROUP_FAVOR_DYNMODS is not set +# CONFIG_CGROUP_MISC is not set +# CONFIG_CHARGER_ADP5061 is not set +# CONFIG_CHARGER_BD99954 is not set +# CONFIG_CHARGER_BQ2415X is not set +# CONFIG_CHARGER_BQ24190 is not set +# CONFIG_CHARGER_BQ24257 is not set +# CONFIG_CHARGER_BQ24735 is not set +# CONFIG_CHARGER_BQ2515X is not set +# CONFIG_CHARGER_BQ256XX is not set +# CONFIG_CHARGER_BQ25890 is not set +# CONFIG_CHARGER_BQ25980 is not set +# CONFIG_CHARGER_DETECTOR_MAX14656 is not set +# CONFIG_CHARGER_GPIO is not set +# CONFIG_CHARGER_ISP1704 is not set +# CONFIG_CHARGER_LP8727 is not set +# CONFIG_CHARGER_LT3651 is not set +# CONFIG_CHARGER_LTC3651 is not set +# CONFIG_CHARGER_LTC4162L is not set +# CONFIG_CHARGER_MANAGER is not set +# CONFIG_CHARGER_MAX77976 is not set +# CONFIG_CHARGER_MAX8903 is not set +# CONFIG_CHARGER_QCOM_SMBB is not set +# CONFIG_CHARGER_RT9455 is not set +# CONFIG_CHARGER_SBS is not set +# CONFIG_CHARGER_SMB347 is not set +# CONFIG_CHARGER_TWL4030 is not set +# CONFIG_CHARGER_UCS1002 is not set +# CONFIG_CHASH_SELFTEST is not set +# CONFIG_CHASH_STATS is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_CHELSIO_T1 is not set +# CONFIG_CHELSIO_T3 is not set +# CONFIG_CHELSIO_T4 is not set +# CONFIG_CHELSIO_T4VF is not set +# CONFIG_CHROME_PLATFORMS is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_CHR_DEV_SCH is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CICADA_PHY is not set +# CONFIG_CIFS is not set +# CONFIG_CIFS_ACL is not set +CONFIG_CIFS_ALLOW_INSECURE_LEGACY=y +# CONFIG_CIFS_DEBUG is not set +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_FSCACHE is not set +# CONFIG_CIFS_NFSD_EXPORT is not set +CONFIG_CIFS_POSIX=y +# CONFIG_CIFS_SMB2 is not set +# CONFIG_CIFS_STATS is not set +# CONFIG_CIFS_STATS2 is not set +# CONFIG_CIFS_SWN_UPCALL is not set +# CONFIG_CIFS_WEAK_PW_HASH is not set +CONFIG_CIFS_XATTR=y +# CONFIG_CIO_DAC is not set +# CONFIG_CLEANCACHE is not set +# CONFIG_CLKSRC_PISTACHIO is not set +# CONFIG_CLKSRC_VERSATILE is not set +# CONFIG_CLK_GFM_LPASS_SM8250 is not set +# CONFIG_CLK_HSDK is not set +# CONFIG_CLK_ICST is not set +# CONFIG_CLK_QORIQ is not set +# CONFIG_CLK_SP810 is not set +# CONFIG_CLOCK_THERMAL is not set +CONFIG_CLS_U32_MARK=y +# CONFIG_CLS_U32_PERF is not set +# CONFIG_CM32181 is not set +# CONFIG_CM3232 is not set +# CONFIG_CM3323 is not set +# CONFIG_CM3605 is not set +# CONFIG_CM36651 is not set +# CONFIG_CMA is not set +CONFIG_CMDLINE="" +# CONFIG_CMDLINE_BOOL is not set +# CONFIG_CMDLINE_EXTEND is not set +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_CMDLINE_FROM_BOOTLOADER is not set +# CONFIG_CMDLINE_PARTITION is not set +# CONFIG_CNIC is not set +# CONFIG_CODA_FS is not set +# CONFIG_CODE_PATCHING_SELFTEST is not set +# CONFIG_COMEDI is not set +# CONFIG_COMMON_CLK_AXI_CLKGEN is not set +# CONFIG_COMMON_CLK_BOSTON is not set +# CONFIG_COMMON_CLK_CDCE706 is not set +# CONFIG_COMMON_CLK_CDCE925 is not set +# CONFIG_COMMON_CLK_CS2000_CP is not set +# CONFIG_COMMON_CLK_FIXED_MMIO is not set +# CONFIG_COMMON_CLK_IPROC is not set +# CONFIG_COMMON_CLK_MAX9485 is not set +# CONFIG_COMMON_CLK_MT6765 is not set +# CONFIG_COMMON_CLK_MT8167 is not set +# CONFIG_COMMON_CLK_MT8167_AUDSYS is not set +# CONFIG_COMMON_CLK_MT8167_IMGSYS is not set +# CONFIG_COMMON_CLK_MT8167_MFGCFG is not set +# CONFIG_COMMON_CLK_MT8167_MMSYS is not set +# CONFIG_COMMON_CLK_MT8167_VDECSYS is not set +# CONFIG_COMMON_CLK_MT8192 is not set +# CONFIG_COMMON_CLK_NXP is not set +# CONFIG_COMMON_CLK_PIC32 is not set +# CONFIG_COMMON_CLK_PISTACHIO is not set +# CONFIG_COMMON_CLK_PWM is not set +# CONFIG_COMMON_CLK_PXA is not set +# CONFIG_COMMON_CLK_QCOM is not set +# CONFIG_COMMON_CLK_RP1 is not set +# CONFIG_COMMON_CLK_RP1_SDIO is not set +# CONFIG_COMMON_CLK_RS9_PCIE is not set +# CONFIG_COMMON_CLK_SI514 is not set +# CONFIG_COMMON_CLK_SI5341 is not set +# CONFIG_COMMON_CLK_SI5351 is not set +# CONFIG_COMMON_CLK_SI544 is not set +# CONFIG_COMMON_CLK_SI570 is not set +# CONFIG_COMMON_CLK_VC5 is not set +# CONFIG_COMMON_CLK_VC7 is not set +# CONFIG_COMMON_CLK_XGENE is not set +# CONFIG_COMMON_CLK_XLNX_CLKWZRD is not set +CONFIG_COMPACTION=y +# CONFIG_COMPAL_LAPTOP is not set +# CONFIG_COMPAT is not set +# CONFIG_COMPAT_BRK is not set +# CONFIG_COMPILE_TEST is not set +# CONFIG_CONFIGFS_FS is not set +# CONFIG_CONNECTOR is not set +CONFIG_CONSOLE_LOGLEVEL_DEFAULT=7 +CONFIG_CONSOLE_LOGLEVEL_QUIET=4 +CONFIG_CONSTRUCTORS=y +# CONFIG_CONTEXT_SWITCH_TRACER is not set +# CONFIG_COPS is not set +# CONFIG_CORDIC is not set +# CONFIG_COREDUMP is not set +# CONFIG_CORESIGHT is not set +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +# CONFIG_CORTINA_PHY is not set +# CONFIG_COUNTER is not set +# CONFIG_CPA_DEBUG is not set +# CONFIG_CPU_BIG_ENDIAN is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +# CONFIG_CPU_FREQ_GOV_SCHEDUTIL is not set +# CONFIG_CPU_FREQ_STAT_DETAILS is not set +# CONFIG_CPU_FREQ_THERMAL is not set +# CONFIG_CPU_HOTPLUG_STATE_CONTROL is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_ICACHE_MISMATCH_WORKAROUND is not set +# CONFIG_CPU_IDLE is not set +# CONFIG_CPU_IDLE_GOV_LADDER is not set +# CONFIG_CPU_IDLE_GOV_MENU is not set +# CONFIG_CPU_IDLE_GOV_TEO is not set +# CONFIG_CPU_IDLE_MULTIPLE_DRIVERS is not set +# CONFIG_CPU_ISOLATION is not set +# CONFIG_CPU_LITTLE_ENDIAN is not set +# CONFIG_CPU_NO_EFFICIENT_FFS is not set +CONFIG_CPU_SW_DOMAIN_PAN=y +# CONFIG_CPU_THERMAL is not set +# CONFIG_CRAMFS is not set +CONFIG_CRAMFS_BLOCKDEV=y +# CONFIG_CRAMFS_MTD is not set +# CONFIG_CRASH_DUMP is not set +# CONFIG_CRC16 is not set +CONFIG_CRC32=y +# CONFIG_CRC32_BIT is not set +CONFIG_CRC32_SARWATE=y +# CONFIG_CRC32_SELFTEST is not set +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SLICEBY8 is not set +# CONFIG_CRC4 is not set +# CONFIG_CRC64 is not set +# CONFIG_CRC64_ROCKSOFT is not set +# CONFIG_CRC7 is not set +# CONFIG_CRC8 is not set +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC_ITU_T is not set +# CONFIG_CRC_T10DIF is not set +CONFIG_CROSS_COMPILE="" +# CONFIG_CROSS_MEMORY_ATTACH is not set +CONFIG_CRYPTO=y +# CONFIG_CRYPTO_842 is not set +CONFIG_CRYPTO_ACOMP2=y +# CONFIG_CRYPTO_ADIANTUM is not set +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +# CONFIG_CRYPTO_AEGIS128 is not set +# CONFIG_CRYPTO_AEGIS128L is not set +# CONFIG_CRYPTO_AEGIS128L_AESNI_SSE2 is not set +# CONFIG_CRYPTO_AEGIS128_AESNI_SSE2 is not set +# CONFIG_CRYPTO_AEGIS256 is not set +# CONFIG_CRYPTO_AEGIS256_AESNI_SSE2 is not set +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_AES_586 is not set +# CONFIG_CRYPTO_AES_ARM is not set +# CONFIG_CRYPTO_AES_ARM64 is not set +# CONFIG_CRYPTO_AES_ARM64_BS is not set +# CONFIG_CRYPTO_AES_ARM64_CE is not set +# CONFIG_CRYPTO_AES_ARM64_CE_BLK is not set +# CONFIG_CRYPTO_AES_ARM64_CE_CCM is not set +# CONFIG_CRYPTO_AES_ARM64_NEON_BLK is not set +# CONFIG_CRYPTO_AES_ARM_BS is not set +# CONFIG_CRYPTO_AES_ARM_CE is not set +# CONFIG_CRYPTO_AES_NI_INTEL is not set +# CONFIG_CRYPTO_AES_TI is not set +CONFIG_CRYPTO_AKCIPHER=y +CONFIG_CRYPTO_AKCIPHER2=y +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_ARIA is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_BLAKE2B is not set +# CONFIG_CRYPTO_BLAKE2B_NEON is not set +# CONFIG_CRYPTO_BLAKE2S_ARM is not set +# CONFIG_CRYPTO_BLAKE2S_X86 is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_CBC is not set +CONFIG_CRYPTO_CCM=y +# CONFIG_CRYPTO_CFB is not set +# CONFIG_CRYPTO_CHACHA20 is not set +# CONFIG_CRYPTO_CHACHA20POLY1305 is not set +# CONFIG_CRYPTO_CHACHA20_NEON is not set +# CONFIG_CRYPTO_CHACHA20_X86_64 is not set +# CONFIG_CRYPTO_CHACHA_MIPS is not set +# CONFIG_CRYPTO_CMAC is not set +# CONFIG_CRYPTO_CRC32 is not set +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_CRC32C_INTEL is not set +# CONFIG_CRYPTO_CRC32_ARM_CE is not set +# CONFIG_CRYPTO_CRCT10DIF is not set +# CONFIG_CRYPTO_CRCT10DIF_ARM64_CE is not set +# CONFIG_CRYPTO_CRCT10DIF_ARM_CE is not set +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_CTR=y +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_CURVE25519 is not set +# CONFIG_CRYPTO_CURVE25519_NEON is not set +# CONFIG_CRYPTO_CURVE25519_X86 is not set +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_DEV_AMLOGIC_GXL is not set +# CONFIG_CRYPTO_DEV_ATMEL_AES is not set +# CONFIG_CRYPTO_DEV_ATMEL_AUTHENC is not set +# CONFIG_CRYPTO_DEV_ATMEL_ECC is not set +# CONFIG_CRYPTO_DEV_ATMEL_SHA is not set +# CONFIG_CRYPTO_DEV_ATMEL_SHA204A is not set +# CONFIG_CRYPTO_DEV_ATMEL_TDES is not set +# CONFIG_CRYPTO_DEV_CAVIUM_ZIP is not set +# CONFIG_CRYPTO_DEV_CCP is not set +# CONFIG_CRYPTO_DEV_CCP_DEBUGFS is not set +# CONFIG_CRYPTO_DEV_CCREE is not set +# CONFIG_CRYPTO_DEV_FSL_CAAM is not set +# CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_DESC is not set +# CONFIG_CRYPTO_DEV_HIFN_795X is not set +# CONFIG_CRYPTO_DEV_HISI_SEC is not set +# CONFIG_CRYPTO_DEV_HISI_ZIP is not set +# CONFIG_CRYPTO_DEV_IMGTEC_HASH is not set +# CONFIG_CRYPTO_DEV_MARVELL_CESA is not set +# CONFIG_CRYPTO_DEV_MEDIATEK is not set +# CONFIG_CRYPTO_DEV_MV_CESA is not set +# CONFIG_CRYPTO_DEV_MXC_SCC is not set +# CONFIG_CRYPTO_DEV_MXS_DCP is not set +# CONFIG_CRYPTO_DEV_NITROX_CNN55XX is not set +# CONFIG_CRYPTO_DEV_OCTEONTX_CPT is not set +# CONFIG_CRYPTO_DEV_QAT_4XXX is not set +# CONFIG_CRYPTO_DEV_QAT_C3XXX is not set +# CONFIG_CRYPTO_DEV_QAT_C3XXXVF is not set +# CONFIG_CRYPTO_DEV_QAT_C62X is not set +# CONFIG_CRYPTO_DEV_QAT_C62XVF is not set +# CONFIG_CRYPTO_DEV_QAT_DH895xCC is not set +# CONFIG_CRYPTO_DEV_QAT_DH895xCCVF is not set +# CONFIG_CRYPTO_DEV_QCE is not set +# CONFIG_CRYPTO_DEV_S5P is not set +# CONFIG_CRYPTO_DEV_SAFEXCEL is not set +# CONFIG_CRYPTO_DEV_SAHARA is not set +# CONFIG_CRYPTO_DEV_SP_PSP is not set +# CONFIG_CRYPTO_DEV_TALITOS is not set +# CONFIG_CRYPTO_DEV_VIRTIO is not set +# CONFIG_CRYPTO_DH is not set +# CONFIG_CRYPTO_DRBG_CTR is not set +# CONFIG_CRYPTO_DRBG_HASH is not set +# CONFIG_CRYPTO_DRBG_MENU is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_ECDH is not set +# CONFIG_CRYPTO_ECDSA is not set +# CONFIG_CRYPTO_ECHAINIV is not set +# CONFIG_CRYPTO_ECRDSA is not set +# CONFIG_CRYPTO_ESSIV is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_FIPS is not set +CONFIG_CRYPTO_GCM=y +CONFIG_CRYPTO_GF128MUL=y +CONFIG_CRYPTO_GHASH=y +# CONFIG_CRYPTO_GHASH_ARM64_CE is not set +# CONFIG_CRYPTO_GHASH_ARM_CE is not set +# CONFIG_CRYPTO_GHASH_CLMUL_NI_INTEL is not set +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +# CONFIG_CRYPTO_HCTR2 is not set +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_HW is not set +# CONFIG_CRYPTO_JITTERENTROPY is not set +# CONFIG_CRYPTO_KEYWRAP is not set +# CONFIG_CRYPTO_KHAZAD is not set +CONFIG_CRYPTO_KPP=y +CONFIG_CRYPTO_KPP2=y +CONFIG_CRYPTO_LIB_AES=y +CONFIG_CRYPTO_LIB_ARC4=y +# CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC is not set +# CONFIG_CRYPTO_LIB_CHACHA is not set +# CONFIG_CRYPTO_LIB_CHACHA20POLY1305 is not set +# CONFIG_CRYPTO_LIB_CURVE25519 is not set +# CONFIG_CRYPTO_LIB_POLY1305 is not set +CONFIG_CRYPTO_LIB_POLY1305_RSIZE=9 +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_LZ4 is not set +# CONFIG_CRYPTO_LZ4HC is not set +# CONFIG_CRYPTO_LZO is not set +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_MCRYPTD is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_MORUS1280 is not set +# CONFIG_CRYPTO_MORUS1280_AVX2 is not set +# CONFIG_CRYPTO_MORUS1280_SSE2 is not set +# CONFIG_CRYPTO_MORUS640 is not set +# CONFIG_CRYPTO_MORUS640_SSE2 is not set +# CONFIG_CRYPTO_NHPOLY1305_NEON is not set +CONFIG_CRYPTO_NULL=y +CONFIG_CRYPTO_NULL2=y +# CONFIG_CRYPTO_OFB is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_PCOMP is not set +# CONFIG_CRYPTO_PCOMP2 is not set +CONFIG_CRYPTO_PCRYPT=y +# CONFIG_CRYPTO_POLY1305 is not set +# CONFIG_CRYPTO_POLY1305_ARM is not set +# CONFIG_CRYPTO_POLY1305_MIPS is not set +# CONFIG_CRYPTO_POLY1305_NEON is not set +# CONFIG_CRYPTO_POLY1305_X86_64 is not set +# CONFIG_CRYPTO_POLYVAL_ARM64_CE is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RNG is not set +# CONFIG_CRYPTO_RSA is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SEQIV is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA1_ARM is not set +# CONFIG_CRYPTO_SHA1_ARM64_CE is not set +# CONFIG_CRYPTO_SHA1_ARM_CE is not set +# CONFIG_CRYPTO_SHA1_ARM_NEON is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA256_ARM is not set +# CONFIG_CRYPTO_SHA256_ARM64 is not set +# CONFIG_CRYPTO_SHA2_ARM64_CE is not set +# CONFIG_CRYPTO_SHA2_ARM_CE is not set +# CONFIG_CRYPTO_SHA3 is not set +# CONFIG_CRYPTO_SHA3_ARM64 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_SHA512_ARM is not set +# CONFIG_CRYPTO_SHA512_ARM64 is not set +# CONFIG_CRYPTO_SHA512_ARM64_CE is not set +# CONFIG_CRYPTO_SIMD is not set +CONFIG_CRYPTO_SKCIPHER=y +CONFIG_CRYPTO_SKCIPHER2=y +# CONFIG_CRYPTO_SM2 is not set +# CONFIG_CRYPTO_SM3 is not set +# CONFIG_CRYPTO_SM3_ARM64_CE is not set +# CONFIG_CRYPTO_SM3_GENERIC is not set +# CONFIG_CRYPTO_SM3_NEON is not set +# CONFIG_CRYPTO_SM4 is not set +# CONFIG_CRYPTO_SM4_ARM64_CE is not set +# CONFIG_CRYPTO_SM4_ARM64_CE_BLK is not set +# CONFIG_CRYPTO_SM4_ARM64_NEON_BLK is not set +# CONFIG_CRYPTO_SM4_GENERIC is not set +# CONFIG_CRYPTO_SPECK is not set +# CONFIG_CRYPTO_STATS is not set +# CONFIG_CRYPTO_STREEBOG is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TEST is not set +# CONFIG_CRYPTO_TWOFISH is not set +# CONFIG_CRYPTO_TWOFISH_586 is not set +# CONFIG_CRYPTO_TWOFISH_COMMON is not set +# CONFIG_CRYPTO_USER is not set +# CONFIG_CRYPTO_USER_API_AEAD is not set +# CONFIG_CRYPTO_USER_API_ENABLE_OBSOLETE is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_RNG is not set +# CONFIG_CRYPTO_USER_API_RNG_CAVP is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +# CONFIG_CRYPTO_VMAC is not set +# CONFIG_CRYPTO_WP512 is not set +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_XTS is not set +# CONFIG_CRYPTO_XXHASH is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_ZSTD is not set +# CONFIG_CS5535_MFGPT is not set +# CONFIG_CS89x0 is not set +# CONFIG_CS89x0_PLATFORM is not set +# CONFIG_CSD_LOCK_WAIT_DEBUG is not set +# CONFIG_CUSE is not set +# CONFIG_CW1200 is not set +# CONFIG_CXD2880_SPI_DRV is not set +# CONFIG_CXL_AFU_DRIVER_OPS is not set +# CONFIG_CXL_BASE is not set +# CONFIG_CXL_BUS is not set +# CONFIG_CXL_EEH is not set +# CONFIG_CXL_KERNEL_API is not set +# CONFIG_CXL_LIB is not set +# CONFIG_CYPRESS_FIRMWARE is not set +# CONFIG_DA280 is not set +# CONFIG_DA311 is not set +# CONFIG_DAMON is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_DAX is not set +# CONFIG_DCB is not set +# CONFIG_DDR is not set +# CONFIG_DEBUG_ALIGN_RODATA is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_CREDENTIALS is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_EFI is not set +# CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_32B is not set +# CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_64B is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +CONFIG_DEBUG_FS=y +CONFIG_DEBUG_FS_ALLOW_ALL=y +# CONFIG_DEBUG_FS_ALLOW_NONE is not set +# CONFIG_DEBUG_FS_DISALLOW_MOUNT is not set +# CONFIG_DEBUG_GPIO is not set +# CONFIG_DEBUG_HIGHMEM is not set +# CONFIG_DEBUG_ICEDCC is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_INFO_BTF is not set +# CONFIG_DEBUG_INFO_COMPRESSED is not set +# CONFIG_DEBUG_INFO_DWARF4 is not set +# CONFIG_DEBUG_INFO_DWARF5 is not set +CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y +# CONFIG_DEBUG_INFO_NONE is not set +# CONFIG_DEBUG_INFO_REDUCED is not set +# CONFIG_DEBUG_INFO_SPLIT is not set +# CONFIG_DEBUG_IRQFLAGS is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_KMAP_LOCAL is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_KOBJECT_RELEASE is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_DEBUG_LL is not set +# CONFIG_DEBUG_LL_UART_8250 is not set +# CONFIG_DEBUG_LL_UART_PL01X is not set +# CONFIG_DEBUG_LOCKDEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_DEBUG_MAPLE_TREE is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_MISC is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_NET is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_NX_TEST is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_PAGEALLOC is not set +# CONFIG_DEBUG_PAGE_REF is not set +# CONFIG_DEBUG_PERF_USE_VMALLOC is not set +# CONFIG_DEBUG_PER_CPU_MAPS is not set +# CONFIG_DEBUG_PINCTRL is not set +# CONFIG_DEBUG_PI_LIST is not set +# CONFIG_DEBUG_PLIST is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_DEBUG_RODATA_TEST is not set +# CONFIG_DEBUG_RSEQ is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_DEBUG_RWSEMS is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +# CONFIG_DEBUG_SEMIHOSTING is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_STACKOVERFLOW is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_STRICT_USER_COPY_CHECKS is not set +# CONFIG_DEBUG_TEST_DRIVER_REMOVE is not set +# CONFIG_DEBUG_TIMEKEEPING is not set +# CONFIG_DEBUG_UART_8250_PALMCHIP is not set +# CONFIG_DEBUG_UART_8250_WORD is not set +# CONFIG_DEBUG_UART_BCM63XX is not set +# CONFIG_DEBUG_UART_FLOW_CONTROL is not set +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_VIRTUAL is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_VM_PGFLAGS is not set +# CONFIG_DEBUG_VM_PGTABLE is not set +# CONFIG_DEBUG_VM_RB is not set +# CONFIG_DEBUG_VM_VMACACHE is not set +# CONFIG_DEBUG_WQ_FORCE_RR_CPU is not set +# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set +# CONFIG_DEBUG_WX is not set +# CONFIG_DEBUG_ZBOOT is not set +# CONFIG_DECNET is not set +# CONFIG_DEFAULT_CODEL is not set +CONFIG_DEFAULT_CUBIC=y +CONFIG_DEFAULT_DEADLINE=y +# CONFIG_DEFAULT_FQ is not set +CONFIG_DEFAULT_FQ_CODEL=y +# CONFIG_DEFAULT_FQ_PIE is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120 +CONFIG_DEFAULT_INIT="" +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_DEFAULT_NET_SCH="fq_codel" +# CONFIG_DEFAULT_NOOP is not set +# CONFIG_DEFAULT_PFIFO_FAST is not set +# CONFIG_DEFAULT_RENO is not set +CONFIG_DEFAULT_SECURITY="" +CONFIG_DEFAULT_SECURITY_DAC=y +# CONFIG_DEFAULT_SECURITY_SELINUX is not set +# CONFIG_DEFAULT_SFQ is not set +CONFIG_DEFAULT_TCP_CONG="cubic" +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +# CONFIG_DEFERRED_STRUCT_PAGE_INIT is not set +# CONFIG_DELL_LAPTOP is not set +# CONFIG_DELL_RBTN is not set +# CONFIG_DELL_SMBIOS is not set +# CONFIG_DELL_SMO8800 is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_DEVKMEM is not set +# CONFIG_DEVMEM is not set +CONFIG_DEVPORT=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_DEVTMPFS is not set +# CONFIG_DEVTMPFS_MOUNT is not set +# CONFIG_DEVTMPFS_SAFE is not set +# CONFIG_DEV_DAX is not set +# CONFIG_DGAP is not set +# CONFIG_DGNC is not set +# CONFIG_DHT11 is not set +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_DISPLAY_CONNECTOR_ANALOG_TV is not set +# CONFIG_DISPLAY_CONNECTOR_DVI is not set +# CONFIG_DISPLAY_CONNECTOR_HDMI is not set +# CONFIG_DISPLAY_ENCODER_TFP410 is not set +# CONFIG_DISPLAY_ENCODER_TPD12S015 is not set +# CONFIG_DISPLAY_PANEL_DPI is not set +# CONFIG_DISPLAY_PANEL_LGPHILIPS_LB035Q02 is not set +# CONFIG_DISPLAY_PANEL_TPO_TD028TTEC1 is not set +# CONFIG_DISPLAY_PANEL_TPO_TD043MTEA1 is not set +# CONFIG_DL2K is not set +# CONFIG_DLHL60D is not set +# CONFIG_DLM is not set +# CONFIG_DM9000 is not set +# CONFIG_DM9051 is not set +# CONFIG_DMABUF_DEBUG is not set +# CONFIG_DMABUF_HEAPS is not set +# CONFIG_DMABUF_MOVE_NOTIFY is not set +# CONFIG_DMABUF_SELFTESTS is not set +# CONFIG_DMABUF_SYSFS_STATS is not set +# CONFIG_DMADEVICES is not set +# CONFIG_DMADEVICES_DEBUG is not set +# CONFIG_DMARD06 is not set +# CONFIG_DMARD09 is not set +# CONFIG_DMARD10 is not set +# CONFIG_DMASCC is not set +# CONFIG_DMATEST is not set +# CONFIG_DMA_API_DEBUG is not set +CONFIG_DMA_COHERENT_POOL=y +CONFIG_DMA_DECLARE_COHERENT=y +# CONFIG_DMA_ENGINE is not set +# CONFIG_DMA_FENCE_TRACE is not set +# CONFIG_DMA_JZ4780 is not set +# CONFIG_DMA_MAP_BENCHMARK is not set +CONFIG_DMA_NONCOHERENT_MMAP=y +# CONFIG_DMA_NOOP_OPS is not set +# CONFIG_DMA_PERNUMA_CMA is not set +# CONFIG_DMA_RESTRICTED_POOL is not set +# CONFIG_DMA_SHARED_BUFFER is not set +# CONFIG_DMA_VIRT_OPS is not set +# CONFIG_DM_CACHE is not set +# CONFIG_DM_CLONE is not set +# CONFIG_DM_DEBUG is not set +# CONFIG_DM_DELAY is not set +# CONFIG_DM_DUST is not set +# CONFIG_DM_EBS is not set +# CONFIG_DM_ERA is not set +# CONFIG_DM_FLAKEY is not set +# CONFIG_DM_INTEGRITY is not set +# CONFIG_DM_LOG_USERSPACE is not set +# CONFIG_DM_LOG_WRITES is not set +# CONFIG_DM_MQ_DEFAULT is not set +# CONFIG_DM_MULTIPATH is not set +# CONFIG_DM_RAID is not set +# CONFIG_DM_SWITCH is not set +# CONFIG_DM_THIN_PROVISIONING is not set +# CONFIG_DM_UEVENT is not set +# CONFIG_DM_UNSTRIPED is not set +# CONFIG_DM_VERITY is not set +# CONFIG_DM_WRITECACHE is not set +# CONFIG_DM_ZERO is not set +# CONFIG_DNET is not set +# CONFIG_DNOTIFY is not set +# CONFIG_DNS_RESOLVER is not set +CONFIG_DOUBLEFAULT=y +# CONFIG_DP83640_PHY is not set +# CONFIG_DP83822_PHY is not set +# CONFIG_DP83848_PHY is not set +# CONFIG_DP83867_PHY is not set +# CONFIG_DP83869_PHY is not set +# CONFIG_DP83TC811_PHY is not set +# CONFIG_DP83TD510_PHY is not set +# CONFIG_DPOT_DAC is not set +# CONFIG_DPS310 is not set +CONFIG_DQL=y +# CONFIG_DRAGONRISE_FF is not set +# CONFIG_DRM is not set +# CONFIG_DRM_AMDGPU is not set +# CONFIG_DRM_AMDGPU_CIK is not set +# CONFIG_DRM_AMDGPU_GART_DEBUGFS is not set +# CONFIG_DRM_AMDGPU_SI is not set +# CONFIG_DRM_AMDGPU_USERPTR is not set +# CONFIG_DRM_AMD_ACP is not set +# CONFIG_DRM_AMD_DC_DCN2_0 is not set +# CONFIG_DRM_AMD_DC_DCN3_0 is not set +# CONFIG_DRM_AMD_DC_HDCP is not set +# CONFIG_DRM_AMD_DC_SI is not set +# CONFIG_DRM_AMD_SECURE_DISPLAY is not set +# CONFIG_DRM_ANALOGIX_ANX6345 is not set +# CONFIG_DRM_ANALOGIX_ANX7625 is not set +# CONFIG_DRM_ANALOGIX_ANX78XX is not set +# CONFIG_DRM_ARCPGU is not set +# CONFIG_DRM_ARMADA is not set +# CONFIG_DRM_AST is not set +# CONFIG_DRM_ATMEL_HLCDC is not set +# CONFIG_DRM_BOCHS is not set +# CONFIG_DRM_CDNS_DSI is not set +# CONFIG_DRM_CDNS_MHDP8546 is not set +# CONFIG_DRM_CHIPONE_ICN6211 is not set +# CONFIG_DRM_CHRONTEL_CH7033 is not set +# CONFIG_DRM_CIRRUS_QEMU is not set +# CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS is not set +# CONFIG_DRM_DEBUG_MM is not set +# CONFIG_DRM_DEBUG_MODESET_LOCK is not set +# CONFIG_DRM_DEBUG_SELFTEST is not set +# CONFIG_DRM_DISPLAY_CONNECTOR is not set +# CONFIG_DRM_DP_AUX_CHARDEV is not set +# CONFIG_DRM_DP_CEC is not set +# CONFIG_DRM_DUMB_VGA_DAC is not set +# CONFIG_DRM_DW_HDMI_CEC is not set +# CONFIG_DRM_ETNAVIV is not set +# CONFIG_DRM_EXYNOS is not set +# CONFIG_DRM_FBDEV_EMULATION is not set +# CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM is not set +# CONFIG_DRM_FSL_DCU is not set +# CONFIG_DRM_GM12U320 is not set +# CONFIG_DRM_GMA500 is not set +# CONFIG_DRM_GUD is not set +# CONFIG_DRM_HDLCD is not set +# CONFIG_DRM_HISI_HIBMC is not set +# CONFIG_DRM_HISI_KIRIN is not set +# CONFIG_DRM_I2C_ADV7511 is not set +# CONFIG_DRM_I2C_CH7006 is not set +# CONFIG_DRM_I2C_NXP_TDA9950 is not set +# CONFIG_DRM_I2C_NXP_TDA998X is not set +# CONFIG_DRM_I2C_SIL164 is not set +# CONFIG_DRM_I915 is not set +# CONFIG_DRM_IMX_LCDIF is not set +# CONFIG_DRM_ITE_IT6505 is not set +# CONFIG_DRM_ITE_IT66121 is not set +# CONFIG_DRM_KOMEDA is not set +# CONFIG_DRM_LEGACY is not set +# CONFIG_DRM_LIB_RANDOM is not set +# CONFIG_DRM_LIMA is not set +# CONFIG_DRM_LOAD_EDID_FIRMWARE is not set +# CONFIG_DRM_LOGICVC is not set +# CONFIG_DRM_LONTIUM_LT8912B is not set +# CONFIG_DRM_LONTIUM_LT9211 is not set +# CONFIG_DRM_LONTIUM_LT9611 is not set +# CONFIG_DRM_LONTIUM_LT9611UXC is not set +# CONFIG_DRM_LVDS_CODEC is not set +# CONFIG_DRM_LVDS_ENCODER is not set +# CONFIG_DRM_MALI_DISPLAY is not set +# CONFIG_DRM_MCDE is not set +# CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW is not set +# CONFIG_DRM_MGAG200 is not set +# CONFIG_DRM_MXSFB is not set +# CONFIG_DRM_NOUVEAU is not set +# CONFIG_DRM_NWL_MIPI_DSI is not set +# CONFIG_DRM_NXP_PTN3460 is not set +# CONFIG_DRM_OMAP is not set +# CONFIG_DRM_PANEL_ABT_Y030XX067A is not set +# CONFIG_DRM_PANEL_ARM_VERSATILE is not set +# CONFIG_DRM_PANEL_ASUS_Z00T_TM5P5_NT35596 is not set +# CONFIG_DRM_PANEL_BOE_BF060Y8M_AJ0 is not set +# CONFIG_DRM_PANEL_BOE_HIMAX8279D is not set +# CONFIG_DRM_PANEL_BOE_TV101WUM_NL6 is not set +# CONFIG_DRM_PANEL_DSI_CM is not set +# CONFIG_DRM_PANEL_EBBG_FT8719 is not set +# CONFIG_DRM_PANEL_EDP is not set +# CONFIG_DRM_PANEL_ELIDA_KD35T133 is not set +# CONFIG_DRM_PANEL_FEIXIN_K101_IM2BA02 is not set +# CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D is not set +# CONFIG_DRM_PANEL_ILITEK_IL9322 is not set +# CONFIG_DRM_PANEL_ILITEK_ILI9341 is not set +# CONFIG_DRM_PANEL_ILITEK_ILI9806E is not set +# CONFIG_DRM_PANEL_ILITEK_ILI9881C is not set +# CONFIG_DRM_PANEL_INNOLUX_EJ030NA is not set +# CONFIG_DRM_PANEL_INNOLUX_P079ZCA is not set +# CONFIG_DRM_PANEL_JDI_LT070ME05000 is not set +# CONFIG_DRM_PANEL_JDI_R63452 is not set +# CONFIG_DRM_PANEL_KHADAS_TS050 is not set +# CONFIG_DRM_PANEL_KINGDISPLAY_KD097D04 is not set +# CONFIG_DRM_PANEL_LEADTEK_LTK050H3146W is not set +# CONFIG_DRM_PANEL_LEADTEK_LTK500HD1829 is not set +# CONFIG_DRM_PANEL_LG_LB035Q02 is not set +# CONFIG_DRM_PANEL_LG_LG4573 is not set +# CONFIG_DRM_PANEL_LVDS is not set +# CONFIG_DRM_PANEL_MANTIX_MLAF057WE51 is not set +# CONFIG_DRM_PANEL_MIPI_DBI is not set +# CONFIG_DRM_PANEL_NEC_NL8048HL11 is not set +# CONFIG_DRM_PANEL_NEWVISION_NV3052C is not set +# CONFIG_DRM_PANEL_NOVATEK_NT35510 is not set +# CONFIG_DRM_PANEL_NOVATEK_NT35560 is not set +# CONFIG_DRM_PANEL_NOVATEK_NT35950 is not set +# CONFIG_DRM_PANEL_NOVATEK_NT36672A is not set +# CONFIG_DRM_PANEL_NOVATEK_NT39016 is not set +# CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO is not set +# CONFIG_DRM_PANEL_ORISETECH_OTM8009A is not set +# CONFIG_DRM_PANEL_OSD_OSD101T2587_53TS is not set +# CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00 is not set +# CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN is not set +# CONFIG_DRM_PANEL_RAYDIUM_RM67191 is not set +# CONFIG_DRM_PANEL_RAYDIUM_RM68200 is not set +# CONFIG_DRM_PANEL_ROCKTECH_JH057N00900 is not set +# CONFIG_DRM_PANEL_RONBO_RB070D30 is not set +# CONFIG_DRM_PANEL_SAMSUNG_ATNA33XC20 is not set +# CONFIG_DRM_PANEL_SAMSUNG_DB7430 is not set +# CONFIG_DRM_PANEL_SAMSUNG_LD9040 is not set +# CONFIG_DRM_PANEL_SAMSUNG_S6D16D0 is not set +# CONFIG_DRM_PANEL_SAMSUNG_S6D27A1 is not set +# CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2 is not set +# CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03 is not set +# CONFIG_DRM_PANEL_SAMSUNG_S6E63M0 is not set +# CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01 is not set +# CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0 is not set +# CONFIG_DRM_PANEL_SAMSUNG_SOFEF00 is not set +# CONFIG_DRM_PANEL_SEIKO_43WVF1G is not set +# CONFIG_DRM_PANEL_SHARP_LQ101R1SX01 is not set +# CONFIG_DRM_PANEL_SHARP_LS037V7DW01 is not set +# CONFIG_DRM_PANEL_SHARP_LS043T1LE01 is not set +# CONFIG_DRM_PANEL_SHARP_LS060T1SX01 is not set +# CONFIG_DRM_PANEL_SIMPLE is not set +# CONFIG_DRM_PANEL_SITRONIX_ST7701 is not set +# CONFIG_DRM_PANEL_SITRONIX_ST7703 is not set +# CONFIG_DRM_PANEL_SITRONIX_ST7789V is not set +# CONFIG_DRM_PANEL_SONY_ACX424AKP is not set +# CONFIG_DRM_PANEL_SONY_ACX565AKM is not set +# CONFIG_DRM_PANEL_SONY_TULIP_TRULY_NT35521 is not set +# CONFIG_DRM_PANEL_TDO_TL070WSH30 is not set +# CONFIG_DRM_PANEL_TPO_TD028TTEC1 is not set +# CONFIG_DRM_PANEL_TPO_TD043MTEA1 is not set +# CONFIG_DRM_PANEL_TPO_TPG110 is not set +# CONFIG_DRM_PANEL_TPO_Y17P is not set +# CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA is not set +# CONFIG_DRM_PANEL_VISIONOX_RM69299 is not set +# CONFIG_DRM_PANEL_WAVESHARE_TOUCHSCREEN is not set +# CONFIG_DRM_PANEL_WIDECHIPS_WS2401 is not set +# CONFIG_DRM_PANEL_XINPENG_XPP055C272 is not set +# CONFIG_DRM_PANFROST is not set +# CONFIG_DRM_PARADE_PS8622 is not set +# CONFIG_DRM_PARADE_PS8640 is not set +# CONFIG_DRM_PL111 is not set +# CONFIG_DRM_QXL is not set +# CONFIG_DRM_RADEON is not set +# CONFIG_DRM_RADEON_USERPTR is not set +# CONFIG_DRM_RCAR_DW_HDMI is not set +# CONFIG_DRM_RCAR_LVDS is not set +# CONFIG_DRM_RCAR_USE_LVDS is not set +# CONFIG_DRM_RCAR_USE_MIPI_DSI is not set +# CONFIG_DRM_ROCKCHIP is not set +# CONFIG_DRM_RP1_DPI is not set +# CONFIG_DRM_RP1_DSI is not set +# CONFIG_DRM_RP1_VEC is not set +# CONFIG_DRM_SII902X is not set +# CONFIG_DRM_SII9234 is not set +# CONFIG_DRM_SIL_SII8620 is not set +# CONFIG_DRM_SIMPLEDRM is not set +# CONFIG_DRM_SIMPLE_BRIDGE is not set +# CONFIG_DRM_SSD130X is not set +# CONFIG_DRM_STI is not set +# CONFIG_DRM_STM is not set +# CONFIG_DRM_SUN4I is not set +# CONFIG_DRM_THINE_THC63LVD1024 is not set +# CONFIG_DRM_TIDSS is not set +# CONFIG_DRM_TILCDC is not set +# CONFIG_DRM_TINYDRM is not set +# CONFIG_DRM_TI_DLPC3433 is not set +# CONFIG_DRM_TI_SN65DSI83 is not set +# CONFIG_DRM_TI_SN65DSI86 is not set +# CONFIG_DRM_TI_TFP410 is not set +# CONFIG_DRM_TI_TPD12S015 is not set +# CONFIG_DRM_TOSHIBA_TC358762 is not set +# CONFIG_DRM_TOSHIBA_TC358764 is not set +# CONFIG_DRM_TOSHIBA_TC358767 is not set +# CONFIG_DRM_TOSHIBA_TC358768 is not set +# CONFIG_DRM_TOSHIBA_TC358775 is not set +# CONFIG_DRM_TVE200 is not set +# CONFIG_DRM_UDL is not set +# CONFIG_DRM_V3D is not set +# CONFIG_DRM_VBOXVIDEO is not set +# CONFIG_DRM_VC4_HDMI_CEC is not set +# CONFIG_DRM_VGEM is not set +# CONFIG_DRM_VIRTIO_GPU is not set +# CONFIG_DRM_VKMS is not set +# CONFIG_DRM_VMWGFX is not set +# CONFIG_DRM_XEN is not set +# CONFIG_DRM_XEN_FRONTEND is not set +# CONFIG_DS1682 is not set +# CONFIG_DS1803 is not set +# CONFIG_DS4424 is not set +# CONFIG_DST_CACHE is not set +# CONFIG_DTLK is not set +# CONFIG_DUMMY is not set +CONFIG_DUMMY_CONSOLE_COLUMNS=80 +CONFIG_DUMMY_CONSOLE_ROWS=25 +# CONFIG_DUMMY_IRQ is not set +# CONFIG_DVB_A8293 is not set +# CONFIG_DVB_AF9013 is not set +# CONFIG_DVB_AF9033 is not set +# CONFIG_DVB_AS102 is not set +# CONFIG_DVB_ASCOT2E is not set +# CONFIG_DVB_ATBM8830 is not set +# CONFIG_DVB_AU8522_DTV is not set +# CONFIG_DVB_AU8522_V4L is not set +# CONFIG_DVB_B2C2_FLEXCOP_PCI is not set +# CONFIG_DVB_B2C2_FLEXCOP_USB is not set +# CONFIG_DVB_BCM3510 is not set +# CONFIG_DVB_CORE is not set +# CONFIG_DVB_CX22700 is not set +# CONFIG_DVB_CX22702 is not set +# CONFIG_DVB_CX24110 is not set +# CONFIG_DVB_CX24116 is not set +# CONFIG_DVB_CX24117 is not set +# CONFIG_DVB_CX24120 is not set +# CONFIG_DVB_CX24123 is not set +# CONFIG_DVB_CXD2099 is not set +# CONFIG_DVB_CXD2820R is not set +# CONFIG_DVB_CXD2841ER is not set +# CONFIG_DVB_CXD2880 is not set +# CONFIG_DVB_DDBRIDGE is not set +# CONFIG_DVB_DEMUX_SECTION_LOSS_LOG is not set +# CONFIG_DVB_DIB3000MB is not set +# CONFIG_DVB_DIB3000MC is not set +# CONFIG_DVB_DIB7000M is not set +# CONFIG_DVB_DIB7000P is not set +# CONFIG_DVB_DIB8000 is not set +# CONFIG_DVB_DIB9000 is not set +# CONFIG_DVB_DRX39XYJ is not set +# CONFIG_DVB_DRXD is not set +# CONFIG_DVB_DRXK is not set +# CONFIG_DVB_DS3000 is not set +# CONFIG_DVB_DUMMY_FE is not set +# CONFIG_DVB_DYNAMIC_MINORS is not set +# CONFIG_DVB_EC100 is not set +# CONFIG_DVB_FIREDTV is not set +# CONFIG_DVB_HELENE is not set +# CONFIG_DVB_HORUS3A is not set +# CONFIG_DVB_ISL6405 is not set +# CONFIG_DVB_ISL6421 is not set +# CONFIG_DVB_ISL6423 is not set +# CONFIG_DVB_IX2505V is not set +# CONFIG_DVB_L64781 is not set +# CONFIG_DVB_LG2160 is not set +# CONFIG_DVB_LGDT3305 is not set +# CONFIG_DVB_LGDT3306A is not set +# CONFIG_DVB_LGDT330X is not set +# CONFIG_DVB_LGS8GL5 is not set +# CONFIG_DVB_LGS8GXX is not set +# CONFIG_DVB_LNBH25 is not set +# CONFIG_DVB_LNBH29 is not set +# CONFIG_DVB_LNBP21 is not set +# CONFIG_DVB_LNBP22 is not set +# CONFIG_DVB_M88DS3103 is not set +# CONFIG_DVB_M88RS2000 is not set +CONFIG_DVB_MAX_ADAPTERS=16 +# CONFIG_DVB_MB86A16 is not set +# CONFIG_DVB_MB86A20S is not set +# CONFIG_DVB_MMAP is not set +# CONFIG_DVB_MN88443X is not set +# CONFIG_DVB_MN88472 is not set +# CONFIG_DVB_MN88473 is not set +# CONFIG_DVB_MT312 is not set +# CONFIG_DVB_MT352 is not set +# CONFIG_DVB_MXL5XX is not set +# CONFIG_DVB_MXL692 is not set +# CONFIG_DVB_NET is not set +# CONFIG_DVB_NETUP_UNIDVB is not set +# CONFIG_DVB_NGENE is not set +# CONFIG_DVB_NXT200X is not set +# CONFIG_DVB_NXT6000 is not set +# CONFIG_DVB_OR51132 is not set +# CONFIG_DVB_OR51211 is not set +# CONFIG_DVB_PLATFORM_DRIVERS is not set +# CONFIG_DVB_PLL is not set +# CONFIG_DVB_PLUTO2 is not set +# CONFIG_DVB_PT1 is not set +# CONFIG_DVB_PT3 is not set +# CONFIG_DVB_RTL2830 is not set +# CONFIG_DVB_RTL2832 is not set +# CONFIG_DVB_RTL2832_SDR is not set +# CONFIG_DVB_S5H1409 is not set +# CONFIG_DVB_S5H1411 is not set +# CONFIG_DVB_S5H1420 is not set +# CONFIG_DVB_S5H1432 is not set +# CONFIG_DVB_S921 is not set +# CONFIG_DVB_SI2165 is not set +# CONFIG_DVB_SI2168 is not set +# CONFIG_DVB_SI21XX is not set +# CONFIG_DVB_SP2 is not set +# CONFIG_DVB_SP8870 is not set +# CONFIG_DVB_SP887X is not set +# CONFIG_DVB_STB0899 is not set +# CONFIG_DVB_STB6000 is not set +# CONFIG_DVB_STB6100 is not set +# CONFIG_DVB_STV0288 is not set +# CONFIG_DVB_STV0297 is not set +# CONFIG_DVB_STV0299 is not set +# CONFIG_DVB_STV0367 is not set +# CONFIG_DVB_STV0900 is not set +# CONFIG_DVB_STV090x is not set +# CONFIG_DVB_STV0910 is not set +# CONFIG_DVB_STV6110 is not set +# CONFIG_DVB_STV6110x is not set +# CONFIG_DVB_STV6111 is not set +# CONFIG_DVB_TC90522 is not set +# CONFIG_DVB_TDA10021 is not set +# CONFIG_DVB_TDA10023 is not set +# CONFIG_DVB_TDA10048 is not set +# CONFIG_DVB_TDA1004X is not set +# CONFIG_DVB_TDA10071 is not set +# CONFIG_DVB_TDA10086 is not set +# CONFIG_DVB_TDA18271C2DD is not set +# CONFIG_DVB_TDA665x is not set +# CONFIG_DVB_TDA8083 is not set +# CONFIG_DVB_TDA8261 is not set +# CONFIG_DVB_TDA826X is not set +# CONFIG_DVB_TEST_DRIVERS is not set +# CONFIG_DVB_TS2020 is not set +# CONFIG_DVB_TTUSB_BUDGET is not set +# CONFIG_DVB_TTUSB_DEC is not set +# CONFIG_DVB_TUA6100 is not set +# CONFIG_DVB_TUNER_CX24113 is not set +# CONFIG_DVB_TUNER_DIB0070 is not set +# CONFIG_DVB_TUNER_DIB0090 is not set +# CONFIG_DVB_TUNER_ITD1000 is not set +# CONFIG_DVB_ULE_DEBUG is not set +# CONFIG_DVB_USB is not set +# CONFIG_DVB_USB_V2 is not set +# CONFIG_DVB_VES1820 is not set +# CONFIG_DVB_VES1X93 is not set +# CONFIG_DVB_ZD1301_DEMOD is not set +# CONFIG_DVB_ZL10036 is not set +# CONFIG_DVB_ZL10039 is not set +# CONFIG_DVB_ZL10353 is not set +# CONFIG_DWC_XLGMAC is not set +# CONFIG_DWMAC_DWC_QOS_ETH is not set +# CONFIG_DWMAC_INTEL_PLAT is not set +# CONFIG_DWMAC_IPQ806X is not set +# CONFIG_DWMAC_LOONGSON is not set +# CONFIG_DWMAC_LPC18XX is not set +# CONFIG_DWMAC_MESON is not set +# CONFIG_DWMAC_ROCKCHIP is not set +# CONFIG_DWMAC_SOCFPGA is not set +# CONFIG_DWMAC_STI is not set +# CONFIG_DW_AXI_DMAC is not set +# CONFIG_DW_DMAC is not set +# CONFIG_DW_DMAC_PCI is not set +# CONFIG_DW_EDMA is not set +# CONFIG_DW_EDMA_PCIE is not set +# CONFIG_DW_WATCHDOG is not set +# CONFIG_DW_XDATA_PCIE is not set +# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_DYNAMIC_DEBUG_CORE is not set +# CONFIG_E100 is not set +# CONFIG_E1000 is not set +# CONFIG_E1000E is not set +# CONFIG_E1000E_HWTS is not set +# CONFIG_EARLY_PRINTK_8250 is not set +# CONFIG_EARLY_PRINTK_USB_XDBC is not set +# CONFIG_EBC_C384_WDT is not set +# CONFIG_ECHO is not set +# CONFIG_ECRYPT_FS is not set +# CONFIG_EDAC is not set +# CONFIG_EEEPC_LAPTOP is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_EEPROM_93XX46 is not set +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_DIGSY_MTC_CFG is not set +# CONFIG_EEPROM_EE1004 is not set +# CONFIG_EEPROM_IDT_89HPESX is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EFI is not set +CONFIG_EFI_PARTITION=y +# CONFIG_EFI_VARS_PSTORE is not set +# CONFIG_EFS_FS is not set +CONFIG_ELFCORE=y +# CONFIG_ELF_CORE is not set +# CONFIG_EMAC_ROCKCHIP is not set +CONFIG_EMBEDDED=y +# CONFIG_EM_TIMER_STI is not set +CONFIG_ENABLE_WARN_DEPRECATED=y +# CONFIG_ENA_ETHERNET is not set +# CONFIG_ENC28J60 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_ENCRYPTED_KEYS is not set +# CONFIG_ENCX24J600 is not set +# CONFIG_ENERGY_MODEL is not set +# CONFIG_ENIC is not set +# CONFIG_ENVELOPE_DETECTOR is not set +# CONFIG_EPAPR_PARAVIRT is not set +# CONFIG_EPIC100 is not set +CONFIG_EPOLL=y +# CONFIG_EQUALIZER is not set +# CONFIG_EROFS_FS is not set +# CONFIG_ET131X is not set +CONFIG_ETHERNET=y +# CONFIG_ETHOC is not set +CONFIG_ETHTOOL_NETLINK=y +CONFIG_EVENTFD=y +# CONFIG_EVM is not set +# CONFIG_EXFAT_FS is not set +CONFIG_EXPERT=y +CONFIG_EXPORTFS=y +# CONFIG_EXPORTFS_BLOCK_OPS is not set +# CONFIG_EXT2_FS is not set +CONFIG_EXT2_FS_XATTR=y +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_DEBUG is not set +# CONFIG_EXT4_ENCRYPTION is not set +# CONFIG_EXT4_FS is not set +# CONFIG_EXT4_FS_POSIX_ACL is not set +# CONFIG_EXT4_FS_SECURITY is not set +CONFIG_EXT4_USE_FOR_EXT2=y +# CONFIG_EXTCON is not set +# CONFIG_EXTCON_ADC_JACK is not set +# CONFIG_EXTCON_ARIZONA is not set +# CONFIG_EXTCON_AXP288 is not set +# CONFIG_EXTCON_FSA9480 is not set +# CONFIG_EXTCON_GPIO is not set +# CONFIG_EXTCON_INTEL_INT3496 is not set +# CONFIG_EXTCON_MAX3355 is not set +# CONFIG_EXTCON_PTN5150 is not set +# CONFIG_EXTCON_QCOM_SPMI_MISC is not set +# CONFIG_EXTCON_RT8973A is not set +# CONFIG_EXTCON_SM5502 is not set +# CONFIG_EXTCON_USBC_TUSB320 is not set +# CONFIG_EXTCON_USB_GPIO is not set +CONFIG_EXTRA_FIRMWARE="" +CONFIG_EXTRA_TARGETS="" +# CONFIG_EXYNOS_ADC is not set +# CONFIG_EXYNOS_VIDEO is not set +# CONFIG_EZCHIP_NPS_MANAGEMENT_ENET is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_F2FS_CHECK_FS is not set +# CONFIG_F2FS_FAULT_INJECTION is not set +# CONFIG_F2FS_FS is not set +# CONFIG_F2FS_FS_COMPRESSION is not set +# CONFIG_F2FS_FS_ENCRYPTION is not set +# CONFIG_F2FS_FS_POSIX_ACL is not set +# CONFIG_F2FS_FS_SECURITY is not set +CONFIG_F2FS_FS_XATTR=y +# CONFIG_F2FS_IOSTAT is not set +# CONFIG_F2FS_IO_TRACE is not set +CONFIG_F2FS_STAT_FS=y +# CONFIG_F2FS_UNFAIR_RWSEM is not set +# CONFIG_FAILOVER is not set +# CONFIG_FAIR_GROUP_SCHED is not set +# CONFIG_FANOTIFY is not set +# CONFIG_FANOTIFY_ACCESS_PERMISSIONS is not set +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_FAT_DEFAULT_UTF8 is not set +# CONFIG_FAT_FS is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_FB is not set +# CONFIG_FB_3DFX is not set +# CONFIG_FB_ARC is not set +# CONFIG_FB_ARK is not set +# CONFIG_FB_ARMCLCD is not set +# CONFIG_FB_ASILIANT is not set +# CONFIG_FB_ATMEL is not set +# CONFIG_FB_ATY is not set +# CONFIG_FB_ATY128 is not set +# CONFIG_FB_AUO_K190X is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_BIG_ENDIAN is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +# CONFIG_FB_BOTH_ENDIAN is not set +# CONFIG_FB_BROADSHEET is not set +# CONFIG_FB_CARMINE is not set +# CONFIG_FB_CFB_COPYAREA is not set +# CONFIG_FB_CFB_FILLRECT is not set +# CONFIG_FB_CFB_IMAGEBLIT is not set +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_CIRRUS is not set +# CONFIG_FB_CYBER2000 is not set +# CONFIG_FB_DA8XX is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_FLEX is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_FSL_DIU is not set +# CONFIG_FB_GEODE is not set +# CONFIG_FB_GOLDFISH is not set +# CONFIG_FB_HGA is not set +# CONFIG_FB_I740 is not set +# CONFIG_FB_IBM_GXT4500 is not set +# CONFIG_FB_IMSTT is not set +# CONFIG_FB_IMX is not set +# CONFIG_FB_KYRO is not set +# CONFIG_FB_LE80578 is not set +# CONFIG_FB_LITTLE_ENDIAN is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_MATROX is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_MXS is not set +# CONFIG_FB_N411 is not set +# CONFIG_FB_NEOMAGIC is not set +CONFIG_FB_NOTIFY=y +# CONFIG_FB_NVIDIA is not set +# CONFIG_FB_OF is not set +# CONFIG_FB_OMAP2 is not set +# CONFIG_FB_OPENCORES is not set +# CONFIG_FB_PM2 is not set +# CONFIG_FB_PM3 is not set +# CONFIG_FB_PS3 is not set +# CONFIG_FB_PXA is not set +# CONFIG_FB_RADEON is not set +# CONFIG_FB_RIVA is not set +# CONFIG_FB_RPISENSE is not set +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_S3 is not set +# CONFIG_FB_SAVAGE is not set +# CONFIG_FB_SIMPLE is not set +# CONFIG_FB_SIS is not set +# CONFIG_FB_SM712 is not set +# CONFIG_FB_SM750 is not set +# CONFIG_FB_SMSCUFX is not set +# CONFIG_FB_SSD1307 is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_TFT is not set +# CONFIG_FB_TFT_AGM1264K_FL is not set +# CONFIG_FB_TFT_BD663474 is not set +# CONFIG_FB_TFT_FBTFT_DEVICE is not set +# CONFIG_FB_TFT_HX8340BN is not set +# CONFIG_FB_TFT_HX8347D is not set +# CONFIG_FB_TFT_HX8353D is not set +# CONFIG_FB_TFT_HX8357D is not set +# CONFIG_FB_TFT_ILI9163 is not set +# CONFIG_FB_TFT_ILI9320 is not set +# CONFIG_FB_TFT_ILI9325 is not set +# CONFIG_FB_TFT_ILI9340 is not set +# CONFIG_FB_TFT_ILI9341 is not set +# CONFIG_FB_TFT_ILI9481 is not set +# CONFIG_FB_TFT_ILI9486 is not set +# CONFIG_FB_TFT_PCD8544 is not set +# CONFIG_FB_TFT_RA8875 is not set +# CONFIG_FB_TFT_S6D02A1 is not set +# CONFIG_FB_TFT_S6D1121 is not set +# CONFIG_FB_TFT_SEPS525 is not set +# CONFIG_FB_TFT_SH1106 is not set +# CONFIG_FB_TFT_SSD1289 is not set +# CONFIG_FB_TFT_SSD1305 is not set +# CONFIG_FB_TFT_SSD1306 is not set +# CONFIG_FB_TFT_SSD1325 is not set +# CONFIG_FB_TFT_SSD1331 is not set +# CONFIG_FB_TFT_SSD1351 is not set +# CONFIG_FB_TFT_ST7735R is not set +# CONFIG_FB_TFT_ST7789V is not set +# CONFIG_FB_TFT_TINYLCD is not set +# CONFIG_FB_TFT_TLS8204 is not set +# CONFIG_FB_TFT_UC1611 is not set +# CONFIG_FB_TFT_UC1701 is not set +# CONFIG_FB_TFT_UPD161704 is not set +# CONFIG_FB_TFT_WATTEROTT is not set +# CONFIG_FB_TILEBLITTING is not set +# CONFIG_FB_TMIO is not set +# CONFIG_FB_TRIDENT is not set +# CONFIG_FB_UDL is not set +# CONFIG_FB_UVESA is not set +# CONFIG_FB_VGA16 is not set +# CONFIG_FB_VIA is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_VOODOO1 is not set +# CONFIG_FB_VT8623 is not set +# CONFIG_FB_XGI is not set +# CONFIG_FCOE is not set +# CONFIG_FCOE_FNIC is not set +# CONFIG_FDDI is not set +# CONFIG_FEALNX is not set +# CONFIG_FENCE_TRACE is not set +# CONFIG_FHANDLE is not set +CONFIG_FIB_RULES=y +# CONFIG_FIELDBUS_DEV is not set +CONFIG_FILE_LOCKING=y +# CONFIG_FIND_BIT_BENCHMARK is not set +# CONFIG_FIREWIRE is not set +# CONFIG_FIREWIRE_NOSY is not set +# CONFIG_FIREWIRE_SERIAL is not set +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FIRMWARE_IN_KERNEL is not set +# CONFIG_FIRMWARE_MEMMAP is not set +# CONFIG_FIXED_PHY is not set +CONFIG_FLATMEM=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_FM10K is not set +# CONFIG_FMC is not set +# CONFIG_FONTS is not set +# CONFIG_FONT_6x8 is not set +# CONFIG_FONT_TER16x32 is not set +# CONFIG_FORCEDETH is not set +# CONFIG_FORCE_NR_CPUS is not set +CONFIG_FORTIFY_SOURCE=y +# CONFIG_FPGA is not set +# CONFIG_FPROBE is not set +# CONFIG_FRAMEBUFFER_CONSOLE is not set +# CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER is not set +# CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION is not set +# CONFIG_FRAME_POINTER is not set +# CONFIG_FREEZER is not set +# CONFIG_FRONTSWAP is not set +# CONFIG_FSCACHE is not set +# CONFIG_FSI is not set +# CONFIG_FSL_EDMA is not set +# CONFIG_FSL_ENETC is not set +# CONFIG_FSL_ENETC_IERB is not set +# CONFIG_FSL_ENETC_MDIO is not set +# CONFIG_FSL_ENETC_VF is not set +# CONFIG_FSL_ERRATUM_A008585 is not set +# CONFIG_FSL_MC_BUS is not set +# CONFIG_FSL_PQ_MDIO is not set +# CONFIG_FSL_QDMA is not set +# CONFIG_FSL_RCPM is not set +# CONFIG_FSL_XGMAC_MDIO is not set +CONFIG_FSNOTIFY=y +# CONFIG_FS_DAX is not set +# CONFIG_FS_ENCRYPTION is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_FS_VERITY is not set +# CONFIG_FTGMAC100 is not set +# CONFIG_FTL is not set +# CONFIG_FTMAC100 is not set +# CONFIG_FTRACE is not set +# CONFIG_FTRACE_RECORD_RECURSION is not set +# CONFIG_FTRACE_SORT_STARTUP_TEST is not set +# CONFIG_FTRACE_STARTUP_TEST is not set +# CONFIG_FTR_FIXUP_SELFTEST is not set +# CONFIG_FTWDT010_WATCHDOG is not set +# CONFIG_FUJITSU_ERRATUM_010001 is not set +# CONFIG_FUJITSU_ES is not set +# CONFIG_FUJITSU_LAPTOP is not set +# CONFIG_FUJITSU_TABLET is not set +# CONFIG_FUNCTION_ERROR_INJECTION is not set +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_FUN_ETH is not set +# CONFIG_FUSE_FS is not set +# CONFIG_FUSION is not set +# CONFIG_FUSION_FC is not set +# CONFIG_FUSION_SAS is not set +# CONFIG_FUSION_SPI is not set +CONFIG_FUTEX=y +CONFIG_FUTEX_PI=y +# CONFIG_FW_CFG_SYSFS is not set +CONFIG_FW_LOADER=y +# CONFIG_FW_LOADER_COMPRESS is not set +CONFIG_FW_LOADER_USER_HELPER=y +CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y +# CONFIG_FW_UPLOAD is not set +# CONFIG_FXAS21002C is not set +# CONFIG_FXLS8962AF_I2C is not set +# CONFIG_FXLS8962AF_SPI is not set +# CONFIG_FXOS8700_I2C is not set +# CONFIG_FXOS8700_SPI is not set +CONFIG_GACT_PROB=y +# CONFIG_GADGET_UAC1 is not set +# CONFIG_GAMEPORT is not set +# CONFIG_GATEWORKS_GW16083 is not set +# CONFIG_GCC_PLUGINS is not set +# CONFIG_GCOV is not set +# CONFIG_GCOV_KERNEL is not set +# CONFIG_GDB_SCRIPTS is not set +# CONFIG_GEMINI_ETHERNET is not set +# CONFIG_GENERIC_ADC_BATTERY is not set +# CONFIG_GENERIC_ADC_THERMAL is not set +CONFIG_GENERIC_CALIBRATE_DELAY=y +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_GENERIC_HWEIGHT=y +# CONFIG_GENERIC_IRQ_DEBUGFS is not set +CONFIG_GENERIC_IRQ_IPI=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_GENERIC_NET_UTILS=y +# CONFIG_GENERIC_PHY is not set +CONFIG_GENERIC_PTDUMP=y +CONFIG_GENERIC_VDSO_TIME_NS=y +# CONFIG_GENEVE is not set +# CONFIG_GENWQE is not set +# CONFIG_GFS2_FS is not set +# CONFIG_GIGASET_CAPI is not set +# CONFIG_GIGASET_DEBUG is not set +# CONFIG_GIGASET_DUMMYLL is not set +# CONFIG_GLOB_SELFTEST is not set +# CONFIG_GNSS is not set +# CONFIG_GOLDFISH is not set +# CONFIG_GOOGLE_FIRMWARE is not set +# CONFIG_GOOGLE_FRAMEBUFFER_COREBOOT is not set +# CONFIG_GOOGLE_MEMCONSOLE_X86_LEGACY is not set +# CONFIG_GOOGLE_SMI is not set +# CONFIG_GP2AP002 is not set +# CONFIG_GP2AP020A00F is not set +# CONFIG_GPD_POCKET_FAN is not set +CONFIG_GPIOLIB=y +CONFIG_GPIOLIB_FASTPATH_LIMIT=512 +# CONFIG_GPIO_104_DIO_48E is not set +# CONFIG_GPIO_104_IDIO_16 is not set +# CONFIG_GPIO_104_IDI_48 is not set +# CONFIG_GPIO_74X164 is not set +# CONFIG_GPIO_74XX_MMIO is not set +# CONFIG_GPIO_ADNP is not set +# CONFIG_GPIO_ADP5588 is not set +# CONFIG_GPIO_AGGREGATOR is not set +# CONFIG_GPIO_ALTERA is not set +# CONFIG_GPIO_AMD8111 is not set +# CONFIG_GPIO_AMDPT is not set +# CONFIG_GPIO_AMD_FCH is not set +# CONFIG_GPIO_BCM_KONA is not set +# CONFIG_GPIO_BRCMSTB is not set +# CONFIG_GPIO_BT8XX is not set +# CONFIG_GPIO_CADENCE is not set +# CONFIG_GPIO_CASCADE is not set +# CONFIG_GPIO_CDEV is not set +# CONFIG_GPIO_CDEV_V1 is not set +# CONFIG_GPIO_CS5535 is not set +# CONFIG_GPIO_DWAPB is not set +# CONFIG_GPIO_EM is not set +# CONFIG_GPIO_EXAR is not set +# CONFIG_GPIO_F7188X is not set +# CONFIG_GPIO_FTGPIO010 is not set +# CONFIG_GPIO_GENERIC_PLATFORM is not set +# CONFIG_GPIO_GPIO_MM is not set +# CONFIG_GPIO_GRGPIO is not set +# CONFIG_GPIO_GW_PLD is not set +# CONFIG_GPIO_HLWD is not set +# CONFIG_GPIO_ICH is not set +# CONFIG_GPIO_IT87 is not set +# CONFIG_GPIO_LOGICVC is not set +# CONFIG_GPIO_LYNXPOINT is not set +# CONFIG_GPIO_MAX3191X is not set +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_MB86S7X is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_ML_IOH is not set +# CONFIG_GPIO_MOCKUP is not set +# CONFIG_GPIO_MPC8XXX is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCA953X_IRQ is not set +# CONFIG_GPIO_PCA9570 is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_PCH is not set +# CONFIG_GPIO_PCIE_IDIO_24 is not set +# CONFIG_GPIO_PCI_IDIO_16 is not set +# CONFIG_GPIO_PISOSR is not set +# CONFIG_GPIO_PL061 is not set +# CONFIG_GPIO_PWM is not set +# CONFIG_GPIO_RCAR is not set +# CONFIG_GPIO_RDC321X is not set +# CONFIG_GPIO_SAMA5D2_PIOBU is not set +# CONFIG_GPIO_SCH is not set +# CONFIG_GPIO_SCH311X is not set +# CONFIG_GPIO_SIFIVE is not set +# CONFIG_GPIO_SIM is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_SYSCON is not set +CONFIG_GPIO_SYSFS=y +# CONFIG_GPIO_TPIC2810 is not set +# CONFIG_GPIO_TS4900 is not set +# CONFIG_GPIO_TS5500 is not set +# CONFIG_GPIO_VIRTIO is not set +# CONFIG_GPIO_VX855 is not set +# CONFIG_GPIO_WATCHDOG is not set +# CONFIG_GPIO_WINBOND is not set +# CONFIG_GPIO_WS16C48 is not set +# CONFIG_GPIO_XGENE is not set +# CONFIG_GPIO_XILINX is not set +# CONFIG_GPIO_XRA1403 is not set +# CONFIG_GPIO_ZEVIO is not set +# CONFIG_GPIO_ZX is not set +# CONFIG_GP_PCI1XXXX is not set +# CONFIG_GREENASIA_FF is not set +# CONFIG_GREYBUS is not set +# CONFIG_GS_FPGABOOT is not set +# CONFIG_GTP is not set +# CONFIG_GUP_BENCHMARK is not set +# CONFIG_GUP_TEST is not set +# CONFIG_GVE is not set +# CONFIG_HABANA_AI is not set +# CONFIG_HAMACHI is not set +# CONFIG_HAMRADIO is not set +# CONFIG_HAPPYMEAL is not set +CONFIG_HARDENED_USERCOPY=y +# CONFIG_HARDENED_USERCOPY_FALLBACK is not set +# CONFIG_HARDENED_USERCOPY_PAGESPAN is not set +CONFIG_HARDEN_BRANCH_HISTORY=y +CONFIG_HARDEN_EL2_VECTORS=y +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_HAVE_ARM_ARCH_TIMER is not set +# CONFIG_HCALL_STATS is not set +# CONFIG_HDC100X is not set +# CONFIG_HDC2010 is not set +# CONFIG_HDLC is not set +# CONFIG_HDLC_CISCO is not set +# CONFIG_HDLC_FR is not set +# CONFIG_HDLC_PPP is not set +# CONFIG_HDLC_RAW is not set +# CONFIG_HDLC_RAW_ETH is not set +# CONFIG_HDMI_LPE_AUDIO is not set +# CONFIG_HDQ_MASTER_OMAP is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_HEADERS_INSTALL is not set +# CONFIG_HEADER_TEST is not set +# CONFIG_HERMES is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_HFSPLUS_FS_POSIX_ACL is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFS_FS_POSIX_ACL is not set +# CONFIG_HI6421V600_IRQ is not set +# CONFIG_HI8435 is not set +# CONFIG_HIBERNATION is not set +# CONFIG_HID is not set +# CONFIG_HIDRAW is not set +# CONFIG_HID_A4TECH is not set +# CONFIG_HID_ACCUTOUCH is not set +# CONFIG_HID_ACRUX is not set +# CONFIG_HID_ACRUX_FF is not set +# CONFIG_HID_ALPS is not set +# CONFIG_HID_APPLE is not set +# CONFIG_HID_APPLEIR is not set +# CONFIG_HID_ASUS is not set +# CONFIG_HID_AUREAL is not set +# CONFIG_HID_BATTERY_STRENGTH is not set +# CONFIG_HID_BELKIN is not set +# CONFIG_HID_BETOP_FF is not set +# CONFIG_HID_BIGBEN_FF is not set +# CONFIG_HID_CHERRY is not set +# CONFIG_HID_CHICONY is not set +# CONFIG_HID_CMEDIA is not set +# CONFIG_HID_CORSAIR is not set +# CONFIG_HID_COUGAR is not set +# CONFIG_HID_CP2112 is not set +# CONFIG_HID_CREATIVE_SB0540 is not set +# CONFIG_HID_CYPRESS is not set +# CONFIG_HID_DRAGONRISE is not set +# CONFIG_HID_ELAN is not set +# CONFIG_HID_ELECOM is not set +# CONFIG_HID_ELO is not set +# CONFIG_HID_EMS_FF is not set +# CONFIG_HID_EZKEY is not set +# CONFIG_HID_FT260 is not set +# CONFIG_HID_GEMBIRD is not set +# CONFIG_HID_GENERIC is not set +# CONFIG_HID_GFRM is not set +# CONFIG_HID_GLORIOUS is not set +# CONFIG_HID_GOOGLE_HAMMER is not set +# CONFIG_HID_GREENASIA is not set +# CONFIG_HID_GT683R is not set +# CONFIG_HID_GYRATION is not set +# CONFIG_HID_HOLTEK is not set +# CONFIG_HID_ICADE is not set +# CONFIG_HID_ITE is not set +# CONFIG_HID_JABRA is not set +# CONFIG_HID_KENSINGTON is not set +# CONFIG_HID_KEYTOUCH is not set +# CONFIG_HID_KYE is not set +# CONFIG_HID_LCPOWER is not set +# CONFIG_HID_LED is not set +# CONFIG_HID_LENOVO is not set +# CONFIG_HID_LETSKETCH is not set +# CONFIG_HID_LOGITECH is not set +# CONFIG_HID_LOGITECH_DJ is not set +# CONFIG_HID_LOGITECH_HIDPP is not set +# CONFIG_HID_MACALLY is not set +# CONFIG_HID_MAGICMOUSE is not set +# CONFIG_HID_MALTRON is not set +# CONFIG_HID_MAYFLASH is not set +# CONFIG_HID_MCP2221 is not set +# CONFIG_HID_MEGAWORLD_FF is not set +# CONFIG_HID_MICROSOFT is not set +# CONFIG_HID_MONTEREY is not set +# CONFIG_HID_MULTITOUCH is not set +# CONFIG_HID_NINTENDO is not set +# CONFIG_HID_NTI is not set +# CONFIG_HID_NTRIG is not set +# CONFIG_HID_ORTEK is not set +# CONFIG_HID_PANTHERLORD is not set +# CONFIG_HID_PENMOUNT is not set +# CONFIG_HID_PETALYNX is not set +# CONFIG_HID_PICOLCD is not set +# CONFIG_HID_PID is not set +# CONFIG_HID_PLANTRONICS is not set +# CONFIG_HID_PLAYSTATION is not set +# CONFIG_HID_PRIMAX is not set +# CONFIG_HID_PRODIKEYS is not set +# CONFIG_HID_PXRC is not set +# CONFIG_HID_RAZER is not set +# CONFIG_HID_REDRAGON is not set +# CONFIG_HID_RETRODE is not set +# CONFIG_HID_RMI is not set +# CONFIG_HID_ROCCAT is not set +# CONFIG_HID_SAITEK is not set +# CONFIG_HID_SAMSUNG is not set +# CONFIG_HID_SEMITEK is not set +# CONFIG_HID_SENSOR_HUB is not set +# CONFIG_HID_SIGMAMICRO is not set +# CONFIG_HID_SMARTJOYPLUS is not set +# CONFIG_HID_SONY is not set +# CONFIG_HID_SPEEDLINK is not set +# CONFIG_HID_STEAM is not set +# CONFIG_HID_STEELSERIES is not set +# CONFIG_HID_SUNPLUS is not set +# CONFIG_HID_THINGM is not set +# CONFIG_HID_THRUSTMASTER is not set +# CONFIG_HID_TIVO is not set +# CONFIG_HID_TOPRE is not set +# CONFIG_HID_TOPSEED is not set +# CONFIG_HID_TWINHAN is not set +# CONFIG_HID_U2FZERO is not set +# CONFIG_HID_UCLOGIC is not set +# CONFIG_HID_UDRAW_PS3 is not set +# CONFIG_HID_VIEWSONIC is not set +# CONFIG_HID_VIVALDI is not set +# CONFIG_HID_VRC2 is not set +# CONFIG_HID_WACOM is not set +# CONFIG_HID_WALTOP is not set +# CONFIG_HID_WIIMOTE is not set +# CONFIG_HID_XIAOMI is not set +# CONFIG_HID_XINMO is not set +# CONFIG_HID_ZEROPLUS is not set +# CONFIG_HID_ZYDACRON is not set +# CONFIG_HIGHMEM is not set +CONFIG_HIGH_RES_TIMERS=y +# CONFIG_HINIC is not set +# CONFIG_HIP04_ETH is not set +# CONFIG_HIPPI is not set +# CONFIG_HISILICON_ERRATUM_161010101 is not set +# CONFIG_HISILICON_ERRATUM_161600802 is not set +# CONFIG_HISI_DMA is not set +# CONFIG_HISI_FEMAC is not set +# CONFIG_HISI_HIKEY_USB is not set +# CONFIG_HISI_PCIE_PMU is not set +# CONFIG_HISI_PTT is not set +# CONFIG_HIST_TRIGGERS_DEBUG is not set +# CONFIG_HIX5HD2_GMAC is not set +# CONFIG_HMC425 is not set +# CONFIG_HMC6352 is not set +# CONFIG_HNS is not set +# CONFIG_HNS3 is not set +# CONFIG_HNS3_PMU is not set +# CONFIG_HNS_DSAF is not set +# CONFIG_HNS_ENET is not set +# CONFIG_HOSTAP is not set +# CONFIG_HOSTAP_CS is not set +# CONFIG_HOSTAP_PCI is not set +# CONFIG_HOSTAP_PLX is not set +# CONFIG_HOTPLUG_CPU is not set +# CONFIG_HOTPLUG_PCI is not set +# CONFIG_HP03 is not set +# CONFIG_HP100 is not set +# CONFIG_HP206C is not set +CONFIG_HPET_MMAP_DEFAULT=y +# CONFIG_HPFS_FS is not set +# CONFIG_HP_ILO is not set +# CONFIG_HP_WATCHDOG is not set +# CONFIG_HP_WIRELESS is not set +# CONFIG_HSA_AMD is not set +# CONFIG_HSI is not set +# CONFIG_HSR is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTE is not set +# CONFIG_HTS221 is not set +# CONFIG_HTU21 is not set +# CONFIG_HUGETLBFS is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_HVC_DCC is not set +# CONFIG_HVC_UDBG is not set +# CONFIG_HWLAT_TRACER is not set +# CONFIG_HWMON is not set +# CONFIG_HWMON_DEBUG_CHIP is not set +# CONFIG_HWMON_VID is not set +# CONFIG_HWSPINLOCK is not set +# CONFIG_HWSPINLOCK_OMAP is not set +CONFIG_HW_PERF_EVENTS=y +# CONFIG_HW_RANDOM is not set +# CONFIG_HW_RANDOM_AMD is not set +# CONFIG_HW_RANDOM_ARM_SMCCC_TRNG is not set +# CONFIG_HW_RANDOM_ATMEL is not set +# CONFIG_HW_RANDOM_BA431 is not set +# CONFIG_HW_RANDOM_BCM2835 is not set +# CONFIG_HW_RANDOM_CAVIUM is not set +# CONFIG_HW_RANDOM_CCTRNG is not set +# CONFIG_HW_RANDOM_CN10K is not set +# CONFIG_HW_RANDOM_EXYNOS is not set +# CONFIG_HW_RANDOM_GEODE is not set +# CONFIG_HW_RANDOM_INTEL is not set +# CONFIG_HW_RANDOM_IPROC_RNG200 is not set +# CONFIG_HW_RANDOM_MTK is not set +# CONFIG_HW_RANDOM_OMAP is not set +# CONFIG_HW_RANDOM_OMAP3_ROM is not set +# CONFIG_HW_RANDOM_PPC4XX is not set +# CONFIG_HW_RANDOM_TIMERIOMEM is not set +CONFIG_HW_RANDOM_TPM=y +# CONFIG_HW_RANDOM_VIA is not set +# CONFIG_HW_RANDOM_VIRTIO is not set +# CONFIG_HW_RANDOM_XIPHERA is not set +# CONFIG_HX711 is not set +# CONFIG_HYPERV is not set +# CONFIG_HYPERV_TSCPAGE is not set +# CONFIG_HYSDN is not set +CONFIG_HZ=100 +CONFIG_HZ_100=y +# CONFIG_HZ_1000 is not set +# CONFIG_HZ_1024 is not set +# CONFIG_HZ_128 is not set +# CONFIG_HZ_200 is not set +# CONFIG_HZ_24 is not set +# CONFIG_HZ_250 is not set +# CONFIG_HZ_256 is not set +# CONFIG_HZ_300 is not set +# CONFIG_HZ_48 is not set +# CONFIG_HZ_500 is not set +# CONFIG_HZ_PERIODIC is not set +# CONFIG_I2C is not set +# CONFIG_I2C_ALGOBIT is not set +# CONFIG_I2C_ALGOPCA is not set +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALI1535 is not set +# CONFIG_I2C_ALI1563 is not set +# CONFIG_I2C_ALI15X3 is not set +# CONFIG_I2C_AMD756 is not set +# CONFIG_I2C_AMD8111 is not set +# CONFIG_I2C_ARB_GPIO_CHALLENGE is not set +# CONFIG_I2C_AU1550 is not set +# CONFIG_I2C_BCM2835 is not set +# CONFIG_I2C_BCM_IPROC is not set +# CONFIG_I2C_BRCMSTB is not set +# CONFIG_I2C_CADENCE is not set +# CONFIG_I2C_CBUS_GPIO is not set +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_COMPAT is not set +# CONFIG_I2C_CP2615 is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEMUX_PINCTRL is not set +# CONFIG_I2C_DESIGNWARE_PCI is not set +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +# CONFIG_I2C_DESIGNWARE_SLAVE is not set +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_EG20T is not set +# CONFIG_I2C_ELEKTOR is not set +# CONFIG_I2C_EMEV2 is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_GPIO_FAULT_INJECTOR is not set +# CONFIG_I2C_HELPER_AUTO is not set +# CONFIG_I2C_HID is not set +# CONFIG_I2C_HID_OF is not set +# CONFIG_I2C_HID_OF_ELAN is not set +# CONFIG_I2C_HID_OF_GOODIX is not set +# CONFIG_I2C_I801 is not set +# CONFIG_I2C_IBM_IIC is not set +# CONFIG_I2C_IMG is not set +# CONFIG_I2C_ISCH is not set +# CONFIG_I2C_ISMT is not set +# CONFIG_I2C_JZ4780 is not set +# CONFIG_I2C_MLXCPLD is not set +# CONFIG_I2C_MPC is not set +# CONFIG_I2C_MT65XX is not set +# CONFIG_I2C_MUX is not set +# CONFIG_I2C_MUX_GPIO is not set +# CONFIG_I2C_MUX_GPMUX is not set +# CONFIG_I2C_MUX_LTC4306 is not set +# CONFIG_I2C_MUX_MLXCPLD is not set +# CONFIG_I2C_MUX_PCA9541 is not set +# CONFIG_I2C_MUX_PCA954x is not set +# CONFIG_I2C_MUX_PINCTRL is not set +# CONFIG_I2C_MUX_REG is not set +# CONFIG_I2C_MV64XXX is not set +# CONFIG_I2C_NFORCE2 is not set +# CONFIG_I2C_NOMADIK is not set +# CONFIG_I2C_NVIDIA_GPU is not set +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_OCTEON is not set +# CONFIG_I2C_PARPORT is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_PCA_ISA is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PCI1XXXX is not set +# CONFIG_I2C_PIIX4 is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_PXA_SLAVE is not set +# CONFIG_I2C_RCAR is not set +# CONFIG_I2C_RK3X is not set +# CONFIG_I2C_ROBOTFUZZ_OSIF is not set +# CONFIG_I2C_S3C2410 is not set +# CONFIG_I2C_SCMI is not set +# CONFIG_I2C_SH_MOBILE is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_SIS5595 is not set +# CONFIG_I2C_SIS630 is not set +# CONFIG_I2C_SIS96X is not set +# CONFIG_I2C_SLAVE is not set +# CONFIG_I2C_SLAVE_EEPROM is not set +# CONFIG_I2C_SMBUS is not set +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_THUNDERX is not set +# CONFIG_I2C_TINY_USB is not set +# CONFIG_I2C_VERSATILE is not set +# CONFIG_I2C_VIA is not set +# CONFIG_I2C_VIAPRO is not set +# CONFIG_I2C_VIRTIO is not set +# CONFIG_I2C_XILINX is not set +# CONFIG_I3C is not set +# CONFIG_I40E is not set +# CONFIG_I40EVF is not set +# CONFIG_I6300ESB_WDT is not set +# CONFIG_I82092 is not set +# CONFIG_I82365 is not set +# CONFIG_IAQCORE is not set +# CONFIG_IBM_ASM is not set +# CONFIG_IBM_EMAC_DEBUG is not set +# CONFIG_IBM_EMAC_EMAC4 is not set +# CONFIG_IBM_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_EMAC_MAL_COMMON_ERR is not set +# CONFIG_IBM_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_EMAC_RGMII is not set +# CONFIG_IBM_EMAC_TAH is not set +# CONFIG_IBM_EMAC_ZMII is not set +# CONFIG_ICE is not set +# CONFIG_ICP10100 is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_ICS932S401 is not set +# CONFIG_ICST is not set +# CONFIG_IDE is not set +# CONFIG_IDEAPAD_LAPTOP is not set +# CONFIG_IDE_GD is not set +# CONFIG_IDE_PROC_FS is not set +# CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IDLE_PAGE_TRACKING is not set +# CONFIG_IEEE802154 is not set +# CONFIG_IEEE802154_ADF7242 is not set +# CONFIG_IEEE802154_ATUSB is not set +# CONFIG_IEEE802154_CA8210 is not set +# CONFIG_IEEE802154_HWSIM is not set +# CONFIG_IEEE802154_MCR20A is not set +# CONFIG_IFB is not set +# CONFIG_IGB is not set +# CONFIG_IGBVF is not set +# CONFIG_IGC is not set +# CONFIG_IIO is not set +# CONFIG_IIO_BUFFER is not set +# CONFIG_IIO_BUFFER_CB is not set +# CONFIG_IIO_BUFFER_DMA is not set +# CONFIG_IIO_BUFFER_DMAENGINE is not set +# CONFIG_IIO_BUFFER_HDC2010 is not set +# CONFIG_IIO_BUFFER_HW_CONSUMER is not set +# CONFIG_IIO_CONFIGFS is not set +CONFIG_IIO_CONSUMERS_PER_TRIGGER=2 +# CONFIG_IIO_CROS_EC_ACCEL_LEGACY is not set +# CONFIG_IIO_INTERRUPT_TRIGGER is not set +# CONFIG_IIO_MUX is not set +# CONFIG_IIO_PERIODIC_RTC_TRIGGER is not set +# CONFIG_IIO_RESCALE is not set +# CONFIG_IIO_SIMPLE_DUMMY is not set +# CONFIG_IIO_SSP_SENSORHUB is not set +# CONFIG_IIO_ST_ACCEL_3AXIS is not set +# CONFIG_IIO_ST_GYRO_3AXIS is not set +# CONFIG_IIO_ST_LSM6DSX is not set +# CONFIG_IIO_ST_LSM9DS0 is not set +# CONFIG_IIO_ST_MAGN_3AXIS is not set +# CONFIG_IIO_ST_PRESS is not set +# CONFIG_IIO_SW_DEVICE is not set +# CONFIG_IIO_SW_TRIGGER is not set +# CONFIG_IIO_SYSFS_TRIGGER is not set +# CONFIG_IIO_TRIGGER is not set +# CONFIG_IIO_TRIGGERED_EVENT is not set +# CONFIG_IKCONFIG is not set +# CONFIG_IKCONFIG_PROC is not set +# CONFIG_IKHEADERS is not set +# CONFIG_IMA is not set +# CONFIG_IMAGE_CMDLINE_HACK is not set +# CONFIG_IMGPDC_WDT is not set +# CONFIG_IMG_MDC_DMA is not set +# CONFIG_IMX7D_ADC is not set +# CONFIG_IMX8QXP_ADC is not set +# CONFIG_IMX_IPUV3_CORE is not set +# CONFIG_IMX_THERMAL is not set +# CONFIG_INA2XX_ADC is not set +# CONFIG_INDIRECT_PIO is not set +CONFIG_INET=y +# CONFIG_INET6_AH is not set +# CONFIG_INET6_ESP is not set +# CONFIG_INET6_ESPINTCP is not set +# CONFIG_INET6_IPCOMP is not set +# CONFIG_INET6_TUNNEL is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_DIAG is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_ESPINTCP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_LRO is not set +CONFIG_INET_TABLE_PERTURB_ORDER=16 +# CONFIG_INET_TCP_DIAG is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_UDP_DIAG is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INFINIBAND is not set +# CONFIG_INFTL is not set +# CONFIG_INGENIC_ADC is not set +# CONFIG_INGENIC_CGU_JZ4725B is not set +# CONFIG_INGENIC_CGU_JZ4740 is not set +# CONFIG_INGENIC_CGU_JZ4760 is not set +# CONFIG_INGENIC_CGU_JZ4770 is not set +# CONFIG_INGENIC_CGU_JZ4780 is not set +# CONFIG_INGENIC_CGU_X1000 is not set +# CONFIG_INGENIC_CGU_X1830 is not set +# CONFIG_INGENIC_OST is not set +# CONFIG_INGENIC_SYSOST is not set +# CONFIG_INGENIC_TCU_CLK is not set +# CONFIG_INGENIC_TCU_IRQ is not set +# CONFIG_INGENIC_TIMER is not set +# CONFIG_INITRAMFS_PRESERVE_MTIME is not set +CONFIG_INIT_ENV_ARG_LIMIT=32 +# CONFIG_INIT_ON_ALLOC_DEFAULT_ON is not set +# CONFIG_INIT_ON_FREE_DEFAULT_ON is not set +# CONFIG_INIT_STACK_ALL_PATTERN is not set +# CONFIG_INIT_STACK_ALL_ZERO is not set +CONFIG_INIT_STACK_NONE=y +CONFIG_INOTIFY_USER=y +# CONFIG_INPUT is not set +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_APANEL is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +# CONFIG_INPUT_ATLAS_BTNS is not set +# CONFIG_INPUT_ATMEL_CAPTOUCH is not set +# CONFIG_INPUT_AXP20X_PEK is not set +# CONFIG_INPUT_BMA150 is not set +# CONFIG_INPUT_CM109 is not set +# CONFIG_INPUT_CMA3000 is not set +# CONFIG_INPUT_DA7280_HAPTICS is not set +# CONFIG_INPUT_DRV260X_HAPTICS is not set +# CONFIG_INPUT_DRV2665_HAPTICS is not set +# CONFIG_INPUT_DRV2667_HAPTICS is not set +# CONFIG_INPUT_E3X0_BUTTON is not set +# CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_GP2A is not set +# CONFIG_INPUT_GPIO_BEEPER is not set +# CONFIG_INPUT_GPIO_DECODER is not set +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +# CONFIG_INPUT_GPIO_TILT_POLLED is not set +# CONFIG_INPUT_GPIO_VIBRA is not set +# CONFIG_INPUT_IBM_PANEL is not set +# CONFIG_INPUT_IDEAPAD_SLIDEBAR is not set +# CONFIG_INPUT_IMS_PCU is not set +# CONFIG_INPUT_IQS269A is not set +# CONFIG_INPUT_IQS626A is not set +# CONFIG_INPUT_IQS7222 is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_KXTJ9 is not set +# CONFIG_INPUT_LEDS is not set +# CONFIG_INPUT_MATRIXKMAP is not set +# CONFIG_INPUT_MAX8997_HAPTIC is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_MMA8450 is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_MPU3050 is not set +# CONFIG_INPUT_MSM_VIBRATOR is not set +# CONFIG_INPUT_PALMAS_PWRBUTTON is not set +# CONFIG_INPUT_PCF8574 is not set +# CONFIG_INPUT_PCSPKR is not set +# CONFIG_INPUT_PM8941_PWRKEY is not set +# CONFIG_INPUT_PM8XXX_VIBRATOR is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_PWM_BEEPER is not set +# CONFIG_INPUT_PWM_VIBRA is not set +# CONFIG_INPUT_RASPBERRYPI_BUTTON is not set +# CONFIG_INPUT_REGULATOR_HAPTIC is not set +# CONFIG_INPUT_SOC_BUTTON_ARRAY is not set +# CONFIG_INPUT_SPARSEKMAP is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_TPS65218_PWRBUTTON is not set +# CONFIG_INPUT_TWL4030_PWRBUTTON is not set +# CONFIG_INPUT_TWL4030_VIBRA is not set +# CONFIG_INPUT_TWL6040_VIBRA is not set +# CONFIG_INPUT_UINPUT is not set +# CONFIG_INPUT_WISTRON_BTNS is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INT340X_THERMAL is not set +# CONFIG_INTEGRITY is not set +# CONFIG_INTEGRITY_AUDIT is not set +# CONFIG_INTEGRITY_SIGNATURE is not set +# CONFIG_INTEL_ATOMISP2_LED is not set +# CONFIG_INTEL_ATOMISP2_PM is not set +# CONFIG_INTEL_CHT_INT33FE is not set +# CONFIG_INTEL_HID_EVENT is not set +# CONFIG_INTEL_IDLE is not set +# CONFIG_INTEL_IDMA64 is not set +# CONFIG_INTEL_INT0002_VGPIO is not set +# CONFIG_INTEL_IOATDMA is not set +# CONFIG_INTEL_ISH_HID is not set +# CONFIG_INTEL_MEI is not set +# CONFIG_INTEL_MEI_ME is not set +# CONFIG_INTEL_MEI_TXE is not set +# CONFIG_INTEL_MIC_CARD is not set +# CONFIG_INTEL_MIC_HOST is not set +# CONFIG_INTEL_MID_PTI is not set +# CONFIG_INTEL_OAKTRAIL is not set +# CONFIG_INTEL_PMC_CORE is not set +# CONFIG_INTEL_PUNIT_IPC is not set +# CONFIG_INTEL_RST is not set +# CONFIG_INTEL_SMARTCONNECT is not set +# CONFIG_INTEL_SOC_PMIC is not set +# CONFIG_INTEL_SOC_PMIC_CHTDC_TI is not set +# CONFIG_INTEL_SOC_PMIC_CHTWC is not set +# CONFIG_INTEL_TH is not set +# CONFIG_INTEL_VBTN is not set +# CONFIG_INTEL_XWAY_PHY is not set +# CONFIG_INTERCONNECT is not set +# CONFIG_INTERVAL_TREE_TEST is not set +# CONFIG_INV_ICM42600_I2C is not set +# CONFIG_INV_ICM42600_SPI is not set +# CONFIG_INV_MPU6050_I2C is not set +# CONFIG_INV_MPU6050_IIO is not set +# CONFIG_INV_MPU6050_SPI is not set +# CONFIG_IOMMU_SUPPORT is not set +# CONFIG_IONIC is not set +# CONFIG_IOSCHED_BFQ is not set +# CONFIG_IOSM is not set +CONFIG_IO_STRICT_DEVMEM=y +# CONFIG_IO_URING is not set +CONFIG_IO_WQ=y +# CONFIG_IP17XX_PHY is not set +# CONFIG_IP5XXX_POWER is not set +# CONFIG_IP6_NF_FILTER is not set +# CONFIG_IP6_NF_IPTABLES is not set +# CONFIG_IP6_NF_MANGLE is not set +# CONFIG_IP6_NF_MATCH_AH is not set +# CONFIG_IP6_NF_MATCH_EUI64 is not set +# CONFIG_IP6_NF_MATCH_FRAG is not set +# CONFIG_IP6_NF_MATCH_HL is not set +# CONFIG_IP6_NF_MATCH_IPV6HEADER is not set +# CONFIG_IP6_NF_MATCH_MH is not set +# CONFIG_IP6_NF_MATCH_OPTS is not set +# CONFIG_IP6_NF_MATCH_RPFILTER is not set +# CONFIG_IP6_NF_MATCH_RT is not set +# CONFIG_IP6_NF_MATCH_SRH is not set +# CONFIG_IP6_NF_NAT is not set +# CONFIG_IP6_NF_RAW is not set +# CONFIG_IP6_NF_SECURITY is not set +# CONFIG_IP6_NF_TARGET_HL is not set +# CONFIG_IP6_NF_TARGET_MASQUERADE is not set +# CONFIG_IP6_NF_TARGET_REJECT is not set +# CONFIG_IP6_NF_TARGET_SYNPROXY is not set +# CONFIG_IPACK_BUS is not set +# CONFIG_IPC_NS is not set +# CONFIG_IPMB_DEVICE_INTERFACE is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_IPV6 is not set +# CONFIG_IPV6_FOU is not set +# CONFIG_IPV6_FOU_TUNNEL is not set +# CONFIG_IPV6_ILA is not set +# CONFIG_IPV6_IOAM6_LWTUNNEL is not set +# CONFIG_IPV6_MIP6 is not set +# CONFIG_IPV6_MROUTE is not set +# CONFIG_IPV6_MROUTE_MULTIPLE_TABLES is not set +# CONFIG_IPV6_MULTIPLE_TABLES is not set +CONFIG_IPV6_NDISC_NODETYPE=y +# CONFIG_IPV6_OPTIMISTIC_DAD is not set +# CONFIG_IPV6_ROUTER_PREF is not set +# CONFIG_IPV6_ROUTE_INFO is not set +# CONFIG_IPV6_RPL_LWTUNNEL is not set +# CONFIG_IPV6_SEG6_HMAC is not set +# CONFIG_IPV6_SEG6_LWTUNNEL is not set +# CONFIG_IPV6_SIT is not set +# CONFIG_IPV6_SIT_6RD is not set +# CONFIG_IPV6_TUNNEL is not set +# CONFIG_IPV6_VTI is not set +# CONFIG_IPVLAN is not set +# CONFIG_IPVTAP is not set +# CONFIG_IPW2100 is not set +# CONFIG_IPW2100_DEBUG is not set +CONFIG_IPW2100_MONITOR=y +# CONFIG_IPW2200 is not set +# CONFIG_IPW2200_DEBUG is not set +CONFIG_IPW2200_MONITOR=y +# CONFIG_IPW2200_PROMISCUOUS is not set +# CONFIG_IPW2200_QOS is not set +# CONFIG_IPW2200_RADIOTAP is not set +# CONFIG_IPWIRELESS is not set +# CONFIG_IPX is not set +CONFIG_IP_ADVANCED_ROUTER=y +# CONFIG_IP_DCCP is not set +# CONFIG_IP_FIB_TRIE_STATS is not set +# CONFIG_IP_MROUTE is not set +CONFIG_IP_MROUTE_MULTIPLE_TABLES=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_MULTIPLE_TABLES=y +# CONFIG_IP_NF_ARPFILTER is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_IP_NF_ARP_MANGLE is not set +# CONFIG_IP_NF_FILTER is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_MANGLE is not set +# CONFIG_IP_NF_MATCH_AH is not set +# CONFIG_IP_NF_MATCH_ECN is not set +# CONFIG_IP_NF_MATCH_RPFILTER is not set +# CONFIG_IP_NF_MATCH_TTL is not set +# CONFIG_IP_NF_RAW is not set +# CONFIG_IP_NF_SECURITY is not set +# CONFIG_IP_NF_TARGET_CLUSTERIP is not set +# CONFIG_IP_NF_TARGET_ECN is not set +# CONFIG_IP_NF_TARGET_MASQUERADE is not set +# CONFIG_IP_NF_TARGET_NETMAP is not set +# CONFIG_IP_NF_TARGET_REDIRECT is not set +# CONFIG_IP_NF_TARGET_REJECT is not set +# CONFIG_IP_NF_TARGET_SYNPROXY is not set +# CONFIG_IP_NF_TARGET_TTL is not set +# CONFIG_IP_PIMSM_V1 is not set +# CONFIG_IP_PIMSM_V2 is not set +# CONFIG_IP_PNP is not set +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +# CONFIG_IP_SCTP is not set +# CONFIG_IP_SET is not set +# CONFIG_IP_SET_HASH_IPMAC is not set +# CONFIG_IP_VS is not set +# CONFIG_IP_VS_MH is not set +CONFIG_IP_VS_MH_TAB_INDEX=10 +# CONFIG_IP_VS_TWOS is not set +# CONFIG_IRDA is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_IRQ_ALL_CPUS is not set +# CONFIG_IRQ_DOMAIN_DEBUG is not set +# CONFIG_IRQ_POLL is not set +# CONFIG_IRQ_TIME_ACCOUNTING is not set +# CONFIG_IR_GPIO_CIR is not set +# CONFIG_IR_HIX5HD2 is not set +# CONFIG_IR_IGORPLUGUSB is not set +# CONFIG_IR_IGUANA is not set +# CONFIG_IR_IMG is not set +# CONFIG_IR_IMON is not set +# CONFIG_IR_IMON_RAW is not set +# CONFIG_IR_JVC_DECODER is not set +# CONFIG_IR_LIRC_CODEC is not set +# CONFIG_IR_MCEUSB is not set +# CONFIG_IR_NEC_DECODER is not set +# CONFIG_IR_RC5_DECODER is not set +# CONFIG_IR_RC6_DECODER is not set +# CONFIG_IR_REDRAT3 is not set +# CONFIG_IR_SERIAL is not set +# CONFIG_IR_SIR is not set +# CONFIG_IR_SONY_DECODER is not set +# CONFIG_IR_STREAMZAP is not set +# CONFIG_IR_TOY is not set +# CONFIG_IR_TTUSBIR is not set +# CONFIG_ISA_BUS is not set +# CONFIG_ISA_BUS_API is not set +# CONFIG_ISCSI_BOOT_SYSFS is not set +# CONFIG_ISCSI_TCP is not set +CONFIG_ISDN=y +# CONFIG_ISDN_AUDIO is not set +# CONFIG_ISDN_CAPI is not set +# CONFIG_ISDN_CAPI_CAPIDRV is not set +# CONFIG_ISDN_DIVERSION is not set +# CONFIG_ISDN_DRV_ACT2000 is not set +# CONFIG_ISDN_DRV_GIGASET is not set +# CONFIG_ISDN_DRV_HISAX is not set +# CONFIG_ISDN_DRV_ICN is not set +# CONFIG_ISDN_DRV_LOOP is not set +# CONFIG_ISDN_DRV_PCBIT is not set +# CONFIG_ISDN_DRV_SC is not set +# CONFIG_ISDN_I4L is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_ISL29125 is not set +# CONFIG_ISL29501 is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_ISS4xx is not set +# CONFIG_ITG3200 is not set +# CONFIG_IWL3945 is not set +# CONFIG_IWLWIFI is not set +# CONFIG_IXGB is not set +# CONFIG_IXGBE is not set +# CONFIG_IXGBEVF is not set +# CONFIG_JAILHOUSE_GUEST is not set +# CONFIG_JBD2_DEBUG is not set +# CONFIG_JFFS2_CMODE_FAVOURLZO is not set +# CONFIG_JFFS2_CMODE_NONE is not set +CONFIG_JFFS2_CMODE_PRIORITY=y +# CONFIG_JFFS2_CMODE_SIZE is not set +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_POSIX_ACL is not set +# CONFIG_JFFS2_FS_SECURITY is not set +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +CONFIG_JFFS2_FS_WRITEBUFFER=y +CONFIG_JFFS2_FS_XATTR=y +CONFIG_JFFS2_LZMA=y +# CONFIG_JFFS2_LZO is not set +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +CONFIG_JFFS2_SUMMARY=y +# CONFIG_JFFS2_ZLIB is not set +# CONFIG_JFS_DEBUG is not set +# CONFIG_JFS_FS is not set +# CONFIG_JFS_POSIX_ACL is not set +# CONFIG_JFS_SECURITY is not set +# CONFIG_JFS_STATISTICS is not set +# CONFIG_JME is not set +CONFIG_JOLIET=y +# CONFIG_JSA1212 is not set +# CONFIG_JUMP_LABEL is not set +# CONFIG_JZ4740_WDT is not set +# CONFIG_JZ4770_PHY is not set +# CONFIG_KALLSYMS is not set +# CONFIG_KALLSYMS_ABSOLUTE_PERCPU is not set +# CONFIG_KALLSYMS_ALL is not set +CONFIG_KALLSYMS_BASE_RELATIVE=y +# CONFIG_KALLSYMS_UNCOMPRESSED is not set +# CONFIG_KARMA_PARTITION is not set +# CONFIG_KASAN is not set +# CONFIG_KASAN_MODULE_TEST is not set +CONFIG_KASAN_STACK=y +# CONFIG_KCMP is not set +# CONFIG_KCOV is not set +CONFIG_KCOV_IRQ_AREA_SIZE=0x40000 +# CONFIG_KCSAN is not set +# CONFIG_KERNEL_BZIP2 is not set +# CONFIG_KERNEL_CAT is not set +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_LZ4 is not set +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_LZO is not set +CONFIG_KERNEL_MODE_NEON=y +CONFIG_KERNEL_XZ=y +# CONFIG_KERNEL_ZSTD is not set +CONFIG_KERNFS=y +# CONFIG_KEXEC is not set +# CONFIG_KEXEC_FILE is not set +# CONFIG_KEXEC_SIG is not set +# CONFIG_KEYBOARD_ADC is not set +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +# CONFIG_KEYBOARD_APPLESPI is not set +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_BCM is not set +# CONFIG_KEYBOARD_CAP11XX is not set +# CONFIG_KEYBOARD_CYPRESS_SF is not set +# CONFIG_KEYBOARD_DLINK_DIR685 is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_LM8333 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_MT6779 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OMAP4 is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_PINEPHONE is not set +# CONFIG_KEYBOARD_PXA27x is not set +# CONFIG_KEYBOARD_QT1050 is not set +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_QT2160 is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_SH_KEYSC is not set +# CONFIG_KEYBOARD_SNVS_PWRKEY is not set +# CONFIG_KEYBOARD_STMPE is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_TEGRA is not set +# CONFIG_KEYBOARD_TM2_TOUCHKEY is not set +# CONFIG_KEYBOARD_TWL4030 is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYS is not set +# CONFIG_KEYS_REQUEST_CACHE is not set +# CONFIG_KEY_DH_OPERATIONS is not set +# CONFIG_KFENCE is not set +# CONFIG_KGDB is not set +# CONFIG_KMEMCHECK is not set +# CONFIG_KMX61 is not set +# CONFIG_KPC2000 is not set +# CONFIG_KPROBES is not set +# CONFIG_KPROBES_SANITY_TEST is not set +# CONFIG_KPROBE_EVENTS_ON_NOTRACE is not set +# CONFIG_KPROBE_EVENT_GEN_TEST is not set +# CONFIG_KS7010 is not set +# CONFIG_KS8842 is not set +# CONFIG_KS8851 is not set +# CONFIG_KS8851_MLL is not set +# CONFIG_KSM is not set +# CONFIG_KSZ884X_PCI is not set +# CONFIG_KUNIT is not set +CONFIG_KUSER_HELPERS=y +# CONFIG_KVM_AMD is not set +# CONFIG_KVM_AMD_SEV is not set +# CONFIG_KVM_GUEST is not set +# CONFIG_KVM_INTEL is not set +# CONFIG_KVM_WERROR is not set +# CONFIG_KVM_XEN is not set +# CONFIG_KXCJK1013 is not set +# CONFIG_KXSD9 is not set +# CONFIG_L2TP is not set +# CONFIG_L2TP_ETH is not set +# CONFIG_L2TP_IP is not set +# CONFIG_L2TP_V3 is not set +# CONFIG_LAN743X is not set +# CONFIG_LAN966X_SWITCH is not set +# CONFIG_LANMEDIA is not set +# CONFIG_LANTIQ is not set +# CONFIG_LAPB is not set +# CONFIG_LASAT is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_LATTICE_ECP3_CONFIG is not set +CONFIG_LBDAF=y +# CONFIG_LCD_AMS369FG06 is not set +# CONFIG_LCD_CLASS_DEVICE is not set +# CONFIG_LCD_HX8357 is not set +# CONFIG_LCD_ILI922X is not set +# CONFIG_LCD_ILI9320 is not set +# CONFIG_LCD_L4F00242T03 is not set +# CONFIG_LCD_LD9040 is not set +# CONFIG_LCD_LMS283GF05 is not set +# CONFIG_LCD_LMS501KF03 is not set +# CONFIG_LCD_LTV350QV is not set +# CONFIG_LCD_OTM3225A is not set +# CONFIG_LCD_S6E63M0 is not set +# CONFIG_LCD_TDO24M is not set +# CONFIG_LCD_VGG2432A4 is not set +CONFIG_LDISC_AUTOLOAD=y +# CONFIG_LDM_PARTITION is not set +CONFIG_LD_DEAD_CODE_DATA_ELIMINATION=y +# CONFIG_LD_HEAD_STUB_CATCH is not set +# CONFIG_LEDS_AN30259A is not set +# CONFIG_LEDS_APU is not set +# CONFIG_LEDS_AW2013 is not set +# CONFIG_LEDS_BCM6328 is not set +# CONFIG_LEDS_BCM6358 is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_BLINKM is not set +CONFIG_LEDS_BRIGHTNESS_HW_CHANGED=y +CONFIG_LEDS_CLASS=y +# CONFIG_LEDS_CLASS_FLASH is not set +CONFIG_LEDS_CLASS_MULTICOLOR=y +# CONFIG_LEDS_CR0014114 is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_EL15203000 is not set +# CONFIG_LEDS_GPIO is not set +# CONFIG_LEDS_INTEL_SS4200 is not set +# CONFIG_LEDS_IS31FL319X is not set +# CONFIG_LEDS_IS31FL32XX is not set +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_LM3532 is not set +# CONFIG_LEDS_LM355x is not set +# CONFIG_LEDS_LM3642 is not set +# CONFIG_LEDS_LM3692X is not set +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP3952 is not set +# CONFIG_LEDS_LP50XX is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +# CONFIG_LEDS_LP5562 is not set +# CONFIG_LEDS_LP55XX_COMMON is not set +# CONFIG_LEDS_LP8501 is not set +# CONFIG_LEDS_LP8860 is not set +# CONFIG_LEDS_LT3593 is not set +# CONFIG_LEDS_MLXCPLD is not set +# CONFIG_LEDS_MLXREG is not set +# CONFIG_LEDS_NIC78BX is not set +# CONFIG_LEDS_NS2 is not set +# CONFIG_LEDS_OT200 is not set +# CONFIG_LEDS_PCA9532 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_PCA963X is not set +# CONFIG_LEDS_PWM is not set +# CONFIG_LEDS_PWM_MULTICOLOR is not set +# CONFIG_LEDS_REGULATOR is not set +# CONFIG_LEDS_SPI_BYTE is not set +# CONFIG_LEDS_SYSCON is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_TI_LMU_COMMON is not set +# CONFIG_LEDS_TLC591XX is not set +CONFIG_LEDS_TRIGGERS=y +# CONFIG_LEDS_TRIGGER_ACTIVITY is not set +# CONFIG_LEDS_TRIGGER_AUDIO is not set +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_CAMERA is not set +# CONFIG_LEDS_TRIGGER_CPU is not set +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y +# CONFIG_LEDS_TRIGGER_DISK is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_LEDS_TRIGGER_MTD is not set +CONFIG_LEDS_TRIGGER_NETDEV=y +# CONFIG_LEDS_TRIGGER_ONESHOT is not set +# CONFIG_LEDS_TRIGGER_PANIC is not set +# CONFIG_LEDS_TRIGGER_PATTERN is not set +CONFIG_LEDS_TRIGGER_TIMER=y +# CONFIG_LEDS_TRIGGER_TRANSIENT is not set +# CONFIG_LEDS_TRIGGER_TTY is not set +# CONFIG_LEDS_TURRIS_OMNIA is not set +# CONFIG_LEDS_USER is not set +# CONFIG_LED_TRIGGER_PHY is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_LGUEST is not set +# CONFIG_LIB80211 is not set +# CONFIG_LIB80211_CRYPT_CCMP is not set +# CONFIG_LIB80211_CRYPT_TKIP is not set +# CONFIG_LIB80211_CRYPT_WEP is not set +# CONFIG_LIB80211_DEBUG is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_LIBERTAS is not set +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_LIBERTAS_USB is not set +# CONFIG_LIBFC is not set +# CONFIG_LIBFCOE is not set +# CONFIG_LIBIPW_DEBUG is not set +# CONFIG_LIBNVDIMM is not set +CONFIG_LIB_MEMNEQ=y +# CONFIG_LIDAR_LITE_V2 is not set +CONFIG_LINEAR_RANGES=y +# CONFIG_LIQUIDIO is not set +# CONFIG_LIQUIDIO_VF is not set +# CONFIG_LIRC is not set +# CONFIG_LIS3L02DQ is not set +# CONFIG_LITEX_LITEETH is not set +# CONFIG_LITEX_SOC_CONTROLLER is not set +# CONFIG_LIVEPATCH is not set +# CONFIG_LKDTM is not set +CONFIG_LLC=y +# CONFIG_LLC2 is not set +# CONFIG_LMK04832 is not set +# CONFIG_LMP91000 is not set +# CONFIG_LNET is not set +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +# CONFIG_LOCKD is not set +CONFIG_LOCKDEP_BITS=15 +CONFIG_LOCKDEP_CHAINS_BITS=16 +CONFIG_LOCKDEP_CIRCULAR_QUEUE_BITS=12 +CONFIG_LOCKDEP_STACK_TRACE_BITS=19 +CONFIG_LOCKDEP_STACK_TRACE_HASH_BITS=14 +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_LOCKD_V4=y +CONFIG_LOCK_MM_AND_FIND_VMA=y +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_LOCK_EVENT_COUNTS is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_LOCK_TORTURE_TEST is not set +# CONFIG_LOGFS is not set +# CONFIG_LOGIG940_FF is not set +# CONFIG_LOGIRUMBLEPAD2_FF is not set +# CONFIG_LOGITECH_FF is not set +# CONFIG_LOGIWHEELS_FF is not set +# CONFIG_LOGO is not set +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 +# CONFIG_LOONGSON_MC146818 is not set +# CONFIG_LPC_ICH is not set +# CONFIG_LPC_SCH is not set +# CONFIG_LP_CONSOLE is not set +CONFIG_LRU_GEN=y +CONFIG_LRU_GEN_ENABLED=y +# CONFIG_LRU_GEN_STATS is not set +# CONFIG_LSI_ET1011C_PHY is not set +CONFIG_LSM="lockdown,yama,loadpin,safesetid,integrity" +CONFIG_LSM_MMAP_MIN_ADDR=65536 +# CONFIG_LTC1660 is not set +# CONFIG_LTC2471 is not set +# CONFIG_LTC2485 is not set +# CONFIG_LTC2496 is not set +# CONFIG_LTC2497 is not set +# CONFIG_LTC2632 is not set +# CONFIG_LTC2688 is not set +# CONFIG_LTC2983 is not set +# CONFIG_LTE_GDM724X is not set +CONFIG_LTO_NONE=y +# CONFIG_LTPC is not set +# CONFIG_LTR501 is not set +# CONFIG_LTRF216A is not set +# CONFIG_LUSTRE_FS is not set +# CONFIG_LV0104CS is not set +# CONFIG_LWTUNNEL is not set +# CONFIG_LXT_PHY is not set +# CONFIG_LZ4HC_COMPRESS is not set +# CONFIG_LZ4_COMPRESS is not set +# CONFIG_LZ4_DECOMPRESS is not set +CONFIG_LZMA_COMPRESS=y +CONFIG_LZMA_DECOMPRESS=y +# CONFIG_LZO_COMPRESS is not set +# CONFIG_LZO_DECOMPRESS is not set +# CONFIG_M62332 is not set +# CONFIG_MAC80211 is not set +# CONFIG_MAC80211_MESSAGE_TRACING is not set +CONFIG_MAC80211_STA_HASH_MAX_SIZE=0 +# CONFIG_MACB is not set +# CONFIG_MACH_ASM9260 is not set +# CONFIG_MACH_DECSTATION is not set +# CONFIG_MACH_INGENIC is not set +# CONFIG_MACH_INGENIC_SOC is not set +# CONFIG_MACH_JAZZ is not set +# CONFIG_MACH_JZ4740 is not set +# CONFIG_MACH_LOONGSON2EF is not set +# CONFIG_MACH_LOONGSON32 is not set +# CONFIG_MACH_LOONGSON64 is not set +# CONFIG_MACH_NINTENDO64 is not set +# CONFIG_MACH_PIC32 is not set +# CONFIG_MACH_PISTACHIO is not set +# CONFIG_MACH_REALTEK_RTL is not set +# CONFIG_MACH_TX39XX is not set +# CONFIG_MACH_TX49XX is not set +# CONFIG_MACH_VR41XX is not set +# CONFIG_MACH_XILFPGA is not set +# CONFIG_MACINTOSH_DRIVERS is not set +# CONFIG_MACSEC is not set +# CONFIG_MACVLAN is not set +# CONFIG_MACVTAP is not set +# CONFIG_MAC_EMUMOUSEBTN is not set +# CONFIG_MAC_PARTITION is not set +# CONFIG_MAG3110 is not set +# CONFIG_MAGIC_SYSRQ is not set +CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1 +# CONFIG_MAGIC_SYSRQ_SERIAL is not set +CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE="" +# CONFIG_MAILBOX is not set +# CONFIG_MANAGER_SBS is not set +# CONFIG_MANDATORY_FILE_LOCKING is not set +# CONFIG_MANGLE_BOOTARGS is not set +# CONFIG_MARVELL_10G_PHY is not set +# CONFIG_MARVELL_88X2222_PHY is not set +# CONFIG_MARVELL_PHY is not set +# CONFIG_MAX1027 is not set +# CONFIG_MAX11100 is not set +# CONFIG_MAX1118 is not set +# CONFIG_MAX11205 is not set +# CONFIG_MAX1241 is not set +# CONFIG_MAX1363 is not set +# CONFIG_MAX30100 is not set +# CONFIG_MAX30102 is not set +# CONFIG_MAX31856 is not set +# CONFIG_MAX31865 is not set +# CONFIG_MAX44000 is not set +# CONFIG_MAX44009 is not set +# CONFIG_MAX517 is not set +# CONFIG_MAX5432 is not set +# CONFIG_MAX5481 is not set +# CONFIG_MAX5487 is not set +# CONFIG_MAX5821 is not set +# CONFIG_MAX63XX_WATCHDOG is not set +# CONFIG_MAX9611 is not set +# CONFIG_MAXIM_THERMOCOUPLE is not set +# CONFIG_MAXLINEAR_GPHY is not set +CONFIG_MAY_USE_DEVLINK=y +# CONFIG_MB1232 is not set +# CONFIG_MC3230 is not set +# CONFIG_MCB is not set +# CONFIG_MCP320X is not set +# CONFIG_MCP3422 is not set +# CONFIG_MCP3911 is not set +# CONFIG_MCP4018 is not set +# CONFIG_MCP41010 is not set +# CONFIG_MCP4131 is not set +# CONFIG_MCP4531 is not set +# CONFIG_MCP4725 is not set +# CONFIG_MCP4922 is not set +# CONFIG_MCPM is not set +# CONFIG_MCTP is not set +# CONFIG_MD is not set +# CONFIG_MDIO_BCM_UNIMAC is not set +# CONFIG_MDIO_BITBANG is not set +# CONFIG_MDIO_BUS_MUX_GPIO is not set +# CONFIG_MDIO_BUS_MUX_MMIOREG is not set +# CONFIG_MDIO_BUS_MUX_MULTIPLEXER is not set +# CONFIG_MDIO_DEVICE is not set +# CONFIG_MDIO_DEVRES is not set +# CONFIG_MDIO_HISI_FEMAC is not set +# CONFIG_MDIO_IPQ4019 is not set +# CONFIG_MDIO_IPQ8064 is not set +# CONFIG_MDIO_MSCC_MIIM is not set +# CONFIG_MDIO_MVUSB is not set +# CONFIG_MDIO_OCTEON is not set +# CONFIG_MDIO_THUNDER is not set +# CONFIG_MDIO_XPCS is not set +# CONFIG_MDM_GCC_9607 is not set +# CONFIG_MD_FAULTY is not set +# CONFIG_MEDIATEK_GE_PHY is not set +# CONFIG_MEDIATEK_MT6577_AUXADC is not set +# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set +# CONFIG_MEDIA_ATTACH is not set +# CONFIG_MEDIA_CAMERA_SUPPORT is not set +# CONFIG_MEDIA_CEC_SUPPORT is not set +# CONFIG_MEDIA_CONTROLLER is not set +# CONFIG_MEDIA_CONTROLLER_DVB is not set +# CONFIG_MEDIA_DIGITAL_TV_SUPPORT is not set +# CONFIG_MEDIA_PCI_SUPPORT is not set +# CONFIG_MEDIA_PLATFORM_DRIVERS is not set +# CONFIG_MEDIA_PLATFORM_SUPPORT is not set +# CONFIG_MEDIA_RADIO_SUPPORT is not set +# CONFIG_MEDIA_RC_SUPPORT is not set +# CONFIG_MEDIA_SDR_SUPPORT is not set +# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set +# CONFIG_MEDIA_SUPPORT is not set +# CONFIG_MEDIA_SUPPORT_FILTER is not set +# CONFIG_MEDIA_TEST_SUPPORT is not set +# CONFIG_MEDIA_TUNER_E4000 is not set +# CONFIG_MEDIA_TUNER_FC0011 is not set +# CONFIG_MEDIA_TUNER_FC0012 is not set +# CONFIG_MEDIA_TUNER_FC0013 is not set +# CONFIG_MEDIA_TUNER_FC2580 is not set +# CONFIG_MEDIA_TUNER_IT913X is not set +# CONFIG_MEDIA_TUNER_M88RS6000T is not set +# CONFIG_MEDIA_TUNER_MAX2165 is not set +# CONFIG_MEDIA_TUNER_MC44S803 is not set +# CONFIG_MEDIA_TUNER_MSI001 is not set +# CONFIG_MEDIA_TUNER_MT2060 is not set +# CONFIG_MEDIA_TUNER_MT2063 is not set +# CONFIG_MEDIA_TUNER_MT20XX is not set +# CONFIG_MEDIA_TUNER_MT2131 is not set +# CONFIG_MEDIA_TUNER_MT2266 is not set +# CONFIG_MEDIA_TUNER_MXL301RF is not set +# CONFIG_MEDIA_TUNER_MXL5005S is not set +# CONFIG_MEDIA_TUNER_MXL5007T is not set +# CONFIG_MEDIA_TUNER_QM1D1B0004 is not set +# CONFIG_MEDIA_TUNER_QM1D1C0042 is not set +# CONFIG_MEDIA_TUNER_QT1010 is not set +# CONFIG_MEDIA_TUNER_R820T is not set +# CONFIG_MEDIA_TUNER_SI2157 is not set +# CONFIG_MEDIA_TUNER_SIMPLE is not set +# CONFIG_MEDIA_TUNER_TDA18212 is not set +# CONFIG_MEDIA_TUNER_TDA18218 is not set +# CONFIG_MEDIA_TUNER_TDA18250 is not set +# CONFIG_MEDIA_TUNER_TDA18271 is not set +# CONFIG_MEDIA_TUNER_TDA827X is not set +# CONFIG_MEDIA_TUNER_TDA8290 is not set +# CONFIG_MEDIA_TUNER_TDA9887 is not set +# CONFIG_MEDIA_TUNER_TEA5761 is not set +# CONFIG_MEDIA_TUNER_TEA5767 is not set +# CONFIG_MEDIA_TUNER_TUA9001 is not set +# CONFIG_MEDIA_TUNER_XC2028 is not set +# CONFIG_MEDIA_TUNER_XC4000 is not set +# CONFIG_MEDIA_TUNER_XC5000 is not set +# CONFIG_MEDIA_USB_SUPPORT is not set +# CONFIG_MEGARAID_LEGACY is not set +# CONFIG_MEGARAID_NEWGEN is not set +# CONFIG_MEGARAID_SAS is not set +# CONFIG_MELLANOX_PLATFORM is not set +CONFIG_MEMBARRIER=y +# CONFIG_MEMORY is not set +# CONFIG_MEMORY_FAILURE is not set +# CONFIG_MEMORY_HOTPLUG is not set +# CONFIG_MEMSTICK is not set +# CONFIG_MEMTEST is not set +# CONFIG_MEN_A21_WDT is not set +# CONFIG_MESON_SM is not set +CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 +# CONFIG_MFD_88PM800 is not set +# CONFIG_MFD_88PM805 is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_AC100 is not set +# CONFIG_MFD_ACT8945A is not set +# CONFIG_MFD_ARIZONA_I2C is not set +# CONFIG_MFD_ARIZONA_SPI is not set +# CONFIG_MFD_AS3711 is not set +# CONFIG_MFD_AS3722 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_MFD_ATC260X_I2C is not set +# CONFIG_MFD_ATMEL_FLEXCOM is not set +# CONFIG_MFD_ATMEL_HLCDC is not set +# CONFIG_MFD_AXP20X is not set +# CONFIG_MFD_AXP20X_I2C is not set +# CONFIG_MFD_BCM590XX is not set +# CONFIG_MFD_BD9571MWV is not set +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_CPCAP is not set +# CONFIG_MFD_CROS_EC is not set +# CONFIG_MFD_CS5535 is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9055 is not set +# CONFIG_MFD_DA9062 is not set +# CONFIG_MFD_DA9063 is not set +# CONFIG_MFD_DA9150 is not set +# CONFIG_MFD_DLN2 is not set +# CONFIG_MFD_EXYNOS_LPASS is not set +# CONFIG_MFD_GATEWORKS_GSC is not set +# CONFIG_MFD_HI6421_PMIC is not set +# CONFIG_MFD_INTEL_M10_BMC is not set +# CONFIG_MFD_INTEL_PMT is not set +# CONFIG_MFD_INTEL_QUARK_I2C_GPIO is not set +# CONFIG_MFD_IQS62X is not set +# CONFIG_MFD_JANZ_CMODIO is not set +# CONFIG_MFD_KEMPLD is not set +# CONFIG_MFD_LM3533 is not set +# CONFIG_MFD_LOCHNAGAR is not set +# CONFIG_MFD_LP3943 is not set +# CONFIG_MFD_LP8788 is not set +# CONFIG_MFD_MADERA is not set +# CONFIG_MFD_MAX14577 is not set +# CONFIG_MFD_MAX77620 is not set +# CONFIG_MFD_MAX77650 is not set +# CONFIG_MFD_MAX77686 is not set +# CONFIG_MFD_MAX77693 is not set +# CONFIG_MFD_MAX77714 is not set +# CONFIG_MFD_MAX77843 is not set +# CONFIG_MFD_MAX8907 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_MFD_MC13XXX_I2C is not set +# CONFIG_MFD_MC13XXX_SPI is not set +# CONFIG_MFD_MENF21BMC is not set +# CONFIG_MFD_MP2629 is not set +# CONFIG_MFD_MT6360 is not set +# CONFIG_MFD_MT6370 is not set +# CONFIG_MFD_MT6397 is not set +# CONFIG_MFD_NTXEC is not set +# CONFIG_MFD_OCELOT is not set +# CONFIG_MFD_OMAP_USB_HOST is not set +# CONFIG_MFD_PALMAS is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_PM8921_CORE is not set +# CONFIG_MFD_PM8XXX is not set +# CONFIG_MFD_QCOM_PM8008 is not set +# CONFIG_MFD_RASPBERRYPI_POE_HAT is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_MFD_RDC321X is not set +# CONFIG_MFD_RETU is not set +# CONFIG_MFD_RK808 is not set +# CONFIG_MFD_RN5T618 is not set +# CONFIG_MFD_ROHM_BD70528 is not set +# CONFIG_MFD_ROHM_BD71828 is not set +# CONFIG_MFD_ROHM_BD718XX is not set +# CONFIG_MFD_ROHM_BD957XMUF is not set +# CONFIG_MFD_RP1 is not set +# CONFIG_MFD_RPISENSE_CORE is not set +# CONFIG_MFD_RSMU_I2C is not set +# CONFIG_MFD_RSMU_SPI is not set +# CONFIG_MFD_RT4831 is not set +# CONFIG_MFD_RT5033 is not set +# CONFIG_MFD_RT5120 is not set +# CONFIG_MFD_RTSX_PCI is not set +# CONFIG_MFD_RTSX_USB is not set +# CONFIG_MFD_SEC_CORE is not set +# CONFIG_MFD_SI476X_CORE is not set +# CONFIG_MFD_SKY81452 is not set +# CONFIG_MFD_SL28CPLD is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_SMSC is not set +# CONFIG_MFD_STMFX is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_STPMIC1 is not set +# CONFIG_MFD_SY7636A is not set +# CONFIG_MFD_SYSCON is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_MFD_TIMBERDALE is not set +# CONFIG_MFD_TI_AM335X_TSCADC is not set +# CONFIG_MFD_TI_LMU is not set +# CONFIG_MFD_TI_LP873X is not set +# CONFIG_MFD_TI_LP87565 is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_TPS65086 is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS65218 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS65910 is not set +# CONFIG_MFD_TPS65912 is not set +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_MFD_TPS68470 is not set +# CONFIG_MFD_TPS80031 is not set +# CONFIG_MFD_TQMX86 is not set +# CONFIG_MFD_VIPERBOARD is not set +# CONFIG_MFD_VX855 is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_WM831X is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MG_DISK is not set +# CONFIG_MHI_BUS is not set +# CONFIG_MHI_BUS_DEBUG is not set +# CONFIG_MHI_BUS_EP is not set +# CONFIG_MHI_BUS_PCI_GENERIC is not set +# CONFIG_MHI_NET is not set +# CONFIG_MHI_WWAN_CTRL is not set +# CONFIG_MHI_WWAN_MBIM is not set +# CONFIG_MICREL_KS8995MA is not set +# CONFIG_MICREL_PHY is not set +# CONFIG_MICROCHIP_KSZ is not set +# CONFIG_MICROCHIP_PHY is not set +# CONFIG_MICROCHIP_PIT64B is not set +# CONFIG_MICROCHIP_T1_PHY is not set +# CONFIG_MICROSEMI_PHY is not set +# CONFIG_MIGRATION is not set +CONFIG_MII=y +# CONFIG_MIKROTIK is not set +# CONFIG_MIKROTIK_RB532 is not set +# CONFIG_MINIX_FS is not set +# CONFIG_MINIX_FS_NATIVE_ENDIAN is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_MIPS32_N32 is not set +# CONFIG_MIPS32_O32 is not set +# CONFIG_MIPS_ALCHEMY is not set +# CONFIG_MIPS_CDMM is not set +# CONFIG_MIPS_CMDLINE_DTB_EXTEND is not set +# CONFIG_MIPS_CMDLINE_FROM_DTB is not set +# CONFIG_MIPS_CMP is not set +# CONFIG_MIPS_COBALT is not set +# CONFIG_MIPS_CPS is not set +# CONFIG_MIPS_ELF_APPENDED_DTB is not set +# CONFIG_MIPS_FPU_EMULATOR is not set +# CONFIG_MIPS_FP_SUPPORT is not set +# CONFIG_MIPS_GENERIC is not set +# CONFIG_MIPS_GENERIC_KERNEL is not set +# CONFIG_MIPS_MALTA is not set +# CONFIG_MIPS_O32_FP64_SUPPORT is not set +# CONFIG_MIPS_PARAVIRT is not set +# CONFIG_MIPS_PLATFORM_DEVICES is not set +# CONFIG_MIPS_RAW_APPENDED_DTB is not set +# CONFIG_MIPS_SEAD3 is not set +# CONFIG_MIPS_VA_BITS_48 is not set +# CONFIG_MIPS_VPE_LOADER is not set +# CONFIG_MISC_ALCOR_PCI is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_MISC_RTSX_PCI is not set +# CONFIG_MISC_RTSX_USB is not set +# CONFIG_MISDN is not set +# CONFIG_MISDN_AVMFRITZ is not set +# CONFIG_MISDN_HFCPCI is not set +# CONFIG_MISDN_HFCUSB is not set +# CONFIG_MISDN_INFINEON is not set +# CONFIG_MISDN_NETJET is not set +# CONFIG_MISDN_SPEEDFAX is not set +# CONFIG_MISDN_W6692 is not set +CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY=y +# CONFIG_MKISS is not set +# CONFIG_MLX4_CORE is not set +# CONFIG_MLX4_EN is not set +# CONFIG_MLX5_CORE is not set +# CONFIG_MLX5_EN_MACSEC is not set +# CONFIG_MLX5_SF is not set +# CONFIG_MLX5_VFIO_PCI is not set +# CONFIG_MLX90614 is not set +# CONFIG_MLX90632 is not set +# CONFIG_MLXFW is not set +# CONFIG_MLXSW_CORE is not set +# CONFIG_MLX_CPLD_PLATFORM is not set +# CONFIG_MLX_PLATFORM is not set +# CONFIG_MMA7455_I2C is not set +# CONFIG_MMA7455_SPI is not set +# CONFIG_MMA7660 is not set +# CONFIG_MMA8452 is not set +# CONFIG_MMA9551 is not set +# CONFIG_MMA9553 is not set +# CONFIG_MMC is not set +# CONFIG_MMC35240 is not set +# CONFIG_MMC_ARMMMCI is not set +# CONFIG_MMC_AU1X is not set +# CONFIG_MMC_BLOCK is not set +CONFIG_MMC_BLOCK_BOUNCE=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_CAVIUM_THUNDERX is not set +# CONFIG_MMC_CB710 is not set +# CONFIG_MMC_CQHCI is not set +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_DW is not set +# CONFIG_MMC_HSQ is not set +# CONFIG_MMC_JZ4740 is not set +# CONFIG_MMC_MTK is not set +# CONFIG_MMC_MVSDIO is not set +# CONFIG_MMC_S3C is not set +# CONFIG_MMC_SDHCI is not set +# CONFIG_MMC_SDHCI_ACPI is not set +# CONFIG_MMC_SDHCI_AM654 is not set +# CONFIG_MMC_SDHCI_BCM_KONA is not set +# CONFIG_MMC_SDHCI_BRCMSTB is not set +# CONFIG_MMC_SDHCI_CADENCE is not set +# CONFIG_MMC_SDHCI_F_SDH30 is not set +# CONFIG_MMC_SDHCI_IPROC is not set +# CONFIG_MMC_SDHCI_MILBEAUT is not set +# CONFIG_MMC_SDHCI_MSM is not set +# CONFIG_MMC_SDHCI_OF_ARASAN is not set +# CONFIG_MMC_SDHCI_OF_ASPEED is not set +# CONFIG_MMC_SDHCI_OF_AT91 is not set +# CONFIG_MMC_SDHCI_OF_DWCMSHC is not set +# CONFIG_MMC_SDHCI_OF_ESDHC is not set +# CONFIG_MMC_SDHCI_OF_HLWD is not set +# CONFIG_MMC_SDHCI_OMAP is not set +# CONFIG_MMC_SDHCI_PXAV2 is not set +# CONFIG_MMC_SDHCI_PXAV3 is not set +# CONFIG_MMC_SDHCI_S3C is not set +# CONFIG_MMC_SDHCI_XENON is not set +# CONFIG_MMC_SDRICOH_CS is not set +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_STM32_SDMMC is not set +# CONFIG_MMC_TEST is not set +# CONFIG_MMC_TIFM_SD is not set +# CONFIG_MMC_TOSHIBA_PCI is not set +# CONFIG_MMC_USDHI6ROL0 is not set +# CONFIG_MMC_USHC is not set +# CONFIG_MMC_VIA_SDMMC is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMIOTRACE is not set +CONFIG_MMU=y +CONFIG_MMU_GATHER_RCU_TABLE_FREE=y +CONFIG_MMU_GATHER_TABLE_FREE=y +CONFIG_MODPROBE_PATH="/sbin/modprobe" +CONFIG_MODULES=y +# CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS is not set +# CONFIG_MODULE_COMPRESS is not set +# CONFIG_MODULE_COMPRESS_GZIP is not set +CONFIG_MODULE_COMPRESS_NONE=y +# CONFIG_MODULE_COMPRESS_XZ is not set +# CONFIG_MODULE_COMPRESS_ZSTD is not set +# CONFIG_MODULE_FORCE_LOAD is not set +# CONFIG_MODULE_FORCE_UNLOAD is not set +# CONFIG_MODULE_SIG is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_MODULE_STRIPPED=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_UNLOAD_TAINT_TRACKING is not set +# CONFIG_MODVERSIONS is not set +# CONFIG_MOST is not set +# CONFIG_MOTORCOMM_PHY is not set +# CONFIG_MOUSE_APPLETOUCH is not set +# CONFIG_MOUSE_ELAN_I2C is not set +# CONFIG_MOUSE_GPIO is not set +# CONFIG_MOUSE_INPORT is not set +# CONFIG_MOUSE_LOGIBM is not set +# CONFIG_MOUSE_PC110PAD is not set +# CONFIG_MOUSE_PS2_FOCALTECH is not set +# CONFIG_MOUSE_PS2_SENTELIC is not set +# CONFIG_MOUSE_SYNAPTICS_I2C is not set +# CONFIG_MOUSE_SYNAPTICS_USB is not set +# CONFIG_MOXTET is not set +# CONFIG_MPL115 is not set +# CONFIG_MPL115_I2C is not set +# CONFIG_MPL115_SPI is not set +# CONFIG_MPL3115 is not set +# CONFIG_MPLS is not set +# CONFIG_MPLS_IPTUNNEL is not set +# CONFIG_MPLS_ROUTING is not set +# CONFIG_MPTCP is not set +# CONFIG_MPU3050_I2C is not set +# CONFIG_MQ_IOSCHED_DEADLINE is not set +# CONFIG_MQ_IOSCHED_KYBER is not set +# CONFIG_MS5611 is not set +# CONFIG_MS5637 is not set +# CONFIG_MSA311 is not set +# CONFIG_MSCC_OCELOT_SWITCH is not set +# CONFIG_MSDOS_FS is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_MSE102X is not set +# CONFIG_MSI_BITMAP_SELFTEST is not set +# CONFIG_MSI_LAPTOP is not set +# CONFIG_MSM_GCC_8953 is not set +# CONFIG_MSM_MMCC_8994 is not set +# CONFIG_MST_IRQ is not set +CONFIG_MTD=y +# CONFIG_MTD_ABSENT is not set +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_MTD_BLOCK2MTD is not set +CONFIG_MTD_CFI=y +# CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_CFI_AMDSTD=y +# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +CONFIG_MTD_CFI_INTELEXT=y +# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set +CONFIG_MTD_CFI_NOSWAP=y +# CONFIG_MTD_CFI_STAA is not set +CONFIG_MTD_CFI_UTIL=y +# CONFIG_MTD_CMDLINE_PARTS is not set +CONFIG_MTD_COMPLEX_MAPPINGS=y +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_DOCG3 is not set +CONFIG_MTD_GEN_PROBE=y +# CONFIG_MTD_GPIO_ADDR is not set +# CONFIG_MTD_HYPERBUS is not set +# CONFIG_MTD_IMPA7 is not set +# CONFIG_MTD_INTEL_VR_NOR is not set +# CONFIG_MTD_JEDECPROBE is not set +# CONFIG_MTD_LATCH_ADDR is not set +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_LPDDR2_NVM is not set +# CONFIG_MTD_M25P80 is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +CONFIG_MTD_MAP_BANK_WIDTH_2=y +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MCHP23K256 is not set +# CONFIG_MTD_MCHP48L640 is not set +# CONFIG_MTD_MT81xx_NOR is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_MYLOADER_PARTS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_NAND_AMS_DELTA is not set +# CONFIG_MTD_NAND_AR934X is not set +# CONFIG_MTD_NAND_AR934X_HW_ECC is not set +# CONFIG_MTD_NAND_ARASAN is not set +# CONFIG_MTD_NAND_ATMEL is not set +# CONFIG_MTD_NAND_AU1550 is not set +# CONFIG_MTD_NAND_BCH is not set +# CONFIG_MTD_NAND_BF5XX is not set +# CONFIG_MTD_NAND_BRCMNAND is not set +# CONFIG_MTD_NAND_BRCMNAND_BCM63XX is not set +# CONFIG_MTD_NAND_BRCMNAND_BCMBCA is not set +# CONFIG_MTD_NAND_BRCMNAND_BRCMSTB is not set +# CONFIG_MTD_NAND_BRCMNAND_IPROC is not set +# CONFIG_MTD_NAND_CADENCE is not set +# CONFIG_MTD_NAND_CAFE is not set +# CONFIG_MTD_NAND_CM_X270 is not set +# CONFIG_MTD_NAND_CS553X is not set +# CONFIG_MTD_NAND_DAVINCI is not set +# CONFIG_MTD_NAND_DENALI is not set +# CONFIG_MTD_NAND_DENALI_DT is not set +# CONFIG_MTD_NAND_DENALI_PCI is not set +CONFIG_MTD_NAND_DENALI_SCRATCH_REG_ADDR=0xff108018 +# CONFIG_MTD_NAND_DISKONCHIP is not set +# CONFIG_MTD_NAND_DOCG4 is not set +# CONFIG_MTD_NAND_ECC is not set +# CONFIG_MTD_NAND_ECC_BCH is not set +# CONFIG_MTD_NAND_ECC_MXIC is not set +# CONFIG_MTD_NAND_ECC_SMC is not set +# CONFIG_MTD_NAND_ECC_SW_BCH is not set +# CONFIG_MTD_NAND_ECC_SW_HAMMING is not set +# CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC is not set +# CONFIG_MTD_NAND_FSL_ELBC is not set +# CONFIG_MTD_NAND_FSL_IFC is not set +# CONFIG_MTD_NAND_FSL_UPM is not set +# CONFIG_MTD_NAND_FSMC is not set +# CONFIG_MTD_NAND_GPIO is not set +# CONFIG_MTD_NAND_GPMI_NAND is not set +# CONFIG_MTD_NAND_HISI504 is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND_INTEL_LGM is not set +# CONFIG_MTD_NAND_JZ4740 is not set +# CONFIG_MTD_NAND_MPC5121_NFC is not set +# CONFIG_MTD_NAND_MTK is not set +# CONFIG_MTD_NAND_MTK_BMT is not set +# CONFIG_MTD_NAND_MXC is not set +# CONFIG_MTD_NAND_MXIC is not set +# CONFIG_MTD_NAND_NANDSIM is not set +# CONFIG_MTD_NAND_NDFC is not set +# CONFIG_MTD_NAND_NUC900 is not set +# CONFIG_MTD_NAND_OMAP2 is not set +# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set +# CONFIG_MTD_NAND_ORION is not set +# CONFIG_MTD_NAND_PASEMI is not set +# CONFIG_MTD_NAND_PLATFORM is not set +# CONFIG_MTD_NAND_PXA3xx is not set +# CONFIG_MTD_NAND_RB4XX is not set +# CONFIG_MTD_NAND_RB750 is not set +# CONFIG_MTD_NAND_RB91X is not set +# CONFIG_MTD_NAND_RICOH is not set +# CONFIG_MTD_NAND_S3C2410 is not set +# CONFIG_MTD_NAND_SHARPSL is not set +# CONFIG_MTD_NAND_SH_FLCTL is not set +# CONFIG_MTD_NAND_SOCRATES is not set +# CONFIG_MTD_NAND_TMIO is not set +# CONFIG_MTD_NAND_TXX9NDFMC is not set +CONFIG_MTD_OF_PARTS=y +# CONFIG_MTD_ONENAND is not set +# CONFIG_MTD_OOPS is not set +# CONFIG_MTD_OTP is not set +# CONFIG_MTD_PARSER_TRX is not set +# CONFIG_MTD_PARTITIONED_MASTER is not set +# CONFIG_MTD_PCI is not set +# CONFIG_MTD_PCMCIA is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_PHYSMAP_COMPAT is not set +# CONFIG_MTD_PHYSMAP_GEMINI is not set +# CONFIG_MTD_PHYSMAP_GPIO_ADDR is not set +# CONFIG_MTD_PHYSMAP_IXP4XX is not set +CONFIG_MTD_PHYSMAP_OF=y +# CONFIG_MTD_PHYSMAP_OF_GEMINI is not set +# CONFIG_MTD_PHYSMAP_OF_VERSATILE is not set +# CONFIG_MTD_PHYSMAP_VERSATILE is not set +# CONFIG_MTD_PLATRAM is not set +# CONFIG_MTD_PMC551 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_RAW_NAND is not set +CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK=-1 +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set +# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set +# CONFIG_MTD_ROM is not set +CONFIG_MTD_ROOTFS_ROOT_DEV=y +# CONFIG_MTD_ROUTERBOOT_PARTS is not set +# CONFIG_MTD_SERCOMM_PARTS is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_SM_COMMON is not set +# CONFIG_MTD_SPINAND_MT29F is not set +# CONFIG_MTD_SPI_NAND is not set +# CONFIG_MTD_SPI_NOR is not set +# CONFIG_MTD_SPI_NOR_SWP_DISABLE is not set +CONFIG_MTD_SPI_NOR_SWP_DISABLE_ON_VOLATILE=y +# CONFIG_MTD_SPI_NOR_SWP_KEEP is not set +# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set +# CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE is not set +CONFIG_MTD_SPLIT=y +# CONFIG_MTD_SPLIT_BCM63XX_FW is not set +# CONFIG_MTD_SPLIT_BCM_WFI_FW is not set +# CONFIG_MTD_SPLIT_BRNIMAGE_FW is not set +# CONFIG_MTD_SPLIT_ELF_FW is not set +# CONFIG_MTD_SPLIT_EVA_FW is not set +# CONFIG_MTD_SPLIT_FIRMWARE is not set +CONFIG_MTD_SPLIT_FIRMWARE_NAME="firmware" +# CONFIG_MTD_SPLIT_FIT_FW is not set +# CONFIG_MTD_SPLIT_H3C_VFS is not set +# CONFIG_MTD_SPLIT_JIMAGE_FW is not set +# CONFIG_MTD_SPLIT_LZMA_FW is not set +# CONFIG_MTD_SPLIT_MINOR_FW is not set +# CONFIG_MTD_SPLIT_SEAMA_FW is not set +# CONFIG_MTD_SPLIT_SEIL_FW is not set +CONFIG_MTD_SPLIT_SQUASHFS_ROOT=y +CONFIG_MTD_SPLIT_SUPPORT=y +# CONFIG_MTD_SPLIT_TPLINK_FW is not set +# CONFIG_MTD_SPLIT_TRX_FW is not set +# CONFIG_MTD_SPLIT_UIMAGE_FW is not set +# CONFIG_MTD_SPLIT_WRGG_FW is not set +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SWAP is not set +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_UBI is not set +# CONFIG_MTD_UBI_FASTMAP is not set +# CONFIG_MTD_UBI_GLUEBI is not set +# CONFIG_MTD_UBI_NVMEM is not set +# CONFIG_MTD_UIMAGE_SPLIT is not set +# CONFIG_MTD_VIRT_CONCAT is not set +# CONFIG_MTK_DEVAPC is not set +# CONFIG_MTK_MMC is not set +# CONFIG_MTK_MMSYS is not set +# CONFIG_MTK_T7XX is not set +# CONFIG_MTK_THERMAL is not set +# CONFIG_MULTIPLEXER is not set +CONFIG_MULTIUSER=y +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +# CONFIG_MUX_ADG792A is not set +# CONFIG_MUX_ADGS1408 is not set +# CONFIG_MUX_GPIO is not set +# CONFIG_MUX_MMIO is not set +# CONFIG_MV643XX_ETH is not set +# CONFIG_MVMDIO is not set +# CONFIG_MVNETA_BM is not set +# CONFIG_MVSW61XX_PHY is not set +# CONFIG_MV_XOR_V2 is not set +# CONFIG_MWAVE is not set +# CONFIG_MWL8K is not set +# CONFIG_MXC4005 is not set +# CONFIG_MXC6255 is not set +# CONFIG_MYRI10GE is not set +# CONFIG_NAMESPACES is not set +# CONFIG_NATIONAL_PHY is not set +# CONFIG_NATSEMI is not set +# CONFIG_NAU7802 is not set +# CONFIG_NBPFAXI_DMA is not set +# CONFIG_NCP_FS is not set +# CONFIG_NE2000 is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_NEC_MARKEINS is not set +CONFIG_NET=y +# CONFIG_NETCONSOLE is not set +CONFIG_NETDEVICES=y +# CONFIG_NETDEVSIM is not set +# CONFIG_NETFILTER is not set +# CONFIG_NETFILTER_ADVANCED is not set +# CONFIG_NETFILTER_DEBUG is not set +# CONFIG_NETFILTER_EGRESS is not set +# CONFIG_NETFILTER_INGRESS is not set +# CONFIG_NETFILTER_NETLINK is not set +# CONFIG_NETFILTER_NETLINK_ACCT is not set +# CONFIG_NETFILTER_NETLINK_GLUE_CT is not set +# CONFIG_NETFILTER_NETLINK_HOOK is not set +# CONFIG_NETFILTER_NETLINK_LOG is not set +# CONFIG_NETFILTER_NETLINK_OSF is not set +# CONFIG_NETFILTER_NETLINK_QUEUE is not set +# CONFIG_NETFILTER_XTABLES is not set +# CONFIG_NETFILTER_XTABLES_COMPAT is not set +# CONFIG_NETFILTER_XT_CONNMARK is not set +# CONFIG_NETFILTER_XT_MARK is not set +# CONFIG_NETFILTER_XT_MATCH_ADDRTYPE is not set +# CONFIG_NETFILTER_XT_MATCH_BPF is not set +# CONFIG_NETFILTER_XT_MATCH_CGROUP is not set +# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set +# CONFIG_NETFILTER_XT_MATCH_COMMENT is not set +# CONFIG_NETFILTER_XT_MATCH_CONNBYTES is not set +# CONFIG_NETFILTER_XT_MATCH_CONNLABEL is not set +# CONFIG_NETFILTER_XT_MATCH_CONNLIMIT is not set +# CONFIG_NETFILTER_XT_MATCH_CONNMARK is not set +# CONFIG_NETFILTER_XT_MATCH_CONNTRACK is not set +# CONFIG_NETFILTER_XT_MATCH_CPU is not set +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set +# CONFIG_NETFILTER_XT_MATCH_DEVGROUP is not set +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set +# CONFIG_NETFILTER_XT_MATCH_ECN is not set +# CONFIG_NETFILTER_XT_MATCH_ESP is not set +# CONFIG_NETFILTER_XT_MATCH_HASHLIMIT is not set +# CONFIG_NETFILTER_XT_MATCH_HELPER is not set +# CONFIG_NETFILTER_XT_MATCH_HL is not set +# CONFIG_NETFILTER_XT_MATCH_IPCOMP is not set +# CONFIG_NETFILTER_XT_MATCH_IPRANGE is not set +# CONFIG_NETFILTER_XT_MATCH_L2TP is not set +# CONFIG_NETFILTER_XT_MATCH_LENGTH is not set +# CONFIG_NETFILTER_XT_MATCH_LIMIT is not set +# CONFIG_NETFILTER_XT_MATCH_MAC is not set +# CONFIG_NETFILTER_XT_MATCH_MARK is not set +# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set +# CONFIG_NETFILTER_XT_MATCH_NFACCT is not set +# CONFIG_NETFILTER_XT_MATCH_OSF is not set +# CONFIG_NETFILTER_XT_MATCH_OWNER is not set +# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set +# CONFIG_NETFILTER_XT_MATCH_PKTTYPE is not set +# CONFIG_NETFILTER_XT_MATCH_POLICY is not set +# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set +# CONFIG_NETFILTER_XT_MATCH_REALM is not set +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +# CONFIG_NETFILTER_XT_MATCH_SOCKET is not set +# CONFIG_NETFILTER_XT_MATCH_STATE is not set +# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set +# CONFIG_NETFILTER_XT_MATCH_STRING is not set +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set +# CONFIG_NETFILTER_XT_MATCH_TIME is not set +# CONFIG_NETFILTER_XT_MATCH_U32 is not set +# CONFIG_NETFILTER_XT_TARGET_AUDIT is not set +# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set +# CONFIG_NETFILTER_XT_TARGET_CLASSIFY is not set +# CONFIG_NETFILTER_XT_TARGET_CONNMARK is not set +# CONFIG_NETFILTER_XT_TARGET_CT is not set +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +# CONFIG_NETFILTER_XT_TARGET_HL is not set +# CONFIG_NETFILTER_XT_TARGET_HMARK is not set +# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set +# CONFIG_NETFILTER_XT_TARGET_LED is not set +# CONFIG_NETFILTER_XT_TARGET_LOG is not set +# CONFIG_NETFILTER_XT_TARGET_MARK is not set +# CONFIG_NETFILTER_XT_TARGET_NETMAP is not set +# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set +# CONFIG_NETFILTER_XT_TARGET_NFQUEUE is not set +# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set +# CONFIG_NETFILTER_XT_TARGET_REDIRECT is not set +# CONFIG_NETFILTER_XT_TARGET_SECMARK is not set +# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set +# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set +# CONFIG_NETFILTER_XT_TARGET_TEE is not set +# CONFIG_NETFILTER_XT_TARGET_TPROXY is not set +# CONFIG_NETFILTER_XT_TARGET_TRACE is not set +# CONFIG_NETFS_STATS is not set +# CONFIG_NETLABEL is not set +# CONFIG_NETLINK_DIAG is not set +# CONFIG_NETLINK_MMAP is not set +# CONFIG_NETPOLL is not set +# CONFIG_NETROM is not set +CONFIG_NETWORK_FILESYSTEMS=y +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETXEN_NIC is not set +# CONFIG_NET_9P is not set +# CONFIG_NET_ACT_BPF is not set +# CONFIG_NET_ACT_CSUM is not set +# CONFIG_NET_ACT_CT is not set +# CONFIG_NET_ACT_GACT is not set +# CONFIG_NET_ACT_GATE is not set +# CONFIG_NET_ACT_IFE is not set +# CONFIG_NET_ACT_IPT is not set +# CONFIG_NET_ACT_MIRRED is not set +# CONFIG_NET_ACT_MPLS is not set +# CONFIG_NET_ACT_NAT is not set +# CONFIG_NET_ACT_PEDIT is not set +# CONFIG_NET_ACT_POLICE is not set +# CONFIG_NET_ACT_SAMPLE is not set +# CONFIG_NET_ACT_SIMP is not set +# CONFIG_NET_ACT_SKBEDIT is not set +# CONFIG_NET_ACT_SKBMOD is not set +# CONFIG_NET_ACT_TUNNEL_KEY is not set +# CONFIG_NET_ACT_VLAN is not set +CONFIG_NET_CADENCE=y +# CONFIG_NET_CALXEDA_XGMAC is not set +CONFIG_NET_CLS=y +# CONFIG_NET_CLS_ACT is not set +# CONFIG_NET_CLS_BASIC is not set +# CONFIG_NET_CLS_BPF is not set +# CONFIG_NET_CLS_FLOW is not set +# CONFIG_NET_CLS_FLOWER is not set +# CONFIG_NET_CLS_FW is not set +CONFIG_NET_CLS_IND=y +# CONFIG_NET_CLS_MATCHALL is not set +# CONFIG_NET_CLS_ROUTE4 is not set +# CONFIG_NET_CLS_RSVP is not set +# CONFIG_NET_CLS_RSVP6 is not set +# CONFIG_NET_CLS_U32 is not set +CONFIG_NET_CORE=y +# CONFIG_NET_DEVLINK is not set +# CONFIG_NET_DEV_REFCNT_TRACKER is not set +# CONFIG_NET_DROP_MONITOR is not set +# CONFIG_NET_DSA is not set +# CONFIG_NET_DSA_AR9331 is not set +# CONFIG_NET_DSA_BCM_SF2 is not set +# CONFIG_NET_DSA_LANTIQ_GSWIP is not set +# CONFIG_NET_DSA_LEGACY is not set +# CONFIG_NET_DSA_LOOP is not set +# CONFIG_NET_DSA_MICROCHIP_KSZ8795 is not set +# CONFIG_NET_DSA_MICROCHIP_KSZ9477 is not set +# CONFIG_NET_DSA_MICROCHIP_KSZ_COMMON is not set +# CONFIG_NET_DSA_MSCC_FELIX is not set +# CONFIG_NET_DSA_MSCC_SEVILLE is not set +# CONFIG_NET_DSA_MT7530 is not set +# CONFIG_NET_DSA_MV88E6060 is not set +# CONFIG_NET_DSA_MV88E6123_61_65 is not set +# CONFIG_NET_DSA_MV88E6131 is not set +# CONFIG_NET_DSA_MV88E6171 is not set +# CONFIG_NET_DSA_MV88E6352 is not set +# CONFIG_NET_DSA_MV88E6XXX is not set +# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set +# CONFIG_NET_DSA_MV88E6XXX_PTP is not set +# CONFIG_NET_DSA_QCA8K is not set +# CONFIG_NET_DSA_QCA8K_LEDS_SUPPORT is not set +# CONFIG_NET_DSA_REALTEK is not set +# CONFIG_NET_DSA_REALTEK_SMI is not set +# CONFIG_NET_DSA_SJA1105 is not set +# CONFIG_NET_DSA_SMSC_LAN9303_I2C is not set +# CONFIG_NET_DSA_SMSC_LAN9303_MDIO is not set +# CONFIG_NET_DSA_TAG_8021Q is not set +# CONFIG_NET_DSA_TAG_AR9331 is not set +# CONFIG_NET_DSA_TAG_BRCM is not set +# CONFIG_NET_DSA_TAG_BRCM_LEGACY is not set +# CONFIG_NET_DSA_TAG_BRCM_PREPEND is not set +# CONFIG_NET_DSA_TAG_DSA is not set +# CONFIG_NET_DSA_TAG_EDSA is not set +# CONFIG_NET_DSA_TAG_GSWIP is not set +# CONFIG_NET_DSA_TAG_HELLCREEK is not set +# CONFIG_NET_DSA_TAG_KSZ is not set +# CONFIG_NET_DSA_TAG_LAN9303 is not set +# CONFIG_NET_DSA_TAG_MTK is not set +# CONFIG_NET_DSA_TAG_OCELOT is not set +# CONFIG_NET_DSA_TAG_OCELOT_8021Q is not set +# CONFIG_NET_DSA_TAG_QCA is not set +# CONFIG_NET_DSA_TAG_RTL4_A is not set +# CONFIG_NET_DSA_TAG_RTL8_4 is not set +# CONFIG_NET_DSA_TAG_RZN1_A5PSW is not set +# CONFIG_NET_DSA_TAG_SJA1105 is not set +# CONFIG_NET_DSA_TAG_TRAILER is not set +# CONFIG_NET_DSA_TAG_XRS700X is not set +# CONFIG_NET_DSA_VITESSE_VSC73XX is not set +# CONFIG_NET_DSA_VITESSE_VSC73XX_PLATFORM is not set +# CONFIG_NET_DSA_VITESSE_VSC73XX_SPI is not set +# CONFIG_NET_DSA_XRS700X_I2C is not set +# CONFIG_NET_DSA_XRS700X_MDIO is not set +# CONFIG_NET_EMATCH is not set +# CONFIG_NET_EMATCH_CANID is not set +# CONFIG_NET_EMATCH_CMP is not set +# CONFIG_NET_EMATCH_IPT is not set +# CONFIG_NET_EMATCH_META is not set +# CONFIG_NET_EMATCH_NBYTE is not set +CONFIG_NET_EMATCH_STACK=32 +# CONFIG_NET_EMATCH_TEXT is not set +# CONFIG_NET_EMATCH_U32 is not set +# CONFIG_NET_FAILOVER is not set +# CONFIG_NET_FC is not set +# CONFIG_NET_FOU is not set +# CONFIG_NET_FOU_IP_TUNNELS is not set +# CONFIG_NET_IFE is not set +# CONFIG_NET_IPGRE is not set +CONFIG_NET_IPGRE_BROADCAST=y +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPVTI is not set +# CONFIG_NET_IP_TUNNEL is not set +# CONFIG_NET_KEY is not set +# CONFIG_NET_KEY_MIGRATE is not set +# CONFIG_NET_L3_MASTER_DEV is not set +# CONFIG_NET_MEDIATEK_STAR_EMAC is not set +# CONFIG_NET_MPLS_GSO is not set +# CONFIG_NET_NCSI is not set +# CONFIG_NET_NSH is not set +# CONFIG_NET_NS_REFCNT_TRACKER is not set +# CONFIG_NET_PACKET_ENGINE is not set +# CONFIG_NET_PKTGEN is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_NET_PTP_CLASSIFY is not set +CONFIG_NET_RX_BUSY_POLL=y +# CONFIG_NET_SB1000 is not set +CONFIG_NET_SCHED=y +# CONFIG_NET_SCH_CAKE is not set +# CONFIG_NET_SCH_CBS is not set +# CONFIG_NET_SCH_CHOKE is not set +# CONFIG_NET_SCH_CODEL is not set +CONFIG_NET_SCH_DEFAULT=y +# CONFIG_NET_SCH_DRR is not set +# CONFIG_NET_SCH_ETF is not set +# CONFIG_NET_SCH_ETS is not set +CONFIG_NET_SCH_FIFO=y +# CONFIG_NET_SCH_FQ is not set +CONFIG_NET_SCH_FQ_CODEL=y +# CONFIG_NET_SCH_FQ_PIE is not set +# CONFIG_NET_SCH_GRED is not set +# CONFIG_NET_SCH_HFSC is not set +# CONFIG_NET_SCH_HHF is not set +# CONFIG_NET_SCH_HTB is not set +# CONFIG_NET_SCH_INGRESS is not set +# CONFIG_NET_SCH_MQPRIO is not set +# CONFIG_NET_SCH_MULTIQ is not set +# CONFIG_NET_SCH_NETEM is not set +# CONFIG_NET_SCH_PIE is not set +# CONFIG_NET_SCH_PLUG is not set +# CONFIG_NET_SCH_PRIO is not set +# CONFIG_NET_SCH_QFQ is not set +# CONFIG_NET_SCH_RED is not set +# CONFIG_NET_SCH_SFB is not set +# CONFIG_NET_SCH_SFQ is not set +# CONFIG_NET_SCH_SKBPRIO is not set +# CONFIG_NET_SCH_TAPRIO is not set +# CONFIG_NET_SCH_TBF is not set +# CONFIG_NET_SCH_TEQL is not set +# CONFIG_NET_SCTPPROBE is not set +# CONFIG_NET_SELFTESTS is not set +CONFIG_NET_SOCK_MSG=y +# CONFIG_NET_SWITCHDEV is not set +# CONFIG_NET_TCPPROBE is not set +# CONFIG_NET_TC_SKB_EXT is not set +# CONFIG_NET_TEAM is not set +# CONFIG_NET_TULIP is not set +# CONFIG_NET_UDP_TUNNEL is not set +CONFIG_NET_VENDOR_3COM=y +CONFIG_NET_VENDOR_8390=y +CONFIG_NET_VENDOR_ADAPTEC=y +CONFIG_NET_VENDOR_ADI=y +CONFIG_NET_VENDOR_AGERE=y +CONFIG_NET_VENDOR_ALACRITECH=y +CONFIG_NET_VENDOR_ALTEON=y +CONFIG_NET_VENDOR_AMAZON=y +CONFIG_NET_VENDOR_AMD=y +CONFIG_NET_VENDOR_AQUANTIA=y +CONFIG_NET_VENDOR_ARC=y +# CONFIG_NET_VENDOR_ASIX is not set +CONFIG_NET_VENDOR_ATHEROS=y +CONFIG_NET_VENDOR_AURORA=y +CONFIG_NET_VENDOR_BROADCOM=y +CONFIG_NET_VENDOR_BROCADE=y +CONFIG_NET_VENDOR_CADENCE=y +CONFIG_NET_VENDOR_CAVIUM=y +CONFIG_NET_VENDOR_CHELSIO=y +CONFIG_NET_VENDOR_CIRRUS=y +CONFIG_NET_VENDOR_CISCO=y +CONFIG_NET_VENDOR_CORTINA=y +# CONFIG_NET_VENDOR_DAVICOM is not set +CONFIG_NET_VENDOR_DEC=y +CONFIG_NET_VENDOR_DLINK=y +CONFIG_NET_VENDOR_EMULEX=y +# CONFIG_NET_VENDOR_ENGLEDER is not set +CONFIG_NET_VENDOR_EXAR=y +CONFIG_NET_VENDOR_EZCHIP=y +CONFIG_NET_VENDOR_FARADAY=y +CONFIG_NET_VENDOR_FREESCALE=y +CONFIG_NET_VENDOR_FUJITSU=y +# CONFIG_NET_VENDOR_FUNGIBLE is not set +CONFIG_NET_VENDOR_GOOGLE=y +CONFIG_NET_VENDOR_HISILICON=y +CONFIG_NET_VENDOR_HP=y +CONFIG_NET_VENDOR_HUAWEI=y +CONFIG_NET_VENDOR_I825XX=y +CONFIG_NET_VENDOR_IBM=y +CONFIG_NET_VENDOR_INTEL=y +# CONFIG_NET_VENDOR_LITEX is not set +CONFIG_NET_VENDOR_MARVELL=y +CONFIG_NET_VENDOR_MELLANOX=y +CONFIG_NET_VENDOR_MICREL=y +CONFIG_NET_VENDOR_MICROCHIP=y +CONFIG_NET_VENDOR_MICROSEMI=y +# CONFIG_NET_VENDOR_MICROSOFT is not set +CONFIG_NET_VENDOR_MYRI=y +CONFIG_NET_VENDOR_NATSEMI=y +CONFIG_NET_VENDOR_NETERION=y +CONFIG_NET_VENDOR_NETRONOME=y +CONFIG_NET_VENDOR_NI=y +CONFIG_NET_VENDOR_NVIDIA=y +CONFIG_NET_VENDOR_OKI=y +CONFIG_NET_VENDOR_PACKET_ENGINES=y +CONFIG_NET_VENDOR_PENSANDO=y +CONFIG_NET_VENDOR_QLOGIC=y +CONFIG_NET_VENDOR_QUALCOMM=y +CONFIG_NET_VENDOR_RDC=y +CONFIG_NET_VENDOR_REALTEK=y +CONFIG_NET_VENDOR_RENESAS=y +CONFIG_NET_VENDOR_ROCKER=y +CONFIG_NET_VENDOR_SAMSUNG=y +CONFIG_NET_VENDOR_SEEQ=y +CONFIG_NET_VENDOR_SILAN=y +CONFIG_NET_VENDOR_SIS=y +CONFIG_NET_VENDOR_SMSC=y +CONFIG_NET_VENDOR_SOCIONEXT=y +CONFIG_NET_VENDOR_SOLARFLARE=y +CONFIG_NET_VENDOR_STMICRO=y +CONFIG_NET_VENDOR_SUN=y +CONFIG_NET_VENDOR_SYNOPSYS=y +CONFIG_NET_VENDOR_TEHUTI=y +CONFIG_NET_VENDOR_TI=y +CONFIG_NET_VENDOR_TOSHIBA=y +# CONFIG_NET_VENDOR_VERTEXCOM is not set +CONFIG_NET_VENDOR_VIA=y +# CONFIG_NET_VENDOR_WANGXUN is not set +CONFIG_NET_VENDOR_WIZNET=y +CONFIG_NET_VENDOR_XILINX=y +CONFIG_NET_VENDOR_XIRCOM=y +# CONFIG_NET_VRF is not set +# CONFIG_NET_XGENE is not set +CONFIG_NEW_LEDS=y +# CONFIG_NFC is not set +# CONFIG_NFP is not set +# CONFIG_NFSD is not set +# CONFIG_NFSD_V2_ACL is not set +CONFIG_NFSD_V3=y +# CONFIG_NFSD_V3_ACL is not set +# CONFIG_NFSD_V4 is not set +# CONFIG_NFS_ACL_SUPPORT is not set +CONFIG_NFS_COMMON=y +# CONFIG_NFS_DISABLE_UDP_SUPPORT is not set +# CONFIG_NFS_FS is not set +# CONFIG_NFS_FSCACHE is not set +# CONFIG_NFS_SWAP is not set +# CONFIG_NFS_V2 is not set +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_V4_1 is not set +# CONFIG_NFTL is not set +# CONFIG_NFT_BRIDGE_META is not set +# CONFIG_NFT_BRIDGE_REJECT is not set +# CONFIG_NFT_CONNLIMIT is not set +# CONFIG_NFT_DUP_IPV4 is not set +# CONFIG_NFT_DUP_IPV6 is not set +# CONFIG_NFT_FIB_IPV4 is not set +# CONFIG_NFT_FIB_IPV6 is not set +# CONFIG_NFT_FIB_NETDEV is not set +# CONFIG_NFT_FLOW_OFFLOAD is not set +# CONFIG_NFT_OBJREF is not set +# CONFIG_NFT_OSF is not set +# CONFIG_NFT_REJECT_NETDEV is not set +# CONFIG_NFT_RT is not set +# CONFIG_NFT_SET_BITMAP is not set +# CONFIG_NFT_SOCKET is not set +# CONFIG_NFT_SYNPROXY is not set +# CONFIG_NFT_TPROXY is not set +# CONFIG_NFT_TUNNEL is not set +# CONFIG_NFT_XFRM is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NF_CONNTRACK_AMANDA is not set +# CONFIG_NF_CONNTRACK_BRIDGE is not set +# CONFIG_NF_CONNTRACK_EVENTS is not set +# CONFIG_NF_CONNTRACK_FTP is not set +# CONFIG_NF_CONNTRACK_H323 is not set +# CONFIG_NF_CONNTRACK_IRC is not set +# CONFIG_NF_CONNTRACK_LABELS is not set +# CONFIG_NF_CONNTRACK_MARK is not set +# CONFIG_NF_CONNTRACK_NETBIOS_NS is not set +# CONFIG_NF_CONNTRACK_PPTP is not set +CONFIG_NF_CONNTRACK_PROCFS=y +# CONFIG_NF_CONNTRACK_PROC_COMPAT is not set +# CONFIG_NF_CONNTRACK_SANE is not set +# CONFIG_NF_CONNTRACK_SECMARK is not set +# CONFIG_NF_CONNTRACK_SIP is not set +# CONFIG_NF_CONNTRACK_SNMP is not set +# CONFIG_NF_CONNTRACK_TFTP is not set +# CONFIG_NF_CONNTRACK_TIMEOUT is not set +# CONFIG_NF_CONNTRACK_TIMESTAMP is not set +# CONFIG_NF_CONNTRACK_ZONES is not set +# CONFIG_NF_CT_NETLINK is not set +# CONFIG_NF_CT_NETLINK_HELPER is not set +# CONFIG_NF_CT_NETLINK_TIMEOUT is not set +# CONFIG_NF_CT_PROTO_DCCP is not set +# CONFIG_NF_CT_PROTO_GRE is not set +# CONFIG_NF_CT_PROTO_SCTP is not set +# CONFIG_NF_CT_PROTO_UDPLITE is not set +# CONFIG_NF_DEFRAG_IPV4 is not set +# CONFIG_NF_DUP_IPV4 is not set +# CONFIG_NF_DUP_IPV6 is not set +# CONFIG_NF_FLOW_TABLE is not set +# CONFIG_NF_FLOW_TABLE_PROCFS is not set +# CONFIG_NF_LOG_ARP is not set +# CONFIG_NF_LOG_BRIDGE is not set +# CONFIG_NF_LOG_IPV4 is not set +# CONFIG_NF_LOG_NETDEV is not set +# CONFIG_NF_LOG_SYSLOG is not set +# CONFIG_NF_NAT is not set +# CONFIG_NF_NAT_AMANDA is not set +# CONFIG_NF_NAT_FTP is not set +# CONFIG_NF_NAT_H323 is not set +# CONFIG_NF_NAT_IRC is not set +# CONFIG_NF_NAT_NEEDED is not set +# CONFIG_NF_NAT_PPTP is not set +# CONFIG_NF_NAT_PROTO_GRE is not set +# CONFIG_NF_NAT_SIP is not set +# CONFIG_NF_NAT_SNMP_BASIC is not set +# CONFIG_NF_NAT_TFTP is not set +# CONFIG_NF_REJECT_IPV4 is not set +# CONFIG_NF_REJECT_IPV6 is not set +# CONFIG_NF_SOCKET_IPV4 is not set +# CONFIG_NF_SOCKET_IPV6 is not set +# CONFIG_NF_TABLES is not set +CONFIG_NF_TABLES_ARP=y +CONFIG_NF_TABLES_BRIDGE=y +CONFIG_NF_TABLES_INET=y +CONFIG_NF_TABLES_IPV4=y +CONFIG_NF_TABLES_IPV6=y +CONFIG_NF_TABLES_NETDEV=y +# CONFIG_NF_TABLES_SET is not set +# CONFIG_NF_TPROXY_IPV4 is not set +# CONFIG_NF_TPROXY_IPV6 is not set +# CONFIG_NGBE is not set +# CONFIG_NI65 is not set +# CONFIG_NI903X_WDT is not set +# CONFIG_NIC7018_WDT is not set +# CONFIG_NILFS2_FS is not set +# CONFIG_NIU is not set +# CONFIG_NI_XGE_MANAGEMENT_ENET is not set +CONFIG_NLATTR=y +# CONFIG_NLMON is not set +# CONFIG_NLM_XLP_BOARD is not set +# CONFIG_NLM_XLR_BOARD is not set +# CONFIG_NLS is not set +# CONFIG_NLS_ASCII is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_MAC_CELTIC is not set +# CONFIG_NLS_MAC_CENTEURO is not set +# CONFIG_NLS_MAC_CROATIAN is not set +# CONFIG_NLS_MAC_CYRILLIC is not set +# CONFIG_NLS_MAC_GAELIC is not set +# CONFIG_NLS_MAC_GREEK is not set +# CONFIG_NLS_MAC_ICELAND is not set +# CONFIG_NLS_MAC_INUIT is not set +# CONFIG_NLS_MAC_ROMAN is not set +# CONFIG_NLS_MAC_ROMANIAN is not set +# CONFIG_NLS_MAC_TURKISH is not set +# CONFIG_NLS_UTF8 is not set +CONFIG_NMI_LOG_BUF_SHIFT=13 +# CONFIG_NOA1305 is not set +# CONFIG_NOP_USB_XCEIV is not set +# CONFIG_NORTEL_HERMES is not set +# CONFIG_NOTIFIER_ERROR_INJECTION is not set +# CONFIG_NOUVEAU_LEGACY_CTX_SUPPORT is not set +# CONFIG_NOZOMI is not set +# CONFIG_NO_BOOTMEM is not set +# CONFIG_NO_HZ is not set +# CONFIG_NO_HZ_FULL is not set +# CONFIG_NO_HZ_IDLE is not set +# CONFIG_NS83820 is not set +# CONFIG_NTB is not set +# CONFIG_NTFS3_64BIT_CLUSTER is not set +# CONFIG_NTFS3_FS is not set +# CONFIG_NTFS3_FS_POSIX_ACL is not set +# CONFIG_NTFS3_LZX_XPRESS is not set +# CONFIG_NTFS_DEBUG is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_NTP_PPS is not set +# CONFIG_NULL_TTY is not set +# CONFIG_NUMA is not set +# CONFIG_NVIDIA_CARMEL_CNP_ERRATUM is not set +# CONFIG_NVM is not set +# CONFIG_NVMEM is not set +# CONFIG_NVMEM_BCM_OCOTP is not set +# CONFIG_NVMEM_IMX_OCOTP is not set +# CONFIG_NVMEM_LAYOUT_ONIE_TLV is not set +# CONFIG_NVMEM_LAYOUT_SL28_VPD is not set +# CONFIG_NVMEM_REBOOT_MODE is not set +# CONFIG_NVMEM_RMEM is not set +# CONFIG_NVMEM_SYSFS is not set +# CONFIG_NVMEM_U_BOOT_ENV is not set +# CONFIG_NVME_AUTH is not set +# CONFIG_NVME_FC is not set +# CONFIG_NVME_TARGET is not set +# CONFIG_NVME_TCP is not set +# CONFIG_NVME_VERBOSE_ERRORS is not set +# CONFIG_NVRAM is not set +# CONFIG_NV_TCO is not set +# CONFIG_NXP_C45_TJA11XX_PHY is not set +# CONFIG_NXP_STB220 is not set +# CONFIG_NXP_STB225 is not set +# CONFIG_NXP_TJA11XX_PHY is not set +# CONFIG_N_GSM is not set +# CONFIG_OABI_COMPAT is not set +# CONFIG_OBS600 is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_OCTEONTX2_AF is not set +# CONFIG_OCTEONTX2_PF is not set +# CONFIG_OCTEON_EP is not set +# CONFIG_OF_OVERLAY is not set +CONFIG_OF_RESERVED_MEM=y +# CONFIG_OF_UNITTEST is not set +# CONFIG_OID_REGISTRY is not set +# CONFIG_OMAP2_DSS_DEBUG is not set +# CONFIG_OMAP2_DSS_DEBUGFS is not set +# CONFIG_OMAP2_DSS_SDI is not set +# CONFIG_OMAP_OCP2SCP is not set +# CONFIG_OMAP_USB2 is not set +# CONFIG_OMFS_FS is not set +# CONFIG_OPENVSWITCH is not set +# CONFIG_OPEN_DICE is not set +# CONFIG_OPROFILE is not set +# CONFIG_OPROFILE_EVENT_MULTIPLEX is not set +# CONFIG_OPT3001 is not set +CONFIG_OPTIMIZE_INLINING=y +# CONFIG_ORANGEFS_FS is not set +# CONFIG_ORION_WATCHDOG is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_OSNOISE_TRACER is not set +CONFIG_OVERLAY_FS=y +# CONFIG_OVERLAY_FS_INDEX is not set +# CONFIG_OVERLAY_FS_METACOPY is not set +CONFIG_OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW=y +# CONFIG_OVERLAY_FS_REDIRECT_DIR is not set +CONFIG_OVERLAY_FS_XINO_AUTO=y +# CONFIG_OWL_LOADER is not set +# CONFIG_P54_COMMON is not set +# CONFIG_PA12203001 is not set +CONFIG_PACKET=y +# CONFIG_PACKET_DIAG is not set +# CONFIG_PACKING is not set +# CONFIG_PAGE_EXTENSION is not set +# CONFIG_PAGE_OWNER is not set +# CONFIG_PAGE_POISONING is not set +# CONFIG_PAGE_POOL is not set +# CONFIG_PAGE_POOL_STATS is not set +# CONFIG_PAGE_REPORTING is not set +# CONFIG_PAGE_SIZE_16KB is not set +# CONFIG_PAGE_SIZE_32KB is not set +CONFIG_PAGE_SIZE_4KB=y +# CONFIG_PAGE_SIZE_64KB is not set +# CONFIG_PAGE_SIZE_8KB is not set +# CONFIG_PAGE_TABLE_CHECK is not set +# CONFIG_PALMAS_GPADC is not set +# CONFIG_PANASONIC_LAPTOP is not set +# CONFIG_PANEL is not set +CONFIG_PANIC_ON_OOPS=y +CONFIG_PANIC_ON_OOPS_VALUE=1 +CONFIG_PANIC_TIMEOUT=1 +# CONFIG_PANTHERLORD_FF is not set +# CONFIG_PARAVIRT is not set +# CONFIG_PARAVIRT_TIME_ACCOUNTING is not set +# CONFIG_PARPORT is not set +# CONFIG_PARPORT_1284 is not set +# CONFIG_PARPORT_AX88796 is not set +# CONFIG_PARPORT_GSC is not set +# CONFIG_PARPORT_PC is not set +CONFIG_PARTITION_ADVANCED=y +# CONFIG_PATA_ALI is not set +# CONFIG_PATA_AMD is not set +# CONFIG_PATA_ARASAN_CF is not set +# CONFIG_PATA_ARTOP is not set +# CONFIG_PATA_ATIIXP is not set +# CONFIG_PATA_ATP867X is not set +# CONFIG_PATA_CMD640_PCI is not set +# CONFIG_PATA_CMD64X is not set +# CONFIG_PATA_CS5520 is not set +# CONFIG_PATA_CS5530 is not set +# CONFIG_PATA_CS5535 is not set +# CONFIG_PATA_CS5536 is not set +# CONFIG_PATA_CYPRESS is not set +# CONFIG_PATA_EFAR is not set +# CONFIG_PATA_HPT366 is not set +# CONFIG_PATA_HPT37X is not set +# CONFIG_PATA_HPT3X2N is not set +# CONFIG_PATA_HPT3X3 is not set +# CONFIG_PATA_IMX is not set +# CONFIG_PATA_ISAPNP is not set +# CONFIG_PATA_IT8213 is not set +# CONFIG_PATA_IT821X is not set +# CONFIG_PATA_JMICRON is not set +# CONFIG_PATA_LEGACY is not set +# CONFIG_PATA_MARVELL is not set +# CONFIG_PATA_MPIIX is not set +# CONFIG_PATA_NETCELL is not set +# CONFIG_PATA_NINJA32 is not set +# CONFIG_PATA_NS87410 is not set +# CONFIG_PATA_NS87415 is not set +# CONFIG_PATA_OCTEON_CF is not set +# CONFIG_PATA_OF_PLATFORM is not set +# CONFIG_PATA_OLDPIIX is not set +# CONFIG_PATA_OPTI is not set +# CONFIG_PATA_OPTIDMA is not set +# CONFIG_PATA_PCMCIA is not set +# CONFIG_PATA_PDC2027X is not set +# CONFIG_PATA_PDC_OLD is not set +# CONFIG_PATA_PLATFORM is not set +# CONFIG_PATA_QDI is not set +# CONFIG_PATA_RADISYS is not set +# CONFIG_PATA_RDC is not set +# CONFIG_PATA_RZ1000 is not set +# CONFIG_PATA_SC1200 is not set +# CONFIG_PATA_SCH is not set +# CONFIG_PATA_SERVERWORKS is not set +# CONFIG_PATA_SIL680 is not set +# CONFIG_PATA_SIS is not set +# CONFIG_PATA_TOSHIBA is not set +# CONFIG_PATA_TRIFLEX is not set +# CONFIG_PATA_VIA is not set +# CONFIG_PATA_WINBOND is not set +# CONFIG_PATA_WINBOND_VLB is not set +# CONFIG_PC104 is not set +# CONFIG_PC300TOO is not set +# CONFIG_PCCARD is not set +# CONFIG_PCH_DMA is not set +# CONFIG_PCH_GBE is not set +# CONFIG_PCH_PHUB is not set +# CONFIG_PCI is not set +# CONFIG_PCI200SYN is not set +# CONFIG_PCIEAER is not set +# CONFIG_PCIEAER_INJECT is not set +# CONFIG_PCIEASPM is not set +# CONFIG_PCIEPORTBUS is not set +# CONFIG_PCIE_AL is not set +# CONFIG_PCIE_ALTERA is not set +# CONFIG_PCIE_ARMADA_8K is not set +CONFIG_PCIE_BUS_DEFAULT=y +# CONFIG_PCIE_BUS_PEER2PEER is not set +# CONFIG_PCIE_BUS_PERFORMANCE is not set +# CONFIG_PCIE_BUS_SAFE is not set +# CONFIG_PCIE_BUS_TUNE_OFF is not set +# CONFIG_PCIE_BW is not set +# CONFIG_PCIE_CADENCE_HOST is not set +# CONFIG_PCIE_CADENCE_PLAT_HOST is not set +# CONFIG_PCIE_DPC is not set +# CONFIG_PCIE_DW_PLAT is not set +# CONFIG_PCIE_DW_PLAT_HOST is not set +# CONFIG_PCIE_ECRC is not set +# CONFIG_PCIE_IPROC is not set +# CONFIG_PCIE_KIRIN is not set +# CONFIG_PCIE_LAYERSCAPE_GEN4 is not set +# CONFIG_PCIE_MEDIATEK_GEN3 is not set +# CONFIG_PCIE_MICROCHIP_HOST is not set +# CONFIG_PCIE_PTM is not set +# CONFIG_PCIE_XILINX is not set +# CONFIG_PCIPCWATCHDOG is not set +# CONFIG_PCI_ATMEL is not set +# CONFIG_PCI_CNB20LE_QUIRK is not set +# CONFIG_PCI_DEBUG is not set +# CONFIG_PCI_DISABLE_COMMON_QUIRKS is not set +# CONFIG_PCI_ENDPOINT is not set +# CONFIG_PCI_ENDPOINT_TEST is not set +# CONFIG_PCI_FTPCI100 is not set +# CONFIG_PCI_HERMES is not set +# CONFIG_PCI_HISI is not set +# CONFIG_PCI_HOST_GENERIC is not set +# CONFIG_PCI_HOST_THUNDER_ECAM is not set +# CONFIG_PCI_HOST_THUNDER_PEM is not set +# CONFIG_PCI_IOV is not set +# CONFIG_PCI_J721E_HOST is not set +# CONFIG_PCI_LAYERSCAPE is not set +# CONFIG_PCI_MESON is not set +# CONFIG_PCI_MSI is not set +# CONFIG_PCI_PASID is not set +# CONFIG_PCI_PF_STUB is not set +# CONFIG_PCI_PRI is not set +CONFIG_PCI_QUIRKS=y +# CONFIG_PCI_REALLOC_ENABLE_AUTO is not set +# CONFIG_PCI_STUB is not set +# CONFIG_PCI_SW_SWITCHTEC is not set +CONFIG_PCI_SYSCALL=y +# CONFIG_PCI_V3_SEMI is not set +# CONFIG_PCI_XGENE is not set +# CONFIG_PCMCIA is not set +# CONFIG_PCMCIA_3C574 is not set +# CONFIG_PCMCIA_3C589 is not set +# CONFIG_PCMCIA_AHA152X is not set +# CONFIG_PCMCIA_ATMEL is not set +# CONFIG_PCMCIA_AXNET is not set +# CONFIG_PCMCIA_DEBUG is not set +# CONFIG_PCMCIA_FDOMAIN is not set +# CONFIG_PCMCIA_FMVJ18X is not set +# CONFIG_PCMCIA_HERMES is not set +# CONFIG_PCMCIA_LOAD_CIS is not set +# CONFIG_PCMCIA_NINJA_SCSI is not set +# CONFIG_PCMCIA_NMCLAN is not set +# CONFIG_PCMCIA_PCNET is not set +# CONFIG_PCMCIA_QLOGIC is not set +# CONFIG_PCMCIA_RAYCS is not set +# CONFIG_PCMCIA_SMC91C92 is not set +# CONFIG_PCMCIA_SPECTRUM is not set +# CONFIG_PCMCIA_SYM53C500 is not set +# CONFIG_PCMCIA_WL3501 is not set +# CONFIG_PCMCIA_XIRC2PS is not set +# CONFIG_PCMCIA_XIRCOM is not set +# CONFIG_PCNET32 is not set +# CONFIG_PCPU_DEV_REFCNT is not set +# CONFIG_PCSPKR_PLATFORM is not set +# CONFIG_PCS_MTK_USXGMII is not set +# CONFIG_PCS_XPCS is not set +# CONFIG_PD6729 is not set +# CONFIG_PDA_POWER is not set +# CONFIG_PDC_ADMA is not set +# CONFIG_PECI is not set +# CONFIG_PERCPU_STATS is not set +# CONFIG_PERCPU_TEST is not set +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_EVENTS_AMD_POWER is not set +# CONFIG_PERSISTENT_KEYRINGS is not set +# CONFIG_PHANTOM is not set +# CONFIG_PHONET is not set +# CONFIG_PHYLIB is not set +# CONFIG_PHYLIB_LEDS is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +# CONFIG_PHY_BRCM_USB is not set +# CONFIG_PHY_CADENCE_DP is not set +# CONFIG_PHY_CADENCE_DPHY is not set +# CONFIG_PHY_CADENCE_DPHY_RX is not set +# CONFIG_PHY_CADENCE_SALVO is not set +# CONFIG_PHY_CADENCE_SIERRA is not set +# CONFIG_PHY_CADENCE_TORRENT is not set +# CONFIG_PHY_CAN_TRANSCEIVER is not set +# CONFIG_PHY_CPCAP_USB is not set +# CONFIG_PHY_EXYNOS_DP_VIDEO is not set +# CONFIG_PHY_EXYNOS_MIPI_VIDEO is not set +# CONFIG_PHY_FSL_IMX8MQ_USB is not set +# CONFIG_PHY_INGENIC_USB is not set +# CONFIG_PHY_INTEL_KEEMBAY_EMMC is not set +# CONFIG_PHY_LAN966X_SERDES is not set +# CONFIG_PHY_MAPPHONE_MDM6600 is not set +# CONFIG_PHY_MIXEL_MIPI_DPHY is not set +# CONFIG_PHY_MTK_HDMI is not set +# CONFIG_PHY_MTK_MIPI_DSI is not set +# CONFIG_PHY_MTK_XFI_TPHY is not set +# CONFIG_PHY_MVEBU_CP110_UTMI is not set +# CONFIG_PHY_OCELOT_SERDES is not set +# CONFIG_PHY_PISTACHIO_USB is not set +# CONFIG_PHY_PXA_28NM_HSIC is not set +# CONFIG_PHY_PXA_28NM_USB2 is not set +# CONFIG_PHY_QCOM_DWC3 is not set +# CONFIG_PHY_QCOM_USB_HS is not set +# CONFIG_PHY_QCOM_USB_HSIC is not set +# CONFIG_PHY_SAMSUNG_USB2 is not set +# CONFIG_PHY_TUSB1210 is not set +# CONFIG_PHY_XGENE is not set +# CONFIG_PI433 is not set +# CONFIG_PID_IN_CONTEXTIDR is not set +# CONFIG_PID_NS is not set +CONFIG_PINCONF=y +# CONFIG_PINCTRL is not set +# CONFIG_PINCTRL_AMD is not set +# CONFIG_PINCTRL_AXP209 is not set +# CONFIG_PINCTRL_BCM2712 is not set +# CONFIG_PINCTRL_CEDARFORK is not set +# CONFIG_PINCTRL_CY8C95X0 is not set +# CONFIG_PINCTRL_EXYNOS is not set +# CONFIG_PINCTRL_EXYNOS5440 is not set +# CONFIG_PINCTRL_ICELAKE is not set +# CONFIG_PINCTRL_INGENIC is not set +# CONFIG_PINCTRL_LPASS_LPI is not set +# CONFIG_PINCTRL_MCP23S08 is not set +# CONFIG_PINCTRL_MDM9607 is not set +# CONFIG_PINCTRL_MICROCHIP_SGPIO is not set +# CONFIG_PINCTRL_MSM8953 is not set +# CONFIG_PINCTRL_MSM8X74 is not set +# CONFIG_PINCTRL_MT6779 is not set +# CONFIG_PINCTRL_MT8167 is not set +# CONFIG_PINCTRL_MT8192 is not set +# CONFIG_PINCTRL_MT8195 is not set +# CONFIG_PINCTRL_MT8365 is not set +# CONFIG_PINCTRL_MTK_V2 is not set +# CONFIG_PINCTRL_OCELOT is not set +# CONFIG_PINCTRL_PISTACHIO is not set +# CONFIG_PINCTRL_RP1 is not set +# CONFIG_PINCTRL_SC7280 is not set +# CONFIG_PINCTRL_SC8180X is not set +# CONFIG_PINCTRL_SDX55 is not set +CONFIG_PINCTRL_SINGLE=y +# CONFIG_PINCTRL_SM6115 is not set +# CONFIG_PINCTRL_SM6125 is not set +# CONFIG_PINCTRL_SM8350 is not set +# CONFIG_PINCTRL_STMFX is not set +# CONFIG_PINCTRL_SX150X is not set +# CONFIG_PING is not set +CONFIG_PINMUX=y +# CONFIG_PKCS7_MESSAGE_PARSER is not set +# CONFIG_PL310_ERRATA_588369 is not set +# CONFIG_PL310_ERRATA_727915 is not set +# CONFIG_PL310_ERRATA_753970 is not set +# CONFIG_PL310_ERRATA_769419 is not set +# CONFIG_PL320_MBOX is not set +# CONFIG_PL330_DMA is not set +# CONFIG_PLATFORM_MHU is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_PLIP is not set +# CONFIG_PLX_DMA is not set +# CONFIG_PLX_HERMES is not set +# CONFIG_PM is not set +# CONFIG_PMBUS is not set +# CONFIG_PMC_MSP is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_PMS7003 is not set +# CONFIG_PM_AUTOSLEEP is not set +# CONFIG_PM_DEBUG is not set +# CONFIG_PM_DEVFREQ is not set +# CONFIG_PM_USERSPACE_AUTOSLEEP is not set +# CONFIG_PM_WAKELOCKS is not set +# CONFIG_POSIX_MQUEUE is not set +CONFIG_POSIX_TIMERS=y +# CONFIG_POWERCAP is not set +# CONFIG_POWER_AVS is not set +# CONFIG_POWER_RESET is not set +# CONFIG_POWER_RESET_BRCMKONA is not set +# CONFIG_POWER_RESET_BRCMSTB is not set +# CONFIG_POWER_RESET_GPIO is not set +# CONFIG_POWER_RESET_GPIO_RESTART is not set +# CONFIG_POWER_RESET_LINKSTATION is not set +# CONFIG_POWER_RESET_LTC2952 is not set +# CONFIG_POWER_RESET_PIIX4_POWEROFF is not set +# CONFIG_POWER_RESET_QNAP is not set +# CONFIG_POWER_RESET_REGULATOR is not set +# CONFIG_POWER_RESET_RESTART is not set +# CONFIG_POWER_RESET_SYSCON is not set +# CONFIG_POWER_RESET_SYSCON_POWEROFF is not set +# CONFIG_POWER_RESET_VERSATILE is not set +# CONFIG_POWER_RESET_XGENE is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_POWER_SUPPLY_HWMON is not set +# CONFIG_PPC4xx_GPIO is not set +# CONFIG_PPC_16K_PAGES is not set +# CONFIG_PPC_256K_PAGES is not set +CONFIG_PPC_4K_PAGES=y +# CONFIG_PPC_64K_PAGES is not set +# CONFIG_PPC_DISABLE_WERROR is not set +# CONFIG_PPC_EMULATED_STATS is not set +# CONFIG_PPC_EPAPR_HV_BYTECHAN is not set +# CONFIG_PPC_QUEUED_SPINLOCKS is not set +# CONFIG_PPP is not set +# CONFIG_PPPOATM is not set +# CONFIG_PPPOE is not set +# CONFIG_PPPOL2TP is not set +# CONFIG_PPP_ASYNC is not set +# CONFIG_PPP_BSDCOMP is not set +# CONFIG_PPP_DEFLATE is not set +CONFIG_PPP_FILTER=y +# CONFIG_PPP_MPPE is not set +CONFIG_PPP_MULTILINK=y +# CONFIG_PPP_SYNC_TTY is not set +# CONFIG_PPS is not set +# CONFIG_PPS_CLIENT_GPIO is not set +# CONFIG_PPS_CLIENT_KTIMER is not set +# CONFIG_PPS_CLIENT_LDISC is not set +# CONFIG_PPS_CLIENT_PARPORT is not set +# CONFIG_PPS_DEBUG is not set +# CONFIG_PPTP is not set +# CONFIG_PREEMPT is not set +# CONFIG_PREEMPTIRQ_DELAY_TEST is not set +# CONFIG_PREEMPTIRQ_EVENTS is not set +# CONFIG_PREEMPT_DYNAMIC is not set +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PRESTERA is not set +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_PRIME_NUMBERS is not set +CONFIG_PRINTK=y +# CONFIG_PRINTK_CALLER is not set +# CONFIG_PRINTK_INDEX is not set +CONFIG_PRINTK_NMI=y +CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT=13 +# CONFIG_PRINTK_TIME is not set +CONFIG_PRINT_STACK_DEPTH=64 +# CONFIG_PRISM2_USB is not set +# CONFIG_PRISM54 is not set +# CONFIG_PROC_CHILDREN is not set +CONFIG_PROC_FS=y +# CONFIG_PROC_KCORE is not set +# CONFIG_PROC_PAGE_MONITOR is not set +# CONFIG_PROC_STRIPPED is not set +CONFIG_PROC_SYSCTL=y +# CONFIG_PROC_VMCORE_DEVICE_DUMP is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILING is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_PROVE_RAW_LOCK_NESTING is not set +# CONFIG_PROVE_RCU is not set +# CONFIG_PROVE_RCU_LIST is not set +# CONFIG_PROVE_RCU_REPEATEDLY is not set +# CONFIG_PSAMPLE is not set +# CONFIG_PSB6970_PHY is not set +# CONFIG_PSE_CONTROLLER is not set +# CONFIG_PSI is not set +# CONFIG_PSTORE is not set +# CONFIG_PSTORE_842_COMPRESS is not set +# CONFIG_PSTORE_BLK is not set +# CONFIG_PSTORE_COMPRESS is not set +# CONFIG_PSTORE_CONSOLE is not set +CONFIG_PSTORE_DEFAULT_KMSG_BYTES=10240 +# CONFIG_PSTORE_DEFLATE_COMPRESS is not set +# CONFIG_PSTORE_DEFLATE_COMPRESS_DEFAULT is not set +# CONFIG_PSTORE_FTRACE is not set +# CONFIG_PSTORE_LZ4HC_COMPRESS is not set +# CONFIG_PSTORE_LZ4_COMPRESS is not set +# CONFIG_PSTORE_LZO_COMPRESS is not set +# CONFIG_PSTORE_PMSG is not set +# CONFIG_PSTORE_RAM is not set +# CONFIG_PSTORE_ZSTD_COMPRESS is not set +# CONFIG_PTDUMP_DEBUGFS is not set +# CONFIG_PTP_1588_CLOCK is not set +# CONFIG_PTP_1588_CLOCK_IDT82P33 is not set +# CONFIG_PTP_1588_CLOCK_IDTCM is not set +# CONFIG_PTP_1588_CLOCK_IXP46X is not set +# CONFIG_PTP_1588_CLOCK_KVM is not set +# CONFIG_PTP_1588_CLOCK_OCP is not set +# CONFIG_PTP_1588_CLOCK_PCH is not set +# CONFIG_PTP_1588_CLOCK_VMW is not set +# CONFIG_PUBLIC_KEY_ALGO_RSA is not set +# CONFIG_PVPANIC is not set +# CONFIG_PWM is not set +# CONFIG_PWM_ATMEL_TCB is not set +# CONFIG_PWM_CLK is not set +# CONFIG_PWM_DEBUG is not set +# CONFIG_PWM_DWC is not set +# CONFIG_PWM_FSL_FTM is not set +# CONFIG_PWM_IMG is not set +# CONFIG_PWM_JZ4740 is not set +# CONFIG_PWM_MEDIATEK is not set +# CONFIG_PWM_PCA9685 is not set +# CONFIG_PWM_RASPBERRYPI_POE is not set +# CONFIG_PWM_RP1 is not set +# CONFIG_PWM_XILINX is not set +CONFIG_PWRSEQ_EMMC=y +# CONFIG_PWRSEQ_SD8787 is not set +CONFIG_PWRSEQ_SIMPLE=y +# CONFIG_QCA7000 is not set +# CONFIG_QCA7000_SPI is not set +# CONFIG_QCA7000_UART is not set +# CONFIG_QCA83XX_PHY is not set +# CONFIG_QCA807X_PHY is not set +# CONFIG_QCA808X_PHY is not set +# CONFIG_QCOM_A7PLL is not set +# CONFIG_QCOM_BAM_DMUX is not set +# CONFIG_QCOM_EMAC is not set +# CONFIG_QCOM_FALKOR_ERRATUM_1003 is not set +# CONFIG_QCOM_FALKOR_ERRATUM_1009 is not set +# CONFIG_QCOM_FALKOR_ERRATUM_E1041 is not set +# CONFIG_QCOM_GPI_DMA is not set +# CONFIG_QCOM_HIDMA is not set +# CONFIG_QCOM_HIDMA_MGMT is not set +# CONFIG_QCOM_LMH is not set +# CONFIG_QCOM_QDF2400_ERRATUM_0065 is not set +# CONFIG_QCOM_SPMI_ADC5 is not set +# CONFIG_QCOM_SPMI_ADC_TM5 is not set +# CONFIG_QCOM_SPMI_IADC is not set +# CONFIG_QCOM_SPMI_TEMP_ALARM is not set +# CONFIG_QCOM_SPMI_VADC is not set +# CONFIG_QCOM_SSC_BLOCK_BUS is not set +# CONFIG_QED is not set +# CONFIG_QFMT_V1 is not set +# CONFIG_QLA3XXX is not set +# CONFIG_QLCNIC is not set +# CONFIG_QLGE is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_QORIQ_CPUFREQ is not set +# CONFIG_QORIQ_THERMAL is not set +# CONFIG_QRTR is not set +# CONFIG_QRTR_MHI is not set +# CONFIG_QRTR_TUN is not set +# CONFIG_QSEMI_PHY is not set +# CONFIG_QUEUED_LOCK_STAT is not set +# CONFIG_QUICC_ENGINE is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_QUOTA_DEBUG is not set +# CONFIG_QUOTA_NETLINK_INTERFACE is not set +# CONFIG_R3964 is not set +# CONFIG_R6040 is not set +# CONFIG_R8169 is not set +# CONFIG_R8188EU is not set +# CONFIG_R8712U is not set +# CONFIG_R8723AU is not set +# CONFIG_RADIO_ADAPTERS is not set +# CONFIG_RADIO_AZTECH is not set +# CONFIG_RADIO_CADET is not set +# CONFIG_RADIO_GEMTEK is not set +# CONFIG_RADIO_MAXIRADIO is not set +# CONFIG_RADIO_RTRACK is not set +# CONFIG_RADIO_RTRACK2 is not set +# CONFIG_RADIO_SF16FMI is not set +# CONFIG_RADIO_SF16FMR2 is not set +# CONFIG_RADIO_TERRATEC is not set +# CONFIG_RADIO_TRUST is not set +# CONFIG_RADIO_TYPHOON is not set +# CONFIG_RADIO_ZOLTRIX is not set +# CONFIG_RAID6_PQ_BENCHMARK is not set +# CONFIG_RAID_ATTRS is not set +# CONFIG_RALINK is not set +# CONFIG_RANDOM32_SELFTEST is not set +# CONFIG_RANDOMIZE_BASE is not set +CONFIG_RANDOMIZE_KSTACK_OFFSET=y +# CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT is not set +CONFIG_RANDOM_TRUST_BOOTLOADER=y +CONFIG_RANDOM_TRUST_CPU=y +# CONFIG_RANDSTRUCT_NONE is not set +# CONFIG_RAPIDIO is not set +# CONFIG_RAS is not set +# CONFIG_RASPBERRYPI_GPIOMEM is not set +# CONFIG_RBTREE_TEST is not set +# CONFIG_RCU_BOOST is not set +CONFIG_RCU_CPU_STALL_TIMEOUT=60 +# CONFIG_RCU_EQS_DEBUG is not set +# CONFIG_RCU_EXPEDITE_BOOT is not set +# CONFIG_RCU_EXPERT is not set +CONFIG_RCU_EXP_CPU_STALL_TIMEOUT=0 +CONFIG_RCU_KTHREAD_PRIO=0 +CONFIG_RCU_NEED_SEGCBLIST=y +# CONFIG_RCU_PERF_TEST is not set +# CONFIG_RCU_REF_SCALE_TEST is not set +# CONFIG_RCU_SCALE_TEST is not set +CONFIG_RCU_STALL_COMMON=y +# CONFIG_RCU_STRICT_GRACE_PERIOD is not set +# CONFIG_RCU_TORTURE_TEST is not set +CONFIG_RCU_TORTURE_TEST_SLOW_INIT_DELAY=3 +# CONFIG_RCU_TRACE is not set +# CONFIG_RC_ATI_REMOTE is not set +# CONFIG_RC_CORE is not set +# CONFIG_RC_DECODERS is not set +# CONFIG_RC_LOOPBACK is not set +# CONFIG_RC_MAP is not set +# CONFIG_RC_XBOX_DVD is not set +# CONFIG_RDS is not set +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_GZIP is not set +# CONFIG_RD_LZ4 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_LZO is not set +# CONFIG_RD_XZ is not set +# CONFIG_RD_ZSTD is not set +# CONFIG_READABLE_ASM is not set +# CONFIG_READ_ONLY_THP_FOR_FS is not set +# CONFIG_REALTEK_PHY is not set +# CONFIG_REDWOOD is not set +# CONFIG_REED_SOLOMON is not set +# CONFIG_REED_SOLOMON_DEC8 is not set +# CONFIG_REED_SOLOMON_ENC8 is not set +# CONFIG_REED_SOLOMON_TEST is not set +# CONFIG_REGMAP is not set +# CONFIG_REGMAP_I2C is not set +# CONFIG_REGMAP_MMIO is not set +# CONFIG_REGMAP_SPI is not set +# CONFIG_REGULATOR is not set +# CONFIG_REGULATOR_88PG86X is not set +# CONFIG_REGULATOR_ACT8865 is not set +# CONFIG_REGULATOR_AD5398 is not set +# CONFIG_REGULATOR_ANATOP is not set +# CONFIG_REGULATOR_DA9121 is not set +# CONFIG_REGULATOR_DA9210 is not set +# CONFIG_REGULATOR_DA9211 is not set +# CONFIG_REGULATOR_DEBUG is not set +# CONFIG_REGULATOR_FAN53555 is not set +# CONFIG_REGULATOR_FAN53880 is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_GPIO is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_ISL9305 is not set +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +# CONFIG_REGULATOR_LP872X is not set +# CONFIG_REGULATOR_LP8755 is not set +# CONFIG_REGULATOR_LTC3589 is not set +# CONFIG_REGULATOR_LTC3676 is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX20086 is not set +# CONFIG_REGULATOR_MAX77620 is not set +# CONFIG_REGULATOR_MAX77826 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8893 is not set +# CONFIG_REGULATOR_MAX8952 is not set +# CONFIG_REGULATOR_MAX8973 is not set +# CONFIG_REGULATOR_MCP16502 is not set +# CONFIG_REGULATOR_MP5416 is not set +# CONFIG_REGULATOR_MP8859 is not set +# CONFIG_REGULATOR_MP886X is not set +# CONFIG_REGULATOR_MPQ7920 is not set +# CONFIG_REGULATOR_MT6311 is not set +# CONFIG_REGULATOR_MT6315 is not set +# CONFIG_REGULATOR_MT6359 is not set +# CONFIG_REGULATOR_PCA9450 is not set +# CONFIG_REGULATOR_PF8X00 is not set +# CONFIG_REGULATOR_PFUZE100 is not set +# CONFIG_REGULATOR_PV88060 is not set +# CONFIG_REGULATOR_PV88080 is not set +# CONFIG_REGULATOR_PV88090 is not set +# CONFIG_REGULATOR_PWM is not set +# CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY is not set +# CONFIG_REGULATOR_RT4801 is not set +# CONFIG_REGULATOR_RT5190A is not set +# CONFIG_REGULATOR_RT5759 is not set +# CONFIG_REGULATOR_RT6160 is not set +# CONFIG_REGULATOR_RT6245 is not set +# CONFIG_REGULATOR_RTMV20 is not set +# CONFIG_REGULATOR_RTQ2134 is not set +# CONFIG_REGULATOR_RTQ6752 is not set +# CONFIG_REGULATOR_SLG51000 is not set +# CONFIG_REGULATOR_SY8106A is not set +# CONFIG_REGULATOR_SY8824X is not set +# CONFIG_REGULATOR_SY8827N is not set +# CONFIG_REGULATOR_TI_ABB is not set +# CONFIG_REGULATOR_TPS51632 is not set +# CONFIG_REGULATOR_TPS62360 is not set +# CONFIG_REGULATOR_TPS6286X is not set +# CONFIG_REGULATOR_TPS65023 is not set +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_REGULATOR_TPS65132 is not set +# CONFIG_REGULATOR_TPS6524X is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_VCTRL is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_FS_POSIX_ACL is not set +# CONFIG_REISERFS_FS_SECURITY is not set +CONFIG_REISERFS_FS_XATTR=y +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_RELAY is not set +# CONFIG_RELOCATABLE is not set +# CONFIG_REMOTEPROC is not set +# CONFIG_RENESAS_PHY is not set +# CONFIG_RESET_ATH79 is not set +# CONFIG_RESET_BERLIN is not set +# CONFIG_RESET_BRCMSTB is not set +# CONFIG_RESET_BRCMSTB_RESCAL is not set +# CONFIG_RESET_CONTROLLER is not set +# CONFIG_RESET_IMX7 is not set +# CONFIG_RESET_INTEL_GW is not set +# CONFIG_RESET_LANTIQ is not set +# CONFIG_RESET_LPC18XX is not set +# CONFIG_RESET_MESON is not set +# CONFIG_RESET_PISTACHIO is not set +# CONFIG_RESET_SIMPLE is not set +# CONFIG_RESET_SOCFPGA is not set +# CONFIG_RESET_STM32 is not set +# CONFIG_RESET_SUNXI is not set +# CONFIG_RESET_TEGRA_BPMP is not set +# CONFIG_RESET_TI_SYSCON is not set +# CONFIG_RESET_TI_TPS380X is not set +# CONFIG_RESET_ZYNQ is not set +# CONFIG_RFD77402 is not set +# CONFIG_RFD_FTL is not set +CONFIG_RFKILL=y +# CONFIG_RFKILL_FULL is not set +# CONFIG_RFKILL_GPIO is not set +# CONFIG_RFKILL_INPUT is not set +# CONFIG_RFKILL_LEDS is not set +# CONFIG_RFKILL_REGULATOR is not set +# CONFIG_RICHTEK_RTQ6056 is not set +# CONFIG_RING_BUFFER_BENCHMARK is not set +# CONFIG_RING_BUFFER_STARTUP_TEST is not set +# CONFIG_RING_BUFFER_VALIDATE_TIME_DELTAS is not set +# CONFIG_RMI4_CORE is not set +# CONFIG_RMNET is not set +# CONFIG_ROCKCHIP_PHY is not set +# CONFIG_ROCKER is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_ROSE is not set +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPMSG_QCOM_GLINK_RPM is not set +# CONFIG_RPMSG_VIRTIO is not set +# CONFIG_RPMSG_WWAN_CTRL is not set +# CONFIG_RPR0521 is not set +# CONFIG_RSEQ is not set +# CONFIG_RT2X00 is not set +# CONFIG_RTC_CLASS is not set +# CONFIG_RTC_DEBUG is not set +# CONFIG_RTC_DRV_ABB5ZES3 is not set +# CONFIG_RTC_DRV_ABEOZ9 is not set +# CONFIG_RTC_DRV_ABX80X is not set +# CONFIG_RTC_DRV_ARMADA38X is not set +# CONFIG_RTC_DRV_AU1XXX is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_CADENCE is not set +CONFIG_RTC_DRV_CMOS=y +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1302 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1307_CENTURY is not set +# CONFIG_RTC_DRV_DS1307_HWMON is not set +# CONFIG_RTC_DRV_DS1343 is not set +# CONFIG_RTC_DRV_DS1347 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS1685_FAMILY is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_DS2404 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_EP93XX is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_FTRTC010 is not set +# CONFIG_RTC_DRV_GENERIC is not set +# CONFIG_RTC_DRV_GOLDFISH is not set +# CONFIG_RTC_DRV_HID_SENSOR_TIME is not set +# CONFIG_RTC_DRV_HYM8563 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_ISL12026 is not set +# CONFIG_RTC_DRV_ISL12057 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_JZ4740 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_MAX6916 is not set +# CONFIG_RTC_DRV_MAX77686 is not set +# CONFIG_RTC_DRV_MCP795 is not set +# CONFIG_RTC_DRV_MOXART is not set +# CONFIG_RTC_DRV_MPC5121 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_MT2712 is not set +# CONFIG_RTC_DRV_NCT3018Y is not set +# CONFIG_RTC_DRV_OMAP is not set +# CONFIG_RTC_DRV_PCF2123 is not set +# CONFIG_RTC_DRV_PCF2127 is not set +# CONFIG_RTC_DRV_PCF85063 is not set +# CONFIG_RTC_DRV_PCF8523 is not set +# CONFIG_RTC_DRV_PCF85363 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_PL030 is not set +# CONFIG_RTC_DRV_PL031 is not set +# CONFIG_RTC_DRV_PS3 is not set +# CONFIG_RTC_DRV_PT7C4338 is not set +# CONFIG_RTC_DRV_R7301 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_RTC7301 is not set +# CONFIG_RTC_DRV_RV3028 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set +# CONFIG_RTC_DRV_RV3032 is not set +# CONFIG_RTC_DRV_RV8803 is not set +# CONFIG_RTC_DRV_RX4581 is not set +# CONFIG_RTC_DRV_RX6110 is not set +# CONFIG_RTC_DRV_RX8010 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_SD3078 is not set +# CONFIG_RTC_DRV_SNVS is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_SUN6I is not set +# CONFIG_RTC_DRV_TEGRA is not set +# CONFIG_RTC_DRV_TEST is not set +# CONFIG_RTC_DRV_V3020 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_XGENE is not set +# CONFIG_RTC_DRV_ZYNQMP is not set +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_LIB=y +# CONFIG_RTC_NVMEM is not set +CONFIG_RTC_SYSTOHC=y +CONFIG_RTC_SYSTOHC_DEVICE="rtc0" +# CONFIG_RTL8180 is not set +# CONFIG_RTL8187 is not set +# CONFIG_RTL8192E is not set +# CONFIG_RTL8192U is not set +# CONFIG_RTL8306_PHY is not set +# CONFIG_RTL8366RB_PHY is not set +# CONFIG_RTL8366S_PHY is not set +# CONFIG_RTL8366_SMI is not set +# CONFIG_RTL8366_SMI_DEBUG_FS is not set +# CONFIG_RTL8367B_PHY is not set +# CONFIG_RTL8367_PHY is not set +# CONFIG_RTLLIB is not set +# CONFIG_RTL_CARDS is not set +# CONFIG_RTS5208 is not set +CONFIG_RT_MUTEXES=y +# CONFIG_RUNTIME_DEBUG is not set +CONFIG_RUNTIME_TESTING_MENU=y +# CONFIG_RV is not set +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_RXKAD=y +# CONFIG_S2IO is not set +# CONFIG_SAMPLES is not set +# CONFIG_SAMSUNG_LAPTOP is not set +# CONFIG_SATA_ACARD_AHCI is not set +# CONFIG_SATA_AHCI is not set +# CONFIG_SATA_AHCI_PLATFORM is not set +# CONFIG_SATA_DWC is not set +# CONFIG_SATA_DWC_OLD_DMA is not set +# CONFIG_SATA_FSL is not set +# CONFIG_SATA_HIGHBANK is not set +# CONFIG_SATA_HOST is not set +# CONFIG_SATA_INIC162X is not set +CONFIG_SATA_MOBILE_LPM_POLICY=0 +# CONFIG_SATA_MV is not set +# CONFIG_SATA_NV is not set +# CONFIG_SATA_PMP is not set +# CONFIG_SATA_PROMISE is not set +# CONFIG_SATA_QSTOR is not set +# CONFIG_SATA_RCAR is not set +# CONFIG_SATA_SIL is not set +# CONFIG_SATA_SIL24 is not set +# CONFIG_SATA_SIS is not set +# CONFIG_SATA_SVW is not set +# CONFIG_SATA_SX4 is not set +# CONFIG_SATA_ULI is not set +# CONFIG_SATA_VIA is not set +# CONFIG_SATA_VITESSE is not set +# CONFIG_SBC_FITPC2_WATCHDOG is not set +CONFIG_SBITMAP=y +# CONFIG_SC92031 is not set +# CONFIG_SCA3000 is not set +# CONFIG_SCA3300 is not set +# CONFIG_SCACHE_DEBUGFS is not set +# CONFIG_SCC is not set +# CONFIG_SCD30_CORE is not set +# CONFIG_SCD4X is not set +# CONFIG_SCF_TORTURE_TEST is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SCHED_CLUSTER is not set +# CONFIG_SCHED_DEBUG is not set +CONFIG_SCHED_HRTICK=y +# CONFIG_SCHED_MC is not set +CONFIG_SCHED_OMIT_FRAME_POINTER=y +# CONFIG_SCHED_SMT is not set +CONFIG_SCHED_STACK_END_CHECK=y +# CONFIG_SCHED_TRACER is not set +# CONFIG_SCR24X is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_3W_9XXX is not set +# CONFIG_SCSI_3W_SAS is not set +# CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_AACRAID is not set +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_ADVANSYS is not set +# CONFIG_SCSI_AHA152X is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AIC79XX is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_AIC94XX is not set +# CONFIG_SCSI_AM53C974 is not set +# CONFIG_SCSI_ARCMSR is not set +# CONFIG_SCSI_BFA_FC is not set +# CONFIG_SCSI_BNX2X_FCOE is not set +# CONFIG_SCSI_BNX2_ISCSI is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_CHELSIO_FCOE is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_CXGB3_ISCSI is not set +# CONFIG_SCSI_CXGB4_ISCSI is not set +# CONFIG_SCSI_DC395x is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_DMX3191D is not set +# CONFIG_SCSI_DPT_I2O is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_ESAS2R is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_FDOMAIN_PCI is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set +# CONFIG_SCSI_HISI_SAS is not set +# CONFIG_SCSI_HPSA is not set +# CONFIG_SCSI_HPTIOP is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_SCSI_INIA100 is not set +# CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_IPR is not set +# CONFIG_SCSI_IPS is not set +# CONFIG_SCSI_ISCI is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_LOGGING is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set +# CONFIG_SCSI_LPFC is not set +CONFIG_SCSI_MOD=y +# CONFIG_SCSI_MPI3MR is not set +# CONFIG_SCSI_MPT2SAS is not set +# CONFIG_SCSI_MPT3SAS is not set +# CONFIG_SCSI_MQ_DEFAULT is not set +# CONFIG_SCSI_MVSAS is not set +# CONFIG_SCSI_MVSAS_DEBUG is not set +# CONFIG_SCSI_MVUMI is not set +# CONFIG_SCSI_MYRB is not set +# CONFIG_SCSI_MYRS is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_SCSI_NSP32 is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PM8001 is not set +# CONFIG_SCSI_PMCRAID is not set +CONFIG_SCSI_PROC_FS=y +# CONFIG_SCSI_QLA_FC is not set +# CONFIG_SCSI_QLA_ISCSI is not set +# CONFIG_SCSI_QLOGIC_1280 is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_SAS_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +# CONFIG_SCSI_SMARTPQI is not set +# CONFIG_SCSI_SNIC is not set +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +# CONFIG_SCSI_STEX is not set +# CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_SYM53C8XX_2 is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_UFSHCD is not set +# CONFIG_SCSI_ULTRASTOR is not set +# CONFIG_SCSI_VIRTIO is not set +# CONFIG_SCSI_WD719X is not set +# CONFIG_SC_CAMCC_7180 is not set +# CONFIG_SC_DISPCC_7280 is not set +# CONFIG_SC_GCC_7280 is not set +# CONFIG_SC_GCC_8180X is not set +# CONFIG_SC_GPUCC_7280 is not set +# CONFIG_SC_GPUCC_8280XP is not set +# CONFIG_SC_VIDEOCC_7280 is not set +# CONFIG_SCx200_ACB is not set +# CONFIG_SDIO_UART is not set +# CONFIG_SDM_GPUCC_660 is not set +# CONFIG_SDM_MMCC_660 is not set +# CONFIG_SDR_MAX2175 is not set +# CONFIG_SDR_PLATFORM_DRIVERS is not set +# CONFIG_SDX_GCC_55 is not set +# CONFIG_SD_ADC_MODULATOR is not set +# CONFIG_SECCOMP is not set +# CONFIG_SECCOMP_CACHE_DEBUG is not set +CONFIG_SECTION_MISMATCH_WARN_ONLY=y +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +# CONFIG_SECURITY_APPARMOR is not set +CONFIG_SECURITY_DMESG_RESTRICT=y +# CONFIG_SECURITY_LANDLOCK is not set +# CONFIG_SECURITY_LOADPIN is not set +# CONFIG_SECURITY_LOCKDOWN_LSM is not set +# CONFIG_SECURITY_NETWORK_XFRM is not set +# CONFIG_SECURITY_PATH is not set +# CONFIG_SECURITY_SAFESETID is not set +# CONFIG_SECURITY_SELINUX_AVC_STATS is not set +# CONFIG_SECURITY_SELINUX_BOOTPARAM is not set +CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=0 +# CONFIG_SECURITY_SELINUX_DEVELOP is not set +# CONFIG_SECURITY_SELINUX_DISABLE is not set +# CONFIG_SECURITY_SMACK is not set +# CONFIG_SECURITY_TOMOYO is not set +# CONFIG_SECURITY_YAMA is not set +CONFIG_SELECT_MEMORY_MODEL=y +# CONFIG_SENSEAIR_SUNRISE_CO2 is not set +# CONFIG_SENSIRION_SGP30 is not set +# CONFIG_SENSIRION_SGP40 is not set +# CONFIG_SENSORS_ABITUGURU is not set +# CONFIG_SENSORS_ABITUGURU3 is not set +# CONFIG_SENSORS_ACPI_POWER is not set +# CONFIG_SENSORS_AD7314 is not set +# CONFIG_SENSORS_AD7414 is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADC128D818 is not set +# CONFIG_SENSORS_ADCXX is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM1177 is not set +# CONFIG_SENSORS_ADM1266 is not set +# CONFIG_SENSORS_ADM1275 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADS1015 is not set +# CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_ADS7871 is not set +# CONFIG_SENSORS_ADT7310 is not set +# CONFIG_SENSORS_ADT7410 is not set +# CONFIG_SENSORS_ADT7411 is not set +# CONFIG_SENSORS_ADT7462 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ADT7475 is not set +# CONFIG_SENSORS_AHT10 is not set +# CONFIG_SENSORS_AMC6821 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_SENSORS_APPLESMC is not set +# CONFIG_SENSORS_AQUACOMPUTER_D5NEXT is not set +# CONFIG_SENSORS_AS370 is not set +# CONFIG_SENSORS_ASB100 is not set +# CONFIG_SENSORS_ASC7621 is not set +# CONFIG_SENSORS_ASPEED is not set +# CONFIG_SENSORS_ATK0110 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_AXI_FAN_CONTROL is not set +# CONFIG_SENSORS_BEL_PFE is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BPA_RS600 is not set +# CONFIG_SENSORS_CORETEMP is not set +# CONFIG_SENSORS_CORSAIR_CPRO is not set +# CONFIG_SENSORS_CORSAIR_PSU is not set +# CONFIG_SENSORS_DELL_SMM is not set +# CONFIG_SENSORS_DELTA_AHE50DC_FAN is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_DPS920AB is not set +# CONFIG_SENSORS_DRIVETEMP is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_DS620 is not set +# CONFIG_SENSORS_EMC1403 is not set +# CONFIG_SENSORS_EMC2103 is not set +# CONFIG_SENSORS_EMC2305 is not set +# CONFIG_SENSORS_EMC6W201 is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_FAM15H_POWER is not set +# CONFIG_SENSORS_FSCHMD is not set +# CONFIG_SENSORS_FSP_3Y is not set +# CONFIG_SENSORS_FTSTEUTATES is not set +# CONFIG_SENSORS_G760A is not set +# CONFIG_SENSORS_G762 is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_GPIO_FAN is not set +# CONFIG_SENSORS_GSC is not set +# CONFIG_SENSORS_HDAPS is not set +# CONFIG_SENSORS_HIH6130 is not set +# CONFIG_SENSORS_HMC5843 is not set +# CONFIG_SENSORS_HMC5843_I2C is not set +# CONFIG_SENSORS_HMC5843_SPI is not set +# CONFIG_SENSORS_HTU21 is not set +# CONFIG_SENSORS_I5500 is not set +# CONFIG_SENSORS_I5K_AMB is not set +# CONFIG_SENSORS_IBM_CFFPS is not set +# CONFIG_SENSORS_IIO_HWMON is not set +# CONFIG_SENSORS_INA209 is not set +# CONFIG_SENSORS_INA238 is not set +# CONFIG_SENSORS_INA2XX is not set +# CONFIG_SENSORS_INA3221 is not set +# CONFIG_SENSORS_INSPUR_IPSPS is not set +# CONFIG_SENSORS_IR35221 is not set +# CONFIG_SENSORS_IR36021 is not set +# CONFIG_SENSORS_IR38064 is not set +# CONFIG_SENSORS_IRPS5401 is not set +# CONFIG_SENSORS_ISL29018 is not set +# CONFIG_SENSORS_ISL29028 is not set +# CONFIG_SENSORS_ISL68137 is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_JC42 is not set +# CONFIG_SENSORS_K10TEMP is not set +# CONFIG_SENSORS_K8TEMP is not set +# CONFIG_SENSORS_LINEAGE is not set +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_SENSORS_LIS3_I2C is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LM25066 is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM70 is not set +# CONFIG_SENSORS_LM73 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_LM95234 is not set +# CONFIG_SENSORS_LM95241 is not set +# CONFIG_SENSORS_LM95245 is not set +# CONFIG_SENSORS_LT7182S is not set +# CONFIG_SENSORS_LTC2945 is not set +# CONFIG_SENSORS_LTC2947_I2C is not set +# CONFIG_SENSORS_LTC2947_SPI is not set +# CONFIG_SENSORS_LTC2978 is not set +# CONFIG_SENSORS_LTC2990 is not set +# CONFIG_SENSORS_LTC2992 is not set +# CONFIG_SENSORS_LTC3815 is not set +# CONFIG_SENSORS_LTC4151 is not set +# CONFIG_SENSORS_LTC4215 is not set +# CONFIG_SENSORS_LTC4222 is not set +# CONFIG_SENSORS_LTC4245 is not set +# CONFIG_SENSORS_LTC4260 is not set +# CONFIG_SENSORS_LTC4261 is not set +# CONFIG_SENSORS_LTQ_CPUTEMP is not set +# CONFIG_SENSORS_MAX1111 is not set +# CONFIG_SENSORS_MAX127 is not set +# CONFIG_SENSORS_MAX15301 is not set +# CONFIG_SENSORS_MAX16064 is not set +# CONFIG_SENSORS_MAX16065 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX16601 is not set +# CONFIG_SENSORS_MAX1668 is not set +# CONFIG_SENSORS_MAX197 is not set +# CONFIG_SENSORS_MAX20730 is not set +# CONFIG_SENSORS_MAX20751 is not set +# CONFIG_SENSORS_MAX31722 is not set +# CONFIG_SENSORS_MAX31730 is not set +# CONFIG_SENSORS_MAX31760 is not set +# CONFIG_SENSORS_MAX31785 is not set +# CONFIG_SENSORS_MAX31790 is not set +# CONFIG_SENSORS_MAX34440 is not set +# CONFIG_SENSORS_MAX6620 is not set +# CONFIG_SENSORS_MAX6621 is not set +# CONFIG_SENSORS_MAX6639 is not set +# CONFIG_SENSORS_MAX6642 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_MAX6697 is not set +# CONFIG_SENSORS_MAX8688 is not set +# CONFIG_SENSORS_MCP3021 is not set +# CONFIG_SENSORS_MP2888 is not set +# CONFIG_SENSORS_MP2975 is not set +# CONFIG_SENSORS_MP5023 is not set +# CONFIG_SENSORS_MR75203 is not set +# CONFIG_SENSORS_NCT6683 is not set +# CONFIG_SENSORS_NCT6775 is not set +# CONFIG_SENSORS_NCT6775_I2C is not set +# CONFIG_SENSORS_NCT7802 is not set +# CONFIG_SENSORS_NCT7904 is not set +# CONFIG_SENSORS_NPCM7XX is not set +# CONFIG_SENSORS_NSA320 is not set +# CONFIG_SENSORS_NTC_THERMISTOR is not set +# CONFIG_SENSORS_NZXT_KRAKEN2 is not set +# CONFIG_SENSORS_NZXT_SMART2 is not set +# CONFIG_SENSORS_OCC_P8_I2C is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_PIM4328 is not set +# CONFIG_SENSORS_PLI1209BC is not set +# CONFIG_SENSORS_PM6764TR is not set +# CONFIG_SENSORS_PMBUS is not set +# CONFIG_SENSORS_POWR1220 is not set +# CONFIG_SENSORS_PWM_FAN is not set +# CONFIG_SENSORS_PXE1610 is not set +# CONFIG_SENSORS_Q54SJ108A2 is not set +# CONFIG_SENSORS_RM3100_I2C is not set +# CONFIG_SENSORS_RM3100_SPI is not set +# CONFIG_SENSORS_RP1_ADC is not set +# CONFIG_SENSORS_SBRMI is not set +# CONFIG_SENSORS_SBTSI is not set +# CONFIG_SENSORS_SCH5627 is not set +# CONFIG_SENSORS_SCH5636 is not set +# CONFIG_SENSORS_SCH56XX_COMMON is not set +# CONFIG_SENSORS_SHT15 is not set +# CONFIG_SENSORS_SHT21 is not set +# CONFIG_SENSORS_SHT3x is not set +# CONFIG_SENSORS_SHT4x is not set +# CONFIG_SENSORS_SHTC1 is not set +# CONFIG_SENSORS_SIS5595 is not set +# CONFIG_SENSORS_SMM665 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_STPDDC60 is not set +# CONFIG_SENSORS_STTS751 is not set +# CONFIG_SENSORS_TC654 is not set +# CONFIG_SENSORS_TC74 is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_TMP102 is not set +# CONFIG_SENSORS_TMP103 is not set +# CONFIG_SENSORS_TMP108 is not set +# CONFIG_SENSORS_TMP401 is not set +# CONFIG_SENSORS_TMP421 is not set +# CONFIG_SENSORS_TMP464 is not set +# CONFIG_SENSORS_TMP513 is not set +# CONFIG_SENSORS_TPS23861 is not set +# CONFIG_SENSORS_TPS40422 is not set +# CONFIG_SENSORS_TPS53679 is not set +# CONFIG_SENSORS_TPS546D24 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_TSL2563 is not set +# CONFIG_SENSORS_UCD9000 is not set +# CONFIG_SENSORS_UCD9200 is not set +# CONFIG_SENSORS_VEXPRESS is not set +# CONFIG_SENSORS_VIA686A is not set +# CONFIG_SENSORS_VIA_CPUTEMP is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_VT8231 is not set +# CONFIG_SENSORS_W83627EHF is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83773G is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83795 is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83L786NG is not set +# CONFIG_SENSORS_XDPE122 is not set +# CONFIG_SENSORS_XDPE152 is not set +# CONFIG_SENSORS_XGENE is not set +# CONFIG_SENSORS_ZL6100 is not set +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_16550A_VARIANTS=y +# CONFIG_SERIAL_8250_ACCENT is not set +# CONFIG_SERIAL_8250_ASPEED_VUART is not set +# CONFIG_SERIAL_8250_BOCA is not set +CONFIG_SERIAL_8250_CONSOLE=y +# CONFIG_SERIAL_8250_CS is not set +# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set +# CONFIG_SERIAL_8250_DETECT_IRQ is not set +CONFIG_SERIAL_8250_DMA=y +# CONFIG_SERIAL_8250_DW is not set +# CONFIG_SERIAL_8250_EM is not set +# CONFIG_SERIAL_8250_EXAR is not set +# CONFIG_SERIAL_8250_EXAR_ST16C554 is not set +# CONFIG_SERIAL_8250_EXTENDED is not set +# CONFIG_SERIAL_8250_FINTEK is not set +# CONFIG_SERIAL_8250_FOURPORT is not set +# CONFIG_SERIAL_8250_HUB6 is not set +# CONFIG_SERIAL_8250_INGENIC is not set +# CONFIG_SERIAL_8250_LPSS is not set +# CONFIG_SERIAL_8250_MANY_PORTS is not set +# CONFIG_SERIAL_8250_MID is not set +# CONFIG_SERIAL_8250_MOXA is not set +CONFIG_SERIAL_8250_NR_UARTS=2 +# CONFIG_SERIAL_8250_PCI is not set +# CONFIG_SERIAL_8250_PERICOM is not set +# CONFIG_SERIAL_8250_RSA is not set +# CONFIG_SERIAL_8250_RT288X is not set +CONFIG_SERIAL_8250_RUNTIME_UARTS=2 +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_AMBA_PL010 is not set +# CONFIG_SERIAL_AMBA_PL011 is not set +# CONFIG_SERIAL_ARC is not set +# CONFIG_SERIAL_BCM63XX is not set +# CONFIG_SERIAL_CONEXANT_DIGICOLOR is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_DEV_BUS is not set +CONFIG_SERIAL_EARLYCON=y +# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set +# CONFIG_SERIAL_FSL_LINFLEXUART is not set +# CONFIG_SERIAL_FSL_LPUART is not set +# CONFIG_SERIAL_GRLIB_GAISLER_APBUART is not set +# CONFIG_SERIAL_IFX6X60 is not set +# CONFIG_SERIAL_JSM is not set +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX310X is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_SERIAL_OF_PLATFORM is not set +# CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL is not set +# CONFIG_SERIAL_PCH_UART is not set +# CONFIG_SERIAL_RP2 is not set +# CONFIG_SERIAL_SC16IS7XX is not set +# CONFIG_SERIAL_SCCNXP is not set +# CONFIG_SERIAL_SH_SCI is not set +# CONFIG_SERIAL_SIFIVE is not set +# CONFIG_SERIAL_SPRD is not set +# CONFIG_SERIAL_STM32 is not set +# CONFIG_SERIAL_ST_ASC is not set +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_UARTLITE is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +# CONFIG_SERIO is not set +# CONFIG_SERIO_ALTERA_PS2 is not set +# CONFIG_SERIO_AMBAKMI is not set +# CONFIG_SERIO_APBPS2 is not set +# CONFIG_SERIO_ARC_PS2 is not set +# CONFIG_SERIO_CT82C710 is not set +# CONFIG_SERIO_GPIO_PS2 is not set +# CONFIG_SERIO_I8042 is not set +# CONFIG_SERIO_LIBPS2 is not set +# CONFIG_SERIO_PARKBD is not set +# CONFIG_SERIO_PCIPS2 is not set +# CONFIG_SERIO_PS2MULT is not set +# CONFIG_SERIO_RAW is not set +# CONFIG_SERIO_SERPORT is not set +# CONFIG_SERIO_SUN4I_PS2 is not set +# CONFIG_SFC is not set +# CONFIG_SFC_FALCON is not set +# CONFIG_SFC_SIENA is not set +# CONFIG_SFI is not set +# CONFIG_SFP is not set +# CONFIG_SF_PDMA is not set +# CONFIG_SGETMASK_SYSCALL is not set +# CONFIG_SGI_IOC4 is not set +# CONFIG_SGI_IP22 is not set +# CONFIG_SGI_IP27 is not set +# CONFIG_SGI_IP28 is not set +# CONFIG_SGI_IP30 is not set +# CONFIG_SGI_IP32 is not set +# CONFIG_SGI_MFD_IOC3 is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_SG_POOL is not set +# CONFIG_SG_SPLIT is not set +# CONFIG_SHADOW_CALL_STACK is not set +CONFIG_SHMEM=y +# CONFIG_SHRINKER_DEBUG is not set +# CONFIG_SHUFFLE_PAGE_ALLOCATOR is not set +# CONFIG_SH_ETH is not set +# CONFIG_SH_TIMER_CMT is not set +# CONFIG_SH_TIMER_MTU2 is not set +# CONFIG_SH_TIMER_TMU is not set +# CONFIG_SI1133 is not set +# CONFIG_SI1145 is not set +# CONFIG_SI7005 is not set +# CONFIG_SI7020 is not set +# CONFIG_SIBYTE_BIGSUR is not set +# CONFIG_SIBYTE_CARMEL is not set +# CONFIG_SIBYTE_CRHINE is not set +# CONFIG_SIBYTE_CRHONE is not set +# CONFIG_SIBYTE_LITTLESUR is not set +# CONFIG_SIBYTE_RHONE is not set +# CONFIG_SIBYTE_SENTOSA is not set +# CONFIG_SIBYTE_SWARM is not set +CONFIG_SIGNALFD=y +# CONFIG_SIGNED_PE_FILE_VERIFICATION is not set +# CONFIG_SIMPLE_GPIO is not set +# CONFIG_SIMPLE_PM_BUS is not set +# CONFIG_SIOX is not set +# CONFIG_SIS190 is not set +# CONFIG_SIS900 is not set +# CONFIG_SKGE is not set +# CONFIG_SKY2 is not set +# CONFIG_SKY2_DEBUG is not set +# CONFIG_SLAB is not set +CONFIG_SLABINFO=y +CONFIG_SLAB_FREELIST_HARDENED=y +CONFIG_SLAB_FREELIST_RANDOM=y +CONFIG_SLAB_MERGE_DEFAULT=y +# CONFIG_SLHC is not set +# CONFIG_SLICOSS is not set +# CONFIG_SLIMBUS is not set +# CONFIG_SLIP is not set +# CONFIG_SLOB is not set +CONFIG_SLUB=y +CONFIG_SLUB_CPU_PARTIAL=y +# CONFIG_SLUB_DEBUG is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SLUB_MEMCG_SYSFS_ON is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_SMARTJOYPLUS_FF is not set +# CONFIG_SMB_SERVER is not set +# CONFIG_SMC911X is not set +# CONFIG_SMC9194 is not set +# CONFIG_SMC91X is not set +# CONFIG_SMP is not set +# CONFIG_SMSC911X is not set +# CONFIG_SMSC9420 is not set +# CONFIG_SMSC_PHY is not set +# CONFIG_SMS_SDIO_DRV is not set +# CONFIG_SMS_USB_DRV is not set +# CONFIG_SM_CAMCC_8250 is not set +# CONFIG_SM_FTL is not set +# CONFIG_SM_GCC_6115 is not set +# CONFIG_SM_GCC_6125 is not set +# CONFIG_SM_GCC_6350 is not set +# CONFIG_SM_GCC_6375 is not set +# CONFIG_SM_GCC_8350 is not set +# CONFIG_SND is not set +# CONFIG_SND_AC97_POWER_SAVE is not set +# CONFIG_SND_AD1816A is not set +# CONFIG_SND_AD1848 is not set +# CONFIG_SND_AD1889 is not set +# CONFIG_SND_ADLIB is not set +# CONFIG_SND_ALI5451 is not set +# CONFIG_SND_ALOOP is not set +# CONFIG_SND_ALS100 is not set +# CONFIG_SND_ALS300 is not set +# CONFIG_SND_ALS4000 is not set +# CONFIG_SND_AMD_ACP_CONFIG is not set +# CONFIG_SND_ARM is not set +# CONFIG_SND_ASIHPI is not set +# CONFIG_SND_ATIIXP is not set +# CONFIG_SND_ATIIXP_MODEM is not set +# CONFIG_SND_ATMEL_AC97C is not set +# CONFIG_SND_ATMEL_SOC is not set +# CONFIG_SND_AU8810 is not set +# CONFIG_SND_AU8820 is not set +# CONFIG_SND_AU8830 is not set +# CONFIG_SND_AUDIO_GRAPH_CARD is not set +# CONFIG_SND_AUDIO_GRAPH_CARD2 is not set +# CONFIG_SND_AUDIO_GRAPH_SCU_CARD is not set +# CONFIG_SND_AW2 is not set +# CONFIG_SND_AZT2320 is not set +# CONFIG_SND_AZT3328 is not set +# CONFIG_SND_BCD2000 is not set +# CONFIG_SND_BCM2835 is not set +# CONFIG_SND_BCM63XX_I2S_WHISTLER is not set +# CONFIG_SND_BT87X is not set +# CONFIG_SND_CA0106 is not set +# CONFIG_SND_CMI8330 is not set +# CONFIG_SND_CMIPCI is not set +# CONFIG_SND_CS4231 is not set +# CONFIG_SND_CS4236 is not set +# CONFIG_SND_CS4281 is not set +# CONFIG_SND_CS46XX is not set +# CONFIG_SND_CS5530 is not set +# CONFIG_SND_CS5535AUDIO is not set +# CONFIG_SND_CTL_FAST_LOOKUP is not set +# CONFIG_SND_CTL_INPUT_VALIDATION is not set +# CONFIG_SND_CTXFI is not set +# CONFIG_SND_DARLA20 is not set +# CONFIG_SND_DARLA24 is not set +# CONFIG_SND_DEBUG is not set +# CONFIG_SND_DESIGNWARE_I2S is not set +CONFIG_SND_DRIVERS=y +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_DYNAMIC_MINORS is not set +# CONFIG_SND_ECHO3G is not set +# CONFIG_SND_EDMA_SOC is not set +# CONFIG_SND_EMU10K1 is not set +# CONFIG_SND_EMU10K1X is not set +# CONFIG_SND_EMU10K1_SEQ is not set +# CONFIG_SND_ENS1370 is not set +# CONFIG_SND_ENS1371 is not set +# CONFIG_SND_ES1688 is not set +# CONFIG_SND_ES18XX is not set +# CONFIG_SND_ES1938 is not set +# CONFIG_SND_ES1968 is not set +# CONFIG_SND_FIREWIRE is not set +# CONFIG_SND_FM801 is not set +# CONFIG_SND_GINA20 is not set +# CONFIG_SND_GINA24 is not set +# CONFIG_SND_GUSCLASSIC is not set +# CONFIG_SND_GUSEXTREME is not set +# CONFIG_SND_GUSMAX is not set +# CONFIG_SND_HDA_CODEC_CS8409 is not set +# CONFIG_SND_HDA_INTEL is not set +# CONFIG_SND_HDA_INTEL_DETECT_DMIC is not set +# CONFIG_SND_HDA_INTEL_HDMI_SILENT_STREAM is not set +CONFIG_SND_HDA_POWER_SAVE_DEFAULT=0 +CONFIG_SND_HDA_PREALLOC_SIZE=64 +# CONFIG_SND_HDSP is not set +# CONFIG_SND_HDSPM is not set +# CONFIG_SND_HRTIMER is not set +# CONFIG_SND_HWDEP is not set +# CONFIG_SND_I2S_HI6210_I2S is not set +# CONFIG_SND_ICE1712 is not set +# CONFIG_SND_ICE1724 is not set +# CONFIG_SND_INDIGO is not set +# CONFIG_SND_INDIGODJ is not set +# CONFIG_SND_INDIGODJX is not set +# CONFIG_SND_INDIGOIO is not set +# CONFIG_SND_INDIGOIOX is not set +# CONFIG_SND_INTEL8X0 is not set +# CONFIG_SND_INTEL8X0M is not set +# CONFIG_SND_INTERWAVE is not set +# CONFIG_SND_INTERWAVE_STB is not set +# CONFIG_SND_ISA is not set +# CONFIG_SND_JZ4740_SOC_I2S is not set +# CONFIG_SND_KIRKWOOD_SOC is not set +# CONFIG_SND_KORG1212 is not set +# CONFIG_SND_LAYLA20 is not set +# CONFIG_SND_LAYLA24 is not set +# CONFIG_SND_LOLA is not set +# CONFIG_SND_LX6464ES is not set +# CONFIG_SND_MAESTRO3 is not set +CONFIG_SND_MAX_CARDS=16 +# CONFIG_SND_MIA is not set +# CONFIG_SND_MIPS is not set +# CONFIG_SND_MIRO is not set +# CONFIG_SND_MIXART is not set +# CONFIG_SND_MIXER_OSS is not set +# CONFIG_SND_MONA is not set +# CONFIG_SND_MPC52xx_SOC_EFIKA is not set +# CONFIG_SND_MPU401 is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_MTS64 is not set +# CONFIG_SND_MXS_SOC is not set +# CONFIG_SND_NM256 is not set +# CONFIG_SND_OPL3SA2 is not set +# CONFIG_SND_OPL3_LIB_SEQ is not set +# CONFIG_SND_OPL4_LIB_SEQ is not set +# CONFIG_SND_OPTI92X_AD1848 is not set +# CONFIG_SND_OPTI92X_CS4231 is not set +# CONFIG_SND_OPTI93X is not set +CONFIG_SND_OSSEMUL=y +# CONFIG_SND_OXYGEN is not set +CONFIG_SND_PCI=y +# CONFIG_SND_PCM is not set +# CONFIG_SND_PCMCIA is not set +# CONFIG_SND_PCM_OSS is not set +CONFIG_SND_PCM_OSS_PLUGINS=y +# CONFIG_SND_PCM_TIMER is not set +# CONFIG_SND_PCM_XRUN_DEBUG is not set +# CONFIG_SND_PCXHR is not set +# CONFIG_SND_PDAUDIOCF is not set +# CONFIG_SND_PORTMAN2X4 is not set +# CONFIG_SND_POWERPC_SOC is not set +# CONFIG_SND_PPC is not set +CONFIG_SND_PROC_FS=y +# CONFIG_SND_RAWMIDI is not set +# CONFIG_SND_RAWMIDI_SEQ is not set +# CONFIG_SND_RIPTIDE is not set +# CONFIG_SND_RME32 is not set +# CONFIG_SND_RME96 is not set +# CONFIG_SND_RME9652 is not set +# CONFIG_SND_RTCTIMER is not set +# CONFIG_SND_SB16 is not set +# CONFIG_SND_SB8 is not set +# CONFIG_SND_SBAWE is not set +# CONFIG_SND_SBAWE_SEQ is not set +# CONFIG_SND_SE6X is not set +# CONFIG_SND_SEQUENCER is not set +# CONFIG_SND_SERIAL_GENERIC is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_SIMPLE_CARD is not set +# CONFIG_SND_SIMPLE_SCU_CARD is not set +# CONFIG_SND_SIS7019 is not set +# CONFIG_SND_SOC is not set +# CONFIG_SND_SOC_AC97_CODEC is not set +# CONFIG_SND_SOC_AD193X_I2C is not set +# CONFIG_SND_SOC_AD193X_SPI is not set +# CONFIG_SND_SOC_ADAU1372_I2C is not set +# CONFIG_SND_SOC_ADAU1372_SPI is not set +# CONFIG_SND_SOC_ADAU1701 is not set +# CONFIG_SND_SOC_ADAU1761_I2C is not set +# CONFIG_SND_SOC_ADAU1761_SPI is not set +# CONFIG_SND_SOC_ADAU7002 is not set +# CONFIG_SND_SOC_ADAU7118_HW is not set +# CONFIG_SND_SOC_ADAU7118_I2C is not set +# CONFIG_SND_SOC_ADI is not set +# CONFIG_SND_SOC_AK4104 is not set +# CONFIG_SND_SOC_AK4118 is not set +# CONFIG_SND_SOC_AK4375 is not set +# CONFIG_SND_SOC_AK4458 is not set +# CONFIG_SND_SOC_AK4554 is not set +# CONFIG_SND_SOC_AK4613 is not set +# CONFIG_SND_SOC_AK4642 is not set +# CONFIG_SND_SOC_AK5386 is not set +# CONFIG_SND_SOC_AK5558 is not set +# CONFIG_SND_SOC_ALC5623 is not set +# CONFIG_SND_SOC_AMD_ACP is not set +# CONFIG_SND_SOC_AMD_ACP3x is not set +# CONFIG_SND_SOC_AMD_ACP5x is not set +# CONFIG_SND_SOC_AMD_RENOIR is not set +# CONFIG_SND_SOC_AU1XAUDIO is not set +# CONFIG_SND_SOC_AU1XPSC is not set +# CONFIG_SND_SOC_AW8738 is not set +# CONFIG_SND_SOC_BD28623 is not set +# CONFIG_SND_SOC_BT_SCO is not set +# CONFIG_SND_SOC_CS35L32 is not set +# CONFIG_SND_SOC_CS35L33 is not set +# CONFIG_SND_SOC_CS35L34 is not set +# CONFIG_SND_SOC_CS35L35 is not set +# CONFIG_SND_SOC_CS35L36 is not set +# CONFIG_SND_SOC_CS35L41_I2C is not set +# CONFIG_SND_SOC_CS35L41_SPI is not set +# CONFIG_SND_SOC_CS35L45_I2C is not set +# CONFIG_SND_SOC_CS35L45_SPI is not set +# CONFIG_SND_SOC_CS4234 is not set +# CONFIG_SND_SOC_CS4265 is not set +# CONFIG_SND_SOC_CS4270 is not set +# CONFIG_SND_SOC_CS4271 is not set +# CONFIG_SND_SOC_CS4271_I2C is not set +# CONFIG_SND_SOC_CS4271_SPI is not set +# CONFIG_SND_SOC_CS42L42 is not set +# CONFIG_SND_SOC_CS42L51_I2C is not set +# CONFIG_SND_SOC_CS42L52 is not set +# CONFIG_SND_SOC_CS42L56 is not set +# CONFIG_SND_SOC_CS42L73 is not set +# CONFIG_SND_SOC_CS42L83 is not set +# CONFIG_SND_SOC_CS42XX8_I2C is not set +# CONFIG_SND_SOC_CS43130 is not set +# CONFIG_SND_SOC_CS4341 is not set +# CONFIG_SND_SOC_CS4349 is not set +# CONFIG_SND_SOC_CS53L30 is not set +# CONFIG_SND_SOC_CX2072X is not set +# CONFIG_SND_SOC_DA7213 is not set +# CONFIG_SND_SOC_DIO2125 is not set +# CONFIG_SND_SOC_DMIC is not set +# CONFIG_SND_SOC_ES7134 is not set +# CONFIG_SND_SOC_ES7241 is not set +# CONFIG_SND_SOC_ES8316 is not set +# CONFIG_SND_SOC_ES8326 is not set +# CONFIG_SND_SOC_ES8328 is not set +# CONFIG_SND_SOC_ES8328_I2C is not set +# CONFIG_SND_SOC_ES8328_SPI is not set +# CONFIG_SND_SOC_EUKREA_TLV320 is not set +# CONFIG_SND_SOC_FSL_ASOC_CARD is not set +# CONFIG_SND_SOC_FSL_ASRC is not set +# CONFIG_SND_SOC_FSL_AUD2HTX is not set +# CONFIG_SND_SOC_FSL_AUDMIX is not set +# CONFIG_SND_SOC_FSL_ESAI is not set +# CONFIG_SND_SOC_FSL_MICFIL is not set +# CONFIG_SND_SOC_FSL_RPMSG is not set +# CONFIG_SND_SOC_FSL_SAI is not set +# CONFIG_SND_SOC_FSL_SPDIF is not set +# CONFIG_SND_SOC_FSL_SSI is not set +# CONFIG_SND_SOC_FSL_XCVR is not set +# CONFIG_SND_SOC_GTM601 is not set +# CONFIG_SND_SOC_HDA is not set +# CONFIG_SND_SOC_ICS43432 is not set +# CONFIG_SND_SOC_IMG is not set +# CONFIG_SND_SOC_IMX_AUDMIX is not set +# CONFIG_SND_SOC_IMX_AUDMUX is not set +# CONFIG_SND_SOC_IMX_CARD is not set +# CONFIG_SND_SOC_IMX_ES8328 is not set +# CONFIG_SND_SOC_IMX_HDMI is not set +# CONFIG_SND_SOC_IMX_RPMSG is not set +# CONFIG_SND_SOC_IMX_SPDIF is not set +# CONFIG_SND_SOC_IMX_WM8962 is not set +# CONFIG_SND_SOC_INNO_RK3036 is not set +# CONFIG_SND_SOC_INTEL_APL is not set +# CONFIG_SND_SOC_INTEL_BAYTRAIL is not set +# CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH is not set +# CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH is not set +# CONFIG_SND_SOC_INTEL_BXT_RT298_MACH is not set +# CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH is not set +# CONFIG_SND_SOC_INTEL_BYTCR_RT5651_MACH is not set +# CONFIG_SND_SOC_INTEL_BYT_CHT_DA7213_MACH is not set +# CONFIG_SND_SOC_INTEL_BYT_CHT_ES8316_MACH is not set +# CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH is not set +# CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH is not set +# CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH is not set +# CONFIG_SND_SOC_INTEL_CATPT is not set +# CONFIG_SND_SOC_INTEL_CFL is not set +# CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH is not set +# CONFIG_SND_SOC_INTEL_CHT_BSW_NAU8824_MACH is not set +# CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH is not set +# CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH is not set +# CONFIG_SND_SOC_INTEL_CML_H is not set +# CONFIG_SND_SOC_INTEL_CML_LP is not set +# CONFIG_SND_SOC_INTEL_CNL is not set +# CONFIG_SND_SOC_INTEL_GLK is not set +# CONFIG_SND_SOC_INTEL_HASWELL is not set +# CONFIG_SND_SOC_INTEL_KBL is not set +# CONFIG_SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH is not set +# CONFIG_SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH is not set +# CONFIG_SND_SOC_INTEL_KEEMBAY is not set +# CONFIG_SND_SOC_INTEL_SKL is not set +# CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH is not set +# CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH is not set +# CONFIG_SND_SOC_INTEL_SKL_RT286_MACH is not set +# CONFIG_SND_SOC_INTEL_SKYLAKE is not set +# CONFIG_SND_SOC_INTEL_SST is not set +CONFIG_SND_SOC_INTEL_SST_TOPLEVEL=y +# CONFIG_SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES is not set +# CONFIG_SND_SOC_JZ4725B_CODEC is not set +# CONFIG_SND_SOC_JZ4740_CODEC is not set +# CONFIG_SND_SOC_JZ4770_CODEC is not set +# CONFIG_SND_SOC_LPASS_RX_MACRO is not set +# CONFIG_SND_SOC_LPASS_TX_MACRO is not set +# CONFIG_SND_SOC_LPASS_VA_MACRO is not set +# CONFIG_SND_SOC_LPASS_WSA_MACRO is not set +# CONFIG_SND_SOC_MA120X0P is not set +# CONFIG_SND_SOC_MAX9759 is not set +# CONFIG_SND_SOC_MAX98088 is not set +# CONFIG_SND_SOC_MAX98357A is not set +# CONFIG_SND_SOC_MAX98373 is not set +# CONFIG_SND_SOC_MAX98373_I2C is not set +# CONFIG_SND_SOC_MAX98390 is not set +# CONFIG_SND_SOC_MAX98396 is not set +# CONFIG_SND_SOC_MAX98504 is not set +# CONFIG_SND_SOC_MAX98520 is not set +# CONFIG_SND_SOC_MAX9860 is not set +# CONFIG_SND_SOC_MAX9867 is not set +# CONFIG_SND_SOC_MAX98927 is not set +# CONFIG_SND_SOC_MEDIATEK is not set +# CONFIG_SND_SOC_MPC5200_AC97 is not set +# CONFIG_SND_SOC_MPC5200_I2S is not set +# CONFIG_SND_SOC_MSM8916_WCD_ANALOG is not set +# CONFIG_SND_SOC_MSM8916_WCD_DIGITAL is not set +# CONFIG_SND_SOC_MT2701 is not set +# CONFIG_SND_SOC_MT6351 is not set +# CONFIG_SND_SOC_MT6358 is not set +# CONFIG_SND_SOC_MT6359 is not set +# CONFIG_SND_SOC_MT6359_ACCDET is not set +# CONFIG_SND_SOC_MT6660 is not set +# CONFIG_SND_SOC_MT6797 is not set +# CONFIG_SND_SOC_MT8173 is not set +# CONFIG_SND_SOC_MT8183 is not set +# CONFIG_SND_SOC_MT8186 is not set +# CONFIG_SND_SOC_MT8192 is not set +# CONFIG_SND_SOC_MT8195 is not set +# CONFIG_SND_SOC_MTK_BTCVSD is not set +# CONFIG_SND_SOC_NAU8315 is not set +# CONFIG_SND_SOC_NAU8540 is not set +# CONFIG_SND_SOC_NAU8810 is not set +# CONFIG_SND_SOC_NAU8821 is not set +# CONFIG_SND_SOC_NAU8822 is not set +# CONFIG_SND_SOC_NAU8824 is not set +# CONFIG_SND_SOC_PCM1681 is not set +# CONFIG_SND_SOC_PCM1789_I2C is not set +# CONFIG_SND_SOC_PCM1792A is not set +# CONFIG_SND_SOC_PCM179X_I2C is not set +# CONFIG_SND_SOC_PCM179X_SPI is not set +# CONFIG_SND_SOC_PCM186X_I2C is not set +# CONFIG_SND_SOC_PCM186X_SPI is not set +# CONFIG_SND_SOC_PCM3060_I2C is not set +# CONFIG_SND_SOC_PCM3060_SPI is not set +# CONFIG_SND_SOC_PCM3168A_I2C is not set +# CONFIG_SND_SOC_PCM3168A_SPI is not set +# CONFIG_SND_SOC_PCM5102A is not set +# CONFIG_SND_SOC_PCM512x_I2C is not set +# CONFIG_SND_SOC_PCM512x_SPI is not set +# CONFIG_SND_SOC_QCOM is not set +# CONFIG_SND_SOC_RK3328 is not set +# CONFIG_SND_SOC_RK817 is not set +# CONFIG_SND_SOC_ROCKCHIP is not set +# CONFIG_SND_SOC_RT5616 is not set +# CONFIG_SND_SOC_RT5631 is not set +# CONFIG_SND_SOC_RT5640 is not set +# CONFIG_SND_SOC_RT5659 is not set +# CONFIG_SND_SOC_RT5677_SPI is not set +# CONFIG_SND_SOC_RT9120 is not set +# CONFIG_SND_SOC_SGTL5000 is not set +# CONFIG_SND_SOC_SIMPLE_AMPLIFIER is not set +# CONFIG_SND_SOC_SIMPLE_MUX is not set +# CONFIG_SND_SOC_SIRF_AUDIO_CODEC is not set +# CONFIG_SND_SOC_SOF_TOPLEVEL is not set +# CONFIG_SND_SOC_SPDIF is not set +# CONFIG_SND_SOC_SRC4XXX_I2C is not set +# CONFIG_SND_SOC_SSM2305 is not set +# CONFIG_SND_SOC_SSM2518 is not set +# CONFIG_SND_SOC_SSM2602_I2C is not set +# CONFIG_SND_SOC_SSM2602_SPI is not set +# CONFIG_SND_SOC_SSM4567 is not set +# CONFIG_SND_SOC_STA32X is not set +# CONFIG_SND_SOC_STA350 is not set +# CONFIG_SND_SOC_STI_SAS is not set +# CONFIG_SND_SOC_TAS2552 is not set +# CONFIG_SND_SOC_TAS2562 is not set +# CONFIG_SND_SOC_TAS2764 is not set +# CONFIG_SND_SOC_TAS2770 is not set +# CONFIG_SND_SOC_TAS2780 is not set +# CONFIG_SND_SOC_TAS5086 is not set +# CONFIG_SND_SOC_TAS571X is not set +# CONFIG_SND_SOC_TAS5720 is not set +# CONFIG_SND_SOC_TAS5805M is not set +# CONFIG_SND_SOC_TAS6424 is not set +# CONFIG_SND_SOC_TDA7419 is not set +# CONFIG_SND_SOC_TFA9879 is not set +# CONFIG_SND_SOC_TFA989X is not set +# CONFIG_SND_SOC_TLV320ADC3XXX is not set +# CONFIG_SND_SOC_TLV320ADCX140 is not set +# CONFIG_SND_SOC_TLV320AIC23_I2C is not set +# CONFIG_SND_SOC_TLV320AIC23_SPI is not set +# CONFIG_SND_SOC_TLV320AIC31XX is not set +# CONFIG_SND_SOC_TLV320AIC32X4_I2C is not set +# CONFIG_SND_SOC_TLV320AIC32X4_SPI is not set +# CONFIG_SND_SOC_TLV320AIC3X is not set +# CONFIG_SND_SOC_TLV320AIC3X_I2C is not set +# CONFIG_SND_SOC_TLV320AIC3X_SPI is not set +# CONFIG_SND_SOC_TPA6130A2 is not set +# CONFIG_SND_SOC_TS3A227E is not set +# CONFIG_SND_SOC_TSCS42XX is not set +# CONFIG_SND_SOC_TSCS454 is not set +# CONFIG_SND_SOC_UDA1334 is not set +# CONFIG_SND_SOC_WM8510 is not set +# CONFIG_SND_SOC_WM8523 is not set +# CONFIG_SND_SOC_WM8524 is not set +# CONFIG_SND_SOC_WM8580 is not set +# CONFIG_SND_SOC_WM8711 is not set +# CONFIG_SND_SOC_WM8728 is not set +# CONFIG_SND_SOC_WM8731 is not set +# CONFIG_SND_SOC_WM8731_I2C is not set +# CONFIG_SND_SOC_WM8731_SPI is not set +# CONFIG_SND_SOC_WM8737 is not set +# CONFIG_SND_SOC_WM8741 is not set +# CONFIG_SND_SOC_WM8750 is not set +# CONFIG_SND_SOC_WM8753 is not set +# CONFIG_SND_SOC_WM8770 is not set +# CONFIG_SND_SOC_WM8776 is not set +# CONFIG_SND_SOC_WM8782 is not set +# CONFIG_SND_SOC_WM8804_I2C is not set +# CONFIG_SND_SOC_WM8804_SPI is not set +# CONFIG_SND_SOC_WM8903 is not set +# CONFIG_SND_SOC_WM8904 is not set +# CONFIG_SND_SOC_WM8940 is not set +# CONFIG_SND_SOC_WM8960 is not set +# CONFIG_SND_SOC_WM8962 is not set +# CONFIG_SND_SOC_WM8974 is not set +# CONFIG_SND_SOC_WM8978 is not set +# CONFIG_SND_SOC_WM8985 is not set +# CONFIG_SND_SOC_XILINX_AUDIO_FORMATTER is not set +# CONFIG_SND_SOC_XILINX_I2S is not set +# CONFIG_SND_SOC_XILINX_SPDIF is not set +# CONFIG_SND_SOC_XTFPGA_I2S is not set +# CONFIG_SND_SOC_ZL38060 is not set +# CONFIG_SND_SOC_ZX_AUD96P22 is not set +# CONFIG_SND_SONICVIBES is not set +# CONFIG_SND_SPI is not set +# CONFIG_SND_SSCAPE is not set +# CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI is not set +# CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_PCI is not set +# CONFIG_SND_SUN4I_CODEC is not set +# CONFIG_SND_SUPPORT_OLD_API is not set +# CONFIG_SND_TEST_COMPONENT is not set +# CONFIG_SND_TIMER is not set +# CONFIG_SND_TRIDENT is not set +CONFIG_SND_USB=y +# CONFIG_SND_USB_6FIRE is not set +# CONFIG_SND_USB_AUDIO is not set +# CONFIG_SND_USB_CAIAQ is not set +# CONFIG_SND_USB_HIFACE is not set +# CONFIG_SND_USB_POD is not set +# CONFIG_SND_USB_PODHD is not set +# CONFIG_SND_USB_TONEPORT is not set +# CONFIG_SND_USB_UA101 is not set +# CONFIG_SND_USB_US122L is not set +# CONFIG_SND_USB_USX2Y is not set +# CONFIG_SND_USB_VARIAX is not set +# CONFIG_SND_VERBOSE_PRINTK is not set +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VIA82XX is not set +# CONFIG_SND_VIA82XX_MODEM is not set +# CONFIG_SND_VIRTIO is not set +# CONFIG_SND_VIRTUOSO is not set +# CONFIG_SND_VX222 is not set +# CONFIG_SND_VXPOCKET is not set +# CONFIG_SND_WAVEFRONT is not set +CONFIG_SND_X86=y +# CONFIG_SND_XEN_FRONTEND is not set +# CONFIG_SND_YMFPCI is not set +# CONFIG_SNI_RM is not set +# CONFIG_SOCIONEXT_SYNQUACER_PREITS is not set +# CONFIG_SOCK_CGROUP_DATA is not set +# CONFIG_SOC_AM33XX is not set +# CONFIG_SOC_AM43XX is not set +# CONFIG_SOC_BRCMSTB is not set +# CONFIG_SOC_CAMERA is not set +# CONFIG_SOC_DRA7XX is not set +# CONFIG_SOC_HAS_OMAP2_SDRC is not set +# CONFIG_SOC_OMAP5 is not set +# CONFIG_SOC_TI is not set +# CONFIG_SOFTLOCKUP_DETECTOR is not set +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_SONYPI is not set +# CONFIG_SONY_LAPTOP is not set +# CONFIG_SOUND is not set +# CONFIG_SOUNDWIRE is not set +# CONFIG_SOUND_OSS_CORE is not set +# CONFIG_SOUND_OSS_CORE_PRECLAIM is not set +# CONFIG_SOUND_PRIME is not set +# CONFIG_SP5100_TCO is not set +# CONFIG_SPARSEMEM_MANUAL is not set +# CONFIG_SPARSEMEM_STATIC is not set +# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set +# CONFIG_SPARSE_IRQ is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_SPEAKUP is not set +# CONFIG_SPI is not set +# CONFIG_SPINLOCK_TEST is not set +# CONFIG_SPI_ALTERA is not set +# CONFIG_SPI_AMD is not set +# CONFIG_SPI_AU1550 is not set +# CONFIG_SPI_AX88796C is not set +# CONFIG_SPI_AXI_SPI_ENGINE is not set +# CONFIG_SPI_BCM2835 is not set +# CONFIG_SPI_BCM63XX_HSSPI is not set +# CONFIG_SPI_BCM_QSPI is not set +# CONFIG_SPI_BITBANG is not set +# CONFIG_SPI_BUTTERFLY is not set +# CONFIG_SPI_CADENCE is not set +# CONFIG_SPI_CADENCE_QUADSPI is not set +# CONFIG_SPI_CADENCE_XSPI is not set +# CONFIG_SPI_DEBUG is not set +# CONFIG_SPI_DESIGNWARE is not set +# CONFIG_SPI_FSL_DSPI is not set +# CONFIG_SPI_FSL_ESPI is not set +# CONFIG_SPI_FSL_SPI is not set +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_GPIO_OLD is not set +# CONFIG_SPI_IMG_SPFI is not set +# CONFIG_SPI_LANTIQ_SSC is not set +# CONFIG_SPI_LM70_LLP is not set +# CONFIG_SPI_LOOPBACK_TEST is not set +# CONFIG_SPI_MASTER is not set +# CONFIG_SPI_MEM is not set +# CONFIG_SPI_MICROCHIP_CORE is not set +# CONFIG_SPI_MICROCHIP_CORE_QSPI is not set +# CONFIG_SPI_MPC52xx is not set +# CONFIG_SPI_MPC52xx_PSC is not set +# CONFIG_SPI_MTK_QUADSPI is not set +# CONFIG_SPI_MUX is not set +# CONFIG_SPI_MXIC is not set +# CONFIG_SPI_NXP_FLEXSPI is not set +# CONFIG_SPI_OCTEON is not set +# CONFIG_SPI_OC_TINY is not set +# CONFIG_SPI_ORION is not set +# CONFIG_SPI_PL022 is not set +# CONFIG_SPI_PPC4xx is not set +# CONFIG_SPI_PXA2XX is not set +# CONFIG_SPI_PXA2XX_PCI is not set +# CONFIG_SPI_QCOM_QSPI is not set +# CONFIG_SPI_ROCKCHIP is not set +# CONFIG_SPI_S3C64XX is not set +# CONFIG_SPI_SC18IS602 is not set +# CONFIG_SPI_SIFIVE is not set +# CONFIG_SPI_SLAVE is not set +# CONFIG_SPI_SPIDEV is not set +# CONFIG_SPI_THUNDERX is not set +# CONFIG_SPI_TI_QSPI is not set +# CONFIG_SPI_TLE62X0 is not set +# CONFIG_SPI_TOPCLIFF_PCH is not set +# CONFIG_SPI_XCOMM is not set +# CONFIG_SPI_XILINX is not set +# CONFIG_SPI_XWAY is not set +# CONFIG_SPI_ZYNQMP_GQSPI is not set +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_SPMI is not set +# CONFIG_SPS30 is not set +# CONFIG_SPS30_I2C is not set +# CONFIG_SPS30_SERIAL is not set +CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set +# CONFIG_SQUASHFS_DECOMP_MULTI is not set +CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU=y +# CONFIG_SQUASHFS_DECOMP_SINGLE is not set +CONFIG_SQUASHFS_EMBEDDED=y +# CONFIG_SQUASHFS_FILE_CACHE is not set +CONFIG_SQUASHFS_FILE_DIRECT=y +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# CONFIG_SQUASHFS_LZ4 is not set +# CONFIG_SQUASHFS_LZO is not set +# CONFIG_SQUASHFS_XATTR is not set +CONFIG_SQUASHFS_XZ=y +# CONFIG_SQUASHFS_ZLIB is not set +# CONFIG_SQUASHFS_ZSTD is not set +# CONFIG_SRAM is not set +# CONFIG_SRF04 is not set +# CONFIG_SRF08 is not set +# CONFIG_SSB is not set +# CONFIG_SSB_DEBUG is not set +# CONFIG_SSB_DRIVER_GPIO is not set +# CONFIG_SSB_HOST_SOC is not set +# CONFIG_SSB_PCMCIAHOST is not set +CONFIG_SSB_POSSIBLE=y +# CONFIG_SSB_SDIOHOST is not set +# CONFIG_SSB_SILENT is not set +# CONFIG_SSFDC is not set +# CONFIG_STACKPROTECTOR is not set +# CONFIG_STACKPROTECTOR_PER_TASK is not set +# CONFIG_STACKPROTECTOR_STRONG is not set +# CONFIG_STACKTRACE is not set +# CONFIG_STACKTRACE_BUILD_ID is not set +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_STACK_HASH_ORDER=20 +# CONFIG_STACK_TRACER is not set +# CONFIG_STACK_VALIDATION is not set +CONFIG_STAGING=y +# CONFIG_STAGING_BOARD is not set +# CONFIG_STAGING_GASKET_FRAMEWORK is not set +# CONFIG_STAGING_MEDIA is not set +CONFIG_STANDALONE=y +# CONFIG_STATIC_KEYS_SELFTEST is not set +# CONFIG_STATIC_USERMODEHELPER is not set +CONFIG_STDBINUTILS=y +# CONFIG_STE10XP is not set +# CONFIG_STE_MODEM_RPROC is not set +# CONFIG_STK3310 is not set +# CONFIG_STK8312 is not set +# CONFIG_STK8BA50 is not set +# CONFIG_STM is not set +# CONFIG_STMMAC_ETH is not set +# CONFIG_STMMAC_PCI is not set +# CONFIG_STMMAC_PLATFORM is not set +# CONFIG_STMMAC_SELFTESTS is not set +# CONFIG_STMPE_ADC is not set +# CONFIG_STM_DUMMY is not set +# CONFIG_STM_SOURCE_CONSOLE is not set +CONFIG_STP=y +# CONFIG_STREAM_PARSER is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_STRICT_KERNEL_RWX=y +CONFIG_STRICT_MODULE_RWX=y +# CONFIG_STRING_SELFTEST is not set +CONFIG_STRIP_ASM_SYMS=y +# CONFIG_STX104 is not set +# CONFIG_ST_UVIS25 is not set +# CONFIG_SUN4I_GPADC is not set +# CONFIG_SUN50I_DE2_BUS is not set +# CONFIG_SUN50I_ERRATUM_UNKNOWN1 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_SUNGEM is not set +# CONFIG_SUNRPC is not set +# CONFIG_SUNRPC_DEBUG is not set +CONFIG_SUNRPC_DISABLE_INSECURE_ENCTYPES=y +# CONFIG_SUNRPC_GSS is not set +# CONFIG_SUNXI_SRAM is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_SURFACE_3_BUTTON is not set +# CONFIG_SURFACE_PLATFORMS is not set +# CONFIG_SUSPEND is not set +# CONFIG_SUSPEND_SKIP_SYNC is not set +CONFIG_SWAP=y +# CONFIG_SWCONFIG is not set +# CONFIG_SWCONFIG_B53 is not set +# CONFIG_SWCONFIG_B53_MDIO_DRIVER is not set +# CONFIG_SWCONFIG_B53_MMAP_DRIVER is not set +# CONFIG_SWCONFIG_B53_SPI_DRIVER is not set +# CONFIG_SWCONFIG_B53_SRAB_DRIVER is not set +# CONFIG_SWCONFIG_LEDS is not set +# CONFIG_SW_SYNC is not set +# CONFIG_SX9310 is not set +# CONFIG_SX9324 is not set +# CONFIG_SX9360 is not set +# CONFIG_SX9500 is not set +# CONFIG_SXGBE_ETH is not set +CONFIG_SYMBOLIC_ERRNAME=y +# CONFIG_SYNCLINK_CS is not set +# CONFIG_SYNC_FILE is not set +# CONFIG_SYNOPSYS_DWC_ETH_QOS is not set +# CONFIG_SYNTH_EVENTS is not set +# CONFIG_SYNTH_EVENT_GEN_TEST is not set +CONFIG_SYN_COOKIES=y +# CONFIG_SYSCON_REBOOT_MODE is not set +CONFIG_SYSCTL=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_SYSFS=y +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_SYSFS_DEPRECATED_V2 is not set +# CONFIG_SYSFS_SYSCALL is not set +# CONFIG_SYSTEMPORT is not set +# CONFIG_SYSTEM_BLACKLIST_KEYRING is not set +# CONFIG_SYSTEM_DATA_VERIFICATION is not set +# CONFIG_SYSTEM_TRUSTED_KEYRING is not set +CONFIG_SYSTEM_TRUSTED_KEYS="" +# CONFIG_SYSV68_PARTITION is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_SYSV_FS is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_T5403 is not set +# CONFIG_TARGET_CORE is not set +# CONFIG_TASKSTATS is not set +# CONFIG_TASKS_RCU is not set +# CONFIG_TASK_XACCT is not set +# CONFIG_TC35815 is not set +# CONFIG_TCG_ATMEL is not set +# CONFIG_TCG_CRB is not set +# CONFIG_TCG_FTPM_TEE is not set +# CONFIG_TCG_INFINEON is not set +# CONFIG_TCG_NSC is not set +# CONFIG_TCG_ST33_I2C is not set +# CONFIG_TCG_TIS is not set +# CONFIG_TCG_TIS_I2C is not set +# CONFIG_TCG_TIS_I2C_ATMEL is not set +# CONFIG_TCG_TIS_I2C_CR50 is not set +# CONFIG_TCG_TIS_I2C_INFINEON is not set +# CONFIG_TCG_TIS_I2C_NUVOTON is not set +# CONFIG_TCG_TIS_SPI is not set +# CONFIG_TCG_TIS_ST33ZP24_I2C is not set +# CONFIG_TCG_TIS_ST33ZP24_SPI is not set +# CONFIG_TCG_TPM is not set +# CONFIG_TCG_VTPM_PROXY is not set +# CONFIG_TCG_XEN is not set +# CONFIG_TCIC is not set +CONFIG_TCP_CONG_ADVANCED=y +# CONFIG_TCP_CONG_BBR is not set +# CONFIG_TCP_CONG_BIC is not set +# CONFIG_TCP_CONG_CDG is not set +CONFIG_TCP_CONG_CUBIC=y +# CONFIG_TCP_CONG_DCTCP is not set +# CONFIG_TCP_CONG_HSTCP is not set +# CONFIG_TCP_CONG_HTCP is not set +# CONFIG_TCP_CONG_HYBLA is not set +# CONFIG_TCP_CONG_ILLINOIS is not set +# CONFIG_TCP_CONG_LP is not set +# CONFIG_TCP_CONG_NV is not set +# CONFIG_TCP_CONG_SCALABLE is not set +# CONFIG_TCP_CONG_VEGAS is not set +# CONFIG_TCP_CONG_VENO is not set +# CONFIG_TCP_CONG_WESTWOOD is not set +# CONFIG_TCP_CONG_YEAH is not set +# CONFIG_TCP_MD5SIG is not set +# CONFIG_TCS3414 is not set +# CONFIG_TCS3472 is not set +# CONFIG_TEE is not set +# CONFIG_TEGRA_AHB is not set +# CONFIG_TEGRA_HOST1X is not set +# CONFIG_TEHUTI is not set +# CONFIG_TERANETICS_PHY is not set +# CONFIG_TEST_ASYNC_DRIVER_PROBE is not set +# CONFIG_TEST_BITFIELD is not set +# CONFIG_TEST_BITMAP is not set +# CONFIG_TEST_BITOPS is not set +# CONFIG_TEST_BLACKHOLE_DEV is not set +# CONFIG_TEST_BPF is not set +# CONFIG_TEST_CLOCKSOURCE_WATCHDOG is not set +# CONFIG_TEST_DEBUG_VIRTUAL is not set +# CONFIG_TEST_DIV64 is not set +# CONFIG_TEST_DYNAMIC_DEBUG is not set +# CONFIG_TEST_FIRMWARE is not set +# CONFIG_TEST_FREE_PAGES is not set +# CONFIG_TEST_HASH is not set +# CONFIG_TEST_HEXDUMP is not set +# CONFIG_TEST_IDA is not set +# CONFIG_TEST_KASAN_MODULE is not set +# CONFIG_TEST_KMOD is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_TEST_LKM is not set +# CONFIG_TEST_LOCKUP is not set +# CONFIG_TEST_MAPLE_TREE is not set +# CONFIG_TEST_MEMCAT_P is not set +# CONFIG_TEST_MEMINIT is not set +# CONFIG_TEST_MIN_HEAP is not set +# CONFIG_TEST_OVERFLOW is not set +# CONFIG_TEST_POWER is not set +# CONFIG_TEST_PRINTF is not set +# CONFIG_TEST_REF_TRACKER is not set +# CONFIG_TEST_RHASHTABLE is not set +# CONFIG_TEST_SCANF is not set +# CONFIG_TEST_SIPHASH is not set +# CONFIG_TEST_SORT is not set +# CONFIG_TEST_STACKINIT is not set +# CONFIG_TEST_STATIC_KEYS is not set +# CONFIG_TEST_STRING_HELPERS is not set +# CONFIG_TEST_STRSCPY is not set +# CONFIG_TEST_SYSCTL is not set +# CONFIG_TEST_UBSAN is not set +# CONFIG_TEST_UDELAY is not set +# CONFIG_TEST_USER_COPY is not set +# CONFIG_TEST_UUID is not set +# CONFIG_TEST_VMALLOC is not set +# CONFIG_TEST_XARRAY is not set +CONFIG_TEXTSEARCH=y +# CONFIG_TEXTSEARCH_BM is not set +# CONFIG_TEXTSEARCH_FSM is not set +# CONFIG_TEXTSEARCH_KMP is not set +# CONFIG_THERMAL is not set +# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set +# CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR is not set +# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set +# CONFIG_THERMAL_EMULATION is not set +# CONFIG_THERMAL_GOV_BANG_BANG is not set +# CONFIG_THERMAL_GOV_FAIR_SHARE is not set +# CONFIG_THERMAL_GOV_POWER_ALLOCATOR is not set +# CONFIG_THERMAL_GOV_USER_SPACE is not set +# CONFIG_THERMAL_HWMON is not set +# CONFIG_THERMAL_MMIO is not set +# CONFIG_THERMAL_NETLINK is not set +# CONFIG_THERMAL_STATISTICS is not set +# CONFIG_THERMAL_WRITABLE_TRIPS is not set +# CONFIG_THINKPAD_ACPI is not set +CONFIG_THIN_ARCHIVES=y +# CONFIG_THRUSTMASTER_FF is not set +# CONFIG_THUMB2_KERNEL is not set +# CONFIG_THUNDERBOLT is not set +# CONFIG_THUNDER_NIC_BGX is not set +# CONFIG_THUNDER_NIC_PF is not set +# CONFIG_THUNDER_NIC_RGX is not set +# CONFIG_THUNDER_NIC_VF is not set +# CONFIG_TICK_CPU_ACCOUNTING is not set +CONFIG_TICK_ONESHOT=y +# CONFIG_TIFM_CORE is not set +# CONFIG_TIGON3 is not set +# CONFIG_TIMB_DMA is not set +CONFIG_TIMERFD=y +# CONFIG_TIMERLAT_TRACER is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_TIME_NS is not set +# CONFIG_TINYDRM_HX8357D is not set +# CONFIG_TINYDRM_ILI9163 is not set +# CONFIG_TINYDRM_ILI9225 is not set +# CONFIG_TINYDRM_ILI9341 is not set +# CONFIG_TINYDRM_ILI9486 is not set +# CONFIG_TINYDRM_MI0283QT is not set +# CONFIG_TINYDRM_REPAPER is not set +# CONFIG_TINYDRM_ST7586 is not set +# CONFIG_TINYDRM_ST7735R is not set +CONFIG_TINY_RCU=y +# CONFIG_TIPC is not set +# CONFIG_TI_ADC081C is not set +# CONFIG_TI_ADC0832 is not set +# CONFIG_TI_ADC084S021 is not set +# CONFIG_TI_ADC108S102 is not set +# CONFIG_TI_ADC12138 is not set +# CONFIG_TI_ADC128S052 is not set +# CONFIG_TI_ADC161S626 is not set +# CONFIG_TI_ADS1015 is not set +# CONFIG_TI_ADS124S08 is not set +# CONFIG_TI_ADS131E08 is not set +# CONFIG_TI_ADS7950 is not set +# CONFIG_TI_ADS8344 is not set +# CONFIG_TI_ADS8688 is not set +# CONFIG_TI_AM335X_ADC is not set +# CONFIG_TI_CPSW is not set +# CONFIG_TI_CPSW_ALE is not set +# CONFIG_TI_CPSW_PHY_SEL is not set +# CONFIG_TI_CPTS is not set +# CONFIG_TI_DAC082S085 is not set +# CONFIG_TI_DAC5571 is not set +# CONFIG_TI_DAC7311 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_TI_DAC7612 is not set +# CONFIG_TI_DAVINCI_CPDMA is not set +# CONFIG_TI_DAVINCI_MDIO is not set +# CONFIG_TI_ST is not set +# CONFIG_TI_SYSCON_RESET is not set +# CONFIG_TI_TLC4541 is not set +# CONFIG_TI_TSC2046 is not set +# CONFIG_TLAN is not set +# CONFIG_TLS is not set +# CONFIG_TLS_DEVICE is not set +# CONFIG_TLS_TOE is not set +# CONFIG_TMD_HERMES is not set +# CONFIG_TMP006 is not set +# CONFIG_TMP007 is not set +# CONFIG_TMP117 is not set +CONFIG_TMPFS=y +# CONFIG_TMPFS_INODE64 is not set +# CONFIG_TMPFS_POSIX_ACL is not set +CONFIG_TMPFS_XATTR=y +# CONFIG_TOPSTAR_LAPTOP is not set +# CONFIG_TORTURE_TEST is not set +# CONFIG_TOSHIBA_HAPS is not set +# CONFIG_TOUCHSCREEN_88PM860X is not set +# CONFIG_TOUCHSCREEN_AD7877 is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_AD7879_I2C is not set +# CONFIG_TOUCHSCREEN_AD7879_SPI is not set +# CONFIG_TOUCHSCREEN_ADC is not set +# CONFIG_TOUCHSCREEN_ADS7846 is not set +# CONFIG_TOUCHSCREEN_AR1021_I2C is not set +# CONFIG_TOUCHSCREEN_ATMEL_MXT is not set +# CONFIG_TOUCHSCREEN_ATMEL_MXT_T37 is not set +# CONFIG_TOUCHSCREEN_AUO_PIXCIR is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +# CONFIG_TOUCHSCREEN_BU21029 is not set +# CONFIG_TOUCHSCREEN_CHIPONE_ICN8318 is not set +# CONFIG_TOUCHSCREEN_CHIPONE_ICN8505 is not set +# CONFIG_TOUCHSCREEN_COLIBRI_VF50 is not set +# CONFIG_TOUCHSCREEN_CY8CTMA140 is not set +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set +# CONFIG_TOUCHSCREEN_CYTTSP4_CORE is not set +# CONFIG_TOUCHSCREEN_CYTTSP4_I2C is not set +# CONFIG_TOUCHSCREEN_CYTTSP4_SPI is not set +# CONFIG_TOUCHSCREEN_CYTTSP_CORE is not set +# CONFIG_TOUCHSCREEN_CYTTSP_I2C is not set +# CONFIG_TOUCHSCREEN_CYTTSP_SPI is not set +# CONFIG_TOUCHSCREEN_DA9034 is not set +# CONFIG_TOUCHSCREEN_DA9052 is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_EDT_FT5X06 is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_EGALAX is not set +# CONFIG_TOUCHSCREEN_EGALAX_SERIAL is not set +# CONFIG_TOUCHSCREEN_EKTF2127 is not set +# CONFIG_TOUCHSCREEN_ELAN is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_EXC3000 is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GOODIX is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_HIDEEP is not set +# CONFIG_TOUCHSCREEN_HP600 is not set +# CONFIG_TOUCHSCREEN_HP7XX is not set +# CONFIG_TOUCHSCREEN_HTCPEN is not set +# CONFIG_TOUCHSCREEN_HYCON_HY46XX is not set +# CONFIG_TOUCHSCREEN_ILI210X is not set +# CONFIG_TOUCHSCREEN_ILITEK is not set +# CONFIG_TOUCHSCREEN_IMAGIS is not set +# CONFIG_TOUCHSCREEN_IMX6UL_TSC is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_IPAQ_MICRO is not set +# CONFIG_TOUCHSCREEN_IPROC is not set +# CONFIG_TOUCHSCREEN_IQS5XX is not set +# CONFIG_TOUCHSCREEN_LPC32XX is not set +# CONFIG_TOUCHSCREEN_MAX11801 is not set +# CONFIG_TOUCHSCREEN_MC13783 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MELFAS_MIP4 is not set +# CONFIG_TOUCHSCREEN_MIGOR is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_MMS114 is not set +# CONFIG_TOUCHSCREEN_MSG2638 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_MX25 is not set +# CONFIG_TOUCHSCREEN_MXS_LRADC is not set +# CONFIG_TOUCHSCREEN_PCAP is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_PIXCIR is not set +# CONFIG_TOUCHSCREEN_PROPERTIES is not set +# CONFIG_TOUCHSCREEN_RASPBERRYPI_FW is not set +# CONFIG_TOUCHSCREEN_RM_TS is not set +# CONFIG_TOUCHSCREEN_ROHM_BU21023 is not set +# CONFIG_TOUCHSCREEN_RPI_FT5406 is not set +# CONFIG_TOUCHSCREEN_S3C2410 is not set +# CONFIG_TOUCHSCREEN_S6SY761 is not set +# CONFIG_TOUCHSCREEN_SILEAD is not set +# CONFIG_TOUCHSCREEN_SIS_I2C is not set +# CONFIG_TOUCHSCREEN_ST1232 is not set +# CONFIG_TOUCHSCREEN_STMFTS is not set +# CONFIG_TOUCHSCREEN_STMPE is not set +# CONFIG_TOUCHSCREEN_SUN4I is not set +# CONFIG_TOUCHSCREEN_SUR40 is not set +# CONFIG_TOUCHSCREEN_SURFACE3_SPI is not set +# CONFIG_TOUCHSCREEN_SX8654 is not set +# CONFIG_TOUCHSCREEN_TI_AM335X_TSC is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +# CONFIG_TOUCHSCREEN_TS4800 is not set +# CONFIG_TOUCHSCREEN_TSC2004 is not set +# CONFIG_TOUCHSCREEN_TSC2005 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_TSC2007_IIO is not set +# CONFIG_TOUCHSCREEN_TSC200X_CORE is not set +# CONFIG_TOUCHSCREEN_TSC_SERIO is not set +# CONFIG_TOUCHSCREEN_UCB1400 is not set +# CONFIG_TOUCHSCREEN_USB_3M is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_USB_DMC_TSC10 is not set +# CONFIG_TOUCHSCREEN_USB_E2I is not set +# CONFIG_TOUCHSCREEN_USB_EASYTOUCH is not set +# CONFIG_TOUCHSCREEN_USB_EGALAX is not set +# CONFIG_TOUCHSCREEN_USB_ELO is not set +# CONFIG_TOUCHSCREEN_USB_ETT_TC45USB is not set +# CONFIG_TOUCHSCREEN_USB_ETURBO is not set +# CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH is not set +# CONFIG_TOUCHSCREEN_USB_GOTOP is not set +# CONFIG_TOUCHSCREEN_USB_GUNZE is not set +# CONFIG_TOUCHSCREEN_USB_IDEALTEK is not set +# CONFIG_TOUCHSCREEN_USB_IRTOUCH is not set +# CONFIG_TOUCHSCREEN_USB_ITM is not set +# CONFIG_TOUCHSCREEN_USB_JASTEC is not set +# CONFIG_TOUCHSCREEN_USB_NEXIO is not set +# CONFIG_TOUCHSCREEN_USB_PANJIT is not set +# CONFIG_TOUCHSCREEN_USB_ZYTRONIC is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_WACOM_I2C is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_WDT87XX_I2C is not set +# CONFIG_TOUCHSCREEN_WM831X is not set +# CONFIG_TOUCHSCREEN_WM9705 is not set +# CONFIG_TOUCHSCREEN_WM9712 is not set +# CONFIG_TOUCHSCREEN_WM9713 is not set +# CONFIG_TOUCHSCREEN_WM97XX is not set +# CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE is not set +# CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE is not set +# CONFIG_TOUCHSCREEN_ZET6223 is not set +# CONFIG_TOUCHSCREEN_ZFORCE is not set +# CONFIG_TOUCHSCREEN_ZINITIX is not set +# CONFIG_TPL0102 is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_TRACEPOINT_BENCHMARK is not set +# CONFIG_TRACER_SNAPSHOT is not set +# CONFIG_TRACER_SNAPSHOT_PER_CPU_SWAP is not set +# CONFIG_TRACE_BRANCH_PROFILING is not set +# CONFIG_TRACE_EVAL_MAP_FILE is not set +# CONFIG_TRACE_EVENT_INJECT is not set +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +# CONFIG_TRACE_MMIO_ACCESS is not set +# CONFIG_TRACE_SINK is not set +# CONFIG_TRACING_EVENTS_GPIO is not set +CONFIG_TRACING_SUPPORT=y +CONFIG_TRAD_SIGNALS=y +# CONFIG_TRANSPARENT_HUGEPAGE is not set +# CONFIG_TREE_RCU is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_TRIM_UNUSED_KSYMS is not set +# CONFIG_TRUSTED_FOUNDATIONS is not set +# CONFIG_TRUSTED_KEYS is not set +# CONFIG_TRUSTED_KEYS_CAAM is not set +# CONFIG_TRUSTED_KEYS_TPM is not set +# CONFIG_TRUSTED_KEYS_TEE is not set +# CONFIG_TSL2583 is not set +# CONFIG_TSL2591 is not set +# CONFIG_TSL2772 is not set +# CONFIG_TSL2x7x is not set +# CONFIG_TSL4531 is not set +# CONFIG_TSNEP is not set +# CONFIG_TSYS01 is not set +# CONFIG_TSYS02D is not set +# CONFIG_TTPCI_EEPROM is not set +CONFIG_TTY=y +# CONFIG_TTY_PRINTK is not set +# CONFIG_TUN is not set +# CONFIG_TUN_VNET_CROSS_LE is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL4030_MADC is not set +# CONFIG_TWL6030_GPADC is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_TXGBE is not set +# CONFIG_TYPEC is not set +# CONFIG_TYPEC_DP_ALTMODE is not set +# CONFIG_TYPEC_TCPM is not set +# CONFIG_TYPEC_UCSI is not set +# CONFIG_TYPHOON is not set +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_UBIFS_ATIME_SUPPORT is not set +# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set +# CONFIG_UBIFS_FS_AUTHENTICATION is not set +# CONFIG_UBIFS_FS_ENCRYPTION is not set +CONFIG_UBIFS_FS_LZO=y +# CONFIG_UBIFS_FS_SECURITY is not set +CONFIG_UBIFS_FS_XATTR=y +CONFIG_UBIFS_FS_ZLIB=y +CONFIG_UBIFS_FS_ZSTD=y +# CONFIG_UBSAN is not set +CONFIG_UBSAN_ALIGNMENT=y +CONFIG_UBSAN_BOOL=y +# CONFIG_UBSAN_DIV_ZERO is not set +CONFIG_UBSAN_ENUM=y +# CONFIG_UBSAN_MISC is not set +CONFIG_UBSAN_SHIFT=y +# CONFIG_UBSAN_UNREACHABLE is not set +# CONFIG_UCB1400_CORE is not set +# CONFIG_UCSI is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDMABUF is not set +CONFIG_UEVENT_HELPER=y +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +# CONFIG_UFS_FS is not set +# CONFIG_UHID is not set +CONFIG_UID16=y +# CONFIG_UIO is not set +# CONFIG_UIMAGE_FIT_BLK is not set +# CONFIG_ULTRA is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_UNICODE is not set +# CONFIG_UNISYSSPAR is not set +# CONFIG_UNISYS_VISORBUS is not set +CONFIG_UNIX=y +CONFIG_UNIX98_PTYS=y +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_UNIX_DIAG is not set +CONFIG_UNIX_SCM=y +# CONFIG_UNUSED_BOARD_FILES is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_UNWINDER_FRAME_POINTER is not set +# CONFIG_UPROBES is not set +# CONFIG_UPROBE_EVENTS is not set +# CONFIG_US5182D is not set +# CONFIG_USB is not set +# CONFIG_USB4 is not set +# CONFIG_USBIP_CORE is not set +CONFIG_USBIP_VHCI_HC_PORTS=8 +CONFIG_USBIP_VHCI_NR_HCS=1 +# CONFIG_USBIP_VUDC is not set +# CONFIG_USBPCWATCHDOG is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_AIRSPY is not set +CONFIG_USB_ALI_M5632=y +# CONFIG_USB_AMD5536UDC is not set +CONFIG_USB_AN2720=y +# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set +# CONFIG_USB_APPLEDISPLAY is not set +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARMLINUX=y +# CONFIG_USB_ATM is not set +# CONFIG_USB_AUDIO is not set +CONFIG_USB_AUTOSUSPEND_DELAY=2 +# CONFIG_USB_BDC_UDC is not set +CONFIG_USB_BELKIN=y +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_CDNS3 is not set +# CONFIG_USB_CDNS_SUPPORT is not set +# CONFIG_USB_CHAOSKEY is not set +# CONFIG_USB_CHIPIDEA is not set +# CONFIG_USB_CHIPIDEA_GENERIC is not set +# CONFIG_USB_CHIPIDEA_IMX is not set +# CONFIG_USB_CHIPIDEA_MSM is not set +# CONFIG_USB_CHIPIDEA_PCI is not set +# CONFIG_USB_CHIPIDEA_TEGRA is not set +# CONFIG_USB_CONFIGFS is not set +# CONFIG_USB_CONN_GPIO is not set +# CONFIG_USB_CXACRU is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +CONFIG_USB_DEFAULT_PERSIST=y +# CONFIG_USB_DSBR is not set +# CONFIG_USB_DUMMY_HCD is not set +# CONFIG_USB_DWC2 is not set +# CONFIG_USB_DWC2_DEBUG is not set +# CONFIG_USB_DWC2_DUAL_ROLE is not set +# CONFIG_USB_DWC2_HOST is not set +# CONFIG_USB_DWC2_PERIPHERAL is not set +# CONFIG_USB_DWC2_TRACK_MISSED_SOFS is not set +# CONFIG_USB_DWC3 is not set +# CONFIG_USB_DWC3_EXYNOS is not set +# CONFIG_USB_DWC3_HAPS is not set +# CONFIG_USB_DWC3_KEYSTONE is not set +# CONFIG_USB_DWC3_OF_SIMPLE is not set +# CONFIG_USB_DWC3_PCI is not set +# CONFIG_USB_DWC3_QCOM is not set +# CONFIG_USB_DWC3_ULPI is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_EG20T is not set +# CONFIG_USB_EHCI_ATH79 is not set +# CONFIG_USB_EHCI_FSL is not set +# CONFIG_USB_EHCI_HCD is not set +# CONFIG_USB_EHCI_HCD_AT91 is not set +# CONFIG_USB_EHCI_HCD_OMAP is not set +# CONFIG_USB_EHCI_HCD_PPC_OF is not set +# CONFIG_USB_EHCI_MSM is not set +# CONFIG_USB_EHCI_MV is not set +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_EHCI_TT_NEWSCHED=y +# CONFIG_USB_EHSET_TEST_FIXTURE is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EPSON2888 is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_EZUSB_FX2 is not set +# CONFIG_USB_FEW_INIT_RETRIES is not set +# CONFIG_USB_FOTG210_HCD is not set +# CONFIG_USB_FOTG210_UDC is not set +# CONFIG_USB_FSL_USB2 is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_FUNCTIONFS is not set +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_GADGET is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 +CONFIG_USB_GADGET_VBUS_DRAW=2 +# CONFIG_USB_GADGET_XILINX is not set +# CONFIG_USB_GL860 is not set +# CONFIG_USB_GOKU is not set +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_GR_UDC is not set +# CONFIG_USB_GSPCA is not set +# CONFIG_USB_GSPCA_BENQ is not set +# CONFIG_USB_GSPCA_CONEX is not set +# CONFIG_USB_GSPCA_CPIA1 is not set +# CONFIG_USB_GSPCA_DTCS033 is not set +# CONFIG_USB_GSPCA_ETOMS is not set +# CONFIG_USB_GSPCA_FINEPIX is not set +# CONFIG_USB_GSPCA_JEILINJ is not set +# CONFIG_USB_GSPCA_JL2005BCD is not set +# CONFIG_USB_GSPCA_KINECT is not set +# CONFIG_USB_GSPCA_KONICA is not set +# CONFIG_USB_GSPCA_MARS is not set +# CONFIG_USB_GSPCA_MR97310A is not set +# CONFIG_USB_GSPCA_NW80X is not set +# CONFIG_USB_GSPCA_OV519 is not set +# CONFIG_USB_GSPCA_OV534 is not set +# CONFIG_USB_GSPCA_OV534_9 is not set +# CONFIG_USB_GSPCA_PAC207 is not set +# CONFIG_USB_GSPCA_PAC7302 is not set +# CONFIG_USB_GSPCA_PAC7311 is not set +# CONFIG_USB_GSPCA_SE401 is not set +# CONFIG_USB_GSPCA_SN9C2028 is not set +# CONFIG_USB_GSPCA_SN9C20X is not set +# CONFIG_USB_GSPCA_SONIXB is not set +# CONFIG_USB_GSPCA_SONIXJ is not set +# CONFIG_USB_GSPCA_SPCA1528 is not set +# CONFIG_USB_GSPCA_SPCA500 is not set +# CONFIG_USB_GSPCA_SPCA501 is not set +# CONFIG_USB_GSPCA_SPCA505 is not set +# CONFIG_USB_GSPCA_SPCA506 is not set +# CONFIG_USB_GSPCA_SPCA508 is not set +# CONFIG_USB_GSPCA_SPCA561 is not set +# CONFIG_USB_GSPCA_SQ905 is not set +# CONFIG_USB_GSPCA_SQ905C is not set +# CONFIG_USB_GSPCA_SQ930X is not set +# CONFIG_USB_GSPCA_STK014 is not set +# CONFIG_USB_GSPCA_STK1135 is not set +# CONFIG_USB_GSPCA_STV0680 is not set +# CONFIG_USB_GSPCA_SUNPLUS is not set +# CONFIG_USB_GSPCA_T613 is not set +# CONFIG_USB_GSPCA_TOPRO is not set +# CONFIG_USB_GSPCA_TOUPTEK is not set +# CONFIG_USB_GSPCA_TV8532 is not set +# CONFIG_USB_GSPCA_VC032X is not set +# CONFIG_USB_GSPCA_VICAM is not set +# CONFIG_USB_GSPCA_XIRLINK_CIT is not set +# CONFIG_USB_GSPCA_ZC3XX is not set +# CONFIG_USB_G_ACM_MS is not set +# CONFIG_USB_G_DBGP is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_NCM is not set +# CONFIG_USB_G_NOKIA is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_WEBCAM is not set +# CONFIG_USB_HACKRF is not set +# CONFIG_USB_HCD_TEST_MODE is not set +# CONFIG_USB_HID is not set +# CONFIG_USB_HIDDEV is not set +# CONFIG_USB_HSIC_USB3503 is not set +# CONFIG_USB_HSIC_USB4604 is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_HUB_USB251XB is not set +# CONFIG_USB_HWA_HCD is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_IMX21_HCD is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_IPHETH is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1301 is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_ISP1760 is not set +# CONFIG_USB_ISP1760_HCD is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_KBD is not set +# CONFIG_USB_KC2190 is not set +# CONFIG_USB_LAN78XX is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_LEDS_TRIGGER_USBPORT is not set +# CONFIG_USB_LED_TRIG is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LGM_PHY is not set +# CONFIG_USB_LINK_LAYER_TEST is not set +# CONFIG_USB_M5602 is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_MASS_STORAGE is not set +# CONFIG_USB_MAX3420_UDC is not set +# CONFIG_USB_MAX3421_HCD is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_MIDI_GADGET is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_MOUSE is not set +# CONFIG_USB_MSI2500 is not set +# CONFIG_USB_MSM_OTG is not set +# CONFIG_USB_MTU3 is not set +# CONFIG_USB_MUSB_GADGET is not set +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_MUSB_HOST is not set +# CONFIG_USB_MV_U3D is not set +# CONFIG_USB_MV_UDC is not set +# CONFIG_USB_MXS_PHY is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_NET2280 is not set +# CONFIG_USB_NET_AQC111 is not set +# CONFIG_USB_NET_AX88179_178A is not set +# CONFIG_USB_NET_AX8817X is not set +# CONFIG_USB_NET_CDCETHER is not set +# CONFIG_USB_NET_CDC_EEM is not set +# CONFIG_USB_NET_CDC_MBIM is not set +# CONFIG_USB_NET_CDC_NCM is not set +# CONFIG_USB_NET_CDC_SUBSET is not set +# CONFIG_USB_NET_CH9200 is not set +# CONFIG_USB_NET_CX82310_ETH is not set +# CONFIG_USB_NET_DM9601 is not set +# CONFIG_USB_NET_DRIVERS is not set +# CONFIG_USB_NET_GL620A is not set +# CONFIG_USB_NET_HUAWEI_CDC_NCM is not set +# CONFIG_USB_NET_INT51X1 is not set +# CONFIG_USB_NET_KALMIA is not set +# CONFIG_USB_NET_MCS7830 is not set +# CONFIG_USB_NET_NET1080 is not set +# CONFIG_USB_NET_PLUSB is not set +# CONFIG_USB_NET_QMI_WWAN is not set +# CONFIG_USB_NET_RNDIS_HOST is not set +# CONFIG_USB_NET_RNDIS_WLAN is not set +# CONFIG_USB_NET_SMSC75XX is not set +# CONFIG_USB_NET_SMSC95XX is not set +# CONFIG_USB_NET_SR9700 is not set +# CONFIG_USB_NET_SR9800 is not set +# CONFIG_USB_NET_ZAURUS is not set +# CONFIG_USB_OHCI_HCD is not set +# CONFIG_USB_OHCI_HCD_PCI is not set +# CONFIG_USB_OHCI_HCD_PPC_OF is not set +# CONFIG_USB_OHCI_HCD_PPC_OF_BE is not set +# CONFIG_USB_OHCI_HCD_PPC_OF_LE is not set +# CONFIG_USB_OHCI_HCD_SSB is not set +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +# CONFIG_USB_ONBOARD_HUB is not set +# CONFIG_USB_OTG is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_OTG_DISABLE_EXTERNAL_HUB is not set +# CONFIG_USB_OTG_FSM is not set +# CONFIG_USB_OTG_PRODUCTLIST is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_PCI is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_PHY is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_PWC_INPUT_EVDEV is not set +# CONFIG_USB_PXA27X is not set +# CONFIG_USB_QCOM_EUD is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_RAW_GADGET is not set +# CONFIG_USB_RCAR_PHY is not set +# CONFIG_USB_RENESAS_USBHS is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_ROLES_INTEL_XHCI is not set +# CONFIG_USB_ROLE_SWITCH is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_RTL8152 is not set +# CONFIG_USB_RTL8153_ECM is not set +# CONFIG_USB_S2255 is not set +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_AIRCABLE is not set +# CONFIG_USB_SERIAL_ARK3116 is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_CH341 is not set +# CONFIG_USB_SERIAL_CP210X is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_CYPRESS_M8 is not set +# CONFIG_USB_SERIAL_DEBUG is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_EDGEPORT_TI is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_F81232 is not set +# CONFIG_USB_SERIAL_F8153X is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_GARMIN is not set +CONFIG_USB_SERIAL_GENERIC=y +# CONFIG_USB_SERIAL_IPAQ is not set +# CONFIG_USB_SERIAL_IPW is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_IUU is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +CONFIG_USB_SERIAL_KEYSPAN_MPR=y +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +CONFIG_USB_SERIAL_KEYSPAN_USA18X=y +CONFIG_USB_SERIAL_KEYSPAN_USA19=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QI=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QW=y +CONFIG_USB_SERIAL_KEYSPAN_USA19W=y +CONFIG_USB_SERIAL_KEYSPAN_USA28=y +CONFIG_USB_SERIAL_KEYSPAN_USA28X=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XA=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XB=y +CONFIG_USB_SERIAL_KEYSPAN_USA49W=y +CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y +# CONFIG_USB_SERIAL_KLSI is not set +# CONFIG_USB_SERIAL_KOBIL_SCT is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_METRO is not set +# CONFIG_USB_SERIAL_MOS7715_PARPORT is not set +# CONFIG_USB_SERIAL_MOS7720 is not set +# CONFIG_USB_SERIAL_MOS7840 is not set +# CONFIG_USB_SERIAL_MXUPORT is not set +# CONFIG_USB_SERIAL_NAVMAN is not set +# CONFIG_USB_SERIAL_OMNINET is not set +# CONFIG_USB_SERIAL_OPTICON is not set +# CONFIG_USB_SERIAL_OPTION is not set +# CONFIG_USB_SERIAL_OTI6858 is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_QCAUX is not set +# CONFIG_USB_SERIAL_QT2 is not set +# CONFIG_USB_SERIAL_QUALCOMM is not set +# CONFIG_USB_SERIAL_SAFE is not set +CONFIG_USB_SERIAL_SAFE_PADDED=y +# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set +# CONFIG_USB_SERIAL_SIMPLE is not set +# CONFIG_USB_SERIAL_SPCP8X5 is not set +# CONFIG_USB_SERIAL_SSU100 is not set +# CONFIG_USB_SERIAL_SYMBOL is not set +# CONFIG_USB_SERIAL_TI is not set +# CONFIG_USB_SERIAL_UPD78F0730 is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_WISHBONE is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SERIAL_XR is not set +# CONFIG_USB_SERIAL_XSENS_MT is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_SIERRA_NET is not set +# CONFIG_USB_SISUSBVGA is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_SNP_UDC_PLAT is not set +# CONFIG_USB_SPEEDTOUCH is not set +# CONFIG_USB_STKWEBCAM is not set +# CONFIG_USB_STORAGE is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_ENE_UB6250 is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_STORAGE_ONETOUCH is not set +# CONFIG_USB_STORAGE_REALTEK is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STV06XX is not set +# CONFIG_USB_SUPPORT is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_TMC is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_UAS is not set +# CONFIG_USB_UEAGLEATM is not set +# CONFIG_USB_ULPI is not set +# CONFIG_USB_ULPI_BUS is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_USS720 is not set +# CONFIG_USB_VIDEO_CLASS is not set +CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y +# CONFIG_USB_VL600 is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_WHCI_HCD is not set +# CONFIG_USB_WUSB is not set +# CONFIG_USB_WUSB_CBAF is not set +# CONFIG_USB_XEN_HCD is not set +# CONFIG_USB_XHCI_DBGCAP is not set +# CONFIG_USB_XHCI_HCD is not set +# CONFIG_USB_XHCI_MVEBU is not set +# CONFIG_USB_XHCI_PCI_RENESAS is not set +# CONFIG_USB_XUSBATM is not set +# CONFIG_USB_YUREX is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ZR364XX is not set +# CONFIG_USELIB is not set +# CONFIG_USERFAULTFD is not set +# CONFIG_USERIO is not set +# CONFIG_USER_DECRYPTED_DATA is not set +# CONFIG_USE_OF is not set +# CONFIG_UTS_NS is not set +# CONFIG_UWB is not set +# CONFIG_U_SERIAL_CONSOLE is not set +# CONFIG_V4L_MEM2MEM_DRIVERS is not set +# CONFIG_V4L_PLATFORM_DRIVERS is not set +# CONFIG_V4L_TEST_DRIVERS is not set +# CONFIG_VALIDATE_FS_PARSER is not set +# CONFIG_VBOXGUEST is not set +# CONFIG_VCNL3020 is not set +# CONFIG_VCNL4000 is not set +# CONFIG_VCNL4035 is not set +# CONFIG_VCPU_STALL_DETECTOR is not set +# CONFIG_VDPA is not set +CONFIG_VDSO=y +# CONFIG_VEML6030 is not set +# CONFIG_VEML6070 is not set +# CONFIG_VETH is not set +# CONFIG_VEXPRESS_CONFIG is not set +# CONFIG_VF610_ADC is not set +# CONFIG_VF610_DAC is not set +# CONFIG_VFAT_FS is not set +# CONFIG_VFIO is not set +# CONFIG_VFIO_FSL_MC is not set +# CONFIG_VFIO_PLATFORM is not set +# CONFIG_VGASTATE is not set +# CONFIG_VGA_ARB is not set +# CONFIG_VGA_CONSOLE is not set +# CONFIG_VGA_SWITCHEROO is not set +# CONFIG_VHOST_CROSS_ENDIAN_LEGACY is not set +CONFIG_VHOST_MENU=y +# CONFIG_VHOST_NET is not set +# CONFIG_VHOST_VSOCK is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_VIA_VELOCITY is not set +# CONFIG_VIDEO_AD5398 is not set +# CONFIG_VIDEO_AD5820 is not set +# CONFIG_VIDEO_AD9389B is not set +# CONFIG_VIDEO_ADP1653 is not set +# CONFIG_VIDEO_ADV7170 is not set +# CONFIG_VIDEO_ADV7175 is not set +# CONFIG_VIDEO_ADV7180 is not set +# CONFIG_VIDEO_ADV7183 is not set +# CONFIG_VIDEO_ADV7343 is not set +# CONFIG_VIDEO_ADV7393 is not set +# CONFIG_VIDEO_ADV748X is not set +# CONFIG_VIDEO_ADV7511 is not set +# CONFIG_VIDEO_ADV7604 is not set +# CONFIG_VIDEO_ADV7842 is not set +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_AK7375 is not set +# CONFIG_VIDEO_AK881X is not set +# CONFIG_VIDEO_AM437X_VPFE is not set +# CONFIG_VIDEO_AR0521 is not set +# CONFIG_VIDEO_ARDUCAM_64MP is not set +# CONFIG_VIDEO_ARDUCAM_PIVARIETY is not set +# CONFIG_VIDEO_ASPEED is not set +# CONFIG_VIDEO_ATMEL_ISC is not set +# CONFIG_VIDEO_ATMEL_ISI is not set +# CONFIG_VIDEO_AU0828 is not set +# CONFIG_VIDEO_BCM2835 is not set +# CONFIG_VIDEO_BCM2835_UNICAM is not set +# CONFIG_VIDEO_BT819 is not set +# CONFIG_VIDEO_BT848 is not set +# CONFIG_VIDEO_BT856 is not set +# CONFIG_VIDEO_BT866 is not set +# CONFIG_VIDEO_BU64754 is not set +# CONFIG_VIDEO_CADENCE is not set +# CONFIG_VIDEO_CADENCE_CSI2RX is not set +# CONFIG_VIDEO_CADENCE_CSI2TX is not set +# CONFIG_VIDEO_CAFE_CCIC is not set +# CONFIG_VIDEO_CAMERA_SENSOR is not set +# CONFIG_VIDEO_CCS is not set +# CONFIG_VIDEO_COBALT is not set +# CONFIG_VIDEO_CODA is not set +# CONFIG_VIDEO_CODEC_BCM2835 is not set +# CONFIG_VIDEO_CS3308 is not set +# CONFIG_VIDEO_CS5345 is not set +# CONFIG_VIDEO_CS53L32A is not set +# CONFIG_VIDEO_CX231XX is not set +# CONFIG_VIDEO_CX2341X is not set +# CONFIG_VIDEO_CX25821 is not set +# CONFIG_VIDEO_CX25840 is not set +# CONFIG_VIDEO_CX88 is not set +# CONFIG_VIDEO_DEV is not set +# CONFIG_VIDEO_DM6446_CCDC is not set +# CONFIG_VIDEO_DT3155 is not set +# CONFIG_VIDEO_DW9714 is not set +# CONFIG_VIDEO_DW9768 is not set +# CONFIG_VIDEO_DW9807_VCM is not set +# CONFIG_VIDEO_EM28XX is not set +# CONFIG_VIDEO_ET8EK8 is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +# CONFIG_VIDEO_GO7007 is not set +# CONFIG_VIDEO_GS1662 is not set +# CONFIG_VIDEO_HDPVR is not set +# CONFIG_VIDEO_HEXIUM_GEMINI is not set +# CONFIG_VIDEO_HEXIUM_ORION is not set +# CONFIG_VIDEO_HI556 is not set +# CONFIG_VIDEO_HI846 is not set +# CONFIG_VIDEO_HI847 is not set +# CONFIG_VIDEO_I2C is not set +# CONFIG_VIDEO_IMX208 is not set +# CONFIG_VIDEO_IMX214 is not set +# CONFIG_VIDEO_IMX219 is not set +# CONFIG_VIDEO_IMX258 is not set +# CONFIG_VIDEO_IMX274 is not set +# CONFIG_VIDEO_IMX290 is not set +# CONFIG_VIDEO_IMX296 is not set +# CONFIG_VIDEO_IMX319 is not set +# CONFIG_VIDEO_IMX334 is not set +# CONFIG_VIDEO_IMX335 is not set +# CONFIG_VIDEO_IMX355 is not set +# CONFIG_VIDEO_IMX412 is not set +# CONFIG_VIDEO_IMX477 is not set +# CONFIG_VIDEO_IMX519 is not set +# CONFIG_VIDEO_IMX708 is not set +# CONFIG_VIDEO_IMX8_JPEG is not set +# CONFIG_VIDEO_IMX_MIPI_CSIS is not set +# CONFIG_VIDEO_IMX_PXP is not set +# CONFIG_VIDEO_IRS1125 is not set +# CONFIG_VIDEO_IR_I2C is not set +# CONFIG_VIDEO_ISL7998X is not set +# CONFIG_VIDEO_ISP_BCM2835 is not set +# CONFIG_VIDEO_IVTV is not set +# CONFIG_VIDEO_KS0127 is not set +# CONFIG_VIDEO_LM3560 is not set +# CONFIG_VIDEO_LM3646 is not set +# CONFIG_VIDEO_M52790 is not set +# CONFIG_VIDEO_M5MOLS is not set +# CONFIG_VIDEO_MAX9286 is not set +# CONFIG_VIDEO_MEM2MEM_DEINTERLACE is not set +# CONFIG_VIDEO_ML86V7667 is not set +# CONFIG_VIDEO_MSP3400 is not set +# CONFIG_VIDEO_MT9M001 is not set +# CONFIG_VIDEO_MT9M032 is not set +# CONFIG_VIDEO_MT9M111 is not set +# CONFIG_VIDEO_MT9P031 is not set +# CONFIG_VIDEO_MT9T001 is not set +# CONFIG_VIDEO_MT9T112 is not set +# CONFIG_VIDEO_MT9V011 is not set +# CONFIG_VIDEO_MT9V032 is not set +# CONFIG_VIDEO_MT9V111 is not set +# CONFIG_VIDEO_MUX is not set +# CONFIG_VIDEO_MXB is not set +# CONFIG_VIDEO_NOON010PC30 is not set +# CONFIG_VIDEO_OG01A1B is not set +# CONFIG_VIDEO_OMAP2_VOUT is not set +# CONFIG_VIDEO_OV02A10 is not set +# CONFIG_VIDEO_OV08D10 is not set +# CONFIG_VIDEO_OV13858 is not set +# CONFIG_VIDEO_OV13B10 is not set +# CONFIG_VIDEO_OV2311 is not set +# CONFIG_VIDEO_OV2640 is not set +# CONFIG_VIDEO_OV2659 is not set +# CONFIG_VIDEO_OV2680 is not set +# CONFIG_VIDEO_OV2685 is not set +# CONFIG_VIDEO_OV2740 is not set +# CONFIG_VIDEO_OV5640 is not set +# CONFIG_VIDEO_OV5645 is not set +# CONFIG_VIDEO_OV5647 is not set +# CONFIG_VIDEO_OV5648 is not set +# CONFIG_VIDEO_OV5670 is not set +# CONFIG_VIDEO_OV5675 is not set +# CONFIG_VIDEO_OV5693 is not set +# CONFIG_VIDEO_OV5695 is not set +# CONFIG_VIDEO_OV64A40 is not set +# CONFIG_VIDEO_OV6650 is not set +# CONFIG_VIDEO_OV7251 is not set +# CONFIG_VIDEO_OV7640 is not set +# CONFIG_VIDEO_OV7670 is not set +# CONFIG_VIDEO_OV772X is not set +# CONFIG_VIDEO_OV7740 is not set +# CONFIG_VIDEO_OV8856 is not set +# CONFIG_VIDEO_OV8865 is not set +# CONFIG_VIDEO_OV9281 is not set +# CONFIG_VIDEO_OV9282 is not set +# CONFIG_VIDEO_OV9640 is not set +# CONFIG_VIDEO_OV9650 is not set +# CONFIG_VIDEO_OV9734 is not set +# CONFIG_VIDEO_PVRUSB2 is not set +# CONFIG_VIDEO_RASPBERRYPI_PISP_BE is not set +# CONFIG_VIDEO_RCAR_CSI2 is not set +# CONFIG_VIDEO_RCAR_ISP is not set +# CONFIG_VIDEO_RCAR_VIN is not set +# CONFIG_VIDEO_RDACM20 is not set +# CONFIG_VIDEO_RDACM21 is not set +# CONFIG_VIDEO_RJ54N1 is not set +# CONFIG_VIDEO_ROCKCHIP_ISP1 is not set +# CONFIG_VIDEO_RP1_CFE is not set +# CONFIG_VIDEO_S5C73M3 is not set +# CONFIG_VIDEO_S5K4ECGX is not set +# CONFIG_VIDEO_S5K5BAF is not set +# CONFIG_VIDEO_S5K6A3 is not set +# CONFIG_VIDEO_S5K6AA is not set +# CONFIG_VIDEO_SAA6588 is not set +# CONFIG_VIDEO_SAA6752HS is not set +# CONFIG_VIDEO_SAA7110 is not set +# CONFIG_VIDEO_SAA711X is not set +# CONFIG_VIDEO_SAA7127 is not set +# CONFIG_VIDEO_SAA7134 is not set +# CONFIG_VIDEO_SAA7164 is not set +# CONFIG_VIDEO_SAA717X is not set +# CONFIG_VIDEO_SAA7185 is not set +# CONFIG_VIDEO_SH_MOBILE_CEU is not set +# CONFIG_VIDEO_SMIAPP is not set +# CONFIG_VIDEO_SOLO6X10 is not set +# CONFIG_VIDEO_SONY_BTF_MPX is not set +# CONFIG_VIDEO_SUN4I_CSI is not set +# CONFIG_VIDEO_SUN6I_CSI is not set +# CONFIG_VIDEO_SUN8I_A83T_MIPI_CSI2 is not set +# CONFIG_VIDEO_SR030PC30 is not set +# CONFIG_VIDEO_STK1160_COMMON is not set +# CONFIG_VIDEO_ST_MIPID02 is not set +# CONFIG_VIDEO_TC358743 is not set +# CONFIG_VIDEO_TDA1997X is not set +# CONFIG_VIDEO_TDA7432 is not set +# CONFIG_VIDEO_TDA9840 is not set +# CONFIG_VIDEO_TEA6415C is not set +# CONFIG_VIDEO_TEA6420 is not set +# CONFIG_VIDEO_THS7303 is not set +# CONFIG_VIDEO_THS8200 is not set +# CONFIG_VIDEO_TIMBERDALE is not set +# CONFIG_VIDEO_TLV320AIC23B is not set +# CONFIG_VIDEO_TM6000 is not set +# CONFIG_VIDEO_TVAUDIO is not set +# CONFIG_VIDEO_TVP514X is not set +# CONFIG_VIDEO_TVP5150 is not set +# CONFIG_VIDEO_TVP7002 is not set +# CONFIG_VIDEO_TW2804 is not set +# CONFIG_VIDEO_TW5864 is not set +# CONFIG_VIDEO_TW68 is not set +# CONFIG_VIDEO_TW9903 is not set +# CONFIG_VIDEO_TW9906 is not set +# CONFIG_VIDEO_TW9910 is not set +# CONFIG_VIDEO_UDA1342 is not set +# CONFIG_VIDEO_UPD64031A is not set +# CONFIG_VIDEO_UPD64083 is not set +# CONFIG_VIDEO_USBTV is not set +# CONFIG_VIDEO_USBVISION is not set +# CONFIG_VIDEO_V4L2 is not set +# CONFIG_VIDEO_VP27SMPX is not set +# CONFIG_VIDEO_VPX3220 is not set +# CONFIG_VIDEO_VS6624 is not set +# CONFIG_VIDEO_WM8739 is not set +# CONFIG_VIDEO_WM8775 is not set +# CONFIG_VIDEO_XILINX is not set +# CONFIG_VIDEO_ZORAN is not set +# CONFIG_VIRTIO_BALLOON is not set +# CONFIG_VIRTIO_BLK_SCSI is not set +# CONFIG_VIRTIO_CONSOLE is not set +# CONFIG_VIRTIO_FS is not set +# CONFIG_VIRTIO_INPUT is not set +CONFIG_VIRTIO_MENU=y +# CONFIG_VIRTIO_MMIO is not set +# CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES is not set +# CONFIG_VIRTIO_PCI is not set +# CONFIG_VIRTUALIZATION is not set +# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set +# CONFIG_VIRT_DRIVERS is not set +CONFIG_VIRT_TO_BUS=y +# CONFIG_VITESSE_PHY is not set +# CONFIG_VL53L0X_I2C is not set +# CONFIG_VL6180 is not set +CONFIG_VLAN_8021Q=y +# CONFIG_VLAN_8021Q_GVRP is not set +# CONFIG_VLAN_8021Q_MVRP is not set +# CONFIG_VMAP_STACK is not set +# CONFIG_VME_BUS is not set +# CONFIG_VMLINUX_MAP is not set +# CONFIG_VMSPLIT_1G is not set +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_2G_OPT is not set +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_3G_OPT is not set +# CONFIG_VMWARE_PVSCSI is not set +# CONFIG_VMWARE_VMCI is not set +# CONFIG_VMXNET3 is not set +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_VOP_BUS is not set +# CONFIG_VORTEX is not set +# CONFIG_VSOCKETS is not set +# CONFIG_VSOCKETS_DIAG is not set +# CONFIG_VT is not set +# CONFIG_VT6655 is not set +# CONFIG_VT6656 is not set +# CONFIG_VXFS_FS is not set +# CONFIG_VXGE is not set +# CONFIG_VXLAN is not set +# CONFIG_VZ89X is not set +# CONFIG_W1 is not set +# CONFIG_W1_CON is not set +# CONFIG_W1_MASTER_DS1WM is not set +# CONFIG_W1_MASTER_DS2482 is not set +# CONFIG_W1_MASTER_DS2490 is not set +# CONFIG_W1_MASTER_GPIO is not set +# CONFIG_W1_MASTER_MATROX is not set +# CONFIG_W1_MASTER_SGI is not set +# CONFIG_W1_SLAVE_DS2405 is not set +# CONFIG_W1_SLAVE_DS2406 is not set +# CONFIG_W1_SLAVE_DS2408 is not set +# CONFIG_W1_SLAVE_DS2413 is not set +# CONFIG_W1_SLAVE_DS2423 is not set +# CONFIG_W1_SLAVE_DS2430 is not set +# CONFIG_W1_SLAVE_DS2431 is not set +# CONFIG_W1_SLAVE_DS2433 is not set +# CONFIG_W1_SLAVE_DS2438 is not set +# CONFIG_W1_SLAVE_DS250X is not set +# CONFIG_W1_SLAVE_DS2780 is not set +# CONFIG_W1_SLAVE_DS2781 is not set +# CONFIG_W1_SLAVE_DS2805 is not set +# CONFIG_W1_SLAVE_DS28E04 is not set +# CONFIG_W1_SLAVE_DS28E17 is not set +# CONFIG_W1_SLAVE_SMEM is not set +# CONFIG_W1_SLAVE_THERM is not set +# CONFIG_W83627HF_WDT is not set +# CONFIG_W83877F_WDT is not set +# CONFIG_W83977F_WDT is not set +# CONFIG_WAN is not set +# CONFIG_WANXL is not set +# CONFIG_WARN_ALL_UNSEEDED_RANDOM is not set +CONFIG_WATCHDOG=y +# CONFIG_WATCHDOG_CORE is not set +CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED=y +# CONFIG_WATCHDOG_HRTIMER_PRETIMEOUT is not set +# CONFIG_WATCHDOG_NOWAYOUT is not set +CONFIG_WATCHDOG_OPEN_TIMEOUT=0 +# CONFIG_WATCHDOG_PRETIMEOUT_GOV is not set +# CONFIG_WATCHDOG_SYSFS is not set +# CONFIG_WATCH_QUEUE is not set +# CONFIG_WD80x3 is not set +# CONFIG_WDAT_WDT is not set +# CONFIG_WDTPCI is not set +# CONFIG_WERROR is not set +# CONFIG_WEXT_CORE is not set +# CONFIG_WEXT_PRIV is not set +# CONFIG_WEXT_PROC is not set +# CONFIG_WEXT_SPY is not set +CONFIG_WILINK_PLATFORM_DATA=y +# CONFIG_WIMAX is not set +# CONFIG_WIREGUARD is not set +CONFIG_WIRELESS=y +# CONFIG_WIRELESS_EXT is not set +# CONFIG_WIRELESS_WDS is not set +# CONFIG_WIZNET_W5100 is not set +# CONFIG_WIZNET_W5300 is not set +# CONFIG_WL1251 is not set +# CONFIG_WL12XX is not set +# CONFIG_WL18XX is not set +CONFIG_WLAN=y +# CONFIG_WLAN_VENDOR_ADMTEK is not set +# CONFIG_WLAN_VENDOR_ATH is not set +# CONFIG_WLAN_VENDOR_ATMEL is not set +# CONFIG_WLAN_VENDOR_BROADCOM is not set +# CONFIG_WLAN_VENDOR_CISCO is not set +# CONFIG_WLAN_VENDOR_INTEL is not set +# CONFIG_WLAN_VENDOR_INTERSIL is not set +# CONFIG_WLAN_VENDOR_MARVELL is not set +# CONFIG_WLAN_VENDOR_MEDIATEK is not set +# CONFIG_WLAN_VENDOR_MICROCHIP is not set +# CONFIG_WLAN_VENDOR_PURELIFI is not set +# CONFIG_WLAN_VENDOR_QUANTENNA is not set +# CONFIG_WLAN_VENDOR_RALINK is not set +# CONFIG_WLAN_VENDOR_REALTEK is not set +# CONFIG_WLAN_VENDOR_RSI is not set +# CONFIG_WLAN_VENDOR_SILABS is not set +# CONFIG_WLAN_VENDOR_ST is not set +# CONFIG_WLAN_VENDOR_TI is not set +# CONFIG_WLAN_VENDOR_ZYDAS is not set +# CONFIG_WLCORE is not set +CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y +# CONFIG_WQ_WATCHDOG is not set +# CONFIG_WWAN is not set +# CONFIG_WWAN_HWSIM is not set +# CONFIG_WW_MUTEX_SELFTEST is not set +# CONFIG_X25 is not set +# CONFIG_X509_CERTIFICATE_PARSER is not set +# CONFIG_X86_PKG_TEMP_THERMAL is not set +CONFIG_X86_SYSFB=y +# CONFIG_XDP_SOCKETS is not set +# CONFIG_XEN is not set +# CONFIG_XEN_GRANT_DMA_ALLOC is not set +# CONFIG_XEN_PVCALLS_FRONTEND is not set +CONFIG_XEN_SCRUB_PAGES_DEFAULT=y +CONFIG_XFRM=y +# CONFIG_XFRM_INTERFACE is not set +# CONFIG_XFRM_IPCOMP is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_USER is not set +# CONFIG_XFS_DEBUG is not set +# CONFIG_XFS_FS is not set +# CONFIG_XFS_ONLINE_SCRUB is not set +# CONFIG_XFS_POSIX_ACL is not set +# CONFIG_XFS_QUOTA is not set +# CONFIG_XFS_RT is not set +# CONFIG_XFS_SUPPORT_V4 is not set +# CONFIG_XFS_WARN is not set +# CONFIG_XILINX_AXI_EMAC is not set +# CONFIG_XILINX_DMA is not set +# CONFIG_XILINX_EMACLITE is not set +# CONFIG_XILINX_GMII2RGMII is not set +# CONFIG_XILINX_INTC is not set +# CONFIG_XILINX_LL_TEMAC is not set +# CONFIG_XILINX_SDFEC is not set +# CONFIG_XILINX_VCU is not set +# CONFIG_XILINX_WATCHDOG is not set +# CONFIG_XILINX_XADC is not set +# CONFIG_XILINX_ZYNQMP_DMA is not set +# CONFIG_XILINX_ZYNQMP_DPDMA is not set +# CONFIG_XILLYBUS is not set +# CONFIG_XILLYUSB is not set +# CONFIG_XIL_AXIS_FIFO is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_XMON is not set +CONFIG_XZ_DEC=y +# CONFIG_XZ_DEC_ARM is not set +# CONFIG_XZ_DEC_ARMTHUMB is not set +# CONFIG_XZ_DEC_BCJ is not set +# CONFIG_XZ_DEC_IA64 is not set +# CONFIG_XZ_DEC_MICROLZMA is not set +# CONFIG_XZ_DEC_POWERPC is not set +# CONFIG_XZ_DEC_SPARC is not set +# CONFIG_XZ_DEC_TEST is not set +# CONFIG_XZ_DEC_X86 is not set +# CONFIG_YAM is not set +# CONFIG_YAMAHA_YAS530 is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_YENTA is not set +# CONFIG_YENTA_O2 is not set +# CONFIG_YENTA_RICOH is not set +# CONFIG_YENTA_TI is not set +# CONFIG_YENTA_TOSHIBA is not set +# CONFIG_ZBUD is not set +# CONFIG_ZD1211RW is not set +# CONFIG_ZD1211RW_DEBUG is not set +# CONFIG_ZEROPLUS_FF is not set +# CONFIG_ZERO_CALL_USED_REGS is not set +# CONFIG_ZIIRAVE_WATCHDOG is not set +# CONFIG_ZISOFS is not set +# CONFIG_ZLIB_DEFLATE is not set +# CONFIG_ZLIB_INFLATE is not set +CONFIG_ZONE_DMA=y +# CONFIG_ZOPT2201 is not set +# CONFIG_ZPA2326 is not set +# CONFIG_ZPOOL is not set +# CONFIG_ZRAM is not set +# CONFIG_ZRAM_DEF_COMP_842 is not set +# CONFIG_ZRAM_DEF_COMP_LZ4 is not set +# CONFIG_ZRAM_DEF_COMP_LZ4HC is not set +# CONFIG_ZRAM_DEF_COMP_LZO is not set +# CONFIG_ZRAM_DEF_COMP_LZORLE is not set +# CONFIG_ZRAM_DEF_COMP_ZSTD is not set +# CONFIG_ZRAM_MEMORY_TRACKING is not set +# CONFIG_ZSMALLOC is not set +# CONFIG_ZSWAP is not set +# CONFIG_ZX_TDM is not set diff --git a/target/linux/generic/hack-6.6/204-module_strip.patch b/target/linux/generic/hack-6.6/204-module_strip.patch new file mode 100644 index 0000000000..9fefd0bd6e --- /dev/null +++ b/target/linux/generic/hack-6.6/204-module_strip.patch @@ -0,0 +1,210 @@ +From a779a482fb9b9f8fcdf8b2519c789b4b9bb5dd05 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Fri, 7 Jul 2017 16:56:48 +0200 +Subject: build: add a hack for removing non-essential module info + +Signed-off-by: Felix Fietkau +--- + include/linux/module.h | 13 ++++++++----- + include/linux/moduleparam.h | 15 ++++++++++++--- + init/Kconfig | 7 +++++++ + kernel/module.c | 5 ++++- + scripts/mod/modpost.c | 12 ++++++++++++ + 5 files changed, 43 insertions(+), 9 deletions(-) + +--- a/include/linux/module.h ++++ b/include/linux/module.h +@@ -163,6 +163,7 @@ extern void cleanup_module(void); + + /* Generic info of form tag = "info" */ + #define MODULE_INFO(tag, info) __MODULE_INFO(tag, tag, info) ++#define MODULE_INFO_STRIP(tag, info) __MODULE_INFO_STRIP(tag, tag, info) + + /* For userspace: you can also call me... */ + #define MODULE_ALIAS(_alias) MODULE_INFO(alias, _alias) +@@ -232,12 +233,12 @@ extern void cleanup_module(void); + * Author(s), use "Name " or just "Name", for multiple + * authors use multiple MODULE_AUTHOR() statements/lines. + */ +-#define MODULE_AUTHOR(_author) MODULE_INFO(author, _author) ++#define MODULE_AUTHOR(_author) MODULE_INFO_STRIP(author, _author) + + /* What your module does. */ +-#define MODULE_DESCRIPTION(_description) MODULE_INFO(description, _description) ++#define MODULE_DESCRIPTION(_description) MODULE_INFO_STRIP(description, _description) + +-#ifdef MODULE ++#if defined(MODULE) && !defined(CONFIG_MODULE_STRIPPED) + /* Creates an alias so file2alias.c can find device table. */ + #define MODULE_DEVICE_TABLE(type, name) \ + extern typeof(name) __mod_##type##__##name##_device_table \ +@@ -264,7 +265,9 @@ extern typeof(name) __mod_##type##__##na + */ + + #if defined(MODULE) || !defined(CONFIG_SYSFS) +-#define MODULE_VERSION(_version) MODULE_INFO(version, _version) ++#define MODULE_VERSION(_version) MODULE_INFO_STRIP(version, _version) ++#elif defined(CONFIG_MODULE_STRIPPED) ++#define MODULE_VERSION(_version) __MODULE_INFO_DISABLED(version) + #else + #define MODULE_VERSION(_version) \ + MODULE_INFO(version, _version); \ +@@ -287,7 +290,7 @@ extern typeof(name) __mod_##type##__##na + /* Optional firmware file (or files) needed by the module + * format is simply firmware file name. Multiple firmware + * files require multiple MODULE_FIRMWARE() specifiers */ +-#define MODULE_FIRMWARE(_firmware) MODULE_INFO(firmware, _firmware) ++#define MODULE_FIRMWARE(_firmware) MODULE_INFO_STRIP(firmware, _firmware) + + #define MODULE_IMPORT_NS(ns) MODULE_INFO(import_ns, __stringify(ns)) + +--- a/include/linux/moduleparam.h ++++ b/include/linux/moduleparam.h +@@ -20,6 +20,16 @@ + /* Chosen so that structs with an unsigned long line up. */ + #define MAX_PARAM_PREFIX_LEN (64 - sizeof(unsigned long)) + ++/* This struct is here for syntactic coherency, it is not used */ ++#define __MODULE_INFO_DISABLED(name) \ ++ struct __UNIQUE_ID(name) {} ++ ++#ifdef CONFIG_MODULE_STRIPPED ++#define __MODULE_INFO_STRIP(tag, name, info) __MODULE_INFO_DISABLED(name) ++#else ++#define __MODULE_INFO_STRIP(tag, name, info) __MODULE_INFO(tag, name, info) ++#endif ++ + #define __MODULE_INFO(tag, name, info) \ + static const char __UNIQUE_ID(name)[] \ + __used __section(".modinfo") __aligned(1) \ +@@ -31,7 +41,7 @@ + /* One for each parameter, describing how to use it. Some files do + multiple of these per line, so can't just use MODULE_INFO. */ + #define MODULE_PARM_DESC(_parm, desc) \ +- __MODULE_INFO(parm, _parm, #_parm ":" desc) ++ __MODULE_INFO_STRIP(parm, _parm, #_parm ":" desc) + + struct kernel_param; + +--- a/kernel/module/Kconfig ++++ b/kernel/module/Kconfig +@@ -290,4 +290,11 @@ config MODULES_TREE_LOOKUP + def_bool y + depends on PERF_EVENTS || TRACING || CFI_CLANG + ++config MODULE_STRIPPED ++ bool "Reduce module size" ++ depends on MODULES ++ help ++ Remove module parameter descriptions, author info, version, aliases, ++ device tables, etc. ++ + endif # MODULES +--- a/kernel/module/main.c ++++ b/kernel/module/main.c +@@ -988,6 +988,7 @@ size_t modinfo_attrs_count = ARRAY_SIZE( + + static const char vermagic[] = VERMAGIC_STRING; + ++#if defined(CONFIG_MODVERSIONS) || !defined(CONFIG_MODULE_STRIPPED) + int try_to_force_load(struct module *mod, const char *reason) + { + #ifdef CONFIG_MODULE_FORCE_LOAD +@@ -999,6 +1000,7 @@ int try_to_force_load(struct module *mod + return -ENOEXEC; + #endif + } ++#endif + + static char *get_modinfo(const struct load_info *info, const char *tag); + static char *get_next_modinfo(const struct load_info *info, const char *tag, +@@ -1958,9 +1960,11 @@ static int setup_load_info(struct load_i + + static int check_modinfo(struct module *mod, struct load_info *info, int flags) + { +- const char *modmagic = get_modinfo(info, "vermagic"); + int err; + ++#ifndef CONFIG_MODULE_STRIPPED ++ const char *modmagic = get_modinfo(info, "vermagic"); ++ + if (flags & MODULE_INIT_IGNORE_VERMAGIC) + modmagic = NULL; + +@@ -1981,6 +1985,7 @@ static int check_modinfo(struct module * + mod->name); + add_taint_module(mod, TAINT_OOT_MODULE, LOCKDEP_STILL_OK); + } ++#endif + + check_modinfo_retpoline(mod, info); + +--- a/scripts/mod/modpost.c ++++ b/scripts/mod/modpost.c +@@ -1785,7 +1785,9 @@ static void read_symbols(const char *mod + symname = remove_dot(info.strtab + sym->st_name); + + handle_symbol(mod, &info, sym, symname); ++#ifndef CONFIG_MODULE_STRIPPED + handle_moddevtable(mod, &info, sym, symname); ++#endif + } + + for (sym = info.symtab_start; sym < info.symtab_stop; sym++) { +@@ -1948,8 +1950,10 @@ static void add_header(struct buffer *b, + buf_printf(b, "BUILD_SALT;\n"); + buf_printf(b, "BUILD_LTO_INFO;\n"); + buf_printf(b, "\n"); ++#ifndef CONFIG_MODULE_STRIPPED + buf_printf(b, "MODULE_INFO(vermagic, VERMAGIC_STRING);\n"); + buf_printf(b, "MODULE_INFO(name, KBUILD_MODNAME);\n"); ++#endif + buf_printf(b, "\n"); + buf_printf(b, "__visible struct module __this_module\n"); + buf_printf(b, "__section(\".gnu.linkonce.this_module\") = {\n"); +@@ -1963,8 +1967,10 @@ static void add_header(struct buffer *b, + buf_printf(b, "\t.arch = MODULE_ARCH_INIT,\n"); + buf_printf(b, "};\n"); + ++#ifndef CONFIG_MODULE_STRIPPED + if (!external_module) + buf_printf(b, "\nMODULE_INFO(intree, \"Y\");\n"); ++#endif + + buf_printf(b, + "\n" +@@ -1972,8 +1978,10 @@ static void add_header(struct buffer *b, + "MODULE_INFO(retpoline, \"Y\");\n" + "#endif\n"); + ++#ifndef CONFIG_MODULE_STRIPPED + if (strstarts(mod->name, "drivers/staging")) + buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n"); ++#endif + + if (strstarts(mod->name, "tools/testing")) + buf_printf(b, "\nMODULE_INFO(test, \"Y\");\n"); +@@ -2069,11 +2077,13 @@ static void add_depends(struct buffer *b + + static void add_srcversion(struct buffer *b, struct module *mod) + { ++#ifndef CONFIG_MODULE_STRIPPED + if (mod->srcversion[0]) { + buf_printf(b, "\n"); + buf_printf(b, "MODULE_INFO(srcversion, \"%s\");\n", + mod->srcversion); + } ++#endif + } + + static void write_buf(struct buffer *b, const char *fname) +@@ -2159,7 +2169,9 @@ static void write_mod_c_file(struct modu + add_exported_symbols(&buf, mod); + add_versions(&buf, mod); + add_depends(&buf, mod); ++#ifndef CONFIG_MODULE_STRIPPED + add_moddevtable(&buf, mod); ++#endif + add_srcversion(&buf, mod); + + ret = snprintf(fname, sizeof(fname), "%s.mod.c", mod->name); diff --git a/target/linux/generic/hack-6.6/205-kconfig-abort-configuration-on-unset-symbol.patch b/target/linux/generic/hack-6.6/205-kconfig-abort-configuration-on-unset-symbol.patch new file mode 100644 index 0000000000..df18507041 --- /dev/null +++ b/target/linux/generic/hack-6.6/205-kconfig-abort-configuration-on-unset-symbol.patch @@ -0,0 +1,41 @@ +From 310e8e04a05d9eb43fa9dd7f00143300afcaa37a Mon Sep 17 00:00:00 2001 +From: David Bauer +Date: Fri, 11 Nov 2022 13:33:44 +0100 +Subject: [PATCH] kconfig: abort configuration on unset symbol + +When a target configuration has unset Kconfig symbols, the build will +fail when OpenWrt is compiled with V=s and stdin is connected to a tty. + +In case OpenWrt is compiled without either of these preconditions, the +build will succeed with the symbols in question being unset. + +Modify the kernel configuration in a way it fails on unset symbols +regardless of the aforementioned preconditions. + +Signed-off-by: David Bauer +--- + scripts/kconfig/conf.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/scripts/kconfig/conf.c ++++ b/scripts/kconfig/conf.c +@@ -338,6 +338,9 @@ static int conf_askvalue(struct symbol * + } + /* fall through */ + default: ++ if (!tty_stdio && getenv("FAIL_ON_UNCONFIGURED")) { ++ exit(1); ++ } + fflush(stdout); + xfgets(line, sizeof(line), stdin); + break; +@@ -520,6 +523,9 @@ static int conf_choice(struct menu *menu + } + /* fall through */ + case oldaskconfig: ++ if (!tty_stdio && getenv("FAIL_ON_UNCONFIGURED")) { ++ exit(1); ++ } + fflush(stdout); + xfgets(line, sizeof(line), stdin); + strip(line); diff --git a/target/linux/generic/hack-6.6/210-darwin_scripts_include.patch b/target/linux/generic/hack-6.6/210-darwin_scripts_include.patch new file mode 100644 index 0000000000..c9612536de --- /dev/null +++ b/target/linux/generic/hack-6.6/210-darwin_scripts_include.patch @@ -0,0 +1,3053 @@ +From db7c30dcd9a0391bf13b62c9f91e144d762ef43a Mon Sep 17 00:00:00 2001 +From: Florian Fainelli +Date: Fri, 7 Jul 2017 17:00:49 +0200 +Subject: Add an OSX specific patch to make the kernel be compiled + +lede-commit: 3fc2a24f0422b2f55f9ed43f116db3111f700526 +Signed-off-by: Florian Fainelli +--- + scripts/kconfig/Makefile | 3 + + scripts/mod/elf.h | 3007 ++++++++++++++++++++++++++++++++++++++++++++ + scripts/mod/mk_elfconfig.c | 4 + + scripts/mod/modpost.h | 4 + + 4 files changed, 3018 insertions(+) + create mode 100644 scripts/mod/elf.h + +--- /dev/null ++++ b/scripts/mod/elf.h +@@ -0,0 +1,3007 @@ ++/* This file defines standard ELF types, structures, and macros. ++ Copyright (C) 1995-2012 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#ifndef _ELF_H ++#define _ELF_H 1 ++ ++/* Standard ELF types. */ ++ ++#include ++ ++/* Type for a 16-bit quantity. */ ++typedef uint16_t Elf32_Half; ++typedef uint16_t Elf64_Half; ++ ++/* Types for signed and unsigned 32-bit quantities. */ ++typedef uint32_t Elf32_Word; ++typedef int32_t Elf32_Sword; ++typedef uint32_t Elf64_Word; ++typedef int32_t Elf64_Sword; ++ ++/* Types for signed and unsigned 64-bit quantities. */ ++typedef uint64_t Elf32_Xword; ++typedef int64_t Elf32_Sxword; ++typedef uint64_t Elf64_Xword; ++typedef int64_t Elf64_Sxword; ++ ++/* Type of addresses. */ ++typedef uint32_t Elf32_Addr; ++typedef uint64_t Elf64_Addr; ++ ++/* Type of file offsets. */ ++typedef uint32_t Elf32_Off; ++typedef uint64_t Elf64_Off; ++ ++/* Type for section indices, which are 16-bit quantities. */ ++typedef uint16_t Elf32_Section; ++typedef uint16_t Elf64_Section; ++ ++/* Type for version symbol information. */ ++typedef Elf32_Half Elf32_Versym; ++typedef Elf64_Half Elf64_Versym; ++ ++ ++/* The ELF file header. This appears at the start of every ELF file. */ ++ ++#define EI_NIDENT (16) ++ ++typedef struct ++{ ++ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ ++ Elf32_Half e_type; /* Object file type */ ++ Elf32_Half e_machine; /* Architecture */ ++ Elf32_Word e_version; /* Object file version */ ++ Elf32_Addr e_entry; /* Entry point virtual address */ ++ Elf32_Off e_phoff; /* Program header table file offset */ ++ Elf32_Off e_shoff; /* Section header table file offset */ ++ Elf32_Word e_flags; /* Processor-specific flags */ ++ Elf32_Half e_ehsize; /* ELF header size in bytes */ ++ Elf32_Half e_phentsize; /* Program header table entry size */ ++ Elf32_Half e_phnum; /* Program header table entry count */ ++ Elf32_Half e_shentsize; /* Section header table entry size */ ++ Elf32_Half e_shnum; /* Section header table entry count */ ++ Elf32_Half e_shstrndx; /* Section header string table index */ ++} Elf32_Ehdr; ++ ++typedef struct ++{ ++ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ ++ Elf64_Half e_type; /* Object file type */ ++ Elf64_Half e_machine; /* Architecture */ ++ Elf64_Word e_version; /* Object file version */ ++ Elf64_Addr e_entry; /* Entry point virtual address */ ++ Elf64_Off e_phoff; /* Program header table file offset */ ++ Elf64_Off e_shoff; /* Section header table file offset */ ++ Elf64_Word e_flags; /* Processor-specific flags */ ++ Elf64_Half e_ehsize; /* ELF header size in bytes */ ++ Elf64_Half e_phentsize; /* Program header table entry size */ ++ Elf64_Half e_phnum; /* Program header table entry count */ ++ Elf64_Half e_shentsize; /* Section header table entry size */ ++ Elf64_Half e_shnum; /* Section header table entry count */ ++ Elf64_Half e_shstrndx; /* Section header string table index */ ++} Elf64_Ehdr; ++ ++/* Fields in the e_ident array. The EI_* macros are indices into the ++ array. The macros under each EI_* macro are the values the byte ++ may have. */ ++ ++#define EI_MAG0 0 /* File identification byte 0 index */ ++#define ELFMAG0 0x7f /* Magic number byte 0 */ ++ ++#define EI_MAG1 1 /* File identification byte 1 index */ ++#define ELFMAG1 'E' /* Magic number byte 1 */ ++ ++#define EI_MAG2 2 /* File identification byte 2 index */ ++#define ELFMAG2 'L' /* Magic number byte 2 */ ++ ++#define EI_MAG3 3 /* File identification byte 3 index */ ++#define ELFMAG3 'F' /* Magic number byte 3 */ ++ ++/* Conglomeration of the identification bytes, for easy testing as a word. */ ++#define ELFMAG "\177ELF" ++#define SELFMAG 4 ++ ++#define EI_CLASS 4 /* File class byte index */ ++#define ELFCLASSNONE 0 /* Invalid class */ ++#define ELFCLASS32 1 /* 32-bit objects */ ++#define ELFCLASS64 2 /* 64-bit objects */ ++#define ELFCLASSNUM 3 ++ ++#define EI_DATA 5 /* Data encoding byte index */ ++#define ELFDATANONE 0 /* Invalid data encoding */ ++#define ELFDATA2LSB 1 /* 2's complement, little endian */ ++#define ELFDATA2MSB 2 /* 2's complement, big endian */ ++#define ELFDATANUM 3 ++ ++#define EI_VERSION 6 /* File version byte index */ ++ /* Value must be EV_CURRENT */ ++ ++#define EI_OSABI 7 /* OS ABI identification */ ++#define ELFOSABI_NONE 0 /* UNIX System V ABI */ ++#define ELFOSABI_SYSV 0 /* Alias. */ ++#define ELFOSABI_HPUX 1 /* HP-UX */ ++#define ELFOSABI_NETBSD 2 /* NetBSD. */ ++#define ELFOSABI_GNU 3 /* Object uses GNU ELF extensions. */ ++#define ELFOSABI_LINUX ELFOSABI_GNU /* Compatibility alias. */ ++#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ ++#define ELFOSABI_AIX 7 /* IBM AIX. */ ++#define ELFOSABI_IRIX 8 /* SGI Irix. */ ++#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ ++#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ ++#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ ++#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ ++#define ELFOSABI_ARM_AEABI 64 /* ARM EABI */ ++#define ELFOSABI_ARM 97 /* ARM */ ++#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ ++ ++#define EI_ABIVERSION 8 /* ABI version */ ++ ++#define EI_PAD 9 /* Byte index of padding bytes */ ++ ++/* Legal values for e_type (object file type). */ ++ ++#define ET_NONE 0 /* No file type */ ++#define ET_REL 1 /* Relocatable file */ ++#define ET_EXEC 2 /* Executable file */ ++#define ET_DYN 3 /* Shared object file */ ++#define ET_CORE 4 /* Core file */ ++#define ET_NUM 5 /* Number of defined types */ ++#define ET_LOOS 0xfe00 /* OS-specific range start */ ++#define ET_HIOS 0xfeff /* OS-specific range end */ ++#define ET_LOPROC 0xff00 /* Processor-specific range start */ ++#define ET_HIPROC 0xffff /* Processor-specific range end */ ++ ++/* Legal values for e_machine (architecture). */ ++ ++#define EM_NONE 0 /* No machine */ ++#define EM_M32 1 /* AT&T WE 32100 */ ++#define EM_SPARC 2 /* SUN SPARC */ ++#define EM_386 3 /* Intel 80386 */ ++#define EM_68K 4 /* Motorola m68k family */ ++#define EM_88K 5 /* Motorola m88k family */ ++#define EM_860 7 /* Intel 80860 */ ++#define EM_MIPS 8 /* MIPS R3000 big-endian */ ++#define EM_S370 9 /* IBM System/370 */ ++#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ ++ ++#define EM_PARISC 15 /* HPPA */ ++#define EM_VPP500 17 /* Fujitsu VPP500 */ ++#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ ++#define EM_960 19 /* Intel 80960 */ ++#define EM_PPC 20 /* PowerPC */ ++#define EM_PPC64 21 /* PowerPC 64-bit */ ++#define EM_S390 22 /* IBM S390 */ ++ ++#define EM_V800 36 /* NEC V800 series */ ++#define EM_FR20 37 /* Fujitsu FR20 */ ++#define EM_RH32 38 /* TRW RH-32 */ ++#define EM_RCE 39 /* Motorola RCE */ ++#define EM_ARM 40 /* ARM */ ++#define EM_FAKE_ALPHA 41 /* Digital Alpha */ ++#define EM_SH 42 /* Hitachi SH */ ++#define EM_SPARCV9 43 /* SPARC v9 64-bit */ ++#define EM_TRICORE 44 /* Siemens Tricore */ ++#define EM_ARC 45 /* Argonaut RISC Core */ ++#define EM_H8_300 46 /* Hitachi H8/300 */ ++#define EM_H8_300H 47 /* Hitachi H8/300H */ ++#define EM_H8S 48 /* Hitachi H8S */ ++#define EM_H8_500 49 /* Hitachi H8/500 */ ++#define EM_IA_64 50 /* Intel Merced */ ++#define EM_MIPS_X 51 /* Stanford MIPS-X */ ++#define EM_COLDFIRE 52 /* Motorola Coldfire */ ++#define EM_68HC12 53 /* Motorola M68HC12 */ ++#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ ++#define EM_PCP 55 /* Siemens PCP */ ++#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ ++#define EM_NDR1 57 /* Denso NDR1 microprocessor */ ++#define EM_STARCORE 58 /* Motorola Start*Core processor */ ++#define EM_ME16 59 /* Toyota ME16 processor */ ++#define EM_ST100 60 /* STMicroelectronic ST100 processor */ ++#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ ++#define EM_X86_64 62 /* AMD x86-64 architecture */ ++#define EM_PDSP 63 /* Sony DSP Processor */ ++ ++#define EM_FX66 66 /* Siemens FX66 microcontroller */ ++#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ ++#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ ++#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ ++#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ ++#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ ++#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ ++#define EM_SVX 73 /* Silicon Graphics SVx */ ++#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ ++#define EM_VAX 75 /* Digital VAX */ ++#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ ++#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ ++#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ ++#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ ++#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ ++#define EM_HUANY 81 /* Harvard University machine-independent object files */ ++#define EM_PRISM 82 /* SiTera Prism */ ++#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ ++#define EM_FR30 84 /* Fujitsu FR30 */ ++#define EM_D10V 85 /* Mitsubishi D10V */ ++#define EM_D30V 86 /* Mitsubishi D30V */ ++#define EM_V850 87 /* NEC v850 */ ++#define EM_M32R 88 /* Mitsubishi M32R */ ++#define EM_MN10300 89 /* Matsushita MN10300 */ ++#define EM_MN10200 90 /* Matsushita MN10200 */ ++#define EM_PJ 91 /* picoJava */ ++#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ ++#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ ++#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ ++#define EM_TILEPRO 188 /* Tilera TILEPro */ ++#define EM_TILEGX 191 /* Tilera TILE-Gx */ ++#define EM_NUM 192 ++ ++/* If it is necessary to assign new unofficial EM_* values, please ++ pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the ++ chances of collision with official or non-GNU unofficial values. */ ++ ++#define EM_ALPHA 0x9026 ++ ++/* Legal values for e_version (version). */ ++ ++#define EV_NONE 0 /* Invalid ELF version */ ++#define EV_CURRENT 1 /* Current version */ ++#define EV_NUM 2 ++ ++/* Section header. */ ++ ++typedef struct ++{ ++ Elf32_Word sh_name; /* Section name (string tbl index) */ ++ Elf32_Word sh_type; /* Section type */ ++ Elf32_Word sh_flags; /* Section flags */ ++ Elf32_Addr sh_addr; /* Section virtual addr at execution */ ++ Elf32_Off sh_offset; /* Section file offset */ ++ Elf32_Word sh_size; /* Section size in bytes */ ++ Elf32_Word sh_link; /* Link to another section */ ++ Elf32_Word sh_info; /* Additional section information */ ++ Elf32_Word sh_addralign; /* Section alignment */ ++ Elf32_Word sh_entsize; /* Entry size if section holds table */ ++} Elf32_Shdr; ++ ++typedef struct ++{ ++ Elf64_Word sh_name; /* Section name (string tbl index) */ ++ Elf64_Word sh_type; /* Section type */ ++ Elf64_Xword sh_flags; /* Section flags */ ++ Elf64_Addr sh_addr; /* Section virtual addr at execution */ ++ Elf64_Off sh_offset; /* Section file offset */ ++ Elf64_Xword sh_size; /* Section size in bytes */ ++ Elf64_Word sh_link; /* Link to another section */ ++ Elf64_Word sh_info; /* Additional section information */ ++ Elf64_Xword sh_addralign; /* Section alignment */ ++ Elf64_Xword sh_entsize; /* Entry size if section holds table */ ++} Elf64_Shdr; ++ ++/* Special section indices. */ ++ ++#define SHN_UNDEF 0 /* Undefined section */ ++#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ ++#define SHN_LOPROC 0xff00 /* Start of processor-specific */ ++#define SHN_BEFORE 0xff00 /* Order section before all others ++ (Solaris). */ ++#define SHN_AFTER 0xff01 /* Order section after all others ++ (Solaris). */ ++#define SHN_HIPROC 0xff1f /* End of processor-specific */ ++#define SHN_LOOS 0xff20 /* Start of OS-specific */ ++#define SHN_HIOS 0xff3f /* End of OS-specific */ ++#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ ++#define SHN_COMMON 0xfff2 /* Associated symbol is common */ ++#define SHN_XINDEX 0xffff /* Index is in extra table. */ ++#define SHN_HIRESERVE 0xffff /* End of reserved indices */ ++ ++/* Legal values for sh_type (section type). */ ++ ++#define SHT_NULL 0 /* Section header table entry unused */ ++#define SHT_PROGBITS 1 /* Program data */ ++#define SHT_SYMTAB 2 /* Symbol table */ ++#define SHT_STRTAB 3 /* String table */ ++#define SHT_RELA 4 /* Relocation entries with addends */ ++#define SHT_HASH 5 /* Symbol hash table */ ++#define SHT_DYNAMIC 6 /* Dynamic linking information */ ++#define SHT_NOTE 7 /* Notes */ ++#define SHT_NOBITS 8 /* Program space with no data (bss) */ ++#define SHT_REL 9 /* Relocation entries, no addends */ ++#define SHT_SHLIB 10 /* Reserved */ ++#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ ++#define SHT_INIT_ARRAY 14 /* Array of constructors */ ++#define SHT_FINI_ARRAY 15 /* Array of destructors */ ++#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ ++#define SHT_GROUP 17 /* Section group */ ++#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ ++#define SHT_NUM 19 /* Number of defined types. */ ++#define SHT_LOOS 0x60000000 /* Start OS-specific. */ ++#define SHT_GNU_ATTRIBUTES 0x6ffffff5 /* Object attributes. */ ++#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */ ++#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ ++#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ ++#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ ++#define SHT_SUNW_move 0x6ffffffa ++#define SHT_SUNW_COMDAT 0x6ffffffb ++#define SHT_SUNW_syminfo 0x6ffffffc ++#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ ++#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ ++#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ ++#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ ++#define SHT_HIOS 0x6fffffff /* End OS-specific type */ ++#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ ++#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ ++#define SHT_LOUSER 0x80000000 /* Start of application-specific */ ++#define SHT_HIUSER 0x8fffffff /* End of application-specific */ ++ ++/* Legal values for sh_flags (section flags). */ ++ ++#define SHF_WRITE (1 << 0) /* Writable */ ++#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ ++#define SHF_EXECINSTR (1 << 2) /* Executable */ ++#define SHF_MERGE (1 << 4) /* Might be merged */ ++#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ ++#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ ++#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ ++#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling ++ required */ ++#define SHF_GROUP (1 << 9) /* Section is member of a group. */ ++#define SHF_TLS (1 << 10) /* Section hold thread-local data. */ ++#define SHF_MASKOS 0x0ff00000 /* OS-specific. */ ++#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ ++#define SHF_ORDERED (1 << 30) /* Special ordering requirement ++ (Solaris). */ ++#define SHF_EXCLUDE (1 << 31) /* Section is excluded unless ++ referenced or allocated (Solaris).*/ ++ ++/* Section group handling. */ ++#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ ++ ++/* Symbol table entry. */ ++ ++typedef struct ++{ ++ Elf32_Word st_name; /* Symbol name (string tbl index) */ ++ Elf32_Addr st_value; /* Symbol value */ ++ Elf32_Word st_size; /* Symbol size */ ++ unsigned char st_info; /* Symbol type and binding */ ++ unsigned char st_other; /* Symbol visibility */ ++ Elf32_Section st_shndx; /* Section index */ ++} Elf32_Sym; ++ ++typedef struct ++{ ++ Elf64_Word st_name; /* Symbol name (string tbl index) */ ++ unsigned char st_info; /* Symbol type and binding */ ++ unsigned char st_other; /* Symbol visibility */ ++ Elf64_Section st_shndx; /* Section index */ ++ Elf64_Addr st_value; /* Symbol value */ ++ Elf64_Xword st_size; /* Symbol size */ ++} Elf64_Sym; ++ ++/* The syminfo section if available contains additional information about ++ every dynamic symbol. */ ++ ++typedef struct ++{ ++ Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ ++ Elf32_Half si_flags; /* Per symbol flags */ ++} Elf32_Syminfo; ++ ++typedef struct ++{ ++ Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ ++ Elf64_Half si_flags; /* Per symbol flags */ ++} Elf64_Syminfo; ++ ++/* Possible values for si_boundto. */ ++#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ ++#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ ++#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ ++ ++/* Possible bitmasks for si_flags. */ ++#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ ++#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ ++#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ ++#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy ++ loaded */ ++/* Syminfo version values. */ ++#define SYMINFO_NONE 0 ++#define SYMINFO_CURRENT 1 ++#define SYMINFO_NUM 2 ++ ++ ++/* How to extract and insert information held in the st_info field. */ ++ ++#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) ++#define ELF32_ST_TYPE(val) ((val) & 0xf) ++#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) ++ ++/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ ++#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) ++#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) ++#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) ++ ++/* Legal values for ST_BIND subfield of st_info (symbol binding). */ ++ ++#define STB_LOCAL 0 /* Local symbol */ ++#define STB_GLOBAL 1 /* Global symbol */ ++#define STB_WEAK 2 /* Weak symbol */ ++#define STB_NUM 3 /* Number of defined types. */ ++#define STB_LOOS 10 /* Start of OS-specific */ ++#define STB_GNU_UNIQUE 10 /* Unique symbol. */ ++#define STB_HIOS 12 /* End of OS-specific */ ++#define STB_LOPROC 13 /* Start of processor-specific */ ++#define STB_HIPROC 15 /* End of processor-specific */ ++ ++/* Legal values for ST_TYPE subfield of st_info (symbol type). */ ++ ++#define STT_NOTYPE 0 /* Symbol type is unspecified */ ++#define STT_OBJECT 1 /* Symbol is a data object */ ++#define STT_FUNC 2 /* Symbol is a code object */ ++#define STT_SECTION 3 /* Symbol associated with a section */ ++#define STT_FILE 4 /* Symbol's name is file name */ ++#define STT_COMMON 5 /* Symbol is a common data object */ ++#define STT_TLS 6 /* Symbol is thread-local data object*/ ++#define STT_NUM 7 /* Number of defined types. */ ++#define STT_LOOS 10 /* Start of OS-specific */ ++#define STT_GNU_IFUNC 10 /* Symbol is indirect code object */ ++#define STT_HIOS 12 /* End of OS-specific */ ++#define STT_LOPROC 13 /* Start of processor-specific */ ++#define STT_HIPROC 15 /* End of processor-specific */ ++ ++ ++/* Symbol table indices are found in the hash buckets and chain table ++ of a symbol hash table section. This special index value indicates ++ the end of a chain, meaning no further symbols are found in that bucket. */ ++ ++#define STN_UNDEF 0 /* End of a chain. */ ++ ++ ++/* How to extract and insert information held in the st_other field. */ ++ ++#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) ++ ++/* For ELF64 the definitions are the same. */ ++#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) ++ ++/* Symbol visibility specification encoded in the st_other field. */ ++#define STV_DEFAULT 0 /* Default symbol visibility rules */ ++#define STV_INTERNAL 1 /* Processor specific hidden class */ ++#define STV_HIDDEN 2 /* Sym unavailable in other modules */ ++#define STV_PROTECTED 3 /* Not preemptible, not exported */ ++ ++ ++/* Relocation table entry without addend (in section of type SHT_REL). */ ++ ++typedef struct ++{ ++ Elf32_Addr r_offset; /* Address */ ++ Elf32_Word r_info; /* Relocation type and symbol index */ ++} Elf32_Rel; ++ ++/* I have seen two different definitions of the Elf64_Rel and ++ Elf64_Rela structures, so we'll leave them out until Novell (or ++ whoever) gets their act together. */ ++/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ ++ ++typedef struct ++{ ++ Elf64_Addr r_offset; /* Address */ ++ Elf64_Xword r_info; /* Relocation type and symbol index */ ++} Elf64_Rel; ++ ++/* Relocation table entry with addend (in section of type SHT_RELA). */ ++ ++typedef struct ++{ ++ Elf32_Addr r_offset; /* Address */ ++ Elf32_Word r_info; /* Relocation type and symbol index */ ++ Elf32_Sword r_addend; /* Addend */ ++} Elf32_Rela; ++ ++typedef struct ++{ ++ Elf64_Addr r_offset; /* Address */ ++ Elf64_Xword r_info; /* Relocation type and symbol index */ ++ Elf64_Sxword r_addend; /* Addend */ ++} Elf64_Rela; ++ ++/* How to extract and insert information held in the r_info field. */ ++ ++#define ELF32_R_SYM(val) ((val) >> 8) ++#define ELF32_R_TYPE(val) ((val) & 0xff) ++#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) ++ ++#define ELF64_R_SYM(i) ((i) >> 32) ++#define ELF64_R_TYPE(i) ((i) & 0xffffffff) ++#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) ++ ++/* Program segment header. */ ++ ++typedef struct ++{ ++ Elf32_Word p_type; /* Segment type */ ++ Elf32_Off p_offset; /* Segment file offset */ ++ Elf32_Addr p_vaddr; /* Segment virtual address */ ++ Elf32_Addr p_paddr; /* Segment physical address */ ++ Elf32_Word p_filesz; /* Segment size in file */ ++ Elf32_Word p_memsz; /* Segment size in memory */ ++ Elf32_Word p_flags; /* Segment flags */ ++ Elf32_Word p_align; /* Segment alignment */ ++} Elf32_Phdr; ++ ++typedef struct ++{ ++ Elf64_Word p_type; /* Segment type */ ++ Elf64_Word p_flags; /* Segment flags */ ++ Elf64_Off p_offset; /* Segment file offset */ ++ Elf64_Addr p_vaddr; /* Segment virtual address */ ++ Elf64_Addr p_paddr; /* Segment physical address */ ++ Elf64_Xword p_filesz; /* Segment size in file */ ++ Elf64_Xword p_memsz; /* Segment size in memory */ ++ Elf64_Xword p_align; /* Segment alignment */ ++} Elf64_Phdr; ++ ++/* Special value for e_phnum. This indicates that the real number of ++ program headers is too large to fit into e_phnum. Instead the real ++ value is in the field sh_info of section 0. */ ++ ++#define PN_XNUM 0xffff ++ ++/* Legal values for p_type (segment type). */ ++ ++#define PT_NULL 0 /* Program header table entry unused */ ++#define PT_LOAD 1 /* Loadable program segment */ ++#define PT_DYNAMIC 2 /* Dynamic linking information */ ++#define PT_INTERP 3 /* Program interpreter */ ++#define PT_NOTE 4 /* Auxiliary information */ ++#define PT_SHLIB 5 /* Reserved */ ++#define PT_PHDR 6 /* Entry for header table itself */ ++#define PT_TLS 7 /* Thread-local storage segment */ ++#define PT_NUM 8 /* Number of defined types */ ++#define PT_LOOS 0x60000000 /* Start of OS-specific */ ++#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ ++#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ ++#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */ ++#define PT_LOSUNW 0x6ffffffa ++#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ ++#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ ++#define PT_HISUNW 0x6fffffff ++#define PT_HIOS 0x6fffffff /* End of OS-specific */ ++#define PT_LOPROC 0x70000000 /* Start of processor-specific */ ++#define PT_HIPROC 0x7fffffff /* End of processor-specific */ ++ ++/* Legal values for p_flags (segment flags). */ ++ ++#define PF_X (1 << 0) /* Segment is executable */ ++#define PF_W (1 << 1) /* Segment is writable */ ++#define PF_R (1 << 2) /* Segment is readable */ ++#define PF_MASKOS 0x0ff00000 /* OS-specific */ ++#define PF_MASKPROC 0xf0000000 /* Processor-specific */ ++ ++/* Legal values for note segment descriptor types for core files. */ ++ ++#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ ++#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ ++#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ ++#define NT_PRXREG 4 /* Contains copy of prxregset struct */ ++#define NT_TASKSTRUCT 4 /* Contains copy of task structure */ ++#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ ++#define NT_AUXV 6 /* Contains copy of auxv array */ ++#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ ++#define NT_ASRS 8 /* Contains copy of asrset struct */ ++#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ ++#define NT_PSINFO 13 /* Contains copy of psinfo struct */ ++#define NT_PRCRED 14 /* Contains copy of prcred struct */ ++#define NT_UTSNAME 15 /* Contains copy of utsname struct */ ++#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ ++#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ ++#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct */ ++#define NT_PRXFPREG 0x46e62b7f /* Contains copy of user_fxsr_struct */ ++#define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */ ++#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */ ++#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ ++#define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */ ++#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */ ++#define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */ ++ ++/* Legal values for the note segment descriptor types for object files. */ ++ ++#define NT_VERSION 1 /* Contains a version string. */ ++ ++ ++/* Dynamic section entry. */ ++ ++typedef struct ++{ ++ Elf32_Sword d_tag; /* Dynamic entry type */ ++ union ++ { ++ Elf32_Word d_val; /* Integer value */ ++ Elf32_Addr d_ptr; /* Address value */ ++ } d_un; ++} Elf32_Dyn; ++ ++typedef struct ++{ ++ Elf64_Sxword d_tag; /* Dynamic entry type */ ++ union ++ { ++ Elf64_Xword d_val; /* Integer value */ ++ Elf64_Addr d_ptr; /* Address value */ ++ } d_un; ++} Elf64_Dyn; ++ ++/* Legal values for d_tag (dynamic entry type). */ ++ ++#define DT_NULL 0 /* Marks end of dynamic section */ ++#define DT_NEEDED 1 /* Name of needed library */ ++#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ ++#define DT_PLTGOT 3 /* Processor defined value */ ++#define DT_HASH 4 /* Address of symbol hash table */ ++#define DT_STRTAB 5 /* Address of string table */ ++#define DT_SYMTAB 6 /* Address of symbol table */ ++#define DT_RELA 7 /* Address of Rela relocs */ ++#define DT_RELASZ 8 /* Total size of Rela relocs */ ++#define DT_RELAENT 9 /* Size of one Rela reloc */ ++#define DT_STRSZ 10 /* Size of string table */ ++#define DT_SYMENT 11 /* Size of one symbol table entry */ ++#define DT_INIT 12 /* Address of init function */ ++#define DT_FINI 13 /* Address of termination function */ ++#define DT_SONAME 14 /* Name of shared object */ ++#define DT_RPATH 15 /* Library search path (deprecated) */ ++#define DT_SYMBOLIC 16 /* Start symbol search here */ ++#define DT_REL 17 /* Address of Rel relocs */ ++#define DT_RELSZ 18 /* Total size of Rel relocs */ ++#define DT_RELENT 19 /* Size of one Rel reloc */ ++#define DT_PLTREL 20 /* Type of reloc in PLT */ ++#define DT_DEBUG 21 /* For debugging; unspecified */ ++#define DT_TEXTREL 22 /* Reloc might modify .text */ ++#define DT_JMPREL 23 /* Address of PLT relocs */ ++#define DT_BIND_NOW 24 /* Process relocations of object */ ++#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ ++#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ ++#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ ++#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ ++#define DT_RUNPATH 29 /* Library search path */ ++#define DT_FLAGS 30 /* Flags for the object being loaded */ ++#define DT_ENCODING 32 /* Start of encoded range */ ++#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ ++#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ ++#define DT_NUM 34 /* Number used */ ++#define DT_LOOS 0x6000000d /* Start of OS-specific */ ++#define DT_HIOS 0x6ffff000 /* End of OS-specific */ ++#define DT_LOPROC 0x70000000 /* Start of processor-specific */ ++#define DT_HIPROC 0x7fffffff /* End of processor-specific */ ++#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ ++ ++/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the ++ Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's ++ approach. */ ++#define DT_VALRNGLO 0x6ffffd00 ++#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ ++#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ ++#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ ++#define DT_CHECKSUM 0x6ffffdf8 ++#define DT_PLTPADSZ 0x6ffffdf9 ++#define DT_MOVEENT 0x6ffffdfa ++#define DT_MOVESZ 0x6ffffdfb ++#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ ++#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting ++ the following DT_* entry. */ ++#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ ++#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ ++#define DT_VALRNGHI 0x6ffffdff ++#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ ++#define DT_VALNUM 12 ++ ++/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the ++ Dyn.d_un.d_ptr field of the Elf*_Dyn structure. ++ ++ If any adjustment is made to the ELF object after it has been ++ built these entries will need to be adjusted. */ ++#define DT_ADDRRNGLO 0x6ffffe00 ++#define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table. */ ++#define DT_TLSDESC_PLT 0x6ffffef6 ++#define DT_TLSDESC_GOT 0x6ffffef7 ++#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ ++#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ ++#define DT_CONFIG 0x6ffffefa /* Configuration information. */ ++#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ ++#define DT_AUDIT 0x6ffffefc /* Object auditing. */ ++#define DT_PLTPAD 0x6ffffefd /* PLT padding. */ ++#define DT_MOVETAB 0x6ffffefe /* Move table. */ ++#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ ++#define DT_ADDRRNGHI 0x6ffffeff ++#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ ++#define DT_ADDRNUM 11 ++ ++/* The versioning entry types. The next are defined as part of the ++ GNU extension. */ ++#define DT_VERSYM 0x6ffffff0 ++ ++#define DT_RELACOUNT 0x6ffffff9 ++#define DT_RELCOUNT 0x6ffffffa ++ ++/* These were chosen by Sun. */ ++#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ ++#define DT_VERDEF 0x6ffffffc /* Address of version definition ++ table */ ++#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ ++#define DT_VERNEED 0x6ffffffe /* Address of table with needed ++ versions */ ++#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ ++#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ ++#define DT_VERSIONTAGNUM 16 ++ ++/* Sun added these machine-independent extensions in the "processor-specific" ++ range. Be compatible. */ ++#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ ++#define DT_FILTER 0x7fffffff /* Shared object to get values from */ ++#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) ++#define DT_EXTRANUM 3 ++ ++/* Values of `d_un.d_val' in the DT_FLAGS entry. */ ++#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ ++#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ ++#define DF_TEXTREL 0x00000004 /* Object contains text relocations */ ++#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ ++#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */ ++ ++/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 ++ entry in the dynamic section. */ ++#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ ++#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ ++#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ ++#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ ++#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ ++#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ ++#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ ++#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ ++#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ ++#define DF_1_TRANS 0x00000200 ++#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ ++#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ ++#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ ++#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ ++#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ ++#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ ++#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ ++ ++/* Flags for the feature selection in DT_FEATURE_1. */ ++#define DTF_1_PARINIT 0x00000001 ++#define DTF_1_CONFEXP 0x00000002 ++ ++/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ ++#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ ++#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not ++ generally available. */ ++ ++/* Version definition sections. */ ++ ++typedef struct ++{ ++ Elf32_Half vd_version; /* Version revision */ ++ Elf32_Half vd_flags; /* Version information */ ++ Elf32_Half vd_ndx; /* Version Index */ ++ Elf32_Half vd_cnt; /* Number of associated aux entries */ ++ Elf32_Word vd_hash; /* Version name hash value */ ++ Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ ++ Elf32_Word vd_next; /* Offset in bytes to next verdef ++ entry */ ++} Elf32_Verdef; ++ ++typedef struct ++{ ++ Elf64_Half vd_version; /* Version revision */ ++ Elf64_Half vd_flags; /* Version information */ ++ Elf64_Half vd_ndx; /* Version Index */ ++ Elf64_Half vd_cnt; /* Number of associated aux entries */ ++ Elf64_Word vd_hash; /* Version name hash value */ ++ Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ ++ Elf64_Word vd_next; /* Offset in bytes to next verdef ++ entry */ ++} Elf64_Verdef; ++ ++ ++/* Legal values for vd_version (version revision). */ ++#define VER_DEF_NONE 0 /* No version */ ++#define VER_DEF_CURRENT 1 /* Current version */ ++#define VER_DEF_NUM 2 /* Given version number */ ++ ++/* Legal values for vd_flags (version information flags). */ ++#define VER_FLG_BASE 0x1 /* Version definition of file itself */ ++#define VER_FLG_WEAK 0x2 /* Weak version identifier */ ++ ++/* Versym symbol index values. */ ++#define VER_NDX_LOCAL 0 /* Symbol is local. */ ++#define VER_NDX_GLOBAL 1 /* Symbol is global. */ ++#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ ++#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ ++ ++/* Auxialiary version information. */ ++ ++typedef struct ++{ ++ Elf32_Word vda_name; /* Version or dependency names */ ++ Elf32_Word vda_next; /* Offset in bytes to next verdaux ++ entry */ ++} Elf32_Verdaux; ++ ++typedef struct ++{ ++ Elf64_Word vda_name; /* Version or dependency names */ ++ Elf64_Word vda_next; /* Offset in bytes to next verdaux ++ entry */ ++} Elf64_Verdaux; ++ ++ ++/* Version dependency section. */ ++ ++typedef struct ++{ ++ Elf32_Half vn_version; /* Version of structure */ ++ Elf32_Half vn_cnt; /* Number of associated aux entries */ ++ Elf32_Word vn_file; /* Offset of filename for this ++ dependency */ ++ Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ ++ Elf32_Word vn_next; /* Offset in bytes to next verneed ++ entry */ ++} Elf32_Verneed; ++ ++typedef struct ++{ ++ Elf64_Half vn_version; /* Version of structure */ ++ Elf64_Half vn_cnt; /* Number of associated aux entries */ ++ Elf64_Word vn_file; /* Offset of filename for this ++ dependency */ ++ Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ ++ Elf64_Word vn_next; /* Offset in bytes to next verneed ++ entry */ ++} Elf64_Verneed; ++ ++ ++/* Legal values for vn_version (version revision). */ ++#define VER_NEED_NONE 0 /* No version */ ++#define VER_NEED_CURRENT 1 /* Current version */ ++#define VER_NEED_NUM 2 /* Given version number */ ++ ++/* Auxiliary needed version information. */ ++ ++typedef struct ++{ ++ Elf32_Word vna_hash; /* Hash value of dependency name */ ++ Elf32_Half vna_flags; /* Dependency specific information */ ++ Elf32_Half vna_other; /* Unused */ ++ Elf32_Word vna_name; /* Dependency name string offset */ ++ Elf32_Word vna_next; /* Offset in bytes to next vernaux ++ entry */ ++} Elf32_Vernaux; ++ ++typedef struct ++{ ++ Elf64_Word vna_hash; /* Hash value of dependency name */ ++ Elf64_Half vna_flags; /* Dependency specific information */ ++ Elf64_Half vna_other; /* Unused */ ++ Elf64_Word vna_name; /* Dependency name string offset */ ++ Elf64_Word vna_next; /* Offset in bytes to next vernaux ++ entry */ ++} Elf64_Vernaux; ++ ++ ++/* Legal values for vna_flags. */ ++#define VER_FLG_WEAK 0x2 /* Weak version identifier */ ++ ++ ++/* Auxiliary vector. */ ++ ++/* This vector is normally only used by the program interpreter. The ++ usual definition in an ABI supplement uses the name auxv_t. The ++ vector is not usually defined in a standard file, but it ++ can't hurt. We rename it to avoid conflicts. The sizes of these ++ types are an arrangement between the exec server and the program ++ interpreter, so we don't fully specify them here. */ ++ ++typedef struct ++{ ++ uint32_t a_type; /* Entry type */ ++ union ++ { ++ uint32_t a_val; /* Integer value */ ++ /* We use to have pointer elements added here. We cannot do that, ++ though, since it does not work when using 32-bit definitions ++ on 64-bit platforms and vice versa. */ ++ } a_un; ++} Elf32_auxv_t; ++ ++typedef struct ++{ ++ uint64_t a_type; /* Entry type */ ++ union ++ { ++ uint64_t a_val; /* Integer value */ ++ /* We use to have pointer elements added here. We cannot do that, ++ though, since it does not work when using 32-bit definitions ++ on 64-bit platforms and vice versa. */ ++ } a_un; ++} Elf64_auxv_t; ++ ++/* Legal values for a_type (entry type). */ ++ ++#define AT_NULL 0 /* End of vector */ ++#define AT_IGNORE 1 /* Entry should be ignored */ ++#define AT_EXECFD 2 /* File descriptor of program */ ++#define AT_PHDR 3 /* Program headers for program */ ++#define AT_PHENT 4 /* Size of program header entry */ ++#define AT_PHNUM 5 /* Number of program headers */ ++#define AT_PAGESZ 6 /* System page size */ ++#define AT_BASE 7 /* Base address of interpreter */ ++#define AT_FLAGS 8 /* Flags */ ++#define AT_ENTRY 9 /* Entry point of program */ ++#define AT_NOTELF 10 /* Program is not ELF */ ++#define AT_UID 11 /* Real uid */ ++#define AT_EUID 12 /* Effective uid */ ++#define AT_GID 13 /* Real gid */ ++#define AT_EGID 14 /* Effective gid */ ++#define AT_CLKTCK 17 /* Frequency of times() */ ++ ++/* Some more special a_type values describing the hardware. */ ++#define AT_PLATFORM 15 /* String identifying platform. */ ++#define AT_HWCAP 16 /* Machine dependent hints about ++ processor capabilities. */ ++ ++/* This entry gives some information about the FPU initialization ++ performed by the kernel. */ ++#define AT_FPUCW 18 /* Used FPU control word. */ ++ ++/* Cache block sizes. */ ++#define AT_DCACHEBSIZE 19 /* Data cache block size. */ ++#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ ++#define AT_UCACHEBSIZE 21 /* Unified cache block size. */ ++ ++/* A special ignored value for PPC, used by the kernel to control the ++ interpretation of the AUXV. Must be > 16. */ ++#define AT_IGNOREPPC 22 /* Entry should be ignored. */ ++ ++#define AT_SECURE 23 /* Boolean, was exec setuid-like? */ ++ ++#define AT_BASE_PLATFORM 24 /* String identifying real platforms.*/ ++ ++#define AT_RANDOM 25 /* Address of 16 random bytes. */ ++ ++#define AT_EXECFN 31 /* Filename of executable. */ ++ ++/* Pointer to the global system page used for system calls and other ++ nice things. */ ++#define AT_SYSINFO 32 ++#define AT_SYSINFO_EHDR 33 ++ ++/* Shapes of the caches. Bits 0-3 contains associativity; bits 4-7 contains ++ log2 of line size; mask those to get cache size. */ ++#define AT_L1I_CACHESHAPE 34 ++#define AT_L1D_CACHESHAPE 35 ++#define AT_L2_CACHESHAPE 36 ++#define AT_L3_CACHESHAPE 37 ++ ++/* Note section contents. Each entry in the note section begins with ++ a header of a fixed form. */ ++ ++typedef struct ++{ ++ Elf32_Word n_namesz; /* Length of the note's name. */ ++ Elf32_Word n_descsz; /* Length of the note's descriptor. */ ++ Elf32_Word n_type; /* Type of the note. */ ++} Elf32_Nhdr; ++ ++typedef struct ++{ ++ Elf64_Word n_namesz; /* Length of the note's name. */ ++ Elf64_Word n_descsz; /* Length of the note's descriptor. */ ++ Elf64_Word n_type; /* Type of the note. */ ++} Elf64_Nhdr; ++ ++/* Known names of notes. */ ++ ++/* Solaris entries in the note section have this name. */ ++#define ELF_NOTE_SOLARIS "SUNW Solaris" ++ ++/* Note entries for GNU systems have this name. */ ++#define ELF_NOTE_GNU "GNU" ++ ++ ++/* Defined types of notes for Solaris. */ ++ ++/* Value of descriptor (one word) is desired pagesize for the binary. */ ++#define ELF_NOTE_PAGESIZE_HINT 1 ++ ++ ++/* Defined note types for GNU systems. */ ++ ++/* ABI information. The descriptor consists of words: ++ word 0: OS descriptor ++ word 1: major version of the ABI ++ word 2: minor version of the ABI ++ word 3: subminor version of the ABI ++*/ ++#define NT_GNU_ABI_TAG 1 ++#define ELF_NOTE_ABI NT_GNU_ABI_TAG /* Old name. */ ++ ++/* Known OSes. These values can appear in word 0 of an ++ NT_GNU_ABI_TAG note section entry. */ ++#define ELF_NOTE_OS_LINUX 0 ++#define ELF_NOTE_OS_GNU 1 ++#define ELF_NOTE_OS_SOLARIS2 2 ++#define ELF_NOTE_OS_FREEBSD 3 ++ ++/* Synthetic hwcap information. The descriptor begins with two words: ++ word 0: number of entries ++ word 1: bitmask of enabled entries ++ Then follow variable-length entries, one byte followed by a ++ '\0'-terminated hwcap name string. The byte gives the bit ++ number to test if enabled, (1U << bit) & bitmask. */ ++#define NT_GNU_HWCAP 2 ++ ++/* Build ID bits as generated by ld --build-id. ++ The descriptor consists of any nonzero number of bytes. */ ++#define NT_GNU_BUILD_ID 3 ++ ++/* Version note generated by GNU gold containing a version string. */ ++#define NT_GNU_GOLD_VERSION 4 ++ ++ ++/* Move records. */ ++typedef struct ++{ ++ Elf32_Xword m_value; /* Symbol value. */ ++ Elf32_Word m_info; /* Size and index. */ ++ Elf32_Word m_poffset; /* Symbol offset. */ ++ Elf32_Half m_repeat; /* Repeat count. */ ++ Elf32_Half m_stride; /* Stride info. */ ++} Elf32_Move; ++ ++typedef struct ++{ ++ Elf64_Xword m_value; /* Symbol value. */ ++ Elf64_Xword m_info; /* Size and index. */ ++ Elf64_Xword m_poffset; /* Symbol offset. */ ++ Elf64_Half m_repeat; /* Repeat count. */ ++ Elf64_Half m_stride; /* Stride info. */ ++} Elf64_Move; ++ ++/* Macro to construct move records. */ ++#define ELF32_M_SYM(info) ((info) >> 8) ++#define ELF32_M_SIZE(info) ((unsigned char) (info)) ++#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) ++ ++#define ELF64_M_SYM(info) ELF32_M_SYM (info) ++#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) ++#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) ++ ++ ++/* Motorola 68k specific definitions. */ ++ ++/* Values for Elf32_Ehdr.e_flags. */ ++#define EF_CPU32 0x00810000 ++ ++/* m68k relocs. */ ++ ++#define R_68K_NONE 0 /* No reloc */ ++#define R_68K_32 1 /* Direct 32 bit */ ++#define R_68K_16 2 /* Direct 16 bit */ ++#define R_68K_8 3 /* Direct 8 bit */ ++#define R_68K_PC32 4 /* PC relative 32 bit */ ++#define R_68K_PC16 5 /* PC relative 16 bit */ ++#define R_68K_PC8 6 /* PC relative 8 bit */ ++#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ ++#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ ++#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ ++#define R_68K_GOT32O 10 /* 32 bit GOT offset */ ++#define R_68K_GOT16O 11 /* 16 bit GOT offset */ ++#define R_68K_GOT8O 12 /* 8 bit GOT offset */ ++#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ ++#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ ++#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ ++#define R_68K_PLT32O 16 /* 32 bit PLT offset */ ++#define R_68K_PLT16O 17 /* 16 bit PLT offset */ ++#define R_68K_PLT8O 18 /* 8 bit PLT offset */ ++#define R_68K_COPY 19 /* Copy symbol at runtime */ ++#define R_68K_GLOB_DAT 20 /* Create GOT entry */ ++#define R_68K_JMP_SLOT 21 /* Create PLT entry */ ++#define R_68K_RELATIVE 22 /* Adjust by program base */ ++#define R_68K_TLS_GD32 25 /* 32 bit GOT offset for GD */ ++#define R_68K_TLS_GD16 26 /* 16 bit GOT offset for GD */ ++#define R_68K_TLS_GD8 27 /* 8 bit GOT offset for GD */ ++#define R_68K_TLS_LDM32 28 /* 32 bit GOT offset for LDM */ ++#define R_68K_TLS_LDM16 29 /* 16 bit GOT offset for LDM */ ++#define R_68K_TLS_LDM8 30 /* 8 bit GOT offset for LDM */ ++#define R_68K_TLS_LDO32 31 /* 32 bit module-relative offset */ ++#define R_68K_TLS_LDO16 32 /* 16 bit module-relative offset */ ++#define R_68K_TLS_LDO8 33 /* 8 bit module-relative offset */ ++#define R_68K_TLS_IE32 34 /* 32 bit GOT offset for IE */ ++#define R_68K_TLS_IE16 35 /* 16 bit GOT offset for IE */ ++#define R_68K_TLS_IE8 36 /* 8 bit GOT offset for IE */ ++#define R_68K_TLS_LE32 37 /* 32 bit offset relative to ++ static TLS block */ ++#define R_68K_TLS_LE16 38 /* 16 bit offset relative to ++ static TLS block */ ++#define R_68K_TLS_LE8 39 /* 8 bit offset relative to ++ static TLS block */ ++#define R_68K_TLS_DTPMOD32 40 /* 32 bit module number */ ++#define R_68K_TLS_DTPREL32 41 /* 32 bit module-relative offset */ ++#define R_68K_TLS_TPREL32 42 /* 32 bit TP-relative offset */ ++/* Keep this the last entry. */ ++#define R_68K_NUM 43 ++ ++/* Intel 80386 specific definitions. */ ++ ++/* i386 relocs. */ ++ ++#define R_386_NONE 0 /* No reloc */ ++#define R_386_32 1 /* Direct 32 bit */ ++#define R_386_PC32 2 /* PC relative 32 bit */ ++#define R_386_GOT32 3 /* 32 bit GOT entry */ ++#define R_386_PLT32 4 /* 32 bit PLT address */ ++#define R_386_COPY 5 /* Copy symbol at runtime */ ++#define R_386_GLOB_DAT 6 /* Create GOT entry */ ++#define R_386_JMP_SLOT 7 /* Create PLT entry */ ++#define R_386_RELATIVE 8 /* Adjust by program base */ ++#define R_386_GOTOFF 9 /* 32 bit offset to GOT */ ++#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ ++#define R_386_32PLT 11 ++#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */ ++#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS ++ block offset */ ++#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block ++ offset */ ++#define R_386_TLS_LE 17 /* Offset relative to static TLS ++ block */ ++#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of ++ general dynamic thread local data */ ++#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of ++ local dynamic thread local data ++ in LE code */ ++#define R_386_16 20 ++#define R_386_PC16 21 ++#define R_386_8 22 ++#define R_386_PC8 23 ++#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic ++ thread local data */ ++#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */ ++#define R_386_TLS_GD_CALL 26 /* Relocation for call to ++ __tls_get_addr() */ ++#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */ ++#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic ++ thread local data in LE code */ ++#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */ ++#define R_386_TLS_LDM_CALL 30 /* Relocation for call to ++ __tls_get_addr() in LDM code */ ++#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */ ++#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */ ++#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS ++ block offset */ ++#define R_386_TLS_LE_32 34 /* Negated offset relative to static ++ TLS block */ ++#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */ ++#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */ ++#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */ ++/* 38? */ ++#define R_386_TLS_GOTDESC 39 /* GOT offset for TLS descriptor. */ ++#define R_386_TLS_DESC_CALL 40 /* Marker of call through TLS ++ descriptor for ++ relaxation. */ ++#define R_386_TLS_DESC 41 /* TLS descriptor containing ++ pointer to code and to ++ argument, returning the TLS ++ offset for the symbol. */ ++#define R_386_IRELATIVE 42 /* Adjust indirectly by program base */ ++/* Keep this the last entry. */ ++#define R_386_NUM 43 ++ ++/* SUN SPARC specific definitions. */ ++ ++/* Legal values for ST_TYPE subfield of st_info (symbol type). */ ++ ++#define STT_SPARC_REGISTER 13 /* Global register reserved to app. */ ++ ++/* Values for Elf64_Ehdr.e_flags. */ ++ ++#define EF_SPARCV9_MM 3 ++#define EF_SPARCV9_TSO 0 ++#define EF_SPARCV9_PSO 1 ++#define EF_SPARCV9_RMO 2 ++#define EF_SPARC_LEDATA 0x800000 /* little endian data */ ++#define EF_SPARC_EXT_MASK 0xFFFF00 ++#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ ++#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ ++#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ ++#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ ++ ++/* SPARC relocs. */ ++ ++#define R_SPARC_NONE 0 /* No reloc */ ++#define R_SPARC_8 1 /* Direct 8 bit */ ++#define R_SPARC_16 2 /* Direct 16 bit */ ++#define R_SPARC_32 3 /* Direct 32 bit */ ++#define R_SPARC_DISP8 4 /* PC relative 8 bit */ ++#define R_SPARC_DISP16 5 /* PC relative 16 bit */ ++#define R_SPARC_DISP32 6 /* PC relative 32 bit */ ++#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ ++#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ ++#define R_SPARC_HI22 9 /* High 22 bit */ ++#define R_SPARC_22 10 /* Direct 22 bit */ ++#define R_SPARC_13 11 /* Direct 13 bit */ ++#define R_SPARC_LO10 12 /* Truncated 10 bit */ ++#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ ++#define R_SPARC_GOT13 14 /* 13 bit GOT entry */ ++#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ ++#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ ++#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ ++#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ ++#define R_SPARC_COPY 19 /* Copy symbol at runtime */ ++#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ ++#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ ++#define R_SPARC_RELATIVE 22 /* Adjust by program base */ ++#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ ++ ++/* Additional Sparc64 relocs. */ ++ ++#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ ++#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ ++#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ ++#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ ++#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ ++#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ ++#define R_SPARC_10 30 /* Direct 10 bit */ ++#define R_SPARC_11 31 /* Direct 11 bit */ ++#define R_SPARC_64 32 /* Direct 64 bit */ ++#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */ ++#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ ++#define R_SPARC_HM10 35 /* High middle 10 bits of ... */ ++#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ ++#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ ++#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ ++#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */ ++#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ ++#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ ++#define R_SPARC_GLOB_JMP 42 /* was part of v9 ABI but was removed */ ++#define R_SPARC_7 43 /* Direct 7 bit */ ++#define R_SPARC_5 44 /* Direct 5 bit */ ++#define R_SPARC_6 45 /* Direct 6 bit */ ++#define R_SPARC_DISP64 46 /* PC relative 64 bit */ ++#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ ++#define R_SPARC_HIX22 48 /* High 22 bit complemented */ ++#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ ++#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ ++#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ ++#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ ++#define R_SPARC_REGISTER 53 /* Global register usage */ ++#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ ++#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ ++#define R_SPARC_TLS_GD_HI22 56 ++#define R_SPARC_TLS_GD_LO10 57 ++#define R_SPARC_TLS_GD_ADD 58 ++#define R_SPARC_TLS_GD_CALL 59 ++#define R_SPARC_TLS_LDM_HI22 60 ++#define R_SPARC_TLS_LDM_LO10 61 ++#define R_SPARC_TLS_LDM_ADD 62 ++#define R_SPARC_TLS_LDM_CALL 63 ++#define R_SPARC_TLS_LDO_HIX22 64 ++#define R_SPARC_TLS_LDO_LOX10 65 ++#define R_SPARC_TLS_LDO_ADD 66 ++#define R_SPARC_TLS_IE_HI22 67 ++#define R_SPARC_TLS_IE_LO10 68 ++#define R_SPARC_TLS_IE_LD 69 ++#define R_SPARC_TLS_IE_LDX 70 ++#define R_SPARC_TLS_IE_ADD 71 ++#define R_SPARC_TLS_LE_HIX22 72 ++#define R_SPARC_TLS_LE_LOX10 73 ++#define R_SPARC_TLS_DTPMOD32 74 ++#define R_SPARC_TLS_DTPMOD64 75 ++#define R_SPARC_TLS_DTPOFF32 76 ++#define R_SPARC_TLS_DTPOFF64 77 ++#define R_SPARC_TLS_TPOFF32 78 ++#define R_SPARC_TLS_TPOFF64 79 ++#define R_SPARC_GOTDATA_HIX22 80 ++#define R_SPARC_GOTDATA_LOX10 81 ++#define R_SPARC_GOTDATA_OP_HIX22 82 ++#define R_SPARC_GOTDATA_OP_LOX10 83 ++#define R_SPARC_GOTDATA_OP 84 ++#define R_SPARC_H34 85 ++#define R_SPARC_SIZE32 86 ++#define R_SPARC_SIZE64 87 ++#define R_SPARC_WDISP10 88 ++#define R_SPARC_JMP_IREL 248 ++#define R_SPARC_IRELATIVE 249 ++#define R_SPARC_GNU_VTINHERIT 250 ++#define R_SPARC_GNU_VTENTRY 251 ++#define R_SPARC_REV32 252 ++/* Keep this the last entry. */ ++#define R_SPARC_NUM 253 ++ ++/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ ++ ++#define DT_SPARC_REGISTER 0x70000001 ++#define DT_SPARC_NUM 2 ++ ++/* MIPS R3000 specific definitions. */ ++ ++/* Legal values for e_flags field of Elf32_Ehdr. */ ++ ++#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */ ++#define EF_MIPS_PIC 2 /* Contains PIC code */ ++#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */ ++#define EF_MIPS_XGOT 8 ++#define EF_MIPS_64BIT_WHIRL 16 ++#define EF_MIPS_ABI2 32 ++#define EF_MIPS_ABI_ON32 64 ++#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */ ++ ++/* Legal values for MIPS architecture level. */ ++ ++#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ ++#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ ++#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ ++#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ ++#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ ++#define EF_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ ++#define EF_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ ++ ++/* The following are non-official names and should not be used. */ ++ ++#define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ ++#define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ ++#define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ ++#define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ ++#define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ ++#define E_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ ++#define E_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ ++ ++/* Special section indices. */ ++ ++#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */ ++#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ ++#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ ++#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */ ++#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */ ++ ++/* Legal values for sh_type field of Elf32_Shdr. */ ++ ++#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */ ++#define SHT_MIPS_MSYM 0x70000001 ++#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */ ++#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */ ++#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ ++#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/ ++#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */ ++#define SHT_MIPS_PACKAGE 0x70000007 ++#define SHT_MIPS_PACKSYM 0x70000008 ++#define SHT_MIPS_RELD 0x70000009 ++#define SHT_MIPS_IFACE 0x7000000b ++#define SHT_MIPS_CONTENT 0x7000000c ++#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ ++#define SHT_MIPS_SHDR 0x70000010 ++#define SHT_MIPS_FDESC 0x70000011 ++#define SHT_MIPS_EXTSYM 0x70000012 ++#define SHT_MIPS_DENSE 0x70000013 ++#define SHT_MIPS_PDESC 0x70000014 ++#define SHT_MIPS_LOCSYM 0x70000015 ++#define SHT_MIPS_AUXSYM 0x70000016 ++#define SHT_MIPS_OPTSYM 0x70000017 ++#define SHT_MIPS_LOCSTR 0x70000018 ++#define SHT_MIPS_LINE 0x70000019 ++#define SHT_MIPS_RFDESC 0x7000001a ++#define SHT_MIPS_DELTASYM 0x7000001b ++#define SHT_MIPS_DELTAINST 0x7000001c ++#define SHT_MIPS_DELTACLASS 0x7000001d ++#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ ++#define SHT_MIPS_DELTADECL 0x7000001f ++#define SHT_MIPS_SYMBOL_LIB 0x70000020 ++#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ ++#define SHT_MIPS_TRANSLATE 0x70000022 ++#define SHT_MIPS_PIXIE 0x70000023 ++#define SHT_MIPS_XLATE 0x70000024 ++#define SHT_MIPS_XLATE_DEBUG 0x70000025 ++#define SHT_MIPS_WHIRL 0x70000026 ++#define SHT_MIPS_EH_REGION 0x70000027 ++#define SHT_MIPS_XLATE_OLD 0x70000028 ++#define SHT_MIPS_PDR_EXCEPTION 0x70000029 ++ ++/* Legal values for sh_flags field of Elf32_Shdr. */ ++ ++#define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */ ++#define SHF_MIPS_MERGE 0x20000000 ++#define SHF_MIPS_ADDR 0x40000000 ++#define SHF_MIPS_STRINGS 0x80000000 ++#define SHF_MIPS_NOSTRIP 0x08000000 ++#define SHF_MIPS_LOCAL 0x04000000 ++#define SHF_MIPS_NAMES 0x02000000 ++#define SHF_MIPS_NODUPE 0x01000000 ++ ++ ++/* Symbol tables. */ ++ ++/* MIPS specific values for `st_other'. */ ++#define STO_MIPS_DEFAULT 0x0 ++#define STO_MIPS_INTERNAL 0x1 ++#define STO_MIPS_HIDDEN 0x2 ++#define STO_MIPS_PROTECTED 0x3 ++#define STO_MIPS_PLT 0x8 ++#define STO_MIPS_SC_ALIGN_UNUSED 0xff ++ ++/* MIPS specific values for `st_info'. */ ++#define STB_MIPS_SPLIT_COMMON 13 ++ ++/* Entries found in sections of type SHT_MIPS_GPTAB. */ ++ ++typedef union ++{ ++ struct ++ { ++ Elf32_Word gt_current_g_value; /* -G value used for compilation */ ++ Elf32_Word gt_unused; /* Not used */ ++ } gt_header; /* First entry in section */ ++ struct ++ { ++ Elf32_Word gt_g_value; /* If this value were used for -G */ ++ Elf32_Word gt_bytes; /* This many bytes would be used */ ++ } gt_entry; /* Subsequent entries in section */ ++} Elf32_gptab; ++ ++/* Entry found in sections of type SHT_MIPS_REGINFO. */ ++ ++typedef struct ++{ ++ Elf32_Word ri_gprmask; /* General registers used */ ++ Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */ ++ Elf32_Sword ri_gp_value; /* $gp register value */ ++} Elf32_RegInfo; ++ ++/* Entries found in sections of type SHT_MIPS_OPTIONS. */ ++ ++typedef struct ++{ ++ unsigned char kind; /* Determines interpretation of the ++ variable part of descriptor. */ ++ unsigned char size; /* Size of descriptor, including header. */ ++ Elf32_Section section; /* Section header index of section affected, ++ 0 for global options. */ ++ Elf32_Word info; /* Kind-specific information. */ ++} Elf_Options; ++ ++/* Values for `kind' field in Elf_Options. */ ++ ++#define ODK_NULL 0 /* Undefined. */ ++#define ODK_REGINFO 1 /* Register usage information. */ ++#define ODK_EXCEPTIONS 2 /* Exception processing options. */ ++#define ODK_PAD 3 /* Section padding options. */ ++#define ODK_HWPATCH 4 /* Hardware workarounds performed */ ++#define ODK_FILL 5 /* record the fill value used by the linker. */ ++#define ODK_TAGS 6 /* reserve space for desktop tools to write. */ ++#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ ++#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ ++ ++/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ ++ ++#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ ++#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ ++#define OEX_PAGE0 0x10000 /* page zero must be mapped. */ ++#define OEX_SMM 0x20000 /* Force sequential memory mode? */ ++#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ ++#define OEX_PRECISEFP OEX_FPDBUG ++#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ ++ ++#define OEX_FPU_INVAL 0x10 ++#define OEX_FPU_DIV0 0x08 ++#define OEX_FPU_OFLO 0x04 ++#define OEX_FPU_UFLO 0x02 ++#define OEX_FPU_INEX 0x01 ++ ++/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ ++ ++#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ ++#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ ++#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ ++#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ ++ ++#define OPAD_PREFIX 0x1 ++#define OPAD_POSTFIX 0x2 ++#define OPAD_SYMBOL 0x4 ++ ++/* Entry found in `.options' section. */ ++ ++typedef struct ++{ ++ Elf32_Word hwp_flags1; /* Extra flags. */ ++ Elf32_Word hwp_flags2; /* Extra flags. */ ++} Elf_Options_Hw; ++ ++/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ ++ ++#define OHWA0_R4KEOP_CHECKED 0x00000001 ++#define OHWA1_R4KEOP_CLEAN 0x00000002 ++ ++/* MIPS relocs. */ ++ ++#define R_MIPS_NONE 0 /* No reloc */ ++#define R_MIPS_16 1 /* Direct 16 bit */ ++#define R_MIPS_32 2 /* Direct 32 bit */ ++#define R_MIPS_REL32 3 /* PC relative 32 bit */ ++#define R_MIPS_26 4 /* Direct 26 bit shifted */ ++#define R_MIPS_HI16 5 /* High 16 bit */ ++#define R_MIPS_LO16 6 /* Low 16 bit */ ++#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ ++#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ ++#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ ++#define R_MIPS_PC16 10 /* PC relative 16 bit */ ++#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ ++#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ ++ ++#define R_MIPS_SHIFT5 16 ++#define R_MIPS_SHIFT6 17 ++#define R_MIPS_64 18 ++#define R_MIPS_GOT_DISP 19 ++#define R_MIPS_GOT_PAGE 20 ++#define R_MIPS_GOT_OFST 21 ++#define R_MIPS_GOT_HI16 22 ++#define R_MIPS_GOT_LO16 23 ++#define R_MIPS_SUB 24 ++#define R_MIPS_INSERT_A 25 ++#define R_MIPS_INSERT_B 26 ++#define R_MIPS_DELETE 27 ++#define R_MIPS_HIGHER 28 ++#define R_MIPS_HIGHEST 29 ++#define R_MIPS_CALL_HI16 30 ++#define R_MIPS_CALL_LO16 31 ++#define R_MIPS_SCN_DISP 32 ++#define R_MIPS_REL16 33 ++#define R_MIPS_ADD_IMMEDIATE 34 ++#define R_MIPS_PJUMP 35 ++#define R_MIPS_RELGOT 36 ++#define R_MIPS_JALR 37 ++#define R_MIPS_TLS_DTPMOD32 38 /* Module number 32 bit */ ++#define R_MIPS_TLS_DTPREL32 39 /* Module-relative offset 32 bit */ ++#define R_MIPS_TLS_DTPMOD64 40 /* Module number 64 bit */ ++#define R_MIPS_TLS_DTPREL64 41 /* Module-relative offset 64 bit */ ++#define R_MIPS_TLS_GD 42 /* 16 bit GOT offset for GD */ ++#define R_MIPS_TLS_LDM 43 /* 16 bit GOT offset for LDM */ ++#define R_MIPS_TLS_DTPREL_HI16 44 /* Module-relative offset, high 16 bits */ ++#define R_MIPS_TLS_DTPREL_LO16 45 /* Module-relative offset, low 16 bits */ ++#define R_MIPS_TLS_GOTTPREL 46 /* 16 bit GOT offset for IE */ ++#define R_MIPS_TLS_TPREL32 47 /* TP-relative offset, 32 bit */ ++#define R_MIPS_TLS_TPREL64 48 /* TP-relative offset, 64 bit */ ++#define R_MIPS_TLS_TPREL_HI16 49 /* TP-relative offset, high 16 bits */ ++#define R_MIPS_TLS_TPREL_LO16 50 /* TP-relative offset, low 16 bits */ ++#define R_MIPS_GLOB_DAT 51 ++#define R_MIPS_COPY 126 ++#define R_MIPS_JUMP_SLOT 127 ++/* Keep this the last entry. */ ++#define R_MIPS_NUM 128 ++ ++/* Legal values for p_type field of Elf32_Phdr. */ ++ ++#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */ ++#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ ++#define PT_MIPS_OPTIONS 0x70000002 ++ ++/* Special program header types. */ ++ ++#define PF_MIPS_LOCAL 0x10000000 ++ ++/* Legal values for d_tag field of Elf32_Dyn. */ ++ ++#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ ++#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ ++#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ ++#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ ++#define DT_MIPS_FLAGS 0x70000005 /* Flags */ ++#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ ++#define DT_MIPS_MSYM 0x70000007 ++#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ ++#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ ++#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ ++#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ ++#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ ++#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ ++#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ ++#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ ++#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ ++#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ ++#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ ++#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in ++ DT_MIPS_DELTA_CLASS. */ ++#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ ++#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in ++ DT_MIPS_DELTA_INSTANCE. */ ++#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ ++#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in ++ DT_MIPS_DELTA_RELOC. */ ++#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta ++ relocations refer to. */ ++#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in ++ DT_MIPS_DELTA_SYM. */ ++#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the ++ class declaration. */ ++#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in ++ DT_MIPS_DELTA_CLASSSYM. */ ++#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ ++#define DT_MIPS_PIXIE_INIT 0x70000023 ++#define DT_MIPS_SYMBOL_LIB 0x70000024 ++#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 ++#define DT_MIPS_LOCAL_GOTIDX 0x70000026 ++#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 ++#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 ++#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ ++#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ ++#define DT_MIPS_DYNSTR_ALIGN 0x7000002b ++#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ ++#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve ++ function stored in GOT. */ ++#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added ++ by rld on dlopen() calls. */ ++#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ ++#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ ++#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ ++/* The address of .got.plt in an executable using the new non-PIC ABI. */ ++#define DT_MIPS_PLTGOT 0x70000032 ++/* The base of the PLT in an executable using the new non-PIC ABI if that ++ PLT is writable. For a non-writable PLT, this is omitted or has a zero ++ value. */ ++#define DT_MIPS_RWPLT 0x70000034 ++#define DT_MIPS_NUM 0x35 ++ ++/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ ++ ++#define RHF_NONE 0 /* No flags */ ++#define RHF_QUICKSTART (1 << 0) /* Use quickstart */ ++#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ ++#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ ++#define RHF_NO_MOVE (1 << 3) ++#define RHF_SGI_ONLY (1 << 4) ++#define RHF_GUARANTEE_INIT (1 << 5) ++#define RHF_DELTA_C_PLUS_PLUS (1 << 6) ++#define RHF_GUARANTEE_START_INIT (1 << 7) ++#define RHF_PIXIE (1 << 8) ++#define RHF_DEFAULT_DELAY_LOAD (1 << 9) ++#define RHF_REQUICKSTART (1 << 10) ++#define RHF_REQUICKSTARTED (1 << 11) ++#define RHF_CORD (1 << 12) ++#define RHF_NO_UNRES_UNDEF (1 << 13) ++#define RHF_RLD_ORDER_SAFE (1 << 14) ++ ++/* Entries found in sections of type SHT_MIPS_LIBLIST. */ ++ ++typedef struct ++{ ++ Elf32_Word l_name; /* Name (string table index) */ ++ Elf32_Word l_time_stamp; /* Timestamp */ ++ Elf32_Word l_checksum; /* Checksum */ ++ Elf32_Word l_version; /* Interface version */ ++ Elf32_Word l_flags; /* Flags */ ++} Elf32_Lib; ++ ++typedef struct ++{ ++ Elf64_Word l_name; /* Name (string table index) */ ++ Elf64_Word l_time_stamp; /* Timestamp */ ++ Elf64_Word l_checksum; /* Checksum */ ++ Elf64_Word l_version; /* Interface version */ ++ Elf64_Word l_flags; /* Flags */ ++} Elf64_Lib; ++ ++ ++/* Legal values for l_flags. */ ++ ++#define LL_NONE 0 ++#define LL_EXACT_MATCH (1 << 0) /* Require exact match */ ++#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ ++#define LL_REQUIRE_MINOR (1 << 2) ++#define LL_EXPORTS (1 << 3) ++#define LL_DELAY_LOAD (1 << 4) ++#define LL_DELTA (1 << 5) ++ ++/* Entries found in sections of type SHT_MIPS_CONFLICT. */ ++ ++typedef Elf32_Addr Elf32_Conflict; ++ ++ ++/* HPPA specific definitions. */ ++ ++/* Legal values for e_flags field of Elf32_Ehdr. */ ++ ++#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ ++#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ ++#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ ++#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ ++#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch ++ prediction. */ ++#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ ++#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ ++ ++/* Defined values for `e_flags & EF_PARISC_ARCH' are: */ ++ ++#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ ++#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ ++#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ ++ ++/* Additional section indeces. */ ++ ++#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared ++ symbols in ANSI C. */ ++#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ ++ ++/* Legal values for sh_type field of Elf32_Shdr. */ ++ ++#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ ++#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ ++#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ ++ ++/* Legal values for sh_flags field of Elf32_Shdr. */ ++ ++#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ ++#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ ++#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ ++ ++/* Legal values for ST_TYPE subfield of st_info (symbol type). */ ++ ++#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ ++ ++#define STT_HP_OPAQUE (STT_LOOS + 0x1) ++#define STT_HP_STUB (STT_LOOS + 0x2) ++ ++/* HPPA relocs. */ ++ ++#define R_PARISC_NONE 0 /* No reloc. */ ++#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ ++#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ ++#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ ++#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ ++#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ ++#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ ++#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ ++#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ ++#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ ++#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ ++#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ ++#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ ++#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ ++#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ ++#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ ++#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ ++#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ ++#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ ++#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ ++#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ ++#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ ++#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ ++#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ ++#define R_PARISC_FPTR64 64 /* 64 bits function address. */ ++#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ ++#define R_PARISC_PLABEL21L 66 /* Left 21 bits of fdesc address. */ ++#define R_PARISC_PLABEL14R 70 /* Right 14 bits of fdesc address. */ ++#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ ++#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ ++#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ ++#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ ++#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ ++#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ ++#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ ++#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ ++#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ ++#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ ++#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ ++#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ ++#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ ++#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ ++#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ ++#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ ++#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ ++#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ ++#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ ++#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ ++#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ ++#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ ++#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ ++#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ ++#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ ++#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ ++#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ ++#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ ++#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ ++#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ ++#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ ++#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ ++#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ ++#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ ++#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ ++#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ ++#define R_PARISC_LORESERVE 128 ++#define R_PARISC_COPY 128 /* Copy relocation. */ ++#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ ++#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ ++#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ ++#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ ++#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ ++#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ ++#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ ++#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ ++#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ ++#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ ++#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ ++#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ ++#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ ++#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ ++#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ ++#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ ++#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ ++#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ ++#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ ++#define R_PARISC_GNU_VTENTRY 232 ++#define R_PARISC_GNU_VTINHERIT 233 ++#define R_PARISC_TLS_GD21L 234 /* GD 21-bit left. */ ++#define R_PARISC_TLS_GD14R 235 /* GD 14-bit right. */ ++#define R_PARISC_TLS_GDCALL 236 /* GD call to __t_g_a. */ ++#define R_PARISC_TLS_LDM21L 237 /* LD module 21-bit left. */ ++#define R_PARISC_TLS_LDM14R 238 /* LD module 14-bit right. */ ++#define R_PARISC_TLS_LDMCALL 239 /* LD module call to __t_g_a. */ ++#define R_PARISC_TLS_LDO21L 240 /* LD offset 21-bit left. */ ++#define R_PARISC_TLS_LDO14R 241 /* LD offset 14-bit right. */ ++#define R_PARISC_TLS_DTPMOD32 242 /* DTP module 32-bit. */ ++#define R_PARISC_TLS_DTPMOD64 243 /* DTP module 64-bit. */ ++#define R_PARISC_TLS_DTPOFF32 244 /* DTP offset 32-bit. */ ++#define R_PARISC_TLS_DTPOFF64 245 /* DTP offset 32-bit. */ ++#define R_PARISC_TLS_LE21L R_PARISC_TPREL21L ++#define R_PARISC_TLS_LE14R R_PARISC_TPREL14R ++#define R_PARISC_TLS_IE21L R_PARISC_LTOFF_TP21L ++#define R_PARISC_TLS_IE14R R_PARISC_LTOFF_TP14R ++#define R_PARISC_TLS_TPREL32 R_PARISC_TPREL32 ++#define R_PARISC_TLS_TPREL64 R_PARISC_TPREL64 ++#define R_PARISC_HIRESERVE 255 ++ ++/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ ++ ++#define PT_HP_TLS (PT_LOOS + 0x0) ++#define PT_HP_CORE_NONE (PT_LOOS + 0x1) ++#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) ++#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) ++#define PT_HP_CORE_COMM (PT_LOOS + 0x4) ++#define PT_HP_CORE_PROC (PT_LOOS + 0x5) ++#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) ++#define PT_HP_CORE_STACK (PT_LOOS + 0x7) ++#define PT_HP_CORE_SHM (PT_LOOS + 0x8) ++#define PT_HP_CORE_MMF (PT_LOOS + 0x9) ++#define PT_HP_PARALLEL (PT_LOOS + 0x10) ++#define PT_HP_FASTBIND (PT_LOOS + 0x11) ++#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) ++#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) ++#define PT_HP_STACK (PT_LOOS + 0x14) ++ ++#define PT_PARISC_ARCHEXT 0x70000000 ++#define PT_PARISC_UNWIND 0x70000001 ++ ++/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ ++ ++#define PF_PARISC_SBP 0x08000000 ++ ++#define PF_HP_PAGE_SIZE 0x00100000 ++#define PF_HP_FAR_SHARED 0x00200000 ++#define PF_HP_NEAR_SHARED 0x00400000 ++#define PF_HP_CODE 0x01000000 ++#define PF_HP_MODIFY 0x02000000 ++#define PF_HP_LAZYSWAP 0x04000000 ++#define PF_HP_SBP 0x08000000 ++ ++ ++/* Alpha specific definitions. */ ++ ++/* Legal values for e_flags field of Elf64_Ehdr. */ ++ ++#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ ++#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ ++ ++/* Legal values for sh_type field of Elf64_Shdr. */ ++ ++/* These two are primerily concerned with ECOFF debugging info. */ ++#define SHT_ALPHA_DEBUG 0x70000001 ++#define SHT_ALPHA_REGINFO 0x70000002 ++ ++/* Legal values for sh_flags field of Elf64_Shdr. */ ++ ++#define SHF_ALPHA_GPREL 0x10000000 ++ ++/* Legal values for st_other field of Elf64_Sym. */ ++#define STO_ALPHA_NOPV 0x80 /* No PV required. */ ++#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ ++ ++/* Alpha relocs. */ ++ ++#define R_ALPHA_NONE 0 /* No reloc */ ++#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ ++#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ ++#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ ++#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ ++#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ ++#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ ++#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ ++#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ ++#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ ++#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ ++#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ ++#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ ++#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ ++#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */ ++#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ ++#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ ++#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ ++#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ ++#define R_ALPHA_TLS_GD_HI 28 ++#define R_ALPHA_TLSGD 29 ++#define R_ALPHA_TLS_LDM 30 ++#define R_ALPHA_DTPMOD64 31 ++#define R_ALPHA_GOTDTPREL 32 ++#define R_ALPHA_DTPREL64 33 ++#define R_ALPHA_DTPRELHI 34 ++#define R_ALPHA_DTPRELLO 35 ++#define R_ALPHA_DTPREL16 36 ++#define R_ALPHA_GOTTPREL 37 ++#define R_ALPHA_TPREL64 38 ++#define R_ALPHA_TPRELHI 39 ++#define R_ALPHA_TPRELLO 40 ++#define R_ALPHA_TPREL16 41 ++/* Keep this the last entry. */ ++#define R_ALPHA_NUM 46 ++ ++/* Magic values of the LITUSE relocation addend. */ ++#define LITUSE_ALPHA_ADDR 0 ++#define LITUSE_ALPHA_BASE 1 ++#define LITUSE_ALPHA_BYTOFF 2 ++#define LITUSE_ALPHA_JSR 3 ++#define LITUSE_ALPHA_TLS_GD 4 ++#define LITUSE_ALPHA_TLS_LDM 5 ++ ++/* Legal values for d_tag of Elf64_Dyn. */ ++#define DT_ALPHA_PLTRO (DT_LOPROC + 0) ++#define DT_ALPHA_NUM 1 ++ ++/* PowerPC specific declarations */ ++ ++/* Values for Elf32/64_Ehdr.e_flags. */ ++#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ ++ ++/* Cygnus local bits below */ ++#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ ++#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib ++ flag */ ++ ++/* PowerPC relocations defined by the ABIs */ ++#define R_PPC_NONE 0 ++#define R_PPC_ADDR32 1 /* 32bit absolute address */ ++#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ ++#define R_PPC_ADDR16 3 /* 16bit absolute address */ ++#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ ++#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ ++#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ ++#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ ++#define R_PPC_ADDR14_BRTAKEN 8 ++#define R_PPC_ADDR14_BRNTAKEN 9 ++#define R_PPC_REL24 10 /* PC relative 26 bit */ ++#define R_PPC_REL14 11 /* PC relative 16 bit */ ++#define R_PPC_REL14_BRTAKEN 12 ++#define R_PPC_REL14_BRNTAKEN 13 ++#define R_PPC_GOT16 14 ++#define R_PPC_GOT16_LO 15 ++#define R_PPC_GOT16_HI 16 ++#define R_PPC_GOT16_HA 17 ++#define R_PPC_PLTREL24 18 ++#define R_PPC_COPY 19 ++#define R_PPC_GLOB_DAT 20 ++#define R_PPC_JMP_SLOT 21 ++#define R_PPC_RELATIVE 22 ++#define R_PPC_LOCAL24PC 23 ++#define R_PPC_UADDR32 24 ++#define R_PPC_UADDR16 25 ++#define R_PPC_REL32 26 ++#define R_PPC_PLT32 27 ++#define R_PPC_PLTREL32 28 ++#define R_PPC_PLT16_LO 29 ++#define R_PPC_PLT16_HI 30 ++#define R_PPC_PLT16_HA 31 ++#define R_PPC_SDAREL16 32 ++#define R_PPC_SECTOFF 33 ++#define R_PPC_SECTOFF_LO 34 ++#define R_PPC_SECTOFF_HI 35 ++#define R_PPC_SECTOFF_HA 36 ++ ++/* PowerPC relocations defined for the TLS access ABI. */ ++#define R_PPC_TLS 67 /* none (sym+add)@tls */ ++#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */ ++#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */ ++#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ ++#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ ++#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ ++#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */ ++#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */ ++#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ ++#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ ++#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ ++#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */ ++#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ ++#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ ++#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ ++#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ ++#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ ++#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ ++#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ ++#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ ++#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */ ++#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */ ++#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ ++#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ ++#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */ ++#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */ ++#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */ ++#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */ ++ ++/* The remaining relocs are from the Embedded ELF ABI, and are not ++ in the SVR4 ELF ABI. */ ++#define R_PPC_EMB_NADDR32 101 ++#define R_PPC_EMB_NADDR16 102 ++#define R_PPC_EMB_NADDR16_LO 103 ++#define R_PPC_EMB_NADDR16_HI 104 ++#define R_PPC_EMB_NADDR16_HA 105 ++#define R_PPC_EMB_SDAI16 106 ++#define R_PPC_EMB_SDA2I16 107 ++#define R_PPC_EMB_SDA2REL 108 ++#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ ++#define R_PPC_EMB_MRKREF 110 ++#define R_PPC_EMB_RELSEC16 111 ++#define R_PPC_EMB_RELST_LO 112 ++#define R_PPC_EMB_RELST_HI 113 ++#define R_PPC_EMB_RELST_HA 114 ++#define R_PPC_EMB_BIT_FLD 115 ++#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ ++ ++/* Diab tool relocations. */ ++#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ ++#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ ++#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ ++#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ ++#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ ++#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ ++ ++/* GNU extension to support local ifunc. */ ++#define R_PPC_IRELATIVE 248 ++ ++/* GNU relocs used in PIC code sequences. */ ++#define R_PPC_REL16 249 /* half16 (sym+add-.) */ ++#define R_PPC_REL16_LO 250 /* half16 (sym+add-.)@l */ ++#define R_PPC_REL16_HI 251 /* half16 (sym+add-.)@h */ ++#define R_PPC_REL16_HA 252 /* half16 (sym+add-.)@ha */ ++ ++/* This is a phony reloc to handle any old fashioned TOC16 references ++ that may still be in object files. */ ++#define R_PPC_TOC16 255 ++ ++/* PowerPC specific values for the Dyn d_tag field. */ ++#define DT_PPC_GOT (DT_LOPROC + 0) ++#define DT_PPC_NUM 1 ++ ++/* PowerPC64 relocations defined by the ABIs */ ++#define R_PPC64_NONE R_PPC_NONE ++#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */ ++#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */ ++#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */ ++#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */ ++#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */ ++#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */ ++#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */ ++#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN ++#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN ++#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */ ++#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */ ++#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN ++#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN ++#define R_PPC64_GOT16 R_PPC_GOT16 ++#define R_PPC64_GOT16_LO R_PPC_GOT16_LO ++#define R_PPC64_GOT16_HI R_PPC_GOT16_HI ++#define R_PPC64_GOT16_HA R_PPC_GOT16_HA ++ ++#define R_PPC64_COPY R_PPC_COPY ++#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT ++#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT ++#define R_PPC64_RELATIVE R_PPC_RELATIVE ++ ++#define R_PPC64_UADDR32 R_PPC_UADDR32 ++#define R_PPC64_UADDR16 R_PPC_UADDR16 ++#define R_PPC64_REL32 R_PPC_REL32 ++#define R_PPC64_PLT32 R_PPC_PLT32 ++#define R_PPC64_PLTREL32 R_PPC_PLTREL32 ++#define R_PPC64_PLT16_LO R_PPC_PLT16_LO ++#define R_PPC64_PLT16_HI R_PPC_PLT16_HI ++#define R_PPC64_PLT16_HA R_PPC_PLT16_HA ++ ++#define R_PPC64_SECTOFF R_PPC_SECTOFF ++#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO ++#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI ++#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA ++#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */ ++#define R_PPC64_ADDR64 38 /* doubleword64 S + A */ ++#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */ ++#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */ ++#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */ ++#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */ ++#define R_PPC64_UADDR64 43 /* doubleword64 S + A */ ++#define R_PPC64_REL64 44 /* doubleword64 S + A - P */ ++#define R_PPC64_PLT64 45 /* doubleword64 L + A */ ++#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */ ++#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */ ++#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */ ++#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */ ++#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */ ++#define R_PPC64_TOC 51 /* doubleword64 .TOC */ ++#define R_PPC64_PLTGOT16 52 /* half16* M + A */ ++#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */ ++#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */ ++#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */ ++ ++#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */ ++#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */ ++#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */ ++#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */ ++#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */ ++#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */ ++#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */ ++#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */ ++#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */ ++#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */ ++#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */ ++ ++/* PowerPC64 relocations defined for the TLS access ABI. */ ++#define R_PPC64_TLS 67 /* none (sym+add)@tls */ ++#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */ ++#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */ ++#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ ++#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ ++#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ ++#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */ ++#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */ ++#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ ++#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ ++#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ ++#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */ ++#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ ++#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ ++#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ ++#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ ++#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ ++#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ ++#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ ++#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ ++#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */ ++#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */ ++#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ ++#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ ++#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */ ++#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */ ++#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */ ++#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */ ++#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */ ++#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */ ++#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */ ++#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */ ++#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */ ++#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */ ++#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */ ++#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */ ++#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */ ++#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */ ++#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */ ++#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */ ++ ++/* GNU extension to support local ifunc. */ ++#define R_PPC64_JMP_IREL 247 ++#define R_PPC64_IRELATIVE 248 ++#define R_PPC64_REL16 249 /* half16 (sym+add-.) */ ++#define R_PPC64_REL16_LO 250 /* half16 (sym+add-.)@l */ ++#define R_PPC64_REL16_HI 251 /* half16 (sym+add-.)@h */ ++#define R_PPC64_REL16_HA 252 /* half16 (sym+add-.)@ha */ ++ ++/* PowerPC64 specific values for the Dyn d_tag field. */ ++#define DT_PPC64_GLINK (DT_LOPROC + 0) ++#define DT_PPC64_OPD (DT_LOPROC + 1) ++#define DT_PPC64_OPDSZ (DT_LOPROC + 2) ++#define DT_PPC64_NUM 3 ++ ++ ++/* ARM specific declarations */ ++ ++/* Processor specific flags for the ELF header e_flags field. */ ++#define EF_ARM_RELEXEC 0x01 ++#define EF_ARM_HASENTRY 0x02 ++#define EF_ARM_INTERWORK 0x04 ++#define EF_ARM_APCS_26 0x08 ++#define EF_ARM_APCS_FLOAT 0x10 ++#define EF_ARM_PIC 0x20 ++#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ ++#define EF_ARM_NEW_ABI 0x80 ++#define EF_ARM_OLD_ABI 0x100 ++#define EF_ARM_SOFT_FLOAT 0x200 ++#define EF_ARM_VFP_FLOAT 0x400 ++#define EF_ARM_MAVERICK_FLOAT 0x800 ++ ++ ++/* Other constants defined in the ARM ELF spec. version B-01. */ ++/* NB. These conflict with values defined above. */ ++#define EF_ARM_SYMSARESORTED 0x04 ++#define EF_ARM_DYNSYMSUSESEGIDX 0x08 ++#define EF_ARM_MAPSYMSFIRST 0x10 ++#define EF_ARM_EABIMASK 0XFF000000 ++ ++/* Constants defined in AAELF. */ ++#define EF_ARM_BE8 0x00800000 ++#define EF_ARM_LE8 0x00400000 ++ ++#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) ++#define EF_ARM_EABI_UNKNOWN 0x00000000 ++#define EF_ARM_EABI_VER1 0x01000000 ++#define EF_ARM_EABI_VER2 0x02000000 ++#define EF_ARM_EABI_VER3 0x03000000 ++#define EF_ARM_EABI_VER4 0x04000000 ++#define EF_ARM_EABI_VER5 0x05000000 ++ ++/* Additional symbol types for Thumb. */ ++#define STT_ARM_TFUNC STT_LOPROC /* A Thumb function. */ ++#define STT_ARM_16BIT STT_HIPROC /* A Thumb label. */ ++ ++/* ARM-specific values for sh_flags */ ++#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ ++#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined ++ in the input to a link step. */ ++ ++/* ARM-specific program header flags */ ++#define PF_ARM_SB 0x10000000 /* Segment contains the location ++ addressed by the static base. */ ++#define PF_ARM_PI 0x20000000 /* Position-independent segment. */ ++#define PF_ARM_ABS 0x40000000 /* Absolute segment. */ ++ ++/* Processor specific values for the Phdr p_type field. */ ++#define PT_ARM_EXIDX (PT_LOPROC + 1) /* ARM unwind segment. */ ++ ++/* Processor specific values for the Shdr sh_type field. */ ++#define SHT_ARM_EXIDX (SHT_LOPROC + 1) /* ARM unwind section. */ ++#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) /* Preemption details. */ ++#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) /* ARM attributes section. */ ++ ++ ++/* ARM relocs. */ ++ ++#define R_ARM_NONE 0 /* No reloc */ ++#define R_ARM_PC24 1 /* PC relative 26 bit branch */ ++#define R_ARM_ABS32 2 /* Direct 32 bit */ ++#define R_ARM_REL32 3 /* PC relative 32 bit */ ++#define R_ARM_PC13 4 ++#define R_ARM_ABS16 5 /* Direct 16 bit */ ++#define R_ARM_ABS12 6 /* Direct 12 bit */ ++#define R_ARM_THM_ABS5 7 ++#define R_ARM_ABS8 8 /* Direct 8 bit */ ++#define R_ARM_SBREL32 9 ++#define R_ARM_THM_PC22 10 ++#define R_ARM_THM_PC8 11 ++#define R_ARM_AMP_VCALL9 12 ++#define R_ARM_SWI24 13 /* Obsolete static relocation. */ ++#define R_ARM_TLS_DESC 13 /* Dynamic relocation. */ ++#define R_ARM_THM_SWI8 14 ++#define R_ARM_XPC25 15 ++#define R_ARM_THM_XPC22 16 ++#define R_ARM_TLS_DTPMOD32 17 /* ID of module containing symbol */ ++#define R_ARM_TLS_DTPOFF32 18 /* Offset in TLS block */ ++#define R_ARM_TLS_TPOFF32 19 /* Offset in static TLS block */ ++#define R_ARM_COPY 20 /* Copy symbol at runtime */ ++#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ ++#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ ++#define R_ARM_RELATIVE 23 /* Adjust by program base */ ++#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ ++#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ ++#define R_ARM_GOT32 26 /* 32 bit GOT entry */ ++#define R_ARM_PLT32 27 /* 32 bit PLT address */ ++#define R_ARM_ALU_PCREL_7_0 32 ++#define R_ARM_ALU_PCREL_15_8 33 ++#define R_ARM_ALU_PCREL_23_15 34 ++#define R_ARM_LDR_SBREL_11_0 35 ++#define R_ARM_ALU_SBREL_19_12 36 ++#define R_ARM_ALU_SBREL_27_20 37 ++#define R_ARM_TLS_GOTDESC 90 ++#define R_ARM_TLS_CALL 91 ++#define R_ARM_TLS_DESCSEQ 92 ++#define R_ARM_THM_TLS_CALL 93 ++#define R_ARM_GNU_VTENTRY 100 ++#define R_ARM_GNU_VTINHERIT 101 ++#define R_ARM_THM_PC11 102 /* thumb unconditional branch */ ++#define R_ARM_THM_PC9 103 /* thumb conditional branch */ ++#define R_ARM_TLS_GD32 104 /* PC-rel 32 bit for global dynamic ++ thread local data */ ++#define R_ARM_TLS_LDM32 105 /* PC-rel 32 bit for local dynamic ++ thread local data */ ++#define R_ARM_TLS_LDO32 106 /* 32 bit offset relative to TLS ++ block */ ++#define R_ARM_TLS_IE32 107 /* PC-rel 32 bit for GOT entry of ++ static TLS block offset */ ++#define R_ARM_TLS_LE32 108 /* 32 bit offset relative to static ++ TLS block */ ++#define R_ARM_THM_TLS_DESCSEQ 129 ++#define R_ARM_IRELATIVE 160 ++#define R_ARM_RXPC25 249 ++#define R_ARM_RSBREL32 250 ++#define R_ARM_THM_RPC22 251 ++#define R_ARM_RREL32 252 ++#define R_ARM_RABS22 253 ++#define R_ARM_RPC24 254 ++#define R_ARM_RBASE 255 ++/* Keep this the last entry. */ ++#define R_ARM_NUM 256 ++ ++/* IA-64 specific declarations. */ ++ ++/* Processor specific flags for the Ehdr e_flags field. */ ++#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ ++#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ ++#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ ++ ++/* Processor specific values for the Phdr p_type field. */ ++#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ ++#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ ++#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12) ++#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13) ++#define PT_IA_64_HP_STACK (PT_LOOS + 0x14) ++ ++/* Processor specific flags for the Phdr p_flags field. */ ++#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ ++ ++/* Processor specific values for the Shdr sh_type field. */ ++#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ ++#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ ++ ++/* Processor specific flags for the Shdr sh_flags field. */ ++#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ ++#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ ++ ++/* Processor specific values for the Dyn d_tag field. */ ++#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) ++#define DT_IA_64_NUM 1 ++ ++/* IA-64 relocations. */ ++#define R_IA64_NONE 0x00 /* none */ ++#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ ++#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ ++#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ ++#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ ++#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ ++#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ ++#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ ++#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ ++#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ ++#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ ++#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ ++#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ ++#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ ++#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ ++#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ ++#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ ++#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ ++#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ ++#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ ++#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ ++#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ ++#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ ++#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ ++#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ ++#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ ++#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ ++#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ ++#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ ++#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ ++#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ ++#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ ++#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ ++#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ ++#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ ++#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ ++#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ ++#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ ++#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ ++#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ ++#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ ++#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ ++#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ ++#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ ++#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ ++#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ ++#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ ++#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ ++#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ ++#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ ++#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ ++#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ ++#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ ++#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ ++#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ ++#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ ++#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ ++#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ ++#define R_IA64_COPY 0x84 /* copy relocation */ ++#define R_IA64_SUB 0x85 /* Addend and symbol difference */ ++#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ ++#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ ++#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ ++#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ ++#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ ++#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ ++#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ ++#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ ++#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ ++#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ ++#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ ++#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ ++#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ ++#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ ++#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ ++#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ ++ ++/* SH specific declarations */ ++ ++/* Processor specific flags for the ELF header e_flags field. */ ++#define EF_SH_MACH_MASK 0x1f ++#define EF_SH_UNKNOWN 0x0 ++#define EF_SH1 0x1 ++#define EF_SH2 0x2 ++#define EF_SH3 0x3 ++#define EF_SH_DSP 0x4 ++#define EF_SH3_DSP 0x5 ++#define EF_SH4AL_DSP 0x6 ++#define EF_SH3E 0x8 ++#define EF_SH4 0x9 ++#define EF_SH2E 0xb ++#define EF_SH4A 0xc ++#define EF_SH2A 0xd ++#define EF_SH4_NOFPU 0x10 ++#define EF_SH4A_NOFPU 0x11 ++#define EF_SH4_NOMMU_NOFPU 0x12 ++#define EF_SH2A_NOFPU 0x13 ++#define EF_SH3_NOMMU 0x14 ++#define EF_SH2A_SH4_NOFPU 0x15 ++#define EF_SH2A_SH3_NOFPU 0x16 ++#define EF_SH2A_SH4 0x17 ++#define EF_SH2A_SH3E 0x18 ++ ++/* SH relocs. */ ++#define R_SH_NONE 0 ++#define R_SH_DIR32 1 ++#define R_SH_REL32 2 ++#define R_SH_DIR8WPN 3 ++#define R_SH_IND12W 4 ++#define R_SH_DIR8WPL 5 ++#define R_SH_DIR8WPZ 6 ++#define R_SH_DIR8BP 7 ++#define R_SH_DIR8W 8 ++#define R_SH_DIR8L 9 ++#define R_SH_SWITCH16 25 ++#define R_SH_SWITCH32 26 ++#define R_SH_USES 27 ++#define R_SH_COUNT 28 ++#define R_SH_ALIGN 29 ++#define R_SH_CODE 30 ++#define R_SH_DATA 31 ++#define R_SH_LABEL 32 ++#define R_SH_SWITCH8 33 ++#define R_SH_GNU_VTINHERIT 34 ++#define R_SH_GNU_VTENTRY 35 ++#define R_SH_TLS_GD_32 144 ++#define R_SH_TLS_LD_32 145 ++#define R_SH_TLS_LDO_32 146 ++#define R_SH_TLS_IE_32 147 ++#define R_SH_TLS_LE_32 148 ++#define R_SH_TLS_DTPMOD32 149 ++#define R_SH_TLS_DTPOFF32 150 ++#define R_SH_TLS_TPOFF32 151 ++#define R_SH_GOT32 160 ++#define R_SH_PLT32 161 ++#define R_SH_COPY 162 ++#define R_SH_GLOB_DAT 163 ++#define R_SH_JMP_SLOT 164 ++#define R_SH_RELATIVE 165 ++#define R_SH_GOTOFF 166 ++#define R_SH_GOTPC 167 ++/* Keep this the last entry. */ ++#define R_SH_NUM 256 ++ ++/* S/390 specific definitions. */ ++ ++/* Valid values for the e_flags field. */ ++ ++#define EF_S390_HIGH_GPRS 0x00000001 /* High GPRs kernel facility needed. */ ++ ++/* Additional s390 relocs */ ++ ++#define R_390_NONE 0 /* No reloc. */ ++#define R_390_8 1 /* Direct 8 bit. */ ++#define R_390_12 2 /* Direct 12 bit. */ ++#define R_390_16 3 /* Direct 16 bit. */ ++#define R_390_32 4 /* Direct 32 bit. */ ++#define R_390_PC32 5 /* PC relative 32 bit. */ ++#define R_390_GOT12 6 /* 12 bit GOT offset. */ ++#define R_390_GOT32 7 /* 32 bit GOT offset. */ ++#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ ++#define R_390_COPY 9 /* Copy symbol at runtime. */ ++#define R_390_GLOB_DAT 10 /* Create GOT entry. */ ++#define R_390_JMP_SLOT 11 /* Create PLT entry. */ ++#define R_390_RELATIVE 12 /* Adjust by program base. */ ++#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ ++#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ ++#define R_390_GOT16 15 /* 16 bit GOT offset. */ ++#define R_390_PC16 16 /* PC relative 16 bit. */ ++#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ ++#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ ++#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ ++#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ ++#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ ++#define R_390_64 22 /* Direct 64 bit. */ ++#define R_390_PC64 23 /* PC relative 64 bit. */ ++#define R_390_GOT64 24 /* 64 bit GOT offset. */ ++#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ ++#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ ++#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ ++#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ ++#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ ++#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ ++#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ ++#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ ++#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ ++#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ ++#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ ++#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ ++#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ ++#define R_390_TLS_GDCALL 38 /* Tag for function call in general ++ dynamic TLS code. */ ++#define R_390_TLS_LDCALL 39 /* Tag for function call in local ++ dynamic TLS code. */ ++#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic ++ thread local data. */ ++#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic ++ thread local data. */ ++#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic ++ thread local data in LE code. */ ++#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic ++ thread local data in LE code. */ ++#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for ++ negated static TLS block offset. */ ++#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for ++ negated static TLS block offset. */ ++#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for ++ negated static TLS block offset. */ ++#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to ++ static TLS block. */ ++#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to ++ static TLS block. */ ++#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS ++ block. */ ++#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS ++ block. */ ++#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ ++#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ ++#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS ++ block. */ ++#define R_390_20 57 /* Direct 20 bit. */ ++#define R_390_GOT20 58 /* 20 bit GOT offset. */ ++#define R_390_GOTPLT20 59 /* 20 bit offset to jump slot. */ ++#define R_390_TLS_GOTIE20 60 /* 20 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_IRELATIVE 61 /* STT_GNU_IFUNC relocation. */ ++/* Keep this the last entry. */ ++#define R_390_NUM 62 ++ ++ ++/* CRIS relocations. */ ++#define R_CRIS_NONE 0 ++#define R_CRIS_8 1 ++#define R_CRIS_16 2 ++#define R_CRIS_32 3 ++#define R_CRIS_8_PCREL 4 ++#define R_CRIS_16_PCREL 5 ++#define R_CRIS_32_PCREL 6 ++#define R_CRIS_GNU_VTINHERIT 7 ++#define R_CRIS_GNU_VTENTRY 8 ++#define R_CRIS_COPY 9 ++#define R_CRIS_GLOB_DAT 10 ++#define R_CRIS_JUMP_SLOT 11 ++#define R_CRIS_RELATIVE 12 ++#define R_CRIS_16_GOT 13 ++#define R_CRIS_32_GOT 14 ++#define R_CRIS_16_GOTPLT 15 ++#define R_CRIS_32_GOTPLT 16 ++#define R_CRIS_32_GOTREL 17 ++#define R_CRIS_32_PLT_GOTREL 18 ++#define R_CRIS_32_PLT_PCREL 19 ++ ++#define R_CRIS_NUM 20 ++ ++ ++/* AMD x86-64 relocations. */ ++#define R_X86_64_NONE 0 /* No reloc */ ++#define R_X86_64_64 1 /* Direct 64 bit */ ++#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ ++#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ ++#define R_X86_64_PLT32 4 /* 32 bit PLT address */ ++#define R_X86_64_COPY 5 /* Copy symbol at runtime */ ++#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ ++#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ ++#define R_X86_64_RELATIVE 8 /* Adjust by program base */ ++#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative ++ offset to GOT */ ++#define R_X86_64_32 10 /* Direct 32 bit zero extended */ ++#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ ++#define R_X86_64_16 12 /* Direct 16 bit zero extended */ ++#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ ++#define R_X86_64_8 14 /* Direct 8 bit sign extended */ ++#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ ++#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ ++#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */ ++#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */ ++#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset ++ to two GOT entries for GD symbol */ ++#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset ++ to two GOT entries for LD symbol */ ++#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ ++#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset ++ to GOT entry for IE symbol */ ++#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */ ++#define R_X86_64_PC64 24 /* PC relative 64 bit */ ++#define R_X86_64_GOTOFF64 25 /* 64 bit offset to GOT */ ++#define R_X86_64_GOTPC32 26 /* 32 bit signed pc relative ++ offset to GOT */ ++#define R_X86_64_GOT64 27 /* 64-bit GOT entry offset */ ++#define R_X86_64_GOTPCREL64 28 /* 64-bit PC relative offset ++ to GOT entry */ ++#define R_X86_64_GOTPC64 29 /* 64-bit PC relative offset to GOT */ ++#define R_X86_64_GOTPLT64 30 /* like GOT64, says PLT entry needed */ ++#define R_X86_64_PLTOFF64 31 /* 64-bit GOT relative offset ++ to PLT entry */ ++#define R_X86_64_SIZE32 32 /* Size of symbol plus 32-bit addend */ ++#define R_X86_64_SIZE64 33 /* Size of symbol plus 64-bit addend */ ++#define R_X86_64_GOTPC32_TLSDESC 34 /* GOT offset for TLS descriptor. */ ++#define R_X86_64_TLSDESC_CALL 35 /* Marker for call through TLS ++ descriptor. */ ++#define R_X86_64_TLSDESC 36 /* TLS descriptor. */ ++#define R_X86_64_IRELATIVE 37 /* Adjust indirectly by program base */ ++#define R_X86_64_RELATIVE64 38 /* 64-bit adjust by program base */ ++ ++#define R_X86_64_NUM 39 ++ ++ ++/* AM33 relocations. */ ++#define R_MN10300_NONE 0 /* No reloc. */ ++#define R_MN10300_32 1 /* Direct 32 bit. */ ++#define R_MN10300_16 2 /* Direct 16 bit. */ ++#define R_MN10300_8 3 /* Direct 8 bit. */ ++#define R_MN10300_PCREL32 4 /* PC-relative 32-bit. */ ++#define R_MN10300_PCREL16 5 /* PC-relative 16-bit signed. */ ++#define R_MN10300_PCREL8 6 /* PC-relative 8-bit signed. */ ++#define R_MN10300_GNU_VTINHERIT 7 /* Ancient C++ vtable garbage... */ ++#define R_MN10300_GNU_VTENTRY 8 /* ... collection annotation. */ ++#define R_MN10300_24 9 /* Direct 24 bit. */ ++#define R_MN10300_GOTPC32 10 /* 32-bit PCrel offset to GOT. */ ++#define R_MN10300_GOTPC16 11 /* 16-bit PCrel offset to GOT. */ ++#define R_MN10300_GOTOFF32 12 /* 32-bit offset from GOT. */ ++#define R_MN10300_GOTOFF24 13 /* 24-bit offset from GOT. */ ++#define R_MN10300_GOTOFF16 14 /* 16-bit offset from GOT. */ ++#define R_MN10300_PLT32 15 /* 32-bit PCrel to PLT entry. */ ++#define R_MN10300_PLT16 16 /* 16-bit PCrel to PLT entry. */ ++#define R_MN10300_GOT32 17 /* 32-bit offset to GOT entry. */ ++#define R_MN10300_GOT24 18 /* 24-bit offset to GOT entry. */ ++#define R_MN10300_GOT16 19 /* 16-bit offset to GOT entry. */ ++#define R_MN10300_COPY 20 /* Copy symbol at runtime. */ ++#define R_MN10300_GLOB_DAT 21 /* Create GOT entry. */ ++#define R_MN10300_JMP_SLOT 22 /* Create PLT entry. */ ++#define R_MN10300_RELATIVE 23 /* Adjust by program base. */ ++ ++#define R_MN10300_NUM 24 ++ ++ ++/* M32R relocs. */ ++#define R_M32R_NONE 0 /* No reloc. */ ++#define R_M32R_16 1 /* Direct 16 bit. */ ++#define R_M32R_32 2 /* Direct 32 bit. */ ++#define R_M32R_24 3 /* Direct 24 bit. */ ++#define R_M32R_10_PCREL 4 /* PC relative 10 bit shifted. */ ++#define R_M32R_18_PCREL 5 /* PC relative 18 bit shifted. */ ++#define R_M32R_26_PCREL 6 /* PC relative 26 bit shifted. */ ++#define R_M32R_HI16_ULO 7 /* High 16 bit with unsigned low. */ ++#define R_M32R_HI16_SLO 8 /* High 16 bit with signed low. */ ++#define R_M32R_LO16 9 /* Low 16 bit. */ ++#define R_M32R_SDA16 10 /* 16 bit offset in SDA. */ ++#define R_M32R_GNU_VTINHERIT 11 ++#define R_M32R_GNU_VTENTRY 12 ++/* M32R relocs use SHT_RELA. */ ++#define R_M32R_16_RELA 33 /* Direct 16 bit. */ ++#define R_M32R_32_RELA 34 /* Direct 32 bit. */ ++#define R_M32R_24_RELA 35 /* Direct 24 bit. */ ++#define R_M32R_10_PCREL_RELA 36 /* PC relative 10 bit shifted. */ ++#define R_M32R_18_PCREL_RELA 37 /* PC relative 18 bit shifted. */ ++#define R_M32R_26_PCREL_RELA 38 /* PC relative 26 bit shifted. */ ++#define R_M32R_HI16_ULO_RELA 39 /* High 16 bit with unsigned low */ ++#define R_M32R_HI16_SLO_RELA 40 /* High 16 bit with signed low */ ++#define R_M32R_LO16_RELA 41 /* Low 16 bit */ ++#define R_M32R_SDA16_RELA 42 /* 16 bit offset in SDA */ ++#define R_M32R_RELA_GNU_VTINHERIT 43 ++#define R_M32R_RELA_GNU_VTENTRY 44 ++#define R_M32R_REL32 45 /* PC relative 32 bit. */ ++ ++#define R_M32R_GOT24 48 /* 24 bit GOT entry */ ++#define R_M32R_26_PLTREL 49 /* 26 bit PC relative to PLT shifted */ ++#define R_M32R_COPY 50 /* Copy symbol at runtime */ ++#define R_M32R_GLOB_DAT 51 /* Create GOT entry */ ++#define R_M32R_JMP_SLOT 52 /* Create PLT entry */ ++#define R_M32R_RELATIVE 53 /* Adjust by program base */ ++#define R_M32R_GOTOFF 54 /* 24 bit offset to GOT */ ++#define R_M32R_GOTPC24 55 /* 24 bit PC relative offset to GOT */ ++#define R_M32R_GOT16_HI_ULO 56 /* High 16 bit GOT entry with unsigned ++ low */ ++#define R_M32R_GOT16_HI_SLO 57 /* High 16 bit GOT entry with signed ++ low */ ++#define R_M32R_GOT16_LO 58 /* Low 16 bit GOT entry */ ++#define R_M32R_GOTPC_HI_ULO 59 /* High 16 bit PC relative offset to ++ GOT with unsigned low */ ++#define R_M32R_GOTPC_HI_SLO 60 /* High 16 bit PC relative offset to ++ GOT with signed low */ ++#define R_M32R_GOTPC_LO 61 /* Low 16 bit PC relative offset to ++ GOT */ ++#define R_M32R_GOTOFF_HI_ULO 62 /* High 16 bit offset to GOT ++ with unsigned low */ ++#define R_M32R_GOTOFF_HI_SLO 63 /* High 16 bit offset to GOT ++ with signed low */ ++#define R_M32R_GOTOFF_LO 64 /* Low 16 bit offset to GOT */ ++#define R_M32R_NUM 256 /* Keep this the last entry. */ ++ ++ ++/* TILEPro relocations. */ ++#define R_TILEPRO_NONE 0 /* No reloc */ ++#define R_TILEPRO_32 1 /* Direct 32 bit */ ++#define R_TILEPRO_16 2 /* Direct 16 bit */ ++#define R_TILEPRO_8 3 /* Direct 8 bit */ ++#define R_TILEPRO_32_PCREL 4 /* PC relative 32 bit */ ++#define R_TILEPRO_16_PCREL 5 /* PC relative 16 bit */ ++#define R_TILEPRO_8_PCREL 6 /* PC relative 8 bit */ ++#define R_TILEPRO_LO16 7 /* Low 16 bit */ ++#define R_TILEPRO_HI16 8 /* High 16 bit */ ++#define R_TILEPRO_HA16 9 /* High 16 bit, adjusted */ ++#define R_TILEPRO_COPY 10 /* Copy relocation */ ++#define R_TILEPRO_GLOB_DAT 11 /* Create GOT entry */ ++#define R_TILEPRO_JMP_SLOT 12 /* Create PLT entry */ ++#define R_TILEPRO_RELATIVE 13 /* Adjust by program base */ ++#define R_TILEPRO_BROFF_X1 14 /* X1 pipe branch offset */ ++#define R_TILEPRO_JOFFLONG_X1 15 /* X1 pipe jump offset */ ++#define R_TILEPRO_JOFFLONG_X1_PLT 16 /* X1 pipe jump offset to PLT */ ++#define R_TILEPRO_IMM8_X0 17 /* X0 pipe 8-bit */ ++#define R_TILEPRO_IMM8_Y0 18 /* Y0 pipe 8-bit */ ++#define R_TILEPRO_IMM8_X1 19 /* X1 pipe 8-bit */ ++#define R_TILEPRO_IMM8_Y1 20 /* Y1 pipe 8-bit */ ++#define R_TILEPRO_MT_IMM15_X1 21 /* X1 pipe mtspr */ ++#define R_TILEPRO_MF_IMM15_X1 22 /* X1 pipe mfspr */ ++#define R_TILEPRO_IMM16_X0 23 /* X0 pipe 16-bit */ ++#define R_TILEPRO_IMM16_X1 24 /* X1 pipe 16-bit */ ++#define R_TILEPRO_IMM16_X0_LO 25 /* X0 pipe low 16-bit */ ++#define R_TILEPRO_IMM16_X1_LO 26 /* X1 pipe low 16-bit */ ++#define R_TILEPRO_IMM16_X0_HI 27 /* X0 pipe high 16-bit */ ++#define R_TILEPRO_IMM16_X1_HI 28 /* X1 pipe high 16-bit */ ++#define R_TILEPRO_IMM16_X0_HA 29 /* X0 pipe high 16-bit, adjusted */ ++#define R_TILEPRO_IMM16_X1_HA 30 /* X1 pipe high 16-bit, adjusted */ ++#define R_TILEPRO_IMM16_X0_PCREL 31 /* X0 pipe PC relative 16 bit */ ++#define R_TILEPRO_IMM16_X1_PCREL 32 /* X1 pipe PC relative 16 bit */ ++#define R_TILEPRO_IMM16_X0_LO_PCREL 33 /* X0 pipe PC relative low 16 bit */ ++#define R_TILEPRO_IMM16_X1_LO_PCREL 34 /* X1 pipe PC relative low 16 bit */ ++#define R_TILEPRO_IMM16_X0_HI_PCREL 35 /* X0 pipe PC relative high 16 bit */ ++#define R_TILEPRO_IMM16_X1_HI_PCREL 36 /* X1 pipe PC relative high 16 bit */ ++#define R_TILEPRO_IMM16_X0_HA_PCREL 37 /* X0 pipe PC relative ha() 16 bit */ ++#define R_TILEPRO_IMM16_X1_HA_PCREL 38 /* X1 pipe PC relative ha() 16 bit */ ++#define R_TILEPRO_IMM16_X0_GOT 39 /* X0 pipe 16-bit GOT offset */ ++#define R_TILEPRO_IMM16_X1_GOT 40 /* X1 pipe 16-bit GOT offset */ ++#define R_TILEPRO_IMM16_X0_GOT_LO 41 /* X0 pipe low 16-bit GOT offset */ ++#define R_TILEPRO_IMM16_X1_GOT_LO 42 /* X1 pipe low 16-bit GOT offset */ ++#define R_TILEPRO_IMM16_X0_GOT_HI 43 /* X0 pipe high 16-bit GOT offset */ ++#define R_TILEPRO_IMM16_X1_GOT_HI 44 /* X1 pipe high 16-bit GOT offset */ ++#define R_TILEPRO_IMM16_X0_GOT_HA 45 /* X0 pipe ha() 16-bit GOT offset */ ++#define R_TILEPRO_IMM16_X1_GOT_HA 46 /* X1 pipe ha() 16-bit GOT offset */ ++#define R_TILEPRO_MMSTART_X0 47 /* X0 pipe mm "start" */ ++#define R_TILEPRO_MMEND_X0 48 /* X0 pipe mm "end" */ ++#define R_TILEPRO_MMSTART_X1 49 /* X1 pipe mm "start" */ ++#define R_TILEPRO_MMEND_X1 50 /* X1 pipe mm "end" */ ++#define R_TILEPRO_SHAMT_X0 51 /* X0 pipe shift amount */ ++#define R_TILEPRO_SHAMT_X1 52 /* X1 pipe shift amount */ ++#define R_TILEPRO_SHAMT_Y0 53 /* Y0 pipe shift amount */ ++#define R_TILEPRO_SHAMT_Y1 54 /* Y1 pipe shift amount */ ++#define R_TILEPRO_DEST_IMM8_X1 55 /* X1 pipe destination 8-bit */ ++/* Relocs 56-59 are currently not defined. */ ++#define R_TILEPRO_TLS_GD_CALL 60 /* "jal" for TLS GD */ ++#define R_TILEPRO_IMM8_X0_TLS_GD_ADD 61 /* X0 pipe "addi" for TLS GD */ ++#define R_TILEPRO_IMM8_X1_TLS_GD_ADD 62 /* X1 pipe "addi" for TLS GD */ ++#define R_TILEPRO_IMM8_Y0_TLS_GD_ADD 63 /* Y0 pipe "addi" for TLS GD */ ++#define R_TILEPRO_IMM8_Y1_TLS_GD_ADD 64 /* Y1 pipe "addi" for TLS GD */ ++#define R_TILEPRO_TLS_IE_LOAD 65 /* "lw_tls" for TLS IE */ ++#define R_TILEPRO_IMM16_X0_TLS_GD 66 /* X0 pipe 16-bit TLS GD offset */ ++#define R_TILEPRO_IMM16_X1_TLS_GD 67 /* X1 pipe 16-bit TLS GD offset */ ++#define R_TILEPRO_IMM16_X0_TLS_GD_LO 68 /* X0 pipe low 16-bit TLS GD offset */ ++#define R_TILEPRO_IMM16_X1_TLS_GD_LO 69 /* X1 pipe low 16-bit TLS GD offset */ ++#define R_TILEPRO_IMM16_X0_TLS_GD_HI 70 /* X0 pipe high 16-bit TLS GD offset */ ++#define R_TILEPRO_IMM16_X1_TLS_GD_HI 71 /* X1 pipe high 16-bit TLS GD offset */ ++#define R_TILEPRO_IMM16_X0_TLS_GD_HA 72 /* X0 pipe ha() 16-bit TLS GD offset */ ++#define R_TILEPRO_IMM16_X1_TLS_GD_HA 73 /* X1 pipe ha() 16-bit TLS GD offset */ ++#define R_TILEPRO_IMM16_X0_TLS_IE 74 /* X0 pipe 16-bit TLS IE offset */ ++#define R_TILEPRO_IMM16_X1_TLS_IE 75 /* X1 pipe 16-bit TLS IE offset */ ++#define R_TILEPRO_IMM16_X0_TLS_IE_LO 76 /* X0 pipe low 16-bit TLS IE offset */ ++#define R_TILEPRO_IMM16_X1_TLS_IE_LO 77 /* X1 pipe low 16-bit TLS IE offset */ ++#define R_TILEPRO_IMM16_X0_TLS_IE_HI 78 /* X0 pipe high 16-bit TLS IE offset */ ++#define R_TILEPRO_IMM16_X1_TLS_IE_HI 79 /* X1 pipe high 16-bit TLS IE offset */ ++#define R_TILEPRO_IMM16_X0_TLS_IE_HA 80 /* X0 pipe ha() 16-bit TLS IE offset */ ++#define R_TILEPRO_IMM16_X1_TLS_IE_HA 81 /* X1 pipe ha() 16-bit TLS IE offset */ ++#define R_TILEPRO_TLS_DTPMOD32 82 /* ID of module containing symbol */ ++#define R_TILEPRO_TLS_DTPOFF32 83 /* Offset in TLS block */ ++#define R_TILEPRO_TLS_TPOFF32 84 /* Offset in static TLS block */ ++#define R_TILEPRO_IMM16_X0_TLS_LE 85 /* X0 pipe 16-bit TLS LE offset */ ++#define R_TILEPRO_IMM16_X1_TLS_LE 86 /* X1 pipe 16-bit TLS LE offset */ ++#define R_TILEPRO_IMM16_X0_TLS_LE_LO 87 /* X0 pipe low 16-bit TLS LE offset */ ++#define R_TILEPRO_IMM16_X1_TLS_LE_LO 88 /* X1 pipe low 16-bit TLS LE offset */ ++#define R_TILEPRO_IMM16_X0_TLS_LE_HI 89 /* X0 pipe high 16-bit TLS LE offset */ ++#define R_TILEPRO_IMM16_X1_TLS_LE_HI 90 /* X1 pipe high 16-bit TLS LE offset */ ++#define R_TILEPRO_IMM16_X0_TLS_LE_HA 91 /* X0 pipe ha() 16-bit TLS LE offset */ ++#define R_TILEPRO_IMM16_X1_TLS_LE_HA 92 /* X1 pipe ha() 16-bit TLS LE offset */ ++ ++#define R_TILEPRO_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */ ++#define R_TILEPRO_GNU_VTENTRY 129 /* GNU C++ vtable member usage */ ++ ++#define R_TILEPRO_NUM 130 ++ ++ ++/* TILE-Gx relocations. */ ++#define R_TILEGX_NONE 0 /* No reloc */ ++#define R_TILEGX_64 1 /* Direct 64 bit */ ++#define R_TILEGX_32 2 /* Direct 32 bit */ ++#define R_TILEGX_16 3 /* Direct 16 bit */ ++#define R_TILEGX_8 4 /* Direct 8 bit */ ++#define R_TILEGX_64_PCREL 5 /* PC relative 64 bit */ ++#define R_TILEGX_32_PCREL 6 /* PC relative 32 bit */ ++#define R_TILEGX_16_PCREL 7 /* PC relative 16 bit */ ++#define R_TILEGX_8_PCREL 8 /* PC relative 8 bit */ ++#define R_TILEGX_HW0 9 /* hword 0 16-bit */ ++#define R_TILEGX_HW1 10 /* hword 1 16-bit */ ++#define R_TILEGX_HW2 11 /* hword 2 16-bit */ ++#define R_TILEGX_HW3 12 /* hword 3 16-bit */ ++#define R_TILEGX_HW0_LAST 13 /* last hword 0 16-bit */ ++#define R_TILEGX_HW1_LAST 14 /* last hword 1 16-bit */ ++#define R_TILEGX_HW2_LAST 15 /* last hword 2 16-bit */ ++#define R_TILEGX_COPY 16 /* Copy relocation */ ++#define R_TILEGX_GLOB_DAT 17 /* Create GOT entry */ ++#define R_TILEGX_JMP_SLOT 18 /* Create PLT entry */ ++#define R_TILEGX_RELATIVE 19 /* Adjust by program base */ ++#define R_TILEGX_BROFF_X1 20 /* X1 pipe branch offset */ ++#define R_TILEGX_JUMPOFF_X1 21 /* X1 pipe jump offset */ ++#define R_TILEGX_JUMPOFF_X1_PLT 22 /* X1 pipe jump offset to PLT */ ++#define R_TILEGX_IMM8_X0 23 /* X0 pipe 8-bit */ ++#define R_TILEGX_IMM8_Y0 24 /* Y0 pipe 8-bit */ ++#define R_TILEGX_IMM8_X1 25 /* X1 pipe 8-bit */ ++#define R_TILEGX_IMM8_Y1 26 /* Y1 pipe 8-bit */ ++#define R_TILEGX_DEST_IMM8_X1 27 /* X1 pipe destination 8-bit */ ++#define R_TILEGX_MT_IMM14_X1 28 /* X1 pipe mtspr */ ++#define R_TILEGX_MF_IMM14_X1 29 /* X1 pipe mfspr */ ++#define R_TILEGX_MMSTART_X0 30 /* X0 pipe mm "start" */ ++#define R_TILEGX_MMEND_X0 31 /* X0 pipe mm "end" */ ++#define R_TILEGX_SHAMT_X0 32 /* X0 pipe shift amount */ ++#define R_TILEGX_SHAMT_X1 33 /* X1 pipe shift amount */ ++#define R_TILEGX_SHAMT_Y0 34 /* Y0 pipe shift amount */ ++#define R_TILEGX_SHAMT_Y1 35 /* Y1 pipe shift amount */ ++#define R_TILEGX_IMM16_X0_HW0 36 /* X0 pipe hword 0 */ ++#define R_TILEGX_IMM16_X1_HW0 37 /* X1 pipe hword 0 */ ++#define R_TILEGX_IMM16_X0_HW1 38 /* X0 pipe hword 1 */ ++#define R_TILEGX_IMM16_X1_HW1 39 /* X1 pipe hword 1 */ ++#define R_TILEGX_IMM16_X0_HW2 40 /* X0 pipe hword 2 */ ++#define R_TILEGX_IMM16_X1_HW2 41 /* X1 pipe hword 2 */ ++#define R_TILEGX_IMM16_X0_HW3 42 /* X0 pipe hword 3 */ ++#define R_TILEGX_IMM16_X1_HW3 43 /* X1 pipe hword 3 */ ++#define R_TILEGX_IMM16_X0_HW0_LAST 44 /* X0 pipe last hword 0 */ ++#define R_TILEGX_IMM16_X1_HW0_LAST 45 /* X1 pipe last hword 0 */ ++#define R_TILEGX_IMM16_X0_HW1_LAST 46 /* X0 pipe last hword 1 */ ++#define R_TILEGX_IMM16_X1_HW1_LAST 47 /* X1 pipe last hword 1 */ ++#define R_TILEGX_IMM16_X0_HW2_LAST 48 /* X0 pipe last hword 2 */ ++#define R_TILEGX_IMM16_X1_HW2_LAST 49 /* X1 pipe last hword 2 */ ++#define R_TILEGX_IMM16_X0_HW0_PCREL 50 /* X0 pipe PC relative hword 0 */ ++#define R_TILEGX_IMM16_X1_HW0_PCREL 51 /* X1 pipe PC relative hword 0 */ ++#define R_TILEGX_IMM16_X0_HW1_PCREL 52 /* X0 pipe PC relative hword 1 */ ++#define R_TILEGX_IMM16_X1_HW1_PCREL 53 /* X1 pipe PC relative hword 1 */ ++#define R_TILEGX_IMM16_X0_HW2_PCREL 54 /* X0 pipe PC relative hword 2 */ ++#define R_TILEGX_IMM16_X1_HW2_PCREL 55 /* X1 pipe PC relative hword 2 */ ++#define R_TILEGX_IMM16_X0_HW3_PCREL 56 /* X0 pipe PC relative hword 3 */ ++#define R_TILEGX_IMM16_X1_HW3_PCREL 57 /* X1 pipe PC relative hword 3 */ ++#define R_TILEGX_IMM16_X0_HW0_LAST_PCREL 58 /* X0 pipe PC-rel last hword 0 */ ++#define R_TILEGX_IMM16_X1_HW0_LAST_PCREL 59 /* X1 pipe PC-rel last hword 0 */ ++#define R_TILEGX_IMM16_X0_HW1_LAST_PCREL 60 /* X0 pipe PC-rel last hword 1 */ ++#define R_TILEGX_IMM16_X1_HW1_LAST_PCREL 61 /* X1 pipe PC-rel last hword 1 */ ++#define R_TILEGX_IMM16_X0_HW2_LAST_PCREL 62 /* X0 pipe PC-rel last hword 2 */ ++#define R_TILEGX_IMM16_X1_HW2_LAST_PCREL 63 /* X1 pipe PC-rel last hword 2 */ ++#define R_TILEGX_IMM16_X0_HW0_GOT 64 /* X0 pipe hword 0 GOT offset */ ++#define R_TILEGX_IMM16_X1_HW0_GOT 65 /* X1 pipe hword 0 GOT offset */ ++/* Relocs 66-71 are currently not defined. */ ++#define R_TILEGX_IMM16_X0_HW0_LAST_GOT 72 /* X0 pipe last hword 0 GOT offset */ ++#define R_TILEGX_IMM16_X1_HW0_LAST_GOT 73 /* X1 pipe last hword 0 GOT offset */ ++#define R_TILEGX_IMM16_X0_HW1_LAST_GOT 74 /* X0 pipe last hword 1 GOT offset */ ++#define R_TILEGX_IMM16_X1_HW1_LAST_GOT 75 /* X1 pipe last hword 1 GOT offset */ ++/* Relocs 76-77 are currently not defined. */ ++#define R_TILEGX_IMM16_X0_HW0_TLS_GD 78 /* X0 pipe hword 0 TLS GD offset */ ++#define R_TILEGX_IMM16_X1_HW0_TLS_GD 79 /* X1 pipe hword 0 TLS GD offset */ ++#define R_TILEGX_IMM16_X0_HW0_TLS_LE 80 /* X0 pipe hword 0 TLS LE offset */ ++#define R_TILEGX_IMM16_X1_HW0_TLS_LE 81 /* X1 pipe hword 0 TLS LE offset */ ++#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE 82 /* X0 pipe last hword 0 LE off */ ++#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE 83 /* X1 pipe last hword 0 LE off */ ++#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE 84 /* X0 pipe last hword 1 LE off */ ++#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE 85 /* X1 pipe last hword 1 LE off */ ++#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD 86 /* X0 pipe last hword 0 GD off */ ++#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD 87 /* X1 pipe last hword 0 GD off */ ++#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD 88 /* X0 pipe last hword 1 GD off */ ++#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD 89 /* X1 pipe last hword 1 GD off */ ++/* Relocs 90-91 are currently not defined. */ ++#define R_TILEGX_IMM16_X0_HW0_TLS_IE 92 /* X0 pipe hword 0 TLS IE offset */ ++#define R_TILEGX_IMM16_X1_HW0_TLS_IE 93 /* X1 pipe hword 0 TLS IE offset */ ++/* Relocs 94-99 are currently not defined. */ ++#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE 100 /* X0 pipe last hword 0 IE off */ ++#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE 101 /* X1 pipe last hword 0 IE off */ ++#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE 102 /* X0 pipe last hword 1 IE off */ ++#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE 103 /* X1 pipe last hword 1 IE off */ ++/* Relocs 104-105 are currently not defined. */ ++#define R_TILEGX_TLS_DTPMOD64 106 /* 64-bit ID of symbol's module */ ++#define R_TILEGX_TLS_DTPOFF64 107 /* 64-bit offset in TLS block */ ++#define R_TILEGX_TLS_TPOFF64 108 /* 64-bit offset in static TLS block */ ++#define R_TILEGX_TLS_DTPMOD32 109 /* 32-bit ID of symbol's module */ ++#define R_TILEGX_TLS_DTPOFF32 110 /* 32-bit offset in TLS block */ ++#define R_TILEGX_TLS_TPOFF32 111 /* 32-bit offset in static TLS block */ ++#define R_TILEGX_TLS_GD_CALL 112 /* "jal" for TLS GD */ ++#define R_TILEGX_IMM8_X0_TLS_GD_ADD 113 /* X0 pipe "addi" for TLS GD */ ++#define R_TILEGX_IMM8_X1_TLS_GD_ADD 114 /* X1 pipe "addi" for TLS GD */ ++#define R_TILEGX_IMM8_Y0_TLS_GD_ADD 115 /* Y0 pipe "addi" for TLS GD */ ++#define R_TILEGX_IMM8_Y1_TLS_GD_ADD 116 /* Y1 pipe "addi" for TLS GD */ ++#define R_TILEGX_TLS_IE_LOAD 117 /* "ld_tls" for TLS IE */ ++#define R_TILEGX_IMM8_X0_TLS_ADD 118 /* X0 pipe "addi" for TLS GD/IE */ ++#define R_TILEGX_IMM8_X1_TLS_ADD 119 /* X1 pipe "addi" for TLS GD/IE */ ++#define R_TILEGX_IMM8_Y0_TLS_ADD 120 /* Y0 pipe "addi" for TLS GD/IE */ ++#define R_TILEGX_IMM8_Y1_TLS_ADD 121 /* Y1 pipe "addi" for TLS GD/IE */ ++ ++#define R_TILEGX_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */ ++#define R_TILEGX_GNU_VTENTRY 129 /* GNU C++ vtable member usage */ ++ ++#define R_TILEGX_NUM 130 ++ ++#endif /* elf.h */ +--- a/scripts/mod/mk_elfconfig.c ++++ b/scripts/mod/mk_elfconfig.c +@@ -2,7 +2,11 @@ + #include + #include + #include ++#ifndef __APPLE__ + #include ++#else ++#include "elf.h" ++#endif + + int + main(int argc, char **argv) +--- a/scripts/mod/modpost.h ++++ b/scripts/mod/modpost.h +@@ -9,7 +9,11 @@ + #include + #include + #include ++#if !(defined(__APPLE__) || defined(__CYGWIN__)) + #include ++#else ++#include "elf.h" ++#endif + + #include "list.h" + #include "elfconfig.h" diff --git a/target/linux/generic/hack-6.6/211-darwin-uuid-typedef-clash.patch b/target/linux/generic/hack-6.6/211-darwin-uuid-typedef-clash.patch new file mode 100644 index 0000000000..50a6227148 --- /dev/null +++ b/target/linux/generic/hack-6.6/211-darwin-uuid-typedef-clash.patch @@ -0,0 +1,22 @@ +From e44fc2af1ddc452b6659d08c16973d65c73b7d0a Mon Sep 17 00:00:00 2001 +From: Kevin Darbyshire-Bryant +Date: Wed, 5 Feb 2020 18:36:43 +0000 +Subject: [PATCH] file2alias: build on macos + +Signed-off-by: Kevin Darbyshire-Bryant +--- + scripts/mod/file2alias.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/scripts/mod/file2alias.c ++++ b/scripts/mod/file2alias.c +@@ -38,6 +38,9 @@ typedef struct { + __u8 b[16]; + } guid_t; + ++#ifdef __APPLE__ ++#define uuid_t compat_uuid_t ++#endif + /* backwards compatibility, don't use in new code */ + typedef struct { + __u8 b[16]; diff --git a/target/linux/generic/hack-6.6/212-tools_portability.patch b/target/linux/generic/hack-6.6/212-tools_portability.patch new file mode 100644 index 0000000000..add281991e --- /dev/null +++ b/target/linux/generic/hack-6.6/212-tools_portability.patch @@ -0,0 +1,342 @@ +From 48232d3d931c95953ce2ddfe7da7bb164aef6a73 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Fri, 7 Jul 2017 17:03:16 +0200 +Subject: fix portability of some includes files in tools/ used on the host + +Signed-off-by: Felix Fietkau +--- + tools/include/tools/be_byteshift.h | 4 ++++ + tools/include/tools/le_byteshift.h | 4 ++++ + tools/include/tools/linux_types.h | 22 ++++++++++++++++++++++ + 3 files changed, 30 insertions(+) + create mode 100644 tools/include/tools/linux_types.h + +--- a/tools/include/tools/be_byteshift.h ++++ b/tools/include/tools/be_byteshift.h +@@ -2,6 +2,10 @@ + #ifndef _TOOLS_BE_BYTESHIFT_H + #define _TOOLS_BE_BYTESHIFT_H + ++#ifndef __linux__ ++#include "linux_types.h" ++#endif ++ + #include + + static inline uint16_t __get_unaligned_be16(const uint8_t *p) +--- a/tools/include/tools/le_byteshift.h ++++ b/tools/include/tools/le_byteshift.h +@@ -2,6 +2,10 @@ + #ifndef _TOOLS_LE_BYTESHIFT_H + #define _TOOLS_LE_BYTESHIFT_H + ++#ifndef __linux__ ++#include "linux_types.h" ++#endif ++ + #include + + static inline uint16_t __get_unaligned_le16(const uint8_t *p) +--- /dev/null ++++ b/tools/include/tools/linux_types.h +@@ -0,0 +1,18 @@ ++#ifndef __LINUX_TYPES_H ++#define __LINUX_TYPES_H ++ ++#include ++ ++typedef int8_t __s8; ++typedef uint8_t __u8; ++ ++typedef int16_t __s16; ++typedef uint16_t __u16; ++ ++typedef int32_t __s32; ++typedef uint32_t __u32; ++ ++typedef int64_t __s64; ++typedef uint64_t __u64; ++ ++#endif +--- a/tools/include/linux/types.h ++++ b/tools/include/linux/types.h +@@ -10,8 +10,12 @@ + #define __SANE_USERSPACE_TYPES__ /* For PPC64, to get LL64 types */ + #endif + ++#ifndef __linux__ ++#include ++#else + #include + #include ++#endif + + struct page; + struct kmem_cache; +@@ -51,7 +55,9 @@ typedef __s8 s8; + #define __force + #define __user + #define __must_check ++#ifndef __cold + #define __cold ++#endif + + typedef __u16 __bitwise __le16; + typedef __u16 __bitwise __be16; +--- a/tools/perf/pmu-events/jevents.py ++++ b/tools/perf/pmu-events/jevents.py +@@ -684,6 +684,7 @@ def main() -> None: + #include "util/header.h" + #include "util/pmu.h" + #include ++#include + #include + + struct compact_pmu_event { +--- a/tools/arch/x86/include/asm/insn.h ++++ b/tools/arch/x86/include/asm/insn.h +@@ -7,8 +7,8 @@ + * Copyright (C) IBM Corporation, 2009 + */ + +-#include + /* insn_attr_t is defined in inat.h */ ++#include + #include "inat.h" /* __ignore_sync_check__ */ + + #if defined(__BYTE_ORDER) ? __BYTE_ORDER == __LITTLE_ENDIAN : defined(__LITTLE_ENDIAN) +--- a/tools/arch/x86/include/asm/orc_types.h ++++ b/tools/arch/x86/include/asm/orc_types.h +@@ -40,7 +40,6 @@ + #define ORC_REG_MAX 15 + + #ifndef __ASSEMBLY__ +-#include + + /* + * This struct is more or less a vastly simplified version of the DWARF Call +@@ -53,12 +52,12 @@ + struct orc_entry { + s16 sp_offset; + s16 bp_offset; +-#if defined(__LITTLE_ENDIAN_BITFIELD) ++#if __BYTE_ORDER == __LITTLE_ENDIAN + unsigned sp_reg:4; + unsigned bp_reg:4; + unsigned type:2; + unsigned end:1; +-#elif defined(__BIG_ENDIAN_BITFIELD) ++#elif __BYTE_ORDER == __BIG_ENDIAN + unsigned bp_reg:4; + unsigned sp_reg:4; + unsigned unused:5; +--- a/tools/arch/x86/lib/insn.c ++++ b/tools/arch/x86/lib/insn.c +@@ -15,7 +15,11 @@ + #include "../include/asm/insn.h" /* __ignore_sync_check__ */ + #include "../include/asm-generic/unaligned.h" /* __ignore_sync_check__ */ + ++#ifdef __KERNEL__ + #include ++#else ++#include ++#endif + #include + + #include "../include/asm/emulate_prefix.h" /* __ignore_sync_check__ */ +--- a/tools/include/asm-generic/bitops/fls.h ++++ b/tools/include/asm-generic/bitops/fls.h +@@ -2,6 +2,8 @@ + #ifndef _ASM_GENERIC_BITOPS_FLS_H_ + #define _ASM_GENERIC_BITOPS_FLS_H_ + ++#include ++ + /** + * fls - find last (most-significant) bit set + * @x: the word to search +@@ -10,7 +12,7 @@ + * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. + */ + +-static __always_inline int fls(unsigned int x) ++static __always_inline int __generic_fls(unsigned int x) + { + int r = 32; + +@@ -38,5 +40,6 @@ static __always_inline int fls(unsigned + } + return r; + } ++#define fls __generic_fls + + #endif /* _ASM_GENERIC_BITOPS_FLS_H_ */ +--- a/tools/include/asm-generic/bitsperlong.h ++++ b/tools/include/asm-generic/bitsperlong.h +@@ -4,11 +4,13 @@ + + #include + ++#ifndef BITS_PER_LONG + #ifdef __SIZEOF_LONG__ + #define BITS_PER_LONG (__CHAR_BIT__ * __SIZEOF_LONG__) + #else + #define BITS_PER_LONG __WORDSIZE + #endif ++#endif + + #if BITS_PER_LONG != __BITS_PER_LONG + #error Inconsistent word size. Check asm/bitsperlong.h +--- a/tools/include/linux/rbtree.h ++++ b/tools/include/linux/rbtree.h +@@ -18,7 +18,6 @@ + #define __TOOLS_LINUX_PERF_RBTREE_H + + #include +-#include + + struct rb_node { + unsigned long __rb_parent_color; +--- a/tools/objtool/Makefile ++++ b/tools/objtool/Makefile +@@ -4,7 +4,7 @@ include ../scripts/Makefile.arch + + # always use the host compiler + AR = $(HOSTAR) +-CC = $(HOSTCC) ++CC = $(HOSTCC) $(HOST_EXTRACFLAGS) + LD = $(HOSTLD) + + ifeq ($(srctree),) +@@ -43,6 +43,7 @@ BUILD_ORC := n + + ifeq ($(SRCARCH),x86) + BUILD_ORC := y ++ CFLAGS += -DBUILD_ORC + endif + + export BUILD_ORC +--- a/tools/objtool/check.c ++++ b/tools/objtool/check.c +@@ -1164,11 +1164,12 @@ static int add_ignore_alternatives(struc + return 0; + } + ++#ifndef BUILD_ORC + /* + * Symbols that replace INSN_CALL_DYNAMIC, every (tail) call to such a symbol + * will be added to the .retpoline_sites section. + */ +-__weak bool arch_is_retpoline(struct symbol *sym) ++bool arch_is_retpoline(struct symbol *sym) + { + return false; + } +@@ -1177,7 +1178,7 @@ __weak bool arch_is_retpoline(struct sym + * Symbols that replace INSN_RETURN, every (tail) call to such a symbol + * will be added to the .return_sites section. + */ +-__weak bool arch_is_rethunk(struct symbol *sym) ++bool arch_is_rethunk(struct symbol *sym) + { + return false; + } +@@ -1186,10 +1187,11 @@ __weak bool arch_is_rethunk(struct symbo + * Symbols that are embedded inside other instructions, because sometimes crazy + * code exists. These are mostly ignored for validation purposes. + */ +-__weak bool arch_is_embedded_insn(struct symbol *sym) ++bool arch_is_embedded_insn(struct symbol *sym) + { + return false; + } ++#endif + + #define NEGATIVE_RELOC ((void *)-1L) + +--- a/tools/objtool/include/objtool/objtool.h ++++ b/tools/objtool/include/objtool/objtool.h +@@ -12,7 +12,9 @@ + + #include + ++#ifndef __weak + #define __weak __attribute__((weak)) ++#endif + + struct pv_state { + bool clean; +--- a/tools/objtool/orc_dump.c ++++ b/tools/objtool/orc_dump.c +@@ -5,10 +5,10 @@ + + #include + #include +-#include + #include + #include + #include ++#include + + static const char *reg_name(unsigned int reg) + { +--- a/tools/objtool/orc_gen.c ++++ b/tools/objtool/orc_gen.c +@@ -7,11 +7,11 @@ + #include + + #include +-#include + + #include + #include + #include ++#include + + static int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, + struct instruction *insn) +--- a/tools/objtool/special.c ++++ b/tools/objtool/special.c +@@ -54,9 +54,11 @@ struct special_entry entries[] = { + {}, + }; + +-void __weak arch_handle_alternative(unsigned short feature, struct special_alt *alt) ++#ifndef BUILD_ORC ++void arch_handle_alternative(unsigned short feature, struct special_alt *alt) + { + } ++#endif + + static void reloc_to_sec_off(struct reloc *reloc, struct section **sec, + unsigned long *off) +--- a/tools/objtool/weak.c ++++ b/tools/objtool/weak.c +@@ -15,12 +15,14 @@ + return ENOSYS; \ + }) + +-int __weak orc_dump(const char *_objname) ++#ifndef BUILD_ORC ++int orc_dump(const char *_objname) + { + UNSUPPORTED("ORC"); + } + +-int __weak orc_create(struct objtool_file *file) ++int orc_create(struct objtool_file *file) + { + UNSUPPORTED("ORC"); + } ++#endif +--- a/tools/scripts/Makefile.include ++++ b/tools/scripts/Makefile.include +@@ -92,7 +92,7 @@ LLVM_OBJCOPY ?= llvm-objcopy + LLVM_STRIP ?= llvm-strip + + ifeq ($(CC_NO_CLANG), 1) +-EXTRA_WARNINGS += -Wstrict-aliasing=3 ++# EXTRA_WARNINGS += -Wstrict-aliasing=3 + + else ifneq ($(CROSS_COMPILE),) + # Allow userspace to override CLANG_CROSS_FLAGS to specify their own diff --git a/target/linux/generic/hack-6.6/214-spidev_h_portability.patch b/target/linux/generic/hack-6.6/214-spidev_h_portability.patch new file mode 100644 index 0000000000..db754a2903 --- /dev/null +++ b/target/linux/generic/hack-6.6/214-spidev_h_portability.patch @@ -0,0 +1,24 @@ +From be9be95ff10e16a5b4ad36f903978d0cc5747024 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Fri, 7 Jul 2017 17:04:08 +0200 +Subject: kernel: fix linux/spi/spidev.h portability issues with musl + +Felix will try to get this define included into musl + +lede-commit: 795e7cf60de19e7a076a46874fab7bb88b43bbff +Signed-off-by: Felix Fietkau +--- + include/uapi/linux/spi/spidev.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/include/uapi/linux/spi/spidev.h ++++ b/include/uapi/linux/spi/spidev.h +@@ -93,7 +93,7 @@ struct spi_ioc_transfer { + + /* not all platforms use or _IOC_TYPECHECK() ... */ + #define SPI_MSGSIZE(N) \ +- ((((N)*(sizeof (struct spi_ioc_transfer))) < (1 << _IOC_SIZEBITS)) \ ++ ((((N)*(sizeof (struct spi_ioc_transfer))) < (1 << 13)) \ + ? ((N)*(sizeof (struct spi_ioc_transfer))) : 0) + #define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)]) + diff --git a/target/linux/generic/hack-6.6/220-arm-gc_sections.patch b/target/linux/generic/hack-6.6/220-arm-gc_sections.patch new file mode 100644 index 0000000000..a6a6c7cf2c --- /dev/null +++ b/target/linux/generic/hack-6.6/220-arm-gc_sections.patch @@ -0,0 +1,123 @@ +From e3d8676f5722b7622685581e06e8f53e6138e3ab Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Sat, 15 Jul 2017 23:42:36 +0200 +Subject: use -ffunction-sections, -fdata-sections and --gc-sections + +In combination with kernel symbol export stripping this significantly reduces +the kernel image size. Used on both ARM and MIPS architectures. + +Signed-off-by: Felix Fietkau +Signed-off-by: Jonas Gorski +Signed-off-by: Gabor Juhos +--- +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -124,6 +124,7 @@ config ARM + select HAVE_VIRT_CPU_ACCOUNTING_GEN + select IRQ_FORCED_THREADING + select LOCK_MM_AND_FIND_VMA ++ select HAVE_LD_DEAD_CODE_DATA_ELIMINATION + select MODULES_USE_ELF_REL + select NEED_DMA_MAP_STATE + select OF_EARLY_FLATTREE if OF +--- a/arch/arm/boot/compressed/Makefile ++++ b/arch/arm/boot/compressed/Makefile +@@ -91,6 +91,7 @@ endif + ifeq ($(CONFIG_USE_OF),y) + OBJS += $(libfdt_objs) fdt_check_mem_start.o + endif ++KBUILD_CFLAGS_KERNEL := $(patsubst -f%-sections,,$(KBUILD_CFLAGS_KERNEL)) + + OBJS += lib1funcs.o ashldi3.o bswapsdi2.o + +--- a/arch/arm/kernel/vmlinux.lds.S ++++ b/arch/arm/kernel/vmlinux.lds.S +@@ -75,7 +75,7 @@ SECTIONS + . = ALIGN(4); + __ex_table : AT(ADDR(__ex_table) - LOAD_OFFSET) { + __start___ex_table = .; +- ARM_MMU_KEEP(*(__ex_table)) ++ KEEP(*(__ex_table)) + __stop___ex_table = .; + } + +@@ -100,24 +100,24 @@ SECTIONS + } + .init.arch.info : { + __arch_info_begin = .; +- *(.arch.info.init) ++ KEEP(*(.arch.info.init)) + __arch_info_end = .; + } + .init.tagtable : { + __tagtable_begin = .; +- *(.taglist.init) ++ KEEP(*(.taglist.init)) + __tagtable_end = .; + } + #ifdef CONFIG_SMP_ON_UP + .init.smpalt : { + __smpalt_begin = .; +- *(.alt.smp.init) ++ KEEP(*(.alt.smp.init)) + __smpalt_end = .; + } + #endif + .init.pv_table : { + __pv_table_begin = .; +- *(.pv_table) ++ KEEP(*(.pv_table)) + __pv_table_end = .; + } + +--- a/arch/arm/include/asm/vmlinux.lds.h ++++ b/arch/arm/include/asm/vmlinux.lds.h +@@ -42,13 +42,13 @@ + #define PROC_INFO \ + . = ALIGN(4); \ + __proc_info_begin = .; \ +- *(.proc.info.init) \ ++ KEEP(*(.proc.info.init)) \ + __proc_info_end = .; + + #define IDMAP_TEXT \ + ALIGN_FUNCTION(); \ + __idmap_text_start = .; \ +- *(.idmap.text) \ ++ KEEP(*(.idmap.text)) \ + __idmap_text_end = .; \ + + #define ARM_DISCARD \ +@@ -109,12 +109,12 @@ + . = ALIGN(8); \ + .ARM.unwind_idx : { \ + __start_unwind_idx = .; \ +- *(.ARM.exidx*) \ ++ KEEP(*(.ARM.exidx*)) \ + __stop_unwind_idx = .; \ + } \ + .ARM.unwind_tab : { \ + __start_unwind_tab = .; \ +- *(.ARM.extab*) \ ++ KEEP(*(.ARM.extab*)) \ + __stop_unwind_tab = .; \ + } + +@@ -126,7 +126,7 @@ + __vectors_lma = .; \ + OVERLAY 0xffff0000 : NOCROSSREFS AT(__vectors_lma) { \ + .vectors { \ +- *(.vectors) \ ++ KEEP(*(.vectors)) \ + } \ + .vectors.bhb.loop8 { \ + *(.vectors.bhb.loop8) \ +@@ -144,7 +144,7 @@ + \ + __stubs_lma = .; \ + .stubs ADDR(.vectors) + 0x1000 : AT(__stubs_lma) { \ +- *(.stubs) \ ++ KEEP(*(.stubs)) \ + } \ + ARM_LMA(__stubs, .stubs); \ + . = __stubs_lma + SIZEOF(.stubs); \ diff --git a/target/linux/generic/hack-6.6/221-module_exports.patch b/target/linux/generic/hack-6.6/221-module_exports.patch new file mode 100644 index 0000000000..967510bcb6 --- /dev/null +++ b/target/linux/generic/hack-6.6/221-module_exports.patch @@ -0,0 +1,126 @@ +From b14784e7883390c20ed3ff904892255404a5914b Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Fri, 7 Jul 2017 17:05:53 +0200 +Subject: add an optional config option for stripping all unnecessary symbol exports from the kernel image + +lede-commit: bb5a40c64b7c4f4848509fa0a6625055fc9e66cc +Signed-off-by: Felix Fietkau +--- + include/asm-generic/vmlinux.lds.h | 18 +++++++++++++++--- + include/linux/export.h | 9 ++++++++- + scripts/Makefile.build | 2 +- + 3 files changed, 24 insertions(+), 5 deletions(-) + +--- a/include/asm-generic/vmlinux.lds.h ++++ b/include/asm-generic/vmlinux.lds.h +@@ -81,6 +81,16 @@ + #define RO_EXCEPTION_TABLE + #endif + ++#ifndef SYMTAB_KEEP ++#define SYMTAB_KEEP KEEP(*(SORT(___ksymtab+*))) ++#define SYMTAB_KEEP_GPL KEEP(*(SORT(___ksymtab_gpl+*))) ++#endif ++ ++#ifndef SYMTAB_DISCARD ++#define SYMTAB_DISCARD ++#define SYMTAB_DISCARD_GPL ++#endif ++ + /* Align . to a 8 byte boundary equals to maximum function alignment. */ + #define ALIGN_FUNCTION() . = ALIGN(8) + +@@ -511,14 +521,14 @@ + /* Kernel symbol table: Normal symbols */ \ + __ksymtab : AT(ADDR(__ksymtab) - LOAD_OFFSET) { \ + __start___ksymtab = .; \ +- KEEP(*(SORT(___ksymtab+*))) \ ++ SYMTAB_KEEP \ + __stop___ksymtab = .; \ + } \ + \ + /* Kernel symbol table: GPL-only symbols */ \ + __ksymtab_gpl : AT(ADDR(__ksymtab_gpl) - LOAD_OFFSET) { \ + __start___ksymtab_gpl = .; \ +- KEEP(*(SORT(___ksymtab_gpl+*))) \ ++ SYMTAB_KEEP_GPL \ + __stop___ksymtab_gpl = .; \ + } \ + \ +@@ -538,7 +548,7 @@ + \ + /* Kernel symbol table: strings */ \ + __ksymtab_strings : AT(ADDR(__ksymtab_strings) - LOAD_OFFSET) { \ +- *(__ksymtab_strings) \ ++ *(__ksymtab_strings+*) \ + } \ + \ + /* __*init sections */ \ +@@ -1042,6 +1052,8 @@ + #define COMMON_DISCARDS \ + SANITIZER_DISCARDS \ + PATCHABLE_DISCARDS \ ++ SYMTAB_DISCARD \ ++ SYMTAB_DISCARD_GPL \ + *(.discard) \ + *(.discard.*) \ + *(.modinfo) \ +--- a/include/linux/export.h ++++ b/include/linux/export.h +@@ -72,6 +72,12 @@ struct kernel_symbol { + + #else + ++#ifdef MODULE ++#define __EXPORT_SUFFIX(sym) ++#else ++#define __EXPORT_SUFFIX(sym) "+" #sym ++#endif ++ + /* + * For every exported symbol, do the following: + * +@@ -87,7 +93,7 @@ struct kernel_symbol { + extern typeof(sym) sym; \ + extern const char __kstrtab_##sym[]; \ + extern const char __kstrtabns_##sym[]; \ +- asm(" .section \"__ksymtab_strings\",\"aMS\",%progbits,1 \n" \ ++ asm(" .section \"__ksymtab_strings" __EXPORT_SUFFIX(sym) "\",\"aMS\",%progbits,1 \n" \ + "__kstrtab_" #sym ": \n" \ + " .asciz \"" #sym "\" \n" \ + "__kstrtabns_" #sym ": \n" \ +--- a/include/asm-generic/export.h ++++ b/include/asm-generic/export.h +@@ -31,6 +31,12 @@ + #endif + .endm + ++#ifdef MODULE ++#define __EXPORT_SUFFIX(name) ++#else ++#define __EXPORT_SUFFIX(name) + #name ++#endif ++ + /* + * note on .section use: we specify progbits since usage of the "M" (SHF_MERGE) + * section flag requires it. Use '%progbits' instead of '@progbits' since the +@@ -44,7 +50,7 @@ + __ksymtab_\name: + __put \val, __kstrtab_\name + .previous +- .section __ksymtab_strings,"aMS",%progbits,1 ++ .section __ksymtab_strings __EXPORT_SUFFIX(name),"aMS",%progbits,1 + __kstrtab_\name: + .asciz "\name" + .previous +--- a/scripts/Makefile.build ++++ b/scripts/Makefile.build +@@ -388,7 +388,7 @@ targets += $(real-dtb-y) $(lib-y) $(alwa + # Linker scripts preprocessor (.lds.S -> .lds) + # --------------------------------------------------------------------------- + quiet_cmd_cpp_lds_S = LDS $@ +- cmd_cpp_lds_S = $(CPP) $(cpp_flags) -P -U$(ARCH) \ ++ cmd_cpp_lds_S = $(CPP) $(EXTRA_LDSFLAGS) $(cpp_flags) -P -U$(ARCH) \ + -D__ASSEMBLY__ -DLINKER_SCRIPT -o $@ $< + + $(obj)/%.lds: $(src)/%.lds.S FORCE diff --git a/target/linux/generic/hack-6.6/230-openwrt_lzma_options.patch b/target/linux/generic/hack-6.6/230-openwrt_lzma_options.patch new file mode 100644 index 0000000000..55530c5c7e --- /dev/null +++ b/target/linux/generic/hack-6.6/230-openwrt_lzma_options.patch @@ -0,0 +1,38 @@ +From b3d00b452467f621317953d9e4c6f9ae8dcfd271 Mon Sep 17 00:00:00 2001 +From: Imre Kaloz +Date: Fri, 7 Jul 2017 17:06:55 +0200 +Subject: use the openwrt lzma options for now + +lede-commit: 548de949f392049420a6a1feeef118b30ab8ea8c +Signed-off-by: Imre Kaloz +--- + lib/decompress.c | 1 + + scripts/Makefile.lib | 2 +- + usr/gen_initramfs_list.sh | 10 +++++----- + 3 files changed, 7 insertions(+), 6 deletions(-) + +--- a/lib/decompress.c ++++ b/lib/decompress.c +@@ -53,6 +53,7 @@ static const struct compress_format comp + { {0x1f, 0x9e}, "gzip", gunzip }, + { {0x42, 0x5a}, "bzip2", bunzip2 }, + { {0x5d, 0x00}, "lzma", unlzma }, ++ { {0x6d, 0x00}, "lzma-openwrt", unlzma }, + { {0xfd, 0x37}, "xz", unxz }, + { {0x89, 0x4c}, "lzo", unlzo }, + { {0x02, 0x21}, "lz4", unlz4 }, +--- a/scripts/Makefile.lib ++++ b/scripts/Makefile.lib +@@ -443,10 +443,10 @@ quiet_cmd_bzip2_with_size = BZIP2 $@ + # --------------------------------------------------------------------------- + + quiet_cmd_lzma = LZMA $@ +- cmd_lzma = cat $(real-prereqs) | $(LZMA) -9 > $@ ++ cmd_lzma = cat $(real-prereqs) | $(LZMA) e -d20 -lc1 -lp2 -pb2 -eos -si -so > $@ + + quiet_cmd_lzma_with_size = LZMA $@ +- cmd_lzma_with_size = { cat $(real-prereqs) | $(LZMA) -9; $(size_append); } > $@ ++ cmd_lzma_with_size = { cat $(real-prereqs) | $(LZMA) e -d20 -lc1 -lp2 -pb2 -eos -si -so; $(size_append); } > $@ + + quiet_cmd_lzo = LZO $@ + cmd_lzo = cat $(real-prereqs) | $(KLZOP) -9 > $@ diff --git a/target/linux/generic/hack-6.6/250-netfilter_depends.patch b/target/linux/generic/hack-6.6/250-netfilter_depends.patch new file mode 100644 index 0000000000..fbb5a61157 --- /dev/null +++ b/target/linux/generic/hack-6.6/250-netfilter_depends.patch @@ -0,0 +1,27 @@ +From: Felix Fietkau +Subject: hack: net: remove bogus netfilter dependencies + +lede-commit: 589d2a377dee27d206fc3725325309cf649e4df6 +Signed-off-by: Felix Fietkau +--- + net/netfilter/Kconfig | 2 -- + 1 file changed, 2 deletions(-) + +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -253,7 +253,6 @@ config NF_CONNTRACK_FTP + + config NF_CONNTRACK_H323 + tristate "H.323 protocol support" +- depends on IPV6 || IPV6=n + depends on NETFILTER_ADVANCED + help + H.323 is a VoIP signalling protocol from ITU-T. As one of the most +@@ -1118,7 +1117,6 @@ config NETFILTER_XT_TARGET_SECMARK + + config NETFILTER_XT_TARGET_TCPMSS + tristate '"TCPMSS" target support' +- depends on IPV6 || IPV6=n + default m if NETFILTER_ADVANCED=n + help + This option adds a `TCPMSS' target, which allows you to alter the diff --git a/target/linux/generic/hack-6.6/251-kconfig.patch b/target/linux/generic/hack-6.6/251-kconfig.patch new file mode 100644 index 0000000000..fcf4eaf6e8 --- /dev/null +++ b/target/linux/generic/hack-6.6/251-kconfig.patch @@ -0,0 +1,157 @@ +From da3c50704f14132f4adf80d48e9a4cd5d46e54c9 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 7 Jul 2017 17:09:21 +0200 +Subject: kconfig: owrt specifc dependencies + +Signed-off-by: John Crispin +--- + crypto/Kconfig | 10 +++++----- + drivers/bcma/Kconfig | 1 + + drivers/ssb/Kconfig | 3 ++- + lib/Kconfig | 8 ++++---- + net/netfilter/Kconfig | 2 +- + net/wireless/Kconfig | 17 ++++++++++------- + sound/core/Kconfig | 4 ++-- + 7 files changed, 25 insertions(+), 20 deletions(-) + +--- a/crypto/Kconfig ++++ b/crypto/Kconfig +@@ -55,7 +55,7 @@ config CRYPTO_FIPS_VERSION + By default the KERNELRELEASE value is used. + + config CRYPTO_ALGAPI +- tristate ++ tristate "ALGAPI" + select CRYPTO_ALGAPI2 + help + This option provides the API for cryptographic algorithms. +@@ -64,7 +64,7 @@ config CRYPTO_ALGAPI2 + tristate + + config CRYPTO_AEAD +- tristate ++ tristate "AEAD" + select CRYPTO_AEAD2 + select CRYPTO_ALGAPI + +@@ -75,7 +75,7 @@ config CRYPTO_AEAD2 + select CRYPTO_RNG2 + + config CRYPTO_SKCIPHER +- tristate ++ tristate "SKCIPHER" + select CRYPTO_SKCIPHER2 + select CRYPTO_ALGAPI + +@@ -85,7 +85,7 @@ config CRYPTO_SKCIPHER2 + select CRYPTO_RNG2 + + config CRYPTO_HASH +- tristate ++ tristate "HASH" + select CRYPTO_HASH2 + select CRYPTO_ALGAPI + +@@ -94,7 +94,7 @@ config CRYPTO_HASH2 + select CRYPTO_ALGAPI2 + + config CRYPTO_RNG +- tristate ++ tristate "RNG" + select CRYPTO_RNG2 + select CRYPTO_ALGAPI + +--- a/drivers/bcma/Kconfig ++++ b/drivers/bcma/Kconfig +@@ -16,6 +16,7 @@ if BCMA + # Support for Block-I/O. SELECT this from the driver that needs it. + config BCMA_BLOCKIO + bool ++ default y + + config BCMA_HOST_PCI_POSSIBLE + bool +--- a/drivers/ssb/Kconfig ++++ b/drivers/ssb/Kconfig +@@ -29,6 +29,7 @@ config SSB_SPROM + config SSB_BLOCKIO + bool + depends on SSB ++ default y + + config SSB_PCIHOST_POSSIBLE + bool +@@ -49,7 +50,7 @@ config SSB_PCIHOST + config SSB_B43_PCI_BRIDGE + bool + depends on SSB_PCIHOST +- default n ++ default y + + config SSB_PCMCIAHOST_POSSIBLE + bool +--- a/lib/Kconfig ++++ b/lib/Kconfig +@@ -457,16 +457,16 @@ config BCH_CONST_T + # Textsearch support is select'ed if needed + # + config TEXTSEARCH +- bool ++ bool "Textsearch support" + + config TEXTSEARCH_KMP +- tristate ++ tristate "Textsearch KMP" + + config TEXTSEARCH_BM +- tristate ++ tristate "Textsearch BM" + + config TEXTSEARCH_FSM +- tristate ++ tristate "Textsearch FSM" + + config BTREE + bool +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -22,7 +22,7 @@ config NETFILTER_SKIP_EGRESS + def_bool NETFILTER_EGRESS && (NET_CLS_ACT || IFB) + + config NETFILTER_NETLINK +- tristate ++ tristate "Netfilter NFNETLINK interface" + + config NETFILTER_FAMILY_BRIDGE + bool +--- a/sound/core/Kconfig ++++ b/sound/core/Kconfig +@@ -17,7 +17,7 @@ config SND_DMAENGINE_PCM + tristate + + config SND_HWDEP +- tristate ++ tristate "Sound hardware support" + + config SND_SEQ_DEVICE + tristate +@@ -27,7 +27,7 @@ config SND_RAWMIDI + select SND_SEQ_DEVICE if SND_SEQUENCER != n + + config SND_COMPRESS_OFFLOAD +- tristate ++ tristate "Compression offloading support" + + config SND_JACK + bool +--- a/net/Kconfig ++++ b/net/Kconfig +@@ -430,7 +430,7 @@ config NET_DEVLINK + default n + + config PAGE_POOL +- bool ++ bool "Page pool support" + + config PAGE_POOL_STATS + default n diff --git a/target/linux/generic/hack-6.6/253-ksmbd-config.patch b/target/linux/generic/hack-6.6/253-ksmbd-config.patch new file mode 100644 index 0000000000..a57c914180 --- /dev/null +++ b/target/linux/generic/hack-6.6/253-ksmbd-config.patch @@ -0,0 +1,32 @@ +From dcd966fa7ca63f38cf7147e1184d13d66e2ca340 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +Date: Wed, 13 Jul 2022 13:33:30 +0200 +Subject: [PATCH] Kconfig: add tristate for OID and ASNI string + +--- + init/Kconfig | 2 +- + lib/Kconfig | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -2013,7 +2013,7 @@ config PADATA + bool + + config ASN1 +- tristate ++ tristate "ASN1" + help + Build a simple ASN.1 grammar compiler that produces a bytecode output + that can be interpreted by the ASN.1 stream decoder and used to +--- a/lib/Kconfig ++++ b/lib/Kconfig +@@ -637,7 +637,7 @@ config LIBFDT + bool + + config OID_REGISTRY +- tristate ++ tristate "OID" + help + Enable fast lookup object identifier registry. + diff --git a/target/linux/generic/hack-6.6/259-regmap_dynamic.patch b/target/linux/generic/hack-6.6/259-regmap_dynamic.patch new file mode 100644 index 0000000000..8d25f59ce2 --- /dev/null +++ b/target/linux/generic/hack-6.6/259-regmap_dynamic.patch @@ -0,0 +1,144 @@ +From 811d9e2268a62b830cfe93cd8bc929afcb8b198b Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Sat, 15 Jul 2017 21:12:38 +0200 +Subject: kernel: move regmap bloat out of the kernel image if it is only being used in modules + +lede-commit: 96f39119815028073583e4fca3a9c5fe9141e998 +Signed-off-by: Felix Fietkau +--- + drivers/base/regmap/Kconfig | 15 ++++++++++----- + drivers/base/regmap/Makefile | 12 ++++++++---- + drivers/base/regmap/regmap.c | 3 +++ + include/linux/regmap.h | 2 +- + 4 files changed, 22 insertions(+), 10 deletions(-) + +--- a/drivers/base/regmap/Kconfig ++++ b/drivers/base/regmap/Kconfig +@@ -4,10 +4,9 @@ + # subsystems should select the appropriate symbols. + + config REGMAP +- default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_W1 || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ || REGMAP_SOUNDWIRE || REGMAP_SOUNDWIRE_MBQ || REGMAP_SCCB || REGMAP_I3C || REGMAP_SPI_AVMM || REGMAP_MDIO) + select IRQ_DOMAIN if REGMAP_IRQ + select MDIO_BUS if REGMAP_MDIO +- bool ++ tristate + + config REGCACHE_COMPRESSED + select LZO_COMPRESS +@@ -15,53 +14,67 @@ config REGCACHE_COMPRESSED + bool + + config REGMAP_AC97 ++ select REGMAP + tristate + + config REGMAP_I2C ++ select REGMAP + tristate + depends on I2C + + config REGMAP_SLIMBUS ++ select REGMAP + tristate + depends on SLIMBUS + + config REGMAP_SPI ++ select REGMAP + tristate + depends on SPI + + config REGMAP_SPMI ++ select REGMAP + tristate + depends on SPMI + + config REGMAP_W1 ++ select REGMAP + tristate + depends on W1 + + config REGMAP_MDIO ++ select REGMAP + tristate + + config REGMAP_MMIO ++ select REGMAP + tristate + + config REGMAP_IRQ ++ select REGMAP + bool + + config REGMAP_SOUNDWIRE ++ select REGMAP + tristate + depends on SOUNDWIRE + + config REGMAP_SOUNDWIRE_MBQ ++ select REGMAP + tristate + depends on SOUNDWIRE + + config REGMAP_SCCB ++ select REGMAP + tristate + depends on I2C + + config REGMAP_I3C ++ select REGMAP + tristate + depends on I3C + + config REGMAP_SPI_AVMM ++ select REGMAP + tristate + depends on SPI +--- a/drivers/base/regmap/Makefile ++++ b/drivers/base/regmap/Makefile +@@ -2,10 +2,14 @@ + # For include/trace/define_trace.h to include trace.h + CFLAGS_regmap.o := -I$(src) + +-obj-$(CONFIG_REGMAP) += regmap.o regcache.o +-obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-flat.o +-obj-$(CONFIG_REGCACHE_COMPRESSED) += regcache-lzo.o +-obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o ++regmap-core-objs = regmap.o regcache.o regcache-rbtree.o regcache-flat.o ++ifdef CONFIG_DEBUG_FS ++regmap-core-objs += regmap-debugfs.o ++endif ++ifdef CONFIG_REGCACHE_COMPRESSED ++regmap-core-objs += regcache-lzo.o ++endif ++obj-$(CONFIG_REGMAP) += regmap-core.o + obj-$(CONFIG_REGMAP_AC97) += regmap-ac97.o + obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o + obj-$(CONFIG_REGMAP_SLIMBUS) += regmap-slimbus.o +--- a/drivers/base/regmap/regmap.c ++++ b/drivers/base/regmap/regmap.c +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -3513,3 +3514,5 @@ static int __init regmap_initcall(void) + return 0; + } + postcore_initcall(regmap_initcall); ++ ++MODULE_LICENSE("GPL"); +--- a/include/linux/regmap.h ++++ b/include/linux/regmap.h +@@ -180,7 +180,7 @@ struct reg_sequence { + __ret ?: __tmp; \ + }) + +-#ifdef CONFIG_REGMAP ++#if IS_REACHABLE(CONFIG_REGMAP) + + enum regmap_endian { + /* Unspecified -> 0 -> Backwards compatible default */ diff --git a/target/linux/generic/hack-6.6/260-crypto_test_dependencies.patch b/target/linux/generic/hack-6.6/260-crypto_test_dependencies.patch new file mode 100644 index 0000000000..7ea517496e --- /dev/null +++ b/target/linux/generic/hack-6.6/260-crypto_test_dependencies.patch @@ -0,0 +1,52 @@ +From fd1799b0bf5efa46dd3e6dfbbf3955564807e508 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Fri, 7 Jul 2017 17:12:51 +0200 +Subject: kernel: prevent cryptomgr from pulling in useless extra dependencies for tests that are not run + +Reduces kernel size after LZMA by about 5k on MIPS + +lede-commit: 044c316167e076479a344c59905e5b435b84a77f +Signed-off-by: Felix Fietkau +--- + crypto/Kconfig | 13 ++++++------- + crypto/algboss.c | 4 ++++ + 2 files changed, 10 insertions(+), 7 deletions(-) + +--- a/crypto/Kconfig ++++ b/crypto/Kconfig +@@ -142,13 +142,13 @@ config CRYPTO_MANAGER + cbc(aes). + + config CRYPTO_MANAGER2 +- def_tristate CRYPTO_MANAGER || (CRYPTO_MANAGER!=n && CRYPTO_ALGAPI=y) +- select CRYPTO_AEAD2 +- select CRYPTO_HASH2 +- select CRYPTO_SKCIPHER2 +- select CRYPTO_AKCIPHER2 +- select CRYPTO_KPP2 +- select CRYPTO_ACOMP2 ++ def_tristate CRYPTO_MANAGER || (CRYPTO_MANAGER!=n && CRYPTO_ALGAPI=y && !CRYPTO_MANAGER_DISABLE_TESTS) ++ select CRYPTO_AEAD2 if !CRYPTO_MANAGER_DISABLE_TESTS ++ select CRYPTO_HASH2 if !CRYPTO_MANAGER_DISABLE_TESTS ++ select CRYPTO_SKCIPHER2 if !CRYPTO_MANAGER_DISABLE_TESTS ++ select CRYPTO_AKCIPHER2 if !CRYPTO_MANAGER_DISABLE_TESTS ++ select CRYPTO_KPP2 if !CRYPTO_MANAGER_DISABLE_TESTS ++ select CRYPTO_ACOMP2 if !CRYPTO_MANAGER_DISABLE_TESTS + + config CRYPTO_USER + tristate "Userspace cryptographic algorithm configuration" +--- a/crypto/algboss.c ++++ b/crypto/algboss.c +@@ -211,8 +211,12 @@ static int cryptomgr_schedule_test(struc + type = alg->cra_flags; + + /* Do not test internal algorithms. */ ++#ifdef CONFIG_CRYPTO_MANAGER_DISABLE_TESTS ++ type |= CRYPTO_ALG_TESTED; ++#else + if (type & CRYPTO_ALG_INTERNAL) + type |= CRYPTO_ALG_TESTED; ++#endif + + param->type = type; + diff --git a/target/linux/generic/hack-6.6/261-lib-arc4-unhide.patch b/target/linux/generic/hack-6.6/261-lib-arc4-unhide.patch new file mode 100644 index 0000000000..01829b2d51 --- /dev/null +++ b/target/linux/generic/hack-6.6/261-lib-arc4-unhide.patch @@ -0,0 +1,24 @@ +From 241e5d3f7b0dd3c01f8c7fa83cbc9a3882286d53 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +Date: Wed, 13 Jul 2022 13:35:18 +0200 +Subject: [PATCH] lib/crypto: add tristate string for ARC4 + +This makes it possible to select CONFIG_CRYPTO_LIB_ARC4 directly. We +need this to be able to compile this into the kernel and make use of it +from backports. + +--- + lib/crypto/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/lib/crypto/Kconfig ++++ b/lib/crypto/Kconfig +@@ -9,7 +9,7 @@ config CRYPTO_LIB_AES + tristate + + config CRYPTO_LIB_ARC4 +- tristate ++ tristate "ARC4 cipher library" + + config CRYPTO_ARCH_HAVE_LIB_BLAKE2S + bool diff --git a/target/linux/generic/hack-6.6/280-rfkill-stubs.patch b/target/linux/generic/hack-6.6/280-rfkill-stubs.patch new file mode 100644 index 0000000000..7a650d132e --- /dev/null +++ b/target/linux/generic/hack-6.6/280-rfkill-stubs.patch @@ -0,0 +1,84 @@ +From 236c1acdfef5958010ac9814a9872e0a46fd78ee Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 7 Jul 2017 17:13:44 +0200 +Subject: rfkill: add fake rfkill support + +allow building of modules depending on RFKILL even if RFKILL is not enabled. + +Signed-off-by: John Crispin +--- + include/linux/rfkill.h | 2 +- + net/Makefile | 2 +- + net/rfkill/Kconfig | 14 +++++++++----- + net/rfkill/Makefile | 2 +- + 4 files changed, 12 insertions(+), 8 deletions(-) + +--- a/include/linux/rfkill.h ++++ b/include/linux/rfkill.h +@@ -64,7 +64,7 @@ struct rfkill_ops { + int (*set_block)(void *data, bool blocked); + }; + +-#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) ++#if defined(CONFIG_RFKILL_FULL) || defined(CONFIG_RFKILL_FULL_MODULE) + /** + * rfkill_alloc - Allocate rfkill structure + * @name: name of the struct -- the string is not copied internally +--- a/net/Makefile ++++ b/net/Makefile +@@ -52,7 +52,7 @@ obj-$(CONFIG_TIPC) += tipc/ + obj-$(CONFIG_NETLABEL) += netlabel/ + obj-$(CONFIG_IUCV) += iucv/ + obj-$(CONFIG_SMC) += smc/ +-obj-$(CONFIG_RFKILL) += rfkill/ ++obj-$(CONFIG_RFKILL_FULL) += rfkill/ + obj-$(CONFIG_NET_9P) += 9p/ + obj-$(CONFIG_CAIF) += caif/ + obj-$(CONFIG_DCB) += dcb/ +--- a/net/rfkill/Kconfig ++++ b/net/rfkill/Kconfig +@@ -2,7 +2,11 @@ + # + # RF switch subsystem configuration + # +-menuconfig RFKILL ++config RFKILL ++ bool ++ default y ++ ++menuconfig RFKILL_FULL + tristate "RF switch subsystem support" + help + Say Y here if you want to have control over RF switches +@@ -14,19 +18,19 @@ menuconfig RFKILL + # LED trigger support + config RFKILL_LEDS + bool +- depends on RFKILL ++ depends on RFKILL_FULL + depends on LEDS_TRIGGERS = y || RFKILL = LEDS_TRIGGERS + default y + + config RFKILL_INPUT + bool "RF switch input support" if EXPERT +- depends on RFKILL ++ depends on RFKILL_FULL + depends on INPUT = y || RFKILL = INPUT + default y if !EXPERT + + config RFKILL_GPIO + tristate "GPIO RFKILL driver" +- depends on RFKILL ++ depends on RFKILL_FULL + depends on GPIOLIB || COMPILE_TEST + default n + help +--- a/net/rfkill/Makefile ++++ b/net/rfkill/Makefile +@@ -5,5 +5,5 @@ + + rfkill-y += core.o + rfkill-$(CONFIG_RFKILL_INPUT) += input.o +-obj-$(CONFIG_RFKILL) += rfkill.o ++obj-$(CONFIG_RFKILL_FULL) += rfkill.o + obj-$(CONFIG_RFKILL_GPIO) += rfkill-gpio.o diff --git a/target/linux/generic/hack-6.6/300-MIPS-r4k_cache-use-more-efficient-cache-blast.patch b/target/linux/generic/hack-6.6/300-MIPS-r4k_cache-use-more-efficient-cache-blast.patch new file mode 100644 index 0000000000..f21f200136 --- /dev/null +++ b/target/linux/generic/hack-6.6/300-MIPS-r4k_cache-use-more-efficient-cache-blast.patch @@ -0,0 +1,64 @@ +From: Ben Menchaca +Date: Fri, 7 Jun 2013 18:35:22 -0500 +Subject: MIPS: r4k_cache: use more efficient cache blast + +Optimize the compiler output for larger cache blast cases that are +common for DMA-based networking. + +Signed-off-by: Ben Menchaca +Signed-off-by: Felix Fietkau +--- +--- a/arch/mips/include/asm/r4kcache.h ++++ b/arch/mips/include/asm/r4kcache.h +@@ -286,14 +286,46 @@ static inline void prot##extra##blast_## + unsigned long end) \ + { \ + unsigned long lsize = cpu_##desc##_line_size(); \ ++ unsigned long lsize_2 = lsize * 2; \ ++ unsigned long lsize_3 = lsize * 3; \ ++ unsigned long lsize_4 = lsize * 4; \ ++ unsigned long lsize_5 = lsize * 5; \ ++ unsigned long lsize_6 = lsize * 6; \ ++ unsigned long lsize_7 = lsize * 7; \ ++ unsigned long lsize_8 = lsize * 8; \ + unsigned long addr = start & ~(lsize - 1); \ +- unsigned long aend = (end - 1) & ~(lsize - 1); \ ++ unsigned long aend = (end + lsize - 1) & ~(lsize - 1); \ ++ int lines = (aend - addr) / lsize; \ + \ +- while (1) { \ ++ while (lines >= 8) { \ ++ prot##cache_op(hitop, addr); \ ++ prot##cache_op(hitop, addr + lsize); \ ++ prot##cache_op(hitop, addr + lsize_2); \ ++ prot##cache_op(hitop, addr + lsize_3); \ ++ prot##cache_op(hitop, addr + lsize_4); \ ++ prot##cache_op(hitop, addr + lsize_5); \ ++ prot##cache_op(hitop, addr + lsize_6); \ ++ prot##cache_op(hitop, addr + lsize_7); \ ++ addr += lsize_8; \ ++ lines -= 8; \ ++ } \ ++ \ ++ if (lines & 0x4) { \ ++ prot##cache_op(hitop, addr); \ ++ prot##cache_op(hitop, addr + lsize); \ ++ prot##cache_op(hitop, addr + lsize_2); \ ++ prot##cache_op(hitop, addr + lsize_3); \ ++ addr += lsize_4; \ ++ } \ ++ \ ++ if (lines & 0x2) { \ ++ prot##cache_op(hitop, addr); \ ++ prot##cache_op(hitop, addr + lsize); \ ++ addr += lsize_2; \ ++ } \ ++ \ ++ if (lines & 0x1) { \ + prot##cache_op(hitop, addr); \ +- if (addr == aend) \ +- break; \ +- addr += lsize; \ + } \ + } + diff --git a/target/linux/generic/hack-6.6/402-mtd-blktrans-call-add-disks-after-mtd-device.patch b/target/linux/generic/hack-6.6/402-mtd-blktrans-call-add-disks-after-mtd-device.patch new file mode 100644 index 0000000000..c82ca3edd8 --- /dev/null +++ b/target/linux/generic/hack-6.6/402-mtd-blktrans-call-add-disks-after-mtd-device.patch @@ -0,0 +1,112 @@ +From 0bccc3722bdd88e8ae995e77ef9f7b77ee4cbdee Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Wed, 7 Apr 2021 22:45:54 +0100 +Subject: [PATCH 2/2] mtd: blktrans: call add disks after mtd device +To: linux-mtd@lists.infradead.org +Cc: Vignesh Raghavendra , + Richard Weinberger , + Miquel Raynal , + David Woodhouse + +Calling device_add_disk while holding mtd_table_mutex leads +to deadlock in case part_bits!=0 as block partition parsers +will try to open the newly created disks, trying to acquire +mutex once again. +Move device_add_disk to additional function called after +add partitions of an MTD device have been added and locks +have been released. + +Signed-off-by: Daniel Golle +--- + drivers/mtd/mtd_blkdevs.c | 33 ++++++++++++++++++++++++++------- + drivers/mtd/mtdcore.c | 3 +++ + include/linux/mtd/blktrans.h | 1 + + 3 files changed, 30 insertions(+), 7 deletions(-) + +--- a/drivers/mtd/mtd_blkdevs.c ++++ b/drivers/mtd/mtd_blkdevs.c +@@ -386,19 +386,8 @@ int add_mtd_blktrans_dev(struct mtd_blkt + if (new->readonly) + set_disk_ro(gd, 1); + +- ret = device_add_disk(&new->mtd->dev, gd, NULL); +- if (ret) +- goto out_cleanup_disk; +- +- if (new->disk_attributes) { +- ret = sysfs_create_group(&disk_to_dev(gd)->kobj, +- new->disk_attributes); +- WARN_ON(ret); +- } + return 0; + +-out_cleanup_disk: +- put_disk(new->disk); + out_free_tag_set: + blk_mq_free_tag_set(new->tag_set); + out_kfree_tag_set: +@@ -408,6 +397,35 @@ out_list_del: + return ret; + } + ++void register_mtd_blktrans_devs(void) ++{ ++ struct mtd_blktrans_ops *tr; ++ struct mtd_blktrans_dev *dev, *next; ++ int ret; ++ ++ list_for_each_entry(tr, &blktrans_majors, list) { ++ list_for_each_entry_safe(dev, next, &tr->devs, list) { ++ if (disk_live(dev->disk)) ++ continue; ++ ++ ret = device_add_disk(&dev->mtd->dev, dev->disk, NULL); ++ if (ret) ++ goto out_cleanup_disk; ++ ++ if (dev->disk_attributes) { ++ ret = sysfs_create_group(&disk_to_dev(dev->disk)->kobj, ++ dev->disk_attributes); ++ WARN_ON(ret); ++ } ++ } ++ } ++ ++ return; ++ ++out_cleanup_disk: ++ put_disk(dev->disk); ++} ++ + int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) + { + unsigned long flags; +--- a/drivers/mtd/mtdcore.c ++++ b/drivers/mtd/mtdcore.c +@@ -32,6 +32,7 @@ + + #include + #include ++#include + + #include "mtdcore.h" + +@@ -1098,6 +1099,8 @@ int mtd_device_parse_register(struct mtd + register_reboot_notifier(&mtd->reboot_notifier); + } + ++ register_mtd_blktrans_devs(); ++ + out: + if (ret) { + nvmem_unregister(mtd->otp_user_nvmem); +--- a/include/linux/mtd/blktrans.h ++++ b/include/linux/mtd/blktrans.h +@@ -76,6 +76,7 @@ extern int deregister_mtd_blktrans(struc + extern int add_mtd_blktrans_dev(struct mtd_blktrans_dev *dev); + extern int del_mtd_blktrans_dev(struct mtd_blktrans_dev *dev); + extern int mtd_blktrans_cease_background(struct mtd_blktrans_dev *dev); ++extern void register_mtd_blktrans_devs(void); + + /** + * module_mtd_blktrans() - Helper macro for registering a mtd blktrans driver diff --git a/target/linux/generic/hack-6.6/420-mtd-support-OpenWrt-s-MTD_ROOTFS_ROOT_DEV.patch b/target/linux/generic/hack-6.6/420-mtd-support-OpenWrt-s-MTD_ROOTFS_ROOT_DEV.patch new file mode 100644 index 0000000000..7c7e4c814e --- /dev/null +++ b/target/linux/generic/hack-6.6/420-mtd-support-OpenWrt-s-MTD_ROOTFS_ROOT_DEV.patch @@ -0,0 +1,24 @@ +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Mon, 7 Nov 2022 23:48:24 +0100 +Subject: [PATCH] mtd: support OpenWrt's MTD_ROOTFS_ROOT_DEV +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This allows setting ROOT_DEV to MTD partition named "rootfs". + +Signed-off-by: Rafał Miłecki +--- + +--- a/drivers/mtd/mtdcore.c ++++ b/drivers/mtd/mtdcore.c +@@ -773,7 +773,8 @@ int add_mtd_device(struct mtd_info *mtd) + + mutex_unlock(&mtd_table_mutex); + +- if (of_find_property(mtd_get_of_node(mtd), "linux,rootfs", NULL)) { ++ if (of_find_property(mtd_get_of_node(mtd), "linux,rootfs", NULL) || ++ (IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV) && !strcmp(mtd->name, "rootfs") && ROOT_DEV == 0)) { + 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); diff --git a/target/linux/generic/hack-6.6/421-drivers-mtd-parsers-add-nvmem-support-to-cmdlinepart.patch b/target/linux/generic/hack-6.6/421-drivers-mtd-parsers-add-nvmem-support-to-cmdlinepart.patch new file mode 100644 index 0000000000..965a331a19 --- /dev/null +++ b/target/linux/generic/hack-6.6/421-drivers-mtd-parsers-add-nvmem-support-to-cmdlinepart.patch @@ -0,0 +1,120 @@ +From 6fa9e3678eb002246df1280322b6a024853950a5 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Mon, 11 Oct 2021 00:53:14 +0200 +Subject: [PATCH] drivers: mtd: parsers: add nvmem support to cmdlinepart + +Assuming cmdlinepart is only one level deep partition scheme and that +static partition are also defined in DTS, we can assign an of_node for +partition declared from bootargs. cmdlinepart have priority than +fiexed-partition parser so in this specific case the parser doesn't +assign an of_node. Fix this by searching a defined of_node using a +similar fixed_partition parser and if a partition is found with the same +label, check that it has the same offset and size and return the DT +of_node to correctly use NVMEM cells. + +Signed-off-by: Ansuel Smith +--- + drivers/mtd/parsers/cmdlinepart.c | 71 +++++++++++++++++++++++++++++++ + 1 file changed, 71 insertions(+) + +--- a/drivers/mtd/parsers/cmdlinepart.c ++++ b/drivers/mtd/parsers/cmdlinepart.c +@@ -43,6 +43,7 @@ + #include + #include + #include ++#include + + /* debug macro */ + #if 0 +@@ -323,6 +324,68 @@ static int mtdpart_setup_real(char *s) + return 0; + } + ++static int search_fixed_partition(struct mtd_info *master, ++ struct mtd_partition *target_part, ++ struct mtd_partition *fixed_part) ++{ ++ struct device_node *mtd_node; ++ struct device_node *ofpart_node; ++ struct device_node *pp; ++ struct mtd_partition part; ++ const char *partname; ++ ++ mtd_node = mtd_get_of_node(master); ++ if (!mtd_node) ++ return -EINVAL; ++ ++ ofpart_node = of_get_child_by_name(mtd_node, "partitions"); ++ ++ for_each_child_of_node(ofpart_node, pp) { ++ const __be32 *reg; ++ int len; ++ int a_cells, s_cells; ++ ++ reg = of_get_property(pp, "reg", &len); ++ if (!reg) { ++ pr_debug("%s: ofpart partition %pOF (%pOF) missing reg property.\n", ++ master->name, pp, ++ mtd_node); ++ continue; ++ } ++ ++ a_cells = of_n_addr_cells(pp); ++ s_cells = of_n_size_cells(pp); ++ if (len / 4 != a_cells + s_cells) { ++ pr_debug("%s: ofpart partition %pOF (%pOF) error parsing reg property.\n", ++ master->name, pp, ++ mtd_node); ++ continue; ++ } ++ ++ part.offset = of_read_number(reg, a_cells); ++ part.size = of_read_number(reg + a_cells, s_cells); ++ part.of_node = pp; ++ ++ partname = of_get_property(pp, "label", &len); ++ if (!partname) ++ partname = of_get_property(pp, "name", &len); ++ part.name = partname; ++ ++ if (!strncmp(target_part->name, part.name, len)) { ++ if (part.offset != target_part->offset) ++ return -EINVAL; ++ ++ if (part.size != target_part->size) ++ return -EINVAL; ++ ++ memcpy(fixed_part, &part, sizeof(struct mtd_partition)); ++ return 0; ++ } ++ } ++ ++ return -EINVAL; ++} ++ + /* + * Main function to be called from the MTD mapping driver/device to + * obtain the partitioning information. At this point the command line +@@ -338,6 +401,7 @@ static int parse_cmdline_partitions(stru + int i, err; + struct cmdline_mtd_partition *part; + const char *mtd_id = master->name; ++ struct mtd_partition fixed_part; + + /* parse command line */ + if (!cmdline_parsed) { +@@ -382,6 +446,13 @@ static int parse_cmdline_partitions(stru + sizeof(*part->parts) * (part->num_parts - i)); + i--; + } ++ ++ err = search_fixed_partition(master, &part->parts[i], &fixed_part); ++ if (!err) { ++ part->parts[i].of_node = fixed_part.of_node; ++ pr_info("Found partition defined in DT for %s. Assigning OF node to support nvmem.", ++ part->parts[i].name); ++ } + } + + *pparts = kmemdup(part->parts, sizeof(*part->parts) * part->num_parts, diff --git a/target/linux/generic/hack-6.6/430-mtk-bmt-support.patch b/target/linux/generic/hack-6.6/430-mtk-bmt-support.patch new file mode 100644 index 0000000000..1e69ee6446 --- /dev/null +++ b/target/linux/generic/hack-6.6/430-mtk-bmt-support.patch @@ -0,0 +1,33 @@ +From ac84397efb3b3868c71c10ad7521161773228a17 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +Date: Wed, 13 Jul 2022 13:41:44 +0200 +Subject: [PATCH] mtd/nand: add MediaTek NAND bad block managment table + +--- + drivers/mtd/nand/Kconfig | 4 ++++ + drivers/mtd/nand/Makefile | 1 + + 2 files changed, 5 insertions(+) + +--- a/drivers/mtd/nand/Kconfig ++++ b/drivers/mtd/nand/Kconfig +@@ -46,6 +46,10 @@ config MTD_NAND_ECC_SW_BCH + ECC codes. They are used with NAND devices requiring more than 1 bit + of error correction. + ++config MTD_NAND_MTK_BMT ++ bool "Support MediaTek NAND Bad-block Management Table" ++ default n ++ + config MTD_NAND_ECC_MXIC + bool "Macronix external hardware ECC engine" + depends on HAS_IOMEM +--- a/drivers/mtd/nand/Makefile ++++ b/drivers/mtd/nand/Makefile +@@ -3,6 +3,7 @@ + nandcore-objs := core.o bbt.o + obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o + obj-$(CONFIG_MTD_NAND_ECC_MEDIATEK) += ecc-mtk.o ++obj-$(CONFIG_MTD_NAND_MTK_BMT) += mtk_bmt.o mtk_bmt_v2.o mtk_bmt_bbt.o mtk_bmt_nmbm.o + + obj-y += onenand/ + obj-y += raw/ diff --git a/target/linux/generic/hack-6.6/645-netfilter-connmark-introduce-set-dscpmark.patch b/target/linux/generic/hack-6.6/645-netfilter-connmark-introduce-set-dscpmark.patch new file mode 100644 index 0000000000..444f8edfea --- /dev/null +++ b/target/linux/generic/hack-6.6/645-netfilter-connmark-introduce-set-dscpmark.patch @@ -0,0 +1,214 @@ +From eda40b8c8c82e0f2789d6bc8bf63846dce2e8f32 Mon Sep 17 00:00:00 2001 +From: Kevin Darbyshire-Bryant +Date: Sat, 23 Mar 2019 09:29:49 +0000 +Subject: [PATCH] netfilter: connmark: introduce set-dscpmark + +set-dscpmark is a method of storing the DSCP of an ip packet into +conntrack mark. In combination with a suitable tc filter action +(act_ctinfo) DSCP values are able to be stored in the mark on egress and +restored on ingress across links that otherwise alter or bleach DSCP. + +This is useful for qdiscs such as CAKE which are able to shape according +to policies based on DSCP. + +Ingress classification is traditionally a challenging task since +iptables rules haven't yet run and tc filter/eBPF programs are pre-NAT +lookups, hence are unable to see internal IPv4 addresses as used on the +typical home masquerading gateway. + +x_tables CONNMARK set-dscpmark target solves the problem of storing the +DSCP to the conntrack mark in a way suitable for the new act_ctinfo tc +action to restore. + +The set-dscpmark option accepts 2 parameters, a 32bit 'dscpmask' and a +32bit 'statemask'. The dscp mask must be 6 contiguous bits and +represents the area where the DSCP will be stored in the connmark. The +state mask is a minimum 1 bit length mask that must not overlap with the +dscpmask. It represents a flag which is set when the DSCP has been +stored in the conntrack mark. This is useful to implement a 'one shot' +iptables based classification where the 'complicated' iptables rules are +only run once to classify the connection on initial (egress) packet and +subsequent packets are all marked/restored with the same DSCP. A state +mask of zero disables the setting of a status bit/s. + +example syntax with a suitably modified iptables user space application: + +iptables -A QOS_MARK_eth0 -t mangle -j CONNMARK --set-dscpmark 0xfc000000/0x01000000 + +Would store the DSCP in the top 6 bits of the 32bit mark field, and use +the LSB of the top byte as the 'DSCP has been stored' marker. + +|----0xFC----conntrack mark----000000---| +| Bits 31-26 | bit 25 | bit24 |~~~ Bit 0| +| DSCP | unused | flag |unused | +|-----------------------0x01---000000---| + ^ ^ + | | + ---| Conditional flag + | set this when dscp +|-ip diffserv-| stored in mark +| 6 bits | +|-------------| + +an identically configured tc action to restore looks like: + +tc filter show dev eth0 ingress +filter parent ffff: protocol all pref 10 u32 chain 0 +filter parent ffff: protocol all pref 10 u32 chain 0 fh 800: ht divisor 1 +filter parent ffff: protocol all pref 10 u32 chain 0 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1: not_in_hw + match 00000000/00000000 at 0 + action order 1: ctinfo zone 0 pipe + index 2 ref 1 bind 1 dscp 0xfc000000/0x1000000 + + action order 2: mirred (Egress Redirect to device ifb4eth0) stolen + index 1 ref 1 bind 1 + +|----0xFC----conntrack mark----000000---| +| Bits 31-26 | bit 25 | bit24 |~~~ Bit 0| +| DSCP | unused | flag |unused | +|-----------------------0x01---000000---| + | | + | | + ---| Conditional flag + v only restore if set +|-ip diffserv-| +| 6 bits | +|-------------| + +Signed-off-by: Kevin Darbyshire-Bryant +--- + include/uapi/linux/netfilter/xt_connmark.h | 10 ++++ + net/netfilter/xt_connmark.c | 55 ++++++++++++++++++---- + 2 files changed, 57 insertions(+), 8 deletions(-) + +--- a/include/uapi/linux/netfilter/xt_connmark.h ++++ b/include/uapi/linux/netfilter/xt_connmark.h +@@ -15,6 +15,11 @@ enum { + }; + + enum { ++ XT_CONNMARK_VALUE = (1 << 0), ++ XT_CONNMARK_DSCP = (1 << 1) ++}; ++ ++enum { + D_SHIFT_LEFT = 0, + D_SHIFT_RIGHT, + }; +@@ -29,6 +34,11 @@ struct xt_connmark_tginfo2 { + __u8 shift_dir, shift_bits, mode; + }; + ++struct xt_connmark_tginfo3 { ++ __u32 ctmark, ctmask, nfmask; ++ __u8 shift_dir, shift_bits, mode, func; ++}; ++ + struct xt_connmark_mtinfo1 { + __u32 mark, mask; + __u8 invert; +--- a/net/netfilter/xt_connmark.c ++++ b/net/netfilter/xt_connmark.c +@@ -24,13 +24,13 @@ MODULE_ALIAS("ipt_connmark"); + MODULE_ALIAS("ip6t_connmark"); + + static unsigned int +-connmark_tg_shift(struct sk_buff *skb, const struct xt_connmark_tginfo2 *info) ++connmark_tg_shift(struct sk_buff *skb, const struct xt_connmark_tginfo3 *info) + { + enum ip_conntrack_info ctinfo; + u_int32_t new_targetmark; + struct nf_conn *ct; + u_int32_t newmark; +- u_int32_t oldmark; ++ u_int8_t dscp; + + ct = nf_ct_get(skb, &ctinfo); + if (ct == NULL) +@@ -38,13 +38,24 @@ connmark_tg_shift(struct sk_buff *skb, c + + switch (info->mode) { + case XT_CONNMARK_SET: +- oldmark = READ_ONCE(ct->mark); +- newmark = (oldmark & ~info->ctmask) ^ info->ctmark; +- if (info->shift_dir == D_SHIFT_RIGHT) +- newmark >>= info->shift_bits; +- else +- newmark <<= info->shift_bits; ++ newmark = READ_ONCE(ct->mark); ++ if (info->func & XT_CONNMARK_VALUE) { ++ newmark = (newmark & ~info->ctmask) ^ info->ctmark; ++ if (info->shift_dir == D_SHIFT_RIGHT) ++ newmark >>= info->shift_bits; ++ else ++ newmark <<= info->shift_bits; ++ } else if (info->func & XT_CONNMARK_DSCP) { ++ if (skb->protocol == htons(ETH_P_IP)) ++ dscp = ipv4_get_dsfield(ip_hdr(skb)) >> 2; ++ else if (skb->protocol == htons(ETH_P_IPV6)) ++ dscp = ipv6_get_dsfield(ipv6_hdr(skb)) >> 2; ++ else /* protocol doesn't have diffserv */ ++ break; + ++ newmark = (newmark & ~info->ctmark) | ++ (info->ctmask | (dscp << info->shift_bits)); ++ } + if (READ_ONCE(ct->mark) != newmark) { + WRITE_ONCE(ct->mark, newmark); + nf_conntrack_event_cache(IPCT_MARK, ct); +@@ -83,20 +94,36 @@ static unsigned int + connmark_tg(struct sk_buff *skb, const struct xt_action_param *par) + { + const struct xt_connmark_tginfo1 *info = par->targinfo; +- const struct xt_connmark_tginfo2 info2 = { ++ const struct xt_connmark_tginfo3 info3 = { + .ctmark = info->ctmark, + .ctmask = info->ctmask, + .nfmask = info->nfmask, + .mode = info->mode, ++ .func = XT_CONNMARK_VALUE + }; + +- return connmark_tg_shift(skb, &info2); ++ return connmark_tg_shift(skb, &info3); + } + + static unsigned int + connmark_tg_v2(struct sk_buff *skb, const struct xt_action_param *par) + { + const struct xt_connmark_tginfo2 *info = par->targinfo; ++ const struct xt_connmark_tginfo3 info3 = { ++ .ctmark = info->ctmark, ++ .ctmask = info->ctmask, ++ .nfmask = info->nfmask, ++ .mode = info->mode, ++ .func = XT_CONNMARK_VALUE ++ }; ++ ++ return connmark_tg_shift(skb, &info3); ++} ++ ++static unsigned int ++connmark_tg_v3(struct sk_buff *skb, const struct xt_action_param *par) ++{ ++ const struct xt_connmark_tginfo3 *info = par->targinfo; + + return connmark_tg_shift(skb, info); + } +@@ -167,6 +194,16 @@ static struct xt_target connmark_tg_reg[ + .targetsize = sizeof(struct xt_connmark_tginfo2), + .destroy = connmark_tg_destroy, + .me = THIS_MODULE, ++ }, ++ { ++ .name = "CONNMARK", ++ .revision = 3, ++ .family = NFPROTO_UNSPEC, ++ .checkentry = connmark_tg_check, ++ .target = connmark_tg_v3, ++ .targetsize = sizeof(struct xt_connmark_tginfo3), ++ .destroy = connmark_tg_destroy, ++ .me = THIS_MODULE, + } + }; + diff --git a/target/linux/generic/hack-6.6/650-netfilter-add-xt_FLOWOFFLOAD-target.patch b/target/linux/generic/hack-6.6/650-netfilter-add-xt_FLOWOFFLOAD-target.patch new file mode 100644 index 0000000000..59a5fb4d91 --- /dev/null +++ b/target/linux/generic/hack-6.6/650-netfilter-add-xt_FLOWOFFLOAD-target.patch @@ -0,0 +1,803 @@ +From: Felix Fietkau +Date: Tue, 20 Feb 2018 15:56:02 +0100 +Subject: [PATCH] netfilter: add xt_FLOWOFFLOAD target + +Signed-off-by: Felix Fietkau +--- + create mode 100644 net/netfilter/xt_OFFLOAD.c + +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -1023,6 +1023,15 @@ config NETFILTER_XT_TARGET_NOTRACK + depends on NETFILTER_ADVANCED + select NETFILTER_XT_TARGET_CT + ++config NETFILTER_XT_TARGET_FLOWOFFLOAD ++ tristate '"FLOWOFFLOAD" target support' ++ depends on NF_FLOW_TABLE ++ depends on NETFILTER_INGRESS ++ help ++ This option adds a `FLOWOFFLOAD' target, which uses the nf_flow_offload ++ module to speed up processing of packets by bypassing the usual ++ netfilter chains ++ + config NETFILTER_XT_TARGET_RATEEST + tristate '"RATEEST" target support' + depends on NETFILTER_ADVANCED +--- a/net/netfilter/Makefile ++++ b/net/netfilter/Makefile +@@ -154,6 +154,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CLASSIF + obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o + obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o + obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o ++obj-$(CONFIG_NETFILTER_XT_TARGET_FLOWOFFLOAD) += xt_FLOWOFFLOAD.o + obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o + obj-$(CONFIG_NETFILTER_XT_TARGET_HMARK) += xt_HMARK.o + obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o +--- /dev/null ++++ b/net/netfilter/xt_FLOWOFFLOAD.c +@@ -0,0 +1,702 @@ ++/* ++ * Copyright (C) 2018-2021 Felix Fietkau ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct xt_flowoffload_hook { ++ struct hlist_node list; ++ struct nf_hook_ops ops; ++ struct net *net; ++ bool registered; ++ bool used; ++}; ++ ++struct xt_flowoffload_table { ++ struct nf_flowtable ft; ++ struct hlist_head hooks; ++ struct delayed_work work; ++}; ++ ++struct nf_forward_info { ++ const struct net_device *indev; ++ const struct net_device *outdev; ++ const struct net_device *hw_outdev; ++ struct id { ++ __u16 id; ++ __be16 proto; ++ } encap[NF_FLOW_TABLE_ENCAP_MAX]; ++ u8 num_encaps; ++ u8 ingress_vlans; ++ u8 h_source[ETH_ALEN]; ++ u8 h_dest[ETH_ALEN]; ++ enum flow_offload_xmit_type xmit_type; ++}; ++ ++static DEFINE_SPINLOCK(hooks_lock); ++ ++struct xt_flowoffload_table flowtable[2]; ++ ++static unsigned int ++xt_flowoffload_net_hook(void *priv, struct sk_buff *skb, ++ const struct nf_hook_state *state) ++{ ++ struct vlan_ethhdr *veth; ++ __be16 proto; ++ ++ switch (skb->protocol) { ++ case htons(ETH_P_8021Q): ++ veth = (struct vlan_ethhdr *)skb_mac_header(skb); ++ proto = veth->h_vlan_encapsulated_proto; ++ break; ++ case htons(ETH_P_PPP_SES): ++ proto = nf_flow_pppoe_proto(skb); ++ break; ++ default: ++ proto = skb->protocol; ++ break; ++ } ++ ++ switch (proto) { ++ case htons(ETH_P_IP): ++ return nf_flow_offload_ip_hook(priv, skb, state); ++ case htons(ETH_P_IPV6): ++ return nf_flow_offload_ipv6_hook(priv, skb, state); ++ } ++ ++ return NF_ACCEPT; ++} ++ ++static int ++xt_flowoffload_create_hook(struct xt_flowoffload_table *table, ++ struct net_device *dev) ++{ ++ struct xt_flowoffload_hook *hook; ++ struct nf_hook_ops *ops; ++ ++ hook = kzalloc(sizeof(*hook), GFP_ATOMIC); ++ if (!hook) ++ return -ENOMEM; ++ ++ ops = &hook->ops; ++ ops->pf = NFPROTO_NETDEV; ++ ops->hooknum = NF_NETDEV_INGRESS; ++ ops->priority = 10; ++ ops->priv = &table->ft; ++ ops->hook = xt_flowoffload_net_hook; ++ ops->dev = dev; ++ ++ hlist_add_head(&hook->list, &table->hooks); ++ mod_delayed_work(system_power_efficient_wq, &table->work, 0); ++ ++ return 0; ++} ++ ++static struct xt_flowoffload_hook * ++flow_offload_lookup_hook(struct xt_flowoffload_table *table, ++ struct net_device *dev) ++{ ++ struct xt_flowoffload_hook *hook; ++ ++ hlist_for_each_entry(hook, &table->hooks, list) { ++ if (hook->ops.dev == dev) ++ return hook; ++ } ++ ++ return NULL; ++} ++ ++static void ++xt_flowoffload_check_device(struct xt_flowoffload_table *table, ++ struct net_device *dev) ++{ ++ struct xt_flowoffload_hook *hook; ++ ++ if (!dev) ++ return; ++ ++ spin_lock_bh(&hooks_lock); ++ hook = flow_offload_lookup_hook(table, dev); ++ if (hook) ++ hook->used = true; ++ else ++ xt_flowoffload_create_hook(table, dev); ++ spin_unlock_bh(&hooks_lock); ++} ++ ++static void ++xt_flowoffload_register_hooks(struct xt_flowoffload_table *table) ++{ ++ struct xt_flowoffload_hook *hook; ++ ++restart: ++ hlist_for_each_entry(hook, &table->hooks, list) { ++ if (hook->registered) ++ continue; ++ ++ hook->registered = true; ++ hook->net = dev_net(hook->ops.dev); ++ spin_unlock_bh(&hooks_lock); ++ nf_register_net_hook(hook->net, &hook->ops); ++ if (table->ft.flags & NF_FLOWTABLE_HW_OFFLOAD) ++ table->ft.type->setup(&table->ft, hook->ops.dev, ++ FLOW_BLOCK_BIND); ++ spin_lock_bh(&hooks_lock); ++ goto restart; ++ } ++ ++} ++ ++static bool ++xt_flowoffload_cleanup_hooks(struct xt_flowoffload_table *table) ++{ ++ struct xt_flowoffload_hook *hook; ++ bool active = false; ++ ++restart: ++ spin_lock_bh(&hooks_lock); ++ hlist_for_each_entry(hook, &table->hooks, list) { ++ if (hook->used || !hook->registered) { ++ active = true; ++ continue; ++ } ++ ++ hlist_del(&hook->list); ++ spin_unlock_bh(&hooks_lock); ++ if (table->ft.flags & NF_FLOWTABLE_HW_OFFLOAD) ++ table->ft.type->setup(&table->ft, hook->ops.dev, ++ FLOW_BLOCK_UNBIND); ++ nf_unregister_net_hook(hook->net, &hook->ops); ++ kfree(hook); ++ goto restart; ++ } ++ spin_unlock_bh(&hooks_lock); ++ ++ return active; ++} ++ ++static void ++xt_flowoffload_check_hook(struct nf_flowtable *flowtable, ++ struct flow_offload *flow, void *data) ++{ ++ struct xt_flowoffload_table *table; ++ struct flow_offload_tuple *tuple0 = &flow->tuplehash[0].tuple; ++ struct flow_offload_tuple *tuple1 = &flow->tuplehash[1].tuple; ++ struct xt_flowoffload_hook *hook; ++ ++ table = container_of(flowtable, struct xt_flowoffload_table, ft); ++ ++ spin_lock_bh(&hooks_lock); ++ hlist_for_each_entry(hook, &table->hooks, list) { ++ if (hook->ops.dev->ifindex != tuple0->iifidx && ++ hook->ops.dev->ifindex != tuple1->iifidx) ++ continue; ++ ++ hook->used = true; ++ } ++ spin_unlock_bh(&hooks_lock); ++} ++ ++static void ++xt_flowoffload_hook_work(struct work_struct *work) ++{ ++ struct xt_flowoffload_table *table; ++ struct xt_flowoffload_hook *hook; ++ int err; ++ ++ table = container_of(work, struct xt_flowoffload_table, work.work); ++ ++ spin_lock_bh(&hooks_lock); ++ xt_flowoffload_register_hooks(table); ++ hlist_for_each_entry(hook, &table->hooks, list) ++ hook->used = false; ++ spin_unlock_bh(&hooks_lock); ++ ++ err = nf_flow_table_iterate(&table->ft, xt_flowoffload_check_hook, ++ NULL); ++ if (err && err != -EAGAIN) ++ goto out; ++ ++ if (!xt_flowoffload_cleanup_hooks(table)) ++ return; ++ ++out: ++ queue_delayed_work(system_power_efficient_wq, &table->work, HZ); ++} ++ ++static bool ++xt_flowoffload_skip(struct sk_buff *skb, int family) ++{ ++ if (skb_sec_path(skb)) ++ return true; ++ ++ if (family == NFPROTO_IPV4) { ++ const struct ip_options *opt = &(IPCB(skb)->opt); ++ ++ if (unlikely(opt->optlen)) ++ return true; ++ } ++ ++ return false; ++} ++ ++static enum flow_offload_xmit_type nf_xmit_type(struct dst_entry *dst) ++{ ++ if (dst_xfrm(dst)) ++ return FLOW_OFFLOAD_XMIT_XFRM; ++ ++ return FLOW_OFFLOAD_XMIT_NEIGH; ++} ++ ++static void nf_default_forward_path(struct nf_flow_route *route, ++ struct dst_entry *dst_cache, ++ enum ip_conntrack_dir dir, ++ struct net_device **dev) ++{ ++ dev[!dir] = dst_cache->dev; ++ route->tuple[!dir].in.ifindex = dst_cache->dev->ifindex; ++ route->tuple[dir].dst = dst_cache; ++ route->tuple[dir].xmit_type = nf_xmit_type(dst_cache); ++} ++ ++static bool nf_is_valid_ether_device(const struct net_device *dev) ++{ ++ if (!dev || (dev->flags & IFF_LOOPBACK) || dev->type != ARPHRD_ETHER || ++ dev->addr_len != ETH_ALEN || !is_valid_ether_addr(dev->dev_addr)) ++ return false; ++ ++ return true; ++} ++ ++static void nf_dev_path_info(const struct net_device_path_stack *stack, ++ struct nf_forward_info *info, ++ unsigned char *ha) ++{ ++ const struct net_device_path *path; ++ int i; ++ ++ memcpy(info->h_dest, ha, ETH_ALEN); ++ ++ for (i = 0; i < stack->num_paths; i++) { ++ path = &stack->path[i]; ++ switch (path->type) { ++ case DEV_PATH_ETHERNET: ++ case DEV_PATH_DSA: ++ case DEV_PATH_VLAN: ++ case DEV_PATH_PPPOE: ++ info->indev = path->dev; ++ if (is_zero_ether_addr(info->h_source)) ++ memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN); ++ ++ if (path->type == DEV_PATH_ETHERNET) ++ break; ++ if (path->type == DEV_PATH_DSA) { ++ i = stack->num_paths; ++ break; ++ } ++ ++ /* DEV_PATH_VLAN and DEV_PATH_PPPOE */ ++ if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) { ++ info->indev = NULL; ++ break; ++ } ++ if (!info->outdev) ++ info->outdev = path->dev; ++ info->encap[info->num_encaps].id = path->encap.id; ++ info->encap[info->num_encaps].proto = path->encap.proto; ++ info->num_encaps++; ++ if (path->type == DEV_PATH_PPPOE) ++ memcpy(info->h_dest, path->encap.h_dest, ETH_ALEN); ++ break; ++ case DEV_PATH_BRIDGE: ++ if (is_zero_ether_addr(info->h_source)) ++ memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN); ++ ++ switch (path->bridge.vlan_mode) { ++ case DEV_PATH_BR_VLAN_UNTAG_HW: ++ info->ingress_vlans |= BIT(info->num_encaps - 1); ++ break; ++ case DEV_PATH_BR_VLAN_TAG: ++ info->encap[info->num_encaps].id = path->bridge.vlan_id; ++ info->encap[info->num_encaps].proto = path->bridge.vlan_proto; ++ info->num_encaps++; ++ break; ++ case DEV_PATH_BR_VLAN_UNTAG: ++ info->num_encaps--; ++ break; ++ case DEV_PATH_BR_VLAN_KEEP: ++ break; ++ } ++ break; ++ default: ++ info->indev = NULL; ++ break; ++ } ++ } ++ if (!info->outdev) ++ info->outdev = info->indev; ++ ++ info->hw_outdev = info->indev; ++ ++ if (nf_is_valid_ether_device(info->indev)) ++ info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT; ++} ++ ++static int nf_dev_fill_forward_path(const struct nf_flow_route *route, ++ const struct dst_entry *dst_cache, ++ const struct nf_conn *ct, ++ enum ip_conntrack_dir dir, u8 *ha, ++ struct net_device_path_stack *stack) ++{ ++ const void *daddr = &ct->tuplehash[!dir].tuple.src.u3; ++ struct net_device *dev = dst_cache->dev; ++ struct neighbour *n; ++ u8 nud_state; ++ ++ if (!nf_is_valid_ether_device(dev)) ++ goto out; ++ ++ n = dst_neigh_lookup(dst_cache, daddr); ++ if (!n) ++ return -1; ++ ++ read_lock_bh(&n->lock); ++ nud_state = n->nud_state; ++ ether_addr_copy(ha, n->ha); ++ read_unlock_bh(&n->lock); ++ neigh_release(n); ++ ++ if (!(nud_state & NUD_VALID)) ++ return -1; ++ ++out: ++ return dev_fill_forward_path(dev, ha, stack); ++} ++ ++static void nf_dev_forward_path(struct nf_flow_route *route, ++ const struct nf_conn *ct, ++ enum ip_conntrack_dir dir, ++ struct net_device **devs) ++{ ++ const struct dst_entry *dst = route->tuple[dir].dst; ++ struct net_device_path_stack stack; ++ struct nf_forward_info info = {}; ++ unsigned char ha[ETH_ALEN]; ++ int i; ++ ++ if (nf_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) >= 0) ++ nf_dev_path_info(&stack, &info, ha); ++ ++ devs[!dir] = (struct net_device *)info.indev; ++ if (!info.indev) ++ return; ++ ++ route->tuple[!dir].in.ifindex = info.indev->ifindex; ++ for (i = 0; i < info.num_encaps; i++) { ++ route->tuple[!dir].in.encap[i].id = info.encap[i].id; ++ route->tuple[!dir].in.encap[i].proto = info.encap[i].proto; ++ } ++ route->tuple[!dir].in.num_encaps = info.num_encaps; ++ route->tuple[!dir].in.ingress_vlans = info.ingress_vlans; ++ ++ if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) { ++ memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN); ++ memcpy(route->tuple[dir].out.h_dest, info.h_dest, ETH_ALEN); ++ route->tuple[dir].out.ifindex = info.outdev->ifindex; ++ route->tuple[dir].out.hw_ifindex = info.hw_outdev->ifindex; ++ route->tuple[dir].xmit_type = info.xmit_type; ++ } ++} ++ ++static int ++xt_flowoffload_route(struct sk_buff *skb, const struct nf_conn *ct, ++ const struct xt_action_param *par, ++ struct nf_flow_route *route, enum ip_conntrack_dir dir, ++ struct net_device **devs) ++{ ++ struct dst_entry *this_dst = skb_dst(skb); ++ struct dst_entry *other_dst = NULL; ++ struct flowi fl; ++ ++ memset(&fl, 0, sizeof(fl)); ++ switch (xt_family(par)) { ++ case NFPROTO_IPV4: ++ fl.u.ip4.daddr = ct->tuplehash[dir].tuple.src.u3.ip; ++ fl.u.ip4.flowi4_oif = xt_in(par)->ifindex; ++ break; ++ case NFPROTO_IPV6: ++ fl.u.ip6.saddr = ct->tuplehash[!dir].tuple.dst.u3.in6; ++ fl.u.ip6.daddr = ct->tuplehash[dir].tuple.src.u3.in6; ++ fl.u.ip6.flowi6_oif = xt_in(par)->ifindex; ++ break; ++ } ++ ++ if (!dst_hold_safe(this_dst)) ++ return -ENOENT; ++ ++ nf_route(xt_net(par), &other_dst, &fl, false, xt_family(par)); ++ if (!other_dst) { ++ dst_release(this_dst); ++ return -ENOENT; ++ } ++ ++ nf_default_forward_path(route, this_dst, dir, devs); ++ nf_default_forward_path(route, other_dst, !dir, devs); ++ ++ if (route->tuple[dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH && ++ route->tuple[!dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH) { ++ nf_dev_forward_path(route, ct, dir, devs); ++ nf_dev_forward_path(route, ct, !dir, devs); ++ } ++ ++ return 0; ++} ++ ++static unsigned int ++flowoffload_tg(struct sk_buff *skb, const struct xt_action_param *par) ++{ ++ struct xt_flowoffload_table *table; ++ const struct xt_flowoffload_target_info *info = par->targinfo; ++ struct tcphdr _tcph, *tcph = NULL; ++ enum ip_conntrack_info ctinfo; ++ enum ip_conntrack_dir dir; ++ struct nf_flow_route route = {}; ++ struct flow_offload *flow = NULL; ++ struct net_device *devs[2] = {}; ++ struct nf_conn *ct; ++ struct net *net; ++ ++ if (xt_flowoffload_skip(skb, xt_family(par))) ++ return XT_CONTINUE; ++ ++ ct = nf_ct_get(skb, &ctinfo); ++ if (ct == NULL) ++ return XT_CONTINUE; ++ ++ switch (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum) { ++ case IPPROTO_TCP: ++ if (ct->proto.tcp.state != TCP_CONNTRACK_ESTABLISHED) ++ return XT_CONTINUE; ++ ++ tcph = skb_header_pointer(skb, par->thoff, ++ sizeof(_tcph), &_tcph); ++ if (unlikely(!tcph || tcph->fin || tcph->rst)) ++ return XT_CONTINUE; ++ break; ++ case IPPROTO_UDP: ++ break; ++ default: ++ return XT_CONTINUE; ++ } ++ ++ if (nf_ct_ext_exist(ct, NF_CT_EXT_HELPER) || ++ ct->status & (IPS_SEQ_ADJUST | IPS_NAT_CLASH)) ++ return XT_CONTINUE; ++ ++ if (!nf_ct_is_confirmed(ct)) ++ return XT_CONTINUE; ++ ++ devs[dir] = xt_out(par); ++ devs[!dir] = xt_in(par); ++ ++ if (!devs[dir] || !devs[!dir]) ++ return XT_CONTINUE; ++ ++ if (test_and_set_bit(IPS_OFFLOAD_BIT, &ct->status)) ++ return XT_CONTINUE; ++ ++ dir = CTINFO2DIR(ctinfo); ++ ++ if (xt_flowoffload_route(skb, ct, par, &route, dir, devs) < 0) ++ goto err_flow_route; ++ ++ flow = flow_offload_alloc(ct); ++ if (!flow) ++ goto err_flow_alloc; ++ ++ flow_offload_route_init(flow, &route); ++ ++ if (tcph) { ++ ct->proto.tcp.seen[0].flags |= IP_CT_TCP_FLAG_BE_LIBERAL; ++ ct->proto.tcp.seen[1].flags |= IP_CT_TCP_FLAG_BE_LIBERAL; ++ } ++ ++ table = &flowtable[!!(info->flags & XT_FLOWOFFLOAD_HW)]; ++ ++ net = read_pnet(&table->ft.net); ++ if (!net) ++ write_pnet(&table->ft.net, xt_net(par)); ++ ++ __set_bit(NF_FLOW_HW_BIDIRECTIONAL, &flow->flags); ++ if (flow_offload_add(&table->ft, flow) < 0) ++ goto err_flow_add; ++ ++ xt_flowoffload_check_device(table, devs[0]); ++ xt_flowoffload_check_device(table, devs[1]); ++ ++ return XT_CONTINUE; ++ ++err_flow_add: ++ flow_offload_free(flow); ++err_flow_alloc: ++ dst_release(route.tuple[dir].dst); ++ dst_release(route.tuple[!dir].dst); ++err_flow_route: ++ clear_bit(IPS_OFFLOAD_BIT, &ct->status); ++ ++ return XT_CONTINUE; ++} ++ ++static int flowoffload_chk(const struct xt_tgchk_param *par) ++{ ++ struct xt_flowoffload_target_info *info = par->targinfo; ++ ++ if (info->flags & ~XT_FLOWOFFLOAD_MASK) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static struct xt_target offload_tg_reg __read_mostly = { ++ .family = NFPROTO_UNSPEC, ++ .name = "FLOWOFFLOAD", ++ .revision = 0, ++ .targetsize = sizeof(struct xt_flowoffload_target_info), ++ .usersize = sizeof(struct xt_flowoffload_target_info), ++ .checkentry = flowoffload_chk, ++ .target = flowoffload_tg, ++ .me = THIS_MODULE, ++}; ++ ++static int flow_offload_netdev_event(struct notifier_block *this, ++ unsigned long event, void *ptr) ++{ ++ struct xt_flowoffload_hook *hook0, *hook1; ++ struct net_device *dev = netdev_notifier_info_to_dev(ptr); ++ ++ if (event != NETDEV_UNREGISTER) ++ return NOTIFY_DONE; ++ ++ spin_lock_bh(&hooks_lock); ++ hook0 = flow_offload_lookup_hook(&flowtable[0], dev); ++ if (hook0) ++ hlist_del(&hook0->list); ++ ++ hook1 = flow_offload_lookup_hook(&flowtable[1], dev); ++ if (hook1) ++ hlist_del(&hook1->list); ++ spin_unlock_bh(&hooks_lock); ++ ++ if (hook0) { ++ nf_unregister_net_hook(hook0->net, &hook0->ops); ++ kfree(hook0); ++ } ++ ++ if (hook1) { ++ nf_unregister_net_hook(hook1->net, &hook1->ops); ++ kfree(hook1); ++ } ++ ++ nf_flow_table_cleanup(dev); ++ ++ return NOTIFY_DONE; ++} ++ ++static struct notifier_block flow_offload_netdev_notifier = { ++ .notifier_call = flow_offload_netdev_event, ++}; ++ ++static int nf_flow_rule_route_inet(struct net *net, ++ struct flow_offload *flow, ++ enum flow_offload_tuple_dir dir, ++ struct nf_flow_rule *flow_rule) ++{ ++ const struct flow_offload_tuple *flow_tuple = &flow->tuplehash[dir].tuple; ++ int err; ++ ++ switch (flow_tuple->l3proto) { ++ case NFPROTO_IPV4: ++ err = nf_flow_rule_route_ipv4(net, flow, dir, flow_rule); ++ break; ++ case NFPROTO_IPV6: ++ err = nf_flow_rule_route_ipv6(net, flow, dir, flow_rule); ++ break; ++ default: ++ err = -1; ++ break; ++ } ++ ++ return err; ++} ++ ++static struct nf_flowtable_type flowtable_inet = { ++ .family = NFPROTO_INET, ++ .init = nf_flow_table_init, ++ .setup = nf_flow_table_offload_setup, ++ .action = nf_flow_rule_route_inet, ++ .free = nf_flow_table_free, ++ .hook = xt_flowoffload_net_hook, ++ .owner = THIS_MODULE, ++}; ++ ++static int init_flowtable(struct xt_flowoffload_table *tbl) ++{ ++ INIT_DELAYED_WORK(&tbl->work, xt_flowoffload_hook_work); ++ tbl->ft.type = &flowtable_inet; ++ tbl->ft.flags = NF_FLOWTABLE_COUNTER; ++ ++ return nf_flow_table_init(&tbl->ft); ++} ++ ++static int __init xt_flowoffload_tg_init(void) ++{ ++ int ret; ++ ++ register_netdevice_notifier(&flow_offload_netdev_notifier); ++ ++ ret = init_flowtable(&flowtable[0]); ++ if (ret) ++ return ret; ++ ++ ret = init_flowtable(&flowtable[1]); ++ if (ret) ++ goto cleanup; ++ ++ flowtable[1].ft.flags |= NF_FLOWTABLE_HW_OFFLOAD; ++ ++ ret = xt_register_target(&offload_tg_reg); ++ if (ret) ++ goto cleanup2; ++ ++ return 0; ++ ++cleanup2: ++ nf_flow_table_free(&flowtable[1].ft); ++cleanup: ++ nf_flow_table_free(&flowtable[0].ft); ++ return ret; ++} ++ ++static void __exit xt_flowoffload_tg_exit(void) ++{ ++ xt_unregister_target(&offload_tg_reg); ++ unregister_netdevice_notifier(&flow_offload_netdev_notifier); ++ nf_flow_table_free(&flowtable[0].ft); ++ nf_flow_table_free(&flowtable[1].ft); ++} ++ ++MODULE_LICENSE("GPL"); ++module_init(xt_flowoffload_tg_init); ++module_exit(xt_flowoffload_tg_exit); +--- a/net/netfilter/nf_flow_table_core.c ++++ b/net/netfilter/nf_flow_table_core.c +@@ -7,7 +7,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -374,8 +373,7 @@ flow_offload_lookup(struct nf_flowtable + } + EXPORT_SYMBOL_GPL(flow_offload_lookup); + +-static int +-nf_flow_table_iterate(struct nf_flowtable *flow_table, ++int nf_flow_table_iterate(struct nf_flowtable *flow_table, + void (*iter)(struct nf_flowtable *flowtable, + struct flow_offload *flow, void *data), + void *data) +@@ -436,6 +434,7 @@ static void nf_flow_offload_gc_step(stru + nf_flow_offload_stats(flow_table, flow); + } + } ++EXPORT_SYMBOL_GPL(nf_flow_table_iterate); + + void nf_flow_table_gc_run(struct nf_flowtable *flow_table) + { +--- /dev/null ++++ b/include/uapi/linux/netfilter/xt_FLOWOFFLOAD.h +@@ -0,0 +1,17 @@ ++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ ++#ifndef _XT_FLOWOFFLOAD_H ++#define _XT_FLOWOFFLOAD_H ++ ++#include ++ ++enum { ++ XT_FLOWOFFLOAD_HW = 1 << 0, ++ ++ XT_FLOWOFFLOAD_MASK = XT_FLOWOFFLOAD_HW ++}; ++ ++struct xt_flowoffload_target_info { ++ __u32 flags; ++}; ++ ++#endif /* _XT_FLOWOFFLOAD_H */ +--- a/include/net/netfilter/nf_flow_table.h ++++ b/include/net/netfilter/nf_flow_table.h +@@ -293,6 +293,11 @@ void nf_flow_table_free(struct nf_flowta + + void flow_offload_teardown(struct flow_offload *flow); + ++int nf_flow_table_iterate(struct nf_flowtable *flow_table, ++ void (*iter)(struct nf_flowtable *flowtable, ++ struct flow_offload *flow, void *data), ++ void *data); ++ + void nf_flow_snat_port(const struct flow_offload *flow, + struct sk_buff *skb, unsigned int thoff, + u8 protocol, enum flow_offload_tuple_dir dir); diff --git a/target/linux/generic/hack-6.6/651-wireless_mesh_header.patch b/target/linux/generic/hack-6.6/651-wireless_mesh_header.patch new file mode 100644 index 0000000000..fd7d5346ad --- /dev/null +++ b/target/linux/generic/hack-6.6/651-wireless_mesh_header.patch @@ -0,0 +1,24 @@ +From 6d3bc769657b0ee7c7506dad9911111c4226a7ea Mon Sep 17 00:00:00 2001 +From: Imre Kaloz +Date: Fri, 7 Jul 2017 17:21:05 +0200 +Subject: mac80211: increase wireless mesh header size + +lede-commit 3d4466cfd8f75f717efdb1f96fdde3c70d865fc1 +Signed-off-by: Imre Kaloz +--- + include/linux/netdevice.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -149,8 +149,8 @@ static inline bool dev_xmit_complete(int + + #if defined(CONFIG_HYPERV_NET) + # define LL_MAX_HEADER 128 +-#elif defined(CONFIG_WLAN) || IS_ENABLED(CONFIG_AX25) +-# if defined(CONFIG_MAC80211_MESH) ++#elif defined(CONFIG_WLAN) || IS_ENABLED(CONFIG_AX25) || 1 ++# if defined(CONFIG_MAC80211_MESH) || 1 + # define LL_MAX_HEADER 128 + # else + # define LL_MAX_HEADER 96 diff --git a/target/linux/generic/hack-6.6/660-fq_codel_defaults.patch b/target/linux/generic/hack-6.6/660-fq_codel_defaults.patch new file mode 100644 index 0000000000..b923a2d206 --- /dev/null +++ b/target/linux/generic/hack-6.6/660-fq_codel_defaults.patch @@ -0,0 +1,27 @@ +From a6ccb238939b25851474a279b20367fd24a0e816 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Fri, 7 Jul 2017 17:21:53 +0200 +Subject: hack: net: fq_codel: tune defaults for small devices + +Assume that x86_64 devices always have a big memory and do not need this +optimization compared to devices with only 32 MB or 64 MB RAM. + +Signed-off-by: Felix Fietkau +--- + net/sched/sch_fq_codel.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/net/sched/sch_fq_codel.c ++++ b/net/sched/sch_fq_codel.c +@@ -471,7 +471,11 @@ static int fq_codel_init(struct Qdisc *s + + sch->limit = 10*1024; + q->flows_cnt = 1024; ++#ifdef CONFIG_X86_64 + q->memory_limit = 32 << 20; /* 32 MBytes */ ++#else ++ q->memory_limit = 4 << 20; /* 4 MBytes */ ++#endif + q->drop_batch_size = 64; + q->quantum = psched_mtu(qdisc_dev(sch)); + INIT_LIST_HEAD(&q->new_flows); diff --git a/target/linux/generic/hack-6.6/661-kernel-ct-size-the-hashtable-more-adequately.patch b/target/linux/generic/hack-6.6/661-kernel-ct-size-the-hashtable-more-adequately.patch new file mode 100644 index 0000000000..fe94a9da0c --- /dev/null +++ b/target/linux/generic/hack-6.6/661-kernel-ct-size-the-hashtable-more-adequately.patch @@ -0,0 +1,25 @@ +From 804fbb3f2ec9283f7b778e057a68bfff440a0be6 Mon Sep 17 00:00:00 2001 +From: Rui Salvaterra +Date: Wed, 30 Mar 2022 22:51:55 +0100 +Subject: [PATCH] kernel: ct: size the hashtable more adequately + +To set the default size of the connection tracking hash table, a divider of +16384 becomes inadequate for a router handling lots of connections. Divide by +2048 instead, making the default size scale better with the available RAM. + +Signed-off-by: Rui Salvaterra +--- + net/netfilter/nf_conntrack_core.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/net/netfilter/nf_conntrack_core.c ++++ b/net/netfilter/nf_conntrack_core.c +@@ -2705,7 +2705,7 @@ int nf_conntrack_init_start(void) + + if (!nf_conntrack_htable_size) { + nf_conntrack_htable_size +- = (((nr_pages << PAGE_SHIFT) / 16384) ++ = (((nr_pages << PAGE_SHIFT) / 2048) + / sizeof(struct hlist_head)); + if (BITS_PER_LONG >= 64 && + nr_pages > (4 * (1024 * 1024 * 1024 / PAGE_SIZE))) diff --git a/target/linux/generic/hack-6.6/700-swconfig_switch_drivers.patch b/target/linux/generic/hack-6.6/700-swconfig_switch_drivers.patch new file mode 100644 index 0000000000..673082d840 --- /dev/null +++ b/target/linux/generic/hack-6.6/700-swconfig_switch_drivers.patch @@ -0,0 +1,131 @@ +From 36e516290611e613aa92996cb4339561452695b4 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Fri, 7 Jul 2017 17:24:23 +0200 +Subject: net: swconfig: adds openwrt switch layer + +Signed-off-by: Felix Fietkau +--- + drivers/net/phy/Kconfig | 83 +++++++++++++++++++++++++++++++++++++++++++++++ + drivers/net/phy/Makefile | 15 +++++++++ + include/uapi/linux/Kbuild | 1 + + 3 files changed, 99 insertions(+) + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -67,6 +67,80 @@ config SFP + depends on HWMON || HWMON=n + select MDIO_I2C + ++comment "Switch configuration API + drivers" ++ ++config SWCONFIG ++ tristate "Switch configuration API" ++ help ++ Switch configuration API using netlink. This allows ++ you to configure the VLAN features of certain switches. ++ ++config SWCONFIG_LEDS ++ bool "Switch LED trigger support" ++ depends on (SWCONFIG && LEDS_TRIGGERS) ++ ++config ADM6996_PHY ++ tristate "Driver for ADM6996 switches" ++ select SWCONFIG ++ help ++ Currently supports the ADM6996FC and ADM6996M switches. ++ Support for FC is very limited. ++ ++config AR8216_PHY ++ tristate "Driver for Atheros AR8216/8327 switches" ++ select SWCONFIG ++ select ETHERNET_PACKET_MANGLE ++ ++config AR8216_PHY_LEDS ++ bool "Atheros AR8216 switch LED support" ++ depends on (AR8216_PHY && LEDS_CLASS) ++ ++source "drivers/net/phy/b53/Kconfig" ++ ++config IP17XX_PHY ++ tristate "Driver for IC+ IP17xx switches" ++ select SWCONFIG ++ ++config PSB6970_PHY ++ tristate "Lantiq XWAY Tantos (PSB6970) Ethernet switch" ++ select SWCONFIG ++ ++config RTL8306_PHY ++ tristate "Driver for Realtek RTL8306S switches" ++ select SWCONFIG ++ ++config RTL8366_SMI ++ tristate "Driver for the RTL8366 SMI interface" ++ depends on GPIOLIB ++ help ++ This module implements the SMI interface protocol which is used ++ by some RTL8366 ethernet switch devices via the generic GPIO API. ++ ++if RTL8366_SMI ++ ++config RTL8366_SMI_DEBUG_FS ++ bool "RTL8366 SMI interface debugfs support" ++ depends on DEBUG_FS ++ default n ++ ++config RTL8366S_PHY ++ tristate "Driver for the Realtek RTL8366S switch" ++ select SWCONFIG ++ ++config RTL8366RB_PHY ++ tristate "Driver for the Realtek RTL8366RB switch" ++ select SWCONFIG ++ ++config RTL8367_PHY ++ tristate "Driver for the Realtek RTL8367R/M switches" ++ select SWCONFIG ++ ++config RTL8367B_PHY ++ tristate "Driver fot the Realtek RTL8367R-VB switch" ++ select SWCONFIG ++ ++endif # RTL8366_SMI ++ + comment "MII PHY device drivers" + + config AIR_EN8811H_PHY +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -24,6 +24,21 @@ libphy-$(CONFIG_LED_TRIGGER_PHY) += phy_ + obj-$(CONFIG_PHYLINK) += phylink.o + obj-$(CONFIG_PHYLIB) += libphy.o + ++obj-$(CONFIG_SWCONFIG) += swconfig.o ++obj-$(CONFIG_ADM6996_PHY) += adm6996.o ++obj-$(CONFIG_AR8216_PHY) += ar8xxx.o ++ar8xxx-y += ar8216.o ++ar8xxx-y += ar8327.o ++obj-$(CONFIG_SWCONFIG_B53) += b53/ ++obj-$(CONFIG_IP17XX_PHY) += ip17xx.o ++obj-$(CONFIG_PSB6970_PHY) += psb6970.o ++obj-$(CONFIG_RTL8306_PHY) += rtl8306.o ++obj-$(CONFIG_RTL8366_SMI) += rtl8366_smi.o ++obj-$(CONFIG_RTL8366S_PHY) += rtl8366s.o ++obj-$(CONFIG_RTL8366RB_PHY) += rtl8366rb.o ++obj-$(CONFIG_RTL8367_PHY) += rtl8367.o ++obj-$(CONFIG_RTL8367B_PHY) += rtl8367b.o ++ + obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += mii_timestamper.o + + obj-$(CONFIG_SFP) += sfp.o +--- a/include/linux/platform_data/b53.h ++++ b/include/linux/platform_data/b53.h +@@ -29,6 +29,9 @@ struct b53_platform_data { + u32 chip_id; + u16 enabled_ports; + ++ /* allow to specify an ethX alias */ ++ const char *alias; ++ + /* only used by MMAP'd driver */ + unsigned big_endian:1; + void __iomem *regs; diff --git a/target/linux/generic/hack-6.6/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch b/target/linux/generic/hack-6.6/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch new file mode 100644 index 0000000000..f09ad117b0 --- /dev/null +++ b/target/linux/generic/hack-6.6/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch @@ -0,0 +1,21 @@ +From ebd924d773223593142d417c41d4ee6fa16f1805 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +Date: Wed, 13 Jul 2022 13:45:56 +0200 +Subject: [PATCH] net/dsa/mv88e6xxx: disable ATU violation + +--- + drivers/net/dsa/mv88e6xxx/chip.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -3488,6 +3488,9 @@ static int mv88e6xxx_setup_port(struct m + else + reg = 1 << port; + ++ /* Disable ATU member violation interrupt */ ++ reg |= MV88E6XXX_PORT_ASSOC_VECTOR_IGNORE_WRONG; ++ + err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_ASSOC_VECTOR, + reg); + if (err) diff --git a/target/linux/generic/hack-6.6/721-net-add-packet-mangeling.patch b/target/linux/generic/hack-6.6/721-net-add-packet-mangeling.patch new file mode 100644 index 0000000000..b0be9dfdcb --- /dev/null +++ b/target/linux/generic/hack-6.6/721-net-add-packet-mangeling.patch @@ -0,0 +1,167 @@ +From ffe387740bbe88dd88bbe04d6375902708003d6e Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Fri, 7 Jul 2017 17:25:00 +0200 +Subject: net: add packet mangeling + +ar8216 switches have a hardware bug, which renders normal 802.1q support +unusable. Packet mangling is required to fix up the vlan for incoming +packets. + +Signed-off-by: Felix Fietkau +--- + include/linux/netdevice.h | 11 +++++++++++ + include/linux/skbuff.h | 14 ++++---------- + net/Kconfig | 6 ++++++ + net/core/dev.c | 20 +++++++++++++++----- + net/core/skbuff.c | 17 +++++++++++++++++ + net/ethernet/eth.c | 6 ++++++ + 6 files changed, 59 insertions(+), 15 deletions(-) + +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -1730,6 +1730,7 @@ enum netdev_priv_flags { + /* was IFF_LIVE_RENAME_OK */ + IFF_TX_SKB_NO_LINEAR = BIT_ULL(31), + IFF_CHANGE_PROTO_DOWN = BIT_ULL(32), ++ IFF_NO_IP_ALIGN = BIT_ULL(33), + }; + + #define IFF_802_1Q_VLAN IFF_802_1Q_VLAN +@@ -1763,6 +1764,7 @@ enum netdev_priv_flags { + #define IFF_FAILOVER_SLAVE IFF_FAILOVER_SLAVE + #define IFF_L3MDEV_RX_HANDLER IFF_L3MDEV_RX_HANDLER + #define IFF_TX_SKB_NO_LINEAR IFF_TX_SKB_NO_LINEAR ++#define IFF_NO_IP_ALIGN IFF_NO_IP_ALIGN + + /* Specifies the type of the struct net_device::ml_priv pointer */ + enum netdev_ml_priv_type { +@@ -2131,6 +2133,11 @@ struct net_device { + const struct tlsdev_ops *tlsdev_ops; + #endif + ++#ifdef CONFIG_ETHERNET_PACKET_MANGLE ++ void (*eth_mangle_rx)(struct net_device *dev, struct sk_buff *skb); ++ struct sk_buff *(*eth_mangle_tx)(struct net_device *dev, struct sk_buff *skb); ++#endif ++ + const struct header_ops *header_ops; + + unsigned char operstate; +@@ -2206,6 +2213,10 @@ struct net_device { + struct mctp_dev __rcu *mctp_ptr; + #endif + ++#ifdef CONFIG_ETHERNET_PACKET_MANGLE ++ void *phy_ptr; /* PHY device specific data */ ++#endif ++ + /* + * Cache lines mostly used on receive path (including eth_type_trans()) + */ +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -3040,6 +3040,10 @@ static inline int pskb_trim(struct sk_bu + return (len < skb->len) ? __pskb_trim(skb, len) : 0; + } + ++extern struct sk_buff *__netdev_alloc_skb_ip_align(struct net_device *dev, ++ unsigned int length, gfp_t gfp); ++ ++ + /** + * pskb_trim_unique - remove end from a paged unique (not cloned) buffer + * @skb: buffer to alter +@@ -3189,16 +3193,6 @@ static inline struct sk_buff *dev_alloc_ + } + + +-static inline struct sk_buff *__netdev_alloc_skb_ip_align(struct net_device *dev, +- unsigned int length, gfp_t gfp) +-{ +- struct sk_buff *skb = __netdev_alloc_skb(dev, length + NET_IP_ALIGN, gfp); +- +- if (NET_IP_ALIGN && skb) +- skb_reserve(skb, NET_IP_ALIGN); +- return skb; +-} +- + static inline struct sk_buff *netdev_alloc_skb_ip_align(struct net_device *dev, + unsigned int length) + { +--- a/net/Kconfig ++++ b/net/Kconfig +@@ -26,6 +26,12 @@ menuconfig NET + + if NET + ++config ETHERNET_PACKET_MANGLE ++ bool ++ help ++ This option can be selected by phy drivers that need to mangle ++ packets going in or out of an ethernet device. ++ + config WANT_COMPAT_NETLINK_MESSAGES + bool + help +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -3607,6 +3607,11 @@ static int xmit_one(struct sk_buff *skb, + if (dev_nit_active(dev)) + dev_queue_xmit_nit(skb, dev); + ++#ifdef CONFIG_ETHERNET_PACKET_MANGLE ++ if (dev->eth_mangle_tx && !(skb = dev->eth_mangle_tx(dev, skb))) ++ return NETDEV_TX_OK; ++#endif ++ + len = skb->len; + trace_net_dev_start_xmit(skb, dev); + rc = netdev_start_xmit(skb, dev, txq, more); +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -61,6 +61,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -709,6 +710,22 @@ skb_fail: + } + EXPORT_SYMBOL(__napi_alloc_skb); + ++struct sk_buff *__netdev_alloc_skb_ip_align(struct net_device *dev, ++ unsigned int length, gfp_t gfp) ++{ ++ struct sk_buff *skb = __netdev_alloc_skb(dev, length + NET_IP_ALIGN, gfp); ++ ++#ifdef CONFIG_ETHERNET_PACKET_MANGLE ++ if (dev && (dev->priv_flags & IFF_NO_IP_ALIGN)) ++ return skb; ++#endif ++ ++ if (NET_IP_ALIGN && skb) ++ skb_reserve(skb, NET_IP_ALIGN); ++ return skb; ++} ++EXPORT_SYMBOL(__netdev_alloc_skb_ip_align); ++ + void skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, int off, + int size, unsigned int truesize) + { +--- a/net/ethernet/eth.c ++++ b/net/ethernet/eth.c +@@ -171,6 +171,12 @@ __be16 eth_type_trans(struct sk_buff *sk + const struct ethhdr *eth; + + skb->dev = dev; ++ ++#ifdef CONFIG_ETHERNET_PACKET_MANGLE ++ if (dev->eth_mangle_rx) ++ dev->eth_mangle_rx(dev, skb); ++#endif ++ + skb_reset_mac_header(skb); + + eth = (struct ethhdr *)skb->data; diff --git a/target/linux/generic/hack-6.6/722-net-phy-aquantia-enable-AQR112-and-AQR412.patch b/target/linux/generic/hack-6.6/722-net-phy-aquantia-enable-AQR112-and-AQR412.patch new file mode 100644 index 0000000000..faaa40f9bd --- /dev/null +++ b/target/linux/generic/hack-6.6/722-net-phy-aquantia-enable-AQR112-and-AQR412.patch @@ -0,0 +1,148 @@ +From 5f62951fba63a9f9cfff564209426bdea5fcc371 Mon Sep 17 00:00:00 2001 +From: Alex Marginean +Date: Tue, 27 Aug 2019 15:16:56 +0300 +Subject: [PATCH] drivers: net: phy: aquantia: enable AQR112 and AQR412 + +Adds support for AQR112 and AQR412 which is mostly based on existing code +with the addition of code configuring the protocol on system side. +This allows changing the system side protocol without having to deploy a +different firmware on the PHY. + +Signed-off-by: Alex Marginean +--- + drivers/net/phy/aquantia/aquantia_main.c | 88 +++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 88 insertions(+) + +--- a/drivers/net/phy/aquantia/aquantia_main.c ++++ b/drivers/net/phy/aquantia/aquantia_main.c +@@ -27,6 +27,8 @@ + #define PHY_ID_AQR113 0x31c31c40 + #define PHY_ID_AQR113C 0x31c31c12 + #define PHY_ID_AQR813 0x31c31cb2 ++#define PHY_ID_AQR112 0x03a1b662 ++#define PHY_ID_AQR412 0x03a1b712 + + #define MDIO_PHYXS_VEND_IF_STATUS 0xe812 + #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK GENMASK(7, 3) +@@ -99,6 +101,29 @@ + #define AQR107_OP_IN_PROG_SLEEP 1000 + #define AQR107_OP_IN_PROG_TIMEOUT 100000 + ++/* registers in MDIO_MMD_VEND1 region */ ++#define AQUANTIA_VND1_GLOBAL_SC 0x000 ++#define AQUANTIA_VND1_GLOBAL_SC_LP BIT(0xb) ++ ++/* global start rate, the protocol associated with this speed is used by default ++ * on SI. ++ */ ++#define AQUANTIA_VND1_GSTART_RATE 0x31a ++#define AQUANTIA_VND1_GSTART_RATE_OFF 0 ++#define AQUANTIA_VND1_GSTART_RATE_100M 1 ++#define AQUANTIA_VND1_GSTART_RATE_1G 2 ++#define AQUANTIA_VND1_GSTART_RATE_10G 3 ++#define AQUANTIA_VND1_GSTART_RATE_2_5G 4 ++#define AQUANTIA_VND1_GSTART_RATE_5G 5 ++ ++/* SYSCFG registers for 100M, 1G, 2.5G, 5G, 10G */ ++#define AQUANTIA_VND1_GSYSCFG_BASE 0x31b ++#define AQUANTIA_VND1_GSYSCFG_100M 0 ++#define AQUANTIA_VND1_GSYSCFG_1G 1 ++#define AQUANTIA_VND1_GSYSCFG_2_5G 2 ++#define AQUANTIA_VND1_GSYSCFG_5G 3 ++#define AQUANTIA_VND1_GSYSCFG_10G 4 ++ + struct aqr107_hw_stat { + const char *name; + int reg; +@@ -230,6 +255,51 @@ static int aqr_config_aneg(struct phy_de + return genphy_c45_check_and_restart_aneg(phydev, changed); + } + ++static struct { ++ u16 syscfg; ++ int cnt; ++ u16 start_rate; ++} aquantia_syscfg[PHY_INTERFACE_MODE_MAX] = { ++ [PHY_INTERFACE_MODE_SGMII] = {0x04b, AQUANTIA_VND1_GSYSCFG_1G, ++ AQUANTIA_VND1_GSTART_RATE_1G}, ++ [PHY_INTERFACE_MODE_2500BASEX] = {0x144, AQUANTIA_VND1_GSYSCFG_2_5G, ++ AQUANTIA_VND1_GSTART_RATE_2_5G}, ++ [PHY_INTERFACE_MODE_XGMII] = {0x100, AQUANTIA_VND1_GSYSCFG_10G, ++ AQUANTIA_VND1_GSTART_RATE_10G}, ++ [PHY_INTERFACE_MODE_USXGMII] = {0x080, AQUANTIA_VND1_GSYSCFG_10G, ++ AQUANTIA_VND1_GSTART_RATE_10G}, ++}; ++ ++/* Sets up protocol on system side before calling aqr_config_aneg */ ++static int aqr_config_aneg_set_prot(struct phy_device *phydev) ++{ ++ int if_type = phydev->interface; ++ int i; ++ ++ if (!aquantia_syscfg[if_type].cnt) ++ return 0; ++ ++ /* set PHY in low power mode so we can configure protocols */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GLOBAL_SC, ++ AQUANTIA_VND1_GLOBAL_SC_LP); ++ mdelay(10); ++ ++ /* set the default rate to enable the SI link */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GSTART_RATE, ++ aquantia_syscfg[if_type].start_rate); ++ ++ for (i = 0; i <= aquantia_syscfg[if_type].cnt; i++) ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, ++ AQUANTIA_VND1_GSYSCFG_BASE + i, ++ aquantia_syscfg[if_type].syscfg); ++ ++ /* wake PHY back up */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GLOBAL_SC, 0); ++ mdelay(10); ++ ++ return aqr_config_aneg(phydev); ++} ++ + static int aqr_config_intr(struct phy_device *phydev) + { + bool en = phydev->interrupts == PHY_INTERRUPT_ENABLED; +@@ -860,6 +930,30 @@ static struct phy_driver aqr_driver[] = + .get_stats = aqr107_get_stats, + .link_change_notify = aqr107_link_change_notify, + }, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR112), ++ .name = "Aquantia AQR112", ++ .probe = aqr107_probe, ++ .config_aneg = aqr_config_aneg_set_prot, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr107_read_status, ++ .get_sset_count = aqr107_get_sset_count, ++ .get_strings = aqr107_get_strings, ++ .get_stats = aqr107_get_stats, ++}, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR412), ++ .name = "Aquantia AQR412", ++ .probe = aqr107_probe, ++ .config_aneg = aqr_config_aneg_set_prot, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr107_read_status, ++ .get_sset_count = aqr107_get_sset_count, ++ .get_strings = aqr107_get_strings, ++ .get_stats = aqr107_get_stats, ++}, + }; + + module_phy_driver(aqr_driver); +@@ -877,6 +971,8 @@ static struct mdio_device_id __maybe_unu + { PHY_ID_MATCH_MODEL(PHY_ID_AQR113) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR813) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR112) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR412) }, + { } + }; + diff --git a/target/linux/generic/hack-6.6/723-net-phy-aquantia-fix-system-side-protocol-mi.patch b/target/linux/generic/hack-6.6/723-net-phy-aquantia-fix-system-side-protocol-mi.patch new file mode 100644 index 0000000000..72a70ebc14 --- /dev/null +++ b/target/linux/generic/hack-6.6/723-net-phy-aquantia-fix-system-side-protocol-mi.patch @@ -0,0 +1,34 @@ +From 5f008cb22f60da4e10375f22266c1a4e20b1252e Mon Sep 17 00:00:00 2001 +From: Alex Marginean +Date: Fri, 20 Sep 2019 18:22:52 +0300 +Subject: [PATCH] drivers: net: phy: aquantia: fix system side protocol + misconfiguration + +Do not set up protocols for speeds that are not supported by FW. Enabling +these protocols leads to link issues on system side. + +Signed-off-by: Alex Marginean +--- + drivers/net/phy/aquantia/aquantia_main.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +--- a/drivers/net/phy/aquantia/aquantia_main.c ++++ b/drivers/net/phy/aquantia/aquantia_main.c +@@ -288,10 +288,16 @@ static int aqr_config_aneg_set_prot(stru + phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GSTART_RATE, + aquantia_syscfg[if_type].start_rate); + +- for (i = 0; i <= aquantia_syscfg[if_type].cnt; i++) ++ for (i = 0; i <= aquantia_syscfg[if_type].cnt; i++) { ++ u16 reg = phy_read_mmd(phydev, MDIO_MMD_VEND1, ++ AQUANTIA_VND1_GSYSCFG_BASE + i); ++ if (!reg) ++ continue; ++ + phy_write_mmd(phydev, MDIO_MMD_VEND1, + AQUANTIA_VND1_GSYSCFG_BASE + i, + aquantia_syscfg[if_type].syscfg); ++ } + + /* wake PHY back up */ + phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GLOBAL_SC, 0); diff --git a/target/linux/generic/hack-6.6/725-net-phy-aquantia-add-PHY_IDs-for-AQR112-variants.patch b/target/linux/generic/hack-6.6/725-net-phy-aquantia-add-PHY_IDs-for-AQR112-variants.patch new file mode 100644 index 0000000000..d83cc48a33 --- /dev/null +++ b/target/linux/generic/hack-6.6/725-net-phy-aquantia-add-PHY_IDs-for-AQR112-variants.patch @@ -0,0 +1,63 @@ +From 3b92ee7b7899b6beffb2b484c58326e36612a873 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 23 Dec 2021 14:52:56 +0000 +Subject: [PATCH] net: phy: aquantia: add PHY_ID for AQR112R + +As advised by Ian Chang this PHY is used in Puzzle devices. + +Signed-off-by: Daniel Golle +--- + drivers/net/phy/aquantia/aquantia_main.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/drivers/net/phy/aquantia/aquantia_main.c ++++ b/drivers/net/phy/aquantia/aquantia_main.c +@@ -29,6 +29,8 @@ + #define PHY_ID_AQR813 0x31c31cb2 + #define PHY_ID_AQR112 0x03a1b662 + #define PHY_ID_AQR412 0x03a1b712 ++#define PHY_ID_AQR112C 0x03a1b790 ++#define PHY_ID_AQR112R 0x31c31d12 + + #define MDIO_PHYXS_VEND_IF_STATUS 0xe812 + #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK GENMASK(7, 3) +@@ -960,6 +962,30 @@ static struct phy_driver aqr_driver[] = + .get_strings = aqr107_get_strings, + .get_stats = aqr107_get_stats, + }, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR112C), ++ .name = "Aquantia AQR112C", ++ .probe = aqr107_probe, ++ .config_aneg = aqr_config_aneg_set_prot, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr107_read_status, ++ .get_sset_count = aqr107_get_sset_count, ++ .get_strings = aqr107_get_strings, ++ .get_stats = aqr107_get_stats, ++}, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR112R), ++ .name = "Aquantia AQR112R", ++ .probe = aqr107_probe, ++ .config_aneg = aqr_config_aneg_set_prot, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr107_read_status, ++ .get_sset_count = aqr107_get_sset_count, ++ .get_strings = aqr107_get_strings, ++ .get_stats = aqr107_get_stats, ++}, + }; + + module_phy_driver(aqr_driver); +@@ -979,6 +1005,8 @@ static struct mdio_device_id __maybe_unu + { PHY_ID_MATCH_MODEL(PHY_ID_AQR813) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR112) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR412) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR112C) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR112R) }, + { } + }; + diff --git a/target/linux/generic/hack-6.6/750-net-pcs-mtk-lynxi-workaround-2500BaseX-no-an.patch b/target/linux/generic/hack-6.6/750-net-pcs-mtk-lynxi-workaround-2500BaseX-no-an.patch new file mode 100644 index 0000000000..a2bd3a3dbb --- /dev/null +++ b/target/linux/generic/hack-6.6/750-net-pcs-mtk-lynxi-workaround-2500BaseX-no-an.patch @@ -0,0 +1,53 @@ +From 880d1311335120f64447ca9d11933872d734e19a Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 27 Mar 2023 18:41:54 +0100 +Subject: [PATCH] generic: pcs-mtk-lynxi: add hack to use 2500Base-X without AN + +Using 2500Base-T SFP modules e.g. on the BananaPi R3 requires manually +disabling auto-negotiation, e.g. using ethtool. While a proper fix +using SFP quirks is being discussed upstream, bring a work-around to +restore user experience to what it was before the switch to the +dedicated SGMII PCS driver. + +Signed-off-by: Daniel Golle + +--- a/drivers/net/pcs/pcs-mtk-lynxi.c ++++ b/drivers/net/pcs/pcs-mtk-lynxi.c +@@ -114,14 +114,23 @@ static void mtk_pcs_lynxi_get_state(stru + struct phylink_link_state *state) + { + struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); +- unsigned int bm, adv; ++ unsigned int bm, bmsr, adv; + + /* Read the BMSR and LPA */ + regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm); +- regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv); ++ bmsr = FIELD_GET(SGMII_BMSR, bm); ++ ++ if (state->interface == PHY_INTERFACE_MODE_2500BASEX) { ++ state->link = !!(bmsr & BMSR_LSTATUS); ++ state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE); ++ state->speed = SPEED_2500; ++ state->duplex = DUPLEX_FULL; + +- phylink_mii_c22_pcs_decode_state(state, FIELD_GET(SGMII_BMSR, bm), +- FIELD_GET(SGMII_LPA, adv)); ++ return; ++ } ++ ++ regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv); ++ phylink_mii_c22_pcs_decode_state(state, bmsr, FIELD_GET(SGMII_LPA, adv)); + } + + static void mtk_sgmii_reset(struct mtk_pcs_lynxi *mpcs) +@@ -163,7 +172,8 @@ static int mtk_pcs_lynxi_config(struct p + if (neg_mode & PHYLINK_PCS_NEG_INBAND) + sgm_mode |= SGMII_REMOTE_FAULT_DIS; + +- if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) { ++ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED && ++ interface != PHY_INTERFACE_MODE_2500BASEX) { + if (interface == PHY_INTERFACE_MODE_SGMII) + sgm_mode |= SGMII_SPEED_DUPLEX_AN; + bmcr = BMCR_ANENABLE; diff --git a/target/linux/generic/hack-6.6/760-net-usb-r8152-add-LED-configuration-from-OF.patch b/target/linux/generic/hack-6.6/760-net-usb-r8152-add-LED-configuration-from-OF.patch new file mode 100644 index 0000000000..48d4626ed6 --- /dev/null +++ b/target/linux/generic/hack-6.6/760-net-usb-r8152-add-LED-configuration-from-OF.patch @@ -0,0 +1,74 @@ +From 82985725e071f2a5735052f18e109a32aeac3a0b Mon Sep 17 00:00:00 2001 +From: David Bauer +Date: Sun, 26 Jul 2020 02:38:31 +0200 +Subject: [PATCH] net: usb: r8152: add LED configuration from OF + +This adds the ability to configure the LED configuration register using +OF. This way, the correct value for board specific LED configuration can +be determined. + +Signed-off-by: David Bauer +--- + drivers/net/usb/r8152.c | 23 +++++++++++++++++++++++ + 1 file changed, 23 insertions(+) + +--- a/drivers/net/usb/r8152.c ++++ b/drivers/net/usb/r8152.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -7034,6 +7035,22 @@ static void rtl_tally_reset(struct r8152 + ocp_write_word(tp, MCU_TYPE_PLA, PLA_RSTTALLY, ocp_data); + } + ++static int r8152_led_configuration(struct r8152 *tp) ++{ ++ u32 led_data; ++ int ret; ++ ++ ret = of_property_read_u32(tp->udev->dev.of_node, "realtek,led-data", ++ &led_data); ++ ++ if (ret) ++ return ret; ++ ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_LEDSEL, led_data); ++ ++ return 0; ++} ++ + static void r8152b_init(struct r8152 *tp) + { + u32 ocp_data; +@@ -7075,6 +7092,8 @@ static void r8152b_init(struct r8152 *tp + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL); + ocp_data &= ~(RX_AGG_DISABLE | RX_ZERO_EN); + ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data); ++ ++ r8152_led_configuration(tp); + } + + static void r8153_init(struct r8152 *tp) +@@ -7215,6 +7234,8 @@ static void r8153_init(struct r8152 *tp) + tp->coalesce = COALESCE_SLOW; + break; + } ++ ++ r8152_led_configuration(tp); + } + + static void r8153b_init(struct r8152 *tp) +@@ -7297,6 +7318,8 @@ static void r8153b_init(struct r8152 *tp + rtl_tally_reset(tp); + + tp->coalesce = 15000; /* 15 us */ ++ ++ r8152_led_configuration(tp); + } + + static void r8153c_init(struct r8152 *tp) diff --git a/target/linux/generic/hack-6.6/761-dt-bindings-net-add-RTL8152-binding-documentation.patch b/target/linux/generic/hack-6.6/761-dt-bindings-net-add-RTL8152-binding-documentation.patch new file mode 100644 index 0000000000..be262b993c --- /dev/null +++ b/target/linux/generic/hack-6.6/761-dt-bindings-net-add-RTL8152-binding-documentation.patch @@ -0,0 +1,54 @@ +From 3ee05f4aa64fc86af3be5bc176ba5808de9260a7 Mon Sep 17 00:00:00 2001 +From: David Bauer +Date: Sun, 26 Jul 2020 15:30:33 +0200 +Subject: [PATCH] dt-bindings: net: add RTL8152 binding documentation + +Add binding documentation for the Realtek RTL8152 / RTL8153 USB ethernet +adapters. + +Signed-off-by: David Bauer +--- + .../bindings/net/realtek,rtl8152.yaml | 36 +++++++++++++++++++ + 1 file changed, 36 insertions(+) + create mode 100644 Documentation/devicetree/bindings/net/realtek,rtl8152.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/net/realtek,rtl8152.yaml +@@ -0,0 +1,36 @@ ++# SPDX-License-Identifier: GPL-2.0 ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/net/realtek,rtl8152.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Realtek RTL8152/RTL8153 series USB ethernet ++ ++maintainers: ++ - David Bauer ++ ++properties: ++ compatible: ++ oneOf: ++ - items: ++ - enum: ++ - realtek,rtl8152 ++ - realtek,rtl8153 ++ ++ reg: ++ description: The device number on the USB bus ++ ++ realtek,led-data: ++ description: Value to be written to the LED configuration register. ++ ++required: ++ - compatible ++ - reg ++ ++examples: ++ - | ++ usb-eth@2 { ++ compatible = "realtek,rtl8153"; ++ reg = <2>; ++ realtek,led-data = <0x87>; ++ }; +\ No newline at end of file diff --git a/target/linux/generic/hack-6.6/765-mxl-gpy-control-LED-reg-from-DT.patch b/target/linux/generic/hack-6.6/765-mxl-gpy-control-LED-reg-from-DT.patch new file mode 100644 index 0000000000..2724efa15e --- /dev/null +++ b/target/linux/generic/hack-6.6/765-mxl-gpy-control-LED-reg-from-DT.patch @@ -0,0 +1,105 @@ +From 94b90966095f3fa625897e8f53d215882f6e19b3 Mon Sep 17 00:00:00 2001 +From: David Bauer +Date: Sat, 11 Mar 2023 17:00:01 +0100 +Subject: [PATCH] mxl-gpy: control LED reg from DT + +Add dynamic configuration for the LED control registers on MXL PHYs. + +This patch has been tested with MaxLinear GPY211C. It is unlikely to be +accepted upstream, as upstream plans on integrating their own framework +for handling these LEDs. + +For the time being, use this hack to configure PHY driven device-LEDs to +show the correct state. + +A possible alternative might be to expose the LEDs using the kernel LED +framework and bind it to the netdevice. This might also be upstreamable, +although it is a considerable extra amount of work. + +Signed-off-by: David Bauer +--- + drivers/net/phy/mxl-gpy.c | 37 ++++++++++++++++++++++++++++++++++++- + 1 file changed, 36 insertions(+), 1 deletion(-) + +--- a/drivers/net/phy/mxl-gpy.c ++++ b/drivers/net/phy/mxl-gpy.c +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -33,6 +34,7 @@ + #define PHY_MIISTAT 0x18 /* MII state */ + #define PHY_IMASK 0x19 /* interrupt mask */ + #define PHY_ISTAT 0x1A /* interrupt status */ ++#define PHY_LED 0x1B /* LED control */ + #define PHY_FWV 0x1E /* firmware version */ + + #define PHY_MIISTAT_SPD_MASK GENMASK(2, 0) +@@ -56,10 +58,15 @@ + PHY_IMASK_ADSC | \ + PHY_IMASK_ANC) + ++#define PHY_LED_NUM_LEDS 4 ++ + #define PHY_FWV_REL_MASK BIT(15) + #define PHY_FWV_MAJOR_MASK GENMASK(11, 8) + #define PHY_FWV_MINOR_MASK GENMASK(7, 0) + ++/* LED */ ++#define VSPEC1_LED(x) (0x1 + x) ++ + /* SGMII */ + #define VSPEC1_SGMII_CTRL 0x08 + #define VSPEC1_SGMII_CTRL_ANEN BIT(12) /* Aneg enable */ +@@ -241,6 +248,35 @@ out: + return ret; + } + ++static int gpy_led_write(struct phy_device *phydev) ++{ ++ struct device_node *node = phydev->mdio.dev.of_node; ++ u32 led_regs[PHY_LED_NUM_LEDS]; ++ int i, ret; ++ u16 val = 0xff00; ++ ++ if (!IS_ENABLED(CONFIG_OF_MDIO)) ++ return 0; ++ ++ if (of_property_read_u32_array(node, "mxl,led-config", led_regs, PHY_LED_NUM_LEDS)) ++ return 0; ++ ++ if (of_property_read_bool(node, "mxl,led-drive-vdd")) ++ val &= 0x0fff; ++ ++ /* Enable LED function handling on all ports*/ ++ phy_write(phydev, PHY_LED, val); ++ ++ /* Write LED register values */ ++ for (i = 0; i < PHY_LED_NUM_LEDS; i++) { ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_LED(i), (u16)led_regs[i]); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ + static int gpy_config_init(struct phy_device *phydev) + { + int ret; +@@ -252,7 +288,10 @@ static int gpy_config_init(struct phy_de + + /* Clear all pending interrupts */ + ret = phy_read(phydev, PHY_ISTAT); +- return ret < 0 ? ret : 0; ++ if (ret < 0) ++ return ret; ++ ++ return gpy_led_write(phydev); + } + + static bool gpy_has_broken_mdint(struct phy_device *phydev) diff --git a/target/linux/generic/hack-6.6/766-net-phy-mediatek-ge-add-LED-configuration-interface.patch b/target/linux/generic/hack-6.6/766-net-phy-mediatek-ge-add-LED-configuration-interface.patch new file mode 100644 index 0000000000..3405d5c535 --- /dev/null +++ b/target/linux/generic/hack-6.6/766-net-phy-mediatek-ge-add-LED-configuration-interface.patch @@ -0,0 +1,72 @@ +From cc225d163b5a4f7a0d1968298bf7927306646a47 Mon Sep 17 00:00:00 2001 +From: David Bauer +Date: Fri, 28 Apr 2023 01:53:01 +0200 +Subject: [PATCH] net: phy: mediatek-ge: add LED configuration interface + +This adds a small hack similar to the one used for ar8xxx switches to +read a reg:value map for configuring the LED configuration registers. + +This allows OpenWrt to write device-specific LED action as well as blink +configurations. It is unlikely to be accepted upstream, as upstream +plans on integrating their own framework for handling these LEDs. + +Signed-off-by: David Bauer +--- + drivers/net/phy/mediatek-ge.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +--- a/drivers/net/phy/mediatek-ge.c ++++ b/drivers/net/phy/mediatek-ge.c +@@ -1,4 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0+ ++#include + #include + #include + #include +@@ -53,6 +54,36 @@ static int mt7530_phy_config_init(struct + return 0; + } + ++static int mt7530_led_config_of(struct phy_device *phydev) ++{ ++ struct device_node *np = phydev->mdio.dev.of_node; ++ const __be32 *paddr; ++ int len; ++ int i; ++ ++ paddr = of_get_property(np, "mediatek,led-config", &len); ++ if (!paddr) ++ return 0; ++ ++ if (len < (2 * sizeof(*paddr))) ++ return -EINVAL; ++ ++ len /= sizeof(*paddr); ++ ++ phydev_warn(phydev, "Configure LED registers (num=%d)\n", len); ++ for (i = 0; i < len - 1; i += 2) { ++ u32 reg; ++ u32 val; ++ ++ reg = be32_to_cpup(paddr + i); ++ val = be32_to_cpup(paddr + i + 1); ++ ++ phy_write_mmd(phydev, MDIO_MMD_VEND2, reg, val); ++ } ++ ++ return 0; ++} ++ + static int mt7531_phy_config_init(struct phy_device *phydev) + { + mtk_gephy_config_init(phydev); +@@ -65,6 +96,9 @@ static int mt7531_phy_config_init(struct + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x13, 0x404); + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x14, 0x404); + ++ /* LED Config*/ ++ mt7530_led_config_of(phydev); ++ + return 0; + } + diff --git a/target/linux/generic/hack-6.6/773-bgmac-add-srab-switch.patch b/target/linux/generic/hack-6.6/773-bgmac-add-srab-switch.patch new file mode 100644 index 0000000000..633cacd1e7 --- /dev/null +++ b/target/linux/generic/hack-6.6/773-bgmac-add-srab-switch.patch @@ -0,0 +1,98 @@ +From 3cb240533ab787899dc7f17aa7d6c5b4810e2e58 Mon Sep 17 00:00:00 2001 +From: Hauke Mehrtens +Date: Fri, 7 Jul 2017 17:26:01 +0200 +Subject: bcm53xx: bgmac: use srab switch driver + +use the srab switch driver on these SoCs. + +Signed-off-by: Hauke Mehrtens +--- + drivers/net/ethernet/broadcom/bgmac-bcma.c | 1 + + drivers/net/ethernet/broadcom/bgmac.c | 24 ++++++++++++++++++++++++ + drivers/net/ethernet/broadcom/bgmac.h | 4 ++++ + 3 files changed, 29 insertions(+) + +--- a/drivers/net/ethernet/broadcom/bgmac-bcma.c ++++ b/drivers/net/ethernet/broadcom/bgmac-bcma.c +@@ -280,6 +280,7 @@ static int bgmac_probe(struct bcma_devic + bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST; + bgmac->feature_flags |= BGMAC_FEAT_NO_RESET; + bgmac->feature_flags |= BGMAC_FEAT_FORCE_SPEED_2500; ++ bgmac->feature_flags |= BGMAC_FEAT_SRAB; + break; + default: + bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST; +--- a/drivers/net/ethernet/broadcom/bgmac.c ++++ b/drivers/net/ethernet/broadcom/bgmac.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1408,6 +1409,17 @@ static const struct ethtool_ops bgmac_et + .set_link_ksettings = phy_ethtool_set_link_ksettings, + }; + ++static struct b53_platform_data bgmac_b53_pdata = { ++}; ++ ++static struct platform_device bgmac_b53_dev = { ++ .name = "b53-srab-switch", ++ .id = -1, ++ .dev = { ++ .platform_data = &bgmac_b53_pdata, ++ }, ++}; ++ + /************************************************** + * MII + **************************************************/ +@@ -1546,6 +1558,14 @@ int bgmac_enet_probe(struct bgmac *bgmac + + bgmac->in_init = false; + ++ if ((bgmac->feature_flags & BGMAC_FEAT_SRAB) && !bgmac_b53_pdata.regs) { ++ bgmac_b53_pdata.regs = ioremap(0x18007000, 0x1000); ++ ++ err = platform_device_register(&bgmac_b53_dev); ++ if (!err) ++ bgmac->b53_device = &bgmac_b53_dev; ++ } ++ + err = register_netdev(bgmac->net_dev); + if (err) { + dev_err(bgmac->dev, "Cannot register net device\n"); +@@ -1568,6 +1588,10 @@ EXPORT_SYMBOL_GPL(bgmac_enet_probe); + + void bgmac_enet_remove(struct bgmac *bgmac) + { ++ if (bgmac->b53_device) ++ platform_device_unregister(&bgmac_b53_dev); ++ bgmac->b53_device = NULL; ++ + unregister_netdev(bgmac->net_dev); + phy_disconnect(bgmac->net_dev->phydev); + netif_napi_del(&bgmac->napi); +--- a/drivers/net/ethernet/broadcom/bgmac.h ++++ b/drivers/net/ethernet/broadcom/bgmac.h +@@ -388,6 +388,7 @@ + #define BGMAC_FEAT_CC4_IF_SW_TYPE_RGMII BIT(18) + #define BGMAC_FEAT_CC7_IF_TYPE_RGMII BIT(19) + #define BGMAC_FEAT_IDM_MASK BIT(20) ++#define BGMAC_FEAT_SRAB BIT(21) + + struct bgmac_slot_info { + union { +@@ -495,6 +496,9 @@ struct bgmac { + void (*cmn_maskset32)(struct bgmac *bgmac, u16 offset, u32 mask, + u32 set); + int (*phy_connect)(struct bgmac *bgmac); ++ ++ /* platform device for associated switch */ ++ struct platform_device *b53_device; + }; + + struct bgmac *bgmac_alloc(struct device *dev); diff --git a/target/linux/generic/hack-6.6/780-usb-net-MeigLink_modem_support.patch b/target/linux/generic/hack-6.6/780-usb-net-MeigLink_modem_support.patch new file mode 100644 index 0000000000..51f939356f --- /dev/null +++ b/target/linux/generic/hack-6.6/780-usb-net-MeigLink_modem_support.patch @@ -0,0 +1,69 @@ +From f81700b6bb2eda3756247bce472d8eaf6f466f61 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +Date: Wed, 13 Jul 2022 13:49:26 +0200 +Subject: [PATCH] net/usb/qmi_wwan: add MeigLink modem support + +--- + drivers/net/usb/qmi_wwan.c | 1 + + drivers/usb/serial/option.c | 7 +++++++ + 2 files changed, 8 insertions(+) + +--- a/drivers/net/usb/qmi_wwan.c ++++ b/drivers/net/usb/qmi_wwan.c +@@ -1082,12 +1082,18 @@ static const struct usb_device_id produc + USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x581d, USB_CLASS_VENDOR_SPEC, 1, 7), + .driver_info = (unsigned long)&qmi_wwan_info, + }, ++ { /* Meiglink SGM828 */ ++ USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d49, USB_CLASS_VENDOR_SPEC, 0x10, 0x05), ++ .driver_info = (unsigned long)&qmi_wwan_info, ++ }, ++ + {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0125)}, /* Quectel EC25, EC20 R2.0 Mini PCIe */ + {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0306)}, /* Quectel EP06/EG06/EM06 */ + {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0512)}, /* Quectel EG12/EM12 */ + {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0620)}, /* Quectel EM160R-GL */ + {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0800)}, /* Quectel RM500Q-GL */ + {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0801)}, /* Quectel RM520N */ ++ {QMI_MATCH_FF_FF_FF(0x05c6, 0xf601)}, /* MeigLink SLM750 */ + + /* 3. Combined interface devices matching on interface number */ + {QMI_FIXED_INTF(0x0408, 0xea42, 4)}, /* Yota / Megafon M100-1 */ +--- a/drivers/usb/serial/option.c ++++ b/drivers/usb/serial/option.c +@@ -247,6 +247,11 @@ static void option_instat_callback(struc + #define UBLOX_PRODUCT_R410M 0x90b2 + /* These Yuga products use Qualcomm's vendor ID */ + #define YUGA_PRODUCT_CLM920_NC5 0x9625 ++/* These MeigLink products use Qualcomm's vendor ID */ ++#define MEIGLINK_PRODUCT_SLM750 0xf601 ++ ++#define MEIGLINK_VENDOR_ID 0x2dee ++#define MEIGLINK_PRODUCT_SLM828 0x4d49 + + #define QUECTEL_VENDOR_ID 0x2c7c + /* These Quectel products use Quectel's vendor ID */ +@@ -1147,6 +1152,11 @@ static const struct usb_device_id option + { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x0023)}, /* ONYX 3G device */ + { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x9000), /* SIMCom SIM5218 */ + .driver_info = NCTRL(0) | NCTRL(1) | NCTRL(2) | NCTRL(3) | RSVD(4) }, ++ /* MeiG */ ++ { USB_DEVICE_AND_INTERFACE_INFO(MEIGLINK_VENDOR_ID, MEIGLINK_PRODUCT_SLM828, USB_CLASS_VENDOR_SPEC, 0x10, 0x01) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(MEIGLINK_VENDOR_ID, MEIGLINK_PRODUCT_SLM828, USB_CLASS_VENDOR_SPEC, 0x10, 0x02) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(MEIGLINK_VENDOR_ID, MEIGLINK_PRODUCT_SLM828, USB_CLASS_VENDOR_SPEC, 0x10, 0x03) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(MEIGLINK_VENDOR_ID, MEIGLINK_PRODUCT_SLM828, USB_CLASS_VENDOR_SPEC, 0x10, 0x04) }, + /* Quectel products using Qualcomm vendor ID */ + { USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC15)}, + { USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC20), +@@ -1188,6 +1198,11 @@ static const struct usb_device_id option + .driver_info = ZLP }, + { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_BG96), + .driver_info = RSVD(4) }, ++ /* Meiglink products using Qualcomm vendor ID */ ++ // Works OK. In case of some issues check macros that are used by Quectel Products ++ { USB_DEVICE_AND_INTERFACE_INFO(QUALCOMM_VENDOR_ID, MEIGLINK_PRODUCT_SLM750, 0xff, 0xff, 0xff), ++ .driver_info = NUMEP2 }, ++ { USB_DEVICE_AND_INTERFACE_INFO(QUALCOMM_VENDOR_ID, MEIGLINK_PRODUCT_SLM750, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0xff, 0xff), + .driver_info = RSVD(1) | RSVD(2) | RSVD(3) | RSVD(4) | NUMEP2 }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0, 0) }, diff --git a/target/linux/generic/hack-6.6/781-usb-net-rndis-support-asr.patch b/target/linux/generic/hack-6.6/781-usb-net-rndis-support-asr.patch new file mode 100644 index 0000000000..9934bb8078 --- /dev/null +++ b/target/linux/generic/hack-6.6/781-usb-net-rndis-support-asr.patch @@ -0,0 +1,56 @@ +--- a/drivers/net/usb/rndis_host.c ++++ b/drivers/net/usb/rndis_host.c +@@ -630,6 +630,16 @@ static const struct driver_info zte_rndi + .tx_fixup = rndis_tx_fixup, + }; + ++static const struct driver_info asr_rndis_info = { ++ .description = "Asr RNDIS device", ++ .flags = FLAG_WWAN | FLAG_POINTTOPOINT | FLAG_FRAMING_RN | FLAG_NO_SETINT | FLAG_NOARP, ++ .bind = rndis_bind, ++ .unbind = rndis_unbind, ++ .status = rndis_status, ++ .rx_fixup = rndis_rx_fixup, ++ .tx_fixup = rndis_tx_fixup, ++}; ++ + /*-------------------------------------------------------------------------*/ + + static const struct usb_device_id products [] = { +@@ -666,6 +676,36 @@ static const struct usb_device_id produc + USB_INTERFACE_INFO(USB_CLASS_WIRELESS_CONTROLLER, 1, 3), + .driver_info = (unsigned long) &rndis_info, + }, { ++ /* Quectel EG060V rndis device */ ++ USB_DEVICE_AND_INTERFACE_INFO(0x2c7c, 0x6004, ++ USB_CLASS_WIRELESS_CONTROLLER, 1, 3), ++ .driver_info = (unsigned long) &asr_rndis_info, ++}, { ++ /* Quectel EC200A rndis device */ ++ USB_DEVICE_AND_INTERFACE_INFO(0x2c7c, 0x6005, ++ USB_CLASS_WIRELESS_CONTROLLER, 1, 3), ++ .driver_info = (unsigned long) &asr_rndis_info, ++}, { ++ /* Quectel EC200T rndis device */ ++ USB_DEVICE_AND_INTERFACE_INFO(0x2c7c, 0x6026, ++ USB_CLASS_WIRELESS_CONTROLLER, 1, 3), ++ .driver_info = (unsigned long) &asr_rndis_info, ++}, { ++ /* Simcom A7906E rndis device */ ++ USB_DEVICE_AND_INTERFACE_INFO(0x1e0e, 0x9011, ++ USB_CLASS_WIRELESS_CONTROLLER, 1, 3), ++ .driver_info = (unsigned long) &asr_rndis_info, ++}, { ++ /* Meig SLM770A */ ++ USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d57, ++ USB_CLASS_WIRELESS_CONTROLLER, 1, 3), ++ .driver_info = (unsigned long) &asr_rndis_info, ++}, { ++ /* Meig SLM828 */ ++ USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d49, ++ USB_CLASS_WIRELESS_CONTROLLER, 1, 3), ++ .driver_info = (unsigned long) &asr_rndis_info, ++}, { + /* Novatel Verizon USB730L */ + USB_INTERFACE_INFO(USB_CLASS_MISC, 4, 1), + .driver_info = (unsigned long) &rndis_info, diff --git a/target/linux/generic/hack-6.6/790-SFP-GE-T-ignore-TX_FAULT.patch b/target/linux/generic/hack-6.6/790-SFP-GE-T-ignore-TX_FAULT.patch new file mode 100644 index 0000000000..bb21bb39d3 --- /dev/null +++ b/target/linux/generic/hack-6.6/790-SFP-GE-T-ignore-TX_FAULT.patch @@ -0,0 +1,63 @@ +From 7cc39a6bedbd85f3ff7e16845f310e4ce8d9833f Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 6 Sep 2022 00:31:19 +0100 +Subject: [PATCH] net: sfp: add quirk for ATS SFP-GE-T 1000Base-TX module +To: netdev@vger.kernel.org, + linux-kernel@vger.kernel.org, + Russell King , + Andrew Lunn , + Heiner Kallweit +Cc: David S. Miller , + Eric Dumazet , + Jakub Kicinski , + Paolo Abeni , + Josef Schlehofer + +This copper module comes with broken TX_FAULT indicator which must be +ignored for it to work. Implement ignoring TX_FAULT state bit also +during reset/insertion and mute the warning telling the user that the +module indicates TX_FAULT. + +Co-authored-by: Josef Schlehofer +Signed-off-by: Daniel Golle +--- + drivers/net/phy/sfp.c | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -409,6 +409,9 @@ static const struct sfp_quirk sfp_quirks + SFP_QUIRK("HUAWEI", "MA5671A", sfp_quirk_2500basex, + sfp_fixup_ignore_tx_fault), + ++ // OEM SFP-GE-T is 1000Base-T module ++ SFP_QUIRK_F("OEM", "SFP-GE-T", sfp_fixup_ignore_tx_fault), ++ + // Lantech 8330-262D-E can operate at 2500base-X, but incorrectly report + // 2500MBd NRZ in their EEPROM + SFP_QUIRK_M("Lantech", "8330-262D-E", sfp_quirk_2500basex), +@@ -2344,7 +2347,8 @@ static void sfp_sm_main(struct sfp *sfp, + * or t_start_up, so assume there is a fault. + */ + sfp_sm_fault(sfp, SFP_S_INIT_TX_FAULT, +- sfp->sm_fault_retries == N_FAULT_INIT); ++ !sfp->tx_fault_ignore && ++ (sfp->sm_fault_retries == N_FAULT_INIT)); + } else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) { + init_done: + /* Create mdiobus and start trying for PHY */ +@@ -2578,10 +2582,12 @@ static void sfp_check_state(struct sfp * + mutex_lock(&sfp->st_mutex); + state = sfp_get_state(sfp); + changed = state ^ sfp->state; +- if (sfp->tx_fault_ignore) ++ if (sfp->tx_fault_ignore) { + changed &= SFP_F_PRESENT | SFP_F_LOS; +- else ++ state &= ~SFP_F_TX_FAULT; ++ } else { + changed &= SFP_F_PRESENT | SFP_F_LOS | SFP_F_TX_FAULT; ++ } + + for (i = 0; i < GPIO_MAX; i++) + if (changed & BIT(i)) diff --git a/target/linux/generic/hack-6.6/800-GPIO-add-named-gpio-exports.patch b/target/linux/generic/hack-6.6/800-GPIO-add-named-gpio-exports.patch new file mode 100644 index 0000000000..b60b428dc8 --- /dev/null +++ b/target/linux/generic/hack-6.6/800-GPIO-add-named-gpio-exports.patch @@ -0,0 +1,190 @@ +From cc809a441d8f2924f785eb863dfa6aef47a25b0b Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 12 Aug 2014 20:49:27 +0200 +Subject: [PATCH 30/36] GPIO: add named gpio exports + +Signed-off-by: John Crispin +--- a/drivers/gpio/gpiolib-of.c ++++ b/drivers/gpio/gpiolib-of.c +@@ -19,6 +19,8 @@ + #include + #include + #include ++#include ++#include + + #include "gpiolib.h" + #include "gpiolib-of.h" +@@ -1030,3 +1032,100 @@ void of_gpio_dev_init(struct gpio_chip * + else + gc->of_node = gdev->dev.of_node; + } ++ ++#ifdef CONFIG_GPIO_SYSFS ++ ++static struct of_device_id gpio_export_ids[] = { ++ { .compatible = "gpio-export" }, ++ { /* sentinel */ } ++}; ++ ++static int of_gpio_export_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct device_node *cnp; ++ u32 val; ++ int nb = 0; ++ ++ for_each_child_of_node(np, cnp) { ++ const char *name = NULL; ++ int gpio; ++ bool dmc; ++ int max_gpio = 1; ++ int i; ++ ++ of_property_read_string(cnp, "gpio-export,name", &name); ++ ++ if (!name) ++ max_gpio = of_gpio_count(cnp); ++ ++ for (i = 0; i < max_gpio; i++) { ++ struct gpio_desc *desc; ++ unsigned flags = 0; ++ enum of_gpio_flags of_flags; ++ ++ gpio = of_get_gpio_flags(cnp, i, &of_flags); ++ if (!gpio_is_valid(gpio)) ++ return gpio; ++ ++ if (of_flags & OF_GPIO_ACTIVE_LOW) ++ flags |= GPIOF_ACTIVE_LOW; ++ ++ if (!of_property_read_u32(cnp, "gpio-export,output", &val)) { ++ if (of_flags & OF_GPIO_SINGLE_ENDED) { ++ /* ++ * As gpiod_direction_output_raw() is used, we ++ * need to emulate open drain or open source here. ++ */ ++ if (of_flags & OF_GPIO_OPEN_DRAIN) { ++ flags |= GPIOF_OPEN_DRAIN; ++ flags |= val ? GPIOF_IN : GPIOF_OUT_INIT_LOW; ++ } else { ++ flags |= GPIOF_OPEN_SOURCE; ++ flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_IN; ++ } ++ } else { ++ flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; ++ } ++ } else { ++ flags |= GPIOF_IN; ++ } ++ ++ if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np))) ++ continue; ++ ++ /* ++ * When emulating open-source or open-drain functionalities by not ++ * actively driving the line (setting mode to input) we still need to ++ * set the IS_OUT flag or otherwise we won't be able to set the line ++ * value anymore. ++ */ ++ if ((flags & GPIOF_IN) && ++ ((flags & GPIOF_OPEN_DRAIN) || (flags & GPIOF_OPEN_SOURCE))) { ++ desc = gpio_to_desc(gpio); ++ set_bit(FLAG_IS_OUT, &desc->flags); ++ } ++ ++ dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change"); ++ gpio_export_with_name(gpio, dmc, name); ++ nb++; ++ } ++ } ++ ++ dev_info(&pdev->dev, "%d gpio(s) exported\n", nb); ++ ++ return 0; ++} ++ ++static struct platform_driver gpio_export_driver = { ++ .driver = { ++ .name = "gpio-export", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(gpio_export_ids), ++ }, ++ .probe = of_gpio_export_probe, ++}; ++ ++module_platform_driver(gpio_export_driver); ++ ++#endif +--- a/include/asm-generic/gpio.h ++++ b/include/asm-generic/gpio.h +@@ -125,6 +125,12 @@ static inline int gpio_export(unsigned g + return gpiod_export(gpio_to_desc(gpio), direction_may_change); + } + ++int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name); ++static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change, const char *name) ++{ ++ return __gpiod_export(gpio_to_desc(gpio), direction_may_change, name); ++} ++ + static inline int gpio_export_link(struct device *dev, const char *name, + unsigned gpio) + { +--- a/include/linux/gpio/consumer.h ++++ b/include/linux/gpio/consumer.h +@@ -715,6 +715,7 @@ static inline struct gpio_desc *acpi_get + + #if IS_ENABLED(CONFIG_GPIOLIB) && IS_ENABLED(CONFIG_GPIO_SYSFS) + ++int _gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name); + int gpiod_export(struct gpio_desc *desc, bool direction_may_change); + int gpiod_export_link(struct device *dev, const char *name, + struct gpio_desc *desc); +@@ -722,6 +723,13 @@ void gpiod_unexport(struct gpio_desc *de + + #else /* CONFIG_GPIOLIB && CONFIG_GPIO_SYSFS */ + ++static inline int _gpiod_export(struct gpio_desc *desc, ++ bool direction_may_change, ++ const char *name) ++{ ++ return -ENOSYS; ++} ++ + static inline int gpiod_export(struct gpio_desc *desc, + bool direction_may_change) + { +--- a/drivers/gpio/gpiolib-sysfs.c ++++ b/drivers/gpio/gpiolib-sysfs.c +@@ -547,7 +547,7 @@ static struct class gpio_class = { + * + * Returns zero on success, else an error. + */ +-int gpiod_export(struct gpio_desc *desc, bool direction_may_change) ++int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name) + { + struct gpio_chip *chip; + struct gpio_device *gdev; +@@ -609,6 +609,8 @@ int gpiod_export(struct gpio_desc *desc, + offset = gpio_chip_hwgpio(desc); + if (chip->names && chip->names[offset]) + ioname = chip->names[offset]; ++ if (name) ++ ioname = name; + + dev = device_create_with_groups(&gpio_class, &gdev->dev, + MKDEV(0, 0), data, gpio_groups, +@@ -630,6 +632,12 @@ err_unlock: + gpiod_dbg(desc, "%s: status %d\n", __func__, status); + return status; + } ++EXPORT_SYMBOL_GPL(__gpiod_export); ++ ++int gpiod_export(struct gpio_desc *desc, bool direction_may_change) ++{ ++ return __gpiod_export(desc, direction_may_change, NULL); ++} + EXPORT_SYMBOL_GPL(gpiod_export); + + static int match_export(struct device *dev, const void *desc) diff --git a/target/linux/generic/hack-6.6/810-bcma-ssb-fallback-sprom.patch b/target/linux/generic/hack-6.6/810-bcma-ssb-fallback-sprom.patch new file mode 100644 index 0000000000..c581a512cb --- /dev/null +++ b/target/linux/generic/hack-6.6/810-bcma-ssb-fallback-sprom.patch @@ -0,0 +1,187 @@ +From e4d708702e6c98f2111e33201a264d6788564cb2 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +Date: Fri, 12 May 2023 11:08:43 +0200 +Subject: [PATCH] ssb_sprom: add generic kernel support for Broadcom Fallback SPROMs + +--- + drivers/bcma/Kconfig | 4 ++++ + drivers/bcma/Makefile | 1 + + drivers/bcma/bcma_private.h | 4 ++++ + drivers/bcma/main.c | 8 ++++++++ + drivers/bcma/sprom.c | 23 ++++++++++++++--------- + drivers/ssb/Kconfig | 5 +++++ + drivers/ssb/Makefile | 1 + + drivers/ssb/main.c | 8 ++++++++ + drivers/ssb/sprom.c | 12 +++++++++++- + drivers/ssb/ssb_private.h | 4 ++++ + 10 files changed, 60 insertions(+), 10 deletions(-) + +--- a/drivers/bcma/Kconfig ++++ b/drivers/bcma/Kconfig +@@ -18,6 +18,10 @@ config BCMA_BLOCKIO + bool + default y + ++config BCMA_FALLBACK_SPROM ++ bool ++ default y ++ + config BCMA_HOST_PCI_POSSIBLE + bool + depends on PCI = y +--- a/drivers/bcma/Makefile ++++ b/drivers/bcma/Makefile +@@ -11,6 +11,7 @@ bcma-$(CONFIG_BCMA_DRIVER_PCI_HOSTMODE) + bcma-$(CONFIG_BCMA_DRIVER_MIPS) += driver_mips.o + bcma-$(CONFIG_BCMA_DRIVER_GMAC_CMN) += driver_gmac_cmn.o + bcma-$(CONFIG_BCMA_DRIVER_GPIO) += driver_gpio.o ++bcma-$(CONFIG_BCMA_FALLBACK_SPROM) += fallback-sprom.o + bcma-$(CONFIG_BCMA_HOST_PCI) += host_pci.o + bcma-$(CONFIG_BCMA_HOST_SOC) += host_soc.o + obj-$(CONFIG_BCMA) += bcma.o +--- a/drivers/bcma/bcma_private.h ++++ b/drivers/bcma/bcma_private.h +@@ -38,6 +38,10 @@ int bcma_bus_resume(struct bcma_bus *bus + void bcma_detect_chip(struct bcma_bus *bus); + int bcma_bus_scan(struct bcma_bus *bus); + ++/* fallback-sprom.c */ ++int __init bcma_fbs_register(void); ++int bcma_get_fallback_sprom(struct bcma_bus *dev, struct ssb_sprom *out); ++ + /* sprom.c */ + int bcma_sprom_get(struct bcma_bus *bus); + +--- a/drivers/bcma/main.c ++++ b/drivers/bcma/main.c +@@ -668,6 +668,14 @@ static int __init bcma_modinit(void) + { + int err; + ++#ifdef CONFIG_BCMA_FALLBACK_SPROM ++ err = bcma_fbs_register(); ++ if (err) { ++ pr_err("Fallback SPROM initialization failed\n"); ++ err = 0; ++ } ++#endif /* CONFIG_BCMA_FALLBACK_SPROM */ ++ + err = bcma_init_bus_register(); + if (err) + return err; +--- a/drivers/bcma/sprom.c ++++ b/drivers/bcma/sprom.c +@@ -51,21 +51,26 @@ static int bcma_fill_sprom_with_fallback + { + int err; + +- if (!get_fallback_sprom) { ++ if (get_fallback_sprom) ++ err = get_fallback_sprom(bus, out); ++ ++#ifdef CONFIG_BCMA_FALLBACK_SPROM ++ if (!get_fallback_sprom || err) ++ err = bcma_get_fallback_sprom(bus, out); ++#else ++ if (!get_fallback_sprom) + err = -ENOENT; +- goto fail; +- } ++#endif /* CONFIG_BCMA_FALLBACK_SPROM */ + +- err = get_fallback_sprom(bus, out); +- if (err) +- goto fail; ++ if (err) { ++ bcma_warn(bus, "Using fallback SPROM failed (err %d)\n", err); ++ return err; ++ } + + bcma_debug(bus, "Using SPROM revision %d provided by platform.\n", + bus->sprom.revision); ++ + return 0; +-fail: +- bcma_warn(bus, "Using fallback SPROM failed (err %d)\n", err); +- return err; + } + + /************************************************** +--- a/drivers/ssb/Kconfig ++++ b/drivers/ssb/Kconfig +@@ -25,6 +25,11 @@ if SSB + config SSB_SPROM + bool + ++config SSB_FALLBACK_SPROM ++ bool ++ depends on SSB_PCIHOST ++ default y ++ + # Support for Block-I/O. SELECT this from the driver that needs it. + config SSB_BLOCKIO + bool +--- a/drivers/ssb/Makefile ++++ b/drivers/ssb/Makefile +@@ -2,6 +2,7 @@ + # core + ssb-y += main.o scan.o + ssb-$(CONFIG_SSB_EMBEDDED) += embedded.o ++ssb-$(CONFIG_SSB_FALLBACK_SPROM) += fallback-sprom.o + ssb-$(CONFIG_SSB_SPROM) += sprom.o + + # host support +--- a/drivers/ssb/main.c ++++ b/drivers/ssb/main.c +@@ -1287,6 +1287,14 @@ static int __init ssb_modinit(void) + { + int err; + ++#ifdef CONFIG_SSB_FALLBACK_SPROM ++ err = ssb_fbs_register(); ++ if (err) { ++ pr_err("Fallback SPROM initialization failed\n"); ++ err = 0; ++ } ++#endif /* CONFIG_SSB_FALLBACK_SPROM */ ++ + /* See the comment at the ssb_is_early_boot definition */ + ssb_is_early_boot = 0; + err = bus_register(&ssb_bustype); +--- a/drivers/ssb/sprom.c ++++ b/drivers/ssb/sprom.c +@@ -180,10 +180,20 @@ int ssb_arch_register_fallback_sprom(int + + int ssb_fill_sprom_with_fallback(struct ssb_bus *bus, struct ssb_sprom *out) + { ++ int err; ++ ++ if (get_fallback_sprom) ++ err = get_fallback_sprom(bus, out); ++ ++#ifdef CONFIG_SSB_FALLBACK_SPROM ++ if (!get_fallback_sprom || err) ++ err = ssb_get_fallback_sprom(bus, out); ++#else + if (!get_fallback_sprom) + return -ENOENT; ++#endif /* CONFIG_SSB_FALLBACK_SPROM */ + +- return get_fallback_sprom(bus, out); ++ return err; + } + + /* https://bcm-v4.sipsolutions.net/802.11/IsSpromAvailable */ +--- a/drivers/ssb/ssb_private.h ++++ b/drivers/ssb/ssb_private.h +@@ -143,6 +143,10 @@ extern int ssb_bus_scan(struct ssb_bus * + extern void ssb_iounmap(struct ssb_bus *ssb); + + ++/* fallback-sprom.c */ ++int __init ssb_fbs_register(void); ++int ssb_get_fallback_sprom(struct ssb_bus *dev, struct ssb_sprom *out); ++ + /* sprom.c */ + extern + ssize_t ssb_attr_sprom_show(struct ssb_bus *bus, char *buf, diff --git a/target/linux/generic/hack-6.6/901-debloat_sock_diag.patch b/target/linux/generic/hack-6.6/901-debloat_sock_diag.patch new file mode 100644 index 0000000000..ec71f9af4b --- /dev/null +++ b/target/linux/generic/hack-6.6/901-debloat_sock_diag.patch @@ -0,0 +1,174 @@ +From 3b6115d6b57a263bdc8c9b1df273bd4a7955eead Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Sat, 8 Jul 2017 08:16:31 +0200 +Subject: debloat: add some debloat patches, strip down procfs and make O_DIRECT support optional, saves ~15K after lzma on MIPS + +Signed-off-by: Felix Fietkau +--- + net/Kconfig | 3 +++ + net/core/Makefile | 3 ++- + net/core/sock.c | 2 ++ + net/ipv4/Kconfig | 1 + + net/netlink/Kconfig | 1 + + net/packet/Kconfig | 1 + + net/unix/Kconfig | 1 + + 7 files changed, 11 insertions(+), 1 deletion(-) + +--- a/net/Kconfig ++++ b/net/Kconfig +@@ -104,6 +104,9 @@ source "net/mptcp/Kconfig" + + endif # if INET + ++config SOCK_DIAG ++ bool ++ + config NETWORK_SECMARK + bool "Security Marking" + help +--- a/net/core/Makefile ++++ b/net/core/Makefile +@@ -11,11 +11,12 @@ obj-$(CONFIG_SYSCTL) += sysctl_net_core. + + obj-y += dev.o dev_addr_lists.o dst.o netevent.o \ + neighbour.o rtnetlink.o utils.o link_watch.o filter.o \ +- sock_diag.o dev_ioctl.o tso.o sock_reuseport.o \ ++ dev_ioctl.o tso.o sock_reuseport.o \ + fib_notifier.o xdp.o flow_offload.o gro.o + + obj-$(CONFIG_NETDEV_ADDR_LIST_TEST) += dev_addr_lists_test.o + ++obj-$(CONFIG_SOCK_DIAG) += sock_diag.o + obj-y += net-sysfs.o + obj-$(CONFIG_PAGE_POOL) += page_pool.o + obj-$(CONFIG_PROC_FS) += net-procfs.o +--- a/net/core/sock.c ++++ b/net/core/sock.c +@@ -115,6 +115,7 @@ + #include + #include + #include ++#include + + #include + +@@ -146,6 +147,7 @@ + + static DEFINE_MUTEX(proto_list_mutex); + static LIST_HEAD(proto_list); ++DEFINE_COOKIE(sock_cookie); + + static void sock_def_write_space_wfree(struct sock *sk); + static void sock_def_write_space(struct sock *sk); +@@ -585,6 +587,18 @@ discard_and_relse: + } + EXPORT_SYMBOL(__sk_receive_skb); + ++u64 __sock_gen_cookie(struct sock *sk) ++{ ++ while (1) { ++ u64 res = atomic64_read(&sk->sk_cookie); ++ ++ if (res) ++ return res; ++ res = gen_cookie_next(&sock_cookie); ++ atomic64_cmpxchg(&sk->sk_cookie, 0, res); ++ } ++} ++ + INDIRECT_CALLABLE_DECLARE(struct dst_entry *ip6_dst_check(struct dst_entry *, + u32)); + INDIRECT_CALLABLE_DECLARE(struct dst_entry *ipv4_dst_check(struct dst_entry *, +@@ -2188,9 +2202,11 @@ static void __sk_free(struct sock *sk) + if (likely(sk->sk_net_refcnt)) + sock_inuse_add(sock_net(sk), -1); + ++#ifdef CONFIG_SOCK_DIAG + if (unlikely(sk->sk_net_refcnt && sock_diag_has_destroy_listeners(sk))) + sock_diag_broadcast_destroy(sk); + else ++#endif + sk_destruct(sk); + } + +--- a/net/core/sock_diag.c ++++ b/net/core/sock_diag.c +@@ -12,7 +12,6 @@ + #include + #include + #include +-#include + #include + #include + +@@ -21,20 +20,6 @@ static int (*inet_rcv_compat)(struct sk_ + static DEFINE_MUTEX(sock_diag_table_mutex); + static struct workqueue_struct *broadcast_wq; + +-DEFINE_COOKIE(sock_cookie); +- +-u64 __sock_gen_cookie(struct sock *sk) +-{ +- while (1) { +- u64 res = atomic64_read(&sk->sk_cookie); +- +- if (res) +- return res; +- res = gen_cookie_next(&sock_cookie); +- atomic64_cmpxchg(&sk->sk_cookie, 0, res); +- } +-} +- + int sock_diag_check_cookie(struct sock *sk, const __u32 *cookie) + { + u64 res; +--- a/net/ipv4/Kconfig ++++ b/net/ipv4/Kconfig +@@ -423,6 +423,7 @@ config INET_TUNNEL + + config INET_DIAG + tristate "INET: socket monitoring interface" ++ select SOCK_DIAG + default y + help + Support for INET (TCP, DCCP, etc) socket monitoring interface used by +--- a/net/netlink/Kconfig ++++ b/net/netlink/Kconfig +@@ -5,6 +5,7 @@ + + config NETLINK_DIAG + tristate "NETLINK: socket monitoring interface" ++ select SOCK_DIAG + default n + help + Support for NETLINK socket monitoring interface used by the ss tool. +--- a/net/packet/Kconfig ++++ b/net/packet/Kconfig +@@ -19,6 +19,7 @@ config PACKET + config PACKET_DIAG + tristate "Packet: sockets monitoring interface" + depends on PACKET ++ select SOCK_DIAG + default n + help + Support for PF_PACKET sockets monitoring interface used by the ss tool. +--- a/net/unix/Kconfig ++++ b/net/unix/Kconfig +@@ -33,6 +33,7 @@ config AF_UNIX_OOB + config UNIX_DIAG + tristate "UNIX: socket monitoring interface" + depends on UNIX ++ select SOCK_DIAG + default n + help + Support for UNIX socket monitoring interface used by the ss tool. +--- a/net/xdp/Kconfig ++++ b/net/xdp/Kconfig +@@ -10,6 +10,7 @@ config XDP_SOCKETS + config XDP_SOCKETS_DIAG + tristate "XDP sockets: monitoring interface" + depends on XDP_SOCKETS ++ select SOCK_DIAG + default n + help + Support for PF_XDP sockets monitoring interface used by the ss tool. diff --git a/target/linux/generic/hack-6.6/902-debloat_proc.patch b/target/linux/generic/hack-6.6/902-debloat_proc.patch new file mode 100644 index 0000000000..a90169efc9 --- /dev/null +++ b/target/linux/generic/hack-6.6/902-debloat_proc.patch @@ -0,0 +1,419 @@ +From 9e3f1d0805b2d919904dd9a4ff0d956314cc3cba Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Sat, 8 Jul 2017 08:20:09 +0200 +Subject: debloat: procfs + +Signed-off-by: Felix Fietkau +--- + fs/locks.c | 2 ++ + fs/proc/Kconfig | 5 +++++ + fs/proc/consoles.c | 3 +++ + fs/proc/proc_tty.c | 11 ++++++++++- + include/net/snmp.h | 18 +++++++++++++++++- + ipc/msg.c | 3 +++ + ipc/sem.c | 2 ++ + ipc/shm.c | 2 ++ + ipc/util.c | 3 +++ + kernel/exec_domain.c | 2 ++ + kernel/irq/proc.c | 9 +++++++++ + kernel/time/timer_list.c | 2 ++ + mm/vmalloc.c | 2 ++ + mm/vmstat.c | 8 +++++--- + net/8021q/vlanproc.c | 6 ++++++ + net/core/net-procfs.c | 18 ++++++++++++------ + net/core/sock.c | 2 ++ + net/ipv4/fib_trie.c | 18 ++++++++++++------ + net/ipv4/proc.c | 3 +++ + net/ipv4/route.c | 3 +++ + 20 files changed, 105 insertions(+), 17 deletions(-) + +--- a/fs/locks.c ++++ b/fs/locks.c +@@ -2909,6 +2909,8 @@ static const struct seq_operations locks + + static int __init proc_locks_init(void) + { ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return 0; + proc_create_seq_private("locks", 0, NULL, &locks_seq_operations, + sizeof(struct locks_iterator), NULL); + return 0; +--- a/fs/proc/Kconfig ++++ b/fs/proc/Kconfig +@@ -101,6 +101,11 @@ config PROC_CHILDREN + Say Y if you are running any user-space software which takes benefit from + this interface. For example, rkt is such a piece of software. + ++config PROC_STRIPPED ++ default n ++ depends on EXPERT ++ bool "Strip non-essential /proc functionality to reduce code size" ++ + config PROC_PID_ARCH_STATUS + def_bool n + depends on PROC_FS +--- a/fs/proc/consoles.c ++++ b/fs/proc/consoles.c +@@ -92,6 +92,9 @@ static const struct seq_operations conso + + static int __init proc_consoles_init(void) + { ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return 0; ++ + proc_create_seq("consoles", 0, NULL, &consoles_op); + return 0; + } +--- a/fs/proc/proc_tty.c ++++ b/fs/proc/proc_tty.c +@@ -131,7 +131,10 @@ static const struct seq_operations tty_d + void proc_tty_register_driver(struct tty_driver *driver) + { + struct proc_dir_entry *ent; +- ++ ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return; ++ + if (!driver->driver_name || driver->proc_entry || + !driver->ops->proc_show) + return; +@@ -148,6 +151,9 @@ void proc_tty_unregister_driver(struct t + { + struct proc_dir_entry *ent; + ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return; ++ + ent = driver->proc_entry; + if (!ent) + return; +@@ -162,6 +168,9 @@ void proc_tty_unregister_driver(struct t + */ + void __init proc_tty_init(void) + { ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return; ++ + if (!proc_mkdir("tty", NULL)) + return; + proc_mkdir("tty/ldisc", NULL); /* Preserved: it's userspace visible */ +--- a/include/net/snmp.h ++++ b/include/net/snmp.h +@@ -124,6 +124,21 @@ struct linux_tls_mib { + #define DECLARE_SNMP_STAT(type, name) \ + extern __typeof__(type) __percpu *name + ++#ifdef CONFIG_PROC_STRIPPED ++#define __SNMP_STATS_DUMMY(mib) \ ++ do { (void) mib->mibs[0]; } while(0) ++ ++#define __SNMP_INC_STATS(mib, field) __SNMP_STATS_DUMMY(mib) ++#define SNMP_INC_STATS_ATOMIC_LONG(mib, field) __SNMP_STATS_DUMMY(mib) ++#define SNMP_INC_STATS(mib, field) __SNMP_STATS_DUMMY(mib) ++#define SNMP_DEC_STATS(mib, field) __SNMP_STATS_DUMMY(mib) ++#define __SNMP_ADD_STATS(mib, field, addend) __SNMP_STATS_DUMMY(mib) ++#define SNMP_ADD_STATS(mib, field, addend) __SNMP_STATS_DUMMY(mib) ++#define SNMP_UPD_PO_STATS(mib, basefield, addend) __SNMP_STATS_DUMMY(mib) ++#define __SNMP_UPD_PO_STATS(mib, basefield, addend) __SNMP_STATS_DUMMY(mib) ++ ++#else ++ + #define __SNMP_INC_STATS(mib, field) \ + __this_cpu_inc(mib->mibs[field]) + +@@ -154,8 +169,9 @@ struct linux_tls_mib { + __this_cpu_add(ptr[basefield##OCTETS], addend); \ + } while (0) + ++#endif + +-#if BITS_PER_LONG==32 ++#if (BITS_PER_LONG==32) && !defined(CONFIG_PROC_STRIPPED) + + #define __SNMP_ADD_STATS64(mib, field, addend) \ + do { \ +--- a/ipc/msg.c ++++ b/ipc/msg.c +@@ -1370,6 +1370,9 @@ void __init msg_init(void) + { + msg_init_ns(&init_ipc_ns); + ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return; ++ + ipc_init_proc_interface("sysvipc/msg", + " key msqid perms cbytes qnum lspid lrpid uid gid cuid cgid stime rtime ctime\n", + IPC_MSG_IDS, sysvipc_msg_proc_show); +--- a/ipc/sem.c ++++ b/ipc/sem.c +@@ -268,6 +268,8 @@ void sem_exit_ns(struct ipc_namespace *n + void __init sem_init(void) + { + sem_init_ns(&init_ipc_ns); ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return; + ipc_init_proc_interface("sysvipc/sem", + " key semid perms nsems uid gid cuid cgid otime ctime\n", + IPC_SEM_IDS, sysvipc_sem_proc_show); +--- a/ipc/shm.c ++++ b/ipc/shm.c +@@ -154,6 +154,8 @@ pure_initcall(ipc_ns_init); + + void __init shm_init(void) + { ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return; + ipc_init_proc_interface("sysvipc/shm", + #if BITS_PER_LONG <= 32 + " key shmid perms size cpid lpid nattch uid gid cuid cgid atime dtime ctime rss swap\n", +--- a/ipc/util.c ++++ b/ipc/util.c +@@ -141,6 +141,9 @@ void __init ipc_init_proc_interface(cons + struct proc_dir_entry *pde; + struct ipc_proc_iface *iface; + ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return; ++ + iface = kmalloc(sizeof(*iface), GFP_KERNEL); + if (!iface) + return; +--- a/kernel/exec_domain.c ++++ b/kernel/exec_domain.c +@@ -29,6 +29,8 @@ static int execdomains_proc_show(struct + + static int __init proc_execdomains_init(void) + { ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return 0; + proc_create_single("execdomains", 0, NULL, execdomains_proc_show); + return 0; + } +--- a/kernel/irq/proc.c ++++ b/kernel/irq/proc.c +@@ -341,6 +341,9 @@ void register_irq_proc(unsigned int irq, + void __maybe_unused *irqp = (void *)(unsigned long) irq; + char name [MAX_NAMELEN]; + ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED) && !IS_ENABLED(CONFIG_SMP)) ++ return; ++ + if (!root_irq_dir || (desc->irq_data.chip == &no_irq_chip)) + return; + +@@ -394,6 +397,9 @@ void unregister_irq_proc(unsigned int ir + { + char name [MAX_NAMELEN]; + ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED) && !IS_ENABLED(CONFIG_SMP)) ++ return; ++ + if (!root_irq_dir || !desc->dir) + return; + #ifdef CONFIG_SMP +@@ -432,6 +438,9 @@ void init_irq_proc(void) + unsigned int irq; + struct irq_desc *desc; + ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED) && !IS_ENABLED(CONFIG_SMP)) ++ return; ++ + /* create /proc/irq */ + root_irq_dir = proc_mkdir("irq", NULL); + if (!root_irq_dir) +--- a/kernel/time/timer_list.c ++++ b/kernel/time/timer_list.c +@@ -350,6 +350,8 @@ static int __init init_timer_list_procfs + { + struct proc_dir_entry *pe; + ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return 0; + pe = proc_create_seq_private("timer_list", 0400, NULL, &timer_list_sops, + sizeof(struct timer_list_iter), NULL); + if (!pe) +--- a/mm/vmalloc.c ++++ b/mm/vmalloc.c +@@ -4205,6 +4205,8 @@ static const struct seq_operations vmall + + static int __init proc_vmalloc_init(void) + { ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return 0; + if (IS_ENABLED(CONFIG_NUMA)) + proc_create_seq_private("vmallocinfo", 0400, NULL, + &vmalloc_op, +--- a/mm/vmstat.c ++++ b/mm/vmstat.c +@@ -2109,10 +2109,12 @@ void __init init_mm_internals(void) + start_shepherd_timer(); + #endif + #ifdef CONFIG_PROC_FS +- proc_create_seq("buddyinfo", 0444, NULL, &fragmentation_op); +- proc_create_seq("pagetypeinfo", 0400, NULL, &pagetypeinfo_op); ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED)) { ++ proc_create_seq("buddyinfo", 0444, NULL, &fragmentation_op); ++ proc_create_seq("pagetypeinfo", 0400, NULL, &pagetypeinfo_op); ++ proc_create_seq("zoneinfo", 0444, NULL, &zoneinfo_op); ++ } + proc_create_seq("vmstat", 0444, NULL, &vmstat_op); +- proc_create_seq("zoneinfo", 0444, NULL, &zoneinfo_op); + #endif + } + +--- a/net/8021q/vlanproc.c ++++ b/net/8021q/vlanproc.c +@@ -93,6 +93,9 @@ void vlan_proc_cleanup(struct net *net) + { + struct vlan_net *vn = net_generic(net, vlan_net_id); + ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return; ++ + if (vn->proc_vlan_conf) + remove_proc_entry(name_conf, vn->proc_vlan_dir); + +@@ -112,6 +115,9 @@ int __net_init vlan_proc_init(struct net + { + struct vlan_net *vn = net_generic(net, vlan_net_id); + ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return 0; ++ + vn->proc_vlan_dir = proc_net_mkdir(net, name_root, net->proc_net); + if (!vn->proc_vlan_dir) + goto err; +--- a/net/core/net-procfs.c ++++ b/net/core/net-procfs.c +@@ -319,10 +319,12 @@ static int __net_init dev_proc_net_init( + if (!proc_create_net("dev", 0444, net->proc_net, &dev_seq_ops, + sizeof(struct seq_net_private))) + goto out; +- if (!proc_create_seq("softnet_stat", 0444, net->proc_net, ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED) && ++ !proc_create_seq("softnet_stat", 0444, net->proc_net, + &softnet_seq_ops)) + goto out_dev; +- if (!proc_create_net("ptype", 0444, net->proc_net, &ptype_seq_ops, ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED) && ++ !proc_create_net("ptype", 0444, net->proc_net, &ptype_seq_ops, + sizeof(struct seq_net_private))) + goto out_softnet; + +@@ -332,9 +334,11 @@ static int __net_init dev_proc_net_init( + out: + return rc; + out_ptype: +- remove_proc_entry("ptype", net->proc_net); ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ remove_proc_entry("ptype", net->proc_net); + out_softnet: +- remove_proc_entry("softnet_stat", net->proc_net); ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ remove_proc_entry("softnet_stat", net->proc_net); + out_dev: + remove_proc_entry("dev", net->proc_net); + goto out; +@@ -344,8 +348,10 @@ static void __net_exit dev_proc_net_exit + { + wext_proc_exit(net); + +- remove_proc_entry("ptype", net->proc_net); +- remove_proc_entry("softnet_stat", net->proc_net); ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED)) { ++ remove_proc_entry("ptype", net->proc_net); ++ remove_proc_entry("softnet_stat", net->proc_net); ++ } + remove_proc_entry("dev", net->proc_net); + } + +--- a/net/core/sock.c ++++ b/net/core/sock.c +@@ -4114,6 +4114,8 @@ static __net_initdata struct pernet_oper + + static int __init proto_init(void) + { ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return 0; + return register_pernet_subsys(&proto_net_ops); + } + +--- a/net/ipv4/fib_trie.c ++++ b/net/ipv4/fib_trie.c +@@ -3036,11 +3036,13 @@ static const struct seq_operations fib_r + + int __net_init fib_proc_init(struct net *net) + { +- if (!proc_create_net("fib_trie", 0444, net->proc_net, &fib_trie_seq_ops, ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED) && ++ !proc_create_net("fib_trie", 0444, net->proc_net, &fib_trie_seq_ops, + sizeof(struct fib_trie_iter))) + goto out1; + +- if (!proc_create_net_single("fib_triestat", 0444, net->proc_net, ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED) && ++ !proc_create_net_single("fib_triestat", 0444, net->proc_net, + fib_triestat_seq_show, NULL)) + goto out2; + +@@ -3051,17 +3053,21 @@ int __net_init fib_proc_init(struct net + return 0; + + out3: +- remove_proc_entry("fib_triestat", net->proc_net); ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ remove_proc_entry("fib_triestat", net->proc_net); + out2: +- remove_proc_entry("fib_trie", net->proc_net); ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ remove_proc_entry("fib_trie", net->proc_net); + out1: + return -ENOMEM; + } + + void __net_exit fib_proc_exit(struct net *net) + { +- remove_proc_entry("fib_trie", net->proc_net); +- remove_proc_entry("fib_triestat", net->proc_net); ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED)) { ++ remove_proc_entry("fib_trie", net->proc_net); ++ remove_proc_entry("fib_triestat", net->proc_net); ++ } + remove_proc_entry("route", net->proc_net); + } + +--- a/net/ipv4/proc.c ++++ b/net/ipv4/proc.c +@@ -553,5 +553,8 @@ static __net_initdata struct pernet_oper + + int __init ip_misc_proc_init(void) + { ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return 0; ++ + return register_pernet_subsys(&ip_proc_ops); + } +--- a/net/ipv4/route.c ++++ b/net/ipv4/route.c +@@ -380,6 +380,9 @@ static struct pernet_operations ip_rt_pr + + static int __init ip_rt_proc_init(void) + { ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return 0; ++ + return register_pernet_subsys(&ip_rt_proc_ops); + } + +--- a/net/ipv4/inet_timewait_sock.c ++++ b/net/ipv4/inet_timewait_sock.c +@@ -269,7 +269,7 @@ void __inet_twsk_schedule(struct inet_ti + */ + + if (!rearm) { +- bool kill = timeo <= 4*HZ; ++ bool __maybe_unused kill = timeo <= 4*HZ; + + __NET_INC_STATS(twsk_net(tw), kill ? LINUX_MIB_TIMEWAITKILLED : + LINUX_MIB_TIMEWAITED); diff --git a/target/linux/generic/hack-6.6/904-debloat_dma_buf.patch b/target/linux/generic/hack-6.6/904-debloat_dma_buf.patch new file mode 100644 index 0000000000..105eb3da4b --- /dev/null +++ b/target/linux/generic/hack-6.6/904-debloat_dma_buf.patch @@ -0,0 +1,93 @@ +From e3692cb2fcd5ba1244512a0f43b8118f65f1c375 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Sat, 8 Jul 2017 08:20:43 +0200 +Subject: debloat: dmabuf + +Signed-off-by: Felix Fietkau +--- + drivers/base/Kconfig | 2 +- + drivers/dma-buf/Makefile | 10 +++++++--- + drivers/dma-buf/dma-buf.c | 4 +++- + kernel/sched/core.c | 1 + + 4 files changed, 12 insertions(+), 5 deletions(-) + +--- a/drivers/base/Kconfig ++++ b/drivers/base/Kconfig +@@ -198,7 +198,7 @@ config SOC_BUS + source "drivers/base/regmap/Kconfig" + + config DMA_SHARED_BUFFER +- bool ++ tristate + default n + select IRQ_WORK + help +--- a/drivers/dma-buf/heaps/Makefile ++++ b/drivers/dma-buf/heaps/Makefile +@@ -1,3 +1,3 @@ + # SPDX-License-Identifier: GPL-2.0 +-obj-$(CONFIG_DMABUF_HEAPS_SYSTEM) += system_heap.o +-obj-$(CONFIG_DMABUF_HEAPS_CMA) += cma_heap.o ++dma-buf-objs-$(CONFIG_DMABUF_HEAPS_SYSTEM) += system_heap.o ++dma-buf-objs-$(CONFIG_DMABUF_HEAPS_CMA) += cma_heap.o +--- a/drivers/dma-buf/Makefile ++++ b/drivers/dma-buf/Makefile +@@ -1,12 +1,14 @@ + # SPDX-License-Identifier: GPL-2.0-only +-obj-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \ ++obj-$(CONFIG_DMA_SHARED_BUFFER) := dma-shared-buffer.o ++ ++dma-buf-objs-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \ + dma-fence-unwrap.o dma-resv.o +-obj-$(CONFIG_DMABUF_HEAPS) += dma-heap.o +-obj-$(CONFIG_DMABUF_HEAPS) += heaps/ +-obj-$(CONFIG_SYNC_FILE) += sync_file.o +-obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o +-obj-$(CONFIG_UDMABUF) += udmabuf.o +-obj-$(CONFIG_DMABUF_SYSFS_STATS) += dma-buf-sysfs-stats.o ++dma-buf-objs-$(CONFIG_DMABUF_HEAPS) += dma-heap.o ++obj-$(CONFIG_DMABUF_HEAPS) += heaps/ ++dma-buf-objs-$(CONFIG_SYNC_FILE) += sync_file.o ++dma-buf-objs-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o ++dma-buf-objs-$(CONFIG_UDMABUF) += udmabuf.o ++dma-buf-objs-$(CONFIG_DMABUF_SYSFS_STATS) += dma-buf-sysfs-stats.o + + dmabuf_selftests-y := \ + selftest.o \ +@@ -15,4 +17,6 @@ dmabuf_selftests-y := \ + st-dma-fence-unwrap.o \ + st-dma-resv.o + +-obj-$(CONFIG_DMABUF_SELFTESTS) += dmabuf_selftests.o ++dma-buf-objs-$(CONFIG_DMABUF_SELFTESTS) += dmabuf_selftests.o ++ ++dma-shared-buffer-objs := $(dma-buf-objs-y) +--- a/drivers/dma-buf/dma-buf.c ++++ b/drivers/dma-buf/dma-buf.c +@@ -1636,4 +1636,5 @@ static void __exit dma_buf_deinit(void) + kern_unmount(dma_buf_mnt); + dma_buf_uninit_sysfs_statistics(); + } +-__exitcall(dma_buf_deinit); ++module_exit(dma_buf_deinit); ++MODULE_LICENSE("GPL"); +--- a/kernel/sched/core.c ++++ b/kernel/sched/core.c +@@ -4367,6 +4367,7 @@ int wake_up_state(struct task_struct *p, + { + return try_to_wake_up(p, state, 0); + } ++EXPORT_SYMBOL_GPL(wake_up_state); + + /* + * Perform scheduler related setup for a newly forked process p. +--- a/fs/d_path.c ++++ b/fs/d_path.c +@@ -313,6 +313,7 @@ char *dynamic_dname(char *buffer, int bu + buffer += buflen - sz; + return memcpy(buffer, temp, sz); + } ++EXPORT_SYMBOL_GPL(dynamic_dname); + + char *simple_dname(struct dentry *dentry, char *buffer, int buflen) + { diff --git a/target/linux/generic/hack-6.6/910-kobject_uevent.patch b/target/linux/generic/hack-6.6/910-kobject_uevent.patch new file mode 100644 index 0000000000..c4c41ca400 --- /dev/null +++ b/target/linux/generic/hack-6.6/910-kobject_uevent.patch @@ -0,0 +1,32 @@ +From 0d37e6edc09c99e683dd91ca0e83bbc0df8477b3 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Sun, 16 Jul 2017 16:56:10 +0200 +Subject: lib: add uevent_next_seqnum() + +Signed-off-by: Felix Fietkau +--- + include/linux/kobject.h | 5 +++++ + lib/kobject_uevent.c | 37 +++++++++++++++++++++++++++++++++++++ + 2 files changed, 42 insertions(+) + +--- a/lib/kobject_uevent.c ++++ b/lib/kobject_uevent.c +@@ -179,6 +179,18 @@ out: + return r; + } + ++u64 uevent_next_seqnum(void) ++{ ++ u64 seq; ++ ++ mutex_lock(&uevent_sock_mutex); ++ seq = ++uevent_seqnum; ++ mutex_unlock(&uevent_sock_mutex); ++ ++ return seq; ++} ++EXPORT_SYMBOL_GPL(uevent_next_seqnum); ++ + /** + * kobject_synth_uevent - send synthetic uevent with arguments + * diff --git a/target/linux/generic/hack-6.6/911-kobject_add_broadcast_uevent.patch b/target/linux/generic/hack-6.6/911-kobject_add_broadcast_uevent.patch new file mode 100644 index 0000000000..9854585d25 --- /dev/null +++ b/target/linux/generic/hack-6.6/911-kobject_add_broadcast_uevent.patch @@ -0,0 +1,76 @@ +From 0d37e6edc09c99e683dd91ca0e83bbc0df8477b3 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Sun, 16 Jul 2017 16:56:10 +0200 +Subject: lib: add uevent_next_seqnum() + +Signed-off-by: Felix Fietkau +--- + include/linux/kobject.h | 5 +++++ + lib/kobject_uevent.c | 37 +++++++++++++++++++++++++++++++++++++ + 2 files changed, 42 insertions(+) + +--- a/include/linux/kobject.h ++++ b/include/linux/kobject.h +@@ -32,6 +32,8 @@ + #define UEVENT_NUM_ENVP 64 /* number of env pointers */ + #define UEVENT_BUFFER_SIZE 2048 /* buffer for the variables */ + ++struct sk_buff; ++ + #ifdef CONFIG_UEVENT_HELPER + /* path to the userspace helper executed on an event */ + extern char uevent_helper[]; +@@ -224,4 +226,7 @@ int kobject_synth_uevent(struct kobject + __printf(2, 3) + int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...); + ++int broadcast_uevent(struct sk_buff *skb, __u32 pid, __u32 group, ++ gfp_t allocation); ++ + #endif /* _KOBJECT_H_ */ +--- a/lib/kobject_uevent.c ++++ b/lib/kobject_uevent.c +@@ -691,6 +691,43 @@ int add_uevent_var(struct kobj_uevent_en + EXPORT_SYMBOL_GPL(add_uevent_var); + + #if defined(CONFIG_NET) ++int broadcast_uevent(struct sk_buff *skb, __u32 pid, __u32 group, ++ gfp_t allocation) ++{ ++ struct uevent_sock *ue_sk; ++ int err = 0; ++ ++ /* send netlink message */ ++ mutex_lock(&uevent_sock_mutex); ++ list_for_each_entry(ue_sk, &uevent_sock_list, list) { ++ struct sock *uevent_sock = ue_sk->sk; ++ struct sk_buff *skb2; ++ ++ skb2 = skb_clone(skb, allocation); ++ if (!skb2) ++ break; ++ ++ err = netlink_broadcast(uevent_sock, skb2, pid, group, ++ allocation); ++ if (err) ++ break; ++ } ++ mutex_unlock(&uevent_sock_mutex); ++ ++ kfree_skb(skb); ++ return err; ++} ++#else ++int broadcast_uevent(struct sk_buff *skb, __u32 pid, __u32 group, ++ gfp_t allocation) ++{ ++ kfree_skb(skb); ++ return 0; ++} ++#endif ++EXPORT_SYMBOL_GPL(broadcast_uevent); ++ ++#if defined(CONFIG_NET) + static int uevent_net_broadcast(struct sock *usk, struct sk_buff *skb, + struct netlink_ext_ack *extack) + { diff --git a/target/linux/generic/hack-6.6/920-device_tree_cmdline.patch b/target/linux/generic/hack-6.6/920-device_tree_cmdline.patch new file mode 100644 index 0000000000..cc384bb553 --- /dev/null +++ b/target/linux/generic/hack-6.6/920-device_tree_cmdline.patch @@ -0,0 +1,21 @@ +From e08bcbbaa52fcc41f02743fd2e62a33255ce52da Mon Sep 17 00:00:00 2001 +From: OpenWrt community +Date: Wed, 13 Jul 2022 13:52:28 +0200 +Subject: [PATCH] of/ftd: add device tree cmdline + +--- + drivers/of/fdt.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/of/fdt.c ++++ b/drivers/of/fdt.c +@@ -1183,6 +1183,9 @@ int __init early_init_dt_scan_chosen(cha + p = of_get_flat_dt_prop(node, "bootargs", &l); + if (p != NULL && l > 0) + strscpy(cmdline, p, min(l, COMMAND_LINE_SIZE)); ++ p = of_get_flat_dt_prop(node, "bootargs-append", &l); ++ if (p != NULL && l > 0) ++ strlcat(cmdline, p, min_t(int, strlen(cmdline) + (int)l, COMMAND_LINE_SIZE)); + + handle_cmdline: + /* diff --git a/target/linux/generic/hack-6.6/930-Revert-Revert-Revert-driver-core-Set-fw_devlink-on-b.patch b/target/linux/generic/hack-6.6/930-Revert-Revert-Revert-driver-core-Set-fw_devlink-on-b.patch new file mode 100644 index 0000000000..04aaab7adf --- /dev/null +++ b/target/linux/generic/hack-6.6/930-Revert-Revert-Revert-driver-core-Set-fw_devlink-on-b.patch @@ -0,0 +1,30 @@ +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Tue, 19 Jul 2022 06:17:48 +0200 +Subject: [PATCH] Revert "Revert "Revert "driver core: Set fw_devlink=on by + default""" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This reverts commit ea718c699055c8566eb64432388a04974c43b2ea. + +With of_platform_populate() called for MTD partitions that commit breaks +probing devices which reference MTD in device tree. + +Link: https://lore.kernel.org/all/696cb2da-20b9-b3dd-46d9-de4bf91a1506@gmail.com/T/#u +Signed-off-by: Rafał Miłecki +--- + drivers/base/core.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/base/core.c ++++ b/drivers/base/core.c +@@ -1702,7 +1702,7 @@ static void device_links_purge(struct de + #define FW_DEVLINK_FLAGS_RPM (FW_DEVLINK_FLAGS_ON | \ + DL_FLAG_PM_RUNTIME) + +-static u32 fw_devlink_flags = FW_DEVLINK_FLAGS_ON; ++static u32 fw_devlink_flags = FW_DEVLINK_FLAGS_PERMISSIVE; + static int __init fw_devlink_setup(char *arg) + { + if (!arg) diff --git a/target/linux/generic/pending-6.6/100-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch b/target/linux/generic/pending-6.6/100-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch new file mode 100644 index 0000000000..7b342e6962 --- /dev/null +++ b/target/linux/generic/pending-6.6/100-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch @@ -0,0 +1,29 @@ +From: Felix Fietkau +Date: Thu, 22 Oct 2020 22:00:03 +0200 +Subject: [PATCH] compiler.h: only include asm/rwonce.h for kernel code + +This header file is not in uapi, which makes any user space code that includes +linux/compiler.h to fail with the error 'asm/rwonce.h: No such file or directory' + +Fixes: e506ea451254 ("compiler.h: Split {READ,WRITE}_ONCE definitions out into rwonce.h") +Signed-off-by: Felix Fietkau +--- + +--- a/include/linux/compiler.h ++++ b/include/linux/compiler.h +@@ -203,6 +203,8 @@ void ftrace_likely_update(struct ftrace_ + __v; \ + }) + ++#include ++ + #endif /* __KERNEL__ */ + + /* +@@ -243,6 +245,4 @@ static inline void *offset_to_ptr(const + */ + #define prevent_tail_call_optimization() mb() + +-#include +- + #endif /* __LINUX_COMPILER_H */ diff --git a/target/linux/generic/pending-6.6/102-MIPS-only-process-negative-stack-offsets-on-stack-tr.patch b/target/linux/generic/pending-6.6/102-MIPS-only-process-negative-stack-offsets-on-stack-tr.patch new file mode 100644 index 0000000000..d79d03defb --- /dev/null +++ b/target/linux/generic/pending-6.6/102-MIPS-only-process-negative-stack-offsets-on-stack-tr.patch @@ -0,0 +1,57 @@ +From: Felix Fietkau +Date: Wed, 18 Apr 2018 10:50:05 +0200 +Subject: [PATCH] MIPS: only process negative stack offsets on stack traces + +Fixes endless back traces in cases where the compiler emits a stack +pointer increase in a branch delay slot (probably for some form of +function return). + +[ 3.475442] BUG: MAX_STACK_TRACE_ENTRIES too low! +[ 3.480070] turning off the locking correctness validator. +[ 3.485521] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.14.34 #0 +[ 3.491475] Stack : 00000000 00000000 00000000 00000000 80e0fce2 00000034 00000000 00000000 +[ 3.499764] 87c3838c 80696377 8061047c 00000000 00000001 00000001 87c2d850 6534689f +[ 3.508059] 00000000 00000000 80e10000 00000000 00000000 000000cf 0000000f 00000000 +[ 3.516353] 00000000 806a0000 00076891 00000000 00000000 00000000 ffffffff 00000000 +[ 3.524648] 806c0000 00000004 80e10000 806a0000 00000003 80690000 00000000 80700000 +[ 3.532942] ... +[ 3.535362] Call Trace: +[ 3.537818] [<80010a48>] show_stack+0x58/0x100 +[ 3.542207] [<804c2f78>] dump_stack+0xe8/0x170 +[ 3.546613] [<80079f90>] save_trace+0xf0/0x110 +[ 3.551010] [<8007b1ec>] mark_lock+0x33c/0x78c +[ 3.555413] [<8007bf48>] __lock_acquire+0x2ac/0x1a08 +[ 3.560337] [<8007de60>] lock_acquire+0x64/0x8c +[ 3.564846] [<804e1570>] _raw_spin_lock_irqsave+0x54/0x78 +[ 3.570186] [<801b618c>] kernfs_notify+0x94/0xac +[ 3.574770] [<801b7b10>] sysfs_notify+0x74/0xa0 +[ 3.579257] [<801b618c>] kernfs_notify+0x94/0xac +[ 3.583839] [<801b7b10>] sysfs_notify+0x74/0xa0 +[ 3.588329] [<801b618c>] kernfs_notify+0x94/0xac +[ 3.592911] [<801b7b10>] sysfs_notify+0x74/0xa0 +[ 3.597401] [<801b618c>] kernfs_notify+0x94/0xac +[ 3.601983] [<801b7b10>] sysfs_notify+0x74/0xa0 +[ 3.606473] [<801b618c>] kernfs_notify+0x94/0xac +[ 3.611055] [<801b7b10>] sysfs_notify+0x74/0xa0 +[ 3.615545] [<801b618c>] kernfs_notify+0x94/0xac +[ 3.620125] [<801b7b10>] sysfs_notify+0x74/0xa0 +[ 3.624619] [<801b618c>] kernfs_notify+0x94/0xac +[ 3.629197] [<801b7b10>] sysfs_notify+0x74/0xa0 +[ 3.633691] [<801b618c>] kernfs_notify+0x94/0xac +[ 3.638269] [<801b7b10>] sysfs_notify+0x74/0xa0 +[ 3.642763] [<801b618c>] kernfs_notify+0x94/0xac + +Signed-off-by: Felix Fietkau +--- + +--- a/arch/mips/kernel/process.c ++++ b/arch/mips/kernel/process.c +@@ -395,6 +395,8 @@ static inline int is_sp_move_ins(union m + + if (ip->i_format.opcode == addiu_op || + ip->i_format.opcode == daddiu_op) { ++ if (ip->i_format.simmediate > 0) ++ return 0; + *frame_size = -ip->i_format.simmediate; + return 1; + } diff --git a/target/linux/generic/pending-6.6/103-kbuild-export-SUBARCH.patch b/target/linux/generic/pending-6.6/103-kbuild-export-SUBARCH.patch new file mode 100644 index 0000000000..c8c0efaecf --- /dev/null +++ b/target/linux/generic/pending-6.6/103-kbuild-export-SUBARCH.patch @@ -0,0 +1,21 @@ +From 173019b66dcc9d68ad9333aa744dad1e369b5aa8 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Sun, 9 Jul 2017 00:26:53 +0200 +Subject: [PATCH 34/34] kernel: add compile fix for linux 4.9 on x86 + +Signed-off-by: Felix Fietkau +--- + Makefile | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/Makefile ++++ b/Makefile +@@ -605,7 +605,7 @@ endif + # Allows the usage of unstable features in stable compilers. + export RUSTC_BOOTSTRAP := 1 + +-export ARCH SRCARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC HOSTPKG_CONFIG ++export ARCH SRCARCH SUBARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC HOSTPKG_CONFIG + export RUSTC RUSTDOC RUSTFMT RUSTC_OR_CLIPPY_QUIET RUSTC_OR_CLIPPY BINDGEN CARGO + export HOSTRUSTC KBUILD_HOSTRUSTFLAGS + export CPP AR NM STRIP OBJCOPY OBJDUMP READELF PAHOLE RESOLVE_BTFIDS LEX YACC AWK INSTALLKERNEL diff --git a/target/linux/generic/pending-6.6/110-v6.3-0001-spidev-Add-Silicon-Labs-EM3581-device-compatible.patch b/target/linux/generic/pending-6.6/110-v6.3-0001-spidev-Add-Silicon-Labs-EM3581-device-compatible.patch new file mode 100644 index 0000000000..ebeeae2f8f --- /dev/null +++ b/target/linux/generic/pending-6.6/110-v6.3-0001-spidev-Add-Silicon-Labs-EM3581-device-compatible.patch @@ -0,0 +1,32 @@ +From f7982c726e02001afc19052fe48f642dfcbc00b2 Mon Sep 17 00:00:00 2001 +From: Vincent Tremblay +Date: Mon, 26 Dec 2022 21:10:37 -0500 +Subject: [PATCH 1/2] spidev: Add Silicon Labs EM3581 device compatible + +Add compatible string for Silicon Labs EM3581 device. + +Note: This patch is adapted from a patch submitted to the for-next branch (v6.3). + +Signed-off-by: Vincent Tremblay +--- + drivers/spi/spidev.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/spi/spidev.c ++++ b/drivers/spi/spidev.c +@@ -700,6 +700,7 @@ static const struct spi_device_id spidev + { .name = "m53cpld" }, + { .name = "spi-petra" }, + { .name = "spi-authenta" }, ++ { .name = "em3581" }, + {}, + }; + MODULE_DEVICE_TABLE(spi, spidev_spi_ids); +@@ -726,6 +727,7 @@ static const struct of_device_id spidev_ + { .compatible = "menlo,m53cpld", .data = &spidev_of_check }, + { .compatible = "cisco,spi-petra", .data = &spidev_of_check }, + { .compatible = "micron,spi-authenta", .data = &spidev_of_check }, ++ { .compatible = "silabs,em3581", .data = &spidev_of_check }, + {}, + }; + MODULE_DEVICE_TABLE(of, spidev_dt_ids); diff --git a/target/linux/generic/pending-6.6/110-v6.3-0002-spidev-Add-Silicon-Labs-SI3210-device-compatible.patch b/target/linux/generic/pending-6.6/110-v6.3-0002-spidev-Add-Silicon-Labs-SI3210-device-compatible.patch new file mode 100644 index 0000000000..db5b5800f4 --- /dev/null +++ b/target/linux/generic/pending-6.6/110-v6.3-0002-spidev-Add-Silicon-Labs-SI3210-device-compatible.patch @@ -0,0 +1,32 @@ +From 536581825219e97fa2ae0c4de35605d2f6311416 Mon Sep 17 00:00:00 2001 +From: Vincent Tremblay +Date: Tue, 27 Dec 2022 09:00:58 -0500 +Subject: [PATCH 2/2] spidev: Add Silicon Labs SI3210 device compatible + +Add compatible string for Silicon Labs SI3210 device. + +Note: This patch is adapted from a patch submitted to the for-next branch (v6.3). + +Signed-off-by: Vincent Tremblay +--- + drivers/spi/spidev.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/spi/spidev.c ++++ b/drivers/spi/spidev.c +@@ -701,6 +701,7 @@ static const struct spi_device_id spidev + { .name = "spi-petra" }, + { .name = "spi-authenta" }, + { .name = "em3581" }, ++ { .name = "si3210" }, + {}, + }; + MODULE_DEVICE_TABLE(spi, spidev_spi_ids); +@@ -728,6 +729,7 @@ static const struct of_device_id spidev_ + { .compatible = "cisco,spi-petra", .data = &spidev_of_check }, + { .compatible = "micron,spi-authenta", .data = &spidev_of_check }, + { .compatible = "silabs,em3581", .data = &spidev_of_check }, ++ { .compatible = "silabs,si3210", .data = &spidev_of_check }, + {}, + }; + MODULE_DEVICE_TABLE(of, spidev_dt_ids); diff --git a/target/linux/generic/pending-6.6/111-watchdog-max63xx_wdt-Add-support-for-specifying-WDI-.patch b/target/linux/generic/pending-6.6/111-watchdog-max63xx_wdt-Add-support-for-specifying-WDI-.patch new file mode 100644 index 0000000000..d6b10491f8 --- /dev/null +++ b/target/linux/generic/pending-6.6/111-watchdog-max63xx_wdt-Add-support-for-specifying-WDI-.patch @@ -0,0 +1,75 @@ +From bd1b9f66d5134e518419f4c4dacf1884c1616983 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= +Date: Thu, 28 Apr 2022 11:13:23 +0200 +Subject: [PATCH] watchdog: max63xx_wdt: Add support for specifying WDI logic + via GPIO +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +On some boards is WDI logic of max6370 chip connected via GPIO. +So extend max63xx_wdt driver to allow specifying WDI logic via GPIO. + +Signed-off-by: Pali Rohár +--- + drivers/watchdog/max63xx_wdt.c | 24 ++++++++++++++++++++++++ + 1 file changed, 24 insertions(+) + +--- a/drivers/watchdog/max63xx_wdt.c ++++ b/drivers/watchdog/max63xx_wdt.c +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + + #define DEFAULT_HEARTBEAT 60 + #define MAX_HEARTBEAT 60 +@@ -53,6 +54,9 @@ struct max63xx_wdt { + void __iomem *base; + spinlock_t lock; + ++ /* GPIOs */ ++ struct gpio_desc *gpio_wdi; ++ + /* WDI and WSET bits write access routines */ + void (*ping)(struct max63xx_wdt *wdt); + void (*set)(struct max63xx_wdt *wdt, u8 set); +@@ -158,6 +162,17 @@ static const struct watchdog_info max63x + .identity = "max63xx Watchdog", + }; + ++static void max63xx_gpio_ping(struct max63xx_wdt *wdt) ++{ ++ spin_lock(&wdt->lock); ++ ++ gpiod_set_value(wdt->gpio_wdi, 1); ++ udelay(1); ++ gpiod_set_value(wdt->gpio_wdi, 0); ++ ++ spin_unlock(&wdt->lock); ++} ++ + static void max63xx_mmap_ping(struct max63xx_wdt *wdt) + { + u8 val; +@@ -225,10 +240,19 @@ static int max63xx_wdt_probe(struct plat + return -EINVAL; + } + ++ wdt->gpio_wdi = devm_gpiod_get(dev, NULL, GPIOD_FLAGS_BIT_DIR_OUT); ++ if (IS_ERR(wdt->gpio_wdi) && PTR_ERR(wdt->gpio_wdi) != -ENOENT) ++ return dev_err_probe(dev, PTR_ERR(wdt->gpio_wdi), ++ "unable to request gpio: %ld\n", ++ PTR_ERR(wdt->gpio_wdi)); ++ + err = max63xx_mmap_init(pdev, wdt); + if (err) + return err; + ++ if (!IS_ERR(wdt->gpio_wdi)) ++ wdt->ping = max63xx_gpio_ping; ++ + platform_set_drvdata(pdev, &wdt->wdd); + watchdog_set_drvdata(&wdt->wdd, wdt); + diff --git a/target/linux/generic/pending-6.6/120-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch b/target/linux/generic/pending-6.6/120-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch new file mode 100644 index 0000000000..4bf473f9a7 --- /dev/null +++ b/target/linux/generic/pending-6.6/120-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch @@ -0,0 +1,82 @@ +From: Tobias Wolf +Subject: mm: Fix alloc_node_mem_map with ARCH_PFN_OFFSET calculation + +An rt288x (ralink) based router (Belkin F5D8235 v1) does not boot with any +kernel beyond version 4.3 resulting in: + +BUG: Bad page state in process swapper pfn:086ac + +bisect resulted in: + +a1c34a3bf00af2cede839879502e12dc68491ad5 is the first bad commit +commit a1c34a3bf00af2cede839879502e12dc68491ad5 +Author: Laura Abbott +Date: Thu Nov 5 18:48:46 2015 -0800 + + mm: Don't offset memmap for flatmem + + Srinivas Kandagatla reported bad page messages when trying to remove the + bottom 2MB on an ARM based IFC6410 board + + BUG: Bad page state in process swapper pfn:fffa8 + page:ef7fb500 count:0 mapcount:0 mapping: (null) index:0x0 + flags: 0x96640253(locked|error|dirty|active|arch_1|reclaim|mlocked) + page dumped because: PAGE_FLAGS_CHECK_AT_FREE flag(s) set + bad because of flags: + flags: 0x200041(locked|active|mlocked) + Modules linked in: + CPU: 0 PID: 0 Comm: swapper Not tainted 3.19.0-rc3-00007-g412f9ba-dirty +#816 + Hardware name: Qualcomm (Flattened Device Tree) + unwind_backtrace + show_stack + dump_stack + bad_page + free_pages_prepare + free_hot_cold_page + __free_pages + free_highmem_page + mem_init + start_kernel + Disabling lock debugging due to kernel taint + [...] +:040000 040000 2de013c372345fd471cd58f0553c9b38b0ef1cc4 +0a8156f848733dfa21e16c196dfb6c0a76290709 M mm + +This fix for ARM does not account ARCH_PFN_OFFSET for mem_map as later used by +page_to_pfn anymore. + +The following output was generated with two hacked in printk statements: + +printk("before %p vs. %p or %p\n", mem_map, mem_map - offset, mem_map - +(pgdat->node_start_pfn - ARCH_PFN_OFFSET)); + if (page_to_pfn(mem_map) != pgdat->node_start_pfn) + mem_map -= offset + (pgdat->node_start_pfn - ARCH_PFN_OFFSET); +printk("after %p\n", mem_map); + +Output: + +[ 0.000000] before 8861b280 vs. 8861b280 or 8851b280 +[ 0.000000] after 8851b280 + +As seen in the first line mem_map with subtraction of offset does not equal the +mem_map after subtraction of ARCH_PFN_OFFSET. + +After adding the offset of ARCH_PFN_OFFSET as well to mem_map as the +previously calculated offset is zero for the named platform it is able to boot +4.4 and 4.9-rc7 again. + +Signed-off-by: Tobias Wolf +--- + +--- a/mm/page_alloc.c ++++ b/mm/page_alloc.c +@@ -7897,7 +7897,7 @@ static void __init alloc_node_mem_map(st + if (pgdat == NODE_DATA(0)) { + mem_map = NODE_DATA(0)->node_mem_map; + if (page_to_pfn(mem_map) != pgdat->node_start_pfn) +- mem_map -= offset; ++ mem_map -= offset + (pgdat->node_start_pfn - ARCH_PFN_OFFSET); + } + #endif + } diff --git a/target/linux/generic/pending-6.6/140-jffs2-use-.rename2-and-add-RENAME_WHITEOUT-support.patch b/target/linux/generic/pending-6.6/140-jffs2-use-.rename2-and-add-RENAME_WHITEOUT-support.patch new file mode 100644 index 0000000000..8f40ae3ba2 --- /dev/null +++ b/target/linux/generic/pending-6.6/140-jffs2-use-.rename2-and-add-RENAME_WHITEOUT-support.patch @@ -0,0 +1,81 @@ +From: Felix Fietkau +Subject: jffs2: use .rename2 and add RENAME_WHITEOUT support + +It is required for renames on overlayfs + +Signed-off-by: Felix Fietkau +--- + +--- a/fs/jffs2/dir.c ++++ b/fs/jffs2/dir.c +@@ -614,8 +614,8 @@ static int jffs2_rmdir (struct inode *di + return ret; + } + +-static int jffs2_mknod (struct user_namespace *mnt_userns, struct inode *dir_i, +- struct dentry *dentry, umode_t mode, dev_t rdev) ++static int __jffs2_mknod (struct user_namespace *mnt_userns, struct inode *dir_i, ++ struct dentry *dentry, umode_t mode, dev_t rdev, bool whiteout) + { + struct jffs2_inode_info *f, *dir_f; + struct jffs2_sb_info *c; +@@ -754,7 +754,11 @@ static int jffs2_mknod (struct user_name + mutex_unlock(&dir_f->sem); + jffs2_complete_reservation(c); + +- d_instantiate_new(dentry, inode); ++ if (!whiteout) ++ d_instantiate_new(dentry, inode); ++ else ++ unlock_new_inode(inode); ++ + return 0; + + fail: +@@ -762,6 +766,19 @@ static int jffs2_mknod (struct user_name + return ret; + } + ++static int jffs2_mknod (struct user_namespace *mnt_userns, struct inode *dir_i, ++ struct dentry *dentry, umode_t mode, dev_t rdev) ++{ ++ return __jffs2_mknod(mnt_userns, dir_i, dentry, mode, rdev, false); ++} ++ ++static int jffs2_whiteout (struct user_namespace *mnt_userns, struct inode *old_dir, ++ struct dentry *old_dentry) ++{ ++ return __jffs2_mknod(mnt_userns, old_dir, old_dentry, S_IFCHR | WHITEOUT_MODE, ++ WHITEOUT_DEV, true); ++} ++ + static int jffs2_rename (struct user_namespace *mnt_userns, + struct inode *old_dir_i, struct dentry *old_dentry, + struct inode *new_dir_i, struct dentry *new_dentry, +@@ -773,7 +790,7 @@ static int jffs2_rename (struct user_nam + uint8_t type; + uint32_t now; + +- if (flags & ~RENAME_NOREPLACE) ++ if (flags & ~(RENAME_NOREPLACE|RENAME_WHITEOUT)) + return -EINVAL; + + /* The VFS will check for us and prevent trying to rename a +@@ -839,9 +856,14 @@ static int jffs2_rename (struct user_nam + if (d_is_dir(old_dentry) && !victim_f) + inc_nlink(new_dir_i); + +- /* Unlink the original */ +- ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i), +- old_dentry->d_name.name, old_dentry->d_name.len, NULL, now); ++ if (flags & RENAME_WHITEOUT) ++ /* Replace with whiteout */ ++ ret = jffs2_whiteout(mnt_userns, old_dir_i, old_dentry); ++ else ++ /* Unlink the original */ ++ ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i), ++ old_dentry->d_name.name, ++ old_dentry->d_name.len, NULL, now); + + /* We don't touch inode->i_nlink */ + diff --git a/target/linux/generic/pending-6.6/141-jffs2-add-RENAME_EXCHANGE-support.patch b/target/linux/generic/pending-6.6/141-jffs2-add-RENAME_EXCHANGE-support.patch new file mode 100644 index 0000000000..f58fc791d2 --- /dev/null +++ b/target/linux/generic/pending-6.6/141-jffs2-add-RENAME_EXCHANGE-support.patch @@ -0,0 +1,73 @@ +From: Felix Fietkau +Subject: jffs2: add RENAME_EXCHANGE support + +Signed-off-by: Felix Fietkau +--- + +--- a/fs/jffs2/dir.c ++++ b/fs/jffs2/dir.c +@@ -787,18 +787,31 @@ static int jffs2_rename (struct user_nam + int ret; + struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb); + struct jffs2_inode_info *victim_f = NULL; ++ struct inode *fst_inode = d_inode(old_dentry); ++ struct inode *snd_inode = d_inode(new_dentry); + uint8_t type; + uint32_t now; + +- if (flags & ~(RENAME_NOREPLACE|RENAME_WHITEOUT)) ++ if (flags & ~(RENAME_NOREPLACE|RENAME_WHITEOUT|RENAME_EXCHANGE)) + return -EINVAL; + ++ if ((flags & RENAME_EXCHANGE) && (old_dir_i != new_dir_i)) { ++ if (S_ISDIR(fst_inode->i_mode) && !S_ISDIR(snd_inode->i_mode)) { ++ inc_nlink(new_dir_i); ++ drop_nlink(old_dir_i); ++ } ++ else if (!S_ISDIR(fst_inode->i_mode) && S_ISDIR(snd_inode->i_mode)) { ++ drop_nlink(new_dir_i); ++ inc_nlink(old_dir_i); ++ } ++ } ++ + /* The VFS will check for us and prevent trying to rename a + * file over a directory and vice versa, but if it's a directory, + * the VFS can't check whether the victim is empty. The filesystem + * needs to do that for itself. + */ +- if (d_really_is_positive(new_dentry)) { ++ if (d_really_is_positive(new_dentry) && !(flags & RENAME_EXCHANGE)) { + victim_f = JFFS2_INODE_INFO(d_inode(new_dentry)); + if (d_is_dir(new_dentry)) { + struct jffs2_full_dirent *fd; +@@ -833,7 +846,7 @@ static int jffs2_rename (struct user_nam + if (ret) + return ret; + +- if (victim_f) { ++ if (victim_f && !(flags & RENAME_EXCHANGE)) { + /* There was a victim. Kill it off nicely */ + if (d_is_dir(new_dentry)) + clear_nlink(d_inode(new_dentry)); +@@ -859,6 +872,12 @@ static int jffs2_rename (struct user_nam + if (flags & RENAME_WHITEOUT) + /* Replace with whiteout */ + ret = jffs2_whiteout(mnt_userns, old_dir_i, old_dentry); ++ else if (flags & RENAME_EXCHANGE) ++ /* Replace the original */ ++ ret = jffs2_do_link(c, JFFS2_INODE_INFO(old_dir_i), ++ d_inode(new_dentry)->i_ino, type, ++ old_dentry->d_name.name, old_dentry->d_name.len, ++ now); + else + /* Unlink the original */ + ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i), +@@ -890,7 +909,7 @@ static int jffs2_rename (struct user_nam + return ret; + } + +- if (d_is_dir(old_dentry)) ++ if (d_is_dir(old_dentry) && !(flags & RENAME_EXCHANGE)) + drop_nlink(old_dir_i); + + new_dir_i->i_mtime = new_dir_i->i_ctime = old_dir_i->i_mtime = old_dir_i->i_ctime = ITIME(now); diff --git a/target/linux/generic/pending-6.6/142-jffs2-add-splice-ops.patch b/target/linux/generic/pending-6.6/142-jffs2-add-splice-ops.patch new file mode 100644 index 0000000000..de847a1f5c --- /dev/null +++ b/target/linux/generic/pending-6.6/142-jffs2-add-splice-ops.patch @@ -0,0 +1,20 @@ +From: Felix Fietkau +Subject: jffs2: add splice ops + +Add splice_read using generic_file_splice_read. +Add splice_write using iter_file_splice_write + +Signed-off-by: Felix Fietkau +--- + +--- a/fs/jffs2/file.c ++++ b/fs/jffs2/file.c +@@ -53,6 +53,8 @@ const struct file_operations jffs2_file_ + .open = generic_file_open, + .read_iter = generic_file_read_iter, + .write_iter = generic_file_write_iter, ++ .splice_read = generic_file_splice_read, ++ .splice_write = iter_file_splice_write, + .unlocked_ioctl=jffs2_ioctl, + .mmap = generic_file_readonly_mmap, + .fsync = jffs2_fsync, diff --git a/target/linux/generic/pending-6.6/150-bridge_allow_receiption_on_disabled_port.patch b/target/linux/generic/pending-6.6/150-bridge_allow_receiption_on_disabled_port.patch new file mode 100644 index 0000000000..93a2d146b5 --- /dev/null +++ b/target/linux/generic/pending-6.6/150-bridge_allow_receiption_on_disabled_port.patch @@ -0,0 +1,45 @@ +From: Stephen Hemminger +Subject: bridge: allow receiption on disabled port + +When an ethernet device is enslaved to a bridge, and the bridge STP +detects loss of carrier (or operational state down), then normally +packet receiption is blocked. + +This breaks control applications like WPA which maybe expecting to +receive packets to negotiate to bring link up. The bridge needs to +block forwarding packets from these disabled ports, but there is no +hard requirement to not allow local packet delivery. + +Signed-off-by: Stephen Hemminger +Signed-off-by: Felix Fietkau + +--- a/net/bridge/br_input.c ++++ b/net/bridge/br_input.c +@@ -222,6 +222,9 @@ static void __br_handle_local_finish(str + /* note: already called with rcu_read_lock */ + static int br_handle_local_finish(struct net *net, struct sock *sk, struct sk_buff *skb) + { ++ struct net_bridge_port *p = br_port_get_rcu(skb->dev); ++ ++ if (p->state != BR_STATE_DISABLED) + __br_handle_local_finish(skb); + + /* return 1 to signal the okfn() was called so it's ok to use the skb */ +@@ -390,6 +393,17 @@ forward: + goto defer_stp_filtering; + + switch (p->state) { ++ case BR_STATE_DISABLED: ++ if (ether_addr_equal(p->br->dev->dev_addr, dest)) ++ skb->pkt_type = PACKET_HOST; ++ ++ if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, ++ dev_net(skb->dev), NULL, skb, skb->dev, NULL, ++ br_handle_local_finish) == 1) { ++ return RX_HANDLER_PASS; ++ } ++ break; ++ + case BR_STATE_FORWARDING: + case BR_STATE_LEARNING: + defer_stp_filtering: diff --git a/target/linux/generic/pending-6.6/151-net-bridge-do-not-send-arp-replies-if-src-and-target.patch b/target/linux/generic/pending-6.6/151-net-bridge-do-not-send-arp-replies-if-src-and-target.patch new file mode 100644 index 0000000000..f420d210c2 --- /dev/null +++ b/target/linux/generic/pending-6.6/151-net-bridge-do-not-send-arp-replies-if-src-and-target.patch @@ -0,0 +1,37 @@ +From: Felix Fietkau +Date: Thu, 4 Jan 2024 15:21:21 +0100 +Subject: [PATCH] net: bridge: do not send arp replies if src and target hw + addr is the same + +There are broken devices in the wild that handle duplicate IP address +detection by sending out ARP requests for the IP that they received from a +DHCP server and refuse the address if they get a reply. +When proxyarp is enabled, they would go into a loop of requesting an address +and then NAKing it again. + +Link: https://github.com/openwrt/openwrt/issues/14309 +Signed-off-by: Felix Fietkau +--- + +--- a/net/bridge/br_arp_nd_proxy.c ++++ b/net/bridge/br_arp_nd_proxy.c +@@ -204,7 +204,10 @@ void br_do_proxy_suppress_arp(struct sk_ + if ((p && (p->flags & BR_PROXYARP)) || + (f->dst && (f->dst->flags & (BR_PROXYARP_WIFI | + BR_NEIGH_SUPPRESS)))) { +- if (!vid) ++ replied = true; ++ if (!memcmp(n->ha, sha, dev->addr_len)) ++ replied = false; ++ else if (!vid) + br_arp_send(br, p, skb->dev, sip, tip, + sha, n->ha, sha, 0, 0); + else +@@ -212,7 +215,6 @@ void br_do_proxy_suppress_arp(struct sk_ + sha, n->ha, sha, + skb->vlan_proto, + skb_vlan_tag_get(skb)); +- replied = true; + } + + /* If we have replied or as long as we know the diff --git a/target/linux/generic/pending-6.6/190-rtc-rs5c372-support_alarms_up_to_1_week.patch b/target/linux/generic/pending-6.6/190-rtc-rs5c372-support_alarms_up_to_1_week.patch new file mode 100644 index 0000000000..2f5c2228c7 --- /dev/null +++ b/target/linux/generic/pending-6.6/190-rtc-rs5c372-support_alarms_up_to_1_week.patch @@ -0,0 +1,94 @@ +From: Daniel González Cabanelas +Subject: [PATCH 1/2] rtc: rs5c372: support alarms up to 1 week + +The Ricoh R2221x, R2223x, RS5C372, RV5C387A chips can handle 1 week +alarms. + +Read the "wday" alarm register and convert it to a date to support up 1 +week in our driver. + +Signed-off-by: Daniel González Cabanelas +--- + drivers/rtc/rtc-rs5c372.c | 48 ++++++++++++++++++++++++++++++++++----- + 1 file changed, 42 insertions(+), 6 deletions(-) + +--- a/drivers/rtc/rtc-rs5c372.c ++++ b/drivers/rtc/rtc-rs5c372.c +@@ -399,7 +399,9 @@ static int rs5c_read_alarm(struct device + { + struct i2c_client *client = to_i2c_client(dev); + struct rs5c372 *rs5c = i2c_get_clientdata(client); +- int status; ++ int status, wday_offs; ++ struct rtc_time rtc; ++ unsigned long alarm_secs; + + status = rs5c_get_regs(rs5c); + if (status < 0) +@@ -409,6 +411,30 @@ static int rs5c_read_alarm(struct device + t->time.tm_sec = 0; + t->time.tm_min = bcd2bin(rs5c->regs[RS5C_REG_ALARM_A_MIN] & 0x7f); + t->time.tm_hour = rs5c_reg2hr(rs5c, rs5c->regs[RS5C_REG_ALARM_A_HOURS]); ++ t->time.tm_wday = ffs(rs5c->regs[RS5C_REG_ALARM_A_WDAY] & 0x7f) - 1; ++ ++ /* determine the day, month and year based on alarm wday, taking as a ++ * reference the current time from the rtc ++ */ ++ status = rs5c372_rtc_read_time(dev, &rtc); ++ if (status < 0) ++ return status; ++ ++ wday_offs = t->time.tm_wday - rtc.tm_wday; ++ alarm_secs = mktime64(rtc.tm_year + 1900, ++ rtc.tm_mon + 1, ++ rtc.tm_mday + wday_offs, ++ t->time.tm_hour, ++ t->time.tm_min, ++ t->time.tm_sec); ++ ++ if (wday_offs < 0 || (wday_offs == 0 && ++ (t->time.tm_hour < rtc.tm_hour || ++ (t->time.tm_hour == rtc.tm_hour && ++ t->time.tm_min <= rtc.tm_min)))) ++ alarm_secs += 7 * 86400; ++ ++ rtc_time64_to_tm(alarm_secs, &t->time); + + /* ... and status */ + t->enabled = !!(rs5c->regs[RS5C_REG_CTRL1] & RS5C_CTRL1_AALE); +@@ -423,12 +449,20 @@ static int rs5c_set_alarm(struct device + struct rs5c372 *rs5c = i2c_get_clientdata(client); + int status, addr, i; + unsigned char buf[3]; ++ struct rtc_time rtc_tm; ++ unsigned long rtc_secs, alarm_secs; + +- /* only handle up to 24 hours in the future, like RTC_ALM_SET */ +- if (t->time.tm_mday != -1 +- || t->time.tm_mon != -1 +- || t->time.tm_year != -1) ++ /* chip only can handle alarms up to one week in the future*/ ++ status = rs5c372_rtc_read_time(dev, &rtc_tm); ++ if (status) ++ return status; ++ rtc_secs = rtc_tm_to_time64(&rtc_tm); ++ alarm_secs = rtc_tm_to_time64(&t->time); ++ if (alarm_secs >= rtc_secs + 7 * 86400) { ++ dev_err(dev, "%s: alarm maximum is one week in the future (%d)\n", ++ __func__, status); + return -EINVAL; ++ } + + /* REVISIT: round up tm_sec */ + +@@ -449,7 +483,9 @@ static int rs5c_set_alarm(struct device + /* set alarm */ + buf[0] = bin2bcd(t->time.tm_min); + buf[1] = rs5c_hr2reg(rs5c, t->time.tm_hour); +- buf[2] = 0x7f; /* any/all days */ ++ /* each bit is the day of the week, 0x7f means all days */ ++ buf[2] = (t->time.tm_wday >= 0 && t->time.tm_wday < 7) ? ++ BIT(t->time.tm_wday) : 0x7f; + + for (i = 0; i < sizeof(buf); i++) { + addr = RS5C_ADDR(RS5C_REG_ALARM_A_MIN + i); diff --git a/target/linux/generic/pending-6.6/191-rtc-rs5c372-let_the_alarm_to_be_used_as_wakeup_source.patch b/target/linux/generic/pending-6.6/191-rtc-rs5c372-let_the_alarm_to_be_used_as_wakeup_source.patch new file mode 100644 index 0000000000..a9a5cdf8ba --- /dev/null +++ b/target/linux/generic/pending-6.6/191-rtc-rs5c372-let_the_alarm_to_be_used_as_wakeup_source.patch @@ -0,0 +1,71 @@ +From: Daniel González Cabanelas +Subject: [PATCH 2/2] rtc: rs5c372: let the alarm to be used as wakeup source + +Currently there is no use for the interrupts on the rs5c372 RTC and the +wakealarm isn't enabled. There are some devices like NASes which use this +RTC to wake up from the power off state when the INTR pin is activated by +the alarm clock. + +Enable the alarm and let to be used as a wakeup source. + +Tested on a Buffalo LS421DE NAS. + +Signed-off-by: Daniel González Cabanelas +--- + drivers/rtc/rtc-rs5c372.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +--- a/drivers/rtc/rtc-rs5c372.c ++++ b/drivers/rtc/rtc-rs5c372.c +@@ -833,6 +833,7 @@ static int rs5c372_probe(struct i2c_clie + int err = 0; + int smbus_mode = 0; + struct rs5c372 *rs5c372; ++ bool rs5c372_can_wakeup_device = false; + + dev_dbg(&client->dev, "%s\n", __func__); + +@@ -868,6 +869,12 @@ static int rs5c372_probe(struct i2c_clie + else + rs5c372->type = id->driver_data; + ++#ifdef CONFIG_OF ++ if(of_property_read_bool(client->dev.of_node, ++ "wakeup-source")) ++ rs5c372_can_wakeup_device = true; ++#endif ++ + /* we read registers 0x0f then 0x00-0x0f; skip the first one */ + rs5c372->regs = &rs5c372->buf[1]; + rs5c372->smbus = smbus_mode; +@@ -901,6 +908,8 @@ static int rs5c372_probe(struct i2c_clie + goto exit; + } + ++ rs5c372->has_irq = 1; ++ + /* if the oscillator lost power and no other software (like + * the bootloader) set it up, do it here. + * +@@ -927,6 +936,10 @@ static int rs5c372_probe(struct i2c_clie + ); + + /* REVISIT use client->irq to register alarm irq ... */ ++ if (rs5c372_can_wakeup_device) { ++ device_init_wakeup(&client->dev, true); ++ } ++ + rs5c372->rtc = devm_rtc_device_register(&client->dev, + rs5c372_driver.driver.name, + &rs5c372_rtc_ops, THIS_MODULE); +@@ -940,6 +953,10 @@ static int rs5c372_probe(struct i2c_clie + if (err) + goto exit; + ++ /* the rs5c372 alarm only supports a minute accuracy */ ++ set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rs5c372->rtc->features); ++ clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rs5c372->rtc->features); ++ + return 0; + + exit: diff --git a/target/linux/generic/pending-6.6/203-kallsyms_uncompressed.patch b/target/linux/generic/pending-6.6/203-kallsyms_uncompressed.patch new file mode 100644 index 0000000000..78ed262b78 --- /dev/null +++ b/target/linux/generic/pending-6.6/203-kallsyms_uncompressed.patch @@ -0,0 +1,118 @@ +From: Felix Fietkau +Subject: kernel: add a config option for keeping the kallsyms table uncompressed, saving ~9kb kernel size after lzma on ar71xx + +[john@phrozen.org: added to my upstream queue 30.12.2016] +lede-commit: e0e3509b5ce2ccf93d4d67ea907613f5f7ec2eed +Signed-off-by: Felix Fietkau +--- + init/Kconfig | 11 +++++++++++ + kernel/kallsyms.c | 8 ++++++++ + scripts/kallsyms.c | 12 ++++++++++++ + scripts/link-vmlinux.sh | 4 ++++ + 4 files changed, 35 insertions(+) + +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -1491,6 +1491,17 @@ config SYSCTL_ARCH_UNALIGN_ALLOW + the unaligned access emulation. + see arch/parisc/kernel/unaligned.c for reference + ++config KALLSYMS_UNCOMPRESSED ++ bool "Keep kallsyms uncompressed" ++ depends on KALLSYMS ++ help ++ Normally kallsyms contains compressed symbols (using a token table), ++ reducing the uncompressed kernel image size. Keeping the symbol table ++ uncompressed significantly improves the size of this part in compressed ++ kernel images. ++ ++ Say N unless you need compressed kernel images to be small. ++ + config HAVE_PCSPKR_PLATFORM + bool + +--- a/kernel/kallsyms.c ++++ b/kernel/kallsyms.c +@@ -69,6 +69,11 @@ static unsigned int kallsyms_expand_symb + * For every byte on the compressed symbol data, copy the table + * entry for that byte. + */ ++#ifdef CONFIG_KALLSYMS_UNCOMPRESSED ++ memcpy(result, data + 1, len - 1); ++ result += len - 1; ++ len = 0; ++#endif + while (len) { + tptr = &kallsyms_token_table[kallsyms_token_index[*data]]; + data++; +@@ -101,6 +106,9 @@ tail: + */ + static char kallsyms_get_symbol_type(unsigned int off) + { ++#ifdef CONFIG_KALLSYMS_UNCOMPRESSED ++ return kallsyms_names[off + 1]; ++#endif + /* + * Get just the first code, look it up in the token table, + * and return the first char from this token. +--- a/scripts/kallsyms.c ++++ b/scripts/kallsyms.c +@@ -77,6 +77,7 @@ static struct addr_range percpu_range = + static struct sym_entry **table; + static unsigned int table_size, table_cnt; + static int all_symbols; ++static int uncompressed; + static int absolute_percpu; + static int base_relative; + static int lto_clang; +@@ -608,6 +609,9 @@ static void write_src(void) + (unsigned char)(table[i]->seq >> 0)); + printf("\n"); + ++ if (uncompressed) ++ return; ++ + output_label("kallsyms_token_table"); + off = 0; + for (i = 0; i < 256; i++) { +@@ -659,6 +663,9 @@ static unsigned char *find_token(unsigne + { + int i; + ++ if (uncompressed) ++ return NULL; ++ + for (i = 0; i < len - 1; i++) { + if (str[i] == token[0] && str[i+1] == token[1]) + return &str[i]; +@@ -731,6 +738,9 @@ static void optimize_result(void) + { + int i, best; + ++ if (uncompressed) ++ return; ++ + /* using the '\0' symbol last allows compress_symbols to use standard + * fast string functions */ + for (i = 255; i >= 0; i--) { +@@ -892,6 +902,7 @@ int main(int argc, char **argv) + {"absolute-percpu", no_argument, &absolute_percpu, 1}, + {"base-relative", no_argument, &base_relative, 1}, + {"lto-clang", no_argument, <o_clang, 1}, ++ {"uncompressed", no_argument, &uncompressed, 1}, + {}, + }; + +--- a/scripts/link-vmlinux.sh ++++ b/scripts/link-vmlinux.sh +@@ -165,6 +165,10 @@ kallsyms() + kallsymopt="${kallsymopt} --lto-clang" + fi + ++ if is_enabled CONFIG_KALLSYMS_UNCOMPRESSED; then ++ kallsymopt="${kallsymopt} --uncompressed" ++ fi ++ + info KSYMS ${2} + scripts/kallsyms ${kallsymopt} ${1} > ${2} + } diff --git a/target/linux/generic/pending-6.6/205-backtrace_module_info.patch b/target/linux/generic/pending-6.6/205-backtrace_module_info.patch new file mode 100644 index 0000000000..27a1347981 --- /dev/null +++ b/target/linux/generic/pending-6.6/205-backtrace_module_info.patch @@ -0,0 +1,41 @@ +From: Felix Fietkau +Subject: kernel: when KALLSYMS is disabled, print module address + size for matching backtrace entries + +[john@phrozen.org: felix will add this to his upstream queue] + +lede-commit 53827cdc824556cda910b23ce5030c363b8f1461 +Signed-off-by: Felix Fietkau +--- + lib/vsprintf.c | 15 +++++++++++---- + 1 file changed, 11 insertions(+), 4 deletions(-) + +--- a/lib/vsprintf.c ++++ b/lib/vsprintf.c +@@ -985,8 +985,10 @@ char *symbol_string(char *buf, char *end + struct printf_spec spec, const char *fmt) + { + unsigned long value; +-#ifdef CONFIG_KALLSYMS + char sym[KSYM_SYMBOL_LEN]; ++#ifndef CONFIG_KALLSYMS ++ struct module *mod; ++ int len; + #endif + + if (fmt[1] == 'R') +@@ -1007,8 +1009,14 @@ char *symbol_string(char *buf, char *end + + return string_nocheck(buf, end, sym, spec); + #else +- return special_hex_number(buf, end, value, sizeof(void *)); ++ len = snprintf(sym, sizeof(sym), "0x%lx", value); ++ mod = __module_address(value); ++ if (mod) ++ snprintf(sym + len, sizeof(sym) - len, " [%s@%p+0x%x]", ++ mod->name, mod->core_layout.base, ++ mod->core_layout.size); + #endif ++ return string(buf, end, sym, spec); + } + + static const struct printf_spec default_str_spec = { diff --git a/target/linux/generic/pending-6.6/240-remove-unsane-filenames-from-deps_initramfs-list.patch b/target/linux/generic/pending-6.6/240-remove-unsane-filenames-from-deps_initramfs-list.patch new file mode 100644 index 0000000000..9e78284ecf --- /dev/null +++ b/target/linux/generic/pending-6.6/240-remove-unsane-filenames-from-deps_initramfs-list.patch @@ -0,0 +1,30 @@ +From: Gabor Juhos +Subject: usr: sanitize deps_initramfs list + +If any filename in the intramfs dependency +list contains a colon, that causes a kernel +build error like this: + +/devel/openwrt/build_dir/linux-ar71xx_generic/linux-3.6.6/usr/Makefile:58: *** multiple target patterns. Stop. +make[5]: *** [usr] Error 2 + +Fix it by removing such filenames from the +deps_initramfs list. + +Signed-off-by: Gabor Juhos +Signed-off-by: Felix Fietkau +--- + usr/Makefile | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +--- a/usr/Makefile ++++ b/usr/Makefile +@@ -56,6 +56,8 @@ hostprogs := gen_init_cpio + # The dependency list is generated by gen_initramfs.sh -l + -include $(obj)/.initramfs_data.cpio.d + ++deps_initramfs := $(foreach v,$(deps_initramfs),$(if $(findstring :,$(v)),,$(v))) ++ + # do not try to update files included in initramfs + $(deps_initramfs): ; + diff --git a/target/linux/generic/pending-6.6/261-enable_wilink_platform_without_drivers.patch b/target/linux/generic/pending-6.6/261-enable_wilink_platform_without_drivers.patch new file mode 100644 index 0000000000..cd31f9d934 --- /dev/null +++ b/target/linux/generic/pending-6.6/261-enable_wilink_platform_without_drivers.patch @@ -0,0 +1,20 @@ +From: Imre Kaloz +Subject: [PATCH] hack: net: wireless: make the wl12xx glue code available with + compat-wireless, too + +Signed-off-by: Imre Kaloz +--- + drivers/net/wireless/ti/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/wireless/ti/Kconfig ++++ b/drivers/net/wireless/ti/Kconfig +@@ -20,7 +20,7 @@ source "drivers/net/wireless/ti/wlcore/K + + config WILINK_PLATFORM_DATA + bool "TI WiLink platform data" +- depends on WLCORE_SDIO || WL1251_SDIO ++ depends on WLCORE_SDIO || WL1251_SDIO || ARCH_OMAP2PLUS + default y + help + Small platform data bit needed to pass data to the sdio modules. diff --git a/target/linux/generic/pending-6.6/270-platform-mikrotik-build-bits.patch b/target/linux/generic/pending-6.6/270-platform-mikrotik-build-bits.patch new file mode 100644 index 0000000000..997e6142a7 --- /dev/null +++ b/target/linux/generic/pending-6.6/270-platform-mikrotik-build-bits.patch @@ -0,0 +1,31 @@ +From c2deb5ef01a0ef09088832744cbace9e239a6ee0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Thibaut=20VAR=C3=88NE?= +Date: Sat, 28 Mar 2020 12:11:50 +0100 +Subject: [PATCH] generic: platform/mikrotik build bits (5.4) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This patch adds platform/mikrotik kernel build bits + +Signed-off-by: Thibaut VARÈNE +--- + drivers/platform/Kconfig | 2 ++ + drivers/platform/Makefile | 1 + + 2 files changed, 3 insertions(+) + +--- a/drivers/platform/Kconfig ++++ b/drivers/platform/Kconfig +@@ -16,3 +16,5 @@ source "drivers/platform/olpc/Kconfig" + source "drivers/platform/surface/Kconfig" + + source "drivers/platform/x86/Kconfig" ++ ++source "drivers/platform/mikrotik/Kconfig" +--- a/drivers/platform/Makefile ++++ b/drivers/platform/Makefile +@@ -11,3 +11,4 @@ obj-$(CONFIG_OLPC_EC) += olpc/ + obj-$(CONFIG_GOLDFISH) += goldfish/ + obj-$(CONFIG_CHROME_PLATFORMS) += chrome/ + obj-$(CONFIG_SURFACE_PLATFORMS) += surface/ ++obj-$(CONFIG_MIKROTIK) += mikrotik/ diff --git a/target/linux/generic/pending-6.6/300-mips_expose_boot_raw.patch b/target/linux/generic/pending-6.6/300-mips_expose_boot_raw.patch new file mode 100644 index 0000000000..d222ec060e --- /dev/null +++ b/target/linux/generic/pending-6.6/300-mips_expose_boot_raw.patch @@ -0,0 +1,40 @@ +From: Mark Miller +Subject: mips: expose CONFIG_BOOT_RAW + +This exposes the CONFIG_BOOT_RAW symbol in Kconfig. This is needed on +certain Broadcom chipsets running CFE in order to load the kernel. + +Signed-off-by: Mark Miller +Acked-by: Rob Landley +--- +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -1035,9 +1035,6 @@ config FW_ARC + config ARCH_MAY_HAVE_PC_FDC + bool + +-config BOOT_RAW +- bool +- + config CEVT_BCM1480 + bool + +@@ -3093,6 +3090,18 @@ choice + bool "Extend builtin kernel arguments with bootloader arguments" + endchoice + ++config BOOT_RAW ++ bool "Enable the kernel to be executed from the load address" ++ default n ++ help ++ Allow the kernel to be executed from the load address for ++ bootloaders which cannot read the ELF format. This places ++ a jump to start_kernel at the load address. ++ ++ If unsure, say N. ++ ++ ++ + endmenu + + config LOCKDEP_SUPPORT diff --git a/target/linux/generic/pending-6.6/301-MIPS-Add-barriers-between-dcache-icache-flushes.patch b/target/linux/generic/pending-6.6/301-MIPS-Add-barriers-between-dcache-icache-flushes.patch new file mode 100644 index 0000000000..bd56adad3a --- /dev/null +++ b/target/linux/generic/pending-6.6/301-MIPS-Add-barriers-between-dcache-icache-flushes.patch @@ -0,0 +1,71 @@ +From e6e6ef4275978823ec3a84133fc91f4ffbef5c84 Mon Sep 17 00:00:00 2001 +From: Paul Burton +Date: Mon, 22 Feb 2016 18:09:44 +0000 +Subject: [PATCH] MIPS: Add barriers between dcache & icache flushes + +Index-based cache operations may be arbitrarily reordered by out of +order CPUs. Thus code which writes back the dcache & then invalidates +the icache using indexed cache ops must include a barrier between +operating on the 2 caches in order to prevent the scenario in which: + + - icache invalidation occurs. + + - icache fetch occurs, due to speculation. + + - dcache writeback occurs. + +If the above were allowed to happen then the icache would contain stale +data. Forcing the dcache writeback to complete before the icache +invalidation avoids this. + +Signed-off-by: Paul Burton +Cc: James Hogan +--- + arch/mips/mm/c-r4k.c | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +--- a/arch/mips/mm/c-r4k.c ++++ b/arch/mips/mm/c-r4k.c +@@ -514,6 +514,7 @@ static inline void local_r4k___flush_cac + + default: + r4k_blast_dcache(); ++ mb(); /* cache instructions may be reordered */ + r4k_blast_icache(); + break; + } +@@ -594,8 +595,10 @@ static inline void local_r4k_flush_cache + if (cpu_has_dc_aliases || (exec && !cpu_has_ic_fills_f_dc)) + r4k_blast_dcache(); + /* If executable, blast stale lines from icache */ +- if (exec) ++ if (exec) { ++ mb(); /* cache instructions may be reordered */ + r4k_blast_icache(); ++ } + } + + static void r4k_flush_cache_range(struct vm_area_struct *vma, +@@ -696,8 +699,13 @@ static inline void local_r4k_flush_cache + if (cpu_has_dc_aliases || (exec && !cpu_has_ic_fills_f_dc)) { + vaddr ? r4k_blast_dcache_page(addr) : + r4k_blast_dcache_user_page(addr); +- if (exec && !cpu_icache_snoops_remote_store) ++ if (exec) ++ mb(); /* cache instructions may be reordered */ ++ ++ if (exec && !cpu_icache_snoops_remote_store) { + r4k_blast_scache_page(addr); ++ mb(); /* cache instructions may be reordered */ ++ } + } + if (exec) { + if (vaddr && cpu_has_vtag_icache && mm == current->active_mm) { +@@ -764,6 +772,7 @@ static inline void __local_r4k_flush_ica + else + blast_dcache_range(start, end); + } ++ mb(); /* cache instructions may be reordered */ + } + + if (type == R4K_INDEX || diff --git a/target/linux/generic/pending-6.6/302-mips_no_branch_likely.patch b/target/linux/generic/pending-6.6/302-mips_no_branch_likely.patch new file mode 100644 index 0000000000..542fba651f --- /dev/null +++ b/target/linux/generic/pending-6.6/302-mips_no_branch_likely.patch @@ -0,0 +1,22 @@ +From: Felix Fietkau +Subject: mips: use -mno-branch-likely for kernel and userspace + +saves ~11k kernel size after lzma and ~12k squashfs size in the + +lede-commit: 41a039f46450ffae9483d6216422098669da2900 +Signed-off-by: Felix Fietkau +--- + arch/mips/Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/mips/Makefile ++++ b/arch/mips/Makefile +@@ -94,7 +94,7 @@ all-$(CONFIG_SYS_SUPPORTS_ZBOOT)+= vmlin + # machines may also. Since BFD is incredibly buggy with respect to + # crossformat linking we rely on the elf2ecoff tool for format conversion. + # +-cflags-y += -G 0 -mno-abicalls -fno-pic -pipe ++cflags-y += -G 0 -mno-abicalls -fno-pic -pipe -mno-branch-likely + cflags-y += -msoft-float + LDFLAGS_vmlinux += -G 0 -static -n -nostdlib + KBUILD_AFLAGS_MODULE += -mlong-calls diff --git a/target/linux/generic/pending-6.6/305-mips_module_reloc.patch b/target/linux/generic/pending-6.6/305-mips_module_reloc.patch new file mode 100644 index 0000000000..5de9019677 --- /dev/null +++ b/target/linux/generic/pending-6.6/305-mips_module_reloc.patch @@ -0,0 +1,370 @@ +From: Felix Fietkau +Subject: mips: replace -mlong-calls with -mno-long-calls to make function calls faster in kernel modules to achieve this, try to + +lede-commit: 3b3d64743ba2a874df9d70cd19e242205b0a788c +Signed-off-by: Felix Fietkau +--- + arch/mips/Makefile | 5 + + arch/mips/include/asm/module.h | 5 + + arch/mips/kernel/module.c | 279 ++++++++++++++++++++++++++++++++++++++++- + 3 files changed, 284 insertions(+), 5 deletions(-) + +--- a/arch/mips/Makefile ++++ b/arch/mips/Makefile +@@ -97,8 +97,18 @@ all-$(CONFIG_SYS_SUPPORTS_ZBOOT)+= vmlin + cflags-y += -G 0 -mno-abicalls -fno-pic -pipe -mno-branch-likely + cflags-y += -msoft-float + LDFLAGS_vmlinux += -G 0 -static -n -nostdlib ++ifdef CONFIG_64BIT + KBUILD_AFLAGS_MODULE += -mlong-calls + KBUILD_CFLAGS_MODULE += -mlong-calls ++else ++ ifdef CONFIG_DYNAMIC_FTRACE ++ KBUILD_AFLAGS_MODULE += -mlong-calls ++ KBUILD_CFLAGS_MODULE += -mlong-calls ++ else ++ KBUILD_AFLAGS_MODULE += -mno-long-calls ++ KBUILD_CFLAGS_MODULE += -mno-long-calls ++ endif ++endif + + ifeq ($(CONFIG_RELOCATABLE),y) + LDFLAGS_vmlinux += --emit-relocs +--- a/arch/mips/include/asm/module.h ++++ b/arch/mips/include/asm/module.h +@@ -12,6 +12,11 @@ struct mod_arch_specific { + const struct exception_table_entry *dbe_start; + const struct exception_table_entry *dbe_end; + struct mips_hi16 *r_mips_hi16_list; ++ ++ void *phys_plt_tbl; ++ void *virt_plt_tbl; ++ unsigned int phys_plt_offset; ++ unsigned int virt_plt_offset; + }; + + typedef uint8_t Elf64_Byte; /* Type for a 8-bit quantity. */ +--- a/arch/mips/kernel/module.c ++++ b/arch/mips/kernel/module.c +@@ -32,23 +32,261 @@ struct mips_hi16 { + static LIST_HEAD(dbe_list); + static DEFINE_SPINLOCK(dbe_lock); + +-#ifdef MODULE_START ++/* ++ * Get the potential max trampolines size required of the init and ++ * non-init sections. Only used if we cannot find enough contiguous ++ * physically mapped memory to put the module into. ++ */ ++static unsigned int ++get_plt_size(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, ++ const char *secstrings, unsigned int symindex, bool is_init) ++{ ++ unsigned long ret = 0; ++ unsigned int i, j; ++ Elf_Sym *syms; ++ ++ /* Everything marked ALLOC (this includes the exported symbols) */ ++ for (i = 1; i < hdr->e_shnum; ++i) { ++ unsigned int info = sechdrs[i].sh_info; ++ ++ if (sechdrs[i].sh_type != SHT_REL ++ && sechdrs[i].sh_type != SHT_RELA) ++ continue; ++ ++ /* Not a valid relocation section? */ ++ if (info >= hdr->e_shnum) ++ continue; ++ ++ /* Don't bother with non-allocated sections */ ++ if (!(sechdrs[info].sh_flags & SHF_ALLOC)) ++ continue; ++ ++ /* If it's called *.init*, and we're not init, we're ++ not interested */ ++ if ((strstr(secstrings + sechdrs[i].sh_name, ".init") != 0) ++ != is_init) ++ continue; ++ ++ syms = (Elf_Sym *) sechdrs[symindex].sh_addr; ++ if (sechdrs[i].sh_type == SHT_REL) { ++ Elf_Mips_Rel *rel = (void *) sechdrs[i].sh_addr; ++ unsigned int size = sechdrs[i].sh_size / sizeof(*rel); ++ ++ for (j = 0; j < size; ++j) { ++ Elf_Sym *sym; ++ ++ if (ELF_MIPS_R_TYPE(rel[j]) != R_MIPS_26) ++ continue; ++ ++ sym = syms + ELF_MIPS_R_SYM(rel[j]); ++ if (!is_init && sym->st_shndx != SHN_UNDEF) ++ continue; ++ ++ ret += 4 * sizeof(int); ++ } ++ } else { ++ Elf_Mips_Rela *rela = (void *) sechdrs[i].sh_addr; ++ unsigned int size = sechdrs[i].sh_size / sizeof(*rela); ++ ++ for (j = 0; j < size; ++j) { ++ Elf_Sym *sym; ++ ++ if (ELF_MIPS_R_TYPE(rela[j]) != R_MIPS_26) ++ continue; ++ ++ sym = syms + ELF_MIPS_R_SYM(rela[j]); ++ if (!is_init && sym->st_shndx != SHN_UNDEF) ++ continue; ++ ++ ret += 4 * sizeof(int); ++ } ++ } ++ } ++ ++ return ret; ++} ++ ++#ifndef MODULE_START ++static void *alloc_phys(unsigned long size) ++{ ++ unsigned order; ++ struct page *page; ++ struct page *p; ++ ++ size = PAGE_ALIGN(size); ++ order = get_order(size); ++ ++ page = alloc_pages(GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN | ++ __GFP_THISNODE, order); ++ if (!page) ++ return NULL; ++ ++ split_page(page, order); ++ ++ /* mark all pages except for the last one */ ++ for (p = page; p + 1 < page + (size >> PAGE_SHIFT); ++p) ++ set_bit(PG_owner_priv_1, &p->flags); ++ ++ for (p = page + (size >> PAGE_SHIFT); p < page + (1 << order); ++p) ++ __free_page(p); ++ ++ return page_address(page); ++} ++#endif ++ ++static void free_phys(void *ptr) ++{ ++ struct page *page; ++ bool free; ++ ++ page = virt_to_page(ptr); ++ do { ++ free = test_and_clear_bit(PG_owner_priv_1, &page->flags); ++ __free_page(page); ++ page++; ++ } while (free); ++} ++ ++ + void *module_alloc(unsigned long size) + { ++#ifdef MODULE_START + return __vmalloc_node_range(size, 1, MODULE_START, MODULE_END, + GFP_KERNEL, PAGE_KERNEL, 0, NUMA_NO_NODE, + __builtin_return_address(0)); ++#else ++ void *ptr; ++ ++ if (size == 0) ++ return NULL; ++ ++ ptr = alloc_phys(size); ++ ++ /* If we failed to allocate physically contiguous memory, ++ * fall back to regular vmalloc. The module loader code will ++ * create jump tables to handle long jumps */ ++ if (!ptr) ++ return vmalloc(size); ++ ++ return ptr; ++#endif + } ++ ++static inline bool is_phys_addr(void *ptr) ++{ ++#ifdef CONFIG_64BIT ++ return (KSEGX((unsigned long)ptr) == CKSEG0); ++#else ++ return (KSEGX(ptr) == KSEG0); + #endif ++} ++ ++/* Free memory returned from module_alloc */ ++void module_memfree(void *module_region) ++{ ++ if (is_phys_addr(module_region)) ++ free_phys(module_region); ++ else ++ vfree(module_region); ++} ++ ++static void *__module_alloc(int size, bool phys) ++{ ++ void *ptr; ++ ++ if (phys) ++ ptr = kmalloc(size, GFP_KERNEL); ++ else ++ ptr = vmalloc(size); ++ return ptr; ++} ++ ++static void __module_free(void *ptr) ++{ ++ if (is_phys_addr(ptr)) ++ kfree(ptr); ++ else ++ vfree(ptr); ++} ++ ++int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, ++ char *secstrings, struct module *mod) ++{ ++ unsigned int symindex = 0; ++ unsigned int core_size, init_size; ++ int i; ++ ++ mod->arch.phys_plt_offset = 0; ++ mod->arch.virt_plt_offset = 0; ++ mod->arch.phys_plt_tbl = NULL; ++ mod->arch.virt_plt_tbl = NULL; ++ ++ if (IS_ENABLED(CONFIG_64BIT)) ++ return 0; ++ ++ for (i = 1; i < hdr->e_shnum; i++) ++ if (sechdrs[i].sh_type == SHT_SYMTAB) ++ symindex = i; ++ ++ core_size = get_plt_size(hdr, sechdrs, secstrings, symindex, false); ++ init_size = get_plt_size(hdr, sechdrs, secstrings, symindex, true); ++ ++ if ((core_size + init_size) == 0) ++ return 0; ++ ++ mod->arch.phys_plt_tbl = __module_alloc(core_size + init_size, 1); ++ if (!mod->arch.phys_plt_tbl) ++ return -ENOMEM; ++ ++ mod->arch.virt_plt_tbl = __module_alloc(core_size + init_size, 0); ++ if (!mod->arch.virt_plt_tbl) { ++ __module_free(mod->arch.phys_plt_tbl); ++ mod->arch.phys_plt_tbl = NULL; ++ return -ENOMEM; ++ } ++ ++ return 0; ++} + + static void apply_r_mips_32(u32 *location, u32 base, Elf_Addr v) + { + *location = base + v; + } + ++static Elf_Addr add_plt_entry_to(unsigned *plt_offset, ++ void *start, Elf_Addr v) ++{ ++ unsigned *tramp = start + *plt_offset; ++ *plt_offset += 4 * sizeof(int); ++ ++ /* adjust carry for addiu */ ++ if (v & 0x00008000) ++ v += 0x10000; ++ ++ tramp[0] = 0x3c190000 | (v >> 16); /* lui t9, hi16 */ ++ tramp[1] = 0x27390000 | (v & 0xffff); /* addiu t9, t9, lo16 */ ++ tramp[2] = 0x03200008; /* jr t9 */ ++ tramp[3] = 0x00000000; /* nop */ ++ ++ return (Elf_Addr) tramp; ++} ++ ++static Elf_Addr add_plt_entry(struct module *me, void *location, Elf_Addr v) ++{ ++ if (is_phys_addr(location)) ++ return add_plt_entry_to(&me->arch.phys_plt_offset, ++ me->arch.phys_plt_tbl, v); ++ else ++ return add_plt_entry_to(&me->arch.virt_plt_offset, ++ me->arch.virt_plt_tbl, v); ++ ++} ++ + static int apply_r_mips_26(struct module *me, u32 *location, u32 base, + Elf_Addr v) + { ++ u32 ofs = base & 0x03ffffff; ++ + if (v % 4) { + pr_err("module %s: dangerous R_MIPS_26 relocation\n", + me->name); +@@ -56,13 +294,17 @@ static int apply_r_mips_26(struct module + } + + if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) { +- pr_err("module %s: relocation overflow\n", +- me->name); +- return -ENOEXEC; ++ v = add_plt_entry(me, location, v + (ofs << 2)); ++ if (!v) { ++ pr_err("module %s: relocation overflow\n", ++ me->name); ++ return -ENOEXEC; ++ } ++ ofs = 0; + } + + *location = (*location & ~0x03ffffff) | +- ((base + (v >> 2)) & 0x03ffffff); ++ ((ofs + (v >> 2)) & 0x03ffffff); + + return 0; + } +@@ -442,9 +684,36 @@ int module_finalize(const Elf_Ehdr *hdr, + list_add(&me->arch.dbe_list, &dbe_list); + spin_unlock_irq(&dbe_lock); + } ++ ++ /* Get rid of the fixup trampoline if we're running the module ++ * from physically mapped address space */ ++ if (me->arch.phys_plt_offset == 0) { ++ __module_free(me->arch.phys_plt_tbl); ++ me->arch.phys_plt_tbl = NULL; ++ } ++ if (me->arch.virt_plt_offset == 0) { ++ __module_free(me->arch.virt_plt_tbl); ++ me->arch.virt_plt_tbl = NULL; ++ } ++ + return 0; + } + ++void module_arch_freeing_init(struct module *mod) ++{ ++ if (mod->state == MODULE_STATE_LIVE) ++ return; ++ ++ if (mod->arch.phys_plt_tbl) { ++ __module_free(mod->arch.phys_plt_tbl); ++ mod->arch.phys_plt_tbl = NULL; ++ } ++ if (mod->arch.virt_plt_tbl) { ++ __module_free(mod->arch.virt_plt_tbl); ++ mod->arch.virt_plt_tbl = NULL; ++ } ++} ++ + void module_arch_cleanup(struct module *mod) + { + spin_lock_irq(&dbe_lock); diff --git a/target/linux/generic/pending-6.6/308-mips32r2_tune.patch b/target/linux/generic/pending-6.6/308-mips32r2_tune.patch new file mode 100644 index 0000000000..db410a6bc0 --- /dev/null +++ b/target/linux/generic/pending-6.6/308-mips32r2_tune.patch @@ -0,0 +1,22 @@ +From: Felix Fietkau +Subject: kernel: add -mtune=34kc to MIPS CFLAGS when building for mips32r2 + +This provides a good tradeoff across at least 24Kc-74Kc, while also +producing smaller code. + +Signed-off-by: Felix Fietkau +--- + arch/mips/Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/mips/Makefile ++++ b/arch/mips/Makefile +@@ -172,7 +172,7 @@ cflags-$(CONFIG_CPU_R4300) += -march=r43 + cflags-$(CONFIG_CPU_R4X00) += -march=r4600 -Wa,--trap + cflags-$(CONFIG_CPU_TX49XX) += -march=r4600 -Wa,--trap + cflags-$(CONFIG_CPU_MIPS32_R1) += -march=mips32 -Wa,--trap +-cflags-$(CONFIG_CPU_MIPS32_R2) += -march=mips32r2 -Wa,--trap ++cflags-$(CONFIG_CPU_MIPS32_R2) += -march=mips32r2 -mtune=34kc -Wa,--trap + cflags-$(CONFIG_CPU_MIPS32_R5) += -march=mips32r5 -Wa,--trap -modd-spreg + cflags-$(CONFIG_CPU_MIPS32_R6) += -march=mips32r6 -Wa,--trap -modd-spreg + cflags-$(CONFIG_CPU_MIPS64_R1) += -march=mips64 -Wa,--trap diff --git a/target/linux/generic/pending-6.6/310-arm_module_unresolved_weak_sym.patch b/target/linux/generic/pending-6.6/310-arm_module_unresolved_weak_sym.patch new file mode 100644 index 0000000000..54cc9ba647 --- /dev/null +++ b/target/linux/generic/pending-6.6/310-arm_module_unresolved_weak_sym.patch @@ -0,0 +1,22 @@ +From: Felix Fietkau +Subject: fix errors in unresolved weak symbols on arm + +lede-commit: 570699d4838a907c3ef9f2819bf19eb72997b32f +Signed-off-by: Felix Fietkau +--- + arch/arm/kernel/module.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/arch/arm/kernel/module.c ++++ b/arch/arm/kernel/module.c +@@ -146,6 +146,10 @@ apply_relocate(Elf32_Shdr *sechdrs, cons + return -ENOEXEC; + } + ++ if ((IS_ERR_VALUE(sym->st_value) || !sym->st_value) && ++ ELF_ST_BIND(sym->st_info) == STB_WEAK) ++ continue; ++ + loc = dstsec->sh_addr + rel->r_offset; + + switch (ELF32_R_TYPE(rel->r_info)) { diff --git a/target/linux/generic/pending-6.6/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch b/target/linux/generic/pending-6.6/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch new file mode 100644 index 0000000000..3f553b28b3 --- /dev/null +++ b/target/linux/generic/pending-6.6/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch @@ -0,0 +1,282 @@ +From: Yousong Zhou +Subject: MIPS: kexec: Accept command line parameters from userspace. + +Signed-off-by: Yousong Zhou +--- + arch/mips/kernel/machine_kexec.c | 153 +++++++++++++++++++++++++++++++----- + arch/mips/kernel/machine_kexec.h | 20 +++++ + arch/mips/kernel/relocate_kernel.S | 21 +++-- + 3 files changed, 167 insertions(+), 27 deletions(-) + create mode 100644 arch/mips/kernel/machine_kexec.h + +--- a/arch/mips/kernel/machine_kexec.c ++++ b/arch/mips/kernel/machine_kexec.c +@@ -9,14 +9,11 @@ + #include + #include + ++#include + #include + #include +- +-extern const unsigned char relocate_new_kernel[]; +-extern const size_t relocate_new_kernel_size; +- +-extern unsigned long kexec_start_address; +-extern unsigned long kexec_indirection_page; ++#include ++#include "machine_kexec.h" + + static unsigned long reboot_code_buffer; + +@@ -30,6 +27,101 @@ void (*_crash_smp_send_stop)(void) = NUL + void (*_machine_kexec_shutdown)(void) = NULL; + void (*_machine_crash_shutdown)(struct pt_regs *regs) = NULL; + ++static void machine_kexec_print_args(void) ++{ ++ unsigned long argc = (int)kexec_args[0]; ++ int i; ++ ++ pr_info("kexec_args[0] (argc): %lu\n", argc); ++ pr_info("kexec_args[1] (argv): %p\n", (void *)kexec_args[1]); ++ pr_info("kexec_args[2] (env ): %p\n", (void *)kexec_args[2]); ++ pr_info("kexec_args[3] (desc): %p\n", (void *)kexec_args[3]); ++ ++ for (i = 0; i < argc; i++) { ++ pr_info("kexec_argv[%d] = %p, %s\n", ++ i, kexec_argv[i], kexec_argv[i]); ++ } ++} ++ ++static void machine_kexec_init_argv(struct kimage *image) ++{ ++ void __user *buf = NULL; ++ size_t bufsz; ++ size_t size; ++ int i; ++ ++ bufsz = 0; ++ for (i = 0; i < image->nr_segments; i++) { ++ struct kexec_segment *seg; ++ ++ seg = &image->segment[i]; ++ if (seg->bufsz < 6) ++ continue; ++ ++ if (strncmp((char *) seg->buf, "kexec ", 6)) ++ continue; ++ ++ buf = seg->buf; ++ bufsz = seg->bufsz; ++ break; ++ } ++ ++ if (!buf) ++ return; ++ ++ size = KEXEC_COMMAND_LINE_SIZE; ++ size = min(size, bufsz); ++ if (size < bufsz) ++ pr_warn("kexec command line truncated to %zd bytes\n", size); ++ ++ /* Copy to kernel space */ ++ if (copy_from_user(kexec_argv_buf, buf, size)) ++ pr_warn("kexec command line copy to kernel space failed\n"); ++ ++ kexec_argv_buf[size - 1] = 0; ++} ++ ++static void machine_kexec_parse_argv(struct kimage *image) ++{ ++ char *reboot_code_buffer; ++ int reloc_delta; ++ char *ptr; ++ int argc; ++ int i; ++ ++ ptr = kexec_argv_buf; ++ argc = 0; ++ ++ /* ++ * convert command line string to array of parameters ++ * (as bootloader does). ++ */ ++ while (ptr && *ptr && (KEXEC_MAX_ARGC > argc)) { ++ if (*ptr == ' ') { ++ *ptr++ = '\0'; ++ continue; ++ } ++ ++ kexec_argv[argc++] = ptr; ++ ptr = strchr(ptr, ' '); ++ } ++ ++ if (!argc) ++ return; ++ ++ kexec_args[0] = argc; ++ kexec_args[1] = (unsigned long)kexec_argv; ++ kexec_args[2] = 0; ++ kexec_args[3] = 0; ++ ++ reboot_code_buffer = page_address(image->control_code_page); ++ reloc_delta = reboot_code_buffer - (char *)kexec_relocate_new_kernel; ++ ++ kexec_args[1] += reloc_delta; ++ for (i = 0; i < argc; i++) ++ kexec_argv[i] += reloc_delta; ++} ++ + static void kexec_image_info(const struct kimage *kimage) + { + unsigned long i; +@@ -99,6 +191,18 @@ machine_kexec_prepare(struct kimage *kim + #endif + + kexec_image_info(kimage); ++ /* ++ * Whenever arguments passed from kexec-tools, Init the arguments as ++ * the original ones to try avoiding booting failure. ++ */ ++ ++ kexec_args[0] = fw_arg0; ++ kexec_args[1] = fw_arg1; ++ kexec_args[2] = fw_arg2; ++ kexec_args[3] = fw_arg3; ++ ++ machine_kexec_init_argv(kimage); ++ machine_kexec_parse_argv(kimage); + + if (_machine_kexec_prepare) + return _machine_kexec_prepare(kimage); +@@ -161,7 +265,7 @@ machine_crash_shutdown(struct pt_regs *r + void kexec_nonboot_cpu_jump(void) + { + local_flush_icache_range((unsigned long)relocated_kexec_smp_wait, +- reboot_code_buffer + relocate_new_kernel_size); ++ reboot_code_buffer + KEXEC_RELOCATE_NEW_KERNEL_SIZE); + + relocated_kexec_smp_wait(NULL); + } +@@ -199,7 +303,7 @@ void kexec_reboot(void) + * machine_kexec() CPU. + */ + local_flush_icache_range(reboot_code_buffer, +- reboot_code_buffer + relocate_new_kernel_size); ++ reboot_code_buffer + KEXEC_RELOCATE_NEW_KERNEL_SIZE); + + do_kexec = (void *)reboot_code_buffer; + do_kexec(); +@@ -212,10 +316,12 @@ machine_kexec(struct kimage *image) + unsigned long *ptr; + + reboot_code_buffer = +- (unsigned long)page_address(image->control_code_page); ++ (unsigned long)page_address(image->control_code_page); ++ pr_info("reboot_code_buffer = %p\n", (void *)reboot_code_buffer); + + kexec_start_address = + (unsigned long) phys_to_virt(image->start); ++ pr_info("kexec_start_address = %p\n", (void *)kexec_start_address); + + if (image->type == KEXEC_TYPE_DEFAULT) { + kexec_indirection_page = +@@ -223,9 +329,19 @@ machine_kexec(struct kimage *image) + } else { + kexec_indirection_page = (unsigned long)&image->head; + } ++ pr_info("kexec_indirection_page = %p\n", (void *)kexec_indirection_page); + +- memcpy((void*)reboot_code_buffer, relocate_new_kernel, +- relocate_new_kernel_size); ++ pr_info("Where is memcpy: %p\n", memcpy); ++ pr_info("kexec_relocate_new_kernel = %p, kexec_relocate_new_kernel_end = %p\n", ++ (void *)kexec_relocate_new_kernel, &kexec_relocate_new_kernel_end); ++ pr_info("Copy %lu bytes from %p to %p\n", KEXEC_RELOCATE_NEW_KERNEL_SIZE, ++ (void *)kexec_relocate_new_kernel, (void *)reboot_code_buffer); ++ memcpy((void*)reboot_code_buffer, kexec_relocate_new_kernel, ++ KEXEC_RELOCATE_NEW_KERNEL_SIZE); ++ ++ pr_info("Before _print_args().\n"); ++ machine_kexec_print_args(); ++ pr_info("Before eval loop.\n"); + + /* + * The generic kexec code builds a page list with physical +@@ -256,7 +372,7 @@ machine_kexec(struct kimage *image) + #ifdef CONFIG_SMP + /* All secondary cpus now may jump to kexec_wait cycle */ + relocated_kexec_smp_wait = reboot_code_buffer + +- (void *)(kexec_smp_wait - relocate_new_kernel); ++ (void *)(kexec_smp_wait - kexec_relocate_new_kernel); + smp_wmb(); + atomic_set(&kexec_ready_to_reboot, 1); + #endif +--- /dev/null ++++ b/arch/mips/kernel/machine_kexec.h +@@ -0,0 +1,20 @@ ++#ifndef _MACHINE_KEXEC_H ++#define _MACHINE_KEXEC_H ++ ++#ifndef __ASSEMBLY__ ++extern const unsigned char kexec_relocate_new_kernel[]; ++extern unsigned long kexec_relocate_new_kernel_end; ++extern unsigned long kexec_start_address; ++extern unsigned long kexec_indirection_page; ++ ++extern char kexec_argv_buf[]; ++extern char *kexec_argv[]; ++ ++#define KEXEC_RELOCATE_NEW_KERNEL_SIZE ((unsigned long)&kexec_relocate_new_kernel_end - (unsigned long)kexec_relocate_new_kernel) ++#endif /* !__ASSEMBLY__ */ ++ ++#define KEXEC_COMMAND_LINE_SIZE 256 ++#define KEXEC_ARGV_SIZE (KEXEC_COMMAND_LINE_SIZE / 16) ++#define KEXEC_MAX_ARGC (KEXEC_ARGV_SIZE / sizeof(long)) ++ ++#endif +--- a/arch/mips/kernel/relocate_kernel.S ++++ b/arch/mips/kernel/relocate_kernel.S +@@ -10,10 +10,11 @@ + #include + #include + #include ++#include "machine_kexec.h" + + #include + +-LEAF(relocate_new_kernel) ++LEAF(kexec_relocate_new_kernel) + PTR_L a0, arg0 + PTR_L a1, arg1 + PTR_L a2, arg2 +@@ -98,7 +99,7 @@ done: + #endif + /* jump to kexec_start_address */ + j s1 +- END(relocate_new_kernel) ++ END(kexec_relocate_new_kernel) + + #ifdef CONFIG_SMP + /* +@@ -177,8 +178,15 @@ EXPORT(kexec_indirection_page) + PTR_WD 0 + .size kexec_indirection_page, PTRSIZE + +-relocate_new_kernel_end: ++kexec_argv_buf: ++ EXPORT(kexec_argv_buf) ++ .skip KEXEC_COMMAND_LINE_SIZE ++ .size kexec_argv_buf, KEXEC_COMMAND_LINE_SIZE ++ ++kexec_argv: ++ EXPORT(kexec_argv) ++ .skip KEXEC_ARGV_SIZE ++ .size kexec_argv, KEXEC_ARGV_SIZE + +-EXPORT(relocate_new_kernel_size) +- PTR_WD relocate_new_kernel_end - relocate_new_kernel +- .size relocate_new_kernel_size, PTRSIZE ++kexec_relocate_new_kernel_end: ++ EXPORT(kexec_relocate_new_kernel_end) diff --git a/target/linux/generic/pending-6.6/332-arc-add-OWRTDTB-section.patch b/target/linux/generic/pending-6.6/332-arc-add-OWRTDTB-section.patch new file mode 100644 index 0000000000..30158cf399 --- /dev/null +++ b/target/linux/generic/pending-6.6/332-arc-add-OWRTDTB-section.patch @@ -0,0 +1,84 @@ +From bb0c3b0175240bf152fd7c644821a0cf9f77c37c Mon Sep 17 00:00:00 2001 +From: Evgeniy Didin +Date: Fri, 15 Mar 2019 18:53:38 +0300 +Subject: [PATCH] arc add OWRTDTB section + +This change allows OpenWRT to patch resulting kernel binary with +external .dtb. + +That allows us to re-use exactky the same vmlinux on different boards +given its ARC core configurations match (at least cache line sizes etc). + +""patch-dtb" searches for ASCII "OWRTDTB:" strign and copies external +.dtb right after it, keeping the string in place. + +Signed-off-by: Eugeniy Paltsev +Signed-off-by: Alexey Brodkin +Signed-off-by: Evgeniy Didin +--- + arch/arc/kernel/head.S | 10 ++++++++++ + arch/arc/kernel/setup.c | 4 +++- + arch/arc/kernel/vmlinux.lds.S | 13 +++++++++++++ + 3 files changed, 26 insertions(+), 1 deletion(-) + +--- a/arch/arc/kernel/head.S ++++ b/arch/arc/kernel/head.S +@@ -88,6 +88,16 @@ + DSP_EARLY_INIT + .endm + ++ ; Here "patch-dtb" will embed external .dtb ++ ; Note "patch-dtb" searches for ASCII "OWRTDTB:" string ++ ; and pastes .dtb right after it, hense the string precedes ++ ; __image_dtb symbol. ++ .section .owrt, "aw",@progbits ++ .ascii "OWRTDTB:" ++ENTRY(__image_dtb) ++ .fill 0x4000 ++END(__image_dtb) ++ + .section .init.text, "ax",@progbits + + ;---------------------------------------------------------------- +--- a/arch/arc/kernel/setup.c ++++ b/arch/arc/kernel/setup.c +@@ -495,6 +495,8 @@ static inline bool uboot_arg_invalid(uns + /* We always pass 0 as magic from U-boot */ + #define UBOOT_MAGIC_VALUE 0 + ++extern struct boot_param_header __image_dtb; ++ + void __init handle_uboot_args(void) + { + bool use_embedded_dtb = true; +@@ -533,7 +535,7 @@ void __init handle_uboot_args(void) + ignore_uboot_args: + + if (use_embedded_dtb) { +- machine_desc = setup_machine_fdt(__dtb_start); ++ machine_desc = setup_machine_fdt(&__image_dtb); + if (!machine_desc) + panic("Embedded DT invalid\n"); + } +--- a/arch/arc/kernel/vmlinux.lds.S ++++ b/arch/arc/kernel/vmlinux.lds.S +@@ -27,6 +27,19 @@ SECTIONS + + . = CONFIG_LINUX_LINK_BASE; + ++ /* ++ * In OpenWRT we want to patch built binary embedding .dtb of choice. ++ * This is implemented with "patch-dtb" utility which searches for ++ * "OWRTDTB:" string in first 16k of image and if it is found ++ * copies .dtb right after mentioned string. ++ * ++ * Note: "OWRTDTB:" won't be overwritten with .dtb, .dtb will follow it. ++ */ ++ .owrt : { ++ *(.owrt) ++ . = ALIGN(PAGE_SIZE); ++ } ++ + _int_vec_base_lds = .; + .vector : { + *(.vector) diff --git a/target/linux/generic/pending-6.6/333-arc-enable-unaligned-access-in-kernel-mode.patch b/target/linux/generic/pending-6.6/333-arc-enable-unaligned-access-in-kernel-mode.patch new file mode 100644 index 0000000000..1848a84cc4 --- /dev/null +++ b/target/linux/generic/pending-6.6/333-arc-enable-unaligned-access-in-kernel-mode.patch @@ -0,0 +1,24 @@ +From: Alexey Brodkin +Subject: arc: enable unaligned access in kernel mode + +This enables misaligned access handling even in kernel mode. +Some wireless drivers (ath9k-htc and mt7601u) use misaligned accesses +here and there and to cope with that without fixing stuff in the drivers +we're just gracefully handling it on ARC. + +Signed-off-by: Alexey Brodkin +--- + arch/arc/kernel/unaligned.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/arc/kernel/unaligned.c ++++ b/arch/arc/kernel/unaligned.c +@@ -202,7 +202,7 @@ int misaligned_fixup(unsigned long addre + char buf[TASK_COMM_LEN]; + + /* handle user mode only and only if enabled by sysadmin */ +- if (!user_mode(regs) || !unaligned_enabled) ++ if (!unaligned_enabled) + return 1; + + if (no_unaligned_warning) { diff --git a/target/linux/generic/pending-6.6/342-powerpc-Enable-kernel-XZ-compression-option-on-PPC_8.patch b/target/linux/generic/pending-6.6/342-powerpc-Enable-kernel-XZ-compression-option-on-PPC_8.patch new file mode 100644 index 0000000000..e9b47d185d --- /dev/null +++ b/target/linux/generic/pending-6.6/342-powerpc-Enable-kernel-XZ-compression-option-on-PPC_8.patch @@ -0,0 +1,25 @@ +From 66770a004afe10df11d3902e16eaa0c2c39436bb Mon Sep 17 00:00:00 2001 +From: Pawel Dembicki +Date: Fri, 24 May 2019 17:56:19 +0200 +Subject: [PATCH] powerpc: Enable kernel XZ compression option on PPC_85xx + +Enable kernel XZ compression option on PPC_85xx. Tested with +simpleImage on TP-Link TL-WDR4900 (Freescale P1014 processor). + +Suggested-by: Christian Lamparter +Signed-off-by: Pawel Dembicki +--- + arch/powerpc/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/powerpc/Kconfig ++++ b/arch/powerpc/Kconfig +@@ -229,7 +229,7 @@ config PPC + select HAVE_KERNEL_GZIP + select HAVE_KERNEL_LZMA if DEFAULT_UIMAGE + select HAVE_KERNEL_LZO if DEFAULT_UIMAGE +- select HAVE_KERNEL_XZ if PPC_BOOK3S || 44x ++ select HAVE_KERNEL_XZ if PPC_BOOK3S || 44x || PPC_85xx + select HAVE_KPROBES + select HAVE_KPROBES_ON_FTRACE + select HAVE_KRETPROBES diff --git a/target/linux/generic/pending-6.6/351-irqchip-bcm-6345-l1-request-memory-region.patch b/target/linux/generic/pending-6.6/351-irqchip-bcm-6345-l1-request-memory-region.patch new file mode 100644 index 0000000000..2675ca4791 --- /dev/null +++ b/target/linux/generic/pending-6.6/351-irqchip-bcm-6345-l1-request-memory-region.patch @@ -0,0 +1,113 @@ +From patchwork Thu Mar 16 19:28:33 2023 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 8bit +X-Patchwork-Submitter: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= + +X-Patchwork-Id: 13178238 +Return-Path: +X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on + aws-us-west-2-korg-lkml-1.web.codeaurora.org +Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) + by smtp.lore.kernel.org (Postfix) with ESMTP id 5EF2AC6FD19 + for ; Thu, 16 Mar 2023 19:28:43 +0000 (UTC) +Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand + id S230076AbjCPT2l (ORCPT ); + Thu, 16 Mar 2023 15:28:41 -0400 +Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56412 "EHLO + lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org + with ESMTP id S230039AbjCPT2k (ORCPT + ); Thu, 16 Mar 2023 15:28:40 -0400 +Received: from mail-wr1-x42f.google.com (mail-wr1-x42f.google.com + [IPv6:2a00:1450:4864:20::42f]) + by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7259B7D9F; + Thu, 16 Mar 2023 12:28:38 -0700 (PDT) +Received: by mail-wr1-x42f.google.com with SMTP id y14so2539231wrq.4; + Thu, 16 Mar 2023 12:28:38 -0700 (PDT) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=gmail.com; s=20210112; t=1678994917; + h=content-transfer-encoding:mime-version:references:in-reply-to + :message-id:date:subject:cc:to:from:from:to:cc:subject:date + :message-id:reply-to; + bh=j8afldfRZftLeVmekmQfoh01jVdumsVP7nkKoPaU3Q0=; + b=FzMRr5ekh/fDiJqTlezNj6nLjzvn5z92FtYeB8MquVSMB8PuvarccnyqAzsXiccf+v + uwRFIomnTWNLGVjzc1xrB2hGiCKD3jBo5n1u8p/yEV6rpolbxVjfM7eTHXyAHXGXz7ZJ + TPeVbWfAlxiSD6+BPtXr/efehcdI64fIoL6G/U1WHNMo01Tzr/Obf3y5tug17N0fGcXg + CH6E5a2HguZUtwrm26LcK9IOV/7xEx5eIE1cOvTLMxPbGWaZwEjjP16HylJr06xRLhaf + RpiYBT3mXwwuOx0jLOhqavY/2kZ9GVbZRWMMwZrZv9xNO13SBwc1VUVgD4k3FntnSk7Z + AaOQ== +X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=1e100.net; s=20210112; t=1678994917; + h=content-transfer-encoding:mime-version:references:in-reply-to + :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc + :subject:date:message-id:reply-to; + bh=j8afldfRZftLeVmekmQfoh01jVdumsVP7nkKoPaU3Q0=; + b=OaA5DMgqalrfqO5iOtmmxFPsH90MkN7l4EJpyVnzuiO1Wd6rSCpqPOR7xpxZno8OPP + tdfm4vzn9Ie4AUDbFKDTUlPG+tgkmIruo3K9C0VnY9DD2PRZMEYBbWaJKU1otqKt0NKu + IAAHNvxvQvCESKzbXFLYwWbRKFScOSMGmGBTDfgThz51A18Ff1hJy/BmnuZk7M2TLgHO + wQpy9t7oeB/Hkxl41y46emLc/nESsvwvAG/fx/zPzCe9UiaQLrdZq+BKeOwSBedktzK5 + U/ZTfgzU2UGSI67aGRqqGnI0uXq+MAJMK18qzM0VByxj6W+AXJ6BJr5P0quljeQ8upSg + bEUg== +X-Gm-Message-State: AO0yUKWnqTlccBDnqwCSRdqOBGc2FyfiLy1Tg7EjPENlISpzXuDYwW/R + lJSI06rrfq+Vel/SigfpGJI= +X-Google-Smtp-Source: + AK7set/jYfYl9ttVzIXJO+ZQVfa6cE/yOsP8fx4teiTmGNNWyVlIJRzMAlF3IUGqRAXAmY3hAabIuQ== +X-Received: by 2002:a5d:40ce:0:b0:2cd:ceab:df1a with SMTP id + b14-20020a5d40ce000000b002cdceabdf1amr381006wrq.32.1678994916642; + Thu, 16 Mar 2023 12:28:36 -0700 (PDT) +Received: from atlantis.lan (255.red-79-146-124.dynamicip.rima-tde.net. + [79.146.124.255]) + by smtp.gmail.com with ESMTPSA id + l10-20020a5d4bca000000b002cfea3c49d5sm180041wrt.52.2023.03.16.12.28.35 + (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); + Thu, 16 Mar 2023 12:28:35 -0700 (PDT) +From: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= +To: f.fainelli@gmail.com, jonas.gorski@gmail.com, + bcm-kernel-feedback-list@broadcom.com, tglx@linutronix.de, + maz@kernel.org, linux-mips@vger.kernel.org, + linux-kernel@vger.kernel.org +Cc: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= +Subject: [PATCH v2] irqchip/bcm-6345-l1: request memory region +Date: Thu, 16 Mar 2023 20:28:33 +0100 +Message-Id: <20230316192833.1603149-1-noltari@gmail.com> +X-Mailer: git-send-email 2.30.2 +In-Reply-To: <20230316180701.783785-1-noltari@gmail.com> +References: <20230316180701.783785-1-noltari@gmail.com> +MIME-Version: 1.0 +Precedence: bulk +List-ID: +X-Mailing-List: linux-mips@vger.kernel.org + +Request memory region in order to display it in /proc/iomem. +Also stop printing the MMIO address since it just displays (ptrval). + +Signed-off-by: Álvaro Fernández Rojas +Acked-by: Florian Fainelli +--- + v2: request memory region and stop displaying MMIO address. + + drivers/irqchip/irq-bcm6345-l1.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/drivers/irqchip/irq-bcm6345-l1.c ++++ b/drivers/irqchip/irq-bcm6345-l1.c +@@ -253,6 +253,9 @@ static int __init bcm6345_l1_init_one(st + if (!cpu->map_base) + return -ENOMEM; + ++ if (!request_mem_region(res.start, sz, res.name)) ++ pr_err("failed to request intc memory"); ++ + for (i = 0; i < n_words; i++) { + cpu->enable_cache[i] = 0; + __raw_writel(0, cpu->map_base + reg_enable(intc, i)); +@@ -331,8 +334,7 @@ static int __init bcm6345_l1_of_init(str + for_each_cpu(idx, &intc->cpumask) { + struct bcm6345_l1_cpu *cpu = intc->cpus[idx]; + +- pr_info(" CPU%u at MMIO 0x%p (irq = %d)\n", idx, +- cpu->map_base, cpu->parent_irq); ++ pr_info(" CPU%u (irq = %d)\n", idx, cpu->parent_irq); + } + + return 0; diff --git a/target/linux/generic/pending-6.6/400-mtd-mtdsplit-support.patch b/target/linux/generic/pending-6.6/400-mtd-mtdsplit-support.patch new file mode 100644 index 0000000000..c619d12ce5 --- /dev/null +++ b/target/linux/generic/pending-6.6/400-mtd-mtdsplit-support.patch @@ -0,0 +1,328 @@ +From 39717277d5c87bdb183cf2f258957b44ba99b4df Mon Sep 17 00:00:00 2001 +From: OpenWrt community +Date: Wed, 13 Jul 2022 11:47:35 +0200 +Subject: [PATCH] mtd: mtdsplit support + +--- + drivers/mtd/Kconfig | 19 ++++ + drivers/mtd/Makefile | 2 + + drivers/mtd/mtdpart.c | 169 ++++++++++++++++++++++++++++----- + include/linux/mtd/mtd.h | 25 +++++ + include/linux/mtd/partitions.h | 7 ++ + 5 files changed, 197 insertions(+), 25 deletions(-) + +--- a/drivers/mtd/Kconfig ++++ b/drivers/mtd/Kconfig +@@ -12,6 +12,25 @@ menuconfig MTD + + if MTD + ++menu "OpenWrt specific MTD options" ++ ++config MTD_ROOTFS_ROOT_DEV ++ bool "Automatically set 'rootfs' partition to be root filesystem" ++ default y ++ ++config MTD_SPLIT_FIRMWARE ++ bool "Automatically split firmware partition for kernel+rootfs" ++ default y ++ ++config MTD_SPLIT_FIRMWARE_NAME ++ string "Firmware partition name" ++ depends on MTD_SPLIT_FIRMWARE ++ default "firmware" ++ ++source "drivers/mtd/mtdsplit/Kconfig" ++ ++endmenu ++ + config MTD_TESTS + tristate "MTD tests support (DANGEROUS)" + depends on m +--- a/drivers/mtd/Makefile ++++ b/drivers/mtd/Makefile +@@ -9,6 +9,8 @@ mtd-y := mtdcore.o mtdsuper.o mtdconc + + obj-y += parsers/ + ++obj-$(CONFIG_MTD_SPLIT) += mtdsplit/ ++ + # 'Users' - code which presents functionality to userspace. + obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o + obj-$(CONFIG_MTD_BLOCK) += mtdblock.o +--- a/drivers/mtd/mtdpart.c ++++ b/drivers/mtd/mtdpart.c +@@ -15,11 +15,13 @@ + #include + #include + #include ++#include + #include + #include + #include + + #include "mtdcore.h" ++#include "mtdsplit/mtdsplit.h" + + /* + * MTD methods which simply translate the effective address and pass through +@@ -236,6 +238,147 @@ static int mtd_add_partition_attrs(struc + return ret; + } + ++static DEFINE_SPINLOCK(part_parser_lock); ++static LIST_HEAD(part_parsers); ++ ++static struct mtd_part_parser *mtd_part_parser_get(const char *name) ++{ ++ struct mtd_part_parser *p, *ret = NULL; ++ ++ spin_lock(&part_parser_lock); ++ ++ list_for_each_entry(p, &part_parsers, list) ++ if (!strcmp(p->name, name) && try_module_get(p->owner)) { ++ ret = p; ++ break; ++ } ++ ++ spin_unlock(&part_parser_lock); ++ ++ return ret; ++} ++ ++static inline void mtd_part_parser_put(const struct mtd_part_parser *p) ++{ ++ module_put(p->owner); ++} ++ ++static struct mtd_part_parser * ++get_partition_parser_by_type(enum mtd_parser_type type, ++ struct mtd_part_parser *start) ++{ ++ struct mtd_part_parser *p, *ret = NULL; ++ ++ spin_lock(&part_parser_lock); ++ ++ p = list_prepare_entry(start, &part_parsers, list); ++ if (start) ++ mtd_part_parser_put(start); ++ ++ list_for_each_entry_continue(p, &part_parsers, list) { ++ if (p->type == type && try_module_get(p->owner)) { ++ ret = p; ++ break; ++ } ++ } ++ ++ spin_unlock(&part_parser_lock); ++ ++ return ret; ++} ++ ++static int parse_mtd_partitions_by_type(struct mtd_info *master, ++ enum mtd_parser_type type, ++ const struct mtd_partition **pparts, ++ struct mtd_part_parser_data *data) ++{ ++ struct mtd_part_parser *prev = NULL; ++ int ret = 0; ++ ++ while (1) { ++ struct mtd_part_parser *parser; ++ ++ parser = get_partition_parser_by_type(type, prev); ++ if (!parser) ++ break; ++ ++ ret = (*parser->parse_fn)(master, pparts, data); ++ ++ if (ret > 0) { ++ mtd_part_parser_put(parser); ++ printk(KERN_NOTICE ++ "%d %s partitions found on MTD device %s\n", ++ ret, parser->name, master->name); ++ break; ++ } ++ ++ prev = parser; ++ } ++ ++ return ret; ++} ++ ++static int ++run_parsers_by_type(struct mtd_info *child, enum mtd_parser_type type) ++{ ++ struct mtd_partition *parts; ++ int nr_parts; ++ int i; ++ ++ nr_parts = parse_mtd_partitions_by_type(child, type, (const struct mtd_partition **)&parts, ++ NULL); ++ if (nr_parts <= 0) ++ return nr_parts; ++ ++ if (WARN_ON(!parts)) ++ return 0; ++ ++ for (i = 0; i < nr_parts; i++) { ++ /* adjust partition offsets */ ++ parts[i].offset += child->part.offset; ++ ++ mtd_add_partition(child->parent, ++ parts[i].name, ++ parts[i].offset, ++ parts[i].size); ++ } ++ ++ kfree(parts); ++ ++ return nr_parts; ++} ++ ++#ifdef CONFIG_MTD_SPLIT_FIRMWARE_NAME ++#define SPLIT_FIRMWARE_NAME CONFIG_MTD_SPLIT_FIRMWARE_NAME ++#else ++#define SPLIT_FIRMWARE_NAME "unused" ++#endif ++ ++static void split_firmware(struct mtd_info *master, struct mtd_info *part) ++{ ++ run_parsers_by_type(part, MTD_PARSER_TYPE_FIRMWARE); ++} ++ ++static void mtd_partition_split(struct mtd_info *master, struct mtd_info *part) ++{ ++ static int rootfs_found = 0; ++ ++ if (rootfs_found) ++ return; ++ ++ if (of_find_property(mtd_get_of_node(part), "linux,rootfs", NULL) || ++ !strcmp(part->name, "rootfs")) { ++ run_parsers_by_type(part, MTD_PARSER_TYPE_ROOTFS); ++ ++ rootfs_found = 1; ++ } ++ ++ if (IS_ENABLED(CONFIG_MTD_SPLIT_FIRMWARE) && ++ !strcmp(part->name, SPLIT_FIRMWARE_NAME) && ++ !of_find_property(mtd_get_of_node(part), "compatible", NULL)) ++ split_firmware(master, part); ++} ++ + int mtd_add_partition(struct mtd_info *parent, const char *name, + long long offset, long long length) + { +@@ -274,6 +417,7 @@ int mtd_add_partition(struct mtd_info *p + if (ret) + goto err_remove_part; + ++ mtd_partition_split(parent, child); + mtd_add_partition_attrs(child); + + return 0; +@@ -422,6 +566,7 @@ int add_mtd_partitions(struct mtd_info * + goto err_del_partitions; + } + ++ mtd_partition_split(master, child); + mtd_add_partition_attrs(child); + + /* Look for subpartitions */ +@@ -438,31 +583,6 @@ err_del_partitions: + return ret; + } + +-static DEFINE_SPINLOCK(part_parser_lock); +-static LIST_HEAD(part_parsers); +- +-static struct mtd_part_parser *mtd_part_parser_get(const char *name) +-{ +- struct mtd_part_parser *p, *ret = NULL; +- +- spin_lock(&part_parser_lock); +- +- list_for_each_entry(p, &part_parsers, list) +- if (!strcmp(p->name, name) && try_module_get(p->owner)) { +- ret = p; +- break; +- } +- +- spin_unlock(&part_parser_lock); +- +- return ret; +-} +- +-static inline void mtd_part_parser_put(const struct mtd_part_parser *p) +-{ +- module_put(p->owner); +-} +- + /* + * Many partition parsers just expected the core to kfree() all their data in + * one chunk. Do that by default. +--- a/include/linux/mtd/mtd.h ++++ b/include/linux/mtd/mtd.h +@@ -615,6 +615,24 @@ static inline void mtd_align_erase_req(s + req->len += mtd->erasesize - mod; + } + ++static inline uint64_t mtd_roundup_to_eb(uint64_t sz, struct mtd_info *mtd) ++{ ++ if (mtd_mod_by_eb(sz, mtd) == 0) ++ return sz; ++ ++ /* Round up to next erase block */ ++ return (mtd_div_by_eb(sz, mtd) + 1) * mtd->erasesize; ++} ++ ++static inline uint64_t mtd_rounddown_to_eb(uint64_t sz, struct mtd_info *mtd) ++{ ++ if (mtd_mod_by_eb(sz, mtd) == 0) ++ return sz; ++ ++ /* Round down to the start of the current erase block */ ++ return (mtd_div_by_eb(sz, mtd)) * mtd->erasesize; ++} ++ + static inline uint32_t mtd_div_by_ws(uint64_t sz, struct mtd_info *mtd) + { + if (mtd->writesize_shift) +@@ -688,6 +706,13 @@ extern struct mtd_info *of_get_mtd_devic + extern struct mtd_info *get_mtd_device_nm(const char *name); + extern void put_mtd_device(struct mtd_info *mtd); + ++static inline uint64_t mtdpart_get_offset(const struct mtd_info *mtd) ++{ ++ if (!mtd_is_partition(mtd)) ++ return 0; ++ ++ return mtd->part.offset; ++} + + struct mtd_notifier { + void (*add)(struct mtd_info *mtd); +--- a/include/linux/mtd/partitions.h ++++ b/include/linux/mtd/partitions.h +@@ -75,6 +75,12 @@ struct mtd_part_parser_data { + * Functions dealing with the various ways of partitioning the space + */ + ++enum mtd_parser_type { ++ MTD_PARSER_TYPE_DEVICE = 0, ++ MTD_PARSER_TYPE_ROOTFS, ++ MTD_PARSER_TYPE_FIRMWARE, ++}; ++ + struct mtd_part_parser { + struct list_head list; + struct module *owner; +@@ -83,6 +89,7 @@ struct mtd_part_parser { + int (*parse_fn)(struct mtd_info *, const struct mtd_partition **, + struct mtd_part_parser_data *); + void (*cleanup)(const struct mtd_partition *pparts, int nr_parts); ++ enum mtd_parser_type type; + }; + + /* Container for passing around a set of parsed partitions */ diff --git a/target/linux/generic/pending-6.6/401-mtd-don-t-register-NVMEM-devices-for-partitions-with.patch b/target/linux/generic/pending-6.6/401-mtd-don-t-register-NVMEM-devices-for-partitions-with.patch new file mode 100644 index 0000000000..42b5a564b1 --- /dev/null +++ b/target/linux/generic/pending-6.6/401-mtd-don-t-register-NVMEM-devices-for-partitions-with.patch @@ -0,0 +1,48 @@ +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Tue, 31 Oct 2023 15:51:01 +0100 +Subject: [PATCH] mtd: don't register NVMEM devices for partitions with custom + drivers +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This fixes issue exposed by upstream commit f4cf4e5db331 ("Revert +"nvmem: add new config option""). + +Signed-off-by: Rafał Miłecki +--- + drivers/mtd/mtdcore.c | 23 +++++++++++++++++++++++ + 1 file changed, 23 insertions(+) + +--- a/drivers/mtd/mtdcore.c ++++ b/drivers/mtd/mtdcore.c +@@ -519,6 +519,29 @@ static int mtd_nvmem_add(struct mtd_info + struct device_node *node = mtd_get_of_node(mtd); + struct nvmem_config config = {}; + ++ /* ++ * Do NOT register NVMEM device for any partition that is meant to be ++ * handled by a U-Boot env driver. That would result in associating two ++ * different NVMEM devices with the same OF node. ++ * ++ * An example of unwanted behaviour of above (forwardtrace): ++ * of_get_mac_addr_nvmem() ++ * of_nvmem_cell_get() ++ * __nvmem_device_get() ++ * ++ * We can't have __nvmem_device_get() return "mtdX" NVMEM device instead ++ * of U-Boot env NVMEM device. That would result in failing to find ++ * NVMEM cell. ++ * ++ * This issue seems to affect U-Boot env case only and will go away with ++ * switch to NVMEM layouts. ++ */ ++ if (of_device_is_compatible(node, "u-boot,env") || ++ of_device_is_compatible(node, "u-boot,env-redundant-bool") || ++ of_device_is_compatible(node, "u-boot,env-redundant-count") || ++ of_device_is_compatible(node, "brcm,env")) ++ return 0; ++ + config.id = -1; + config.dev = &mtd->dev; + config.name = dev_name(&mtd->dev); diff --git a/target/linux/generic/pending-6.6/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch b/target/linux/generic/pending-6.6/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch new file mode 100644 index 0000000000..c7da2f8834 --- /dev/null +++ b/target/linux/generic/pending-6.6/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch @@ -0,0 +1,245 @@ +From acacdac272927ae1d96e0bca51eb82899671eaea Mon Sep 17 00:00:00 2001 +From: John Thomson +Date: Fri, 25 Dec 2020 18:50:08 +1000 +Subject: [PATCH] mtd: spi-nor: write support for minor aligned partitions +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Do not prevent writing to mtd partitions where a partition boundary sits +on a minor erasesize boundary. +This addresses a FIXME that has been present since the start of the +linux git history: +/* Doesn't start on a boundary of major erase size */ +/* FIXME: Let it be writable if it is on a boundary of + * _minor_ erase size though */ + +Allow a uniform erase region spi-nor device to be configured +to use the non-uniform erase regions code path for an erase with: +CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE=y + +On supporting hardware (SECT_4K: majority of current SPI-NOR device) +provide the facility for an erase to use the least number +of SPI-NOR operations, as well as access to 4K erase without +requiring CONFIG_MTD_SPI_NOR_USE_4K_SECTORS + +Introduce erasesize_minor to the mtd struct, +the smallest erasesize supported by the device + +On existing devices, this is useful where write support is wanted +for data on a 4K partition, such as some u-boot-env partitions, +or RouterBoot soft_config, while still netting the performance +benefits of using 64K sectors + +Performance: +time mtd erase firmware +OpenWrt 5.10 ramips MT7621 w25q128jv 0xfc0000 partition length + +Without this patch +MTD_SPI_NOR_USE_4K_SECTORS=y |n +real 2m 11.66s |0m 50.86s +user 0m 0.00s |0m 0.00s +sys 1m 56.20s |0m 50.80s + +With this patch +MTD_SPI_NOR_USE_VARIABLE_ERASE=n|y |4K_SECTORS=y +real 0m 51.68s |0m 50.85s |2m 12.89s +user 0m 0.00s |0m 0.00s |0m 0.01s +sys 0m 46.94s |0m 50.38s |2m 12.46s + +Signed-off-by: John Thomson +Signed-off-by: Thibaut VARÈNE + +--- + +checkpatch does not like the printk(KERN_WARNING +these should be changed separately beforehand? + +Changes v1 -> v2: +Added mtdcore sysfs for erasesize_minor +Removed finding minor erasesize for variable erase regions device, +as untested and no responses regarding it. +Moved IF_ENABLED for SPINOR variable erase to guard setting +erasesize_minor in spi-nor/core.c +Removed setting erasesize to minor where partition boundaries require +minor erase to be writable +Simplified minor boundary check by relying on minor being a factor of +major + +Changes RFC -> v1: +Fix uninitialized variable smatch warning +Reported-by: kernel test robot +Reported-by: Dan Carpenter +--- + drivers/mtd/mtdcore.c | 10 ++++++++++ + drivers/mtd/mtdpart.c | 35 +++++++++++++++++++++++++---------- + drivers/mtd/spi-nor/Kconfig | 10 ++++++++++ + drivers/mtd/spi-nor/core.c | 11 +++++++++-- + include/linux/mtd/mtd.h | 2 ++ + 5 files changed, 56 insertions(+), 12 deletions(-) + +--- a/drivers/mtd/mtdcore.c ++++ b/drivers/mtd/mtdcore.c +@@ -169,6 +169,15 @@ static ssize_t mtd_erasesize_show(struct + } + MTD_DEVICE_ATTR_RO(erasesize); + ++static ssize_t mtd_erasesize_minor_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct mtd_info *mtd = dev_get_drvdata(dev); ++ ++ return sysfs_emit(buf, "%lu\n", (unsigned long)mtd->erasesize_minor); ++} ++MTD_DEVICE_ATTR_RO(erasesize_minor); ++ + static ssize_t mtd_writesize_show(struct device *dev, + struct device_attribute *attr, char *buf) + { +@@ -314,6 +323,7 @@ static struct attribute *mtd_attrs[] = { + &dev_attr_flags.attr, + &dev_attr_size.attr, + &dev_attr_erasesize.attr, ++ &dev_attr_erasesize_minor.attr, + &dev_attr_writesize.attr, + &dev_attr_subpagesize.attr, + &dev_attr_oobsize.attr, +--- a/drivers/mtd/mtdpart.c ++++ b/drivers/mtd/mtdpart.c +@@ -41,6 +41,7 @@ static struct mtd_info *allocate_partiti + struct mtd_info *master = mtd_get_master(parent); + int wr_alignment = (parent->flags & MTD_NO_ERASE) ? + master->writesize : master->erasesize; ++ int wr_alignment_minor = 0; + u64 parent_size = mtd_is_partition(parent) ? + parent->part.size : parent->size; + struct mtd_info *child; +@@ -165,6 +166,7 @@ static struct mtd_info *allocate_partiti + } else { + /* Single erase size */ + child->erasesize = master->erasesize; ++ child->erasesize_minor = master->erasesize_minor; + } + + /* +@@ -172,26 +174,39 @@ static struct mtd_info *allocate_partiti + * exposes several regions with different erasesize. Adjust + * wr_alignment accordingly. + */ +- if (!(child->flags & MTD_NO_ERASE)) ++ if (!(child->flags & MTD_NO_ERASE)) { + wr_alignment = child->erasesize; ++ wr_alignment_minor = child->erasesize_minor; ++ } + + tmp = mtd_get_master_ofs(child, 0); + remainder = do_div(tmp, wr_alignment); + if ((child->flags & MTD_WRITEABLE) && remainder) { +- /* Doesn't start on a boundary of major erase size */ +- /* FIXME: Let it be writable if it is on a boundary of +- * _minor_ erase size though */ +- child->flags &= ~MTD_WRITEABLE; +- printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n", +- part->name); ++ if (wr_alignment_minor) { ++ /* rely on minor being a factor of major erasesize */ ++ tmp = remainder; ++ remainder = do_div(tmp, wr_alignment_minor); ++ } ++ if (remainder) { ++ child->flags &= ~MTD_WRITEABLE; ++ printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n", ++ part->name); ++ } + } + + tmp = mtd_get_master_ofs(child, 0) + child->part.size; + remainder = do_div(tmp, wr_alignment); + if ((child->flags & MTD_WRITEABLE) && remainder) { +- child->flags &= ~MTD_WRITEABLE; +- printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n", +- part->name); ++ if (wr_alignment_minor) { ++ tmp = remainder; ++ remainder = do_div(tmp, wr_alignment_minor); ++ } ++ ++ if (remainder) { ++ child->flags &= ~MTD_WRITEABLE; ++ printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n", ++ part->name); ++ } + } + + child->size = child->part.size; +--- a/drivers/mtd/spi-nor/Kconfig ++++ b/drivers/mtd/spi-nor/Kconfig +@@ -10,6 +10,16 @@ menuconfig MTD_SPI_NOR + + if MTD_SPI_NOR + ++config MTD_SPI_NOR_USE_VARIABLE_ERASE ++ bool "Disable uniform_erase to allow use of all hardware supported erasesizes" ++ depends on !MTD_SPI_NOR_USE_4K_SECTORS ++ default n ++ help ++ Allow mixed use of all hardware supported erasesizes, ++ by forcing spi_nor to use the multiple eraseregions code path. ++ For example: A 68K erase will use one 64K erase, and one 4K erase ++ on supporting hardware. ++ + config MTD_SPI_NOR_USE_4K_SECTORS + bool "Use small 4096 B erase sectors" + default y +--- a/drivers/mtd/spi-nor/core.c ++++ b/drivers/mtd/spi-nor/core.c +@@ -1050,6 +1050,8 @@ static u8 spi_nor_convert_3to4_erase(u8 + + static bool spi_nor_has_uniform_erase(const struct spi_nor *nor) + { ++ if (IS_ENABLED(CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE)) ++ return false; + return !!nor->params->erase_map.uniform_erase_type; + } + +@@ -2158,6 +2160,7 @@ static int spi_nor_select_erase(struct s + { + struct spi_nor_erase_map *map = &nor->params->erase_map; + const struct spi_nor_erase_type *erase = NULL; ++ const struct spi_nor_erase_type *erase_minor = NULL; + struct mtd_info *mtd = &nor->mtd; + u32 wanted_size = nor->info->sector_size; + int i; +@@ -2190,8 +2193,9 @@ static int spi_nor_select_erase(struct s + */ + for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) { + if (map->erase_type[i].size) { +- erase = &map->erase_type[i]; +- break; ++ if (!erase) ++ erase = &map->erase_type[i]; ++ erase_minor = &map->erase_type[i]; + } + } + +@@ -2199,6 +2203,9 @@ static int spi_nor_select_erase(struct s + return -EINVAL; + + mtd->erasesize = erase->size; ++ if (IS_ENABLED(CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE) && ++ erase_minor && erase_minor->size < erase->size) ++ mtd->erasesize_minor = erase_minor->size; + return 0; + } + +--- a/include/linux/mtd/mtd.h ++++ b/include/linux/mtd/mtd.h +@@ -245,6 +245,8 @@ struct mtd_info { + * information below if they desire + */ + uint32_t erasesize; ++ /* "Minor" (smallest) erase size supported by the whole device */ ++ uint32_t erasesize_minor; + /* Minimal writable flash unit size. In case of NOR flash it is 1 (even + * though individual bits can be cleared), in case of NAND flash it is + * one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR diff --git a/target/linux/generic/pending-6.6/420-mtd-redboot_space.patch b/target/linux/generic/pending-6.6/420-mtd-redboot_space.patch new file mode 100644 index 0000000000..5518ea71dd --- /dev/null +++ b/target/linux/generic/pending-6.6/420-mtd-redboot_space.patch @@ -0,0 +1,41 @@ +From: Felix Fietkau +Subject: add patch for including unpartitioned space in the rootfs partition for redboot devices (if applicable) + +[john@phrozen.org: used by ixp and others] + +lede-commit: 394918851f84e4d00fa16eb900e7700e95091f00 +Signed-off-by: Felix Fietkau +--- + drivers/mtd/redboot.c | 19 +++++++++++++------ + 1 file changed, 13 insertions(+), 6 deletions(-) + +--- a/drivers/mtd/parsers/redboot.c ++++ b/drivers/mtd/parsers/redboot.c +@@ -278,14 +278,21 @@ nogood: + #endif + names += strlen(names) + 1; + +-#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED + if (fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) { +- i++; +- parts[i].offset = parts[i - 1].size + parts[i - 1].offset; +- parts[i].size = fl->next->img->flash_base - parts[i].offset; +- parts[i].name = nullname; +- } ++ if (!strcmp(parts[i].name, "rootfs")) { ++ parts[i].size = fl->next->img->flash_base; ++ parts[i].size &= ~(master->erasesize - 1); ++ parts[i].size -= parts[i].offset; ++#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED ++ nrparts--; ++ } else { ++ i++; ++ parts[i].offset = parts[i-1].size + parts[i-1].offset; ++ parts[i].size = fl->next->img->flash_base - parts[i].offset; ++ parts[i].name = nullname; + #endif ++ } ++ } + tmp_fl = fl; + fl = fl->next; + kfree(tmp_fl); diff --git a/target/linux/generic/pending-6.6/430-mtd-add-myloader-partition-parser.patch b/target/linux/generic/pending-6.6/430-mtd-add-myloader-partition-parser.patch new file mode 100644 index 0000000000..35e80d6dcc --- /dev/null +++ b/target/linux/generic/pending-6.6/430-mtd-add-myloader-partition-parser.patch @@ -0,0 +1,229 @@ +From: Florian Fainelli +Subject: Add myloader partition table parser + +[john@phozen.org: shoud be upstreamable] + +lede-commit: d8bf22859b51faa09d22c056fe221a45d2f7a3b8 +Signed-off-by: Florian Fainelli +[adjust for kernel 5.4, add myloader.c to patch] +Signed-off-by: Adrian Schmutzler + +--- a/drivers/mtd/parsers/Kconfig ++++ b/drivers/mtd/parsers/Kconfig +@@ -67,6 +67,22 @@ config MTD_CMDLINE_PARTS + + If unsure, say 'N'. + ++config MTD_MYLOADER_PARTS ++ tristate "MyLoader partition parsing" ++ depends on ADM5120 || ATH79 ++ help ++ MyLoader is a bootloader which allows the user to define partitions ++ in flash devices, by putting a table in the second erase block ++ on the device, similar to a partition table. This table gives the ++ offsets and lengths of the user defined partitions. ++ ++ If you need code which can detect and parse these tables, and ++ register MTD 'partitions' corresponding to each image detected, ++ enable this option. ++ ++ You will still need the parsing functions to be called by the driver ++ for your particular device. It won't happen automatically. ++ + config MTD_OF_PARTS + tristate "OpenFirmware (device tree) partitioning parser" + default y +--- a/drivers/mtd/parsers/Makefile ++++ b/drivers/mtd/parsers/Makefile +@@ -4,6 +4,7 @@ obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm4 + obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o + obj-$(CONFIG_MTD_BRCM_U_BOOT) += brcm_u-boot.o + obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o ++obj-$(CONFIG_MTD_MYLOADER_PARTS) += myloader.o + obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o + ofpart-y += ofpart_core.o + ofpart-$(CONFIG_MTD_OF_PARTS_BCM4908) += ofpart_bcm4908.o +--- /dev/null ++++ b/drivers/mtd/parsers/myloader.c +@@ -0,0 +1,181 @@ ++/* ++ * Parse MyLoader-style flash partition tables and produce a Linux partition ++ * array to match. ++ * ++ * Copyright (C) 2007-2009 Gabor Juhos ++ * ++ * This file was based on drivers/mtd/redboot.c ++ * Author: Red Hat, Inc. - David Woodhouse ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define BLOCK_LEN_MIN 0x10000 ++#define PART_NAME_LEN 32 ++ ++struct part_data { ++ struct mylo_partition_table tab; ++ char names[MYLO_MAX_PARTITIONS][PART_NAME_LEN]; ++}; ++ ++static int myloader_parse_partitions(struct mtd_info *master, ++ const struct mtd_partition **pparts, ++ struct mtd_part_parser_data *data) ++{ ++ struct part_data *buf; ++ struct mylo_partition_table *tab; ++ struct mylo_partition *part; ++ struct mtd_partition *mtd_parts; ++ struct mtd_partition *mtd_part; ++ int num_parts; ++ int ret, i; ++ size_t retlen; ++ char *names; ++ unsigned long offset; ++ unsigned long blocklen; ++ ++ buf = vmalloc(sizeof(*buf)); ++ if (!buf) { ++ return -ENOMEM; ++ goto out; ++ } ++ tab = &buf->tab; ++ ++ blocklen = master->erasesize; ++ if (blocklen < BLOCK_LEN_MIN) ++ blocklen = BLOCK_LEN_MIN; ++ ++ offset = blocklen; ++ ++ /* Find the partition table */ ++ for (i = 0; i < 4; i++, offset += blocklen) { ++ printk(KERN_DEBUG "%s: searching for MyLoader partition table" ++ " at offset 0x%lx\n", master->name, offset); ++ ++ ret = mtd_read(master, offset, sizeof(*buf), &retlen, ++ (void *)buf); ++ if (ret) ++ goto out_free_buf; ++ ++ if (retlen != sizeof(*buf)) { ++ ret = -EIO; ++ goto out_free_buf; ++ } ++ ++ /* Check for Partition Table magic number */ ++ if (tab->magic == le32_to_cpu(MYLO_MAGIC_PARTITIONS)) ++ break; ++ ++ } ++ ++ if (tab->magic != le32_to_cpu(MYLO_MAGIC_PARTITIONS)) { ++ printk(KERN_DEBUG "%s: no MyLoader partition table found\n", ++ master->name); ++ ret = 0; ++ goto out_free_buf; ++ } ++ ++ /* The MyLoader and the Partition Table is always present */ ++ num_parts = 2; ++ ++ /* Detect number of used partitions */ ++ for (i = 0; i < MYLO_MAX_PARTITIONS; i++) { ++ part = &tab->partitions[i]; ++ ++ if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE) ++ continue; ++ ++ num_parts++; ++ } ++ ++ mtd_parts = kzalloc((num_parts * sizeof(*mtd_part) + ++ num_parts * PART_NAME_LEN), GFP_KERNEL); ++ ++ if (!mtd_parts) { ++ ret = -ENOMEM; ++ goto out_free_buf; ++ } ++ ++ mtd_part = mtd_parts; ++ names = (char *)&mtd_parts[num_parts]; ++ ++ strncpy(names, "myloader", PART_NAME_LEN); ++ mtd_part->name = names; ++ mtd_part->offset = 0; ++ mtd_part->size = offset; ++ mtd_part->mask_flags = MTD_WRITEABLE; ++ mtd_part++; ++ names += PART_NAME_LEN; ++ ++ strncpy(names, "partition_table", PART_NAME_LEN); ++ mtd_part->name = names; ++ mtd_part->offset = offset; ++ mtd_part->size = blocklen; ++ mtd_part->mask_flags = MTD_WRITEABLE; ++ mtd_part++; ++ names += PART_NAME_LEN; ++ ++ for (i = 0; i < MYLO_MAX_PARTITIONS; i++) { ++ part = &tab->partitions[i]; ++ ++ if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE) ++ continue; ++ ++ if ((buf->names[i][0]) && (buf->names[i][0] != '\xff')) ++ strncpy(names, buf->names[i], PART_NAME_LEN); ++ else ++ snprintf(names, PART_NAME_LEN, "partition%d", i); ++ ++ mtd_part->offset = le32_to_cpu(part->addr); ++ mtd_part->size = le32_to_cpu(part->size); ++ mtd_part->name = names; ++ mtd_part++; ++ names += PART_NAME_LEN; ++ } ++ ++ *pparts = mtd_parts; ++ ret = num_parts; ++ ++ out_free_buf: ++ vfree(buf); ++ out: ++ return ret; ++} ++ ++static struct mtd_part_parser myloader_mtd_parser = { ++ .owner = THIS_MODULE, ++ .parse_fn = myloader_parse_partitions, ++ .name = "MyLoader", ++}; ++ ++static int __init myloader_mtd_parser_init(void) ++{ ++ register_mtd_parser(&myloader_mtd_parser); ++ ++ return 0; ++} ++ ++static void __exit myloader_mtd_parser_exit(void) ++{ ++ deregister_mtd_parser(&myloader_mtd_parser); ++} ++ ++module_init(myloader_mtd_parser_init); ++module_exit(myloader_mtd_parser_exit); ++ ++MODULE_AUTHOR("Gabor Juhos "); ++MODULE_DESCRIPTION("Parsing code for MyLoader partition tables"); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/generic/pending-6.6/431-mtd-bcm47xxpart-check-for-bad-blocks-when-calculatin.patch b/target/linux/generic/pending-6.6/431-mtd-bcm47xxpart-check-for-bad-blocks-when-calculatin.patch new file mode 100644 index 0000000000..bcea45d009 --- /dev/null +++ b/target/linux/generic/pending-6.6/431-mtd-bcm47xxpart-check-for-bad-blocks-when-calculatin.patch @@ -0,0 +1,68 @@ +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Subject: [PATCH] mtd: bcm47xxpart: check for bad blocks when calculating offsets + +Signed-off-by: Rafał Miłecki +--- + +--- a/drivers/mtd/parsers/parser_trx.c ++++ b/drivers/mtd/parsers/parser_trx.c +@@ -25,6 +25,33 @@ struct trx_header { + uint32_t offset[3]; + } __packed; + ++/* ++ * Calculate real end offset (address) for a given amount of data. It checks ++ * all blocks skipping bad ones. ++ */ ++static size_t parser_trx_real_offset(struct mtd_info *mtd, size_t bytes) ++{ ++ size_t real_offset = 0; ++ ++ if (mtd_block_isbad(mtd, real_offset)) ++ pr_warn("Base offset shouldn't be at bad block"); ++ ++ while (bytes >= mtd->erasesize) { ++ bytes -= mtd->erasesize; ++ real_offset += mtd->erasesize; ++ while (mtd_block_isbad(mtd, real_offset)) { ++ real_offset += mtd->erasesize; ++ ++ if (real_offset >= mtd->size) ++ return real_offset - mtd->erasesize; ++ } ++ } ++ ++ real_offset += bytes; ++ ++ return real_offset; ++} ++ + static const char *parser_trx_data_part_name(struct mtd_info *master, + size_t offset) + { +@@ -86,21 +113,21 @@ static int parser_trx_parse(struct mtd_i + if (trx.offset[2]) { + part = &parts[curr_part++]; + part->name = "loader"; +- part->offset = trx.offset[i]; ++ part->offset = parser_trx_real_offset(mtd, trx.offset[i]); + i++; + } + + if (trx.offset[i]) { + part = &parts[curr_part++]; + part->name = "linux"; +- part->offset = trx.offset[i]; ++ part->offset = parser_trx_real_offset(mtd, trx.offset[i]); + i++; + } + + if (trx.offset[i]) { + part = &parts[curr_part++]; +- part->name = parser_trx_data_part_name(mtd, trx.offset[i]); +- part->offset = trx.offset[i]; ++ part->offset = parser_trx_real_offset(mtd, trx.offset[i]); ++ part->name = parser_trx_data_part_name(mtd, part->offset); + i++; + } + diff --git a/target/linux/generic/pending-6.6/432-mtd-bcm47xxpart-detect-T_Meter-partition.patch b/target/linux/generic/pending-6.6/432-mtd-bcm47xxpart-detect-T_Meter-partition.patch new file mode 100644 index 0000000000..852654d924 --- /dev/null +++ b/target/linux/generic/pending-6.6/432-mtd-bcm47xxpart-detect-T_Meter-partition.patch @@ -0,0 +1,37 @@ +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Subject: mtd: bcm47xxpart: detect T_Meter partition + +It can be found on many Netgear devices. It consists of many 0x30 blocks +starting with 4D 54. + +Signed-off-by: Rafał Miłecki +--- + drivers/mtd/bcm47xxpart.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/drivers/mtd/parsers/bcm47xxpart.c ++++ b/drivers/mtd/parsers/bcm47xxpart.c +@@ -35,6 +35,7 @@ + #define NVRAM_HEADER 0x48534C46 /* FLSH */ + #define POT_MAGIC1 0x54544f50 /* POTT */ + #define POT_MAGIC2 0x504f /* OP */ ++#define T_METER_MAGIC 0x4D540000 /* MT */ + #define ML_MAGIC1 0x39685a42 + #define ML_MAGIC2 0x26594131 + #define TRX_MAGIC 0x30524448 +@@ -178,6 +179,15 @@ static int bcm47xxpart_parse(struct mtd_ + MTD_WRITEABLE); + continue; + } ++ ++ /* T_Meter */ ++ if ((le32_to_cpu(buf[0x000 / 4]) & 0xFFFF0000) == T_METER_MAGIC && ++ (le32_to_cpu(buf[0x030 / 4]) & 0xFFFF0000) == T_METER_MAGIC && ++ (le32_to_cpu(buf[0x060 / 4]) & 0xFFFF0000) == T_METER_MAGIC) { ++ bcm47xxpart_add_part(&parts[curr_part++], "T_Meter", offset, ++ MTD_WRITEABLE); ++ continue; ++ } + + /* TRX */ + if (buf[0x000 / 4] == TRX_MAGIC) { diff --git a/target/linux/generic/pending-6.6/435-mtd-add-routerbootpart-parser-config.patch b/target/linux/generic/pending-6.6/435-mtd-add-routerbootpart-parser-config.patch new file mode 100644 index 0000000000..a42dcc868f --- /dev/null +++ b/target/linux/generic/pending-6.6/435-mtd-add-routerbootpart-parser-config.patch @@ -0,0 +1,38 @@ +From 4437e01fb6bca63fccdba5d6c44888b0935885c2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Thibaut=20VAR=C3=88NE?= +Date: Tue, 24 Mar 2020 11:45:07 +0100 +Subject: [PATCH] generic: routerboot partition build bits (5.4) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This patch adds routerbootpart kernel build bits + +Signed-off-by: Thibaut VARÈNE +--- + drivers/mtd/parsers/Kconfig | 9 +++++++++ + drivers/mtd/parsers/Makefile | 1 + + 2 files changed, 10 insertions(+) + +--- a/drivers/mtd/parsers/Kconfig ++++ b/drivers/mtd/parsers/Kconfig +@@ -236,3 +236,12 @@ config MTD_SERCOMM_PARTS + partition map. This partition table contains real partition + offsets, which may differ from device to device depending on the + number and location of bad blocks on NAND. ++ ++config MTD_ROUTERBOOT_PARTS ++ tristate "RouterBoot flash partition parser" ++ depends on MTD && OF ++ help ++ MikroTik RouterBoot is implemented as a multi segment system on the ++ flash, some of which are fixed and some of which are located at ++ variable offsets. This parser handles both cases via properly ++ formatted DTS. +--- a/drivers/mtd/parsers/Makefile ++++ b/drivers/mtd/parsers/Makefile +@@ -17,3 +17,4 @@ obj-$(CONFIG_MTD_SERCOMM_PARTS) += scpa + obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o + obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o + obj-$(CONFIG_MTD_QCOMSMEM_PARTS) += qcomsmempart.o ++obj-$(CONFIG_MTD_ROUTERBOOT_PARTS) += routerbootpart.o diff --git a/target/linux/generic/pending-6.6/450-01-dt-bindings-mtd-add-basic-bindings-for-UBI.patch b/target/linux/generic/pending-6.6/450-01-dt-bindings-mtd-add-basic-bindings-for-UBI.patch new file mode 100644 index 0000000000..063d3fa79c --- /dev/null +++ b/target/linux/generic/pending-6.6/450-01-dt-bindings-mtd-add-basic-bindings-for-UBI.patch @@ -0,0 +1,121 @@ +From ffbbe7d66872ff8957dad2136133e28a1fd5d437 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 7 Aug 2023 22:51:05 +0100 +Subject: [PATCH 01/15] dt-bindings: mtd: add basic bindings for UBI + +Add basic bindings for UBI devices and volumes. + +Signed-off-by: Daniel Golle +--- + .../bindings/mtd/partitions/linux,ubi.yaml | 65 +++++++++++++++++++ + .../bindings/mtd/partitions/ubi-volume.yaml | 35 ++++++++++ + 2 files changed, 100 insertions(+) + create mode 100644 Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml + create mode 100644 Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml +@@ -0,0 +1,65 @@ ++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/mtd/partitions/linux,ubi.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Unsorted Block Images ++ ++description: | ++ UBI ("Unsorted Block Images") is a volume management system for raw ++ flash devices which manages multiple logical volumes on a single ++ physical flash device and spreads the I/O load (i.e wear-leveling) ++ across the whole flash chip. ++ ++maintainers: ++ - Daniel Golle ++ ++allOf: ++ - $ref: partition.yaml# ++ ++properties: ++ compatible: ++ const: linux,ubi ++ ++ volumes: ++ type: object ++ description: UBI Volumes ++ ++ patternProperties: ++ "^ubi-volume-.*$": ++ $ref: /schemas/mtd/partitions/ubi-volume.yaml# ++ ++ unevaluatedProperties: false ++ ++required: ++ - compatible ++ ++unevaluatedProperties: false ++ ++examples: ++ - | ++ partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ partition@0 { ++ reg = <0x0 0x100000>; ++ label = "bootloader"; ++ read-only; ++ }; ++ ++ partition@100000 { ++ reg = <0x100000 0x1ff00000>; ++ label = "ubi"; ++ compatible = "linux,ubi"; ++ ++ volumes { ++ ubi-volume-caldata { ++ volid = <2>; ++ volname = "rf"; ++ }; ++ }; ++ }; ++ }; +--- /dev/null ++++ b/Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml +@@ -0,0 +1,35 @@ ++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/mtd/partitions/ubi-volume.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: UBI volume ++ ++description: | ++ This binding describes a single UBI volume. Volumes can be matches either ++ by their ID or their name, or both. ++ ++maintainers: ++ - Daniel Golle ++ ++properties: ++ volid: ++ $ref: "/schemas/types.yaml#/definitions/uint32" ++ description: ++ Match UBI volume ID ++ ++ volname: ++ $ref: "/schemas/types.yaml#/definitions/string" ++ description: ++ Match UBI volume ID ++ ++anyOf: ++ - required: ++ - volid ++ ++ - required: ++ - volname ++ ++# This is a generic file other binding inherit from and extend ++additionalProperties: true diff --git a/target/linux/generic/pending-6.6/450-02-dt-bindings-mtd-ubi-volume-allow-UBI-volumes-to-prov.patch b/target/linux/generic/pending-6.6/450-02-dt-bindings-mtd-ubi-volume-allow-UBI-volumes-to-prov.patch new file mode 100644 index 0000000000..823c8e83b7 --- /dev/null +++ b/target/linux/generic/pending-6.6/450-02-dt-bindings-mtd-ubi-volume-allow-UBI-volumes-to-prov.patch @@ -0,0 +1,48 @@ +From e4dad3aa5c3ab9c553555dd23c0b85f725f2eb51 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 7 Aug 2023 22:53:01 +0100 +Subject: [PATCH 02/15] dt-bindings: mtd: ubi-volume: allow UBI volumes to + provide NVMEM + +UBI volumes may be used to contain NVMEM bits, typically device MAC +addresses or wireless radio calibration data. + +Signed-off-by: Daniel Golle +--- + .../devicetree/bindings/mtd/partitions/linux,ubi.yaml | 10 ++++++++++ + .../devicetree/bindings/mtd/partitions/ubi-volume.yaml | 5 +++++ + 2 files changed, 15 insertions(+) + +--- a/Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml ++++ b/Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml +@@ -59,6 +59,16 @@ examples: + ubi-volume-caldata { + volid = <2>; + volname = "rf"; ++ ++ nvmem-layout { ++ compatible = "fixed-layout"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ eeprom@0 { ++ reg = <0x0 0x1000>; ++ }; ++ }; + }; + }; + }; +--- a/Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml ++++ b/Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml +@@ -24,6 +24,11 @@ properties: + description: + Match UBI volume ID + ++ nvmem-layout: ++ $ref: /schemas/nvmem/layouts/nvmem-layout.yaml# ++ description: ++ This container may reference an NVMEM layout parser. ++ + anyOf: + - required: + - volid diff --git a/target/linux/generic/pending-6.6/450-03-mtd-ubi-block-use-notifier-to-create-ubiblock-from-p.patch b/target/linux/generic/pending-6.6/450-03-mtd-ubi-block-use-notifier-to-create-ubiblock-from-p.patch new file mode 100644 index 0000000000..eda3b108da --- /dev/null +++ b/target/linux/generic/pending-6.6/450-03-mtd-ubi-block-use-notifier-to-create-ubiblock-from-p.patch @@ -0,0 +1,225 @@ +From e5cf19bd8204925f3bd2067df9e867313eac388b Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 1 May 2023 11:57:51 +0100 +Subject: [PATCH 03/15] mtd: ubi: block: use notifier to create ubiblock from + parameter + +Use UBI_VOLUME_ADDED notification to create ubiblock device specified +on kernel cmdline or module parameter. +This makes thing more simple and has the advantage that ubiblock devices +on volumes which are not present at the time the ubi module is probed +will still be created. + +Suggested-by: Zhihao Cheng +Signed-off-by: Daniel Golle +--- + drivers/mtd/ubi/block.c | 154 ++++++++++++++++++++++------------------ + 1 file changed, 85 insertions(+), 69 deletions(-) + +--- a/drivers/mtd/ubi/block.c ++++ b/drivers/mtd/ubi/block.c +@@ -33,6 +33,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -67,10 +68,10 @@ struct ubiblock_pdu { + }; + + /* Numbers of elements set in the @ubiblock_param array */ +-static int ubiblock_devs __initdata; ++static int ubiblock_devs; + + /* MTD devices specification parameters */ +-static struct ubiblock_param ubiblock_param[UBIBLOCK_MAX_DEVICES] __initdata; ++static struct ubiblock_param ubiblock_param[UBIBLOCK_MAX_DEVICES]; + + struct ubiblock { + struct ubi_volume_desc *desc; +@@ -504,7 +505,7 @@ int ubiblock_remove(struct ubi_volume_in + } + + /* Found a device, let's lock it so we can check if it's busy */ +- mutex_lock(&dev->dev_mutex); ++ mutex_lock_nested(&dev->dev_mutex, SINGLE_DEPTH_NESTING); + if (dev->refcnt > 0) { + ret = -EBUSY; + goto out_unlock_dev; +@@ -567,6 +568,85 @@ static int ubiblock_resize(struct ubi_vo + return 0; + } + ++static bool ++match_volume_desc(struct ubi_volume_info *vi, const char *name, int ubi_num, int vol_id) ++{ ++ int err, len; ++ struct path path; ++ struct kstat stat; ++ ++ if (ubi_num == -1) { ++ /* No ubi num, name must be a vol device path */ ++ err = kern_path(name, LOOKUP_FOLLOW, &path); ++ if (err) ++ return false; ++ ++ err = vfs_getattr(&path, &stat, STATX_TYPE, AT_STATX_SYNC_AS_STAT); ++ path_put(&path); ++ if (err) ++ return false; ++ ++ if (!S_ISCHR(stat.mode)) ++ return false; ++ ++ if (vi->ubi_num != ubi_major2num(MAJOR(stat.rdev))) ++ return false; ++ ++ if (vi->vol_id != MINOR(stat.rdev) - 1) ++ return false; ++ ++ return true; ++ } ++ ++ if (vol_id == -1) { ++ if (vi->ubi_num != ubi_num) ++ return false; ++ ++ len = strnlen(name, UBI_VOL_NAME_MAX + 1); ++ if (len < 1 || vi->name_len != len) ++ return false; ++ ++ if (strcmp(name, vi->name)) ++ return false; ++ ++ return true; ++ } ++ ++ if (vi->ubi_num != ubi_num) ++ return false; ++ ++ if (vi->vol_id != vol_id) ++ return false; ++ ++ return true; ++} ++ ++static void ++ubiblock_create_from_param(struct ubi_volume_info *vi) ++{ ++ int i, ret = 0; ++ struct ubiblock_param *p; ++ ++ /* ++ * Iterate over ubiblock cmdline parameters. If a parameter matches the ++ * newly added volume create the ubiblock device for it. ++ */ ++ for (i = 0; i < ubiblock_devs; i++) { ++ p = &ubiblock_param[i]; ++ ++ if (!match_volume_desc(vi, p->name, p->ubi_num, p->vol_id)) ++ continue; ++ ++ ret = ubiblock_create(vi); ++ if (ret) { ++ pr_err( ++ "UBI: block: can't add '%s' volume on ubi%d_%d, err=%d\n", ++ vi->name, p->ubi_num, p->vol_id, ret); ++ } ++ break; ++ } ++} ++ + static int ubiblock_notify(struct notifier_block *nb, + unsigned long notification_type, void *ns_ptr) + { +@@ -574,10 +654,7 @@ static int ubiblock_notify(struct notifi + + switch (notification_type) { + case UBI_VOLUME_ADDED: +- /* +- * We want to enforce explicit block device creation for +- * volumes, so when a volume is added we do nothing. +- */ ++ ubiblock_create_from_param(&nt->vi); + break; + case UBI_VOLUME_REMOVED: + ubiblock_remove(&nt->vi); +@@ -603,56 +680,6 @@ static struct notifier_block ubiblock_no + .notifier_call = ubiblock_notify, + }; + +-static struct ubi_volume_desc * __init +-open_volume_desc(const char *name, int ubi_num, int vol_id) +-{ +- if (ubi_num == -1) +- /* No ubi num, name must be a vol device path */ +- return ubi_open_volume_path(name, UBI_READONLY); +- else if (vol_id == -1) +- /* No vol_id, must be vol_name */ +- return ubi_open_volume_nm(ubi_num, name, UBI_READONLY); +- else +- return ubi_open_volume(ubi_num, vol_id, UBI_READONLY); +-} +- +-static void __init ubiblock_create_from_param(void) +-{ +- int i, ret = 0; +- struct ubiblock_param *p; +- struct ubi_volume_desc *desc; +- struct ubi_volume_info vi; +- +- /* +- * If there is an error creating one of the ubiblocks, continue on to +- * create the following ubiblocks. This helps in a circumstance where +- * the kernel command-line specifies multiple block devices and some +- * may be broken, but we still want the working ones to come up. +- */ +- for (i = 0; i < ubiblock_devs; i++) { +- p = &ubiblock_param[i]; +- +- desc = open_volume_desc(p->name, p->ubi_num, p->vol_id); +- if (IS_ERR(desc)) { +- pr_err( +- "UBI: block: can't open volume on ubi%d_%d, err=%ld\n", +- p->ubi_num, p->vol_id, PTR_ERR(desc)); +- continue; +- } +- +- ubi_get_volume_info(desc, &vi); +- ubi_close_volume(desc); +- +- ret = ubiblock_create(&vi); +- if (ret) { +- pr_err( +- "UBI: block: can't add '%s' volume on ubi%d_%d, err=%d\n", +- vi.name, p->ubi_num, p->vol_id, ret); +- continue; +- } +- } +-} +- + static void ubiblock_remove_all(void) + { + struct ubiblock *next; +@@ -678,18 +705,7 @@ int __init ubiblock_init(void) + if (ubiblock_major < 0) + return ubiblock_major; + +- /* +- * Attach block devices from 'block=' module param. +- * Even if one block device in the param list fails to come up, +- * still allow the module to load and leave any others up. +- */ +- ubiblock_create_from_param(); +- +- /* +- * Block devices are only created upon user requests, so we ignore +- * existing volumes. +- */ +- ret = ubi_register_volume_notifier(&ubiblock_notifier, 1); ++ ret = ubi_register_volume_notifier(&ubiblock_notifier, 0); + if (ret) + goto err_unreg; + return 0; diff --git a/target/linux/generic/pending-6.6/450-04-mtd-ubi-attach-from-device-tree.patch b/target/linux/generic/pending-6.6/450-04-mtd-ubi-attach-from-device-tree.patch new file mode 100644 index 0000000000..6e10e5ebed --- /dev/null +++ b/target/linux/generic/pending-6.6/450-04-mtd-ubi-attach-from-device-tree.patch @@ -0,0 +1,264 @@ +From 471a17d8d1b838092d1a76e48cdce8b5b67ff809 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 27 Nov 2023 01:54:28 +0000 +Subject: [PATCH 04/15] mtd: ubi: attach from device tree + +Introduce device tree compatible 'linux,ubi' and attach compatible MTD +devices using the MTD add notifier. This is needed for a UBI device to +be available early at boot (and not only after late_initcall), so +volumes on them can be used eg. as NVMEM providers for other drivers. + +Signed-off-by: Daniel Golle +--- + drivers/mtd/ubi/build.c | 146 ++++++++++++++++++++++++++++------------ + drivers/mtd/ubi/cdev.c | 2 +- + drivers/mtd/ubi/ubi.h | 2 +- + 3 files changed, 106 insertions(+), 44 deletions(-) + +--- a/drivers/mtd/ubi/build.c ++++ b/drivers/mtd/ubi/build.c +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + #include + #include + #include "ubi.h" +@@ -1071,6 +1072,7 @@ out_free: + * ubi_detach_mtd_dev - detach an MTD device. + * @ubi_num: UBI device number to detach from + * @anyway: detach MTD even if device reference count is not zero ++ * @have_lock: called by MTD notifier holding mtd_table_mutex + * + * This function destroys an UBI device number @ubi_num and detaches the + * underlying MTD device. Returns zero in case of success and %-EBUSY if the +@@ -1080,7 +1082,7 @@ out_free: + * Note, the invocations of this function has to be serialized by the + * @ubi_devices_mutex. + */ +-int ubi_detach_mtd_dev(int ubi_num, int anyway) ++int ubi_detach_mtd_dev(int ubi_num, int anyway, bool have_lock) + { + struct ubi_device *ubi; + +@@ -1136,7 +1138,11 @@ int ubi_detach_mtd_dev(int ubi_num, int + vfree(ubi->peb_buf); + vfree(ubi->fm_buf); + ubi_msg(ubi, "mtd%d is detached", ubi->mtd->index); +- put_mtd_device(ubi->mtd); ++ if (have_lock) ++ __put_mtd_device(ubi->mtd); ++ else ++ put_mtd_device(ubi->mtd); ++ + put_device(&ubi->dev); + return 0; + } +@@ -1213,43 +1219,43 @@ static struct mtd_info * __init open_mtd + return mtd; + } + +-static int __init ubi_init(void) ++static void ubi_notify_add(struct mtd_info *mtd) + { +- int err, i, k; ++ struct device_node *np = mtd_get_of_node(mtd); ++ int err; + +- /* Ensure that EC and VID headers have correct size */ +- BUILD_BUG_ON(sizeof(struct ubi_ec_hdr) != 64); +- BUILD_BUG_ON(sizeof(struct ubi_vid_hdr) != 64); ++ if (!of_device_is_compatible(np, "linux,ubi")) ++ return; + +- if (mtd_devs > UBI_MAX_DEVICES) { +- pr_err("UBI error: too many MTD devices, maximum is %d\n", +- UBI_MAX_DEVICES); +- return -EINVAL; +- } ++ /* ++ * we are already holding &mtd_table_mutex, but still need ++ * to bump refcount ++ */ ++ err = __get_mtd_device(mtd); ++ if (err) ++ return; + +- /* Create base sysfs directory and sysfs files */ +- err = class_register(&ubi_class); ++ /* called while holding mtd_table_mutex */ ++ mutex_lock_nested(&ubi_devices_mutex, SINGLE_DEPTH_NESTING); ++ err = ubi_attach_mtd_dev(mtd, UBI_DEV_NUM_AUTO, 0, 0, false); ++ mutex_unlock(&ubi_devices_mutex); + if (err < 0) +- return err; +- +- err = misc_register(&ubi_ctrl_cdev); +- if (err) { +- pr_err("UBI error: cannot register device\n"); +- goto out; +- } ++ __put_mtd_device(mtd); ++} + +- ubi_wl_entry_slab = kmem_cache_create("ubi_wl_entry_slab", +- sizeof(struct ubi_wl_entry), +- 0, 0, NULL); +- if (!ubi_wl_entry_slab) { +- err = -ENOMEM; +- goto out_dev_unreg; +- } ++static void ubi_notify_remove(struct mtd_info *mtd) ++{ ++ WARN(1, "mtd%d removed despite UBI still being attached", mtd->index); ++} + +- err = ubi_debugfs_init(); +- if (err) +- goto out_slab; ++static struct mtd_notifier ubi_mtd_notifier = { ++ .add = ubi_notify_add, ++ .remove = ubi_notify_remove, ++}; + ++static int __init ubi_init_attach(void) ++{ ++ int err, i, k; + + /* Attach MTD devices */ + for (i = 0; i < mtd_devs; i++) { +@@ -1297,25 +1303,79 @@ static int __init ubi_init(void) + } + } + ++ return 0; ++ ++out_detach: ++ for (k = 0; k < i; k++) ++ if (ubi_devices[k]) { ++ mutex_lock(&ubi_devices_mutex); ++ ubi_detach_mtd_dev(ubi_devices[k]->ubi_num, 1, false); ++ mutex_unlock(&ubi_devices_mutex); ++ } ++ return err; ++} ++#ifndef CONFIG_MTD_UBI_MODULE ++late_initcall(ubi_init_attach); ++#endif ++ ++static int __init ubi_init(void) ++{ ++ int err; ++ ++ /* Ensure that EC and VID headers have correct size */ ++ BUILD_BUG_ON(sizeof(struct ubi_ec_hdr) != 64); ++ BUILD_BUG_ON(sizeof(struct ubi_vid_hdr) != 64); ++ ++ if (mtd_devs > UBI_MAX_DEVICES) { ++ pr_err("UBI error: too many MTD devices, maximum is %d\n", ++ UBI_MAX_DEVICES); ++ return -EINVAL; ++ } ++ ++ /* Create base sysfs directory and sysfs files */ ++ err = class_register(&ubi_class); ++ if (err < 0) ++ return err; ++ ++ err = misc_register(&ubi_ctrl_cdev); ++ if (err) { ++ pr_err("UBI error: cannot register device\n"); ++ goto out; ++ } ++ ++ ubi_wl_entry_slab = kmem_cache_create("ubi_wl_entry_slab", ++ sizeof(struct ubi_wl_entry), ++ 0, 0, NULL); ++ if (!ubi_wl_entry_slab) { ++ err = -ENOMEM; ++ goto out_dev_unreg; ++ } ++ ++ err = ubi_debugfs_init(); ++ if (err) ++ goto out_slab; ++ + err = ubiblock_init(); + if (err) { + pr_err("UBI error: block: cannot initialize, error %d\n", err); + + /* See comment above re-ubi_is_module(). */ + if (ubi_is_module()) +- goto out_detach; ++ goto out_slab; ++ } ++ ++ register_mtd_user(&ubi_mtd_notifier); ++ ++ if (ubi_is_module()) { ++ err = ubi_init_attach(); ++ if (err) ++ goto out_mtd_notifier; + } + + return 0; + +-out_detach: +- for (k = 0; k < i; k++) +- if (ubi_devices[k]) { +- mutex_lock(&ubi_devices_mutex); +- ubi_detach_mtd_dev(ubi_devices[k]->ubi_num, 1); +- mutex_unlock(&ubi_devices_mutex); +- } +- ubi_debugfs_exit(); ++out_mtd_notifier: ++ unregister_mtd_user(&ubi_mtd_notifier); + out_slab: + kmem_cache_destroy(ubi_wl_entry_slab); + out_dev_unreg: +@@ -1325,18 +1385,20 @@ out: + pr_err("UBI error: cannot initialize UBI, error %d\n", err); + return err; + } +-late_initcall(ubi_init); ++device_initcall(ubi_init); ++ + + static void __exit ubi_exit(void) + { + int i; + + ubiblock_exit(); ++ unregister_mtd_user(&ubi_mtd_notifier); + + for (i = 0; i < UBI_MAX_DEVICES; i++) + if (ubi_devices[i]) { + mutex_lock(&ubi_devices_mutex); +- ubi_detach_mtd_dev(ubi_devices[i]->ubi_num, 1); ++ ubi_detach_mtd_dev(ubi_devices[i]->ubi_num, 1, false); + mutex_unlock(&ubi_devices_mutex); + } + ubi_debugfs_exit(); +--- a/drivers/mtd/ubi/cdev.c ++++ b/drivers/mtd/ubi/cdev.c +@@ -1065,7 +1065,7 @@ static long ctrl_cdev_ioctl(struct file + } + + mutex_lock(&ubi_devices_mutex); +- err = ubi_detach_mtd_dev(ubi_num, 0); ++ err = ubi_detach_mtd_dev(ubi_num, 0, false); + mutex_unlock(&ubi_devices_mutex); + break; + } +--- a/drivers/mtd/ubi/ubi.h ++++ b/drivers/mtd/ubi/ubi.h +@@ -939,7 +939,7 @@ int ubi_io_write_vid_hdr(struct ubi_devi + int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, + int vid_hdr_offset, int max_beb_per1024, + bool disable_fm); +-int ubi_detach_mtd_dev(int ubi_num, int anyway); ++int ubi_detach_mtd_dev(int ubi_num, int anyway, bool have_lock); + struct ubi_device *ubi_get_device(int ubi_num); + void ubi_put_device(struct ubi_device *ubi); + struct ubi_device *ubi_get_by_major(int major); diff --git a/target/linux/generic/pending-6.6/450-05-mtd-ubi-introduce-pre-removal-notification-for-UBI-v.patch b/target/linux/generic/pending-6.6/450-05-mtd-ubi-introduce-pre-removal-notification-for-UBI-v.patch new file mode 100644 index 0000000000..d5da37b856 --- /dev/null +++ b/target/linux/generic/pending-6.6/450-05-mtd-ubi-introduce-pre-removal-notification-for-UBI-v.patch @@ -0,0 +1,226 @@ +From 2d664266cfdd114cc7a1fa28dd64275e99222455 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 8 Jun 2023 17:18:09 +0100 +Subject: [PATCH 05/15] mtd: ubi: introduce pre-removal notification for UBI + volumes + +Introduce a new notification type UBI_VOLUME_SHUTDOWN to inform users +that a volume is just about to be removed. +This is needed because users (such as the NVMEM subsystem) expect that +at the time their removal function is called, the parenting device is +still available (for removal of sysfs nodes, for example, in case of +NVMEM which otherwise WARNs on volume removal). + +Signed-off-by: Daniel Golle +--- + drivers/mtd/ubi/block.c | 26 ++++++++++++++++++++++++++ + drivers/mtd/ubi/build.c | 20 +++++++++++++++----- + drivers/mtd/ubi/kapi.c | 2 +- + drivers/mtd/ubi/ubi.h | 2 ++ + drivers/mtd/ubi/vmt.c | 17 +++++++++++++++-- + include/linux/mtd/ubi.h | 2 ++ + 6 files changed, 61 insertions(+), 8 deletions(-) + +--- a/drivers/mtd/ubi/block.c ++++ b/drivers/mtd/ubi/block.c +@@ -568,6 +568,29 @@ static int ubiblock_resize(struct ubi_vo + return 0; + } + ++static int ubiblock_shutdown(struct ubi_volume_info *vi) ++{ ++ struct ubiblock *dev; ++ struct gendisk *disk; ++ int ret = 0; ++ ++ mutex_lock(&devices_mutex); ++ dev = find_dev_nolock(vi->ubi_num, vi->vol_id); ++ if (!dev) { ++ ret = -ENODEV; ++ goto out_unlock; ++ } ++ disk = dev->gd; ++ ++out_unlock: ++ mutex_unlock(&devices_mutex); ++ ++ if (!ret) ++ blk_mark_disk_dead(disk); ++ ++ return ret; ++}; ++ + static bool + match_volume_desc(struct ubi_volume_info *vi, const char *name, int ubi_num, int vol_id) + { +@@ -659,6 +682,9 @@ static int ubiblock_notify(struct notifi + case UBI_VOLUME_REMOVED: + ubiblock_remove(&nt->vi); + break; ++ case UBI_VOLUME_SHUTDOWN: ++ ubiblock_shutdown(&nt->vi); ++ break; + case UBI_VOLUME_RESIZED: + ubiblock_resize(&nt->vi); + break; +--- a/drivers/mtd/ubi/build.c ++++ b/drivers/mtd/ubi/build.c +@@ -89,7 +89,7 @@ static struct ubi_device *ubi_devices[UB + /* Serializes UBI devices creations and removals */ + DEFINE_MUTEX(ubi_devices_mutex); + +-/* Protects @ubi_devices and @ubi->ref_count */ ++/* Protects @ubi_devices, @ubi->ref_count and @ubi->is_dead */ + static DEFINE_SPINLOCK(ubi_devices_lock); + + /* "Show" method for files in '//class/ubi/' */ +@@ -258,6 +258,9 @@ struct ubi_device *ubi_get_device(int ub + + spin_lock(&ubi_devices_lock); + ubi = ubi_devices[ubi_num]; ++ if (ubi && ubi->is_dead) ++ ubi = NULL; ++ + if (ubi) { + ubi_assert(ubi->ref_count >= 0); + ubi->ref_count += 1; +@@ -295,7 +298,7 @@ struct ubi_device *ubi_get_by_major(int + spin_lock(&ubi_devices_lock); + for (i = 0; i < UBI_MAX_DEVICES; i++) { + ubi = ubi_devices[i]; +- if (ubi && MAJOR(ubi->cdev.dev) == major) { ++ if (ubi && !ubi->is_dead && MAJOR(ubi->cdev.dev) == major) { + ubi_assert(ubi->ref_count >= 0); + ubi->ref_count += 1; + get_device(&ubi->dev); +@@ -324,7 +327,7 @@ int ubi_major2num(int major) + for (i = 0; i < UBI_MAX_DEVICES; i++) { + struct ubi_device *ubi = ubi_devices[i]; + +- if (ubi && MAJOR(ubi->cdev.dev) == major) { ++ if (ubi && !ubi->is_dead && MAJOR(ubi->cdev.dev) == major) { + ubi_num = ubi->ubi_num; + break; + } +@@ -511,7 +514,7 @@ static void ubi_free_volumes_from(struct + int i; + + for (i = from; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) { +- if (!ubi->volumes[i]) ++ if (!ubi->volumes[i] || ubi->volumes[i]->is_dead) + continue; + ubi_eba_replace_table(ubi->volumes[i], NULL); + ubi_fastmap_destroy_checkmap(ubi->volumes[i]); +@@ -1094,10 +1097,10 @@ int ubi_detach_mtd_dev(int ubi_num, int + return -EINVAL; + + spin_lock(&ubi_devices_lock); +- put_device(&ubi->dev); + ubi->ref_count -= 1; + if (ubi->ref_count) { + if (!anyway) { ++ ubi->ref_count += 1; + spin_unlock(&ubi_devices_lock); + return -EBUSY; + } +@@ -1105,6 +1108,13 @@ int ubi_detach_mtd_dev(int ubi_num, int + ubi_err(ubi, "%s reference count %d, destroy anyway", + ubi->ubi_name, ubi->ref_count); + } ++ ubi->is_dead = true; ++ spin_unlock(&ubi_devices_lock); ++ ++ ubi_notify_all(ubi, UBI_VOLUME_SHUTDOWN, NULL); ++ ++ spin_lock(&ubi_devices_lock); ++ put_device(&ubi->dev); + ubi_devices[ubi_num] = NULL; + spin_unlock(&ubi_devices_lock); + +--- a/drivers/mtd/ubi/kapi.c ++++ b/drivers/mtd/ubi/kapi.c +@@ -152,7 +152,7 @@ struct ubi_volume_desc *ubi_open_volume( + + spin_lock(&ubi->volumes_lock); + vol = ubi->volumes[vol_id]; +- if (!vol) ++ if (!vol || vol->is_dead) + goto out_unlock; + + err = -EBUSY; +--- a/drivers/mtd/ubi/ubi.h ++++ b/drivers/mtd/ubi/ubi.h +@@ -345,6 +345,7 @@ struct ubi_volume { + int writers; + int exclusive; + int metaonly; ++ bool is_dead; + + int reserved_pebs; + int vol_type; +@@ -564,6 +565,7 @@ struct ubi_device { + spinlock_t volumes_lock; + int ref_count; + int image_seq; ++ bool is_dead; + + int rsvd_pebs; + int avail_pebs; +--- a/drivers/mtd/ubi/vmt.c ++++ b/drivers/mtd/ubi/vmt.c +@@ -59,7 +59,7 @@ static ssize_t vol_attribute_show(struct + struct ubi_device *ubi = vol->ubi; + + spin_lock(&ubi->volumes_lock); +- if (!ubi->volumes[vol->vol_id]) { ++ if (!ubi->volumes[vol->vol_id] || ubi->volumes[vol->vol_id]->is_dead) { + spin_unlock(&ubi->volumes_lock); + return -ENODEV; + } +@@ -189,7 +189,7 @@ int ubi_create_volume(struct ubi_device + + /* Ensure that the name is unique */ + for (i = 0; i < ubi->vtbl_slots; i++) +- if (ubi->volumes[i] && ++ if (ubi->volumes[i] && !ubi->volumes[i]->is_dead && + ubi->volumes[i]->name_len == req->name_len && + !strcmp(ubi->volumes[i]->name, req->name)) { + ubi_err(ubi, "volume \"%s\" exists (ID %d)", +@@ -352,6 +352,19 @@ int ubi_remove_volume(struct ubi_volume_ + err = -EBUSY; + goto out_unlock; + } ++ ++ /* ++ * Mark volume as dead at this point to prevent that anyone ++ * can take a reference to the volume from now on. ++ * This is necessary as we have to release the spinlock before ++ * calling ubi_volume_notify. ++ */ ++ vol->is_dead = true; ++ spin_unlock(&ubi->volumes_lock); ++ ++ ubi_volume_notify(ubi, vol, UBI_VOLUME_SHUTDOWN); ++ ++ spin_lock(&ubi->volumes_lock); + ubi->volumes[vol_id] = NULL; + spin_unlock(&ubi->volumes_lock); + +--- a/include/linux/mtd/ubi.h ++++ b/include/linux/mtd/ubi.h +@@ -192,6 +192,7 @@ struct ubi_device_info { + * or a volume was removed) + * @UBI_VOLUME_RESIZED: a volume has been re-sized + * @UBI_VOLUME_RENAMED: a volume has been re-named ++ * @UBI_VOLUME_SHUTDOWN: a volume is going to removed, shutdown users + * @UBI_VOLUME_UPDATED: data has been written to a volume + * + * These constants define which type of event has happened when a volume +@@ -202,6 +203,7 @@ enum { + UBI_VOLUME_REMOVED, + UBI_VOLUME_RESIZED, + UBI_VOLUME_RENAMED, ++ UBI_VOLUME_SHUTDOWN, + UBI_VOLUME_UPDATED, + }; + diff --git a/target/linux/generic/pending-6.6/450-06-mtd-ubi-populate-ubi-volume-fwnode.patch b/target/linux/generic/pending-6.6/450-06-mtd-ubi-populate-ubi-volume-fwnode.patch new file mode 100644 index 0000000000..1322766965 --- /dev/null +++ b/target/linux/generic/pending-6.6/450-06-mtd-ubi-populate-ubi-volume-fwnode.patch @@ -0,0 +1,65 @@ +From 3a041ee543cdf2e707a1dd72946cd6a583509b28 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Fri, 21 Jul 2023 19:26:37 +0100 +Subject: [PATCH 06/15] mtd: ubi: populate ubi volume fwnode + +Look for the 'volumes' subnode of an MTD partition attached to a UBI +device and attach matching child nodes to UBI volumes. +This allows UBI volumes to be referenced in device tree, e.g. for use +as NVMEM providers. + +Signed-off-by: Daniel Golle +--- + drivers/mtd/ubi/vmt.c | 27 +++++++++++++++++++++++++++ + 1 file changed, 27 insertions(+) + +--- a/drivers/mtd/ubi/vmt.c ++++ b/drivers/mtd/ubi/vmt.c +@@ -124,6 +124,31 @@ static void vol_release(struct device *d + kfree(vol); + } + ++static struct fwnode_handle *find_volume_fwnode(struct ubi_volume *vol) ++{ ++ struct fwnode_handle *fw_vols, *fw_vol; ++ const char *volname; ++ u32 volid; ++ ++ fw_vols = device_get_named_child_node(vol->dev.parent->parent, "volumes"); ++ if (!fw_vols) ++ return NULL; ++ ++ fwnode_for_each_child_node(fw_vols, fw_vol) { ++ if (!fwnode_property_read_string(fw_vol, "volname", &volname) && ++ strncmp(volname, vol->name, vol->name_len)) ++ continue; ++ ++ if (!fwnode_property_read_u32(fw_vol, "volid", &volid) && ++ vol->vol_id != volid) ++ continue; ++ ++ return fw_vol; ++ } ++ ++ return NULL; ++} ++ + /** + * ubi_create_volume - create volume. + * @ubi: UBI device description object +@@ -223,6 +248,7 @@ int ubi_create_volume(struct ubi_device + vol->name_len = req->name_len; + memcpy(vol->name, req->name, vol->name_len); + vol->ubi = ubi; ++ device_set_node(&vol->dev, find_volume_fwnode(vol)); + + /* + * Finish all pending erases because there may be some LEBs belonging +@@ -605,6 +631,7 @@ int ubi_add_volume(struct ubi_device *ub + vol->dev.class = &ubi_class; + vol->dev.groups = volume_dev_groups; + dev_set_name(&vol->dev, "%s_%d", ubi->ubi_name, vol->vol_id); ++ device_set_node(&vol->dev, find_volume_fwnode(vol)); + err = device_register(&vol->dev); + if (err) { + cdev_del(&vol->cdev); diff --git a/target/linux/generic/pending-6.6/450-07-mtd-ubi-provide-NVMEM-layer-over-UBI-volumes.patch b/target/linux/generic/pending-6.6/450-07-mtd-ubi-provide-NVMEM-layer-over-UBI-volumes.patch new file mode 100644 index 0000000000..59a1eb9374 --- /dev/null +++ b/target/linux/generic/pending-6.6/450-07-mtd-ubi-provide-NVMEM-layer-over-UBI-volumes.patch @@ -0,0 +1,246 @@ +From 7eb6666348f3f2d1f7308c712fa5903cbe189401 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 8 Jun 2023 17:22:04 +0100 +Subject: [PATCH 07/15] mtd: ubi: provide NVMEM layer over UBI volumes + +In an ideal world we would like UBI to be used where ever possible on a +NAND chip. And with UBI support in ARM Trusted Firmware and U-Boot it +is possible to achieve an (almost-)all-UBI flash layout. Hence the need +for a way to also use UBI volumes to store board-level constants, such +as MAC addresses and calibration data of wireless interfaces. + +Add UBI volume NVMEM driver module exposing UBI volumes as NVMEM +providers. Allow UBI devices to have a "volumes" firmware subnode with +volumes which may be compatible with "nvmem-cells". +Access to UBI volumes via the NVMEM interface at this point is +read-only, and it is slow, opening and closing the UBI volume for each +access due to limitations of the NVMEM provider API. + +Signed-off-by: Daniel Golle +--- + drivers/mtd/ubi/Kconfig | 12 +++ + drivers/mtd/ubi/Makefile | 1 + + drivers/mtd/ubi/nvmem.c | 188 +++++++++++++++++++++++++++++++++++++++ + 3 files changed, 201 insertions(+) + create mode 100644 drivers/mtd/ubi/nvmem.c + +--- a/drivers/mtd/ubi/Kconfig ++++ b/drivers/mtd/ubi/Kconfig +@@ -104,4 +104,16 @@ config MTD_UBI_BLOCK + + If in doubt, say "N". + ++config MTD_UBI_NVMEM ++ tristate "UBI virtual NVMEM" ++ default n ++ depends on NVMEM ++ help ++ This option enabled an additional driver exposing UBI volumes as NVMEM ++ providers, intended for platforms where UBI is part of the firmware ++ specification and used to store also e.g. MAC addresses or board- ++ specific Wi-Fi calibration data. ++ ++ If in doubt, say "N". ++ + endif # MTD_UBI +--- a/drivers/mtd/ubi/Makefile ++++ b/drivers/mtd/ubi/Makefile +@@ -7,3 +7,4 @@ ubi-$(CONFIG_MTD_UBI_FASTMAP) += fastmap + ubi-$(CONFIG_MTD_UBI_BLOCK) += block.o + + obj-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o ++obj-$(CONFIG_MTD_UBI_NVMEM) += nvmem.o +--- /dev/null ++++ b/drivers/mtd/ubi/nvmem.c +@@ -0,0 +1,191 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Copyright (c) 2023 Daniel Golle ++ */ ++ ++/* UBI NVMEM provider */ ++#include "ubi.h" ++#include ++#include ++ ++/* List of all NVMEM devices */ ++static LIST_HEAD(nvmem_devices); ++static DEFINE_MUTEX(devices_mutex); ++ ++struct ubi_nvmem { ++ struct nvmem_device *nvmem; ++ int ubi_num; ++ int vol_id; ++ int usable_leb_size; ++ struct list_head list; ++}; ++ ++static int ubi_nvmem_reg_read(void *priv, unsigned int from, ++ void *val, size_t bytes) ++{ ++ uint32_t offs, to_read, bytes_left; ++ struct ubi_nvmem *unv = priv; ++ struct ubi_volume_desc *desc; ++ uint64_t lnum = from; ++ int err = 0; ++ ++ desc = ubi_open_volume(unv->ubi_num, unv->vol_id, UBI_READONLY); ++ if (IS_ERR(desc)) ++ return PTR_ERR(desc); ++ ++ bytes_left = bytes; ++ offs = do_div(lnum, unv->usable_leb_size); ++ while (bytes_left) { ++ to_read = unv->usable_leb_size - offs; ++ ++ if (to_read > bytes_left) ++ to_read = bytes_left; ++ ++ err = ubi_read(desc, lnum, val, offs, to_read); ++ if (err) ++ break; ++ ++ lnum += 1; ++ offs = 0; ++ bytes_left -= to_read; ++ val += to_read; ++ } ++ ubi_close_volume(desc); ++ ++ if (err) ++ return err; ++ ++ return bytes_left == 0 ? 0 : -EIO; ++} ++ ++static int ubi_nvmem_add(struct ubi_volume_info *vi) ++{ ++ struct device_node *np = dev_of_node(vi->dev); ++ struct nvmem_config config = {}; ++ struct ubi_nvmem *unv; ++ int ret; ++ ++ if (!np) ++ return 0; ++ ++ if (!of_get_child_by_name(np, "nvmem-layout")) ++ return 0; ++ ++ if (WARN_ON_ONCE(vi->usable_leb_size <= 0) || ++ WARN_ON_ONCE(vi->size <= 0)) ++ return -EINVAL; ++ ++ unv = kzalloc(sizeof(struct ubi_nvmem), GFP_KERNEL); ++ if (!unv) ++ return -ENOMEM; ++ ++ config.id = NVMEM_DEVID_NONE; ++ config.dev = vi->dev; ++ config.name = dev_name(vi->dev); ++ config.owner = THIS_MODULE; ++ config.priv = unv; ++ config.reg_read = ubi_nvmem_reg_read; ++ config.size = vi->usable_leb_size * vi->size; ++ config.word_size = 1; ++ config.stride = 1; ++ config.read_only = true; ++ config.root_only = true; ++ config.ignore_wp = true; ++ config.of_node = np; ++ ++ unv->ubi_num = vi->ubi_num; ++ unv->vol_id = vi->vol_id; ++ unv->usable_leb_size = vi->usable_leb_size; ++ unv->nvmem = nvmem_register(&config); ++ if (IS_ERR(unv->nvmem)) { ++ ret = dev_err_probe(vi->dev, PTR_ERR(unv->nvmem), ++ "Failed to register NVMEM device\n"); ++ kfree(unv); ++ return ret; ++ } ++ ++ mutex_lock(&devices_mutex); ++ list_add_tail(&unv->list, &nvmem_devices); ++ mutex_unlock(&devices_mutex); ++ ++ return 0; ++} ++ ++static void ubi_nvmem_remove(struct ubi_volume_info *vi) ++{ ++ struct ubi_nvmem *unv_c, *unv = NULL; ++ ++ mutex_lock(&devices_mutex); ++ list_for_each_entry(unv_c, &nvmem_devices, list) ++ if (unv_c->ubi_num == vi->ubi_num && unv_c->vol_id == vi->vol_id) { ++ unv = unv_c; ++ break; ++ } ++ ++ if (!unv) { ++ mutex_unlock(&devices_mutex); ++ return; ++ } ++ ++ list_del(&unv->list); ++ mutex_unlock(&devices_mutex); ++ nvmem_unregister(unv->nvmem); ++ kfree(unv); ++} ++ ++/** ++ * nvmem_notify - UBI notification handler. ++ * @nb: registered notifier block ++ * @l: notification type ++ * @ns_ptr: pointer to the &struct ubi_notification object ++ */ ++static int nvmem_notify(struct notifier_block *nb, unsigned long l, ++ void *ns_ptr) ++{ ++ struct ubi_notification *nt = ns_ptr; ++ ++ switch (l) { ++ case UBI_VOLUME_RESIZED: ++ ubi_nvmem_remove(&nt->vi); ++ fallthrough; ++ case UBI_VOLUME_ADDED: ++ ubi_nvmem_add(&nt->vi); ++ break; ++ case UBI_VOLUME_SHUTDOWN: ++ ubi_nvmem_remove(&nt->vi); ++ break; ++ default: ++ break; ++ } ++ return NOTIFY_OK; ++} ++ ++static struct notifier_block nvmem_notifier = { ++ .notifier_call = nvmem_notify, ++}; ++ ++static int __init ubi_nvmem_init(void) ++{ ++ return ubi_register_volume_notifier(&nvmem_notifier, 0); ++} ++ ++static void __exit ubi_nvmem_exit(void) ++{ ++ struct ubi_nvmem *unv, *tmp; ++ ++ mutex_lock(&devices_mutex); ++ list_for_each_entry_safe(unv, tmp, &nvmem_devices, list) { ++ nvmem_unregister(unv->nvmem); ++ list_del(&unv->list); ++ kfree(unv); ++ } ++ mutex_unlock(&devices_mutex); ++ ++ ubi_unregister_volume_notifier(&nvmem_notifier); ++} ++ ++module_init(ubi_nvmem_init); ++module_exit(ubi_nvmem_exit); ++MODULE_DESCRIPTION("NVMEM layer over UBI volumes"); ++MODULE_AUTHOR("Daniel Golle"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/pending-6.6/450-08-dt-bindings-block-add-basic-bindings-for-block-devic.patch b/target/linux/generic/pending-6.6/450-08-dt-bindings-block-add-basic-bindings-for-block-devic.patch new file mode 100644 index 0000000000..d0727faf3d --- /dev/null +++ b/target/linux/generic/pending-6.6/450-08-dt-bindings-block-add-basic-bindings-for-block-devic.patch @@ -0,0 +1,120 @@ +From 9ffc1d7d73609a89eb264d6066340f8b7b3b0ebe Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 7 Aug 2023 21:19:45 +0100 +Subject: [PATCH 08/15] dt-bindings: block: add basic bindings for block + devices + +Add bindings for block devices which are used to allow referencing +nvmem bits on them. + +Signed-off-by: Daniel Golle +--- + .../bindings/block/block-device.yaml | 22 ++++++++ + .../devicetree/bindings/block/partition.yaml | 50 +++++++++++++++++++ + .../devicetree/bindings/block/partitions.yaml | 20 ++++++++ + 3 files changed, 92 insertions(+) + create mode 100644 Documentation/devicetree/bindings/block/block-device.yaml + create mode 100644 Documentation/devicetree/bindings/block/partition.yaml + create mode 100644 Documentation/devicetree/bindings/block/partitions.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/block/block-device.yaml +@@ -0,0 +1,22 @@ ++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/block/block-device.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: block storage device ++ ++description: | ++ This binding is generic and describes a block-oriented storage device. ++ ++maintainers: ++ - Daniel Golle ++ ++properties: ++ partitions: ++ $ref: /schemas/block/partitions.yaml ++ ++ nvmem-layout: ++ $ref: /schemas/nvmem/layouts/nvmem-layout.yaml# ++ ++unevaluatedProperties: false +--- /dev/null ++++ b/Documentation/devicetree/bindings/block/partition.yaml +@@ -0,0 +1,50 @@ ++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/block/partition.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Partition on a block device ++ ++description: | ++ This binding describes a partition on a block device. ++ Partitions may be matched by a combination of partition number, name, ++ and UUID. ++ ++maintainers: ++ - Daniel Golle ++ ++properties: ++ $nodename: ++ pattern: '^block-partition-.+$' ++ ++ partnum: ++ description: ++ Matches partition by number if present. ++ ++ partname: ++ "$ref": "/schemas/types.yaml#/definitions/string" ++ description: ++ Matches partition by PARTNAME if present. ++ ++ uuid: ++ "$ref": "/schemas/types.yaml#/definitions/string" ++ description: ++ Matches partition by PARTUUID if present. ++ ++ nvmem-layout: ++ $ref: /schemas/nvmem/layouts/nvmem-layout.yaml# ++ description: ++ This container may reference an NVMEM layout parser. ++ ++anyOf: ++ - required: ++ - partnum ++ ++ - required: ++ - partname ++ ++ - required: ++ - uuid ++ ++unevaluatedProperties: false +--- /dev/null ++++ b/Documentation/devicetree/bindings/block/partitions.yaml +@@ -0,0 +1,20 @@ ++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/block/partitions.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Partitions on block devices ++ ++description: | ++ This binding is generic and describes the content of the partitions container ++ node. ++ ++maintainers: ++ - Daniel Golle ++ ++patternProperties: ++ "^block-partition-.+$": ++ $ref: partition.yaml ++ ++unevaluatedProperties: false diff --git a/target/linux/generic/pending-6.6/450-09-block-partitions-populate-fwnode.patch b/target/linux/generic/pending-6.6/450-09-block-partitions-populate-fwnode.patch new file mode 100644 index 0000000000..8aa5cba678 --- /dev/null +++ b/target/linux/generic/pending-6.6/450-09-block-partitions-populate-fwnode.patch @@ -0,0 +1,77 @@ +From 614f4f6fdda09e30ecf7ef6c8091579db15018cb Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Fri, 21 Jul 2023 17:51:03 +0100 +Subject: [PATCH 09/15] block: partitions: populate fwnode + +Let block partitions to be represented by a firmware node and hence +allow them to being referenced e.g. for use with blk-nvmem. + +Signed-off-by: Daniel Golle +--- + block/partitions/core.c | 41 +++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 41 insertions(+) + +--- a/block/partitions/core.c ++++ b/block/partitions/core.c +@@ -10,6 +10,8 @@ + #include + #include + #include ++#include ++ + #include "check.h" + + static int (*check_part[])(struct parsed_partitions *) = { +@@ -298,6 +300,43 @@ static ssize_t whole_disk_show(struct de + } + static DEVICE_ATTR(whole_disk, 0444, whole_disk_show, NULL); + ++static struct fwnode_handle *find_partition_fwnode(struct block_device *bdev) ++{ ++ struct fwnode_handle *fw_parts, *fw_part; ++ struct device *ddev = disk_to_dev(bdev->bd_disk); ++ const char *partname, *uuid; ++ u32 partno; ++ ++ fw_parts = device_get_named_child_node(ddev, "partitions"); ++ if (!fw_parts) ++ fw_parts = device_get_named_child_node(ddev->parent, "partitions"); ++ ++ if (!fw_parts) ++ return NULL; ++ ++ fwnode_for_each_child_node(fw_parts, fw_part) { ++ if (!fwnode_property_read_string(fw_part, "uuid", &uuid) && ++ (!bdev->bd_meta_info || strncmp(uuid, ++ bdev->bd_meta_info->uuid, ++ PARTITION_META_INFO_UUIDLTH))) ++ continue; ++ ++ if (!fwnode_property_read_string(fw_part, "partname", &partname) && ++ (!bdev->bd_meta_info || strncmp(partname, ++ bdev->bd_meta_info->volname, ++ PARTITION_META_INFO_VOLNAMELTH))) ++ continue; ++ ++ if (!fwnode_property_read_u32(fw_part, "partno", &partno) && ++ bdev->bd_partno != partno) ++ continue; ++ ++ return fw_part; ++ } ++ ++ return NULL; ++} ++ + /* + * Must be called either with open_mutex held, before a disk can be opened or + * after all disk users are gone. +@@ -380,6 +419,8 @@ static struct block_device *add_partitio + goto out_put; + } + ++ device_set_node(pdev, find_partition_fwnode(bdev)); ++ + /* delay uevent until 'holders' subdir is created */ + dev_set_uevent_suppress(pdev, 1); + err = device_add(pdev); diff --git a/target/linux/generic/pending-6.6/450-10-block-add-new-genhd-flag-GENHD_FL_NVMEM.patch b/target/linux/generic/pending-6.6/450-10-block-add-new-genhd-flag-GENHD_FL_NVMEM.patch new file mode 100644 index 0000000000..4cbec14f5c --- /dev/null +++ b/target/linux/generic/pending-6.6/450-10-block-add-new-genhd-flag-GENHD_FL_NVMEM.patch @@ -0,0 +1,29 @@ +From 65f3ff9672ccd5ee78937047e7a2fc696eee1c8f Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 13 Jul 2023 04:07:16 +0100 +Subject: [PATCH 10/15] block: add new genhd flag GENHD_FL_NVMEM + +Add new flag to destinguish block devices which may act as an NVMEM +provider. + +Signed-off-by: Daniel Golle +--- + include/linux/blkdev.h | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/include/linux/blkdev.h ++++ b/include/linux/blkdev.h +@@ -87,11 +87,13 @@ struct partition_meta_info { + * ``GENHD_FL_NO_PART``: partition support is disabled. The kernel will not + * scan for partitions from add_disk, and users can't add partitions manually. + * ++ * ``GENHD_FL_NVMEM``: the block device should be considered as NVMEM provider. + */ + enum { + GENHD_FL_REMOVABLE = 1 << 0, + GENHD_FL_HIDDEN = 1 << 1, + GENHD_FL_NO_PART = 1 << 2, ++ GENHD_FL_NVMEM = 1 << 3, + }; + + enum { diff --git a/target/linux/generic/pending-6.6/450-11-block-implement-NVMEM-provider.patch b/target/linux/generic/pending-6.6/450-11-block-implement-NVMEM-provider.patch new file mode 100644 index 0000000000..e18b0c3a5c --- /dev/null +++ b/target/linux/generic/pending-6.6/450-11-block-implement-NVMEM-provider.patch @@ -0,0 +1,235 @@ +From b9936aa8a3775c2027f655d91a206d0e6e1c7ec0 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 11 Jul 2023 00:17:31 +0100 +Subject: [PATCH 11/15] block: implement NVMEM provider + +On embedded devices using an eMMC it is common that one or more partitions +on the eMMC are used to store MAC addresses and Wi-Fi calibration EEPROM +data. Allow referencing the partition in device tree for the kernel and +Wi-Fi drivers accessing it via the NVMEM layer. + +Signed-off-by: Daniel Golle +--- + block/Kconfig | 9 +++ + block/Makefile | 1 + + block/blk-nvmem.c | 186 ++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 196 insertions(+) + create mode 100644 block/blk-nvmem.c + +--- a/block/Kconfig ++++ b/block/Kconfig +@@ -203,6 +203,15 @@ config BLK_INLINE_ENCRYPTION_FALLBACK + by falling back to the kernel crypto API when inline + encryption hardware is not present. + ++config BLK_NVMEM ++ bool "Block device NVMEM provider" ++ depends on OF ++ depends on NVMEM ++ help ++ Allow block devices (or partitions) to act as NVMEM prodivers, ++ typically used with eMMC to store MAC addresses or Wi-Fi ++ calibration data on embedded devices. ++ + source "block/partitions/Kconfig" + + config BLOCK_COMPAT +--- a/block/Makefile ++++ b/block/Makefile +@@ -35,6 +35,7 @@ obj-$(CONFIG_BLK_DEV_ZONED) += blk-zoned + obj-$(CONFIG_BLK_WBT) += blk-wbt.o + obj-$(CONFIG_BLK_DEBUG_FS) += blk-mq-debugfs.o + obj-$(CONFIG_BLK_DEBUG_FS_ZONED)+= blk-mq-debugfs-zoned.o ++obj-$(CONFIG_BLK_NVMEM) += blk-nvmem.o + obj-$(CONFIG_BLK_SED_OPAL) += sed-opal.o + obj-$(CONFIG_BLK_PM) += blk-pm.o + obj-$(CONFIG_BLK_INLINE_ENCRYPTION) += blk-crypto.o blk-crypto-profile.o \ +--- /dev/null ++++ b/block/blk-nvmem.c +@@ -0,0 +1,186 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * block device NVMEM provider ++ * ++ * Copyright (c) 2023 Daniel Golle ++ * ++ * Useful on devices using a partition on an eMMC for MAC addresses or ++ * Wi-Fi calibration EEPROM data. ++ */ ++ ++#include "blk.h" ++#include ++#include ++#include ++#include ++ ++/* List of all NVMEM devices */ ++static LIST_HEAD(nvmem_devices); ++static DEFINE_MUTEX(devices_mutex); ++ ++struct blk_nvmem { ++ struct nvmem_device *nvmem; ++ struct block_device *bdev; ++ struct list_head list; ++}; ++ ++static int blk_nvmem_reg_read(void *priv, unsigned int from, ++ void *val, size_t bytes) ++{ ++ unsigned long offs = from & ~PAGE_MASK, to_read; ++ pgoff_t f_index = from >> PAGE_SHIFT; ++ struct address_space *mapping; ++ struct blk_nvmem *bnv = priv; ++ size_t bytes_left = bytes; ++ struct folio *folio; ++ void *p; ++ int ret; ++ ++ if (!bnv->bdev) ++ return -ENODEV; ++ ++ if (!bnv->bdev->bd_disk) ++ return -EINVAL; ++ ++ if (!bnv->bdev->bd_disk->fops) ++ return -EIO; ++ ++ if (!bnv->bdev->bd_disk->fops->open) ++ return -EIO; ++ ++ ret = bnv->bdev->bd_disk->fops->open(bnv->bdev, FMODE_READ); ++ if (ret) ++ return ret; ++ ++ mapping = bnv->bdev->bd_inode->i_mapping; ++ ++ while (bytes_left) { ++ folio = read_mapping_folio(mapping, f_index++, NULL); ++ if (IS_ERR(folio)) { ++ ret = PTR_ERR(folio); ++ goto err_release_bdev; ++ } ++ to_read = min_t(unsigned long, bytes_left, PAGE_SIZE - offs); ++ p = folio_address(folio) + offset_in_folio(folio, offs); ++ memcpy(val, p, to_read); ++ offs = 0; ++ bytes_left -= to_read; ++ val += to_read; ++ folio_put(folio); ++ } ++ ++err_release_bdev: ++ bnv->bdev->bd_disk->fops->release(bnv->bdev->bd_disk, FMODE_READ); ++ ++ return ret; ++} ++ ++static int blk_nvmem_register(struct device *dev, struct class_interface *iface) ++{ ++ struct device_node *np = dev_of_node(dev); ++ struct block_device *bdev = dev_to_bdev(dev); ++ struct nvmem_config config = {}; ++ struct blk_nvmem *bnv; ++ ++ /* skip devices which do not have a device tree node */ ++ if (!np) ++ return 0; ++ ++ /* skip devices without an nvmem layout defined */ ++ if (!of_get_child_by_name(np, "nvmem-layout")) ++ return 0; ++ ++ /* ++ * skip devices which don't have GENHD_FL_NVMEM set ++ * ++ * This flag is used for mtdblock and ubiblock devices because ++ * both, MTD and UBI already implement their own NVMEM provider. ++ * To avoid registering multiple NVMEM providers for the same ++ * device node, don't register the block NVMEM provider for them. ++ */ ++ if (!(bdev->bd_disk->flags & GENHD_FL_NVMEM)) ++ return 0; ++ ++ /* ++ * skip block device too large to be represented as NVMEM devices ++ * which are using an 'int' as address ++ */ ++ if (bdev_nr_bytes(bdev) > INT_MAX) ++ return -EFBIG; ++ ++ bnv = kzalloc(sizeof(struct blk_nvmem), GFP_KERNEL); ++ if (!bnv) ++ return -ENOMEM; ++ ++ config.id = NVMEM_DEVID_NONE; ++ config.dev = &bdev->bd_device; ++ config.name = dev_name(&bdev->bd_device); ++ config.owner = THIS_MODULE; ++ config.priv = bnv; ++ config.reg_read = blk_nvmem_reg_read; ++ config.size = bdev_nr_bytes(bdev); ++ config.word_size = 1; ++ config.stride = 1; ++ config.read_only = true; ++ config.root_only = true; ++ config.ignore_wp = true; ++ config.of_node = to_of_node(dev->fwnode); ++ ++ bnv->bdev = bdev; ++ bnv->nvmem = nvmem_register(&config); ++ if (IS_ERR(bnv->nvmem)) { ++ dev_err_probe(&bdev->bd_device, PTR_ERR(bnv->nvmem), ++ "Failed to register NVMEM device\n"); ++ ++ kfree(bnv); ++ return PTR_ERR(bnv->nvmem); ++ } ++ ++ mutex_lock(&devices_mutex); ++ list_add_tail(&bnv->list, &nvmem_devices); ++ mutex_unlock(&devices_mutex); ++ ++ return 0; ++} ++ ++static void blk_nvmem_unregister(struct device *dev, struct class_interface *iface) ++{ ++ struct block_device *bdev = dev_to_bdev(dev); ++ struct blk_nvmem *bnv_c, *bnv = NULL; ++ ++ mutex_lock(&devices_mutex); ++ list_for_each_entry(bnv_c, &nvmem_devices, list) { ++ if (bnv_c->bdev == bdev) { ++ bnv = bnv_c; ++ break; ++ } ++ } ++ ++ if (!bnv) { ++ mutex_unlock(&devices_mutex); ++ return; ++ } ++ ++ list_del(&bnv->list); ++ mutex_unlock(&devices_mutex); ++ nvmem_unregister(bnv->nvmem); ++ kfree(bnv); ++} ++ ++static struct class_interface blk_nvmem_bus_interface __refdata = { ++ .class = &block_class, ++ .add_dev = &blk_nvmem_register, ++ .remove_dev = &blk_nvmem_unregister, ++}; ++ ++static int __init blk_nvmem_init(void) ++{ ++ int ret; ++ ++ ret = class_interface_register(&blk_nvmem_bus_interface); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++device_initcall(blk_nvmem_init); diff --git a/target/linux/generic/pending-6.6/450-12-dt-bindings-mmc-mmc-card-add-block-device-nodes.patch b/target/linux/generic/pending-6.6/450-12-dt-bindings-mmc-mmc-card-add-block-device-nodes.patch new file mode 100644 index 0000000000..77c9bf91a5 --- /dev/null +++ b/target/linux/generic/pending-6.6/450-12-dt-bindings-mmc-mmc-card-add-block-device-nodes.patch @@ -0,0 +1,74 @@ +From 86864bf8f40e84dc881c197ef470a88668329dbf Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 7 Aug 2023 21:21:45 +0100 +Subject: [PATCH 12/15] dt-bindings: mmc: mmc-card: add block device nodes + +Add nodes representing the block devices exposed by an MMC device +including an example involving nvmem-cells. + +Signed-off-by: Daniel Golle +--- + .../devicetree/bindings/mmc/mmc-card.yaml | 45 +++++++++++++++++++ + 1 file changed, 45 insertions(+) + +--- a/Documentation/devicetree/bindings/mmc/mmc-card.yaml ++++ b/Documentation/devicetree/bindings/mmc/mmc-card.yaml +@@ -26,6 +26,18 @@ properties: + Use this to indicate that the mmc-card has a broken hpi + implementation, and that hpi should not be used. + ++ block: ++ $ref: /schemas/block/block-device.yaml# ++ description: ++ Represents the block storage provided by an SD card or the ++ main hardware partition of an eMMC. ++ ++patternProperties: ++ '^boot[0-9]+': ++ $ref: /schemas/block/block-device.yaml# ++ description: ++ Represents a boot hardware partition on an eMMC. ++ + required: + - compatible + - reg +@@ -42,6 +54,39 @@ examples: + compatible = "mmc-card"; + reg = <0>; + broken-hpi; ++ ++ block { ++ partitions { ++ cal_data: block-partition-rf { ++ partnum = <3>; ++ partname = "rf"; ++ ++ nvmem-layout { ++ compatible = "fixed-layout"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ eeprom@0 { ++ reg = <0x0 0x1000>; ++ }; ++ }; ++ }; ++ }; ++ }; ++ ++ boot1 { ++ nvmem-layout { ++ compatible = "fixed-layout"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ macaddr: macaddr@a { ++ compatible = "mac-base"; ++ reg = <0xa 0x6>; ++ #nvmem-cell-cells = <1>; ++ }; ++ }; ++ }; + }; + }; + diff --git a/target/linux/generic/pending-6.6/450-13-mmc-core-set-card-fwnode_handle.patch b/target/linux/generic/pending-6.6/450-13-mmc-core-set-card-fwnode_handle.patch new file mode 100644 index 0000000000..fada280437 --- /dev/null +++ b/target/linux/generic/pending-6.6/450-13-mmc-core-set-card-fwnode_handle.patch @@ -0,0 +1,23 @@ +From 644942a31719de674e2aa68f83d66bd8ae7e4fb7 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 13 Jul 2023 04:12:21 +0100 +Subject: [PATCH 13/15] mmc: core: set card fwnode_handle + +Set fwnode in case it isn't set yet and of_node is present. + +Signed-off-by: Daniel Golle +--- + drivers/mmc/core/bus.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/mmc/core/bus.c ++++ b/drivers/mmc/core/bus.c +@@ -363,6 +363,8 @@ int mmc_add_card(struct mmc_card *card) + mmc_add_card_debugfs(card); + #endif + card->dev.of_node = mmc_of_find_child_device(card->host, 0); ++ if (card->dev.of_node && !card->dev.fwnode) ++ card->dev.fwnode = &card->dev.of_node->fwnode; + + device_enable_async_suspend(&card->dev); + diff --git a/target/linux/generic/pending-6.6/450-14-mmc-block-set-fwnode-of-disk-devices.patch b/target/linux/generic/pending-6.6/450-14-mmc-block-set-fwnode-of-disk-devices.patch new file mode 100644 index 0000000000..a9e53f1155 --- /dev/null +++ b/target/linux/generic/pending-6.6/450-14-mmc-block-set-fwnode-of-disk-devices.patch @@ -0,0 +1,39 @@ +From d9143f86330dd038fc48878558dd287ceee5d3d4 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 13 Jul 2023 04:13:04 +0100 +Subject: [PATCH 14/15] mmc: block: set fwnode of disk devices + +Set fwnode of disk devices to 'block', 'boot0' and 'boot1' subnodes of +the mmc-card. This is done in preparation for having the eMMC act as +NVMEM provider. + +Signed-off-by: Daniel Golle +--- + drivers/mmc/core/block.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/drivers/mmc/core/block.c ++++ b/drivers/mmc/core/block.c +@@ -2484,6 +2484,8 @@ static struct mmc_blk_data *mmc_blk_allo + int area_type, + unsigned int part_type) + { ++ struct fwnode_handle *fwnode; ++ struct device *ddev; + struct mmc_blk_data *md; + int devidx, ret; + char cap_str[10]; +@@ -2580,6 +2582,13 @@ static struct mmc_blk_data *mmc_blk_allo + + blk_queue_write_cache(md->queue.queue, cache_enabled, fua_enabled); + ++ ddev = disk_to_dev(md->disk); ++ fwnode = device_get_named_child_node(subname ? md->parent->parent : ++ md->parent, ++ subname ? subname : "block"); ++ if (fwnode) ++ device_set_node(ddev, fwnode); ++ + string_get_size((u64)size, 512, STRING_UNITS_2, + cap_str, sizeof(cap_str)); + pr_info("%s: %s %s %s %s\n", diff --git a/target/linux/generic/pending-6.6/450-15-mmc-block-set-GENHD_FL_NVMEM.patch b/target/linux/generic/pending-6.6/450-15-mmc-block-set-GENHD_FL_NVMEM.patch new file mode 100644 index 0000000000..d76e7b2fe5 --- /dev/null +++ b/target/linux/generic/pending-6.6/450-15-mmc-block-set-GENHD_FL_NVMEM.patch @@ -0,0 +1,22 @@ +From 322035ab2b0113d98b6c0ea788d971e0df2952a4 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 20 Jul 2023 17:36:44 +0100 +Subject: [PATCH 15/15] mmc: block: set GENHD_FL_NVMEM + +Set flag to consider MMC block devices as NVMEM providers. + +Signed-off-by: Daniel Golle +--- + drivers/mmc/core/block.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/mmc/core/block.c ++++ b/drivers/mmc/core/block.c +@@ -2538,6 +2538,7 @@ static struct mmc_blk_data *mmc_blk_allo + md->disk->major = MMC_BLOCK_MAJOR; + md->disk->minors = perdev_minors; + md->disk->first_minor = devidx * perdev_minors; ++ md->disk->flags = GENHD_FL_NVMEM; + md->disk->fops = &mmc_bdops; + md->disk->private_data = md; + md->parent = parent; diff --git a/target/linux/generic/pending-6.6/460-mtd-cfi_cmdset_0002-no-erase_suspend.patch b/target/linux/generic/pending-6.6/460-mtd-cfi_cmdset_0002-no-erase_suspend.patch new file mode 100644 index 0000000000..1b653fb735 --- /dev/null +++ b/target/linux/generic/pending-6.6/460-mtd-cfi_cmdset_0002-no-erase_suspend.patch @@ -0,0 +1,25 @@ +From: Felix Fietkau +Subject: kernel: disable cfi cmdset 0002 erase suspend + +on some platforms, erase suspend leads to data corruption and lockups when write +ops collide with erase ops. this has been observed on the buffalo wzr-hp-g300nh. +rather than play whack-a-mole with a hard to reproduce issue on a variety of devices, +simply disable erase suspend, as it will usually not produce any useful gain on +the small filesystems used on embedded hardware. + +Signed-off-by: Felix Fietkau +--- + drivers/mtd/chips/cfi_cmdset_0002.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/mtd/chips/cfi_cmdset_0002.c ++++ b/drivers/mtd/chips/cfi_cmdset_0002.c +@@ -908,7 +908,7 @@ static int get_chip(struct map_info *map + return 0; + + case FL_ERASING: +- if (!cfip || !(cfip->EraseSuspend & (0x1|0x2)) || ++ if (1 /* no suspend */ || !cfip || !(cfip->EraseSuspend & (0x1|0x2)) || + !(mode == FL_READY || mode == FL_POINT || + (mode == FL_WRITING && (cfip->EraseSuspend & 0x2)))) + goto sleep; diff --git a/target/linux/generic/pending-6.6/461-mtd-cfi_cmdset_0002-add-buffer-write-cmd-timeout.patch b/target/linux/generic/pending-6.6/461-mtd-cfi_cmdset_0002-add-buffer-write-cmd-timeout.patch new file mode 100644 index 0000000000..f2788c5214 --- /dev/null +++ b/target/linux/generic/pending-6.6/461-mtd-cfi_cmdset_0002-add-buffer-write-cmd-timeout.patch @@ -0,0 +1,17 @@ +From: George Kashperko +Subject: Issue map read after Write Buffer Load command to ensure chip is ready to receive data. + +Signed-off-by: George Kashperko +--- + drivers/mtd/chips/cfi_cmdset_0002.c | 1 + + 1 file changed, 1 insertion(+) +--- a/drivers/mtd/chips/cfi_cmdset_0002.c ++++ b/drivers/mtd/chips/cfi_cmdset_0002.c +@@ -2052,6 +2052,7 @@ static int __xipram do_write_buffer(stru + + /* Write Buffer Load */ + map_write(map, CMD(0x25), cmd_adr); ++ (void) map_read(map, cmd_adr); + + chip->state = FL_WRITING_TO_BUFFER; + diff --git a/target/linux/generic/pending-6.6/465-m25p80-mx-disable-software-protection.patch b/target/linux/generic/pending-6.6/465-m25p80-mx-disable-software-protection.patch new file mode 100644 index 0000000000..1e28077338 --- /dev/null +++ b/target/linux/generic/pending-6.6/465-m25p80-mx-disable-software-protection.patch @@ -0,0 +1,18 @@ +From: Felix Fietkau +Subject: Disable software protection bits for Macronix flashes. + +Signed-off-by: Felix Fietkau +--- + drivers/mtd/spi-nor/spi-nor.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/mtd/spi-nor/macronix.c ++++ b/drivers/mtd/spi-nor/macronix.c +@@ -106,6 +106,7 @@ static void macronix_nor_default_init(st + { + nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable; + nor->params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode; ++ nor->flags |= SNOR_F_HAS_LOCK; + } + + static const struct spi_nor_fixups macronix_nor_fixups = { diff --git a/target/linux/generic/pending-6.6/476-mtd-spi-nor-add-eon-en25q128.patch b/target/linux/generic/pending-6.6/476-mtd-spi-nor-add-eon-en25q128.patch new file mode 100644 index 0000000000..303e488433 --- /dev/null +++ b/target/linux/generic/pending-6.6/476-mtd-spi-nor-add-eon-en25q128.patch @@ -0,0 +1,19 @@ +From: Piotr Dymacz +Subject: kernel/mtd: add support for EON EN25Q128 + +Signed-off-by: Piotr Dymacz +--- + drivers/mtd/spi-nor/spi-nor.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/mtd/spi-nor/eon.c ++++ b/drivers/mtd/spi-nor/eon.c +@@ -17,6 +17,8 @@ static const struct flash_info eon_nor_p + { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128) }, + { "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128) + NO_SFDP_FLAGS(SECT_4K) }, ++ { "en25q128", INFO(0x1c3018, 0, 64 * 1024, 256) ++ NO_SFDP_FLAGS(SECT_4K) }, + { "en25q80a", INFO(0x1c3014, 0, 64 * 1024, 16) + NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ) }, + { "en25qh16", INFO(0x1c7015, 0, 64 * 1024, 32) diff --git a/target/linux/generic/pending-6.6/477-mtd-spi-nor-add-eon-en25qx128a.patch b/target/linux/generic/pending-6.6/477-mtd-spi-nor-add-eon-en25qx128a.patch new file mode 100644 index 0000000000..6740d1d7be --- /dev/null +++ b/target/linux/generic/pending-6.6/477-mtd-spi-nor-add-eon-en25qx128a.patch @@ -0,0 +1,21 @@ +From: Christian Marangi +Subject: kernel/mtd: add support for EON EN25QX128A + +Add support for EON EN25QX128A with no flags as it does +support SFDP parsing. + +Signed-off-by: Christian Marangi +--- + drivers/mtd/spi-nor/spi-nor.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/mtd/spi-nor/eon.c ++++ b/drivers/mtd/spi-nor/eon.c +@@ -19,6 +19,7 @@ static const struct flash_info eon_nor_p + NO_SFDP_FLAGS(SECT_4K) }, + { "en25q128", INFO(0x1c3018, 0, 64 * 1024, 256) + NO_SFDP_FLAGS(SECT_4K) }, ++ { "en25qx128a", INFO(0x1c7118, 0, 64 * 1024, 256) }, + { "en25q80a", INFO(0x1c3014, 0, 64 * 1024, 16) + NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ) }, + { "en25qh16", INFO(0x1c7015, 0, 64 * 1024, 32) diff --git a/target/linux/generic/pending-6.6/479-mtd-spi-nor-add-xtx-xt25f128b.patch b/target/linux/generic/pending-6.6/479-mtd-spi-nor-add-xtx-xt25f128b.patch new file mode 100644 index 0000000000..d2a9fb3436 --- /dev/null +++ b/target/linux/generic/pending-6.6/479-mtd-spi-nor-add-xtx-xt25f128b.patch @@ -0,0 +1,81 @@ +From patchwork Thu Feb 6 17:19:41 2020 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Daniel Golle +X-Patchwork-Id: 1234465 +Date: Thu, 6 Feb 2020 19:19:41 +0200 +From: Daniel Golle +To: linux-mtd@lists.infradead.org +Subject: [PATCH v2] mtd: spi-nor: Add support for xt25f128b chip +Message-ID: <20200206171941.GA2398@makrotopia.org> +MIME-Version: 1.0 +Content-Disposition: inline +List-Subscribe: , + +Cc: Eitan Cohen , Piotr Dymacz , + Tudor Ambarus +Sender: "linux-mtd" +Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org + +Add XT25F128B made by XTX Technology (Shenzhen) Limited. +This chip supports dual and quad read and uniform 4K-byte erase. +Verified on Teltonika RUT955 which comes with XT25F128B in recent +versions of the device. + +Signed-off-by: Daniel Golle +Signed-off-by: Felix Fietkau +--- + drivers/mtd/spi-nor/spi-nor.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/mtd/spi-nor/Makefile ++++ b/drivers/mtd/spi-nor/Makefile +@@ -17,6 +17,7 @@ spi-nor-objs += sst.o + spi-nor-objs += winbond.o + spi-nor-objs += xilinx.o + spi-nor-objs += xmc.o ++spi-nor-objs += xtx.o + spi-nor-$(CONFIG_DEBUG_FS) += debugfs.o + obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o + +--- /dev/null ++++ b/drivers/mtd/spi-nor/xtx.c +@@ -0,0 +1,17 @@ ++// SPDX-License-Identifier: GPL-2.0 ++#include ++ ++#include "core.h" ++ ++static const struct flash_info xtx_parts[] = { ++ /* XTX Technology (Shenzhen) Limited */ ++ { "xt25f128b", INFO(0x0B4018, 0, 64 * 1024, 256) ++ NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | ++ SPI_NOR_QUAD_READ) }, ++}; ++ ++const struct spi_nor_manufacturer spi_nor_xtx = { ++ .name = "xtx", ++ .parts = xtx_parts, ++ .nparts = ARRAY_SIZE(xtx_parts), ++}; +--- a/drivers/mtd/spi-nor/core.c ++++ b/drivers/mtd/spi-nor/core.c +@@ -1636,6 +1636,7 @@ static const struct spi_nor_manufacturer + &spi_nor_winbond, + &spi_nor_xilinx, + &spi_nor_xmc, ++ &spi_nor_xtx, + }; + + static const struct flash_info *spi_nor_match_id(struct spi_nor *nor, +--- a/drivers/mtd/spi-nor/core.h ++++ b/drivers/mtd/spi-nor/core.h +@@ -633,6 +633,7 @@ extern const struct spi_nor_manufacturer + extern const struct spi_nor_manufacturer spi_nor_winbond; + extern const struct spi_nor_manufacturer spi_nor_xilinx; + extern const struct spi_nor_manufacturer spi_nor_xmc; ++extern const struct spi_nor_manufacturer spi_nor_xtx; + + extern const struct attribute_group *spi_nor_sysfs_groups[]; + diff --git a/target/linux/generic/pending-6.6/481-mtd-spi-nor-add-support-for-Gigadevice-GD25D05.patch b/target/linux/generic/pending-6.6/481-mtd-spi-nor-add-support-for-Gigadevice-GD25D05.patch new file mode 100644 index 0000000000..581e06190b --- /dev/null +++ b/target/linux/generic/pending-6.6/481-mtd-spi-nor-add-support-for-Gigadevice-GD25D05.patch @@ -0,0 +1,23 @@ +From d68b4aa22e8c625685bfad642dd7337948dc0ad1 Mon Sep 17 00:00:00 2001 +From: Koen Vandeputte +Date: Mon, 6 Jan 2020 13:07:56 +0100 +Subject: [PATCH] mtd: spi-nor: add support for Gigadevice GD25D05 + +Signed-off-by: Koen Vandeputte +--- + drivers/mtd/spi-nor/spi-nor.c | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/drivers/mtd/spi-nor/gigadevice.c ++++ b/drivers/mtd/spi-nor/gigadevice.c +@@ -34,6 +34,10 @@ static const struct spi_nor_fixups gd25q + }; + + static const struct flash_info gigadevice_nor_parts[] = { ++ { "gd25q05", INFO(0xc84010, 0, 64 * 1024, 1) ++ FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) ++ NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | ++ SPI_NOR_QUAD_READ) }, + { "gd25q16", INFO(0xc84015, 0, 64 * 1024, 32) + FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | diff --git a/target/linux/generic/pending-6.6/482-mtd-spi-nor-add-gd25q512.patch b/target/linux/generic/pending-6.6/482-mtd-spi-nor-add-gd25q512.patch new file mode 100644 index 0000000000..ddd3405ae7 --- /dev/null +++ b/target/linux/generic/pending-6.6/482-mtd-spi-nor-add-gd25q512.patch @@ -0,0 +1,23 @@ +From f8943df3beb0d3f9754bb35320c3a378727175a8 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +Date: Thu, 14 Jul 2022 08:38:07 +0200 +Subject: [PATCH] spi-nor/gigadevic: add gd25q512 + +--- + drivers/mtd/spi-nor/gigadevice.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/mtd/spi-nor/gigadevice.c ++++ b/drivers/mtd/spi-nor/gigadevice.c +@@ -71,6 +71,11 @@ static const struct flash_info gigadevic + FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6) + FIXUP_FLAGS(SPI_NOR_4B_OPCODES) + .fixups = &gd25q256_fixups }, ++ { "gd25q512", INFO(0xc84020, 0, 64 * 1024, 1024) ++ FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) ++ FIXUP_FLAGS(SPI_NOR_4B_OPCODES) ++ NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | ++ SPI_NOR_QUAD_READ) }, + }; + + const struct spi_nor_manufacturer spi_nor_gigadevice = { diff --git a/target/linux/generic/pending-6.6/484-mtd-spi-nor-add-esmt-f25l16pa.patch b/target/linux/generic/pending-6.6/484-mtd-spi-nor-add-esmt-f25l16pa.patch new file mode 100644 index 0000000000..d5ebe20309 --- /dev/null +++ b/target/linux/generic/pending-6.6/484-mtd-spi-nor-add-esmt-f25l16pa.patch @@ -0,0 +1,24 @@ +From 87363cc0e522de3294ea6ae10fb468d2a8d6fb2f Mon Sep 17 00:00:00 2001 +From: OpenWrt community +Date: Wed, 13 Jul 2022 12:17:21 +0200 +Subject: [PATCH] spi-nor/esmt.c: add esmt f25l16pa + +This fixes support for Dongwon T&I DW02-412H which uses F25L16PA(2S) +flash. + +--- + drivers/mtd/spi-nor/esmt.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/mtd/spi-nor/esmt.c ++++ b/drivers/mtd/spi-nor/esmt.c +@@ -10,6 +10,9 @@ + + static const struct flash_info esmt_nor_parts[] = { + /* ESMT */ ++ { "f25l16pa-2s", INFO(0x8c2115, 0, 64 * 1024, 32) ++ FLAGS(SPI_NOR_HAS_LOCK) ++ NO_SFDP_FLAGS(SECT_4K) }, + { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64) + FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) + NO_SFDP_FLAGS(SECT_4K) }, diff --git a/target/linux/generic/pending-6.6/485-mtd-spi-nor-add-xmc-xm25qh128c.patch b/target/linux/generic/pending-6.6/485-mtd-spi-nor-add-xmc-xm25qh128c.patch new file mode 100644 index 0000000000..e8583cc257 --- /dev/null +++ b/target/linux/generic/pending-6.6/485-mtd-spi-nor-add-xmc-xm25qh128c.patch @@ -0,0 +1,25 @@ +From f6b33d850f7f12555df2fa0e3349b33427bf5890 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +Date: Wed, 13 Jul 2022 12:19:01 +0200 +Subject: [PATCH] spi-nor/xmc.c: add xm25qh128c + +The XMC XM25QH128C is a 16MB SPI NOR chip. The patch is verified on +Ruijie RG-EW3200GX PRO. +Datasheet available at https://www.xmcwh.com/uploads/435/XM25QH128C.pdf + +--- + drivers/mtd/spi-nor/xmc.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/mtd/spi-nor/xmc.c ++++ b/drivers/mtd/spi-nor/xmc.c +@@ -16,6 +16,9 @@ static const struct flash_info xmc_nor_p + { "XM25QH128A", INFO(0x207018, 0, 64 * 1024, 256) + NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | + SPI_NOR_QUAD_READ) }, ++ { "XM25QH128C", INFO(0x204018, 0, 64 * 1024, 256) ++ NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | ++ SPI_NOR_QUAD_READ) }, + }; + + const struct spi_nor_manufacturer spi_nor_xmc = { diff --git a/target/linux/generic/pending-6.6/486-01-mtd-spinand-add-support-for-ESMT-F50x1G41LB.patch b/target/linux/generic/pending-6.6/486-01-mtd-spinand-add-support-for-ESMT-F50x1G41LB.patch new file mode 100644 index 0000000000..b8116db842 --- /dev/null +++ b/target/linux/generic/pending-6.6/486-01-mtd-spinand-add-support-for-ESMT-F50x1G41LB.patch @@ -0,0 +1,143 @@ +From a43b844cb40bf1b783055fdc81b7f991e21e7e76 Mon Sep 17 00:00:00 2001 +From: Chuanhong Guo +Date: Wed, 13 Apr 2022 11:58:17 +0800 +Subject: [PATCH] mtd: spinand: add support for ESMT F50x1G41LB + +This patch adds support for ESMT F50L1G41LB and F50D1G41LB. +It seems that ESMT likes to use random JEDEC ID from other vendors. +Their 1G chips uses 0xc8 from GigaDevice and 2G/4G chips uses 0x2c from +Micron. For this reason, the ESMT entry is named esmt_c8 with explicit +JEDEC ID in variable name. + +Datasheets: +https://www.esmt.com.tw/upload/pdf/ESMT/datasheets/F50L1G41LB(2M).pdf +https://www.esmt.com.tw/upload/pdf/ESMT/datasheets/F50D1G41LB(2M).pdf + +Signed-off-by: Chuanhong Guo +--- + drivers/mtd/nand/spi/Makefile | 2 +- + drivers/mtd/nand/spi/core.c | 1 + + drivers/mtd/nand/spi/esmt.c | 89 +++++++++++++++++++++++++++++++++++ + include/linux/mtd/spinand.h | 1 + + 4 files changed, 92 insertions(+), 1 deletion(-) + create mode 100644 drivers/mtd/nand/spi/esmt.c + +--- a/drivers/mtd/nand/spi/Makefile ++++ b/drivers/mtd/nand/spi/Makefile +@@ -1,3 +1,3 @@ + # SPDX-License-Identifier: GPL-2.0 +-spinand-objs := core.o ato.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o ++spinand-objs := core.o ato.o esmt.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o + obj-$(CONFIG_MTD_SPI_NAND) += spinand.o +--- a/drivers/mtd/nand/spi/core.c ++++ b/drivers/mtd/nand/spi/core.c +@@ -938,6 +938,7 @@ static const struct nand_ops spinand_ops + + static const struct spinand_manufacturer *spinand_manufacturers[] = { + &ato_spinand_manufacturer, ++ &esmt_c8_spinand_manufacturer, + &gigadevice_spinand_manufacturer, + ¯onix_spinand_manufacturer, + µn_spinand_manufacturer, +--- /dev/null ++++ b/drivers/mtd/nand/spi/esmt.c +@@ -0,0 +1,89 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Author: ++ * Chuanhong Guo ++ */ ++ ++#include ++#include ++#include ++ ++/* ESMT uses GigaDevice 0xc8 JECDEC ID on some SPI NANDs */ ++#define SPINAND_MFR_ESMT_C8 0xc8 ++ ++static SPINAND_OP_VARIANTS(read_cache_variants, ++ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); ++ ++static SPINAND_OP_VARIANTS(write_cache_variants, ++ SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), ++ SPINAND_PROG_LOAD(true, 0, NULL, 0)); ++ ++static SPINAND_OP_VARIANTS(update_cache_variants, ++ SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), ++ SPINAND_PROG_LOAD(false, 0, NULL, 0)); ++ ++static int f50l1g41lb_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) ++{ ++ if (section > 3) ++ return -ERANGE; ++ ++ region->offset = 16 * section + 8; ++ region->length = 8; ++ ++ return 0; ++} ++ ++static int f50l1g41lb_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 = 6; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops f50l1g41lb_ooblayout = { ++ .ecc = f50l1g41lb_ooblayout_ecc, ++ .free = f50l1g41lb_ooblayout_free, ++}; ++ ++static const struct spinand_info esmt_c8_spinand_table[] = { ++ SPINAND_INFO("F50L1G41LB", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01), ++ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(1, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ 0, ++ SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)), ++ SPINAND_INFO("F50D1G41LB", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x11), ++ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(1, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ 0, ++ SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)), ++}; ++ ++static const struct spinand_manufacturer_ops esmt_spinand_manuf_ops = { ++}; ++ ++const struct spinand_manufacturer esmt_c8_spinand_manufacturer = { ++ .id = SPINAND_MFR_ESMT_C8, ++ .name = "ESMT", ++ .chips = esmt_c8_spinand_table, ++ .nchips = ARRAY_SIZE(esmt_c8_spinand_table), ++ .ops = &esmt_spinand_manuf_ops, ++}; +--- a/include/linux/mtd/spinand.h ++++ b/include/linux/mtd/spinand.h +@@ -261,6 +261,7 @@ struct spinand_manufacturer { + + /* SPI NAND manufacturers */ + extern const struct spinand_manufacturer ato_spinand_manufacturer; ++extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer; + extern const struct spinand_manufacturer gigadevice_spinand_manufacturer; + extern const struct spinand_manufacturer macronix_spinand_manufacturer; + extern const struct spinand_manufacturer micron_spinand_manufacturer; diff --git a/target/linux/generic/pending-6.6/487-mtd-spinand-Add-support-for-Etron-EM73D044VCx.patch b/target/linux/generic/pending-6.6/487-mtd-spinand-Add-support-for-Etron-EM73D044VCx.patch new file mode 100644 index 0000000000..81378fba86 --- /dev/null +++ b/target/linux/generic/pending-6.6/487-mtd-spinand-Add-support-for-Etron-EM73D044VCx.patch @@ -0,0 +1,168 @@ +From f32085fc0b87049491b07e198d924d738a1a2834 Mon Sep 17 00:00:00 2001 +From: Daniel Danzberger +Date: Wed, 3 Aug 2022 17:31:03 +0200 +Subject: [PATCH] mtd: spinand: Add support for Etron EM73D044VCx + +Airoha is a new ARM platform based on Cortex-A53 which has recently been +merged into linux-next. + +Due to BootROM limitations on this platform, the Cortex-A53 can't run in +Aarch64 mode and code must be compiled for 32-Bit ARM. + +This support is based mostly on those linux-next commits backported +for kernel 5.15. + +Patches: +1 - platform support = linux-next +2 - clock driver = linux-next +3 - gpio driver = linux-next +4 - linux,usable-memory-range dts support = linux-next +5 - mtd spinand driver +6 - spi driver +7 - pci driver (kconfig only, uses mediatek PCI) = linux-next + +Still missing: +- Ethernet driver +- Sysupgrade support + +A.t.m there exists one subtarget EN7523 with only one evaluation +board. + +The initramfs can be run with the following commands from u-boot: +- +u-boot> setenv bootfile \ + openwrt-airoha-airoha_en7523-evb-initramfs-kernel.bin +u-boot> tftpboot +u-boot> bootm 0x81800000 +- + +Submitted-by: Daniel Danzberger + +--- a/drivers/mtd/nand/spi/Makefile ++++ b/drivers/mtd/nand/spi/Makefile +@@ -1,3 +1,3 @@ + # SPDX-License-Identifier: GPL-2.0 +-spinand-objs := core.o ato.o esmt.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o ++spinand-objs := core.o ato.o esmt.o etron.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o + obj-$(CONFIG_MTD_SPI_NAND) += spinand.o +--- a/drivers/mtd/nand/spi/core.c ++++ b/drivers/mtd/nand/spi/core.c +@@ -939,6 +939,7 @@ static const struct nand_ops spinand_ops + static const struct spinand_manufacturer *spinand_manufacturers[] = { + &ato_spinand_manufacturer, + &esmt_c8_spinand_manufacturer, ++ &etron_spinand_manufacturer, + &gigadevice_spinand_manufacturer, + ¯onix_spinand_manufacturer, + µn_spinand_manufacturer, +--- /dev/null ++++ b/drivers/mtd/nand/spi/etron.c +@@ -0,0 +1,98 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++#include ++#include ++#include ++ ++#define SPINAND_MFR_ETRON 0xd5 ++ ++ ++static SPINAND_OP_VARIANTS(read_cache_variants, ++ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); ++ ++static SPINAND_OP_VARIANTS(write_cache_variants, ++ SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), ++ SPINAND_PROG_LOAD(true, 0, NULL, 0)); ++ ++static SPINAND_OP_VARIANTS(update_cache_variants, ++ SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), ++ SPINAND_PROG_LOAD(false, 0, NULL, 0)); ++ ++static int etron_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->offset = 72; ++ oobregion->length = 56; ++ ++ return 0; ++} ++ ++static int etron_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->offset = 1; ++ oobregion->length = 71; ++ ++ return 0; ++} ++ ++static int etron_ecc_get_status(struct spinand_device *spinand, u8 status) ++{ ++ switch (status & STATUS_ECC_MASK) { ++ case STATUS_ECC_NO_BITFLIPS: ++ return 0; ++ ++ case STATUS_ECC_HAS_BITFLIPS: ++ /* Between 1-7 bitflips were corrected */ ++ return 7; ++ ++ case STATUS_ECC_MASK: ++ /* Maximum bitflips were corrected */ ++ return 8; ++ ++ case STATUS_ECC_UNCOR_ERROR: ++ return -EBADMSG; ++ } ++ ++ return -EINVAL; ++} ++ ++static const struct mtd_ooblayout_ops etron_ooblayout = { ++ .ecc = etron_ooblayout_ecc, ++ .free = etron_ooblayout_free, ++}; ++ ++static const struct spinand_info etron_spinand_table[] = { ++ SPINAND_INFO("EM73D044VCx", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x1f), ++ // bpc, pagesize, oobsize, pagesperblock, bperlun, maxbadplun, ppl, lpt, #t ++ 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), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&etron_ooblayout, etron_ecc_get_status)), ++}; ++ ++static const struct spinand_manufacturer_ops etron_spinand_manuf_ops = { ++}; ++ ++const struct spinand_manufacturer etron_spinand_manufacturer = { ++ .id = SPINAND_MFR_ETRON, ++ .name = "Etron", ++ .chips = etron_spinand_table, ++ .nchips = ARRAY_SIZE(etron_spinand_table), ++ .ops = &etron_spinand_manuf_ops, ++}; +--- a/include/linux/mtd/spinand.h ++++ b/include/linux/mtd/spinand.h +@@ -262,6 +262,7 @@ struct spinand_manufacturer { + /* SPI NAND manufacturers */ + extern const struct spinand_manufacturer ato_spinand_manufacturer; + extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer; ++extern const struct spinand_manufacturer etron_spinand_manufacturer; + extern const struct spinand_manufacturer gigadevice_spinand_manufacturer; + extern const struct spinand_manufacturer macronix_spinand_manufacturer; + extern const struct spinand_manufacturer micron_spinand_manufacturer; diff --git a/target/linux/generic/pending-6.6/488-mtd-spi-nor-add-xmc-xm25qh64c.patch b/target/linux/generic/pending-6.6/488-mtd-spi-nor-add-xmc-xm25qh64c.patch new file mode 100644 index 0000000000..e1e4f25e11 --- /dev/null +++ b/target/linux/generic/pending-6.6/488-mtd-spi-nor-add-xmc-xm25qh64c.patch @@ -0,0 +1,23 @@ +From: Joe Mullally +Subject: mtd/spi-nor/xmc: add support for XMC XM25QH64C + +The XMC XM25QH64C is a 8MB SPI NOR chip. The patch is verified on TL-WPA8631P v3. +Datasheet available at https://www.xmcwh.com/uploads/442/XM25QH64C.pdf + +Signed-off-by: Joe Mullally +--- + drivers/mtd/spi-nor/xmc.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/mtd/spi-nor/xmc.c ++++ b/drivers/mtd/spi-nor/xmc.c +@@ -13,6 +13,9 @@ static const struct flash_info xmc_nor_p + { "XM25QH64A", INFO(0x207017, 0, 64 * 1024, 128) + NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | + SPI_NOR_QUAD_READ) }, ++ { "XM25QH64C", INFO(0x204017, 0, 64 * 1024, 128) ++ NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | ++ SPI_NOR_QUAD_READ) }, + { "XM25QH128A", INFO(0x207018, 0, 64 * 1024, 256) + NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | + SPI_NOR_QUAD_READ) }, diff --git a/target/linux/generic/pending-6.6/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch b/target/linux/generic/pending-6.6/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch new file mode 100644 index 0000000000..2eccb9d3bb --- /dev/null +++ b/target/linux/generic/pending-6.6/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch @@ -0,0 +1,98 @@ +From: Daniel Golle +Subject: ubi: auto-attach mtd device named "ubi" or "data" on boot + +Signed-off-by: Daniel Golle +--- + drivers/mtd/ubi/build.c | 36 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 36 insertions(+) + +--- a/drivers/mtd/ubi/build.c ++++ b/drivers/mtd/ubi/build.c +@@ -1263,6 +1263,74 @@ static struct mtd_notifier ubi_mtd_notif + .remove = ubi_notify_remove, + }; + ++ ++/* ++ * This function tries attaching mtd partitions named either "ubi" or "data" ++ * during boot. ++ */ ++static void __init ubi_auto_attach(void) ++{ ++ int err; ++ struct mtd_info *mtd; ++ loff_t offset = 0; ++ size_t len; ++ char magic[4]; ++ ++ /* try attaching mtd device named "ubi" or "data" */ ++ mtd = open_mtd_device("ubi"); ++ if (IS_ERR(mtd)) ++ mtd = open_mtd_device("data"); ++ ++ if (IS_ERR(mtd)) ++ return; ++ ++ /* get the first not bad block */ ++ if (mtd_can_have_bb(mtd)) ++ while (mtd_block_isbad(mtd, offset)) { ++ offset += mtd->erasesize; ++ ++ if (offset > mtd->size) { ++ pr_err("UBI error: Failed to find a non-bad " ++ "block on mtd%d\n", mtd->index); ++ goto cleanup; ++ } ++ } ++ ++ /* check if the read from flash was successful */ ++ err = mtd_read(mtd, offset, 4, &len, (void *) magic); ++ if ((err && !mtd_is_bitflip(err)) || len != 4) { ++ pr_err("UBI error: unable to read from mtd%d\n", mtd->index); ++ goto cleanup; ++ } ++ ++ /* check for a valid ubi magic */ ++ if (strncmp(magic, "UBI#", 4)) { ++ pr_err("UBI error: no valid UBI magic found inside mtd%d\n", mtd->index); ++ goto cleanup; ++ } ++ ++ /* don't auto-add media types where UBI doesn't makes sense */ ++ if (mtd->type != MTD_NANDFLASH && ++ mtd->type != MTD_NORFLASH && ++ mtd->type != MTD_DATAFLASH && ++ mtd->type != MTD_MLCNANDFLASH) ++ goto cleanup; ++ ++ mutex_lock(&ubi_devices_mutex); ++ pr_notice("UBI: auto-attach mtd%d\n", mtd->index); ++ err = ubi_attach_mtd_dev(mtd, UBI_DEV_NUM_AUTO, 0, 0, false); ++ mutex_unlock(&ubi_devices_mutex); ++ if (err < 0) { ++ pr_err("UBI error: cannot attach mtd%d\n", mtd->index); ++ goto cleanup; ++ } ++ ++ return; ++ ++cleanup: ++ put_mtd_device(mtd); ++} ++ + static int __init ubi_init_attach(void) + { + int err, i, k; +@@ -1313,6 +1381,12 @@ static int __init ubi_init_attach(void) + } + } + ++ /* auto-attach mtd devices only if built-in to the kernel and no ubi.mtd ++ * parameter was given */ ++ if (IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV) && ++ !ubi_is_module() && !mtd_devs) ++ ubi_auto_attach(); ++ + return 0; + + out_detach: diff --git a/target/linux/generic/pending-6.6/491-ubi-auto-create-ubiblock-device-for-rootfs.patch b/target/linux/generic/pending-6.6/491-ubi-auto-create-ubiblock-device-for-rootfs.patch new file mode 100644 index 0000000000..a43da2a572 --- /dev/null +++ b/target/linux/generic/pending-6.6/491-ubi-auto-create-ubiblock-device-for-rootfs.patch @@ -0,0 +1,77 @@ +From: Daniel Golle +Subject: ubi: auto-create ubiblock device for rootfs + +Signed-off-by: Daniel Golle +--- + drivers/mtd/ubi/block.c | 42 ++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 42 insertions(+) + +--- a/drivers/mtd/ubi/block.c ++++ b/drivers/mtd/ubi/block.c +@@ -644,10 +644,47 @@ match_volume_desc(struct ubi_volume_info + return true; + } + ++#define UBIFS_NODE_MAGIC 0x06101831 ++static inline int ubi_vol_is_ubifs(struct ubi_volume_desc *desc) ++{ ++ int ret; ++ uint32_t magic_of, magic; ++ ret = ubi_read(desc, 0, (char *)&magic_of, 0, 4); ++ if (ret) ++ return 0; ++ magic = le32_to_cpu(magic_of); ++ return magic == UBIFS_NODE_MAGIC; ++} ++ ++static void __init ubiblock_create_auto_rootfs(struct ubi_volume_info *vi) ++{ ++ int ret, is_ubifs; ++ struct ubi_volume_desc *desc; ++ ++ if (strcmp(vi->name, "rootfs") && ++ strcmp(vi->name, "fit")) ++ return; ++ ++ desc = ubi_open_volume(vi->ubi_num, vi->vol_id, UBI_READONLY); ++ if (IS_ERR(desc)) ++ return; ++ ++ is_ubifs = ubi_vol_is_ubifs(desc); ++ ubi_close_volume(desc); ++ if (is_ubifs) ++ return; ++ ++ ret = ubiblock_create(vi); ++ if (ret) ++ pr_err("UBI error: block: can't add '%s' volume, err=%d\n", ++ vi->name, ret); ++} ++ + static void + ubiblock_create_from_param(struct ubi_volume_info *vi) + { + int i, ret = 0; ++ bool got_param = false; + struct ubiblock_param *p; + + /* +@@ -660,6 +697,7 @@ ubiblock_create_from_param(struct ubi_vo + if (!match_volume_desc(vi, p->name, p->ubi_num, p->vol_id)) + continue; + ++ got_param = true; + ret = ubiblock_create(vi); + if (ret) { + pr_err( +@@ -668,6 +706,10 @@ ubiblock_create_from_param(struct ubi_vo + } + break; + } ++ ++ /* auto-attach "rootfs" volume if existing and non-ubifs */ ++ if (!got_param && IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV)) ++ ubiblock_create_auto_rootfs(vi); + } + + static int ubiblock_notify(struct notifier_block *nb, diff --git a/target/linux/generic/pending-6.6/492-try-auto-mounting-ubi0-rootfs-in-init-do_mounts.c.patch b/target/linux/generic/pending-6.6/492-try-auto-mounting-ubi0-rootfs-in-init-do_mounts.c.patch new file mode 100644 index 0000000000..f95ec46f14 --- /dev/null +++ b/target/linux/generic/pending-6.6/492-try-auto-mounting-ubi0-rootfs-in-init-do_mounts.c.patch @@ -0,0 +1,53 @@ +From: Daniel Golle +Subject: try auto-mounting ubi0:rootfs in init/do_mounts.c + +Signed-off-by: Daniel Golle +--- + init/do_mounts.c | 26 +++++++++++++++++++++++++- + 1 file changed, 25 insertions(+), 1 deletion(-) + +--- a/init/do_mounts.c ++++ b/init/do_mounts.c +@@ -446,7 +446,30 @@ retry: + out: + put_page(page); + } +- ++ ++#ifdef CONFIG_MTD_ROOTFS_ROOT_DEV ++static int __init mount_ubi_rootfs(void) ++{ ++ int flags = MS_SILENT; ++ int err, tried = 0; ++ ++ while (tried < 2) { ++ err = do_mount_root("ubi0:rootfs", "ubifs", flags, \ ++ root_mount_data); ++ switch (err) { ++ case -EACCES: ++ flags |= MS_RDONLY; ++ tried++; ++ break; ++ default: ++ return err; ++ } ++ } ++ ++ return -EINVAL; ++} ++#endif ++ + #ifdef CONFIG_ROOT_NFS + + #define NFSROOT_TIMEOUT_MIN 5 +@@ -579,6 +602,10 @@ void __init mount_root(void) + return; + } + #endif ++#ifdef CONFIG_MTD_ROOTFS_ROOT_DEV ++ if (!mount_ubi_rootfs()) ++ return; ++#endif + if (ROOT_DEV == 0 && root_device_name && root_fs_names) { + if (mount_nodev_root() == 0) + return; diff --git a/target/linux/generic/pending-6.6/493-ubi-set-ROOT_DEV-to-ubiblock-rootfs-if-unset.patch b/target/linux/generic/pending-6.6/493-ubi-set-ROOT_DEV-to-ubiblock-rootfs-if-unset.patch new file mode 100644 index 0000000000..5357c7e15d --- /dev/null +++ b/target/linux/generic/pending-6.6/493-ubi-set-ROOT_DEV-to-ubiblock-rootfs-if-unset.patch @@ -0,0 +1,34 @@ +From: Daniel Golle +Subject: ubi: set ROOT_DEV to ubiblock "rootfs" if unset + +Signed-off-by: Daniel Golle +--- + drivers/mtd/ubi/block.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/drivers/mtd/ubi/block.c ++++ b/drivers/mtd/ubi/block.c +@@ -43,6 +43,7 @@ + #include + #include + #include ++#include + + #include "ubi-media.h" + #include "ubi.h" +@@ -460,6 +461,15 @@ int ubiblock_create(struct ubi_volume_in + dev_info(disk_to_dev(dev->gd), "created from ubi%d:%d(%s)", + dev->ubi_num, dev->vol_id, vi->name); + mutex_unlock(&devices_mutex); ++ ++ if (!strcmp(vi->name, "rootfs") && ++ IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV) && ++ ROOT_DEV == 0) { ++ pr_notice("ubiblock: device ubiblock%d_%d (%s) set to be root filesystem\n", ++ dev->ubi_num, dev->vol_id, vi->name); ++ ROOT_DEV = MKDEV(gd->major, gd->first_minor); ++ } ++ + return 0; + + out_destroy_wq: diff --git a/target/linux/generic/pending-6.6/494-mtd-ubi-add-EOF-marker-support.patch b/target/linux/generic/pending-6.6/494-mtd-ubi-add-EOF-marker-support.patch new file mode 100644 index 0000000000..fc48146221 --- /dev/null +++ b/target/linux/generic/pending-6.6/494-mtd-ubi-add-EOF-marker-support.patch @@ -0,0 +1,60 @@ +From: Gabor Juhos +Subject: mtd: add EOF marker support to the UBI layer + +Signed-off-by: Gabor Juhos +--- + drivers/mtd/ubi/attach.c | 25 ++++++++++++++++++++++--- + drivers/mtd/ubi/ubi.h | 1 + + 2 files changed, 23 insertions(+), 3 deletions(-) + +--- a/drivers/mtd/ubi/attach.c ++++ b/drivers/mtd/ubi/attach.c +@@ -926,6 +926,13 @@ static bool vol_ignored(int vol_id) + #endif + } + ++static bool ec_hdr_has_eof(struct ubi_ec_hdr *ech) ++{ ++ return ech->padding1[0] == 'E' && ++ ech->padding1[1] == 'O' && ++ ech->padding1[2] == 'F'; ++} ++ + /** + * scan_peb - scan and process UBI headers of a PEB. + * @ubi: UBI device description object +@@ -958,9 +965,21 @@ static int scan_peb(struct ubi_device *u + return 0; + } + +- err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0); +- if (err < 0) +- return err; ++ if (!ai->eof_found) { ++ err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0); ++ if (err < 0) ++ return err; ++ ++ if (ec_hdr_has_eof(ech)) { ++ pr_notice("UBI: EOF marker found, PEBs from %d will be erased\n", ++ pnum); ++ ai->eof_found = true; ++ } ++ } ++ ++ if (ai->eof_found) ++ err = UBI_IO_FF_BITFLIPS; ++ + switch (err) { + case 0: + break; +--- a/drivers/mtd/ubi/ubi.h ++++ b/drivers/mtd/ubi/ubi.h +@@ -780,6 +780,7 @@ struct ubi_attach_info { + int mean_ec; + uint64_t ec_sum; + int ec_count; ++ bool eof_found; + struct kmem_cache *aeb_slab_cache; + struct ubi_ec_hdr *ech; + struct ubi_vid_io_buf *vidb; diff --git a/target/linux/generic/pending-6.6/496-dt-bindings-add-bindings-for-mtd-concat-devices.patch b/target/linux/generic/pending-6.6/496-dt-bindings-add-bindings-for-mtd-concat-devices.patch new file mode 100644 index 0000000000..01f3b9ec2d --- /dev/null +++ b/target/linux/generic/pending-6.6/496-dt-bindings-add-bindings-for-mtd-concat-devices.patch @@ -0,0 +1,52 @@ +From 5734c6669fba7ddb5ef491ccff7159d15dba0b59 Mon Sep 17 00:00:00 2001 +From: Bernhard Frauendienst +Date: Wed, 5 Sep 2018 01:32:51 +0200 +Subject: [PATCH 496/497] dt-bindings: add bindings for mtd-concat devices + +Document virtual mtd-concat device bindings. + +Signed-off-by: Bernhard Frauendienst +--- + .../devicetree/bindings/mtd/mtd-concat.txt | 36 +++++++++++++++++++ + 1 file changed, 36 insertions(+) + create mode 100644 Documentation/devicetree/bindings/mtd/mtd-concat.txt + +--- /dev/null ++++ b/Documentation/devicetree/bindings/mtd/mtd-concat.txt +@@ -0,0 +1,36 @@ ++Virtual MTD concat device ++ ++Requires properties: ++- devices: list of phandles to mtd nodes that should be concatenated ++ ++Example: ++ ++&spi { ++ flash0: flash@0 { ++ ... ++ }; ++ flash1: flash@1 { ++ ... ++ }; ++}; ++ ++flash { ++ compatible = "mtd-concat"; ++ ++ devices = <&flash0 &flash1>; ++ ++ partitions { ++ compatible = "fixed-partitions"; ++ ++ partition@0 { ++ label = "boot"; ++ reg = <0x0000000 0x0040000>; ++ read-only; ++ }; ++ ++ partition@40000 { ++ label = "firmware"; ++ reg = <0x0040000 0x1fc0000>; ++ }; ++ } ++} diff --git a/target/linux/generic/pending-6.6/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch b/target/linux/generic/pending-6.6/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch new file mode 100644 index 0000000000..e0cbc4508b --- /dev/null +++ b/target/linux/generic/pending-6.6/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch @@ -0,0 +1,216 @@ +From e53f712d8eac71f54399b61038ccf87d2cee99d7 Mon Sep 17 00:00:00 2001 +From: Bernhard Frauendienst +Date: Sat, 25 Aug 2018 12:35:22 +0200 +Subject: [PATCH 497/497] mtd: mtdconcat: add dt driver for concat devices + +Some mtd drivers like physmap variants have support for concatenating +multiple mtd devices, but there is no generic way to define such a +concat device from within the device tree. + +This is useful for some SoC boards that use multiple flash chips as +memory banks of a single mtd device, with partitions spanning chip +borders. + +This commit adds a driver for creating virtual mtd-concat devices. They +must have a compatible = "mtd-concat" line, and define a list of devices +to concat in the 'devices' property, for example: + +flash { + compatible = "mtd-concat"; + + devices = <&flash0 &flash1>; + + partitions { + ... + }; +}; + +The driver is added to the very end of the mtd Makefile to increase the +likelyhood of all child devices already being loaded at the time of +probing, preventing unnecessary deferred probes. + +Signed-off-by: Bernhard Frauendienst +--- + drivers/mtd/Kconfig | 2 + + drivers/mtd/Makefile | 3 + + drivers/mtd/composite/Kconfig | 12 +++ + drivers/mtd/composite/Makefile | 6 ++ + drivers/mtd/composite/virt_concat.c | 128 ++++++++++++++++++++++++++++ + 5 files changed, 151 insertions(+) + create mode 100644 drivers/mtd/composite/Kconfig + create mode 100644 drivers/mtd/composite/Makefile + create mode 100644 drivers/mtd/composite/virt_concat.c + +--- a/drivers/mtd/Kconfig ++++ b/drivers/mtd/Kconfig +@@ -241,4 +241,6 @@ source "drivers/mtd/ubi/Kconfig" + + source "drivers/mtd/hyperbus/Kconfig" + ++source "drivers/mtd/composite/Kconfig" ++ + endif # MTD +--- a/drivers/mtd/Makefile ++++ b/drivers/mtd/Makefile +@@ -33,3 +33,6 @@ obj-y += chips/ lpddr/ maps/ devices/ n + obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/ + obj-$(CONFIG_MTD_UBI) += ubi/ + obj-$(CONFIG_MTD_HYPERBUS) += hyperbus/ ++ ++# Composite drivers must be loaded last ++obj-y += composite/ +--- /dev/null ++++ b/drivers/mtd/composite/Kconfig +@@ -0,0 +1,12 @@ ++menu "Composite MTD device drivers" ++ depends on MTD!=n ++ ++config MTD_VIRT_CONCAT ++ tristate "Virtual concat MTD device" ++ help ++ This driver allows creation of a virtual MTD concat device, which ++ concatenates multiple underlying MTD devices to a single device. ++ This is required by some SoC boards where multiple memory banks are ++ used as one device with partitions spanning across device boundaries. ++ ++endmenu +--- /dev/null ++++ b/drivers/mtd/composite/Makefile +@@ -0,0 +1,6 @@ ++# SPDX-License-Identifier: GPL-2.0 ++# ++# linux/drivers/mtd/composite/Makefile ++# ++ ++obj-$(CONFIG_MTD_VIRT_CONCAT) += virt_concat.o +--- /dev/null ++++ b/drivers/mtd/composite/virt_concat.c +@@ -0,0 +1,128 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Virtual concat MTD device driver ++ * ++ * Copyright (C) 2018 Bernhard Frauendienst ++ * Author: Bernhard Frauendienst, kernel@nospam.obeliks.de ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * struct of_virt_concat - platform device driver data. ++ * @cmtd the final mtd_concat device ++ * @num_devices the number of devices in @devices ++ * @devices points to an array of devices already loaded ++ */ ++struct of_virt_concat { ++ struct mtd_info *cmtd; ++ int num_devices; ++ struct mtd_info **devices; ++}; ++ ++static int virt_concat_remove(struct platform_device *pdev) ++{ ++ struct of_virt_concat *info; ++ int i; ++ ++ info = platform_get_drvdata(pdev); ++ if (!info) ++ return 0; ++ ++ // unset data for when this is called after a probe error ++ platform_set_drvdata(pdev, NULL); ++ ++ if (info->cmtd) { ++ mtd_device_unregister(info->cmtd); ++ mtd_concat_destroy(info->cmtd); ++ } ++ ++ if (info->devices) { ++ for (i = 0; i < info->num_devices; i++) ++ put_mtd_device(info->devices[i]); ++ } ++ ++ return 0; ++} ++ ++static int virt_concat_probe(struct platform_device *pdev) ++{ ++ struct device_node *node = pdev->dev.of_node; ++ struct of_phandle_iterator it; ++ struct of_virt_concat *info; ++ struct mtd_info *mtd; ++ int err = 0, count; ++ ++ count = of_count_phandle_with_args(node, "devices", NULL); ++ if (count <= 0) ++ return -EINVAL; ++ ++ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); ++ if (!info) ++ return -ENOMEM; ++ info->devices = devm_kcalloc(&pdev->dev, count, ++ sizeof(*(info->devices)), GFP_KERNEL); ++ if (!info->devices) { ++ err = -ENOMEM; ++ goto err_remove; ++ } ++ ++ platform_set_drvdata(pdev, info); ++ ++ of_for_each_phandle(&it, err, node, "devices", NULL, 0) { ++ mtd = of_get_mtd_device_by_node(it.node); ++ if (IS_ERR(mtd)) { ++ of_node_put(it.node); ++ err = -EPROBE_DEFER; ++ goto err_remove; ++ } ++ ++ info->devices[info->num_devices++] = mtd; ++ } ++ ++ info->cmtd = mtd_concat_create(info->devices, info->num_devices, ++ dev_name(&pdev->dev)); ++ if (!info->cmtd) { ++ err = -ENXIO; ++ goto err_remove; ++ } ++ ++ info->cmtd->dev.parent = &pdev->dev; ++ mtd_set_of_node(info->cmtd, node); ++ mtd_device_register(info->cmtd, NULL, 0); ++ ++ return 0; ++ ++err_remove: ++ virt_concat_remove(pdev); ++ ++ return err; ++} ++ ++static const struct of_device_id virt_concat_of_match[] = { ++ { .compatible = "mtd-concat", }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, virt_concat_of_match); ++ ++static struct platform_driver virt_concat_driver = { ++ .probe = virt_concat_probe, ++ .remove = virt_concat_remove, ++ .driver = { ++ .name = "virt-mtdconcat", ++ .of_match_table = virt_concat_of_match, ++ }, ++}; ++ ++module_platform_driver(virt_concat_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Bernhard Frauendienst "); ++MODULE_DESCRIPTION("Virtual concat MTD device driver"); diff --git a/target/linux/generic/pending-6.6/498-mtd-spi-nor-locking-support-for-MX25L6405D.patch b/target/linux/generic/pending-6.6/498-mtd-spi-nor-locking-support-for-MX25L6405D.patch new file mode 100644 index 0000000000..1a4d5a766f --- /dev/null +++ b/target/linux/generic/pending-6.6/498-mtd-spi-nor-locking-support-for-MX25L6405D.patch @@ -0,0 +1,32 @@ +From 8bf2ce6ea4ee840b70f55a27f80e1cd308051b13 Mon Sep 17 00:00:00 2001 +From: Nick Hainke +Date: Mon, 27 Dec 2021 00:38:13 +0100 +Subject: [PATCH 1/2] mtd: spi-nor: locking support for MX25L6405D + +Macronix MX25L6405D supports locking with four block-protection bits. +Currently, the driver only sets three bits. If the bootloader does not +sustain the flash chip in an unlocked state, the flash might be +non-writeable. Add the corresponding flag to enable locking support with +four bits in the status register. + +Tested on Nanostation M2 XM. + +Similar to commit 7ea40b54e83b ("mtd: spi-nor: enable locking support for +MX25L12805D") + +Signed-off-by: David Bauer +Signed-off-by: Nick Hainke +--- + drivers/mtd/spi-nor/macronix.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/mtd/spi-nor/macronix.c ++++ b/drivers/mtd/spi-nor/macronix.c +@@ -48,6 +48,7 @@ static const struct flash_info macronix_ + { "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64) + NO_SFDP_FLAGS(SECT_4K) }, + { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128) ++ FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_4BIT_BP) + NO_SFDP_FLAGS(SECT_4K) }, + { "mx25u2033e", INFO(0xc22532, 0, 64 * 1024, 4) + NO_SFDP_FLAGS(SECT_4K) }, diff --git a/target/linux/generic/pending-6.6/499-mtd-spi-nor-disable-16-bit-sr-for-macronix.patch b/target/linux/generic/pending-6.6/499-mtd-spi-nor-disable-16-bit-sr-for-macronix.patch new file mode 100644 index 0000000000..99e0fc72bf --- /dev/null +++ b/target/linux/generic/pending-6.6/499-mtd-spi-nor-disable-16-bit-sr-for-macronix.patch @@ -0,0 +1,30 @@ +From 245224608b5368c10407da07557e546743d3c489 Mon Sep 17 00:00:00 2001 +From: Nick Hainke +Date: Mon, 27 Dec 2021 09:33:13 +0100 +Subject: [PATCH 2/2] mtd: spi-nor: disable 16-bit-sr for macronix + +Macronix flash chips seem to consist of only one status register. +These chips will not work with the "16-bit Write Status (01h) Command". +Disable SNOR_F_HAS_16BIT_SR for all Macronix chips. + +Tested with MX25L6405D. + +Fixes: 39d1e3340c73 ("mtd: spi-nor: Fix clearing of QE bit on +lock()/unlock()") + +Signed-off-by: David Bauer +Signed-off-by: Nick Hainke +--- + drivers/mtd/spi-nor/macronix.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/mtd/spi-nor/macronix.c ++++ b/drivers/mtd/spi-nor/macronix.c +@@ -107,6 +107,7 @@ static void macronix_nor_default_init(st + { + nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable; + nor->params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode; ++ nor->flags &= ~SNOR_F_HAS_16BIT_SR; + nor->flags |= SNOR_F_HAS_LOCK; + } + diff --git a/target/linux/generic/pending-6.6/500-fs_cdrom_dependencies.patch b/target/linux/generic/pending-6.6/500-fs_cdrom_dependencies.patch new file mode 100644 index 0000000000..2053c0fbe2 --- /dev/null +++ b/target/linux/generic/pending-6.6/500-fs_cdrom_dependencies.patch @@ -0,0 +1,52 @@ +From af7b91bcecce0eae24e90acd35d96ecee73e1407 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +Date: Wed, 13 Jul 2022 12:21:15 +0200 +Subject: [PATCH] fs: add cdrom dependency + +--- + fs/hfs/Kconfig | 1 + + fs/hfsplus/Kconfig | 1 + + fs/isofs/Kconfig | 1 + + fs/udf/Kconfig | 1 + + 4 files changed, 4 insertions(+) + +--- a/fs/hfs/Kconfig ++++ b/fs/hfs/Kconfig +@@ -2,6 +2,7 @@ + config HFS_FS + tristate "Apple Macintosh file system support" + depends on BLOCK ++ select CDROM + select NLS + help + If you say Y here, you will be able to mount Macintosh-formatted +--- a/fs/hfsplus/Kconfig ++++ b/fs/hfsplus/Kconfig +@@ -2,6 +2,7 @@ + config HFSPLUS_FS + tristate "Apple Extended HFS file system support" + depends on BLOCK ++ select CDROM + select NLS + select NLS_UTF8 + help +--- a/fs/isofs/Kconfig ++++ b/fs/isofs/Kconfig +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0-only + config ISO9660_FS + tristate "ISO 9660 CDROM file system support" ++ select CDROM + help + This is the standard file system used on CD-ROMs. It was previously + known as "High Sierra File System" and is called "hsfs" on other +--- a/fs/udf/Kconfig ++++ b/fs/udf/Kconfig +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0-only + config UDF_FS + tristate "UDF file system support" ++ select CDROM + select CRC_ITU_T + select NLS + help diff --git a/target/linux/generic/pending-6.6/510-block-add-uImage.FIT-subimage-block-driver.patch b/target/linux/generic/pending-6.6/510-block-add-uImage.FIT-subimage-block-driver.patch new file mode 100644 index 0000000000..54234caf94 --- /dev/null +++ b/target/linux/generic/pending-6.6/510-block-add-uImage.FIT-subimage-block-driver.patch @@ -0,0 +1,733 @@ +From 6173a065cb395d4a9528c4e49810af127db68141 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Wed, 16 Nov 2022 12:49:52 +0000 +Subject: [PATCH 1/2] block: add uImage.FIT subimage block driver + +Add a small block driver which exposes filesystem sub-images contained +in U-Boot uImage.FIT images as block devices. + +The uImage.FIT image has to be stored directly on a block device or +partition, MTD device or partition, or UBI volume. + +The driver is intended for systems using the U-Boot bootloader and +uses the root device hint left by the bootloader (or the user) in +the 'chosen' section of the device-tree. + +Example: +/dts-v1/; +/ { + chosen { + rootdisk = <&mmc0_part3>; + }; +}; + +Signed-off-by: Daniel Golle +--- + MAINTAINERS | 6 + + drivers/block/Kconfig | 12 + + drivers/block/Makefile | 2 + + drivers/block/fitblk.c | 658 ++++++++++++++++++++++++++++++++++++ + drivers/block/open | 4 + + include/uapi/linux/fitblk.h | 10 + + 6 files changed, 692 insertions(+) + create mode 100644 drivers/block/fitblk.c + create mode 100644 drivers/block/open + create mode 100644 include/uapi/linux/fitblk.h + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -21052,6 +21052,12 @@ F: Documentation/filesystems/ubifs-authe + F: Documentation/filesystems/ubifs.rst + F: fs/ubifs/ + ++U-BOOT UIMAGE.FIT PARSER ++M: Daniel Golle ++L: linux-block@vger.kernel.org ++S: Maintained ++F: drivers/block/fitblk.c ++ + UBLK USERSPACE BLOCK DRIVER + M: Ming Lei + L: linux-block@vger.kernel.org +--- a/drivers/block/Kconfig ++++ b/drivers/block/Kconfig +@@ -383,6 +383,18 @@ config VIRTIO_BLK + This is the virtual block driver for virtio. It can be used with + QEMU based VMMs (like KVM or Xen). Say Y or M. + ++config UIMAGE_FIT_BLK ++ bool "uImage.FIT block driver" ++ help ++ This driver allows using filesystems contained in uImage.FIT images ++ by mapping them as block devices. ++ ++ It can currently not be built as a module due to libfdt symbols not ++ being exported. ++ ++ Say Y if you want to mount filesystems sub-images of a uImage.FIT ++ stored in a block device partition, mtdblock or ubiblock device. ++ + config BLK_DEV_RBD + tristate "Rados block device (RBD)" + depends on INET && BLOCK +--- a/drivers/block/Makefile ++++ b/drivers/block/Makefile +@@ -39,4 +39,6 @@ obj-$(CONFIG_BLK_DEV_NULL_BLK) += null_b + + obj-$(CONFIG_BLK_DEV_UBLK) += ublk_drv.o + ++obj-$(CONFIG_UIMAGE_FIT_BLK) += fitblk.o ++ + swim_mod-y := swim.o swim_asm.o +--- /dev/null ++++ b/drivers/block/fitblk.c +@@ -0,0 +1,636 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * uImage.FIT virtual block device driver. ++ * ++ * Copyright (C) 2023 Daniel Golle ++ * Copyright (C) 2007 Nick Piggin ++ * Copyright (C) 2007 Novell Inc. ++ * ++ * Initially derived from drivers/block/brd.c which is in parts derived from ++ * drivers/block/rd.c, and drivers/block/loop.c, copyright of their respective ++ * owners. ++ * ++ * uImage.FIT headers extracted from Das U-Boot ++ * (C) Copyright 2008 Semihalf ++ * (C) Copyright 2000-2005 ++ * Wolfgang Denk, DENX Software Engineering, wd@denx.de. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define FIT_DEVICE_PREFIX "fit" ++ ++/* maximum number of pages used for the uImage.FIT index structure */ ++#define FIT_MAX_PAGES 1024 ++ ++/* minimum free sectors to map as read-write "remainder" volume */ ++#define MIN_FREE_SECT 16 ++ ++/* maximum number of mapped loadables */ ++#define MAX_FIT_LOADABLES 16 ++ ++/* constants for uImage.FIT structrure traversal */ ++#define FIT_IMAGES_PATH "/images" ++#define FIT_CONFS_PATH "/configurations" ++ ++/* hash/signature/key node */ ++#define FIT_HASH_NODENAME "hash" ++#define FIT_ALGO_PROP "algo" ++#define FIT_VALUE_PROP "value" ++#define FIT_IGNORE_PROP "uboot-ignore" ++#define FIT_SIG_NODENAME "signature" ++#define FIT_KEY_REQUIRED "required" ++#define FIT_KEY_HINT "key-name-hint" ++ ++/* cipher node */ ++#define FIT_CIPHER_NODENAME "cipher" ++#define FIT_ALGO_PROP "algo" ++ ++/* image node */ ++#define FIT_DATA_PROP "data" ++#define FIT_DATA_POSITION_PROP "data-position" ++#define FIT_DATA_OFFSET_PROP "data-offset" ++#define FIT_DATA_SIZE_PROP "data-size" ++#define FIT_TIMESTAMP_PROP "timestamp" ++#define FIT_DESC_PROP "description" ++#define FIT_ARCH_PROP "arch" ++#define FIT_TYPE_PROP "type" ++#define FIT_OS_PROP "os" ++#define FIT_COMP_PROP "compression" ++#define FIT_ENTRY_PROP "entry" ++#define FIT_LOAD_PROP "load" ++ ++/* configuration node */ ++#define FIT_KERNEL_PROP "kernel" ++#define FIT_FILESYSTEM_PROP "filesystem" ++#define FIT_RAMDISK_PROP "ramdisk" ++#define FIT_FDT_PROP "fdt" ++#define FIT_LOADABLE_PROP "loadables" ++#define FIT_DEFAULT_PROP "default" ++#define FIT_SETUP_PROP "setup" ++#define FIT_FPGA_PROP "fpga" ++#define FIT_FIRMWARE_PROP "firmware" ++#define FIT_STANDALONE_PROP "standalone" ++ ++/* fitblk driver data */ ++static const char *_fitblk_claim_ptr = "I belong to fitblk"; ++static const char *ubootver; ++struct device_node *rootdisk; ++static struct platform_device *pdev; ++static LIST_HEAD(fitblk_devices); ++static DEFINE_MUTEX(devices_mutex); ++refcount_t num_devs; ++ ++struct fitblk { ++ struct platform_device *pdev; ++ struct block_device *lower_bdev; ++ sector_t start_sect; ++ struct gendisk *disk; ++ struct work_struct remove_work; ++ struct list_head list; ++ bool dead; ++}; ++ ++static int fitblk_open(struct block_device *bdev, fmode_t mode) ++{ ++ struct fitblk *fitblk = bdev->bd_disk->private_data; ++ ++ if (fitblk->dead) ++ return -ENOENT; ++ ++ return 0; ++} ++ ++static void fitblk_release(struct gendisk *disk, fmode_t mode) ++{ ++ return; ++} ++ ++static void fitblk_submit_bio(struct bio *orig_bio) ++{ ++ struct bio *bio = orig_bio; ++ struct fitblk *fitblk = bio->bi_bdev->bd_disk->private_data; ++ ++ if (fitblk->dead) ++ return; ++ ++ /* mangle bio and re-submit */ ++ while (bio) { ++ bio->bi_iter.bi_sector += fitblk->start_sect; ++ bio->bi_bdev = fitblk->lower_bdev; ++ bio = bio->bi_next; ++ } ++ submit_bio(orig_bio); ++} ++ ++static void fitblk_remove(struct fitblk *fitblk) ++{ ++ blk_mark_disk_dead(fitblk->disk); ++ mutex_lock(&devices_mutex); ++ fitblk->dead = true; ++ list_del(&fitblk->list); ++ mutex_unlock(&devices_mutex); ++ ++ schedule_work(&fitblk->remove_work); ++} ++ ++static int fitblk_ioctl(struct block_device *bdev, fmode_t mode, ++ unsigned int cmd, unsigned long arg) ++{ ++ struct fitblk *fitblk = bdev->bd_disk->private_data; ++ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EACCES; ++ ++ if (fitblk->dead) ++ return -ENOENT; ++ ++ switch (cmd) { ++ case FITBLK_RELEASE: ++ fitblk_remove(fitblk); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static const struct block_device_operations fitblk_fops = { ++ .owner = THIS_MODULE, ++ .ioctl = fitblk_ioctl, ++ .open = fitblk_open, ++ .release = fitblk_release, ++ .submit_bio = fitblk_submit_bio, ++}; ++ ++static void fitblk_purge(struct work_struct *work) ++{ ++ struct fitblk *fitblk = container_of(work, struct fitblk, remove_work); ++ ++ //del_gendisk(fitblk->disk); // causes crash, not doing it doesn't matter ++ refcount_dec(&num_devs); ++ platform_device_del(fitblk->pdev); ++ platform_device_put(fitblk->pdev); ++ ++ if (refcount_dec_if_one(&num_devs)) { ++ sysfs_remove_link(&pdev->dev.kobj, "lower_dev"); ++ blkdev_put(fitblk->lower_bdev, FMODE_READ | FMODE_EXCL); ++ } ++ ++ kfree(fitblk); ++} ++ ++static int add_fit_subimage_device(struct block_device *lower_bdev, ++ unsigned int slot, sector_t start_sect, ++ sector_t nr_sect, bool readonly) ++{ ++ struct fitblk *fitblk; ++ struct gendisk *disk; ++ int err; ++ ++ mutex_lock(&devices_mutex); ++ if (!refcount_inc_not_zero(&num_devs)) ++ return -EBADF; ++ ++ fitblk = kzalloc(sizeof(struct fitblk), GFP_KERNEL); ++ if (!fitblk) { ++ err = -ENOMEM; ++ goto out_unlock; ++ } ++ ++ fitblk->lower_bdev = lower_bdev; ++ fitblk->start_sect = start_sect; ++ INIT_WORK(&fitblk->remove_work, fitblk_purge); ++ ++ disk = blk_alloc_disk(NUMA_NO_NODE); ++ if (!disk) { ++ err = -ENOMEM; ++ goto out_free_fitblk; ++ } ++ ++ disk->first_minor = 0; ++ disk->flags = lower_bdev->bd_disk->flags | GENHD_FL_NO_PART; ++ disk->fops = &fitblk_fops; ++ disk->private_data = fitblk; ++ if (readonly) { ++ set_disk_ro(disk, 1); ++ snprintf(disk->disk_name, sizeof(disk->disk_name), FIT_DEVICE_PREFIX "%u", slot); ++ } else { ++ strcpy(disk->disk_name, FIT_DEVICE_PREFIX "rw"); ++ } ++ ++ set_capacity(disk, nr_sect); ++ ++ disk->queue->queue_flags = lower_bdev->bd_disk->queue->queue_flags; ++ memcpy(&disk->queue->limits, &lower_bdev->bd_disk->queue->limits, ++ sizeof(struct queue_limits)); ++ ++ fitblk->disk = disk; ++ fitblk->pdev = platform_device_alloc(disk->disk_name, PLATFORM_DEVID_NONE); ++ if (!fitblk->pdev) { ++ err = -ENOMEM; ++ goto out_cleanup_disk; ++ } ++ ++ fitblk->pdev->dev.parent = &pdev->dev; ++ err = platform_device_add(fitblk->pdev); ++ if (err) ++ goto out_put_pdev; ++ ++ err = device_add_disk(&fitblk->pdev->dev, disk, NULL); ++ if (err) ++ goto out_del_pdev; ++ ++ if (!ROOT_DEV) ++ ROOT_DEV = disk->part0->bd_dev; ++ ++ list_add_tail(&fitblk->list, &fitblk_devices); ++ ++ mutex_unlock(&devices_mutex); ++ ++ return 0; ++ ++out_del_pdev: ++ platform_device_del(fitblk->pdev); ++out_put_pdev: ++ platform_device_put(fitblk->pdev); ++out_cleanup_disk: ++ put_disk(disk); ++out_free_fitblk: ++ kfree(fitblk); ++out_unlock: ++ refcount_dec(&num_devs); ++ mutex_unlock(&devices_mutex); ++ return err; ++} ++ ++static int parse_fit_on_dev(struct device *dev) ++{ ++ struct block_device *bdev; ++ struct address_space *mapping; ++ struct folio *folio; ++ pgoff_t f_index = 0; ++ size_t bytes_left, bytes_to_copy; ++ void *pre_fit, *fit, *fit_c; ++ u64 dsize, dsectors, imgmaxsect = 0; ++ u32 size, image_pos, image_len; ++ const __be32 *image_offset_be, *image_len_be, *image_pos_be; ++ int ret = 0, node, images, config; ++ const char *image_name, *image_type, *image_description, ++ *config_default, *config_description, *config_loadables; ++ u32 image_name_len, image_type_len, image_description_len, ++ bootconf_len, config_default_len, config_description_len, ++ config_loadables_len; ++ sector_t start_sect, nr_sects; ++ struct device_node *np = NULL; ++ const char *bootconf_c; ++ const char *loadable; ++ char *bootconf = NULL, *bootconf_term; ++ bool found; ++ int loadables_rem_len, loadable_len; ++ u16 loadcnt; ++ unsigned int slot = 0; ++ ++ /* Exclusive open the block device to receive holder notifications */ ++ bdev = blkdev_get_by_dev(dev->devt, FMODE_READ | FMODE_EXCL, &_fitblk_claim_ptr); ++ if (!bdev) ++ return -ENODEV; ++ ++ if (IS_ERR(bdev)) ++ return PTR_ERR(bdev); ++ ++ mapping = bdev->bd_inode->i_mapping; ++ ++ /* map first page */ ++ folio = read_mapping_folio(mapping, f_index++, NULL); ++ if (IS_ERR(folio)) { ++ ret = PTR_ERR(folio); ++ goto out_blkdev; ++ } ++ pre_fit = folio_address(folio) + offset_in_folio(folio, 0); ++ ++ /* uImage.FIT is based on flattened device tree structure */ ++ if (fdt_check_header(pre_fit)) { ++ ret = -EINVAL; ++ folio_put(folio); ++ goto out_blkdev; ++ } ++ ++ size = fdt_totalsize(pre_fit); ++ ++ if (size > PAGE_SIZE * FIT_MAX_PAGES) { ++ ret = -EOPNOTSUPP; ++ folio_put(folio); ++ goto out_blkdev; ++ } ++ ++ /* acquire disk size */ ++ dsectors = bdev_nr_sectors(bdev); ++ dsize = dsectors << SECTOR_SHIFT; ++ ++ /* abort if FIT structure is larger than disk or partition size */ ++ if (size >= dsize) { ++ ret = -EFBIG; ++ folio_put(folio); ++ goto out_blkdev; ++ } ++ ++ fit = kmalloc(size, GFP_KERNEL); ++ if (!fit) { ++ ret = -ENOMEM; ++ folio_put(folio); ++ goto out_blkdev; ++ } ++ ++ bytes_left = size; ++ fit_c = fit; ++ while (bytes_left > 0) { ++ bytes_to_copy = min_t(size_t, bytes_left, ++ folio_size(folio) - offset_in_folio(folio, 0)); ++ memcpy(fit_c, pre_fit, bytes_to_copy); ++ fit_c += bytes_to_copy; ++ bytes_left -= bytes_to_copy; ++ if (bytes_left) { ++ folio_put(folio); ++ folio = read_mapping_folio(mapping, f_index++, NULL); ++ if (IS_ERR(folio)) { ++ ret = PTR_ERR(folio); ++ goto out_blkdev; ++ }; ++ pre_fit = folio_address(folio) + offset_in_folio(folio, 0); ++ } ++ } ++ folio_put(folio); ++ ++ /* set boot config node name U-Boot may have added to the device tree */ ++ np = of_find_node_by_path("/chosen"); ++ if (np) { ++ bootconf_c = of_get_property(np, "u-boot,bootconf", &bootconf_len); ++ if (bootconf_c && bootconf_len) ++ bootconf = kmemdup_nul(bootconf_c, bootconf_len, GFP_KERNEL); ++ } ++ ++ if (bootconf) { ++ bootconf_term = strchr(bootconf, '#'); ++ if (bootconf_term) ++ *bootconf_term = '\0'; ++ } ++ ++ /* find configuration path in uImage.FIT */ ++ config = fdt_path_offset(fit, FIT_CONFS_PATH); ++ if (config < 0) { ++ pr_err("FIT: Cannot find %s node: %d\n", ++ FIT_CONFS_PATH, config); ++ ret = -ENOENT; ++ goto out_bootconf; ++ } ++ ++ /* get default configuration node name */ ++ config_default = ++ fdt_getprop(fit, config, FIT_DEFAULT_PROP, &config_default_len); ++ ++ /* make sure we got either default or selected boot config node name */ ++ if (!config_default && !bootconf) { ++ pr_err("FIT: Cannot find default configuration\n"); ++ ret = -ENOENT; ++ goto out_bootconf; ++ } ++ ++ /* find selected boot config node, fallback on default config node */ ++ node = fdt_subnode_offset(fit, config, bootconf ?: config_default); ++ if (node < 0) { ++ pr_err("FIT: Cannot find %s node: %d\n", ++ bootconf ?: config_default, node); ++ ret = -ENOENT; ++ goto out_bootconf; ++ } ++ ++ pr_info("FIT: Detected U-Boot %s\n", ubootver); ++ ++ /* get selected configuration data */ ++ config_description = ++ fdt_getprop(fit, node, FIT_DESC_PROP, &config_description_len); ++ config_loadables = fdt_getprop(fit, node, FIT_LOADABLE_PROP, ++ &config_loadables_len); ++ ++ pr_info("FIT: %s configuration: \"%.*s\"%s%.*s%s\n", ++ bootconf ? "Selected" : "Default", ++ bootconf ? bootconf_len : config_default_len, ++ bootconf ?: config_default, ++ config_description ? " (" : "", ++ config_description ? config_description_len : 0, ++ config_description ?: "", ++ config_description ? ")" : ""); ++ ++ if (!config_loadables || !config_loadables_len) { ++ pr_err("FIT: No loadables configured in \"%s\"\n", ++ bootconf ?: config_default); ++ ret = -ENOENT; ++ goto out_bootconf; ++ } ++ ++ /* get images path in uImage.FIT */ ++ images = fdt_path_offset(fit, FIT_IMAGES_PATH); ++ if (images < 0) { ++ pr_err("FIT: Cannot find %s node: %d\n", FIT_IMAGES_PATH, images); ++ ret = -EINVAL; ++ goto out_bootconf; ++ } ++ ++ /* iterate over images in uImage.FIT */ ++ fdt_for_each_subnode(node, fit, images) { ++ image_name = fdt_get_name(fit, node, &image_name_len); ++ image_type = fdt_getprop(fit, node, FIT_TYPE_PROP, &image_type_len); ++ image_offset_be = fdt_getprop(fit, node, FIT_DATA_OFFSET_PROP, NULL); ++ image_pos_be = fdt_getprop(fit, node, FIT_DATA_POSITION_PROP, NULL); ++ image_len_be = fdt_getprop(fit, node, FIT_DATA_SIZE_PROP, NULL); ++ ++ if (!image_name || !image_type || !image_len_be || ++ !image_name_len || !image_type_len) ++ continue; ++ ++ image_len = be32_to_cpu(*image_len_be); ++ if (!image_len) ++ continue; ++ ++ if (image_offset_be) ++ image_pos = be32_to_cpu(*image_offset_be) + size; ++ else if (image_pos_be) ++ image_pos = be32_to_cpu(*image_pos_be); ++ else ++ continue; ++ ++ image_description = fdt_getprop(fit, node, FIT_DESC_PROP, ++ &image_description_len); ++ ++ pr_info("FIT: %16s sub-image 0x%08x..0x%08x \"%.*s\"%s%.*s%s\n", ++ image_type, image_pos, image_pos + image_len - 1, ++ image_name_len, image_name, image_description ? " (" : "", ++ image_description ? image_description_len : 0, ++ image_description ?: "", image_description ? ") " : ""); ++ ++ /* only 'filesystem' images should be mapped as partitions */ ++ if (strncmp(image_type, FIT_FILESYSTEM_PROP, image_type_len)) ++ continue; ++ ++ /* check if sub-image is part of configured loadables */ ++ found = false; ++ loadable = config_loadables; ++ loadables_rem_len = config_loadables_len; ++ for (loadcnt = 0; loadables_rem_len > 1 && ++ loadcnt < MAX_FIT_LOADABLES; ++loadcnt) { ++ loadable_len = ++ strnlen(loadable, loadables_rem_len - 1) + 1; ++ loadables_rem_len -= loadable_len; ++ if (!strncmp(image_name, loadable, loadable_len)) { ++ found = true; ++ break; ++ } ++ loadable += loadable_len; ++ } ++ if (!found) ++ continue; ++ ++ if (image_pos % (1 << PAGE_SHIFT)) { ++ dev_err(dev, "FIT: image %.*s start not aligned to page boundaries, skipping\n", ++ image_name_len, image_name); ++ continue; ++ } ++ ++ if (image_len % (1 << PAGE_SHIFT)) { ++ dev_err(dev, "FIT: sub-image %.*s end not aligned to page boundaries, skipping\n", ++ image_name_len, image_name); ++ continue; ++ } ++ ++ start_sect = image_pos >> SECTOR_SHIFT; ++ nr_sects = image_len >> SECTOR_SHIFT; ++ imgmaxsect = max_t(sector_t, imgmaxsect, start_sect + nr_sects); ++ ++ if (start_sect + nr_sects > dsectors) { ++ dev_err(dev, "FIT: sub-image %.*s disk access beyond EOD\n", ++ image_name_len, image_name); ++ continue; ++ } ++ ++ if (!slot) { ++ ret = sysfs_create_link_nowarn(&pdev->dev.kobj, bdev_kobj(bdev), "lower_dev"); ++ if (ret && ret != -EEXIST) ++ goto out_bootconf; ++ ++ ret = 0; ++ } ++ ++ add_fit_subimage_device(bdev, slot++, start_sect, nr_sects, true); ++ } ++ ++ if (!found || !slot) ++ goto out_bootconf; ++ ++ dev_info(dev, "mapped %u uImage.FIT filesystem sub-image%s as /dev/fit%s%u%s\n", ++ slot, (slot > 1)?"s":"", (slot > 1)?"[0...":"", slot - 1, ++ (slot > 1)?"]":""); ++ ++ /* in case uImage.FIT is stored in a partition, map the remaining space */ ++ if (!bdev->bd_read_only && bdev_is_partition(bdev) && ++ (imgmaxsect + MIN_FREE_SECT) < dsectors) { ++ add_fit_subimage_device(bdev, slot++, imgmaxsect, ++ dsectors - imgmaxsect, false); ++ dev_info(dev, "mapped remaing space as /dev/fitrw\n"); ++ } ++ ++out_bootconf: ++ kfree(bootconf); ++ kfree(fit); ++out_blkdev: ++ if (!found || ret) ++ blkdev_put(bdev, FMODE_READ | FMODE_EXCL); ++ ++ return ret; ++} ++ ++static int fitblk_match_of_node(struct device *dev, const void *np) ++{ ++ int ret; ++ ++ ret = device_match_of_node(dev, np); ++ if (ret) ++ return ret; ++ ++ /* ++ * To match ubiblock and mtdblock devices by their parent ubi ++ * or mtd device, also consider block device parent ++ */ ++ if (!dev->parent) ++ return 0; ++ ++ return device_match_of_node(dev->parent, np); ++} ++ ++static int fitblk_probe(struct platform_device *pdev) ++{ ++ struct device *dev; ++ ++ dev = class_find_device(&block_class, NULL, rootdisk, fitblk_match_of_node); ++ if (!dev) ++ return -EPROBE_DEFER; ++ ++ return parse_fit_on_dev(dev); ++} ++ ++static struct platform_driver fitblk_driver = { ++ .probe = fitblk_probe, ++ .driver = { ++ .name = "fitblk", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init fitblk_init(void) ++{ ++ /* detect U-Boot firmware */ ++ ubootver = of_get_property(of_chosen, "u-boot,version", NULL); ++ if (!ubootver) ++ return 0; ++ ++ /* parse 'rootdisk' property phandle */ ++ rootdisk = of_parse_phandle(of_chosen, "rootdisk", 0); ++ if (!rootdisk) ++ return 0; ++ ++ if (platform_driver_register(&fitblk_driver)) ++ return -ENODEV; ++ ++ refcount_set(&num_devs, 1); ++ pdev = platform_device_register_simple("fitblk", -1, NULL, 0); ++ if (IS_ERR(pdev)) ++ return PTR_ERR(pdev); ++ ++ return 0; ++} ++device_initcall(fitblk_init); +--- /dev/null ++++ b/include/uapi/linux/fitblk.h +@@ -0,0 +1,10 @@ ++/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ ++#ifndef _UAPI_LINUX_FITBLK_H ++#define _UAPI_LINUX_FITBLK_H ++ ++/* ++ * IOCTL commands --- we will commandeer 0x46 ('F') ++ */ ++#define FITBLK_RELEASE 0x4600 ++ ++#endif /* _UAPI_LINUX_FITBLK_H */ diff --git a/target/linux/generic/pending-6.6/511-init-bypass-device-lookup-for-dev-fit-rootfs.patch b/target/linux/generic/pending-6.6/511-init-bypass-device-lookup-for-dev-fit-rootfs.patch new file mode 100644 index 0000000000..685a5f9da5 --- /dev/null +++ b/target/linux/generic/pending-6.6/511-init-bypass-device-lookup-for-dev-fit-rootfs.patch @@ -0,0 +1,25 @@ +From 5ede3f8aed9a1a579bf7304142600d1f3500add9 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 12 Jun 2023 03:58:42 +0100 +Subject: [PATCH 2/2] init: bypass device lookup for /dev/fit* rootfs + +Allow 'rootwait' as /dev/fit* can show up late if the underlaying +device is probed late. + +Signed-off-by: Daniel Golle +--- + init/do_mounts.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/init/do_mounts.c ++++ b/init/do_mounts.c +@@ -645,7 +645,8 @@ void __init prepare_namespace(void) + + if (saved_root_name[0]) { + root_device_name = saved_root_name; +- if (!strncmp(root_device_name, "mtd", 3) || ++ if (!strncmp(root_device_name, "fit", 3) || ++ !strncmp(root_device_name, "mtd", 3) || + !strncmp(root_device_name, "ubi", 3)) { + mount_block_root(root_device_name, root_mountflags); + goto out; diff --git a/target/linux/generic/pending-6.6/530-jffs2_make_lzma_available.patch b/target/linux/generic/pending-6.6/530-jffs2_make_lzma_available.patch new file mode 100644 index 0000000000..27a673399a --- /dev/null +++ b/target/linux/generic/pending-6.6/530-jffs2_make_lzma_available.patch @@ -0,0 +1,5180 @@ +From: Alexandros C. Couloumbis +Subject: fs: add jffs2/lzma support (not activated by default yet) + +lede-commit: c2c88d315fa0e881f8b19da07b62859b915b11b2 +Signed-off-by: Alexandros C. Couloumbis +--- + fs/jffs2/Kconfig | 9 + + fs/jffs2/Makefile | 3 + + fs/jffs2/compr.c | 6 + + fs/jffs2/compr.h | 10 +- + fs/jffs2/compr_lzma.c | 128 +++ + fs/jffs2/super.c | 33 +- + include/linux/lzma.h | 62 ++ + include/linux/lzma/LzFind.h | 115 +++ + include/linux/lzma/LzHash.h | 54 + + include/linux/lzma/LzmaDec.h | 231 +++++ + include/linux/lzma/LzmaEnc.h | 80 ++ + include/linux/lzma/Types.h | 226 +++++ + include/uapi/linux/jffs2.h | 1 + + lib/Kconfig | 6 + + lib/Makefile | 12 + + lib/lzma/LzFind.c | 761 ++++++++++++++ + lib/lzma/LzmaDec.c | 999 +++++++++++++++++++ + lib/lzma/LzmaEnc.c | 2271 ++++++++++++++++++++++++++++++++++++++++++ + lib/lzma/Makefile | 7 + + 19 files changed, 5008 insertions(+), 6 deletions(-) + create mode 100644 fs/jffs2/compr_lzma.c + create mode 100644 include/linux/lzma.h + create mode 100644 include/linux/lzma/LzFind.h + create mode 100644 include/linux/lzma/LzHash.h + create mode 100644 include/linux/lzma/LzmaDec.h + create mode 100644 include/linux/lzma/LzmaEnc.h + create mode 100644 include/linux/lzma/Types.h + create mode 100644 lib/lzma/LzFind.c + create mode 100644 lib/lzma/LzmaDec.c + create mode 100644 lib/lzma/LzmaEnc.c + create mode 100644 lib/lzma/Makefile + +--- a/fs/jffs2/Kconfig ++++ b/fs/jffs2/Kconfig +@@ -136,6 +136,15 @@ config JFFS2_LZO + This feature was added in July, 2007. Say 'N' if you need + compatibility with older bootloaders or kernels. + ++config JFFS2_LZMA ++ bool "JFFS2 LZMA compression support" if JFFS2_COMPRESSION_OPTIONS ++ select LZMA_COMPRESS ++ select LZMA_DECOMPRESS ++ depends on JFFS2_FS ++ default n ++ help ++ JFFS2 wrapper to the LZMA C SDK ++ + config JFFS2_RTIME + bool "JFFS2 RTIME compression support" if JFFS2_COMPRESSION_OPTIONS + depends on JFFS2_FS +--- a/fs/jffs2/Makefile ++++ b/fs/jffs2/Makefile +@@ -19,4 +19,7 @@ jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rub + jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o + jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o + jffs2-$(CONFIG_JFFS2_LZO) += compr_lzo.o ++jffs2-$(CONFIG_JFFS2_LZMA) += compr_lzma.o + jffs2-$(CONFIG_JFFS2_SUMMARY) += summary.o ++ ++CFLAGS_compr_lzma.o += -Iinclude/linux -Ilib/lzma +--- a/fs/jffs2/compr.c ++++ b/fs/jffs2/compr.c +@@ -378,6 +378,9 @@ int __init jffs2_compressors_init(void) + #ifdef CONFIG_JFFS2_LZO + jffs2_lzo_init(); + #endif ++#ifdef CONFIG_JFFS2_LZMA ++ jffs2_lzma_init(); ++#endif + /* Setting default compression mode */ + #ifdef CONFIG_JFFS2_CMODE_NONE + jffs2_compression_mode = JFFS2_COMPR_MODE_NONE; +@@ -401,6 +404,9 @@ int __init jffs2_compressors_init(void) + int jffs2_compressors_exit(void) + { + /* Unregistering compressors */ ++#ifdef CONFIG_JFFS2_LZMA ++ jffs2_lzma_exit(); ++#endif + #ifdef CONFIG_JFFS2_LZO + jffs2_lzo_exit(); + #endif +--- a/fs/jffs2/compr.h ++++ b/fs/jffs2/compr.h +@@ -29,9 +29,9 @@ + #define JFFS2_DYNRUBIN_PRIORITY 20 + #define JFFS2_LZARI_PRIORITY 30 + #define JFFS2_RTIME_PRIORITY 50 +-#define JFFS2_ZLIB_PRIORITY 60 +-#define JFFS2_LZO_PRIORITY 80 +- ++#define JFFS2_LZMA_PRIORITY 70 ++#define JFFS2_ZLIB_PRIORITY 80 ++#define JFFS2_LZO_PRIORITY 90 + + #define JFFS2_RUBINMIPS_DISABLED /* RUBINs will be used only */ + #define JFFS2_DYNRUBIN_DISABLED /* for decompression */ +@@ -101,5 +101,9 @@ void jffs2_zlib_exit(void); + int jffs2_lzo_init(void); + void jffs2_lzo_exit(void); + #endif ++#ifdef CONFIG_JFFS2_LZMA ++int jffs2_lzma_init(void); ++void jffs2_lzma_exit(void); ++#endif + + #endif /* __JFFS2_COMPR_H__ */ +--- /dev/null ++++ b/fs/jffs2/compr_lzma.c +@@ -0,0 +1,128 @@ ++/* ++ * JFFS2 -- Journalling Flash File System, Version 2. ++ * ++ * For licensing information, see the file 'LICENCE' in this directory. ++ * ++ * JFFS2 wrapper to the LZMA C SDK ++ * ++ */ ++ ++#include ++#include "compr.h" ++ ++#ifdef __KERNEL__ ++ static DEFINE_MUTEX(deflate_mutex); ++#endif ++ ++CLzmaEncHandle *p; ++Byte propsEncoded[LZMA_PROPS_SIZE]; ++SizeT propsSize = sizeof(propsEncoded); ++ ++STATIC void lzma_free_workspace(void) ++{ ++ LzmaEnc_Destroy(p, &lzma_alloc, &lzma_alloc); ++} ++ ++STATIC int INIT lzma_alloc_workspace(CLzmaEncProps *props) ++{ ++ if ((p = (CLzmaEncHandle *)LzmaEnc_Create(&lzma_alloc)) == NULL) ++ { ++ PRINT_ERROR("Failed to allocate lzma deflate workspace\n"); ++ return -ENOMEM; ++ } ++ ++ if (LzmaEnc_SetProps(p, props) != SZ_OK) ++ { ++ lzma_free_workspace(); ++ return -1; ++ } ++ ++ if (LzmaEnc_WriteProperties(p, propsEncoded, &propsSize) != SZ_OK) ++ { ++ lzma_free_workspace(); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++STATIC int jffs2_lzma_compress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t *sourcelen, uint32_t *dstlen) ++{ ++ SizeT compress_size = (SizeT)(*dstlen); ++ int ret; ++ ++ #ifdef __KERNEL__ ++ mutex_lock(&deflate_mutex); ++ #endif ++ ++ ret = LzmaEnc_MemEncode(p, cpage_out, &compress_size, data_in, *sourcelen, ++ 0, NULL, &lzma_alloc, &lzma_alloc); ++ ++ #ifdef __KERNEL__ ++ mutex_unlock(&deflate_mutex); ++ #endif ++ ++ if (ret != SZ_OK) ++ return -1; ++ ++ *dstlen = (uint32_t)compress_size; ++ ++ return 0; ++} ++ ++STATIC int jffs2_lzma_decompress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t srclen, uint32_t destlen) ++{ ++ int ret; ++ SizeT dl = (SizeT)destlen; ++ SizeT sl = (SizeT)srclen; ++ ELzmaStatus status; ++ ++ ret = LzmaDecode(cpage_out, &dl, data_in, &sl, propsEncoded, ++ propsSize, LZMA_FINISH_ANY, &status, &lzma_alloc); ++ ++ if (ret != SZ_OK || status == LZMA_STATUS_NOT_FINISHED || dl != (SizeT)destlen) ++ return -1; ++ ++ return 0; ++} ++ ++static struct jffs2_compressor jffs2_lzma_comp = { ++ .priority = JFFS2_LZMA_PRIORITY, ++ .name = "lzma", ++ .compr = JFFS2_COMPR_LZMA, ++ .compress = &jffs2_lzma_compress, ++ .decompress = &jffs2_lzma_decompress, ++ .disabled = 0, ++}; ++ ++int INIT jffs2_lzma_init(void) ++{ ++ int ret; ++ CLzmaEncProps props; ++ LzmaEncProps_Init(&props); ++ ++ props.dictSize = LZMA_BEST_DICT(0x2000); ++ props.level = LZMA_BEST_LEVEL; ++ props.lc = LZMA_BEST_LC; ++ props.lp = LZMA_BEST_LP; ++ props.pb = LZMA_BEST_PB; ++ props.fb = LZMA_BEST_FB; ++ ++ ret = lzma_alloc_workspace(&props); ++ if (ret < 0) ++ return ret; ++ ++ ret = jffs2_register_compressor(&jffs2_lzma_comp); ++ if (ret) ++ lzma_free_workspace(); ++ ++ return ret; ++} ++ ++void jffs2_lzma_exit(void) ++{ ++ jffs2_unregister_compressor(&jffs2_lzma_comp); ++ lzma_free_workspace(); ++} +--- a/fs/jffs2/super.c ++++ b/fs/jffs2/super.c +@@ -374,14 +374,41 @@ static int __init init_jffs2_fs(void) + BUILD_BUG_ON(sizeof(struct jffs2_raw_inode) != 68); + BUILD_BUG_ON(sizeof(struct jffs2_raw_summary) != 32); + +- pr_info("version 2.2." ++ pr_info("version 2.2" + #ifdef CONFIG_JFFS2_FS_WRITEBUFFER + " (NAND)" + #endif + #ifdef CONFIG_JFFS2_SUMMARY +- " (SUMMARY) " ++ " (SUMMARY)" + #endif +- " © 2001-2006 Red Hat, Inc.\n"); ++#ifdef CONFIG_JFFS2_ZLIB ++ " (ZLIB)" ++#endif ++#ifdef CONFIG_JFFS2_LZO ++ " (LZO)" ++#endif ++#ifdef CONFIG_JFFS2_LZMA ++ " (LZMA)" ++#endif ++#ifdef CONFIG_JFFS2_RTIME ++ " (RTIME)" ++#endif ++#ifdef CONFIG_JFFS2_RUBIN ++ " (RUBIN)" ++#endif ++#ifdef CONFIG_JFFS2_CMODE_NONE ++ " (CMODE_NONE)" ++#endif ++#ifdef CONFIG_JFFS2_CMODE_PRIORITY ++ " (CMODE_PRIORITY)" ++#endif ++#ifdef CONFIG_JFFS2_CMODE_SIZE ++ " (CMODE_SIZE)" ++#endif ++#ifdef CONFIG_JFFS2_CMODE_FAVOURLZO ++ " (CMODE_FAVOURLZO)" ++#endif ++ " (c) 2001-2006 Red Hat, Inc.\n"); + + jffs2_inode_cachep = kmem_cache_create("jffs2_i", + sizeof(struct jffs2_inode_info), +--- /dev/null ++++ b/include/linux/lzma.h +@@ -0,0 +1,62 @@ ++#ifndef __LZMA_H__ ++#define __LZMA_H__ ++ ++#ifdef __KERNEL__ ++ #include ++ #include ++ #include ++ #include ++ #include ++ #define LZMA_MALLOC vmalloc ++ #define LZMA_FREE vfree ++ #define PRINT_ERROR(msg) printk(KERN_WARNING #msg) ++ #define INIT __init ++ #define STATIC static ++#else ++ #include ++ #include ++ #include ++ #include ++ #include ++ #include ++ #include ++ #include ++ #ifndef PAGE_SIZE ++ extern int page_size; ++ #define PAGE_SIZE page_size ++ #endif ++ #define LZMA_MALLOC malloc ++ #define LZMA_FREE free ++ #define PRINT_ERROR(msg) fprintf(stderr, msg) ++ #define INIT ++ #define STATIC ++#endif ++ ++#include "lzma/LzmaDec.h" ++#include "lzma/LzmaEnc.h" ++ ++#define LZMA_BEST_LEVEL (9) ++#define LZMA_BEST_LC (0) ++#define LZMA_BEST_LP (0) ++#define LZMA_BEST_PB (0) ++#define LZMA_BEST_FB (273) ++ ++#define LZMA_BEST_DICT(n) (((int)((n) / 2)) * 2) ++ ++static void *p_lzma_malloc(void *p, size_t size) ++{ ++ if (size == 0) ++ return NULL; ++ ++ return LZMA_MALLOC(size); ++} ++ ++static void p_lzma_free(void *p, void *address) ++{ ++ if (address != NULL) ++ LZMA_FREE(address); ++} ++ ++static ISzAlloc lzma_alloc = { .Alloc = p_lzma_malloc, .Free = p_lzma_free }; ++ ++#endif +--- /dev/null ++++ b/include/linux/lzma/LzFind.h +@@ -0,0 +1,115 @@ ++/* LzFind.h -- Match finder for LZ algorithms ++2009-04-22 : Igor Pavlov : Public domain */ ++ ++#ifndef __LZ_FIND_H ++#define __LZ_FIND_H ++ ++#include "Types.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++typedef UInt32 CLzRef; ++ ++typedef struct _CMatchFinder ++{ ++ Byte *buffer; ++ UInt32 pos; ++ UInt32 posLimit; ++ UInt32 streamPos; ++ UInt32 lenLimit; ++ ++ UInt32 cyclicBufferPos; ++ UInt32 cyclicBufferSize; /* it must be = (historySize + 1) */ ++ ++ UInt32 matchMaxLen; ++ CLzRef *hash; ++ CLzRef *son; ++ UInt32 hashMask; ++ UInt32 cutValue; ++ ++ Byte *bufferBase; ++ ISeqInStream *stream; ++ int streamEndWasReached; ++ ++ UInt32 blockSize; ++ UInt32 keepSizeBefore; ++ UInt32 keepSizeAfter; ++ ++ UInt32 numHashBytes; ++ int directInput; ++ size_t directInputRem; ++ int btMode; ++ int bigHash; ++ UInt32 historySize; ++ UInt32 fixedHashSize; ++ UInt32 hashSizeSum; ++ UInt32 numSons; ++ SRes result; ++ UInt32 crc[256]; ++} CMatchFinder; ++ ++#define Inline_MatchFinder_GetPointerToCurrentPos(p) ((p)->buffer) ++#define Inline_MatchFinder_GetIndexByte(p, index) ((p)->buffer[(Int32)(index)]) ++ ++#define Inline_MatchFinder_GetNumAvailableBytes(p) ((p)->streamPos - (p)->pos) ++ ++int MatchFinder_NeedMove(CMatchFinder *p); ++Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p); ++void MatchFinder_MoveBlock(CMatchFinder *p); ++void MatchFinder_ReadIfRequired(CMatchFinder *p); ++ ++void MatchFinder_Construct(CMatchFinder *p); ++ ++/* Conditions: ++ historySize <= 3 GB ++ keepAddBufferBefore + matchMaxLen + keepAddBufferAfter < 511MB ++*/ ++int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, ++ UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ++ ISzAlloc *alloc); ++void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc); ++void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems); ++void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue); ++ ++UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *buffer, CLzRef *son, ++ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue, ++ UInt32 *distances, UInt32 maxLen); ++ ++/* ++Conditions: ++ Mf_GetNumAvailableBytes_Func must be called before each Mf_GetMatchLen_Func. ++ Mf_GetPointerToCurrentPos_Func's result must be used only before any other function ++*/ ++ ++typedef void (*Mf_Init_Func)(void *object); ++typedef Byte (*Mf_GetIndexByte_Func)(void *object, Int32 index); ++typedef UInt32 (*Mf_GetNumAvailableBytes_Func)(void *object); ++typedef const Byte * (*Mf_GetPointerToCurrentPos_Func)(void *object); ++typedef UInt32 (*Mf_GetMatches_Func)(void *object, UInt32 *distances); ++typedef void (*Mf_Skip_Func)(void *object, UInt32); ++ ++typedef struct _IMatchFinder ++{ ++ Mf_Init_Func Init; ++ Mf_GetIndexByte_Func GetIndexByte; ++ Mf_GetNumAvailableBytes_Func GetNumAvailableBytes; ++ Mf_GetPointerToCurrentPos_Func GetPointerToCurrentPos; ++ Mf_GetMatches_Func GetMatches; ++ Mf_Skip_Func Skip; ++} IMatchFinder; ++ ++void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable); ++ ++void MatchFinder_Init(CMatchFinder *p); ++UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); ++UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); ++void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); ++void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +--- /dev/null ++++ b/include/linux/lzma/LzHash.h +@@ -0,0 +1,54 @@ ++/* LzHash.h -- HASH functions for LZ algorithms ++2009-02-07 : Igor Pavlov : Public domain */ ++ ++#ifndef __LZ_HASH_H ++#define __LZ_HASH_H ++ ++#define kHash2Size (1 << 10) ++#define kHash3Size (1 << 16) ++#define kHash4Size (1 << 20) ++ ++#define kFix3HashSize (kHash2Size) ++#define kFix4HashSize (kHash2Size + kHash3Size) ++#define kFix5HashSize (kHash2Size + kHash3Size + kHash4Size) ++ ++#define HASH2_CALC hashValue = cur[0] | ((UInt32)cur[1] << 8); ++ ++#define HASH3_CALC { \ ++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ ++ hash2Value = temp & (kHash2Size - 1); \ ++ hashValue = (temp ^ ((UInt32)cur[2] << 8)) & p->hashMask; } ++ ++#define HASH4_CALC { \ ++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ ++ hash2Value = temp & (kHash2Size - 1); \ ++ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ ++ hashValue = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & p->hashMask; } ++ ++#define HASH5_CALC { \ ++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ ++ hash2Value = temp & (kHash2Size - 1); \ ++ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ ++ hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)); \ ++ hashValue = (hash4Value ^ (p->crc[cur[4]] << 3)) & p->hashMask; \ ++ hash4Value &= (kHash4Size - 1); } ++ ++/* #define HASH_ZIP_CALC hashValue = ((cur[0] | ((UInt32)cur[1] << 8)) ^ p->crc[cur[2]]) & 0xFFFF; */ ++#define HASH_ZIP_CALC hashValue = ((cur[2] | ((UInt32)cur[0] << 8)) ^ p->crc[cur[1]]) & 0xFFFF; ++ ++ ++#define MT_HASH2_CALC \ ++ hash2Value = (p->crc[cur[0]] ^ cur[1]) & (kHash2Size - 1); ++ ++#define MT_HASH3_CALC { \ ++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ ++ hash2Value = temp & (kHash2Size - 1); \ ++ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); } ++ ++#define MT_HASH4_CALC { \ ++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ ++ hash2Value = temp & (kHash2Size - 1); \ ++ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ ++ hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & (kHash4Size - 1); } ++ ++#endif +--- /dev/null ++++ b/include/linux/lzma/LzmaDec.h +@@ -0,0 +1,231 @@ ++/* LzmaDec.h -- LZMA Decoder ++2009-02-07 : Igor Pavlov : Public domain */ ++ ++#ifndef __LZMA_DEC_H ++#define __LZMA_DEC_H ++ ++#include "Types.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/* #define _LZMA_PROB32 */ ++/* _LZMA_PROB32 can increase the speed on some CPUs, ++ but memory usage for CLzmaDec::probs will be doubled in that case */ ++ ++#ifdef _LZMA_PROB32 ++#define CLzmaProb UInt32 ++#else ++#define CLzmaProb UInt16 ++#endif ++ ++ ++/* ---------- LZMA Properties ---------- */ ++ ++#define LZMA_PROPS_SIZE 5 ++ ++typedef struct _CLzmaProps ++{ ++ unsigned lc, lp, pb; ++ UInt32 dicSize; ++} CLzmaProps; ++ ++/* LzmaProps_Decode - decodes properties ++Returns: ++ SZ_OK ++ SZ_ERROR_UNSUPPORTED - Unsupported properties ++*/ ++ ++SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size); ++ ++ ++/* ---------- LZMA Decoder state ---------- */ ++ ++/* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case. ++ Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */ ++ ++#define LZMA_REQUIRED_INPUT_MAX 20 ++ ++typedef struct ++{ ++ CLzmaProps prop; ++ CLzmaProb *probs; ++ Byte *dic; ++ const Byte *buf; ++ UInt32 range, code; ++ SizeT dicPos; ++ SizeT dicBufSize; ++ UInt32 processedPos; ++ UInt32 checkDicSize; ++ unsigned state; ++ UInt32 reps[4]; ++ unsigned remainLen; ++ int needFlush; ++ int needInitState; ++ UInt32 numProbs; ++ unsigned tempBufSize; ++ Byte tempBuf[LZMA_REQUIRED_INPUT_MAX]; ++} CLzmaDec; ++ ++#define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; } ++ ++void LzmaDec_Init(CLzmaDec *p); ++ ++/* There are two types of LZMA streams: ++ 0) Stream with end mark. That end mark adds about 6 bytes to compressed size. ++ 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */ ++ ++typedef enum ++{ ++ LZMA_FINISH_ANY, /* finish at any point */ ++ LZMA_FINISH_END /* block must be finished at the end */ ++} ELzmaFinishMode; ++ ++/* ELzmaFinishMode has meaning only if the decoding reaches output limit !!! ++ ++ You must use LZMA_FINISH_END, when you know that current output buffer ++ covers last bytes of block. In other cases you must use LZMA_FINISH_ANY. ++ ++ If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK, ++ and output value of destLen will be less than output buffer size limit. ++ You can check status result also. ++ ++ You can use multiple checks to test data integrity after full decompression: ++ 1) Check Result and "status" variable. ++ 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize. ++ 3) Check that output(srcLen) = compressedSize, if you know real compressedSize. ++ You must use correct finish mode in that case. */ ++ ++typedef enum ++{ ++ LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */ ++ LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ ++ LZMA_STATUS_NOT_FINISHED, /* stream was not finished */ ++ LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */ ++ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */ ++} ELzmaStatus; ++ ++/* ELzmaStatus is used only as output value for function call */ ++ ++ ++/* ---------- Interfaces ---------- */ ++ ++/* There are 3 levels of interfaces: ++ 1) Dictionary Interface ++ 2) Buffer Interface ++ 3) One Call Interface ++ You can select any of these interfaces, but don't mix functions from different ++ groups for same object. */ ++ ++ ++/* There are two variants to allocate state for Dictionary Interface: ++ 1) LzmaDec_Allocate / LzmaDec_Free ++ 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs ++ You can use variant 2, if you set dictionary buffer manually. ++ For Buffer Interface you must always use variant 1. ++ ++LzmaDec_Allocate* can return: ++ SZ_OK ++ SZ_ERROR_MEM - Memory allocation error ++ SZ_ERROR_UNSUPPORTED - Unsupported properties ++*/ ++ ++SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc); ++void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc); ++ ++SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc); ++void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc); ++ ++/* ---------- Dictionary Interface ---------- */ ++ ++/* You can use it, if you want to eliminate the overhead for data copying from ++ dictionary to some other external buffer. ++ You must work with CLzmaDec variables directly in this interface. ++ ++ STEPS: ++ LzmaDec_Constr() ++ LzmaDec_Allocate() ++ for (each new stream) ++ { ++ LzmaDec_Init() ++ while (it needs more decompression) ++ { ++ LzmaDec_DecodeToDic() ++ use data from CLzmaDec::dic and update CLzmaDec::dicPos ++ } ++ } ++ LzmaDec_Free() ++*/ ++ ++/* LzmaDec_DecodeToDic ++ ++ The decoding to internal dictionary buffer (CLzmaDec::dic). ++ You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!! ++ ++finishMode: ++ It has meaning only if the decoding reaches output limit (dicLimit). ++ LZMA_FINISH_ANY - Decode just dicLimit bytes. ++ LZMA_FINISH_END - Stream must be finished after dicLimit. ++ ++Returns: ++ SZ_OK ++ status: ++ LZMA_STATUS_FINISHED_WITH_MARK ++ LZMA_STATUS_NOT_FINISHED ++ LZMA_STATUS_NEEDS_MORE_INPUT ++ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK ++ SZ_ERROR_DATA - Data error ++*/ ++ ++SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, ++ const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); ++ ++ ++/* ---------- Buffer Interface ---------- */ ++ ++/* It's zlib-like interface. ++ See LzmaDec_DecodeToDic description for information about STEPS and return results, ++ but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need ++ to work with CLzmaDec variables manually. ++ ++finishMode: ++ It has meaning only if the decoding reaches output limit (*destLen). ++ LZMA_FINISH_ANY - Decode just destLen bytes. ++ LZMA_FINISH_END - Stream must be finished after (*destLen). ++*/ ++ ++SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, ++ const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); ++ ++ ++/* ---------- One Call Interface ---------- */ ++ ++/* LzmaDecode ++ ++finishMode: ++ It has meaning only if the decoding reaches output limit (*destLen). ++ LZMA_FINISH_ANY - Decode just destLen bytes. ++ LZMA_FINISH_END - Stream must be finished after (*destLen). ++ ++Returns: ++ SZ_OK ++ status: ++ LZMA_STATUS_FINISHED_WITH_MARK ++ LZMA_STATUS_NOT_FINISHED ++ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK ++ SZ_ERROR_DATA - Data error ++ SZ_ERROR_MEM - Memory allocation error ++ SZ_ERROR_UNSUPPORTED - Unsupported properties ++ SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). ++*/ ++ ++SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ++ const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, ++ ELzmaStatus *status, ISzAlloc *alloc); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +--- /dev/null ++++ b/include/linux/lzma/LzmaEnc.h +@@ -0,0 +1,80 @@ ++/* LzmaEnc.h -- LZMA Encoder ++2009-02-07 : Igor Pavlov : Public domain */ ++ ++#ifndef __LZMA_ENC_H ++#define __LZMA_ENC_H ++ ++#include "Types.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define LZMA_PROPS_SIZE 5 ++ ++typedef struct _CLzmaEncProps ++{ ++ int level; /* 0 <= level <= 9 */ ++ UInt32 dictSize; /* (1 << 12) <= dictSize <= (1 << 27) for 32-bit version ++ (1 << 12) <= dictSize <= (1 << 30) for 64-bit version ++ default = (1 << 24) */ ++ int lc; /* 0 <= lc <= 8, default = 3 */ ++ int lp; /* 0 <= lp <= 4, default = 0 */ ++ int pb; /* 0 <= pb <= 4, default = 2 */ ++ int algo; /* 0 - fast, 1 - normal, default = 1 */ ++ int fb; /* 5 <= fb <= 273, default = 32 */ ++ int btMode; /* 0 - hashChain Mode, 1 - binTree mode - normal, default = 1 */ ++ int numHashBytes; /* 2, 3 or 4, default = 4 */ ++ UInt32 mc; /* 1 <= mc <= (1 << 30), default = 32 */ ++ unsigned writeEndMark; /* 0 - do not write EOPM, 1 - write EOPM, default = 0 */ ++ int numThreads; /* 1 or 2, default = 2 */ ++} CLzmaEncProps; ++ ++void LzmaEncProps_Init(CLzmaEncProps *p); ++void LzmaEncProps_Normalize(CLzmaEncProps *p); ++UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2); ++ ++ ++/* ---------- CLzmaEncHandle Interface ---------- */ ++ ++/* LzmaEnc_* functions can return the following exit codes: ++Returns: ++ SZ_OK - OK ++ SZ_ERROR_MEM - Memory allocation error ++ SZ_ERROR_PARAM - Incorrect paramater in props ++ SZ_ERROR_WRITE - Write callback error. ++ SZ_ERROR_PROGRESS - some break from progress callback ++ SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) ++*/ ++ ++typedef void * CLzmaEncHandle; ++ ++CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc); ++void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig); ++SRes LzmaEnc_SetProps(CLzmaEncHandle p, const CLzmaEncProps *props); ++SRes LzmaEnc_WriteProperties(CLzmaEncHandle p, Byte *properties, SizeT *size); ++SRes LzmaEnc_Encode(CLzmaEncHandle p, ISeqOutStream *outStream, ISeqInStream *inStream, ++ ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); ++SRes LzmaEnc_MemEncode(CLzmaEncHandle p, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, ++ int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); ++ ++/* ---------- One Call Interface ---------- */ ++ ++/* LzmaEncode ++Return code: ++ SZ_OK - OK ++ SZ_ERROR_MEM - Memory allocation error ++ SZ_ERROR_PARAM - Incorrect paramater ++ SZ_ERROR_OUTPUT_EOF - output buffer overflow ++ SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) ++*/ ++ ++SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, ++ const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, ++ ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +--- /dev/null ++++ b/include/linux/lzma/Types.h +@@ -0,0 +1,226 @@ ++/* Types.h -- Basic types ++2009-11-23 : Igor Pavlov : Public domain */ ++ ++#ifndef __7Z_TYPES_H ++#define __7Z_TYPES_H ++ ++#include ++ ++#ifdef _WIN32 ++#include ++#endif ++ ++#ifndef EXTERN_C_BEGIN ++#ifdef __cplusplus ++#define EXTERN_C_BEGIN extern "C" { ++#define EXTERN_C_END } ++#else ++#define EXTERN_C_BEGIN ++#define EXTERN_C_END ++#endif ++#endif ++ ++EXTERN_C_BEGIN ++ ++#define SZ_OK 0 ++ ++#define SZ_ERROR_DATA 1 ++#define SZ_ERROR_MEM 2 ++#define SZ_ERROR_CRC 3 ++#define SZ_ERROR_UNSUPPORTED 4 ++#define SZ_ERROR_PARAM 5 ++#define SZ_ERROR_INPUT_EOF 6 ++#define SZ_ERROR_OUTPUT_EOF 7 ++#define SZ_ERROR_READ 8 ++#define SZ_ERROR_WRITE 9 ++#define SZ_ERROR_PROGRESS 10 ++#define SZ_ERROR_FAIL 11 ++#define SZ_ERROR_THREAD 12 ++ ++#define SZ_ERROR_ARCHIVE 16 ++#define SZ_ERROR_NO_ARCHIVE 17 ++ ++typedef int SRes; ++ ++#ifdef _WIN32 ++typedef DWORD WRes; ++#else ++typedef int WRes; ++#endif ++ ++#ifndef RINOK ++#define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; } ++#endif ++ ++typedef unsigned char Byte; ++typedef short Int16; ++typedef unsigned short UInt16; ++ ++#ifdef _LZMA_UINT32_IS_ULONG ++typedef long Int32; ++typedef unsigned long UInt32; ++#else ++typedef int Int32; ++typedef unsigned int UInt32; ++#endif ++ ++#ifdef _SZ_NO_INT_64 ++ ++/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers. ++ NOTES: Some code will work incorrectly in that case! */ ++ ++typedef long Int64; ++typedef unsigned long UInt64; ++ ++#else ++ ++#if defined(_MSC_VER) || defined(__BORLANDC__) ++typedef __int64 Int64; ++typedef unsigned __int64 UInt64; ++#else ++typedef long long int Int64; ++typedef unsigned long long int UInt64; ++#endif ++ ++#endif ++ ++#ifdef _LZMA_NO_SYSTEM_SIZE_T ++typedef UInt32 SizeT; ++#else ++typedef size_t SizeT; ++#endif ++ ++typedef int Bool; ++#define True 1 ++#define False 0 ++ ++ ++#ifdef _WIN32 ++#define MY_STD_CALL __stdcall ++#else ++#define MY_STD_CALL ++#endif ++ ++#ifdef _MSC_VER ++ ++#if _MSC_VER >= 1300 ++#define MY_NO_INLINE __declspec(noinline) ++#else ++#define MY_NO_INLINE ++#endif ++ ++#define MY_CDECL __cdecl ++#define MY_FAST_CALL __fastcall ++ ++#else ++ ++#define MY_CDECL ++#define MY_FAST_CALL ++ ++#endif ++ ++ ++/* The following interfaces use first parameter as pointer to structure */ ++ ++typedef struct ++{ ++ SRes (*Read)(void *p, void *buf, size_t *size); ++ /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. ++ (output(*size) < input(*size)) is allowed */ ++} ISeqInStream; ++ ++/* it can return SZ_ERROR_INPUT_EOF */ ++SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size); ++SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType); ++SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf); ++ ++typedef struct ++{ ++ size_t (*Write)(void *p, const void *buf, size_t size); ++ /* Returns: result - the number of actually written bytes. ++ (result < size) means error */ ++} ISeqOutStream; ++ ++typedef enum ++{ ++ SZ_SEEK_SET = 0, ++ SZ_SEEK_CUR = 1, ++ SZ_SEEK_END = 2 ++} ESzSeek; ++ ++typedef struct ++{ ++ SRes (*Read)(void *p, void *buf, size_t *size); /* same as ISeqInStream::Read */ ++ SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); ++} ISeekInStream; ++ ++typedef struct ++{ ++ SRes (*Look)(void *p, void **buf, size_t *size); ++ /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. ++ (output(*size) > input(*size)) is not allowed ++ (output(*size) < input(*size)) is allowed */ ++ SRes (*Skip)(void *p, size_t offset); ++ /* offset must be <= output(*size) of Look */ ++ ++ SRes (*Read)(void *p, void *buf, size_t *size); ++ /* reads directly (without buffer). It's same as ISeqInStream::Read */ ++ SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); ++} ILookInStream; ++ ++SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size); ++SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset); ++ ++/* reads via ILookInStream::Read */ ++SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType); ++SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size); ++ ++#define LookToRead_BUF_SIZE (1 << 14) ++ ++typedef struct ++{ ++ ILookInStream s; ++ ISeekInStream *realStream; ++ size_t pos; ++ size_t size; ++ Byte buf[LookToRead_BUF_SIZE]; ++} CLookToRead; ++ ++void LookToRead_CreateVTable(CLookToRead *p, int lookahead); ++void LookToRead_Init(CLookToRead *p); ++ ++typedef struct ++{ ++ ISeqInStream s; ++ ILookInStream *realStream; ++} CSecToLook; ++ ++void SecToLook_CreateVTable(CSecToLook *p); ++ ++typedef struct ++{ ++ ISeqInStream s; ++ ILookInStream *realStream; ++} CSecToRead; ++ ++void SecToRead_CreateVTable(CSecToRead *p); ++ ++typedef struct ++{ ++ SRes (*Progress)(void *p, UInt64 inSize, UInt64 outSize); ++ /* Returns: result. (result != SZ_OK) means break. ++ Value (UInt64)(Int64)-1 for size means unknown value. */ ++} ICompressProgress; ++ ++typedef struct ++{ ++ void *(*Alloc)(void *p, size_t size); ++ void (*Free)(void *p, void *address); /* address can be 0 */ ++} ISzAlloc; ++ ++#define IAlloc_Alloc(p, size) (p)->Alloc((p), size) ++#define IAlloc_Free(p, a) (p)->Free((p), a) ++ ++EXTERN_C_END ++ ++#endif +--- a/include/uapi/linux/jffs2.h ++++ b/include/uapi/linux/jffs2.h +@@ -46,6 +46,7 @@ + #define JFFS2_COMPR_DYNRUBIN 0x05 + #define JFFS2_COMPR_ZLIB 0x06 + #define JFFS2_COMPR_LZO 0x07 ++#define JFFS2_COMPR_LZMA 0x08 + /* Compatibility flags. */ + #define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */ + #define JFFS2_NODE_ACCURATE 0x2000 +--- a/lib/Kconfig ++++ b/lib/Kconfig +@@ -354,6 +354,12 @@ config ZSTD_DECOMPRESS + + source "lib/xz/Kconfig" + ++config LZMA_COMPRESS ++ tristate ++ ++config LZMA_DECOMPRESS ++ tristate ++ + # + # These all provide a common interface (hence the apparent duplication with + # ZLIB_INFLATE; DECOMPRESS_GZIP is just a wrapper.) +--- a/lib/Makefile ++++ b/lib/Makefile +@@ -140,6 +140,16 @@ CFLAGS_kobject.o += -DDEBUG + CFLAGS_kobject_uevent.o += -DDEBUG + endif + ++ifdef CONFIG_JFFS2_ZLIB ++ CONFIG_ZLIB_INFLATE:=y ++ CONFIG_ZLIB_DEFLATE:=y ++endif ++ ++ifdef CONFIG_JFFS2_LZMA ++ CONFIG_LZMA_DECOMPRESS:=y ++ CONFIG_LZMA_COMPRESS:=y ++endif ++ + obj-$(CONFIG_DEBUG_INFO_REDUCED) += debug_info.o + CFLAGS_debug_info.o += $(call cc-option, -femit-struct-debug-detailed=any) + +@@ -200,6 +210,8 @@ obj-$(CONFIG_ZSTD_COMPRESS) += zstd/ + obj-$(CONFIG_ZSTD_DECOMPRESS) += zstd/ + obj-$(CONFIG_XZ_DEC) += xz/ + obj-$(CONFIG_RAID6_PQ) += raid6/ ++obj-$(CONFIG_LZMA_COMPRESS) += lzma/ ++obj-$(CONFIG_LZMA_DECOMPRESS) += lzma/ + + lib-$(CONFIG_DECOMPRESS_GZIP) += decompress_inflate.o + lib-$(CONFIG_DECOMPRESS_BZIP2) += decompress_bunzip2.o +--- /dev/null ++++ b/lib/lzma/LzFind.c +@@ -0,0 +1,761 @@ ++/* LzFind.c -- Match finder for LZ algorithms ++2009-04-22 : Igor Pavlov : Public domain */ ++ ++#include ++ ++#include "LzFind.h" ++#include "LzHash.h" ++ ++#define kEmptyHashValue 0 ++#define kMaxValForNormalize ((UInt32)0xFFFFFFFF) ++#define kNormalizeStepMin (1 << 10) /* it must be power of 2 */ ++#define kNormalizeMask (~(kNormalizeStepMin - 1)) ++#define kMaxHistorySize ((UInt32)3 << 30) ++ ++#define kStartMaxLen 3 ++ ++static void LzInWindow_Free(CMatchFinder *p, ISzAlloc *alloc) ++{ ++ if (!p->directInput) ++ { ++ alloc->Free(alloc, p->bufferBase); ++ p->bufferBase = 0; ++ } ++} ++ ++/* keepSizeBefore + keepSizeAfter + keepSizeReserv must be < 4G) */ ++ ++static int LzInWindow_Create(CMatchFinder *p, UInt32 keepSizeReserv, ISzAlloc *alloc) ++{ ++ UInt32 blockSize = p->keepSizeBefore + p->keepSizeAfter + keepSizeReserv; ++ if (p->directInput) ++ { ++ p->blockSize = blockSize; ++ return 1; ++ } ++ if (p->bufferBase == 0 || p->blockSize != blockSize) ++ { ++ LzInWindow_Free(p, alloc); ++ p->blockSize = blockSize; ++ p->bufferBase = (Byte *)alloc->Alloc(alloc, (size_t)blockSize); ++ } ++ return (p->bufferBase != 0); ++} ++ ++Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; } ++Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 index) { return p->buffer[index]; } ++ ++UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; } ++ ++void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue) ++{ ++ p->posLimit -= subValue; ++ p->pos -= subValue; ++ p->streamPos -= subValue; ++} ++ ++static void MatchFinder_ReadBlock(CMatchFinder *p) ++{ ++ if (p->streamEndWasReached || p->result != SZ_OK) ++ return; ++ if (p->directInput) ++ { ++ UInt32 curSize = 0xFFFFFFFF - p->streamPos; ++ if (curSize > p->directInputRem) ++ curSize = (UInt32)p->directInputRem; ++ p->directInputRem -= curSize; ++ p->streamPos += curSize; ++ if (p->directInputRem == 0) ++ p->streamEndWasReached = 1; ++ return; ++ } ++ for (;;) ++ { ++ Byte *dest = p->buffer + (p->streamPos - p->pos); ++ size_t size = (p->bufferBase + p->blockSize - dest); ++ if (size == 0) ++ return; ++ p->result = p->stream->Read(p->stream, dest, &size); ++ if (p->result != SZ_OK) ++ return; ++ if (size == 0) ++ { ++ p->streamEndWasReached = 1; ++ return; ++ } ++ p->streamPos += (UInt32)size; ++ if (p->streamPos - p->pos > p->keepSizeAfter) ++ return; ++ } ++} ++ ++void MatchFinder_MoveBlock(CMatchFinder *p) ++{ ++ memmove(p->bufferBase, ++ p->buffer - p->keepSizeBefore, ++ (size_t)(p->streamPos - p->pos + p->keepSizeBefore)); ++ p->buffer = p->bufferBase + p->keepSizeBefore; ++} ++ ++int MatchFinder_NeedMove(CMatchFinder *p) ++{ ++ if (p->directInput) ++ return 0; ++ /* if (p->streamEndWasReached) return 0; */ ++ return ((size_t)(p->bufferBase + p->blockSize - p->buffer) <= p->keepSizeAfter); ++} ++ ++void MatchFinder_ReadIfRequired(CMatchFinder *p) ++{ ++ if (p->streamEndWasReached) ++ return; ++ if (p->keepSizeAfter >= p->streamPos - p->pos) ++ MatchFinder_ReadBlock(p); ++} ++ ++static void MatchFinder_CheckAndMoveAndRead(CMatchFinder *p) ++{ ++ if (MatchFinder_NeedMove(p)) ++ MatchFinder_MoveBlock(p); ++ MatchFinder_ReadBlock(p); ++} ++ ++static void MatchFinder_SetDefaultSettings(CMatchFinder *p) ++{ ++ p->cutValue = 32; ++ p->btMode = 1; ++ p->numHashBytes = 4; ++ p->bigHash = 0; ++} ++ ++#define kCrcPoly 0xEDB88320 ++ ++void MatchFinder_Construct(CMatchFinder *p) ++{ ++ UInt32 i; ++ p->bufferBase = 0; ++ p->directInput = 0; ++ p->hash = 0; ++ MatchFinder_SetDefaultSettings(p); ++ ++ for (i = 0; i < 256; i++) ++ { ++ UInt32 r = i; ++ int j; ++ for (j = 0; j < 8; j++) ++ r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); ++ p->crc[i] = r; ++ } ++} ++ ++static void MatchFinder_FreeThisClassMemory(CMatchFinder *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->hash); ++ p->hash = 0; ++} ++ ++void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc) ++{ ++ MatchFinder_FreeThisClassMemory(p, alloc); ++ LzInWindow_Free(p, alloc); ++} ++ ++static CLzRef* AllocRefs(UInt32 num, ISzAlloc *alloc) ++{ ++ size_t sizeInBytes = (size_t)num * sizeof(CLzRef); ++ if (sizeInBytes / sizeof(CLzRef) != num) ++ return 0; ++ return (CLzRef *)alloc->Alloc(alloc, sizeInBytes); ++} ++ ++int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, ++ UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ++ ISzAlloc *alloc) ++{ ++ UInt32 sizeReserv; ++ if (historySize > kMaxHistorySize) ++ { ++ MatchFinder_Free(p, alloc); ++ return 0; ++ } ++ sizeReserv = historySize >> 1; ++ if (historySize > ((UInt32)2 << 30)) ++ sizeReserv = historySize >> 2; ++ sizeReserv += (keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2 + (1 << 19); ++ ++ p->keepSizeBefore = historySize + keepAddBufferBefore + 1; ++ p->keepSizeAfter = matchMaxLen + keepAddBufferAfter; ++ /* we need one additional byte, since we use MoveBlock after pos++ and before dictionary using */ ++ if (LzInWindow_Create(p, sizeReserv, alloc)) ++ { ++ UInt32 newCyclicBufferSize = historySize + 1; ++ UInt32 hs; ++ p->matchMaxLen = matchMaxLen; ++ { ++ p->fixedHashSize = 0; ++ if (p->numHashBytes == 2) ++ hs = (1 << 16) - 1; ++ else ++ { ++ hs = historySize - 1; ++ hs |= (hs >> 1); ++ hs |= (hs >> 2); ++ hs |= (hs >> 4); ++ hs |= (hs >> 8); ++ hs >>= 1; ++ hs |= 0xFFFF; /* don't change it! It's required for Deflate */ ++ if (hs > (1 << 24)) ++ { ++ if (p->numHashBytes == 3) ++ hs = (1 << 24) - 1; ++ else ++ hs >>= 1; ++ } ++ } ++ p->hashMask = hs; ++ hs++; ++ if (p->numHashBytes > 2) p->fixedHashSize += kHash2Size; ++ if (p->numHashBytes > 3) p->fixedHashSize += kHash3Size; ++ if (p->numHashBytes > 4) p->fixedHashSize += kHash4Size; ++ hs += p->fixedHashSize; ++ } ++ ++ { ++ UInt32 prevSize = p->hashSizeSum + p->numSons; ++ UInt32 newSize; ++ p->historySize = historySize; ++ p->hashSizeSum = hs; ++ p->cyclicBufferSize = newCyclicBufferSize; ++ p->numSons = (p->btMode ? newCyclicBufferSize * 2 : newCyclicBufferSize); ++ newSize = p->hashSizeSum + p->numSons; ++ if (p->hash != 0 && prevSize == newSize) ++ return 1; ++ MatchFinder_FreeThisClassMemory(p, alloc); ++ p->hash = AllocRefs(newSize, alloc); ++ if (p->hash != 0) ++ { ++ p->son = p->hash + p->hashSizeSum; ++ return 1; ++ } ++ } ++ } ++ MatchFinder_Free(p, alloc); ++ return 0; ++} ++ ++static void MatchFinder_SetLimits(CMatchFinder *p) ++{ ++ UInt32 limit = kMaxValForNormalize - p->pos; ++ UInt32 limit2 = p->cyclicBufferSize - p->cyclicBufferPos; ++ if (limit2 < limit) ++ limit = limit2; ++ limit2 = p->streamPos - p->pos; ++ if (limit2 <= p->keepSizeAfter) ++ { ++ if (limit2 > 0) ++ limit2 = 1; ++ } ++ else ++ limit2 -= p->keepSizeAfter; ++ if (limit2 < limit) ++ limit = limit2; ++ { ++ UInt32 lenLimit = p->streamPos - p->pos; ++ if (lenLimit > p->matchMaxLen) ++ lenLimit = p->matchMaxLen; ++ p->lenLimit = lenLimit; ++ } ++ p->posLimit = p->pos + limit; ++} ++ ++void MatchFinder_Init(CMatchFinder *p) ++{ ++ UInt32 i; ++ for (i = 0; i < p->hashSizeSum; i++) ++ p->hash[i] = kEmptyHashValue; ++ p->cyclicBufferPos = 0; ++ p->buffer = p->bufferBase; ++ p->pos = p->streamPos = p->cyclicBufferSize; ++ p->result = SZ_OK; ++ p->streamEndWasReached = 0; ++ MatchFinder_ReadBlock(p); ++ MatchFinder_SetLimits(p); ++} ++ ++static UInt32 MatchFinder_GetSubValue(CMatchFinder *p) ++{ ++ return (p->pos - p->historySize - 1) & kNormalizeMask; ++} ++ ++void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems) ++{ ++ UInt32 i; ++ for (i = 0; i < numItems; i++) ++ { ++ UInt32 value = items[i]; ++ if (value <= subValue) ++ value = kEmptyHashValue; ++ else ++ value -= subValue; ++ items[i] = value; ++ } ++} ++ ++static void MatchFinder_Normalize(CMatchFinder *p) ++{ ++ UInt32 subValue = MatchFinder_GetSubValue(p); ++ MatchFinder_Normalize3(subValue, p->hash, p->hashSizeSum + p->numSons); ++ MatchFinder_ReduceOffsets(p, subValue); ++} ++ ++static void MatchFinder_CheckLimits(CMatchFinder *p) ++{ ++ if (p->pos == kMaxValForNormalize) ++ MatchFinder_Normalize(p); ++ if (!p->streamEndWasReached && p->keepSizeAfter == p->streamPos - p->pos) ++ MatchFinder_CheckAndMoveAndRead(p); ++ if (p->cyclicBufferPos == p->cyclicBufferSize) ++ p->cyclicBufferPos = 0; ++ MatchFinder_SetLimits(p); ++} ++ ++static UInt32 * Hc_GetMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, ++ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, ++ UInt32 *distances, UInt32 maxLen) ++{ ++ son[_cyclicBufferPos] = curMatch; ++ for (;;) ++ { ++ UInt32 delta = pos - curMatch; ++ if (cutValue-- == 0 || delta >= _cyclicBufferSize) ++ return distances; ++ { ++ const Byte *pb = cur - delta; ++ curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)]; ++ if (pb[maxLen] == cur[maxLen] && *pb == *cur) ++ { ++ UInt32 len = 0; ++ while (++len != lenLimit) ++ if (pb[len] != cur[len]) ++ break; ++ if (maxLen < len) ++ { ++ *distances++ = maxLen = len; ++ *distances++ = delta - 1; ++ if (len == lenLimit) ++ return distances; ++ } ++ } ++ } ++ } ++} ++ ++UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, ++ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, ++ UInt32 *distances, UInt32 maxLen) ++{ ++ CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; ++ CLzRef *ptr1 = son + (_cyclicBufferPos << 1); ++ UInt32 len0 = 0, len1 = 0; ++ for (;;) ++ { ++ UInt32 delta = pos - curMatch; ++ if (cutValue-- == 0 || delta >= _cyclicBufferSize) ++ { ++ *ptr0 = *ptr1 = kEmptyHashValue; ++ return distances; ++ } ++ { ++ CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); ++ const Byte *pb = cur - delta; ++ UInt32 len = (len0 < len1 ? len0 : len1); ++ if (pb[len] == cur[len]) ++ { ++ if (++len != lenLimit && pb[len] == cur[len]) ++ while (++len != lenLimit) ++ if (pb[len] != cur[len]) ++ break; ++ if (maxLen < len) ++ { ++ *distances++ = maxLen = len; ++ *distances++ = delta - 1; ++ if (len == lenLimit) ++ { ++ *ptr1 = pair[0]; ++ *ptr0 = pair[1]; ++ return distances; ++ } ++ } ++ } ++ if (pb[len] < cur[len]) ++ { ++ *ptr1 = curMatch; ++ ptr1 = pair + 1; ++ curMatch = *ptr1; ++ len1 = len; ++ } ++ else ++ { ++ *ptr0 = curMatch; ++ ptr0 = pair; ++ curMatch = *ptr0; ++ len0 = len; ++ } ++ } ++ } ++} ++ ++static void SkipMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, ++ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue) ++{ ++ CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; ++ CLzRef *ptr1 = son + (_cyclicBufferPos << 1); ++ UInt32 len0 = 0, len1 = 0; ++ for (;;) ++ { ++ UInt32 delta = pos - curMatch; ++ if (cutValue-- == 0 || delta >= _cyclicBufferSize) ++ { ++ *ptr0 = *ptr1 = kEmptyHashValue; ++ return; ++ } ++ { ++ CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); ++ const Byte *pb = cur - delta; ++ UInt32 len = (len0 < len1 ? len0 : len1); ++ if (pb[len] == cur[len]) ++ { ++ while (++len != lenLimit) ++ if (pb[len] != cur[len]) ++ break; ++ { ++ if (len == lenLimit) ++ { ++ *ptr1 = pair[0]; ++ *ptr0 = pair[1]; ++ return; ++ } ++ } ++ } ++ if (pb[len] < cur[len]) ++ { ++ *ptr1 = curMatch; ++ ptr1 = pair + 1; ++ curMatch = *ptr1; ++ len1 = len; ++ } ++ else ++ { ++ *ptr0 = curMatch; ++ ptr0 = pair; ++ curMatch = *ptr0; ++ len0 = len; ++ } ++ } ++ } ++} ++ ++#define MOVE_POS \ ++ ++p->cyclicBufferPos; \ ++ p->buffer++; \ ++ if (++p->pos == p->posLimit) MatchFinder_CheckLimits(p); ++ ++#define MOVE_POS_RET MOVE_POS return offset; ++ ++static void MatchFinder_MovePos(CMatchFinder *p) { MOVE_POS; } ++ ++#define GET_MATCHES_HEADER2(minLen, ret_op) \ ++ UInt32 lenLimit; UInt32 hashValue; const Byte *cur; UInt32 curMatch; \ ++ lenLimit = p->lenLimit; { if (lenLimit < minLen) { MatchFinder_MovePos(p); ret_op; }} \ ++ cur = p->buffer; ++ ++#define GET_MATCHES_HEADER(minLen) GET_MATCHES_HEADER2(minLen, return 0) ++#define SKIP_HEADER(minLen) GET_MATCHES_HEADER2(minLen, continue) ++ ++#define MF_PARAMS(p) p->pos, p->buffer, p->son, p->cyclicBufferPos, p->cyclicBufferSize, p->cutValue ++ ++#define GET_MATCHES_FOOTER(offset, maxLen) \ ++ offset = (UInt32)(GetMatchesSpec1(lenLimit, curMatch, MF_PARAMS(p), \ ++ distances + offset, maxLen) - distances); MOVE_POS_RET; ++ ++#define SKIP_FOOTER \ ++ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS; ++ ++static UInt32 Bt2_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 offset; ++ GET_MATCHES_HEADER(2) ++ HASH2_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ offset = 0; ++ GET_MATCHES_FOOTER(offset, 1) ++} ++ ++UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 offset; ++ GET_MATCHES_HEADER(3) ++ HASH_ZIP_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ offset = 0; ++ GET_MATCHES_FOOTER(offset, 2) ++} ++ ++static UInt32 Bt3_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 hash2Value, delta2, maxLen, offset; ++ GET_MATCHES_HEADER(3) ++ ++ HASH3_CALC; ++ ++ delta2 = p->pos - p->hash[hash2Value]; ++ curMatch = p->hash[kFix3HashSize + hashValue]; ++ ++ p->hash[hash2Value] = ++ p->hash[kFix3HashSize + hashValue] = p->pos; ++ ++ ++ maxLen = 2; ++ offset = 0; ++ if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) ++ { ++ for (; maxLen != lenLimit; maxLen++) ++ if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) ++ break; ++ distances[0] = maxLen; ++ distances[1] = delta2 - 1; ++ offset = 2; ++ if (maxLen == lenLimit) ++ { ++ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); ++ MOVE_POS_RET; ++ } ++ } ++ GET_MATCHES_FOOTER(offset, maxLen) ++} ++ ++static UInt32 Bt4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; ++ GET_MATCHES_HEADER(4) ++ ++ HASH4_CALC; ++ ++ delta2 = p->pos - p->hash[ hash2Value]; ++ delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; ++ curMatch = p->hash[kFix4HashSize + hashValue]; ++ ++ p->hash[ hash2Value] = ++ p->hash[kFix3HashSize + hash3Value] = ++ p->hash[kFix4HashSize + hashValue] = p->pos; ++ ++ maxLen = 1; ++ offset = 0; ++ if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) ++ { ++ distances[0] = maxLen = 2; ++ distances[1] = delta2 - 1; ++ offset = 2; ++ } ++ if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) ++ { ++ maxLen = 3; ++ distances[offset + 1] = delta3 - 1; ++ offset += 2; ++ delta2 = delta3; ++ } ++ if (offset != 0) ++ { ++ for (; maxLen != lenLimit; maxLen++) ++ if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) ++ break; ++ distances[offset - 2] = maxLen; ++ if (maxLen == lenLimit) ++ { ++ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); ++ MOVE_POS_RET; ++ } ++ } ++ if (maxLen < 3) ++ maxLen = 3; ++ GET_MATCHES_FOOTER(offset, maxLen) ++} ++ ++static UInt32 Hc4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; ++ GET_MATCHES_HEADER(4) ++ ++ HASH4_CALC; ++ ++ delta2 = p->pos - p->hash[ hash2Value]; ++ delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; ++ curMatch = p->hash[kFix4HashSize + hashValue]; ++ ++ p->hash[ hash2Value] = ++ p->hash[kFix3HashSize + hash3Value] = ++ p->hash[kFix4HashSize + hashValue] = p->pos; ++ ++ maxLen = 1; ++ offset = 0; ++ if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) ++ { ++ distances[0] = maxLen = 2; ++ distances[1] = delta2 - 1; ++ offset = 2; ++ } ++ if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) ++ { ++ maxLen = 3; ++ distances[offset + 1] = delta3 - 1; ++ offset += 2; ++ delta2 = delta3; ++ } ++ if (offset != 0) ++ { ++ for (; maxLen != lenLimit; maxLen++) ++ if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) ++ break; ++ distances[offset - 2] = maxLen; ++ if (maxLen == lenLimit) ++ { ++ p->son[p->cyclicBufferPos] = curMatch; ++ MOVE_POS_RET; ++ } ++ } ++ if (maxLen < 3) ++ maxLen = 3; ++ offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), ++ distances + offset, maxLen) - (distances)); ++ MOVE_POS_RET ++} ++ ++UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 offset; ++ GET_MATCHES_HEADER(3) ++ HASH_ZIP_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), ++ distances, 2) - (distances)); ++ MOVE_POS_RET ++} ++ ++static void Bt2_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ SKIP_HEADER(2) ++ HASH2_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ SKIP_FOOTER ++ } ++ while (--num != 0); ++} ++ ++void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ SKIP_HEADER(3) ++ HASH_ZIP_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ SKIP_FOOTER ++ } ++ while (--num != 0); ++} ++ ++static void Bt3_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ UInt32 hash2Value; ++ SKIP_HEADER(3) ++ HASH3_CALC; ++ curMatch = p->hash[kFix3HashSize + hashValue]; ++ p->hash[hash2Value] = ++ p->hash[kFix3HashSize + hashValue] = p->pos; ++ SKIP_FOOTER ++ } ++ while (--num != 0); ++} ++ ++static void Bt4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ UInt32 hash2Value, hash3Value; ++ SKIP_HEADER(4) ++ HASH4_CALC; ++ curMatch = p->hash[kFix4HashSize + hashValue]; ++ p->hash[ hash2Value] = ++ p->hash[kFix3HashSize + hash3Value] = p->pos; ++ p->hash[kFix4HashSize + hashValue] = p->pos; ++ SKIP_FOOTER ++ } ++ while (--num != 0); ++} ++ ++static void Hc4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ UInt32 hash2Value, hash3Value; ++ SKIP_HEADER(4) ++ HASH4_CALC; ++ curMatch = p->hash[kFix4HashSize + hashValue]; ++ p->hash[ hash2Value] = ++ p->hash[kFix3HashSize + hash3Value] = ++ p->hash[kFix4HashSize + hashValue] = p->pos; ++ p->son[p->cyclicBufferPos] = curMatch; ++ MOVE_POS ++ } ++ while (--num != 0); ++} ++ ++void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ SKIP_HEADER(3) ++ HASH_ZIP_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ p->son[p->cyclicBufferPos] = curMatch; ++ MOVE_POS ++ } ++ while (--num != 0); ++} ++ ++void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable) ++{ ++ vTable->Init = (Mf_Init_Func)MatchFinder_Init; ++ vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinder_GetIndexByte; ++ vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinder_GetNumAvailableBytes; ++ vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinder_GetPointerToCurrentPos; ++ if (!p->btMode) ++ { ++ vTable->GetMatches = (Mf_GetMatches_Func)Hc4_MatchFinder_GetMatches; ++ vTable->Skip = (Mf_Skip_Func)Hc4_MatchFinder_Skip; ++ } ++ else if (p->numHashBytes == 2) ++ { ++ vTable->GetMatches = (Mf_GetMatches_Func)Bt2_MatchFinder_GetMatches; ++ vTable->Skip = (Mf_Skip_Func)Bt2_MatchFinder_Skip; ++ } ++ else if (p->numHashBytes == 3) ++ { ++ vTable->GetMatches = (Mf_GetMatches_Func)Bt3_MatchFinder_GetMatches; ++ vTable->Skip = (Mf_Skip_Func)Bt3_MatchFinder_Skip; ++ } ++ else ++ { ++ vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches; ++ vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip; ++ } ++} +--- /dev/null ++++ b/lib/lzma/LzmaDec.c +@@ -0,0 +1,999 @@ ++/* LzmaDec.c -- LZMA Decoder ++2009-09-20 : Igor Pavlov : Public domain */ ++ ++#include "LzmaDec.h" ++ ++#include ++ ++#define kNumTopBits 24 ++#define kTopValue ((UInt32)1 << kNumTopBits) ++ ++#define kNumBitModelTotalBits 11 ++#define kBitModelTotal (1 << kNumBitModelTotalBits) ++#define kNumMoveBits 5 ++ ++#define RC_INIT_SIZE 5 ++ ++#define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } ++ ++#define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) ++#define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); ++#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits)); ++#define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \ ++ { UPDATE_0(p); i = (i + i); A0; } else \ ++ { UPDATE_1(p); i = (i + i) + 1; A1; } ++#define GET_BIT(p, i) GET_BIT2(p, i, ; , ;) ++ ++#define TREE_GET_BIT(probs, i) { GET_BIT((probs + i), i); } ++#define TREE_DECODE(probs, limit, i) \ ++ { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; } ++ ++/* #define _LZMA_SIZE_OPT */ ++ ++#ifdef _LZMA_SIZE_OPT ++#define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i) ++#else ++#define TREE_6_DECODE(probs, i) \ ++ { i = 1; \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ i -= 0x40; } ++#endif ++ ++#define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } ++ ++#define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) ++#define UPDATE_0_CHECK range = bound; ++#define UPDATE_1_CHECK range -= bound; code -= bound; ++#define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \ ++ { UPDATE_0_CHECK; i = (i + i); A0; } else \ ++ { UPDATE_1_CHECK; i = (i + i) + 1; A1; } ++#define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;) ++#define TREE_DECODE_CHECK(probs, limit, i) \ ++ { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; } ++ ++ ++#define kNumPosBitsMax 4 ++#define kNumPosStatesMax (1 << kNumPosBitsMax) ++ ++#define kLenNumLowBits 3 ++#define kLenNumLowSymbols (1 << kLenNumLowBits) ++#define kLenNumMidBits 3 ++#define kLenNumMidSymbols (1 << kLenNumMidBits) ++#define kLenNumHighBits 8 ++#define kLenNumHighSymbols (1 << kLenNumHighBits) ++ ++#define LenChoice 0 ++#define LenChoice2 (LenChoice + 1) ++#define LenLow (LenChoice2 + 1) ++#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) ++#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) ++#define kNumLenProbs (LenHigh + kLenNumHighSymbols) ++ ++ ++#define kNumStates 12 ++#define kNumLitStates 7 ++ ++#define kStartPosModelIndex 4 ++#define kEndPosModelIndex 14 ++#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) ++ ++#define kNumPosSlotBits 6 ++#define kNumLenToPosStates 4 ++ ++#define kNumAlignBits 4 ++#define kAlignTableSize (1 << kNumAlignBits) ++ ++#define kMatchMinLen 2 ++#define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) ++ ++#define IsMatch 0 ++#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) ++#define IsRepG0 (IsRep + kNumStates) ++#define IsRepG1 (IsRepG0 + kNumStates) ++#define IsRepG2 (IsRepG1 + kNumStates) ++#define IsRep0Long (IsRepG2 + kNumStates) ++#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) ++#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) ++#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) ++#define LenCoder (Align + kAlignTableSize) ++#define RepLenCoder (LenCoder + kNumLenProbs) ++#define Literal (RepLenCoder + kNumLenProbs) ++ ++#define LZMA_BASE_SIZE 1846 ++#define LZMA_LIT_SIZE 768 ++ ++#define LzmaProps_GetNumProbs(p) ((UInt32)LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((p)->lc + (p)->lp))) ++ ++#if Literal != LZMA_BASE_SIZE ++StopCompilingDueBUG ++#endif ++ ++#define LZMA_DIC_MIN (1 << 12) ++ ++/* First LZMA-symbol is always decoded. ++And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization ++Out: ++ Result: ++ SZ_OK - OK ++ SZ_ERROR_DATA - Error ++ p->remainLen: ++ < kMatchSpecLenStart : normal remain ++ = kMatchSpecLenStart : finished ++ = kMatchSpecLenStart + 1 : Flush marker ++ = kMatchSpecLenStart + 2 : State Init Marker ++*/ ++ ++static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte *bufLimit) ++{ ++ CLzmaProb *probs = p->probs; ++ ++ unsigned state = p->state; ++ UInt32 rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3]; ++ unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1; ++ unsigned lpMask = ((unsigned)1 << (p->prop.lp)) - 1; ++ unsigned lc = p->prop.lc; ++ ++ Byte *dic = p->dic; ++ SizeT dicBufSize = p->dicBufSize; ++ SizeT dicPos = p->dicPos; ++ ++ UInt32 processedPos = p->processedPos; ++ UInt32 checkDicSize = p->checkDicSize; ++ unsigned len = 0; ++ ++ const Byte *buf = p->buf; ++ UInt32 range = p->range; ++ UInt32 code = p->code; ++ ++ do ++ { ++ CLzmaProb *prob; ++ UInt32 bound; ++ unsigned ttt; ++ unsigned posState = processedPos & pbMask; ++ ++ prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; ++ IF_BIT_0(prob) ++ { ++ unsigned symbol; ++ UPDATE_0(prob); ++ prob = probs + Literal; ++ if (checkDicSize != 0 || processedPos != 0) ++ prob += (LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) + ++ (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc)))); ++ ++ if (state < kNumLitStates) ++ { ++ state -= (state < 4) ? state : 3; ++ symbol = 1; ++ do { GET_BIT(prob + symbol, symbol) } while (symbol < 0x100); ++ } ++ else ++ { ++ unsigned matchByte = p->dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; ++ unsigned offs = 0x100; ++ state -= (state < 10) ? 3 : 6; ++ symbol = 1; ++ do ++ { ++ unsigned bit; ++ CLzmaProb *probLit; ++ matchByte <<= 1; ++ bit = (matchByte & offs); ++ probLit = prob + offs + bit + symbol; ++ GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit) ++ } ++ while (symbol < 0x100); ++ } ++ dic[dicPos++] = (Byte)symbol; ++ processedPos++; ++ continue; ++ } ++ else ++ { ++ UPDATE_1(prob); ++ prob = probs + IsRep + state; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ state += kNumStates; ++ prob = probs + LenCoder; ++ } ++ else ++ { ++ UPDATE_1(prob); ++ if (checkDicSize == 0 && processedPos == 0) ++ return SZ_ERROR_DATA; ++ prob = probs + IsRepG0 + state; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; ++ dicPos++; ++ processedPos++; ++ state = state < kNumLitStates ? 9 : 11; ++ continue; ++ } ++ UPDATE_1(prob); ++ } ++ else ++ { ++ UInt32 distance; ++ UPDATE_1(prob); ++ prob = probs + IsRepG1 + state; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ distance = rep1; ++ } ++ else ++ { ++ UPDATE_1(prob); ++ prob = probs + IsRepG2 + state; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ distance = rep2; ++ } ++ else ++ { ++ UPDATE_1(prob); ++ distance = rep3; ++ rep3 = rep2; ++ } ++ rep2 = rep1; ++ } ++ rep1 = rep0; ++ rep0 = distance; ++ } ++ state = state < kNumLitStates ? 8 : 11; ++ prob = probs + RepLenCoder; ++ } ++ { ++ unsigned limit, offset; ++ CLzmaProb *probLen = prob + LenChoice; ++ IF_BIT_0(probLen) ++ { ++ UPDATE_0(probLen); ++ probLen = prob + LenLow + (posState << kLenNumLowBits); ++ offset = 0; ++ limit = (1 << kLenNumLowBits); ++ } ++ else ++ { ++ UPDATE_1(probLen); ++ probLen = prob + LenChoice2; ++ IF_BIT_0(probLen) ++ { ++ UPDATE_0(probLen); ++ probLen = prob + LenMid + (posState << kLenNumMidBits); ++ offset = kLenNumLowSymbols; ++ limit = (1 << kLenNumMidBits); ++ } ++ else ++ { ++ UPDATE_1(probLen); ++ probLen = prob + LenHigh; ++ offset = kLenNumLowSymbols + kLenNumMidSymbols; ++ limit = (1 << kLenNumHighBits); ++ } ++ } ++ TREE_DECODE(probLen, limit, len); ++ len += offset; ++ } ++ ++ if (state >= kNumStates) ++ { ++ UInt32 distance; ++ prob = probs + PosSlot + ++ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); ++ TREE_6_DECODE(prob, distance); ++ if (distance >= kStartPosModelIndex) ++ { ++ unsigned posSlot = (unsigned)distance; ++ int numDirectBits = (int)(((distance >> 1) - 1)); ++ distance = (2 | (distance & 1)); ++ if (posSlot < kEndPosModelIndex) ++ { ++ distance <<= numDirectBits; ++ prob = probs + SpecPos + distance - posSlot - 1; ++ { ++ UInt32 mask = 1; ++ unsigned i = 1; ++ do ++ { ++ GET_BIT2(prob + i, i, ; , distance |= mask); ++ mask <<= 1; ++ } ++ while (--numDirectBits != 0); ++ } ++ } ++ else ++ { ++ numDirectBits -= kNumAlignBits; ++ do ++ { ++ NORMALIZE ++ range >>= 1; ++ ++ { ++ UInt32 t; ++ code -= range; ++ t = (0 - ((UInt32)code >> 31)); /* (UInt32)((Int32)code >> 31) */ ++ distance = (distance << 1) + (t + 1); ++ code += range & t; ++ } ++ /* ++ distance <<= 1; ++ if (code >= range) ++ { ++ code -= range; ++ distance |= 1; ++ } ++ */ ++ } ++ while (--numDirectBits != 0); ++ prob = probs + Align; ++ distance <<= kNumAlignBits; ++ { ++ unsigned i = 1; ++ GET_BIT2(prob + i, i, ; , distance |= 1); ++ GET_BIT2(prob + i, i, ; , distance |= 2); ++ GET_BIT2(prob + i, i, ; , distance |= 4); ++ GET_BIT2(prob + i, i, ; , distance |= 8); ++ } ++ if (distance == (UInt32)0xFFFFFFFF) ++ { ++ len += kMatchSpecLenStart; ++ state -= kNumStates; ++ break; ++ } ++ } ++ } ++ rep3 = rep2; ++ rep2 = rep1; ++ rep1 = rep0; ++ rep0 = distance + 1; ++ if (checkDicSize == 0) ++ { ++ if (distance >= processedPos) ++ return SZ_ERROR_DATA; ++ } ++ else if (distance >= checkDicSize) ++ return SZ_ERROR_DATA; ++ state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; ++ } ++ ++ len += kMatchMinLen; ++ ++ if (limit == dicPos) ++ return SZ_ERROR_DATA; ++ { ++ SizeT rem = limit - dicPos; ++ unsigned curLen = ((rem < len) ? (unsigned)rem : len); ++ SizeT pos = (dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0); ++ ++ processedPos += curLen; ++ ++ len -= curLen; ++ if (pos + curLen <= dicBufSize) ++ { ++ Byte *dest = dic + dicPos; ++ ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos; ++ const Byte *lim = dest + curLen; ++ dicPos += curLen; ++ do ++ *(dest) = (Byte)*(dest + src); ++ while (++dest != lim); ++ } ++ else ++ { ++ do ++ { ++ dic[dicPos++] = dic[pos]; ++ if (++pos == dicBufSize) ++ pos = 0; ++ } ++ while (--curLen != 0); ++ } ++ } ++ } ++ } ++ while (dicPos < limit && buf < bufLimit); ++ NORMALIZE; ++ p->buf = buf; ++ p->range = range; ++ p->code = code; ++ p->remainLen = len; ++ p->dicPos = dicPos; ++ p->processedPos = processedPos; ++ p->reps[0] = rep0; ++ p->reps[1] = rep1; ++ p->reps[2] = rep2; ++ p->reps[3] = rep3; ++ p->state = state; ++ ++ return SZ_OK; ++} ++ ++static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit) ++{ ++ if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart) ++ { ++ Byte *dic = p->dic; ++ SizeT dicPos = p->dicPos; ++ SizeT dicBufSize = p->dicBufSize; ++ unsigned len = p->remainLen; ++ UInt32 rep0 = p->reps[0]; ++ if (limit - dicPos < len) ++ len = (unsigned)(limit - dicPos); ++ ++ if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len) ++ p->checkDicSize = p->prop.dicSize; ++ ++ p->processedPos += len; ++ p->remainLen -= len; ++ while (len-- != 0) ++ { ++ dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; ++ dicPos++; ++ } ++ p->dicPos = dicPos; ++ } ++} ++ ++static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit) ++{ ++ do ++ { ++ SizeT limit2 = limit; ++ if (p->checkDicSize == 0) ++ { ++ UInt32 rem = p->prop.dicSize - p->processedPos; ++ if (limit - p->dicPos > rem) ++ limit2 = p->dicPos + rem; ++ } ++ RINOK(LzmaDec_DecodeReal(p, limit2, bufLimit)); ++ if (p->processedPos >= p->prop.dicSize) ++ p->checkDicSize = p->prop.dicSize; ++ LzmaDec_WriteRem(p, limit); ++ } ++ while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart); ++ ++ if (p->remainLen > kMatchSpecLenStart) ++ { ++ p->remainLen = kMatchSpecLenStart; ++ } ++ return 0; ++} ++ ++typedef enum ++{ ++ DUMMY_ERROR, /* unexpected end of input stream */ ++ DUMMY_LIT, ++ DUMMY_MATCH, ++ DUMMY_REP ++} ELzmaDummy; ++ ++static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inSize) ++{ ++ UInt32 range = p->range; ++ UInt32 code = p->code; ++ const Byte *bufLimit = buf + inSize; ++ CLzmaProb *probs = p->probs; ++ unsigned state = p->state; ++ ELzmaDummy res; ++ ++ { ++ CLzmaProb *prob; ++ UInt32 bound; ++ unsigned ttt; ++ unsigned posState = (p->processedPos) & ((1 << p->prop.pb) - 1); ++ ++ prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK ++ ++ /* if (bufLimit - buf >= 7) return DUMMY_LIT; */ ++ ++ prob = probs + Literal; ++ if (p->checkDicSize != 0 || p->processedPos != 0) ++ prob += (LZMA_LIT_SIZE * ++ ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) + ++ (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc)))); ++ ++ if (state < kNumLitStates) ++ { ++ unsigned symbol = 1; ++ do { GET_BIT_CHECK(prob + symbol, symbol) } while (symbol < 0x100); ++ } ++ else ++ { ++ unsigned matchByte = p->dic[p->dicPos - p->reps[0] + ++ ((p->dicPos < p->reps[0]) ? p->dicBufSize : 0)]; ++ unsigned offs = 0x100; ++ unsigned symbol = 1; ++ do ++ { ++ unsigned bit; ++ CLzmaProb *probLit; ++ matchByte <<= 1; ++ bit = (matchByte & offs); ++ probLit = prob + offs + bit + symbol; ++ GET_BIT2_CHECK(probLit, symbol, offs &= ~bit, offs &= bit) ++ } ++ while (symbol < 0x100); ++ } ++ res = DUMMY_LIT; ++ } ++ else ++ { ++ unsigned len; ++ UPDATE_1_CHECK; ++ ++ prob = probs + IsRep + state; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ state = 0; ++ prob = probs + LenCoder; ++ res = DUMMY_MATCH; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ res = DUMMY_REP; ++ prob = probs + IsRepG0 + state; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ NORMALIZE_CHECK; ++ return DUMMY_REP; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ } ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ prob = probs + IsRepG1 + state; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ prob = probs + IsRepG2 + state; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ } ++ } ++ } ++ state = kNumStates; ++ prob = probs + RepLenCoder; ++ } ++ { ++ unsigned limit, offset; ++ CLzmaProb *probLen = prob + LenChoice; ++ IF_BIT_0_CHECK(probLen) ++ { ++ UPDATE_0_CHECK; ++ probLen = prob + LenLow + (posState << kLenNumLowBits); ++ offset = 0; ++ limit = 1 << kLenNumLowBits; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ probLen = prob + LenChoice2; ++ IF_BIT_0_CHECK(probLen) ++ { ++ UPDATE_0_CHECK; ++ probLen = prob + LenMid + (posState << kLenNumMidBits); ++ offset = kLenNumLowSymbols; ++ limit = 1 << kLenNumMidBits; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ probLen = prob + LenHigh; ++ offset = kLenNumLowSymbols + kLenNumMidSymbols; ++ limit = 1 << kLenNumHighBits; ++ } ++ } ++ TREE_DECODE_CHECK(probLen, limit, len); ++ len += offset; ++ } ++ ++ if (state < 4) ++ { ++ unsigned posSlot; ++ prob = probs + PosSlot + ++ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << ++ kNumPosSlotBits); ++ TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot); ++ if (posSlot >= kStartPosModelIndex) ++ { ++ int numDirectBits = ((posSlot >> 1) - 1); ++ ++ /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */ ++ ++ if (posSlot < kEndPosModelIndex) ++ { ++ prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1; ++ } ++ else ++ { ++ numDirectBits -= kNumAlignBits; ++ do ++ { ++ NORMALIZE_CHECK ++ range >>= 1; ++ code -= range & (((code - range) >> 31) - 1); ++ /* if (code >= range) code -= range; */ ++ } ++ while (--numDirectBits != 0); ++ prob = probs + Align; ++ numDirectBits = kNumAlignBits; ++ } ++ { ++ unsigned i = 1; ++ do ++ { ++ GET_BIT_CHECK(prob + i, i); ++ } ++ while (--numDirectBits != 0); ++ } ++ } ++ } ++ } ++ } ++ NORMALIZE_CHECK; ++ return res; ++} ++ ++ ++static void LzmaDec_InitRc(CLzmaDec *p, const Byte *data) ++{ ++ p->code = ((UInt32)data[1] << 24) | ((UInt32)data[2] << 16) | ((UInt32)data[3] << 8) | ((UInt32)data[4]); ++ p->range = 0xFFFFFFFF; ++ p->needFlush = 0; ++} ++ ++void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) ++{ ++ p->needFlush = 1; ++ p->remainLen = 0; ++ p->tempBufSize = 0; ++ ++ if (initDic) ++ { ++ p->processedPos = 0; ++ p->checkDicSize = 0; ++ p->needInitState = 1; ++ } ++ if (initState) ++ p->needInitState = 1; ++} ++ ++void LzmaDec_Init(CLzmaDec *p) ++{ ++ p->dicPos = 0; ++ LzmaDec_InitDicAndState(p, True, True); ++} ++ ++static void LzmaDec_InitStateReal(CLzmaDec *p) ++{ ++ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (p->prop.lc + p->prop.lp)); ++ UInt32 i; ++ CLzmaProb *probs = p->probs; ++ for (i = 0; i < numProbs; i++) ++ probs[i] = kBitModelTotal >> 1; ++ p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1; ++ p->state = 0; ++ p->needInitState = 0; ++} ++ ++SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, ++ ELzmaFinishMode finishMode, ELzmaStatus *status) ++{ ++ SizeT inSize = *srcLen; ++ (*srcLen) = 0; ++ LzmaDec_WriteRem(p, dicLimit); ++ ++ *status = LZMA_STATUS_NOT_SPECIFIED; ++ ++ while (p->remainLen != kMatchSpecLenStart) ++ { ++ int checkEndMarkNow; ++ ++ if (p->needFlush != 0) ++ { ++ for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--) ++ p->tempBuf[p->tempBufSize++] = *src++; ++ if (p->tempBufSize < RC_INIT_SIZE) ++ { ++ *status = LZMA_STATUS_NEEDS_MORE_INPUT; ++ return SZ_OK; ++ } ++ if (p->tempBuf[0] != 0) ++ return SZ_ERROR_DATA; ++ ++ LzmaDec_InitRc(p, p->tempBuf); ++ p->tempBufSize = 0; ++ } ++ ++ checkEndMarkNow = 0; ++ if (p->dicPos >= dicLimit) ++ { ++ if (p->remainLen == 0 && p->code == 0) ++ { ++ *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK; ++ return SZ_OK; ++ } ++ if (finishMode == LZMA_FINISH_ANY) ++ { ++ *status = LZMA_STATUS_NOT_FINISHED; ++ return SZ_OK; ++ } ++ if (p->remainLen != 0) ++ { ++ *status = LZMA_STATUS_NOT_FINISHED; ++ return SZ_ERROR_DATA; ++ } ++ checkEndMarkNow = 1; ++ } ++ ++ if (p->needInitState) ++ LzmaDec_InitStateReal(p); ++ ++ if (p->tempBufSize == 0) ++ { ++ SizeT processed; ++ const Byte *bufLimit; ++ if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) ++ { ++ int dummyRes = LzmaDec_TryDummy(p, src, inSize); ++ if (dummyRes == DUMMY_ERROR) ++ { ++ memcpy(p->tempBuf, src, inSize); ++ p->tempBufSize = (unsigned)inSize; ++ (*srcLen) += inSize; ++ *status = LZMA_STATUS_NEEDS_MORE_INPUT; ++ return SZ_OK; ++ } ++ if (checkEndMarkNow && dummyRes != DUMMY_MATCH) ++ { ++ *status = LZMA_STATUS_NOT_FINISHED; ++ return SZ_ERROR_DATA; ++ } ++ bufLimit = src; ++ } ++ else ++ bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX; ++ p->buf = src; ++ if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0) ++ return SZ_ERROR_DATA; ++ processed = (SizeT)(p->buf - src); ++ (*srcLen) += processed; ++ src += processed; ++ inSize -= processed; ++ } ++ else ++ { ++ unsigned rem = p->tempBufSize, lookAhead = 0; ++ while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize) ++ p->tempBuf[rem++] = src[lookAhead++]; ++ p->tempBufSize = rem; ++ if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) ++ { ++ int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, rem); ++ if (dummyRes == DUMMY_ERROR) ++ { ++ (*srcLen) += lookAhead; ++ *status = LZMA_STATUS_NEEDS_MORE_INPUT; ++ return SZ_OK; ++ } ++ if (checkEndMarkNow && dummyRes != DUMMY_MATCH) ++ { ++ *status = LZMA_STATUS_NOT_FINISHED; ++ return SZ_ERROR_DATA; ++ } ++ } ++ p->buf = p->tempBuf; ++ if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0) ++ return SZ_ERROR_DATA; ++ lookAhead -= (rem - (unsigned)(p->buf - p->tempBuf)); ++ (*srcLen) += lookAhead; ++ src += lookAhead; ++ inSize -= lookAhead; ++ p->tempBufSize = 0; ++ } ++ } ++ if (p->code == 0) ++ *status = LZMA_STATUS_FINISHED_WITH_MARK; ++ return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA; ++} ++ ++SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) ++{ ++ SizeT outSize = *destLen; ++ SizeT inSize = *srcLen; ++ *srcLen = *destLen = 0; ++ for (;;) ++ { ++ SizeT inSizeCur = inSize, outSizeCur, dicPos; ++ ELzmaFinishMode curFinishMode; ++ SRes res; ++ if (p->dicPos == p->dicBufSize) ++ p->dicPos = 0; ++ dicPos = p->dicPos; ++ if (outSize > p->dicBufSize - dicPos) ++ { ++ outSizeCur = p->dicBufSize; ++ curFinishMode = LZMA_FINISH_ANY; ++ } ++ else ++ { ++ outSizeCur = dicPos + outSize; ++ curFinishMode = finishMode; ++ } ++ ++ res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status); ++ src += inSizeCur; ++ inSize -= inSizeCur; ++ *srcLen += inSizeCur; ++ outSizeCur = p->dicPos - dicPos; ++ memcpy(dest, p->dic + dicPos, outSizeCur); ++ dest += outSizeCur; ++ outSize -= outSizeCur; ++ *destLen += outSizeCur; ++ if (res != 0) ++ return res; ++ if (outSizeCur == 0 || outSize == 0) ++ return SZ_OK; ++ } ++} ++ ++void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->probs); ++ p->probs = 0; ++} ++ ++static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->dic); ++ p->dic = 0; ++} ++ ++void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc) ++{ ++ LzmaDec_FreeProbs(p, alloc); ++ LzmaDec_FreeDict(p, alloc); ++} ++ ++SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size) ++{ ++ UInt32 dicSize; ++ Byte d; ++ ++ if (size < LZMA_PROPS_SIZE) ++ return SZ_ERROR_UNSUPPORTED; ++ else ++ dicSize = data[1] | ((UInt32)data[2] << 8) | ((UInt32)data[3] << 16) | ((UInt32)data[4] << 24); ++ ++ if (dicSize < LZMA_DIC_MIN) ++ dicSize = LZMA_DIC_MIN; ++ p->dicSize = dicSize; ++ ++ d = data[0]; ++ if (d >= (9 * 5 * 5)) ++ return SZ_ERROR_UNSUPPORTED; ++ ++ p->lc = d % 9; ++ d /= 9; ++ p->pb = d / 5; ++ p->lp = d % 5; ++ ++ return SZ_OK; ++} ++ ++static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc) ++{ ++ UInt32 numProbs = LzmaProps_GetNumProbs(propNew); ++ if (p->probs == 0 || numProbs != p->numProbs) ++ { ++ LzmaDec_FreeProbs(p, alloc); ++ p->probs = (CLzmaProb *)alloc->Alloc(alloc, numProbs * sizeof(CLzmaProb)); ++ p->numProbs = numProbs; ++ if (p->probs == 0) ++ return SZ_ERROR_MEM; ++ } ++ return SZ_OK; ++} ++ ++SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) ++{ ++ CLzmaProps propNew; ++ RINOK(LzmaProps_Decode(&propNew, props, propsSize)); ++ RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); ++ p->prop = propNew; ++ return SZ_OK; ++} ++ ++SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) ++{ ++ CLzmaProps propNew; ++ SizeT dicBufSize; ++ RINOK(LzmaProps_Decode(&propNew, props, propsSize)); ++ RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); ++ dicBufSize = propNew.dicSize; ++ if (p->dic == 0 || dicBufSize != p->dicBufSize) ++ { ++ LzmaDec_FreeDict(p, alloc); ++ p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize); ++ if (p->dic == 0) ++ { ++ LzmaDec_FreeProbs(p, alloc); ++ return SZ_ERROR_MEM; ++ } ++ } ++ p->dicBufSize = dicBufSize; ++ p->prop = propNew; ++ return SZ_OK; ++} ++ ++SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ++ const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, ++ ELzmaStatus *status, ISzAlloc *alloc) ++{ ++ CLzmaDec p; ++ SRes res; ++ SizeT inSize = *srcLen; ++ SizeT outSize = *destLen; ++ *srcLen = *destLen = 0; ++ if (inSize < RC_INIT_SIZE) ++ return SZ_ERROR_INPUT_EOF; ++ ++ LzmaDec_Construct(&p); ++ res = LzmaDec_AllocateProbs(&p, propData, propSize, alloc); ++ if (res != 0) ++ return res; ++ p.dic = dest; ++ p.dicBufSize = outSize; ++ ++ LzmaDec_Init(&p); ++ ++ *srcLen = inSize; ++ res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status); ++ ++ if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT) ++ res = SZ_ERROR_INPUT_EOF; ++ ++ (*destLen) = p.dicPos; ++ LzmaDec_FreeProbs(&p, alloc); ++ return res; ++} +--- /dev/null ++++ b/lib/lzma/LzmaEnc.c +@@ -0,0 +1,2271 @@ ++/* LzmaEnc.c -- LZMA Encoder ++2009-11-24 : Igor Pavlov : Public domain */ ++ ++#include ++ ++/* #define SHOW_STAT */ ++/* #define SHOW_STAT2 */ ++ ++#if defined(SHOW_STAT) || defined(SHOW_STAT2) ++#include ++#endif ++ ++#include "LzmaEnc.h" ++ ++/* disable MT */ ++#define _7ZIP_ST ++ ++#include "LzFind.h" ++#ifndef _7ZIP_ST ++#include "LzFindMt.h" ++#endif ++ ++#ifdef SHOW_STAT ++static int ttt = 0; ++#endif ++ ++#define kBlockSizeMax ((1 << LZMA_NUM_BLOCK_SIZE_BITS) - 1) ++ ++#define kBlockSize (9 << 10) ++#define kUnpackBlockSize (1 << 18) ++#define kMatchArraySize (1 << 21) ++#define kMatchRecordMaxSize ((LZMA_MATCH_LEN_MAX * 2 + 3) * LZMA_MATCH_LEN_MAX) ++ ++#define kNumMaxDirectBits (31) ++ ++#define kNumTopBits 24 ++#define kTopValue ((UInt32)1 << kNumTopBits) ++ ++#define kNumBitModelTotalBits 11 ++#define kBitModelTotal (1 << kNumBitModelTotalBits) ++#define kNumMoveBits 5 ++#define kProbInitValue (kBitModelTotal >> 1) ++ ++#define kNumMoveReducingBits 4 ++#define kNumBitPriceShiftBits 4 ++#define kBitPrice (1 << kNumBitPriceShiftBits) ++ ++void LzmaEncProps_Init(CLzmaEncProps *p) ++{ ++ p->level = 5; ++ p->dictSize = p->mc = 0; ++ p->lc = p->lp = p->pb = p->algo = p->fb = p->btMode = p->numHashBytes = p->numThreads = -1; ++ p->writeEndMark = 0; ++} ++ ++void LzmaEncProps_Normalize(CLzmaEncProps *p) ++{ ++ int level = p->level; ++ if (level < 0) level = 5; ++ p->level = level; ++ if (p->dictSize == 0) p->dictSize = (level <= 5 ? (1 << (level * 2 + 14)) : (level == 6 ? (1 << 25) : (1 << 26))); ++ if (p->lc < 0) p->lc = 3; ++ if (p->lp < 0) p->lp = 0; ++ if (p->pb < 0) p->pb = 2; ++ if (p->algo < 0) p->algo = (level < 5 ? 0 : 1); ++ if (p->fb < 0) p->fb = (level < 7 ? 32 : 64); ++ if (p->btMode < 0) p->btMode = (p->algo == 0 ? 0 : 1); ++ if (p->numHashBytes < 0) p->numHashBytes = 4; ++ if (p->mc == 0) p->mc = (16 + (p->fb >> 1)) >> (p->btMode ? 0 : 1); ++ if (p->numThreads < 0) ++ p->numThreads = ++ #ifndef _7ZIP_ST ++ ((p->btMode && p->algo) ? 2 : 1); ++ #else ++ 1; ++ #endif ++} ++ ++UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2) ++{ ++ CLzmaEncProps props = *props2; ++ LzmaEncProps_Normalize(&props); ++ return props.dictSize; ++} ++ ++/* #define LZMA_LOG_BSR */ ++/* Define it for Intel's CPU */ ++ ++ ++#ifdef LZMA_LOG_BSR ++ ++#define kDicLogSizeMaxCompress 30 ++ ++#define BSR2_RET(pos, res) { unsigned long i; _BitScanReverse(&i, (pos)); res = (i + i) + ((pos >> (i - 1)) & 1); } ++ ++UInt32 GetPosSlot1(UInt32 pos) ++{ ++ UInt32 res; ++ BSR2_RET(pos, res); ++ return res; ++} ++#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } ++#define GetPosSlot(pos, res) { if (pos < 2) res = pos; else BSR2_RET(pos, res); } ++ ++#else ++ ++#define kNumLogBits (9 + (int)sizeof(size_t) / 2) ++#define kDicLogSizeMaxCompress ((kNumLogBits - 1) * 2 + 7) ++ ++void LzmaEnc_FastPosInit(Byte *g_FastPos) ++{ ++ int c = 2, slotFast; ++ g_FastPos[0] = 0; ++ g_FastPos[1] = 1; ++ ++ for (slotFast = 2; slotFast < kNumLogBits * 2; slotFast++) ++ { ++ UInt32 k = (1 << ((slotFast >> 1) - 1)); ++ UInt32 j; ++ for (j = 0; j < k; j++, c++) ++ g_FastPos[c] = (Byte)slotFast; ++ } ++} ++ ++#define BSR2_RET(pos, res) { UInt32 i = 6 + ((kNumLogBits - 1) & \ ++ (0 - (((((UInt32)1 << (kNumLogBits + 6)) - 1) - pos) >> 31))); \ ++ res = p->g_FastPos[pos >> i] + (i * 2); } ++/* ++#define BSR2_RET(pos, res) { res = (pos < (1 << (kNumLogBits + 6))) ? \ ++ p->g_FastPos[pos >> 6] + 12 : \ ++ p->g_FastPos[pos >> (6 + kNumLogBits - 1)] + (6 + (kNumLogBits - 1)) * 2; } ++*/ ++ ++#define GetPosSlot1(pos) p->g_FastPos[pos] ++#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } ++#define GetPosSlot(pos, res) { if (pos < kNumFullDistances) res = p->g_FastPos[pos]; else BSR2_RET(pos, res); } ++ ++#endif ++ ++ ++#define LZMA_NUM_REPS 4 ++ ++typedef unsigned CState; ++ ++typedef struct ++{ ++ UInt32 price; ++ ++ CState state; ++ int prev1IsChar; ++ int prev2; ++ ++ UInt32 posPrev2; ++ UInt32 backPrev2; ++ ++ UInt32 posPrev; ++ UInt32 backPrev; ++ UInt32 backs[LZMA_NUM_REPS]; ++} COptimal; ++ ++#define kNumOpts (1 << 12) ++ ++#define kNumLenToPosStates 4 ++#define kNumPosSlotBits 6 ++#define kDicLogSizeMin 0 ++#define kDicLogSizeMax 32 ++#define kDistTableSizeMax (kDicLogSizeMax * 2) ++ ++ ++#define kNumAlignBits 4 ++#define kAlignTableSize (1 << kNumAlignBits) ++#define kAlignMask (kAlignTableSize - 1) ++ ++#define kStartPosModelIndex 4 ++#define kEndPosModelIndex 14 ++#define kNumPosModels (kEndPosModelIndex - kStartPosModelIndex) ++ ++#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) ++ ++#ifdef _LZMA_PROB32 ++#define CLzmaProb UInt32 ++#else ++#define CLzmaProb UInt16 ++#endif ++ ++#define LZMA_PB_MAX 4 ++#define LZMA_LC_MAX 8 ++#define LZMA_LP_MAX 4 ++ ++#define LZMA_NUM_PB_STATES_MAX (1 << LZMA_PB_MAX) ++ ++ ++#define kLenNumLowBits 3 ++#define kLenNumLowSymbols (1 << kLenNumLowBits) ++#define kLenNumMidBits 3 ++#define kLenNumMidSymbols (1 << kLenNumMidBits) ++#define kLenNumHighBits 8 ++#define kLenNumHighSymbols (1 << kLenNumHighBits) ++ ++#define kLenNumSymbolsTotal (kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) ++ ++#define LZMA_MATCH_LEN_MIN 2 ++#define LZMA_MATCH_LEN_MAX (LZMA_MATCH_LEN_MIN + kLenNumSymbolsTotal - 1) ++ ++#define kNumStates 12 ++ ++typedef struct ++{ ++ CLzmaProb choice; ++ CLzmaProb choice2; ++ CLzmaProb low[LZMA_NUM_PB_STATES_MAX << kLenNumLowBits]; ++ CLzmaProb mid[LZMA_NUM_PB_STATES_MAX << kLenNumMidBits]; ++ CLzmaProb high[kLenNumHighSymbols]; ++} CLenEnc; ++ ++typedef struct ++{ ++ CLenEnc p; ++ UInt32 prices[LZMA_NUM_PB_STATES_MAX][kLenNumSymbolsTotal]; ++ UInt32 tableSize; ++ UInt32 counters[LZMA_NUM_PB_STATES_MAX]; ++} CLenPriceEnc; ++ ++typedef struct ++{ ++ UInt32 range; ++ Byte cache; ++ UInt64 low; ++ UInt64 cacheSize; ++ Byte *buf; ++ Byte *bufLim; ++ Byte *bufBase; ++ ISeqOutStream *outStream; ++ UInt64 processed; ++ SRes res; ++} CRangeEnc; ++ ++typedef struct ++{ ++ CLzmaProb *litProbs; ++ ++ CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; ++ CLzmaProb isRep[kNumStates]; ++ CLzmaProb isRepG0[kNumStates]; ++ CLzmaProb isRepG1[kNumStates]; ++ CLzmaProb isRepG2[kNumStates]; ++ CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; ++ ++ CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; ++ CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; ++ CLzmaProb posAlignEncoder[1 << kNumAlignBits]; ++ ++ CLenPriceEnc lenEnc; ++ CLenPriceEnc repLenEnc; ++ ++ UInt32 reps[LZMA_NUM_REPS]; ++ UInt32 state; ++} CSaveState; ++ ++typedef struct ++{ ++ IMatchFinder matchFinder; ++ void *matchFinderObj; ++ ++ #ifndef _7ZIP_ST ++ Bool mtMode; ++ CMatchFinderMt matchFinderMt; ++ #endif ++ ++ CMatchFinder matchFinderBase; ++ ++ #ifndef _7ZIP_ST ++ Byte pad[128]; ++ #endif ++ ++ UInt32 optimumEndIndex; ++ UInt32 optimumCurrentIndex; ++ ++ UInt32 longestMatchLength; ++ UInt32 numPairs; ++ UInt32 numAvail; ++ COptimal opt[kNumOpts]; ++ ++ #ifndef LZMA_LOG_BSR ++ Byte g_FastPos[1 << kNumLogBits]; ++ #endif ++ ++ UInt32 ProbPrices[kBitModelTotal >> kNumMoveReducingBits]; ++ UInt32 matches[LZMA_MATCH_LEN_MAX * 2 + 2 + 1]; ++ UInt32 numFastBytes; ++ UInt32 additionalOffset; ++ UInt32 reps[LZMA_NUM_REPS]; ++ UInt32 state; ++ ++ UInt32 posSlotPrices[kNumLenToPosStates][kDistTableSizeMax]; ++ UInt32 distancesPrices[kNumLenToPosStates][kNumFullDistances]; ++ UInt32 alignPrices[kAlignTableSize]; ++ UInt32 alignPriceCount; ++ ++ UInt32 distTableSize; ++ ++ unsigned lc, lp, pb; ++ unsigned lpMask, pbMask; ++ ++ CLzmaProb *litProbs; ++ ++ CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; ++ CLzmaProb isRep[kNumStates]; ++ CLzmaProb isRepG0[kNumStates]; ++ CLzmaProb isRepG1[kNumStates]; ++ CLzmaProb isRepG2[kNumStates]; ++ CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; ++ ++ CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; ++ CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; ++ CLzmaProb posAlignEncoder[1 << kNumAlignBits]; ++ ++ CLenPriceEnc lenEnc; ++ CLenPriceEnc repLenEnc; ++ ++ unsigned lclp; ++ ++ Bool fastMode; ++ ++ CRangeEnc rc; ++ ++ Bool writeEndMark; ++ UInt64 nowPos64; ++ UInt32 matchPriceCount; ++ Bool finished; ++ Bool multiThread; ++ ++ SRes result; ++ UInt32 dictSize; ++ UInt32 matchFinderCycles; ++ ++ int needInit; ++ ++ CSaveState saveState; ++} CLzmaEnc; ++ ++void LzmaEnc_SaveState(CLzmaEncHandle pp) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ CSaveState *dest = &p->saveState; ++ int i; ++ dest->lenEnc = p->lenEnc; ++ dest->repLenEnc = p->repLenEnc; ++ dest->state = p->state; ++ ++ for (i = 0; i < kNumStates; i++) ++ { ++ memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); ++ memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); ++ } ++ for (i = 0; i < kNumLenToPosStates; i++) ++ memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); ++ memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); ++ memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); ++ memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); ++ memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); ++ memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); ++ memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); ++ memcpy(dest->reps, p->reps, sizeof(p->reps)); ++ memcpy(dest->litProbs, p->litProbs, (0x300 << p->lclp) * sizeof(CLzmaProb)); ++} ++ ++void LzmaEnc_RestoreState(CLzmaEncHandle pp) ++{ ++ CLzmaEnc *dest = (CLzmaEnc *)pp; ++ const CSaveState *p = &dest->saveState; ++ int i; ++ dest->lenEnc = p->lenEnc; ++ dest->repLenEnc = p->repLenEnc; ++ dest->state = p->state; ++ ++ for (i = 0; i < kNumStates; i++) ++ { ++ memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); ++ memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); ++ } ++ for (i = 0; i < kNumLenToPosStates; i++) ++ memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); ++ memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); ++ memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); ++ memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); ++ memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); ++ memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); ++ memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); ++ memcpy(dest->reps, p->reps, sizeof(p->reps)); ++ memcpy(dest->litProbs, p->litProbs, (0x300 << dest->lclp) * sizeof(CLzmaProb)); ++} ++ ++SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ CLzmaEncProps props = *props2; ++ LzmaEncProps_Normalize(&props); ++ ++ if (props.lc > LZMA_LC_MAX || props.lp > LZMA_LP_MAX || props.pb > LZMA_PB_MAX || ++ props.dictSize > (1 << kDicLogSizeMaxCompress) || props.dictSize > (1 << 30)) ++ return SZ_ERROR_PARAM; ++ p->dictSize = props.dictSize; ++ p->matchFinderCycles = props.mc; ++ { ++ unsigned fb = props.fb; ++ if (fb < 5) ++ fb = 5; ++ if (fb > LZMA_MATCH_LEN_MAX) ++ fb = LZMA_MATCH_LEN_MAX; ++ p->numFastBytes = fb; ++ } ++ p->lc = props.lc; ++ p->lp = props.lp; ++ p->pb = props.pb; ++ p->fastMode = (props.algo == 0); ++ p->matchFinderBase.btMode = props.btMode; ++ { ++ UInt32 numHashBytes = 4; ++ if (props.btMode) ++ { ++ if (props.numHashBytes < 2) ++ numHashBytes = 2; ++ else if (props.numHashBytes < 4) ++ numHashBytes = props.numHashBytes; ++ } ++ p->matchFinderBase.numHashBytes = numHashBytes; ++ } ++ ++ p->matchFinderBase.cutValue = props.mc; ++ ++ p->writeEndMark = props.writeEndMark; ++ ++ #ifndef _7ZIP_ST ++ /* ++ if (newMultiThread != _multiThread) ++ { ++ ReleaseMatchFinder(); ++ _multiThread = newMultiThread; ++ } ++ */ ++ p->multiThread = (props.numThreads > 1); ++ #endif ++ ++ return SZ_OK; ++} ++ ++static const int kLiteralNextStates[kNumStates] = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5}; ++static const int kMatchNextStates[kNumStates] = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10}; ++static const int kRepNextStates[kNumStates] = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11}; ++static const int kShortRepNextStates[kNumStates]= {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11}; ++ ++#define IsCharState(s) ((s) < 7) ++ ++#define GetLenToPosState(len) (((len) < kNumLenToPosStates + 1) ? (len) - 2 : kNumLenToPosStates - 1) ++ ++#define kInfinityPrice (1 << 30) ++ ++static void RangeEnc_Construct(CRangeEnc *p) ++{ ++ p->outStream = 0; ++ p->bufBase = 0; ++} ++ ++#define RangeEnc_GetProcessed(p) ((p)->processed + ((p)->buf - (p)->bufBase) + (p)->cacheSize) ++ ++#define RC_BUF_SIZE (1 << 16) ++static int RangeEnc_Alloc(CRangeEnc *p, ISzAlloc *alloc) ++{ ++ if (p->bufBase == 0) ++ { ++ p->bufBase = (Byte *)alloc->Alloc(alloc, RC_BUF_SIZE); ++ if (p->bufBase == 0) ++ return 0; ++ p->bufLim = p->bufBase + RC_BUF_SIZE; ++ } ++ return 1; ++} ++ ++static void RangeEnc_Free(CRangeEnc *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->bufBase); ++ p->bufBase = 0; ++} ++ ++static void RangeEnc_Init(CRangeEnc *p) ++{ ++ /* Stream.Init(); */ ++ p->low = 0; ++ p->range = 0xFFFFFFFF; ++ p->cacheSize = 1; ++ p->cache = 0; ++ ++ p->buf = p->bufBase; ++ ++ p->processed = 0; ++ p->res = SZ_OK; ++} ++ ++static void RangeEnc_FlushStream(CRangeEnc *p) ++{ ++ size_t num; ++ if (p->res != SZ_OK) ++ return; ++ num = p->buf - p->bufBase; ++ if (num != p->outStream->Write(p->outStream, p->bufBase, num)) ++ p->res = SZ_ERROR_WRITE; ++ p->processed += num; ++ p->buf = p->bufBase; ++} ++ ++static void MY_FAST_CALL RangeEnc_ShiftLow(CRangeEnc *p) ++{ ++ if ((UInt32)p->low < (UInt32)0xFF000000 || (int)(p->low >> 32) != 0) ++ { ++ Byte temp = p->cache; ++ do ++ { ++ Byte *buf = p->buf; ++ *buf++ = (Byte)(temp + (Byte)(p->low >> 32)); ++ p->buf = buf; ++ if (buf == p->bufLim) ++ RangeEnc_FlushStream(p); ++ temp = 0xFF; ++ } ++ while (--p->cacheSize != 0); ++ p->cache = (Byte)((UInt32)p->low >> 24); ++ } ++ p->cacheSize++; ++ p->low = (UInt32)p->low << 8; ++} ++ ++static void RangeEnc_FlushData(CRangeEnc *p) ++{ ++ int i; ++ for (i = 0; i < 5; i++) ++ RangeEnc_ShiftLow(p); ++} ++ ++static void RangeEnc_EncodeDirectBits(CRangeEnc *p, UInt32 value, int numBits) ++{ ++ do ++ { ++ p->range >>= 1; ++ p->low += p->range & (0 - ((value >> --numBits) & 1)); ++ if (p->range < kTopValue) ++ { ++ p->range <<= 8; ++ RangeEnc_ShiftLow(p); ++ } ++ } ++ while (numBits != 0); ++} ++ ++static void RangeEnc_EncodeBit(CRangeEnc *p, CLzmaProb *prob, UInt32 symbol) ++{ ++ UInt32 ttt = *prob; ++ UInt32 newBound = (p->range >> kNumBitModelTotalBits) * ttt; ++ if (symbol == 0) ++ { ++ p->range = newBound; ++ ttt += (kBitModelTotal - ttt) >> kNumMoveBits; ++ } ++ else ++ { ++ p->low += newBound; ++ p->range -= newBound; ++ ttt -= ttt >> kNumMoveBits; ++ } ++ *prob = (CLzmaProb)ttt; ++ if (p->range < kTopValue) ++ { ++ p->range <<= 8; ++ RangeEnc_ShiftLow(p); ++ } ++} ++ ++static void LitEnc_Encode(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol) ++{ ++ symbol |= 0x100; ++ do ++ { ++ RangeEnc_EncodeBit(p, probs + (symbol >> 8), (symbol >> 7) & 1); ++ symbol <<= 1; ++ } ++ while (symbol < 0x10000); ++} ++ ++static void LitEnc_EncodeMatched(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol, UInt32 matchByte) ++{ ++ UInt32 offs = 0x100; ++ symbol |= 0x100; ++ do ++ { ++ matchByte <<= 1; ++ RangeEnc_EncodeBit(p, probs + (offs + (matchByte & offs) + (symbol >> 8)), (symbol >> 7) & 1); ++ symbol <<= 1; ++ offs &= ~(matchByte ^ symbol); ++ } ++ while (symbol < 0x10000); ++} ++ ++void LzmaEnc_InitPriceTables(UInt32 *ProbPrices) ++{ ++ UInt32 i; ++ for (i = (1 << kNumMoveReducingBits) / 2; i < kBitModelTotal; i += (1 << kNumMoveReducingBits)) ++ { ++ const int kCyclesBits = kNumBitPriceShiftBits; ++ UInt32 w = i; ++ UInt32 bitCount = 0; ++ int j; ++ for (j = 0; j < kCyclesBits; j++) ++ { ++ w = w * w; ++ bitCount <<= 1; ++ while (w >= ((UInt32)1 << 16)) ++ { ++ w >>= 1; ++ bitCount++; ++ } ++ } ++ ProbPrices[i >> kNumMoveReducingBits] = ((kNumBitModelTotalBits << kCyclesBits) - 15 - bitCount); ++ } ++} ++ ++ ++#define GET_PRICE(prob, symbol) \ ++ p->ProbPrices[((prob) ^ (((-(int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; ++ ++#define GET_PRICEa(prob, symbol) \ ++ ProbPrices[((prob) ^ ((-((int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; ++ ++#define GET_PRICE_0(prob) p->ProbPrices[(prob) >> kNumMoveReducingBits] ++#define GET_PRICE_1(prob) p->ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] ++ ++#define GET_PRICE_0a(prob) ProbPrices[(prob) >> kNumMoveReducingBits] ++#define GET_PRICE_1a(prob) ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] ++ ++static UInt32 LitEnc_GetPrice(const CLzmaProb *probs, UInt32 symbol, UInt32 *ProbPrices) ++{ ++ UInt32 price = 0; ++ symbol |= 0x100; ++ do ++ { ++ price += GET_PRICEa(probs[symbol >> 8], (symbol >> 7) & 1); ++ symbol <<= 1; ++ } ++ while (symbol < 0x10000); ++ return price; ++} ++ ++static UInt32 LitEnc_GetPriceMatched(const CLzmaProb *probs, UInt32 symbol, UInt32 matchByte, UInt32 *ProbPrices) ++{ ++ UInt32 price = 0; ++ UInt32 offs = 0x100; ++ symbol |= 0x100; ++ do ++ { ++ matchByte <<= 1; ++ price += GET_PRICEa(probs[offs + (matchByte & offs) + (symbol >> 8)], (symbol >> 7) & 1); ++ symbol <<= 1; ++ offs &= ~(matchByte ^ symbol); ++ } ++ while (symbol < 0x10000); ++ return price; ++} ++ ++ ++static void RcTree_Encode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol) ++{ ++ UInt32 m = 1; ++ int i; ++ for (i = numBitLevels; i != 0;) ++ { ++ UInt32 bit; ++ i--; ++ bit = (symbol >> i) & 1; ++ RangeEnc_EncodeBit(rc, probs + m, bit); ++ m = (m << 1) | bit; ++ } ++} ++ ++static void RcTree_ReverseEncode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol) ++{ ++ UInt32 m = 1; ++ int i; ++ for (i = 0; i < numBitLevels; i++) ++ { ++ UInt32 bit = symbol & 1; ++ RangeEnc_EncodeBit(rc, probs + m, bit); ++ m = (m << 1) | bit; ++ symbol >>= 1; ++ } ++} ++ ++static UInt32 RcTree_GetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices) ++{ ++ UInt32 price = 0; ++ symbol |= (1 << numBitLevels); ++ while (symbol != 1) ++ { ++ price += GET_PRICEa(probs[symbol >> 1], symbol & 1); ++ symbol >>= 1; ++ } ++ return price; ++} ++ ++static UInt32 RcTree_ReverseGetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices) ++{ ++ UInt32 price = 0; ++ UInt32 m = 1; ++ int i; ++ for (i = numBitLevels; i != 0; i--) ++ { ++ UInt32 bit = symbol & 1; ++ symbol >>= 1; ++ price += GET_PRICEa(probs[m], bit); ++ m = (m << 1) | bit; ++ } ++ return price; ++} ++ ++ ++static void LenEnc_Init(CLenEnc *p) ++{ ++ unsigned i; ++ p->choice = p->choice2 = kProbInitValue; ++ for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumLowBits); i++) ++ p->low[i] = kProbInitValue; ++ for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumMidBits); i++) ++ p->mid[i] = kProbInitValue; ++ for (i = 0; i < kLenNumHighSymbols; i++) ++ p->high[i] = kProbInitValue; ++} ++ ++static void LenEnc_Encode(CLenEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState) ++{ ++ if (symbol < kLenNumLowSymbols) ++ { ++ RangeEnc_EncodeBit(rc, &p->choice, 0); ++ RcTree_Encode(rc, p->low + (posState << kLenNumLowBits), kLenNumLowBits, symbol); ++ } ++ else ++ { ++ RangeEnc_EncodeBit(rc, &p->choice, 1); ++ if (symbol < kLenNumLowSymbols + kLenNumMidSymbols) ++ { ++ RangeEnc_EncodeBit(rc, &p->choice2, 0); ++ RcTree_Encode(rc, p->mid + (posState << kLenNumMidBits), kLenNumMidBits, symbol - kLenNumLowSymbols); ++ } ++ else ++ { ++ RangeEnc_EncodeBit(rc, &p->choice2, 1); ++ RcTree_Encode(rc, p->high, kLenNumHighBits, symbol - kLenNumLowSymbols - kLenNumMidSymbols); ++ } ++ } ++} ++ ++static void LenEnc_SetPrices(CLenEnc *p, UInt32 posState, UInt32 numSymbols, UInt32 *prices, UInt32 *ProbPrices) ++{ ++ UInt32 a0 = GET_PRICE_0a(p->choice); ++ UInt32 a1 = GET_PRICE_1a(p->choice); ++ UInt32 b0 = a1 + GET_PRICE_0a(p->choice2); ++ UInt32 b1 = a1 + GET_PRICE_1a(p->choice2); ++ UInt32 i = 0; ++ for (i = 0; i < kLenNumLowSymbols; i++) ++ { ++ if (i >= numSymbols) ++ return; ++ prices[i] = a0 + RcTree_GetPrice(p->low + (posState << kLenNumLowBits), kLenNumLowBits, i, ProbPrices); ++ } ++ for (; i < kLenNumLowSymbols + kLenNumMidSymbols; i++) ++ { ++ if (i >= numSymbols) ++ return; ++ prices[i] = b0 + RcTree_GetPrice(p->mid + (posState << kLenNumMidBits), kLenNumMidBits, i - kLenNumLowSymbols, ProbPrices); ++ } ++ for (; i < numSymbols; i++) ++ prices[i] = b1 + RcTree_GetPrice(p->high, kLenNumHighBits, i - kLenNumLowSymbols - kLenNumMidSymbols, ProbPrices); ++} ++ ++static void MY_FAST_CALL LenPriceEnc_UpdateTable(CLenPriceEnc *p, UInt32 posState, UInt32 *ProbPrices) ++{ ++ LenEnc_SetPrices(&p->p, posState, p->tableSize, p->prices[posState], ProbPrices); ++ p->counters[posState] = p->tableSize; ++} ++ ++static void LenPriceEnc_UpdateTables(CLenPriceEnc *p, UInt32 numPosStates, UInt32 *ProbPrices) ++{ ++ UInt32 posState; ++ for (posState = 0; posState < numPosStates; posState++) ++ LenPriceEnc_UpdateTable(p, posState, ProbPrices); ++} ++ ++static void LenEnc_Encode2(CLenPriceEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState, Bool updatePrice, UInt32 *ProbPrices) ++{ ++ LenEnc_Encode(&p->p, rc, symbol, posState); ++ if (updatePrice) ++ if (--p->counters[posState] == 0) ++ LenPriceEnc_UpdateTable(p, posState, ProbPrices); ++} ++ ++ ++ ++ ++static void MovePos(CLzmaEnc *p, UInt32 num) ++{ ++ #ifdef SHOW_STAT ++ ttt += num; ++ printf("\n MovePos %d", num); ++ #endif ++ if (num != 0) ++ { ++ p->additionalOffset += num; ++ p->matchFinder.Skip(p->matchFinderObj, num); ++ } ++} ++ ++static UInt32 ReadMatchDistances(CLzmaEnc *p, UInt32 *numDistancePairsRes) ++{ ++ UInt32 lenRes = 0, numPairs; ++ p->numAvail = p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); ++ numPairs = p->matchFinder.GetMatches(p->matchFinderObj, p->matches); ++ #ifdef SHOW_STAT ++ printf("\n i = %d numPairs = %d ", ttt, numPairs / 2); ++ ttt++; ++ { ++ UInt32 i; ++ for (i = 0; i < numPairs; i += 2) ++ printf("%2d %6d | ", p->matches[i], p->matches[i + 1]); ++ } ++ #endif ++ if (numPairs > 0) ++ { ++ lenRes = p->matches[numPairs - 2]; ++ if (lenRes == p->numFastBytes) ++ { ++ const Byte *pby = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; ++ UInt32 distance = p->matches[numPairs - 1] + 1; ++ UInt32 numAvail = p->numAvail; ++ if (numAvail > LZMA_MATCH_LEN_MAX) ++ numAvail = LZMA_MATCH_LEN_MAX; ++ { ++ const Byte *pby2 = pby - distance; ++ for (; lenRes < numAvail && pby[lenRes] == pby2[lenRes]; lenRes++); ++ } ++ } ++ } ++ p->additionalOffset++; ++ *numDistancePairsRes = numPairs; ++ return lenRes; ++} ++ ++ ++#define MakeAsChar(p) (p)->backPrev = (UInt32)(-1); (p)->prev1IsChar = False; ++#define MakeAsShortRep(p) (p)->backPrev = 0; (p)->prev1IsChar = False; ++#define IsShortRep(p) ((p)->backPrev == 0) ++ ++static UInt32 GetRepLen1Price(CLzmaEnc *p, UInt32 state, UInt32 posState) ++{ ++ return ++ GET_PRICE_0(p->isRepG0[state]) + ++ GET_PRICE_0(p->isRep0Long[state][posState]); ++} ++ ++static UInt32 GetPureRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 state, UInt32 posState) ++{ ++ UInt32 price; ++ if (repIndex == 0) ++ { ++ price = GET_PRICE_0(p->isRepG0[state]); ++ price += GET_PRICE_1(p->isRep0Long[state][posState]); ++ } ++ else ++ { ++ price = GET_PRICE_1(p->isRepG0[state]); ++ if (repIndex == 1) ++ price += GET_PRICE_0(p->isRepG1[state]); ++ else ++ { ++ price += GET_PRICE_1(p->isRepG1[state]); ++ price += GET_PRICE(p->isRepG2[state], repIndex - 2); ++ } ++ } ++ return price; ++} ++ ++static UInt32 GetRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 len, UInt32 state, UInt32 posState) ++{ ++ return p->repLenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN] + ++ GetPureRepPrice(p, repIndex, state, posState); ++} ++ ++static UInt32 Backward(CLzmaEnc *p, UInt32 *backRes, UInt32 cur) ++{ ++ UInt32 posMem = p->opt[cur].posPrev; ++ UInt32 backMem = p->opt[cur].backPrev; ++ p->optimumEndIndex = cur; ++ do ++ { ++ if (p->opt[cur].prev1IsChar) ++ { ++ MakeAsChar(&p->opt[posMem]) ++ p->opt[posMem].posPrev = posMem - 1; ++ if (p->opt[cur].prev2) ++ { ++ p->opt[posMem - 1].prev1IsChar = False; ++ p->opt[posMem - 1].posPrev = p->opt[cur].posPrev2; ++ p->opt[posMem - 1].backPrev = p->opt[cur].backPrev2; ++ } ++ } ++ { ++ UInt32 posPrev = posMem; ++ UInt32 backCur = backMem; ++ ++ backMem = p->opt[posPrev].backPrev; ++ posMem = p->opt[posPrev].posPrev; ++ ++ p->opt[posPrev].backPrev = backCur; ++ p->opt[posPrev].posPrev = cur; ++ cur = posPrev; ++ } ++ } ++ while (cur != 0); ++ *backRes = p->opt[0].backPrev; ++ p->optimumCurrentIndex = p->opt[0].posPrev; ++ return p->optimumCurrentIndex; ++} ++ ++#define LIT_PROBS(pos, prevByte) (p->litProbs + ((((pos) & p->lpMask) << p->lc) + ((prevByte) >> (8 - p->lc))) * 0x300) ++ ++static UInt32 GetOptimum(CLzmaEnc *p, UInt32 position, UInt32 *backRes) ++{ ++ UInt32 numAvail, mainLen, numPairs, repMaxIndex, i, posState, lenEnd, len, cur; ++ UInt32 matchPrice, repMatchPrice, normalMatchPrice; ++ UInt32 reps[LZMA_NUM_REPS], repLens[LZMA_NUM_REPS]; ++ UInt32 *matches; ++ const Byte *data; ++ Byte curByte, matchByte; ++ if (p->optimumEndIndex != p->optimumCurrentIndex) ++ { ++ const COptimal *opt = &p->opt[p->optimumCurrentIndex]; ++ UInt32 lenRes = opt->posPrev - p->optimumCurrentIndex; ++ *backRes = opt->backPrev; ++ p->optimumCurrentIndex = opt->posPrev; ++ return lenRes; ++ } ++ p->optimumCurrentIndex = p->optimumEndIndex = 0; ++ ++ if (p->additionalOffset == 0) ++ mainLen = ReadMatchDistances(p, &numPairs); ++ else ++ { ++ mainLen = p->longestMatchLength; ++ numPairs = p->numPairs; ++ } ++ ++ numAvail = p->numAvail; ++ if (numAvail < 2) ++ { ++ *backRes = (UInt32)(-1); ++ return 1; ++ } ++ if (numAvail > LZMA_MATCH_LEN_MAX) ++ numAvail = LZMA_MATCH_LEN_MAX; ++ ++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; ++ repMaxIndex = 0; ++ for (i = 0; i < LZMA_NUM_REPS; i++) ++ { ++ UInt32 lenTest; ++ const Byte *data2; ++ reps[i] = p->reps[i]; ++ data2 = data - (reps[i] + 1); ++ if (data[0] != data2[0] || data[1] != data2[1]) ++ { ++ repLens[i] = 0; ++ continue; ++ } ++ for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); ++ repLens[i] = lenTest; ++ if (lenTest > repLens[repMaxIndex]) ++ repMaxIndex = i; ++ } ++ if (repLens[repMaxIndex] >= p->numFastBytes) ++ { ++ UInt32 lenRes; ++ *backRes = repMaxIndex; ++ lenRes = repLens[repMaxIndex]; ++ MovePos(p, lenRes - 1); ++ return lenRes; ++ } ++ ++ matches = p->matches; ++ if (mainLen >= p->numFastBytes) ++ { ++ *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; ++ MovePos(p, mainLen - 1); ++ return mainLen; ++ } ++ curByte = *data; ++ matchByte = *(data - (reps[0] + 1)); ++ ++ if (mainLen < 2 && curByte != matchByte && repLens[repMaxIndex] < 2) ++ { ++ *backRes = (UInt32)-1; ++ return 1; ++ } ++ ++ p->opt[0].state = (CState)p->state; ++ ++ posState = (position & p->pbMask); ++ ++ { ++ const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); ++ p->opt[1].price = GET_PRICE_0(p->isMatch[p->state][posState]) + ++ (!IsCharState(p->state) ? ++ LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : ++ LitEnc_GetPrice(probs, curByte, p->ProbPrices)); ++ } ++ ++ MakeAsChar(&p->opt[1]); ++ ++ matchPrice = GET_PRICE_1(p->isMatch[p->state][posState]); ++ repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[p->state]); ++ ++ if (matchByte == curByte) ++ { ++ UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, p->state, posState); ++ if (shortRepPrice < p->opt[1].price) ++ { ++ p->opt[1].price = shortRepPrice; ++ MakeAsShortRep(&p->opt[1]); ++ } ++ } ++ lenEnd = ((mainLen >= repLens[repMaxIndex]) ? mainLen : repLens[repMaxIndex]); ++ ++ if (lenEnd < 2) ++ { ++ *backRes = p->opt[1].backPrev; ++ return 1; ++ } ++ ++ p->opt[1].posPrev = 0; ++ for (i = 0; i < LZMA_NUM_REPS; i++) ++ p->opt[0].backs[i] = reps[i]; ++ ++ len = lenEnd; ++ do ++ p->opt[len--].price = kInfinityPrice; ++ while (len >= 2); ++ ++ for (i = 0; i < LZMA_NUM_REPS; i++) ++ { ++ UInt32 repLen = repLens[i]; ++ UInt32 price; ++ if (repLen < 2) ++ continue; ++ price = repMatchPrice + GetPureRepPrice(p, i, p->state, posState); ++ do ++ { ++ UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][repLen - 2]; ++ COptimal *opt = &p->opt[repLen]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = 0; ++ opt->backPrev = i; ++ opt->prev1IsChar = False; ++ } ++ } ++ while (--repLen >= 2); ++ } ++ ++ normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[p->state]); ++ ++ len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2); ++ if (len <= mainLen) ++ { ++ UInt32 offs = 0; ++ while (len > matches[offs]) ++ offs += 2; ++ for (; ; len++) ++ { ++ COptimal *opt; ++ UInt32 distance = matches[offs + 1]; ++ ++ UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN]; ++ UInt32 lenToPosState = GetLenToPosState(len); ++ if (distance < kNumFullDistances) ++ curAndLenPrice += p->distancesPrices[lenToPosState][distance]; ++ else ++ { ++ UInt32 slot; ++ GetPosSlot2(distance, slot); ++ curAndLenPrice += p->alignPrices[distance & kAlignMask] + p->posSlotPrices[lenToPosState][slot]; ++ } ++ opt = &p->opt[len]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = 0; ++ opt->backPrev = distance + LZMA_NUM_REPS; ++ opt->prev1IsChar = False; ++ } ++ if (len == matches[offs]) ++ { ++ offs += 2; ++ if (offs == numPairs) ++ break; ++ } ++ } ++ } ++ ++ cur = 0; ++ ++ #ifdef SHOW_STAT2 ++ if (position >= 0) ++ { ++ unsigned i; ++ printf("\n pos = %4X", position); ++ for (i = cur; i <= lenEnd; i++) ++ printf("\nprice[%4X] = %d", position - cur + i, p->opt[i].price); ++ } ++ #endif ++ ++ for (;;) ++ { ++ UInt32 numAvailFull, newLen, numPairs, posPrev, state, posState, startLen; ++ UInt32 curPrice, curAnd1Price, matchPrice, repMatchPrice; ++ Bool nextIsChar; ++ Byte curByte, matchByte; ++ const Byte *data; ++ COptimal *curOpt; ++ COptimal *nextOpt; ++ ++ cur++; ++ if (cur == lenEnd) ++ return Backward(p, backRes, cur); ++ ++ newLen = ReadMatchDistances(p, &numPairs); ++ if (newLen >= p->numFastBytes) ++ { ++ p->numPairs = numPairs; ++ p->longestMatchLength = newLen; ++ return Backward(p, backRes, cur); ++ } ++ position++; ++ curOpt = &p->opt[cur]; ++ posPrev = curOpt->posPrev; ++ if (curOpt->prev1IsChar) ++ { ++ posPrev--; ++ if (curOpt->prev2) ++ { ++ state = p->opt[curOpt->posPrev2].state; ++ if (curOpt->backPrev2 < LZMA_NUM_REPS) ++ state = kRepNextStates[state]; ++ else ++ state = kMatchNextStates[state]; ++ } ++ else ++ state = p->opt[posPrev].state; ++ state = kLiteralNextStates[state]; ++ } ++ else ++ state = p->opt[posPrev].state; ++ if (posPrev == cur - 1) ++ { ++ if (IsShortRep(curOpt)) ++ state = kShortRepNextStates[state]; ++ else ++ state = kLiteralNextStates[state]; ++ } ++ else ++ { ++ UInt32 pos; ++ const COptimal *prevOpt; ++ if (curOpt->prev1IsChar && curOpt->prev2) ++ { ++ posPrev = curOpt->posPrev2; ++ pos = curOpt->backPrev2; ++ state = kRepNextStates[state]; ++ } ++ else ++ { ++ pos = curOpt->backPrev; ++ if (pos < LZMA_NUM_REPS) ++ state = kRepNextStates[state]; ++ else ++ state = kMatchNextStates[state]; ++ } ++ prevOpt = &p->opt[posPrev]; ++ if (pos < LZMA_NUM_REPS) ++ { ++ UInt32 i; ++ reps[0] = prevOpt->backs[pos]; ++ for (i = 1; i <= pos; i++) ++ reps[i] = prevOpt->backs[i - 1]; ++ for (; i < LZMA_NUM_REPS; i++) ++ reps[i] = prevOpt->backs[i]; ++ } ++ else ++ { ++ UInt32 i; ++ reps[0] = (pos - LZMA_NUM_REPS); ++ for (i = 1; i < LZMA_NUM_REPS; i++) ++ reps[i] = prevOpt->backs[i - 1]; ++ } ++ } ++ curOpt->state = (CState)state; ++ ++ curOpt->backs[0] = reps[0]; ++ curOpt->backs[1] = reps[1]; ++ curOpt->backs[2] = reps[2]; ++ curOpt->backs[3] = reps[3]; ++ ++ curPrice = curOpt->price; ++ nextIsChar = False; ++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; ++ curByte = *data; ++ matchByte = *(data - (reps[0] + 1)); ++ ++ posState = (position & p->pbMask); ++ ++ curAnd1Price = curPrice + GET_PRICE_0(p->isMatch[state][posState]); ++ { ++ const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); ++ curAnd1Price += ++ (!IsCharState(state) ? ++ LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : ++ LitEnc_GetPrice(probs, curByte, p->ProbPrices)); ++ } ++ ++ nextOpt = &p->opt[cur + 1]; ++ ++ if (curAnd1Price < nextOpt->price) ++ { ++ nextOpt->price = curAnd1Price; ++ nextOpt->posPrev = cur; ++ MakeAsChar(nextOpt); ++ nextIsChar = True; ++ } ++ ++ matchPrice = curPrice + GET_PRICE_1(p->isMatch[state][posState]); ++ repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[state]); ++ ++ if (matchByte == curByte && !(nextOpt->posPrev < cur && nextOpt->backPrev == 0)) ++ { ++ UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, state, posState); ++ if (shortRepPrice <= nextOpt->price) ++ { ++ nextOpt->price = shortRepPrice; ++ nextOpt->posPrev = cur; ++ MakeAsShortRep(nextOpt); ++ nextIsChar = True; ++ } ++ } ++ numAvailFull = p->numAvail; ++ { ++ UInt32 temp = kNumOpts - 1 - cur; ++ if (temp < numAvailFull) ++ numAvailFull = temp; ++ } ++ ++ if (numAvailFull < 2) ++ continue; ++ numAvail = (numAvailFull <= p->numFastBytes ? numAvailFull : p->numFastBytes); ++ ++ if (!nextIsChar && matchByte != curByte) /* speed optimization */ ++ { ++ /* try Literal + rep0 */ ++ UInt32 temp; ++ UInt32 lenTest2; ++ const Byte *data2 = data - (reps[0] + 1); ++ UInt32 limit = p->numFastBytes + 1; ++ if (limit > numAvailFull) ++ limit = numAvailFull; ++ ++ for (temp = 1; temp < limit && data[temp] == data2[temp]; temp++); ++ lenTest2 = temp - 1; ++ if (lenTest2 >= 2) ++ { ++ UInt32 state2 = kLiteralNextStates[state]; ++ UInt32 posStateNext = (position + 1) & p->pbMask; ++ UInt32 nextRepMatchPrice = curAnd1Price + ++ GET_PRICE_1(p->isMatch[state2][posStateNext]) + ++ GET_PRICE_1(p->isRep[state2]); ++ /* for (; lenTest2 >= 2; lenTest2--) */ ++ { ++ UInt32 curAndLenPrice; ++ COptimal *opt; ++ UInt32 offset = cur + 1 + lenTest2; ++ while (lenEnd < offset) ++ p->opt[++lenEnd].price = kInfinityPrice; ++ curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); ++ opt = &p->opt[offset]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = cur + 1; ++ opt->backPrev = 0; ++ opt->prev1IsChar = True; ++ opt->prev2 = False; ++ } ++ } ++ } ++ } ++ ++ startLen = 2; /* speed optimization */ ++ { ++ UInt32 repIndex; ++ for (repIndex = 0; repIndex < LZMA_NUM_REPS; repIndex++) ++ { ++ UInt32 lenTest; ++ UInt32 lenTestTemp; ++ UInt32 price; ++ const Byte *data2 = data - (reps[repIndex] + 1); ++ if (data[0] != data2[0] || data[1] != data2[1]) ++ continue; ++ for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); ++ while (lenEnd < cur + lenTest) ++ p->opt[++lenEnd].price = kInfinityPrice; ++ lenTestTemp = lenTest; ++ price = repMatchPrice + GetPureRepPrice(p, repIndex, state, posState); ++ do ++ { ++ UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][lenTest - 2]; ++ COptimal *opt = &p->opt[cur + lenTest]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = cur; ++ opt->backPrev = repIndex; ++ opt->prev1IsChar = False; ++ } ++ } ++ while (--lenTest >= 2); ++ lenTest = lenTestTemp; ++ ++ if (repIndex == 0) ++ startLen = lenTest + 1; ++ ++ /* if (_maxMode) */ ++ { ++ UInt32 lenTest2 = lenTest + 1; ++ UInt32 limit = lenTest2 + p->numFastBytes; ++ UInt32 nextRepMatchPrice; ++ if (limit > numAvailFull) ++ limit = numAvailFull; ++ for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); ++ lenTest2 -= lenTest + 1; ++ if (lenTest2 >= 2) ++ { ++ UInt32 state2 = kRepNextStates[state]; ++ UInt32 posStateNext = (position + lenTest) & p->pbMask; ++ UInt32 curAndLenCharPrice = ++ price + p->repLenEnc.prices[posState][lenTest - 2] + ++ GET_PRICE_0(p->isMatch[state2][posStateNext]) + ++ LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), ++ data[lenTest], data2[lenTest], p->ProbPrices); ++ state2 = kLiteralNextStates[state2]; ++ posStateNext = (position + lenTest + 1) & p->pbMask; ++ nextRepMatchPrice = curAndLenCharPrice + ++ GET_PRICE_1(p->isMatch[state2][posStateNext]) + ++ GET_PRICE_1(p->isRep[state2]); ++ ++ /* for (; lenTest2 >= 2; lenTest2--) */ ++ { ++ UInt32 curAndLenPrice; ++ COptimal *opt; ++ UInt32 offset = cur + lenTest + 1 + lenTest2; ++ while (lenEnd < offset) ++ p->opt[++lenEnd].price = kInfinityPrice; ++ curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); ++ opt = &p->opt[offset]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = cur + lenTest + 1; ++ opt->backPrev = 0; ++ opt->prev1IsChar = True; ++ opt->prev2 = True; ++ opt->posPrev2 = cur; ++ opt->backPrev2 = repIndex; ++ } ++ } ++ } ++ } ++ } ++ } ++ /* for (UInt32 lenTest = 2; lenTest <= newLen; lenTest++) */ ++ if (newLen > numAvail) ++ { ++ newLen = numAvail; ++ for (numPairs = 0; newLen > matches[numPairs]; numPairs += 2); ++ matches[numPairs] = newLen; ++ numPairs += 2; ++ } ++ if (newLen >= startLen) ++ { ++ UInt32 normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[state]); ++ UInt32 offs, curBack, posSlot; ++ UInt32 lenTest; ++ while (lenEnd < cur + newLen) ++ p->opt[++lenEnd].price = kInfinityPrice; ++ ++ offs = 0; ++ while (startLen > matches[offs]) ++ offs += 2; ++ curBack = matches[offs + 1]; ++ GetPosSlot2(curBack, posSlot); ++ for (lenTest = /*2*/ startLen; ; lenTest++) ++ { ++ UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][lenTest - LZMA_MATCH_LEN_MIN]; ++ UInt32 lenToPosState = GetLenToPosState(lenTest); ++ COptimal *opt; ++ if (curBack < kNumFullDistances) ++ curAndLenPrice += p->distancesPrices[lenToPosState][curBack]; ++ else ++ curAndLenPrice += p->posSlotPrices[lenToPosState][posSlot] + p->alignPrices[curBack & kAlignMask]; ++ ++ opt = &p->opt[cur + lenTest]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = cur; ++ opt->backPrev = curBack + LZMA_NUM_REPS; ++ opt->prev1IsChar = False; ++ } ++ ++ if (/*_maxMode && */lenTest == matches[offs]) ++ { ++ /* Try Match + Literal + Rep0 */ ++ const Byte *data2 = data - (curBack + 1); ++ UInt32 lenTest2 = lenTest + 1; ++ UInt32 limit = lenTest2 + p->numFastBytes; ++ UInt32 nextRepMatchPrice; ++ if (limit > numAvailFull) ++ limit = numAvailFull; ++ for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); ++ lenTest2 -= lenTest + 1; ++ if (lenTest2 >= 2) ++ { ++ UInt32 state2 = kMatchNextStates[state]; ++ UInt32 posStateNext = (position + lenTest) & p->pbMask; ++ UInt32 curAndLenCharPrice = curAndLenPrice + ++ GET_PRICE_0(p->isMatch[state2][posStateNext]) + ++ LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), ++ data[lenTest], data2[lenTest], p->ProbPrices); ++ state2 = kLiteralNextStates[state2]; ++ posStateNext = (posStateNext + 1) & p->pbMask; ++ nextRepMatchPrice = curAndLenCharPrice + ++ GET_PRICE_1(p->isMatch[state2][posStateNext]) + ++ GET_PRICE_1(p->isRep[state2]); ++ ++ /* for (; lenTest2 >= 2; lenTest2--) */ ++ { ++ UInt32 offset = cur + lenTest + 1 + lenTest2; ++ UInt32 curAndLenPrice; ++ COptimal *opt; ++ while (lenEnd < offset) ++ p->opt[++lenEnd].price = kInfinityPrice; ++ curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); ++ opt = &p->opt[offset]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = cur + lenTest + 1; ++ opt->backPrev = 0; ++ opt->prev1IsChar = True; ++ opt->prev2 = True; ++ opt->posPrev2 = cur; ++ opt->backPrev2 = curBack + LZMA_NUM_REPS; ++ } ++ } ++ } ++ offs += 2; ++ if (offs == numPairs) ++ break; ++ curBack = matches[offs + 1]; ++ if (curBack >= kNumFullDistances) ++ GetPosSlot2(curBack, posSlot); ++ } ++ } ++ } ++ } ++} ++ ++#define ChangePair(smallDist, bigDist) (((bigDist) >> 7) > (smallDist)) ++ ++static UInt32 GetOptimumFast(CLzmaEnc *p, UInt32 *backRes) ++{ ++ UInt32 numAvail, mainLen, mainDist, numPairs, repIndex, repLen, i; ++ const Byte *data; ++ const UInt32 *matches; ++ ++ if (p->additionalOffset == 0) ++ mainLen = ReadMatchDistances(p, &numPairs); ++ else ++ { ++ mainLen = p->longestMatchLength; ++ numPairs = p->numPairs; ++ } ++ ++ numAvail = p->numAvail; ++ *backRes = (UInt32)-1; ++ if (numAvail < 2) ++ return 1; ++ if (numAvail > LZMA_MATCH_LEN_MAX) ++ numAvail = LZMA_MATCH_LEN_MAX; ++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; ++ ++ repLen = repIndex = 0; ++ for (i = 0; i < LZMA_NUM_REPS; i++) ++ { ++ UInt32 len; ++ const Byte *data2 = data - (p->reps[i] + 1); ++ if (data[0] != data2[0] || data[1] != data2[1]) ++ continue; ++ for (len = 2; len < numAvail && data[len] == data2[len]; len++); ++ if (len >= p->numFastBytes) ++ { ++ *backRes = i; ++ MovePos(p, len - 1); ++ return len; ++ } ++ if (len > repLen) ++ { ++ repIndex = i; ++ repLen = len; ++ } ++ } ++ ++ matches = p->matches; ++ if (mainLen >= p->numFastBytes) ++ { ++ *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; ++ MovePos(p, mainLen - 1); ++ return mainLen; ++ } ++ ++ mainDist = 0; /* for GCC */ ++ if (mainLen >= 2) ++ { ++ mainDist = matches[numPairs - 1]; ++ while (numPairs > 2 && mainLen == matches[numPairs - 4] + 1) ++ { ++ if (!ChangePair(matches[numPairs - 3], mainDist)) ++ break; ++ numPairs -= 2; ++ mainLen = matches[numPairs - 2]; ++ mainDist = matches[numPairs - 1]; ++ } ++ if (mainLen == 2 && mainDist >= 0x80) ++ mainLen = 1; ++ } ++ ++ if (repLen >= 2 && ( ++ (repLen + 1 >= mainLen) || ++ (repLen + 2 >= mainLen && mainDist >= (1 << 9)) || ++ (repLen + 3 >= mainLen && mainDist >= (1 << 15)))) ++ { ++ *backRes = repIndex; ++ MovePos(p, repLen - 1); ++ return repLen; ++ } ++ ++ if (mainLen < 2 || numAvail <= 2) ++ return 1; ++ ++ p->longestMatchLength = ReadMatchDistances(p, &p->numPairs); ++ if (p->longestMatchLength >= 2) ++ { ++ UInt32 newDistance = matches[p->numPairs - 1]; ++ if ((p->longestMatchLength >= mainLen && newDistance < mainDist) || ++ (p->longestMatchLength == mainLen + 1 && !ChangePair(mainDist, newDistance)) || ++ (p->longestMatchLength > mainLen + 1) || ++ (p->longestMatchLength + 1 >= mainLen && mainLen >= 3 && ChangePair(newDistance, mainDist))) ++ return 1; ++ } ++ ++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; ++ for (i = 0; i < LZMA_NUM_REPS; i++) ++ { ++ UInt32 len, limit; ++ const Byte *data2 = data - (p->reps[i] + 1); ++ if (data[0] != data2[0] || data[1] != data2[1]) ++ continue; ++ limit = mainLen - 1; ++ for (len = 2; len < limit && data[len] == data2[len]; len++); ++ if (len >= limit) ++ return 1; ++ } ++ *backRes = mainDist + LZMA_NUM_REPS; ++ MovePos(p, mainLen - 2); ++ return mainLen; ++} ++ ++static void WriteEndMarker(CLzmaEnc *p, UInt32 posState) ++{ ++ UInt32 len; ++ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); ++ RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); ++ p->state = kMatchNextStates[p->state]; ++ len = LZMA_MATCH_LEN_MIN; ++ LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); ++ RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, (1 << kNumPosSlotBits) - 1); ++ RangeEnc_EncodeDirectBits(&p->rc, (((UInt32)1 << 30) - 1) >> kNumAlignBits, 30 - kNumAlignBits); ++ RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, kAlignMask); ++} ++ ++static SRes CheckErrors(CLzmaEnc *p) ++{ ++ if (p->result != SZ_OK) ++ return p->result; ++ if (p->rc.res != SZ_OK) ++ p->result = SZ_ERROR_WRITE; ++ if (p->matchFinderBase.result != SZ_OK) ++ p->result = SZ_ERROR_READ; ++ if (p->result != SZ_OK) ++ p->finished = True; ++ return p->result; ++} ++ ++static SRes Flush(CLzmaEnc *p, UInt32 nowPos) ++{ ++ /* ReleaseMFStream(); */ ++ p->finished = True; ++ if (p->writeEndMark) ++ WriteEndMarker(p, nowPos & p->pbMask); ++ RangeEnc_FlushData(&p->rc); ++ RangeEnc_FlushStream(&p->rc); ++ return CheckErrors(p); ++} ++ ++static void FillAlignPrices(CLzmaEnc *p) ++{ ++ UInt32 i; ++ for (i = 0; i < kAlignTableSize; i++) ++ p->alignPrices[i] = RcTree_ReverseGetPrice(p->posAlignEncoder, kNumAlignBits, i, p->ProbPrices); ++ p->alignPriceCount = 0; ++} ++ ++static void FillDistancesPrices(CLzmaEnc *p) ++{ ++ UInt32 tempPrices[kNumFullDistances]; ++ UInt32 i, lenToPosState; ++ for (i = kStartPosModelIndex; i < kNumFullDistances; i++) ++ { ++ UInt32 posSlot = GetPosSlot1(i); ++ UInt32 footerBits = ((posSlot >> 1) - 1); ++ UInt32 base = ((2 | (posSlot & 1)) << footerBits); ++ tempPrices[i] = RcTree_ReverseGetPrice(p->posEncoders + base - posSlot - 1, footerBits, i - base, p->ProbPrices); ++ } ++ ++ for (lenToPosState = 0; lenToPosState < kNumLenToPosStates; lenToPosState++) ++ { ++ UInt32 posSlot; ++ const CLzmaProb *encoder = p->posSlotEncoder[lenToPosState]; ++ UInt32 *posSlotPrices = p->posSlotPrices[lenToPosState]; ++ for (posSlot = 0; posSlot < p->distTableSize; posSlot++) ++ posSlotPrices[posSlot] = RcTree_GetPrice(encoder, kNumPosSlotBits, posSlot, p->ProbPrices); ++ for (posSlot = kEndPosModelIndex; posSlot < p->distTableSize; posSlot++) ++ posSlotPrices[posSlot] += ((((posSlot >> 1) - 1) - kNumAlignBits) << kNumBitPriceShiftBits); ++ ++ { ++ UInt32 *distancesPrices = p->distancesPrices[lenToPosState]; ++ UInt32 i; ++ for (i = 0; i < kStartPosModelIndex; i++) ++ distancesPrices[i] = posSlotPrices[i]; ++ for (; i < kNumFullDistances; i++) ++ distancesPrices[i] = posSlotPrices[GetPosSlot1(i)] + tempPrices[i]; ++ } ++ } ++ p->matchPriceCount = 0; ++} ++ ++void LzmaEnc_Construct(CLzmaEnc *p) ++{ ++ RangeEnc_Construct(&p->rc); ++ MatchFinder_Construct(&p->matchFinderBase); ++ #ifndef _7ZIP_ST ++ MatchFinderMt_Construct(&p->matchFinderMt); ++ p->matchFinderMt.MatchFinder = &p->matchFinderBase; ++ #endif ++ ++ { ++ CLzmaEncProps props; ++ LzmaEncProps_Init(&props); ++ LzmaEnc_SetProps(p, &props); ++ } ++ ++ #ifndef LZMA_LOG_BSR ++ LzmaEnc_FastPosInit(p->g_FastPos); ++ #endif ++ ++ LzmaEnc_InitPriceTables(p->ProbPrices); ++ p->litProbs = 0; ++ p->saveState.litProbs = 0; ++} ++ ++CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc) ++{ ++ void *p; ++ p = alloc->Alloc(alloc, sizeof(CLzmaEnc)); ++ if (p != 0) ++ LzmaEnc_Construct((CLzmaEnc *)p); ++ return p; ++} ++ ++void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->litProbs); ++ alloc->Free(alloc, p->saveState.litProbs); ++ p->litProbs = 0; ++ p->saveState.litProbs = 0; ++} ++ ++void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ #ifndef _7ZIP_ST ++ MatchFinderMt_Destruct(&p->matchFinderMt, allocBig); ++ #endif ++ MatchFinder_Free(&p->matchFinderBase, allocBig); ++ LzmaEnc_FreeLits(p, alloc); ++ RangeEnc_Free(&p->rc, alloc); ++} ++ ++void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ LzmaEnc_Destruct((CLzmaEnc *)p, alloc, allocBig); ++ alloc->Free(alloc, p); ++} ++ ++static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, Bool useLimits, UInt32 maxPackSize, UInt32 maxUnpackSize) ++{ ++ UInt32 nowPos32, startPos32; ++ if (p->needInit) ++ { ++ p->matchFinder.Init(p->matchFinderObj); ++ p->needInit = 0; ++ } ++ ++ if (p->finished) ++ return p->result; ++ RINOK(CheckErrors(p)); ++ ++ nowPos32 = (UInt32)p->nowPos64; ++ startPos32 = nowPos32; ++ ++ if (p->nowPos64 == 0) ++ { ++ UInt32 numPairs; ++ Byte curByte; ++ if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) ++ return Flush(p, nowPos32); ++ ReadMatchDistances(p, &numPairs); ++ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][0], 0); ++ p->state = kLiteralNextStates[p->state]; ++ curByte = p->matchFinder.GetIndexByte(p->matchFinderObj, 0 - p->additionalOffset); ++ LitEnc_Encode(&p->rc, p->litProbs, curByte); ++ p->additionalOffset--; ++ nowPos32++; ++ } ++ ++ if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) != 0) ++ for (;;) ++ { ++ UInt32 pos, len, posState; ++ ++ if (p->fastMode) ++ len = GetOptimumFast(p, &pos); ++ else ++ len = GetOptimum(p, nowPos32, &pos); ++ ++ #ifdef SHOW_STAT2 ++ printf("\n pos = %4X, len = %d pos = %d", nowPos32, len, pos); ++ #endif ++ ++ posState = nowPos32 & p->pbMask; ++ if (len == 1 && pos == (UInt32)-1) ++ { ++ Byte curByte; ++ CLzmaProb *probs; ++ const Byte *data; ++ ++ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 0); ++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; ++ curByte = *data; ++ probs = LIT_PROBS(nowPos32, *(data - 1)); ++ if (IsCharState(p->state)) ++ LitEnc_Encode(&p->rc, probs, curByte); ++ else ++ LitEnc_EncodeMatched(&p->rc, probs, curByte, *(data - p->reps[0] - 1)); ++ p->state = kLiteralNextStates[p->state]; ++ } ++ else ++ { ++ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); ++ if (pos < LZMA_NUM_REPS) ++ { ++ RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 1); ++ if (pos == 0) ++ { ++ RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 0); ++ RangeEnc_EncodeBit(&p->rc, &p->isRep0Long[p->state][posState], ((len == 1) ? 0 : 1)); ++ } ++ else ++ { ++ UInt32 distance = p->reps[pos]; ++ RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 1); ++ if (pos == 1) ++ RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 0); ++ else ++ { ++ RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 1); ++ RangeEnc_EncodeBit(&p->rc, &p->isRepG2[p->state], pos - 2); ++ if (pos == 3) ++ p->reps[3] = p->reps[2]; ++ p->reps[2] = p->reps[1]; ++ } ++ p->reps[1] = p->reps[0]; ++ p->reps[0] = distance; ++ } ++ if (len == 1) ++ p->state = kShortRepNextStates[p->state]; ++ else ++ { ++ LenEnc_Encode2(&p->repLenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); ++ p->state = kRepNextStates[p->state]; ++ } ++ } ++ else ++ { ++ UInt32 posSlot; ++ RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); ++ p->state = kMatchNextStates[p->state]; ++ LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); ++ pos -= LZMA_NUM_REPS; ++ GetPosSlot(pos, posSlot); ++ RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, posSlot); ++ ++ if (posSlot >= kStartPosModelIndex) ++ { ++ UInt32 footerBits = ((posSlot >> 1) - 1); ++ UInt32 base = ((2 | (posSlot & 1)) << footerBits); ++ UInt32 posReduced = pos - base; ++ ++ if (posSlot < kEndPosModelIndex) ++ RcTree_ReverseEncode(&p->rc, p->posEncoders + base - posSlot - 1, footerBits, posReduced); ++ else ++ { ++ RangeEnc_EncodeDirectBits(&p->rc, posReduced >> kNumAlignBits, footerBits - kNumAlignBits); ++ RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, posReduced & kAlignMask); ++ p->alignPriceCount++; ++ } ++ } ++ p->reps[3] = p->reps[2]; ++ p->reps[2] = p->reps[1]; ++ p->reps[1] = p->reps[0]; ++ p->reps[0] = pos; ++ p->matchPriceCount++; ++ } ++ } ++ p->additionalOffset -= len; ++ nowPos32 += len; ++ if (p->additionalOffset == 0) ++ { ++ UInt32 processed; ++ if (!p->fastMode) ++ { ++ if (p->matchPriceCount >= (1 << 7)) ++ FillDistancesPrices(p); ++ if (p->alignPriceCount >= kAlignTableSize) ++ FillAlignPrices(p); ++ } ++ if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) ++ break; ++ processed = nowPos32 - startPos32; ++ if (useLimits) ++ { ++ if (processed + kNumOpts + 300 >= maxUnpackSize || ++ RangeEnc_GetProcessed(&p->rc) + kNumOpts * 2 >= maxPackSize) ++ break; ++ } ++ else if (processed >= (1 << 15)) ++ { ++ p->nowPos64 += nowPos32 - startPos32; ++ return CheckErrors(p); ++ } ++ } ++ } ++ p->nowPos64 += nowPos32 - startPos32; ++ return Flush(p, nowPos32); ++} ++ ++#define kBigHashDicLimit ((UInt32)1 << 24) ++ ++static SRes LzmaEnc_Alloc(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ UInt32 beforeSize = kNumOpts; ++ Bool btMode; ++ if (!RangeEnc_Alloc(&p->rc, alloc)) ++ return SZ_ERROR_MEM; ++ btMode = (p->matchFinderBase.btMode != 0); ++ #ifndef _7ZIP_ST ++ p->mtMode = (p->multiThread && !p->fastMode && btMode); ++ #endif ++ ++ { ++ unsigned lclp = p->lc + p->lp; ++ if (p->litProbs == 0 || p->saveState.litProbs == 0 || p->lclp != lclp) ++ { ++ LzmaEnc_FreeLits(p, alloc); ++ p->litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb)); ++ p->saveState.litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb)); ++ if (p->litProbs == 0 || p->saveState.litProbs == 0) ++ { ++ LzmaEnc_FreeLits(p, alloc); ++ return SZ_ERROR_MEM; ++ } ++ p->lclp = lclp; ++ } ++ } ++ ++ p->matchFinderBase.bigHash = (p->dictSize > kBigHashDicLimit); ++ ++ if (beforeSize + p->dictSize < keepWindowSize) ++ beforeSize = keepWindowSize - p->dictSize; ++ ++ #ifndef _7ZIP_ST ++ if (p->mtMode) ++ { ++ RINOK(MatchFinderMt_Create(&p->matchFinderMt, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)); ++ p->matchFinderObj = &p->matchFinderMt; ++ MatchFinderMt_CreateVTable(&p->matchFinderMt, &p->matchFinder); ++ } ++ else ++ #endif ++ { ++ if (!MatchFinder_Create(&p->matchFinderBase, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)) ++ return SZ_ERROR_MEM; ++ p->matchFinderObj = &p->matchFinderBase; ++ MatchFinder_CreateVTable(&p->matchFinderBase, &p->matchFinder); ++ } ++ return SZ_OK; ++} ++ ++void LzmaEnc_Init(CLzmaEnc *p) ++{ ++ UInt32 i; ++ p->state = 0; ++ for (i = 0 ; i < LZMA_NUM_REPS; i++) ++ p->reps[i] = 0; ++ ++ RangeEnc_Init(&p->rc); ++ ++ ++ for (i = 0; i < kNumStates; i++) ++ { ++ UInt32 j; ++ for (j = 0; j < LZMA_NUM_PB_STATES_MAX; j++) ++ { ++ p->isMatch[i][j] = kProbInitValue; ++ p->isRep0Long[i][j] = kProbInitValue; ++ } ++ p->isRep[i] = kProbInitValue; ++ p->isRepG0[i] = kProbInitValue; ++ p->isRepG1[i] = kProbInitValue; ++ p->isRepG2[i] = kProbInitValue; ++ } ++ ++ { ++ UInt32 num = 0x300 << (p->lp + p->lc); ++ for (i = 0; i < num; i++) ++ p->litProbs[i] = kProbInitValue; ++ } ++ ++ { ++ for (i = 0; i < kNumLenToPosStates; i++) ++ { ++ CLzmaProb *probs = p->posSlotEncoder[i]; ++ UInt32 j; ++ for (j = 0; j < (1 << kNumPosSlotBits); j++) ++ probs[j] = kProbInitValue; ++ } ++ } ++ { ++ for (i = 0; i < kNumFullDistances - kEndPosModelIndex; i++) ++ p->posEncoders[i] = kProbInitValue; ++ } ++ ++ LenEnc_Init(&p->lenEnc.p); ++ LenEnc_Init(&p->repLenEnc.p); ++ ++ for (i = 0; i < (1 << kNumAlignBits); i++) ++ p->posAlignEncoder[i] = kProbInitValue; ++ ++ p->optimumEndIndex = 0; ++ p->optimumCurrentIndex = 0; ++ p->additionalOffset = 0; ++ ++ p->pbMask = (1 << p->pb) - 1; ++ p->lpMask = (1 << p->lp) - 1; ++} ++ ++void LzmaEnc_InitPrices(CLzmaEnc *p) ++{ ++ if (!p->fastMode) ++ { ++ FillDistancesPrices(p); ++ FillAlignPrices(p); ++ } ++ ++ p->lenEnc.tableSize = ++ p->repLenEnc.tableSize = ++ p->numFastBytes + 1 - LZMA_MATCH_LEN_MIN; ++ LenPriceEnc_UpdateTables(&p->lenEnc, 1 << p->pb, p->ProbPrices); ++ LenPriceEnc_UpdateTables(&p->repLenEnc, 1 << p->pb, p->ProbPrices); ++} ++ ++static SRes LzmaEnc_AllocAndInit(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ UInt32 i; ++ for (i = 0; i < (UInt32)kDicLogSizeMaxCompress; i++) ++ if (p->dictSize <= ((UInt32)1 << i)) ++ break; ++ p->distTableSize = i * 2; ++ ++ p->finished = False; ++ p->result = SZ_OK; ++ RINOK(LzmaEnc_Alloc(p, keepWindowSize, alloc, allocBig)); ++ LzmaEnc_Init(p); ++ LzmaEnc_InitPrices(p); ++ p->nowPos64 = 0; ++ return SZ_OK; ++} ++ ++static SRes LzmaEnc_Prepare(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ++ ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ p->matchFinderBase.stream = inStream; ++ p->needInit = 1; ++ p->rc.outStream = outStream; ++ return LzmaEnc_AllocAndInit(p, 0, alloc, allocBig); ++} ++ ++SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp, ++ ISeqInStream *inStream, UInt32 keepWindowSize, ++ ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ p->matchFinderBase.stream = inStream; ++ p->needInit = 1; ++ return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); ++} ++ ++static void LzmaEnc_SetInputBuf(CLzmaEnc *p, const Byte *src, SizeT srcLen) ++{ ++ p->matchFinderBase.directInput = 1; ++ p->matchFinderBase.bufferBase = (Byte *)src; ++ p->matchFinderBase.directInputRem = srcLen; ++} ++ ++SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen, ++ UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ LzmaEnc_SetInputBuf(p, src, srcLen); ++ p->needInit = 1; ++ ++ return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); ++} ++ ++void LzmaEnc_Finish(CLzmaEncHandle pp) ++{ ++ #ifndef _7ZIP_ST ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ if (p->mtMode) ++ MatchFinderMt_ReleaseStream(&p->matchFinderMt); ++ #else ++ pp = pp; ++ #endif ++} ++ ++typedef struct ++{ ++ ISeqOutStream funcTable; ++ Byte *data; ++ SizeT rem; ++ Bool overflow; ++} CSeqOutStreamBuf; ++ ++static size_t MyWrite(void *pp, const void *data, size_t size) ++{ ++ CSeqOutStreamBuf *p = (CSeqOutStreamBuf *)pp; ++ if (p->rem < size) ++ { ++ size = p->rem; ++ p->overflow = True; ++ } ++ memcpy(p->data, data, size); ++ p->rem -= size; ++ p->data += size; ++ return size; ++} ++ ++ ++UInt32 LzmaEnc_GetNumAvailableBytes(CLzmaEncHandle pp) ++{ ++ const CLzmaEnc *p = (CLzmaEnc *)pp; ++ return p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); ++} ++ ++const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp) ++{ ++ const CLzmaEnc *p = (CLzmaEnc *)pp; ++ return p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; ++} ++ ++SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, Bool reInit, ++ Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ UInt64 nowPos64; ++ SRes res; ++ CSeqOutStreamBuf outStream; ++ ++ outStream.funcTable.Write = MyWrite; ++ outStream.data = dest; ++ outStream.rem = *destLen; ++ outStream.overflow = False; ++ ++ p->writeEndMark = False; ++ p->finished = False; ++ p->result = SZ_OK; ++ ++ if (reInit) ++ LzmaEnc_Init(p); ++ LzmaEnc_InitPrices(p); ++ nowPos64 = p->nowPos64; ++ RangeEnc_Init(&p->rc); ++ p->rc.outStream = &outStream.funcTable; ++ ++ res = LzmaEnc_CodeOneBlock(p, True, desiredPackSize, *unpackSize); ++ ++ *unpackSize = (UInt32)(p->nowPos64 - nowPos64); ++ *destLen -= outStream.rem; ++ if (outStream.overflow) ++ return SZ_ERROR_OUTPUT_EOF; ++ ++ return res; ++} ++ ++static SRes LzmaEnc_Encode2(CLzmaEnc *p, ICompressProgress *progress) ++{ ++ SRes res = SZ_OK; ++ ++ #ifndef _7ZIP_ST ++ Byte allocaDummy[0x300]; ++ int i = 0; ++ for (i = 0; i < 16; i++) ++ allocaDummy[i] = (Byte)i; ++ #endif ++ ++ for (;;) ++ { ++ res = LzmaEnc_CodeOneBlock(p, False, 0, 0); ++ if (res != SZ_OK || p->finished != 0) ++ break; ++ if (progress != 0) ++ { ++ res = progress->Progress(progress, p->nowPos64, RangeEnc_GetProcessed(&p->rc)); ++ if (res != SZ_OK) ++ { ++ res = SZ_ERROR_PROGRESS; ++ break; ++ } ++ } ++ } ++ LzmaEnc_Finish(p); ++ return res; ++} ++ ++SRes LzmaEnc_Encode(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress, ++ ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ RINOK(LzmaEnc_Prepare(pp, outStream, inStream, alloc, allocBig)); ++ return LzmaEnc_Encode2((CLzmaEnc *)pp, progress); ++} ++ ++SRes LzmaEnc_WriteProperties(CLzmaEncHandle pp, Byte *props, SizeT *size) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ int i; ++ UInt32 dictSize = p->dictSize; ++ if (*size < LZMA_PROPS_SIZE) ++ return SZ_ERROR_PARAM; ++ *size = LZMA_PROPS_SIZE; ++ props[0] = (Byte)((p->pb * 5 + p->lp) * 9 + p->lc); ++ ++ for (i = 11; i <= 30; i++) ++ { ++ if (dictSize <= ((UInt32)2 << i)) ++ { ++ dictSize = (2 << i); ++ break; ++ } ++ if (dictSize <= ((UInt32)3 << i)) ++ { ++ dictSize = (3 << i); ++ break; ++ } ++ } ++ ++ for (i = 0; i < 4; i++) ++ props[1 + i] = (Byte)(dictSize >> (8 * i)); ++ return SZ_OK; ++} ++ ++SRes LzmaEnc_MemEncode(CLzmaEncHandle pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, ++ int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ SRes res; ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ ++ CSeqOutStreamBuf outStream; ++ ++ LzmaEnc_SetInputBuf(p, src, srcLen); ++ ++ outStream.funcTable.Write = MyWrite; ++ outStream.data = dest; ++ outStream.rem = *destLen; ++ outStream.overflow = False; ++ ++ p->writeEndMark = writeEndMark; ++ ++ p->rc.outStream = &outStream.funcTable; ++ res = LzmaEnc_MemPrepare(pp, src, srcLen, 0, alloc, allocBig); ++ if (res == SZ_OK) ++ res = LzmaEnc_Encode2(p, progress); ++ ++ *destLen -= outStream.rem; ++ if (outStream.overflow) ++ return SZ_ERROR_OUTPUT_EOF; ++ return res; ++} ++ ++SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, ++ const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, ++ ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)LzmaEnc_Create(alloc); ++ SRes res; ++ if (p == 0) ++ return SZ_ERROR_MEM; ++ ++ res = LzmaEnc_SetProps(p, props); ++ if (res == SZ_OK) ++ { ++ res = LzmaEnc_WriteProperties(p, propsEncoded, propsSize); ++ if (res == SZ_OK) ++ res = LzmaEnc_MemEncode(p, dest, destLen, src, srcLen, ++ writeEndMark, progress, alloc, allocBig); ++ } ++ ++ LzmaEnc_Destroy(p, alloc, allocBig); ++ return res; ++} +--- /dev/null ++++ b/lib/lzma/Makefile +@@ -0,0 +1,7 @@ ++lzma_compress-objs := LzFind.o LzmaEnc.o ++lzma_decompress-objs := LzmaDec.o ++ ++obj-$(CONFIG_LZMA_COMPRESS) += lzma_compress.o ++obj-$(CONFIG_LZMA_DECOMPRESS) += lzma_decompress.o ++ ++EXTRA_CFLAGS += -Iinclude/linux -Iinclude/linux/lzma -include types.h diff --git a/target/linux/generic/pending-6.6/532-jffs2_eofdetect.patch b/target/linux/generic/pending-6.6/532-jffs2_eofdetect.patch new file mode 100644 index 0000000000..744fbd0e21 --- /dev/null +++ b/target/linux/generic/pending-6.6/532-jffs2_eofdetect.patch @@ -0,0 +1,65 @@ +From: Felix Fietkau +Subject: fs: jffs2: EOF marker + +Signed-off-by: Felix Fietkau +--- + fs/jffs2/build.c | 10 ++++++++++ + fs/jffs2/scan.c | 21 +++++++++++++++++++-- + 2 files changed, 29 insertions(+), 2 deletions(-) + +--- a/fs/jffs2/build.c ++++ b/fs/jffs2/build.c +@@ -117,6 +117,16 @@ static int jffs2_build_filesystem(struct + dbg_fsbuild("scanned flash completely\n"); + jffs2_dbg_dump_block_lists_nolock(c); + ++ if (c->flags & (1 << 7)) { ++ printk("%s(): unlocking the mtd device... ", __func__); ++ mtd_unlock(c->mtd, 0, c->mtd->size); ++ printk("done.\n"); ++ ++ printk("%s(): erasing all blocks after the end marker... ", __func__); ++ jffs2_erase_pending_blocks(c, -1); ++ printk("done.\n"); ++ } ++ + dbg_fsbuild("pass 1 starting\n"); + c->flags |= JFFS2_SB_FLAG_BUILDING; + /* Now scan the directory tree, increasing nlink according to every dirent found. */ +--- a/fs/jffs2/scan.c ++++ b/fs/jffs2/scan.c +@@ -148,8 +148,14 @@ int jffs2_scan_medium(struct jffs2_sb_in + /* reset summary info for next eraseblock scan */ + jffs2_sum_reset_collected(s); + +- ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), +- buf_size, s); ++ if (c->flags & (1 << 7)) { ++ if (mtd_block_isbad(c->mtd, jeb->offset)) ++ ret = BLK_STATE_BADBLOCK; ++ else ++ ret = BLK_STATE_ALLFF; ++ } else ++ ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), ++ buf_size, s); + + if (ret < 0) + goto out; +@@ -567,6 +573,17 @@ full_scan: + return err; + } + ++ if ((buf[0] == 0xde) && ++ (buf[1] == 0xad) && ++ (buf[2] == 0xc0) && ++ (buf[3] == 0xde)) { ++ /* end of filesystem. erase everything after this point */ ++ printk("%s(): End of filesystem marker found at 0x%x\n", __func__, jeb->offset); ++ c->flags |= (1 << 7); ++ ++ return BLK_STATE_ALLFF; ++ } ++ + /* We temporarily use 'ofs' as a pointer into the buffer/jeb */ + ofs = 0; + max_ofs = EMPTY_SCAN_SIZE(c->sector_size); diff --git a/target/linux/generic/pending-6.6/600-netfilter_conntrack_flush.patch b/target/linux/generic/pending-6.6/600-netfilter_conntrack_flush.patch new file mode 100644 index 0000000000..d7548c1419 --- /dev/null +++ b/target/linux/generic/pending-6.6/600-netfilter_conntrack_flush.patch @@ -0,0 +1,90 @@ +From: Felix Fietkau +Subject: netfilter: add support for flushing conntrack via /proc + +lede-commit 8193bbe59a74d34d6a26d4a8cb857b1952905314 +Signed-off-by: Felix Fietkau +--- + net/netfilter/nf_conntrack_standalone.c | 59 ++++++++++++++++++++++++++++++++- + 1 file changed, 58 insertions(+), 1 deletion(-) + +--- a/net/netfilter/nf_conntrack_standalone.c ++++ b/net/netfilter/nf_conntrack_standalone.c +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + #include + #ifdef CONFIG_SYSCTL + #include +@@ -465,6 +466,58 @@ static int ct_cpu_seq_show(struct seq_fi + return 0; + } + ++struct kill_request { ++ u16 family; ++ union nf_inet_addr addr; ++}; ++ ++static int kill_matching(struct nf_conn *i, void *data) ++{ ++ struct kill_request *kr = data; ++ struct nf_conntrack_tuple *t1 = &i->tuplehash[IP_CT_DIR_ORIGINAL].tuple; ++ struct nf_conntrack_tuple *t2 = &i->tuplehash[IP_CT_DIR_REPLY].tuple; ++ ++ if (!kr->family) ++ return 1; ++ ++ if (t1->src.l3num != kr->family) ++ return 0; ++ ++ return (nf_inet_addr_cmp(&kr->addr, &t1->src.u3) || ++ nf_inet_addr_cmp(&kr->addr, &t1->dst.u3) || ++ nf_inet_addr_cmp(&kr->addr, &t2->src.u3) || ++ nf_inet_addr_cmp(&kr->addr, &t2->dst.u3)); ++} ++ ++static int ct_file_write(struct file *file, char *buf, size_t count) ++{ ++ struct seq_file *seq = file->private_data; ++ struct nf_ct_iter_data iter_data; ++ struct kill_request kr = { }; ++ ++ if (count == 0) ++ return 0; ++ ++ if (count >= INET6_ADDRSTRLEN) ++ count = INET6_ADDRSTRLEN - 1; ++ ++ if (strnchr(buf, count, ':')) { ++ kr.family = AF_INET6; ++ if (!in6_pton(buf, count, (void *)&kr.addr, '\n', NULL)) ++ return -EINVAL; ++ } else if (strnchr(buf, count, '.')) { ++ kr.family = AF_INET; ++ if (!in4_pton(buf, count, (void *)&kr.addr, '\n', NULL)) ++ return -EINVAL; ++ } ++ ++ iter_data.net = seq_file_net(seq); ++ iter_data.data = &kr; ++ nf_ct_iterate_cleanup_net(kill_matching, &iter_data); ++ ++ return 0; ++} ++ + static const struct seq_operations ct_cpu_seq_ops = { + .start = ct_cpu_seq_start, + .next = ct_cpu_seq_next, +@@ -478,8 +531,9 @@ static int nf_conntrack_standalone_init_ + kuid_t root_uid; + kgid_t root_gid; + +- pde = proc_create_net("nf_conntrack", 0440, net->proc_net, &ct_seq_ops, +- sizeof(struct ct_iter_state)); ++ pde = proc_create_net_data_write("nf_conntrack", 0440, net->proc_net, ++ &ct_seq_ops, &ct_file_write, ++ sizeof(struct ct_iter_state), NULL); + if (!pde) + goto out_nf_conntrack; + diff --git a/target/linux/generic/pending-6.6/610-netfilter_match_bypass_default_checks.patch b/target/linux/generic/pending-6.6/610-netfilter_match_bypass_default_checks.patch new file mode 100644 index 0000000000..7e34ef3713 --- /dev/null +++ b/target/linux/generic/pending-6.6/610-netfilter_match_bypass_default_checks.patch @@ -0,0 +1,110 @@ +From: Felix Fietkau +Subject: kernel: add a new version of my netfilter speedup patches for linux 2.6.39 and 3.0 + +Signed-off-by: Felix Fietkau +--- + include/uapi/linux/netfilter_ipv4/ip_tables.h | 1 + + net/ipv4/netfilter/ip_tables.c | 37 +++++++++++++++++++++++++++ + 2 files changed, 38 insertions(+) + +--- a/include/uapi/linux/netfilter_ipv4/ip_tables.h ++++ b/include/uapi/linux/netfilter_ipv4/ip_tables.h +@@ -89,6 +89,7 @@ struct ipt_ip { + #define IPT_F_FRAG 0x01 /* Set if rule is a fragment rule */ + #define IPT_F_GOTO 0x02 /* Set if jump is a goto */ + #define IPT_F_MASK 0x03 /* All possible flag bits mask. */ ++#define IPT_F_NO_DEF_MATCH 0x80 /* Internal: no default match rules present */ + + /* Values for "inv" field in struct ipt_ip. */ + #define IPT_INV_VIA_IN 0x01 /* Invert the sense of IN IFACE. */ +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -50,6 +50,9 @@ ip_packet_match(const struct iphdr *ip, + { + unsigned long ret; + ++ if (ipinfo->flags & IPT_F_NO_DEF_MATCH) ++ return true; ++ + if (NF_INVF(ipinfo, IPT_INV_SRCIP, + (ip->saddr & ipinfo->smsk.s_addr) != ipinfo->src.s_addr) || + NF_INVF(ipinfo, IPT_INV_DSTIP, +@@ -80,6 +83,29 @@ ip_packet_match(const struct iphdr *ip, + return true; + } + ++static void ++ip_checkdefault(struct ipt_ip *ip) ++{ ++ static const char iface_mask[IFNAMSIZ] = {}; ++ ++ if (ip->invflags || ip->flags & IPT_F_FRAG) ++ return; ++ ++ if (memcmp(ip->iniface_mask, iface_mask, IFNAMSIZ) != 0) ++ return; ++ ++ if (memcmp(ip->outiface_mask, iface_mask, IFNAMSIZ) != 0) ++ return; ++ ++ if (ip->smsk.s_addr || ip->dmsk.s_addr) ++ return; ++ ++ if (ip->proto) ++ return; ++ ++ ip->flags |= IPT_F_NO_DEF_MATCH; ++} ++ + static bool + ip_checkentry(const struct ipt_ip *ip) + { +@@ -525,6 +551,8 @@ find_check_entry(struct ipt_entry *e, st + struct xt_mtchk_param mtpar; + struct xt_entry_match *ematch; + ++ ip_checkdefault(&e->ip); ++ + if (!xt_percpu_counter_alloc(alloc_state, &e->counters)) + return -ENOMEM; + +@@ -819,6 +847,7 @@ copy_entries_to_user(unsigned int total_ + const struct xt_table_info *private = table->private; + int ret = 0; + const void *loc_cpu_entry; ++ u8 flags; + + counters = alloc_counters(table); + if (IS_ERR(counters)) +@@ -846,6 +875,14 @@ copy_entries_to_user(unsigned int total_ + goto free_counters; + } + ++ flags = e->ip.flags & IPT_F_MASK; ++ if (copy_to_user(userptr + off ++ + offsetof(struct ipt_entry, ip.flags), ++ &flags, sizeof(flags)) != 0) { ++ ret = -EFAULT; ++ goto free_counters; ++ } ++ + for (i = sizeof(struct ipt_entry); + i < e->target_offset; + i += m->u.match_size) { +@@ -1223,12 +1260,15 @@ compat_copy_entry_to_user(struct ipt_ent + compat_uint_t origsize; + const struct xt_entry_match *ematch; + int ret = 0; ++ u8 flags = e->ip.flags & IPT_F_MASK; + + origsize = *size; + ce = *dstptr; + if (copy_to_user(ce, e, sizeof(struct ipt_entry)) != 0 || + copy_to_user(&ce->counters, &counters[i], +- sizeof(counters[i])) != 0) ++ sizeof(counters[i])) != 0 || ++ copy_to_user(&ce->ip.flags, &flags, ++ sizeof(flags)) != 0) + return -EFAULT; + + *dstptr += sizeof(struct compat_ipt_entry); diff --git a/target/linux/generic/pending-6.6/611-netfilter_match_bypass_default_table.patch b/target/linux/generic/pending-6.6/611-netfilter_match_bypass_default_table.patch new file mode 100644 index 0000000000..dd557fd3ea --- /dev/null +++ b/target/linux/generic/pending-6.6/611-netfilter_match_bypass_default_table.patch @@ -0,0 +1,106 @@ +From: Felix Fietkau +Subject: netfilter: match bypass default table + +Signed-off-by: Felix Fietkau +--- + net/ipv4/netfilter/ip_tables.c | 79 +++++++++++++++++++++++++++++++----------- + 1 file changed, 58 insertions(+), 21 deletions(-) + +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -246,6 +246,33 @@ struct ipt_entry *ipt_next_entry(const s + return (void *)entry + entry->next_offset; + } + ++static bool ++ipt_handle_default_rule(struct ipt_entry *e, unsigned int *verdict) ++{ ++ struct xt_entry_target *t; ++ struct xt_standard_target *st; ++ ++ if (e->target_offset != sizeof(struct ipt_entry)) ++ return false; ++ ++ if (!(e->ip.flags & IPT_F_NO_DEF_MATCH)) ++ return false; ++ ++ t = ipt_get_target(e); ++ if (t->u.kernel.target->target) ++ return false; ++ ++ st = (struct xt_standard_target *) t; ++ if (st->verdict == XT_RETURN) ++ return false; ++ ++ if (st->verdict >= 0) ++ return false; ++ ++ *verdict = (unsigned)(-st->verdict) - 1; ++ return true; ++} ++ + /* Returns one of the generic firewall policies, like NF_ACCEPT. */ + unsigned int + ipt_do_table(void *priv, +@@ -267,27 +294,28 @@ ipt_do_table(void *priv, + unsigned int addend; + + /* Initialization */ ++ WARN_ON(!(table->valid_hooks & (1 << hook))); ++ local_bh_disable(); ++ private = READ_ONCE(table->private); /* Address dependency. */ ++ cpu = smp_processor_id(); ++ table_base = private->entries; ++ ++ e = get_entry(table_base, private->hook_entry[hook]); ++ if (ipt_handle_default_rule(e, &verdict)) { ++ struct xt_counters *counter; ++ ++ counter = xt_get_this_cpu_counter(&e->counters); ++ ADD_COUNTER(*counter, skb->len, 1); ++ local_bh_enable(); ++ return verdict; ++ } ++ + stackidx = 0; + ip = ip_hdr(skb); + indev = state->in ? state->in->name : nulldevname; + outdev = state->out ? state->out->name : nulldevname; +- /* We handle fragments by dealing with the first fragment as +- * if it was a normal packet. All other fragments are treated +- * normally, except that they will NEVER match rules that ask +- * things we don't know, ie. tcp syn flag or ports). If the +- * rule is also a fragment-specific rule, non-fragments won't +- * match it. */ +- acpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET; +- acpar.thoff = ip_hdrlen(skb); +- acpar.hotdrop = false; +- acpar.state = state; + +- WARN_ON(!(table->valid_hooks & (1 << hook))); +- local_bh_disable(); + addend = xt_write_recseq_begin(); +- private = READ_ONCE(table->private); /* Address dependency. */ +- cpu = smp_processor_id(); +- table_base = private->entries; + jumpstack = (struct ipt_entry **)private->jumpstack[cpu]; + + /* Switch to alternate jumpstack if we're being invoked via TEE. +@@ -300,7 +328,16 @@ ipt_do_table(void *priv, + if (static_key_false(&xt_tee_enabled)) + jumpstack += private->stacksize * __this_cpu_read(nf_skb_duplicated); + +- e = get_entry(table_base, private->hook_entry[hook]); ++ /* We handle fragments by dealing with the first fragment as ++ * if it was a normal packet. All other fragments are treated ++ * normally, except that they will NEVER match rules that ask ++ * things we don't know, ie. tcp syn flag or ports). If the ++ * rule is also a fragment-specific rule, non-fragments won't ++ * match it. */ ++ acpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET; ++ acpar.thoff = ip_hdrlen(skb); ++ acpar.hotdrop = false; ++ acpar.state = state; + + do { + const struct xt_entry_target *t; diff --git a/target/linux/generic/pending-6.6/612-netfilter_match_reduce_memory_access.patch b/target/linux/generic/pending-6.6/612-netfilter_match_reduce_memory_access.patch new file mode 100644 index 0000000000..79da6778b6 --- /dev/null +++ b/target/linux/generic/pending-6.6/612-netfilter_match_reduce_memory_access.patch @@ -0,0 +1,22 @@ +From: Felix Fietkau +Subject: netfilter: reduce match memory access + +Signed-off-by: Felix Fietkau +--- + net/ipv4/netfilter/ip_tables.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -53,9 +53,9 @@ ip_packet_match(const struct iphdr *ip, + if (ipinfo->flags & IPT_F_NO_DEF_MATCH) + return true; + +- if (NF_INVF(ipinfo, IPT_INV_SRCIP, ++ if (NF_INVF(ipinfo, IPT_INV_SRCIP, ipinfo->smsk.s_addr && + (ip->saddr & ipinfo->smsk.s_addr) != ipinfo->src.s_addr) || +- NF_INVF(ipinfo, IPT_INV_DSTIP, ++ NF_INVF(ipinfo, IPT_INV_DSTIP, ipinfo->dmsk.s_addr && + (ip->daddr & ipinfo->dmsk.s_addr) != ipinfo->dst.s_addr)) + return false; + diff --git a/target/linux/generic/pending-6.6/620-net_sched-codel-do-not-defer-queue-length-update.patch b/target/linux/generic/pending-6.6/620-net_sched-codel-do-not-defer-queue-length-update.patch new file mode 100644 index 0000000000..4b4825ae3b --- /dev/null +++ b/target/linux/generic/pending-6.6/620-net_sched-codel-do-not-defer-queue-length-update.patch @@ -0,0 +1,86 @@ +From: Konstantin Khlebnikov +Date: Mon, 21 Aug 2017 11:14:14 +0300 +Subject: [PATCH] net_sched/codel: do not defer queue length update + +When codel wants to drop last packet in ->dequeue() it cannot call +qdisc_tree_reduce_backlog() right away - it will notify parent qdisc +about zero qlen and HTB/HFSC will deactivate class. The same class will +be deactivated second time by caller of ->dequeue(). Currently codel and +fq_codel defer update. This triggers warning in HFSC when it's qlen != 0 +but there is no active classes. + +This patch update parent queue length immediately: just temporary increase +qlen around qdisc_tree_reduce_backlog() to prevent first class deactivation +if we have skb to return. + +This might open another problem in HFSC - now operation peek could fail and +deactivate parent class. + +Signed-off-by: Konstantin Khlebnikov +Link: https://bugzilla.kernel.org/show_bug.cgi?id=109581 +--- + +--- a/net/sched/sch_codel.c ++++ b/net/sched/sch_codel.c +@@ -95,11 +95,17 @@ static struct sk_buff *codel_qdisc_deque + &q->stats, qdisc_pkt_len, codel_get_enqueue_time, + drop_func, dequeue_func); + +- /* We cant call qdisc_tree_reduce_backlog() if our qlen is 0, +- * or HTB crashes. Defer it for next round. ++ /* If our qlen is 0 qdisc_tree_reduce_backlog() will deactivate ++ * parent class, dequeue in parent qdisc will do the same if we ++ * return skb. Temporary increment qlen if we have skb. + */ +- if (q->stats.drop_count && sch->q.qlen) { +- qdisc_tree_reduce_backlog(sch, q->stats.drop_count, q->stats.drop_len); ++ if (q->stats.drop_count) { ++ if (skb) ++ sch->q.qlen++; ++ qdisc_tree_reduce_backlog(sch, q->stats.drop_count, ++ q->stats.drop_len); ++ if (skb) ++ sch->q.qlen--; + q->stats.drop_count = 0; + q->stats.drop_len = 0; + } +--- a/net/sched/sch_fq_codel.c ++++ b/net/sched/sch_fq_codel.c +@@ -304,6 +304,21 @@ begin: + &flow->cvars, &q->cstats, qdisc_pkt_len, + codel_get_enqueue_time, drop_func, dequeue_func); + ++ /* If our qlen is 0 qdisc_tree_reduce_backlog() will deactivate ++ * parent class, dequeue in parent qdisc will do the same if we ++ * return skb. Temporary increment qlen if we have skb. ++ */ ++ if (q->cstats.drop_count) { ++ if (skb) ++ sch->q.qlen++; ++ qdisc_tree_reduce_backlog(sch, q->cstats.drop_count, ++ q->cstats.drop_len); ++ if (skb) ++ sch->q.qlen--; ++ q->cstats.drop_count = 0; ++ q->cstats.drop_len = 0; ++ } ++ + if (!skb) { + /* force a pass through old_flows to prevent starvation */ + if ((head == &q->new_flows) && !list_empty(&q->old_flows)) +@@ -314,15 +329,6 @@ begin: + } + qdisc_bstats_update(sch, skb); + flow->deficit -= qdisc_pkt_len(skb); +- /* We cant call qdisc_tree_reduce_backlog() if our qlen is 0, +- * or HTB crashes. Defer it for next round. +- */ +- if (q->cstats.drop_count && sch->q.qlen) { +- qdisc_tree_reduce_backlog(sch, q->cstats.drop_count, +- q->cstats.drop_len); +- q->cstats.drop_count = 0; +- q->cstats.drop_len = 0; +- } + return skb; + } + diff --git a/target/linux/generic/pending-6.6/630-packet_socket_type.patch b/target/linux/generic/pending-6.6/630-packet_socket_type.patch new file mode 100644 index 0000000000..c40c709635 --- /dev/null +++ b/target/linux/generic/pending-6.6/630-packet_socket_type.patch @@ -0,0 +1,138 @@ +From: Felix Fietkau +Subject: net: add an optimization for dealing with raw sockets + +lede-commit: 4898039703d7315f0f3431c860123338ec3be0f6 +Signed-off-by: Felix Fietkau +--- + include/uapi/linux/if_packet.h | 3 +++ + net/packet/af_packet.c | 34 +++++++++++++++++++++++++++------- + net/packet/internal.h | 1 + + 3 files changed, 31 insertions(+), 7 deletions(-) + +--- a/include/uapi/linux/if_packet.h ++++ b/include/uapi/linux/if_packet.h +@@ -33,6 +33,8 @@ struct sockaddr_ll { + #define PACKET_KERNEL 7 /* To kernel space */ + /* Unused, PACKET_FASTROUTE and PACKET_LOOPBACK are invisible to user space */ + #define PACKET_FASTROUTE 6 /* Fastrouted frame */ ++#define PACKET_MASK_ANY 0xffffffff /* mask for packet type bits */ ++ + + /* Packet socket options */ + +@@ -59,6 +61,7 @@ struct sockaddr_ll { + #define PACKET_ROLLOVER_STATS 21 + #define PACKET_FANOUT_DATA 22 + #define PACKET_IGNORE_OUTGOING 23 ++#define PACKET_RECV_TYPE 24 + + #define PACKET_FANOUT_HASH 0 + #define PACKET_FANOUT_LB 1 +--- a/net/packet/af_packet.c ++++ b/net/packet/af_packet.c +@@ -1866,6 +1866,7 @@ static int packet_rcv_spkt(struct sk_buf + { + struct sock *sk; + struct sockaddr_pkt *spkt; ++ struct packet_sock *po; + + /* + * When we registered the protocol we saved the socket in the data +@@ -1873,6 +1874,7 @@ static int packet_rcv_spkt(struct sk_buf + */ + + sk = pt->af_packet_priv; ++ po = pkt_sk(sk); + + /* + * Yank back the headers [hope the device set this +@@ -1885,7 +1887,7 @@ static int packet_rcv_spkt(struct sk_buf + * so that this procedure is noop. + */ + +- if (skb->pkt_type == PACKET_LOOPBACK) ++ if (!(po->pkt_type & (1 << skb->pkt_type))) + goto out; + + if (!net_eq(dev_net(dev), sock_net(sk))) +@@ -2131,12 +2133,12 @@ static int packet_rcv(struct sk_buff *sk + unsigned int snaplen, res; + bool is_drop_n_account = false; + +- if (skb->pkt_type == PACKET_LOOPBACK) +- goto drop; +- + sk = pt->af_packet_priv; + po = pkt_sk(sk); + ++ if (!(po->pkt_type & (1 << skb->pkt_type))) ++ goto drop; ++ + if (!net_eq(dev_net(dev), sock_net(sk))) + goto drop; + +@@ -2263,12 +2265,12 @@ static int tpacket_rcv(struct sk_buff *s + BUILD_BUG_ON(TPACKET_ALIGN(sizeof(*h.h2)) != 32); + BUILD_BUG_ON(TPACKET_ALIGN(sizeof(*h.h3)) != 48); + +- if (skb->pkt_type == PACKET_LOOPBACK) +- goto drop; +- + sk = pt->af_packet_priv; + po = pkt_sk(sk); + ++ if (!(po->pkt_type & (1 << skb->pkt_type))) ++ goto drop; ++ + if (!net_eq(dev_net(dev), sock_net(sk))) + goto drop; + +@@ -3378,6 +3380,7 @@ static int packet_create(struct net *net + mutex_init(&po->pg_vec_lock); + po->rollover = NULL; + po->prot_hook.func = packet_rcv; ++ po->pkt_type = PACKET_MASK_ANY & ~(1 << PACKET_LOOPBACK); + + if (sock->type == SOCK_PACKET) + po->prot_hook.func = packet_rcv_spkt; +@@ -4013,6 +4016,16 @@ packet_setsockopt(struct socket *sock, i + WRITE_ONCE(po->xmit, val ? packet_direct_xmit : dev_queue_xmit); + return 0; + } ++ case PACKET_RECV_TYPE: ++ { ++ unsigned int val; ++ if (optlen != sizeof(val)) ++ return -EINVAL; ++ if (copy_from_sockptr(&val, optval, sizeof(val))) ++ return -EFAULT; ++ po->pkt_type = val & ~BIT(PACKET_LOOPBACK); ++ return 0; ++ } + default: + return -ENOPROTOOPT; + } +@@ -4069,6 +4082,13 @@ static int packet_getsockopt(struct sock + case PACKET_VNET_HDR: + val = po->has_vnet_hdr; + break; ++ case PACKET_RECV_TYPE: ++ if (len > sizeof(unsigned int)) ++ len = sizeof(unsigned int); ++ val = po->pkt_type; ++ ++ data = &val; ++ break; + case PACKET_VERSION: + val = po->tp_version; + break; +--- a/net/packet/internal.h ++++ b/net/packet/internal.h +@@ -136,6 +136,7 @@ struct packet_sock { + int (*xmit)(struct sk_buff *skb); + struct packet_type prot_hook ____cacheline_aligned_in_smp; + atomic_t tp_drops ____cacheline_aligned_in_smp; ++ unsigned int pkt_type; + }; + + static inline struct packet_sock *pkt_sk(struct sock *sk) diff --git a/target/linux/generic/pending-6.6/655-increase_skb_pad.patch b/target/linux/generic/pending-6.6/655-increase_skb_pad.patch new file mode 100644 index 0000000000..0c47bc9d2c --- /dev/null +++ b/target/linux/generic/pending-6.6/655-increase_skb_pad.patch @@ -0,0 +1,20 @@ +From: Felix Fietkau +Subject: kernel: add a few patches for avoiding unnecessary skb reallocations - significantly improves ethernet<->wireless performance + +lede-commit: 6f89cffc9add6939d44a6b54cf9a5e77849aa7fd +Signed-off-by: Felix Fietkau +--- + include/linux/skbuff.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -3006,7 +3006,7 @@ static inline int pskb_network_may_pull( + * NET_IP_ALIGN(2) + ethernet_header(14) + IP_header(20/40) + ports(8) + */ + #ifndef NET_SKB_PAD +-#define NET_SKB_PAD max(32, L1_CACHE_BYTES) ++#define NET_SKB_PAD max(64, L1_CACHE_BYTES) + #endif + + int ___pskb_trim(struct sk_buff *skb, unsigned int len); diff --git a/target/linux/generic/pending-6.6/666-Add-support-for-MAP-E-FMRs-mesh-mode.patch b/target/linux/generic/pending-6.6/666-Add-support-for-MAP-E-FMRs-mesh-mode.patch new file mode 100644 index 0000000000..b799c6fc9c --- /dev/null +++ b/target/linux/generic/pending-6.6/666-Add-support-for-MAP-E-FMRs-mesh-mode.patch @@ -0,0 +1,511 @@ +From: Steven Barth +Subject: Add support for MAP-E FMRs (mesh mode) + +MAP-E FMRs (draft-ietf-softwire-map-10) are rules for IPv4-communication +between MAP CEs (mesh mode) without the need to forward such data to a +border relay. This is similar to how 6rd works but for IPv4 over IPv6. + +Signed-off-by: Steven Barth +--- + include/net/ip6_tunnel.h | 13 ++ + include/uapi/linux/if_tunnel.h | 13 ++ + net/ipv6/ip6_tunnel.c | 276 +++++++++++++++++++++++++++++++++++++++-- + 3 files changed, 291 insertions(+), 11 deletions(-) + +--- a/include/net/ip6_tunnel.h ++++ b/include/net/ip6_tunnel.h +@@ -18,6 +18,18 @@ + /* determine capability on a per-packet basis */ + #define IP6_TNL_F_CAP_PER_PACKET 0x40000 + ++/* IPv6 tunnel FMR */ ++struct __ip6_tnl_fmr { ++ struct __ip6_tnl_fmr *next; /* next fmr in list */ ++ struct in6_addr ip6_prefix; ++ struct in_addr ip4_prefix; ++ ++ __u8 ip6_prefix_len; ++ __u8 ip4_prefix_len; ++ __u8 ea_len; ++ __u8 offset; ++}; ++ + struct __ip6_tnl_parm { + char name[IFNAMSIZ]; /* name of tunnel device */ + int link; /* ifindex of underlying L2 interface */ +@@ -29,6 +41,7 @@ struct __ip6_tnl_parm { + __u32 flags; /* tunnel flags */ + struct in6_addr laddr; /* local tunnel end-point address */ + struct in6_addr raddr; /* remote tunnel end-point address */ ++ struct __ip6_tnl_fmr *fmrs; /* FMRs */ + + __be16 i_flags; + __be16 o_flags; +--- a/include/uapi/linux/if_tunnel.h ++++ b/include/uapi/linux/if_tunnel.h +@@ -77,10 +77,23 @@ enum { + IFLA_IPTUN_ENCAP_DPORT, + IFLA_IPTUN_COLLECT_METADATA, + IFLA_IPTUN_FWMARK, ++ IFLA_IPTUN_FMRS, + __IFLA_IPTUN_MAX, + }; + #define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1) + ++enum { ++ IFLA_IPTUN_FMR_UNSPEC, ++ IFLA_IPTUN_FMR_IP6_PREFIX, ++ IFLA_IPTUN_FMR_IP4_PREFIX, ++ IFLA_IPTUN_FMR_IP6_PREFIX_LEN, ++ IFLA_IPTUN_FMR_IP4_PREFIX_LEN, ++ IFLA_IPTUN_FMR_EA_LEN, ++ IFLA_IPTUN_FMR_OFFSET, ++ __IFLA_IPTUN_FMR_MAX, ++}; ++#define IFLA_IPTUN_FMR_MAX (__IFLA_IPTUN_FMR_MAX - 1) ++ + enum tunnel_encap_types { + TUNNEL_ENCAP_NONE, + TUNNEL_ENCAP_FOU, +--- a/net/ipv6/ip6_tunnel.c ++++ b/net/ipv6/ip6_tunnel.c +@@ -11,6 +11,9 @@ + * linux/net/ipv6/sit.c and linux/net/ipv4/ipip.c + * + * RFC 2473 ++ * ++ * Changes: ++ * Steven Barth : MAP-E FMR support + */ + + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +@@ -67,9 +70,9 @@ static bool log_ecn_error = true; + module_param(log_ecn_error, bool, 0644); + MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); + +-static u32 HASH(const struct in6_addr *addr1, const struct in6_addr *addr2) ++static u32 HASH(const struct in6_addr *addr) + { +- u32 hash = ipv6_addr_hash(addr1) ^ ipv6_addr_hash(addr2); ++ u32 hash = ipv6_addr_hash(addr); + + return hash_32(hash, IP6_TUNNEL_HASH_SIZE_SHIFT); + } +@@ -114,17 +117,33 @@ static struct ip6_tnl * + ip6_tnl_lookup(struct net *net, int link, + const struct in6_addr *remote, const struct in6_addr *local) + { +- unsigned int hash = HASH(remote, local); ++ unsigned int hash = HASH(local); + struct ip6_tnl *t, *cand = NULL; + struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); + struct in6_addr any; + + for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) { + if (!ipv6_addr_equal(local, &t->parms.laddr) || +- !ipv6_addr_equal(remote, &t->parms.raddr) || + !(t->dev->flags & IFF_UP)) + continue; + ++ if (!ipv6_addr_equal(remote, &t->parms.raddr)) { ++ struct __ip6_tnl_fmr *fmr; ++ bool found = false; ++ ++ for (fmr = t->parms.fmrs; fmr; fmr = fmr->next) { ++ if (!ipv6_prefix_equal(remote, &fmr->ip6_prefix, ++ fmr->ip6_prefix_len)) ++ continue; ++ ++ found = true; ++ break; ++ } ++ ++ if (!found) ++ continue; ++ } ++ + if (link == t->parms.link) + return t; + else +@@ -132,7 +151,7 @@ ip6_tnl_lookup(struct net *net, int link + } + + memset(&any, 0, sizeof(any)); +- hash = HASH(&any, local); ++ hash = HASH(local); + for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) { + if (!ipv6_addr_equal(local, &t->parms.laddr) || + !ipv6_addr_any(&t->parms.raddr) || +@@ -145,7 +164,7 @@ ip6_tnl_lookup(struct net *net, int link + cand = t; + } + +- hash = HASH(remote, &any); ++ hash = HASH(&any); + for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) { + if (!ipv6_addr_equal(remote, &t->parms.raddr) || + !ipv6_addr_any(&t->parms.laddr) || +@@ -194,7 +213,7 @@ ip6_tnl_bucket(struct ip6_tnl_net *ip6n, + + if (!ipv6_addr_any(remote) || !ipv6_addr_any(local)) { + prio = 1; +- h = HASH(remote, local); ++ h = HASH(local); + } + return &ip6n->tnls[prio][h]; + } +@@ -376,6 +395,12 @@ ip6_tnl_dev_uninit(struct net_device *de + struct net *net = t->net; + struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); + ++ while (t->parms.fmrs) { ++ struct __ip6_tnl_fmr *next = t->parms.fmrs->next; ++ kfree(t->parms.fmrs); ++ t->parms.fmrs = next; ++ } ++ + if (dev == ip6n->fb_tnl_dev) + RCU_INIT_POINTER(ip6n->tnls_wc[0], NULL); + else +@@ -788,6 +813,107 @@ int ip6_tnl_rcv_ctl(struct ip6_tnl *t, + } + EXPORT_SYMBOL_GPL(ip6_tnl_rcv_ctl); + ++/** ++ * ip4ip6_fmr_calc - calculate target / source IPv6-address based on FMR ++ * @dest: destination IPv6 address buffer ++ * @skb: received socket buffer ++ * @fmr: MAP FMR ++ * @xmit: Calculate for xmit or rcv ++ **/ ++static void ip4ip6_fmr_calc(struct in6_addr *dest, ++ const struct iphdr *iph, const uint8_t *end, ++ const struct __ip6_tnl_fmr *fmr, bool xmit) ++{ ++ int psidlen = fmr->ea_len - (32 - fmr->ip4_prefix_len); ++ u8 *portp = NULL; ++ bool use_dest_addr; ++ const struct iphdr *dsth = iph; ++ ++ if ((u8*)dsth >= end) ++ return; ++ ++ /* find significant IP header */ ++ if (iph->protocol == IPPROTO_ICMP) { ++ struct icmphdr *ih = (struct icmphdr*)(((u8*)dsth) + dsth->ihl * 4); ++ if (ih && ((u8*)&ih[1]) <= end && ( ++ ih->type == ICMP_DEST_UNREACH || ++ ih->type == ICMP_SOURCE_QUENCH || ++ ih->type == ICMP_TIME_EXCEEDED || ++ ih->type == ICMP_PARAMETERPROB || ++ ih->type == ICMP_REDIRECT)) ++ dsth = (const struct iphdr*)&ih[1]; ++ } ++ ++ /* in xmit-path use dest port by default and source port only if ++ this is an ICMP reply to something else; vice versa in rcv-path */ ++ use_dest_addr = (xmit && dsth == iph) || (!xmit && dsth != iph); ++ ++ /* get dst port */ ++ if (((u8*)&dsth[1]) <= end && ( ++ dsth->protocol == IPPROTO_UDP || ++ dsth->protocol == IPPROTO_TCP || ++ dsth->protocol == IPPROTO_SCTP || ++ dsth->protocol == IPPROTO_DCCP)) { ++ /* for UDP, TCP, SCTP and DCCP source and dest port ++ follow IPv4 header directly */ ++ portp = ((u8*)dsth) + dsth->ihl * 4; ++ ++ if (use_dest_addr) ++ portp += sizeof(u16); ++ } else if (iph->protocol == IPPROTO_ICMP) { ++ struct icmphdr *ih = (struct icmphdr*)(((u8*)dsth) + dsth->ihl * 4); ++ ++ /* use icmp identifier as port */ ++ if (((u8*)&ih) <= end && ( ++ (use_dest_addr && ( ++ ih->type == ICMP_ECHOREPLY || ++ ih->type == ICMP_TIMESTAMPREPLY || ++ ih->type == ICMP_INFO_REPLY || ++ ih->type == ICMP_ADDRESSREPLY)) || ++ (!use_dest_addr && ( ++ ih->type == ICMP_ECHO || ++ ih->type == ICMP_TIMESTAMP || ++ ih->type == ICMP_INFO_REQUEST || ++ ih->type == ICMP_ADDRESS) ++ ))) ++ portp = (u8*)&ih->un.echo.id; ++ } ++ ++ if ((portp && &portp[2] <= end) || psidlen == 0) { ++ int frombyte = fmr->ip6_prefix_len / 8; ++ int fromrem = fmr->ip6_prefix_len % 8; ++ int bytes = sizeof(struct in6_addr) - frombyte; ++ const u32 *addr = (use_dest_addr) ? &iph->daddr : &iph->saddr; ++ u64 eabits = ((u64)ntohl(*addr)) << (32 + fmr->ip4_prefix_len); ++ u64 t = 0; ++ ++ /* extract PSID from port and add it to eabits */ ++ u16 psidbits = 0; ++ if (psidlen > 0) { ++ psidbits = ((u16)portp[0]) << 8 | ((u16)portp[1]); ++ psidbits >>= 16 - psidlen - fmr->offset; ++ psidbits = (u16)(psidbits << (16 - psidlen)); ++ eabits |= ((u64)psidbits) << (48 - (fmr->ea_len - psidlen)); ++ } ++ ++ /* rewrite destination address */ ++ *dest = fmr->ip6_prefix; ++ memcpy(&dest->s6_addr[10], addr, sizeof(*addr)); ++ dest->s6_addr16[7] = htons(psidbits >> (16 - psidlen)); ++ ++ if (bytes > sizeof(u64)) ++ bytes = sizeof(u64); ++ ++ /* insert eabits */ ++ memcpy(&t, &dest->s6_addr[frombyte], bytes); ++ t = be64_to_cpu(t) & ~(((((u64)1) << fmr->ea_len) - 1) ++ << (64 - fmr->ea_len - fromrem)); ++ t = cpu_to_be64(t | (eabits >> fromrem)); ++ memcpy(&dest->s6_addr[frombyte], &t, bytes); ++ } ++} ++ ++ + static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb, + const struct tnl_ptk_info *tpi, + struct metadata_dst *tun_dst, +@@ -855,6 +981,27 @@ static int __ip6_tnl_rcv(struct ip6_tnl + + memset(skb->cb, 0, sizeof(struct inet6_skb_parm)); + ++ if (tpi->proto == htons(ETH_P_IP) && tunnel->parms.fmrs && ++ !ipv6_addr_equal(&ipv6h->saddr, &tunnel->parms.raddr)) { ++ /* Packet didn't come from BR, so lookup FMR */ ++ struct __ip6_tnl_fmr *fmr; ++ struct in6_addr expected = tunnel->parms.raddr; ++ for (fmr = tunnel->parms.fmrs; fmr; fmr = fmr->next) ++ if (ipv6_prefix_equal(&ipv6h->saddr, ++ &fmr->ip6_prefix, fmr->ip6_prefix_len)) ++ break; ++ ++ /* Check that IPv6 matches IPv4 source to prevent spoofing */ ++ if (fmr) ++ ip4ip6_fmr_calc(&expected, ip_hdr(skb), ++ skb_tail_pointer(skb), fmr, false); ++ ++ if (!ipv6_addr_equal(&ipv6h->saddr, &expected)) { ++ rcu_read_unlock(); ++ goto drop; ++ } ++ } ++ + __skb_tunnel_rx(skb, tunnel->dev, tunnel->net); + + err = dscp_ecn_decapsulate(tunnel, ipv6h, skb); +@@ -1002,6 +1149,7 @@ static void init_tel_txopt(struct ipv6_t + opt->ops.opt_nflen = 8; + } + ++ + /** + * ip6_tnl_addr_conflict - compare packet addresses to tunnel's own + * @t: the outgoing tunnel device +@@ -1293,6 +1441,7 @@ ipxip6_tnl_xmit(struct sk_buff *skb, str + u8 protocol) + { + struct ip6_tnl *t = netdev_priv(dev); ++ struct __ip6_tnl_fmr *fmr; + struct ipv6hdr *ipv6h; + const struct iphdr *iph; + int encap_limit = -1; +@@ -1392,6 +1541,18 @@ ipxip6_tnl_xmit(struct sk_buff *skb, str + fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL); + dsfield = INET_ECN_encapsulate(dsfield, orig_dsfield); + ++ /* try to find matching FMR */ ++ for (fmr = t->parms.fmrs; fmr; fmr = fmr->next) { ++ unsigned mshift = 32 - fmr->ip4_prefix_len; ++ if (ntohl(fmr->ip4_prefix.s_addr) >> mshift == ++ ntohl(ip_hdr(skb)->daddr) >> mshift) ++ break; ++ } ++ ++ /* change dstaddr according to FMR */ ++ if (fmr) ++ ip4ip6_fmr_calc(&fl6.daddr, ip_hdr(skb), skb_tail_pointer(skb), fmr, true); ++ + if (iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6)) + return -1; + +@@ -1545,6 +1706,14 @@ ip6_tnl_change(struct ip6_tnl *t, const + t->parms.link = p->link; + t->parms.proto = p->proto; + t->parms.fwmark = p->fwmark; ++ ++ while (t->parms.fmrs) { ++ struct __ip6_tnl_fmr *next = t->parms.fmrs->next; ++ kfree(t->parms.fmrs); ++ t->parms.fmrs = next; ++ } ++ t->parms.fmrs = p->fmrs; ++ + dst_cache_reset(&t->dst_cache); + ip6_tnl_link_config(t); + } +@@ -1579,6 +1748,7 @@ ip6_tnl_parm_from_user(struct __ip6_tnl_ + p->flowinfo = u->flowinfo; + p->link = u->link; + p->proto = u->proto; ++ p->fmrs = NULL; + memcpy(p->name, u->name, sizeof(u->name)); + } + +@@ -1965,6 +2135,15 @@ static int ip6_tnl_validate(struct nlatt + return 0; + } + ++static const struct nla_policy ip6_tnl_fmr_policy[IFLA_IPTUN_FMR_MAX + 1] = { ++ [IFLA_IPTUN_FMR_IP6_PREFIX] = { .len = sizeof(struct in6_addr) }, ++ [IFLA_IPTUN_FMR_IP4_PREFIX] = { .len = sizeof(struct in_addr) }, ++ [IFLA_IPTUN_FMR_IP6_PREFIX_LEN] = { .type = NLA_U8 }, ++ [IFLA_IPTUN_FMR_IP4_PREFIX_LEN] = { .type = NLA_U8 }, ++ [IFLA_IPTUN_FMR_EA_LEN] = { .type = NLA_U8 }, ++ [IFLA_IPTUN_FMR_OFFSET] = { .type = NLA_U8 } ++}; ++ + static void ip6_tnl_netlink_parms(struct nlattr *data[], + struct __ip6_tnl_parm *parms) + { +@@ -2002,6 +2181,46 @@ static void ip6_tnl_netlink_parms(struct + + if (data[IFLA_IPTUN_FWMARK]) + parms->fwmark = nla_get_u32(data[IFLA_IPTUN_FWMARK]); ++ ++ if (data[IFLA_IPTUN_FMRS]) { ++ unsigned rem; ++ struct nlattr *fmr; ++ nla_for_each_nested(fmr, data[IFLA_IPTUN_FMRS], rem) { ++ struct nlattr *fmrd[IFLA_IPTUN_FMR_MAX + 1], *c; ++ struct __ip6_tnl_fmr *nfmr; ++ ++ nla_parse_nested(fmrd, IFLA_IPTUN_FMR_MAX, ++ fmr, ip6_tnl_fmr_policy, NULL); ++ ++ if (!(nfmr = kzalloc(sizeof(*nfmr), GFP_KERNEL))) ++ continue; ++ ++ nfmr->offset = 6; ++ ++ if ((c = fmrd[IFLA_IPTUN_FMR_IP6_PREFIX])) ++ nla_memcpy(&nfmr->ip6_prefix, fmrd[IFLA_IPTUN_FMR_IP6_PREFIX], ++ sizeof(nfmr->ip6_prefix)); ++ ++ if ((c = fmrd[IFLA_IPTUN_FMR_IP4_PREFIX])) ++ nla_memcpy(&nfmr->ip4_prefix, fmrd[IFLA_IPTUN_FMR_IP4_PREFIX], ++ sizeof(nfmr->ip4_prefix)); ++ ++ if ((c = fmrd[IFLA_IPTUN_FMR_IP6_PREFIX_LEN])) ++ nfmr->ip6_prefix_len = nla_get_u8(c); ++ ++ if ((c = fmrd[IFLA_IPTUN_FMR_IP4_PREFIX_LEN])) ++ nfmr->ip4_prefix_len = nla_get_u8(c); ++ ++ if ((c = fmrd[IFLA_IPTUN_FMR_EA_LEN])) ++ nfmr->ea_len = nla_get_u8(c); ++ ++ if ((c = fmrd[IFLA_IPTUN_FMR_OFFSET])) ++ nfmr->offset = nla_get_u8(c); ++ ++ nfmr->next = parms->fmrs; ++ parms->fmrs = nfmr; ++ } ++ } + } + + static int ip6_tnl_newlink(struct net *src_net, struct net_device *dev, +@@ -2085,6 +2304,12 @@ static void ip6_tnl_dellink(struct net_d + + static size_t ip6_tnl_get_size(const struct net_device *dev) + { ++ const struct ip6_tnl *t = netdev_priv(dev); ++ struct __ip6_tnl_fmr *c; ++ int fmrs = 0; ++ for (c = t->parms.fmrs; c; c = c->next) ++ ++fmrs; ++ + return + /* IFLA_IPTUN_LINK */ + nla_total_size(4) + +@@ -2114,6 +2339,24 @@ static size_t ip6_tnl_get_size(const str + nla_total_size(0) + + /* IFLA_IPTUN_FWMARK */ + nla_total_size(4) + ++ /* IFLA_IPTUN_FMRS */ ++ nla_total_size(0) + ++ ( ++ /* nest */ ++ nla_total_size(0) + ++ /* IFLA_IPTUN_FMR_IP6_PREFIX */ ++ nla_total_size(sizeof(struct in6_addr)) + ++ /* IFLA_IPTUN_FMR_IP4_PREFIX */ ++ nla_total_size(sizeof(struct in_addr)) + ++ /* IFLA_IPTUN_FMR_EA_LEN */ ++ nla_total_size(1) + ++ /* IFLA_IPTUN_FMR_IP6_PREFIX_LEN */ ++ nla_total_size(1) + ++ /* IFLA_IPTUN_FMR_IP4_PREFIX_LEN */ ++ nla_total_size(1) + ++ /* IFLA_IPTUN_FMR_OFFSET */ ++ nla_total_size(1) ++ ) * fmrs + + 0; + } + +@@ -2121,6 +2364,9 @@ static int ip6_tnl_fill_info(struct sk_b + { + struct ip6_tnl *tunnel = netdev_priv(dev); + struct __ip6_tnl_parm *parm = &tunnel->parms; ++ struct __ip6_tnl_fmr *c; ++ int fmrcnt = 0; ++ struct nlattr *fmrs; + + if (nla_put_u32(skb, IFLA_IPTUN_LINK, parm->link) || + nla_put_in6_addr(skb, IFLA_IPTUN_LOCAL, &parm->laddr) || +@@ -2130,9 +2376,27 @@ static int ip6_tnl_fill_info(struct sk_b + nla_put_be32(skb, IFLA_IPTUN_FLOWINFO, parm->flowinfo) || + nla_put_u32(skb, IFLA_IPTUN_FLAGS, parm->flags) || + nla_put_u8(skb, IFLA_IPTUN_PROTO, parm->proto) || +- nla_put_u32(skb, IFLA_IPTUN_FWMARK, parm->fwmark)) ++ nla_put_u32(skb, IFLA_IPTUN_FWMARK, parm->fwmark) || ++ !(fmrs = nla_nest_start(skb, IFLA_IPTUN_FMRS))) + goto nla_put_failure; + ++ for (c = parm->fmrs; c; c = c->next) { ++ struct nlattr *fmr = nla_nest_start(skb, ++fmrcnt); ++ if (!fmr || ++ nla_put(skb, IFLA_IPTUN_FMR_IP6_PREFIX, ++ sizeof(c->ip6_prefix), &c->ip6_prefix) || ++ nla_put(skb, IFLA_IPTUN_FMR_IP4_PREFIX, ++ sizeof(c->ip4_prefix), &c->ip4_prefix) || ++ nla_put_u8(skb, IFLA_IPTUN_FMR_IP6_PREFIX_LEN, c->ip6_prefix_len) || ++ nla_put_u8(skb, IFLA_IPTUN_FMR_IP4_PREFIX_LEN, c->ip4_prefix_len) || ++ nla_put_u8(skb, IFLA_IPTUN_FMR_EA_LEN, c->ea_len) || ++ nla_put_u8(skb, IFLA_IPTUN_FMR_OFFSET, c->offset)) ++ goto nla_put_failure; ++ ++ nla_nest_end(skb, fmr); ++ } ++ nla_nest_end(skb, fmrs); ++ + if (nla_put_u16(skb, IFLA_IPTUN_ENCAP_TYPE, tunnel->encap.type) || + nla_put_be16(skb, IFLA_IPTUN_ENCAP_SPORT, tunnel->encap.sport) || + nla_put_be16(skb, IFLA_IPTUN_ENCAP_DPORT, tunnel->encap.dport) || +@@ -2172,6 +2436,7 @@ static const struct nla_policy ip6_tnl_p + [IFLA_IPTUN_ENCAP_DPORT] = { .type = NLA_U16 }, + [IFLA_IPTUN_COLLECT_METADATA] = { .type = NLA_FLAG }, + [IFLA_IPTUN_FWMARK] = { .type = NLA_U32 }, ++ [IFLA_IPTUN_FMRS] = { .type = NLA_NESTED }, + }; + + static struct rtnl_link_ops ip6_link_ops __read_mostly = { diff --git a/target/linux/generic/pending-6.6/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch b/target/linux/generic/pending-6.6/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch new file mode 100644 index 0000000000..72e8de2f1d --- /dev/null +++ b/target/linux/generic/pending-6.6/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch @@ -0,0 +1,263 @@ +From: Jonas Gorski +Subject: ipv6: allow rejecting with "source address failed policy" + +RFC6204 L-14 requires rejecting traffic from invalid addresses with +ICMPv6 Destination Unreachable, Code 5 (Source address failed ingress/ +egress policy) on the LAN side, so add an appropriate rule for that. + +Signed-off-by: Jonas Gorski +--- + include/net/netns/ipv6.h | 1 + + include/uapi/linux/fib_rules.h | 4 +++ + include/uapi/linux/rtnetlink.h | 1 + + net/ipv4/fib_semantics.c | 4 +++ + net/ipv4/fib_trie.c | 1 + + net/ipv4/ipmr.c | 1 + + net/ipv6/fib6_rules.c | 4 +++ + net/ipv6/ip6mr.c | 2 ++ + net/ipv6/route.c | 58 +++++++++++++++++++++++++++++++++++++++++- + 9 files changed, 75 insertions(+), 1 deletion(-) + +--- a/include/net/netns/ipv6.h ++++ b/include/net/netns/ipv6.h +@@ -85,6 +85,7 @@ struct netns_ipv6 { + unsigned int fib6_routes_require_src; + #endif + struct rt6_info *ip6_prohibit_entry; ++ struct rt6_info *ip6_policy_failed_entry; + struct rt6_info *ip6_blk_hole_entry; + struct fib6_table *fib6_local_tbl; + struct fib_rules_ops *fib6_rules_ops; +--- a/include/uapi/linux/fib_rules.h ++++ b/include/uapi/linux/fib_rules.h +@@ -82,6 +82,10 @@ enum { + FR_ACT_BLACKHOLE, /* Drop without notification */ + FR_ACT_UNREACHABLE, /* Drop with ENETUNREACH */ + FR_ACT_PROHIBIT, /* Drop with EACCES */ ++ FR_ACT_RES9, ++ FR_ACT_RES10, ++ FR_ACT_RES11, ++ FR_ACT_POLICY_FAILED, /* Drop with EACCES */ + __FR_ACT_MAX, + }; + +--- a/include/uapi/linux/rtnetlink.h ++++ b/include/uapi/linux/rtnetlink.h +@@ -265,6 +265,7 @@ enum { + RTN_THROW, /* Not in this table */ + RTN_NAT, /* Translate this address */ + RTN_XRESOLVE, /* Use external resolver */ ++ RTN_POLICY_FAILED, /* Failed ingress/egress policy */ + __RTN_MAX + }; + +--- a/net/ipv4/fib_semantics.c ++++ b/net/ipv4/fib_semantics.c +@@ -145,6 +145,10 @@ const struct fib_prop fib_props[RTN_MAX + .error = -EINVAL, + .scope = RT_SCOPE_NOWHERE, + }, ++ [RTN_POLICY_FAILED] = { ++ .error = -EACCES, ++ .scope = RT_SCOPE_UNIVERSE, ++ }, + }; + + static void rt_fibinfo_free(struct rtable __rcu **rtp) +--- a/net/ipv4/fib_trie.c ++++ b/net/ipv4/fib_trie.c +@@ -2783,6 +2783,7 @@ static const char *const rtn_type_names[ + [RTN_THROW] = "THROW", + [RTN_NAT] = "NAT", + [RTN_XRESOLVE] = "XRESOLVE", ++ [RTN_POLICY_FAILED] = "POLICY_FAILED", + }; + + static inline const char *rtn_type(char *buf, size_t len, unsigned int t) +--- a/net/ipv4/ipmr.c ++++ b/net/ipv4/ipmr.c +@@ -180,6 +180,7 @@ static int ipmr_rule_action(struct fib_r + case FR_ACT_UNREACHABLE: + return -ENETUNREACH; + case FR_ACT_PROHIBIT: ++ case FR_ACT_POLICY_FAILED: + return -EACCES; + case FR_ACT_BLACKHOLE: + default: +--- a/net/ipv6/fib6_rules.c ++++ b/net/ipv6/fib6_rules.c +@@ -221,6 +221,10 @@ static int __fib6_rule_action(struct fib + err = -EACCES; + rt = net->ipv6.ip6_prohibit_entry; + goto discard_pkt; ++ case FR_ACT_POLICY_FAILED: ++ err = -EACCES; ++ rt = net->ipv6.ip6_policy_failed_entry; ++ goto discard_pkt; + } + + tb_id = fib_rule_get_table(rule, arg); +--- a/net/ipv6/ip6mr.c ++++ b/net/ipv6/ip6mr.c +@@ -170,6 +170,8 @@ static int ip6mr_rule_action(struct fib_ + return -ENETUNREACH; + case FR_ACT_PROHIBIT: + return -EACCES; ++ case FR_ACT_POLICY_FAILED: ++ return -EACCES; + case FR_ACT_BLACKHOLE: + default: + return -EINVAL; +--- a/net/ipv6/route.c ++++ b/net/ipv6/route.c +@@ -97,6 +97,8 @@ static int ip6_pkt_discard(struct sk_bu + static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb); + static int ip6_pkt_prohibit(struct sk_buff *skb); + static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb); ++static int ip6_pkt_policy_failed(struct sk_buff *skb); ++static int ip6_pkt_policy_failed_out(struct net *net, struct sock *sk, struct sk_buff *skb); + static void ip6_link_failure(struct sk_buff *skb); + static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, + struct sk_buff *skb, u32 mtu, +@@ -317,6 +319,18 @@ static const struct rt6_info ip6_prohibi + .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), + }; + ++static const struct rt6_info ip6_policy_failed_entry_template = { ++ .dst = { ++ .__refcnt = ATOMIC_INIT(1), ++ .__use = 1, ++ .obsolete = DST_OBSOLETE_FORCE_CHK, ++ .error = -EACCES, ++ .input = ip6_pkt_policy_failed, ++ .output = ip6_pkt_policy_failed_out, ++ }, ++ .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), ++}; ++ + static const struct rt6_info ip6_blk_hole_entry_template = { + .dst = { + .__refcnt = ATOMIC_INIT(1), +@@ -1039,6 +1053,7 @@ static const int fib6_prop[RTN_MAX + 1] + [RTN_BLACKHOLE] = -EINVAL, + [RTN_UNREACHABLE] = -EHOSTUNREACH, + [RTN_PROHIBIT] = -EACCES, ++ [RTN_POLICY_FAILED] = -EACCES, + [RTN_THROW] = -EAGAIN, + [RTN_NAT] = -EINVAL, + [RTN_XRESOLVE] = -EINVAL, +@@ -1074,6 +1089,10 @@ static void ip6_rt_init_dst_reject(struc + rt->dst.output = ip6_pkt_prohibit_out; + rt->dst.input = ip6_pkt_prohibit; + break; ++ case RTN_POLICY_FAILED: ++ rt->dst.output = ip6_pkt_policy_failed_out; ++ rt->dst.input = ip6_pkt_policy_failed; ++ break; + case RTN_THROW: + case RTN_UNREACHABLE: + default: +@@ -4540,6 +4559,17 @@ static int ip6_pkt_prohibit_out(struct n + return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES); + } + ++static int ip6_pkt_policy_failed(struct sk_buff *skb) ++{ ++ return ip6_pkt_drop(skb, ICMPV6_POLICY_FAIL, IPSTATS_MIB_INNOROUTES); ++} ++ ++static int ip6_pkt_policy_failed_out(struct net *net, struct sock *sk, struct sk_buff *skb) ++{ ++ skb->dev = skb_dst(skb)->dev; ++ return ip6_pkt_drop(skb, ICMPV6_POLICY_FAIL, IPSTATS_MIB_OUTNOROUTES); ++} ++ + /* + * Allocate a dst for local (unicast / anycast) address. + */ +@@ -5033,7 +5063,8 @@ static int rtm_to_fib6_config(struct sk_ + if (rtm->rtm_type == RTN_UNREACHABLE || + rtm->rtm_type == RTN_BLACKHOLE || + rtm->rtm_type == RTN_PROHIBIT || +- rtm->rtm_type == RTN_THROW) ++ rtm->rtm_type == RTN_THROW || ++ rtm->rtm_type == RTN_POLICY_FAILED) + cfg->fc_flags |= RTF_REJECT; + + if (rtm->rtm_type == RTN_LOCAL) +@@ -6287,6 +6318,8 @@ static int ip6_route_dev_notify(struct n + #ifdef CONFIG_IPV6_MULTIPLE_TABLES + net->ipv6.ip6_prohibit_entry->dst.dev = dev; + net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev); ++ net->ipv6.ip6_policy_failed_entry->dst.dev = dev; ++ net->ipv6.ip6_policy_failed_entry->rt6i_idev = in6_dev_get(dev); + net->ipv6.ip6_blk_hole_entry->dst.dev = dev; + net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev); + #endif +@@ -6298,6 +6331,7 @@ static int ip6_route_dev_notify(struct n + in6_dev_put_clear(&net->ipv6.ip6_null_entry->rt6i_idev); + #ifdef CONFIG_IPV6_MULTIPLE_TABLES + in6_dev_put_clear(&net->ipv6.ip6_prohibit_entry->rt6i_idev); ++ in6_dev_put_clear(&net->ipv6.ip6_policy_failed_entry->rt6i_idev); + in6_dev_put_clear(&net->ipv6.ip6_blk_hole_entry->rt6i_idev); + #endif + } +@@ -6489,6 +6523,8 @@ static int __net_init ip6_route_net_init + + #ifdef CONFIG_IPV6_MULTIPLE_TABLES + net->ipv6.fib6_has_custom_rules = false; ++ ++ + net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, + sizeof(*net->ipv6.ip6_prohibit_entry), + GFP_KERNEL); +@@ -6499,11 +6535,21 @@ static int __net_init ip6_route_net_init + ip6_template_metrics, true); + INIT_LIST_HEAD(&net->ipv6.ip6_prohibit_entry->rt6i_uncached); + ++ net->ipv6.ip6_policy_failed_entry = ++ kmemdup(&ip6_policy_failed_entry_template, ++ sizeof(*net->ipv6.ip6_policy_failed_entry), GFP_KERNEL); ++ if (!net->ipv6.ip6_policy_failed_entry) ++ goto out_ip6_prohibit_entry; ++ net->ipv6.ip6_policy_failed_entry->dst.ops = &net->ipv6.ip6_dst_ops; ++ dst_init_metrics(&net->ipv6.ip6_policy_failed_entry->dst, ++ ip6_template_metrics, true); ++ INIT_LIST_HEAD(&net->ipv6.ip6_policy_failed_entry->rt6i_uncached); ++ + net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, + sizeof(*net->ipv6.ip6_blk_hole_entry), + GFP_KERNEL); + if (!net->ipv6.ip6_blk_hole_entry) +- goto out_ip6_prohibit_entry; ++ goto out_ip6_policy_failed_entry; + net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; + dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, + ip6_template_metrics, true); +@@ -6530,6 +6576,8 @@ out: + return ret; + + #ifdef CONFIG_IPV6_MULTIPLE_TABLES ++out_ip6_policy_failed_entry: ++ kfree(net->ipv6.ip6_policy_failed_entry); + out_ip6_prohibit_entry: + kfree(net->ipv6.ip6_prohibit_entry); + out_ip6_null_entry: +@@ -6549,6 +6597,7 @@ static void __net_exit ip6_route_net_exi + kfree(net->ipv6.ip6_null_entry); + #ifdef CONFIG_IPV6_MULTIPLE_TABLES + kfree(net->ipv6.ip6_prohibit_entry); ++ kfree(net->ipv6.ip6_policy_failed_entry); + kfree(net->ipv6.ip6_blk_hole_entry); + #endif + dst_entries_destroy(&net->ipv6.ip6_dst_ops); +@@ -6632,6 +6681,9 @@ void __init ip6_route_init_special_entri + init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); + init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev; + init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); ++ init_net.ipv6.ip6_policy_failed_entry->dst.dev = init_net.loopback_dev; ++ init_net.ipv6.ip6_policy_failed_entry->rt6i_idev = ++ in6_dev_get(init_net.loopback_dev); + #endif + } + diff --git a/target/linux/generic/pending-6.6/671-net-provide-defines-for-_POLICY_FAILED-until-all-cod.patch b/target/linux/generic/pending-6.6/671-net-provide-defines-for-_POLICY_FAILED-until-all-cod.patch new file mode 100644 index 0000000000..94416a5d70 --- /dev/null +++ b/target/linux/generic/pending-6.6/671-net-provide-defines-for-_POLICY_FAILED-until-all-cod.patch @@ -0,0 +1,50 @@ +From: Jonas Gorski +Subject: net: provide defines for _POLICY_FAILED until all code is updated + +Upstream introduced ICMPV6_POLICY_FAIL for code 5 of destination +unreachable, conflicting with our name. + +Add appropriate defines to allow our code to build with the new +name until we have updated our local patches for older kernels +and userspace packages. + +Signed-off-by: Jonas Gorski +--- + include/uapi/linux/fib_rules.h | 2 ++ + include/uapi/linux/icmpv6.h | 2 ++ + include/uapi/linux/rtnetlink.h | 2 ++ + 3 files changed, 6 insertions(+) + +--- a/include/uapi/linux/fib_rules.h ++++ b/include/uapi/linux/fib_rules.h +@@ -89,6 +89,8 @@ enum { + __FR_ACT_MAX, + }; + ++#define FR_ACT_FAILED_POLICY FR_ACT_POLICY_FAILED ++ + #define FR_ACT_MAX (__FR_ACT_MAX - 1) + + #endif +--- a/include/uapi/linux/icmpv6.h ++++ b/include/uapi/linux/icmpv6.h +@@ -126,6 +126,8 @@ struct icmp6hdr { + #define ICMPV6_POLICY_FAIL 5 + #define ICMPV6_REJECT_ROUTE 6 + ++#define ICMPV6_FAILED_POLICY ICMPV6_POLICY_FAIL ++ + /* + * Codes for Time Exceeded + */ +--- a/include/uapi/linux/rtnetlink.h ++++ b/include/uapi/linux/rtnetlink.h +@@ -269,6 +269,8 @@ enum { + __RTN_MAX + }; + ++#define RTN_FAILED_POLICY RTN_POLICY_FAILED ++ + #define RTN_MAX (__RTN_MAX - 1) + + diff --git a/target/linux/generic/pending-6.6/680-NET-skip-GRO-for-foreign-MAC-addresses.patch b/target/linux/generic/pending-6.6/680-NET-skip-GRO-for-foreign-MAC-addresses.patch new file mode 100644 index 0000000000..11850c0418 --- /dev/null +++ b/target/linux/generic/pending-6.6/680-NET-skip-GRO-for-foreign-MAC-addresses.patch @@ -0,0 +1,151 @@ +From: Felix Fietkau +Subject: net: replace GRO optimization patch with a new one that supports VLANs/bridges with different MAC addresses + +Signed-off-by: Felix Fietkau +--- + include/linux/netdevice.h | 2 ++ + include/linux/skbuff.h | 3 ++- + net/core/dev.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++ + net/ethernet/eth.c | 18 +++++++++++++++++- + 4 files changed, 69 insertions(+), 2 deletions(-) + +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -2157,6 +2157,8 @@ struct net_device { + struct netdev_hw_addr_list mc; + struct netdev_hw_addr_list dev_addrs; + ++ unsigned char local_addr_mask[MAX_ADDR_LEN]; ++ + #ifdef CONFIG_SYSFS + struct kset *queues_kset; + #endif +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -972,6 +972,7 @@ struct sk_buff { + #ifdef CONFIG_IPV6_NDISC_NODETYPE + __u8 ndisc_nodetype:2; + #endif ++ __u8 gro_skip:1; + + __u8 ipvs_property:1; + __u8 inner_protocol_type:1; +--- a/net/core/gro.c ++++ b/net/core/gro.c +@@ -491,6 +491,9 @@ static enum gro_result dev_gro_receive(s + int same_flow; + int grow; + ++ if (skb->gro_skip) ++ goto normal; ++ + if (netif_elide_gro(skb->dev)) + goto normal; + +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -7625,6 +7625,48 @@ static void __netdev_adjacent_dev_unlink + &upper_dev->adj_list.lower); + } + ++static void __netdev_addr_mask(unsigned char *mask, const unsigned char *addr, ++ struct net_device *dev) ++{ ++ int i; ++ ++ for (i = 0; i < dev->addr_len; i++) ++ mask[i] |= addr[i] ^ dev->dev_addr[i]; ++} ++ ++static void __netdev_upper_mask(unsigned char *mask, struct net_device *dev, ++ struct net_device *lower) ++{ ++ struct net_device *cur; ++ struct list_head *iter; ++ ++ netdev_for_each_upper_dev_rcu(dev, cur, iter) { ++ __netdev_addr_mask(mask, cur->dev_addr, lower); ++ __netdev_upper_mask(mask, cur, lower); ++ } ++} ++ ++static void __netdev_update_addr_mask(struct net_device *dev) ++{ ++ unsigned char mask[MAX_ADDR_LEN]; ++ struct net_device *cur; ++ struct list_head *iter; ++ ++ memset(mask, 0, sizeof(mask)); ++ __netdev_upper_mask(mask, dev, dev); ++ memcpy(dev->local_addr_mask, mask, dev->addr_len); ++ ++ netdev_for_each_lower_dev(dev, cur, iter) ++ __netdev_update_addr_mask(cur); ++} ++ ++static void netdev_update_addr_mask(struct net_device *dev) ++{ ++ rcu_read_lock(); ++ __netdev_update_addr_mask(dev); ++ rcu_read_unlock(); ++} ++ + static int __netdev_upper_dev_link(struct net_device *dev, + struct net_device *upper_dev, bool master, + void *upper_priv, void *upper_info, +@@ -7676,6 +7718,7 @@ static int __netdev_upper_dev_link(struc + if (ret) + return ret; + ++ netdev_update_addr_mask(dev); + ret = call_netdevice_notifiers_info(NETDEV_CHANGEUPPER, + &changeupper_info.info); + ret = notifier_to_errno(ret); +@@ -7772,6 +7815,7 @@ static void __netdev_upper_dev_unlink(st + + __netdev_adjacent_dev_unlink_neighbour(dev, upper_dev); + ++ netdev_update_addr_mask(dev); + call_netdevice_notifiers_info(NETDEV_CHANGEUPPER, + &changeupper_info.info); + +@@ -8824,6 +8868,7 @@ int dev_set_mac_address(struct net_devic + if (err) + return err; + dev->addr_assign_type = NET_ADDR_SET; ++ netdev_update_addr_mask(dev); + call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); + add_device_randomness(dev->dev_addr, dev->addr_len); + return 0; +--- a/net/ethernet/eth.c ++++ b/net/ethernet/eth.c +@@ -143,6 +143,18 @@ u32 eth_get_headlen(const struct net_dev + } + EXPORT_SYMBOL(eth_get_headlen); + ++static inline bool ++eth_check_local_mask(const void *addr1, const void *addr2, const void *mask) ++{ ++ const u16 *a1 = addr1; ++ const u16 *a2 = addr2; ++ const u16 *m = mask; ++ ++ return (((a1[0] ^ a2[0]) & ~m[0]) | ++ ((a1[1] ^ a2[1]) & ~m[1]) | ++ ((a1[2] ^ a2[2]) & ~m[2])); ++} ++ + /** + * eth_type_trans - determine the packet's protocol ID. + * @skb: received socket data +@@ -174,6 +186,10 @@ __be16 eth_type_trans(struct sk_buff *sk + } else { + skb->pkt_type = PACKET_OTHERHOST; + } ++ ++ if (eth_check_local_mask(eth->h_dest, dev->dev_addr, ++ dev->local_addr_mask)) ++ skb->gro_skip = 1; + } + + /* diff --git a/target/linux/generic/pending-6.6/683-of_net-add-mac-address-to-of-tree.patch b/target/linux/generic/pending-6.6/683-of_net-add-mac-address-to-of-tree.patch new file mode 100644 index 0000000000..03ee537fb8 --- /dev/null +++ b/target/linux/generic/pending-6.6/683-of_net-add-mac-address-to-of-tree.patch @@ -0,0 +1,75 @@ +From 8585756342caa6d27008d1ad0c18023e4211a40a Mon Sep 17 00:00:00 2001 +From: OpenWrt community +Date: Wed, 13 Jul 2022 12:22:48 +0200 +Subject: [PATCH] of/of_net: write back netdev MAC-address to device-tree + +The label-mac logic relies on the mac-address property of a netdev +devices of-node. However, the mac address can also be stored as a +different property or read from e.g. an mtd device. + +Create this node when reading a mac-address from OF if it does not +already exist and copy the mac-address used for the device to this +property. This way, the MAC address can be accessed using procfs. + +--- + net/core/of_net.c | 22 ++++++++++++++++++++++ + 1 file changed, 22 insertions(+) + +--- a/net/core/of_net.c ++++ b/net/core/of_net.c +@@ -95,6 +95,27 @@ static int of_get_mac_addr_nvmem(struct + return 0; + } + ++static int of_add_mac_address(struct device_node *np, u8* addr) ++{ ++ struct property *prop; ++ ++ prop = kzalloc(sizeof(*prop), GFP_KERNEL); ++ if (!prop) ++ return -ENOMEM; ++ ++ prop->name = "mac-address"; ++ prop->length = ETH_ALEN; ++ prop->value = kmemdup(addr, ETH_ALEN, GFP_KERNEL); ++ if (!prop->value || of_update_property(np, prop)) ++ goto free; ++ ++ return 0; ++free: ++ kfree(prop->value); ++ kfree(prop); ++ return -ENOMEM; ++} ++ + /** + * of_get_mac_address() + * @np: Caller's Device Node +@@ -130,17 +151,23 @@ int of_get_mac_address(struct device_nod + + ret = of_get_mac_addr(np, "mac-address", addr); + if (!ret) +- return 0; ++ goto found; + + ret = of_get_mac_addr(np, "local-mac-address", addr); + if (!ret) +- return 0; ++ goto found; + + ret = of_get_mac_addr(np, "address", addr); + if (!ret) +- return 0; ++ goto found; + +- return of_get_mac_addr_nvmem(np, addr); ++ ret = of_get_mac_addr_nvmem(np, addr); ++ if (ret) ++ return ret; ++ ++found: ++ ret = of_add_mac_address(np, addr); ++ return ret; + } + EXPORT_SYMBOL(of_get_mac_address); + diff --git a/target/linux/generic/pending-6.6/700-netfilter-nft_flow_offload-handle-netdevice-events-f.patch b/target/linux/generic/pending-6.6/700-netfilter-nft_flow_offload-handle-netdevice-events-f.patch new file mode 100644 index 0000000000..b335748705 --- /dev/null +++ b/target/linux/generic/pending-6.6/700-netfilter-nft_flow_offload-handle-netdevice-events-f.patch @@ -0,0 +1,110 @@ +From: Pablo Neira Ayuso +Date: Thu, 25 Jan 2018 12:58:55 +0100 +Subject: [PATCH] netfilter: nft_flow_offload: handle netdevice events from + nf_flow_table + +Move the code that deals with device events to the core. + +Signed-off-by: Pablo Neira Ayuso +--- + +--- a/net/netfilter/nf_flow_table_core.c ++++ b/net/netfilter/nf_flow_table_core.c +@@ -659,6 +659,23 @@ static struct pernet_operations nf_flow_ + .exit_batch = nf_flow_table_pernet_exit, + }; + ++static int nf_flow_table_netdev_event(struct notifier_block *this, ++ unsigned long event, void *ptr) ++{ ++ struct net_device *dev = netdev_notifier_info_to_dev(ptr); ++ ++ if (event != NETDEV_DOWN) ++ return NOTIFY_DONE; ++ ++ nf_flow_table_cleanup(dev); ++ ++ return NOTIFY_DONE; ++} ++ ++static struct notifier_block flow_offload_netdev_notifier = { ++ .notifier_call = nf_flow_table_netdev_event, ++}; ++ + static int __init nf_flow_table_module_init(void) + { + int ret; +@@ -671,8 +688,14 @@ static int __init nf_flow_table_module_i + if (ret) + goto out_offload; + ++ ret = register_netdevice_notifier(&flow_offload_netdev_notifier); ++ if (ret) ++ goto out_offload_init; ++ + return 0; + ++out_offload_init: ++ nf_flow_table_offload_exit(); + out_offload: + unregister_pernet_subsys(&nf_flow_table_net_ops); + return ret; +@@ -680,6 +703,7 @@ out_offload: + + static void __exit nf_flow_table_module_exit(void) + { ++ unregister_netdevice_notifier(&flow_offload_netdev_notifier); + nf_flow_table_offload_exit(); + unregister_pernet_subsys(&nf_flow_table_net_ops); + } +--- a/net/netfilter/nft_flow_offload.c ++++ b/net/netfilter/nft_flow_offload.c +@@ -479,47 +479,14 @@ static struct nft_expr_type nft_flow_off + .owner = THIS_MODULE, + }; + +-static int flow_offload_netdev_event(struct notifier_block *this, +- unsigned long event, void *ptr) +-{ +- struct net_device *dev = netdev_notifier_info_to_dev(ptr); +- +- if (event != NETDEV_DOWN) +- return NOTIFY_DONE; +- +- nf_flow_table_cleanup(dev); +- +- return NOTIFY_DONE; +-} +- +-static struct notifier_block flow_offload_netdev_notifier = { +- .notifier_call = flow_offload_netdev_event, +-}; +- + static int __init nft_flow_offload_module_init(void) + { +- int err; +- +- err = register_netdevice_notifier(&flow_offload_netdev_notifier); +- if (err) +- goto err; +- +- err = nft_register_expr(&nft_flow_offload_type); +- if (err < 0) +- goto register_expr; +- +- return 0; +- +-register_expr: +- unregister_netdevice_notifier(&flow_offload_netdev_notifier); +-err: +- return err; ++ return nft_register_expr(&nft_flow_offload_type); + } + + static void __exit nft_flow_offload_module_exit(void) + { + nft_unregister_expr(&nft_flow_offload_type); +- unregister_netdevice_notifier(&flow_offload_netdev_notifier); + } + + module_init(nft_flow_offload_module_init); diff --git a/target/linux/generic/pending-6.6/701-netfilter-nf_tables-ignore-EOPNOTSUPP-on-flowtable-d.patch b/target/linux/generic/pending-6.6/701-netfilter-nf_tables-ignore-EOPNOTSUPP-on-flowtable-d.patch new file mode 100644 index 0000000000..757c0b2449 --- /dev/null +++ b/target/linux/generic/pending-6.6/701-netfilter-nf_tables-ignore-EOPNOTSUPP-on-flowtable-d.patch @@ -0,0 +1,29 @@ +From: Felix Fietkau +Date: Thu, 31 Aug 2023 21:48:38 +0200 +Subject: [PATCH] netfilter: nf_tables: ignore -EOPNOTSUPP on flowtable device + offload setup + +On many embedded devices, it is common to configure flowtable offloading for +a mix of different devices, some of which have hardware offload support and +some of which don't. +The current code limits the ability of user space to properly set up such a +configuration by only allowing adding devices with hardware offload support to +a offload-enabled flowtable. +Given that offload-enabled flowtables also imply fallback to pure software +offloading, this limitation makes little sense. +Fix it by not bailing out when the offload setup returns -EOPNOTSUPP + +Signed-off-by: Felix Fietkau +--- + +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -7911,7 +7911,7 @@ static int nft_register_flowtable_net_ho + err = flowtable->data.type->setup(&flowtable->data, + hook->ops.dev, + FLOW_BLOCK_BIND); +- if (err < 0) ++ if (err < 0 && err != -EOPNOTSUPP) + goto err_unregister_net_hooks; + + err = nf_register_net_hook(net, &hook->ops); diff --git a/target/linux/generic/pending-6.6/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch b/target/linux/generic/pending-6.6/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch new file mode 100644 index 0000000000..842fef3a9c --- /dev/null +++ b/target/linux/generic/pending-6.6/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch @@ -0,0 +1,21 @@ +From: Felix Fietkau +Date: Mon, 21 Mar 2022 20:39:59 +0100 +Subject: [PATCH] net: ethernet: mtk_eth_soc: enable threaded NAPI + +This can improve performance under load by ensuring that NAPI processing is +not pinned on CPU 0. + +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -4941,6 +4941,8 @@ static int mtk_probe(struct platform_dev + * for NAPI to work + */ + init_dummy_netdev(ð->dummy_dev); ++ eth->dummy_dev.threaded = 1; ++ strcpy(eth->dummy_dev.name, "mtk_eth"); + netif_napi_add(ð->dummy_dev, ð->tx_napi, mtk_napi_tx); + netif_napi_add(ð->dummy_dev, ð->rx_napi, mtk_napi_rx); + diff --git a/target/linux/generic/pending-6.6/703-phy-add-detach-callback-to-struct-phy_driver.patch b/target/linux/generic/pending-6.6/703-phy-add-detach-callback-to-struct-phy_driver.patch new file mode 100644 index 0000000000..e4937a1df1 --- /dev/null +++ b/target/linux/generic/pending-6.6/703-phy-add-detach-callback-to-struct-phy_driver.patch @@ -0,0 +1,38 @@ +From: Gabor Juhos +Subject: generic: add detach callback to struct phy_driver + +lede-commit: fe61fc2d7d0b3fb348b502f68f98243b3ddf5867 + +Signed-off-by: Gabor Juhos +--- + drivers/net/phy/phy_device.c | 3 +++ + include/linux/phy.h | 6 ++++++ + 2 files changed, 9 insertions(+) + +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -1850,6 +1850,9 @@ void phy_detach(struct phy_device *phyde + struct module *ndev_owner = NULL; + struct mii_bus *bus; + ++ if (phydev->drv && phydev->drv->detach) ++ phydev->drv->detach(phydev); ++ + if (phydev->sysfs_links) { + if (dev) + sysfs_remove_link(&dev->dev.kobj, "phydev"); +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -900,6 +900,12 @@ struct phy_driver { + /** @handle_interrupt: Override default interrupt handling */ + irqreturn_t (*handle_interrupt)(struct phy_device *phydev); + ++ /* ++ * Called before an ethernet device is detached ++ * from the PHY. ++ */ ++ void (*detach)(struct phy_device *phydev); ++ + /** @remove: Clears up any memory if needed */ + void (*remove)(struct phy_device *phydev); + diff --git a/target/linux/generic/pending-6.6/704-netfilter-nf_tables-fix-bidirectional-offload-regres.patch b/target/linux/generic/pending-6.6/704-netfilter-nf_tables-fix-bidirectional-offload-regres.patch new file mode 100644 index 0000000000..d1d6fa3fe1 --- /dev/null +++ b/target/linux/generic/pending-6.6/704-netfilter-nf_tables-fix-bidirectional-offload-regres.patch @@ -0,0 +1,24 @@ +From: Felix Fietkau +Date: Wed, 14 Feb 2024 15:24:41 +0100 +Subject: [PATCH] netfilter: nf_tables: fix bidirectional offload regression + +Commit 8f84780b84d6 ("netfilter: flowtable: allow unidirectional rules") +made unidirectional flow offload possible, while completely ignoring (and +breaking) bidirectional flow offload for nftables. +Add the missing flag that was left out as an exercise for the reader :) + +Cc: Vlad Buslov +Fixes: 8f84780b84d6 ("netfilter: flowtable: allow unidirectional rules") +Signed-off-by: Felix Fietkau +--- + +--- a/net/netfilter/nft_flow_offload.c ++++ b/net/netfilter/nft_flow_offload.c +@@ -361,6 +361,7 @@ static void nft_flow_offload_eval(const + ct->proto.tcp.seen[1].flags |= IP_CT_TCP_FLAG_BE_LIBERAL; + } + ++ __set_bit(NF_FLOW_HW_BIDIRECTIONAL, &flow->flags); + ret = flow_offload_add(flowtable, flow); + if (ret < 0) + goto err_flow_add; diff --git a/target/linux/generic/pending-6.6/705-net-dsa-tag_mtk-add-padding-for-tx-packets.patch b/target/linux/generic/pending-6.6/705-net-dsa-tag_mtk-add-padding-for-tx-packets.patch new file mode 100644 index 0000000000..d444b2027c --- /dev/null +++ b/target/linux/generic/pending-6.6/705-net-dsa-tag_mtk-add-padding-for-tx-packets.patch @@ -0,0 +1,28 @@ +From: Felix Fietkau +Date: Fri, 6 May 2022 21:38:42 +0200 +Subject: [PATCH] net: dsa: tag_mtk: add padding for tx packets + +Padding for transmitted packets needs to account for the special tag. +With not enough padding, garbage bytes are inserted by the switch at the +end of small packets. + +Fixes: 5cd8985a1909 ("net-next: dsa: add Mediatek tag RX/TX handler") +Signed-off-by: Felix Fietkau +--- + +--- a/net/dsa/tag_mtk.c ++++ b/net/dsa/tag_mtk.c +@@ -27,6 +27,13 @@ static struct sk_buff *mtk_tag_xmit(stru + + skb_set_queue_mapping(skb, dp->index); + ++ /* The Ethernet switch we are interfaced with needs packets to be at ++ * least 64 bytes (including FCS) otherwise their padding might be ++ * corrupted. With tags enabled, we need to make sure that packets are ++ * at least 68 bytes (including FCS and tag). ++ */ ++ eth_skb_pad(skb); ++ + /* 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 diff --git a/target/linux/generic/pending-6.6/710-bridge-add-knob-for-filtering-rx-tx-BPDU-pack.patch b/target/linux/generic/pending-6.6/710-bridge-add-knob-for-filtering-rx-tx-BPDU-pack.patch new file mode 100644 index 0000000000..989aca8f35 --- /dev/null +++ b/target/linux/generic/pending-6.6/710-bridge-add-knob-for-filtering-rx-tx-BPDU-pack.patch @@ -0,0 +1,174 @@ +From: Felix Fietkau +Date: Fri, 27 Aug 2021 12:22:32 +0200 +Subject: [PATCH] bridge: add knob for filtering rx/tx BPDU packets on a port + +Some devices (e.g. wireless APs) can't have devices behind them be part of +a bridge topology with redundant links, due to address limitations. +Additionally, broadcast traffic on these devices is somewhat expensive, due to +the low data rate and wakeups of clients in powersave mode. +This knob can be used to ensure that BPDU packets are never sent or forwarded +to/from these devices + +Signed-off-by: Felix Fietkau +--- + +--- a/include/linux/if_bridge.h ++++ b/include/linux/if_bridge.h +@@ -59,6 +59,7 @@ struct br_ip_list { + #define BR_MRP_LOST_IN_CONT BIT(19) + #define BR_TX_FWD_OFFLOAD BIT(20) + #define BR_PORT_LOCKED BIT(21) ++#define BR_BPDU_FILTER BIT(22) + + #define BR_DEFAULT_AGEING_TIME (300 * HZ) + +--- a/net/bridge/br_forward.c ++++ b/net/bridge/br_forward.c +@@ -199,6 +199,7 @@ out: + void br_flood(struct net_bridge *br, struct sk_buff *skb, + enum br_pkt_type pkt_type, bool local_rcv, bool local_orig) + { ++ const unsigned char *dest = eth_hdr(skb)->h_dest; + struct net_bridge_port *prev = NULL; + struct net_bridge_port *p; + +@@ -214,6 +215,10 @@ void br_flood(struct net_bridge *br, str + case BR_PKT_MULTICAST: + if (!(p->flags & BR_MCAST_FLOOD) && skb->dev != br->dev) + continue; ++ if ((p->flags & BR_BPDU_FILTER) && ++ unlikely(is_link_local_ether_addr(dest) && ++ dest[5] == 0)) ++ continue; + break; + case BR_PKT_BROADCAST: + if (!(p->flags & BR_BCAST_FLOOD) && skb->dev != br->dev) +--- a/net/bridge/br_input.c ++++ b/net/bridge/br_input.c +@@ -344,6 +344,8 @@ static rx_handler_result_t br_handle_fra + fwd_mask |= p->group_fwd_mask; + switch (dest[5]) { + case 0x00: /* Bridge Group Address */ ++ if (p->flags & BR_BPDU_FILTER) ++ goto drop; + /* If STP is turned off, + then must forward to keep loop detection */ + if (p->br->stp_enabled == BR_NO_STP || +--- a/net/bridge/br_sysfs_if.c ++++ b/net/bridge/br_sysfs_if.c +@@ -240,6 +240,7 @@ BRPORT_ATTR_FLAG(multicast_flood, BR_MCA + BRPORT_ATTR_FLAG(broadcast_flood, BR_BCAST_FLOOD); + BRPORT_ATTR_FLAG(neigh_suppress, BR_NEIGH_SUPPRESS); + BRPORT_ATTR_FLAG(isolated, BR_ISOLATED); ++BRPORT_ATTR_FLAG(bpdu_filter, BR_BPDU_FILTER); + + #ifdef CONFIG_BRIDGE_IGMP_SNOOPING + static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf) +@@ -292,6 +293,7 @@ static const struct brport_attribute *br + &brport_attr_group_fwd_mask, + &brport_attr_neigh_suppress, + &brport_attr_isolated, ++ &brport_attr_bpdu_filter, + &brport_attr_backup_port, + NULL + }; +--- a/net/bridge/br_stp_bpdu.c ++++ b/net/bridge/br_stp_bpdu.c +@@ -80,7 +80,8 @@ void br_send_config_bpdu(struct net_brid + { + unsigned char buf[35]; + +- if (p->br->stp_enabled != BR_KERNEL_STP) ++ if (p->br->stp_enabled != BR_KERNEL_STP || ++ (p->flags & BR_BPDU_FILTER)) + return; + + buf[0] = 0; +@@ -127,7 +128,8 @@ void br_send_tcn_bpdu(struct net_bridge_ + { + unsigned char buf[4]; + +- if (p->br->stp_enabled != BR_KERNEL_STP) ++ if (p->br->stp_enabled != BR_KERNEL_STP || ++ (p->flags & BR_BPDU_FILTER)) + return; + + buf[0] = 0; +@@ -172,6 +174,9 @@ void br_stp_rcv(const struct stp_proto * + if (!(br->dev->flags & IFF_UP)) + goto out; + ++ if (p->flags & BR_BPDU_FILTER) ++ goto out; ++ + if (p->state == BR_STATE_DISABLED) + goto out; + +--- a/include/uapi/linux/if_link.h ++++ b/include/uapi/linux/if_link.h +@@ -561,6 +561,7 @@ enum { + IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT, + IFLA_BRPORT_MCAST_EHT_HOSTS_CNT, + IFLA_BRPORT_LOCKED, ++ IFLA_BRPORT_BPDU_FILTER, + __IFLA_BRPORT_MAX + }; + #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) +--- a/net/bridge/br_netlink.c ++++ b/net/bridge/br_netlink.c +@@ -188,6 +188,7 @@ static inline size_t br_port_info_size(v + + nla_total_size(1) /* IFLA_BRPORT_NEIGH_SUPPRESS */ + + nla_total_size(1) /* IFLA_BRPORT_ISOLATED */ + + nla_total_size(1) /* IFLA_BRPORT_LOCKED */ ++ + nla_total_size(1) /* IFLA_BRPORT_BPDU_FILTER */ + + nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_ROOT_ID */ + + nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_BRIDGE_ID */ + + nla_total_size(sizeof(u16)) /* IFLA_BRPORT_DESIGNATED_PORT */ +@@ -274,7 +275,8 @@ static int br_port_fill_attrs(struct sk_ + nla_put_u8(skb, IFLA_BRPORT_MRP_IN_OPEN, + !!(p->flags & BR_MRP_LOST_IN_CONT)) || + nla_put_u8(skb, IFLA_BRPORT_ISOLATED, !!(p->flags & BR_ISOLATED)) || +- nla_put_u8(skb, IFLA_BRPORT_LOCKED, !!(p->flags & BR_PORT_LOCKED))) ++ nla_put_u8(skb, IFLA_BRPORT_LOCKED, !!(p->flags & BR_PORT_LOCKED)) || ++ nla_put_u8(skb, IFLA_BRPORT_BPDU_FILTER, !!(p->flags & BR_BPDU_FILTER))) + return -EMSGSIZE; + + timerval = br_timer_value(&p->message_age_timer); +@@ -878,6 +880,7 @@ static const struct nla_policy br_port_p + [IFLA_BRPORT_LOCKED] = { .type = NLA_U8 }, + [IFLA_BRPORT_BACKUP_PORT] = { .type = NLA_U32 }, + [IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT] = { .type = NLA_U32 }, ++ [IFLA_BRPORT_BPDU_FILTER] = { .type = NLA_U8 }, + }; + + /* Change the state of the port and notify spanning tree */ +@@ -943,6 +946,7 @@ static int br_setport(struct net_bridge_ + br_set_port_flag(p, tb, IFLA_BRPORT_NEIGH_SUPPRESS, BR_NEIGH_SUPPRESS); + br_set_port_flag(p, tb, IFLA_BRPORT_ISOLATED, BR_ISOLATED); + br_set_port_flag(p, tb, IFLA_BRPORT_LOCKED, BR_PORT_LOCKED); ++ br_set_port_flag(p, tb, IFLA_BRPORT_BPDU_FILTER, BR_BPDU_FILTER); + + changed_mask = old_flags ^ p->flags; + +--- a/net/core/rtnetlink.c ++++ b/net/core/rtnetlink.c +@@ -57,7 +57,7 @@ + #include "dev.h" + + #define RTNL_MAX_TYPE 50 +-#define RTNL_SLAVE_MAX_TYPE 40 ++#define RTNL_SLAVE_MAX_TYPE 41 + + struct rtnl_link { + rtnl_doit_func doit; +@@ -4840,7 +4840,9 @@ int ndo_dflt_bridge_getlink(struct sk_bu + brport_nla_put_flag(skb, flags, mask, + IFLA_BRPORT_MCAST_FLOOD, BR_MCAST_FLOOD) || + brport_nla_put_flag(skb, flags, mask, +- IFLA_BRPORT_BCAST_FLOOD, BR_BCAST_FLOOD)) { ++ IFLA_BRPORT_BCAST_FLOOD, BR_BCAST_FLOOD) || ++ brport_nla_put_flag(skb, flags, mask, ++ IFLA_BRPORT_BPDU_FILTER, BR_BPDU_FILTER)) { + nla_nest_cancel(skb, protinfo); + goto nla_put_failure; + } diff --git a/target/linux/generic/pending-6.6/711-01-net-dsa-qca8k-implement-lag_fdb_add-del-ops.patch b/target/linux/generic/pending-6.6/711-01-net-dsa-qca8k-implement-lag_fdb_add-del-ops.patch new file mode 100644 index 0000000000..629b141572 --- /dev/null +++ b/target/linux/generic/pending-6.6/711-01-net-dsa-qca8k-implement-lag_fdb_add-del-ops.patch @@ -0,0 +1,86 @@ +From 3b4329230db8750bea7a56ef07f07cbbf5fc6c5a Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 4 Jul 2023 22:50:12 +0200 +Subject: [PATCH 19/20] net: dsa: qca8k: implement lag_fdb_add/del ops + +Implement lag_fdb_add/del ops to correctly support using LAG interface. +Qca8k switch supports declaring fdb entry for link aggregation by simply +setting the DES_PORT bits to all the LAG member. + +Signed-off-by: Christian Marangi +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 2 ++ + drivers/net/dsa/qca/qca8k-common.c | 48 ++++++++++++++++++++++++++++++ + drivers/net/dsa/qca/qca8k.h | 6 ++++ + 3 files changed, 56 insertions(+) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -2015,6 +2015,8 @@ static const struct dsa_switch_ops qca8k + .port_fdb_add = qca8k_port_fdb_add, + .port_fdb_del = qca8k_port_fdb_del, + .port_fdb_dump = qca8k_port_fdb_dump, ++ .lag_fdb_add = qca8k_lag_fdb_add, ++ .lag_fdb_del = qca8k_lag_fdb_del, + .port_mdb_add = qca8k_port_mdb_add, + .port_mdb_del = qca8k_port_mdb_del, + .port_mirror_add = qca8k_port_mirror_add, +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -1215,6 +1215,42 @@ int qca8k_port_lag_leave(struct dsa_swit + return qca8k_lag_refresh_portmap(ds, port, lag, true); + } + ++int qca8k_lag_fdb_add(struct dsa_switch *ds, struct dsa_lag lag, ++ const unsigned char *addr, u16 vid, ++ struct dsa_db db) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ struct dsa_port *dp; ++ u16 port_mask = 0; ++ ++ /* Set the vid to the port vlan id if no vid is set */ ++ if (!vid) ++ vid = QCA8K_PORT_VID_DEF; ++ ++ dsa_lag_foreach_port(dp, ds->dst, &lag) ++ port_mask |= BIT(dp->index); ++ ++ return qca8k_port_fdb_insert(priv, addr, port_mask, vid); ++} ++ ++int qca8k_lag_fdb_del(struct dsa_switch *ds, struct dsa_lag lag, ++ const unsigned char *addr, u16 vid, ++ struct dsa_db db) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ struct dsa_port *dp; ++ u16 port_mask = 0; ++ ++ /* Set the vid to the port vlan id if no vid is set */ ++ if (!vid) ++ vid = QCA8K_PORT_VID_DEF; ++ ++ dsa_lag_foreach_port(dp, ds->dst, &lag) ++ port_mask |= BIT(dp->index); ++ ++ return qca8k_fdb_del(priv, addr, port_mask, vid); ++} ++ + int qca8k_read_switch_id(struct qca8k_priv *priv) + { + u32 val; +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -590,5 +590,11 @@ int qca8k_port_lag_join(struct dsa_switc + struct netlink_ext_ack *extack); + int qca8k_port_lag_leave(struct dsa_switch *ds, int port, + struct dsa_lag lag); ++int qca8k_lag_fdb_add(struct dsa_switch *ds, struct dsa_lag lag, ++ const unsigned char *addr, u16 vid, ++ struct dsa_db db); ++int qca8k_lag_fdb_del(struct dsa_switch *ds, struct dsa_lag lag, ++ const unsigned char *addr, u16 vid, ++ struct dsa_db db); + + #endif /* __QCA8K_H */ diff --git a/target/linux/generic/pending-6.6/711-02-net-dsa-qca8k-enable-flooding-to-both-CPU-port.patch b/target/linux/generic/pending-6.6/711-02-net-dsa-qca8k-enable-flooding-to-both-CPU-port.patch new file mode 100644 index 0000000000..24243468a8 --- /dev/null +++ b/target/linux/generic/pending-6.6/711-02-net-dsa-qca8k-enable-flooding-to-both-CPU-port.patch @@ -0,0 +1,37 @@ +From b954d61d9ecfa64450fc178586719dc2a95b92a7 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 20 Jun 2023 21:48:24 +0200 +Subject: [PATCH 3/4] net: dsa: qca8k: enable flooding to both CPU port + +To permit a multi-CPU setup, flood all unknown frames to all CPU ports. +Each CPU port should have correct LOOKUP MEMBER configuration to +prevent receiving duplicate packets from user ports. + +Signed-off-by: Christian Marangi +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 13 +++++-------- + 1 file changed, 5 insertions(+), 8 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -1904,15 +1904,12 @@ qca8k_setup(struct dsa_switch *ds) + } + } + +- /* Forward all unknown frames to CPU port for Linux processing +- * Notice that in multi-cpu config only one port should be set +- * for igmp, unknown, multicast and broadcast packet +- */ ++ /* Forward all unknown frames to CPU port for Linux processing */ + ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1, +- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, BIT(cpu_port)) | +- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, BIT(cpu_port)) | +- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, BIT(cpu_port)) | +- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, BIT(cpu_port))); ++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, dsa_cpu_ports(ds)) | ++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, dsa_cpu_ports(ds)) | ++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, dsa_cpu_ports(ds)) | ++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, dsa_cpu_ports(ds))); + if (ret) + return ret; + diff --git a/target/linux/generic/pending-6.6/711-03-net-dsa-qca8k-add-support-for-port_change_master.patch b/target/linux/generic/pending-6.6/711-03-net-dsa-qca8k-add-support-for-port_change_master.patch new file mode 100644 index 0000000000..8a58e0f76e --- /dev/null +++ b/target/linux/generic/pending-6.6/711-03-net-dsa-qca8k-add-support-for-port_change_master.patch @@ -0,0 +1,158 @@ +From b2d6ebf2f92f8695c83fa6979f4ab579c588df76 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 20 Jun 2023 07:57:38 +0200 +Subject: [PATCH 4/4] net: dsa: qca8k: add support for port_change_master + +Add support for port_change_master to permit assigning an alternative +CPU port if the switch have both CPU port connected or create a LAG on +both CPU port and assign the LAG as DSA master. + +On port change master request, we check if the master is a LAG. +With LAG we compose the cpu_port_mask with the CPU port in the LAG, if +master is a simple dsa_port, we derive the index. + +Finally we apply the new cpu_port_mask to the LOOKUP MEMBER to permit +the port to receive packet by the new CPU port setup for the port and we +refresh the CPU ports LOOKUP MEMBER configuration to reflect the new +user port state. + +port_lag_join/leave is updated to refresh the user ports if we detect +that the LAG is a DSA master and we have user port using it as a master. + +Signed-off-by: Christian Marangi +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 116 ++++++++++++++++++++++++++++++- + 1 file changed, 114 insertions(+), 2 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -1741,6 +1741,117 @@ qca8k_get_tag_protocol(struct dsa_switch + return DSA_TAG_PROTO_QCA; + } + ++static int qca8k_port_change_master(struct dsa_switch *ds, int port, ++ struct net_device *master, ++ struct netlink_ext_ack *extack) ++{ ++ struct dsa_switch_tree *dst = ds->dst; ++ struct qca8k_priv *priv = ds->priv; ++ u8 cpu_port_mask = 0; ++ struct dsa_port *dp; ++ u32 val; ++ int ret; ++ ++ /* With LAG of CPU port, compose the mask for port LOOKUP MEMBER */ ++ if (netif_is_lag_master(master)) { ++ struct dsa_lag *lag; ++ int id; ++ ++ id = dsa_lag_id(dst, master); ++ lag = dsa_lag_by_id(dst, id); ++ ++ dsa_lag_foreach_port(dp, dst, lag) ++ if (dsa_port_is_cpu(dp)) ++ cpu_port_mask |= BIT(dp->index); ++ } else { ++ dp = master->dsa_ptr; ++ cpu_port_mask |= BIT(dp->index); ++ } ++ ++ /* Connect port to new cpu port */ ++ ret = regmap_read(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), &val); ++ if (ret) ++ return ret; ++ ++ /* Reset connected CPU port in port LOOKUP MEMBER */ ++ val &= ~dsa_cpu_ports(ds); ++ /* Assign the new CPU port in port LOOKUP MEMBER */ ++ val |= cpu_port_mask; ++ ++ ret = regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_MEMBER, ++ val); ++ if (ret) ++ return ret; ++ ++ /* Refresh CPU port LOOKUP MEMBER with new port */ ++ dsa_tree_for_each_cpu_port(dp, ds->dst) { ++ u32 reg = QCA8K_PORT_LOOKUP_CTRL(dp->index); ++ ++ /* If CPU port in mask assign port, else remove port */ ++ if (BIT(dp->index) & cpu_port_mask) ++ ret = regmap_set_bits(priv->regmap, reg, BIT(port)); ++ else ++ ret = regmap_clear_bits(priv->regmap, reg, BIT(port)); ++ ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int qca8k_port_lag_refresh_user_ports(struct dsa_switch *ds, ++ struct dsa_lag lag) ++{ ++ struct net_device *lag_dev = lag.dev; ++ struct dsa_port *dp; ++ int ret; ++ ++ /* Ignore if LAG is not a DSA master */ ++ if (!netif_is_lag_master(lag_dev)) ++ return 0; ++ ++ dsa_switch_for_each_user_port(dp, ds) { ++ /* Skip if assigned master is not the LAG */ ++ if (dsa_port_to_master(dp) != lag_dev) ++ continue; ++ ++ ret = qca8k_port_change_master(ds, dp->index, ++ lag_dev, NULL); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int qca8xxx_port_lag_join(struct dsa_switch *ds, int port, ++ struct dsa_lag lag, ++ struct netdev_lag_upper_info *info, ++ struct netlink_ext_ack *extack) ++{ ++ int ret; ++ ++ ret = qca8k_port_lag_join(ds, port, lag, info, extack); ++ if (ret) ++ return ret; ++ ++ return qca8k_port_lag_refresh_user_ports(ds, lag); ++} ++ ++static int qca8xxx_port_lag_leave(struct dsa_switch *ds, int port, ++ struct dsa_lag lag) ++{ ++ int ret; ++ ++ ret = qca8k_port_lag_leave(ds, port, lag); ++ if (ret) ++ return ret; ++ ++ return qca8k_port_lag_refresh_user_ports(ds, lag); ++} ++ + static void + qca8k_master_change(struct dsa_switch *ds, const struct net_device *master, + bool operational) +@@ -2027,8 +2138,9 @@ static const struct dsa_switch_ops qca8k + .phylink_mac_link_down = qca8k_phylink_mac_link_down, + .phylink_mac_link_up = qca8k_phylink_mac_link_up, + .get_phy_flags = qca8k_get_phy_flags, +- .port_lag_join = qca8k_port_lag_join, +- .port_lag_leave = qca8k_port_lag_leave, ++ .port_lag_join = qca8xxx_port_lag_join, ++ .port_lag_leave = qca8xxx_port_lag_leave, ++ .port_change_master = qca8k_port_change_master, + .master_state_change = qca8k_master_change, + .connect_tag_protocol = qca8k_connect_tag_protocol, + }; diff --git a/target/linux/generic/pending-6.6/712-net-dsa-qca8k-enable-assisted-learning-on-CPU-port.patch b/target/linux/generic/pending-6.6/712-net-dsa-qca8k-enable-assisted-learning-on-CPU-port.patch new file mode 100644 index 0000000000..23816fe366 --- /dev/null +++ b/target/linux/generic/pending-6.6/712-net-dsa-qca8k-enable-assisted-learning-on-CPU-port.patch @@ -0,0 +1,57 @@ +From 0f6599167c126ce32c85d4f8a1f3d1775a268572 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 6 Oct 2023 12:44:00 +0200 +Subject: [PATCH] net: dsa: qca8k: enable assisted learning on CPU port + +Enable assisted learning on CPU port. + +It has been verified that there is a problem in packet roaming +from one BSS to another in the same security settings from one +physical R7800 to another physical R7800 where they are in the +same L2 broadcast domain backhauled/linked together via one +of the ethernet ports. +DHCP will fail to complete and traffic cannot flow for around 300 +seconds. + +Signed-off-by: Christian Marangi +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 14 +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -2013,6 +2013,12 @@ qca8k_setup(struct dsa_switch *ds) + dev_err(priv->dev, "failed enabling QCA header mode on port %d", dp->index); + return ret; + } ++ ++ /* Disable learning by default on all ports */ ++ ret = regmap_clear_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(dp->index), ++ QCA8K_PORT_LOOKUP_LEARN); ++ if (ret) ++ return ret; + } + + /* Forward all unknown frames to CPU port for Linux processing */ +@@ -2042,11 +2048,6 @@ qca8k_setup(struct dsa_switch *ds) + 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 + */ +@@ -2098,6 +2099,9 @@ qca8k_setup(struct dsa_switch *ds) + /* Set max number of LAGs supported */ + ds->num_lag_ids = QCA8K_NUM_LAGS; + ++ /* HW learn on CPU port is limited and require manual setting */ ++ ds->assisted_learning_on_cpu_port = true; ++ + return 0; + } + diff --git a/target/linux/generic/pending-6.6/713-03-arm64-dts-qcom-ipq8074-add-clock-frequency-to-MDIO-n.patch b/target/linux/generic/pending-6.6/713-03-arm64-dts-qcom-ipq8074-add-clock-frequency-to-MDIO-n.patch new file mode 100644 index 0000000000..55f116ec5f --- /dev/null +++ b/target/linux/generic/pending-6.6/713-03-arm64-dts-qcom-ipq8074-add-clock-frequency-to-MDIO-n.patch @@ -0,0 +1,25 @@ +From 3b5a603bf66236b956287909556fd7ad4904450c Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 24 Jan 2024 19:38:01 +0100 +Subject: [PATCH 3/3] arm64: dts: qcom: ipq8074: add clock-frequency to MDIO + node + +Add clock-frequency to MDIO node to set the MDC rate to 6.25Mhz instead +of using the default value of 390KHz from MDIO default divider. + +Signed-off-by: Christian Marangi +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -263,6 +263,8 @@ + clocks = <&gcc GCC_MDIO_AHB_CLK>; + clock-names = "gcc_mdio_ahb_clk"; + ++ clock-frequency = <6250000>; ++ + status = "disabled"; + }; + diff --git a/target/linux/generic/pending-6.6/721-net-phy-realtek-rtl8221-allow-to-configure-SERDES-mo.patch b/target/linux/generic/pending-6.6/721-net-phy-realtek-rtl8221-allow-to-configure-SERDES-mo.patch new file mode 100644 index 0000000000..ed13b004e4 --- /dev/null +++ b/target/linux/generic/pending-6.6/721-net-phy-realtek-rtl8221-allow-to-configure-SERDES-mo.patch @@ -0,0 +1,106 @@ +From ace6abaa0f9203083fe4c0a6a74da2d96410b625 Mon Sep 17 00:00:00 2001 +From: Alexander Couzens +Date: Sat, 13 Aug 2022 12:49:33 +0200 +Subject: [PATCH 01/10] net: phy: realtek: rtl8221: allow to configure SERDES + mode + +The rtl8221 supports multiple SERDES modes: +- SGMII +- 2500base-x +- HiSGMII + +Further it supports rate adaption on SERDES links to allow +slow ethernet speeds (10/100/1000mbit) to work on 2500base-x/HiSGMII +links without reducing the SERDES speed. + +When operating without rate adapters the SERDES link will follow the +ethernet speed. + +Signed-off-by: Alexander Couzens +--- + drivers/net/phy/realtek.c | 48 +++++++++++++++++++++++++++++++++++++++ + 1 file changed, 48 insertions(+) + +--- a/drivers/net/phy/realtek.c ++++ b/drivers/net/phy/realtek.c +@@ -53,6 +53,15 @@ + RTL8201F_ISR_LINK) + #define RTL8201F_IER 0x13 + ++#define RTL8221B_MMD_SERDES_CTRL MDIO_MMD_VEND1 ++#define RTL8221B_MMD_PHY_CTRL MDIO_MMD_VEND2 ++#define RTL8221B_SERDES_OPTION 0x697a ++#define RTL8221B_SERDES_OPTION_MODE_MASK GENMASK(5, 0) ++#define RTL8221B_SERDES_OPTION_MODE_2500BASEX_SGMII 0 ++#define RTL8221B_SERDES_OPTION_MODE_HISGMII_SGMII 1 ++#define RTL8221B_SERDES_OPTION_MODE_2500BASEX 2 ++#define RTL8221B_SERDES_OPTION_MODE_HISGMII 3 ++ + #define RTL8366RB_POWER_SAVE 0x15 + #define RTL8366RB_POWER_SAVE_ON BIT(12) + +@@ -851,6 +860,48 @@ static irqreturn_t rtl9000a_handle_inter + return IRQ_HANDLED; + } + ++static int rtl8221b_config_init(struct phy_device *phydev) ++{ ++ u16 option_mode; ++ ++ switch (phydev->interface) { ++ case PHY_INTERFACE_MODE_2500BASEX: ++ if (!phydev->is_c45) { ++ option_mode = RTL8221B_SERDES_OPTION_MODE_2500BASEX; ++ break; ++ } ++ fallthrough; ++ case PHY_INTERFACE_MODE_SGMII: ++ option_mode = RTL8221B_SERDES_OPTION_MODE_2500BASEX_SGMII; ++ break; ++ default: ++ return 0; ++ } ++ ++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, ++ 0x75f3, 0); ++ ++ phy_modify_mmd_changed(phydev, RTL8221B_MMD_SERDES_CTRL, ++ RTL8221B_SERDES_OPTION, ++ RTL8221B_SERDES_OPTION_MODE_MASK, option_mode); ++ switch (option_mode) { ++ case RTL8221B_SERDES_OPTION_MODE_2500BASEX_SGMII: ++ case RTL8221B_SERDES_OPTION_MODE_2500BASEX: ++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6a04, 0x0503); ++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6f10, 0xd455); ++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6f11, 0x8020); ++ break; ++ case RTL8221B_SERDES_OPTION_MODE_HISGMII_SGMII: ++ case RTL8221B_SERDES_OPTION_MODE_HISGMII: ++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6a04, 0x0503); ++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6f10, 0xd433); ++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6f11, 0x8020); ++ break; ++ } ++ ++ return 0; ++} ++ + static struct phy_driver realtek_drvs[] = { + { + PHY_ID_MATCH_EXACT(0x00008201), +@@ -1003,6 +1054,7 @@ static struct phy_driver realtek_drvs[] + PHY_ID_MATCH_EXACT(0x001cc849), + .name = "RTL8221B-VB-CG 2.5Gbps PHY", + .get_features = rtl822x_get_features, ++ .config_init = rtl8221b_config_init, + .config_aneg = rtl822x_config_aneg, + .read_status = rtl822x_read_status, + .suspend = genphy_suspend, +@@ -1014,6 +1066,7 @@ static struct phy_driver realtek_drvs[] + .name = "RTL8221B-VM-CG 2.5Gbps PHY", + .get_features = rtl822x_get_features, + .config_aneg = rtl822x_config_aneg, ++ .config_init = rtl8221b_config_init, + .read_status = rtl822x_read_status, + .suspend = genphy_suspend, + .resume = rtlgen_resume, diff --git a/target/linux/generic/pending-6.6/722-net-phy-realtek-support-switching-between-SGMII-and-.patch b/target/linux/generic/pending-6.6/722-net-phy-realtek-support-switching-between-SGMII-and-.patch new file mode 100644 index 0000000000..abe31e66af --- /dev/null +++ b/target/linux/generic/pending-6.6/722-net-phy-realtek-support-switching-between-SGMII-and-.patch @@ -0,0 +1,61 @@ +From 312753d0aadba0f58841ae513b80fdbabc887523 Mon Sep 17 00:00:00 2001 +From: Chukun Pan +Date: Wed, 8 Feb 2023 16:32:18 +0800 +Subject: [PATCH] net: phy: realtek: support switching between SGMII and + 2500BASE-X for RTL822x series + +After commit ace6aba ("net: phy: realtek: rtl8221: allow to configure +SERDES mode"), the rtl8221 phy can work in SGMII and 2500base-x modes +respectively. So add interface automatic switching for rtl8221 phy to +match various wire speeds. + +Signed-off-by: Chukun Pan +--- + drivers/net/phy/realtek.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +--- a/drivers/net/phy/realtek.c ++++ b/drivers/net/phy/realtek.c +@@ -686,6 +686,25 @@ static int rtl822x_config_aneg(struct ph + return __genphy_config_aneg(phydev, ret); + } + ++static void rtl822x_update_interface(struct phy_device *phydev) ++{ ++ /* Automatically switch SERDES interface between ++ * SGMII and 2500-BaseX according to speed. ++ */ ++ switch (phydev->speed) { ++ case SPEED_2500: ++ phydev->interface = PHY_INTERFACE_MODE_2500BASEX; ++ break; ++ case SPEED_1000: ++ case SPEED_100: ++ case SPEED_10: ++ phydev->interface = PHY_INTERFACE_MODE_SGMII; ++ break; ++ default: ++ break; ++ } ++} ++ + static int rtl822x_read_status(struct phy_device *phydev) + { + int ret; +@@ -704,11 +723,14 @@ static int rtl822x_read_status(struct ph + phydev->lp_advertising, lpadv & RTL_LPADV_2500FULL); + } + +- ret = genphy_read_status(phydev); ++ ret = rtlgen_read_status(phydev); + if (ret < 0) + return ret; + +- return rtlgen_get_speed(phydev); ++ if (phydev->is_c45 && phydev->link) ++ rtl822x_update_interface(phydev); ++ ++ return 0; + } + + static bool rtlgen_supports_2_5gbps(struct phy_device *phydev) diff --git a/target/linux/generic/pending-6.6/723-net-mt7531-ensure-all-MACs-are-powered-down-before-r.patch b/target/linux/generic/pending-6.6/723-net-mt7531-ensure-all-MACs-are-powered-down-before-r.patch new file mode 100644 index 0000000000..a18d1ad717 --- /dev/null +++ b/target/linux/generic/pending-6.6/723-net-mt7531-ensure-all-MACs-are-powered-down-before-r.patch @@ -0,0 +1,28 @@ +From 3fb8841513c4ec3a2e5d366df86230c45f239a57 Mon Sep 17 00:00:00 2001 +From: Alexander Couzens +Date: Sat, 13 Aug 2022 13:08:22 +0200 +Subject: [PATCH 03/10] net: mt7531: ensure all MACs are powered down before + reset + +The datasheet [1] explicit describes it as requirement for a reset. + +[1] MT7531 Reference Manual for Development Board rev 1.0, page 735 + +Signed-off-by: Alexander Couzens +--- + drivers/net/dsa/mt7530.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2252,6 +2252,10 @@ mt7530_setup(struct dsa_switch *ds) + return -ENODEV; + } + ++ /* all MACs must be forced link-down before sw reset */ ++ for (i = 0; i < MT7530_NUM_PORTS; i++) ++ mt7530_write(priv, MT7530_PMCR_P(i), MT7531_FORCE_LNK); ++ + /* Reset the switch through internal reset */ + mt7530_write(priv, MT7530_SYS_CTRL, + SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST | diff --git a/target/linux/generic/pending-6.6/724-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch b/target/linux/generic/pending-6.6/724-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch new file mode 100644 index 0000000000..8e0e4b1fdd --- /dev/null +++ b/target/linux/generic/pending-6.6/724-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch @@ -0,0 +1,65 @@ +From 85cd45580f5e3b26068cccb7d6173f200e754dc0 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Sun, 2 Apr 2023 23:56:16 +0100 +Subject: [PATCH 1/2] net: phy: realtek: use genphy_soft_reset for 2.5G PHYs + +Some vendor bootloaders do weird things with those PHYs which result in +link modes being reported wrongly. Start from a clean sheet by resetting +the PHY. + +Reported-by: Yevhen Kolomeiko +Signed-off-by: Daniel Golle +--- + drivers/net/phy/realtek.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/drivers/net/phy/realtek.c ++++ b/drivers/net/phy/realtek.c +@@ -1040,6 +1040,7 @@ static struct phy_driver realtek_drvs[] + .write_page = rtl821x_write_page, + .read_mmd = rtl822x_read_mmd, + .write_mmd = rtl822x_write_mmd, ++ .soft_reset = genphy_soft_reset, + }, { + PHY_ID_MATCH_EXACT(0x001cc840), + .name = "RTL8226B_RTL8221B 2.5Gbps PHY", +@@ -1052,6 +1053,7 @@ static struct phy_driver realtek_drvs[] + .write_page = rtl821x_write_page, + .read_mmd = rtl822x_read_mmd, + .write_mmd = rtl822x_write_mmd, ++ .soft_reset = genphy_soft_reset, + }, { + PHY_ID_MATCH_EXACT(0x001cc838), + .name = "RTL8226-CG 2.5Gbps PHY", +@@ -1062,6 +1064,7 @@ static struct phy_driver realtek_drvs[] + .resume = rtlgen_resume, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, ++ .soft_reset = genphy_soft_reset, + }, { + PHY_ID_MATCH_EXACT(0x001cc848), + .name = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY", +@@ -1072,6 +1075,7 @@ static struct phy_driver realtek_drvs[] + .resume = rtlgen_resume, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, ++ .soft_reset = genphy_soft_reset, + }, { + PHY_ID_MATCH_EXACT(0x001cc849), + .name = "RTL8221B-VB-CG 2.5Gbps PHY", +@@ -1083,6 +1087,7 @@ static struct phy_driver realtek_drvs[] + .resume = rtlgen_resume, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, ++ .soft_reset = genphy_soft_reset, + }, { + PHY_ID_MATCH_EXACT(0x001cc84a), + .name = "RTL8221B-VM-CG 2.5Gbps PHY", +@@ -1094,6 +1099,7 @@ static struct phy_driver realtek_drvs[] + .resume = rtlgen_resume, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, ++ .soft_reset = genphy_soft_reset, + }, { + PHY_ID_MATCH_EXACT(0x001cc961), + .name = "RTL8366RB Gigabit Ethernet", diff --git a/target/linux/generic/pending-6.6/725-net-phy-realtek-disable-SGMII-in-band-AN-for-2-5G-PHYs.patch b/target/linux/generic/pending-6.6/725-net-phy-realtek-disable-SGMII-in-band-AN-for-2-5G-PHYs.patch new file mode 100644 index 0000000000..babaa47aed --- /dev/null +++ b/target/linux/generic/pending-6.6/725-net-phy-realtek-disable-SGMII-in-band-AN-for-2-5G-PHYs.patch @@ -0,0 +1,43 @@ +From 2b1b8c4c215af7988136401c902338d091d408a1 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 3 Apr 2023 01:21:57 +0300 +Subject: [PATCH 2/2] net: phy: realtek: disable SGMII in-band AN for 2.5G PHYs + +MAC drivers don't use SGMII in-band autonegotiation unless told to do so +in device tree using 'managed = "in-band-status"'. When using MDIO to +access a PHY, in-band-status is unneeded as we have link-status via +MDIO. Switch off SGMII in-band autonegotiation using magic values. + +Reported-by: Chen Minqiang +Reported-by: Chukun Pan +Reported-by: Yevhen Kolomeiko +Tested-by: Yevhen Kolomeiko +Signed-off-by: Daniel Golle +--- + drivers/net/phy/realtek.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/drivers/net/phy/realtek.c ++++ b/drivers/net/phy/realtek.c +@@ -885,6 +885,7 @@ static irqreturn_t rtl9000a_handle_inter + static int rtl8221b_config_init(struct phy_device *phydev) + { + u16 option_mode; ++ int val; + + switch (phydev->interface) { + case PHY_INTERFACE_MODE_2500BASEX: +@@ -921,6 +922,13 @@ static int rtl8221b_config_init(struct p + break; + } + ++ /* Disable SGMII AN */ ++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x7588, 0x2); ++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x7589, 0x71d0); ++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x7587, 0x3); ++ phy_read_mmd_poll_timeout(phydev, RTL8221B_MMD_SERDES_CTRL, 0x7587, ++ val, !(val & BIT(0)), 500, 100000, false); ++ + return 0; + } + diff --git a/target/linux/generic/pending-6.6/726-net-phy-realtek-make-sure-paged-read-is-protected-by.patch b/target/linux/generic/pending-6.6/726-net-phy-realtek-make-sure-paged-read-is-protected-by.patch new file mode 100644 index 0000000000..6e338d9474 --- /dev/null +++ b/target/linux/generic/pending-6.6/726-net-phy-realtek-make-sure-paged-read-is-protected-by.patch @@ -0,0 +1,35 @@ +From 4dd2cc9b91ecb25f278a2c55e07e6455e9000e6b Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Sat, 22 Apr 2023 01:21:14 +0100 +Subject: [PATCH] net: phy: realtek: make sure paged read is protected by mutex + +As we cannot rely on phy_read_paged function before the PHY is +identified, the paged read in rtlgen_supports_2_5gbps needs to be open +coded as it is being called by the match_phy_device function, ie. before +.read_page and .write_page have been populated. + +Make sure it is also protected by the MDIO bus mutex and use +rtl821x_write_page instead of 3 individually locked MDIO bus operations. + +Signed-off-by: Daniel Golle +--- + drivers/net/phy/realtek.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +--- a/drivers/net/phy/realtek.c ++++ b/drivers/net/phy/realtek.c +@@ -737,9 +737,11 @@ static bool rtlgen_supports_2_5gbps(stru + { + int val; + +- phy_write(phydev, RTL821x_PAGE_SELECT, 0xa61); +- val = phy_read(phydev, 0x13); +- phy_write(phydev, RTL821x_PAGE_SELECT, 0); ++ mutex_lock(&phydev->mdio.bus->mdio_lock); ++ rtl821x_write_page(phydev, 0xa61); ++ val = __phy_read(phydev, 0x13); ++ rtl821x_write_page(phydev, 0); ++ mutex_unlock(&phydev->mdio.bus->mdio_lock); + + return val >= 0 && val & RTL_SUPPORTS_2500FULL; + } diff --git a/target/linux/generic/pending-6.6/727-net-phy-realtek-use-inline-functions-for-10GbE-adver.patch b/target/linux/generic/pending-6.6/727-net-phy-realtek-use-inline-functions-for-10GbE-adver.patch new file mode 100644 index 0000000000..cafaefd3bf --- /dev/null +++ b/target/linux/generic/pending-6.6/727-net-phy-realtek-use-inline-functions-for-10GbE-adver.patch @@ -0,0 +1,60 @@ +From 92c8b9d558160d94b981dd8a2b9c47657627ffdc Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Sat, 22 Apr 2023 01:23:08 +0100 +Subject: [PATCH 2/3] net: phy: realtek: use inline functions for 10GbE + advertisement + +Use existing generic inline functions to encode local advertisement +of 10GbE link modes as well as to decode link-partner advertisement. + +Signed-off-by: Daniel Golle +--- + drivers/net/phy/realtek.c | 22 +++++----------------- + 1 file changed, 5 insertions(+), 17 deletions(-) + +--- a/drivers/net/phy/realtek.c ++++ b/drivers/net/phy/realtek.c +@@ -68,10 +68,6 @@ + #define RTL_SUPPORTS_5000FULL BIT(14) + #define RTL_SUPPORTS_2500FULL BIT(13) + #define RTL_SUPPORTS_10000FULL BIT(0) +-#define RTL_ADV_2500FULL BIT(7) +-#define RTL_LPADV_10000FULL BIT(11) +-#define RTL_LPADV_5000FULL BIT(6) +-#define RTL_LPADV_2500FULL BIT(5) + + #define RTL9000A_GINMR 0x14 + #define RTL9000A_GINMR_LINK_STATUS BIT(4) +@@ -671,14 +667,11 @@ static int rtl822x_config_aneg(struct ph + int ret = 0; + + if (phydev->autoneg == AUTONEG_ENABLE) { +- u16 adv2500 = 0; +- +- if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, +- phydev->advertising)) +- adv2500 = RTL_ADV_2500FULL; +- + ret = phy_modify_paged_changed(phydev, 0xa5d, 0x12, +- RTL_ADV_2500FULL, adv2500); ++ MDIO_AN_10GBT_CTRL_ADV10G | ++ MDIO_AN_10GBT_CTRL_ADV5G | ++ MDIO_AN_10GBT_CTRL_ADV2_5G, ++ linkmode_adv_to_mii_10gbt_adv_t(phydev->advertising)); + if (ret < 0) + return ret; + } +@@ -715,12 +708,7 @@ static int rtl822x_read_status(struct ph + if (lpadv < 0) + return lpadv; + +- linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, +- phydev->lp_advertising, lpadv & RTL_LPADV_10000FULL); +- linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, +- phydev->lp_advertising, lpadv & RTL_LPADV_5000FULL); +- linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, +- phydev->lp_advertising, lpadv & RTL_LPADV_2500FULL); ++ mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, lpadv); + } + + ret = rtlgen_read_status(phydev); diff --git a/target/linux/generic/pending-6.6/728-net-phy-realtek-check-validity-of-10GbE-link-partner.patch b/target/linux/generic/pending-6.6/728-net-phy-realtek-check-validity-of-10GbE-link-partner.patch new file mode 100644 index 0000000000..8be1af6e50 --- /dev/null +++ b/target/linux/generic/pending-6.6/728-net-phy-realtek-check-validity-of-10GbE-link-partner.patch @@ -0,0 +1,28 @@ +From 929bb4d3cfbc7878326c0771a01a636d49c54b40 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Sat, 22 Apr 2023 01:25:39 +0100 +Subject: [PATCH 3/3] net: phy: realtek: check validity of 10GbE link-partner + advertisement + +Only use link-partner advertisement bits for 10GbE modes if they are +actually valid. Check LOCALOK and REMOTEOK bits and clear 10GbE modes +unless both of them are set. + +Signed-off-by: Daniel Golle +--- + drivers/net/phy/realtek.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/net/phy/realtek.c ++++ b/drivers/net/phy/realtek.c +@@ -708,6 +708,10 @@ static int rtl822x_read_status(struct ph + if (lpadv < 0) + return lpadv; + ++ if (!(lpadv & MDIO_AN_10GBT_STAT_REMOK) || ++ !(lpadv & MDIO_AN_10GBT_STAT_LOCOK)) ++ lpadv = 0; ++ + mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, lpadv); + } + diff --git a/target/linux/generic/pending-6.6/729-net-phy-realtek-introduce-rtl822x_probe.patch b/target/linux/generic/pending-6.6/729-net-phy-realtek-introduce-rtl822x_probe.patch new file mode 100644 index 0000000000..92e7a8742a --- /dev/null +++ b/target/linux/generic/pending-6.6/729-net-phy-realtek-introduce-rtl822x_probe.patch @@ -0,0 +1,84 @@ +From 9155098547fb1172d4fa536f3f6bc9d42f59d08c Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Sat, 22 Apr 2023 03:26:01 +0100 +Subject: [PATCH] net: phy: realtek: setup ALDPS on RTL822x + +Setup Link Down Power Saving Mode according the DTS property +just like for RTL821x 1GE PHYs. + +Signed-off-by: Daniel Golle +--- + drivers/net/phy/realtek.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +--- a/drivers/net/phy/realtek.c ++++ b/drivers/net/phy/realtek.c +@@ -62,6 +62,10 @@ + #define RTL8221B_SERDES_OPTION_MODE_2500BASEX 2 + #define RTL8221B_SERDES_OPTION_MODE_HISGMII 3 + ++#define RTL8221B_PHYCR1 0xa430 ++#define RTL8221B_PHYCR1_ALDPS_EN BIT(2) ++#define RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN BIT(12) ++ + #define RTL8366RB_POWER_SAVE 0x15 + #define RTL8366RB_POWER_SAVE_ON BIT(12) + +@@ -750,6 +754,25 @@ static int rtl8226_match_phy_device(stru + rtlgen_supports_2_5gbps(phydev); + } + ++static int rtl822x_probe(struct phy_device *phydev) ++{ ++ struct device *dev = &phydev->mdio.dev; ++ int val; ++ ++ val = phy_read_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, RTL8221B_PHYCR1); ++ if (val < 0) ++ return val; ++ ++ if (of_property_read_bool(dev->of_node, "realtek,aldps-enable")) ++ val |= RTL8221B_PHYCR1_ALDPS_EN | RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN; ++ else ++ val &= ~(RTL8221B_PHYCR1_ALDPS_EN | RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN); ++ ++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, RTL8221B_PHYCR1, val); ++ ++ return 0; ++} ++ + static int rtlgen_resume(struct phy_device *phydev) + { + int ret = genphy_resume(phydev); +@@ -1061,6 +1084,7 @@ static struct phy_driver realtek_drvs[] + .name = "RTL8226-CG 2.5Gbps PHY", + .get_features = rtl822x_get_features, + .config_aneg = rtl822x_config_aneg, ++ .probe = rtl822x_probe, + .read_status = rtl822x_read_status, + .suspend = genphy_suspend, + .resume = rtlgen_resume, +@@ -1072,6 +1096,7 @@ static struct phy_driver realtek_drvs[] + .name = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY", + .get_features = rtl822x_get_features, + .config_aneg = rtl822x_config_aneg, ++ .probe = rtl822x_probe, + .read_status = rtl822x_read_status, + .suspend = genphy_suspend, + .resume = rtlgen_resume, +@@ -1084,6 +1109,7 @@ static struct phy_driver realtek_drvs[] + .get_features = rtl822x_get_features, + .config_init = rtl8221b_config_init, + .config_aneg = rtl822x_config_aneg, ++ .probe = rtl822x_probe, + .read_status = rtl822x_read_status, + .suspend = genphy_suspend, + .resume = rtlgen_resume, +@@ -1096,6 +1122,7 @@ static struct phy_driver realtek_drvs[] + .get_features = rtl822x_get_features, + .config_aneg = rtl822x_config_aneg, + .config_init = rtl8221b_config_init, ++ .probe = rtl822x_probe, + .read_status = rtl822x_read_status, + .suspend = genphy_suspend, + .resume = rtlgen_resume, diff --git a/target/linux/generic/pending-6.6/730-net-phy-realtek-detect-early-version-of-RTL8221B.patch b/target/linux/generic/pending-6.6/730-net-phy-realtek-detect-early-version-of-RTL8221B.patch new file mode 100644 index 0000000000..f5987109f6 --- /dev/null +++ b/target/linux/generic/pending-6.6/730-net-phy-realtek-detect-early-version-of-RTL8221B.patch @@ -0,0 +1,63 @@ +From 0de82310d2b32e78ff79d42c08b1122a6ede3778 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Sun, 30 Apr 2023 00:15:41 +0100 +Subject: [PATCH] net: phy: realtek: detect early version of RTL8221B + +Early versions (?) of the RTL8221B PHY cannot be identified in a regular +Clause-45 bus scan as the PHY doesn't report the implemented MMDs +correctly but returns 0 instead. +Implement custom identify function using the PKGID instead of iterating +over the implemented MMDs. + +Signed-off-by: Daniel Golle + +--- a/drivers/net/phy/realtek.c ++++ b/drivers/net/phy/realtek.c +@@ -754,6 +754,38 @@ static int rtl8226_match_phy_device(stru + rtlgen_supports_2_5gbps(phydev); + } + ++static int rtl8221b_vb_cg_match_phy_device(struct phy_device *phydev) ++{ ++ int val; ++ u32 id; ++ ++ if (phydev->mdio.bus->probe_capabilities >= MDIOBUS_C45) { ++ val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PKGID1); ++ if (val < 0) ++ return 0; ++ ++ id = val << 16; ++ val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PKGID2); ++ if (val < 0) ++ return 0; ++ ++ id |= val; ++ } else { ++ val = phy_read(phydev, MII_PHYSID1); ++ if (val < 0) ++ return 0; ++ ++ id = val << 16; ++ val = phy_read(phydev, MII_PHYSID2); ++ if (val < 0) ++ return 0; ++ ++ id |= val; ++ } ++ ++ return (id == 0x001cc849); ++} ++ + static int rtl822x_probe(struct phy_device *phydev) + { + struct device *dev = &phydev->mdio.dev; +@@ -1104,7 +1136,7 @@ static struct phy_driver realtek_drvs[] + .write_page = rtl821x_write_page, + .soft_reset = genphy_soft_reset, + }, { +- PHY_ID_MATCH_EXACT(0x001cc849), ++ .match_phy_device = rtl8221b_vb_cg_match_phy_device, + .name = "RTL8221B-VB-CG 2.5Gbps PHY", + .get_features = rtl822x_get_features, + .config_init = rtl8221b_config_init, diff --git a/target/linux/generic/pending-6.6/731-net-permit-ieee80211_ptr-even-with-no-CFG82111-suppo.patch b/target/linux/generic/pending-6.6/731-net-permit-ieee80211_ptr-even-with-no-CFG82111-suppo.patch new file mode 100644 index 0000000000..a7a4bafbb6 --- /dev/null +++ b/target/linux/generic/pending-6.6/731-net-permit-ieee80211_ptr-even-with-no-CFG82111-suppo.patch @@ -0,0 +1,59 @@ +From 686c603f67ae87bf21a61b5e4b1564443f41c3ee Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 20 Oct 2022 03:34:43 +0200 +Subject: [PATCH] net: permit ieee80211_ptr even with no CFG82111 support + +Introduce a new flag CONFIG_CFG80211_HEADERS to compile in ieee80211_ptr +even if CFG80211 support is not compiled in. This is needed for the +backports project and for any downstream wireless driver that loads in +the kernel dynamically. + +Signed-off-by: Christian Marangi +--- + include/linux/netdevice.h | 2 +- + net/batman-adv/hard-interface.c | 2 +- + net/wireless/Kconfig | 4 ++++ + 3 files changed, 6 insertions(+), 2 deletions(-) + +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -2192,7 +2192,7 @@ struct net_device { + #if IS_ENABLED(CONFIG_AX25) + void *ax25_ptr; + #endif +-#if IS_ENABLED(CONFIG_CFG80211) ++#if IS_ENABLED(CONFIG_CFG80211_HEADERS) + struct wireless_dev *ieee80211_ptr; + #endif + #if IS_ENABLED(CONFIG_IEEE802154) || IS_ENABLED(CONFIG_6LOWPAN) +--- a/net/batman-adv/hard-interface.c ++++ b/net/batman-adv/hard-interface.c +@@ -308,7 +308,7 @@ static bool batadv_is_cfg80211_netdev(st + if (!net_device) + return false; + +-#if IS_ENABLED(CONFIG_CFG80211) ++#if IS_ENABLED(CONFIG_CFG80211_HEADERS) + /* cfg80211 drivers have to set ieee80211_ptr */ + if (net_device->ieee80211_ptr) + return true; +--- a/net/wireless/Kconfig ++++ b/net/wireless/Kconfig +@@ -26,6 +26,7 @@ config CFG80211 + # using a different algorithm, though right now they shouldn't + # (this is here rather than below to allow it to be a module) + select CRYPTO_SHA256 if CFG80211_USE_KERNEL_REGDB_KEYS ++ select CFG80211_HEADERS + help + cfg80211 is the Linux wireless LAN (802.11) configuration API. + Enable this if you have a wireless device. +@@ -36,6 +37,9 @@ config CFG80211 + + When built as a module it will be called cfg80211. + ++config CFG80211_HEADERS ++ bool "cfg80211 - headers support" ++ + if CFG80211 + + config NL80211_TESTMODE diff --git a/target/linux/generic/pending-6.6/732-00-net-ethernet-mtk_eth_soc-compile-out-netsys-v2-code-.patch b/target/linux/generic/pending-6.6/732-00-net-ethernet-mtk_eth_soc-compile-out-netsys-v2-code-.patch new file mode 100644 index 0000000000..decf647bce --- /dev/null +++ b/target/linux/generic/pending-6.6/732-00-net-ethernet-mtk_eth_soc-compile-out-netsys-v2-code-.patch @@ -0,0 +1,44 @@ +From: Felix Fietkau +Date: Thu, 27 Oct 2022 23:39:52 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: compile out netsys v2 code + on mt7621 + +Avoid some branches in the hot path on low-end devices with limited CPU power, +and reduce code size + +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -1326,6 +1326,22 @@ 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[]; + ++#ifdef CONFIG_SOC_MT7621 ++static inline bool mtk_is_netsys_v1(struct mtk_eth *eth) ++{ ++ return true; ++} ++ ++static inline bool mtk_is_netsys_v2_or_greater(struct mtk_eth *eth) ++{ ++ return false; ++} ++ ++static inline bool mtk_is_netsys_v3_or_greater(struct mtk_eth *eth) ++{ ++ return false; ++} ++#else + static inline bool mtk_is_netsys_v1(struct mtk_eth *eth) + { + return eth->soc->version == 1; +@@ -1340,6 +1356,7 @@ static inline bool mtk_is_netsys_v3_or_g + { + return eth->soc->version > 2; + } ++#endif + + static inline struct mtk_foe_entry * + mtk_foe_get_entry(struct mtk_ppe *ppe, u16 hash) diff --git a/target/linux/generic/pending-6.6/732-01-net-ethernet-mtk_eth_soc-work-around-issue-with-send.patch b/target/linux/generic/pending-6.6/732-01-net-ethernet-mtk_eth_soc-work-around-issue-with-send.patch new file mode 100644 index 0000000000..a1cc109050 --- /dev/null +++ b/target/linux/generic/pending-6.6/732-01-net-ethernet-mtk_eth_soc-work-around-issue-with-send.patch @@ -0,0 +1,94 @@ +From: Felix Fietkau +Date: Thu, 3 Nov 2022 12:38:49 +0100 +Subject: [PATCH] net: ethernet: mtk_eth_soc: work around issue with sending + small fragments + +When lots of frames are sent with a number of very small fragments, an +internal FIFO can overflow, causing the DMA engine to lock up lock up and +transmit attempts time out. + +Fix this on MT7986 by increasing the reserved FIFO space. +Fix this on older chips by detecting the presence of small fragments and use +skb_gso_segment + skb_linearize to deal with them. + +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -1562,12 +1562,28 @@ static void mtk_wake_queue(struct mtk_et + } + } + ++static bool mtk_skb_has_small_frag(struct sk_buff *skb) ++{ ++ int min_size = 16; ++ int i; ++ ++ if (skb_headlen(skb) < min_size) ++ return true; ++ ++ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) ++ if (skb_frag_size(&skb_shinfo(skb)->frags[i]) < min_size) ++ return true; ++ ++ return false; ++} ++ + static netdev_tx_t mtk_start_xmit(struct sk_buff *skb, struct net_device *dev) + { + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + struct mtk_tx_ring *ring = ð->tx_ring; + struct net_device_stats *stats = &dev->stats; ++ struct sk_buff *segs, *next; + bool gso = false; + int tx_num; + +@@ -1589,6 +1605,18 @@ static netdev_tx_t mtk_start_xmit(struct + return NETDEV_TX_BUSY; + } + ++ if (mtk_is_netsys_v1(eth) && ++ skb_is_gso(skb) && mtk_skb_has_small_frag(skb)) { ++ segs = skb_gso_segment(skb, dev->features & ~NETIF_F_ALL_TSO); ++ if (IS_ERR(segs)) ++ goto drop; ++ ++ if (segs) { ++ consume_skb(skb); ++ skb = segs; ++ } ++ } ++ + /* TSO: fill MSS info in tcp checksum field */ + if (skb_is_gso(skb)) { + if (skb_cow_head(skb, 0)) { +@@ -1604,8 +1632,14 @@ static netdev_tx_t mtk_start_xmit(struct + } + } + +- if (mtk_tx_map(skb, dev, tx_num, ring, gso) < 0) +- goto drop; ++ skb_list_walk_safe(skb, skb, next) { ++ if ((mtk_is_netsys_v1(eth) && ++ mtk_skb_has_small_frag(skb) && skb_linearize(skb)) || ++ mtk_tx_map(skb, dev, tx_num, ring, gso) < 0) { ++ stats->tx_dropped++; ++ dev_kfree_skb_any(skb); ++ } ++ } + + if (unlikely(atomic_read(&ring->free_count) <= ring->thresh)) + netif_tx_stop_all_queues(dev); +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -268,7 +268,7 @@ + #define MTK_CHK_DDONE_EN BIT(28) + #define MTK_DMAD_WR_WDONE BIT(26) + #define MTK_WCOMP_EN BIT(24) +-#define MTK_RESV_BUF (0x40 << 16) ++#define MTK_RESV_BUF (0x80 << 16) + #define MTK_MUTLI_CNT (0x4 << 12) + #define MTK_LEAKY_BUCKET_EN BIT(11) + diff --git a/target/linux/generic/pending-6.6/732-02-net-ethernet-mtk_eth_soc-set-NETIF_F_ALL_TSO.patch b/target/linux/generic/pending-6.6/732-02-net-ethernet-mtk_eth_soc-set-NETIF_F_ALL_TSO.patch new file mode 100644 index 0000000000..11a81dd0bf --- /dev/null +++ b/target/linux/generic/pending-6.6/732-02-net-ethernet-mtk_eth_soc-set-NETIF_F_ALL_TSO.patch @@ -0,0 +1,21 @@ +From: Felix Fietkau +Date: Fri, 28 Oct 2022 12:54:48 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: set NETIF_F_ALL_TSO + +Significantly improves performance by avoiding unnecessary segmentation + +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -47,8 +47,7 @@ + #define MTK_HW_FEATURES (NETIF_F_IP_CSUM | \ + NETIF_F_RXCSUM | \ + NETIF_F_HW_VLAN_CTAG_TX | \ +- NETIF_F_SG | NETIF_F_TSO | \ +- NETIF_F_TSO6 | \ ++ NETIF_F_SG | NETIF_F_ALL_TSO | \ + NETIF_F_IPV6_CSUM |\ + NETIF_F_HW_TC) + #define MTK_HW_FEATURES_MT7628 (NETIF_F_SG | NETIF_F_RXCSUM) diff --git a/target/linux/generic/pending-6.6/732-03-net-ethernet-mtk_eth_soc-fix-remaining-throughput-re.patch b/target/linux/generic/pending-6.6/732-03-net-ethernet-mtk_eth_soc-fix-remaining-throughput-re.patch new file mode 100644 index 0000000000..0a49c75b00 --- /dev/null +++ b/target/linux/generic/pending-6.6/732-03-net-ethernet-mtk_eth_soc-fix-remaining-throughput-re.patch @@ -0,0 +1,42 @@ +From: Felix Fietkau +Date: Wed, 29 Mar 2023 16:02:54 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: fix remaining throughput + regression + +Based on further tests, it seems that the QDMA shaper is not able to +perform shaping close to the MAC link rate without throughput loss. +This cannot be compensated by increasing the shaping rate, so it seems +to be an internal limit. + +Fix the remaining throughput regression by detecting that condition and +limiting shaping to ports with lower link speed. + +This patch intentionally ignores link speed gain from TRGMII, because +even on such links, shaping to 1000 Mbit/s incurs some throughput +degradation. + +Fixes: f63959c7eec3 ("net: ethernet: mtk_eth_soc: implement multi-queue support for per-port queues") +Reported-by: Frank Wunderlich +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -767,6 +767,7 @@ static void mtk_mac_link_up(struct phyli + MAC_MCR_FORCE_RX_FC); + + /* Configure speed */ ++ mac->speed = speed; + switch (speed) { + case SPEED_2500: + case SPEED_1000: +@@ -3348,6 +3349,9 @@ found: + if (dp->index >= MTK_QDMA_NUM_QUEUES) + return NOTIFY_DONE; + ++ if (mac->speed > 0 && mac->speed <= s.base.speed) ++ s.base.speed = 0; ++ + mtk_set_queue_speed(eth, dp->index + 3, s.base.speed); + + return NOTIFY_DONE; diff --git a/target/linux/generic/pending-6.6/734-net-ethernet-mtk_eth_soc-ppe-fix-L2-offloading-with-.patch b/target/linux/generic/pending-6.6/734-net-ethernet-mtk_eth_soc-ppe-fix-L2-offloading-with-.patch new file mode 100644 index 0000000000..61042c1ad0 --- /dev/null +++ b/target/linux/generic/pending-6.6/734-net-ethernet-mtk_eth_soc-ppe-fix-L2-offloading-with-.patch @@ -0,0 +1,33 @@ +From: Felix Fietkau +Date: Tue, 27 Dec 2022 15:02:51 +0100 +Subject: [PATCH] net: ethernet: mtk_eth_soc: ppe: fix L2 offloading with DSA + untagging offload enabled + +Check for skb metadata in order to detect the case where the DSA header is not +present. + +Fixes: 2d7605a72906 ("net: ethernet: mtk_eth_soc: enable hardware DSA untagging") +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ethernet/mediatek/mtk_ppe.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + #include + #include "mtk_eth_soc.h" + #include "mtk_ppe.h" +@@ -829,7 +830,9 @@ void __mtk_ppe_check_skb(struct mtk_ppe + skb->dev->dsa_ptr->tag_ops->proto != DSA_TAG_PROTO_MTK) + goto out; + +- tag += 4; ++ if (!skb_metadata_dst(skb)) ++ tag += 4; ++ + if (get_unaligned_be16(tag) != ETH_P_8021Q) + break; + diff --git a/target/linux/generic/pending-6.6/737-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch b/target/linux/generic/pending-6.6/737-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch new file mode 100644 index 0000000000..afe561ab31 --- /dev/null +++ b/target/linux/generic/pending-6.6/737-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch @@ -0,0 +1,935 @@ +From 91bda2f441f9e37273922028ffc48ce8e91bf5bd Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 12 Dec 2023 03:51:14 +0000 +Subject: [PATCH] net: ethernet: mtk_eth_soc: add paths and SerDes modes for + MT7988 + +MT7988 comes with a built-in 2.5G PHY as well as SerDes lanes to +connect external PHYs or transceivers in USXGMII, 10GBase-R, 5GBase-R, +2500Base-X, 1000Base-X and Cisco SGMII interface modes. + +Implement support for configuring for the new paths to SerDes interfaces +and the internal 2.5G PHY. + +Add USXGMII PCS driver for 10GBase-R, 5GBase-R and USXGMII mode, and +setup the new PHYA on MT7988 to access the also still existing old +LynxI PCS for 1000Base-X, 2500Base-X and Cisco SGMII PCS interface +modes. + +Signed-off-by: Daniel Golle +--- + drivers/net/ethernet/mediatek/mtk_eth_path.c | 122 +++++++- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 291 +++++++++++++++++-- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 107 ++++++- + 3 files changed, 469 insertions(+), 51 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_path.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_path.c +@@ -31,10 +31,20 @@ static const char *mtk_eth_path_name(u64 + return "gmac2_rgmii"; + case MTK_ETH_PATH_GMAC2_SGMII: + return "gmac2_sgmii"; ++ case MTK_ETH_PATH_GMAC2_2P5GPHY: ++ return "gmac2_2p5gphy"; + case MTK_ETH_PATH_GMAC2_GEPHY: + return "gmac2_gephy"; ++ case MTK_ETH_PATH_GMAC3_SGMII: ++ return "gmac3_sgmii"; + case MTK_ETH_PATH_GDM1_ESW: + return "gdm1_esw"; ++ case MTK_ETH_PATH_GMAC1_USXGMII: ++ return "gmac1_usxgmii"; ++ case MTK_ETH_PATH_GMAC2_USXGMII: ++ return "gmac2_usxgmii"; ++ case MTK_ETH_PATH_GMAC3_USXGMII: ++ return "gmac3_usxgmii"; + default: + return "unknown path"; + } +@@ -127,6 +137,27 @@ static int set_mux_u3_gmac2_to_qphy(stru + return 0; + } + ++static int set_mux_gmac2_to_2p5gphy(struct mtk_eth *eth, u64 path) ++{ ++ int ret; ++ ++ if (path == MTK_ETH_PATH_GMAC2_2P5GPHY) { ++ ret = regmap_clear_bits(eth->ethsys, ETHSYS_SYSCFG0, SYSCFG0_SGMII_GMAC2_V2); ++ if (ret) ++ return ret; ++ ++ /* Setup mux to 2p5g PHY */ ++ ret = regmap_clear_bits(eth->infra, TOP_MISC_NETSYS_PCS_MUX, MUX_G2_USXGMII_SEL); ++ if (ret) ++ return ret; ++ ++ dev_dbg(eth->dev, "path %s in %s updated\n", ++ mtk_eth_path_name(path), __func__); ++ } ++ ++ return 0; ++} ++ + static int set_mux_gmac1_gmac2_to_sgmii_rgmii(struct mtk_eth *eth, u64 path) + { + unsigned int val = 0; +@@ -165,7 +196,48 @@ static int set_mux_gmac1_gmac2_to_sgmii_ + return 0; + } + +-static int set_mux_gmac12_to_gephy_sgmii(struct mtk_eth *eth, u64 path) ++static int set_mux_gmac123_to_usxgmii(struct mtk_eth *eth, u64 path) ++{ ++ unsigned int val = 0; ++ bool updated = true; ++ int mac_id = 0; ++ ++ /* Disable SYSCFG1 SGMII */ ++ regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val); ++ ++ switch (path) { ++ case MTK_ETH_PATH_GMAC1_USXGMII: ++ val &= ~(u32)SYSCFG0_SGMII_GMAC1_V2; ++ mac_id = MTK_GMAC1_ID; ++ break; ++ case MTK_ETH_PATH_GMAC2_USXGMII: ++ val &= ~(u32)SYSCFG0_SGMII_GMAC2_V2; ++ mac_id = MTK_GMAC2_ID; ++ break; ++ case MTK_ETH_PATH_GMAC3_USXGMII: ++ val &= ~(u32)SYSCFG0_SGMII_GMAC3_V2; ++ mac_id = MTK_GMAC3_ID; ++ break; ++ default: ++ updated = false; ++ }; ++ ++ if (updated) { ++ regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0, ++ SYSCFG0_SGMII_MASK, val); ++ ++ if (mac_id == MTK_GMAC2_ID) ++ regmap_set_bits(eth->infra, TOP_MISC_NETSYS_PCS_MUX, ++ MUX_G2_USXGMII_SEL); ++ } ++ ++ dev_dbg(eth->dev, "path %s in %s updated = %d\n", ++ mtk_eth_path_name(path), __func__, updated); ++ ++ return 0; ++} ++ ++static int set_mux_gmac123_to_gephy_sgmii(struct mtk_eth *eth, u64 path) + { + unsigned int val = 0; + bool updated = true; +@@ -182,6 +254,9 @@ static int set_mux_gmac12_to_gephy_sgmii + case MTK_ETH_PATH_GMAC2_SGMII: + val |= SYSCFG0_SGMII_GMAC2_V2; + break; ++ case MTK_ETH_PATH_GMAC3_SGMII: ++ val |= SYSCFG0_SGMII_GMAC3_V2; ++ break; + default: + updated = false; + } +@@ -210,13 +285,25 @@ static const struct mtk_eth_muxc mtk_eth + .cap_bit = MTK_ETH_MUX_U3_GMAC2_TO_QPHY, + .set_path = set_mux_u3_gmac2_to_qphy, + }, { ++ .name = "mux_gmac2_to_2p5gphy", ++ .cap_bit = MTK_ETH_MUX_GMAC2_TO_2P5GPHY, ++ .set_path = set_mux_gmac2_to_2p5gphy, ++ }, { + .name = "mux_gmac1_gmac2_to_sgmii_rgmii", + .cap_bit = MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII, + .set_path = set_mux_gmac1_gmac2_to_sgmii_rgmii, + }, { + .name = "mux_gmac12_to_gephy_sgmii", + .cap_bit = MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII, +- .set_path = set_mux_gmac12_to_gephy_sgmii, ++ .set_path = set_mux_gmac123_to_gephy_sgmii, ++ }, { ++ .name = "mux_gmac123_to_gephy_sgmii", ++ .cap_bit = MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII, ++ .set_path = set_mux_gmac123_to_gephy_sgmii, ++ }, { ++ .name = "mux_gmac123_to_usxgmii", ++ .cap_bit = MTK_ETH_MUX_GMAC123_TO_USXGMII, ++ .set_path = set_mux_gmac123_to_usxgmii, + }, + }; + +@@ -249,12 +336,39 @@ out: + return err; + } + ++int mtk_gmac_usxgmii_path_setup(struct mtk_eth *eth, int mac_id) ++{ ++ u64 path; ++ ++ path = (mac_id == MTK_GMAC1_ID) ? MTK_ETH_PATH_GMAC1_USXGMII : ++ (mac_id == MTK_GMAC2_ID) ? MTK_ETH_PATH_GMAC2_USXGMII : ++ MTK_ETH_PATH_GMAC3_USXGMII; ++ ++ /* Setup proper MUXes along the path */ ++ return mtk_eth_mux_setup(eth, path); ++} ++ + int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id) + { + u64 path; + +- path = (mac_id == 0) ? MTK_ETH_PATH_GMAC1_SGMII : +- MTK_ETH_PATH_GMAC2_SGMII; ++ path = (mac_id == MTK_GMAC1_ID) ? MTK_ETH_PATH_GMAC1_SGMII : ++ (mac_id == MTK_GMAC2_ID) ? MTK_ETH_PATH_GMAC2_SGMII : ++ MTK_ETH_PATH_GMAC3_SGMII; ++ ++ /* Setup proper MUXes along the path */ ++ return mtk_eth_mux_setup(eth, path); ++} ++ ++int mtk_gmac_2p5gphy_path_setup(struct mtk_eth *eth, int mac_id) ++{ ++ u64 path = 0; ++ ++ if (mac_id == MTK_GMAC2_ID) ++ path = MTK_ETH_PATH_GMAC2_2P5GPHY; ++ ++ if (!path) ++ return -EINVAL; + + /* Setup proper MUXes along the path */ + return mtk_eth_mux_setup(eth, path); +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -21,6 +21,8 @@ + #include + #include + #include ++#include ++#include + #include + #include + #include +@@ -258,12 +260,8 @@ static const char * const mtk_clks_sourc + "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", +@@ -475,6 +473,30 @@ static void mtk_setup_bridge_switch(stru + MTK_GSW_CFG); + } + ++static bool mtk_check_gmac23_idle(struct mtk_mac *mac) ++{ ++ u32 mac_fsm, gdm_fsm; ++ ++ mac_fsm = mtk_r32(mac->hw, MTK_MAC_FSM(mac->id)); ++ ++ switch (mac->id) { ++ case MTK_GMAC2_ID: ++ gdm_fsm = mtk_r32(mac->hw, MTK_FE_GDM2_FSM); ++ break; ++ case MTK_GMAC3_ID: ++ gdm_fsm = mtk_r32(mac->hw, MTK_FE_GDM3_FSM); ++ break; ++ default: ++ return true; ++ }; ++ ++ if ((mac_fsm & 0xFFFF0000) == 0x01010000 && ++ (gdm_fsm & 0xFFFF0000) == 0x00000000) ++ return true; ++ ++ return false; ++} ++ + static struct phylink_pcs *mtk_mac_select_pcs(struct phylink_config *config, + phy_interface_t interface) + { +@@ -483,6 +505,21 @@ static struct phylink_pcs *mtk_mac_selec + struct mtk_eth *eth = mac->hw; + unsigned int sid; + ++ if (mtk_is_netsys_v3_or_greater(eth)) { ++ switch (interface) { ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ case PHY_INTERFACE_MODE_SGMII: ++ return mac->sgmii_pcs; ++ case PHY_INTERFACE_MODE_5GBASER: ++ case PHY_INTERFACE_MODE_10GBASER: ++ case PHY_INTERFACE_MODE_USXGMII: ++ return mac->usxgmii_pcs; ++ default: ++ return NULL; ++ } ++ } ++ + if (interface == PHY_INTERFACE_MODE_SGMII || + phy_interface_mode_is_8023z(interface)) { + sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ? +@@ -544,7 +581,22 @@ static void mtk_mac_config(struct phylin + goto init_err; + } + break; ++ case PHY_INTERFACE_MODE_USXGMII: ++ case PHY_INTERFACE_MODE_10GBASER: ++ case PHY_INTERFACE_MODE_5GBASER: ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_USXGMII)) { ++ err = mtk_gmac_usxgmii_path_setup(eth, mac->id); ++ if (err) ++ goto init_err; ++ } ++ break; + case PHY_INTERFACE_MODE_INTERNAL: ++ if (mac->id == MTK_GMAC2_ID && ++ MTK_HAS_CAPS(eth->soc->caps, MTK_2P5GPHY)) { ++ err = mtk_gmac_2p5gphy_path_setup(eth, mac->id); ++ if (err) ++ goto init_err; ++ } + break; + default: + goto err_phy; +@@ -599,8 +651,6 @@ static void mtk_mac_config(struct phylin + val &= ~SYSCFG0_GE_MODE(SYSCFG0_GE_MASK, mac->id); + val |= SYSCFG0_GE_MODE(ge_mode, mac->id); + regmap_write(eth->ethsys, ETHSYS_SYSCFG0, val); +- +- mac->interface = state->interface; + } + + /* SGMII */ +@@ -617,21 +667,40 @@ static void mtk_mac_config(struct phylin + + /* Save the syscfg0 value for mac_finish */ + mac->syscfg0 = val; +- } else if (phylink_autoneg_inband(mode)) { ++ } else if (state->interface != PHY_INTERFACE_MODE_USXGMII && ++ state->interface != PHY_INTERFACE_MODE_10GBASER && ++ state->interface != PHY_INTERFACE_MODE_5GBASER && ++ phylink_autoneg_inband(mode)) { + dev_err(eth->dev, +- "In-band mode not supported in non SGMII mode!\n"); ++ "In-band mode not supported in non-SerDes modes!\n"); + 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)); ++ if (mtk_is_netsys_v3_or_greater(eth)) { ++ if (mtk_interface_mode_is_xgmii(state->interface)) { ++ 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)); ++ ++ if (mac->id == MTK_GMAC1_ID) ++ mtk_setup_bridge_switch(eth); ++ } else { ++ mtk_w32(eth, 0, MTK_GDMA_EG_CTRL(mac->id)); + +- mtk_setup_bridge_switch(eth); ++ /* FIXME: In current hardware design, we have to reset FE ++ * when swtiching XGDM to GDM. Therefore, here trigger an SER ++ * to let GDM go back to the initial state. ++ */ ++ if ((mtk_interface_mode_is_xgmii(mac->interface) || ++ mac->interface == PHY_INTERFACE_MODE_NA) && ++ !mtk_check_gmac23_idle(mac) && ++ !test_bit(MTK_RESETTING, ð->state)) ++ schedule_work(ð->pending_work); ++ } + } + ++ mac->interface = state->interface; ++ + return; + + err_phy: +@@ -644,6 +713,18 @@ init_err: + mac->id, phy_modes(state->interface), err); + } + ++static int mtk_mac_prepare(struct phylink_config *config, unsigned int mode, ++ phy_interface_t interface) ++{ ++ struct mtk_mac *mac = container_of(config, struct mtk_mac, ++ phylink_config); ++ ++ if (mac->pextp && mac->interface != interface) ++ phy_reset(mac->pextp); ++ ++ return 0; ++} ++ + static int mtk_mac_finish(struct phylink_config *config, unsigned int mode, + phy_interface_t interface) + { +@@ -652,6 +733,10 @@ static int mtk_mac_finish(struct phylink + struct mtk_eth *eth = mac->hw; + u32 mcr_cur, mcr_new; + ++ /* Setup PMA/PMD */ ++ if (mac->pextp) ++ phy_set_mode_ext(mac->pextp, PHY_MODE_ETHERNET, interface); ++ + /* Enable SGMII */ + if (interface == PHY_INTERFACE_MODE_SGMII || + phy_interface_mode_is_8023z(interface)) +@@ -677,10 +762,13 @@ static void mtk_mac_link_down(struct phy + { + struct mtk_mac *mac = container_of(config, struct mtk_mac, + phylink_config); +- u32 mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); + +- mcr &= ~(MAC_MCR_TX_EN | MAC_MCR_RX_EN); +- mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); ++ if (!mtk_interface_mode_is_xgmii(interface)) { ++ mtk_m32(mac->hw, MAC_MCR_TX_EN | MAC_MCR_RX_EN, 0, MTK_MAC_MCR(mac->id)); ++ mtk_m32(mac->hw, MTK_XGMAC_FORCE_LINK(mac->id), 0, MTK_XGMAC_STS(mac->id)); ++ } else if (mac->id != MTK_GMAC1_ID) { ++ mtk_m32(mac->hw, XMAC_MCR_TRX_DISABLE, XMAC_MCR_TRX_DISABLE, MTK_XMAC_MCR(mac->id)); ++ } + } + + static void mtk_set_queue_speed(struct mtk_eth *eth, unsigned int idx, +@@ -752,13 +840,11 @@ static void mtk_set_queue_speed(struct m + 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, +- int speed, int duplex, bool tx_pause, bool rx_pause) ++static void mtk_gdm_mac_link_up(struct mtk_mac *mac, ++ struct phy_device *phy, ++ unsigned int mode, phy_interface_t interface, ++ int speed, int duplex, bool tx_pause, bool rx_pause) + { +- struct mtk_mac *mac = container_of(config, struct mtk_mac, +- phylink_config); + u32 mcr; + + mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); +@@ -792,9 +878,63 @@ static void mtk_mac_link_up(struct phyli + mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); + } + ++static void mtk_xgdm_mac_link_up(struct mtk_mac *mac, ++ struct phy_device *phy, ++ unsigned int mode, phy_interface_t interface, ++ int speed, int duplex, bool tx_pause, bool rx_pause) ++{ ++ u32 mcr, force_link = 0; ++ ++ if (mac->id == MTK_GMAC1_ID) ++ return; ++ ++ /* Eliminate the interference(before link-up) caused by PHY noise */ ++ mtk_m32(mac->hw, XMAC_LOGIC_RST, 0, MTK_XMAC_LOGIC_RST(mac->id)); ++ mdelay(20); ++ mtk_m32(mac->hw, XMAC_GLB_CNTCLR, XMAC_GLB_CNTCLR, MTK_XMAC_CNT_CTRL(mac->id)); ++ ++ if (mac->interface == PHY_INTERFACE_MODE_INTERNAL || mac->id == MTK_GMAC3_ID) ++ force_link = MTK_XGMAC_FORCE_LINK(mac->id); ++ ++ mtk_m32(mac->hw, MTK_XGMAC_FORCE_LINK(mac->id), force_link, MTK_XGMAC_STS(mac->id)); ++ ++ mcr = mtk_r32(mac->hw, MTK_XMAC_MCR(mac->id)); ++ mcr &= ~(XMAC_MCR_FORCE_TX_FC | XMAC_MCR_FORCE_RX_FC | XMAC_MCR_TRX_DISABLE); ++ /* Configure pause modes - ++ * phylink will avoid these for half duplex ++ */ ++ if (tx_pause) ++ mcr |= XMAC_MCR_FORCE_TX_FC; ++ if (rx_pause) ++ mcr |= XMAC_MCR_FORCE_RX_FC; ++ ++ mtk_w32(mac->hw, mcr, MTK_XMAC_MCR(mac->id)); ++} ++ ++static void mtk_mac_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 mtk_mac *mac = container_of(config, struct mtk_mac, ++ phylink_config); ++ ++ if (mtk_interface_mode_is_xgmii(interface)) ++ mtk_xgdm_mac_link_up(mac, phy, mode, interface, speed, duplex, ++ tx_pause, rx_pause); ++ else ++ mtk_gdm_mac_link_up(mac, phy, mode, interface, speed, duplex, ++ tx_pause, rx_pause); ++ ++ /* Repeat pextp setup to tune link */ ++ if (mac->pextp) ++ phy_set_mode_ext(mac->pextp, PHY_MODE_ETHERNET, interface); ++} ++ + static const struct phylink_mac_ops mtk_phylink_ops = { + .mac_select_pcs = mtk_mac_select_pcs, + .mac_config = mtk_mac_config, ++ .mac_prepare = mtk_mac_prepare, + .mac_finish = mtk_mac_finish, + .mac_link_down = mtk_mac_link_down, + .mac_link_up = mtk_mac_link_up, +@@ -3373,6 +3513,9 @@ static int mtk_open(struct net_device *d + struct mtk_eth *eth = mac->hw; + int i, err; + ++ if (mac->pextp) ++ phy_power_on(mac->pextp); ++ + err = phylink_of_phy_connect(mac->phylink, mac->of_node, 0); + if (err) { + netdev_err(dev, "%s: could not attach PHY: %d\n", __func__, +@@ -3501,6 +3644,9 @@ static int mtk_stop(struct net_device *d + for (i = 0; i < ARRAY_SIZE(eth->ppe); i++) + mtk_ppe_stop(eth->ppe[i]); + ++ if (mac->pextp) ++ phy_power_off(mac->pextp); ++ + return 0; + } + +@@ -4498,6 +4644,7 @@ static const struct net_device_ops mtk_n + static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) + { + const __be32 *_id = of_get_property(np, "reg", NULL); ++ struct device_node *pcs_np; + phy_interface_t phy_mode; + struct phylink *phylink; + struct mtk_mac *mac; +@@ -4533,16 +4680,41 @@ static int mtk_add_mac(struct mtk_eth *e + mac->id = id; + mac->hw = eth; + mac->of_node = np; ++ pcs_np = of_parse_phandle(mac->of_node, "pcs-handle", 0); ++ if (pcs_np) { ++ mac->sgmii_pcs = mtk_pcs_lynxi_get(eth->dev, pcs_np); ++ if (IS_ERR(mac->sgmii_pcs)) { ++ if (PTR_ERR(mac->sgmii_pcs) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; + +- err = of_get_ethdev_address(mac->of_node, eth->netdev[id]); +- if (err == -EPROBE_DEFER) +- return err; ++ dev_err(eth->dev, "cannot select SGMII PCS, error %ld\n", ++ PTR_ERR(mac->sgmii_pcs)); ++ return PTR_ERR(mac->sgmii_pcs); ++ } ++ } + +- if (err) { +- /* If the mac address is invalid, use random mac address */ +- eth_hw_addr_random(eth->netdev[id]); +- dev_err(eth->dev, "generated random MAC address %pM\n", +- eth->netdev[id]->dev_addr); ++ pcs_np = of_parse_phandle(mac->of_node, "pcs-handle", 1); ++ if (pcs_np) { ++ mac->usxgmii_pcs = mtk_usxgmii_pcs_get(eth->dev, pcs_np); ++ if (IS_ERR(mac->usxgmii_pcs)) { ++ if (PTR_ERR(mac->usxgmii_pcs) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ ++ dev_err(eth->dev, "cannot select USXGMII PCS, error %ld\n", ++ PTR_ERR(mac->usxgmii_pcs)); ++ return PTR_ERR(mac->usxgmii_pcs); ++ } ++ } ++ ++ if (mtk_is_netsys_v3_or_greater(eth) && (mac->sgmii_pcs || mac->usxgmii_pcs)) { ++ mac->pextp = devm_of_phy_get(eth->dev, mac->of_node, NULL); ++ if (IS_ERR(mac->pextp)) { ++ if (PTR_ERR(mac->pextp) != -EPROBE_DEFER) ++ dev_err(eth->dev, "cannot get PHY, error %ld\n", ++ PTR_ERR(mac->pextp)); ++ ++ return PTR_ERR(mac->pextp); ++ } + } + + memset(mac->hwlro_ip, 0, sizeof(mac->hwlro_ip)); +@@ -4616,8 +4788,21 @@ static int mtk_add_mac(struct mtk_eth *e + phy_interface_zero(mac->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + mac->phylink_config.supported_interfaces); ++ } else if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_USXGMII)) { ++ mac->phylink_config.mac_capabilities |= MAC_5000FD | MAC_10000FD; ++ __set_bit(PHY_INTERFACE_MODE_5GBASER, ++ mac->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_10GBASER, ++ mac->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_USXGMII, ++ mac->phylink_config.supported_interfaces); + } + ++ if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_2P5GPHY) && ++ id == MTK_GMAC2_ID) ++ __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); +@@ -4662,6 +4847,26 @@ free_netdev: + return err; + } + ++static int mtk_mac_assign_address(struct mtk_eth *eth, int i, bool test_defer_only) ++{ ++ int err = of_get_ethdev_address(eth->mac[i]->of_node, eth->netdev[i]); ++ ++ if (err == -EPROBE_DEFER) ++ return err; ++ ++ if (test_defer_only) ++ return 0; ++ ++ if (err) { ++ /* If the mac address is invalid, use random mac address */ ++ eth_hw_addr_random(eth->netdev[i]); ++ dev_err(eth->dev, "generated random MAC address %pM\n", ++ eth->netdev[i]); ++ } ++ ++ return 0; ++} ++ + void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev) + { + struct net_device *dev, *tmp; +@@ -4805,7 +5010,8 @@ static int mtk_probe(struct platform_dev + regmap_write(cci, 0, 3); + } + +- if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) { ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII) && ++ !mtk_is_netsys_v3_or_greater(eth)) { + err = mtk_sgmii_init(eth); + + if (err) +@@ -4916,6 +5122,24 @@ static int mtk_probe(struct platform_dev + } + } + ++ for (i = 0; i < MTK_MAX_DEVS; i++) { ++ if (!eth->netdev[i]) ++ continue; ++ ++ err = mtk_mac_assign_address(eth, i, true); ++ if (err) ++ goto err_deinit_hw; ++ } ++ ++ for (i = 0; i < MTK_MAX_DEVS; i++) { ++ if (!eth->netdev[i]) ++ continue; ++ ++ err = mtk_mac_assign_address(eth, i, false); ++ if (err) ++ goto err_deinit_hw; ++ } ++ + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_INT)) { + err = devm_request_irq(eth->dev, eth->irq[0], + mtk_handle_irq, 0, +@@ -5018,6 +5242,11 @@ static int mtk_remove(struct platform_de + mtk_stop(eth->netdev[i]); + mac = netdev_priv(eth->netdev[i]); + phylink_disconnect_phy(mac->phylink); ++ if (mac->sgmii_pcs) ++ mtk_pcs_lynxi_put(mac->sgmii_pcs); ++ ++ if (mac->usxgmii_pcs) ++ mtk_usxgmii_pcs_put(mac->usxgmii_pcs); + } + + mtk_wed_exit(); +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -502,6 +503,21 @@ + #define INTF_MODE_RGMII_1000 (TRGMII_MODE | TRGMII_CENTRAL_ALIGNED) + #define INTF_MODE_RGMII_10_100 0 + ++/* XFI Mac control registers */ ++#define MTK_XMAC_BASE(x) (0x12000 + (((x) - 1) * 0x1000)) ++#define MTK_XMAC_MCR(x) (MTK_XMAC_BASE(x)) ++#define XMAC_MCR_TRX_DISABLE 0xf ++#define XMAC_MCR_FORCE_TX_FC BIT(5) ++#define XMAC_MCR_FORCE_RX_FC BIT(4) ++ ++/* XFI Mac logic reset registers */ ++#define MTK_XMAC_LOGIC_RST(x) (MTK_XMAC_BASE(x) + 0x10) ++#define XMAC_LOGIC_RST BIT(0) ++ ++/* XFI Mac count global control */ ++#define MTK_XMAC_CNT_CTRL(x) (MTK_XMAC_BASE(x) + 0x100) ++#define XMAC_GLB_CNTCLR BIT(0) ++ + /* GPIO port control registers for GMAC 2*/ + #define GPIO_OD33_CTRL8 0x4c0 + #define GPIO_BIAS_CTRL 0xed0 +@@ -527,6 +543,7 @@ + #define SYSCFG0_SGMII_GMAC2 ((3 << 8) & SYSCFG0_SGMII_MASK) + #define SYSCFG0_SGMII_GMAC1_V2 BIT(9) + #define SYSCFG0_SGMII_GMAC2_V2 BIT(8) ++#define SYSCFG0_SGMII_GMAC3_V2 BIT(7) + + + /* ethernet subsystem clock register */ +@@ -565,6 +582,11 @@ + #define GEPHY_MAC_SEL BIT(1) + + /* Top misc registers */ ++#define TOP_MISC_NETSYS_PCS_MUX 0x84 ++#define NETSYS_PCS_MUX_MASK GENMASK(1, 0) ++#define MUX_G2_USXGMII_SEL BIT(1) ++#define MUX_HSGMII1_G1_SEL BIT(0) ++ + #define USB_PHY_SWITCH_REG 0x218 + #define QPHY_SEL_MASK GENMASK(1, 0) + #define SGMII_QPHY_SEL 0x2 +@@ -589,6 +611,8 @@ + #define MT7628_SDM_RBCNT (MT7628_SDM_OFFSET + 0x10c) + #define MT7628_SDM_CS_ERR (MT7628_SDM_OFFSET + 0x110) + ++/* Debug Purpose Register */ ++#define MTK_PSE_FQFC_CFG 0x100 + #define MTK_FE_CDM1_FSM 0x220 + #define MTK_FE_CDM2_FSM 0x224 + #define MTK_FE_CDM3_FSM 0x238 +@@ -597,6 +621,11 @@ + #define MTK_FE_CDM6_FSM 0x328 + #define MTK_FE_GDM1_FSM 0x228 + #define MTK_FE_GDM2_FSM 0x22C ++#define MTK_FE_GDM3_FSM 0x23C ++#define MTK_FE_PSE_FREE 0x240 ++#define MTK_FE_DROP_FQ 0x244 ++#define MTK_FE_DROP_FC 0x248 ++#define MTK_FE_DROP_PPE 0x24C + + #define MTK_MAC_FSM(x) (0x1010C + ((x) * 0x100)) + +@@ -721,12 +750,8 @@ enum mtk_clks_map { + 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, +@@ -797,19 +822,9 @@ enum mtk_clks_map { + 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) | \ +@@ -943,6 +958,8 @@ enum mkt_eth_capabilities { + MTK_RGMII_BIT = 0, + MTK_TRGMII_BIT, + MTK_SGMII_BIT, ++ MTK_USXGMII_BIT, ++ MTK_2P5GPHY_BIT, + MTK_ESW_BIT, + MTK_GEPHY_BIT, + MTK_MUX_BIT, +@@ -963,8 +980,11 @@ enum mkt_eth_capabilities { + MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT, + MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT, + MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT, ++ MTK_ETH_MUX_GMAC2_TO_2P5GPHY_BIT, + MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT, + MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_BIT, ++ MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII_BIT, ++ MTK_ETH_MUX_GMAC123_TO_USXGMII_BIT, + + /* PATH BITS */ + MTK_ETH_PATH_GMAC1_RGMII_BIT, +@@ -972,14 +992,21 @@ enum mkt_eth_capabilities { + MTK_ETH_PATH_GMAC1_SGMII_BIT, + MTK_ETH_PATH_GMAC2_RGMII_BIT, + MTK_ETH_PATH_GMAC2_SGMII_BIT, ++ MTK_ETH_PATH_GMAC2_2P5GPHY_BIT, + MTK_ETH_PATH_GMAC2_GEPHY_BIT, ++ MTK_ETH_PATH_GMAC3_SGMII_BIT, + MTK_ETH_PATH_GDM1_ESW_BIT, ++ MTK_ETH_PATH_GMAC1_USXGMII_BIT, ++ MTK_ETH_PATH_GMAC2_USXGMII_BIT, ++ MTK_ETH_PATH_GMAC3_USXGMII_BIT, + }; + + /* Supported hardware group on SoCs */ + #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_USXGMII BIT_ULL(MTK_USXGMII_BIT) ++#define MTK_2P5GPHY BIT_ULL(MTK_2P5GPHY_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) +@@ -1002,10 +1029,16 @@ enum mkt_eth_capabilities { + BIT_ULL(MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT) + #define MTK_ETH_MUX_U3_GMAC2_TO_QPHY \ + BIT_ULL(MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT) ++#define MTK_ETH_MUX_GMAC2_TO_2P5GPHY \ ++ BIT_ULL(MTK_ETH_MUX_GMAC2_TO_2P5GPHY_BIT) + #define MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII \ + BIT_ULL(MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT) + #define MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII \ + BIT_ULL(MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_BIT) ++#define MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII \ ++ BIT_ULL(MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII_BIT) ++#define MTK_ETH_MUX_GMAC123_TO_USXGMII \ ++ BIT_ULL(MTK_ETH_MUX_GMAC123_TO_USXGMII_BIT) + + /* Supported path present on SoCs */ + #define MTK_ETH_PATH_GMAC1_RGMII BIT_ULL(MTK_ETH_PATH_GMAC1_RGMII_BIT) +@@ -1013,8 +1046,13 @@ enum mkt_eth_capabilities { + #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_2P5GPHY BIT_ULL(MTK_ETH_PATH_GMAC2_2P5GPHY_BIT) + #define MTK_ETH_PATH_GMAC2_GEPHY BIT_ULL(MTK_ETH_PATH_GMAC2_GEPHY_BIT) ++#define MTK_ETH_PATH_GMAC3_SGMII BIT_ULL(MTK_ETH_PATH_GMAC3_SGMII_BIT) + #define MTK_ETH_PATH_GDM1_ESW BIT_ULL(MTK_ETH_PATH_GDM1_ESW_BIT) ++#define MTK_ETH_PATH_GMAC1_USXGMII BIT_ULL(MTK_ETH_PATH_GMAC1_USXGMII_BIT) ++#define MTK_ETH_PATH_GMAC2_USXGMII BIT_ULL(MTK_ETH_PATH_GMAC2_USXGMII_BIT) ++#define MTK_ETH_PATH_GMAC3_USXGMII BIT_ULL(MTK_ETH_PATH_GMAC3_USXGMII_BIT) + + #define MTK_GMAC1_RGMII (MTK_ETH_PATH_GMAC1_RGMII | MTK_RGMII) + #define MTK_GMAC1_TRGMII (MTK_ETH_PATH_GMAC1_TRGMII | MTK_TRGMII) +@@ -1022,7 +1060,12 @@ enum mkt_eth_capabilities { + #define MTK_GMAC2_RGMII (MTK_ETH_PATH_GMAC2_RGMII | MTK_RGMII) + #define MTK_GMAC2_SGMII (MTK_ETH_PATH_GMAC2_SGMII | MTK_SGMII) + #define MTK_GMAC2_GEPHY (MTK_ETH_PATH_GMAC2_GEPHY | MTK_GEPHY) ++#define MTK_GMAC2_2P5GPHY (MTK_ETH_PATH_GMAC2_2P5GPHY | MTK_2P5GPHY) ++#define MTK_GMAC3_SGMII (MTK_ETH_PATH_GMAC3_SGMII | MTK_SGMII) + #define MTK_GDM1_ESW (MTK_ETH_PATH_GDM1_ESW | MTK_ESW) ++#define MTK_GMAC1_USXGMII (MTK_ETH_PATH_GMAC1_USXGMII | MTK_USXGMII) ++#define MTK_GMAC2_USXGMII (MTK_ETH_PATH_GMAC2_USXGMII | MTK_USXGMII) ++#define MTK_GMAC3_USXGMII (MTK_ETH_PATH_GMAC3_USXGMII | MTK_USXGMII) + + /* MUXes present on SoCs */ + /* 0: GDM1 -> GMAC1, 1: GDM1 -> ESW */ +@@ -1041,10 +1084,20 @@ enum mkt_eth_capabilities { + (MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII | MTK_MUX | \ + MTK_SHARED_SGMII) + ++/* 2: GMAC2 -> XGMII */ ++#define MTK_MUX_GMAC2_TO_2P5GPHY \ ++ (MTK_ETH_MUX_GMAC2_TO_2P5GPHY | MTK_MUX | MTK_INFRA) ++ + /* 0: GMACx -> GEPHY, 1: GMACx -> SGMII where x is 1 or 2 */ + #define MTK_MUX_GMAC12_TO_GEPHY_SGMII \ + (MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII | MTK_MUX) + ++#define MTK_MUX_GMAC123_TO_GEPHY_SGMII \ ++ (MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII | MTK_MUX) ++ ++#define MTK_MUX_GMAC123_TO_USXGMII \ ++ (MTK_ETH_MUX_GMAC123_TO_USXGMII | MTK_MUX | MTK_INFRA) ++ + #define MTK_HAS_CAPS(caps, _x) (((caps) & (_x)) == (_x)) + + #define MT7621_CAPS (MTK_GMAC1_RGMII | MTK_GMAC1_TRGMII | \ +@@ -1076,8 +1129,12 @@ enum mkt_eth_capabilities { + MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \ + MTK_RSTCTRL_PPE1 | MTK_SRAM) + +-#define MT7988_CAPS (MTK_36BIT_DMA | MTK_GDM1_ESW | MTK_QDMA | \ +- MTK_RSTCTRL_PPE1 | MTK_RSTCTRL_PPE2 | MTK_SRAM) ++#define MT7988_CAPS (MTK_36BIT_DMA | MTK_GDM1_ESW | MTK_GMAC1_SGMII | \ ++ MTK_GMAC2_2P5GPHY | MTK_GMAC2_SGMII | MTK_GMAC2_USXGMII | \ ++ MTK_GMAC3_SGMII | MTK_GMAC3_USXGMII | \ ++ MTK_MUX_GMAC123_TO_GEPHY_SGMII | \ ++ MTK_MUX_GMAC123_TO_USXGMII | MTK_MUX_GMAC2_TO_2P5GPHY | \ ++ MTK_QDMA | MTK_RSTCTRL_PPE1 | MTK_RSTCTRL_PPE2 | MTK_SRAM) + + struct mtk_tx_dma_desc_info { + dma_addr_t addr; +@@ -1314,6 +1371,9 @@ struct mtk_mac { + struct device_node *of_node; + struct phylink *phylink; + struct phylink_config phylink_config; ++ struct phylink_pcs *sgmii_pcs; ++ struct phylink_pcs *usxgmii_pcs; ++ struct phy *pextp; + struct mtk_eth *hw; + struct mtk_hw_stats *hw_stats; + __be32 hwlro_ip[MTK_MAX_LRO_IP_CNT]; +@@ -1437,6 +1497,19 @@ static inline u32 mtk_get_ib2_multicast_ + return MTK_FOE_IB2_MULTICAST; + } + ++static inline bool mtk_interface_mode_is_xgmii(phy_interface_t interface) ++{ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_INTERNAL: ++ case PHY_INTERFACE_MODE_USXGMII: ++ case PHY_INTERFACE_MODE_10GBASER: ++ case PHY_INTERFACE_MODE_5GBASER: ++ return true; ++ default: ++ return false; ++ } ++} ++ + /* read the hardware status register */ + void mtk_stats_update_mac(struct mtk_mac *mac); + +@@ -1445,8 +1518,10 @@ u32 mtk_r32(struct mtk_eth *eth, unsigne + 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_2p5gphy_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); ++int mtk_gmac_usxgmii_path_setup(struct mtk_eth *eth, int mac_id); + + int mtk_eth_offload_init(struct mtk_eth *eth); + int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type, diff --git a/target/linux/generic/pending-6.6/738-net-ethernet-mtk_eth_soc-set-coherent-mask-to-get-PP.patch b/target/linux/generic/pending-6.6/738-net-ethernet-mtk_eth_soc-set-coherent-mask-to-get-PP.patch new file mode 100644 index 0000000000..b598d15866 --- /dev/null +++ b/target/linux/generic/pending-6.6/738-net-ethernet-mtk_eth_soc-set-coherent-mask-to-get-PP.patch @@ -0,0 +1,46 @@ +From dee3f591103910c8d8b2a6d57879ccd2a4be4b10 Mon Sep 17 00:00:00 2001 +Message-ID: +From: Daniel Golle +Date: Wed, 24 Jan 2024 03:19:49 +0000 +Subject: [PATCH net] net: ethernet: mtk_eth_soc: set coherent mask to get PPE + working +To: Felix Fietkau , + Sean Wang , + Mark Lee , + Lorenzo Bianconi , + David S. Miller , + Eric Dumazet , + Jakub Kicinski , + Paolo Abeni , + Matthias Brugger , + AngeloGioacchino Del Regno , + Daniel Golle , + netdev@vger.kernel.org, + linux-kernel@vger.kernel.org, + linux-arm-kernel@lists.infradead.org, + linux-mediatek@lists.infradead.org + +Set DMA coherent mask to 32-bit which makes PPE offloading engine start +working on BPi-R4 which got 4 GiB of RAM. + +Fixes: 2d75891ebc09 ("net: ethernet: mtk_eth_soc: support 36-bit DMA addressing on MT7988") +Suggested-by: Elad Yifee +Signed-off-by: Daniel Golle +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -4963,7 +4963,10 @@ 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)); ++ err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(36)); ++ if (!err) ++ err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); ++ + if (err) { + dev_err(&pdev->dev, "Wrong DMA config\n"); + return -EINVAL; diff --git a/target/linux/generic/pending-6.6/739-01-dt-bindings-phy-mediatek-xfi-tphy-add-new-bindings.patch b/target/linux/generic/pending-6.6/739-01-dt-bindings-phy-mediatek-xfi-tphy-add-new-bindings.patch new file mode 100644 index 0000000000..1f1c40b1d9 --- /dev/null +++ b/target/linux/generic/pending-6.6/739-01-dt-bindings-phy-mediatek-xfi-tphy-add-new-bindings.patch @@ -0,0 +1,136 @@ +From patchwork Thu Feb 1 21:52:20 2024 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Daniel Golle +X-Patchwork-Id: 13541842 +Date: Thu, 1 Feb 2024 21:52:20 +0000 +From: Daniel Golle +To: Bc-bocun Chen , + Steven Liu , + John Crispin , + Chunfeng Yun , + Vinod Koul , + Kishon Vijay Abraham I , + Rob Herring , + Krzysztof Kozlowski , + Conor Dooley , + Daniel Golle , + Qingfang Deng , + SkyLake Huang , + Matthias Brugger , + AngeloGioacchino Del Regno , + Philipp Zabel , + linux-arm-kernel@lists.infradead.org, + linux-mediatek@lists.infradead.org, linux-phy@lists.infradead.org, + devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, + netdev@vger.kernel.org +Subject: [PATCH 1/2] dt-bindings: phy: mediatek,xfi-tphy: add new bindings +Message-ID: + <702afb0c1246d95c90b22e57105304028bdd3083.1706823233.git.daniel@makrotopia.org> +MIME-Version: 1.0 +Content-Disposition: inline +List-Id: Linux Phy Mailing list + +Add bindings for the MediaTek XFI T-PHY Ethernet SerDes PHY found in the +MediaTek MT7988 SoC which can operate at various interfaces modes: + +via USXGMII PCS: + * USXGMII + * 10GBase-R + * 5GBase-R + +via LynxI SGMII PCS: + * 2500Base-X + * 1000Base-X + * Cisco SGMII (MAC side) + +Signed-off-by: Daniel Golle +--- + .../bindings/phy/mediatek,xfi-tphy.yaml | 80 +++++++++++++++++++ + 1 file changed, 80 insertions(+) + create mode 100644 Documentation/devicetree/bindings/phy/mediatek,xfi-tphy.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/phy/mediatek,xfi-tphy.yaml +@@ -0,0 +1,80 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/phy/mediatek,xfi-tphy.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: MediaTek XFI T-PHY ++ ++maintainers: ++ - Daniel Golle ++ ++description: ++ The MediaTek XFI SerDes T-PHY provides the physical SerDes lanes ++ used by the (10G/5G) USXGMII PCS and (1G/2.5G) LynxI PCS found in ++ MediaTek's 10G-capabale SoCs. ++ ++properties: ++ $nodename: ++ pattern: "^phy@[0-9a-f]+$" ++ ++ compatible: ++ const: mediatek,mt7988-xfi-tphy ++ ++ reg: ++ maxItems: 1 ++ ++ clocks: ++ items: ++ - description: XFI PHY clock ++ - description: XFI register clock ++ ++ clock-names: ++ items: ++ - const: xfipll ++ - const: topxtal ++ ++ resets: ++ items: ++ - description: PEXTP reset ++ ++ mediatek,usxgmii-performance-errata: ++ $ref: /schemas/types.yaml#/definitions/flag ++ description: ++ One instance of the T-PHY on MT7988 suffers from a performance ++ problem in 10GBase-R mode which needs a work-around in the driver. ++ The work-around is enabled using this flag. ++ ++ "#phy-cells": ++ const: 0 ++ ++required: ++ - compatible ++ - reg ++ - clocks ++ - clock-names ++ - resets ++ - "#phy-cells" ++ ++additionalProperties: false ++ ++examples: ++ - | ++ #include ++ soc { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ++ phy@11f20000 { ++ compatible = "mediatek,mt7988-xfi-tphy"; ++ reg = <0 0x11f20000 0 0x10000>; ++ clocks = <&xfi_pll CLK_XFIPLL_PLL_EN>, ++ <&topckgen CLK_TOP_XFI_PHY_0_XTAL_SEL>; ++ clock-names = "xfipll", "topxtal"; ++ resets = <&watchdog 14>; ++ mediatek,usxgmii-performance-errata; ++ #phy-cells = <0>; ++ }; ++ }; ++ ++... diff --git a/target/linux/generic/pending-6.6/739-02-phy-add-driver-for-MediaTek-XFI-T-PHY.patch b/target/linux/generic/pending-6.6/739-02-phy-add-driver-for-MediaTek-XFI-T-PHY.patch new file mode 100644 index 0000000000..d7424020a7 --- /dev/null +++ b/target/linux/generic/pending-6.6/739-02-phy-add-driver-for-MediaTek-XFI-T-PHY.patch @@ -0,0 +1,497 @@ +From patchwork Thu Feb 1 21:53:06 2024 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Daniel Golle +X-Patchwork-Id: 13541843 +Date: Thu, 1 Feb 2024 21:53:06 +0000 +From: Daniel Golle +To: Bc-bocun Chen , + Chunfeng Yun , + Vinod Koul , + Kishon Vijay Abraham I , + Rob Herring , + Krzysztof Kozlowski , + Conor Dooley , + Daniel Golle , + Qingfang Deng , + SkyLake Huang , + Matthias Brugger , + AngeloGioacchino Del Regno , + Philipp Zabel , + linux-arm-kernel@lists.infradead.org, + linux-mediatek@lists.infradead.org, linux-phy@lists.infradead.org, + devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, + netdev@vger.kernel.org +Subject: [PATCH 2/2] phy: add driver for MediaTek XFI T-PHY +Message-ID: + +References: + <702afb0c1246d95c90b22e57105304028bdd3083.1706823233.git.daniel@makrotopia.org> +MIME-Version: 1.0 +Content-Disposition: inline +In-Reply-To: + <702afb0c1246d95c90b22e57105304028bdd3083.1706823233.git.daniel@makrotopia.org> +List-Id: Linux Phy Mailing list + +Add driver for MediaTek's XFI T-PHY, 10 Gigabit/s Ethernet SerDes PHY +which can be found in the MT7988 SoC. + +The PHY can operates only in PHY_MODE_ETHERNET, the submode is one of +PHY_INTERFACE_MODE_* corresponding to the supported modes: + + * USXGMII \ + * 10GBase-R }- USXGMII PCS - XGDM \ + * 5GBase-R / \ + }- Ethernet MAC + * 2500Base-X \ / + * 1000Base-X }- LynxI PCS - GDM / + * Cisco SGMII (MAC side) / + +In order to work-around a performance issue present on the first of +two XFI T-PHYs present in MT7988, special tuning is applied which can be +selected by adding the 'mediatek,usxgmii-performance-errata' property to +the device tree node. + +There is no documentation for most registers used for the +analog/tuning part, however, most of the registers have been partially +reverse-engineered from MediaTek's SDK implementation (an opaque +sequence of 32-bit register writes) and descriptions for all relevant +digital registers and bits such as resets and muxes have been supplied +by MediaTek. + +Signed-off-by: Daniel Golle +--- + MAINTAINERS | 1 + + drivers/phy/mediatek/Kconfig | 12 + + drivers/phy/mediatek/Makefile | 1 + + drivers/phy/mediatek/phy-mtk-xfi-tphy.c | 392 ++++++++++++++++++++++++ + 4 files changed, 406 insertions(+) + create mode 100644 drivers/phy/mediatek/phy-mtk-xfi-tphy.c + +--- a/drivers/phy/mediatek/Kconfig ++++ b/drivers/phy/mediatek/Kconfig +@@ -13,6 +13,18 @@ config PHY_MTK_PCIE + callback for PCIe GEN3 port, it supports software efuse + initialization. + ++config PHY_MTK_XFI_TPHY ++ tristate "MediaTek XFI T-PHY Driver" ++ depends on ARCH_MEDIATEK || COMPILE_TEST ++ depends on OF && OF_ADDRESS ++ depends on HAS_IOMEM ++ select GENERIC_PHY ++ help ++ Say 'Y' here to add support for MediaTek XFI T-PHY driver. ++ The driver provides access to the Ethernet SerDes T-PHY supporting ++ 1GE and 2.5GE modes via the LynxI PCS, and 5GE and 10GE modes ++ via the USXGMII PCS found in MediaTek SoCs with 10G Ethernet. ++ + config PHY_MTK_TPHY + tristate "MediaTek T-PHY Driver" + depends on ARCH_MEDIATEK || COMPILE_TEST +--- a/drivers/phy/mediatek/Makefile ++++ b/drivers/phy/mediatek/Makefile +@@ -8,6 +8,7 @@ obj-$(CONFIG_PHY_MTK_PCIE) += phy-mtk-p + obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o + obj-$(CONFIG_PHY_MTK_UFS) += phy-mtk-ufs.o + obj-$(CONFIG_PHY_MTK_XSPHY) += phy-mtk-xsphy.o ++obj-$(CONFIG_PHY_MTK_XFI_TPHY) += phy-mtk-xfi-tphy.o + + phy-mtk-hdmi-drv-y := phy-mtk-hdmi.o + phy-mtk-hdmi-drv-y += phy-mtk-hdmi-mt2701.o +--- /dev/null ++++ b/drivers/phy/mediatek/phy-mtk-xfi-tphy.c +@@ -0,0 +1,392 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* MediaTek 10GE SerDes PHY driver ++ * ++ * Copyright (c) 2024 Daniel Golle ++ * Bc-bocun Chen ++ * based on mtk_usxgmii.c found in MediaTek's SDK released under GPL-2.0 ++ * Copyright (c) 2022 MediaTek Inc. ++ * Author: Henry Yen ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MTK_XFI_TPHY_NUM_CLOCKS 2 ++ ++#define REG_DIG_GLB_70 0x0070 ++#define XTP_PCS_RX_EQ_IN_PROGRESS(x) FIELD_PREP(GENMASK(25, 24), (x)) ++#define XTP_PCS_MODE_MASK GENMASK(17, 16) ++#define XTP_PCS_MODE(x) FIELD_PREP(GENMASK(17, 16), (x)) ++#define XTP_PCS_RST_B BIT(15) ++#define XTP_FRC_PCS_RST_B BIT(14) ++#define XTP_PCS_PWD_SYNC_MASK GENMASK(13, 12) ++#define XTP_PCS_PWD_SYNC(x) FIELD_PREP(XTP_PCS_PWD_SYNC_MASK, (x)) ++#define XTP_PCS_PWD_ASYNC_MASK GENMASK(11, 10) ++#define XTP_PCS_PWD_ASYNC(x) FIELD_PREP(XTP_PCS_PWD_ASYNC_MASK, (x)) ++#define XTP_FRC_PCS_PWD_ASYNC BIT(8) ++#define XTP_PCS_UPDT BIT(4) ++#define XTP_PCS_IN_FR_RG BIT(0) ++ ++#define REG_DIG_GLB_F4 0x00f4 ++#define XFI_DPHY_PCS_SEL BIT(0) ++#define XFI_DPHY_PCS_SEL_SGMII FIELD_PREP(XFI_DPHY_PCS_SEL, 1) ++#define XFI_DPHY_PCS_SEL_USXGMII FIELD_PREP(XFI_DPHY_PCS_SEL, 0) ++#define XFI_DPHY_AD_SGDT_FRC_EN BIT(5) ++ ++#define REG_DIG_LN_TRX_40 0x3040 ++#define XTP_LN_FRC_TX_DATA_EN BIT(29) ++#define XTP_LN_TX_DATA_EN BIT(28) ++ ++#define REG_DIG_LN_TRX_B0 0x30b0 ++#define XTP_LN_FRC_TX_MACCK_EN BIT(5) ++#define XTP_LN_TX_MACCK_EN BIT(4) ++ ++#define REG_ANA_GLB_D0 0x90d0 ++#define XTP_GLB_USXGMII_SEL_MASK GENMASK(3, 1) ++#define XTP_GLB_USXGMII_SEL(x) FIELD_PREP(GENMASK(3, 1), (x)) ++#define XTP_GLB_USXGMII_EN BIT(0) ++ ++struct mtk_xfi_tphy { ++ void __iomem *base; ++ struct device *dev; ++ struct reset_control *reset; ++ struct clk_bulk_data clocks[MTK_XFI_TPHY_NUM_CLOCKS]; ++ bool da_war; ++}; ++ ++static void mtk_xfi_tphy_write(struct mtk_xfi_tphy *xfi_tphy, u16 reg, ++ u32 value) ++{ ++ iowrite32(value, xfi_tphy->base + reg); ++} ++ ++static void mtk_xfi_tphy_rmw(struct mtk_xfi_tphy *xfi_tphy, u16 reg, ++ u32 clr, u32 set) ++{ ++ u32 val; ++ ++ val = ioread32(xfi_tphy->base + reg); ++ val &= ~clr; ++ val |= set; ++ iowrite32(val, xfi_tphy->base + reg); ++} ++ ++static void mtk_xfi_tphy_set(struct mtk_xfi_tphy *xfi_tphy, u16 reg, ++ u32 set) ++{ ++ mtk_xfi_tphy_rmw(xfi_tphy, reg, 0, set); ++} ++ ++static void mtk_xfi_tphy_clear(struct mtk_xfi_tphy *xfi_tphy, u16 reg, ++ u32 clr) ++{ ++ mtk_xfi_tphy_rmw(xfi_tphy, reg, clr, 0); ++} ++ ++static void mtk_xfi_tphy_setup(struct mtk_xfi_tphy *xfi_tphy, ++ phy_interface_t interface) ++{ ++ bool is_2p5g = (interface == PHY_INTERFACE_MODE_2500BASEX); ++ bool is_1g = (interface == PHY_INTERFACE_MODE_1000BASEX || ++ interface == PHY_INTERFACE_MODE_SGMII); ++ bool is_10g = (interface == PHY_INTERFACE_MODE_10GBASER || ++ interface == PHY_INTERFACE_MODE_USXGMII); ++ bool is_5g = (interface == PHY_INTERFACE_MODE_5GBASER); ++ bool is_xgmii = (is_10g || is_5g); ++ ++ dev_dbg(xfi_tphy->dev, "setting up for mode %s\n", phy_modes(interface)); ++ ++ /* Setup PLL setting */ ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x9024, 0x100000, is_10g ? 0x0 : 0x100000); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x2020, 0x202000, is_5g ? 0x202000 : 0x0); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x2030, 0x500, is_1g ? 0x0 : 0x500); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x2034, 0xa00, is_1g ? 0x0 : 0xa00); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x2040, 0x340000, is_1g ? 0x200000 : ++ 0x140000); ++ ++ /* Setup RXFE BW setting */ ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x50f0, 0xc10, is_1g ? 0x410 : ++ is_5g ? 0x800 : 0x400); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x50e0, 0x4000, is_5g ? 0x0 : 0x4000); ++ ++ /* Setup RX CDR setting */ ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x506c, 0x30000, is_5g ? 0x0 : 0x30000); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x5070, 0x670000, is_5g ? 0x620000 : 0x50000); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x5074, 0x180000, is_5g ? 0x180000 : 0x0); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x5078, 0xf000400, is_5g ? 0x8000000 : ++ 0x7000400); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x507c, 0x5000500, is_5g ? 0x4000400 : ++ 0x1000100); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x5080, 0x1410, is_1g ? 0x400 : ++ is_5g ? 0x1010 : 0x0); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x5084, 0x30300, is_1g ? 0x30300 : ++ is_5g ? 0x30100 : ++ 0x100); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x5088, 0x60200, is_1g ? 0x20200 : ++ is_5g ? 0x40000 : ++ 0x20000); ++ ++ /* Setting RXFE adaptation range setting */ ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x50e4, 0xc0000, is_5g ? 0x0 : 0xc0000); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x50e8, 0x40000, is_5g ? 0x0 : 0x40000); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x50ec, 0xa00, is_1g ? 0x200 : 0x800); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x50a8, 0xee0000, is_5g ? 0x800000 : ++ 0x6e0000); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x6004, 0x190000, is_5g ? 0x0 : 0x190000); ++ if (is_10g) ++ mtk_xfi_tphy_write(xfi_tphy, 0x00f8, 0x01423342); ++ else if (is_5g) ++ mtk_xfi_tphy_write(xfi_tphy, 0x00f8, 0x00a132a1); ++ else if (is_2p5g) ++ mtk_xfi_tphy_write(xfi_tphy, 0x00f8, 0x009c329c); ++ else ++ mtk_xfi_tphy_write(xfi_tphy, 0x00f8, 0x00fa32fa); ++ ++ /* Force SGDT_OUT off and select PCS */ ++ mtk_xfi_tphy_rmw(xfi_tphy, REG_DIG_GLB_F4, ++ XFI_DPHY_AD_SGDT_FRC_EN | XFI_DPHY_PCS_SEL, ++ XFI_DPHY_AD_SGDT_FRC_EN | ++ (is_xgmii ? XFI_DPHY_PCS_SEL_USXGMII : ++ XFI_DPHY_PCS_SEL_SGMII)); ++ ++ ++ /* Force GLB_CKDET_OUT */ ++ mtk_xfi_tphy_set(xfi_tphy, 0x0030, 0xc00); ++ ++ /* Force AEQ on */ ++ mtk_xfi_tphy_write(xfi_tphy, REG_DIG_GLB_70, ++ XTP_PCS_RX_EQ_IN_PROGRESS(2) | ++ XTP_PCS_PWD_SYNC(2) | ++ XTP_PCS_PWD_ASYNC(2)); ++ ++ usleep_range(1, 5); ++ ++ /* Setup TX DA default value */ ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x30b0, 0x30, 0x20); ++ mtk_xfi_tphy_write(xfi_tphy, 0x3028, 0x00008a01); ++ mtk_xfi_tphy_write(xfi_tphy, 0x302c, 0x0000a884); ++ mtk_xfi_tphy_write(xfi_tphy, 0x3024, 0x00083002); ++ ++ /* Setup RG default value */ ++ if (is_xgmii) { ++ mtk_xfi_tphy_write(xfi_tphy, 0x3010, 0x00022220); ++ mtk_xfi_tphy_write(xfi_tphy, 0x5064, 0x0f020a01); ++ mtk_xfi_tphy_write(xfi_tphy, 0x50b4, 0x06100600); ++ if (interface == PHY_INTERFACE_MODE_USXGMII) ++ mtk_xfi_tphy_write(xfi_tphy, 0x3048, 0x40704000); ++ else ++ mtk_xfi_tphy_write(xfi_tphy, 0x3048, 0x47684100); ++ } else { ++ mtk_xfi_tphy_write(xfi_tphy, 0x3010, 0x00011110); ++ mtk_xfi_tphy_write(xfi_tphy, 0x3048, 0x40704000); ++ } ++ ++ if (is_1g) ++ mtk_xfi_tphy_write(xfi_tphy, 0x3064, 0x0000c000); ++ ++ /* Setup RX EQ initial value */ ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x3050, 0xa8000000, ++ (interface != PHY_INTERFACE_MODE_10GBASER) ? ++ 0xa8000000 : 0x0); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x3054, 0xaa, ++ (interface != PHY_INTERFACE_MODE_10GBASER) ? ++ 0xaa : 0x0); ++ ++ if (is_xgmii) ++ mtk_xfi_tphy_write(xfi_tphy, 0x306c, 0x00000f00); ++ else if (is_2p5g) ++ mtk_xfi_tphy_write(xfi_tphy, 0x306c, 0x22000f00); ++ else ++ mtk_xfi_tphy_write(xfi_tphy, 0x306c, 0x20200f00); ++ ++ if (interface == PHY_INTERFACE_MODE_10GBASER && xfi_tphy->da_war) ++ mtk_xfi_tphy_rmw(xfi_tphy, 0xa008, 0x10000, 0x10000); ++ ++ mtk_xfi_tphy_rmw(xfi_tphy, 0xa060, 0x50000, is_xgmii ? 0x40000 : ++ 0x50000); ++ ++ /* Setup PHYA speed */ ++ mtk_xfi_tphy_rmw(xfi_tphy, REG_ANA_GLB_D0, ++ XTP_GLB_USXGMII_SEL_MASK | XTP_GLB_USXGMII_EN, ++ is_10g ? XTP_GLB_USXGMII_SEL(0) : ++ is_5g ? XTP_GLB_USXGMII_SEL(1) : ++ is_2p5g ? XTP_GLB_USXGMII_SEL(2) : ++ XTP_GLB_USXGMII_SEL(3)); ++ mtk_xfi_tphy_set(xfi_tphy, REG_ANA_GLB_D0, XTP_GLB_USXGMII_EN); ++ ++ /* Release reset */ ++ mtk_xfi_tphy_set(xfi_tphy, REG_DIG_GLB_70, ++ XTP_PCS_RST_B | XTP_FRC_PCS_RST_B); ++ usleep_range(150, 500); ++ ++ /* Switch to P0 */ ++ mtk_xfi_tphy_rmw(xfi_tphy, REG_DIG_GLB_70, ++ XTP_PCS_PWD_SYNC_MASK | ++ XTP_PCS_PWD_ASYNC_MASK, ++ XTP_FRC_PCS_PWD_ASYNC | ++ XTP_PCS_UPDT | XTP_PCS_IN_FR_RG); ++ usleep_range(1, 5); ++ ++ mtk_xfi_tphy_clear(xfi_tphy, REG_DIG_GLB_70, XTP_PCS_UPDT); ++ usleep_range(15, 50); ++ ++ if (is_xgmii) { ++ /* Switch to Gen3 */ ++ mtk_xfi_tphy_rmw(xfi_tphy, REG_DIG_GLB_70, ++ XTP_PCS_MODE_MASK | XTP_PCS_UPDT, ++ XTP_PCS_MODE(2) | XTP_PCS_UPDT); ++ } else { ++ /* Switch to Gen2 */ ++ mtk_xfi_tphy_rmw(xfi_tphy, REG_DIG_GLB_70, ++ XTP_PCS_MODE_MASK | XTP_PCS_UPDT, ++ XTP_PCS_MODE(1) | XTP_PCS_UPDT); ++ } ++ usleep_range(1, 5); ++ ++ mtk_xfi_tphy_clear(xfi_tphy, REG_DIG_GLB_70, XTP_PCS_UPDT); ++ ++ usleep_range(100, 500); ++ ++ /* Enable MAC CK */ ++ mtk_xfi_tphy_set(xfi_tphy, REG_DIG_LN_TRX_B0, XTP_LN_TX_MACCK_EN); ++ mtk_xfi_tphy_clear(xfi_tphy, REG_DIG_GLB_F4, XFI_DPHY_AD_SGDT_FRC_EN); ++ ++ /* Enable TX data */ ++ mtk_xfi_tphy_set(xfi_tphy, REG_DIG_LN_TRX_40, ++ XTP_LN_FRC_TX_DATA_EN | XTP_LN_TX_DATA_EN); ++ usleep_range(400, 1000); ++} ++ ++static int mtk_xfi_tphy_set_mode(struct phy *phy, enum phy_mode mode, int ++ submode) ++{ ++ struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy); ++ ++ if (mode != PHY_MODE_ETHERNET) ++ return -EINVAL; ++ ++ switch (submode) { ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_5GBASER: ++ case PHY_INTERFACE_MODE_10GBASER: ++ case PHY_INTERFACE_MODE_USXGMII: ++ mtk_xfi_tphy_setup(xfi_tphy, submode); ++ return 0; ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int mtk_xfi_tphy_reset(struct phy *phy) ++{ ++ struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy); ++ ++ reset_control_assert(xfi_tphy->reset); ++ usleep_range(100, 500); ++ reset_control_deassert(xfi_tphy->reset); ++ usleep_range(1, 10); ++ ++ return 0; ++} ++ ++static int mtk_xfi_tphy_power_on(struct phy *phy) ++{ ++ struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy); ++ ++ return clk_bulk_prepare_enable(MTK_XFI_TPHY_NUM_CLOCKS, xfi_tphy->clocks); ++} ++ ++static int mtk_xfi_tphy_power_off(struct phy *phy) ++{ ++ struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy); ++ ++ clk_bulk_disable_unprepare(MTK_XFI_TPHY_NUM_CLOCKS, xfi_tphy->clocks); ++ ++ return 0; ++} ++ ++static const struct phy_ops mtk_xfi_tphy_ops = { ++ .power_on = mtk_xfi_tphy_power_on, ++ .power_off = mtk_xfi_tphy_power_off, ++ .set_mode = mtk_xfi_tphy_set_mode, ++ .reset = mtk_xfi_tphy_reset, ++ .owner = THIS_MODULE, ++}; ++ ++static int mtk_xfi_tphy_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct phy_provider *phy_provider; ++ struct mtk_xfi_tphy *xfi_tphy; ++ struct phy *phy; ++ ++ if (!np) ++ return -ENODEV; ++ ++ xfi_tphy = devm_kzalloc(&pdev->dev, sizeof(*xfi_tphy), GFP_KERNEL); ++ if (!xfi_tphy) ++ return -ENOMEM; ++ ++ xfi_tphy->base = devm_of_iomap(&pdev->dev, np, 0, NULL); ++ if (!xfi_tphy->base) ++ return -EIO; ++ ++ xfi_tphy->dev = &pdev->dev; ++ ++ xfi_tphy->clocks[0].id = "topxtal"; ++ xfi_tphy->clocks[0].clk = devm_clk_get(&pdev->dev, xfi_tphy->clocks[0].id); ++ if (IS_ERR(xfi_tphy->clocks[0].clk)) ++ return PTR_ERR(xfi_tphy->clocks[0].clk); ++ ++ xfi_tphy->clocks[1].id = "xfipll"; ++ xfi_tphy->clocks[1].clk = devm_clk_get(&pdev->dev, xfi_tphy->clocks[1].id); ++ if (IS_ERR(xfi_tphy->clocks[1].clk)) ++ return PTR_ERR(xfi_tphy->clocks[1].clk); ++ ++ xfi_tphy->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL); ++ if (IS_ERR(xfi_tphy->reset)) ++ return PTR_ERR(xfi_tphy->reset); ++ ++ xfi_tphy->da_war = of_property_read_bool(np, ++ "mediatek,usxgmii-performance-errata"); ++ ++ phy = devm_phy_create(&pdev->dev, NULL, &mtk_xfi_tphy_ops); ++ if (IS_ERR(phy)) ++ return PTR_ERR(phy); ++ ++ phy_set_drvdata(phy, xfi_tphy); ++ ++ phy_provider = devm_of_phy_provider_register(&pdev->dev, ++ of_phy_simple_xlate); ++ ++ return PTR_ERR_OR_ZERO(phy_provider); ++} ++ ++static const struct of_device_id mtk_xfi_tphy_match[] = { ++ { .compatible = "mediatek,mt7988-xfi-tphy", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, mtk_xfi_tphy_match); ++ ++static struct platform_driver mtk_xfi_tphy_driver = { ++ .probe = mtk_xfi_tphy_probe, ++ .driver = { ++ .name = "mtk-xfi-tphy", ++ .of_match_table = mtk_xfi_tphy_match, ++ }, ++}; ++module_platform_driver(mtk_xfi_tphy_driver); ++ ++MODULE_DESCRIPTION("MediaTek XFI T-PHY driver"); ++MODULE_AUTHOR("Daniel Golle "); ++MODULE_AUTHOR("Bc-bocun Chen "); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/pending-6.6/739-03-net-pcs-pcs-mtk-lynxi-add-platform-driver-for-MT7988.patch b/target/linux/generic/pending-6.6/739-03-net-pcs-pcs-mtk-lynxi-add-platform-driver-for-MT7988.patch new file mode 100644 index 0000000000..adb95e9587 --- /dev/null +++ b/target/linux/generic/pending-6.6/739-03-net-pcs-pcs-mtk-lynxi-add-platform-driver-for-MT7988.patch @@ -0,0 +1,371 @@ +From 4b1a2716299c0e96a698044aebf3f80513509ae7 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 12 Dec 2023 03:47:18 +0000 +Subject: [PATCH 3/5] net: pcs: pcs-mtk-lynxi: add platform driver for MT7988 + +Introduce a proper platform MFD driver for the LynxI (H)SGMII PCS which +is going to initially be used for the MT7988 SoC. + +Signed-off-by: Daniel Golle +--- + drivers/net/pcs/pcs-mtk-lynxi.c | 227 ++++++++++++++++++++++++++++-- + include/linux/pcs/pcs-mtk-lynxi.h | 11 ++ + 2 files changed, 227 insertions(+), 11 deletions(-) + +--- a/drivers/net/pcs/pcs-mtk-lynxi.c ++++ b/drivers/net/pcs/pcs-mtk-lynxi.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + // Copyright (c) 2018-2019 MediaTek Inc. +-/* A library for MediaTek SGMII circuit ++/* A library and platform driver for the MediaTek LynxI SGMII circuit + * + * Author: Sean Wang + * Author: Alexander Couzens +@@ -8,11 +8,17 @@ + * + */ + ++#include + #include ++#include ++#include + #include ++#include + #include + #include ++#include + #include ++#include + + /* SGMII subsystem config registers */ + /* BMCR (low 16) BMSR (high 16) */ +@@ -65,6 +71,8 @@ + #define SGMII_PN_SWAP_MASK GENMASK(1, 0) + #define SGMII_PN_SWAP_TX_RX (BIT(0) | BIT(1)) + ++#define MTK_NETSYS_V3_AMA_RGC3 0x128 ++ + /* struct mtk_pcs_lynxi - This structure holds each sgmii regmap andassociated + * data + * @regmap: The register map pointing at the range used to setup +@@ -74,15 +82,29 @@ + * @interface: Currently configured interface mode + * @pcs: Phylink PCS structure + * @flags: Flags indicating hardware properties ++ * @rstc: Reset controller ++ * @sgmii_sel: SGMII Register Clock ++ * @sgmii_rx: SGMII RX Clock ++ * @sgmii_tx: SGMII TX Clock ++ * @node: List node + */ + struct mtk_pcs_lynxi { + struct regmap *regmap; ++ struct device *dev; + u32 ana_rgc3; + phy_interface_t interface; + struct phylink_pcs pcs; + u32 flags; ++ struct reset_control *rstc; ++ struct clk *sgmii_sel; ++ struct clk *sgmii_rx; ++ struct clk *sgmii_tx; ++ struct list_head node; + }; + ++static LIST_HEAD(mtk_pcs_lynxi_instances); ++static DEFINE_MUTEX(instance_mutex); ++ + static struct mtk_pcs_lynxi *pcs_to_mtk_pcs_lynxi(struct phylink_pcs *pcs) + { + return container_of(pcs, struct mtk_pcs_lynxi, pcs); +@@ -102,6 +124,17 @@ static void mtk_pcs_lynxi_get_state(stru + FIELD_GET(SGMII_LPA, adv)); + } + ++static void mtk_sgmii_reset(struct mtk_pcs_lynxi *mpcs) ++{ ++ if (!mpcs->rstc) ++ return; ++ ++ reset_control_assert(mpcs->rstc); ++ udelay(100); ++ reset_control_deassert(mpcs->rstc); ++ mdelay(1); ++} ++ + static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int neg_mode, + phy_interface_t interface, + const unsigned long *advertising, +@@ -148,6 +181,7 @@ static int mtk_pcs_lynxi_config(struct p + SGMII_PHYA_PWD); + + /* Reset SGMII PCS state */ ++ mtk_sgmii_reset(mpcs); + regmap_set_bits(mpcs->regmap, SGMSYS_RESERVED_0, + SGMII_SW_RESET); + +@@ -234,10 +268,29 @@ static void mtk_pcs_lynxi_link_up(struct + } + } + ++static int mtk_pcs_lynxi_enable(struct phylink_pcs *pcs) ++{ ++ struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); ++ ++ if (mpcs->sgmii_tx && mpcs->sgmii_rx) { ++ clk_prepare_enable(mpcs->sgmii_rx); ++ clk_prepare_enable(mpcs->sgmii_tx); ++ } ++ ++ return 0; ++} ++ + static void mtk_pcs_lynxi_disable(struct phylink_pcs *pcs) + { + struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); + ++ regmap_set_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, SGMII_PHYA_PWD); ++ ++ if (mpcs->sgmii_tx && mpcs->sgmii_rx) { ++ clk_disable_unprepare(mpcs->sgmii_tx); ++ clk_disable_unprepare(mpcs->sgmii_rx); ++ } ++ + mpcs->interface = PHY_INTERFACE_MODE_NA; + } + +@@ -247,11 +300,12 @@ static const struct phylink_pcs_ops mtk_ + .pcs_an_restart = mtk_pcs_lynxi_restart_an, + .pcs_link_up = mtk_pcs_lynxi_link_up, + .pcs_disable = mtk_pcs_lynxi_disable, ++ .pcs_enable = mtk_pcs_lynxi_enable, + }; + +-struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev, +- struct regmap *regmap, u32 ana_rgc3, +- u32 flags) ++static struct phylink_pcs *mtk_pcs_lynxi_init(struct device *dev, struct regmap *regmap, ++ u32 ana_rgc3, u32 flags, ++ struct mtk_pcs_lynxi *prealloc) + { + struct mtk_pcs_lynxi *mpcs; + u32 id, ver; +@@ -259,29 +313,33 @@ struct phylink_pcs *mtk_pcs_lynxi_create + + ret = regmap_read(regmap, SGMSYS_PCS_DEVICE_ID, &id); + if (ret < 0) +- return NULL; ++ return ERR_PTR(ret); + + if (id != SGMII_LYNXI_DEV_ID) { + dev_err(dev, "unknown PCS device id %08x\n", id); +- return NULL; ++ return ERR_PTR(-ENODEV); + } + + ret = regmap_read(regmap, SGMSYS_PCS_SCRATCH, &ver); + if (ret < 0) +- return NULL; ++ return ERR_PTR(ret); + + ver = FIELD_GET(SGMII_DEV_VERSION, ver); + if (ver != 0x1) { + dev_err(dev, "unknown PCS device version %04x\n", ver); +- return NULL; ++ return ERR_PTR(-ENODEV); + } + + 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; ++ if (prealloc) { ++ mpcs = prealloc; ++ } else { ++ mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL); ++ if (!mpcs) ++ return ERR_PTR(-ENOMEM); ++ }; + + mpcs->ana_rgc3 = ana_rgc3; + mpcs->regmap = regmap; +@@ -292,6 +350,13 @@ struct phylink_pcs *mtk_pcs_lynxi_create + mpcs->interface = PHY_INTERFACE_MODE_NA; + + return &mpcs->pcs; ++}; ++ ++struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev, ++ struct regmap *regmap, u32 ana_rgc3, ++ u32 flags) ++{ ++ return mtk_pcs_lynxi_init(dev, regmap, ana_rgc3, flags, NULL); + } + EXPORT_SYMBOL(mtk_pcs_lynxi_create); + +@@ -304,4 +369,144 @@ void mtk_pcs_lynxi_destroy(struct phylin + } + EXPORT_SYMBOL(mtk_pcs_lynxi_destroy); + ++static int mtk_pcs_lynxi_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ struct mtk_pcs_lynxi *mpcs; ++ struct phylink_pcs *pcs; ++ struct regmap *regmap; ++ u32 flags = 0; ++ ++ mpcs = devm_kzalloc(dev, sizeof(*mpcs), GFP_KERNEL); ++ if (!mpcs) ++ return -ENOMEM; ++ ++ mpcs->dev = dev; ++ regmap = syscon_node_to_regmap(np->parent); ++ if (IS_ERR(regmap)) ++ return PTR_ERR(regmap); ++ ++ if (of_property_read_bool(np->parent, "mediatek,pnswap")) ++ flags |= MTK_SGMII_FLAG_PN_SWAP; ++ ++ mpcs->rstc = of_reset_control_get_shared(np->parent, NULL); ++ if (IS_ERR(mpcs->rstc)) ++ return PTR_ERR(mpcs->rstc); ++ ++ reset_control_deassert(mpcs->rstc); ++ mpcs->sgmii_sel = devm_clk_get_enabled(dev, "sgmii_sel"); ++ if (IS_ERR(mpcs->sgmii_sel)) ++ return PTR_ERR(mpcs->sgmii_sel); ++ ++ mpcs->sgmii_rx = devm_clk_get(dev, "sgmii_rx"); ++ if (IS_ERR(mpcs->sgmii_rx)) ++ return PTR_ERR(mpcs->sgmii_rx); ++ ++ mpcs->sgmii_tx = devm_clk_get(dev, "sgmii_tx"); ++ if (IS_ERR(mpcs->sgmii_tx)) ++ return PTR_ERR(mpcs->sgmii_tx); ++ ++ pcs = mtk_pcs_lynxi_init(dev, regmap, (uintptr_t)of_device_get_match_data(dev), ++ flags, mpcs); ++ if (IS_ERR(pcs)) ++ return PTR_ERR(pcs); ++ ++ regmap_set_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, SGMII_PHYA_PWD); ++ ++ platform_set_drvdata(pdev, mpcs); ++ ++ mutex_lock(&instance_mutex); ++ list_add_tail(&mpcs->node, &mtk_pcs_lynxi_instances); ++ mutex_unlock(&instance_mutex); ++ ++ return 0; ++} ++ ++static int mtk_pcs_lynxi_remove(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct mtk_pcs_lynxi *cur, *tmp; ++ ++ mutex_lock(&instance_mutex); ++ list_for_each_entry_safe(cur, tmp, &mtk_pcs_lynxi_instances, node) ++ if (cur->dev == dev) { ++ list_del(&cur->node); ++ kfree(cur); ++ break; ++ } ++ mutex_unlock(&instance_mutex); ++ ++ return 0; ++} ++ ++static const struct of_device_id mtk_pcs_lynxi_of_match[] = { ++ { .compatible = "mediatek,mt7988-sgmii", .data = (void *)MTK_NETSYS_V3_AMA_RGC3 }, ++ { /* sentinel */ }, ++}; ++MODULE_DEVICE_TABLE(of, mtk_pcs_lynxi_of_match); ++ ++struct phylink_pcs *mtk_pcs_lynxi_get(struct device *dev, struct device_node *np) ++{ ++ struct platform_device *pdev; ++ struct mtk_pcs_lynxi *mpcs; ++ ++ if (!np) ++ return NULL; ++ ++ if (!of_device_is_available(np)) ++ return ERR_PTR(-ENODEV); ++ ++ if (!of_match_node(mtk_pcs_lynxi_of_match, np)) ++ return ERR_PTR(-EINVAL); ++ ++ pdev = of_find_device_by_node(np); ++ if (!pdev || !platform_get_drvdata(pdev)) { ++ if (pdev) ++ put_device(&pdev->dev); ++ return ERR_PTR(-EPROBE_DEFER); ++ } ++ ++ mpcs = platform_get_drvdata(pdev); ++ device_link_add(dev, mpcs->dev, DL_FLAG_AUTOREMOVE_CONSUMER); ++ ++ return &mpcs->pcs; ++} ++EXPORT_SYMBOL(mtk_pcs_lynxi_get); ++ ++void mtk_pcs_lynxi_put(struct phylink_pcs *pcs) ++{ ++ struct mtk_pcs_lynxi *cur, *mpcs = NULL; ++ ++ if (!pcs) ++ return; ++ ++ mutex_lock(&instance_mutex); ++ list_for_each_entry(cur, &mtk_pcs_lynxi_instances, node) ++ if (pcs == &cur->pcs) { ++ mpcs = cur; ++ break; ++ } ++ mutex_unlock(&instance_mutex); ++ ++ if (WARN_ON(!mpcs)) ++ return; ++ ++ put_device(mpcs->dev); ++} ++EXPORT_SYMBOL(mtk_pcs_lynxi_put); ++ ++static struct platform_driver mtk_pcs_lynxi_driver = { ++ .driver = { ++ .name = "mtk-pcs-lynxi", ++ .suppress_bind_attrs = true, ++ .of_match_table = mtk_pcs_lynxi_of_match, ++ }, ++ .probe = mtk_pcs_lynxi_probe, ++ .remove = mtk_pcs_lynxi_remove, ++}; ++module_platform_driver(mtk_pcs_lynxi_driver); ++ ++MODULE_AUTHOR("Daniel Golle "); ++MODULE_DESCRIPTION("MediaTek LynxI HSGMII PCS"); + MODULE_LICENSE("GPL"); +--- a/include/linux/pcs/pcs-mtk-lynxi.h ++++ b/include/linux/pcs/pcs-mtk-lynxi.h +@@ -10,4 +10,15 @@ struct phylink_pcs *mtk_pcs_lynxi_create + struct regmap *regmap, + u32 ana_rgc3, u32 flags); + void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs); ++ ++#if IS_ENABLED(CONFIG_PCS_MTK_LYNXI) ++struct phylink_pcs *mtk_pcs_lynxi_get(struct device *dev, struct device_node *np); ++void mtk_pcs_lynxi_put(struct phylink_pcs *pcs); ++#else ++static inline struct phylink_pcs *mtk_pcs_lynxi_get(struct device *dev, struct device_node *np) ++{ ++ return NULL; ++} ++static inline void mtk_pcs_lynxi_put(struct phylink_pcs *pcs) { } ++#endif /* IS_ENABLED(CONFIG_PCS_MTK_LYNXI) */ + #endif diff --git a/target/linux/generic/pending-6.6/739-04-dt-bindings-net-pcs-add-bindings-for-MediaTek-USXGMI.patch b/target/linux/generic/pending-6.6/739-04-dt-bindings-net-pcs-add-bindings-for-MediaTek-USXGMI.patch new file mode 100644 index 0000000000..215bd2ca2e --- /dev/null +++ b/target/linux/generic/pending-6.6/739-04-dt-bindings-net-pcs-add-bindings-for-MediaTek-USXGMI.patch @@ -0,0 +1,81 @@ +From 7d88d79c0f65b27a92754d7547f7af098b3de67b Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 12 Dec 2023 03:47:31 +0000 +Subject: [PATCH 4/5] dt-bindings: net: pcs: add bindings for MediaTek USXGMII + PCS + +MediaTek's USXGMII can be found in the MT7988 SoC. We need to access +it in order to configure and monitor the Ethernet SerDes link in +USXGMII, 10GBase-R and 5GBase-R mode. By including a wrapped +legacy 1000Base-X/2500Base-X/Cisco SGMII LynxI PCS as well, those +interface modes are also available. + +Signed-off-by: Daniel Golle +--- + .../bindings/net/pcs/mediatek,usxgmii.yaml | 60 +++++++++++++++++++ + 1 file changed, 60 insertions(+) + create mode 100644 Documentation/devicetree/bindings/net/pcs/mediatek,usxgmii.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/net/pcs/mediatek,usxgmii.yaml +@@ -0,0 +1,60 @@ ++# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/net/pcs/mediatek,usxgmii.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: MediaTek USXGMII PCS ++ ++maintainers: ++ - Daniel Golle ++ ++description: ++ The MediaTek USXGMII PCS provides physical link control and status ++ for USXGMII, 10GBase-R and 5GBase-R links on the SerDes interfaces ++ provided by the PEXTP PHY. ++ In order to also support legacy 2500Base-X, 1000Base-X and Cisco ++ SGMII an existing mediatek,*-sgmiisys LynxI PCS is wrapped to ++ provide those interfaces modes on the same SerDes interfaces shared ++ with the USXGMII PCS. ++ ++properties: ++ $nodename: ++ pattern: "^pcs@[0-9a-f]+$" ++ ++ compatible: ++ const: mediatek,mt7988-usxgmiisys ++ ++ reg: ++ maxItems: 1 ++ ++ clocks: ++ items: ++ - description: USXGMII top-level clock ++ ++ resets: ++ items: ++ - description: XFI reset ++ ++required: ++ - compatible ++ - reg ++ - clocks ++ - resets ++ ++additionalProperties: false ++ ++examples: ++ - | ++ #include ++ #define MT7988_TOPRGU_XFI0_GRST 12 ++ soc { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ usxgmiisys0: pcs@10080000 { ++ compatible = "mediatek,mt7988-usxgmiisys"; ++ reg = <0 0x10080000 0 0x1000>; ++ clocks = <&topckgen CLK_TOP_USXGMII_SBUS_0_SEL>; ++ resets = <&watchdog MT7988_TOPRGU_XFI0_GRST>; ++ }; ++ }; diff --git a/target/linux/generic/pending-6.6/739-05-net-pcs-add-driver-for-MediaTek-USXGMII-PCS.patch b/target/linux/generic/pending-6.6/739-05-net-pcs-add-driver-for-MediaTek-USXGMII-PCS.patch new file mode 100644 index 0000000000..5bd2850fec --- /dev/null +++ b/target/linux/generic/pending-6.6/739-05-net-pcs-add-driver-for-MediaTek-USXGMII-PCS.patch @@ -0,0 +1,547 @@ +From dde0e95fff92e9f5009f3bea75278e0e34a48822 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 12 Dec 2023 03:47:47 +0000 +Subject: [PATCH 5/5] net: pcs: add driver for MediaTek USXGMII PCS + +Add driver for USXGMII PCS found in the MediaTek MT7988 SoC and supporting +USXGMII, 10GBase-R and 5GBase-R interface modes. + +Signed-off-by: Daniel Golle +--- + MAINTAINERS | 2 + + drivers/net/pcs/Kconfig | 11 + + drivers/net/pcs/Makefile | 1 + + drivers/net/pcs/pcs-mtk-usxgmii.c | 456 ++++++++++++++++++++++++++++ + include/linux/pcs/pcs-mtk-usxgmii.h | 27 ++ + 5 files changed, 497 insertions(+) + create mode 100644 drivers/net/pcs/pcs-mtk-usxgmii.c + create mode 100644 include/linux/pcs/pcs-mtk-usxgmii.h + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -12934,7 +12934,9 @@ M: Daniel Golle + L: netdev@vger.kernel.org + S: Maintained + F: drivers/net/pcs/pcs-mtk-lynxi.c ++F: drivers/net/pcs/pcs-mtk-usxgmii.c + F: include/linux/pcs/pcs-mtk-lynxi.h ++F: include/linux/pcs/pcs-mtk-usxgmii.h + + MEDIATEK I2C CONTROLLER DRIVER + M: Qii Wang +--- a/drivers/net/pcs/Kconfig ++++ b/drivers/net/pcs/Kconfig +@@ -18,6 +18,17 @@ config PCS_LYNX + This module provides helpers to phylink for managing the Lynx PCS + which is part of the Layerscape and QorIQ Ethernet SERDES. + ++config PCS_MTK_USXGMII ++ tristate "MediaTek USXGMII PCS" ++ select PCS_MTK_LYNXI ++ select PHY_MTK_PEXTP ++ select PHYLINK ++ help ++ This module provides a driver for MediaTek's USXGMII PCS supporting ++ 10GBase-R, 5GBase-R and USXGMII interface modes. ++ 1000Base-X, 2500Base-X and Cisco SGMII are supported on the same ++ differential pairs via an embedded LynxI PHY. ++ + config PCS_RZN1_MIIC + tristate "Renesas RZ/N1 MII converter" + depends on OF && (ARCH_RZN1 || COMPILE_TEST) +--- a/drivers/net/pcs/Makefile ++++ b/drivers/net/pcs/Makefile +@@ -8,3 +8,4 @@ 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 ++obj-$(CONFIG_PCS_MTK_USXGMII) += pcs-mtk-usxgmii.o +--- /dev/null ++++ b/drivers/net/pcs/pcs-mtk-usxgmii.c +@@ -0,0 +1,456 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2023 MediaTek Inc. ++ * Author: Henry Yen ++ * Daniel Golle ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* USXGMII subsystem config registers */ ++/* Register to control speed */ ++#define RG_PHY_TOP_SPEED_CTRL1 0x80c ++#define USXGMII_RATE_UPDATE_MODE BIT(31) ++#define USXGMII_MAC_CK_GATED BIT(29) ++#define USXGMII_IF_FORCE_EN BIT(28) ++#define USXGMII_RATE_ADAPT_MODE GENMASK(10, 8) ++#define USXGMII_RATE_ADAPT_MODE_X1 0 ++#define USXGMII_RATE_ADAPT_MODE_X2 1 ++#define USXGMII_RATE_ADAPT_MODE_X4 2 ++#define USXGMII_RATE_ADAPT_MODE_X10 3 ++#define USXGMII_RATE_ADAPT_MODE_X100 4 ++#define USXGMII_RATE_ADAPT_MODE_X5 5 ++#define USXGMII_RATE_ADAPT_MODE_X50 6 ++#define USXGMII_XFI_RX_MODE GENMASK(6, 4) ++#define USXGMII_XFI_TX_MODE GENMASK(2, 0) ++#define USXGMII_XFI_MODE_10G 0 ++#define USXGMII_XFI_MODE_5G 1 ++#define USXGMII_XFI_MODE_2P5G 3 ++ ++/* Register to control PCS AN */ ++#define RG_PCS_AN_CTRL0 0x810 ++#define USXGMII_AN_RESTART BIT(31) ++#define USXGMII_AN_SYNC_CNT GENMASK(30, 11) ++#define USXGMII_AN_ENABLE BIT(0) ++ ++#define RG_PCS_AN_CTRL2 0x818 ++#define USXGMII_LINK_TIMER_IDLE_DETECT GENMASK(29, 20) ++#define USXGMII_LINK_TIMER_COMP_ACK_DETECT GENMASK(19, 10) ++#define USXGMII_LINK_TIMER_AN_RESTART GENMASK(9, 0) ++ ++/* Register to read PCS AN status */ ++#define RG_PCS_AN_STS0 0x81c ++#define USXGMII_LPA GENMASK(15, 0) ++#define USXGMII_LPA_LATCH BIT(31) ++ ++/* Register to read PCS link status */ ++#define RG_PCS_RX_STATUS0 0x904 ++#define RG_PCS_RX_STATUS_UPDATE BIT(16) ++#define RG_PCS_RX_LINK_STATUS BIT(2) ++ ++/* struct mtk_usxgmii_pcs - This structure holds each usxgmii PCS ++ * @pcs: Phylink PCS structure ++ * @dev: Pointer to device structure ++ * @base: IO memory to access PCS hardware ++ * @clk: Pointer to USXGMII clk ++ * @reset: Pointer to USXGMII reset control ++ * @interface: Currently selected interface mode ++ * @neg_mode: Currently used phylink neg_mode ++ * @node: List node ++ */ ++struct mtk_usxgmii_pcs { ++ struct phylink_pcs pcs; ++ struct device *dev; ++ void __iomem *base; ++ struct clk *clk; ++ struct reset_control *reset; ++ phy_interface_t interface; ++ unsigned int neg_mode; ++ struct list_head node; ++}; ++ ++static LIST_HEAD(mtk_usxgmii_pcs_instances); ++static DEFINE_MUTEX(instance_mutex); ++ ++static u32 mtk_r32(struct mtk_usxgmii_pcs *mpcs, unsigned int reg) ++{ ++ return ioread32(mpcs->base + reg); ++} ++ ++static void mtk_m32(struct mtk_usxgmii_pcs *mpcs, unsigned int reg, u32 mask, u32 set) ++{ ++ u32 val; ++ ++ val = ioread32(mpcs->base + reg); ++ val &= ~mask; ++ val |= set; ++ iowrite32(val, mpcs->base + reg); ++} ++ ++static struct mtk_usxgmii_pcs *pcs_to_mtk_usxgmii_pcs(struct phylink_pcs *pcs) ++{ ++ return container_of(pcs, struct mtk_usxgmii_pcs, pcs); ++} ++ ++static void mtk_usxgmii_reset(struct mtk_usxgmii_pcs *mpcs) ++{ ++ reset_control_assert(mpcs->reset); ++ udelay(100); ++ reset_control_deassert(mpcs->reset); ++ ++ mdelay(10); ++} ++ ++static int mtk_usxgmii_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, ++ phy_interface_t interface, ++ const unsigned long *advertising, ++ bool permit_pause_to_mac) ++{ ++ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs); ++ unsigned int an_ctrl = 0, link_timer = 0, xfi_mode = 0, adapt_mode = 0; ++ bool mode_changed = false; ++ ++ if (interface == PHY_INTERFACE_MODE_USXGMII) { ++ an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0x1FF) | USXGMII_AN_ENABLE; ++ link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x7B) | ++ FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x7B) | ++ FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x7B); ++ xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_MODE_10G) | ++ FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_MODE_10G); ++ } else if (interface == PHY_INTERFACE_MODE_10GBASER) { ++ an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0x1FF); ++ link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x7B) | ++ FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x7B) | ++ FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x7B); ++ xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_MODE_10G) | ++ FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_MODE_10G); ++ adapt_mode = USXGMII_RATE_UPDATE_MODE; ++ } else if (interface == PHY_INTERFACE_MODE_5GBASER) { ++ an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0xFF); ++ link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x3D) | ++ FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x3D) | ++ FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x3D); ++ xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_MODE_5G) | ++ FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_MODE_5G); ++ adapt_mode = USXGMII_RATE_UPDATE_MODE; ++ } else { ++ return -EINVAL; ++ } ++ ++ adapt_mode |= FIELD_PREP(USXGMII_RATE_ADAPT_MODE, USXGMII_RATE_ADAPT_MODE_X1); ++ ++ if (mpcs->interface != interface) { ++ mpcs->interface = interface; ++ mode_changed = true; ++ } ++ ++ mtk_usxgmii_reset(mpcs); ++ ++ /* Setup USXGMII AN ctrl */ ++ mtk_m32(mpcs, RG_PCS_AN_CTRL0, ++ USXGMII_AN_SYNC_CNT | USXGMII_AN_ENABLE, ++ an_ctrl); ++ ++ mtk_m32(mpcs, RG_PCS_AN_CTRL2, ++ USXGMII_LINK_TIMER_IDLE_DETECT | ++ USXGMII_LINK_TIMER_COMP_ACK_DETECT | ++ USXGMII_LINK_TIMER_AN_RESTART, ++ link_timer); ++ ++ mpcs->neg_mode = neg_mode; ++ ++ /* Gated MAC CK */ ++ mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_MAC_CK_GATED, USXGMII_MAC_CK_GATED); ++ ++ /* Enable interface force mode */ ++ mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_IF_FORCE_EN, USXGMII_IF_FORCE_EN); ++ ++ /* Setup USXGMII adapt mode */ ++ mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_RATE_UPDATE_MODE | USXGMII_RATE_ADAPT_MODE, ++ adapt_mode); ++ ++ /* Setup USXGMII speed */ ++ mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_XFI_RX_MODE | USXGMII_XFI_TX_MODE, ++ xfi_mode); ++ ++ usleep_range(1, 10); ++ ++ /* Un-gated MAC CK */ ++ mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1, USXGMII_MAC_CK_GATED, 0); ++ ++ usleep_range(1, 10); ++ ++ /* Disable interface force mode for the AN mode */ ++ if (an_ctrl & USXGMII_AN_ENABLE) ++ mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1, USXGMII_IF_FORCE_EN, 0); ++ ++ return mode_changed; ++} ++ ++static void mtk_usxgmii_pcs_get_fixed_speed(struct mtk_usxgmii_pcs *mpcs, ++ struct phylink_link_state *state) ++{ ++ u32 val = mtk_r32(mpcs, RG_PHY_TOP_SPEED_CTRL1); ++ int speed; ++ ++ /* Calculate speed from interface speed and rate adapt mode */ ++ switch (FIELD_GET(USXGMII_XFI_RX_MODE, val)) { ++ case USXGMII_XFI_MODE_10G: ++ speed = 10000; ++ break; ++ case USXGMII_XFI_MODE_5G: ++ speed = 5000; ++ break; ++ case USXGMII_XFI_MODE_2P5G: ++ speed = 2500; ++ break; ++ default: ++ state->speed = SPEED_UNKNOWN; ++ return; ++ } ++ ++ switch (FIELD_GET(USXGMII_RATE_ADAPT_MODE, val)) { ++ case USXGMII_RATE_ADAPT_MODE_X100: ++ speed /= 100; ++ break; ++ case USXGMII_RATE_ADAPT_MODE_X50: ++ speed /= 50; ++ break; ++ case USXGMII_RATE_ADAPT_MODE_X10: ++ speed /= 10; ++ break; ++ case USXGMII_RATE_ADAPT_MODE_X5: ++ speed /= 5; ++ break; ++ case USXGMII_RATE_ADAPT_MODE_X4: ++ speed /= 4; ++ break; ++ case USXGMII_RATE_ADAPT_MODE_X2: ++ speed /= 2; ++ break; ++ case USXGMII_RATE_ADAPT_MODE_X1: ++ break; ++ default: ++ state->speed = SPEED_UNKNOWN; ++ return; ++ } ++ ++ state->speed = speed; ++ state->duplex = DUPLEX_FULL; ++} ++ ++static void mtk_usxgmii_pcs_get_an_state(struct mtk_usxgmii_pcs *mpcs, ++ struct phylink_link_state *state) ++{ ++ u16 lpa; ++ ++ /* Refresh LPA by toggling LPA_LATCH */ ++ mtk_m32(mpcs, RG_PCS_AN_STS0, USXGMII_LPA_LATCH, USXGMII_LPA_LATCH); ++ ndelay(1020); ++ mtk_m32(mpcs, RG_PCS_AN_STS0, USXGMII_LPA_LATCH, 0); ++ ndelay(1020); ++ lpa = FIELD_GET(USXGMII_LPA, mtk_r32(mpcs, RG_PCS_AN_STS0)); ++ ++ phylink_decode_usxgmii_word(state, lpa); ++} ++ ++static void mtk_usxgmii_pcs_get_state(struct phylink_pcs *pcs, ++ struct phylink_link_state *state) ++{ ++ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs); ++ ++ /* Refresh USXGMII link status by toggling RG_PCS_AN_STATUS_UPDATE */ ++ mtk_m32(mpcs, RG_PCS_RX_STATUS0, RG_PCS_RX_STATUS_UPDATE, ++ RG_PCS_RX_STATUS_UPDATE); ++ ndelay(1020); ++ mtk_m32(mpcs, RG_PCS_RX_STATUS0, RG_PCS_RX_STATUS_UPDATE, 0); ++ ndelay(1020); ++ ++ /* Read USXGMII link status */ ++ state->link = FIELD_GET(RG_PCS_RX_LINK_STATUS, ++ mtk_r32(mpcs, RG_PCS_RX_STATUS0)); ++ ++ /* Continuously repeat re-configuration sequence until link comes up */ ++ if (!state->link) { ++ mtk_usxgmii_pcs_config(pcs, mpcs->neg_mode, ++ state->interface, NULL, false); ++ return; ++ } ++ ++ if (FIELD_GET(USXGMII_AN_ENABLE, mtk_r32(mpcs, RG_PCS_AN_CTRL0))) ++ mtk_usxgmii_pcs_get_an_state(mpcs, state); ++ else ++ mtk_usxgmii_pcs_get_fixed_speed(mpcs, state); ++} ++ ++static void mtk_usxgmii_pcs_restart_an(struct phylink_pcs *pcs) ++{ ++ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs); ++ ++ mtk_m32(mpcs, RG_PCS_AN_CTRL0, USXGMII_AN_RESTART, USXGMII_AN_RESTART); ++} ++ ++static void mtk_usxgmii_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode, ++ phy_interface_t interface, ++ int speed, int duplex) ++{ ++ /* Reconfiguring USXGMII to ensure the quality of the RX signal ++ * after the line side link up. ++ */ ++ mtk_usxgmii_pcs_config(pcs, neg_mode, interface, NULL, false); ++} ++ ++static void mtk_usxgmii_pcs_disable(struct phylink_pcs *pcs) ++{ ++ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs); ++ ++ mpcs->interface = PHY_INTERFACE_MODE_NA; ++ mpcs->neg_mode = -1; ++} ++ ++static const struct phylink_pcs_ops mtk_usxgmii_pcs_ops = { ++ .pcs_config = mtk_usxgmii_pcs_config, ++ .pcs_get_state = mtk_usxgmii_pcs_get_state, ++ .pcs_an_restart = mtk_usxgmii_pcs_restart_an, ++ .pcs_link_up = mtk_usxgmii_pcs_link_up, ++ .pcs_disable = mtk_usxgmii_pcs_disable, ++}; ++ ++static int mtk_usxgmii_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct mtk_usxgmii_pcs *mpcs; ++ ++ mpcs = devm_kzalloc(dev, sizeof(*mpcs), GFP_KERNEL); ++ if (!mpcs) ++ return -ENOMEM; ++ ++ mpcs->base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(mpcs->base)) ++ return PTR_ERR(mpcs->base); ++ ++ mpcs->dev = dev; ++ mpcs->pcs.ops = &mtk_usxgmii_pcs_ops; ++ mpcs->pcs.poll = true; ++ mpcs->pcs.neg_mode = true; ++ mpcs->interface = PHY_INTERFACE_MODE_NA; ++ mpcs->neg_mode = -1; ++ ++ mpcs->clk = devm_clk_get_enabled(mpcs->dev, NULL); ++ if (IS_ERR(mpcs->clk)) ++ return PTR_ERR(mpcs->clk); ++ ++ mpcs->reset = devm_reset_control_get_shared(dev, NULL); ++ if (IS_ERR(mpcs->reset)) ++ return PTR_ERR(mpcs->reset); ++ ++ reset_control_deassert(mpcs->reset); ++ ++ platform_set_drvdata(pdev, mpcs); ++ ++ mutex_lock(&instance_mutex); ++ list_add_tail(&mpcs->node, &mtk_usxgmii_pcs_instances); ++ mutex_unlock(&instance_mutex); ++ ++ return 0; ++} ++ ++static int mtk_usxgmii_remove(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct mtk_usxgmii_pcs *cur, *tmp; ++ ++ mutex_lock(&instance_mutex); ++ list_for_each_entry_safe(cur, tmp, &mtk_usxgmii_pcs_instances, node) ++ if (cur->dev == dev) { ++ list_del(&cur->node); ++ break; ++ } ++ mutex_unlock(&instance_mutex); ++ ++ return 0; ++} ++ ++static const struct of_device_id mtk_usxgmii_of_mtable[] = { ++ { .compatible = "mediatek,mt7988-usxgmiisys" }, ++ { /* sentinel */ }, ++}; ++MODULE_DEVICE_TABLE(of, mtk_usxgmii_of_mtable); ++ ++struct phylink_pcs *mtk_usxgmii_pcs_get(struct device *dev, struct device_node *np) ++{ ++ struct platform_device *pdev; ++ struct mtk_usxgmii_pcs *mpcs; ++ ++ if (!np) ++ return NULL; ++ ++ if (!of_device_is_available(np)) ++ return ERR_PTR(-ENODEV); ++ ++ if (!of_match_node(mtk_usxgmii_of_mtable, np)) ++ return ERR_PTR(-EINVAL); ++ ++ pdev = of_find_device_by_node(np); ++ if (!pdev || !platform_get_drvdata(pdev)) { ++ if (pdev) ++ put_device(&pdev->dev); ++ return ERR_PTR(-EPROBE_DEFER); ++ } ++ ++ mpcs = platform_get_drvdata(pdev); ++ device_link_add(dev, mpcs->dev, DL_FLAG_AUTOREMOVE_CONSUMER); ++ ++ return &mpcs->pcs; ++} ++EXPORT_SYMBOL(mtk_usxgmii_pcs_get); ++ ++void mtk_usxgmii_pcs_put(struct phylink_pcs *pcs) ++{ ++ struct mtk_usxgmii_pcs *cur, *mpcs = NULL; ++ ++ if (!pcs) ++ return; ++ ++ mutex_lock(&instance_mutex); ++ list_for_each_entry(cur, &mtk_usxgmii_pcs_instances, node) ++ if (pcs == &cur->pcs) { ++ mpcs = cur; ++ break; ++ } ++ mutex_unlock(&instance_mutex); ++ ++ if (WARN_ON(!mpcs)) ++ return; ++ ++ put_device(mpcs->dev); ++} ++EXPORT_SYMBOL(mtk_usxgmii_pcs_put); ++ ++static struct platform_driver mtk_usxgmii_driver = { ++ .driver = { ++ .name = "mtk_usxgmii", ++ .suppress_bind_attrs = true, ++ .of_match_table = mtk_usxgmii_of_mtable, ++ }, ++ .probe = mtk_usxgmii_probe, ++ .remove = mtk_usxgmii_remove, ++}; ++module_platform_driver(mtk_usxgmii_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("MediaTek USXGMII PCS driver"); ++MODULE_AUTHOR("Daniel Golle "); +--- /dev/null ++++ b/include/linux/pcs/pcs-mtk-usxgmii.h +@@ -0,0 +1,27 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++#ifndef __LINUX_PCS_MTK_USXGMII_H ++#define __LINUX_PCS_MTK_USXGMII_H ++ ++#include ++ ++/** ++ * mtk_usxgmii_select_pcs() - Get MediaTek PCS instance ++ * @np: Pointer to device node indentifying a MediaTek USXGMII PCS ++ * @mode: Ethernet PHY interface mode ++ * ++ * Return PCS identified by a device node and the PHY interface mode in use ++ * ++ * Return: Pointer to phylink PCS instance of NULL ++ */ ++#if IS_ENABLED(CONFIG_PCS_MTK_USXGMII) ++struct phylink_pcs *mtk_usxgmii_pcs_get(struct device *dev, struct device_node *np); ++void mtk_usxgmii_pcs_put(struct phylink_pcs *pcs); ++#else ++static inline struct phylink_pcs *mtk_usxgmii_pcs_get(struct device *dev, struct device_node *np) ++{ ++ return NULL; ++} ++static inline void mtk_usxgmii_pcs_put(struct phylink_pcs *pcs) { } ++#endif /* IS_ENABLED(CONFIG_PCS_MTK_USXGMII) */ ++ ++#endif /* __LINUX_PCS_MTK_USXGMII_H */ diff --git a/target/linux/generic/pending-6.6/740-net-phy-motorcomm-Add-missing-include.patch b/target/linux/generic/pending-6.6/740-net-phy-motorcomm-Add-missing-include.patch new file mode 100644 index 0000000000..2a1f908cfb --- /dev/null +++ b/target/linux/generic/pending-6.6/740-net-phy-motorcomm-Add-missing-include.patch @@ -0,0 +1,22 @@ +From 6f291aa7da199c6486cc229b055dcbcd5cee7a21 Mon Sep 17 00:00:00 2001 +From: Hauke Mehrtens +Date: Sun, 21 May 2023 22:24:56 +0200 +Subject: [PATCH] net: phy: motorcomm: Add missing include + +Directly include linux/bitfield.h which provides FIELD_PREP. + +Signed-off-by: Hauke Mehrtens +--- + drivers/net/phy/motorcomm.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/phy/motorcomm.c ++++ b/drivers/net/phy/motorcomm.c +@@ -6,6 +6,7 @@ + * Author: Frank + */ + ++#include + #include + #include + #include diff --git a/target/linux/generic/pending-6.6/741-net-phy-realtek-support-interrupt-of-RTL8221B.patch b/target/linux/generic/pending-6.6/741-net-phy-realtek-support-interrupt-of-RTL8221B.patch new file mode 100644 index 0000000000..51cd98c883 --- /dev/null +++ b/target/linux/generic/pending-6.6/741-net-phy-realtek-support-interrupt-of-RTL8221B.patch @@ -0,0 +1,63 @@ +--- a/drivers/net/phy/realtek.c ++++ b/drivers/net/phy/realtek.c +@@ -981,6 +981,51 @@ static int rtl8221b_config_init(struct p + return 0; + } + ++static int rtl8221b_ack_interrupt(struct phy_device *phydev) ++{ ++ int err; ++ ++ err = phy_read_mmd(phydev, RTL8221B_MMD_PHY_CTRL, 0xa4d4); ++ ++ return (err < 0) ? err : 0; ++} ++ ++static int rtl8221b_config_intr(struct phy_device *phydev) ++{ ++ int err; ++ ++ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { ++ err = rtl8221b_ack_interrupt(phydev); ++ if (err) ++ return err; ++ ++ err = phy_write_mmd(phydev, RTL8221B_MMD_PHY_CTRL, 0xa4d2, 0x7ff); ++ } else { ++ err = phy_write_mmd(phydev, RTL8221B_MMD_PHY_CTRL, 0xa4d2, 0x0); ++ if (err) ++ return err; ++ ++ err = rtl8221b_ack_interrupt(phydev); ++ } ++ ++ return err; ++} ++ ++static irqreturn_t rtl8221b_handle_interrupt(struct phy_device *phydev) ++{ ++ int err; ++ ++ err = rtl8221b_ack_interrupt(phydev); ++ if (err) { ++ phy_error(phydev); ++ return IRQ_NONE; ++ } ++ ++ phy_trigger_machine(phydev); ++ ++ return IRQ_HANDLED; ++} ++ + static struct phy_driver realtek_drvs[] = { + { + PHY_ID_MATCH_EXACT(0x00008201), +@@ -1141,6 +1186,8 @@ static struct phy_driver realtek_drvs[] + .get_features = rtl822x_get_features, + .config_init = rtl8221b_config_init, + .config_aneg = rtl822x_config_aneg, ++ .config_intr = rtl8221b_config_intr, ++ .handle_interrupt = rtl8221b_handle_interrupt, + .probe = rtl822x_probe, + .read_status = rtl822x_read_status, + .suspend = genphy_suspend, diff --git a/target/linux/generic/pending-6.6/750-net-phy-airoha-Add-the-Airoha-EN8811H-PHY-driver.patch b/target/linux/generic/pending-6.6/750-net-phy-airoha-Add-the-Airoha-EN8811H-PHY-driver.patch new file mode 100644 index 0000000000..1dfa1366eb --- /dev/null +++ b/target/linux/generic/pending-6.6/750-net-phy-airoha-Add-the-Airoha-EN8811H-PHY-driver.patch @@ -0,0 +1,1115 @@ +From patchwork Tue Feb 6 19:47:51 2024 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Eric Woudstra +X-Patchwork-Id: 13547762 +X-Patchwork-Delegate: kuba@kernel.org +From: Eric Woudstra +To: "David S. Miller" , + Eric Dumazet , + Jakub Kicinski , + Paolo Abeni , + Rob Herring , + Krzysztof Kozlowski , + Conor Dooley , + Andrew Lunn , + Heiner Kallweit , + Russell King , + Matthias Brugger , + AngeloGioacchino Del Regno , + "Frank Wunderlich" , + Daniel Golle , + Lucien Jheng , + Zhi-Jun You +Cc: netdev@vger.kernel.org, + devicetree@vger.kernel.org, + Eric Woudstra +Subject: [PATCH net-next 2/2] net: phy: air_en8811h: Add the Airoha EN8811H + PHY driver +Date: Tue, 6 Feb 2024 20:47:51 +0100 +Message-ID: <20240206194751.1901802-3-ericwouds@gmail.com> +X-Mailer: git-send-email 2.42.1 +In-Reply-To: <20240206194751.1901802-1-ericwouds@gmail.com> +References: <20240206194751.1901802-1-ericwouds@gmail.com> +Precedence: bulk +X-Mailing-List: netdev@vger.kernel.org +List-Id: +List-Subscribe: +List-Unsubscribe: +MIME-Version: 1.0 +X-Patchwork-Delegate: kuba@kernel.org + +* Source originated from airoha's en8811h v1.2.1 driver + * Moved air_en8811h.h to air_en8811h.c + * Removed air_pbus_reg_write() as it writes to another device on mdio-bus + * Load firmware from /lib/firmware/airoha/ instead of /lib/firmware/ + * Added .get_rate_matching() + * Use generic phy_read/write() and phy_read/write_mmd() + * Edited .get_features() to use generic C45 functions + * Edited .config_aneg() and .read_status() to use a mix of generic C22/C45 + * Use led handling functions from mediatek-ge-soc.c + * Simplified led handling by storing led rules + * Cleanup macro definitions + * Cleanup code to pass checkpatch.pl + * General code cleanup + +Changes from original RFC patch: + + * Use the correct order in Kconfig and Makefile + * Change some register naming to correspond with datasheet + * Use phy_driver .read_page() and .write_page() + * Use module_phy_driver() + * Use get_unaligned_le16() instead of macro + * In .config_aneg() and .read_status() use genphy_xxx() C22 + * Use another vendor register to read real speed + * Load firmware only once and store firmware version + * Apply 2.5G LPA work-around (firmware before 24011202) + * Read 2.5G LPA from vendor register (firmware 24011202 and later) + +Changes to be committed: + modified: drivers/net/phy/Kconfig + modified: drivers/net/phy/Makefile + new file: drivers/net/phy/air_en8811h.c + +Signed-off-by: Eric Woudstra +--- + drivers/net/phy/Kconfig | 5 + + drivers/net/phy/Makefile | 1 + + drivers/net/phy/air_en8811h.c | 1006 +++++++++++++++++++++++++++++++++ + 3 files changed, 1012 insertions(+) + create mode 100644 drivers/net/phy/air_en8811h.c + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -69,6 +69,11 @@ config SFP + + comment "MII PHY device drivers" + ++config AIR_EN8811H_PHY ++ tristate "Airoha EN8811H 2.5 Gigabit PHY" ++ help ++ Currently supports the Airoha EN8811H PHY. ++ + config AMD_PHY + tristate "AMD and Altima PHYs" + help +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -32,6 +32,7 @@ obj-y += $(sfp-obj-y) $(sfp-obj-m) + + obj-$(CONFIG_ADIN_PHY) += adin.o + obj-$(CONFIG_ADIN1100_PHY) += adin1100.o ++obj-$(CONFIG_AIR_EN8811H_PHY) += air_en8811h.o + obj-$(CONFIG_AMD_PHY) += amd.o + obj-$(CONFIG_AQUANTIA_PHY) += aquantia/ + obj-$(CONFIG_AX88796B_PHY) += ax88796b.o +--- /dev/null ++++ b/drivers/net/phy/air_en8811h.c +@@ -0,0 +1,1006 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Driver for Airoha Ethernet PHYs ++ * ++ * Currently supporting the EN8811H. ++ * ++ * Limitations of the EN8811H: ++ * - Only full duplex supported ++ * - Forced speed (AN off) is not supported by hardware (100Mbps) ++ * ++ * Source originated from airoha's en8811h.c and en8811h.h v1.2.1 ++ * ++ * Copyright (C) 2023 Airoha Technology Corp. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#define EN8811H_PHY_ID 0x03a2a411 ++ ++#define EN8811H_MD32_DM "airoha/EthMD32.dm.bin" ++#define EN8811H_MD32_DSP "airoha/EthMD32.DSP.bin" ++ ++#define AIR_FW_ADDR_DM 0x00000000 ++#define AIR_FW_ADDR_DSP 0x00100000 ++ ++/* u32 (DWORD) component macros */ ++#define LOWORD(d) ((u16)(u32)(d)) ++#define HIWORD(d) ((u16)(((u32)(d)) >> 16)) ++ ++/* MII Registers */ ++#define AIR_AUX_CTRL_STATUS 0x1d ++#define AIR_AUX_CTRL_STATUS_SPEED_MASK GENMASK(4, 2) ++#define AIR_AUX_CTRL_STATUS_SPEED_100 0x4 ++#define AIR_AUX_CTRL_STATUS_SPEED_1000 0x8 ++#define AIR_AUX_CTRL_STATUS_SPEED_2500 0xc ++ ++#define AIR_EXT_PAGE_ACCESS 0x1f ++#define AIR_PHY_PAGE_STANDARD 0x0000 ++#define AIR_PHY_PAGE_EXTENDED_4 0x0004 ++ ++/* MII Registers Page 4*/ ++#define AIR_PBUS_MODE 0x10 ++#define AIR_PBUS_MODE_ADDR_FIXED 0x0000 ++#define AIR_PBUS_MODE_ADDR_INCR BIT(15) ++#define AIR_PBUS_WR_ADDR_HIGH 0x11 ++#define AIR_PBUS_WR_ADDR_LOW 0x12 ++#define AIR_PBUS_WR_DATA_HIGH 0x13 ++#define AIR_PBUS_WR_DATA_LOW 0x14 ++#define AIR_PBUS_RD_ADDR_HIGH 0x15 ++#define AIR_PBUS_RD_ADDR_LOW 0x16 ++#define AIR_PBUS_RD_DATA_HIGH 0x17 ++#define AIR_PBUS_RD_DATA_LOW 0x18 ++ ++/* Registers on MDIO_MMD_VEND1 */ ++#define EN8811H_PHY_FW_STATUS 0x8009 ++#define EN8811H_PHY_READY 0x02 ++ ++#define AIR_PHY_HOST_CMD_1 0x800c ++#define AIR_PHY_HOST_CMD_1_MODE1 0x0 ++#define AIR_PHY_HOST_CMD_2 0x800d ++#define AIR_PHY_HOST_CMD_2_MODE1 0x0 ++#define AIR_PHY_HOST_CMD_3 0x800e ++#define AIR_PHY_HOST_CMD_3_MODE1 0x1101 ++#define AIR_PHY_HOST_CMD_3_DOCMD 0x1100 ++#define AIR_PHY_HOST_CMD_4 0x800f ++#define AIR_PHY_HOST_CMD_4_MODE1 0x0002 ++#define AIR_PHY_HOST_CMD_4_INTCLR 0x00e4 ++ ++/* Registers on MDIO_MMD_VEND2 */ ++#define AIR_PHY_LED_BCR 0x021 ++#define AIR_PHY_LED_BCR_MODE_MASK GENMASK(1, 0) ++#define AIR_PHY_LED_BCR_TIME_TEST BIT(2) ++#define AIR_PHY_LED_BCR_CLK_EN BIT(3) ++#define AIR_PHY_LED_BCR_EXT_CTRL BIT(15) ++ ++#define AIR_PHY_LED_DUR_ON 0x022 ++ ++#define AIR_PHY_LED_DUR_BLINK 0x023 ++ ++#define AIR_PHY_LED_ON(i) (0x024 + ((i) * 2)) ++#define AIR_PHY_LED_ON_MASK (GENMASK(6, 0) | BIT(8)) ++#define AIR_PHY_LED_ON_LINK1000 BIT(0) ++#define AIR_PHY_LED_ON_LINK100 BIT(1) ++#define AIR_PHY_LED_ON_LINK10 BIT(2) ++#define AIR_PHY_LED_ON_LINKDOWN BIT(3) ++#define AIR_PHY_LED_ON_FDX BIT(4) /* Full duplex */ ++#define AIR_PHY_LED_ON_HDX BIT(5) /* Half duplex */ ++#define AIR_PHY_LED_ON_FORCE_ON BIT(6) ++#define AIR_PHY_LED_ON_LINK2500 BIT(8) ++#define AIR_PHY_LED_ON_POLARITY BIT(14) ++#define AIR_PHY_LED_ON_ENABLE BIT(15) ++ ++#define AIR_PHY_LED_BLINK(i) (0x025 + ((i) * 2)) ++#define AIR_PHY_LED_BLINK_1000TX BIT(0) ++#define AIR_PHY_LED_BLINK_1000RX BIT(1) ++#define AIR_PHY_LED_BLINK_100TX BIT(2) ++#define AIR_PHY_LED_BLINK_100RX BIT(3) ++#define AIR_PHY_LED_BLINK_10TX BIT(4) ++#define AIR_PHY_LED_BLINK_10RX BIT(5) ++#define AIR_PHY_LED_BLINK_COLLISION BIT(6) ++#define AIR_PHY_LED_BLINK_RX_CRC_ERR BIT(7) ++#define AIR_PHY_LED_BLINK_RX_IDLE_ERR BIT(8) ++#define AIR_PHY_LED_BLINK_FORCE_BLINK BIT(9) ++#define AIR_PHY_LED_BLINK_2500TX BIT(10) ++#define AIR_PHY_LED_BLINK_2500RX BIT(11) ++ ++/* Registers on BUCKPBUS */ ++#define EN8811H_2P5G_LPA 0x3b30 ++#define EN8811H_2P5G_LPA_2P5G BIT(0) ++ ++#define EN8811H_FW_VERSION 0x3b3c ++ ++#define EN8811H_POLARITY 0xca0f8 ++#define EN8811H_POLARITY_TX_NORMAL BIT(0) ++#define EN8811H_POLARITY_RX_REVERSE BIT(1) ++ ++#define EN8811H_GPIO_OUTPUT 0xcf8b8 ++#define EN8811H_GPIO_OUTPUT_345 (BIT(3) | BIT(4) | BIT(5)) ++ ++#define EN8811H_FW_CTRL_1 0x0f0018 ++#define EN8811H_FW_CTRL_1_START 0x0 ++#define EN8811H_FW_CTRL_1_FINISH 0x1 ++#define EN8811H_FW_CTRL_2 0x800000 ++#define EN8811H_FW_CTRL_2_LOADING BIT(11) ++ ++#define EN8811H_LED_COUNT 3 ++ ++/* GPIO5 <-> BASE_T_LED0 ++ * GPIO4 <-> BASE_T_LED1 ++ * GPIO3 <-> BASE_T_LED2 ++ * ++ * Default setup suitable for 2 leds connected: ++ * 100M link up triggers led0, only led0 blinking on traffic ++ * 1000M link up triggers led1, only led1 blinking on traffic ++ * 2500M link up triggers led0 and led1, both blinking on traffic ++ * Also suitable for 1 led connected: ++ * any link up triggers led2 ++ */ ++#define AIR_DEFAULT_TRIGGER_LED0 (BIT(TRIGGER_NETDEV_LINK_2500) | \ ++ BIT(TRIGGER_NETDEV_LINK_100) | \ ++ BIT(TRIGGER_NETDEV_RX) | \ ++ BIT(TRIGGER_NETDEV_TX)) ++#define AIR_DEFAULT_TRIGGER_LED1 (BIT(TRIGGER_NETDEV_LINK_2500) | \ ++ BIT(TRIGGER_NETDEV_LINK_1000) | \ ++ BIT(TRIGGER_NETDEV_RX) | \ ++ BIT(TRIGGER_NETDEV_TX)) ++#define AIR_DEFAULT_TRIGGER_LED2 BIT(TRIGGER_NETDEV_LINK) ++ ++struct led { ++ unsigned long rules; ++ unsigned long state; ++}; ++ ++struct en8811h_priv { ++ u32 firmware_version; ++ struct led led[EN8811H_LED_COUNT]; ++}; ++ ++enum { ++ AIR_PHY_LED_STATE_FORCE_ON, ++ AIR_PHY_LED_STATE_FORCE_BLINK, ++}; ++ ++enum { ++ AIR_PHY_LED_DUR_BLINK_32M, ++ AIR_PHY_LED_DUR_BLINK_64M, ++ AIR_PHY_LED_DUR_BLINK_128M, ++ AIR_PHY_LED_DUR_BLINK_256M, ++ AIR_PHY_LED_DUR_BLINK_512M, ++ AIR_PHY_LED_DUR_BLINK_1024M, ++}; ++ ++enum { ++ AIR_LED_DISABLE, ++ AIR_LED_ENABLE, ++}; ++ ++enum { ++ AIR_ACTIVE_LOW, ++ AIR_ACTIVE_HIGH, ++}; ++ ++enum { ++ AIR_LED_MODE_DISABLE, ++ AIR_LED_MODE_USER_DEFINE, ++}; ++ ++#define AIR_PHY_LED_DUR_UNIT 1024 ++#define AIR_PHY_LED_DUR (AIR_PHY_LED_DUR_UNIT << AIR_PHY_LED_DUR_BLINK_64M) ++ ++static const unsigned long en8811h_led_trig = (BIT(TRIGGER_NETDEV_FULL_DUPLEX) | ++ BIT(TRIGGER_NETDEV_LINK) | ++ BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_RX) | ++ BIT(TRIGGER_NETDEV_TX)); ++ ++static int air_phy_read_page(struct phy_device *phydev) ++{ ++ return __phy_read(phydev, AIR_EXT_PAGE_ACCESS); ++} ++ ++static int air_phy_write_page(struct phy_device *phydev, int page) ++{ ++ return __phy_write(phydev, AIR_EXT_PAGE_ACCESS, page); ++} ++ ++static int __air_buckpbus_reg_write(struct phy_device *phydev, ++ u32 pbus_address, u32 pbus_data) ++{ ++ int ret; ++ ++ ret = __phy_write(phydev, AIR_PBUS_MODE, AIR_PBUS_MODE_ADDR_FIXED); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_PBUS_WR_ADDR_HIGH, HIWORD(pbus_address)); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_PBUS_WR_ADDR_LOW, LOWORD(pbus_address)); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_PBUS_WR_DATA_HIGH, HIWORD(pbus_data)); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_PBUS_WR_DATA_LOW, LOWORD(pbus_data)); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int air_buckpbus_reg_write(struct phy_device *phydev, ++ u32 pbus_address, u32 pbus_data) ++{ ++ int ret, saved_page; ++ ++ saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); ++ ++ ret = __air_buckpbus_reg_write(phydev, pbus_address, pbus_data); ++ if (ret < 0) ++ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, ++ pbus_address, ret); ++ ++ return phy_restore_page(phydev, saved_page, ret); ++; ++} ++ ++static int __air_buckpbus_reg_read(struct phy_device *phydev, ++ u32 pbus_address, u32 *pbus_data) ++{ ++ int pbus_data_low, pbus_data_high; ++ int ret; ++ ++ ret = __phy_write(phydev, AIR_PBUS_MODE, AIR_PBUS_MODE_ADDR_FIXED); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_PBUS_RD_ADDR_HIGH, HIWORD(pbus_address)); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_PBUS_RD_ADDR_LOW, LOWORD(pbus_address)); ++ if (ret < 0) ++ return ret; ++ ++ pbus_data_high = __phy_read(phydev, AIR_PBUS_RD_DATA_HIGH); ++ if (pbus_data_high < 0) ++ return ret; ++ ++ pbus_data_low = __phy_read(phydev, AIR_PBUS_RD_DATA_LOW); ++ if (pbus_data_low < 0) ++ return ret; ++ ++ *pbus_data = (u16)pbus_data_low | ((u32)(u16)pbus_data_high << 16); ++ return 0; ++} ++ ++static int air_buckpbus_reg_read(struct phy_device *phydev, ++ u32 pbus_address, u32 *pbus_data) ++{ ++ int ret, saved_page; ++ ++ saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); ++ ++ ret = __air_buckpbus_reg_read(phydev, pbus_address, pbus_data); ++ if (ret < 0) ++ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, ++ pbus_address, ret); ++ ++ return phy_restore_page(phydev, saved_page, ret); ++} ++ ++static int __air_write_buf(struct phy_device *phydev, u32 address, ++ const struct firmware *fw) ++{ ++ unsigned int offset; ++ int ret; ++ u16 val; ++ ++ ret = __phy_write(phydev, AIR_PBUS_MODE, AIR_PBUS_MODE_ADDR_INCR); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_PBUS_WR_ADDR_HIGH, HIWORD(address)); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_PBUS_WR_ADDR_LOW, LOWORD(address)); ++ if (ret < 0) ++ return ret; ++ ++ for (offset = 0; offset < fw->size; offset += 4) { ++ val = get_unaligned_le16(&fw->data[offset + 2]); ++ ret = __phy_write(phydev, AIR_PBUS_WR_DATA_HIGH, val); ++ if (ret < 0) ++ return ret; ++ ++ val = get_unaligned_le16(&fw->data[offset]); ++ ret = __phy_write(phydev, AIR_PBUS_WR_DATA_LOW, val); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int air_write_buf(struct phy_device *phydev, u32 address, ++ const struct firmware *fw) ++{ ++ int ret, saved_page; ++ ++ saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); ++ ++ ret = __air_write_buf(phydev, address, fw); ++ if (ret < 0) ++ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, ++ address, ret); ++ ++ return phy_restore_page(phydev, saved_page, ret); ++} ++ ++static int en8811h_load_firmware(struct phy_device *phydev) ++{ ++ struct device *dev = &phydev->mdio.dev; ++ const struct firmware *fw1, *fw2; ++ u32 pbus_value; ++ int ret; ++ ++ ret = request_firmware_direct(&fw1, EN8811H_MD32_DM, dev); ++ if (ret < 0) ++ return ret; ++ ++ ret = request_firmware_direct(&fw2, EN8811H_MD32_DSP, dev); ++ if (ret < 0) ++ goto en8811h_load_firmware_rel1; ++ ++ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, ++ EN8811H_FW_CTRL_1_START); ++ if (ret < 0) ++ goto en8811h_load_firmware_out; ++ ++ ret = air_buckpbus_reg_read(phydev, EN8811H_FW_CTRL_2, &pbus_value); ++ if (ret < 0) ++ goto en8811h_load_firmware_out; ++ pbus_value |= EN8811H_FW_CTRL_2_LOADING; ++ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_2, pbus_value); ++ if (ret < 0) ++ goto en8811h_load_firmware_out; ++ ++ ret = air_write_buf(phydev, AIR_FW_ADDR_DM, fw1); ++ if (ret < 0) ++ goto en8811h_load_firmware_out; ++ ++ ret = air_write_buf(phydev, AIR_FW_ADDR_DSP, fw2); ++ if (ret < 0) ++ goto en8811h_load_firmware_out; ++ ++ ret = air_buckpbus_reg_read(phydev, EN8811H_FW_CTRL_2, &pbus_value); ++ if (ret < 0) ++ goto en8811h_load_firmware_out; ++ pbus_value &= ~EN8811H_FW_CTRL_2_LOADING; ++ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_2, pbus_value); ++ if (ret < 0) ++ goto en8811h_load_firmware_out; ++ ++ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, ++ EN8811H_FW_CTRL_1_FINISH); ++ if (ret < 0) ++ goto en8811h_load_firmware_out; ++ ++ ret = 0; ++ ++en8811h_load_firmware_out: ++ release_firmware(fw2); ++ ++en8811h_load_firmware_rel1: ++ release_firmware(fw1); ++ ++ if (ret < 0) ++ phydev_err(phydev, "Load firmware failed: %d\n", ret); ++ ++ return ret; ++} ++ ++static int en8811h_restart_host(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, ++ EN8811H_FW_CTRL_1_START); ++ if (ret < 0) ++ return ret; ++ ++ return air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, ++ EN8811H_FW_CTRL_1_FINISH); ++} ++ ++static int air_hw_led_on_set(struct phy_device *phydev, u8 index, bool on) ++{ ++ struct en8811h_priv *priv = phydev->priv; ++ bool changed; ++ ++ if (index >= EN8811H_LED_COUNT) ++ return -EINVAL; ++ ++ if (on) ++ changed = !test_and_set_bit(AIR_PHY_LED_STATE_FORCE_ON, ++ &priv->led[index].state); ++ else ++ changed = !!test_and_clear_bit(AIR_PHY_LED_STATE_FORCE_ON, ++ &priv->led[index].state); ++ ++ changed |= (priv->led[index].rules != 0); ++ ++ if (changed) ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND2, ++ AIR_PHY_LED_ON(index), ++ AIR_PHY_LED_ON_MASK, ++ on ? AIR_PHY_LED_ON_FORCE_ON : 0); ++ ++ return 0; ++} ++ ++static int air_hw_led_blink_set(struct phy_device *phydev, u8 index, ++ bool blinking) ++{ ++ struct en8811h_priv *priv = phydev->priv; ++ bool changed; ++ ++ if (index >= EN8811H_LED_COUNT) ++ return -EINVAL; ++ ++ if (blinking) ++ changed = !test_and_set_bit(AIR_PHY_LED_STATE_FORCE_BLINK, ++ &priv->led[index].state); ++ else ++ changed = !!test_and_clear_bit(AIR_PHY_LED_STATE_FORCE_BLINK, ++ &priv->led[index].state); ++ ++ changed |= (priv->led[index].rules != 0); ++ ++ if (changed) ++ return phy_write_mmd(phydev, MDIO_MMD_VEND2, ++ AIR_PHY_LED_BLINK(index), ++ blinking ? ++ AIR_PHY_LED_BLINK_FORCE_BLINK : 0); ++ else ++ return 0; ++} ++ ++static int air_led_blink_set(struct phy_device *phydev, u8 index, ++ unsigned long *delay_on, ++ unsigned long *delay_off) ++{ ++ struct en8811h_priv *priv = phydev->priv; ++ bool blinking = false; ++ int err; ++ ++ if (index >= EN8811H_LED_COUNT) ++ return -EINVAL; ++ ++ if (delay_on && delay_off && (*delay_on > 0) && (*delay_off > 0)) { ++ blinking = true; ++ *delay_on = 50; ++ *delay_off = 50; ++ } ++ ++ err = air_hw_led_blink_set(phydev, index, blinking); ++ if (err) ++ return err; ++ ++ /* led-blink set, so switch led-on off */ ++ err = air_hw_led_on_set(phydev, index, false); ++ if (err) ++ return err; ++ ++ /* hw-control is off*/ ++ if (!!test_bit(AIR_PHY_LED_STATE_FORCE_BLINK, &priv->led[index].state)) ++ priv->led[index].rules = 0; ++ ++ return 0; ++} ++ ++static int air_led_brightness_set(struct phy_device *phydev, u8 index, ++ enum led_brightness value) ++{ ++ struct en8811h_priv *priv = phydev->priv; ++ int err; ++ ++ if (index >= EN8811H_LED_COUNT) ++ return -EINVAL; ++ ++ /* led-on set, so switch led-blink off */ ++ err = air_hw_led_blink_set(phydev, index, false); ++ if (err) ++ return err; ++ ++ err = air_hw_led_on_set(phydev, index, (value != LED_OFF)); ++ if (err) ++ return err; ++ ++ /* hw-control is off */ ++ if (!!test_bit(AIR_PHY_LED_STATE_FORCE_ON, &priv->led[index].state)) ++ priv->led[index].rules = 0; ++ ++ return 0; ++} ++ ++static int air_led_hw_control_get(struct phy_device *phydev, u8 index, ++ unsigned long *rules) ++{ ++ struct en8811h_priv *priv = phydev->priv; ++ ++ if (index >= EN8811H_LED_COUNT) ++ return -EINVAL; ++ ++ *rules = priv->led[index].rules; ++ ++ return 0; ++}; ++ ++static int air_led_hw_control_set(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ struct en8811h_priv *priv = phydev->priv; ++ u16 on = 0, blink = 0; ++ int ret; ++ ++ priv->led[index].rules = rules; ++ ++ if (index >= EN8811H_LED_COUNT) ++ return -EINVAL; ++ ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK))) { ++ on |= AIR_PHY_LED_ON_LINK10; ++ if (rules & BIT(TRIGGER_NETDEV_RX)) ++ blink |= AIR_PHY_LED_BLINK_10RX; ++ if (rules & BIT(TRIGGER_NETDEV_TX)) ++ blink |= AIR_PHY_LED_BLINK_10TX; ++ } ++ ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK))) { ++ on |= AIR_PHY_LED_ON_LINK100; ++ if (rules & BIT(TRIGGER_NETDEV_RX)) ++ blink |= AIR_PHY_LED_BLINK_100RX; ++ if (rules & BIT(TRIGGER_NETDEV_TX)) ++ blink |= AIR_PHY_LED_BLINK_100TX; ++ } ++ ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK))) { ++ on |= AIR_PHY_LED_ON_LINK1000; ++ if (rules & BIT(TRIGGER_NETDEV_RX)) ++ blink |= AIR_PHY_LED_BLINK_1000RX; ++ if (rules & BIT(TRIGGER_NETDEV_TX)) ++ blink |= AIR_PHY_LED_BLINK_1000TX; ++ } ++ ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_2500) | BIT(TRIGGER_NETDEV_LINK))) { ++ on |= AIR_PHY_LED_ON_LINK2500; ++ if (rules & BIT(TRIGGER_NETDEV_RX)) ++ blink |= AIR_PHY_LED_BLINK_2500RX; ++ if (rules & BIT(TRIGGER_NETDEV_TX)) ++ blink |= AIR_PHY_LED_BLINK_2500TX; ++ } ++ ++ if (on == 0) { ++ if (rules & BIT(TRIGGER_NETDEV_RX)) { ++ blink |= AIR_PHY_LED_BLINK_10RX | ++ AIR_PHY_LED_BLINK_100RX | ++ AIR_PHY_LED_BLINK_1000RX | ++ AIR_PHY_LED_BLINK_2500RX; ++ } ++ if (rules & BIT(TRIGGER_NETDEV_TX)) { ++ blink |= AIR_PHY_LED_BLINK_10TX | ++ AIR_PHY_LED_BLINK_100TX | ++ AIR_PHY_LED_BLINK_1000TX | ++ AIR_PHY_LED_BLINK_2500TX; ++ } ++ } ++ ++ if (rules & BIT(TRIGGER_NETDEV_FULL_DUPLEX)) ++ on |= AIR_PHY_LED_ON_FDX; ++ ++ if (rules & BIT(TRIGGER_NETDEV_HALF_DUPLEX)) ++ on |= AIR_PHY_LED_ON_HDX; ++ ++ if (blink || on) { ++ /* switch hw-control on, so led-on and led-blink are off */ ++ clear_bit(AIR_PHY_LED_STATE_FORCE_ON, &priv->led[index].state); ++ clear_bit(AIR_PHY_LED_STATE_FORCE_BLINK, &priv->led[index].state); ++ } else { ++ priv->led[index].rules = 0; ++ } ++ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_ON(index), ++ AIR_PHY_LED_ON_MASK, on); ++ ++ if (ret < 0) ++ return ret; ++ ++ return phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BLINK(index), ++ blink); ++}; ++ ++static int air_led_init(struct phy_device *phydev, u8 index, u8 state, u8 pol) ++{ ++ int cl45_data; ++ int err; ++ ++ if (index >= EN8811H_LED_COUNT) ++ return -EINVAL; ++ ++ cl45_data = phy_read_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_ON(index)); ++ if (cl45_data < 0) ++ return cl45_data; ++ ++ if (state == AIR_LED_ENABLE) ++ cl45_data |= AIR_PHY_LED_ON_ENABLE; ++ else ++ cl45_data &= ~AIR_PHY_LED_ON_ENABLE; ++ ++ if (pol == AIR_ACTIVE_HIGH) ++ cl45_data |= AIR_PHY_LED_ON_POLARITY; ++ else ++ cl45_data &= ~AIR_PHY_LED_ON_POLARITY; ++ ++ err = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_ON(index), ++ cl45_data); ++ if (err < 0) ++ return err; ++ ++ return 0; ++} ++ ++static int air_leds_init(struct phy_device *phydev, int num, int dur, int mode) ++{ ++ struct en8811h_priv *priv = phydev->priv; ++ int cl45_data = dur; ++ int ret, i; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_BLINK, ++ cl45_data); ++ if (ret < 0) ++ return ret; ++ ++ cl45_data >>= 1; ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_ON, ++ cl45_data); ++ if (ret < 0) ++ return ret; ++ ++ cl45_data = phy_read_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR); ++ if (cl45_data < 0) ++ return cl45_data; ++ ++ switch (mode) { ++ case AIR_LED_MODE_DISABLE: ++ cl45_data &= ~AIR_PHY_LED_BCR_EXT_CTRL; ++ cl45_data &= ~AIR_PHY_LED_BCR_MODE_MASK; ++ break; ++ case AIR_LED_MODE_USER_DEFINE: ++ cl45_data |= AIR_PHY_LED_BCR_EXT_CTRL; ++ cl45_data |= AIR_PHY_LED_BCR_CLK_EN; ++ break; ++ default: ++ phydev_err(phydev, "LED mode %d is not supported\n", mode); ++ return -EINVAL; ++ } ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR, cl45_data); ++ if (ret < 0) ++ return ret; ++ ++ for (i = 0; i < num; ++i) { ++ ret = air_led_init(phydev, i, AIR_LED_ENABLE, AIR_ACTIVE_HIGH); ++ if (ret < 0) { ++ phydev_err(phydev, "LED%d init failed: %d\n", i, ret); ++ return ret; ++ } ++ air_led_hw_control_set(phydev, i, priv->led[i].rules); ++ } ++ ++ return 0; ++} ++ ++static int en8811h_led_hw_is_supported(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ if (index >= EN8811H_LED_COUNT) ++ return -EINVAL; ++ ++ /* All combinations of the supported triggers are allowed */ ++ if (rules & ~en8811h_led_trig) ++ return -EOPNOTSUPP; ++ ++ return 0; ++}; ++ ++static int en8811h_probe(struct phy_device *phydev) ++{ ++ struct en8811h_priv *priv; ++ ++ priv = devm_kzalloc(&phydev->mdio.dev, sizeof(struct en8811h_priv), ++ GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->led[0].rules = AIR_DEFAULT_TRIGGER_LED0; ++ priv->led[1].rules = AIR_DEFAULT_TRIGGER_LED1; ++ priv->led[2].rules = AIR_DEFAULT_TRIGGER_LED2; ++ ++ phydev->priv = priv; ++ ++ /* MDIO_DEVS1/2 empty, so set mmds_present bits here */ ++ phydev->c45_ids.mmds_present |= MDIO_DEVS_PMAPMD | MDIO_DEVS_AN; ++ ++ return 0; ++} ++ ++static int en8811h_config_init(struct phy_device *phydev) ++{ ++ struct en8811h_priv *priv = phydev->priv; ++ struct device *dev = &phydev->mdio.dev; ++ int ret, pollret, reg_value; ++ u32 pbus_value; ++ ++ if (!priv->firmware_version) ++ ret = en8811h_load_firmware(phydev); ++ else ++ ret = en8811h_restart_host(phydev); ++ if (ret < 0) ++ return ret; ++ ++ /* Because of mdio-lock, may have to wait for multiple loads */ ++ pollret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, ++ EN8811H_PHY_FW_STATUS, reg_value, ++ reg_value == EN8811H_PHY_READY, ++ 20000, 7500000, true); ++ ++ ret = air_buckpbus_reg_read(phydev, EN8811H_FW_VERSION, &pbus_value); ++ if (ret < 0) ++ return ret; ++ ++ if (pollret || !pbus_value) { ++ phydev_err(phydev, "Firmware not ready: 0x%x\n", reg_value); ++ return -ENODEV; ++ } ++ ++ if (!priv->firmware_version) { ++ phydev_info(phydev, "MD32 firmware version: %08x\n", pbus_value); ++ priv->firmware_version = pbus_value; ++ } ++ ++ /* Select mode 1, the only mode supported */ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_HOST_CMD_1, ++ AIR_PHY_HOST_CMD_1_MODE1); ++ if (ret < 0) ++ return ret; ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_HOST_CMD_2, ++ AIR_PHY_HOST_CMD_2_MODE1); ++ if (ret < 0) ++ return ret; ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_HOST_CMD_3, ++ AIR_PHY_HOST_CMD_3_MODE1); ++ if (ret < 0) ++ return ret; ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_HOST_CMD_4, ++ AIR_PHY_HOST_CMD_4_MODE1); ++ if (ret < 0) ++ return ret; ++ ++ /* Serdes polarity */ ++ ret = air_buckpbus_reg_read(phydev, EN8811H_POLARITY, &pbus_value); ++ if (ret < 0) ++ return ret; ++ if (device_property_read_bool(dev, "airoha,pnswap-rx")) ++ pbus_value |= EN8811H_POLARITY_RX_REVERSE; ++ else ++ pbus_value &= ~EN8811H_POLARITY_RX_REVERSE; ++ if (device_property_read_bool(dev, "airoha,pnswap-tx")) ++ pbus_value &= ~EN8811H_POLARITY_TX_NORMAL; ++ else ++ pbus_value |= EN8811H_POLARITY_TX_NORMAL; ++ ret = air_buckpbus_reg_write(phydev, EN8811H_POLARITY, pbus_value); ++ if (ret < 0) ++ return ret; ++ ++ ret = air_leds_init(phydev, EN8811H_LED_COUNT, AIR_PHY_LED_DUR, ++ AIR_LED_MODE_USER_DEFINE); ++ if (ret < 0) { ++ phydev_err(phydev, "Failed to initialize leds: %d\n", ret); ++ return ret; ++ } ++ ++ ret = air_buckpbus_reg_read(phydev, EN8811H_GPIO_OUTPUT, &pbus_value); ++ if (ret < 0) ++ return ret; ++ pbus_value |= EN8811H_GPIO_OUTPUT_345; ++ ret = air_buckpbus_reg_write(phydev, EN8811H_GPIO_OUTPUT, pbus_value); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int en8811h_get_features(struct phy_device *phydev) ++{ ++ linkmode_set_bit_array(phy_basic_ports_array, ++ ARRAY_SIZE(phy_basic_ports_array), ++ phydev->supported); ++ ++ return genphy_c45_pma_read_abilities(phydev); ++} ++ ++static int en8811h_get_rate_matching(struct phy_device *phydev, ++ phy_interface_t iface) ++{ ++ return RATE_MATCH_PAUSE; ++} ++ ++static int en8811h_config_aneg(struct phy_device *phydev) ++{ ++ bool changed = false; ++ int err, val; ++ ++ val = 0; ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, ++ phydev->advertising)) ++ val |= MDIO_AN_10GBT_CTRL_ADV2_5G; ++ err = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, ++ MDIO_AN_10GBT_CTRL_ADV2_5G, val); ++ if (err < 0) ++ return err; ++ if (err > 0) ++ changed = true; ++ ++ return __genphy_config_aneg(phydev, changed); ++} ++ ++static int en8811h_read_status(struct phy_device *phydev) ++{ ++ struct en8811h_priv *priv = phydev->priv; ++ u32 pbus_value; ++ int ret, val; ++ ++ ret = genphy_update_link(phydev); ++ if (ret) ++ return ret; ++ ++ phydev->master_slave_get = MASTER_SLAVE_CFG_UNSUPPORTED; ++ phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED; ++ phydev->speed = SPEED_UNKNOWN; ++ phydev->duplex = DUPLEX_UNKNOWN; ++ phydev->pause = 0; ++ phydev->asym_pause = 0; ++ ++ ret = genphy_read_master_slave(phydev); ++ if (ret < 0) ++ return ret; ++ ++ ret = genphy_read_lpa(phydev); ++ if (ret < 0) ++ return ret; ++ ++ /* Get link partner 2.5GBASE-T ability from vendor register */ ++ ret = air_buckpbus_reg_read(phydev, EN8811H_2P5G_LPA, &pbus_value); ++ if (ret < 0) ++ return ret; ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, ++ phydev->lp_advertising, ++ pbus_value & EN8811H_2P5G_LPA_2P5G); ++ ++ if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) ++ phy_resolve_aneg_pause(phydev); ++ ++ if (!phydev->link) ++ return 0; ++ ++ /* Get real speed from vendor register */ ++ val = phy_read(phydev, AIR_AUX_CTRL_STATUS); ++ if (val < 0) ++ return val; ++ switch (val & AIR_AUX_CTRL_STATUS_SPEED_MASK) { ++ case AIR_AUX_CTRL_STATUS_SPEED_2500: ++ phydev->speed = SPEED_2500; ++ break; ++ case AIR_AUX_CTRL_STATUS_SPEED_1000: ++ phydev->speed = SPEED_1000; ++ break; ++ case AIR_AUX_CTRL_STATUS_SPEED_100: ++ phydev->speed = SPEED_100; ++ break; ++ } ++ ++ /* BUG in PHY firmware: MDIO_AN_10GBT_STAT_LP2_5G does not get set. ++ * Firmware before version 24011202 has no vendor register 2P5G_LPA. ++ * Assume link partner advertised it if connected at 2500Mbps. ++ */ ++ if (priv->firmware_version < 0x24011202) { ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, ++ phydev->lp_advertising, ++ phydev->speed == SPEED_2500); ++ } ++ ++ /* Only supports full duplex */ ++ phydev->duplex = DUPLEX_FULL; ++ ++ return 0; ++} ++ ++static int en8811h_clear_intr(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_HOST_CMD_3, ++ AIR_PHY_HOST_CMD_3_DOCMD); ++ if (ret < 0) ++ return ret; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_HOST_CMD_4, ++ AIR_PHY_HOST_CMD_4_INTCLR); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static irqreturn_t en8811h_handle_interrupt(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = en8811h_clear_intr(phydev); ++ if (ret < 0) { ++ phy_error(phydev); ++ return IRQ_NONE; ++ } ++ ++ phy_trigger_machine(phydev); ++ ++ return IRQ_HANDLED; ++} ++ ++static struct phy_driver en8811h_driver[] = { ++{ ++ PHY_ID_MATCH_MODEL(EN8811H_PHY_ID), ++ .name = "Airoha EN8811H", ++ .probe = en8811h_probe, ++ .get_features = en8811h_get_features, ++ .config_init = en8811h_config_init, ++ .get_rate_matching = en8811h_get_rate_matching, ++ .config_aneg = en8811h_config_aneg, ++ .read_status = en8811h_read_status, ++ .config_intr = en8811h_clear_intr, ++ .handle_interrupt = en8811h_handle_interrupt, ++ .led_hw_is_supported = en8811h_led_hw_is_supported, ++ .read_page = air_phy_read_page, ++ .write_page = air_phy_write_page, ++ .led_blink_set = air_led_blink_set, ++ .led_brightness_set = air_led_brightness_set, ++ .led_hw_control_set = air_led_hw_control_set, ++ .led_hw_control_get = air_led_hw_control_get, ++} }; ++ ++module_phy_driver(en8811h_driver); ++ ++static struct mdio_device_id __maybe_unused en8811h_tbl[] = { ++ { PHY_ID_MATCH_MODEL(EN8811H_PHY_ID) }, ++ { } ++}; ++MODULE_DEVICE_TABLE(mdio, en8811h_tbl); ++MODULE_FIRMWARE(EN8811H_MD32_DM); ++MODULE_FIRMWARE(EN8811H_MD32_DSP); ++ ++MODULE_DESCRIPTION("Airoha EN8811H PHY drivers"); ++MODULE_AUTHOR("Airoha"); ++MODULE_AUTHOR("Eric Woudstra "); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/pending-6.6/760-net-core-add-optional-threading-for-backlog-processi.patch b/target/linux/generic/pending-6.6/760-net-core-add-optional-threading-for-backlog-processi.patch new file mode 100644 index 0000000000..42c8519c90 --- /dev/null +++ b/target/linux/generic/pending-6.6/760-net-core-add-optional-threading-for-backlog-processi.patch @@ -0,0 +1,227 @@ +From: Felix Fietkau +Date: Thu, 16 Feb 2023 18:39:04 +0100 +Subject: [PATCH] net/core: add optional threading for backlog processing + +When dealing with few flows or an imbalance on CPU utilization, static RPS +CPU assignment can be too inflexible. Add support for enabling threaded NAPI +for backlog processing in order to allow the scheduler to better balance +processing. This helps better spread the load across idle CPUs. + +Signed-off-by: Felix Fietkau +--- + +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -543,6 +543,7 @@ static inline bool napi_complete(struct + } + + int dev_set_threaded(struct net_device *dev, bool threaded); ++int backlog_set_threaded(bool threaded); + + /** + * napi_disable - prevent NAPI from scheduling +@@ -3152,6 +3153,7 @@ struct softnet_data { + unsigned int processed; + unsigned int time_squeeze; + unsigned int received_rps; ++ unsigned int process_queue_empty; + #ifdef CONFIG_RPS + struct softnet_data *rps_ipi_list; + #endif +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -4625,7 +4625,7 @@ static int napi_schedule_rps(struct soft + struct softnet_data *mysd = this_cpu_ptr(&softnet_data); + + #ifdef CONFIG_RPS +- if (sd != mysd) { ++ if (sd != mysd && !test_bit(NAPI_STATE_THREADED, &sd->backlog.state)) { + sd->rps_ipi_next = mysd->rps_ipi_list; + mysd->rps_ipi_list = sd; + +@@ -5806,6 +5806,8 @@ static DEFINE_PER_CPU(struct work_struct + /* Network device is going away, flush any packets still pending */ + static void flush_backlog(struct work_struct *work) + { ++ unsigned int process_queue_empty; ++ bool threaded, flush_processq; + struct sk_buff *skb, *tmp; + struct softnet_data *sd; + +@@ -5820,8 +5822,17 @@ static void flush_backlog(struct work_st + input_queue_head_incr(sd); + } + } ++ ++ threaded = test_bit(NAPI_STATE_THREADED, &sd->backlog.state); ++ flush_processq = threaded && ++ !skb_queue_empty_lockless(&sd->process_queue); ++ if (flush_processq) ++ process_queue_empty = sd->process_queue_empty; + rps_unlock_irq_enable(sd); + ++ if (threaded) ++ goto out; ++ + skb_queue_walk_safe(&sd->process_queue, skb, tmp) { + if (skb->dev->reg_state == NETREG_UNREGISTERING) { + __skb_unlink(skb, &sd->process_queue); +@@ -5829,7 +5840,16 @@ static void flush_backlog(struct work_st + input_queue_head_incr(sd); + } + } ++ ++out: + local_bh_enable(); ++ ++ while (flush_processq) { ++ msleep(1); ++ rps_lock_irq_disable(sd); ++ flush_processq = process_queue_empty == sd->process_queue_empty; ++ rps_unlock_irq_enable(sd); ++ } + } + + static bool flush_required(int cpu) +@@ -5961,6 +5981,7 @@ static int process_backlog(struct napi_s + } + + rps_lock_irq_disable(sd); ++ sd->process_queue_empty++; + if (skb_queue_empty(&sd->input_pkt_queue)) { + /* + * Inline a custom version of __napi_complete(). +@@ -5970,7 +5991,8 @@ static int process_backlog(struct napi_s + * We can use a plain write instead of clear_bit(), + * and we dont need an smp_mb() memory barrier. + */ +- napi->state = 0; ++ napi->state &= ~(NAPIF_STATE_SCHED | ++ NAPIF_STATE_SCHED_THREADED); + again = false; + } else { + skb_queue_splice_tail_init(&sd->input_pkt_queue, +@@ -6386,6 +6408,55 @@ int dev_set_threaded(struct net_device * + } + EXPORT_SYMBOL(dev_set_threaded); + ++int backlog_set_threaded(bool threaded) ++{ ++ static bool backlog_threaded; ++ int err = 0; ++ int i; ++ ++ if (backlog_threaded == threaded) ++ return 0; ++ ++ for_each_possible_cpu(i) { ++ struct softnet_data *sd = &per_cpu(softnet_data, i); ++ struct napi_struct *n = &sd->backlog; ++ ++ if (n->thread) ++ continue; ++ n->thread = kthread_run(napi_threaded_poll, n, "napi/backlog-%d", i); ++ if (IS_ERR(n->thread)) { ++ err = PTR_ERR(n->thread); ++ pr_err("kthread_run failed with err %d\n", err); ++ n->thread = NULL; ++ threaded = false; ++ break; ++ } ++ ++ } ++ ++ backlog_threaded = threaded; ++ ++ /* Make sure kthread is created before THREADED bit ++ * is set. ++ */ ++ smp_mb__before_atomic(); ++ ++ for_each_possible_cpu(i) { ++ struct softnet_data *sd = &per_cpu(softnet_data, i); ++ struct napi_struct *n = &sd->backlog; ++ unsigned long flags; ++ ++ rps_lock_irqsave(sd, &flags); ++ if (threaded) ++ n->state |= NAPIF_STATE_THREADED; ++ else ++ n->state &= ~NAPIF_STATE_THREADED; ++ rps_unlock_irq_restore(sd, &flags); ++ } ++ ++ return err; ++} ++ + void netif_napi_add_weight(struct net_device *dev, struct napi_struct *napi, + int (*poll)(struct napi_struct *, int), int weight) + { +@@ -11168,6 +11239,9 @@ static int dev_cpu_dead(unsigned int old + raise_softirq_irqoff(NET_TX_SOFTIRQ); + local_irq_enable(); + ++ if (test_bit(NAPI_STATE_THREADED, &oldsd->backlog.state)) ++ return 0; ++ + #ifdef CONFIG_RPS + remsd = oldsd->rps_ipi_list; + oldsd->rps_ipi_list = NULL; +@@ -11480,6 +11554,7 @@ static int __init net_dev_init(void) + INIT_CSD(&sd->defer_csd, trigger_rx_softirq, sd); + spin_lock_init(&sd->defer_lock); + ++ INIT_LIST_HEAD(&sd->backlog.poll_list); + init_gro_hash(&sd->backlog); + sd->backlog.poll = process_backlog; + sd->backlog.weight = weight_p; +--- a/net/core/sysctl_net_core.c ++++ b/net/core/sysctl_net_core.c +@@ -29,6 +29,7 @@ static int int_3600 = 3600; + static int min_sndbuf = SOCK_MIN_SNDBUF; + static int min_rcvbuf = SOCK_MIN_RCVBUF; + static int max_skb_frags = MAX_SKB_FRAGS; ++static int backlog_threaded; + + static int net_msg_warn; /* Unused, but still a sysctl */ + +@@ -112,6 +113,23 @@ static int rps_sock_flow_sysctl(struct c + } + #endif /* CONFIG_RPS */ + ++static int backlog_threaded_sysctl(struct ctl_table *table, int write, ++ void *buffer, size_t *lenp, loff_t *ppos) ++{ ++ static DEFINE_MUTEX(backlog_threaded_mutex); ++ int ret; ++ ++ mutex_lock(&backlog_threaded_mutex); ++ ++ ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); ++ if (write && !ret) ++ ret = backlog_set_threaded(backlog_threaded); ++ ++ mutex_unlock(&backlog_threaded_mutex); ++ ++ return ret; ++} ++ + #ifdef CONFIG_NET_FLOW_LIMIT + static DEFINE_MUTEX(flow_limit_update_mutex); + +@@ -473,6 +491,15 @@ static struct ctl_table net_core_table[] + .proc_handler = rps_sock_flow_sysctl + }, + #endif ++ { ++ .procname = "backlog_threaded", ++ .data = &backlog_threaded, ++ .maxlen = sizeof(unsigned int), ++ .mode = 0644, ++ .proc_handler = backlog_threaded_sysctl, ++ .extra1 = SYSCTL_ZERO, ++ .extra2 = SYSCTL_ONE ++ }, + #ifdef CONFIG_NET_FLOW_LIMIT + { + .procname = "flow_limit_cpu_bitmap", diff --git a/target/linux/generic/pending-6.6/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch b/target/linux/generic/pending-6.6/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch new file mode 100644 index 0000000000..9556c90b57 --- /dev/null +++ b/target/linux/generic/pending-6.6/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch @@ -0,0 +1,27 @@ +From: Tobias Waldekranz +Subject: [RFC net-next 7/7] net: dsa: mv88e6xxx: Request assisted learning on CPU port +Date: Sat, 16 Jan 2021 02:25:15 +0100 +Archived-At: + +While the hardware is capable of performing learning on the CPU port, +it requires alot of additions to the bridge's forwarding path in order +to handle multi-destination traffic correctly. + +Until that is in place, opt for the next best thing and let DSA sync +the relevant addresses down to the hardware FDB. + +Signed-off-by: Tobias Waldekranz +--- + drivers/net/dsa/mv88e6xxx/chip.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -7025,6 +7025,7 @@ static int mv88e6xxx_register_switch(str + ds->ops = &mv88e6xxx_switch_ops; + ds->ageing_time_min = chip->info->age_time_coeff; + ds->ageing_time_max = chip->info->age_time_coeff * U8_MAX; ++ ds->assisted_learning_on_cpu_port = true; + + /* Some chips support up to 32, but that requires enabling the + * 5-bit port mode, which we do not support. 640k^W16 ought to diff --git a/target/linux/generic/pending-6.6/772-net-dsa-b53-add-support-for-BCM63xx-RGMIIs.patch b/target/linux/generic/pending-6.6/772-net-dsa-b53-add-support-for-BCM63xx-RGMIIs.patch new file mode 100644 index 0000000000..23859816fa --- /dev/null +++ b/target/linux/generic/pending-6.6/772-net-dsa-b53-add-support-for-BCM63xx-RGMIIs.patch @@ -0,0 +1,174 @@ +From patchwork Sun Mar 19 22:08:05 2023 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 8bit +X-Patchwork-Submitter: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= + +X-Patchwork-Id: 13180645 +X-Patchwork-Delegate: kuba@kernel.org +Return-Path: +X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on + aws-us-west-2-korg-lkml-1.web.codeaurora.org +Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) + by smtp.lore.kernel.org (Postfix) with ESMTP id A7A46C6FD1F + for ; Sun, 19 Mar 2023 22:08:15 +0000 (UTC) +Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand + id S230223AbjCSWIN (ORCPT ); + Sun, 19 Mar 2023 18:08:13 -0400 +Received: from lindbergh.monkeyblade.net ([23.128.96.19]:32878 "EHLO + lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org + with ESMTP id S229565AbjCSWIM (ORCPT + ); Sun, 19 Mar 2023 18:08:12 -0400 +Received: from mail-wr1-x42e.google.com (mail-wr1-x42e.google.com + [IPv6:2a00:1450:4864:20::42e]) + by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 605D3E062; + Sun, 19 Mar 2023 15:08:10 -0700 (PDT) +Received: by mail-wr1-x42e.google.com with SMTP id h17so8695188wrt.8; + Sun, 19 Mar 2023 15:08:10 -0700 (PDT) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=gmail.com; s=20210112; t=1679263689; + h=content-transfer-encoding:mime-version:references:in-reply-to + :message-id:date:subject:cc:to:from:from:to:cc:subject:date + :message-id:reply-to; + bh=GmPK75Q9ZD3h3IYflWYuDwS99V2e532KgDlnNucAoJg=; + b=PSdrywW48P4Lq8z9wOSPXFB/ZdO/JfuyiGlw3Gz1Iriy+Smo/cBnJ0Ve9zKkX3AKTO + Tr7/g8xhSQX8sU5WAOEPC13uVjKpO4VZsamXHTmMKL4mmfII3K/piAsQcMQkkNpgouab + Ci9yr+7ASSmqEUHIbYTM6sl6a47rPwqk3b3DcTIE2CwJsPPNXnpQ/aSVbJAcEdhcZICc + X4rAmjrYjcsl8coFIGHHPlrMH9ShekQWxB84vEb6bO1nXOORNPizOHuY1vJ3wa3WgXsx + YwlvutMFVIUXfgL2ZwCmQAKWJPiAaFk+CCk3oxSeOYoAzkjcbMyapz9VnooStfvR2aV3 + k+2g== +X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=1e100.net; s=20210112; t=1679263689; + h=content-transfer-encoding:mime-version:references:in-reply-to + :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc + :subject:date:message-id:reply-to; + bh=GmPK75Q9ZD3h3IYflWYuDwS99V2e532KgDlnNucAoJg=; + b=NGjqrGERyaxRwINtevHaY97h9X9W+1UY62YYwotqwv5+cfvB8myjBbD3WH2WzaqMes + o9MMER9RE8/arW3jIVlBv4ORDUuEZ7AeGgy5UbFyQZIPHlp+hJ/sxFrGvYUwamg4Qrr9 + ojargh8ORsEiMeqaf+5AkmEagNhrrV3ax0pUuWDzbJ3vXGoHjfCetHz5xyNL46dvXBfb + l/OZqjv9IYob552uUoUmCy/TbEQDqvmjkFrROFK9gtBNxgxUJkwbyiWIOVsf6RR8OarP + f7bbvSJYkvTvzx2u/g0Up7NW5ZyihMGBmDs377M3yW6AnSxW6jlfl30QmMU1aEigYXvy + v3mA== +X-Gm-Message-State: AO0yUKUm1PYmYa4xlHuVD23mZcZm83a+xbhcbs0Xryi3yF/+UnjM4Cho + GAfqSh5MZ/rlOAm3Vnpn//9hOG5Lc8vLYg== +X-Google-Smtp-Source: + AK7set+5pTahGGgk1hF/mHGkGBhsMf0//oQjZd4QFHx+HaeSgP5f6q7g0bRUcTX8kRtgHH0T7l1/hQ== +X-Received: by 2002:a5d:474f:0:b0:2d6:2ae8:70d with SMTP id + o15-20020a5d474f000000b002d62ae8070dmr2382593wrs.39.1679263688549; + Sun, 19 Mar 2023 15:08:08 -0700 (PDT) +Received: from atlantis.lan (255.red-79-146-124.dynamicip.rima-tde.net. + [79.146.124.255]) + by smtp.gmail.com with ESMTPSA id + d6-20020a5d6dc6000000b002c53f6c7599sm7354727wrz.29.2023.03.19.15.08.07 + (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); + Sun, 19 Mar 2023 15:08:07 -0700 (PDT) +From: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= +To: f.fainelli@gmail.com, jonas.gorski@gmail.com, andrew@lunn.ch, + olteanv@gmail.com, davem@davemloft.net, edumazet@google.com, + kuba@kernel.org, pabeni@redhat.com, netdev@vger.kernel.org, + linux-kernel@vger.kernel.org +Cc: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= +Subject: [PATCH v2] net: dsa: b53: add support for BCM63xx RGMIIs +Date: Sun, 19 Mar 2023 23:08:05 +0100 +Message-Id: <20230319220805.124024-1-noltari@gmail.com> +X-Mailer: git-send-email 2.30.2 +In-Reply-To: <20230319183330.761251-1-noltari@gmail.com> +References: <20230319183330.761251-1-noltari@gmail.com> +MIME-Version: 1.0 +Precedence: bulk +List-ID: +X-Mailing-List: netdev@vger.kernel.org +X-Patchwork-Delegate: kuba@kernel.org + +BCM63xx RGMII ports require additional configuration in order to work. + +Signed-off-by: Álvaro Fernández Rojas +Reviewed-by: Andrew Lunn +--- + v2: add changes suggested by Andrew: + - Use a switch statement. + - Use dev_dbg() instead of dev_info(). + + drivers/net/dsa/b53/b53_common.c | 46 ++++++++++++++++++++++++++++++++ + drivers/net/dsa/b53/b53_priv.h | 1 + + 2 files changed, 47 insertions(+) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -1209,6 +1209,46 @@ static void b53_force_port_config(struct + b53_write8(dev, B53_CTRL_PAGE, off, reg); + } + ++static void b53_adjust_63xx_rgmii(struct dsa_switch *ds, int port, ++ phy_interface_t interface) ++{ ++ struct b53_device *dev = ds->priv; ++ u8 rgmii_ctrl = 0, off; ++ ++ if (port == dev->imp_port) ++ off = B53_RGMII_CTRL_IMP; ++ else ++ off = B53_RGMII_CTRL_P(port); ++ ++ b53_read8(dev, B53_CTRL_PAGE, off, &rgmii_ctrl); ++ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ rgmii_ctrl |= (RGMII_CTRL_DLL_RXC | RGMII_CTRL_DLL_TXC); ++ break; ++ case PHY_INTERFACE_MODE_RGMII_RXID: ++ rgmii_ctrl &= ~(RGMII_CTRL_DLL_TXC); ++ rgmii_ctrl |= RGMII_CTRL_DLL_RXC; ++ break; ++ case PHY_INTERFACE_MODE_RGMII_TXID: ++ rgmii_ctrl &= ~(RGMII_CTRL_DLL_RXC); ++ rgmii_ctrl |= RGMII_CTRL_DLL_TXC; ++ break; ++ case PHY_INTERFACE_MODE_RGMII: ++ default: ++ rgmii_ctrl &= ~(RGMII_CTRL_DLL_RXC | RGMII_CTRL_DLL_TXC); ++ break; ++ } ++ ++ if (port != dev->imp_port) ++ rgmii_ctrl |= RGMII_CTRL_ENABLE_GMII; ++ ++ b53_write8(dev, B53_CTRL_PAGE, off, rgmii_ctrl); ++ ++ dev_dbg(ds->dev, "Configured port %d for %s\n", port, ++ phy_modes(interface)); ++} ++ + static void b53_adjust_link(struct dsa_switch *ds, int port, + struct phy_device *phydev) + { +@@ -1235,6 +1275,9 @@ static void b53_adjust_link(struct dsa_s + tx_pause, rx_pause); + b53_force_link(dev, port, phydev->link); + ++ if (is63xx(dev) && port >= B53_63XX_RGMII0) ++ b53_adjust_63xx_rgmii(ds, port, phydev->interface); ++ + if (is531x5(dev) && phy_interface_is_rgmii(phydev)) { + if (port == dev->imp_port) + off = B53_RGMII_CTRL_IMP; +@@ -1402,6 +1445,9 @@ void b53_phylink_mac_link_up(struct dsa_ + { + struct b53_device *dev = ds->priv; + ++ if (is63xx(dev) && port >= B53_63XX_RGMII0) ++ b53_adjust_63xx_rgmii(ds, port, interface); ++ + if (mode == MLO_AN_PHY) + return; + +--- a/drivers/net/dsa/b53/b53_priv.h ++++ b/drivers/net/dsa/b53/b53_priv.h +@@ -211,6 +211,7 @@ static inline int is58xx(struct b53_devi + dev->chip_id == BCM7278_DEVICE_ID; + } + ++#define B53_63XX_RGMII0 4 + #define B53_CPU_PORT_25 5 + #define B53_CPU_PORT 8 + diff --git a/target/linux/generic/pending-6.6/773-net-dsa-b53-mmap-add-more-63xx-SoCs.patch b/target/linux/generic/pending-6.6/773-net-dsa-b53-mmap-add-more-63xx-SoCs.patch new file mode 100644 index 0000000000..8ab701ef56 --- /dev/null +++ b/target/linux/generic/pending-6.6/773-net-dsa-b53-mmap-add-more-63xx-SoCs.patch @@ -0,0 +1,108 @@ +From patchwork Tue Mar 21 17:33:57 2023 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 8bit +X-Patchwork-Submitter: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= + +X-Patchwork-Id: 13183003 +X-Patchwork-Delegate: kuba@kernel.org +Return-Path: +X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on + aws-us-west-2-korg-lkml-1.web.codeaurora.org +Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) + by smtp.lore.kernel.org (Postfix) with ESMTP id 823A1C761AF + for ; Tue, 21 Mar 2023 17:35:06 +0000 (UTC) +Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand + id S230490AbjCURfE (ORCPT ); + Tue, 21 Mar 2023 13:35:04 -0400 +Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47440 "EHLO + lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org + with ESMTP id S230357AbjCURex (ORCPT + ); Tue, 21 Mar 2023 13:34:53 -0400 +Received: from mail-wr1-x430.google.com (mail-wr1-x430.google.com + [IPv6:2a00:1450:4864:20::430]) + by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C9A45559D8; + Tue, 21 Mar 2023 10:34:26 -0700 (PDT) +Received: by mail-wr1-x430.google.com with SMTP id m2so14547588wrh.6; + Tue, 21 Mar 2023 10:34:26 -0700 (PDT) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=gmail.com; s=20210112; t=1679420063; + h=content-transfer-encoding:mime-version:references:in-reply-to + :message-id:date:subject:cc:to:from:from:to:cc:subject:date + :message-id:reply-to; + bh=cUvnluVaZPzfEQB9fMRuYo+4/361t/7po7nyUBBJfxc=; + b=F0pa8JmQZ1FeXVtdpCygur8UmLrgKwxCcjaMn312u5zNvsXsEPeCAIDqP2tvNNTwv/ + UYjaNaoZ77HSvv/gSqeG808AXGyNs1PvLuHZYuUTJRNuLaMixKtkNFi4ypheCdk0WCiE + IWz0DIm6ojmdwMqafDUKQ6Qwkv5R0vo8Wh5vpjimEmCelOyMvfuLZNqubsiGqpnCguBp + uWlmKh95/VubCGgiGG2xK1IXQayL14ENuWseDds7nVpVK50NycrFgJbL17Bd6qJKYkbo + m70IC+9jM0hjwKXpyi6ipCBNcW+1E6JIwILVC04Xi+BTpOGhbUAQ59Yn2hyq7tQM7dzs + 4PLg== +X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=1e100.net; s=20210112; t=1679420063; + h=content-transfer-encoding:mime-version:references:in-reply-to + :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc + :subject:date:message-id:reply-to; + bh=cUvnluVaZPzfEQB9fMRuYo+4/361t/7po7nyUBBJfxc=; + b=SCX78yTuGjdnE5nuL0p7+kxGnOzsCExsigLdaV+x/JswmwxSpZvxn223i1yM95klj9 + Rk0RnXqATLF1wZA7L1YmbeZ66zxUwW/osnCjJHPeEF8AGgjK/qawtLl/HJQHN67NaRNQ + bDsRn2nWQ2GRTRFpvD+iGRy4uyQCDu9HFxLbn43fBsBmRnXWGPQP5cEb90tL83/Onp4D + Lx/XcyZOh9QRfJNhj+G1BAeRCLRA/sdA0W3Ecu5SCFs+LtS6uvLVGWDKEDfnZhYY8Xqf + Mx9evWzdW2OorEN2FI6+xTglvnEBcVhHIJ7XEGAhCG6ocgMZeck++774S8RWumWl8xpy + /K9Q== +X-Gm-Message-State: AO0yUKUORAlGfbkNwnYmQnTWcGPqW6sp4g9WfgQmRZGCV+9tCB0OebSP + ICq6v4YPmUPNRl/WNnVCbps= +X-Google-Smtp-Source: + AK7set8pFDl8fHRwGPhAguqxIfqnQ4PY+b57IHEsybIaQ/HPNwdJ1cs1+IPBGHe3TL14dTS4aVNpHA== +X-Received: by 2002:a5d:6991:0:b0:2ce:aab5:f96b with SMTP id + g17-20020a5d6991000000b002ceaab5f96bmr2965175wru.67.1679420062764; + Tue, 21 Mar 2023 10:34:22 -0700 (PDT) +Received: from atlantis.lan (255.red-79-146-124.dynamicip.rima-tde.net. + [79.146.124.255]) + by smtp.gmail.com with ESMTPSA id + b13-20020a056000054d00b002da1261aa44sm184775wrf.48.2023.03.21.10.34.21 + (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); + Tue, 21 Mar 2023 10:34:21 -0700 (PDT) +From: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= +To: f.fainelli@gmail.com, jonas.gorski@gmail.com, andrew@lunn.ch, + olteanv@gmail.com, davem@davemloft.net, edumazet@google.com, + kuba@kernel.org, pabeni@redhat.com, robh+dt@kernel.org, + krzysztof.kozlowski+dt@linaro.org, netdev@vger.kernel.org, + devicetree@vger.kernel.org, linux-kernel@vger.kernel.org +Cc: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= +Subject: [PATCH v2 2/4] net: dsa: b53: mmap: add more 63xx SoCs +Date: Tue, 21 Mar 2023 18:33:57 +0100 +Message-Id: <20230321173359.251778-3-noltari@gmail.com> +X-Mailer: git-send-email 2.30.2 +In-Reply-To: <20230321173359.251778-1-noltari@gmail.com> +References: <20230320155024.164523-1-noltari@gmail.com> + <20230321173359.251778-1-noltari@gmail.com> +MIME-Version: 1.0 +Precedence: bulk +List-ID: +X-Mailing-List: netdev@vger.kernel.org +X-Patchwork-Delegate: kuba@kernel.org + +BCM6318, BCM6362 and BCM63268 are SoCs with a B53 MMAP switch. + +Signed-off-by: Álvaro Fernández Rojas +Reviewed-by: Florian Fainelli +--- + v2: no changes. + + drivers/net/dsa/b53/b53_mmap.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/net/dsa/b53/b53_mmap.c ++++ b/drivers/net/dsa/b53/b53_mmap.c +@@ -345,8 +345,11 @@ static void b53_mmap_shutdown(struct pla + + static const struct of_device_id b53_mmap_of_table[] = { + { .compatible = "brcm,bcm3384-switch" }, ++ { .compatible = "brcm,bcm6318-switch" }, + { .compatible = "brcm,bcm6328-switch" }, ++ { .compatible = "brcm,bcm6362-switch" }, + { .compatible = "brcm,bcm6368-switch" }, ++ { .compatible = "brcm,bcm63268-switch" }, + { .compatible = "brcm,bcm63xx-switch" }, + { /* sentinel */ }, + }; diff --git a/target/linux/generic/pending-6.6/774-net-dsa-b53-mmap-allow-passing-a-chip-ID.patch b/target/linux/generic/pending-6.6/774-net-dsa-b53-mmap-allow-passing-a-chip-ID.patch new file mode 100644 index 0000000000..de237374af --- /dev/null +++ b/target/linux/generic/pending-6.6/774-net-dsa-b53-mmap-allow-passing-a-chip-ID.patch @@ -0,0 +1,195 @@ +From patchwork Tue Mar 21 17:33:58 2023 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 8bit +X-Patchwork-Submitter: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= + +X-Patchwork-Id: 13183004 +X-Patchwork-Delegate: kuba@kernel.org +Return-Path: +X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on + aws-us-west-2-korg-lkml-1.web.codeaurora.org +Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) + by smtp.lore.kernel.org (Postfix) with ESMTP id B2B12C74A5B + for ; Tue, 21 Mar 2023 17:35:12 +0000 (UTC) +Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand + id S230297AbjCURfK (ORCPT ); + Tue, 21 Mar 2023 13:35:10 -0400 +Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47438 "EHLO + lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org + with ESMTP id S230374AbjCURex (ORCPT + ); Tue, 21 Mar 2023 13:34:53 -0400 +Received: from mail-wr1-x432.google.com (mail-wr1-x432.google.com + [IPv6:2a00:1450:4864:20::432]) + by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C906B5550A; + Tue, 21 Mar 2023 10:34:26 -0700 (PDT) +Received: by mail-wr1-x432.google.com with SMTP id y14so14546846wrq.4; + Tue, 21 Mar 2023 10:34:26 -0700 (PDT) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=gmail.com; s=20210112; t=1679420064; + h=content-transfer-encoding:mime-version:references:in-reply-to + :message-id:date:subject:cc:to:from:from:to:cc:subject:date + :message-id:reply-to; + bh=vnvnwWc5Tmg09HBQo/m9RbRM6yM8KLx8r1VA+Abfg3k=; + b=eFv+mwe94Y2YZMiJP5gydXVrGlbIAR5HCrY0rdcoGoMPzQUHLFckZeYCgEKudI55I7 + gMLZYCtLwvDXvKeHM2AUigsq2YuJSeF5QwICPrhTnMwUGBg4yyyltrc3+J0lSd6/4kQv + h0yM1Oo4v0d8CuqjBU6bXienIk34AFVJfsPq+vWQTjAbUL7ht4WHZ2Ez2MFoTvZpkIJA + 5iWMyVoMbugZl6eqNRjvDHFmtBtrZIv8AFs10r2Ca6+Yxm+aq0v33DRkbSVVqgFPNEzy + q5QOXOeLBPL6BvyovOpmVSWGoHf1zFV7lrzcqi+uc+FuYxQ9dyN3ND73DrrhWSkLaSg9 + r8yA== +X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=1e100.net; s=20210112; t=1679420064; + h=content-transfer-encoding:mime-version:references:in-reply-to + :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc + :subject:date:message-id:reply-to; + bh=vnvnwWc5Tmg09HBQo/m9RbRM6yM8KLx8r1VA+Abfg3k=; + b=jIRB8pIlrLA/ovhnEoePs/6SX8fn6l7l4fY2CxX2pLrTbP1JI8AAetPavvrNVQTr2M + Vm0iLbKyL/VpTq9+bSN1SMjaoi4lAMj0pgafoHrwABMVZpFauYvtCfSYTstZ2pw4Dr1j + wYQGj3BUSpFIYHtSIDMkb5449WA3T3TONhaQLRFAUCBD6gAFyEky5fY+DIHrGaj352B6 + 9ST/tkqHgPpuFlmromr42KQWoTFU+Pj0Uhyp7ru4BsnF7tTshWroZZIHUJmSACudEadr + fBPiuurX9jgp9zNqj8Oy0HjiVUnULFCapj8yICGp5s44uDAK/XFqFXpOuJ8ptS6uPazU + xUwg== +X-Gm-Message-State: AO0yUKX2w6QZfaGDHtlZAlY/U8F8VuJa3HwlgXbxgGChgdgvIoFThawv + oDyFAhWbVfe4DxwXTwxgJ/I= +X-Google-Smtp-Source: + AK7set+sH60XiJYup7bqrZTzFJVNe1YGcX/UTfjWV9xfGwNyodc34cHvKpqNagw5J+vEpv6CKvNHaA== +X-Received: by 2002:adf:f344:0:b0:2cd:de25:1c76 with SMTP id + e4-20020adff344000000b002cdde251c76mr12989754wrp.17.1679420064464; + Tue, 21 Mar 2023 10:34:24 -0700 (PDT) +Received: from atlantis.lan (255.red-79-146-124.dynamicip.rima-tde.net. + [79.146.124.255]) + by smtp.gmail.com with ESMTPSA id + b13-20020a056000054d00b002da1261aa44sm184775wrf.48.2023.03.21.10.34.22 + (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); + Tue, 21 Mar 2023 10:34:23 -0700 (PDT) +From: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= +To: f.fainelli@gmail.com, jonas.gorski@gmail.com, andrew@lunn.ch, + olteanv@gmail.com, davem@davemloft.net, edumazet@google.com, + kuba@kernel.org, pabeni@redhat.com, robh+dt@kernel.org, + krzysztof.kozlowski+dt@linaro.org, netdev@vger.kernel.org, + devicetree@vger.kernel.org, linux-kernel@vger.kernel.org +Cc: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= +Subject: [PATCH v2 3/4] net: dsa: b53: mmap: allow passing a chip ID +Date: Tue, 21 Mar 2023 18:33:58 +0100 +Message-Id: <20230321173359.251778-4-noltari@gmail.com> +X-Mailer: git-send-email 2.30.2 +In-Reply-To: <20230321173359.251778-1-noltari@gmail.com> +References: <20230320155024.164523-1-noltari@gmail.com> + <20230321173359.251778-1-noltari@gmail.com> +MIME-Version: 1.0 +Precedence: bulk +List-ID: +X-Mailing-List: netdev@vger.kernel.org +X-Patchwork-Delegate: kuba@kernel.org + +BCM6318 and BCM63268 SoCs require a special handling for their RGMIIs, so we +should be able to identify them as a special BCM63xx switch. + +Signed-off-by: Álvaro Fernández Rojas +--- + v2: + - Add missing chip to b53_switch_chips[]. + - Fix device_get_match_data() casting warning. + - Add BCM63268_DEVICE_ID to BCM6318 too. + - Add BCM6318 in commit description. + + drivers/net/dsa/b53/b53_common.c | 13 +++++++++++++ + drivers/net/dsa/b53/b53_mmap.c | 32 +++++++++++++++++++++++--------- + drivers/net/dsa/b53/b53_priv.h | 9 ++++++++- + 3 files changed, 44 insertions(+), 10 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -2466,6 +2466,19 @@ static const struct b53_chip_data b53_sw + .jumbo_size_reg = B53_JUMBO_MAX_SIZE_63XX, + }, + { ++ .chip_id = BCM63268_DEVICE_ID, ++ .dev_name = "BCM63268", ++ .vlans = 4096, ++ .enabled_ports = 0, /* pdata must provide them */ ++ .arl_bins = 4, ++ .arl_buckets = 1024, ++ .imp_port = 8, ++ .vta_regs = B53_VTA_REGS_63XX, ++ .duplex_reg = B53_DUPLEX_STAT_63XX, ++ .jumbo_pm_reg = B53_JUMBO_PORT_MASK_63XX, ++ .jumbo_size_reg = B53_JUMBO_MAX_SIZE_63XX, ++ }, ++ { + .chip_id = BCM53010_DEVICE_ID, + .dev_name = "BCM53010", + .vlans = 4096, +--- a/drivers/net/dsa/b53/b53_mmap.c ++++ b/drivers/net/dsa/b53/b53_mmap.c +@@ -262,7 +262,7 @@ static int b53_mmap_probe_of(struct plat + return -ENOMEM; + + pdata->regs = mem; +- pdata->chip_id = BCM63XX_DEVICE_ID; ++ pdata->chip_id = (u32)(unsigned long)device_get_match_data(dev); + pdata->big_endian = of_property_read_bool(np, "big-endian"); + + of_ports = of_get_child_by_name(np, "ports"); +@@ -344,14 +344,28 @@ static void b53_mmap_shutdown(struct pla + } + + static const struct of_device_id b53_mmap_of_table[] = { +- { .compatible = "brcm,bcm3384-switch" }, +- { .compatible = "brcm,bcm6318-switch" }, +- { .compatible = "brcm,bcm6328-switch" }, +- { .compatible = "brcm,bcm6362-switch" }, +- { .compatible = "brcm,bcm6368-switch" }, +- { .compatible = "brcm,bcm63268-switch" }, +- { .compatible = "brcm,bcm63xx-switch" }, +- { /* sentinel */ }, ++ { ++ .compatible = "brcm,bcm3384-switch", ++ .data = (void *)BCM63XX_DEVICE_ID, ++ }, { ++ .compatible = "brcm,bcm6318-switch", ++ .data = (void *)BCM63268_DEVICE_ID, ++ }, { ++ .compatible = "brcm,bcm6328-switch", ++ .data = (void *)BCM63XX_DEVICE_ID, ++ }, { ++ .compatible = "brcm,bcm6362-switch", ++ .data = (void *)BCM63XX_DEVICE_ID, ++ }, { ++ .compatible = "brcm,bcm6368-switch", ++ .data = (void *)BCM63XX_DEVICE_ID, ++ }, { ++ .compatible = "brcm,bcm63268-switch", ++ .data = (void *)BCM63268_DEVICE_ID, ++ }, { ++ .compatible = "brcm,bcm63xx-switch", ++ .data = (void *)BCM63XX_DEVICE_ID, ++ }, { /* sentinel */ } + }; + MODULE_DEVICE_TABLE(of, b53_mmap_of_table); + +--- a/drivers/net/dsa/b53/b53_priv.h ++++ b/drivers/net/dsa/b53/b53_priv.h +@@ -70,6 +70,7 @@ enum { + BCM53125_DEVICE_ID = 0x53125, + BCM53128_DEVICE_ID = 0x53128, + BCM63XX_DEVICE_ID = 0x6300, ++ BCM63268_DEVICE_ID = 0x63268, + BCM53010_DEVICE_ID = 0x53010, + BCM53011_DEVICE_ID = 0x53011, + BCM53012_DEVICE_ID = 0x53012, +@@ -191,7 +192,13 @@ static inline int is531x5(struct b53_dev + + static inline int is63xx(struct b53_device *dev) + { +- return dev->chip_id == BCM63XX_DEVICE_ID; ++ return dev->chip_id == BCM63XX_DEVICE_ID || ++ dev->chip_id == BCM63268_DEVICE_ID; ++} ++ ++static inline int is63268(struct b53_device *dev) ++{ ++ return dev->chip_id == BCM63268_DEVICE_ID; + } + + static inline int is5301x(struct b53_device *dev) diff --git a/target/linux/generic/pending-6.6/775-net-dsa-b53-add-BCM63268-RGMII-configuration.patch b/target/linux/generic/pending-6.6/775-net-dsa-b53-add-BCM63268-RGMII-configuration.patch new file mode 100644 index 0000000000..d90d757fb2 --- /dev/null +++ b/target/linux/generic/pending-6.6/775-net-dsa-b53-add-BCM63268-RGMII-configuration.patch @@ -0,0 +1,123 @@ +From patchwork Tue Mar 21 17:33:59 2023 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 8bit +X-Patchwork-Submitter: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= + +X-Patchwork-Id: 13183005 +X-Patchwork-Delegate: kuba@kernel.org +Return-Path: +X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on + aws-us-west-2-korg-lkml-1.web.codeaurora.org +Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) + by smtp.lore.kernel.org (Postfix) with ESMTP id 31BE4C761A6 + for ; Tue, 21 Mar 2023 17:35:16 +0000 (UTC) +Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand + id S229674AbjCURfN (ORCPT ); + Tue, 21 Mar 2023 13:35:13 -0400 +Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47684 "EHLO + lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org + with ESMTP id S230327AbjCURfB (ORCPT + ); Tue, 21 Mar 2023 13:35:01 -0400 +Received: from mail-wr1-x436.google.com (mail-wr1-x436.google.com + [IPv6:2a00:1450:4864:20::436]) + by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E1D855507D; + Tue, 21 Mar 2023 10:34:27 -0700 (PDT) +Received: by mail-wr1-x436.google.com with SMTP id i9so14537769wrp.3; + Tue, 21 Mar 2023 10:34:27 -0700 (PDT) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=gmail.com; s=20210112; t=1679420066; + h=content-transfer-encoding:mime-version:references:in-reply-to + :message-id:date:subject:cc:to:from:from:to:cc:subject:date + :message-id:reply-to; + bh=asmFs22xWYwR1Ql9m/IrNv+MPUNDn8hSjmwDRYvO7mE=; + b=Cqj2C6aG5vEOlhh9N3ybvDA0CV38nhQODnfdnr7utNddd323iDagoJty1Wmi3MAzj1 + 5ORmYT5fQvUnild7C4RhcCNTBn+MoYZ+wDZwZYelu6BKHkW11YFK949ax5B50by+ASR2 + z+rGI3wR5fVXd4VDgmcsT6zF5x69wKyhbhqIfrhG9BVFTctfaBgDS/l+bX1C56kSqv82 + bQkKSSAehSLGpFoCU3q62OGoZVi3jDe6HDb5M1Dp2mgHhqsW19otZpJ57DjtZ1CmtPai + o7T/ew6WoIYSl6whBmV36jeNaDJ3TItOBrKc4nMJBDWaCg4DNzUSe0ei5Xz7Oik5lb3p + y9ew== +X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=1e100.net; s=20210112; t=1679420066; + h=content-transfer-encoding:mime-version:references:in-reply-to + :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc + :subject:date:message-id:reply-to; + bh=asmFs22xWYwR1Ql9m/IrNv+MPUNDn8hSjmwDRYvO7mE=; + b=UdI2iQNBYwRf40ivf3ROR132t95BU/p3RUzXdZLCyz6c6JWtECQ5byyGeEwoX10n5u + HlepoNTJxMFLYrAHGvNLDPpWPuLXMa645S1mCVZ7NyWp8W96XzSynNZPeXHuJdb464QU + A7UTRSW3mlvKe9OR3EcB2CfBZv0yHWR0ldbnxcxGUFw8z78PNqpOVnITtjBdfpGesJ9c + VJw+fiM6hCcahor4nk9LLcAryPm8xmhDLxBKaLILO8wyTUiHY8G9hsXnFCtcpetnF5wS + pW13beAE+odb7ZZaXZUYpWGYhCe/hLzNjbo8YpgzHwadZthxPrT5YvNIYwyrvoViLM0n + KDRQ== +X-Gm-Message-State: AO0yUKW+9H/kqcAUyWeZhZJhiJjsBcYn1THmZaSDrPrk/pNuGXJXGtJd + NgsGZW8iSqLEv81yK+U5Os8= +X-Google-Smtp-Source: + AK7set/lzQZwCSxVaOe5dZ+7TR3xaQty/vg5xvZDpRW8TwTiPQblIbw5kJJTPLp67RySehrPIlCqSg== +X-Received: by 2002:a5d:65c9:0:b0:2ce:ac31:54ff with SMTP id + e9-20020a5d65c9000000b002ceac3154ffmr2776515wrw.2.1679420066191; + Tue, 21 Mar 2023 10:34:26 -0700 (PDT) +Received: from atlantis.lan (255.red-79-146-124.dynamicip.rima-tde.net. + [79.146.124.255]) + by smtp.gmail.com with ESMTPSA id + b13-20020a056000054d00b002da1261aa44sm184775wrf.48.2023.03.21.10.34.24 + (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); + Tue, 21 Mar 2023 10:34:25 -0700 (PDT) +From: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= +To: f.fainelli@gmail.com, jonas.gorski@gmail.com, andrew@lunn.ch, + olteanv@gmail.com, davem@davemloft.net, edumazet@google.com, + kuba@kernel.org, pabeni@redhat.com, robh+dt@kernel.org, + krzysztof.kozlowski+dt@linaro.org, netdev@vger.kernel.org, + devicetree@vger.kernel.org, linux-kernel@vger.kernel.org +Cc: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= , + Simon Horman +Subject: [PATCH v2 4/4] net: dsa: b53: add BCM63268 RGMII configuration +Date: Tue, 21 Mar 2023 18:33:59 +0100 +Message-Id: <20230321173359.251778-5-noltari@gmail.com> +X-Mailer: git-send-email 2.30.2 +In-Reply-To: <20230321173359.251778-1-noltari@gmail.com> +References: <20230320155024.164523-1-noltari@gmail.com> + <20230321173359.251778-1-noltari@gmail.com> +MIME-Version: 1.0 +Precedence: bulk +List-ID: +X-Mailing-List: netdev@vger.kernel.org +X-Patchwork-Delegate: kuba@kernel.org + +BCM63268 requires special RGMII configuration to work. + +Signed-off-by: Álvaro Fernández Rojas +Reviewed-by: Florian Fainelli +Reviewed-by: Simon Horman +--- + v2: no changes. + + drivers/net/dsa/b53/b53_common.c | 6 +++++- + drivers/net/dsa/b53/b53_regs.h | 1 + + 2 files changed, 6 insertions(+), 1 deletion(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -1240,8 +1240,12 @@ static void b53_adjust_63xx_rgmii(struct + break; + } + +- if (port != dev->imp_port) ++ if (port != dev->imp_port) { ++ if (is63268(dev)) ++ rgmii_ctrl |= RGMII_CTRL_MII_OVERRIDE; ++ + rgmii_ctrl |= RGMII_CTRL_ENABLE_GMII; ++ } + + b53_write8(dev, B53_CTRL_PAGE, off, rgmii_ctrl); + +--- a/drivers/net/dsa/b53/b53_regs.h ++++ b/drivers/net/dsa/b53/b53_regs.h +@@ -138,6 +138,7 @@ + + #define B53_RGMII_CTRL_IMP 0x60 + #define RGMII_CTRL_ENABLE_GMII BIT(7) ++#define RGMII_CTRL_MII_OVERRIDE BIT(6) + #define RGMII_CTRL_TIMING_SEL BIT(2) + #define RGMII_CTRL_DLL_RXC BIT(1) + #define RGMII_CTRL_DLL_TXC BIT(0) diff --git a/target/linux/generic/pending-6.6/777-net-dsa-b53-mdio-add-support-for-BCM53134.patch b/target/linux/generic/pending-6.6/777-net-dsa-b53-mdio-add-support-for-BCM53134.patch new file mode 100644 index 0000000000..f0ae2defce --- /dev/null +++ b/target/linux/generic/pending-6.6/777-net-dsa-b53-mdio-add-support-for-BCM53134.patch @@ -0,0 +1,189 @@ +From patchwork Fri Mar 24 08:41:38 2023 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 8bit +X-Patchwork-Submitter: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= + +X-Patchwork-Id: 13186549 +X-Patchwork-Delegate: kuba@kernel.org +Return-Path: +X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on + aws-us-west-2-korg-lkml-1.web.codeaurora.org +Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) + by smtp.lore.kernel.org (Postfix) with ESMTP id EF744C76195 + for ; Fri, 24 Mar 2023 08:42:01 +0000 (UTC) +Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand + id S231807AbjCXImA (ORCPT ); + Fri, 24 Mar 2023 04:42:00 -0400 +Received: from lindbergh.monkeyblade.net ([23.128.96.19]:32956 "EHLO + lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org + with ESMTP id S231272AbjCXIly (ORCPT + ); Fri, 24 Mar 2023 04:41:54 -0400 +Received: from mail-ed1-x535.google.com (mail-ed1-x535.google.com + [IPv6:2a00:1450:4864:20::535]) + by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 113A517CF3; + Fri, 24 Mar 2023 01:41:46 -0700 (PDT) +Received: by mail-ed1-x535.google.com with SMTP id ek18so4877175edb.6; + Fri, 24 Mar 2023 01:41:45 -0700 (PDT) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=gmail.com; s=20210112; t=1679647304; + h=content-transfer-encoding:mime-version:references:in-reply-to + :message-id:date:subject:cc:to:from:from:to:cc:subject:date + :message-id:reply-to; + bh=OfUWRaFIQIQw/lRivER+LHryfdLliXzvabGrcmkQVEU=; + b=JMrl6Eay1FS0JZqgPHsbcVzuNAbFELc0SLNGyzYtOVQXcI+YwKDM9Ls7I9PsQVEPoZ + CthomCTYoz5G9DU7uBuia207rnjOhssZJRu0syrCoU+O/ZiQyGLJDvq61z5oZJxC2S40 + kzRsUsC6MRjn64DKPWmxhsSTMKLzn2+P233LKNFbHtfi3NWF5Qu/85sUkxMurnfUgja0 + qQhl25qYY7ZIvmlFHYefaI5UkITQFuiybrqJW9tztCdHf/gS+f33YkkvQ8njmMQa1DW0 + ppedfOUotX+6kmHZGX1yea2V5ezEGGvRourZtYMoecTiD0E5d1J1bKhktKslVLIDm0ig + oc2g== +X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=1e100.net; s=20210112; t=1679647304; + h=content-transfer-encoding:mime-version:references:in-reply-to + :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc + :subject:date:message-id:reply-to; + bh=OfUWRaFIQIQw/lRivER+LHryfdLliXzvabGrcmkQVEU=; + b=b3Gmga5ZDbnmQfnw1GCz+eU2JwgsVzfciZuSmfYAiVxpW4c6cur3MHbpzDPhi99wzA + ZYAM7ryLv88rXl/tQB5g2Nte5rvMfxUeHXsT/JpsRcSSocFRbRrk0QJyiA/Xj86NiD5N + C1sKz50Im190FmrvPcBh6OHQbv/3MQyE+1fQx+9q3jW5rQiAWQaYk4Ng8GlWA7gtG3jB + fHO6Fuoenn32pgkveJbQLYL/2t2f53wGf3QLQ3IeKW7jdfIHNThwrwqBMxdHoIDaTBT9 + UWMeJuiYtylIibo/3zbORbWOgIERlWxZRf3BCOFpnzUn4eBzio4LgjtNxZ77ITRxsmbk + 3+Hg== +X-Gm-Message-State: AAQBX9dfyBfbR7Sdd5wqxMiAv3Yhk47pK1XzD87MZyAF3AxyoFyKcMaF + EbwJLyRvTGQEFdVWCGw1eMU= +X-Google-Smtp-Source: + AKy350bpDVLq7k1FxG2Mek/VIobZL4KhufiKx8qfmpxpcWmLI3bLg8wfQKEAKJRNJBleo/7CZuCL5g== +X-Received: by 2002:aa7:c711:0:b0:4a2:588f:b3c5 with SMTP id + i17-20020aa7c711000000b004a2588fb3c5mr2261236edq.21.1679647304260; + Fri, 24 Mar 2023 01:41:44 -0700 (PDT) +Received: from atlantis.lan (255.red-79-146-124.dynamicip.rima-tde.net. + [79.146.124.255]) + by smtp.gmail.com with ESMTPSA id + z21-20020a50cd15000000b004acbda55f6bsm10323728edi.27.2023.03.24.01.41.43 + (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); + Fri, 24 Mar 2023 01:41:43 -0700 (PDT) +From: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= +To: paul.geurts@prodrive-technologies.com, f.fainelli@gmail.com, + jonas.gorski@gmail.com, andrew@lunn.ch, olteanv@gmail.com, + davem@davemloft.net, edumazet@google.com, kuba@kernel.org, + pabeni@redhat.com, robh+dt@kernel.org, + krzysztof.kozlowski+dt@linaro.org, netdev@vger.kernel.org, + devicetree@vger.kernel.org, linux-kernel@vger.kernel.org +Cc: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= +Subject: [PATCH v2 2/2] net: dsa: b53: mdio: add support for BCM53134 +Date: Fri, 24 Mar 2023 09:41:38 +0100 +Message-Id: <20230324084138.664285-3-noltari@gmail.com> +X-Mailer: git-send-email 2.30.2 +In-Reply-To: <20230324084138.664285-1-noltari@gmail.com> +References: <20230323121804.2249605-1-noltari@gmail.com> + <20230324084138.664285-1-noltari@gmail.com> +MIME-Version: 1.0 +Precedence: bulk +List-ID: +X-Mailing-List: netdev@vger.kernel.org +X-Patchwork-Delegate: kuba@kernel.org + +From: Paul Geurts + +Add support for the BCM53134 Ethernet switch in the existing b53 dsa driver. +BCM53134 is very similar to the BCM58XX series. + +Signed-off-by: Paul Geurts +Signed-off-by: Álvaro Fernández Rojas +--- + v2: add BCM53134 to is531x5() and remove special RGMII config + + drivers/net/dsa/b53/b53_common.c | 15 +++++++++++++++ + drivers/net/dsa/b53/b53_mdio.c | 5 ++++- + drivers/net/dsa/b53/b53_priv.h | 7 +++++-- + 3 files changed, 24 insertions(+), 3 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -2613,6 +2613,20 @@ static const struct b53_chip_data b53_sw + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, + }, ++ { ++ .chip_id = BCM53134_DEVICE_ID, ++ .dev_name = "BCM53134", ++ .vlans = 4096, ++ .enabled_ports = 0x12f, ++ .imp_port = 8, ++ .cpu_port = B53_CPU_PORT, ++ .vta_regs = B53_VTA_REGS, ++ .arl_bins = 4, ++ .arl_buckets = 1024, ++ .duplex_reg = B53_DUPLEX_STAT_GE, ++ .jumbo_pm_reg = B53_JUMBO_PORT_MASK, ++ .jumbo_size_reg = B53_JUMBO_MAX_SIZE, ++ }, + }; + + static int b53_switch_init(struct b53_device *dev) +@@ -2790,6 +2804,7 @@ int b53_switch_detect(struct b53_device + case BCM53012_DEVICE_ID: + case BCM53018_DEVICE_ID: + case BCM53019_DEVICE_ID: ++ case BCM53134_DEVICE_ID: + dev->chip_id = id32; + break; + default: +--- a/drivers/net/dsa/b53/b53_mdio.c ++++ b/drivers/net/dsa/b53/b53_mdio.c +@@ -286,6 +286,7 @@ static const struct b53_io_ops b53_mdio_ + #define B53_BRCM_OUI_2 0x03625c00 + #define B53_BRCM_OUI_3 0x00406000 + #define B53_BRCM_OUI_4 0x01410c00 ++#define B53_BRCM_OUI_5 0xae025000 + + static int b53_mdio_probe(struct mdio_device *mdiodev) + { +@@ -313,7 +314,8 @@ static int b53_mdio_probe(struct mdio_de + if ((phy_id & 0xfffffc00) != B53_BRCM_OUI_1 && + (phy_id & 0xfffffc00) != B53_BRCM_OUI_2 && + (phy_id & 0xfffffc00) != B53_BRCM_OUI_3 && +- (phy_id & 0xfffffc00) != B53_BRCM_OUI_4) { ++ (phy_id & 0xfffffc00) != B53_BRCM_OUI_4 && ++ (phy_id & 0xfffffc00) != B53_BRCM_OUI_5) { + dev_err(&mdiodev->dev, "Unsupported device: 0x%08x\n", phy_id); + return -ENODEV; + } +@@ -375,6 +377,7 @@ static const struct of_device_id b53_of_ + { .compatible = "brcm,bcm53115" }, + { .compatible = "brcm,bcm53125" }, + { .compatible = "brcm,bcm53128" }, ++ { .compatible = "brcm,bcm53134" }, + { .compatible = "brcm,bcm5365" }, + { .compatible = "brcm,bcm5389" }, + { .compatible = "brcm,bcm5395" }, +--- a/drivers/net/dsa/b53/b53_priv.h ++++ b/drivers/net/dsa/b53/b53_priv.h +@@ -80,6 +80,7 @@ enum { + BCM583XX_DEVICE_ID = 0x58300, + BCM7445_DEVICE_ID = 0x7445, + BCM7278_DEVICE_ID = 0x7278, ++ BCM53134_DEVICE_ID = 0x5075, + }; + + struct b53_pcs { +@@ -187,7 +188,8 @@ static inline int is531x5(struct b53_dev + { + return dev->chip_id == BCM53115_DEVICE_ID || + dev->chip_id == BCM53125_DEVICE_ID || +- dev->chip_id == BCM53128_DEVICE_ID; ++ dev->chip_id == BCM53128_DEVICE_ID || ++ dev->chip_id == BCM53134_DEVICE_ID; + } + + static inline int is63xx(struct b53_device *dev) +@@ -215,7 +217,8 @@ static inline int is58xx(struct b53_devi + return dev->chip_id == BCM58XX_DEVICE_ID || + dev->chip_id == BCM583XX_DEVICE_ID || + dev->chip_id == BCM7445_DEVICE_ID || +- dev->chip_id == BCM7278_DEVICE_ID; ++ dev->chip_id == BCM7278_DEVICE_ID || ++ dev->chip_id == BCM53134_DEVICE_ID; + } + + #define B53_63XX_RGMII0 4 diff --git a/target/linux/generic/pending-6.6/778-net-l2tp-drop-flow-hash-on-forward.patch b/target/linux/generic/pending-6.6/778-net-l2tp-drop-flow-hash-on-forward.patch new file mode 100644 index 0000000000..a2c0edcbbf --- /dev/null +++ b/target/linux/generic/pending-6.6/778-net-l2tp-drop-flow-hash-on-forward.patch @@ -0,0 +1,31 @@ +From 4a44a52f16ccd3d03e0cb5fb437a5eb31a5f9f05 Mon Sep 17 00:00:00 2001 +From: David Bauer +Date: Mon, 26 Feb 2024 21:39:34 +0100 +Subject: [PATCH] net l2tp: drop flow hash on forward + +Drop the flow-hash of the skb when forwarding to the L2TP netdev. + +This avoids the L2TP qdisc from using the flow-hash from the outer +packet, which is identical for every flow within the tunnel. + +This does not affect every platform but is specific for the ethernet +driver. It depends on the platform including L4 information in the +flow-hash. + +Signed-off-by: David Bauer +--- + net/l2tp/l2tp_eth.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/net/l2tp/l2tp_eth.c ++++ b/net/l2tp/l2tp_eth.c +@@ -136,6 +136,9 @@ static void l2tp_eth_dev_recv(struct l2t + /* checksums verified by L2TP */ + skb->ip_summed = CHECKSUM_NONE; + ++ /* drop outer flow-hash */ ++ skb_clear_hash(skb); ++ + skb_dst_drop(skb); + nf_reset_ct(skb); + diff --git a/target/linux/generic/pending-6.6/779-net-vxlan-don-t-learn-non-unicast-L2-destinations.patch b/target/linux/generic/pending-6.6/779-net-vxlan-don-t-learn-non-unicast-L2-destinations.patch new file mode 100644 index 0000000000..6c1f596759 --- /dev/null +++ b/target/linux/generic/pending-6.6/779-net-vxlan-don-t-learn-non-unicast-L2-destinations.patch @@ -0,0 +1,30 @@ +From 3f1a227cb071f65f6ecc4db9f399649869735a7c Mon Sep 17 00:00:00 2001 +From: David Bauer +Date: Sat, 17 Feb 2024 22:34:59 +0100 +Subject: [PATCH] net vxlan: don't learn non-unicast L2 destinations + +This patch avoids learning non-unicast targets in the vxlan FDB. +They are non-unicast and thus should be sent to the broadcast-IPv6 +instead of a unicast address. + +Link: https://lore.kernel.org/netdev/15ee0cc7-9252-466b-8ce7-5225d605dde8@david-bauer.net/ +Link: https://github.com/freifunk-gluon/gluon/issues/3191 + +Signed-off-by: David Bauer +--- + drivers/net/vxlan.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/net/vxlan/vxlan_core.c ++++ b/drivers/net/vxlan/vxlan_core.c +@@ -1493,6 +1493,10 @@ static bool vxlan_snoop(struct net_devic + struct vxlan_fdb *f; + u32 ifindex = 0; + ++ /* Don't learn broadcast packets */ ++ if (is_multicast_ether_addr(src_mac) || is_zero_ether_addr(src_mac)) ++ return false; ++ + #if IS_ENABLED(CONFIG_IPV6) + if (src_ip->sa.sa_family == AF_INET6 && + (ipv6_addr_type(&src_ip->sin6.sin6_addr) & IPV6_ADDR_LINKLOCAL)) diff --git a/target/linux/generic/pending-6.6/780-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch b/target/linux/generic/pending-6.6/780-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch new file mode 100644 index 0000000000..39ba71606e --- /dev/null +++ b/target/linux/generic/pending-6.6/780-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch @@ -0,0 +1,61 @@ +From patchwork Thu Aug 5 22:23:30 2021 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Daniel Golle +X-Patchwork-Id: 12422209 +Date: Thu, 5 Aug 2021 23:23:30 +0100 +From: Daniel Golle +To: linux-arm-kernel@lists.infradead.org, netdev@vger.kernel.org, + linux-kernel@vger.kernel.org +Cc: "David S. Miller" , Andrew Lunn , + Michael Walle +Subject: [PATCH] ARM: kirkwood: add missing for ETH_ALEN +Message-ID: +MIME-Version: 1.0 +Content-Disposition: inline +X-BeenThere: linux-arm-kernel@lists.infradead.org +X-Mailman-Version: 2.1.34 +Precedence: list +List-Id: +List-Archive: +Sender: "linux-arm-kernel" + +After commit 83216e3988cd1 ("of: net: pass the dst buffer to +of_get_mac_address()") build fails for kirkwood as ETH_ALEN is not +defined. + +arch/arm/mach-mvebu/kirkwood.c: In function 'kirkwood_dt_eth_fixup': +arch/arm/mach-mvebu/kirkwood.c:87:13: error: 'ETH_ALEN' undeclared (first use in this function); did you mean 'ESTALE'? + u8 tmpmac[ETH_ALEN]; + ^~~~~~~~ + ESTALE +arch/arm/mach-mvebu/kirkwood.c:87:13: note: each undeclared identifier is reported only once for each function it appears in +arch/arm/mach-mvebu/kirkwood.c:87:6: warning: unused variable 'tmpmac' [-Wunused-variable] + u8 tmpmac[ETH_ALEN]; + ^~~~~~ +make[5]: *** [scripts/Makefile.build:262: arch/arm/mach-mvebu/kirkwood.o] Error 1 +make[5]: *** Waiting for unfinished jobs.... + +Add missing #include to fix this. + +Cc: David S. Miller +Cc: Andrew Lunn +Cc: Michael Walle +Reported-by: https://buildbot.openwrt.org/master/images/#/builders/56/builds/220/steps/44/logs/stdio +Fixes: 83216e3988cd1 ("of: net: pass the dst buffer to of_get_mac_address()") +Signed-off-by: Daniel Golle +--- + arch/arm/mach-mvebu/kirkwood.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/arch/arm/mach-mvebu/kirkwood.c ++++ b/arch/arm/mach-mvebu/kirkwood.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + #include + #include diff --git a/target/linux/generic/pending-6.6/790-bus-mhi-core-add-SBL-state-callback.patch b/target/linux/generic/pending-6.6/790-bus-mhi-core-add-SBL-state-callback.patch new file mode 100644 index 0000000000..f239355594 --- /dev/null +++ b/target/linux/generic/pending-6.6/790-bus-mhi-core-add-SBL-state-callback.patch @@ -0,0 +1,48 @@ +From 5f7c5e1c0d7a79be144e5efc1f24728ddd7fc25c Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Sat, 5 Nov 2022 20:02:56 +0100 +Subject: [PATCH 1/2] bus: mhi: core: add SBL state callback + +Add support for SBL state callback in MHI core. + +It is required for ath11k MHI devices in order to be able to set QRTR +instance ID in the SBL state so that QRTR instance ID-s dont conflict in +case of multiple PCI/MHI cards or AHB + PCI/MHI card. +Setting QRTR instance ID is only possible in SBL state and there is +currently no way to ensure that we are in that state, so provide a +callback that the controller can trigger off. + +Signed-off-by: Robert Marko +--- + drivers/bus/mhi/host/main.c | 1 + + include/linux/mhi.h | 2 ++ + 2 files changed, 3 insertions(+) + +--- a/drivers/bus/mhi/host/main.c ++++ b/drivers/bus/mhi/host/main.c +@@ -905,6 +905,7 @@ int mhi_process_ctrl_ev_ring(struct mhi_ + switch (event) { + case MHI_EE_SBL: + st = DEV_ST_TRANSITION_SBL; ++ mhi_cntrl->status_cb(mhi_cntrl, MHI_CB_EE_SBL_MODE); + break; + case MHI_EE_WFW: + case MHI_EE_AMSS: +--- a/include/linux/mhi.h ++++ b/include/linux/mhi.h +@@ -34,6 +34,7 @@ struct mhi_buf_info; + * @MHI_CB_SYS_ERROR: MHI device entered error state (may recover) + * @MHI_CB_FATAL_ERROR: MHI device entered fatal error state + * @MHI_CB_BW_REQ: Received a bandwidth switch request from device ++ * @MHI_CB_EE_SBL_MODE: MHI device entered SBL mode + */ + enum mhi_callback { + MHI_CB_IDLE, +@@ -45,6 +46,7 @@ enum mhi_callback { + MHI_CB_SYS_ERROR, + MHI_CB_FATAL_ERROR, + MHI_CB_BW_REQ, ++ MHI_CB_EE_SBL_MODE, + }; + + /** diff --git a/target/linux/generic/pending-6.6/795-mt7530-register-OF-node-for-internal-MDIO-bus.patch b/target/linux/generic/pending-6.6/795-mt7530-register-OF-node-for-internal-MDIO-bus.patch new file mode 100644 index 0000000000..d333f3f489 --- /dev/null +++ b/target/linux/generic/pending-6.6/795-mt7530-register-OF-node-for-internal-MDIO-bus.patch @@ -0,0 +1,43 @@ +From 1d81e51d6d79d9098013b2e8cdd677bae998c5d8 Mon Sep 17 00:00:00 2001 +From: David Bauer +Date: Fri, 28 Apr 2023 02:22:59 +0200 +Subject: [PATCH 1/2] mt7530: register OF node for internal MDIO bus + +The MT753x switches provide a switch-internal MDIO bus for the embedded +PHYs. + +Register a OF sub-node on the switch OF-node for this internal MDIO bus. +This allows to configure the embedded PHYs using device-tree. + +Signed-off-by: David Bauer +--- + drivers/net/dsa/mt7530.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2142,10 +2142,13 @@ mt7530_setup_mdio(struct mt7530_priv *pr + { + struct dsa_switch *ds = priv->ds; + struct device *dev = priv->dev; ++ struct device_node *np, *mnp; + struct mii_bus *bus; + static int idx; + int ret; + ++ np = priv->dev->of_node; ++ + bus = devm_mdiobus_alloc(dev); + if (!bus) + return -ENOMEM; +@@ -2162,7 +2165,9 @@ mt7530_setup_mdio(struct mt7530_priv *pr + if (priv->irq) + mt7530_setup_mdio_irq(priv); + +- ret = devm_mdiobus_register(dev, bus); ++ mnp = of_get_child_by_name(np, "mdio"); ++ ret = devm_of_mdiobus_register(dev, bus, mnp); ++ of_node_put(mnp); + if (ret) { + dev_err(dev, "failed to register MDIO bus: %d\n", ret); + if (priv->irq) diff --git a/target/linux/generic/pending-6.6/796-net-dsa-mt7530-fix-10M-100M-speed-on-MT7988-switch.patch b/target/linux/generic/pending-6.6/796-net-dsa-mt7530-fix-10M-100M-speed-on-MT7988-switch.patch new file mode 100644 index 0000000000..47e3b140c8 --- /dev/null +++ b/target/linux/generic/pending-6.6/796-net-dsa-mt7530-fix-10M-100M-speed-on-MT7988-switch.patch @@ -0,0 +1,45 @@ +From a444877c10a665cd8a869e6d37facdb89fd95f79 Mon Sep 17 00:00:00 2001 +Message-ID: +From: Daniel Golle +Date: Wed, 24 Jan 2024 04:17:11 +0000 +Subject: [PATCH net] net: dsa: mt7530: fix 10M/100M speed on MT7988 switch +To: Arınç ÜNAL , + Daniel Golle , + DENG Qingfang , + Sean Wang , + Andrew Lunn , + Florian Fainelli , + Vladimir Oltean , + David S. Miller , + Eric Dumazet , + Jakub Kicinski , + Paolo Abeni , + Matthias Brugger , + AngeloGioacchino Del Regno , + netdev@vger.kernel.org, + linux-kernel@vger.kernel.org, + linux-arm-kernel@lists.infradead.org, + linux-mediatek@lists.infradead.org + +Setup PMCR port register for actual speed and duplex on internally +connected PHYs of the MT7988 built-in switch. This fixes links with +speeds other than 1000M. + +Fixes: ("110c18bfed414 net: dsa: mt7530: introduce driver for MT7988 built-in switch") +Signed-off-by: Daniel Golle +--- + drivers/net/dsa/mt7530.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2849,8 +2849,7 @@ 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_INTERNAL || +- interface == PHY_INTERFACE_MODE_TRGMII || ++ if (interface == PHY_INTERFACE_MODE_TRGMII || + (phy_interface_mode_is_8023z(interface))) { + speed = SPEED_1000; + duplex = DUPLEX_FULL; diff --git a/target/linux/generic/pending-6.6/800-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch b/target/linux/generic/pending-6.6/800-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch new file mode 100644 index 0000000000..478a2cb27d --- /dev/null +++ b/target/linux/generic/pending-6.6/800-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch @@ -0,0 +1,73 @@ +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Subject: [PATCH] bcma: get SoC device struct & copy its DMA params to the + subdevices +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +For bus devices to be fully usable it's required to set their DMA +parameters. + +For years it has been missing and remained unnoticed because of +mips_dma_alloc_coherent() silently handling the empty coherent_dma_mask. +Kernel 4.19 came with a lot of DMA changes and caused a regression on +the bcm47xx. Starting with the commit f8c55dc6e828 ("MIPS: use generic +dma noncoherent ops for simple noncoherent platforms") DMA coherent +allocations just fail. Example: +[ 1.114914] bgmac_bcma bcma0:2: Allocation of TX ring 0x200 failed +[ 1.121215] bgmac_bcma bcma0:2: Unable to alloc memory for DMA +[ 1.127626] bgmac_bcma: probe of bcma0:2 failed with error -12 +[ 1.133838] bgmac_bcma: Broadcom 47xx GBit MAC driver loaded + +This change fixes above regression in addition to the MIPS bcm47xx +commit 321c46b91550 ("MIPS: BCM47XX: Setup struct device for the SoC"). + +It also fixes another *old* GPIO regression caused by a parent pointing +to the NULL: +[ 0.157054] missing gpiochip .dev parent pointer +[ 0.157287] bcma: bus0: Error registering GPIO driver: -22 +introduced by the commit 74f4e0cc6108 ("bcma: switch GPIO portions to +use GPIOLIB_IRQCHIP"). + +Fixes: f8c55dc6e828 ("MIPS: use generic dma noncoherent ops for simple noncoherent platforms") +Fixes: 74f4e0cc6108 ("bcma: switch GPIO portions to use GPIOLIB_IRQCHIP") +Cc: linux-mips@linux-mips.org +Cc: Christoph Hellwig +Cc: Linus Walleij +Signed-off-by: Rafał Miłecki +--- + +--- a/drivers/bcma/host_soc.c ++++ b/drivers/bcma/host_soc.c +@@ -191,6 +191,8 @@ int __init bcma_host_soc_init(struct bcm + struct bcma_bus *bus = &soc->bus; + int err; + ++ bus->dev = soc->dev; ++ + /* Scan bus and initialize it */ + err = bcma_bus_early_register(bus); + if (err) +--- a/drivers/bcma/main.c ++++ b/drivers/bcma/main.c +@@ -236,13 +236,17 @@ EXPORT_SYMBOL(bcma_core_irq); + + void bcma_prepare_core(struct bcma_bus *bus, struct bcma_device *core) + { +- device_initialize(&core->dev); ++ struct device *dev = &core->dev; ++ ++ device_initialize(dev); + core->dev.release = bcma_release_core_dev; + core->dev.bus = &bcma_bus_type; +- dev_set_name(&core->dev, "bcma%d:%d", bus->num, core->core_index); ++ dev_set_name(dev, "bcma%d:%d", bus->num, core->core_index); + core->dev.parent = bus->dev; +- if (bus->dev) ++ if (bus->dev) { + bcma_of_fill_device(bus->dev, core); ++ dma_coerce_mask_and_coherent(dev, bus->dev->coherent_dma_mask); ++ } + + switch (bus->hosttype) { + case BCMA_HOSTTYPE_PCI: diff --git a/target/linux/generic/pending-6.6/801-gpio-gpio-cascade-add-generic-GPIO-cascade.patch b/target/linux/generic/pending-6.6/801-gpio-gpio-cascade-add-generic-GPIO-cascade.patch new file mode 100644 index 0000000000..a13d405e64 --- /dev/null +++ b/target/linux/generic/pending-6.6/801-gpio-gpio-cascade-add-generic-GPIO-cascade.patch @@ -0,0 +1,222 @@ +From fc23ea48ba52c24f201fe5ca0132ee1a3de5a70a Mon Sep 17 00:00:00 2001 +From: Mauri Sandberg +Date: Thu, 25 Mar 2021 11:48:05 +0200 +Subject: [PATCH 2/2] gpio: gpio-cascade: add generic GPIO cascade + +Adds support for building cascades of GPIO lines. That is, it allows +setups when there is one upstream line and multiple cascaded lines, out +of which one can be chosen at a time. The status of the upstream line +can be conveyed to the selected cascaded line or, vice versa, the status +of the cascaded line can be conveyed to the upstream line. + +A multiplexer is being used to select, which cascaded GPIO line is being +used at any given time. + +At the moment only input direction is supported. In future it should be +possible to add support for output direction, too. + +Signed-off-by: Mauri Sandberg +Reviewed-by: Linus Walleij +Reviewed-by: Andy Shevchenko +--- +v7 -> v8: + - rearrange members in struct gpio_cascade + - cosmetic changes in file header and in one function declaration + - added Reviewed-by tags by Linus and Andy +v6 -> v7: + - In Kconfig add info about module name + - adhere to new convention that allows lines longer than 80 chars + - use dev_probe_err with upstream gpio line too + - refactor for cleaner exit of probe function. +v5 -> v6: + - In Kconfig, remove dependency to OF_GPIO and select only MULTIPLEXER + - refactor code preferring one-liners + - clean up prints, removing them from success-path. + - don't explicitly set gpio_chip.of_node as it's done in the GPIO library + - use devm_gpiochip_add_data instead of gpiochip_add +v4 -> v5: + - renamed gpio-mux-input -> gpio-cascade. refactored code accordingly + here and there and changed to use new bindings and compatible string + - ambigious and vague 'pin' was rename to 'upstream_line' + - dropped Tested-by and Reviewed-by due to changes in bindings + - dropped Reported-by suggested by an automatic bot as it was not really + appropriate to begin with + - functionally it's the same as v4 +v3 -> v4: + - Changed author email + - Included Tested-by and Reviewed-by from Drew +v2 -> v3: + - use managed device resources + - update Kconfig description +v1 -> v2: + - removed .owner from platform_driver as per test bot's instruction + - added MODULE_AUTHOR, MODULE_DESCRIPTION, MODULE_LICENSE + - added gpio_mux_input_get_direction as it's recommended for all chips + - removed because this is input only chip: gpio_mux_input_set_value + - removed because they are not needed for input/output only chips: + gpio_mux_input_direction_input + gpio_mux_input_direction_output + - fixed typo in an error message + - added info message about successful registration + - removed can_sleep flag as this does not sleep while getting GPIO value + like I2C or SPI do + - Updated description in Kconfig +--- + drivers/gpio/Kconfig | 15 +++++ + drivers/gpio/Makefile | 1 + + drivers/gpio/gpio-cascade.c | 117 ++++++++++++++++++++++++++++++++++++ + 3 files changed, 133 insertions(+) + create mode 100644 drivers/gpio/gpio-cascade.c + +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -1711,4 +1711,19 @@ config GPIO_SIM + + endmenu + ++comment "Other GPIO expanders" ++ ++config GPIO_CASCADE ++ tristate "General GPIO cascade" ++ select MULTIPLEXER ++ help ++ Say yes here to enable support for generic GPIO cascade. ++ ++ This allows building one-to-many cascades of GPIO lines using ++ different types of multiplexers readily available. At the ++ moment only input lines are supported. ++ ++ To build the driver as a module choose 'm' and the resulting module ++ will be called 'gpio-cascade'. ++ + endif +--- a/drivers/gpio/Makefile ++++ b/drivers/gpio/Makefile +@@ -43,6 +43,7 @@ obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd + obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o + obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o + obj-$(CONFIG_GPIO_CADENCE) += gpio-cadence.o ++obj-$(CONFIG_GPIO_CASCADE) += gpio-cascade.o + obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o + obj-$(CONFIG_GPIO_SNPS_CREG) += gpio-creg-snps.o + obj-$(CONFIG_GPIO_CRYSTAL_COVE) += gpio-crystalcove.o +--- /dev/null ++++ b/drivers/gpio/gpio-cascade.c +@@ -0,0 +1,117 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * A generic GPIO cascade driver ++ * ++ * Copyright (C) 2021 Mauri Sandberg ++ * ++ * This allows building cascades of GPIO lines in a manner illustrated ++ * below: ++ * ++ * /|---- Cascaded GPIO line 0 ++ * Upstream | |---- Cascaded GPIO line 1 ++ * GPIO line ----+ | . ++ * | | . ++ * \|---- Cascaded GPIO line n ++ * ++ * A multiplexer is being used to select, which cascaded line is being ++ * addressed at any given time. ++ * ++ * At the moment only input mode is supported due to lack of means for ++ * testing output functionality. At least theoretically output should be ++ * possible with open drain constructions. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++struct gpio_cascade { ++ struct gpio_chip gpio_chip; ++ struct device *parent; ++ struct mux_control *mux_control; ++ struct gpio_desc *upstream_line; ++}; ++ ++static struct gpio_cascade *chip_to_cascade(struct gpio_chip *gc) ++{ ++ return container_of(gc, struct gpio_cascade, gpio_chip); ++} ++ ++static int gpio_cascade_get_direction(struct gpio_chip *gc, unsigned int offset) ++{ ++ return GPIO_LINE_DIRECTION_IN; ++} ++ ++static int gpio_cascade_get_value(struct gpio_chip *gc, unsigned int offset) ++{ ++ struct gpio_cascade *cas = chip_to_cascade(gc); ++ int ret; ++ ++ ret = mux_control_select(cas->mux_control, offset); ++ if (ret) ++ return ret; ++ ++ ret = gpiod_get_value(cas->upstream_line); ++ mux_control_deselect(cas->mux_control); ++ return ret; ++} ++ ++static int gpio_cascade_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct gpio_cascade *cas; ++ struct mux_control *mc; ++ struct gpio_desc *upstream; ++ struct gpio_chip *gc; ++ ++ cas = devm_kzalloc(dev, sizeof(*cas), GFP_KERNEL); ++ if (!cas) ++ return -ENOMEM; ++ ++ mc = devm_mux_control_get(dev, NULL); ++ if (IS_ERR(mc)) ++ return dev_err_probe(dev, PTR_ERR(mc), "unable to get mux-control\n"); ++ ++ cas->mux_control = mc; ++ upstream = devm_gpiod_get(dev, "upstream", GPIOD_IN); ++ if (IS_ERR(upstream)) ++ return dev_err_probe(dev, PTR_ERR(upstream), "unable to claim upstream GPIO line\n"); ++ ++ cas->upstream_line = upstream; ++ cas->parent = dev; ++ ++ gc = &cas->gpio_chip; ++ gc->get = gpio_cascade_get_value; ++ gc->get_direction = gpio_cascade_get_direction; ++ gc->base = -1; ++ gc->ngpio = mux_control_states(mc); ++ gc->label = dev_name(cas->parent); ++ gc->parent = cas->parent; ++ gc->owner = THIS_MODULE; ++ ++ platform_set_drvdata(pdev, cas); ++ return devm_gpiochip_add_data(dev, &cas->gpio_chip, NULL); ++} ++ ++static const struct of_device_id gpio_cascade_id[] = { ++ { .compatible = "gpio-cascade" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, gpio_cascade_id); ++ ++static struct platform_driver gpio_cascade_driver = { ++ .driver = { ++ .name = "gpio-cascade", ++ .of_match_table = gpio_cascade_id, ++ }, ++ .probe = gpio_cascade_probe, ++}; ++module_platform_driver(gpio_cascade_driver); ++ ++MODULE_AUTHOR("Mauri Sandberg "); ++MODULE_DESCRIPTION("Generic GPIO cascade"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/pending-6.6/802-OPP-Provide-old-opp-to-config_clks-on-_set_opp.patch b/target/linux/generic/pending-6.6/802-OPP-Provide-old-opp-to-config_clks-on-_set_opp.patch new file mode 100644 index 0000000000..2b3e4bbf71 --- /dev/null +++ b/target/linux/generic/pending-6.6/802-OPP-Provide-old-opp-to-config_clks-on-_set_opp.patch @@ -0,0 +1,108 @@ +From fd59b838dd90452f61a17dc9e5ff175205003068 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 15 Sep 2022 18:49:43 +0200 +Subject: [PATCH] OPP: Provide old opp to config_clks on _set_opp + +With the target opp, also pass the old opp to config_clks function. +This can be useful when a driver needs to take decision on what fequency +to set based on what is the current frequency without using a +clk_get_freq call. +Update the only user of custom config_clks (tegra30 devfreq driver) to +this new implementation. + +Signed-off-by: Christian Marangi +--- + drivers/devfreq/tegra30-devfreq.c | 5 +++-- + drivers/opp/core.c | 11 ++++++----- + include/linux/pm_opp.h | 11 ++++++----- + 3 files changed, 15 insertions(+), 12 deletions(-) + +--- a/drivers/devfreq/tegra30-devfreq.c ++++ b/drivers/devfreq/tegra30-devfreq.c +@@ -823,8 +823,9 @@ static int devm_tegra_devfreq_init_hw(st + + static int tegra_devfreq_config_clks_nop(struct device *dev, + struct opp_table *opp_table, +- struct dev_pm_opp *opp, void *data, +- bool scaling_down) ++ struct dev_pm_opp *old_opp, ++ struct dev_pm_opp *opp, ++ void *data, bool scaling_down) + { + /* We want to skip clk configuration via dev_pm_opp_set_opp() */ + return 0; +--- a/drivers/opp/core.c ++++ b/drivers/opp/core.c +@@ -816,7 +816,8 @@ static int _set_opp_voltage(struct devic + + static int + _opp_config_clk_single(struct device *dev, struct opp_table *opp_table, +- struct dev_pm_opp *opp, void *data, bool scaling_down) ++ struct dev_pm_opp *old_opp, struct dev_pm_opp *opp, ++ void *data, bool scaling_down) + { + unsigned long *target = data; + unsigned long freq; +@@ -848,8 +849,8 @@ _opp_config_clk_single(struct device *de + * the order in which they are present in the array while scaling up. + */ + int dev_pm_opp_config_clks_simple(struct device *dev, +- struct opp_table *opp_table, struct dev_pm_opp *opp, void *data, +- bool scaling_down) ++ struct opp_table *opp_table, struct dev_pm_opp *old_opp, ++ struct dev_pm_opp *opp, void *data, bool scaling_down) + { + int ret, i; + +@@ -1121,7 +1122,7 @@ static int _set_opp(struct device *dev, + } + + if (opp_table->config_clks) { +- ret = opp_table->config_clks(dev, opp_table, opp, clk_data, scaling_down); ++ ret = opp_table->config_clks(dev, opp_table, old_opp, opp, clk_data, scaling_down); + if (ret) + return ret; + } +@@ -1196,7 +1197,7 @@ int dev_pm_opp_set_rate(struct device *d + * equivalent to a clk_set_rate() + */ + if (!_get_opp_count(opp_table)) { +- ret = opp_table->config_clks(dev, opp_table, NULL, ++ ret = opp_table->config_clks(dev, opp_table, NULL, NULL, + &target_freq, false); + goto put_opp_table; + } +--- a/include/linux/pm_opp.h ++++ b/include/linux/pm_opp.h +@@ -61,7 +61,8 @@ typedef int (*config_regulators_t)(struc + struct dev_pm_opp *old_opp, struct dev_pm_opp *new_opp, + struct regulator **regulators, unsigned int count); + +-typedef int (*config_clks_t)(struct device *dev, struct opp_table *opp_table, ++typedef int (*config_clks_t)(struct device *dev, ++ struct opp_table *opp_table, struct dev_pm_opp *old_opp, + struct dev_pm_opp *opp, void *data, bool scaling_down); + + /** +@@ -160,8 +161,8 @@ int dev_pm_opp_set_config(struct device + int devm_pm_opp_set_config(struct device *dev, struct dev_pm_opp_config *config); + void dev_pm_opp_clear_config(int token); + int dev_pm_opp_config_clks_simple(struct device *dev, +- struct opp_table *opp_table, struct dev_pm_opp *opp, void *data, +- bool scaling_down); ++ struct opp_table *opp_table, struct dev_pm_opp *old_opp, ++ struct dev_pm_opp *opp, void *data, bool scaling_down); + + struct dev_pm_opp *dev_pm_opp_xlate_required_opp(struct opp_table *src_table, struct opp_table *dst_table, struct dev_pm_opp *src_opp); + int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate); +@@ -346,8 +347,8 @@ static inline int devm_pm_opp_set_config + static inline void dev_pm_opp_clear_config(int token) {} + + static inline int dev_pm_opp_config_clks_simple(struct device *dev, +- struct opp_table *opp_table, struct dev_pm_opp *opp, void *data, +- bool scaling_down) ++ struct opp_table *opp_table, struct dev_pm_opp *old_opp, ++ struct dev_pm_opp *opp, void *data, bool scaling_down) + { + return -EOPNOTSUPP; + } diff --git a/target/linux/generic/pending-6.6/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch b/target/linux/generic/pending-6.6/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch new file mode 100644 index 0000000000..d07447bcba --- /dev/null +++ b/target/linux/generic/pending-6.6/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch @@ -0,0 +1,47 @@ +From 0e71cac033bb7689c4dfa2e6814191337ef770f5 Mon Sep 17 00:00:00 2001 +From: INAGAKI Hiroshi +Date: Thu, 13 Oct 2022 00:51:33 +0900 +Subject: [PATCH] nvmem: u-boot-env: align endianness of crc32 values +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This patch fixes crc32 error on Big-Endianness system by conversion of +calculated crc32 value. + +Little-Endianness system: + + obtained crc32: Little +calculated crc32: Little + +Big-Endianness system: + + obtained crc32: Little +calculated crc32: Big + +log (APRESIA ApresiaLightGS120GT-SS, RTL8382M, Big-Endianness): + +[ 8.570000] u_boot_env 18001200.spi:flash@0:partitions:partition@c0000: Invalid calculated CRC32: 0x88cd6f09 (expected: 0x096fcd88) +[ 8.580000] u_boot_env: probe of 18001200.spi:flash@0:partitions:partition@c0000 failed with error -22 + +Fixes: f955dc1445069 ("nvmem: add driver handling U-Boot environment variables") + +Signed-off-by: INAGAKI Hiroshi +Acked-by: Rafał Miłecki +Tested-by: Christian Lamparter +Signed-off-by: Srinivas Kandagatla +--- + 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 +@@ -181,7 +181,7 @@ static int u_boot_env_parse(struct u_boo + crc32_data_len = dev_size - crc32_data_offset; + data_len = dev_size - data_offset; + +- calc = crc32(~0, buf + crc32_data_offset, crc32_data_len) ^ ~0L; ++ calc = le32_to_cpu((__le32)crc32(~0, buf + crc32_data_offset, crc32_data_len) ^ ~0L); + if (calc != crc32) { + dev_err(dev, "Invalid calculated CRC32: 0x%08x (expected: 0x%08x)\n", calc, crc32); + err = -EINVAL; diff --git a/target/linux/generic/pending-6.6/804-nvmem-core-support-mac-base-fixed-layout-cells.patch b/target/linux/generic/pending-6.6/804-nvmem-core-support-mac-base-fixed-layout-cells.patch new file mode 100644 index 0000000000..9bb94a28b5 --- /dev/null +++ b/target/linux/generic/pending-6.6/804-nvmem-core-support-mac-base-fixed-layout-cells.patch @@ -0,0 +1,124 @@ +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Thu, 13 Jul 2023 18:29:19 +0200 +Subject: [PATCH] nvmem: core: support "mac-base" fixed layout cells + +Fixed layout binding allows specifying "mac-base" NVMEM cells. It's used +for base MAC address (that can be used for calculating relative +addresses). It can be stored in a raw binary format or as an ASCII +string. +--- + +--- a/drivers/nvmem/Kconfig ++++ b/drivers/nvmem/Kconfig +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0-only + menuconfig NVMEM + bool "NVMEM Support" ++ select GENERIC_NET_UTILS + imply NVMEM_LAYOUTS + help + Support for NVMEM(Non Volatile Memory) devices like EEPROM, EFUSES... +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -7,9 +7,12 @@ + */ + + #include ++#include ++#include + #include + #include + #include ++#include + #include + #include + #include +@@ -780,6 +783,62 @@ static int nvmem_validate_keepouts(struc + return 0; + } + ++static int nvmem_mac_base_raw_read(void *context, const char *id, int index, unsigned int offset, ++ void *buf, size_t bytes) ++{ ++ if (WARN_ON(bytes != ETH_ALEN)) ++ return -EINVAL; ++ ++ if (index) ++ eth_addr_add(buf, index); ++ ++ return 0; ++} ++ ++static int nvmem_mac_base_ascii_read(void *context, const char *id, int index, unsigned int offset, ++ void *buf, size_t bytes) ++{ ++ u8 mac[ETH_ALEN]; ++ ++ if (WARN_ON(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 nvmem_mac_base_hex_read(void *context, const char *id, int index, unsigned int offset, ++ void *buf, size_t bytes) ++{ ++ u8 mac[ETH_ALEN], *hexstr; ++ int i; ++ ++ if (WARN_ON(bytes != 2 * ETH_ALEN)) ++ return -EINVAL; ++ ++ hexstr = (u8 *)buf; ++ for (i = 0; i < ETH_ALEN; i++) { ++ if (!isxdigit(hexstr[i * 2]) || !isxdigit(hexstr[i * 2 + 1])) ++ return -EINVAL; ++ ++ mac[i] = (hex_to_bin(hexstr[i * 2]) << 4) | hex_to_bin(hexstr[i * 2 + 1]); ++ } ++ ++ if (index) ++ eth_addr_add(mac, index); ++ ++ ether_addr_copy(buf, mac); ++ ++ return 0; ++} ++ + static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np) + { + struct device *dev = &nvmem->dev; +@@ -814,6 +873,25 @@ static int nvmem_add_cells_from_dt(struc + if (nvmem->fixup_dt_cell_info) + nvmem->fixup_dt_cell_info(nvmem, &info); + ++ if (of_device_is_compatible(np, "fixed-layout")) { ++ if (of_device_is_compatible(child, "mac-base")) { ++ if (info.bytes == ETH_ALEN) { ++ info.raw_len = info.bytes; ++ info.bytes = ETH_ALEN; ++ info.read_post_process = nvmem_mac_base_raw_read; ++ } else if (info.bytes == 2 * ETH_ALEN) { ++ info.raw_len = info.bytes; ++ info.bytes = ETH_ALEN; ++ info.read_post_process = nvmem_mac_base_hex_read; ++ } else if (info.bytes == 3 * ETH_ALEN - 1) { ++ info.raw_len = info.bytes; ++ info.bytes = ETH_ALEN; ++ info.read_post_process = nvmem_mac_base_ascii_read; ++ } ++ ++ } ++ } ++ + ret = nvmem_add_one_cell(nvmem, &info); + kfree(info.name); + if (ret) { diff --git a/target/linux/generic/pending-6.6/810-pci_disable_common_quirks.patch b/target/linux/generic/pending-6.6/810-pci_disable_common_quirks.patch new file mode 100644 index 0000000000..ba06196f7c --- /dev/null +++ b/target/linux/generic/pending-6.6/810-pci_disable_common_quirks.patch @@ -0,0 +1,62 @@ +From: Gabor Juhos +Subject: debloat: add kernel config option to disabling common PCI quirks + +Signed-off-by: Gabor Juhos +--- + drivers/pci/Kconfig | 6 ++++++ + drivers/pci/quirks.c | 6 ++++++ + 2 files changed, 12 insertions(+) + +--- a/drivers/pci/Kconfig ++++ b/drivers/pci/Kconfig +@@ -118,6 +118,13 @@ config XEN_PCIDEV_FRONTEND + The PCI device frontend driver allows the kernel to import arbitrary + PCI devices from a PCI backend to support PCI driver domains. + ++config PCI_DISABLE_COMMON_QUIRKS ++ bool "PCI disable common quirks" ++ depends on PCI ++ help ++ If you don't know what to do here, say N. ++ ++ + config PCI_ATS + bool + +--- a/drivers/pci/quirks.c ++++ b/drivers/pci/quirks.c +@@ -207,6 +207,7 @@ static void quirk_mmio_always_on(struct + DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_BRIDGE_HOST, 8, quirk_mmio_always_on); + ++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS + /* + * The Mellanox Tavor device gives false positive parity errors. Disable + * parity error reporting. +@@ -3393,6 +3394,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_I + DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65f9, quirk_intel_mc_errata); + DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65fa, quirk_intel_mc_errata); + ++#endif /* !CONFIG_PCI_DISABLE_COMMON_QUIRKS */ ++ + /* + * Ivytown NTB BAR sizes are misreported by the hardware due to an erratum. + * To work around this, query the size it should be configured to by the +@@ -3418,6 +3421,8 @@ static void quirk_intel_ntb(struct pci_d + DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e08, quirk_intel_ntb); + DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e0d, quirk_intel_ntb); + ++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS ++ + /* + * Some BIOS implementations leave the Intel GPU interrupts enabled, even + * though no one is handling them (e.g., if the i915 driver is never +@@ -3456,6 +3461,8 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_IN + DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq); + DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0152, disable_igfx_irq); + ++#endif /* !CONFIG_PCI_DISABLE_COMMON_QUIRKS */ ++ + /* + * PCI devices which are on Intel chips can skip the 10ms delay + * before entering D3 mode. diff --git a/target/linux/generic/pending-6.6/811-pci_disable_usb_common_quirks.patch b/target/linux/generic/pending-6.6/811-pci_disable_usb_common_quirks.patch new file mode 100644 index 0000000000..98ea4c06d9 --- /dev/null +++ b/target/linux/generic/pending-6.6/811-pci_disable_usb_common_quirks.patch @@ -0,0 +1,115 @@ +From: Felix Fietkau +Subject: debloat: disable common USB quirks + +Signed-off-by: Felix Fietkau +--- + drivers/usb/host/pci-quirks.c | 16 ++++++++++++++++ + drivers/usb/host/pci-quirks.h | 18 +++++++++++++++++- + include/linux/usb/hcd.h | 7 +++++++ + 3 files changed, 40 insertions(+), 1 deletion(-) + +--- a/drivers/usb/host/pci-quirks.c ++++ b/drivers/usb/host/pci-quirks.c +@@ -128,6 +128,8 @@ struct amd_chipset_type { + u8 rev; + }; + ++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS ++ + static struct amd_chipset_info { + struct pci_dev *nb_dev; + struct pci_dev *smbus_dev; +@@ -633,6 +635,10 @@ bool usb_amd_pt_check_port(struct device + } + EXPORT_SYMBOL_GPL(usb_amd_pt_check_port); + ++#endif /* CONFIG_PCI_DISABLE_COMMON_QUIRKS */ ++ ++#if IS_ENABLED(CONFIG_USB_UHCI_HCD) ++ + /* + * Make sure the controller is completely inactive, unable to + * generate interrupts or do DMA. +@@ -712,8 +718,17 @@ reset_needed: + uhci_reset_hc(pdev, base); + return 1; + } ++#else ++int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base) ++{ ++ return 0; ++} ++ ++#endif + EXPORT_SYMBOL_GPL(uhci_check_and_reset_hc); + ++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS ++ + static inline int io_type_enabled(struct pci_dev *pdev, unsigned int mask) + { + u16 cmd; +@@ -1285,3 +1300,4 @@ static void quirk_usb_early_handoff(stru + } + DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_SERIAL_USB, 8, quirk_usb_early_handoff); ++#endif +--- a/drivers/usb/host/pci-quirks.h ++++ b/drivers/usb/host/pci-quirks.h +@@ -5,6 +5,9 @@ + #ifdef CONFIG_USB_PCI + void uhci_reset_hc(struct pci_dev *pdev, unsigned long base); + int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base); ++#endif /* CONFIG_USB_PCI */ ++ ++#if defined(CONFIG_USB_PCI) && !defined(CONFIG_PCI_DISABLE_COMMON_QUIRKS) + int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *pdev); + bool usb_amd_hang_symptom_quirk(void); + bool usb_amd_prefetch_quirk(void); +@@ -19,6 +22,18 @@ void sb800_prefetch(struct device *dev, + bool usb_amd_pt_check_port(struct device *device, int port); + #else + struct pci_dev; ++static inline int usb_amd_quirk_pll_check(void) ++{ ++ return 0; ++} ++static inline bool usb_amd_hang_symptom_quirk(void) ++{ ++ return false; ++} ++static inline bool usb_amd_prefetch_quirk(void) ++{ ++ return false; ++} + static inline void usb_amd_quirk_pll_disable(void) {} + static inline void usb_amd_quirk_pll_enable(void) {} + static inline void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev) {} +@@ -29,6 +44,11 @@ static inline bool usb_amd_pt_check_port + { + return false; + } ++static inline void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev) {} ++static inline bool usb_xhci_needs_pci_reset(struct pci_dev *pdev) ++{ ++ return false; ++} + #endif /* CONFIG_USB_PCI */ + + #endif /* __LINUX_USB_PCI_QUIRKS_H */ +--- a/include/linux/usb/hcd.h ++++ b/include/linux/usb/hcd.h +@@ -483,7 +483,14 @@ extern int usb_hcd_pci_probe(struct pci_ + extern void usb_hcd_pci_remove(struct pci_dev *dev); + extern void usb_hcd_pci_shutdown(struct pci_dev *dev); + ++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS + extern int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *dev); ++#else ++static inline int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *dev) ++{ ++ return 0; ++} ++#endif + + #ifdef CONFIG_PM + extern const struct dev_pm_ops usb_hcd_pci_pm_ops; diff --git a/target/linux/generic/pending-6.6/820-w1-gpio-fix-problem-with-platfom-data-in-w1-gpio.patch b/target/linux/generic/pending-6.6/820-w1-gpio-fix-problem-with-platfom-data-in-w1-gpio.patch new file mode 100644 index 0000000000..33eb34c913 --- /dev/null +++ b/target/linux/generic/pending-6.6/820-w1-gpio-fix-problem-with-platfom-data-in-w1-gpio.patch @@ -0,0 +1,26 @@ +From d9c8bc8c1408f3e8529db6e4e04017b4c579c342 Mon Sep 17 00:00:00 2001 +From: Pawel Dembicki +Date: Sun, 18 Feb 2018 17:08:04 +0100 +Subject: [PATCH] w1: gpio: fix problem with platfom data in w1-gpio + +In devices, where fdt is used, is impossible to apply platform data +without proper fdt node. + +This patch allow to use platform data in devices with fdt. + +Signed-off-by: Pawel Dembicki +--- + drivers/w1/masters/w1-gpio.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +--- a/drivers/w1/masters/w1-gpio.c ++++ b/drivers/w1/masters/w1-gpio.c +@@ -76,7 +76,7 @@ static int w1_gpio_probe(struct platform + enum gpiod_flags gflags = GPIOD_OUT_LOW_OPEN_DRAIN; + int err; + +- if (of_have_populated_dt()) { ++ if (of_have_populated_dt() && !dev_get_platdata(&pdev->dev)) { + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; diff --git a/target/linux/generic/pending-6.6/834-ledtrig-libata.patch b/target/linux/generic/pending-6.6/834-ledtrig-libata.patch new file mode 100644 index 0000000000..050c3515f6 --- /dev/null +++ b/target/linux/generic/pending-6.6/834-ledtrig-libata.patch @@ -0,0 +1,149 @@ +From: Daniel Golle +Subject: libata: add ledtrig support + +This adds a LED trigger for each ATA port indicating disk activity. + +As this is needed only on specific platforms (NAS SoCs and such), +these platforms should define ARCH_WANTS_LIBATA_LEDS if there +are boards with LED(s) intended to indicate ATA disk activity and +need the OS to take care of that. +In that way, if not selected, LED trigger support not will be +included in libata-core and both, codepaths and structures remain +untouched. + +Signed-off-by: Daniel Golle +--- + drivers/ata/Kconfig | 16 ++++++++++++++++ + drivers/ata/libata-core.c | 41 +++++++++++++++++++++++++++++++++++++++++ + include/linux/libata.h | 9 +++++++++ + 3 files changed, 66 insertions(+) + +--- a/drivers/ata/Kconfig ++++ b/drivers/ata/Kconfig +@@ -67,6 +67,22 @@ config ATA_FORCE + + If unsure, say Y. + ++config ARCH_WANT_LIBATA_LEDS ++ bool ++ ++config ATA_LEDS ++ bool "support ATA port LED triggers" ++ depends on ARCH_WANT_LIBATA_LEDS ++ select NEW_LEDS ++ select LEDS_CLASS ++ select LEDS_TRIGGERS ++ default y ++ help ++ This option adds a LED trigger for each registered ATA port. ++ It is used to drive disk activity leds connected via GPIO. ++ ++ If unsure, say N. ++ + config ATA_ACPI + bool "ATA ACPI Support" + depends on ACPI +--- a/drivers/ata/libata-core.c ++++ b/drivers/ata/libata-core.c +@@ -663,6 +663,19 @@ u64 ata_tf_read_block(const struct ata_t + return block; + } + ++#ifdef CONFIG_ATA_LEDS ++#define LIBATA_BLINK_DELAY 20 /* ms */ ++static inline void ata_led_act(struct ata_port *ap) ++{ ++ unsigned long led_delay = LIBATA_BLINK_DELAY; ++ ++ if (unlikely(!ap->ledtrig)) ++ return; ++ ++ led_trigger_blink_oneshot(ap->ledtrig, &led_delay, &led_delay, 0); ++} ++#endif ++ + /** + * ata_build_rw_tf - Build ATA taskfile for given read/write request + * @qc: Metadata associated with the taskfile to build +@@ -4716,6 +4729,9 @@ void __ata_qc_complete(struct ata_queued + link->active_tag = ATA_TAG_POISON; + ap->nr_active_links--; + } ++#ifdef CONFIG_ATA_LEDS ++ ata_led_act(ap); ++#endif + + /* clear exclusive status */ + if (unlikely(qc->flags & ATA_QCFLAG_CLEAR_EXCL && +@@ -5438,6 +5454,9 @@ struct ata_port *ata_port_alloc(struct a + ap->stats.unhandled_irq = 1; + ap->stats.idle_irq = 1; + #endif ++#ifdef CONFIG_ATA_LEDS ++ ap->ledtrig = kzalloc(sizeof(struct led_trigger), GFP_KERNEL); ++#endif + ata_sff_port_init(ap); + + return ap; +@@ -5473,6 +5492,12 @@ static void ata_host_release(struct kref + + kfree(ap->pmp_link); + kfree(ap->slave_link); ++#ifdef CONFIG_ATA_LEDS ++ if (ap->ledtrig) { ++ led_trigger_unregister(ap->ledtrig); ++ kfree(ap->ledtrig); ++ }; ++#endif + kfree(ap); + host->ports[i] = NULL; + } +@@ -5875,7 +5900,23 @@ int ata_host_register(struct ata_host *h + host->ports[i]->print_id = atomic_inc_return(&ata_print_id); + host->ports[i]->local_port_no = i + 1; + } ++#ifdef CONFIG_ATA_LEDS ++ for (i = 0; i < host->n_ports; i++) { ++ if (unlikely(!host->ports[i]->ledtrig)) ++ continue; + ++ snprintf(host->ports[i]->ledtrig_name, ++ sizeof(host->ports[i]->ledtrig_name), "ata%u", ++ host->ports[i]->print_id); ++ ++ host->ports[i]->ledtrig->name = host->ports[i]->ledtrig_name; ++ ++ if (led_trigger_register(host->ports[i]->ledtrig)) { ++ kfree(host->ports[i]->ledtrig); ++ host->ports[i]->ledtrig = NULL; ++ } ++ } ++#endif + /* Create associated sysfs transport objects */ + for (i = 0; i < host->n_ports; i++) { + rc = ata_tport_add(host->dev,host->ports[i]); +--- a/include/linux/libata.h ++++ b/include/linux/libata.h +@@ -23,6 +23,9 @@ + #include + #include + #include ++#ifdef CONFIG_ATA_LEDS ++#include ++#endif + + /* + * Define if arch has non-standard setup. This is a _PCI_ standard +@@ -864,6 +867,12 @@ struct ata_port { + #ifdef CONFIG_ATA_ACPI + struct ata_acpi_gtm __acpi_init_gtm; /* use ata_acpi_init_gtm() */ + #endif ++ ++#ifdef CONFIG_ATA_LEDS ++ struct led_trigger *ledtrig; ++ char ledtrig_name[8]; ++#endif ++ + /* owned by EH */ + u8 sector_buf[ATA_SECT_SIZE] ____cacheline_aligned; + }; diff --git a/target/linux/generic/pending-6.6/840-hwrng-bcm2835-set-quality-to-1000.patch b/target/linux/generic/pending-6.6/840-hwrng-bcm2835-set-quality-to-1000.patch new file mode 100644 index 0000000000..5ca8933d6f --- /dev/null +++ b/target/linux/generic/pending-6.6/840-hwrng-bcm2835-set-quality-to-1000.patch @@ -0,0 +1,26 @@ +From d6988cf1d16faac56899918bb2b1be8d85155e3f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Sat, 20 Feb 2021 18:36:38 +0100 +Subject: [PATCH] hwrng: bcm2835: set quality to 1000 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This allows devices without a high precission timer to reduce boot from >100s +to <30s. + +Signed-off-by: Álvaro Fernández Rojas +--- + drivers/char/hw_random/bcm2835-rng.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/char/hw_random/bcm2835-rng.c ++++ b/drivers/char/hw_random/bcm2835-rng.c +@@ -170,6 +170,7 @@ static int bcm2835_rng_probe(struct plat + priv->rng.init = bcm2835_rng_init; + priv->rng.read = bcm2835_rng_read; + priv->rng.cleanup = bcm2835_rng_cleanup; ++ priv->rng.quality = 1000; + + if (dev_of_node(dev)) { + rng_id = of_match_node(bcm2835_rng_of_match, dev->of_node); diff --git a/target/linux/generic/pending-6.6/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch b/target/linux/generic/pending-6.6/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch new file mode 100644 index 0000000000..fc61ee202a --- /dev/null +++ b/target/linux/generic/pending-6.6/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch @@ -0,0 +1,102 @@ +From 663b9f99bb35dbc0c7b685f71ee3668a60d31320 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= +Date: Mon, 10 Jan 2022 02:02:00 +0100 +Subject: [PATCH] PCI: aardvark: Make main irq_chip structure a static driver + structure +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Marc Zyngier says [1] that we should use struct irq_chip as a global +static struct in the driver. Even though the structure currently +contains a dynamic member (parent_device), Marc says [2] that he plans +to kill it and make the structure completely static. + +We have already converted others irq_chip structures in this driver in +this way, but we omitted this one because the .name member is +dynamically created from device's name, and the name is displayed in +sysfs, so changing it would break sysfs ABI. + +The rationale for changing the name (to "advk-INT") in spite of sysfs +ABI, and thus allowing to convert to a static structure, is that after +the other changes we made in this series, the IRQ chip is basically +something different: it no logner generates ERR and PME interrupts (they +are generated by emulated bridge's rp_irq_chip). + +[1] https://lore.kernel.org/linux-pci/877dbcvngf.wl-maz@kernel.org/ +[2] https://lore.kernel.org/linux-pci/874k6gvkhz.wl-maz@kernel.org/ + +Signed-off-by: Marek Behún +--- + drivers/pci/controller/pci-aardvark.c | 25 +++++++------------------ + 1 file changed, 7 insertions(+), 18 deletions(-) + +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -277,7 +277,6 @@ struct advk_pcie { + u8 wins_count; + struct irq_domain *rp_irq_domain; + struct irq_domain *irq_domain; +- struct irq_chip irq_chip; + raw_spinlock_t irq_lock; + struct irq_domain *msi_domain; + struct irq_domain *msi_inner_domain; +@@ -1426,14 +1425,19 @@ static void advk_pcie_irq_unmask(struct + raw_spin_unlock_irqrestore(&pcie->irq_lock, flags); + } + ++static struct irq_chip advk_irq_chip = { ++ .name = "advk-INT", ++ .irq_mask = advk_pcie_irq_mask, ++ .irq_unmask = advk_pcie_irq_unmask, ++}; ++ + static int advk_pcie_irq_map(struct irq_domain *h, + unsigned int virq, irq_hw_number_t hwirq) + { + struct advk_pcie *pcie = h->host_data; + + irq_set_status_flags(virq, IRQ_LEVEL); +- irq_set_chip_and_handler(virq, &pcie->irq_chip, +- handle_level_irq); ++ irq_set_chip_and_handler(virq, &advk_irq_chip, handle_level_irq); + irq_set_chip_data(virq, pcie); + + return 0; +@@ -1492,7 +1496,6 @@ static int advk_pcie_init_irq_domain(str + struct device *dev = &pcie->pdev->dev; + struct device_node *node = dev->of_node; + struct device_node *pcie_intc_node; +- struct irq_chip *irq_chip; + int ret = 0; + + raw_spin_lock_init(&pcie->irq_lock); +@@ -1503,28 +1506,14 @@ static int advk_pcie_init_irq_domain(str + return -ENODEV; + } + +- irq_chip = &pcie->irq_chip; +- +- irq_chip->name = devm_kasprintf(dev, GFP_KERNEL, "%s-irq", +- dev_name(dev)); +- if (!irq_chip->name) { +- ret = -ENOMEM; +- goto out_put_node; +- } +- +- irq_chip->irq_mask = advk_pcie_irq_mask; +- irq_chip->irq_unmask = advk_pcie_irq_unmask; +- + pcie->irq_domain = + irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, + &advk_pcie_irq_domain_ops, pcie); + if (!pcie->irq_domain) { + dev_err(dev, "Failed to get a INTx IRQ domain\n"); + ret = -ENOMEM; +- goto out_put_node; + } + +-out_put_node: + of_node_put(pcie_intc_node); + return ret; + } diff --git a/target/linux/generic/pending-6.6/850-dt-bindings-clk-add-BCM63268-timer-clock-definitions.patch b/target/linux/generic/pending-6.6/850-dt-bindings-clk-add-BCM63268-timer-clock-definitions.patch new file mode 100644 index 0000000000..cc6f1e0d9d --- /dev/null +++ b/target/linux/generic/pending-6.6/850-dt-bindings-clk-add-BCM63268-timer-clock-definitions.patch @@ -0,0 +1,114 @@ +From patchwork Wed Mar 22 17:15:12 2023 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 8bit +X-Patchwork-Submitter: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= + +X-Patchwork-Id: 13184389 +Return-Path: +X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on + aws-us-west-2-korg-lkml-1.web.codeaurora.org +Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) + by smtp.lore.kernel.org (Postfix) with ESMTP id 73F2DC6FD1C + for ; Wed, 22 Mar 2023 17:15:59 +0000 (UTC) +Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand + id S231363AbjCVRP5 (ORCPT ); + Wed, 22 Mar 2023 13:15:57 -0400 +Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58824 "EHLO + lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org + with ESMTP id S231408AbjCVRPy (ORCPT + ); Wed, 22 Mar 2023 13:15:54 -0400 +Received: from mail-wm1-x32d.google.com (mail-wm1-x32d.google.com + [IPv6:2a00:1450:4864:20::32d]) + by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 70E4C64B28; + Wed, 22 Mar 2023 10:15:24 -0700 (PDT) +Received: by mail-wm1-x32d.google.com with SMTP id n19so1740892wms.0; + Wed, 22 Mar 2023 10:15:24 -0700 (PDT) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=gmail.com; s=20210112; t=1679505322; + h=content-transfer-encoding:mime-version:references:in-reply-to + :message-id:date:subject:cc:to:from:from:to:cc:subject:date + :message-id:reply-to; + bh=dEknM98Izmc8d/crPsoJ+ejZxfl78958Ei6SPYhYDHs=; + b=LTOQ75W3s5nYo+nEfiJAKqytSopONB4jCtU3zRygzPMasugVOrYFMsUR+WrpsAjuRT + v4HgWpJxEsIWeRXrUN9W21mFXhGgJLJXSxRnrio0CsZZBNMdkebbNOphgKXIWAdm+2iM + PzqAdGm5t38wT2mmm6V/9hCy90+12raHM82tNFdhhiezfg2cukVOKP3j/TeOVCwas0gQ + iFc+CuZB6y73zYXvMUMUpTsqI5vev4xJsSMHIQJVmUxJAwqhOBhN9JCRo7Ao+wayjn2d + Fxo6AV3A8v68nVfoQ0K0I+eWXG48nMCX45iWh/lVvVTOFcR99kn4va7NY1oVnPsh+WQz + WcLA== +X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=1e100.net; s=20210112; t=1679505322; + h=content-transfer-encoding:mime-version:references:in-reply-to + :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc + :subject:date:message-id:reply-to; + bh=dEknM98Izmc8d/crPsoJ+ejZxfl78958Ei6SPYhYDHs=; + b=wv2NSR1B5RnsdoEE7mgJSHAfSs1JHZbQ1HPMldyaGWAk1dcucqh/uDzM3Flz+ADRi1 + 19NoaB2Ur7QaWZejbuplnIOK/nte3PnmqJ9ZNw8HejmuS4eU8mB1V1aJUSKSPGsfUi4a + LYe3HSw87l0jrAC7ptdKvdUtzBoIkX0CeFvfguTQQkDhUTyAFIG144hY6uPXY9Mga96b + gnNe2dLCzHQLbEJpaDaavT7FEEcLDxaq7jNcR2xqEEZaIwfcew+Q05t4xL/3i8GAj9Ru + 6ivQjIbBKfYQF88o7KnOW9o1wjrGsk+Nd4Iy0OLZix3JQasCJGrKV7ib5awI9J39upYV + fa4A== +X-Gm-Message-State: AO0yUKWw75I1M5Vjrd4vXq4GTruQu0H84pycgyi2CT3bczTYRJpWmEWg + +bHDhvp1n5IWW85GI9vKWpbclB13a/S0RQ== +X-Google-Smtp-Source: + AK7set9T/2oJsVetUb2L4mPEWu8YqDrnK8EzHK5bJf1ABIa1Et8f7BFJ7AA3j14ITZuf8cH0HqlRtg== +X-Received: by 2002:a05:600c:2304:b0:3ed:2949:985b with SMTP id + 4-20020a05600c230400b003ed2949985bmr206833wmo.23.1679505322457; + Wed, 22 Mar 2023 10:15:22 -0700 (PDT) +Received: from atlantis.lan (255.red-79-146-124.dynamicip.rima-tde.net. + [79.146.124.255]) + by smtp.gmail.com with ESMTPSA id + v10-20020a05600c470a00b003ee11ac2288sm8414333wmo.21.2023.03.22.10.15.21 + (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); + Wed, 22 Mar 2023 10:15:22 -0700 (PDT) +From: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= +To: mturquette@baylibre.com, sboyd@kernel.org, robh+dt@kernel.org, + krzysztof.kozlowski+dt@linaro.org, p.zabel@pengutronix.de, + f.fainelli@gmail.com, jonas.gorski@gmail.com, + william.zhang@broadcom.com, linux-clk@vger.kernel.org, + devicetree@vger.kernel.org, linux-kernel@vger.kernel.org +Cc: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= , + Rob Herring +Subject: [PATCH v4 1/4] dt-bindings: clk: add BCM63268 timer clock definitions +Date: Wed, 22 Mar 2023 18:15:12 +0100 +Message-Id: <20230322171515.120353-2-noltari@gmail.com> +X-Mailer: git-send-email 2.30.2 +In-Reply-To: <20230322171515.120353-1-noltari@gmail.com> +References: <20230322171515.120353-1-noltari@gmail.com> +MIME-Version: 1.0 +Precedence: bulk +List-ID: +X-Mailing-List: linux-clk@vger.kernel.org + +Add missing timer clock definitions for BCM63268. + +Signed-off-by: Álvaro Fernández Rojas +Acked-by: Rob Herring +--- + v4: no changes + v3: no changes + v2: change commit title, as suggested by Stephen Boyd + + include/dt-bindings/clock/bcm63268-clock.h | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +--- a/include/dt-bindings/clock/bcm63268-clock.h ++++ b/include/dt-bindings/clock/bcm63268-clock.h +@@ -27,4 +27,17 @@ + #define BCM63268_CLK_TBUS 27 + #define BCM63268_CLK_ROBOSW250 31 + ++#define BCM63268_TCLK_EPHY1 0 ++#define BCM63268_TCLK_EPHY2 1 ++#define BCM63268_TCLK_EPHY3 2 ++#define BCM63268_TCLK_GPHY1 3 ++#define BCM63268_TCLK_DSL 4 ++#define BCM63268_TCLK_WAKEON_EPHY 6 ++#define BCM63268_TCLK_WAKEON_DSL 7 ++#define BCM63268_TCLK_FAP1 11 ++#define BCM63268_TCLK_FAP2 15 ++#define BCM63268_TCLK_UTO_50 16 ++#define BCM63268_TCLK_UTO_EXTIN 17 ++#define BCM63268_TCLK_USB_REF 18 ++ + #endif /* __DT_BINDINGS_CLOCK_BCM63268_H */ diff --git a/target/linux/generic/pending-6.6/851-dt-bindings-reset-add-BCM63268-timer-reset-definitions.patch b/target/linux/generic/pending-6.6/851-dt-bindings-reset-add-BCM63268-timer-reset-definitions.patch new file mode 100644 index 0000000000..5f1be105ac --- /dev/null +++ b/target/linux/generic/pending-6.6/851-dt-bindings-reset-add-BCM63268-timer-reset-definitions.patch @@ -0,0 +1,107 @@ +From patchwork Wed Mar 22 17:15:13 2023 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 8bit +X-Patchwork-Submitter: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= + +X-Patchwork-Id: 13184390 +Return-Path: +X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on + aws-us-west-2-korg-lkml-1.web.codeaurora.org +Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) + by smtp.lore.kernel.org (Postfix) with ESMTP id D0B1AC6FD1C + for ; Wed, 22 Mar 2023 17:16:08 +0000 (UTC) +Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand + id S231472AbjCVRQI (ORCPT ); + Wed, 22 Mar 2023 13:16:08 -0400 +Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58934 "EHLO + lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org + with ESMTP id S231435AbjCVRP5 (ORCPT + ); Wed, 22 Mar 2023 13:15:57 -0400 +Received: from mail-wm1-x329.google.com (mail-wm1-x329.google.com + [IPv6:2a00:1450:4864:20::329]) + by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9655064863; + Wed, 22 Mar 2023 10:15:25 -0700 (PDT) +Received: by mail-wm1-x329.google.com with SMTP id + v4-20020a05600c470400b003ee4f06428fso2424553wmo.4; + Wed, 22 Mar 2023 10:15:25 -0700 (PDT) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=gmail.com; s=20210112; t=1679505324; + h=content-transfer-encoding:mime-version:references:in-reply-to + :message-id:date:subject:cc:to:from:from:to:cc:subject:date + :message-id:reply-to; + bh=C7ykhArT1dO7P8wtmI92eo4c7KtPZI9w182/5+cB3T0=; + b=WRZRU2SM9n1LfUj4SgTPfQczADC2pfvoIrOsNpBLTym2eOfmkTetb/WbGSla5kw2Wb + SH5MIC2fFeScJg6T5FFAUOOLmRVW9xvl8Q3T3NKb3z/9wvPHO767nrdIbffRWMJFs7gW + wT/kuTpn8GYdfY0sZ/dMTkq41DVusEkxfX6GxtG85O98ZP8xMHQog8aPs9fRfUvI5ZKB + eGYcRz/Wn1cHhjey9jtWzQEEmZ/BT3b0HQTF9Tl88oofhiEgbyjFXr91+vRsLbsJpGXH + /1FjjaLG5DnonKubV9rmbuCU8KzwH331gi2KuRjvLD2V+OMewqSa5i+GvgVv8x2zC8y+ + /mLQ== +X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=1e100.net; s=20210112; t=1679505324; + h=content-transfer-encoding:mime-version:references:in-reply-to + :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc + :subject:date:message-id:reply-to; + bh=C7ykhArT1dO7P8wtmI92eo4c7KtPZI9w182/5+cB3T0=; + b=bhd0fNh0jDOMlGSC4F+p5igV8AUlGEPj2cXUwgdgqRfSSuUy9z+Li8cT0MbY/aWH5Z + qInRVA+R1cWV3ubrDyKag6oEc0LDU234bnMFcP9b7MRlrM8Dpit9TFSyqJU4sDUWNDs5 + KOe2k/SNIdat6munC9VOuEBDO0eB/UDMN+repKwXNdHChp/Toq9qMvW4Uy8uHxosbQlD + 8P88GbKFjynb1E8I8croGjfub7+y8PPsWB0xNUcafIv6xs3MnVOP1Mk4KwBCbqS509la + mfjsriXtIybO8XFqtn100ungjvbFWdogEplLdSPVdgAqdfF5J8gHxAoApoeYejYkL5/R + kOhQ== +X-Gm-Message-State: AO0yUKWdzr3dMmjKhD8tF+ec4Dfdq9VGZ/WCU4d85npKQvxSwhNPZZ1J + 5WYRIqivh0suFC1OqEidwenpiJYvXedYjw== +X-Google-Smtp-Source: + AK7set87ew2/mKWeShXTTW/YBbBJNR2zeGFV0CfuqLXhiJEU6tqFuyKcW+vFEoKHIbNUS8wRy1SzLA== +X-Received: by 2002:a05:600c:290:b0:3ee:6d88:774a with SMTP id + 16-20020a05600c029000b003ee6d88774amr160734wmk.14.1679505323514; + Wed, 22 Mar 2023 10:15:23 -0700 (PDT) +Received: from atlantis.lan (255.red-79-146-124.dynamicip.rima-tde.net. + [79.146.124.255]) + by smtp.gmail.com with ESMTPSA id + v10-20020a05600c470a00b003ee11ac2288sm8414333wmo.21.2023.03.22.10.15.22 + (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); + Wed, 22 Mar 2023 10:15:23 -0700 (PDT) +From: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= +To: mturquette@baylibre.com, sboyd@kernel.org, robh+dt@kernel.org, + krzysztof.kozlowski+dt@linaro.org, p.zabel@pengutronix.de, + f.fainelli@gmail.com, jonas.gorski@gmail.com, + william.zhang@broadcom.com, linux-clk@vger.kernel.org, + devicetree@vger.kernel.org, linux-kernel@vger.kernel.org +Cc: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= , + Rob Herring +Subject: [PATCH v4 2/4] dt-bindings: reset: add BCM63268 timer reset + definitions +Date: Wed, 22 Mar 2023 18:15:13 +0100 +Message-Id: <20230322171515.120353-3-noltari@gmail.com> +X-Mailer: git-send-email 2.30.2 +In-Reply-To: <20230322171515.120353-1-noltari@gmail.com> +References: <20230322171515.120353-1-noltari@gmail.com> +MIME-Version: 1.0 +Precedence: bulk +List-ID: +X-Mailing-List: linux-clk@vger.kernel.org + +Add missing timer reset definitions for BCM63268. + +Signed-off-by: Álvaro Fernández Rojas +Acked-by: Rob Herring +--- + v4: no changes + v3: no changes + v2: change commit title, as suggested by Stephen Boyd + + include/dt-bindings/reset/bcm63268-reset.h | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/include/dt-bindings/reset/bcm63268-reset.h ++++ b/include/dt-bindings/reset/bcm63268-reset.h +@@ -23,4 +23,8 @@ + #define BCM63268_RST_PCIE_HARD 17 + #define BCM63268_RST_GPHY 18 + ++#define BCM63268_TRST_SW 29 ++#define BCM63268_TRST_HW 30 ++#define BCM63268_TRST_POR 31 ++ + #endif /* __DT_BINDINGS_RESET_BCM63268_H */ diff --git a/target/linux/generic/pending-6.6/852-clk-bcm-Add-BCM63268-timer-clock-and-reset-driver.patch b/target/linux/generic/pending-6.6/852-clk-bcm-Add-BCM63268-timer-clock-and-reset-driver.patch new file mode 100644 index 0000000000..7e500cd1b5 --- /dev/null +++ b/target/linux/generic/pending-6.6/852-clk-bcm-Add-BCM63268-timer-clock-and-reset-driver.patch @@ -0,0 +1,345 @@ +From patchwork Wed Mar 22 17:15:15 2023 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 8bit +X-Patchwork-Submitter: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= + +X-Patchwork-Id: 13184392 +Return-Path: +X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on + aws-us-west-2-korg-lkml-1.web.codeaurora.org +Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) + by smtp.lore.kernel.org (Postfix) with ESMTP id 199D9C76196 + for ; Wed, 22 Mar 2023 17:16:11 +0000 (UTC) +Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand + id S231512AbjCVRQJ (ORCPT ); + Wed, 22 Mar 2023 13:16:09 -0400 +Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58942 "EHLO + lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org + with ESMTP id S231442AbjCVRP5 (ORCPT + ); Wed, 22 Mar 2023 13:15:57 -0400 +Received: from mail-wm1-x32c.google.com (mail-wm1-x32c.google.com + [IPv6:2a00:1450:4864:20::32c]) + by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1DDB36487D; + Wed, 22 Mar 2023 10:15:27 -0700 (PDT) +Received: by mail-wm1-x32c.google.com with SMTP id + i5-20020a05600c354500b003edd24054e0so6717370wmq.4; + Wed, 22 Mar 2023 10:15:27 -0700 (PDT) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=gmail.com; s=20210112; t=1679505325; + h=content-transfer-encoding:mime-version:references:in-reply-to + :message-id:date:subject:cc:to:from:from:to:cc:subject:date + :message-id:reply-to; + bh=rkv/eZYA1ncHp5FnV2ZWc3hgYnAx28S86QA9vmcXFCY=; + b=Y1mva2Bt3sUbKxLgEUS331CJbGxUc4z8kTQW8qiHWGhYlFKtm+d5z4sT40E5BeZAnU + zmTbCI7jbroe9NYBxGUmSli6LNVDPjND80ChbhWTqbqMQTmeQFWut9KmeBWK6Oze2lC/ + XMSOorUzowjcU2xtHNrzoq2KH2pstW573lsB8WnzFVfhMaRkE9DfRr6WNyA7zC8DyxM5 + ezxlCQtCmgPfCqlyksbIDKrgrRf3GiUR0yUd6xRU+MssyvH1FkYGDCerPctDto6lGHBz + 8Y15jT3l6OnQMT6dkekgpPF5/XrSUY93u9g0B4U8+0dhNj+K7vmDen+jqdess+tpLnq/ + gFrA== +X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=1e100.net; s=20210112; t=1679505325; + h=content-transfer-encoding:mime-version:references:in-reply-to + :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc + :subject:date:message-id:reply-to; + bh=rkv/eZYA1ncHp5FnV2ZWc3hgYnAx28S86QA9vmcXFCY=; + b=Ym4+u8bbTQGNkewUBrLf+89vE0EFJBQp2f1crwUxZFboKTROF9ltZonY1CGepo7b0B + fkx3TbWQy5X65g3ScuieqtClCI8WanPeNBJ48+JipJYO3ODVNBxnVaTuW/0FOIcahfqe + sG5GvggHhzRz+Yeybsbnupmzxnw8Ez0BpMl3p7zcjHL7BGZDdOOX2Zbw3zfyYa5sg2nX + UXYJT36zy2h39gxUsy9QkhQ76CG3w6omniohZpYidpojpiDjbOy0nKFky4kUe+YyA1fF + 4IBhjAm6mH+uh6wHSG1qj+NAXHs0xDDJps16PbJwAgL7Qt9K5WW+R/UAYPmHFgaRIHOw + /seA== +X-Gm-Message-State: AO0yUKXRtoYO8Nfus6Ca8lhM39P1Xn6TGkhatEfoISd1YNOkTJJN2hW+ + xRphLgxlzNfCLcVPlpGK9dk= +X-Google-Smtp-Source: + AK7set9VnMEykugk8ZYnkXuqK41bX1dzlvKsAXHEjr8i2NZBld0buKhQLcGYEcwxnBgVTtC7eRGfXw== +X-Received: by 2002:a1c:7c0b:0:b0:3e2:1dac:b071 with SMTP id + x11-20020a1c7c0b000000b003e21dacb071mr178053wmc.13.1679505325582; + Wed, 22 Mar 2023 10:15:25 -0700 (PDT) +Received: from atlantis.lan (255.red-79-146-124.dynamicip.rima-tde.net. + [79.146.124.255]) + by smtp.gmail.com with ESMTPSA id + v10-20020a05600c470a00b003ee11ac2288sm8414333wmo.21.2023.03.22.10.15.24 + (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); + Wed, 22 Mar 2023 10:15:25 -0700 (PDT) +From: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= +To: mturquette@baylibre.com, sboyd@kernel.org, robh+dt@kernel.org, + krzysztof.kozlowski+dt@linaro.org, p.zabel@pengutronix.de, + f.fainelli@gmail.com, jonas.gorski@gmail.com, + william.zhang@broadcom.com, linux-clk@vger.kernel.org, + devicetree@vger.kernel.org, linux-kernel@vger.kernel.org +Cc: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= +Subject: [PATCH v4 4/4] clk: bcm: Add BCM63268 timer clock and reset driver +Date: Wed, 22 Mar 2023 18:15:15 +0100 +Message-Id: <20230322171515.120353-5-noltari@gmail.com> +X-Mailer: git-send-email 2.30.2 +In-Reply-To: <20230322171515.120353-1-noltari@gmail.com> +References: <20230322171515.120353-1-noltari@gmail.com> +MIME-Version: 1.0 +Precedence: bulk +List-ID: +X-Mailing-List: linux-clk@vger.kernel.org + +Add driver for BCM63268 timer clock and reset controller. + +Signed-off-by: Álvaro Fernández Rojas +--- + v4: add changes suggested by Stephen Boyd: + - Usage of of_device_get_match_data() isn't needed. + - Use devm_clk_hw_register_gate(). + - Drop clk_hw_unregister_gate(). + v3: add missing include to fix build warning + v2: add changes suggested by Stephen Boyd + + drivers/clk/bcm/Kconfig | 9 ++ + drivers/clk/bcm/Makefile | 1 + + drivers/clk/bcm/clk-bcm63268-timer.c | 215 +++++++++++++++++++++++++++ + 3 files changed, 225 insertions(+) + create mode 100644 drivers/clk/bcm/clk-bcm63268-timer.c + +--- a/drivers/clk/bcm/Kconfig ++++ b/drivers/clk/bcm/Kconfig +@@ -37,6 +37,15 @@ config CLK_BCM_63XX_GATE + Enable common clock framework support for Broadcom BCM63xx DSL SoCs + based on the MIPS architecture + ++config CLK_BCM63268_TIMER ++ bool "Broadcom BCM63268 timer clock and reset support" ++ depends on BMIPS_GENERIC || COMPILE_TEST ++ default BMIPS_GENERIC ++ select RESET_CONTROLLER ++ help ++ Enable timer clock and reset support for Broadcom BCM63268 DSL SoCs ++ based on the MIPS architecture. ++ + config CLK_BCM_KONA + bool "Broadcom Kona CCU clock support" + depends on ARCH_BCM_MOBILE || COMPILE_TEST +--- a/drivers/clk/bcm/Makefile ++++ b/drivers/clk/bcm/Makefile +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0 + obj-$(CONFIG_CLK_BCM_63XX) += clk-bcm63xx.o + obj-$(CONFIG_CLK_BCM_63XX_GATE) += clk-bcm63xx-gate.o ++obj-$(CONFIG_CLK_BCM63268_TIMER) += clk-bcm63268-timer.o + obj-$(CONFIG_CLK_BCM_KONA) += clk-kona.o + obj-$(CONFIG_CLK_BCM_KONA) += clk-kona-setup.o + obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281xx.o +--- /dev/null ++++ b/drivers/clk/bcm/clk-bcm63268-timer.c +@@ -0,0 +1,215 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * BCM63268 Timer Clock and Reset Controller Driver ++ * ++ * Copyright (C) 2023 Álvaro Fernández Rojas ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define BCM63268_TIMER_RESET_SLEEP_MIN_US 10000 ++#define BCM63268_TIMER_RESET_SLEEP_MAX_US 20000 ++ ++struct bcm63268_tclkrst_hw { ++ void __iomem *regs; ++ spinlock_t lock; ++ ++ struct reset_controller_dev rcdev; ++ struct clk_hw_onecell_data data; ++}; ++ ++struct bcm63268_tclk_table_entry { ++ const char * const name; ++ u8 bit; ++}; ++ ++static const struct bcm63268_tclk_table_entry bcm63268_timer_clocks[] = { ++ { ++ .name = "ephy1", ++ .bit = BCM63268_TCLK_EPHY1, ++ }, { ++ .name = "ephy2", ++ .bit = BCM63268_TCLK_EPHY2, ++ }, { ++ .name = "ephy3", ++ .bit = BCM63268_TCLK_EPHY3, ++ }, { ++ .name = "gphy1", ++ .bit = BCM63268_TCLK_GPHY1, ++ }, { ++ .name = "dsl", ++ .bit = BCM63268_TCLK_DSL, ++ }, { ++ .name = "wakeon_ephy", ++ .bit = BCM63268_TCLK_WAKEON_EPHY, ++ }, { ++ .name = "wakeon_dsl", ++ .bit = BCM63268_TCLK_WAKEON_DSL, ++ }, { ++ .name = "fap1_pll", ++ .bit = BCM63268_TCLK_FAP1, ++ }, { ++ .name = "fap2_pll", ++ .bit = BCM63268_TCLK_FAP2, ++ }, { ++ .name = "uto_50", ++ .bit = BCM63268_TCLK_UTO_50, ++ }, { ++ .name = "uto_extin", ++ .bit = BCM63268_TCLK_UTO_EXTIN, ++ }, { ++ .name = "usb_ref", ++ .bit = BCM63268_TCLK_USB_REF, ++ }, { ++ /* sentinel */ ++ } ++}; ++ ++static inline struct bcm63268_tclkrst_hw * ++to_bcm63268_timer_reset(struct reset_controller_dev *rcdev) ++{ ++ return container_of(rcdev, struct bcm63268_tclkrst_hw, rcdev); ++} ++ ++static int bcm63268_timer_reset_update(struct reset_controller_dev *rcdev, ++ unsigned long id, bool assert) ++{ ++ struct bcm63268_tclkrst_hw *reset = to_bcm63268_timer_reset(rcdev); ++ unsigned long flags; ++ uint32_t val; ++ ++ spin_lock_irqsave(&reset->lock, flags); ++ val = __raw_readl(reset->regs); ++ if (assert) ++ val &= ~BIT(id); ++ else ++ val |= BIT(id); ++ __raw_writel(val, reset->regs); ++ spin_unlock_irqrestore(&reset->lock, flags); ++ ++ return 0; ++} ++ ++static int bcm63268_timer_reset_assert(struct reset_controller_dev *rcdev, ++ unsigned long id) ++{ ++ return bcm63268_timer_reset_update(rcdev, id, true); ++} ++ ++static int bcm63268_timer_reset_deassert(struct reset_controller_dev *rcdev, ++ unsigned long id) ++{ ++ return bcm63268_timer_reset_update(rcdev, id, false); ++} ++ ++static int bcm63268_timer_reset_reset(struct reset_controller_dev *rcdev, ++ unsigned long id) ++{ ++ bcm63268_timer_reset_update(rcdev, id, true); ++ usleep_range(BCM63268_TIMER_RESET_SLEEP_MIN_US, ++ BCM63268_TIMER_RESET_SLEEP_MAX_US); ++ ++ bcm63268_timer_reset_update(rcdev, id, false); ++ /* ++ * Ensure component is taken out reset state by sleeping also after ++ * deasserting the reset. Otherwise, the component may not be ready ++ * for operation. ++ */ ++ usleep_range(BCM63268_TIMER_RESET_SLEEP_MIN_US, ++ BCM63268_TIMER_RESET_SLEEP_MAX_US); ++ ++ return 0; ++} ++ ++static int bcm63268_timer_reset_status(struct reset_controller_dev *rcdev, ++ unsigned long id) ++{ ++ struct bcm63268_tclkrst_hw *reset = to_bcm63268_timer_reset(rcdev); ++ ++ return !(__raw_readl(reset->regs) & BIT(id)); ++} ++ ++static struct reset_control_ops bcm63268_timer_reset_ops = { ++ .assert = bcm63268_timer_reset_assert, ++ .deassert = bcm63268_timer_reset_deassert, ++ .reset = bcm63268_timer_reset_reset, ++ .status = bcm63268_timer_reset_status, ++}; ++ ++static int bcm63268_tclk_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ const struct bcm63268_tclk_table_entry *entry; ++ struct bcm63268_tclkrst_hw *hw; ++ struct clk_hw *clk; ++ u8 maxbit = 0; ++ int i, ret; ++ ++ for (entry = bcm63268_timer_clocks; entry->name; entry++) ++ maxbit = max(maxbit, entry->bit); ++ maxbit++; ++ ++ hw = devm_kzalloc(&pdev->dev, struct_size(hw, data.hws, maxbit), ++ GFP_KERNEL); ++ if (!hw) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, hw); ++ ++ spin_lock_init(&hw->lock); ++ ++ hw->data.num = maxbit; ++ for (i = 0; i < maxbit; i++) ++ hw->data.hws[i] = ERR_PTR(-ENODEV); ++ ++ hw->regs = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(hw->regs)) ++ return PTR_ERR(hw->regs); ++ ++ for (entry = bcm63268_timer_clocks; entry->name; entry++) { ++ clk = devm_clk_hw_register_gate(dev, entry->name, NULL, 0, ++ hw->regs, entry->bit, ++ CLK_GATE_BIG_ENDIAN, ++ &hw->lock); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ ++ hw->data.hws[entry->bit] = clk; ++ } ++ ++ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, ++ &hw->data); ++ if (ret) ++ return ret; ++ ++ hw->rcdev.of_node = dev->of_node; ++ hw->rcdev.ops = &bcm63268_timer_reset_ops; ++ ++ ret = devm_reset_controller_register(dev, &hw->rcdev); ++ if (ret) ++ dev_err(dev, "Failed to register reset controller\n"); ++ ++ return 0; ++} ++ ++static const struct of_device_id bcm63268_tclk_dt_ids[] = { ++ { .compatible = "brcm,bcm63268-timer-clocks" }, ++ { /* sentinel */ } ++}; ++ ++static struct platform_driver bcm63268_tclk = { ++ .probe = bcm63268_tclk_probe, ++ .driver = { ++ .name = "bcm63268-timer-clock", ++ .of_match_table = bcm63268_tclk_dt_ids, ++ }, ++}; ++builtin_platform_driver(bcm63268_tclk); diff --git a/target/linux/generic/pending-6.6/860-serial-8250_mtk-track-busclk-state-to-avoid-bus-error.patch b/target/linux/generic/pending-6.6/860-serial-8250_mtk-track-busclk-state-to-avoid-bus-error.patch new file mode 100644 index 0000000000..c5db5d9491 --- /dev/null +++ b/target/linux/generic/pending-6.6/860-serial-8250_mtk-track-busclk-state-to-avoid-bus-error.patch @@ -0,0 +1,61 @@ +From 629c701fc39f1ada9416e0766a86729e83bde86c Mon Sep 17 00:00:00 2001 +Message-ID: <629c701fc39f1ada9416e0766a86729e83bde86c.1694465766.git.daniel@makrotopia.org> +From: Daniel Golle +Date: Mon, 11 Sep 2023 21:27:44 +0100 +Subject: [PATCH] serial: 8250_mtk: track busclk state to avoid bus error +To: Greg Kroah-Hartman , + Jiri Slaby , + Matthias Brugger , + AngeloGioacchino Del Regno , + Daniel Golle , + John Ogness , + Chen-Yu Tsai , + Changqi Hu , + linux-kernel@vger.kernel.org, + linux-serial@vger.kernel.org, + linux-arm-kernel@lists.infradead.org, + linux-mediatek@lists.infradead.org + +Commit e32a83c70cf9 ("serial: 8250-mtk: modify mtk uart power and +clock management") introduced polling a debug register to make sure +the UART is idle before disabling the bus clock. However, at least on +some MediaTek SoCs access to that very debug register requires the bus +clock being enabled. Hence calling the suspend function while already +in suspended state results in that register access triggering a bus +error. In order to avoid that, track the state of the bus clock and +only poll the debug register if not already in suspended state. + +Fixes: e32a83c70cf9 ("serial: 8250-mtk: modify mtk uart power and clock management") +Signed-off-by: Daniel Golle +--- + drivers/tty/serial/8250/8250_mtk.c | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +--- a/drivers/tty/serial/8250/8250_mtk.c ++++ b/drivers/tty/serial/8250/8250_mtk.c +@@ -32,7 +32,7 @@ + #define MTK_UART_RXTRI_AD 0x14 /* RX Trigger address */ + #define MTK_UART_FRACDIV_L 0x15 /* Fractional divider LSB address */ + #define MTK_UART_FRACDIV_M 0x16 /* Fractional divider MSB address */ +-#define MTK_UART_DEBUG0 0x18 ++#define MTK_UART_DEBUG0 0x18 + #define MTK_UART_IER_XOFFI 0x20 /* Enable XOFF character interrupt */ + #define MTK_UART_IER_RTSI 0x40 /* Enable RTS Modem status interrupt */ + #define MTK_UART_IER_CTSI 0x80 /* Enable CTS Modem status interrupt */ +@@ -418,13 +418,12 @@ static int __maybe_unused mtk8250_runtim + struct mtk8250_data *data = dev_get_drvdata(dev); + struct uart_8250_port *up = serial8250_get_port(data->line); + +- /* wait until UART in idle status */ +- while +- (serial_in(up, MTK_UART_DEBUG0)); +- + if (data->clk_count == 0U) { + dev_dbg(dev, "%s clock count is 0\n", __func__); + } else { ++ /* wait until UART in idle status */ ++ while ++ (serial_in(up, MTK_UART_DEBUG0)); + clk_disable_unprepare(data->bus_clk); + data->clk_count--; + } diff --git a/target/linux/generic/pending-6.6/870-ARM-dts-nxp-imx7d-pico-add-cpu-supply-nodes.patch b/target/linux/generic/pending-6.6/870-ARM-dts-nxp-imx7d-pico-add-cpu-supply-nodes.patch new file mode 100644 index 0000000000..e25389363f --- /dev/null +++ b/target/linux/generic/pending-6.6/870-ARM-dts-nxp-imx7d-pico-add-cpu-supply-nodes.patch @@ -0,0 +1,43 @@ +From d0562705bcd4cb9849156f095b2af0ec1bb53b56 Mon Sep 17 00:00:00 2001 +From: Lech Perczak +Date: Fri, 17 Nov 2023 21:33:04 +0100 +Subject: [PATCH] ARM: dts: nxp: imx7d-pico: add cpu-supply nodes + +The PICO-IMX7D SoM has the usual power supply configuration using +output sw1a of PF3000 PMIC, which was defined in downstream derivative +of linux-imx (see link) in the sources for "Android Things" devkit. +It is required to support CPU frequency scaling. + +Map the respective "cpu-supply" nodes of each core to sw1a of the PMIC. + +Enabling them causes cpufreq-dt, and imx-thermal drivers to probe +successfully, and CPU frequency scaling to function. + +Link: https://android.googlesource.com/platform/hardware/bsp/kernel/nxp/imx-v4.1/+/o-iot-preview-5/arch/arm/boot/dts/imx7d-pico.dtsi#849 + +Cc: Fabio Estevam +Cc: Shawn Guo +Cc: Sascha Hauer + +Signed-off-by: Lech Perczak +--- + arch/arm/boot/dts/imx7d-pico.dtsi | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/arch/arm/boot/dts/imx7d-pico.dtsi ++++ b/arch/arm/boot/dts/imx7d-pico.dtsi +@@ -108,6 +108,14 @@ + assigned-clock-rates = <0>, <32768>; + }; + ++&cpu0 { ++ cpu-supply = <&sw1a_reg>; ++}; ++ ++&cpu1 { ++ cpu-supply = <&sw1a_reg>; ++}; ++ + &ecspi3 { + cs-gpios = <&gpio4 11 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; diff --git a/target/linux/generic/pending-6.6/920-mangle_bootargs.patch b/target/linux/generic/pending-6.6/920-mangle_bootargs.patch new file mode 100644 index 0000000000..4552b4cd66 --- /dev/null +++ b/target/linux/generic/pending-6.6/920-mangle_bootargs.patch @@ -0,0 +1,71 @@ +From: Imre Kaloz +Subject: init: add CONFIG_MANGLE_BOOTARGS and disable it by default + +Enabling this option renames the bootloader supplied root= +and rootfstype= variables, which might have to be know but +would break the automatisms OpenWrt uses. + +Signed-off-by: Imre Kaloz +--- + init/Kconfig | 9 +++++++++ + init/main.c | 24 ++++++++++++++++++++++++ + 2 files changed, 33 insertions(+) + +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -1836,6 +1836,15 @@ config EMBEDDED + an embedded system so certain expert options are available + for configuration. + ++config MANGLE_BOOTARGS ++ bool "Rename offending bootargs" ++ depends on EXPERT ++ help ++ Sometimes the bootloader passed bogus root= and rootfstype= ++ parameters to the kernel, and while you want to ignore them, ++ you need to know the values f.e. to support dual firmware ++ layouts on the flash. ++ + config HAVE_PERF_EVENTS + bool + help +--- a/init/main.c ++++ b/init/main.c +@@ -611,6 +611,29 @@ static inline void setup_nr_cpu_ids(void + static inline void smp_prepare_cpus(unsigned int maxcpus) { } + #endif + ++#ifdef CONFIG_MANGLE_BOOTARGS ++static void __init mangle_bootargs(char *command_line) ++{ ++ char *rootdev; ++ char *rootfs; ++ ++ rootdev = strstr(command_line, "root=/dev/mtdblock"); ++ ++ if (rootdev) ++ strncpy(rootdev, "mangled_rootblock=", 18); ++ ++ rootfs = strstr(command_line, "rootfstype"); ++ ++ if (rootfs) ++ strncpy(rootfs, "mangled_fs", 10); ++ ++} ++#else ++static void __init mangle_bootargs(char *command_line) ++{ ++} ++#endif ++ + /* + * We need to store the untouched command line for future reference. + * We also need to store the touched command line since the parameter +@@ -958,6 +981,7 @@ asmlinkage __visible void __init __no_sa + pr_notice("%s", linux_banner); + early_security_init(); + setup_arch(&command_line); ++ mangle_bootargs(command_line); + setup_boot_config(); + setup_command_line(command_line); + setup_nr_cpu_ids(); diff --git a/target/linux/generic/pending-6.6/980-tools-thermal-tmon-Fix-compilation-warning-for-wrong.patch b/target/linux/generic/pending-6.6/980-tools-thermal-tmon-Fix-compilation-warning-for-wrong.patch new file mode 100644 index 0000000000..6a0a19987f --- /dev/null +++ b/target/linux/generic/pending-6.6/980-tools-thermal-tmon-Fix-compilation-warning-for-wrong.patch @@ -0,0 +1,51 @@ +From a7a94ca21ac0f347f683d33c72b4aab57ce5eec3 Mon Sep 17 00:00:00 2001 +From: Florian Eckert +Date: Mon, 20 Nov 2023 11:13:20 +0100 +Subject: [PATCH] tools/thermal/tmon: Fix compilation warning for wrong format + +The following warnings are shown during compilation: + +tui.c: In function 'show_cooling_device': + tui.c:216:40: warning: format '%d' expects argument of type 'int', but +argument 7 has type 'long unsigned int' [-Wformat=] + 216 | "%02d %12.12s%6d %6d", + | ~~^ + | | + | int + | %6ld + ...... + 219 | ptdata.cdi[j].cur_state, + | ~~~~~~~~~~~~~~~~~~~~~~~ + | | + | long unsigned int + tui.c:216:44: warning: format '%d' expects argument of type 'int', but +argument 8 has type 'long unsigned int' [-Wformat=] + 216 | "%02d %12.12s%6d %6d", + | ~~^ + | | + | int + | %6ld + ...... + 220 | ptdata.cdi[j].max_state); + | ~~~~~~~~~~~~~~~~~~~~~~~ + | | + | long unsigned int + +To fix this, the correct string format must be used for printing. + +Signed-off-by: Florian Eckert +--- + tools/thermal/tmon/tui.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/tools/thermal/tmon/tui.c ++++ b/tools/thermal/tmon/tui.c +@@ -213,7 +213,7 @@ void show_cooling_device(void) + * cooling device instances. skip unused idr. + */ + mvwprintw(cooling_device_window, j + 2, 1, +- "%02d %12.12s%6d %6d", ++ "%02d %12.12s%6lu %6lu", + ptdata.cdi[j].instance, + ptdata.cdi[j].type, + ptdata.cdi[j].cur_state,