mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-03 22:44:27 -05:00 
			
		
		
		
	generic: drop 6.1 support
Drop config and files for Linux 6.1. Signed-off-by: Daniel Golle <daniel@makrotopia.org>
This commit is contained in:
		
							parent
							
								
									97e1a223fd
								
							
						
					
					
						commit
						3f01b29146
					
				@ -1,352 +0,0 @@
 | 
				
			|||||||
From 8c20e2eb5f2a0175b774134685e4d7bd93e85ff8 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Yu Zhao <yuzhao@google.com>
 | 
					 | 
				
			||||||
Date: Wed, 21 Dec 2022 21:18:59 -0700
 | 
					 | 
				
			||||||
Subject: [PATCH 01/19] UPSTREAM: mm: multi-gen LRU: rename lru_gen_struct to
 | 
					 | 
				
			||||||
 lru_gen_folio
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Patch series "mm: multi-gen LRU: memcg LRU", v3.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Overview
 | 
					 | 
				
			||||||
========
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
An memcg LRU is a per-node LRU of memcgs.  It is also an LRU of LRUs,
 | 
					 | 
				
			||||||
since each node and memcg combination has an LRU of folios (see
 | 
					 | 
				
			||||||
mem_cgroup_lruvec()).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Its goal is to improve the scalability of global reclaim, which is
 | 
					 | 
				
			||||||
critical to system-wide memory overcommit in data centers.  Note that
 | 
					 | 
				
			||||||
memcg reclaim is currently out of scope.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Its memory bloat is a pointer to each lruvec and negligible to each
 | 
					 | 
				
			||||||
pglist_data.  In terms of traversing memcgs during global reclaim, it
 | 
					 | 
				
			||||||
improves the best-case complexity from O(n) to O(1) and does not affect
 | 
					 | 
				
			||||||
the worst-case complexity O(n).  Therefore, on average, it has a sublinear
 | 
					 | 
				
			||||||
complexity in contrast to the current linear complexity.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The basic structure of an memcg LRU can be understood by an analogy to
 | 
					 | 
				
			||||||
the active/inactive LRU (of folios):
 | 
					 | 
				
			||||||
1. It has the young and the old (generations), i.e., the counterparts
 | 
					 | 
				
			||||||
   to the active and the inactive;
 | 
					 | 
				
			||||||
2. The increment of max_seq triggers promotion, i.e., the counterpart
 | 
					 | 
				
			||||||
   to activation;
 | 
					 | 
				
			||||||
3. Other events trigger similar operations, e.g., offlining an memcg
 | 
					 | 
				
			||||||
   triggers demotion, i.e., the counterpart to deactivation.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
In terms of global reclaim, it has two distinct features:
 | 
					 | 
				
			||||||
1. Sharding, which allows each thread to start at a random memcg (in
 | 
					 | 
				
			||||||
   the old generation) and improves parallelism;
 | 
					 | 
				
			||||||
2. Eventual fairness, which allows direct reclaim to bail out at will
 | 
					 | 
				
			||||||
   and reduces latency without affecting fairness over some time.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The commit message in patch 6 details the workflow:
 | 
					 | 
				
			||||||
https://lore.kernel.org/r/20221222041905.2431096-7-yuzhao@google.com/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The following is a simple test to quickly verify its effectiveness.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  Test design:
 | 
					 | 
				
			||||||
  1. Create multiple memcgs.
 | 
					 | 
				
			||||||
  2. Each memcg contains a job (fio).
 | 
					 | 
				
			||||||
  3. All jobs access the same amount of memory randomly.
 | 
					 | 
				
			||||||
  4. The system does not experience global memory pressure.
 | 
					 | 
				
			||||||
  5. Periodically write to the root memory.reclaim.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  Desired outcome:
 | 
					 | 
				
			||||||
  1. All memcgs have similar pgsteal counts, i.e., stddev(pgsteal)
 | 
					 | 
				
			||||||
     over mean(pgsteal) is close to 0%.
 | 
					 | 
				
			||||||
  2. The total pgsteal is close to the total requested through
 | 
					 | 
				
			||||||
     memory.reclaim, i.e., sum(pgsteal) over sum(requested) is close
 | 
					 | 
				
			||||||
     to 100%.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  Actual outcome [1]:
 | 
					 | 
				
			||||||
                                     MGLRU off    MGLRU on
 | 
					 | 
				
			||||||
  stddev(pgsteal) / mean(pgsteal)    75%          20%
 | 
					 | 
				
			||||||
  sum(pgsteal) / sum(requested)      425%         95%
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ####################################################################
 | 
					 | 
				
			||||||
  MEMCGS=128
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  for ((memcg = 0; memcg < $MEMCGS; memcg++)); do
 | 
					 | 
				
			||||||
      mkdir /sys/fs/cgroup/memcg$memcg
 | 
					 | 
				
			||||||
  done
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  start() {
 | 
					 | 
				
			||||||
      echo $BASHPID > /sys/fs/cgroup/memcg$memcg/cgroup.procs
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      fio -name=memcg$memcg --numjobs=1 --ioengine=mmap \
 | 
					 | 
				
			||||||
          --filename=/dev/zero --size=1920M --rw=randrw \
 | 
					 | 
				
			||||||
          --rate=64m,64m --random_distribution=random \
 | 
					 | 
				
			||||||
          --fadvise_hint=0 --time_based --runtime=10h \
 | 
					 | 
				
			||||||
          --group_reporting --minimal
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  for ((memcg = 0; memcg < $MEMCGS; memcg++)); do
 | 
					 | 
				
			||||||
      start &
 | 
					 | 
				
			||||||
  done
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  sleep 600
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  for ((i = 0; i < 600; i++)); do
 | 
					 | 
				
			||||||
      echo 256m >/sys/fs/cgroup/memory.reclaim
 | 
					 | 
				
			||||||
      sleep 6
 | 
					 | 
				
			||||||
  done
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  for ((memcg = 0; memcg < $MEMCGS; memcg++)); do
 | 
					 | 
				
			||||||
      grep "pgsteal " /sys/fs/cgroup/memcg$memcg/memory.stat
 | 
					 | 
				
			||||||
  done
 | 
					 | 
				
			||||||
  ####################################################################
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[1]: This was obtained from running the above script (touches less
 | 
					 | 
				
			||||||
     than 256GB memory) on an EPYC 7B13 with 512GB DRAM for over an
 | 
					 | 
				
			||||||
     hour.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This patch (of 8):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The new name lru_gen_folio will be more distinct from the coming
 | 
					 | 
				
			||||||
lru_gen_memcg.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Link: https://lkml.kernel.org/r/20221222041905.2431096-1-yuzhao@google.com
 | 
					 | 
				
			||||||
Link: https://lkml.kernel.org/r/20221222041905.2431096-2-yuzhao@google.com
 | 
					 | 
				
			||||||
Signed-off-by: Yu Zhao <yuzhao@google.com>
 | 
					 | 
				
			||||||
Cc: Johannes Weiner <hannes@cmpxchg.org>
 | 
					 | 
				
			||||||
Cc: Jonathan Corbet <corbet@lwn.net>
 | 
					 | 
				
			||||||
Cc: Michael Larabel <Michael@MichaelLarabel.com>
 | 
					 | 
				
			||||||
Cc: Michal Hocko <mhocko@kernel.org>
 | 
					 | 
				
			||||||
Cc: Mike Rapoport <rppt@kernel.org>
 | 
					 | 
				
			||||||
Cc: Roman Gushchin <roman.gushchin@linux.dev>
 | 
					 | 
				
			||||||
Cc: Suren Baghdasaryan <surenb@google.com>
 | 
					 | 
				
			||||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
 | 
					 | 
				
			||||||
Bug: 274865848
 | 
					 | 
				
			||||||
(cherry picked from commit 391655fe08d1f942359a11148aa9aaf3f99d6d6f)
 | 
					 | 
				
			||||||
Change-Id: I7df67e0e2435ba28f10eaa57d28d98b61a9210a6
 | 
					 | 
				
			||||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 include/linux/mm_inline.h |  4 ++--
 | 
					 | 
				
			||||||
 include/linux/mmzone.h    |  6 +++---
 | 
					 | 
				
			||||||
 mm/vmscan.c               | 34 +++++++++++++++++-----------------
 | 
					 | 
				
			||||||
 mm/workingset.c           |  4 ++--
 | 
					 | 
				
			||||||
 4 files changed, 24 insertions(+), 24 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/include/linux/mm_inline.h
 | 
					 | 
				
			||||||
+++ b/include/linux/mm_inline.h
 | 
					 | 
				
			||||||
@@ -178,7 +178,7 @@ static inline void lru_gen_update_size(s
 | 
					 | 
				
			||||||
 	int zone = folio_zonenum(folio);
 | 
					 | 
				
			||||||
 	int delta = folio_nr_pages(folio);
 | 
					 | 
				
			||||||
 	enum lru_list lru = type * LRU_INACTIVE_FILE;
 | 
					 | 
				
			||||||
-	struct lru_gen_struct *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
+	struct lru_gen_folio *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	VM_WARN_ON_ONCE(old_gen != -1 && old_gen >= MAX_NR_GENS);
 | 
					 | 
				
			||||||
 	VM_WARN_ON_ONCE(new_gen != -1 && new_gen >= MAX_NR_GENS);
 | 
					 | 
				
			||||||
@@ -224,7 +224,7 @@ static inline bool lru_gen_add_folio(str
 | 
					 | 
				
			||||||
 	int gen = folio_lru_gen(folio);
 | 
					 | 
				
			||||||
 	int type = folio_is_file_lru(folio);
 | 
					 | 
				
			||||||
 	int zone = folio_zonenum(folio);
 | 
					 | 
				
			||||||
-	struct lru_gen_struct *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
+	struct lru_gen_folio *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	VM_WARN_ON_ONCE_FOLIO(gen != -1, folio);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
--- a/include/linux/mmzone.h
 | 
					 | 
				
			||||||
+++ b/include/linux/mmzone.h
 | 
					 | 
				
			||||||
@@ -404,7 +404,7 @@ enum {
 | 
					 | 
				
			||||||
  * The number of pages in each generation is eventually consistent and therefore
 | 
					 | 
				
			||||||
  * can be transiently negative when reset_batch_size() is pending.
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
-struct lru_gen_struct {
 | 
					 | 
				
			||||||
+struct lru_gen_folio {
 | 
					 | 
				
			||||||
 	/* the aging increments the youngest generation number */
 | 
					 | 
				
			||||||
 	unsigned long max_seq;
 | 
					 | 
				
			||||||
 	/* the eviction increments the oldest generation numbers */
 | 
					 | 
				
			||||||
@@ -461,7 +461,7 @@ struct lru_gen_mm_state {
 | 
					 | 
				
			||||||
 struct lru_gen_mm_walk {
 | 
					 | 
				
			||||||
 	/* the lruvec under reclaim */
 | 
					 | 
				
			||||||
 	struct lruvec *lruvec;
 | 
					 | 
				
			||||||
-	/* unstable max_seq from lru_gen_struct */
 | 
					 | 
				
			||||||
+	/* unstable max_seq from lru_gen_folio */
 | 
					 | 
				
			||||||
 	unsigned long max_seq;
 | 
					 | 
				
			||||||
 	/* the next address within an mm to scan */
 | 
					 | 
				
			||||||
 	unsigned long next_addr;
 | 
					 | 
				
			||||||
@@ -524,7 +524,7 @@ struct lruvec {
 | 
					 | 
				
			||||||
 	unsigned long			flags;
 | 
					 | 
				
			||||||
 #ifdef CONFIG_LRU_GEN
 | 
					 | 
				
			||||||
 	/* evictable pages divided into generations */
 | 
					 | 
				
			||||||
-	struct lru_gen_struct		lrugen;
 | 
					 | 
				
			||||||
+	struct lru_gen_folio		lrugen;
 | 
					 | 
				
			||||||
 	/* to concurrently iterate lru_gen_mm_list */
 | 
					 | 
				
			||||||
 	struct lru_gen_mm_state		mm_state;
 | 
					 | 
				
			||||||
 #endif
 | 
					 | 
				
			||||||
--- a/mm/vmscan.c
 | 
					 | 
				
			||||||
+++ b/mm/vmscan.c
 | 
					 | 
				
			||||||
@@ -3190,7 +3190,7 @@ static int get_nr_gens(struct lruvec *lr
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static bool __maybe_unused seq_is_valid(struct lruvec *lruvec)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
-	/* see the comment on lru_gen_struct */
 | 
					 | 
				
			||||||
+	/* see the comment on lru_gen_folio */
 | 
					 | 
				
			||||||
 	return get_nr_gens(lruvec, LRU_GEN_FILE) >= MIN_NR_GENS &&
 | 
					 | 
				
			||||||
 	       get_nr_gens(lruvec, LRU_GEN_FILE) <= get_nr_gens(lruvec, LRU_GEN_ANON) &&
 | 
					 | 
				
			||||||
 	       get_nr_gens(lruvec, LRU_GEN_ANON) <= MAX_NR_GENS;
 | 
					 | 
				
			||||||
@@ -3596,7 +3596,7 @@ struct ctrl_pos {
 | 
					 | 
				
			||||||
 static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier, int gain,
 | 
					 | 
				
			||||||
 			  struct ctrl_pos *pos)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
-	struct lru_gen_struct *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
+	struct lru_gen_folio *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
 	int hist = lru_hist_from_seq(lrugen->min_seq[type]);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	pos->refaulted = lrugen->avg_refaulted[type][tier] +
 | 
					 | 
				
			||||||
@@ -3611,7 +3611,7 @@ static void read_ctrl_pos(struct lruvec
 | 
					 | 
				
			||||||
 static void reset_ctrl_pos(struct lruvec *lruvec, int type, bool carryover)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	int hist, tier;
 | 
					 | 
				
			||||||
-	struct lru_gen_struct *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
+	struct lru_gen_folio *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
 	bool clear = carryover ? NR_HIST_GENS == 1 : NR_HIST_GENS > 1;
 | 
					 | 
				
			||||||
 	unsigned long seq = carryover ? lrugen->min_seq[type] : lrugen->max_seq + 1;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -3688,7 +3688,7 @@ static int folio_update_gen(struct folio
 | 
					 | 
				
			||||||
 static int folio_inc_gen(struct lruvec *lruvec, struct folio *folio, bool reclaiming)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	int type = folio_is_file_lru(folio);
 | 
					 | 
				
			||||||
-	struct lru_gen_struct *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
+	struct lru_gen_folio *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
 	int new_gen, old_gen = lru_gen_from_seq(lrugen->min_seq[type]);
 | 
					 | 
				
			||||||
 	unsigned long new_flags, old_flags = READ_ONCE(folio->flags);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -3733,7 +3733,7 @@ static void update_batch_size(struct lru
 | 
					 | 
				
			||||||
 static void reset_batch_size(struct lruvec *lruvec, struct lru_gen_mm_walk *walk)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	int gen, type, zone;
 | 
					 | 
				
			||||||
-	struct lru_gen_struct *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
+	struct lru_gen_folio *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	walk->batched = 0;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -4250,7 +4250,7 @@ static bool inc_min_seq(struct lruvec *l
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	int zone;
 | 
					 | 
				
			||||||
 	int remaining = MAX_LRU_BATCH;
 | 
					 | 
				
			||||||
-	struct lru_gen_struct *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
+	struct lru_gen_folio *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
 	int new_gen, old_gen = lru_gen_from_seq(lrugen->min_seq[type]);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	if (type == LRU_GEN_ANON && !can_swap)
 | 
					 | 
				
			||||||
@@ -4286,7 +4286,7 @@ static bool try_to_inc_min_seq(struct lr
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	int gen, type, zone;
 | 
					 | 
				
			||||||
 	bool success = false;
 | 
					 | 
				
			||||||
-	struct lru_gen_struct *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
+	struct lru_gen_folio *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
 	DEFINE_MIN_SEQ(lruvec);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	VM_WARN_ON_ONCE(!seq_is_valid(lruvec));
 | 
					 | 
				
			||||||
@@ -4307,7 +4307,7 @@ next:
 | 
					 | 
				
			||||||
 		;
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	/* see the comment on lru_gen_struct */
 | 
					 | 
				
			||||||
+	/* see the comment on lru_gen_folio */
 | 
					 | 
				
			||||||
 	if (can_swap) {
 | 
					 | 
				
			||||||
 		min_seq[LRU_GEN_ANON] = min(min_seq[LRU_GEN_ANON], min_seq[LRU_GEN_FILE]);
 | 
					 | 
				
			||||||
 		min_seq[LRU_GEN_FILE] = max(min_seq[LRU_GEN_ANON], lrugen->min_seq[LRU_GEN_FILE]);
 | 
					 | 
				
			||||||
@@ -4329,7 +4329,7 @@ static void inc_max_seq(struct lruvec *l
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	int prev, next;
 | 
					 | 
				
			||||||
 	int type, zone;
 | 
					 | 
				
			||||||
-	struct lru_gen_struct *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
+	struct lru_gen_folio *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 restart:
 | 
					 | 
				
			||||||
 	spin_lock_irq(&lruvec->lru_lock);
 | 
					 | 
				
			||||||
@@ -4389,7 +4389,7 @@ static bool try_to_inc_max_seq(struct lr
 | 
					 | 
				
			||||||
 	bool success;
 | 
					 | 
				
			||||||
 	struct lru_gen_mm_walk *walk;
 | 
					 | 
				
			||||||
 	struct mm_struct *mm = NULL;
 | 
					 | 
				
			||||||
-	struct lru_gen_struct *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
+	struct lru_gen_folio *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	VM_WARN_ON_ONCE(max_seq > READ_ONCE(lrugen->max_seq));
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -4454,7 +4454,7 @@ static bool should_run_aging(struct lruv
 | 
					 | 
				
			||||||
 	unsigned long old = 0;
 | 
					 | 
				
			||||||
 	unsigned long young = 0;
 | 
					 | 
				
			||||||
 	unsigned long total = 0;
 | 
					 | 
				
			||||||
-	struct lru_gen_struct *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
+	struct lru_gen_folio *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
 	struct mem_cgroup *memcg = lruvec_memcg(lruvec);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	for (type = !can_swap; type < ANON_AND_FILE; type++) {
 | 
					 | 
				
			||||||
@@ -4740,7 +4740,7 @@ static bool sort_folio(struct lruvec *lr
 | 
					 | 
				
			||||||
 	int delta = folio_nr_pages(folio);
 | 
					 | 
				
			||||||
 	int refs = folio_lru_refs(folio);
 | 
					 | 
				
			||||||
 	int tier = lru_tier_from_refs(refs);
 | 
					 | 
				
			||||||
-	struct lru_gen_struct *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
+	struct lru_gen_folio *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	VM_WARN_ON_ONCE_FOLIO(gen >= MAX_NR_GENS, folio);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -4848,7 +4848,7 @@ static int scan_folios(struct lruvec *lr
 | 
					 | 
				
			||||||
 	int scanned = 0;
 | 
					 | 
				
			||||||
 	int isolated = 0;
 | 
					 | 
				
			||||||
 	int remaining = MAX_LRU_BATCH;
 | 
					 | 
				
			||||||
-	struct lru_gen_struct *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
+	struct lru_gen_folio *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
 	struct mem_cgroup *memcg = lruvec_memcg(lruvec);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	VM_WARN_ON_ONCE(!list_empty(list));
 | 
					 | 
				
			||||||
@@ -5248,7 +5248,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;
 | 
					 | 
				
			||||||
@@ -5530,7 +5530,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);
 | 
					 | 
				
			||||||
@@ -5580,7 +5580,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);
 | 
					 | 
				
			||||||
@@ -5834,7 +5834,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);
 | 
					 | 
				
			||||||
@ -1,192 +0,0 @@
 | 
				
			|||||||
From 14f9a7a15f3d1af351f30e0438fd747b7ac253b0 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Yu Zhao <yuzhao@google.com>
 | 
					 | 
				
			||||||
Date: Wed, 21 Dec 2022 21:19:01 -0700
 | 
					 | 
				
			||||||
Subject: [PATCH 03/19] UPSTREAM: mm: multi-gen LRU: remove eviction fairness
 | 
					 | 
				
			||||||
 safeguard
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Recall that the eviction consumes the oldest generation: first it
 | 
					 | 
				
			||||||
bucket-sorts folios whose gen counters were updated by the aging and
 | 
					 | 
				
			||||||
reclaims the rest; then it increments lrugen->min_seq.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The current eviction fairness safeguard for global reclaim has a
 | 
					 | 
				
			||||||
dilemma: when there are multiple eligible memcgs, should it continue
 | 
					 | 
				
			||||||
or stop upon meeting the reclaim goal? If it continues, it overshoots
 | 
					 | 
				
			||||||
and increases direct reclaim latency; if it stops, it loses fairness
 | 
					 | 
				
			||||||
between memcgs it has taken memory away from and those it has yet to.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
With memcg LRU, the eviction, while ensuring eventual fairness, will
 | 
					 | 
				
			||||||
stop upon meeting its goal. Therefore the current eviction fairness
 | 
					 | 
				
			||||||
safeguard for global reclaim will not be needed.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Note that memcg LRU only applies to global reclaim. For memcg reclaim,
 | 
					 | 
				
			||||||
the eviction will continue, even if it is overshooting. This becomes
 | 
					 | 
				
			||||||
unconditional due to code simplification.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Link: https://lkml.kernel.org/r/20221222041905.2431096-4-yuzhao@google.com
 | 
					 | 
				
			||||||
Signed-off-by: Yu Zhao <yuzhao@google.com>
 | 
					 | 
				
			||||||
Cc: Johannes Weiner <hannes@cmpxchg.org>
 | 
					 | 
				
			||||||
Cc: Jonathan Corbet <corbet@lwn.net>
 | 
					 | 
				
			||||||
Cc: Michael Larabel <Michael@MichaelLarabel.com>
 | 
					 | 
				
			||||||
Cc: Michal Hocko <mhocko@kernel.org>
 | 
					 | 
				
			||||||
Cc: Mike Rapoport <rppt@kernel.org>
 | 
					 | 
				
			||||||
Cc: Roman Gushchin <roman.gushchin@linux.dev>
 | 
					 | 
				
			||||||
Cc: Suren Baghdasaryan <surenb@google.com>
 | 
					 | 
				
			||||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
 | 
					 | 
				
			||||||
Bug: 274865848
 | 
					 | 
				
			||||||
(cherry picked from commit a579086c99ed70cc4bfc104348dbe3dd8f2787e6)
 | 
					 | 
				
			||||||
Change-Id: I08ac1b3c90e29cafd0566785aaa4bcdb5db7d22c
 | 
					 | 
				
			||||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 mm/vmscan.c | 81 +++++++++++++++--------------------------------------
 | 
					 | 
				
			||||||
 1 file changed, 23 insertions(+), 58 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/mm/vmscan.c
 | 
					 | 
				
			||||||
+++ b/mm/vmscan.c
 | 
					 | 
				
			||||||
@@ -448,6 +448,11 @@ static bool cgroup_reclaim(struct scan_c
 | 
					 | 
				
			||||||
 	return sc->target_mem_cgroup;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static bool global_reclaim(struct scan_control *sc)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return !sc->target_mem_cgroup || mem_cgroup_is_root(sc->target_mem_cgroup);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 /**
 | 
					 | 
				
			||||||
  * writeback_throttling_sane - is the usual dirty throttling mechanism available?
 | 
					 | 
				
			||||||
  * @sc: scan_control in question
 | 
					 | 
				
			||||||
@@ -498,6 +503,11 @@ static bool cgroup_reclaim(struct scan_c
 | 
					 | 
				
			||||||
 	return false;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static bool global_reclaim(struct scan_control *sc)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return true;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static bool writeback_throttling_sane(struct scan_control *sc)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	return true;
 | 
					 | 
				
			||||||
@@ -5005,8 +5015,7 @@ static int isolate_folios(struct lruvec
 | 
					 | 
				
			||||||
 	return scanned;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-static int evict_folios(struct lruvec *lruvec, struct scan_control *sc, int swappiness,
 | 
					 | 
				
			||||||
-			bool *need_swapping)
 | 
					 | 
				
			||||||
+static int evict_folios(struct lruvec *lruvec, struct scan_control *sc, int swappiness)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	int type;
 | 
					 | 
				
			||||||
 	int scanned;
 | 
					 | 
				
			||||||
@@ -5094,9 +5103,6 @@ retry:
 | 
					 | 
				
			||||||
 		goto retry;
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if (need_swapping && type == LRU_GEN_ANON)
 | 
					 | 
				
			||||||
-		*need_swapping = true;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 	return scanned;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -5135,67 +5141,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();
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -5219,7 +5184,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;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -5227,7 +5192,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();
 | 
					 | 
				
			||||||
@@ -5677,7 +5642,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();
 | 
					 | 
				
			||||||
@ -1,294 +0,0 @@
 | 
				
			|||||||
From f3c93d2e37a3c56593d7ccf4f4bcf1b58426fdd8 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Yu Zhao <yuzhao@google.com>
 | 
					 | 
				
			||||||
Date: Wed, 21 Dec 2022 21:19:02 -0700
 | 
					 | 
				
			||||||
Subject: [PATCH 04/19] BACKPORT: mm: multi-gen LRU: remove aging fairness
 | 
					 | 
				
			||||||
 safeguard
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Recall that the aging produces the youngest generation: first it scans
 | 
					 | 
				
			||||||
for accessed folios and updates their gen counters; then it increments
 | 
					 | 
				
			||||||
lrugen->max_seq.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The current aging fairness safeguard for kswapd uses two passes to
 | 
					 | 
				
			||||||
ensure the fairness to multiple eligible memcgs. On the first pass,
 | 
					 | 
				
			||||||
which is shared with the eviction, it checks whether all eligible
 | 
					 | 
				
			||||||
memcgs are low on cold folios. If so, it requires a second pass, on
 | 
					 | 
				
			||||||
which it ages all those memcgs at the same time.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
With memcg LRU, the aging, while ensuring eventual fairness, will run
 | 
					 | 
				
			||||||
when necessary. Therefore the current aging fairness safeguard for
 | 
					 | 
				
			||||||
kswapd will not be needed.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Note that memcg LRU only applies to global reclaim. For memcg reclaim,
 | 
					 | 
				
			||||||
the aging can be unfair to different memcgs, i.e., their
 | 
					 | 
				
			||||||
lrugen->max_seq can be incremented at different paces.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Link: https://lkml.kernel.org/r/20221222041905.2431096-5-yuzhao@google.com
 | 
					 | 
				
			||||||
Signed-off-by: Yu Zhao <yuzhao@google.com>
 | 
					 | 
				
			||||||
Cc: Johannes Weiner <hannes@cmpxchg.org>
 | 
					 | 
				
			||||||
Cc: Jonathan Corbet <corbet@lwn.net>
 | 
					 | 
				
			||||||
Cc: Michael Larabel <Michael@MichaelLarabel.com>
 | 
					 | 
				
			||||||
Cc: Michal Hocko <mhocko@kernel.org>
 | 
					 | 
				
			||||||
Cc: Mike Rapoport <rppt@kernel.org>
 | 
					 | 
				
			||||||
Cc: Roman Gushchin <roman.gushchin@linux.dev>
 | 
					 | 
				
			||||||
Cc: Suren Baghdasaryan <surenb@google.com>
 | 
					 | 
				
			||||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
 | 
					 | 
				
			||||||
Bug: 274865848
 | 
					 | 
				
			||||||
(cherry picked from commit 7348cc91821b0cb24dfb00e578047f68299a50ab)
 | 
					 | 
				
			||||||
[TJ: Resolved conflicts with older function signatures for
 | 
					 | 
				
			||||||
min_cgroup_below_min / min_cgroup_below_low]
 | 
					 | 
				
			||||||
Change-Id: I6e36ecfbaaefbc0a56d9a9d5d7cbe404ed7f57a5
 | 
					 | 
				
			||||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 mm/vmscan.c | 126 ++++++++++++++++++++++++----------------------------
 | 
					 | 
				
			||||||
 1 file changed, 59 insertions(+), 67 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/mm/vmscan.c
 | 
					 | 
				
			||||||
+++ b/mm/vmscan.c
 | 
					 | 
				
			||||||
@@ -136,7 +136,6 @@ struct scan_control {
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #ifdef CONFIG_LRU_GEN
 | 
					 | 
				
			||||||
 	/* help kswapd make better choices among multiple memcgs */
 | 
					 | 
				
			||||||
-	unsigned int memcgs_need_aging:1;
 | 
					 | 
				
			||||||
 	unsigned long last_reclaimed;
 | 
					 | 
				
			||||||
 #endif
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -4457,7 +4456,7 @@ done:
 | 
					 | 
				
			||||||
 	return true;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq, unsigned long *min_seq,
 | 
					 | 
				
			||||||
+static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq,
 | 
					 | 
				
			||||||
 			     struct scan_control *sc, bool can_swap, unsigned long *nr_to_scan)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	int gen, type, zone;
 | 
					 | 
				
			||||||
@@ -4466,6 +4465,13 @@ static bool should_run_aging(struct lruv
 | 
					 | 
				
			||||||
 	unsigned long total = 0;
 | 
					 | 
				
			||||||
 	struct lru_gen_folio *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
 	struct mem_cgroup *memcg = lruvec_memcg(lruvec);
 | 
					 | 
				
			||||||
+	DEFINE_MIN_SEQ(lruvec);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* whether this lruvec is completely out of cold folios */
 | 
					 | 
				
			||||||
+	if (min_seq[!can_swap] + MIN_NR_GENS > max_seq) {
 | 
					 | 
				
			||||||
+		*nr_to_scan = 0;
 | 
					 | 
				
			||||||
+		return true;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	for (type = !can_swap; type < ANON_AND_FILE; type++) {
 | 
					 | 
				
			||||||
 		unsigned long seq;
 | 
					 | 
				
			||||||
@@ -4494,8 +4500,6 @@ static bool should_run_aging(struct lruv
 | 
					 | 
				
			||||||
 	 * stalls when the number of generations reaches MIN_NR_GENS. Hence, the
 | 
					 | 
				
			||||||
 	 * ideal number of generations is MIN_NR_GENS+1.
 | 
					 | 
				
			||||||
 	 */
 | 
					 | 
				
			||||||
-	if (min_seq[!can_swap] + MIN_NR_GENS > max_seq)
 | 
					 | 
				
			||||||
-		return true;
 | 
					 | 
				
			||||||
 	if (min_seq[!can_swap] + MIN_NR_GENS < max_seq)
 | 
					 | 
				
			||||||
 		return false;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -4514,40 +4518,54 @@ static bool should_run_aging(struct lruv
 | 
					 | 
				
			||||||
 	return false;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-static bool age_lruvec(struct lruvec *lruvec, struct scan_control *sc, unsigned long min_ttl)
 | 
					 | 
				
			||||||
+static bool lruvec_is_sizable(struct lruvec *lruvec, struct scan_control *sc)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
-	bool need_aging;
 | 
					 | 
				
			||||||
-	unsigned long nr_to_scan;
 | 
					 | 
				
			||||||
-	int swappiness = get_swappiness(lruvec, sc);
 | 
					 | 
				
			||||||
+	int gen, type, zone;
 | 
					 | 
				
			||||||
+	unsigned long total = 0;
 | 
					 | 
				
			||||||
+	bool can_swap = get_swappiness(lruvec, sc);
 | 
					 | 
				
			||||||
+	struct lru_gen_folio *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
 	struct mem_cgroup *memcg = lruvec_memcg(lruvec);
 | 
					 | 
				
			||||||
 	DEFINE_MAX_SEQ(lruvec);
 | 
					 | 
				
			||||||
 	DEFINE_MIN_SEQ(lruvec);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	VM_WARN_ON_ONCE(sc->memcg_low_reclaim);
 | 
					 | 
				
			||||||
+	for (type = !can_swap; type < ANON_AND_FILE; type++) {
 | 
					 | 
				
			||||||
+		unsigned long seq;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	mem_cgroup_calculate_protection(NULL, memcg);
 | 
					 | 
				
			||||||
+		for (seq = min_seq[type]; seq <= max_seq; seq++) {
 | 
					 | 
				
			||||||
+			gen = lru_gen_from_seq(seq);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if (mem_cgroup_below_min(memcg))
 | 
					 | 
				
			||||||
-		return false;
 | 
					 | 
				
			||||||
+			for (zone = 0; zone < MAX_NR_ZONES; zone++)
 | 
					 | 
				
			||||||
+				total += max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L);
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	need_aging = should_run_aging(lruvec, max_seq, min_seq, sc, swappiness, &nr_to_scan);
 | 
					 | 
				
			||||||
+	/* whether the size is big enough to be helpful */
 | 
					 | 
				
			||||||
+	return mem_cgroup_online(memcg) ? (total >> sc->priority) : total;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if (min_ttl) {
 | 
					 | 
				
			||||||
-		int gen = lru_gen_from_seq(min_seq[LRU_GEN_FILE]);
 | 
					 | 
				
			||||||
-		unsigned long birth = READ_ONCE(lruvec->lrugen.timestamps[gen]);
 | 
					 | 
				
			||||||
+static bool lruvec_is_reclaimable(struct lruvec *lruvec, struct scan_control *sc,
 | 
					 | 
				
			||||||
+				  unsigned long min_ttl)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int gen;
 | 
					 | 
				
			||||||
+	unsigned long birth;
 | 
					 | 
				
			||||||
+	struct mem_cgroup *memcg = lruvec_memcg(lruvec);
 | 
					 | 
				
			||||||
+	DEFINE_MIN_SEQ(lruvec);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-		if (time_is_after_jiffies(birth + min_ttl))
 | 
					 | 
				
			||||||
-			return false;
 | 
					 | 
				
			||||||
+	VM_WARN_ON_ONCE(sc->memcg_low_reclaim);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-		/* the size is likely too small to be helpful */
 | 
					 | 
				
			||||||
-		if (!nr_to_scan && sc->priority != DEF_PRIORITY)
 | 
					 | 
				
			||||||
-			return false;
 | 
					 | 
				
			||||||
-	}
 | 
					 | 
				
			||||||
+	/* see the comment on lru_gen_folio */
 | 
					 | 
				
			||||||
+	gen = lru_gen_from_seq(min_seq[LRU_GEN_FILE]);
 | 
					 | 
				
			||||||
+	birth = READ_ONCE(lruvec->lrugen.timestamps[gen]);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if (need_aging)
 | 
					 | 
				
			||||||
-		try_to_inc_max_seq(lruvec, max_seq, sc, swappiness, false);
 | 
					 | 
				
			||||||
+	if (time_is_after_jiffies(birth + min_ttl))
 | 
					 | 
				
			||||||
+		return false;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	return true;
 | 
					 | 
				
			||||||
+	if (!lruvec_is_sizable(lruvec, sc))
 | 
					 | 
				
			||||||
+		return false;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	mem_cgroup_calculate_protection(NULL, memcg);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return !mem_cgroup_below_min(memcg);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /* to protect the working set of the last N jiffies */
 | 
					 | 
				
			||||||
@@ -4556,46 +4574,32 @@ static unsigned long lru_gen_min_ttl __r
 | 
					 | 
				
			||||||
 static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_control *sc)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	struct mem_cgroup *memcg;
 | 
					 | 
				
			||||||
-	bool success = false;
 | 
					 | 
				
			||||||
 	unsigned long min_ttl = READ_ONCE(lru_gen_min_ttl);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	VM_WARN_ON_ONCE(!current_is_kswapd());
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	sc->last_reclaimed = sc->nr_reclaimed;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	/*
 | 
					 | 
				
			||||||
-	 * To reduce the chance of going into the aging path, which can be
 | 
					 | 
				
			||||||
-	 * costly, optimistically skip it if the flag below was cleared in the
 | 
					 | 
				
			||||||
-	 * eviction path. This improves the overall performance when multiple
 | 
					 | 
				
			||||||
-	 * memcgs are available.
 | 
					 | 
				
			||||||
-	 */
 | 
					 | 
				
			||||||
-	if (!sc->memcgs_need_aging) {
 | 
					 | 
				
			||||||
-		sc->memcgs_need_aging = true;
 | 
					 | 
				
			||||||
+	/* check the order to exclude compaction-induced reclaim */
 | 
					 | 
				
			||||||
+	if (!min_ttl || sc->order || sc->priority == DEF_PRIORITY)
 | 
					 | 
				
			||||||
 		return;
 | 
					 | 
				
			||||||
-	}
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	set_mm_walk(pgdat);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	memcg = mem_cgroup_iter(NULL, NULL, NULL);
 | 
					 | 
				
			||||||
 	do {
 | 
					 | 
				
			||||||
 		struct lruvec *lruvec = mem_cgroup_lruvec(memcg, pgdat);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-		if (age_lruvec(lruvec, sc, min_ttl))
 | 
					 | 
				
			||||||
-			success = true;
 | 
					 | 
				
			||||||
+		if (lruvec_is_reclaimable(lruvec, sc, min_ttl)) {
 | 
					 | 
				
			||||||
+			mem_cgroup_iter_break(NULL, memcg);
 | 
					 | 
				
			||||||
+			return;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 		cond_resched();
 | 
					 | 
				
			||||||
 	} while ((memcg = mem_cgroup_iter(NULL, memcg, NULL)));
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	clear_mm_walk();
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	/* check the order to exclude compaction-induced reclaim */
 | 
					 | 
				
			||||||
-	if (success || !min_ttl || sc->order)
 | 
					 | 
				
			||||||
-		return;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 	/*
 | 
					 | 
				
			||||||
 	 * The main goal is to OOM kill if every generation from all memcgs is
 | 
					 | 
				
			||||||
 	 * younger than min_ttl. However, another possibility is all memcgs are
 | 
					 | 
				
			||||||
-	 * either below min or empty.
 | 
					 | 
				
			||||||
+	 * either too small or below min.
 | 
					 | 
				
			||||||
 	 */
 | 
					 | 
				
			||||||
 	if (mutex_trylock(&oom_lock)) {
 | 
					 | 
				
			||||||
 		struct oom_control oc = {
 | 
					 | 
				
			||||||
@@ -5112,33 +5116,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)
 | 
					 | 
				
			||||||
@@ -5157,9 +5155,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();
 | 
					 | 
				
			||||||
@@ -5180,13 +5176,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)
 | 
					 | 
				
			||||||
@@ -5198,10 +5194,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);
 | 
					 | 
				
			||||||
@ -1,166 +0,0 @@
 | 
				
			|||||||
From eca3858631e0cbad2ca6e40f788892749428e4cb Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Yu Zhao <yuzhao@google.com>
 | 
					 | 
				
			||||||
Date: Wed, 21 Dec 2022 21:19:03 -0700
 | 
					 | 
				
			||||||
Subject: [PATCH 05/19] UPSTREAM: mm: multi-gen LRU: shuffle should_run_aging()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Move should_run_aging() next to its only caller left.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Link: https://lkml.kernel.org/r/20221222041905.2431096-6-yuzhao@google.com
 | 
					 | 
				
			||||||
Cc: Johannes Weiner <hannes@cmpxchg.org>
 | 
					 | 
				
			||||||
Cc: Jonathan Corbet <corbet@lwn.net>
 | 
					 | 
				
			||||||
Cc: Michael Larabel <Michael@MichaelLarabel.com>
 | 
					 | 
				
			||||||
Cc: Michal Hocko <mhocko@kernel.org>
 | 
					 | 
				
			||||||
Cc: Mike Rapoport <rppt@kernel.org>
 | 
					 | 
				
			||||||
Cc: Roman Gushchin <roman.gushchin@linux.dev>
 | 
					 | 
				
			||||||
Cc: Suren Baghdasaryan <surenb@google.com>
 | 
					 | 
				
			||||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
 | 
					 | 
				
			||||||
Bug: 274865848
 | 
					 | 
				
			||||||
(cherry picked from commit 77d4459a4a1a472b7309e475f962dda87d950abd)
 | 
					 | 
				
			||||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
 | 
					 | 
				
			||||||
Change-Id: I3b0383fe16b93a783b4d8c0b3a0b325160392576
 | 
					 | 
				
			||||||
Signed-off-by: Yu Zhao <yuzhao@google.com>
 | 
					 | 
				
			||||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 mm/vmscan.c | 124 ++++++++++++++++++++++++++--------------------------
 | 
					 | 
				
			||||||
 1 file changed, 62 insertions(+), 62 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/mm/vmscan.c
 | 
					 | 
				
			||||||
+++ b/mm/vmscan.c
 | 
					 | 
				
			||||||
@@ -4456,68 +4456,6 @@ done:
 | 
					 | 
				
			||||||
 	return true;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq,
 | 
					 | 
				
			||||||
-			     struct scan_control *sc, bool can_swap, unsigned long *nr_to_scan)
 | 
					 | 
				
			||||||
-{
 | 
					 | 
				
			||||||
-	int gen, type, zone;
 | 
					 | 
				
			||||||
-	unsigned long old = 0;
 | 
					 | 
				
			||||||
-	unsigned long young = 0;
 | 
					 | 
				
			||||||
-	unsigned long total = 0;
 | 
					 | 
				
			||||||
-	struct lru_gen_folio *lrugen = &lruvec->lrugen;
 | 
					 | 
				
			||||||
-	struct mem_cgroup *memcg = lruvec_memcg(lruvec);
 | 
					 | 
				
			||||||
-	DEFINE_MIN_SEQ(lruvec);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	/* whether this lruvec is completely out of cold folios */
 | 
					 | 
				
			||||||
-	if (min_seq[!can_swap] + MIN_NR_GENS > max_seq) {
 | 
					 | 
				
			||||||
-		*nr_to_scan = 0;
 | 
					 | 
				
			||||||
-		return true;
 | 
					 | 
				
			||||||
-	}
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	for (type = !can_swap; type < ANON_AND_FILE; type++) {
 | 
					 | 
				
			||||||
-		unsigned long seq;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-		for (seq = min_seq[type]; seq <= max_seq; seq++) {
 | 
					 | 
				
			||||||
-			unsigned long size = 0;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-			gen = lru_gen_from_seq(seq);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-			for (zone = 0; zone < MAX_NR_ZONES; zone++)
 | 
					 | 
				
			||||||
-				size += max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-			total += size;
 | 
					 | 
				
			||||||
-			if (seq == max_seq)
 | 
					 | 
				
			||||||
-				young += size;
 | 
					 | 
				
			||||||
-			else if (seq + MIN_NR_GENS == max_seq)
 | 
					 | 
				
			||||||
-				old += size;
 | 
					 | 
				
			||||||
-		}
 | 
					 | 
				
			||||||
-	}
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	/* try to scrape all its memory if this memcg was deleted */
 | 
					 | 
				
			||||||
-	*nr_to_scan = mem_cgroup_online(memcg) ? (total >> sc->priority) : total;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	/*
 | 
					 | 
				
			||||||
-	 * The aging tries to be lazy to reduce the overhead, while the eviction
 | 
					 | 
				
			||||||
-	 * stalls when the number of generations reaches MIN_NR_GENS. Hence, the
 | 
					 | 
				
			||||||
-	 * ideal number of generations is MIN_NR_GENS+1.
 | 
					 | 
				
			||||||
-	 */
 | 
					 | 
				
			||||||
-	if (min_seq[!can_swap] + MIN_NR_GENS < max_seq)
 | 
					 | 
				
			||||||
-		return false;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	/*
 | 
					 | 
				
			||||||
-	 * It's also ideal to spread pages out evenly, i.e., 1/(MIN_NR_GENS+1)
 | 
					 | 
				
			||||||
-	 * of the total number of pages for each generation. A reasonable range
 | 
					 | 
				
			||||||
-	 * for this average portion is [1/MIN_NR_GENS, 1/(MIN_NR_GENS+2)]. The
 | 
					 | 
				
			||||||
-	 * aging cares about the upper bound of hot pages, while the eviction
 | 
					 | 
				
			||||||
-	 * cares about the lower bound of cold pages.
 | 
					 | 
				
			||||||
-	 */
 | 
					 | 
				
			||||||
-	if (young * MIN_NR_GENS > total)
 | 
					 | 
				
			||||||
-		return true;
 | 
					 | 
				
			||||||
-	if (old * (MIN_NR_GENS + 2) < total)
 | 
					 | 
				
			||||||
-		return true;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	return false;
 | 
					 | 
				
			||||||
-}
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 static bool lruvec_is_sizable(struct lruvec *lruvec, struct scan_control *sc)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	int gen, type, zone;
 | 
					 | 
				
			||||||
@@ -5110,6 +5048,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
 | 
					 | 
				
			||||||
@ -1,876 +0,0 @@
 | 
				
			|||||||
From 8ee8571e47aa75221e5fbd4c9c7802fc4244c346 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Yu Zhao <yuzhao@google.com>
 | 
					 | 
				
			||||||
Date: Wed, 21 Dec 2022 21:19:04 -0700
 | 
					 | 
				
			||||||
Subject: [PATCH 06/19] BACKPORT: mm: multi-gen LRU: per-node lru_gen_folio
 | 
					 | 
				
			||||||
 lists
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
For each node, memcgs are divided into two generations: the old and
 | 
					 | 
				
			||||||
the young. For each generation, memcgs are randomly sharded into
 | 
					 | 
				
			||||||
multiple bins to improve scalability. For each bin, an RCU hlist_nulls
 | 
					 | 
				
			||||||
is virtually divided into three segments: the head, the tail and the
 | 
					 | 
				
			||||||
default.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
An onlining memcg is added to the tail of a random bin in the old
 | 
					 | 
				
			||||||
generation. The eviction starts at the head of a random bin in the old
 | 
					 | 
				
			||||||
generation. The per-node memcg generation counter, whose reminder (mod
 | 
					 | 
				
			||||||
2) indexes the old generation, is incremented when all its bins become
 | 
					 | 
				
			||||||
empty.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
There are four operations:
 | 
					 | 
				
			||||||
1. MEMCG_LRU_HEAD, which moves an memcg to the head of a random bin in
 | 
					 | 
				
			||||||
   its current generation (old or young) and updates its "seg" to
 | 
					 | 
				
			||||||
   "head";
 | 
					 | 
				
			||||||
2. MEMCG_LRU_TAIL, which moves an memcg to the tail of a random bin in
 | 
					 | 
				
			||||||
   its current generation (old or young) and updates its "seg" to
 | 
					 | 
				
			||||||
   "tail";
 | 
					 | 
				
			||||||
3. MEMCG_LRU_OLD, which moves an memcg to the head of a random bin in
 | 
					 | 
				
			||||||
   the old generation, updates its "gen" to "old" and resets its "seg"
 | 
					 | 
				
			||||||
   to "default";
 | 
					 | 
				
			||||||
4. MEMCG_LRU_YOUNG, which moves an memcg to the tail of a random bin
 | 
					 | 
				
			||||||
   in the young generation, updates its "gen" to "young" and resets
 | 
					 | 
				
			||||||
   its "seg" to "default".
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The events that trigger the above operations are:
 | 
					 | 
				
			||||||
1. Exceeding the soft limit, which triggers MEMCG_LRU_HEAD;
 | 
					 | 
				
			||||||
2. The first attempt to reclaim an memcg below low, which triggers
 | 
					 | 
				
			||||||
   MEMCG_LRU_TAIL;
 | 
					 | 
				
			||||||
3. The first attempt to reclaim an memcg below reclaimable size
 | 
					 | 
				
			||||||
   threshold, which triggers MEMCG_LRU_TAIL;
 | 
					 | 
				
			||||||
4. The second attempt to reclaim an memcg below reclaimable size
 | 
					 | 
				
			||||||
   threshold, which triggers MEMCG_LRU_YOUNG;
 | 
					 | 
				
			||||||
5. Attempting to reclaim an memcg below min, which triggers
 | 
					 | 
				
			||||||
   MEMCG_LRU_YOUNG;
 | 
					 | 
				
			||||||
6. Finishing the aging on the eviction path, which triggers
 | 
					 | 
				
			||||||
   MEMCG_LRU_YOUNG;
 | 
					 | 
				
			||||||
7. Offlining an memcg, which triggers MEMCG_LRU_OLD.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Note that memcg LRU only applies to global reclaim, and the
 | 
					 | 
				
			||||||
round-robin incrementing of their max_seq counters ensures the
 | 
					 | 
				
			||||||
eventual fairness to all eligible memcgs. For memcg reclaim, it still
 | 
					 | 
				
			||||||
relies on mem_cgroup_iter().
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Link: https://lkml.kernel.org/r/20221222041905.2431096-7-yuzhao@google.com
 | 
					 | 
				
			||||||
Signed-off-by: Yu Zhao <yuzhao@google.com>
 | 
					 | 
				
			||||||
Cc: Johannes Weiner <hannes@cmpxchg.org>
 | 
					 | 
				
			||||||
Cc: Jonathan Corbet <corbet@lwn.net>
 | 
					 | 
				
			||||||
Cc: Michael Larabel <Michael@MichaelLarabel.com>
 | 
					 | 
				
			||||||
Cc: Michal Hocko <mhocko@kernel.org>
 | 
					 | 
				
			||||||
Cc: Mike Rapoport <rppt@kernel.org>
 | 
					 | 
				
			||||||
Cc: Roman Gushchin <roman.gushchin@linux.dev>
 | 
					 | 
				
			||||||
Cc: Suren Baghdasaryan <surenb@google.com>
 | 
					 | 
				
			||||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
 | 
					 | 
				
			||||||
Bug: 274865848
 | 
					 | 
				
			||||||
(cherry picked from commit e4dde56cd208674ce899b47589f263499e5b8cdc)
 | 
					 | 
				
			||||||
[TJ: Resolved conflicts with older function signatures for
 | 
					 | 
				
			||||||
min_cgroup_below_min / min_cgroup_below_low and includes]
 | 
					 | 
				
			||||||
Change-Id: Idc8a0f635e035d72dd911f807d1224cb47cbd655
 | 
					 | 
				
			||||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 include/linux/memcontrol.h |  10 +
 | 
					 | 
				
			||||||
 include/linux/mm_inline.h  |  17 ++
 | 
					 | 
				
			||||||
 include/linux/mmzone.h     | 117 +++++++++++-
 | 
					 | 
				
			||||||
 mm/memcontrol.c            |  16 ++
 | 
					 | 
				
			||||||
 mm/page_alloc.c            |   1 +
 | 
					 | 
				
			||||||
 mm/vmscan.c                | 374 +++++++++++++++++++++++++++++++++----
 | 
					 | 
				
			||||||
 6 files changed, 500 insertions(+), 35 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/include/linux/memcontrol.h
 | 
					 | 
				
			||||||
+++ b/include/linux/memcontrol.h
 | 
					 | 
				
			||||||
@@ -795,6 +795,11 @@ static inline void obj_cgroup_put(struct
 | 
					 | 
				
			||||||
 	percpu_ref_put(&objcg->refcnt);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static inline bool mem_cgroup_tryget(struct mem_cgroup *memcg)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return !memcg || css_tryget(&memcg->css);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static inline void mem_cgroup_put(struct mem_cgroup *memcg)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	if (memcg)
 | 
					 | 
				
			||||||
@@ -1295,6 +1300,11 @@ static inline void obj_cgroup_put(struct
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static inline bool mem_cgroup_tryget(struct mem_cgroup *memcg)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return true;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static inline void mem_cgroup_put(struct mem_cgroup *memcg)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
--- a/include/linux/mm_inline.h
 | 
					 | 
				
			||||||
+++ b/include/linux/mm_inline.h
 | 
					 | 
				
			||||||
@@ -122,6 +122,18 @@ static inline bool lru_gen_in_fault(void
 | 
					 | 
				
			||||||
 	return current->in_lru_fault;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+#ifdef CONFIG_MEMCG
 | 
					 | 
				
			||||||
+static inline int lru_gen_memcg_seg(struct lruvec *lruvec)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return READ_ONCE(lruvec->lrugen.seg);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+#else
 | 
					 | 
				
			||||||
+static inline int lru_gen_memcg_seg(struct lruvec *lruvec)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static inline int lru_gen_from_seq(unsigned long seq)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	return seq % MAX_NR_GENS;
 | 
					 | 
				
			||||||
@@ -302,6 +314,11 @@ static inline bool lru_gen_in_fault(void
 | 
					 | 
				
			||||||
 	return false;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static inline int lru_gen_memcg_seg(struct lruvec *lruvec)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static inline bool lru_gen_add_folio(struct lruvec *lruvec, struct folio *folio, bool reclaiming)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	return false;
 | 
					 | 
				
			||||||
--- a/include/linux/mmzone.h
 | 
					 | 
				
			||||||
+++ b/include/linux/mmzone.h
 | 
					 | 
				
			||||||
@@ -7,6 +7,7 @@
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #include <linux/spinlock.h>
 | 
					 | 
				
			||||||
 #include <linux/list.h>
 | 
					 | 
				
			||||||
+#include <linux/list_nulls.h>
 | 
					 | 
				
			||||||
 #include <linux/wait.h>
 | 
					 | 
				
			||||||
 #include <linux/bitops.h>
 | 
					 | 
				
			||||||
 #include <linux/cache.h>
 | 
					 | 
				
			||||||
@@ -367,6 +368,15 @@ struct page_vma_mapped_walk;
 | 
					 | 
				
			||||||
 #define LRU_GEN_MASK		((BIT(LRU_GEN_WIDTH) - 1) << LRU_GEN_PGOFF)
 | 
					 | 
				
			||||||
 #define LRU_REFS_MASK		((BIT(LRU_REFS_WIDTH) - 1) << LRU_REFS_PGOFF)
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+/* see the comment on MEMCG_NR_GENS */
 | 
					 | 
				
			||||||
+enum {
 | 
					 | 
				
			||||||
+	MEMCG_LRU_NOP,
 | 
					 | 
				
			||||||
+	MEMCG_LRU_HEAD,
 | 
					 | 
				
			||||||
+	MEMCG_LRU_TAIL,
 | 
					 | 
				
			||||||
+	MEMCG_LRU_OLD,
 | 
					 | 
				
			||||||
+	MEMCG_LRU_YOUNG,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 #ifdef CONFIG_LRU_GEN
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 enum {
 | 
					 | 
				
			||||||
@@ -426,6 +436,14 @@ struct lru_gen_folio {
 | 
					 | 
				
			||||||
 	atomic_long_t refaulted[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS];
 | 
					 | 
				
			||||||
 	/* whether the multi-gen LRU is enabled */
 | 
					 | 
				
			||||||
 	bool enabled;
 | 
					 | 
				
			||||||
+#ifdef CONFIG_MEMCG
 | 
					 | 
				
			||||||
+	/* the memcg generation this lru_gen_folio belongs to */
 | 
					 | 
				
			||||||
+	u8 gen;
 | 
					 | 
				
			||||||
+	/* the list segment this lru_gen_folio belongs to */
 | 
					 | 
				
			||||||
+	u8 seg;
 | 
					 | 
				
			||||||
+	/* per-node lru_gen_folio list for global reclaim */
 | 
					 | 
				
			||||||
+	struct hlist_nulls_node list;
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 enum {
 | 
					 | 
				
			||||||
@@ -479,12 +497,87 @@ void lru_gen_init_lruvec(struct lruvec *
 | 
					 | 
				
			||||||
 void lru_gen_look_around(struct page_vma_mapped_walk *pvmw);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #ifdef CONFIG_MEMCG
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ * For each node, memcgs are divided into two generations: the old and the
 | 
					 | 
				
			||||||
+ * young. For each generation, memcgs are randomly sharded into multiple bins
 | 
					 | 
				
			||||||
+ * to improve scalability. For each bin, the hlist_nulls is virtually divided
 | 
					 | 
				
			||||||
+ * into three segments: the head, the tail and the default.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * An onlining memcg is added to the tail of a random bin in the old generation.
 | 
					 | 
				
			||||||
+ * The eviction starts at the head of a random bin in the old generation. The
 | 
					 | 
				
			||||||
+ * per-node memcg generation counter, whose reminder (mod MEMCG_NR_GENS) indexes
 | 
					 | 
				
			||||||
+ * the old generation, is incremented when all its bins become empty.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * There are four operations:
 | 
					 | 
				
			||||||
+ * 1. MEMCG_LRU_HEAD, which moves an memcg to the head of a random bin in its
 | 
					 | 
				
			||||||
+ *    current generation (old or young) and updates its "seg" to "head";
 | 
					 | 
				
			||||||
+ * 2. MEMCG_LRU_TAIL, which moves an memcg to the tail of a random bin in its
 | 
					 | 
				
			||||||
+ *    current generation (old or young) and updates its "seg" to "tail";
 | 
					 | 
				
			||||||
+ * 3. MEMCG_LRU_OLD, which moves an memcg to the head of a random bin in the old
 | 
					 | 
				
			||||||
+ *    generation, updates its "gen" to "old" and resets its "seg" to "default";
 | 
					 | 
				
			||||||
+ * 4. MEMCG_LRU_YOUNG, which moves an memcg to the tail of a random bin in the
 | 
					 | 
				
			||||||
+ *    young generation, updates its "gen" to "young" and resets its "seg" to
 | 
					 | 
				
			||||||
+ *    "default".
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * The events that trigger the above operations are:
 | 
					 | 
				
			||||||
+ * 1. Exceeding the soft limit, which triggers MEMCG_LRU_HEAD;
 | 
					 | 
				
			||||||
+ * 2. The first attempt to reclaim an memcg below low, which triggers
 | 
					 | 
				
			||||||
+ *    MEMCG_LRU_TAIL;
 | 
					 | 
				
			||||||
+ * 3. The first attempt to reclaim an memcg below reclaimable size threshold,
 | 
					 | 
				
			||||||
+ *    which triggers MEMCG_LRU_TAIL;
 | 
					 | 
				
			||||||
+ * 4. The second attempt to reclaim an memcg below reclaimable size threshold,
 | 
					 | 
				
			||||||
+ *    which triggers MEMCG_LRU_YOUNG;
 | 
					 | 
				
			||||||
+ * 5. Attempting to reclaim an memcg below min, which triggers MEMCG_LRU_YOUNG;
 | 
					 | 
				
			||||||
+ * 6. Finishing the aging on the eviction path, which triggers MEMCG_LRU_YOUNG;
 | 
					 | 
				
			||||||
+ * 7. Offlining an memcg, which triggers MEMCG_LRU_OLD.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Note that memcg LRU only applies to global reclaim, and the round-robin
 | 
					 | 
				
			||||||
+ * incrementing of their max_seq counters ensures the eventual fairness to all
 | 
					 | 
				
			||||||
+ * eligible memcgs. For memcg reclaim, it still relies on mem_cgroup_iter().
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+#define MEMCG_NR_GENS	2
 | 
					 | 
				
			||||||
+#define MEMCG_NR_BINS	8
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+struct lru_gen_memcg {
 | 
					 | 
				
			||||||
+	/* the per-node memcg generation counter */
 | 
					 | 
				
			||||||
+	unsigned long seq;
 | 
					 | 
				
			||||||
+	/* each memcg has one lru_gen_folio per node */
 | 
					 | 
				
			||||||
+	unsigned long nr_memcgs[MEMCG_NR_GENS];
 | 
					 | 
				
			||||||
+	/* per-node lru_gen_folio list for global reclaim */
 | 
					 | 
				
			||||||
+	struct hlist_nulls_head	fifo[MEMCG_NR_GENS][MEMCG_NR_BINS];
 | 
					 | 
				
			||||||
+	/* protects the above */
 | 
					 | 
				
			||||||
+	spinlock_t lock;
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+void lru_gen_init_pgdat(struct pglist_data *pgdat);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 void lru_gen_init_memcg(struct mem_cgroup *memcg);
 | 
					 | 
				
			||||||
 void lru_gen_exit_memcg(struct mem_cgroup *memcg);
 | 
					 | 
				
			||||||
-#endif
 | 
					 | 
				
			||||||
+void lru_gen_online_memcg(struct mem_cgroup *memcg);
 | 
					 | 
				
			||||||
+void lru_gen_offline_memcg(struct mem_cgroup *memcg);
 | 
					 | 
				
			||||||
+void lru_gen_release_memcg(struct mem_cgroup *memcg);
 | 
					 | 
				
			||||||
+void lru_gen_rotate_memcg(struct lruvec *lruvec, int op);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#else /* !CONFIG_MEMCG */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define MEMCG_NR_GENS	1
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+struct lru_gen_memcg {
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static inline void lru_gen_init_pgdat(struct pglist_data *pgdat)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#endif /* CONFIG_MEMCG */
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #else /* !CONFIG_LRU_GEN */
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static inline void lru_gen_init_pgdat(struct pglist_data *pgdat)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static inline void lru_gen_init_lruvec(struct lruvec *lruvec)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
@@ -494,6 +587,7 @@ static inline void lru_gen_look_around(s
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #ifdef CONFIG_MEMCG
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static inline void lru_gen_init_memcg(struct mem_cgroup *memcg)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
@@ -501,7 +595,24 @@ static inline void lru_gen_init_memcg(st
 | 
					 | 
				
			||||||
 static inline void lru_gen_exit_memcg(struct mem_cgroup *memcg)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
-#endif
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static inline void lru_gen_online_memcg(struct mem_cgroup *memcg)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static inline void lru_gen_offline_memcg(struct mem_cgroup *memcg)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static inline void lru_gen_release_memcg(struct mem_cgroup *memcg)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static inline void lru_gen_rotate_memcg(struct lruvec *lruvec, int op)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#endif /* CONFIG_MEMCG */
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #endif /* CONFIG_LRU_GEN */
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -1218,6 +1329,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;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -5390,6 +5403,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);
 | 
					 | 
				
			||||||
@@ -5421,6 +5435,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);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -5432,6 +5447,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
 | 
					 | 
				
			||||||
@@ -7956,6 +7956,7 @@ static void __init free_area_init_node(i
 | 
					 | 
				
			||||||
 	pgdat_set_deferred_range(pgdat);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	free_area_init_core(pgdat);
 | 
					 | 
				
			||||||
+	lru_gen_init_pgdat(pgdat);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static void __init free_area_init_memoryless_node(int nid)
 | 
					 | 
				
			||||||
--- a/mm/vmscan.c
 | 
					 | 
				
			||||||
+++ b/mm/vmscan.c
 | 
					 | 
				
			||||||
@@ -54,6 +54,8 @@
 | 
					 | 
				
			||||||
 #include <linux/shmem_fs.h>
 | 
					 | 
				
			||||||
 #include <linux/ctype.h>
 | 
					 | 
				
			||||||
 #include <linux/debugfs.h>
 | 
					 | 
				
			||||||
+#include <linux/rculist_nulls.h>
 | 
					 | 
				
			||||||
+#include <linux/random.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #include <asm/tlbflush.h>
 | 
					 | 
				
			||||||
 #include <asm/div64.h>
 | 
					 | 
				
			||||||
@@ -134,11 +136,6 @@ struct scan_control {
 | 
					 | 
				
			||||||
 	/* Always discard instead of demoting to lower tier memory */
 | 
					 | 
				
			||||||
 	unsigned int no_demotion:1;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-#ifdef CONFIG_LRU_GEN
 | 
					 | 
				
			||||||
-	/* help kswapd make better choices among multiple memcgs */
 | 
					 | 
				
			||||||
-	unsigned long last_reclaimed;
 | 
					 | 
				
			||||||
-#endif
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 	/* Allocation order */
 | 
					 | 
				
			||||||
 	s8 order;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -3160,6 +3157,9 @@ DEFINE_STATIC_KEY_ARRAY_FALSE(lru_gen_ca
 | 
					 | 
				
			||||||
 		for ((type) = 0; (type) < ANON_AND_FILE; (type)++)	\
 | 
					 | 
				
			||||||
 			for ((zone) = 0; (zone) < MAX_NR_ZONES; (zone)++)
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+#define get_memcg_gen(seq)	((seq) % MEMCG_NR_GENS)
 | 
					 | 
				
			||||||
+#define get_memcg_bin(bin)	((bin) % MEMCG_NR_BINS)
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static struct lruvec *get_lruvec(struct mem_cgroup *memcg, int nid)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	struct pglist_data *pgdat = NODE_DATA(nid);
 | 
					 | 
				
			||||||
@@ -4442,8 +4442,7 @@ done:
 | 
					 | 
				
			||||||
 		if (sc->priority <= DEF_PRIORITY - 2)
 | 
					 | 
				
			||||||
 			wait_event_killable(lruvec->mm_state.wait,
 | 
					 | 
				
			||||||
 					    max_seq < READ_ONCE(lrugen->max_seq));
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-		return max_seq < READ_ONCE(lrugen->max_seq);
 | 
					 | 
				
			||||||
+		return false;
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	VM_WARN_ON_ONCE(max_seq != READ_ONCE(lrugen->max_seq));
 | 
					 | 
				
			||||||
@@ -4516,8 +4515,6 @@ static void lru_gen_age_node(struct pgli
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	VM_WARN_ON_ONCE(!current_is_kswapd());
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	sc->last_reclaimed = sc->nr_reclaimed;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 	/* check the order to exclude compaction-induced reclaim */
 | 
					 | 
				
			||||||
 	if (!min_ttl || sc->order || sc->priority == DEF_PRIORITY)
 | 
					 | 
				
			||||||
 		return;
 | 
					 | 
				
			||||||
@@ -5115,8 +5112,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);
 | 
					 | 
				
			||||||
@@ -5133,10 +5129,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)
 | 
					 | 
				
			||||||
@@ -5145,29 +5139,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);
 | 
					 | 
				
			||||||
@@ -5177,7 +5160,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);
 | 
					 | 
				
			||||||
@@ -5194,10 +5177,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
 | 
					 | 
				
			||||||
@@ -5655,11 +5879,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)
 | 
					 | 
				
			||||||
@@ -5807,6 +6031,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);
 | 
					 | 
				
			||||||
@@ -5830,7 +6067,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)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
@@ -5857,6 +6156,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)
 | 
					 | 
				
			||||||
@@ -5870,7 +6173,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;
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
@@ -6113,6 +6416,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:
 | 
					 | 
				
			||||||
@ -1,202 +0,0 @@
 | 
				
			|||||||
From 11b14ee8cbbbebd8204609076a9327a1171cd253 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Yu Zhao <yuzhao@google.com>
 | 
					 | 
				
			||||||
Date: Wed, 21 Dec 2022 21:19:05 -0700
 | 
					 | 
				
			||||||
Subject: [PATCH 07/19] BACKPORT: mm: multi-gen LRU: clarify scan_control flags
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Among the flags in scan_control:
 | 
					 | 
				
			||||||
1. sc->may_swap, which indicates swap constraint due to memsw.max, is
 | 
					 | 
				
			||||||
   supported as usual.
 | 
					 | 
				
			||||||
2. sc->proactive, which indicates reclaim by memory.reclaim, may not
 | 
					 | 
				
			||||||
   opportunistically skip the aging path, since it is considered less
 | 
					 | 
				
			||||||
   latency sensitive.
 | 
					 | 
				
			||||||
3. !(sc->gfp_mask & __GFP_IO), which indicates IO constraint, lowers
 | 
					 | 
				
			||||||
   swappiness to prioritize file LRU, since clean file folios are more
 | 
					 | 
				
			||||||
   likely to exist.
 | 
					 | 
				
			||||||
4. sc->may_writepage and sc->may_unmap, which indicates opportunistic
 | 
					 | 
				
			||||||
   reclaim, are rejected, since unmapped clean folios are already
 | 
					 | 
				
			||||||
   prioritized. Scanning for more of them is likely futile and can
 | 
					 | 
				
			||||||
   cause high reclaim latency when there is a large number of memcgs.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The rest are handled by the existing code.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Link: https://lkml.kernel.org/r/20221222041905.2431096-8-yuzhao@google.com
 | 
					 | 
				
			||||||
Signed-off-by: Yu Zhao <yuzhao@google.com>
 | 
					 | 
				
			||||||
Cc: Johannes Weiner <hannes@cmpxchg.org>
 | 
					 | 
				
			||||||
Cc: Jonathan Corbet <corbet@lwn.net>
 | 
					 | 
				
			||||||
Cc: Michael Larabel <Michael@MichaelLarabel.com>
 | 
					 | 
				
			||||||
Cc: Michal Hocko <mhocko@kernel.org>
 | 
					 | 
				
			||||||
Cc: Mike Rapoport <rppt@kernel.org>
 | 
					 | 
				
			||||||
Cc: Roman Gushchin <roman.gushchin@linux.dev>
 | 
					 | 
				
			||||||
Cc: Suren Baghdasaryan <surenb@google.com>
 | 
					 | 
				
			||||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
 | 
					 | 
				
			||||||
Bug: 274865848
 | 
					 | 
				
			||||||
(cherry picked from commit e9d4e1ee788097484606c32122f146d802a9c5fb)
 | 
					 | 
				
			||||||
[TJ: Resolved conflict with older function signature for min_cgroup_below_min, and over
 | 
					 | 
				
			||||||
cdded861182142ac4488a4d64c571107aeb77f53 ("ANDROID: MGLRU: Don't skip anon reclaim if swap low")]
 | 
					 | 
				
			||||||
Change-Id: Ic2e779eaf4e91a3921831b4e2fa10c740dc59d50
 | 
					 | 
				
			||||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 mm/vmscan.c | 55 +++++++++++++++++++++++++++--------------------------
 | 
					 | 
				
			||||||
 1 file changed, 28 insertions(+), 27 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/mm/vmscan.c
 | 
					 | 
				
			||||||
+++ b/mm/vmscan.c
 | 
					 | 
				
			||||||
@@ -3185,6 +3185,9 @@ static int get_swappiness(struct lruvec
 | 
					 | 
				
			||||||
 	struct mem_cgroup *memcg = lruvec_memcg(lruvec);
 | 
					 | 
				
			||||||
 	struct pglist_data *pgdat = lruvec_pgdat(lruvec);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	if (!sc->may_swap)
 | 
					 | 
				
			||||||
+		return 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	if (!can_demote(pgdat->node_id, sc) &&
 | 
					 | 
				
			||||||
 	    mem_cgroup_get_nr_swap_pages(memcg) < MIN_LRU_BATCH)
 | 
					 | 
				
			||||||
 		return 0;
 | 
					 | 
				
			||||||
@@ -4223,7 +4226,7 @@ static void walk_mm(struct lruvec *lruve
 | 
					 | 
				
			||||||
 	} while (err == -EAGAIN);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-static struct lru_gen_mm_walk *set_mm_walk(struct pglist_data *pgdat)
 | 
					 | 
				
			||||||
+static struct lru_gen_mm_walk *set_mm_walk(struct pglist_data *pgdat, bool force_alloc)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	struct lru_gen_mm_walk *walk = current->reclaim_state->mm_walk;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -4231,7 +4234,7 @@ static struct lru_gen_mm_walk *set_mm_wa
 | 
					 | 
				
			||||||
 		VM_WARN_ON_ONCE(walk);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 		walk = &pgdat->mm_walk;
 | 
					 | 
				
			||||||
-	} else if (!pgdat && !walk) {
 | 
					 | 
				
			||||||
+	} else if (!walk && force_alloc) {
 | 
					 | 
				
			||||||
 		VM_WARN_ON_ONCE(current_is_kswapd());
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 		walk = kzalloc(sizeof(*walk), __GFP_HIGH | __GFP_NOMEMALLOC | __GFP_NOWARN);
 | 
					 | 
				
			||||||
@@ -4419,7 +4422,7 @@ static bool try_to_inc_max_seq(struct lr
 | 
					 | 
				
			||||||
 		goto done;
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	walk = set_mm_walk(NULL);
 | 
					 | 
				
			||||||
+	walk = set_mm_walk(NULL, true);
 | 
					 | 
				
			||||||
 	if (!walk) {
 | 
					 | 
				
			||||||
 		success = iterate_mm_list_nowalk(lruvec, max_seq);
 | 
					 | 
				
			||||||
 		goto done;
 | 
					 | 
				
			||||||
@@ -4488,8 +4491,6 @@ static bool lruvec_is_reclaimable(struct
 | 
					 | 
				
			||||||
 	struct mem_cgroup *memcg = lruvec_memcg(lruvec);
 | 
					 | 
				
			||||||
 	DEFINE_MIN_SEQ(lruvec);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	VM_WARN_ON_ONCE(sc->memcg_low_reclaim);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 	/* see the comment on lru_gen_folio */
 | 
					 | 
				
			||||||
 	gen = lru_gen_from_seq(min_seq[LRU_GEN_FILE]);
 | 
					 | 
				
			||||||
 	birth = READ_ONCE(lruvec->lrugen.timestamps[gen]);
 | 
					 | 
				
			||||||
@@ -4753,12 +4754,8 @@ static bool isolate_folio(struct lruvec
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	bool success;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	/* unmapping inhibited */
 | 
					 | 
				
			||||||
-	if (!sc->may_unmap && folio_mapped(folio))
 | 
					 | 
				
			||||||
-		return false;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 	/* swapping inhibited */
 | 
					 | 
				
			||||||
-	if (!(sc->may_writepage && (sc->gfp_mask & __GFP_IO)) &&
 | 
					 | 
				
			||||||
+	if (!(sc->gfp_mask & __GFP_IO) &&
 | 
					 | 
				
			||||||
 	    (folio_test_dirty(folio) ||
 | 
					 | 
				
			||||||
 	     (folio_test_anon(folio) && !folio_test_swapcache(folio))))
 | 
					 | 
				
			||||||
 		return false;
 | 
					 | 
				
			||||||
@@ -4857,9 +4854,8 @@ static int scan_folios(struct lruvec *lr
 | 
					 | 
				
			||||||
 	__count_vm_events(PGSCAN_ANON + type, isolated);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/*
 | 
					 | 
				
			||||||
-	 * There might not be eligible pages due to reclaim_idx, may_unmap and
 | 
					 | 
				
			||||||
-	 * may_writepage. Check the remaining to prevent livelock if it's not
 | 
					 | 
				
			||||||
-	 * making progress.
 | 
					 | 
				
			||||||
+	 * There might not be eligible folios due to reclaim_idx. Check the
 | 
					 | 
				
			||||||
+	 * remaining to prevent livelock if it's not making progress.
 | 
					 | 
				
			||||||
 	 */
 | 
					 | 
				
			||||||
 	return isolated || !remaining ? scanned : 0;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
@@ -5118,8 +5114,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))
 | 
					 | 
				
			||||||
@@ -5147,17 +5142,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)
 | 
					 | 
				
			||||||
@@ -5288,12 +5280,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);
 | 
					 | 
				
			||||||
@@ -5349,11 +5342,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);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -5371,7 +5372,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;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
@@ -5943,7 +5944,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;
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
@ -1,38 +0,0 @@
 | 
				
			|||||||
From 25887d48dff860751a06caa4188bfaf6bfb6e4b2 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Yu Zhao <yuzhao@google.com>
 | 
					 | 
				
			||||||
Date: Wed, 21 Dec 2022 21:19:06 -0700
 | 
					 | 
				
			||||||
Subject: [PATCH 08/19] UPSTREAM: mm: multi-gen LRU: simplify
 | 
					 | 
				
			||||||
 arch_has_hw_pte_young() check
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Scanning page tables when hardware does not set the accessed bit has
 | 
					 | 
				
			||||||
no real use cases.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Link: https://lkml.kernel.org/r/20221222041905.2431096-9-yuzhao@google.com
 | 
					 | 
				
			||||||
Signed-off-by: Yu Zhao <yuzhao@google.com>
 | 
					 | 
				
			||||||
Cc: Johannes Weiner <hannes@cmpxchg.org>
 | 
					 | 
				
			||||||
Cc: Jonathan Corbet <corbet@lwn.net>
 | 
					 | 
				
			||||||
Cc: Michael Larabel <Michael@MichaelLarabel.com>
 | 
					 | 
				
			||||||
Cc: Michal Hocko <mhocko@kernel.org>
 | 
					 | 
				
			||||||
Cc: Mike Rapoport <rppt@kernel.org>
 | 
					 | 
				
			||||||
Cc: Roman Gushchin <roman.gushchin@linux.dev>
 | 
					 | 
				
			||||||
Cc: Suren Baghdasaryan <surenb@google.com>
 | 
					 | 
				
			||||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
 | 
					 | 
				
			||||||
Bug: 274865848
 | 
					 | 
				
			||||||
(cherry picked from commit f386e9314025ea99dae639ed2032560a92081430)
 | 
					 | 
				
			||||||
Change-Id: I84d97ab665b4e3bb862a9bc7d72f50dea7191a6b
 | 
					 | 
				
			||||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 mm/vmscan.c | 2 +-
 | 
					 | 
				
			||||||
 1 file changed, 1 insertion(+), 1 deletion(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/mm/vmscan.c
 | 
					 | 
				
			||||||
+++ b/mm/vmscan.c
 | 
					 | 
				
			||||||
@@ -4417,7 +4417,7 @@ static bool try_to_inc_max_seq(struct lr
 | 
					 | 
				
			||||||
 	 * handful of PTEs. Spreading the work out over a period of time usually
 | 
					 | 
				
			||||||
 	 * is less efficient, but it avoids bursty page faults.
 | 
					 | 
				
			||||||
 	 */
 | 
					 | 
				
			||||||
-	if (!force_scan && !(arch_has_hw_pte_young() && get_cap(LRU_GEN_MM_WALK))) {
 | 
					 | 
				
			||||||
+	if (!arch_has_hw_pte_young() || !get_cap(LRU_GEN_MM_WALK)) {
 | 
					 | 
				
			||||||
 		success = iterate_mm_list_nowalk(lruvec, max_seq);
 | 
					 | 
				
			||||||
 		goto done;
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
@ -1,92 +0,0 @@
 | 
				
			|||||||
From 620b0ee94455e48d124414cd06d8a53f69fb6453 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Yu Zhao <yuzhao@google.com>
 | 
					 | 
				
			||||||
Date: Mon, 13 Feb 2023 00:53:22 -0700
 | 
					 | 
				
			||||||
Subject: [PATCH 09/19] UPSTREAM: mm: multi-gen LRU: avoid futile retries
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Recall that the per-node memcg LRU has two generations and they alternate
 | 
					 | 
				
			||||||
when the last memcg (of a given node) is moved from one to the other.
 | 
					 | 
				
			||||||
Each generation is also sharded into multiple bins to improve scalability.
 | 
					 | 
				
			||||||
A reclaimer starts with a random bin (in the old generation) and, if it
 | 
					 | 
				
			||||||
fails, it will retry, i.e., to try the rest of the bins.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
If a reclaimer fails with the last memcg, it should move this memcg to the
 | 
					 | 
				
			||||||
young generation first, which causes the generations to alternate, and
 | 
					 | 
				
			||||||
then retry.  Otherwise, the retries will be futile because all other bins
 | 
					 | 
				
			||||||
are empty.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Link: https://lkml.kernel.org/r/20230213075322.1416966-1-yuzhao@google.com
 | 
					 | 
				
			||||||
Fixes: e4dde56cd208 ("mm: multi-gen LRU: per-node lru_gen_folio lists")
 | 
					 | 
				
			||||||
Signed-off-by: Yu Zhao <yuzhao@google.com>
 | 
					 | 
				
			||||||
Reported-by: T.J. Mercier <tjmercier@google.com>
 | 
					 | 
				
			||||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
 | 
					 | 
				
			||||||
Bug: 274865848
 | 
					 | 
				
			||||||
(cherry picked from commit 9f550d78b40da21b4da515db4c37d8d7b12aa1a6)
 | 
					 | 
				
			||||||
Change-Id: Ie92535676b005ec9e7987632b742fdde8d54436f
 | 
					 | 
				
			||||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 mm/vmscan.c | 25 +++++++++++++++----------
 | 
					 | 
				
			||||||
 1 file changed, 15 insertions(+), 10 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/mm/vmscan.c
 | 
					 | 
				
			||||||
+++ b/mm/vmscan.c
 | 
					 | 
				
			||||||
@@ -5217,18 +5217,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();
 | 
					 | 
				
			||||||
@@ -5252,14 +5254,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;
 | 
					 | 
				
			||||||
@@ -5268,11 +5278,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)
 | 
					 | 
				
			||||||
@ -1,191 +0,0 @@
 | 
				
			|||||||
From 70d216c71ff5c5b17dd1da6294f97b91fb6aba7a Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Yu Zhao <yuzhao@google.com>
 | 
					 | 
				
			||||||
Date: Fri, 30 Dec 2022 14:52:51 -0700
 | 
					 | 
				
			||||||
Subject: [PATCH 10/19] UPSTREAM: mm: add vma_has_recency()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Add vma_has_recency() to indicate whether a VMA may exhibit temporal
 | 
					 | 
				
			||||||
locality that the LRU algorithm relies on.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This function returns false for VMAs marked by VM_SEQ_READ or
 | 
					 | 
				
			||||||
VM_RAND_READ.  While the former flag indicates linear access, i.e., a
 | 
					 | 
				
			||||||
special case of spatial locality, both flags indicate a lack of temporal
 | 
					 | 
				
			||||||
locality, i.e., the reuse of an area within a relatively small duration.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"Recency" is chosen over "locality" to avoid confusion between temporal
 | 
					 | 
				
			||||||
and spatial localities.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Before this patch, the active/inactive LRU only ignored the accessed bit
 | 
					 | 
				
			||||||
from VMAs marked by VM_SEQ_READ.  After this patch, the active/inactive
 | 
					 | 
				
			||||||
LRU and MGLRU share the same logic: they both ignore the accessed bit if
 | 
					 | 
				
			||||||
vma_has_recency() returns false.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
For the active/inactive LRU, the following fio test showed a [6, 8]%
 | 
					 | 
				
			||||||
increase in IOPS when randomly accessing mapped files under memory
 | 
					 | 
				
			||||||
pressure.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  kb=$(awk '/MemTotal/ { print $2 }' /proc/meminfo)
 | 
					 | 
				
			||||||
  kb=$((kb - 8*1024*1024))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  modprobe brd rd_nr=1 rd_size=$kb
 | 
					 | 
				
			||||||
  dd if=/dev/zero of=/dev/ram0 bs=1M
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  mkfs.ext4 /dev/ram0
 | 
					 | 
				
			||||||
  mount /dev/ram0 /mnt/
 | 
					 | 
				
			||||||
  swapoff -a
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  fio --name=test --directory=/mnt/ --ioengine=mmap --numjobs=8 \
 | 
					 | 
				
			||||||
      --size=8G --rw=randrw --time_based --runtime=10m \
 | 
					 | 
				
			||||||
      --group_reporting
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The discussion that led to this patch is here [1].  Additional test
 | 
					 | 
				
			||||||
results are available in that thread.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[1] https://lore.kernel.org/r/Y31s%2FK8T85jh05wH@google.com/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Link: https://lkml.kernel.org/r/20221230215252.2628425-1-yuzhao@google.com
 | 
					 | 
				
			||||||
Change-Id: I291dcb795197659e40e46539cd32b857677c34ad
 | 
					 | 
				
			||||||
Signed-off-by: Yu Zhao <yuzhao@google.com>
 | 
					 | 
				
			||||||
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
 | 
					 | 
				
			||||||
Cc: Andrea Righi <andrea.righi@canonical.com>
 | 
					 | 
				
			||||||
Cc: Johannes Weiner <hannes@cmpxchg.org>
 | 
					 | 
				
			||||||
Cc: Michael Larabel <Michael@MichaelLarabel.com>
 | 
					 | 
				
			||||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
 | 
					 | 
				
			||||||
(cherry picked from commit 8788f6781486769d9598dcaedc3fe0eb12fc3e59)
 | 
					 | 
				
			||||||
Bug: 274865848
 | 
					 | 
				
			||||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 include/linux/mm_inline.h |  8 ++++++++
 | 
					 | 
				
			||||||
 mm/memory.c               |  7 +++----
 | 
					 | 
				
			||||||
 mm/rmap.c                 | 42 +++++++++++++++++----------------------
 | 
					 | 
				
			||||||
 mm/vmscan.c               |  5 ++++-
 | 
					 | 
				
			||||||
 4 files changed, 33 insertions(+), 29 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/include/linux/mm_inline.h
 | 
					 | 
				
			||||||
+++ b/include/linux/mm_inline.h
 | 
					 | 
				
			||||||
@@ -600,4 +600,12 @@ pte_install_uffd_wp_if_needed(struct vm_
 | 
					 | 
				
			||||||
 #endif
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static inline bool vma_has_recency(struct vm_area_struct *vma)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	if (vma->vm_flags & (VM_SEQ_READ | VM_RAND_READ))
 | 
					 | 
				
			||||||
+		return false;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return true;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 #endif
 | 
					 | 
				
			||||||
--- a/mm/memory.c
 | 
					 | 
				
			||||||
+++ b/mm/memory.c
 | 
					 | 
				
			||||||
@@ -1445,8 +1445,7 @@ again:
 | 
					 | 
				
			||||||
 					force_flush = 1;
 | 
					 | 
				
			||||||
 					set_page_dirty(page);
 | 
					 | 
				
			||||||
 				}
 | 
					 | 
				
			||||||
-				if (pte_young(ptent) &&
 | 
					 | 
				
			||||||
-				    likely(!(vma->vm_flags & VM_SEQ_READ)))
 | 
					 | 
				
			||||||
+				if (pte_young(ptent) && likely(vma_has_recency(vma)))
 | 
					 | 
				
			||||||
 					mark_page_accessed(page);
 | 
					 | 
				
			||||||
 			}
 | 
					 | 
				
			||||||
 			rss[mm_counter(page)]--;
 | 
					 | 
				
			||||||
@@ -5218,8 +5217,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))
 | 
					 | 
				
			||||||
@ -1,129 +0,0 @@
 | 
				
			|||||||
From 9ca4e437a24dfc4ec6c362f319eb9850b9eca497 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Yu Zhao <yuzhao@google.com>
 | 
					 | 
				
			||||||
Date: Fri, 30 Dec 2022 14:52:52 -0700
 | 
					 | 
				
			||||||
Subject: [PATCH 11/19] UPSTREAM: mm: support POSIX_FADV_NOREUSE
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This patch adds POSIX_FADV_NOREUSE to vma_has_recency() so that the LRU
 | 
					 | 
				
			||||||
algorithm can ignore access to mapped files marked by this flag.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The advantages of POSIX_FADV_NOREUSE are:
 | 
					 | 
				
			||||||
1. Unlike MADV_SEQUENTIAL and MADV_RANDOM, it does not alter the
 | 
					 | 
				
			||||||
   default readahead behavior.
 | 
					 | 
				
			||||||
2. Unlike MADV_SEQUENTIAL and MADV_RANDOM, it does not split VMAs and
 | 
					 | 
				
			||||||
   therefore does not take mmap_lock.
 | 
					 | 
				
			||||||
3. Unlike MADV_COLD, setting it has a negligible cost, regardless of
 | 
					 | 
				
			||||||
   how many pages it affects.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Its limitations are:
 | 
					 | 
				
			||||||
1. Like POSIX_FADV_RANDOM and POSIX_FADV_SEQUENTIAL, it currently does
 | 
					 | 
				
			||||||
   not support range. IOW, its scope is the entire file.
 | 
					 | 
				
			||||||
2. It currently does not ignore access through file descriptors.
 | 
					 | 
				
			||||||
   Specifically, for the active/inactive LRU, given a file page shared
 | 
					 | 
				
			||||||
   by two users and one of them having set POSIX_FADV_NOREUSE on the
 | 
					 | 
				
			||||||
   file, this page will be activated upon the second user accessing
 | 
					 | 
				
			||||||
   it. This corner case can be covered by checking POSIX_FADV_NOREUSE
 | 
					 | 
				
			||||||
   before calling folio_mark_accessed() on the read path. But it is
 | 
					 | 
				
			||||||
   considered not worth the effort.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
There have been a few attempts to support POSIX_FADV_NOREUSE, e.g., [1].
 | 
					 | 
				
			||||||
This time the goal is to fill a niche: a few desktop applications, e.g.,
 | 
					 | 
				
			||||||
large file transferring and video encoding/decoding, want fast file
 | 
					 | 
				
			||||||
streaming with mmap() rather than direct IO.  Among those applications, an
 | 
					 | 
				
			||||||
SVT-AV1 regression was reported when running with MGLRU [2].  The
 | 
					 | 
				
			||||||
following test can reproduce that regression.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  kb=$(awk '/MemTotal/ { print $2 }' /proc/meminfo)
 | 
					 | 
				
			||||||
  kb=$((kb - 8*1024*1024))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  modprobe brd rd_nr=1 rd_size=$kb
 | 
					 | 
				
			||||||
  dd if=/dev/zero of=/dev/ram0 bs=1M
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  mkfs.ext4 /dev/ram0
 | 
					 | 
				
			||||||
  mount /dev/ram0 /mnt/
 | 
					 | 
				
			||||||
  swapoff -a
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  fallocate -l 8G /mnt/swapfile
 | 
					 | 
				
			||||||
  mkswap /mnt/swapfile
 | 
					 | 
				
			||||||
  swapon /mnt/swapfile
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  wget http://ultravideo.cs.tut.fi/video/Bosphorus_3840x2160_120fps_420_8bit_YUV_Y4M.7z
 | 
					 | 
				
			||||||
  7z e -o/mnt/ Bosphorus_3840x2160_120fps_420_8bit_YUV_Y4M.7z
 | 
					 | 
				
			||||||
  SvtAv1EncApp --preset 12 -w 3840 -h 2160 \
 | 
					 | 
				
			||||||
               -i /mnt/Bosphorus_3840x2160.y4m
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
For MGLRU, the following change showed a [9-11]% increase in FPS,
 | 
					 | 
				
			||||||
which makes it on par with the active/inactive LRU.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  patch Source/App/EncApp/EbAppMain.c <<EOF
 | 
					 | 
				
			||||||
  31a32
 | 
					 | 
				
			||||||
  > #include <fcntl.h>
 | 
					 | 
				
			||||||
  35d35
 | 
					 | 
				
			||||||
  < #include <fcntl.h> /* _O_BINARY */
 | 
					 | 
				
			||||||
  117a118
 | 
					 | 
				
			||||||
  >             posix_fadvise(config->mmap.fd, 0, 0, POSIX_FADV_NOREUSE);
 | 
					 | 
				
			||||||
  EOF
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[1] https://lore.kernel.org/r/1308923350-7932-1-git-send-email-andrea@betterlinux.com/
 | 
					 | 
				
			||||||
[2] https://openbenchmarking.org/result/2209259-PTS-MGLRU8GB57
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Link: https://lkml.kernel.org/r/20221230215252.2628425-2-yuzhao@google.com
 | 
					 | 
				
			||||||
Change-Id: I0b7f5f971d78014ea1ba44cee6a8ec902a4330d0
 | 
					 | 
				
			||||||
Signed-off-by: Yu Zhao <yuzhao@google.com>
 | 
					 | 
				
			||||||
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
 | 
					 | 
				
			||||||
Cc: Andrea Righi <andrea.righi@canonical.com>
 | 
					 | 
				
			||||||
Cc: Johannes Weiner <hannes@cmpxchg.org>
 | 
					 | 
				
			||||||
Cc: Michael Larabel <Michael@MichaelLarabel.com>
 | 
					 | 
				
			||||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
 | 
					 | 
				
			||||||
(cherry picked from commit 17e810229cb3068b692fa078bd9b3a6527e0866a)
 | 
					 | 
				
			||||||
Bug: 274865848
 | 
					 | 
				
			||||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 include/linux/fs.h        | 2 ++
 | 
					 | 
				
			||||||
 include/linux/mm_inline.h | 3 +++
 | 
					 | 
				
			||||||
 mm/fadvise.c              | 5 ++++-
 | 
					 | 
				
			||||||
 3 files changed, 9 insertions(+), 1 deletion(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/include/linux/fs.h
 | 
					 | 
				
			||||||
+++ b/include/linux/fs.h
 | 
					 | 
				
			||||||
@@ -166,6 +166,8 @@ typedef int (dio_iodone_t)(struct kiocb
 | 
					 | 
				
			||||||
 /* File supports DIRECT IO */
 | 
					 | 
				
			||||||
 #define	FMODE_CAN_ODIRECT	((__force fmode_t)0x400000)
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+#define	FMODE_NOREUSE		((__force fmode_t)0x800000)
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 /* File was opened by fanotify and shouldn't generate fanotify events */
 | 
					 | 
				
			||||||
 #define FMODE_NONOTIFY		((__force fmode_t)0x4000000)
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
--- a/include/linux/mm_inline.h
 | 
					 | 
				
			||||||
+++ b/include/linux/mm_inline.h
 | 
					 | 
				
			||||||
@@ -605,6 +605,9 @@ static inline bool vma_has_recency(struc
 | 
					 | 
				
			||||||
 	if (vma->vm_flags & (VM_SEQ_READ | VM_RAND_READ))
 | 
					 | 
				
			||||||
 		return false;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	if (vma->vm_file && (vma->vm_file->f_mode & FMODE_NOREUSE))
 | 
					 | 
				
			||||||
+		return false;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	return true;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
--- a/mm/fadvise.c
 | 
					 | 
				
			||||||
+++ b/mm/fadvise.c
 | 
					 | 
				
			||||||
@@ -80,7 +80,7 @@ int generic_fadvise(struct file *file, l
 | 
					 | 
				
			||||||
 	case POSIX_FADV_NORMAL:
 | 
					 | 
				
			||||||
 		file->f_ra.ra_pages = bdi->ra_pages;
 | 
					 | 
				
			||||||
 		spin_lock(&file->f_lock);
 | 
					 | 
				
			||||||
-		file->f_mode &= ~FMODE_RANDOM;
 | 
					 | 
				
			||||||
+		file->f_mode &= ~(FMODE_RANDOM | FMODE_NOREUSE);
 | 
					 | 
				
			||||||
 		spin_unlock(&file->f_lock);
 | 
					 | 
				
			||||||
 		break;
 | 
					 | 
				
			||||||
 	case POSIX_FADV_RANDOM:
 | 
					 | 
				
			||||||
@@ -107,6 +107,9 @@ int generic_fadvise(struct file *file, l
 | 
					 | 
				
			||||||
 		force_page_cache_readahead(mapping, file, start_index, nrpages);
 | 
					 | 
				
			||||||
 		break;
 | 
					 | 
				
			||||||
 	case POSIX_FADV_NOREUSE:
 | 
					 | 
				
			||||||
+		spin_lock(&file->f_lock);
 | 
					 | 
				
			||||||
+		file->f_mode |= FMODE_NOREUSE;
 | 
					 | 
				
			||||||
+		spin_unlock(&file->f_lock);
 | 
					 | 
				
			||||||
 		break;
 | 
					 | 
				
			||||||
 	case POSIX_FADV_DONTNEED:
 | 
					 | 
				
			||||||
 		__filemap_fdatawrite_range(mapping, offset, endbyte,
 | 
					 | 
				
			||||||
@ -1,67 +0,0 @@
 | 
				
			|||||||
From 1b5e4c317d80f4826eceb3781702d18d06b14394 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: "T.J. Alumbaugh" <talumbau@google.com>
 | 
					 | 
				
			||||||
Date: Wed, 18 Jan 2023 00:18:21 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH 12/19] UPSTREAM: mm: multi-gen LRU: section for working set
 | 
					 | 
				
			||||||
 protection
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Patch series "mm: multi-gen LRU: improve".
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This patch series improves a few MGLRU functions, collects related
 | 
					 | 
				
			||||||
functions, and adds additional documentation.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This patch (of 7):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Add a section for working set protection in the code and the design doc.
 | 
					 | 
				
			||||||
The admin doc already contains its usage.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Link: https://lkml.kernel.org/r/20230118001827.1040870-1-talumbau@google.com
 | 
					 | 
				
			||||||
Link: https://lkml.kernel.org/r/20230118001827.1040870-2-talumbau@google.com
 | 
					 | 
				
			||||||
Change-Id: I65599075fd42951db7739a2ab7cee78516e157b3
 | 
					 | 
				
			||||||
Signed-off-by: T.J. Alumbaugh <talumbau@google.com>
 | 
					 | 
				
			||||||
Cc: Yu Zhao <yuzhao@google.com>
 | 
					 | 
				
			||||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
 | 
					 | 
				
			||||||
(cherry picked from commit 7b8144e63d84716f16a1b929e0c7e03ae5c4d5c1)
 | 
					 | 
				
			||||||
Bug: 274865848
 | 
					 | 
				
			||||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 Documentation/mm/multigen_lru.rst | 15 +++++++++++++++
 | 
					 | 
				
			||||||
 mm/vmscan.c                       |  4 ++++
 | 
					 | 
				
			||||||
 2 files changed, 19 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/Documentation/mm/multigen_lru.rst
 | 
					 | 
				
			||||||
+++ b/Documentation/mm/multigen_lru.rst
 | 
					 | 
				
			||||||
@@ -141,6 +141,21 @@ loop has detected outlying refaults from
 | 
					 | 
				
			||||||
 this end, the feedback loop uses the first tier as the baseline, for
 | 
					 | 
				
			||||||
 the reason stated earlier.
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+Working set protection
 | 
					 | 
				
			||||||
+----------------------
 | 
					 | 
				
			||||||
+Each generation is timestamped at birth. If ``lru_gen_min_ttl`` is
 | 
					 | 
				
			||||||
+set, an ``lruvec`` is protected from the eviction when its oldest
 | 
					 | 
				
			||||||
+generation was born within ``lru_gen_min_ttl`` milliseconds. In other
 | 
					 | 
				
			||||||
+words, it prevents the working set of ``lru_gen_min_ttl`` milliseconds
 | 
					 | 
				
			||||||
+from getting evicted. The OOM killer is triggered if this working set
 | 
					 | 
				
			||||||
+cannot be kept in memory.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+This time-based approach has the following advantages:
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+1. It is easier to configure because it is agnostic to applications
 | 
					 | 
				
			||||||
+   and memory sizes.
 | 
					 | 
				
			||||||
+2. It is more reliable because it is directly wired to the OOM killer.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 Summary
 | 
					 | 
				
			||||||
 -------
 | 
					 | 
				
			||||||
 The multi-gen LRU can be disassembled into the following parts:
 | 
					 | 
				
			||||||
--- a/mm/vmscan.c
 | 
					 | 
				
			||||||
+++ b/mm/vmscan.c
 | 
					 | 
				
			||||||
@@ -4461,6 +4461,10 @@ done:
 | 
					 | 
				
			||||||
 	return true;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+/******************************************************************************
 | 
					 | 
				
			||||||
+ *                          working set protection
 | 
					 | 
				
			||||||
+ ******************************************************************************/
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static bool lruvec_is_sizable(struct lruvec *lruvec, struct scan_control *sc)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	int gen, type, zone;
 | 
					 | 
				
			||||||
@ -1,57 +0,0 @@
 | 
				
			|||||||
From 5ddf9d53d375e42af49b744bd7c2f8247c6bce15 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: "T.J. Alumbaugh" <talumbau@google.com>
 | 
					 | 
				
			||||||
Date: Wed, 18 Jan 2023 00:18:22 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH 13/19] UPSTREAM: mm: multi-gen LRU: section for rmap/PT walk
 | 
					 | 
				
			||||||
 feedback
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Add a section for lru_gen_look_around() in the code and the design doc.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Link: https://lkml.kernel.org/r/20230118001827.1040870-3-talumbau@google.com
 | 
					 | 
				
			||||||
Change-Id: I5097af63f61b3b69ec2abee6cdbdc33c296df213
 | 
					 | 
				
			||||||
Signed-off-by: T.J. Alumbaugh <talumbau@google.com>
 | 
					 | 
				
			||||||
Cc: Yu Zhao <yuzhao@google.com>
 | 
					 | 
				
			||||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
 | 
					 | 
				
			||||||
(cherry picked from commit db19a43d9b3a8876552f00f656008206ef9a5efa)
 | 
					 | 
				
			||||||
Bug: 274865848
 | 
					 | 
				
			||||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 Documentation/mm/multigen_lru.rst | 14 ++++++++++++++
 | 
					 | 
				
			||||||
 mm/vmscan.c                       |  4 ++++
 | 
					 | 
				
			||||||
 2 files changed, 18 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/Documentation/mm/multigen_lru.rst
 | 
					 | 
				
			||||||
+++ b/Documentation/mm/multigen_lru.rst
 | 
					 | 
				
			||||||
@@ -156,6 +156,20 @@ This time-based approach has the followi
 | 
					 | 
				
			||||||
    and memory sizes.
 | 
					 | 
				
			||||||
 2. It is more reliable because it is directly wired to the OOM killer.
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+Rmap/PT walk feedback
 | 
					 | 
				
			||||||
+---------------------
 | 
					 | 
				
			||||||
+Searching the rmap for PTEs mapping each page on an LRU list (to test
 | 
					 | 
				
			||||||
+and clear the accessed bit) can be expensive because pages from
 | 
					 | 
				
			||||||
+different VMAs (PA space) are not cache friendly to the rmap (VA
 | 
					 | 
				
			||||||
+space). For workloads mostly using mapped pages, searching the rmap
 | 
					 | 
				
			||||||
+can incur the highest CPU cost in the reclaim path.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+``lru_gen_look_around()`` exploits spatial locality to reduce the
 | 
					 | 
				
			||||||
+trips into the rmap. It scans the adjacent PTEs of a young PTE and
 | 
					 | 
				
			||||||
+promotes hot pages. If the scan was done cacheline efficiently, it
 | 
					 | 
				
			||||||
+adds the PMD entry pointing to the PTE table to the Bloom filter. This
 | 
					 | 
				
			||||||
+forms a feedback loop between the eviction and the aging.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 Summary
 | 
					 | 
				
			||||||
 -------
 | 
					 | 
				
			||||||
 The multi-gen LRU can be disassembled into the following parts:
 | 
					 | 
				
			||||||
--- a/mm/vmscan.c
 | 
					 | 
				
			||||||
+++ b/mm/vmscan.c
 | 
					 | 
				
			||||||
@@ -4555,6 +4555,10 @@ static void lru_gen_age_node(struct pgli
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+/******************************************************************************
 | 
					 | 
				
			||||||
+ *                          rmap/PT walk feedback
 | 
					 | 
				
			||||||
+ ******************************************************************************/
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 /*
 | 
					 | 
				
			||||||
  * This function exploits spatial locality when shrink_folio_list() walks the
 | 
					 | 
				
			||||||
  * rmap. It scans the adjacent PTEs of a young PTE and promotes hot pages. If
 | 
					 | 
				
			||||||
@ -1,243 +0,0 @@
 | 
				
			|||||||
From 397624e12244ec038f51cb1f178ccb7a2ec562e5 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: "T.J. Alumbaugh" <talumbau@google.com>
 | 
					 | 
				
			||||||
Date: Wed, 18 Jan 2023 00:18:23 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH 14/19] UPSTREAM: mm: multi-gen LRU: section for Bloom filters
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Move Bloom filters code into a dedicated section.  Improve the design doc
 | 
					 | 
				
			||||||
to explain Bloom filter usage and connection between aging and eviction in
 | 
					 | 
				
			||||||
their use.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Link: https://lkml.kernel.org/r/20230118001827.1040870-4-talumbau@google.com
 | 
					 | 
				
			||||||
Change-Id: I73e866f687c1ed9f5c8538086aa39408b79897db
 | 
					 | 
				
			||||||
Signed-off-by: T.J. Alumbaugh <talumbau@google.com>
 | 
					 | 
				
			||||||
Cc: Yu Zhao <yuzhao@google.com>
 | 
					 | 
				
			||||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
 | 
					 | 
				
			||||||
(cherry picked from commit ccbbbb85945d8f0255aa9dbc1b617017e2294f2c)
 | 
					 | 
				
			||||||
Bug: 274865848
 | 
					 | 
				
			||||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 Documentation/mm/multigen_lru.rst |  16 +++
 | 
					 | 
				
			||||||
 mm/vmscan.c                       | 180 +++++++++++++++---------------
 | 
					 | 
				
			||||||
 2 files changed, 108 insertions(+), 88 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/Documentation/mm/multigen_lru.rst
 | 
					 | 
				
			||||||
+++ b/Documentation/mm/multigen_lru.rst
 | 
					 | 
				
			||||||
@@ -170,6 +170,22 @@ promotes hot pages. If the scan was done
 | 
					 | 
				
			||||||
 adds the PMD entry pointing to the PTE table to the Bloom filter. This
 | 
					 | 
				
			||||||
 forms a feedback loop between the eviction and the aging.
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+Bloom Filters
 | 
					 | 
				
			||||||
+-------------
 | 
					 | 
				
			||||||
+Bloom filters are a space and memory efficient data structure for set
 | 
					 | 
				
			||||||
+membership test, i.e., test if an element is not in the set or may be
 | 
					 | 
				
			||||||
+in the set.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+In the eviction path, specifically, in ``lru_gen_look_around()``, if a
 | 
					 | 
				
			||||||
+PMD has a sufficient number of hot pages, its address is placed in the
 | 
					 | 
				
			||||||
+filter. In the aging path, set membership means that the PTE range
 | 
					 | 
				
			||||||
+will be scanned for young pages.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+Note that Bloom filters are probabilistic on set membership. If a test
 | 
					 | 
				
			||||||
+is false positive, the cost is an additional scan of a range of PTEs,
 | 
					 | 
				
			||||||
+which may yield hot pages anyway. Parameters of the filter itself can
 | 
					 | 
				
			||||||
+control the false positive rate in the limit.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 Summary
 | 
					 | 
				
			||||||
 -------
 | 
					 | 
				
			||||||
 The multi-gen LRU can be disassembled into the following parts:
 | 
					 | 
				
			||||||
--- a/mm/vmscan.c
 | 
					 | 
				
			||||||
+++ b/mm/vmscan.c
 | 
					 | 
				
			||||||
@@ -3209,6 +3209,98 @@ static bool __maybe_unused seq_is_valid(
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /******************************************************************************
 | 
					 | 
				
			||||||
+ *                          Bloom filters
 | 
					 | 
				
			||||||
+ ******************************************************************************/
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ * Bloom filters with m=1<<15, k=2 and the false positive rates of ~1/5 when
 | 
					 | 
				
			||||||
+ * n=10,000 and ~1/2 when n=20,000, where, conventionally, m is the number of
 | 
					 | 
				
			||||||
+ * bits in a bitmap, k is the number of hash functions and n is the number of
 | 
					 | 
				
			||||||
+ * inserted items.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Page table walkers use one of the two filters to reduce their search space.
 | 
					 | 
				
			||||||
+ * To get rid of non-leaf entries that no longer have enough leaf entries, the
 | 
					 | 
				
			||||||
+ * aging uses the double-buffering technique to flip to the other filter each
 | 
					 | 
				
			||||||
+ * time it produces a new generation. For non-leaf entries that have enough
 | 
					 | 
				
			||||||
+ * leaf entries, the aging carries them over to the next generation in
 | 
					 | 
				
			||||||
+ * walk_pmd_range(); the eviction also report them when walking the rmap
 | 
					 | 
				
			||||||
+ * in lru_gen_look_around().
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * For future optimizations:
 | 
					 | 
				
			||||||
+ * 1. It's not necessary to keep both filters all the time. The spare one can be
 | 
					 | 
				
			||||||
+ *    freed after the RCU grace period and reallocated if needed again.
 | 
					 | 
				
			||||||
+ * 2. And when reallocating, it's worth scaling its size according to the number
 | 
					 | 
				
			||||||
+ *    of inserted entries in the other filter, to reduce the memory overhead on
 | 
					 | 
				
			||||||
+ *    small systems and false positives on large systems.
 | 
					 | 
				
			||||||
+ * 3. Jenkins' hash function is an alternative to Knuth's.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+#define BLOOM_FILTER_SHIFT	15
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static inline int filter_gen_from_seq(unsigned long seq)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return seq % NR_BLOOM_FILTERS;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void get_item_key(void *item, int *key)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	u32 hash = hash_ptr(item, BLOOM_FILTER_SHIFT * 2);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	BUILD_BUG_ON(BLOOM_FILTER_SHIFT * 2 > BITS_PER_TYPE(u32));
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	key[0] = hash & (BIT(BLOOM_FILTER_SHIFT) - 1);
 | 
					 | 
				
			||||||
+	key[1] = hash >> BLOOM_FILTER_SHIFT;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static bool test_bloom_filter(struct lruvec *lruvec, unsigned long seq, void *item)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int key[2];
 | 
					 | 
				
			||||||
+	unsigned long *filter;
 | 
					 | 
				
			||||||
+	int gen = filter_gen_from_seq(seq);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	filter = READ_ONCE(lruvec->mm_state.filters[gen]);
 | 
					 | 
				
			||||||
+	if (!filter)
 | 
					 | 
				
			||||||
+		return true;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	get_item_key(item, key);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return test_bit(key[0], filter) && test_bit(key[1], filter);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void update_bloom_filter(struct lruvec *lruvec, unsigned long seq, void *item)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int key[2];
 | 
					 | 
				
			||||||
+	unsigned long *filter;
 | 
					 | 
				
			||||||
+	int gen = filter_gen_from_seq(seq);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	filter = READ_ONCE(lruvec->mm_state.filters[gen]);
 | 
					 | 
				
			||||||
+	if (!filter)
 | 
					 | 
				
			||||||
+		return;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	get_item_key(item, key);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (!test_bit(key[0], filter))
 | 
					 | 
				
			||||||
+		set_bit(key[0], filter);
 | 
					 | 
				
			||||||
+	if (!test_bit(key[1], filter))
 | 
					 | 
				
			||||||
+		set_bit(key[1], filter);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void reset_bloom_filter(struct lruvec *lruvec, unsigned long seq)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	unsigned long *filter;
 | 
					 | 
				
			||||||
+	int gen = filter_gen_from_seq(seq);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	filter = lruvec->mm_state.filters[gen];
 | 
					 | 
				
			||||||
+	if (filter) {
 | 
					 | 
				
			||||||
+		bitmap_clear(filter, 0, BIT(BLOOM_FILTER_SHIFT));
 | 
					 | 
				
			||||||
+		return;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	filter = bitmap_zalloc(BIT(BLOOM_FILTER_SHIFT),
 | 
					 | 
				
			||||||
+			       __GFP_HIGH | __GFP_NOMEMALLOC | __GFP_NOWARN);
 | 
					 | 
				
			||||||
+	WRITE_ONCE(lruvec->mm_state.filters[gen], filter);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/******************************************************************************
 | 
					 | 
				
			||||||
  *                          mm_struct list
 | 
					 | 
				
			||||||
  ******************************************************************************/
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -3333,94 +3425,6 @@ void lru_gen_migrate_mm(struct mm_struct
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 #endif
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-/*
 | 
					 | 
				
			||||||
- * Bloom filters with m=1<<15, k=2 and the false positive rates of ~1/5 when
 | 
					 | 
				
			||||||
- * n=10,000 and ~1/2 when n=20,000, where, conventionally, m is the number of
 | 
					 | 
				
			||||||
- * bits in a bitmap, k is the number of hash functions and n is the number of
 | 
					 | 
				
			||||||
- * inserted items.
 | 
					 | 
				
			||||||
- *
 | 
					 | 
				
			||||||
- * Page table walkers use one of the two filters to reduce their search space.
 | 
					 | 
				
			||||||
- * To get rid of non-leaf entries that no longer have enough leaf entries, the
 | 
					 | 
				
			||||||
- * aging uses the double-buffering technique to flip to the other filter each
 | 
					 | 
				
			||||||
- * time it produces a new generation. For non-leaf entries that have enough
 | 
					 | 
				
			||||||
- * leaf entries, the aging carries them over to the next generation in
 | 
					 | 
				
			||||||
- * walk_pmd_range(); the eviction also report them when walking the rmap
 | 
					 | 
				
			||||||
- * in lru_gen_look_around().
 | 
					 | 
				
			||||||
- *
 | 
					 | 
				
			||||||
- * For future optimizations:
 | 
					 | 
				
			||||||
- * 1. It's not necessary to keep both filters all the time. The spare one can be
 | 
					 | 
				
			||||||
- *    freed after the RCU grace period and reallocated if needed again.
 | 
					 | 
				
			||||||
- * 2. And when reallocating, it's worth scaling its size according to the number
 | 
					 | 
				
			||||||
- *    of inserted entries in the other filter, to reduce the memory overhead on
 | 
					 | 
				
			||||||
- *    small systems and false positives on large systems.
 | 
					 | 
				
			||||||
- * 3. Jenkins' hash function is an alternative to Knuth's.
 | 
					 | 
				
			||||||
- */
 | 
					 | 
				
			||||||
-#define BLOOM_FILTER_SHIFT	15
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-static inline int filter_gen_from_seq(unsigned long seq)
 | 
					 | 
				
			||||||
-{
 | 
					 | 
				
			||||||
-	return seq % NR_BLOOM_FILTERS;
 | 
					 | 
				
			||||||
-}
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-static void get_item_key(void *item, int *key)
 | 
					 | 
				
			||||||
-{
 | 
					 | 
				
			||||||
-	u32 hash = hash_ptr(item, BLOOM_FILTER_SHIFT * 2);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	BUILD_BUG_ON(BLOOM_FILTER_SHIFT * 2 > BITS_PER_TYPE(u32));
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	key[0] = hash & (BIT(BLOOM_FILTER_SHIFT) - 1);
 | 
					 | 
				
			||||||
-	key[1] = hash >> BLOOM_FILTER_SHIFT;
 | 
					 | 
				
			||||||
-}
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-static void reset_bloom_filter(struct lruvec *lruvec, unsigned long seq)
 | 
					 | 
				
			||||||
-{
 | 
					 | 
				
			||||||
-	unsigned long *filter;
 | 
					 | 
				
			||||||
-	int gen = filter_gen_from_seq(seq);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	filter = lruvec->mm_state.filters[gen];
 | 
					 | 
				
			||||||
-	if (filter) {
 | 
					 | 
				
			||||||
-		bitmap_clear(filter, 0, BIT(BLOOM_FILTER_SHIFT));
 | 
					 | 
				
			||||||
-		return;
 | 
					 | 
				
			||||||
-	}
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	filter = bitmap_zalloc(BIT(BLOOM_FILTER_SHIFT),
 | 
					 | 
				
			||||||
-			       __GFP_HIGH | __GFP_NOMEMALLOC | __GFP_NOWARN);
 | 
					 | 
				
			||||||
-	WRITE_ONCE(lruvec->mm_state.filters[gen], filter);
 | 
					 | 
				
			||||||
-}
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-static void update_bloom_filter(struct lruvec *lruvec, unsigned long seq, void *item)
 | 
					 | 
				
			||||||
-{
 | 
					 | 
				
			||||||
-	int key[2];
 | 
					 | 
				
			||||||
-	unsigned long *filter;
 | 
					 | 
				
			||||||
-	int gen = filter_gen_from_seq(seq);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	filter = READ_ONCE(lruvec->mm_state.filters[gen]);
 | 
					 | 
				
			||||||
-	if (!filter)
 | 
					 | 
				
			||||||
-		return;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	get_item_key(item, key);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	if (!test_bit(key[0], filter))
 | 
					 | 
				
			||||||
-		set_bit(key[0], filter);
 | 
					 | 
				
			||||||
-	if (!test_bit(key[1], filter))
 | 
					 | 
				
			||||||
-		set_bit(key[1], filter);
 | 
					 | 
				
			||||||
-}
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-static bool test_bloom_filter(struct lruvec *lruvec, unsigned long seq, void *item)
 | 
					 | 
				
			||||||
-{
 | 
					 | 
				
			||||||
-	int key[2];
 | 
					 | 
				
			||||||
-	unsigned long *filter;
 | 
					 | 
				
			||||||
-	int gen = filter_gen_from_seq(seq);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	filter = READ_ONCE(lruvec->mm_state.filters[gen]);
 | 
					 | 
				
			||||||
-	if (!filter)
 | 
					 | 
				
			||||||
-		return true;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	get_item_key(item, key);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	return test_bit(key[0], filter) && test_bit(key[1], filter);
 | 
					 | 
				
			||||||
-}
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 static void reset_mm_stats(struct lruvec *lruvec, struct lru_gen_mm_walk *walk, bool last)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	int i;
 | 
					 | 
				
			||||||
@ -1,427 +0,0 @@
 | 
				
			|||||||
From 48c916b812652f9453be5bd45a703728926d41ca Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: "T.J. Alumbaugh" <talumbau@google.com>
 | 
					 | 
				
			||||||
Date: Wed, 18 Jan 2023 00:18:24 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH 15/19] UPSTREAM: mm: multi-gen LRU: section for memcg LRU
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Move memcg LRU code into a dedicated section.  Improve the design doc to
 | 
					 | 
				
			||||||
outline its architecture.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Link: https://lkml.kernel.org/r/20230118001827.1040870-5-talumbau@google.com
 | 
					 | 
				
			||||||
Change-Id: Id252e420cff7a858acb098cf2b3642da5c40f602
 | 
					 | 
				
			||||||
Signed-off-by: T.J. Alumbaugh <talumbau@google.com>
 | 
					 | 
				
			||||||
Cc: Yu Zhao <yuzhao@google.com>
 | 
					 | 
				
			||||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
 | 
					 | 
				
			||||||
(cherry picked from commit 36c7b4db7c942ae9e1b111f0c6b468c8b2e33842)
 | 
					 | 
				
			||||||
Bug: 274865848
 | 
					 | 
				
			||||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 Documentation/mm/multigen_lru.rst |  33 +++-
 | 
					 | 
				
			||||||
 include/linux/mm_inline.h         |  17 --
 | 
					 | 
				
			||||||
 include/linux/mmzone.h            |  13 +-
 | 
					 | 
				
			||||||
 mm/memcontrol.c                   |   8 +-
 | 
					 | 
				
			||||||
 mm/vmscan.c                       | 250 +++++++++++++++++-------------
 | 
					 | 
				
			||||||
 5 files changed, 178 insertions(+), 143 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/Documentation/mm/multigen_lru.rst
 | 
					 | 
				
			||||||
+++ b/Documentation/mm/multigen_lru.rst
 | 
					 | 
				
			||||||
@@ -186,9 +186,40 @@ is false positive, the cost is an additi
 | 
					 | 
				
			||||||
 which may yield hot pages anyway. Parameters of the filter itself can
 | 
					 | 
				
			||||||
 control the false positive rate in the limit.
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+Memcg LRU
 | 
					 | 
				
			||||||
+---------
 | 
					 | 
				
			||||||
+An memcg LRU is a per-node LRU of memcgs. It is also an LRU of LRUs,
 | 
					 | 
				
			||||||
+since each node and memcg combination has an LRU of folios (see
 | 
					 | 
				
			||||||
+``mem_cgroup_lruvec()``). Its goal is to improve the scalability of
 | 
					 | 
				
			||||||
+global reclaim, which is critical to system-wide memory overcommit in
 | 
					 | 
				
			||||||
+data centers. Note that memcg LRU only applies to global reclaim.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+The basic structure of an memcg LRU can be understood by an analogy to
 | 
					 | 
				
			||||||
+the active/inactive LRU (of folios):
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+1. It has the young and the old (generations), i.e., the counterparts
 | 
					 | 
				
			||||||
+   to the active and the inactive;
 | 
					 | 
				
			||||||
+2. The increment of ``max_seq`` triggers promotion, i.e., the
 | 
					 | 
				
			||||||
+   counterpart to activation;
 | 
					 | 
				
			||||||
+3. Other events trigger similar operations, e.g., offlining an memcg
 | 
					 | 
				
			||||||
+   triggers demotion, i.e., the counterpart to deactivation.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+In terms of global reclaim, it has two distinct features:
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+1. Sharding, which allows each thread to start at a random memcg (in
 | 
					 | 
				
			||||||
+   the old generation) and improves parallelism;
 | 
					 | 
				
			||||||
+2. Eventual fairness, which allows direct reclaim to bail out at will
 | 
					 | 
				
			||||||
+   and reduces latency without affecting fairness over some time.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+In terms of traversing memcgs during global reclaim, it improves the
 | 
					 | 
				
			||||||
+best-case complexity from O(n) to O(1) and does not affect the
 | 
					 | 
				
			||||||
+worst-case complexity O(n). Therefore, on average, it has a sublinear
 | 
					 | 
				
			||||||
+complexity.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 Summary
 | 
					 | 
				
			||||||
 -------
 | 
					 | 
				
			||||||
-The multi-gen LRU can be disassembled into the following parts:
 | 
					 | 
				
			||||||
+The multi-gen LRU (of folios) can be disassembled into the following
 | 
					 | 
				
			||||||
+parts:
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 * Generations
 | 
					 | 
				
			||||||
 * Rmap walks
 | 
					 | 
				
			||||||
--- a/include/linux/mm_inline.h
 | 
					 | 
				
			||||||
+++ b/include/linux/mm_inline.h
 | 
					 | 
				
			||||||
@@ -122,18 +122,6 @@ static inline bool lru_gen_in_fault(void
 | 
					 | 
				
			||||||
 	return current->in_lru_fault;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-#ifdef CONFIG_MEMCG
 | 
					 | 
				
			||||||
-static inline int lru_gen_memcg_seg(struct lruvec *lruvec)
 | 
					 | 
				
			||||||
-{
 | 
					 | 
				
			||||||
-	return READ_ONCE(lruvec->lrugen.seg);
 | 
					 | 
				
			||||||
-}
 | 
					 | 
				
			||||||
-#else
 | 
					 | 
				
			||||||
-static inline int lru_gen_memcg_seg(struct lruvec *lruvec)
 | 
					 | 
				
			||||||
-{
 | 
					 | 
				
			||||||
-	return 0;
 | 
					 | 
				
			||||||
-}
 | 
					 | 
				
			||||||
-#endif
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 static inline int lru_gen_from_seq(unsigned long seq)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	return seq % MAX_NR_GENS;
 | 
					 | 
				
			||||||
@@ -314,11 +302,6 @@ static inline bool lru_gen_in_fault(void
 | 
					 | 
				
			||||||
 	return false;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-static inline int lru_gen_memcg_seg(struct lruvec *lruvec)
 | 
					 | 
				
			||||||
-{
 | 
					 | 
				
			||||||
-	return 0;
 | 
					 | 
				
			||||||
-}
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 static inline bool lru_gen_add_folio(struct lruvec *lruvec, struct folio *folio, bool reclaiming)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	return false;
 | 
					 | 
				
			||||||
--- a/include/linux/mmzone.h
 | 
					 | 
				
			||||||
+++ b/include/linux/mmzone.h
 | 
					 | 
				
			||||||
@@ -368,15 +368,6 @@ struct page_vma_mapped_walk;
 | 
					 | 
				
			||||||
 #define LRU_GEN_MASK		((BIT(LRU_GEN_WIDTH) - 1) << LRU_GEN_PGOFF)
 | 
					 | 
				
			||||||
 #define LRU_REFS_MASK		((BIT(LRU_REFS_WIDTH) - 1) << LRU_REFS_PGOFF)
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-/* see the comment on MEMCG_NR_GENS */
 | 
					 | 
				
			||||||
-enum {
 | 
					 | 
				
			||||||
-	MEMCG_LRU_NOP,
 | 
					 | 
				
			||||||
-	MEMCG_LRU_HEAD,
 | 
					 | 
				
			||||||
-	MEMCG_LRU_TAIL,
 | 
					 | 
				
			||||||
-	MEMCG_LRU_OLD,
 | 
					 | 
				
			||||||
-	MEMCG_LRU_YOUNG,
 | 
					 | 
				
			||||||
-};
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 #ifdef CONFIG_LRU_GEN
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 enum {
 | 
					 | 
				
			||||||
@@ -557,7 +548,7 @@ void lru_gen_exit_memcg(struct mem_cgrou
 | 
					 | 
				
			||||||
 void lru_gen_online_memcg(struct mem_cgroup *memcg);
 | 
					 | 
				
			||||||
 void lru_gen_offline_memcg(struct mem_cgroup *memcg);
 | 
					 | 
				
			||||||
 void lru_gen_release_memcg(struct mem_cgroup *memcg);
 | 
					 | 
				
			||||||
-void lru_gen_rotate_memcg(struct lruvec *lruvec, int op);
 | 
					 | 
				
			||||||
+void lru_gen_soft_reclaim(struct lruvec *lruvec);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #else /* !CONFIG_MEMCG */
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -608,7 +599,7 @@ static inline void lru_gen_release_memcg
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-static inline void lru_gen_rotate_memcg(struct lruvec *lruvec, int op)
 | 
					 | 
				
			||||||
+static inline void lru_gen_soft_reclaim(struct lruvec *lruvec)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
--- a/mm/memcontrol.c
 | 
					 | 
				
			||||||
+++ b/mm/memcontrol.c
 | 
					 | 
				
			||||||
@@ -478,12 +478,8 @@ static void mem_cgroup_update_tree(struc
 | 
					 | 
				
			||||||
 	struct mem_cgroup_tree_per_node *mctz;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	if (lru_gen_enabled()) {
 | 
					 | 
				
			||||||
-		struct lruvec *lruvec = &memcg->nodeinfo[nid]->lruvec;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-		/* see the comment on MEMCG_NR_GENS */
 | 
					 | 
				
			||||||
-		if (soft_limit_excess(memcg) && lru_gen_memcg_seg(lruvec) != MEMCG_LRU_HEAD)
 | 
					 | 
				
			||||||
-			lru_gen_rotate_memcg(lruvec, MEMCG_LRU_HEAD);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
+		if (soft_limit_excess(memcg))
 | 
					 | 
				
			||||||
+			lru_gen_soft_reclaim(&memcg->nodeinfo[nid]->lruvec);
 | 
					 | 
				
			||||||
 		return;
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
--- a/mm/vmscan.c
 | 
					 | 
				
			||||||
+++ b/mm/vmscan.c
 | 
					 | 
				
			||||||
@@ -4692,6 +4692,148 @@ void lru_gen_look_around(struct page_vma
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /******************************************************************************
 | 
					 | 
				
			||||||
+ *                          memcg LRU
 | 
					 | 
				
			||||||
+ ******************************************************************************/
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* see the comment on MEMCG_NR_GENS */
 | 
					 | 
				
			||||||
+enum {
 | 
					 | 
				
			||||||
+	MEMCG_LRU_NOP,
 | 
					 | 
				
			||||||
+	MEMCG_LRU_HEAD,
 | 
					 | 
				
			||||||
+	MEMCG_LRU_TAIL,
 | 
					 | 
				
			||||||
+	MEMCG_LRU_OLD,
 | 
					 | 
				
			||||||
+	MEMCG_LRU_YOUNG,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#ifdef CONFIG_MEMCG
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int lru_gen_memcg_seg(struct lruvec *lruvec)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return READ_ONCE(lruvec->lrugen.seg);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void lru_gen_rotate_memcg(struct lruvec *lruvec, int op)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int seg;
 | 
					 | 
				
			||||||
+	int old, new;
 | 
					 | 
				
			||||||
+	int bin = get_random_u32_below(MEMCG_NR_BINS);
 | 
					 | 
				
			||||||
+	struct pglist_data *pgdat = lruvec_pgdat(lruvec);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	spin_lock(&pgdat->memcg_lru.lock);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	VM_WARN_ON_ONCE(hlist_nulls_unhashed(&lruvec->lrugen.list));
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	seg = 0;
 | 
					 | 
				
			||||||
+	new = old = lruvec->lrugen.gen;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* see the comment on MEMCG_NR_GENS */
 | 
					 | 
				
			||||||
+	if (op == MEMCG_LRU_HEAD)
 | 
					 | 
				
			||||||
+		seg = MEMCG_LRU_HEAD;
 | 
					 | 
				
			||||||
+	else if (op == MEMCG_LRU_TAIL)
 | 
					 | 
				
			||||||
+		seg = MEMCG_LRU_TAIL;
 | 
					 | 
				
			||||||
+	else if (op == MEMCG_LRU_OLD)
 | 
					 | 
				
			||||||
+		new = get_memcg_gen(pgdat->memcg_lru.seq);
 | 
					 | 
				
			||||||
+	else if (op == MEMCG_LRU_YOUNG)
 | 
					 | 
				
			||||||
+		new = get_memcg_gen(pgdat->memcg_lru.seq + 1);
 | 
					 | 
				
			||||||
+	else
 | 
					 | 
				
			||||||
+		VM_WARN_ON_ONCE(true);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	hlist_nulls_del_rcu(&lruvec->lrugen.list);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (op == MEMCG_LRU_HEAD || op == MEMCG_LRU_OLD)
 | 
					 | 
				
			||||||
+		hlist_nulls_add_head_rcu(&lruvec->lrugen.list, &pgdat->memcg_lru.fifo[new][bin]);
 | 
					 | 
				
			||||||
+	else
 | 
					 | 
				
			||||||
+		hlist_nulls_add_tail_rcu(&lruvec->lrugen.list, &pgdat->memcg_lru.fifo[new][bin]);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	pgdat->memcg_lru.nr_memcgs[old]--;
 | 
					 | 
				
			||||||
+	pgdat->memcg_lru.nr_memcgs[new]++;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	lruvec->lrugen.gen = new;
 | 
					 | 
				
			||||||
+	WRITE_ONCE(lruvec->lrugen.seg, seg);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (!pgdat->memcg_lru.nr_memcgs[old] && old == get_memcg_gen(pgdat->memcg_lru.seq))
 | 
					 | 
				
			||||||
+		WRITE_ONCE(pgdat->memcg_lru.seq, pgdat->memcg_lru.seq + 1);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	spin_unlock(&pgdat->memcg_lru.lock);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+void lru_gen_online_memcg(struct mem_cgroup *memcg)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int gen;
 | 
					 | 
				
			||||||
+	int nid;
 | 
					 | 
				
			||||||
+	int bin = get_random_u32_below(MEMCG_NR_BINS);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	for_each_node(nid) {
 | 
					 | 
				
			||||||
+		struct pglist_data *pgdat = NODE_DATA(nid);
 | 
					 | 
				
			||||||
+		struct lruvec *lruvec = get_lruvec(memcg, nid);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		spin_lock(&pgdat->memcg_lru.lock);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		VM_WARN_ON_ONCE(!hlist_nulls_unhashed(&lruvec->lrugen.list));
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		gen = get_memcg_gen(pgdat->memcg_lru.seq);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		hlist_nulls_add_tail_rcu(&lruvec->lrugen.list, &pgdat->memcg_lru.fifo[gen][bin]);
 | 
					 | 
				
			||||||
+		pgdat->memcg_lru.nr_memcgs[gen]++;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		lruvec->lrugen.gen = gen;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		spin_unlock(&pgdat->memcg_lru.lock);
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+void lru_gen_offline_memcg(struct mem_cgroup *memcg)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int nid;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	for_each_node(nid) {
 | 
					 | 
				
			||||||
+		struct lruvec *lruvec = get_lruvec(memcg, nid);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		lru_gen_rotate_memcg(lruvec, MEMCG_LRU_OLD);
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+void lru_gen_release_memcg(struct mem_cgroup *memcg)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int gen;
 | 
					 | 
				
			||||||
+	int nid;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	for_each_node(nid) {
 | 
					 | 
				
			||||||
+		struct pglist_data *pgdat = NODE_DATA(nid);
 | 
					 | 
				
			||||||
+		struct lruvec *lruvec = get_lruvec(memcg, nid);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		spin_lock(&pgdat->memcg_lru.lock);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		VM_WARN_ON_ONCE(hlist_nulls_unhashed(&lruvec->lrugen.list));
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		gen = lruvec->lrugen.gen;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		hlist_nulls_del_rcu(&lruvec->lrugen.list);
 | 
					 | 
				
			||||||
+		pgdat->memcg_lru.nr_memcgs[gen]--;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		if (!pgdat->memcg_lru.nr_memcgs[gen] && gen == get_memcg_gen(pgdat->memcg_lru.seq))
 | 
					 | 
				
			||||||
+			WRITE_ONCE(pgdat->memcg_lru.seq, pgdat->memcg_lru.seq + 1);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		spin_unlock(&pgdat->memcg_lru.lock);
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+void lru_gen_soft_reclaim(struct lruvec *lruvec)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	/* see the comment on MEMCG_NR_GENS */
 | 
					 | 
				
			||||||
+	if (lru_gen_memcg_seg(lruvec) != MEMCG_LRU_HEAD)
 | 
					 | 
				
			||||||
+		lru_gen_rotate_memcg(lruvec, MEMCG_LRU_HEAD);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#else /* !CONFIG_MEMCG */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int lru_gen_memcg_seg(struct lruvec *lruvec)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/******************************************************************************
 | 
					 | 
				
			||||||
  *                          the eviction
 | 
					 | 
				
			||||||
  ******************************************************************************/
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -5397,53 +5539,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
 | 
					 | 
				
			||||||
  ******************************************************************************/
 | 
					 | 
				
			||||||
@@ -6089,67 +6184,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)
 | 
					 | 
				
			||||||
@ -1,40 +0,0 @@
 | 
				
			|||||||
From bec433f29537652ed054148edfd7e2183ddcf7c3 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: "T.J. Alumbaugh" <talumbau@google.com>
 | 
					 | 
				
			||||||
Date: Wed, 18 Jan 2023 00:18:25 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH 16/19] UPSTREAM: mm: multi-gen LRU: improve
 | 
					 | 
				
			||||||
 lru_gen_exit_memcg()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Add warnings and poison ->next.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Link: https://lkml.kernel.org/r/20230118001827.1040870-6-talumbau@google.com
 | 
					 | 
				
			||||||
Change-Id: I53de9e04c1ae941e122b33cd45d2bbb5f34aae0c
 | 
					 | 
				
			||||||
Signed-off-by: T.J. Alumbaugh <talumbau@google.com>
 | 
					 | 
				
			||||||
Cc: Yu Zhao <yuzhao@google.com>
 | 
					 | 
				
			||||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
 | 
					 | 
				
			||||||
(cherry picked from commit 37cc99979d04cca677c0ad5c0acd1149ec165d1b)
 | 
					 | 
				
			||||||
Bug: 274865848
 | 
					 | 
				
			||||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 mm/vmscan.c | 5 +++++
 | 
					 | 
				
			||||||
 1 file changed, 5 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/mm/vmscan.c
 | 
					 | 
				
			||||||
+++ b/mm/vmscan.c
 | 
					 | 
				
			||||||
@@ -6171,12 +6171,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;
 | 
					 | 
				
			||||||
@ -1,135 +0,0 @@
 | 
				
			|||||||
From fc0e3b06e0f19917b7ecad7967a72f61d4743644 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: "T.J. Alumbaugh" <talumbau@google.com>
 | 
					 | 
				
			||||||
Date: Wed, 18 Jan 2023 00:18:26 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH 17/19] UPSTREAM: mm: multi-gen LRU: improve walk_pmd_range()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Improve readability of walk_pmd_range() and walk_pmd_range_locked().
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Link: https://lkml.kernel.org/r/20230118001827.1040870-7-talumbau@google.com
 | 
					 | 
				
			||||||
Change-Id: Ia084fbf53fe989673b7804ca8ca520af12d7d52a
 | 
					 | 
				
			||||||
Signed-off-by: T.J. Alumbaugh <talumbau@google.com>
 | 
					 | 
				
			||||||
Cc: Yu Zhao <yuzhao@google.com>
 | 
					 | 
				
			||||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
 | 
					 | 
				
			||||||
(cherry picked from commit b5ff4133617d0eced35b685da0bd0929dd9fabb7)
 | 
					 | 
				
			||||||
Bug: 274865848
 | 
					 | 
				
			||||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 mm/vmscan.c | 40 ++++++++++++++++++++--------------------
 | 
					 | 
				
			||||||
 1 file changed, 20 insertions(+), 20 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/mm/vmscan.c
 | 
					 | 
				
			||||||
+++ b/mm/vmscan.c
 | 
					 | 
				
			||||||
@@ -3980,8 +3980,8 @@ restart:
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #if defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG)
 | 
					 | 
				
			||||||
-static void walk_pmd_range_locked(pud_t *pud, unsigned long next, struct vm_area_struct *vma,
 | 
					 | 
				
			||||||
-				  struct mm_walk *args, unsigned long *bitmap, unsigned long *start)
 | 
					 | 
				
			||||||
+static void walk_pmd_range_locked(pud_t *pud, unsigned long addr, struct vm_area_struct *vma,
 | 
					 | 
				
			||||||
+				  struct mm_walk *args, unsigned long *bitmap, unsigned long *first)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	int i;
 | 
					 | 
				
			||||||
 	pmd_t *pmd;
 | 
					 | 
				
			||||||
@@ -3994,18 +3994,19 @@ static void walk_pmd_range_locked(pud_t
 | 
					 | 
				
			||||||
 	VM_WARN_ON_ONCE(pud_leaf(*pud));
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* try to batch at most 1+MIN_LRU_BATCH+1 entries */
 | 
					 | 
				
			||||||
-	if (*start == -1) {
 | 
					 | 
				
			||||||
-		*start = next;
 | 
					 | 
				
			||||||
+	if (*first == -1) {
 | 
					 | 
				
			||||||
+		*first = addr;
 | 
					 | 
				
			||||||
+		bitmap_zero(bitmap, MIN_LRU_BATCH);
 | 
					 | 
				
			||||||
 		return;
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	i = next == -1 ? 0 : pmd_index(next) - pmd_index(*start);
 | 
					 | 
				
			||||||
+	i = addr == -1 ? 0 : pmd_index(addr) - pmd_index(*first);
 | 
					 | 
				
			||||||
 	if (i && i <= MIN_LRU_BATCH) {
 | 
					 | 
				
			||||||
 		__set_bit(i - 1, bitmap);
 | 
					 | 
				
			||||||
 		return;
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	pmd = pmd_offset(pud, *start);
 | 
					 | 
				
			||||||
+	pmd = pmd_offset(pud, *first);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	ptl = pmd_lockptr(args->mm, pmd);
 | 
					 | 
				
			||||||
 	if (!spin_trylock(ptl))
 | 
					 | 
				
			||||||
@@ -4016,15 +4017,16 @@ static void walk_pmd_range_locked(pud_t
 | 
					 | 
				
			||||||
 	do {
 | 
					 | 
				
			||||||
 		unsigned long pfn;
 | 
					 | 
				
			||||||
 		struct folio *folio;
 | 
					 | 
				
			||||||
-		unsigned long addr = i ? (*start & PMD_MASK) + i * PMD_SIZE : *start;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		/* don't round down the first address */
 | 
					 | 
				
			||||||
+		addr = i ? (*first & PMD_MASK) + i * PMD_SIZE : *first;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 		pfn = get_pmd_pfn(pmd[i], vma, addr);
 | 
					 | 
				
			||||||
 		if (pfn == -1)
 | 
					 | 
				
			||||||
 			goto next;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 		if (!pmd_trans_huge(pmd[i])) {
 | 
					 | 
				
			||||||
-			if (arch_has_hw_nonleaf_pmd_young() &&
 | 
					 | 
				
			||||||
-			    get_cap(LRU_GEN_NONLEAF_YOUNG))
 | 
					 | 
				
			||||||
+			if (arch_has_hw_nonleaf_pmd_young() && get_cap(LRU_GEN_NONLEAF_YOUNG))
 | 
					 | 
				
			||||||
 				pmdp_test_and_clear_young(vma, addr, pmd + i);
 | 
					 | 
				
			||||||
 			goto next;
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
@@ -4053,12 +4055,11 @@ next:
 | 
					 | 
				
			||||||
 	arch_leave_lazy_mmu_mode();
 | 
					 | 
				
			||||||
 	spin_unlock(ptl);
 | 
					 | 
				
			||||||
 done:
 | 
					 | 
				
			||||||
-	*start = -1;
 | 
					 | 
				
			||||||
-	bitmap_zero(bitmap, MIN_LRU_BATCH);
 | 
					 | 
				
			||||||
+	*first = -1;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 #else
 | 
					 | 
				
			||||||
-static void walk_pmd_range_locked(pud_t *pud, unsigned long next, struct vm_area_struct *vma,
 | 
					 | 
				
			||||||
-				  struct mm_walk *args, unsigned long *bitmap, unsigned long *start)
 | 
					 | 
				
			||||||
+static void walk_pmd_range_locked(pud_t *pud, unsigned long addr, struct vm_area_struct *vma,
 | 
					 | 
				
			||||||
+				  struct mm_walk *args, unsigned long *bitmap, unsigned long *first)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 #endif
 | 
					 | 
				
			||||||
@@ -4071,9 +4072,9 @@ static void walk_pmd_range(pud_t *pud, u
 | 
					 | 
				
			||||||
 	unsigned long next;
 | 
					 | 
				
			||||||
 	unsigned long addr;
 | 
					 | 
				
			||||||
 	struct vm_area_struct *vma;
 | 
					 | 
				
			||||||
-	unsigned long pos = -1;
 | 
					 | 
				
			||||||
+	unsigned long bitmap[BITS_TO_LONGS(MIN_LRU_BATCH)];
 | 
					 | 
				
			||||||
+	unsigned long first = -1;
 | 
					 | 
				
			||||||
 	struct lru_gen_mm_walk *walk = args->private;
 | 
					 | 
				
			||||||
-	unsigned long bitmap[BITS_TO_LONGS(MIN_LRU_BATCH)] = {};
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	VM_WARN_ON_ONCE(pud_leaf(*pud));
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -4115,18 +4116,17 @@ restart:
 | 
					 | 
				
			||||||
 			if (pfn < pgdat->node_start_pfn || pfn >= pgdat_end_pfn(pgdat))
 | 
					 | 
				
			||||||
 				continue;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-			walk_pmd_range_locked(pud, addr, vma, args, bitmap, &pos);
 | 
					 | 
				
			||||||
+			walk_pmd_range_locked(pud, addr, vma, args, bitmap, &first);
 | 
					 | 
				
			||||||
 			continue;
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
 #endif
 | 
					 | 
				
			||||||
 		walk->mm_stats[MM_NONLEAF_TOTAL]++;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-		if (arch_has_hw_nonleaf_pmd_young() &&
 | 
					 | 
				
			||||||
-		    get_cap(LRU_GEN_NONLEAF_YOUNG)) {
 | 
					 | 
				
			||||||
+		if (arch_has_hw_nonleaf_pmd_young() && get_cap(LRU_GEN_NONLEAF_YOUNG)) {
 | 
					 | 
				
			||||||
 			if (!pmd_young(val))
 | 
					 | 
				
			||||||
 				continue;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-			walk_pmd_range_locked(pud, addr, vma, args, bitmap, &pos);
 | 
					 | 
				
			||||||
+			walk_pmd_range_locked(pud, addr, vma, args, bitmap, &first);
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 		if (!walk->force_scan && !test_bloom_filter(walk->lruvec, walk->max_seq, pmd + i))
 | 
					 | 
				
			||||||
@@ -4143,7 +4143,7 @@ restart:
 | 
					 | 
				
			||||||
 		update_bloom_filter(walk->lruvec, walk->max_seq + 1, pmd + i);
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	walk_pmd_range_locked(pud, -1, vma, args, bitmap, &pos);
 | 
					 | 
				
			||||||
+	walk_pmd_range_locked(pud, -1, vma, args, bitmap, &first);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	if (i < PTRS_PER_PMD && get_next_vma(PUD_MASK, PMD_SIZE, args, &start, &end))
 | 
					 | 
				
			||||||
 		goto restart;
 | 
					 | 
				
			||||||
@ -1,148 +0,0 @@
 | 
				
			|||||||
From e604c3ccb4dfbdde2467fccef9bb36170a392695 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: "T.J. Alumbaugh" <talumbau@google.com>
 | 
					 | 
				
			||||||
Date: Wed, 18 Jan 2023 00:18:27 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH 18/19] UPSTREAM: mm: multi-gen LRU: simplify
 | 
					 | 
				
			||||||
 lru_gen_look_around()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Update the folio generation in place with or without
 | 
					 | 
				
			||||||
current->reclaim_state->mm_walk.  The LRU lock is held for longer, if
 | 
					 | 
				
			||||||
mm_walk is NULL and the number of folios to update is more than
 | 
					 | 
				
			||||||
PAGEVEC_SIZE.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This causes a measurable regression from the LRU lock contention during a
 | 
					 | 
				
			||||||
microbencmark.  But a tiny regression is not worth the complexity.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Link: https://lkml.kernel.org/r/20230118001827.1040870-8-talumbau@google.com
 | 
					 | 
				
			||||||
Change-Id: I9ce18b4f4062e6c1c13c98ece9422478eb8e1846
 | 
					 | 
				
			||||||
Signed-off-by: T.J. Alumbaugh <talumbau@google.com>
 | 
					 | 
				
			||||||
Cc: Yu Zhao <yuzhao@google.com>
 | 
					 | 
				
			||||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
 | 
					 | 
				
			||||||
(cherry picked from commit abf086721a2f1e6897c57796f7268df1b194c750)
 | 
					 | 
				
			||||||
Bug: 274865848
 | 
					 | 
				
			||||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 mm/vmscan.c | 73 +++++++++++++++++------------------------------------
 | 
					 | 
				
			||||||
 1 file changed, 23 insertions(+), 50 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/mm/vmscan.c
 | 
					 | 
				
			||||||
+++ b/mm/vmscan.c
 | 
					 | 
				
			||||||
@@ -4573,13 +4573,12 @@ static void lru_gen_age_node(struct pgli
 | 
					 | 
				
			||||||
 void lru_gen_look_around(struct page_vma_mapped_walk *pvmw)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	int i;
 | 
					 | 
				
			||||||
-	pte_t *pte;
 | 
					 | 
				
			||||||
 	unsigned long start;
 | 
					 | 
				
			||||||
 	unsigned long end;
 | 
					 | 
				
			||||||
-	unsigned long addr;
 | 
					 | 
				
			||||||
 	struct lru_gen_mm_walk *walk;
 | 
					 | 
				
			||||||
 	int young = 0;
 | 
					 | 
				
			||||||
-	unsigned long bitmap[BITS_TO_LONGS(MIN_LRU_BATCH)] = {};
 | 
					 | 
				
			||||||
+	pte_t *pte = pvmw->pte;
 | 
					 | 
				
			||||||
+	unsigned long addr = pvmw->address;
 | 
					 | 
				
			||||||
 	struct folio *folio = pfn_folio(pvmw->pfn);
 | 
					 | 
				
			||||||
 	struct mem_cgroup *memcg = folio_memcg(folio);
 | 
					 | 
				
			||||||
 	struct pglist_data *pgdat = folio_pgdat(folio);
 | 
					 | 
				
			||||||
@@ -4596,25 +4595,28 @@ void lru_gen_look_around(struct page_vma
 | 
					 | 
				
			||||||
 	/* avoid taking the LRU lock under the PTL when possible */
 | 
					 | 
				
			||||||
 	walk = current->reclaim_state ? current->reclaim_state->mm_walk : NULL;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	start = max(pvmw->address & PMD_MASK, pvmw->vma->vm_start);
 | 
					 | 
				
			||||||
-	end = min(pvmw->address | ~PMD_MASK, pvmw->vma->vm_end - 1) + 1;
 | 
					 | 
				
			||||||
+	start = max(addr & PMD_MASK, pvmw->vma->vm_start);
 | 
					 | 
				
			||||||
+	end = min(addr | ~PMD_MASK, pvmw->vma->vm_end - 1) + 1;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	if (end - start > MIN_LRU_BATCH * PAGE_SIZE) {
 | 
					 | 
				
			||||||
-		if (pvmw->address - start < MIN_LRU_BATCH * PAGE_SIZE / 2)
 | 
					 | 
				
			||||||
+		if (addr - start < MIN_LRU_BATCH * PAGE_SIZE / 2)
 | 
					 | 
				
			||||||
 			end = start + MIN_LRU_BATCH * PAGE_SIZE;
 | 
					 | 
				
			||||||
-		else if (end - pvmw->address < MIN_LRU_BATCH * PAGE_SIZE / 2)
 | 
					 | 
				
			||||||
+		else if (end - addr < MIN_LRU_BATCH * PAGE_SIZE / 2)
 | 
					 | 
				
			||||||
 			start = end - MIN_LRU_BATCH * PAGE_SIZE;
 | 
					 | 
				
			||||||
 		else {
 | 
					 | 
				
			||||||
-			start = pvmw->address - MIN_LRU_BATCH * PAGE_SIZE / 2;
 | 
					 | 
				
			||||||
-			end = pvmw->address + MIN_LRU_BATCH * PAGE_SIZE / 2;
 | 
					 | 
				
			||||||
+			start = addr - MIN_LRU_BATCH * PAGE_SIZE / 2;
 | 
					 | 
				
			||||||
+			end = addr + MIN_LRU_BATCH * PAGE_SIZE / 2;
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	pte = pvmw->pte - (pvmw->address - start) / PAGE_SIZE;
 | 
					 | 
				
			||||||
+	/* folio_update_gen() requires stable folio_memcg() */
 | 
					 | 
				
			||||||
+	if (!mem_cgroup_trylock_pages(memcg))
 | 
					 | 
				
			||||||
+		return;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	rcu_read_lock();
 | 
					 | 
				
			||||||
 	arch_enter_lazy_mmu_mode();
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	pte -= (addr - start) / PAGE_SIZE;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	for (i = 0, addr = start; addr != end; i++, addr += PAGE_SIZE) {
 | 
					 | 
				
			||||||
 		unsigned long pfn;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -4639,56 +4641,27 @@ void lru_gen_look_around(struct page_vma
 | 
					 | 
				
			||||||
 		      !folio_test_swapcache(folio)))
 | 
					 | 
				
			||||||
 			folio_mark_dirty(folio);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+		if (walk) {
 | 
					 | 
				
			||||||
+			old_gen = folio_update_gen(folio, new_gen);
 | 
					 | 
				
			||||||
+			if (old_gen >= 0 && old_gen != new_gen)
 | 
					 | 
				
			||||||
+				update_batch_size(walk, folio, old_gen, new_gen);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+			continue;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 		old_gen = folio_lru_gen(folio);
 | 
					 | 
				
			||||||
 		if (old_gen < 0)
 | 
					 | 
				
			||||||
 			folio_set_referenced(folio);
 | 
					 | 
				
			||||||
 		else if (old_gen != new_gen)
 | 
					 | 
				
			||||||
-			__set_bit(i, bitmap);
 | 
					 | 
				
			||||||
+			folio_activate(folio);
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	arch_leave_lazy_mmu_mode();
 | 
					 | 
				
			||||||
-	rcu_read_unlock();
 | 
					 | 
				
			||||||
+	mem_cgroup_unlock_pages();
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* feedback from rmap walkers to page table walkers */
 | 
					 | 
				
			||||||
 	if (suitable_to_scan(i, young))
 | 
					 | 
				
			||||||
 		update_bloom_filter(lruvec, max_seq, pvmw->pmd);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	if (!walk && bitmap_weight(bitmap, MIN_LRU_BATCH) < PAGEVEC_SIZE) {
 | 
					 | 
				
			||||||
-		for_each_set_bit(i, bitmap, MIN_LRU_BATCH) {
 | 
					 | 
				
			||||||
-			folio = pfn_folio(pte_pfn(pte[i]));
 | 
					 | 
				
			||||||
-			folio_activate(folio);
 | 
					 | 
				
			||||||
-		}
 | 
					 | 
				
			||||||
-		return;
 | 
					 | 
				
			||||||
-	}
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	/* folio_update_gen() requires stable folio_memcg() */
 | 
					 | 
				
			||||||
-	if (!mem_cgroup_trylock_pages(memcg))
 | 
					 | 
				
			||||||
-		return;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	if (!walk) {
 | 
					 | 
				
			||||||
-		spin_lock_irq(&lruvec->lru_lock);
 | 
					 | 
				
			||||||
-		new_gen = lru_gen_from_seq(lruvec->lrugen.max_seq);
 | 
					 | 
				
			||||||
-	}
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	for_each_set_bit(i, bitmap, MIN_LRU_BATCH) {
 | 
					 | 
				
			||||||
-		folio = pfn_folio(pte_pfn(pte[i]));
 | 
					 | 
				
			||||||
-		if (folio_memcg_rcu(folio) != memcg)
 | 
					 | 
				
			||||||
-			continue;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-		old_gen = folio_update_gen(folio, new_gen);
 | 
					 | 
				
			||||||
-		if (old_gen < 0 || old_gen == new_gen)
 | 
					 | 
				
			||||||
-			continue;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-		if (walk)
 | 
					 | 
				
			||||||
-			update_batch_size(walk, folio, old_gen, new_gen);
 | 
					 | 
				
			||||||
-		else
 | 
					 | 
				
			||||||
-			lru_gen_update_size(lruvec, folio, old_gen, new_gen);
 | 
					 | 
				
			||||||
-	}
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	if (!walk)
 | 
					 | 
				
			||||||
-		spin_unlock_irq(&lruvec->lru_lock);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	mem_cgroup_unlock_pages();
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /******************************************************************************
 | 
					 | 
				
			||||||
@ -1,273 +0,0 @@
 | 
				
			|||||||
From 418038c22452df38cde519cc8c662bb15139764a Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Kalesh Singh <kaleshsingh@google.com>
 | 
					 | 
				
			||||||
Date: Thu, 13 Apr 2023 14:43:26 -0700
 | 
					 | 
				
			||||||
Subject: [PATCH 19/19] mm: Multi-gen LRU: remove wait_event_killable()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Android 14 and later default to MGLRU [1] and field telemetry showed
 | 
					 | 
				
			||||||
occasional long tail latency (>100ms) in the reclaim path.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Tracing revealed priority inversion in the reclaim path.  In
 | 
					 | 
				
			||||||
try_to_inc_max_seq(), when high priority tasks were blocked on
 | 
					 | 
				
			||||||
wait_event_killable(), the preemption of the low priority task to call
 | 
					 | 
				
			||||||
wake_up_all() caused those high priority tasks to wait longer than
 | 
					 | 
				
			||||||
necessary.  In general, this problem is not different from others of its
 | 
					 | 
				
			||||||
kind, e.g., one caused by mutex_lock().  However, it is specific to MGLRU
 | 
					 | 
				
			||||||
because it introduced the new wait queue lruvec->mm_state.wait.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The purpose of this new wait queue is to avoid the thundering herd
 | 
					 | 
				
			||||||
problem.  If many direct reclaimers rush into try_to_inc_max_seq(), only
 | 
					 | 
				
			||||||
one can succeed, i.e., the one to wake up the rest, and the rest who
 | 
					 | 
				
			||||||
failed might cause premature OOM kills if they do not wait.  So far there
 | 
					 | 
				
			||||||
is no evidence supporting this scenario, based on how often the wait has
 | 
					 | 
				
			||||||
been hit.  And this begs the question how useful the wait queue is in
 | 
					 | 
				
			||||||
practice.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Based on Minchan's recommendation, which is in line with his commit
 | 
					 | 
				
			||||||
6d4675e60135 ("mm: don't be stuck to rmap lock on reclaim path") and the
 | 
					 | 
				
			||||||
rest of the MGLRU code which also uses trylock when possible, remove the
 | 
					 | 
				
			||||||
wait queue.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[1] https://android-review.googlesource.com/q/I7ed7fbfd6ef9ce10053347528125dd98c39e50bf
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Link: https://lkml.kernel.org/r/20230413214326.2147568-1-kaleshsingh@google.com
 | 
					 | 
				
			||||||
Fixes: bd74fdaea146 ("mm: multi-gen LRU: support page table walks")
 | 
					 | 
				
			||||||
Signed-off-by: Kalesh Singh <kaleshsingh@google.com>
 | 
					 | 
				
			||||||
Suggested-by: Minchan Kim <minchan@kernel.org>
 | 
					 | 
				
			||||||
Reported-by: Wei Wang <wvw@google.com>
 | 
					 | 
				
			||||||
Acked-by: Yu Zhao <yuzhao@google.com>
 | 
					 | 
				
			||||||
Cc: Minchan Kim <minchan@kernel.org>
 | 
					 | 
				
			||||||
Cc: Jan Alexander Steffens (heftig) <heftig@archlinux.org>
 | 
					 | 
				
			||||||
Cc: Oleksandr Natalenko <oleksandr@natalenko.name>
 | 
					 | 
				
			||||||
Cc: Suleiman Souhlal <suleiman@google.com>
 | 
					 | 
				
			||||||
Cc: Suren Baghdasaryan <surenb@google.com>
 | 
					 | 
				
			||||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 include/linux/mmzone.h |   8 +--
 | 
					 | 
				
			||||||
 mm/vmscan.c            | 112 +++++++++++++++--------------------------
 | 
					 | 
				
			||||||
 2 files changed, 42 insertions(+), 78 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/include/linux/mmzone.h
 | 
					 | 
				
			||||||
+++ b/include/linux/mmzone.h
 | 
					 | 
				
			||||||
@@ -453,18 +453,14 @@ enum {
 | 
					 | 
				
			||||||
 struct lru_gen_mm_state {
 | 
					 | 
				
			||||||
 	/* set to max_seq after each iteration */
 | 
					 | 
				
			||||||
 	unsigned long seq;
 | 
					 | 
				
			||||||
-	/* where the current iteration continues (inclusive) */
 | 
					 | 
				
			||||||
+	/* where the current iteration continues after */
 | 
					 | 
				
			||||||
 	struct list_head *head;
 | 
					 | 
				
			||||||
-	/* where the last iteration ended (exclusive) */
 | 
					 | 
				
			||||||
+	/* where the last iteration ended before */
 | 
					 | 
				
			||||||
 	struct list_head *tail;
 | 
					 | 
				
			||||||
-	/* to wait for the last page table walker to finish */
 | 
					 | 
				
			||||||
-	struct wait_queue_head wait;
 | 
					 | 
				
			||||||
 	/* Bloom filters flip after each iteration */
 | 
					 | 
				
			||||||
 	unsigned long *filters[NR_BLOOM_FILTERS];
 | 
					 | 
				
			||||||
 	/* the mm stats for debugging */
 | 
					 | 
				
			||||||
 	unsigned long stats[NR_HIST_GENS][NR_MM_STATS];
 | 
					 | 
				
			||||||
-	/* the number of concurrent page table walkers */
 | 
					 | 
				
			||||||
-	int nr_walkers;
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 struct lru_gen_mm_walk {
 | 
					 | 
				
			||||||
--- a/mm/vmscan.c
 | 
					 | 
				
			||||||
+++ b/mm/vmscan.c
 | 
					 | 
				
			||||||
@@ -3371,18 +3371,13 @@ void lru_gen_del_mm(struct mm_struct *mm
 | 
					 | 
				
			||||||
 		if (!lruvec)
 | 
					 | 
				
			||||||
 			continue;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-		/* where the last iteration ended (exclusive) */
 | 
					 | 
				
			||||||
+		/* where the current iteration continues after */
 | 
					 | 
				
			||||||
+		if (lruvec->mm_state.head == &mm->lru_gen.list)
 | 
					 | 
				
			||||||
+			lruvec->mm_state.head = lruvec->mm_state.head->prev;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		/* where the last iteration ended before */
 | 
					 | 
				
			||||||
 		if (lruvec->mm_state.tail == &mm->lru_gen.list)
 | 
					 | 
				
			||||||
 			lruvec->mm_state.tail = lruvec->mm_state.tail->next;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-		/* where the current iteration continues (inclusive) */
 | 
					 | 
				
			||||||
-		if (lruvec->mm_state.head != &mm->lru_gen.list)
 | 
					 | 
				
			||||||
-			continue;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-		lruvec->mm_state.head = lruvec->mm_state.head->next;
 | 
					 | 
				
			||||||
-		/* the deletion ends the current iteration */
 | 
					 | 
				
			||||||
-		if (lruvec->mm_state.head == &mm_list->fifo)
 | 
					 | 
				
			||||||
-			WRITE_ONCE(lruvec->mm_state.seq, lruvec->mm_state.seq + 1);
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	list_del_init(&mm->lru_gen.list);
 | 
					 | 
				
			||||||
@@ -3478,68 +3473,54 @@ static bool iterate_mm_list(struct lruve
 | 
					 | 
				
			||||||
 			    struct mm_struct **iter)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	bool first = false;
 | 
					 | 
				
			||||||
-	bool last = true;
 | 
					 | 
				
			||||||
+	bool last = false;
 | 
					 | 
				
			||||||
 	struct mm_struct *mm = NULL;
 | 
					 | 
				
			||||||
 	struct mem_cgroup *memcg = lruvec_memcg(lruvec);
 | 
					 | 
				
			||||||
 	struct lru_gen_mm_list *mm_list = get_mm_list(memcg);
 | 
					 | 
				
			||||||
 	struct lru_gen_mm_state *mm_state = &lruvec->mm_state;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/*
 | 
					 | 
				
			||||||
-	 * There are four interesting cases for this page table walker:
 | 
					 | 
				
			||||||
-	 * 1. It tries to start a new iteration of mm_list with a stale max_seq;
 | 
					 | 
				
			||||||
-	 *    there is nothing left to do.
 | 
					 | 
				
			||||||
-	 * 2. It's the first of the current generation, and it needs to reset
 | 
					 | 
				
			||||||
-	 *    the Bloom filter for the next generation.
 | 
					 | 
				
			||||||
-	 * 3. It reaches the end of mm_list, and it needs to increment
 | 
					 | 
				
			||||||
-	 *    mm_state->seq; the iteration is done.
 | 
					 | 
				
			||||||
-	 * 4. It's the last of the current generation, and it needs to reset the
 | 
					 | 
				
			||||||
-	 *    mm stats counters for the next generation.
 | 
					 | 
				
			||||||
+	 * mm_state->seq is incremented after each iteration of mm_list. There
 | 
					 | 
				
			||||||
+	 * are three interesting cases for this page table walker:
 | 
					 | 
				
			||||||
+	 * 1. It tries to start a new iteration with a stale max_seq: there is
 | 
					 | 
				
			||||||
+	 *    nothing left to do.
 | 
					 | 
				
			||||||
+	 * 2. It started the next iteration: it needs to reset the Bloom filter
 | 
					 | 
				
			||||||
+	 *    so that a fresh set of PTE tables can be recorded.
 | 
					 | 
				
			||||||
+	 * 3. It ended the current iteration: it needs to reset the mm stats
 | 
					 | 
				
			||||||
+	 *    counters and tell its caller to increment max_seq.
 | 
					 | 
				
			||||||
 	 */
 | 
					 | 
				
			||||||
 	spin_lock(&mm_list->lock);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	VM_WARN_ON_ONCE(mm_state->seq + 1 < walk->max_seq);
 | 
					 | 
				
			||||||
-	VM_WARN_ON_ONCE(*iter && mm_state->seq > walk->max_seq);
 | 
					 | 
				
			||||||
-	VM_WARN_ON_ONCE(*iter && !mm_state->nr_walkers);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if (walk->max_seq <= mm_state->seq) {
 | 
					 | 
				
			||||||
-		if (!*iter)
 | 
					 | 
				
			||||||
-			last = false;
 | 
					 | 
				
			||||||
+	if (walk->max_seq <= mm_state->seq)
 | 
					 | 
				
			||||||
 		goto done;
 | 
					 | 
				
			||||||
-	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if (!mm_state->nr_walkers) {
 | 
					 | 
				
			||||||
-		VM_WARN_ON_ONCE(mm_state->head && mm_state->head != &mm_list->fifo);
 | 
					 | 
				
			||||||
+	if (!mm_state->head)
 | 
					 | 
				
			||||||
+		mm_state->head = &mm_list->fifo;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-		mm_state->head = mm_list->fifo.next;
 | 
					 | 
				
			||||||
+	if (mm_state->head == &mm_list->fifo)
 | 
					 | 
				
			||||||
 		first = true;
 | 
					 | 
				
			||||||
-	}
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	while (!mm && mm_state->head != &mm_list->fifo) {
 | 
					 | 
				
			||||||
-		mm = list_entry(mm_state->head, struct mm_struct, lru_gen.list);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	do {
 | 
					 | 
				
			||||||
 		mm_state->head = mm_state->head->next;
 | 
					 | 
				
			||||||
+		if (mm_state->head == &mm_list->fifo) {
 | 
					 | 
				
			||||||
+			WRITE_ONCE(mm_state->seq, mm_state->seq + 1);
 | 
					 | 
				
			||||||
+			last = true;
 | 
					 | 
				
			||||||
+			break;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 		/* force scan for those added after the last iteration */
 | 
					 | 
				
			||||||
-		if (!mm_state->tail || mm_state->tail == &mm->lru_gen.list) {
 | 
					 | 
				
			||||||
-			mm_state->tail = mm_state->head;
 | 
					 | 
				
			||||||
+		if (!mm_state->tail || mm_state->tail == mm_state->head) {
 | 
					 | 
				
			||||||
+			mm_state->tail = mm_state->head->next;
 | 
					 | 
				
			||||||
 			walk->force_scan = true;
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+		mm = list_entry(mm_state->head, struct mm_struct, lru_gen.list);
 | 
					 | 
				
			||||||
 		if (should_skip_mm(mm, walk))
 | 
					 | 
				
			||||||
 			mm = NULL;
 | 
					 | 
				
			||||||
-	}
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	if (mm_state->head == &mm_list->fifo)
 | 
					 | 
				
			||||||
-		WRITE_ONCE(mm_state->seq, mm_state->seq + 1);
 | 
					 | 
				
			||||||
+	} while (!mm);
 | 
					 | 
				
			||||||
 done:
 | 
					 | 
				
			||||||
-	if (*iter && !mm)
 | 
					 | 
				
			||||||
-		mm_state->nr_walkers--;
 | 
					 | 
				
			||||||
-	if (!*iter && mm)
 | 
					 | 
				
			||||||
-		mm_state->nr_walkers++;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	if (mm_state->nr_walkers)
 | 
					 | 
				
			||||||
-		last = false;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 	if (*iter || last)
 | 
					 | 
				
			||||||
 		reset_mm_stats(lruvec, walk, last);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -3567,9 +3548,9 @@ static bool iterate_mm_list_nowalk(struc
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	VM_WARN_ON_ONCE(mm_state->seq + 1 < max_seq);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if (max_seq > mm_state->seq && !mm_state->nr_walkers) {
 | 
					 | 
				
			||||||
-		VM_WARN_ON_ONCE(mm_state->head && mm_state->head != &mm_list->fifo);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
+	if (max_seq > mm_state->seq) {
 | 
					 | 
				
			||||||
+		mm_state->head = NULL;
 | 
					 | 
				
			||||||
+		mm_state->tail = NULL;
 | 
					 | 
				
			||||||
 		WRITE_ONCE(mm_state->seq, mm_state->seq + 1);
 | 
					 | 
				
			||||||
 		reset_mm_stats(lruvec, NULL, true);
 | 
					 | 
				
			||||||
 		success = true;
 | 
					 | 
				
			||||||
@@ -4172,10 +4153,6 @@ restart:
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 		walk_pmd_range(&val, addr, next, args);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-		/* a racy check to curtail the waiting time */
 | 
					 | 
				
			||||||
-		if (wq_has_sleeper(&walk->lruvec->mm_state.wait))
 | 
					 | 
				
			||||||
-			return 1;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 		if (need_resched() || walk->batched >= MAX_LRU_BATCH) {
 | 
					 | 
				
			||||||
 			end = (addr | ~PUD_MASK) + 1;
 | 
					 | 
				
			||||||
 			goto done;
 | 
					 | 
				
			||||||
@@ -4208,8 +4185,14 @@ static void walk_mm(struct lruvec *lruve
 | 
					 | 
				
			||||||
 	walk->next_addr = FIRST_USER_ADDRESS;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	do {
 | 
					 | 
				
			||||||
+		DEFINE_MAX_SEQ(lruvec);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 		err = -EBUSY;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+		/* another thread might have called inc_max_seq() */
 | 
					 | 
				
			||||||
+		if (walk->max_seq != max_seq)
 | 
					 | 
				
			||||||
+			break;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 		/* folio_update_gen() requires stable folio_memcg() */
 | 
					 | 
				
			||||||
 		if (!mem_cgroup_trylock_pages(memcg))
 | 
					 | 
				
			||||||
 			break;
 | 
					 | 
				
			||||||
@@ -4444,25 +4427,12 @@ static bool try_to_inc_max_seq(struct lr
 | 
					 | 
				
			||||||
 		success = iterate_mm_list(lruvec, walk, &mm);
 | 
					 | 
				
			||||||
 		if (mm)
 | 
					 | 
				
			||||||
 			walk_mm(lruvec, mm, walk);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-		cond_resched();
 | 
					 | 
				
			||||||
 	} while (mm);
 | 
					 | 
				
			||||||
 done:
 | 
					 | 
				
			||||||
-	if (!success) {
 | 
					 | 
				
			||||||
-		if (sc->priority <= DEF_PRIORITY - 2)
 | 
					 | 
				
			||||||
-			wait_event_killable(lruvec->mm_state.wait,
 | 
					 | 
				
			||||||
-					    max_seq < READ_ONCE(lrugen->max_seq));
 | 
					 | 
				
			||||||
-		return false;
 | 
					 | 
				
			||||||
-	}
 | 
					 | 
				
			||||||
+	if (success)
 | 
					 | 
				
			||||||
+		inc_max_seq(lruvec, can_swap, force_scan);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	VM_WARN_ON_ONCE(max_seq != READ_ONCE(lrugen->max_seq));
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	inc_max_seq(lruvec, can_swap, force_scan);
 | 
					 | 
				
			||||||
-	/* either this sees any waiters or they will see updated max_seq */
 | 
					 | 
				
			||||||
-	if (wq_has_sleeper(&lruvec->mm_state.wait))
 | 
					 | 
				
			||||||
-		wake_up_all(&lruvec->mm_state.wait);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	return true;
 | 
					 | 
				
			||||||
+	return success;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /******************************************************************************
 | 
					 | 
				
			||||||
@@ -6116,7 +6086,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
 | 
					 | 
				
			||||||
@@ -6149,7 +6118,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)));
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@ -1,58 +0,0 @@
 | 
				
			|||||||
From e2192de59e457aef8d1f055a452131f0b3e5d097 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Johannes Berg <johannes.berg@intel.com>
 | 
					 | 
				
			||||||
Date: Wed, 18 Jan 2023 14:26:53 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] bitfield: add FIELD_PREP_CONST()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Neither FIELD_PREP() nor *_encode_bits() can be used
 | 
					 | 
				
			||||||
in constant contexts (such as initializers), but we
 | 
					 | 
				
			||||||
don't want to define shift constants for all masks
 | 
					 | 
				
			||||||
just for use in initializers, and having checks that
 | 
					 | 
				
			||||||
the values fit is also useful.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Therefore, add FIELD_PREP_CONST() which is a smaller
 | 
					 | 
				
			||||||
version of FIELD_PREP() that can only take constant
 | 
					 | 
				
			||||||
arguments and has less friendly (but not less strict)
 | 
					 | 
				
			||||||
error checks, and expands to a constant value.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
 | 
					 | 
				
			||||||
Link: https://lore.kernel.org/r/20230118142652.53f20593504b.Iaeea0aee77a6493d70e573b4aa55c91c00e01e4b@changeid
 | 
					 | 
				
			||||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 include/linux/bitfield.h | 26 ++++++++++++++++++++++++++
 | 
					 | 
				
			||||||
 1 file changed, 26 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/include/linux/bitfield.h
 | 
					 | 
				
			||||||
+++ b/include/linux/bitfield.h
 | 
					 | 
				
			||||||
@@ -115,6 +115,32 @@
 | 
					 | 
				
			||||||
 		((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask);	\
 | 
					 | 
				
			||||||
 	})
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+#define __BF_CHECK_POW2(n)	BUILD_BUG_ON_ZERO(((n) & ((n) - 1)) != 0)
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/**
 | 
					 | 
				
			||||||
+ * FIELD_PREP_CONST() - prepare a constant bitfield element
 | 
					 | 
				
			||||||
+ * @_mask: shifted mask defining the field's length and position
 | 
					 | 
				
			||||||
+ * @_val:  value to put in the field
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * FIELD_PREP_CONST() masks and shifts up the value.  The result should
 | 
					 | 
				
			||||||
+ * be combined with other fields of the bitfield using logical OR.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Unlike FIELD_PREP() this is a constant expression and can therefore
 | 
					 | 
				
			||||||
+ * be used in initializers. Error checking is less comfortable for this
 | 
					 | 
				
			||||||
+ * version, and non-constant masks cannot be used.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+#define FIELD_PREP_CONST(_mask, _val)					\
 | 
					 | 
				
			||||||
+	(								\
 | 
					 | 
				
			||||||
+		/* mask must be non-zero */				\
 | 
					 | 
				
			||||||
+		BUILD_BUG_ON_ZERO((_mask) == 0) +			\
 | 
					 | 
				
			||||||
+		/* check if value fits */				\
 | 
					 | 
				
			||||||
+		BUILD_BUG_ON_ZERO(~((_mask) >> __bf_shf(_mask)) & (_val)) + \
 | 
					 | 
				
			||||||
+		/* check if mask is contiguous */			\
 | 
					 | 
				
			||||||
+		__BF_CHECK_POW2((_mask) + (1ULL << __bf_shf(_mask))) +	\
 | 
					 | 
				
			||||||
+		/* and create the value */				\
 | 
					 | 
				
			||||||
+		(((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask))	\
 | 
					 | 
				
			||||||
+	)
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 /**
 | 
					 | 
				
			||||||
  * FIELD_GET() - extract a bitfield element
 | 
					 | 
				
			||||||
  * @_mask: shifted mask defining the field's length and position
 | 
					 | 
				
			||||||
@ -1,63 +0,0 @@
 | 
				
			|||||||
From 579aee9fc594af94c242068c011b0233563d4bbf Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Stephen Rothwell <sfr@canb.auug.org.au>
 | 
					 | 
				
			||||||
Date: Mon, 10 Oct 2022 16:57:21 +1100
 | 
					 | 
				
			||||||
Subject: [PATCH] powerpc: suppress some linker warnings in recent linker
 | 
					 | 
				
			||||||
 versions
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This is a follow on from commit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  0d362be5b142 ("Makefile: link with -z noexecstack --no-warn-rwx-segments")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
for arch/powerpc/boot to address wanrings like:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ld: warning: opal-calls.o: missing .note.GNU-stack section implies executable stack
 | 
					 | 
				
			||||||
  ld: NOTE: This behaviour is deprecated and will be removed in a future version of the linker
 | 
					 | 
				
			||||||
  ld: warning: arch/powerpc/boot/zImage.epapr has a LOAD segment with RWX permissions
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This fixes issue https://github.com/linuxppc/issues/issues/417
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
 | 
					 | 
				
			||||||
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
 | 
					 | 
				
			||||||
Link: https://lore.kernel.org/r/20221010165721.106267e6@canb.auug.org.au
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 arch/powerpc/boot/wrapper | 15 ++++++++++++++-
 | 
					 | 
				
			||||||
 1 file changed, 14 insertions(+), 1 deletion(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/arch/powerpc/boot/wrapper
 | 
					 | 
				
			||||||
+++ b/arch/powerpc/boot/wrapper
 | 
					 | 
				
			||||||
@@ -215,6 +215,11 @@ ld_version()
 | 
					 | 
				
			||||||
     }'
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+ld_is_lld()
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	${CROSS}ld -V 2>&1 | grep -q LLD
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 # Do not include PT_INTERP segment when linking pie. Non-pie linking
 | 
					 | 
				
			||||||
 # just ignores this option.
 | 
					 | 
				
			||||||
 LD_VERSION=$(${CROSS}ld --version | ld_version)
 | 
					 | 
				
			||||||
@@ -223,6 +228,14 @@ if [ "$LD_VERSION" -ge "$LD_NO_DL_MIN_VE
 | 
					 | 
				
			||||||
 	nodl="--no-dynamic-linker"
 | 
					 | 
				
			||||||
 fi
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+# suppress some warnings in recent ld versions
 | 
					 | 
				
			||||||
+nowarn="-z noexecstack"
 | 
					 | 
				
			||||||
+if ! ld_is_lld; then
 | 
					 | 
				
			||||||
+	if [ "$LD_VERSION" -ge "$(echo 2.39 | ld_version)" ]; then
 | 
					 | 
				
			||||||
+		nowarn="$nowarn --no-warn-rwx-segments"
 | 
					 | 
				
			||||||
+	fi
 | 
					 | 
				
			||||||
+fi
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 platformo=$object/"$platform".o
 | 
					 | 
				
			||||||
 lds=$object/zImage.lds
 | 
					 | 
				
			||||||
 ext=strip
 | 
					 | 
				
			||||||
@@ -504,7 +517,7 @@ if [ "$platform" != "miboot" ]; then
 | 
					 | 
				
			||||||
         text_start="-Ttext $link_address"
 | 
					 | 
				
			||||||
     fi
 | 
					 | 
				
			||||||
 #link everything
 | 
					 | 
				
			||||||
-    ${CROSS}ld -m $format -T $lds $text_start $pie $nodl $rodynamic $notext -o "$ofile" $map \
 | 
					 | 
				
			||||||
+    ${CROSS}ld -m $format -T $lds $text_start $pie $nodl $nowarn $rodynamic $notext -o "$ofile" $map \
 | 
					 | 
				
			||||||
 	$platformo $tmp $object/wrapper.a
 | 
					 | 
				
			||||||
     rm $tmp
 | 
					 | 
				
			||||||
 fi
 | 
					 | 
				
			||||||
@ -1,161 +0,0 @@
 | 
				
			|||||||
From 66a5c40f60f5d88ad8d47ba6a4ba05892853fa1f Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Tanzir Hasan <tanzirh@google.com>
 | 
					 | 
				
			||||||
Date: Tue, 26 Dec 2023 18:00:00 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] kernel.h: removed REPEAT_BYTE from kernel.h
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This patch creates wordpart.h and includes it in asm/word-at-a-time.h
 | 
					 | 
				
			||||||
for all architectures. WORD_AT_A_TIME_CONSTANTS depends on kernel.h
 | 
					 | 
				
			||||||
because of REPEAT_BYTE. Moving this to another header and including it
 | 
					 | 
				
			||||||
where necessary allows us to not include the bloated kernel.h. Making
 | 
					 | 
				
			||||||
this implicit dependency on REPEAT_BYTE explicit allows for later
 | 
					 | 
				
			||||||
improvements in the lib/string.c inclusion list.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Suggested-by: Al Viro <viro@zeniv.linux.org.uk>
 | 
					 | 
				
			||||||
Suggested-by: Andy Shevchenko <andy.shevchenko@gmail.com>
 | 
					 | 
				
			||||||
Signed-off-by: Tanzir Hasan <tanzirh@google.com>
 | 
					 | 
				
			||||||
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
 | 
					 | 
				
			||||||
Link: https://lore.kernel.org/r/20231226-libstringheader-v6-1-80aa08c7652c@google.com
 | 
					 | 
				
			||||||
Signed-off-by: Kees Cook <keescook@chromium.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 arch/arm/include/asm/word-at-a-time.h     |  3 ++-
 | 
					 | 
				
			||||||
 arch/arm64/include/asm/word-at-a-time.h   |  3 ++-
 | 
					 | 
				
			||||||
 arch/powerpc/include/asm/word-at-a-time.h |  4 ++--
 | 
					 | 
				
			||||||
 arch/riscv/include/asm/word-at-a-time.h   |  3 ++-
 | 
					 | 
				
			||||||
 arch/s390/include/asm/word-at-a-time.h    |  3 ++-
 | 
					 | 
				
			||||||
 arch/sh/include/asm/word-at-a-time.h      |  2 ++
 | 
					 | 
				
			||||||
 arch/x86/include/asm/word-at-a-time.h     |  3 ++-
 | 
					 | 
				
			||||||
 arch/x86/kvm/mmu/mmu.c                    |  1 +
 | 
					 | 
				
			||||||
 fs/namei.c                                |  2 +-
 | 
					 | 
				
			||||||
 include/asm-generic/word-at-a-time.h      |  3 ++-
 | 
					 | 
				
			||||||
 include/linux/kernel.h                    |  8 --------
 | 
					 | 
				
			||||||
 include/linux/wordpart.h                  | 13 +++++++++++++
 | 
					 | 
				
			||||||
 12 files changed, 31 insertions(+), 17 deletions(-)
 | 
					 | 
				
			||||||
 create mode 100644 include/linux/wordpart.h
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/arch/arm/include/asm/word-at-a-time.h
 | 
					 | 
				
			||||||
+++ b/arch/arm/include/asm/word-at-a-time.h
 | 
					 | 
				
			||||||
@@ -8,7 +8,8 @@
 | 
					 | 
				
			||||||
  * Little-endian word-at-a-time zero byte handling.
 | 
					 | 
				
			||||||
  * Heavily based on the x86 algorithm.
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
-#include <linux/kernel.h>
 | 
					 | 
				
			||||||
+#include <linux/bitops.h>
 | 
					 | 
				
			||||||
+#include <linux/wordpart.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 struct word_at_a_time {
 | 
					 | 
				
			||||||
 	const unsigned long one_bits, high_bits;
 | 
					 | 
				
			||||||
--- a/arch/arm64/include/asm/word-at-a-time.h
 | 
					 | 
				
			||||||
+++ b/arch/arm64/include/asm/word-at-a-time.h
 | 
					 | 
				
			||||||
@@ -9,7 +9,8 @@
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #ifndef __AARCH64EB__
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-#include <linux/kernel.h>
 | 
					 | 
				
			||||||
+#include <linux/bitops.h>
 | 
					 | 
				
			||||||
+#include <linux/wordpart.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 struct word_at_a_time {
 | 
					 | 
				
			||||||
 	const unsigned long one_bits, high_bits;
 | 
					 | 
				
			||||||
--- a/arch/powerpc/include/asm/word-at-a-time.h
 | 
					 | 
				
			||||||
+++ b/arch/powerpc/include/asm/word-at-a-time.h
 | 
					 | 
				
			||||||
@@ -4,8 +4,8 @@
 | 
					 | 
				
			||||||
 /*
 | 
					 | 
				
			||||||
  * Word-at-a-time interfaces for PowerPC.
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-#include <linux/kernel.h>
 | 
					 | 
				
			||||||
+#include <linux/bitops.h>
 | 
					 | 
				
			||||||
+#include <linux/wordpart.h>
 | 
					 | 
				
			||||||
 #include <asm/asm-compat.h>
 | 
					 | 
				
			||||||
 #include <asm/extable.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
--- a/arch/sh/include/asm/word-at-a-time.h
 | 
					 | 
				
			||||||
+++ b/arch/sh/include/asm/word-at-a-time.h
 | 
					 | 
				
			||||||
@@ -5,6 +5,8 @@
 | 
					 | 
				
			||||||
 #ifdef CONFIG_CPU_BIG_ENDIAN
 | 
					 | 
				
			||||||
 # include <asm-generic/word-at-a-time.h>
 | 
					 | 
				
			||||||
 #else
 | 
					 | 
				
			||||||
+#include <linux/bitops.h>
 | 
					 | 
				
			||||||
+#include <linux/wordpart.h>
 | 
					 | 
				
			||||||
 /*
 | 
					 | 
				
			||||||
  * Little-endian version cribbed from x86.
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
--- a/arch/x86/include/asm/word-at-a-time.h
 | 
					 | 
				
			||||||
+++ b/arch/x86/include/asm/word-at-a-time.h
 | 
					 | 
				
			||||||
@@ -2,7 +2,8 @@
 | 
					 | 
				
			||||||
 #ifndef _ASM_WORD_AT_A_TIME_H
 | 
					 | 
				
			||||||
 #define _ASM_WORD_AT_A_TIME_H
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-#include <linux/kernel.h>
 | 
					 | 
				
			||||||
+#include <linux/bitops.h>
 | 
					 | 
				
			||||||
+#include <linux/wordpart.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /*
 | 
					 | 
				
			||||||
  * This is largely generic for little-endian machines, but the
 | 
					 | 
				
			||||||
--- a/arch/x86/kvm/mmu/mmu.c
 | 
					 | 
				
			||||||
+++ b/arch/x86/kvm/mmu/mmu.c
 | 
					 | 
				
			||||||
@@ -44,6 +44,7 @@
 | 
					 | 
				
			||||||
 #include <linux/kern_levels.h>
 | 
					 | 
				
			||||||
 #include <linux/kstrtox.h>
 | 
					 | 
				
			||||||
 #include <linux/kthread.h>
 | 
					 | 
				
			||||||
+#include <linux/wordpart.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #include <asm/page.h>
 | 
					 | 
				
			||||||
 #include <asm/memtype.h>
 | 
					 | 
				
			||||||
--- a/fs/namei.c
 | 
					 | 
				
			||||||
+++ b/fs/namei.c
 | 
					 | 
				
			||||||
@@ -17,8 +17,8 @@
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #include <linux/init.h>
 | 
					 | 
				
			||||||
 #include <linux/export.h>
 | 
					 | 
				
			||||||
-#include <linux/kernel.h>
 | 
					 | 
				
			||||||
 #include <linux/slab.h>
 | 
					 | 
				
			||||||
+#include <linux/wordpart.h>
 | 
					 | 
				
			||||||
 #include <linux/fs.h>
 | 
					 | 
				
			||||||
 #include <linux/namei.h>
 | 
					 | 
				
			||||||
 #include <linux/pagemap.h>
 | 
					 | 
				
			||||||
--- a/include/asm-generic/word-at-a-time.h
 | 
					 | 
				
			||||||
+++ b/include/asm-generic/word-at-a-time.h
 | 
					 | 
				
			||||||
@@ -2,7 +2,8 @@
 | 
					 | 
				
			||||||
 #ifndef _ASM_WORD_AT_A_TIME_H
 | 
					 | 
				
			||||||
 #define _ASM_WORD_AT_A_TIME_H
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-#include <linux/kernel.h>
 | 
					 | 
				
			||||||
+#include <linux/bitops.h>
 | 
					 | 
				
			||||||
+#include <linux/wordpart.h>
 | 
					 | 
				
			||||||
 #include <asm/byteorder.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #ifdef __BIG_ENDIAN
 | 
					 | 
				
			||||||
--- a/include/linux/kernel.h
 | 
					 | 
				
			||||||
+++ b/include/linux/kernel.h
 | 
					 | 
				
			||||||
@@ -36,14 +36,6 @@
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #define STACK_MAGIC	0xdeadbeef
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-/**
 | 
					 | 
				
			||||||
- * REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value
 | 
					 | 
				
			||||||
- * @x: value to repeat
 | 
					 | 
				
			||||||
- *
 | 
					 | 
				
			||||||
- * NOTE: @x is not checked for > 0xff; larger values produce odd results.
 | 
					 | 
				
			||||||
- */
 | 
					 | 
				
			||||||
-#define REPEAT_BYTE(x)	((~0ul / 0xff) * (x))
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 /* generic data direction definitions */
 | 
					 | 
				
			||||||
 #define READ			0
 | 
					 | 
				
			||||||
 #define WRITE			1
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/include/linux/wordpart.h
 | 
					 | 
				
			||||||
@@ -0,0 +1,13 @@
 | 
					 | 
				
			||||||
+/* SPDX-License-Identifier: GPL-2.0 */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#ifndef _LINUX_WORDPART_H
 | 
					 | 
				
			||||||
+#define _LINUX_WORDPART_H
 | 
					 | 
				
			||||||
+/**
 | 
					 | 
				
			||||||
+ * REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value
 | 
					 | 
				
			||||||
+ * @x: value to repeat
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * NOTE: @x is not checked for > 0xff; larger values produce odd results.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+#define REPEAT_BYTE(x)	((~0ul / 0xff) * (x))
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#endif // _LINUX_WORDPART_H
 | 
					 | 
				
			||||||
@ -1,107 +0,0 @@
 | 
				
			|||||||
From adeb04362d74188c1e22ccb824b15a0a7b3de2f4 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
 | 
					 | 
				
			||||||
Date: Wed, 14 Feb 2024 19:26:32 +0200
 | 
					 | 
				
			||||||
Subject: [PATCH] kernel.h: Move upper_*_bits() and lower_*_bits() to
 | 
					 | 
				
			||||||
 wordpart.h
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The wordpart.h header is collecting APIs related to the handling
 | 
					 | 
				
			||||||
parts of the word (usually in byte granularity). The upper_*_bits()
 | 
					 | 
				
			||||||
and lower_*_bits() are good candidates to be moved to there.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This helps to clean up header dependency hell with regard to kernel.h
 | 
					 | 
				
			||||||
as the latter gathers completely unrelated stuff together and slows
 | 
					 | 
				
			||||||
down compilation (especially when it's included into other header).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
 | 
					 | 
				
			||||||
Link: https://lore.kernel.org/r/20240214172752.3605073-1-andriy.shevchenko@linux.intel.com
 | 
					 | 
				
			||||||
Reviewed-by: Randy Dunlap <rdunlap@infradead.org>
 | 
					 | 
				
			||||||
Signed-off-by: Kees Cook <keescook@chromium.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 include/linux/kernel.h   | 30 ++----------------------------
 | 
					 | 
				
			||||||
 include/linux/wordpart.h | 29 +++++++++++++++++++++++++++++
 | 
					 | 
				
			||||||
 2 files changed, 31 insertions(+), 28 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/include/linux/kernel.h
 | 
					 | 
				
			||||||
+++ b/include/linux/kernel.h
 | 
					 | 
				
			||||||
@@ -30,6 +30,8 @@
 | 
					 | 
				
			||||||
 #include <linux/build_bug.h>
 | 
					 | 
				
			||||||
 #include <linux/static_call_types.h>
 | 
					 | 
				
			||||||
 #include <linux/instruction_pointer.h>
 | 
					 | 
				
			||||||
+#include <linux/wordpart.h>
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 #include <asm/byteorder.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #include <uapi/linux/kernel.h>
 | 
					 | 
				
			||||||
@@ -55,34 +57,6 @@
 | 
					 | 
				
			||||||
 }					\
 | 
					 | 
				
			||||||
 )
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-/**
 | 
					 | 
				
			||||||
- * upper_32_bits - return bits 32-63 of a number
 | 
					 | 
				
			||||||
- * @n: the number we're accessing
 | 
					 | 
				
			||||||
- *
 | 
					 | 
				
			||||||
- * A basic shift-right of a 64- or 32-bit quantity.  Use this to suppress
 | 
					 | 
				
			||||||
- * the "right shift count >= width of type" warning when that quantity is
 | 
					 | 
				
			||||||
- * 32-bits.
 | 
					 | 
				
			||||||
- */
 | 
					 | 
				
			||||||
-#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16))
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-/**
 | 
					 | 
				
			||||||
- * lower_32_bits - return bits 0-31 of a number
 | 
					 | 
				
			||||||
- * @n: the number we're accessing
 | 
					 | 
				
			||||||
- */
 | 
					 | 
				
			||||||
-#define lower_32_bits(n) ((u32)((n) & 0xffffffff))
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-/**
 | 
					 | 
				
			||||||
- * upper_16_bits - return bits 16-31 of a number
 | 
					 | 
				
			||||||
- * @n: the number we're accessing
 | 
					 | 
				
			||||||
- */
 | 
					 | 
				
			||||||
-#define upper_16_bits(n) ((u16)((n) >> 16))
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-/**
 | 
					 | 
				
			||||||
- * lower_16_bits - return bits 0-15 of a number
 | 
					 | 
				
			||||||
- * @n: the number we're accessing
 | 
					 | 
				
			||||||
- */
 | 
					 | 
				
			||||||
-#define lower_16_bits(n) ((u16)((n) & 0xffff))
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 struct completion;
 | 
					 | 
				
			||||||
 struct user;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
--- a/include/linux/wordpart.h
 | 
					 | 
				
			||||||
+++ b/include/linux/wordpart.h
 | 
					 | 
				
			||||||
@@ -2,6 +2,35 @@
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #ifndef _LINUX_WORDPART_H
 | 
					 | 
				
			||||||
 #define _LINUX_WORDPART_H
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/**
 | 
					 | 
				
			||||||
+ * upper_32_bits - return bits 32-63 of a number
 | 
					 | 
				
			||||||
+ * @n: the number we're accessing
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * A basic shift-right of a 64- or 32-bit quantity.  Use this to suppress
 | 
					 | 
				
			||||||
+ * the "right shift count >= width of type" warning when that quantity is
 | 
					 | 
				
			||||||
+ * 32-bits.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16))
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/**
 | 
					 | 
				
			||||||
+ * lower_32_bits - return bits 0-31 of a number
 | 
					 | 
				
			||||||
+ * @n: the number we're accessing
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+#define lower_32_bits(n) ((u32)((n) & 0xffffffff))
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/**
 | 
					 | 
				
			||||||
+ * upper_16_bits - return bits 16-31 of a number
 | 
					 | 
				
			||||||
+ * @n: the number we're accessing
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+#define upper_16_bits(n) ((u16)((n) >> 16))
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/**
 | 
					 | 
				
			||||||
+ * lower_16_bits - return bits 0-15 of a number
 | 
					 | 
				
			||||||
+ * @n: the number we're accessing
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+#define lower_16_bits(n) ((u16)((n) & 0xffff))
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 /**
 | 
					 | 
				
			||||||
  * REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value
 | 
					 | 
				
			||||||
  * @x: value to repeat
 | 
					 | 
				
			||||||
@ -1,36 +0,0 @@
 | 
				
			|||||||
From 8e7daa85641c9559c113f6b217bdc923397de77c Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: William Zhang <william.zhang@broadcom.com>
 | 
					 | 
				
			||||||
Date: Thu, 22 Feb 2024 19:47:58 -0800
 | 
					 | 
				
			||||||
Subject: [PATCH] mtd: rawnand: brcmnand: Support write protection setting from
 | 
					 | 
				
			||||||
 dts
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The write protection feature is controlled by the module parameter wp_on
 | 
					 | 
				
			||||||
with default set to enabled. But not all the board use this feature
 | 
					 | 
				
			||||||
especially in BCMBCA broadband board. And module parameter is not
 | 
					 | 
				
			||||||
sufficient as different board can have different option.  Add a device
 | 
					 | 
				
			||||||
tree property and allow this feature to be configured through the board
 | 
					 | 
				
			||||||
dts on per board basis.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: William Zhang <william.zhang@broadcom.com>
 | 
					 | 
				
			||||||
Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
 | 
					 | 
				
			||||||
Reviewed-by: Kamal Dasu <kamal.dasu@broadcom.com>
 | 
					 | 
				
			||||||
Reviewed-by: David Regan <dregan@broadcom.com>
 | 
					 | 
				
			||||||
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
 | 
					 | 
				
			||||||
Link: https://lore.kernel.org/linux-mtd/20240223034758.13753-14-william.zhang@broadcom.com
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/mtd/nand/raw/brcmnand/brcmnand.c | 4 ++++
 | 
					 | 
				
			||||||
 1 file changed, 4 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c
 | 
					 | 
				
			||||||
+++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
 | 
					 | 
				
			||||||
@@ -3188,6 +3188,10 @@ int brcmnand_probe(struct platform_devic
 | 
					 | 
				
			||||||
 	/* Disable XOR addressing */
 | 
					 | 
				
			||||||
 	brcmnand_rmw_reg(ctrl, BRCMNAND_CS_XOR, 0xff, 0, 0);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	/* Check if the board connects the WP pin */
 | 
					 | 
				
			||||||
+	if (of_property_read_bool(dn, "brcm,wp-not-connected"))
 | 
					 | 
				
			||||||
+		wp_on = 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	if (ctrl->features & BRCMNAND_HAS_WP) {
 | 
					 | 
				
			||||||
 		/* Permanently disable write protection */
 | 
					 | 
				
			||||||
 		if (wp_on == 2)
 | 
					 | 
				
			||||||
@ -1,65 +0,0 @@
 | 
				
			|||||||
From 63db0cb35e1cb3b3c134906d1062f65513fdda2d Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
 | 
					 | 
				
			||||||
Date: Tue, 4 Oct 2022 10:37:09 +0200
 | 
					 | 
				
			||||||
Subject: [PATCH] mtd: core: simplify (a bit) code find partition-matching
 | 
					 | 
				
			||||||
 dynamic OF node
 | 
					 | 
				
			||||||
MIME-Version: 1.0
 | 
					 | 
				
			||||||
Content-Type: text/plain; charset=UTF-8
 | 
					 | 
				
			||||||
Content-Transfer-Encoding: 8bit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
1. Don't hardcode "partition-" string twice
 | 
					 | 
				
			||||||
2. Use simpler logic & use ->name to avoid of_property_read_string()
 | 
					 | 
				
			||||||
3. Use mtd_get_of_node() helper
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Cc: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
 | 
					 | 
				
			||||||
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
 | 
					 | 
				
			||||||
Link: https://lore.kernel.org/linux-mtd/20221004083710.27704-1-zajec5@gmail.com
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/mtd/mtdcore.c | 16 +++++++---------
 | 
					 | 
				
			||||||
 1 file changed, 7 insertions(+), 9 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/mtd/mtdcore.c
 | 
					 | 
				
			||||||
+++ b/drivers/mtd/mtdcore.c
 | 
					 | 
				
			||||||
@@ -551,18 +551,16 @@ static void mtd_check_of_node(struct mtd
 | 
					 | 
				
			||||||
 	struct device_node *partitions, *parent_dn, *mtd_dn = NULL;
 | 
					 | 
				
			||||||
 	const char *pname, *prefix = "partition-";
 | 
					 | 
				
			||||||
 	int plen, mtd_name_len, offset, prefix_len;
 | 
					 | 
				
			||||||
-	struct mtd_info *parent;
 | 
					 | 
				
			||||||
 	bool found = false;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* Check if MTD already has a device node */
 | 
					 | 
				
			||||||
-	if (dev_of_node(&mtd->dev))
 | 
					 | 
				
			||||||
+	if (mtd_get_of_node(mtd))
 | 
					 | 
				
			||||||
 		return;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* Check if a partitions node exist */
 | 
					 | 
				
			||||||
 	if (!mtd_is_partition(mtd))
 | 
					 | 
				
			||||||
 		return;
 | 
					 | 
				
			||||||
-	parent = mtd->parent;
 | 
					 | 
				
			||||||
-	parent_dn = of_node_get(dev_of_node(&parent->dev));
 | 
					 | 
				
			||||||
+	parent_dn = of_node_get(mtd_get_of_node(mtd->parent));
 | 
					 | 
				
			||||||
 	if (!parent_dn)
 | 
					 | 
				
			||||||
 		return;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -575,15 +573,15 @@ static void mtd_check_of_node(struct mtd
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* Search if a partition is defined with the same name */
 | 
					 | 
				
			||||||
 	for_each_child_of_node(partitions, mtd_dn) {
 | 
					 | 
				
			||||||
-		offset = 0;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 		/* Skip partition with no/wrong prefix */
 | 
					 | 
				
			||||||
-		if (!of_node_name_prefix(mtd_dn, "partition-"))
 | 
					 | 
				
			||||||
+		if (!of_node_name_prefix(mtd_dn, prefix))
 | 
					 | 
				
			||||||
 			continue;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 		/* Label have priority. Check that first */
 | 
					 | 
				
			||||||
-		if (of_property_read_string(mtd_dn, "label", &pname)) {
 | 
					 | 
				
			||||||
-			of_property_read_string(mtd_dn, "name", &pname);
 | 
					 | 
				
			||||||
+		if (!of_property_read_string(mtd_dn, "label", &pname)) {
 | 
					 | 
				
			||||||
+			offset = 0;
 | 
					 | 
				
			||||||
+		} else {
 | 
					 | 
				
			||||||
+			pname = mtd_dn->name;
 | 
					 | 
				
			||||||
 			offset = prefix_len;
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@ -1,84 +0,0 @@
 | 
				
			|||||||
From ddb8cefb7af288950447ca6eeeafb09977dab56f Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
 | 
					 | 
				
			||||||
Date: Tue, 4 Oct 2022 10:37:10 +0200
 | 
					 | 
				
			||||||
Subject: [PATCH] mtd: core: try to find OF node for every MTD partition
 | 
					 | 
				
			||||||
MIME-Version: 1.0
 | 
					 | 
				
			||||||
Content-Type: text/plain; charset=UTF-8
 | 
					 | 
				
			||||||
Content-Transfer-Encoding: 8bit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
So far this feature was limited to the top-level "nvmem-cells" node.
 | 
					 | 
				
			||||||
There are multiple parsers creating partitions and subpartitions
 | 
					 | 
				
			||||||
dynamically. Extend that code to handle them too.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This allows finding partition-* node for every MTD (sub)partition.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Random example:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
partitions {
 | 
					 | 
				
			||||||
	compatible = "brcm,bcm947xx-cfe-partitions";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	partition-firmware {
 | 
					 | 
				
			||||||
		compatible = "brcm,trx";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		partition-loader {
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Cc: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
 | 
					 | 
				
			||||||
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
 | 
					 | 
				
			||||||
Link: https://lore.kernel.org/linux-mtd/20221004083710.27704-2-zajec5@gmail.com
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/mtd/mtdcore.c | 18 ++++++------------
 | 
					 | 
				
			||||||
 1 file changed, 6 insertions(+), 12 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/mtd/mtdcore.c
 | 
					 | 
				
			||||||
+++ b/drivers/mtd/mtdcore.c
 | 
					 | 
				
			||||||
@@ -551,20 +551,22 @@ static void mtd_check_of_node(struct mtd
 | 
					 | 
				
			||||||
 	struct device_node *partitions, *parent_dn, *mtd_dn = NULL;
 | 
					 | 
				
			||||||
 	const char *pname, *prefix = "partition-";
 | 
					 | 
				
			||||||
 	int plen, mtd_name_len, offset, prefix_len;
 | 
					 | 
				
			||||||
-	bool found = false;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* Check if MTD already has a device node */
 | 
					 | 
				
			||||||
 	if (mtd_get_of_node(mtd))
 | 
					 | 
				
			||||||
 		return;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	/* Check if a partitions node exist */
 | 
					 | 
				
			||||||
 	if (!mtd_is_partition(mtd))
 | 
					 | 
				
			||||||
 		return;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	parent_dn = of_node_get(mtd_get_of_node(mtd->parent));
 | 
					 | 
				
			||||||
 	if (!parent_dn)
 | 
					 | 
				
			||||||
 		return;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	partitions = of_get_child_by_name(parent_dn, "partitions");
 | 
					 | 
				
			||||||
+	if (mtd_is_partition(mtd->parent))
 | 
					 | 
				
			||||||
+		partitions = of_node_get(parent_dn);
 | 
					 | 
				
			||||||
+	else
 | 
					 | 
				
			||||||
+		partitions = of_get_child_by_name(parent_dn, "partitions");
 | 
					 | 
				
			||||||
 	if (!partitions)
 | 
					 | 
				
			||||||
 		goto exit_parent;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -588,19 +590,11 @@ static void mtd_check_of_node(struct mtd
 | 
					 | 
				
			||||||
 		plen = strlen(pname) - offset;
 | 
					 | 
				
			||||||
 		if (plen == mtd_name_len &&
 | 
					 | 
				
			||||||
 		    !strncmp(mtd->name, pname + offset, plen)) {
 | 
					 | 
				
			||||||
-			found = true;
 | 
					 | 
				
			||||||
+			mtd_set_of_node(mtd, mtd_dn);
 | 
					 | 
				
			||||||
 			break;
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if (!found)
 | 
					 | 
				
			||||||
-		goto exit_partitions;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	/* Set of_node only for nvmem */
 | 
					 | 
				
			||||||
-	if (of_device_is_compatible(mtd_dn, "nvmem-cells"))
 | 
					 | 
				
			||||||
-		mtd_set_of_node(mtd, mtd_dn);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-exit_partitions:
 | 
					 | 
				
			||||||
 	of_node_put(partitions);
 | 
					 | 
				
			||||||
 exit_parent:
 | 
					 | 
				
			||||||
 	of_node_put(parent_dn);
 | 
					 | 
				
			||||||
@ -1,47 +0,0 @@
 | 
				
			|||||||
From 26422ac78e9d8767bd4aabfbae616b15edbf6a1b Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
 | 
					 | 
				
			||||||
Date: Sat, 22 Oct 2022 23:13:18 +0200
 | 
					 | 
				
			||||||
Subject: [PATCH] mtd: core: set ROOT_DEV for partitions marked as rootfs in DT
 | 
					 | 
				
			||||||
MIME-Version: 1.0
 | 
					 | 
				
			||||||
Content-Type: text/plain; charset=UTF-8
 | 
					 | 
				
			||||||
Content-Transfer-Encoding: 8bit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This adds support for "linux,rootfs" binding that is used to mark flash
 | 
					 | 
				
			||||||
partition containing rootfs. It's useful for devices using device tree
 | 
					 | 
				
			||||||
that don't have bootloader passing root info in cmdline.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
 | 
					 | 
				
			||||||
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
 | 
					 | 
				
			||||||
Link: https://lore.kernel.org/linux-mtd/20221022211318.32009-2-zajec5@gmail.com
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/mtd/mtdcore.c | 12 ++++++++++++
 | 
					 | 
				
			||||||
 1 file changed, 12 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/mtd/mtdcore.c
 | 
					 | 
				
			||||||
+++ b/drivers/mtd/mtdcore.c
 | 
					 | 
				
			||||||
@@ -28,6 +28,7 @@
 | 
					 | 
				
			||||||
 #include <linux/leds.h>
 | 
					 | 
				
			||||||
 #include <linux/debugfs.h>
 | 
					 | 
				
			||||||
 #include <linux/nvmem-provider.h>
 | 
					 | 
				
			||||||
+#include <linux/root_dev.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #include <linux/mtd/mtd.h>
 | 
					 | 
				
			||||||
 #include <linux/mtd/partitions.h>
 | 
					 | 
				
			||||||
@@ -737,6 +738,17 @@ int add_mtd_device(struct mtd_info *mtd)
 | 
					 | 
				
			||||||
 		not->add(mtd);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	mutex_unlock(&mtd_table_mutex);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (of_find_property(mtd_get_of_node(mtd), "linux,rootfs", NULL)) {
 | 
					 | 
				
			||||||
+		if (IS_BUILTIN(CONFIG_MTD)) {
 | 
					 | 
				
			||||||
+			pr_info("mtd: setting mtd%d (%s) as root device\n", mtd->index, mtd->name);
 | 
					 | 
				
			||||||
+			ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, mtd->index);
 | 
					 | 
				
			||||||
+		} else {
 | 
					 | 
				
			||||||
+			pr_warn("mtd: can't set mtd%d (%s) as root device - mtd must be builtin\n",
 | 
					 | 
				
			||||||
+				mtd->index, mtd->name);
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	/* We _know_ we aren't being removed, because
 | 
					 | 
				
			||||||
 	   our caller is still holding us here. So none
 | 
					 | 
				
			||||||
 	   of this try_ nonsense, and no bitching about it
 | 
					 | 
				
			||||||
@ -1,117 +0,0 @@
 | 
				
			|||||||
From 773bbe10449731c9525457873e0c2342e5cf883b Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Michael Walle <michael@walle.cc>
 | 
					 | 
				
			||||||
Date: Thu, 11 Aug 2022 00:06:53 +0200
 | 
					 | 
				
			||||||
Subject: [PATCH] mtd: spi-nor: add generic flash driver
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Our SFDP parsing is everything we need to support all basic operations
 | 
					 | 
				
			||||||
of a flash device. If the flash isn't found in our in-kernel flash
 | 
					 | 
				
			||||||
database, gracefully fall back to a driver described solely by its SFDP
 | 
					 | 
				
			||||||
tables.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Michael Walle <michael@walle.cc>
 | 
					 | 
				
			||||||
Signed-off-by: Tudor Ambarus <tudor.ambarus@microchip.com>
 | 
					 | 
				
			||||||
Tested-by: Tudor Ambarus <tudor.ambarus@microchip.com>
 | 
					 | 
				
			||||||
Reviewed-by: Takahiro Kuwano <Takahiro.Kuwano@infineon.com>
 | 
					 | 
				
			||||||
Link: https://lore.kernel.org/r/20220810220654.1297699-7-michael@walle.cc
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/mtd/spi-nor/core.c | 26 ++++++++++++++++++++++++--
 | 
					 | 
				
			||||||
 drivers/mtd/spi-nor/core.h |  1 +
 | 
					 | 
				
			||||||
 drivers/mtd/spi-nor/sfdp.c | 27 +++++++++++++++++++++++++++
 | 
					 | 
				
			||||||
 3 files changed, 52 insertions(+), 2 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/mtd/spi-nor/core.c
 | 
					 | 
				
			||||||
+++ b/drivers/mtd/spi-nor/core.c
 | 
					 | 
				
			||||||
@@ -1636,6 +1636,16 @@ static const struct spi_nor_manufacturer
 | 
					 | 
				
			||||||
 	&spi_nor_xmc,
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static const struct flash_info spi_nor_generic_flash = {
 | 
					 | 
				
			||||||
+	.name = "spi-nor-generic",
 | 
					 | 
				
			||||||
+	/*
 | 
					 | 
				
			||||||
+	 * JESD216 rev A doesn't specify the page size, therefore we need a
 | 
					 | 
				
			||||||
+	 * sane default.
 | 
					 | 
				
			||||||
+	 */
 | 
					 | 
				
			||||||
+	.page_size = 256,
 | 
					 | 
				
			||||||
+	.parse_sfdp = true,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static const struct flash_info *spi_nor_match_id(struct spi_nor *nor,
 | 
					 | 
				
			||||||
 						 const u8 *id)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
@@ -1669,6 +1679,14 @@ static const struct flash_info *spi_nor_
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	info = spi_nor_match_id(nor, id);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* Fallback to a generic flash described only by its SFDP data. */
 | 
					 | 
				
			||||||
+	if (!info) {
 | 
					 | 
				
			||||||
+		ret = spi_nor_check_sfdp_signature(nor);
 | 
					 | 
				
			||||||
+		if (!ret)
 | 
					 | 
				
			||||||
+			info = &spi_nor_generic_flash;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	if (!info) {
 | 
					 | 
				
			||||||
 		dev_err(nor->dev, "unrecognized JEDEC id bytes: %*ph\n",
 | 
					 | 
				
			||||||
 			SPI_NOR_MAX_ID_LEN, id);
 | 
					 | 
				
			||||||
@@ -2105,8 +2123,12 @@ static int spi_nor_select_pp(struct spi_
 | 
					 | 
				
			||||||
  * spi_nor_select_uniform_erase() - select optimum uniform erase type
 | 
					 | 
				
			||||||
  * @map:		the erase map of the SPI NOR
 | 
					 | 
				
			||||||
  * @wanted_size:	the erase type size to search for. Contains the value of
 | 
					 | 
				
			||||||
- *			info->sector_size or of the "small sector" size in case
 | 
					 | 
				
			||||||
- *			CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is defined.
 | 
					 | 
				
			||||||
+ *			info->sector_size, the "small sector" size in case
 | 
					 | 
				
			||||||
+ *			CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is defined or 0 if
 | 
					 | 
				
			||||||
+ *			there is no information about the sector size. The
 | 
					 | 
				
			||||||
+ *			latter is the case if the flash parameters are parsed
 | 
					 | 
				
			||||||
+ *			solely by SFDP, then the largest supported erase type
 | 
					 | 
				
			||||||
+ *			is selected.
 | 
					 | 
				
			||||||
  *
 | 
					 | 
				
			||||||
  * Once the optimum uniform sector erase command is found, disable all the
 | 
					 | 
				
			||||||
  * other.
 | 
					 | 
				
			||||||
--- a/drivers/mtd/spi-nor/core.h
 | 
					 | 
				
			||||||
+++ b/drivers/mtd/spi-nor/core.h
 | 
					 | 
				
			||||||
@@ -708,6 +708,8 @@ int spi_nor_controller_ops_read_reg(stru
 | 
					 | 
				
			||||||
 int spi_nor_controller_ops_write_reg(struct spi_nor *nor, u8 opcode,
 | 
					 | 
				
			||||||
 				     const u8 *buf, size_t len);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+int spi_nor_check_sfdp_signature(struct spi_nor *nor);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	return container_of(mtd, struct spi_nor, mtd);
 | 
					 | 
				
			||||||
--- a/drivers/mtd/spi-nor/sfdp.c
 | 
					 | 
				
			||||||
+++ b/drivers/mtd/spi-nor/sfdp.c
 | 
					 | 
				
			||||||
@@ -1250,6 +1250,33 @@ static void spi_nor_post_sfdp_fixups(str
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /**
 | 
					 | 
				
			||||||
+ * spi_nor_check_sfdp_signature() - check for a valid SFDP signature
 | 
					 | 
				
			||||||
+ * @nor:	pointer to a 'struct spi_nor'
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Used to detect if the flash supports the RDSFDP command as well as the
 | 
					 | 
				
			||||||
+ * presence of a valid SFDP table.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Return: 0 on success, -errno otherwise.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+int spi_nor_check_sfdp_signature(struct spi_nor *nor)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	u32 signature;
 | 
					 | 
				
			||||||
+	int err;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* Get the SFDP header. */
 | 
					 | 
				
			||||||
+	err = spi_nor_read_sfdp_dma_unsafe(nor, 0, sizeof(signature),
 | 
					 | 
				
			||||||
+					   &signature);
 | 
					 | 
				
			||||||
+	if (err < 0)
 | 
					 | 
				
			||||||
+		return err;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* Check the SFDP signature. */
 | 
					 | 
				
			||||||
+	if (le32_to_cpu(signature) != SFDP_SIGNATURE)
 | 
					 | 
				
			||||||
+		return -EINVAL;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/**
 | 
					 | 
				
			||||||
  * spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters.
 | 
					 | 
				
			||||||
  * @nor:		pointer to a 'struct spi_nor'
 | 
					 | 
				
			||||||
  *
 | 
					 | 
				
			||||||
@ -1,32 +0,0 @@
 | 
				
			|||||||
From 6c9d1fd52956c3148e847a214bae9102b1811de5 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Vincent Tremblay <vincent@vtremblay.dev>
 | 
					 | 
				
			||||||
Date: Tue, 27 Dec 2022 09:10:08 -0500
 | 
					 | 
				
			||||||
Subject: [PATCH] spidev: Add Silicon Labs SI3210 device compatible
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Add compatible string for Silicon Labs SI3210 device.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Vincent Tremblay <vincent@vtremblay.dev>
 | 
					 | 
				
			||||||
Link: https://lore.kernel.org/r/20221227141011.111410-2-vincent@vtremblay.dev
 | 
					 | 
				
			||||||
Signed-off-by: Mark Brown <broonie@kernel.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/spi/spidev.c | 2 ++
 | 
					 | 
				
			||||||
 1 file changed, 2 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/spi/spidev.c
 | 
					 | 
				
			||||||
+++ b/drivers/spi/spidev.c
 | 
					 | 
				
			||||||
@@ -702,6 +702,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);
 | 
					 | 
				
			||||||
@@ -730,6 +731,7 @@ static const struct of_device_id spidev_
 | 
					 | 
				
			||||||
 	{ .compatible = "rohm,dh2228fv", .data = &spidev_of_check },
 | 
					 | 
				
			||||||
 	{ .compatible = "semtech,sx1301", .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);
 | 
					 | 
				
			||||||
@ -1,229 +0,0 @@
 | 
				
			|||||||
From aec4d5f5ffd0f0092bd9dc21ea90e0bc237d4b74 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
 | 
					 | 
				
			||||||
Date: Sat, 15 Oct 2022 11:29:50 +0200
 | 
					 | 
				
			||||||
Subject: [PATCH] mtd: parsers: add TP-Link SafeLoader partitions table parser
 | 
					 | 
				
			||||||
MIME-Version: 1.0
 | 
					 | 
				
			||||||
Content-Type: text/plain; charset=UTF-8
 | 
					 | 
				
			||||||
Content-Transfer-Encoding: 8bit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This parser deals with most TP-Link home routers. It reads info about
 | 
					 | 
				
			||||||
partitions and registers them in the MTD subsystem.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Example from TP-Link Archer C5 V2:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
spi-nor spi0.0: s25fl128s1 (16384 Kbytes)
 | 
					 | 
				
			||||||
15 tplink-safeloader partitions found on MTD device spi0.0
 | 
					 | 
				
			||||||
Creating 15 MTD partitions on "spi0.0":
 | 
					 | 
				
			||||||
0x000000000000-0x000000040000 : "fs-uboot"
 | 
					 | 
				
			||||||
0x000000040000-0x000000440000 : "os-image"
 | 
					 | 
				
			||||||
0x000000440000-0x000000e40000 : "rootfs"
 | 
					 | 
				
			||||||
0x000000e40000-0x000000e40200 : "default-mac"
 | 
					 | 
				
			||||||
0x000000e40200-0x000000e40400 : "pin"
 | 
					 | 
				
			||||||
0x000000e40400-0x000000e40600 : "product-info"
 | 
					 | 
				
			||||||
0x000000e50000-0x000000e60000 : "partition-table"
 | 
					 | 
				
			||||||
0x000000e60000-0x000000e60200 : "soft-version"
 | 
					 | 
				
			||||||
0x000000e61000-0x000000e70000 : "support-list"
 | 
					 | 
				
			||||||
0x000000e70000-0x000000e80000 : "profile"
 | 
					 | 
				
			||||||
0x000000e80000-0x000000e90000 : "default-config"
 | 
					 | 
				
			||||||
0x000000e90000-0x000000ee0000 : "user-config"
 | 
					 | 
				
			||||||
0x000000ee0000-0x000000fe0000 : "log"
 | 
					 | 
				
			||||||
0x000000fe0000-0x000000ff0000 : "radio_bk"
 | 
					 | 
				
			||||||
0x000000ff0000-0x000001000000 : "radio"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
 | 
					 | 
				
			||||||
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
 | 
					 | 
				
			||||||
Link: https://lore.kernel.org/linux-mtd/20221015092950.27467-2-zajec5@gmail.com
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/mtd/parsers/Kconfig             |  15 +++
 | 
					 | 
				
			||||||
 drivers/mtd/parsers/Makefile            |   1 +
 | 
					 | 
				
			||||||
 drivers/mtd/parsers/tplink_safeloader.c | 150 ++++++++++++++++++++++++
 | 
					 | 
				
			||||||
 3 files changed, 166 insertions(+)
 | 
					 | 
				
			||||||
 create mode 100644 drivers/mtd/parsers/tplink_safeloader.c
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/mtd/parsers/Kconfig
 | 
					 | 
				
			||||||
+++ b/drivers/mtd/parsers/Kconfig
 | 
					 | 
				
			||||||
@@ -123,6 +123,21 @@ config MTD_AFS_PARTS
 | 
					 | 
				
			||||||
 	  for your particular device. It won't happen automatically. The
 | 
					 | 
				
			||||||
 	  'physmap' map driver (CONFIG_MTD_PHYSMAP) does this, for example.
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+config MTD_PARSER_TPLINK_SAFELOADER
 | 
					 | 
				
			||||||
+	tristate "TP-Link Safeloader partitions parser"
 | 
					 | 
				
			||||||
+	depends on MTD && (ARCH_BCM_5301X || ATH79 || SOC_MT7620 || SOC_MT7621 || COMPILE_TEST)
 | 
					 | 
				
			||||||
+	help
 | 
					 | 
				
			||||||
+	  TP-Link home routers use flash partitions to store various data. Info
 | 
					 | 
				
			||||||
+	  about flash space layout is stored in a partitions table using a
 | 
					 | 
				
			||||||
+	  custom ASCII-based format.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	  That format was first found in devices with SafeLoader bootloader and
 | 
					 | 
				
			||||||
+	  was named after it. Later it was adapted to CFE and U-Boot
 | 
					 | 
				
			||||||
+	  bootloaders.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	  This driver reads partitions table, parses it and creates MTD
 | 
					 | 
				
			||||||
+	  partitions.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 config MTD_PARSER_TRX
 | 
					 | 
				
			||||||
 	tristate "Parser for TRX format partitions"
 | 
					 | 
				
			||||||
 	depends on MTD && (BCM47XX || ARCH_BCM_5301X || ARCH_MEDIATEK || RALINK || COMPILE_TEST)
 | 
					 | 
				
			||||||
--- a/drivers/mtd/parsers/Makefile
 | 
					 | 
				
			||||||
+++ b/drivers/mtd/parsers/Makefile
 | 
					 | 
				
			||||||
@@ -10,6 +10,7 @@ ofpart-$(CONFIG_MTD_OF_PARTS_BCM4908)	+=
 | 
					 | 
				
			||||||
 ofpart-$(CONFIG_MTD_OF_PARTS_LINKSYS_NS)+= ofpart_linksys_ns.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_MTD_PARSER_IMAGETAG)	+= parser_imagetag.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_MTD_AFS_PARTS)		+= afs.o
 | 
					 | 
				
			||||||
+obj-$(CONFIG_MTD_PARSER_TPLINK_SAFELOADER)	+= tplink_safeloader.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_MTD_PARSER_TRX)		+= parser_trx.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_MTD_SERCOMM_PARTS)		+= scpart.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_MTD_SHARPSL_PARTS)		+= sharpslpart.o
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/drivers/mtd/parsers/tplink_safeloader.c
 | 
					 | 
				
			||||||
@@ -0,0 +1,150 @@
 | 
					 | 
				
			||||||
+// SPDX-License-Identifier: GPL-2.0-only
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ * Copyright © 2022 Rafał Miłecki <rafal@milecki.pl>
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#include <linux/kernel.h>
 | 
					 | 
				
			||||||
+#include <linux/module.h>
 | 
					 | 
				
			||||||
+#include <linux/mtd/mtd.h>
 | 
					 | 
				
			||||||
+#include <linux/mtd/partitions.h>
 | 
					 | 
				
			||||||
+#include <linux/of.h>
 | 
					 | 
				
			||||||
+#include <linux/slab.h>
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define TPLINK_SAFELOADER_DATA_OFFSET		4
 | 
					 | 
				
			||||||
+#define TPLINK_SAFELOADER_MAX_PARTS		32
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+struct safeloader_cmn_header {
 | 
					 | 
				
			||||||
+	__be32 size;
 | 
					 | 
				
			||||||
+	uint32_t unused;
 | 
					 | 
				
			||||||
+} __packed;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void *mtd_parser_tplink_safeloader_read_table(struct mtd_info *mtd)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct safeloader_cmn_header hdr;
 | 
					 | 
				
			||||||
+	struct device_node *np;
 | 
					 | 
				
			||||||
+	size_t bytes_read;
 | 
					 | 
				
			||||||
+	size_t offset;
 | 
					 | 
				
			||||||
+	size_t size;
 | 
					 | 
				
			||||||
+	char *buf;
 | 
					 | 
				
			||||||
+	int err;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	np = mtd_get_of_node(mtd);
 | 
					 | 
				
			||||||
+	if (mtd_is_partition(mtd))
 | 
					 | 
				
			||||||
+		of_node_get(np);
 | 
					 | 
				
			||||||
+	else
 | 
					 | 
				
			||||||
+		np = of_get_child_by_name(np, "partitions");
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (of_property_read_u32(np, "partitions-table-offset", (u32 *)&offset)) {
 | 
					 | 
				
			||||||
+		pr_err("Failed to get partitions table offset\n");
 | 
					 | 
				
			||||||
+		goto err_put;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	err = mtd_read(mtd, offset, sizeof(hdr), &bytes_read, (uint8_t *)&hdr);
 | 
					 | 
				
			||||||
+	if (err && !mtd_is_bitflip(err)) {
 | 
					 | 
				
			||||||
+		pr_err("Failed to read from %s at 0x%zx\n", mtd->name, offset);
 | 
					 | 
				
			||||||
+		goto err_put;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	size = be32_to_cpu(hdr.size);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	buf = kmalloc(size + 1, GFP_KERNEL);
 | 
					 | 
				
			||||||
+	if (!buf)
 | 
					 | 
				
			||||||
+		goto err_put;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	err = mtd_read(mtd, offset + sizeof(hdr), size, &bytes_read, buf);
 | 
					 | 
				
			||||||
+	if (err && !mtd_is_bitflip(err)) {
 | 
					 | 
				
			||||||
+		pr_err("Failed to read from %s at 0x%zx\n", mtd->name, offset + sizeof(hdr));
 | 
					 | 
				
			||||||
+		goto err_kfree;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	buf[size] = '\0';
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	of_node_put(np);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return buf;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+err_kfree:
 | 
					 | 
				
			||||||
+	kfree(buf);
 | 
					 | 
				
			||||||
+err_put:
 | 
					 | 
				
			||||||
+	of_node_put(np);
 | 
					 | 
				
			||||||
+	return NULL;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int mtd_parser_tplink_safeloader_parse(struct mtd_info *mtd,
 | 
					 | 
				
			||||||
+					      const struct mtd_partition **pparts,
 | 
					 | 
				
			||||||
+					      struct mtd_part_parser_data *data)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct mtd_partition *parts;
 | 
					 | 
				
			||||||
+	char name[65];
 | 
					 | 
				
			||||||
+	size_t offset;
 | 
					 | 
				
			||||||
+	size_t bytes;
 | 
					 | 
				
			||||||
+	char *buf;
 | 
					 | 
				
			||||||
+	int idx;
 | 
					 | 
				
			||||||
+	int err;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	parts = kcalloc(TPLINK_SAFELOADER_MAX_PARTS, sizeof(*parts), GFP_KERNEL);
 | 
					 | 
				
			||||||
+	if (!parts) {
 | 
					 | 
				
			||||||
+		err = -ENOMEM;
 | 
					 | 
				
			||||||
+		goto err_out;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	buf = mtd_parser_tplink_safeloader_read_table(mtd);
 | 
					 | 
				
			||||||
+	if (!buf) {
 | 
					 | 
				
			||||||
+		err = -ENOENT;
 | 
					 | 
				
			||||||
+		goto err_out;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	for (idx = 0, offset = TPLINK_SAFELOADER_DATA_OFFSET;
 | 
					 | 
				
			||||||
+	     idx < TPLINK_SAFELOADER_MAX_PARTS &&
 | 
					 | 
				
			||||||
+	     sscanf(buf + offset, "partition %64s base 0x%llx size 0x%llx%zn\n",
 | 
					 | 
				
			||||||
+		    name, &parts[idx].offset, &parts[idx].size, &bytes) == 3;
 | 
					 | 
				
			||||||
+	     idx++, offset += bytes + 1) {
 | 
					 | 
				
			||||||
+		parts[idx].name = kstrdup(name, GFP_KERNEL);
 | 
					 | 
				
			||||||
+		if (!parts[idx].name) {
 | 
					 | 
				
			||||||
+			err = -ENOMEM;
 | 
					 | 
				
			||||||
+			goto err_free;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (idx == TPLINK_SAFELOADER_MAX_PARTS)
 | 
					 | 
				
			||||||
+		pr_warn("Reached maximum number of partitions!\n");
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	kfree(buf);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	*pparts = parts;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return idx;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+err_free:
 | 
					 | 
				
			||||||
+	for (idx -= 1; idx >= 0; idx--)
 | 
					 | 
				
			||||||
+		kfree(parts[idx].name);
 | 
					 | 
				
			||||||
+err_out:
 | 
					 | 
				
			||||||
+	return err;
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void mtd_parser_tplink_safeloader_cleanup(const struct mtd_partition *pparts,
 | 
					 | 
				
			||||||
+						 int nr_parts)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int i;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	for (i = 0; i < nr_parts; i++)
 | 
					 | 
				
			||||||
+		kfree(pparts[i].name);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	kfree(pparts);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static const struct of_device_id mtd_parser_tplink_safeloader_of_match_table[] = {
 | 
					 | 
				
			||||||
+	{ .compatible = "tplink,safeloader-partitions" },
 | 
					 | 
				
			||||||
+	{},
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+MODULE_DEVICE_TABLE(of, mtd_parser_tplink_safeloader_of_match_table);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct mtd_part_parser mtd_parser_tplink_safeloader = {
 | 
					 | 
				
			||||||
+	.parse_fn = mtd_parser_tplink_safeloader_parse,
 | 
					 | 
				
			||||||
+	.cleanup = mtd_parser_tplink_safeloader_cleanup,
 | 
					 | 
				
			||||||
+	.name = "tplink-safeloader",
 | 
					 | 
				
			||||||
+	.of_match_table = mtd_parser_tplink_safeloader_of_match_table,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+module_mtd_part_parser(mtd_parser_tplink_safeloader);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+MODULE_LICENSE("GPL");
 | 
					 | 
				
			||||||
@ -1,35 +0,0 @@
 | 
				
			|||||||
From ebed787a0becb9354f0a23620a5130cccd6c730c Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Daniel Golle <daniel@makrotopia.org>
 | 
					 | 
				
			||||||
Date: Thu, 19 Jan 2023 03:45:43 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] mtd: spinand: macronix: use scratch buffer for DMA operation
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The mx35lf1ge4ab_get_eccsr() function uses an SPI DMA operation to
 | 
					 | 
				
			||||||
read the eccsr, hence the buffer should not be on stack. Since commit
 | 
					 | 
				
			||||||
380583227c0c7f ("spi: spi-mem: Add extra sanity checks on the op param")
 | 
					 | 
				
			||||||
the kernel emmits a warning and blocks such operations.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Use the scratch buffer to get eccsr instead of trying to directly read
 | 
					 | 
				
			||||||
into a stack-allocated variable.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 | 
					 | 
				
			||||||
Reviewed-by: Dhruva Gole <d-gole@ti.com>
 | 
					 | 
				
			||||||
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
 | 
					 | 
				
			||||||
Link: https://lore.kernel.org/linux-mtd/Y8i85zM0u4XdM46z@makrotopia.org
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/mtd/nand/spi/macronix.c | 3 ++-
 | 
					 | 
				
			||||||
 1 file changed, 2 insertions(+), 1 deletion(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/mtd/nand/spi/macronix.c
 | 
					 | 
				
			||||||
+++ b/drivers/mtd/nand/spi/macronix.c
 | 
					 | 
				
			||||||
@@ -83,9 +83,10 @@ static int mx35lf1ge4ab_ecc_get_status(s
 | 
					 | 
				
			||||||
 		 * in order to avoid forcing the wear-leveling layer to move
 | 
					 | 
				
			||||||
 		 * data around if it's not necessary.
 | 
					 | 
				
			||||||
 		 */
 | 
					 | 
				
			||||||
-		if (mx35lf1ge4ab_get_eccsr(spinand, &eccsr))
 | 
					 | 
				
			||||||
+		if (mx35lf1ge4ab_get_eccsr(spinand, spinand->scratchbuf))
 | 
					 | 
				
			||||||
 			return nanddev_get_ecc_conf(nand)->strength;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+		eccsr = *spinand->scratchbuf;
 | 
					 | 
				
			||||||
 		if (WARN_ON(eccsr > nanddev_get_ecc_conf(nand)->strength ||
 | 
					 | 
				
			||||||
 			    !eccsr))
 | 
					 | 
				
			||||||
 			return nanddev_get_ecc_conf(nand)->strength;
 | 
					 | 
				
			||||||
@ -1,33 +0,0 @@
 | 
				
			|||||||
From 1ecf9e390452e73a362ea7fbde8f3f0db83de856 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Daniel Golle <daniel@makrotopia.org>
 | 
					 | 
				
			||||||
Date: Thu, 22 Dec 2022 19:33:04 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] mtd: ubi: wire-up parent MTD device
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Wire up the device parent pointer of UBI devices to their lower MTD
 | 
					 | 
				
			||||||
device, typically an MTD partition or whole-chip device.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The most noticeable change is that in sysfs, previously ubi devices
 | 
					 | 
				
			||||||
would be could in /sys/devices/virtual/ubi while after this change they
 | 
					 | 
				
			||||||
would be correctly attached to their parent MTD device, e.g.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/sys/devices/platform/1100d000.spi/spi_master/spi1/spi1.0/mtd/mtd2/ubi0.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Locating UBI devices using /sys/class/ubi/ of course still works as
 | 
					 | 
				
			||||||
well.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 | 
					 | 
				
			||||||
Signed-off-by: Richard Weinberger <richard@nod.at>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/mtd/ubi/build.c | 1 +
 | 
					 | 
				
			||||||
 1 file changed, 1 insertion(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/mtd/ubi/build.c
 | 
					 | 
				
			||||||
+++ b/drivers/mtd/ubi/build.c
 | 
					 | 
				
			||||||
@@ -929,6 +929,7 @@ int ubi_attach_mtd_dev(struct mtd_info *
 | 
					 | 
				
			||||||
 	ubi->dev.release = dev_release;
 | 
					 | 
				
			||||||
 	ubi->dev.class = &ubi_class;
 | 
					 | 
				
			||||||
 	ubi->dev.groups = ubi_dev_groups;
 | 
					 | 
				
			||||||
+	ubi->dev.parent = &mtd->dev;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	ubi->mtd = mtd;
 | 
					 | 
				
			||||||
 	ubi->ubi_num = ubi_num;
 | 
					 | 
				
			||||||
@ -1,49 +0,0 @@
 | 
				
			|||||||
From 05b8773ca33253ea562be145cf3145b05ef19f86 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Daniel Golle <daniel@makrotopia.org>
 | 
					 | 
				
			||||||
Date: Thu, 22 Dec 2022 19:33:31 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] mtd: ubi: block: wire-up device parent
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
ubiblock devices were previously only identifyable by their name, but
 | 
					 | 
				
			||||||
not connected to their parent UBI volume device e.g. in sysfs.
 | 
					 | 
				
			||||||
Properly parent ubiblock device as descendant of a UBI volume device
 | 
					 | 
				
			||||||
to reflect device model hierachy.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 | 
					 | 
				
			||||||
Signed-off-by: Richard Weinberger <richard@nod.at>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/mtd/ubi/block.c | 2 +-
 | 
					 | 
				
			||||||
 drivers/mtd/ubi/kapi.c  | 1 +
 | 
					 | 
				
			||||||
 include/linux/mtd/ubi.h | 1 +
 | 
					 | 
				
			||||||
 3 files changed, 3 insertions(+), 1 deletion(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/mtd/ubi/block.c
 | 
					 | 
				
			||||||
+++ b/drivers/mtd/ubi/block.c
 | 
					 | 
				
			||||||
@@ -452,7 +452,7 @@ int ubiblock_create(struct ubi_volume_in
 | 
					 | 
				
			||||||
 	list_add_tail(&dev->list, &ubiblock_devices);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* Must be the last step: anyone can call file ops from now on */
 | 
					 | 
				
			||||||
-	ret = add_disk(dev->gd);
 | 
					 | 
				
			||||||
+	ret = device_add_disk(vi->dev, dev->gd, NULL);
 | 
					 | 
				
			||||||
 	if (ret)
 | 
					 | 
				
			||||||
 		goto out_destroy_wq;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
--- a/drivers/mtd/ubi/kapi.c
 | 
					 | 
				
			||||||
+++ b/drivers/mtd/ubi/kapi.c
 | 
					 | 
				
			||||||
@@ -79,6 +79,7 @@ void ubi_do_get_volume_info(struct ubi_d
 | 
					 | 
				
			||||||
 	vi->name_len = vol->name_len;
 | 
					 | 
				
			||||||
 	vi->name = vol->name;
 | 
					 | 
				
			||||||
 	vi->cdev = vol->cdev.dev;
 | 
					 | 
				
			||||||
+	vi->dev = &vol->dev;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /**
 | 
					 | 
				
			||||||
--- a/include/linux/mtd/ubi.h
 | 
					 | 
				
			||||||
+++ b/include/linux/mtd/ubi.h
 | 
					 | 
				
			||||||
@@ -110,6 +110,7 @@ struct ubi_volume_info {
 | 
					 | 
				
			||||||
 	int name_len;
 | 
					 | 
				
			||||||
 	const char *name;
 | 
					 | 
				
			||||||
 	dev_t cdev;
 | 
					 | 
				
			||||||
+	struct device *dev;
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /**
 | 
					 | 
				
			||||||
@ -1,47 +0,0 @@
 | 
				
			|||||||
From 281f7a6c1a33fffcde32001bacbb4f672140fbf9 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Michael Walle <michael@walle.cc>
 | 
					 | 
				
			||||||
Date: Wed, 8 Mar 2023 09:20:21 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] mtd: core: prepare mtd_otp_nvmem_add() to handle
 | 
					 | 
				
			||||||
 -EPROBE_DEFER
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
NVMEM soon will get the ability for nvmem layouts and these might
 | 
					 | 
				
			||||||
not be ready when nvmem_register() is called and thus it might
 | 
					 | 
				
			||||||
return -EPROBE_DEFER. Don't print the error message in this case.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Michael Walle <michael@walle.cc>
 | 
					 | 
				
			||||||
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
 | 
					 | 
				
			||||||
Link: https://lore.kernel.org/linux-mtd/20230308082021.870459-4-michael@walle.cc
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/mtd/mtdcore.c | 7 +++----
 | 
					 | 
				
			||||||
 1 file changed, 3 insertions(+), 4 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/mtd/mtdcore.c
 | 
					 | 
				
			||||||
+++ b/drivers/mtd/mtdcore.c
 | 
					 | 
				
			||||||
@@ -955,8 +955,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;
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
@@ -973,7 +973,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;
 | 
					 | 
				
			||||||
 			}
 | 
					 | 
				
			||||||
@@ -985,7 +984,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");
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /**
 | 
					 | 
				
			||||||
@ -1,107 +0,0 @@
 | 
				
			|||||||
From: Richard Gobert <richardbgobert@gmail.com>
 | 
					 | 
				
			||||||
Date: Wed, 3 Jan 2024 15:44:21 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] net: gro: parse ipv6 ext headers without frag0 invalidation
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The existing code always pulls the IPv6 header and sets the transport
 | 
					 | 
				
			||||||
offset initially. Then optionally again pulls any extension headers in
 | 
					 | 
				
			||||||
ipv6_gso_pull_exthdrs and sets the transport offset again on return from
 | 
					 | 
				
			||||||
that call. skb->data is set at the start of the first extension header
 | 
					 | 
				
			||||||
before calling ipv6_gso_pull_exthdrs, and must disable the frag0
 | 
					 | 
				
			||||||
optimization because that function uses pskb_may_pull/pskb_pull instead of
 | 
					 | 
				
			||||||
skb_gro_ helpers. It sets the GRO offset to the TCP header with
 | 
					 | 
				
			||||||
skb_gro_pull and sets the transport header. Then returns skb->data to its
 | 
					 | 
				
			||||||
position before this block.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This commit introduces a new helper function - ipv6_gro_pull_exthdrs -
 | 
					 | 
				
			||||||
which is used in ipv6_gro_receive to pull ipv6 ext headers instead of
 | 
					 | 
				
			||||||
ipv6_gso_pull_exthdrs. Thus, there is no modification of skb->data, all
 | 
					 | 
				
			||||||
operations use skb_gro_* helpers, and the frag0 fast path can be taken for
 | 
					 | 
				
			||||||
IPv6 packets with ext headers.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Richard Gobert <richardbgobert@gmail.com>
 | 
					 | 
				
			||||||
Reviewed-by: Willem de Bruijn <willemb@google.com>
 | 
					 | 
				
			||||||
Reviewed-by: David Ahern <dsahern@kernel.org>
 | 
					 | 
				
			||||||
Reviewed-by: Eric Dumazet <edumazet@google.com>
 | 
					 | 
				
			||||||
Link: https://lore.kernel.org/r/504130f6-b56c-4dcc-882c-97942c59f5b7@gmail.com
 | 
					 | 
				
			||||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/net/ipv6/ip6_offload.c
 | 
					 | 
				
			||||||
+++ b/net/ipv6/ip6_offload.c
 | 
					 | 
				
			||||||
@@ -36,6 +36,40 @@
 | 
					 | 
				
			||||||
 		INDIRECT_CALL_L4(cb, f2, f1, head, skb);	\
 | 
					 | 
				
			||||||
 })
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static int ipv6_gro_pull_exthdrs(struct sk_buff *skb, int off, int proto)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	const struct net_offload *ops = NULL;
 | 
					 | 
				
			||||||
+	struct ipv6_opt_hdr *opth;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	for (;;) {
 | 
					 | 
				
			||||||
+		int len;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		ops = rcu_dereference(inet6_offloads[proto]);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		if (unlikely(!ops))
 | 
					 | 
				
			||||||
+			break;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		if (!(ops->flags & INET6_PROTO_GSO_EXTHDR))
 | 
					 | 
				
			||||||
+			break;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		opth = skb_gro_header(skb, off + sizeof(*opth), off);
 | 
					 | 
				
			||||||
+		if (unlikely(!opth))
 | 
					 | 
				
			||||||
+			break;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		len = ipv6_optlen(opth);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		opth = skb_gro_header(skb, off + len, off);
 | 
					 | 
				
			||||||
+		if (unlikely(!opth))
 | 
					 | 
				
			||||||
+			break;
 | 
					 | 
				
			||||||
+		proto = opth->nexthdr;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		off += len;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	skb_gro_pull(skb, off - skb_network_offset(skb));
 | 
					 | 
				
			||||||
+	return proto;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static int ipv6_gso_pull_exthdrs(struct sk_buff *skb, int proto)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	const struct net_offload *ops = NULL;
 | 
					 | 
				
			||||||
@@ -224,28 +258,25 @@ INDIRECT_CALLABLE_SCOPE struct sk_buff *
 | 
					 | 
				
			||||||
 		goto out;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	skb_set_network_header(skb, off);
 | 
					 | 
				
			||||||
-	skb_gro_pull(skb, sizeof(*iph));
 | 
					 | 
				
			||||||
-	skb_set_transport_header(skb, skb_gro_offset(skb));
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	flush += ntohs(iph->payload_len) != skb_gro_len(skb);
 | 
					 | 
				
			||||||
+	flush += ntohs(iph->payload_len) != skb->len - hlen;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	proto = iph->nexthdr;
 | 
					 | 
				
			||||||
 	ops = rcu_dereference(inet6_offloads[proto]);
 | 
					 | 
				
			||||||
 	if (!ops || !ops->callbacks.gro_receive) {
 | 
					 | 
				
			||||||
-		pskb_pull(skb, skb_gro_offset(skb));
 | 
					 | 
				
			||||||
-		skb_gro_frag0_invalidate(skb);
 | 
					 | 
				
			||||||
-		proto = ipv6_gso_pull_exthdrs(skb, proto);
 | 
					 | 
				
			||||||
-		skb_gro_pull(skb, -skb_transport_offset(skb));
 | 
					 | 
				
			||||||
-		skb_reset_transport_header(skb);
 | 
					 | 
				
			||||||
-		__skb_push(skb, skb_gro_offset(skb));
 | 
					 | 
				
			||||||
+		proto = ipv6_gro_pull_exthdrs(skb, hlen, proto);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 		ops = rcu_dereference(inet6_offloads[proto]);
 | 
					 | 
				
			||||||
 		if (!ops || !ops->callbacks.gro_receive)
 | 
					 | 
				
			||||||
 			goto out;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-		iph = ipv6_hdr(skb);
 | 
					 | 
				
			||||||
+		iph = skb_gro_network_header(skb);
 | 
					 | 
				
			||||||
+	} else {
 | 
					 | 
				
			||||||
+		skb_gro_pull(skb, sizeof(*iph));
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	skb_set_transport_header(skb, skb_gro_offset(skb));
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	NAPI_GRO_CB(skb)->proto = proto;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	flush--;
 | 
					 | 
				
			||||||
@ -1,41 +0,0 @@
 | 
				
			|||||||
From 7390609b0121a1b982c5ecdfcd72dc328e5784ee Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Michael Walle <michael@walle.cc>
 | 
					 | 
				
			||||||
Date: Mon, 6 Feb 2023 13:43:42 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] net: add helper eth_addr_add()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Add a helper to add an offset to a ethernet address. This comes in handy
 | 
					 | 
				
			||||||
if you have a base ethernet address for multiple interfaces.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Michael Walle <michael@walle.cc>
 | 
					 | 
				
			||||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | 
					 | 
				
			||||||
Acked-by: Jakub Kicinski <kuba@kernel.org>
 | 
					 | 
				
			||||||
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
 | 
					 | 
				
			||||||
Link: https://lore.kernel.org/r/20230206134356.839737-9-srinivas.kandagatla@linaro.org
 | 
					 | 
				
			||||||
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 include/linux/etherdevice.h | 14 ++++++++++++++
 | 
					 | 
				
			||||||
 1 file changed, 14 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/include/linux/etherdevice.h
 | 
					 | 
				
			||||||
+++ b/include/linux/etherdevice.h
 | 
					 | 
				
			||||||
@@ -508,6 +508,20 @@ static inline void eth_addr_inc(u8 *addr
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /**
 | 
					 | 
				
			||||||
+ * eth_addr_add() - Add (or subtract) an offset to/from the given MAC address.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * @offset: Offset to add.
 | 
					 | 
				
			||||||
+ * @addr: Pointer to a six-byte array containing Ethernet address to increment.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+static inline void eth_addr_add(u8 *addr, long offset)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	u64 u = ether_addr_to_u64(addr);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	u += offset;
 | 
					 | 
				
			||||||
+	u64_to_ether_addr(u, addr);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/**
 | 
					 | 
				
			||||||
  * is_etherdev_addr - Tell if given Ethernet address belongs to the device.
 | 
					 | 
				
			||||||
  * @dev: Pointer to a device structure
 | 
					 | 
				
			||||||
  * @addr: Pointer to a six-byte array containing the Ethernet address
 | 
					 | 
				
			||||||
@ -1,32 +0,0 @@
 | 
				
			|||||||
From d387e34fec407f881fdf165b5d7ec128ebff362f Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Date: Tue, 19 Sep 2023 14:47:20 +0200
 | 
					 | 
				
			||||||
Subject: [PATCH] net: sfp: add quirk for Fiberstone GPON-ONU-34-20BI
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Fiberstone GPON-ONU-34-20B can operate at 2500base-X, but report 1.2GBd
 | 
					 | 
				
			||||||
NRZ in their EEPROM.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The module also require the ignore tx fault fixup similar to Huawei MA5671A
 | 
					 | 
				
			||||||
as it gets disabled on error messages with serial redirection enabled.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Link: https://lore.kernel.org/r/20230919124720.8210-1-ansuelsmth@gmail.com
 | 
					 | 
				
			||||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/phy/sfp.c | 5 +++++
 | 
					 | 
				
			||||||
 1 file changed, 5 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/sfp.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/sfp.c
 | 
					 | 
				
			||||||
@@ -393,6 +393,11 @@ static const struct sfp_quirk sfp_quirks
 | 
					 | 
				
			||||||
 	SFP_QUIRK("ALCATELLUCENT", "3FE46541AA", sfp_quirk_2500basex,
 | 
					 | 
				
			||||||
 		  sfp_fixup_long_startup),
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	// Fiberstore GPON-ONU-34-20BI can operate at 2500base-X, but report 1.2GBd
 | 
					 | 
				
			||||||
+	// NRZ in their EEPROM
 | 
					 | 
				
			||||||
+	SFP_QUIRK("FS", "GPON-ONU-34-20BI", sfp_quirk_2500basex,
 | 
					 | 
				
			||||||
+		  sfp_fixup_ignore_tx_fault),
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	SFP_QUIRK_F("HALNy", "HL-GSFP", sfp_fixup_halny_gsfp),
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	// HG MXPD-483II-F 2.5G supports 2500Base-X, but incorrectly reports
 | 
					 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1,183 +0,0 @@
 | 
				
			|||||||
From e1fbfa4a995d42e02e22b0dff2f8b4fdee1504b3 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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 <linux/device.h>
 | 
					 | 
				
			||||||
 #include <linux/phy.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+/* 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
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
@ -1,504 +0,0 @@
 | 
				
			|||||||
From e93984ebc1c82bd34f7a1b3391efaceee0a8ae96 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Robert Marko <robimarko@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Signed-off-by: Robert Marko <robimarko@gmail.com>
 | 
					 | 
				
			||||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/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 <linux/phy.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /* 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 <linux/bitfield.h>
 | 
					 | 
				
			||||||
+#include <linux/of.h>
 | 
					 | 
				
			||||||
+#include <linux/firmware.h>
 | 
					 | 
				
			||||||
+#include <linux/crc-ccitt.h>
 | 
					 | 
				
			||||||
+#include <linux/nvmem-consumer.h>
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#include <asm/unaligned.h>
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#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);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@ -1,326 +0,0 @@
 | 
				
			|||||||
From 4e4aafcddbbfcdd6eed5780e190fcbfac8b4685a Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Andrew Lunn <andrew@lunn.ch>
 | 
					 | 
				
			||||||
Date: Mon, 9 Jan 2023 16:30:41 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] net: mdio: Add dedicated C45 API to MDIO bus drivers
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Currently C22 and C45 transactions are mixed over a combined API calls
 | 
					 | 
				
			||||||
which make use of a special bit in the reg address to indicate if a
 | 
					 | 
				
			||||||
C45 transaction should be performed. This makes it impossible to know
 | 
					 | 
				
			||||||
if the bus driver actually supports C45. Additionally, many C22 only
 | 
					 | 
				
			||||||
drivers don't return -EOPNOTSUPP when asked to perform a C45
 | 
					 | 
				
			||||||
transaction, they mistaking perform a C22 transaction.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This is the first step to cleanly separate C22 from C45. To maintain
 | 
					 | 
				
			||||||
backwards compatibility until all drivers which are capable of
 | 
					 | 
				
			||||||
performing C45 are converted to this new API, the helper functions
 | 
					 | 
				
			||||||
will fall back to the older API if the new API is not
 | 
					 | 
				
			||||||
supported. Eventually this fallback will be removed.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
 | 
					 | 
				
			||||||
Signed-off-by: Michael Walle <michael@walle.cc>
 | 
					 | 
				
			||||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/phy/mdio_bus.c | 189 +++++++++++++++++++++++++++++++++++++
 | 
					 | 
				
			||||||
 include/linux/mdio.h       |  39 ++++----
 | 
					 | 
				
			||||||
 include/linux/phy.h        |   5 +
 | 
					 | 
				
			||||||
 3 files changed, 214 insertions(+), 19 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/mdio_bus.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/mdio_bus.c
 | 
					 | 
				
			||||||
@@ -832,6 +832,100 @@ int __mdiobus_modify_changed(struct mii_
 | 
					 | 
				
			||||||
 EXPORT_SYMBOL_GPL(__mdiobus_modify_changed);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /**
 | 
					 | 
				
			||||||
+ * __mdiobus_c45_read - Unlocked version of the mdiobus_c45_read function
 | 
					 | 
				
			||||||
+ * @bus: the mii_bus struct
 | 
					 | 
				
			||||||
+ * @addr: the phy address
 | 
					 | 
				
			||||||
+ * @devad: device address to read
 | 
					 | 
				
			||||||
+ * @regnum: register number to read
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Read a MDIO bus register. Caller must hold the mdio bus lock.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * NOTE: MUST NOT be called from interrupt context.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+int __mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int retval;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	lockdep_assert_held_once(&bus->mdio_lock);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (bus->read_c45)
 | 
					 | 
				
			||||||
+		retval = bus->read_c45(bus, addr, devad, regnum);
 | 
					 | 
				
			||||||
+	else
 | 
					 | 
				
			||||||
+		retval = bus->read(bus, addr, mdiobus_c45_addr(devad, regnum));
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	trace_mdio_access(bus, 1, addr, regnum, retval, retval);
 | 
					 | 
				
			||||||
+	mdiobus_stats_acct(&bus->stats[addr], true, retval);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return retval;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL(__mdiobus_c45_read);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/**
 | 
					 | 
				
			||||||
+ * __mdiobus_c45_write - Unlocked version of the mdiobus_write function
 | 
					 | 
				
			||||||
+ * @bus: the mii_bus struct
 | 
					 | 
				
			||||||
+ * @addr: the phy address
 | 
					 | 
				
			||||||
+ * @devad: device address to read
 | 
					 | 
				
			||||||
+ * @regnum: register number to write
 | 
					 | 
				
			||||||
+ * @val: value to write to @regnum
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Write a MDIO bus register. Caller must hold the mdio bus lock.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * NOTE: MUST NOT be called from interrupt context.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+int __mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum,
 | 
					 | 
				
			||||||
+			u16 val)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int err;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	lockdep_assert_held_once(&bus->mdio_lock);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (bus->write_c45)
 | 
					 | 
				
			||||||
+		err = bus->write_c45(bus, addr, devad, regnum, val);
 | 
					 | 
				
			||||||
+	else
 | 
					 | 
				
			||||||
+		err = bus->write(bus, addr, mdiobus_c45_addr(devad, regnum),
 | 
					 | 
				
			||||||
+				 val);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	trace_mdio_access(bus, 0, addr, regnum, val, err);
 | 
					 | 
				
			||||||
+	mdiobus_stats_acct(&bus->stats[addr], false, err);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return err;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL(__mdiobus_c45_write);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/**
 | 
					 | 
				
			||||||
+ * __mdiobus_c45_modify_changed - Unlocked version of the mdiobus_modify function
 | 
					 | 
				
			||||||
+ * @bus: the mii_bus struct
 | 
					 | 
				
			||||||
+ * @addr: the phy address
 | 
					 | 
				
			||||||
+ * @devad: device address to read
 | 
					 | 
				
			||||||
+ * @regnum: register number to modify
 | 
					 | 
				
			||||||
+ * @mask: bit mask of bits to clear
 | 
					 | 
				
			||||||
+ * @set: bit mask of bits to set
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Read, modify, and if any change, write the register value back to the
 | 
					 | 
				
			||||||
+ * device. Any error returns a negative number.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * NOTE: MUST NOT be called from interrupt context.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+static int __mdiobus_c45_modify_changed(struct mii_bus *bus, int addr,
 | 
					 | 
				
			||||||
+					int devad, u32 regnum, u16 mask,
 | 
					 | 
				
			||||||
+					u16 set)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int new, ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ret = __mdiobus_c45_read(bus, addr, devad, regnum);
 | 
					 | 
				
			||||||
+	if (ret < 0)
 | 
					 | 
				
			||||||
+		return ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	new = (ret & ~mask) | set;
 | 
					 | 
				
			||||||
+	if (new == ret)
 | 
					 | 
				
			||||||
+		return 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ret = __mdiobus_c45_write(bus, addr, devad, regnum, new);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return ret < 0 ? ret : 1;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/**
 | 
					 | 
				
			||||||
  * mdiobus_read_nested - Nested version of the mdiobus_read function
 | 
					 | 
				
			||||||
  * @bus: the mii_bus struct
 | 
					 | 
				
			||||||
  * @addr: the phy address
 | 
					 | 
				
			||||||
@@ -879,6 +973,29 @@ int mdiobus_read(struct mii_bus *bus, in
 | 
					 | 
				
			||||||
 EXPORT_SYMBOL(mdiobus_read);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /**
 | 
					 | 
				
			||||||
+ * mdiobus_c45_read - Convenience function for reading a given MII mgmt register
 | 
					 | 
				
			||||||
+ * @bus: the mii_bus struct
 | 
					 | 
				
			||||||
+ * @addr: the phy address
 | 
					 | 
				
			||||||
+ * @devad: device address to read
 | 
					 | 
				
			||||||
+ * @regnum: register number to read
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * NOTE: MUST NOT be called from interrupt context,
 | 
					 | 
				
			||||||
+ * because the bus read/write functions may wait for an interrupt
 | 
					 | 
				
			||||||
+ * to conclude the operation.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+int mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int retval;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	mutex_lock(&bus->mdio_lock);
 | 
					 | 
				
			||||||
+	retval = __mdiobus_c45_read(bus, addr, devad, regnum);
 | 
					 | 
				
			||||||
+	mutex_unlock(&bus->mdio_lock);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return retval;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL(mdiobus_c45_read);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/**
 | 
					 | 
				
			||||||
  * mdiobus_write_nested - Nested version of the mdiobus_write function
 | 
					 | 
				
			||||||
  * @bus: the mii_bus struct
 | 
					 | 
				
			||||||
  * @addr: the phy address
 | 
					 | 
				
			||||||
@@ -928,6 +1045,31 @@ int mdiobus_write(struct mii_bus *bus, i
 | 
					 | 
				
			||||||
 EXPORT_SYMBOL(mdiobus_write);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /**
 | 
					 | 
				
			||||||
+ * mdiobus_c45_write - Convenience function for writing a given MII mgmt register
 | 
					 | 
				
			||||||
+ * @bus: the mii_bus struct
 | 
					 | 
				
			||||||
+ * @addr: the phy address
 | 
					 | 
				
			||||||
+ * @devad: device address to read
 | 
					 | 
				
			||||||
+ * @regnum: register number to write
 | 
					 | 
				
			||||||
+ * @val: value to write to @regnum
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * NOTE: MUST NOT be called from interrupt context,
 | 
					 | 
				
			||||||
+ * because the bus read/write functions may wait for an interrupt
 | 
					 | 
				
			||||||
+ * to conclude the operation.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+int mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum,
 | 
					 | 
				
			||||||
+		      u16 val)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int err;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	mutex_lock(&bus->mdio_lock);
 | 
					 | 
				
			||||||
+	err = __mdiobus_c45_write(bus, addr, devad, regnum, val);
 | 
					 | 
				
			||||||
+	mutex_unlock(&bus->mdio_lock);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return err;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL(mdiobus_c45_write);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/**
 | 
					 | 
				
			||||||
  * mdiobus_modify - Convenience function for modifying a given mdio device
 | 
					 | 
				
			||||||
  *	register
 | 
					 | 
				
			||||||
  * @bus: the mii_bus struct
 | 
					 | 
				
			||||||
@@ -949,6 +1091,30 @@ int mdiobus_modify(struct mii_bus *bus,
 | 
					 | 
				
			||||||
 EXPORT_SYMBOL_GPL(mdiobus_modify);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /**
 | 
					 | 
				
			||||||
+ * mdiobus_c45_modify - Convenience function for modifying a given mdio device
 | 
					 | 
				
			||||||
+ *	register
 | 
					 | 
				
			||||||
+ * @bus: the mii_bus struct
 | 
					 | 
				
			||||||
+ * @addr: the phy address
 | 
					 | 
				
			||||||
+ * @devad: device address to read
 | 
					 | 
				
			||||||
+ * @regnum: register number to write
 | 
					 | 
				
			||||||
+ * @mask: bit mask of bits to clear
 | 
					 | 
				
			||||||
+ * @set: bit mask of bits to set
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+int mdiobus_c45_modify(struct mii_bus *bus, int addr, int devad, u32 regnum,
 | 
					 | 
				
			||||||
+		       u16 mask, u16 set)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int err;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	mutex_lock(&bus->mdio_lock);
 | 
					 | 
				
			||||||
+	err = __mdiobus_c45_modify_changed(bus, addr, devad, regnum,
 | 
					 | 
				
			||||||
+					   mask, set);
 | 
					 | 
				
			||||||
+	mutex_unlock(&bus->mdio_lock);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return err < 0 ? err : 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL_GPL(mdiobus_c45_modify);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/**
 | 
					 | 
				
			||||||
  * mdiobus_modify_changed - Convenience function for modifying a given mdio
 | 
					 | 
				
			||||||
  *	device register and returning if it changed
 | 
					 | 
				
			||||||
  * @bus: the mii_bus struct
 | 
					 | 
				
			||||||
@@ -971,6 +1137,29 @@ int mdiobus_modify_changed(struct mii_bu
 | 
					 | 
				
			||||||
 EXPORT_SYMBOL_GPL(mdiobus_modify_changed);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /**
 | 
					 | 
				
			||||||
+ * mdiobus_c45_modify_changed - Convenience function for modifying a given mdio
 | 
					 | 
				
			||||||
+ *	device register and returning if it changed
 | 
					 | 
				
			||||||
+ * @bus: the mii_bus struct
 | 
					 | 
				
			||||||
+ * @addr: the phy address
 | 
					 | 
				
			||||||
+ * @devad: device address to read
 | 
					 | 
				
			||||||
+ * @regnum: register number to write
 | 
					 | 
				
			||||||
+ * @mask: bit mask of bits to clear
 | 
					 | 
				
			||||||
+ * @set: bit mask of bits to set
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+int mdiobus_c45_modify_changed(struct mii_bus *bus, int devad, int addr,
 | 
					 | 
				
			||||||
+			       u32 regnum, u16 mask, u16 set)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int err;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	mutex_lock(&bus->mdio_lock);
 | 
					 | 
				
			||||||
+	err = __mdiobus_c45_modify_changed(bus, addr, devad, regnum, mask, set);
 | 
					 | 
				
			||||||
+	mutex_unlock(&bus->mdio_lock);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return err;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL_GPL(mdiobus_c45_modify_changed);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/**
 | 
					 | 
				
			||||||
  * mdio_bus_match - determine if given MDIO driver supports the given
 | 
					 | 
				
			||||||
  *		    MDIO device
 | 
					 | 
				
			||||||
  * @dev: target MDIO device
 | 
					 | 
				
			||||||
--- a/include/linux/mdio.h
 | 
					 | 
				
			||||||
+++ b/include/linux/mdio.h
 | 
					 | 
				
			||||||
@@ -423,6 +423,17 @@ int mdiobus_modify(struct mii_bus *bus,
 | 
					 | 
				
			||||||
 		   u16 set);
 | 
					 | 
				
			||||||
 int mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum,
 | 
					 | 
				
			||||||
 			   u16 mask, u16 set);
 | 
					 | 
				
			||||||
+int __mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum);
 | 
					 | 
				
			||||||
+int mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum);
 | 
					 | 
				
			||||||
+int __mdiobus_c45_write(struct mii_bus *bus, int addr,  int devad, u32 regnum,
 | 
					 | 
				
			||||||
+			u16 val);
 | 
					 | 
				
			||||||
+int mdiobus_c45_write(struct mii_bus *bus, int addr,  int devad, u32 regnum,
 | 
					 | 
				
			||||||
+		      u16 val);
 | 
					 | 
				
			||||||
+int mdiobus_c45_modify(struct mii_bus *bus, int addr, int devad, u32 regnum,
 | 
					 | 
				
			||||||
+		       u16 mask, u16 set);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+int mdiobus_c45_modify_changed(struct mii_bus *bus, int addr, int devad,
 | 
					 | 
				
			||||||
+			       u32 regnum, u16 mask, u16 set);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static inline int mdiodev_read(struct mdio_device *mdiodev, u32 regnum)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
@@ -463,29 +474,19 @@ static inline u16 mdiobus_c45_devad(u32
 | 
					 | 
				
			||||||
 	return FIELD_GET(MII_DEVADDR_C45_MASK, regnum);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-static inline int __mdiobus_c45_read(struct mii_bus *bus, int prtad, int devad,
 | 
					 | 
				
			||||||
-				     u16 regnum)
 | 
					 | 
				
			||||||
-{
 | 
					 | 
				
			||||||
-	return __mdiobus_read(bus, prtad, mdiobus_c45_addr(devad, regnum));
 | 
					 | 
				
			||||||
-}
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-static inline int __mdiobus_c45_write(struct mii_bus *bus, int prtad, int devad,
 | 
					 | 
				
			||||||
-				      u16 regnum, u16 val)
 | 
					 | 
				
			||||||
-{
 | 
					 | 
				
			||||||
-	return __mdiobus_write(bus, prtad, mdiobus_c45_addr(devad, regnum),
 | 
					 | 
				
			||||||
-			       val);
 | 
					 | 
				
			||||||
-}
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-static inline int mdiobus_c45_read(struct mii_bus *bus, int prtad, int devad,
 | 
					 | 
				
			||||||
-				   u16 regnum)
 | 
					 | 
				
			||||||
+static inline int mdiodev_c45_modify(struct mdio_device *mdiodev, int devad,
 | 
					 | 
				
			||||||
+				     u32 regnum, u16 mask, u16 set)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
-	return mdiobus_read(bus, prtad, mdiobus_c45_addr(devad, regnum));
 | 
					 | 
				
			||||||
+	return mdiobus_c45_modify(mdiodev->bus, mdiodev->addr, devad, regnum,
 | 
					 | 
				
			||||||
+				  mask, set);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-static inline int mdiobus_c45_write(struct mii_bus *bus, int prtad, int devad,
 | 
					 | 
				
			||||||
-				    u16 regnum, u16 val)
 | 
					 | 
				
			||||||
+static inline int mdiodev_c45_modify_changed(struct mdio_device *mdiodev,
 | 
					 | 
				
			||||||
+					     int devad, u32 regnum, u16 mask,
 | 
					 | 
				
			||||||
+					     u16 set)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
-	return mdiobus_write(bus, prtad, mdiobus_c45_addr(devad, regnum), val);
 | 
					 | 
				
			||||||
+	return mdiobus_c45_modify_changed(mdiodev->bus, mdiodev->addr, devad,
 | 
					 | 
				
			||||||
+					  regnum, mask, set);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 int mdiobus_register_device(struct mdio_device *mdiodev);
 | 
					 | 
				
			||||||
--- a/include/linux/phy.h
 | 
					 | 
				
			||||||
+++ b/include/linux/phy.h
 | 
					 | 
				
			||||||
@@ -364,6 +364,11 @@ struct mii_bus {
 | 
					 | 
				
			||||||
 	int (*read)(struct mii_bus *bus, int addr, int regnum);
 | 
					 | 
				
			||||||
 	/** @write: Perform a write transfer on the bus */
 | 
					 | 
				
			||||||
 	int (*write)(struct mii_bus *bus, int addr, int regnum, u16 val);
 | 
					 | 
				
			||||||
+	/** @read_c45: Perform a C45 read transfer on the bus */
 | 
					 | 
				
			||||||
+	int (*read_c45)(struct mii_bus *bus, int addr, int devnum, int regnum);
 | 
					 | 
				
			||||||
+	/** @write_c45: Perform a C45 write transfer on the bus */
 | 
					 | 
				
			||||||
+	int (*write_c45)(struct mii_bus *bus, int addr, int devnum,
 | 
					 | 
				
			||||||
+			 int regnum, u16 val);
 | 
					 | 
				
			||||||
 	/** @reset: Perform a reset of the bus */
 | 
					 | 
				
			||||||
 	int (*reset)(struct mii_bus *bus);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@ -1,105 +0,0 @@
 | 
				
			|||||||
From 9a0e95e34e9c0a713ddfd48c3a88a20d2bdfd514 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Gabor Juhos <j4g8y7@gmail.com>
 | 
					 | 
				
			||||||
Date: Fri, 11 Aug 2023 13:10:07 +0200
 | 
					 | 
				
			||||||
Subject: [PATCH] net: phy: Introduce PSGMII PHY interface mode
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The PSGMII interface is similar to QSGMII. The main difference
 | 
					 | 
				
			||||||
is that the PSGMII interface combines five SGMII lines into a
 | 
					 | 
				
			||||||
single link while in QSGMII only four lines are combined.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Similarly to the QSGMII, this interface mode might also needs
 | 
					 | 
				
			||||||
special handling within the MAC driver.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
It is commonly used by Qualcomm with their QCA807x PHY series and
 | 
					 | 
				
			||||||
modern WiSoC-s.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Add definitions for the PHY layer to allow to express this type
 | 
					 | 
				
			||||||
of connection between the MAC and PHY.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Gabor Juhos <j4g8y7@gmail.com>
 | 
					 | 
				
			||||||
Signed-off-by: Robert Marko <robert.marko@sartura.hr>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 Documentation/networking/phy.rst | 4 ++++
 | 
					 | 
				
			||||||
 drivers/net/phy/phy-core.c       | 2 ++
 | 
					 | 
				
			||||||
 drivers/net/phy/phylink.c        | 3 +++
 | 
					 | 
				
			||||||
 include/linux/phy.h              | 4 ++++
 | 
					 | 
				
			||||||
 4 files changed, 13 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/Documentation/networking/phy.rst
 | 
					 | 
				
			||||||
+++ b/Documentation/networking/phy.rst
 | 
					 | 
				
			||||||
@@ -323,6 +323,10 @@ Some of the interface modes are describe
 | 
					 | 
				
			||||||
     contrast with the 1000BASE-X phy mode used for Clause 38 and 39 PMDs, this
 | 
					 | 
				
			||||||
     interface mode has different autonegotiation and only supports full duplex.
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+``PHY_INTERFACE_MODE_PSGMII``
 | 
					 | 
				
			||||||
+    This is the Penta SGMII mode, it is similar to QSGMII but it combines 5
 | 
					 | 
				
			||||||
+    SGMII lines into a single link compared to 4 on QSGMII.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 Pause frames / flow control
 | 
					 | 
				
			||||||
 ===========================
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/phy-core.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/phy-core.c
 | 
					 | 
				
			||||||
@@ -140,6 +140,8 @@ int phy_interface_num_ports(phy_interfac
 | 
					 | 
				
			||||||
 	case PHY_INTERFACE_MODE_QSGMII:
 | 
					 | 
				
			||||||
 	case PHY_INTERFACE_MODE_QUSGMII:
 | 
					 | 
				
			||||||
 		return 4;
 | 
					 | 
				
			||||||
+	case PHY_INTERFACE_MODE_PSGMII:
 | 
					 | 
				
			||||||
+		return 5;
 | 
					 | 
				
			||||||
 	case PHY_INTERFACE_MODE_MAX:
 | 
					 | 
				
			||||||
 		WARN_ONCE(1, "PHY_INTERFACE_MODE_MAX isn't a valid interface mode");
 | 
					 | 
				
			||||||
 		return 0;
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/phylink.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/phylink.c
 | 
					 | 
				
			||||||
@@ -187,6 +187,7 @@ static int phylink_interface_max_speed(p
 | 
					 | 
				
			||||||
 	case PHY_INTERFACE_MODE_RGMII_RXID:
 | 
					 | 
				
			||||||
 	case PHY_INTERFACE_MODE_RGMII_ID:
 | 
					 | 
				
			||||||
 	case PHY_INTERFACE_MODE_RGMII:
 | 
					 | 
				
			||||||
+	case PHY_INTERFACE_MODE_PSGMII:
 | 
					 | 
				
			||||||
 	case PHY_INTERFACE_MODE_QSGMII:
 | 
					 | 
				
			||||||
 	case PHY_INTERFACE_MODE_QUSGMII:
 | 
					 | 
				
			||||||
 	case PHY_INTERFACE_MODE_SGMII:
 | 
					 | 
				
			||||||
@@ -448,6 +449,7 @@ unsigned long phylink_get_capabilities(p
 | 
					 | 
				
			||||||
 	case PHY_INTERFACE_MODE_RGMII_RXID:
 | 
					 | 
				
			||||||
 	case PHY_INTERFACE_MODE_RGMII_ID:
 | 
					 | 
				
			||||||
 	case PHY_INTERFACE_MODE_RGMII:
 | 
					 | 
				
			||||||
+	case PHY_INTERFACE_MODE_PSGMII:
 | 
					 | 
				
			||||||
 	case PHY_INTERFACE_MODE_QSGMII:
 | 
					 | 
				
			||||||
 	case PHY_INTERFACE_MODE_QUSGMII:
 | 
					 | 
				
			||||||
 	case PHY_INTERFACE_MODE_SGMII:
 | 
					 | 
				
			||||||
@@ -814,6 +816,7 @@ static int phylink_parse_mode(struct phy
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 		switch (pl->link_config.interface) {
 | 
					 | 
				
			||||||
 		case PHY_INTERFACE_MODE_SGMII:
 | 
					 | 
				
			||||||
+		case PHY_INTERFACE_MODE_PSGMII:
 | 
					 | 
				
			||||||
 		case PHY_INTERFACE_MODE_QSGMII:
 | 
					 | 
				
			||||||
 		case PHY_INTERFACE_MODE_QUSGMII:
 | 
					 | 
				
			||||||
 		case PHY_INTERFACE_MODE_RGMII:
 | 
					 | 
				
			||||||
--- a/include/linux/phy.h
 | 
					 | 
				
			||||||
+++ b/include/linux/phy.h
 | 
					 | 
				
			||||||
@@ -103,6 +103,7 @@ extern const int phy_10gbit_features_arr
 | 
					 | 
				
			||||||
  * @PHY_INTERFACE_MODE_XGMII: 10 gigabit media-independent interface
 | 
					 | 
				
			||||||
  * @PHY_INTERFACE_MODE_XLGMII:40 gigabit media-independent interface
 | 
					 | 
				
			||||||
  * @PHY_INTERFACE_MODE_MOCA: Multimedia over Coax
 | 
					 | 
				
			||||||
+ * @PHY_INTERFACE_MODE_PSGMII: Penta SGMII
 | 
					 | 
				
			||||||
  * @PHY_INTERFACE_MODE_QSGMII: Quad SGMII
 | 
					 | 
				
			||||||
  * @PHY_INTERFACE_MODE_TRGMII: Turbo RGMII
 | 
					 | 
				
			||||||
  * @PHY_INTERFACE_MODE_100BASEX: 100 BaseX
 | 
					 | 
				
			||||||
@@ -140,6 +141,7 @@ typedef enum {
 | 
					 | 
				
			||||||
 	PHY_INTERFACE_MODE_XGMII,
 | 
					 | 
				
			||||||
 	PHY_INTERFACE_MODE_XLGMII,
 | 
					 | 
				
			||||||
 	PHY_INTERFACE_MODE_MOCA,
 | 
					 | 
				
			||||||
+	PHY_INTERFACE_MODE_PSGMII,
 | 
					 | 
				
			||||||
 	PHY_INTERFACE_MODE_QSGMII,
 | 
					 | 
				
			||||||
 	PHY_INTERFACE_MODE_TRGMII,
 | 
					 | 
				
			||||||
 	PHY_INTERFACE_MODE_100BASEX,
 | 
					 | 
				
			||||||
@@ -247,6 +249,8 @@ static inline const char *phy_modes(phy_
 | 
					 | 
				
			||||||
 		return "xlgmii";
 | 
					 | 
				
			||||||
 	case PHY_INTERFACE_MODE_MOCA:
 | 
					 | 
				
			||||||
 		return "moca";
 | 
					 | 
				
			||||||
+	case PHY_INTERFACE_MODE_PSGMII:
 | 
					 | 
				
			||||||
+		return "psgmii";
 | 
					 | 
				
			||||||
 	case PHY_INTERFACE_MODE_QSGMII:
 | 
					 | 
				
			||||||
 		return "qsgmii";
 | 
					 | 
				
			||||||
 	case PHY_INTERFACE_MODE_TRGMII:
 | 
					 | 
				
			||||||
@ -1,32 +0,0 @@
 | 
				
			|||||||
From a593a2fcfdfb92cfd0ffc54bc81b07e6bfaaaf46 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
 | 
					 | 
				
			||||||
Date: Thu, 16 Mar 2023 14:08:26 +0200
 | 
					 | 
				
			||||||
Subject: [PATCH] net: phy: at803x: Replace of_gpio.h with what indeed is used
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
of_gpio.h in this driver is solely used as a proxy to other headers.
 | 
					 | 
				
			||||||
This is incorrect usage of the of_gpio.h. Replace it .h with what
 | 
					 | 
				
			||||||
indeed is used in the code.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
 | 
					 | 
				
			||||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/phy/at803x.c | 3 +--
 | 
					 | 
				
			||||||
 1 file changed, 1 insertion(+), 2 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/at803x.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/at803x.c
 | 
					 | 
				
			||||||
@@ -13,12 +13,11 @@
 | 
					 | 
				
			||||||
 #include <linux/netdevice.h>
 | 
					 | 
				
			||||||
 #include <linux/etherdevice.h>
 | 
					 | 
				
			||||||
 #include <linux/ethtool_netlink.h>
 | 
					 | 
				
			||||||
-#include <linux/of_gpio.h>
 | 
					 | 
				
			||||||
 #include <linux/bitfield.h>
 | 
					 | 
				
			||||||
-#include <linux/gpio/consumer.h>
 | 
					 | 
				
			||||||
 #include <linux/regulator/of_regulator.h>
 | 
					 | 
				
			||||||
 #include <linux/regulator/driver.h>
 | 
					 | 
				
			||||||
 #include <linux/regulator/consumer.h>
 | 
					 | 
				
			||||||
+#include <linux/of.h>
 | 
					 | 
				
			||||||
 #include <linux/phylink.h>
 | 
					 | 
				
			||||||
 #include <linux/sfp.h>
 | 
					 | 
				
			||||||
 #include <dt-bindings/net/qca-ar803x.h>
 | 
					 | 
				
			||||||
@ -1,70 +0,0 @@
 | 
				
			|||||||
From 8b8bc13d89a7d23d14b0e041c73f037c9db997b1 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Luo Jie <quic_luoj@quicinc.com>
 | 
					 | 
				
			||||||
Date: Sun, 16 Jul 2023 16:49:19 +0800
 | 
					 | 
				
			||||||
Subject: [PATCH 1/6] net: phy: at803x: support qca8081
 | 
					 | 
				
			||||||
 genphy_c45_pma_read_abilities
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
qca8081 PHY supports to use genphy_c45_pma_read_abilities for
 | 
					 | 
				
			||||||
getting the PHY features supported except for the autoneg ability
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
but autoneg ability exists in MDIO_STAT1 instead of MMD7.1, add it
 | 
					 | 
				
			||||||
manually after calling genphy_c45_pma_read_abilities.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
 | 
					 | 
				
			||||||
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/phy/at803x.c | 28 ++++++++++++++++++----------
 | 
					 | 
				
			||||||
 1 file changed, 18 insertions(+), 10 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/at803x.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/at803x.c
 | 
					 | 
				
			||||||
@@ -902,15 +902,6 @@ static int at803x_get_features(struct ph
 | 
					 | 
				
			||||||
 	if (err)
 | 
					 | 
				
			||||||
 		return err;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if (phydev->drv->phy_id == QCA8081_PHY_ID) {
 | 
					 | 
				
			||||||
-		err = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_NG_EXTABLE);
 | 
					 | 
				
			||||||
-		if (err < 0)
 | 
					 | 
				
			||||||
-			return err;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-		linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported,
 | 
					 | 
				
			||||||
-				err & MDIO_PMA_NG_EXTABLE_2_5GBT);
 | 
					 | 
				
			||||||
-	}
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 	if (phydev->drv->phy_id != ATH8031_PHY_ID)
 | 
					 | 
				
			||||||
 		return 0;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -1996,6 +1987,23 @@ static int qca808x_cable_test_get_status
 | 
					 | 
				
			||||||
 	return 0;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static int qca808x_get_features(struct phy_device *phydev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ret = genphy_c45_pma_read_abilities(phydev);
 | 
					 | 
				
			||||||
+	if (ret)
 | 
					 | 
				
			||||||
+		return ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* The autoneg ability is not existed in bit3 of MMD7.1,
 | 
					 | 
				
			||||||
+	 * but it is supported by qca808x PHY, so we add it here
 | 
					 | 
				
			||||||
+	 * manually.
 | 
					 | 
				
			||||||
+	 */
 | 
					 | 
				
			||||||
+	linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static struct phy_driver at803x_driver[] = {
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	/* Qualcomm Atheros AR8035 */
 | 
					 | 
				
			||||||
@@ -2163,7 +2171,7 @@ static struct phy_driver at803x_driver[]
 | 
					 | 
				
			||||||
 	.set_tunable		= at803x_set_tunable,
 | 
					 | 
				
			||||||
 	.set_wol		= at803x_set_wol,
 | 
					 | 
				
			||||||
 	.get_wol		= at803x_get_wol,
 | 
					 | 
				
			||||||
-	.get_features		= at803x_get_features,
 | 
					 | 
				
			||||||
+	.get_features		= qca808x_get_features,
 | 
					 | 
				
			||||||
 	.config_aneg		= at803x_config_aneg,
 | 
					 | 
				
			||||||
 	.suspend		= genphy_suspend,
 | 
					 | 
				
			||||||
 	.resume			= genphy_resume,
 | 
					 | 
				
			||||||
@ -1,73 +0,0 @@
 | 
				
			|||||||
From f3db55ae860a82e1224a909072783ef850e5d228 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Luo Jie <quic_luoj@quicinc.com>
 | 
					 | 
				
			||||||
Date: Sun, 16 Jul 2023 16:49:20 +0800
 | 
					 | 
				
			||||||
Subject: [PATCH 2/6] net: phy: at803x: merge qca8081 slave seed function
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
merge the seed enablement and seed value configuration into
 | 
					 | 
				
			||||||
one function, since the random seed value is needed to be
 | 
					 | 
				
			||||||
configured when the seed is enabled.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
 | 
					 | 
				
			||||||
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/phy/at803x.c | 29 +++++++++--------------------
 | 
					 | 
				
			||||||
 1 file changed, 9 insertions(+), 20 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/at803x.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/at803x.c
 | 
					 | 
				
			||||||
@@ -1730,24 +1730,19 @@ static int qca808x_phy_fast_retrain_conf
 | 
					 | 
				
			||||||
 	return 0;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-static int qca808x_phy_ms_random_seed_set(struct phy_device *phydev)
 | 
					 | 
				
			||||||
-{
 | 
					 | 
				
			||||||
-	u16 seed_value = prandom_u32_max(QCA808X_MASTER_SLAVE_SEED_RANGE);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
 | 
					 | 
				
			||||||
-			QCA808X_MASTER_SLAVE_SEED_CFG,
 | 
					 | 
				
			||||||
-			FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value));
 | 
					 | 
				
			||||||
-}
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 static int qca808x_phy_ms_seed_enable(struct phy_device *phydev, bool enable)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
-	u16 seed_enable = 0;
 | 
					 | 
				
			||||||
+	u16 seed_value;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if (enable)
 | 
					 | 
				
			||||||
-		seed_enable = QCA808X_MASTER_SLAVE_SEED_ENABLE;
 | 
					 | 
				
			||||||
+	if (!enable)
 | 
					 | 
				
			||||||
+		return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
 | 
					 | 
				
			||||||
+				QCA808X_MASTER_SLAVE_SEED_ENABLE, 0);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	seed_value = prandom_u32_max(QCA808X_MASTER_SLAVE_SEED_RANGE);
 | 
					 | 
				
			||||||
 	return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
 | 
					 | 
				
			||||||
-			QCA808X_MASTER_SLAVE_SEED_ENABLE, seed_enable);
 | 
					 | 
				
			||||||
+			QCA808X_MASTER_SLAVE_SEED_CFG | QCA808X_MASTER_SLAVE_SEED_ENABLE,
 | 
					 | 
				
			||||||
+			FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value) |
 | 
					 | 
				
			||||||
+			QCA808X_MASTER_SLAVE_SEED_ENABLE);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static int qca808x_config_init(struct phy_device *phydev)
 | 
					 | 
				
			||||||
@@ -1771,12 +1766,7 @@ static int qca808x_config_init(struct ph
 | 
					 | 
				
			||||||
 	if (ret)
 | 
					 | 
				
			||||||
 		return ret;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	/* Configure lower ramdom seed to make phy linked as slave mode */
 | 
					 | 
				
			||||||
-	ret = qca808x_phy_ms_random_seed_set(phydev);
 | 
					 | 
				
			||||||
-	if (ret)
 | 
					 | 
				
			||||||
-		return ret;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	/* Enable seed */
 | 
					 | 
				
			||||||
+	/* Enable seed and configure lower ramdom seed to make phy linked as slave mode */
 | 
					 | 
				
			||||||
 	ret = qca808x_phy_ms_seed_enable(phydev, true);
 | 
					 | 
				
			||||||
 	if (ret)
 | 
					 | 
				
			||||||
 		return ret;
 | 
					 | 
				
			||||||
@@ -1821,7 +1811,6 @@ static int qca808x_read_status(struct ph
 | 
					 | 
				
			||||||
 		if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR) {
 | 
					 | 
				
			||||||
 			qca808x_phy_ms_seed_enable(phydev, false);
 | 
					 | 
				
			||||||
 		} else {
 | 
					 | 
				
			||||||
-			qca808x_phy_ms_random_seed_set(phydev);
 | 
					 | 
				
			||||||
 			qca808x_phy_ms_seed_enable(phydev, true);
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
@ -1,76 +0,0 @@
 | 
				
			|||||||
From 7cc3209558002d95c0d45a1276ba4f5f741eec42 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Luo Jie <quic_luoj@quicinc.com>
 | 
					 | 
				
			||||||
Date: Sun, 16 Jul 2023 16:49:21 +0800
 | 
					 | 
				
			||||||
Subject: [PATCH 3/6] net: phy: at803x: enable qca8081 slave seed conditionally
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
qca8081 is the single port PHY, the slave prefer mode is used
 | 
					 | 
				
			||||||
by default.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if the phy master perfer mode is configured, the slave seed
 | 
					 | 
				
			||||||
configuration should not be enabled, since the slave seed
 | 
					 | 
				
			||||||
enablement is for making PHY linked as slave mode easily.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
disable slave seed if the master mode is preferred.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
 | 
					 | 
				
			||||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/phy/at803x.c | 25 ++++++++++++++++++++-----
 | 
					 | 
				
			||||||
 1 file changed, 20 insertions(+), 5 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/at803x.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/at803x.c
 | 
					 | 
				
			||||||
@@ -1745,6 +1745,12 @@ static int qca808x_phy_ms_seed_enable(st
 | 
					 | 
				
			||||||
 			QCA808X_MASTER_SLAVE_SEED_ENABLE);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static bool qca808x_is_prefer_master(struct phy_device *phydev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_FORCE) ||
 | 
					 | 
				
			||||||
+		(phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_PREFERRED);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static int qca808x_config_init(struct phy_device *phydev)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	int ret;
 | 
					 | 
				
			||||||
@@ -1766,11 +1772,17 @@ static int qca808x_config_init(struct ph
 | 
					 | 
				
			||||||
 	if (ret)
 | 
					 | 
				
			||||||
 		return ret;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	/* Enable seed and configure lower ramdom seed to make phy linked as slave mode */
 | 
					 | 
				
			||||||
-	ret = qca808x_phy_ms_seed_enable(phydev, true);
 | 
					 | 
				
			||||||
-	if (ret)
 | 
					 | 
				
			||||||
+	ret = genphy_read_master_slave(phydev);
 | 
					 | 
				
			||||||
+	if (ret < 0)
 | 
					 | 
				
			||||||
 		return ret;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	if (!qca808x_is_prefer_master(phydev)) {
 | 
					 | 
				
			||||||
+		/* Enable seed and configure lower ramdom seed to make phy linked as slave mode */
 | 
					 | 
				
			||||||
+		ret = qca808x_phy_ms_seed_enable(phydev, true);
 | 
					 | 
				
			||||||
+		if (ret)
 | 
					 | 
				
			||||||
+			return ret;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	/* Configure adc threshold as 100mv for the link 10M */
 | 
					 | 
				
			||||||
 	return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD,
 | 
					 | 
				
			||||||
 			QCA808X_ADC_THRESHOLD_MASK, QCA808X_ADC_THRESHOLD_100MV);
 | 
					 | 
				
			||||||
@@ -1802,13 +1814,16 @@ static int qca808x_read_status(struct ph
 | 
					 | 
				
			||||||
 			phydev->interface = PHY_INTERFACE_MODE_SGMII;
 | 
					 | 
				
			||||||
 	} else {
 | 
					 | 
				
			||||||
 		/* generate seed as a lower random value to make PHY linked as SLAVE easily,
 | 
					 | 
				
			||||||
-		 * except for master/slave configuration fault detected.
 | 
					 | 
				
			||||||
+		 * except for master/slave configuration fault detected or the master mode
 | 
					 | 
				
			||||||
+		 * preferred.
 | 
					 | 
				
			||||||
+		 *
 | 
					 | 
				
			||||||
 		 * the reason for not putting this code into the function link_change_notify is
 | 
					 | 
				
			||||||
 		 * the corner case where the link partner is also the qca8081 PHY and the seed
 | 
					 | 
				
			||||||
 		 * value is configured as the same value, the link can't be up and no link change
 | 
					 | 
				
			||||||
 		 * occurs.
 | 
					 | 
				
			||||||
 		 */
 | 
					 | 
				
			||||||
-		if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR) {
 | 
					 | 
				
			||||||
+		if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||
 | 
					 | 
				
			||||||
+				qca808x_is_prefer_master(phydev)) {
 | 
					 | 
				
			||||||
 			qca808x_phy_ms_seed_enable(phydev, false);
 | 
					 | 
				
			||||||
 		} else {
 | 
					 | 
				
			||||||
 			qca808x_phy_ms_seed_enable(phydev, true);
 | 
					 | 
				
			||||||
@ -1,48 +0,0 @@
 | 
				
			|||||||
From fea7cfb83d1a2782e39cd101dd44ed2548539de5 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Luo Jie <quic_luoj@quicinc.com>
 | 
					 | 
				
			||||||
Date: Sun, 16 Jul 2023 16:49:22 +0800
 | 
					 | 
				
			||||||
Subject: [PATCH 4/6] net: phy: at803x: support qca8081 1G chip type
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The qca8081 1G chip version does not support 2.5 capability, which
 | 
					 | 
				
			||||||
is distinguished from qca8081 2.5G chip according to the bit0 of
 | 
					 | 
				
			||||||
register mmd7.0x901d, the 1G version chip also has the same PHY ID
 | 
					 | 
				
			||||||
as the normal qca8081 2.5G chip.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
 | 
					 | 
				
			||||||
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/phy/at803x.c | 15 +++++++++++++++
 | 
					 | 
				
			||||||
 1 file changed, 15 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/at803x.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/at803x.c
 | 
					 | 
				
			||||||
@@ -272,6 +272,10 @@
 | 
					 | 
				
			||||||
 #define QCA808X_CDT_STATUS_STAT_OPEN		2
 | 
					 | 
				
			||||||
 #define QCA808X_CDT_STATUS_STAT_SHORT		3
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+/* QCA808X 1G chip type */
 | 
					 | 
				
			||||||
+#define QCA808X_PHY_MMD7_CHIP_TYPE		0x901d
 | 
					 | 
				
			||||||
+#define QCA808X_PHY_CHIP_TYPE_1G		BIT(0)
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 MODULE_DESCRIPTION("Qualcomm Atheros AR803x and QCA808X PHY driver");
 | 
					 | 
				
			||||||
 MODULE_AUTHOR("Matus Ujhelyi");
 | 
					 | 
				
			||||||
 MODULE_LICENSE("GPL");
 | 
					 | 
				
			||||||
@@ -2005,6 +2009,17 @@ static int qca808x_get_features(struct p
 | 
					 | 
				
			||||||
 	 */
 | 
					 | 
				
			||||||
 	linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	/* As for the qca8081 1G version chip, the 2500baseT ability is also
 | 
					 | 
				
			||||||
+	 * existed in the bit0 of MMD1.21, we need to remove it manually if
 | 
					 | 
				
			||||||
+	 * it is the qca8081 1G chip according to the bit0 of MMD7.0x901d.
 | 
					 | 
				
			||||||
+	 */
 | 
					 | 
				
			||||||
+	ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE);
 | 
					 | 
				
			||||||
+	if (ret < 0)
 | 
					 | 
				
			||||||
+		return ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (QCA808X_PHY_CHIP_TYPE_1G & ret)
 | 
					 | 
				
			||||||
+		linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	return 0;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@ -1,98 +0,0 @@
 | 
				
			|||||||
From df9401ff3e6eeaa42bfb06761967f1b71f5afce7 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Luo Jie <quic_luoj@quicinc.com>
 | 
					 | 
				
			||||||
Date: Sun, 16 Jul 2023 16:49:23 +0800
 | 
					 | 
				
			||||||
Subject: [PATCH 5/6] net: phy: at803x: remove qca8081 1G fast retrain and
 | 
					 | 
				
			||||||
 slave seed config
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The fast retrain and slave seed configs are only applicable when the 2.5G
 | 
					 | 
				
			||||||
ability is supported.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
 | 
					 | 
				
			||||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/phy/at803x.c | 50 +++++++++++++++++++++++++---------------
 | 
					 | 
				
			||||||
 1 file changed, 32 insertions(+), 18 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/at803x.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/at803x.c
 | 
					 | 
				
			||||||
@@ -1755,6 +1755,11 @@ static bool qca808x_is_prefer_master(str
 | 
					 | 
				
			||||||
 		(phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_PREFERRED);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static bool qca808x_has_fast_retrain_or_slave_seed(struct phy_device *phydev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static int qca808x_config_init(struct phy_device *phydev)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	int ret;
 | 
					 | 
				
			||||||
@@ -1771,20 +1776,24 @@ static int qca808x_config_init(struct ph
 | 
					 | 
				
			||||||
 	if (ret)
 | 
					 | 
				
			||||||
 		return ret;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	/* Config the fast retrain for the link 2500M */
 | 
					 | 
				
			||||||
-	ret = qca808x_phy_fast_retrain_config(phydev);
 | 
					 | 
				
			||||||
-	if (ret)
 | 
					 | 
				
			||||||
-		return ret;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	ret = genphy_read_master_slave(phydev);
 | 
					 | 
				
			||||||
-	if (ret < 0)
 | 
					 | 
				
			||||||
-		return ret;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	if (!qca808x_is_prefer_master(phydev)) {
 | 
					 | 
				
			||||||
-		/* Enable seed and configure lower ramdom seed to make phy linked as slave mode */
 | 
					 | 
				
			||||||
-		ret = qca808x_phy_ms_seed_enable(phydev, true);
 | 
					 | 
				
			||||||
+	if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
 | 
					 | 
				
			||||||
+		/* Config the fast retrain for the link 2500M */
 | 
					 | 
				
			||||||
+		ret = qca808x_phy_fast_retrain_config(phydev);
 | 
					 | 
				
			||||||
 		if (ret)
 | 
					 | 
				
			||||||
 			return ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		ret = genphy_read_master_slave(phydev);
 | 
					 | 
				
			||||||
+		if (ret < 0)
 | 
					 | 
				
			||||||
+			return ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		if (!qca808x_is_prefer_master(phydev)) {
 | 
					 | 
				
			||||||
+			/* Enable seed and configure lower ramdom seed to make phy
 | 
					 | 
				
			||||||
+			 * linked as slave mode.
 | 
					 | 
				
			||||||
+			 */
 | 
					 | 
				
			||||||
+			ret = qca808x_phy_ms_seed_enable(phydev, true);
 | 
					 | 
				
			||||||
+			if (ret)
 | 
					 | 
				
			||||||
+				return ret;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* Configure adc threshold as 100mv for the link 10M */
 | 
					 | 
				
			||||||
@@ -1826,11 +1835,13 @@ static int qca808x_read_status(struct ph
 | 
					 | 
				
			||||||
 		 * value is configured as the same value, the link can't be up and no link change
 | 
					 | 
				
			||||||
 		 * occurs.
 | 
					 | 
				
			||||||
 		 */
 | 
					 | 
				
			||||||
-		if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||
 | 
					 | 
				
			||||||
-				qca808x_is_prefer_master(phydev)) {
 | 
					 | 
				
			||||||
-			qca808x_phy_ms_seed_enable(phydev, false);
 | 
					 | 
				
			||||||
-		} else {
 | 
					 | 
				
			||||||
-			qca808x_phy_ms_seed_enable(phydev, true);
 | 
					 | 
				
			||||||
+		if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
 | 
					 | 
				
			||||||
+			if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||
 | 
					 | 
				
			||||||
+					qca808x_is_prefer_master(phydev)) {
 | 
					 | 
				
			||||||
+				qca808x_phy_ms_seed_enable(phydev, false);
 | 
					 | 
				
			||||||
+			} else {
 | 
					 | 
				
			||||||
+				qca808x_phy_ms_seed_enable(phydev, true);
 | 
					 | 
				
			||||||
+			}
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -1845,7 +1856,10 @@ static int qca808x_soft_reset(struct phy
 | 
					 | 
				
			||||||
 	if (ret < 0)
 | 
					 | 
				
			||||||
 		return ret;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	return qca808x_phy_ms_seed_enable(phydev, true);
 | 
					 | 
				
			||||||
+	if (qca808x_has_fast_retrain_or_slave_seed(phydev))
 | 
					 | 
				
			||||||
+		ret = qca808x_phy_ms_seed_enable(phydev, true);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return ret;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static bool qca808x_cdt_fault_length_valid(int cdt_code)
 | 
					 | 
				
			||||||
@ -1,55 +0,0 @@
 | 
				
			|||||||
From 723970affdd8766fa0d91cd34bf2ffc861538b5f Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Luo Jie <quic_luoj@quicinc.com>
 | 
					 | 
				
			||||||
Date: Sun, 16 Jul 2023 16:49:24 +0800
 | 
					 | 
				
			||||||
Subject: [PATCH 6/6] net: phy: at803x: add qca8081 fifo reset on the link
 | 
					 | 
				
			||||||
 changed
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The qca8081 sgmii fifo needs to be reset on link down and
 | 
					 | 
				
			||||||
released on the link up in case of any abnormal issue
 | 
					 | 
				
			||||||
such as the packet blocked on the PHY.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
 | 
					 | 
				
			||||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | 
					 | 
				
			||||||
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/phy/at803x.c | 14 ++++++++++++++
 | 
					 | 
				
			||||||
 1 file changed, 14 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/at803x.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/at803x.c
 | 
					 | 
				
			||||||
@@ -276,6 +276,9 @@
 | 
					 | 
				
			||||||
 #define QCA808X_PHY_MMD7_CHIP_TYPE		0x901d
 | 
					 | 
				
			||||||
 #define QCA808X_PHY_CHIP_TYPE_1G		BIT(0)
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+#define QCA8081_PHY_SERDES_MMD1_FIFO_CTRL	0x9072
 | 
					 | 
				
			||||||
+#define QCA8081_PHY_FIFO_RSTN			BIT(11)
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 MODULE_DESCRIPTION("Qualcomm Atheros AR803x and QCA808X PHY driver");
 | 
					 | 
				
			||||||
 MODULE_AUTHOR("Matus Ujhelyi");
 | 
					 | 
				
			||||||
 MODULE_LICENSE("GPL");
 | 
					 | 
				
			||||||
@@ -2037,6 +2040,16 @@ static int qca808x_get_features(struct p
 | 
					 | 
				
			||||||
 	return 0;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static void qca808x_link_change_notify(struct phy_device *phydev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	/* Assert interface sgmii fifo on link down, deassert it on link up,
 | 
					 | 
				
			||||||
+	 * the interface device address is always phy address added by 1.
 | 
					 | 
				
			||||||
+	 */
 | 
					 | 
				
			||||||
+	mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1,
 | 
					 | 
				
			||||||
+			MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL,
 | 
					 | 
				
			||||||
+			QCA8081_PHY_FIFO_RSTN, phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static struct phy_driver at803x_driver[] = {
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	/* Qualcomm Atheros AR8035 */
 | 
					 | 
				
			||||||
@@ -2213,6 +2226,7 @@ static struct phy_driver at803x_driver[]
 | 
					 | 
				
			||||||
 	.soft_reset		= qca808x_soft_reset,
 | 
					 | 
				
			||||||
 	.cable_test_start	= qca808x_cable_test_start,
 | 
					 | 
				
			||||||
 	.cable_test_get_status	= qca808x_cable_test_get_status,
 | 
					 | 
				
			||||||
+	.link_change_notify	= qca808x_link_change_notify,
 | 
					 | 
				
			||||||
 }, };
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 module_phy_driver(at803x_driver);
 | 
					 | 
				
			||||||
@ -1,69 +0,0 @@
 | 
				
			|||||||
From 6a3b8c573b5a152a6aa7a0b54c5e18b84c6ba6f5 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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,
 | 
					 | 
				
			||||||
@ -1,129 +0,0 @@
 | 
				
			|||||||
From 07b1ad83b9ed6db1735ba10baf67b7a565ac0cef Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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,
 | 
					 | 
				
			||||||
 }, {
 | 
					 | 
				
			||||||
@ -1,155 +0,0 @@
 | 
				
			|||||||
From d43cff3f82336c0bd965ea552232d9f4ddac71a6 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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 */
 | 
					 | 
				
			||||||
@ -1,94 +0,0 @@
 | 
				
			|||||||
From 900eef75cc5018e149c52fe305c9c3fe424c52a7 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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
 | 
					 | 
				
			||||||
 	 */
 | 
					 | 
				
			||||||
@ -1,78 +0,0 @@
 | 
				
			|||||||
From 25d2ba94005fac18fe68878cddff59a67e115554 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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
 | 
					 | 
				
			||||||
 	 */
 | 
					 | 
				
			||||||
@ -1,86 +0,0 @@
 | 
				
			|||||||
From 3ae3bc426eaf57ca8f53d75777d9a5ef779bc7b7 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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,
 | 
					 | 
				
			||||||
@ -1,92 +0,0 @@
 | 
				
			|||||||
From 27b89c9dc1b0393090d68d651b82f30ad2696baa Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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,
 | 
					 | 
				
			||||||
@ -1,78 +0,0 @@
 | 
				
			|||||||
From 30dd62191d3dd97c08f7f9dc9ce77ffab457e4fb Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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,
 | 
					 | 
				
			||||||
@ -1,78 +0,0 @@
 | 
				
			|||||||
From a5ab9d8e7ae0da8328ac1637a9755311508dc8ab Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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)
 | 
					 | 
				
			||||||
@ -1,297 +0,0 @@
 | 
				
			|||||||
From f932a6dc8bae0dae9645b5b1b4c65aed8a8acb2a Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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;
 | 
					 | 
				
			||||||
@ -1,114 +0,0 @@
 | 
				
			|||||||
From 21a2802a8365cfa82cc02187c1f95136d85592ad Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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,
 | 
					 | 
				
			||||||
@ -1,219 +0,0 @@
 | 
				
			|||||||
From ef9df47b449e32e06501a11272809be49019bdb6 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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,
 | 
					 | 
				
			||||||
@ -1,116 +0,0 @@
 | 
				
			|||||||
From 8e732f1c6f2dc5e18f766d0f1b11df9db2dd044a Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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,
 | 
					 | 
				
			||||||
@ -1,97 +0,0 @@
 | 
				
			|||||||
From 38eb804e8458ba181a03a0498ce4bf84eebd1931 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@ -1,27 +0,0 @@
 | 
				
			|||||||
From fc9d7264ddc32eaa647d6bfcdc25cdf9f786fde0 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | 
					 | 
				
			||||||
Link: https://lore.kernel.org/r/20231217232739.27065-1-ansuelsmth@gmail.com
 | 
					 | 
				
			||||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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;
 | 
					 | 
				
			||||||
@ -1,38 +0,0 @@
 | 
				
			|||||||
From 3ab5720881a924fb6405d9e6a3b09f1026467c47 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | 
					 | 
				
			||||||
Link: https://lore.kernel.org/r/20231217232508.26470-1-ansuelsmth@gmail.com
 | 
					 | 
				
			||||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
@ -1,152 +0,0 @@
 | 
				
			|||||||
From 7961ef1fa10ec35ad6923fb5751877116e4b035b Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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[] = {
 | 
					 | 
				
			||||||
@ -1,62 +0,0 @@
 | 
				
			|||||||
From 22eb276098da820d9440fad22901f9b74ed4d659 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Reviewed-by: Simon Horman <horms@kernel.org>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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)
 | 
					 | 
				
			||||||
@ -1,118 +0,0 @@
 | 
				
			|||||||
From e0e9ada1df6133513249861c1d91c1dbefd9383b Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Reviewed-by: Simon Horman <horms@kernel.org>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@ -1,182 +0,0 @@
 | 
				
			|||||||
From ea73e5ea442ee2aade67b1fb1233ccb3cbea2ceb Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Reviewed-by: Simon Horman <horms@kernel.org>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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;
 | 
					 | 
				
			||||||
@ -1,62 +0,0 @@
 | 
				
			|||||||
From c34d9452d4e5d98a655d7b625e85466320885416 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Reviewed-by: Simon Horman <horms@kernel.org>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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,
 | 
					 | 
				
			||||||
@ -1,408 +0,0 @@
 | 
				
			|||||||
From 7196062b64ee470b91015f3d2e82d225948258ea Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | 
					 | 
				
			||||||
Link: https://lore.kernel.org/r/20240125203702.4552-6-ansuelsmth@gmail.com
 | 
					 | 
				
			||||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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);
 | 
					 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1,243 +0,0 @@
 | 
				
			|||||||
From 6fb760972c49490b03f3db2ad64cf30bdd28c54a Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | 
					 | 
				
			||||||
Link: https://lore.kernel.org/r/20240129141600.2592-3-ansuelsmth@gmail.com
 | 
					 | 
				
			||||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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 <linux/sfp.h>
 | 
					 | 
				
			||||||
 #include <dt-bindings/net/qca-ar803x.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+#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 <linux/phy.h>
 | 
					 | 
				
			||||||
+#include <linux/module.h>
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#include "qcom.h"
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+MODULE_DESCRIPTION("Qualcomm PHY driver Common Functions");
 | 
					 | 
				
			||||||
+MODULE_AUTHOR("Matus Ujhelyi");
 | 
					 | 
				
			||||||
+MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
 | 
					 | 
				
			||||||
+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);
 | 
					 | 
				
			||||||
@ -1,638 +0,0 @@
 | 
				
			|||||||
From 2e45d404d99d43bb7127b74b5dea8818df64996c Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | 
					 | 
				
			||||||
Link: https://lore.kernel.org/r/20240129141600.2592-4-ansuelsmth@gmail.com
 | 
					 | 
				
			||||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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 <linux/phy.h>
 | 
					 | 
				
			||||||
+#include <linux/module.h>
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#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 <ansuelsmth@gmail.com>");
 | 
					 | 
				
			||||||
+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);
 | 
					 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1,394 +0,0 @@
 | 
				
			|||||||
From 4765a9722e09765866e131ec31f7b9cf4c1f4854 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Daniel Golle <daniel@makrotopia.org>
 | 
					 | 
				
			||||||
Date: Sun, 19 Mar 2023 12:57:50 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] net: pcs: add driver for MediaTek SGMII PCS
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The SGMII core found in several MediaTek SoCs is identical to what can
 | 
					 | 
				
			||||||
also be found in MediaTek's MT7531 Ethernet switch IC.
 | 
					 | 
				
			||||||
As this has not always been clear, both drivers developed different
 | 
					 | 
				
			||||||
implementations to deal with the PCS.
 | 
					 | 
				
			||||||
Recently Alexander Couzens pointed out this fact which lead to the
 | 
					 | 
				
			||||||
development of this shared driver.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Add a dedicated driver, mostly by copying the code now found in the
 | 
					 | 
				
			||||||
Ethernet driver. The now redundant code will be removed by a follow-up
 | 
					 | 
				
			||||||
commit.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Suggested-by: Alexander Couzens <lynxis@fe80.eu>
 | 
					 | 
				
			||||||
Suggested-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 | 
					 | 
				
			||||||
Tested-by: Frank Wunderlich <frank-w@public-files.de>
 | 
					 | 
				
			||||||
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 MAINTAINERS                       |   8 +
 | 
					 | 
				
			||||||
 drivers/net/pcs/Kconfig           |   7 +
 | 
					 | 
				
			||||||
 drivers/net/pcs/Makefile          |   1 +
 | 
					 | 
				
			||||||
 drivers/net/pcs/pcs-mtk-lynxi.c   | 305 ++++++++++++++++++++++++++++++
 | 
					 | 
				
			||||||
 include/linux/pcs/pcs-mtk-lynxi.h |  13 ++
 | 
					 | 
				
			||||||
 5 files changed, 334 insertions(+)
 | 
					 | 
				
			||||||
 create mode 100644 drivers/net/pcs/pcs-mtk-lynxi.c
 | 
					 | 
				
			||||||
 create mode 100644 include/linux/pcs/pcs-mtk-lynxi.h
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/MAINTAINERS
 | 
					 | 
				
			||||||
+++ b/MAINTAINERS
 | 
					 | 
				
			||||||
@@ -12935,6 +12935,14 @@ L:	netdev@vger.kernel.org
 | 
					 | 
				
			||||||
 S:	Maintained
 | 
					 | 
				
			||||||
 F:	drivers/net/ethernet/mediatek/
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+MEDIATEK ETHERNET PCS DRIVER
 | 
					 | 
				
			||||||
+M:	Alexander Couzens <lynxis@fe80.eu>
 | 
					 | 
				
			||||||
+M:	Daniel Golle <daniel@makrotopia.org>
 | 
					 | 
				
			||||||
+L:	netdev@vger.kernel.org
 | 
					 | 
				
			||||||
+S:	Maintained
 | 
					 | 
				
			||||||
+F:	drivers/net/pcs/pcs-mtk-lynxi.c
 | 
					 | 
				
			||||||
+F:	include/linux/pcs/pcs-mtk-lynxi.h
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 MEDIATEK I2C CONTROLLER DRIVER
 | 
					 | 
				
			||||||
 M:	Qii Wang <qii.wang@mediatek.com>
 | 
					 | 
				
			||||||
 L:	linux-i2c@vger.kernel.org
 | 
					 | 
				
			||||||
--- a/drivers/net/pcs/Kconfig
 | 
					 | 
				
			||||||
+++ b/drivers/net/pcs/Kconfig
 | 
					 | 
				
			||||||
@@ -32,4 +32,11 @@ config PCS_ALTERA_TSE
 | 
					 | 
				
			||||||
 	  This module provides helper functions for the Altera Triple Speed
 | 
					 | 
				
			||||||
 	  Ethernet SGMII PCS, that can be found on the Intel Socfpga family.
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+config PCS_MTK_LYNXI
 | 
					 | 
				
			||||||
+	tristate
 | 
					 | 
				
			||||||
+	select REGMAP
 | 
					 | 
				
			||||||
+	help
 | 
					 | 
				
			||||||
+	  This module provides helpers to phylink for managing the LynxI PCS
 | 
					 | 
				
			||||||
+	  which is part of MediaTek's SoC and Ethernet switch ICs.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 endmenu
 | 
					 | 
				
			||||||
--- a/drivers/net/pcs/Makefile
 | 
					 | 
				
			||||||
+++ b/drivers/net/pcs/Makefile
 | 
					 | 
				
			||||||
@@ -7,3 +7,4 @@ obj-$(CONFIG_PCS_XPCS)		+= pcs_xpcs.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_PCS_LYNX)		+= pcs-lynx.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_PCS_RZN1_MIIC)	+= pcs-rzn1-miic.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_PCS_ALTERA_TSE)	+= pcs-altera-tse.o
 | 
					 | 
				
			||||||
+obj-$(CONFIG_PCS_MTK_LYNXI)	+= pcs-mtk-lynxi.o
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/drivers/net/pcs/pcs-mtk-lynxi.c
 | 
					 | 
				
			||||||
@@ -0,0 +1,305 @@
 | 
					 | 
				
			||||||
+// SPDX-License-Identifier: GPL-2.0
 | 
					 | 
				
			||||||
+// Copyright (c) 2018-2019 MediaTek Inc.
 | 
					 | 
				
			||||||
+/* A library for MediaTek SGMII circuit
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Author: Sean Wang <sean.wang@mediatek.com>
 | 
					 | 
				
			||||||
+ * Author: Alexander Couzens <lynxis@fe80.eu>
 | 
					 | 
				
			||||||
+ * Author: Daniel Golle <daniel@makrotopia.org>
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#include <linux/mdio.h>
 | 
					 | 
				
			||||||
+#include <linux/of.h>
 | 
					 | 
				
			||||||
+#include <linux/pcs/pcs-mtk-lynxi.h>
 | 
					 | 
				
			||||||
+#include <linux/phylink.h>
 | 
					 | 
				
			||||||
+#include <linux/regmap.h>
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* SGMII subsystem config registers */
 | 
					 | 
				
			||||||
+/* BMCR (low 16) BMSR (high 16) */
 | 
					 | 
				
			||||||
+#define SGMSYS_PCS_CONTROL_1		0x0
 | 
					 | 
				
			||||||
+#define SGMII_BMCR			GENMASK(15, 0)
 | 
					 | 
				
			||||||
+#define SGMII_BMSR			GENMASK(31, 16)
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define SGMSYS_PCS_DEVICE_ID		0x4
 | 
					 | 
				
			||||||
+#define SGMII_LYNXI_DEV_ID		0x4d544950
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define SGMSYS_PCS_ADVERTISE		0x8
 | 
					 | 
				
			||||||
+#define SGMII_ADVERTISE			GENMASK(15, 0)
 | 
					 | 
				
			||||||
+#define SGMII_LPA			GENMASK(31, 16)
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define SGMSYS_PCS_SCRATCH		0x14
 | 
					 | 
				
			||||||
+#define SGMII_DEV_VERSION		GENMASK(31, 16)
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* Register to programmable link timer, the unit in 2 * 8ns */
 | 
					 | 
				
			||||||
+#define SGMSYS_PCS_LINK_TIMER		0x18
 | 
					 | 
				
			||||||
+#define SGMII_LINK_TIMER_MASK		GENMASK(19, 0)
 | 
					 | 
				
			||||||
+#define SGMII_LINK_TIMER_VAL(ns)	FIELD_PREP(SGMII_LINK_TIMER_MASK, \
 | 
					 | 
				
			||||||
+						   ((ns) / 2 / 8))
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* Register to control remote fault */
 | 
					 | 
				
			||||||
+#define SGMSYS_SGMII_MODE		0x20
 | 
					 | 
				
			||||||
+#define SGMII_IF_MODE_SGMII		BIT(0)
 | 
					 | 
				
			||||||
+#define SGMII_SPEED_DUPLEX_AN		BIT(1)
 | 
					 | 
				
			||||||
+#define SGMII_SPEED_MASK		GENMASK(3, 2)
 | 
					 | 
				
			||||||
+#define SGMII_SPEED_10			FIELD_PREP(SGMII_SPEED_MASK, 0)
 | 
					 | 
				
			||||||
+#define SGMII_SPEED_100			FIELD_PREP(SGMII_SPEED_MASK, 1)
 | 
					 | 
				
			||||||
+#define SGMII_SPEED_1000		FIELD_PREP(SGMII_SPEED_MASK, 2)
 | 
					 | 
				
			||||||
+#define SGMII_DUPLEX_HALF		BIT(4)
 | 
					 | 
				
			||||||
+#define SGMII_REMOTE_FAULT_DIS		BIT(8)
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* Register to reset SGMII design */
 | 
					 | 
				
			||||||
+#define SGMSYS_RESERVED_0		0x34
 | 
					 | 
				
			||||||
+#define SGMII_SW_RESET			BIT(0)
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* Register to set SGMII speed, ANA RG_ Control Signals III */
 | 
					 | 
				
			||||||
+#define SGMII_PHY_SPEED_MASK		GENMASK(3, 2)
 | 
					 | 
				
			||||||
+#define SGMII_PHY_SPEED_1_25G		FIELD_PREP(SGMII_PHY_SPEED_MASK, 0)
 | 
					 | 
				
			||||||
+#define SGMII_PHY_SPEED_3_125G		FIELD_PREP(SGMII_PHY_SPEED_MASK, 1)
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* Register to power up QPHY */
 | 
					 | 
				
			||||||
+#define SGMSYS_QPHY_PWR_STATE_CTRL	0xe8
 | 
					 | 
				
			||||||
+#define	SGMII_PHYA_PWD			BIT(4)
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* Register to QPHY wrapper control */
 | 
					 | 
				
			||||||
+#define SGMSYS_QPHY_WRAP_CTRL		0xec
 | 
					 | 
				
			||||||
+#define SGMII_PN_SWAP_MASK		GENMASK(1, 0)
 | 
					 | 
				
			||||||
+#define SGMII_PN_SWAP_TX_RX		(BIT(0) | BIT(1))
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* struct mtk_pcs_lynxi -  This structure holds each sgmii regmap andassociated
 | 
					 | 
				
			||||||
+ *                         data
 | 
					 | 
				
			||||||
+ * @regmap:                The register map pointing at the range used to setup
 | 
					 | 
				
			||||||
+ *                         SGMII modes
 | 
					 | 
				
			||||||
+ * @dev:                   Pointer to device owning the PCS
 | 
					 | 
				
			||||||
+ * @ana_rgc3:              The offset of register ANA_RGC3 relative to regmap
 | 
					 | 
				
			||||||
+ * @interface:             Currently configured interface mode
 | 
					 | 
				
			||||||
+ * @pcs:                   Phylink PCS structure
 | 
					 | 
				
			||||||
+ * @flags:                 Flags indicating hardware properties
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+struct mtk_pcs_lynxi {
 | 
					 | 
				
			||||||
+	struct regmap		*regmap;
 | 
					 | 
				
			||||||
+	u32			ana_rgc3;
 | 
					 | 
				
			||||||
+	phy_interface_t		interface;
 | 
					 | 
				
			||||||
+	struct			phylink_pcs pcs;
 | 
					 | 
				
			||||||
+	u32			flags;
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct mtk_pcs_lynxi *pcs_to_mtk_pcs_lynxi(struct phylink_pcs *pcs)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return container_of(pcs, struct mtk_pcs_lynxi, pcs);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void mtk_pcs_lynxi_get_state(struct phylink_pcs *pcs,
 | 
					 | 
				
			||||||
+				    struct phylink_link_state *state)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
 | 
					 | 
				
			||||||
+	unsigned int bm, adv;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* Read the BMSR and LPA */
 | 
					 | 
				
			||||||
+	regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm);
 | 
					 | 
				
			||||||
+	regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	phylink_mii_c22_pcs_decode_state(state, FIELD_GET(SGMII_BMSR, bm),
 | 
					 | 
				
			||||||
+					 FIELD_GET(SGMII_LPA, adv));
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int mode,
 | 
					 | 
				
			||||||
+				phy_interface_t interface,
 | 
					 | 
				
			||||||
+				const unsigned long *advertising,
 | 
					 | 
				
			||||||
+				bool permit_pause_to_mac)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
 | 
					 | 
				
			||||||
+	bool mode_changed = false, changed, use_an;
 | 
					 | 
				
			||||||
+	unsigned int rgc3, sgm_mode, bmcr;
 | 
					 | 
				
			||||||
+	int advertise, link_timer;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	advertise = phylink_mii_c22_pcs_encode_advertisement(interface,
 | 
					 | 
				
			||||||
+							     advertising);
 | 
					 | 
				
			||||||
+	if (advertise < 0)
 | 
					 | 
				
			||||||
+		return advertise;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* Clearing IF_MODE_BIT0 switches the PCS to BASE-X mode, and
 | 
					 | 
				
			||||||
+	 * we assume that fixes it's speed at bitrate = line rate (in
 | 
					 | 
				
			||||||
+	 * other words, 1000Mbps or 2500Mbps).
 | 
					 | 
				
			||||||
+	 */
 | 
					 | 
				
			||||||
+	if (interface == PHY_INTERFACE_MODE_SGMII) {
 | 
					 | 
				
			||||||
+		sgm_mode = SGMII_IF_MODE_SGMII;
 | 
					 | 
				
			||||||
+		if (phylink_autoneg_inband(mode)) {
 | 
					 | 
				
			||||||
+			sgm_mode |= SGMII_REMOTE_FAULT_DIS |
 | 
					 | 
				
			||||||
+				    SGMII_SPEED_DUPLEX_AN;
 | 
					 | 
				
			||||||
+			use_an = true;
 | 
					 | 
				
			||||||
+		} else {
 | 
					 | 
				
			||||||
+			use_an = false;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+	} else if (phylink_autoneg_inband(mode)) {
 | 
					 | 
				
			||||||
+		/* 1000base-X or 2500base-X autoneg */
 | 
					 | 
				
			||||||
+		sgm_mode = SGMII_REMOTE_FAULT_DIS;
 | 
					 | 
				
			||||||
+		use_an = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
 | 
					 | 
				
			||||||
+					   advertising);
 | 
					 | 
				
			||||||
+	} else {
 | 
					 | 
				
			||||||
+		/* 1000base-X or 2500base-X without autoneg */
 | 
					 | 
				
			||||||
+		sgm_mode = 0;
 | 
					 | 
				
			||||||
+		use_an = false;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (use_an)
 | 
					 | 
				
			||||||
+		bmcr = BMCR_ANENABLE;
 | 
					 | 
				
			||||||
+	else
 | 
					 | 
				
			||||||
+		bmcr = 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (mpcs->interface != interface) {
 | 
					 | 
				
			||||||
+		link_timer = phylink_get_link_timer_ns(interface);
 | 
					 | 
				
			||||||
+		if (link_timer < 0)
 | 
					 | 
				
			||||||
+			return link_timer;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		/* PHYA power down */
 | 
					 | 
				
			||||||
+		regmap_set_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
 | 
					 | 
				
			||||||
+				SGMII_PHYA_PWD);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		/* Reset SGMII PCS state */
 | 
					 | 
				
			||||||
+		regmap_set_bits(mpcs->regmap, SGMSYS_RESERVED_0,
 | 
					 | 
				
			||||||
+				SGMII_SW_RESET);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		if (mpcs->flags & MTK_SGMII_FLAG_PN_SWAP)
 | 
					 | 
				
			||||||
+			regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_WRAP_CTRL,
 | 
					 | 
				
			||||||
+					   SGMII_PN_SWAP_MASK,
 | 
					 | 
				
			||||||
+					   SGMII_PN_SWAP_TX_RX);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		if (interface == PHY_INTERFACE_MODE_2500BASEX)
 | 
					 | 
				
			||||||
+			rgc3 = SGMII_PHY_SPEED_3_125G;
 | 
					 | 
				
			||||||
+		else
 | 
					 | 
				
			||||||
+			rgc3 = SGMII_PHY_SPEED_1_25G;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		/* Configure the underlying interface speed */
 | 
					 | 
				
			||||||
+		regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
 | 
					 | 
				
			||||||
+				   SGMII_PHY_SPEED_MASK, rgc3);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		/* Setup the link timer */
 | 
					 | 
				
			||||||
+		regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
 | 
					 | 
				
			||||||
+			     SGMII_LINK_TIMER_VAL(link_timer));
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		mpcs->interface = interface;
 | 
					 | 
				
			||||||
+		mode_changed = true;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* Update the advertisement, noting whether it has changed */
 | 
					 | 
				
			||||||
+	regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE,
 | 
					 | 
				
			||||||
+				 SGMII_ADVERTISE, advertise, &changed);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* Update the sgmsys mode register */
 | 
					 | 
				
			||||||
+	regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
 | 
					 | 
				
			||||||
+			   SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN |
 | 
					 | 
				
			||||||
+			   SGMII_IF_MODE_SGMII, sgm_mode);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* Update the BMCR */
 | 
					 | 
				
			||||||
+	regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
 | 
					 | 
				
			||||||
+			   BMCR_ANENABLE, bmcr);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* Release PHYA power down state
 | 
					 | 
				
			||||||
+	 * Only removing bit SGMII_PHYA_PWD isn't enough.
 | 
					 | 
				
			||||||
+	 * There are cases when the SGMII_PHYA_PWD register contains 0x9 which
 | 
					 | 
				
			||||||
+	 * prevents SGMII from working. The SGMII still shows link but no traffic
 | 
					 | 
				
			||||||
+	 * can flow. Writing 0x0 to the PHYA_PWD register fix the issue. 0x0 was
 | 
					 | 
				
			||||||
+	 * taken from a good working state of the SGMII interface.
 | 
					 | 
				
			||||||
+	 * Unknown how much the QPHY needs but it is racy without a sleep.
 | 
					 | 
				
			||||||
+	 * Tested on mt7622 & mt7986.
 | 
					 | 
				
			||||||
+	 */
 | 
					 | 
				
			||||||
+	usleep_range(50, 100);
 | 
					 | 
				
			||||||
+	regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return changed || mode_changed;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void mtk_pcs_lynxi_restart_an(struct phylink_pcs *pcs)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	regmap_set_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, BMCR_ANRESTART);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void mtk_pcs_lynxi_link_up(struct phylink_pcs *pcs, unsigned int mode,
 | 
					 | 
				
			||||||
+				  phy_interface_t interface, int speed,
 | 
					 | 
				
			||||||
+				  int duplex)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
 | 
					 | 
				
			||||||
+	unsigned int sgm_mode;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (!phylink_autoneg_inband(mode)) {
 | 
					 | 
				
			||||||
+		/* Force the speed and duplex setting */
 | 
					 | 
				
			||||||
+		if (speed == SPEED_10)
 | 
					 | 
				
			||||||
+			sgm_mode = SGMII_SPEED_10;
 | 
					 | 
				
			||||||
+		else if (speed == SPEED_100)
 | 
					 | 
				
			||||||
+			sgm_mode = SGMII_SPEED_100;
 | 
					 | 
				
			||||||
+		else
 | 
					 | 
				
			||||||
+			sgm_mode = SGMII_SPEED_1000;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		if (duplex != DUPLEX_FULL)
 | 
					 | 
				
			||||||
+			sgm_mode |= SGMII_DUPLEX_HALF;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
 | 
					 | 
				
			||||||
+				   SGMII_DUPLEX_HALF | SGMII_SPEED_MASK,
 | 
					 | 
				
			||||||
+				   sgm_mode);
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static const struct phylink_pcs_ops mtk_pcs_lynxi_ops = {
 | 
					 | 
				
			||||||
+	.pcs_get_state = mtk_pcs_lynxi_get_state,
 | 
					 | 
				
			||||||
+	.pcs_config = mtk_pcs_lynxi_config,
 | 
					 | 
				
			||||||
+	.pcs_an_restart = mtk_pcs_lynxi_restart_an,
 | 
					 | 
				
			||||||
+	.pcs_link_up = mtk_pcs_lynxi_link_up,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev,
 | 
					 | 
				
			||||||
+					 struct regmap *regmap, u32 ana_rgc3,
 | 
					 | 
				
			||||||
+					 u32 flags)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct mtk_pcs_lynxi *mpcs;
 | 
					 | 
				
			||||||
+	u32 id, ver;
 | 
					 | 
				
			||||||
+	int ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ret = regmap_read(regmap, SGMSYS_PCS_DEVICE_ID, &id);
 | 
					 | 
				
			||||||
+	if (ret < 0)
 | 
					 | 
				
			||||||
+		return NULL;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (id != SGMII_LYNXI_DEV_ID) {
 | 
					 | 
				
			||||||
+		dev_err(dev, "unknown PCS device id %08x\n", id);
 | 
					 | 
				
			||||||
+		return NULL;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ret = regmap_read(regmap, SGMSYS_PCS_SCRATCH, &ver);
 | 
					 | 
				
			||||||
+	if (ret < 0)
 | 
					 | 
				
			||||||
+		return NULL;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ver = FIELD_GET(SGMII_DEV_VERSION, ver);
 | 
					 | 
				
			||||||
+	if (ver != 0x1) {
 | 
					 | 
				
			||||||
+		dev_err(dev, "unknown PCS device version %04x\n", ver);
 | 
					 | 
				
			||||||
+		return NULL;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	dev_dbg(dev, "MediaTek LynxI SGMII PCS (id 0x%08x, ver 0x%04x)\n", id,
 | 
					 | 
				
			||||||
+		ver);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL);
 | 
					 | 
				
			||||||
+	if (!mpcs)
 | 
					 | 
				
			||||||
+		return NULL;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	mpcs->ana_rgc3 = ana_rgc3;
 | 
					 | 
				
			||||||
+	mpcs->regmap = regmap;
 | 
					 | 
				
			||||||
+	mpcs->flags = flags;
 | 
					 | 
				
			||||||
+	mpcs->pcs.ops = &mtk_pcs_lynxi_ops;
 | 
					 | 
				
			||||||
+	mpcs->pcs.poll = true;
 | 
					 | 
				
			||||||
+	mpcs->interface = PHY_INTERFACE_MODE_NA;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return &mpcs->pcs;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL(mtk_pcs_lynxi_create);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	if (!pcs)
 | 
					 | 
				
			||||||
+		return;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	kfree(pcs_to_mtk_pcs_lynxi(pcs));
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL(mtk_pcs_lynxi_destroy);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+MODULE_LICENSE("GPL");
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/include/linux/pcs/pcs-mtk-lynxi.h
 | 
					 | 
				
			||||||
@@ -0,0 +1,13 @@
 | 
					 | 
				
			||||||
+/* SPDX-License-Identifier: GPL-2.0 */
 | 
					 | 
				
			||||||
+#ifndef __LINUX_PCS_MTK_LYNXI_H
 | 
					 | 
				
			||||||
+#define __LINUX_PCS_MTK_LYNXI_H
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#include <linux/phylink.h>
 | 
					 | 
				
			||||||
+#include <linux/regmap.h>
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define MTK_SGMII_FLAG_PN_SWAP BIT(0)
 | 
					 | 
				
			||||||
+struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev,
 | 
					 | 
				
			||||||
+					 struct regmap *regmap,
 | 
					 | 
				
			||||||
+					 u32 ana_rgc3, u32 flags);
 | 
					 | 
				
			||||||
+void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs);
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
@ -1,28 +0,0 @@
 | 
				
			|||||||
From ebb30ccbbdbd6fae5177b676da4f4ac92bb4f635 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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;
 | 
					 | 
				
			||||||
@ -1,341 +0,0 @@
 | 
				
			|||||||
From 9eea577eb1155fe4a183bc5e7bf269b0b2e7a6ba Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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,
 | 
					 | 
				
			||||||
@ -1,116 +0,0 @@
 | 
				
			|||||||
From 028672bd1d73cf65249a420c1de75e8d2acd2f6a Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@ -1,196 +0,0 @@
 | 
				
			|||||||
From d63710fc0f1a501fd75a7025e3070a96ffa1645f Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Christian Marangi <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
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 <ansuelsmth@gmail.com>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 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)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
@ -1,103 +0,0 @@
 | 
				
			|||||||
From affa013f494486079c3c5ad2d00cebc41a3d7445 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Sean Anderson <sean.anderson@seco.com>
 | 
					 | 
				
			||||||
Date: Mon, 17 Oct 2022 16:22:36 -0400
 | 
					 | 
				
			||||||
Subject: [PATCH 01/21] net: fman: memac: Add serdes support
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This adds support for using a serdes which has to be configured. This is
 | 
					 | 
				
			||||||
primarly in preparation for phylink conversion, which will then change the
 | 
					 | 
				
			||||||
serdes mode dynamically.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Sean Anderson <sean.anderson@seco.com>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 .../net/ethernet/freescale/fman/fman_memac.c  | 49 ++++++++++++++++++-
 | 
					 | 
				
			||||||
 1 file changed, 47 insertions(+), 2 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/ethernet/freescale/fman/fman_memac.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/ethernet/freescale/fman/fman_memac.c
 | 
					 | 
				
			||||||
@@ -13,6 +13,7 @@
 | 
					 | 
				
			||||||
 #include <linux/io.h>
 | 
					 | 
				
			||||||
 #include <linux/phy.h>
 | 
					 | 
				
			||||||
 #include <linux/phy_fixed.h>
 | 
					 | 
				
			||||||
+#include <linux/phy/phy.h>
 | 
					 | 
				
			||||||
 #include <linux/of_mdio.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /* PCS registers */
 | 
					 | 
				
			||||||
@@ -324,6 +325,7 @@ struct fman_mac {
 | 
					 | 
				
			||||||
 	void *fm;
 | 
					 | 
				
			||||||
 	struct fman_rev_info fm_rev_info;
 | 
					 | 
				
			||||||
 	bool basex_if;
 | 
					 | 
				
			||||||
+	struct phy *serdes;
 | 
					 | 
				
			||||||
 	struct phy_device *pcsphy;
 | 
					 | 
				
			||||||
 	bool allmulti_enabled;
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
@@ -1203,17 +1205,56 @@ int memac_initialization(struct mac_devi
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	memac->serdes = devm_of_phy_get(mac_dev->dev, mac_node, "serdes");
 | 
					 | 
				
			||||||
+	err = PTR_ERR(memac->serdes);
 | 
					 | 
				
			||||||
+	if (err == -ENODEV || err == -ENOSYS) {
 | 
					 | 
				
			||||||
+		dev_dbg(mac_dev->dev, "could not get (optional) serdes\n");
 | 
					 | 
				
			||||||
+		memac->serdes = NULL;
 | 
					 | 
				
			||||||
+	} else if (IS_ERR(memac->serdes)) {
 | 
					 | 
				
			||||||
+		dev_err_probe(mac_dev->dev, err, "could not get serdes\n");
 | 
					 | 
				
			||||||
+		goto _return_fm_mac_free;
 | 
					 | 
				
			||||||
+	} else {
 | 
					 | 
				
			||||||
+		err = phy_init(memac->serdes);
 | 
					 | 
				
			||||||
+		if (err) {
 | 
					 | 
				
			||||||
+			dev_err_probe(mac_dev->dev, err,
 | 
					 | 
				
			||||||
+				      "could not initialize serdes\n");
 | 
					 | 
				
			||||||
+			goto _return_fm_mac_free;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		err = phy_power_on(memac->serdes);
 | 
					 | 
				
			||||||
+		if (err) {
 | 
					 | 
				
			||||||
+			dev_err_probe(mac_dev->dev, err,
 | 
					 | 
				
			||||||
+				      "could not power on serdes\n");
 | 
					 | 
				
			||||||
+			goto _return_phy_exit;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		if (memac->phy_if == PHY_INTERFACE_MODE_SGMII ||
 | 
					 | 
				
			||||||
+		    memac->phy_if == PHY_INTERFACE_MODE_1000BASEX ||
 | 
					 | 
				
			||||||
+		    memac->phy_if == PHY_INTERFACE_MODE_2500BASEX ||
 | 
					 | 
				
			||||||
+		    memac->phy_if == PHY_INTERFACE_MODE_QSGMII ||
 | 
					 | 
				
			||||||
+		    memac->phy_if == PHY_INTERFACE_MODE_XGMII) {
 | 
					 | 
				
			||||||
+			err = phy_set_mode_ext(memac->serdes, PHY_MODE_ETHERNET,
 | 
					 | 
				
			||||||
+					       memac->phy_if);
 | 
					 | 
				
			||||||
+			if (err) {
 | 
					 | 
				
			||||||
+				dev_err_probe(mac_dev->dev, err,
 | 
					 | 
				
			||||||
+					      "could not set serdes mode to %s\n",
 | 
					 | 
				
			||||||
+					      phy_modes(memac->phy_if));
 | 
					 | 
				
			||||||
+				goto _return_phy_power_off;
 | 
					 | 
				
			||||||
+			}
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	if (!mac_dev->phy_node && of_phy_is_fixed_link(mac_node)) {
 | 
					 | 
				
			||||||
 		struct phy_device *phy;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 		err = of_phy_register_fixed_link(mac_node);
 | 
					 | 
				
			||||||
 		if (err)
 | 
					 | 
				
			||||||
-			goto _return_fm_mac_free;
 | 
					 | 
				
			||||||
+			goto _return_phy_power_off;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 		fixed_link = kzalloc(sizeof(*fixed_link), GFP_KERNEL);
 | 
					 | 
				
			||||||
 		if (!fixed_link) {
 | 
					 | 
				
			||||||
 			err = -ENOMEM;
 | 
					 | 
				
			||||||
-			goto _return_fm_mac_free;
 | 
					 | 
				
			||||||
+			goto _return_phy_power_off;
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 		mac_dev->phy_node = of_node_get(mac_node);
 | 
					 | 
				
			||||||
@@ -1242,6 +1283,10 @@ int memac_initialization(struct mac_devi
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	goto _return;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+_return_phy_power_off:
 | 
					 | 
				
			||||||
+	phy_power_off(memac->serdes);
 | 
					 | 
				
			||||||
+_return_phy_exit:
 | 
					 | 
				
			||||||
+	phy_exit(memac->serdes);
 | 
					 | 
				
			||||||
 _return_fixed_link_free:
 | 
					 | 
				
			||||||
 	kfree(fixed_link);
 | 
					 | 
				
			||||||
 _return_fm_mac_free:
 | 
					 | 
				
			||||||
@ -1,384 +0,0 @@
 | 
				
			|||||||
From fe60e7154d3a35af975c5e6570d6ec31aab9a731 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Sean Anderson <sean.anderson@seco.com>
 | 
					 | 
				
			||||||
Date: Mon, 17 Oct 2022 16:22:37 -0400
 | 
					 | 
				
			||||||
Subject: [PATCH 02/21] net: fman: memac: Use lynx pcs driver
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Although not stated in the datasheet, as far as I can tell PCS for mEMACs
 | 
					 | 
				
			||||||
is a "Lynx." By reusing the existing driver, we can remove the PCS
 | 
					 | 
				
			||||||
management code from the memac driver. This requires calling some PCS
 | 
					 | 
				
			||||||
functions manually which phylink would usually do for us, but we will let
 | 
					 | 
				
			||||||
it do that soon.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
One problem is that we don't actually have a PCS for QSGMII. We pretend
 | 
					 | 
				
			||||||
that each mEMAC's MDIO bus has four QSGMII PCSs, but this is not the case.
 | 
					 | 
				
			||||||
Only the "base" mEMAC's MDIO bus has the four QSGMII PCSs. This is not an
 | 
					 | 
				
			||||||
issue yet, because we never get the PCS state. However, it will be once the
 | 
					 | 
				
			||||||
conversion to phylink is complete, since the links will appear to never
 | 
					 | 
				
			||||||
come up. To get around this, we allow specifying multiple PCSs in pcsphy.
 | 
					 | 
				
			||||||
This breaks backwards compatibility with old device trees, but only for
 | 
					 | 
				
			||||||
QSGMII. IMO this is the only reasonable way to figure out what the actual
 | 
					 | 
				
			||||||
QSGMII PCS is.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Additionally, we now also support a separate XFI PCS. This can allow the
 | 
					 | 
				
			||||||
SerDes driver to set different addresses for the SGMII and XFI PCSs so they
 | 
					 | 
				
			||||||
can be accessed at the same time.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Sean Anderson <sean.anderson@seco.com>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/ethernet/freescale/fman/Kconfig   |   3 +
 | 
					 | 
				
			||||||
 .../net/ethernet/freescale/fman/fman_memac.c  | 258 +++++++-----------
 | 
					 | 
				
			||||||
 2 files changed, 105 insertions(+), 156 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/ethernet/freescale/fman/Kconfig
 | 
					 | 
				
			||||||
+++ b/drivers/net/ethernet/freescale/fman/Kconfig
 | 
					 | 
				
			||||||
@@ -4,6 +4,9 @@ config FSL_FMAN
 | 
					 | 
				
			||||||
 	depends on FSL_SOC || ARCH_LAYERSCAPE || COMPILE_TEST
 | 
					 | 
				
			||||||
 	select GENERIC_ALLOCATOR
 | 
					 | 
				
			||||||
 	select PHYLIB
 | 
					 | 
				
			||||||
+	select PHYLINK
 | 
					 | 
				
			||||||
+	select PCS
 | 
					 | 
				
			||||||
+	select PCS_LYNX
 | 
					 | 
				
			||||||
 	select CRC32
 | 
					 | 
				
			||||||
 	default n
 | 
					 | 
				
			||||||
 	help
 | 
					 | 
				
			||||||
--- a/drivers/net/ethernet/freescale/fman/fman_memac.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/ethernet/freescale/fman/fman_memac.c
 | 
					 | 
				
			||||||
@@ -11,43 +11,12 @@
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #include <linux/slab.h>
 | 
					 | 
				
			||||||
 #include <linux/io.h>
 | 
					 | 
				
			||||||
+#include <linux/pcs-lynx.h>
 | 
					 | 
				
			||||||
 #include <linux/phy.h>
 | 
					 | 
				
			||||||
 #include <linux/phy_fixed.h>
 | 
					 | 
				
			||||||
 #include <linux/phy/phy.h>
 | 
					 | 
				
			||||||
 #include <linux/of_mdio.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-/* PCS registers */
 | 
					 | 
				
			||||||
-#define MDIO_SGMII_CR			0x00
 | 
					 | 
				
			||||||
-#define MDIO_SGMII_DEV_ABIL_SGMII	0x04
 | 
					 | 
				
			||||||
-#define MDIO_SGMII_LINK_TMR_L		0x12
 | 
					 | 
				
			||||||
-#define MDIO_SGMII_LINK_TMR_H		0x13
 | 
					 | 
				
			||||||
-#define MDIO_SGMII_IF_MODE		0x14
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-/* SGMII Control defines */
 | 
					 | 
				
			||||||
-#define SGMII_CR_AN_EN			0x1000
 | 
					 | 
				
			||||||
-#define SGMII_CR_RESTART_AN		0x0200
 | 
					 | 
				
			||||||
-#define SGMII_CR_FD			0x0100
 | 
					 | 
				
			||||||
-#define SGMII_CR_SPEED_SEL1_1G		0x0040
 | 
					 | 
				
			||||||
-#define SGMII_CR_DEF_VAL		(SGMII_CR_AN_EN | SGMII_CR_FD | \
 | 
					 | 
				
			||||||
-					 SGMII_CR_SPEED_SEL1_1G)
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-/* SGMII Device Ability for SGMII defines */
 | 
					 | 
				
			||||||
-#define MDIO_SGMII_DEV_ABIL_SGMII_MODE	0x4001
 | 
					 | 
				
			||||||
-#define MDIO_SGMII_DEV_ABIL_BASEX_MODE	0x01A0
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-/* Link timer define */
 | 
					 | 
				
			||||||
-#define LINK_TMR_L			0xa120
 | 
					 | 
				
			||||||
-#define LINK_TMR_H			0x0007
 | 
					 | 
				
			||||||
-#define LINK_TMR_L_BASEX		0xaf08
 | 
					 | 
				
			||||||
-#define LINK_TMR_H_BASEX		0x002f
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-/* SGMII IF Mode defines */
 | 
					 | 
				
			||||||
-#define IF_MODE_USE_SGMII_AN		0x0002
 | 
					 | 
				
			||||||
-#define IF_MODE_SGMII_EN		0x0001
 | 
					 | 
				
			||||||
-#define IF_MODE_SGMII_SPEED_100M	0x0004
 | 
					 | 
				
			||||||
-#define IF_MODE_SGMII_SPEED_1G		0x0008
 | 
					 | 
				
			||||||
-#define IF_MODE_SGMII_DUPLEX_HALF	0x0010
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 /* Num of additional exact match MAC adr regs */
 | 
					 | 
				
			||||||
 #define MEMAC_NUM_OF_PADDRS 7
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -326,7 +295,9 @@ struct fman_mac {
 | 
					 | 
				
			||||||
 	struct fman_rev_info fm_rev_info;
 | 
					 | 
				
			||||||
 	bool basex_if;
 | 
					 | 
				
			||||||
 	struct phy *serdes;
 | 
					 | 
				
			||||||
-	struct phy_device *pcsphy;
 | 
					 | 
				
			||||||
+	struct phylink_pcs *sgmii_pcs;
 | 
					 | 
				
			||||||
+	struct phylink_pcs *qsgmii_pcs;
 | 
					 | 
				
			||||||
+	struct phylink_pcs *xfi_pcs;
 | 
					 | 
				
			||||||
 	bool allmulti_enabled;
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -487,91 +458,22 @@ static u32 get_mac_addr_hash_code(u64 et
 | 
					 | 
				
			||||||
 	return xor_val;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-static void setup_sgmii_internal_phy(struct fman_mac *memac,
 | 
					 | 
				
			||||||
-				     struct fixed_phy_status *fixed_link)
 | 
					 | 
				
			||||||
-{
 | 
					 | 
				
			||||||
-	u16 tmp_reg16;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	if (WARN_ON(!memac->pcsphy))
 | 
					 | 
				
			||||||
-		return;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	/* SGMII mode */
 | 
					 | 
				
			||||||
-	tmp_reg16 = IF_MODE_SGMII_EN;
 | 
					 | 
				
			||||||
-	if (!fixed_link)
 | 
					 | 
				
			||||||
-		/* AN enable */
 | 
					 | 
				
			||||||
-		tmp_reg16 |= IF_MODE_USE_SGMII_AN;
 | 
					 | 
				
			||||||
-	else {
 | 
					 | 
				
			||||||
-		switch (fixed_link->speed) {
 | 
					 | 
				
			||||||
-		case 10:
 | 
					 | 
				
			||||||
-			/* For 10M: IF_MODE[SPEED_10M] = 0 */
 | 
					 | 
				
			||||||
-		break;
 | 
					 | 
				
			||||||
-		case 100:
 | 
					 | 
				
			||||||
-			tmp_reg16 |= IF_MODE_SGMII_SPEED_100M;
 | 
					 | 
				
			||||||
-		break;
 | 
					 | 
				
			||||||
-		case 1000:
 | 
					 | 
				
			||||||
-		default:
 | 
					 | 
				
			||||||
-			tmp_reg16 |= IF_MODE_SGMII_SPEED_1G;
 | 
					 | 
				
			||||||
-		break;
 | 
					 | 
				
			||||||
-		}
 | 
					 | 
				
			||||||
-		if (!fixed_link->duplex)
 | 
					 | 
				
			||||||
-			tmp_reg16 |= IF_MODE_SGMII_DUPLEX_HALF;
 | 
					 | 
				
			||||||
-	}
 | 
					 | 
				
			||||||
-	phy_write(memac->pcsphy, MDIO_SGMII_IF_MODE, tmp_reg16);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	/* Device ability according to SGMII specification */
 | 
					 | 
				
			||||||
-	tmp_reg16 = MDIO_SGMII_DEV_ABIL_SGMII_MODE;
 | 
					 | 
				
			||||||
-	phy_write(memac->pcsphy, MDIO_SGMII_DEV_ABIL_SGMII, tmp_reg16);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	/* Adjust link timer for SGMII  -
 | 
					 | 
				
			||||||
-	 * According to Cisco SGMII specification the timer should be 1.6 ms.
 | 
					 | 
				
			||||||
-	 * The link_timer register is configured in units of the clock.
 | 
					 | 
				
			||||||
-	 * - When running as 1G SGMII, Serdes clock is 125 MHz, so
 | 
					 | 
				
			||||||
-	 * unit = 1 / (125*10^6 Hz) = 8 ns.
 | 
					 | 
				
			||||||
-	 * 1.6 ms in units of 8 ns = 1.6ms / 8ns = 2*10^5 = 0x30d40
 | 
					 | 
				
			||||||
-	 * - When running as 2.5G SGMII, Serdes clock is 312.5 MHz, so
 | 
					 | 
				
			||||||
-	 * unit = 1 / (312.5*10^6 Hz) = 3.2 ns.
 | 
					 | 
				
			||||||
-	 * 1.6 ms in units of 3.2 ns = 1.6ms / 3.2ns = 5*10^5 = 0x7a120.
 | 
					 | 
				
			||||||
-	 * Since link_timer value of 1G SGMII will be too short for 2.5 SGMII,
 | 
					 | 
				
			||||||
-	 * we always set up here a value of 2.5 SGMII.
 | 
					 | 
				
			||||||
-	 */
 | 
					 | 
				
			||||||
-	phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_H, LINK_TMR_H);
 | 
					 | 
				
			||||||
-	phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_L, LINK_TMR_L);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	if (!fixed_link)
 | 
					 | 
				
			||||||
-		/* Restart AN */
 | 
					 | 
				
			||||||
-		tmp_reg16 = SGMII_CR_DEF_VAL | SGMII_CR_RESTART_AN;
 | 
					 | 
				
			||||||
+static void setup_sgmii_internal(struct fman_mac *memac,
 | 
					 | 
				
			||||||
+				 struct phylink_pcs *pcs,
 | 
					 | 
				
			||||||
+				 struct fixed_phy_status *fixed_link)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	__ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
 | 
					 | 
				
			||||||
+	phy_interface_t iface = memac->basex_if ? PHY_INTERFACE_MODE_1000BASEX :
 | 
					 | 
				
			||||||
+				PHY_INTERFACE_MODE_SGMII;
 | 
					 | 
				
			||||||
+	unsigned int mode = fixed_link ? MLO_AN_FIXED : MLO_AN_INBAND;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	linkmode_set_pause(advertising, true, true);
 | 
					 | 
				
			||||||
+	pcs->ops->pcs_config(pcs, mode, iface, advertising, true);
 | 
					 | 
				
			||||||
+	if (fixed_link)
 | 
					 | 
				
			||||||
+		pcs->ops->pcs_link_up(pcs, mode, iface, fixed_link->speed,
 | 
					 | 
				
			||||||
+				      fixed_link->duplex);
 | 
					 | 
				
			||||||
 	else
 | 
					 | 
				
			||||||
-		/* AN disabled */
 | 
					 | 
				
			||||||
-		tmp_reg16 = SGMII_CR_DEF_VAL & ~SGMII_CR_AN_EN;
 | 
					 | 
				
			||||||
-	phy_write(memac->pcsphy, 0x0, tmp_reg16);
 | 
					 | 
				
			||||||
-}
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-static void setup_sgmii_internal_phy_base_x(struct fman_mac *memac)
 | 
					 | 
				
			||||||
-{
 | 
					 | 
				
			||||||
-	u16 tmp_reg16;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	/* AN Device capability  */
 | 
					 | 
				
			||||||
-	tmp_reg16 = MDIO_SGMII_DEV_ABIL_BASEX_MODE;
 | 
					 | 
				
			||||||
-	phy_write(memac->pcsphy, MDIO_SGMII_DEV_ABIL_SGMII, tmp_reg16);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	/* Adjust link timer for SGMII  -
 | 
					 | 
				
			||||||
-	 * For Serdes 1000BaseX auto-negotiation the timer should be 10 ms.
 | 
					 | 
				
			||||||
-	 * The link_timer register is configured in units of the clock.
 | 
					 | 
				
			||||||
-	 * - When running as 1G SGMII, Serdes clock is 125 MHz, so
 | 
					 | 
				
			||||||
-	 * unit = 1 / (125*10^6 Hz) = 8 ns.
 | 
					 | 
				
			||||||
-	 * 10 ms in units of 8 ns = 10ms / 8ns = 1250000 = 0x1312d0
 | 
					 | 
				
			||||||
-	 * - When running as 2.5G SGMII, Serdes clock is 312.5 MHz, so
 | 
					 | 
				
			||||||
-	 * unit = 1 / (312.5*10^6 Hz) = 3.2 ns.
 | 
					 | 
				
			||||||
-	 * 10 ms in units of 3.2 ns = 10ms / 3.2ns = 3125000 = 0x2faf08.
 | 
					 | 
				
			||||||
-	 * Since link_timer value of 1G SGMII will be too short for 2.5 SGMII,
 | 
					 | 
				
			||||||
-	 * we always set up here a value of 2.5 SGMII.
 | 
					 | 
				
			||||||
-	 */
 | 
					 | 
				
			||||||
-	phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_H, LINK_TMR_H_BASEX);
 | 
					 | 
				
			||||||
-	phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_L, LINK_TMR_L_BASEX);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	/* Restart AN */
 | 
					 | 
				
			||||||
-	tmp_reg16 = SGMII_CR_DEF_VAL | SGMII_CR_RESTART_AN;
 | 
					 | 
				
			||||||
-	phy_write(memac->pcsphy, 0x0, tmp_reg16);
 | 
					 | 
				
			||||||
+		pcs->ops->pcs_an_restart(pcs);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static int check_init_parameters(struct fman_mac *memac)
 | 
					 | 
				
			||||||
@@ -983,7 +885,6 @@ static int memac_set_exception(struct fm
 | 
					 | 
				
			||||||
 static int memac_init(struct fman_mac *memac)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	struct memac_cfg *memac_drv_param;
 | 
					 | 
				
			||||||
-	u8 i;
 | 
					 | 
				
			||||||
 	enet_addr_t eth_addr;
 | 
					 | 
				
			||||||
 	bool slow_10g_if = false;
 | 
					 | 
				
			||||||
 	struct fixed_phy_status *fixed_link = NULL;
 | 
					 | 
				
			||||||
@@ -1036,32 +937,10 @@ static int memac_init(struct fman_mac *m
 | 
					 | 
				
			||||||
 		iowrite32be(reg32, &memac->regs->command_config);
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if (memac->phy_if == PHY_INTERFACE_MODE_SGMII) {
 | 
					 | 
				
			||||||
-		/* Configure internal SGMII PHY */
 | 
					 | 
				
			||||||
-		if (memac->basex_if)
 | 
					 | 
				
			||||||
-			setup_sgmii_internal_phy_base_x(memac);
 | 
					 | 
				
			||||||
-		else
 | 
					 | 
				
			||||||
-			setup_sgmii_internal_phy(memac, fixed_link);
 | 
					 | 
				
			||||||
-	} else if (memac->phy_if == PHY_INTERFACE_MODE_QSGMII) {
 | 
					 | 
				
			||||||
-		/* Configure 4 internal SGMII PHYs */
 | 
					 | 
				
			||||||
-		for (i = 0; i < 4; i++) {
 | 
					 | 
				
			||||||
-			u8 qsmgii_phy_addr, phy_addr;
 | 
					 | 
				
			||||||
-			/* QSGMII PHY address occupies 3 upper bits of 5-bit
 | 
					 | 
				
			||||||
-			 * phy_address; the lower 2 bits are used to extend
 | 
					 | 
				
			||||||
-			 * register address space and access each one of 4
 | 
					 | 
				
			||||||
-			 * ports inside QSGMII.
 | 
					 | 
				
			||||||
-			 */
 | 
					 | 
				
			||||||
-			phy_addr = memac->pcsphy->mdio.addr;
 | 
					 | 
				
			||||||
-			qsmgii_phy_addr = (u8)((phy_addr << 2) | i);
 | 
					 | 
				
			||||||
-			memac->pcsphy->mdio.addr = qsmgii_phy_addr;
 | 
					 | 
				
			||||||
-			if (memac->basex_if)
 | 
					 | 
				
			||||||
-				setup_sgmii_internal_phy_base_x(memac);
 | 
					 | 
				
			||||||
-			else
 | 
					 | 
				
			||||||
-				setup_sgmii_internal_phy(memac, fixed_link);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-			memac->pcsphy->mdio.addr = phy_addr;
 | 
					 | 
				
			||||||
-		}
 | 
					 | 
				
			||||||
-	}
 | 
					 | 
				
			||||||
+	if (memac->phy_if == PHY_INTERFACE_MODE_SGMII)
 | 
					 | 
				
			||||||
+		setup_sgmii_internal(memac, memac->sgmii_pcs, fixed_link);
 | 
					 | 
				
			||||||
+	else if (memac->phy_if == PHY_INTERFACE_MODE_QSGMII)
 | 
					 | 
				
			||||||
+		setup_sgmii_internal(memac, memac->qsgmii_pcs, fixed_link);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* Max Frame Length */
 | 
					 | 
				
			||||||
 	err = fman_set_mac_max_frame(memac->fm, memac->mac_id,
 | 
					 | 
				
			||||||
@@ -1097,12 +976,25 @@ static int memac_init(struct fman_mac *m
 | 
					 | 
				
			||||||
 	return 0;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static void pcs_put(struct phylink_pcs *pcs)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct mdio_device *mdiodev;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (IS_ERR_OR_NULL(pcs))
 | 
					 | 
				
			||||||
+		return;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	mdiodev = lynx_get_mdio_device(pcs);
 | 
					 | 
				
			||||||
+	lynx_pcs_destroy(pcs);
 | 
					 | 
				
			||||||
+	mdio_device_free(mdiodev);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static int memac_free(struct fman_mac *memac)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	free_init_resources(memac);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if (memac->pcsphy)
 | 
					 | 
				
			||||||
-		put_device(&memac->pcsphy->mdio.dev);
 | 
					 | 
				
			||||||
+	pcs_put(memac->sgmii_pcs);
 | 
					 | 
				
			||||||
+	pcs_put(memac->qsgmii_pcs);
 | 
					 | 
				
			||||||
+	pcs_put(memac->xfi_pcs);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	kfree(memac->memac_drv_param);
 | 
					 | 
				
			||||||
 	kfree(memac);
 | 
					 | 
				
			||||||
@@ -1153,12 +1045,31 @@ static struct fman_mac *memac_config(str
 | 
					 | 
				
			||||||
 	return memac;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static struct phylink_pcs *memac_pcs_create(struct device_node *mac_node,
 | 
					 | 
				
			||||||
+					    int index)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct device_node *node;
 | 
					 | 
				
			||||||
+	struct mdio_device *mdiodev = NULL;
 | 
					 | 
				
			||||||
+	struct phylink_pcs *pcs;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	node = of_parse_phandle(mac_node, "pcsphy-handle", index);
 | 
					 | 
				
			||||||
+	if (node && of_device_is_available(node))
 | 
					 | 
				
			||||||
+		mdiodev = of_mdio_find_device(node);
 | 
					 | 
				
			||||||
+	of_node_put(node);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (!mdiodev)
 | 
					 | 
				
			||||||
+		return ERR_PTR(-EPROBE_DEFER);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	pcs = lynx_pcs_create(mdiodev);
 | 
					 | 
				
			||||||
+	return pcs;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 int memac_initialization(struct mac_device *mac_dev,
 | 
					 | 
				
			||||||
 			 struct device_node *mac_node,
 | 
					 | 
				
			||||||
 			 struct fman_mac_params *params)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	int			 err;
 | 
					 | 
				
			||||||
-	struct device_node	*phy_node;
 | 
					 | 
				
			||||||
+	struct phylink_pcs	*pcs;
 | 
					 | 
				
			||||||
 	struct fixed_phy_status *fixed_link;
 | 
					 | 
				
			||||||
 	struct fman_mac		*memac;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -1188,23 +1099,58 @@ int memac_initialization(struct mac_devi
 | 
					 | 
				
			||||||
 	memac = mac_dev->fman_mac;
 | 
					 | 
				
			||||||
 	memac->memac_drv_param->max_frame_length = fman_get_max_frm();
 | 
					 | 
				
			||||||
 	memac->memac_drv_param->reset_on_init = true;
 | 
					 | 
				
			||||||
-	if (memac->phy_if == PHY_INTERFACE_MODE_SGMII ||
 | 
					 | 
				
			||||||
-	    memac->phy_if == PHY_INTERFACE_MODE_QSGMII) {
 | 
					 | 
				
			||||||
-		phy_node = of_parse_phandle(mac_node, "pcsphy-handle", 0);
 | 
					 | 
				
			||||||
-		if (!phy_node) {
 | 
					 | 
				
			||||||
-			pr_err("PCS PHY node is not available\n");
 | 
					 | 
				
			||||||
-			err = -EINVAL;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	err = of_property_match_string(mac_node, "pcs-handle-names", "xfi");
 | 
					 | 
				
			||||||
+	if (err >= 0) {
 | 
					 | 
				
			||||||
+		memac->xfi_pcs = memac_pcs_create(mac_node, err);
 | 
					 | 
				
			||||||
+		if (IS_ERR(memac->xfi_pcs)) {
 | 
					 | 
				
			||||||
+			err = PTR_ERR(memac->xfi_pcs);
 | 
					 | 
				
			||||||
+			dev_err_probe(mac_dev->dev, err, "missing xfi pcs\n");
 | 
					 | 
				
			||||||
 			goto _return_fm_mac_free;
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
+	} else if (err != -EINVAL && err != -ENODATA) {
 | 
					 | 
				
			||||||
+		goto _return_fm_mac_free;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-		memac->pcsphy = of_phy_find_device(phy_node);
 | 
					 | 
				
			||||||
-		if (!memac->pcsphy) {
 | 
					 | 
				
			||||||
-			pr_err("of_phy_find_device (PCS PHY) failed\n");
 | 
					 | 
				
			||||||
-			err = -EINVAL;
 | 
					 | 
				
			||||||
+	err = of_property_match_string(mac_node, "pcs-handle-names", "qsgmii");
 | 
					 | 
				
			||||||
+	if (err >= 0) {
 | 
					 | 
				
			||||||
+		memac->qsgmii_pcs = memac_pcs_create(mac_node, err);
 | 
					 | 
				
			||||||
+		if (IS_ERR(memac->qsgmii_pcs)) {
 | 
					 | 
				
			||||||
+			err = PTR_ERR(memac->qsgmii_pcs);
 | 
					 | 
				
			||||||
+			dev_err_probe(mac_dev->dev, err,
 | 
					 | 
				
			||||||
+				      "missing qsgmii pcs\n");
 | 
					 | 
				
			||||||
 			goto _return_fm_mac_free;
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
+	} else if (err != -EINVAL && err != -ENODATA) {
 | 
					 | 
				
			||||||
+		goto _return_fm_mac_free;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* For compatibility, if pcs-handle-names is missing, we assume this
 | 
					 | 
				
			||||||
+	 * phy is the first one in pcsphy-handle
 | 
					 | 
				
			||||||
+	 */
 | 
					 | 
				
			||||||
+	err = of_property_match_string(mac_node, "pcs-handle-names", "sgmii");
 | 
					 | 
				
			||||||
+	if (err == -EINVAL || err == -ENODATA)
 | 
					 | 
				
			||||||
+		pcs = memac_pcs_create(mac_node, 0);
 | 
					 | 
				
			||||||
+	else if (err < 0)
 | 
					 | 
				
			||||||
+		goto _return_fm_mac_free;
 | 
					 | 
				
			||||||
+	else
 | 
					 | 
				
			||||||
+		pcs = memac_pcs_create(mac_node, err);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (!pcs) {
 | 
					 | 
				
			||||||
+		dev_err(mac_dev->dev, "missing pcs\n");
 | 
					 | 
				
			||||||
+		err = -ENOENT;
 | 
					 | 
				
			||||||
+		goto _return_fm_mac_free;
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	/* If err is set here, it means that pcs-handle-names was missing above
 | 
					 | 
				
			||||||
+	 * (and therefore that xfi_pcs cannot be set). If we are defaulting to
 | 
					 | 
				
			||||||
+	 * XGMII, assume this is for XFI. Otherwise, assume it is for SGMII.
 | 
					 | 
				
			||||||
+	 */
 | 
					 | 
				
			||||||
+	if (err && mac_dev->phy_if == PHY_INTERFACE_MODE_XGMII)
 | 
					 | 
				
			||||||
+		memac->xfi_pcs = pcs;
 | 
					 | 
				
			||||||
+	else
 | 
					 | 
				
			||||||
+		memac->sgmii_pcs = pcs;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	memac->serdes = devm_of_phy_get(mac_dev->dev, mac_node, "serdes");
 | 
					 | 
				
			||||||
 	err = PTR_ERR(memac->serdes);
 | 
					 | 
				
			||||||
 	if (err == -ENODEV || err == -ENOSYS) {
 | 
					 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1,93 +0,0 @@
 | 
				
			|||||||
From bf4de031052fe7c5309e8956c342d4e5ce79038e Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Date: Mon, 17 Oct 2022 16:22:35 -0400
 | 
					 | 
				
			||||||
Subject: [PATCH 04/21] net: phylink: provide phylink_validate_mask_caps()
 | 
					 | 
				
			||||||
 helper
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Provide a helper that restricts the link modes according to the
 | 
					 | 
				
			||||||
phylink capabilities.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
[rebased on net-next/master and added documentation]
 | 
					 | 
				
			||||||
Signed-off-by: Sean Anderson <sean.anderson@seco.com>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/phy/phylink.c | 41 +++++++++++++++++++++++++++------------
 | 
					 | 
				
			||||||
 include/linux/phylink.h   |  3 +++
 | 
					 | 
				
			||||||
 2 files changed, 32 insertions(+), 12 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/phylink.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/phylink.c
 | 
					 | 
				
			||||||
@@ -564,31 +564,48 @@ unsigned long phylink_get_capabilities(p
 | 
					 | 
				
			||||||
 EXPORT_SYMBOL_GPL(phylink_get_capabilities);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /**
 | 
					 | 
				
			||||||
- * phylink_generic_validate() - generic validate() callback implementation
 | 
					 | 
				
			||||||
- * @config: a pointer to a &struct phylink_config.
 | 
					 | 
				
			||||||
+ * phylink_validate_mask_caps() - Restrict link modes based on caps
 | 
					 | 
				
			||||||
  * @supported: ethtool bitmask for supported link modes.
 | 
					 | 
				
			||||||
- * @state: a pointer to a &struct phylink_link_state.
 | 
					 | 
				
			||||||
+ * @state: an (optional) pointer to a &struct phylink_link_state.
 | 
					 | 
				
			||||||
+ * @mac_capabilities: bitmask of MAC capabilities
 | 
					 | 
				
			||||||
  *
 | 
					 | 
				
			||||||
- * Generic implementation of the validate() callback that MAC drivers can
 | 
					 | 
				
			||||||
- * use when they pass the range of supported interfaces and MAC capabilities.
 | 
					 | 
				
			||||||
- * This makes use of phylink_get_linkmodes().
 | 
					 | 
				
			||||||
+ * Calculate the supported link modes based on @mac_capabilities, and restrict
 | 
					 | 
				
			||||||
+ * @supported and @state based on that. Use this function if your capabiliies
 | 
					 | 
				
			||||||
+ * aren't constant, such as if they vary depending on the interface.
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
-void phylink_generic_validate(struct phylink_config *config,
 | 
					 | 
				
			||||||
-			      unsigned long *supported,
 | 
					 | 
				
			||||||
-			      struct phylink_link_state *state)
 | 
					 | 
				
			||||||
+void phylink_validate_mask_caps(unsigned long *supported,
 | 
					 | 
				
			||||||
+				struct phylink_link_state *state,
 | 
					 | 
				
			||||||
+				unsigned long mac_capabilities)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
 | 
					 | 
				
			||||||
 	unsigned long caps;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	phylink_set_port_modes(mask);
 | 
					 | 
				
			||||||
 	phylink_set(mask, Autoneg);
 | 
					 | 
				
			||||||
-	caps = phylink_get_capabilities(state->interface,
 | 
					 | 
				
			||||||
-					config->mac_capabilities,
 | 
					 | 
				
			||||||
+	caps = phylink_get_capabilities(state->interface, mac_capabilities,
 | 
					 | 
				
			||||||
 					state->rate_matching);
 | 
					 | 
				
			||||||
 	phylink_caps_to_linkmodes(mask, caps);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	linkmode_and(supported, supported, mask);
 | 
					 | 
				
			||||||
-	linkmode_and(state->advertising, state->advertising, mask);
 | 
					 | 
				
			||||||
+	if (state)
 | 
					 | 
				
			||||||
+		linkmode_and(state->advertising, state->advertising, mask);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL_GPL(phylink_validate_mask_caps);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/**
 | 
					 | 
				
			||||||
+ * phylink_generic_validate() - generic validate() callback implementation
 | 
					 | 
				
			||||||
+ * @config: a pointer to a &struct phylink_config.
 | 
					 | 
				
			||||||
+ * @supported: ethtool bitmask for supported link modes.
 | 
					 | 
				
			||||||
+ * @state: a pointer to a &struct phylink_link_state.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Generic implementation of the validate() callback that MAC drivers can
 | 
					 | 
				
			||||||
+ * use when they pass the range of supported interfaces and MAC capabilities.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+void phylink_generic_validate(struct phylink_config *config,
 | 
					 | 
				
			||||||
+			      unsigned long *supported,
 | 
					 | 
				
			||||||
+			      struct phylink_link_state *state)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	phylink_validate_mask_caps(supported, state, config->mac_capabilities);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 EXPORT_SYMBOL_GPL(phylink_generic_validate);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
--- a/include/linux/phylink.h
 | 
					 | 
				
			||||||
+++ b/include/linux/phylink.h
 | 
					 | 
				
			||||||
@@ -558,6 +558,9 @@ void phylink_caps_to_linkmodes(unsigned
 | 
					 | 
				
			||||||
 unsigned long phylink_get_capabilities(phy_interface_t interface,
 | 
					 | 
				
			||||||
 				       unsigned long mac_capabilities,
 | 
					 | 
				
			||||||
 				       int rate_matching);
 | 
					 | 
				
			||||||
+void phylink_validate_mask_caps(unsigned long *supported,
 | 
					 | 
				
			||||||
+				struct phylink_link_state *state,
 | 
					 | 
				
			||||||
+				unsigned long caps);
 | 
					 | 
				
			||||||
 void phylink_generic_validate(struct phylink_config *config,
 | 
					 | 
				
			||||||
 			      unsigned long *supported,
 | 
					 | 
				
			||||||
 			      struct phylink_link_state *state);
 | 
					 | 
				
			||||||
@ -1,39 +0,0 @@
 | 
				
			|||||||
From 2bf7e4a68c42eed909f3c29582e1fb85cb157e35 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Jakub Kicinski <kuba@kernel.org>
 | 
					 | 
				
			||||||
Date: Tue, 25 Oct 2022 11:51:26 -0700
 | 
					 | 
				
			||||||
Subject: [PATCH 05/21] phylink: require valid state argument to
 | 
					 | 
				
			||||||
 phylink_validate_mask_caps()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
state is deferenced earlier in the function, the NULL check
 | 
					 | 
				
			||||||
is pointless. Since we don't have any crash reports presumably
 | 
					 | 
				
			||||||
it's safe to assume state is not NULL.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Fixes: f392a1846489 ("net: phylink: provide phylink_validate_mask_caps() helper")
 | 
					 | 
				
			||||||
Reviewed-by: Sean Anderson <sean.anderson@seco.com>
 | 
					 | 
				
			||||||
Link: https://lore.kernel.org/r/20221025185126.1720553-1-kuba@kernel.org
 | 
					 | 
				
			||||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/phy/phylink.c | 5 ++---
 | 
					 | 
				
			||||||
 1 file changed, 2 insertions(+), 3 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/phylink.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/phylink.c
 | 
					 | 
				
			||||||
@@ -566,7 +566,7 @@ EXPORT_SYMBOL_GPL(phylink_get_capabiliti
 | 
					 | 
				
			||||||
 /**
 | 
					 | 
				
			||||||
  * phylink_validate_mask_caps() - Restrict link modes based on caps
 | 
					 | 
				
			||||||
  * @supported: ethtool bitmask for supported link modes.
 | 
					 | 
				
			||||||
- * @state: an (optional) pointer to a &struct phylink_link_state.
 | 
					 | 
				
			||||||
+ * @state: pointer to a &struct phylink_link_state.
 | 
					 | 
				
			||||||
  * @mac_capabilities: bitmask of MAC capabilities
 | 
					 | 
				
			||||||
  *
 | 
					 | 
				
			||||||
  * Calculate the supported link modes based on @mac_capabilities, and restrict
 | 
					 | 
				
			||||||
@@ -587,8 +587,7 @@ void phylink_validate_mask_caps(unsigned
 | 
					 | 
				
			||||||
 	phylink_caps_to_linkmodes(mask, caps);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	linkmode_and(supported, supported, mask);
 | 
					 | 
				
			||||||
-	if (state)
 | 
					 | 
				
			||||||
-		linkmode_and(state->advertising, state->advertising, mask);
 | 
					 | 
				
			||||||
+	linkmode_and(state->advertising, state->advertising, mask);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 EXPORT_SYMBOL_GPL(phylink_validate_mask_caps);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@ -1,48 +0,0 @@
 | 
				
			|||||||
From f8fc363bf0c023e4736a0328174b4a24b44ab23a Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Date: Thu, 27 Oct 2022 14:10:37 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH 06/21] net: phylink: add phylink_get_link_timer_ns() helper
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Add a helper to convert the PHY interface mode to the required link
 | 
					 | 
				
			||||||
timer setting as stated by the appropriate standard. Inappropriate
 | 
					 | 
				
			||||||
interface modes return an error.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 include/linux/phylink.h | 24 ++++++++++++++++++++++++
 | 
					 | 
				
			||||||
 1 file changed, 24 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/include/linux/phylink.h
 | 
					 | 
				
			||||||
+++ b/include/linux/phylink.h
 | 
					 | 
				
			||||||
@@ -617,6 +617,30 @@ int phylink_speed_up(struct phylink *pl)
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 void phylink_set_port_modes(unsigned long *bits);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+/**
 | 
					 | 
				
			||||||
+ * phylink_get_link_timer_ns - return the PCS link timer value
 | 
					 | 
				
			||||||
+ * @interface: link &typedef phy_interface_t mode
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Return the PCS link timer setting in nanoseconds for the PHY @interface
 | 
					 | 
				
			||||||
+ * mode, or -EINVAL if not appropriate.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+static inline int phylink_get_link_timer_ns(phy_interface_t interface)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	switch (interface) {
 | 
					 | 
				
			||||||
+	case PHY_INTERFACE_MODE_SGMII:
 | 
					 | 
				
			||||||
+	case PHY_INTERFACE_MODE_QSGMII:
 | 
					 | 
				
			||||||
+	case PHY_INTERFACE_MODE_USXGMII:
 | 
					 | 
				
			||||||
+		return 1600000;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	case PHY_INTERFACE_MODE_1000BASEX:
 | 
					 | 
				
			||||||
+	case PHY_INTERFACE_MODE_2500BASEX:
 | 
					 | 
				
			||||||
+		return 10000000;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	default:
 | 
					 | 
				
			||||||
+		return -EINVAL;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 void phylink_mii_c22_pcs_decode_state(struct phylink_link_state *state,
 | 
					 | 
				
			||||||
 				      u16 bmsr, u16 lpa);
 | 
					 | 
				
			||||||
 void phylink_mii_c22_pcs_get_state(struct mdio_device *pcs,
 | 
					 | 
				
			||||||
@ -1,250 +0,0 @@
 | 
				
			|||||||
From b45b773a96b0e9e8d51e5d005485f4e376d6ce9a Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Date: Fri, 4 Nov 2022 17:13:01 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH 07/21] net: remove explicit phylink_generic_validate()
 | 
					 | 
				
			||||||
 references
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Virtually all conventional network drivers are now converted to use
 | 
					 | 
				
			||||||
phylink_generic_validate() - only DSA drivers and fman_memac remain,
 | 
					 | 
				
			||||||
so lets remove the necessity for network drivers to explicitly set
 | 
					 | 
				
			||||||
this member, and default to phylink_generic_validate() when unset.
 | 
					 | 
				
			||||||
This is possible as .validate must currently be set.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Any remaining instances that have not been addressed by this patch can
 | 
					 | 
				
			||||||
be fixed up later.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Reviewed-by: Vladimir Oltean <vladimir.oltean@nxp.com>
 | 
					 | 
				
			||||||
Link: https://lore.kernel.org/r/E1or0FZ-001tRa-DI@rmk-PC.armlinux.org.uk
 | 
					 | 
				
			||||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/ethernet/altera/altera_tse_main.c            | 1 -
 | 
					 | 
				
			||||||
 drivers/net/ethernet/atheros/ag71xx.c                    | 1 -
 | 
					 | 
				
			||||||
 drivers/net/ethernet/cadence/macb_main.c                 | 1 -
 | 
					 | 
				
			||||||
 drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c         | 1 -
 | 
					 | 
				
			||||||
 drivers/net/ethernet/freescale/enetc/enetc_pf.c          | 1 -
 | 
					 | 
				
			||||||
 drivers/net/ethernet/freescale/fman/fman_dtsec.c         | 1 -
 | 
					 | 
				
			||||||
 drivers/net/ethernet/freescale/fman/fman_tgec.c          | 1 -
 | 
					 | 
				
			||||||
 drivers/net/ethernet/marvell/mvneta.c                    | 1 -
 | 
					 | 
				
			||||||
 drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c          | 1 -
 | 
					 | 
				
			||||||
 drivers/net/ethernet/marvell/prestera/prestera_main.c    | 1 -
 | 
					 | 
				
			||||||
 drivers/net/ethernet/mediatek/mtk_eth_soc.c              | 1 -
 | 
					 | 
				
			||||||
 drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c | 1 -
 | 
					 | 
				
			||||||
 drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c   | 1 -
 | 
					 | 
				
			||||||
 drivers/net/ethernet/mscc/ocelot_net.c                   | 1 -
 | 
					 | 
				
			||||||
 drivers/net/ethernet/stmicro/stmmac/stmmac_main.c        | 1 -
 | 
					 | 
				
			||||||
 drivers/net/ethernet/ti/am65-cpsw-nuss.c                 | 1 -
 | 
					 | 
				
			||||||
 drivers/net/ethernet/xilinx/xilinx_axienet_main.c        | 1 -
 | 
					 | 
				
			||||||
 drivers/net/phy/phylink.c                                | 5 ++++-
 | 
					 | 
				
			||||||
 drivers/net/usb/asix_devices.c                           | 1 -
 | 
					 | 
				
			||||||
 include/linux/phylink.h                                  | 5 +++++
 | 
					 | 
				
			||||||
 20 files changed, 9 insertions(+), 19 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/ethernet/altera/altera_tse_main.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/ethernet/altera/altera_tse_main.c
 | 
					 | 
				
			||||||
@@ -1096,7 +1096,6 @@ static struct phylink_pcs *alt_tse_selec
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static const struct phylink_mac_ops alt_tse_phylink_ops = {
 | 
					 | 
				
			||||||
-	.validate = phylink_generic_validate,
 | 
					 | 
				
			||||||
 	.mac_an_restart = alt_tse_mac_an_restart,
 | 
					 | 
				
			||||||
 	.mac_config = alt_tse_mac_config,
 | 
					 | 
				
			||||||
 	.mac_link_down = alt_tse_mac_link_down,
 | 
					 | 
				
			||||||
--- a/drivers/net/ethernet/atheros/ag71xx.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/ethernet/atheros/ag71xx.c
 | 
					 | 
				
			||||||
@@ -1086,7 +1086,6 @@ static void ag71xx_mac_link_up(struct ph
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static const struct phylink_mac_ops ag71xx_phylink_mac_ops = {
 | 
					 | 
				
			||||||
-	.validate = phylink_generic_validate,
 | 
					 | 
				
			||||||
 	.mac_config = ag71xx_mac_config,
 | 
					 | 
				
			||||||
 	.mac_link_down = ag71xx_mac_link_down,
 | 
					 | 
				
			||||||
 	.mac_link_up = ag71xx_mac_link_up,
 | 
					 | 
				
			||||||
--- a/drivers/net/ethernet/cadence/macb_main.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/ethernet/cadence/macb_main.c
 | 
					 | 
				
			||||||
@@ -752,7 +752,6 @@ static struct phylink_pcs *macb_mac_sele
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static const struct phylink_mac_ops macb_phylink_ops = {
 | 
					 | 
				
			||||||
-	.validate = phylink_generic_validate,
 | 
					 | 
				
			||||||
 	.mac_select_pcs = macb_mac_select_pcs,
 | 
					 | 
				
			||||||
 	.mac_config = macb_mac_config,
 | 
					 | 
				
			||||||
 	.mac_link_down = macb_mac_link_down,
 | 
					 | 
				
			||||||
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
 | 
					 | 
				
			||||||
@@ -235,7 +235,6 @@ static void dpaa2_mac_link_down(struct p
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static const struct phylink_mac_ops dpaa2_mac_phylink_ops = {
 | 
					 | 
				
			||||||
-	.validate = phylink_generic_validate,
 | 
					 | 
				
			||||||
 	.mac_select_pcs = dpaa2_mac_select_pcs,
 | 
					 | 
				
			||||||
 	.mac_config = dpaa2_mac_config,
 | 
					 | 
				
			||||||
 	.mac_link_up = dpaa2_mac_link_up,
 | 
					 | 
				
			||||||
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
 | 
					 | 
				
			||||||
@@ -1111,7 +1111,6 @@ static void enetc_pl_mac_link_down(struc
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static const struct phylink_mac_ops enetc_mac_phylink_ops = {
 | 
					 | 
				
			||||||
-	.validate = phylink_generic_validate,
 | 
					 | 
				
			||||||
 	.mac_select_pcs = enetc_pl_mac_select_pcs,
 | 
					 | 
				
			||||||
 	.mac_config = enetc_pl_mac_config,
 | 
					 | 
				
			||||||
 	.mac_link_up = enetc_pl_mac_link_up,
 | 
					 | 
				
			||||||
--- a/drivers/net/ethernet/freescale/fman/fman_dtsec.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c
 | 
					 | 
				
			||||||
@@ -986,7 +986,6 @@ static void dtsec_link_down(struct phyli
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static const struct phylink_mac_ops dtsec_mac_ops = {
 | 
					 | 
				
			||||||
-	.validate = phylink_generic_validate,
 | 
					 | 
				
			||||||
 	.mac_select_pcs = dtsec_select_pcs,
 | 
					 | 
				
			||||||
 	.mac_config = dtsec_mac_config,
 | 
					 | 
				
			||||||
 	.mac_link_up = dtsec_link_up,
 | 
					 | 
				
			||||||
--- a/drivers/net/ethernet/freescale/fman/fman_tgec.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/ethernet/freescale/fman/fman_tgec.c
 | 
					 | 
				
			||||||
@@ -469,7 +469,6 @@ static void tgec_link_down(struct phylin
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static const struct phylink_mac_ops tgec_mac_ops = {
 | 
					 | 
				
			||||||
-	.validate = phylink_generic_validate,
 | 
					 | 
				
			||||||
 	.mac_config = tgec_mac_config,
 | 
					 | 
				
			||||||
 	.mac_link_up = tgec_link_up,
 | 
					 | 
				
			||||||
 	.mac_link_down = tgec_link_down,
 | 
					 | 
				
			||||||
--- a/drivers/net/ethernet/marvell/mvneta.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/ethernet/marvell/mvneta.c
 | 
					 | 
				
			||||||
@@ -4228,7 +4228,6 @@ static void mvneta_mac_link_up(struct ph
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static const struct phylink_mac_ops mvneta_phylink_ops = {
 | 
					 | 
				
			||||||
-	.validate = phylink_generic_validate,
 | 
					 | 
				
			||||||
 	.mac_select_pcs = mvneta_mac_select_pcs,
 | 
					 | 
				
			||||||
 	.mac_prepare = mvneta_mac_prepare,
 | 
					 | 
				
			||||||
 	.mac_config = mvneta_mac_config,
 | 
					 | 
				
			||||||
--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
 | 
					 | 
				
			||||||
@@ -6633,7 +6633,6 @@ static void mvpp2_mac_link_down(struct p
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static const struct phylink_mac_ops mvpp2_phylink_ops = {
 | 
					 | 
				
			||||||
-	.validate = phylink_generic_validate,
 | 
					 | 
				
			||||||
 	.mac_select_pcs = mvpp2_select_pcs,
 | 
					 | 
				
			||||||
 	.mac_prepare = mvpp2_mac_prepare,
 | 
					 | 
				
			||||||
 	.mac_config = mvpp2_mac_config,
 | 
					 | 
				
			||||||
--- a/drivers/net/ethernet/marvell/prestera/prestera_main.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/ethernet/marvell/prestera/prestera_main.c
 | 
					 | 
				
			||||||
@@ -360,7 +360,6 @@ static void prestera_pcs_an_restart(stru
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static const struct phylink_mac_ops prestera_mac_ops = {
 | 
					 | 
				
			||||||
-	.validate = phylink_generic_validate,
 | 
					 | 
				
			||||||
 	.mac_select_pcs = prestera_mac_select_pcs,
 | 
					 | 
				
			||||||
 	.mac_config = prestera_mac_config,
 | 
					 | 
				
			||||||
 	.mac_link_down = prestera_mac_link_down,
 | 
					 | 
				
			||||||
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 | 
					 | 
				
			||||||
@@ -653,7 +653,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
 | 
					 | 
				
			||||||
@@ -1737,7 +1737,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);
 | 
					 | 
				
			||||||
@ -1,48 +0,0 @@
 | 
				
			|||||||
From a90ac762d345890b40d88a1385a34a2449c2d75e Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Date: Fri, 24 Mar 2023 09:23:42 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] net: sfp: make sfp_bus_find_fwnode() take a const fwnode
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
sfp_bus_find_fwnode() does not write to the fwnode, so let's make it
 | 
					 | 
				
			||||||
const.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Reviewed-by: Simon Horman <simon.horman@corigine.com>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/phy/sfp-bus.c | 2 +-
 | 
					 | 
				
			||||||
 include/linux/sfp.h       | 5 +++--
 | 
					 | 
				
			||||||
 2 files changed, 4 insertions(+), 3 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/sfp-bus.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/sfp-bus.c
 | 
					 | 
				
			||||||
@@ -603,7 +603,7 @@ static void sfp_upstream_clear(struct sf
 | 
					 | 
				
			||||||
  *	- %-ENOMEM if we failed to allocate the bus.
 | 
					 | 
				
			||||||
  *	- an error from the upstream's connect_phy() method.
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
-struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode)
 | 
					 | 
				
			||||||
+struct sfp_bus *sfp_bus_find_fwnode(const struct fwnode_handle *fwnode)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	struct fwnode_reference_args ref;
 | 
					 | 
				
			||||||
 	struct sfp_bus *bus;
 | 
					 | 
				
			||||||
--- a/include/linux/sfp.h
 | 
					 | 
				
			||||||
+++ b/include/linux/sfp.h
 | 
					 | 
				
			||||||
@@ -548,7 +548,7 @@ int sfp_get_module_eeprom_by_page(struct
 | 
					 | 
				
			||||||
 void sfp_upstream_start(struct sfp_bus *bus);
 | 
					 | 
				
			||||||
 void sfp_upstream_stop(struct sfp_bus *bus);
 | 
					 | 
				
			||||||
 void sfp_bus_put(struct sfp_bus *bus);
 | 
					 | 
				
			||||||
-struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode);
 | 
					 | 
				
			||||||
+struct sfp_bus *sfp_bus_find_fwnode(const struct fwnode_handle *fwnode);
 | 
					 | 
				
			||||||
 int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream,
 | 
					 | 
				
			||||||
 			 const struct sfp_upstream_ops *ops);
 | 
					 | 
				
			||||||
 void sfp_bus_del_upstream(struct sfp_bus *bus);
 | 
					 | 
				
			||||||
@@ -610,7 +610,8 @@ static inline void sfp_bus_put(struct sf
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-static inline struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode)
 | 
					 | 
				
			||||||
+static inline struct sfp_bus *
 | 
					 | 
				
			||||||
+sfp_bus_find_fwnode(const struct fwnode_handle *fwnode)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	return NULL;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
@ -1,31 +0,0 @@
 | 
				
			|||||||
From ecec0ebbc6381a5a375f1cf10c4858f24e91e2ef Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Date: Wed, 15 Mar 2023 14:46:49 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] net: pcs: lynx: don't print an_enabled in pcs_get_state()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
an_enabled will be going away, and in any case, pcs_get_state() should
 | 
					 | 
				
			||||||
not be updating this member. Remove the print.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Reviewed-by: Steen Hegelund <Steen.Hegelund@microchip.com>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/pcs/pcs-lynx.c | 4 ++--
 | 
					 | 
				
			||||||
 1 file changed, 2 insertions(+), 2 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/pcs/pcs-lynx.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/pcs/pcs-lynx.c
 | 
					 | 
				
			||||||
@@ -115,11 +115,11 @@ static void lynx_pcs_get_state(struct ph
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	dev_dbg(&lynx->mdio->dev,
 | 
					 | 
				
			||||||
-		"mode=%s/%s/%s link=%u an_enabled=%u an_complete=%u\n",
 | 
					 | 
				
			||||||
+		"mode=%s/%s/%s link=%u an_complete=%u\n",
 | 
					 | 
				
			||||||
 		phy_modes(state->interface),
 | 
					 | 
				
			||||||
 		phy_speed_to_str(state->speed),
 | 
					 | 
				
			||||||
 		phy_duplex_to_str(state->duplex),
 | 
					 | 
				
			||||||
-		state->link, state->an_enabled, state->an_complete);
 | 
					 | 
				
			||||||
+		state->link, state->an_complete);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static int lynx_pcs_config_giga(struct mdio_device *pcs, unsigned int mode,
 | 
					 | 
				
			||||||
@ -1,32 +0,0 @@
 | 
				
			|||||||
From 99d0f3a1095f4c938b1665025c29411edafe8a01 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Date: Tue, 21 Mar 2023 15:58:44 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] net: dpaa2-mac: use Autoneg bit rather than an_enabled
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The Autoneg bit in the advertising bitmap and state->an_enabled are
 | 
					 | 
				
			||||||
always identical. Thus, we will be removing state->an_enabled.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Use the Autoneg bit in the advertising bitmap to indicate whether
 | 
					 | 
				
			||||||
autonegotiation should be used, rather than using the an_enabled
 | 
					 | 
				
			||||||
member which will be going away. This means we use the same condition
 | 
					 | 
				
			||||||
as phylink_mii_c22_pcs_config().
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Reviewed-by: Simon Horman <simon.horman@corigine.com>
 | 
					 | 
				
			||||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 3 ++-
 | 
					 | 
				
			||||||
 1 file changed, 2 insertions(+), 1 deletion(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
 | 
					 | 
				
			||||||
@@ -158,7 +158,8 @@ static void dpaa2_mac_config(struct phyl
 | 
					 | 
				
			||||||
 	struct dpmac_link_state *dpmac_state = &mac->state;
 | 
					 | 
				
			||||||
 	int err;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if (state->an_enabled)
 | 
					 | 
				
			||||||
+	if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
 | 
					 | 
				
			||||||
+			      state->advertising))
 | 
					 | 
				
			||||||
 		dpmac_state->options |= DPMAC_LINK_OPT_AUTONEG;
 | 
					 | 
				
			||||||
 	else
 | 
					 | 
				
			||||||
 		dpmac_state->options &= ~DPMAC_LINK_OPT_AUTONEG;
 | 
					 | 
				
			||||||
@ -1,64 +0,0 @@
 | 
				
			|||||||
From 471c40bde606ec0b1ce8c616f7998739c7a783a6 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Ivan Bornyakov <i.bornyakov@metrotek.ru>
 | 
					 | 
				
			||||||
Date: Fri, 10 Feb 2023 18:46:27 +0300
 | 
					 | 
				
			||||||
Subject: [PATCH 10/21] net: phylink: support validated pause and autoneg in
 | 
					 | 
				
			||||||
 fixed-link
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
In fixed-link setup phylink_parse_fixedlink() unconditionally sets
 | 
					 | 
				
			||||||
Pause, Asym_Pause and Autoneg bits to "supported" bitmap, while MAC may
 | 
					 | 
				
			||||||
not support these.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This leads to ethtool reporting:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 > Supported pause frame use: Symmetric Receive-only
 | 
					 | 
				
			||||||
 > Supports auto-negotiation: Yes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
regardless of what is actually supported.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Instead of unconditionally set Pause, Asym_Pause and Autoneg it is
 | 
					 | 
				
			||||||
sensible to set them according to validated "supported" bitmap, i.e. the
 | 
					 | 
				
			||||||
result of phylink_validate().
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Ivan Bornyakov <i.bornyakov@metrotek.ru>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/phy/phylink.c | 17 ++++++++++++++---
 | 
					 | 
				
			||||||
 1 file changed, 14 insertions(+), 3 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/phylink.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/phylink.c
 | 
					 | 
				
			||||||
@@ -709,6 +709,7 @@ static int phylink_parse_fixedlink(struc
 | 
					 | 
				
			||||||
 				   struct fwnode_handle *fwnode)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	struct fwnode_handle *fixed_node;
 | 
					 | 
				
			||||||
+	bool pause, asym_pause, autoneg;
 | 
					 | 
				
			||||||
 	const struct phy_setting *s;
 | 
					 | 
				
			||||||
 	struct gpio_desc *desc;
 | 
					 | 
				
			||||||
 	u32 speed;
 | 
					 | 
				
			||||||
@@ -781,13 +782,23 @@ static int phylink_parse_fixedlink(struc
 | 
					 | 
				
			||||||
 	linkmode_copy(pl->link_config.advertising, pl->supported);
 | 
					 | 
				
			||||||
 	phylink_validate(pl, pl->supported, &pl->link_config);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	pause = phylink_test(pl->supported, Pause);
 | 
					 | 
				
			||||||
+	asym_pause = phylink_test(pl->supported, Asym_Pause);
 | 
					 | 
				
			||||||
+	autoneg = phylink_test(pl->supported, Autoneg);
 | 
					 | 
				
			||||||
 	s = phy_lookup_setting(pl->link_config.speed, pl->link_config.duplex,
 | 
					 | 
				
			||||||
 			       pl->supported, true);
 | 
					 | 
				
			||||||
 	linkmode_zero(pl->supported);
 | 
					 | 
				
			||||||
 	phylink_set(pl->supported, MII);
 | 
					 | 
				
			||||||
-	phylink_set(pl->supported, Pause);
 | 
					 | 
				
			||||||
-	phylink_set(pl->supported, Asym_Pause);
 | 
					 | 
				
			||||||
-	phylink_set(pl->supported, Autoneg);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (pause)
 | 
					 | 
				
			||||||
+		phylink_set(pl->supported, Pause);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (asym_pause)
 | 
					 | 
				
			||||||
+		phylink_set(pl->supported, Asym_Pause);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (autoneg)
 | 
					 | 
				
			||||||
+		phylink_set(pl->supported, Autoneg);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	if (s) {
 | 
					 | 
				
			||||||
 		__set_bit(s->bit, pl->supported);
 | 
					 | 
				
			||||||
 		__set_bit(s->bit, pl->link_config.lp_advertising);
 | 
					 | 
				
			||||||
@ -1,177 +0,0 @@
 | 
				
			|||||||
From 7211ffd70941933a7825a56cf480f07ee81c321c Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Date: Tue, 21 Mar 2023 15:58:54 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH 11/21] net: phylink: remove an_enabled
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The Autoneg bit in the advertising bitmap and state->an_enabled are
 | 
					 | 
				
			||||||
always identical. state->an_enabled is now no longer used by any
 | 
					 | 
				
			||||||
drivers, so lets kill this duplication.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Reviewed-by: Simon Horman <simon.horman@corigine.com>
 | 
					 | 
				
			||||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/phy/phylink.c | 37 +++++++++++++++++--------------------
 | 
					 | 
				
			||||||
 include/linux/phylink.h   |  2 --
 | 
					 | 
				
			||||||
 2 files changed, 17 insertions(+), 22 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/phylink.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/phylink.c
 | 
					 | 
				
			||||||
@@ -841,7 +841,6 @@ static int phylink_parse_mode(struct phy
 | 
					 | 
				
			||||||
 		phylink_set(pl->supported, Autoneg);
 | 
					 | 
				
			||||||
 		phylink_set(pl->supported, Asym_Pause);
 | 
					 | 
				
			||||||
 		phylink_set(pl->supported, Pause);
 | 
					 | 
				
			||||||
-		pl->link_config.an_enabled = true;
 | 
					 | 
				
			||||||
 		pl->cfg_link_an_mode = MLO_AN_INBAND;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 		switch (pl->link_config.interface) {
 | 
					 | 
				
			||||||
@@ -944,9 +943,6 @@ static int phylink_parse_mode(struct phy
 | 
					 | 
				
			||||||
 				    "failed to validate link configuration for in-band status\n");
 | 
					 | 
				
			||||||
 			return -EINVAL;
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-		/* Check if MAC/PCS also supports Autoneg. */
 | 
					 | 
				
			||||||
-		pl->link_config.an_enabled = phylink_test(pl->supported, Autoneg);
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	return 0;
 | 
					 | 
				
			||||||
@@ -956,7 +952,8 @@ static void phylink_apply_manual_flow(st
 | 
					 | 
				
			||||||
 				      struct phylink_link_state *state)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	/* If autoneg is disabled, pause AN is also disabled */
 | 
					 | 
				
			||||||
-	if (!state->an_enabled)
 | 
					 | 
				
			||||||
+	if (!linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
 | 
					 | 
				
			||||||
+			       state->advertising))
 | 
					 | 
				
			||||||
 		state->pause &= ~MLO_PAUSE_AN;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* Manual configuration of pause modes */
 | 
					 | 
				
			||||||
@@ -996,21 +993,22 @@ static void phylink_mac_config(struct ph
 | 
					 | 
				
			||||||
 			       const struct phylink_link_state *state)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	phylink_dbg(pl,
 | 
					 | 
				
			||||||
-		    "%s: mode=%s/%s/%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n",
 | 
					 | 
				
			||||||
+		    "%s: mode=%s/%s/%s/%s/%s adv=%*pb pause=%02x link=%u\n",
 | 
					 | 
				
			||||||
 		    __func__, phylink_an_mode_str(pl->cur_link_an_mode),
 | 
					 | 
				
			||||||
 		    phy_modes(state->interface),
 | 
					 | 
				
			||||||
 		    phy_speed_to_str(state->speed),
 | 
					 | 
				
			||||||
 		    phy_duplex_to_str(state->duplex),
 | 
					 | 
				
			||||||
 		    phy_rate_matching_to_str(state->rate_matching),
 | 
					 | 
				
			||||||
 		    __ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising,
 | 
					 | 
				
			||||||
-		    state->pause, state->link, state->an_enabled);
 | 
					 | 
				
			||||||
+		    state->pause, state->link);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	pl->mac_ops->mac_config(pl->config, pl->cur_link_an_mode, state);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static void phylink_mac_pcs_an_restart(struct phylink *pl)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
-	if (pl->link_config.an_enabled &&
 | 
					 | 
				
			||||||
+	if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
 | 
					 | 
				
			||||||
+			      pl->link_config.advertising) &&
 | 
					 | 
				
			||||||
 	    phy_interface_mode_is_8023z(pl->link_config.interface) &&
 | 
					 | 
				
			||||||
 	    phylink_autoneg_inband(pl->cur_link_an_mode)) {
 | 
					 | 
				
			||||||
 		if (pl->pcs)
 | 
					 | 
				
			||||||
@@ -1137,9 +1135,9 @@ static void phylink_mac_pcs_get_state(st
 | 
					 | 
				
			||||||
 	linkmode_copy(state->advertising, pl->link_config.advertising);
 | 
					 | 
				
			||||||
 	linkmode_zero(state->lp_advertising);
 | 
					 | 
				
			||||||
 	state->interface = pl->link_config.interface;
 | 
					 | 
				
			||||||
-	state->an_enabled = pl->link_config.an_enabled;
 | 
					 | 
				
			||||||
 	state->rate_matching = pl->link_config.rate_matching;
 | 
					 | 
				
			||||||
-	if (state->an_enabled) {
 | 
					 | 
				
			||||||
+	if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
 | 
					 | 
				
			||||||
+			      state->advertising)) {
 | 
					 | 
				
			||||||
 		state->speed = SPEED_UNKNOWN;
 | 
					 | 
				
			||||||
 		state->duplex = DUPLEX_UNKNOWN;
 | 
					 | 
				
			||||||
 		state->pause = MLO_PAUSE_NONE;
 | 
					 | 
				
			||||||
@@ -1531,7 +1529,6 @@ struct phylink *phylink_create(struct ph
 | 
					 | 
				
			||||||
 	pl->link_config.pause = MLO_PAUSE_AN;
 | 
					 | 
				
			||||||
 	pl->link_config.speed = SPEED_UNKNOWN;
 | 
					 | 
				
			||||||
 	pl->link_config.duplex = DUPLEX_UNKNOWN;
 | 
					 | 
				
			||||||
-	pl->link_config.an_enabled = true;
 | 
					 | 
				
			||||||
 	pl->mac_ops = mac_ops;
 | 
					 | 
				
			||||||
 	__set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
 | 
					 | 
				
			||||||
 	timer_setup(&pl->link_poll, phylink_fixed_poll, 0);
 | 
					 | 
				
			||||||
@@ -2155,8 +2152,9 @@ static void phylink_get_ksettings(const
 | 
					 | 
				
			||||||
 		kset->base.speed = state->speed;
 | 
					 | 
				
			||||||
 		kset->base.duplex = state->duplex;
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
-	kset->base.autoneg = state->an_enabled ? AUTONEG_ENABLE :
 | 
					 | 
				
			||||||
-				AUTONEG_DISABLE;
 | 
					 | 
				
			||||||
+	kset->base.autoneg = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
 | 
					 | 
				
			||||||
+					       state->advertising) ?
 | 
					 | 
				
			||||||
+				AUTONEG_ENABLE : AUTONEG_DISABLE;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /**
 | 
					 | 
				
			||||||
@@ -2303,9 +2301,8 @@ int phylink_ethtool_ksettings_set(struct
 | 
					 | 
				
			||||||
 	/* We have ruled out the case with a PHY attached, and the
 | 
					 | 
				
			||||||
 	 * fixed-link cases.  All that is left are in-band links.
 | 
					 | 
				
			||||||
 	 */
 | 
					 | 
				
			||||||
-	config.an_enabled = kset->base.autoneg == AUTONEG_ENABLE;
 | 
					 | 
				
			||||||
 	linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising,
 | 
					 | 
				
			||||||
-			 config.an_enabled);
 | 
					 | 
				
			||||||
+			 kset->base.autoneg == AUTONEG_ENABLE);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* If this link is with an SFP, ensure that changes to advertised modes
 | 
					 | 
				
			||||||
 	 * also cause the associated interface to be selected such that the
 | 
					 | 
				
			||||||
@@ -2339,13 +2336,14 @@ int phylink_ethtool_ksettings_set(struct
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* If autonegotiation is enabled, we must have an advertisement */
 | 
					 | 
				
			||||||
-	if (config.an_enabled && phylink_is_empty_linkmode(config.advertising))
 | 
					 | 
				
			||||||
+	if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
 | 
					 | 
				
			||||||
+			      config.advertising) &&
 | 
					 | 
				
			||||||
+	    phylink_is_empty_linkmode(config.advertising))
 | 
					 | 
				
			||||||
 		return -EINVAL;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	mutex_lock(&pl->state_mutex);
 | 
					 | 
				
			||||||
 	pl->link_config.speed = config.speed;
 | 
					 | 
				
			||||||
 	pl->link_config.duplex = config.duplex;
 | 
					 | 
				
			||||||
-	pl->link_config.an_enabled = config.an_enabled;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	if (pl->link_config.interface != config.interface) {
 | 
					 | 
				
			||||||
 		/* The interface changed, e.g. 1000base-X <-> 2500base-X */
 | 
					 | 
				
			||||||
@@ -2951,7 +2949,6 @@ static int phylink_sfp_config_phy(struct
 | 
					 | 
				
			||||||
 	config.speed = SPEED_UNKNOWN;
 | 
					 | 
				
			||||||
 	config.duplex = DUPLEX_UNKNOWN;
 | 
					 | 
				
			||||||
 	config.pause = MLO_PAUSE_AN;
 | 
					 | 
				
			||||||
-	config.an_enabled = pl->link_config.an_enabled;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* Ignore errors if we're expecting a PHY to attach later */
 | 
					 | 
				
			||||||
 	ret = phylink_validate(pl, support, &config);
 | 
					 | 
				
			||||||
@@ -3020,7 +3017,6 @@ static int phylink_sfp_config_optical(st
 | 
					 | 
				
			||||||
 	config.speed = SPEED_UNKNOWN;
 | 
					 | 
				
			||||||
 	config.duplex = DUPLEX_UNKNOWN;
 | 
					 | 
				
			||||||
 	config.pause = MLO_PAUSE_AN;
 | 
					 | 
				
			||||||
-	config.an_enabled = true;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* For all the interfaces that are supported, reduce the sfp_support
 | 
					 | 
				
			||||||
 	 * mask to only those link modes that can be supported.
 | 
					 | 
				
			||||||
@@ -3354,7 +3350,8 @@ void phylink_mii_c22_pcs_decode_state(st
 | 
					 | 
				
			||||||
 	/* If there is no link or autonegotiation is disabled, the LP advertisement
 | 
					 | 
				
			||||||
 	 * data is not meaningful, so don't go any further.
 | 
					 | 
				
			||||||
 	 */
 | 
					 | 
				
			||||||
-	if (!state->link || !state->an_enabled)
 | 
					 | 
				
			||||||
+	if (!state->link || !linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
 | 
					 | 
				
			||||||
+					       state->advertising))
 | 
					 | 
				
			||||||
 		return;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	switch (state->interface) {
 | 
					 | 
				
			||||||
--- a/include/linux/phylink.h
 | 
					 | 
				
			||||||
+++ b/include/linux/phylink.h
 | 
					 | 
				
			||||||
@@ -93,7 +93,6 @@ static inline bool phylink_autoneg_inban
 | 
					 | 
				
			||||||
  *   the medium link mode (@speed and @duplex) and the speed/duplex of the phy
 | 
					 | 
				
			||||||
  *   interface mode (@interface) are different.
 | 
					 | 
				
			||||||
  * @link: true if the link is up.
 | 
					 | 
				
			||||||
- * @an_enabled: true if autonegotiation is enabled/desired.
 | 
					 | 
				
			||||||
  * @an_complete: true if autonegotiation has completed.
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
 struct phylink_link_state {
 | 
					 | 
				
			||||||
@@ -105,7 +104,6 @@ struct phylink_link_state {
 | 
					 | 
				
			||||||
 	int pause;
 | 
					 | 
				
			||||||
 	int rate_matching;
 | 
					 | 
				
			||||||
 	unsigned int link:1;
 | 
					 | 
				
			||||||
-	unsigned int an_enabled:1;
 | 
					 | 
				
			||||||
 	unsigned int an_complete:1;
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@ -1,88 +0,0 @@
 | 
				
			|||||||
From a3555d1f5c208f0a63eafee77381f68d304a0512 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Date: Fri, 12 May 2023 17:58:37 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH 12/21] net: phylink: constify fwnode arguments
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Both phylink_create() and phylink_fwnode_phy_connect() do not modify
 | 
					 | 
				
			||||||
the fwnode argument that they are passed, so lets constify these.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Reviewed-by: Simon Horman <simon.horman@corigine.com>
 | 
					 | 
				
			||||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/phy/phylink.c | 11 ++++++-----
 | 
					 | 
				
			||||||
 include/linux/phylink.h   |  9 +++++----
 | 
					 | 
				
			||||||
 2 files changed, 11 insertions(+), 9 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/phylink.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/phylink.c
 | 
					 | 
				
			||||||
@@ -706,7 +706,7 @@ static int phylink_validate(struct phyli
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static int phylink_parse_fixedlink(struct phylink *pl,
 | 
					 | 
				
			||||||
-				   struct fwnode_handle *fwnode)
 | 
					 | 
				
			||||||
+				   const struct fwnode_handle *fwnode)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	struct fwnode_handle *fixed_node;
 | 
					 | 
				
			||||||
 	bool pause, asym_pause, autoneg;
 | 
					 | 
				
			||||||
@@ -817,7 +817,8 @@ static int phylink_parse_fixedlink(struc
 | 
					 | 
				
			||||||
 	return 0;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode)
 | 
					 | 
				
			||||||
+static int phylink_parse_mode(struct phylink *pl,
 | 
					 | 
				
			||||||
+			      const struct fwnode_handle *fwnode)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	struct fwnode_handle *dn;
 | 
					 | 
				
			||||||
 	const char *managed;
 | 
					 | 
				
			||||||
@@ -1440,7 +1441,7 @@ static void phylink_fixed_poll(struct ti
 | 
					 | 
				
			||||||
 static const struct sfp_upstream_ops sfp_phylink_ops;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static int phylink_register_sfp(struct phylink *pl,
 | 
					 | 
				
			||||||
-				struct fwnode_handle *fwnode)
 | 
					 | 
				
			||||||
+				const struct fwnode_handle *fwnode)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	struct sfp_bus *bus;
 | 
					 | 
				
			||||||
 	int ret;
 | 
					 | 
				
			||||||
@@ -1479,7 +1480,7 @@ static int phylink_register_sfp(struct p
 | 
					 | 
				
			||||||
  * must use IS_ERR() to check for errors from this function.
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
 struct phylink *phylink_create(struct phylink_config *config,
 | 
					 | 
				
			||||||
-			       struct fwnode_handle *fwnode,
 | 
					 | 
				
			||||||
+			       const struct fwnode_handle *fwnode,
 | 
					 | 
				
			||||||
 			       phy_interface_t iface,
 | 
					 | 
				
			||||||
 			       const struct phylink_mac_ops *mac_ops)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
@@ -1809,7 +1810,7 @@ EXPORT_SYMBOL_GPL(phylink_of_phy_connect
 | 
					 | 
				
			||||||
  * Returns 0 on success or a negative errno.
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
 int phylink_fwnode_phy_connect(struct phylink *pl,
 | 
					 | 
				
			||||||
-			       struct fwnode_handle *fwnode,
 | 
					 | 
				
			||||||
+			       const struct fwnode_handle *fwnode,
 | 
					 | 
				
			||||||
 			       u32 flags)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	struct fwnode_handle *phy_fwnode;
 | 
					 | 
				
			||||||
--- a/include/linux/phylink.h
 | 
					 | 
				
			||||||
+++ b/include/linux/phylink.h
 | 
					 | 
				
			||||||
@@ -568,16 +568,17 @@ void phylink_generic_validate(struct phy
 | 
					 | 
				
			||||||
 			      unsigned long *supported,
 | 
					 | 
				
			||||||
 			      struct phylink_link_state *state);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-struct phylink *phylink_create(struct phylink_config *, struct fwnode_handle *,
 | 
					 | 
				
			||||||
-			       phy_interface_t iface,
 | 
					 | 
				
			||||||
-			       const struct phylink_mac_ops *mac_ops);
 | 
					 | 
				
			||||||
+struct phylink *phylink_create(struct phylink_config *,
 | 
					 | 
				
			||||||
+			       const struct fwnode_handle *,
 | 
					 | 
				
			||||||
+			       phy_interface_t,
 | 
					 | 
				
			||||||
+			       const struct phylink_mac_ops *);
 | 
					 | 
				
			||||||
 void phylink_destroy(struct phylink *);
 | 
					 | 
				
			||||||
 bool phylink_expects_phy(struct phylink *pl);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 int phylink_connect_phy(struct phylink *, struct phy_device *);
 | 
					 | 
				
			||||||
 int phylink_of_phy_connect(struct phylink *, struct device_node *, u32 flags);
 | 
					 | 
				
			||||||
 int phylink_fwnode_phy_connect(struct phylink *pl,
 | 
					 | 
				
			||||||
-			       struct fwnode_handle *fwnode,
 | 
					 | 
				
			||||||
+			       const struct fwnode_handle *fwnode,
 | 
					 | 
				
			||||||
 			       u32 flags);
 | 
					 | 
				
			||||||
 void phylink_disconnect_phy(struct phylink *);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@ -1,38 +0,0 @@
 | 
				
			|||||||
From 4a0faa02d419a6728abef0f1d8a32d8c35ef95e6 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Date: Fri, 24 Mar 2023 09:23:53 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] net: phy: constify fwnode_get_phy_node() fwnode argument
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fwnode_get_phy_node() does not motify the fwnode structure, so make
 | 
					 | 
				
			||||||
the argument const,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Reviewed-by: Simon Horman <simon.horman@corigine.com>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/phy/phy_device.c | 2 +-
 | 
					 | 
				
			||||||
 include/linux/phy.h          | 2 +-
 | 
					 | 
				
			||||||
 2 files changed, 2 insertions(+), 2 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/phy_device.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/phy_device.c
 | 
					 | 
				
			||||||
@@ -3003,7 +3003,7 @@ EXPORT_SYMBOL_GPL(device_phy_find_device
 | 
					 | 
				
			||||||
  * and "phy-device" are not supported in ACPI. DT supports all the three
 | 
					 | 
				
			||||||
  * named references to the phy node.
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
-struct fwnode_handle *fwnode_get_phy_node(struct fwnode_handle *fwnode)
 | 
					 | 
				
			||||||
+struct fwnode_handle *fwnode_get_phy_node(const struct fwnode_handle *fwnode)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	struct fwnode_handle *phy_node;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
--- a/include/linux/phy.h
 | 
					 | 
				
			||||||
+++ b/include/linux/phy.h
 | 
					 | 
				
			||||||
@@ -1473,7 +1473,7 @@ int fwnode_get_phy_id(struct fwnode_hand
 | 
					 | 
				
			||||||
 struct mdio_device *fwnode_mdio_find_device(struct fwnode_handle *fwnode);
 | 
					 | 
				
			||||||
 struct phy_device *fwnode_phy_find_device(struct fwnode_handle *phy_fwnode);
 | 
					 | 
				
			||||||
 struct phy_device *device_phy_find_device(struct device *dev);
 | 
					 | 
				
			||||||
-struct fwnode_handle *fwnode_get_phy_node(struct fwnode_handle *fwnode);
 | 
					 | 
				
			||||||
+struct fwnode_handle *fwnode_get_phy_node(const struct fwnode_handle *fwnode);
 | 
					 | 
				
			||||||
 struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45);
 | 
					 | 
				
			||||||
 int phy_device_register(struct phy_device *phy);
 | 
					 | 
				
			||||||
 void phy_device_free(struct phy_device *phydev);
 | 
					 | 
				
			||||||
@ -1,44 +0,0 @@
 | 
				
			|||||||
From cc73de0411f7d3cdd157564a78f7a39058420ff8 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Date: Sat, 13 May 2023 22:03:45 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH 13/21] net: phylink: fix ksettings_set() ethtool call
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
While testing a Fiberstore SFP-10G-T module (which uses 10GBASE-R with
 | 
					 | 
				
			||||||
rate adaption) in a Clearfog platform (which can't do that) it was
 | 
					 | 
				
			||||||
found that the PHYs advertisement was not limited according to the
 | 
					 | 
				
			||||||
hosts capabilities when using ethtool to change it.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Fix this by ensuring that we mask the advertisement with the computed
 | 
					 | 
				
			||||||
support mask as the very first thing we do.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Fixes: cbc1bb1e4689 ("net: phylink: simplify phy case for ksettings_set method")
 | 
					 | 
				
			||||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/phy/phylink.c | 8 ++++----
 | 
					 | 
				
			||||||
 1 file changed, 4 insertions(+), 4 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/phylink.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/phylink.c
 | 
					 | 
				
			||||||
@@ -2226,6 +2226,10 @@ int phylink_ethtool_ksettings_set(struct
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	ASSERT_RTNL();
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	/* Mask out unsupported advertisements */
 | 
					 | 
				
			||||||
+	linkmode_and(config.advertising, kset->link_modes.advertising,
 | 
					 | 
				
			||||||
+		     pl->supported);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	if (pl->phydev) {
 | 
					 | 
				
			||||||
 		/* We can rely on phylib for this update; we also do not need
 | 
					 | 
				
			||||||
 		 * to update the pl->link_config settings:
 | 
					 | 
				
			||||||
@@ -2250,10 +2254,6 @@ int phylink_ethtool_ksettings_set(struct
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	config = pl->link_config;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	/* Mask out unsupported advertisements */
 | 
					 | 
				
			||||||
-	linkmode_and(config.advertising, kset->link_modes.advertising,
 | 
					 | 
				
			||||||
-		     pl->supported);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 	/* FIXME: should we reject autoneg if phy/mac does not support it? */
 | 
					 | 
				
			||||||
 	switch (kset->base.autoneg) {
 | 
					 | 
				
			||||||
 	case AUTONEG_DISABLE:
 | 
					 | 
				
			||||||
@ -1,149 +0,0 @@
 | 
				
			|||||||
From 0100d1c5789018ba77bf2f4fab3bd91ecece7b3b Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Date: Wed, 17 May 2023 11:38:12 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH 14/21] net: sfp: add support for setting signalling rate
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Add support to the SFP layer to allow phylink to set the signalling
 | 
					 | 
				
			||||||
rate for a SFP module. The rate given will be in units of kilo-baud
 | 
					 | 
				
			||||||
(1000 baud).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Reviewed-by: Simon Horman <simon.horman@corigine.com>
 | 
					 | 
				
			||||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/phy/phylink.c | 24 ++++++++++++++++++++++++
 | 
					 | 
				
			||||||
 drivers/net/phy/sfp-bus.c | 20 ++++++++++++++++++++
 | 
					 | 
				
			||||||
 drivers/net/phy/sfp.c     |  5 +++++
 | 
					 | 
				
			||||||
 drivers/net/phy/sfp.h     |  1 +
 | 
					 | 
				
			||||||
 include/linux/sfp.h       |  6 ++++++
 | 
					 | 
				
			||||||
 5 files changed, 56 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/phylink.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/phylink.c
 | 
					 | 
				
			||||||
@@ -156,6 +156,23 @@ static const char *phylink_an_mode_str(u
 | 
					 | 
				
			||||||
 	return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown";
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static unsigned int phylink_interface_signal_rate(phy_interface_t interface)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	switch (interface) {
 | 
					 | 
				
			||||||
+	case PHY_INTERFACE_MODE_SGMII:
 | 
					 | 
				
			||||||
+	case PHY_INTERFACE_MODE_1000BASEX: /* 1.25Mbd */
 | 
					 | 
				
			||||||
+		return 1250;
 | 
					 | 
				
			||||||
+	case PHY_INTERFACE_MODE_2500BASEX: /* 3.125Mbd */
 | 
					 | 
				
			||||||
+		return 3125;
 | 
					 | 
				
			||||||
+	case PHY_INTERFACE_MODE_5GBASER: /* 5.15625Mbd */
 | 
					 | 
				
			||||||
+		return 5156;
 | 
					 | 
				
			||||||
+	case PHY_INTERFACE_MODE_10GBASER: /* 10.3125Mbd */
 | 
					 | 
				
			||||||
+		return 10313;
 | 
					 | 
				
			||||||
+	default:
 | 
					 | 
				
			||||||
+		return 0;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 /**
 | 
					 | 
				
			||||||
  * phylink_interface_max_speed() - get the maximum speed of a phy interface
 | 
					 | 
				
			||||||
  * @interface: phy interface mode defined by &typedef phy_interface_t
 | 
					 | 
				
			||||||
@@ -1024,6 +1041,7 @@ static void phylink_major_config(struct
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	struct phylink_pcs *pcs = NULL;
 | 
					 | 
				
			||||||
 	bool pcs_changed = false;
 | 
					 | 
				
			||||||
+	unsigned int rate_kbd;
 | 
					 | 
				
			||||||
 	int err;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	phylink_dbg(pl, "major config %s\n", phy_modes(state->interface));
 | 
					 | 
				
			||||||
@@ -1083,6 +1101,12 @@ static void phylink_major_config(struct
 | 
					 | 
				
			||||||
 				    ERR_PTR(err));
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	if (pl->sfp_bus) {
 | 
					 | 
				
			||||||
+		rate_kbd = phylink_interface_signal_rate(state->interface);
 | 
					 | 
				
			||||||
+		if (rate_kbd)
 | 
					 | 
				
			||||||
+			sfp_upstream_set_signal_rate(pl->sfp_bus, rate_kbd);
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	phylink_pcs_poll_start(pl);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/sfp-bus.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/sfp-bus.c
 | 
					 | 
				
			||||||
@@ -586,6 +586,26 @@ static void sfp_upstream_clear(struct sf
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /**
 | 
					 | 
				
			||||||
+ * sfp_upstream_set_signal_rate() - set data signalling rate
 | 
					 | 
				
			||||||
+ * @bus: a pointer to the &struct sfp_bus structure for the sfp module
 | 
					 | 
				
			||||||
+ * @rate_kbd: signalling rate in units of 1000 baud
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Configure the rate select settings on the SFP module for the signalling
 | 
					 | 
				
			||||||
+ * rate (not the same as the data rate).
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Locks that may be held:
 | 
					 | 
				
			||||||
+ *  Phylink's state_mutex
 | 
					 | 
				
			||||||
+ *  rtnl lock
 | 
					 | 
				
			||||||
+ *  SFP's sm_mutex
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+void sfp_upstream_set_signal_rate(struct sfp_bus *bus, unsigned int rate_kbd)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	if (bus->registered)
 | 
					 | 
				
			||||||
+		bus->socket_ops->set_signal_rate(bus->sfp, rate_kbd);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL_GPL(sfp_upstream_set_signal_rate);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/**
 | 
					 | 
				
			||||||
  * sfp_bus_find_fwnode() - parse and locate the SFP bus from fwnode
 | 
					 | 
				
			||||||
  * @fwnode: firmware node for the parent device (MAC or PHY)
 | 
					 | 
				
			||||||
  *
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/sfp.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/sfp.c
 | 
					 | 
				
			||||||
@@ -2473,6 +2473,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 */
 | 
					 | 
				
			||||||
@@ -2551,6 +2555,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)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
@ -1,147 +0,0 @@
 | 
				
			|||||||
From b84acdb07222a701bfc6403b374249c86f97d18d Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Date: Fri, 19 May 2023 14:03:59 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH 15/21] net: phy: add helpers for comparing phy IDs
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
There are several places which open code comparing PHY IDs. Provide a
 | 
					 | 
				
			||||||
couple of helpers to assist with this, using a slightly simpler test
 | 
					 | 
				
			||||||
than the original:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- phy_id_compare() compares two arbitary PHY IDs and a mask of the
 | 
					 | 
				
			||||||
  significant bits in the ID.
 | 
					 | 
				
			||||||
- phydev_id_compare() compares the bound phydev with the specified
 | 
					 | 
				
			||||||
  PHY ID, using the bound driver's mask.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Reviewed-by: Simon Horman <simon.horman@corigine.com>
 | 
					 | 
				
			||||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | 
					 | 
				
			||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/phy/micrel.c     |  6 +++---
 | 
					 | 
				
			||||||
 drivers/net/phy/phy_device.c | 16 +++++++---------
 | 
					 | 
				
			||||||
 drivers/net/phy/phylink.c    |  4 ++--
 | 
					 | 
				
			||||||
 include/linux/phy.h          | 28 ++++++++++++++++++++++++++++
 | 
					 | 
				
			||||||
 4 files changed, 40 insertions(+), 14 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/micrel.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/micrel.c
 | 
					 | 
				
			||||||
@@ -620,7 +620,7 @@ static int ksz8051_ksz8795_match_phy_dev
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	int ret;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if ((phydev->phy_id & MICREL_PHY_ID_MASK) != PHY_ID_KSZ8051)
 | 
					 | 
				
			||||||
+	if (!phy_id_compare(phydev->phy_id, PHY_ID_KSZ8051, MICREL_PHY_ID_MASK))
 | 
					 | 
				
			||||||
 		return 0;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	ret = phy_read(phydev, MII_BMSR);
 | 
					 | 
				
			||||||
@@ -1455,7 +1455,7 @@ static int ksz9x31_cable_test_fault_leng
 | 
					 | 
				
			||||||
 	 *
 | 
					 | 
				
			||||||
 	 * distance to fault = (VCT_DATA - 22) * 4 / cable propagation velocity
 | 
					 | 
				
			||||||
 	 */
 | 
					 | 
				
			||||||
-	if ((phydev->phy_id & MICREL_PHY_ID_MASK) == PHY_ID_KSZ9131)
 | 
					 | 
				
			||||||
+	if (phydev_id_compare(phydev, PHY_ID_KSZ9131))
 | 
					 | 
				
			||||||
 		dt = clamp(dt - 22, 0, 255);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	return (dt * 400) / 10;
 | 
					 | 
				
			||||||
@@ -1887,7 +1887,7 @@ static __always_inline int ksz886x_cable
 | 
					 | 
				
			||||||
 	 */
 | 
					 | 
				
			||||||
 	dt = FIELD_GET(data_mask, status);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if ((phydev->phy_id & MICREL_PHY_ID_MASK) == PHY_ID_LAN8814)
 | 
					 | 
				
			||||||
+	if (phydev_id_compare(phydev, PHY_ID_LAN8814))
 | 
					 | 
				
			||||||
 		return ((dt - 22) * 800) / 10;
 | 
					 | 
				
			||||||
 	else
 | 
					 | 
				
			||||||
 		return (dt * 400) / 10;
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/phy_device.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/phy_device.c
 | 
					 | 
				
			||||||
@@ -422,8 +422,7 @@ int phy_unregister_fixup(const char *bus
 | 
					 | 
				
			||||||
 		fixup = list_entry(pos, struct phy_fixup, list);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 		if ((!strcmp(fixup->bus_id, bus_id)) &&
 | 
					 | 
				
			||||||
-		    ((fixup->phy_uid & phy_uid_mask) ==
 | 
					 | 
				
			||||||
-		     (phy_uid & phy_uid_mask))) {
 | 
					 | 
				
			||||||
+		    phy_id_compare(fixup->phy_uid, phy_uid, phy_uid_mask)) {
 | 
					 | 
				
			||||||
 			list_del(&fixup->list);
 | 
					 | 
				
			||||||
 			kfree(fixup);
 | 
					 | 
				
			||||||
 			ret = 0;
 | 
					 | 
				
			||||||
@@ -459,8 +458,8 @@ static int phy_needs_fixup(struct phy_de
 | 
					 | 
				
			||||||
 		if (strcmp(fixup->bus_id, PHY_ANY_ID) != 0)
 | 
					 | 
				
			||||||
 			return 0;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if ((fixup->phy_uid & fixup->phy_uid_mask) !=
 | 
					 | 
				
			||||||
-	    (phydev->phy_id & fixup->phy_uid_mask))
 | 
					 | 
				
			||||||
+	if (!phy_id_compare(phydev->phy_id, fixup->phy_uid,
 | 
					 | 
				
			||||||
+			    fixup->phy_uid_mask))
 | 
					 | 
				
			||||||
 		if (fixup->phy_uid != PHY_ANY_UID)
 | 
					 | 
				
			||||||
 			return 0;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -507,15 +506,14 @@ static int phy_bus_match(struct device *
 | 
					 | 
				
			||||||
 			if (phydev->c45_ids.device_ids[i] == 0xffffffff)
 | 
					 | 
				
			||||||
 				continue;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-			if ((phydrv->phy_id & phydrv->phy_id_mask) ==
 | 
					 | 
				
			||||||
-			    (phydev->c45_ids.device_ids[i] &
 | 
					 | 
				
			||||||
-			     phydrv->phy_id_mask))
 | 
					 | 
				
			||||||
+			if (phy_id_compare(phydev->c45_ids.device_ids[i],
 | 
					 | 
				
			||||||
+					   phydrv->phy_id, phydrv->phy_id_mask))
 | 
					 | 
				
			||||||
 				return 1;
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
 		return 0;
 | 
					 | 
				
			||||||
 	} else {
 | 
					 | 
				
			||||||
-		return (phydrv->phy_id & phydrv->phy_id_mask) ==
 | 
					 | 
				
			||||||
-			(phydev->phy_id & phydrv->phy_id_mask);
 | 
					 | 
				
			||||||
+		return phy_id_compare(phydev->phy_id, phydrv->phy_id,
 | 
					 | 
				
			||||||
+				      phydrv->phy_id_mask);
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/phylink.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/phylink.c
 | 
					 | 
				
			||||||
@@ -3151,8 +3151,8 @@ static void phylink_sfp_link_up(void *up
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
 static bool phylink_phy_no_inband(struct phy_device *phy)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
-	return phy->is_c45 &&
 | 
					 | 
				
			||||||
-		(phy->c45_ids.device_ids[1] & 0xfffffff0) == 0xae025150;
 | 
					 | 
				
			||||||
+	return phy->is_c45 && phy_id_compare(phy->c45_ids.device_ids[1],
 | 
					 | 
				
			||||||
+					     0xae025150, 0xfffffff0);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy)
 | 
					 | 
				
			||||||
--- a/include/linux/phy.h
 | 
					 | 
				
			||||||
+++ b/include/linux/phy.h
 | 
					 | 
				
			||||||
@@ -993,6 +993,34 @@ struct phy_driver {
 | 
					 | 
				
			||||||
 #define PHY_ID_MATCH_MODEL(id) .phy_id = (id), .phy_id_mask = GENMASK(31, 4)
 | 
					 | 
				
			||||||
 #define PHY_ID_MATCH_VENDOR(id) .phy_id = (id), .phy_id_mask = GENMASK(31, 10)
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+/**
 | 
					 | 
				
			||||||
+ * phy_id_compare - compare @id1 with @id2 taking account of @mask
 | 
					 | 
				
			||||||
+ * @id1: first PHY ID
 | 
					 | 
				
			||||||
+ * @id2: second PHY ID
 | 
					 | 
				
			||||||
+ * @mask: the PHY ID mask, set bits are significant in matching
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Return true if the bits from @id1 and @id2 specified by @mask match.
 | 
					 | 
				
			||||||
+ * This uses an equivalent test to (@id & @mask) == (@phy_id & @mask).
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+static inline bool phy_id_compare(u32 id1, u32 id2, u32 mask)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return !((id1 ^ id2) & mask);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/**
 | 
					 | 
				
			||||||
+ * phydev_id_compare - compare @id with the PHY's Clause 22 ID
 | 
					 | 
				
			||||||
+ * @phydev: the PHY device
 | 
					 | 
				
			||||||
+ * @id: the PHY ID to be matched
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Compare the @phydev clause 22 ID with the provided @id and return true or
 | 
					 | 
				
			||||||
+ * false depending whether it matches, using the bound driver mask. The
 | 
					 | 
				
			||||||
+ * @phydev must be bound to a driver.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+static inline bool phydev_id_compare(struct phy_device *phydev, u32 id)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return phy_id_compare(id, phydev->phy_id, phydev->drv->phy_id_mask);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 /* A Structure for boards to register fixups with the PHY Lib */
 | 
					 | 
				
			||||||
 struct phy_fixup {
 | 
					 | 
				
			||||||
 	struct list_head list;
 | 
					 | 
				
			||||||
@ -1,71 +0,0 @@
 | 
				
			|||||||
From 441e1e44301fc5762a06737f8ec04bf1ce3fb039 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Date: Sat, 20 May 2023 11:41:42 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH 16/21] net: phylink: require supported_interfaces to be filled
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
We have been requiring the supported_interfaces bitmap to be filled in
 | 
					 | 
				
			||||||
by MAC drivers that have a mac_select_pcs() method. Now that all MAC
 | 
					 | 
				
			||||||
drivers fill in the supported_interfaces bitmap, it is time to enforce
 | 
					 | 
				
			||||||
this. We have already required supported_interfaces to be set in order
 | 
					 | 
				
			||||||
for optical SFPs to be configured in commit f81fa96d8a6c ("net: phylink:
 | 
					 | 
				
			||||||
use phy_interface_t bitmaps for optical modules").
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Refuse phylink creation if supported_interfaces is empty, and remove
 | 
					 | 
				
			||||||
code to deal with cases where this mask is empty.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | 
					 | 
				
			||||||
Link: https://lore.kernel.org/r/E1q0K1u-006EIP-ET@rmk-PC.armlinux.org.uk
 | 
					 | 
				
			||||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/phy/phylink.c | 26 +++++++++++---------------
 | 
					 | 
				
			||||||
 1 file changed, 11 insertions(+), 15 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/phylink.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/phylink.c
 | 
					 | 
				
			||||||
@@ -710,14 +710,11 @@ static int phylink_validate(struct phyli
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	const unsigned long *interfaces = pl->config->supported_interfaces;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if (!phy_interface_empty(interfaces)) {
 | 
					 | 
				
			||||||
-		if (state->interface == PHY_INTERFACE_MODE_NA)
 | 
					 | 
				
			||||||
-			return phylink_validate_mask(pl, supported, state,
 | 
					 | 
				
			||||||
-						     interfaces);
 | 
					 | 
				
			||||||
+	if (state->interface == PHY_INTERFACE_MODE_NA)
 | 
					 | 
				
			||||||
+		return phylink_validate_mask(pl, supported, state, interfaces);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-		if (!test_bit(state->interface, interfaces))
 | 
					 | 
				
			||||||
-			return -EINVAL;
 | 
					 | 
				
			||||||
-	}
 | 
					 | 
				
			||||||
+	if (!test_bit(state->interface, interfaces))
 | 
					 | 
				
			||||||
+		return -EINVAL;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	return phylink_validate_mac_and_pcs(pl, supported, state);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
@@ -1512,19 +1509,18 @@ struct phylink *phylink_create(struct ph
 | 
					 | 
				
			||||||
 	struct phylink *pl;
 | 
					 | 
				
			||||||
 	int ret;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if (mac_ops->mac_select_pcs &&
 | 
					 | 
				
			||||||
-	    mac_ops->mac_select_pcs(config, PHY_INTERFACE_MODE_NA) !=
 | 
					 | 
				
			||||||
-	      ERR_PTR(-EOPNOTSUPP))
 | 
					 | 
				
			||||||
-		using_mac_select_pcs = true;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 	/* Validate the supplied configuration */
 | 
					 | 
				
			||||||
-	if (using_mac_select_pcs &&
 | 
					 | 
				
			||||||
-	    phy_interface_empty(config->supported_interfaces)) {
 | 
					 | 
				
			||||||
+	if (phy_interface_empty(config->supported_interfaces)) {
 | 
					 | 
				
			||||||
 		dev_err(config->dev,
 | 
					 | 
				
			||||||
-			"phylink: error: empty supported_interfaces but mac_select_pcs() method present\n");
 | 
					 | 
				
			||||||
+			"phylink: error: empty supported_interfaces\n");
 | 
					 | 
				
			||||||
 		return ERR_PTR(-EINVAL);
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	if (mac_ops->mac_select_pcs &&
 | 
					 | 
				
			||||||
+	    mac_ops->mac_select_pcs(config, PHY_INTERFACE_MODE_NA) !=
 | 
					 | 
				
			||||||
+	      ERR_PTR(-EOPNOTSUPP))
 | 
					 | 
				
			||||||
+		using_mac_select_pcs = true;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	pl = kzalloc(sizeof(*pl), GFP_KERNEL);
 | 
					 | 
				
			||||||
 	if (!pl)
 | 
					 | 
				
			||||||
 		return ERR_PTR(-ENOMEM);
 | 
					 | 
				
			||||||
@ -1,64 +0,0 @@
 | 
				
			|||||||
From 4b624a39f2ab523ca6a6ad9448fab1deb7b101e2 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Date: Tue, 23 May 2023 11:15:53 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH 17/21] net: phylink: remove duplicated linkmode pause
 | 
					 | 
				
			||||||
 resolution
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Phylink had two chunks of code virtually the same for resolving the
 | 
					 | 
				
			||||||
negotiated pause modes. Factor this down to one function.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | 
					 | 
				
			||||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/phy/phylink.c | 15 ++++-----------
 | 
					 | 
				
			||||||
 1 file changed, 4 insertions(+), 11 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/phylink.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/phylink.c
 | 
					 | 
				
			||||||
@@ -976,11 +976,10 @@ static void phylink_apply_manual_flow(st
 | 
					 | 
				
			||||||
 		state->pause = pl->link_config.pause;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-static void phylink_resolve_flow(struct phylink_link_state *state)
 | 
					 | 
				
			||||||
+static void phylink_resolve_an_pause(struct phylink_link_state *state)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	bool tx_pause, rx_pause;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	state->pause = MLO_PAUSE_NONE;
 | 
					 | 
				
			||||||
 	if (state->duplex == DUPLEX_FULL) {
 | 
					 | 
				
			||||||
 		linkmode_resolve_pause(state->advertising,
 | 
					 | 
				
			||||||
 				       state->lp_advertising,
 | 
					 | 
				
			||||||
@@ -1192,7 +1191,8 @@ static void phylink_get_fixed_state(stru
 | 
					 | 
				
			||||||
 	else if (pl->link_gpio)
 | 
					 | 
				
			||||||
 		state->link = !!gpiod_get_value_cansleep(pl->link_gpio);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	phylink_resolve_flow(state);
 | 
					 | 
				
			||||||
+	state->pause = MLO_PAUSE_NONE;
 | 
					 | 
				
			||||||
+	phylink_resolve_an_pause(state);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static void phylink_mac_initial_config(struct phylink *pl, bool force_restart)
 | 
					 | 
				
			||||||
@@ -3215,7 +3215,6 @@ static const struct sfp_upstream_ops sfp
 | 
					 | 
				
			||||||
 static void phylink_decode_c37_word(struct phylink_link_state *state,
 | 
					 | 
				
			||||||
 				    uint16_t config_reg, int speed)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
-	bool tx_pause, rx_pause;
 | 
					 | 
				
			||||||
 	int fd_bit;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	if (speed == SPEED_2500)
 | 
					 | 
				
			||||||
@@ -3234,13 +3233,7 @@ static void phylink_decode_c37_word(stru
 | 
					 | 
				
			||||||
 		state->link = false;
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	linkmode_resolve_pause(state->advertising, state->lp_advertising,
 | 
					 | 
				
			||||||
-			       &tx_pause, &rx_pause);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	if (tx_pause)
 | 
					 | 
				
			||||||
-		state->pause |= MLO_PAUSE_TX;
 | 
					 | 
				
			||||||
-	if (rx_pause)
 | 
					 | 
				
			||||||
-		state->pause |= MLO_PAUSE_RX;
 | 
					 | 
				
			||||||
+	phylink_resolve_an_pause(state);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static void phylink_decode_sgmii_word(struct phylink_link_state *state,
 | 
					 | 
				
			||||||
@ -1,76 +0,0 @@
 | 
				
			|||||||
From aa8b6bd2b1f235b262bd27f317a0516f196c2c6a Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Date: Tue, 23 May 2023 11:15:58 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH 18/21] net: phylink: add function to resolve clause 73
 | 
					 | 
				
			||||||
 negotiation
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Add a function to resolve clause 73 negotiation according to the
 | 
					 | 
				
			||||||
priority resolution function described in clause 73.3.6.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
 | 
					 | 
				
			||||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | 
					 | 
				
			||||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/phy/phylink.c | 39 +++++++++++++++++++++++++++++++++++++++
 | 
					 | 
				
			||||||
 include/linux/phylink.h   |  2 ++
 | 
					 | 
				
			||||||
 2 files changed, 41 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/phylink.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/phylink.c
 | 
					 | 
				
			||||||
@@ -3212,6 +3212,45 @@ static const struct sfp_upstream_ops sfp
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /* Helpers for MAC drivers */
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static struct {
 | 
					 | 
				
			||||||
+	int bit;
 | 
					 | 
				
			||||||
+	int speed;
 | 
					 | 
				
			||||||
+} phylink_c73_priority_resolution[] = {
 | 
					 | 
				
			||||||
+	{ ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, SPEED_100000 },
 | 
					 | 
				
			||||||
+	{ ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT, SPEED_100000 },
 | 
					 | 
				
			||||||
+	/* 100GBASE-KP4 and 100GBASE-CR10 not supported */
 | 
					 | 
				
			||||||
+	{ ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, SPEED_40000 },
 | 
					 | 
				
			||||||
+	{ ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, SPEED_40000 },
 | 
					 | 
				
			||||||
+	{ ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, SPEED_10000 },
 | 
					 | 
				
			||||||
+	{ ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, SPEED_10000 },
 | 
					 | 
				
			||||||
+	/* 5GBASE-KR not supported */
 | 
					 | 
				
			||||||
+	{ ETHTOOL_LINK_MODE_2500baseX_Full_BIT, SPEED_2500 },
 | 
					 | 
				
			||||||
+	{ ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, SPEED_1000 },
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+void phylink_resolve_c73(struct phylink_link_state *state)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int i;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	for (i = 0; i < ARRAY_SIZE(phylink_c73_priority_resolution); i++) {
 | 
					 | 
				
			||||||
+		int bit = phylink_c73_priority_resolution[i].bit;
 | 
					 | 
				
			||||||
+		if (linkmode_test_bit(bit, state->advertising) &&
 | 
					 | 
				
			||||||
+		    linkmode_test_bit(bit, state->lp_advertising))
 | 
					 | 
				
			||||||
+			break;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (i < ARRAY_SIZE(phylink_c73_priority_resolution)) {
 | 
					 | 
				
			||||||
+		state->speed = phylink_c73_priority_resolution[i].speed;
 | 
					 | 
				
			||||||
+		state->duplex = DUPLEX_FULL;
 | 
					 | 
				
			||||||
+	} else {
 | 
					 | 
				
			||||||
+		/* negotiation failure */
 | 
					 | 
				
			||||||
+		state->link = false;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	phylink_resolve_an_pause(state);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL_GPL(phylink_resolve_c73);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static void phylink_decode_c37_word(struct phylink_link_state *state,
 | 
					 | 
				
			||||||
 				    uint16_t config_reg, int speed)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
--- a/include/linux/phylink.h
 | 
					 | 
				
			||||||
+++ b/include/linux/phylink.h
 | 
					 | 
				
			||||||
@@ -656,6 +656,8 @@ int phylink_mii_c22_pcs_config(struct md
 | 
					 | 
				
			||||||
 			       const unsigned long *advertising);
 | 
					 | 
				
			||||||
 void phylink_mii_c22_pcs_an_restart(struct mdio_device *pcs);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+void phylink_resolve_c73(struct phylink_link_state *state);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 void phylink_mii_c45_pcs_get_state(struct mdio_device *pcs,
 | 
					 | 
				
			||||||
 				   struct phylink_link_state *state);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue
	
	Block a user