diff --git a/config/Config-kernel.in b/config/Config-kernel.in index 8a78e297cb..3350558adb 100644 --- a/config/Config-kernel.in +++ b/config/Config-kernel.in @@ -365,10 +365,6 @@ config KERNEL_KPROBES instrumentation and testing. If in doubt, say "N". -config KERNEL_KPROBE_EVENT - bool - default y if KERNEL_KPROBES - config KERNEL_KPROBE_EVENTS bool default y if KERNEL_KPROBES diff --git a/include/kernel-version.mk b/include/kernel-version.mk index 5f8614f060..4a61601ce5 100644 --- a/include/kernel-version.mk +++ b/include/kernel-version.mk @@ -6,9 +6,9 @@ ifdef CONFIG_TESTING_KERNEL KERNEL_PATCHVER:=$(KERNEL_TESTING_PATCHVER) endif -LINUX_VERSION-5.4 = .91 +LINUX_VERSION-5.4 = .92 -LINUX_KERNEL_HASH-5.4.91 = 0e0161bb034b9ba59e58a20985e49ecfb38104586602f53f37b382f508fc5c17 +LINUX_KERNEL_HASH-5.4.92 = c0937ff98824c4b14cfea68a04340e0beb3c00f1cc02984daf2f3bdf542394fd remove_uri_prefix=$(subst git://,,$(subst http://,,$(subst https://,,$(1)))) sanitize_uri=$(call qstrip,$(subst @,_,$(subst :,_,$(subst .,_,$(subst -,_,$(subst /,_,$(1))))))) diff --git a/package/base-files/Makefile b/package/base-files/Makefile index d134641cb9..8a1ddf96f5 100644 --- a/package/base-files/Makefile +++ b/package/base-files/Makefile @@ -1,5 +1,5 @@ # -# Copyright (C) 2007-2016 OpenWrt.org +# Copyright (C) 2007-2021 OpenWrt.org # Copyright (C) 2010 Vertical Communications # # This is free software, licensed under the GNU General Public License v2. diff --git a/package/kernel/mac80211/patches/subsys/346-mac80211-minstrel_ht-use-bitfields-to-encode-rate-in.patch b/package/kernel/mac80211/patches/subsys/346-mac80211-minstrel_ht-use-bitfields-to-encode-rate-in.patch new file mode 100644 index 0000000000..6aa6f0ed93 --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/346-mac80211-minstrel_ht-use-bitfields-to-encode-rate-in.patch @@ -0,0 +1,412 @@ +From: Felix Fietkau +Date: Thu, 21 Jan 2021 18:29:30 +0100 +Subject: [PATCH] mac80211: minstrel_ht: use bitfields to encode rate + indexes + +Get rid of a lot of divisions and modulo operations +Reduces code size and improves performance + +Signed-off-by: Felix Fietkau +--- + +--- a/net/mac80211/rc80211_minstrel_ht.c ++++ b/net/mac80211/rc80211_minstrel_ht.c +@@ -379,14 +379,14 @@ out: + static inline struct minstrel_rate_stats * + minstrel_get_ratestats(struct minstrel_ht_sta *mi, int index) + { +- return &mi->groups[index / MCS_GROUP_RATES].rates[index % MCS_GROUP_RATES]; ++ return &mi->groups[MI_RATE_GROUP(index)].rates[MI_RATE_IDX(index)]; + } + +-static inline int +-minstrel_get_duration(int index) ++static inline int minstrel_get_duration(int index) + { +- const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES]; +- unsigned int duration = group->duration[index % MCS_GROUP_RATES]; ++ const struct mcs_group *group = &minstrel_mcs_groups[MI_RATE_GROUP(index)]; ++ unsigned int duration = group->duration[MI_RATE_IDX(index)]; ++ + return duration << group->shift; + } + +@@ -398,7 +398,7 @@ minstrel_ht_avg_ampdu_len(struct minstre + if (mi->avg_ampdu_len) + return MINSTREL_TRUNC(mi->avg_ampdu_len); + +- if (minstrel_ht_is_legacy_group(mi->max_tp_rate[0] / MCS_GROUP_RATES)) ++ if (minstrel_ht_is_legacy_group(MI_RATE_GROUP(mi->max_tp_rate[0]))) + return 1; + + duration = minstrel_get_duration(mi->max_tp_rate[0]); +@@ -465,14 +465,14 @@ minstrel_ht_sort_best_tp_rates(struct mi + int tmp_group, tmp_idx, tmp_tp_avg, tmp_prob; + int j = MAX_THR_RATES; + +- cur_group = index / MCS_GROUP_RATES; +- cur_idx = index % MCS_GROUP_RATES; ++ cur_group = MI_RATE_GROUP(index); ++ cur_idx = MI_RATE_IDX(index); + cur_prob = mi->groups[cur_group].rates[cur_idx].prob_avg; + cur_tp_avg = minstrel_ht_get_tp_avg(mi, cur_group, cur_idx, cur_prob); + + do { +- tmp_group = tp_list[j - 1] / MCS_GROUP_RATES; +- tmp_idx = tp_list[j - 1] % MCS_GROUP_RATES; ++ tmp_group = MI_RATE_GROUP(tp_list[j - 1]); ++ tmp_idx = MI_RATE_IDX(tp_list[j - 1]); + tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_avg; + tmp_tp_avg = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, + tmp_prob); +@@ -504,23 +504,23 @@ minstrel_ht_set_best_prob_rate(struct mi + int max_gpr_group, max_gpr_idx; + int max_gpr_tp_avg, max_gpr_prob; + +- cur_group = index / MCS_GROUP_RATES; +- cur_idx = index % MCS_GROUP_RATES; +- mg = &mi->groups[index / MCS_GROUP_RATES]; +- mrs = &mg->rates[index % MCS_GROUP_RATES]; ++ cur_group = MI_RATE_GROUP(index); ++ cur_idx = MI_RATE_IDX(index); ++ mg = &mi->groups[cur_group]; ++ mrs = &mg->rates[cur_idx]; + +- tmp_group = *dest / MCS_GROUP_RATES; +- tmp_idx = *dest % MCS_GROUP_RATES; ++ tmp_group = MI_RATE_GROUP(*dest); ++ tmp_idx = MI_RATE_IDX(*dest); + tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_avg; + tmp_tp_avg = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, tmp_prob); + + /* if max_tp_rate[0] is from MCS_GROUP max_prob_rate get selected from + * MCS_GROUP as well as CCK_GROUP rates do not allow aggregation */ +- max_tp_group = mi->max_tp_rate[0] / MCS_GROUP_RATES; +- max_tp_idx = mi->max_tp_rate[0] % MCS_GROUP_RATES; ++ max_tp_group = MI_RATE_GROUP(mi->max_tp_rate[0]); ++ max_tp_idx = MI_RATE_IDX(mi->max_tp_rate[0]); + max_tp_prob = mi->groups[max_tp_group].rates[max_tp_idx].prob_avg; + +- if (minstrel_ht_is_legacy_group(index / MCS_GROUP_RATES) && ++ if (minstrel_ht_is_legacy_group(MI_RATE_GROUP(index)) && + !minstrel_ht_is_legacy_group(max_tp_group)) + return; + +@@ -529,8 +529,8 @@ minstrel_ht_set_best_prob_rate(struct mi + mrs->prob_avg < max_tp_prob) + return; + +- max_gpr_group = mg->max_group_prob_rate / MCS_GROUP_RATES; +- max_gpr_idx = mg->max_group_prob_rate % MCS_GROUP_RATES; ++ max_gpr_group = MI_RATE_GROUP(mg->max_group_prob_rate); ++ max_gpr_idx = MI_RATE_IDX(mg->max_group_prob_rate); + max_gpr_prob = mi->groups[max_gpr_group].rates[max_gpr_idx].prob_avg; + + if (mrs->prob_avg > MINSTREL_FRAC(75, 100)) { +@@ -567,13 +567,13 @@ minstrel_ht_assign_best_tp_rates(struct + unsigned int tmp_group, tmp_idx, tmp_cck_tp, tmp_mcs_tp, tmp_prob; + int i; + +- tmp_group = tmp_legacy_tp_rate[0] / MCS_GROUP_RATES; +- tmp_idx = tmp_legacy_tp_rate[0] % MCS_GROUP_RATES; ++ tmp_group = MI_RATE_GROUP(tmp_legacy_tp_rate[0]); ++ tmp_idx = MI_RATE_IDX(tmp_legacy_tp_rate[0]); + tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_avg; + tmp_cck_tp = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, tmp_prob); + +- tmp_group = tmp_mcs_tp_rate[0] / MCS_GROUP_RATES; +- tmp_idx = tmp_mcs_tp_rate[0] % MCS_GROUP_RATES; ++ tmp_group = MI_RATE_GROUP(tmp_mcs_tp_rate[0]); ++ tmp_idx = MI_RATE_IDX(tmp_mcs_tp_rate[0]); + tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_avg; + tmp_mcs_tp = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, tmp_prob); + +@@ -600,14 +600,14 @@ minstrel_ht_prob_rate_reduce_streams(str + if (!mi->sta->ht_cap.ht_supported) + return; + +- tmp_max_streams = minstrel_mcs_groups[mi->max_tp_rate[0] / +- MCS_GROUP_RATES].streams; ++ group = MI_RATE_GROUP(mi->max_tp_rate[0]); ++ tmp_max_streams = minstrel_mcs_groups[group].streams; + for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) { + mg = &mi->groups[group]; + if (!mi->supported[group] || group == MINSTREL_CCK_GROUP) + continue; + +- tmp_idx = mg->max_group_prob_rate % MCS_GROUP_RATES; ++ tmp_idx = MI_RATE_IDX(mg->max_group_prob_rate); + tmp_prob = mi->groups[group].rates[tmp_idx].prob_avg; + + if (tmp_tp < minstrel_ht_get_tp_avg(mi, group, tmp_idx, tmp_prob) && +@@ -644,8 +644,8 @@ minstrel_ht_find_probe_rates(struct mins + int i, g, max_dur; + int tp_idx; + +- tp_group = &minstrel_mcs_groups[mi->max_tp_rate[0] / MCS_GROUP_RATES]; +- tp_idx = mi->max_tp_rate[0] % MCS_GROUP_RATES; ++ tp_group = &minstrel_mcs_groups[MI_RATE_GROUP(mi->max_tp_rate[0])]; ++ tp_idx = MI_RATE_IDX(mi->max_tp_rate[0]); + + max_dur = minstrel_get_duration(mi->max_tp_rate[0]); + if (faster_rate) +@@ -670,7 +670,7 @@ minstrel_ht_find_probe_rates(struct mins + if ((group->duration[i] << group->shift) > max_dur) + continue; + +- idx = g * MCS_GROUP_RATES + i; ++ idx = MI_RATE(g, i); + if (idx == mi->max_tp_rate[0]) + continue; + +@@ -712,10 +712,10 @@ minstrel_ht_rate_sample_switch(struct mi + + /* If no suitable rate was found, try to pick the next one in the group */ + if (!n_rates) { +- int g_idx = mi->max_tp_rate[0] / MCS_GROUP_RATES; ++ int g_idx = MI_RATE_GROUP(mi->max_tp_rate[0]); + u16 supported = mi->supported[g_idx]; + +- supported >>= mi->max_tp_rate[0] % MCS_GROUP_RATES; ++ supported >>= MI_RATE_IDX(mi->max_tp_rate[0]); + for (i = 0; supported; supported >>= 1, i++) { + if (!(supported & 1)) + continue; +@@ -854,24 +854,27 @@ minstrel_ht_update_stats(struct minstrel + mi->sample_slow = 0; + mi->sample_count = 0; + +- memset(tmp_mcs_tp_rate, 0, sizeof(tmp_mcs_tp_rate)); +- memset(tmp_legacy_tp_rate, 0, sizeof(tmp_legacy_tp_rate)); + if (mi->supported[MINSTREL_CCK_GROUP]) +- for (j = 0; j < ARRAY_SIZE(tmp_legacy_tp_rate); j++) +- tmp_legacy_tp_rate[j] = MINSTREL_CCK_GROUP * MCS_GROUP_RATES; ++ group = MINSTREL_CCK_GROUP; + else if (mi->supported[MINSTREL_OFDM_GROUP]) +- for (j = 0; j < ARRAY_SIZE(tmp_legacy_tp_rate); j++) +- tmp_legacy_tp_rate[j] = MINSTREL_OFDM_GROUP * MCS_GROUP_RATES; ++ group = MINSTREL_OFDM_GROUP; ++ else ++ group = 0; ++ ++ index = MI_RATE(group, 0); ++ for (j = 0; j < ARRAY_SIZE(tmp_legacy_tp_rate); j++) ++ tmp_legacy_tp_rate[j] = index; + + if (mi->supported[MINSTREL_VHT_GROUP_0]) +- index = MINSTREL_VHT_GROUP_0 * MCS_GROUP_RATES; ++ group = MINSTREL_VHT_GROUP_0; + else if (ht_supported) +- index = MINSTREL_HT_GROUP_0 * MCS_GROUP_RATES; ++ group = MINSTREL_HT_GROUP_0; + else if (mi->supported[MINSTREL_CCK_GROUP]) +- index = MINSTREL_CCK_GROUP * MCS_GROUP_RATES; ++ group = MINSTREL_CCK_GROUP; + else +- index = MINSTREL_OFDM_GROUP * MCS_GROUP_RATES; ++ group = MINSTREL_OFDM_GROUP; + ++ index = MI_RATE(group, 0); + tmp_max_prob_rate = index; + for (j = 0; j < ARRAY_SIZE(tmp_mcs_tp_rate); j++) + tmp_mcs_tp_rate[j] = index; +@@ -888,7 +891,7 @@ minstrel_ht_update_stats(struct minstrel + + /* (re)Initialize group rate indexes */ + for(j = 0; j < MAX_THR_RATES; j++) +- tmp_group_tp_rate[j] = MCS_GROUP_RATES * group; ++ tmp_group_tp_rate[j] = MI_RATE(group, 0); + + if (group == MINSTREL_CCK_GROUP && ht_supported) + tp_rate = tmp_legacy_tp_rate; +@@ -897,7 +900,7 @@ minstrel_ht_update_stats(struct minstrel + if (!(mi->supported[group] & BIT(i))) + continue; + +- index = MCS_GROUP_RATES * group + i; ++ index = MI_RATE(group, i); + + mrs = &mg->rates[i]; + mrs->retry_updated = false; +@@ -929,13 +932,13 @@ minstrel_ht_update_stats(struct minstrel + continue; + + mg = &mi->groups[group]; +- mg->max_group_prob_rate = MCS_GROUP_RATES * group; ++ mg->max_group_prob_rate = MI_RATE(group, 0); + + for (i = 0; i < MCS_GROUP_RATES; i++) { + if (!(mi->supported[group] & BIT(i))) + continue; + +- index = MCS_GROUP_RATES * group + i; ++ index = MI_RATE(group, i); + + /* Find max probability rate per group and global */ + minstrel_ht_set_best_prob_rate(mi, &tmp_max_prob_rate, +@@ -1022,7 +1025,7 @@ minstrel_downgrade_rate(struct minstrel_ + { + int group, orig_group; + +- orig_group = group = *idx / MCS_GROUP_RATES; ++ orig_group = group = MI_RATE_GROUP(*idx); + while (group > 0) { + group--; + +@@ -1206,7 +1209,7 @@ minstrel_calc_retransmit(struct minstrel + ctime += (t_slot * cw) >> 1; + cw = min((cw << 1) | 1, mp->cw_max); + +- if (minstrel_ht_is_legacy_group(index / MCS_GROUP_RATES)) { ++ if (minstrel_ht_is_legacy_group(MI_RATE_GROUP(index))) { + overhead = mi->overhead_legacy; + overhead_rtscts = mi->overhead_legacy_rtscts; + } else { +@@ -1239,7 +1242,7 @@ static void + minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, + struct ieee80211_sta_rates *ratetbl, int offset, int index) + { +- int group_idx = index / MCS_GROUP_RATES; ++ int group_idx = MI_RATE_GROUP(index); + const struct mcs_group *group = &minstrel_mcs_groups[group_idx]; + struct minstrel_rate_stats *mrs; + u8 idx; +@@ -1259,7 +1262,7 @@ minstrel_ht_set_rate(struct minstrel_pri + ratetbl->rate[offset].count_rts = mrs->retry_count_rtscts; + } + +- index %= MCS_GROUP_RATES; ++ index = MI_RATE_IDX(index); + if (group_idx == MINSTREL_CCK_GROUP) + idx = mp->cck_rates[index % ARRAY_SIZE(mp->cck_rates)]; + else if (group_idx == MINSTREL_OFDM_GROUP) +@@ -1289,17 +1292,17 @@ minstrel_ht_set_rate(struct minstrel_pri + static inline int + minstrel_ht_get_prob_avg(struct minstrel_ht_sta *mi, int rate) + { +- int group = rate / MCS_GROUP_RATES; +- rate %= MCS_GROUP_RATES; ++ int group = MI_RATE_GROUP(rate); ++ rate = MI_RATE_IDX(rate); + return mi->groups[group].rates[rate].prob_avg; + } + + static int + minstrel_ht_get_max_amsdu_len(struct minstrel_ht_sta *mi) + { +- int group = mi->max_prob_rate / MCS_GROUP_RATES; ++ int group = MI_RATE_GROUP(mi->max_prob_rate); + const struct mcs_group *g = &minstrel_mcs_groups[group]; +- int rate = mi->max_prob_rate % MCS_GROUP_RATES; ++ int rate = MI_RATE_IDX(mi->max_prob_rate); + unsigned int duration; + + /* Disable A-MSDU if max_prob_rate is bad */ +@@ -1405,7 +1408,7 @@ minstrel_get_sample_rate(struct minstrel + return -1; + + mrs = &mg->rates[sample_idx]; +- sample_idx += sample_group * MCS_GROUP_RATES; ++ sample_idx += MI_RATE(sample_group, 0); + + tp_rate1 = mi->max_tp_rate[0]; + +@@ -1455,8 +1458,7 @@ minstrel_get_sample_rate(struct minstrel + * if the link is working perfectly. + */ + +- cur_max_tp_streams = minstrel_mcs_groups[tp_rate1 / +- MCS_GROUP_RATES].streams; ++ cur_max_tp_streams = minstrel_mcs_groups[MI_RATE_GROUP(tp_rate1)].streams; + if (sample_dur >= minstrel_get_duration(tp_rate2) && + (cur_max_tp_streams - 1 < + minstrel_mcs_groups[sample_group].streams || +@@ -1484,7 +1486,7 @@ minstrel_ht_get_rate(void *priv, struct + int sample_idx; + + if (!(info->flags & IEEE80211_TX_CTL_AMPDU) && +- !minstrel_ht_is_legacy_group(mi->max_prob_rate / MCS_GROUP_RATES)) ++ !minstrel_ht_is_legacy_group(MI_RATE_GROUP(mi->max_prob_rate))) + minstrel_aggr_check(sta, txrc->skb); + + info->flags |= mi->tx_flags; +@@ -1512,8 +1514,8 @@ minstrel_ht_get_rate(void *priv, struct + if (sample_idx < 0) + return; + +- sample_group = &minstrel_mcs_groups[sample_idx / MCS_GROUP_RATES]; +- sample_idx %= MCS_GROUP_RATES; ++ sample_group = &minstrel_mcs_groups[MI_RATE_GROUP(sample_idx)]; ++ sample_idx = MI_RATE_IDX(sample_idx); + + if (sample_group == &minstrel_mcs_groups[MINSTREL_CCK_GROUP] && + (sample_idx >= 4) != txrc->short_preamble) +@@ -1529,7 +1531,7 @@ minstrel_ht_get_rate(void *priv, struct + int idx = sample_idx % ARRAY_SIZE(mp->ofdm_rates[0]); + rate->idx = mp->ofdm_rates[mi->band][idx]; + } else if (sample_group->flags & IEEE80211_TX_RC_VHT_MCS) { +- ieee80211_rate_set_vht(rate, sample_idx % MCS_GROUP_RATES, ++ ieee80211_rate_set_vht(rate, MI_RATE_IDX(sample_idx), + sample_group->streams); + } else { + rate->idx = sample_idx + (sample_group->streams - 1) * 8; +@@ -1898,8 +1900,8 @@ static u32 minstrel_ht_get_expected_thro + struct minstrel_ht_sta *mi = priv_sta; + int i, j, prob, tp_avg; + +- i = mi->max_tp_rate[0] / MCS_GROUP_RATES; +- j = mi->max_tp_rate[0] % MCS_GROUP_RATES; ++ i = MI_RATE_GROUP(mi->max_tp_rate[0]); ++ j = MI_RATE_IDX(mi->max_tp_rate[0]); + prob = mi->groups[i].rates[j].prob_avg; + + /* convert tp_avg from pkt per second in kbps */ +--- a/net/mac80211/rc80211_minstrel_ht.h ++++ b/net/mac80211/rc80211_minstrel_ht.h +@@ -6,6 +6,8 @@ + #ifndef __RC_MINSTREL_HT_H + #define __RC_MINSTREL_HT_H + ++#include ++ + /* number of highest throughput rates to consider*/ + #define MAX_THR_RATES 4 + #define SAMPLE_COLUMNS 10 /* number of columns in sample table */ +@@ -57,6 +59,17 @@ + + #define MCS_GROUP_RATES 10 + ++#define MI_RATE_IDX_MASK GENMASK(3, 0) ++#define MI_RATE_GROUP_MASK GENMASK(15, 4) ++ ++#define MI_RATE(_group, _idx) \ ++ (FIELD_PREP(MI_RATE_GROUP_MASK, _group) | \ ++ FIELD_PREP(MI_RATE_IDX_MASK, _idx)) ++ ++#define MI_RATE_IDX(_rate) FIELD_GET(MI_RATE_IDX_MASK, _rate) ++#define MI_RATE_GROUP(_rate) FIELD_GET(MI_RATE_GROUP_MASK, _rate) ++ ++ + struct minstrel_priv { + struct ieee80211_hw *hw; + bool has_mrr; +--- a/net/mac80211/rc80211_minstrel_ht_debugfs.c ++++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c +@@ -56,7 +56,7 @@ minstrel_ht_stats_dump(struct minstrel_h + + for (j = 0; j < MCS_GROUP_RATES; j++) { + struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j]; +- int idx = i * MCS_GROUP_RATES + j; ++ int idx = MI_RATE(i, j); + unsigned int duration; + + if (!(mi->supported[i] & BIT(j))) +@@ -201,7 +201,7 @@ minstrel_ht_stats_csv_dump(struct minstr + + for (j = 0; j < MCS_GROUP_RATES; j++) { + struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j]; +- int idx = i * MCS_GROUP_RATES + j; ++ int idx = MI_RATE(i, j); + unsigned int duration; + + if (!(mi->supported[i] & BIT(j))) diff --git a/package/kernel/mac80211/patches/subsys/347-mac80211-minstrel_ht-update-total-packets-counter-in.patch b/package/kernel/mac80211/patches/subsys/347-mac80211-minstrel_ht-update-total-packets-counter-in.patch new file mode 100644 index 0000000000..dce8104934 --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/347-mac80211-minstrel_ht-update-total-packets-counter-in.patch @@ -0,0 +1,54 @@ +From: Felix Fietkau +Date: Fri, 22 Jan 2021 18:21:13 +0100 +Subject: [PATCH] mac80211: minstrel_ht: update total packets counter in tx + status path + +Keep the update in one place and prepare for further rework + +Signed-off-by: Felix Fietkau +--- + +--- a/net/mac80211/rc80211_minstrel_ht.c ++++ b/net/mac80211/rc80211_minstrel_ht.c +@@ -1092,6 +1092,16 @@ minstrel_ht_tx_status(void *priv, struct + info->status.ampdu_len = 1; + } + ++ /* wraparound */ ++ if (mi->total_packets >= ~0 - info->status.ampdu_len) { ++ mi->total_packets = 0; ++ mi->sample_packets = 0; ++ } ++ ++ mi->total_packets += info->status.ampdu_len; ++ if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) ++ mi->sample_packets += info->status.ampdu_len; ++ + mi->ampdu_packets++; + mi->ampdu_len += info->status.ampdu_len; + +@@ -1103,9 +1113,6 @@ minstrel_ht_tx_status(void *priv, struct + mi->sample_count--; + } + +- if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) +- mi->sample_packets += info->status.ampdu_len; +- + if (mi->sample_mode != MINSTREL_SAMPLE_IDLE) + rate_sample = minstrel_get_ratestats(mi, mi->sample_rate); + +@@ -1503,14 +1510,6 @@ minstrel_ht_get_rate(void *priv, struct + else + sample_idx = minstrel_get_sample_rate(mp, mi); + +- mi->total_packets++; +- +- /* wraparound */ +- if (mi->total_packets == ~0) { +- mi->total_packets = 0; +- mi->sample_packets = 0; +- } +- + if (sample_idx < 0) + return; + diff --git a/package/kernel/mac80211/patches/subsys/348-mac80211-minstrel_ht-reduce-the-need-to-sample-slowe.patch b/package/kernel/mac80211/patches/subsys/348-mac80211-minstrel_ht-reduce-the-need-to-sample-slowe.patch new file mode 100644 index 0000000000..dc6f11e4b9 --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/348-mac80211-minstrel_ht-reduce-the-need-to-sample-slowe.patch @@ -0,0 +1,102 @@ +From: Felix Fietkau +Date: Fri, 22 Jan 2021 19:24:59 +0100 +Subject: [PATCH] mac80211: minstrel_ht: reduce the need to sample slower + rates + +In order to more gracefully be able to fall back to lower rates without too +much throughput fluctuations, initialize all untested rates below tested ones +to the maximum probabilty of higher rates. +Usually this leads to untested lower rates getting initialized with a +probability value of 100%, making them better candidates for fallback without +having to rely on random probing + +Signed-off-by: Felix Fietkau +--- + +--- a/net/mac80211/rc80211_minstrel_ht.c ++++ b/net/mac80211/rc80211_minstrel_ht.c +@@ -791,14 +791,11 @@ minstrel_ht_calc_rate_stats(struct minst + unsigned int cur_prob; + + if (unlikely(mrs->attempts > 0)) { +- mrs->sample_skipped = 0; + cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts); + minstrel_filter_avg_add(&mrs->prob_avg, + &mrs->prob_avg_1, cur_prob); + mrs->att_hist += mrs->attempts; + mrs->succ_hist += mrs->success; +- } else { +- mrs->sample_skipped++; + } + + mrs->last_success = mrs->success; +@@ -851,7 +848,6 @@ minstrel_ht_update_stats(struct minstrel + mi->ampdu_packets = 0; + } + +- mi->sample_slow = 0; + mi->sample_count = 0; + + if (mi->supported[MINSTREL_CCK_GROUP]) +@@ -882,6 +878,7 @@ minstrel_ht_update_stats(struct minstrel + /* Find best rate sets within all MCS groups*/ + for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) { + u16 *tp_rate = tmp_mcs_tp_rate; ++ u16 last_prob = 0; + + mg = &mi->groups[group]; + if (!mi->supported[group]) +@@ -896,7 +893,7 @@ minstrel_ht_update_stats(struct minstrel + if (group == MINSTREL_CCK_GROUP && ht_supported) + tp_rate = tmp_legacy_tp_rate; + +- for (i = 0; i < MCS_GROUP_RATES; i++) { ++ for (i = MCS_GROUP_RATES - 1; i >= 0; i--) { + if (!(mi->supported[group] & BIT(i))) + continue; + +@@ -905,6 +902,11 @@ minstrel_ht_update_stats(struct minstrel + mrs = &mg->rates[i]; + mrs->retry_updated = false; + minstrel_ht_calc_rate_stats(mp, mrs); ++ ++ if (mrs->att_hist) ++ last_prob = max(last_prob, mrs->prob_avg); ++ else ++ mrs->prob_avg = max(last_prob, mrs->prob_avg); + cur_prob = mrs->prob_avg; + + if (minstrel_ht_get_tp_avg(mi, group, i, cur_prob) == 0) +@@ -1469,13 +1471,9 @@ minstrel_get_sample_rate(struct minstrel + if (sample_dur >= minstrel_get_duration(tp_rate2) && + (cur_max_tp_streams - 1 < + minstrel_mcs_groups[sample_group].streams || +- sample_dur >= minstrel_get_duration(mi->max_prob_rate))) { +- if (mrs->sample_skipped < 20) ++ sample_dur >= minstrel_get_duration(mi->max_prob_rate))) + return -1; + +- if (mi->sample_slow++ > 2) +- return -1; +- } + mi->sample_tries--; + + return sample_idx; +--- a/net/mac80211/rc80211_minstrel_ht.h ++++ b/net/mac80211/rc80211_minstrel_ht.h +@@ -123,7 +123,6 @@ struct minstrel_rate_stats { + u8 retry_count; + u8 retry_count_rtscts; + +- u8 sample_skipped; + bool retry_updated; + }; + +@@ -179,7 +178,6 @@ struct minstrel_ht_sta { + u8 sample_wait; + u8 sample_tries; + u8 sample_count; +- u8 sample_slow; + + enum minstrel_sample_mode sample_mode; + u16 sample_rate; diff --git a/package/kernel/mac80211/patches/subsys/349-mac80211-minstrel_ht-significantly-redesign-the-rate.patch b/package/kernel/mac80211/patches/subsys/349-mac80211-minstrel_ht-significantly-redesign-the-rate.patch new file mode 100644 index 0000000000..7af13661a8 --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/349-mac80211-minstrel_ht-significantly-redesign-the-rate.patch @@ -0,0 +1,767 @@ +From: Felix Fietkau +Date: Fri, 22 Jan 2021 23:57:50 +0100 +Subject: [PATCH] mac80211: minstrel_ht: significantly redesign the rate + probing strategy + +The biggest flaw in current minstrel_ht is the fact that it needs way too +many probing packets to be able to quickly find the best rate. +Depending on the wifi hardware and operating mode, this can significantly +reduce throughput when not operating at the highest available data rate. + +In order to be able to significantly reduce the amount of rate sampling, +we need a much smarter selection of probing rates. + +The new approach introduced by this patch maintains a limited set of +available rates to be tested during a statistics window. + +They are split into distinct categories: +- MINSTREL_SAMPLE_TYPE_INC - incremental rate upgrade: + Pick the next rate group and find the first rate that is faster than + the current max. throughput rate +- MINSTREL_SAMPLE_TYPE_JUMP - random testing of higher rates: + Pick a random rate from the next group that is faster than the current + max throughput rate. This allows faster adaptation when the link changes + significantly +- MINSTREL_SAMPLE_TYPE_SLOW - test a rate between max_prob, max_tp2 and + max_tp in order to reduce the gap between them + +In order to prioritize sampling, every 6 attempts are split into 3x INC, +2x JUMP, 1x SLOW. + +Available rates are checked and refilled on every stats window update. + +With this approach, we finally get a very small delta in throughput when +comparing setting the optimal data rate as a fixed rate vs normal rate +control operation. + +Signed-off-by: Felix Fietkau +--- + +--- a/net/mac80211/rc80211_minstrel_ht.c ++++ b/net/mac80211/rc80211_minstrel_ht.c +@@ -266,6 +266,14 @@ const struct mcs_group minstrel_mcs_grou + const s16 minstrel_cck_bitrates[4] = { 10, 20, 55, 110 }; + const s16 minstrel_ofdm_bitrates[8] = { 60, 90, 120, 180, 240, 360, 480, 540 }; + static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES] __read_mostly; ++static const u8 minstrel_sample_seq[] = { ++ MINSTREL_SAMPLE_TYPE_INC, ++ MINSTREL_SAMPLE_TYPE_JUMP, ++ MINSTREL_SAMPLE_TYPE_INC, ++ MINSTREL_SAMPLE_TYPE_JUMP, ++ MINSTREL_SAMPLE_TYPE_INC, ++ MINSTREL_SAMPLE_TYPE_SLOW, ++}; + + static void + minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi); +@@ -620,77 +628,31 @@ minstrel_ht_prob_rate_reduce_streams(str + } + } + +-static bool +-minstrel_ht_probe_group(struct minstrel_ht_sta *mi, const struct mcs_group *tp_group, +- int tp_idx, const struct mcs_group *group) +-{ +- if (group->bw < tp_group->bw) +- return false; +- +- if (group->streams == tp_group->streams) +- return true; +- +- if (tp_idx < 4 && group->streams == tp_group->streams - 1) +- return true; +- +- return group->streams == tp_group->streams + 1; +-} +- +-static void +-minstrel_ht_find_probe_rates(struct minstrel_ht_sta *mi, u16 *rates, int *n_rates, +- bool faster_rate) ++static u16 ++__minstrel_ht_get_sample_rate(struct minstrel_ht_sta *mi, ++ enum minstrel_sample_type type) + { +- const struct mcs_group *group, *tp_group; +- int i, g, max_dur; +- int tp_idx; +- +- tp_group = &minstrel_mcs_groups[MI_RATE_GROUP(mi->max_tp_rate[0])]; +- tp_idx = MI_RATE_IDX(mi->max_tp_rate[0]); +- +- max_dur = minstrel_get_duration(mi->max_tp_rate[0]); +- if (faster_rate) +- max_dur -= max_dur / 16; +- +- for (g = 0; g < MINSTREL_GROUPS_NB; g++) { +- u16 supported = mi->supported[g]; +- +- if (!supported) +- continue; ++ u16 *rates = mi->sample[type].sample_rates; ++ u16 cur; ++ int i; + +- group = &minstrel_mcs_groups[g]; +- if (!minstrel_ht_probe_group(mi, tp_group, tp_idx, group)) ++ for (i = 0; i < MINSTREL_SAMPLE_RATES; i++) { ++ if (!rates[i]) + continue; + +- for (i = 0; supported; supported >>= 1, i++) { +- int idx; +- +- if (!(supported & 1)) +- continue; +- +- if ((group->duration[i] << group->shift) > max_dur) +- continue; +- +- idx = MI_RATE(g, i); +- if (idx == mi->max_tp_rate[0]) +- continue; +- +- rates[(*n_rates)++] = idx; +- break; +- } ++ cur = rates[i]; ++ rates[i] = 0; ++ return cur; + } ++ ++ return 0; + } + + static void + minstrel_ht_rate_sample_switch(struct minstrel_priv *mp, + struct minstrel_ht_sta *mi) + { +- struct minstrel_rate_stats *mrs; +- u16 rates[MINSTREL_GROUPS_NB]; +- int n_rates = 0; +- int probe_rate = 0; +- bool faster_rate; +- int i; +- u8 random; ++ u16 rate; + + /* + * Use rate switching instead of probing packets for devices with +@@ -699,43 +661,11 @@ minstrel_ht_rate_sample_switch(struct mi + if (mp->hw->max_rates > 1) + return; + +- /* +- * If the current EWMA prob is >75%, look for a rate that's 6.25% +- * faster than the max tp rate. +- * If that fails, look again for a rate that is at least as fast +- */ +- mrs = minstrel_get_ratestats(mi, mi->max_tp_rate[0]); +- faster_rate = mrs->prob_avg > MINSTREL_FRAC(75, 100); +- minstrel_ht_find_probe_rates(mi, rates, &n_rates, faster_rate); +- if (!n_rates && faster_rate) +- minstrel_ht_find_probe_rates(mi, rates, &n_rates, false); +- +- /* If no suitable rate was found, try to pick the next one in the group */ +- if (!n_rates) { +- int g_idx = MI_RATE_GROUP(mi->max_tp_rate[0]); +- u16 supported = mi->supported[g_idx]; +- +- supported >>= MI_RATE_IDX(mi->max_tp_rate[0]); +- for (i = 0; supported; supported >>= 1, i++) { +- if (!(supported & 1)) +- continue; +- +- probe_rate = mi->max_tp_rate[0] + i; +- goto out; +- } +- ++ rate = __minstrel_ht_get_sample_rate(mi, MINSTREL_SAMPLE_TYPE_INC); ++ if (!rate) + return; +- } + +- i = 0; +- if (n_rates > 1) { +- random = prandom_u32(); +- i = random % n_rates; +- } +- probe_rate = rates[i]; +- +-out: +- mi->sample_rate = probe_rate; ++ mi->sample_rate = rate; + mi->sample_mode = MINSTREL_SAMPLE_ACTIVE; + } + +@@ -804,6 +734,274 @@ minstrel_ht_calc_rate_stats(struct minst + mrs->attempts = 0; + } + ++static bool ++minstrel_ht_find_sample_rate(struct minstrel_ht_sta *mi, int type, int idx) ++{ ++ int i; ++ ++ for (i = 0; i < MINSTREL_SAMPLE_RATES; i++) { ++ u16 cur = mi->sample[type].sample_rates[i]; ++ ++ if (cur == idx) ++ return true; ++ ++ if (!cur) ++ break; ++ } ++ ++ return false; ++} ++ ++static int ++minstrel_ht_move_sample_rates(struct minstrel_ht_sta *mi, int type, ++ u32 fast_rate_dur, u32 slow_rate_dur) ++{ ++ u16 *rates = mi->sample[type].sample_rates; ++ int i, j; ++ ++ for (i = 0, j = 0; i < MINSTREL_SAMPLE_RATES; i++) { ++ u32 duration; ++ bool valid = false; ++ u16 cur; ++ ++ cur = rates[i]; ++ if (!cur) ++ continue; ++ ++ duration = minstrel_get_duration(cur); ++ switch (type) { ++ case MINSTREL_SAMPLE_TYPE_SLOW: ++ valid = duration > fast_rate_dur && ++ duration < slow_rate_dur; ++ break; ++ case MINSTREL_SAMPLE_TYPE_INC: ++ case MINSTREL_SAMPLE_TYPE_JUMP: ++ valid = duration < fast_rate_dur; ++ break; ++ default: ++ valid = false; ++ break; ++ } ++ ++ if (!valid) { ++ rates[i] = 0; ++ continue; ++ } ++ ++ if (i == j) ++ continue; ++ ++ rates[j++] = cur; ++ rates[i] = 0; ++ } ++ ++ return j; ++} ++ ++static int ++minstrel_ht_group_min_rate_offset(struct minstrel_ht_sta *mi, int group, ++ u32 max_duration) ++{ ++ u16 supported = mi->supported[group]; ++ int i; ++ ++ for (i = 0; i < MCS_GROUP_RATES && supported; i++, supported >>= 1) { ++ if (!(supported & BIT(0))) ++ continue; ++ ++ if (minstrel_get_duration(MI_RATE(group, i)) >= max_duration) ++ continue; ++ ++ return i; ++ } ++ ++ return -1; ++} ++ ++/* ++ * Incremental update rates: ++ * Flip through groups and pick the first group rate that is faster than the ++ * highest currently selected rate ++ */ ++static u16 ++minstrel_ht_next_inc_rate(struct minstrel_ht_sta *mi, u32 fast_rate_dur) ++{ ++ struct minstrel_mcs_group_data *mg; ++ u8 type = MINSTREL_SAMPLE_TYPE_INC; ++ int i, index = 0; ++ u8 group; ++ ++ group = mi->sample[type].sample_group; ++ for (i = 0; i < ARRAY_SIZE(minstrel_mcs_groups); i++) { ++ group = (group + 1) % ARRAY_SIZE(minstrel_mcs_groups); ++ mg = &mi->groups[group]; ++ ++ index = minstrel_ht_group_min_rate_offset(mi, group, ++ fast_rate_dur); ++ if (index < 0) ++ continue; ++ ++ index = MI_RATE(group, index & 0xf); ++ if (!minstrel_ht_find_sample_rate(mi, type, index)) ++ goto out; ++ } ++ index = 0; ++ ++out: ++ mi->sample[type].sample_group = group; ++ ++ return index; ++} ++ ++static int ++minstrel_ht_next_group_sample_rate(struct minstrel_ht_sta *mi, int group, ++ u16 supported, int offset) ++{ ++ struct minstrel_mcs_group_data *mg = &mi->groups[group]; ++ u16 idx; ++ int i; ++ ++ for (i = 0; i < MCS_GROUP_RATES; i++) { ++ idx = sample_table[mg->column][mg->index]; ++ if (++mg->index >= MCS_GROUP_RATES) { ++ mg->index = 0; ++ if (++mg->column >= ARRAY_SIZE(sample_table)) ++ mg->column = 0; ++ } ++ ++ if (idx < offset) ++ continue; ++ ++ if (!(supported & BIT(idx))) ++ continue; ++ ++ return MI_RATE(group, idx); ++ } ++ ++ return -1; ++} ++ ++/* ++ * Jump rates: ++ * Sample random rates, use those that are faster than the highest ++ * currently selected rate. Rates between the fastest and the slowest ++ * get sorted into the slow sample bucket, but only if it has room ++ */ ++static u16 ++minstrel_ht_next_jump_rate(struct minstrel_ht_sta *mi, u32 fast_rate_dur, ++ u32 slow_rate_dur, int *slow_rate_ofs) ++{ ++ struct minstrel_mcs_group_data *mg; ++ struct minstrel_rate_stats *mrs; ++ u32 max_duration = slow_rate_dur; ++ int i, index, offset; ++ u16 *slow_rates; ++ u16 supported; ++ u32 duration; ++ u8 group; ++ ++ if (*slow_rate_ofs >= MINSTREL_SAMPLE_RATES) ++ max_duration = fast_rate_dur; ++ ++ slow_rates = mi->sample[MINSTREL_SAMPLE_TYPE_SLOW].sample_rates; ++ group = mi->sample[MINSTREL_SAMPLE_TYPE_JUMP].sample_group; ++ for (i = 0; i < ARRAY_SIZE(minstrel_mcs_groups); i++) { ++ u8 type; ++ ++ group = (group + 1) % ARRAY_SIZE(minstrel_mcs_groups); ++ mg = &mi->groups[group]; ++ ++ supported = mi->supported[group]; ++ if (!supported) ++ continue; ++ ++ offset = minstrel_ht_group_min_rate_offset(mi, group, ++ max_duration); ++ if (offset < 0) ++ continue; ++ ++ index = minstrel_ht_next_group_sample_rate(mi, group, supported, ++ offset); ++ if (index < 0) ++ continue; ++ ++ duration = minstrel_get_duration(index); ++ if (duration < fast_rate_dur) ++ type = MINSTREL_SAMPLE_TYPE_JUMP; ++ else ++ type = MINSTREL_SAMPLE_TYPE_SLOW; ++ ++ if (minstrel_ht_find_sample_rate(mi, type, index)) ++ continue; ++ ++ if (type == MINSTREL_SAMPLE_TYPE_JUMP) ++ goto found; ++ ++ if (*slow_rate_ofs >= MINSTREL_SAMPLE_RATES) ++ continue; ++ ++ if (duration >= slow_rate_dur) ++ continue; ++ ++ /* skip slow rates with high success probability */ ++ mrs = minstrel_get_ratestats(mi, index); ++ if (mrs->prob_avg > MINSTREL_FRAC(95, 100)) ++ continue; ++ ++ slow_rates[(*slow_rate_ofs)++] = index; ++ if (*slow_rate_ofs >= MINSTREL_SAMPLE_RATES) ++ max_duration = fast_rate_dur; ++ } ++ index = 0; ++ ++found: ++ mi->sample[MINSTREL_SAMPLE_TYPE_JUMP].sample_group = group; ++ ++ return index; ++} ++ ++static void ++minstrel_ht_refill_sample_rates(struct minstrel_ht_sta *mi) ++{ ++ u32 prob_dur = minstrel_get_duration(mi->max_prob_rate); ++ u32 tp_dur = minstrel_get_duration(mi->max_tp_rate[0]); ++ u32 tp2_dur = minstrel_get_duration(mi->max_tp_rate[1]); ++ u32 fast_rate_dur = min(min(tp_dur, tp2_dur), prob_dur); ++ u32 slow_rate_dur = max(max(tp_dur, tp2_dur), prob_dur); ++ u16 *rates; ++ int i, j; ++ ++ rates = mi->sample[MINSTREL_SAMPLE_TYPE_INC].sample_rates; ++ i = minstrel_ht_move_sample_rates(mi, MINSTREL_SAMPLE_TYPE_INC, ++ fast_rate_dur, slow_rate_dur); ++ while (i < MINSTREL_SAMPLE_RATES) { ++ rates[i] = minstrel_ht_next_inc_rate(mi, tp_dur); ++ if (!rates[i]) ++ break; ++ ++ i++; ++ } ++ ++ rates = mi->sample[MINSTREL_SAMPLE_TYPE_JUMP].sample_rates; ++ i = minstrel_ht_move_sample_rates(mi, MINSTREL_SAMPLE_TYPE_JUMP, ++ fast_rate_dur, slow_rate_dur); ++ j = minstrel_ht_move_sample_rates(mi, MINSTREL_SAMPLE_TYPE_SLOW, ++ fast_rate_dur, slow_rate_dur); ++ while (i < MINSTREL_SAMPLE_RATES) { ++ rates[i] = minstrel_ht_next_jump_rate(mi, fast_rate_dur, ++ slow_rate_dur, &j); ++ if (!rates[i]) ++ break; ++ ++ i++; ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(mi->sample); i++) ++ memcpy(mi->sample[i].cur_sample_rates, mi->sample[i].sample_rates, ++ sizeof(mi->sample[i].cur_sample_rates)); ++} ++ ++ + /* + * Update rate statistics and select new primary rates + * +@@ -848,8 +1046,6 @@ minstrel_ht_update_stats(struct minstrel + mi->ampdu_packets = 0; + } + +- mi->sample_count = 0; +- + if (mi->supported[MINSTREL_CCK_GROUP]) + group = MINSTREL_CCK_GROUP; + else if (mi->supported[MINSTREL_OFDM_GROUP]) +@@ -884,8 +1080,6 @@ minstrel_ht_update_stats(struct minstrel + if (!mi->supported[group]) + continue; + +- mi->sample_count++; +- + /* (re)Initialize group rate indexes */ + for(j = 0; j < MAX_THR_RATES; j++) + tmp_group_tp_rate[j] = MI_RATE(group, 0); +@@ -952,9 +1146,7 @@ minstrel_ht_update_stats(struct minstrel + + /* Try to increase robustness of max_prob_rate*/ + minstrel_ht_prob_rate_reduce_streams(mi); +- +- /* try to sample half of all available rates during each interval */ +- mi->sample_count *= 4; ++ minstrel_ht_refill_sample_rates(mi); + + if (sample) + minstrel_ht_rate_sample_switch(mp, mi); +@@ -971,6 +1163,7 @@ minstrel_ht_update_stats(struct minstrel + + /* Reset update timer */ + mi->last_stats_update = jiffies; ++ mi->sample_time = jiffies; + } + + static bool +@@ -1001,28 +1194,6 @@ minstrel_ht_txstat_valid(struct minstrel + } + + static void +-minstrel_set_next_sample_idx(struct minstrel_ht_sta *mi) +-{ +- struct minstrel_mcs_group_data *mg; +- +- for (;;) { +- mi->sample_group++; +- mi->sample_group %= ARRAY_SIZE(minstrel_mcs_groups); +- mg = &mi->groups[mi->sample_group]; +- +- if (!mi->supported[mi->sample_group]) +- continue; +- +- if (++mg->index >= MCS_GROUP_RATES) { +- mg->index = 0; +- if (++mg->column >= ARRAY_SIZE(sample_table)) +- mg->column = 0; +- } +- break; +- } +-} +- +-static void + minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u16 *idx, bool primary) + { + int group, orig_group; +@@ -1107,14 +1278,6 @@ minstrel_ht_tx_status(void *priv, struct + mi->ampdu_packets++; + mi->ampdu_len += info->status.ampdu_len; + +- if (!mi->sample_wait && !mi->sample_tries && mi->sample_count > 0) { +- int avg_ampdu_len = minstrel_ht_avg_ampdu_len(mi); +- +- mi->sample_wait = 16 + 2 * avg_ampdu_len; +- mi->sample_tries = 1; +- mi->sample_count--; +- } +- + if (mi->sample_mode != MINSTREL_SAMPLE_IDLE) + rate_sample = minstrel_get_ratestats(mi, mi->sample_rate); + +@@ -1386,97 +1549,20 @@ minstrel_ht_update_rates(struct minstrel + rate_control_set_rates(mp->hw, mi->sta, rates); + } + +-static int +-minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) ++static u16 ++minstrel_ht_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) + { +- struct minstrel_rate_stats *mrs; +- struct minstrel_mcs_group_data *mg; +- unsigned int sample_dur, sample_group, cur_max_tp_streams; +- int tp_rate1, tp_rate2; +- int sample_idx = 0; +- +- if (mp->hw->max_rates == 1 && mp->sample_switch && +- (mi->total_packets_cur >= SAMPLE_SWITCH_THR || +- mp->sample_switch == 1)) +- return -1; +- +- if (mi->sample_wait > 0) { +- mi->sample_wait--; +- return -1; +- } +- +- if (!mi->sample_tries) +- return -1; +- +- sample_group = mi->sample_group; +- mg = &mi->groups[sample_group]; +- sample_idx = sample_table[mg->column][mg->index]; +- minstrel_set_next_sample_idx(mi); +- +- if (!(mi->supported[sample_group] & BIT(sample_idx))) +- return -1; +- +- mrs = &mg->rates[sample_idx]; +- sample_idx += MI_RATE(sample_group, 0); +- +- tp_rate1 = mi->max_tp_rate[0]; ++ u8 seq; + +- /* Set tp_rate2 to the second highest max_tp_rate */ +- if (minstrel_get_duration(mi->max_tp_rate[0]) > +- minstrel_get_duration(mi->max_tp_rate[1])) { +- tp_rate2 = mi->max_tp_rate[0]; ++ if (mp->hw->max_rates > 1) { ++ seq = mi->sample_seq; ++ mi->sample_seq = (seq + 1) % ARRAY_SIZE(minstrel_sample_seq); ++ seq = minstrel_sample_seq[seq]; + } else { +- tp_rate2 = mi->max_tp_rate[1]; ++ seq = MINSTREL_SAMPLE_TYPE_INC; + } + +- /* +- * Sampling might add some overhead (RTS, no aggregation) +- * to the frame. Hence, don't use sampling for the highest currently +- * used highest throughput or probability rate. +- */ +- if (sample_idx == mi->max_tp_rate[0] || sample_idx == mi->max_prob_rate) +- return -1; +- +- /* +- * Do not sample if the probability is already higher than 95%, +- * or if the rate is 3 times slower than the current max probability +- * rate, to avoid wasting airtime. +- */ +- sample_dur = minstrel_get_duration(sample_idx); +- if (mrs->prob_avg > MINSTREL_FRAC(95, 100) || +- minstrel_get_duration(mi->max_prob_rate) * 3 < sample_dur) +- return -1; +- +- +- /* +- * For devices with no configurable multi-rate retry, skip sampling +- * below the per-group max throughput rate, and only use one sampling +- * attempt per rate +- */ +- if (mp->hw->max_rates == 1 && +- (minstrel_get_duration(mg->max_group_tp_rate[0]) < sample_dur || +- mrs->attempts)) +- return -1; +- +- /* Skip already sampled slow rates */ +- if (sample_dur >= minstrel_get_duration(tp_rate1) && mrs->attempts) +- return -1; +- +- /* +- * Make sure that lower rates get sampled only occasionally, +- * if the link is working perfectly. +- */ +- +- cur_max_tp_streams = minstrel_mcs_groups[MI_RATE_GROUP(tp_rate1)].streams; +- if (sample_dur >= minstrel_get_duration(tp_rate2) && +- (cur_max_tp_streams - 1 < +- minstrel_mcs_groups[sample_group].streams || +- sample_dur >= minstrel_get_duration(mi->max_prob_rate))) +- return -1; +- +- mi->sample_tries--; +- +- return sample_idx; ++ return __minstrel_ht_get_sample_rate(mi, seq); + } + + static void +@@ -1488,7 +1574,7 @@ minstrel_ht_get_rate(void *priv, struct + struct ieee80211_tx_rate *rate = &info->status.rates[0]; + struct minstrel_ht_sta *mi = priv_sta; + struct minstrel_priv *mp = priv; +- int sample_idx; ++ u16 sample_idx; + + if (!(info->flags & IEEE80211_TX_CTL_AMPDU) && + !minstrel_ht_is_legacy_group(MI_RATE_GROUP(mi->max_prob_rate))) +@@ -1504,11 +1590,19 @@ minstrel_ht_get_rate(void *priv, struct + /* Don't use EAPOL frames for sampling on non-mrr hw */ + if (mp->hw->max_rates == 1 && + (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO)) +- sample_idx = -1; +- else +- sample_idx = minstrel_get_sample_rate(mp, mi); ++ return; + +- if (sample_idx < 0) ++ if (mp->hw->max_rates == 1 && mp->sample_switch && ++ (mi->total_packets_cur >= SAMPLE_SWITCH_THR || ++ mp->sample_switch == 1)) ++ return; ++ ++ if (time_is_before_jiffies(mi->sample_time)) ++ return; ++ ++ mi->sample_time = jiffies + MINSTREL_SAMPLE_INTERVAL; ++ sample_idx = minstrel_ht_get_sample_rate(mp, mi); ++ if (!sample_idx) + return; + + sample_group = &minstrel_mcs_groups[MI_RATE_GROUP(sample_idx)]; +@@ -1629,16 +1723,6 @@ minstrel_ht_update_caps(void *priv, stru + + mi->avg_ampdu_len = MINSTREL_FRAC(1, 1); + +- /* When using MRR, sample more on the first attempt, without delay */ +- if (mp->has_mrr) { +- mi->sample_count = 16; +- mi->sample_wait = 0; +- } else { +- mi->sample_count = 8; +- mi->sample_wait = 8; +- } +- mi->sample_tries = 4; +- + if (!use_vht) { + stbc = (ht_cap & IEEE80211_HT_CAP_RX_STBC) >> + IEEE80211_HT_CAP_RX_STBC_SHIFT; +--- a/net/mac80211/rc80211_minstrel_ht.h ++++ b/net/mac80211/rc80211_minstrel_ht.h +@@ -69,6 +69,8 @@ + #define MI_RATE_IDX(_rate) FIELD_GET(MI_RATE_IDX_MASK, _rate) + #define MI_RATE_GROUP(_rate) FIELD_GET(MI_RATE_GROUP_MASK, _rate) + ++#define MINSTREL_SAMPLE_RATES 5 /* rates per sample type */ ++#define MINSTREL_SAMPLE_INTERVAL (HZ / 50) + + struct minstrel_priv { + struct ieee80211_hw *hw; +@@ -126,6 +128,13 @@ struct minstrel_rate_stats { + bool retry_updated; + }; + ++enum minstrel_sample_type { ++ MINSTREL_SAMPLE_TYPE_INC, ++ MINSTREL_SAMPLE_TYPE_JUMP, ++ MINSTREL_SAMPLE_TYPE_SLOW, ++ __MINSTREL_SAMPLE_TYPE_MAX ++}; ++ + struct minstrel_mcs_group_data { + u8 index; + u8 column; +@@ -144,6 +153,12 @@ enum minstrel_sample_mode { + MINSTREL_SAMPLE_PENDING, + }; + ++struct minstrel_sample_category { ++ u8 sample_group; ++ u16 sample_rates[MINSTREL_SAMPLE_RATES]; ++ u16 cur_sample_rates[MINSTREL_SAMPLE_RATES]; ++}; ++ + struct minstrel_ht_sta { + struct ieee80211_sta *sta; + +@@ -175,16 +190,14 @@ struct minstrel_ht_sta { + /* tx flags to add for frames for this sta */ + u32 tx_flags; + +- u8 sample_wait; +- u8 sample_tries; +- u8 sample_count; ++ unsigned long sample_time; ++ struct minstrel_sample_category sample[__MINSTREL_SAMPLE_TYPE_MAX]; ++ ++ u8 sample_seq; + + enum minstrel_sample_mode sample_mode; + u16 sample_rate; + +- /* current MCS group to be sampled */ +- u8 sample_group; +- + u8 band; + + /* Bitfield of supported MCS rates of all groups */ diff --git a/package/kernel/mac80211/patches/subsys/350-mac80211-minstrel_ht-show-sampling-rates-in-debugfs.patch b/package/kernel/mac80211/patches/subsys/350-mac80211-minstrel_ht-show-sampling-rates-in-debugfs.patch new file mode 100644 index 0000000000..041ba31a37 --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/350-mac80211-minstrel_ht-show-sampling-rates-in-debugfs.patch @@ -0,0 +1,58 @@ +From: Felix Fietkau +Date: Sat, 23 Jan 2021 00:10:34 +0100 +Subject: [PATCH] mac80211: minstrel_ht: show sampling rates in debugfs + +This makes it easier to see what rates are going to be tested next + +Signed-off-by: Felix Fietkau +--- + +--- a/net/mac80211/rc80211_minstrel_ht_debugfs.c ++++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c +@@ -32,6 +32,18 @@ minstrel_stats_release(struct inode *ino + return 0; + } + ++static bool ++minstrel_ht_is_sample_rate(struct minstrel_ht_sta *mi, int idx) ++{ ++ int type, i; ++ ++ for (type = 0; type < ARRAY_SIZE(mi->sample); type++) ++ for (i = 0; i < MINSTREL_SAMPLE_RATES; i++) ++ if (mi->sample[type].cur_sample_rates[i] == idx) ++ return true; ++ return false; ++} ++ + static char * + minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) + { +@@ -84,6 +96,7 @@ minstrel_ht_stats_dump(struct minstrel_h + *(p++) = (idx == mi->max_tp_rate[2]) ? 'C' : ' '; + *(p++) = (idx == mi->max_tp_rate[3]) ? 'D' : ' '; + *(p++) = (idx == mi->max_prob_rate) ? 'P' : ' '; ++ *(p++) = minstrel_ht_is_sample_rate(mi, idx) ? 'S' : ' '; + + if (gflags & IEEE80211_TX_RC_MCS) { + p += sprintf(p, " MCS%-2u", (mg->streams - 1) * 8 + j); +@@ -145,9 +158,9 @@ minstrel_ht_stats_open(struct inode *ino + + p += sprintf(p, "\n"); + p += sprintf(p, +- " best ____________rate__________ ____statistics___ _____last____ ______sum-of________\n"); ++ " best ____________rate__________ ____statistics___ _____last____ ______sum-of________\n"); + p += sprintf(p, +- "mode guard # rate [name idx airtime max_tp] [avg(tp) avg(prob)] [retry|suc|att] [#success | #attempts]\n"); ++ "mode guard # rate [name idx airtime max_tp] [avg(tp) avg(prob)] [retry|suc|att] [#success | #attempts]\n"); + + p = minstrel_ht_stats_dump(mi, MINSTREL_CCK_GROUP, p); + for (i = 0; i < MINSTREL_CCK_GROUP; i++) +@@ -228,6 +241,7 @@ minstrel_ht_stats_csv_dump(struct minstr + p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[2]) ? "C" : "")); + p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[3]) ? "D" : "")); + p += sprintf(p, "%s" ,((idx == mi->max_prob_rate) ? "P" : "")); ++ p += sprintf(p, "%s", (minstrel_ht_is_sample_rate(mi, idx) ? "S" : "")); + + if (gflags & IEEE80211_TX_RC_MCS) { + p += sprintf(p, ",MCS%-2u,", (mg->streams - 1) * 8 + j); diff --git a/package/kernel/mac80211/patches/subsys/351-mac80211-minstrel_ht-remove-sample-rate-switching-co.patch b/package/kernel/mac80211/patches/subsys/351-mac80211-minstrel_ht-remove-sample-rate-switching-co.patch new file mode 100644 index 0000000000..8170ff85f8 --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/351-mac80211-minstrel_ht-remove-sample-rate-switching-co.patch @@ -0,0 +1,279 @@ +From: Felix Fietkau +Date: Sat, 23 Jan 2021 07:18:26 +0100 +Subject: [PATCH] mac80211: minstrel_ht: remove sample rate switching code for + constrained devices + +This was added to mitigate the effects of too much sampling on devices that +use a static global fallback table instead of configurable multi-rate retry. +Now that the sampling algorithm is improved, this code path no longer performs +any better than the standard probing on affected devices. + +Signed-off-by: Felix Fietkau +--- + +--- a/net/mac80211/rc80211_minstrel_ht.c ++++ b/net/mac80211/rc80211_minstrel_ht.c +@@ -648,27 +648,6 @@ __minstrel_ht_get_sample_rate(struct min + return 0; + } + +-static void +-minstrel_ht_rate_sample_switch(struct minstrel_priv *mp, +- struct minstrel_ht_sta *mi) +-{ +- u16 rate; +- +- /* +- * Use rate switching instead of probing packets for devices with +- * little control over retry fallback behavior +- */ +- if (mp->hw->max_rates > 1) +- return; +- +- rate = __minstrel_ht_get_sample_rate(mi, MINSTREL_SAMPLE_TYPE_INC); +- if (!rate) +- return; +- +- mi->sample_rate = rate; +- mi->sample_mode = MINSTREL_SAMPLE_ACTIVE; +-} +- + static inline int + minstrel_ewma(int old, int new, int weight) + { +@@ -1012,8 +991,7 @@ minstrel_ht_refill_sample_rates(struct m + * higher throughput rates, even if the probablity is a bit lower + */ + static void +-minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, +- bool sample) ++minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) + { + struct minstrel_mcs_group_data *mg; + struct minstrel_rate_stats *mrs; +@@ -1023,18 +1001,6 @@ minstrel_ht_update_stats(struct minstrel + u16 index; + bool ht_supported = mi->sta->ht_cap.ht_supported; + +- mi->sample_mode = MINSTREL_SAMPLE_IDLE; +- +- if (sample) { +- mi->total_packets_cur = mi->total_packets - +- mi->total_packets_last; +- mi->total_packets_last = mi->total_packets; +- } +- if (!mp->sample_switch) +- sample = false; +- if (mi->total_packets_cur < SAMPLE_SWITCH_THR && mp->sample_switch != 1) +- sample = false; +- + if (mi->ampdu_packets > 0) { + if (!ieee80211_hw_check(mp->hw, TX_STATUS_NO_AMPDU_LEN)) + mi->avg_ampdu_len = minstrel_ewma(mi->avg_ampdu_len, +@@ -1148,16 +1114,12 @@ minstrel_ht_update_stats(struct minstrel + minstrel_ht_prob_rate_reduce_streams(mi); + minstrel_ht_refill_sample_rates(mi); + +- if (sample) +- minstrel_ht_rate_sample_switch(mp, mi); +- + #ifdef CPTCFG_MAC80211_DEBUGFS + /* use fixed index if set */ + if (mp->fixed_rate_idx != -1) { + for (i = 0; i < 4; i++) + mi->max_tp_rate[i] = mp->fixed_rate_idx; + mi->max_prob_rate = mp->fixed_rate_idx; +- mi->sample_mode = MINSTREL_SAMPLE_IDLE; + } + #endif + +@@ -1247,11 +1209,10 @@ minstrel_ht_tx_status(void *priv, struct + struct ieee80211_tx_info *info = st->info; + struct minstrel_ht_sta *mi = priv_sta; + struct ieee80211_tx_rate *ar = info->status.rates; +- struct minstrel_rate_stats *rate, *rate2, *rate_sample = NULL; ++ struct minstrel_rate_stats *rate, *rate2; + struct minstrel_priv *mp = priv; + u32 update_interval = mp->update_interval; + bool last, update = false; +- bool sample_status = false; + int i; + + /* This packet was aggregated but doesn't carry status info */ +@@ -1278,49 +1239,18 @@ minstrel_ht_tx_status(void *priv, struct + mi->ampdu_packets++; + mi->ampdu_len += info->status.ampdu_len; + +- if (mi->sample_mode != MINSTREL_SAMPLE_IDLE) +- rate_sample = minstrel_get_ratestats(mi, mi->sample_rate); +- + last = !minstrel_ht_txstat_valid(mp, mi, &ar[0]); + for (i = 0; !last; i++) { + last = (i == IEEE80211_TX_MAX_RATES - 1) || + !minstrel_ht_txstat_valid(mp, mi, &ar[i + 1]); + + rate = minstrel_ht_get_stats(mp, mi, &ar[i]); +- if (rate == rate_sample) +- sample_status = true; +- + if (last) + rate->success += info->status.ampdu_ack_len; + + rate->attempts += ar[i].count * info->status.ampdu_len; + } + +- switch (mi->sample_mode) { +- case MINSTREL_SAMPLE_IDLE: +- if (mp->hw->max_rates > 1 || +- mi->total_packets_cur < SAMPLE_SWITCH_THR) +- update_interval /= 2; +- break; +- +- case MINSTREL_SAMPLE_ACTIVE: +- if (!sample_status) +- break; +- +- mi->sample_mode = MINSTREL_SAMPLE_PENDING; +- update = true; +- break; +- +- case MINSTREL_SAMPLE_PENDING: +- if (sample_status) +- break; +- +- update = true; +- minstrel_ht_update_stats(mp, mi, false); +- break; +- } +- +- + if (mp->hw->max_rates > 1) { + /* + * check for sudden death of spatial multiplexing, +@@ -1343,7 +1273,7 @@ minstrel_ht_tx_status(void *priv, struct + + if (time_after(jiffies, mi->last_stats_update + update_interval)) { + update = true; +- minstrel_ht_update_stats(mp, mi, true); ++ minstrel_ht_update_stats(mp, mi); + } + + if (update) +@@ -1522,18 +1452,14 @@ static void + minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) + { + struct ieee80211_sta_rates *rates; +- u16 first_rate = mi->max_tp_rate[0]; + int i = 0; + +- if (mi->sample_mode == MINSTREL_SAMPLE_ACTIVE) +- first_rate = mi->sample_rate; +- + rates = kzalloc(sizeof(*rates), GFP_ATOMIC); + if (!rates) + return; + + /* Start with max_tp_rate[0] */ +- minstrel_ht_set_rate(mp, mi, rates, i++, first_rate); ++ minstrel_ht_set_rate(mp, mi, rates, i++, mi->max_tp_rate[0]); + + if (mp->hw->max_rates >= 3) { + /* At least 3 tx rates supported, use max_tp_rate[1] next */ +@@ -1592,11 +1518,6 @@ minstrel_ht_get_rate(void *priv, struct + (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO)) + return; + +- if (mp->hw->max_rates == 1 && mp->sample_switch && +- (mi->total_packets_cur >= SAMPLE_SWITCH_THR || +- mp->sample_switch == 1)) +- return; +- + if (time_is_before_jiffies(mi->sample_time)) + return; + +@@ -1810,7 +1731,7 @@ minstrel_ht_update_caps(void *priv, stru + minstrel_ht_update_ofdm(mp, mi, sband, sta); + + /* create an initial rate table with the lowest supported rates */ +- minstrel_ht_update_stats(mp, mi, true); ++ minstrel_ht_update_stats(mp, mi); + minstrel_ht_update_rates(mp, mi); + } + +@@ -1926,8 +1847,6 @@ minstrel_ht_alloc(struct ieee80211_hw *h + if (!mp) + return NULL; + +- mp->sample_switch = -1; +- + /* contention window settings + * Just an approximation. Using the per-queue values would complicate + * the calculations and is probably unnecessary */ +@@ -1947,7 +1866,7 @@ minstrel_ht_alloc(struct ieee80211_hw *h + mp->has_mrr = true; + + mp->hw = hw; +- mp->update_interval = HZ / 10; ++ mp->update_interval = HZ / 20; + + minstrel_ht_init_cck_rates(mp); + for (i = 0; i < ARRAY_SIZE(mp->hw->wiphy->bands); i++) +@@ -1965,8 +1884,6 @@ static void minstrel_ht_add_debugfs(stru + mp->fixed_rate_idx = (u32) -1; + debugfs_create_u32("fixed_rate_idx", S_IRUGO | S_IWUGO, debugfsdir, + &mp->fixed_rate_idx); +- debugfs_create_u32("sample_switch", S_IRUGO | S_IWUSR, debugfsdir, +- &mp->sample_switch); + } + #endif + +--- a/net/mac80211/rc80211_minstrel_ht.h ++++ b/net/mac80211/rc80211_minstrel_ht.h +@@ -75,7 +75,6 @@ + struct minstrel_priv { + struct ieee80211_hw *hw; + bool has_mrr; +- u32 sample_switch; + unsigned int cw_min; + unsigned int cw_max; + unsigned int max_retry; +@@ -147,12 +146,6 @@ struct minstrel_mcs_group_data { + struct minstrel_rate_stats rates[MCS_GROUP_RATES]; + }; + +-enum minstrel_sample_mode { +- MINSTREL_SAMPLE_IDLE, +- MINSTREL_SAMPLE_ACTIVE, +- MINSTREL_SAMPLE_PENDING, +-}; +- + struct minstrel_sample_category { + u8 sample_group; + u16 sample_rates[MINSTREL_SAMPLE_RATES]; +@@ -182,23 +175,19 @@ struct minstrel_ht_sta { + unsigned int overhead_legacy; + unsigned int overhead_legacy_rtscts; + +- unsigned int total_packets_last; +- unsigned int total_packets_cur; + unsigned int total_packets; + unsigned int sample_packets; + + /* tx flags to add for frames for this sta */ + u32 tx_flags; + +- unsigned long sample_time; +- struct minstrel_sample_category sample[__MINSTREL_SAMPLE_TYPE_MAX]; ++ u8 band; + + u8 sample_seq; +- +- enum minstrel_sample_mode sample_mode; + u16 sample_rate; + +- u8 band; ++ unsigned long sample_time; ++ struct minstrel_sample_category sample[__MINSTREL_SAMPLE_TYPE_MAX]; + + /* Bitfield of supported MCS rates of all groups */ + u16 supported[MINSTREL_GROUPS_NB]; diff --git a/package/kernel/mac80211/patches/subsys/352-mac80211-minstrel_ht-fix-regression-in-the-max_prob_.patch b/package/kernel/mac80211/patches/subsys/352-mac80211-minstrel_ht-fix-regression-in-the-max_prob_.patch new file mode 100644 index 0000000000..a366a921d4 --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/352-mac80211-minstrel_ht-fix-regression-in-the-max_prob_.patch @@ -0,0 +1,23 @@ +From: Felix Fietkau +Date: Tue, 26 Jan 2021 16:40:52 +0100 +Subject: [PATCH] mac80211: minstrel_ht: fix regression in the max_prob_rate + fix + +Since mi->max_prob_rate is overwritten after the loop that calls +minstrel_ht_set_best_prob_rate, the new best rate needs to be written to *dest + +Fixes: a7fca4e4037f ("mac80211: minstrel_ht: fix max probability rate selection") +Signed-off-by: Felix Fietkau +--- + +--- a/net/mac80211/rc80211_minstrel_ht.c ++++ b/net/mac80211/rc80211_minstrel_ht.c +@@ -545,7 +545,7 @@ minstrel_ht_set_best_prob_rate(struct mi + cur_tp_avg = minstrel_ht_get_tp_avg(mi, cur_group, cur_idx, + mrs->prob_avg); + if (cur_tp_avg > tmp_tp_avg) +- mi->max_prob_rate = index; ++ *dest = index; + + max_gpr_tp_avg = minstrel_ht_get_tp_avg(mi, max_gpr_group, + max_gpr_idx, diff --git a/package/network/services/dnsmasq/Makefile b/package/network/services/dnsmasq/Makefile index 7b5af1dd27..2e404dfef3 100644 --- a/package/network/services/dnsmasq/Makefile +++ b/package/network/services/dnsmasq/Makefile @@ -8,13 +8,13 @@ include $(TOPDIR)/rules.mk PKG_NAME:=dnsmasq -PKG_UPSTREAM_VERSION:=2.83 +PKG_UPSTREAM_VERSION:=2.84test3 PKG_VERSION:=$(subst test,~~test,$(subst rc,~rc,$(PKG_UPSTREAM_VERSION))) PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_UPSTREAM_VERSION).tar.xz -PKG_SOURCE_URL:=http://thekelleys.org.uk/dnsmasq -PKG_HASH:=ffc1f7e8b05e22d910b9a71d09f1128197292766dc7c54cb7018a1b2c3af4aea +PKG_SOURCE_URL:=http://thekelleys.org.uk/dnsmasq/test-releases +PKG_HASH:=20d1109c991ca08778ea20322b8f3245f2e974688d494b59b2e6ae096ec592b1 PKG_LICENSE:=GPL-2.0 PKG_LICENSE_FILES:=COPYING diff --git a/package/network/services/umdns/Makefile b/package/network/services/umdns/Makefile index 41945ce007..9a5f46a705 100644 --- a/package/network/services/umdns/Makefile +++ b/package/network/services/umdns/Makefile @@ -1,5 +1,5 @@ # -# Copyright (C) 2014 OpenWrt.org +# Copyright (C) 2014-2021 OpenWrt.org # # This is free software, licensed under the GNU General Public License v2. # See /LICENSE for more information. @@ -8,13 +8,13 @@ include $(TOPDIR)/rules.mk PKG_NAME:=umdns -PKG_RELEASE:=4 +PKG_RELEASE:=$(AUTORELEASE) PKG_SOURCE_URL=$(PROJECT_GIT)/project/mdnsd.git PKG_SOURCE_PROTO:=git -PKG_SOURCE_DATE:=2020-10-26 -PKG_SOURCE_VERSION:=59e4fc98162d253b4e5ecd110f7bc5ea3962e221 -PKG_MIRROR_HASH:=35fefc76d84c963ccb0aa72ac738065649f361d2946d3bc45fbd205d1dfc3d9f +PKG_SOURCE_DATE:=2021-01-26 +PKG_SOURCE_VERSION:=78aa36b0e9808e801c527c6dc47320e593309522 +PKG_MIRROR_HASH:=241833f2bf2f3366f356703159be386862ef747d9b253af6c13555f252cc970d PKG_MAINTAINER:=John Crispin PKG_LICENSE:=LGPL-2.1 diff --git a/package/network/utils/bpftools/Makefile b/package/network/utils/bpftools/Makefile index e760d50c5e..06a7a7adfd 100644 --- a/package/network/utils/bpftools/Makefile +++ b/package/network/utils/bpftools/Makefile @@ -8,12 +8,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=bpftools -PKG_VERSION:=5.8.9 +PKG_VERSION:=5.10.10 PKG_RELEASE:=1 PKG_SOURCE:=linux-$(PKG_VERSION).tar.xz PKG_SOURCE_URL:=@KERNEL/linux/kernel/v5.x -PKG_HASH:=99d8bc1b82f17d7d79f9af4a94af4c0e3772159e9e6e278761bde8569f93e15f +PKG_HASH:=60ed866fa951522a5255ea37ec3ac2006d3f3427d4783a13ef478464f37cdb19 PKG_MAINTAINER:=Tony Ambardar diff --git a/package/network/utils/bpftools/patches/001-libbpf-ensure-no-local-symbols-counted-in-ABI-check.patch b/package/network/utils/bpftools/patches/001-libbpf-ensure-no-local-symbols-counted-in-ABI-check.patch deleted file mode 100644 index 18000b6940..0000000000 --- a/package/network/utils/bpftools/patches/001-libbpf-ensure-no-local-symbols-counted-in-ABI-check.patch +++ /dev/null @@ -1,31 +0,0 @@ -From fafb2e7eaec6d33ce16e28f481edf781219d5d27 Mon Sep 17 00:00:00 2001 -From: Tony Ambardar -Date: Fri, 24 Jul 2020 23:58:17 -0700 -Subject: [PATCH] tools/libbpf: ensure no local symbols counted in ABI check - -This avoids finding versioned local symbols such as _init and _fini in -the libbpf.so file. - -Signed-off-by: Tony Ambardar ---- - tools/lib/bpf/Makefile | 2 ++ - 1 file changed, 2 insertions(+) - ---- a/tools/lib/bpf/Makefile -+++ b/tools/lib/bpf/Makefile -@@ -152,6 +152,7 @@ GLOBAL_SYM_COUNT = $(shell readelf -s -- - awk '/GLOBAL/ && /DEFAULT/ && !/UND/ {print $$NF}' | \ - sort -u | wc -l) - VERSIONED_SYM_COUNT = $(shell readelf --dyn-syms --wide $(OUTPUT)libbpf.so | \ -+ awk '/GLOBAL/ && /DEFAULT/ && !/UND/ {print $$NF}' | \ - grep -Eo '[^ ]+@LIBBPF_' | cut -d@ -f1 | sort -u | wc -l) - - CMD_TARGETS = $(LIB_TARGET) $(PC_FILE) -@@ -219,6 +220,7 @@ check_abi: $(OUTPUT)libbpf.so - awk '/GLOBAL/ && /DEFAULT/ && !/UND/ {print $$NF}'| \ - sort -u > $(OUTPUT)libbpf_global_syms.tmp; \ - readelf --dyn-syms --wide $(OUTPUT)libbpf.so | \ -+ awk '/GLOBAL/ && /DEFAULT/ && !/UND/ {print $$NF}'| \ - grep -Eo '[^ ]+@LIBBPF_' | cut -d@ -f1 | \ - sort -u > $(OUTPUT)libbpf_versioned_syms.tmp; \ - diff -u $(OUTPUT)libbpf_global_syms.tmp \ diff --git a/package/network/utils/bpftools/patches/002-libbpf-fix-build-failure-from-uninitialized-variable.patch b/package/network/utils/bpftools/patches/002-libbpf-fix-build-failure-from-uninitialized-variable.patch deleted file mode 100644 index 96ab2084b7..0000000000 --- a/package/network/utils/bpftools/patches/002-libbpf-fix-build-failure-from-uninitialized-variable.patch +++ /dev/null @@ -1,41 +0,0 @@ -From 74d0dcf7608b1bab116e297468ac51b57eb97ce0 Mon Sep 17 00:00:00 2001 -From: Tony Ambardar -Date: Thu, 20 Aug 2020 10:06:24 -0700 -Subject: [PATCH] libbpf: fix build failure from uninitialized variable warning - -While compiling libbpf, some GCC versions (at least 8.4.0) have difficulty -determining control flow and a emit warning for potentially uninitialized -usage of 'map', which results in a build error if using "-Werror": - -In file included from libbpf.c:56: -libbpf.c: In function '__bpf_object__open': -libbpf_internal.h:59:2: warning: 'map' may be used uninitialized in this function [-Wmaybe-uninitialized] - libbpf_print(level, "libbpf: " fmt, ##__VA_ARGS__); \ - ^~~~~~~~~~~~ -libbpf.c:5032:18: note: 'map' was declared here - struct bpf_map *map, *targ_map; - ^~~ - -The warning/error is false based on code inspection, so silence it with a -NULL initialization. - -Fixes: 646f02ffdd49 ("libbpf: Add BTF-defined map-in-map support") -Ref: 063e68813391 ("libbpf: Fix false uninitialized variable warning") - -Signed-off-by: Tony Ambardar ---- - tools/lib/bpf/libbpf.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/tools/lib/bpf/libbpf.c -+++ b/tools/lib/bpf/libbpf.c -@@ -5030,8 +5030,8 @@ static int bpf_object__collect_map_relos - int i, j, nrels, new_sz; - const struct btf_var_secinfo *vi = NULL; - const struct btf_type *sec, *var, *def; -+ struct bpf_map *map = NULL, *targ_map; - const struct btf_member *member; -- struct bpf_map *map, *targ_map; - const char *name, *mname; - Elf_Data *symbols; - unsigned int moff; diff --git a/package/network/utils/bpftools/patches/003-bpftool-allow-passing-BPFTOOL_VERSION-to-make.patch b/package/network/utils/bpftools/patches/003-bpftool-allow-passing-BPFTOOL_VERSION-to-make.patch deleted file mode 100644 index 1c569afe6b..0000000000 --- a/package/network/utils/bpftools/patches/003-bpftool-allow-passing-BPFTOOL_VERSION-to-make.patch +++ /dev/null @@ -1,21 +0,0 @@ -From 668d1c2951e18512a27aec20b80dea627d01bf04 Mon Sep 17 00:00:00 2001 -From: Tony Ambardar -Date: Thu, 20 Aug 2020 16:05:48 -0700 -Subject: [PATCH] tools/bpftool: allow passing BPFTOOL_VERSION to make - -Signed-off-by: Tony Ambardar ---- - tools/bpf/bpftool/Makefile | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/tools/bpf/bpftool/Makefile -+++ b/tools/bpf/bpftool/Makefile -@@ -25,7 +25,7 @@ endif - - LIBBPF = $(LIBBPF_PATH)libbpf.a - --BPFTOOL_VERSION := $(shell make -rR --no-print-directory -sC ../../.. kernelversion) -+BPFTOOL_VERSION ?= $(shell make -rR --no-print-directory -sC ../../.. kernelversion) - - $(LIBBPF): FORCE - $(if $(LIBBPF_OUTPUT),@mkdir -p $(LIBBPF_OUTPUT)) diff --git a/package/network/utils/bpftools/patches/004-v5.9-bpftool-use-only-ftw-for-file-tree-parsing.patch b/package/network/utils/bpftools/patches/004-v5.9-bpftool-use-only-ftw-for-file-tree-parsing.patch deleted file mode 100644 index 99ce513723..0000000000 --- a/package/network/utils/bpftools/patches/004-v5.9-bpftool-use-only-ftw-for-file-tree-parsing.patch +++ /dev/null @@ -1,231 +0,0 @@ -From 6edda7633e4fdf33b91c2e86c05cab805a0dabb3 Mon Sep 17 00:00:00 2001 -From: Tony Ambardar -Date: Mon, 20 Jul 2020 19:48:16 -0700 -Subject: [PATCH] bpftool: Use only nftw for file tree parsing - -The bpftool sources include code to walk file trees, but use multiple -frameworks to do so: nftw and fts. While nftw conforms to POSIX/SUSv3 and -is widely available, fts is not conformant and less common, especially on -non-glibc systems. The inconsistent framework usage hampers maintenance -and portability of bpftool, in particular for embedded systems. - -Standardize code usage by rewriting one fts-based function to use nftw and -clean up some related function warnings by extending use of "const char *" -arguments. This change helps in building bpftool against musl for OpenWrt. - -Also fix an unsafe call to dirname() by duplicating the string to pass, -since some implementations may directly alter it. The same approach is -used in libbpf.c. - -Signed-off-by: Tony Ambardar -Signed-off-by: Daniel Borkmann -Reviewed-by: Quentin Monnet -Link: https://lore.kernel.org/bpf/20200721024817.13701-1-Tony.Ambardar@gmail.com ---- - tools/bpf/bpftool/common.c | 137 ++++++++++++++++++++++--------------- - tools/bpf/bpftool/main.h | 4 +- - 2 files changed, 82 insertions(+), 59 deletions(-) - ---- a/tools/bpf/bpftool/common.c -+++ b/tools/bpf/bpftool/common.c -@@ -1,10 +1,11 @@ - // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) - /* Copyright (C) 2017-2018 Netronome Systems, Inc. */ - -+#define _GNU_SOURCE - #include - #include - #include --#include -+#include - #include - #include - #include -@@ -123,24 +124,35 @@ int mount_tracefs(const char *target) - return err; - } - --int open_obj_pinned(char *path, bool quiet) -+int open_obj_pinned(const char *path, bool quiet) - { -- int fd; -+ char *pname; -+ int fd = -1; - -- fd = bpf_obj_get(path); -+ pname = strdup(path); -+ if (!pname) { -+ if (!quiet) -+ p_err("mem alloc failed"); -+ goto out_ret; -+ } -+ -+ fd = bpf_obj_get(pname); - if (fd < 0) { - if (!quiet) -- p_err("bpf obj get (%s): %s", path, -- errno == EACCES && !is_bpffs(dirname(path)) ? -+ p_err("bpf obj get (%s): %s", pname, -+ errno == EACCES && !is_bpffs(dirname(pname)) ? - "directory not in bpf file system (bpffs)" : - strerror(errno)); -- return -1; -+ goto out_free; - } - -+out_free: -+ free(pname); -+out_ret: - return fd; - } - --int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type) -+int open_obj_pinned_any(const char *path, enum bpf_obj_type exp_type) - { - enum bpf_obj_type type; - int fd; -@@ -330,71 +342,82 @@ void print_hex_data_json(uint8_t *data, - jsonw_end_array(json_wtr); - } - -+/* extra params for nftw cb */ -+static struct pinned_obj_table *build_fn_table; -+static enum bpf_obj_type build_fn_type; -+ -+static int do_build_table_cb(const char *fpath, const struct stat *sb, -+ int typeflag, struct FTW *ftwbuf) -+{ -+ struct bpf_prog_info pinned_info; -+ __u32 len = sizeof(pinned_info); -+ struct pinned_obj *obj_node; -+ enum bpf_obj_type objtype; -+ int fd, err = 0; -+ -+ if (typeflag != FTW_F) -+ goto out_ret; -+ -+ fd = open_obj_pinned(fpath, true); -+ if (fd < 0) -+ goto out_ret; -+ -+ objtype = get_fd_type(fd); -+ if (objtype != build_fn_type) -+ goto out_close; -+ -+ memset(&pinned_info, 0, sizeof(pinned_info)); -+ if (bpf_obj_get_info_by_fd(fd, &pinned_info, &len)) -+ goto out_close; -+ -+ obj_node = calloc(1, sizeof(*obj_node)); -+ if (!obj_node) { -+ err = -1; -+ goto out_close; -+ } -+ -+ obj_node->id = pinned_info.id; -+ obj_node->path = strdup(fpath); -+ if (!obj_node->path) { -+ err = -1; -+ free(obj_node); -+ goto out_close; -+ } -+ -+ hash_add(build_fn_table->table, &obj_node->hash, obj_node->id); -+out_close: -+ close(fd); -+out_ret: -+ return err; -+} -+ - int build_pinned_obj_table(struct pinned_obj_table *tab, - enum bpf_obj_type type) - { -- struct bpf_prog_info pinned_info = {}; -- struct pinned_obj *obj_node = NULL; -- __u32 len = sizeof(pinned_info); - struct mntent *mntent = NULL; -- enum bpf_obj_type objtype; - FILE *mntfile = NULL; -- FTSENT *ftse = NULL; -- FTS *fts = NULL; -- int fd, err; -+ int flags = FTW_PHYS; -+ int nopenfd = 16; -+ int err = 0; - - mntfile = setmntent("/proc/mounts", "r"); - if (!mntfile) - return -1; - -+ build_fn_table = tab; -+ build_fn_type = type; -+ - while ((mntent = getmntent(mntfile))) { -- char *path[] = { mntent->mnt_dir, NULL }; -+ char *path = mntent->mnt_dir; - - if (strncmp(mntent->mnt_type, "bpf", 3) != 0) - continue; -- -- fts = fts_open(path, 0, NULL); -- if (!fts) -- continue; -- -- while ((ftse = fts_read(fts))) { -- if (!(ftse->fts_info & FTS_F)) -- continue; -- fd = open_obj_pinned(ftse->fts_path, true); -- if (fd < 0) -- continue; -- -- objtype = get_fd_type(fd); -- if (objtype != type) { -- close(fd); -- continue; -- } -- memset(&pinned_info, 0, sizeof(pinned_info)); -- err = bpf_obj_get_info_by_fd(fd, &pinned_info, &len); -- if (err) { -- close(fd); -- continue; -- } -- -- obj_node = malloc(sizeof(*obj_node)); -- if (!obj_node) { -- close(fd); -- fts_close(fts); -- fclose(mntfile); -- return -1; -- } -- -- memset(obj_node, 0, sizeof(*obj_node)); -- obj_node->id = pinned_info.id; -- obj_node->path = strdup(ftse->fts_path); -- hash_add(tab->table, &obj_node->hash, obj_node->id); -- -- close(fd); -- } -- fts_close(fts); -+ err = nftw(path, do_build_table_cb, nopenfd, flags); -+ if (err) -+ break; - } - fclose(mntfile); -- return 0; -+ return err; - } - - void delete_pinned_obj_table(struct pinned_obj_table *tab) ---- a/tools/bpf/bpftool/main.h -+++ b/tools/bpf/bpftool/main.h -@@ -196,8 +196,8 @@ int cmd_select(const struct cmd *cmds, i - int get_fd_type(int fd); - const char *get_fd_type_name(enum bpf_obj_type type); - char *get_fdinfo(int fd, const char *key); --int open_obj_pinned(char *path, bool quiet); --int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type); -+int open_obj_pinned(const char *path, bool quiet); -+int open_obj_pinned_any(const char *path, enum bpf_obj_type exp_type); - int mount_bpffs_for_pin(const char *name); - int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(int *, char ***)); - int do_pin_fd(int fd, const char *name); diff --git a/package/utils/px5g-wolfssl/Makefile b/package/utils/px5g-wolfssl/Makefile index be36f9f33d..90296008d6 100644 --- a/package/utils/px5g-wolfssl/Makefile +++ b/package/utils/px5g-wolfssl/Makefile @@ -5,7 +5,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=px5g-wolfssl -PKG_RELEASE:=1 +PKG_RELEASE:=$(COMMITCOUNT) PKG_LICENSE:=GPL-2.0-or-later PKG_USE_MIPS16:=0 diff --git a/package/utils/px5g-wolfssl/px5g-wolfssl.c b/package/utils/px5g-wolfssl/px5g-wolfssl.c index b937d220ca..763d7b4b71 100644 --- a/package/utils/px5g-wolfssl/px5g-wolfssl.c +++ b/package/utils/px5g-wolfssl/px5g-wolfssl.c @@ -232,8 +232,10 @@ int selfsigned(WC_RNG *rng, char **arg) { subject, fstr, tstr); if (type == EC_KEY_TYPE) { + newCert.sigType = CTC_SHA256wECDSA; ret = wc_MakeCert(&newCert, derBuf, sizeof(derBuf), NULL, &ecKey, rng); } else { + newCert.sigType = CTC_SHA256wRSA; ret = wc_MakeCert(&newCert, derBuf, sizeof(derBuf), &rsaKey, NULL, rng); } if (ret <= 0) { @@ -242,11 +244,9 @@ int selfsigned(WC_RNG *rng, char **arg) { } if (type == EC_KEY_TYPE) { - newCert.sigType = CTC_SHA256wECDSA; ret = wc_SignCert(newCert.bodySz, newCert.sigType, derBuf, sizeof(derBuf), NULL, &ecKey, rng); } else { - newCert.sigType = CTC_SHA256wRSA; ret = wc_SignCert(newCert.bodySz, newCert.sigType, derBuf, sizeof(derBuf), &rsaKey, NULL, rng); } diff --git a/rules.mk b/rules.mk index cf4aa30d21..b1b69eabf9 100644 --- a/rules.mk +++ b/rules.mk @@ -421,7 +421,7 @@ $(shell \ if [ -n "$$last_bump" ]; then \ echo -n $$(($$(git rev-list --count "$$last_bump..HEAD" .) + 1)); \ else \ - echo -n $$(($$(git rev-list --count HEAD .) + 1)); \ + git rev-list --count HEAD .; \ fi; \ else \ secs="$$(($(SOURCE_DATE_EPOCH) % 86400))"; \ diff --git a/target/linux/archs38/config-5.4 b/target/linux/archs38/config-5.4 index d98ba4ce32..57d708e77a 100644 --- a/target/linux/archs38/config-5.4 +++ b/target/linux/archs38/config-5.4 @@ -217,7 +217,6 @@ CONFIG_OF_MDIO=y CONFIG_OF_NET=y CONFIG_PADATA=y CONFIG_PAGE_POOL=y -CONFIG_PERF_EVENTS=y CONFIG_PGTABLE_LEVELS=2 CONFIG_PHYLIB=y CONFIG_PHYLINK=y diff --git a/target/linux/generic/hack-5.4/721-phy_packets.patch b/target/linux/generic/hack-5.4/721-phy_packets.patch index 407597c896..dba648f8e6 100644 --- a/target/linux/generic/hack-5.4/721-phy_packets.patch +++ b/target/linux/generic/hack-5.4/721-phy_packets.patch @@ -56,7 +56,7 @@ Signed-off-by: Felix Fietkau */ --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h -@@ -2679,6 +2679,10 @@ static inline int pskb_trim(struct sk_bu +@@ -2684,6 +2684,10 @@ static inline int pskb_trim(struct sk_bu return (len < skb->len) ? __pskb_trim(skb, len) : 0; } @@ -67,7 +67,7 @@ Signed-off-by: Felix Fietkau /** * pskb_trim_unique - remove end from a paged unique (not cloned) buffer * @skb: buffer to alter -@@ -2810,16 +2814,6 @@ static inline struct sk_buff *dev_alloc_ +@@ -2815,16 +2819,6 @@ static inline struct sk_buff *dev_alloc_ } @@ -136,7 +136,7 @@ Signed-off-by: Felix Fietkau #include #include -@@ -540,6 +541,22 @@ skb_fail: +@@ -545,6 +546,22 @@ skb_fail: } EXPORT_SYMBOL(__napi_alloc_skb); diff --git a/target/linux/generic/pending-5.4/655-increase_skb_pad.patch b/target/linux/generic/pending-5.4/655-increase_skb_pad.patch index f7ab7e492f..e325301d3c 100644 --- a/target/linux/generic/pending-5.4/655-increase_skb_pad.patch +++ b/target/linux/generic/pending-5.4/655-increase_skb_pad.patch @@ -9,7 +9,7 @@ Signed-off-by: Felix Fietkau --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h -@@ -2645,7 +2645,7 @@ static inline int pskb_network_may_pull( +@@ -2650,7 +2650,7 @@ static inline int pskb_network_may_pull( * NET_IP_ALIGN(2) + ethernet_header(14) + IP_header(20/40) + ports(8) */ #ifndef NET_SKB_PAD diff --git a/target/linux/ipq807x/config-default b/target/linux/ipq807x/config-default index 9ee84791a1..24f99f4219 100644 --- a/target/linux/ipq807x/config-default +++ b/target/linux/ipq807x/config-default @@ -459,7 +459,6 @@ CONFIG_PCI_DOMAINS_GENERIC=y CONFIG_PCI_LABEL=y CONFIG_PCI_MSI=y CONFIG_PCI_MSI_IRQ_DOMAIN=y -CONFIG_PERF_EVENTS=y CONFIG_PGTABLE_LEVELS=3 CONFIG_PHYLIB=y CONFIG_PHYS_ADDR_T_64BIT=y diff --git a/target/linux/layerscape/armv7/config-5.4 b/target/linux/layerscape/armv7/config-5.4 index 019119d6db..ba64e8fdda 100644 --- a/target/linux/layerscape/armv7/config-5.4 +++ b/target/linux/layerscape/armv7/config-5.4 @@ -549,7 +549,6 @@ CONFIG_PCI_HOST_GENERIC=y CONFIG_PCI_LAYERSCAPE=y CONFIG_PCI_MSI=y CONFIG_PCI_MSI_IRQ_DOMAIN=y -CONFIG_PERF_EVENTS=y CONFIG_PERF_USE_VMALLOC=y CONFIG_PGTABLE_LEVELS=3 CONFIG_PHYLIB=y diff --git a/target/linux/layerscape/armv8_64b/config-5.4 b/target/linux/layerscape/armv8_64b/config-5.4 index cd8ea82e52..75f31e2bc4 100644 --- a/target/linux/layerscape/armv8_64b/config-5.4 +++ b/target/linux/layerscape/armv8_64b/config-5.4 @@ -740,7 +740,6 @@ CONFIG_PCI_IOV=y CONFIG_PCI_LAYERSCAPE=y CONFIG_PCI_MSI=y CONFIG_PCI_MSI_IRQ_DOMAIN=y -CONFIG_PERF_EVENTS=y CONFIG_PGTABLE_LEVELS=4 CONFIG_PHYLIB=y CONFIG_PHYS_ADDR_T_64BIT=y diff --git a/target/linux/layerscape/patches-5.4/303-core-0001-net-readd-skb_recycle.patch b/target/linux/layerscape/patches-5.4/303-core-0001-net-readd-skb_recycle.patch index cd52f66084..7b257bb17c 100644 --- a/target/linux/layerscape/patches-5.4/303-core-0001-net-readd-skb_recycle.patch +++ b/target/linux/layerscape/patches-5.4/303-core-0001-net-readd-skb_recycle.patch @@ -24,7 +24,7 @@ Signed-off-by: Madalin Bucur --- a/net/core/skbuff.c +++ b/net/core/skbuff.c -@@ -936,6 +936,32 @@ void napi_consume_skb(struct sk_buff *sk +@@ -941,6 +941,32 @@ void napi_consume_skb(struct sk_buff *sk } EXPORT_SYMBOL(napi_consume_skb); diff --git a/target/linux/malta/config-5.4 b/target/linux/malta/config-5.4 index d480e1de42..73f6965d01 100644 --- a/target/linux/malta/config-5.4 +++ b/target/linux/malta/config-5.4 @@ -56,7 +56,7 @@ CONFIG_CPU_HAS_PREFETCH=y # CONFIG_CPU_HAS_SMARTMIPS is not set CONFIG_CPU_HAS_SYNC=y # CONFIG_CPU_MICROMIPS is not set -CONFIG_CPU_MIPS32=y +# CONFIG_CPU_MIPS32 is not set # CONFIG_CPU_MIPS32_3_5_FEATURES is not set # CONFIG_CPU_MIPS32_R1 is not set # CONFIG_CPU_MIPS32_R2 is not set @@ -65,9 +65,9 @@ CONFIG_CPU_MIPS32=y # CONFIG_CPU_MIPS64_R2 is not set # CONFIG_CPU_MIPS32_R5_FEATURES is not set # CONFIG_CPU_MIPS64_R6 is not set -CONFIG_CPU_MIPSR1=y -CONFIG_CPU_MIPSR2_IRQ_EI=y -CONFIG_CPU_MIPSR2_IRQ_VI=y +# CONFIG_CPU_MIPSR2 is not set +# CONFIG_CPU_MIPSR2_IRQ_EI is not set +# CONFIG_CPU_MIPSR2_IRQ_VI is not set CONFIG_CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS=y # CONFIG_CPU_NEVADA is not set CONFIG_CPU_R4K_CACHE_TLB=y @@ -253,7 +253,6 @@ CONFIG_PCI_DOMAINS=y CONFIG_PCI_DRIVERS_LEGACY=y CONFIG_PCI_GT64XXX_PCI0=y CONFIG_PCSPKR_PLATFORM=y -CONFIG_PERF_EVENTS=y CONFIG_PERF_USE_VMALLOC=y CONFIG_PGTABLE_LEVELS=2 CONFIG_POWER_RESET=y diff --git a/target/linux/mediatek/mt7622/config-5.4 b/target/linux/mediatek/mt7622/config-5.4 index 929a04ce07..ffebc43cb2 100644 --- a/target/linux/mediatek/mt7622/config-5.4 +++ b/target/linux/mediatek/mt7622/config-5.4 @@ -458,7 +458,6 @@ CONFIG_PCI_DOMAINS=y CONFIG_PCI_DOMAINS_GENERIC=y CONFIG_PCI_MSI=y CONFIG_PCI_MSI_IRQ_DOMAIN=y -CONFIG_PERF_EVENTS=y CONFIG_PGTABLE_LEVELS=3 CONFIG_PHYLIB=y CONFIG_PHYLINK=y diff --git a/target/linux/mediatek/mt7629/config-5.4 b/target/linux/mediatek/mt7629/config-5.4 index 7dd04b4e2e..82ef4996af 100644 --- a/target/linux/mediatek/mt7629/config-5.4 +++ b/target/linux/mediatek/mt7629/config-5.4 @@ -290,7 +290,6 @@ CONFIG_PCI_DOMAINS=y CONFIG_PCI_DOMAINS_GENERIC=y CONFIG_PCI_MSI=y CONFIG_PCI_MSI_IRQ_DOMAIN=y -CONFIG_PERF_EVENTS=y CONFIG_PERF_USE_VMALLOC=y CONFIG_PGTABLE_LEVELS=2 CONFIG_PHYLIB=y diff --git a/target/linux/mediatek/patches-5.4/0600-net-phylink-propagate-resolved-link-config-via-mac_l.patch b/target/linux/mediatek/patches-5.4/0600-net-phylink-propagate-resolved-link-config-via-mac_l.patch index fc987e8e9d..3bf1d0c135 100644 --- a/target/linux/mediatek/patches-5.4/0600-net-phylink-propagate-resolved-link-config-via-mac_l.patch +++ b/target/linux/mediatek/patches-5.4/0600-net-phylink-propagate-resolved-link-config-via-mac_l.patch @@ -95,7 +95,7 @@ Signed-off-by: David S. Miller } netif_tx_start_all_queues(port->dev); -@@ -5139,8 +5143,11 @@ static void mvpp2_mac_config(struct phyl +@@ -5137,8 +5141,11 @@ static void mvpp2_mac_config(struct phyl mvpp2_port_enable(port); } diff --git a/target/linux/oxnas/config-5.4 b/target/linux/oxnas/config-5.4 index d8c87acabf..ed6f03d388 100644 --- a/target/linux/oxnas/config-5.4 +++ b/target/linux/oxnas/config-5.4 @@ -267,7 +267,6 @@ CONFIG_PAGE_POOL=y # CONFIG_PANIC_ON_OOPS is not set CONFIG_PANIC_ON_OOPS_VALUE=0 CONFIG_PANIC_TIMEOUT=0 -CONFIG_PERF_EVENTS=y CONFIG_PERF_USE_VMALLOC=y CONFIG_PGTABLE_LEVELS=2 CONFIG_PHYLIB=y diff --git a/target/linux/realtek/config-5.4 b/target/linux/realtek/config-5.4 index 2176a2cf50..2fbd904376 100644 --- a/target/linux/realtek/config-5.4 +++ b/target/linux/realtek/config-5.4 @@ -2,7 +2,6 @@ CONFIG_ARCH_32BIT_OFF_T=y CONFIG_ARCH_CLOCKSOURCE_DATA=y CONFIG_ARCH_HIBERNATION_POSSIBLE=y CONFIG_ARCH_MMAP_RND_BITS_MAX=15 -CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=15 CONFIG_ARCH_SUSPEND_POSSIBLE=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_COUNT=16 @@ -10,6 +9,10 @@ CONFIG_BLK_DEV_RAM_SIZE=4096 CONFIG_CEVT_R4K=y CONFIG_CLONE_BACKWARDS=y CONFIG_COMPAT_32BIT_TIME=y +CONFIG_HAVE_CLK=y +CONFIG_CLKDEV_LOOKUP=y +CONFIG_COMMON_CLK=y +CONFIG_COMMON_CLK_BOSTON=y CONFIG_CONSOLE_LOGLEVEL_DEFAULT=15 CONFIG_CPU_BIG_ENDIAN=y CONFIG_CPU_GENERIC_DUMP_TLB=y @@ -160,6 +163,7 @@ CONFIG_REGMAP=y CONFIG_REGMAP_MMIO=y CONFIG_RESET_CONTROLLER=y CONFIG_RTL838X=y +CONFIG_RTL9300_TIMER=y CONFIG_SERIAL_MCTRL_GPIO=y CONFIG_SERIAL_OF_PLATFORM=y CONFIG_SFP=y @@ -168,7 +172,7 @@ CONFIG_SPI_MASTER=y CONFIG_SPI_MEM=y CONFIG_SPI_RTL838X=y CONFIG_SRCU=y -CONFIG_SWCONFIG=y +CONFIG_SWAP_IO_SPACE=y CONFIG_SWPHY=y CONFIG_SYSCTL_EXCEPTION_TRACE=y CONFIG_SYS_HAS_CPU_MIPS32_R1=y diff --git a/target/linux/realtek/dts/rtl8380_zyxel_gs1900.dtsi b/target/linux/realtek/dts/rtl8380_zyxel_gs1900.dtsi index 5f06339d13..c4441ec30e 100644 --- a/target/linux/realtek/dts/rtl8380_zyxel_gs1900.dtsi +++ b/target/linux/realtek/dts/rtl8380_zyxel_gs1900.dtsi @@ -92,7 +92,8 @@ partition@b260000 { label = "firmware"; reg = <0x260000 0x6d0000>; - compatible = "denx,uimage"; + compatible = "openwrt,uimage", "denx,uimage"; + openwrt,ih-magic = <0x83800000>; }; partition@930000 { label = "runtime2"; diff --git a/target/linux/realtek/dts/rtl838x.dtsi b/target/linux/realtek/dts/rtl838x.dtsi index 36d8847a15..b59b141f66 100644 --- a/target/linux/realtek/dts/rtl838x.dtsi +++ b/target/linux/realtek/dts/rtl838x.dtsi @@ -101,8 +101,8 @@ clock-frequency = <200000000>; - interrupt-parent = <&cpuintc>; - interrupts = <3>; + interrupt-parent = <&intc>; + interrupts = <31>; reg-io-width = <1>; reg-shift = <2>; @@ -180,9 +180,8 @@ switch0: switch@bb000000 { status = "okay"; - interrupt-parent = <&cpuintc>; - interrupts = <4>; - + interrupt-parent = <&intc>; + interrupts = <20>; compatible = "realtek,rtl83xx-switch"; }; diff --git a/target/linux/realtek/dts/rtl930x.dtsi b/target/linux/realtek/dts/rtl930x.dtsi new file mode 100644 index 0000000000..5d13fc31b3 --- /dev/null +++ b/target/linux/realtek/dts/rtl930x.dtsi @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +/dts-v1/; + +#define STRINGIZE(s) #s +#define LAN_LABEL(p, s) STRINGIZE(p ## s) +#define SWITCH_PORT_LABEL(n) LAN_LABEL(lan, n) + +#define INTERNAL_PHY(n) \ + phy##n: ethernet-phy@##n { \ + reg = <##n>; \ + compatible = "ethernet-phy-ieee802.3-c22"; \ + phy-is-integrated; \ + }; + +#define EXTERNAL_PHY(n) \ + phy##n: ethernet-phy@##n { \ + reg = <##n>; \ + compatible = "ethernet-phy-ieee802.3-c22"; \ + }; + +#define EXTERNAL_SFP_PHY(n) \ + phy##n: ethernet-phy@##n { \ + compatible = "ethernet-phy-ieee802.3-c22"; \ + sfp; \ + media = "fibre"; \ + reg = <##n>; \ + }; + +#define SWITCH_PORT(n, s, m) \ + port@##n { \ + reg = <##n>; \ + label = SWITCH_PORT_LABEL(s) ; \ + phy-handle = <&phy##n>; \ + phy-mode = #m ; \ + }; + +#define SWITCH_SFP_PORT(n, s, m) \ + port@##n { \ + reg = <##n>; \ + label = SWITCH_PORT_LABEL(s) ; \ + phy-handle = <&phy##n>; \ + phy-mode = #m ; \ + fixed-link { \ + speed = <1000>; \ + full-duplex; \ + }; \ + }; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + compatible = "realtek,rtl838x-soc"; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + frequency = <800000000>; + + cpu@0 { + compatible = "mips,mips34Kc"; + reg = <0>; + }; + }; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x8000000>; + }; + + chosen { + bootargs = "console=ttyS0,38400"; + }; + + cpuintc: cpuintc { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + compatible = "mti,cpu-interrupt-controller"; + }; + + intc: rtlintc { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + compatible = "realtek,rt9300-intc"; + reg = <0xb8003000 0x20>; + }; + + osc: oscillator { + compatible = "fixed-clock"; + #clock-cells = <1>; + clock-frequency = <175000000>; + clock-output-names = "osc"; + }; + + timer: timer@b8003200 { + compatible = "realtek,rtl9300-timer"; + reg = <0xb8003200 0x60>; + interrupt-parent = <&intc>; + interrupts = <8>; + interrupt-names = "ostimer"; + clocks = <&osc 0>; + }; + + spi0: spi@b8001200 { + status = "okay"; + + compatible = "realtek,rtl838x-nor"; + reg = <0xb8001200 0x100>; + + #address-cells = <1>; + #size-cells = <0>; + }; + + uart0: uart@b8002000 { + compatible = "ns16550a"; + reg = <0xb8002000 0x100>; + + clock-frequency = <175000000>; + + interrupt-parent = <&intc>; + interrupts = <30>; + + reg-io-width = <1>; + reg-shift = <2>; + fifo-size = <1>; + no-loopback-test; + + status = "okay"; + }; + + uart1: uart@b8002100 { + compatible = "ns16550a"; + reg = <0xb8002100 0x100>; + + clock-frequency = <175000000>; + + interrupt-parent = <&intc>; + interrupts = <31>; + + reg-io-width = <1>; + reg-shift = <2>; + fifo-size = <1>; + no-loopback-test; + + status = "okay"; + }; + + gpio0: gpio-controller@b8003500 { + compatible = "realtek,rtl838x-gpio"; + reg = <0xb8003500 0x20>; + gpio-controller; + #gpio-cells = <2>; + interrupt-parent = <&intc>; + interrupts = <31>; + }; + + ethernet0: ethernet@bb00a300 { + status = "okay"; + compatible = "realtek,rtl838x-eth"; + reg = <0xbb00a300 0x100>; + interrupt-parent = <&intc>; + interrupts = <24>; + #interrupt-cells = <1>; + phy-mode = "internal"; + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + switch0: switch@bb000000 { + status = "okay"; + + interrupt-parent = <&intc>; + interrupts = <20>; + + compatible = "realtek,rtl83xx-switch"; + }; +}; diff --git a/target/linux/realtek/files-5.4/arch/mips/include/asm/mach-rtl838x/irq.h b/target/linux/realtek/files-5.4/arch/mips/include/asm/mach-rtl838x/irq.h index 580c3d8d61..a4e95ab511 100644 --- a/target/linux/realtek/files-5.4/arch/mips/include/asm/mach-rtl838x/irq.h +++ b/target/linux/realtek/files-5.4/arch/mips/include/asm/mach-rtl838x/irq.h @@ -34,6 +34,9 @@ #define UART1_CASCADE 1 #define TC0_CASCADE 5 #define TC1_CASCADE 1 +#define TC2_CASCADE 1 +#define TC3_CASCADE 1 +#define TC4_CASCADE 1 #define OCPTO_CASCADE 1 #define HLXTO_CASCADE 1 #define SLXTO_CASCADE 1 @@ -44,6 +47,7 @@ #define SWCORE_CASCADE 3 #define WDT_IP1_CASCADE 4 #define WDT_IP2_CASCADE 5 +#define USB_H2_CASCADE 1 /* Pack cascade map into interrupt routing registers */ #define RTL83XX_IRR0_SETTING (\ @@ -63,4 +67,27 @@ #define RTL83XX_IRR2_SETTING 0 #define RTL83XX_IRR3_SETTING 0 +/* On the RTL8390 there is no GPIO_EFGH and RTC IRQ */ +#define RTL8390_IRR1_SETTING (\ + (GPIO_ABCD_CASCADE << 28) | \ + (SWCORE_CASCADE << 16)) + +/* The RTL9300 has a different external IRQ numbering scheme */ +#define RTL9300_IRR0_SETTING (\ + (UART1_CASCADE << 28) | \ + (UART0_CASCADE << 24) | \ + (USB_H2_CASCADE << 16) | \ + (NIC_CASCADE << 0)) +#define RTL9300_IRR1_SETTING (\ + (SWCORE_CASCADE << 28)) +#define RTL9300_IRR2_SETTING (\ + (GPIO_ABCD_CASCADE << 20) | \ + (TC4_CASCADE << 12) | \ + (TC3_CASCADE << 8) | \ + (TC2_CASCADE << 4) | \ + (TC1_CASCADE << 0)) +#define RTL9300_IRR3_SETTING (\ + (TC0_CASCADE << 28) | \ + (WDT_IP1_CASCADE << 20)) + #endif /* _RTL83XX_IRQ_H_ */ diff --git a/target/linux/realtek/files-5.4/arch/mips/include/asm/mach-rtl838x/mach-rtl83xx.h b/target/linux/realtek/files-5.4/arch/mips/include/asm/mach-rtl838x/mach-rtl83xx.h index b7d2a6f037..fc401e5481 100644 --- a/target/linux/realtek/files-5.4/arch/mips/include/asm/mach-rtl838x/mach-rtl83xx.h +++ b/target/linux/realtek/files-5.4/arch/mips/include/asm/mach-rtl838x/mach-rtl83xx.h @@ -47,20 +47,32 @@ #define RTL838X_IRQ_ICTL_BASE (RTL838X_IRQ_CPU_BASE + RTL838X_IRQ_CPU_NUM) #define RTL838X_IRQ_ICTL_NUM 32 -#define RTL83XX_IRQ_UART0 31 -#define RTL83XX_IRQ_UART1 30 -#define RTL83XX_IRQ_TC0 29 -#define RTL83XX_IRQ_TC1 28 -#define RTL83XX_IRQ_OCPTO 27 -#define RTL83XX_IRQ_HLXTO 26 -#define RTL83XX_IRQ_SLXTO 25 -#define RTL83XX_IRQ_NIC 24 -#define RTL83XX_IRQ_GPIO_ABCD 23 -#define RTL83XX_IRQ_GPIO_EFGH 22 -#define RTL83XX_IRQ_RTC 21 -#define RTL83XX_IRQ_SWCORE 20 -#define RTL83XX_IRQ_WDT_IP1 19 -#define RTL83XX_IRQ_WDT_IP2 18 +#define RTL83XX_IRQ_UART0 31 +#define RTL83XX_IRQ_UART1 30 +#define RTL83XX_IRQ_TC0 29 +#define RTL83XX_IRQ_TC1 28 +#define RTL83XX_IRQ_OCPTO 27 +#define RTL83XX_IRQ_HLXTO 26 +#define RTL83XX_IRQ_SLXTO 25 +#define RTL83XX_IRQ_NIC 24 +#define RTL83XX_IRQ_GPIO_ABCD 23 +#define RTL83XX_IRQ_GPIO_EFGH 22 +#define RTL83XX_IRQ_RTC 21 +#define RTL83XX_IRQ_SWCORE 20 +#define RTL83XX_IRQ_WDT_IP1 19 +#define RTL83XX_IRQ_WDT_IP2 18 + +#define RTL9300_UART1_IRQ 31 +#define RTL9300_UART0_IRQ 30 +#define RTL9300_USB_H2_IRQ 28 +#define RTL9300_NIC_IRQ 24 +#define RTL9300_SWCORE_IRQ 23 +#define RTL9300_GPIO_ABC_IRQ 13 +#define RTL9300_TC4_IRQ 11 +#define RTL9300_TC3_IRQ 10 +#define RTL9300_TC2_IRQ 9 +#define RTL9300_TC1_IRQ 8 +#define RTL9300_TC0_IRQ 7 /* @@ -108,32 +120,6 @@ #define WDT_IP1_IP (1 << 19) #define WDT_IP2_IP (1 << 18) -#define IRR0 (0x08) -#define IRR0_SETTING ((UART0_RS << 28) | \ - (UART1_RS << 24) | \ - (TC0_RS << 20) | \ - (TC1_RS << 16) | \ - (OCPTO_RS << 12) | \ - (HLXTO_RS << 8) | \ - (SLXTO_RS << 4) | \ - (NIC_RS << 0) \ - ) - -#define IRR1 (0x0c) -#define IRR1_SETTING_RTL838X ((GPIO_ABCD_RS << 28) | \ - (GPIO_EFGH_RS << 24) | \ - (RTC_RS << 20) | \ - (SWCORE_RS << 16) \ - ) -#define IRR1_SETTING_RTL839X ((GPIO_ABCD_RS << 28) | \ - (SWCORE_RS << 16) \ - ) - -#define IRR2 (0x10) -#define IRR2_SETTING 0 - -#define IRR3 (0x14) -#define IRR3_SETTING 0 /* Interrupt Routing Selection */ #define UART0_RS 2 @@ -200,107 +186,9 @@ #define UART1_DLM (RTL838X_UART1_BASE + 0x004) #define UART1_IIR (RTL838X_UART1_BASE + 0x008) #define UART1_FCR (RTL838X_UART1_BASE + 0x008) - #define FCR_EN 0x01 - #define FCR_RXRST 0x02 - #define XRST 0x02 - #define FCR_TXRST 0x04 - #define TXRST 0x04 - #define FCR_DMA 0x08 - #define FCR_RTRG 0xC0 - #define CHAR_TRIGGER_01 0x00 - #define CHAR_TRIGGER_04 0x40 - #define CHAR_TRIGGER_08 0x80 - #define CHAR_TRIGGER_14 0xC0 #define UART1_LCR (RTL838X_UART1_BASE + 0x00C) - #define LCR_WLN 0x03 - #define CHAR_LEN_5 0x00 - #define CHAR_LEN_6 0x01 - #define CHAR_LEN_7 0x02 - #define CHAR_LEN_8 0x03 - #define LCR_STB 0x04 - #define ONE_STOP 0x00 - #define TWO_STOP 0x04 - #define LCR_PEN 0x08 - #define PARITY_ENABLE 0x01 - #define PARITY_DISABLE 0x00 - #define LCR_EPS 0x30 - #define PARITY_ODD 0x00 - #define PARITY_EVEN 0x10 - #define PARITY_MARK 0x20 - #define PARITY_SPACE 0x30 - #define LCR_BRK 0x40 - #define LCR_DLAB 0x80 - #define DLAB 0x80 #define UART1_MCR (RTL838X_UART1_BASE + 0x010) #define UART1_LSR (RTL838X_UART1_BASE + 0x014) - #define LSR_DR 0x01 - #define RxCHAR_AVAIL 0x01 - #define LSR_OE 0x02 - #define LSR_PE 0x04 - #define LSR_FE 0x08 - #define LSR_BI 0x10 - #define LSR_THRE 0x20 - #define TxCHAR_AVAIL 0x00 - #define TxCHAR_EMPTY 0x20 - #define LSR_TEMT 0x40 - #define LSR_RFE 0x80 - -/* - * Timer/counter for 8390/80/28 TC & MP chip - */ -#define RTL838X_TIMER0_BASE ((volatile void *)(0xb8003100UL)) -#define RTL838X_TIMER0_IRQ RTL838X_TC0_EXT_IRQ - -#define RTL8390TC_TC1DATA (RTL838X_TIMER0_BASE + 0x04) -#define RTL8390TC_TCD_OFFSET 8 -#define RTL8390TC_TC0CNT (RTL838X_TIMER0_BASE + 0x08) -#define RTL8390TC_TC1CNT (RTL838X_TIMER0_BASE + 0x0C) -#define RTL8390TC_TCCNR (RTL838X_TIMER0_BASE + 0x10) -#define RTL8390TC_TC0EN (1 << 31) -#define RTL8390TC_TC0MODE_TIMER (1 << 30) -#define RTL8390TC_TC1EN (1 << 29) -#define RTL8390TC_TC1MODE_TIMER (1 << 28) -#define RTL8390TC_TCIR (RTL838X_TIMER0_BASE + 0x14) -#define RTL8390TC_TC0IE (1 << 31) -#define RTL8390TC_TC1IE (1 << 30) -#define RTL8390TC_TC0IP (1 << 29) -#define RTL8390TC_TC1IP (1 << 28) -#define RTL8390TC_CDBR (RTL838X_TIMER0_BASE + 0x18) -#define RTL8390TC_DIVF_OFFSET 16 -#define RTL8390TC_WDTCNR (RTL838X_TIMER0_BASE + 0x1C) - -#define RTL8390MP_TC1DATA (RTL838X_TIMER0_BASE + 0x10) -#define RTL8390MP_TC0CNT (RTL838X_TIMER0_BASE + 0x04) -#define RTL8390MP_TC1CNT (RTL838X_TIMER0_BASE + 0x14) -#define RTL8390MP_TC0CTL (RTL838X_TIMER0_BASE + 0x08) -#define RTL8390MP_TC1CTL (RTL838X_TIMER0_BASE + 0x18) -#define RTL8390MP_TCEN (1 << 28) -#define RTL8390MP_TCMODE_TIMER (1 << 24) -#define RTL8390MP_TCDIV_FACTOR (0xFFFF << 0) -#define RTL8390MP_TC0INT (RTL838X_TIMER0_BASE + 0xC) -#define RTL8390MP_TC1INT (RTL838X_TIMER0_BASE + 0x1C) -#define RTL8390MP_TCIE (1 << 20) -#define RTL8390MP_TCIP (1 << 16) -#define RTL8390MP_WDTCNR (RTL838X_TIMER0_BASE + 0x50) - -#define RTL8380MP_TC0DATA (RTL838X_TIMER0_BASE + 0x00) -#define RTL8380MP_TC1DATA (RTL838X_TIMER0_BASE + 0x10) -#define RTL8380MP_TC0CNT (RTL838X_TIMER0_BASE + 0x04) -#define RTL8380MP_TC1CNT (RTL838X_TIMER0_BASE + 0x14) -#define RTL8380MP_TC0CTL (RTL838X_TIMER0_BASE + 0x08) -#define RTL8380MP_TC1CTL (RTL838X_TIMER0_BASE + 0x18) -#define RTL8380MP_TCEN (1 << 28) -#define RTL8380MP_TCMODE_TIMER (1 << 24) -#define RTL8380MP_TCDIV_FACTOR (0xFFFF << 0) -#define RTL8380MP_TC0INT (RTL838X_TIMER0_BASE + 0xC) -#define RTL8380MP_TC1INT (RTL838X_TIMER0_BASE + 0x1C) -#define RTL8380MP_TCIE (1 << 20) -#define RTL8380MP_TCIP (1 << 16) -#define RTL8380MP_WDTCNR (RTL838X_TIMER0_BASE + 0x50) - -#define DIVISOR_RTL8390 55 -#define DIVISOR_RTL8380 2500 -#define DIVISOR_MAX 16834 /* * Memory Controller @@ -328,8 +216,14 @@ #define RTL838X_MODEL_NAME_INFO (0x00D4) #define RTL839X_MODEL_NAME_INFO (0x0FF0) +#define RTL93XX_MODEL_NAME_INFO (0x0004) + #define RTL838X_LED_GLB_CTRL (0xA000) #define RTL839X_LED_GLB_CTRL (0x00E4) +#define RTL9302_LED_GLB_CTRL (0xcc00) +#define RTL930X_LED_GLB_CTRL (0xC400) +#define RTL931X_LED_GLB_CTRL (0x0600) + #define RTL838X_EXT_GPIO_DIR (0xA08C) #define RTL839X_EXT_GPIO_DIR (0x0214) #define RTL838X_EXT_GPIO_DATA (0xA094) @@ -377,8 +271,11 @@ * Reset */ #define RGCR (0x1E70) -#define RTL839X_RST_GLB_CTRL (0x0014) +#define RTL838X_RST_GLB_CTRL_0 (0x003c) #define RTL838X_RST_GLB_CTRL_1 (0x0040) +#define RTL839X_RST_GLB_CTRL (0x0014) +#define RTL930X_RST_GLB_CTRL_0 (0x000c) +#define RTL931X_RST_GLB_CTRL (0x0400) /* LED control by switch */ #define RTL838X_LED_MODE_SEL (0x1004) @@ -419,6 +316,24 @@ #define RTL839X_PHYREG_PORT_CTRL(p) (0x03E4 + ((p >> 5) << 2)) #define RTL839X_PHYREG_DATA_CTRL (0x03F0) +#define RTL930X_SMI_GLB_CTRL (0xCA00) +#define RTL930X_SMI_POLL_CTRL (0xca90) +#define RTL930X_SMI_PORT0_15_POLLING_SEL (0xCA08) +#define RTL930X_SMI_PORT16_27_POLLING_SEL (0xCA0C) +#define RTL930X_SMI_PORT0_5_ADDR (0xCB80) +#define RTL930X_SMI_ACCESS_PHY_CTRL_0 (0xCB70) +#define RTL930X_SMI_ACCESS_PHY_CTRL_1 (0xCB74) +#define RTL930X_SMI_ACCESS_PHY_CTRL_2 (0xCB78) +#define RTL930X_SMI_ACCESS_PHY_CTRL_3 (0xCB7C) + +#define RTL931X_SMI_GLB_CTRL1 (0x0CBC) +#define RTL931X_SMI_GLB_CTRL0 (0x0CC0) +#define RTL931X_SMI_PORT_POLLING_CTRL (0x0CCC) +#define RTL931X_SMI_INDRT_ACCESS_CTRL_0 (0x0C00) +#define RTL931X_SMI_INDRT_ACCESS_CTRL_1 (0x0C04) +#define RTL931X_SMI_INDRT_ACCESS_CTRL_2 (0x0C08) +#define RTL931X_SMI_INDRT_ACCESS_CTRL_3 (0x0C10) + /* * Switch interrupts */ @@ -426,11 +341,22 @@ #define RTL838X_IMR_PORT_LINK_STS_CHG (0x1104) #define RTL838X_ISR_GLB_SRC (0x1148) #define RTL838X_ISR_PORT_LINK_STS_CHG (0x114C) + #define RTL839X_IMR_GLB (0x0064) #define RTL839X_IMR_PORT_LINK_STS_CHG (0x0068) #define RTL839X_ISR_GLB_SRC (0x009c) #define RTL839X_ISR_PORT_LINK_STS_CHG (0x00a0) +#define RTL930X_IMR_GLB (0xC628) +#define RTL930X_IMR_PORT_LINK_STS_CHG (0xC62C) +#define RTL930X_ISR_GLB (0xC658) +#define RTL930X_ISR_PORT_LINK_STS_CHG (0xC660) + +// IMR_GLB does not exit on RTL931X +#define RTL931X_IMR_PORT_LINK_STS_CHG (0x126C) +#define RTL931X_ISR_GLB_SRC (0x12B4) +#define RTL931X_ISR_PORT_LINK_STS_CHG (0x12B8) + /* Definition of family IDs */ #define RTL8389_FAMILY_ID (0x8389) #define RTL8328_FAMILY_ID (0x8328) @@ -438,6 +364,14 @@ #define RTL8350_FAMILY_ID (0x8350) #define RTL8380_FAMILY_ID (0x8380) #define RTL8330_FAMILY_ID (0x8330) +#define RTL9300_FAMILY_ID (0x9300) +#define RTL9310_FAMILY_ID (0x9310) + +/* Basic SoC Features */ +#define RTL838X_CPU_PORT 28 +#define RTL839X_CPU_PORT 52 +#define RTL930X_CPU_PORT 28 +#define RTL931X_CPU_PORT 56 struct rtl83xx_soc_info { unsigned char *name; @@ -446,6 +380,7 @@ struct rtl83xx_soc_info { unsigned char *compatible; volatile void *sw_base; volatile void *icu_base; + int cpu_port; }; /* rtl83xx-related functions used across subsystems */ @@ -454,5 +389,9 @@ int rtl838x_read_phy(u32 port, u32 page, u32 reg, u32 *val); int rtl838x_write_phy(u32 port, u32 page, u32 reg, u32 val); int rtl839x_read_phy(u32 port, u32 page, u32 reg, u32 *val); int rtl839x_write_phy(u32 port, u32 page, u32 reg, u32 val); +int rtl930x_read_phy(u32 port, u32 page, u32 reg, u32 *val); +int rtl930x_write_phy(u32 port, u32 page, u32 reg, u32 val); +int rtl931x_read_phy(u32 port, u32 page, u32 reg, u32 *val); +int rtl931x_write_phy(u32 port, u32 page, u32 reg, u32 val); #endif /* _MACH_RTL838X_H_ */ diff --git a/target/linux/realtek/files-5.4/arch/mips/rtl838x/irq.c b/target/linux/realtek/files-5.4/arch/mips/rtl838x/irq.c index 9057d48543..c0dd2f608c 100644 --- a/target/linux/realtek/files-5.4/arch/mips/rtl838x/irq.c +++ b/target/linux/realtek/files-5.4/arch/mips/rtl838x/irq.c @@ -85,7 +85,8 @@ static const struct irq_domain_ops irq_domain_ops = { static void rtl838x_irq_dispatch(struct irq_desc *desc) { - unsigned int pending = rtl83xx_r32(REG(RTL83XX_ICTL_GIMR)) & rtl83xx_r32(REG(RTL83XX_ICTL_GISR)); + unsigned int pending = rtl83xx_r32(REG(RTL83XX_ICTL_GIMR)) & + rtl83xx_r32(REG(RTL83XX_ICTL_GISR)); if (pending) { struct irq_domain *domain = irq_desc_get_handler_data(desc); @@ -95,7 +96,7 @@ static void rtl838x_irq_dispatch(struct irq_desc *desc) } } -asmlinkage void plat_irq_dispatch(void) +asmlinkage void plat_rtl83xx_irq_dispatch(void) { unsigned int pending; @@ -123,17 +124,27 @@ asmlinkage void plat_irq_dispatch(void) spurious_interrupt(); } -static void __init icu_of_init(struct device_node *node, struct device_node *parent) +static int icu_setup_domain(struct device_node *node) { struct irq_domain *domain; domain = irq_domain_add_simple(node, 32, 0, &irq_domain_ops, NULL); - irq_set_chained_handler_and_data(2, rtl838x_irq_dispatch, domain); - irq_set_chained_handler_and_data(5, rtl838x_irq_dispatch, domain); + irq_set_chained_handler_and_data(2, rtl838x_irq_dispatch, domain); + irq_set_chained_handler_and_data(3, rtl838x_irq_dispatch, domain); + irq_set_chained_handler_and_data(4, rtl838x_irq_dispatch, domain); + irq_set_chained_handler_and_data(5, rtl838x_irq_dispatch, domain); rtl83xx_ictl_base = of_iomap(node, 0); if (!rtl83xx_ictl_base) + return -EINVAL; + + return 0; +} + +static void __init rtl8380_icu_of_init(struct device_node *node, struct device_node *parent) +{ + if (icu_setup_domain(node)) return; /* Disable all cascaded interrupts */ @@ -155,9 +166,57 @@ static void __init icu_of_init(struct device_node *node, struct device_node *par rtl83xx_w32(BIT(RTL83XX_IRQ_TC0) | BIT(RTL83XX_IRQ_UART0), REG(RTL83XX_ICTL_GIMR)); } +static void __init rtl8390_icu_of_init(struct device_node *node, struct device_node *parent) +{ + if (icu_setup_domain(node)) + return; + + /* Disable all cascaded interrupts */ + rtl83xx_w32(0, REG(RTL83XX_ICTL_GIMR)); + + /* Set up interrupt routing */ + rtl83xx_w32(RTL83XX_IRR0_SETTING, REG(RTL83XX_IRR0)); + rtl83xx_w32(RTL8390_IRR1_SETTING, REG(RTL83XX_IRR1)); + rtl83xx_w32(RTL83XX_IRR2_SETTING, REG(RTL83XX_IRR2)); + rtl83xx_w32(RTL83XX_IRR3_SETTING, REG(RTL83XX_IRR3)); + + /* Clear timer interrupt */ + write_c0_compare(0); + + /* Enable all CPU interrupts */ + write_c0_status(read_c0_status() | ST0_IM); + + /* Enable timer0 and uart0 interrupts */ + rtl83xx_w32(BIT(RTL83XX_IRQ_TC0) | BIT(RTL83XX_IRQ_UART0), REG(RTL83XX_ICTL_GIMR)); +} + +static void __init rtl9300_icu_of_init(struct device_node *node, struct device_node *parent) +{ + pr_info("RTL9300: Setting up IRQs\n"); + if (icu_setup_domain(node)) + return; + + /* Disable all cascaded interrupts */ + rtl83xx_w32(0, REG(RTL83XX_ICTL_GIMR)); + + /* Set up interrupt routing */ + rtl83xx_w32(RTL9300_IRR0_SETTING, REG(RTL83XX_IRR0)); + rtl83xx_w32(RTL9300_IRR1_SETTING, REG(RTL83XX_IRR1)); + rtl83xx_w32(RTL9300_IRR2_SETTING, REG(RTL83XX_IRR2)); + rtl83xx_w32(RTL9300_IRR3_SETTING, REG(RTL83XX_IRR3)); + + /* Clear timer interrupt */ + write_c0_compare(0); + + /* Enable all CPU interrupts */ + write_c0_status(read_c0_status() | ST0_IM); +} + static struct of_device_id __initdata of_irq_ids[] = { { .compatible = "mti,cpu-interrupt-controller", .data = mips_cpu_irq_of_init }, - { .compatible = "realtek,rt8380-intc", .data = icu_of_init }, + { .compatible = "realtek,rt8380-intc", .data = rtl8380_icu_of_init }, + { .compatible = "realtek,rt8390-intc", .data = rtl8390_icu_of_init }, + { .compatible = "realtek,rt9300-intc", .data = rtl9300_icu_of_init }, {}, }; diff --git a/target/linux/realtek/files-5.4/arch/mips/rtl838x/prom.c b/target/linux/realtek/files-5.4/arch/mips/rtl838x/prom.c index 5278afae03..3390c04334 100644 --- a/target/linux/realtek/files-5.4/arch/mips/rtl838x/prom.c +++ b/target/linux/realtek/files-5.4/arch/mips/rtl838x/prom.c @@ -66,19 +66,64 @@ static void __init prom_init_cmdline(void) pr_info("Kernel command line: %s\n", arcs_cmdline); } +void __init identify_rtl9302(void) +{ + switch (sw_r32(RTL93XX_MODEL_NAME_INFO) & 0xfffffff0) { + case 0x93020810: + soc_info.name = "RTL9302A 12x2.5G"; + break; + case 0x93021010: + soc_info.name = "RTL9302B 8x2.5G"; + break; + case 0x93021810: + soc_info.name = "RTL9302C 16x2.5G"; + break; + case 0x93022010: + soc_info.name = "RTL9302D 24x2.5G"; + break; + case 0x93020800: + soc_info.name = "RTL9302A"; + break; + case 0x93021000: + soc_info.name = "RTL9302B"; + break; + case 0x93021800: + soc_info.name = "RTL9302C"; + break; + case 0x93022000: + soc_info.name = "RTL9302D"; + break; + case 0x93023001: + soc_info.name = "RTL9302F"; + break; + default: + soc_info.name = "RTL9302"; + } +} + void __init prom_init(void) { uint32_t model; /* uart0 */ - setup_8250_early_printk_port(0xb8002000, 2, 0); + setup_8250_early_printk_port(0xb8002000, 2, 0); - soc_info.sw_base = RTL838X_SW_BASE; + model = sw_r32(RTL838X_MODEL_NAME_INFO); + pr_info("RTL838X model is %x\n", model); + model = model >> 16 & 0xFFFF; - model = sw_r32(RTL838X_MODEL_NAME_INFO) >> 16; - if (model != 0x8328 && model != 0x8330 && model != 0x8332 && - model != 0x8380 && model != 0x8382) - model = sw_r32(RTL839X_MODEL_NAME_INFO) >> 16; + if ((model != 0x8328) && (model != 0x8330) && (model != 0x8332) + && (model != 0x8380) && (model != 0x8382)) { + model = sw_r32(RTL839X_MODEL_NAME_INFO); + pr_info("RTL839X model is %x\n", model); + model = model >> 16 & 0xFFFF; + } + + if ((model & 0x8390) != 0x8380 && (model & 0x8390) != 0x8390) { + model = sw_r32(RTL93XX_MODEL_NAME_INFO); + pr_info("RTL93XX model is %x\n", model); + model = model >> 16 & 0xFFFF; + } soc_info.id = model; @@ -115,10 +160,24 @@ void __init prom_init(void) soc_info.name = "RTL8393"; soc_info.family = RTL8390_FAMILY_ID; break; + case 0x9301: + soc_info.name = "RTL9301"; + soc_info.family = RTL9300_FAMILY_ID; + break; + case 0x9302: + identify_rtl9302(); + soc_info.family = RTL9300_FAMILY_ID; + break; + case 0x9313: + soc_info.name = "RTL9313"; + soc_info.family = RTL9310_FAMILY_ID; + break; default: soc_info.name = "DEFAULT"; soc_info.family = 0; } + pr_info("SoC Type: %s\n", get_system_type()); + prom_init_cmdline(); } diff --git a/target/linux/realtek/files-5.4/arch/mips/rtl838x/setup.c b/target/linux/realtek/files-5.4/arch/mips/rtl838x/setup.c index 24c70b8274..ef97d485e1 100644 --- a/target/linux/realtek/files-5.4/arch/mips/rtl838x/setup.c +++ b/target/linux/realtek/files-5.4/arch/mips/rtl838x/setup.c @@ -11,23 +11,21 @@ #include #include -#include #include #include +#include +#include #include #include - #include -#include #include -#include /* for mips_hpt_frequency */ +#include #include #include #include "mach-rtl83xx.h" -extern int rtl838x_serial_init(void); extern struct rtl83xx_soc_info soc_info; u32 pll_reset_value; @@ -35,30 +33,109 @@ u32 pll_reset_value; static void rtl838x_restart(char *command) { u32 pll = sw_r32(RTL838X_PLL_CML_CTRL); - /* SoC reset vector (in flash memory): on RTL839x platform preferred way to reset */ - void (*f)(void) = (void *) 0xbfc00000; pr_info("System restart.\n"); - if (soc_info.family == RTL8390_FAMILY_ID) { - f(); - /* If calling reset vector fails, reset entire chip */ - sw_w32(0xFFFFFFFF, RTL839X_RST_GLB_CTRL); - /* If this fails, halt the CPU */ - while - (1); - } - pr_info("PLL control register: %x, applying reset value %x\n", pll, pll_reset_value); + sw_w32(3, RTL838X_INT_RW_CTRL); sw_w32(pll_reset_value, RTL838X_PLL_CML_CTRL); sw_w32(0, RTL838X_INT_RW_CTRL); - pr_info("Resetting RTL838X SoC\n"); /* Reset Global Control1 Register */ sw_w32(1, RTL838X_RST_GLB_CTRL_1); } +static void rtl839x_restart(char *command) +{ + /* SoC reset vector (in flash memory): on RTL839x platform preferred way to reset */ + void (*f)(void) = (void *) 0xbfc00000; + + pr_info("System restart.\n"); + /* Reset SoC */ + sw_w32(0xFFFFFFFF, RTL839X_RST_GLB_CTRL); + /* and call reset vector */ + f(); + /* If this fails, halt the CPU */ + while + (1); +} + +static void rtl930x_restart(char *command) +{ + pr_info("System restart.\n"); + sw_w32(0x1, RTL930X_RST_GLB_CTRL_0); + while + (1); +} + +static void rtl931x_restart(char *command) +{ + u32 v; + + pr_info("System restart.\n"); + sw_w32(1, RTL931X_RST_GLB_CTRL); + v = sw_r32(RTL931X_RST_GLB_CTRL); + sw_w32(0x101, RTL931X_RST_GLB_CTRL); + msleep(15); + sw_w32(v, RTL931X_RST_GLB_CTRL); + msleep(15); + sw_w32(0x101, RTL931X_RST_GLB_CTRL); +} + +static void rtl838x_halt(void) +{ + pr_info("System halted.\n"); + while + (1); +} + +static void __init rtl838x_setup(void) +{ + pr_info("Registering _machine_restart\n"); + _machine_restart = rtl838x_restart; + _machine_halt = rtl838x_halt; + + /* This PLL value needs to be restored before a reset and will then be + * preserved over a SoC reset. A wrong value prevents the SoC from + * connecting to the SPI flash controller at boot and reading the + * reset routine */ + pll_reset_value = sw_r32(RTL838X_PLL_CML_CTRL); + + /* Setup System LED. Bit 15 then allows to toggle it */ + sw_w32_mask(0, 3 << 16, RTL838X_LED_GLB_CTRL); +} + +static void __init rtl839x_setup(void) +{ + pr_info("Registering _machine_restart\n"); + _machine_restart = rtl839x_restart; + _machine_halt = rtl838x_halt; + + /* Setup System LED. Bit 14 of RTL839X_LED_GLB_CTRL then allows to toggle it */ + sw_w32_mask(0, 3 << 15, RTL839X_LED_GLB_CTRL); +} + +static void __init rtl930x_setup(void) +{ + pr_info("Registering _machine_restart\n"); + _machine_restart = rtl930x_restart; + _machine_halt = rtl838x_halt; + + if (soc_info.id == 0x9302) + sw_w32_mask(0, 3 << 13, RTL9302_LED_GLB_CTRL); + else + sw_w32_mask(0, 3 << 13, RTL930X_LED_GLB_CTRL); +} + +static void __init rtl931x_setup(void) +{ + pr_info("Registering _machine_restart\n"); + _machine_restart = rtl931x_restart; + _machine_halt = rtl838x_halt; + sw_w32_mask(0, 3 << 12, RTL931X_LED_GLB_CTRL); +} + void __init plat_mem_setup(void) { void *dtb; @@ -78,6 +155,21 @@ void __init plat_mem_setup(void) * parsed resulting in our memory appearing */ __dt_setup_arch(dtb); + + switch (soc_info.family) { + case RTL8380_FAMILY_ID: + rtl838x_setup(); + break; + case RTL8390_FAMILY_ID: + rtl839x_setup(); + break; + case RTL9300_FAMILY_ID: + rtl930x_setup(); + break; + case RTL9310_FAMILY_ID: + rtl931x_setup(); + break; + } } void __init plat_time_init(void) @@ -85,6 +177,9 @@ void __init plat_time_init(void) struct device_node *np; u32 freq = 500000000; + of_clk_init(NULL); + timer_probe(); + np = of_find_node_by_name(NULL, "cpus"); if (!np) { pr_err("Missing 'cpus' DT node, using default frequency."); @@ -92,11 +187,9 @@ void __init plat_time_init(void) if (of_property_read_u32(np, "frequency", &freq) < 0) pr_err("No 'frequency' property in DT, using default."); else - pr_info("CPU frequency from device tree: %d", freq); + pr_info("CPU frequency from device tree: %dMHz", freq / 1000000); of_node_put(np); } mips_hpt_frequency = freq / 2; - - pll_reset_value = sw_r32(RTL838X_PLL_CML_CTRL); } diff --git a/target/linux/realtek/files-5.4/drivers/clocksource/timer-rtl9300.c b/target/linux/realtek/files-5.4/drivers/clocksource/timer-rtl9300.c new file mode 100644 index 0000000000..9ab1733fe3 --- /dev/null +++ b/target/linux/realtek/files-5.4/drivers/clocksource/timer-rtl9300.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include "timer-of.h" + +#include + +/* + * Timer registers + * the RTL9300/9310 SoCs have 6 timers, each register block 0x10 apart + */ +#define RTL9300_TC_DATA 0x0 +#define RTL9300_TC_CNT 0x4 +#define RTL9300_TC_CTRL 0x8 +#define RTL9300_TC_CTRL_MODE BIT(24) +#define RTL9300_TC_CTRL_EN BIT(28) +#define RTL9300_TC_INT 0xc +#define RTL9300_TC_INT_IP BIT(16) +#define RTL9300_TC_INT_IE BIT(20) + +// Clocksource is using timer 0, clock event uses timer 1 +#define TIMER_CLK_SRC 0 +#define TIMER_CLK_EVT 1 +#define TIMER_BLK_EVT (TIMER_CLK_EVT << 4) + +// Timer modes +#define TIMER_MODE_REPEAT 1 +#define TIMER_MODE_ONCE 0 + +// Minimum divider is 2 +#define DIVISOR_RTL9300 2 + +#define N_BITS 28 + +static void __iomem *rtl9300_sched_reg __read_mostly; + +static u64 notrace rtl9300_sched_clock_read(void) +{ +/* pr_info("In %s: %x\n", __func__, readl_relaxed(rtl9300_sched_reg)); + dump_stack();*/ + return readl_relaxed(rtl9300_sched_reg); +} + +static irqreturn_t rtl9300_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *clk = dev_id; + struct timer_of *to = to_timer_of(clk); + u32 v = readl(timer_of_base(to) + TIMER_BLK_EVT + RTL9300_TC_INT); + + // Acknowledge the IRQ + v |= RTL9300_TC_INT_IP; + writel(v, timer_of_base(to) + TIMER_BLK_EVT + RTL9300_TC_INT); + + clk->event_handler(clk); + return IRQ_HANDLED; +} + +static void rtl9300_timer_stop(struct timer_of *to) +{ + u32 v; + + writel(0, timer_of_base(to) + TIMER_BLK_EVT + RTL9300_TC_CTRL); + + // Acknowledge possibly pending IRQ + v = readl(timer_of_base(to) + TIMER_BLK_EVT + RTL9300_TC_INT); + if (v & RTL9300_TC_INT_IP) + writel(v, timer_of_base(to) + TIMER_BLK_EVT + RTL9300_TC_INT); +} + +static void rtl9300_timer_start(struct timer_of *to, int timer, bool periodic) +{ + u32 v = (periodic ? RTL9300_TC_CTRL_MODE : 0) | RTL9300_TC_CTRL_EN | DIVISOR_RTL9300; + writel(v, timer_of_base(to) + timer * 0x10 + RTL9300_TC_CTRL); +} + +static int rtl9300_set_next_event(unsigned long delta, struct clock_event_device *clk) +{ + struct timer_of *to = to_timer_of(clk); + + rtl9300_timer_stop(to); + writel(delta, timer_of_base(to) + TIMER_BLK_EVT + RTL9300_TC_DATA); + rtl9300_timer_start(to, TIMER_CLK_EVT, TIMER_MODE_ONCE); + return 0; +} + +static int rtl9300_set_state_periodic(struct clock_event_device *clk) +{ + struct timer_of *to = to_timer_of(clk); + + rtl9300_timer_stop(to); + writel(to->of_clk.period, timer_of_base(to) + TIMER_BLK_EVT + RTL9300_TC_DATA); + rtl9300_timer_start(to, TIMER_CLK_EVT, TIMER_MODE_REPEAT); + return 0; +} + +static int rtl9300_set_state_oneshot(struct clock_event_device *clk) +{ + struct timer_of *to = to_timer_of(clk); + + rtl9300_timer_stop(to); + writel(to->of_clk.period, timer_of_base(to) + TIMER_BLK_EVT + RTL9300_TC_DATA); + rtl9300_timer_start(to, TIMER_CLK_EVT, TIMER_MODE_ONCE); + return 0; +} + +static int rtl9300_set_state_shutdown(struct clock_event_device *clk) +{ + struct timer_of *to = to_timer_of(clk); + + rtl9300_timer_stop(to); + return 0; +} + +static struct timer_of t_of = { + .flags = TIMER_OF_BASE | TIMER_OF_IRQ | TIMER_OF_CLOCK, + + .clkevt = { + .name = "rtl9300_timer", + .rating = 350, + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_next_event = rtl9300_set_next_event, + .set_state_oneshot = rtl9300_set_state_oneshot, + .set_state_periodic = rtl9300_set_state_periodic, + .set_state_shutdown = rtl9300_set_state_shutdown, + }, + + .of_irq = { + .name = "ostimer", + .handler = rtl9300_timer_interrupt, + .flags = IRQF_TIMER, + }, +}; + +static void __init rtl9300_timer_setup(u8 timer) +{ + u32 v; + + // Disable timer + writel(0, timer_of_base(&t_of) + 0x10 * timer + RTL9300_TC_CTRL); + + // Acknowledge possibly pending IRQ + v = readl(timer_of_base(&t_of) + 0x10 * timer + RTL9300_TC_INT); + if (v & RTL9300_TC_INT_IP) + writel(v, timer_of_base(&t_of) + 0x10 * timer + RTL9300_TC_INT); + + // Setup maximum period (for use as clock-source) + writel(0x0fffffff, timer_of_base(&t_of) + 0x10 * timer + RTL9300_TC_DATA); +} + + +static int __init rtl9300_timer_init(struct device_node *node) +{ + int err = 0; + unsigned long rate; + + pr_info("%s: setting up timer\n", __func__); + + err = timer_of_init(node, &t_of); + if (err) + return err; + + rate = timer_of_rate(&t_of) / DIVISOR_RTL9300; + pr_info("Frequency in dts: %ld, my rate is %ld, period %ld\n", + timer_of_rate(&t_of), rate, timer_of_period(&t_of)); + pr_info("With base %08x IRQ: %d\n", (u32)timer_of_base(&t_of), timer_of_irq(&t_of)); + + // Configure clock source and register it for scheduling + rtl9300_timer_setup(TIMER_CLK_SRC); + rtl9300_timer_start(&t_of, TIMER_CLK_SRC, TIMER_MODE_REPEAT); + + rtl9300_sched_reg = timer_of_base(&t_of) + TIMER_CLK_SRC * 0x10 + RTL9300_TC_CNT; + + err = clocksource_mmio_init(rtl9300_sched_reg, node->name, rate , 100, N_BITS, + clocksource_mmio_readl_up); + if (err) + return err; + + sched_clock_register(rtl9300_sched_clock_read, N_BITS, rate); + + // Configure clock event source + rtl9300_timer_setup(TIMER_CLK_EVT); + clockevents_config_and_register(&t_of.clkevt, rate, 100, 0x0fffffff); + + // Enable interrupt + writel(RTL9300_TC_INT_IE, timer_of_base(&t_of) + TIMER_BLK_EVT + RTL9300_TC_INT); + + return err; +} + +TIMER_OF_DECLARE(rtl9300_timer, "realtek,rtl9300-timer", rtl9300_timer_init); diff --git a/target/linux/realtek/files-5.4/drivers/gpio/gpio-rtl8231.c b/target/linux/realtek/files-5.4/drivers/gpio/gpio-rtl8231.c index be5efc2997..031f60f530 100644 --- a/target/linux/realtek/files-5.4/drivers/gpio/gpio-rtl8231.c +++ b/target/linux/realtek/files-5.4/drivers/gpio/gpio-rtl8231.c @@ -12,6 +12,8 @@ #define RTL8231_GPIO_DIR(gpio) ((0x0005) + ((gpio) >> 4)) #define RTL8231_GPIO_DATA(gpio) ((0x001C) + ((gpio) >> 4)) +#define USEC_TIMEOUT 5000 + struct rtl8231_gpios { struct gpio_chip gc; struct device *dev; @@ -27,7 +29,7 @@ extern struct rtl83xx_soc_info soc_info; static u32 rtl8231_read(struct rtl8231_gpios *gpios, u32 reg) { - u32 t = 0; + u32 t = 0, n = 0; u8 bus_id = gpios->smi_bus_id; reg &= 0x1f; @@ -38,10 +40,18 @@ static u32 rtl8231_read(struct rtl8231_gpios *gpios, u32 reg) /* Set execution bit: cleared when operation completed */ t |= 1; + + // Start execution sw_w32(t, gpios->ext_gpio_indrt_access); - do { /* TODO: Return 0x80000000 if timeout */ + do { + udelay(1); t = sw_r32(gpios->ext_gpio_indrt_access); - } while (t & 1); + n++; + } while ((t & 1) && (n < USEC_TIMEOUT)); + + if (n >= USEC_TIMEOUT) + return 0x80000000; + pr_debug("%s: %x, %x, %x\n", __func__, bus_id, reg, (t & 0xffff0000) >> 16); return (t & 0xffff0000) >> 16; @@ -49,7 +59,7 @@ static u32 rtl8231_read(struct rtl8231_gpios *gpios, u32 reg) static int rtl8231_write(struct rtl8231_gpios *gpios, u32 reg, u32 data) { - u32 t = 0; + u32 t = 0, n = 0; u8 bus_id = gpios->smi_bus_id; pr_debug("%s: %x, %x, %x\n", __func__, bus_id, reg, data); @@ -62,10 +72,16 @@ static int rtl8231_write(struct rtl8231_gpios *gpios, u32 reg, u32 data) /* Set execution bit: cleared when operation completed */ t |= 1; + + // Start execution sw_w32(t, gpios->ext_gpio_indrt_access); - do { /* TODO: Return -1 if timeout */ + do { + udelay(1); t = sw_r32(gpios->ext_gpio_indrt_access); - } while (t & 1); + } while ((t & 1) && (n < USEC_TIMEOUT)); + + if (n >= USEC_TIMEOUT) + return -1; return 0; } @@ -94,7 +110,7 @@ static int rtl8231_pin_dir(struct rtl8231_gpios *gpios, u32 gpio, u32 dir) int dpin = pin; if (gpio > 31) { - pr_info("WARNING: HIGH pin\n"); + pr_debug("WARNING: HIGH pin\n"); dpin = pin << 5; pin_dir_addr = pin_sel_addr; } @@ -226,26 +242,21 @@ int rtl8231_init(struct rtl8231_gpios *gpios) { pr_info("%s called, MDIO bus ID: %d\n", __func__, gpios->smi_bus_id); + gpios->reg_cached = 0; + if (soc_info.family == RTL8390_FAMILY_ID) { + // RTL8390: Enable external gpio in global led control register sw_w32_mask(0x7 << 18, 0x4 << 18, RTL839X_LED_GLB_CTRL); - return 0; + } else if (soc_info.family == RTL8380_FAMILY_ID) { + // RTL8380: Enable RTL8231 indirect access mode + sw_w32_mask(0, 1, RTL838X_EXTRA_GPIO_CTRL); + sw_w32_mask(3, 1, RTL838X_DMY_REG5); } - /* Enable RTL8231 indirect access mode */ - sw_w32_mask(0, 1, RTL838X_EXTRA_GPIO_CTRL); - sw_w32_mask(3, 1, RTL838X_DMY_REG5); - - /* Enable RTL8231 via GPIO_A1 line - rtl838x_w32_mask(0, 1 << RTL838X_GPIO_A1, RTL838X_GPIO_PABC_DIR); - rtl838x_w32_mask(0, 1 << RTL838X_GPIO_A1, RTL838X_GPIO_PABC_DATA); */ - mdelay(50); /* wait 50ms for reset */ - /*Select GPIO functionality for pins 0-15, 16-31 and 32-37 */ rtl8231_write(gpios, RTL8231_GPIO_PIN_SEL(0), 0xffff); rtl8231_write(gpios, RTL8231_GPIO_PIN_SEL(16), 0xffff); - gpios->reg_cached = 0; - return 0; } @@ -262,7 +273,7 @@ static int rtl8231_gpio_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; struct rtl8231_gpios *gpios; int err; - u8 indirect_bus_id; + u32 indirect_bus_id; pr_info("Probing RTL8231 GPIOs\n"); @@ -284,10 +295,15 @@ static int rtl8231_gpio_probe(struct platform_device *pdev) gpios->ext_gpio_indrt_access = RTL839X_EXT_GPIO_INDRT_ACCESS; } - if (!of_property_read_u8(np, "indirect-access-bus-id", &indirect_bus_id)) { - gpios->smi_bus_id = indirect_bus_id; - rtl8231_init(gpios); - } + /* + * We use a default MDIO bus ID for the 8231 of 0, which can be overriden + * by the indirect-access-bus-id property in the dts. + */ + gpios->smi_bus_id = 0; + of_property_read_u32(np, "indirect-access-bus-id", &indirect_bus_id); + gpios->smi_bus_id = indirect_bus_id; + + rtl8231_init(gpios); gpios->dev = dev; gpios->gc.base = 160; diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/Makefile b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/Makefile index 52cc151a56..016184c3d9 100644 --- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/Makefile +++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_NET_DSA_RTL83XX) += common.o dsa.o \ - rtl838x.o rtl839x.o storm.o debugfs.o + rtl838x.o rtl839x.o rtl930x.o rtl931x.o debugfs.o qos.o diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/common.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/common.c index 1b57ddc92c..698f2892ea 100644 --- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/common.c +++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/common.c @@ -10,11 +10,14 @@ extern struct rtl83xx_soc_info soc_info; extern const struct rtl838x_reg rtl838x_reg; extern const struct rtl838x_reg rtl839x_reg; +extern const struct rtl838x_reg rtl930x_reg; +extern const struct rtl838x_reg rtl931x_reg; + extern const struct dsa_switch_ops rtl83xx_switch_ops; +extern const struct dsa_switch_ops rtl930x_switch_ops; DEFINE_MUTEX(smi_lock); - // TODO: unused static void dump_fdb(struct rtl838x_switch_priv *priv) { @@ -36,130 +39,237 @@ static void dump_fdb(struct rtl838x_switch_priv *priv) mutex_unlock(&priv->reg_mutex); } -// TODO: unused -static void rtl83xx_port_get_stp_state(struct rtl838x_switch_priv *priv, int port) +int rtl83xx_port_get_stp_state(struct rtl838x_switch_priv *priv, int port) { - u32 cmd, msti = 0; + u32 msti = 0; u32 port_state[4]; - int index, bit, i; + int index, bit; int pos = port; - int n = priv->family_id == RTL8380_FAMILY_ID ? 2 : 4; + int n = priv->port_width << 1; - /* CPU PORT can only be configured on RTL838x */ - if (port >= priv->cpu_port || port > 51) - return; + /* Ports above or equal CPU port can never be configured */ + if (port >= priv->cpu_port) + return -1; mutex_lock(&priv->reg_mutex); - /* For the RTL839x, the bits are left-aligned in the 128 bit field */ + /* For the RTL839x and following, the bits are left-aligned in the 64/128 bit field */ if (priv->family_id == RTL8390_FAMILY_ID) pos += 12; + if (priv->family_id == RTL9300_FAMILY_ID) + pos += 3; + if (priv->family_id == RTL9310_FAMILY_ID) + pos += 8; index = n - (pos >> 4) - 1; bit = (pos << 1) % 32; - if (priv->family_id == RTL8380_FAMILY_ID) { - cmd = BIT(15) /* Execute cmd */ - | BIT(14) /* Read */ - | 2 << 12 /* Table type 0b10 */ - | (msti & 0xfff); - } else { - cmd = BIT(16) /* Execute cmd */ - | 0 << 15 /* Read */ - | 5 << 12 /* Table type 0b101 */ - | (msti & 0xfff); - } - priv->r->exec_tbl0_cmd(cmd); - - for (i = 0; i < n; i++) - port_state[i] = sw_r32(priv->r->tbl_access_data_0(i)); + priv->r->stp_get(priv, msti, port_state); mutex_unlock(&priv->reg_mutex); + + return (port_state[index] >> bit) & 3; } -int rtl83xx_dsa_phy_read(struct dsa_switch *ds, int phy_addr, int phy_reg) -{ - u32 val; - u32 offset = 0; - struct rtl838x_switch_priv *priv = ds->priv; +static struct table_reg rtl838x_tbl_regs[] = { + TBL_DESC(0x6900, 0x6908, 3, 15, 13, 1), // RTL8380_TBL_L2 + TBL_DESC(0x6914, 0x6918, 18, 14, 12, 1), // RTL8380_TBL_0 + TBL_DESC(0xA4C8, 0xA4CC, 6, 14, 12, 1), // RTL8380_TBL_1 - if (phy_addr >= 24 && phy_addr <= 27 - && priv->ports[24].phy == PHY_RTL838X_SDS) { - if (phy_addr == 26) - offset = 0x100; - val = sw_r32(MAPLE_SDS4_FIB_REG0r + offset + (phy_reg << 2)) & 0xffff; - return val; + TBL_DESC(0x1180, 0x1184, 3, 16, 14, 0), // RTL8390_TBL_L2 + TBL_DESC(0x1190, 0x1194, 17, 15, 12, 0), // RTL8390_TBL_0 + TBL_DESC(0x6B80, 0x6B84, 4, 14, 12, 0), // RTL8390_TBL_1 + TBL_DESC(0x611C, 0x6120, 9, 8, 6, 0), // RTL8390_TBL_2 + + TBL_DESC(0xB320, 0xB334, 3, 18, 16, 0), // RTL9300_TBL_L2 + TBL_DESC(0xB340, 0xB344, 19, 16, 12, 0), // RTL9300_TBL_0 + TBL_DESC(0xB3A0, 0xB3A4, 20, 16, 13, 0), // RTL9300_TBL_1 + TBL_DESC(0xCE04, 0xCE08, 6, 14, 12, 0), // RTL9300_TBL_2 + TBL_DESC(0xD600, 0xD604, 30, 7, 6, 0), // RTL9300_TBL_HSB + TBL_DESC(0x7880, 0x7884, 22, 9, 8, 0), // RTL9300_TBL_HSA + + TBL_DESC(0x8500, 0x8508, 8, 19, 15, 0), // RTL9310_TBL_0 + TBL_DESC(0x40C0, 0x40C4, 22, 16, 14, 0), // RTL9310_TBL_1 + TBL_DESC(0x8528, 0x852C, 6, 18, 14, 0), // RTL9310_TBL_2 + TBL_DESC(0x0200, 0x0204, 9, 15, 12, 0), // RTL9310_TBL_3 + TBL_DESC(0x20dc, 0x20e0, 29, 7, 6, 0), // RTL9310_TBL_4 + TBL_DESC(0x7e1c, 0x7e20, 53, 8, 6, 0), // RTL9310_TBL_5 +}; + +void rtl_table_init(void) +{ + int i; + + for (i = 0; i < RTL_TBL_END; i++) + mutex_init(&rtl838x_tbl_regs[i].lock); +} + +/* + * Request access to table t in table access register r + * Returns a handle to a lock for that table + */ +struct table_reg *rtl_table_get(rtl838x_tbl_reg_t r, int t) +{ + if (r >= RTL_TBL_END) + return NULL; + + if (t >= BIT(rtl838x_tbl_regs[r].c_bit-rtl838x_tbl_regs[r].t_bit)) + return NULL; + + mutex_lock(&rtl838x_tbl_regs[r].lock); + rtl838x_tbl_regs[r].tbl = t; + + return &rtl838x_tbl_regs[r]; +} + +/* + * Release a table r, unlock the corresponding lock + */ +void rtl_table_release(struct table_reg *r) +{ + if (!r) + return; + +// pr_info("Unlocking %08x\n", (u32)r); + mutex_unlock(&r->lock); +// pr_info("Unlock done\n"); +} + +/* + * Reads table index idx into the data registers of the table + */ +void rtl_table_read(struct table_reg *r, int idx) +{ + u32 cmd = r->rmode ? BIT(r->c_bit) : 0; + + cmd |= BIT(r->c_bit + 1) | (r->tbl << r->t_bit) | (idx & (BIT(r->t_bit) - 1)); + sw_w32(cmd, r->addr); + pr_debug("Writing %08x to %x for read\n", cmd, r->addr); + do { } while (sw_r32(r->addr) & BIT(r->c_bit + 1)); +} + +/* + * Writes the content of the table data registers into the table at index idx + */ +void rtl_table_write(struct table_reg *r, int idx) +{ + u32 cmd = r->rmode ? 0 : BIT(r->c_bit); + + cmd |= BIT(r->c_bit + 1) | (r->tbl << r->t_bit) | (idx & (BIT(r->t_bit) - 1)); + pr_debug("Writing %08x to %x for write, value %08x\n", + cmd, r->addr, sw_r32(0xb344)); + sw_w32(cmd, r->addr); + do { } while (sw_r32(r->addr) & BIT(r->c_bit + 1)); +} + +/* + * Returns the address of the ith data register of table register r + * the address is relative to the beginning of the Switch-IO block at 0xbb000000 + */ +inline u16 rtl_table_data(struct table_reg *r, int i) +{ + if (i >= r->max_data) + i = r->max_data - 1; + return r->data + i * 4; +} + +inline u32 rtl_table_data_r(struct table_reg *r, int i) +{ + return sw_r32(rtl_table_data(r, i)); +} + +inline void rtl_table_data_w(struct table_reg *r, u32 v, int i) +{ + sw_w32(v, rtl_table_data(r, i)); +} + +/* Port register accessor functions for the RTL838x and RTL930X SoCs */ +void rtl838x_mask_port_reg(u64 clear, u64 set, int reg) +{ + sw_w32_mask((u32)clear, (u32)set, reg); +} + +void rtl838x_set_port_reg(u64 set, int reg) +{ + sw_w32((u32)set, reg); +} + +u64 rtl838x_get_port_reg(int reg) +{ + return ((u64) sw_r32(reg)); +} + +/* Port register accessor functions for the RTL839x and RTL931X SoCs */ +void rtl839x_mask_port_reg_be(u64 clear, u64 set, int reg) +{ + sw_w32_mask((u32)(clear >> 32), (u32)(set >> 32), reg); + sw_w32_mask((u32)(clear & 0xffffffff), (u32)(set & 0xffffffff), reg + 4); +} + +u64 rtl839x_get_port_reg_be(int reg) +{ + u64 v = sw_r32(reg); + + v <<= 32; + v |= sw_r32(reg + 4); + return v; +} + +void rtl839x_set_port_reg_be(u64 set, int reg) +{ + sw_w32(set >> 32, reg); + sw_w32(set & 0xffffffff, reg + 4); +} + +void rtl839x_mask_port_reg_le(u64 clear, u64 set, int reg) +{ + sw_w32_mask((u32)clear, (u32)set, reg); + sw_w32_mask((u32)(clear >> 32), (u32)(set >> 32), reg + 4); +} + +void rtl839x_set_port_reg_le(u64 set, int reg) +{ + sw_w32(set, reg); + sw_w32(set >> 32, reg + 4); +} + +u64 rtl839x_get_port_reg_le(int reg) +{ + u64 v = sw_r32(reg + 4); + + v <<= 32; + v |= sw_r32(reg); + return v; +} + +int read_phy(u32 port, u32 page, u32 reg, u32 *val) +{ + switch (soc_info.family) { + case RTL8380_FAMILY_ID: + return rtl838x_read_phy(port, page, reg, val); + case RTL8390_FAMILY_ID: + return rtl839x_read_phy(port, page, reg, val); + case RTL9300_FAMILY_ID: + return rtl930x_read_phy(port, page, reg, val); + case RTL9310_FAMILY_ID: + return rtl931x_read_phy(port, page, reg, val); } - - if (soc_info.family == RTL8390_FAMILY_ID) - rtl839x_read_phy(phy_addr, 0, phy_reg, &val); - else - rtl838x_read_phy(phy_addr, 0, phy_reg, &val); - return val; + return -1; } -int rtl83xx_dsa_phy_write(struct dsa_switch *ds, int phy_addr, int phy_reg, u16 val) +int write_phy(u32 port, u32 page, u32 reg, u32 val) { - u32 offset = 0; - struct rtl838x_switch_priv *priv = ds->priv; - - if (phy_addr >= 24 && phy_addr <= 27 - && priv->ports[24].phy == PHY_RTL838X_SDS) { - if (phy_addr == 26) - offset = 0x100; - sw_w32(val, MAPLE_SDS4_FIB_REG0r + offset + (phy_reg << 2)); - return 0; + switch (soc_info.family) { + case RTL8380_FAMILY_ID: + return rtl838x_write_phy(port, page, reg, val); + case RTL8390_FAMILY_ID: + return rtl839x_write_phy(port, page, reg, val); + case RTL9300_FAMILY_ID: + return rtl930x_write_phy(port, page, reg, val); + case RTL9310_FAMILY_ID: + return rtl931x_write_phy(port, page, reg, val); } - if (soc_info.family == RTL8390_FAMILY_ID) - return rtl839x_write_phy(phy_addr, 0, phy_reg, val); - else - return rtl838x_write_phy(phy_addr, 0, phy_reg, val); -} - -static int rtl83xx_mdio_read(struct mii_bus *bus, int addr, int regnum) -{ - int ret; - struct rtl838x_switch_priv *priv = bus->priv; - - ret = rtl83xx_dsa_phy_read(priv->ds, addr, regnum); - return ret; -} - -static int rtl83xx_mdio_write(struct mii_bus *bus, int addr, int regnum, - u16 val) -{ - struct rtl838x_switch_priv *priv = bus->priv; - - return rtl83xx_dsa_phy_write(priv->ds, addr, regnum, val); -} - -static void rtl8380_sds_rst(int mac) -{ - u32 offset = (mac == 24) ? 0 : 0x100; - - sw_w32_mask(1 << 11, 0, RTL8380_SDS4_FIB_REG0 + offset); - sw_w32_mask(0x3, 0, RTL838X_SDS4_REG28 + offset); - sw_w32_mask(0x3, 0x3, RTL838X_SDS4_REG28 + offset); - sw_w32_mask(0, 0x1 << 6, RTL838X_SDS4_DUMMY0 + offset); - sw_w32_mask(0x1 << 6, 0, RTL838X_SDS4_DUMMY0 + offset); - pr_debug("SERDES reset: %d\n", mac); -} - -static int __init rtl8380_sds_power(int mac, int val) -{ - u32 mode = (val == 1) ? 0x4 : 0x9; - u32 offset = (mac == 24) ? 5 : 0; - - if ((mac != 24) && (mac != 26)) { - pr_err("%s: not a fibre port: %d\n", __func__, mac); - return -1; - } - - sw_w32_mask(0x1f << offset, mode << offset, RTL838X_SDS_MODE_SEL); - - rtl8380_sds_rst(mac); - - return 0; + return -1; } static int __init rtl83xx_mdio_probe(struct rtl838x_switch_priv *priv) @@ -192,9 +302,15 @@ static int __init rtl83xx_mdio_probe(struct rtl838x_switch_priv *priv) return -ENOMEM; bus->name = "rtl838x slave mii"; - bus->read = &rtl83xx_mdio_read; - bus->write = &rtl83xx_mdio_write; + + /* + * Since the NIC driver is loaded first, we can use the mdio rw functions + * assigned there. + */ + bus->read = priv->mii_bus->read; + bus->write = priv->mii_bus->write; snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", bus->name, dev->id); + bus->parent = dev; priv->ds->slave_mii_bus = bus; priv->ds->slave_mii_bus->priv = priv; @@ -239,6 +355,14 @@ static int __init rtl83xx_mdio_probe(struct rtl838x_switch_priv *priv) } } + // TODO: Do this needs to come from the .dts, at least the SerDes number + if (priv->family_id == RTL9300_FAMILY_ID) { + priv->ports[24].is2G5 = true; + priv->ports[25].is2G5 = true; + priv->ports[24].sds_num = 1; + priv->ports[24].sds_num = 2; + } + /* Disable MAC polling the PHY so that we can start configuration */ priv->r->set_port_reg_le(0ULL, priv->r->smi_poll_ctrl); @@ -258,6 +382,15 @@ static int __init rtl83xx_mdio_probe(struct rtl838x_switch_priv *priv) rtl8380_sds_power(26, 1); } + // TODO: Only power on SerDes with external PHYs connected + if (priv->family_id == RTL9300_FAMILY_ID) { + pr_info("RTL9300 Powering on SerDes ports\n"); + rtl9300_sds_power(24, 1); + rtl9300_sds_power(25, 1); + rtl9300_sds_power(26, 1); + rtl9300_sds_power(27, 1); + } + pr_debug("%s done\n", __func__); return 0; } @@ -278,12 +411,152 @@ static int __init rtl83xx_get_l2aging(struct rtl838x_switch_priv *priv) return t; } +/* Caller must hold priv->reg_mutex */ +int rtl83xx_lag_add(struct dsa_switch *ds, int group, int port) +{ + struct rtl838x_switch_priv *priv = ds->priv; + int i; + + pr_info("%s: Adding port %d to LA-group %d\n", __func__, port, group); + if (group >= priv->n_lags) { + pr_err("Link Agrregation group too large.\n"); + return -EINVAL; + } + + if (port >= priv->cpu_port) { + pr_err("Invalid port number.\n"); + return -EINVAL; + } + + for (i = 0; i < priv->n_lags; i++) { + if (priv->lags_port_members[i] & BIT_ULL(i)) + break; + } + if (i != priv->n_lags) { + pr_err("%s: Port already member of LAG: %d\n", __func__, i); + return -ENOSPC; + } + + priv->r->mask_port_reg_be(0, BIT_ULL(port), priv->r->trk_mbr_ctr(group)); + priv->lags_port_members[group] |= BIT_ULL(port); + + pr_info("lags_port_members %d now %016llx\n", group, priv->lags_port_members[group]); + return 0; +} + +/* Caller must hold priv->reg_mutex */ +int rtl83xx_lag_del(struct dsa_switch *ds, int group, int port) +{ + struct rtl838x_switch_priv *priv = ds->priv; + + pr_info("%s: Removing port %d from LA-group %d\n", __func__, port, group); + + if (group >= priv->n_lags) { + pr_err("Link Agrregation group too large.\n"); + return -EINVAL; + } + + if (port >= priv->cpu_port) { + pr_err("Invalid port number.\n"); + return -EINVAL; + } + + + if (!(priv->lags_port_members[group] & BIT_ULL(port))) { + pr_err("%s: Port not member of LAG: %d\n", __func__, group + ); + return -ENOSPC; + } + + priv->r->mask_port_reg_be(BIT_ULL(port), 0, priv->r->trk_mbr_ctr(group)); + priv->lags_port_members[group] &= ~BIT_ULL(port); + + pr_info("lags_port_members %d now %016llx\n", group, priv->lags_port_members[group]); + return 0; +} + +static int rtl83xx_handle_changeupper(struct rtl838x_switch_priv *priv, + struct net_device *ndev, + struct netdev_notifier_changeupper_info *info) +{ + struct net_device *upper = info->upper_dev; + int i, j, err; + + if (!netif_is_lag_master(upper)) + return 0; + + mutex_lock(&priv->reg_mutex); + + for (i = 0; i < priv->n_lags; i++) { + if ((!priv->lag_devs[i]) || (priv->lag_devs[i] == upper)) + break; + } + for (j = 0; j < priv->cpu_port; j++) { + if (priv->ports[j].dp->slave == ndev) + break; + } + if (j >= priv->cpu_port) { + err = -EINVAL; + goto out; + } + + if (info->linking) { + if (!priv->lag_devs[i]) + priv->lag_devs[i] = upper; + err = rtl83xx_lag_add(priv->ds, i, priv->ports[j].dp->index); + if (err) { + err = -EINVAL; + goto out; + } + } else { + if (!priv->lag_devs[i]) + err = -EINVAL; + err = rtl83xx_lag_del(priv->ds, i, priv->ports[j].dp->index); + if (err) { + err = -EINVAL; + goto out; + } + if (!priv->lags_port_members[i]) + priv->lag_devs[i] = NULL; + } + +out: + mutex_unlock(&priv->reg_mutex); + return 0; +} + +static int rtl83xx_netdevice_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct net_device *ndev = netdev_notifier_info_to_dev(ptr); + struct rtl838x_switch_priv *priv; + int err; + + pr_debug("In: %s, event: %lu\n", __func__, event); + + if ((event != NETDEV_CHANGEUPPER) && (event != NETDEV_CHANGELOWERSTATE)) + return NOTIFY_DONE; + + priv = container_of(this, struct rtl838x_switch_priv, nb); + switch (event) { + case NETDEV_CHANGEUPPER: + err = rtl83xx_handle_changeupper(priv, ndev, ptr); + break; + } + + if (err) + return err; + + return NOTIFY_DONE; +} + static int __init rtl83xx_sw_probe(struct platform_device *pdev) { int err = 0, i; struct rtl838x_switch_priv *priv; struct device *dev = &pdev->dev; u64 irq_mask; + u64 bpdu_mask; pr_debug("Probing RTL838X switch device\n"); if (!pdev->dev.of_node) { @@ -291,6 +564,9 @@ static int __init rtl83xx_sw_probe(struct platform_device *pdev) return -EINVAL; } + // Initialize access to RTL switch tables + rtl_table_init(); + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -306,20 +582,56 @@ static int __init rtl83xx_sw_probe(struct platform_device *pdev) priv->family_id = soc_info.family; priv->id = soc_info.id; - if (soc_info.family == RTL8380_FAMILY_ID) { + switch(soc_info.family) { + case RTL8380_FAMILY_ID: + priv->ds->ops = &rtl83xx_switch_ops; priv->cpu_port = RTL838X_CPU_PORT; priv->port_mask = 0x1f; + priv->port_width = 1; + priv->irq_mask = 0x0FFFFFFF; priv->r = &rtl838x_reg; - priv->ds->num_ports = 30; + priv->ds->num_ports = 29; priv->fib_entries = 8192; rtl8380_get_version(priv); - } else { + priv->n_lags = 8; + break; + case RTL8390_FAMILY_ID: + priv->ds->ops = &rtl83xx_switch_ops; priv->cpu_port = RTL839X_CPU_PORT; priv->port_mask = 0x3f; + priv->port_width = 2; + priv->irq_mask = 0xFFFFFFFFFFFFFULL; priv->r = &rtl839x_reg; priv->ds->num_ports = 53; priv->fib_entries = 16384; rtl8390_get_version(priv); + priv->n_lags = 16; + break; + case RTL9300_FAMILY_ID: + priv->ds->ops = &rtl930x_switch_ops; + priv->cpu_port = RTL930X_CPU_PORT; + priv->port_mask = 0x1f; + priv->port_width = 1; + priv->irq_mask = 0x0FFFFFFF; + priv->r = &rtl930x_reg; + priv->ds->num_ports = 29; + priv->fib_entries = 16384; + priv->version = RTL8390_VERSION_A; + priv->n_lags = 16; + sw_w32(1, RTL930X_ST_CTRL); + break; + case RTL9310_FAMILY_ID: + priv->ds->ops = &rtl930x_switch_ops; + priv->cpu_port = RTL931X_CPU_PORT; + priv->port_mask = 0x3f; + priv->port_width = 2; + priv->irq_mask = 0xFFFFFFFFFFFFFULL; + priv->r = &rtl931x_reg; + priv->ds->num_ports = 57; + priv->fib_entries = 16384; + priv->version = RTL8390_VERSION_A; + priv->n_lags = 16; + break; } pr_debug("Chip version %c\n", priv->version); @@ -338,39 +650,63 @@ static int __init rtl83xx_sw_probe(struct platform_device *pdev) /* Enable link and media change interrupts. Are the SERDES masks needed? */ sw_w32_mask(0, 3, priv->r->isr_glb_src); - /* ... for all ports */ - irq_mask = soc_info.family == RTL8380_FAMILY_ID ? 0x0FFFFFFF : 0xFFFFFFFFFFFFFULL; + priv->r->set_port_reg_le(irq_mask, priv->r->isr_port_link_sts_chg); priv->r->set_port_reg_le(irq_mask, priv->r->imr_port_link_sts_chg); - priv->link_state_irq = platform_get_irq(pdev, 0);; - if (priv->family_id == RTL8380_FAMILY_ID) { + priv->link_state_irq = platform_get_irq(pdev, 0); + pr_info("LINK state irq: %d\n", priv->link_state_irq); + switch (priv->family_id) { + case RTL8380_FAMILY_ID: err = request_irq(priv->link_state_irq, rtl838x_switch_irq, IRQF_SHARED, "rtl838x-link-state", priv->ds); - } else { + break; + case RTL8390_FAMILY_ID: err = request_irq(priv->link_state_irq, rtl839x_switch_irq, IRQF_SHARED, "rtl839x-link-state", priv->ds); + break; + case RTL9300_FAMILY_ID: + err = request_irq(priv->link_state_irq, rtl930x_switch_irq, + IRQF_SHARED, "rtl930x-link-state", priv->ds); + break; + case RTL9310_FAMILY_ID: + err = request_irq(priv->link_state_irq, rtl931x_switch_irq, + IRQF_SHARED, "rtl931x-link-state", priv->ds); + break; } if (err) { dev_err(dev, "Error setting up switch interrupt.\n"); /* Need to free allocated switch here */ } - /* Enable interrupts for switch */ - sw_w32(0x1, priv->r->imr_glb); + /* Enable interrupts for switch, on RTL931x, the IRQ is always on globally */ + if (soc_info.family != RTL9310_FAMILY_ID) + sw_w32(0x1, priv->r->imr_glb); rtl83xx_get_l2aging(priv); -/* - if (priv->family_id == RTL8380_FAMILY_ID) - rtl83xx_storm_control_init(priv); -*/ + rtl83xx_setup_qos(priv); /* Clear all destination ports for mirror groups */ for (i = 0; i < 4; i++) priv->mirror_group_ports[i] = -1; - rtl838x_dbgfs_init(priv); + priv->nb.notifier_call = rtl83xx_netdevice_event; + if (register_netdevice_notifier(&priv->nb)) { + priv->nb.notifier_call = NULL; + dev_err(dev, "Failed to register LAG netdev notifier\n"); + } + + // Flood BPDUs to all ports including cpu-port + if (soc_info.family != RTL9300_FAMILY_ID) { // TODO: Port this functionality + bpdu_mask = soc_info.family == RTL8380_FAMILY_ID ? 0x1FFFFFFF : 0x1FFFFFFFFFFFFF; + priv->r->set_port_reg_be(bpdu_mask, priv->r->rma_bpdu_fld_pmask); + + // TRAP 802.1X frames (EAPOL) to the CPU-Port, bypass STP and VLANs + sw_w32(7, priv->r->spcl_trap_eapol_ctrl); + + rtl838x_dbgfs_init(priv); + } return err; } diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/debugfs.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/debugfs.c index af24e8fa42..4f81408453 100644 --- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/debugfs.c +++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/debugfs.c @@ -1,12 +1,219 @@ // SPDX-License-Identifier: GPL-2.0-only #include +#include #include -#include "rtl838x.h" +#include "rtl83xx.h" #define RTL838X_DRIVER_NAME "rtl838x" +#define RTL8380_LED_GLB_CTRL (0xA000) +#define RTL8380_LED_MODE_SEL (0x1004) +#define RTL8380_LED_MODE_CTRL (0xA004) +#define RTL8380_LED_P_EN_CTRL (0xA008) +#define RTL8380_LED_SW_CTRL (0xA00C) +#define RTL8380_LED0_SW_P_EN_CTRL (0xA010) +#define RTL8380_LED1_SW_P_EN_CTRL (0xA014) +#define RTL8380_LED2_SW_P_EN_CTRL (0xA018) +#define RTL8380_LED_SW_P_CTRL(p) (0xA01C + (((p) << 2))) + +#define RTL8390_LED_GLB_CTRL (0x00E4) +#define RTL8390_LED_SET_2_3_CTRL (0x00E8) +#define RTL8390_LED_SET_0_1_CTRL (0x00EC) +#define RTL8390_LED_COPR_SET_SEL_CTRL(p) (0x00F0 + (((p >> 4) << 2))) +#define RTL8390_LED_FIB_SET_SEL_CTRL(p) (0x0100 + (((p >> 4) << 2))) +#define RTL8390_LED_COPR_PMASK_CTRL(p) (0x0110 + (((p >> 5) << 2))) +#define RTL8390_LED_FIB_PMASK_CTRL(p) (0x00118 + (((p >> 5) << 2))) +#define RTL8390_LED_COMBO_CTRL(p) (0x0120 + (((p >> 5) << 2))) +#define RTL8390_LED_SW_CTRL (0x0128) +#define RTL8390_LED_SW_P_EN_CTRL(p) (0x012C + (((p / 10) << 2))) +#define RTL8390_LED_SW_P_CTRL(p) (0x0144 + (((p) << 2))) + +#define RTL838X_MIR_QID_CTRL(grp) (0xAD44 + (((grp) << 2))) +#define RTL838X_MIR_RSPAN_VLAN_CTRL(grp) (0xA340 + (((grp) << 2))) +#define RTL838X_MIR_RSPAN_VLAN_CTRL_MAC(grp) (0xAA70 + (((grp) << 2))) +#define RTL838X_MIR_RSPAN_TX_CTRL (0xA350) +#define RTL838X_MIR_RSPAN_TX_TAG_RM_CTRL (0xAA80) +#define RTL838X_MIR_RSPAN_TX_TAG_EN_CTRL (0xAA84) +#define RTL839X_MIR_RSPAN_VLAN_CTRL(grp) (0xA340 + (((grp) << 2))) +#define RTL839X_MIR_RSPAN_TX_CTRL (0x69b0) +#define RTL839X_MIR_RSPAN_TX_TAG_RM_CTRL (0x2550) +#define RTL839X_MIR_RSPAN_TX_TAG_EN_CTRL (0x2554) +#define RTL839X_MIR_SAMPLE_RATE_CTRL (0x2558) + +int rtl83xx_port_get_stp_state(struct rtl838x_switch_priv *priv, int port); +void rtl83xx_port_stp_state_set(struct dsa_switch *ds, int port, u8 state); +void rtl83xx_fast_age(struct dsa_switch *ds, int port); +u32 rtl838x_get_egress_rate(struct rtl838x_switch_priv *priv, int port); +u32 rtl839x_get_egress_rate(struct rtl838x_switch_priv *priv, int port); +int rtl838x_set_egress_rate(struct rtl838x_switch_priv *priv, int port, u32 rate); +int rtl839x_set_egress_rate(struct rtl838x_switch_priv *priv, int port, u32 rate); + +static ssize_t rtl838x_common_read(char __user *buffer, size_t count, + loff_t *ppos, unsigned int value) +{ + char *buf; + ssize_t len; + + if (*ppos != 0) + return 0; + + buf = kasprintf(GFP_KERNEL, "0x%08x\n", value); + if (!buf) + return -ENOMEM; + + if (count < strlen(buf)) { + kfree(buf); + return -ENOSPC; + } + + len = simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf)); + kfree(buf); + + return len; +} + +static ssize_t rtl838x_common_write(const char __user *buffer, size_t count, + loff_t *ppos, unsigned int *value) +{ + char b[32]; + ssize_t len; + int ret; + + if (*ppos != 0) + return -EINVAL; + + if (count >= sizeof(b)) + return -ENOSPC; + + len = simple_write_to_buffer(b, sizeof(b) - 1, ppos, + buffer, count); + if (len < 0) + return len; + + b[len] = '\0'; + ret = kstrtouint(b, 16, value); + if (ret) + return -EIO; + + return len; +} + +static ssize_t stp_state_read(struct file *filp, char __user *buffer, size_t count, + loff_t *ppos) +{ + struct rtl838x_port *p = filp->private_data; + struct dsa_switch *ds = p->dp->ds; + int value = rtl83xx_port_get_stp_state(ds->priv, p->dp->index); + + if (value < 0) + return -EINVAL; + + return rtl838x_common_read(buffer, count, ppos, (u32)value); +} + +static ssize_t stp_state_write(struct file *filp, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct rtl838x_port *p = filp->private_data; + u32 value; + size_t res = rtl838x_common_write(buffer, count, ppos, &value); + if (res < 0) + return res; + + rtl83xx_port_stp_state_set(p->dp->ds, p->dp->index, (u8)value); + + return res; +} + +static const struct file_operations stp_state_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = stp_state_read, + .write = stp_state_write, +}; + +static ssize_t age_out_read(struct file *filp, char __user *buffer, size_t count, + loff_t *ppos) +{ + struct rtl838x_port *p = filp->private_data; + struct dsa_switch *ds = p->dp->ds; + struct rtl838x_switch_priv *priv = ds->priv; + int value = sw_r32(priv->r->l2_port_aging_out); + + if (value < 0) + return -EINVAL; + + return rtl838x_common_read(buffer, count, ppos, (u32)value); +} + +static ssize_t age_out_write(struct file *filp, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct rtl838x_port *p = filp->private_data; + u32 value; + size_t res = rtl838x_common_write(buffer, count, ppos, &value); + if (res < 0) + return res; + + rtl83xx_fast_age(p->dp->ds, p->dp->index); + + return res; +} + +static const struct file_operations age_out_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = age_out_read, + .write = age_out_write, +}; + +static ssize_t port_egress_rate_read(struct file *filp, char __user *buffer, size_t count, + loff_t *ppos) +{ + struct rtl838x_port *p = filp->private_data; + struct dsa_switch *ds = p->dp->ds; + struct rtl838x_switch_priv *priv = ds->priv; + int value; + if (priv->family_id == RTL8380_FAMILY_ID) + value = rtl838x_get_egress_rate(priv, p->dp->index); + else + value = rtl839x_get_egress_rate(priv, p->dp->index); + + if (value < 0) + return -EINVAL; + + return rtl838x_common_read(buffer, count, ppos, (u32)value); +} + +static ssize_t port_egress_rate_write(struct file *filp, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct rtl838x_port *p = filp->private_data; + struct dsa_switch *ds = p->dp->ds; + struct rtl838x_switch_priv *priv = ds->priv; + u32 value; + size_t res = rtl838x_common_write(buffer, count, ppos, &value); + if (res < 0) + return res; + + if (priv->family_id == RTL8380_FAMILY_ID) + rtl838x_set_egress_rate(priv, p->dp->index, value); + else + rtl839x_set_egress_rate(priv, p->dp->index, value); + + return res; +} + +static const struct file_operations port_egress_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = port_egress_rate_read, + .write = port_egress_rate_write, +}; + + static const struct debugfs_reg32 port_ctrl_regs[] = { { .name = "port_isolation", .offset = RTL838X_PORT_ISO_CTRL(0), }, { .name = "mac_force_mode", .offset = RTL838X_MAC_FORCE_MODE_CTRL, }, @@ -27,20 +234,35 @@ static int rtl838x_dbgfs_port_init(struct dentry *parent, struct rtl838x_switch_ port_dir = debugfs_create_dir(priv->ports[port].dp->name, parent); - debugfs_create_x32("rate_uc", 0644, port_dir, - (u32 *)(RTL838X_SW_BASE + RTL838X_STORM_CTRL_PORT_UC(port))); + if (priv->family_id == RTL8380_FAMILY_ID) { + debugfs_create_x32("storm_rate_uc", 0644, port_dir, + (u32 *)(RTL838X_SW_BASE + RTL838X_STORM_CTRL_PORT_UC(port))); - debugfs_create_x32("rate_mc", 0644, port_dir, - (u32 *)(RTL838X_SW_BASE + RTL838X_STORM_CTRL_PORT_BC(port))); + debugfs_create_x32("storm_rate_mc", 0644, port_dir, + (u32 *)(RTL838X_SW_BASE + RTL838X_STORM_CTRL_PORT_MC(port))); - debugfs_create_x32("rate_bc", 0644, port_dir, - (u32 *)(RTL838X_SW_BASE + RTL838X_STORM_CTRL_PORT_BC(port))); + debugfs_create_x32("storm_rate_bc", 0644, port_dir, + (u32 *)(RTL838X_SW_BASE + RTL838X_STORM_CTRL_PORT_BC(port))); - debugfs_create_u32("id", 0444, port_dir, &priv->ports[port].dp->index); + debugfs_create_x32("vlan_port_tag_sts_ctrl", 0644, port_dir, + (u32 *)(RTL838X_SW_BASE + RTL838X_VLAN_PORT_TAG_STS_CTRL + + (port << 2))); + } else { + debugfs_create_x32("storm_rate_uc", 0644, port_dir, + (u32 *)(RTL838X_SW_BASE + RTL839X_STORM_CTRL_PORT_UC_0(port))); + debugfs_create_x32("storm_rate_mc", 0644, port_dir, + (u32 *)(RTL838X_SW_BASE + RTL839X_STORM_CTRL_PORT_MC_0(port))); - debugfs_create_x32("vlan_port_tag_sts_ctrl", 0644, port_dir, - (u32 *)(RTL838X_SW_BASE + RTL838X_VLAN_PORT_TAG_STS_CTRL(port))); + debugfs_create_x32("storm_rate_bc", 0644, port_dir, + (u32 *)(RTL838X_SW_BASE + RTL839X_STORM_CTRL_PORT_BC_0(port))); + + debugfs_create_x32("vlan_port_tag_sts_ctrl", 0644, port_dir, + (u32 *)(RTL838X_SW_BASE + RTL839X_VLAN_PORT_TAG_STS_CTRL + + (port << 2))); + } + + debugfs_create_u32("id", 0444, port_dir, (u32 *)&priv->ports[port].dp->index); port_ctrl_regset = devm_kzalloc(priv->dev, sizeof(*port_ctrl_regset), GFP_KERNEL); if (!port_ctrl_regset) @@ -48,9 +270,88 @@ static int rtl838x_dbgfs_port_init(struct dentry *parent, struct rtl838x_switch_ port_ctrl_regset->regs = port_ctrl_regs; port_ctrl_regset->nregs = ARRAY_SIZE(port_ctrl_regs); - port_ctrl_regset->base = RTL838X_SW_BASE + (port << 2); + port_ctrl_regset->base = (void *)(RTL838X_SW_BASE + (port << 2)); debugfs_create_regset32("port_ctrl", 0400, port_dir, port_ctrl_regset); + debugfs_create_file("stp_state", 0600, port_dir, &priv->ports[port], &stp_state_fops); + debugfs_create_file("age_out", 0600, port_dir, &priv->ports[port], &age_out_fops); + debugfs_create_file("port_egress_rate", 0600, port_dir, &priv->ports[port], + &port_egress_fops); + return 0; +} + +static int rtl838x_dbgfs_leds(struct dentry *parent, struct rtl838x_switch_priv *priv) +{ + struct dentry *led_dir; + int p; + char led_sw_p_ctrl_name[20]; + char port_led_name[20]; + + led_dir = debugfs_create_dir("led", parent); + + if (priv->family_id == RTL8380_FAMILY_ID) { + debugfs_create_x32("led_glb_ctrl", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8380_LED_GLB_CTRL)); + debugfs_create_x32("led_mode_sel", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8380_LED_MODE_SEL)); + debugfs_create_x32("led_mode_ctrl", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8380_LED_MODE_CTRL)); + debugfs_create_x32("led_p_en_ctrl", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8380_LED_P_EN_CTRL)); + debugfs_create_x32("led_sw_ctrl", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8380_LED_SW_CTRL)); + debugfs_create_x32("led0_sw_p_en_ctrl", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8380_LED0_SW_P_EN_CTRL)); + debugfs_create_x32("led1_sw_p_en_ctrl", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8380_LED1_SW_P_EN_CTRL)); + debugfs_create_x32("led2_sw_p_en_ctrl", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8380_LED2_SW_P_EN_CTRL)); + for (p = 0; p < 28; p++) { + snprintf(led_sw_p_ctrl_name, sizeof(led_sw_p_ctrl_name), + "led_sw_p_ctrl.%02d", p); + debugfs_create_x32(led_sw_p_ctrl_name, 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8380_LED_SW_P_CTRL(p))); + } + } else if (priv->family_id == RTL8390_FAMILY_ID) { + debugfs_create_x32("led_glb_ctrl", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8390_LED_GLB_CTRL)); + debugfs_create_x32("led_set_2_3", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8390_LED_SET_2_3_CTRL)); + debugfs_create_x32("led_set_0_1", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8390_LED_SET_0_1_CTRL)); + for (p = 0; p < 4; p++) { + snprintf(port_led_name, sizeof(port_led_name), "led_copr_set_sel.%1d", p); + debugfs_create_x32(port_led_name, 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8390_LED_COPR_SET_SEL_CTRL(p << 4))); + snprintf(port_led_name, sizeof(port_led_name), "led_fib_set_sel.%1d", p); + debugfs_create_x32(port_led_name, 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8390_LED_FIB_SET_SEL_CTRL(p << 4))); + } + debugfs_create_x32("led_copr_pmask_ctrl_0", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8390_LED_COPR_PMASK_CTRL(0))); + debugfs_create_x32("led_copr_pmask_ctrl_1", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8390_LED_COPR_PMASK_CTRL(32))); + debugfs_create_x32("led_fib_pmask_ctrl_0", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8390_LED_FIB_PMASK_CTRL(0))); + debugfs_create_x32("led_fib_pmask_ctrl_1", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8390_LED_FIB_PMASK_CTRL(32))); + debugfs_create_x32("led_combo_ctrl_0", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8390_LED_COMBO_CTRL(0))); + debugfs_create_x32("led_combo_ctrl_1", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8390_LED_COMBO_CTRL(32))); + debugfs_create_x32("led_sw_ctrl", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8390_LED_SW_CTRL)); + for (p = 0; p < 5; p++) { + snprintf(port_led_name, sizeof(port_led_name), "led_sw_p_en_ctrl.%1d", p); + debugfs_create_x32(port_led_name, 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8390_LED_SW_P_EN_CTRL(p * 10))); + } + for (p = 0; p < 28; p++) { + snprintf(port_led_name, sizeof(port_led_name), "led_sw_p_ctrl.%02d", p); + debugfs_create_x32(port_led_name, 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8390_LED_SW_P_CTRL(p))); + } + } return 0; } @@ -58,9 +359,13 @@ void rtl838x_dbgfs_init(struct rtl838x_switch_priv *priv) { struct dentry *rtl838x_dir; struct dentry *port_dir; + struct dentry *mirror_dir; struct debugfs_regset32 *port_ctrl_regset; int ret, i; + char lag_name[10]; + char mirror_name[10]; + pr_info("%s called\n", __func__); rtl838x_dir = debugfs_lookup(RTL838X_DRIVER_NAME, NULL); if (!rtl838x_dir) rtl838x_dir = debugfs_create_dir(RTL838X_DRIVER_NAME, NULL); @@ -73,7 +378,6 @@ void rtl838x_dbgfs_init(struct rtl838x_switch_priv *priv) /* Create one directory per port */ for (i = 0; i < priv->cpu_port; i++) { if (priv->ports[i].phy) { - pr_debug("debugfs, port %d\n", i); ret = rtl838x_dbgfs_port_init(rtl838x_dir, priv, i); if (ret) goto err; @@ -81,7 +385,8 @@ void rtl838x_dbgfs_init(struct rtl838x_switch_priv *priv) } /* Create directory for CPU-port */ - port_dir = debugfs_create_dir("cpu_port", rtl838x_dir); port_ctrl_regset = devm_kzalloc(priv->dev, sizeof(*port_ctrl_regset), GFP_KERNEL); + port_dir = debugfs_create_dir("cpu_port", rtl838x_dir); + port_ctrl_regset = devm_kzalloc(priv->dev, sizeof(*port_ctrl_regset), GFP_KERNEL); if (!port_ctrl_regset) { ret = -ENOMEM; goto err; @@ -89,10 +394,82 @@ void rtl838x_dbgfs_init(struct rtl838x_switch_priv *priv) port_ctrl_regset->regs = port_ctrl_regs; port_ctrl_regset->nregs = ARRAY_SIZE(port_ctrl_regs); - port_ctrl_regset->base = RTL838X_SW_BASE + (priv->cpu_port << 2); + port_ctrl_regset->base = (void *)(RTL838X_SW_BASE + (priv->cpu_port << 2)); debugfs_create_regset32("port_ctrl", 0400, port_dir, port_ctrl_regset); debugfs_create_u8("id", 0444, port_dir, &priv->cpu_port); + /* Create entries for LAGs */ + for (i = 0; i < priv->n_lags; i++) { + snprintf(lag_name, sizeof(lag_name), "lag.%02d", i); + if (priv->family_id == RTL8380_FAMILY_ID) + debugfs_create_x32(lag_name, 0644, rtl838x_dir, + (u32 *)(RTL838X_SW_BASE + priv->r->trk_mbr_ctr(i))); + else + debugfs_create_x64(lag_name, 0644, rtl838x_dir, + (u64 *)(RTL838X_SW_BASE + priv->r->trk_mbr_ctr(i))); + } + + /* Create directories for mirror groups */ + for (i = 0; i < 4; i++) { + snprintf(mirror_name, sizeof(mirror_name), "mirror.%1d", i); + mirror_dir = debugfs_create_dir(mirror_name, rtl838x_dir); + if (priv->family_id == RTL8380_FAMILY_ID) { + debugfs_create_x32("ctrl", 0644, mirror_dir, + (u32 *)(RTL838X_SW_BASE + RTL838X_MIR_CTRL + i * 4)); + debugfs_create_x32("ingress_pm", 0644, mirror_dir, + (u32 *)(RTL838X_SW_BASE + priv->r->mir_spm + i * 4)); + debugfs_create_x32("egress_pm", 0644, mirror_dir, + (u32 *)(RTL838X_SW_BASE + priv->r->mir_dpm + i * 4)); + debugfs_create_x32("qid", 0644, mirror_dir, + (u32 *)(RTL838X_SW_BASE + RTL838X_MIR_QID_CTRL(i))); + debugfs_create_x32("rspan_vlan", 0644, mirror_dir, + (u32 *)(RTL838X_SW_BASE + RTL838X_MIR_RSPAN_VLAN_CTRL(i))); + debugfs_create_x32("rspan_vlan_mac", 0644, mirror_dir, + (u32 *)(RTL838X_SW_BASE + RTL838X_MIR_RSPAN_VLAN_CTRL_MAC(i))); + debugfs_create_x32("rspan_tx", 0644, mirror_dir, + (u32 *)(RTL838X_SW_BASE + RTL838X_MIR_RSPAN_TX_CTRL)); + debugfs_create_x32("rspan_tx_tag_rm", 0644, mirror_dir, + (u32 *)(RTL838X_SW_BASE + RTL838X_MIR_RSPAN_TX_TAG_RM_CTRL)); + debugfs_create_x32("rspan_tx_tag_en", 0644, mirror_dir, + (u32 *)(RTL838X_SW_BASE + RTL838X_MIR_RSPAN_TX_TAG_EN_CTRL)); + } else { + debugfs_create_x32("ctrl", 0644, mirror_dir, + (u32 *)(RTL838X_SW_BASE + RTL839X_MIR_CTRL + i * 4)); + debugfs_create_x64("ingress_pm", 0644, mirror_dir, + (u64 *)(RTL838X_SW_BASE + priv->r->mir_spm + i * 8)); + debugfs_create_x64("egress_pm", 0644, mirror_dir, + (u64 *)(RTL838X_SW_BASE + priv->r->mir_dpm + i * 8)); + debugfs_create_x32("rspan_vlan", 0644, mirror_dir, + (u32 *)(RTL838X_SW_BASE + RTL839X_MIR_RSPAN_VLAN_CTRL(i))); + debugfs_create_x32("rspan_tx", 0644, mirror_dir, + (u32 *)(RTL838X_SW_BASE + RTL839X_MIR_RSPAN_TX_CTRL)); + debugfs_create_x32("rspan_tx_tag_rm", 0644, mirror_dir, + (u32 *)(RTL838X_SW_BASE + RTL839X_MIR_RSPAN_TX_TAG_RM_CTRL)); + debugfs_create_x32("rspan_tx_tag_en", 0644, mirror_dir, + (u32 *)(RTL838X_SW_BASE + RTL839X_MIR_RSPAN_TX_TAG_EN_CTRL)); + debugfs_create_x64("sample_rate", 0644, mirror_dir, + (u64 *)(RTL838X_SW_BASE + RTL839X_MIR_SAMPLE_RATE_CTRL)); + } + } + + if (priv->family_id == RTL8380_FAMILY_ID) + debugfs_create_x32("bpdu_flood_mask", 0644, rtl838x_dir, + (u32 *)(RTL838X_SW_BASE + priv->r->rma_bpdu_fld_pmask)); + else + debugfs_create_x64("bpdu_flood_mask", 0644, rtl838x_dir, + (u64 *)(RTL838X_SW_BASE + priv->r->rma_bpdu_fld_pmask)); + + if (priv->family_id == RTL8380_FAMILY_ID) + debugfs_create_x32("vlan_ctrl", 0644, rtl838x_dir, + (u32 *)(RTL838X_SW_BASE + RTL838X_VLAN_CTRL)); + else + debugfs_create_x32("vlan_ctrl", 0644, rtl838x_dir, + (u32 *)(RTL838X_SW_BASE + RTL839X_VLAN_CTRL)); + + ret = rtl838x_dbgfs_leds(rtl838x_dir, priv); + if (ret) + goto err; + return; err: rtl838x_dbgfs_cleanup(priv); diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/dsa.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/dsa.c index a0717e1d9a..e0832c42b8 100644 --- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/dsa.c +++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/dsa.c @@ -10,29 +10,6 @@ extern struct rtl83xx_soc_info soc_info; -static void rtl83xx_print_matrix(void) -{ - unsigned volatile int *ptr8; - volatile u64 *ptr9; - int i; - - if (soc_info.family == RTL8380_FAMILY_ID) { - ptr8 = RTL838X_SW_BASE + RTL838X_PORT_ISO_CTRL(0); - for (i = 0; i < 28; i += 8) - pr_debug("> %8x %8x %8x %8x %8x %8x %8x %8x\n", - ptr8[i + 0], ptr8[i + 1], ptr8[i + 2], ptr8[i + 3], - ptr8[i + 4], ptr8[i + 5], ptr8[i + 6], ptr8[i + 7]); - pr_debug("CPU_PORT> %8x\n", ptr8[28]); - } else { - ptr9 = RTL838X_SW_BASE + RTL839X_PORT_ISO_CTRL(0); - for (i = 0; i < 52; i += 4) - pr_debug("> %16llx %16llx %16llx %16llx\n", - ptr9[i + 0], ptr9[i + 1], ptr9[i + 2], ptr9[i + 3]); - pr_debug("CPU_PORT> %16llx\n", ptr9[52]); - } - -} - static void rtl83xx_init_stats(struct rtl838x_switch_priv *priv) { mutex_lock(&priv->reg_mutex); @@ -66,10 +43,17 @@ static void rtl83xx_write_cam(int idx, u32 *r) static u64 rtl83xx_hash_key(struct rtl838x_switch_priv *priv, u64 mac, u32 vid) { - if (priv->family_id == RTL8380_FAMILY_ID) + switch (priv->family_id) { + case RTL8380_FAMILY_ID: return rtl838x_hash(priv, mac << 12 | vid); - else + case RTL8390_FAMILY_ID: return rtl839x_hash(priv, mac << 12 | vid); + case RTL9300_FAMILY_ID: + return rtl930x_hash(priv, ((u64)vid) << 48 | mac); + default: + pr_err("Hash not implemented\n"); + } + return 0; } static void rtl83xx_write_hash(int idx, u32 *r) @@ -101,10 +85,10 @@ static void rtl83xx_enable_phy_polling(struct rtl838x_switch_priv *priv) pr_debug("%s: %16llx\n", __func__, v); priv->r->set_port_reg_le(v, priv->r->smi_poll_ctrl); - /* PHY update complete */ + /* PHY update complete, there is no global PHY polling enable bit on the 9300 */ if (priv->family_id == RTL8390_FAMILY_ID) sw_w32_mask(0, BIT(7), RTL839X_SMI_GLB_CTRL); - else + else if(priv->family_id == RTL9300_FAMILY_ID) sw_w32_mask(0, 0x8000, RTL838X_SMI_GLB_CTRL); } @@ -197,7 +181,10 @@ static int rtl83xx_setup(struct dsa_switch *ds) } priv->r->set_port_reg_be(port_bitmap, priv->r->port_iso_ctrl(priv->cpu_port)); - rtl83xx_print_matrix(); + if (priv->family_id == RTL8380_FAMILY_ID) + rtl838x_print_matrix(); + else + rtl839x_print_matrix(); rtl83xx_init_stats(priv); @@ -210,6 +197,44 @@ static int rtl83xx_setup(struct dsa_switch *ds) return 0; } +static int rtl930x_setup(struct dsa_switch *ds) +{ + int i; + struct rtl838x_switch_priv *priv = ds->priv; + u32 port_bitmap = BIT(priv->cpu_port); + + pr_info("%s called\n", __func__); + + // Enable CSTI STP mode +// sw_w32(1, RTL930X_ST_CTRL); + + /* Disable MAC polling the PHY so that we can start configuration */ + sw_w32(0, RTL930X_SMI_POLL_CTRL); + + // Disable all ports except CPU port + for (i = 0; i < ds->num_ports; i++) + priv->ports[i].enable = false; + priv->ports[priv->cpu_port].enable = true; + + for (i = 0; i < priv->cpu_port; i++) { + if (priv->ports[i].phy) { + priv->r->traffic_set(i, BIT(priv->cpu_port) | BIT(i)); + port_bitmap |= 1ULL << i; + } + } + priv->r->traffic_set(priv->cpu_port, port_bitmap); + + rtl930x_print_matrix(); + + // TODO: Initialize statistics + + ds->configure_vlan_while_not_filtering = true; + + rtl83xx_enable_phy_polling(priv); + + return 0; +} + static void rtl83xx_phylink_validate(struct dsa_switch *ds, int port, unsigned long *supported, struct phylink_link_state *state) @@ -269,13 +294,24 @@ static int rtl83xx_phylink_mac_link_state(struct dsa_switch *ds, int port, { struct rtl838x_switch_priv *priv = ds->priv; u64 speed; + u64 link; if (port < 0 || port > priv->cpu_port) return -EINVAL; + /* + * On the RTL9300 for at least the RTL8226B PHY, the MAC-side link + * state needs to be read twice in order to read a correct result. + * This would not be necessary for ports connected e.g. to RTL8218D + * PHYs. + */ state->link = 0; - if (priv->r->get_port_reg_le(priv->r->mac_link_sts) & BIT_ULL(port)) + link = priv->r->get_port_reg_le(priv->r->mac_link_sts); + link = priv->r->get_port_reg_le(priv->r->mac_link_sts); + if (link & BIT_ULL(port)) state->link = 1; + pr_info("%s: link state: %llx\n", __func__, link & BIT_ULL(port)); + state->duplex = 0; if (priv->r->get_port_reg_le(priv->r->mac_link_dup_sts) & BIT_ULL(port)) state->duplex = 1; @@ -317,6 +353,10 @@ static void rtl83xx_phylink_mac_config(struct dsa_switch *ds, int port, pr_debug("%s port %d, mode %x\n", __func__, port, mode); + // BUG: Make this work on RTL93XX + if (priv->family_id >= RTL9300_FAMILY_ID) + return; + if (port == priv->cpu_port) { /* Set Speed, duplex, flow control * FORCE_EN | LINK_EN | NWAY_EN | DUP_SEL @@ -422,15 +462,15 @@ static void rtl83xx_get_ethtool_stats(struct dsa_switch *ds, int port, struct rtl838x_switch_priv *priv = ds->priv; const struct rtl83xx_mib_desc *mib; int i; - u64 high; + u64 h; for (i = 0; i < ARRAY_SIZE(rtl83xx_mib); i++) { mib = &rtl83xx_mib[i]; - data[i] = sw_r32(priv->r->stat_port_std_mib(port) + 252 - mib->offset); + data[i] = sw_r32(priv->r->stat_port_std_mib + (port << 8) + 252 - mib->offset); if (mib->size == 2) { - high = sw_r32(priv->r->stat_port_std_mib(port) + 252 - mib->offset - 4); - data[i] |= high << 32; + h = sw_r32(priv->r->stat_port_std_mib + (port << 8) + 248 - mib->offset); + data[i] |= h << 32; } } } @@ -447,21 +487,30 @@ static int rtl83xx_port_enable(struct dsa_switch *ds, int port, struct phy_device *phydev) { struct rtl838x_switch_priv *priv = ds->priv; + u64 v; pr_debug("%s: %x %d", __func__, (u32) priv, port); priv->ports[port].enable = true; /* enable inner tagging on egress, do not keep any tags */ - sw_w32(1, priv->r->vlan_port_tag_sts_ctrl(port)); + if (priv->family_id == RTL9310_FAMILY_ID) + sw_w32(BIT(4), priv->r->vlan_port_tag_sts_ctrl + (port << 2)); + else + sw_w32(1, priv->r->vlan_port_tag_sts_ctrl + (port << 2)); if (dsa_is_cpu_port(ds, port)) return 0; /* add port to switch mask of CPU_PORT */ - priv->r->mask_port_reg_be(0ULL, BIT_ULL(port), priv->r->port_iso_ctrl(priv->cpu_port)); + priv->r->traffic_enable(priv->cpu_port, port); /* add all other ports in the same bridge to switch mask of port */ - priv->r->mask_port_reg_be(0ULL, priv->ports[port].pm, priv->r->port_iso_ctrl(port)); + v = priv->r->traffic_get(port); + v |= priv->ports[port].pm; + priv->r->traffic_set(port, v); + + sw_w32_mask(0, BIT(port), RTL930X_L2_PORT_SABLK_CTRL); + sw_w32_mask(0, BIT(port), RTL930X_L2_PORT_DABLK_CTRL); return 0; } @@ -469,17 +518,21 @@ static int rtl83xx_port_enable(struct dsa_switch *ds, int port, static void rtl83xx_port_disable(struct dsa_switch *ds, int port) { struct rtl838x_switch_priv *priv = ds->priv; + u64 v; pr_debug("%s %x: %d", __func__, (u32)priv, port); /* you can only disable user ports */ if (!dsa_is_user_port(ds, port)) return; + // BUG: This does not work on RTL931X /* remove port from switch mask of CPU_PORT */ - priv->r->mask_port_reg_be(BIT_ULL(port), 0, priv->r->port_iso_ctrl(priv->cpu_port)); + priv->r->traffic_disable(priv->cpu_port, port); /* remove all other ports in the same bridge from switch mask of port */ - priv->r->mask_port_reg_be(priv->ports[port].pm, 0LL, priv->r->port_iso_ctrl(port)); + v = priv->r->traffic_get(port); + v &= ~priv->ports[port].pm; + priv->r->traffic_set(port, v); priv->ports[port].enable = false; } @@ -565,7 +618,7 @@ static int rtl83xx_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *bridge) { struct rtl838x_switch_priv *priv = ds->priv; - u64 port_bitmap = BIT_ULL(priv->cpu_port); + u64 port_bitmap = 1ULL << priv->cpu_port, v; int i; pr_debug("%s %x: %d %llx", __func__, (u32)priv, port, port_bitmap); @@ -579,20 +632,19 @@ static int rtl83xx_port_bridge_join(struct dsa_switch *ds, int port, if (dsa_to_port(ds, i)->bridge_dev != bridge) continue; if (priv->ports[i].enable) - priv->r->mask_port_reg_be(0, BIT_ULL(port), - priv->r->port_iso_ctrl(i)); - priv->ports[i].pm |= BIT_ULL(port); + priv->r->traffic_enable(i, port); - port_bitmap |= BIT_ULL(i); + priv->ports[i].pm |= 1ULL << port; + port_bitmap |= 1ULL << i; } } /* Add all other ports to this port matrix. */ if (priv->ports[port].enable) { - priv->r->mask_port_reg_be(0, BIT_ULL(port), - priv->r->port_iso_ctrl(priv->cpu_port)); - priv->r->mask_port_reg_be(0, port_bitmap, - priv->r->port_iso_ctrl(port)); + priv->r->traffic_enable(priv->cpu_port, port); + v = priv->r->traffic_get(port); + v |= port_bitmap; + priv->r->traffic_set(port, v); } priv->ports[port].pm |= port_bitmap; mutex_unlock(&priv->reg_mutex); @@ -604,7 +656,7 @@ static void rtl83xx_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *bridge) { struct rtl838x_switch_priv *priv = ds->priv; - u64 port_bitmap = BIT_ULL(priv->cpu_port); + u64 port_bitmap = 1ULL << priv->cpu_port, v; int i; pr_debug("%s %x: %d", __func__, (u32)priv, port); @@ -620,62 +672,53 @@ static void rtl83xx_port_bridge_leave(struct dsa_switch *ds, int port, if (dsa_to_port(ds, i)->bridge_dev != bridge) continue; if (priv->ports[i].enable) - priv->r->mask_port_reg_be(BIT_ULL(port), 0, - priv->r->port_iso_ctrl(i)); - priv->ports[i].pm &= ~BIT_ULL(port); + priv->r->traffic_disable(i, port); + priv->ports[i].pm |= 1ULL << port; port_bitmap &= ~BIT_ULL(i); } } /* Add all other ports to this port matrix. */ - if (priv->ports[port].enable) - priv->r->mask_port_reg_be(0, port_bitmap, priv->r->port_iso_ctrl(port)); + if (priv->ports[port].enable) { + v = priv->r->traffic_get(port); + v |= port_bitmap; + priv->r->traffic_set(port, v); + } priv->ports[port].pm &= ~port_bitmap; mutex_unlock(&priv->reg_mutex); } -static void rtl83xx_port_stp_state_set(struct dsa_switch *ds, int port, - u8 state) +void rtl83xx_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) { - u32 cmd, msti = 0; + u32 msti = 0; u32 port_state[4]; - int index, bit, i; + int index, bit; int pos = port; struct rtl838x_switch_priv *priv = ds->priv; - int n = priv->family_id == RTL8380_FAMILY_ID ? 2 : 4; + int n = priv->port_width << 1; - pr_debug("%s: port %d state %2x\n", __func__, port, state); - - /* CPU PORT can only be configured on RTL838x */ - if (port >= priv->cpu_port || port > 51) + /* Ports above or equal CPU port can never be configured */ + if (port >= priv->cpu_port) return; mutex_lock(&priv->reg_mutex); - /* For the RTL839x, the bits are left-aligned in the 128 bit field */ + /* For the RTL839x and following, the bits are left-aligned, 838x and 930x + * have 64 bit fields, 839x and 931x have 128 bit fields + */ if (priv->family_id == RTL8390_FAMILY_ID) pos += 12; + if (priv->family_id == RTL9300_FAMILY_ID) + pos += 3; + if (priv->family_id == RTL9310_FAMILY_ID) + pos += 8; index = n - (pos >> 4) - 1; bit = (pos << 1) % 32; - if (priv->family_id == RTL8380_FAMILY_ID) { - cmd = BIT(15) /* Execute cmd */ - | BIT(14) /* Read */ - | 2 << 12 /* Table type 0b10 */ - | (msti & 0xfff); - } else { - cmd = BIT(16) /* Execute cmd */ - | 0 << 15 /* Read */ - | 5 << 12 /* Table type 0b101 */ - | (msti & 0xfff); - } - priv->r->exec_tbl0_cmd(cmd); - - for (i = 0; i < n; i++) - port_state[i] = sw_r32(priv->r->tbl_access_data_0(i)); + priv->r->stp_get(priv, msti, port_state); pr_debug("Current state, port %d: %d\n", port, (port_state[index] >> bit) & 3); port_state[index] &= ~(3 << bit); @@ -697,25 +740,12 @@ static void rtl83xx_port_stp_state_set(struct dsa_switch *ds, int port, break; } - if (priv->family_id == RTL8380_FAMILY_ID) { - cmd = BIT(15) /* Execute cmd */ - | 0 << 14 /* Write */ - | 2 << 12 /* Table type 0b10 */ - | (msti & 0xfff); - } else { - cmd = 1 << 16 /* Execute cmd */ - | BIT(15) /* Write */ - | 5 << 12 /* Table type 0b101 */ - | (msti & 0xfff); - } - for (i = 0; i < n; i++) - sw_w32(port_state[i], priv->r->tbl_access_data_0(i)); - priv->r->exec_tbl0_cmd(cmd); + priv->r->stp_set(priv, msti, port_state); mutex_unlock(&priv->reg_mutex); } -static void rtl83xx_fast_age(struct dsa_switch *ds, int port) +void rtl83xx_fast_age(struct dsa_switch *ds, int port) { struct rtl838x_switch_priv *priv = ds->priv; int s = priv->family_id == RTL8390_FAMILY_ID ? 2 : 0; @@ -735,7 +765,22 @@ static void rtl83xx_fast_age(struct dsa_switch *ds, int port) */ sw_w32(1 << (26 + s) | 1 << (23 + s) | port << (5 + (s / 2)), priv->r->l2_tbl_flush_ctrl); - do { } while (sw_r32(priv->r->l2_tbl_flush_ctrl) & (1 << (26 + s))); + do { } while (sw_r32(priv->r->l2_tbl_flush_ctrl) & BIT(26 + s)); + + mutex_unlock(&priv->reg_mutex); +} + +void rtl930x_fast_age(struct dsa_switch *ds, int port) +{ + struct rtl838x_switch_priv *priv = ds->priv; + + pr_debug("FAST AGE port %d\n", port); + mutex_lock(&priv->reg_mutex); + sw_w32(port << 11, RTL930X_L2_TBL_FLUSH_CTRL + 4); + + sw_w32(BIT(26) | BIT(30), RTL930X_L2_TBL_FLUSH_CTRL); + + do { } while (sw_r32(priv->r->l2_tbl_flush_ctrl) & BIT(30)); mutex_unlock(&priv->reg_mutex); } @@ -749,17 +794,24 @@ static int rtl83xx_vlan_filtering(struct dsa_switch *ds, int port, mutex_lock(&priv->reg_mutex); if (vlan_filtering) { - /* Enable ingress and egress filtering */ + /* Enable ingress and egress filtering + * The VLAN_PORT_IGR_FILTER register uses 2 bits for each port to define + * the filter action: + * 0: Always Forward + * 1: Drop packet + * 2: Trap packet to CPU port + * The Egress filter used 1 bit per state (0: DISABLED, 1: ENABLED) + */ if (port != priv->cpu_port) sw_w32_mask(0b10 << ((port % 16) << 1), 0b01 << ((port % 16) << 1), - priv->r->vlan_port_igr_filter(port)); - sw_w32_mask(0, 1 << (port % 32), priv->r->vlan_port_egr_filter(port)); + priv->r->vlan_port_igr_filter + ((port >> 5) << 2)); + sw_w32_mask(0, BIT(port % 32), priv->r->vlan_port_egr_filter + ((port >> 4) << 2)); } else { /* Disable ingress and egress filtering */ if (port != priv->cpu_port) sw_w32_mask(0b11 << ((port % 16) << 1), 0, - priv->r->vlan_port_igr_filter(port)); - sw_w32_mask(1 << (port % 32), 0, priv->r->vlan_port_egr_filter(port)); + priv->r->vlan_port_igr_filter + ((port >> 5) << 2)); + sw_w32_mask(BIT(port % 32), 0, priv->r->vlan_port_egr_filter + ((port >> 4) << 2)); } /* Do we need to do something to the CPU-Port, too? */ @@ -774,21 +826,23 @@ static int rtl83xx_vlan_prepare(struct dsa_switch *ds, int port, struct rtl838x_vlan_info info; struct rtl838x_switch_priv *priv = ds->priv; - pr_debug("%s: port %d\n", __func__, port); + pr_info("%s: port %d\n", __func__, port); mutex_lock(&priv->reg_mutex); - if (priv->family_id == RTL8380_FAMILY_ID) - rtl838x_vlan_profile_dump(0); - else - rtl839x_vlan_profile_dump(0); + priv->r->vlan_profile_dump(1); + priv->r->vlan_tables_read(1, &info); - priv->r->vlan_tables_read(0, &info); - - pr_debug("Tagged ports %llx, untag %llx, prof %x, MC# %d, UC# %d, FID %x\n", + pr_info("Tagged ports %llx, untag %llx, prof %x, MC# %d, UC# %d, FID %x\n", info.tagged_ports, info.untagged_ports, info.profile_id, info.hash_mc_fid, info.hash_uc_fid, info.fid); + priv->r->vlan_set_untagged(1, info.untagged_ports); + pr_debug("SET: Untagged ports, VLAN %d: %llx\n", 1, info.untagged_ports); + + priv->r->vlan_set_tagged(1, &info); + pr_debug("SET: Tagged ports, VLAN %d: %llx\n", 1, info.tagged_ports); + mutex_unlock(&priv->reg_mutex); return 0; } @@ -796,11 +850,11 @@ static int rtl83xx_vlan_prepare(struct dsa_switch *ds, int port, static void rtl83xx_vlan_add(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan) { - struct rtl838x_vlan_info info = {}; + struct rtl838x_vlan_info info; struct rtl838x_switch_priv *priv = ds->priv; int v; - pr_debug("%s port %d, vid_end %d, vid_end %d, flags %x\n", __func__, + pr_info("%s port %d, vid_end %d, vid_end %d, flags %x\n", __func__, port, vlan->vid_begin, vlan->vid_end, vlan->flags); if (vlan->vid_begin > 4095 || vlan->vid_end > 4095) { @@ -812,12 +866,19 @@ static void rtl83xx_vlan_add(struct dsa_switch *ds, int port, mutex_lock(&priv->reg_mutex); if (vlan->flags & BRIDGE_VLAN_INFO_PVID) { - /* Set both inner and outer PVID of the port */ - sw_w32((vlan->vid_end << 16) | vlan->vid_end << 2, priv->r->vlan_port_pb(port)); - priv->ports[port].pvid = vlan->vid_end; + for (v = vlan->vid_begin; v <= vlan->vid_end; v++) { + if (!v) + continue; + /* Set both inner and outer PVID of the port */ + sw_w32((v << 16) | v << 2, priv->r->vlan_port_pb + (port << 2)); + priv->ports[port].pvid = vlan->vid_end; + } } for (v = vlan->vid_begin; v <= vlan->vid_end; v++) { + if (!v) + continue; + /* Get port memberships of this vlan */ priv->r->vlan_tables_read(v, &info); @@ -838,10 +899,10 @@ static void rtl83xx_vlan_add(struct dsa_switch *ds, int port, info.untagged_ports |= BIT_ULL(port); priv->r->vlan_set_untagged(v, info.untagged_ports); - pr_debug("Untagged ports, VLAN %d: %llx\n", v, info.untagged_ports); + pr_info("Untagged ports, VLAN %d: %llx\n", v, info.untagged_ports); priv->r->vlan_set_tagged(v, &info); - pr_debug("Tagged ports, VLAN %d: %llx\n", v, info.tagged_ports); + pr_info("Tagged ports, VLAN %d: %llx\n", v, info.tagged_ports); } mutex_unlock(&priv->reg_mutex); @@ -870,7 +931,7 @@ static int rtl83xx_vlan_del(struct dsa_switch *ds, int port, for (v = vlan->vid_begin; v <= vlan->vid_end; v++) { /* Reset to default if removing the current PVID */ if (v == pvid) - sw_w32(0, priv->r->vlan_port_pb(port)); + sw_w32(0, priv->r->vlan_port_pb + (port << 2)); /* Get port memberships of this vlan */ priv->r->vlan_tables_read(v, &info); @@ -962,7 +1023,7 @@ static int rtl83xx_port_fdb_del(struct dsa_switch *ds, int port, u64 entry; int idx = -1, err = 0, i; - pr_debug("In %s, mac %llx, vid: %d, key: %x\n", __func__, mac, vid, key); + pr_debug("In %s, mac %llx, vid: %d, key: %x08x\n", __func__, mac, vid, key); mutex_lock(&priv->reg_mutex); for (i = 0; i < 4; i++) { entry = priv->r->read_l2_entry_using_hash(key, i, &e); @@ -1048,6 +1109,7 @@ static int rtl83xx_port_mirror_add(struct dsa_switch *ds, int port, /* We support 4 mirror groups, one destination port per group */ int group; struct rtl838x_switch_priv *priv = ds->priv; + int ctrl_reg, dpm_reg, spm_reg; pr_debug("In %s\n", __func__); @@ -1065,30 +1127,34 @@ static int rtl83xx_port_mirror_add(struct dsa_switch *ds, int port, if (group >= 4) return -ENOSPC; + ctrl_reg = priv->r->mir_ctrl + group * 4; + dpm_reg = priv->r->mir_dpm + group * 4 * priv->port_width; + spm_reg = priv->r->mir_spm + group * 4 * priv->port_width; + pr_debug("Using group %d\n", group); mutex_lock(&priv->reg_mutex); if (priv->family_id == RTL8380_FAMILY_ID) { /* Enable mirroring to port across VLANs (bit 11) */ - sw_w32(1 << 11 | (mirror->to_local_port << 4) | 1, RTL838X_MIR_CTRL(group)); + sw_w32(1 << 11 | (mirror->to_local_port << 4) | 1, ctrl_reg); } else { /* Enable mirroring to destination port */ - sw_w32((mirror->to_local_port << 4) | 1, RTL839X_MIR_CTRL(group)); + sw_w32((mirror->to_local_port << 4) | 1, ctrl_reg); } - if (ingress && (priv->r->get_port_reg_be(priv->r->mir_spm(group)) & (1ULL << port))) { + if (ingress && (priv->r->get_port_reg_be(spm_reg) & (1ULL << port))) { mutex_unlock(&priv->reg_mutex); return -EEXIST; } - if ((!ingress) && (priv->r->get_port_reg_be(priv->r->mir_dpm(group)) & (1ULL << port))) { + if ((!ingress) && (priv->r->get_port_reg_be(dpm_reg) & (1ULL << port))) { mutex_unlock(&priv->reg_mutex); return -EEXIST; } if (ingress) - priv->r->mask_port_reg_be(0, 1ULL << port, priv->r->mir_spm(group)); + priv->r->mask_port_reg_be(0, 1ULL << port, spm_reg); else - priv->r->mask_port_reg_be(0, 1ULL << port, priv->r->mir_dpm(group)); + priv->r->mask_port_reg_be(0, 1ULL << port, dpm_reg); priv->mirror_group_ports[group] = mirror->to_local_port; mutex_unlock(&priv->reg_mutex); @@ -1100,6 +1166,7 @@ static void rtl83xx_port_mirror_del(struct dsa_switch *ds, int port, { int group = 0; struct rtl838x_switch_priv *priv = ds->priv; + int ctrl_reg, dpm_reg, spm_reg; pr_debug("In %s\n", __func__); for (group = 0; group < 4; group++) { @@ -1109,29 +1176,66 @@ static void rtl83xx_port_mirror_del(struct dsa_switch *ds, int port, if (group >= 4) return; + ctrl_reg = priv->r->mir_ctrl + group * 4; + dpm_reg = priv->r->mir_dpm + group * 4 * priv->port_width; + spm_reg = priv->r->mir_spm + group * 4 * priv->port_width; + mutex_lock(&priv->reg_mutex); if (mirror->ingress) { /* Ingress, clear source port matrix */ - priv->r->mask_port_reg_be(1ULL << port, 0, priv->r->mir_spm(group)); + priv->r->mask_port_reg_be(1ULL << port, 0, spm_reg); } else { /* Egress, clear destination port matrix */ - priv->r->mask_port_reg_be(1ULL << port, 0, priv->r->mir_dpm(group)); + priv->r->mask_port_reg_be(1ULL << port, 0, dpm_reg); } - if (!(sw_r32(priv->r->mir_spm(group)) || sw_r32(priv->r->mir_dpm(group)))) { + if (!(sw_r32(spm_reg) || sw_r32(dpm_reg))) { priv->mirror_group_ports[group] = -1; - sw_w32(0, priv->r->mir_ctrl(group)); + sw_w32(0, ctrl_reg); } mutex_unlock(&priv->reg_mutex); } +int dsa_phy_read(struct dsa_switch *ds, int phy_addr, int phy_reg) +{ + u32 val; + u32 offset = 0; + struct rtl838x_switch_priv *priv = ds->priv; + + if (phy_addr >= 24 && phy_addr <= 27 + && priv->ports[24].phy == PHY_RTL838X_SDS) { + if (phy_addr == 26) + offset = 0x100; + val = sw_r32(RTL838X_SDS4_FIB_REG0 + offset + (phy_reg << 2)) & 0xffff; + return val; + } + + read_phy(phy_addr, 0, phy_reg, &val); + return val; +} + +int dsa_phy_write(struct dsa_switch *ds, int phy_addr, int phy_reg, u16 val) +{ + u32 offset = 0; + struct rtl838x_switch_priv *priv = ds->priv; + + if (phy_addr >= 24 && phy_addr <= 27 + && priv->ports[24].phy == PHY_RTL838X_SDS) { + if (phy_addr == 26) + offset = 0x100; + sw_w32(val, RTL838X_SDS4_FIB_REG0 + offset + (phy_reg << 2)); + return 0; + } + return write_phy(phy_addr, 0, phy_reg, val); +} + const struct dsa_switch_ops rtl83xx_switch_ops = { .get_tag_protocol = rtl83xx_get_tag_protocol, .setup = rtl83xx_setup, - .phy_read = rtl83xx_dsa_phy_read, - .phy_write = rtl83xx_dsa_phy_write, + .phy_read = dsa_phy_read, + .phy_write = dsa_phy_write, .phylink_validate = rtl83xx_phylink_validate, .phylink_mac_link_state = rtl83xx_phylink_mac_link_state, @@ -1168,3 +1272,38 @@ const struct dsa_switch_ops rtl83xx_switch_ops = { .port_mirror_del = rtl83xx_port_mirror_del, }; +const struct dsa_switch_ops rtl930x_switch_ops = { + .get_tag_protocol = rtl83xx_get_tag_protocol, + .setup = rtl930x_setup, + + .phy_read = dsa_phy_read, + .phy_write = dsa_phy_write, + + .phylink_validate = rtl83xx_phylink_validate, + .phylink_mac_link_state = rtl83xx_phylink_mac_link_state, + .phylink_mac_config = rtl83xx_phylink_mac_config, + .phylink_mac_link_down = rtl83xx_phylink_mac_link_down, + .phylink_mac_link_up = rtl83xx_phylink_mac_link_up, + + .get_strings = rtl83xx_get_strings, + .get_ethtool_stats = rtl83xx_get_ethtool_stats, + .get_sset_count = rtl83xx_get_sset_count, + + .port_enable = rtl83xx_port_enable, + .port_disable = rtl83xx_port_disable, + + .set_ageing_time = rtl83xx_set_l2aging, + .port_bridge_join = rtl83xx_port_bridge_join, + .port_bridge_leave = rtl83xx_port_bridge_leave, + .port_stp_state_set = rtl83xx_port_stp_state_set, + .port_fast_age = rtl930x_fast_age, + + .port_vlan_filtering = rtl83xx_vlan_filtering, + .port_vlan_prepare = rtl83xx_vlan_prepare, + .port_vlan_add = rtl83xx_vlan_add, + .port_vlan_del = rtl83xx_vlan_del, + + .port_fdb_add = rtl83xx_port_fdb_add, + .port_fdb_del = rtl83xx_port_fdb_del, + .port_fdb_dump = rtl83xx_port_fdb_dump, +}; diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/qos.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/qos.c new file mode 100644 index 0000000000..2fc8d37f3e --- /dev/null +++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/qos.c @@ -0,0 +1,576 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include + +#include +#include "rtl83xx.h" + +static struct rtl838x_switch_priv *switch_priv; +extern struct rtl83xx_soc_info soc_info; + +enum scheduler_type { + WEIGHTED_FAIR_QUEUE = 0, + WEIGHTED_ROUND_ROBIN, +}; + +int max_available_queue[] = {0, 1, 2, 3, 4, 5, 6, 7}; +int default_queue_weights[] = {1, 1, 1, 1, 1, 1, 1, 1}; +int dot1p_priority_remapping[] = {0, 1, 2, 3, 4, 5, 6, 7}; + +static void rtl839x_read_scheduling_table(int port) +{ + u32 cmd = 1 << 9 /* Execute cmd */ + | 0 << 8 /* Read */ + | 0 << 6 /* Table type 0b00 */ + | (port & 0x3f); + rtl839x_exec_tbl2_cmd(cmd); +} + +static void rtl839x_write_scheduling_table(int port) +{ + u32 cmd = 1 << 9 /* Execute cmd */ + | 1 << 8 /* Write */ + | 0 << 6 /* Table type 0b00 */ + | (port & 0x3f); + rtl839x_exec_tbl2_cmd(cmd); +} + +static void rtl839x_read_out_q_table(int port) +{ + u32 cmd = 1 << 9 /* Execute cmd */ + | 0 << 8 /* Read */ + | 2 << 6 /* Table type 0b10 */ + | (port & 0x3f); + rtl839x_exec_tbl2_cmd(cmd); +} + +static void rtl838x_storm_enable(struct rtl838x_switch_priv *priv, int port, bool enable) +{ + // Enable Storm control for that port for UC, MC, and BC + if (enable) + sw_w32(0x7, RTL838X_STORM_CTRL_LB_CTRL(port)); + else + sw_w32(0x0, RTL838X_STORM_CTRL_LB_CTRL(port)); +} + +u32 rtl838x_get_egress_rate(struct rtl838x_switch_priv *priv, int port) +{ + u32 rate; + + if (port > priv->cpu_port) + return 0; + rate = sw_r32(RTL838X_SCHED_P_EGR_RATE_CTRL(port)) & 0x3fff; + return rate; +} + +/* Sets the rate limit, 10MBit/s is equal to a rate value of 625 */ +int rtl838x_set_egress_rate(struct rtl838x_switch_priv *priv, int port, u32 rate) +{ + u32 old_rate; + + if (port > priv->cpu_port) + return -1; + + old_rate = sw_r32(RTL838X_SCHED_P_EGR_RATE_CTRL(port)); + sw_w32(rate, RTL838X_SCHED_P_EGR_RATE_CTRL(port)); + + return old_rate; +} + +/* Set the rate limit for a particular queue in Bits/s + * units of the rate is 16Kbps + */ +void rtl838x_egress_rate_queue_limit(struct rtl838x_switch_priv *priv, int port, + int queue, u32 rate) +{ + if (port > priv->cpu_port) + return; + if (queue > 7) + return; + sw_w32(rate, RTL838X_SCHED_Q_EGR_RATE_CTRL(port, queue)); +} + +static void rtl838x_rate_control_init(struct rtl838x_switch_priv *priv) +{ + int i; + + pr_info("Enabling Storm control\n"); + // TICK_PERIOD_PPS + if (priv->id == 0x8380) + sw_w32_mask(0x3ff << 20, 434 << 20, RTL838X_SCHED_LB_TICK_TKN_CTRL_0); + + // Set burst rate + sw_w32(0x00008000, RTL838X_STORM_CTRL_BURST_0); // UC + sw_w32(0x80008000, RTL838X_STORM_CTRL_BURST_1); // MC and BC + + // Set burst Packets per Second to 32 + sw_w32(0x00000020, RTL838X_STORM_CTRL_BURST_PPS_0); // UC + sw_w32(0x00200020, RTL838X_STORM_CTRL_BURST_PPS_1); // MC and BC + + // Include IFG in storm control, rate based on bytes/s (0 = packets) + sw_w32_mask(0, 1 << 6 | 1 << 5, RTL838X_STORM_CTRL); + // Bandwidth control includes preamble and IFG (10 Bytes) + sw_w32_mask(0, 1, RTL838X_SCHED_CTRL); + + // On SoCs except RTL8382M, set burst size of port egress + if (priv->id != 0x8382) + sw_w32_mask(0xffff, 0x800, RTL838X_SCHED_LB_THR); + + /* Enable storm control on all ports with a PHY and limit rates, + * for UC and MC for both known and unknown addresses */ + for (i = 0; i < priv->cpu_port; i++) { + if (priv->ports[i].phy) { + sw_w32((1 << 18) | 0x8000, RTL838X_STORM_CTRL_PORT_UC(i)); + sw_w32((1 << 18) | 0x8000, RTL838X_STORM_CTRL_PORT_MC(i)); + sw_w32(0x8000, RTL838X_STORM_CTRL_PORT_BC(i)); + rtl838x_storm_enable(priv, i, true); + } + } + + // Attack prevention, enable all attack prevention measures + //sw_w32(0x1ffff, RTL838X_ATK_PRVNT_CTRL); + /* Attack prevention, drop (bit = 0) problematic packets on all ports. + * Setting bit = 1 means: trap to CPU + */ + //sw_w32(0, RTL838X_ATK_PRVNT_ACT); + // Enable attack prevention on all ports + //sw_w32(0x0fffffff, RTL838X_ATK_PRVNT_PORT_EN); +} + +/* Sets the rate limit, 10MBit/s is equal to a rate value of 625 */ +u32 rtl839x_get_egress_rate(struct rtl838x_switch_priv *priv, int port) +{ + u32 rate; + + pr_debug("%s: Getting egress rate on port %d to %d\n", __func__, port, rate); + if (port >= priv->cpu_port) + return 0; + + mutex_lock(&priv->reg_mutex); + + rtl839x_read_scheduling_table(port); + + rate = sw_r32(RTL839X_TBL_ACCESS_DATA_2(7)); + rate <<= 12; + rate |= sw_r32(RTL839X_TBL_ACCESS_DATA_2(8)) >> 20; + + mutex_unlock(&priv->reg_mutex); + + return rate; +} + +/* Sets the rate limit, 10MBit/s is equal to a rate value of 625, returns previous rate */ +int rtl839x_set_egress_rate(struct rtl838x_switch_priv *priv, int port, u32 rate) +{ + u32 old_rate; + + pr_debug("%s: Setting egress rate on port %d to %d\n", __func__, port, rate); + if (port >= priv->cpu_port) + return -1; + + mutex_lock(&priv->reg_mutex); + + rtl839x_read_scheduling_table(port); + + old_rate = sw_r32(RTL839X_TBL_ACCESS_DATA_2(7)) & 0xff; + old_rate <<= 12; + old_rate |= sw_r32(RTL839X_TBL_ACCESS_DATA_2(8)) >> 20; + sw_w32_mask(0xff, (rate >> 12) & 0xff, RTL839X_TBL_ACCESS_DATA_2(7)); + sw_w32_mask(0xfff << 20, rate << 20, RTL839X_TBL_ACCESS_DATA_2(8)); + + rtl839x_write_scheduling_table(port); + + mutex_unlock(&priv->reg_mutex); + + return old_rate; +} + +/* Set the rate limit for a particular queue in Bits/s + * units of the rate is 16Kbps + */ +void rtl839x_egress_rate_queue_limit(struct rtl838x_switch_priv *priv, int port, + int queue, u32 rate) +{ + int lsb = 128 + queue * 20; + int low_byte = 8 - (lsb >> 5); + int start_bit = lsb - (low_byte << 5); + u32 high_mask = 0xfffff >> (32 - start_bit); + + pr_debug("%s: Setting egress rate on port %d, queue %d to %d\n", + __func__, port, queue, rate); + if (port >= priv->cpu_port) + return; + if (queue > 7) + return; + + mutex_lock(&priv->reg_mutex); + + rtl839x_read_scheduling_table(port); + + sw_w32_mask(0xfffff << start_bit, (rate & 0xfffff) << start_bit, + RTL839X_TBL_ACCESS_DATA_2(low_byte)); + if (high_mask) + sw_w32_mask(high_mask, (rate & 0xfffff) >> (32- start_bit), + RTL839X_TBL_ACCESS_DATA_2(low_byte - 1)); + + rtl839x_write_scheduling_table(port); + + mutex_unlock(&priv->reg_mutex); +} + +static void rtl839x_rate_control_init(struct rtl838x_switch_priv *priv) +{ + int p, q; + + pr_info("%s: enabling rate control\n", __func__); + /* Tick length and token size settings for SoC with 250MHz, + * RTL8350 family would use 50MHz + */ + // Set the special tick period + sw_w32(976563, RTL839X_STORM_CTRL_SPCL_LB_TICK_TKN_CTRL); + // Ingress tick period and token length 10G + sw_w32(18 << 11 | 151, RTL839X_IGR_BWCTRL_LB_TICK_TKN_CTRL_0); + // Ingress tick period and token length 1G + sw_w32(245 << 11 | 129, RTL839X_IGR_BWCTRL_LB_TICK_TKN_CTRL_1); + // Egress tick period 10G, bytes/token 10G and tick period 1G, bytes/token 1G + sw_w32(18 << 24 | 151 << 16 | 185 << 8 | 97, RTL839X_SCHED_LB_TICK_TKN_CTRL); + // Set the tick period of the CPU and the Token Len + sw_w32(3815 << 8 | 1, RTL839X_SCHED_LB_TICK_TKN_PPS_CTRL); + + // Set the Weighted Fair Queueing burst size + sw_w32_mask(0xffff, 4500, RTL839X_SCHED_LB_THR); + + // Storm-rate calculation is based on bytes/sec (bit 5), include IFG (bit 6) + sw_w32_mask(0, 1 << 5 | 1 << 6, RTL839X_STORM_CTRL); + + /* Based on the rate control mode being bytes/s + * set tick period and token length for 10G + */ + sw_w32(18 << 10 | 151, RTL839X_STORM_CTRL_LB_TICK_TKN_CTRL_0); + /* and for 1G ports */ + sw_w32(246 << 10 | 129, RTL839X_STORM_CTRL_LB_TICK_TKN_CTRL_1); + + /* Set default burst rates on all ports (the same for 1G / 10G) with a PHY + * for UC, MC and BC + * For 1G port, the minimum burst rate is 1700, maximum 65535, + * For 10G ports it is 2650 and 1048575 respectively */ + for (p = 0; p < priv->cpu_port; p++) { + if (priv->ports[p].phy && !priv->ports[p].is10G) { + sw_w32_mask(0xffff, 0x8000, RTL839X_STORM_CTRL_PORT_UC_1(p)); + sw_w32_mask(0xffff, 0x8000, RTL839X_STORM_CTRL_PORT_MC_1(p)); + sw_w32_mask(0xffff, 0x8000, RTL839X_STORM_CTRL_PORT_BC_1(p)); + } + } + + /* Setup ingress/egress per-port rate control */ + for (p = 0; p < priv->cpu_port; p++) { + if (!priv->ports[p].phy) + continue; + + if (priv->ports[p].is10G) + rtl839x_set_egress_rate(priv, p, 625000); // 10GB/s + else + rtl839x_set_egress_rate(priv, p, 62500); // 1GB/s + + // Setup queues: all RTL83XX SoCs have 8 queues, maximum rate + for (q = 0; q < 8; q++) + rtl839x_egress_rate_queue_limit(priv, p, q, 0xfffff); + + if (priv->ports[p].is10G) { + // Set high threshold to maximum + sw_w32_mask(0xffff, 0xffff, RTL839X_IGR_BWCTRL_PORT_CTRL_10G_0(p)); + } else { + // Set high threshold to maximum + sw_w32_mask(0xffff, 0xffff, RTL839X_IGR_BWCTRL_PORT_CTRL_1(p)); + } + } + + // Set global ingress low watermark rate + sw_w32(65532, RTL839X_IGR_BWCTRL_CTRL_LB_THR); +} + + + +void rtl838x_setup_prio2queue_matrix(int *min_queues) +{ + int i; + u32 v; + + pr_info("Current Intprio2queue setting: %08x\n", sw_r32(RTL838X_QM_INTPRI2QID_CTRL)); + for (i = 0; i < MAX_PRIOS; i++) + v |= i << (min_queues[i] * 3); + sw_w32(v, RTL838X_QM_INTPRI2QID_CTRL); +} + +void rtl839x_setup_prio2queue_matrix(int *min_queues) +{ + int i, q; + + pr_info("Current Intprio2queue setting: %08x\n", sw_r32(RTL839X_QM_INTPRI2QID_CTRL(0))); + for (i = 0; i < MAX_PRIOS; i++) { + q = min_queues[i]; + sw_w32(i << (q * 3), RTL839X_QM_INTPRI2QID_CTRL(q)); + } +} + +/* Sets the CPU queue depending on the internal priority of a packet */ +void rtl83xx_setup_prio2queue_cpu_matrix(int *max_queues) +{ + int reg = soc_info.family == RTL8380_FAMILY_ID ? RTL838X_QM_PKT2CPU_INTPRI_MAP + : RTL839X_QM_PKT2CPU_INTPRI_MAP; + int i; + u32 v; + + pr_info("QM_PKT2CPU_INTPRI_MAP: %08x\n", sw_r32(reg)); + for (i = 0; i < MAX_PRIOS; i++) + v |= max_queues[i] << (i * 3); + sw_w32(v, reg); +} + +void rtl83xx_setup_default_prio2queue(void) +{ + if (soc_info.family == RTL8380_FAMILY_ID) { + rtl838x_setup_prio2queue_matrix(max_available_queue); + } else { + rtl839x_setup_prio2queue_matrix(max_available_queue); + } + rtl83xx_setup_prio2queue_cpu_matrix(max_available_queue); +} + +/* Sets the output queue assigned to a port, the port can be the CPU-port */ +void rtl839x_set_egress_queue(int port, int queue) +{ + sw_w32(queue << ((port % 10) *3), RTL839X_QM_PORT_QNUM(port)); +} + +/* Sets the priority assigned of an ingress port, the port can be the CPU-port */ +void rtl83xx_set_ingress_priority(int port, int priority) +{ + if (soc_info.family == RTL8380_FAMILY_ID) + sw_w32(priority << ((port % 10) *3), RTL838X_PRI_SEL_PORT_PRI(port)); + else + sw_w32(priority << ((port % 10) *3), RTL839X_PRI_SEL_PORT_PRI(port)); + +} + +int rtl839x_get_scheduling_algorithm(struct rtl838x_switch_priv *priv, int port) +{ + u32 v; + + mutex_lock(&priv->reg_mutex); + + rtl839x_read_scheduling_table(port); + v = sw_r32(RTL839X_TBL_ACCESS_DATA_2(8)); + + mutex_unlock(&priv->reg_mutex); + + if (v & BIT(19)) + return WEIGHTED_ROUND_ROBIN; + return WEIGHTED_FAIR_QUEUE; +} + +void rtl839x_set_scheduling_algorithm(struct rtl838x_switch_priv *priv, int port, + enum scheduler_type sched) +{ + enum scheduler_type t = rtl839x_get_scheduling_algorithm(priv, port); + u32 v, oam_state, oam_port_state; + u32 count; + int i, egress_rate; + + mutex_lock(&priv->reg_mutex); + /* Check whether we need to empty the egress queue of that port due to Errata E0014503 */ + if (sched == WEIGHTED_FAIR_QUEUE && t == WEIGHTED_ROUND_ROBIN && port != priv->cpu_port) { + // Read Operations, Adminstatrion and Management control register + oam_state = sw_r32(RTL839X_OAM_CTRL); + + // Get current OAM state + oam_port_state = sw_r32(RTL839X_OAM_PORT_ACT_CTRL(port)); + + // Disable OAM to block traffice + v = sw_r32(RTL839X_OAM_CTRL); + sw_w32_mask(0, 1, RTL839X_OAM_CTRL); + v = sw_r32(RTL839X_OAM_CTRL); + + // Set to trap action OAM forward (bits 1, 2) and OAM Mux Action Drop (bit 0) + sw_w32(0x2, RTL839X_OAM_PORT_ACT_CTRL(port)); + + // Set port egress rate to unlimited + egress_rate = rtl839x_set_egress_rate(priv, port, 0xFFFFF); + + // Wait until the egress used page count of that port is 0 + i = 0; + do { + usleep_range(100, 200); + rtl839x_read_out_q_table(port); + count = sw_r32(RTL839X_TBL_ACCESS_DATA_2(6)); + count >>= 20; + i++; + } while (i < 3500 && count > 0); + } + + // Actually set the scheduling algorithm + rtl839x_read_scheduling_table(port); + sw_w32_mask(BIT(19), sched ? BIT(19) : 0, RTL839X_TBL_ACCESS_DATA_2(8)); + rtl839x_write_scheduling_table(port); + + if (sched == WEIGHTED_FAIR_QUEUE && t == WEIGHTED_ROUND_ROBIN && port != priv->cpu_port) { + // Restore OAM state to control register + sw_w32(oam_state, RTL839X_OAM_CTRL); + + // Restore trap action state + sw_w32(oam_port_state, RTL839X_OAM_PORT_ACT_CTRL(port)); + + // Restore port egress rate + rtl839x_set_egress_rate(priv, port, egress_rate); + } + + mutex_unlock(&priv->reg_mutex); +} + +void rtl839x_set_scheduling_queue_weights(struct rtl838x_switch_priv *priv, int port, + int *queue_weights) +{ + int i, lsb, low_byte, start_bit, high_mask; + + mutex_lock(&priv->reg_mutex); + + rtl839x_read_scheduling_table(port); + + for (i = 0; i < 8; i++) { + lsb = 48 + i * 8; + low_byte = 8 - (lsb >> 5); + start_bit = lsb - (low_byte << 5); + high_mask = 0x3ff >> (32 - start_bit); + sw_w32_mask(0x3ff << start_bit, (queue_weights[i] & 0x3ff) << start_bit, + RTL839X_TBL_ACCESS_DATA_2(low_byte)); + if (high_mask) + sw_w32_mask(high_mask, (queue_weights[i] & 0x3ff) >> (32- start_bit), + RTL839X_TBL_ACCESS_DATA_2(low_byte - 1)); + } + + rtl839x_write_scheduling_table(port); + mutex_unlock(&priv->reg_mutex); +} + +void rtl838x_config_qos(void) +{ + int i, p; + u32 v; + + pr_info("Setting up RTL838X QoS\n"); + pr_info("RTL838X_PRI_SEL_TBL_CTRL(i): %08x\n", sw_r32(RTL838X_PRI_SEL_TBL_CTRL(0))); + rtl83xx_setup_default_prio2queue(); + + // Enable inner (bit 12) and outer (bit 13) priority remapping from DSCP + sw_w32_mask(0, BIT(12) | BIT(13), RTL838X_PRI_DSCP_INVLD_CTRL0); + + /* Set default weight for calculating internal priority, in prio selection group 0 + * Port based (prio 3), Port outer-tag (4), DSCP (5), Inner Tag (6), Outer Tag (7) + */ + v = 3 | (4 << 3) | (5 << 6) | (6 << 9) | (7 << 12); + sw_w32(v, RTL838X_PRI_SEL_TBL_CTRL(0)); + + // Set the inner and outer priority one-to-one to re-marked outer dot1p priority + v = 0; + for (p = 0; p < 8; p++) + v |= p << (3 * p); + sw_w32(v, RTL838X_RMK_OPRI_CTRL); + sw_w32(v, RTL838X_RMK_IPRI_CTRL); + + v = 0; + for (p = 0; p < 8; p++) + v |= (dot1p_priority_remapping[p] & 0x7) << (p * 3); + sw_w32(v, RTL838X_PRI_SEL_IPRI_REMAP); + + // On all ports set scheduler type to WFQ + for (i = 0; i <= soc_info.cpu_port; i++) + sw_w32(0, RTL838X_SCHED_P_TYPE_CTRL(i)); + + // Enable egress scheduler for CPU-Port + sw_w32_mask(0, BIT(8), RTL838X_SCHED_LB_CTRL(soc_info.cpu_port)); + + // Enable egress drop allways on + sw_w32_mask(0, BIT(11), RTL838X_FC_P_EGR_DROP_CTRL(soc_info.cpu_port)); + + // Give special trap frames priority 7 (BPDUs) and routing exceptions: + sw_w32_mask(0, 7 << 3 | 7, RTL838X_QM_PKT2CPU_INTPRI_2); + // Give RMA frames priority 7: + sw_w32_mask(0, 7, RTL838X_QM_PKT2CPU_INTPRI_1); +} + +void rtl839x_config_qos(void) +{ + int port, p, q; + u32 v; + struct rtl838x_switch_priv *priv = switch_priv; + + pr_info("Setting up RTL839X QoS\n"); + pr_info("RTL839X_PRI_SEL_TBL_CTRL(i): %08x\n", sw_r32(RTL839X_PRI_SEL_TBL_CTRL(0))); + rtl83xx_setup_default_prio2queue(); + + for (port = 0; port < soc_info.cpu_port; port++) + sw_w32(7, RTL839X_QM_PORT_QNUM(port)); + + // CPU-port gets queue number 7 + sw_w32(7, RTL839X_QM_PORT_QNUM(soc_info.cpu_port)); + + for (port = 0; port <= soc_info.cpu_port; port++) { + rtl83xx_set_ingress_priority(port, 0); + rtl839x_set_scheduling_algorithm(priv, port, WEIGHTED_FAIR_QUEUE); + rtl839x_set_scheduling_queue_weights(priv, port, default_queue_weights); + // Do re-marking based on outer tag + sw_w32_mask(0, BIT(port % 32), RTL839X_RMK_PORT_DEI_TAG_CTRL(port)); + } + + // Remap dot1p priorities to internal priority, for this the outer tag needs be re-marked + v = 0; + for (p = 0; p < 8; p++) + v |= (dot1p_priority_remapping[p] & 0x7) << (p * 3); + sw_w32(v, RTL839X_PRI_SEL_IPRI_REMAP); + + /* Configure Drop Precedence for Drop Eligible Indicator (DEI) + * Index 0: 0 + * Index 1: 2 + * Each indicator is 2 bits long + */ + sw_w32(2 << 2, RTL839X_PRI_SEL_DEI2DP_REMAP); + + // Re-mark DEI: 4 bit-fields of 2 bits each, field 0 is bits 0-1, ... + sw_w32((0x1 << 2) | (0x1 << 4), RTL839X_RMK_DEI_CTRL); + + /* Set Congestion avoidance drop probability to 0 for drop precedences 0-2 (bits 24-31) + * low threshold (bits 0-11) to 4095 and high threshold (bits 12-23) to 4095 + * Weighted Random Early Detection (WRED) is used + */ + sw_w32(4095 << 12| 4095, RTL839X_WRED_PORT_THR_CTRL(0)); + sw_w32(4095 << 12| 4095, RTL839X_WRED_PORT_THR_CTRL(1)); + sw_w32(4095 << 12| 4095, RTL839X_WRED_PORT_THR_CTRL(2)); + + /* Set queue-based congestion avoidance properties, register fields are as + * for forward RTL839X_WRED_PORT_THR_CTRL + */ + for (q = 0; q < 8; q++) { + sw_w32(255 << 24 | 78 << 12 | 68, RTL839X_WRED_QUEUE_THR_CTRL(q, 0)); + sw_w32(255 << 24 | 74 << 12 | 64, RTL839X_WRED_QUEUE_THR_CTRL(q, 0)); + sw_w32(255 << 24 | 70 << 12 | 60, RTL839X_WRED_QUEUE_THR_CTRL(q, 0)); + } +} + +void __init rtl83xx_setup_qos(struct rtl838x_switch_priv *priv) +{ + switch_priv = priv; + + pr_info("In %s\n", __func__); + + if (priv->family_id == RTL8380_FAMILY_ID) + return rtl838x_config_qos(); + else if (priv->family_id == RTL8390_FAMILY_ID) + return rtl839x_config_qos(); + + if (priv->family_id == RTL8380_FAMILY_ID) + rtl838x_rate_control_init(priv); + else if (priv->family_id == RTL8390_FAMILY_ID) + rtl839x_rate_control_init(priv); + +} diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c index c7b5873e99..de9e83bb8d 100644 --- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c +++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c @@ -5,25 +5,17 @@ extern struct mutex smi_lock; - -static inline void rtl838x_mask_port_reg(u64 clear, u64 set, int reg) +void rtl838x_print_matrix(void) { - sw_w32_mask((u32)clear, (u32)set, reg); -} + unsigned volatile int *ptr8; + int i; -static inline void rtl838x_set_port_reg(u64 set, int reg) -{ - sw_w32(set, reg); -} - -static inline u64 rtl838x_get_port_reg(int reg) -{ - return ((u64) sw_r32(reg)); -} - -static inline int rtl838x_stat_port_std_mib(int p) -{ - return RTL838X_STAT_PORT_STD_MIB + (p << 8); + ptr8 = RTL838X_SW_BASE + RTL838X_PORT_ISO_CTRL(0); + for (i = 0; i < 28; i += 8) + pr_info("> %8x %8x %8x %8x %8x %8x %8x %8x\n", + ptr8[i + 0], ptr8[i + 1], ptr8[i + 2], ptr8[i + 3], + ptr8[i + 4], ptr8[i + 5], ptr8[i + 6], ptr8[i + 7]); + pr_info("CPU_PORT> %8x\n", ptr8[28]); } static inline int rtl838x_port_iso_ctrl(int p) @@ -122,26 +114,16 @@ static inline int rtl838x_l2_port_new_sa_fwd(int p) return RTL838X_L2_PORT_NEW_SA_FWD(p); } -static inline int rtl838x_mir_ctrl(int group) -{ - return RTL838X_MIR_CTRL(group); -} - -static inline int rtl838x_mir_dpm(int group) -{ - return RTL838X_MIR_DPM_CTRL(group); -} - -static inline int rtl838x_mir_spm(int group) -{ - return RTL838X_MIR_SPM_CTRL(group); -} - static inline int rtl838x_mac_link_spd_sts(int p) { return RTL838X_MAC_LINK_SPD_STS(p); } +inline static int rtl838x_trk_mbr_ctr(int group) +{ + return RTL838X_TRK_MBR_CTR + (group << 2); +} + static u64 rtl838x_read_l2_entry_using_hash(u32 hash, u32 position, struct rtl838x_l2_entry *e) { u64 entry; @@ -235,14 +217,50 @@ static inline int rtl838x_vlan_port_igr_filter(int port) return RTL838X_VLAN_PORT_IGR_FLTR(port); } -static inline int rtl838x_vlan_port_pb(int port) +static void rtl838x_stp_get(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]) { - return RTL838X_VLAN_PORT_PB_VLAN(port); + int i; + u32 cmd = 1 << 15 /* Execute cmd */ + | 1 << 14 /* Read */ + | 2 << 12 /* Table type 0b10 */ + | (msti & 0xfff); + priv->r->exec_tbl0_cmd(cmd); + + for (i = 0; i < 2; i++) + port_state[i] = sw_r32(priv->r->tbl_access_data_0(i)); } -static inline int rtl838x_vlan_port_tag_sts_ctrl(int port) +static void rtl838x_stp_set(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]) { - return RTL838X_VLAN_PORT_TAG_STS_CTRL(port); + int i; + u32 cmd = 1 << 15 /* Execute cmd */ + | 0 << 14 /* Write */ + | 2 << 12 /* Table type 0b10 */ + | (msti & 0xfff); + + for (i = 0; i < 2; i++) + sw_w32(port_state[i], priv->r->tbl_access_data_0(i)); + priv->r->exec_tbl0_cmd(cmd); +} + +u64 rtl838x_traffic_get(int source) +{ + return rtl838x_get_port_reg(rtl838x_port_iso_ctrl(source)); +} + +void rtl838x_traffic_set(int source, u64 dest_matrix) +{ + rtl838x_set_port_reg(dest_matrix, rtl838x_port_iso_ctrl(source)); +} + +void rtl838x_traffic_enable(int source, int dest) +{ + rtl838x_mask_port_reg(0, BIT(dest), rtl838x_port_iso_ctrl(source)); +} + +void rtl838x_traffic_disable(int source, int dest) +{ + rtl838x_mask_port_reg(BIT(dest), 0, rtl838x_port_iso_ctrl(source)); } const struct rtl838x_reg rtl838x_reg = { @@ -254,8 +272,12 @@ const struct rtl838x_reg rtl838x_reg = { .get_port_reg_le = rtl838x_get_port_reg, .stat_port_rst = RTL838X_STAT_PORT_RST, .stat_rst = RTL838X_STAT_RST, - .stat_port_std_mib = rtl838x_stat_port_std_mib, + .stat_port_std_mib = RTL838X_STAT_PORT_STD_MIB, .port_iso_ctrl = rtl838x_port_iso_ctrl, + .traffic_enable = rtl838x_traffic_enable, + .traffic_disable = rtl838x_traffic_disable, + .traffic_get = rtl838x_traffic_get, + .traffic_set = rtl838x_traffic_set, .l2_ctrl_0 = RTL838X_L2_CTRL_0, .l2_ctrl_1 = RTL838X_L2_CTRL_1, .l2_port_aging_out = RTL838X_L2_PORT_AGING_OUT, @@ -272,12 +294,15 @@ const struct rtl838x_reg rtl838x_reg = { .vlan_set_tagged = rtl838x_vlan_set_tagged, .vlan_set_untagged = rtl838x_vlan_set_untagged, .mac_force_mode_ctrl = rtl838x_mac_force_mode_ctrl, + .vlan_profile_dump = rtl838x_vlan_profile_dump, + .stp_get = rtl838x_stp_get, + .stp_set = rtl838x_stp_set, .mac_port_ctrl = rtl838x_mac_port_ctrl, .l2_port_new_salrn = rtl838x_l2_port_new_salrn, .l2_port_new_sa_fwd = rtl838x_l2_port_new_sa_fwd, - .mir_ctrl = rtl838x_mir_ctrl, - .mir_dpm = rtl838x_mir_dpm, - .mir_spm = rtl838x_mir_spm, + .mir_ctrl = RTL838X_MIR_CTRL, + .mir_dpm = RTL838X_MIR_DPM_CTRL, + .mir_spm = RTL838X_MIR_SPM_CTRL, .mac_link_sts = RTL838X_MAC_LINK_STS, .mac_link_dup_sts = RTL838X_MAC_LINK_DUP_STS, .mac_link_spd_sts = rtl838x_mac_link_spd_sts, @@ -285,11 +310,13 @@ const struct rtl838x_reg rtl838x_reg = { .mac_tx_pause_sts = RTL838X_MAC_TX_PAUSE_STS, .read_l2_entry_using_hash = rtl838x_read_l2_entry_using_hash, .read_cam = rtl838x_read_cam, - .vlan_profile = rtl838x_vlan_profile, - .vlan_port_egr_filter = rtl838x_vlan_port_egr_filter, - .vlan_port_igr_filter = rtl838x_vlan_port_igr_filter, - .vlan_port_pb = rtl838x_vlan_port_pb, - .vlan_port_tag_sts_ctrl = rtl838x_vlan_port_tag_sts_ctrl, + .vlan_port_egr_filter = RTL838X_VLAN_PORT_EGR_FLTR, + .vlan_port_igr_filter = RTL838X_VLAN_PORT_IGR_FLTR(0), + .vlan_port_pb = RTL838X_VLAN_PORT_PB_VLAN, + .vlan_port_tag_sts_ctrl = RTL838X_VLAN_PORT_TAG_STS_CTRL, + .trk_mbr_ctr = rtl838x_trk_mbr_ctr, + .rma_bpdu_fld_pmask = RTL838X_RMA_BPDU_FLD_PMSK, + .spcl_trap_eapol_ctrl = RTL838X_SPCL_TRAP_EAPOL_CTRL, }; irqreturn_t rtl838x_switch_irq(int irq, void *dev_id) @@ -302,7 +329,7 @@ irqreturn_t rtl838x_switch_irq(int irq, void *dev_id) /* Clear status */ sw_w32(ports, RTL838X_ISR_PORT_LINK_STS_CHG); - pr_debug("RTL8380 Link change: status: %x, ports %x\n", status, ports); + pr_info("RTL8380 Link change: status: %x, ports %x\n", status, ports); for (i = 0; i < 28; i++) { if (ports & BIT(i)) { @@ -469,8 +496,37 @@ void rtl838x_vlan_profile_dump(int index) profile = sw_r32(RTL838X_VLAN_PROFILE(index)); - pr_debug("VLAN %d: L2 learning: %d, L2 Unknown MultiCast Field %x, \ + pr_info("VLAN %d: L2 learning: %d, L2 Unknown MultiCast Field %x, \ IPv4 Unknown MultiCast Field %x, IPv6 Unknown MultiCast Field: %x", index, profile & 1, (profile >> 1) & 0x1ff, (profile >> 10) & 0x1ff, (profile >> 19) & 0x1ff); } + +void rtl8380_sds_rst(int mac) +{ + u32 offset = (mac == 24) ? 0 : 0x100; + + sw_w32_mask(1 << 11, 0, RTL838X_SDS4_FIB_REG0 + offset); + sw_w32_mask(0x3, 0, RTL838X_SDS4_REG28 + offset); + sw_w32_mask(0x3, 0x3, RTL838X_SDS4_REG28 + offset); + sw_w32_mask(0, 0x1 << 6, RTL838X_SDS4_DUMMY0 + offset); + sw_w32_mask(0x1 << 6, 0, RTL838X_SDS4_DUMMY0 + offset); + pr_debug("SERDES reset: %d\n", mac); +} + +int rtl8380_sds_power(int mac, int val) +{ + u32 mode = (val == 1) ? 0x4 : 0x9; + u32 offset = (mac == 24) ? 5 : 0; + + if ((mac != 24) && (mac != 26)) { + pr_err("%s: not a fibre port: %d\n", __func__, mac); + return -1; + } + + sw_w32_mask(0x1f << offset, mode << offset, RTL838X_SDS_MODE_SEL); + + rtl8380_sds_rst(mac); + + return 0; +} diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.h b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.h index 1ebb4dff72..d5ca153a10 100644 --- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.h +++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.h @@ -8,14 +8,18 @@ /* * Register definition */ -#define RTL838X_CPU_PORT 28 -#define RTL839X_CPU_PORT 52 - #define RTL838X_MAC_PORT_CTRL(port) (0xd560 + (((port) << 7))) #define RTL839X_MAC_PORT_CTRL(port) (0x8004 + (((port) << 7))) +#define RTL930X_MAC_PORT_CTRL(port) (0x3260 + (((port) << 6))) +#define RTL930X_MAC_L2_PORT_CTRL(port) (0x3268 + (((port) << 6))) +#define RTL931X_MAC_PORT_CTRL(port) (0x6004 + (((port) << 7))) + #define RTL838X_RST_GLB_CTRL_0 (0x003c) + #define RTL838X_MAC_FORCE_MODE_CTRL (0xa104) #define RTL839X_MAC_FORCE_MODE_CTRL (0x02bc) +#define RTL930X_MAC_FORCE_MODE_CTRL (0xCA1C) +#define RTL931X_MAC_FORCE_MODE_CTRL (0x0DCC) #define RTL838X_DMY_REG31 (0x3b28) #define RTL838X_SDS_MODE_SEL (0x0028) @@ -23,20 +27,25 @@ #define RTL838X_INT_MODE_CTRL (0x005c) #define RTL838X_CHIP_INFO (0x00d8) #define RTL839X_CHIP_INFO (0x0ff4) -#define RTL838X_SDS4_REG28 (0xef80) -#define RTL838X_SDS4_DUMMY0 (0xef8c) -#define RTL838X_SDS5_EXT_REG6 (0xf18c) #define RTL838X_PORT_ISO_CTRL(port) (0x4100 + ((port) << 2)) #define RTL839X_PORT_ISO_CTRL(port) (0x1400 + ((port) << 3)) -#define RTL8380_SDS4_FIB_REG0 (0xF800) + +/* Packet statistics */ #define RTL838X_STAT_PORT_STD_MIB (0x1200) #define RTL839X_STAT_PORT_STD_MIB (0xC000) +#define RTL930X_STAT_PORT_MIB_CNTR (0x0664) #define RTL838X_STAT_RST (0x3100) #define RTL839X_STAT_RST (0xF504) +#define RTL930X_STAT_RST (0x3240) +#define RTL931X_STAT_RST (0x7ef4) #define RTL838X_STAT_PORT_RST (0x3104) #define RTL839X_STAT_PORT_RST (0xF508) +#define RTL930X_STAT_PORT_RST (0x3244) +#define RTL931X_STAT_PORT_RST (0x7ef8) #define RTL838X_STAT_CTRL (0x3108) #define RTL839X_STAT_CTRL (0x04cc) +#define RTL930X_STAT_CTRL (0x3248) +#define RTL931X_STAT_CTRL (0x5720) /* Registers of the internal Serdes of the 8390 */ #define RTL8390_SDS0_1_XSG0 (0xA000) @@ -47,53 +56,95 @@ #define RTL839X_SDS12_13_PWR1 (0xb980) /* Registers of the internal Serdes of the 8380 */ -#define MAPLE_SDS4_REG0r RTL838X_SDS4_REG28 -#define MAPLE_SDS5_REG0r (RTL838X_SDS4_REG28 + 0x100) -#define MAPLE_SDS4_REG3r RTL838X_SDS4_DUMMY0 -#define MAPLE_SDS5_REG3r (RTL838X_SDS4_REG28 + 0x100) -#define MAPLE_SDS4_FIB_REG0r (RTL838X_SDS4_REG28 + 0x880) -#define MAPLE_SDS5_FIB_REG0r (RTL838X_SDS4_REG28 + 0x980) +#define RTL838X_SDS4_FIB_REG0 (0xF800) +#define RTL838X_SDS4_REG28 (0xef80) +#define RTL838X_SDS4_DUMMY0 (0xef8c) +#define RTL838X_SDS5_EXT_REG6 (0xf18c) /* VLAN registers */ +#define RTL838X_VLAN_CTRL (0x3A74) #define RTL838X_VLAN_PROFILE(idx) (0x3A88 + ((idx) << 2)) #define RTL838X_VLAN_PORT_EGR_FLTR (0x3A84) -#define RTL838X_VLAN_PORT_PB_VLAN(port) (0x3C00 + ((port) << 2)) +#define RTL838X_VLAN_PORT_PB_VLAN (0x3C00) #define RTL838X_VLAN_PORT_IGR_FLTR(port) (0x3A7C + (((port >> 4) << 2))) #define RTL838X_VLAN_PORT_IGR_FLTR_0 (0x3A7C) #define RTL838X_VLAN_PORT_IGR_FLTR_1 (0x3A7C + 4) -#define RTL838X_VLAN_PORT_TAG_STS_CTRL(port) (0xA530 + (((port) << 2))) +#define RTL838X_VLAN_PORT_TAG_STS_CTRL (0xA530) + #define RTL839X_VLAN_PROFILE(idx) (0x25C0 + (((idx) << 3))) #define RTL839X_VLAN_CTRL (0x26D4) -#define RTL839X_VLAN_PORT_PB_VLAN(port) (0x26D8 + (((port) << 2))) +#define RTL839X_VLAN_PORT_PB_VLAN (0x26D8) #define RTL839X_VLAN_PORT_IGR_FLTR(port) (0x27B4 + (((port >> 4) << 2))) #define RTL839X_VLAN_PORT_EGR_FLTR(port) (0x27C4 + (((port >> 5) << 2))) -#define RTL839X_VLAN_PORT_TAG_STS_CTRL(port) (0x6828 + (((port) << 2))) +#define RTL839X_VLAN_PORT_TAG_STS_CTRL (0x6828) -/* Table 0/1 access registers */ +#define RTL930X_VLAN_PROFILE_SET(idx) (0x9c60 + (((idx) * 20))) +#define RTL930X_VLAN_CTRL (0x82D4) +#define RTL930X_VLAN_PORT_PB_VLAN (0x82D8) +#define RTL930X_VLAN_PORT_IGR_FLTR(port) (0x83C0 + (((port >> 4) << 2))) +#define RTL930X_VLAN_PORT_EGR_FLTR (0x83C8) +#define RTL930X_VLAN_PORT_TAG_STS_CTRL (0xCE24) + +#define RTL931X_VLAN_PROFILE_SET(idx) (0x9800 + (((idx) * 28))) +#define RTL931X_VLAN_CTRL (0x94E4) +#define RTL931X_VLAN_PORT_IGR_FLTR(port) (0x96B4 + (((port >> 4) << 2))) +#define RTL931X_VLAN_PORT_EGR_FLTR(port) (0x96C4 + (((port >> 5) << 2))) +#define RTL931X_VLAN_PORT_TAG_CTRL (0x4860) + +/* Table access registers */ #define RTL838X_TBL_ACCESS_CTRL_0 (0x6914) #define RTL838X_TBL_ACCESS_DATA_0(idx) (0x6918 + ((idx) << 2)) #define RTL838X_TBL_ACCESS_CTRL_1 (0xA4C8) #define RTL838X_TBL_ACCESS_DATA_1(idx) (0xA4CC + ((idx) << 2)) + #define RTL839X_TBL_ACCESS_CTRL_0 (0x1190) #define RTL839X_TBL_ACCESS_DATA_0(idx) (0x1194 + ((idx) << 2)) #define RTL839X_TBL_ACCESS_CTRL_1 (0x6b80) #define RTL839X_TBL_ACCESS_DATA_1(idx) (0x6b84 + ((idx) << 2)) +#define RTL839X_TBL_ACCESS_CTRL_2 (0x611C) +#define RTL839X_TBL_ACCESS_DATA_2(i) (0x6120 + (((i) << 2))) + +#define RTL930X_TBL_ACCESS_CTRL_0 (0xB340) +#define RTL930X_TBL_ACCESS_DATA_0(idx) (0xB344 + ((idx) << 2)) +#define RTL930X_TBL_ACCESS_CTRL_1 (0xB3A0) +#define RTL930X_TBL_ACCESS_DATA_1(idx) (0xB3A4 + ((idx) << 2)) +#define RTL930X_TBL_ACCESS_CTRL_2 (0xCE04) +#define RTL930X_TBL_ACCESS_DATA_2(i) (0xCE08 + (((i) << 2))) + +#define RTL931X_TBL_ACCESS_CTRL_0 (0x8500) +#define RTL931X_TBL_ACCESS_DATA_0(idx) (0x8508 + ((idx) << 2)) +#define RTL931X_TBL_ACCESS_CTRL_1 (0x40C0) +#define RTL931X_TBL_ACCESS_DATA_1(idx) (0x40C4 + ((idx) << 2)) +#define RTL931X_TBL_ACCESS_CTRL_2 (0x8528) +#define RTL931X_TBL_ACCESS_DATA_2(i) (0x852C + (((i) << 2))) +#define RTL931X_TBL_ACCESS_CTRL_3 (0x0200) +#define RTL931X_TBL_ACCESS_DATA_3(i) (0x0204 + (((i) << 2))) +#define RTL931X_TBL_ACCESS_CTRL_4 (0x20DC) +#define RTL931X_TBL_ACCESS_DATA_4(i) (0x20E0 + (((i) << 2))) +#define RTL931X_TBL_ACCESS_CTRL_5 (0x7E1C) +#define RTL931X_TBL_ACCESS_DATA_5(i) (0x7E20 + (((i) << 2))) /* MAC handling */ #define RTL838X_MAC_LINK_STS (0xa188) #define RTL839X_MAC_LINK_STS (0x0390) -#define RTL838X_MAC_LINK_SPD_STS(port) (0xa190 + (((port >> 4) << 2))) -#define RTL839X_MAC_LINK_SPD_STS(port) (0x03a0 + (((port >> 4) << 2))) +#define RTL930X_MAC_LINK_STS (0xCB10) +#define RTL931X_MAC_LINK_STS (0x0EC0) +#define RTL838X_MAC_LINK_SPD_STS(p) (0xa190 + (((p >> 4) << 2))) +#define RTL839X_MAC_LINK_SPD_STS(p) (0x03a0 + (((p >> 4) << 2))) +#define RTL930X_MAC_LINK_SPD_STS(p) (0xCB18 + (((p >> 3) << 2))) +#define RTL931X_MAC_LINK_SPD_STS(p) (0x0ED0 + (((p >> 3) << 2))) #define RTL838X_MAC_LINK_DUP_STS (0xa19c) #define RTL839X_MAC_LINK_DUP_STS (0x03b0) +#define RTL930X_MAC_LINK_DUP_STS (0xCB28) +#define RTL931X_MAC_LINK_DUP_STS (0x0EF0) #define RTL838X_MAC_TX_PAUSE_STS (0xa1a0) #define RTL839X_MAC_TX_PAUSE_STS (0x03b8) +#define RTL930X_MAC_TX_PAUSE_STS (0xCB2C) +#define RTL931X_MAC_TX_PAUSE_STS (0x0EF8) #define RTL838X_MAC_RX_PAUSE_STS (0xa1a4) #define RTL839X_MAC_RX_PAUSE_STS (0x03c0) -#define RTL838X_EEE_TX_TIMER_GIGA_CTRL (0xaa04) -#define RTL838X_EEE_TX_TIMER_GELITE_CTRL (0xaa08) - -#define RTL838X_DMA_IF_CTRL (0x9f58) +#define RTL930X_MAC_RX_PAUSE_STS (0xCB30) +#define RTL931X_MAC_RX_PAUSE_STS (0x0F00) /* MAC link state bits */ #define FORCE_EN (1 << 0) @@ -108,36 +159,71 @@ #define RTL838X_EEE_PORT_TX_EN (0x014c) #define RTL838X_EEE_PORT_RX_EN (0x0150) #define RTL838X_EEE_CLK_STOP_CTRL (0x0148) +#define RTL838X_EEE_TX_TIMER_GIGA_CTRL (0xaa04) +#define RTL838X_EEE_TX_TIMER_GELITE_CTRL (0xaa08) /* L2 functionality */ #define RTL838X_L2_CTRL_0 (0x3200) #define RTL839X_L2_CTRL_0 (0x3800) +#define RTL930X_L2_CTRL (0x8FD8) +#define RTL931X_L2_CTRL (0xC800) #define RTL838X_L2_CTRL_1 (0x3204) #define RTL839X_L2_CTRL_1 (0x3804) +#define RTL930X_L2_AGE_CTRL (0x8FDC) +#define RTL931X_L2_AGE_CTRL (0xC804) #define RTL838X_L2_PORT_AGING_OUT (0x3358) #define RTL839X_L2_PORT_AGING_OUT (0x3b74) +#define RTL930X_L2_PORT_AGE_CTRL (0x8FE0) +#define RTL931X_L2_PORT_AGE_CTRL (0xc808) #define RTL838X_TBL_ACCESS_L2_CTRL (0x6900) #define RTL839X_TBL_ACCESS_L2_CTRL (0x1180) +#define RTL930X_TBL_ACCESS_L2_CTRL (0xB320) +#define RTL930X_TBL_ACCESS_L2_METHOD_CTRL (0xB324) #define RTL838X_TBL_ACCESS_L2_DATA(idx) (0x6908 + ((idx) << 2)) #define RTL839X_TBL_ACCESS_L2_DATA(idx) (0x1184 + ((idx) << 2)) +#define RTL930X_TBL_ACCESS_L2_DATA(idx) (0xab08 + ((idx) << 2)) #define RTL838X_L2_TBL_FLUSH_CTRL (0x3370) #define RTL839X_L2_TBL_FLUSH_CTRL (0x3ba0) +#define RTL930X_L2_TBL_FLUSH_CTRL (0x9404) +#define RTL931X_L2_TBL_FLUSH_CTRL (0xCD9C) + #define RTL838X_L2_PORT_NEW_SALRN(p) (0x328c + (((p >> 4) << 2))) #define RTL839X_L2_PORT_NEW_SALRN(p) (0x38F0 + (((p >> 4) << 2))) +#define RTL930X_L2_PORT_SALRN(p) (0x8FEC + (((p >> 4) << 2))) +#define RTL931X_L2_PORT_NEW_SALRN(p) (0xC820 + (((p >> 4) << 2))) #define RTL838X_L2_PORT_NEW_SA_FWD(p) (0x3294 + (((p >> 4) << 2))) #define RTL839X_L2_PORT_NEW_SA_FWD(p) (0x3900 + (((p >> 4) << 2))) -#define RTL838X_L2_PORT_SALRN(p) (0x328c + (((p >> 4) << 2))) -#define RTL839X_L2_PORT_SALRN(p) (0x38F0 + (((p >> 4) << 2))) +#define RTL930X_L2_PORT_NEW_SA_FWD(p) (0x8FF4 + (((p / 10) << 2))) +#define RTL931X_L2_PORT_NEW_SA_FWD(p) (0xC830 + (((p / 10) << 2))) + +#define RTL930X_ST_CTRL (0x8798) + +#define RTL930X_L2_PORT_SABLK_CTRL (0x905c) +#define RTL930X_L2_PORT_DABLK_CTRL (0x9060) + +#define RTL838X_RMA_BPDU_FLD_PMSK (0x4348) +#define RTL930X_RMA_BPDU_FLD_PMSK (0x9F18) +#define RTL931X_RMA_BPDU_FLD_PMSK (0x8950) +#define RTL839X_RMA_BPDU_FLD_PMSK (0x125C) /* Port Mirroring */ -#define RTL838X_MIR_CTRL(grp) (0x5D00 + (((grp) << 2))) -#define RTL838X_MIR_DPM_CTRL(grp) (0x5D20 + (((grp) << 2))) -#define RTL838X_MIR_SPM_CTRL(grp) (0x5D10 + (((grp) << 2))) -#define RTL839X_MIR_CTRL(grp) (0x2500 + (((grp) << 2))) -#define RTL839X_MIR_DPM_CTRL(grp) (0x2530 + (((grp) << 2))) -#define RTL839X_MIR_SPM_CTRL(grp) (0x2510 + (((grp) << 2))) +#define RTL838X_MIR_CTRL (0x5D00) +#define RTL838X_MIR_DPM_CTRL (0x5D20) +#define RTL838X_MIR_SPM_CTRL (0x5D10) -/* Storm control */ +#define RTL839X_MIR_CTRL (0x2500) +#define RTL839X_MIR_DPM_CTRL (0x2530) +#define RTL839X_MIR_SPM_CTRL (0x2510) + +#define RTL930X_MIR_CTRL (0xA2A0) +#define RTL930X_MIR_DPM_CTRL (0xA2C0) +#define RTL930X_MIR_SPM_CTRL (0xA2B0) + +#define RTL931X_MIR_CTRL (0xAF00) +#define RTL931X_MIR_DPM_CTRL (0xAF30) +#define RTL931X_MIR_SPM_CTRL (0xAF10) + +/* Storm/rate control and scheduling */ #define RTL838X_STORM_CTRL (0x4700) #define RTL839X_STORM_CTRL (0x1800) #define RTL838X_STORM_CTRL_LB_CTRL(p) (0x4884 + (((p) << 2))) @@ -145,12 +231,23 @@ #define RTL838X_STORM_CTRL_BURST_PPS_1 (0x4878) #define RTL838X_STORM_CTRL_BURST_0 (0x487c) #define RTL838X_STORM_CTRL_BURST_1 (0x4880) +#define RTL839X_STORM_CTRL_LB_TICK_TKN_CTRL_0 (0x1804) +#define RTL839X_STORM_CTRL_LB_TICK_TKN_CTRL_1 (0x1808) #define RTL838X_SCHED_CTRL (0xB980) +#define RTL839X_SCHED_CTRL (0x60F4) #define RTL838X_SCHED_LB_TICK_TKN_CTRL_0 (0xAD58) #define RTL838X_SCHED_LB_TICK_TKN_CTRL_1 (0xAD5C) #define RTL839X_SCHED_LB_TICK_TKN_CTRL_0 (0x1804) #define RTL839X_SCHED_LB_TICK_TKN_CTRL_1 (0x1808) +#define RTL839X_STORM_CTRL_SPCL_LB_TICK_TKN_CTRL (0x2000) +#define RTL839X_IGR_BWCTRL_LB_TICK_TKN_CTRL_0 (0x1604) +#define RTL839X_IGR_BWCTRL_LB_TICK_TKN_CTRL_1 (0x1608) +#define RTL839X_SCHED_LB_TICK_TKN_CTRL (0x60F8) +#define RTL839X_SCHED_LB_TICK_TKN_PPS_CTRL (0x6200) #define RTL838X_SCHED_LB_THR (0xB984) +#define RTL839X_SCHED_LB_THR (0x60FC) +#define RTL838X_SCHED_P_EGR_RATE_CTRL(p) (0xC008 + (((p) << 7))) +#define RTL838X_SCHED_Q_EGR_RATE_CTRL(p, q) (0xC00C + (p << 7) + (((q) << 2))) #define RTL838X_STORM_CTRL_PORT_BC_EXCEED (0x470C) #define RTL838X_STORM_CTRL_PORT_MC_EXCEED (0x4710) #define RTL838X_STORM_CTRL_PORT_UC_EXCEED (0x4714) @@ -160,6 +257,25 @@ #define RTL838X_STORM_CTRL_PORT_UC(p) (0x4718 + (((p) << 2))) #define RTL838X_STORM_CTRL_PORT_MC(p) (0x478c + (((p) << 2))) #define RTL838X_STORM_CTRL_PORT_BC(p) (0x4800 + (((p) << 2))) +#define RTL839X_STORM_CTRL_PORT_UC_0(p) (0x185C + (((p) << 3))) +#define RTL839X_STORM_CTRL_PORT_UC_1(p) (0x1860 + (((p) << 3))) +#define RTL839X_STORM_CTRL_PORT_MC_0(p) (0x19FC + (((p) << 3))) +#define RTL839X_STORM_CTRL_PORT_MC_1(p) (0x1a00 + (((p) << 3))) +#define RTL839X_STORM_CTRL_PORT_BC_0(p) (0x1B9C + (((p) << 3))) +#define RTL839X_STORM_CTRL_PORT_BC_1(p) (0x1BA0 + (((p) << 3))) +#define RTL839X_TBL_ACCESS_CTRL_2 (0x611C) +#define RTL839X_TBL_ACCESS_DATA_2(i) (0x6120 + (((i) << 2))) +#define RTL839X_IGR_BWCTRL_PORT_CTRL_10G_0(p) (0x1618 + (((p) << 3))) +#define RTL839X_IGR_BWCTRL_PORT_CTRL_10G_1(p) (0x161C + (((p) << 3))) +#define RTL839X_IGR_BWCTRL_PORT_CTRL_0(p) (0x1640 + (((p) << 3))) +#define RTL839X_IGR_BWCTRL_PORT_CTRL_1(p) (0x1644 + (((p) << 3))) +#define RTL839X_IGR_BWCTRL_CTRL_LB_THR (0x1614) + +/* Link aggregation (Trunking) */ +#define RTL839X_TRK_MBR_CTR (0x2200) +#define RTL838X_TRK_MBR_CTR (0x3E00) +#define RTL930X_TRK_MBR_CTRL (0xA41C) +#define RTL931X_TRK_MBR_CTRL (0xB8D0) /* Attack prevention */ #define RTL838X_ATK_PRVNT_PORT_EN (0x5B00) @@ -167,6 +283,48 @@ #define RTL838X_ATK_PRVNT_ACT (0x5B08) #define RTL838X_ATK_PRVNT_STS (0x5B1C) +/* 802.1X */ +#define RTL838X_SPCL_TRAP_EAPOL_CTRL (0x6988) +#define RTL839X_SPCL_TRAP_EAPOL_CTRL (0x105C) + +/* QoS */ +#define RTL838X_QM_INTPRI2QID_CTRL (0x5F00) +#define RTL839X_QM_INTPRI2QID_CTRL(q) (0x1110 + (q << 2)) +#define RTL839X_QM_PORT_QNUM(p) (0x1130 + (((p / 10) << 2))) +#define RTL838X_PRI_SEL_PORT_PRI(p) (0x5FB8 + (((p / 10) << 2))) +#define RTL839X_PRI_SEL_PORT_PRI(p) (0x10A8 + (((p / 10) << 2))) +#define RTL838X_QM_PKT2CPU_INTPRI_MAP (0x5F10) +#define RTL839X_QM_PKT2CPU_INTPRI_MAP (0x1154) +#define RTL838X_PRI_SEL_CTRL (0x10E0) +#define RTL839X_PRI_SEL_CTRL (0x10E0) +#define RTL838X_PRI_SEL_TBL_CTRL(i) (0x5FD8 + (((i) << 2))) +#define RTL839X_PRI_SEL_TBL_CTRL(i) (0x10D0 + (((i) << 2))) +#define RTL838X_QM_PKT2CPU_INTPRI_0 (0x5F04) +#define RTL838X_QM_PKT2CPU_INTPRI_1 (0x5F08) +#define RTL838X_QM_PKT2CPU_INTPRI_2 (0x5F0C) +#define RTL839X_OAM_CTRL (0x2100) +#define RTL839X_OAM_PORT_ACT_CTRL(p) (0x2104 + (((p) << 2))) +#define RTL839X_RMK_PORT_DEI_TAG_CTRL(p) (0x6A9C + (((p >> 5) << 2))) +#define RTL839X_PRI_SEL_IPRI_REMAP (0x1080) +#define RTL838X_PRI_SEL_IPRI_REMAP (0x5F8C) +#define RTL839X_PRI_SEL_DEI2DP_REMAP (0x10EC) +#define RTL839X_PRI_SEL_DSCP2DP_REMAP_ADDR(i) (0x10F0 + (((i >> 4) << 2))) +#define RTL839X_RMK_DEI_CTRL (0x6AA4) +#define RTL839X_WRED_PORT_THR_CTRL(i) (0x6084 + ((i) << 2)) +#define RTL839X_WRED_QUEUE_THR_CTRL(q, i) (0x6090 + ((q) * 12) + ((i) << 2)) +#define RTL838X_PRI_DSCP_INVLD_CTRL0 (0x5FE8) +#define RTL838X_RMK_IPRI_CTRL (0xA460) +#define RTL838X_RMK_OPRI_CTRL (0xA464) +#define RTL838X_SCHED_P_TYPE_CTRL(p) (0xC04C + (((p) << 7))) +#define RTL838X_SCHED_LB_CTRL(p) (0xC004 + (((p) << 7))) +#define RTL838X_FC_P_EGR_DROP_CTRL(p) (0x6B1C + (((p) << 2))) + +/* Debug features */ +#define RTL930X_STAT_PRVTE_DROP_COUNTER0 (0xB5B8) + +#define MAX_LAGS 16 +#define MAX_PRIOS 8 + enum phy_type { PHY_NONE = 0, PHY_RTL838X_SDS = 1, @@ -182,6 +340,9 @@ struct rtl838x_port { u16 pvid; bool eee_enabled; enum phy_type phy; + bool is10G; + bool is2G5; + u8 sds_num; const struct dsa_port *dp; }; @@ -217,6 +378,8 @@ struct rtl838x_l2_entry { bool suspended; bool next_hop; int age; + u8 trunk; + u8 stackDev; u16 mc_portmask_index; }; @@ -231,8 +394,12 @@ struct rtl838x_reg { u64 (*get_port_reg_le)(int reg); int stat_port_rst; int stat_rst; - int (*stat_port_std_mib)(int p); + int stat_port_std_mib; int (*port_iso_ctrl)(int p); + void (*traffic_enable)(int source, int dest); + void (*traffic_disable)(int source, int dest); + void (*traffic_set)(int source, u64 dest_matrix); + u64 (*traffic_get)(int source); int l2_ctrl_0; int l2_ctrl_1; int l2_port_aging_out; @@ -248,13 +415,16 @@ struct rtl838x_reg { void (*vlan_tables_read)(u32 vlan, struct rtl838x_vlan_info *info); void (*vlan_set_tagged)(u32 vlan, struct rtl838x_vlan_info *info); void (*vlan_set_untagged)(u32 vlan, u64 portmask); + void (*vlan_profile_dump)(int index); + void (*stp_get)(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]); + void (*stp_set)(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]); int (*mac_force_mode_ctrl)(int port); int (*mac_port_ctrl)(int port); int (*l2_port_new_salrn)(int port); int (*l2_port_new_sa_fwd)(int port); - int (*mir_ctrl)(int group); - int (*mir_dpm)(int group); - int (*mir_spm)(int group); + int mir_ctrl; + int mir_dpm; + int mir_spm; int mac_link_sts; int mac_link_dup_sts; int (*mac_link_spd_sts)(int port); @@ -262,11 +432,14 @@ struct rtl838x_reg { int mac_tx_pause_sts; u64 (*read_l2_entry_using_hash)(u32 hash, u32 position, struct rtl838x_l2_entry *e); u64 (*read_cam)(int idx, struct rtl838x_l2_entry *e); - int (*vlan_profile)(int profile); - int (*vlan_port_egr_filter)(int port); - int (*vlan_port_igr_filter)(int port); - int (*vlan_port_pb)(int port); - int (*vlan_port_tag_sts_ctrl)(int port); + int vlan_port_egr_filter; + int vlan_port_igr_filter; + int vlan_port_pb; + int vlan_port_tag_sts_ctrl; + int (*rtl838x_vlan_port_tag_sts_ctrl)(int port); + int (*trk_mbr_ctr)(int group); + int rma_bpdu_fld_pmask; + int spcl_trap_eapol_ctrl; }; struct rtl838x_switch_priv { @@ -276,7 +449,7 @@ struct rtl838x_switch_priv { u16 id; u16 family_id; char version; - struct rtl838x_port ports[54]; /* TODO: correct size! */ + struct rtl838x_port ports[57]; struct mutex reg_mutex; int link_state_irq; int mirror_group_ports[4]; @@ -284,8 +457,14 @@ struct rtl838x_switch_priv { const struct rtl838x_reg *r; u8 cpu_port; u8 port_mask; + u8 port_width; + u64 irq_mask; u32 fib_entries; struct dentry *dbgfs_dir; + int n_lags; + u64 lags_port_members[MAX_LAGS]; + struct net_device *lag_devs[MAX_LAGS]; + struct notifier_block nb; }; void rtl838x_dbgfs_init(struct rtl838x_switch_priv *priv); diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl839x.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl839x.c index 8dd123f609..5106bd2e9d 100644 --- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl839x.c +++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl839x.c @@ -4,53 +4,18 @@ #include "rtl83xx.h" extern struct mutex smi_lock; +extern struct rtl83xx_soc_info soc_info; - -static inline void rtl839x_mask_port_reg_be(u64 clear, u64 set, int reg) +void rtl839x_print_matrix(void) { - sw_w32_mask((u32)(clear >> 32), (u32)(set >> 32), reg); - sw_w32_mask((u32)(clear & 0xffffffff), (u32)(set & 0xffffffff), reg + 4); -} + volatile u64 *ptr9; + int i; -static inline u64 rtl839x_get_port_reg_be(int reg) -{ - u64 v = sw_r32(reg); - - v <<= 32; - v |= sw_r32(reg + 4); - return v; -} - -static inline void rtl839x_set_port_reg_be(u64 set, int reg) -{ - sw_w32(set >> 32, reg); - sw_w32(set & 0xffffffff, reg + 4); -} - -static inline void rtl839x_mask_port_reg_le(u64 clear, u64 set, int reg) -{ - sw_w32_mask((u32)clear, (u32)set, reg); - sw_w32_mask((u32)(clear >> 32), (u32)(set >> 32), reg + 4); -} - -static inline void rtl839x_set_port_reg_le(u64 set, int reg) -{ - sw_w32(set, reg); - sw_w32(set >> 32, reg + 4); -} - -static inline u64 rtl839x_get_port_reg_le(int reg) -{ - u64 v = sw_r32(reg + 4); - - v <<= 32; - v |= sw_r32(reg); - return v; -} - -static inline int rtl839x_stat_port_std_mib(int p) -{ - return RTL839X_STAT_PORT_STD_MIB + (p << 8); + ptr9 = RTL838X_SW_BASE + RTL839X_PORT_ISO_CTRL(0); + for (i = 0; i < 52; i += 4) + pr_debug("> %16llx %16llx %16llx %16llx\n", + ptr9[i + 0], ptr9[i + 1], ptr9[i + 2], ptr9[i + 3]); + pr_debug("CPU_PORT> %16llx\n", ptr9[52]); } static inline int rtl839x_port_iso_ctrl(int p) @@ -70,6 +35,12 @@ static inline void rtl839x_exec_tbl1_cmd(u32 cmd) do { } while (sw_r32(RTL839X_TBL_ACCESS_CTRL_1) & BIT(16)); } +inline void rtl839x_exec_tbl2_cmd(u32 cmd) +{ + sw_w32(cmd, RTL839X_TBL_ACCESS_CTRL_2); + do { } while (sw_r32(RTL839X_TBL_ACCESS_CTRL_2) & (1 << 9)); +} + static inline int rtl839x_tbl_access_data_0(int i) { return RTL839X_TBL_ACCESS_DATA_0(i); @@ -81,33 +52,33 @@ static void rtl839x_vlan_tables_read(u32 vlan, struct rtl838x_vlan_info *info) u64 v; u32 u, w; - cmd = BIT(16) /* Execute cmd */ + cmd = 1 << 16 /* Execute cmd */ | 0 << 15 /* Read */ | 0 << 12 /* Table type 0b000 */ | (vlan & 0xfff); rtl839x_exec_tbl0_cmd(cmd); - v = sw_r32(RTL838X_TBL_ACCESS_DATA_0(0)); + v = sw_r32(RTL839X_TBL_ACCESS_DATA_0(0)); v <<= 32; - u = sw_r32(RTL838X_TBL_ACCESS_DATA_0(1)); + u = sw_r32(RTL839X_TBL_ACCESS_DATA_0(1)); v |= u; info->tagged_ports = v >> 11; - w = sw_r32(RTL838X_TBL_ACCESS_DATA_0(2)); + w = sw_r32(RTL839X_TBL_ACCESS_DATA_0(2)); info->profile_id = w >> 30 | ((u & 1) << 2); info->hash_mc_fid = !!(u & 2); info->hash_uc_fid = !!(u & 4); info->fid = (u >> 3) & 0xff; - cmd = BIT(16) /* Execute cmd */ - | 0 << 15 /* Read */ - | 0 << 12 /* Table type 0b000 */ + cmd = 1 << 15 /* Execute cmd */ + | 0 << 14 /* Read */ + | 0 << 12 /* Table type 0b00 */ | (vlan & 0xfff); rtl839x_exec_tbl1_cmd(cmd); - v = sw_r32(RTL838X_TBL_ACCESS_DATA_1(0)); + v = sw_r32(RTL839X_TBL_ACCESS_DATA_1(0)); v <<= 32; - v |= sw_r32(RTL838X_TBL_ACCESS_DATA_1(1)); + v |= sw_r32(RTL839X_TBL_ACCESS_DATA_1(1)); info->untagged_ports = v >> 11; } @@ -161,45 +132,18 @@ static inline int rtl839x_l2_port_new_sa_fwd(int p) return RTL839X_L2_PORT_NEW_SA_FWD(p); } -static inline int rtl839x_mir_ctrl(int group) -{ - return RTL839X_MIR_CTRL(group); -} - -static inline int rtl839x_mir_dpm(int group) -{ - return RTL839X_MIR_DPM_CTRL(group); -} - -static inline int rtl839x_mir_spm(int group) -{ - return RTL839X_MIR_SPM_CTRL(group); -} - static inline int rtl839x_mac_link_spd_sts(int p) { return RTL839X_MAC_LINK_SPD_STS(p); } -static u64 rtl839x_read_l2_entry_using_hash(u32 hash, u32 position, struct rtl838x_l2_entry *e) +static inline int rtl839x_trk_mbr_ctr(int group) { - u64 entry; - u32 r[3]; - - /* Search in SRAM, with hash and at position in hash bucket (0-3) */ - u32 idx = (0 << 14) | (hash << 2) | position; - - u32 cmd = BIT(17) /* Execute cmd */ - | 0 << 16 /* Read */ - | 0 << 14 /* Table type 0b00 */ - | (idx & 0x3fff); - - sw_w32(cmd, RTL839X_TBL_ACCESS_L2_CTRL); - do { } while (sw_r32(RTL839X_TBL_ACCESS_L2_CTRL) & BIT(17)); - r[0] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(0)); - r[1] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(1)); - r[2] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(2)); + return RTL839X_TRK_MBR_CTR + (group << 3); +} +static void rtl839x_fill_l2_entry(u32 r[], struct rtl838x_l2_entry *e) +{ /* Table contains different entry types, we need to identify the right one: * Check for MC entries, first */ @@ -220,12 +164,12 @@ static u64 rtl839x_read_l2_entry_using_hash(u32 hash, u32 position, struct rtl83 e->vid = (r[2] >> 4) & 0xfff; e->rvid = (r[0] >> 20) & 0xfff; e->port = (r[2] >> 24) & 0x3f; - e->block_da = !!(r[2] & BIT(19)); - e->block_sa = !!(r[2] & BIT(20)); - e->suspended = !!(r[2] & BIT(17)); - e->next_hop = !!(r[2] & BIT(16)); + e->block_da = !!(r[2] & (1 << 19)); + e->block_sa = !!(r[2] & (1 << 20)); + e->suspended = !!(r[2] & (1 << 17)); + e->next_hop = !!(r[2] & (1 << 16)); if (e->next_hop) - pr_debug("Found next hop entry, need to read data\n"); + pr_info("Found next hop entry, need to read data\n"); e->age = (r[2] >> 21) & 3; e->valid = true; if (!(r[2] & 0xc0fd0000)) /* Check for valid entry */ @@ -246,6 +190,28 @@ static u64 rtl839x_read_l2_entry_using_hash(u32 hash, u32 position, struct rtl83 e->valid = true; e->type = IP6_MULTICAST; } +} + +static u64 rtl839x_read_l2_entry_using_hash(u32 hash, u32 position, struct rtl838x_l2_entry *e) +{ + u64 entry; + u32 r[3]; + + /* Search in SRAM, with hash and at position in hash bucket (0-3) */ + u32 idx = (0 << 14) | (hash << 2) | position; + + u32 cmd = 1 << 17 /* Execute cmd */ + | 0 << 16 /* Read */ + | 0 << 14 /* Table type 0b00 */ + | (idx & 0x3fff); + + sw_w32(cmd, RTL839X_TBL_ACCESS_L2_CTRL); + do { } while (sw_r32(RTL839X_TBL_ACCESS_L2_CTRL) & (1 << 17)); + r[0] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(0)); + r[1] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(1)); + r[2] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(2)); + + rtl839x_fill_l2_entry(r, e); entry = (((u64) r[0]) << 12) | ((r[1] & 0xfffffff0) << 12) | ((r[2] >> 4) & 0xfff); return entry; @@ -256,33 +222,22 @@ static u64 rtl839x_read_cam(int idx, struct rtl838x_l2_entry *e) u64 entry; u32 r[3]; - u32 cmd = BIT(17) /* Execute cmd */ + u32 cmd = 1 << 17 /* Execute cmd */ | 0 << 16 /* Read */ - | BIT(14) /* Table type 0b01 */ + | 1 << 14 /* Table type 0b01 */ | (idx & 0x3f); sw_w32(cmd, RTL839X_TBL_ACCESS_L2_CTRL); - do { } while (sw_r32(RTL839X_TBL_ACCESS_L2_CTRL) & BIT(17)); + do { } while (sw_r32(RTL839X_TBL_ACCESS_L2_CTRL) & (1 << 17)); r[0] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(0)); r[1] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(1)); r[2] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(2)); - e->mac[0] = (r[0] >> 12); - e->mac[1] = (r[0] >> 4); - e->mac[2] = ((r[1] >> 28) | (r[0] << 4)); - e->mac[3] = (r[1] >> 20); - e->mac[4] = (r[1] >> 12); - e->mac[5] = (r[1] >> 4); - e->is_static = !!((r[2] >> 18) & 1); - e->vid = (r[2] >> 4) & 0xfff; - e->rvid = (r[0] >> 20) & 0xfff; - e->port = (r[2] >> 24) & 0x3f; - - e->valid = true; - if (!(r[2] & 0x10fd0000)) /* Check for invalid entry */ - e->valid = false; + rtl839x_fill_l2_entry(r, e); if (e->valid) - pr_debug("Found in CAM: R1 %x R2 %x R3 %x\n", r[0], r[1], r[2]); + pr_info("Found in CAM: R1 %x R2 %x R3 %x\n", r[0], r[1], r[2]); + else + return 0; entry = (((u64) r[0]) << 12) | ((r[1] & 0xfffffff0) << 12) | ((r[2] >> 4) & 0xfff); return entry; @@ -303,62 +258,25 @@ static inline int rtl839x_vlan_port_igr_filter(int port) return RTL839X_VLAN_PORT_IGR_FLTR(port); } -static inline int rtl839x_vlan_port_pb(int port) +u64 rtl839x_traffic_get(int source) { - return RTL839X_VLAN_PORT_PB_VLAN(port); + return rtl839x_get_port_reg_be(rtl839x_port_iso_ctrl(source)); } -static inline int rtl839x_vlan_port_tag_sts_ctrl(int port) +void rtl839x_traffic_set(int source, u64 dest_matrix) { - return RTL839X_VLAN_PORT_TAG_STS_CTRL(port); + rtl839x_set_port_reg_be(dest_matrix, rtl839x_port_iso_ctrl(source)); } -const struct rtl838x_reg rtl839x_reg = { - .mask_port_reg_be = rtl839x_mask_port_reg_be, - .set_port_reg_be = rtl839x_set_port_reg_be, - .get_port_reg_be = rtl839x_get_port_reg_be, - .mask_port_reg_le = rtl839x_mask_port_reg_le, - .set_port_reg_le = rtl839x_set_port_reg_le, - .get_port_reg_le = rtl839x_get_port_reg_le, - .stat_port_rst = RTL839X_STAT_PORT_RST, - .stat_rst = RTL839X_STAT_RST, - .stat_port_std_mib = rtl839x_stat_port_std_mib, - .port_iso_ctrl = rtl839x_port_iso_ctrl, - .l2_ctrl_0 = RTL839X_L2_CTRL_0, - .l2_ctrl_1 = RTL839X_L2_CTRL_1, - .l2_port_aging_out = RTL839X_L2_PORT_AGING_OUT, - .smi_poll_ctrl = RTL839X_SMI_PORT_POLLING_CTRL, - .l2_tbl_flush_ctrl = RTL839X_L2_TBL_FLUSH_CTRL, - .exec_tbl0_cmd = rtl839x_exec_tbl0_cmd, - .exec_tbl1_cmd = rtl839x_exec_tbl1_cmd, - .tbl_access_data_0 = rtl839x_tbl_access_data_0, - .isr_glb_src = RTL839X_ISR_GLB_SRC, - .isr_port_link_sts_chg = RTL839X_ISR_PORT_LINK_STS_CHG, - .imr_port_link_sts_chg = RTL839X_IMR_PORT_LINK_STS_CHG, - .imr_glb = RTL839X_IMR_GLB, - .vlan_tables_read = rtl839x_vlan_tables_read, - .vlan_set_tagged = rtl839x_vlan_set_tagged, - .vlan_set_untagged = rtl839x_vlan_set_untagged, - .mac_force_mode_ctrl = rtl839x_mac_force_mode_ctrl, - .mac_port_ctrl = rtl839x_mac_port_ctrl, - .l2_port_new_salrn = rtl839x_l2_port_new_salrn, - .l2_port_new_sa_fwd = rtl839x_l2_port_new_sa_fwd, - .mir_ctrl = rtl839x_mir_ctrl, - .mir_dpm = rtl839x_mir_dpm, - .mir_spm = rtl839x_mir_spm, - .mac_link_sts = RTL839X_MAC_LINK_STS, - .mac_link_dup_sts = RTL839X_MAC_LINK_DUP_STS, - .mac_link_spd_sts = rtl839x_mac_link_spd_sts, - .mac_rx_pause_sts = RTL839X_MAC_RX_PAUSE_STS, - .mac_tx_pause_sts = RTL839X_MAC_TX_PAUSE_STS, - .read_l2_entry_using_hash = rtl839x_read_l2_entry_using_hash, - .read_cam = rtl839x_read_cam, - .vlan_profile = rtl839x_vlan_profile, - .vlan_port_egr_filter = rtl839x_vlan_port_egr_filter, - .vlan_port_igr_filter = rtl839x_vlan_port_igr_filter, - .vlan_port_pb = rtl839x_vlan_port_pb, - .vlan_port_tag_sts_ctrl = rtl839x_vlan_port_tag_sts_ctrl, -}; +void rtl839x_traffic_enable(int source, int dest) +{ + rtl839x_mask_port_reg_be(0, BIT_ULL(dest), rtl839x_port_iso_ctrl(source)); +} + +void rtl839x_traffic_disable(int source, int dest) +{ + rtl839x_mask_port_reg_be(BIT(dest), 0, rtl839x_port_iso_ctrl(source)); +} irqreturn_t rtl839x_switch_irq(int irq, void *dev_id) { @@ -512,3 +430,84 @@ void rtl839x_vlan_profile_dump(int index) index, profile & 1, (profile >> 1) & 0xfff, (profile >> 13) & 0xfff, (profile1) & 0xfff); } + +static void rtl839x_stp_get(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]) +{ + int i; + u32 cmd = 1 << 16 /* Execute cmd */ + | 0 << 15 /* Read */ + | 5 << 12 /* Table type 0b101 */ + | (msti & 0xfff); + priv->r->exec_tbl0_cmd(cmd); + + for (i = 0; i < 4; i++) + port_state[i] = sw_r32(priv->r->tbl_access_data_0(i)); +} + +static void rtl839x_stp_set(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]) +{ + int i; + u32 cmd = 1 << 16 /* Execute cmd */ + | 1 << 15 /* Write */ + | 5 << 12 /* Table type 0b101 */ + | (msti & 0xfff); + for (i = 0; i < 4; i++) + sw_w32(port_state[i], priv->r->tbl_access_data_0(i)); + priv->r->exec_tbl0_cmd(cmd); +} + +const struct rtl838x_reg rtl839x_reg = { + .mask_port_reg_be = rtl839x_mask_port_reg_be, + .set_port_reg_be = rtl839x_set_port_reg_be, + .get_port_reg_be = rtl839x_get_port_reg_be, + .mask_port_reg_le = rtl839x_mask_port_reg_le, + .set_port_reg_le = rtl839x_set_port_reg_le, + .get_port_reg_le = rtl839x_get_port_reg_le, + .stat_port_rst = RTL839X_STAT_PORT_RST, + .stat_rst = RTL839X_STAT_RST, + .stat_port_std_mib = RTL839X_STAT_PORT_STD_MIB, + .traffic_enable = rtl839x_traffic_enable, + .traffic_disable = rtl839x_traffic_disable, + .traffic_get = rtl839x_traffic_get, + .traffic_set = rtl839x_traffic_set, + .port_iso_ctrl = rtl839x_port_iso_ctrl, + .l2_ctrl_0 = RTL839X_L2_CTRL_0, + .l2_ctrl_1 = RTL839X_L2_CTRL_1, + .l2_port_aging_out = RTL839X_L2_PORT_AGING_OUT, + .smi_poll_ctrl = RTL839X_SMI_PORT_POLLING_CTRL, + .l2_tbl_flush_ctrl = RTL839X_L2_TBL_FLUSH_CTRL, + .exec_tbl0_cmd = rtl839x_exec_tbl0_cmd, + .exec_tbl1_cmd = rtl839x_exec_tbl1_cmd, + .tbl_access_data_0 = rtl839x_tbl_access_data_0, + .isr_glb_src = RTL839X_ISR_GLB_SRC, + .isr_port_link_sts_chg = RTL839X_ISR_PORT_LINK_STS_CHG, + .imr_port_link_sts_chg = RTL839X_IMR_PORT_LINK_STS_CHG, + .imr_glb = RTL839X_IMR_GLB, + .vlan_tables_read = rtl839x_vlan_tables_read, + .vlan_set_tagged = rtl839x_vlan_set_tagged, + .vlan_set_untagged = rtl839x_vlan_set_untagged, + .vlan_profile_dump = rtl839x_vlan_profile_dump, + .stp_get = rtl839x_stp_get, + .stp_set = rtl839x_stp_set, + .mac_force_mode_ctrl = rtl839x_mac_force_mode_ctrl, + .mac_port_ctrl = rtl839x_mac_port_ctrl, + .l2_port_new_salrn = rtl839x_l2_port_new_salrn, + .l2_port_new_sa_fwd = rtl839x_l2_port_new_sa_fwd, + .mir_ctrl = RTL839X_MIR_CTRL, + .mir_dpm = RTL839X_MIR_DPM_CTRL, + .mir_spm = RTL839X_MIR_SPM_CTRL, + .mac_link_sts = RTL839X_MAC_LINK_STS, + .mac_link_dup_sts = RTL839X_MAC_LINK_DUP_STS, + .mac_link_spd_sts = rtl839x_mac_link_spd_sts, + .mac_rx_pause_sts = RTL839X_MAC_RX_PAUSE_STS, + .mac_tx_pause_sts = RTL839X_MAC_TX_PAUSE_STS, + .read_l2_entry_using_hash = rtl839x_read_l2_entry_using_hash, + .read_cam = rtl839x_read_cam, + .vlan_port_egr_filter = RTL839X_VLAN_PORT_EGR_FLTR(0), + .vlan_port_igr_filter = RTL839X_VLAN_PORT_IGR_FLTR(0), + .vlan_port_pb = RTL839X_VLAN_PORT_PB_VLAN, + .vlan_port_tag_sts_ctrl = RTL839X_VLAN_PORT_TAG_STS_CTRL, + .trk_mbr_ctr = rtl839x_trk_mbr_ctr, + .rma_bpdu_fld_pmask = RTL839X_RMA_BPDU_FLD_PMSK, + .spcl_trap_eapol_ctrl = RTL839X_SPCL_TRAP_EAPOL_CTRL, +}; diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl83xx.h b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl83xx.h index 3953512525..fd0455a6cd 100644 --- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl83xx.h +++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl83xx.h @@ -24,7 +24,71 @@ struct rtl83xx_mib_desc { const char *name; }; -void __init rtl83xx_storm_control_init(struct rtl838x_switch_priv *priv); +/* API for switch table access */ +struct table_reg { + u16 addr; + u16 data; + u8 max_data; + u8 c_bit; + u8 t_bit; + u8 rmode; + u8 tbl; + struct mutex lock; +}; + +#define TBL_DESC(_addr, _data, _max_data, _c_bit, _t_bit, _rmode) \ + { .addr = _addr, .data = _data, .max_data = _max_data, .c_bit = _c_bit, \ + .t_bit = _t_bit, .rmode = _rmode \ + } + +typedef enum { + RTL8380_TBL_L2 = 0, + RTL8380_TBL_0, + RTL8380_TBL_1, + RTL8390_TBL_L2, + RTL8390_TBL_0, + RTL8390_TBL_1, + RTL8390_TBL_2, + RTL9300_TBL_L2, + RTL9300_TBL_0, + RTL9300_TBL_1, + RTL9300_TBL_2, + RTL9300_TBL_HSB, + RTL9300_TBL_HSA, + RTL9310_TBL_0, + RTL9310_TBL_1, + RTL9310_TBL_2, + RTL9310_TBL_3, + RTL9310_TBL_4, + RTL9310_TBL_5, + RTL_TBL_END +} rtl838x_tbl_reg_t; + +void rtl_table_init(void); +struct table_reg *rtl_table_get(rtl838x_tbl_reg_t r, int t); +void rtl_table_release(struct table_reg *r); +void rtl_table_read(struct table_reg *r, int idx); +void rtl_table_write(struct table_reg *r, int idx); +inline u16 rtl_table_data(struct table_reg *r, int i); +inline u32 rtl_table_data_r(struct table_reg *r, int i); +inline void rtl_table_data_w(struct table_reg *r, u32 v, int i); + +void __init rtl83xx_setup_qos(struct rtl838x_switch_priv *priv); +int read_phy(u32 port, u32 page, u32 reg, u32 *val); +int write_phy(u32 port, u32 page, u32 reg, u32 val); + +/* Port register accessor functions for the RTL839x and RTL931X SoCs */ +void rtl839x_mask_port_reg_be(u64 clear, u64 set, int reg); +u64 rtl839x_get_port_reg_be(int reg); +void rtl839x_set_port_reg_be(u64 set, int reg); +void rtl839x_mask_port_reg_le(u64 clear, u64 set, int reg); +void rtl839x_set_port_reg_le(u64 set, int reg); +u64 rtl839x_get_port_reg_le(int reg); + +/* Port register accessor functions for the RTL838x and RTL930X SoCs */ +void rtl838x_mask_port_reg(u64 clear, u64 set, int reg); +void rtl838x_set_port_reg(u64 set, int reg); +u64 rtl838x_get_port_reg(int reg); /* RTL838x-specific */ u32 rtl838x_hash(struct rtl838x_switch_priv *priv, u64 seed); @@ -32,6 +96,9 @@ irqreturn_t rtl838x_switch_irq(int irq, void *dev_id); void rtl8380_get_version(struct rtl838x_switch_priv *priv); void rtl838x_vlan_profile_dump(int index); int rtl83xx_dsa_phy_read(struct dsa_switch *ds, int phy_addr, int phy_reg); +void rtl8380_sds_rst(int mac); +int rtl8380_sds_power(int mac, int val); +void rtl838x_print_matrix(void); /* RTL839x-specific */ u32 rtl839x_hash(struct rtl838x_switch_priv *priv, u64 seed); @@ -39,6 +106,20 @@ irqreturn_t rtl839x_switch_irq(int irq, void *dev_id); void rtl8390_get_version(struct rtl838x_switch_priv *priv); void rtl839x_vlan_profile_dump(int index); int rtl83xx_dsa_phy_write(struct dsa_switch *ds, int phy_addr, int phy_reg, u16 val); +void rtl839x_exec_tbl2_cmd(u32 cmd); +void rtl839x_print_matrix(void); + +/* RTL930x-specific */ +u32 rtl930x_hash(struct rtl838x_switch_priv *priv, u64 seed); +irqreturn_t rtl930x_switch_irq(int irq, void *dev_id); +irqreturn_t rtl839x_switch_irq(int irq, void *dev_id); +void rtl930x_vlan_profile_dump(int index); +int rtl9300_sds_power(int mac, int val); +void rtl9300_sds_rst(int sds_num, u32 mode); +void rtl930x_print_matrix(void); + +/* RTL931x-specific */ +irqreturn_t rtl931x_switch_irq(int irq, void *dev_id); #endif /* _NET_DSA_RTL83XX_H */ diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl930x.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl930x.c new file mode 100644 index 0000000000..59c283903b --- /dev/null +++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl930x.c @@ -0,0 +1,607 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include "rtl83xx.h" + +extern struct mutex smi_lock; +extern struct rtl83xx_soc_info soc_info; + +void rtl930x_print_matrix(void) +{ + int i; + struct table_reg *r = rtl_table_get(RTL9300_TBL_0, 6); + + for (i = 0; i < 29; i++) { + rtl_table_read(r, i); + pr_debug("> %08x\n", sw_r32(rtl_table_data(r, 0))); + } + rtl_table_release(r); +} + +inline void rtl930x_exec_tbl0_cmd(u32 cmd) +{ + sw_w32(cmd, RTL930X_TBL_ACCESS_CTRL_0); + do { } while (sw_r32(RTL930X_TBL_ACCESS_CTRL_0) & (1 << 17)); +} + +inline void rtl930x_exec_tbl1_cmd(u32 cmd) +{ + sw_w32(cmd, RTL930X_TBL_ACCESS_CTRL_1); + do { } while (sw_r32(RTL930X_TBL_ACCESS_CTRL_1) & (1 << 17)); +} + +inline int rtl930x_tbl_access_data_0(int i) +{ + return RTL930X_TBL_ACCESS_DATA_0(i); +} + +static inline int rtl930x_l2_port_new_salrn(int p) +{ + return RTL930X_L2_PORT_SALRN(p); +} + +static inline int rtl930x_l2_port_new_sa_fwd(int p) +{ + // TODO: The definition of the fields changed, because of the master-cpu in a stack + return RTL930X_L2_PORT_NEW_SA_FWD(p); +} + +inline static int rtl930x_trk_mbr_ctr(int group) +{ + return RTL930X_TRK_MBR_CTRL + (group << 2); +} + +static void rtl930x_vlan_tables_read(u32 vlan, struct rtl838x_vlan_info *info) +{ + u32 v, w; + // Read VLAN table (0) via register 0 + struct table_reg *r = rtl_table_get(RTL9300_TBL_0, 1); + + rtl_table_read(r, vlan); + v = sw_r32(rtl_table_data(r, 0)); + w = sw_r32(rtl_table_data(r, 1)); + pr_debug("VLAN_READ %d: %08x %08x\n", vlan, v, w); + rtl_table_release(r); + + info->tagged_ports = v >> 3; + info->profile_id = (w >> 24) & 7; + info->hash_mc_fid = !!(w & BIT(27)); + info->hash_uc_fid = !!(w & BIT(28)); + info->fid = ((v & 0x7) << 3) | ((w >> 29) & 0x7); + + // Read UNTAG table via table register 2 + r = rtl_table_get(RTL9300_TBL_2, 0); + rtl_table_read(r, vlan); + v = sw_r32(rtl_table_data(r, 0)); + rtl_table_release(r); + + info->untagged_ports = v >> 3; +} + +static void rtl930x_vlan_set_tagged(u32 vlan, struct rtl838x_vlan_info *info) +{ + u32 v, w; + // Access VLAN table (1) via register 0 + struct table_reg *r = rtl_table_get(RTL9300_TBL_0, 1); + + v = info->tagged_ports << 3; + v |= ((u32)info->fid) >> 3; + + w = ((u32)info->fid) << 29; + w |= info->hash_mc_fid ? BIT(27) : 0; + w |= info->hash_uc_fid ? BIT(28) : 0; + w |= info->profile_id << 24; + + sw_w32(v, rtl_table_data(r, 0)); + sw_w32(w, rtl_table_data(r, 1)); + + rtl_table_write(r, vlan); + rtl_table_release(r); +} + +void rtl930x_vlan_profile_dump(int index) +{ + u32 profile[5]; + + if (index < 0 || index > 7) + return; + + profile[0] = sw_r32(RTL930X_VLAN_PROFILE_SET(index)); + profile[1] = sw_r32(RTL930X_VLAN_PROFILE_SET(index) + 4); + profile[2] = sw_r32(RTL930X_VLAN_PROFILE_SET(index) + 8) & 0x1FFFFFFF; + profile[3] = sw_r32(RTL930X_VLAN_PROFILE_SET(index) + 12) & 0x1FFFFFFF; + profile[4] = sw_r32(RTL930X_VLAN_PROFILE_SET(index) + 16) & 0x1FFFFFFF; + + pr_debug("VLAN %d: L2 learning: %d, L2 Unknown MultiCast Field %x, \ + IPv4 Unknown MultiCast Field %x, IPv6 Unknown MultiCast Field: %x", + index, profile[0] & (3 << 21), profile[2], profile[3], profile[4]); +} + +static void rtl930x_vlan_set_untagged(u32 vlan, u64 portmask) +{ + struct table_reg *r = rtl_table_get(RTL9300_TBL_2, 0); + + sw_w32(portmask << 3, rtl_table_data(r, 0)); + rtl_table_write(r, vlan); + rtl_table_release(r); +} + +static void rtl930x_stp_get(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]) +{ + int i; + u32 cmd = 1 << 17 /* Execute cmd */ + | 0 << 16 /* Read */ + | 4 << 12 /* Table type 0b10 */ + | (msti & 0xfff); + priv->r->exec_tbl0_cmd(cmd); + + for (i = 0; i < 2; i++) + port_state[i] = sw_r32(RTL930X_TBL_ACCESS_DATA_0(i)); + pr_debug("MSTI: %d STATE: %08x, %08x\n", msti, port_state[0], port_state[1]); +} + +static void rtl930x_stp_set(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]) +{ + int i; + u32 cmd = 1 << 17 /* Execute cmd */ + | 1 << 16 /* Write */ + | 4 << 12 /* Table type 4 */ + | (msti & 0xfff); + + for (i = 0; i < 2; i++) + sw_w32(port_state[i], RTL930X_TBL_ACCESS_DATA_0(i)); + priv->r->exec_tbl0_cmd(cmd); +} + +static inline int rtl930x_mac_force_mode_ctrl(int p) +{ + return RTL930X_MAC_FORCE_MODE_CTRL + (p << 2); +} + +static inline int rtl930x_mac_port_ctrl(int p) +{ + return RTL930X_MAC_L2_PORT_CTRL(p); +} + +static inline int rtl930x_mac_link_spd_sts(int p) +{ + return RTL930X_MAC_LINK_SPD_STS(p); +} + +static void rtl930x_fill_l2_entry(u32 r[], struct rtl838x_l2_entry *e) +{ + e->valid = !!(r[2] & BIT(31)); + if (!e->valid) + return; + + // TODO: Is there not a function to copy directly MAC memory? + e->mac[0] = (r[0] >> 24); + e->mac[1] = (r[0] >> 16); + e->mac[2] = (r[0] >> 8); + e->mac[3] = r[0]; + e->mac[4] = (r[1] >> 24); + e->mac[5] = (r[1] >> 16); + + /* Is it a unicast entry? check multicast bit */ + if (!(e->mac[0] & 1)) { + e->type = L2_UNICAST; + e->is_static = !!(r[2] & BIT(14)); + e->vid = r[2] & 0xfff; + e->rvid = r[1] & 0xfff; + e->port = (r[2] >> 20) & 0x3ff; + // Check for trunk port + if (r[2] & BIT(30)) { + e->stackDev = (e->port >> 9) & 1; + e->trunk = e->port & 0x3f; + } else { + e->stackDev = (e->port >> 6) & 0xf; + e->port = e->port & 0x3f; + } + + e->block_da = !!(r[2] & BIT(15)); + e->block_sa = !!(r[2] & BIT(16)); + e->suspended = !!(r[2] & BIT(13)); + e->next_hop = !!(r[2] & BIT(12)); + e->age = (r[2] >> 17) & 3; + e->valid = true; + + } else { + e->valid = true; + e->type = L2_MULTICAST; + e->mc_portmask_index = (r[2]>>6) & 0xfff; + } +} + +static u64 rtl930x_read_l2_entry_using_hash(u32 hash, u32 position, struct rtl838x_l2_entry *e) +{ + u64 entry; + u32 r[3]; + struct table_reg *q = rtl_table_get(RTL9300_TBL_L2, 0); + u32 idx = (0 << 14) | (hash << 2) | position; + int i; + + rtl_table_read(q, idx); + for (i= 0; i < 3; i++) + r[i] = sw_r32(rtl_table_data(q, i)); + + rtl_table_release(q); + + rtl930x_fill_l2_entry(r, e); + if (!e->valid) + return 0; + + entry = ((u64)r[0] << 32) | (r[1] & 0xffff0000) | e->vid; + return entry; +} + +static u64 rtl930x_read_cam(int idx, struct rtl838x_l2_entry *e) +{ + u64 entry; + u32 r[3]; + struct table_reg *q = rtl_table_get(RTL9300_TBL_L2, 1); + int i; + + rtl_table_read(q, idx); + for (i= 0; i < 3; i++) + r[i] = sw_r32(rtl_table_data(q, i)); + + rtl_table_release(q); + + rtl930x_fill_l2_entry(r, e); + if (!e->valid) + return 0; + + entry = ((u64)r[0] << 32) | (r[1] & 0xffff0000) | e->vid; + + return entry; +} + +u64 rtl930x_traffic_get(int source) +{ + u32 v; + struct table_reg *r = rtl_table_get(RTL9300_TBL_0, 6); + + rtl_table_read(r, source); + v = sw_r32(rtl_table_data(r, 0)); + rtl_table_release(r); + return v >> 3; +} + +/* + * Enable traffic between a source port and a destination port matrix + */ +void rtl930x_traffic_set(int source, u64 dest_matrix) +{ + struct table_reg *r = rtl_table_get(RTL9300_TBL_0, 6); + + sw_w32((dest_matrix << 3), rtl_table_data(r, 0)); + rtl_table_write(r, source); + rtl_table_release(r); +} + +void rtl930x_traffic_enable(int source, int dest) +{ + struct table_reg *r = rtl_table_get(RTL9300_TBL_0, 6); + rtl_table_read(r, source); + sw_w32_mask(0, BIT(dest + 3), rtl_table_data(r, 0)); + rtl_table_write(r, source); + rtl_table_release(r); +} + +void rtl930x_traffic_disable(int source, int dest) +{ + struct table_reg *r = rtl_table_get(RTL9300_TBL_0, 6); + rtl_table_read(r, source); + sw_w32_mask(BIT(dest + 3), 0, rtl_table_data(r, 0)); + rtl_table_write(r, source); + rtl_table_release(r); +} + +void rtl9300_dump_debug(void) +{ + int i; + u16 r = RTL930X_STAT_PRVTE_DROP_COUNTER0; + + for (i = 0; i < 10; i ++) { + pr_info("# %d %08x %08x %08x %08x %08x %08x %08x %08x\n", i * 8, + sw_r32(r), sw_r32(r + 4), sw_r32(r + 8), sw_r32(r + 12), + sw_r32(r + 16), sw_r32(r + 20), sw_r32(r + 24), sw_r32(r + 28)); + r += 32; + } + pr_info("# %08x %08x %08x %08x %08x\n", + sw_r32(r), sw_r32(r + 4), sw_r32(r + 8), sw_r32(r + 12), sw_r32(r + 16)); + rtl930x_print_matrix(); + pr_info("RTL930X_L2_PORT_SABLK_CTRL: %08x, RTL930X_L2_PORT_DABLK_CTRL %08x\n", + sw_r32(RTL930X_L2_PORT_SABLK_CTRL), sw_r32(RTL930X_L2_PORT_DABLK_CTRL) + + ); +} + +irqreturn_t rtl930x_switch_irq(int irq, void *dev_id) +{ + struct dsa_switch *ds = dev_id; + u32 status = sw_r32(RTL930X_ISR_GLB); + u32 ports = sw_r32(RTL930X_ISR_PORT_LINK_STS_CHG); + u32 link; + int i; + + /* Clear status */ + sw_w32(ports, RTL930X_ISR_PORT_LINK_STS_CHG); + pr_info("RTL9300 Link change: status: %x, ports %x\n", status, ports); + + rtl9300_dump_debug(); + + for (i = 0; i < 28; i++) { + if (ports & BIT(i)) { + /* Read the register twice because of issues with latency at least + * with the external RTL8226 PHY on the XGS1210 */ + link = sw_r32(RTL930X_MAC_LINK_STS); + link = sw_r32(RTL930X_MAC_LINK_STS); + if (link & BIT(i)) + dsa_port_phylink_mac_change(ds, i, true); + else + dsa_port_phylink_mac_change(ds, i, false); + } + } + + return IRQ_HANDLED; +} + +int rtl9300_sds_power(int mac, int val) +{ + int sds_num; + u32 mode; + + // TODO: these numbers are hard-coded for the Zyxel XGS1210 12 Switch + pr_info("SerDes: %s %d\n", __func__, mac); + switch (mac) { + case 24: + sds_num = 6; + mode = 0x12; // HISGMII + break; + case 25: + sds_num = 7; + mode = 0x12; // HISGMII + break; + case 26: + sds_num = 8; + mode = 0x1b; // 10GR/1000BX auto + break; + case 27: + sds_num = 9; + mode = 0x1b; // 10GR/1000BX auto + break; + default: + return -1; + } + if (!val) + mode = 0x1f; // OFF + + rtl9300_sds_rst(sds_num, mode); + + return 0; +} + + +int rtl930x_write_phy(u32 port, u32 page, u32 reg, u32 val) +{ + u32 v; + int err = 0; + + pr_debug("%s: port %d, page: %d, reg: %x, val: %x\n", __func__, port, page, reg, val); + + if (port > 63 || page > 4095 || reg > 31) + return -ENOTSUPP; + + val &= 0xffff; + mutex_lock(&smi_lock); + + sw_w32(BIT(port), RTL930X_SMI_ACCESS_PHY_CTRL_0); + sw_w32_mask(0xffff << 16, val << 16, RTL930X_SMI_ACCESS_PHY_CTRL_2); + v = reg << 20 | page << 3 | 0x1f << 15 | BIT(2) | BIT(0); + sw_w32(v, RTL930X_SMI_ACCESS_PHY_CTRL_1); + + do { + v = sw_r32(RTL930X_SMI_ACCESS_PHY_CTRL_1); + } while (v & 0x1); + + if (v & 0x2) + err = -EIO; + + mutex_unlock(&smi_lock); + + return err; +} + +int rtl930x_read_phy(u32 port, u32 page, u32 reg, u32 *val) +{ + u32 v; + int err = 0; + +// pr_info("In %s\n", __func__); + if (port > 63 || page > 4095 || reg > 31) + return -ENOTSUPP; + + mutex_lock(&smi_lock); + + sw_w32_mask(0xffff << 16, port << 16, RTL930X_SMI_ACCESS_PHY_CTRL_2); + v = reg << 20 | page << 3 | 0x1f << 15 | 1; + sw_w32(v, RTL930X_SMI_ACCESS_PHY_CTRL_1); + + do { + v = sw_r32(RTL930X_SMI_ACCESS_PHY_CTRL_1); + } while ( v & 0x1); + + if (v & BIT(25)) { + pr_debug("Error reading phy %d, register %d\n", port, reg); + err = -EIO; + } + *val = (sw_r32(RTL930X_SMI_ACCESS_PHY_CTRL_2) & 0xffff); + + pr_debug("%s: port %d, page: %d, reg: %x, val: %x\n", __func__, port, page, reg, *val); + + mutex_unlock(&smi_lock); + + return err; +} + + +/* + * Write to an mmd register of the PHY + */ +int rtl930x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val) +{ + int err = 0; + u32 v; + + mutex_lock(&smi_lock); + + // Set PHY to access + sw_w32(BIT(port), RTL930X_SMI_ACCESS_PHY_CTRL_0); + + // Set data to write + sw_w32_mask(0xffff << 16, val << 16, RTL930X_SMI_ACCESS_PHY_CTRL_2); + + // Set MMD device number and register to write to + sw_w32(devnum << 16 | (regnum & 0xffff), RTL930X_SMI_ACCESS_PHY_CTRL_3); + + v = BIT(2)| BIT(1)| BIT(0); // WRITE | MMD-access | EXEC + sw_w32(v, RTL930X_SMI_ACCESS_PHY_CTRL_1); + + do { + v = sw_r32(RTL930X_SMI_ACCESS_PHY_CTRL_1); + } while ( v & BIT(0)); + + pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, val, err); + mutex_unlock(&smi_lock); + return err; +} + +/* + * Read an mmd register of the PHY + */ +int rtl930x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val) +{ + int err = 0; + u32 v; + + mutex_lock(&smi_lock); + + // Set PHY to access + sw_w32_mask(0xffff << 16, port << 16, RTL930X_SMI_ACCESS_PHY_CTRL_2); + + // Set MMD device number and register to write to + sw_w32(devnum << 16 | (regnum & 0xffff), RTL930X_SMI_ACCESS_PHY_CTRL_3); + + v = BIT(1)| BIT(0); // MMD-access | EXEC + sw_w32(v, RTL930X_SMI_ACCESS_PHY_CTRL_1); + + do { + v = sw_r32(RTL930X_SMI_ACCESS_PHY_CTRL_1); + } while ( v & 0x1); + // There is no error-checking via BIT 25 of v, as it does not seem to be set correctly + *val = (sw_r32(RTL930X_SMI_ACCESS_PHY_CTRL_2) & 0xffff); + pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, *val, err); + + mutex_unlock(&smi_lock); + + return err; +} + + +/* + * Calculate both the block 0 and the block 1 hash, and return in + * lower and higher word of the return value since only 12 bit of + * the hash are significant + */ +u32 rtl930x_hash(struct rtl838x_switch_priv *priv, u64 seed) +{ + u32 k0, k1, h1, h2, h; + + k0 = (u32) (((seed >> 55) & 0x1f) ^ ((seed >> 44) & 0x7ff) + ^ ((seed >> 33) & 0x7ff) ^ ((seed >> 22) & 0x7ff) + ^ ((seed >> 11) & 0x7ff) ^ (seed & 0x7ff)); + + h1 = (seed >> 11) & 0x7ff; + h1 = ((h1 & 0x1f) << 6) | ((h1 >> 5) & 0x3f); + + h2 = (seed >> 33) & 0x7ff; + h2 = ((h2 & 0x3f) << 5)| ((h2 >> 6) & 0x3f); + + k1 = (u32) (((seed << 55) & 0x1f) ^ ((seed >> 44) & 0x7ff) ^ h2 + ^ ((seed >> 22) & 0x7ff) ^ h1 + ^ (seed & 0x7ff)); + + // Algorithm choice for block 0 + if (sw_r32(RTL930X_L2_CTRL) & BIT(0)) + h = k1; + else + h = k0; + + /* Algorithm choice for block 1 + * Since k0 and k1 are < 2048, adding 2048 will offset the hash into the second + * half of hash-space + * 2048 is in fact the hash-table size 16384 divided by 4 hashes per bucket + * divided by 2 to divide the hash space in 2 + */ + if (sw_r32(RTL930X_L2_CTRL) & BIT(1)) + h |= (k1 + 2048) << 16; + else + h |= (k0 + 2048) << 16; + + return h; +} + +const struct rtl838x_reg rtl930x_reg = { + .mask_port_reg_be = rtl838x_mask_port_reg, + .set_port_reg_be = rtl838x_set_port_reg, + .get_port_reg_be = rtl838x_get_port_reg, + .mask_port_reg_le = rtl838x_mask_port_reg, + .set_port_reg_le = rtl838x_set_port_reg, + .get_port_reg_le = rtl838x_get_port_reg, + .stat_port_rst = RTL930X_STAT_PORT_RST, + .stat_rst = RTL930X_STAT_RST, + .stat_port_std_mib = RTL930X_STAT_PORT_MIB_CNTR, + .traffic_enable = rtl930x_traffic_enable, + .traffic_disable = rtl930x_traffic_disable, + .traffic_get = rtl930x_traffic_get, + .traffic_set = rtl930x_traffic_set, + .l2_ctrl_0 = RTL930X_L2_CTRL, + .l2_ctrl_1 = RTL930X_L2_AGE_CTRL, + .l2_port_aging_out = RTL930X_L2_PORT_AGE_CTRL, + .smi_poll_ctrl = RTL930X_SMI_POLL_CTRL, // TODO: Difference to RTL9300_SMI_PRVTE_POLLING_CTRL + .l2_tbl_flush_ctrl = RTL930X_L2_TBL_FLUSH_CTRL, + .exec_tbl0_cmd = rtl930x_exec_tbl0_cmd, + .exec_tbl1_cmd = rtl930x_exec_tbl1_cmd, + .tbl_access_data_0 = rtl930x_tbl_access_data_0, + .isr_glb_src = RTL930X_ISR_GLB, + .isr_port_link_sts_chg = RTL930X_ISR_PORT_LINK_STS_CHG, + .imr_port_link_sts_chg = RTL930X_IMR_PORT_LINK_STS_CHG, + .imr_glb = RTL930X_IMR_GLB, + .vlan_tables_read = rtl930x_vlan_tables_read, + .vlan_set_tagged = rtl930x_vlan_set_tagged, + .vlan_set_untagged = rtl930x_vlan_set_untagged, + .vlan_profile_dump = rtl930x_vlan_profile_dump, + .stp_get = rtl930x_stp_get, + .stp_set = rtl930x_stp_set, + .mac_force_mode_ctrl = rtl930x_mac_force_mode_ctrl, + .mac_port_ctrl = rtl930x_mac_port_ctrl, + .l2_port_new_salrn = rtl930x_l2_port_new_salrn, + .l2_port_new_sa_fwd = rtl930x_l2_port_new_sa_fwd, + .mir_ctrl = RTL930X_MIR_CTRL, + .mir_dpm = RTL930X_MIR_DPM_CTRL, + .mir_spm = RTL930X_MIR_SPM_CTRL, + .mac_link_sts = RTL930X_MAC_LINK_STS, + .mac_link_dup_sts = RTL930X_MAC_LINK_DUP_STS, + .mac_link_spd_sts = rtl930x_mac_link_spd_sts, + .mac_rx_pause_sts = RTL930X_MAC_RX_PAUSE_STS, + .mac_tx_pause_sts = RTL930X_MAC_TX_PAUSE_STS, + .read_l2_entry_using_hash = rtl930x_read_l2_entry_using_hash, + .read_cam = rtl930x_read_cam, + .vlan_port_egr_filter = RTL930X_VLAN_PORT_EGR_FLTR, + .vlan_port_igr_filter = RTL930X_VLAN_PORT_IGR_FLTR(0), + .vlan_port_pb = RTL930X_VLAN_PORT_PB_VLAN, + .vlan_port_tag_sts_ctrl = RTL930X_VLAN_PORT_TAG_STS_CTRL, + .trk_mbr_ctr = rtl930x_trk_mbr_ctr, + .rma_bpdu_fld_pmask = RTL930X_RMA_BPDU_FLD_PMSK, +}; diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl931x.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl931x.c new file mode 100644 index 0000000000..a33941a0eb --- /dev/null +++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl931x.c @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include "rtl83xx.h" + +extern struct mutex smi_lock; +extern struct rtl83xx_soc_info soc_info; + +inline void rtl931x_exec_tbl0_cmd(u32 cmd) +{ + sw_w32(cmd, RTL931X_TBL_ACCESS_CTRL_0); + do { } while (sw_r32(RTL931X_TBL_ACCESS_CTRL_0) & (1 << 20)); +} + +inline void rtl931x_exec_tbl1_cmd(u32 cmd) +{ + sw_w32(cmd, RTL931X_TBL_ACCESS_CTRL_1); + do { } while (sw_r32(RTL931X_TBL_ACCESS_CTRL_1) & (1 << 17)); +} + +inline int rtl931x_tbl_access_data_0(int i) +{ + return RTL931X_TBL_ACCESS_DATA_0(i); +} + +void rtl931x_vlan_profile_dump(int index) +{ + u64 profile[4]; + + if (index < 0 || index > 15) + return; + + profile[0] = sw_r32(RTL931X_VLAN_PROFILE_SET(index)); + profile[1] = (sw_r32(RTL931X_VLAN_PROFILE_SET(index) + 4) & 0x1FFFFFFFULL) << 32 + | (sw_r32(RTL931X_VLAN_PROFILE_SET(index) + 8) & 0xFFFFFFFF); + profile[2] = (sw_r32(RTL931X_VLAN_PROFILE_SET(index) + 16) & 0xFFFFFFFFULL) << 32 + | (sw_r32(RTL931X_VLAN_PROFILE_SET(index) + 12) & 0x1FFFFFFULL); + profile[3] = (sw_r32(RTL931X_VLAN_PROFILE_SET(index) + 20) & 0x1FFFFFFFULL) << 32 + | (sw_r32(RTL931X_VLAN_PROFILE_SET(index) + 24) & 0xFFFFFFFF); + + pr_info("VLAN %d: L2 learning: %d, L2 Unknown MultiCast Field %llx, \ + IPv4 Unknown MultiCast Field %llx, IPv6 Unknown MultiCast Field: %llx", + index, (u32) (profile[0] & (3 << 14)), profile[1], profile[2], profile[3]); +} + +static void rtl931x_stp_get(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]) +{ + int i; + u32 cmd = 1 << 20 /* Execute cmd */ + | 0 << 19 /* Read */ + | 2 << 15 /* Table type 0b10 */ + | (msti & 0x3fff); + priv->r->exec_tbl0_cmd(cmd); + + for (i = 0; i < 4; i++) + port_state[i] = sw_r32(priv->r->tbl_access_data_0(i)); +} + +static void rtl931x_stp_set(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]) +{ + int i; + u32 cmd = 1 << 20 /* Execute cmd */ + | 1 << 19 /* Write */ + | 5 << 15 /* Table type 0b101 */ + | (msti & 0x3fff); + for (i = 0; i < 4; i++) + sw_w32(port_state[i], priv->r->tbl_access_data_0(i)); + priv->r->exec_tbl0_cmd(cmd); +} + +inline static int rtl931x_trk_mbr_ctr(int group) +{ + return RTL931X_TRK_MBR_CTRL + (group << 2); +} + +static void rtl931x_vlan_tables_read(u32 vlan, struct rtl838x_vlan_info *info) +{ + u32 v, w, x, y; + // Read VLAN table (3) via register 0 + struct table_reg *r = rtl_table_get(RTL9310_TBL_0, 3); + + rtl_table_read(r, vlan); + v = sw_r32(rtl_table_data(r, 0)); + w = sw_r32(rtl_table_data(r, 1)); + x = sw_r32(rtl_table_data(r, 2)); + y = sw_r32(rtl_table_data(r, 3)); + pr_debug("VLAN_READ %d: %08x %08x\n", vlan, v, w); + rtl_table_release(r); + + info->tagged_ports = ((u64) v) << 25 | (w >> 7); + info->profile_id = (x >> 16) & 0xf; + info->hash_mc_fid = !!(x & BIT(30)); + info->hash_uc_fid = !!(x & BIT(31)); + info->fid = w & 0x7f; + // TODO: use also info in 4th register + + // Read UNTAG table via table register 3 + r = rtl_table_get(RTL9310_TBL_3, 0); + rtl_table_read(r, vlan); + v = ((u64)sw_r32(rtl_table_data(r, 0))) << 25; + v |= sw_r32(rtl_table_data(r, 1)) >> 7; + rtl_table_release(r); + + info->untagged_ports = v; +} + +static void rtl931x_vlan_set_tagged(u32 vlan, struct rtl838x_vlan_info *info) +{ + u32 v, w, x; + // Access VLAN table (1) via register 0 + struct table_reg *r = rtl_table_get(RTL9310_TBL_0, 3); + + v = info->tagged_ports << 7; + w = (info->tagged_ports & 0x7f000000) << 25; + w |= (u32)info->fid; + x = info->profile_id << 16; + w |= info->hash_mc_fid ? BIT(30) : 0; + w |= info->hash_uc_fid ? BIT(31) : 0; + // TODO: use also info in 4th register + + sw_w32(v, rtl_table_data(r, 0)); + sw_w32(w, rtl_table_data(r, 1)); + sw_w32(x, rtl_table_data(r, 2)); + + rtl_table_write(r, vlan); + rtl_table_release(r); +} + +static void rtl931x_vlan_set_untagged(u32 vlan, u64 portmask) +{ + struct table_reg *r = rtl_table_get(RTL9310_TBL_3, 0); + + rtl839x_set_port_reg_be(portmask << 7, rtl_table_data(r, 0)); + rtl_table_write(r, vlan); + rtl_table_release(r); +} + +static inline int rtl931x_mac_force_mode_ctrl(int p) +{ + return RTL931X_MAC_FORCE_MODE_CTRL + (p << 2); +} + +static inline int rtl931x_mac_link_spd_sts(int p) +{ + return RTL931X_MAC_LINK_SPD_STS(p); +} + +static inline int rtl931x_mac_port_ctrl(int p) +{ + return RTL931X_MAC_PORT_CTRL(p); +} + +static inline int rtl931x_l2_port_new_salrn(int p) +{ + return RTL931X_L2_PORT_NEW_SALRN(p); +} + +static inline int rtl931x_l2_port_new_sa_fwd(int p) +{ + return RTL931X_L2_PORT_NEW_SA_FWD(p); +} + +static u64 rtl931x_read_l2_entry_using_hash(u32 hash, u32 position, struct rtl838x_l2_entry *e) +{ + u64 entry = 0; + + // TODO: Implement + return entry; +} + +static u64 rtl931x_read_cam(int idx, struct rtl838x_l2_entry *e) +{ + u64 entry = 0; + + // TODO: Implement + return entry; +} +irqreturn_t rtl931x_switch_irq(int irq, void *dev_id) +{ + struct dsa_switch *ds = dev_id; + u32 status = sw_r32(RTL931X_ISR_GLB_SRC); + u64 ports = rtl839x_get_port_reg_le(RTL931X_ISR_PORT_LINK_STS_CHG); + u64 link; + int i; + + /* Clear status */ + rtl839x_set_port_reg_le(ports, RTL931X_ISR_PORT_LINK_STS_CHG); + pr_info("RTL9310 Link change: status: %x, ports %llx\n", status, ports); + + for (i = 0; i < 56; i++) { + if (ports & BIT_ULL(i)) { + link = rtl839x_get_port_reg_le(RTL931X_MAC_LINK_STS); + if (link & BIT_ULL(i)) + dsa_port_phylink_mac_change(ds, i, true); + else + dsa_port_phylink_mac_change(ds, i, false); + } + } + return IRQ_HANDLED; +} + + +int rtl931x_write_phy(u32 port, u32 page, u32 reg, u32 val) +{ + u32 v; + int err = 0; + + val &= 0xffff; + if (port > 63 || page > 4095 || reg > 31) + return -ENOTSUPP; + + mutex_lock(&smi_lock); + /* Clear both port registers */ + sw_w32(0, RTL931X_SMI_INDRT_ACCESS_CTRL_2); + sw_w32(0, RTL931X_SMI_INDRT_ACCESS_CTRL_2 + 4); + sw_w32_mask(0, BIT(port), RTL931X_SMI_INDRT_ACCESS_CTRL_2+ (port % 32) * 4); + + sw_w32_mask(0xffff0000, val << 16, RTL931X_SMI_INDRT_ACCESS_CTRL_3); + + v = reg << 6 | page << 11 ; + sw_w32(v, RTL931X_SMI_INDRT_ACCESS_CTRL_0); + + sw_w32(0x1ff, RTL931X_SMI_INDRT_ACCESS_CTRL_1); + + v |= 1 << 3 | 1; /* Write operation and execute */ + sw_w32(v, RTL931X_SMI_INDRT_ACCESS_CTRL_0); + + do { + } while (sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_0) & 0x1); + + if (sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_0) & 0x2) + err = -EIO; + + mutex_unlock(&smi_lock); + return err; +} + +int rtl931x_read_phy(u32 port, u32 page, u32 reg, u32 *val) +{ + u32 v; + + if (port > 63 || page > 4095 || reg > 31) + return -ENOTSUPP; + + mutex_lock(&smi_lock); + + sw_w32_mask(0xffff, port, RTL931X_SMI_INDRT_ACCESS_CTRL_3); + v = reg << 6 | page << 11; // TODO: ACCESS Offset? Park page + sw_w32(v, RTL931X_SMI_INDRT_ACCESS_CTRL_0); + + sw_w32(0x1ff, RTL931X_SMI_INDRT_ACCESS_CTRL_1); + + v |= 1; + sw_w32(v, RTL931X_SMI_INDRT_ACCESS_CTRL_0); + + do { + } while (sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_0) & 0x1); + + *val = (sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_3) & 0xffff0000) >> 16; + + pr_info("%s: port %d, page: %d, reg: %x, val: %x\n", __func__, port, page, reg, *val); + + mutex_unlock(&smi_lock); + return 0; +} + +void rtl931x_print_matrix(void) +{ + volatile u64 *ptr = RTL838X_SW_BASE + RTL839X_PORT_ISO_CTRL(0); + int i; + + for (i = 0; i < 52; i += 4) + pr_info("> %16llx %16llx %16llx %16llx\n", + ptr[i + 0], ptr[i + 1], ptr[i + 2], ptr[i + 3]); + pr_info("CPU_PORT> %16llx\n", ptr[52]); +} + +const struct rtl838x_reg rtl931x_reg = { + .mask_port_reg_be = rtl839x_mask_port_reg_be, + .set_port_reg_be = rtl839x_set_port_reg_be, + .get_port_reg_be = rtl839x_get_port_reg_be, + .mask_port_reg_le = rtl839x_mask_port_reg_le, + .set_port_reg_le = rtl839x_set_port_reg_le, + .get_port_reg_le = rtl839x_get_port_reg_le, + .stat_port_rst = RTL931X_STAT_PORT_RST, + .stat_rst = RTL931X_STAT_RST, + .stat_port_std_mib = 0, // Not defined + .l2_ctrl_0 = RTL931X_L2_CTRL, + .l2_ctrl_1 = RTL931X_L2_AGE_CTRL, + .l2_port_aging_out = RTL931X_L2_PORT_AGE_CTRL, + // .smi_poll_ctrl does not exist + .l2_tbl_flush_ctrl = RTL931X_L2_TBL_FLUSH_CTRL, + .exec_tbl0_cmd = rtl931x_exec_tbl0_cmd, + .exec_tbl1_cmd = rtl931x_exec_tbl1_cmd, + .tbl_access_data_0 = rtl931x_tbl_access_data_0, + .isr_glb_src = RTL931X_ISR_GLB_SRC, + .isr_port_link_sts_chg = RTL931X_ISR_PORT_LINK_STS_CHG, + .imr_port_link_sts_chg = RTL931X_IMR_PORT_LINK_STS_CHG, + // imr_glb does not exist on RTL931X + .vlan_tables_read = rtl931x_vlan_tables_read, + .vlan_set_tagged = rtl931x_vlan_set_tagged, + .vlan_set_untagged = rtl931x_vlan_set_untagged, + .vlan_profile_dump = rtl931x_vlan_profile_dump, + .stp_get = rtl931x_stp_get, + .stp_set = rtl931x_stp_set, + .mac_force_mode_ctrl = rtl931x_mac_force_mode_ctrl, + .mac_port_ctrl = rtl931x_mac_port_ctrl, + .l2_port_new_salrn = rtl931x_l2_port_new_salrn, + .l2_port_new_sa_fwd = rtl931x_l2_port_new_sa_fwd, + .mir_ctrl = RTL931X_MIR_CTRL, + .mir_dpm = RTL931X_MIR_DPM_CTRL, + .mir_spm = RTL931X_MIR_SPM_CTRL, + .mac_link_sts = RTL931X_MAC_LINK_STS, + .mac_link_dup_sts = RTL931X_MAC_LINK_DUP_STS, + .mac_link_spd_sts = rtl931x_mac_link_spd_sts, + .mac_rx_pause_sts = RTL931X_MAC_RX_PAUSE_STS, + .mac_tx_pause_sts = RTL931X_MAC_TX_PAUSE_STS, + .read_l2_entry_using_hash = rtl931x_read_l2_entry_using_hash, + .read_cam = rtl931x_read_cam, + .vlan_port_egr_filter = RTL931X_VLAN_PORT_EGR_FLTR(0), + .vlan_port_igr_filter = RTL931X_VLAN_PORT_IGR_FLTR(0), +// .vlan_port_pb = does not exist + .vlan_port_tag_sts_ctrl = RTL931X_VLAN_PORT_TAG_CTRL, + .trk_mbr_ctr = rtl931x_trk_mbr_ctr, +}; + diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/storm.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/storm.c deleted file mode 100644 index de0af033f3..0000000000 --- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/storm.c +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only - -#include -#include "rtl83xx.h" - - -static void rtl83xx_storm_enable(struct rtl838x_switch_priv *priv, int port, bool enable) -{ - // Enable Storm control for that port for UC, MC, and BC - if (enable) - sw_w32(0x7, RTL838X_STORM_CTRL_LB_CTRL(port)); - else - sw_w32(0x0, RTL838X_STORM_CTRL_LB_CTRL(port)); -} - -void __init rtl83xx_storm_control_init(struct rtl838x_switch_priv *priv) -{ - int i; - - pr_debug("Enabling Storm control\n"); - // TICK_PERIOD_PPS - if (priv->id == 0x8380) - sw_w32_mask(0x3ff << 20, 434 << 20, RTL838X_SCHED_LB_TICK_TKN_CTRL_0); - - // Set burst rate - sw_w32(0x00008000, RTL838X_STORM_CTRL_BURST_0); // UC - sw_w32(0x80008000, RTL838X_STORM_CTRL_BURST_1); // MC and BC - - // Set burst Packets per Second to 32 - sw_w32(0x00000020, RTL838X_STORM_CTRL_BURST_PPS_0); // UC - sw_w32(0x00200020, RTL838X_STORM_CTRL_BURST_PPS_1); // MC and BC - - // Include IFG in storm control - sw_w32_mask(0, BIT(6), RTL838X_STORM_CTRL); - // Rate control is based on bytes/s (0 = packets) - sw_w32_mask(0, BIT(5), RTL838X_STORM_CTRL); - // Bandwidth control includes preamble and IFG (10 Bytes) - sw_w32_mask(0, 1, RTL838X_SCHED_CTRL); - - // On SoCs except RTL8382M, set burst size of port egress - if (priv->id != 0x8382) - sw_w32_mask(0xffff, 0x800, RTL838X_SCHED_LB_THR); - - /* Enable storm control on all ports with a PHY and limit rates, - * for UC and MC for both known and unknown addresses */ - for (i = 0; i < priv->cpu_port; i++) { - if (priv->ports[i].phy) { - sw_w32(BIT(18) | 0x8000, RTL838X_STORM_CTRL_PORT_UC(i)); - sw_w32(BIT(18) | 0x8000, RTL838X_STORM_CTRL_PORT_MC(i)); - sw_w32(0x000, RTL838X_STORM_CTRL_PORT_BC(i)); - rtl83xx_storm_enable(priv, i, true); - } - } - - // Attack prevention, enable all attack prevention measures - //sw_w32(0x1ffff, RTL838X_ATK_PRVNT_CTRL); - /* Attack prevention, drop (bit = 0) problematic packets on all ports. - * Setting bit = 1 means: trap to CPU - */ - //sw_w32(0, RTL838X_ATK_PRVNT_ACT); - // Enable attack prevention on all ports - //sw_w32(0x0fffffff, RTL838X_ATK_PRVNT_PORT_EN); -} - diff --git a/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.c index fec842674e..7931daff07 100644 --- a/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.c +++ b/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -26,20 +27,24 @@ extern struct rtl83xx_soc_info soc_info; /* - * Maximum number of RX rings is 8, assigned by switch based on - * packet/port priortity (not implemented) - * Maximum number of TX rings is 2 (only ring 0 used) - * RX ringlength needs to be at least 200, otherwise CPU and Switch - * may gridlock. + * Maximum number of RX rings is 8 on RTL83XX and 32 on the 93XX + * The ring is assigned by switch based on packet/port priortity + * Maximum number of TX rings is 2, Ring 2 being the high priority + * ring on the RTL93xx SoCs. MAX_RING_SIZE * RING_BUFFER gives + * the memory used for the ring buffer. */ -#define RXRINGS 8 -#define RXRINGLEN 300 +#define MAX_RXRINGS 32 +#define MAX_RXLEN 100 +#define MAX_ENTRIES (200 * 8) #define TXRINGS 2 -#define TXRINGLEN 160 +// BUG: TXRINGLEN can be 160 +#define TXRINGLEN 16 #define NOTIFY_EVENTS 10 #define NOTIFY_BLOCKS 10 #define TX_EN 0x8 #define RX_EN 0x4 +#define TX_EN_93XX 0x20 +#define RX_EN_93XX 0x10 #define TX_DO 0x2 #define WRAP 0x2 @@ -53,11 +58,10 @@ extern struct rtl83xx_soc_info soc_info; struct p_hdr { uint8_t *buf; uint16_t reserved; - uint16_t size; /* buffer size */ + uint16_t size; /* buffer size */ uint16_t offset; - uint16_t len; /* pkt len */ - uint16_t reserved2; - uint16_t cpu_tag[5]; + uint16_t len; /* pkt len */ + uint16_t cpu_tag[10]; } __packed __aligned(1); struct n_event { @@ -70,14 +74,14 @@ struct n_event { } __packed __aligned(1); struct ring_b { - uint32_t rx_r[RXRINGS][RXRINGLEN]; + uint32_t rx_r[MAX_RXRINGS][MAX_RXLEN]; uint32_t tx_r[TXRINGS][TXRINGLEN]; - struct p_hdr rx_header[RXRINGS][RXRINGLEN]; + struct p_hdr rx_header[MAX_RXRINGS][MAX_RXLEN]; struct p_hdr tx_header[TXRINGS][TXRINGLEN]; - uint32_t c_rx[RXRINGS]; + uint32_t c_rx[MAX_RXRINGS]; uint32_t c_tx[TXRINGS]; - uint8_t rx_space[RXRINGS*RXRINGLEN*RING_BUFFER]; - uint8_t tx_space[TXRINGLEN*RING_BUFFER]; + uint8_t tx_space[TXRINGS * TXRINGLEN * RING_BUFFER]; + uint8_t *rx_space; }; struct notify_block { @@ -91,132 +95,230 @@ struct notify_b { u32 reserved2[8]; }; -inline void rtl838x_create_tx_header(struct p_hdr *h, int dest_port) +void rtl838x_create_tx_header(struct p_hdr *h, int dest_port, int prio) { + prio &= 0x7; + if (dest_port > 0) { - h->cpu_tag[0] = 0x0400; - h->cpu_tag[1] = 0x0200; - h->cpu_tag[2] = 0x0000; - h->cpu_tag[3] = (1 << dest_port) >> 16; - h->cpu_tag[4] = (1 << dest_port) & 0xffff; - } else { - h->cpu_tag[0] = 0; - h->cpu_tag[1] = 0; - h->cpu_tag[2] = 0; - h->cpu_tag[3] = 0; - h->cpu_tag[4] = 0; + // cpu_tag[0] is reserved on the RTL83XX SoCs + h->cpu_tag[1] = 0x0400; + h->cpu_tag[2] = 0x0200; + h->cpu_tag[3] = 0x0000; + h->cpu_tag[4] = BIT(dest_port) >> 16; + h->cpu_tag[5] = BIT(dest_port) & 0xffff; + // Set internal priority and AS_PRIO + if (prio >= 0) + h->cpu_tag[2] |= (prio | 0x8) << 12; } } -inline void rtl839x_create_tx_header(struct p_hdr *h, int dest_port) +void rtl839x_create_tx_header(struct p_hdr *h, int dest_port, int prio) { + prio &= 0x7; + if (dest_port > 0) { - h->cpu_tag[0] = 0x0100; - h->cpu_tag[1] = ((1 << (dest_port - 32)) >> 16) | (1 << 21); - h->cpu_tag[2] = (1 << (dest_port - 32)) & 0xffff; - h->cpu_tag[3] = (1 << dest_port) >> 16; - h->cpu_tag[4] = (1 << dest_port) & 0xffff; - } else { - h->cpu_tag[0] = 0; - h->cpu_tag[1] = 0; - h->cpu_tag[2] = 0; - h->cpu_tag[3] = 0; - h->cpu_tag[4] = 0; + // cpu_tag[0] is reserved on the RTL83XX SoCs + h->cpu_tag[1] = 0x0100; + h->cpu_tag[2] = h->cpu_tag[3] = h->cpu_tag[4] = h->cpu_tag[5] = 0; + if (dest_port >= 32) { + dest_port -= 32; + h->cpu_tag[2] = BIT(dest_port) >> 16; + h->cpu_tag[3] = BIT(dest_port) & 0xffff; + } else { + h->cpu_tag[4] = BIT(dest_port) >> 16; + h->cpu_tag[5] = BIT(dest_port) & 0xffff; + } + h->cpu_tag[6] |= BIT(21); // Enable destination port mask use + // Set internal priority and AS_PRIO + if (prio >= 0) + h->cpu_tag[1] |= prio | BIT(3); } } +void rtl930x_create_tx_header(struct p_hdr *h, int dest_port, int prio) +{ + h->cpu_tag[0] = 0x8000; + h->cpu_tag[1] = 0; // TODO: Fill port and prio + h->cpu_tag[2] = 0; + h->cpu_tag[3] = 0; + h->cpu_tag[4] = 0; + h->cpu_tag[5] = 0; + h->cpu_tag[6] = 0; + h->cpu_tag[7] = 0xffff; +} + +void rtl931x_create_tx_header(struct p_hdr *h, int dest_port, int prio) +{ + h->cpu_tag[0] = 0x8000; + h->cpu_tag[1] = 0; // TODO: Fill port and prio + h->cpu_tag[2] = 0; + h->cpu_tag[3] = 0; + h->cpu_tag[4] = 0; + h->cpu_tag[5] = 0; + h->cpu_tag[6] = 0; + h->cpu_tag[7] = 0xffff; +} + +struct rtl838x_rx_q { + int id; + struct rtl838x_eth_priv *priv; + struct napi_struct napi; +}; + struct rtl838x_eth_priv { struct net_device *netdev; struct platform_device *pdev; void *membase; spinlock_t lock; struct mii_bus *mii_bus; - struct napi_struct napi; + struct rtl838x_rx_q rx_qs[MAX_RXRINGS]; struct phylink *phylink; struct phylink_config phylink_config; u16 id; u16 family_id; const struct rtl838x_reg *r; u8 cpu_port; - u8 port_mask; u32 lastEvent; -}; - -static const struct rtl838x_reg rtl838x_reg = { - .mac_port_ctrl = rtl838x_mac_port_ctrl, - .dma_if_intr_sts = RTL838X_DMA_IF_INTR_STS, - .dma_if_intr_msk = RTL838X_DMA_IF_INTR_MSK, - .dma_if_ctrl = RTL838X_DMA_IF_CTRL, - .mac_force_mode_ctrl = rtl838x_mac_force_mode_ctrl, - .dma_rx_base = rtl838x_dma_rx_base, - .dma_tx_base = rtl838x_dma_tx_base, - .dma_if_rx_ring_size = rtl838x_dma_if_rx_ring_size, - .dma_if_rx_ring_cntr = rtl838x_dma_if_rx_ring_cntr, - .dma_if_rx_cur = rtl838x_dma_if_rx_cur, - .rst_glb_ctrl = RTL838X_RST_GLB_CTRL_0, - .get_mac_link_sts = rtl838x_get_mac_link_sts, - .get_mac_link_dup_sts = rtl838x_get_mac_link_dup_sts, - .get_mac_link_spd_sts = rtl838x_get_mac_link_spd_sts, - .get_mac_rx_pause_sts = rtl838x_get_mac_rx_pause_sts, - .get_mac_tx_pause_sts = rtl838x_get_mac_tx_pause_sts, - .mac = RTL838X_MAC, - .l2_tbl_flush_ctrl = RTL838X_L2_TBL_FLUSH_CTRL, -}; - -static const struct rtl838x_reg rtl839x_reg = { - .mac_port_ctrl = rtl839x_mac_port_ctrl, - .dma_if_intr_sts = RTL839X_DMA_IF_INTR_STS, - .dma_if_intr_msk = RTL839X_DMA_IF_INTR_MSK, - .dma_if_ctrl = RTL839X_DMA_IF_CTRL, - .mac_force_mode_ctrl = rtl839x_mac_force_mode_ctrl, - .dma_rx_base = rtl839x_dma_rx_base, - .dma_tx_base = rtl839x_dma_tx_base, - .dma_if_rx_ring_size = rtl839x_dma_if_rx_ring_size, - .dma_if_rx_ring_cntr = rtl839x_dma_if_rx_ring_cntr, - .dma_if_rx_cur = rtl839x_dma_if_rx_cur, - .rst_glb_ctrl = RTL839X_RST_GLB_CTRL, - .get_mac_link_sts = rtl839x_get_mac_link_sts, - .get_mac_link_dup_sts = rtl839x_get_mac_link_dup_sts, - .get_mac_link_spd_sts = rtl839x_get_mac_link_spd_sts, - .get_mac_rx_pause_sts = rtl839x_get_mac_rx_pause_sts, - .get_mac_tx_pause_sts = rtl839x_get_mac_tx_pause_sts, - .mac = RTL839X_MAC, - .l2_tbl_flush_ctrl = RTL839X_L2_TBL_FLUSH_CTRL, + u16 rxrings; + u16 rxringlen; }; extern int rtl838x_phy_init(struct rtl838x_eth_priv *priv); extern int rtl838x_read_sds_phy(int phy_addr, int phy_reg); extern int rtl839x_read_sds_phy(int phy_addr, int phy_reg); extern int rtl839x_write_sds_phy(int phy_addr, int phy_reg, u16 v); +extern int rtl930x_read_sds_phy(int phy_addr, int page, int phy_reg); +extern int rtl930x_write_sds_phy(int phy_addr, int page, int phy_reg, u16 v); +extern int rtl930x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val); +extern int rtl930x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val); + +/* + * On the RTL93XX, the RTL93XX_DMA_IF_RX_RING_CNTR track the fill level of + * the rings. Writing x into these registers substracts x from its content. + * When the content reaches the ring size, the ASIC no longer adds + * packets to this receive queue. + */ +void rtl838x_update_cntr(int r, int released) +{ + // This feature is not available on RTL838x SoCs +} + +void rtl839x_update_cntr(int r, int released) +{ + // This feature is not available on RTL839x SoCs +} + +void rtl930x_update_cntr(int r, int released) +{ + int pos = (r % 3) * 10; + u32 reg = RTL930X_DMA_IF_RX_RING_CNTR + ((r / 3) << 2); + u32 v = sw_r32(reg); + + v = (v >> pos) & 0x3ff; + pr_debug("RX: Work done %d, old value: %d, pos %d, reg %04x\n", released, v, pos, reg); + sw_w32_mask(0x3ff << pos, released << pos, reg); + sw_w32(v, reg); +} + +void rtl931x_update_cntr(int r, int released) +{ + int pos = (r % 3) * 10; + u32 reg = RTL931X_DMA_IF_RX_RING_CNTR + ((r / 3) << 2); + + sw_w32_mask(0x3ff << pos, released << pos, reg); +} + +struct dsa_tag { + u8 reason; + u8 queue; + u16 port; + u8 l2_offloaded; + u8 prio; +}; + +bool rtl838x_decode_tag(struct p_hdr *h, struct dsa_tag *t) +{ + t->reason = h->cpu_tag[3] & 0xf; + if (t->reason != 15) + pr_debug("Reason: %d\n", t->reason); + t->queue = (h->cpu_tag[0] & 0xe0) >> 5; + if (t->reason != 4) // NIC_RX_REASON_SPECIAL_TRAP + t->l2_offloaded = 1; + else + t->l2_offloaded = 0; + t->port = h->cpu_tag[1] & 0x1f; + + return t->l2_offloaded; +} + +bool rtl839x_decode_tag(struct p_hdr *h, struct dsa_tag *t) +{ + t->reason = h->cpu_tag[4] & 0x1f; + if (t->reason != 31) + pr_debug("Reason: %d\n", t->reason); + t->queue = (h->cpu_tag[3] & 0xe000) >> 13; + if ((t->reason != 7) && (t->reason != 8)) // NIC_RX_REASON_RMA_USR + t->l2_offloaded = 1; + else + t->l2_offloaded = 0; + + t->port = h->cpu_tag[1] & 0x3f; + + return t->l2_offloaded; +} + +bool rtl931x_decode_tag(struct p_hdr *h, struct dsa_tag *t) +{ + t->reason = h->cpu_tag[7] & 0x3f; + pr_debug("Reason %d\n", t->reason); + t->queue = (h->cpu_tag[2] >> 11) & 0x1f; + if (t->reason >= 19 && t->reason <= 27) + t->l2_offloaded = 0; + else + t->l2_offloaded = 1; + t->port = (h->cpu_tag[0] >> 8) & 0x3f; + + return t->l2_offloaded; +} + +bool rtl930x_decode_tag(struct p_hdr *h, struct dsa_tag *t) +{ + rtl931x_decode_tag(h, t); + t->port &= 0x1f; + return t->l2_offloaded; +} /* * Discard the RX ring-buffers, called as part of the net-ISR * when the buffer runs over * Caller needs to hold priv->lock */ -static void rtl838x_rb_cleanup(struct rtl838x_eth_priv *priv) +static void rtl838x_rb_cleanup(struct rtl838x_eth_priv *priv, int status) { int r; u32 *last; struct p_hdr *h; struct ring_b *ring = priv->membase; - for (r = 0; r < RXRINGS; r++) { - last = (u32 *)KSEG1ADDR(sw_r32(priv->r->dma_if_rx_cur(r))); + for (r = 0; r < priv->rxrings; r++) { + pr_debug("In %s working on r: %d\n", __func__, r); + last = (u32 *)KSEG1ADDR(sw_r32(priv->r->dma_if_rx_cur + r * 4)); do { if ((ring->rx_r[r][ring->c_rx[r]] & 0x1)) break; + pr_debug("Got something: %d\n", ring->c_rx[r]); h = &ring->rx_header[r][ring->c_rx[r]]; + memset(h, 0, sizeof(struct p_hdr)); h->buf = (u8 *)KSEG1ADDR(ring->rx_space - + r * ring->c_rx[r] * RING_BUFFER); + + r * priv->rxringlen * RING_BUFFER + + ring->c_rx[r] * RING_BUFFER); h->size = RING_BUFFER; - h->len = 0; /* make sure the header is visible to the ASIC */ mb(); ring->rx_r[r][ring->c_rx[r]] = KSEG1ADDR(h) | 0x1 - | (ring->c_rx[r] == (RXRINGLEN - 1) ? WRAP : 0x1); - ring->c_rx[r] = (ring->c_rx[r] + 1) % RXRINGLEN; + | (ring->c_rx[r] == (priv->rxringlen - 1) ? WRAP : 0x1); + ring->c_rx[r] = (ring->c_rx[r] + 1) % priv->rxringlen; } while (&ring->rx_r[r][ring->c_rx[r]] != last); } } @@ -229,25 +331,25 @@ struct fdb_update_work { void rtl838x_fdb_sync(struct work_struct *work) { - const struct fdb_update_work *uw = - container_of(work, struct fdb_update_work, work); - struct switchdev_notifier_fdb_info info; - u8 addr[ETH_ALEN]; - int i = 0; - int action; + const struct fdb_update_work *uw = + container_of(work, struct fdb_update_work, work); + struct switchdev_notifier_fdb_info info; + u8 addr[ETH_ALEN]; + int i = 0; + int action; - while (uw->macs[i]) { - action = (uw->macs[i] & (1ULL << 63)) ? SWITCHDEV_FDB_ADD_TO_BRIDGE - : SWITCHDEV_FDB_DEL_TO_BRIDGE; - u64_to_ether_addr(uw->macs[i] & 0xffffffffffffULL, addr); - info.addr = &addr[0]; - info.vid = 0; - info.offloaded = 1; - pr_debug("FDB entry %d: %llx, action %d\n", i, uw->macs[0], action); - call_switchdev_notifiers(action, uw->ndev, &info.info, NULL); - i++; - } - kfree(work); + while (uw->macs[i]) { + action = (uw->macs[i] & (1ULL << 63)) ? SWITCHDEV_FDB_ADD_TO_BRIDGE + : SWITCHDEV_FDB_DEL_TO_BRIDGE; + u64_to_ether_addr(uw->macs[i] & 0xffffffffffffULL, addr); + info.addr = &addr[0]; + info.vid = 0; + info.offloaded = 1; + pr_debug("FDB entry %d: %llx, action %d\n", i, uw->macs[0], action); + call_switchdev_notifiers(action, uw->ndev, &info.info, NULL); + i++; + } + kfree(work); } static void rtl839x_l2_notification_handler(struct rtl838x_eth_priv *priv) @@ -288,19 +390,20 @@ static void rtl839x_l2_notification_handler(struct rtl838x_eth_priv *priv) priv->lastEvent = e; } -static irqreturn_t rtl838x_net_irq(int irq, void *dev_id) +static irqreturn_t rtl83xx_net_irq(int irq, void *dev_id) { struct net_device *dev = dev_id; struct rtl838x_eth_priv *priv = netdev_priv(dev); u32 status = sw_r32(priv->r->dma_if_intr_sts); bool triggered = false; u32 atk = sw_r32(RTL838X_ATK_PRVNT_STS); + int i; u32 storm_uc = sw_r32(RTL838X_STORM_CTRL_PORT_UC_EXCEED); u32 storm_mc = sw_r32(RTL838X_STORM_CTRL_PORT_MC_EXCEED); u32 storm_bc = sw_r32(RTL838X_STORM_CTRL_PORT_BC_EXCEED); + pr_debug("IRQ: %08x\n", status); if (storm_uc || storm_mc || storm_bc) { - pr_warn("Storm control UC: %08x, MC: %08x, BC: %08x\n", storm_uc, storm_mc, storm_bc); @@ -325,20 +428,23 @@ static irqreturn_t rtl838x_net_irq(int irq, void *dev_id) /* RX interrupt */ if (status & 0x0ff00) { - /* Disable RX interrupt */ - if (triggered) - pr_info("RX\n"); - sw_w32_mask(0xff00, 0, priv->r->dma_if_intr_msk); - sw_w32(0x0000ff00, priv->r->dma_if_intr_sts); - napi_schedule(&priv->napi); + /* ACK and disable RX interrupt for this ring */ + sw_w32_mask(0xff00 & status, 0, priv->r->dma_if_intr_msk); + sw_w32(0x0000ff00 & status, priv->r->dma_if_intr_sts); + for (i = 0; i < priv->rxrings; i++) { + if (status & BIT(i + 8)) { + pr_debug("Scheduling queue: %d\n", i); + napi_schedule(&priv->rx_qs[i].napi); + } + } } /* RX buffer overrun */ if (status & 0x000ff) { pr_info("RX buffer overrun: status %x, mask: %x\n", status, sw_r32(priv->r->dma_if_intr_msk)); - sw_w32(0x000000ff, priv->r->dma_if_intr_sts); - rtl838x_rb_cleanup(priv); + sw_w32(status, priv->r->dma_if_intr_sts); + rtl838x_rb_cleanup(priv, status & 0xff); } if (priv->family_id == RTL8390_FAMILY_ID && status & 0x00100000) { @@ -360,14 +466,185 @@ static irqreturn_t rtl838x_net_irq(int irq, void *dev_id) return IRQ_HANDLED; } +static irqreturn_t rtl93xx_net_irq(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct rtl838x_eth_priv *priv = netdev_priv(dev); + u32 status_rx_r = sw_r32(priv->r->dma_if_intr_rx_runout_sts); + u32 status_rx = sw_r32(priv->r->dma_if_intr_rx_done_sts); + u32 status_tx = sw_r32(priv->r->dma_if_intr_tx_done_sts); + int i; + + pr_debug("In %s, status_tx: %08x, status_rx: %08x, status_rx_r: %08x\n", + __func__, status_tx, status_rx, status_rx_r); + spin_lock(&priv->lock); + + /* Ignore TX interrupt */ + if (status_tx) { + /* Clear ISR */ + pr_debug("TX done\n"); + sw_w32(status_tx, priv->r->dma_if_intr_tx_done_sts); + } + + /* RX interrupt */ + if (status_rx) { + pr_debug("RX IRQ\n"); + /* ACK and disable RX interrupt for given rings */ + sw_w32(status_rx, priv->r->dma_if_intr_rx_done_sts); + sw_w32_mask(status_rx, 0, priv->r->dma_if_intr_rx_done_msk); + for (i = 0; i < priv->rxrings; i++) { + if (status_rx & BIT(i)) { + pr_debug("Scheduling queue: %d\n", i); + napi_schedule(&priv->rx_qs[i].napi); + } + } + } + + /* RX buffer overrun */ + if (status_rx_r) { + pr_debug("RX buffer overrun: status %x, mask: %x\n", + status_rx_r, sw_r32(priv->r->dma_if_intr_rx_runout_msk)); + sw_w32(status_rx_r, priv->r->dma_if_intr_rx_runout_sts); + rtl838x_rb_cleanup(priv, status_rx_r); + } + + spin_unlock(&priv->lock); + return IRQ_HANDLED; +} + +static const struct rtl838x_reg rtl838x_reg = { + .net_irq = rtl83xx_net_irq, + .mac_port_ctrl = rtl838x_mac_port_ctrl, + .dma_if_intr_sts = RTL838X_DMA_IF_INTR_STS, + .dma_if_intr_msk = RTL838X_DMA_IF_INTR_MSK, + .dma_if_ctrl = RTL838X_DMA_IF_CTRL, + .mac_force_mode_ctrl = RTL838X_MAC_FORCE_MODE_CTRL, + .dma_rx_base = RTL838X_DMA_RX_BASE, + .dma_tx_base = RTL838X_DMA_TX_BASE, + .dma_if_rx_ring_size = rtl838x_dma_if_rx_ring_size, + .dma_if_rx_ring_cntr = rtl838x_dma_if_rx_ring_cntr, + .dma_if_rx_cur = RTL838X_DMA_IF_RX_CUR, + .rst_glb_ctrl = RTL838X_RST_GLB_CTRL_0, + .get_mac_link_sts = rtl838x_get_mac_link_sts, + .get_mac_link_dup_sts = rtl838x_get_mac_link_dup_sts, + .get_mac_link_spd_sts = rtl838x_get_mac_link_spd_sts, + .get_mac_rx_pause_sts = rtl838x_get_mac_rx_pause_sts, + .get_mac_tx_pause_sts = rtl838x_get_mac_tx_pause_sts, + .mac = RTL838X_MAC, + .l2_tbl_flush_ctrl = RTL838X_L2_TBL_FLUSH_CTRL, + .update_cntr = rtl838x_update_cntr, + .create_tx_header = rtl838x_create_tx_header, + .decode_tag = rtl838x_decode_tag, +}; + +static const struct rtl838x_reg rtl839x_reg = { + .net_irq = rtl83xx_net_irq, + .mac_port_ctrl = rtl839x_mac_port_ctrl, + .dma_if_intr_sts = RTL839X_DMA_IF_INTR_STS, + .dma_if_intr_msk = RTL839X_DMA_IF_INTR_MSK, + .dma_if_ctrl = RTL839X_DMA_IF_CTRL, + .mac_force_mode_ctrl = RTL839X_MAC_FORCE_MODE_CTRL, + .dma_rx_base = RTL839X_DMA_RX_BASE, + .dma_tx_base = RTL839X_DMA_TX_BASE, + .dma_if_rx_ring_size = rtl839x_dma_if_rx_ring_size, + .dma_if_rx_ring_cntr = rtl839x_dma_if_rx_ring_cntr, + .dma_if_rx_cur = RTL839X_DMA_IF_RX_CUR, + .rst_glb_ctrl = RTL839X_RST_GLB_CTRL, + .get_mac_link_sts = rtl839x_get_mac_link_sts, + .get_mac_link_dup_sts = rtl839x_get_mac_link_dup_sts, + .get_mac_link_spd_sts = rtl839x_get_mac_link_spd_sts, + .get_mac_rx_pause_sts = rtl839x_get_mac_rx_pause_sts, + .get_mac_tx_pause_sts = rtl839x_get_mac_tx_pause_sts, + .mac = RTL839X_MAC, + .l2_tbl_flush_ctrl = RTL839X_L2_TBL_FLUSH_CTRL, + .update_cntr = rtl839x_update_cntr, + .create_tx_header = rtl839x_create_tx_header, + .decode_tag = rtl839x_decode_tag, +}; + +static const struct rtl838x_reg rtl930x_reg = { + .net_irq = rtl93xx_net_irq, + .mac_port_ctrl = rtl930x_mac_port_ctrl, + .dma_if_intr_rx_runout_sts = RTL930X_DMA_IF_INTR_RX_RUNOUT_STS, + .dma_if_intr_rx_done_sts = RTL930X_DMA_IF_INTR_RX_DONE_STS, + .dma_if_intr_tx_done_sts = RTL930X_DMA_IF_INTR_TX_DONE_STS, + .dma_if_intr_rx_runout_msk = RTL930X_DMA_IF_INTR_RX_RUNOUT_MSK, + .dma_if_intr_rx_done_msk = RTL930X_DMA_IF_INTR_RX_DONE_MSK, + .dma_if_intr_tx_done_msk = RTL930X_DMA_IF_INTR_TX_DONE_MSK, + .l2_ntfy_if_intr_sts = RTL930X_L2_NTFY_IF_INTR_STS, + .l2_ntfy_if_intr_msk = RTL930X_L2_NTFY_IF_INTR_MSK, + .dma_if_ctrl = RTL930X_DMA_IF_CTRL, + .mac_force_mode_ctrl = RTL930X_MAC_FORCE_MODE_CTRL, + .dma_rx_base = RTL930X_DMA_RX_BASE, + .dma_tx_base = RTL930X_DMA_TX_BASE, + .dma_if_rx_ring_size = rtl930x_dma_if_rx_ring_size, + .dma_if_rx_ring_cntr = rtl930x_dma_if_rx_ring_cntr, + .dma_if_rx_cur = RTL930X_DMA_IF_RX_CUR, + .rst_glb_ctrl = RTL930X_RST_GLB_CTRL_0, + .get_mac_link_sts = rtl930x_get_mac_link_sts, + .get_mac_link_dup_sts = rtl930x_get_mac_link_dup_sts, + .get_mac_link_spd_sts = rtl930x_get_mac_link_spd_sts, + .get_mac_rx_pause_sts = rtl930x_get_mac_rx_pause_sts, + .get_mac_tx_pause_sts = rtl930x_get_mac_tx_pause_sts, + .mac = RTL930X_MAC_L2_ADDR_CTRL, + .l2_tbl_flush_ctrl = RTL930X_L2_TBL_FLUSH_CTRL, + .update_cntr = rtl930x_update_cntr, + .create_tx_header = rtl930x_create_tx_header, + .decode_tag = rtl930x_decode_tag, +}; + +static const struct rtl838x_reg rtl931x_reg = { + .net_irq = rtl93xx_net_irq, + .mac_port_ctrl = rtl931x_mac_port_ctrl, + .dma_if_intr_rx_runout_sts = RTL931X_DMA_IF_INTR_RX_RUNOUT_STS, + .dma_if_intr_rx_done_sts = RTL931X_DMA_IF_INTR_RX_DONE_STS, + .dma_if_intr_tx_done_sts = RTL931X_DMA_IF_INTR_TX_DONE_STS, + .dma_if_intr_rx_runout_msk = RTL931X_DMA_IF_INTR_RX_RUNOUT_MSK, + .dma_if_intr_rx_done_msk = RTL931X_DMA_IF_INTR_RX_DONE_MSK, + .dma_if_intr_tx_done_msk = RTL931X_DMA_IF_INTR_TX_DONE_MSK, + .l2_ntfy_if_intr_sts = RTL931X_L2_NTFY_IF_INTR_STS, + .l2_ntfy_if_intr_msk = RTL931X_L2_NTFY_IF_INTR_MSK, + .dma_if_ctrl = RTL931X_DMA_IF_CTRL, + .mac_force_mode_ctrl = RTL931X_MAC_FORCE_MODE_CTRL, + .dma_rx_base = RTL931X_DMA_RX_BASE, + .dma_tx_base = RTL931X_DMA_TX_BASE, + .dma_if_rx_ring_size = rtl931x_dma_if_rx_ring_size, + .dma_if_rx_ring_cntr = rtl931x_dma_if_rx_ring_cntr, + .dma_if_rx_cur = RTL931X_DMA_IF_RX_CUR, + .rst_glb_ctrl = RTL931X_RST_GLB_CTRL, + .get_mac_link_sts = rtl931x_get_mac_link_sts, + .get_mac_link_dup_sts = rtl931x_get_mac_link_dup_sts, + .get_mac_link_spd_sts = rtl931x_get_mac_link_spd_sts, + .get_mac_rx_pause_sts = rtl931x_get_mac_rx_pause_sts, + .get_mac_tx_pause_sts = rtl931x_get_mac_tx_pause_sts, + .mac = RTL931X_MAC_L2_ADDR_CTRL, + .l2_tbl_flush_ctrl = RTL931X_L2_TBL_FLUSH_CTRL, + .update_cntr = rtl931x_update_cntr, + .create_tx_header = rtl931x_create_tx_header, + .decode_tag = rtl931x_decode_tag, +}; + static void rtl838x_hw_reset(struct rtl838x_eth_priv *priv) { u32 int_saved, nbuf; - + int i, pos; + pr_info("RESETTING %x, CPU_PORT %d\n", priv->family_id, priv->cpu_port); - /* Stop TX/RX */ sw_w32_mask(0x3, 0, priv->r->mac_port_ctrl(priv->cpu_port)); - mdelay(500); + mdelay(100); + + /* Disable and clear interrupts */ + if (priv->family_id == RTL9300_FAMILY_ID || priv->family_id == RTL9310_FAMILY_ID) { + sw_w32(0x00000000, priv->r->dma_if_intr_rx_runout_msk); + sw_w32(0xffffffff, priv->r->dma_if_intr_rx_runout_sts); + sw_w32(0x00000000, priv->r->dma_if_intr_rx_done_msk); + sw_w32(0xffffffff, priv->r->dma_if_intr_rx_done_sts); + sw_w32(0x00000000, priv->r->dma_if_intr_tx_done_msk); + sw_w32(0x0000000f, priv->r->dma_if_intr_tx_done_sts); + } else { + sw_w32(0x00000000, priv->r->dma_if_intr_msk); + sw_w32(0xffffffff, priv->r->dma_if_intr_sts); + } if (priv->family_id == RTL8390_FAMILY_ID) { /* Preserve L2 notification and NBUF settings */ @@ -382,17 +659,30 @@ static void rtl838x_hw_reset(struct rtl838x_eth_priv *priv) sw_w32(0xffffffff, priv->r->dma_if_intr_sts); } - /* Reset NIC and Queue */ - sw_w32(0x08, priv->r->rst_glb_ctrl); + /* Reset NIC */ + if (priv->family_id == RTL9300_FAMILY_ID || priv->family_id == RTL9310_FAMILY_ID) + sw_w32(0x4, priv->r->rst_glb_ctrl); + else + sw_w32(0x8, priv->r->rst_glb_ctrl); + + do { /* Wait for reset of NIC and Queues done */ + udelay(20); + } while (sw_r32(priv->r->rst_glb_ctrl) & 0xc); + mdelay(100); + + /* Setup Head of Line */ + if (priv->family_id == RTL8380_FAMILY_ID) + sw_w32(0, RTL838X_DMA_IF_RX_RING_SIZE); // Disabled on RTL8380 if (priv->family_id == RTL8390_FAMILY_ID) sw_w32(0xffffffff, RTL839X_DMA_IF_RX_RING_CNTR); - do { /* Reset NIC */ - udelay(20); - } while (sw_r32(priv->r->rst_glb_ctrl) & 0x08); - do { /* Reset Queues */ - udelay(20); - } while (sw_r32(priv->r->rst_glb_ctrl) & 0x04); - mdelay(100); + if (priv->family_id == RTL9300_FAMILY_ID) { + for (i = 0; i < priv->rxrings; i++) { + pos = (i % 3) * 10; + sw_w32_mask(0x3ff << pos, 0, priv->r->dma_if_rx_ring_size(i)); + sw_w32_mask(0x3ff << pos, priv->rxringlen, + priv->r->dma_if_rx_ring_cntr(i)); + } + } /* Re-enable link change interrupt */ if (priv->family_id == RTL8390_FAMILY_ID) { @@ -405,32 +695,6 @@ static void rtl838x_hw_reset(struct rtl838x_eth_priv *priv) sw_w32_mask(7 << 20, int_saved & (7 << 20), priv->r->dma_if_intr_msk); sw_w32(nbuf, RTL839X_DMA_IF_NBUF_BASE_DESC_ADDR_CTRL); } - - /* Restart TX/RX to CPU port */ - sw_w32_mask(0x0, 0x3, priv->r->mac_port_ctrl(priv->cpu_port)); - - if (priv->family_id == RTL8380_FAMILY_ID) { - /* Set Speed, duplex, flow control - * FORCE_EN | LINK_EN | NWAY_EN | DUP_SEL - * | SPD_SEL = 0b10 | FORCE_FC_EN | PHY_MASTER_SLV_MANUAL_EN - * | MEDIA_SEL - */ - sw_w32(0x6192F, priv->r->mac_force_mode_ctrl(priv->cpu_port)); - /* allow CRC errors on CPU-port */ - sw_w32_mask(0, 0x8, priv->r->mac_port_ctrl(priv->cpu_port)); - } else { - /* CPU port joins Lookup Miss Flooding Portmask */ - sw_w32(0x28000, RTL839X_TBL_ACCESS_L2_CTRL); - sw_w32_mask(0, 0x80000000, RTL839X_TBL_ACCESS_L2_DATA(0)); - sw_w32(0x38000, RTL839X_TBL_ACCESS_L2_CTRL); - - /* Force CPU port link up */ - sw_w32_mask(0, 3, priv->r->mac_force_mode_ctrl(priv->cpu_port)); - } - - /* Disable and clear interrupts */ - sw_w32(0x00000000, priv->r->dma_if_intr_msk); - sw_w32(0xffffffff, priv->r->dma_if_intr_sts); } static void rtl838x_hw_ring_setup(struct rtl838x_eth_priv *priv) @@ -438,11 +702,11 @@ static void rtl838x_hw_ring_setup(struct rtl838x_eth_priv *priv) int i; struct ring_b *ring = priv->membase; - for (i = 0; i < RXRINGS; i++) - sw_w32(KSEG1ADDR(&ring->rx_r[i]), priv->r->dma_rx_base(i)); + for (i = 0; i < priv->rxrings; i++) + sw_w32(KSEG1ADDR(&ring->rx_r[i]), priv->r->dma_rx_base + i * 4); for (i = 0; i < TXRINGS; i++) - sw_w32(KSEG1ADDR(&ring->tx_r[i]), priv->r->dma_tx_base(i)); + sw_w32(KSEG1ADDR(&ring->tx_r[i]), priv->r->dma_tx_base + i * 4); } static void rtl838x_hw_en_rxtx(struct rtl838x_eth_priv *priv) @@ -456,8 +720,19 @@ static void rtl838x_hw_en_rxtx(struct rtl838x_eth_priv *priv) /* Enable RX done, RX overflow and TX done interrupts */ sw_w32(0xfffff, priv->r->dma_if_intr_msk); - /* Enable traffic, engine expects empty FCS field */ + /* Enable DMA, engine expects empty FCS field */ sw_w32_mask(0, RX_EN | TX_EN, priv->r->dma_if_ctrl); + + /* Restart TX/RX to CPU port */ + sw_w32_mask(0x0, 0x3, priv->r->mac_port_ctrl(priv->cpu_port)); + /* Set Speed, duplex, flow control + * FORCE_EN | LINK_EN | NWAY_EN | DUP_SEL + * | SPD_SEL = 0b10 | FORCE_FC_EN | PHY_MASTER_SLV_MANUAL_EN + * | MEDIA_SEL + */ + sw_w32(0x6192F, priv->r->mac_force_mode_ctrl + priv->cpu_port * 4); + /* allow CRC errors on CPU-port */ + sw_w32_mask(0, 0x8, priv->r->mac_port_ctrl(priv->cpu_port)); } static void rtl839x_hw_en_rxtx(struct rtl838x_eth_priv *priv) @@ -468,27 +743,71 @@ static void rtl839x_hw_en_rxtx(struct rtl838x_eth_priv *priv) /* Enable Notify, RX done, RX overflow and TX done interrupts */ sw_w32(0x007fffff, priv->r->dma_if_intr_msk); // Notify IRQ! - /* Enable traffic */ + /* Enable DMA */ sw_w32_mask(0, RX_EN | TX_EN, priv->r->dma_if_ctrl); + + /* Restart TX/RX to CPU port */ + sw_w32_mask(0x0, 0x3, priv->r->mac_port_ctrl(priv->cpu_port)); + + /* CPU port joins Lookup Miss Flooding Portmask */ + // TODO: The code below should also work for the RTL838x + sw_w32(0x28000, RTL839X_TBL_ACCESS_L2_CTRL); + sw_w32_mask(0, 0x80000000, RTL839X_TBL_ACCESS_L2_DATA(0)); + sw_w32(0x38000, RTL839X_TBL_ACCESS_L2_CTRL); + + /* Force CPU port link up */ + sw_w32_mask(0, 3, priv->r->mac_force_mode_ctrl + priv->cpu_port * 4); } -static void rtl838x_setup_ring_buffer(struct ring_b *ring) +static void rtl93xx_hw_en_rxtx(struct rtl838x_eth_priv *priv) +{ + int i, pos; + u32 v; + + /* Setup CPU-Port: RX Buffer truncated at 1600 Bytes */ + sw_w32(0x06400040, priv->r->dma_if_ctrl); + + for (i = 0; i < priv->rxrings; i++) { + pos = (i % 3) * 10; + sw_w32_mask(0x3ff << pos, priv->rxringlen << pos, priv->r->dma_if_rx_ring_size(i)); + + // Some SoCs have issues with missing underflow protection + v = (sw_r32(priv->r->dma_if_rx_ring_cntr(i)) >> pos) & 0x3ff; + sw_w32_mask(0x3ff << pos, v, priv->r->dma_if_rx_ring_cntr(i)); + } + + /* Enable Notify, RX done, RX overflow and TX done interrupts */ + sw_w32(0xffffffff, priv->r->dma_if_intr_rx_runout_msk); + sw_w32(0xffffffff, priv->r->dma_if_intr_rx_done_msk); + sw_w32(0x0000000f, priv->r->dma_if_intr_tx_done_msk); + + /* Enable DMA */ + sw_w32_mask(0, RX_EN_93XX | TX_EN_93XX, priv->r->dma_if_ctrl); + + /* Restart TX/RX to CPU port */ + sw_w32_mask(0x0, 0x3, priv->r->mac_port_ctrl(priv->cpu_port)); + + sw_w32_mask(0, BIT(priv->cpu_port), RTL930X_L2_UNKN_UC_FLD_PMSK); + sw_w32(0x217, priv->r->mac_force_mode_ctrl + priv->cpu_port * 4); +} + +static void rtl838x_setup_ring_buffer(struct rtl838x_eth_priv *priv, struct ring_b *ring) { int i, j; struct p_hdr *h; - for (i = 0; i < RXRINGS; i++) { - for (j = 0; j < RXRINGLEN; j++) { + for (i = 0; i < priv->rxrings; i++) { + for (j = 0; j < priv->rxringlen; j++) { h = &ring->rx_header[i][j]; - h->buf = (u8 *)KSEG1ADDR(ring->rx_space + i * j * RING_BUFFER); - h->reserved = 0; + memset(h, 0, sizeof(struct p_hdr)); + h->buf = (u8 *)KSEG1ADDR(ring->rx_space + + i * priv->rxringlen * RING_BUFFER + + j * RING_BUFFER); h->size = RING_BUFFER; - h->offset = 0; - h->len = 0; - memset(&h->cpu_tag, 0, sizeof(uint16_t[5])); /* All rings owned by switch, last one wraps */ - ring->rx_r[i][j] = KSEG1ADDR(h) | 1 | (j == (RXRINGLEN - 1) ? WRAP : 0); + ring->rx_r[i][j] = KSEG1ADDR(h) | 1 + | (j == (priv->rxringlen - 1) ? WRAP : 0); } ring->c_rx[i] = 0; } @@ -496,12 +815,11 @@ static void rtl838x_setup_ring_buffer(struct ring_b *ring) for (i = 0; i < TXRINGS; i++) { for (j = 0; j < TXRINGLEN; j++) { h = &ring->tx_header[i][j]; - h->buf = (u8 *)KSEG1ADDR(ring->tx_space + i * j * RING_BUFFER); - h->reserved = 0; + memset(h, 0, sizeof(struct p_hdr)); + h->buf = (u8 *)KSEG1ADDR(ring->tx_space + + i * TXRINGLEN * RING_BUFFER + + j * RING_BUFFER); h->size = RING_BUFFER; - h->offset = 0; - h->len = 0; - memset(&h->cpu_tag, 0, sizeof(uint16_t[5])); ring->tx_r[i][j] = KSEG1ADDR(&ring->tx_header[i][j]); } /* Last header is wrapping around */ @@ -535,13 +853,14 @@ static int rtl838x_eth_open(struct net_device *ndev) unsigned long flags; struct rtl838x_eth_priv *priv = netdev_priv(ndev); struct ring_b *ring = priv->membase; - int err; + int i, err; - pr_info("%s called: RX rings %d, TX rings %d\n", __func__, RXRINGS, TXRINGS); + pr_info("%s called: RX rings %d(length %d), TX rings %d(length %d)\n", + __func__, priv->rxrings, priv->rxringlen, TXRINGS, TXRINGLEN); spin_lock_irqsave(&priv->lock, flags); rtl838x_hw_reset(priv); - rtl838x_setup_ring_buffer(ring); + rtl838x_setup_ring_buffer(priv, ring); if (priv->family_id == RTL8390_FAMILY_ID) { rtl839x_setup_notify_ring_buffer(priv); /* Make sure the ring structure is visible to the ASIC */ @@ -550,8 +869,7 @@ static int rtl838x_eth_open(struct net_device *ndev) } rtl838x_hw_ring_setup(priv); - err = request_irq(ndev->irq, rtl838x_net_irq, IRQF_SHARED, - ndev->name, ndev); + err = request_irq(ndev->irq, priv->r->net_irq, IRQF_SHARED, ndev->name, ndev); if (err) { netdev_err(ndev, "%s: could not acquire interrupt: %d\n", __func__, err); @@ -559,21 +877,39 @@ static int rtl838x_eth_open(struct net_device *ndev) } phylink_start(priv->phylink); - napi_enable(&priv->napi); - netif_start_queue(ndev); + for (i = 0; i < priv->rxrings; i++) + napi_enable(&priv->rx_qs[i].napi); - if (priv->family_id == RTL8380_FAMILY_ID) { + switch (priv->family_id) { + case RTL8380_FAMILY_ID: rtl838x_hw_en_rxtx(priv); /* Trap IGMP traffic to CPU-Port */ sw_w32(0x3, RTL838X_SPCL_TRAP_IGMP_CTRL); /* Flush learned FDB entries on link down of a port */ - sw_w32_mask(0, 1 << 7, RTL838X_L2_CTRL_0); - } else { + sw_w32_mask(0, BIT(7), RTL838X_L2_CTRL_0); + break; + case RTL8390_FAMILY_ID: rtl839x_hw_en_rxtx(priv); sw_w32(0x3, RTL839X_SPCL_TRAP_IGMP_CTRL); - sw_w32_mask(0, 1 << 7, RTL839X_L2_CTRL_0); + /* Flush learned FDB entries on link down of a port */ + sw_w32_mask(0, BIT(7), RTL839X_L2_CTRL_0); + break; + case RTL9300_FAMILY_ID: + rtl93xx_hw_en_rxtx(priv); + /* Flush learned FDB entries on link down of a port */ + sw_w32_mask(0, BIT(7), RTL930X_L2_CTRL); + sw_w32_mask(BIT(28), 0, RTL930X_L2_PORT_SABLK_CTRL); + sw_w32_mask(BIT(28), 0, RTL930X_L2_PORT_DABLK_CTRL); + break; + + case RTL9310_FAMILY_ID: + rtl93xx_hw_en_rxtx(priv); +// TODO: Add trapping of IGMP frames to CPU-port + break; } + netif_tx_start_all_queues(ndev); + spin_unlock_irqrestore(&priv->lock, flags); return 0; @@ -581,10 +917,20 @@ static int rtl838x_eth_open(struct net_device *ndev) static void rtl838x_hw_stop(struct rtl838x_eth_priv *priv) { - u32 force_mac = priv->family_id == RTL8380_FAMILY_ID ? 0x6192D : 0x75; + u32 force_mac = priv->family_id == RTL8380_FAMILY_ID ? 0x6192C : 0x75; u32 clear_irq = priv->family_id == RTL8380_FAMILY_ID ? 0x000fffff : 0x007fffff; int i; + // Disable RX/TX from/to CPU-port + sw_w32_mask(0x3, 0, priv->r->mac_port_ctrl(priv->cpu_port)); + + /* Disable traffic */ + if (priv->family_id == RTL9300_FAMILY_ID || priv->family_id == RTL9310_FAMILY_ID) + sw_w32_mask(RX_EN_93XX | TX_EN_93XX, 0, priv->r->dma_if_ctrl); + else + sw_w32_mask(RX_EN | TX_EN, 0, priv->r->dma_if_ctrl); + mdelay(200); // Test, whether this is needed + /* Block all ports */ if (priv->family_id == RTL8380_FAMILY_ID) { sw_w32(0x03000000, RTL838X_TBL_ACCESS_DATA_0(0)); @@ -598,24 +944,33 @@ static void rtl838x_hw_stop(struct rtl838x_eth_priv *priv) sw_w32(1 << 26 | 1 << 23 | i << 5, priv->r->l2_tbl_flush_ctrl); do { } while (sw_r32(priv->r->l2_tbl_flush_ctrl) & (1 << 26)); } - } else { + } else if (priv->family_id == RTL8390_FAMILY_ID) { for (i = 0; i <= priv->cpu_port; i++) { sw_w32(1 << 28 | 1 << 25 | i << 5, priv->r->l2_tbl_flush_ctrl); do { } while (sw_r32(priv->r->l2_tbl_flush_ctrl) & (1 << 28)); } } + // TODO: L2 flush register is 64 bit on RTL931X and 930X /* CPU-Port: Link down */ - sw_w32(force_mac, priv->r->mac_force_mode_ctrl(priv->cpu_port)); + if (priv->family_id == RTL8380_FAMILY_ID || priv->family_id == RTL8390_FAMILY_ID) + sw_w32(force_mac, priv->r->mac_force_mode_ctrl + priv->cpu_port * 4); + else + sw_w32_mask(0x3, 0, priv->r->mac_force_mode_ctrl + priv->cpu_port *4); mdelay(100); - /* Disable traffic */ - sw_w32_mask(RX_EN | TX_EN, 0, priv->r->dma_if_ctrl); - mdelay(200); // Test, whether this is needed - /* Disable all TX/RX interrupts */ - sw_w32(0x00000000, priv->r->dma_if_intr_msk); - sw_w32(clear_irq, priv->r->dma_if_intr_sts); + if (priv->family_id == RTL9300_FAMILY_ID || priv->family_id == RTL9310_FAMILY_ID) { + sw_w32(0x00000000, priv->r->dma_if_intr_rx_runout_msk); + sw_w32(0xffffffff, priv->r->dma_if_intr_rx_runout_sts); + sw_w32(0x00000000, priv->r->dma_if_intr_rx_done_msk); + sw_w32(0xffffffff, priv->r->dma_if_intr_rx_done_sts); + sw_w32(0x00000000, priv->r->dma_if_intr_tx_done_msk); + sw_w32(0x0000000f, priv->r->dma_if_intr_tx_done_sts); + } else { + sw_w32(0x00000000, priv->r->dma_if_intr_msk); + sw_w32(clear_irq, priv->r->dma_if_intr_sts); + } /* Disable TX/RX DMA */ sw_w32(0x00000000, priv->r->dma_if_ctrl); @@ -625,6 +980,7 @@ static void rtl838x_hw_stop(struct rtl838x_eth_priv *priv) static int rtl838x_eth_stop(struct net_device *ndev) { unsigned long flags; + int i; struct rtl838x_eth_priv *priv = netdev_priv(ndev); pr_info("in %s\n", __func__); @@ -633,8 +989,12 @@ static int rtl838x_eth_stop(struct net_device *ndev) phylink_stop(priv->phylink); rtl838x_hw_stop(priv); free_irq(ndev->irq, ndev); - napi_disable(&priv->napi); - netif_stop_queue(ndev); + + for (i = 0; i < priv->rxrings; i++) + napi_disable(&priv->rx_qs[i].napi); + + netif_tx_stop_all_queues(ndev); + spin_unlock_irqrestore(&priv->lock, flags); return 0; @@ -680,12 +1040,50 @@ static void rtl838x_eth_set_multicast_list(struct net_device *ndev) } } +static void rtl930x_eth_set_multicast_list(struct net_device *ndev) +{ + if (!(ndev->flags & (IFF_PROMISC | IFF_ALLMULTI))) { + sw_w32(0x0, RTL930X_RMA_CTRL_0); + sw_w32(0x0, RTL930X_RMA_CTRL_1); + sw_w32(0x0, RTL930X_RMA_CTRL_2); + } + if (ndev->flags & IFF_ALLMULTI) { + sw_w32(0x7fffffff, RTL930X_RMA_CTRL_0); + sw_w32(0x7fffffff, RTL930X_RMA_CTRL_1); + sw_w32(0x7fffffff, RTL930X_RMA_CTRL_2); + } + if (ndev->flags & IFF_PROMISC) { + sw_w32(0x7fffffff, RTL930X_RMA_CTRL_0); + sw_w32(0x7fffffff, RTL930X_RMA_CTRL_1); + sw_w32(0x7fffffff, RTL930X_RMA_CTRL_2); + } +} + +static void rtl931x_eth_set_multicast_list(struct net_device *ndev) +{ + if (!(ndev->flags & (IFF_PROMISC | IFF_ALLMULTI))) { + sw_w32(0x0, RTL931X_RMA_CTRL_0); + sw_w32(0x0, RTL931X_RMA_CTRL_1); + sw_w32(0x0, RTL931X_RMA_CTRL_2); + } + if (ndev->flags & IFF_ALLMULTI) { + sw_w32(0x7fffffff, RTL931X_RMA_CTRL_0); + sw_w32(0x7fffffff, RTL931X_RMA_CTRL_1); + sw_w32(0x7fffffff, RTL931X_RMA_CTRL_2); + } + if (ndev->flags & IFF_PROMISC) { + sw_w32(0x7fffffff, RTL931X_RMA_CTRL_0); + sw_w32(0x7fffffff, RTL931X_RMA_CTRL_1); + sw_w32(0x7fffffff, RTL931X_RMA_CTRL_2); + } +} + static void rtl838x_eth_tx_timeout(struct net_device *ndev) { unsigned long flags; struct rtl838x_eth_priv *priv = netdev_priv(ndev); - pr_info("in %s\n", __func__); + pr_warn("%s\n", __func__); spin_lock_irqsave(&priv->lock, flags); rtl838x_hw_stop(priv); rtl838x_hw_ring_setup(priv); @@ -705,6 +1103,10 @@ static int rtl838x_eth_tx(struct sk_buff *skb, struct net_device *dev) unsigned long flags; struct p_hdr *h; int dest_port = -1; + int q = skb_get_queue_mapping(skb) % TXRINGS; + + if (q) // Check for high prio queue + pr_debug("SKB priority: %d\n", skb->priority); spin_lock_irqsave(&priv->lock, flags); len = skb->len; @@ -729,44 +1131,48 @@ static int rtl838x_eth_tx(struct sk_buff *skb, struct net_device *dev) } /* We can send this packet if CPU owns the descriptor */ - if (!(ring->tx_r[0][ring->c_tx[0]] & 0x1)) { - /* Set descriptor for tx */ - h = &ring->tx_header[0][ring->c_tx[0]]; + if (!(ring->tx_r[q][ring->c_tx[q]] & 0x1)) { - h->buf = (u8 *)KSEG1ADDR(ring->tx_space); + /* Set descriptor for tx */ + h = &ring->tx_header[q][ring->c_tx[q]]; h->size = len; h->len = len; - /* Create cpu_tag */ - if (priv->family_id == RTL8380_FAMILY_ID) - rtl838x_create_tx_header(h, dest_port); - else - rtl839x_create_tx_header(h, dest_port); + priv->r->create_tx_header(h, dest_port, skb->priority >> 1); /* Copy packet data to tx buffer */ memcpy((void *)KSEG1ADDR(h->buf), skb->data, len); /* Make sure packet data is visible to ASIC */ - mb(); /* wmb() probably works, too */ + wmb(); /* Hand over to switch */ - ring->tx_r[0][ring->c_tx[0]] = ring->tx_r[0][ring->c_tx[0]] | 0x1; + ring->tx_r[q][ring->c_tx[q]] |= 1; - /* BUG: before tx fetch, need to make sure right data is accessed - * This might not be necessary on newer RTL839x, though. - */ - for (i = 0; i < 10; i++) { - val = sw_r32(priv->r->dma_if_ctrl); - if ((val & 0xc) == 0xc) - break; + // Before starting TX, prevent a Lextra bus bug on RTL8380 SoCs + if (priv->family_id == RTL8380_FAMILY_ID) { + for (i = 0; i < 10; i++) { + val = sw_r32(priv->r->dma_if_ctrl); + if ((val & 0xc) == 0xc) + break; + } } /* Tell switch to send data */ - sw_w32_mask(0, TX_DO, priv->r->dma_if_ctrl); + if (priv->family_id == RTL9310_FAMILY_ID + || priv->family_id == RTL9300_FAMILY_ID) { + // Ring ID q == 0: Low priority, Ring ID = 1: High prio queue + if (!q) + sw_w32_mask(0, BIT(2), priv->r->dma_if_ctrl); + else + sw_w32_mask(0, BIT(3), priv->r->dma_if_ctrl); + } else { + sw_w32_mask(0, TX_DO, priv->r->dma_if_ctrl); + } dev->stats.tx_packets++; dev->stats.tx_bytes += len; dev_kfree_skb(skb); - ring->c_tx[0] = (ring->c_tx[0] + 1) % TXRINGLEN; + ring->c_tx[q] = (ring->c_tx[q] + 1) % TXRINGLEN; ret = NETDEV_TX_OK; } else { dev_warn(&priv->pdev->dev, "Data is owned by switch\n"); @@ -777,6 +1183,30 @@ txdone: return ret; } +/* + * Return queue number for TX. On the RTL83XX, these queues have equal priority + * so we do round-robin + */ +u16 rtl83xx_pick_tx_queue(struct net_device *dev, struct sk_buff *skb, + struct net_device *sb_dev) +{ + static u8 last = 0; + + last++; + return last % TXRINGS; +} + +/* + * Return queue number for TX. On the RTL93XX, queue 1 is the high priority queue + */ +u16 rtl93xx_pick_tx_queue(struct net_device *dev, struct sk_buff *skb, + struct net_device *sb_dev) +{ + if (skb->priority >= TC_PRIO_CONTROL) + return 1; + return 0; +} + static int rtl838x_hw_receive(struct net_device *dev, int r, int budget) { struct rtl838x_eth_priv *priv = netdev_priv(dev); @@ -789,27 +1219,24 @@ static int rtl838x_hw_receive(struct net_device *dev, int r, int budget) u32 *last; struct p_hdr *h; bool dsa = netdev_uses_dsa(dev); + struct dsa_tag tag; spin_lock_irqsave(&priv->lock, flags); - last = (u32 *)KSEG1ADDR(sw_r32(priv->r->dma_if_rx_cur(r))); + last = (u32 *)KSEG1ADDR(sw_r32(priv->r->dma_if_rx_cur + r * 4)); + pr_debug("---------------------------------------------------------- RX - %d\n", r); - if (&ring->rx_r[r][ring->c_rx[r]] == last) { - spin_unlock_irqrestore(&priv->lock, flags); - return 0; - } do { if ((ring->rx_r[r][ring->c_rx[r]] & 0x1)) { - netdev_warn(dev, "WARNING Ring contention: ring %x, last %x, current %x, cPTR %x, ISR %x\n", r, (uint32_t)last, - (u32) &ring->rx_r[r][ring->c_rx[r]], - ring->rx_r[r][ring->c_rx[r]], - sw_r32(priv->r->dma_if_intr_sts)); + if (&ring->rx_r[r][ring->c_rx[r]] != last) { + netdev_warn(dev, "Ring contention: r: %x, last %x, cur %x\n", + r, (uint32_t)last, (u32) &ring->rx_r[r][ring->c_rx[r]]); + } break; } h = &ring->rx_header[r][ring->c_rx[r]]; data = (u8 *)KSEG1ADDR(h->buf); len = h->len; - if (!len) break; work_done++; @@ -826,7 +1253,7 @@ static int rtl838x_hw_receive(struct net_device *dev, int r, int budget) /* BUG: Prevent bug on RTL838x SoCs*/ if (priv->family_id == RTL8380_FAMILY_ID) { sw_w32(0xffffffff, priv->r->dma_if_rx_ring_size(0)); - for (i = 0; i < RXRINGS; i++) { + for (i = 0; i < priv->rxrings; i++) { /* Update each ring cnt */ val = sw_r32(priv->r->dma_if_rx_ring_cntr(i)); sw_w32(val, priv->r->dma_if_rx_ring_cntr(i)); @@ -839,12 +1266,19 @@ static int rtl838x_hw_receive(struct net_device *dev, int r, int budget) memcpy(skb->data, (u8 *)KSEG1ADDR(data), len); /* Overwrite CRC with cpu_tag */ if (dsa) { + priv->r->decode_tag(h, &tag); skb->data[len-4] = 0x80; - skb->data[len-3] = h->cpu_tag[0] & priv->port_mask; + skb->data[len-3] = tag.port; skb->data[len-2] = 0x10; skb->data[len-1] = 0x00; + if (tag.l2_offloaded) + skb->data[len-3] |= 0x40; } + if (tag.queue >= 0) + pr_debug("Queue: %d, len: %d, reason %d port %d\n", + tag.queue, len, tag.reason, tag.port); + skb->protocol = eth_type_trans(skb, dev); dev->stats.rx_packets++; dev->stats.rx_bytes += len; @@ -856,35 +1290,47 @@ static int rtl838x_hw_receive(struct net_device *dev, int r, int budget) dev->stats.rx_dropped++; } - h->buf = (u8 *)KSEG1ADDR(ring->rx_space - + r * ring->c_rx[r] * RING_BUFFER); + /* Reset header structure */ + memset(h, 0, sizeof(struct p_hdr)); + h->buf = data; h->size = RING_BUFFER; - h->len = 0; - memset(&h->cpu_tag, 0, sizeof(uint16_t[5])); - ring->rx_r[r][ring->c_rx[r]] - = KSEG1ADDR(h) | 0x1 | (ring->c_rx[r] == (RXRINGLEN-1) ? WRAP : 0x1); - ring->c_rx[r] = (ring->c_rx[r] + 1) % RXRINGLEN; + ring->rx_r[r][ring->c_rx[r]] = KSEG1ADDR(h) | 0x1 + | (ring->c_rx[r] == (priv->rxringlen - 1) ? WRAP : 0x1); + ring->c_rx[r] = (ring->c_rx[r] + 1) % priv->rxringlen; + last = (u32 *)KSEG1ADDR(sw_r32(priv->r->dma_if_rx_cur + r * 4)); } while (&ring->rx_r[r][ring->c_rx[r]] != last && work_done < budget); + // Update counters + priv->r->update_cntr(r, 0); + spin_unlock_irqrestore(&priv->lock, flags); return work_done; } static int rtl838x_poll_rx(struct napi_struct *napi, int budget) { - struct rtl838x_eth_priv *priv = container_of(napi, struct rtl838x_eth_priv, napi); - int work_done = 0, r = 0; + struct rtl838x_rx_q *rx_q = container_of(napi, struct rtl838x_rx_q, napi); + struct rtl838x_eth_priv *priv = rx_q->priv; + int work_done = 0; + int r = rx_q->id; + int work; - while (work_done < budget && r < RXRINGS) { - work_done += rtl838x_hw_receive(priv->netdev, r, budget - work_done); - r++; + while (work_done < budget) { + work = rtl838x_hw_receive(priv->netdev, r, budget - work_done); + if (!work) + break; + work_done += work; } if (work_done < budget) { napi_complete_done(napi, work_done); + /* Enable RX interrupt */ - sw_w32_mask(0, 0xfffff, priv->r->dma_if_intr_msk); + if (priv->family_id == RTL9300_FAMILY_ID || priv->family_id == RTL9310_FAMILY_ID) + sw_w32(0xffffffff, priv->r->dma_if_intr_rx_done_msk); + else + sw_w32_mask(0, 0xf00ff | BIT(r + 8), priv->r->dma_if_intr_msk); } return work_done; } @@ -960,9 +1406,9 @@ static void rtl838x_mac_an_restart(struct phylink_config *config) pr_info("In %s\n", __func__); /* Restart by disabling and re-enabling link */ - sw_w32(0x6192D, priv->r->mac_force_mode_ctrl(priv->cpu_port)); + sw_w32(0x6192D, priv->r->mac_force_mode_ctrl + priv->cpu_port * 4); mdelay(20); - sw_w32(0x6192F, priv->r->mac_force_mode_ctrl(priv->cpu_port)); + sw_w32(0x6192F, priv->r->mac_force_mode_ctrl + priv->cpu_port * 4); } static int rtl838x_mac_pcs_get_state(struct phylink_config *config, @@ -1114,70 +1560,6 @@ static int rtl838x_set_link_ksettings(struct net_device *ndev, return phylink_ethtool_ksettings_set(priv->phylink, cmd); } -int rtl839x_read_sds_phy(int phy_addr, int phy_reg) -{ - int offset = 0; - int reg; - u32 val; - - if (phy_addr == 49) - offset = 0x100; - - /* For the RTL8393 internal SerDes, we simulate a PHY ID in registers 2/3 - * which would otherwise read as 0 - */ - if (soc_info.id == 0x8393) { - if (phy_reg == 2) - return 0x1c; - if (phy_reg == 3) - return 0x8393; - } - - reg = (phy_reg << 1) & 0xfc; - val = sw_r32(RTL839X_SDS12_13_XSG0 + offset + 0x80 + reg); - - if (phy_reg & 1) - val = (val >> 16) & 0xffff; - else - val &= 0xffff; - return val; -} - -int rtl838x_read_sds_phy(int phy_addr, int phy_reg) -{ - int offset = 0; - u32 val; - - if (phy_addr == 26) - offset = 0x100; - val = sw_r32(MAPLE_SDS4_FIB_REG0r + offset + (phy_reg << 2)) & 0xffff; - - return val; -} - -int rtl839x_write_sds_phy(int phy_addr, int phy_reg, u16 v) -{ - int offset = 0; - int reg; - u32 val; - - if (phy_addr == 49) - offset = 0x100; - - reg = (phy_reg << 1) & 0xfc; - val = v; - if (phy_reg & 1) { - val = val << 16; - sw_w32_mask(0xffff0000, val, - RTL839X_SDS12_13_XSG0 + offset + 0x80 + reg); - } else { - sw_w32_mask(0xffff, val, - RTL839X_SDS12_13_XSG0 + offset + 0x80 + reg); - } - - return 0; -} - static int rtl838x_mdio_read(struct mii_bus *bus, int mii_id, int regnum) { u32 val; @@ -1207,6 +1589,41 @@ static int rtl839x_mdio_read(struct mii_bus *bus, int mii_id, int regnum) return val; } +static int rtl930x_mdio_read(struct mii_bus *bus, int mii_id, int regnum) +{ + u32 val; + int err; + + // TODO: These are hard-coded for the 2 Fibre Ports of the XGS1210 + if (mii_id >= 26 && mii_id <= 27) + return rtl930x_read_sds_phy(mii_id - 18, 0, regnum); + + if (regnum & MII_ADDR_C45) { + regnum &= ~MII_ADDR_C45; + err = rtl930x_read_mmd_phy(mii_id, regnum >> 16, regnum & 0xffff, &val); + } else { + err = rtl930x_read_phy(mii_id, 0, regnum, &val); + } + if (err) + return err; + return val; +} + +static int rtl931x_mdio_read(struct mii_bus *bus, int mii_id, int regnum) +{ + u32 val; + int err; +// struct rtl838x_eth_priv *priv = bus->priv; + +// if (mii_id >= 48 && mii_id <= 49 && priv->id == 0x8393) +// return rtl839x_read_sds_phy(mii_id, regnum); + + err = rtl931x_read_phy(mii_id, 0, regnum, &val); + if (err) + return err; + return val; +} + static int rtl838x_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value) { @@ -1216,7 +1633,7 @@ static int rtl838x_mdio_write(struct mii_bus *bus, int mii_id, if (mii_id >= 24 && mii_id <= 27 && priv->id == 0x8380) { if (mii_id == 26) offset = 0x100; - sw_w32(value, MAPLE_SDS4_FIB_REG0r + offset + (regnum << 2)); + sw_w32(value, RTL838X_SDS4_FIB_REG0 + offset + (regnum << 2)); return 0; } return rtl838x_write_phy(mii_id, 0, regnum, value); @@ -1233,6 +1650,32 @@ static int rtl839x_mdio_write(struct mii_bus *bus, int mii_id, return rtl839x_write_phy(mii_id, 0, regnum, value); } +static int rtl930x_mdio_write(struct mii_bus *bus, int mii_id, + int regnum, u16 value) +{ +// struct rtl838x_eth_priv *priv = bus->priv; + +// if (mii_id >= 48 && mii_id <= 49 && priv->id == 0x8393) +// return rtl839x_write_sds_phy(mii_id, regnum, value); + if (regnum & MII_ADDR_C45) { + regnum &= ~MII_ADDR_C45; + return rtl930x_write_mmd_phy(mii_id, regnum >> 16, regnum & 0xffff, value); + } + + return rtl930x_write_phy(mii_id, 0, regnum, value); +} + +static int rtl931x_mdio_write(struct mii_bus *bus, int mii_id, + int regnum, u16 value) +{ +// struct rtl838x_eth_priv *priv = bus->priv; + +// if (mii_id >= 48 && mii_id <= 49 && priv->id == 0x8393) +// return rtl839x_write_sds_phy(mii_id, regnum, value); + + return rtl931x_write_phy(mii_id, 0, regnum, value); +} + static int rtl838x_mdio_reset(struct mii_bus *bus) { pr_info("%s called\n", __func__); @@ -1262,6 +1705,62 @@ static int rtl839x_mdio_reset(struct mii_bus *bus) return 0; } +static int rtl931x_mdio_reset(struct mii_bus *bus) +{ + sw_w32(0x00000000, RTL931X_SMI_PORT_POLLING_CTRL); + sw_w32(0x00000000, RTL931X_SMI_PORT_POLLING_CTRL + 4); + + pr_info("%s called\n", __func__); + + return 0; +} + +static int rtl930x_mdio_reset(struct mii_bus *bus) +{ + int i; + int pos; + + pr_info("RTL930X_SMI_PORT0_15_POLLING_SEL %08x 16-27: %08x\n", + sw_r32(RTL930X_SMI_PORT0_15_POLLING_SEL), + sw_r32(RTL930X_SMI_PORT16_27_POLLING_SEL)); + + pr_info("%s: Enable SMI polling on SMI bus 0, SMI1, SMI2, disable on SMI3\n", __func__); + sw_w32_mask(BIT(20) | BIT(21) | BIT(22), BIT(23), RTL930X_SMI_GLB_CTRL); + + pr_info("RTL9300 Powering on SerDes ports\n"); + rtl9300_sds_power(24, 1); + rtl9300_sds_power(25, 1); + rtl9300_sds_power(26, 1); + rtl9300_sds_power(27, 1); + mdelay(200); + + // RTL930X_SMI_PORT0_15_POLLING_SEL 55550000 16-27: 00f9aaaa + // i.e SMI=0 for all ports + for (i = 0; i < 5; i++) + pr_info("port phy: %08x\n", sw_r32(RTL930X_SMI_PORT0_5_ADDR + i *4)); + + // 1-to-1 mapping of port to phy-address + for (i = 0; i < 24; i++) { + pos = (i % 6) * 5; + sw_w32_mask(0x1f << pos, i << pos, RTL930X_SMI_PORT0_5_ADDR + (i / 6) * 4); + } + + // ports 24 and 25 have PHY addresses 8 and 9, ports 26/27 PHY 26/27 + sw_w32(8 | 9 << 5 | 26 << 10 | 27 << 15, RTL930X_SMI_PORT0_5_ADDR + 4 * 4); + + // Ports 24 and 25 live on SMI bus 1 and 2 + sw_w32_mask(0x3 << 16, 0x1 << 16, RTL930X_SMI_PORT16_27_POLLING_SEL); + sw_w32_mask(0x3 << 18, 0x2 << 18, RTL930X_SMI_PORT16_27_POLLING_SEL); + + // SMI bus 1 and 2 speak Clause 45 TODO: Configure from .dts + sw_w32_mask(0, BIT(17) | BIT(18), RTL930X_SMI_GLB_CTRL); + + // Ports 24 and 25 are 2.5 Gig, set this type (1) + sw_w32_mask(0x7 << 12, 1 << 12, RTL930X_SMI_MAC_TYPE_CTRL); + sw_w32_mask(0x7 << 15, 1 << 15, RTL930X_SMI_MAC_TYPE_CTRL); + + return 0; +} static int rtl838x_mdio_init(struct rtl838x_eth_priv *priv) { @@ -1287,16 +1786,33 @@ static int rtl838x_mdio_init(struct rtl838x_eth_priv *priv) goto err_put_node; } - if (priv->family_id == RTL8380_FAMILY_ID) { + switch(priv->family_id) { + case RTL8380_FAMILY_ID: priv->mii_bus->name = "rtl838x-eth-mdio"; priv->mii_bus->read = rtl838x_mdio_read; priv->mii_bus->write = rtl838x_mdio_write; priv->mii_bus->reset = rtl838x_mdio_reset; - } else { + break; + case RTL8390_FAMILY_ID: priv->mii_bus->name = "rtl839x-eth-mdio"; priv->mii_bus->read = rtl839x_mdio_read; priv->mii_bus->write = rtl839x_mdio_write; priv->mii_bus->reset = rtl839x_mdio_reset; + break; + case RTL9300_FAMILY_ID: + priv->mii_bus->name = "rtl930x-eth-mdio"; + priv->mii_bus->read = rtl930x_mdio_read; + priv->mii_bus->write = rtl930x_mdio_write; + priv->mii_bus->reset = rtl930x_mdio_reset; + // priv->mii_bus->probe_capabilities = MDIOBUS_C22_C45; TODO for linux 5.9 + break; + case RTL9310_FAMILY_ID: + priv->mii_bus->name = "rtl931x-eth-mdio"; + priv->mii_bus->read = rtl931x_mdio_read; + priv->mii_bus->write = rtl931x_mdio_write; + priv->mii_bus->reset = rtl931x_mdio_reset; +// priv->mii_bus->probe_capabilities = MDIOBUS_C22_C45; TODO for linux 5.9 + break; } priv->mii_bus->priv = priv; priv->mii_bus->parent = &priv->pdev->dev; @@ -1325,12 +1841,46 @@ static const struct net_device_ops rtl838x_eth_netdev_ops = { .ndo_open = rtl838x_eth_open, .ndo_stop = rtl838x_eth_stop, .ndo_start_xmit = rtl838x_eth_tx, + .ndo_select_queue = rtl83xx_pick_tx_queue, .ndo_set_mac_address = rtl838x_set_mac_address, .ndo_validate_addr = eth_validate_addr, .ndo_set_rx_mode = rtl838x_eth_set_multicast_list, .ndo_tx_timeout = rtl838x_eth_tx_timeout, }; +static const struct net_device_ops rtl839x_eth_netdev_ops = { + .ndo_open = rtl838x_eth_open, + .ndo_stop = rtl838x_eth_stop, + .ndo_start_xmit = rtl838x_eth_tx, + .ndo_select_queue = rtl83xx_pick_tx_queue, + .ndo_set_mac_address = rtl838x_set_mac_address, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_rx_mode = rtl839x_eth_set_multicast_list, + .ndo_tx_timeout = rtl838x_eth_tx_timeout, +}; + +static const struct net_device_ops rtl930x_eth_netdev_ops = { + .ndo_open = rtl838x_eth_open, + .ndo_stop = rtl838x_eth_stop, + .ndo_start_xmit = rtl838x_eth_tx, + .ndo_select_queue = rtl93xx_pick_tx_queue, + .ndo_set_mac_address = rtl838x_set_mac_address, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_rx_mode = rtl930x_eth_set_multicast_list, + .ndo_tx_timeout = rtl838x_eth_tx_timeout, +}; + +static const struct net_device_ops rtl931x_eth_netdev_ops = { + .ndo_open = rtl838x_eth_open, + .ndo_stop = rtl838x_eth_stop, + .ndo_start_xmit = rtl838x_eth_tx, + .ndo_select_queue = rtl93xx_pick_tx_queue, + .ndo_set_mac_address = rtl838x_set_mac_address, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_rx_mode = rtl931x_eth_set_multicast_list, + .ndo_tx_timeout = rtl838x_eth_tx_timeout, +}; + static const struct phylink_mac_ops rtl838x_phylink_ops = { .validate = rtl838x_validate, .mac_link_state = rtl838x_mac_pcs_get_state, @@ -1354,7 +1904,8 @@ static int __init rtl838x_eth_probe(struct platform_device *pdev) const void *mac; phy_interface_t phy_mode; struct phylink *phylink; - int err = 0; + int err = 0, i, rxrings, rxringlen; + struct ring_b *ring; pr_info("Probing RTL838X eth device pdev: %x, dev: %x\n", (u32)pdev, (u32)(&(pdev->dev))); @@ -1364,7 +1915,13 @@ static int __init rtl838x_eth_probe(struct platform_device *pdev) return -EINVAL; } - dev = alloc_etherdev(sizeof(struct rtl838x_eth_priv)); + rxrings = (soc_info.family == RTL8380_FAMILY_ID + || soc_info.family == RTL8390_FAMILY_ID) ? 8 : 32; + rxrings = rxrings > MAX_RXRINGS ? MAX_RXRINGS : rxrings; + rxringlen = MAX_ENTRIES / rxrings; + rxringlen = rxringlen > MAX_RXLEN ? MAX_RXLEN : rxringlen; + + dev = alloc_etherdev_mqs(sizeof(struct rtl838x_eth_priv), TXRINGS, rxrings); if (!dev) { err = -ENOMEM; goto err_free; @@ -1392,8 +1949,8 @@ static int __init rtl838x_eth_probe(struct platform_device *pdev) } /* Allocate buffer memory */ - priv->membase = dmam_alloc_coherent(&pdev->dev, - sizeof(struct ring_b) + sizeof(struct notify_b), + priv->membase = dmam_alloc_coherent(&pdev->dev, rxrings * rxringlen * RING_BUFFER + + sizeof(struct ring_b) + sizeof(struct notify_b), (void *)&dev->mem_start, GFP_KERNEL); if (!priv->membase) { dev_err(&pdev->dev, "cannot allocate DMA buffer\n"); @@ -1401,6 +1958,10 @@ static int __init rtl838x_eth_probe(struct platform_device *pdev) goto err_free; } + // Allocate ring-buffer space at the end of the allocated memory + ring = priv->membase; + ring->rx_space = priv->membase + sizeof(struct ring_b) + sizeof(struct notify_b); + spin_lock_init(&priv->lock); /* obtain device IRQ number */ @@ -1412,6 +1973,8 @@ static int __init rtl838x_eth_probe(struct platform_device *pdev) dev->irq = res->start; } dev->ethtool_ops = &rtl838x_ethtool_ops; + dev->min_mtu = ETH_ZLEN; + dev->max_mtu = 1536; priv->id = soc_info.id; priv->family_id = soc_info.family; @@ -1423,15 +1986,33 @@ static int __init rtl838x_eth_probe(struct platform_device *pdev) return -ENODEV; } - if (priv->family_id == 0x8390) { - priv->cpu_port = RTL839X_CPU_PORT; - priv->r = &rtl839x_reg; - priv->port_mask = 0x3f; - } else { + switch (priv->family_id) { + case RTL8380_FAMILY_ID: priv->cpu_port = RTL838X_CPU_PORT; priv->r = &rtl838x_reg; - priv->port_mask = 0x1f; + dev->netdev_ops = &rtl838x_eth_netdev_ops; + break; + case RTL8390_FAMILY_ID: + priv->cpu_port = RTL839X_CPU_PORT; + priv->r = &rtl839x_reg; + dev->netdev_ops = &rtl839x_eth_netdev_ops; + break; + case RTL9300_FAMILY_ID: + priv->cpu_port = RTL930X_CPU_PORT; + priv->r = &rtl930x_reg; + dev->netdev_ops = &rtl930x_eth_netdev_ops; + break; + case RTL9310_FAMILY_ID: + priv->cpu_port = RTL931X_CPU_PORT; + priv->r = &rtl931x_reg; + dev->netdev_ops = &rtl931x_eth_netdev_ops; + break; + default: + pr_err("Unknown SoC family\n"); + return -ENODEV; } + priv->rxringlen = rxringlen; + priv->rxrings = rxrings; rtl8380_init_mac(priv); @@ -1464,7 +2045,6 @@ static int __init rtl838x_eth_probe(struct platform_device *pdev) pr_info("Using MAC %08x%08x\n", sw_r32(priv->r->mac), sw_r32(priv->r->mac + 4)); strcpy(dev->name, "eth%d"); - dev->netdev_ops = &rtl838x_eth_netdev_ops; priv->pdev = pdev; priv->netdev = dev; @@ -1476,7 +2056,12 @@ static int __init rtl838x_eth_probe(struct platform_device *pdev) if (err) goto err_free; - netif_napi_add(dev, &priv->napi, rtl838x_poll_rx, 64); + for (i = 0; i < priv->rxrings; i++) { + priv->rx_qs[i].id = i; + priv->rx_qs[i].priv = priv; + netif_napi_add(dev, &priv->rx_qs[i].napi, rtl838x_poll_rx, 64); + } + platform_set_drvdata(pdev, dev); phy_mode = of_get_phy_mode(dn); @@ -1508,13 +2093,18 @@ static int rtl838x_eth_remove(struct platform_device *pdev) { struct net_device *dev = platform_get_drvdata(pdev); struct rtl838x_eth_priv *priv = netdev_priv(dev); + int i; if (dev) { pr_info("Removing platform driver for rtl838x-eth\n"); rtl838x_mdio_remove(priv); rtl838x_hw_stop(priv); - netif_stop_queue(dev); - netif_napi_del(&priv->napi); + + netif_tx_stop_all_queues(dev); + + for (i = 0; i < priv->rxrings; i++) + netif_napi_del(&priv->rx_qs[i].napi); + unregister_netdev(dev); free_netdev(dev); } diff --git a/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.h b/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.h index 4366e6b64f..d7b4317cbb 100644 --- a/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.h +++ b/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.h @@ -7,37 +7,85 @@ * Register definition */ -#define RTL838X_CPU_PORT 28 -#define RTL839X_CPU_PORT 52 - +/* Per port MAC control */ #define RTL838X_MAC_PORT_CTRL (0xd560) #define RTL839X_MAC_PORT_CTRL (0x8004) -#define RTL838X_DMA_IF_INTR_STS (0x9f54) -#define RTL839X_DMA_IF_INTR_STS (0x7868) -#define RTL838X_DMA_IF_INTR_MSK (0x9f50) -#define RTL839X_DMA_IF_INTR_MSK (0x7864) +#define RTL930X_MAC_L2_PORT_CTRL (0x3268) +#define RTL930X_MAC_PORT_CTRL (0x3260) +#define RTL931X_MAC_L2_PORT_CTRL (0x6000) +#define RTL931X_MAC_PORT_CTRL (0x6004) + +/* DMA interrupt control and status registers */ #define RTL838X_DMA_IF_CTRL (0x9f58) +#define RTL838X_DMA_IF_INTR_STS (0x9f54) +#define RTL838X_DMA_IF_INTR_MSK (0x9f50) + #define RTL839X_DMA_IF_CTRL (0x786c) -#define RTL838X_RST_GLB_CTRL_0 (0x003c) +#define RTL839X_DMA_IF_INTR_STS (0x7868) +#define RTL839X_DMA_IF_INTR_MSK (0x7864) + +#define RTL930X_DMA_IF_CTRL (0xe028) +#define RTL930X_DMA_IF_INTR_RX_RUNOUT_STS (0xe01C) +#define RTL930X_DMA_IF_INTR_RX_DONE_STS (0xe020) +#define RTL930X_DMA_IF_INTR_TX_DONE_STS (0xe024) +#define RTL930X_DMA_IF_INTR_RX_RUNOUT_MSK (0xe010) +#define RTL930X_DMA_IF_INTR_RX_DONE_MSK (0xe014) +#define RTL930X_DMA_IF_INTR_TX_DONE_MSK (0xe018) +#define RTL930X_L2_NTFY_IF_INTR_MSK (0xe04C) +#define RTL930X_L2_NTFY_IF_INTR_STS (0xe050) + +/* TODO: RTL931X_DMA_IF_CTRL has different bits meanings */ +#define RTL931X_DMA_IF_CTRL (0x0928) +#define RTL931X_DMA_IF_INTR_RX_RUNOUT_STS (0x091c) +#define RTL931X_DMA_IF_INTR_RX_DONE_STS (0x0920) +#define RTL931X_DMA_IF_INTR_TX_DONE_STS (0x0924) +#define RTL931X_DMA_IF_INTR_RX_RUNOUT_MSK (0x0910) +#define RTL931X_DMA_IF_INTR_RX_DONE_MSK (0x0914) +#define RTL931X_DMA_IF_INTR_TX_DONE_MSK (0x0918) +#define RTL931X_L2_NTFY_IF_INTR_MSK (0x09E4) +#define RTL931X_L2_NTFY_IF_INTR_STS (0x09E8) + #define RTL838X_MAC_FORCE_MODE_CTRL (0xa104) #define RTL839X_MAC_FORCE_MODE_CTRL (0x02bc) +#define RTL930X_MAC_FORCE_MODE_CTRL (0xCA1C) +#define RTL931X_MAC_FORCE_MODE_CTRL (0x0ddc) /* MAC address settings */ #define RTL838X_MAC (0xa9ec) #define RTL839X_MAC (0x02b4) #define RTL838X_MAC_ALE (0x6b04) #define RTL838X_MAC2 (0xa320) +#define RTL930X_MAC_L2_ADDR_CTRL (0xC714) +#define RTL931X_MAC_L2_ADDR_CTRL (0x135c) +/* Ringbuffer setup */ #define RTL838X_DMA_RX_BASE (0x9f00) #define RTL839X_DMA_RX_BASE (0x780c) +#define RTL930X_DMA_RX_BASE (0xdf00) +#define RTL931X_DMA_RX_BASE (0x0800) + #define RTL838X_DMA_TX_BASE (0x9f40) #define RTL839X_DMA_TX_BASE (0x784c) +#define RTL930X_DMA_TX_BASE (0xe000) +#define RTL931X_DMA_TX_BASE (0x0900) + #define RTL838X_DMA_IF_RX_RING_SIZE (0xB7E4) #define RTL839X_DMA_IF_RX_RING_SIZE (0x6038) +#define RTL930X_DMA_IF_RX_RING_SIZE (0x7C60) +#define RTL931X_DMA_IF_RX_RING_SIZE (0x2080) + #define RTL838X_DMA_IF_RX_RING_CNTR (0xB7E8) #define RTL839X_DMA_IF_RX_RING_CNTR (0x603c) +#define RTL930X_DMA_IF_RX_RING_CNTR (0x7C8C) +#define RTL931X_DMA_IF_RX_RING_CNTR (0x20AC) + #define RTL838X_DMA_IF_RX_CUR (0x9F20) #define RTL839X_DMA_IF_RX_CUR (0x782c) +#define RTL930X_DMA_IF_RX_CUR (0xdf80) +#define RTL931X_DMA_IF_RX_CUR (0x0880) + +#define RTL838X_DMA_IF_TX_CUR_DESC_ADDR_CTRL (0x9F48) +#define RTL930X_DMA_IF_TX_CUR_DESC_ADDR_CTRL (0xE008) #define RTL838X_DMY_REG31 (0x3b28) #define RTL838X_SDS_MODE_SEL (0x0028) @@ -47,50 +95,56 @@ #define RTL838X_SDS4_REG28 (0xef80) #define RTL838X_SDS4_DUMMY0 (0xef8c) #define RTL838X_SDS5_EXT_REG6 (0xf18c) -#define RTL838X_PORT_ISO_CTRL(port) (0x4100 + ((port) << 2)) -#define RTL838X_STAT_PORT_STD_MIB(port) (0x1200 + (((port) << 8))) -#define RTL838X_STAT_RST (0x3100) -#define RTL838X_STAT_CTRL (0x3108) -/* Registers of the internal Serdes of the 8380 */ -#define MAPLE_SDS4_REG0r RTL838X_SDS4_REG28 -#define MAPLE_SDS5_REG0r (RTL838X_SDS4_REG28 + 0x100) -#define MAPLE_SDS4_REG3r RTL838X_SDS4_DUMMY0 -#define MAPLE_SDS5_REG3r (RTL838X_SDS4_REG28 + 0x100) -#define MAPLE_SDS4_FIB_REG0r (RTL838X_SDS4_REG28 + 0x880) -#define MAPLE_SDS5_FIB_REG0r (RTL838X_SDS4_REG28 + 0x980) - -/* VLAN registers */ -#define RTL838X_VLAN_PROFILE(idx) (0x3A88 + ((idx) << 2)) -#define RTL838X_VLAN_PORT_EGR_FLTR (0x3A84) -#define RTL838X_VLAN_PORT_PB_VLAN(port) (0x3C00 + ((port) << 2)) -#define RTL838X_VLAN_PORT_IGR_FLTR_0 (0x3A7C) -#define RTL838X_VLAN_PORT_IGR_FLTR_1 (0x3A7C + 4) -#define RTL838X_TBL_ACCESS_CTRL_0 (0x6914) -#define RTL838X_TBL_ACCESS_DATA_0(idx) (0x6918 + ((idx) << 2)) -#define RTL838X_TBL_ACCESS_CTRL_1 (0xA4C8) -#define RTL838X_TBL_ACCESS_DATA_1(idx) (0xA4CC + ((idx) << 2)) +/* L2 features */ #define RTL839X_TBL_ACCESS_L2_CTRL (0x1180) #define RTL839X_TBL_ACCESS_L2_DATA(idx) (0x1184 + ((idx) << 2)) -/* MAC handling */ +#define RTL838X_TBL_ACCESS_CTRL_0 (0x6914) +#define RTL838X_TBL_ACCESS_DATA_0(idx) (0x6918 + ((idx) << 2)) + +/* MAC-side link state handling */ #define RTL838X_MAC_LINK_STS (0xa188) #define RTL839X_MAC_LINK_STS (0x0390) +#define RTL930X_MAC_LINK_STS (0xCB10) +#define RTL931X_MAC_LINK_STS (0x0ec0) + #define RTL838X_MAC_LINK_SPD_STS (0xa190) #define RTL839X_MAC_LINK_SPD_STS (0x03a0) +#define RTL930X_MAC_LINK_SPD_STS (0xCB18) +#define RTL931X_MAC_LINK_SPD_STS (0x0ed0) + #define RTL838X_MAC_LINK_DUP_STS (0xa19c) #define RTL839X_MAC_LINK_DUP_STS (0x03b0) +#define RTL930X_MAC_LINK_DUP_STS (0xCB28) +#define RTL931X_MAC_LINK_DUP_STS (0x0ef0) + // TODO: RTL8390_MAC_LINK_MEDIA_STS_ADDR ??? + #define RTL838X_MAC_TX_PAUSE_STS (0xa1a0) #define RTL839X_MAC_TX_PAUSE_STS (0x03b8) +#define RTL930X_MAC_TX_PAUSE_STS (0xCB2C) +#define RTL931X_MAC_TX_PAUSE_STS (0x0ef8) + #define RTL838X_MAC_RX_PAUSE_STS (0xa1a4) -#define RTL839X_MAC_RX_PAUSE_STS (0x03c0) +#define RTL839X_MAC_RX_PAUSE_STS (0xCB30) +#define RTL930X_MAC_RX_PAUSE_STS (0xC2F8) +#define RTL931X_MAC_RX_PAUSE_STS (0x0f00) + #define RTL838X_EEE_TX_TIMER_GIGA_CTRL (0xaa04) #define RTL838X_EEE_TX_TIMER_GELITE_CTRL (0xaa08) + +#define RTL930X_L2_UNKN_UC_FLD_PMSK (0x9064) + #define RTL839X_MAC_GLB_CTRL (0x02a8) #define RTL839X_SCHED_LB_TICK_TKN_CTRL (0x60f8) #define RTL838X_L2_TBL_FLUSH_CTRL (0x3370) #define RTL839X_L2_TBL_FLUSH_CTRL (0x3ba0) +#define RTL930X_L2_TBL_FLUSH_CTRL (0x9404) +#define RTL931X_L2_TBL_FLUSH_CTRL (0xCD9C) + +#define RTL930X_L2_PORT_SABLK_CTRL (0x905c) +#define RTL930X_L2_PORT_DABLK_CTRL (0x9060) /* MAC link state bits */ #define FORCE_EN (1 << 0) @@ -100,25 +154,48 @@ #define TX_PAUSE_EN (1 << 6) #define RX_PAUSE_EN (1 << 7) -/* RTL839X L2 Notification DMA interface */ +/* L2 Notification DMA interface */ #define RTL839X_DMA_IF_NBUF_BASE_DESC_ADDR_CTRL (0x785C) #define RTL839X_L2_NOTIFICATION_CTRL (0x7808) +#define RTL931X_L2_NTFY_RING_BASE_ADDR (0x09DC) +#define RTL931X_L2_NTFY_RING_CUR_ADDR (0x09E0) +#define RTL839X_L2_NOTIFICATION_CTRL (0x7808) +#define RTL931X_L2_NTFY_CTRL (0xCDC8) #define RTL838X_L2_CTRL_0 (0x3200) #define RTL839X_L2_CTRL_0 (0x3800) +#define RTL930X_L2_CTRL (0x8FD8) +#define RTL931X_L2_CTRL (0xC800) /* TRAPPING to CPU-PORT */ #define RTL838X_SPCL_TRAP_IGMP_CTRL (0x6984) -#define RTL839X_SPCL_TRAP_IGMP_CTRL (0x1058) #define RTL838X_RMA_CTRL_0 (0x4300) #define RTL838X_RMA_CTRL_1 (0x4304) #define RTL839X_RMA_CTRL_0 (0x1200) + +#define RTL839X_SPCL_TRAP_IGMP_CTRL (0x1058) #define RTL839X_RMA_CTRL_1 (0x1204) #define RTL839X_RMA_CTRL_2 (0x1208) #define RTL839X_RMA_CTRL_3 (0x120C) +#define RTL930X_RMA_CTRL_0 (0x9E60) +#define RTL930X_RMA_CTRL_1 (0x9E64) +#define RTL930X_RMA_CTRL_2 (0x9E68) + +#define RTL931X_RMA_CTRL_0 (0x8800) +#define RTL931X_RMA_CTRL_1 (0x8804) +#define RTL931X_RMA_CTRL_2 (0x8808) + +/* Advanced SMI control for clause 45 PHYs */ +#define RTL930X_SMI_MAC_TYPE_CTRL (0xCA04) +#define RTL930X_SMI_PORT24_27_ADDR_CTRL (0xCB90) +#define RTL930X_SMI_PORT0_15_POLLING_SEL (0xCA08) +#define RTL930X_SMI_PORT16_27_POLLING_SEL (0xCA0C) + /* Registers of the internal Serdes of the 8390 */ #define RTL839X_SDS12_13_XSG0 (0xB800) +/* Registers of the internal Serdes of the 8380 */ +#define RTL838X_SDS4_FIB_REG0 (0xF800) inline int rtl838x_mac_port_ctrl(int p) { @@ -130,34 +207,19 @@ inline int rtl839x_mac_port_ctrl(int p) return RTL839X_MAC_PORT_CTRL + (p << 7); } -static inline int rtl838x_mac_force_mode_ctrl(int p) +/* On the RTL931XX, the functionality of the MAC port control register is split up + * into RTL931X_MAC_L2_PORT_CTRL and RTL931X_MAC_PORT_CTRL the functionality used + * by the Ethernet driver is in the same bits now in RTL931X_MAC_L2_PORT_CTRL + */ + +inline int rtl930x_mac_port_ctrl(int p) { - return RTL838X_MAC_FORCE_MODE_CTRL + (p << 2); + return RTL930X_MAC_L2_PORT_CTRL + (p << 6); } -static inline int rtl839x_mac_force_mode_ctrl(int p) +inline int rtl931x_mac_port_ctrl(int p) { - return RTL839X_MAC_FORCE_MODE_CTRL + (p << 2); -} - -inline int rtl838x_dma_rx_base(int i) -{ - return RTL838X_DMA_RX_BASE + (i << 2); -} - -inline int rtl839x_dma_rx_base(int i) -{ - return RTL839X_DMA_RX_BASE + (i << 2); -} - -inline int rtl838x_dma_tx_base(int i) -{ - return RTL838X_DMA_TX_BASE + (i << 2); -} - -inline int rtl839x_dma_tx_base(int i) -{ - return RTL839X_DMA_TX_BASE + (i << 2); + return RTL931X_MAC_L2_PORT_CTRL + (p << 7); } inline int rtl838x_dma_if_rx_ring_size(int i) @@ -170,6 +232,16 @@ inline int rtl839x_dma_if_rx_ring_size(int i) return RTL839X_DMA_IF_RX_RING_SIZE + ((i >> 3) << 2); } +inline int rtl930x_dma_if_rx_ring_size(int i) +{ + return RTL930X_DMA_IF_RX_RING_SIZE + ((i / 3) << 2); +} + +inline int rtl931x_dma_if_rx_ring_size(int i) +{ + return RTL931X_DMA_IF_RX_RING_SIZE + ((i / 3) << 2); +} + inline int rtl838x_dma_if_rx_ring_cntr(int i) { return RTL838X_DMA_IF_RX_RING_CNTR + ((i >> 3) << 2); @@ -180,35 +252,54 @@ inline int rtl839x_dma_if_rx_ring_cntr(int i) return RTL839X_DMA_IF_RX_RING_CNTR + ((i >> 3) << 2); } - -inline int rtl838x_dma_if_rx_cur(int i) +inline int rtl930x_dma_if_rx_ring_cntr(int i) { - return RTL838X_DMA_IF_RX_CUR + (i << 2); + return RTL930X_DMA_IF_RX_RING_CNTR + ((i / 3) << 2); } -inline int rtl839x_dma_if_rx_cur(int i) +inline int rtl931x_dma_if_rx_ring_cntr(int i) { - return RTL839X_DMA_IF_RX_CUR + (i << 2); + return RTL931X_DMA_IF_RX_RING_CNTR + ((i / 3) << 2); } inline u32 rtl838x_get_mac_link_sts(int port) { - return (sw_r32(RTL838X_MAC_LINK_STS) & (1 << port)); + return (sw_r32(RTL838X_MAC_LINK_STS) & BIT(port)); } inline u32 rtl839x_get_mac_link_sts(int p) { - return (sw_r32(RTL839X_MAC_LINK_STS + ((p >> 5) << 2)) & (1 << p)); + return (sw_r32(RTL839X_MAC_LINK_STS + ((p >> 5) << 2)) & BIT(p % 32)); +} + +inline u32 rtl930x_get_mac_link_sts(int port) +{ + return (sw_r32(RTL930X_MAC_LINK_STS) & BIT(port)); +} + +inline u32 rtl931x_get_mac_link_sts(int p) +{ + return (sw_r32(RTL931X_MAC_LINK_STS + ((p >> 5) << 2)) & BIT(p % 32)); } inline u32 rtl838x_get_mac_link_dup_sts(int port) { - return (sw_r32(RTL838X_MAC_LINK_DUP_STS) & (1 << port)); + return (sw_r32(RTL838X_MAC_LINK_DUP_STS) & BIT(port)); } inline u32 rtl839x_get_mac_link_dup_sts(int p) { - return (sw_r32(RTL839X_MAC_LINK_DUP_STS + ((p >> 5) << 2)) & (1 << p)); + return (sw_r32(RTL839X_MAC_LINK_DUP_STS + ((p >> 5) << 2)) & BIT(p % 32)); +} + +inline u32 rtl930x_get_mac_link_dup_sts(int port) +{ + return (sw_r32(RTL930X_MAC_LINK_DUP_STS) & BIT(port)); +} + +inline u32 rtl931x_get_mac_link_dup_sts(int p) +{ + return (sw_r32(RTL931X_MAC_LINK_DUP_STS + ((p >> 5) << 2)) & BIT(p % 32)); } inline u32 rtl838x_get_mac_link_spd_sts(int port) @@ -229,6 +320,25 @@ inline u32 rtl839x_get_mac_link_spd_sts(int port) return (speed & 0x3); } + +inline u32 rtl930x_get_mac_link_spd_sts(int port) +{ + int r = RTL930X_MAC_LINK_SPD_STS + ((port / 10) << 2); + u32 speed = sw_r32(r); + + speed >>= (port % 10) * 3; + return (speed & 0x7); +} + +inline u32 rtl931x_get_mac_link_spd_sts(int port) +{ + int r = RTL931X_MAC_LINK_SPD_STS + ((port >> 3) << 2); + u32 speed = sw_r32(r); + + speed >>= (port % 8) << 2; + return (speed & 0xf); +} + inline u32 rtl838x_get_mac_rx_pause_sts(int port) { return (sw_r32(RTL838X_MAC_RX_PAUSE_STS) & (1 << port)); @@ -236,7 +346,17 @@ inline u32 rtl838x_get_mac_rx_pause_sts(int port) inline u32 rtl839x_get_mac_rx_pause_sts(int p) { - return (sw_r32(RTL839X_MAC_RX_PAUSE_STS + ((p >> 5) << 2)) & (1 << p)); + return (sw_r32(RTL839X_MAC_RX_PAUSE_STS + ((p >> 5) << 2)) & BIT(p % 32)); +} + +inline u32 rtl930x_get_mac_rx_pause_sts(int port) +{ + return (sw_r32(RTL930X_MAC_RX_PAUSE_STS) & (1 << port)); +} + +inline u32 rtl931x_get_mac_rx_pause_sts(int p) +{ + return (sw_r32(RTL931X_MAC_RX_PAUSE_STS + ((p >> 5) << 2)) & BIT(p % 32)); } inline u32 rtl838x_get_mac_tx_pause_sts(int port) @@ -246,21 +366,42 @@ inline u32 rtl838x_get_mac_tx_pause_sts(int port) inline u32 rtl839x_get_mac_tx_pause_sts(int p) { - return (sw_r32(RTL839X_MAC_TX_PAUSE_STS + ((p >> 5) << 2)) & (1 << p)); + return (sw_r32(RTL839X_MAC_TX_PAUSE_STS + ((p >> 5) << 2)) & BIT(p % 32)); } +inline u32 rtl930x_get_mac_tx_pause_sts(int port) +{ + return (sw_r32(RTL930X_MAC_TX_PAUSE_STS) & (1 << port)); +} + +inline u32 rtl931x_get_mac_tx_pause_sts(int p) +{ + return (sw_r32(RTL931X_MAC_TX_PAUSE_STS + ((p >> 5) << 2)) & BIT(p % 32)); +} + +struct p_hdr; +struct dsa_tag; struct rtl838x_reg { + irqreturn_t (*net_irq)(int irq, void *dev_id); int (*mac_port_ctrl)(int port); int dma_if_intr_sts; int dma_if_intr_msk; + int dma_if_intr_rx_runout_sts; + int dma_if_intr_rx_done_sts; + int dma_if_intr_tx_done_sts; + int dma_if_intr_rx_runout_msk; + int dma_if_intr_rx_done_msk; + int dma_if_intr_tx_done_msk; + int l2_ntfy_if_intr_sts; + int l2_ntfy_if_intr_msk; int dma_if_ctrl; - int (*mac_force_mode_ctrl)(int port); - int (*dma_rx_base)(int ring); - int (*dma_tx_base)(int ring); - int (*dma_if_rx_ring_size)(int ring); - int (*dma_if_rx_ring_cntr)(int ring); - int (*dma_if_rx_cur)(int ring); + int mac_force_mode_ctrl; + int dma_rx_base; + int dma_tx_base; + int (*dma_if_rx_ring_size)(int ring); + int (*dma_if_rx_ring_cntr)(int ring); + int dma_if_rx_cur; int rst_glb_ctrl; u32 (*get_mac_link_sts)(int port); u32 (*get_mac_link_dup_sts)(int port); @@ -269,11 +410,19 @@ struct rtl838x_reg { u32 (*get_mac_tx_pause_sts)(int port); int mac; int l2_tbl_flush_ctrl; + void (*update_cntr)(int r, int work_done); + void (*create_tx_header)(struct p_hdr *h, int dest_port, int prio); + bool (*decode_tag)(struct p_hdr *h, struct dsa_tag *tag); }; int rtl838x_write_phy(u32 port, u32 page, u32 reg, u32 val); int rtl838x_read_phy(u32 port, u32 page, u32 reg, u32 *val); int rtl839x_write_phy(u32 port, u32 page, u32 reg, u32 val); int rtl839x_read_phy(u32 port, u32 page, u32 reg, u32 *val); +int rtl930x_write_phy(u32 port, u32 page, u32 reg, u32 val); +int rtl930x_read_phy(u32 port, u32 page, u32 reg, u32 *val); +int rtl931x_write_phy(u32 port, u32 page, u32 reg, u32 val); +int rtl931x_read_phy(u32 port, u32 page, u32 reg, u32 *val); +void rtl9300_sds_power(int sds_num, int val); #endif /* _RTL838X_ETH_H */ diff --git a/target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.c b/target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.c index eba416934f..78953c6d17 100644 --- a/target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.c +++ b/target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.c @@ -22,35 +22,50 @@ static const struct firmware rtl838x_8380_fw; static const struct firmware rtl838x_8214fc_fw; static const struct firmware rtl838x_8218b_fw; +int rtl930x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val); +int rtl930x_write_mmd_phy(u32 port, u32 addr, u32 reg, u32 val); static int read_phy(u32 port, u32 page, u32 reg, u32 *val) -{ - if (soc_info.family == RTL8390_FAMILY_ID) - return rtl839x_read_phy(port, page, reg, val); - else +{ switch (soc_info.family) { + case RTL8380_FAMILY_ID: return rtl838x_read_phy(port, page, reg, val); + case RTL8390_FAMILY_ID: + return rtl839x_read_phy(port, page, reg, val); + case RTL9300_FAMILY_ID: + return rtl930x_read_phy(port, page, reg, val); + case RTL9310_FAMILY_ID: + return rtl931x_read_phy(port, page, reg, val); + } + return -1; } static int write_phy(u32 port, u32 page, u32 reg, u32 val) { - if (soc_info.family == RTL8390_FAMILY_ID) - return rtl839x_write_phy(port, page, reg, val); - else + switch (soc_info.family) { + case RTL8380_FAMILY_ID: return rtl838x_write_phy(port, page, reg, val); + case RTL8390_FAMILY_ID: + return rtl839x_write_phy(port, page, reg, val); + case RTL9300_FAMILY_ID: + return rtl930x_write_phy(port, page, reg, val); + case RTL9310_FAMILY_ID: + return rtl931x_write_phy(port, page, reg, val); + } + return -1; } -static void int_phy_on_off(int mac, bool on) +static void rtl8380_int_phy_on_off(int mac, bool on) { u32 val; read_phy(mac, 0, 0, &val); if (on) - write_phy(mac, 0, 0, val & ~(1 << 11)); + write_phy(mac, 0, 0, val & ~BIT(11)); else - write_phy(mac, 0, 0, val | (1 << 11)); + write_phy(mac, 0, 0, val | BIT(11)); } -static void rtl8214fc_on_off(int mac, bool on) +static void rtl8380_rtl8214fc_on_off(int mac, bool on) { u32 val; @@ -58,25 +73,194 @@ static void rtl8214fc_on_off(int mac, bool on) write_phy(mac, 4095, 30, 3); read_phy(mac, 0, 16, &val); if (on) - write_phy(mac, 0, 16, val & ~(1 << 11)); + write_phy(mac, 0, 16, val & ~BIT(11)); else - write_phy(mac, 0, 16, val | (1 << 11)); + write_phy(mac, 0, 16, val | BIT(11)); /* copper ports */ write_phy(mac, 4095, 30, 1); read_phy(mac, 0, 16, &val); if (on) - write_phy(mac, 0xa40, 16, val & ~(1 << 11)); + write_phy(mac, 0xa40, 16, val & ~BIT(11)); else - write_phy(mac, 0xa40, 16, val | (1 << 11)); + write_phy(mac, 0xa40, 16, val | BIT(11)); } -static void phy_reset(int mac) +static void rtl8380_phy_reset(int mac) { u32 val; read_phy(mac, 0, 0, &val); - write_phy(mac, 0, 0, val | (0x1 << 15)); + write_phy(mac, 0, 0, val | BIT(15)); +} + +static void rtl8380_sds_rst(int mac) +{ + u32 offset = (mac == 24) ? 0 : 0x100; + + sw_w32_mask(1 << 11, 0, RTL8380_SDS4_FIB_REG0 + offset); + sw_w32_mask(0x3, 0, RTL838X_SDS4_REG28 + offset); + sw_w32_mask(0x3, 0x3, RTL838X_SDS4_REG28 + offset); + sw_w32_mask(0, 0x1 << 6, RTL838X_SDS4_DUMMY0 + offset); + sw_w32_mask(0x1 << 6, 0, RTL838X_SDS4_DUMMY0 + offset); + pr_info("SERDES reset: %d\n", mac); +} + +/* + * Reset the SerDes by powering it off and set a new operations mode + * of the SerDes. 0x1f is off. Other modes are + * 0x01: QSGMII 0x04: 1000BX_FIBER 0x05: FIBER100 + * 0x06: QSGMII 0x09: RSGMII 0x0d: USXGMII + * 0x10: XSGMII 0x12: HISGMII 0x16: 2500Base_X + * 0x17: RXAUI_LITE 0x19: RXAUI_PLUS 0x1a: 10G Base-R + * 0x1b: 10GR1000BX_AUTO 0x1f: OFF + */ +void rtl9300_sds_rst(int sds_num, u32 mode) +{ + // The access registers for SDS_MODE_SEL and the LSB for each SDS within + u16 regs[] = { 0x0194, 0x0194, 0x0194, 0x0194, 0x02a0, 0x02a0, 0x02a0, 0x02a0, + 0x02A4, 0x02A4, 0x0198, 0x0198 }; + u8 lsb[] = { 0, 6, 12, 18, 0, 6, 12, 18, 0, 6, 0, 6}; + + pr_info("SerDes: %s %d\n", __func__, mode); + if (sds_num < 0 || sds_num > 11) { + pr_err("Wrong SerDes number: %d\n", sds_num); + return; + } + + sw_w32_mask(0x1f << lsb[sds_num], 0x1f << lsb[sds_num], regs[sds_num]); + mdelay(10); + + sw_w32_mask(0x1f << lsb[sds_num], mode << lsb[sds_num], regs[sds_num]); + mdelay(10); + + pr_info("SDS: 194:%08x 198:%08x 2a0:%08x 2a4:%08x\n", + sw_r32(0x194), sw_r32(0x198), sw_r32(0x2a0), sw_r32(0x2a4)); +} + +/* + * On the RTL839x family of SoCs with inbuilt SerDes, these SerDes are accessed through + * a 2048 bit register that holds the contents of the PHY being simulated by the SoC. + */ +int rtl839x_read_sds_phy(int phy_addr, int phy_reg) +{ + int offset = 0; + int reg; + u32 val; + + if (phy_addr == 49) + offset = 0x100; + + /* + * For the RTL8393 internal SerDes, we simulate a PHY ID in registers 2/3 + * which would otherwise read as 0. + */ + if (soc_info.id == 0x8393) { + if (phy_reg == 2) + return 0x1c; + if (phy_reg == 3) + return 0x8393; + } + + /* + * Register RTL839X_SDS12_13_XSG0 is 2048 bit broad, the MSB (bit 15) of the + * 0th PHY register is bit 1023 (in byte 0x80). Because PHY-registers are 16 + * bit broad, we offset by reg << 1. In the SoC 2 registers are stored in + * one 32 bit register. + */ + reg = (phy_reg << 1) & 0xfc; + val = sw_r32(RTL839X_SDS12_13_XSG0 + offset + 0x80 + reg); + + if (phy_reg & 1) + val = (val >> 16) & 0xffff; + else + val &= 0xffff; + return val; +} + +/* + * On the RTL930x family of SoCs, the internal SerDes are accessed through an IO + * register which simulates commands to an internal MDIO bus. + */ +int rtl930x_read_sds_phy(int phy_addr, int page, int phy_reg) +{ + int i; + u32 cmd = phy_addr << 2 | page << 7 | phy_reg << 13 | 1; + + pr_info("%s: phy_addr %d, phy_reg: %d\n", __func__, phy_addr, phy_reg); + sw_w32(cmd, RTL930X_SDS_INDACS_CMD); + + for (i = 0; i < 100; i++) { + if (!(sw_r32(RTL930X_SDS_INDACS_CMD) & 0x1)) + break; + mdelay(1); + } + + if (i >= 100) + return -EIO; + + pr_info("%s: returning %04x\n", __func__, sw_r32(RTL930X_SDS_INDACS_DATA) & 0xffff); + return sw_r32(RTL930X_SDS_INDACS_DATA) & 0xffff; +} + +int rtl930x_write_sds_phy(int phy_addr, int page, int phy_reg, u16 v) +{ + int i; + u32 cmd; + + sw_w32(v, RTL930X_SDS_INDACS_DATA); + cmd = phy_addr << 2 | page << 7 | phy_reg << 13 | 0x3; + + for (i = 0; i < 100; i++) { + if (!(sw_r32(RTL930X_SDS_INDACS_CMD) & 0x1)) + break; + mdelay(1); + } + + if (i >= 100) + return -EIO; + + return 0; +} + +/* + * On the RTL838x SoCs, the internal SerDes is accessed through direct access to + * standard PHY registers, where a 32 bit register holds a 16 bit word as found + * in a standard page 0 of a PHY + */ +int rtl838x_read_sds_phy(int phy_addr, int phy_reg) +{ + int offset = 0; + u32 val; + + if (phy_addr == 26) + offset = 0x100; + val = sw_r32(RTL838X_SDS4_FIB_REG0 + offset + (phy_reg << 2)) & 0xffff; + + return val; +} + +int rtl839x_write_sds_phy(int phy_addr, int phy_reg, u16 v) +{ + int offset = 0; + int reg; + u32 val; + + if (phy_addr == 49) + offset = 0x100; + + reg = (phy_reg << 1) & 0xfc; + val = v; + if (phy_reg & 1) { + val = val << 16; + sw_w32_mask(0xffff0000, val, + RTL839X_SDS12_13_XSG0 + offset + 0x80 + reg); + } else { + sw_w32_mask(0xffff, val, + RTL839X_SDS12_13_XSG0 + offset + 0x80 + reg); + } + + return 0; } /* Read the link and speed status of the 2 internal SGMII/1000Base-X @@ -123,6 +307,149 @@ static int rtl8393_read_status(struct phy_device *phydev) return err; } +static int rtl8226_read_page(struct phy_device *phydev) +{ + return __phy_read(phydev, 0x1f); +} + +static int rtl8226_write_page(struct phy_device *phydev, int page) +{ + return __phy_write(phydev, 0x1f, page); +} + +static int rtl8226_read_status(struct phy_device *phydev) +{ + int ret = 0, i; + u32 val; + int port = phydev->mdio.addr; + +// TODO: ret = genphy_read_status(phydev); +// if (ret < 0) { +// pr_info("%s: genphy_read_status failed\n", __func__); +// return ret; +// } + + // Link status must be read twice + for (i = 0; i < 2; i++) { + rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA402, &val); + } + phydev->link = val & BIT(2) ? 1 : 0; + if (!phydev->link) + goto out; + + // Read duplex status + ret = rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA434, &val); + if (ret) + goto out; + phydev->duplex = !!(val & BIT(3)); + + // Read speed + ret = rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA434, &val); + switch (val & 0x0630) { + case 0x0000: + phydev->speed = SPEED_10; + break; + case 0x0010: + phydev->speed = SPEED_100; + break; + case 0x0020: + phydev->speed = SPEED_1000; + break; + case 0x0200: + phydev->speed = SPEED_10000; + break; + case 0x0210: + phydev->speed = SPEED_2500; + break; + case 0x0220: + phydev->speed = SPEED_5000; + break; + default: + break; + } +out: + return ret; +} + +static int rtl8266_advertise_aneg(struct phy_device *phydev) +{ + int ret = 0; + u32 v; + int port = phydev->mdio.addr; + + pr_info("In %s\n", __func__); + + ret = rtl930x_read_mmd_phy(port, MMD_AN, 16, &v); + if (ret) + goto out; + + v |= BIT(5); // HD 10M + v |= BIT(6); // FD 10M + v |= BIT(7); // HD 100M + v |= BIT(8); // FD 100M + + ret = rtl930x_write_mmd_phy(port, MMD_AN, 16, v); + + // Allow 1GBit + ret = rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA412, &v); + if (ret) + goto out; + v |= BIT(9); // FD 1000M + + ret = rtl930x_write_mmd_phy(port, MMD_VEND2, 0xA412, v); + if (ret) + goto out; + + // Allow 2.5G + ret = rtl930x_read_mmd_phy(port, MMD_AN, 32, &v); + if (ret) + goto out; + + v |= BIT(7); + ret = rtl930x_write_mmd_phy(port, MMD_AN, 32, v); + +out: + return ret; +} + + +static int rtl8226_config_aneg(struct phy_device *phydev) +{ + int ret = 0; + u32 v; + int port = phydev->mdio.addr; + + pr_info("In %s\n", __func__); + if (phydev->autoneg == AUTONEG_ENABLE) { + ret = rtl8266_advertise_aneg(phydev); + if (ret) + goto out; + // AutoNegotiationEnable + ret = rtl930x_read_mmd_phy(port, MMD_AN, 0, &v); + if (ret) + goto out; + + v |= BIT(12); // Enable AN + ret = rtl930x_write_mmd_phy(port, MMD_AN, 0, v); + if (ret) + goto out; + + // RestartAutoNegotiation + ret = rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA400, &v); + if (ret) + goto out; + v |= BIT(9); + + ret = rtl930x_write_mmd_phy(port, MMD_VEND2, 0xA400, v); + } + + pr_info("%s: Ret is already: %d\n", __func__, ret); +// TODO: ret = __genphy_config_aneg(phydev, ret); + +out: + pr_info("%s: And ret is now: %d\n", __func__, ret); + return ret; +} static struct fw_header *rtl838x_request_fw(struct phy_device *phydev, const struct firmware *fw, @@ -234,9 +561,9 @@ static int rtl8380_configure_int_rtl8218b(struct phy_device *phydev) read_phy(mac, 0, 0, &val); if (val & (1 << 11)) - int_phy_on_off(mac, true); + rtl8380_int_phy_on_off(mac, true); else - phy_reset(mac); + rtl8380_phy_reset(mac); msleep(100); /* Ready PHY for patch */ @@ -326,9 +653,9 @@ static int rtl8380_configure_ext_rtl8218b(struct phy_device *phydev) read_phy(mac, 0, 0, &val); if (val & (1 << 11)) - int_phy_on_off(mac, true); + rtl8380_int_phy_on_off(mac, true); else - phy_reset(mac); + rtl8380_phy_reset(mac); msleep(100); /* Get Chip revision */ @@ -517,6 +844,26 @@ static int rtl8218b_write_mmd(struct phy_device *phydev, return rtl838x_write_mmd_phy(addr, devnum, regnum, val); } +static int rtl8226_read_mmd(struct phy_device *phydev, int devnum, u16 regnum) +{ + int port = phydev->mdio.addr; // the SoC translates port addresses to PHY addr + int err; + u32 val; + + err = rtl930x_read_mmd_phy(port, devnum, regnum, &val); + + if (err) + return err; + return val; +} + +static int rtl8226_write_mmd(struct phy_device *phydev, int devnum, u16 regnum, u16 val) +{ + int port = phydev->mdio.addr; // the SoC translates port addresses to PHY addr + + return rtl930x_write_mmd_phy(port, devnum, regnum, val); +} + static void rtl8380_rtl8214fc_media_set(int mac, bool set_fibre) { int base = mac - (mac % 4); @@ -654,7 +1001,7 @@ static void rtl8218b_eee_set_u_boot(int port, bool enable) } // TODO: unused -static void rtl8380_rtl8218b_eee_set(int port, bool enable) +void rtl8380_rtl8218b_eee_set(int port, bool enable) { u32 val; bool an_enabled; @@ -720,7 +1067,7 @@ static int rtl8218b_get_eee(struct phy_device *phydev, } // TODO: unused -static void rtl8380_rtl8218b_green_set(int mac, bool enable) +void rtl8380_rtl8218b_green_set(int mac, bool enable) { u32 val; @@ -744,7 +1091,7 @@ static void rtl8380_rtl8218b_green_set(int mac, bool enable) } // TODO: unused -static int rtl8380_rtl8214fc_get_green(struct phy_device *phydev, struct ethtool_eee *e) +int rtl8380_rtl8214fc_get_green(struct phy_device *phydev, struct ethtool_eee *e) { u32 val; int addr = phydev->mdio.addr; @@ -886,9 +1233,9 @@ static int rtl8380_configure_rtl8214fc(struct phy_device *phydev) read_phy(mac, 0, 16, &val); if (val & (1 << 11)) - rtl8214fc_on_off(mac, true); + rtl8380_rtl8214fc_on_off(mac, true); else - phy_reset(mac); + rtl8380_phy_reset(mac); msleep(100); write_phy(mac, 0, 30, 0x0001); @@ -1143,6 +1490,45 @@ static int rtl8390_configure_serdes(struct phy_device *phydev) return 0; } +int rtl9300_configure_serdes(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + int phy_addr = phydev->mdio.addr; + int sds_num = 0; + int v; + + phydev_info(phydev, "Configuring internal RTL9300 SERDES\n"); + + switch (phy_addr) { + case 26: + sds_num = 8; + break; + case 27: + sds_num = 9; + break; + default: + dev_err(dev, "Not a SerDes PHY\n"); + return -EINVAL; + } + + /* Set default Medium to fibre */ + v = rtl930x_read_sds_phy(sds_num, 0x1f, 11); + if (v < 0) { + dev_err(dev, "Cannot access SerDes PHY %d\n", phy_addr); + return -EINVAL; + } + v |= BIT(2); + rtl930x_write_sds_phy(sds_num, 0x1f, 11, v); + + // TODO: this needs to be configurable via ethtool/.dts + pr_info("Setting 10G/1000BX auto fibre medium\n"); + rtl9300_sds_rst(sds_num, 0x1b); + + // TODO: Apply patch set for fibre type + + return 0; +} + static int rtl8214fc_phy_probe(struct phy_device *phydev) { struct device *dev = &phydev->mdio.dev; @@ -1232,6 +1618,43 @@ static int rtl8218b_int_phy_probe(struct phy_device *phydev) return 0; } +static int rtl8218d_phy_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + struct rtl838x_phy_priv *priv; + int addr = phydev->mdio.addr; + + pr_info("%s: id: %d\n", __func__, addr); + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->name = "RTL8218D"; + + /* All base addresses of the PHYs start at multiples of 8 */ + if (!(addr % 8)) { + /* Configuration must be done while patching still possible */ +// TODO: return configure_rtl8218d(phydev); + } + return 0; +} + +static int rtl8226_phy_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + struct rtl838x_phy_priv *priv; + int addr = phydev->mdio.addr; + + pr_info("%s: id: %d\n", __func__, addr); + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->name = "RTL8226"; + + return 0; +} + static int rtl838x_serdes_probe(struct phy_device *phydev) { struct device *dev = &phydev->mdio.dev; @@ -1299,6 +1722,26 @@ static int rtl8390_serdes_probe(struct phy_device *phydev) return rtl8390_configure_generic(phydev); } +static int rtl9300_serdes_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + struct rtl838x_phy_priv *priv; + int addr = phydev->mdio.addr; + + if (soc_info.family != RTL9300_FAMILY_ID) + return -ENODEV; + + if (addr < 24) + return -ENODEV; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->name = "RTL9300 Serdes"; + return rtl9300_configure_serdes(phydev); +} + static struct phy_driver rtl83xx_phy_driver[] = { { PHY_ID_MATCH_MODEL(PHY_ID_RTL8214C), @@ -1340,6 +1783,29 @@ static struct phy_driver rtl83xx_phy_driver[] = { .set_eee = rtl8218b_set_eee, .get_eee = rtl8218b_get_eee, }, + { + PHY_ID_MATCH_MODEL(PHY_ID_RTL8218D), + .name = "REALTEK RTL8218D", + .features = PHY_GBIT_FEATURES, + .probe = rtl8218d_phy_probe, + .suspend = genphy_suspend, + .resume = genphy_resume, + .set_loopback = genphy_loopback, + }, { + PHY_ID_MATCH_MODEL(PHY_ID_RTL8226), + .name = "REALTEK RTL8226", + .features = PHY_GBIT_FEATURES, + .probe = rtl8226_phy_probe, + .suspend = genphy_suspend, + .resume = genphy_resume, + .set_loopback = genphy_loopback, + .read_mmd = rtl8226_read_mmd, + .write_mmd = rtl8226_write_mmd, + .read_page = rtl8226_read_page, + .write_page = rtl8226_write_page, + .read_status = rtl8226_read_status, + .config_aneg = rtl8226_config_aneg, + }, { PHY_ID_MATCH_MODEL(PHY_ID_RTL8218B_I), .name = "Realtek RTL8218B (internal)", @@ -1383,7 +1849,16 @@ static struct phy_driver rtl83xx_phy_driver[] = { .suspend = genphy_suspend, .resume = genphy_resume, .set_loopback = genphy_loopback, - } + }, + { + PHY_ID_MATCH_MODEL(PHY_ID_RTL9300_I), + .name = "REALTEK RTL9300 SERDES", + .features = PHY_GBIT_FIBRE_FEATURES, + .probe = rtl9300_serdes_probe, + .suspend = genphy_suspend, + .resume = genphy_resume, + .set_loopback = genphy_loopback, + }, }; module_phy_driver(rtl83xx_phy_driver); diff --git a/target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.h b/target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.h index 7af35a3583..031ec8a0e9 100644 --- a/target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.h +++ b/target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.h @@ -28,13 +28,33 @@ struct __attribute__ ((__packed__)) fw_header { #define PHY_ID_RTL8214C 0x001cc942 #define PHY_ID_RTL8214FC 0x001cc981 #define PHY_ID_RTL8218B_E 0x001cc981 +#define PHY_ID_RTL8218D 0x001cc983 #define PHY_ID_RTL8218B_I 0x001cca40 +#define PHY_ID_RTL8226 0x001cc838 #define PHY_ID_RTL8390_GENERIC 0x001ccab0 #define PHY_ID_RTL8393_I 0x001c8393 +#define PHY_ID_RTL9300_I 0x70d03106 -#define RTL839X_SDS12_13_XSG0 (0xB800) +// PHY MMD devices +#define MMD_AN 7 +#define MMD_VEND2 31 +/* Registers of the internal Serdes of the 8380 */ #define RTL838X_SDS_MODE_SEL (0x0028) #define RTL838X_SDS_CFG_REG (0x0034) #define RTL838X_INT_MODE_CTRL (0x005c) #define RTL838X_DMY_REG31 (0x3b28) + +#define RTL8380_SDS4_FIB_REG0 (0xF800) +#define RTL838X_SDS4_REG28 (0xef80) +#define RTL838X_SDS4_DUMMY0 (0xef8c) +#define RTL838X_SDS5_EXT_REG6 (0xf18c) +#define RTL838X_SDS4_FIB_REG0 (RTL838X_SDS4_REG28 + 0x880) +#define RTL838X_SDS5_FIB_REG0 (RTL838X_SDS4_REG28 + 0x980) + +/* Registers of the internal SerDes of the RTL8390 */ +#define RTL839X_SDS12_13_XSG0 (0xB800) + +/* Registers of the internal Serdes of the 9300 */ +#define RTL930X_SDS_INDACS_CMD (0x03B0) +#define RTL930X_SDS_INDACS_DATA (0x03B4) diff --git a/target/linux/realtek/image/Makefile b/target/linux/realtek/image/Makefile index 87c616c3f2..8ad7ad1b5d 100644 --- a/target/linux/realtek/image/Makefile +++ b/target/linux/realtek/image/Makefile @@ -7,6 +7,14 @@ include $(INCLUDE_DIR)/image.mk KERNEL_LOADADDR = 0x80000000 KERNEL_ENTRY = 0x80000400 +define Build/zyxel-vers + ( echo VERS;\ + for hw in $(1); do\ + echo -n "V9.99($$hw.0) | ";\ + date -d @$(SOURCE_DATE_EPOCH) +%m/%d/%Y;\ + done ) >> $@ +endef + define Device/Default PROFILES = Default KERNEL := kernel-bin | append-dtb | gzip | uImage gzip @@ -69,6 +77,8 @@ define Device/zyxel_gs1900-10hp IMAGE_SIZE := 6976k DEVICE_VENDOR := ZyXEL DEVICE_MODEL := GS1900-10HP + UIMAGE_MAGIC := 0x83800000 + KERNEL_INITRAMFS := kernel-bin | append-dtb | gzip | zyxel-vers AAZI | uImage gzip endef TARGET_DEVICES += zyxel_gs1900-10hp @@ -79,6 +89,8 @@ define Device/zyxel_gs1900-8hp-v1 DEVICE_MODEL := GS1900-8HP DEVICE_VARIANT := v1 DEVICE_PACKAGES += lua-rs232 + UIMAGE_MAGIC := 0x83800000 + KERNEL_INITRAMFS := kernel-bin | append-dtb | gzip | zyxel-vers AAHI | uImage gzip endef TARGET_DEVICES += zyxel_gs1900-8hp-v1 @@ -89,6 +101,8 @@ define Device/zyxel_gs1900-8hp-v2 DEVICE_MODEL := GS1900-8HP DEVICE_VARIANT := v2 DEVICE_PACKAGES += lua-rs232 + UIMAGE_MAGIC := 0x83800000 + KERNEL_INITRAMFS := kernel-bin | append-dtb | gzip | zyxel-vers AAHI | uImage gzip endef TARGET_DEVICES += zyxel_gs1900-8hp-v2 diff --git a/target/linux/realtek/patches-5.4/302-clocksource-add-rtl9300-driver.patch b/target/linux/realtek/patches-5.4/302-clocksource-add-rtl9300-driver.patch new file mode 100644 index 0000000000..763f6adc58 --- /dev/null +++ b/target/linux/realtek/patches-5.4/302-clocksource-add-rtl9300-driver.patch @@ -0,0 +1,34 @@ +--- a/drivers/clocksource/Kconfig ++++ b/drivers/clocksource/Kconfig +@@ -126,6 +126,15 @@ + help + Enables the support for the RDA Micro timer driver. + ++config RTL9300_TIMER ++ bool "Clocksource/timer for the Realtek RTL9300 family of SoCs" ++ depends on MIPS ++ select COMMON_CLK ++ select TIMER_OF ++ select CLKSRC_MMIO ++ help ++ Enables support for the Realtek RTL9300 timer driver. ++ + config SUN4I_TIMER + bool "Sun4i timer driver" if COMPILE_TEST + depends on HAS_IOMEM +@@ -695,5 +704,4 @@ + select IRQ_DOMAIN + help + Support for the timer/counter unit of the Ingenic JZ SoCs. +- + endmenu +--- a/drivers/clocksource/Makefile ++++ b/drivers/clocksource/Makefile +@@ -61,6 +61,7 @@ + obj-$(CONFIG_SPRD_TIMER) += timer-sprd.o + obj-$(CONFIG_NPCM7XX_TIMER) += timer-npcm7xx.o + obj-$(CONFIG_RDA_TIMER) += timer-rda.o ++obj-$(CONFIG_RTL9300_TIMER) += timer-rtl9300.o + + obj-$(CONFIG_ARC_TIMERS) += arc_timer.o + obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o diff --git a/target/linux/realtek/patches-5.4/701-net-dsa-add-rtl838x-support-for-tag-trailer.patch b/target/linux/realtek/patches-5.4/701-net-dsa-add-rtl838x-support-for-tag-trailer.patch index c8a09c50d3..7bfbd0a5f6 100644 --- a/target/linux/realtek/patches-5.4/701-net-dsa-add-rtl838x-support-for-tag-trailer.patch +++ b/target/linux/realtek/patches-5.4/701-net-dsa-add-rtl838x-support-for-tag-trailer.patch @@ -9,21 +9,24 @@ + trailer[1] = dp->index; +#else trailer[1] = 1 << dp->index; -+#endif /* CONFIG_NET_DSA_RTL83XX */ ++#endif /* CONFIG_NET_DSA_RTL838X */ trailer[2] = 0x10; trailer[3] = 0x00; -@@ -61,12 +66,20 @@ static struct sk_buff *trailer_rcv(struc +@@ -61,12 +69,23 @@ static struct sk_buff *trailer_rcv(struc return NULL; trailer = skb_tail_pointer(skb) - 4; + +#ifdef CONFIG_NET_DSA_RTL83XX -+ if (trailer[0] != 0x80 || (trailer[1] & 0xe0) != 0x00 || ++ if (trailer[0] != 0x80 || (trailer[1] & 0x80) != 0x00 || + (trailer[2] & 0xef) != 0x00 || trailer[3] != 0x00) + return NULL; + -+ source_port = trailer[1] & 0x1f; ++ if (trailer[1] & 0x40) ++ skb->offload_fwd_mark = 1; ++ ++ source_port = trailer[1] & 0x3f; +#else if (trailer[0] != 0x80 || (trailer[1] & 0xf8) != 0x00 || (trailer[2] & 0xef) != 0x00 || trailer[3] != 0x00) diff --git a/target/linux/sunxi/config-5.4 b/target/linux/sunxi/config-5.4 index d34cb4c7ae..7318d5a3e9 100644 --- a/target/linux/sunxi/config-5.4 +++ b/target/linux/sunxi/config-5.4 @@ -396,7 +396,6 @@ CONFIG_PADATA=y CONFIG_PAGE_OFFSET=0xC0000000 CONFIG_PAGE_POOL=y CONFIG_PARTITION_PERCPU=y -CONFIG_PERF_EVENTS=y CONFIG_PERF_USE_VMALLOC=y CONFIG_PGTABLE_LEVELS=3 CONFIG_PHYLIB=y diff --git a/target/linux/x86/config-5.4 b/target/linux/x86/config-5.4 index 83d9cf2e05..f26c20c59d 100644 --- a/target/linux/x86/config-5.4 +++ b/target/linux/x86/config-5.4 @@ -388,7 +388,6 @@ CONFIG_PCI_LOCKLESS_CONFIG=y CONFIG_PCI_MSI=y CONFIG_PCI_MSI_IRQ_DOMAIN=y CONFIG_PCSPKR_PLATFORM=y -CONFIG_PERF_EVENTS=y CONFIG_PERF_EVENTS_INTEL_CSTATE=y CONFIG_PERF_EVENTS_INTEL_RAPL=y CONFIG_PERF_EVENTS_INTEL_UNCORE=y diff --git a/target/linux/zynq/config-5.4 b/target/linux/zynq/config-5.4 index 258c3e14b4..38a6dae36a 100644 --- a/target/linux/zynq/config-5.4 +++ b/target/linux/zynq/config-5.4 @@ -486,7 +486,6 @@ CONFIG_PCI_DOMAINS=y CONFIG_PCI_DOMAINS_GENERIC=y CONFIG_PCI_MSI=y CONFIG_PCI_MSI_IRQ_DOMAIN=y -CONFIG_PERF_EVENTS=y CONFIG_PERF_USE_VMALLOC=y CONFIG_PGTABLE_LEVELS=2 CONFIG_PHYLIB=y diff --git a/toolchain/gcc/common.mk b/toolchain/gcc/common.mk index aaddb74642..55fad1fcc4 100644 --- a/toolchain/gcc/common.mk +++ b/toolchain/gcc/common.mk @@ -117,7 +117,9 @@ GCC_CONFIGURE:= \ --with-mpc=$(TOPDIR)/staging_dir/host \ --disable-decimal-float \ --with-diagnostics-color=auto-if-env \ - --enable-__cxa_atexit + --enable-__cxa_atexit \ + --disable-libstdcxx-dual-abi \ + --with-default-libstdcxx-abi=new ifneq ($(CONFIG_mips)$(CONFIG_mipsel),) GCC_CONFIGURE += --with-mips-plt endif