mirror of
https://github.com/Telecominfraproject/wlan-ap.git
synced 2025-10-30 18:07:52 +00:00
mediatek: update to mp2.3 SDK
Signed-off-by: John Crispin <john@phrozen.org>
This commit is contained in:
@@ -77,6 +77,7 @@ config-y:= \
|
||||
MAC80211_STA_DEBUG \
|
||||
MAC80211_HT_DEBUG \
|
||||
MAC80211_PS_DEBUG \
|
||||
MAC80211_DEBUG_COUNTERS \
|
||||
|
||||
config-$(call config_package,cfg80211) += CFG80211
|
||||
config-$(CONFIG_PACKAGE_CFG80211_TESTMODE) += NL80211_TESTMODE
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
From 7a5c5d30c9f95cf990c415c4edb3359ac1c1ab5b Mon Sep 17 00:00:00 2001
|
||||
From 5c8d90efe02c47ce76a6d5383ea6aa90eb0c73d8 Mon Sep 17 00:00:00 2001
|
||||
From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
|
||||
Date: Mon, 20 Feb 2023 14:25:24 +0800
|
||||
Subject: [PATCH 13/19] mac80211: mtk: add sta-assisted DFS state update
|
||||
mechanism
|
||||
Subject: [PATCH] mac80211: mtk: add sta-assisted DFS state update mechanism
|
||||
|
||||
Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
|
||||
---
|
||||
@@ -62,7 +61,7 @@ index e674aa7..3e348a5 100644
|
||||
|
||||
/**
|
||||
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
|
||||
index 8ee325a..48053e4 100644
|
||||
index 8ee325a..2dbc18c 100644
|
||||
--- a/net/mac80211/mlme.c
|
||||
+++ b/net/mac80211/mlme.c
|
||||
@@ -1442,6 +1442,10 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
@@ -87,16 +86,16 @@ index 8ee325a..48053e4 100644
|
||||
}
|
||||
|
||||
static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata)
|
||||
@@ -3782,6 +3790,9 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
|
||||
event.u.mlme.status = MLME_SUCCESS;
|
||||
drv_event_callback(sdata->local, sdata, &event);
|
||||
sdata_info(sdata, "associated\n");
|
||||
@@ -3779,6 +3787,9 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
|
||||
cfg80211_assoc_timeout(sdata->dev, cbss);
|
||||
goto notify_driver;
|
||||
}
|
||||
+ cfg80211_sta_update_dfs_state(&sdata->wdev,
|
||||
+ &sdata->vif.bss_conf.chandef,
|
||||
+ NULL, sdata->vif.bss_conf.assoc);
|
||||
|
||||
/*
|
||||
* destroy assoc_data afterwards, as otherwise an idle
|
||||
event.u.mlme.status = MLME_SUCCESS;
|
||||
drv_event_callback(sdata->local, sdata, &event);
|
||||
sdata_info(sdata, "associated\n");
|
||||
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
|
||||
index c217276..9f651f9 100644
|
||||
--- a/net/wireless/chan.c
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
From d10310d3eb842ebc449271b45ad2f47634149385 Mon Sep 17 00:00:00 2001
|
||||
From bb918e40dcc7d082f898234cf29cd545de78621e Mon Sep 17 00:00:00 2001
|
||||
From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
|
||||
Date: Wed, 15 Nov 2023 15:05:17 +0800
|
||||
Subject: [PATCH] mac80211: mtk: add DFS CAC countdown in CSA flow
|
||||
|
||||
Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
|
||||
---
|
||||
include/net/cfg80211.h | 32 +++++++++++++++++
|
||||
net/mac80211/cfg.c | 45 +++++++++++++++++++++++-
|
||||
net/mac80211/ieee80211_i.h | 2 ++
|
||||
net/mac80211/iface.c | 2 ++
|
||||
net/mac80211/mlme.c | 6 +++-
|
||||
net/mac80211/util.c | 11 +++++-
|
||||
net/wireless/chan.c | 71 ++++++++++++++++++++++++++++++++++++++
|
||||
net/wireless/nl80211.c | 5 +--
|
||||
net/wireless/rdev-ops.h | 17 +++++++++
|
||||
9 files changed, 186 insertions(+), 5 deletions(-)
|
||||
include/net/cfg80211.h | 32 +++++++++++++++
|
||||
net/mac80211/cfg.c | 84 +++++++++++++++++++++++++++++++++++---
|
||||
net/mac80211/ieee80211_i.h | 2 +
|
||||
net/mac80211/iface.c | 2 +
|
||||
net/mac80211/mlme.c | 6 ++-
|
||||
net/mac80211/util.c | 11 ++++-
|
||||
net/wireless/chan.c | 72 ++++++++++++++++++++++++++++++++
|
||||
net/wireless/nl80211.c | 5 ++-
|
||||
net/wireless/rdev-ops.h | 17 ++++++++
|
||||
9 files changed, 221 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
|
||||
index 03f072f..a443b0d 100644
|
||||
@@ -67,7 +67,7 @@ index 03f072f..a443b0d 100644
|
||||
* cfg80211_ch_switch_notify - update wdev channel and notify userspace
|
||||
* @dev: the device which switched channels
|
||||
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
|
||||
index 56381f8..3e6e903 100644
|
||||
index 56381f8..7a30ca6 100644
|
||||
--- a/net/mac80211/cfg.c
|
||||
+++ b/net/mac80211/cfg.c
|
||||
@@ -3328,6 +3328,39 @@ static int ieee80211_set_after_csa_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
@@ -122,7 +122,78 @@ index 56381f8..3e6e903 100644
|
||||
sdata->vif.csa_active = false;
|
||||
|
||||
err = ieee80211_set_after_csa_beacon(sdata, &changed);
|
||||
@@ -4538,7 +4576,11 @@ ieee80211_skip_cac(struct wireless_dev *wdev)
|
||||
@@ -3428,6 +3466,11 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
switch (sdata->vif.type) {
|
||||
case NL80211_IFTYPE_AP:
|
||||
+ if (sdata->u.ap.next_beacon) {
|
||||
+ kfree(sdata->u.ap.next_beacon->mbssid_ies);
|
||||
+ kfree(sdata->u.ap.next_beacon);
|
||||
+ sdata->u.ap.next_beacon = NULL;
|
||||
+ }
|
||||
sdata->u.ap.next_beacon =
|
||||
cfg80211_beacon_dup(¶ms->beacon_after);
|
||||
if (!sdata->u.ap.next_beacon)
|
||||
@@ -3586,15 +3629,14 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
||||
if (!list_empty(&local->roc_list) || local->scanning)
|
||||
return -EBUSY;
|
||||
|
||||
- if (sdata->wdev.cac_started)
|
||||
- return -EBUSY;
|
||||
-
|
||||
if (cfg80211_chandef_identical(¶ms->chandef,
|
||||
&sdata->vif.bss_conf.chandef))
|
||||
return -EINVAL;
|
||||
|
||||
- /* don't allow another channel switch if one is already active. */
|
||||
- if (sdata->vif.csa_active)
|
||||
+ /* don't allow another channel switch if one is already active
|
||||
+ * unless its during post CSA radar detection.
|
||||
+ */
|
||||
+ if (sdata->vif.csa_active && !sdata->wdev.cac_started)
|
||||
return -EBUSY;
|
||||
|
||||
mutex_lock(&local->chanctx_mtx);
|
||||
@@ -3646,6 +3688,14 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
||||
goto out;
|
||||
}
|
||||
|
||||
+ /* Finalize CSA immediately if CAC is started during last channel switch */
|
||||
+ if (sdata->wdev.cac_started) {
|
||||
+ ieee80211_wake_vif_queues(local, sdata, IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
+ cancel_delayed_work(&sdata->dfs_cac_timer_work);
|
||||
+ sdata->wdev.cac_started = false;
|
||||
+ changed = 0;
|
||||
+ }
|
||||
+
|
||||
sdata->csa_chandef = params->chandef;
|
||||
sdata->csa_block_tx = params->block_tx;
|
||||
sdata->vif.csa_active = true;
|
||||
@@ -3661,6 +3711,23 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
drv_channel_switch_beacon(sdata, ¶ms->chandef);
|
||||
} else {
|
||||
+ struct ieee80211_sub_if_data *tmp;
|
||||
+ int n_assigned = 0, n_reserved = 0;
|
||||
+
|
||||
+ list_for_each_entry(tmp, &chanctx->assigned_vifs,
|
||||
+ assigned_chanctx_list) {
|
||||
+ n_assigned++;
|
||||
+ if (tmp->reserved_chanctx)
|
||||
+ n_reserved++;
|
||||
+ }
|
||||
+
|
||||
+ /* Wait for all interfaces to be ready */
|
||||
+ if (n_assigned != n_reserved) {
|
||||
+ sdata->reserved_ready = true;
|
||||
+ err = 0;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
/* if the beacon didn't change, we can finalize immediately */
|
||||
ieee80211_csa_finalize(sdata);
|
||||
}
|
||||
@@ -4538,7 +4605,11 @@ ieee80211_skip_cac(struct wireless_dev *wdev)
|
||||
|
||||
cancel_delayed_work(&sdata->dfs_cac_timer_work);
|
||||
if (wdev->cac_started) {
|
||||
@@ -135,7 +206,7 @@ index 56381f8..3e6e903 100644
|
||||
cac_time_ms = wdev->cac_time_ms;
|
||||
wdev->cac_start_time = jiffies -
|
||||
msecs_to_jiffies(cac_time_ms + 1);
|
||||
@@ -4630,6 +4672,7 @@ const struct cfg80211_ops mac80211_config_ops = {
|
||||
@@ -4630,6 +4701,7 @@ const struct cfg80211_ops mac80211_config_ops = {
|
||||
#endif
|
||||
.get_channel = ieee80211_cfg_get_channel,
|
||||
.start_radar_detection = ieee80211_start_radar_detection,
|
||||
@@ -184,7 +255,7 @@ index 00b0443..ef32d53 100644
|
||||
INIT_LIST_HEAD(&sdata->assigned_chanctx_list);
|
||||
INIT_LIST_HEAD(&sdata->reserved_chanctx_list);
|
||||
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
|
||||
index 48053e4..e9ec32d 100644
|
||||
index 2dbc18c..ed81ebf 100644
|
||||
--- a/net/mac80211/mlme.c
|
||||
+++ b/net/mac80211/mlme.c
|
||||
@@ -1870,7 +1870,11 @@ void ieee80211_dfs_cac_timer_work(struct work_struct *work)
|
||||
@@ -223,10 +294,10 @@ index 26cd627..e07fe73 100644
|
||||
&chandef,
|
||||
NL80211_RADAR_CAC_ABORTED,
|
||||
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
|
||||
index 9f651f9..88dd69e 100644
|
||||
index 9f651f9..f02598b 100644
|
||||
--- a/net/wireless/chan.c
|
||||
+++ b/net/wireless/chan.c
|
||||
@@ -1262,6 +1262,77 @@ bool cfg80211_reg_can_beacon_relax(struct wiphy *wiphy,
|
||||
@@ -1262,6 +1262,78 @@ bool cfg80211_reg_can_beacon_relax(struct wiphy *wiphy,
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_reg_can_beacon_relax);
|
||||
|
||||
@@ -267,11 +338,12 @@ index 9f651f9..88dd69e 100644
|
||||
+ enum nl80211_dfs_regions dfs_region;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ /* Update DFS channel state especially when original channel include DFS channel */
|
||||
+ cfg80211_sched_dfs_chan_update(rdev);
|
||||
+
|
||||
+ if (cfg80211_chandef_dfs_available(wiphy, chandef))
|
||||
+ goto out;
|
||||
+
|
||||
+ flush_delayed_work(&rdev->dfs_update_channels_wk);
|
||||
+
|
||||
+ dfs_region = reg_get_dfs_region(wiphy);
|
||||
+ if (dfs_region == NL80211_DFS_UNSET)
|
||||
+ goto out;
|
||||
|
||||
@@ -0,0 +1,456 @@
|
||||
From 34063f56b7b5f69daea335ae70582fdbcea429a5 Mon Sep 17 00:00:00 2001
|
||||
From: Rex Lu <rex.lu@mediatek.com>
|
||||
Date: Thu, 14 Mar 2024 17:06:52 +0800
|
||||
Subject: [PATCH] mac80211: mtk: backport refactor STA CSA paring flow
|
||||
|
||||
According to https://github.com/torvalds/linux/commit/21c3f8f95554feff9bed15703e89adbe582e0383
|
||||
|
||||
Signed-off-by: Rex Lu <rex.lu@mediatek.com>
|
||||
---
|
||||
include/net/cfg80211.h | 13 ++
|
||||
net/mac80211/spectmgmt.c | 273 +++++++++++++++++++++++++++++++--------
|
||||
net/wireless/util.c | 74 +++++++++++
|
||||
3 files changed, 308 insertions(+), 52 deletions(-)
|
||||
|
||||
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
|
||||
index 0114c26..c333ba8 100644
|
||||
--- a/include/net/cfg80211.h
|
||||
+++ b/include/net/cfg80211.h
|
||||
@@ -7840,6 +7840,19 @@ void cfg80211_ch_switch_started_notify(struct net_device *dev,
|
||||
bool ieee80211_operating_class_to_band(u8 operating_class,
|
||||
enum nl80211_band *band);
|
||||
|
||||
+/**
|
||||
+ * ieee80211_operating_class_to_chandef - convert operating class to chandef
|
||||
+ *
|
||||
+ * @operating_class: the operating class to convert
|
||||
+ * @chan: the ieee80211_channel to convert
|
||||
+ * @chandef: a pointer to the resulting chandef
|
||||
+ *
|
||||
+ * Returns %true if the conversion was successful, %false otherwise.
|
||||
+ */
|
||||
+bool ieee80211_operating_class_to_chandef(u8 operating_class,
|
||||
+ struct ieee80211_channel *chan,
|
||||
+ struct cfg80211_chan_def *chandef);
|
||||
+
|
||||
/**
|
||||
* ieee80211_chandef_to_operating_class - convert chandef to operation class
|
||||
*
|
||||
diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c
|
||||
index 76747bf..ce00687 100644
|
||||
--- a/net/mac80211/spectmgmt.c
|
||||
+++ b/net/mac80211/spectmgmt.c
|
||||
@@ -19,6 +19,182 @@
|
||||
#include "sta_info.h"
|
||||
#include "wme.h"
|
||||
|
||||
+static bool
|
||||
+wbcs_elem_to_chandef(const struct ieee80211_wide_bw_chansw_ie *wbcs_elem,
|
||||
+ struct cfg80211_chan_def *chandef)
|
||||
+{
|
||||
+ u8 ccfs0 = wbcs_elem->new_center_freq_seg0;
|
||||
+ u8 ccfs1 = wbcs_elem->new_center_freq_seg1;
|
||||
+ u32 cf0 = ieee80211_channel_to_frequency(ccfs0, chandef->chan->band);
|
||||
+ u32 cf1 = ieee80211_channel_to_frequency(ccfs1, chandef->chan->band);
|
||||
+
|
||||
+ switch (wbcs_elem->new_channel_width) {
|
||||
+ case IEEE80211_VHT_CHANWIDTH_160MHZ:
|
||||
+ /* deprecated encoding */
|
||||
+ chandef->width = NL80211_CHAN_WIDTH_160;
|
||||
+ chandef->center_freq1 = cf0;
|
||||
+ break;
|
||||
+ case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
|
||||
+ /* deprecated encoding */
|
||||
+ chandef->width = NL80211_CHAN_WIDTH_80P80;
|
||||
+ chandef->center_freq1 = cf0;
|
||||
+ chandef->center_freq2 = cf1;
|
||||
+ break;
|
||||
+ case IEEE80211_VHT_CHANWIDTH_80MHZ:
|
||||
+ chandef->width = NL80211_CHAN_WIDTH_80;
|
||||
+ chandef->center_freq1 = cf0;
|
||||
+
|
||||
+ if (ccfs1) {
|
||||
+ u8 diff = abs(ccfs0 - ccfs1);
|
||||
+
|
||||
+ if (diff == 8) {
|
||||
+ chandef->width = NL80211_CHAN_WIDTH_160;
|
||||
+ chandef->center_freq1 = cf1;
|
||||
+ } else if (diff > 8) {
|
||||
+ chandef->width = NL80211_CHAN_WIDTH_80P80;
|
||||
+ chandef->center_freq2 = cf1;
|
||||
+ }
|
||||
+ }
|
||||
+ break;
|
||||
+ case IEEE80211_VHT_CHANWIDTH_USE_HT:
|
||||
+ default:
|
||||
+ /* If the WBCS Element is present, new channel bandwidth is
|
||||
+ * at least 40 MHz.
|
||||
+ */
|
||||
+ chandef->width = NL80211_CHAN_WIDTH_40;
|
||||
+ chandef->center_freq1 = cf0;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ return cfg80211_chandef_valid(chandef);
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+validate_chandef_by_ht_vht_oper(struct ieee80211_sub_if_data *sdata,
|
||||
+ u32 sta_flags,
|
||||
+ u32 vht_cap_info,
|
||||
+ struct cfg80211_chan_def *chandef)
|
||||
+{
|
||||
+ u32 control_freq, center_freq1, center_freq2;
|
||||
+ enum nl80211_chan_width chan_width;
|
||||
+ struct ieee80211_ht_operation ht_oper;
|
||||
+ struct ieee80211_vht_operation vht_oper;
|
||||
+
|
||||
+ if (sta_flags & (IEEE80211_STA_DISABLE_HT |
|
||||
+ IEEE80211_STA_DISABLE_40MHZ)) {
|
||||
+ chandef->chan = NULL;
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ control_freq = chandef->chan->center_freq;
|
||||
+ center_freq1 = chandef->center_freq1;
|
||||
+ center_freq2 = chandef->center_freq2;
|
||||
+ chan_width = chandef->width;
|
||||
+
|
||||
+ ht_oper.primary_chan = ieee80211_frequency_to_channel(control_freq);
|
||||
+ if (control_freq != center_freq1)
|
||||
+ ht_oper.ht_param = control_freq > center_freq1 ?
|
||||
+ IEEE80211_HT_PARAM_CHA_SEC_BELOW :
|
||||
+ IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
|
||||
+ else
|
||||
+ ht_oper.ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE;
|
||||
+
|
||||
+ ieee80211_chandef_ht_oper(&ht_oper, chandef);
|
||||
+
|
||||
+ if (sta_flags & IEEE80211_STA_DISABLE_VHT)
|
||||
+ return;
|
||||
+
|
||||
+ vht_oper.center_freq_seg0_idx =
|
||||
+ ieee80211_frequency_to_channel(center_freq1);
|
||||
+ vht_oper.center_freq_seg1_idx = center_freq2 ?
|
||||
+ ieee80211_frequency_to_channel(center_freq2) : 0;
|
||||
+
|
||||
+ switch (chan_width) {
|
||||
+ case NL80211_CHAN_WIDTH_160:
|
||||
+ vht_oper.chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
|
||||
+ vht_oper.center_freq_seg1_idx = vht_oper.center_freq_seg0_idx;
|
||||
+ vht_oper.center_freq_seg0_idx +=
|
||||
+ control_freq < center_freq1 ? -8 : 8;
|
||||
+ break;
|
||||
+ case NL80211_CHAN_WIDTH_80P80:
|
||||
+ vht_oper.chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
|
||||
+ break;
|
||||
+ case NL80211_CHAN_WIDTH_80:
|
||||
+ vht_oper.chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
|
||||
+ break;
|
||||
+ default:
|
||||
+ vht_oper.chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ ht_oper.operation_mode =
|
||||
+ cpu_to_le16(vht_oper.center_freq_seg1_idx <<
|
||||
+ IEEE80211_HT_OP_MODE_CCFS2_SHIFT);
|
||||
+
|
||||
+ if (!ieee80211_chandef_vht_oper(&sdata->local->hw, vht_cap_info,
|
||||
+ &vht_oper, &ht_oper, chandef))
|
||||
+ chandef->chan = NULL;
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+validate_chandef_by_6ghz_he_eht_oper(struct ieee80211_sub_if_data *sdata,
|
||||
+ u32 sta_flags,
|
||||
+ struct cfg80211_chan_def *chandef)
|
||||
+{
|
||||
+ u32 control_freq, center_freq1, center_freq2;
|
||||
+ enum nl80211_chan_width chan_width;
|
||||
+ struct {
|
||||
+ struct ieee80211_he_operation _oper;
|
||||
+ struct ieee80211_he_6ghz_oper _6ghz_oper;
|
||||
+ } __packed he;
|
||||
+
|
||||
+ if (sta_flags & (IEEE80211_STA_DISABLE_HE)) {
|
||||
+ chandef->chan = NULL;
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ control_freq = chandef->chan->center_freq;
|
||||
+ center_freq1 = chandef->center_freq1;
|
||||
+ center_freq2 = chandef->center_freq2;
|
||||
+ chan_width = chandef->width;
|
||||
+
|
||||
+ he._oper.he_oper_params = cpu_to_le32(u32_encode_bits(1,
|
||||
+ IEEE80211_HE_OPERATION_6GHZ_OP_INFO));
|
||||
+ he._6ghz_oper.primary =
|
||||
+ ieee80211_frequency_to_channel(control_freq);
|
||||
+ he._6ghz_oper.ccfs0 = ieee80211_frequency_to_channel(center_freq1);
|
||||
+ he._6ghz_oper.ccfs1 = center_freq2 ?
|
||||
+ ieee80211_frequency_to_channel(center_freq2) : 0;
|
||||
+
|
||||
+ switch (chan_width) {
|
||||
+ case NL80211_CHAN_WIDTH_160:
|
||||
+ he._6ghz_oper.ccfs1 = he._6ghz_oper.ccfs0;
|
||||
+ he._6ghz_oper.ccfs0 += control_freq < center_freq1 ? -8 : 8;
|
||||
+ fallthrough;
|
||||
+ case NL80211_CHAN_WIDTH_80P80:
|
||||
+ he._6ghz_oper.control =
|
||||
+ IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ;
|
||||
+ break;
|
||||
+ case NL80211_CHAN_WIDTH_80:
|
||||
+ he._6ghz_oper.control =
|
||||
+ IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_80MHZ;
|
||||
+ break;
|
||||
+ case NL80211_CHAN_WIDTH_40:
|
||||
+ he._6ghz_oper.control =
|
||||
+ IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_40MHZ;
|
||||
+ break;
|
||||
+ default:
|
||||
+ he._6ghz_oper.control =
|
||||
+ IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_20MHZ;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ if (!ieee80211_chandef_he_6ghz_oper(sdata, &he._oper, chandef))
|
||||
+ chandef->chan = NULL;
|
||||
+
|
||||
+}
|
||||
+
|
||||
int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee802_11_elems *elems,
|
||||
enum nl80211_band current_band,
|
||||
@@ -28,17 +204,19 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
|
||||
{
|
||||
enum nl80211_band new_band = current_band;
|
||||
int new_freq;
|
||||
- u8 new_chan_no;
|
||||
+ u8 new_chan_no = 0, new_op_class = 0;
|
||||
struct ieee80211_channel *new_chan;
|
||||
- struct cfg80211_chan_def new_vht_chandef = {};
|
||||
+ struct cfg80211_chan_def new_chandef = {};
|
||||
const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
|
||||
const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie;
|
||||
+ const struct ieee80211_ext_chansw_ie *ext_chansw_elem;
|
||||
int secondary_channel_offset = -1;
|
||||
|
||||
memset(csa_ie, 0, sizeof(*csa_ie));
|
||||
|
||||
sec_chan_offs = elems->sec_chan_offs;
|
||||
wide_bw_chansw_ie = elems->wide_bw_chansw_ie;
|
||||
+ ext_chansw_elem = elems->ext_chansw_ie;
|
||||
|
||||
if (sta_flags & (IEEE80211_STA_DISABLE_HT |
|
||||
IEEE80211_STA_DISABLE_40MHZ)) {
|
||||
@@ -49,26 +227,29 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
|
||||
if (sta_flags & IEEE80211_STA_DISABLE_VHT)
|
||||
wide_bw_chansw_ie = NULL;
|
||||
|
||||
- if (elems->ext_chansw_ie) {
|
||||
- if (!ieee80211_operating_class_to_band(
|
||||
- elems->ext_chansw_ie->new_operating_class,
|
||||
- &new_band)) {
|
||||
- sdata_info(sdata,
|
||||
- "cannot understand ECSA IE operating class, %d, ignoring\n",
|
||||
- elems->ext_chansw_ie->new_operating_class);
|
||||
+ if (ext_chansw_elem) {
|
||||
+ new_op_class = ext_chansw_elem->new_operating_class;
|
||||
+ if (!ieee80211_operating_class_to_band(new_op_class, &new_band)) {
|
||||
+ new_op_class = 0;
|
||||
+ sdata_info(sdata, "cannot understand ECSA IE operating class, %d, ignoring\n",
|
||||
+ ext_chansw_elem->new_operating_class);
|
||||
+ } else {
|
||||
+ new_chan_no = ext_chansw_elem->new_ch_num;
|
||||
+ csa_ie->count = ext_chansw_elem->count;
|
||||
+ csa_ie->mode = ext_chansw_elem->mode;
|
||||
}
|
||||
- new_chan_no = elems->ext_chansw_ie->new_ch_num;
|
||||
- csa_ie->count = elems->ext_chansw_ie->count;
|
||||
- csa_ie->mode = elems->ext_chansw_ie->mode;
|
||||
- } else if (elems->ch_switch_ie) {
|
||||
+ }
|
||||
+
|
||||
+ if (!new_op_class && elems->ch_switch_ie) {
|
||||
new_chan_no = elems->ch_switch_ie->new_ch_num;
|
||||
csa_ie->count = elems->ch_switch_ie->count;
|
||||
csa_ie->mode = elems->ch_switch_ie->mode;
|
||||
- } else {
|
||||
- /* nothing here we understand */
|
||||
- return 1;
|
||||
}
|
||||
|
||||
+ /* nothing here we understand */
|
||||
+ if (!new_chan_no)
|
||||
+ return 1;
|
||||
+
|
||||
/* Mesh Channel Switch Parameters Element */
|
||||
if (elems->mesh_chansw_params_ie) {
|
||||
csa_ie->ttl = elems->mesh_chansw_params_ie->mesh_ttl;
|
||||
@@ -132,52 +313,40 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
|
||||
break;
|
||||
}
|
||||
|
||||
- if (wide_bw_chansw_ie) {
|
||||
- u8 new_seg1 = wide_bw_chansw_ie->new_center_freq_seg1;
|
||||
- struct ieee80211_vht_operation vht_oper = {
|
||||
- .chan_width =
|
||||
- wide_bw_chansw_ie->new_channel_width,
|
||||
- .center_freq_seg0_idx =
|
||||
- wide_bw_chansw_ie->new_center_freq_seg0,
|
||||
- .center_freq_seg1_idx = new_seg1,
|
||||
- /* .basic_mcs_set doesn't matter */
|
||||
- };
|
||||
- struct ieee80211_ht_operation ht_oper = {
|
||||
- .operation_mode =
|
||||
- cpu_to_le16(new_seg1 <<
|
||||
- IEEE80211_HT_OP_MODE_CCFS2_SHIFT),
|
||||
- };
|
||||
-
|
||||
- /* default, for the case of IEEE80211_VHT_CHANWIDTH_USE_HT,
|
||||
- * to the previously parsed chandef
|
||||
- */
|
||||
- new_vht_chandef = csa_ie->chandef;
|
||||
+ /* parse one of the Elements to build a new chandef */
|
||||
+ memset(&new_chandef, 0, sizeof(new_chandef));
|
||||
+ new_chandef.chan = new_chan;
|
||||
+
|
||||
+ if (!wide_bw_chansw_ie || !wbcs_elem_to_chandef(wide_bw_chansw_ie,
|
||||
+ &new_chandef)) {
|
||||
+ if (!ieee80211_operating_class_to_chandef(new_op_class, new_chan,
|
||||
+ &new_chandef))
|
||||
+ new_chandef = csa_ie->chandef;
|
||||
+ }
|
||||
|
||||
- /* ignore if parsing fails */
|
||||
- if (!ieee80211_chandef_vht_oper(&sdata->local->hw,
|
||||
- vht_cap_info,
|
||||
- &vht_oper, &ht_oper,
|
||||
- &new_vht_chandef))
|
||||
- new_vht_chandef.chan = NULL;
|
||||
+ /* check if the new chandef fits the capabilities */
|
||||
+ if (new_band == NL80211_BAND_6GHZ)
|
||||
+ validate_chandef_by_6ghz_he_eht_oper(sdata, sta_flags,
|
||||
+ &new_chandef);
|
||||
+ else
|
||||
+ validate_chandef_by_ht_vht_oper(sdata, sta_flags, vht_cap_info,
|
||||
+ &new_chandef);
|
||||
|
||||
+ /* if data is there validate the bandwidth & use it */
|
||||
+ if (new_chandef.chan) {
|
||||
if (sta_flags & IEEE80211_STA_DISABLE_80P80MHZ &&
|
||||
- new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80)
|
||||
- ieee80211_chandef_downgrade(&new_vht_chandef);
|
||||
- if (sta_flags & IEEE80211_STA_DISABLE_160MHZ &&
|
||||
- new_vht_chandef.width == NL80211_CHAN_WIDTH_160)
|
||||
- ieee80211_chandef_downgrade(&new_vht_chandef);
|
||||
- }
|
||||
+ (new_chandef.width == NL80211_CHAN_WIDTH_80P80 ||
|
||||
+ new_chandef.width == NL80211_CHAN_WIDTH_160))
|
||||
+ ieee80211_chandef_downgrade(&new_chandef);
|
||||
|
||||
- /* if VHT data is there validate & use it */
|
||||
- if (new_vht_chandef.chan) {
|
||||
- if (!cfg80211_chandef_compatible(&new_vht_chandef,
|
||||
+ if (!cfg80211_chandef_compatible(&new_chandef,
|
||||
&csa_ie->chandef)) {
|
||||
sdata_info(sdata,
|
||||
"BSS %pM: CSA has inconsistent channel data, disconnecting\n",
|
||||
bssid);
|
||||
return -EINVAL;
|
||||
}
|
||||
- csa_ie->chandef = new_vht_chandef;
|
||||
+ csa_ie->chandef = new_chandef;
|
||||
}
|
||||
|
||||
if (elems->max_channel_switch_time)
|
||||
diff --git a/net/wireless/util.c b/net/wireless/util.c
|
||||
index c2a560f..ddf85be 100644
|
||||
--- a/net/wireless/util.c
|
||||
+++ b/net/wireless/util.c
|
||||
@@ -1668,6 +1668,80 @@ bool ieee80211_operating_class_to_band(u8 operating_class,
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_operating_class_to_band);
|
||||
|
||||
+bool ieee80211_operating_class_to_chandef(u8 operating_class,
|
||||
+ struct ieee80211_channel *chan,
|
||||
+ struct cfg80211_chan_def *chandef)
|
||||
+{
|
||||
+ u32 control_freq, offset = 0;
|
||||
+ enum nl80211_band band;
|
||||
+
|
||||
+ if (!ieee80211_operating_class_to_band(operating_class, &band) ||
|
||||
+ !chan || band != chan->band)
|
||||
+ return false;
|
||||
+
|
||||
+ control_freq = chan->center_freq;
|
||||
+ chandef->chan = chan;
|
||||
+
|
||||
+ if (control_freq >= 5955)
|
||||
+ offset = control_freq - 5955;
|
||||
+ else if (control_freq >= 5745)
|
||||
+ offset = control_freq - 5745;
|
||||
+ else if (control_freq >= 5180)
|
||||
+ offset = control_freq - 5180;
|
||||
+ offset /= 20;
|
||||
+
|
||||
+ switch (operating_class) {
|
||||
+ case 81: /* 2 GHz band; 20 MHz; channels 1..13 */
|
||||
+ case 82: /* 2 GHz band; 20 MHz; channel 14 */
|
||||
+ case 115: /* 5 GHz band; 20 MHz; channels 36,40,44,48 */
|
||||
+ case 118: /* 5 GHz band; 20 MHz; channels 52,56,60,64 */
|
||||
+ case 121: /* 5 GHz band; 20 MHz; channels 100..144 */
|
||||
+ case 124: /* 5 GHz band; 20 MHz; channels 149,153,157,161 */
|
||||
+ case 125: /* 5 GHz band; 20 MHz; channels 149..177 */
|
||||
+ case 131: /* 6 GHz band; 20 MHz; channels 1..233*/
|
||||
+ case 136: /* 6 GHz band; 20 MHz; channel 2 */
|
||||
+ chandef->center_freq1 = control_freq;
|
||||
+ chandef->width = NL80211_CHAN_WIDTH_20;
|
||||
+ return true;
|
||||
+ case 83: /* 2 GHz band; 40 MHz; channels 1..9 */
|
||||
+ case 116: /* 5 GHz band; 40 MHz; channels 36,44 */
|
||||
+ case 119: /* 5 GHz band; 40 MHz; channels 52,60 */
|
||||
+ case 122: /* 5 GHz band; 40 MHz; channels 100,108,116,124,132,140 */
|
||||
+ case 126: /* 5 GHz band; 40 MHz; channels 149,157,165,173 */
|
||||
+ chandef->center_freq1 = control_freq + 10;
|
||||
+ chandef->width = NL80211_CHAN_WIDTH_40;
|
||||
+ return true;
|
||||
+ case 84: /* 2 GHz band; 40 MHz; channels 5..13 */
|
||||
+ case 117: /* 5 GHz band; 40 MHz; channels 40,48 */
|
||||
+ case 120: /* 5 GHz band; 40 MHz; channels 56,64 */
|
||||
+ case 123: /* 5 GHz band; 40 MHz; channels 104,112,120,128,136,144 */
|
||||
+ case 127: /* 5 GHz band; 40 MHz; channels 153,161,169,177 */
|
||||
+ chandef->center_freq1 = control_freq - 10;
|
||||
+ chandef->width = NL80211_CHAN_WIDTH_40;
|
||||
+ return true;
|
||||
+ case 132: /* 6 GHz band; 40 MHz; channels 1,5,..,229*/
|
||||
+ chandef->center_freq1 = control_freq + 10 - (offset & 1) * 20;
|
||||
+ chandef->width = NL80211_CHAN_WIDTH_40;
|
||||
+ return true;
|
||||
+ case 128: /* 5 GHz band; 80 MHz; channels 36..64,100..144,149..177 */
|
||||
+ case 133: /* 6 GHz band; 80 MHz; channels 1,5,..,229 */
|
||||
+ chandef->center_freq1 = control_freq + 30 - (offset & 3) * 20;
|
||||
+ chandef->width = NL80211_CHAN_WIDTH_80;
|
||||
+ return true;
|
||||
+ case 129: /* 5 GHz band; 160 MHz; channels 36..64,100..144,149..177 */
|
||||
+ case 134: /* 6 GHz band; 160 MHz; channels 1,5,..,229 */
|
||||
+ chandef->center_freq1 = control_freq + 70 - (offset & 7) * 20;
|
||||
+ chandef->width = NL80211_CHAN_WIDTH_160;
|
||||
+ return true;
|
||||
+ case 130: /* 5 GHz band; 80+80 MHz; channels 36..64,100..144,149..177 */
|
||||
+ case 135: /* 6 GHz band; 80+80 MHz; channels 1,5,..,229 */
|
||||
+ /* The center_freq2 of 80+80 MHz is unknown */
|
||||
+ default:
|
||||
+ return false;
|
||||
+ }
|
||||
+}
|
||||
+EXPORT_SYMBOL(ieee80211_operating_class_to_chandef);
|
||||
+
|
||||
bool ieee80211_chandef_to_operating_class(struct cfg80211_chan_def *chandef,
|
||||
u8 *op_class)
|
||||
{
|
||||
--
|
||||
2.18.0
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
From 8cb06d601dff5b2728684f230ffe46d42e98cab3 Mon Sep 17 00:00:00 2001
|
||||
From: Peter Chiu <chui-hao.chiu@mediatek.com>
|
||||
Date: Fri, 29 Mar 2024 14:55:41 +0800
|
||||
Subject: [PATCH] mac80211: mtk: send 4 addr nullfunc after drv_event_callback
|
||||
|
||||
mt76 set channel in drv_event_callback and the 4 addr nullfunc may
|
||||
be dropped. Send 4 addr nullfunc after drv_event_callback to avoid
|
||||
this issue.
|
||||
|
||||
Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
|
||||
|
||||
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
|
||||
index ed81ebf..0f3d8cc 100644
|
||||
--- a/net/mac80211/mlme.c
|
||||
+++ b/net/mac80211/mlme.c
|
||||
@@ -3671,13 +3671,6 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
|
||||
bss_conf->assoc_capability = capab_info;
|
||||
ieee80211_set_associated(sdata, cbss, changed);
|
||||
|
||||
- /*
|
||||
- * If we're using 4-addr mode, let the AP know that we're
|
||||
- * doing so, so that it can create the STA VLAN on its side
|
||||
- */
|
||||
- if (ifmgd->use_4addr)
|
||||
- ieee80211_send_4addr_nullfunc(local, sdata);
|
||||
-
|
||||
/*
|
||||
* Start timer to probe the connection to the AP now.
|
||||
* Also start the timer that will detect beacon loss.
|
||||
@@ -3798,6 +3791,13 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
|
||||
drv_event_callback(sdata->local, sdata, &event);
|
||||
sdata_info(sdata, "associated\n");
|
||||
|
||||
+ /*
|
||||
+ * If we're using 4-addr mode, let the AP know that we're
|
||||
+ * doing so, so that it can create the STA VLAN on its side
|
||||
+ */
|
||||
+ if (ifmgd->use_4addr)
|
||||
+ ieee80211_send_4addr_nullfunc(sdata->local, sdata);
|
||||
+
|
||||
/*
|
||||
* destroy assoc_data afterwards, as otherwise an idle
|
||||
* recalc after assoc_data is NULL but before associated
|
||||
--
|
||||
2.18.0
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
From 6f8a21b15ee4078ecf14c11a5cca0d8dee0bc474 Mon Sep 17 00:00:00 2001
|
||||
From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
|
||||
Date: Wed, 3 Apr 2024 14:23:51 +0800
|
||||
Subject: [PATCH] mac80211: mtk: fix incorrect VIF assignment for IEEE 802.11
|
||||
fragments
|
||||
|
||||
When in WDS mode (vif is AP_VLAN), only the first fragment would be dequeued from txq and go through some tx processing which includes the replacement of vif in CB(SKB) from the AP_VLAN one to the AP one. However, the rest of the fragments would be dequeued from txqi->frags and bypass this process which in turn results in different VIF assignment for softmac to fill in TXD. Therefore, we fix this by assigning the correct AP VIF to SKB_CB of the rest fragments.
|
||||
|
||||
Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
|
||||
---
|
||||
net/mac80211/tx.c | 5 ++++-
|
||||
1 file changed, 4 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
|
||||
index 4085444..0577764 100644
|
||||
--- a/net/mac80211/tx.c
|
||||
+++ b/net/mac80211/tx.c
|
||||
@@ -3736,8 +3736,11 @@ begin:
|
||||
skb = __skb_dequeue(&txqi->frags);
|
||||
if (unlikely(skb)) {
|
||||
if (!(IEEE80211_SKB_CB(skb)->control.flags &
|
||||
- IEEE80211_TX_INTCFL_NEED_TXPROCESSING))
|
||||
+ IEEE80211_TX_INTCFL_NEED_TXPROCESSING)) {
|
||||
+ // TODO: report airtime of non-first fragments.
|
||||
+ IEEE80211_SKB_CB(skb)->control.vif = vif;
|
||||
goto out;
|
||||
+ }
|
||||
IEEE80211_SKB_CB(skb)->control.flags &=
|
||||
~IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
|
||||
} else {
|
||||
--
|
||||
2.18.0
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
From ac1e8443a250f418b6124e7b4f4ea65a03c4d02b Mon Sep 17 00:00:00 2001
|
||||
From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
|
||||
Date: Fri, 26 Apr 2024 09:29:39 +0800
|
||||
Subject: [PATCH] mac80211: mtk: add exported function for SoftMAC driver to
|
||||
get QoS map
|
||||
|
||||
The mapping from IP DSCP to IEEE 802.11 user priority may be customized.
|
||||
Therefore, driver needs to pass the mapping to HW, so that the QoS type of traffic can be mapped in a consistent manner for both SW and HW paths.
|
||||
|
||||
Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
|
||||
---
|
||||
include/net/mac80211.h | 12 ++++++++++++
|
||||
net/mac80211/util.c | 10 +++++++++-
|
||||
2 files changed, 21 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
|
||||
index 5c26752..420963f 100644
|
||||
--- a/include/net/mac80211.h
|
||||
+++ b/include/net/mac80211.h
|
||||
@@ -6942,4 +6942,16 @@ static inline bool ieee80211_is_tx_data(struct sk_buff *skb)
|
||||
* @hw: pointer as obtained from ieee80211_alloc_hw()
|
||||
*/
|
||||
unsigned long ieee80211_get_scanning(struct ieee80211_hw *hw);
|
||||
+
|
||||
+/**
|
||||
+ * ieee80211_get_qos_map - get QoS mapping information.
|
||||
+ *
|
||||
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
|
||||
+ *
|
||||
+ * Return: Pointer to the QoS mapping information.
|
||||
+ *
|
||||
+ * Note that the return value is an RCU-protected pointer, so rcu_read_lock()
|
||||
+ * must be held when calling this function.
|
||||
+ */
|
||||
+struct cfg80211_qos_map *ieee80211_get_qos_map(struct ieee80211_vif *vif);
|
||||
#endif /* MAC80211_H */
|
||||
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
|
||||
index e07fe73..865c4ac 100644
|
||||
--- a/net/mac80211/util.c
|
||||
+++ b/net/mac80211/util.c
|
||||
@@ -4643,4 +4643,12 @@ unsigned long ieee80211_get_scanning(struct ieee80211_hw *hw)
|
||||
|
||||
return local->scanning;
|
||||
}
|
||||
-EXPORT_SYMBOL(ieee80211_get_scanning);
|
||||
\ No newline at end of file
|
||||
+EXPORT_SYMBOL(ieee80211_get_scanning);
|
||||
+
|
||||
+struct cfg80211_qos_map *ieee80211_get_qos_map(struct ieee80211_vif *vif)
|
||||
+{
|
||||
+ struct mac80211_qos_map *qos_map = rcu_dereference(vif_to_sdata(vif)->qos_map);
|
||||
+
|
||||
+ return qos_map ? &qos_map->qos_map : NULL;
|
||||
+}
|
||||
+EXPORT_SYMBOL(ieee80211_get_qos_map);
|
||||
--
|
||||
2.18.0
|
||||
|
||||
@@ -11,6 +11,8 @@ FEATURES:=squashfs nand ramdisk fpu
|
||||
KERNEL_PATCHVER:=5.4
|
||||
KERNEL_TESTING_PATCHVER:=5.4
|
||||
|
||||
DEVICE_TYPE:=fw3
|
||||
|
||||
PLATFORM_DIR:=${CURDIR}
|
||||
GENERIC_BACKPORT_DIR := $(PLATFORM_DIR)/backport-$(KERNEL_PATCHVER)
|
||||
GENERIC_PATCH_DIR := $(PLATFORM_DIR)/pending-$(KERNEL_PATCHVER)
|
||||
|
||||
@@ -0,0 +1,295 @@
|
||||
/dts-v1/;
|
||||
#include "mt7981.dtsi"
|
||||
/ {
|
||||
model = "MediaTek MT7981 RFB";
|
||||
compatible = "mediatek,mt7981-spim-snand-2500wan-an8855-gsw-rfb";
|
||||
chosen {
|
||||
bootargs = "console=ttyS0,115200n1 loglevel=8 \
|
||||
earlycon=uart8250,mmio32,0x11002000";
|
||||
};
|
||||
|
||||
memory {
|
||||
// fpga ddr2: 128MB*2
|
||||
reg = <0 0x40000000 0 0x10000000>;
|
||||
};
|
||||
|
||||
gpio-keys {
|
||||
compatible = "gpio-keys";
|
||||
reset {
|
||||
label = "reset";
|
||||
linux,code = <KEY_RESTART>;
|
||||
gpios = <&pio 1 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
|
||||
wps {
|
||||
label = "wps";
|
||||
linux,code = <KEY_WPS_BUTTON>;
|
||||
gpios = <&pio 0 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
||||
|
||||
nmbm_spim_nand {
|
||||
compatible = "generic,nmbm";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
lower-mtd-device = <&spi_nand>;
|
||||
forced-create;
|
||||
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
partition@0 {
|
||||
label = "BL2";
|
||||
reg = <0x00000 0x0100000>;
|
||||
read-only;
|
||||
};
|
||||
|
||||
partition@100000 {
|
||||
label = "u-boot-env";
|
||||
reg = <0x0100000 0x0080000>;
|
||||
};
|
||||
|
||||
factory: partition@180000 {
|
||||
label = "Factory";
|
||||
reg = <0x180000 0x0200000>;
|
||||
};
|
||||
|
||||
partition@380000 {
|
||||
label = "FIP";
|
||||
reg = <0x380000 0x0200000>;
|
||||
};
|
||||
|
||||
partition@580000 {
|
||||
label = "ubi";
|
||||
reg = <0x580000 0x4000000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
gsw: gsw@0 {
|
||||
compatible = "airoha,an8855";
|
||||
#mediatek,ethsys = <ðsys>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
&uart0 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&watchdog {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
ð {
|
||||
status = "okay";
|
||||
|
||||
gmac0: mac@0 {
|
||||
compatible = "mediatek,eth-mac";
|
||||
reg = <0>;
|
||||
phy-mode = "2500base-x";
|
||||
|
||||
fixed-link {
|
||||
speed = <2500>;
|
||||
full-duplex;
|
||||
pause;
|
||||
};
|
||||
};
|
||||
|
||||
gmac1: mac@1 {
|
||||
compatible = "mediatek,eth-mac";
|
||||
reg = <1>;
|
||||
phy-mode = "2500base-x";
|
||||
phy-handle = <&phy5>;
|
||||
};
|
||||
|
||||
mdio: mdio-bus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
reset-gpios = <&pio 14 1>;
|
||||
reset-delay-us = <600>;
|
||||
|
||||
phy5: phy@5 {
|
||||
compatible = "ethernet-phy-ieee802.3-c45";
|
||||
reg = <5>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&gsw {
|
||||
airoha,mdio = <&mdio>;
|
||||
airoha,portmap = "llllw";
|
||||
airoha,smi-addr = <6>;
|
||||
reset-gpios = <&pio 39 0>;
|
||||
interrupt-parent = <&pio>;
|
||||
interrupts = <38 IRQ_TYPE_LEVEL_HIGH>;
|
||||
status = "okay";
|
||||
|
||||
port5: port@5 {
|
||||
compatible = "airoha,an8855-port";
|
||||
reg = <5>;
|
||||
phy-mode = "2500base-x";
|
||||
/* airoha,stag-on = <1>; */
|
||||
|
||||
fixed-link {
|
||||
speed = <2500>;
|
||||
full-duplex;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&hnat {
|
||||
mtketh-wan = "eth1";
|
||||
mtketh-lan = "eth0";
|
||||
mtketh-max-gmac = <2>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&spi0 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&spi0_flash_pins>;
|
||||
status = "okay";
|
||||
spi_nand: spi_nand@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "spi-nand";
|
||||
spi-cal-enable;
|
||||
spi-cal-mode = "read-data";
|
||||
spi-cal-datalen = <7>;
|
||||
spi-cal-data = /bits/ 8 <0x53 0x50 0x49 0x4E 0x41 0x4E 0x44>;
|
||||
spi-cal-addrlen = <5>;
|
||||
spi-cal-addr = /bits/ 32 <0x0 0x0 0x0 0x0 0x0>;
|
||||
reg = <0>;
|
||||
spi-max-frequency = <52000000>;
|
||||
spi-tx-bus-width = <4>;
|
||||
spi-rx-bus-width = <4>;
|
||||
};
|
||||
};
|
||||
|
||||
&spi1 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&spic_pins>;
|
||||
status = "disabled";
|
||||
|
||||
slb9670: slb9670@0 {
|
||||
compatible = "infineon,slb9670";
|
||||
reg = <0>; /* CE0 */
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
spi-cal-enable;
|
||||
spi-cal-mode = "read-data";
|
||||
spi-cal-datalen = <2>;
|
||||
spi-cal-data = /bits/ 8 <0x00 0x1b>;
|
||||
spi-max-frequency = <20000000>;
|
||||
};
|
||||
};
|
||||
|
||||
&wbsys {
|
||||
mediatek,mtd-eeprom = <&factory 0x0000>;
|
||||
status = "okay";
|
||||
pinctrl-names = "dbdc";
|
||||
pinctrl-0 = <&wf_dbdc_pins>;
|
||||
};
|
||||
|
||||
&pio {
|
||||
|
||||
i2c_pins: i2c-pins-g0 {
|
||||
mux {
|
||||
function = "i2c";
|
||||
groups = "i2c0_0";
|
||||
};
|
||||
};
|
||||
|
||||
pcm_pins: pcm-pins-g0 {
|
||||
mux {
|
||||
function = "pcm";
|
||||
groups = "pcm";
|
||||
};
|
||||
};
|
||||
|
||||
pwm0_pin: pwm0-pin-g0 {
|
||||
mux {
|
||||
function = "pwm";
|
||||
groups = "pwm0_0";
|
||||
};
|
||||
};
|
||||
|
||||
pwm1_pin: pwm1-pin-g0 {
|
||||
mux {
|
||||
function = "pwm";
|
||||
groups = "pwm1_0";
|
||||
};
|
||||
};
|
||||
|
||||
pwm2_pin: pwm2-pin {
|
||||
mux {
|
||||
function = "pwm";
|
||||
groups = "pwm2";
|
||||
};
|
||||
};
|
||||
|
||||
spi0_flash_pins: spi0-pins {
|
||||
mux {
|
||||
function = "spi";
|
||||
groups = "spi0", "spi0_wp_hold";
|
||||
};
|
||||
|
||||
conf-pu {
|
||||
pins = "SPI0_CS", "SPI0_HOLD", "SPI0_WP";
|
||||
drive-strength = <MTK_DRIVE_8mA>;
|
||||
bias-pull-up = <MTK_PUPD_SET_R1R0_11>;
|
||||
};
|
||||
|
||||
conf-pd {
|
||||
pins = "SPI0_CLK", "SPI0_MOSI", "SPI0_MISO";
|
||||
drive-strength = <MTK_DRIVE_8mA>;
|
||||
bias-pull-down = <MTK_PUPD_SET_R1R0_11>;
|
||||
};
|
||||
};
|
||||
|
||||
spic_pins: spi1-pins {
|
||||
mux {
|
||||
function = "spi";
|
||||
groups = "spi1_1";
|
||||
};
|
||||
};
|
||||
|
||||
uart1_pins: uart1-pins-g1 {
|
||||
mux {
|
||||
function = "uart";
|
||||
groups = "uart1_1";
|
||||
};
|
||||
};
|
||||
|
||||
uart2_pins: uart2-pins-g1 {
|
||||
mux {
|
||||
function = "uart";
|
||||
groups = "uart2_1";
|
||||
};
|
||||
};
|
||||
|
||||
wf_dbdc_pins: wf_dbdc-pins {
|
||||
mux {
|
||||
function = "eth";
|
||||
groups = "wf0_mode1";
|
||||
};
|
||||
conf {
|
||||
pins = "WF_HB1", "WF_HB2", "WF_HB3", "WF_HB4",
|
||||
"WF_HB0", "WF_HB0_B", "WF_HB5", "WF_HB6",
|
||||
"WF_HB7", "WF_HB8", "WF_HB9", "WF_HB10",
|
||||
"WF_TOP_CLK", "WF_TOP_DATA", "WF_XO_REQ",
|
||||
"WF_CBA_RESETB", "WF_DIG_RESETB";
|
||||
drive-strength = <MTK_DRIVE_4mA>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&xhci {
|
||||
status = "okay";
|
||||
};
|
||||
@@ -0,0 +1,310 @@
|
||||
/dts-v1/;
|
||||
#include "mt7981.dtsi"
|
||||
/ {
|
||||
model = "MediaTek MT7981 RFB";
|
||||
compatible = "mediatek,mt7981-spim-snand-2500wan-an8855-rfb";
|
||||
chosen {
|
||||
bootargs = "console=ttyS0,115200n1 loglevel=8 \
|
||||
earlycon=uart8250,mmio32,0x11002000";
|
||||
};
|
||||
|
||||
memory {
|
||||
// fpga ddr2: 128MB*2
|
||||
reg = <0 0x40000000 0 0x10000000>;
|
||||
};
|
||||
|
||||
gpio-keys {
|
||||
compatible = "gpio-keys";
|
||||
reset {
|
||||
label = "reset";
|
||||
linux,code = <KEY_RESTART>;
|
||||
gpios = <&pio 1 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
|
||||
wps {
|
||||
label = "wps";
|
||||
linux,code = <KEY_WPS_BUTTON>;
|
||||
gpios = <&pio 0 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
||||
|
||||
nmbm_spim_nand {
|
||||
compatible = "generic,nmbm";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
lower-mtd-device = <&spi_nand>;
|
||||
forced-create;
|
||||
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
partition@0 {
|
||||
label = "BL2";
|
||||
reg = <0x00000 0x0100000>;
|
||||
read-only;
|
||||
};
|
||||
|
||||
partition@100000 {
|
||||
label = "u-boot-env";
|
||||
reg = <0x0100000 0x0080000>;
|
||||
};
|
||||
|
||||
factory: partition@180000 {
|
||||
label = "Factory";
|
||||
reg = <0x180000 0x0200000>;
|
||||
};
|
||||
|
||||
partition@380000 {
|
||||
label = "FIP";
|
||||
reg = <0x380000 0x0200000>;
|
||||
};
|
||||
|
||||
partition@580000 {
|
||||
label = "ubi";
|
||||
reg = <0x580000 0x4000000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&uart0 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&watchdog {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
ð {
|
||||
status = "okay";
|
||||
|
||||
gmac0: mac@0 {
|
||||
compatible = "mediatek,eth-mac";
|
||||
reg = <0>;
|
||||
phy-mode = "2500base-x";
|
||||
|
||||
fixed-link {
|
||||
speed = <2500>;
|
||||
full-duplex;
|
||||
pause;
|
||||
};
|
||||
};
|
||||
|
||||
gmac1: mac@1 {
|
||||
compatible = "mediatek,eth-mac";
|
||||
reg = <1>;
|
||||
phy-mode = "2500base-x";
|
||||
phy-handle = <&phy5>;
|
||||
};
|
||||
|
||||
mdio: mdio-bus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
reset-gpios = <&pio 14 1>;
|
||||
reset-delay-us = <600>;
|
||||
|
||||
phy5: phy@5 {
|
||||
compatible = "ethernet-phy-ieee802.3-c45";
|
||||
reg = <5>;
|
||||
};
|
||||
|
||||
switch@0 {
|
||||
compatible = "airoha,an8855";
|
||||
reg = <1>;
|
||||
reset-gpios = <&pio 39 0>;
|
||||
changesmiaddr = <6>;
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
label = "lan1";
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
label = "lan2";
|
||||
};
|
||||
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
label = "lan3";
|
||||
};
|
||||
|
||||
port@3 {
|
||||
reg = <3>;
|
||||
label = "lan4";
|
||||
};
|
||||
|
||||
port@5 {
|
||||
reg = <5>;
|
||||
label = "cpu";
|
||||
ethernet = <&gmac0>;
|
||||
phy-mode = "2500base-x";
|
||||
|
||||
fixed-link {
|
||||
speed = <2500>;
|
||||
full-duplex;
|
||||
pause;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&hnat {
|
||||
mtketh-wan = "eth1";
|
||||
mtketh-lan = "lan";
|
||||
mtketh-max-gmac = <2>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&spi0 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&spi0_flash_pins>;
|
||||
status = "okay";
|
||||
spi_nand: spi_nand@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "spi-nand";
|
||||
spi-cal-enable;
|
||||
spi-cal-mode = "read-data";
|
||||
spi-cal-datalen = <7>;
|
||||
spi-cal-data = /bits/ 8 <0x53 0x50 0x49 0x4E 0x41 0x4E 0x44>;
|
||||
spi-cal-addrlen = <5>;
|
||||
spi-cal-addr = /bits/ 32 <0x0 0x0 0x0 0x0 0x0>;
|
||||
reg = <0>;
|
||||
spi-max-frequency = <52000000>;
|
||||
spi-tx-bus-width = <4>;
|
||||
spi-rx-bus-width = <4>;
|
||||
};
|
||||
};
|
||||
|
||||
&spi1 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&spic_pins>;
|
||||
status = "disabled";
|
||||
|
||||
slb9670: slb9670@0 {
|
||||
compatible = "infineon,slb9670";
|
||||
reg = <0>; /* CE0 */
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
spi-cal-enable;
|
||||
spi-cal-mode = "read-data";
|
||||
spi-cal-datalen = <2>;
|
||||
spi-cal-data = /bits/ 8 <0x00 0x1b>;
|
||||
spi-max-frequency = <20000000>;
|
||||
};
|
||||
};
|
||||
|
||||
&wbsys {
|
||||
mediatek,mtd-eeprom = <&factory 0x0000>;
|
||||
status = "okay";
|
||||
pinctrl-names = "dbdc";
|
||||
pinctrl-0 = <&wf_dbdc_pins>;
|
||||
};
|
||||
|
||||
&pio {
|
||||
|
||||
i2c_pins: i2c-pins-g0 {
|
||||
mux {
|
||||
function = "i2c";
|
||||
groups = "i2c0_0";
|
||||
};
|
||||
};
|
||||
|
||||
pcm_pins: pcm-pins-g0 {
|
||||
mux {
|
||||
function = "pcm";
|
||||
groups = "pcm";
|
||||
};
|
||||
};
|
||||
|
||||
pwm0_pin: pwm0-pin-g0 {
|
||||
mux {
|
||||
function = "pwm";
|
||||
groups = "pwm0_0";
|
||||
};
|
||||
};
|
||||
|
||||
pwm1_pin: pwm1-pin-g0 {
|
||||
mux {
|
||||
function = "pwm";
|
||||
groups = "pwm1_0";
|
||||
};
|
||||
};
|
||||
|
||||
pwm2_pin: pwm2-pin {
|
||||
mux {
|
||||
function = "pwm";
|
||||
groups = "pwm2";
|
||||
};
|
||||
};
|
||||
|
||||
spi0_flash_pins: spi0-pins {
|
||||
mux {
|
||||
function = "spi";
|
||||
groups = "spi0", "spi0_wp_hold";
|
||||
};
|
||||
|
||||
conf-pu {
|
||||
pins = "SPI0_CS", "SPI0_HOLD", "SPI0_WP";
|
||||
drive-strength = <MTK_DRIVE_8mA>;
|
||||
bias-pull-up = <MTK_PUPD_SET_R1R0_11>;
|
||||
};
|
||||
|
||||
conf-pd {
|
||||
pins = "SPI0_CLK", "SPI0_MOSI", "SPI0_MISO";
|
||||
drive-strength = <MTK_DRIVE_8mA>;
|
||||
bias-pull-down = <MTK_PUPD_SET_R1R0_11>;
|
||||
};
|
||||
};
|
||||
|
||||
spic_pins: spi1-pins {
|
||||
mux {
|
||||
function = "spi";
|
||||
groups = "spi1_1";
|
||||
};
|
||||
};
|
||||
|
||||
uart1_pins: uart1-pins-g1 {
|
||||
mux {
|
||||
function = "uart";
|
||||
groups = "uart1_1";
|
||||
};
|
||||
};
|
||||
|
||||
uart2_pins: uart2-pins-g1 {
|
||||
mux {
|
||||
function = "uart";
|
||||
groups = "uart2_1";
|
||||
};
|
||||
};
|
||||
|
||||
wf_dbdc_pins: wf_dbdc-pins {
|
||||
mux {
|
||||
function = "eth";
|
||||
groups = "wf0_mode1";
|
||||
};
|
||||
conf {
|
||||
pins = "WF_HB1", "WF_HB2", "WF_HB3", "WF_HB4",
|
||||
"WF_HB0", "WF_HB0_B", "WF_HB5", "WF_HB6",
|
||||
"WF_HB7", "WF_HB8", "WF_HB9", "WF_HB10",
|
||||
"WF_TOP_CLK", "WF_TOP_DATA", "WF_XO_REQ",
|
||||
"WF_CBA_RESETB", "WF_DIG_RESETB";
|
||||
drive-strength = <MTK_DRIVE_4mA>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&xhci {
|
||||
status = "okay";
|
||||
};
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
memory {
|
||||
// fpga ddr2: 128MB*2
|
||||
reg = <0 0x40000000 0 0x20000000>;
|
||||
reg = <0 0x40000000 0 0x10000000>;
|
||||
};
|
||||
|
||||
gpio-keys {
|
||||
@@ -123,12 +123,6 @@
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&uart2 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&uart2_pins>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&watchdog {
|
||||
status = "okay";
|
||||
};
|
||||
@@ -136,12 +130,17 @@
|
||||
ð {
|
||||
status = "okay";
|
||||
|
||||
gmac0: mac@0 {
|
||||
compatible = "mediatek,eth-mac";
|
||||
reg = <0>;
|
||||
phy-mode = "sgmii";
|
||||
phy-handle = <&phy1>; // add phy handler
|
||||
};
|
||||
gmac0: mac@0 {
|
||||
compatible = "mediatek,eth-mac";
|
||||
reg = <0>;
|
||||
phy-mode = "2500base-x";
|
||||
|
||||
fixed-link {
|
||||
speed = <2500>;
|
||||
full-duplex;
|
||||
pause;
|
||||
};
|
||||
};
|
||||
|
||||
gmac1: mac@1 {
|
||||
compatible = "mediatek,eth-mac";
|
||||
@@ -162,15 +161,49 @@
|
||||
nvmem-cell-names = "phy-cal-data";
|
||||
};
|
||||
|
||||
phy1: ethernet-phy@1 {
|
||||
compatible = "ethernet-phy-id03a2.9471";
|
||||
reg = <24>; // set phy address to 0x18
|
||||
reset-gpios = <&pio 39 1>;
|
||||
reset-assert-us = <600>;
|
||||
reset-deassert-us = <20000>;
|
||||
phy-mode = "sgmii";
|
||||
};
|
||||
switch@0 {
|
||||
compatible = "mediatek,mt7531";
|
||||
reg = <31>;
|
||||
reset-gpios = <&pio 39 0>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
label = "lan1";
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
label = "lan2";
|
||||
};
|
||||
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
label = "lan3";
|
||||
};
|
||||
|
||||
port@3 {
|
||||
reg = <3>;
|
||||
label = "lan4";
|
||||
};
|
||||
|
||||
port@6 {
|
||||
reg = <6>;
|
||||
label = "cpu";
|
||||
ethernet = <&gmac0>;
|
||||
phy-mode = "2500base-x";
|
||||
|
||||
fixed-link {
|
||||
speed = <2500>;
|
||||
full-duplex;
|
||||
pause;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -221,24 +254,6 @@
|
||||
pinctrl-0 = <&wf_dbdc_pins>;
|
||||
};
|
||||
|
||||
&spi2 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&spi2_pins>;
|
||||
status = "okay";
|
||||
|
||||
slb9670: slb9670@0 {
|
||||
compatible = "infineon,slb9670";
|
||||
reg = <0>; /* CE0 */
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
spi-cal-enable;
|
||||
spi-cal-mode = "read-data";
|
||||
spi-cal-datalen = <2>;
|
||||
spi-cal-data = /bits/ 8 <0x00 0x1b>;
|
||||
spi-max-frequency = <40000000>;
|
||||
};
|
||||
};
|
||||
|
||||
&pio {
|
||||
|
||||
i2c_pins: i2c-pins-g0 {
|
||||
@@ -302,13 +317,6 @@
|
||||
};
|
||||
};
|
||||
|
||||
spi2_pins: spi2-pins {
|
||||
mux {
|
||||
function = "spi";
|
||||
groups = "spi2";
|
||||
};
|
||||
};
|
||||
|
||||
uart1_pins: uart1-pins-g1 {
|
||||
mux {
|
||||
function = "uart";
|
||||
|
||||
@@ -0,0 +1,319 @@
|
||||
/dts-v1/;
|
||||
#include "mt7981.dtsi"
|
||||
/ {
|
||||
model = "MediaTek MT7981 RFB";
|
||||
compatible = "mediatek,mt7981-spim-snand-sfp";
|
||||
chosen {
|
||||
bootargs = "console=ttyS0,115200n1 loglevel=8 \
|
||||
earlycon=uart8250,mmio32,0x11002000";
|
||||
};
|
||||
|
||||
memory {
|
||||
// fpga ddr2: 128MB*2
|
||||
reg = <0 0x40000000 0 0x10000000>;
|
||||
};
|
||||
|
||||
gpio-keys {
|
||||
compatible = "gpio-keys";
|
||||
reset {
|
||||
label = "reset";
|
||||
linux,code = <KEY_RESTART>;
|
||||
gpios = <&pio 1 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
|
||||
wps {
|
||||
label = "wps";
|
||||
linux,code = <KEY_WPS_BUTTON>;
|
||||
gpios = <&pio 0 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
||||
|
||||
nmbm_spim_nand {
|
||||
compatible = "generic,nmbm";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
lower-mtd-device = <&spi_nand>;
|
||||
forced-create;
|
||||
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
partition@0 {
|
||||
label = "BL2";
|
||||
reg = <0x00000 0x0100000>;
|
||||
read-only;
|
||||
};
|
||||
|
||||
partition@100000 {
|
||||
label = "u-boot-env";
|
||||
reg = <0x0100000 0x0080000>;
|
||||
};
|
||||
|
||||
factory: partition@180000 {
|
||||
label = "Factory";
|
||||
reg = <0x180000 0x0200000>;
|
||||
};
|
||||
|
||||
partition@380000 {
|
||||
label = "FIP";
|
||||
reg = <0x380000 0x0200000>;
|
||||
};
|
||||
|
||||
partition@580000 {
|
||||
label = "ubi";
|
||||
reg = <0x580000 0x4000000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
sfp_cage1: sfp@1 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c0>;
|
||||
mod-def0-gpios = <&pio 25 1>;
|
||||
los-gpios = <&pio 28 0>;
|
||||
tx-disable-gpios = <&pio 27 0>;
|
||||
};
|
||||
};
|
||||
|
||||
&uart0 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&i2c0 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&i2c_pins>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&watchdog {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
ð {
|
||||
status = "okay";
|
||||
|
||||
gmac0: mac@0 {
|
||||
compatible = "mediatek,eth-mac";
|
||||
reg = <0>;
|
||||
phy-mode = "2500base-x";
|
||||
|
||||
fixed-link {
|
||||
speed = <2500>;
|
||||
full-duplex;
|
||||
pause;
|
||||
};
|
||||
};
|
||||
|
||||
gmac1: mac@1 {
|
||||
compatible = "mediatek,eth-mac";
|
||||
reg = <1>;
|
||||
phy-mode = "2500base-x";
|
||||
managed = "in-band-status";
|
||||
sfp = <&sfp_cage1>;
|
||||
};
|
||||
|
||||
mdio: mdio-bus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
reset-gpios = <&pio 14 1>;
|
||||
reset-delay-us = <600>;
|
||||
|
||||
switch@0 {
|
||||
compatible = "mediatek,mt7531";
|
||||
reg = <31>;
|
||||
reset-gpios = <&pio 39 0>;
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
label = "lan1";
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
label = "lan2";
|
||||
};
|
||||
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
label = "lan3";
|
||||
};
|
||||
|
||||
port@3 {
|
||||
reg = <3>;
|
||||
label = "lan4";
|
||||
};
|
||||
|
||||
port@6 {
|
||||
reg = <6>;
|
||||
label = "cpu";
|
||||
ethernet = <&gmac0>;
|
||||
phy-mode = "2500base-x";
|
||||
|
||||
fixed-link {
|
||||
speed = <2500>;
|
||||
full-duplex;
|
||||
pause;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&hnat {
|
||||
mtketh-wan = "eth1";
|
||||
mtketh-lan = "lan";
|
||||
mtketh-max-gmac = <2>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&spi0 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&spi0_flash_pins>;
|
||||
status = "okay";
|
||||
spi_nand: spi_nand@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "spi-nand";
|
||||
spi-cal-enable;
|
||||
spi-cal-mode = "read-data";
|
||||
spi-cal-datalen = <7>;
|
||||
spi-cal-data = /bits/ 8 <0x53 0x50 0x49 0x4E 0x41 0x4E 0x44>;
|
||||
spi-cal-addrlen = <5>;
|
||||
spi-cal-addr = /bits/ 32 <0x0 0x0 0x0 0x0 0x0>;
|
||||
reg = <0>;
|
||||
spi-max-frequency = <52000000>;
|
||||
spi-tx-bus-width = <4>;
|
||||
spi-rx-bus-width = <4>;
|
||||
};
|
||||
};
|
||||
|
||||
&spi1 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&spic_pins>;
|
||||
status = "disabled";
|
||||
|
||||
slb9670: slb9670@0 {
|
||||
compatible = "infineon,slb9670";
|
||||
reg = <0>; /* CE0 */
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
spi-cal-enable;
|
||||
spi-cal-mode = "read-data";
|
||||
spi-cal-datalen = <2>;
|
||||
spi-cal-data = /bits/ 8 <0x00 0x1b>;
|
||||
spi-max-frequency = <20000000>;
|
||||
};
|
||||
};
|
||||
|
||||
&wbsys {
|
||||
mediatek,mtd-eeprom = <&factory 0x0000>;
|
||||
status = "okay";
|
||||
pinctrl-names = "dbdc";
|
||||
pinctrl-0 = <&wf_dbdc_pins>;
|
||||
};
|
||||
|
||||
&pio {
|
||||
|
||||
i2c_pins: i2c-pins-g1 {
|
||||
mux {
|
||||
function = "i2c";
|
||||
groups = "i2c0_1";
|
||||
};
|
||||
};
|
||||
|
||||
pcm_pins: pcm-pins-g0 {
|
||||
mux {
|
||||
function = "pcm";
|
||||
groups = "pcm";
|
||||
};
|
||||
};
|
||||
|
||||
pwm0_pin: pwm0-pin-g0 {
|
||||
mux {
|
||||
function = "pwm";
|
||||
groups = "pwm0_0";
|
||||
};
|
||||
};
|
||||
|
||||
pwm1_pin: pwm1-pin-g0 {
|
||||
mux {
|
||||
function = "pwm";
|
||||
groups = "pwm1_0";
|
||||
};
|
||||
};
|
||||
|
||||
pwm2_pin: pwm2-pin {
|
||||
mux {
|
||||
function = "pwm";
|
||||
groups = "pwm2";
|
||||
};
|
||||
};
|
||||
|
||||
spi0_flash_pins: spi0-pins {
|
||||
mux {
|
||||
function = "spi";
|
||||
groups = "spi0", "spi0_wp_hold";
|
||||
};
|
||||
|
||||
conf-pu {
|
||||
pins = "SPI0_CS", "SPI0_HOLD", "SPI0_WP";
|
||||
drive-strength = <MTK_DRIVE_8mA>;
|
||||
bias-pull-up = <MTK_PUPD_SET_R1R0_11>;
|
||||
};
|
||||
|
||||
conf-pd {
|
||||
pins = "SPI0_CLK", "SPI0_MOSI", "SPI0_MISO";
|
||||
drive-strength = <MTK_DRIVE_8mA>;
|
||||
bias-pull-down = <MTK_PUPD_SET_R1R0_11>;
|
||||
};
|
||||
};
|
||||
|
||||
spic_pins: spi1-pins {
|
||||
mux {
|
||||
function = "spi";
|
||||
groups = "spi1_1";
|
||||
};
|
||||
};
|
||||
|
||||
uart1_pins: uart1-pins-g1 {
|
||||
mux {
|
||||
function = "uart";
|
||||
groups = "uart1_1";
|
||||
};
|
||||
};
|
||||
|
||||
uart2_pins: uart2-pins-g1 {
|
||||
mux {
|
||||
function = "uart";
|
||||
groups = "uart2_1";
|
||||
};
|
||||
};
|
||||
|
||||
wf_dbdc_pins: wf_dbdc-pins {
|
||||
mux {
|
||||
function = "eth";
|
||||
groups = "wf0_mode1";
|
||||
};
|
||||
conf {
|
||||
pins = "WF_HB1", "WF_HB2", "WF_HB3", "WF_HB4",
|
||||
"WF_HB0", "WF_HB0_B", "WF_HB5", "WF_HB6",
|
||||
"WF_HB7", "WF_HB8", "WF_HB9", "WF_HB10",
|
||||
"WF_TOP_CLK", "WF_TOP_DATA", "WF_XO_REQ",
|
||||
"WF_CBA_RESETB", "WF_DIG_RESETB";
|
||||
drive-strength = <MTK_DRIVE_4mA>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&xhci {
|
||||
status = "okay";
|
||||
};
|
||||
@@ -585,6 +585,7 @@
|
||||
interrupt-names = "ring0", "ring1", "ring2", "ring3";
|
||||
status = "okay";
|
||||
eth = <ð>;
|
||||
hnat = <&hnat>;
|
||||
};
|
||||
|
||||
afe: audio-controller@11210000 {
|
||||
|
||||
@@ -0,0 +1,703 @@
|
||||
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
|
||||
/*
|
||||
* Copyright (C) 2021 MediaTek Inc.
|
||||
* Author: Sam.Shih <sam.shih@mediatek.com>
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
#include <dt-bindings/input/input.h>
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
#include "mt7988.dtsi"
|
||||
|
||||
/ {
|
||||
model = "Bananapi BPI-R4";
|
||||
compatible = "bananapi,bpi-r4", "mediatek,mt7988a";
|
||||
chosen {
|
||||
bootargs = "console=ttyS0,115200n1 loglevel=8 \
|
||||
earlycon=uart8250,mmio32,0x11000000 \
|
||||
earlyprintk pci=pcie_bus_perf \
|
||||
root=PARTLABEL=rootfs rootwait \
|
||||
rootfstype=squashfs,f2fs";
|
||||
};
|
||||
|
||||
memory {
|
||||
reg = <0 0x40000000 0 0x10000000>;
|
||||
};
|
||||
|
||||
gpio-keys {
|
||||
compatible = "gpio-keys";
|
||||
|
||||
reset {
|
||||
label = "reset";
|
||||
linux,code = <BTN_0>;
|
||||
gpios = <&pio 13 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
|
||||
wps {
|
||||
label = "wps";
|
||||
linux,code = <KEY_WPS_BUTTON>;
|
||||
gpios = <&pio 14 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
};
|
||||
|
||||
gpio-leds {
|
||||
compatible = "gpio-leds";
|
||||
|
||||
blue {
|
||||
label = "bpi-r4:pio:blue";
|
||||
gpios = <&pio 63 GPIO_ACTIVE_HIGH>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
green {
|
||||
label = "bpi-r4:pio:green";
|
||||
gpios = <&pio 79 GPIO_ACTIVE_HIGH>;
|
||||
default-state = "off";
|
||||
};
|
||||
};
|
||||
|
||||
nmbm_spim_nand {
|
||||
compatible = "generic,nmbm";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
lower-mtd-device = <&spi_nand>;
|
||||
forced-create;
|
||||
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
partition@0 {
|
||||
label = "BL2";
|
||||
reg = <0x00000 0x0100000>;
|
||||
read-only;
|
||||
};
|
||||
|
||||
partition@100000 {
|
||||
label = "u-boot-env";
|
||||
reg = <0x0100000 0x0080000>;
|
||||
};
|
||||
|
||||
factory: partition@180000 {
|
||||
label = "Factory";
|
||||
reg = <0x180000 0x0400000>;
|
||||
};
|
||||
|
||||
partition@580000 {
|
||||
label = "FIP";
|
||||
reg = <0x580000 0x0200000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
wsys_adie: wsys_adie@0 {
|
||||
// fpga cases need to manual change adie_id / sku_type for dvt only
|
||||
compatible = "mediatek,rebb-mt7988-adie";
|
||||
adie_id = <7976>;
|
||||
sku_type = <3000>;
|
||||
};
|
||||
|
||||
sfp_esp0: sfp@0 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c2_sfp0>;
|
||||
mod-def0-gpios = <&pio 82 1>;
|
||||
los-gpios = <&pio 54 0>;
|
||||
tx-disable-gpios = <&pio 70 0>;
|
||||
maximum-power-milliwatt = <3000>;
|
||||
};
|
||||
|
||||
sfp_esp1: sfp@1 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c2_sfp1>;
|
||||
mod-def0-gpios = <&pio 83 1>;
|
||||
los-gpios = <&pio 2 0>;
|
||||
tx-disable-gpios = <&pio 0 0>;
|
||||
maximum-power-milliwatt = <3000>;
|
||||
};
|
||||
|
||||
reg_1p8v: regulator-1p8v {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "fixed-1.8V";
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
reg_3p3v: regulator-3p3v {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "fixed-3.3V";
|
||||
regulator-min-microvolt = <3300000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
};
|
||||
|
||||
&fan {
|
||||
pwms = <&pwm 0 50000 0>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&pwm {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&uart0 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&uart1 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&uart1_pins>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
&uart2 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&uart2_pins>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
&i2c0 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&i2c0_pins>;
|
||||
status = "okay";
|
||||
|
||||
rt5190a_64: rt5190a@64 {
|
||||
compatible = "richtek,rt5190a";
|
||||
reg = <0x64>;
|
||||
/*interrupts-extended = <&gpio26 0 IRQ_TYPE_LEVEL_LOW>;*/
|
||||
vin2-supply = <&rt5190_buck1>;
|
||||
vin3-supply = <&rt5190_buck1>;
|
||||
vin4-supply = <&rt5190_buck1>;
|
||||
|
||||
regulators {
|
||||
rt5190_buck1: buck1 {
|
||||
regulator-name = "rt5190a-buck1";
|
||||
regulator-min-microvolt = <5090000>;
|
||||
regulator-max-microvolt = <5090000>;
|
||||
regulator-allowed-modes =
|
||||
<RT5190A_OPMODE_AUTO RT5190A_OPMODE_FPWM>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck2 {
|
||||
regulator-name = "vcore";
|
||||
regulator-min-microvolt = <600000>;
|
||||
regulator-max-microvolt = <1400000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck3 {
|
||||
regulator-name = "proc";
|
||||
regulator-min-microvolt = <600000>;
|
||||
regulator-max-microvolt = <1400000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck4 {
|
||||
regulator-name = "rt5190a-buck4";
|
||||
regulator-min-microvolt = <850000>;
|
||||
regulator-max-microvolt = <850000>;
|
||||
regulator-allowed-modes =
|
||||
<RT5190A_OPMODE_AUTO RT5190A_OPMODE_FPWM>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
ldo {
|
||||
regulator-name = "rt5190a-ldo";
|
||||
regulator-min-microvolt = <1200000>;
|
||||
regulator-max-microvolt = <1200000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&i2c1 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&i2c1_pins>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&i2c2 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&i2c2_pins>;
|
||||
status = "okay";
|
||||
|
||||
pca9545@70 {
|
||||
compatible = "nxp,pca9545";
|
||||
reg = <0x70>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
i2c@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x0>;
|
||||
|
||||
// MAC Address EEPROM
|
||||
eeprom@57 {
|
||||
compatible = "atmel,24c02";
|
||||
reg = <0x57>;
|
||||
|
||||
address-bits = <8>;
|
||||
page-size = <8>;
|
||||
size = <256>;
|
||||
};
|
||||
|
||||
// RTC AT8563
|
||||
rtc@51 {
|
||||
compatible = "nxp,pcf8563";
|
||||
reg = <0x51>;
|
||||
};
|
||||
};
|
||||
|
||||
i2c2_sfp0: i2c@1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x1>;
|
||||
};
|
||||
|
||||
i2c2_sfp1: i2c@2 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x2>;
|
||||
};
|
||||
|
||||
i2c@3 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x3>;
|
||||
|
||||
// 5G WIFI MAC Address EEPROM
|
||||
wifi_eeprom@51 {
|
||||
compatible = "atmel,24c02";
|
||||
reg = <0x51>;
|
||||
|
||||
address-bits = <8>;
|
||||
page-size = <8>;
|
||||
size = <256>;
|
||||
};
|
||||
|
||||
// 6G WIFI MAC Address EEPROM
|
||||
wifi_eeprom@52 {
|
||||
compatible = "atmel,24c02";
|
||||
reg = <0x52>;
|
||||
|
||||
address-bits = <8>;
|
||||
page-size = <8>;
|
||||
size = <256>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&spi0 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&spi0_flash_pins>;
|
||||
status = "okay";
|
||||
|
||||
spi_nand: spi_nand@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "spi-nand";
|
||||
spi-cal-enable;
|
||||
spi-cal-mode = "read-data";
|
||||
spi-cal-datalen = <7>;
|
||||
spi-cal-data = /bits/ 8 <0x53 0x50 0x49 0x4E 0x41 0x4E 0x44>;
|
||||
spi-cal-addrlen = <5>;
|
||||
spi-cal-addr = /bits/ 32 <0x0 0x0 0x0 0x0 0x0>;
|
||||
reg = <0>;
|
||||
spi-max-frequency = <52000000>;
|
||||
spi-tx-bus-width = <4>;
|
||||
spi-rx-bus-width = <4>;
|
||||
};
|
||||
};
|
||||
|
||||
&spi1 {
|
||||
pinctrl-names = "default";
|
||||
/* pin shared with snfi */
|
||||
pinctrl-0 = <&spic_pins>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
&pcie0 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pcie0_pins>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&pcie1 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pcie1_pins>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&pcie2 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pcie2_pins>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&pcie3 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pcie3_pins>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&pio {
|
||||
/* GPIO 4 WIFI PWREN. output-high: enable, output-low: disable */
|
||||
asm_sel {
|
||||
gpio-hog;
|
||||
gpios = <4 GPIO_ACTIVE_HIGH>;
|
||||
output-high;
|
||||
};
|
||||
|
||||
/* GPIO 5 PCA9545 RST PIN, output-high */
|
||||
pca9545_rst {
|
||||
gpio-hog;
|
||||
gpios = <5 GPIO_ACTIVE_HIGH>;
|
||||
output-high;
|
||||
};
|
||||
|
||||
mdio0_pins: mdio0-pins {
|
||||
mux {
|
||||
function = "mdio";
|
||||
groups = "mdc_mdio0";
|
||||
};
|
||||
|
||||
conf {
|
||||
groups = "mdc_mdio0";
|
||||
drive-strength = <MTK_DRIVE_10mA>;
|
||||
};
|
||||
};
|
||||
|
||||
gbe0_led0_pins: gbe0-pins {
|
||||
mux {
|
||||
function = "led";
|
||||
groups = "gbe0_led0";
|
||||
};
|
||||
};
|
||||
|
||||
gbe1_led0_pins: gbe1-pins {
|
||||
mux {
|
||||
function = "led";
|
||||
groups = "gbe1_led0";
|
||||
};
|
||||
};
|
||||
|
||||
gbe2_led0_pins: gbe2-pins {
|
||||
mux {
|
||||
function = "led";
|
||||
groups = "gbe2_led0";
|
||||
};
|
||||
};
|
||||
|
||||
gbe3_led0_pins: gbe3-pins {
|
||||
mux {
|
||||
function = "led";
|
||||
groups = "gbe3_led0";
|
||||
};
|
||||
};
|
||||
|
||||
i2p5gbe_led0_pins: 2p5gbe-pins {
|
||||
mux {
|
||||
function = "led";
|
||||
groups = "2p5gbe_led0";
|
||||
};
|
||||
};
|
||||
|
||||
pcie0_pins: pcie0-pins {
|
||||
mux {
|
||||
function = "pcie";
|
||||
groups = "pcie_2l_0_pereset", "pcie_clk_req_n0_0",
|
||||
"pcie_wake_n0_0";
|
||||
};
|
||||
};
|
||||
|
||||
pcie1_pins: pcie1-pins {
|
||||
mux {
|
||||
function = "pcie";
|
||||
groups = "pcie_2l_1_pereset", "pcie_clk_req_n1",
|
||||
"pcie_wake_n1_0";
|
||||
};
|
||||
};
|
||||
|
||||
pcie2_pins: pcie2-pins {
|
||||
mux {
|
||||
function = "pcie";
|
||||
groups = "pcie_1l_0_pereset", "pcie_clk_req_n2_0",
|
||||
"pcie_wake_n2_0";
|
||||
};
|
||||
};
|
||||
|
||||
pcie3_pins: pcie3-pins {
|
||||
mux {
|
||||
function = "pcie";
|
||||
groups = "pcie_1l_1_pereset", "pcie_clk_req_n3",
|
||||
"pcie_wake_n3_0";
|
||||
};
|
||||
};
|
||||
|
||||
spi0_flash_pins: spi0-pins {
|
||||
mux {
|
||||
function = "spi";
|
||||
groups = "spi0", "spi0_wp_hold";
|
||||
};
|
||||
};
|
||||
|
||||
spic_pins: spi1-pins {
|
||||
mux {
|
||||
function = "spi";
|
||||
groups = "spi1";
|
||||
};
|
||||
};
|
||||
|
||||
i2c0_pins: i2c0-pins-g0 {
|
||||
mux {
|
||||
function = "i2c";
|
||||
groups = "i2c0_1";
|
||||
};
|
||||
};
|
||||
|
||||
i2c1_pins: i2c1-pins {
|
||||
mux {
|
||||
function = "i2c";
|
||||
groups = "i2c1_0";
|
||||
};
|
||||
};
|
||||
|
||||
i2c2_pins: i2c2-pins {
|
||||
mux {
|
||||
function = "i2c";
|
||||
groups = "i2c2_1";
|
||||
};
|
||||
};
|
||||
|
||||
uart1_pins: uart1-pins {
|
||||
mux {
|
||||
function = "uart";
|
||||
groups = "tops_uart1_2";
|
||||
};
|
||||
};
|
||||
|
||||
uart2_pins: uart2-pins {
|
||||
mux {
|
||||
function = "uart";
|
||||
groups = "uart2";
|
||||
};
|
||||
};
|
||||
|
||||
mmc0_pins_default: mmc0-pins-default {
|
||||
mux {
|
||||
function = "flash";
|
||||
groups = "emmc_51";
|
||||
};
|
||||
};
|
||||
|
||||
mmc0_pins_uhs: mmc0-pins-uhs {
|
||||
mux {
|
||||
function = "flash";
|
||||
groups = "emmc_51";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&watchdog {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
&xhci0 {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
ð {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&mdio0_pins>;
|
||||
status = "okay";
|
||||
|
||||
gmac0: mac@0 {
|
||||
compatible = "mediatek,eth-mac";
|
||||
reg = <0>;
|
||||
mac-type = "xgdm";
|
||||
phy-mode = "10gbase-kr";
|
||||
|
||||
fixed-link {
|
||||
speed = <10000>;
|
||||
full-duplex;
|
||||
pause;
|
||||
};
|
||||
};
|
||||
|
||||
gmac1: mac@1 {
|
||||
compatible = "mediatek,eth-mac";
|
||||
reg = <1>;
|
||||
mac-type = "xgdm";
|
||||
|
||||
/* support internal 2.5G PHY Copper */
|
||||
//phy-mode = "xgmii";
|
||||
//phy-handle = <&phy0>;
|
||||
|
||||
/* Support external 10G SFP+ Module */
|
||||
phy-mode = "10gbase-kr";
|
||||
managed = "in-band-status";
|
||||
sfp = <&sfp_esp1>;
|
||||
};
|
||||
|
||||
gmac2: mac@2 {
|
||||
compatible = "mediatek,eth-mac";
|
||||
reg = <2>;
|
||||
mac-type = "xgdm";
|
||||
|
||||
/* Support external 10G SFP+ Module */
|
||||
phy-mode = "10gbase-kr";
|
||||
managed = "in-band-status";
|
||||
sfp = <&sfp_esp0>;
|
||||
};
|
||||
|
||||
mdio: mdio-bus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clock-frequency = <10500000>;
|
||||
|
||||
phy0: ethernet-phy@0 {
|
||||
pinctrl-names = "i2p5gbe-led";
|
||||
pinctrl-0 = <&i2p5gbe_led0_pins>;
|
||||
reg = <15>;
|
||||
compatible = "ethernet-phy-ieee802.3-c45";
|
||||
phy-mode = "xgmii";
|
||||
};
|
||||
|
||||
switch@0 {
|
||||
compatible = "mediatek,mt7988";
|
||||
reg = <31>;
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
label = "lan0";
|
||||
phy-mode = "gmii";
|
||||
phy-handle = <&sphy0>;
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
label = "lan1";
|
||||
phy-mode = "gmii";
|
||||
phy-handle = <&sphy1>;
|
||||
};
|
||||
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
label = "lan2";
|
||||
phy-mode = "gmii";
|
||||
phy-handle = <&sphy2>;
|
||||
};
|
||||
|
||||
port@3 {
|
||||
reg = <3>;
|
||||
label = "lan3";
|
||||
phy-mode = "gmii";
|
||||
phy-handle = <&sphy3>;
|
||||
};
|
||||
|
||||
port@6 {
|
||||
reg = <6>;
|
||||
label = "cpu";
|
||||
ethernet = <&gmac0>;
|
||||
phy-mode = "10gbase-kr";
|
||||
|
||||
fixed-link {
|
||||
speed = <10000>;
|
||||
full-duplex;
|
||||
pause;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
mdio {
|
||||
compatible = "mediatek,dsa-slave-mdio";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
sphy0: switch_phy0@0 {
|
||||
compatible = "ethernet-phy-id03a2.9481";
|
||||
reg = <0>;
|
||||
pinctrl-names = "gbe-led";
|
||||
pinctrl-0 = <&gbe0_led0_pins>;
|
||||
nvmem-cells = <&phy_calibration_p0>;
|
||||
nvmem-cell-names = "phy-cal-data";
|
||||
};
|
||||
|
||||
sphy1: switch_phy1@1 {
|
||||
compatible = "ethernet-phy-id03a2.9481";
|
||||
reg = <1>;
|
||||
pinctrl-names = "gbe-led";
|
||||
pinctrl-0 = <&gbe1_led0_pins>;
|
||||
nvmem-cells = <&phy_calibration_p1>;
|
||||
nvmem-cell-names = "phy-cal-data";
|
||||
};
|
||||
|
||||
sphy2: switch_phy2@2 {
|
||||
compatible = "ethernet-phy-id03a2.9481";
|
||||
reg = <2>;
|
||||
pinctrl-names = "gbe-led";
|
||||
pinctrl-0 = <&gbe2_led0_pins>;
|
||||
nvmem-cells = <&phy_calibration_p2>;
|
||||
nvmem-cell-names = "phy-cal-data";
|
||||
};
|
||||
|
||||
sphy3: switch_phy3@3 {
|
||||
compatible = "ethernet-phy-id03a2.9481";
|
||||
reg = <3>;
|
||||
pinctrl-names = "gbe-led";
|
||||
pinctrl-0 = <&gbe3_led0_pins>;
|
||||
nvmem-cells = <&phy_calibration_p3>;
|
||||
nvmem-cell-names = "phy-cal-data";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&hnat {
|
||||
mtketh-wan = "eth2";
|
||||
mtketh-lan = "lan";
|
||||
mtketh-lan2 = "eth1";
|
||||
mtketh-max-gmac = <3>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&crypto {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
&slot0 {
|
||||
mt7996@0,0 {
|
||||
reg = <0x0000 0 0 0 0>;
|
||||
device_type = "pci";
|
||||
mediatek,mtd-eeprom = <&factory 0x0>;
|
||||
};
|
||||
};
|
||||
|
||||
&mmc0 {
|
||||
pinctrl-names = "default", "state_uhs";
|
||||
pinctrl-0 = <&mmc0_pins_default>;
|
||||
pinctrl-1 = <&mmc0_pins_uhs>;
|
||||
bus-width = <8>;
|
||||
max-frequency = <200000000>;
|
||||
cap-mmc-highspeed;
|
||||
mmc-hs200-1_8v;
|
||||
mmc-hs400-1_8v;
|
||||
hs400-ds-delay = <0x12814>;
|
||||
vqmmc-supply = <®_1p8v>;
|
||||
vmmc-supply = <®_3p3v>;
|
||||
non-removable;
|
||||
no-sd;
|
||||
no-sdio;
|
||||
status = "okay";
|
||||
};
|
||||
@@ -0,0 +1,706 @@
|
||||
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
|
||||
/*
|
||||
* Copyright (C) 2021 MediaTek Inc.
|
||||
* Author: Sam.Shih <sam.shih@mediatek.com>
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
#include <dt-bindings/input/input.h>
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
#include "mt7988.dtsi"
|
||||
|
||||
/ {
|
||||
model = "Bananapi BPI-R4";
|
||||
compatible = "bananapi,bpi-r4", "mediatek,mt7988a";
|
||||
chosen {
|
||||
bootargs = "console=ttyS0,115200n1 loglevel=8 \
|
||||
earlycon=uart8250,mmio32,0x11000000 \
|
||||
pci=pcie_bus_perf";
|
||||
};
|
||||
|
||||
memory {
|
||||
reg = <0 0x40000000 0 0x10000000>;
|
||||
};
|
||||
|
||||
gpio-keys {
|
||||
compatible = "gpio-keys";
|
||||
|
||||
reset {
|
||||
label = "reset";
|
||||
linux,code = <BTN_0>;
|
||||
gpios = <&pio 13 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
|
||||
wps {
|
||||
label = "wps";
|
||||
linux,code = <KEY_WPS_BUTTON>;
|
||||
gpios = <&pio 14 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
};
|
||||
|
||||
gpio-leds {
|
||||
compatible = "gpio-leds";
|
||||
|
||||
blue {
|
||||
label = "bpi-r4:pio:blue";
|
||||
gpios = <&pio 63 GPIO_ACTIVE_HIGH>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
green {
|
||||
label = "bpi-r4:pio:green";
|
||||
gpios = <&pio 79 GPIO_ACTIVE_HIGH>;
|
||||
default-state = "off";
|
||||
};
|
||||
};
|
||||
|
||||
nmbm_spim_nand {
|
||||
compatible = "generic,nmbm";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
lower-mtd-device = <&spi_nand>;
|
||||
forced-create;
|
||||
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
partition@0 {
|
||||
label = "BL2";
|
||||
reg = <0x00000 0x0100000>;
|
||||
read-only;
|
||||
};
|
||||
|
||||
partition@100000 {
|
||||
label = "u-boot-env";
|
||||
reg = <0x0100000 0x0080000>;
|
||||
};
|
||||
|
||||
factory: partition@180000 {
|
||||
label = "Factory";
|
||||
reg = <0x180000 0x0400000>;
|
||||
};
|
||||
|
||||
partition@580000 {
|
||||
label = "FIP";
|
||||
reg = <0x580000 0x0200000>;
|
||||
};
|
||||
|
||||
partition@780000 {
|
||||
label = "ubi";
|
||||
reg = <0x780000 0x7080000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
wsys_adie: wsys_adie@0 {
|
||||
// fpga cases need to manual change adie_id / sku_type for dvt only
|
||||
compatible = "mediatek,rebb-mt7988-adie";
|
||||
adie_id = <7976>;
|
||||
sku_type = <3000>;
|
||||
};
|
||||
|
||||
sfp_esp0: sfp@0 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c2_sfp0>;
|
||||
mod-def0-gpios = <&pio 82 1>;
|
||||
los-gpios = <&pio 54 0>;
|
||||
tx-disable-gpios = <&pio 70 0>;
|
||||
maximum-power-milliwatt = <3000>;
|
||||
};
|
||||
|
||||
sfp_esp1: sfp@1 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c2_sfp1>;
|
||||
mod-def0-gpios = <&pio 83 1>;
|
||||
los-gpios = <&pio 2 0>;
|
||||
tx-disable-gpios = <&pio 0 0>;
|
||||
maximum-power-milliwatt = <3000>;
|
||||
};
|
||||
|
||||
reg_1p8v: regulator-1p8v {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "fixed-1.8V";
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
reg_3p3v: regulator-3p3v {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "fixed-3.3V";
|
||||
regulator-min-microvolt = <3300000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
};
|
||||
|
||||
&fan {
|
||||
pwms = <&pwm 0 50000 0>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&pwm {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&uart0 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&uart1 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&uart1_pins>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
&uart2 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&uart2_pins>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
&i2c0 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&i2c0_pins>;
|
||||
status = "okay";
|
||||
|
||||
rt5190a_64: rt5190a@64 {
|
||||
compatible = "richtek,rt5190a";
|
||||
reg = <0x64>;
|
||||
/*interrupts-extended = <&gpio26 0 IRQ_TYPE_LEVEL_LOW>;*/
|
||||
vin2-supply = <&rt5190_buck1>;
|
||||
vin3-supply = <&rt5190_buck1>;
|
||||
vin4-supply = <&rt5190_buck1>;
|
||||
|
||||
regulators {
|
||||
rt5190_buck1: buck1 {
|
||||
regulator-name = "rt5190a-buck1";
|
||||
regulator-min-microvolt = <5090000>;
|
||||
regulator-max-microvolt = <5090000>;
|
||||
regulator-allowed-modes =
|
||||
<RT5190A_OPMODE_AUTO RT5190A_OPMODE_FPWM>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck2 {
|
||||
regulator-name = "vcore";
|
||||
regulator-min-microvolt = <600000>;
|
||||
regulator-max-microvolt = <1400000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck3 {
|
||||
regulator-name = "proc";
|
||||
regulator-min-microvolt = <600000>;
|
||||
regulator-max-microvolt = <1400000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck4 {
|
||||
regulator-name = "rt5190a-buck4";
|
||||
regulator-min-microvolt = <850000>;
|
||||
regulator-max-microvolt = <850000>;
|
||||
regulator-allowed-modes =
|
||||
<RT5190A_OPMODE_AUTO RT5190A_OPMODE_FPWM>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
ldo {
|
||||
regulator-name = "rt5190a-ldo";
|
||||
regulator-min-microvolt = <1200000>;
|
||||
regulator-max-microvolt = <1200000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&i2c1 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&i2c1_pins>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&i2c2 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&i2c2_pins>;
|
||||
status = "okay";
|
||||
|
||||
pca9545@70 {
|
||||
compatible = "nxp,pca9545";
|
||||
reg = <0x70>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
i2c@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x0>;
|
||||
|
||||
// MAC Address EEPROM
|
||||
eeprom@57 {
|
||||
compatible = "atmel,24c02";
|
||||
reg = <0x57>;
|
||||
|
||||
address-bits = <8>;
|
||||
page-size = <8>;
|
||||
size = <256>;
|
||||
};
|
||||
|
||||
// RTC AT8563
|
||||
rtc@51 {
|
||||
compatible = "nxp,pcf8563";
|
||||
reg = <0x51>;
|
||||
};
|
||||
};
|
||||
|
||||
i2c2_sfp0: i2c@1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x1>;
|
||||
};
|
||||
|
||||
i2c2_sfp1: i2c@2 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x2>;
|
||||
};
|
||||
|
||||
i2c@3 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x3>;
|
||||
|
||||
// 5G WIFI MAC Address EEPROM
|
||||
wifi_eeprom@51 {
|
||||
compatible = "atmel,24c02";
|
||||
reg = <0x51>;
|
||||
|
||||
address-bits = <8>;
|
||||
page-size = <8>;
|
||||
size = <256>;
|
||||
};
|
||||
|
||||
// 6G WIFI MAC Address EEPROM
|
||||
wifi_eeprom@52 {
|
||||
compatible = "atmel,24c02";
|
||||
reg = <0x52>;
|
||||
|
||||
address-bits = <8>;
|
||||
page-size = <8>;
|
||||
size = <256>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&spi0 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&spi0_flash_pins>;
|
||||
status = "okay";
|
||||
|
||||
spi_nand: spi_nand@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "spi-nand";
|
||||
spi-cal-enable;
|
||||
spi-cal-mode = "read-data";
|
||||
spi-cal-datalen = <7>;
|
||||
spi-cal-data = /bits/ 8 <0x53 0x50 0x49 0x4E 0x41 0x4E 0x44>;
|
||||
spi-cal-addrlen = <5>;
|
||||
spi-cal-addr = /bits/ 32 <0x0 0x0 0x0 0x0 0x0>;
|
||||
reg = <0>;
|
||||
spi-max-frequency = <52000000>;
|
||||
spi-tx-bus-width = <4>;
|
||||
spi-rx-bus-width = <4>;
|
||||
};
|
||||
};
|
||||
|
||||
&spi1 {
|
||||
pinctrl-names = "default";
|
||||
/* pin shared with snfi */
|
||||
pinctrl-0 = <&spic_pins>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
&pcie0 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pcie0_pins>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&pcie1 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pcie1_pins>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&pcie2 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pcie2_pins>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&pcie3 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pcie3_pins>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&pio {
|
||||
/* GPIO 4 WIFI PWREN. output-high: enable, output-low: disable */
|
||||
asm_sel {
|
||||
gpio-hog;
|
||||
gpios = <4 GPIO_ACTIVE_HIGH>;
|
||||
output-high;
|
||||
};
|
||||
|
||||
/* GPIO 5 PCA9545 RST PIN, output-high */
|
||||
pca9545_rst {
|
||||
gpio-hog;
|
||||
gpios = <5 GPIO_ACTIVE_HIGH>;
|
||||
output-high;
|
||||
};
|
||||
|
||||
mdio0_pins: mdio0-pins {
|
||||
mux {
|
||||
function = "mdio";
|
||||
groups = "mdc_mdio0";
|
||||
};
|
||||
|
||||
conf {
|
||||
groups = "mdc_mdio0";
|
||||
drive-strength = <MTK_DRIVE_10mA>;
|
||||
};
|
||||
};
|
||||
|
||||
gbe0_led0_pins: gbe0-pins {
|
||||
mux {
|
||||
function = "led";
|
||||
groups = "gbe0_led0";
|
||||
};
|
||||
};
|
||||
|
||||
gbe1_led0_pins: gbe1-pins {
|
||||
mux {
|
||||
function = "led";
|
||||
groups = "gbe1_led0";
|
||||
};
|
||||
};
|
||||
|
||||
gbe2_led0_pins: gbe2-pins {
|
||||
mux {
|
||||
function = "led";
|
||||
groups = "gbe2_led0";
|
||||
};
|
||||
};
|
||||
|
||||
gbe3_led0_pins: gbe3-pins {
|
||||
mux {
|
||||
function = "led";
|
||||
groups = "gbe3_led0";
|
||||
};
|
||||
};
|
||||
|
||||
i2p5gbe_led0_pins: 2p5gbe-pins {
|
||||
mux {
|
||||
function = "led";
|
||||
groups = "2p5gbe_led0";
|
||||
};
|
||||
};
|
||||
|
||||
pcie0_pins: pcie0-pins {
|
||||
mux {
|
||||
function = "pcie";
|
||||
groups = "pcie_2l_0_pereset", "pcie_clk_req_n0_0",
|
||||
"pcie_wake_n0_0";
|
||||
};
|
||||
};
|
||||
|
||||
pcie1_pins: pcie1-pins {
|
||||
mux {
|
||||
function = "pcie";
|
||||
groups = "pcie_2l_1_pereset", "pcie_clk_req_n1",
|
||||
"pcie_wake_n1_0";
|
||||
};
|
||||
};
|
||||
|
||||
pcie2_pins: pcie2-pins {
|
||||
mux {
|
||||
function = "pcie";
|
||||
groups = "pcie_1l_0_pereset", "pcie_clk_req_n2_0",
|
||||
"pcie_wake_n2_0";
|
||||
};
|
||||
};
|
||||
|
||||
pcie3_pins: pcie3-pins {
|
||||
mux {
|
||||
function = "pcie";
|
||||
groups = "pcie_1l_1_pereset", "pcie_clk_req_n3",
|
||||
"pcie_wake_n3_0";
|
||||
};
|
||||
};
|
||||
|
||||
spi0_flash_pins: spi0-pins {
|
||||
mux {
|
||||
function = "spi";
|
||||
groups = "spi0", "spi0_wp_hold";
|
||||
};
|
||||
};
|
||||
|
||||
spic_pins: spi1-pins {
|
||||
mux {
|
||||
function = "spi";
|
||||
groups = "spi1";
|
||||
};
|
||||
};
|
||||
|
||||
i2c0_pins: i2c0-pins-g0 {
|
||||
mux {
|
||||
function = "i2c";
|
||||
groups = "i2c0_1";
|
||||
};
|
||||
};
|
||||
|
||||
i2c1_pins: i2c1-pins {
|
||||
mux {
|
||||
function = "i2c";
|
||||
groups = "i2c1_0";
|
||||
};
|
||||
};
|
||||
|
||||
i2c2_pins: i2c2-pins {
|
||||
mux {
|
||||
function = "i2c";
|
||||
groups = "i2c2_1";
|
||||
};
|
||||
};
|
||||
|
||||
uart1_pins: uart1-pins {
|
||||
mux {
|
||||
function = "uart";
|
||||
groups = "tops_uart1_2";
|
||||
};
|
||||
};
|
||||
|
||||
uart2_pins: uart2-pins {
|
||||
mux {
|
||||
function = "uart";
|
||||
groups = "uart2";
|
||||
};
|
||||
};
|
||||
|
||||
mmc0_pins_default: mmc0-pins-default {
|
||||
mux {
|
||||
function = "flash";
|
||||
groups = "emmc_51";
|
||||
};
|
||||
};
|
||||
|
||||
mmc0_pins_uhs: mmc0-pins-uhs {
|
||||
mux {
|
||||
function = "flash";
|
||||
groups = "emmc_51";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&watchdog {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
&xhci0 {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
ð {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&mdio0_pins>;
|
||||
status = "okay";
|
||||
|
||||
gmac0: mac@0 {
|
||||
compatible = "mediatek,eth-mac";
|
||||
reg = <0>;
|
||||
mac-type = "xgdm";
|
||||
phy-mode = "10gbase-kr";
|
||||
|
||||
fixed-link {
|
||||
speed = <10000>;
|
||||
full-duplex;
|
||||
pause;
|
||||
};
|
||||
};
|
||||
|
||||
gmac1: mac@1 {
|
||||
compatible = "mediatek,eth-mac";
|
||||
reg = <1>;
|
||||
mac-type = "xgdm";
|
||||
|
||||
/* support internal 2.5G PHY Copper */
|
||||
//phy-mode = "xgmii";
|
||||
//phy-handle = <&phy0>;
|
||||
|
||||
/* Support external 10G SFP+ Module */
|
||||
phy-mode = "10gbase-kr";
|
||||
managed = "in-band-status";
|
||||
sfp = <&sfp_esp1>;
|
||||
};
|
||||
|
||||
gmac2: mac@2 {
|
||||
compatible = "mediatek,eth-mac";
|
||||
reg = <2>;
|
||||
mac-type = "xgdm";
|
||||
|
||||
/* Support external 10G SFP+ Module */
|
||||
phy-mode = "10gbase-kr";
|
||||
managed = "in-band-status";
|
||||
sfp = <&sfp_esp0>;
|
||||
};
|
||||
|
||||
mdio: mdio-bus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clock-frequency = <10500000>;
|
||||
|
||||
phy0: ethernet-phy@0 {
|
||||
pinctrl-names = "i2p5gbe-led";
|
||||
pinctrl-0 = <&i2p5gbe_led0_pins>;
|
||||
reg = <15>;
|
||||
compatible = "ethernet-phy-ieee802.3-c45";
|
||||
phy-mode = "xgmii";
|
||||
};
|
||||
|
||||
switch@0 {
|
||||
compatible = "mediatek,mt7988";
|
||||
reg = <31>;
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
label = "lan0";
|
||||
phy-mode = "gmii";
|
||||
phy-handle = <&sphy0>;
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
label = "lan1";
|
||||
phy-mode = "gmii";
|
||||
phy-handle = <&sphy1>;
|
||||
};
|
||||
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
label = "lan2";
|
||||
phy-mode = "gmii";
|
||||
phy-handle = <&sphy2>;
|
||||
};
|
||||
|
||||
port@3 {
|
||||
reg = <3>;
|
||||
label = "lan3";
|
||||
phy-mode = "gmii";
|
||||
phy-handle = <&sphy3>;
|
||||
};
|
||||
|
||||
port@6 {
|
||||
reg = <6>;
|
||||
label = "cpu";
|
||||
ethernet = <&gmac0>;
|
||||
phy-mode = "10gbase-kr";
|
||||
|
||||
fixed-link {
|
||||
speed = <10000>;
|
||||
full-duplex;
|
||||
pause;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
mdio {
|
||||
compatible = "mediatek,dsa-slave-mdio";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
sphy0: switch_phy0@0 {
|
||||
compatible = "ethernet-phy-id03a2.9481";
|
||||
reg = <0>;
|
||||
pinctrl-names = "gbe-led";
|
||||
pinctrl-0 = <&gbe0_led0_pins>;
|
||||
nvmem-cells = <&phy_calibration_p0>;
|
||||
nvmem-cell-names = "phy-cal-data";
|
||||
};
|
||||
|
||||
sphy1: switch_phy1@1 {
|
||||
compatible = "ethernet-phy-id03a2.9481";
|
||||
reg = <1>;
|
||||
pinctrl-names = "gbe-led";
|
||||
pinctrl-0 = <&gbe1_led0_pins>;
|
||||
nvmem-cells = <&phy_calibration_p1>;
|
||||
nvmem-cell-names = "phy-cal-data";
|
||||
};
|
||||
|
||||
sphy2: switch_phy2@2 {
|
||||
compatible = "ethernet-phy-id03a2.9481";
|
||||
reg = <2>;
|
||||
pinctrl-names = "gbe-led";
|
||||
pinctrl-0 = <&gbe2_led0_pins>;
|
||||
nvmem-cells = <&phy_calibration_p2>;
|
||||
nvmem-cell-names = "phy-cal-data";
|
||||
};
|
||||
|
||||
sphy3: switch_phy3@3 {
|
||||
compatible = "ethernet-phy-id03a2.9481";
|
||||
reg = <3>;
|
||||
pinctrl-names = "gbe-led";
|
||||
pinctrl-0 = <&gbe3_led0_pins>;
|
||||
nvmem-cells = <&phy_calibration_p3>;
|
||||
nvmem-cell-names = "phy-cal-data";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&hnat {
|
||||
mtketh-wan = "eth2";
|
||||
mtketh-lan = "lan";
|
||||
mtketh-lan2 = "eth1";
|
||||
mtketh-max-gmac = <3>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&crypto {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
&slot0 {
|
||||
mt7996@0,0 {
|
||||
reg = <0x0000 0 0 0 0>;
|
||||
device_type = "pci";
|
||||
mediatek,mtd-eeprom = <&factory 0x0>;
|
||||
};
|
||||
};
|
||||
|
||||
&mmc0 {
|
||||
pinctrl-names = "default", "state_uhs";
|
||||
pinctrl-0 = <&mmc0_pins_default>;
|
||||
pinctrl-1 = <&mmc0_pins_uhs>;
|
||||
bus-width = <8>;
|
||||
max-frequency = <200000000>;
|
||||
cap-mmc-highspeed;
|
||||
mmc-hs200-1_8v;
|
||||
mmc-hs400-1_8v;
|
||||
hs400-ds-delay = <0x12814>;
|
||||
vqmmc-supply = <®_1p8v>;
|
||||
vmmc-supply = <®_3p3v>;
|
||||
non-removable;
|
||||
no-sd;
|
||||
no-sdio;
|
||||
status = "okay";
|
||||
};
|
||||
@@ -0,0 +1,690 @@
|
||||
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
|
||||
/*
|
||||
* Copyright (C) 2021 MediaTek Inc.
|
||||
* Author: Sam.Shih <sam.shih@mediatek.com>
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
#include <dt-bindings/input/input.h>
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
#include "mt7988.dtsi"
|
||||
|
||||
/ {
|
||||
model = "Bananapi BPI-R4";
|
||||
compatible = "bananapi,bpi-r4", "mediatek,mt7988a";
|
||||
chosen {
|
||||
bootargs = "console=ttyS0,115200n1 loglevel=8 \
|
||||
earlycon=uart8250,mmio32,0x11000000 \
|
||||
earlyprintk pci=pcie_bus_perf \
|
||||
root=PARTLABEL=rootfs rootwait \
|
||||
rootfstype=squashfs,f2fs";
|
||||
};
|
||||
|
||||
memory {
|
||||
reg = <0 0x40000000 0 0x10000000>;
|
||||
};
|
||||
|
||||
gpio-keys {
|
||||
compatible = "gpio-keys";
|
||||
|
||||
reset {
|
||||
label = "reset";
|
||||
linux,code = <BTN_0>;
|
||||
gpios = <&pio 13 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
|
||||
wps {
|
||||
label = "wps";
|
||||
linux,code = <KEY_WPS_BUTTON>;
|
||||
gpios = <&pio 14 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
};
|
||||
|
||||
gpio-leds {
|
||||
compatible = "gpio-leds";
|
||||
|
||||
blue {
|
||||
label = "bpi-r4:pio:blue";
|
||||
gpios = <&pio 63 GPIO_ACTIVE_HIGH>;
|
||||
default-state = "off";
|
||||
};
|
||||
|
||||
green {
|
||||
label = "bpi-r4:pio:green";
|
||||
gpios = <&pio 79 GPIO_ACTIVE_HIGH>;
|
||||
default-state = "off";
|
||||
};
|
||||
};
|
||||
|
||||
nmbm_spim_nand {
|
||||
compatible = "generic,nmbm";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
lower-mtd-device = <&spi_nand>;
|
||||
forced-create;
|
||||
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
partition@0 {
|
||||
label = "BL2";
|
||||
reg = <0x00000 0x0100000>;
|
||||
read-only;
|
||||
};
|
||||
|
||||
partition@100000 {
|
||||
label = "u-boot-env";
|
||||
reg = <0x0100000 0x0080000>;
|
||||
};
|
||||
|
||||
factory: partition@180000 {
|
||||
label = "Factory";
|
||||
reg = <0x180000 0x0400000>;
|
||||
};
|
||||
|
||||
partition@580000 {
|
||||
label = "FIP";
|
||||
reg = <0x580000 0x0200000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
wsys_adie: wsys_adie@0 {
|
||||
// fpga cases need to manual change adie_id / sku_type for dvt only
|
||||
compatible = "mediatek,rebb-mt7988-adie";
|
||||
adie_id = <7976>;
|
||||
sku_type = <3000>;
|
||||
};
|
||||
|
||||
sfp_esp0: sfp@0 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c2_sfp0>;
|
||||
mod-def0-gpios = <&pio 82 1>;
|
||||
los-gpios = <&pio 54 0>;
|
||||
tx-disable-gpios = <&pio 70 0>;
|
||||
maximum-power-milliwatt = <3000>;
|
||||
};
|
||||
|
||||
sfp_esp1: sfp@1 {
|
||||
compatible = "sff,sfp";
|
||||
i2c-bus = <&i2c2_sfp1>;
|
||||
mod-def0-gpios = <&pio 83 1>;
|
||||
los-gpios = <&pio 2 0>;
|
||||
tx-disable-gpios = <&pio 0 0>;
|
||||
maximum-power-milliwatt = <3000>;
|
||||
};
|
||||
|
||||
reg_3p3v: regulator-3p3v {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "fixed-3.3V";
|
||||
regulator-min-microvolt = <3300000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
};
|
||||
|
||||
&fan {
|
||||
pwms = <&pwm 0 50000 0>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&pwm {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&uart0 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&uart1 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&uart1_pins>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
&uart2 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&uart2_pins>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
&i2c0 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&i2c0_pins>;
|
||||
status = "okay";
|
||||
|
||||
rt5190a_64: rt5190a@64 {
|
||||
compatible = "richtek,rt5190a";
|
||||
reg = <0x64>;
|
||||
/*interrupts-extended = <&gpio26 0 IRQ_TYPE_LEVEL_LOW>;*/
|
||||
vin2-supply = <&rt5190_buck1>;
|
||||
vin3-supply = <&rt5190_buck1>;
|
||||
vin4-supply = <&rt5190_buck1>;
|
||||
|
||||
regulators {
|
||||
rt5190_buck1: buck1 {
|
||||
regulator-name = "rt5190a-buck1";
|
||||
regulator-min-microvolt = <5090000>;
|
||||
regulator-max-microvolt = <5090000>;
|
||||
regulator-allowed-modes =
|
||||
<RT5190A_OPMODE_AUTO RT5190A_OPMODE_FPWM>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck2 {
|
||||
regulator-name = "vcore";
|
||||
regulator-min-microvolt = <600000>;
|
||||
regulator-max-microvolt = <1400000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck3 {
|
||||
regulator-name = "proc";
|
||||
regulator-min-microvolt = <600000>;
|
||||
regulator-max-microvolt = <1400000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck4 {
|
||||
regulator-name = "rt5190a-buck4";
|
||||
regulator-min-microvolt = <850000>;
|
||||
regulator-max-microvolt = <850000>;
|
||||
regulator-allowed-modes =
|
||||
<RT5190A_OPMODE_AUTO RT5190A_OPMODE_FPWM>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
ldo {
|
||||
regulator-name = "rt5190a-ldo";
|
||||
regulator-min-microvolt = <1200000>;
|
||||
regulator-max-microvolt = <1200000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&i2c1 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&i2c1_pins>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&i2c2 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&i2c2_pins>;
|
||||
status = "okay";
|
||||
|
||||
pca9545@70 {
|
||||
compatible = "nxp,pca9545";
|
||||
reg = <0x70>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
i2c@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x0>;
|
||||
|
||||
// MAC Address EEPROM
|
||||
eeprom@57 {
|
||||
compatible = "atmel,24c02";
|
||||
reg = <0x57>;
|
||||
|
||||
address-bits = <8>;
|
||||
page-size = <8>;
|
||||
size = <256>;
|
||||
};
|
||||
|
||||
// RTC AT8563
|
||||
rtc@51 {
|
||||
compatible = "nxp,pcf8563";
|
||||
reg = <0x51>;
|
||||
};
|
||||
};
|
||||
|
||||
i2c2_sfp0: i2c@1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x1>;
|
||||
};
|
||||
|
||||
i2c2_sfp1: i2c@2 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x2>;
|
||||
};
|
||||
|
||||
i2c@3 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x3>;
|
||||
|
||||
// 5G WIFI MAC Address EEPROM
|
||||
wifi_eeprom@51 {
|
||||
compatible = "atmel,24c02";
|
||||
reg = <0x51>;
|
||||
|
||||
address-bits = <8>;
|
||||
page-size = <8>;
|
||||
size = <256>;
|
||||
};
|
||||
|
||||
// 6G WIFI MAC Address EEPROM
|
||||
wifi_eeprom@52 {
|
||||
compatible = "atmel,24c02";
|
||||
reg = <0x52>;
|
||||
|
||||
address-bits = <8>;
|
||||
page-size = <8>;
|
||||
size = <256>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&spi0 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&spi0_flash_pins>;
|
||||
status = "okay";
|
||||
|
||||
spi_nand: spi_nand@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "spi-nand";
|
||||
spi-cal-enable;
|
||||
spi-cal-mode = "read-data";
|
||||
spi-cal-datalen = <7>;
|
||||
spi-cal-data = /bits/ 8 <0x53 0x50 0x49 0x4E 0x41 0x4E 0x44>;
|
||||
spi-cal-addrlen = <5>;
|
||||
spi-cal-addr = /bits/ 32 <0x0 0x0 0x0 0x0 0x0>;
|
||||
reg = <0>;
|
||||
spi-max-frequency = <52000000>;
|
||||
spi-tx-bus-width = <4>;
|
||||
spi-rx-bus-width = <4>;
|
||||
};
|
||||
};
|
||||
|
||||
&spi1 {
|
||||
pinctrl-names = "default";
|
||||
/* pin shared with snfi */
|
||||
pinctrl-0 = <&spic_pins>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
&pcie0 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pcie0_pins>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&pcie1 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pcie1_pins>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&pcie2 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pcie2_pins>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&pcie3 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pcie3_pins>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&pio {
|
||||
/* GPIO 4 WIFI PWREN. output-high: enable, output-low: disable */
|
||||
asm_sel {
|
||||
gpio-hog;
|
||||
gpios = <4 GPIO_ACTIVE_HIGH>;
|
||||
output-high;
|
||||
};
|
||||
|
||||
/* GPIO 5 PCA9545 RST PIN, output-high */
|
||||
pca9545_rst {
|
||||
gpio-hog;
|
||||
gpios = <5 GPIO_ACTIVE_HIGH>;
|
||||
output-high;
|
||||
};
|
||||
|
||||
mdio0_pins: mdio0-pins {
|
||||
mux {
|
||||
function = "mdio";
|
||||
groups = "mdc_mdio0";
|
||||
};
|
||||
|
||||
conf {
|
||||
groups = "mdc_mdio0";
|
||||
drive-strength = <MTK_DRIVE_10mA>;
|
||||
};
|
||||
};
|
||||
|
||||
gbe0_led0_pins: gbe0-pins {
|
||||
mux {
|
||||
function = "led";
|
||||
groups = "gbe0_led0";
|
||||
};
|
||||
};
|
||||
|
||||
gbe1_led0_pins: gbe1-pins {
|
||||
mux {
|
||||
function = "led";
|
||||
groups = "gbe1_led0";
|
||||
};
|
||||
};
|
||||
|
||||
gbe2_led0_pins: gbe2-pins {
|
||||
mux {
|
||||
function = "led";
|
||||
groups = "gbe2_led0";
|
||||
};
|
||||
};
|
||||
|
||||
gbe3_led0_pins: gbe3-pins {
|
||||
mux {
|
||||
function = "led";
|
||||
groups = "gbe3_led0";
|
||||
};
|
||||
};
|
||||
|
||||
i2p5gbe_led0_pins: 2p5gbe-pins {
|
||||
mux {
|
||||
function = "led";
|
||||
groups = "2p5gbe_led0";
|
||||
};
|
||||
};
|
||||
|
||||
pcie0_pins: pcie0-pins {
|
||||
mux {
|
||||
function = "pcie";
|
||||
groups = "pcie_2l_0_pereset", "pcie_clk_req_n0_0",
|
||||
"pcie_wake_n0_0";
|
||||
};
|
||||
};
|
||||
|
||||
pcie1_pins: pcie1-pins {
|
||||
mux {
|
||||
function = "pcie";
|
||||
groups = "pcie_2l_1_pereset", "pcie_clk_req_n1",
|
||||
"pcie_wake_n1_0";
|
||||
};
|
||||
};
|
||||
|
||||
pcie2_pins: pcie2-pins {
|
||||
mux {
|
||||
function = "pcie";
|
||||
groups = "pcie_1l_0_pereset", "pcie_clk_req_n2_0",
|
||||
"pcie_wake_n2_0";
|
||||
};
|
||||
};
|
||||
|
||||
pcie3_pins: pcie3-pins {
|
||||
mux {
|
||||
function = "pcie";
|
||||
groups = "pcie_1l_1_pereset", "pcie_clk_req_n3",
|
||||
"pcie_wake_n3_0";
|
||||
};
|
||||
};
|
||||
|
||||
spi0_flash_pins: spi0-pins {
|
||||
mux {
|
||||
function = "spi";
|
||||
groups = "spi0", "spi0_wp_hold";
|
||||
};
|
||||
};
|
||||
|
||||
spic_pins: spi1-pins {
|
||||
mux {
|
||||
function = "spi";
|
||||
groups = "spi1";
|
||||
};
|
||||
};
|
||||
|
||||
i2c0_pins: i2c0-pins-g0 {
|
||||
mux {
|
||||
function = "i2c";
|
||||
groups = "i2c0_1";
|
||||
};
|
||||
};
|
||||
|
||||
i2c1_pins: i2c1-pins {
|
||||
mux {
|
||||
function = "i2c";
|
||||
groups = "i2c1_0";
|
||||
};
|
||||
};
|
||||
|
||||
i2c2_pins: i2c2-pins {
|
||||
mux {
|
||||
function = "i2c";
|
||||
groups = "i2c2_1";
|
||||
};
|
||||
};
|
||||
|
||||
uart1_pins: uart1-pins {
|
||||
mux {
|
||||
function = "uart";
|
||||
groups = "tops_uart1_2";
|
||||
};
|
||||
};
|
||||
|
||||
uart2_pins: uart2-pins {
|
||||
mux {
|
||||
function = "uart";
|
||||
groups = "uart2";
|
||||
};
|
||||
};
|
||||
|
||||
mmc0_pins_default: mmc0-pins-default {
|
||||
mux {
|
||||
function = "flash";
|
||||
groups = "sdcard";
|
||||
};
|
||||
};
|
||||
|
||||
mmc0_pins_uhs: mmc0-pins-uhs {
|
||||
mux {
|
||||
function = "flash";
|
||||
groups = "sdcard";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&watchdog {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
&xhci0 {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
ð {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&mdio0_pins>;
|
||||
status = "okay";
|
||||
|
||||
gmac0: mac@0 {
|
||||
compatible = "mediatek,eth-mac";
|
||||
reg = <0>;
|
||||
mac-type = "xgdm";
|
||||
phy-mode = "10gbase-kr";
|
||||
|
||||
fixed-link {
|
||||
speed = <10000>;
|
||||
full-duplex;
|
||||
pause;
|
||||
};
|
||||
};
|
||||
|
||||
gmac1: mac@1 {
|
||||
compatible = "mediatek,eth-mac";
|
||||
reg = <1>;
|
||||
mac-type = "xgdm";
|
||||
|
||||
/* support internal 2.5G PHY Copper */
|
||||
//phy-mode = "xgmii";
|
||||
//phy-handle = <&phy0>;
|
||||
|
||||
/* Support external 10G SFP+ Module */
|
||||
phy-mode = "10gbase-kr";
|
||||
managed = "in-band-status";
|
||||
sfp = <&sfp_esp1>;
|
||||
};
|
||||
|
||||
gmac2: mac@2 {
|
||||
compatible = "mediatek,eth-mac";
|
||||
reg = <2>;
|
||||
mac-type = "xgdm";
|
||||
|
||||
/* Support external 10G SFP+ Module */
|
||||
phy-mode = "10gbase-kr";
|
||||
managed = "in-band-status";
|
||||
sfp = <&sfp_esp0>;
|
||||
};
|
||||
|
||||
mdio: mdio-bus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clock-frequency = <10500000>;
|
||||
|
||||
phy0: ethernet-phy@0 {
|
||||
pinctrl-names = "i2p5gbe-led";
|
||||
pinctrl-0 = <&i2p5gbe_led0_pins>;
|
||||
reg = <15>;
|
||||
compatible = "ethernet-phy-ieee802.3-c45";
|
||||
phy-mode = "xgmii";
|
||||
};
|
||||
|
||||
switch@0 {
|
||||
compatible = "mediatek,mt7988";
|
||||
reg = <31>;
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
label = "lan0";
|
||||
phy-mode = "gmii";
|
||||
phy-handle = <&sphy0>;
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
label = "lan1";
|
||||
phy-mode = "gmii";
|
||||
phy-handle = <&sphy1>;
|
||||
};
|
||||
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
label = "lan2";
|
||||
phy-mode = "gmii";
|
||||
phy-handle = <&sphy2>;
|
||||
};
|
||||
|
||||
port@3 {
|
||||
reg = <3>;
|
||||
label = "lan3";
|
||||
phy-mode = "gmii";
|
||||
phy-handle = <&sphy3>;
|
||||
};
|
||||
|
||||
port@6 {
|
||||
reg = <6>;
|
||||
label = "cpu";
|
||||
ethernet = <&gmac0>;
|
||||
phy-mode = "10gbase-kr";
|
||||
|
||||
fixed-link {
|
||||
speed = <10000>;
|
||||
full-duplex;
|
||||
pause;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
mdio {
|
||||
compatible = "mediatek,dsa-slave-mdio";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
sphy0: switch_phy0@0 {
|
||||
compatible = "ethernet-phy-id03a2.9481";
|
||||
reg = <0>;
|
||||
pinctrl-names = "gbe-led";
|
||||
pinctrl-0 = <&gbe0_led0_pins>;
|
||||
nvmem-cells = <&phy_calibration_p0>;
|
||||
nvmem-cell-names = "phy-cal-data";
|
||||
};
|
||||
|
||||
sphy1: switch_phy1@1 {
|
||||
compatible = "ethernet-phy-id03a2.9481";
|
||||
reg = <1>;
|
||||
pinctrl-names = "gbe-led";
|
||||
pinctrl-0 = <&gbe1_led0_pins>;
|
||||
nvmem-cells = <&phy_calibration_p1>;
|
||||
nvmem-cell-names = "phy-cal-data";
|
||||
};
|
||||
|
||||
sphy2: switch_phy2@2 {
|
||||
compatible = "ethernet-phy-id03a2.9481";
|
||||
reg = <2>;
|
||||
pinctrl-names = "gbe-led";
|
||||
pinctrl-0 = <&gbe2_led0_pins>;
|
||||
nvmem-cells = <&phy_calibration_p2>;
|
||||
nvmem-cell-names = "phy-cal-data";
|
||||
};
|
||||
|
||||
sphy3: switch_phy3@3 {
|
||||
compatible = "ethernet-phy-id03a2.9481";
|
||||
reg = <3>;
|
||||
pinctrl-names = "gbe-led";
|
||||
pinctrl-0 = <&gbe3_led0_pins>;
|
||||
nvmem-cells = <&phy_calibration_p3>;
|
||||
nvmem-cell-names = "phy-cal-data";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&hnat {
|
||||
mtketh-wan = "eth2";
|
||||
mtketh-lan = "lan";
|
||||
mtketh-lan2 = "eth1";
|
||||
mtketh-max-gmac = <3>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&crypto {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
&slot0 {
|
||||
mt7996@0,0 {
|
||||
reg = <0x0000 0 0 0 0>;
|
||||
device_type = "pci";
|
||||
mediatek,mtd-eeprom = <&factory 0x0>;
|
||||
};
|
||||
};
|
||||
|
||||
&mmc0 {
|
||||
pinctrl-names = "default", "state_uhs";
|
||||
pinctrl-0 = <&mmc0_pins_default>;
|
||||
pinctrl-1 = <&mmc0_pins_uhs>;
|
||||
bus-width = <4>;
|
||||
max-frequency = <52000000>;
|
||||
cap-sd-highspeed;
|
||||
vmmc-supply = <®_3p3v>;
|
||||
vqmmc-supply = <®_3p3v>;
|
||||
no-mmc;
|
||||
no-sdio;
|
||||
status = "okay";
|
||||
};
|
||||
@@ -63,6 +63,58 @@
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&i2c0 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&i2c0_pins>;
|
||||
status = "okay";
|
||||
|
||||
rt5190a_64: rt5190a@64 {
|
||||
compatible = "richtek,rt5190a";
|
||||
reg = <0x64>;
|
||||
/*interrupts-extended = <&gpio26 0 IRQ_TYPE_LEVEL_LOW>;*/
|
||||
vin2-supply = <&rt5190_buck1>;
|
||||
vin3-supply = <&rt5190_buck1>;
|
||||
vin4-supply = <&rt5190_buck1>;
|
||||
|
||||
regulators {
|
||||
rt5190_buck1: buck1 {
|
||||
regulator-name = "rt5190a-buck1";
|
||||
regulator-min-microvolt = <5090000>;
|
||||
regulator-max-microvolt = <5090000>;
|
||||
regulator-allowed-modes =
|
||||
<RT5190A_OPMODE_AUTO RT5190A_OPMODE_FPWM>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck2 {
|
||||
regulator-name = "vcore";
|
||||
regulator-min-microvolt = <600000>;
|
||||
regulator-max-microvolt = <1400000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck3 {
|
||||
regulator-name = "proc";
|
||||
regulator-min-microvolt = <600000>;
|
||||
regulator-max-microvolt = <1400000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck4 {
|
||||
regulator-name = "rt5190a-buck4";
|
||||
regulator-min-microvolt = <850000>;
|
||||
regulator-max-microvolt = <850000>;
|
||||
regulator-allowed-modes =
|
||||
<RT5190A_OPMODE_AUTO RT5190A_OPMODE_FPWM>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
ldo {
|
||||
regulator-name = "rt5190a-ldo";
|
||||
regulator-min-microvolt = <1200000>;
|
||||
regulator-max-microvolt = <1200000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&spi1 {
|
||||
pinctrl-names = "default";
|
||||
/* pin shared with snfi */
|
||||
@@ -137,6 +189,13 @@
|
||||
};
|
||||
};
|
||||
|
||||
i2c0_pins: i2c0-pins-g0 {
|
||||
mux {
|
||||
function = "i2c";
|
||||
groups = "i2c0_1";
|
||||
};
|
||||
};
|
||||
|
||||
pcie0_pins: pcie0-pins {
|
||||
mux {
|
||||
function = "pcie";
|
||||
|
||||
@@ -54,6 +54,58 @@
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&i2c0 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&i2c0_pins>;
|
||||
status = "okay";
|
||||
|
||||
rt5190a_64: rt5190a@64 {
|
||||
compatible = "richtek,rt5190a";
|
||||
reg = <0x64>;
|
||||
/*interrupts-extended = <&gpio26 0 IRQ_TYPE_LEVEL_LOW>;*/
|
||||
vin2-supply = <&rt5190_buck1>;
|
||||
vin3-supply = <&rt5190_buck1>;
|
||||
vin4-supply = <&rt5190_buck1>;
|
||||
|
||||
regulators {
|
||||
rt5190_buck1: buck1 {
|
||||
regulator-name = "rt5190a-buck1";
|
||||
regulator-min-microvolt = <5090000>;
|
||||
regulator-max-microvolt = <5090000>;
|
||||
regulator-allowed-modes =
|
||||
<RT5190A_OPMODE_AUTO RT5190A_OPMODE_FPWM>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck2 {
|
||||
regulator-name = "vcore";
|
||||
regulator-min-microvolt = <600000>;
|
||||
regulator-max-microvolt = <1400000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck3 {
|
||||
regulator-name = "proc";
|
||||
regulator-min-microvolt = <600000>;
|
||||
regulator-max-microvolt = <1400000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck4 {
|
||||
regulator-name = "rt5190a-buck4";
|
||||
regulator-min-microvolt = <850000>;
|
||||
regulator-max-microvolt = <850000>;
|
||||
regulator-allowed-modes =
|
||||
<RT5190A_OPMODE_AUTO RT5190A_OPMODE_FPWM>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
ldo {
|
||||
regulator-name = "rt5190a-ldo";
|
||||
regulator-min-microvolt = <1200000>;
|
||||
regulator-max-microvolt = <1200000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&spi1 {
|
||||
pinctrl-names = "default";
|
||||
/* pin shared with snfi */
|
||||
@@ -128,6 +180,13 @@
|
||||
};
|
||||
};
|
||||
|
||||
i2c0_pins: i2c0-pins-g0 {
|
||||
mux {
|
||||
function = "i2c";
|
||||
groups = "i2c0_1";
|
||||
};
|
||||
};
|
||||
|
||||
pcie0_pins: pcie0-pins {
|
||||
mux {
|
||||
function = "pcie";
|
||||
|
||||
@@ -88,6 +88,58 @@
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&i2c0 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&i2c0_pins>;
|
||||
status = "okay";
|
||||
|
||||
rt5190a_64: rt5190a@64 {
|
||||
compatible = "richtek,rt5190a";
|
||||
reg = <0x64>;
|
||||
/*interrupts-extended = <&gpio26 0 IRQ_TYPE_LEVEL_LOW>;*/
|
||||
vin2-supply = <&rt5190_buck1>;
|
||||
vin3-supply = <&rt5190_buck1>;
|
||||
vin4-supply = <&rt5190_buck1>;
|
||||
|
||||
regulators {
|
||||
rt5190_buck1: buck1 {
|
||||
regulator-name = "rt5190a-buck1";
|
||||
regulator-min-microvolt = <5090000>;
|
||||
regulator-max-microvolt = <5090000>;
|
||||
regulator-allowed-modes =
|
||||
<RT5190A_OPMODE_AUTO RT5190A_OPMODE_FPWM>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck2 {
|
||||
regulator-name = "vcore";
|
||||
regulator-min-microvolt = <600000>;
|
||||
regulator-max-microvolt = <1400000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck3 {
|
||||
regulator-name = "proc";
|
||||
regulator-min-microvolt = <600000>;
|
||||
regulator-max-microvolt = <1400000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck4 {
|
||||
regulator-name = "rt5190a-buck4";
|
||||
regulator-min-microvolt = <850000>;
|
||||
regulator-max-microvolt = <850000>;
|
||||
regulator-allowed-modes =
|
||||
<RT5190A_OPMODE_AUTO RT5190A_OPMODE_FPWM>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
ldo {
|
||||
regulator-name = "rt5190a-ldo";
|
||||
regulator-min-microvolt = <1200000>;
|
||||
regulator-max-microvolt = <1200000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&spi1 {
|
||||
pinctrl-names = "default";
|
||||
/* pin shared with snfi */
|
||||
@@ -162,6 +214,13 @@
|
||||
};
|
||||
};
|
||||
|
||||
i2c0_pins: i2c0-pins-g0 {
|
||||
mux {
|
||||
function = "i2c";
|
||||
groups = "i2c0_1";
|
||||
};
|
||||
};
|
||||
|
||||
pcie0_pins: pcie0-pins {
|
||||
mux {
|
||||
function = "pcie";
|
||||
|
||||
@@ -45,6 +45,58 @@
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&i2c0 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&i2c0_pins>;
|
||||
status = "okay";
|
||||
|
||||
rt5190a_64: rt5190a@64 {
|
||||
compatible = "richtek,rt5190a";
|
||||
reg = <0x64>;
|
||||
/*interrupts-extended = <&gpio26 0 IRQ_TYPE_LEVEL_LOW>;*/
|
||||
vin2-supply = <&rt5190_buck1>;
|
||||
vin3-supply = <&rt5190_buck1>;
|
||||
vin4-supply = <&rt5190_buck1>;
|
||||
|
||||
regulators {
|
||||
rt5190_buck1: buck1 {
|
||||
regulator-name = "rt5190a-buck1";
|
||||
regulator-min-microvolt = <5090000>;
|
||||
regulator-max-microvolt = <5090000>;
|
||||
regulator-allowed-modes =
|
||||
<RT5190A_OPMODE_AUTO RT5190A_OPMODE_FPWM>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck2 {
|
||||
regulator-name = "vcore";
|
||||
regulator-min-microvolt = <600000>;
|
||||
regulator-max-microvolt = <1400000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck3 {
|
||||
regulator-name = "proc";
|
||||
regulator-min-microvolt = <600000>;
|
||||
regulator-max-microvolt = <1400000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck4 {
|
||||
regulator-name = "rt5190a-buck4";
|
||||
regulator-min-microvolt = <850000>;
|
||||
regulator-max-microvolt = <850000>;
|
||||
regulator-allowed-modes =
|
||||
<RT5190A_OPMODE_AUTO RT5190A_OPMODE_FPWM>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
ldo {
|
||||
regulator-name = "rt5190a-ldo";
|
||||
regulator-min-microvolt = <1200000>;
|
||||
regulator-max-microvolt = <1200000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&spi1 {
|
||||
pinctrl-names = "default";
|
||||
/* pin shared with snfi */
|
||||
@@ -162,6 +214,13 @@
|
||||
};
|
||||
};
|
||||
|
||||
i2c0_pins: i2c0-pins-g0 {
|
||||
mux {
|
||||
function = "i2c";
|
||||
groups = "i2c0_1";
|
||||
};
|
||||
};
|
||||
|
||||
pcie0_pins: pcie0-pins {
|
||||
mux {
|
||||
function = "pcie";
|
||||
|
||||
@@ -67,6 +67,58 @@
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&i2c0 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&i2c0_pins>;
|
||||
status = "okay";
|
||||
|
||||
rt5190a_64: rt5190a@64 {
|
||||
compatible = "richtek,rt5190a";
|
||||
reg = <0x64>;
|
||||
/*interrupts-extended = <&gpio26 0 IRQ_TYPE_LEVEL_LOW>;*/
|
||||
vin2-supply = <&rt5190_buck1>;
|
||||
vin3-supply = <&rt5190_buck1>;
|
||||
vin4-supply = <&rt5190_buck1>;
|
||||
|
||||
regulators {
|
||||
rt5190_buck1: buck1 {
|
||||
regulator-name = "rt5190a-buck1";
|
||||
regulator-min-microvolt = <5090000>;
|
||||
regulator-max-microvolt = <5090000>;
|
||||
regulator-allowed-modes =
|
||||
<RT5190A_OPMODE_AUTO RT5190A_OPMODE_FPWM>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck2 {
|
||||
regulator-name = "vcore";
|
||||
regulator-min-microvolt = <600000>;
|
||||
regulator-max-microvolt = <1400000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck3 {
|
||||
regulator-name = "proc";
|
||||
regulator-min-microvolt = <600000>;
|
||||
regulator-max-microvolt = <1400000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck4 {
|
||||
regulator-name = "rt5190a-buck4";
|
||||
regulator-min-microvolt = <850000>;
|
||||
regulator-max-microvolt = <850000>;
|
||||
regulator-allowed-modes =
|
||||
<RT5190A_OPMODE_AUTO RT5190A_OPMODE_FPWM>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
ldo {
|
||||
regulator-name = "rt5190a-ldo";
|
||||
regulator-min-microvolt = <1200000>;
|
||||
regulator-max-microvolt = <1200000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&spi1 {
|
||||
pinctrl-names = "default";
|
||||
/* pin shared with snfi */
|
||||
@@ -148,6 +200,13 @@
|
||||
};
|
||||
};
|
||||
|
||||
i2c0_pins: i2c0-pins-g0 {
|
||||
mux {
|
||||
function = "i2c";
|
||||
groups = "i2c0_1";
|
||||
};
|
||||
};
|
||||
|
||||
pcie0_pins: pcie0-pins {
|
||||
mux {
|
||||
function = "pcie";
|
||||
|
||||
@@ -58,6 +58,58 @@
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&i2c0 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&i2c0_pins>;
|
||||
status = "okay";
|
||||
|
||||
rt5190a_64: rt5190a@64 {
|
||||
compatible = "richtek,rt5190a";
|
||||
reg = <0x64>;
|
||||
/*interrupts-extended = <&gpio26 0 IRQ_TYPE_LEVEL_LOW>;*/
|
||||
vin2-supply = <&rt5190_buck1>;
|
||||
vin3-supply = <&rt5190_buck1>;
|
||||
vin4-supply = <&rt5190_buck1>;
|
||||
|
||||
regulators {
|
||||
rt5190_buck1: buck1 {
|
||||
regulator-name = "rt5190a-buck1";
|
||||
regulator-min-microvolt = <5090000>;
|
||||
regulator-max-microvolt = <5090000>;
|
||||
regulator-allowed-modes =
|
||||
<RT5190A_OPMODE_AUTO RT5190A_OPMODE_FPWM>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck2 {
|
||||
regulator-name = "vcore";
|
||||
regulator-min-microvolt = <600000>;
|
||||
regulator-max-microvolt = <1400000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck3 {
|
||||
regulator-name = "proc";
|
||||
regulator-min-microvolt = <600000>;
|
||||
regulator-max-microvolt = <1400000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck4 {
|
||||
regulator-name = "rt5190a-buck4";
|
||||
regulator-min-microvolt = <850000>;
|
||||
regulator-max-microvolt = <850000>;
|
||||
regulator-allowed-modes =
|
||||
<RT5190A_OPMODE_AUTO RT5190A_OPMODE_FPWM>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
ldo {
|
||||
regulator-name = "rt5190a-ldo";
|
||||
regulator-min-microvolt = <1200000>;
|
||||
regulator-max-microvolt = <1200000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&spi1 {
|
||||
pinctrl-names = "default";
|
||||
/* pin shared with snfi */
|
||||
@@ -139,6 +191,13 @@
|
||||
};
|
||||
};
|
||||
|
||||
i2c0_pins: i2c0-pins-g0 {
|
||||
mux {
|
||||
function = "i2c";
|
||||
groups = "i2c0_1";
|
||||
};
|
||||
};
|
||||
|
||||
pcie0_pins: pcie0-pins {
|
||||
mux {
|
||||
function = "pcie";
|
||||
|
||||
@@ -92,6 +92,58 @@
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&i2c0 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&i2c0_pins>;
|
||||
status = "okay";
|
||||
|
||||
rt5190a_64: rt5190a@64 {
|
||||
compatible = "richtek,rt5190a";
|
||||
reg = <0x64>;
|
||||
/*interrupts-extended = <&gpio26 0 IRQ_TYPE_LEVEL_LOW>;*/
|
||||
vin2-supply = <&rt5190_buck1>;
|
||||
vin3-supply = <&rt5190_buck1>;
|
||||
vin4-supply = <&rt5190_buck1>;
|
||||
|
||||
regulators {
|
||||
rt5190_buck1: buck1 {
|
||||
regulator-name = "rt5190a-buck1";
|
||||
regulator-min-microvolt = <5090000>;
|
||||
regulator-max-microvolt = <5090000>;
|
||||
regulator-allowed-modes =
|
||||
<RT5190A_OPMODE_AUTO RT5190A_OPMODE_FPWM>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck2 {
|
||||
regulator-name = "vcore";
|
||||
regulator-min-microvolt = <600000>;
|
||||
regulator-max-microvolt = <1400000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck3 {
|
||||
regulator-name = "proc";
|
||||
regulator-min-microvolt = <600000>;
|
||||
regulator-max-microvolt = <1400000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck4 {
|
||||
regulator-name = "rt5190a-buck4";
|
||||
regulator-min-microvolt = <850000>;
|
||||
regulator-max-microvolt = <850000>;
|
||||
regulator-allowed-modes =
|
||||
<RT5190A_OPMODE_AUTO RT5190A_OPMODE_FPWM>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
ldo {
|
||||
regulator-name = "rt5190a-ldo";
|
||||
regulator-min-microvolt = <1200000>;
|
||||
regulator-max-microvolt = <1200000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&spi1 {
|
||||
pinctrl-names = "default";
|
||||
/* pin shared with snfi */
|
||||
@@ -173,6 +225,13 @@
|
||||
};
|
||||
};
|
||||
|
||||
i2c0_pins: i2c0-pins-g0 {
|
||||
mux {
|
||||
function = "i2c";
|
||||
groups = "i2c0_1";
|
||||
};
|
||||
};
|
||||
|
||||
pcie0_pins: pcie0-pins {
|
||||
mux {
|
||||
function = "pcie";
|
||||
|
||||
@@ -49,6 +49,58 @@
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&i2c0 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&i2c0_pins>;
|
||||
status = "okay";
|
||||
|
||||
rt5190a_64: rt5190a@64 {
|
||||
compatible = "richtek,rt5190a";
|
||||
reg = <0x64>;
|
||||
/*interrupts-extended = <&gpio26 0 IRQ_TYPE_LEVEL_LOW>;*/
|
||||
vin2-supply = <&rt5190_buck1>;
|
||||
vin3-supply = <&rt5190_buck1>;
|
||||
vin4-supply = <&rt5190_buck1>;
|
||||
|
||||
regulators {
|
||||
rt5190_buck1: buck1 {
|
||||
regulator-name = "rt5190a-buck1";
|
||||
regulator-min-microvolt = <5090000>;
|
||||
regulator-max-microvolt = <5090000>;
|
||||
regulator-allowed-modes =
|
||||
<RT5190A_OPMODE_AUTO RT5190A_OPMODE_FPWM>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck2 {
|
||||
regulator-name = "vcore";
|
||||
regulator-min-microvolt = <600000>;
|
||||
regulator-max-microvolt = <1400000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck3 {
|
||||
regulator-name = "proc";
|
||||
regulator-min-microvolt = <600000>;
|
||||
regulator-max-microvolt = <1400000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck4 {
|
||||
regulator-name = "rt5190a-buck4";
|
||||
regulator-min-microvolt = <850000>;
|
||||
regulator-max-microvolt = <850000>;
|
||||
regulator-allowed-modes =
|
||||
<RT5190A_OPMODE_AUTO RT5190A_OPMODE_FPWM>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
ldo {
|
||||
regulator-name = "rt5190a-ldo";
|
||||
regulator-min-microvolt = <1200000>;
|
||||
regulator-max-microvolt = <1200000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&spi1 {
|
||||
pinctrl-names = "default";
|
||||
/* pin shared with snfi */
|
||||
@@ -173,6 +225,13 @@
|
||||
};
|
||||
};
|
||||
|
||||
i2c0_pins: i2c0-pins-g0 {
|
||||
mux {
|
||||
function = "i2c";
|
||||
groups = "i2c0_1";
|
||||
};
|
||||
};
|
||||
|
||||
pcie0_pins: pcie0-pins {
|
||||
mux {
|
||||
function = "pcie";
|
||||
|
||||
@@ -67,6 +67,58 @@
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&i2c0 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&i2c0_pins>;
|
||||
status = "okay";
|
||||
|
||||
rt5190a_64: rt5190a@64 {
|
||||
compatible = "richtek,rt5190a";
|
||||
reg = <0x64>;
|
||||
/*interrupts-extended = <&gpio26 0 IRQ_TYPE_LEVEL_LOW>;*/
|
||||
vin2-supply = <&rt5190_buck1>;
|
||||
vin3-supply = <&rt5190_buck1>;
|
||||
vin4-supply = <&rt5190_buck1>;
|
||||
|
||||
regulators {
|
||||
rt5190_buck1: buck1 {
|
||||
regulator-name = "rt5190a-buck1";
|
||||
regulator-min-microvolt = <5090000>;
|
||||
regulator-max-microvolt = <5090000>;
|
||||
regulator-allowed-modes =
|
||||
<RT5190A_OPMODE_AUTO RT5190A_OPMODE_FPWM>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck2 {
|
||||
regulator-name = "vcore";
|
||||
regulator-min-microvolt = <600000>;
|
||||
regulator-max-microvolt = <1400000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck3 {
|
||||
regulator-name = "proc";
|
||||
regulator-min-microvolt = <600000>;
|
||||
regulator-max-microvolt = <1400000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck4 {
|
||||
regulator-name = "rt5190a-buck4";
|
||||
regulator-min-microvolt = <850000>;
|
||||
regulator-max-microvolt = <850000>;
|
||||
regulator-allowed-modes =
|
||||
<RT5190A_OPMODE_AUTO RT5190A_OPMODE_FPWM>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
ldo {
|
||||
regulator-name = "rt5190a-ldo";
|
||||
regulator-min-microvolt = <1200000>;
|
||||
regulator-max-microvolt = <1200000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&spi1 {
|
||||
pinctrl-names = "default";
|
||||
/* pin shared with snfi */
|
||||
@@ -148,6 +200,13 @@
|
||||
};
|
||||
};
|
||||
|
||||
i2c0_pins: i2c0-pins-g0 {
|
||||
mux {
|
||||
function = "i2c";
|
||||
groups = "i2c0_1";
|
||||
};
|
||||
};
|
||||
|
||||
pcie0_pins: pcie0-pins {
|
||||
mux {
|
||||
function = "pcie";
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
obj-$(CONFIG_MTK_ICE_DEBUG) += ice_debug/
|
||||
obj-y += infra_bus_prot/
|
||||
obj-y += reset-boot-count/
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
obj-y := reset-boot-count.o
|
||||
@@ -0,0 +1,143 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024 MediaTek Inc. All rights reserved.
|
||||
*
|
||||
* Helper for resetting boot count of A/B boot systems
|
||||
*
|
||||
* Author: Weijie Gao <weijie.gao@mediatek.com>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/arm-smccc.h>
|
||||
|
||||
#define RBC "reset_boot_count"
|
||||
|
||||
#define MTK_SIP_READ_NONRST_REG 0xC2000550
|
||||
#define MTK_SIP_WRITE_NONRST_REG 0xC2000551
|
||||
|
||||
static struct proc_dir_entry *rbc_entry;
|
||||
|
||||
static bool dual_boot_get_boot_count(u32 *retslot, u32 *retcnt)
|
||||
{
|
||||
struct arm_smccc_res res = {0};
|
||||
u32 val, slot;
|
||||
s8 neg, pos;
|
||||
|
||||
arm_smccc_smc(MTK_SIP_READ_NONRST_REG, 0, 0, 0, 0, 0, 0, 0, &res);
|
||||
|
||||
val = (u32)res.a0;
|
||||
|
||||
/* slot: val[31..24] = -slot, val[23..16] = slot */
|
||||
pos = (val >> 16) & 0xff;
|
||||
neg = (val >> 24) & 0xff;
|
||||
|
||||
if (!(pos >= 0 && neg <= 0 && pos + neg == 0)) {
|
||||
pr_debug("slot of boot count is invalid\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
slot = pos;
|
||||
|
||||
/* count: val[15..8] = -count, val[7..0] = count */
|
||||
pos = val & 0xff;
|
||||
neg = (val >> 8) & 0xff;
|
||||
|
||||
if (!(pos >= 0 && neg <= 0 && pos + neg == 0)) {
|
||||
pr_debug("count of boot count is invalid\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (retslot)
|
||||
*retslot = slot;
|
||||
|
||||
if (retcnt)
|
||||
*retcnt = pos;
|
||||
|
||||
return true;
|
||||
|
||||
err:
|
||||
if (retslot)
|
||||
*retslot = 0;
|
||||
|
||||
if (retcnt)
|
||||
*retcnt = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void dual_boot_set_boot_count(u32 slot, u32 count)
|
||||
{
|
||||
struct arm_smccc_res res = {0};
|
||||
u32 val;
|
||||
s32 neg;
|
||||
|
||||
if (slot > 127 || count > 127)
|
||||
return;
|
||||
|
||||
neg = -count;
|
||||
val = count | ((neg << 8) & 0xff00);
|
||||
|
||||
neg = -slot;
|
||||
val = val | ((uint32_t)slot << 16) | ((neg << 24) & 0xff000000);
|
||||
|
||||
arm_smccc_smc(MTK_SIP_WRITE_NONRST_REG, 0, val, 0, 0, 0, 0, 0, &res);
|
||||
}
|
||||
|
||||
static int rbc_display(struct seq_file *seq, void *v)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rbc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, rbc_display, inode->i_private);
|
||||
}
|
||||
|
||||
static ssize_t rbc_write(struct file *file, const char __user *buffer,
|
||||
size_t count, loff_t *pos)
|
||||
{
|
||||
u32 slot;
|
||||
|
||||
dual_boot_get_boot_count(&slot, NULL);
|
||||
dual_boot_set_boot_count(slot, 0);
|
||||
|
||||
pr_info("Boot count reset\n");
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations rbc_fops =
|
||||
{
|
||||
.open = rbc_open,
|
||||
.read = seq_read,
|
||||
.write = rbc_write,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int __init rbc_init(void)
|
||||
{
|
||||
rbc_entry = proc_create(RBC, 0200, NULL, &rbc_fops);
|
||||
|
||||
if (!rbc_entry)
|
||||
pr_err("failed to create proc entry " RBC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit rbc_exit(void)
|
||||
{
|
||||
remove_proc_entry(RBC, NULL);
|
||||
}
|
||||
|
||||
module_init(rbc_init);
|
||||
module_exit(rbc_exit);
|
||||
|
||||
MODULE_AUTHOR("Weijie Gao <weijie.gao@mediatek.com>");
|
||||
MODULE_DESCRIPTION("Kernel module for resetting boot count of A/B boot systems");
|
||||
MODULE_LICENSE("BSD");
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
config NET_DSA_AN8855
|
||||
tristate "Airoha AN8855 Ethernet switch support"
|
||||
depends on NET_DSA
|
||||
select NET_DSA_TAG_AIROHA
|
||||
help
|
||||
AN8855 support 2.5G speed and managed by SMI interface.
|
||||
This enables support for the Airoha AN8855 Ethernet switch
|
||||
chip.
|
||||
To compile this driver as a module, choose M here.
|
||||
@@ -0,0 +1,5 @@
|
||||
#
|
||||
# Makefile for Airoha AN8855 gigabit switch
|
||||
#
|
||||
obj-$(CONFIG_NET_DSA_AN8855) += an8855-dsa.o
|
||||
an8855-dsa-objs := an8855.o an8855_nl.o an8855_phy.o
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,580 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (C) 2023 Min Yao <min.yao@airoha.com>
|
||||
*/
|
||||
|
||||
#ifndef __AN8855_H
|
||||
#define __AN8855_H
|
||||
|
||||
#define BITS(m, n) (~(BIT(m) - 1) & ((BIT(n) - 1) | BIT(n)))
|
||||
|
||||
#define AN8855_NUM_PORTS 6
|
||||
#define AN8855_CPU_PORT 5
|
||||
#define AN8855_NUM_FDB_RECORDS 2048
|
||||
#define AN8855_ALL_MEMBERS 0x3f
|
||||
#define AN8855_RESERVED_VLAN 2
|
||||
#define AN8855_GPHY_SMI_ADDR_DEFAULT 1
|
||||
|
||||
enum an8855_id {
|
||||
ID_AN8855 = 0,
|
||||
};
|
||||
|
||||
enum sgmii_mode {
|
||||
SGMII_MODE_AN,
|
||||
SGMII_MODE_FORCE,
|
||||
};
|
||||
|
||||
/* Registers to mac forward control for unknown frames */
|
||||
#define AN8855_MFC 0x10200010
|
||||
#define CPU_EN BIT(15)
|
||||
#define CPU_PORT(x) ((x) << 8)
|
||||
#define CPU_MASK (0x9f << 8)
|
||||
|
||||
#define AN8855_UNUF 0x102000b4
|
||||
#define AN8855_UNMF 0x102000b8
|
||||
#define AN8855_BCF 0x102000bc
|
||||
|
||||
/* Registers for mirror port control */
|
||||
#define AN8855_MIR 0x102000cc
|
||||
#define AN8855_MIRROR_EN BIT(7)
|
||||
#define AN8855_MIRROR_MASK (0x1f)
|
||||
#define AN8855_MIRROR_PORT_GET(x) ((x) & AN8855_MIRROR_MASK)
|
||||
#define AN8855_MIRROR_PORT_SET(x) ((x) & AN8855_MIRROR_MASK)
|
||||
|
||||
/* Registers for BPDU and PAE frame control*/
|
||||
#define AN8855_BPC 0x102000D0
|
||||
#define AN8855_BPDU_PORT_FW_MASK GENMASK(2, 0)
|
||||
|
||||
enum an8855_bpdu_port_fw {
|
||||
AN8855_BPDU_FOLLOW_MFC,
|
||||
AN8855_BPDU_CPU_EXCLUDE = 4,
|
||||
AN8855_BPDU_CPU_INCLUDE = 5,
|
||||
AN8855_BPDU_CPU_ONLY = 6,
|
||||
AN8855_BPDU_DROP = 7,
|
||||
};
|
||||
|
||||
/* Registers for address table access */
|
||||
#define AN8855_ATA1 0x10200304
|
||||
#define AN8855_ATA2 0x10200308
|
||||
|
||||
/* Register for address table write data */
|
||||
#define AN8855_ATWD 0x10200324
|
||||
#define AN8855_ATWD2 0x10200328
|
||||
|
||||
/* Register for address table control */
|
||||
#define AN8855_ATC 0x10200300
|
||||
#define ATC_BUSY BIT(31)
|
||||
#define ATC_INVALID ~BIT(30)
|
||||
#define ATC_HASH 16
|
||||
#define ATC_HASH_MASK 0x1ff
|
||||
#define ATC_HIT 12
|
||||
#define ATC_HIT_MASK 0xf
|
||||
#define ATC_MAT(x) (((x) & 0x1f) << 7)
|
||||
#define ATC_MAT_MACTAB ATC_MAT(1)
|
||||
|
||||
enum an8855_fdb_cmds {
|
||||
AN8855_FDB_READ = 0,
|
||||
AN8855_FDB_WRITE = 1,
|
||||
AN8855_FDB_FLUSH = 2,
|
||||
AN8855_FDB_START = 4,
|
||||
AN8855_FDB_NEXT = 5,
|
||||
};
|
||||
|
||||
/* Registers for table search read address */
|
||||
#define AN8855_ATRDS 0x10200330
|
||||
#define AN8855_ATRD0 0x10200334
|
||||
#define CVID 10
|
||||
#define CVID_MASK 0xfff
|
||||
|
||||
enum an8855_fdb_type {
|
||||
AN8855_MAC_TB_TY_MAC = 0,
|
||||
AN8855_MAC_TB_TY_DIP = 1,
|
||||
AN8855_MAC_TB_TY_DIP_SIP = 2,
|
||||
};
|
||||
|
||||
#define AN8855_ATRD1 0x10200338
|
||||
#define MAC_BYTE_4 24
|
||||
#define MAC_BYTE_5 16
|
||||
#define AGE_TIMER 3
|
||||
#define AGE_TIMER_MASK 0x1ff
|
||||
|
||||
#define AN8855_ATRD2 0x1020033c
|
||||
#define MAC_BYTE_0 24
|
||||
#define MAC_BYTE_1 16
|
||||
#define MAC_BYTE_2 8
|
||||
#define MAC_BYTE_3 0
|
||||
#define MAC_BYTE_MASK 0xff
|
||||
|
||||
#define AN8855_ATRD3 0x10200340
|
||||
#define PORT_MAP 4
|
||||
#define PORT_MAP_MASK 0xff
|
||||
|
||||
/* Register for vlan table control */
|
||||
#define AN8855_VTCR 0x10200600
|
||||
#define VTCR_BUSY BIT(31)
|
||||
#define VTCR_FUNC(x) (((x) & 0xf) << 12)
|
||||
#define VTCR_VID ((x) & 0xfff)
|
||||
|
||||
enum an8855_vlan_cmd {
|
||||
/* Read/Write the specified VID entry from VAWD register based
|
||||
* on VID.
|
||||
*/
|
||||
AN8855_VTCR_RD_VID = 0,
|
||||
AN8855_VTCR_WR_VID = 1,
|
||||
};
|
||||
|
||||
/* Register for setup vlan write data */
|
||||
#define AN8855_VAWD0 0x10200604
|
||||
|
||||
/* Independent VLAN Learning */
|
||||
#define IVL_MAC BIT(5)
|
||||
/* Per VLAN Egress Tag Control */
|
||||
#define VTAG_EN BIT(10)
|
||||
/* Egress Tag Control */
|
||||
#define PORT_EG_CTRL_SHIFT 12
|
||||
/* VLAN Member Control */
|
||||
#define PORT_MEM_SHFT 26
|
||||
#define PORT_MEM_MASK 0x7f
|
||||
#define PORT_MEM(x) (((x) & PORT_MEM_MASK) << PORT_MEM_SHFT)
|
||||
/* VLAN Entry Valid */
|
||||
#define VLAN_VALID BIT(0)
|
||||
|
||||
#define AN8855_VAWD1 0x10200608
|
||||
#define PORT_STAG BIT(1)
|
||||
/* Egress Tag Control */
|
||||
#define ETAG_CTRL_P(p, x) (((x) & 0x3) << ((p) << 1))
|
||||
#define ETAG_CTRL_P_MASK(p) ETAG_CTRL_P(p, 3)
|
||||
#define ETAG_CTRL_MASK (0x3FFF)
|
||||
|
||||
#define AN8855_VARD0 0x10200618
|
||||
|
||||
enum an8855_vlan_egress_attr {
|
||||
AN8855_VLAN_EGRESS_UNTAG = 0,
|
||||
AN8855_VLAN_EGRESS_TAG = 2,
|
||||
AN8855_VLAN_EGRESS_STACK = 3,
|
||||
};
|
||||
|
||||
/* Register for port STP state control */
|
||||
#define AN8855_SSP_P(x) (0x10208000 + ((x) * 0x200))
|
||||
#define FID_PST(x) ((x) & 0x3)
|
||||
#define FID_PST_MASK FID_PST(0x3)
|
||||
|
||||
enum an8855_stp_state {
|
||||
AN8855_STP_DISABLED = 0,
|
||||
AN8855_STP_BLOCKING = 1,
|
||||
AN8855_STP_LISTENING = 1,
|
||||
AN8855_STP_LEARNING = 2,
|
||||
AN8855_STP_FORWARDING = 3
|
||||
};
|
||||
|
||||
/* Register for port control */
|
||||
#define AN8855_PCR_P(x) (0x10208004 + ((x) * 0x200))
|
||||
#define PORT_TX_MIR BIT(20)
|
||||
#define PORT_RX_MIR BIT(16)
|
||||
#define PORT_VLAN(x) ((x) & 0x3)
|
||||
|
||||
enum an8855_port_mode {
|
||||
/* Port Matrix Mode: Frames are forwarded by the PCR_MATRIX members. */
|
||||
AN8855_PORT_MATRIX_MODE = PORT_VLAN(0),
|
||||
|
||||
/* Fallback Mode: Forward received frames with ingress ports that do
|
||||
* not belong to the VLAN member. Frames whose VID is not listed on
|
||||
* the VLAN table are forwarded by the PCR_MATRIX members.
|
||||
*/
|
||||
AN8855_PORT_FALLBACK_MODE = PORT_VLAN(1),
|
||||
|
||||
/* Security Mode: Discard any frame due to ingress membership
|
||||
* violation or VID missed on the VLAN table.
|
||||
*/
|
||||
AN8855_PORT_SECURITY_MODE = PORT_VLAN(3),
|
||||
};
|
||||
|
||||
#define PORT_PRI(x) (((x) & 0x7) << 24)
|
||||
#define EG_TAG(x) (((x) & 0x3) << 28)
|
||||
#define PCR_PORT_VLAN_MASK PORT_VLAN(3)
|
||||
|
||||
/* Register for port security control */
|
||||
#define AN8855_PSC_P(x) (0x1020800c + ((x) * 0x200))
|
||||
#define SA_DIS BIT(4)
|
||||
|
||||
/* Register for port vlan control */
|
||||
#define AN8855_PVC_P(x) (0x10208010 + ((x) * 0x200))
|
||||
#define PORT_SPEC_REPLACE_MODE BIT(11)
|
||||
#define PORT_SPEC_TAG BIT(5)
|
||||
#define PVC_EG_TAG(x) (((x) & 0x7) << 8)
|
||||
#define PVC_EG_TAG_MASK PVC_EG_TAG(7)
|
||||
#define VLAN_ATTR(x) (((x) & 0x3) << 6)
|
||||
#define VLAN_ATTR_MASK VLAN_ATTR(3)
|
||||
|
||||
#define AN8855_PORTMATRIX_P(x) (0x10208044 + ((x) * 0x200))
|
||||
#define PORTMATRIX_MATRIX(x) ((x) & 0x3f)
|
||||
#define PORTMATRIX_MASK PORTMATRIX_MATRIX(0x3f)
|
||||
#define PORTMATRIX_CLR PORTMATRIX_MATRIX(0)
|
||||
|
||||
enum an8855_vlan_port_eg_tag {
|
||||
AN8855_VLAN_EG_DISABLED = 0,
|
||||
AN8855_VLAN_EG_CONSISTENT = 1,
|
||||
};
|
||||
|
||||
enum an8855_vlan_port_attr {
|
||||
AN8855_VLAN_USER = 0,
|
||||
AN8855_VLAN_TRANSPARENT = 3,
|
||||
};
|
||||
|
||||
/* Register for port PVID */
|
||||
#define AN8855_PVID_P(x) (0x10208048 + ((x) * 0x200))
|
||||
#define G0_PORT_VID(x) (((x) & 0xfff) << 0)
|
||||
#define G0_PORT_VID_MASK G0_PORT_VID(0xfff)
|
||||
#define G0_PORT_VID_DEF G0_PORT_VID(1)
|
||||
|
||||
/* Register for port MAC control register */
|
||||
#define AN8855_PMCR_P(x) (0x10210000 + ((x) * 0x200))
|
||||
#define PMCR_IFG_XMIT(x) (((x) & 0x3) << 20)
|
||||
#define PMCR_EXT_PHY BIT(19)
|
||||
#define PMCR_MAC_MODE BIT(18)
|
||||
#define PMCR_FORCE_MODE BIT(31)
|
||||
#define PMCR_TX_EN BIT(16)
|
||||
#define PMCR_RX_EN BIT(15)
|
||||
#define PMCR_BACKOFF_EN BIT(12)
|
||||
#define PMCR_BACKPR_EN BIT(11)
|
||||
#define PMCR_FORCE_EEE2P5G BIT(8)
|
||||
#define PMCR_FORCE_EEE1G BIT(7)
|
||||
#define PMCR_FORCE_EEE100 BIT(6)
|
||||
#define PMCR_TX_FC_EN BIT(5)
|
||||
#define PMCR_RX_FC_EN BIT(4)
|
||||
#define PMCR_FORCE_SPEED_2500 (0x3 << 28)
|
||||
#define PMCR_FORCE_SPEED_1000 (0x2 << 28)
|
||||
#define PMCR_FORCE_SPEED_100 (0x1 << 28)
|
||||
#define PMCR_FORCE_FDX BIT(25)
|
||||
#define PMCR_FORCE_LNK BIT(24)
|
||||
#define PMCR_SPEED_MASK BITS(28, 30)
|
||||
#define AN8855_FORCE_LNK BIT(31)
|
||||
#define AN8855_FORCE_MODE (AN8855_FORCE_LNK)
|
||||
#define PMCR_LINK_SETTINGS_MASK (PMCR_TX_EN | \
|
||||
PMCR_RX_EN | PMCR_FORCE_SPEED_2500 | \
|
||||
PMCR_TX_FC_EN | PMCR_RX_FC_EN | \
|
||||
PMCR_FORCE_FDX | PMCR_FORCE_LNK)
|
||||
#define PMCR_CPU_PORT_SETTING(id) (AN8855_FORCE_MODE | \
|
||||
PMCR_IFG_XMIT(1) | PMCR_MAC_MODE | \
|
||||
PMCR_BACKOFF_EN | PMCR_BACKPR_EN | \
|
||||
PMCR_TX_EN | PMCR_RX_EN | \
|
||||
PMCR_TX_FC_EN | PMCR_RX_FC_EN | \
|
||||
PMCR_FORCE_SPEED_2500 | \
|
||||
PMCR_FORCE_FDX | PMCR_FORCE_LNK)
|
||||
|
||||
#define AN8855_PMSR_P(x) (0x10210010 + (x) * 0x200)
|
||||
#define PMSR_EEE1G BIT(7)
|
||||
#define PMSR_EEE100M BIT(6)
|
||||
#define PMSR_RX_FC BIT(5)
|
||||
#define PMSR_TX_FC BIT(4)
|
||||
#define PMSR_SPEED_2500 (0x3 << 28)
|
||||
#define PMSR_SPEED_1000 (0x2 << 28)
|
||||
#define PMSR_SPEED_100 (0x1 << 28)
|
||||
#define PMSR_SPEED_10 (0x0 << 28)
|
||||
#define PMSR_SPEED_MASK BITS(28, 30)
|
||||
#define PMSR_DPX BIT(25)
|
||||
#define PMSR_LINK BIT(24)
|
||||
|
||||
#define AN8855_PMEEECR_P(x) (0x10210004 + (x) * 0x200)
|
||||
#define WAKEUP_TIME_2500(x) ((x & 0xFF) << 16)
|
||||
#define WAKEUP_TIME_1000(x) ((x & 0xFF) << 8)
|
||||
#define WAKEUP_TIME_100(x) ((x & 0xFF) << 0)
|
||||
#define LPI_MODE_EN BIT(31)
|
||||
#define AN8855_PMEEECR2_P(x) (0x10210008 + (x) * 0x200)
|
||||
#define WAKEUP_TIME_5000(x) ((x & 0xFF) << 0)
|
||||
|
||||
#define AN8855_CKGCR (0x10213e1c)
|
||||
#define LPI_TXIDLE_THD 14
|
||||
#define LPI_TXIDLE_THD_MASK BITS(14, 31)
|
||||
|
||||
/* Register for MIB */
|
||||
#define AN8855_PORT_MIB_COUNTER(x) (0x10214000 + (x) * 0x200)
|
||||
#define AN8855_MIB_CCR 0x10213e30
|
||||
#define CCR_MIB_ENABLE BIT(31)
|
||||
#define CCR_RX_OCT_CNT_GOOD BIT(7)
|
||||
#define CCR_RX_OCT_CNT_BAD BIT(6)
|
||||
#define CCR_TX_OCT_CNT_GOOD BIT(5)
|
||||
#define CCR_TX_OCT_CNT_BAD BIT(4)
|
||||
#define CCR_RX_OCT_CNT_GOOD_2 BIT(3)
|
||||
#define CCR_RX_OCT_CNT_BAD_2 BIT(2)
|
||||
#define CCR_TX_OCT_CNT_GOOD_2 BIT(1)
|
||||
#define CCR_TX_OCT_CNT_BAD_2 BIT(0)
|
||||
#define CCR_MIB_FLUSH (CCR_RX_OCT_CNT_GOOD | \
|
||||
CCR_RX_OCT_CNT_BAD | \
|
||||
CCR_TX_OCT_CNT_GOOD | \
|
||||
CCR_TX_OCT_CNT_BAD | \
|
||||
CCR_RX_OCT_CNT_GOOD_2 | \
|
||||
CCR_RX_OCT_CNT_BAD_2 | \
|
||||
CCR_TX_OCT_CNT_GOOD_2 | \
|
||||
CCR_TX_OCT_CNT_BAD_2)
|
||||
#define CCR_MIB_ACTIVATE (CCR_MIB_ENABLE | \
|
||||
CCR_RX_OCT_CNT_GOOD | \
|
||||
CCR_RX_OCT_CNT_BAD | \
|
||||
CCR_TX_OCT_CNT_GOOD | \
|
||||
CCR_TX_OCT_CNT_BAD | \
|
||||
CCR_RX_OCT_CNT_GOOD_2 | \
|
||||
CCR_RX_OCT_CNT_BAD_2 | \
|
||||
CCR_TX_OCT_CNT_GOOD_2 | \
|
||||
CCR_TX_OCT_CNT_BAD_2)
|
||||
|
||||
/* AN8855 SGMII register group */
|
||||
#define AN8855_SGMII_REG_BASE 0x10220000
|
||||
#define AN8855_SGMII_REG(p, r) (AN8855_SGMII_REG_BASE + \
|
||||
((p) - 5) * 0x1000 + (r))
|
||||
|
||||
/* Register forSGMII PCS_CONTROL_1 */
|
||||
#define AN8855_PCS_CONTROL_1(p) AN8855_SGMII_REG(p, 0x00)
|
||||
#define AN8855_SGMII_AN_ENABLE BIT(12)
|
||||
#define AN8855_SGMII_AN_RESTART BIT(9)
|
||||
|
||||
/* Register for system reset */
|
||||
#define AN8855_RST_CTRL 0x100050c0
|
||||
#define SYS_CTRL_SYS_RST BIT(31)
|
||||
|
||||
/* Register for hw trap status */
|
||||
#define AN8855_HWTRAP 0x1000009c
|
||||
|
||||
#define AN8855_CREV 0x10005000
|
||||
#define AN8855_ID 0x8855
|
||||
|
||||
#define SCU_BASE 0x10000000
|
||||
#define RG_RGMII_TXCK_C (SCU_BASE + 0x1d0)
|
||||
|
||||
#define HSGMII_AN_CSR_BASE 0x10220000
|
||||
#define SGMII_REG_AN0 (HSGMII_AN_CSR_BASE + 0x000)
|
||||
#define SGMII_REG_AN_13 (HSGMII_AN_CSR_BASE + 0x034)
|
||||
#define SGMII_REG_AN_FORCE_CL37 (HSGMII_AN_CSR_BASE + 0x060)
|
||||
|
||||
#define HSGMII_CSR_PCS_BASE 0x10220000
|
||||
#define RG_HSGMII_PCS_CTROL_1 (HSGMII_CSR_PCS_BASE + 0xa00)
|
||||
#define RG_AN_SGMII_MODE_FORCE (HSGMII_CSR_PCS_BASE + 0xa24)
|
||||
|
||||
#define MULTI_SGMII_CSR_BASE 0x10224000
|
||||
#define SGMII_STS_CTRL_0 (MULTI_SGMII_CSR_BASE + 0x018)
|
||||
#define MSG_RX_CTRL_0 (MULTI_SGMII_CSR_BASE + 0x100)
|
||||
#define MSG_RX_LIK_STS_0 (MULTI_SGMII_CSR_BASE + 0x514)
|
||||
#define MSG_RX_LIK_STS_2 (MULTI_SGMII_CSR_BASE + 0x51c)
|
||||
#define PHY_RX_FORCE_CTRL_0 (MULTI_SGMII_CSR_BASE + 0x520)
|
||||
|
||||
#define XFI_CSR_PCS_BASE 0x10225000
|
||||
#define RG_USXGMII_AN_CONTROL_0 (XFI_CSR_PCS_BASE + 0xbf8)
|
||||
|
||||
#define MULTI_PHY_RA_CSR_BASE 0x10226000
|
||||
#define RG_RATE_ADAPT_CTRL_0 (MULTI_PHY_RA_CSR_BASE + 0x000)
|
||||
#define RATE_ADP_P0_CTRL_0 (MULTI_PHY_RA_CSR_BASE + 0x100)
|
||||
#define MII_RA_AN_ENABLE (MULTI_PHY_RA_CSR_BASE + 0x300)
|
||||
|
||||
#define QP_DIG_CSR_BASE 0x1022a000
|
||||
#define QP_CK_RST_CTRL_4 (QP_DIG_CSR_BASE + 0x310)
|
||||
#define QP_DIG_MODE_CTRL_0 (QP_DIG_CSR_BASE + 0x324)
|
||||
#define QP_DIG_MODE_CTRL_1 (QP_DIG_CSR_BASE + 0x330)
|
||||
|
||||
#define SERDES_WRAPPER_BASE 0x1022c000
|
||||
#define USGMII_CTRL_0 (SERDES_WRAPPER_BASE + 0x000)
|
||||
|
||||
#define QP_PMA_TOP_BASE 0x1022e000
|
||||
#define PON_RXFEDIG_CTRL_0 (QP_PMA_TOP_BASE + 0x100)
|
||||
#define PON_RXFEDIG_CTRL_9 (QP_PMA_TOP_BASE + 0x124)
|
||||
|
||||
#define SS_LCPLL_PWCTL_SETTING_2 (QP_PMA_TOP_BASE + 0x208)
|
||||
#define SS_LCPLL_TDC_FLT_2 (QP_PMA_TOP_BASE + 0x230)
|
||||
#define SS_LCPLL_TDC_FLT_5 (QP_PMA_TOP_BASE + 0x23c)
|
||||
#define SS_LCPLL_TDC_PCW_1 (QP_PMA_TOP_BASE + 0x248)
|
||||
#define INTF_CTRL_8 (QP_PMA_TOP_BASE + 0x320)
|
||||
#define INTF_CTRL_9 (QP_PMA_TOP_BASE + 0x324)
|
||||
#define PLL_CTRL_0 (QP_PMA_TOP_BASE + 0x400)
|
||||
#define PLL_CTRL_2 (QP_PMA_TOP_BASE + 0x408)
|
||||
#define PLL_CTRL_3 (QP_PMA_TOP_BASE + 0x40c)
|
||||
#define PLL_CTRL_4 (QP_PMA_TOP_BASE + 0x410)
|
||||
#define PLL_CK_CTRL_0 (QP_PMA_TOP_BASE + 0x414)
|
||||
#define RX_DLY_0 (QP_PMA_TOP_BASE + 0x614)
|
||||
#define RX_CTRL_2 (QP_PMA_TOP_BASE + 0x630)
|
||||
#define RX_CTRL_5 (QP_PMA_TOP_BASE + 0x63c)
|
||||
#define RX_CTRL_6 (QP_PMA_TOP_BASE + 0x640)
|
||||
#define RX_CTRL_7 (QP_PMA_TOP_BASE + 0x644)
|
||||
#define RX_CTRL_8 (QP_PMA_TOP_BASE + 0x648)
|
||||
#define RX_CTRL_26 (QP_PMA_TOP_BASE + 0x690)
|
||||
#define RX_CTRL_42 (QP_PMA_TOP_BASE + 0x6d0)
|
||||
|
||||
#define QP_ANA_CSR_BASE 0x1022f000
|
||||
#define RG_QP_RX_DAC_EN (QP_ANA_CSR_BASE + 0x00)
|
||||
#define RG_QP_RXAFE_RESERVE (QP_ANA_CSR_BASE + 0x04)
|
||||
#define RG_QP_CDR_LPF_MJV_LIM (QP_ANA_CSR_BASE + 0x0c)
|
||||
#define RG_QP_CDR_LPF_SETVALUE (QP_ANA_CSR_BASE + 0x14)
|
||||
#define RG_QP_CDR_PR_CKREF_DIV1 (QP_ANA_CSR_BASE + 0x18)
|
||||
#define RG_QP_CDR_PR_KBAND_DIV_PCIE (QP_ANA_CSR_BASE + 0x1c)
|
||||
#define RG_QP_CDR_FORCE_IBANDLPF_R_OFF (QP_ANA_CSR_BASE + 0x20)
|
||||
#define RG_QP_TX_MODE_16B_EN (QP_ANA_CSR_BASE + 0x28)
|
||||
#define RG_QP_PLL_IPLL_DIG_PWR_SEL (QP_ANA_CSR_BASE + 0x3c)
|
||||
#define RG_QP_PLL_SDM_ORD (QP_ANA_CSR_BASE + 0x40)
|
||||
|
||||
#define ETHER_SYS_BASE 0x1028c800
|
||||
#define RG_GPHY_AFE_PWD (ETHER_SYS_BASE + 0x40)
|
||||
#define RG_GPHY_SMI_ADDR (ETHER_SYS_BASE + 0x48)
|
||||
|
||||
#define MIB_DESC(_s, _o, _n) \
|
||||
{ \
|
||||
.size = (_s), \
|
||||
.offset = (_o), \
|
||||
.name = (_n), \
|
||||
}
|
||||
|
||||
struct an8855_mib_desc {
|
||||
unsigned int size;
|
||||
unsigned int offset;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct an8855_fdb {
|
||||
u16 vid;
|
||||
u8 port_mask;
|
||||
u8 aging;
|
||||
u8 mac[6];
|
||||
bool noarp;
|
||||
u8 live;
|
||||
u8 type;
|
||||
u8 fid;
|
||||
u8 ivl;
|
||||
};
|
||||
|
||||
/* struct an8855_port - This is the main data structure for holding the state
|
||||
* of the port.
|
||||
* @enable: The status used for show port is enabled or not.
|
||||
* @pm: The matrix used to show all connections with the port.
|
||||
* @pvid: The VLAN specified is to be considered a PVID at ingress. Any
|
||||
* untagged frames will be assigned to the related VLAN.
|
||||
* @vlan_filtering: The flags indicating whether the port that can recognize
|
||||
* VLAN-tagged frames.
|
||||
*/
|
||||
struct an8855_port {
|
||||
bool enable;
|
||||
u32 pm;
|
||||
u16 pvid;
|
||||
};
|
||||
|
||||
/* struct an8855_info - This is the main data structure for holding the specific
|
||||
* part for each supported device
|
||||
* @sw_setup: Holding the handler to a device initialization
|
||||
* @phy_read: Holding the way reading PHY port
|
||||
* @phy_write: Holding the way writing PHY port
|
||||
* @pad_setup: Holding the way setting up the bus pad for a certain
|
||||
* MAC port
|
||||
* @phy_mode_supported: Check if the PHY type is being supported on a certain
|
||||
* port
|
||||
* @mac_port_validate: Holding the way to set addition validate type for a
|
||||
* certan MAC port
|
||||
* @mac_port_get_state: Holding the way getting the MAC/PCS state for a certain
|
||||
* MAC port
|
||||
* @mac_port_config: Holding the way setting up the PHY attribute to a
|
||||
* certain MAC port
|
||||
* @mac_pcs_an_restart Holding the way restarting PCS autonegotiation for a
|
||||
* certain MAC port
|
||||
* @mac_pcs_link_up: Holding the way setting up the PHY attribute to the pcs
|
||||
* of the certain MAC port
|
||||
*/
|
||||
struct an8855_dev_info {
|
||||
enum an8855_id id;
|
||||
|
||||
int (*sw_setup)(struct dsa_switch *ds);
|
||||
int (*phy_read)(struct dsa_switch *ds, int port, int regnum);
|
||||
int (*phy_write)(struct dsa_switch *ds, int port, int regnum,
|
||||
u16 val);
|
||||
int (*pad_setup)(struct dsa_switch *ds, phy_interface_t interface);
|
||||
int (*cpu_port_config)(struct dsa_switch *ds, int port);
|
||||
bool (*phy_mode_supported)(struct dsa_switch *ds, int port,
|
||||
const struct phylink_link_state *state);
|
||||
void (*mac_port_validate)(struct dsa_switch *ds, int port,
|
||||
unsigned long *supported);
|
||||
int (*mac_port_get_state)(struct dsa_switch *ds, int port,
|
||||
struct phylink_link_state *state);
|
||||
int (*mac_port_config)(struct dsa_switch *ds, int port,
|
||||
unsigned int mode, phy_interface_t interface);
|
||||
void (*mac_pcs_an_restart)(struct dsa_switch *ds, int port);
|
||||
};
|
||||
|
||||
/* struct an8855_priv - This is the main data structure for holding the state
|
||||
* of the driver
|
||||
* @dev: The device pointer
|
||||
* @ds: The pointer to the dsa core structure
|
||||
* @bus: The bus used for the device and built-in PHY
|
||||
* @rstc: The pointer to reset control used by MCM
|
||||
* @core_pwr: The power supplied into the core
|
||||
* @io_pwr: The power supplied into the I/O
|
||||
* @reset: The descriptor for GPIO line tied to its reset pin
|
||||
* @mcm: Flag for distinguishing if standalone IC or module
|
||||
* coupling
|
||||
* @ports: Holding the state among ports
|
||||
* @reg_mutex: The lock for protecting among process accessing
|
||||
* registers
|
||||
* @p6_interface Holding the current port 6 interface
|
||||
* @p5_intf_sel: Holding the current port 5 interface select
|
||||
*/
|
||||
struct an8855_priv {
|
||||
struct device *dev;
|
||||
struct dsa_switch *ds;
|
||||
struct mii_bus *bus;
|
||||
struct reset_control *rstc;
|
||||
struct regulator *core_pwr;
|
||||
struct regulator *io_pwr;
|
||||
struct gpio_desc *reset;
|
||||
void __iomem *base;
|
||||
const struct an8855_dev_info *info;
|
||||
unsigned int phy_base;
|
||||
int phy_base_new;
|
||||
unsigned int id;
|
||||
phy_interface_t p5_interface;
|
||||
unsigned int p5_intf_sel;
|
||||
u8 mirror_rx;
|
||||
u8 mirror_tx;
|
||||
u8 eee_enable;
|
||||
|
||||
struct an8855_port ports[AN8855_NUM_PORTS];
|
||||
/* protect among processes for registers access */
|
||||
struct mutex reg_mutex;
|
||||
};
|
||||
|
||||
struct an8855_hw_vlan_entry {
|
||||
int port;
|
||||
u8 old_members;
|
||||
bool untagged;
|
||||
};
|
||||
|
||||
static inline void an8855_hw_vlan_entry_init(struct an8855_hw_vlan_entry *e,
|
||||
int port, bool untagged)
|
||||
{
|
||||
e->port = port;
|
||||
e->untagged = untagged;
|
||||
}
|
||||
|
||||
typedef void (*an8855_vlan_op) (struct an8855_priv *,
|
||||
struct an8855_hw_vlan_entry *);
|
||||
|
||||
struct an8855_hw_stats {
|
||||
const char *string;
|
||||
u16 reg;
|
||||
u8 sizeof_stat;
|
||||
};
|
||||
|
||||
struct an8855_dummy_poll {
|
||||
struct an8855_priv *priv;
|
||||
u32 reg;
|
||||
};
|
||||
|
||||
static inline void INIT_AN8855_DUMMY_POLL(struct an8855_dummy_poll *p,
|
||||
struct an8855_priv *priv, u32 reg)
|
||||
{
|
||||
p->priv = priv;
|
||||
p->reg = reg;
|
||||
}
|
||||
|
||||
int an8855_phy_setup(struct dsa_switch *ds);
|
||||
u32 an8855_read(struct an8855_priv *priv, u32 reg);
|
||||
void an8855_write(struct an8855_priv *priv, u32 reg, u32 val);
|
||||
int an8855_phy_cl22_read(struct an8855_priv *priv, int port, int regnum);
|
||||
int an8855_phy_cl22_write(struct an8855_priv *priv, int port,
|
||||
int regnum, u16 val);
|
||||
int an8855_phy_cl45_read(struct an8855_priv *priv, int port, int devad,
|
||||
int regnum);
|
||||
int an8855_phy_cl45_write(struct an8855_priv *priv, int port, int devad,
|
||||
int regnum, u16 val);
|
||||
#endif /* __AN8855_H */
|
||||
@@ -0,0 +1,322 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2023 Airoha Inc.
|
||||
* Author: Min Yao <min.yao@airoha.com>
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <net/genetlink.h>
|
||||
#include <linux/of_mdio.h>
|
||||
#include <linux/phylink.h>
|
||||
#include <net/dsa.h>
|
||||
|
||||
#include "an8855.h"
|
||||
#include "an8855_nl.h"
|
||||
|
||||
struct an8855_nl_cmd_item {
|
||||
enum an8855_cmd cmd;
|
||||
bool require_dev;
|
||||
int (*process)(struct genl_info *info);
|
||||
u32 nr_required_attrs;
|
||||
const enum an8855_attr *required_attrs;
|
||||
};
|
||||
|
||||
struct an8855_priv *an8855_sw_priv;
|
||||
|
||||
static DEFINE_MUTEX(an8855_devs_lock);
|
||||
|
||||
void
|
||||
an8855_put(void)
|
||||
{
|
||||
mutex_unlock(&an8855_devs_lock);
|
||||
}
|
||||
|
||||
void
|
||||
an8855_lock(void)
|
||||
{
|
||||
mutex_lock(&an8855_devs_lock);
|
||||
}
|
||||
|
||||
static int an8855_nl_response(struct sk_buff *skb, struct genl_info *info);
|
||||
|
||||
static const struct nla_policy an8855_nl_cmd_policy[] = {
|
||||
[AN8855_ATTR_TYPE_MESG] = {.type = NLA_STRING},
|
||||
[AN8855_ATTR_TYPE_PHY] = {.type = NLA_S32},
|
||||
[AN8855_ATTR_TYPE_REG] = {.type = NLA_S32},
|
||||
[AN8855_ATTR_TYPE_VAL] = {.type = NLA_S32},
|
||||
[AN8855_ATTR_TYPE_DEV_NAME] = {.type = NLA_S32},
|
||||
[AN8855_ATTR_TYPE_DEV_ID] = {.type = NLA_S32},
|
||||
[AN8855_ATTR_TYPE_DEVAD] = {.type = NLA_S32},
|
||||
};
|
||||
|
||||
static const struct genl_ops an8855_nl_ops[] = {
|
||||
{
|
||||
.cmd = AN8855_CMD_REQUEST,
|
||||
.doit = an8855_nl_response,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
{
|
||||
.cmd = AN8855_CMD_READ,
|
||||
.doit = an8855_nl_response,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
{
|
||||
.cmd = AN8855_CMD_WRITE,
|
||||
.doit = an8855_nl_response,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
};
|
||||
|
||||
static struct genl_family an8855_nl_family = {
|
||||
.name = AN8855_DSA_GENL_NAME,
|
||||
.version = AN8855_GENL_VERSION,
|
||||
.maxattr = AN8855_NR_ATTR_TYPE,
|
||||
.ops = an8855_nl_ops,
|
||||
.n_ops = ARRAY_SIZE(an8855_nl_ops),
|
||||
.policy = an8855_nl_cmd_policy,
|
||||
};
|
||||
|
||||
static int
|
||||
an8855_nl_prepare_reply(struct genl_info *info, u8 cmd,
|
||||
struct sk_buff **skbp)
|
||||
{
|
||||
struct sk_buff *msg;
|
||||
void *reply;
|
||||
|
||||
if (!info)
|
||||
return -EINVAL;
|
||||
|
||||
msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Construct send-back message header */
|
||||
reply = genlmsg_put(msg, info->snd_portid, info->snd_seq,
|
||||
&an8855_nl_family, 0, cmd);
|
||||
if (!reply) {
|
||||
nlmsg_free(msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*skbp = msg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
an8855_nl_send_reply(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct genlmsghdr *genlhdr = nlmsg_data(nlmsg_hdr(skb));
|
||||
void *reply = genlmsg_data(genlhdr);
|
||||
|
||||
/* Finalize a generic netlink message (update message header) */
|
||||
genlmsg_end(skb, reply);
|
||||
|
||||
/* reply to a request */
|
||||
return genlmsg_reply(skb, info);
|
||||
}
|
||||
|
||||
static s32
|
||||
an8855_nl_get_s32(struct genl_info *info, enum an8855_attr attr,
|
||||
s32 defval)
|
||||
{
|
||||
struct nlattr *na;
|
||||
|
||||
na = info->attrs[attr];
|
||||
if (na)
|
||||
return nla_get_s32(na);
|
||||
|
||||
return defval;
|
||||
}
|
||||
|
||||
static int
|
||||
an8855_nl_get_u32(struct genl_info *info, enum an8855_attr attr,
|
||||
u32 *val)
|
||||
{
|
||||
struct nlattr *na;
|
||||
|
||||
na = info->attrs[attr];
|
||||
if (na) {
|
||||
*val = nla_get_u32(na);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
an8855_nl_reply_read(struct genl_info *info)
|
||||
{
|
||||
struct sk_buff *rep_skb = NULL;
|
||||
s32 phy, devad, reg;
|
||||
int value;
|
||||
int ret = 0;
|
||||
|
||||
phy = an8855_nl_get_s32(info, AN8855_ATTR_TYPE_PHY, -1);
|
||||
devad = an8855_nl_get_s32(info, AN8855_ATTR_TYPE_DEVAD, -1);
|
||||
reg = an8855_nl_get_s32(info, AN8855_ATTR_TYPE_REG, -1);
|
||||
|
||||
if (reg < 0)
|
||||
goto err;
|
||||
|
||||
ret = an8855_nl_prepare_reply(info, AN8855_CMD_READ, &rep_skb);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
if (phy >= 0) {
|
||||
if (devad < 0)
|
||||
value = an8855_phy_cl22_read(an8855_sw_priv, phy, reg);
|
||||
else
|
||||
value = an8855_phy_cl45_read(an8855_sw_priv, phy,
|
||||
devad, reg);
|
||||
} else
|
||||
value = an8855_read(an8855_sw_priv, reg);
|
||||
ret = nla_put_s32(rep_skb, AN8855_ATTR_TYPE_REG, reg);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
ret = nla_put_s32(rep_skb, AN8855_ATTR_TYPE_VAL, value);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
return an8855_nl_send_reply(rep_skb, info);
|
||||
|
||||
err:
|
||||
if (rep_skb)
|
||||
nlmsg_free(rep_skb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
an8855_nl_reply_write(struct genl_info *info)
|
||||
{
|
||||
struct sk_buff *rep_skb = NULL;
|
||||
s32 phy, devad, reg;
|
||||
u32 value;
|
||||
int ret = 0;
|
||||
|
||||
phy = an8855_nl_get_s32(info, AN8855_ATTR_TYPE_PHY, -1);
|
||||
devad = an8855_nl_get_s32(info, AN8855_ATTR_TYPE_DEVAD, -1);
|
||||
reg = an8855_nl_get_s32(info, AN8855_ATTR_TYPE_REG, -1);
|
||||
|
||||
if (an8855_nl_get_u32(info, AN8855_ATTR_TYPE_VAL, &value))
|
||||
goto err;
|
||||
|
||||
if (reg < 0)
|
||||
goto err;
|
||||
|
||||
ret = an8855_nl_prepare_reply(info, AN8855_CMD_WRITE, &rep_skb);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
if (phy >= 0) {
|
||||
if (devad < 0)
|
||||
an8855_phy_cl22_write(an8855_sw_priv, phy, reg, value);
|
||||
else
|
||||
an8855_phy_cl45_write(an8855_sw_priv, phy, devad, reg,
|
||||
value);
|
||||
} else
|
||||
an8855_write(an8855_sw_priv, reg, value);
|
||||
ret = nla_put_s32(rep_skb, AN8855_ATTR_TYPE_REG, reg);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
ret = nla_put_s32(rep_skb, AN8855_ATTR_TYPE_VAL, value);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
return an8855_nl_send_reply(rep_skb, info);
|
||||
|
||||
err:
|
||||
if (rep_skb)
|
||||
nlmsg_free(rep_skb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const enum an8855_attr an8855_nl_cmd_read_attrs[] = {
|
||||
AN8855_ATTR_TYPE_REG
|
||||
};
|
||||
|
||||
static const enum an8855_attr an8855_nl_cmd_write_attrs[] = {
|
||||
AN8855_ATTR_TYPE_REG,
|
||||
AN8855_ATTR_TYPE_VAL
|
||||
};
|
||||
|
||||
static const struct an8855_nl_cmd_item an8855_nl_cmds[] = {
|
||||
{
|
||||
.cmd = AN8855_CMD_READ,
|
||||
.require_dev = true,
|
||||
.process = an8855_nl_reply_read,
|
||||
.required_attrs = an8855_nl_cmd_read_attrs,
|
||||
.nr_required_attrs = ARRAY_SIZE(an8855_nl_cmd_read_attrs),
|
||||
},
|
||||
{
|
||||
.cmd = AN8855_CMD_WRITE,
|
||||
.require_dev = true,
|
||||
.process = an8855_nl_reply_write,
|
||||
.required_attrs = an8855_nl_cmd_write_attrs,
|
||||
.nr_required_attrs = ARRAY_SIZE(an8855_nl_cmd_write_attrs),
|
||||
}
|
||||
};
|
||||
|
||||
static int
|
||||
an8855_nl_response(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct genlmsghdr *hdr = nlmsg_data(info->nlhdr);
|
||||
const struct an8855_nl_cmd_item *cmditem = NULL;
|
||||
u32 sat_req_attrs = 0;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(an8855_nl_cmds); i++) {
|
||||
if (hdr->cmd == an8855_nl_cmds[i].cmd) {
|
||||
cmditem = &an8855_nl_cmds[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!cmditem) {
|
||||
pr_info("an8855-nl: unknown cmd %u\n", hdr->cmd);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < cmditem->nr_required_attrs; i++) {
|
||||
if (info->attrs[cmditem->required_attrs[i]])
|
||||
sat_req_attrs++;
|
||||
}
|
||||
|
||||
if (sat_req_attrs != cmditem->nr_required_attrs) {
|
||||
pr_info("an8855-nl: missing required attr(s) for cmd %u\n",
|
||||
hdr->cmd);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = cmditem->process(info);
|
||||
|
||||
an8855_put();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
an8855_nl_init(struct an8855_priv **priv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pr_info("an8855-nl: genl_register_family_with_ops\n");
|
||||
|
||||
an8855_sw_priv = *priv;
|
||||
ret = genl_register_family(&an8855_nl_family);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
an8855_nl_exit(void)
|
||||
{
|
||||
an8855_sw_priv = NULL;
|
||||
genl_unregister_family(&an8855_nl_family);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2023 Airoha Inc.
|
||||
* Author: Min Yao <min.yao@airoha.com>
|
||||
*/
|
||||
|
||||
#ifndef _AN8855_NL_H_
|
||||
#define _AN8855_NL_H_
|
||||
|
||||
#define AN8855_DSA_GENL_NAME "an8855_dsa"
|
||||
#define AN8855_GENL_VERSION 0x1
|
||||
|
||||
enum an8855_cmd {
|
||||
AN8855_CMD_UNSPEC = 0,
|
||||
AN8855_CMD_REQUEST,
|
||||
AN8855_CMD_REPLY,
|
||||
AN8855_CMD_READ,
|
||||
AN8855_CMD_WRITE,
|
||||
|
||||
__AN8855_CMD_MAX,
|
||||
};
|
||||
|
||||
enum an8855_attr {
|
||||
AN8855_ATTR_TYPE_UNSPEC = 0,
|
||||
AN8855_ATTR_TYPE_MESG,
|
||||
AN8855_ATTR_TYPE_PHY,
|
||||
AN8855_ATTR_TYPE_DEVAD,
|
||||
AN8855_ATTR_TYPE_REG,
|
||||
AN8855_ATTR_TYPE_VAL,
|
||||
AN8855_ATTR_TYPE_DEV_NAME,
|
||||
AN8855_ATTR_TYPE_DEV_ID,
|
||||
|
||||
__AN8855_ATTR_TYPE_MAX,
|
||||
};
|
||||
|
||||
#define AN8855_NR_ATTR_TYPE (__AN8855_ATTR_TYPE_MAX - 1)
|
||||
|
||||
#ifdef __KERNEL__
|
||||
int an8855_nl_init(struct an8855_priv **priv);
|
||||
void an8855_nl_exit(void);
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#endif /* _AN8855_NL_H_ */
|
||||
@@ -0,0 +1,141 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Common part for Airoha AN8855 gigabit switch
|
||||
*
|
||||
* Copyright (C) 2023 Airoha Inc. All Rights Reserved.
|
||||
*
|
||||
* Author: Min Yao <min.yao@airoha.com>
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <net/dsa.h>
|
||||
#include "an8855.h"
|
||||
#include "an8855_phy.h"
|
||||
|
||||
#define AN8855_NUM_PHYS 5
|
||||
|
||||
static u32
|
||||
an8855_phy_read_dev_reg(struct dsa_switch *ds, u32 port_num,
|
||||
u32 dev_addr, u32 reg_addr)
|
||||
{
|
||||
struct an8855_priv *priv = ds->priv;
|
||||
u32 phy_val;
|
||||
u32 addr;
|
||||
|
||||
addr = MII_ADDR_C45 | (dev_addr << 16) | (reg_addr & 0xffff);
|
||||
phy_val = priv->info->phy_read(ds, port_num, addr);
|
||||
|
||||
return phy_val;
|
||||
}
|
||||
|
||||
static void
|
||||
an8855_phy_write_dev_reg(struct dsa_switch *ds, u32 port_num,
|
||||
u32 dev_addr, u32 reg_addr, u32 write_data)
|
||||
{
|
||||
struct an8855_priv *priv = ds->priv;
|
||||
u32 addr;
|
||||
|
||||
addr = MII_ADDR_C45 | (dev_addr << 16) | (reg_addr & 0xffff);
|
||||
|
||||
priv->info->phy_write(ds, port_num, addr, write_data);
|
||||
}
|
||||
|
||||
static void
|
||||
an8855_switch_phy_write(struct dsa_switch *ds, u32 port_num,
|
||||
u32 reg_addr, u32 write_data)
|
||||
{
|
||||
struct an8855_priv *priv = ds->priv;
|
||||
|
||||
priv->info->phy_write(ds, port_num, reg_addr, write_data);
|
||||
}
|
||||
|
||||
static u32
|
||||
an8855_switch_phy_read(struct dsa_switch *ds, u32 port_num,
|
||||
u32 reg_addr)
|
||||
{
|
||||
struct an8855_priv *priv = ds->priv;
|
||||
|
||||
return priv->info->phy_read(ds, port_num, reg_addr);
|
||||
}
|
||||
|
||||
static void
|
||||
an8855_phy_setting(struct dsa_switch *ds)
|
||||
{
|
||||
struct an8855_priv *priv = ds->priv;
|
||||
int i;
|
||||
u32 val;
|
||||
|
||||
/* Release power down */
|
||||
an8855_write(priv, RG_GPHY_AFE_PWD, 0x0);
|
||||
for (i = 0; i < AN8855_NUM_PHYS; i++) {
|
||||
/* Enable HW auto downshift */
|
||||
an8855_switch_phy_write(ds, i, 0x1f, 0x1);
|
||||
val = an8855_switch_phy_read(ds, i, PHY_EXT_REG_14);
|
||||
val |= PHY_EN_DOWN_SHFIT;
|
||||
an8855_switch_phy_write(ds, i, PHY_EXT_REG_14, val);
|
||||
an8855_switch_phy_write(ds, i, 0x1f, 0x0);
|
||||
|
||||
/* Enable Asymmetric Pause Capability */
|
||||
val = an8855_switch_phy_read(ds, i, MII_ADVERTISE);
|
||||
val |= ADVERTISE_PAUSE_ASYM;
|
||||
an8855_switch_phy_write(ds, i, MII_ADVERTISE, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
an8855_low_power_setting(struct dsa_switch *ds)
|
||||
{
|
||||
int port, addr;
|
||||
|
||||
for (port = 0; port < AN8855_NUM_PHYS; port++) {
|
||||
an8855_phy_write_dev_reg(ds, port, 0x1e, 0x11, 0x0f00);
|
||||
an8855_phy_write_dev_reg(ds, port, 0x1e, 0x3c, 0x0000);
|
||||
an8855_phy_write_dev_reg(ds, port, 0x1e, 0x3d, 0x0000);
|
||||
an8855_phy_write_dev_reg(ds, port, 0x1e, 0x3e, 0x0000);
|
||||
an8855_phy_write_dev_reg(ds, port, 0x1e, 0xc6, 0x53aa);
|
||||
}
|
||||
|
||||
an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x268, 0x07f1);
|
||||
an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x269, 0x2111);
|
||||
an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x26a, 0x0000);
|
||||
an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x26b, 0x0074);
|
||||
an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x26e, 0x00f6);
|
||||
an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x26f, 0x6666);
|
||||
an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x271, 0x2c02);
|
||||
an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x272, 0x0c22);
|
||||
an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x700, 0x0001);
|
||||
an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x701, 0x0803);
|
||||
an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x702, 0x01b6);
|
||||
an8855_phy_write_dev_reg(ds, 0, 0x1f, 0x703, 0x2111);
|
||||
|
||||
an8855_phy_write_dev_reg(ds, 1, 0x1f, 0x700, 0x0001);
|
||||
|
||||
for (addr = 0x200; addr <= 0x230; addr += 2)
|
||||
an8855_phy_write_dev_reg(ds, 0, 0x1f, addr, 0x2020);
|
||||
|
||||
for (addr = 0x201; addr <= 0x231; addr += 2)
|
||||
an8855_phy_write_dev_reg(ds, 0, 0x1f, addr, 0x0020);
|
||||
}
|
||||
|
||||
static void
|
||||
an8855_eee_setting(struct dsa_switch *ds, u32 port)
|
||||
{
|
||||
/* Disable EEE */
|
||||
an8855_phy_write_dev_reg(ds, port, PHY_DEV07, PHY_DEV07_REG_03C, 0);
|
||||
}
|
||||
|
||||
int
|
||||
an8855_phy_setup(struct dsa_switch *ds)
|
||||
{
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
an8855_phy_setting(ds);
|
||||
|
||||
for (i = 0; i < AN8855_NUM_PHYS; i++)
|
||||
an8855_eee_setting(ds, i);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Common part for Airoha AN8855 gigabit switch
|
||||
*
|
||||
* Copyright (C) 2023 Airoha Inc. All Rights Reserved.
|
||||
*
|
||||
* Author: Min Yao <min.yao@airoha.com>
|
||||
*/
|
||||
|
||||
#ifndef _AN8855_PHY_H_
|
||||
#define _AN8855_PHY_H_
|
||||
|
||||
#include <linux/bitops.h>
|
||||
|
||||
/*phy calibration use*/
|
||||
#define DEV_1E 0x1E
|
||||
/*global device 0x1f, always set P0*/
|
||||
#define DEV_1F 0x1F
|
||||
|
||||
/************IEXT/REXT CAL***************/
|
||||
/* bits range: for example BITS(16,23) = 0xFF0000*/
|
||||
#define BITS(m, n) (~(BIT(m) - 1) & ((BIT(n) - 1) | BIT(n)))
|
||||
#define ANACAL_INIT 0x01
|
||||
#define ANACAL_ERROR 0xFD
|
||||
#define ANACAL_SATURATION 0xFE
|
||||
#define ANACAL_FINISH 0xFF
|
||||
#define ANACAL_PAIR_A 0
|
||||
#define ANACAL_PAIR_B 1
|
||||
#define ANACAL_PAIR_C 2
|
||||
#define ANACAL_PAIR_D 3
|
||||
#define DAC_IN_0V 0x00
|
||||
#define DAC_IN_2V 0xf0
|
||||
#define TX_AMP_OFFSET_0MV 0x20
|
||||
#define TX_AMP_OFFSET_VALID_BITS 6
|
||||
|
||||
#define R0 0
|
||||
#define PHY0 0
|
||||
#define PHY1 1
|
||||
#define PHY2 2
|
||||
#define PHY3 3
|
||||
#define PHY4 4
|
||||
#define ANA_TEST_MODE BITS(8, 15)
|
||||
#define TST_TCLK_SEL BITs(6, 7)
|
||||
#define ANA_TEST_VGA_RG 0x100
|
||||
|
||||
#define FORCE_MDI_CROSS_OVER BITS(3, 4)
|
||||
#define T10_TEST_CTL_RG 0x145
|
||||
#define RG_185 0x185
|
||||
#define RG_TX_SLEW BIT(0)
|
||||
#define ANA_CAL_0 0xdb
|
||||
#define RG_CAL_CKINV BIT(12)
|
||||
#define RG_ANA_CALEN BIT(8)
|
||||
#define RG_REXT_CALEN BIT(4)
|
||||
#define RG_ZCALEN_A BIT(0)
|
||||
#define ANA_CAL_1 0xdc
|
||||
#define RG_ZCALEN_B BIT(12)
|
||||
#define RG_ZCALEN_C BIT(8)
|
||||
#define RG_ZCALEN_D BIT(4)
|
||||
#define RG_TXVOS_CALEN BIT(0)
|
||||
#define ANA_CAL_6 0xe1
|
||||
#define RG_CAL_REFSEL BIT(4)
|
||||
#define RG_CAL_COMP_PWD BIT(0)
|
||||
#define ANA_CAL_5 0xe0
|
||||
#define RG_REXT_TRIM BITs(8, 13)
|
||||
#define RG_ZCAL_CTRL BITs(0, 5)
|
||||
#define RG_17A 0x17a
|
||||
#define AD_CAL_COMP_OUT BIT(8)
|
||||
#define RG_17B 0x17b
|
||||
#define AD_CAL_CLK bit(0)
|
||||
#define RG_17C 0x17c
|
||||
#define DA_CALIN_FLAG bit(0)
|
||||
/************R50 CAL****************************/
|
||||
#define RG_174 0x174
|
||||
#define RG_R50OHM_RSEL_TX_A_EN BIT[15]
|
||||
#define CR_R50OHM_RSEL_TX_A BITS[8:14]
|
||||
#define RG_R50OHM_RSEL_TX_B_EN BIT[7]
|
||||
#define CR_R50OHM_RSEL_TX_B BITS[6:0]
|
||||
#define RG_175 0x175
|
||||
#define RG_R50OHM_RSEL_TX_C_EN BITS[15]
|
||||
#define CR_R50OHM_RSEL_TX_C BITS[8:14]
|
||||
#define RG_R50OHM_RSEL_TX_D_EN BIT[7]
|
||||
#define CR_R50OHM_RSEL_TX_D BITS[0:6]
|
||||
/**********TX offset Calibration***************************/
|
||||
#define RG_95 0x96
|
||||
#define BYPASS_TX_OFFSET_CAL BIT(15)
|
||||
#define RG_3E 0x3e
|
||||
#define BYPASS_PD_TXVLD_A BIT(15)
|
||||
#define BYPASS_PD_TXVLD_B BIT(14)
|
||||
#define BYPASS_PD_TXVLD_C BIT(13)
|
||||
#define BYPASS_PD_TXVLD_D BIT(12)
|
||||
#define BYPASS_PD_TX_10M BIT(11)
|
||||
#define POWER_DOWN_TXVLD_A BIT(7)
|
||||
#define POWER_DOWN_TXVLD_B BIT(6)
|
||||
#define POWER_DOWN_TXVLD_C BIT(5)
|
||||
#define POWER_DOWN_TXVLD_D BIT(4)
|
||||
#define POWER_DOWN_TX_10M BIT(3)
|
||||
#define RG_DD 0xdd
|
||||
#define RG_TXG_CALEN_A BIT(12)
|
||||
#define RG_TXG_CALEN_B BIT(8)
|
||||
#define RG_TXG_CALEN_C BIT(4)
|
||||
#define RG_TXG_CALEN_D BIT(0)
|
||||
#define RG_17D 0x17D
|
||||
#define FORCE_DASN_DAC_IN0_A BIT(15)
|
||||
#define DASN_DAC_IN0_A BITS(0, 9)
|
||||
#define RG_17E 0x17E
|
||||
#define FORCE_DASN_DAC_IN0_B BIT(15)
|
||||
#define DASN_DAC_IN0_B BITS(0, 9)
|
||||
#define RG_17F 0x17F
|
||||
|
||||
#define FORCE_DASN_DAC_IN0_C BIT(15)
|
||||
#define DASN_DAC_IN0_C BITS(0, 9)
|
||||
#define RG_180 0x180
|
||||
#define FORCE_DASN_DAC_IN0_D BIT(15)
|
||||
#define DASN_DAC_IN0_D BITS(0, 9)
|
||||
|
||||
#define RG_181 0x181
|
||||
#define FORCE_DASN_DAC_IN1_A BIT(15)
|
||||
#define DASN_DAC_IN1_A BITS(0, 9)
|
||||
#define RG_182 0x182
|
||||
#define FORCE_DASN_DAC_IN1_B BIT(15)
|
||||
#define DASN_DAC_IN1_B BITS(0, 9)
|
||||
#define RG_183 0x183
|
||||
#define FORCE_DASN_DAC_IN1_C BIT(15)
|
||||
#define DASN_DAC_IN1_C BITS(0, 9)
|
||||
#define RG_184 0x184
|
||||
#define FORCE_DASN_DAC_IN1_D BIT(15)
|
||||
#define DASN_DAC_IN1_D BITS(0, 9)
|
||||
#define RG_172 0x172
|
||||
#define CR_TX_AMP_OFFSET_A BITS(8, 13)
|
||||
#define CR_TX_AMP_OFFSET_B BITS(0, 5)
|
||||
#define RG_173 0x173
|
||||
#define CR_TX_AMP_OFFSET_C BITS(8, 13)
|
||||
#define CR_TX_AMP_OFFSET_D BITS(0, 5)
|
||||
/**********TX Amp Calibration ***************************/
|
||||
#define RG_12 0x12
|
||||
#define DA_TX_I2MPB_A_GBE BITS(10, 15)
|
||||
#define RG_17 0x17
|
||||
#define DA_TX_I2MPB_B_GBE BITS(8, 13)
|
||||
#define RG_19 0x19
|
||||
#define DA_TX_I2MPB_C_GBE BITS(8, 13)
|
||||
#define RG_21 0x21
|
||||
#define DA_TX_I2MPB_D_GBE BITS(8, 13)
|
||||
#define TX_AMP_MAX 0x3f
|
||||
#define TX_AMP_MAX_OFFSET 0xb
|
||||
#define TX_AMP_HIGHEST_TS ((TX_AMP_MAX) + 3)
|
||||
#define TX_AMP_LOWEST_TS (0 - 3)
|
||||
#define TX_AMP_HIGH_TS (TX_AMP_MAX)
|
||||
#define TX_AMP_LOW_TS 0
|
||||
|
||||
/* PHY Extend Register 0x14 bitmap of define */
|
||||
#define PHY_EXT_REG_14 0x14
|
||||
|
||||
/* Fields of PHY_EXT_REG_14 */
|
||||
#define PHY_EN_DOWN_SHFIT BIT(4)
|
||||
|
||||
/* PHY Extend Register 0x17 bitmap of define */
|
||||
#define PHY_EXT_REG_17 0x17
|
||||
|
||||
/* Fields of PHY_EXT_REG_17 */
|
||||
#define PHY_LINKDOWN_POWER_SAVING_EN BIT(4)
|
||||
|
||||
/* PHY PMA Register 0x17 bitmap of define */
|
||||
#define SLV_DSP_READY_TIME_S 15
|
||||
#define SLV_DSP_READY_TIME_M (0xff << SLV_DSP_READY_TIME_S)
|
||||
|
||||
/* PHY PMA Register 0x18 bitmap of define */
|
||||
#define ENABLE_RANDOM_UPDATE_TRIGGER BIT(8)
|
||||
|
||||
/* PHY EEE Register bitmap of define */
|
||||
#define PHY_DEV07 0x07
|
||||
#define PHY_DEV07_REG_03C 0x3c
|
||||
|
||||
/* PHY DEV 0x1e Register bitmap of define */
|
||||
#define PHY_DEV1E 0x1e
|
||||
#define PHY_DEV1F 0x1f
|
||||
|
||||
/* Proprietory Control Register of Internal Phy device 0x1e */
|
||||
#define PHY_TX_MLT3_BASE 0x0
|
||||
#define PHY_DEV1E_REG_13 0x13
|
||||
#define PHY_DEV1E_REG_14 0x14
|
||||
#define PHY_DEV1E_REG_41 0x41
|
||||
#define PHY_DEV1E_REG_A6 0xa6
|
||||
#define RXADC_CONTROL_3 0xc2
|
||||
#define PHY_DEV1E_REG_0C6 0xc6
|
||||
#define RXADC_LDO_CONTROL_2 0xd3
|
||||
#define PHY_DEV1E_REG_0FE 0xfe
|
||||
#define PHY_DEV1E_REG_123 0x123
|
||||
#define PHY_DEV1E_REG_189 0x189
|
||||
#define PHY_DEV1E_REG_234 0x234
|
||||
|
||||
/* Proprietory Control Register of Internal Phy device 0x1f */
|
||||
#define PHY_DEV1F_REG_44 0x44
|
||||
#define PHY_DEV1F_REG_268 0x268
|
||||
#define PHY_DEV1F_REG_269 0x269
|
||||
#define PHY_DEV1F_REG_26A 0x26A
|
||||
#define TXVLD_DA_271 0x271
|
||||
#define TXVLD_DA_272 0x272
|
||||
#define TXVLD_DA_273 0x273
|
||||
|
||||
/* Fields of PHY_DEV1E_REG_0C6 */
|
||||
#define PHY_POWER_SAVING_S 8
|
||||
#define PHY_POWER_SAVING_M 0x300
|
||||
#define PHY_POWER_SAVING_TX 0x0
|
||||
|
||||
/* Fields of PHY_DEV1E_REG_189 */
|
||||
#define DESCRAMBLER_CLEAR_EN 0x1
|
||||
|
||||
/* Fields of PHY_DEV1E_REG_234 */
|
||||
#define TR_OPEN_LOOP_EN BIT(0)
|
||||
|
||||
/* Internal GPHY Page Control Register */
|
||||
#define PHY_CL22_PAGE_CTRL 0x1f
|
||||
#define PHY_TR_PAGE 0x52b5
|
||||
|
||||
/* Internal GPHY Token Ring Access Registers */
|
||||
#define PHY_TR_CTRL 0x10
|
||||
#define PHY_TR_LOW_DATA 0x11
|
||||
#define PHY_TR_HIGH_DATA 0x12
|
||||
|
||||
/* Fields of PHY_TR_CTRL */
|
||||
#define PHY_TR_PKT_XMT_STA BIT(15)
|
||||
#define PHY_TR_WR_S 13
|
||||
#define PHY_TR_CH_ADDR_S 11
|
||||
#define PHY_TR_NODE_ADDR_S 7
|
||||
#define PHY_TR_DATA_ADDR_S 1
|
||||
|
||||
enum phy_tr_wr {
|
||||
PHY_TR_WRITE = 0,
|
||||
PHY_TR_READ = 1,
|
||||
};
|
||||
|
||||
/* Helper macro for GPHY Token Ring Access */
|
||||
#define PHY_TR_LOW_VAL(x) ((x) & 0xffff)
|
||||
#define PHY_TR_HIGH_VAL(x) (((x) & 0xff0000) >> 16)
|
||||
|
||||
/* Token Ring Channels */
|
||||
#define PMA_CH 0x1
|
||||
#define DSP_CH 0x2
|
||||
|
||||
/* Token Ring Nodes */
|
||||
#define PMA_NOD 0xf
|
||||
#define DSP_NOD 0xd
|
||||
|
||||
/* Token Ring register range */
|
||||
enum tr_pma_reg_addr {
|
||||
PMA_MIN = 0x0,
|
||||
PMA_01 = 0x1,
|
||||
PMA_17 = 0x17,
|
||||
PMA_18 = 0x18,
|
||||
PMA_MAX = 0x3d,
|
||||
};
|
||||
|
||||
enum tr_dsp_reg_addr {
|
||||
DSP_MIN = 0x0,
|
||||
DSP_06 = 0x6,
|
||||
DSP_08 = 0x8,
|
||||
DSP_0f = 0xf,
|
||||
DSP_10 = 0x10,
|
||||
DSP_MAX = 0x3e,
|
||||
};
|
||||
#endif /* _AN8855_REGS_H_ */
|
||||
@@ -885,9 +885,11 @@ int tx_ring_read(struct seq_file *seq, void *v)
|
||||
int i = 0;
|
||||
|
||||
seq_printf(seq, "free count = %d\n", (int)atomic_read(&ring->free_count));
|
||||
seq_printf(seq, "cpu next free: %d\n", (int)(ring->next_free - ring->dma));
|
||||
seq_printf(seq, "cpu last free: %d\n", (int)(ring->last_free - ring->dma));
|
||||
for (i = 0; i < MTK_DMA_SIZE; i++) {
|
||||
seq_printf(seq, "cpu next free: %d\n",
|
||||
(int)(ring->next_free - ring->dma) / eth->soc->txrx.txd_size);
|
||||
seq_printf(seq, "cpu last free: %d\n",
|
||||
(int)(ring->last_free - ring->dma) / eth->soc->txrx.txd_size);
|
||||
for (i = 0; i < eth->soc->txrx.tx_dma_size; i++) {
|
||||
dma_addr_t tmp = ring->phys +
|
||||
i * (dma_addr_t)eth->soc->txrx.txd_size;
|
||||
|
||||
@@ -929,7 +931,7 @@ int hwtx_ring_read(struct seq_file *seq, void *v)
|
||||
struct mtk_tx_dma_v2 *hwtx_ring;
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < MTK_DMA_SIZE; i++) {
|
||||
for (i = 0; i < eth->soc->txrx.fq_dma_size; i++) {
|
||||
dma_addr_t addr = eth->phy_scratch_ring +
|
||||
i * (dma_addr_t)eth->soc->txrx.txd_size;
|
||||
|
||||
@@ -978,7 +980,7 @@ int rx_ring_read(struct seq_file *seq, void *v)
|
||||
continue;
|
||||
|
||||
seq_printf(seq, "[Ring%d] next to read: %d\n", j,
|
||||
NEXT_DESP_IDX(ring->calc_idx, MTK_DMA_SIZE));
|
||||
NEXT_DESP_IDX(ring->calc_idx, eth->soc->txrx.rx_dma_size));
|
||||
for (i = 0; i < ring->dma_size; i++) {
|
||||
rx_ring = ring->dma + i * eth->soc->txrx.rxd_size;
|
||||
|
||||
@@ -1155,7 +1157,16 @@ int dbg_regs_read(struct seq_file *seq, void *v)
|
||||
seq_printf(seq, "| MAC_P3_FSM : %08x |\n",
|
||||
mtk_r32(eth, MTK_MAC_FSM(2)));
|
||||
}
|
||||
|
||||
if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
|
||||
seq_printf(seq, "| XMAC_P1_MCR : %08x |\n",
|
||||
mtk_r32(eth, MTK_XMAC_MCR(1)));
|
||||
seq_printf(seq, "| XMAC_P2_MCR : %08x |\n",
|
||||
mtk_r32(eth, MTK_XMAC_MCR(2)));
|
||||
seq_printf(seq, "| XMAC_P1_STS : %08x |\n",
|
||||
mtk_r32(eth, MTK_XGMAC_STS(1)));
|
||||
seq_printf(seq, "| XMAC_P2_STS : %08x |\n",
|
||||
mtk_r32(eth, MTK_XGMAC_STS(2)));
|
||||
}
|
||||
if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) ||
|
||||
MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
|
||||
seq_printf(seq, "| FE_CDM1_FSM : %08x |\n",
|
||||
|
||||
@@ -194,6 +194,27 @@ static void mtk_dump_reg(void *_eth, char *name, u32 offset, u32 range)
|
||||
}
|
||||
}
|
||||
|
||||
static void mtk_dump_regmap(struct regmap *pmap, char *name,
|
||||
u32 offset, u32 range)
|
||||
{
|
||||
unsigned int cur = offset;
|
||||
unsigned int val1 = 0, val2 = 0, val3 = 0, val4 = 0;
|
||||
|
||||
if (!pmap)
|
||||
return;
|
||||
|
||||
pr_info("\n============ %s ============\n", name);
|
||||
while (cur < offset + range) {
|
||||
regmap_read(pmap, cur, &val1);
|
||||
regmap_read(pmap, cur + 0x4, &val2);
|
||||
regmap_read(pmap, cur + 0x8, &val3);
|
||||
regmap_read(pmap, cur + 0xc, &val4);
|
||||
pr_info("0x%x: %08x %08x %08x %08x\n",
|
||||
cur, val1, val2, val3, val4);
|
||||
cur += 0x10;
|
||||
}
|
||||
}
|
||||
|
||||
void mtk_dump_netsys_info(void *_eth)
|
||||
{
|
||||
struct mtk_eth *eth = _eth;
|
||||
@@ -211,13 +232,17 @@ void mtk_dump_netsys_info(void *_eth)
|
||||
mtk_dump_reg(eth, "WDMA", WDMA_BASE(0), 0x600);
|
||||
mtk_dump_reg(eth, "PPE", 0x2200, 0x200);
|
||||
mtk_dump_reg(eth, "GMAC", 0x10000, 0x300);
|
||||
mtk_dump_regmap(eth->sgmii->pcs[0].regmap,
|
||||
"SGMII0", 0, 0x1a0);
|
||||
mtk_dump_regmap(eth->sgmii->pcs[1].regmap,
|
||||
"SGMII1", 0, 0x1a0);
|
||||
if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
|
||||
mtk_dump_reg(eth, "XGMAC0", 0x12000, 0x300);
|
||||
mtk_dump_reg(eth, "XGMAC1", 0x13000, 0x300);
|
||||
mtk_dump_usxgmii(eth->usxgmii->pcs[0].regmap,
|
||||
"USXGMII0", 0, 0x1000);
|
||||
mtk_dump_usxgmii(eth->usxgmii->pcs[1].regmap,
|
||||
"USXGMII1", 0, 0x1000);
|
||||
mtk_dump_regmap(eth->usxgmii->pcs[0].regmap,
|
||||
"USXGMII0", 0, 0x1000);
|
||||
mtk_dump_regmap(eth->usxgmii->pcs[1].regmap,
|
||||
"USXGMII1", 0, 0x1000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -816,9 +816,12 @@ static int mtk_mac_finish(struct phylink_config *config, unsigned int mode,
|
||||
|
||||
/* Enable SGMII */
|
||||
if (interface == PHY_INTERFACE_MODE_SGMII ||
|
||||
phy_interface_mode_is_8023z(interface))
|
||||
phy_interface_mode_is_8023z(interface)) {
|
||||
spin_lock(ð->syscfg0_lock);
|
||||
regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0,
|
||||
SYSCFG0_SGMII_MASK, mac->syscfg0);
|
||||
spin_unlock(ð->syscfg0_lock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -967,10 +970,28 @@ static void mtk_gdm_fsm_poll(struct mtk_mac *mac)
|
||||
pr_info("%s fsm invalid", __func__);
|
||||
}
|
||||
|
||||
static void mtk_pse_port_link_set(struct mtk_mac *mac, bool up)
|
||||
static void mtk_pse_port_link_set(struct mtk_mac *mac, bool up,
|
||||
phy_interface_t interface)
|
||||
{
|
||||
u32 fe_glo_cfg, val = 0;
|
||||
|
||||
if (!up && interface == PHY_INTERFACE_MODE_XGMII) {
|
||||
void __iomem *base;
|
||||
|
||||
base = ioremap(0x0F0CFB00, SZ_4K);
|
||||
if (base) {
|
||||
/* wait for internal 2.5G PHY to turn off */
|
||||
usleep_range(100, 1000);
|
||||
/* enable the XGMAC clock for 10 msecs to
|
||||
* flush the packets.
|
||||
*/
|
||||
writel(readl(base) | BIT(9), base);
|
||||
usleep_range(10000, 11000);
|
||||
writel(readl(base) & ~BIT(9), base);
|
||||
iounmap(base);
|
||||
}
|
||||
}
|
||||
|
||||
fe_glo_cfg = mtk_r32(mac->hw, MTK_FE_GLO_CFG(mac->id));
|
||||
switch (mac->id) {
|
||||
case MTK_GMAC1_ID:
|
||||
@@ -1002,7 +1023,7 @@ static void mtk_mac_link_down(struct phylink_config *config, unsigned int mode,
|
||||
unsigned int id;
|
||||
u32 mcr, sts;
|
||||
|
||||
mtk_pse_port_link_set(mac, false);
|
||||
mtk_pse_port_link_set(mac, false, interface);
|
||||
if (mac->type == MTK_GDM_TYPE) {
|
||||
mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
|
||||
mcr &= ~(MAC_MCR_TX_EN | MAC_MCR_RX_EN | MAC_MCR_FORCE_LINK);
|
||||
@@ -1140,6 +1161,8 @@ static void mtk_mac_link_up(struct phylink_config *config, unsigned int mode,
|
||||
if (duplex == DUPLEX_FULL ||
|
||||
interface == PHY_INTERFACE_MODE_SGMII)
|
||||
mcr |= MAC_MCR_FORCE_DPX;
|
||||
else if (interface == PHY_INTERFACE_MODE_GMII)
|
||||
mcr |= MAC_MCR_PRMBL_LMT_EN;
|
||||
|
||||
/* Configure pause modes -
|
||||
* phylink will avoid these for half duplex
|
||||
@@ -1184,7 +1207,7 @@ static void mtk_mac_link_up(struct phylink_config *config, unsigned int mode,
|
||||
mcr &= ~(XMAC_MCR_TRX_DISABLE);
|
||||
mtk_w32(mac->hw, mcr, MTK_XMAC_MCR(mac->id));
|
||||
}
|
||||
mtk_pse_port_link_set(mac, true);
|
||||
mtk_pse_port_link_set(mac, true, interface);
|
||||
}
|
||||
|
||||
static void mtk_validate(struct phylink_config *config,
|
||||
@@ -1700,10 +1723,10 @@ static int mtk_init_fq_dma(struct mtk_eth *eth)
|
||||
{
|
||||
const struct mtk_soc_data *soc = eth->soc;
|
||||
dma_addr_t phy_ring_tail;
|
||||
int cnt = MTK_DMA_SIZE;
|
||||
int cnt = soc->txrx.fq_dma_size;
|
||||
dma_addr_t dma_addr;
|
||||
u64 addr64 = 0;
|
||||
int i;
|
||||
int i, j, len;
|
||||
|
||||
if (!eth->soc->has_sram) {
|
||||
eth->scratch_ring = dma_alloc_coherent(eth->dma_dev,
|
||||
@@ -1717,40 +1740,44 @@ static int mtk_init_fq_dma(struct mtk_eth *eth)
|
||||
if (unlikely(!eth->scratch_ring))
|
||||
return -ENOMEM;
|
||||
|
||||
eth->scratch_head = kcalloc(cnt, MTK_QDMA_PAGE_SIZE, GFP_KERNEL);
|
||||
if (unlikely(!eth->scratch_head))
|
||||
return -ENOMEM;
|
||||
|
||||
dma_addr = dma_map_single(eth->dma_dev,
|
||||
eth->scratch_head, cnt * MTK_QDMA_PAGE_SIZE,
|
||||
DMA_FROM_DEVICE);
|
||||
if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr)))
|
||||
return -ENOMEM;
|
||||
|
||||
phy_ring_tail = eth->phy_scratch_ring +
|
||||
(dma_addr_t)soc->txrx.txd_size * (cnt - 1);
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
struct mtk_tx_dma_v2 *txd;
|
||||
for (j = 0; j < DIV_ROUND_UP(soc->txrx.fq_dma_size, MTK_FQ_DMA_LENGTH); j++) {
|
||||
len = min_t(int, cnt - j * MTK_FQ_DMA_LENGTH, MTK_FQ_DMA_LENGTH);
|
||||
|
||||
txd = eth->scratch_ring + i * soc->txrx.txd_size;
|
||||
txd->txd1 = dma_addr + i * MTK_QDMA_PAGE_SIZE;
|
||||
if (i < cnt - 1)
|
||||
txd->txd2 = eth->phy_scratch_ring +
|
||||
(i + 1) * soc->txrx.txd_size;
|
||||
eth->scratch_head[j] = kcalloc(len, MTK_QDMA_PAGE_SIZE, GFP_KERNEL);
|
||||
if (unlikely(!eth->scratch_head[j]))
|
||||
return -ENOMEM;
|
||||
|
||||
addr64 = (MTK_HAS_CAPS(eth->soc->caps, MTK_8GB_ADDRESSING)) ?
|
||||
TX_DMA_SDP1(dma_addr + i * MTK_QDMA_PAGE_SIZE) : 0;
|
||||
dma_addr = dma_map_single(eth->dma_dev,
|
||||
eth->scratch_head[j], len * MTK_QDMA_PAGE_SIZE,
|
||||
DMA_FROM_DEVICE);
|
||||
if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr)))
|
||||
return -ENOMEM;
|
||||
|
||||
txd->txd3 = TX_DMA_PLEN0(MTK_QDMA_PAGE_SIZE) | addr64;
|
||||
txd->txd4 = 0;
|
||||
for (i = 0; i < len; i++) {
|
||||
struct mtk_tx_dma_v2 *txd;
|
||||
|
||||
if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) ||
|
||||
MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
|
||||
txd->txd5 = 0;
|
||||
txd->txd6 = 0;
|
||||
txd->txd7 = 0;
|
||||
txd->txd8 = 0;
|
||||
txd = eth->scratch_ring + (j * MTK_FQ_DMA_LENGTH + i) * soc->txrx.txd_size;
|
||||
txd->txd1 = dma_addr + i * MTK_QDMA_PAGE_SIZE;
|
||||
if (j * MTK_FQ_DMA_LENGTH + i < cnt)
|
||||
txd->txd2 = eth->phy_scratch_ring +
|
||||
(j * MTK_FQ_DMA_LENGTH + i + 1) * soc->txrx.txd_size;
|
||||
|
||||
addr64 = (MTK_HAS_CAPS(eth->soc->caps, MTK_8GB_ADDRESSING)) ?
|
||||
TX_DMA_SDP1(dma_addr + i * MTK_QDMA_PAGE_SIZE) : 0;
|
||||
|
||||
txd->txd3 = TX_DMA_PLEN0(MTK_QDMA_PAGE_SIZE) | addr64;
|
||||
txd->txd4 = 0;
|
||||
|
||||
if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) ||
|
||||
MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
|
||||
txd->txd5 = 0;
|
||||
txd->txd6 = 0;
|
||||
txd->txd7 = 0;
|
||||
txd->txd8 = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2779,25 +2806,25 @@ static int mtk_tx_alloc(struct mtk_eth *eth)
|
||||
int i, sz = soc->txrx.txd_size;
|
||||
struct mtk_tx_dma_v2 *txd, *pdma_txd;
|
||||
|
||||
ring->buf = kcalloc(MTK_DMA_SIZE, sizeof(*ring->buf),
|
||||
ring->buf = kcalloc(soc->txrx.tx_dma_size, sizeof(*ring->buf),
|
||||
GFP_KERNEL);
|
||||
if (!ring->buf)
|
||||
goto no_tx_mem;
|
||||
|
||||
if (!eth->soc->has_sram)
|
||||
ring->dma = dma_alloc_coherent(eth->dma_dev, MTK_DMA_SIZE * sz,
|
||||
ring->dma = dma_alloc_coherent(eth->dma_dev, soc->txrx.tx_dma_size * sz,
|
||||
&ring->phys, GFP_KERNEL);
|
||||
else {
|
||||
ring->dma = eth->sram_base + MTK_DMA_SIZE * sz;
|
||||
ring->dma = eth->sram_base + soc->txrx.fq_dma_size * sz;
|
||||
ring->phys = eth->phy_scratch_ring +
|
||||
MTK_DMA_SIZE * (dma_addr_t)sz;
|
||||
soc->txrx.fq_dma_size * (dma_addr_t)sz;
|
||||
}
|
||||
|
||||
if (!ring->dma)
|
||||
goto no_tx_mem;
|
||||
|
||||
for (i = 0; i < MTK_DMA_SIZE; i++) {
|
||||
int next = (i + 1) % MTK_DMA_SIZE;
|
||||
for (i = 0; i < soc->txrx.tx_dma_size; i++) {
|
||||
int next = (i + 1) % soc->txrx.tx_dma_size;
|
||||
u32 next_ptr = ring->phys + next * sz;
|
||||
|
||||
txd = ring->dma + i * sz;
|
||||
@@ -2820,12 +2847,12 @@ static int mtk_tx_alloc(struct mtk_eth *eth)
|
||||
*/
|
||||
if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
|
||||
ring->dma_pdma = dma_alloc_coherent(eth->dma_dev,
|
||||
MTK_DMA_SIZE * sz,
|
||||
soc->txrx.tx_dma_size * sz,
|
||||
&ring->phys_pdma, GFP_KERNEL);
|
||||
if (!ring->dma_pdma)
|
||||
goto no_tx_mem;
|
||||
|
||||
for (i = 0; i < MTK_DMA_SIZE; i++) {
|
||||
for (i = 0; i < soc->txrx.tx_dma_size; i++) {
|
||||
pdma_txd = ring->dma_pdma + i * sz;
|
||||
|
||||
pdma_txd->txd2 = TX_DMA_DESP2_DEF;
|
||||
@@ -2840,11 +2867,11 @@ static int mtk_tx_alloc(struct mtk_eth *eth)
|
||||
}
|
||||
}
|
||||
|
||||
ring->dma_size = MTK_DMA_SIZE;
|
||||
atomic_set(&ring->free_count, MTK_DMA_SIZE - 2);
|
||||
ring->dma_size = soc->txrx.tx_dma_size;
|
||||
atomic_set(&ring->free_count, soc->txrx.tx_dma_size - 2);
|
||||
ring->next_free = ring->dma;
|
||||
ring->last_free = (void *)txd;
|
||||
ring->last_free_ptr = (u32)(ring->phys + ((MTK_DMA_SIZE - 1) * sz));
|
||||
ring->last_free_ptr = (u32)(ring->phys + ((soc->txrx.tx_dma_size - 1) * sz));
|
||||
ring->thresh = MAX_SKB_FRAGS;
|
||||
ring->cpu_idx = 0;
|
||||
|
||||
@@ -2857,14 +2884,14 @@ static int mtk_tx_alloc(struct mtk_eth *eth)
|
||||
mtk_w32(eth, ring->phys, soc->reg_map->qdma.ctx_ptr);
|
||||
mtk_w32(eth, ring->phys, soc->reg_map->qdma.dtx_ptr);
|
||||
mtk_w32(eth,
|
||||
ring->phys + ((MTK_DMA_SIZE - 1) * sz),
|
||||
ring->phys + ((soc->txrx.tx_dma_size - 1) * sz),
|
||||
soc->reg_map->qdma.crx_ptr);
|
||||
mtk_w32(eth, ring->last_free_ptr, soc->reg_map->qdma.drx_ptr);
|
||||
mtk_w32(eth, (QDMA_RES_THRES << 8) | QDMA_RES_THRES,
|
||||
soc->reg_map->qdma.qtx_cfg);
|
||||
} else {
|
||||
mtk_w32(eth, ring->phys_pdma, soc->reg_map->pdma.tx_ptr);
|
||||
mtk_w32(eth, MTK_DMA_SIZE, soc->reg_map->pdma.tx_cnt_cfg);
|
||||
mtk_w32(eth, soc->txrx.tx_dma_size, soc->reg_map->pdma.tx_cnt_cfg);
|
||||
mtk_w32(eth, ring->cpu_idx, soc->reg_map->pdma.pctx_ptr);
|
||||
mtk_w32(eth, MTK_PST_DTX_IDX_CFG(0), soc->reg_map->pdma.rst_idx);
|
||||
}
|
||||
@@ -2882,7 +2909,7 @@ static void mtk_tx_clean(struct mtk_eth *eth)
|
||||
int i;
|
||||
|
||||
if (ring->buf) {
|
||||
for (i = 0; i < MTK_DMA_SIZE; i++)
|
||||
for (i = 0; i < soc->txrx.tx_dma_size; i++)
|
||||
mtk_tx_unmap(eth, &ring->buf[i], false);
|
||||
kfree(ring->buf);
|
||||
ring->buf = NULL;
|
||||
@@ -2890,14 +2917,14 @@ static void mtk_tx_clean(struct mtk_eth *eth)
|
||||
|
||||
if (!eth->soc->has_sram && ring->dma) {
|
||||
dma_free_coherent(eth->dma_dev,
|
||||
MTK_DMA_SIZE * soc->txrx.txd_size,
|
||||
soc->txrx.tx_dma_size * soc->txrx.txd_size,
|
||||
ring->dma, ring->phys);
|
||||
ring->dma = NULL;
|
||||
}
|
||||
|
||||
if (ring->dma_pdma) {
|
||||
dma_free_coherent(eth->dma_dev,
|
||||
MTK_DMA_SIZE * soc->txrx.txd_size,
|
||||
soc->txrx.tx_dma_size * soc->txrx.txd_size,
|
||||
ring->dma_pdma, ring->phys_pdma);
|
||||
ring->dma_pdma = NULL;
|
||||
}
|
||||
@@ -2906,6 +2933,7 @@ static void mtk_tx_clean(struct mtk_eth *eth)
|
||||
static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag)
|
||||
{
|
||||
const struct mtk_reg_map *reg_map = eth->soc->reg_map;
|
||||
const struct mtk_soc_data *soc = eth->soc;
|
||||
struct mtk_rx_ring *ring;
|
||||
int rx_data_len, rx_dma_size;
|
||||
int i;
|
||||
@@ -2924,7 +2952,7 @@ static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag)
|
||||
rx_dma_size = MTK_HW_LRO_DMA_SIZE;
|
||||
} else {
|
||||
rx_data_len = ETH_DATA_LEN;
|
||||
rx_dma_size = MTK_DMA_SIZE;
|
||||
rx_dma_size = soc->txrx.rx_dma_size;
|
||||
}
|
||||
|
||||
ring->frag_size = mtk_max_frag_size(rx_data_len);
|
||||
@@ -2950,10 +2978,12 @@ static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag)
|
||||
&ring->phys, GFP_KERNEL);
|
||||
else {
|
||||
struct mtk_tx_ring *tx_ring = ð->tx_ring;
|
||||
ring->dma = tx_ring->dma + MTK_DMA_SIZE *
|
||||
eth->soc->txrx.txd_size * (ring_no + 1);
|
||||
ring->phys = tx_ring->phys + MTK_DMA_SIZE *
|
||||
eth->soc->txrx.txd_size * (ring_no + 1);
|
||||
ring->dma = tx_ring->dma +
|
||||
soc->txrx.tx_dma_size * (dma_addr_t)eth->soc->txrx.txd_size +
|
||||
soc->txrx.rx_dma_size * (dma_addr_t)eth->soc->txrx.rxd_size * ring_no;
|
||||
ring->phys = tx_ring->phys +
|
||||
soc->txrx.tx_dma_size * (dma_addr_t)eth->soc->txrx.txd_size +
|
||||
soc->txrx.rx_dma_size * (dma_addr_t)eth->soc->txrx.rxd_size * ring_no;
|
||||
}
|
||||
|
||||
if (!ring->dma)
|
||||
@@ -3682,7 +3712,7 @@ static void mtk_dma_free(struct mtk_eth *eth)
|
||||
netdev_reset_queue(eth->netdev[i]);
|
||||
if ( !eth->soc->has_sram && eth->scratch_ring) {
|
||||
dma_free_coherent(eth->dma_dev,
|
||||
MTK_DMA_SIZE * soc->txrx.txd_size,
|
||||
soc->txrx.fq_dma_size * soc->txrx.txd_size,
|
||||
eth->scratch_ring, eth->phy_scratch_ring);
|
||||
eth->scratch_ring = NULL;
|
||||
eth->phy_scratch_ring = 0;
|
||||
@@ -3705,9 +3735,9 @@ static void mtk_dma_free(struct mtk_eth *eth)
|
||||
mtk_rx_clean(eth, ð->rx_ring[MTK_RSS_RING(i)], 1);
|
||||
}
|
||||
|
||||
if (eth->scratch_head) {
|
||||
kfree(eth->scratch_head);
|
||||
eth->scratch_head = NULL;
|
||||
for (i = 0; i < DIV_ROUND_UP(soc->txrx.fq_dma_size, MTK_FQ_DMA_LENGTH); i++) {
|
||||
kfree(eth->scratch_head[i]);
|
||||
eth->scratch_head[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3838,8 +3868,16 @@ static int mtk_start_dma(struct mtk_eth *eth)
|
||||
|
||||
if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
|
||||
val = mtk_r32(eth, reg_map->qdma.glo_cfg);
|
||||
if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) ||
|
||||
MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
|
||||
if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) {
|
||||
val &= ~(MTK_RESV_BUF_MASK | MTK_DMA_SIZE_MASK);
|
||||
mtk_w32(eth,
|
||||
val | MTK_TX_DMA_EN | MTK_RX_DMA_EN |
|
||||
MTK_DMA_SIZE_16DWORDS | MTK_TX_WB_DDONE |
|
||||
MTK_NDP_CO_PRO | MTK_MUTLI_CNT |
|
||||
MTK_RESV_BUF | MTK_WCOMP_EN |
|
||||
MTK_DMAD_WR_WDONE | MTK_CHK_DDONE_EN |
|
||||
MTK_RX_2B_OFFSET, reg_map->qdma.glo_cfg);
|
||||
} else if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
|
||||
val &= ~MTK_RESV_BUF_MASK;
|
||||
mtk_w32(eth,
|
||||
val | MTK_TX_DMA_EN | MTK_RX_DMA_EN |
|
||||
@@ -3848,8 +3886,7 @@ static int mtk_start_dma(struct mtk_eth *eth)
|
||||
MTK_RESV_BUF | MTK_WCOMP_EN |
|
||||
MTK_DMAD_WR_WDONE | MTK_CHK_DDONE_EN |
|
||||
MTK_RX_2B_OFFSET, reg_map->qdma.glo_cfg);
|
||||
}
|
||||
else
|
||||
} else
|
||||
mtk_w32(eth,
|
||||
val | MTK_TX_DMA_EN |
|
||||
MTK_DMA_SIZE_32DWORDS | MTK_NDP_CO_PRO |
|
||||
@@ -3958,9 +3995,6 @@ set_speed:
|
||||
if (s.base.speed == 0 || s.base.speed == ((__u32)-1))
|
||||
return NOTIFY_DONE;
|
||||
|
||||
if (queue >= MTK_QDMA_TX_NUM)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
if (mac->speed > 0 && mac->speed < s.base.speed)
|
||||
s.base.speed = 0;
|
||||
|
||||
@@ -4383,6 +4417,7 @@ static int mtk_hw_init(struct mtk_eth *eth, u32 type)
|
||||
mtk_w32(eth, 0x00600009, PSE_IQ_REV(8));
|
||||
|
||||
/* GDM and CDM Threshold */
|
||||
mtk_w32(eth, 0x00000004, MTK_CDM2_THRES);
|
||||
mtk_w32(eth, 0x00000707, MTK_CDMW0_THRES);
|
||||
mtk_w32(eth, 0x00000077, MTK_CDMW1_THRES);
|
||||
|
||||
@@ -5439,6 +5474,8 @@ static int mtk_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
eth->soc = of_device_get_match_data(&pdev->dev);
|
||||
if (!eth->soc)
|
||||
return -EINVAL;
|
||||
|
||||
eth->dev = &pdev->dev;
|
||||
eth->dma_dev = &pdev->dev;
|
||||
@@ -5812,6 +5849,9 @@ static const struct mtk_soc_data mt2701_data = {
|
||||
.txrx = {
|
||||
.txd_size = sizeof(struct mtk_tx_dma),
|
||||
.rxd_size = sizeof(struct mtk_rx_dma),
|
||||
.tx_dma_size = MTK_DMA_SIZE(2K),
|
||||
.rx_dma_size = MTK_DMA_SIZE(2K),
|
||||
.fq_dma_size = MTK_DMA_SIZE(2K),
|
||||
.rx_dma_l4_valid = RX_DMA_L4_VALID,
|
||||
.dma_max_len = MTK_TX_DMA_BUF_LEN,
|
||||
.dma_len_offset = MTK_TX_DMA_BUF_SHIFT,
|
||||
@@ -5829,6 +5869,9 @@ static const struct mtk_soc_data mt7621_data = {
|
||||
.txrx = {
|
||||
.txd_size = sizeof(struct mtk_tx_dma),
|
||||
.rx_dma_l4_valid = RX_DMA_L4_VALID,
|
||||
.tx_dma_size = MTK_DMA_SIZE(2K),
|
||||
.rx_dma_size = MTK_DMA_SIZE(2K),
|
||||
.fq_dma_size = MTK_DMA_SIZE(2K),
|
||||
.rxd_size = sizeof(struct mtk_rx_dma),
|
||||
.dma_max_len = MTK_TX_DMA_BUF_LEN,
|
||||
.dma_len_offset = MTK_TX_DMA_BUF_SHIFT,
|
||||
@@ -5847,6 +5890,9 @@ static const struct mtk_soc_data mt7622_data = {
|
||||
.txrx = {
|
||||
.txd_size = sizeof(struct mtk_tx_dma),
|
||||
.rxd_size = sizeof(struct mtk_rx_dma),
|
||||
.tx_dma_size = MTK_DMA_SIZE(2K),
|
||||
.rx_dma_size = MTK_DMA_SIZE(2K),
|
||||
.fq_dma_size = MTK_DMA_SIZE(2K),
|
||||
.rx_dma_l4_valid = RX_DMA_L4_VALID,
|
||||
.dma_max_len = MTK_TX_DMA_BUF_LEN,
|
||||
.dma_len_offset = MTK_TX_DMA_BUF_SHIFT,
|
||||
@@ -5864,6 +5910,9 @@ static const struct mtk_soc_data mt7623_data = {
|
||||
.txrx = {
|
||||
.txd_size = sizeof(struct mtk_tx_dma),
|
||||
.rxd_size = sizeof(struct mtk_rx_dma),
|
||||
.tx_dma_size = MTK_DMA_SIZE(2K),
|
||||
.rx_dma_size = MTK_DMA_SIZE(2K),
|
||||
.fq_dma_size = MTK_DMA_SIZE(2K),
|
||||
.rx_dma_l4_valid = RX_DMA_L4_VALID,
|
||||
.dma_max_len = MTK_TX_DMA_BUF_LEN,
|
||||
.dma_len_offset = MTK_TX_DMA_BUF_SHIFT,
|
||||
@@ -5882,6 +5931,9 @@ static const struct mtk_soc_data mt7629_data = {
|
||||
.txrx = {
|
||||
.txd_size = sizeof(struct mtk_tx_dma),
|
||||
.rxd_size = sizeof(struct mtk_rx_dma),
|
||||
.tx_dma_size = MTK_DMA_SIZE(2K),
|
||||
.rx_dma_size = MTK_DMA_SIZE(2K),
|
||||
.fq_dma_size = MTK_DMA_SIZE(2K),
|
||||
.rx_dma_l4_valid = RX_DMA_L4_VALID,
|
||||
.dma_max_len = MTK_TX_DMA_BUF_LEN,
|
||||
.dma_len_offset = MTK_TX_DMA_BUF_SHIFT,
|
||||
@@ -5900,6 +5952,9 @@ static const struct mtk_soc_data mt7986_data = {
|
||||
.txrx = {
|
||||
.txd_size = sizeof(struct mtk_tx_dma_v2),
|
||||
.rxd_size = sizeof(struct mtk_rx_dma),
|
||||
.tx_dma_size = MTK_DMA_SIZE(4K),
|
||||
.rx_dma_size = MTK_DMA_SIZE(1K),
|
||||
.fq_dma_size = MTK_DMA_SIZE(2K),
|
||||
.rx_dma_l4_valid = RX_DMA_L4_VALID_V2,
|
||||
.dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
|
||||
.dma_len_offset = MTK_TX_DMA_BUF_SHIFT_V2,
|
||||
@@ -5918,6 +5973,9 @@ static const struct mtk_soc_data mt7981_data = {
|
||||
.txrx = {
|
||||
.txd_size = sizeof(struct mtk_tx_dma_v2),
|
||||
.rxd_size = sizeof(struct mtk_rx_dma),
|
||||
.tx_dma_size = MTK_DMA_SIZE(4K),
|
||||
.rx_dma_size = MTK_DMA_SIZE(1K),
|
||||
.fq_dma_size = MTK_DMA_SIZE(2K),
|
||||
.rx_dma_l4_valid = RX_DMA_L4_VALID_V2,
|
||||
.dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
|
||||
.dma_len_offset = MTK_TX_DMA_BUF_SHIFT_V2,
|
||||
@@ -5936,6 +5994,9 @@ static const struct mtk_soc_data mt7988_data = {
|
||||
.txrx = {
|
||||
.txd_size = sizeof(struct mtk_tx_dma_v2),
|
||||
.rxd_size = sizeof(struct mtk_rx_dma_v2),
|
||||
.tx_dma_size = MTK_DMA_SIZE(4K),
|
||||
.rx_dma_size = MTK_DMA_SIZE(1K),
|
||||
.fq_dma_size = MTK_DMA_SIZE(4K),
|
||||
.rx_dma_l4_valid = RX_DMA_L4_VALID_V2,
|
||||
.dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
|
||||
.dma_len_offset = MTK_TX_DMA_BUF_SHIFT_V2,
|
||||
@@ -5953,6 +6014,9 @@ static const struct mtk_soc_data rt5350_data = {
|
||||
.txrx = {
|
||||
.txd_size = sizeof(struct mtk_tx_dma),
|
||||
.rxd_size = sizeof(struct mtk_rx_dma),
|
||||
.tx_dma_size = MTK_DMA_SIZE(2K),
|
||||
.rx_dma_size = MTK_DMA_SIZE(2K),
|
||||
.fq_dma_size = MTK_DMA_SIZE(2K),
|
||||
.rx_dma_l4_valid = RX_DMA_L4_VALID_PDMA,
|
||||
.dma_max_len = MTK_TX_DMA_BUF_LEN,
|
||||
.dma_len_offset = MTK_TX_DMA_BUF_SHIFT,
|
||||
|
||||
@@ -19,7 +19,9 @@
|
||||
#define MTK_QDMA_PAGE_SIZE 2048
|
||||
#define MTK_MAX_RX_LENGTH 1536
|
||||
#define MTK_MIN_TX_LENGTH 60
|
||||
#define MTK_DMA_SIZE 2048
|
||||
#define MTK_DMA_SIZE(x) (SZ_##x)
|
||||
#define MTK_FQ_DMA_HEAD 32
|
||||
#define MTK_FQ_DMA_LENGTH 2048
|
||||
#define MTK_NAPI_WEIGHT 256
|
||||
|
||||
#if defined(CONFIG_MEDIATEK_NETSYS_V3)
|
||||
@@ -175,6 +177,7 @@
|
||||
|
||||
/* GDM and CDM Threshold */
|
||||
#define MTK_GDM2_THRES 0x1530
|
||||
#define MTK_CDM2_THRES 0x1534
|
||||
#define MTK_CDMW0_THRES 0x164c
|
||||
#define MTK_CDMW1_THRES 0x1650
|
||||
#define MTK_CDME0_THRES 0x1654
|
||||
@@ -453,6 +456,7 @@
|
||||
#define MTK_RX_BT_32DWORDS (3 << 11)
|
||||
#define MTK_NDP_CO_PRO BIT(10)
|
||||
#define MTK_TX_WB_DDONE BIT(6)
|
||||
#define MTK_DMA_SIZE_MASK GENMASK(5, 4)
|
||||
#define MTK_DMA_SIZE_16DWORDS (2 << 4)
|
||||
#define MTK_DMA_SIZE_32DWORDS (3 << 4)
|
||||
#define MTK_RX_DMA_BUSY BIT(3)
|
||||
@@ -745,6 +749,7 @@
|
||||
#define MAC_MCR_FORCE_MODE BIT(15)
|
||||
#define MAC_MCR_TX_EN BIT(14)
|
||||
#define MAC_MCR_RX_EN BIT(13)
|
||||
#define MAC_MCR_PRMBL_LMT_EN BIT(10)
|
||||
#define MAC_MCR_BACKOFF_EN BIT(9)
|
||||
#define MAC_MCR_BACKPR_EN BIT(8)
|
||||
#define MAC_MCR_FORCE_EEE1000 BIT(7)
|
||||
@@ -1718,6 +1723,9 @@ struct mtk_soc_data {
|
||||
struct {
|
||||
u32 txd_size;
|
||||
u32 rxd_size;
|
||||
u32 tx_dma_size;
|
||||
u32 rx_dma_size;
|
||||
u32 fq_dma_size;
|
||||
u32 rx_dma_l4_valid;
|
||||
u32 dma_max_len;
|
||||
u32 dma_len_offset;
|
||||
@@ -1900,7 +1908,7 @@ struct mtk_eth {
|
||||
void *scratch_ring;
|
||||
struct mtk_reset_event reset_event;
|
||||
dma_addr_t phy_scratch_ring;
|
||||
void *scratch_head;
|
||||
void *scratch_head[MTK_FQ_DMA_HEAD];
|
||||
struct clk *clks[MTK_CLK_MAX];
|
||||
|
||||
struct mii_bus *mii_bus;
|
||||
@@ -1991,7 +1999,6 @@ int mtk_mac2xgmii_id(struct mtk_eth *eth, int mac_id);
|
||||
struct phylink_pcs *mtk_usxgmii_select_pcs(struct mtk_usxgmii *ss, int id);
|
||||
int mtk_usxgmii_init(struct mtk_eth *eth, struct device_node *r);
|
||||
int mtk_toprgu_init(struct mtk_eth *eth, struct device_node *r);
|
||||
int mtk_dump_usxgmii(struct regmap *pmap, char *name, u32 offset, u32 range);
|
||||
void mtk_usxgmii_link_poll(struct work_struct *work);
|
||||
|
||||
void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev);
|
||||
|
||||
@@ -1092,8 +1092,13 @@ enum FoeIpAct {
|
||||
#define entry_hnat_is_bound(e) (e->bfib1.state == BIND)
|
||||
#define entry_hnat_state(e) (e->bfib1.state)
|
||||
|
||||
#define skb_hnat_is_hashed(skb) \
|
||||
#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3)
|
||||
#define skb_hnat_is_hashed(skb) \
|
||||
(skb_hnat_entry(skb) != 0x7fff && skb_hnat_entry(skb) < hnat_priv->foe_etry_num)
|
||||
#else
|
||||
#define skb_hnat_is_hashed(skb) \
|
||||
(skb_hnat_entry(skb) != 0x3fff && skb_hnat_entry(skb) < hnat_priv->foe_etry_num)
|
||||
#endif
|
||||
#define FROM_GE_LAN_GRP(skb) (FROM_GE_LAN(skb) | FROM_GE_LAN2(skb))
|
||||
#define FROM_GE_LAN(skb) (skb_hnat_iface(skb) == FOE_MAGIC_GE_LAN)
|
||||
#define FROM_GE_LAN2(skb) (skb_hnat_iface(skb) == FOE_MAGIC_GE_LAN2)
|
||||
|
||||
@@ -608,6 +608,15 @@ int entry_detail(u32 ppe_id, int index)
|
||||
entry->ipv4_hnapt.tport_id,
|
||||
entry->ipv4_hnapt.tops_entry,
|
||||
entry->ipv4_hnapt.cdrt_id);
|
||||
pr_info("usr_info = %d, tid = %d, hf = %d, amsdu = %d\n",
|
||||
entry->ipv4_hnapt.winfo_pao.usr_info,
|
||||
entry->ipv4_hnapt.winfo_pao.tid,
|
||||
entry->ipv4_hnapt.winfo_pao.hf,
|
||||
entry->ipv4_hnapt.winfo_pao.amsdu);
|
||||
pr_info("is_fixedrate = %d, is_prior = %d, is_sp = %d\n",
|
||||
entry->ipv4_hnapt.winfo_pao.is_fixedrate,
|
||||
entry->ipv4_hnapt.winfo_pao.is_prior,
|
||||
entry->ipv4_hnapt.winfo_pao.is_sp);
|
||||
#endif
|
||||
pr_info("=========================================\n\n");
|
||||
} else {
|
||||
@@ -638,17 +647,43 @@ int entry_detail(u32 ppe_id, int index)
|
||||
entry->ipv6_hnapt.tport_id,
|
||||
entry->ipv6_hnapt.tops_entry,
|
||||
entry->ipv6_hnapt.cdrt_id);
|
||||
|
||||
pr_info("usr_info = %d, tid = %d, hf = %d, amsdu = %d\n",
|
||||
entry->ipv6_hnapt.winfo_pao.usr_info,
|
||||
entry->ipv6_hnapt.winfo_pao.tid,
|
||||
entry->ipv6_hnapt.winfo_pao.hf,
|
||||
entry->ipv6_hnapt.winfo_pao.amsdu);
|
||||
pr_info("is_fixedrate = %d, is_prior = %d, is_sp = %d\n",
|
||||
entry->ipv6_hnapt.winfo_pao.is_fixedrate,
|
||||
entry->ipv6_hnapt.winfo_pao.is_prior,
|
||||
entry->ipv6_hnapt.winfo_pao.is_sp);
|
||||
} else if (IS_IPV4_MAPE(entry) || IS_IPV4_MAPT(entry)) {
|
||||
pr_info("tport_id = %d, tops_entry = %d, cdrt_id = %d\n",
|
||||
entry->ipv4_mape.tport_id,
|
||||
entry->ipv4_mape.tops_entry,
|
||||
entry->ipv4_mape.cdrt_id);
|
||||
pr_info("usr_info = %d, tid = %d, hf = %d, amsdu = %d\n",
|
||||
entry->ipv4_mape.winfo_pao.usr_info,
|
||||
entry->ipv4_mape.winfo_pao.tid,
|
||||
entry->ipv4_mape.winfo_pao.hf,
|
||||
entry->ipv4_mape.winfo_pao.amsdu);
|
||||
pr_info("is_fixedrate = %d, is_prior = %d, is_sp = %d\n",
|
||||
entry->ipv4_mape.winfo_pao.is_fixedrate,
|
||||
entry->ipv4_mape.winfo_pao.is_prior,
|
||||
entry->ipv4_mape.winfo_pao.is_sp);
|
||||
} else {
|
||||
pr_info("tport_id = %d, tops_entry = %d, cdrt_id = %d\n",
|
||||
entry->ipv6_5t_route.tport_id,
|
||||
entry->ipv6_5t_route.tops_entry,
|
||||
entry->ipv6_5t_route.cdrt_id);
|
||||
pr_info("usr_info = %d, tid = %d, hf = %d, amsdu = %d\n",
|
||||
entry->ipv6_5t_route.winfo_pao.usr_info,
|
||||
entry->ipv6_5t_route.winfo_pao.tid,
|
||||
entry->ipv6_5t_route.winfo_pao.hf,
|
||||
entry->ipv6_5t_route.winfo_pao.amsdu);
|
||||
pr_info("is_fixedrate = %d, is_prior = %d, is_sp = %d\n",
|
||||
entry->ipv6_5t_route.winfo_pao.is_fixedrate,
|
||||
entry->ipv6_5t_route.winfo_pao.is_prior,
|
||||
entry->ipv6_5t_route.winfo_pao.is_sp);
|
||||
}
|
||||
#endif
|
||||
pr_info("=========================================\n\n");
|
||||
@@ -2805,7 +2840,7 @@ static ssize_t hnat_qos_toggle_write(struct file *file, const char __user *buffe
|
||||
qos_toggle = 1;
|
||||
} else if (buf[0] == '2') {
|
||||
pr_info("Per-port-per-queue mode is going to be enabled!\n");
|
||||
pr_info("PPPQ use qid 0~5 (scheduler 0).\n");
|
||||
pr_info("PPPQ use qid 0~11 (scheduler 0).\n");
|
||||
qos_toggle = 2;
|
||||
qos_dl_toggle = 1;
|
||||
qos_ul_toggle = 1;
|
||||
|
||||
@@ -832,8 +832,7 @@ static unsigned int
|
||||
mtk_hnat_ipv4_nf_pre_routing(void *priv, struct sk_buff *skb,
|
||||
const struct nf_hook_state *state)
|
||||
{
|
||||
struct flow_offload_hw_path hw_path = { .dev = skb->dev,
|
||||
.virt_dev = skb->dev };
|
||||
struct flow_offload_hw_path hw_path;
|
||||
|
||||
if (!skb)
|
||||
goto drop;
|
||||
@@ -848,6 +847,9 @@ mtk_hnat_ipv4_nf_pre_routing(void *priv, struct sk_buff *skb,
|
||||
|
||||
hnat_set_head_frags(state, skb, -1, hnat_set_iif);
|
||||
|
||||
hw_path.dev = skb->dev;
|
||||
hw_path.virt_dev = skb->dev;
|
||||
|
||||
/*
|
||||
* Avoid mistakenly binding of outer IP, ports in SW L2TP decap flow.
|
||||
* In pre-routing, if dev is virtual iface, TOPS module is not loaded,
|
||||
@@ -1221,6 +1223,7 @@ static unsigned int skb_to_hnat_info(struct sk_buff *skb,
|
||||
int udp = 0;
|
||||
u32 qid = 0;
|
||||
u32 port_id = 0;
|
||||
u32 payload_len = 0;
|
||||
int mape = 0;
|
||||
struct mtk_mac *mac = netdev_priv(dev);
|
||||
|
||||
@@ -1757,6 +1760,29 @@ static unsigned int skb_to_hnat_info(struct sk_buff *skb,
|
||||
else
|
||||
qid = 0;
|
||||
|
||||
if (IS_PPPQ_MODE && IS_PPPQ_PATH(dev, skb)) {
|
||||
if (ntohs(eth->h_proto) == ETH_P_IP) {
|
||||
iph = ip_hdr(skb);
|
||||
if (iph->protocol == IPPROTO_TCP) {
|
||||
skb_set_transport_header(skb, sizeof(struct iphdr));
|
||||
payload_len = be16_to_cpu(iph->tot_len) -
|
||||
skb_transport_offset(skb) - tcp_hdrlen(skb);
|
||||
/* Dispatch ACK packets to high priority queue */
|
||||
if (payload_len == 0)
|
||||
qid += 6;
|
||||
}
|
||||
} else if (ntohs(eth->h_proto) == ETH_P_IPV6) {
|
||||
ip6h = ipv6_hdr(skb);
|
||||
if (ip6h->nexthdr == NEXTHDR_TCP) {
|
||||
skb_set_transport_header(skb, sizeof(struct ipv6hdr));
|
||||
payload_len = be16_to_cpu(ip6h->payload_len) - tcp_hdrlen(skb);
|
||||
/* Dispatch ACK packets to high priority queue */
|
||||
if (payload_len == 0)
|
||||
qid += 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_IPV4_GRP(foe)) {
|
||||
entry.ipv4_hnapt.iblk2.dp = gmac;
|
||||
entry.ipv4_hnapt.iblk2.port_mg =
|
||||
|
||||
@@ -227,6 +227,8 @@ void mtk_sgmii_setup_phya_gen1(struct mtk_sgmii_pcs *mpcs)
|
||||
regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
|
||||
0x02002800);
|
||||
ndelay(1020);
|
||||
regmap_update_bits(mpcs->regmap_pextp, 0x3040, GENMASK(31, 0),
|
||||
0x20000000);
|
||||
/* Setup DA default value */
|
||||
regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0),
|
||||
0x00000020);
|
||||
@@ -337,6 +339,8 @@ void mtk_sgmii_setup_phya_gen2(struct mtk_sgmii_pcs *mpcs)
|
||||
regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
|
||||
0x02002800);
|
||||
ndelay(1020);
|
||||
regmap_update_bits(mpcs->regmap_pextp, 0x3040, GENMASK(31, 0),
|
||||
0x20000000);
|
||||
/* Setup DA default value */
|
||||
regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0),
|
||||
0x00000020);
|
||||
@@ -583,7 +587,7 @@ static void mtk_sgmii_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
|
||||
if (mtk_sgmii_link_status(mpcs))
|
||||
goto exit;
|
||||
|
||||
if (mode == MLO_AN_PHY)
|
||||
if (mode != MLO_AN_INBAND)
|
||||
mtk_sgmii_pcs_config(&mpcs->pcs, mode,
|
||||
interface, mpcs->advertising, false);
|
||||
} while (time_before(jiffies, t_start + msecs_to_jiffies(3000)));
|
||||
|
||||
@@ -187,6 +187,8 @@ void mtk_usxgmii_setup_phya_usxgmii(struct mtk_usxgmii_pcs *mpcs)
|
||||
regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
|
||||
0x02002800);
|
||||
ndelay(1020);
|
||||
regmap_update_bits(mpcs->regmap_pextp, 0x3040, GENMASK(31, 0),
|
||||
0x20000000);
|
||||
/* Setup DA default value */
|
||||
regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0),
|
||||
0x00000020);
|
||||
@@ -299,6 +301,8 @@ void mtk_usxgmii_setup_phya_5gbaser(struct mtk_usxgmii_pcs *mpcs)
|
||||
regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
|
||||
0x02002800);
|
||||
ndelay(1020);
|
||||
regmap_update_bits(mpcs->regmap_pextp, 0x3040, GENMASK(31, 0),
|
||||
0x20000000);
|
||||
/* Setup DA default value */
|
||||
regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0),
|
||||
0x00000020);
|
||||
@@ -411,6 +415,8 @@ void mtk_usxgmii_setup_phya_10gbaser(struct mtk_usxgmii_pcs *mpcs)
|
||||
regmap_update_bits(mpcs->regmap_pextp, 0x0070, GENMASK(31, 0),
|
||||
0x02002800);
|
||||
ndelay(1020);
|
||||
regmap_update_bits(mpcs->regmap_pextp, 0x3040, GENMASK(31, 0),
|
||||
0x20000000);
|
||||
/* Setup DA default value */
|
||||
regmap_update_bits(mpcs->regmap_pextp, 0x30B0, GENMASK(31, 0),
|
||||
0x00000020);
|
||||
@@ -763,6 +769,7 @@ static void mtk_usxgmii_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
|
||||
{
|
||||
struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs);
|
||||
unsigned long t_start = jiffies;
|
||||
unsigned int mpcs_mode;
|
||||
|
||||
/* Reconfiguring USXGMII to ensure the quality of the RX signal
|
||||
* after the line side link up.
|
||||
@@ -776,7 +783,11 @@ static void mtk_usxgmii_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
|
||||
if (mtk_usxgmii_link_status(mpcs))
|
||||
return;
|
||||
|
||||
if (mpcs->mode == MLO_AN_PHY)
|
||||
spin_lock(&mpcs->regmap_lock);
|
||||
mpcs_mode = mpcs->mode;
|
||||
spin_unlock(&mpcs->regmap_lock);
|
||||
|
||||
if (mpcs_mode != MLO_AN_INBAND)
|
||||
mtk_usxgmii_pcs_config(&mpcs->pcs, mode,
|
||||
interface, NULL, false);
|
||||
} while (time_before(jiffies, t_start + msecs_to_jiffies(3000)));
|
||||
@@ -836,23 +847,3 @@ struct phylink_pcs *mtk_usxgmii_select_pcs(struct mtk_usxgmii *ss, int id)
|
||||
|
||||
return &ss->pcs[id].pcs;
|
||||
}
|
||||
|
||||
int mtk_dump_usxgmii(struct regmap *pmap, char *name, u32 offset, u32 range)
|
||||
{
|
||||
unsigned int cur = offset;
|
||||
unsigned int val1 = 0, val2 = 0, val3 = 0, val4 = 0;
|
||||
|
||||
pr_info("\n============ %s ============ pmap:%lx\n",
|
||||
name, (unsigned long)pmap);
|
||||
while (cur < offset + range) {
|
||||
regmap_read(pmap, cur, &val1);
|
||||
regmap_read(pmap, cur + 0x4, &val2);
|
||||
regmap_read(pmap, cur + 0x8, &val3);
|
||||
regmap_read(pmap, cur + 0xc, &val4);
|
||||
pr_info("0x%x: %08x %08x %08x %08x\n", cur,
|
||||
val1, val2, val3, val4);
|
||||
cur += 0x10;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
#define MII_MMD_ADDR_DATA_REG 0x0e
|
||||
#define MMD_OP_MODE_DATA BIT(14)
|
||||
|
||||
#define EN8811H_DRIVER_VERSION "v1.2.4"
|
||||
#define EN8811H_DRIVER_VERSION "v1.2.5"
|
||||
|
||||
#define LED_ON_CTRL(i) (0x024 + ((i)*2))
|
||||
#define LED_ON_EN (1 << 15)
|
||||
@@ -99,6 +99,7 @@ struct en8811h_priv {
|
||||
unsigned int dsp_crc32;
|
||||
char buf[512];
|
||||
int pol;
|
||||
int surge;
|
||||
};
|
||||
|
||||
struct air_base_t_led_cfg {
|
||||
|
||||
@@ -334,6 +334,22 @@ int air_buckpbus_reg_write(struct phy_device *phydev,
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int air_surge_5ohm_config(struct phy_device *phydev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct device *dev = phydev_dev(phydev);
|
||||
|
||||
ret |= air_mii_cl45_write(phydev, 0x1e, 0x800c, 0x0);
|
||||
ret |= air_mii_cl45_write(phydev, 0x1e, 0x800d, 0x0);
|
||||
ret |= air_mii_cl45_write(phydev, 0x1e, 0x800e, 0x1101);
|
||||
ret |= air_mii_cl45_write(phydev, 0x1e, 0x800f, 0x00b0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
dev_info(dev, "surge protection mode - 5R\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
int en8811h_of_init(struct phy_device *phydev)
|
||||
{
|
||||
@@ -346,12 +362,12 @@ int en8811h_of_init(struct phy_device *phydev)
|
||||
if (of_find_property(of_node, "airoha,polarity", NULL)) {
|
||||
if (of_property_read_u32(of_node, "airoha,polarity",
|
||||
&val) != 0) {
|
||||
phydev_err(phydev, "airoha,polarity value is invalid.");
|
||||
dev_err(dev, "airoha,polarity value is invalid.");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (val < AIR_POL_TX_REV_RX_NOR ||
|
||||
val > AIR_POL_TX_NOR_RX_REV) {
|
||||
phydev_err(phydev,
|
||||
dev_err(dev,
|
||||
"airoha,polarity value %u out of range.",
|
||||
val);
|
||||
return -EINVAL;
|
||||
@@ -360,6 +376,25 @@ int en8811h_of_init(struct phy_device *phydev)
|
||||
} else
|
||||
priv->pol = AIR_POL_TX_NOR_RX_NOR;
|
||||
|
||||
if (of_find_property(of_node, "airoha,surge", NULL)) {
|
||||
if (of_property_read_u32(of_node, "airoha,surge",
|
||||
&val) != 0) {
|
||||
dev_err(dev, "airoha,surge value is invalid.");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (val < 0 || val > 1) {
|
||||
dev_err(dev,
|
||||
"airoha,surge value %u out of range.",
|
||||
val);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (val)
|
||||
priv->surge = 1;
|
||||
else
|
||||
priv->surge = 0;
|
||||
} else
|
||||
priv->surge = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
@@ -710,7 +745,7 @@ static int airphy_fcm_counter_show(struct phy_device *phydev,
|
||||
seq_puts(seq, "| Tx to Line side_T :");
|
||||
pkt_cnt = air_buckpbus_reg_read(phydev, 0xe0088);
|
||||
seq_printf(seq, "%010u |\n", pkt_cnt);
|
||||
ret = air_buckpbus_reg_write(phydev, 0xe0074, 0xf);
|
||||
ret = air_buckpbus_reg_write(phydev, 0xe0074, 0x3);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return 0;
|
||||
@@ -1169,8 +1204,12 @@ static int airphy_temp_show(struct seq_file *seq, void *v)
|
||||
u32 pbus_value = 0;
|
||||
|
||||
seq_puts(seq, "<<AIR EN8811H Temp>>\n");
|
||||
air_mii_cl45_write(phydev, 0x1e, 0x800e, 0x1100);
|
||||
air_mii_cl45_write(phydev, 0x1e, 0x800f, 0xe5);
|
||||
ret |= air_mii_cl45_write(phydev, 0x1e, 0x800e, 0x1100);
|
||||
ret |= air_mii_cl45_write(phydev, 0x1e, 0x800f, 0xe5);
|
||||
if (ret < 0) {
|
||||
pr_notice("\nmii_cl45_write fail\n");
|
||||
return -EIO;
|
||||
}
|
||||
pbus_value = air_buckpbus_reg_read(phydev, 0x3B38);
|
||||
seq_printf(seq, "| Temperature : %dC |\n",
|
||||
pbus_value);
|
||||
@@ -1285,6 +1324,114 @@ static int airphy_lp_speed_open(struct inode *inode, struct file *file)
|
||||
return single_open(file, airphy_lp_speed, inode->i_private);
|
||||
}
|
||||
|
||||
static void airphy_debugfs_mii_cl22_help(void)
|
||||
{
|
||||
pr_notice("\nUsage:\n"
|
||||
"[debugfs] = /sys/kernel/debug/mdio-bus\':[phy_addr]\n"
|
||||
"Read:\n"
|
||||
"echo r [phy_register] > /[debugfs]/mii_mgr_op\n"
|
||||
"Write:\n"
|
||||
"echo w [phy_register] [value] > /[debugfs]/mii_mgr_op\n");
|
||||
}
|
||||
|
||||
|
||||
static ssize_t airphy_debugfs_cl22(struct file *file,
|
||||
const char __user *buffer, size_t count,
|
||||
loff_t *data)
|
||||
{
|
||||
struct phy_device *phydev = file->private_data;
|
||||
struct mii_bus *mbus = phydev_mdio_bus(phydev);
|
||||
int addr = phydev_addr(phydev);
|
||||
char buf[64];
|
||||
int ret = 0;
|
||||
unsigned int reg, val;
|
||||
|
||||
memset(buf, 0, 64);
|
||||
if (count > sizeof(buf) - 1)
|
||||
return -EINVAL;
|
||||
if (copy_from_user(buf, buffer, count))
|
||||
return -EFAULT;
|
||||
|
||||
if (buf[0] == 'w') {
|
||||
if (sscanf(buf, "w %x %x", ®, &val) == -1)
|
||||
return -EFAULT;
|
||||
|
||||
pr_notice("\nphy=%d, reg=0x%x, val=0x%x\n",
|
||||
phydev_addr(phydev), reg, val);
|
||||
ret = air_mii_cl22_write(mbus, addr, reg, val);
|
||||
if (ret < 0) {
|
||||
pr_notice("\nmii_cl22_write fail\n");
|
||||
return -EIO;
|
||||
}
|
||||
val = air_mii_cl22_read(mbus, addr, reg);
|
||||
pr_notice("\nphy=%d, reg=0x%x, val=0x%x confirm..\n",
|
||||
phydev_addr(phydev), reg, val);
|
||||
} else if (buf[0] == 'r') {
|
||||
if (sscanf(buf, "r %x", ®) == -1)
|
||||
return -EFAULT;
|
||||
|
||||
val = air_mii_cl22_read(mbus, addr, reg);
|
||||
pr_notice("\nphy=%d, reg=0x%x, val=0x%x\n",
|
||||
phydev_addr(phydev), reg, val);
|
||||
} else
|
||||
airphy_debugfs_mii_cl22_help();
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static void airphy_debugfs_mii_cl45_help(void)
|
||||
{
|
||||
pr_notice("\nUsage:\n"
|
||||
"[debugfs] = /sys/kernel/debug/mdio-bus\':[phy_addr]\n"
|
||||
"Read:\n"
|
||||
"echo r [device number] [phy_register] > /[debugfs]/mii_mgr_cl45_op\n"
|
||||
"Write:\n"
|
||||
"echo w [device number] [phy_register] [value] > /[debugfs]/mii_mgr_cl45_op\n");
|
||||
}
|
||||
|
||||
|
||||
static ssize_t airphy_debugfs_cl45(struct file *file,
|
||||
const char __user *buffer, size_t count,
|
||||
loff_t *data)
|
||||
{
|
||||
struct phy_device *phydev = file->private_data;
|
||||
char buf[64];
|
||||
int ret = 0;
|
||||
unsigned int reg, val, devnum;
|
||||
|
||||
memset(buf, 0, 64);
|
||||
if (count > sizeof(buf) - 1)
|
||||
return -EINVAL;
|
||||
if (copy_from_user(buf, buffer, count))
|
||||
return -EFAULT;
|
||||
|
||||
if (buf[0] == 'w') {
|
||||
if (sscanf(buf, "w %x %x %x", &devnum, ®, &val) == -1)
|
||||
return -EFAULT;
|
||||
|
||||
pr_notice("\nphy=%d, devnum=0x%x, reg=0x%x, val=0x%x\n",
|
||||
phydev_addr(phydev), devnum, reg, val);
|
||||
ret = air_mii_cl45_write(phydev, devnum, reg, val);
|
||||
if (ret < 0) {
|
||||
pr_notice("\nmii_cl45_write fail\n");
|
||||
return -EIO;
|
||||
}
|
||||
val = air_mii_cl45_read(phydev, devnum, reg);
|
||||
pr_notice("\nphy=%d, devnum=0x%x, reg=0x%x, val=0x%x confirm..\n",
|
||||
phydev_addr(phydev), devnum, reg, val);
|
||||
} else if (buf[0] == 'r') {
|
||||
if (sscanf(buf, "r %x %x", &devnum, ®) == -1)
|
||||
return -EFAULT;
|
||||
|
||||
val = air_mii_cl45_read(phydev, devnum, reg);
|
||||
pr_notice("\nphy=%d, devnum=0x%x, reg=0x%x, val=0x%x\n",
|
||||
phydev_addr(phydev), devnum, reg, val);
|
||||
} else
|
||||
airphy_debugfs_mii_cl45_help();
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations airphy_lp_speed_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = airphy_lp_speed_open,
|
||||
@@ -1361,6 +1508,20 @@ static const struct file_operations airphy_temp_fops = {
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static const struct file_operations airphy_debugfs_cl22_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = simple_open,
|
||||
.write = airphy_debugfs_cl22,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
static const struct file_operations airphy_debugfs_cl45_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = simple_open,
|
||||
.write = airphy_debugfs_cl45,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
int airphy_debugfs_init(struct phy_device *phydev)
|
||||
{
|
||||
int ret = 0;
|
||||
@@ -1404,6 +1565,12 @@ int airphy_debugfs_init(struct phy_device *phydev)
|
||||
debugfs_create_file(DEBUGFS_LP_SPEED, S_IFREG | 0444,
|
||||
dir, phydev,
|
||||
&airphy_lp_speed_fops);
|
||||
debugfs_create_file(DEBUGFS_MII_CL22_OP, S_IFREG | 0200,
|
||||
dir, phydev,
|
||||
&airphy_debugfs_cl22_fops);
|
||||
debugfs_create_file(DEBUGFS_MII_CL45_OP, S_IFREG | 0200,
|
||||
dir, phydev,
|
||||
&airphy_debugfs_cl45_fops);
|
||||
|
||||
priv->debugfs_root = dir;
|
||||
return ret;
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
#define DEBUGFS_DBG_REG_SHOW "dbg_regs_show"
|
||||
#define DEBUGFS_TEMPERATURE "temp"
|
||||
#define DEBUGFS_LP_SPEED "lp_speed"
|
||||
#define DEBUGFS_MII_CL22_OP "cl22_op"
|
||||
#define DEBUGFS_MII_CL45_OP "cl45_op"
|
||||
|
||||
enum air_port_mode {
|
||||
AIR_PORT_MODE_FORCE_100,
|
||||
@@ -82,6 +84,7 @@ unsigned int air_buckpbus_reg_read(struct phy_device *phydev,
|
||||
int air_buckpbus_reg_write(struct phy_device *phydev,
|
||||
unsigned int pbus_address, unsigned int pbus_data);
|
||||
int en8811h_of_init(struct phy_device *phydev);
|
||||
int air_surge_5ohm_config(struct phy_device *phydev);
|
||||
#ifdef CONFIG_AIROHA_EN8811H_PHY_DEBUGFS
|
||||
int airphy_debugfs_init(struct phy_device *phydev);
|
||||
void airphy_debugfs_remove(struct phy_device *phydev);
|
||||
|
||||
@@ -35,6 +35,7 @@ MODULE_LICENSE("GPL");
|
||||
* GPIO3 <-> BASE_T_LED2,
|
||||
**************************/
|
||||
/* User-defined.B */
|
||||
/*#define AIR_MD32_FW_CHECK*/
|
||||
#define AIR_LED_SUPPORT
|
||||
#ifdef AIR_LED_SUPPORT
|
||||
static const struct air_base_t_led_cfg led_cfg[3] = {
|
||||
@@ -52,9 +53,45 @@ static const u16 led_dur = UNIT_LED_BLINK_DURATION << AIR_LED_BLK_DUR_64M;
|
||||
/***********************************************************
|
||||
* F U N C T I O N S
|
||||
***********************************************************/
|
||||
static void air_mdio_read_buf(struct phy_device *phydev, unsigned long address,
|
||||
const struct firmware *fw, unsigned int *crc32)
|
||||
{
|
||||
unsigned int write_data, offset;
|
||||
int ret = 0, len = 0;
|
||||
unsigned int pbus_data_low, pbus_data_high;
|
||||
struct device *dev = phydev_dev(phydev);
|
||||
struct mii_bus *mbus = phydev_mdio_bus(phydev);
|
||||
int addr = phydev_addr(phydev);
|
||||
char *buf = kmalloc(fw->size, GFP_KERNEL);
|
||||
|
||||
memset(buf, '\0', fw->size);
|
||||
/* page 4 */
|
||||
ret |= air_mii_cl22_write(mbus, addr, 0x1F, 4);
|
||||
/* address increment*/
|
||||
ret |= air_mii_cl22_write(mbus, addr, 0x10, 0x8000);
|
||||
ret |= air_mii_cl22_write(mbus, addr,
|
||||
0x15, (unsigned int)((address >> 16) & 0xffff));
|
||||
ret |= air_mii_cl22_write(mbus, addr,
|
||||
0x16, (unsigned int)(address & 0xffff));
|
||||
for (offset = 0; offset < fw->size; offset += 4) {
|
||||
pbus_data_high = air_mii_cl22_read(mbus, addr, 0x17);
|
||||
pbus_data_low = air_mii_cl22_read(mbus, addr, 0x18);
|
||||
buf[offset + 0] = pbus_data_low & 0xff;
|
||||
buf[offset + 1] = (pbus_data_low & 0xff00) >> 8;
|
||||
buf[offset + 2] = pbus_data_high & 0xff;
|
||||
buf[offset + 3] = (pbus_data_high & 0xff00) >> 8;
|
||||
}
|
||||
msleep(100);
|
||||
*crc32 = ~crc32(~0, buf, fw->size);
|
||||
ret |= air_mii_cl22_write(mbus, addr, 0x1F, 0);
|
||||
kfree(buf);
|
||||
if (ret) {
|
||||
dev_info(dev, "%s 0x%lx FAIL(ret:%d)\n",
|
||||
__func__, address, ret);
|
||||
}
|
||||
}
|
||||
|
||||
static int MDIOWriteBuf(struct phy_device *phydev,
|
||||
static int air_mdio_write_buf(struct phy_device *phydev,
|
||||
unsigned long address, const struct firmware *fw)
|
||||
{
|
||||
unsigned int write_data, offset;
|
||||
@@ -115,6 +152,10 @@ static int en8811h_load_firmware(struct phy_device *phydev)
|
||||
const char *firmware;
|
||||
int ret = 0;
|
||||
u32 pbus_value = 0;
|
||||
#ifdef AIR_MD32_FW_CHECK
|
||||
unsigned int d_crc32 = 0, crc32 = 0;
|
||||
int retry = 0;
|
||||
#endif
|
||||
struct en8811h_priv *priv = phydev->priv;
|
||||
|
||||
ret = air_buckpbus_reg_write(phydev,
|
||||
@@ -138,12 +179,12 @@ static int en8811h_load_firmware(struct phy_device *phydev)
|
||||
dev_info(dev, "%s: crc32=0x%x\n",
|
||||
firmware, ~crc32(~0, fw->data, fw->size));
|
||||
/* Download DM */
|
||||
ret = MDIOWriteBuf(phydev, 0x00000000, fw);
|
||||
ret = air_mdio_write_buf(phydev, 0x00000000, fw);
|
||||
release_firmware(fw);
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"MDIOWriteBuf 0x00000000 fail, ret: %d\n", ret);
|
||||
return ret;
|
||||
"air_mdio_write_buf 0x00000000 fail, ret: %d\n", ret);
|
||||
goto release;
|
||||
}
|
||||
|
||||
firmware = EN8811H_MD32_DSP;
|
||||
@@ -157,22 +198,56 @@ static int en8811h_load_firmware(struct phy_device *phydev)
|
||||
dev_info(dev, "%s: crc32=0x%x\n",
|
||||
firmware, ~crc32(~0, fw->data, fw->size));
|
||||
/* Download PM */
|
||||
ret = MDIOWriteBuf(phydev, 0x00100000, fw);
|
||||
release_firmware(fw);
|
||||
ret = air_mdio_write_buf(phydev, 0x00100000, fw);
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"MDIOWriteBuf 0x00100000 fail , ret: %d\n", ret);
|
||||
return ret;
|
||||
"air_mdio_write_buf 0x00100000 fail , ret: %d\n", ret);
|
||||
goto release;
|
||||
}
|
||||
pbus_value = air_buckpbus_reg_read(phydev, 0x800000);
|
||||
pbus_value &= ~BIT(11);
|
||||
ret = air_buckpbus_reg_write(phydev, 0x800000, pbus_value);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto release;
|
||||
#ifdef AIR_MD32_FW_CHECK
|
||||
crc32 = ~crc32(~0, fw->data, fw->size);
|
||||
/* Check PM */
|
||||
air_mdio_read_buf(phydev, 0x100000, fw, &d_crc32);
|
||||
if (d_crc32 == crc32)
|
||||
dev_info(dev, "0x00100000 Check Sum Pass.\n");
|
||||
else {
|
||||
dev_info(dev, "0x00100000 Check Sum Fail.\n");
|
||||
dev_info(dev, "CRC32 0x%x != Caculated CRC32 0x%x\n",
|
||||
crc32, d_crc32);
|
||||
}
|
||||
release_firmware(fw);
|
||||
retry = MAX_RETRY;
|
||||
do {
|
||||
ret = air_buckpbus_reg_write(phydev, 0x0f0018, 0x01);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
msleep(100);
|
||||
pbus_value = air_buckpbus_reg_read(phydev, 0x0f0018);
|
||||
if (retry == 0) {
|
||||
dev_err(dev,
|
||||
"Release Software Reset fail , ret: %d\n",
|
||||
pbus_value);
|
||||
break;
|
||||
}
|
||||
retry--;
|
||||
} while (pbus_value != 0x1);
|
||||
dev_info(dev,
|
||||
"Release Software Reset successful.\n");
|
||||
#else
|
||||
release_firmware(fw);
|
||||
ret = air_buckpbus_reg_write(phydev, 0x0f0018, 0x01);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
#endif
|
||||
return 0;
|
||||
release:
|
||||
release_firmware(fw);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef AIR_LED_SUPPORT
|
||||
@@ -383,18 +458,10 @@ static int en8811h_probe(struct phy_device *phydev)
|
||||
"EN8811H initialize fail!\n");
|
||||
goto priv_free;
|
||||
}
|
||||
/* Mode selection*/
|
||||
dev_info(dev, "EN8811H Mode 1 !\n");
|
||||
ret = air_mii_cl45_write(phydev, 0x1e, 0x800c, 0x0);
|
||||
if (ret < 0)
|
||||
goto priv_free;
|
||||
ret = air_mii_cl45_write(phydev, 0x1e, 0x800d, 0x0);
|
||||
if (ret < 0)
|
||||
goto priv_free;
|
||||
ret = air_mii_cl45_write(phydev, 0x1e, 0x800e, 0x1101);
|
||||
if (ret < 0)
|
||||
goto priv_free;
|
||||
ret = air_mii_cl45_write(phydev, 0x1e, 0x800f, 0x0002);
|
||||
ret |= air_mii_cl45_write(phydev, 0x1e, 0x800c, 0x0);
|
||||
ret |= air_mii_cl45_write(phydev, 0x1e, 0x800d, 0x0);
|
||||
ret |= air_mii_cl45_write(phydev, 0x1e, 0x800e, 0x1101);
|
||||
ret |= air_mii_cl45_write(phydev, 0x1e, 0x800f, 0x0002);
|
||||
if (ret < 0)
|
||||
goto priv_free;
|
||||
/* Serdes polarity */
|
||||
@@ -415,6 +482,13 @@ static int en8811h_probe(struct phy_device *phydev)
|
||||
dev_info(dev, "Tx, Rx Polarity : %08x\n", pbus_value);
|
||||
pbus_value = air_buckpbus_reg_read(phydev, 0x3b3c);
|
||||
dev_info(dev, "MD32 FW Version : %08x\n", pbus_value);
|
||||
if (priv->surge) {
|
||||
ret = air_surge_5ohm_config(phydev);
|
||||
if (ret < 0)
|
||||
dev_err(dev,
|
||||
"air_surge_5ohm_config fail. (ret=%d)\n", ret);
|
||||
} else
|
||||
dev_info(dev, "Surge Protection Mode - 0R\n");
|
||||
#if defined(AIR_LED_SUPPORT)
|
||||
ret = en8811h_led_init(phydev);
|
||||
if (ret < 0) {
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
config AN8855_GSW
|
||||
tristate "Driver for the Airoha AN8855 switch"
|
||||
help
|
||||
Airoha AN8855 devices and swconfig driver code,
|
||||
Provide swconfig command for basic switch operation.
|
||||
AN8855 support 2.5G speed and managed by SMI interface.
|
||||
To compile this driver as a module, choose M here.
|
||||
@@ -0,0 +1,10 @@
|
||||
#
|
||||
# Makefile for Airoha AN8855 gigabit switch
|
||||
#
|
||||
|
||||
obj-$(CONFIG_AN8855_GSW) += an8855.o
|
||||
|
||||
an8855-$(CONFIG_SWCONFIG) += an8855_swconfig.o
|
||||
|
||||
an8855-y += an8855_mdio.o an8855.o \
|
||||
an8855_common.o an8855_vlan.o an8855_nl.o
|
||||
@@ -0,0 +1,913 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2023 Airoha Inc.
|
||||
* Author: Min Yao <min.yao@airoha.com>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
#include "an8855.h"
|
||||
#include "an8855_regs.h"
|
||||
|
||||
/* AN8855 registers */
|
||||
#define SCU_BASE 0x10000000
|
||||
#define RG_RGMII_TXCK_C (SCU_BASE + 0x1d0)
|
||||
|
||||
#define HSGMII_AN_CSR_BASE 0x10220000
|
||||
#define SGMII_REG_AN0 (HSGMII_AN_CSR_BASE + 0x000)
|
||||
#define SGMII_REG_AN_13 (HSGMII_AN_CSR_BASE + 0x034)
|
||||
#define SGMII_REG_AN_FORCE_CL37 (HSGMII_AN_CSR_BASE + 0x060)
|
||||
|
||||
#define HSGMII_CSR_PCS_BASE 0x10220000
|
||||
#define RG_HSGMII_PCS_CTROL_1 (HSGMII_CSR_PCS_BASE + 0xa00)
|
||||
#define RG_AN_SGMII_MODE_FORCE (HSGMII_CSR_PCS_BASE + 0xa24)
|
||||
|
||||
#define MULTI_SGMII_CSR_BASE 0x10224000
|
||||
#define SGMII_STS_CTRL_0 (MULTI_SGMII_CSR_BASE + 0x018)
|
||||
#define MSG_RX_CTRL_0 (MULTI_SGMII_CSR_BASE + 0x100)
|
||||
#define MSG_RX_LIK_STS_0 (MULTI_SGMII_CSR_BASE + 0x514)
|
||||
#define MSG_RX_LIK_STS_2 (MULTI_SGMII_CSR_BASE + 0x51c)
|
||||
#define PHY_RX_FORCE_CTRL_0 (MULTI_SGMII_CSR_BASE + 0x520)
|
||||
|
||||
#define XFI_CSR_PCS_BASE 0x10225000
|
||||
#define RG_USXGMII_AN_CONTROL_0 (XFI_CSR_PCS_BASE + 0xbf8)
|
||||
|
||||
#define MULTI_PHY_RA_CSR_BASE 0x10226000
|
||||
#define RG_RATE_ADAPT_CTRL_0 (MULTI_PHY_RA_CSR_BASE + 0x000)
|
||||
#define RATE_ADP_P0_CTRL_0 (MULTI_PHY_RA_CSR_BASE + 0x100)
|
||||
#define MII_RA_AN_ENABLE (MULTI_PHY_RA_CSR_BASE + 0x300)
|
||||
|
||||
#define QP_DIG_CSR_BASE 0x1022a000
|
||||
#define QP_CK_RST_CTRL_4 (QP_DIG_CSR_BASE + 0x310)
|
||||
#define QP_DIG_MODE_CTRL_0 (QP_DIG_CSR_BASE + 0x324)
|
||||
#define QP_DIG_MODE_CTRL_1 (QP_DIG_CSR_BASE + 0x330)
|
||||
|
||||
#define SERDES_WRAPPER_BASE 0x1022c000
|
||||
#define USGMII_CTRL_0 (SERDES_WRAPPER_BASE + 0x000)
|
||||
|
||||
#define QP_PMA_TOP_BASE 0x1022e000
|
||||
#define PON_RXFEDIG_CTRL_0 (QP_PMA_TOP_BASE + 0x100)
|
||||
#define PON_RXFEDIG_CTRL_9 (QP_PMA_TOP_BASE + 0x124)
|
||||
|
||||
#define SS_LCPLL_PWCTL_SETTING_2 (QP_PMA_TOP_BASE + 0x208)
|
||||
#define SS_LCPLL_TDC_FLT_2 (QP_PMA_TOP_BASE + 0x230)
|
||||
#define SS_LCPLL_TDC_FLT_5 (QP_PMA_TOP_BASE + 0x23c)
|
||||
#define SS_LCPLL_TDC_PCW_1 (QP_PMA_TOP_BASE + 0x248)
|
||||
#define INTF_CTRL_8 (QP_PMA_TOP_BASE + 0x320)
|
||||
#define INTF_CTRL_9 (QP_PMA_TOP_BASE + 0x324)
|
||||
#define PLL_CTRL_0 (QP_PMA_TOP_BASE + 0x400)
|
||||
#define PLL_CTRL_2 (QP_PMA_TOP_BASE + 0x408)
|
||||
#define PLL_CTRL_3 (QP_PMA_TOP_BASE + 0x40c)
|
||||
#define PLL_CTRL_4 (QP_PMA_TOP_BASE + 0x410)
|
||||
#define PLL_CK_CTRL_0 (QP_PMA_TOP_BASE + 0x414)
|
||||
#define RX_DLY_0 (QP_PMA_TOP_BASE + 0x614)
|
||||
#define RX_CTRL_2 (QP_PMA_TOP_BASE + 0x630)
|
||||
#define RX_CTRL_5 (QP_PMA_TOP_BASE + 0x63c)
|
||||
#define RX_CTRL_6 (QP_PMA_TOP_BASE + 0x640)
|
||||
#define RX_CTRL_7 (QP_PMA_TOP_BASE + 0x644)
|
||||
#define RX_CTRL_8 (QP_PMA_TOP_BASE + 0x648)
|
||||
#define RX_CTRL_26 (QP_PMA_TOP_BASE + 0x690)
|
||||
#define RX_CTRL_42 (QP_PMA_TOP_BASE + 0x6d0)
|
||||
|
||||
#define QP_ANA_CSR_BASE 0x1022f000
|
||||
#define RG_QP_RX_DAC_EN (QP_ANA_CSR_BASE + 0x00)
|
||||
#define RG_QP_RXAFE_RESERVE (QP_ANA_CSR_BASE + 0x04)
|
||||
#define RG_QP_CDR_LPF_MJV_LIM (QP_ANA_CSR_BASE + 0x0c)
|
||||
#define RG_QP_CDR_LPF_SETVALUE (QP_ANA_CSR_BASE + 0x14)
|
||||
#define RG_QP_CDR_PR_CKREF_DIV1 (QP_ANA_CSR_BASE + 0x18)
|
||||
#define RG_QP_CDR_PR_KBAND_DIV_PCIE (QP_ANA_CSR_BASE + 0x1c)
|
||||
#define RG_QP_CDR_FORCE_IBANDLPF_R_OFF (QP_ANA_CSR_BASE + 0x20)
|
||||
#define RG_QP_TX_MODE_16B_EN (QP_ANA_CSR_BASE + 0x28)
|
||||
#define RG_QP_PLL_IPLL_DIG_PWR_SEL (QP_ANA_CSR_BASE + 0x3c)
|
||||
#define RG_QP_PLL_SDM_ORD (QP_ANA_CSR_BASE + 0x40)
|
||||
|
||||
#define ETHER_SYS_BASE 0x1028c800
|
||||
#define RG_P5MUX_MODE (ETHER_SYS_BASE + 0x00)
|
||||
#define RG_FORCE_CKDIR_SEL (ETHER_SYS_BASE + 0x04)
|
||||
#define RG_SWITCH_MODE (ETHER_SYS_BASE + 0x08)
|
||||
#define RG_FORCE_MAC5_SB (ETHER_SYS_BASE + 0x2c)
|
||||
#define RG_GPHY_AFE_PWD (ETHER_SYS_BASE + 0x40)
|
||||
#define RG_GPHY_SMI_ADDR (ETHER_SYS_BASE + 0x48)
|
||||
#define CSR_RMII (ETHER_SYS_BASE + 0x70)
|
||||
|
||||
/* PHY EEE Register bitmap of define */
|
||||
#define PHY_DEV07 0x07
|
||||
#define PHY_DEV07_REG_03C 0x3c
|
||||
|
||||
/* PHY Extend Register 0x14 bitmap of define */
|
||||
#define PHY_EXT_REG_14 0x14
|
||||
|
||||
/* Fields of PHY_EXT_REG_14 */
|
||||
#define PHY_EN_DOWN_SHFIT BIT(4)
|
||||
|
||||
/* Unique fields of PMCR for AN8855 */
|
||||
#define FORCE_TX_FC BIT(4)
|
||||
#define FORCE_RX_FC BIT(5)
|
||||
#define FORCE_DPX BIT(25)
|
||||
#define FORCE_SPD BITS(28, 30)
|
||||
#define FORCE_LNK BIT(24)
|
||||
#define FORCE_MODE BIT(31)
|
||||
|
||||
#define CHIP_ID 0x10005000
|
||||
#define CHIP_REV 0x10005004
|
||||
|
||||
static int an8855_set_hsgmii_mode(struct gsw_an8855 *gsw)
|
||||
{
|
||||
u32 val = 0;
|
||||
|
||||
/* PLL */
|
||||
val = an8855_reg_read(gsw, QP_DIG_MODE_CTRL_1);
|
||||
val &= ~(0x3 << 2);
|
||||
val |= (0x1 << 2);
|
||||
an8855_reg_write(gsw, QP_DIG_MODE_CTRL_1, val);
|
||||
|
||||
/* PLL - LPF */
|
||||
val = an8855_reg_read(gsw, PLL_CTRL_2);
|
||||
val &= ~(0x3 << 0);
|
||||
val |= (0x1 << 0);
|
||||
val &= ~(0x7 << 2);
|
||||
val |= (0x5 << 2);
|
||||
val &= ~BITS(6, 7);
|
||||
val &= ~(0x7 << 8);
|
||||
val |= (0x3 << 8);
|
||||
val |= BIT(29);
|
||||
val &= ~BITS(12, 13);
|
||||
an8855_reg_write(gsw, PLL_CTRL_2, val);
|
||||
|
||||
/* PLL - ICO */
|
||||
val = an8855_reg_read(gsw, PLL_CTRL_4);
|
||||
val |= BIT(2);
|
||||
an8855_reg_write(gsw, PLL_CTRL_4, val);
|
||||
|
||||
val = an8855_reg_read(gsw, PLL_CTRL_2);
|
||||
val &= ~BIT(14);
|
||||
an8855_reg_write(gsw, PLL_CTRL_2, val);
|
||||
|
||||
/* PLL - CHP */
|
||||
val = an8855_reg_read(gsw, PLL_CTRL_2);
|
||||
val &= ~(0xf << 16);
|
||||
val |= (0x6 << 16);
|
||||
an8855_reg_write(gsw, PLL_CTRL_2, val);
|
||||
|
||||
/* PLL - PFD */
|
||||
val = an8855_reg_read(gsw, PLL_CTRL_2);
|
||||
val &= ~(0x3 << 20);
|
||||
val |= (0x1 << 20);
|
||||
val &= ~(0x3 << 24);
|
||||
val |= (0x1 << 24);
|
||||
val &= ~BIT(26);
|
||||
an8855_reg_write(gsw, PLL_CTRL_2, val);
|
||||
|
||||
/* PLL - POSTDIV */
|
||||
val = an8855_reg_read(gsw, PLL_CTRL_2);
|
||||
val |= BIT(22);
|
||||
val &= ~BIT(27);
|
||||
val &= ~BIT(28);
|
||||
an8855_reg_write(gsw, PLL_CTRL_2, val);
|
||||
|
||||
/* PLL - SDM */
|
||||
val = an8855_reg_read(gsw, PLL_CTRL_4);
|
||||
val &= ~BITS(3, 4);
|
||||
an8855_reg_write(gsw, PLL_CTRL_4, val);
|
||||
|
||||
val = an8855_reg_read(gsw, PLL_CTRL_2);
|
||||
val &= ~BIT(30);
|
||||
an8855_reg_write(gsw, PLL_CTRL_2, val);
|
||||
|
||||
val = an8855_reg_read(gsw, SS_LCPLL_PWCTL_SETTING_2);
|
||||
val &= ~(0x3 << 16);
|
||||
val |= (0x1 << 16);
|
||||
an8855_reg_write(gsw, SS_LCPLL_PWCTL_SETTING_2, val);
|
||||
|
||||
an8855_reg_write(gsw, SS_LCPLL_TDC_FLT_2, 0x7a000000);
|
||||
an8855_reg_write(gsw, SS_LCPLL_TDC_PCW_1, 0x7a000000);
|
||||
|
||||
val = an8855_reg_read(gsw, SS_LCPLL_TDC_FLT_5);
|
||||
val &= ~BIT(24);
|
||||
an8855_reg_write(gsw, SS_LCPLL_TDC_FLT_5, val);
|
||||
|
||||
val = an8855_reg_read(gsw, PLL_CK_CTRL_0);
|
||||
val &= ~BIT(8);
|
||||
an8855_reg_write(gsw, PLL_CK_CTRL_0, val);
|
||||
|
||||
/* PLL - SS */
|
||||
val = an8855_reg_read(gsw, PLL_CTRL_3);
|
||||
val &= ~BITS(0, 15);
|
||||
an8855_reg_write(gsw, PLL_CTRL_3, val);
|
||||
|
||||
val = an8855_reg_read(gsw, PLL_CTRL_4);
|
||||
val &= ~BITS(0, 1);
|
||||
an8855_reg_write(gsw, PLL_CTRL_4, val);
|
||||
|
||||
val = an8855_reg_read(gsw, PLL_CTRL_3);
|
||||
val &= ~BITS(16, 31);
|
||||
an8855_reg_write(gsw, PLL_CTRL_3, val);
|
||||
|
||||
/* PLL - TDC */
|
||||
val = an8855_reg_read(gsw, PLL_CK_CTRL_0);
|
||||
val &= ~BIT(9);
|
||||
an8855_reg_write(gsw, PLL_CK_CTRL_0, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RG_QP_PLL_SDM_ORD);
|
||||
val |= BIT(3);
|
||||
val |= BIT(4);
|
||||
an8855_reg_write(gsw, RG_QP_PLL_SDM_ORD, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RG_QP_RX_DAC_EN);
|
||||
val &= ~(0x3 << 16);
|
||||
val |= (0x2 << 16);
|
||||
an8855_reg_write(gsw, RG_QP_RX_DAC_EN, val);
|
||||
|
||||
/* TCL Disable (only for Co-SIM) */
|
||||
val = an8855_reg_read(gsw, PON_RXFEDIG_CTRL_0);
|
||||
val &= ~BIT(12);
|
||||
an8855_reg_write(gsw, PON_RXFEDIG_CTRL_0, val);
|
||||
|
||||
/* TX Init */
|
||||
val = an8855_reg_read(gsw, RG_QP_TX_MODE_16B_EN);
|
||||
val &= ~BIT(0);
|
||||
val &= ~(0xffff << 16);
|
||||
val |= (0x4 << 16);
|
||||
an8855_reg_write(gsw, RG_QP_TX_MODE_16B_EN, val);
|
||||
|
||||
/* RX Control */
|
||||
val = an8855_reg_read(gsw, RG_QP_RXAFE_RESERVE);
|
||||
val |= BIT(11);
|
||||
an8855_reg_write(gsw, RG_QP_RXAFE_RESERVE, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RG_QP_CDR_LPF_MJV_LIM);
|
||||
val &= ~(0x3 << 4);
|
||||
val |= (0x1 << 4);
|
||||
an8855_reg_write(gsw, RG_QP_CDR_LPF_MJV_LIM, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RG_QP_CDR_LPF_SETVALUE);
|
||||
val &= ~(0xf << 25);
|
||||
val |= (0x1 << 25);
|
||||
val &= ~(0x7 << 29);
|
||||
val |= (0x3 << 29);
|
||||
an8855_reg_write(gsw, RG_QP_CDR_LPF_SETVALUE, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RG_QP_CDR_PR_CKREF_DIV1);
|
||||
val &= ~(0x1f << 8);
|
||||
val |= (0xf << 8);
|
||||
an8855_reg_write(gsw, RG_QP_CDR_PR_CKREF_DIV1, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RG_QP_CDR_PR_KBAND_DIV_PCIE);
|
||||
val &= ~(0x3f << 0);
|
||||
val |= (0x19 << 0);
|
||||
val &= ~BIT(6);
|
||||
an8855_reg_write(gsw, RG_QP_CDR_PR_KBAND_DIV_PCIE, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RG_QP_CDR_FORCE_IBANDLPF_R_OFF);
|
||||
val &= ~(0x7f << 6);
|
||||
val |= (0x21 << 6);
|
||||
val &= ~(0x3 << 16);
|
||||
val |= (0x2 << 16);
|
||||
val &= ~BIT(13);
|
||||
an8855_reg_write(gsw, RG_QP_CDR_FORCE_IBANDLPF_R_OFF, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RG_QP_CDR_PR_KBAND_DIV_PCIE);
|
||||
val &= ~BIT(30);
|
||||
an8855_reg_write(gsw, RG_QP_CDR_PR_KBAND_DIV_PCIE, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RG_QP_CDR_PR_CKREF_DIV1);
|
||||
val &= ~(0x7 << 24);
|
||||
val |= (0x4 << 24);
|
||||
an8855_reg_write(gsw, RG_QP_CDR_PR_CKREF_DIV1, val);
|
||||
|
||||
val = an8855_reg_read(gsw, PLL_CTRL_0);
|
||||
val |= BIT(0);
|
||||
an8855_reg_write(gsw, PLL_CTRL_0, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RX_CTRL_26);
|
||||
val &= ~BIT(23);
|
||||
val |= BIT(26);
|
||||
an8855_reg_write(gsw, RX_CTRL_26, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RX_DLY_0);
|
||||
val &= ~(0xff << 0);
|
||||
val |= (0x6f << 0);
|
||||
val |= BITS(8, 13);
|
||||
an8855_reg_write(gsw, RX_DLY_0, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RX_CTRL_42);
|
||||
val &= ~(0x1fff << 0);
|
||||
val |= (0x150 << 0);
|
||||
an8855_reg_write(gsw, RX_CTRL_42, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RX_CTRL_2);
|
||||
val &= ~(0x1fff << 16);
|
||||
val |= (0x150 << 16);
|
||||
an8855_reg_write(gsw, RX_CTRL_2, val);
|
||||
|
||||
val = an8855_reg_read(gsw, PON_RXFEDIG_CTRL_9);
|
||||
val &= ~(0x7 << 0);
|
||||
val |= (0x1 << 0);
|
||||
an8855_reg_write(gsw, PON_RXFEDIG_CTRL_9, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RX_CTRL_8);
|
||||
val &= ~(0xfff << 16);
|
||||
val |= (0x200 << 16);
|
||||
val &= ~(0x7fff << 14);
|
||||
val |= (0xfff << 14);
|
||||
an8855_reg_write(gsw, RX_CTRL_8, val);
|
||||
|
||||
/* Frequency memter */
|
||||
val = an8855_reg_read(gsw, RX_CTRL_5);
|
||||
val &= ~(0xfffff << 10);
|
||||
val |= (0x10 << 10);
|
||||
an8855_reg_write(gsw, RX_CTRL_5, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RX_CTRL_6);
|
||||
val &= ~(0xfffff << 0);
|
||||
val |= (0x64 << 0);
|
||||
an8855_reg_write(gsw, RX_CTRL_6, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RX_CTRL_7);
|
||||
val &= ~(0xfffff << 0);
|
||||
val |= (0x2710 << 0);
|
||||
an8855_reg_write(gsw, RX_CTRL_7, val);
|
||||
|
||||
/* PCS Init */
|
||||
val = an8855_reg_read(gsw, RG_HSGMII_PCS_CTROL_1);
|
||||
val &= ~BIT(30);
|
||||
an8855_reg_write(gsw, RG_HSGMII_PCS_CTROL_1, val);
|
||||
|
||||
/* Rate Adaption */
|
||||
val = an8855_reg_read(gsw, RATE_ADP_P0_CTRL_0);
|
||||
val &= ~BIT(31);
|
||||
an8855_reg_write(gsw, RATE_ADP_P0_CTRL_0, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RG_RATE_ADAPT_CTRL_0);
|
||||
val |= BIT(0);
|
||||
val |= BIT(4);
|
||||
val |= BITS(26, 27);
|
||||
an8855_reg_write(gsw, RG_RATE_ADAPT_CTRL_0, val);
|
||||
|
||||
/* Disable AN */
|
||||
val = an8855_reg_read(gsw, SGMII_REG_AN0);
|
||||
val &= ~BIT(12);
|
||||
an8855_reg_write(gsw, SGMII_REG_AN0, val);
|
||||
|
||||
/* Force Speed */
|
||||
val = an8855_reg_read(gsw, SGMII_STS_CTRL_0);
|
||||
val |= BIT(2);
|
||||
val |= BITS(4, 5);
|
||||
an8855_reg_write(gsw, SGMII_STS_CTRL_0, val);
|
||||
|
||||
/* bypass flow control to MAC */
|
||||
an8855_reg_write(gsw, MSG_RX_LIK_STS_0, 0x01010107);
|
||||
an8855_reg_write(gsw, MSG_RX_LIK_STS_2, 0x00000EEF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int an8855_sgmii_setup(struct gsw_an8855 *gsw, int mode)
|
||||
{
|
||||
u32 val = 0;
|
||||
|
||||
/* PMA Init */
|
||||
/* PLL */
|
||||
val = an8855_reg_read(gsw, QP_DIG_MODE_CTRL_1);
|
||||
val &= ~BITS(2, 3);
|
||||
an8855_reg_write(gsw, QP_DIG_MODE_CTRL_1, val);
|
||||
|
||||
/* PLL - LPF */
|
||||
val = an8855_reg_read(gsw, PLL_CTRL_2);
|
||||
val &= ~(0x3 << 0);
|
||||
val |= (0x1 << 0);
|
||||
val &= ~(0x7 << 2);
|
||||
val |= (0x5 << 2);
|
||||
val &= ~BITS(6, 7);
|
||||
val &= ~(0x7 << 8);
|
||||
val |= (0x3 << 8);
|
||||
val |= BIT(29);
|
||||
val &= ~BITS(12, 13);
|
||||
an8855_reg_write(gsw, PLL_CTRL_2, val);
|
||||
|
||||
/* PLL - ICO */
|
||||
val = an8855_reg_read(gsw, PLL_CTRL_4);
|
||||
val |= BIT(2);
|
||||
an8855_reg_write(gsw, PLL_CTRL_4, val);
|
||||
|
||||
val = an8855_reg_read(gsw, PLL_CTRL_2);
|
||||
val &= ~BIT(14);
|
||||
an8855_reg_write(gsw, PLL_CTRL_2, val);
|
||||
|
||||
/* PLL - CHP */
|
||||
val = an8855_reg_read(gsw, PLL_CTRL_2);
|
||||
val &= ~(0xf << 16);
|
||||
val |= (0x4 << 16);
|
||||
an8855_reg_write(gsw, PLL_CTRL_2, val);
|
||||
|
||||
/* PLL - PFD */
|
||||
val = an8855_reg_read(gsw, PLL_CTRL_2);
|
||||
val &= ~(0x3 << 20);
|
||||
val |= (0x1 << 20);
|
||||
val &= ~(0x3 << 24);
|
||||
val |= (0x1 << 24);
|
||||
val &= ~BIT(26);
|
||||
an8855_reg_write(gsw, PLL_CTRL_2, val);
|
||||
|
||||
/* PLL - POSTDIV */
|
||||
val = an8855_reg_read(gsw, PLL_CTRL_2);
|
||||
val |= BIT(22);
|
||||
val &= ~BIT(27);
|
||||
val &= ~BIT(28);
|
||||
an8855_reg_write(gsw, PLL_CTRL_2, val);
|
||||
|
||||
/* PLL - SDM */
|
||||
val = an8855_reg_read(gsw, PLL_CTRL_4);
|
||||
val &= ~BITS(3, 4);
|
||||
an8855_reg_write(gsw, PLL_CTRL_4, val);
|
||||
|
||||
val = an8855_reg_read(gsw, PLL_CTRL_2);
|
||||
val &= ~BIT(30);
|
||||
an8855_reg_write(gsw, PLL_CTRL_2, val);
|
||||
|
||||
val = an8855_reg_read(gsw, SS_LCPLL_PWCTL_SETTING_2);
|
||||
val &= ~(0x3 << 16);
|
||||
val |= (0x1 << 16);
|
||||
an8855_reg_write(gsw, SS_LCPLL_PWCTL_SETTING_2, val);
|
||||
|
||||
an8855_reg_write(gsw, SS_LCPLL_TDC_FLT_2, 0x48000000);
|
||||
an8855_reg_write(gsw, SS_LCPLL_TDC_PCW_1, 0x48000000);
|
||||
|
||||
val = an8855_reg_read(gsw, SS_LCPLL_TDC_FLT_5);
|
||||
val &= ~BIT(24);
|
||||
an8855_reg_write(gsw, SS_LCPLL_TDC_FLT_5, val);
|
||||
|
||||
val = an8855_reg_read(gsw, PLL_CK_CTRL_0);
|
||||
val &= ~BIT(8);
|
||||
an8855_reg_write(gsw, PLL_CK_CTRL_0, val);
|
||||
|
||||
/* PLL - SS */
|
||||
val = an8855_reg_read(gsw, PLL_CTRL_3);
|
||||
val &= ~BITS(0, 15);
|
||||
an8855_reg_write(gsw, PLL_CTRL_3, val);
|
||||
|
||||
val = an8855_reg_read(gsw, PLL_CTRL_4);
|
||||
val &= ~BITS(0, 1);
|
||||
an8855_reg_write(gsw, PLL_CTRL_4, val);
|
||||
|
||||
val = an8855_reg_read(gsw, PLL_CTRL_3);
|
||||
val &= ~BITS(16, 31);
|
||||
an8855_reg_write(gsw, PLL_CTRL_3, val);
|
||||
|
||||
/* PLL - TDC */
|
||||
val = an8855_reg_read(gsw, PLL_CK_CTRL_0);
|
||||
val &= ~BIT(9);
|
||||
an8855_reg_write(gsw, PLL_CK_CTRL_0, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RG_QP_PLL_SDM_ORD);
|
||||
val |= BIT(3);
|
||||
val |= BIT(4);
|
||||
an8855_reg_write(gsw, RG_QP_PLL_SDM_ORD, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RG_QP_RX_DAC_EN);
|
||||
val &= ~(0x3 << 16);
|
||||
val |= (0x2 << 16);
|
||||
an8855_reg_write(gsw, RG_QP_RX_DAC_EN, val);
|
||||
|
||||
/* PLL - TCL Disable (only for Co-SIM) */
|
||||
val = an8855_reg_read(gsw, PON_RXFEDIG_CTRL_0);
|
||||
val &= ~BIT(12);
|
||||
an8855_reg_write(gsw, PON_RXFEDIG_CTRL_0, val);
|
||||
|
||||
/* TX Init */
|
||||
val = an8855_reg_read(gsw, RG_QP_TX_MODE_16B_EN);
|
||||
val &= ~BIT(0);
|
||||
val &= ~BITS(16, 31);
|
||||
an8855_reg_write(gsw, RG_QP_TX_MODE_16B_EN, val);
|
||||
|
||||
/* RX Init */
|
||||
val = an8855_reg_read(gsw, RG_QP_RXAFE_RESERVE);
|
||||
val |= BIT(11);
|
||||
an8855_reg_write(gsw, RG_QP_RXAFE_RESERVE, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RG_QP_CDR_LPF_MJV_LIM);
|
||||
val &= ~(0x3 << 4);
|
||||
val |= (0x2 << 4);
|
||||
an8855_reg_write(gsw, RG_QP_CDR_LPF_MJV_LIM, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RG_QP_CDR_LPF_SETVALUE);
|
||||
val &= ~(0xf << 25);
|
||||
val |= (0x1 << 25);
|
||||
val &= ~(0x7 << 29);
|
||||
val |= (0x6 << 29);
|
||||
an8855_reg_write(gsw, RG_QP_CDR_LPF_SETVALUE, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RG_QP_CDR_PR_CKREF_DIV1);
|
||||
val &= ~(0x1f << 8);
|
||||
val |= (0xc << 8);
|
||||
an8855_reg_write(gsw, RG_QP_CDR_PR_CKREF_DIV1, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RG_QP_CDR_PR_KBAND_DIV_PCIE);
|
||||
val &= ~(0x3f << 0);
|
||||
val |= (0x19 << 0);
|
||||
val &= ~BIT(6);
|
||||
an8855_reg_write(gsw, RG_QP_CDR_PR_KBAND_DIV_PCIE, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RG_QP_CDR_FORCE_IBANDLPF_R_OFF);
|
||||
val &= ~(0x7f << 6);
|
||||
val |= (0x21 << 6);
|
||||
val &= ~(0x3 << 16);
|
||||
val |= (0x2 << 16);
|
||||
val &= ~BIT(13);
|
||||
an8855_reg_write(gsw, RG_QP_CDR_FORCE_IBANDLPF_R_OFF, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RG_QP_CDR_PR_KBAND_DIV_PCIE);
|
||||
val &= ~BIT(30);
|
||||
an8855_reg_write(gsw, RG_QP_CDR_PR_KBAND_DIV_PCIE, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RG_QP_CDR_PR_CKREF_DIV1);
|
||||
val &= ~(0x7 << 24);
|
||||
val |= (0x4 << 24);
|
||||
an8855_reg_write(gsw, RG_QP_CDR_PR_CKREF_DIV1, val);
|
||||
|
||||
val = an8855_reg_read(gsw, PLL_CTRL_0);
|
||||
val |= BIT(0);
|
||||
an8855_reg_write(gsw, PLL_CTRL_0, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RX_CTRL_26);
|
||||
val &= ~BIT(23);
|
||||
if (mode == SGMII_MODE_AN)
|
||||
val |= BIT(26);
|
||||
|
||||
an8855_reg_write(gsw, RX_CTRL_26, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RX_DLY_0);
|
||||
val &= ~(0xff << 0);
|
||||
val |= (0x6f << 0);
|
||||
val |= BITS(8, 13);
|
||||
an8855_reg_write(gsw, RX_DLY_0, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RX_CTRL_42);
|
||||
val &= ~(0x1fff << 0);
|
||||
val |= (0x150 << 0);
|
||||
an8855_reg_write(gsw, RX_CTRL_42, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RX_CTRL_2);
|
||||
val &= ~(0x1fff << 16);
|
||||
val |= (0x150 << 16);
|
||||
an8855_reg_write(gsw, RX_CTRL_2, val);
|
||||
|
||||
val = an8855_reg_read(gsw, PON_RXFEDIG_CTRL_9);
|
||||
val &= ~(0x7 << 0);
|
||||
val |= (0x1 << 0);
|
||||
an8855_reg_write(gsw, PON_RXFEDIG_CTRL_9, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RX_CTRL_8);
|
||||
val &= ~(0xfff << 16);
|
||||
val |= (0x200 << 16);
|
||||
val &= ~(0x7fff << 0);
|
||||
val |= (0xfff << 0);
|
||||
an8855_reg_write(gsw, RX_CTRL_8, val);
|
||||
|
||||
/* Frequency memter */
|
||||
val = an8855_reg_read(gsw, RX_CTRL_5);
|
||||
val &= ~(0xfffff << 10);
|
||||
val |= (0x28 << 10);
|
||||
an8855_reg_write(gsw, RX_CTRL_5, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RX_CTRL_6);
|
||||
val &= ~(0xfffff << 0);
|
||||
val |= (0x64 << 0);
|
||||
an8855_reg_write(gsw, RX_CTRL_6, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RX_CTRL_7);
|
||||
val &= ~(0xfffff << 0);
|
||||
val |= (0x2710 << 0);
|
||||
an8855_reg_write(gsw, RX_CTRL_7, val);
|
||||
|
||||
if (mode == SGMII_MODE_FORCE) {
|
||||
/* PCS Init */
|
||||
val = an8855_reg_read(gsw, QP_DIG_MODE_CTRL_0);
|
||||
val &= ~BIT(0);
|
||||
val &= ~BITS(4, 5);
|
||||
an8855_reg_write(gsw, QP_DIG_MODE_CTRL_0, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RG_HSGMII_PCS_CTROL_1);
|
||||
val &= ~BIT(30);
|
||||
an8855_reg_write(gsw, RG_HSGMII_PCS_CTROL_1, val);
|
||||
|
||||
/* Rate Adaption - GMII path config. */
|
||||
val = an8855_reg_read(gsw, RG_AN_SGMII_MODE_FORCE);
|
||||
val |= BIT(0);
|
||||
val &= ~BITS(4, 5);
|
||||
an8855_reg_write(gsw, RG_AN_SGMII_MODE_FORCE, val);
|
||||
|
||||
val = an8855_reg_read(gsw, SGMII_STS_CTRL_0);
|
||||
val |= BIT(2);
|
||||
val &= ~(0x3 << 4);
|
||||
val |= (0x2 << 4);
|
||||
an8855_reg_write(gsw, SGMII_STS_CTRL_0, val);
|
||||
|
||||
val = an8855_reg_read(gsw, SGMII_REG_AN0);
|
||||
val &= ~BIT(12);
|
||||
an8855_reg_write(gsw, SGMII_REG_AN0, val);
|
||||
|
||||
val = an8855_reg_read(gsw, PHY_RX_FORCE_CTRL_0);
|
||||
val |= BIT(4);
|
||||
an8855_reg_write(gsw, PHY_RX_FORCE_CTRL_0, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RATE_ADP_P0_CTRL_0);
|
||||
val &= ~BITS(0, 3);
|
||||
val |= BIT(28);
|
||||
an8855_reg_write(gsw, RATE_ADP_P0_CTRL_0, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RG_RATE_ADAPT_CTRL_0);
|
||||
val |= BIT(0);
|
||||
val |= BIT(4);
|
||||
val |= BITS(26, 27);
|
||||
an8855_reg_write(gsw, RG_RATE_ADAPT_CTRL_0, val);
|
||||
} else {
|
||||
/* PCS Init */
|
||||
val = an8855_reg_read(gsw, RG_HSGMII_PCS_CTROL_1);
|
||||
val &= ~BIT(30);
|
||||
an8855_reg_write(gsw, RG_HSGMII_PCS_CTROL_1, val);
|
||||
|
||||
/* Set AN Ability - Interrupt */
|
||||
val = an8855_reg_read(gsw, SGMII_REG_AN_FORCE_CL37);
|
||||
val |= BIT(0);
|
||||
an8855_reg_write(gsw, SGMII_REG_AN_FORCE_CL37, val);
|
||||
|
||||
val = an8855_reg_read(gsw, SGMII_REG_AN_13);
|
||||
val &= ~(0x3f << 0);
|
||||
val |= (0xb << 0);
|
||||
val |= BIT(8);
|
||||
an8855_reg_write(gsw, SGMII_REG_AN_13, val);
|
||||
|
||||
/* Rate Adaption - GMII path config. */
|
||||
val = an8855_reg_read(gsw, SGMII_REG_AN0);
|
||||
val |= BIT(12);
|
||||
an8855_reg_write(gsw, SGMII_REG_AN0, val);
|
||||
|
||||
val = an8855_reg_read(gsw, MII_RA_AN_ENABLE);
|
||||
val |= BIT(0);
|
||||
an8855_reg_write(gsw, MII_RA_AN_ENABLE, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RATE_ADP_P0_CTRL_0);
|
||||
val |= BIT(28);
|
||||
an8855_reg_write(gsw, RATE_ADP_P0_CTRL_0, val);
|
||||
|
||||
val = an8855_reg_read(gsw, RG_RATE_ADAPT_CTRL_0);
|
||||
val |= BIT(0);
|
||||
val |= BIT(4);
|
||||
val |= BITS(26, 27);
|
||||
an8855_reg_write(gsw, RG_RATE_ADAPT_CTRL_0, val);
|
||||
|
||||
/* Only for Co-SIM */
|
||||
|
||||
/* AN Speed up (Only for Co-SIM) */
|
||||
|
||||
/* Restart AN */
|
||||
val = an8855_reg_read(gsw, SGMII_REG_AN0);
|
||||
val |= BIT(9);
|
||||
val |= BIT(15);
|
||||
an8855_reg_write(gsw, SGMII_REG_AN0, val);
|
||||
}
|
||||
|
||||
/* bypass flow control to MAC */
|
||||
an8855_reg_write(gsw, MSG_RX_LIK_STS_0, 0x01010107);
|
||||
an8855_reg_write(gsw, MSG_RX_LIK_STS_2, 0x00000EEF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int an8855_set_port_rmii(struct gsw_an8855 *gsw)
|
||||
{
|
||||
an8855_reg_write(gsw, RG_P5MUX_MODE, 0x301);
|
||||
an8855_reg_write(gsw, RG_FORCE_CKDIR_SEL, 0x101);
|
||||
an8855_reg_write(gsw, RG_SWITCH_MODE, 0x101);
|
||||
an8855_reg_write(gsw, RG_FORCE_MAC5_SB, 0x1010101);
|
||||
an8855_reg_write(gsw, CSR_RMII, 0x420102);
|
||||
an8855_reg_write(gsw, RG_RGMII_TXCK_C, 0x1100910);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int an8855_set_port_rgmii(struct gsw_an8855 *gsw)
|
||||
{
|
||||
an8855_reg_write(gsw, RG_FORCE_MAC5_SB, 0x20101);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int an8855_mac_port_setup(struct gsw_an8855 *gsw, u32 port,
|
||||
struct an8855_port_cfg *port_cfg)
|
||||
{
|
||||
u32 pmcr;
|
||||
|
||||
if (port != 5) {
|
||||
dev_info(gsw->dev, "port %d is not a MAC port\n", port);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (port_cfg->enabled) {
|
||||
pmcr = an8855_reg_read(gsw, PMCR(5));
|
||||
|
||||
switch (port_cfg->phy_mode) {
|
||||
case PHY_INTERFACE_MODE_RMII:
|
||||
pmcr &= ~FORCE_SPD;
|
||||
pmcr |= FORCE_MODE | (MAC_SPD_100 << 28) | FORCE_DPX
|
||||
| FORCE_LNK | FORCE_TX_FC | FORCE_RX_FC;
|
||||
an8855_set_port_rmii(gsw);
|
||||
break;
|
||||
case PHY_INTERFACE_MODE_RGMII:
|
||||
pmcr &= ~FORCE_SPD;
|
||||
pmcr |= FORCE_MODE | (MAC_SPD_1000 << 28) | FORCE_DPX
|
||||
| FORCE_LNK | FORCE_TX_FC | FORCE_RX_FC;
|
||||
an8855_set_port_rgmii(gsw);
|
||||
break;
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
if (port_cfg->force_link) {
|
||||
pmcr &= ~FORCE_SPD;
|
||||
pmcr |= FORCE_MODE | (MAC_SPD_1000 << 28)
|
||||
| FORCE_DPX | FORCE_LNK | FORCE_TX_FC
|
||||
| FORCE_RX_FC;
|
||||
an8855_sgmii_setup(gsw, SGMII_MODE_FORCE);
|
||||
} else
|
||||
an8855_sgmii_setup(gsw, SGMII_MODE_AN);
|
||||
break;
|
||||
case PHY_INTERFACE_MODE_2500BASEX:
|
||||
pmcr &= ~FORCE_SPD;
|
||||
pmcr |= FORCE_MODE | (MAC_SPD_2500 << 28) | FORCE_DPX
|
||||
| FORCE_LNK | FORCE_TX_FC | FORCE_RX_FC;
|
||||
an8855_set_hsgmii_mode(gsw);
|
||||
break;
|
||||
default:
|
||||
dev_info(gsw->dev, "%s is not supported by port %d\n",
|
||||
phy_modes(port_cfg->phy_mode), port);
|
||||
}
|
||||
|
||||
if (port_cfg->force_link)
|
||||
an8855_reg_write(gsw, PMCR(port), pmcr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int an8855_sw_detect(struct gsw_an8855 *gsw, struct chip_rev *crev)
|
||||
{
|
||||
u32 id, rev;
|
||||
|
||||
id = an8855_reg_read(gsw, CHIP_ID);
|
||||
rev = an8855_reg_read(gsw, CHIP_REV);
|
||||
if (id == AN8855) {
|
||||
if (crev) {
|
||||
crev->rev = rev;
|
||||
crev->name = "AN8855";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static void an8855_phy_setting(struct gsw_an8855 *gsw)
|
||||
{
|
||||
int i;
|
||||
u32 val;
|
||||
|
||||
/* Release power down */
|
||||
an8855_reg_write(gsw, RG_GPHY_AFE_PWD, 0x0);
|
||||
|
||||
for (i = 0; i < AN8855_NUM_PHYS; i++) {
|
||||
/* Enable HW auto downshift */
|
||||
gsw->mii_write(gsw, i, 0x1f, 0x1);
|
||||
val = gsw->mii_read(gsw, i, PHY_EXT_REG_14);
|
||||
val |= PHY_EN_DOWN_SHFIT;
|
||||
gsw->mii_write(gsw, i, PHY_EXT_REG_14, val);
|
||||
gsw->mii_write(gsw, i, 0x1f, 0x0);
|
||||
|
||||
/* Enable Asymmetric Pause Capability */
|
||||
val = gsw->mii_read(gsw, i, MII_ADVERTISE);
|
||||
val |= ADVERTISE_PAUSE_ASYM;
|
||||
gsw->mii_write(gsw, i, MII_ADVERTISE, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void an8855_low_power_setting(struct gsw_an8855 *gsw)
|
||||
{
|
||||
int port, addr;
|
||||
|
||||
for (port = 0; port < AN8855_NUM_PHYS; port++) {
|
||||
gsw->mmd_write(gsw, port, 0x1e, 0x11, 0x0f00);
|
||||
gsw->mmd_write(gsw, port, 0x1e, 0x3c, 0x0000);
|
||||
gsw->mmd_write(gsw, port, 0x1e, 0x3d, 0x0000);
|
||||
gsw->mmd_write(gsw, port, 0x1e, 0x3e, 0x0000);
|
||||
gsw->mmd_write(gsw, port, 0x1e, 0xc6, 0x53aa);
|
||||
}
|
||||
|
||||
gsw->mmd_write(gsw, 0, 0x1f, 0x268, 0x07f1);
|
||||
gsw->mmd_write(gsw, 0, 0x1f, 0x269, 0x2111);
|
||||
gsw->mmd_write(gsw, 0, 0x1f, 0x26a, 0x0000);
|
||||
gsw->mmd_write(gsw, 0, 0x1f, 0x26b, 0x0074);
|
||||
gsw->mmd_write(gsw, 0, 0x1f, 0x26e, 0x00f6);
|
||||
gsw->mmd_write(gsw, 0, 0x1f, 0x26f, 0x6666);
|
||||
gsw->mmd_write(gsw, 0, 0x1f, 0x271, 0x2c02);
|
||||
gsw->mmd_write(gsw, 0, 0x1f, 0x272, 0x0c22);
|
||||
gsw->mmd_write(gsw, 0, 0x1f, 0x700, 0x0001);
|
||||
gsw->mmd_write(gsw, 0, 0x1f, 0x701, 0x0803);
|
||||
gsw->mmd_write(gsw, 0, 0x1f, 0x702, 0x01b6);
|
||||
gsw->mmd_write(gsw, 0, 0x1f, 0x703, 0x2111);
|
||||
|
||||
gsw->mmd_write(gsw, 1, 0x1f, 0x700, 0x0001);
|
||||
|
||||
for (addr = 0x200; addr <= 0x230; addr += 2)
|
||||
gsw->mmd_write(gsw, 0, 0x1f, addr, 0x2020);
|
||||
|
||||
for (addr = 0x201; addr <= 0x231; addr += 2)
|
||||
gsw->mmd_write(gsw, 0, 0x1f, addr, 0x0020);
|
||||
}
|
||||
|
||||
static void an8855_eee_setting(struct gsw_an8855 *gsw, u32 port)
|
||||
{
|
||||
/* Disable EEE */
|
||||
gsw->mmd_write(gsw, port, PHY_DEV07, PHY_DEV07_REG_03C, 0);
|
||||
}
|
||||
|
||||
static int an8855_sw_init(struct gsw_an8855 *gsw)
|
||||
{
|
||||
int i;
|
||||
u32 val;
|
||||
|
||||
gsw->phy_base = gsw->smi_addr & AN8855_SMI_ADDR_MASK;
|
||||
|
||||
gsw->mii_read = an8855_mii_read;
|
||||
gsw->mii_write = an8855_mii_write;
|
||||
gsw->mmd_read = an8855_mmd_read;
|
||||
gsw->mmd_write = an8855_mmd_write;
|
||||
|
||||
/* Force MAC link down before reset */
|
||||
an8855_reg_write(gsw, PMCR(5), FORCE_MODE);
|
||||
|
||||
/* Switch soft reset */
|
||||
an8855_reg_write(gsw, SYS_CTRL, SW_SYS_RST);
|
||||
usleep_range(100000, 110000);
|
||||
|
||||
/* change gphy smi address */
|
||||
if (gsw->new_smi_addr != gsw->smi_addr) {
|
||||
an8855_reg_write(gsw, RG_GPHY_SMI_ADDR, gsw->new_smi_addr);
|
||||
gsw->smi_addr = gsw->new_smi_addr;
|
||||
gsw->phy_base = gsw->new_smi_addr;
|
||||
}
|
||||
|
||||
for (i = 0; i < AN8855_NUM_PHYS; i++) {
|
||||
val = gsw->mii_read(gsw, i, MII_BMCR);
|
||||
val |= BMCR_ISOLATE;
|
||||
gsw->mii_write(gsw, i, MII_BMCR, val);
|
||||
}
|
||||
|
||||
an8855_mac_port_setup(gsw, 5, &gsw->port5_cfg);
|
||||
|
||||
/* Global mac control settings */
|
||||
val = an8855_reg_read(gsw, GMACCR);
|
||||
val |= (15 << MAX_RX_JUMBO_S) | RX_PKT_LEN_MAX_JUMBO;
|
||||
an8855_reg_write(gsw, GMACCR, val);
|
||||
|
||||
val = an8855_reg_read(gsw, CKGCR);
|
||||
val &= ~(CKG_LNKDN_GLB_STOP | CKG_LNKDN_PORT_STOP);
|
||||
an8855_reg_write(gsw, CKGCR, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int an8855_sw_post_init(struct gsw_an8855 *gsw)
|
||||
{
|
||||
int i;
|
||||
u32 val;
|
||||
|
||||
for (i = 0; i < AN8855_NUM_PHYS; i++) {
|
||||
val = gsw->mii_read(gsw, i, MII_BMCR);
|
||||
val &= ~BMCR_ISOLATE;
|
||||
gsw->mii_write(gsw, i, MII_BMCR, val);
|
||||
}
|
||||
|
||||
an8855_phy_setting(gsw);
|
||||
|
||||
for (i = 0; i < AN8855_NUM_PHYS; i++)
|
||||
an8855_eee_setting(gsw, i);
|
||||
|
||||
/* PHY restart AN*/
|
||||
for (i = 0; i < AN8855_NUM_PHYS; i++)
|
||||
gsw->mii_write(gsw, i, MII_BMCR, 0x1240);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct an8855_sw_id an8855_id = {
|
||||
.model = AN8855,
|
||||
.detect = an8855_sw_detect,
|
||||
.init = an8855_sw_init,
|
||||
.post_init = an8855_sw_post_init
|
||||
};
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Min Yao <min.yao@airoha.com>");
|
||||
MODULE_DESCRIPTION("Driver for Airoha AN8855 Gigabit Switch");
|
||||
@@ -0,0 +1,122 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2023 Airoha Inc.
|
||||
* Author: Min Yao <min.yao@airoha.com>
|
||||
*/
|
||||
|
||||
#ifndef _AN8855_H_
|
||||
#define _AN8855_H_
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/of_mdio.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/phy.h>
|
||||
|
||||
#ifdef CONFIG_SWCONFIG
|
||||
#include <linux/switch.h>
|
||||
#endif
|
||||
|
||||
#include "an8855_vlan.h"
|
||||
|
||||
#define AN8855_DFL_CPU_PORT 5
|
||||
#define AN8855_NUM_PHYS 5
|
||||
#define AN8855_DFL_SMI_ADDR 0x1
|
||||
#define AN8855_SMI_ADDR_MASK 0x1f
|
||||
|
||||
struct gsw_an8855;
|
||||
|
||||
enum an8855_model {
|
||||
AN8855 = 0x8855,
|
||||
};
|
||||
|
||||
enum sgmii_mode {
|
||||
SGMII_MODE_AN,
|
||||
SGMII_MODE_FORCE,
|
||||
};
|
||||
|
||||
struct an8855_port_cfg {
|
||||
struct device_node *np;
|
||||
phy_interface_t phy_mode;
|
||||
u32 enabled: 1;
|
||||
u32 force_link: 1;
|
||||
u32 speed: 2;
|
||||
u32 duplex: 1;
|
||||
bool stag_on;
|
||||
};
|
||||
|
||||
struct gsw_an8855 {
|
||||
u32 id;
|
||||
|
||||
struct device *dev;
|
||||
struct mii_bus *host_bus;
|
||||
u32 smi_addr;
|
||||
u32 new_smi_addr;
|
||||
u32 phy_base;
|
||||
|
||||
enum an8855_model model;
|
||||
const char *name;
|
||||
|
||||
struct an8855_port_cfg port5_cfg;
|
||||
|
||||
int phy_link_sts;
|
||||
|
||||
int irq;
|
||||
int reset_pin;
|
||||
struct work_struct irq_worker;
|
||||
|
||||
#ifdef CONFIG_SWCONFIG
|
||||
struct switch_dev swdev;
|
||||
u32 cpu_port;
|
||||
#endif
|
||||
|
||||
int global_vlan_enable;
|
||||
struct an8855_vlan_entry vlan_entries[AN8855_NUM_VLANS];
|
||||
struct an8855_port_entry port_entries[AN8855_NUM_PORTS];
|
||||
|
||||
int (*mii_read)(struct gsw_an8855 *gsw, int phy, int reg);
|
||||
void (*mii_write)(struct gsw_an8855 *gsw, int phy, int reg, u16 val);
|
||||
|
||||
int (*mmd_read)(struct gsw_an8855 *gsw, int addr, int devad, u16 reg);
|
||||
void (*mmd_write)(struct gsw_an8855 *gsw, int addr, int devad, u16 reg,
|
||||
u16 val);
|
||||
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct chip_rev {
|
||||
const char *name;
|
||||
u32 rev;
|
||||
};
|
||||
|
||||
struct an8855_sw_id {
|
||||
enum an8855_model model;
|
||||
int (*detect)(struct gsw_an8855 *gsw, struct chip_rev *crev);
|
||||
int (*init)(struct gsw_an8855 *gsw);
|
||||
int (*post_init)(struct gsw_an8855 *gsw);
|
||||
};
|
||||
|
||||
extern struct list_head an8855_devs;
|
||||
extern struct an8855_sw_id an8855_id;
|
||||
|
||||
struct gsw_an8855 *an8855_get_gsw(u32 id);
|
||||
struct gsw_an8855 *an8855_get_first_gsw(void);
|
||||
void an8855_put_gsw(void);
|
||||
void an8855_lock_gsw(void);
|
||||
|
||||
u32 an8855_reg_read(struct gsw_an8855 *gsw, u32 reg);
|
||||
void an8855_reg_write(struct gsw_an8855 *gsw, u32 reg, u32 val);
|
||||
|
||||
int an8855_mii_read(struct gsw_an8855 *gsw, int phy, int reg);
|
||||
void an8855_mii_write(struct gsw_an8855 *gsw, int phy, int reg, u16 val);
|
||||
|
||||
int an8855_mmd_read(struct gsw_an8855 *gsw, int addr, int devad, u16 reg);
|
||||
void an8855_mmd_write(struct gsw_an8855 *gsw, int addr, int devad, u16 reg,
|
||||
u16 val);
|
||||
|
||||
void an8855_irq_worker(struct work_struct *work);
|
||||
void an8855_irq_enable(struct gsw_an8855 *gsw);
|
||||
|
||||
#endif /* _AN8855_H_ */
|
||||
@@ -0,0 +1,96 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2023 Airoha Inc.
|
||||
* Author: Min Yao <min.yao@airoha.com>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "an8855.h"
|
||||
#include "an8855_regs.h"
|
||||
|
||||
void an8855_irq_enable(struct gsw_an8855 *gsw)
|
||||
{
|
||||
u32 val;
|
||||
int i;
|
||||
|
||||
/* Record initial PHY link status */
|
||||
for (i = 0; i < AN8855_NUM_PHYS; i++) {
|
||||
val = gsw->mii_read(gsw, i, MII_BMSR);
|
||||
if (val & BMSR_LSTATUS)
|
||||
gsw->phy_link_sts |= BIT(i);
|
||||
}
|
||||
|
||||
val = BIT(AN8855_NUM_PHYS) - 1;
|
||||
an8855_reg_write(gsw, SYS_INT_EN, val);
|
||||
|
||||
val = an8855_reg_read(gsw, INT_MASK);
|
||||
val |= INT_SYS_BIT;
|
||||
an8855_reg_write(gsw, INT_MASK, val);
|
||||
}
|
||||
|
||||
static void display_port_link_status(struct gsw_an8855 *gsw, u32 port)
|
||||
{
|
||||
u32 pmsr, speed_bits;
|
||||
const char *speed;
|
||||
|
||||
pmsr = an8855_reg_read(gsw, PMSR(port));
|
||||
|
||||
speed_bits = (pmsr & MAC_SPD_STS_M) >> MAC_SPD_STS_S;
|
||||
|
||||
switch (speed_bits) {
|
||||
case MAC_SPD_10:
|
||||
speed = "10Mbps";
|
||||
break;
|
||||
case MAC_SPD_100:
|
||||
speed = "100Mbps";
|
||||
break;
|
||||
case MAC_SPD_1000:
|
||||
speed = "1Gbps";
|
||||
break;
|
||||
case MAC_SPD_2500:
|
||||
speed = "2.5Gbps";
|
||||
break;
|
||||
default:
|
||||
dev_info(gsw->dev, "Invalid speed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (pmsr & MAC_LNK_STS) {
|
||||
dev_info(gsw->dev, "Port %d Link is Up - %s/%s\n",
|
||||
port, speed, (pmsr & MAC_DPX_STS) ? "Full" : "Half");
|
||||
} else {
|
||||
dev_info(gsw->dev, "Port %d Link is Down\n", port);
|
||||
}
|
||||
}
|
||||
|
||||
void an8855_irq_worker(struct work_struct *work)
|
||||
{
|
||||
struct gsw_an8855 *gsw;
|
||||
u32 sts, physts, laststs;
|
||||
int i;
|
||||
|
||||
gsw = container_of(work, struct gsw_an8855, irq_worker);
|
||||
|
||||
sts = an8855_reg_read(gsw, SYS_INT_STS);
|
||||
|
||||
/* Check for changed PHY link status */
|
||||
for (i = 0; i < AN8855_NUM_PHYS; i++) {
|
||||
if (!(sts & PHY_LC_INT(i)))
|
||||
continue;
|
||||
|
||||
laststs = gsw->phy_link_sts & BIT(i);
|
||||
physts = !!(gsw->mii_read(gsw, i, MII_BMSR) & BMSR_LSTATUS);
|
||||
physts <<= i;
|
||||
|
||||
if (physts ^ laststs) {
|
||||
gsw->phy_link_sts ^= BIT(i);
|
||||
display_port_link_status(gsw, i);
|
||||
}
|
||||
}
|
||||
|
||||
an8855_reg_write(gsw, SYS_INT_STS, sts);
|
||||
|
||||
enable_irq(gsw->irq);
|
||||
}
|
||||
@@ -0,0 +1,491 @@
|
||||
// SPDX-License-Identifian8855_gsw_ider: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2023 Airoha Inc.
|
||||
* Author: Min Yao <min.yao@airoha.com>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/of_mdio.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_net.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/phy.h>
|
||||
|
||||
#include "an8855.h"
|
||||
#include "an8855_swconfig.h"
|
||||
#include "an8855_regs.h"
|
||||
#include "an8855_nl.h"
|
||||
|
||||
/* AN8855 driver version */
|
||||
#define ARHT_AN8855_SWCFG_DRIVER_VER "1.0.1-L5.4"
|
||||
|
||||
static u32 an8855_gsw_id;
|
||||
struct list_head an8855_devs;
|
||||
static DEFINE_MUTEX(an8855_devs_lock);
|
||||
|
||||
static struct an8855_sw_id *an8855_sw_ids[] = {
|
||||
&an8855_id,
|
||||
};
|
||||
|
||||
u32 an8855_reg_read(struct gsw_an8855 *gsw, u32 reg)
|
||||
{
|
||||
u32 high, low;
|
||||
|
||||
mutex_lock(&gsw->host_bus->mdio_lock);
|
||||
|
||||
gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x1f, 0x4);
|
||||
gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x10, 0x0);
|
||||
|
||||
gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x15,
|
||||
((reg >> 16) & 0xFFFF));
|
||||
gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x16,
|
||||
(reg & 0xFFFF));
|
||||
|
||||
low = gsw->host_bus->read(gsw->host_bus, gsw->smi_addr, 0x18);
|
||||
high = gsw->host_bus->read(gsw->host_bus, gsw->smi_addr, 0x17);
|
||||
|
||||
gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x1f, 0x0);
|
||||
gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x10, 0x0);
|
||||
|
||||
mutex_unlock(&gsw->host_bus->mdio_lock);
|
||||
|
||||
return (high << 16) | (low & 0xffff);
|
||||
}
|
||||
|
||||
void an8855_reg_write(struct gsw_an8855 *gsw, u32 reg, u32 val)
|
||||
{
|
||||
mutex_lock(&gsw->host_bus->mdio_lock);
|
||||
|
||||
gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x1f, 0x4);
|
||||
gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x10, 0x0);
|
||||
|
||||
gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x11,
|
||||
((reg >> 16) & 0xFFFF));
|
||||
gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x12,
|
||||
(reg & 0xFFFF));
|
||||
|
||||
gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x13,
|
||||
((val >> 16) & 0xFFFF));
|
||||
gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x14,
|
||||
(val & 0xFFFF));
|
||||
|
||||
gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x1f, 0x0);
|
||||
gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x10, 0x0);
|
||||
|
||||
mutex_unlock(&gsw->host_bus->mdio_lock);
|
||||
}
|
||||
|
||||
int an8855_mii_read(struct gsw_an8855 *gsw, int phy, int reg)
|
||||
{
|
||||
int val;
|
||||
|
||||
if (phy < AN8855_NUM_PHYS)
|
||||
phy = (gsw->phy_base + phy) & AN8855_SMI_ADDR_MASK;
|
||||
|
||||
mutex_lock(&gsw->host_bus->mdio_lock);
|
||||
val = gsw->host_bus->read(gsw->host_bus, phy, reg);
|
||||
mutex_unlock(&gsw->host_bus->mdio_lock);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
void an8855_mii_write(struct gsw_an8855 *gsw, int phy, int reg, u16 val)
|
||||
{
|
||||
if (phy < AN8855_NUM_PHYS)
|
||||
phy = (gsw->phy_base + phy) & AN8855_SMI_ADDR_MASK;
|
||||
|
||||
mutex_lock(&gsw->host_bus->mdio_lock);
|
||||
gsw->host_bus->write(gsw->host_bus, phy, reg, val);
|
||||
mutex_unlock(&gsw->host_bus->mdio_lock);
|
||||
}
|
||||
|
||||
int an8855_mmd_read(struct gsw_an8855 *gsw, int addr, int devad, u16 reg)
|
||||
{
|
||||
int val;
|
||||
u32 regnum = MII_ADDR_C45 | (devad << 16) | reg;
|
||||
|
||||
if (addr < AN8855_NUM_PHYS)
|
||||
addr = (gsw->phy_base + addr) & AN8855_SMI_ADDR_MASK;
|
||||
|
||||
mutex_lock(&gsw->host_bus->mdio_lock);
|
||||
val = gsw->host_bus->read(gsw->host_bus, addr, regnum);
|
||||
mutex_unlock(&gsw->host_bus->mdio_lock);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
void an8855_mmd_write(struct gsw_an8855 *gsw, int addr, int devad, u16 reg,
|
||||
u16 val)
|
||||
{
|
||||
u32 regnum = MII_ADDR_C45 | (devad << 16) | reg;
|
||||
|
||||
if (addr < AN8855_NUM_PHYS)
|
||||
addr = (gsw->phy_base + addr) & AN8855_SMI_ADDR_MASK;
|
||||
|
||||
mutex_lock(&gsw->host_bus->mdio_lock);
|
||||
gsw->host_bus->write(gsw->host_bus, addr, regnum, val);
|
||||
mutex_unlock(&gsw->host_bus->mdio_lock);
|
||||
}
|
||||
|
||||
static inline int an8855_get_duplex(const struct device_node *np)
|
||||
{
|
||||
return of_property_read_bool(np, "full-duplex");
|
||||
}
|
||||
|
||||
static void an8855_load_port_cfg(struct gsw_an8855 *gsw)
|
||||
{
|
||||
struct device_node *port_np;
|
||||
struct device_node *fixed_link_node;
|
||||
struct an8855_port_cfg *port_cfg;
|
||||
u32 port;
|
||||
|
||||
for_each_child_of_node(gsw->dev->of_node, port_np) {
|
||||
if (!of_device_is_compatible(port_np, "airoha,an8855-port"))
|
||||
continue;
|
||||
|
||||
if (!of_device_is_available(port_np))
|
||||
continue;
|
||||
|
||||
if (of_property_read_u32(port_np, "reg", &port))
|
||||
continue;
|
||||
|
||||
switch (port) {
|
||||
case 5:
|
||||
port_cfg = &gsw->port5_cfg;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
if (port_cfg->enabled) {
|
||||
dev_info(gsw->dev, "duplicated node for port%d\n",
|
||||
port_cfg->phy_mode);
|
||||
continue;
|
||||
}
|
||||
|
||||
port_cfg->np = port_np;
|
||||
|
||||
port_cfg->phy_mode = of_get_phy_mode(port_np);
|
||||
if (port_cfg->phy_mode < 0) {
|
||||
dev_info(gsw->dev, "incorrect phy-mode %d\n", port);
|
||||
continue;
|
||||
}
|
||||
|
||||
fixed_link_node = of_get_child_by_name(port_np, "fixed-link");
|
||||
if (fixed_link_node) {
|
||||
u32 speed;
|
||||
|
||||
port_cfg->force_link = 1;
|
||||
port_cfg->duplex = an8855_get_duplex(fixed_link_node);
|
||||
|
||||
if (of_property_read_u32(fixed_link_node, "speed",
|
||||
&speed)) {
|
||||
speed = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
of_node_put(fixed_link_node);
|
||||
|
||||
switch (speed) {
|
||||
case 10:
|
||||
port_cfg->speed = MAC_SPD_10;
|
||||
break;
|
||||
case 100:
|
||||
port_cfg->speed = MAC_SPD_100;
|
||||
break;
|
||||
case 1000:
|
||||
port_cfg->speed = MAC_SPD_1000;
|
||||
break;
|
||||
case 2500:
|
||||
port_cfg->speed = MAC_SPD_2500;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_info(gsw->dev, "incorrect speed %d\n",
|
||||
speed);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
port_cfg->stag_on =
|
||||
of_property_read_bool(port_cfg->np, "airoha,stag-on");
|
||||
port_cfg->enabled = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void an8855_add_gsw(struct gsw_an8855 *gsw)
|
||||
{
|
||||
mutex_lock(&an8855_devs_lock);
|
||||
gsw->id = an8855_gsw_id++;
|
||||
INIT_LIST_HEAD(&gsw->list);
|
||||
list_add_tail(&gsw->list, &an8855_devs);
|
||||
mutex_unlock(&an8855_devs_lock);
|
||||
}
|
||||
|
||||
static void an8855_remove_gsw(struct gsw_an8855 *gsw)
|
||||
{
|
||||
mutex_lock(&an8855_devs_lock);
|
||||
list_del(&gsw->list);
|
||||
mutex_unlock(&an8855_devs_lock);
|
||||
}
|
||||
|
||||
struct gsw_an8855 *an8855_get_gsw(u32 id)
|
||||
{
|
||||
struct gsw_an8855 *dev;
|
||||
|
||||
mutex_lock(&an8855_devs_lock);
|
||||
|
||||
list_for_each_entry(dev, &an8855_devs, list) {
|
||||
if (dev->id == id)
|
||||
return dev;
|
||||
}
|
||||
|
||||
mutex_unlock(&an8855_devs_lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct gsw_an8855 *an8855_get_first_gsw(void)
|
||||
{
|
||||
struct gsw_an8855 *dev;
|
||||
|
||||
mutex_lock(&an8855_devs_lock);
|
||||
|
||||
list_for_each_entry(dev, &an8855_devs, list)
|
||||
return dev;
|
||||
|
||||
mutex_unlock(&an8855_devs_lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void an8855_put_gsw(void)
|
||||
{
|
||||
mutex_unlock(&an8855_devs_lock);
|
||||
}
|
||||
|
||||
void an8855_lock_gsw(void)
|
||||
{
|
||||
mutex_lock(&an8855_devs_lock);
|
||||
}
|
||||
|
||||
static int an8855_hw_reset(struct gsw_an8855 *gsw)
|
||||
{
|
||||
struct device_node *np = gsw->dev->of_node;
|
||||
int ret;
|
||||
|
||||
gsw->reset_pin = of_get_named_gpio(np, "reset-gpios", 0);
|
||||
if (gsw->reset_pin < 0) {
|
||||
dev_info(gsw->dev, "No reset pin of switch\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = devm_gpio_request(gsw->dev, gsw->reset_pin, "an8855-reset");
|
||||
if (ret) {
|
||||
dev_info(gsw->dev, "Failed to request gpio %d\n",
|
||||
gsw->reset_pin);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gpio_direction_output(gsw->reset_pin, 0);
|
||||
usleep_range(100000, 150000);
|
||||
gpio_set_value(gsw->reset_pin, 1);
|
||||
usleep_range(100000, 150000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t an8855_irq_handler(int irq, void *dev)
|
||||
{
|
||||
struct gsw_an8855 *gsw = dev;
|
||||
|
||||
disable_irq_nosync(gsw->irq);
|
||||
|
||||
schedule_work(&gsw->irq_worker);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int an8855_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct gsw_an8855 *gsw;
|
||||
struct an8855_sw_id *sw;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device_node *mdio;
|
||||
struct mii_bus *mdio_bus;
|
||||
int ret = -EINVAL;
|
||||
struct chip_rev rev;
|
||||
struct an8855_mapping *map;
|
||||
int i;
|
||||
|
||||
mdio = of_parse_phandle(np, "airoha,mdio", 0);
|
||||
if (!mdio)
|
||||
return -EINVAL;
|
||||
|
||||
mdio_bus = of_mdio_find_bus(mdio);
|
||||
if (!mdio_bus)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
gsw = devm_kzalloc(&pdev->dev, sizeof(struct gsw_an8855), GFP_KERNEL);
|
||||
if (!gsw)
|
||||
return -ENOMEM;
|
||||
|
||||
gsw->host_bus = mdio_bus;
|
||||
gsw->dev = &pdev->dev;
|
||||
|
||||
dev_info(gsw->dev, "AN8855 Driver Version=%s\n",
|
||||
ARHT_AN8855_SWCFG_DRIVER_VER);
|
||||
|
||||
/* Switch hard reset */
|
||||
if (an8855_hw_reset(gsw)) {
|
||||
dev_info(&pdev->dev, "reset switch fail.\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Fetch the SMI address first */
|
||||
gsw->smi_addr = AN8855_DFL_SMI_ADDR;
|
||||
if (of_property_read_u32(np, "airoha,smi-addr", &gsw->new_smi_addr))
|
||||
gsw->new_smi_addr = AN8855_DFL_SMI_ADDR;
|
||||
|
||||
/* Get LAN/WAN port mapping */
|
||||
map = an8855_find_mapping(np);
|
||||
if (map) {
|
||||
an8855_apply_mapping(gsw, map);
|
||||
gsw->global_vlan_enable = 1;
|
||||
dev_info(gsw->dev, "LAN/WAN VLAN setting=%s\n", map->name);
|
||||
}
|
||||
|
||||
/* Load MAC port configurations */
|
||||
an8855_load_port_cfg(gsw);
|
||||
|
||||
/* Check for valid switch and then initialize */
|
||||
an8855_gsw_id = 0;
|
||||
for (i = 0; i < ARRAY_SIZE(an8855_sw_ids); i++) {
|
||||
if (!an8855_sw_ids[i]->detect(gsw, &rev)) {
|
||||
sw = an8855_sw_ids[i];
|
||||
|
||||
gsw->name = rev.name;
|
||||
gsw->model = sw->model;
|
||||
|
||||
dev_info(gsw->dev, "Switch is Airoha %s rev %d",
|
||||
gsw->name, rev.rev);
|
||||
|
||||
/* Initialize the switch */
|
||||
ret = sw->init(gsw);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= ARRAY_SIZE(an8855_sw_ids)) {
|
||||
dev_err(gsw->dev, "No an8855 switch found\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
gsw->irq = platform_get_irq(pdev, 0);
|
||||
if (gsw->irq >= 0) {
|
||||
ret = devm_request_irq(gsw->dev, gsw->irq, an8855_irq_handler,
|
||||
0, dev_name(gsw->dev), gsw);
|
||||
if (ret) {
|
||||
dev_err(gsw->dev, "Failed to request irq %d\n",
|
||||
gsw->irq);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
INIT_WORK(&gsw->irq_worker, an8855_irq_worker);
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, gsw);
|
||||
|
||||
an8855_add_gsw(gsw);
|
||||
|
||||
an8855_gsw_nl_init();
|
||||
|
||||
an8855_swconfig_init(gsw);
|
||||
|
||||
if (sw->post_init)
|
||||
sw->post_init(gsw);
|
||||
|
||||
if (gsw->irq >= 0)
|
||||
an8855_irq_enable(gsw);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
devm_kfree(&pdev->dev, gsw);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int an8855_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct gsw_an8855 *gsw = platform_get_drvdata(pdev);
|
||||
|
||||
if (gsw->irq >= 0)
|
||||
cancel_work_sync(&gsw->irq_worker);
|
||||
|
||||
if (gsw->reset_pin >= 0)
|
||||
devm_gpio_free(&pdev->dev, gsw->reset_pin);
|
||||
|
||||
#ifdef CONFIG_SWCONFIG
|
||||
an8855_swconfig_destroy(gsw);
|
||||
#endif
|
||||
|
||||
an8855_gsw_nl_exit();
|
||||
|
||||
an8855_remove_gsw(gsw);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id an8855_ids[] = {
|
||||
{.compatible = "airoha,an8855"},
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, an8855_ids);
|
||||
|
||||
static struct platform_driver an8855_driver = {
|
||||
.probe = an8855_probe,
|
||||
.remove = an8855_remove,
|
||||
.driver = {
|
||||
.name = "an8855",
|
||||
.of_match_table = an8855_ids,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init an8855_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
INIT_LIST_HEAD(&an8855_devs);
|
||||
ret = platform_driver_register(&an8855_driver);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
module_init(an8855_init);
|
||||
|
||||
static void __exit an8855_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&an8855_driver);
|
||||
}
|
||||
|
||||
module_exit(an8855_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Min Yao <min.yao@airoha.com>");
|
||||
MODULE_VERSION(ARHT_AN8855_SWCFG_DRIVER_VER);
|
||||
MODULE_DESCRIPTION("Driver for Airoha AN8855 Gigabit Switch");
|
||||
@@ -0,0 +1,384 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2023 Airoha Inc.
|
||||
* Author: Min Yao <min.yao@airoha.com>
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <net/genetlink.h>
|
||||
|
||||
#include "an8855.h"
|
||||
#include "an8855_nl.h"
|
||||
|
||||
struct an8855_nl_cmd_item {
|
||||
enum an8855_cmd cmd;
|
||||
bool require_dev;
|
||||
int (*process)(struct genl_info *info, struct gsw_an8855 *gsw);
|
||||
u32 nr_required_attrs;
|
||||
const enum an8855_attr *required_attrs;
|
||||
};
|
||||
|
||||
static int an8855_nl_response(struct sk_buff *skb, struct genl_info *info);
|
||||
|
||||
static const struct nla_policy an8855_nl_cmd_policy[] = {
|
||||
[AN8855_ATTR_TYPE_MESG] = { .type = NLA_STRING },
|
||||
[AN8855_ATTR_TYPE_PHY] = { .type = NLA_S32 },
|
||||
[AN8855_ATTR_TYPE_REG] = { .type = NLA_S32 },
|
||||
[AN8855_ATTR_TYPE_VAL] = { .type = NLA_S32 },
|
||||
[AN8855_ATTR_TYPE_DEV_NAME] = { .type = NLA_S32 },
|
||||
[AN8855_ATTR_TYPE_DEV_ID] = { .type = NLA_S32 },
|
||||
[AN8855_ATTR_TYPE_DEVAD] = { .type = NLA_S32 },
|
||||
};
|
||||
|
||||
static const struct genl_ops an8855_nl_ops[] = {
|
||||
{
|
||||
.cmd = AN8855_CMD_REQUEST,
|
||||
.doit = an8855_nl_response,
|
||||
// .policy = an8855_nl_cmd_policy,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
}, {
|
||||
.cmd = AN8855_CMD_READ,
|
||||
.doit = an8855_nl_response,
|
||||
// .policy = an8855_nl_cmd_policy,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
}, {
|
||||
.cmd = AN8855_CMD_WRITE,
|
||||
.doit = an8855_nl_response,
|
||||
// .policy = an8855_nl_cmd_policy,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
};
|
||||
|
||||
static struct genl_family an8855_nl_family = {
|
||||
.name = AN8855_GENL_NAME,
|
||||
.version = AN8855_GENL_VERSION,
|
||||
.maxattr = AN8855_NR_ATTR_TYPE,
|
||||
.ops = an8855_nl_ops,
|
||||
.n_ops = ARRAY_SIZE(an8855_nl_ops),
|
||||
.policy = an8855_nl_cmd_policy,
|
||||
};
|
||||
|
||||
static int an8855_nl_list_devs(char *buff, int size)
|
||||
{
|
||||
struct gsw_an8855 *gsw;
|
||||
int len, total = 0;
|
||||
char buf[80];
|
||||
|
||||
memset(buff, 0, size);
|
||||
|
||||
an8855_lock_gsw();
|
||||
|
||||
list_for_each_entry(gsw, &an8855_devs, list) {
|
||||
len = snprintf(buf, sizeof(buf),
|
||||
"id: %d, model: %s, node: %s\n",
|
||||
gsw->id, gsw->name, gsw->dev->of_node->name);
|
||||
if (len == strlen(buf)) {
|
||||
if (size - total > 0)
|
||||
strncat(buff, buf, size - total);
|
||||
total += len;
|
||||
}
|
||||
}
|
||||
|
||||
an8855_put_gsw();
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
static int an8855_nl_prepare_reply(struct genl_info *info, u8 cmd,
|
||||
struct sk_buff **skbp)
|
||||
{
|
||||
struct sk_buff *msg;
|
||||
void *reply;
|
||||
|
||||
if (!info)
|
||||
return -EINVAL;
|
||||
|
||||
msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Construct send-back message header */
|
||||
reply = genlmsg_put(msg, info->snd_portid, info->snd_seq,
|
||||
&an8855_nl_family, 0, cmd);
|
||||
if (!reply) {
|
||||
nlmsg_free(msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*skbp = msg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int an8855_nl_send_reply(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct genlmsghdr *genlhdr = nlmsg_data(nlmsg_hdr(skb));
|
||||
void *reply = genlmsg_data(genlhdr);
|
||||
|
||||
/* Finalize a generic netlink message (update message header) */
|
||||
genlmsg_end(skb, reply);
|
||||
|
||||
/* reply to a request */
|
||||
return genlmsg_reply(skb, info);
|
||||
}
|
||||
|
||||
static s32 an8855_nl_get_s32(struct genl_info *info, enum an8855_attr attr,
|
||||
s32 defval)
|
||||
{
|
||||
struct nlattr *na;
|
||||
|
||||
na = info->attrs[attr];
|
||||
if (na)
|
||||
return nla_get_s32(na);
|
||||
|
||||
return defval;
|
||||
}
|
||||
|
||||
static int an8855_nl_get_u32(struct genl_info *info, enum an8855_attr attr,
|
||||
u32 *val)
|
||||
{
|
||||
struct nlattr *na;
|
||||
|
||||
na = info->attrs[attr];
|
||||
if (na) {
|
||||
*val = nla_get_u32(na);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct gsw_an8855 *an8855_nl_parse_find_gsw(struct genl_info *info)
|
||||
{
|
||||
struct gsw_an8855 *gsw;
|
||||
struct nlattr *na;
|
||||
int gsw_id;
|
||||
|
||||
na = info->attrs[AN8855_ATTR_TYPE_DEV_ID];
|
||||
if (na) {
|
||||
gsw_id = nla_get_s32(na);
|
||||
if (gsw_id >= 0)
|
||||
gsw = an8855_get_gsw(gsw_id);
|
||||
else
|
||||
gsw = an8855_get_first_gsw();
|
||||
} else {
|
||||
gsw = an8855_get_first_gsw();
|
||||
}
|
||||
|
||||
return gsw;
|
||||
}
|
||||
|
||||
static int an8855_nl_get_swdevs(struct genl_info *info, struct gsw_an8855 *gsw)
|
||||
{
|
||||
struct sk_buff *rep_skb = NULL;
|
||||
char dev_info[512];
|
||||
int ret;
|
||||
|
||||
ret = an8855_nl_list_devs(dev_info, sizeof(dev_info) - 1);
|
||||
if (!ret) {
|
||||
pr_info("No switch registered\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = an8855_nl_prepare_reply(info, AN8855_CMD_REPLY, &rep_skb);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
ret = nla_put_string(rep_skb, AN8855_ATTR_TYPE_MESG, dev_info);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
return an8855_nl_send_reply(rep_skb, info);
|
||||
|
||||
err:
|
||||
if (rep_skb)
|
||||
nlmsg_free(rep_skb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int an8855_nl_reply_read(struct genl_info *info, struct gsw_an8855 *gsw)
|
||||
{
|
||||
struct sk_buff *rep_skb = NULL;
|
||||
s32 phy, devad, reg;
|
||||
int value;
|
||||
int ret = 0;
|
||||
|
||||
phy = an8855_nl_get_s32(info, AN8855_ATTR_TYPE_PHY, -1);
|
||||
devad = an8855_nl_get_s32(info, AN8855_ATTR_TYPE_DEVAD, -1);
|
||||
reg = an8855_nl_get_s32(info, AN8855_ATTR_TYPE_REG, -1);
|
||||
|
||||
if (reg < 0)
|
||||
goto err;
|
||||
|
||||
ret = an8855_nl_prepare_reply(info, AN8855_CMD_READ, &rep_skb);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
if (phy >= 0) {
|
||||
if (devad < 0)
|
||||
value = gsw->mii_read(gsw, phy, reg);
|
||||
else
|
||||
value = gsw->mmd_read(gsw, phy, devad, reg);
|
||||
} else {
|
||||
value = an8855_reg_read(gsw, reg);
|
||||
}
|
||||
|
||||
ret = nla_put_s32(rep_skb, AN8855_ATTR_TYPE_REG, reg);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
ret = nla_put_s32(rep_skb, AN8855_ATTR_TYPE_VAL, value);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
return an8855_nl_send_reply(rep_skb, info);
|
||||
|
||||
err:
|
||||
if (rep_skb)
|
||||
nlmsg_free(rep_skb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int an8855_nl_reply_write(struct genl_info *info, struct gsw_an8855 *gsw)
|
||||
{
|
||||
struct sk_buff *rep_skb = NULL;
|
||||
s32 phy, devad, reg;
|
||||
u32 value;
|
||||
int ret = 0;
|
||||
|
||||
phy = an8855_nl_get_s32(info, AN8855_ATTR_TYPE_PHY, -1);
|
||||
devad = an8855_nl_get_s32(info, AN8855_ATTR_TYPE_DEVAD, -1);
|
||||
reg = an8855_nl_get_s32(info, AN8855_ATTR_TYPE_REG, -1);
|
||||
|
||||
if (an8855_nl_get_u32(info, AN8855_ATTR_TYPE_VAL, &value))
|
||||
goto err;
|
||||
|
||||
if (reg < 0)
|
||||
goto err;
|
||||
|
||||
ret = an8855_nl_prepare_reply(info, AN8855_CMD_WRITE, &rep_skb);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
if (phy >= 0) {
|
||||
if (devad < 0)
|
||||
gsw->mii_write(gsw, phy, reg, value);
|
||||
else
|
||||
gsw->mmd_write(gsw, phy, devad, reg, value);
|
||||
} else {
|
||||
an8855_reg_write(gsw, reg, value);
|
||||
}
|
||||
|
||||
ret = nla_put_s32(rep_skb, AN8855_ATTR_TYPE_REG, reg);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
ret = nla_put_s32(rep_skb, AN8855_ATTR_TYPE_VAL, value);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
return an8855_nl_send_reply(rep_skb, info);
|
||||
|
||||
err:
|
||||
if (rep_skb)
|
||||
nlmsg_free(rep_skb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const enum an8855_attr an8855_nl_cmd_read_attrs[] = {
|
||||
AN8855_ATTR_TYPE_REG
|
||||
};
|
||||
|
||||
static const enum an8855_attr an8855_nl_cmd_write_attrs[] = {
|
||||
AN8855_ATTR_TYPE_REG,
|
||||
AN8855_ATTR_TYPE_VAL
|
||||
};
|
||||
|
||||
static const struct an8855_nl_cmd_item an8855_nl_cmds[] = {
|
||||
{
|
||||
.cmd = AN8855_CMD_REQUEST,
|
||||
.require_dev = false,
|
||||
.process = an8855_nl_get_swdevs
|
||||
}, {
|
||||
.cmd = AN8855_CMD_READ,
|
||||
.require_dev = true,
|
||||
.process = an8855_nl_reply_read,
|
||||
.required_attrs = an8855_nl_cmd_read_attrs,
|
||||
.nr_required_attrs = ARRAY_SIZE(an8855_nl_cmd_read_attrs),
|
||||
}, {
|
||||
.cmd = AN8855_CMD_WRITE,
|
||||
.require_dev = true,
|
||||
.process = an8855_nl_reply_write,
|
||||
.required_attrs = an8855_nl_cmd_write_attrs,
|
||||
.nr_required_attrs = ARRAY_SIZE(an8855_nl_cmd_write_attrs),
|
||||
}
|
||||
};
|
||||
|
||||
static int an8855_nl_response(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct genlmsghdr *hdr = nlmsg_data(info->nlhdr);
|
||||
const struct an8855_nl_cmd_item *cmditem = NULL;
|
||||
struct gsw_an8855 *gsw = NULL;
|
||||
u32 sat_req_attrs = 0;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(an8855_nl_cmds); i++) {
|
||||
if (hdr->cmd == an8855_nl_cmds[i].cmd) {
|
||||
cmditem = &an8855_nl_cmds[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!cmditem) {
|
||||
pr_info("an8855-nl: unknown cmd %u\n", hdr->cmd);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < cmditem->nr_required_attrs; i++) {
|
||||
if (info->attrs[cmditem->required_attrs[i]])
|
||||
sat_req_attrs++;
|
||||
}
|
||||
|
||||
if (sat_req_attrs != cmditem->nr_required_attrs) {
|
||||
pr_info("an8855-nl: missing required attr(s) for cmd %u\n",
|
||||
hdr->cmd);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (cmditem->require_dev) {
|
||||
gsw = an8855_nl_parse_find_gsw(info);
|
||||
if (!gsw) {
|
||||
pr_info("an8855-nl: failed to find switch dev\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
ret = cmditem->process(info, gsw);
|
||||
|
||||
an8855_put_gsw();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int an8855_gsw_nl_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = genl_register_family(&an8855_nl_family);
|
||||
if (ret) {
|
||||
pr_info("an8855-nl: genl_register_family_with_ops failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void an8855_gsw_nl_exit(void)
|
||||
{
|
||||
genl_unregister_family(&an8855_nl_family);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2023 Airoha Inc.
|
||||
* Author: Min Yao <min.yao@airoha.com>
|
||||
*/
|
||||
|
||||
#ifndef _AN8855_NL_H_
|
||||
#define _AN8855_NL_H_
|
||||
|
||||
#define AN8855_GENL_NAME "an8855"
|
||||
#define AN8855_GENL_VERSION 0x1
|
||||
|
||||
enum an8855_cmd {
|
||||
AN8855_CMD_UNSPEC = 0,
|
||||
AN8855_CMD_REQUEST,
|
||||
AN8855_CMD_REPLY,
|
||||
AN8855_CMD_READ,
|
||||
AN8855_CMD_WRITE,
|
||||
|
||||
__AN8855_CMD_MAX,
|
||||
};
|
||||
|
||||
enum an8855_attr {
|
||||
AN8855_ATTR_TYPE_UNSPEC = 0,
|
||||
AN8855_ATTR_TYPE_MESG,
|
||||
AN8855_ATTR_TYPE_PHY,
|
||||
AN8855_ATTR_TYPE_DEVAD,
|
||||
AN8855_ATTR_TYPE_REG,
|
||||
AN8855_ATTR_TYPE_VAL,
|
||||
AN8855_ATTR_TYPE_DEV_NAME,
|
||||
AN8855_ATTR_TYPE_DEV_ID,
|
||||
|
||||
__AN8855_ATTR_TYPE_MAX,
|
||||
};
|
||||
|
||||
#define AN8855_NR_ATTR_TYPE (__AN8855_ATTR_TYPE_MAX - 1)
|
||||
|
||||
#ifdef __KERNEL__
|
||||
int an8855_gsw_nl_init(void);
|
||||
void an8855_gsw_nl_exit(void);
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#endif /* _AN8855_NL_H_ */
|
||||
@@ -0,0 +1,188 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2023 Airoha Inc.
|
||||
* Author: Min Yao <min.yao@airoha.com>
|
||||
*/
|
||||
|
||||
#ifndef _AN8855_REGS_H_
|
||||
#define _AN8855_REGS_H_
|
||||
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#define BITS(m, n) (~(BIT(m) - 1) & ((BIT(n) - 1) | BIT(n)))
|
||||
|
||||
/* Values of Egress TAG Control */
|
||||
#define ETAG_CTRL_UNTAG 0
|
||||
#define ETAG_CTRL_TAG 2
|
||||
#define ETAG_CTRL_SWAP 1
|
||||
#define ETAG_CTRL_STACK 3
|
||||
|
||||
#define VTCR 0x10200600
|
||||
#define VAWD0 0x10200604
|
||||
#define VAWD1 0x10200608
|
||||
#define VARD0 0x10200618
|
||||
#define VARD1 0x1020061C
|
||||
|
||||
/* Fields of VTCR */
|
||||
#define VTCR_BUSY BIT(31)
|
||||
#define VTCR_FUNC_S 12
|
||||
#define VTCR_FUNC_M 0xf000
|
||||
#define VTCR_VID_S 0
|
||||
#define VTCR_VID_M 0xfff
|
||||
|
||||
/* Values of VTCR_FUNC */
|
||||
#define VTCR_READ_VLAN_ENTRY 0
|
||||
#define VTCR_WRITE_VLAN_ENTRY 1
|
||||
|
||||
/* VLAN entry fields */
|
||||
#define IVL_MAC BIT(5)
|
||||
#define EG_CON BIT(11)
|
||||
#define VTAG_EN BIT(10)
|
||||
#define PORT_MEM_S 26
|
||||
#define PORT_MEM_M 0xfc000000
|
||||
#define VENTRY_VALID BIT(0)
|
||||
#define ETAG_M 0x3fff000
|
||||
#define PORT_ETAG_S(p) (((p) * 2) + 12)
|
||||
#define PORT_ETAG_M 0x03
|
||||
|
||||
#define PORT_CTRL_BASE 0x10208000
|
||||
#define PORT_CTRL_PORT_OFFSET 0x200
|
||||
#define PORT_CTRL_REG(p, r) (PORT_CTRL_BASE + \
|
||||
(p) * PORT_CTRL_PORT_OFFSET + (r))
|
||||
#define PCR(p) PORT_CTRL_REG(p, 0x04)
|
||||
#define PVC(p) PORT_CTRL_REG(p, 0x10)
|
||||
#define PORTMATRIX(p) PORT_CTRL_REG(p, 0x44)
|
||||
#define PVID(p) PORT_CTRL_REG(p, 0x48)
|
||||
|
||||
#define GRP_PORT_VID_M 0xfff
|
||||
|
||||
/* Values of PORT_VLAN */
|
||||
#define PORT_MATRIX_MODE 0
|
||||
#define FALLBACK_MODE 1
|
||||
#define CHECK_MODE 2
|
||||
#define SECURITY_MODE 3
|
||||
|
||||
/* Fields of PVC */
|
||||
#define STAG_VPID_S 16
|
||||
#define STAG_VPID_M 0xffff0000
|
||||
#define VLAN_ATTR_S 6
|
||||
#define VLAN_ATTR_M 0xc0
|
||||
#define PVC_STAG_REPLACE BIT(11)
|
||||
#define PVC_PORT_STAG BIT(5)
|
||||
|
||||
/* Values of VLAN_ATTR */
|
||||
#define VA_USER_PORT 0
|
||||
#define VA_STACK_PORT 1
|
||||
#define VA_TRANSLATION_PORT 2
|
||||
#define VA_TRANSPARENT_PORT 3
|
||||
|
||||
#define PORT_MATRIX_M ((1 << AN8855_NUM_PORTS) - 1)
|
||||
|
||||
#define PORT_MAC_CTRL_BASE 0x10210000
|
||||
#define PORT_MAC_CTRL_PORT_OFFSET 0x200
|
||||
#define PORT_MAC_CTRL_REG(p, r) (PORT_MAC_CTRL_BASE + \
|
||||
(p) * PORT_MAC_CTRL_PORT_OFFSET + (r))
|
||||
#define PMCR(p) PORT_MAC_CTRL_REG(p, 0x00)
|
||||
#define PMSR(p) PORT_MAC_CTRL_REG(p, 0x10)
|
||||
|
||||
#define GMACCR (PORT_MAC_CTRL_BASE + 0x30e0)
|
||||
|
||||
#define MAX_RX_JUMBO_S 2
|
||||
#define MAX_RX_JUMBO_M 0x3c
|
||||
#define MAX_RX_PKT_LEN_S 0
|
||||
#define MAX_RX_PKT_LEN_M 0x3
|
||||
|
||||
/* Values of MAX_RX_PKT_LEN */
|
||||
#define RX_PKT_LEN_1518 0
|
||||
#define RX_PKT_LEN_1536 1
|
||||
#define RX_PKT_LEN_1522 2
|
||||
#define RX_PKT_LEN_MAX_JUMBO 3
|
||||
|
||||
/* Fields of PMSR */
|
||||
#define EEE1G_STS BIT(7)
|
||||
#define EEE100_STS BIT(6)
|
||||
#define RX_FC_STS BIT(5)
|
||||
#define TX_FC_STS BIT(4)
|
||||
#define MAC_SPD_STS_S 28
|
||||
#define MAC_SPD_STS_M 0x70000000
|
||||
#define MAC_DPX_STS BIT(25)
|
||||
#define MAC_LNK_STS BIT(24)
|
||||
|
||||
/* Values of MAC_SPD_STS */
|
||||
#define MAC_SPD_10 0
|
||||
#define MAC_SPD_100 1
|
||||
#define MAC_SPD_1000 2
|
||||
#define MAC_SPD_2500 3
|
||||
|
||||
#define MIB_COUNTER_BASE 0x10214000
|
||||
#define MIB_COUNTER_PORT_OFFSET 0x200
|
||||
#define MIB_COUNTER_REG(p, r) (MIB_COUNTER_BASE + \
|
||||
(p) * MIB_COUNTER_PORT_OFFSET + (r))
|
||||
#define STATS_TDPC 0x00
|
||||
#define STATS_TCRC 0x04
|
||||
#define STATS_TUPC 0x08
|
||||
#define STATS_TMPC 0x0C
|
||||
#define STATS_TBPC 0x10
|
||||
#define STATS_TCEC 0x14
|
||||
#define STATS_TSCEC 0x18
|
||||
#define STATS_TMCEC 0x1C
|
||||
#define STATS_TDEC 0x20
|
||||
#define STATS_TLCEC 0x24
|
||||
#define STATS_TXCEC 0x28
|
||||
#define STATS_TPPC 0x2C
|
||||
#define STATS_TL64PC 0x30
|
||||
#define STATS_TL65PC 0x34
|
||||
#define STATS_TL128PC 0x38
|
||||
#define STATS_TL256PC 0x3C
|
||||
#define STATS_TL512PC 0x40
|
||||
#define STATS_TL1024PC 0x44
|
||||
#define STATS_TL1519PC 0x48
|
||||
#define STATS_TOC 0x4C
|
||||
#define STATS_TODPC 0x54
|
||||
#define STATS_TOC2 0x58
|
||||
|
||||
#define STATS_RDPC 0x80
|
||||
#define STATS_RFPC 0x84
|
||||
#define STATS_RUPC 0x88
|
||||
#define STATS_RMPC 0x8C
|
||||
#define STATS_RBPC 0x90
|
||||
#define STATS_RAEPC 0x94
|
||||
#define STATS_RCEPC 0x98
|
||||
#define STATS_RUSPC 0x9C
|
||||
#define STATS_RFEPC 0xA0
|
||||
#define STATS_ROSPC 0xA4
|
||||
#define STATS_RJEPC 0xA8
|
||||
#define STATS_RPPC 0xAC
|
||||
#define STATS_RL64PC 0xB0
|
||||
#define STATS_RL65PC 0xB4
|
||||
#define STATS_RL128PC 0xB8
|
||||
#define STATS_RL256PC 0xBC
|
||||
#define STATS_RL512PC 0xC0
|
||||
#define STATS_RL1024PC 0xC4
|
||||
#define STATS_RL1519PC 0xC8
|
||||
#define STATS_ROC 0xCC
|
||||
#define STATS_RDPC_CTRL 0xD4
|
||||
#define STATS_RDPC_ING 0xD8
|
||||
#define STATS_RDPC_ARL 0xDC
|
||||
#define STATS_RDPC_FC 0xE0
|
||||
#define STATS_RDPC_WRED 0xE4
|
||||
#define STATS_RDPC_MIR 0xE8
|
||||
#define STATS_ROC2 0xEC
|
||||
#define STATS_RSFSPC 0xF4
|
||||
#define STATS_RSFTPC 0xF8
|
||||
#define STATS_RXCDPC 0xFC
|
||||
|
||||
#define SYS_CTRL 0x100050C0
|
||||
#define SW_SYS_RST BIT(31)
|
||||
|
||||
#define INT_MASK 0x100050F0
|
||||
#define INT_SYS_BIT BIT(15)
|
||||
|
||||
#define SYS_INT_EN 0x1021C010
|
||||
#define SYS_INT_STS 0x1021C014
|
||||
#define PHY_LC_INT(p) BIT(p)
|
||||
|
||||
#define CKGCR (0x10213E1C)
|
||||
#define CKG_LNKDN_GLB_STOP (0x01)
|
||||
#define CKG_LNKDN_PORT_STOP (0x02)
|
||||
#endif /* _AN8855_REGS_H_ */
|
||||
@@ -0,0 +1,527 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2023 Airoha Inc.
|
||||
* Author: Min Yao <min.yao@airoha.com>
|
||||
*/
|
||||
|
||||
#include <linux/if.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <net/genetlink.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/lockdep.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include "an8855.h"
|
||||
#include "an8855_swconfig.h"
|
||||
#include "an8855_regs.h"
|
||||
|
||||
#define AN8855_PORT_MIB_TXB_ID 19 /* TxByte */
|
||||
#define AN8855_PORT_MIB_RXB_ID 40 /* RxByte */
|
||||
|
||||
#define MIB_DESC(_s, _o, _n) \
|
||||
{ \
|
||||
.size = (_s), \
|
||||
.offset = (_o), \
|
||||
.name = (_n), \
|
||||
}
|
||||
|
||||
struct an8855_mib_desc {
|
||||
unsigned int size;
|
||||
unsigned int offset;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
static const struct an8855_mib_desc an8855_mibs[] = {
|
||||
MIB_DESC(1, STATS_TDPC, "TxDrop"),
|
||||
MIB_DESC(1, STATS_TCRC, "TxCRC"),
|
||||
MIB_DESC(1, STATS_TUPC, "TxUni"),
|
||||
MIB_DESC(1, STATS_TMPC, "TxMulti"),
|
||||
MIB_DESC(1, STATS_TBPC, "TxBroad"),
|
||||
MIB_DESC(1, STATS_TCEC, "TxCollision"),
|
||||
MIB_DESC(1, STATS_TSCEC, "TxSingleCol"),
|
||||
MIB_DESC(1, STATS_TMCEC, "TxMultiCol"),
|
||||
MIB_DESC(1, STATS_TDEC, "TxDefer"),
|
||||
MIB_DESC(1, STATS_TLCEC, "TxLateCol"),
|
||||
MIB_DESC(1, STATS_TXCEC, "TxExcCol"),
|
||||
MIB_DESC(1, STATS_TPPC, "TxPause"),
|
||||
MIB_DESC(1, STATS_TL64PC, "Tx64Byte"),
|
||||
MIB_DESC(1, STATS_TL65PC, "Tx65Byte"),
|
||||
MIB_DESC(1, STATS_TL128PC, "Tx128Byte"),
|
||||
MIB_DESC(1, STATS_TL256PC, "Tx256Byte"),
|
||||
MIB_DESC(1, STATS_TL512PC, "Tx512Byte"),
|
||||
MIB_DESC(1, STATS_TL1024PC, "Tx1024Byte"),
|
||||
MIB_DESC(1, STATS_TL1519PC, "Tx1519Byte"),
|
||||
MIB_DESC(2, STATS_TOC, "TxByte"),
|
||||
MIB_DESC(1, STATS_TODPC, "TxOverSize"),
|
||||
MIB_DESC(2, STATS_TOC2, "SecondaryTxByte"),
|
||||
MIB_DESC(1, STATS_RDPC, "RxDrop"),
|
||||
MIB_DESC(1, STATS_RFPC, "RxFiltered"),
|
||||
MIB_DESC(1, STATS_RUPC, "RxUni"),
|
||||
MIB_DESC(1, STATS_RMPC, "RxMulti"),
|
||||
MIB_DESC(1, STATS_RBPC, "RxBroad"),
|
||||
MIB_DESC(1, STATS_RAEPC, "RxAlignErr"),
|
||||
MIB_DESC(1, STATS_RCEPC, "RxCRC"),
|
||||
MIB_DESC(1, STATS_RUSPC, "RxUnderSize"),
|
||||
MIB_DESC(1, STATS_RFEPC, "RxFragment"),
|
||||
MIB_DESC(1, STATS_ROSPC, "RxOverSize"),
|
||||
MIB_DESC(1, STATS_RJEPC, "RxJabber"),
|
||||
MIB_DESC(1, STATS_RPPC, "RxPause"),
|
||||
MIB_DESC(1, STATS_RL64PC, "Rx64Byte"),
|
||||
MIB_DESC(1, STATS_RL65PC, "Rx65Byte"),
|
||||
MIB_DESC(1, STATS_RL128PC, "Rx128Byte"),
|
||||
MIB_DESC(1, STATS_RL256PC, "Rx256Byte"),
|
||||
MIB_DESC(1, STATS_RL512PC, "Rx512Byte"),
|
||||
MIB_DESC(1, STATS_RL1024PC, "Rx1024Byte"),
|
||||
MIB_DESC(2, STATS_ROC, "RxByte"),
|
||||
MIB_DESC(1, STATS_RDPC_CTRL, "RxCtrlDrop"),
|
||||
MIB_DESC(1, STATS_RDPC_ING, "RxIngDrop"),
|
||||
MIB_DESC(1, STATS_RDPC_ARL, "RxARLDrop"),
|
||||
MIB_DESC(1, STATS_RDPC_FC, "RxFCDrop"),
|
||||
MIB_DESC(1, STATS_RDPC_WRED, "RxWREDDrop"),
|
||||
MIB_DESC(1, STATS_RDPC_MIR, "RxMIRDrop"),
|
||||
MIB_DESC(2, STATS_ROC2, "SecondaryRxByte"),
|
||||
MIB_DESC(1, STATS_RSFSPC, "RxsFlowSampling"),
|
||||
MIB_DESC(1, STATS_RSFTPC, "RxsFlowTotal"),
|
||||
MIB_DESC(1, STATS_RXCDPC, "RxPortDrop"),
|
||||
};
|
||||
|
||||
enum {
|
||||
/* Global attributes. */
|
||||
AN8855_ATTR_ENABLE_VLAN,
|
||||
};
|
||||
|
||||
static int an8855_get_vlan_enable(struct switch_dev *dev,
|
||||
const struct switch_attr *attr,
|
||||
struct switch_val *val)
|
||||
{
|
||||
struct gsw_an8855 *gsw = container_of(dev, struct gsw_an8855, swdev);
|
||||
|
||||
val->value.i = gsw->global_vlan_enable;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int an8855_set_vlan_enable(struct switch_dev *dev,
|
||||
const struct switch_attr *attr,
|
||||
struct switch_val *val)
|
||||
{
|
||||
struct gsw_an8855 *gsw = container_of(dev, struct gsw_an8855, swdev);
|
||||
|
||||
gsw->global_vlan_enable = val->value.i != 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int an8855_get_port_pvid(struct switch_dev *dev, int port, int *val)
|
||||
{
|
||||
struct gsw_an8855 *gsw = container_of(dev, struct gsw_an8855, swdev);
|
||||
|
||||
if (port >= AN8855_NUM_PORTS)
|
||||
return -EINVAL;
|
||||
|
||||
*val = an8855_reg_read(gsw, PVID(port));
|
||||
*val &= GRP_PORT_VID_M;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int an8855_set_port_pvid(struct switch_dev *dev, int port, int pvid)
|
||||
{
|
||||
struct gsw_an8855 *gsw = container_of(dev, struct gsw_an8855, swdev);
|
||||
|
||||
if (port >= AN8855_NUM_PORTS)
|
||||
return -EINVAL;
|
||||
|
||||
if (pvid < AN8855_MIN_VID || pvid > AN8855_MAX_VID)
|
||||
return -EINVAL;
|
||||
|
||||
gsw->port_entries[port].pvid = pvid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int an8855_get_vlan_ports(struct switch_dev *dev, struct switch_val *val)
|
||||
{
|
||||
struct gsw_an8855 *gsw = container_of(dev, struct gsw_an8855, swdev);
|
||||
u32 member;
|
||||
u32 etags;
|
||||
int i;
|
||||
|
||||
val->len = 0;
|
||||
|
||||
if (val->port_vlan >= AN8855_NUM_VLANS)
|
||||
return -EINVAL;
|
||||
|
||||
an8855_vlan_ctrl(gsw, VTCR_READ_VLAN_ENTRY, val->port_vlan);
|
||||
|
||||
member = an8855_reg_read(gsw, VARD0);
|
||||
member &= PORT_MEM_M;
|
||||
member >>= PORT_MEM_S;
|
||||
member |= ((an8855_reg_read(gsw, VARD1) & 0x1) << 6);
|
||||
|
||||
etags = an8855_reg_read(gsw, VARD0) & ETAG_M;
|
||||
|
||||
for (i = 0; i < AN8855_NUM_PORTS; i++) {
|
||||
struct switch_port *p;
|
||||
int etag;
|
||||
|
||||
if (!(member & BIT(i)))
|
||||
continue;
|
||||
|
||||
p = &val->value.ports[val->len++];
|
||||
p->id = i;
|
||||
|
||||
etag = (etags >> PORT_ETAG_S(i)) & PORT_ETAG_M;
|
||||
|
||||
if (etag == ETAG_CTRL_TAG)
|
||||
p->flags |= BIT(SWITCH_PORT_FLAG_TAGGED);
|
||||
else if (etag != ETAG_CTRL_UNTAG)
|
||||
dev_info(gsw->dev,
|
||||
"vlan egress tag control neither untag nor tag.\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int an8855_set_vlan_ports(struct switch_dev *dev, struct switch_val *val)
|
||||
{
|
||||
struct gsw_an8855 *gsw = container_of(dev, struct gsw_an8855, swdev);
|
||||
u8 member = 0;
|
||||
u8 etags = 0;
|
||||
int i;
|
||||
|
||||
if (val->port_vlan >= AN8855_NUM_VLANS ||
|
||||
val->len > AN8855_NUM_PORTS)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < val->len; i++) {
|
||||
struct switch_port *p = &val->value.ports[i];
|
||||
|
||||
if (p->id >= AN8855_NUM_PORTS)
|
||||
return -EINVAL;
|
||||
|
||||
member |= BIT(p->id);
|
||||
|
||||
if (p->flags & BIT(SWITCH_PORT_FLAG_TAGGED))
|
||||
etags |= BIT(p->id);
|
||||
}
|
||||
|
||||
gsw->vlan_entries[val->port_vlan].member = member;
|
||||
gsw->vlan_entries[val->port_vlan].etags = etags;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int an8855_set_vid(struct switch_dev *dev,
|
||||
const struct switch_attr *attr,
|
||||
struct switch_val *val)
|
||||
{
|
||||
struct gsw_an8855 *gsw = container_of(dev, struct gsw_an8855, swdev);
|
||||
int vlan;
|
||||
u16 vid;
|
||||
|
||||
vlan = val->port_vlan;
|
||||
vid = (u16)val->value.i;
|
||||
|
||||
if (vlan < 0 || vlan >= AN8855_NUM_VLANS)
|
||||
return -EINVAL;
|
||||
|
||||
if (vid > AN8855_MAX_VID)
|
||||
return -EINVAL;
|
||||
|
||||
gsw->vlan_entries[vlan].vid = vid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int an8855_get_vid(struct switch_dev *dev,
|
||||
const struct switch_attr *attr,
|
||||
struct switch_val *val)
|
||||
{
|
||||
val->value.i = val->port_vlan;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int an8855_get_port_link(struct switch_dev *dev, int port,
|
||||
struct switch_port_link *link)
|
||||
{
|
||||
struct gsw_an8855 *gsw = container_of(dev, struct gsw_an8855, swdev);
|
||||
u32 speed, pmsr;
|
||||
|
||||
if (port < 0 || port >= AN8855_NUM_PORTS)
|
||||
return -EINVAL;
|
||||
|
||||
pmsr = an8855_reg_read(gsw, PMSR(port));
|
||||
|
||||
link->link = pmsr & MAC_LNK_STS;
|
||||
link->duplex = pmsr & MAC_DPX_STS;
|
||||
speed = (pmsr & MAC_SPD_STS_M) >> MAC_SPD_STS_S;
|
||||
|
||||
switch (speed) {
|
||||
case MAC_SPD_10:
|
||||
link->speed = SWITCH_PORT_SPEED_10;
|
||||
break;
|
||||
case MAC_SPD_100:
|
||||
link->speed = SWITCH_PORT_SPEED_100;
|
||||
break;
|
||||
case MAC_SPD_1000:
|
||||
link->speed = SWITCH_PORT_SPEED_1000;
|
||||
break;
|
||||
case MAC_SPD_2500:
|
||||
/* TODO: swconfig has no support for 2500 now */
|
||||
link->speed = SWITCH_PORT_SPEED_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int an8855_set_port_link(struct switch_dev *dev, int port,
|
||||
struct switch_port_link *link)
|
||||
{
|
||||
#ifndef MODULE
|
||||
if (port >= AN8855_NUM_PHYS)
|
||||
return -EINVAL;
|
||||
|
||||
return switch_generic_set_link(dev, port, link);
|
||||
#else
|
||||
return -ENOTSUPP;
|
||||
#endif
|
||||
}
|
||||
|
||||
static u64 get_mib_counter(struct gsw_an8855 *gsw, int i, int port)
|
||||
{
|
||||
unsigned int offset;
|
||||
u64 lo, hi, hi2;
|
||||
|
||||
offset = an8855_mibs[i].offset;
|
||||
|
||||
if (an8855_mibs[i].size == 1)
|
||||
return an8855_reg_read(gsw, MIB_COUNTER_REG(port, offset));
|
||||
|
||||
do {
|
||||
hi = an8855_reg_read(gsw, MIB_COUNTER_REG(port, offset + 4));
|
||||
lo = an8855_reg_read(gsw, MIB_COUNTER_REG(port, offset));
|
||||
hi2 = an8855_reg_read(gsw, MIB_COUNTER_REG(port, offset + 4));
|
||||
} while (hi2 != hi);
|
||||
|
||||
return (hi << 32) | lo;
|
||||
}
|
||||
|
||||
static int an8855_get_port_mib(struct switch_dev *dev,
|
||||
const struct switch_attr *attr,
|
||||
struct switch_val *val)
|
||||
{
|
||||
static char buf[4096];
|
||||
struct gsw_an8855 *gsw = container_of(dev, struct gsw_an8855, swdev);
|
||||
int i, len = 0;
|
||||
|
||||
if (val->port_vlan >= AN8855_NUM_PORTS)
|
||||
return -EINVAL;
|
||||
|
||||
len += snprintf(buf + len, sizeof(buf) - len,
|
||||
"Port %d MIB counters\n", val->port_vlan);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(an8855_mibs); ++i) {
|
||||
u64 counter;
|
||||
|
||||
len += snprintf(buf + len, sizeof(buf) - len,
|
||||
"%-11s: ", an8855_mibs[i].name);
|
||||
counter = get_mib_counter(gsw, i, val->port_vlan);
|
||||
len += snprintf(buf + len, sizeof(buf) - len, "%llu\n",
|
||||
counter);
|
||||
}
|
||||
|
||||
val->value.s = buf;
|
||||
val->len = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int an8855_get_port_stats(struct switch_dev *dev, int port,
|
||||
struct switch_port_stats *stats)
|
||||
{
|
||||
struct gsw_an8855 *gsw = container_of(dev, struct gsw_an8855, swdev);
|
||||
|
||||
if (port < 0 || port >= AN8855_NUM_PORTS)
|
||||
return -EINVAL;
|
||||
|
||||
stats->tx_bytes = get_mib_counter(gsw, AN8855_PORT_MIB_TXB_ID, port);
|
||||
stats->rx_bytes = get_mib_counter(gsw, AN8855_PORT_MIB_RXB_ID, port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void an8855_port_isolation(struct gsw_an8855 *gsw)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < AN8855_NUM_PORTS; i++)
|
||||
an8855_reg_write(gsw, PORTMATRIX(i),
|
||||
BIT(gsw->cpu_port));
|
||||
|
||||
an8855_reg_write(gsw, PORTMATRIX(gsw->cpu_port), PORT_MATRIX_M);
|
||||
|
||||
for (i = 0; i < AN8855_NUM_PORTS; i++) {
|
||||
u32 pvc_mode = 0x8100 << STAG_VPID_S;
|
||||
|
||||
if (gsw->port5_cfg.stag_on && i == 5)
|
||||
pvc_mode |= PVC_PORT_STAG | PVC_STAG_REPLACE;
|
||||
else
|
||||
pvc_mode |= (VA_TRANSPARENT_PORT << VLAN_ATTR_S);
|
||||
|
||||
an8855_reg_write(gsw, PVC(i), pvc_mode);
|
||||
}
|
||||
}
|
||||
|
||||
static int an8855_apply_config(struct switch_dev *dev)
|
||||
{
|
||||
struct gsw_an8855 *gsw = container_of(dev, struct gsw_an8855, swdev);
|
||||
|
||||
if (!gsw->global_vlan_enable) {
|
||||
an8855_port_isolation(gsw);
|
||||
return 0;
|
||||
}
|
||||
|
||||
an8855_apply_vlan_config(gsw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int an8855_reset_switch(struct switch_dev *dev)
|
||||
{
|
||||
struct gsw_an8855 *gsw = container_of(dev, struct gsw_an8855, swdev);
|
||||
int i;
|
||||
|
||||
memset(gsw->port_entries, 0, sizeof(gsw->port_entries));
|
||||
memset(gsw->vlan_entries, 0, sizeof(gsw->vlan_entries));
|
||||
|
||||
/* set default vid of each vlan to the same number of vlan, so the vid
|
||||
* won't need be set explicitly.
|
||||
*/
|
||||
for (i = 0; i < AN8855_NUM_VLANS; i++)
|
||||
gsw->vlan_entries[i].vid = i;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int an8855_phy_read16(struct switch_dev *dev, int addr, u8 reg,
|
||||
u16 *value)
|
||||
{
|
||||
struct gsw_an8855 *gsw = container_of(dev, struct gsw_an8855, swdev);
|
||||
|
||||
*value = gsw->mii_read(gsw, addr, reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int an8855_phy_write16(struct switch_dev *dev, int addr, u8 reg,
|
||||
u16 value)
|
||||
{
|
||||
struct gsw_an8855 *gsw = container_of(dev, struct gsw_an8855, swdev);
|
||||
|
||||
gsw->mii_write(gsw, addr, reg, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct switch_attr an8855_global[] = {
|
||||
{
|
||||
.type = SWITCH_TYPE_INT,
|
||||
.name = "enable_vlan",
|
||||
.description = "VLAN mode (1:enabled)",
|
||||
.max = 1,
|
||||
.id = AN8855_ATTR_ENABLE_VLAN,
|
||||
.get = an8855_get_vlan_enable,
|
||||
.set = an8855_set_vlan_enable,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct switch_attr an8855_port[] = {
|
||||
{
|
||||
.type = SWITCH_TYPE_STRING,
|
||||
.name = "mib",
|
||||
.description = "Get MIB counters for port",
|
||||
.get = an8855_get_port_mib,
|
||||
.set = NULL,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct switch_attr an8855_vlan[] = {
|
||||
{
|
||||
.type = SWITCH_TYPE_INT,
|
||||
.name = "vid",
|
||||
.description = "VLAN ID (0-4094)",
|
||||
.set = an8855_set_vid,
|
||||
.get = an8855_get_vid,
|
||||
.max = 4094,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct switch_dev_ops an8855_swdev_ops = {
|
||||
.attr_global = {
|
||||
.attr = an8855_global,
|
||||
.n_attr = ARRAY_SIZE(an8855_global),
|
||||
},
|
||||
.attr_port = {
|
||||
.attr = an8855_port,
|
||||
.n_attr = ARRAY_SIZE(an8855_port),
|
||||
},
|
||||
.attr_vlan = {
|
||||
.attr = an8855_vlan,
|
||||
.n_attr = ARRAY_SIZE(an8855_vlan),
|
||||
},
|
||||
.get_vlan_ports = an8855_get_vlan_ports,
|
||||
.set_vlan_ports = an8855_set_vlan_ports,
|
||||
.get_port_pvid = an8855_get_port_pvid,
|
||||
.set_port_pvid = an8855_set_port_pvid,
|
||||
.get_port_link = an8855_get_port_link,
|
||||
.set_port_link = an8855_set_port_link,
|
||||
.get_port_stats = an8855_get_port_stats,
|
||||
.apply_config = an8855_apply_config,
|
||||
.reset_switch = an8855_reset_switch,
|
||||
.phy_read16 = an8855_phy_read16,
|
||||
.phy_write16 = an8855_phy_write16,
|
||||
};
|
||||
|
||||
int an8855_swconfig_init(struct gsw_an8855 *gsw)
|
||||
{
|
||||
struct device_node *np = gsw->dev->of_node;
|
||||
struct switch_dev *swdev;
|
||||
int ret;
|
||||
|
||||
if (of_property_read_u32(np, "airoha,cpuport", &gsw->cpu_port))
|
||||
gsw->cpu_port = AN8855_DFL_CPU_PORT;
|
||||
|
||||
swdev = &gsw->swdev;
|
||||
|
||||
swdev->name = gsw->name;
|
||||
swdev->alias = gsw->name;
|
||||
swdev->cpu_port = gsw->cpu_port;
|
||||
swdev->ports = AN8855_NUM_PORTS;
|
||||
swdev->vlans = AN8855_NUM_VLANS;
|
||||
swdev->ops = &an8855_swdev_ops;
|
||||
|
||||
ret = register_switch(swdev, NULL);
|
||||
if (ret) {
|
||||
dev_notice(gsw->dev, "Failed to register switch %s\n",
|
||||
swdev->name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
an8855_apply_config(swdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void an8855_swconfig_destroy(struct gsw_an8855 *gsw)
|
||||
{
|
||||
unregister_switch(&gsw->swdev);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2023 Airoha Inc.
|
||||
* Author: Min Yao <min.yao@airoha.com>
|
||||
*/
|
||||
|
||||
#ifndef _AN8855_SWCONFIG_H_
|
||||
#define _AN8855_SWCONFIG_H_
|
||||
|
||||
#ifdef CONFIG_SWCONFIG
|
||||
#include <linux/switch.h>
|
||||
#include "an8855.h"
|
||||
|
||||
int an8855_swconfig_init(struct gsw_an8855 *gsw);
|
||||
void an8855_swconfig_destroy(struct gsw_an8855 *gsw);
|
||||
#else
|
||||
static inline int an8855_swconfig_init(struct gsw_an8855 *gsw)
|
||||
{
|
||||
an8855_apply_vlan_config(gsw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void an8855_swconfig_destroy(struct gsw_an8855 *gsw)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _AN8855_SWCONFIG_H_ */
|
||||
@@ -0,0 +1,199 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2023 Airoha Inc.
|
||||
*/
|
||||
|
||||
#include "an8855.h"
|
||||
#include "an8855_regs.h"
|
||||
|
||||
struct an8855_mapping an8855_def_mapping[] = {
|
||||
{
|
||||
.name = "llllw",
|
||||
.pvids = { 1, 1, 1, 1, 2, 1 },
|
||||
.members = { 0, 0x2f, 0x30 },
|
||||
.etags = { 0, 0, 0x20 },
|
||||
.vids = { 0, 1, 2 },
|
||||
}, {
|
||||
.name = "wllll",
|
||||
.pvids = { 2, 1, 1, 1, 1, 1 },
|
||||
.members = { 0, 0x3e, 0x21 },
|
||||
.etags = { 0, 0, 0x20 },
|
||||
.vids = { 0, 1, 2 },
|
||||
}, {
|
||||
.name = "lwlll",
|
||||
.pvids = { 1, 2, 1, 1, 1, 1 },
|
||||
.members = { 0, 0x3d, 0x22 },
|
||||
.etags = { 0, 0, 0x20 },
|
||||
.vids = { 0, 1, 2 },
|
||||
}, {
|
||||
.name = "lllll",
|
||||
.pvids = { 1, 1, 1, 1, 1, 1 },
|
||||
.members = { 0, 0x3f },
|
||||
.etags = { 0, 0 },
|
||||
.vids = { 0, 1 },
|
||||
},
|
||||
};
|
||||
|
||||
void an8855_vlan_ctrl(struct gsw_an8855 *gsw, u32 cmd, u32 val)
|
||||
{
|
||||
int i;
|
||||
|
||||
an8855_reg_write(gsw, VTCR,
|
||||
VTCR_BUSY | ((cmd << VTCR_FUNC_S) & VTCR_FUNC_M) |
|
||||
(val & VTCR_VID_M));
|
||||
|
||||
for (i = 0; i < 300; i++) {
|
||||
u32 val = an8855_reg_read(gsw, VTCR);
|
||||
|
||||
if ((val & VTCR_BUSY) == 0)
|
||||
break;
|
||||
|
||||
usleep_range(1000, 1100);
|
||||
}
|
||||
|
||||
if (i == 300)
|
||||
dev_info(gsw->dev, "vtcr timeout\n");
|
||||
}
|
||||
|
||||
static void an8855_write_vlan_entry(struct gsw_an8855 *gsw, int vlan, u16 vid,
|
||||
u8 ports, u8 etags)
|
||||
{
|
||||
int port;
|
||||
u32 val;
|
||||
|
||||
/* vlan port membership */
|
||||
if (ports) {
|
||||
val = IVL_MAC | VTAG_EN | VENTRY_VALID
|
||||
| ((ports << PORT_MEM_S) & PORT_MEM_M);
|
||||
/* egress mode */
|
||||
for (port = 0; port < AN8855_NUM_PORTS; port++) {
|
||||
if (etags & BIT(port))
|
||||
val |= (ETAG_CTRL_TAG << PORT_ETAG_S(port));
|
||||
else
|
||||
val |= (ETAG_CTRL_UNTAG << PORT_ETAG_S(port));
|
||||
}
|
||||
an8855_reg_write(gsw, VAWD0, val);
|
||||
} else {
|
||||
an8855_reg_write(gsw, VAWD0, 0);
|
||||
}
|
||||
|
||||
if (ports & 0x40)
|
||||
an8855_reg_write(gsw, VAWD1, 0x1);
|
||||
else
|
||||
an8855_reg_write(gsw, VAWD1, 0x0);
|
||||
|
||||
/* write to vlan table */
|
||||
an8855_vlan_ctrl(gsw, VTCR_WRITE_VLAN_ENTRY, vid);
|
||||
}
|
||||
|
||||
void an8855_apply_vlan_config(struct gsw_an8855 *gsw)
|
||||
{
|
||||
int i, j;
|
||||
u8 tag_ports;
|
||||
u8 untag_ports;
|
||||
u32 val;
|
||||
|
||||
/* set all ports as security mode */
|
||||
for (i = 0; i < AN8855_NUM_PORTS; i++) {
|
||||
val = an8855_reg_read(gsw, PCR(i));
|
||||
an8855_reg_write(gsw, PCR(i), val | SECURITY_MODE);
|
||||
an8855_reg_write(gsw, PORTMATRIX(i), PORT_MATRIX_M);
|
||||
}
|
||||
|
||||
/* check if a port is used in tag/untag vlan egress mode */
|
||||
tag_ports = 0;
|
||||
untag_ports = 0;
|
||||
|
||||
for (i = 0; i < AN8855_NUM_VLANS; i++) {
|
||||
u8 member = gsw->vlan_entries[i].member;
|
||||
u8 etags = gsw->vlan_entries[i].etags;
|
||||
|
||||
if (!member)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < AN8855_NUM_PORTS; j++) {
|
||||
if (!(member & BIT(j)))
|
||||
continue;
|
||||
|
||||
if (etags & BIT(j))
|
||||
tag_ports |= 1u << j;
|
||||
else
|
||||
untag_ports |= 1u << j;
|
||||
}
|
||||
}
|
||||
|
||||
/* set all untag-only ports as transparent and the rest as user port */
|
||||
for (i = 0; i < AN8855_NUM_PORTS; i++) {
|
||||
u32 pvc_mode = 0x8100 << STAG_VPID_S;
|
||||
|
||||
if (untag_ports & BIT(i) && !(tag_ports & BIT(i)))
|
||||
pvc_mode = (0x8100 << STAG_VPID_S) |
|
||||
(VA_TRANSPARENT_PORT << VLAN_ATTR_S);
|
||||
|
||||
if (gsw->port5_cfg.stag_on && i == 5)
|
||||
pvc_mode = (u32)((0x8100 << STAG_VPID_S) | PVC_PORT_STAG
|
||||
| PVC_STAG_REPLACE);
|
||||
|
||||
an8855_reg_write(gsw, PVC(i), pvc_mode);
|
||||
}
|
||||
|
||||
/* first clear the switch vlan table */
|
||||
for (i = 0; i < AN8855_NUM_VLANS; i++)
|
||||
an8855_write_vlan_entry(gsw, i, i, 0, 0);
|
||||
|
||||
/* now program only vlans with members to avoid
|
||||
* clobbering remapped entries in later iterations
|
||||
*/
|
||||
for (i = 0; i < AN8855_NUM_VLANS; i++) {
|
||||
u16 vid = gsw->vlan_entries[i].vid;
|
||||
u8 member = gsw->vlan_entries[i].member;
|
||||
u8 etags = gsw->vlan_entries[i].etags;
|
||||
|
||||
if (member)
|
||||
an8855_write_vlan_entry(gsw, i, vid, member, etags);
|
||||
}
|
||||
|
||||
/* Port Default PVID */
|
||||
for (i = 0; i < AN8855_NUM_PORTS; i++) {
|
||||
int vlan = gsw->port_entries[i].pvid;
|
||||
u16 pvid = 0;
|
||||
u32 val;
|
||||
|
||||
if (vlan < AN8855_NUM_VLANS && gsw->vlan_entries[vlan].member)
|
||||
pvid = gsw->vlan_entries[vlan].vid;
|
||||
|
||||
val = an8855_reg_read(gsw, PVID(i));
|
||||
val &= ~GRP_PORT_VID_M;
|
||||
val |= pvid;
|
||||
an8855_reg_write(gsw, PVID(i), val);
|
||||
}
|
||||
}
|
||||
|
||||
struct an8855_mapping *an8855_find_mapping(struct device_node *np)
|
||||
{
|
||||
const char *map;
|
||||
int i;
|
||||
|
||||
if (of_property_read_string(np, "airoha,portmap", &map))
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(an8855_def_mapping); i++)
|
||||
if (!strcmp(map, an8855_def_mapping[i].name))
|
||||
return &an8855_def_mapping[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void an8855_apply_mapping(struct gsw_an8855 *gsw, struct an8855_mapping *map)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < AN8855_NUM_PORTS; i++)
|
||||
gsw->port_entries[i].pvid = map->pvids[i];
|
||||
|
||||
for (i = 0; i < AN8855_NUM_VLANS; i++) {
|
||||
gsw->vlan_entries[i].member = map->members[i];
|
||||
gsw->vlan_entries[i].etags = map->etags[i];
|
||||
gsw->vlan_entries[i].vid = map->vids[i];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2023 Airoha Inc.
|
||||
*/
|
||||
|
||||
#ifndef _AN8855_VLAN_H_
|
||||
#define _AN8855_VLAN_H_
|
||||
|
||||
#define AN8855_NUM_PORTS 6
|
||||
#define AN8855_NUM_VLANS 4095
|
||||
#define AN8855_MAX_VID 4095
|
||||
#define AN8855_MIN_VID 0
|
||||
|
||||
struct gsw_an8855;
|
||||
|
||||
struct an8855_port_entry {
|
||||
u16 pvid;
|
||||
};
|
||||
|
||||
struct an8855_vlan_entry {
|
||||
u16 vid;
|
||||
u8 member;
|
||||
u8 etags;
|
||||
};
|
||||
|
||||
struct an8855_mapping {
|
||||
char *name;
|
||||
u16 pvids[AN8855_NUM_PORTS];
|
||||
u8 members[AN8855_NUM_VLANS];
|
||||
u8 etags[AN8855_NUM_VLANS];
|
||||
u16 vids[AN8855_NUM_VLANS];
|
||||
};
|
||||
|
||||
extern struct an8855_mapping an8855_defaults[];
|
||||
|
||||
void an8855_vlan_ctrl(struct gsw_an8855 *gsw, u32 cmd, u32 val);
|
||||
void an8855_apply_vlan_config(struct gsw_an8855 *gsw);
|
||||
struct an8855_mapping *an8855_find_mapping(struct device_node *np);
|
||||
void an8855_apply_mapping(struct gsw_an8855 *gsw, struct an8855_mapping *map);
|
||||
#endif /* _AN8855_VLAN_H_ */
|
||||
133
feeds/mediatek-sdk/mediatek/files-5.4/net/dsa/tag_arht.c
Normal file
133
feeds/mediatek-sdk/mediatek/files-5.4/net/dsa/tag_arht.c
Normal file
@@ -0,0 +1,133 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Airoha DSA Tag support
|
||||
* Copyright (C) 2023 Min Yao <min.yao@airoha.com>
|
||||
*/
|
||||
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/if_vlan.h>
|
||||
|
||||
#include "dsa_priv.h"
|
||||
|
||||
#define AIR_HDR_LEN 4
|
||||
#define AIR_HDR_XMIT_UNTAGGED 0
|
||||
#define AIR_HDR_XMIT_TAGGED_TPID_8100 1
|
||||
#define AIR_HDR_XMIT_TAGGED_TPID_88A8 2
|
||||
#define AIR_HDR_RECV_SOURCE_PORT_MASK GENMASK(2, 0)
|
||||
#define AIR_HDR_XMIT_DP_BIT_MASK GENMASK(5, 0)
|
||||
|
||||
static struct sk_buff *air_tag_xmit(struct sk_buff *skb,
|
||||
struct net_device *dev)
|
||||
{
|
||||
struct dsa_port *dp = dsa_slave_to_port(dev);
|
||||
u8 xmit_tpid;
|
||||
u8 *air_tag;
|
||||
unsigned char *dest = eth_hdr(skb)->h_dest;
|
||||
|
||||
/* Build the special tag after the MAC Source Address. If VLAN header
|
||||
* is present, it's required that VLAN header and special tag is
|
||||
* being combined. Only in this way we can allow the switch can parse
|
||||
* the both special and VLAN tag at the same time and then look up VLAN
|
||||
* table with VID.
|
||||
*/
|
||||
switch (skb->protocol) {
|
||||
case htons(ETH_P_8021Q):
|
||||
xmit_tpid = AIR_HDR_XMIT_TAGGED_TPID_8100;
|
||||
break;
|
||||
case htons(ETH_P_8021AD):
|
||||
xmit_tpid = AIR_HDR_XMIT_TAGGED_TPID_88A8;
|
||||
break;
|
||||
default:
|
||||
if (skb_cow_head(skb, AIR_HDR_LEN) < 0)
|
||||
return NULL;
|
||||
|
||||
xmit_tpid = AIR_HDR_XMIT_UNTAGGED;
|
||||
skb_push(skb, AIR_HDR_LEN);
|
||||
memmove(skb->data, skb->data + AIR_HDR_LEN, 2 * ETH_ALEN);
|
||||
}
|
||||
|
||||
air_tag = skb->data + 2 * ETH_ALEN;
|
||||
|
||||
/* Mark tag attribute on special tag insertion to notify hardware
|
||||
* whether that's a combined special tag with 802.1Q header.
|
||||
*/
|
||||
air_tag[0] = xmit_tpid;
|
||||
air_tag[1] = (1 << dp->index) & AIR_HDR_XMIT_DP_BIT_MASK;
|
||||
|
||||
/* Tag control information is kept for 802.1Q */
|
||||
if (xmit_tpid == AIR_HDR_XMIT_UNTAGGED) {
|
||||
air_tag[2] = 0;
|
||||
air_tag[3] = 0;
|
||||
}
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
static struct sk_buff *air_tag_rcv(struct sk_buff *skb, struct net_device *dev,
|
||||
struct packet_type *pt)
|
||||
{
|
||||
int port;
|
||||
__be16 *phdr, hdr;
|
||||
unsigned char *dest = eth_hdr(skb)->h_dest;
|
||||
bool is_multicast_skb = is_multicast_ether_addr(dest) &&
|
||||
!is_broadcast_ether_addr(dest);
|
||||
|
||||
if (dev->features & NETIF_F_HW_VLAN_CTAG_RX) {
|
||||
hdr = ntohs(skb->vlan_proto);
|
||||
skb->vlan_proto = 0;
|
||||
skb->vlan_tci = 0;
|
||||
} else {
|
||||
if (unlikely(!pskb_may_pull(skb, AIR_HDR_LEN)))
|
||||
return NULL;
|
||||
|
||||
/* The AIR header is added by the switch between src addr
|
||||
* and ethertype at this point, skb->data points to 2 bytes
|
||||
* after src addr so header should be 2 bytes right before.
|
||||
*/
|
||||
phdr = (__be16 *)(skb->data - 2);
|
||||
hdr = ntohs(*phdr);
|
||||
|
||||
/* Remove AIR tag and recalculate checksum. */
|
||||
skb_pull_rcsum(skb, AIR_HDR_LEN);
|
||||
|
||||
memmove(skb->data - ETH_HLEN,
|
||||
skb->data - ETH_HLEN - AIR_HDR_LEN,
|
||||
2 * ETH_ALEN);
|
||||
}
|
||||
|
||||
/* Get source port information */
|
||||
port = (hdr & AIR_HDR_RECV_SOURCE_PORT_MASK);
|
||||
|
||||
skb->dev = dsa_master_find_slave(dev, 0, port);
|
||||
if (!skb->dev)
|
||||
return NULL;
|
||||
|
||||
/* Only unicast or broadcast frames are offloaded */
|
||||
if (likely(!is_multicast_skb))
|
||||
skb->offload_fwd_mark = 1;
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
static int air_tag_flow_dissect(const struct sk_buff *skb, __be16 *proto,
|
||||
int *offset)
|
||||
{
|
||||
*offset = 4;
|
||||
*proto = ((__be16 *)skb->data)[1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dsa_device_ops air_netdev_ops = {
|
||||
.name = "air",
|
||||
.proto = DSA_TAG_PROTO_ARHT,
|
||||
.xmit = air_tag_xmit,
|
||||
.rcv = air_tag_rcv,
|
||||
.flow_dissect = air_tag_flow_dissect,
|
||||
.overhead = AIR_HDR_LEN,
|
||||
};
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_AIR);
|
||||
|
||||
module_dsa_tag_driver(air_netdev_ops);
|
||||
@@ -1,303 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* fs/partitions/fit.c
|
||||
* Copyright (C) 2021 Daniel Golle
|
||||
*
|
||||
* headers extracted from U-Boot mkimage sources
|
||||
* (C) Copyright 2008 Semihalf
|
||||
* (C) Copyright 2000-2005
|
||||
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
*
|
||||
* based on existing partition parsers
|
||||
* Copyright (C) 1991-1998 Linus Torvalds
|
||||
* Re-organised Feb 1998 Russell King
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) fmt
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_fdt.h>
|
||||
#include <linux/libfdt.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include "check.h"
|
||||
|
||||
#define FIT_IMAGES_PATH "/images"
|
||||
#define FIT_CONFS_PATH "/configurations"
|
||||
|
||||
/* hash/signature/key node */
|
||||
#define FIT_HASH_NODENAME "hash"
|
||||
#define FIT_ALGO_PROP "algo"
|
||||
#define FIT_VALUE_PROP "value"
|
||||
#define FIT_IGNORE_PROP "uboot-ignore"
|
||||
#define FIT_SIG_NODENAME "signature"
|
||||
#define FIT_KEY_REQUIRED "required"
|
||||
#define FIT_KEY_HINT "key-name-hint"
|
||||
|
||||
/* cipher node */
|
||||
#define FIT_CIPHER_NODENAME "cipher"
|
||||
#define FIT_ALGO_PROP "algo"
|
||||
|
||||
/* image node */
|
||||
#define FIT_DATA_PROP "data"
|
||||
#define FIT_DATA_POSITION_PROP "data-position"
|
||||
#define FIT_DATA_OFFSET_PROP "data-offset"
|
||||
#define FIT_DATA_SIZE_PROP "data-size"
|
||||
#define FIT_TIMESTAMP_PROP "timestamp"
|
||||
#define FIT_DESC_PROP "description"
|
||||
#define FIT_ARCH_PROP "arch"
|
||||
#define FIT_TYPE_PROP "type"
|
||||
#define FIT_OS_PROP "os"
|
||||
#define FIT_COMP_PROP "compression"
|
||||
#define FIT_ENTRY_PROP "entry"
|
||||
#define FIT_LOAD_PROP "load"
|
||||
|
||||
/* configuration node */
|
||||
#define FIT_KERNEL_PROP "kernel"
|
||||
#define FIT_FILESYSTEM_PROP "filesystem"
|
||||
#define FIT_RAMDISK_PROP "ramdisk"
|
||||
#define FIT_FDT_PROP "fdt"
|
||||
#define FIT_LOADABLE_PROP "loadables"
|
||||
#define FIT_DEFAULT_PROP "default"
|
||||
#define FIT_SETUP_PROP "setup"
|
||||
#define FIT_FPGA_PROP "fpga"
|
||||
#define FIT_FIRMWARE_PROP "firmware"
|
||||
#define FIT_STANDALONE_PROP "standalone"
|
||||
|
||||
#define FIT_MAX_HASH_LEN HASH_MAX_DIGEST_SIZE
|
||||
|
||||
#define MIN_FREE_SECT 16
|
||||
#define REMAIN_VOLNAME "rootfs_data"
|
||||
|
||||
int parse_fit_partitions(struct parsed_partitions *state, u64 fit_start_sector, u64 sectors, int *slot, int add_remain)
|
||||
{
|
||||
struct block_device *bdev = state->disk->part0;
|
||||
struct address_space *mapping = bdev->bd_inode->i_mapping;
|
||||
struct page *page;
|
||||
void *fit, *init_fit;
|
||||
struct partition_meta_info *info;
|
||||
char tmp[sizeof(info->volname)];
|
||||
u64 dsize, dsectors, imgmaxsect = 0;
|
||||
u32 size, image_pos, image_len;
|
||||
const u32 *image_offset_be, *image_len_be, *image_pos_be;
|
||||
int ret = 1, node, images, config;
|
||||
const char *image_name, *image_type, *image_description, *config_default,
|
||||
*config_description, *config_loadables, *bootconf_c;
|
||||
int image_name_len, image_type_len, image_description_len, config_default_len,
|
||||
config_description_len, config_loadables_len, bootconf_len;
|
||||
sector_t start_sect, nr_sects;
|
||||
size_t label_min;
|
||||
struct device_node *np = NULL;
|
||||
char *bootconf = NULL, *bootconf_term;
|
||||
const char *loadable;
|
||||
const char *select_rootfs = NULL;
|
||||
bool found;
|
||||
int loadables_rem_len, loadable_len;
|
||||
|
||||
if (fit_start_sector % (1<<(PAGE_SHIFT - SECTOR_SHIFT)))
|
||||
return -ERANGE;
|
||||
|
||||
page = read_mapping_page(mapping, fit_start_sector >> (PAGE_SHIFT - SECTOR_SHIFT), NULL);
|
||||
if (IS_ERR(page))
|
||||
return -EFAULT;
|
||||
|
||||
if (PageError(page))
|
||||
return -EFAULT;
|
||||
|
||||
init_fit = page_address(page);
|
||||
|
||||
if (!init_fit) {
|
||||
put_page(page);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (fdt_check_header(init_fit)) {
|
||||
put_page(page);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dsectors = get_capacity(bdev->bd_disk);
|
||||
if (sectors)
|
||||
dsectors = (dsectors>sectors)?sectors:dsectors;
|
||||
|
||||
dsize = dsectors << SECTOR_SHIFT;
|
||||
size = fdt_totalsize(init_fit);
|
||||
|
||||
/* silently skip non-external-data legacy FIT images */
|
||||
if (size > PAGE_SIZE) {
|
||||
put_page(page);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (size >= dsize) {
|
||||
state->access_beyond_eod = 1;
|
||||
put_page(page);
|
||||
return -EFBIG;
|
||||
}
|
||||
|
||||
fit = kmemdup(init_fit, size, GFP_KERNEL);
|
||||
put_page(page);
|
||||
if (!fit)
|
||||
return -ENOMEM;
|
||||
|
||||
np = of_find_node_by_path("/chosen");
|
||||
if (np) {
|
||||
bootconf_c = of_get_property(np, "u-boot,bootconf", &bootconf_len);
|
||||
if (bootconf_c && bootconf_len)
|
||||
bootconf = kmemdup_nul(bootconf_c, bootconf_len, GFP_KERNEL);
|
||||
}
|
||||
|
||||
if (bootconf) {
|
||||
bootconf_term = strchr(bootconf, '#');
|
||||
if (bootconf_term)
|
||||
*bootconf_term = '\0';
|
||||
}
|
||||
|
||||
config = fdt_path_offset(fit, FIT_CONFS_PATH);
|
||||
if (config < 0) {
|
||||
printk(KERN_ERR "FIT: Cannot find %s node: %d\n", FIT_CONFS_PATH, images);
|
||||
ret = -ENOENT;
|
||||
goto ret_out;
|
||||
}
|
||||
|
||||
config_default = fdt_getprop(fit, config, FIT_DEFAULT_PROP, &config_default_len);
|
||||
|
||||
if (!config_default && !bootconf) {
|
||||
printk(KERN_ERR "FIT: Cannot find default configuration\n");
|
||||
ret = -ENOENT;
|
||||
goto ret_out;
|
||||
}
|
||||
|
||||
node = fdt_subnode_offset(fit, config, bootconf?:config_default);
|
||||
if (node < 0) {
|
||||
printk(KERN_ERR "FIT: Cannot find %s node: %d\n", bootconf?:config_default, node);
|
||||
ret = -ENOENT;
|
||||
goto ret_out;
|
||||
}
|
||||
|
||||
config_description = fdt_getprop(fit, node, FIT_DESC_PROP, &config_description_len);
|
||||
config_loadables = fdt_getprop(fit, node, FIT_LOADABLE_PROP, &config_loadables_len);
|
||||
|
||||
printk(KERN_DEBUG "FIT: %s configuration: \"%s\"%s%s%s\n",
|
||||
bootconf?"Selected":"Default", bootconf?:config_default,
|
||||
config_description?" (":"", config_description?:"", config_description?")":"");
|
||||
|
||||
if (!config_loadables || !config_loadables_len) {
|
||||
printk(KERN_ERR "FIT: No loadables configured in \"%s\"\n", bootconf?:config_default);
|
||||
ret = -ENOENT;
|
||||
goto ret_out;
|
||||
}
|
||||
|
||||
images = fdt_path_offset(fit, FIT_IMAGES_PATH);
|
||||
if (images < 0) {
|
||||
printk(KERN_ERR "FIT: Cannot find %s node: %d\n", FIT_IMAGES_PATH, images);
|
||||
ret = -EINVAL;
|
||||
goto ret_out;
|
||||
}
|
||||
|
||||
fdt_for_each_subnode(node, fit, images) {
|
||||
image_name = fdt_get_name(fit, node, &image_name_len);
|
||||
image_type = fdt_getprop(fit, node, FIT_TYPE_PROP, &image_type_len);
|
||||
image_offset_be = fdt_getprop(fit, node, FIT_DATA_OFFSET_PROP, NULL);
|
||||
image_pos_be = fdt_getprop(fit, node, FIT_DATA_POSITION_PROP, NULL);
|
||||
image_len_be = fdt_getprop(fit, node, FIT_DATA_SIZE_PROP, NULL);
|
||||
if (!image_name || !image_type || !image_len_be)
|
||||
continue;
|
||||
|
||||
image_len = be32_to_cpu(*image_len_be);
|
||||
if (!image_len)
|
||||
continue;
|
||||
|
||||
if (image_offset_be)
|
||||
image_pos = be32_to_cpu(*image_offset_be) + size;
|
||||
else if (image_pos_be)
|
||||
image_pos = be32_to_cpu(*image_pos_be);
|
||||
else
|
||||
continue;
|
||||
|
||||
image_description = fdt_getprop(fit, node, FIT_DESC_PROP, &image_description_len);
|
||||
|
||||
printk(KERN_DEBUG "FIT: %16s sub-image 0x%08x..0x%08x \"%s\" %s%s%s\n",
|
||||
image_type, image_pos, image_pos + image_len - 1, image_name,
|
||||
image_description?"(":"", image_description?:"", image_description?") ":"");
|
||||
|
||||
if (strcmp(image_type, FIT_FILESYSTEM_PROP))
|
||||
continue;
|
||||
|
||||
/* check if sub-image is part of configured loadables */
|
||||
found = false;
|
||||
loadable = config_loadables;
|
||||
loadables_rem_len = config_loadables_len;
|
||||
while (loadables_rem_len > 1) {
|
||||
loadable_len = strnlen(loadable, loadables_rem_len - 1) + 1;
|
||||
loadables_rem_len -= loadable_len;
|
||||
if (!strncmp(image_name, loadable, loadable_len)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
loadable += loadable_len;
|
||||
}
|
||||
if (!found)
|
||||
continue;
|
||||
|
||||
if (image_pos & ((1 << PAGE_SHIFT)-1)) {
|
||||
printk(KERN_ERR "FIT: image %s start not aligned to page boundaries, skipping\n", image_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (image_len & ((1 << PAGE_SHIFT)-1)) {
|
||||
printk(KERN_ERR "FIT: sub-image %s end not aligned to page boundaries, skipping\n", image_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
start_sect = image_pos >> SECTOR_SHIFT;
|
||||
nr_sects = image_len >> SECTOR_SHIFT;
|
||||
imgmaxsect = (imgmaxsect < (start_sect + nr_sects))?(start_sect + nr_sects):imgmaxsect;
|
||||
|
||||
if (start_sect + nr_sects > dsectors) {
|
||||
state->access_beyond_eod = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
put_partition(state, ++(*slot), fit_start_sector + start_sect, nr_sects);
|
||||
state->parts[*slot].flags = ADDPART_FLAG_READONLY;
|
||||
state->parts[*slot].has_info = true;
|
||||
info = &state->parts[*slot].info;
|
||||
|
||||
label_min = min_t(int, sizeof(info->volname) - 1, image_name_len);
|
||||
strncpy(info->volname, image_name, label_min);
|
||||
info->volname[label_min] = '\0';
|
||||
|
||||
snprintf(tmp, sizeof(tmp), "(%s)", info->volname);
|
||||
strlcat(state->pp_buf, tmp, PAGE_SIZE);
|
||||
|
||||
/* Mark first loadable listed to be mounted as rootfs */
|
||||
if (!strcmp(image_name, config_loadables)) {
|
||||
select_rootfs = image_name;
|
||||
state->parts[*slot].flags |= ADDPART_FLAG_ROOTDEV;
|
||||
}
|
||||
}
|
||||
|
||||
if (select_rootfs)
|
||||
printk(KERN_DEBUG "FIT: selecting configured loadable \"%s\" to be root filesystem\n", select_rootfs);
|
||||
|
||||
if (add_remain && (imgmaxsect + MIN_FREE_SECT) < dsectors) {
|
||||
put_partition(state, ++(*slot), fit_start_sector + imgmaxsect, dsectors - imgmaxsect);
|
||||
state->parts[*slot].flags = 0;
|
||||
info = &state->parts[*slot].info;
|
||||
strcpy(info->volname, REMAIN_VOLNAME);
|
||||
snprintf(tmp, sizeof(tmp), "(%s)", REMAIN_VOLNAME);
|
||||
strlcat(state->pp_buf, tmp, PAGE_SIZE);
|
||||
}
|
||||
ret_out:
|
||||
kfree(bootconf);
|
||||
kfree(fit);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int fit_partition(struct parsed_partitions *state) {
|
||||
int slot = 0;
|
||||
return parse_fit_partitions(state, 0, 0, &slot, 0);
|
||||
}
|
||||
@@ -1,533 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* BCMA Fallback SPROM Driver
|
||||
*
|
||||
* Copyright (C) 2020 Álvaro Fernández Rojas <noltari@gmail.com>
|
||||
* Copyright (C) 2014 Jonas Gorski <jonas.gorski@gmail.com>
|
||||
* Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
|
||||
* Copyright (C) 2008 Florian Fainelli <f.fainelli@gmail.com>
|
||||
*/
|
||||
|
||||
#include <linux/bcma/bcma.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/of_net.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
#define BCMA_FBS_MAX_SIZE 468
|
||||
|
||||
/* SPROM Extraction */
|
||||
#define SPOFF(offset) ((offset) / sizeof(u16))
|
||||
|
||||
#define SPEX(_outvar, _offset, _mask, _shift) \
|
||||
out->_outvar = ((in[SPOFF(_offset)] & (_mask)) >> (_shift))
|
||||
|
||||
#define SPEX32(_outvar, _offset, _mask, _shift) \
|
||||
out->_outvar = ((((u32)in[SPOFF((_offset)+2)] << 16 | \
|
||||
in[SPOFF(_offset)]) & (_mask)) >> (_shift))
|
||||
|
||||
#define SPEX_ARRAY8(_field, _offset, _mask, _shift) \
|
||||
do { \
|
||||
SPEX(_field[0], _offset + 0, _mask, _shift); \
|
||||
SPEX(_field[1], _offset + 2, _mask, _shift); \
|
||||
SPEX(_field[2], _offset + 4, _mask, _shift); \
|
||||
SPEX(_field[3], _offset + 6, _mask, _shift); \
|
||||
SPEX(_field[4], _offset + 8, _mask, _shift); \
|
||||
SPEX(_field[5], _offset + 10, _mask, _shift); \
|
||||
SPEX(_field[6], _offset + 12, _mask, _shift); \
|
||||
SPEX(_field[7], _offset + 14, _mask, _shift); \
|
||||
} while (0)
|
||||
|
||||
struct bcma_fbs {
|
||||
struct device *dev;
|
||||
struct list_head list;
|
||||
struct ssb_sprom sprom;
|
||||
u32 pci_bus;
|
||||
u32 pci_dev;
|
||||
bool devid_override;
|
||||
};
|
||||
|
||||
static DEFINE_SPINLOCK(bcma_fbs_lock);
|
||||
static struct list_head bcma_fbs_list = LIST_HEAD_INIT(bcma_fbs_list);
|
||||
|
||||
int bcma_get_fallback_sprom(struct bcma_bus *bus, struct ssb_sprom *out)
|
||||
{
|
||||
struct bcma_fbs *pos;
|
||||
u32 pci_bus, pci_dev;
|
||||
|
||||
if (bus->hosttype != BCMA_HOSTTYPE_PCI)
|
||||
return -ENOENT;
|
||||
|
||||
pci_bus = bus->host_pci->bus->number;
|
||||
pci_dev = PCI_SLOT(bus->host_pci->devfn);
|
||||
|
||||
list_for_each_entry(pos, &bcma_fbs_list, list) {
|
||||
if (pos->pci_bus != pci_bus ||
|
||||
pos->pci_dev != pci_dev)
|
||||
continue;
|
||||
|
||||
if (pos->devid_override)
|
||||
bus->host_pci->device = pos->sprom.dev_id;
|
||||
|
||||
memcpy(out, &pos->sprom, sizeof(struct ssb_sprom));
|
||||
dev_info(pos->dev, "requested by [%x:%x]",
|
||||
pos->pci_bus, pos->pci_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
pr_err("unable to fill SPROM for [%x:%x]\n", pci_bus, pci_dev);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static s8 sprom_extract_antgain(const u16 *in, u16 offset, u16 mask, u16 shift)
|
||||
{
|
||||
u16 v;
|
||||
u8 gain;
|
||||
|
||||
v = in[SPOFF(offset)];
|
||||
gain = (v & mask) >> shift;
|
||||
if (gain == 0xFF) {
|
||||
gain = 8; /* If unset use 2dBm */
|
||||
} else {
|
||||
/* Q5.2 Fractional part is stored in 0xC0 */
|
||||
gain = ((gain & 0xC0) >> 6) | ((gain & 0x3F) << 2);
|
||||
}
|
||||
|
||||
return (s8)gain;
|
||||
}
|
||||
|
||||
static void sprom_extract_r8(struct ssb_sprom *out, const u16 *in)
|
||||
{
|
||||
static const u16 pwr_info_offset[] = {
|
||||
SSB_SROM8_PWR_INFO_CORE0, SSB_SROM8_PWR_INFO_CORE1,
|
||||
SSB_SROM8_PWR_INFO_CORE2, SSB_SROM8_PWR_INFO_CORE3
|
||||
};
|
||||
u16 o;
|
||||
int i;
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) !=
|
||||
ARRAY_SIZE(out->core_pwr_info));
|
||||
|
||||
SPEX(board_rev, SSB_SPROM8_BOARDREV, ~0, 0);
|
||||
SPEX(board_type, SSB_SPROM1_SPID, ~0, 0);
|
||||
|
||||
SPEX(txpid2g[0], SSB_SPROM4_TXPID2G01, SSB_SPROM4_TXPID2G0,
|
||||
SSB_SPROM4_TXPID2G0_SHIFT);
|
||||
SPEX(txpid2g[1], SSB_SPROM4_TXPID2G01, SSB_SPROM4_TXPID2G1,
|
||||
SSB_SPROM4_TXPID2G1_SHIFT);
|
||||
SPEX(txpid2g[2], SSB_SPROM4_TXPID2G23, SSB_SPROM4_TXPID2G2,
|
||||
SSB_SPROM4_TXPID2G2_SHIFT);
|
||||
SPEX(txpid2g[3], SSB_SPROM4_TXPID2G23, SSB_SPROM4_TXPID2G3,
|
||||
SSB_SPROM4_TXPID2G3_SHIFT);
|
||||
|
||||
SPEX(txpid5gl[0], SSB_SPROM4_TXPID5GL01, SSB_SPROM4_TXPID5GL0,
|
||||
SSB_SPROM4_TXPID5GL0_SHIFT);
|
||||
SPEX(txpid5gl[1], SSB_SPROM4_TXPID5GL01, SSB_SPROM4_TXPID5GL1,
|
||||
SSB_SPROM4_TXPID5GL1_SHIFT);
|
||||
SPEX(txpid5gl[2], SSB_SPROM4_TXPID5GL23, SSB_SPROM4_TXPID5GL2,
|
||||
SSB_SPROM4_TXPID5GL2_SHIFT);
|
||||
SPEX(txpid5gl[3], SSB_SPROM4_TXPID5GL23, SSB_SPROM4_TXPID5GL3,
|
||||
SSB_SPROM4_TXPID5GL3_SHIFT);
|
||||
|
||||
SPEX(txpid5g[0], SSB_SPROM4_TXPID5G01, SSB_SPROM4_TXPID5G0,
|
||||
SSB_SPROM4_TXPID5G0_SHIFT);
|
||||
SPEX(txpid5g[1], SSB_SPROM4_TXPID5G01, SSB_SPROM4_TXPID5G1,
|
||||
SSB_SPROM4_TXPID5G1_SHIFT);
|
||||
SPEX(txpid5g[2], SSB_SPROM4_TXPID5G23, SSB_SPROM4_TXPID5G2,
|
||||
SSB_SPROM4_TXPID5G2_SHIFT);
|
||||
SPEX(txpid5g[3], SSB_SPROM4_TXPID5G23, SSB_SPROM4_TXPID5G3,
|
||||
SSB_SPROM4_TXPID5G3_SHIFT);
|
||||
|
||||
SPEX(txpid5gh[0], SSB_SPROM4_TXPID5GH01, SSB_SPROM4_TXPID5GH0,
|
||||
SSB_SPROM4_TXPID5GH0_SHIFT);
|
||||
SPEX(txpid5gh[1], SSB_SPROM4_TXPID5GH01, SSB_SPROM4_TXPID5GH1,
|
||||
SSB_SPROM4_TXPID5GH1_SHIFT);
|
||||
SPEX(txpid5gh[2], SSB_SPROM4_TXPID5GH23, SSB_SPROM4_TXPID5GH2,
|
||||
SSB_SPROM4_TXPID5GH2_SHIFT);
|
||||
SPEX(txpid5gh[3], SSB_SPROM4_TXPID5GH23, SSB_SPROM4_TXPID5GH3,
|
||||
SSB_SPROM4_TXPID5GH3_SHIFT);
|
||||
|
||||
SPEX(boardflags_lo, SSB_SPROM8_BFLLO, ~0, 0);
|
||||
SPEX(boardflags_hi, SSB_SPROM8_BFLHI, ~0, 0);
|
||||
SPEX(boardflags2_lo, SSB_SPROM8_BFL2LO, ~0, 0);
|
||||
SPEX(boardflags2_hi, SSB_SPROM8_BFL2HI, ~0, 0);
|
||||
|
||||
SPEX(alpha2[0], SSB_SPROM8_CCODE, 0xff00, 8);
|
||||
SPEX(alpha2[1], SSB_SPROM8_CCODE, 0x00ff, 0);
|
||||
|
||||
/* Extract core's power info */
|
||||
for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) {
|
||||
o = pwr_info_offset[i];
|
||||
SPEX(core_pwr_info[i].itssi_2g, o + SSB_SROM8_2G_MAXP_ITSSI,
|
||||
SSB_SPROM8_2G_ITSSI, SSB_SPROM8_2G_ITSSI_SHIFT);
|
||||
SPEX(core_pwr_info[i].maxpwr_2g, o + SSB_SROM8_2G_MAXP_ITSSI,
|
||||
SSB_SPROM8_2G_MAXP, 0);
|
||||
|
||||
SPEX(core_pwr_info[i].pa_2g[0], o + SSB_SROM8_2G_PA_0, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_2g[1], o + SSB_SROM8_2G_PA_1, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_2g[2], o + SSB_SROM8_2G_PA_2, ~0, 0);
|
||||
|
||||
SPEX(core_pwr_info[i].itssi_5g, o + SSB_SROM8_5G_MAXP_ITSSI,
|
||||
SSB_SPROM8_5G_ITSSI, SSB_SPROM8_5G_ITSSI_SHIFT);
|
||||
SPEX(core_pwr_info[i].maxpwr_5g, o + SSB_SROM8_5G_MAXP_ITSSI,
|
||||
SSB_SPROM8_5G_MAXP, 0);
|
||||
SPEX(core_pwr_info[i].maxpwr_5gh, o + SSB_SPROM8_5GHL_MAXP,
|
||||
SSB_SPROM8_5GH_MAXP, 0);
|
||||
SPEX(core_pwr_info[i].maxpwr_5gl, o + SSB_SPROM8_5GHL_MAXP,
|
||||
SSB_SPROM8_5GL_MAXP, SSB_SPROM8_5GL_MAXP_SHIFT);
|
||||
|
||||
SPEX(core_pwr_info[i].pa_5gl[0], o + SSB_SROM8_5GL_PA_0, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_5gl[1], o + SSB_SROM8_5GL_PA_1, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_5gl[2], o + SSB_SROM8_5GL_PA_2, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_5g[0], o + SSB_SROM8_5G_PA_0, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_5g[1], o + SSB_SROM8_5G_PA_1, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_5g[2], o + SSB_SROM8_5G_PA_2, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_5gh[0], o + SSB_SROM8_5GH_PA_0, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_5gh[1], o + SSB_SROM8_5GH_PA_1, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_5gh[2], o + SSB_SROM8_5GH_PA_2, ~0, 0);
|
||||
}
|
||||
|
||||
SPEX(fem.ghz2.tssipos, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_TSSIPOS,
|
||||
SSB_SROM8_FEM_TSSIPOS_SHIFT);
|
||||
SPEX(fem.ghz2.extpa_gain, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_EXTPA_GAIN,
|
||||
SSB_SROM8_FEM_EXTPA_GAIN_SHIFT);
|
||||
SPEX(fem.ghz2.pdet_range, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_PDET_RANGE,
|
||||
SSB_SROM8_FEM_PDET_RANGE_SHIFT);
|
||||
SPEX(fem.ghz2.tr_iso, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_TR_ISO,
|
||||
SSB_SROM8_FEM_TR_ISO_SHIFT);
|
||||
SPEX(fem.ghz2.antswlut, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_ANTSWLUT,
|
||||
SSB_SROM8_FEM_ANTSWLUT_SHIFT);
|
||||
|
||||
SPEX(fem.ghz5.tssipos, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_TSSIPOS,
|
||||
SSB_SROM8_FEM_TSSIPOS_SHIFT);
|
||||
SPEX(fem.ghz5.extpa_gain, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_EXTPA_GAIN,
|
||||
SSB_SROM8_FEM_EXTPA_GAIN_SHIFT);
|
||||
SPEX(fem.ghz5.pdet_range, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_PDET_RANGE,
|
||||
SSB_SROM8_FEM_PDET_RANGE_SHIFT);
|
||||
SPEX(fem.ghz5.tr_iso, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_TR_ISO,
|
||||
SSB_SROM8_FEM_TR_ISO_SHIFT);
|
||||
SPEX(fem.ghz5.antswlut, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_ANTSWLUT,
|
||||
SSB_SROM8_FEM_ANTSWLUT_SHIFT);
|
||||
|
||||
SPEX(ant_available_a, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_A,
|
||||
SSB_SPROM8_ANTAVAIL_A_SHIFT);
|
||||
SPEX(ant_available_bg, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_BG,
|
||||
SSB_SPROM8_ANTAVAIL_BG_SHIFT);
|
||||
SPEX(maxpwr_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_MAXP_BG_MASK, 0);
|
||||
SPEX(itssi_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_ITSSI_BG,
|
||||
SSB_SPROM8_ITSSI_BG_SHIFT);
|
||||
SPEX(maxpwr_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_MAXP_A_MASK, 0);
|
||||
SPEX(itssi_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_ITSSI_A,
|
||||
SSB_SPROM8_ITSSI_A_SHIFT);
|
||||
SPEX(maxpwr_ah, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AH_MASK, 0);
|
||||
SPEX(maxpwr_al, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AL_MASK,
|
||||
SSB_SPROM8_MAXP_AL_SHIFT);
|
||||
SPEX(gpio0, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P0, 0);
|
||||
SPEX(gpio1, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P1,
|
||||
SSB_SPROM8_GPIOA_P1_SHIFT);
|
||||
SPEX(gpio2, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P2, 0);
|
||||
SPEX(gpio3, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P3,
|
||||
SSB_SPROM8_GPIOB_P3_SHIFT);
|
||||
SPEX(tri2g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI2G, 0);
|
||||
SPEX(tri5g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI5G,
|
||||
SSB_SPROM8_TRI5G_SHIFT);
|
||||
SPEX(tri5gl, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GL, 0);
|
||||
SPEX(tri5gh, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GH,
|
||||
SSB_SPROM8_TRI5GH_SHIFT);
|
||||
SPEX(rxpo2g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO2G,
|
||||
SSB_SPROM8_RXPO2G_SHIFT);
|
||||
SPEX(rxpo5g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO5G,
|
||||
SSB_SPROM8_RXPO5G_SHIFT);
|
||||
SPEX(rssismf2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMF2G, 0);
|
||||
SPEX(rssismc2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMC2G,
|
||||
SSB_SPROM8_RSSISMC2G_SHIFT);
|
||||
SPEX(rssisav2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISAV2G,
|
||||
SSB_SPROM8_RSSISAV2G_SHIFT);
|
||||
SPEX(bxa2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_BXA2G,
|
||||
SSB_SPROM8_BXA2G_SHIFT);
|
||||
SPEX(rssismf5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMF5G, 0);
|
||||
SPEX(rssismc5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMC5G,
|
||||
SSB_SPROM8_RSSISMC5G_SHIFT);
|
||||
SPEX(rssisav5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISAV5G,
|
||||
SSB_SPROM8_RSSISAV5G_SHIFT);
|
||||
SPEX(bxa5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_BXA5G,
|
||||
SSB_SPROM8_BXA5G_SHIFT);
|
||||
|
||||
SPEX(pa0b0, SSB_SPROM8_PA0B0, ~0, 0);
|
||||
SPEX(pa0b1, SSB_SPROM8_PA0B1, ~0, 0);
|
||||
SPEX(pa0b2, SSB_SPROM8_PA0B2, ~0, 0);
|
||||
SPEX(pa1b0, SSB_SPROM8_PA1B0, ~0, 0);
|
||||
SPEX(pa1b1, SSB_SPROM8_PA1B1, ~0, 0);
|
||||
SPEX(pa1b2, SSB_SPROM8_PA1B2, ~0, 0);
|
||||
SPEX(pa1lob0, SSB_SPROM8_PA1LOB0, ~0, 0);
|
||||
SPEX(pa1lob1, SSB_SPROM8_PA1LOB1, ~0, 0);
|
||||
SPEX(pa1lob2, SSB_SPROM8_PA1LOB2, ~0, 0);
|
||||
SPEX(pa1hib0, SSB_SPROM8_PA1HIB0, ~0, 0);
|
||||
SPEX(pa1hib1, SSB_SPROM8_PA1HIB1, ~0, 0);
|
||||
SPEX(pa1hib2, SSB_SPROM8_PA1HIB2, ~0, 0);
|
||||
SPEX(cck2gpo, SSB_SPROM8_CCK2GPO, ~0, 0);
|
||||
SPEX32(ofdm2gpo, SSB_SPROM8_OFDM2GPO, ~0, 0);
|
||||
SPEX32(ofdm5glpo, SSB_SPROM8_OFDM5GLPO, ~0, 0);
|
||||
SPEX32(ofdm5gpo, SSB_SPROM8_OFDM5GPO, ~0, 0);
|
||||
SPEX32(ofdm5ghpo, SSB_SPROM8_OFDM5GHPO, ~0, 0);
|
||||
|
||||
/* Extract the antenna gain values. */
|
||||
out->antenna_gain.a0 = sprom_extract_antgain(in,
|
||||
SSB_SPROM8_AGAIN01,
|
||||
SSB_SPROM8_AGAIN0,
|
||||
SSB_SPROM8_AGAIN0_SHIFT);
|
||||
out->antenna_gain.a1 = sprom_extract_antgain(in,
|
||||
SSB_SPROM8_AGAIN01,
|
||||
SSB_SPROM8_AGAIN1,
|
||||
SSB_SPROM8_AGAIN1_SHIFT);
|
||||
out->antenna_gain.a2 = sprom_extract_antgain(in,
|
||||
SSB_SPROM8_AGAIN23,
|
||||
SSB_SPROM8_AGAIN2,
|
||||
SSB_SPROM8_AGAIN2_SHIFT);
|
||||
out->antenna_gain.a3 = sprom_extract_antgain(in,
|
||||
SSB_SPROM8_AGAIN23,
|
||||
SSB_SPROM8_AGAIN3,
|
||||
SSB_SPROM8_AGAIN3_SHIFT);
|
||||
|
||||
SPEX(leddc_on_time, SSB_SPROM8_LEDDC, SSB_SPROM8_LEDDC_ON,
|
||||
SSB_SPROM8_LEDDC_ON_SHIFT);
|
||||
SPEX(leddc_off_time, SSB_SPROM8_LEDDC, SSB_SPROM8_LEDDC_OFF,
|
||||
SSB_SPROM8_LEDDC_OFF_SHIFT);
|
||||
|
||||
SPEX(txchain, SSB_SPROM8_TXRXC, SSB_SPROM8_TXRXC_TXCHAIN,
|
||||
SSB_SPROM8_TXRXC_TXCHAIN_SHIFT);
|
||||
SPEX(rxchain, SSB_SPROM8_TXRXC, SSB_SPROM8_TXRXC_RXCHAIN,
|
||||
SSB_SPROM8_TXRXC_RXCHAIN_SHIFT);
|
||||
SPEX(antswitch, SSB_SPROM8_TXRXC, SSB_SPROM8_TXRXC_SWITCH,
|
||||
SSB_SPROM8_TXRXC_SWITCH_SHIFT);
|
||||
|
||||
SPEX(opo, SSB_SPROM8_OFDM2GPO, 0x00ff, 0);
|
||||
|
||||
SPEX_ARRAY8(mcs2gpo, SSB_SPROM8_2G_MCSPO, ~0, 0);
|
||||
SPEX_ARRAY8(mcs5gpo, SSB_SPROM8_5G_MCSPO, ~0, 0);
|
||||
SPEX_ARRAY8(mcs5glpo, SSB_SPROM8_5GL_MCSPO, ~0, 0);
|
||||
SPEX_ARRAY8(mcs5ghpo, SSB_SPROM8_5GH_MCSPO, ~0, 0);
|
||||
|
||||
SPEX(rawtempsense, SSB_SPROM8_RAWTS, SSB_SPROM8_RAWTS_RAWTEMP,
|
||||
SSB_SPROM8_RAWTS_RAWTEMP_SHIFT);
|
||||
SPEX(measpower, SSB_SPROM8_RAWTS, SSB_SPROM8_RAWTS_MEASPOWER,
|
||||
SSB_SPROM8_RAWTS_MEASPOWER_SHIFT);
|
||||
SPEX(tempsense_slope, SSB_SPROM8_OPT_CORRX,
|
||||
SSB_SPROM8_OPT_CORRX_TEMP_SLOPE,
|
||||
SSB_SPROM8_OPT_CORRX_TEMP_SLOPE_SHIFT);
|
||||
SPEX(tempcorrx, SSB_SPROM8_OPT_CORRX, SSB_SPROM8_OPT_CORRX_TEMPCORRX,
|
||||
SSB_SPROM8_OPT_CORRX_TEMPCORRX_SHIFT);
|
||||
SPEX(tempsense_option, SSB_SPROM8_OPT_CORRX,
|
||||
SSB_SPROM8_OPT_CORRX_TEMP_OPTION,
|
||||
SSB_SPROM8_OPT_CORRX_TEMP_OPTION_SHIFT);
|
||||
SPEX(freqoffset_corr, SSB_SPROM8_HWIQ_IQSWP,
|
||||
SSB_SPROM8_HWIQ_IQSWP_FREQ_CORR,
|
||||
SSB_SPROM8_HWIQ_IQSWP_FREQ_CORR_SHIFT);
|
||||
SPEX(iqcal_swp_dis, SSB_SPROM8_HWIQ_IQSWP,
|
||||
SSB_SPROM8_HWIQ_IQSWP_IQCAL_SWP,
|
||||
SSB_SPROM8_HWIQ_IQSWP_IQCAL_SWP_SHIFT);
|
||||
SPEX(hw_iqcal_en, SSB_SPROM8_HWIQ_IQSWP, SSB_SPROM8_HWIQ_IQSWP_HW_IQCAL,
|
||||
SSB_SPROM8_HWIQ_IQSWP_HW_IQCAL_SHIFT);
|
||||
|
||||
SPEX(bw40po, SSB_SPROM8_BW40PO, ~0, 0);
|
||||
SPEX(cddpo, SSB_SPROM8_CDDPO, ~0, 0);
|
||||
SPEX(stbcpo, SSB_SPROM8_STBCPO, ~0, 0);
|
||||
SPEX(bwduppo, SSB_SPROM8_BWDUPPO, ~0, 0);
|
||||
|
||||
SPEX(tempthresh, SSB_SPROM8_THERMAL, SSB_SPROM8_THERMAL_TRESH,
|
||||
SSB_SPROM8_THERMAL_TRESH_SHIFT);
|
||||
SPEX(tempoffset, SSB_SPROM8_THERMAL, SSB_SPROM8_THERMAL_OFFSET,
|
||||
SSB_SPROM8_THERMAL_OFFSET_SHIFT);
|
||||
SPEX(phycal_tempdelta, SSB_SPROM8_TEMPDELTA,
|
||||
SSB_SPROM8_TEMPDELTA_PHYCAL,
|
||||
SSB_SPROM8_TEMPDELTA_PHYCAL_SHIFT);
|
||||
SPEX(temps_period, SSB_SPROM8_TEMPDELTA, SSB_SPROM8_TEMPDELTA_PERIOD,
|
||||
SSB_SPROM8_TEMPDELTA_PERIOD_SHIFT);
|
||||
SPEX(temps_hysteresis, SSB_SPROM8_TEMPDELTA,
|
||||
SSB_SPROM8_TEMPDELTA_HYSTERESIS,
|
||||
SSB_SPROM8_TEMPDELTA_HYSTERESIS_SHIFT);
|
||||
}
|
||||
|
||||
static int sprom_extract(struct bcma_fbs *priv, const u16 *in, u16 size)
|
||||
{
|
||||
struct ssb_sprom *out = &priv->sprom;
|
||||
|
||||
memset(out, 0, sizeof(*out));
|
||||
|
||||
out->revision = in[size - 1] & 0x00FF;
|
||||
if (out->revision < 8 || out->revision > 11) {
|
||||
dev_warn(priv->dev,
|
||||
"Unsupported SPROM revision %d detected."
|
||||
" Will extract v8\n",
|
||||
out->revision);
|
||||
out->revision = 8;
|
||||
}
|
||||
|
||||
sprom_extract_r8(out, in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bcma_fbs_fixup(struct bcma_fbs *priv, u16 *sprom)
|
||||
{
|
||||
struct device_node *node = priv->dev->of_node;
|
||||
u32 fixups, off, val;
|
||||
int i = 0;
|
||||
|
||||
if (!of_get_property(node, "brcm,sprom-fixups", &fixups))
|
||||
return;
|
||||
|
||||
fixups /= sizeof(u32);
|
||||
|
||||
dev_info(priv->dev, "patching SPROM with %u fixups...\n", fixups >> 1);
|
||||
|
||||
while (i < fixups) {
|
||||
if (of_property_read_u32_index(node, "brcm,sprom-fixups",
|
||||
i++, &off)) {
|
||||
dev_err(priv->dev, "error reading fixup[%u] offset\n",
|
||||
i - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (of_property_read_u32_index(node, "brcm,sprom-fixups",
|
||||
i++, &val)) {
|
||||
dev_err(priv->dev, "error reading fixup[%u] value\n",
|
||||
i - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
dev_dbg(priv->dev, "fixup[%d]=0x%04x\n", off, val);
|
||||
|
||||
sprom[off] = val;
|
||||
}
|
||||
}
|
||||
|
||||
static bool sprom_override_devid(struct bcma_fbs *priv, struct ssb_sprom *out,
|
||||
const u16 *in)
|
||||
{
|
||||
SPEX(dev_id, 0x0060, 0xFFFF, 0);
|
||||
return !!out->dev_id;
|
||||
}
|
||||
|
||||
static void bcma_fbs_set(struct bcma_fbs *priv, struct device_node *node)
|
||||
{
|
||||
struct ssb_sprom *sprom = &priv->sprom;
|
||||
const struct firmware *fw;
|
||||
const char *sprom_name;
|
||||
int err;
|
||||
|
||||
if (of_property_read_string(node, "brcm,sprom", &sprom_name))
|
||||
sprom_name = NULL;
|
||||
|
||||
if (sprom_name) {
|
||||
err = request_firmware_direct(&fw, sprom_name, priv->dev);
|
||||
if (err)
|
||||
dev_err(priv->dev, "%s load error\n", sprom_name);
|
||||
} else {
|
||||
err = -ENOENT;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
sprom->revision = 0x02;
|
||||
sprom->board_rev = 0x0017;
|
||||
sprom->country_code = 0x00;
|
||||
sprom->ant_available_bg = 0x03;
|
||||
sprom->pa0b0 = 0x15ae;
|
||||
sprom->pa0b1 = 0xfa85;
|
||||
sprom->pa0b2 = 0xfe8d;
|
||||
sprom->pa1b0 = 0xffff;
|
||||
sprom->pa1b1 = 0xffff;
|
||||
sprom->pa1b2 = 0xffff;
|
||||
sprom->gpio0 = 0xff;
|
||||
sprom->gpio1 = 0xff;
|
||||
sprom->gpio2 = 0xff;
|
||||
sprom->gpio3 = 0xff;
|
||||
sprom->maxpwr_bg = 0x4c;
|
||||
sprom->itssi_bg = 0x00;
|
||||
sprom->boardflags_lo = 0x2848;
|
||||
sprom->boardflags_hi = 0x0000;
|
||||
priv->devid_override = false;
|
||||
|
||||
dev_warn(priv->dev, "using basic SPROM\n");
|
||||
} else {
|
||||
size_t size = min(fw->size, (size_t) BCMA_FBS_MAX_SIZE);
|
||||
u16 tmp_sprom[BCMA_FBS_MAX_SIZE >> 1];
|
||||
u32 i, j;
|
||||
|
||||
for (i = 0, j = 0; i < size; i += 2, j++)
|
||||
tmp_sprom[j] = (fw->data[i] << 8) | fw->data[i + 1];
|
||||
|
||||
release_firmware(fw);
|
||||
bcma_fbs_fixup(priv, tmp_sprom);
|
||||
sprom_extract(priv, tmp_sprom, size >> 1);
|
||||
|
||||
priv->devid_override = sprom_override_devid(priv, sprom,
|
||||
tmp_sprom);
|
||||
}
|
||||
}
|
||||
|
||||
static int bcma_fbs_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct bcma_fbs *priv;
|
||||
unsigned long flags;
|
||||
u8 mac[ETH_ALEN];
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->dev = dev;
|
||||
|
||||
bcma_fbs_set(priv, node);
|
||||
|
||||
of_property_read_u32(node, "pci-bus", &priv->pci_bus);
|
||||
of_property_read_u32(node, "pci-dev", &priv->pci_dev);
|
||||
|
||||
of_get_mac_address(node, mac);
|
||||
if (is_valid_ether_addr(mac)) {
|
||||
dev_info(dev, "mtd mac %pM\n", mac);
|
||||
} else {
|
||||
random_ether_addr(mac);
|
||||
dev_info(dev, "random mac %pM\n", mac);
|
||||
}
|
||||
|
||||
memcpy(priv->sprom.il0mac, mac, ETH_ALEN);
|
||||
memcpy(priv->sprom.et0mac, mac, ETH_ALEN);
|
||||
memcpy(priv->sprom.et1mac, mac, ETH_ALEN);
|
||||
memcpy(priv->sprom.et2mac, mac, ETH_ALEN);
|
||||
|
||||
spin_lock_irqsave(&bcma_fbs_lock, flags);
|
||||
list_add(&priv->list, &bcma_fbs_list);
|
||||
spin_unlock_irqrestore(&bcma_fbs_lock, flags);
|
||||
|
||||
dev_info(dev, "registered SPROM for [%x:%x]\n",
|
||||
priv->pci_bus, priv->pci_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id bcma_fbs_of_match[] = {
|
||||
{ .compatible = "brcm,bcma-sprom", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bcma_fbs_of_match);
|
||||
|
||||
static struct platform_driver bcma_fbs_driver = {
|
||||
.probe = bcma_fbs_probe,
|
||||
.driver = {
|
||||
.name = "bcma-sprom",
|
||||
.of_match_table = bcma_fbs_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
int __init bcma_fbs_register(void)
|
||||
{
|
||||
return platform_driver_register(&bcma_fbs_driver);
|
||||
}
|
||||
@@ -12,6 +12,7 @@ config MTD_SPLIT_SQUASHFS_ROOT
|
||||
bool "Squashfs based root partition parser"
|
||||
depends on MTD_SPLIT_SUPPORT
|
||||
select MTD_SPLIT
|
||||
default n
|
||||
help
|
||||
This provides a parsing function which allows to detect the
|
||||
offset and size of the unused portion of a rootfs partition
|
||||
@@ -19,11 +20,6 @@ config MTD_SPLIT_SQUASHFS_ROOT
|
||||
|
||||
comment "Firmware partition parsers"
|
||||
|
||||
config MTD_SPLIT_BCM63XX_FW
|
||||
bool "BCM63xx firmware parser"
|
||||
depends on MTD_SPLIT_SUPPORT
|
||||
select MTD_SPLIT
|
||||
|
||||
config MTD_SPLIT_BCM_WFI_FW
|
||||
bool "Broadcom Whole Flash Image parser"
|
||||
depends on MTD_SPLIT_SUPPORT
|
||||
@@ -100,8 +96,3 @@ config MTD_SPLIT_ELF_FW
|
||||
bool "ELF loader firmware partition parser"
|
||||
depends on MTD_SPLIT_SUPPORT
|
||||
select MTD_SPLIT
|
||||
|
||||
config MTD_SPLIT_H3C_VFS
|
||||
bool "Parser finding rootfs appended to H3C VFS"
|
||||
depends on MTD_SPLIT_SUPPORT
|
||||
select MTD_SPLIT
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
obj-$(CONFIG_MTD_SPLIT) += mtdsplit.o
|
||||
obj-$(CONFIG_MTD_SPLIT_BCM63XX_FW) += mtdsplit_bcm63xx.o
|
||||
obj-$(CONFIG_MTD_SPLIT_BCM_WFI_FW) += mtdsplit_bcm_wfi.o
|
||||
obj-$(CONFIG_MTD_SPLIT_CFE_BOOTFS) += mtdsplit_cfe_bootfs.o
|
||||
obj-$(CONFIG_MTD_SPLIT_SEAMA_FW) += mtdsplit_seama.o
|
||||
@@ -15,4 +14,3 @@ obj-$(CONFIG_MTD_SPLIT_WRGG_FW) += mtdsplit_wrgg.o
|
||||
obj-$(CONFIG_MTD_SPLIT_MINOR_FW) += mtdsplit_minor.o
|
||||
obj-$(CONFIG_MTD_SPLIT_JIMAGE_FW) += mtdsplit_jimage.o
|
||||
obj-$(CONFIG_MTD_SPLIT_ELF_FW) += mtdsplit_elf.o
|
||||
obj-$(CONFIG_MTD_SPLIT_H3C_VFS) += mtdsplit_h3c_vfs.o
|
||||
|
||||
@@ -1,186 +0,0 @@
|
||||
/*
|
||||
* Firmware MTD split for BCM63XX, based on bcm63xxpart.c
|
||||
*
|
||||
* Copyright (C) 2006-2008 Florian Fainelli <florian@openwrt.org>
|
||||
* Copyright (C) 2006-2008 Mike Albon <malbon@openwrt.org>
|
||||
* Copyright (C) 2009-2010 Daniel Dickinson <openwrt@cshore.neomailbox.net>
|
||||
* Copyright (C) 2011-2013 Jonas Gorski <jonas.gorski@gmail.com>
|
||||
* Copyright (C) 2015 Simon Arlott <simon@fire.lp0.eu>
|
||||
* Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/bcm963xx_tag.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/byteorder/generic.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include "mtdsplit.h"
|
||||
|
||||
/* Ensure strings read from flash structs are null terminated */
|
||||
#define STR_NULL_TERMINATE(x) \
|
||||
do { char *_str = (x); _str[sizeof(x) - 1] = 0; } while (0)
|
||||
|
||||
#define BCM63XX_NR_PARTS 2
|
||||
|
||||
static int bcm63xx_read_image_tag(struct mtd_info *master, loff_t offset,
|
||||
struct bcm_tag *hdr)
|
||||
{
|
||||
int ret;
|
||||
size_t retlen;
|
||||
u32 computed_crc;
|
||||
|
||||
ret = mtd_read(master, offset, sizeof(*hdr), &retlen, (void *) hdr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (retlen != sizeof(*hdr))
|
||||
return -EIO;
|
||||
|
||||
computed_crc = crc32_le(IMAGETAG_CRC_START, (u8 *)hdr,
|
||||
offsetof(struct bcm_tag, header_crc));
|
||||
if (computed_crc == hdr->header_crc) {
|
||||
STR_NULL_TERMINATE(hdr->board_id);
|
||||
STR_NULL_TERMINATE(hdr->tag_version);
|
||||
|
||||
pr_info("CFE image tag found at 0x%llx with version %s, "
|
||||
"board type %s\n", offset, hdr->tag_version,
|
||||
hdr->board_id);
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
pr_err("CFE image tag at 0x%llx CRC invalid "
|
||||
"(expected %08x, actual %08x)\n",
|
||||
offset, hdr->header_crc, computed_crc);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int bcm63xx_parse_partitions(struct mtd_info *master,
|
||||
const struct mtd_partition **pparts,
|
||||
struct bcm_tag *hdr)
|
||||
{
|
||||
struct mtd_partition *parts;
|
||||
unsigned int flash_image_start;
|
||||
unsigned int kernel_address;
|
||||
unsigned int kernel_length;
|
||||
size_t kernel_offset = 0, kernel_size = 0;
|
||||
size_t rootfs_offset = 0, rootfs_size = 0;
|
||||
int kernel_part, rootfs_part;
|
||||
|
||||
STR_NULL_TERMINATE(hdr->flash_image_start);
|
||||
if (kstrtouint(hdr->flash_image_start, 10, &flash_image_start) ||
|
||||
flash_image_start < BCM963XX_EXTENDED_SIZE) {
|
||||
pr_err("invalid rootfs address: %*ph\n",
|
||||
(int) sizeof(hdr->flash_image_start),
|
||||
hdr->flash_image_start);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
STR_NULL_TERMINATE(hdr->kernel_address);
|
||||
if (kstrtouint(hdr->kernel_address, 10, &kernel_address) ||
|
||||
kernel_address < BCM963XX_EXTENDED_SIZE) {
|
||||
pr_err("invalid kernel address: %*ph\n",
|
||||
(int) sizeof(hdr->kernel_address), hdr->kernel_address);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
STR_NULL_TERMINATE(hdr->kernel_length);
|
||||
if (kstrtouint(hdr->kernel_length, 10, &kernel_length) ||
|
||||
!kernel_length) {
|
||||
pr_err("invalid kernel length: %*ph\n",
|
||||
(int) sizeof(hdr->kernel_length), hdr->kernel_length);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
kernel_offset = kernel_address - BCM963XX_EXTENDED_SIZE -
|
||||
mtdpart_get_offset(master);
|
||||
kernel_size = kernel_length;
|
||||
|
||||
if (flash_image_start < kernel_address) {
|
||||
/* rootfs first */
|
||||
rootfs_part = 0;
|
||||
kernel_part = 1;
|
||||
rootfs_offset = flash_image_start - BCM963XX_EXTENDED_SIZE -
|
||||
mtdpart_get_offset(master);
|
||||
rootfs_size = kernel_offset - rootfs_offset;
|
||||
} else {
|
||||
/* kernel first */
|
||||
kernel_part = 0;
|
||||
rootfs_part = 1;
|
||||
rootfs_offset = kernel_offset + kernel_size;
|
||||
rootfs_size = master->size - rootfs_offset;
|
||||
}
|
||||
|
||||
if (mtd_check_rootfs_magic(master, rootfs_offset, NULL))
|
||||
pr_warn("rootfs magic not found\n");
|
||||
|
||||
parts = kzalloc(BCM63XX_NR_PARTS * sizeof(*parts), GFP_KERNEL);
|
||||
if (!parts)
|
||||
return -ENOMEM;
|
||||
|
||||
parts[kernel_part].name = KERNEL_PART_NAME;
|
||||
parts[kernel_part].offset = kernel_offset;
|
||||
parts[kernel_part].size = kernel_size;
|
||||
|
||||
parts[rootfs_part].name = ROOTFS_PART_NAME;
|
||||
parts[rootfs_part].offset = rootfs_offset;
|
||||
parts[rootfs_part].size = rootfs_size;
|
||||
|
||||
*pparts = parts;
|
||||
return BCM63XX_NR_PARTS;
|
||||
}
|
||||
|
||||
static int mtdsplit_parse_bcm63xx(struct mtd_info *master,
|
||||
const struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data)
|
||||
{
|
||||
struct bcm_tag hdr;
|
||||
loff_t offset;
|
||||
|
||||
if (mtd_type_is_nand(master))
|
||||
return -EINVAL;
|
||||
|
||||
/* find bcm63xx_cfe image on erase block boundaries */
|
||||
for (offset = 0; offset < master->size; offset += master->erasesize) {
|
||||
if (!bcm63xx_read_image_tag(master, offset, (void *) &hdr))
|
||||
return bcm63xx_parse_partitions(master, pparts,
|
||||
(void *) &hdr);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct of_device_id mtdsplit_fit_of_match_table[] = {
|
||||
{ .compatible = "brcm,bcm963xx-imagetag" },
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct mtd_part_parser mtdsplit_bcm63xx_parser = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "bcm63xx-fw",
|
||||
.of_match_table = mtdsplit_fit_of_match_table,
|
||||
.parse_fn = mtdsplit_parse_bcm63xx,
|
||||
.type = MTD_PARSER_TYPE_FIRMWARE,
|
||||
};
|
||||
|
||||
static int __init mtdsplit_bcm63xx_init(void)
|
||||
{
|
||||
register_mtd_parser(&mtdsplit_bcm63xx_parser);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(mtdsplit_bcm63xx_init);
|
||||
@@ -21,204 +21,47 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/byteorder/generic.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/libfdt.h>
|
||||
#include <linux/of_fdt.h>
|
||||
|
||||
#include "mtdsplit.h"
|
||||
|
||||
// string macros from git://git.denx.de/u-boot.git/include/image.h
|
||||
struct fdt_header {
|
||||
uint32_t magic; /* magic word FDT_MAGIC */
|
||||
uint32_t totalsize; /* total size of DT block */
|
||||
uint32_t off_dt_struct; /* offset to structure */
|
||||
uint32_t off_dt_strings; /* offset to strings */
|
||||
uint32_t off_mem_rsvmap; /* offset to memory reserve map */
|
||||
uint32_t version; /* format version */
|
||||
uint32_t last_comp_version; /* last compatible version */
|
||||
|
||||
#define FIT_IMAGES_PATH "/images"
|
||||
#define FIT_DATA_PROP "data"
|
||||
#define FIT_DATA_POSITION_PROP "data-position"
|
||||
#define FIT_DATA_OFFSET_PROP "data-offset"
|
||||
#define FIT_DATA_SIZE_PROP "data-size"
|
||||
/* version 2 fields below */
|
||||
uint32_t boot_cpuid_phys; /* Which physical CPU id we're
|
||||
booting on */
|
||||
/* version 3 fields below */
|
||||
uint32_t size_dt_strings; /* size of the strings block */
|
||||
|
||||
// functions from git://git.denx.de/u-boot.git/common/image-fit.c
|
||||
|
||||
/**
|
||||
* fit_image_get_data - get data property and its size for a given component image node
|
||||
* @fit: pointer to the FIT format image header
|
||||
* @noffset: component image node offset
|
||||
* @data: double pointer to void, will hold data property's data address
|
||||
* @size: pointer to size_t, will hold data property's data size
|
||||
*
|
||||
* fit_image_get_data() finds data property in a given component image node.
|
||||
* If the property is found its data start address and size are returned to
|
||||
* the caller.
|
||||
*
|
||||
* returns:
|
||||
* 0, on success
|
||||
* -1, on failure
|
||||
*/
|
||||
static int fit_image_get_data(const void *fit, int noffset,
|
||||
const void **data, size_t *size)
|
||||
{
|
||||
int len;
|
||||
|
||||
*data = fdt_getprop(fit, noffset, FIT_DATA_PROP, &len);
|
||||
if (*data == NULL) {
|
||||
*size = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*size = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get 'data-offset' property from a given image node.
|
||||
*
|
||||
* @fit: pointer to the FIT image header
|
||||
* @noffset: component image node offset
|
||||
* @data_offset: holds the data-offset property
|
||||
*
|
||||
* returns:
|
||||
* 0, on success
|
||||
* -ENOENT if the property could not be found
|
||||
*/
|
||||
static int fit_image_get_data_offset(const void *fit, int noffset, int *data_offset)
|
||||
{
|
||||
const fdt32_t *val;
|
||||
|
||||
val = fdt_getprop(fit, noffset, FIT_DATA_OFFSET_PROP, NULL);
|
||||
if (!val)
|
||||
return -ENOENT;
|
||||
|
||||
*data_offset = fdt32_to_cpu(*val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get 'data-position' property from a given image node.
|
||||
*
|
||||
* @fit: pointer to the FIT image header
|
||||
* @noffset: component image node offset
|
||||
* @data_position: holds the data-position property
|
||||
*
|
||||
* returns:
|
||||
* 0, on success
|
||||
* -ENOENT if the property could not be found
|
||||
*/
|
||||
static int fit_image_get_data_position(const void *fit, int noffset,
|
||||
int *data_position)
|
||||
{
|
||||
const fdt32_t *val;
|
||||
|
||||
val = fdt_getprop(fit, noffset, FIT_DATA_POSITION_PROP, NULL);
|
||||
if (!val)
|
||||
return -ENOENT;
|
||||
|
||||
*data_position = fdt32_to_cpu(*val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get 'data-size' property from a given image node.
|
||||
*
|
||||
* @fit: pointer to the FIT image header
|
||||
* @noffset: component image node offset
|
||||
* @data_size: holds the data-size property
|
||||
*
|
||||
* returns:
|
||||
* 0, on success
|
||||
* -ENOENT if the property could not be found
|
||||
*/
|
||||
static int fit_image_get_data_size(const void *fit, int noffset, int *data_size)
|
||||
{
|
||||
const fdt32_t *val;
|
||||
|
||||
val = fdt_getprop(fit, noffset, FIT_DATA_SIZE_PROP, NULL);
|
||||
if (!val)
|
||||
return -ENOENT;
|
||||
|
||||
*data_size = fdt32_to_cpu(*val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fit_image_get_data_and_size - get data and its size including
|
||||
* both embedded and external data
|
||||
* @fit: pointer to the FIT format image header
|
||||
* @noffset: component image node offset
|
||||
* @data: double pointer to void, will hold data property's data address
|
||||
* @size: pointer to size_t, will hold data property's data size
|
||||
*
|
||||
* fit_image_get_data_and_size() finds data and its size including
|
||||
* both embedded and external data. If the property is found
|
||||
* its data start address and size are returned to the caller.
|
||||
*
|
||||
* returns:
|
||||
* 0, on success
|
||||
* otherwise, on failure
|
||||
*/
|
||||
static int fit_image_get_data_and_size(const void *fit, int noffset,
|
||||
const void **data, size_t *size)
|
||||
{
|
||||
bool external_data = false;
|
||||
int offset;
|
||||
int len;
|
||||
int ret;
|
||||
|
||||
if (!fit_image_get_data_position(fit, noffset, &offset)) {
|
||||
external_data = true;
|
||||
} else if (!fit_image_get_data_offset(fit, noffset, &offset)) {
|
||||
external_data = true;
|
||||
/*
|
||||
* For FIT with external data, figure out where
|
||||
* the external images start. This is the base
|
||||
* for the data-offset properties in each image.
|
||||
*/
|
||||
offset += ((fdt_totalsize(fit) + 3) & ~3);
|
||||
}
|
||||
|
||||
if (external_data) {
|
||||
ret = fit_image_get_data_size(fit, noffset, &len);
|
||||
if (!ret) {
|
||||
*data = fit + offset;
|
||||
*size = len;
|
||||
}
|
||||
} else {
|
||||
ret = fit_image_get_data(fit, noffset, data, size);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
/* version 17 fields below */
|
||||
uint32_t size_dt_struct; /* size of the structure block */
|
||||
};
|
||||
|
||||
static int
|
||||
mtdsplit_fit_parse(struct mtd_info *mtd,
|
||||
const struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data)
|
||||
{
|
||||
struct device_node *np = mtd_get_of_node(mtd);
|
||||
const char *cmdline_match = NULL;
|
||||
struct fdt_header hdr;
|
||||
size_t hdr_len, retlen;
|
||||
size_t offset;
|
||||
u32 offset_start = 0;
|
||||
size_t fit_offset, fit_size;
|
||||
size_t rootfs_offset, rootfs_size;
|
||||
size_t data_size, img_total, max_size = 0;
|
||||
struct mtd_partition *parts;
|
||||
int ret, ndepth, noffset, images_noffset;
|
||||
const void *img_data;
|
||||
void *fit;
|
||||
|
||||
of_property_read_string(np, "openwrt,cmdline-match", &cmdline_match);
|
||||
if (cmdline_match && !strstr(saved_command_line, cmdline_match))
|
||||
return -ENODEV;
|
||||
|
||||
of_property_read_u32(np, "openwrt,fit-offset", &offset_start);
|
||||
int ret;
|
||||
|
||||
hdr_len = sizeof(struct fdt_header);
|
||||
|
||||
/* Parse the MTD device & search for the FIT image location */
|
||||
for(offset = 0; offset + hdr_len <= mtd->size; offset += mtd->erasesize) {
|
||||
ret = mtd_read(mtd, offset + offset_start, hdr_len, &retlen, (void*) &hdr);
|
||||
ret = mtd_read(mtd, offset, hdr_len, &retlen, (void*) &hdr);
|
||||
if (ret) {
|
||||
pr_err("read error in \"%s\" at offset 0x%llx\n",
|
||||
mtd->name, (unsigned long long) offset);
|
||||
@@ -250,92 +93,31 @@ mtdsplit_fit_parse(struct mtd_info *mtd,
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* Classic uImage.FIT has all data embedded into the FDT
|
||||
* data structure. Hence the total size of the image equals
|
||||
* the total size of the FDT structure.
|
||||
* Modern uImage.FIT may have only references to data in FDT,
|
||||
* hence we need to parse FDT structure to find the end of the
|
||||
* last external data refernced.
|
||||
*/
|
||||
if (fit_size > 0x1000) {
|
||||
enum mtdsplit_part_type type;
|
||||
|
||||
/* Search for the rootfs partition after the FIT image */
|
||||
ret = mtd_find_rootfs_from(mtd, fit_offset + fit_size + offset_start, mtd->size,
|
||||
&rootfs_offset, &type);
|
||||
if (ret) {
|
||||
pr_info("no rootfs found after FIT image in \"%s\"\n",
|
||||
mtd->name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
rootfs_size = mtd->size - rootfs_offset;
|
||||
|
||||
parts = kzalloc(2 * sizeof(*parts), GFP_KERNEL);
|
||||
if (!parts)
|
||||
return -ENOMEM;
|
||||
|
||||
parts[0].name = KERNEL_PART_NAME;
|
||||
parts[0].offset = fit_offset;
|
||||
parts[0].size = mtd_rounddown_to_eb(fit_size + offset_start, mtd) + mtd->erasesize;
|
||||
|
||||
if (type == MTDSPLIT_PART_TYPE_UBI)
|
||||
parts[1].name = UBI_PART_NAME;
|
||||
else
|
||||
parts[1].name = ROOTFS_PART_NAME;
|
||||
parts[1].offset = rootfs_offset;
|
||||
parts[1].size = rootfs_size;
|
||||
|
||||
*pparts = parts;
|
||||
|
||||
return 2;
|
||||
} else {
|
||||
/* Search for rootfs_data after FIT external data */
|
||||
fit = kzalloc(fit_size, GFP_KERNEL);
|
||||
ret = mtd_read(mtd, offset, fit_size + offset_start, &retlen, fit);
|
||||
if (ret) {
|
||||
pr_err("read error in \"%s\" at offset 0x%llx\n",
|
||||
mtd->name, (unsigned long long) offset);
|
||||
return ret;
|
||||
}
|
||||
|
||||
images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH);
|
||||
if (images_noffset < 0) {
|
||||
pr_err("Can't find images parent node '%s' (%s)\n",
|
||||
FIT_IMAGES_PATH, fdt_strerror(images_noffset));
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
for (ndepth = 0,
|
||||
noffset = fdt_next_node(fit, images_noffset, &ndepth);
|
||||
(noffset >= 0) && (ndepth > 0);
|
||||
noffset = fdt_next_node(fit, noffset, &ndepth)) {
|
||||
if (ndepth == 1) {
|
||||
ret = fit_image_get_data_and_size(fit, noffset, &img_data, &data_size);
|
||||
if (ret)
|
||||
return 0;
|
||||
|
||||
img_total = data_size + (img_data - fit);
|
||||
|
||||
max_size = (max_size > img_total) ? max_size : img_total;
|
||||
}
|
||||
}
|
||||
|
||||
parts = kzalloc(sizeof(*parts), GFP_KERNEL);
|
||||
if (!parts)
|
||||
return -ENOMEM;
|
||||
|
||||
parts[0].name = ROOTFS_SPLIT_NAME;
|
||||
parts[0].offset = fit_offset + mtd_rounddown_to_eb(max_size, mtd) + mtd->erasesize;
|
||||
parts[0].size = mtd->size - parts[0].offset;
|
||||
|
||||
*pparts = parts;
|
||||
|
||||
kfree(fit);
|
||||
|
||||
return 1;
|
||||
/* Search for the rootfs partition after the FIT image */
|
||||
ret = mtd_find_rootfs_from(mtd, fit_offset + fit_size, mtd->size,
|
||||
&rootfs_offset, NULL);
|
||||
if (ret) {
|
||||
pr_info("no rootfs found after FIT image in \"%s\"\n",
|
||||
mtd->name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
rootfs_size = mtd->size - rootfs_offset;
|
||||
|
||||
parts = kzalloc(2 * sizeof(*parts), GFP_KERNEL);
|
||||
if (!parts)
|
||||
return -ENOMEM;
|
||||
|
||||
parts[0].name = KERNEL_PART_NAME;
|
||||
parts[0].offset = fit_offset;
|
||||
parts[0].size = mtd_rounddown_to_eb(fit_size, mtd) + mtd->erasesize;
|
||||
|
||||
parts[1].name = ROOTFS_PART_NAME;
|
||||
parts[1].offset = rootfs_offset;
|
||||
parts[1].size = rootfs_size;
|
||||
|
||||
*pparts = parts;
|
||||
return 2;
|
||||
}
|
||||
|
||||
static const struct of_device_id mtdsplit_fit_of_match_table[] = {
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Some devices made by H3C use a "VFS" filesystem to store firmware images.
|
||||
* This parses the start of the filesystem to read the length of the first
|
||||
* file (the kernel image). It then searches for the rootfs after the end of
|
||||
* the file data. This driver assumes that the filesystem was generated by
|
||||
* mkh3cvfs, and only works if the filesystem matches the expected layout,
|
||||
* which includes the file name of the kernel image.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include "mtdsplit.h"
|
||||
|
||||
#define VFS_ERASEBLOCK_SIZE 0x10000
|
||||
#define VFS_BLOCK_SIZE 0x400
|
||||
#define VFS_BLOCKS_PER_ERASEBLOCK (VFS_ERASEBLOCK_SIZE / VFS_BLOCK_SIZE)
|
||||
|
||||
#define FORMAT_FLAG_OFFSET 0x0
|
||||
|
||||
#define FORMAT_FLAG (VFS_ERASEBLOCK_SIZE << 12 | VFS_BLOCK_SIZE)
|
||||
|
||||
#define FILE_ENTRY_OFFSET 0x800
|
||||
|
||||
#define FILE_ENTRY_FLAGS 0x3f
|
||||
#define FILE_ENTRY_PARENT_BLOCK 0
|
||||
#define FILE_ENTRY_PARENT_INDEX 0
|
||||
#define FILE_ENTRY_DATA_BLOCK 2
|
||||
#define FILE_ENTRY_NAME "openwrt-kernel.bin"
|
||||
|
||||
#define NR_PARTS 2
|
||||
|
||||
struct file_entry {
|
||||
uint8_t flags;
|
||||
|
||||
uint8_t res0[5];
|
||||
|
||||
uint16_t year;
|
||||
uint8_t month;
|
||||
uint8_t day;
|
||||
uint8_t hour;
|
||||
uint8_t minute;
|
||||
uint8_t second;
|
||||
|
||||
uint8_t res1[3];
|
||||
|
||||
uint32_t length;
|
||||
|
||||
uint32_t parent_block;
|
||||
uint16_t parent_index;
|
||||
|
||||
uint8_t res2[2];
|
||||
|
||||
uint32_t data_block;
|
||||
|
||||
char name[96];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
static inline size_t block_offset(int block)
|
||||
{
|
||||
return VFS_ERASEBLOCK_SIZE * (block / (VFS_BLOCKS_PER_ERASEBLOCK-1))
|
||||
+ VFS_BLOCK_SIZE * (1 + (block % (VFS_BLOCKS_PER_ERASEBLOCK-1)));
|
||||
}
|
||||
|
||||
static inline int block_count(size_t size)
|
||||
{
|
||||
return (size + VFS_BLOCK_SIZE - 1) / VFS_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
static int mtdsplit_h3c_vfs_parse(struct mtd_info *mtd,
|
||||
const struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data)
|
||||
{
|
||||
struct mtd_partition *parts;
|
||||
uint32_t format_flag;
|
||||
struct file_entry file_entry;
|
||||
size_t retlen;
|
||||
int err;
|
||||
size_t kernel_size;
|
||||
size_t expected_offset;
|
||||
size_t rootfs_offset;
|
||||
|
||||
if (mtd->erasesize != VFS_ERASEBLOCK_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check format flag */
|
||||
err = mtd_read(mtd, FORMAT_FLAG_OFFSET, sizeof(format_flag), &retlen,
|
||||
(void *) &format_flag);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (retlen != sizeof(format_flag))
|
||||
return -EIO;
|
||||
|
||||
if (format_flag != FORMAT_FLAG)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check file entry */
|
||||
err = mtd_read(mtd, FILE_ENTRY_OFFSET, sizeof(file_entry), &retlen,
|
||||
(void *) &file_entry);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (retlen != sizeof(file_entry))
|
||||
return -EIO;
|
||||
|
||||
if (file_entry.flags != FILE_ENTRY_FLAGS)
|
||||
return -EINVAL;
|
||||
|
||||
if (file_entry.parent_block != FILE_ENTRY_PARENT_BLOCK)
|
||||
return -EINVAL;
|
||||
|
||||
if (file_entry.parent_index != FILE_ENTRY_PARENT_INDEX)
|
||||
return -EINVAL;
|
||||
|
||||
if (file_entry.data_block != FILE_ENTRY_DATA_BLOCK)
|
||||
return -EINVAL;
|
||||
|
||||
if (strncmp(file_entry.name, FILE_ENTRY_NAME, sizeof(file_entry.name)) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Find rootfs offset */
|
||||
kernel_size = block_offset(file_entry.data_block +
|
||||
block_count(file_entry.length) - 1) +
|
||||
VFS_BLOCK_SIZE;
|
||||
|
||||
expected_offset = mtd_roundup_to_eb(kernel_size, mtd);
|
||||
|
||||
err = mtd_find_rootfs_from(mtd, expected_offset, mtd->size,
|
||||
&rootfs_offset, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
parts = kzalloc(NR_PARTS * sizeof(*parts), GFP_KERNEL);
|
||||
if (!parts)
|
||||
return -ENOMEM;
|
||||
|
||||
parts[0].name = KERNEL_PART_NAME;
|
||||
parts[0].offset = 0;
|
||||
parts[0].size = rootfs_offset;
|
||||
|
||||
parts[1].name = ROOTFS_PART_NAME;
|
||||
parts[1].offset = rootfs_offset;
|
||||
parts[1].size = mtd->size - rootfs_offset;
|
||||
|
||||
*pparts = parts;
|
||||
return NR_PARTS;
|
||||
}
|
||||
|
||||
static const struct of_device_id mtdsplit_h3c_vfs_of_match_table[] = {
|
||||
{ .compatible = "h3c,vfs-firmware" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mtdsplit_h3c_vfs_of_match_table);
|
||||
|
||||
static struct mtd_part_parser mtdsplit_h3c_vfs_parser = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "h3c-vfs",
|
||||
.of_match_table = mtdsplit_h3c_vfs_of_match_table,
|
||||
.parse_fn = mtdsplit_h3c_vfs_parse,
|
||||
.type = MTD_PARSER_TYPE_FIRMWARE,
|
||||
};
|
||||
|
||||
module_mtd_part_parser(mtdsplit_h3c_vfs_parser);
|
||||
@@ -1,465 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017 MediaTek Inc.
|
||||
* Author: Xiangsheng Hou <xiangsheng.hou@mediatek.com>
|
||||
* Copyright (c) 2020-2022 Felix Fietkau <nbd@nbd.name>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bits.h>
|
||||
#include "mtk_bmt.h"
|
||||
|
||||
struct bmt_desc bmtd = {};
|
||||
|
||||
/* -------- Nand operations wrapper -------- */
|
||||
int bbt_nand_copy(u16 dest_blk, u16 src_blk, loff_t max_offset)
|
||||
{
|
||||
int pages = bmtd.blk_size >> bmtd.pg_shift;
|
||||
loff_t src = (loff_t)src_blk << bmtd.blk_shift;
|
||||
loff_t dest = (loff_t)dest_blk << bmtd.blk_shift;
|
||||
loff_t offset = 0;
|
||||
uint8_t oob[64];
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < pages; i++) {
|
||||
struct mtd_oob_ops rd_ops = {
|
||||
.mode = MTD_OPS_PLACE_OOB,
|
||||
.oobbuf = oob,
|
||||
.ooblen = min_t(int, bmtd.mtd->oobsize / pages, sizeof(oob)),
|
||||
.datbuf = bmtd.data_buf,
|
||||
.len = bmtd.pg_size,
|
||||
};
|
||||
struct mtd_oob_ops wr_ops = {
|
||||
.mode = MTD_OPS_PLACE_OOB,
|
||||
.oobbuf = oob,
|
||||
.datbuf = bmtd.data_buf,
|
||||
.len = bmtd.pg_size,
|
||||
};
|
||||
|
||||
if (offset >= max_offset)
|
||||
break;
|
||||
|
||||
ret = bmtd._read_oob(bmtd.mtd, src + offset, &rd_ops);
|
||||
if (ret < 0 && !mtd_is_bitflip(ret))
|
||||
return ret;
|
||||
|
||||
if (!rd_ops.retlen)
|
||||
break;
|
||||
|
||||
ret = bmtd._write_oob(bmtd.mtd, dest + offset, &wr_ops);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
wr_ops.ooblen = rd_ops.oobretlen;
|
||||
offset += rd_ops.retlen;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------- Bad Blocks Management -------- */
|
||||
bool mapping_block_in_range(int block, int *start, int *end)
|
||||
{
|
||||
const __be32 *cur = bmtd.remap_range;
|
||||
u32 addr = block << bmtd.blk_shift;
|
||||
int i;
|
||||
|
||||
if (!cur || !bmtd.remap_range_len) {
|
||||
*start = 0;
|
||||
*end = bmtd.total_blks;
|
||||
return true;
|
||||
}
|
||||
|
||||
for (i = 0; i < bmtd.remap_range_len; i++, cur += 2) {
|
||||
if (addr < be32_to_cpu(cur[0]) || addr >= be32_to_cpu(cur[1]))
|
||||
continue;
|
||||
|
||||
*start = be32_to_cpu(cur[0]);
|
||||
*end = be32_to_cpu(cur[1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
mtk_bmt_remap_block(u32 block, u32 mapped_block, int copy_len)
|
||||
{
|
||||
int start, end;
|
||||
|
||||
if (!mapping_block_in_range(block, &start, &end))
|
||||
return false;
|
||||
|
||||
return bmtd.ops->remap_block(block, mapped_block, copy_len);
|
||||
}
|
||||
|
||||
static int
|
||||
mtk_bmt_read(struct mtd_info *mtd, loff_t from,
|
||||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
struct mtd_oob_ops cur_ops = *ops;
|
||||
int retry_count = 0;
|
||||
loff_t cur_from;
|
||||
int ret = 0;
|
||||
int max_bitflips = 0;
|
||||
|
||||
ops->retlen = 0;
|
||||
ops->oobretlen = 0;
|
||||
|
||||
while (ops->retlen < ops->len || ops->oobretlen < ops->ooblen) {
|
||||
int cur_ret;
|
||||
|
||||
u32 offset = from & (bmtd.blk_size - 1);
|
||||
u32 block = from >> bmtd.blk_shift;
|
||||
int cur_block;
|
||||
|
||||
cur_block = bmtd.ops->get_mapping_block(block);
|
||||
if (cur_block < 0)
|
||||
return -EIO;
|
||||
|
||||
cur_from = ((loff_t)cur_block << bmtd.blk_shift) + offset;
|
||||
|
||||
cur_ops.oobretlen = 0;
|
||||
cur_ops.retlen = 0;
|
||||
cur_ops.len = min_t(u32, mtd->erasesize - offset,
|
||||
ops->len - ops->retlen);
|
||||
cur_ret = bmtd._read_oob(mtd, cur_from, &cur_ops);
|
||||
if (cur_ret < 0)
|
||||
ret = cur_ret;
|
||||
else
|
||||
max_bitflips = max_t(int, max_bitflips, cur_ret);
|
||||
if (cur_ret < 0 && !mtd_is_bitflip(cur_ret)) {
|
||||
if (mtk_bmt_remap_block(block, cur_block, mtd->erasesize) &&
|
||||
retry_count++ < 10)
|
||||
continue;
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (mtd->bitflip_threshold && cur_ret >= mtd->bitflip_threshold)
|
||||
mtk_bmt_remap_block(block, cur_block, mtd->erasesize);
|
||||
|
||||
ops->retlen += cur_ops.retlen;
|
||||
ops->oobretlen += cur_ops.oobretlen;
|
||||
|
||||
cur_ops.ooboffs = 0;
|
||||
cur_ops.datbuf += cur_ops.retlen;
|
||||
cur_ops.oobbuf += cur_ops.oobretlen;
|
||||
cur_ops.ooblen -= cur_ops.oobretlen;
|
||||
|
||||
if (!cur_ops.len)
|
||||
cur_ops.len = mtd->erasesize - offset;
|
||||
|
||||
from += cur_ops.len;
|
||||
retry_count = 0;
|
||||
}
|
||||
|
||||
out:
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
static int
|
||||
mtk_bmt_write(struct mtd_info *mtd, loff_t to,
|
||||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
struct mtd_oob_ops cur_ops = *ops;
|
||||
int retry_count = 0;
|
||||
loff_t cur_to;
|
||||
int ret;
|
||||
|
||||
ops->retlen = 0;
|
||||
ops->oobretlen = 0;
|
||||
|
||||
while (ops->retlen < ops->len || ops->oobretlen < ops->ooblen) {
|
||||
u32 offset = to & (bmtd.blk_size - 1);
|
||||
u32 block = to >> bmtd.blk_shift;
|
||||
int cur_block;
|
||||
|
||||
cur_block = bmtd.ops->get_mapping_block(block);
|
||||
if (cur_block < 0)
|
||||
return -EIO;
|
||||
|
||||
cur_to = ((loff_t)cur_block << bmtd.blk_shift) + offset;
|
||||
|
||||
cur_ops.oobretlen = 0;
|
||||
cur_ops.retlen = 0;
|
||||
cur_ops.len = min_t(u32, bmtd.blk_size - offset,
|
||||
ops->len - ops->retlen);
|
||||
ret = bmtd._write_oob(mtd, cur_to, &cur_ops);
|
||||
if (ret < 0) {
|
||||
if (mtk_bmt_remap_block(block, cur_block, offset) &&
|
||||
retry_count++ < 10)
|
||||
continue;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ops->retlen += cur_ops.retlen;
|
||||
ops->oobretlen += cur_ops.oobretlen;
|
||||
|
||||
cur_ops.ooboffs = 0;
|
||||
cur_ops.datbuf += cur_ops.retlen;
|
||||
cur_ops.oobbuf += cur_ops.oobretlen;
|
||||
cur_ops.ooblen -= cur_ops.oobretlen;
|
||||
|
||||
if (!cur_ops.len)
|
||||
cur_ops.len = mtd->erasesize - offset;
|
||||
|
||||
to += cur_ops.len;
|
||||
retry_count = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mtk_bmt_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
{
|
||||
struct erase_info mapped_instr = {
|
||||
.len = bmtd.blk_size,
|
||||
};
|
||||
int retry_count = 0;
|
||||
u64 start_addr, end_addr;
|
||||
int ret;
|
||||
u16 orig_block;
|
||||
int block;
|
||||
|
||||
start_addr = instr->addr & (~mtd->erasesize_mask);
|
||||
end_addr = instr->addr + instr->len;
|
||||
|
||||
while (start_addr < end_addr) {
|
||||
orig_block = start_addr >> bmtd.blk_shift;
|
||||
block = bmtd.ops->get_mapping_block(orig_block);
|
||||
if (block < 0)
|
||||
return -EIO;
|
||||
mapped_instr.addr = (loff_t)block << bmtd.blk_shift;
|
||||
ret = bmtd._erase(mtd, &mapped_instr);
|
||||
if (ret) {
|
||||
if (mtk_bmt_remap_block(orig_block, block, 0) &&
|
||||
retry_count++ < 10)
|
||||
continue;
|
||||
instr->fail_addr = start_addr;
|
||||
break;
|
||||
}
|
||||
start_addr += mtd->erasesize;
|
||||
retry_count = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
static int
|
||||
mtk_bmt_block_isbad(struct mtd_info *mtd, loff_t ofs)
|
||||
{
|
||||
int retry_count = 0;
|
||||
u16 orig_block = ofs >> bmtd.blk_shift;
|
||||
u16 block;
|
||||
int ret;
|
||||
|
||||
retry:
|
||||
block = bmtd.ops->get_mapping_block(orig_block);
|
||||
ret = bmtd._block_isbad(mtd, (loff_t)block << bmtd.blk_shift);
|
||||
if (ret) {
|
||||
if (mtk_bmt_remap_block(orig_block, block, bmtd.blk_size) &&
|
||||
retry_count++ < 10)
|
||||
goto retry;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
mtk_bmt_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||
{
|
||||
u16 orig_block = ofs >> bmtd.blk_shift;
|
||||
int block;
|
||||
|
||||
block = bmtd.ops->get_mapping_block(orig_block);
|
||||
if (block < 0)
|
||||
return -EIO;
|
||||
|
||||
mtk_bmt_remap_block(orig_block, block, bmtd.blk_size);
|
||||
|
||||
return bmtd._block_markbad(mtd, (loff_t)block << bmtd.blk_shift);
|
||||
}
|
||||
|
||||
static void
|
||||
mtk_bmt_replace_ops(struct mtd_info *mtd)
|
||||
{
|
||||
bmtd._read_oob = mtd->_read_oob;
|
||||
bmtd._write_oob = mtd->_write_oob;
|
||||
bmtd._erase = mtd->_erase;
|
||||
bmtd._block_isbad = mtd->_block_isbad;
|
||||
bmtd._block_markbad = mtd->_block_markbad;
|
||||
|
||||
mtd->_read_oob = mtk_bmt_read;
|
||||
mtd->_write_oob = mtk_bmt_write;
|
||||
mtd->_erase = mtk_bmt_mtd_erase;
|
||||
mtd->_block_isbad = mtk_bmt_block_isbad;
|
||||
mtd->_block_markbad = mtk_bmt_block_markbad;
|
||||
}
|
||||
|
||||
static int mtk_bmt_debug_repair(void *data, u64 val)
|
||||
{
|
||||
int block = val >> bmtd.blk_shift;
|
||||
int prev_block, new_block;
|
||||
|
||||
prev_block = bmtd.ops->get_mapping_block(block);
|
||||
if (prev_block < 0)
|
||||
return -EIO;
|
||||
|
||||
bmtd.ops->unmap_block(block);
|
||||
new_block = bmtd.ops->get_mapping_block(block);
|
||||
if (new_block < 0)
|
||||
return -EIO;
|
||||
|
||||
if (prev_block == new_block)
|
||||
return 0;
|
||||
|
||||
bbt_nand_erase(new_block);
|
||||
bbt_nand_copy(new_block, prev_block, bmtd.blk_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_bmt_debug_mark_good(void *data, u64 val)
|
||||
{
|
||||
bmtd.ops->unmap_block(val >> bmtd.blk_shift);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_bmt_debug_mark_bad(void *data, u64 val)
|
||||
{
|
||||
u32 block = val >> bmtd.blk_shift;
|
||||
int cur_block;
|
||||
|
||||
cur_block = bmtd.ops->get_mapping_block(block);
|
||||
if (cur_block < 0)
|
||||
return -EIO;
|
||||
|
||||
mtk_bmt_remap_block(block, cur_block, bmtd.blk_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_bmt_debug(void *data, u64 val)
|
||||
{
|
||||
return bmtd.ops->debug(data, val);
|
||||
}
|
||||
|
||||
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_repair, NULL, mtk_bmt_debug_repair, "%llu\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_mark_good, NULL, mtk_bmt_debug_mark_good, "%llu\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_mark_bad, NULL, mtk_bmt_debug_mark_bad, "%llu\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_debug, NULL, mtk_bmt_debug, "%llu\n");
|
||||
|
||||
static void
|
||||
mtk_bmt_add_debugfs(void)
|
||||
{
|
||||
struct dentry *dir;
|
||||
|
||||
dir = bmtd.debugfs_dir = debugfs_create_dir("mtk-bmt", NULL);
|
||||
if (!dir)
|
||||
return;
|
||||
|
||||
debugfs_create_file_unsafe("repair", S_IWUSR, dir, NULL, &fops_repair);
|
||||
debugfs_create_file_unsafe("mark_good", S_IWUSR, dir, NULL, &fops_mark_good);
|
||||
debugfs_create_file_unsafe("mark_bad", S_IWUSR, dir, NULL, &fops_mark_bad);
|
||||
debugfs_create_file_unsafe("debug", S_IWUSR, dir, NULL, &fops_debug);
|
||||
}
|
||||
|
||||
void mtk_bmt_detach(struct mtd_info *mtd)
|
||||
{
|
||||
if (bmtd.mtd != mtd)
|
||||
return;
|
||||
|
||||
if (bmtd.debugfs_dir)
|
||||
debugfs_remove_recursive(bmtd.debugfs_dir);
|
||||
bmtd.debugfs_dir = NULL;
|
||||
|
||||
kfree(bmtd.bbt_buf);
|
||||
kfree(bmtd.data_buf);
|
||||
|
||||
mtd->_read_oob = bmtd._read_oob;
|
||||
mtd->_write_oob = bmtd._write_oob;
|
||||
mtd->_erase = bmtd._erase;
|
||||
mtd->_block_isbad = bmtd._block_isbad;
|
||||
mtd->_block_markbad = bmtd._block_markbad;
|
||||
mtd->size = bmtd.total_blks << bmtd.blk_shift;
|
||||
|
||||
memset(&bmtd, 0, sizeof(bmtd));
|
||||
}
|
||||
|
||||
|
||||
int mtk_bmt_attach(struct mtd_info *mtd)
|
||||
{
|
||||
struct device_node *np;
|
||||
int ret = 0;
|
||||
|
||||
if (bmtd.mtd)
|
||||
return -ENOSPC;
|
||||
|
||||
np = mtd_get_of_node(mtd);
|
||||
if (!np)
|
||||
return 0;
|
||||
|
||||
if (of_property_read_bool(np, "mediatek,bmt-v2"))
|
||||
bmtd.ops = &mtk_bmt_v2_ops;
|
||||
else if (of_property_read_bool(np, "mediatek,nmbm"))
|
||||
bmtd.ops = &mtk_bmt_nmbm_ops;
|
||||
else if (of_property_read_bool(np, "mediatek,bbt"))
|
||||
bmtd.ops = &mtk_bmt_bbt_ops;
|
||||
else
|
||||
return 0;
|
||||
|
||||
bmtd.remap_range = of_get_property(np, "mediatek,bmt-remap-range",
|
||||
&bmtd.remap_range_len);
|
||||
bmtd.remap_range_len /= 8;
|
||||
|
||||
bmtd.mtd = mtd;
|
||||
mtk_bmt_replace_ops(mtd);
|
||||
|
||||
bmtd.blk_size = mtd->erasesize;
|
||||
bmtd.blk_shift = ffs(bmtd.blk_size) - 1;
|
||||
bmtd.pg_size = mtd->writesize;
|
||||
bmtd.pg_shift = ffs(bmtd.pg_size) - 1;
|
||||
bmtd.total_blks = mtd->size >> bmtd.blk_shift;
|
||||
|
||||
bmtd.data_buf = kzalloc(bmtd.pg_size + bmtd.mtd->oobsize, GFP_KERNEL);
|
||||
if (!bmtd.data_buf) {
|
||||
pr_info("nand: FATAL ERR: allocate buffer failed!\n");
|
||||
ret = -1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
memset(bmtd.data_buf, 0xff, bmtd.pg_size + bmtd.mtd->oobsize);
|
||||
|
||||
ret = bmtd.ops->init(np);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
mtk_bmt_add_debugfs();
|
||||
return 0;
|
||||
|
||||
error:
|
||||
mtk_bmt_detach(mtd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Xiangsheng Hou <xiangsheng.hou@mediatek.com>, Felix Fietkau <nbd@nbd.name>");
|
||||
MODULE_DESCRIPTION("Bad Block mapping management v2 for MediaTek NAND Flash Driver");
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
#ifndef __MTK_BMT_PRIV_H
|
||||
#define __MTK_BMT_PRIV_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/mtk_bmt.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#define MAIN_SIGNATURE_OFFSET 0
|
||||
#define OOB_SIGNATURE_OFFSET 1
|
||||
|
||||
#define BBT_LOG(fmt, ...) pr_debug("[BBT][%s|%d] "fmt"\n", __func__, __LINE__, ##__VA_ARGS__)
|
||||
|
||||
struct mtk_bmt_ops {
|
||||
char *sig;
|
||||
unsigned int sig_len;
|
||||
int (*init)(struct device_node *np);
|
||||
bool (*remap_block)(u16 block, u16 mapped_block, int copy_len);
|
||||
void (*unmap_block)(u16 block);
|
||||
int (*get_mapping_block)(int block);
|
||||
int (*debug)(void *data, u64 val);
|
||||
};
|
||||
|
||||
struct bbbt;
|
||||
struct nmbm_instance;
|
||||
|
||||
struct bmt_desc {
|
||||
struct mtd_info *mtd;
|
||||
unsigned char *bbt_buf;
|
||||
unsigned char *data_buf;
|
||||
|
||||
int (*_read_oob) (struct mtd_info *mtd, loff_t from,
|
||||
struct mtd_oob_ops *ops);
|
||||
int (*_write_oob) (struct mtd_info *mtd, loff_t to,
|
||||
struct mtd_oob_ops *ops);
|
||||
int (*_erase) (struct mtd_info *mtd, struct erase_info *instr);
|
||||
int (*_block_isbad) (struct mtd_info *mtd, loff_t ofs);
|
||||
int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs);
|
||||
|
||||
const struct mtk_bmt_ops *ops;
|
||||
|
||||
union {
|
||||
struct bbbt *bbt;
|
||||
struct nmbm_instance *ni;
|
||||
};
|
||||
|
||||
struct dentry *debugfs_dir;
|
||||
|
||||
u32 table_size;
|
||||
u32 pg_size;
|
||||
u32 blk_size;
|
||||
u16 pg_shift;
|
||||
u16 blk_shift;
|
||||
/* bbt logical address */
|
||||
u16 pool_lba;
|
||||
/* bbt physical address */
|
||||
u16 pool_pba;
|
||||
/* Maximum count of bad blocks that the vendor guaranteed */
|
||||
u16 bb_max;
|
||||
/* Total blocks of the Nand Chip */
|
||||
u16 total_blks;
|
||||
/* The block(n) BMT is located at (bmt_tbl[n]) */
|
||||
u16 bmt_blk_idx;
|
||||
/* How many pages needs to store 'struct bbbt' */
|
||||
u32 bmt_pgs;
|
||||
|
||||
const __be32 *remap_range;
|
||||
int remap_range_len;
|
||||
|
||||
/* to compensate for driver level remapping */
|
||||
u8 oob_offset;
|
||||
};
|
||||
|
||||
extern struct bmt_desc bmtd;
|
||||
extern const struct mtk_bmt_ops mtk_bmt_v2_ops;
|
||||
extern const struct mtk_bmt_ops mtk_bmt_bbt_ops;
|
||||
extern const struct mtk_bmt_ops mtk_bmt_nmbm_ops;
|
||||
|
||||
static inline u32 blk_pg(u16 block)
|
||||
{
|
||||
return (u32)(block << (bmtd.blk_shift - bmtd.pg_shift));
|
||||
}
|
||||
|
||||
static inline int
|
||||
bbt_nand_read(u32 page, unsigned char *dat, int dat_len,
|
||||
unsigned char *fdm, int fdm_len)
|
||||
{
|
||||
struct mtd_oob_ops ops = {
|
||||
.mode = MTD_OPS_PLACE_OOB,
|
||||
.ooboffs = bmtd.oob_offset,
|
||||
.oobbuf = fdm,
|
||||
.ooblen = fdm_len,
|
||||
.datbuf = dat,
|
||||
.len = dat_len,
|
||||
};
|
||||
int ret;
|
||||
|
||||
ret = bmtd._read_oob(bmtd.mtd, page << bmtd.pg_shift, &ops);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret)
|
||||
pr_info("%s: %d bitflips\n", __func__, ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int bbt_nand_erase(u16 block)
|
||||
{
|
||||
struct mtd_info *mtd = bmtd.mtd;
|
||||
struct erase_info instr = {
|
||||
.addr = (loff_t)block << bmtd.blk_shift,
|
||||
.len = bmtd.blk_size,
|
||||
};
|
||||
|
||||
return bmtd._erase(mtd, &instr);
|
||||
}
|
||||
|
||||
static inline int write_bmt(u16 block, unsigned char *dat)
|
||||
{
|
||||
struct mtd_oob_ops ops = {
|
||||
.mode = MTD_OPS_PLACE_OOB,
|
||||
.ooboffs = OOB_SIGNATURE_OFFSET + bmtd.oob_offset,
|
||||
.oobbuf = bmtd.ops->sig,
|
||||
.ooblen = bmtd.ops->sig_len,
|
||||
.datbuf = dat,
|
||||
.len = bmtd.bmt_pgs << bmtd.pg_shift,
|
||||
};
|
||||
loff_t addr = (loff_t)block << bmtd.blk_shift;
|
||||
|
||||
return bmtd._write_oob(bmtd.mtd, addr, &ops);
|
||||
}
|
||||
|
||||
int bbt_nand_copy(u16 dest_blk, u16 src_blk, loff_t max_offset);
|
||||
bool mapping_block_in_range(int block, int *start, int *end);
|
||||
|
||||
#endif
|
||||
@@ -1,203 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017 MediaTek Inc.
|
||||
* Author: Xiangsheng Hou <xiangsheng.hou@mediatek.com>
|
||||
* Copyright (c) 2020-2022 Felix Fietkau <nbd@nbd.name>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include "mtk_bmt.h"
|
||||
|
||||
static bool
|
||||
bbt_block_is_bad(u16 block)
|
||||
{
|
||||
u8 cur = bmtd.bbt_buf[block / 4];
|
||||
|
||||
return cur & (3 << ((block % 4) * 2));
|
||||
}
|
||||
|
||||
static void
|
||||
bbt_set_block_state(u16 block, bool bad)
|
||||
{
|
||||
u8 mask = (3 << ((block % 4) * 2));
|
||||
|
||||
if (bad)
|
||||
bmtd.bbt_buf[block / 4] |= mask;
|
||||
else
|
||||
bmtd.bbt_buf[block / 4] &= ~mask;
|
||||
|
||||
bbt_nand_erase(bmtd.bmt_blk_idx);
|
||||
write_bmt(bmtd.bmt_blk_idx, bmtd.bbt_buf);
|
||||
}
|
||||
|
||||
static int
|
||||
get_mapping_block_index_bbt(int block)
|
||||
{
|
||||
int start, end, ofs;
|
||||
int bad_blocks = 0;
|
||||
int i;
|
||||
|
||||
if (!mapping_block_in_range(block, &start, &end))
|
||||
return block;
|
||||
|
||||
start >>= bmtd.blk_shift;
|
||||
end >>= bmtd.blk_shift;
|
||||
/* skip bad blocks within the mapping range */
|
||||
ofs = block - start;
|
||||
for (i = start; i < end; i++) {
|
||||
if (bbt_block_is_bad(i))
|
||||
bad_blocks++;
|
||||
else if (ofs)
|
||||
ofs--;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (i < end)
|
||||
return i;
|
||||
|
||||
/* when overflowing, remap remaining blocks to bad ones */
|
||||
for (i = end - 1; bad_blocks > 0; i--) {
|
||||
if (!bbt_block_is_bad(i))
|
||||
continue;
|
||||
|
||||
bad_blocks--;
|
||||
if (bad_blocks <= ofs)
|
||||
return i;
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
static bool remap_block_bbt(u16 block, u16 mapped_blk, int copy_len)
|
||||
{
|
||||
int start, end;
|
||||
u16 new_blk;
|
||||
|
||||
if (!mapping_block_in_range(block, &start, &end))
|
||||
return false;
|
||||
|
||||
bbt_set_block_state(mapped_blk, true);
|
||||
|
||||
new_blk = get_mapping_block_index_bbt(block);
|
||||
bbt_nand_erase(new_blk);
|
||||
if (copy_len > 0)
|
||||
bbt_nand_copy(new_blk, mapped_blk, copy_len);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
unmap_block_bbt(u16 block)
|
||||
{
|
||||
bbt_set_block_state(block, false);
|
||||
}
|
||||
|
||||
static int
|
||||
mtk_bmt_read_bbt(void)
|
||||
{
|
||||
u8 oob_buf[8];
|
||||
int i;
|
||||
|
||||
for (i = bmtd.total_blks - 1; i >= bmtd.total_blks - 5; i--) {
|
||||
u32 page = i << (bmtd.blk_shift - bmtd.pg_shift);
|
||||
|
||||
if (bbt_nand_read(page, bmtd.bbt_buf, bmtd.pg_size,
|
||||
oob_buf, sizeof(oob_buf))) {
|
||||
pr_info("read_bbt: could not read block %d\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (oob_buf[0] != 0xff) {
|
||||
pr_info("read_bbt: bad block at %d\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (memcmp(&oob_buf[1], "mtknand", 7) != 0) {
|
||||
pr_info("read_bbt: signature mismatch in block %d\n", i);
|
||||
print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1, oob_buf, 8, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
pr_info("read_bbt: found bbt at block %d\n", i);
|
||||
bmtd.bmt_blk_idx = i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
mtk_bmt_init_bbt(struct device_node *np)
|
||||
{
|
||||
int buf_size = round_up(bmtd.total_blks >> 2, bmtd.blk_size);
|
||||
int ret;
|
||||
|
||||
bmtd.bbt_buf = kmalloc(buf_size, GFP_KERNEL);
|
||||
if (!bmtd.bbt_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(bmtd.bbt_buf, 0xff, buf_size);
|
||||
bmtd.mtd->size -= 4 * bmtd.mtd->erasesize;
|
||||
|
||||
ret = mtk_bmt_read_bbt();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
bmtd.bmt_pgs = buf_size / bmtd.pg_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_bmt_debug_bbt(void *data, u64 val)
|
||||
{
|
||||
char buf[5];
|
||||
int i, k;
|
||||
|
||||
switch (val) {
|
||||
case 0:
|
||||
for (i = 0; i < bmtd.total_blks; i += 4) {
|
||||
u8 cur = bmtd.bbt_buf[i / 4];
|
||||
|
||||
for (k = 0; k < 4; k++, cur >>= 2)
|
||||
buf[k] = (cur & 3) ? 'B' : '.';
|
||||
|
||||
buf[4] = 0;
|
||||
printk("[%06x] %s\n", i * bmtd.blk_size, buf);
|
||||
}
|
||||
break;
|
||||
case 100:
|
||||
#if 0
|
||||
for (i = bmtd.bmt_blk_idx; i < bmtd.total_blks - 1; i++)
|
||||
bbt_nand_erase(bmtd.bmt_blk_idx);
|
||||
#endif
|
||||
|
||||
bmtd.bmt_blk_idx = bmtd.total_blks - 1;
|
||||
bbt_nand_erase(bmtd.bmt_blk_idx);
|
||||
write_bmt(bmtd.bmt_blk_idx, bmtd.bbt_buf);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct mtk_bmt_ops mtk_bmt_bbt_ops = {
|
||||
.sig = "mtknand",
|
||||
.sig_len = 7,
|
||||
.init = mtk_bmt_init_bbt,
|
||||
.remap_block = remap_block_bbt,
|
||||
.unmap_block = unmap_block_bbt,
|
||||
.get_mapping_block = get_mapping_block_index_bbt,
|
||||
.debug = mtk_bmt_debug_bbt,
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,506 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017 MediaTek Inc.
|
||||
* Author: Xiangsheng Hou <xiangsheng.hou@mediatek.com>
|
||||
* Copyright (c) 2020-2022 Felix Fietkau <nbd@nbd.name>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include "mtk_bmt.h"
|
||||
|
||||
struct bbbt {
|
||||
char signature[3];
|
||||
/* This version is used to distinguish the legacy and new algorithm */
|
||||
#define BBMT_VERSION 2
|
||||
unsigned char version;
|
||||
/* Below 2 tables will be written in SLC */
|
||||
u16 bb_tbl[];
|
||||
};
|
||||
|
||||
struct bbmt {
|
||||
u16 block;
|
||||
#define NO_MAPPED 0
|
||||
#define NORMAL_MAPPED 1
|
||||
#define BMT_MAPPED 2
|
||||
u16 mapped;
|
||||
};
|
||||
|
||||
/* Maximum 8k blocks */
|
||||
#define BBPOOL_RATIO 2
|
||||
#define BB_TABLE_MAX bmtd.table_size
|
||||
#define BMT_TABLE_MAX (BB_TABLE_MAX * BBPOOL_RATIO / 100)
|
||||
#define BMT_TBL_DEF_VAL 0x0
|
||||
|
||||
static inline struct bbmt *bmt_tbl(struct bbbt *bbbt)
|
||||
{
|
||||
return (struct bbmt *)&bbbt->bb_tbl[bmtd.table_size];
|
||||
}
|
||||
|
||||
static u16 find_valid_block(u16 block)
|
||||
{
|
||||
u8 fdm[4];
|
||||
int ret;
|
||||
int loop = 0;
|
||||
|
||||
retry:
|
||||
if (block >= bmtd.total_blks)
|
||||
return 0;
|
||||
|
||||
ret = bbt_nand_read(blk_pg(block), bmtd.data_buf, bmtd.pg_size,
|
||||
fdm, sizeof(fdm));
|
||||
/* Read the 1st byte of FDM to judge whether it's a bad
|
||||
* or not
|
||||
*/
|
||||
if (ret || fdm[0] != 0xff) {
|
||||
pr_info("nand: found bad block 0x%x\n", block);
|
||||
if (loop >= bmtd.bb_max) {
|
||||
pr_info("nand: FATAL ERR: too many bad blocks!!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
loop++;
|
||||
block++;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
/* Find out all bad blocks, and fill in the mapping table */
|
||||
static int scan_bad_blocks(struct bbbt *bbt)
|
||||
{
|
||||
int i;
|
||||
u16 block = 0;
|
||||
|
||||
/* First time download, the block0 MUST NOT be a bad block,
|
||||
* this is guaranteed by vendor
|
||||
*/
|
||||
bbt->bb_tbl[0] = 0;
|
||||
|
||||
/*
|
||||
* Construct the mapping table of Normal data area(non-PMT/BMTPOOL)
|
||||
* G - Good block; B - Bad block
|
||||
* ---------------------------
|
||||
* physical |G|G|B|G|B|B|G|G|G|G|B|G|B|
|
||||
* ---------------------------
|
||||
* What bb_tbl[i] looks like:
|
||||
* physical block(i):
|
||||
* 0 1 2 3 4 5 6 7 8 9 a b c
|
||||
* mapped block(bb_tbl[i]):
|
||||
* 0 1 3 6 7 8 9 b ......
|
||||
* ATTENTION:
|
||||
* If new bad block ocurred(n), search bmt_tbl to find
|
||||
* a available block(x), and fill in the bb_tbl[n] = x;
|
||||
*/
|
||||
for (i = 1; i < bmtd.pool_lba; i++) {
|
||||
bbt->bb_tbl[i] = find_valid_block(bbt->bb_tbl[i - 1] + 1);
|
||||
BBT_LOG("bb_tbl[0x%x] = 0x%x", i, bbt->bb_tbl[i]);
|
||||
if (bbt->bb_tbl[i] == 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Physical Block start Address of BMT pool */
|
||||
bmtd.pool_pba = bbt->bb_tbl[i - 1] + 1;
|
||||
if (bmtd.pool_pba >= bmtd.total_blks - 2) {
|
||||
pr_info("nand: FATAL ERR: Too many bad blocks!!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
BBT_LOG("pool_pba=0x%x", bmtd.pool_pba);
|
||||
i = 0;
|
||||
block = bmtd.pool_pba;
|
||||
/*
|
||||
* The bmt table is used for runtime bad block mapping
|
||||
* G - Good block; B - Bad block
|
||||
* ---------------------------
|
||||
* physical |G|G|B|G|B|B|G|G|G|G|B|G|B|
|
||||
* ---------------------------
|
||||
* block: 0 1 2 3 4 5 6 7 8 9 a b c
|
||||
* What bmt_tbl[i] looks like in initial state:
|
||||
* i:
|
||||
* 0 1 2 3 4 5 6 7
|
||||
* bmt_tbl[i].block:
|
||||
* 0 1 3 6 7 8 9 b
|
||||
* bmt_tbl[i].mapped:
|
||||
* N N N N N N N B
|
||||
* N - Not mapped(Available)
|
||||
* M - Mapped
|
||||
* B - BMT
|
||||
* ATTENTION:
|
||||
* BMT always in the last valid block in pool
|
||||
*/
|
||||
while ((block = find_valid_block(block)) != 0) {
|
||||
bmt_tbl(bbt)[i].block = block;
|
||||
bmt_tbl(bbt)[i].mapped = NO_MAPPED;
|
||||
BBT_LOG("bmt_tbl[%d].block = 0x%x", i, block);
|
||||
block++;
|
||||
i++;
|
||||
}
|
||||
|
||||
/* i - How many available blocks in pool, which is the length of bmt_tbl[]
|
||||
* bmtd.bmt_blk_idx - bmt_tbl[bmtd.bmt_blk_idx].block => the BMT block
|
||||
*/
|
||||
bmtd.bmt_blk_idx = i - 1;
|
||||
bmt_tbl(bbt)[bmtd.bmt_blk_idx].mapped = BMT_MAPPED;
|
||||
|
||||
if (i < 1) {
|
||||
pr_info("nand: FATAL ERR: no space to store BMT!!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pr_info("[BBT] %d available blocks in BMT pool\n", i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool is_valid_bmt(unsigned char *buf, unsigned char *fdm)
|
||||
{
|
||||
struct bbbt *bbt = (struct bbbt *)buf;
|
||||
u8 *sig = (u8*)bbt->signature + MAIN_SIGNATURE_OFFSET;
|
||||
|
||||
|
||||
if (memcmp(bbt->signature + MAIN_SIGNATURE_OFFSET, "BMT", 3) == 0 &&
|
||||
memcmp(fdm + OOB_SIGNATURE_OFFSET, "bmt", 3) == 0) {
|
||||
if (bbt->version == BBMT_VERSION)
|
||||
return true;
|
||||
}
|
||||
BBT_LOG("[BBT] BMT Version not match,upgrage preloader and uboot please! sig=%02x%02x%02x, fdm=%02x%02x%02x",
|
||||
sig[0], sig[1], sig[2],
|
||||
fdm[1], fdm[2], fdm[3]);
|
||||
return false;
|
||||
}
|
||||
|
||||
static u16 get_bmt_index(struct bbmt *bmt)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while (bmt[i].block != BMT_TBL_DEF_VAL) {
|
||||
if (bmt[i].mapped == BMT_MAPPED)
|
||||
return i;
|
||||
i++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Write the Burner Bad Block Table to Nand Flash
|
||||
* n - write BMT to bmt_tbl[n]
|
||||
*/
|
||||
static u16 upload_bmt(struct bbbt *bbt, int n)
|
||||
{
|
||||
u16 block;
|
||||
|
||||
retry:
|
||||
if (n < 0 || bmt_tbl(bbt)[n].mapped == NORMAL_MAPPED) {
|
||||
pr_info("nand: FATAL ERR: no space to store BMT!\n");
|
||||
return (u16)-1;
|
||||
}
|
||||
|
||||
block = bmt_tbl(bbt)[n].block;
|
||||
BBT_LOG("n = 0x%x, block = 0x%x", n, block);
|
||||
if (bbt_nand_erase(block)) {
|
||||
bmt_tbl(bbt)[n].block = 0;
|
||||
/* erase failed, try the previous block: bmt_tbl[n - 1].block */
|
||||
n--;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
/* The signature offset is fixed set to 0,
|
||||
* oob signature offset is fixed set to 1
|
||||
*/
|
||||
memcpy(bbt->signature + MAIN_SIGNATURE_OFFSET, "BMT", 3);
|
||||
bbt->version = BBMT_VERSION;
|
||||
|
||||
if (write_bmt(block, (unsigned char *)bbt)) {
|
||||
bmt_tbl(bbt)[n].block = 0;
|
||||
|
||||
/* write failed, try the previous block in bmt_tbl[n - 1] */
|
||||
n--;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
/* Return the current index(n) of BMT pool (bmt_tbl[n]) */
|
||||
return n;
|
||||
}
|
||||
|
||||
static u16 find_valid_block_in_pool(struct bbbt *bbt)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (bmtd.bmt_blk_idx == 0)
|
||||
goto error;
|
||||
|
||||
for (i = 0; i < bmtd.bmt_blk_idx; i++) {
|
||||
if (bmt_tbl(bbt)[i].block != 0 && bmt_tbl(bbt)[i].mapped == NO_MAPPED) {
|
||||
bmt_tbl(bbt)[i].mapped = NORMAL_MAPPED;
|
||||
return bmt_tbl(bbt)[i].block;
|
||||
}
|
||||
}
|
||||
|
||||
error:
|
||||
pr_info("nand: FATAL ERR: BMT pool is run out!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We met a bad block, mark it as bad and map it to a valid block in pool,
|
||||
* if it's a write failure, we need to write the data to mapped block
|
||||
*/
|
||||
static bool remap_block_v2(u16 block, u16 mapped_block, int copy_len)
|
||||
{
|
||||
u16 new_block;
|
||||
struct bbbt *bbt;
|
||||
|
||||
bbt = bmtd.bbt;
|
||||
new_block = find_valid_block_in_pool(bbt);
|
||||
if (new_block == 0)
|
||||
return false;
|
||||
|
||||
/* Map new bad block to available block in pool */
|
||||
bbt->bb_tbl[block] = new_block;
|
||||
|
||||
/* Erase new block */
|
||||
bbt_nand_erase(new_block);
|
||||
if (copy_len > 0)
|
||||
bbt_nand_copy(new_block, mapped_block, copy_len);
|
||||
|
||||
bmtd.bmt_blk_idx = upload_bmt(bbt, bmtd.bmt_blk_idx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int get_mapping_block_index_v2(int block)
|
||||
{
|
||||
int start, end;
|
||||
|
||||
if (block >= bmtd.pool_lba)
|
||||
return block;
|
||||
|
||||
if (!mapping_block_in_range(block, &start, &end))
|
||||
return block;
|
||||
|
||||
return bmtd.bbt->bb_tbl[block];
|
||||
}
|
||||
|
||||
static void
|
||||
unmap_block_v2(u16 block)
|
||||
{
|
||||
bmtd.bbt->bb_tbl[block] = block;
|
||||
bmtd.bmt_blk_idx = upload_bmt(bmtd.bbt, bmtd.bmt_blk_idx);
|
||||
}
|
||||
|
||||
static unsigned long *
|
||||
mtk_bmt_get_mapping_mask(void)
|
||||
{
|
||||
struct bbmt *bbmt = bmt_tbl(bmtd.bbt);
|
||||
int main_blocks = bmtd.mtd->size >> bmtd.blk_shift;
|
||||
unsigned long *used;
|
||||
int i, k;
|
||||
|
||||
used = kcalloc(sizeof(unsigned long), BIT_WORD(bmtd.bmt_blk_idx) + 1, GFP_KERNEL);
|
||||
if (!used)
|
||||
return NULL;
|
||||
|
||||
for (i = 1; i < main_blocks; i++) {
|
||||
if (bmtd.bbt->bb_tbl[i] == i)
|
||||
continue;
|
||||
|
||||
for (k = 0; k < bmtd.bmt_blk_idx; k++) {
|
||||
if (bmtd.bbt->bb_tbl[i] != bbmt[k].block)
|
||||
continue;
|
||||
|
||||
set_bit(k, used);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return used;
|
||||
}
|
||||
|
||||
static int mtk_bmt_debug_v2(void *data, u64 val)
|
||||
{
|
||||
struct bbmt *bbmt = bmt_tbl(bmtd.bbt);
|
||||
struct mtd_info *mtd = bmtd.mtd;
|
||||
unsigned long *used;
|
||||
int main_blocks = mtd->size >> bmtd.blk_shift;
|
||||
int n_remap = 0;
|
||||
int i;
|
||||
|
||||
used = mtk_bmt_get_mapping_mask();
|
||||
if (!used)
|
||||
return -ENOMEM;
|
||||
|
||||
switch (val) {
|
||||
case 0:
|
||||
for (i = 1; i < main_blocks; i++) {
|
||||
if (bmtd.bbt->bb_tbl[i] == i)
|
||||
continue;
|
||||
|
||||
printk("remap [%x->%x]\n", i, bmtd.bbt->bb_tbl[i]);
|
||||
n_remap++;
|
||||
}
|
||||
for (i = 0; i <= bmtd.bmt_blk_idx; i++) {
|
||||
char c;
|
||||
|
||||
switch (bbmt[i].mapped) {
|
||||
case NO_MAPPED:
|
||||
continue;
|
||||
case NORMAL_MAPPED:
|
||||
c = 'm';
|
||||
if (test_bit(i, used))
|
||||
c = 'M';
|
||||
break;
|
||||
case BMT_MAPPED:
|
||||
c = 'B';
|
||||
break;
|
||||
default:
|
||||
c = 'X';
|
||||
break;
|
||||
}
|
||||
printk("[%x:%c] = 0x%x\n", i, c, bbmt[i].block);
|
||||
}
|
||||
break;
|
||||
case 100:
|
||||
for (i = 0; i <= bmtd.bmt_blk_idx; i++) {
|
||||
if (bbmt[i].mapped != NORMAL_MAPPED)
|
||||
continue;
|
||||
|
||||
if (test_bit(i, used))
|
||||
continue;
|
||||
|
||||
n_remap++;
|
||||
bbmt[i].mapped = NO_MAPPED;
|
||||
printk("free block [%d:%x]\n", i, bbmt[i].block);
|
||||
}
|
||||
if (n_remap)
|
||||
bmtd.bmt_blk_idx = upload_bmt(bmtd.bbt, bmtd.bmt_blk_idx);
|
||||
break;
|
||||
}
|
||||
|
||||
kfree(used);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_bmt_init_v2(struct device_node *np)
|
||||
{
|
||||
u32 bmt_pool_size, bmt_table_size;
|
||||
u32 bufsz, block;
|
||||
u16 pmt_block;
|
||||
|
||||
if (of_property_read_u32(np, "mediatek,bmt-pool-size",
|
||||
&bmt_pool_size) != 0)
|
||||
bmt_pool_size = 80;
|
||||
|
||||
if (of_property_read_u8(np, "mediatek,bmt-oob-offset",
|
||||
&bmtd.oob_offset) != 0)
|
||||
bmtd.oob_offset = 0;
|
||||
|
||||
if (of_property_read_u32(np, "mediatek,bmt-table-size",
|
||||
&bmt_table_size) != 0)
|
||||
bmt_table_size = 0x2000U;
|
||||
|
||||
bmtd.table_size = bmt_table_size;
|
||||
|
||||
pmt_block = bmtd.total_blks - bmt_pool_size - 2;
|
||||
|
||||
bmtd.mtd->size = pmt_block << bmtd.blk_shift;
|
||||
|
||||
/*
|
||||
* ---------------------------------------
|
||||
* | PMT(2blks) | BMT POOL(totalblks * 2%) |
|
||||
* ---------------------------------------
|
||||
* ^ ^
|
||||
* | |
|
||||
* pmt_block pmt_block + 2blocks(pool_lba)
|
||||
*
|
||||
* ATTETION!!!!!!
|
||||
* The blocks ahead of the boundary block are stored in bb_tbl
|
||||
* and blocks behind are stored in bmt_tbl
|
||||
*/
|
||||
|
||||
bmtd.pool_lba = (u16)(pmt_block + 2);
|
||||
bmtd.bb_max = bmtd.total_blks * BBPOOL_RATIO / 100;
|
||||
|
||||
bufsz = round_up(sizeof(struct bbbt) +
|
||||
bmt_table_size * sizeof(struct bbmt), bmtd.pg_size);
|
||||
bmtd.bmt_pgs = bufsz >> bmtd.pg_shift;
|
||||
|
||||
bmtd.bbt_buf = kzalloc(bufsz, GFP_KERNEL);
|
||||
if (!bmtd.bbt_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(bmtd.bbt_buf, 0xff, bufsz);
|
||||
|
||||
/* Scanning start from the first page of the last block
|
||||
* of whole flash
|
||||
*/
|
||||
bmtd.bbt = NULL;
|
||||
for (u16 block = bmtd.total_blks - 1; !bmtd.bbt && block >= bmtd.pool_lba; block--) {
|
||||
u8 fdm[4];
|
||||
|
||||
if (bbt_nand_read(blk_pg(block), bmtd.bbt_buf, bufsz, fdm, sizeof(fdm))) {
|
||||
/* Read failed, try the previous block */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_valid_bmt(bmtd.bbt_buf, fdm)) {
|
||||
/* No valid BMT found, try the previous block */
|
||||
continue;
|
||||
}
|
||||
|
||||
bmtd.bmt_blk_idx = get_bmt_index(bmt_tbl((struct bbbt *)bmtd.bbt_buf));
|
||||
if (bmtd.bmt_blk_idx == 0) {
|
||||
pr_info("[BBT] FATAL ERR: bmt block index is wrong!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
pr_info("[BBT] BMT.v2 is found at 0x%x\n", block);
|
||||
bmtd.bbt = (struct bbbt *)bmtd.bbt_buf;
|
||||
}
|
||||
|
||||
if (!bmtd.bbt) {
|
||||
/* BMT not found */
|
||||
if (bmtd.total_blks > BB_TABLE_MAX + BMT_TABLE_MAX) {
|
||||
pr_info("nand: FATAL: Too many blocks, can not support!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bmtd.bbt = (struct bbbt *)bmtd.bbt_buf;
|
||||
memset(bmt_tbl(bmtd.bbt), BMT_TBL_DEF_VAL,
|
||||
bmtd.table_size * sizeof(struct bbmt));
|
||||
|
||||
if (scan_bad_blocks(bmtd.bbt))
|
||||
return -1;
|
||||
|
||||
/* BMT always in the last valid block in pool */
|
||||
bmtd.bmt_blk_idx = upload_bmt(bmtd.bbt, bmtd.bmt_blk_idx);
|
||||
block = bmt_tbl(bmtd.bbt)[bmtd.bmt_blk_idx].block;
|
||||
pr_notice("[BBT] BMT.v2 is written into PBA:0x%x\n", block);
|
||||
|
||||
if (bmtd.bmt_blk_idx == 0)
|
||||
pr_info("nand: Warning: no available block in BMT pool!\n");
|
||||
else if (bmtd.bmt_blk_idx == (u16)-1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
const struct mtk_bmt_ops mtk_bmt_v2_ops = {
|
||||
.sig = "bmt",
|
||||
.sig_len = 3,
|
||||
.init = mtk_bmt_init_v2,
|
||||
.remap_block = remap_block_v2,
|
||||
.unmap_block = unmap_block_v2,
|
||||
.get_mapping_block = get_mapping_block_index_v2,
|
||||
.debug = mtk_bmt_debug_v2,
|
||||
};
|
||||
@@ -1,20 +1,20 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
|
||||
*
|
||||
* Debug addons for NAND Mapped-block Management (NMBM)
|
||||
*
|
||||
* Author: Weijie Gao <weijie.gao@mediatek.com>
|
||||
*/
|
||||
|
||||
#ifndef _NMBM_DEBUG_H_
|
||||
#define _NMBM_DEBUG_H_
|
||||
|
||||
#define nmbm_mark_block_color_normal(ni, start_ba, end_ba)
|
||||
#define nmbm_mark_block_color_bad(ni, ba)
|
||||
#define nmbm_mark_block_color_mgmt(ni, start_ba, end_ba)
|
||||
#define nmbm_mark_block_color_signature(ni, ba)
|
||||
#define nmbm_mark_block_color_info_table(ni, start_ba, end_ba)
|
||||
#define nmbm_mark_block_color_mapped(ni, ba)
|
||||
|
||||
#endif /* _NMBM_DEBUG_H_ */
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
|
||||
*
|
||||
* Debug addons for NAND Mapped-block Management (NMBM)
|
||||
*
|
||||
* Author: Weijie Gao <weijie.gao@mediatek.com>
|
||||
*/
|
||||
|
||||
#ifndef _NMBM_DEBUG_H_
|
||||
#define _NMBM_DEBUG_H_
|
||||
|
||||
#define nmbm_mark_block_color_normal(ni, start_ba, end_ba)
|
||||
#define nmbm_mark_block_color_bad(ni, ba)
|
||||
#define nmbm_mark_block_color_mgmt(ni, start_ba, end_ba)
|
||||
#define nmbm_mark_block_color_signature(ni, ba)
|
||||
#define nmbm_mark_block_color_info_table(ni, start_ba, end_ba)
|
||||
#define nmbm_mark_block_color_mapped(ni, ba)
|
||||
|
||||
#endif /* _NMBM_DEBUG_H_ */
|
||||
@@ -513,8 +513,6 @@ ar8216_read_port_link(struct ar8xxx_priv *priv, int port,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ETHERNET_PACKET_MANGLE
|
||||
|
||||
static struct sk_buff *
|
||||
ar8216_mangle_tx(struct net_device *dev, struct sk_buff *skb)
|
||||
{
|
||||
@@ -581,8 +579,6 @@ ar8216_mangle_rx(struct net_device *dev, struct sk_buff *skb)
|
||||
buf[15 + 2] = vlan & 0xff;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int
|
||||
ar8216_wait_bit(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val)
|
||||
{
|
||||
@@ -891,7 +887,7 @@ ar8216_phy_write(struct ar8xxx_priv *priv, int addr, int regnum, u16 val)
|
||||
static int
|
||||
ar8229_hw_init(struct ar8xxx_priv *priv)
|
||||
{
|
||||
phy_interface_t phy_if_mode;
|
||||
int phy_if_mode;
|
||||
|
||||
if (priv->initialized)
|
||||
return 0;
|
||||
@@ -899,7 +895,7 @@ ar8229_hw_init(struct ar8xxx_priv *priv)
|
||||
ar8xxx_write(priv, AR8216_REG_CTRL, AR8216_CTRL_RESET);
|
||||
ar8xxx_reg_wait(priv, AR8216_REG_CTRL, AR8216_CTRL_RESET, 0, 1000);
|
||||
|
||||
of_get_phy_mode(priv->pdev->of_node, &phy_if_mode);
|
||||
phy_if_mode = of_get_phy_mode(priv->pdev->of_node);
|
||||
|
||||
if (phy_if_mode == PHY_INTERFACE_MODE_GMII) {
|
||||
ar8xxx_write(priv, AR8229_REG_OPER_MODE0,
|
||||
@@ -2425,9 +2421,7 @@ static int
|
||||
ar8xxx_phy_config_init(struct phy_device *phydev)
|
||||
{
|
||||
struct ar8xxx_priv *priv = phydev->priv;
|
||||
#ifdef CONFIG_ETHERNET_PACKET_MANGLE
|
||||
struct net_device *dev = phydev->attached_dev;
|
||||
#endif
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(!priv))
|
||||
@@ -2455,15 +2449,13 @@ ar8xxx_phy_config_init(struct phy_device *phydev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
#ifdef CONFIG_ETHERNET_PACKET_MANGLE
|
||||
/* VID fixup only needed on ar8216 */
|
||||
if (chip_is_ar8216(priv)) {
|
||||
dev->phy_ptr = priv;
|
||||
dev->extra_priv_flags |= IFF_NO_IP_ALIGN;
|
||||
dev->priv_flags |= IFF_NO_IP_ALIGN;
|
||||
dev->eth_mangle_rx = ar8216_mangle_rx;
|
||||
dev->eth_mangle_tx = ar8216_mangle_tx;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -2692,12 +2684,10 @@ ar8xxx_phy_detach(struct phy_device *phydev)
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
#ifdef CONFIG_ETHERNET_PACKET_MANGLE
|
||||
dev->phy_ptr = NULL;
|
||||
dev->extra_priv_flags &= ~IFF_NO_IP_ALIGN;
|
||||
dev->priv_flags &= ~IFF_NO_IP_ALIGN;
|
||||
dev->eth_mangle_rx = NULL;
|
||||
dev->eth_mangle_tx = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -2725,6 +2715,13 @@ ar8xxx_phy_remove(struct phy_device *phydev)
|
||||
ar8xxx_free(priv);
|
||||
}
|
||||
|
||||
static int
|
||||
ar8xxx_phy_soft_reset(struct phy_device *phydev)
|
||||
{
|
||||
/* we don't need an extra reset */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct phy_driver ar8xxx_phy_driver[] = {
|
||||
{
|
||||
.phy_id = 0x004d0000,
|
||||
@@ -2736,6 +2733,7 @@ static struct phy_driver ar8xxx_phy_driver[] = {
|
||||
.config_init = ar8xxx_phy_config_init,
|
||||
.config_aneg = ar8xxx_phy_config_aneg,
|
||||
.read_status = ar8xxx_phy_read_status,
|
||||
.soft_reset = ar8xxx_phy_soft_reset,
|
||||
.get_features = ar8xxx_get_features,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -183,7 +183,7 @@ ar8327_phy_fixup(struct ar8xxx_priv *priv, int phy)
|
||||
|
||||
case 2:
|
||||
ar8xxx_phy_mmd_write(priv, phy, 0x7, 0x3c, 0x0);
|
||||
fallthrough;
|
||||
/* fallthrough */
|
||||
case 4:
|
||||
ar8xxx_phy_mmd_write(priv, phy, 0x3, 0x800d, 0x803f);
|
||||
ar8xxx_phy_dbg_write(priv, phy, 0x3d, 0x6860);
|
||||
|
||||
@@ -506,9 +506,15 @@ static int b53_configure_ports_of(struct b53_device *dev)
|
||||
if (fixed_link) {
|
||||
u32 spd;
|
||||
u8 po = GMII_PO_LINK;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
|
||||
phy_interface_t mode;
|
||||
#else
|
||||
int mode = of_get_phy_mode(pn);
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
|
||||
of_get_phy_mode(pn, &mode);
|
||||
#endif
|
||||
|
||||
if (!of_property_read_u32(fixed_link, "speed", &spd)) {
|
||||
switch (spd) {
|
||||
@@ -523,7 +529,7 @@ static int b53_configure_ports_of(struct b53_device *dev)
|
||||
po |= PORT_OVERRIDE_SPEED_2000M;
|
||||
else
|
||||
po |= GMII_PO_SPEED_2000M;
|
||||
fallthrough;
|
||||
/* fall through */
|
||||
case 1000:
|
||||
po |= GMII_PO_SPEED_1000M;
|
||||
break;
|
||||
@@ -1611,8 +1617,8 @@ static int b53_switch_init(struct b53_device *dev)
|
||||
return b53_switch_reset(dev);
|
||||
}
|
||||
|
||||
struct b53_device *b53_swconfig_switch_alloc(struct device *base, struct b53_io_ops *ops,
|
||||
void *priv)
|
||||
struct b53_device *b53_switch_alloc(struct device *base, struct b53_io_ops *ops,
|
||||
void *priv)
|
||||
{
|
||||
struct b53_device *dev;
|
||||
|
||||
@@ -1627,9 +1633,9 @@ struct b53_device *b53_swconfig_switch_alloc(struct device *base, struct b53_io_
|
||||
|
||||
return dev;
|
||||
}
|
||||
EXPORT_SYMBOL(b53_swconfig_switch_alloc);
|
||||
EXPORT_SYMBOL(b53_switch_alloc);
|
||||
|
||||
int b53_swconfig_switch_detect(struct b53_device *dev)
|
||||
int b53_switch_detect(struct b53_device *dev)
|
||||
{
|
||||
u32 id32;
|
||||
u16 tmp;
|
||||
@@ -1694,9 +1700,9 @@ int b53_swconfig_switch_detect(struct b53_device *dev)
|
||||
return b53_read8(dev, B53_MGMT_PAGE, B53_REV_ID,
|
||||
&dev->core_rev);
|
||||
}
|
||||
EXPORT_SYMBOL(b53_swconfig_switch_detect);
|
||||
EXPORT_SYMBOL(b53_switch_detect);
|
||||
|
||||
int b53_swconfig_switch_register(struct b53_device *dev)
|
||||
int b53_switch_register(struct b53_device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@@ -1706,7 +1712,7 @@ int b53_swconfig_switch_register(struct b53_device *dev)
|
||||
dev->sw_dev.alias = dev->pdata->alias;
|
||||
}
|
||||
|
||||
if (!dev->chip_id && b53_swconfig_switch_detect(dev))
|
||||
if (!dev->chip_id && b53_switch_detect(dev))
|
||||
return -EINVAL;
|
||||
|
||||
ret = b53_switch_init(dev);
|
||||
@@ -1717,7 +1723,7 @@ int b53_swconfig_switch_register(struct b53_device *dev)
|
||||
|
||||
return register_switch(&dev->sw_dev, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(b53_swconfig_switch_register);
|
||||
EXPORT_SYMBOL(b53_switch_register);
|
||||
|
||||
MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");
|
||||
MODULE_DESCRIPTION("B53 switch library");
|
||||
|
||||
@@ -280,7 +280,7 @@ static int b53_phy_probe(struct phy_device *phydev)
|
||||
if (phydev->mdio.addr != B53_PSEUDO_PHY && phydev->mdio.addr != 0)
|
||||
return -ENODEV;
|
||||
|
||||
dev = b53_swconfig_switch_alloc(&phydev->mdio.dev, &b53_mdio_ops, phydev->mdio.bus);
|
||||
dev = b53_switch_alloc(&phydev->mdio.dev, &b53_mdio_ops, phydev->mdio.bus);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -290,7 +290,7 @@ static int b53_phy_probe(struct phy_device *phydev)
|
||||
dev->pdata = NULL;
|
||||
mutex_init(&dev->reg_mutex);
|
||||
|
||||
ret = b53_swconfig_switch_detect(dev);
|
||||
ret = b53_switch_detect(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -302,7 +302,7 @@ static int b53_phy_probe(struct phy_device *phydev)
|
||||
|
||||
linkmode_copy(phydev->advertising, phydev->supported);
|
||||
|
||||
ret = b53_swconfig_switch_register(dev);
|
||||
ret = b53_switch_register(dev);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "failed to register switch: %i\n", ret);
|
||||
return ret;
|
||||
@@ -361,6 +361,26 @@ static int b53_phy_read_status(struct phy_device *phydev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id b53_of_match_1[] = {
|
||||
{ .compatible = "brcm,bcm5325" },
|
||||
{ .compatible = "brcm,bcm5395" },
|
||||
{ .compatible = "brcm,bcm5397" },
|
||||
{ .compatible = "brcm,bcm5398" },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static const struct of_device_id b53_of_match_2[] = {
|
||||
{ .compatible = "brcm,bcm53115" },
|
||||
{ .compatible = "brcm,bcm53125" },
|
||||
{ .compatible = "brcm,bcm53128" },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static const struct of_device_id b53_of_match_3[] = {
|
||||
{ .compatible = "brcm,bcm5365" },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
/* BCM5325, BCM539x */
|
||||
static struct phy_driver b53_phy_driver_id1 = {
|
||||
.phy_id = 0x0143bc00,
|
||||
@@ -372,6 +392,10 @@ static struct phy_driver b53_phy_driver_id1 = {
|
||||
.config_aneg = b53_phy_config_aneg,
|
||||
.config_init = b53_phy_config_init,
|
||||
.read_status = b53_phy_read_status,
|
||||
.mdiodrv.driver = {
|
||||
.name = "bcm539x",
|
||||
.of_match_table = b53_of_match_1,
|
||||
},
|
||||
};
|
||||
|
||||
/* BCM53125, BCM53128 */
|
||||
@@ -385,6 +409,10 @@ static struct phy_driver b53_phy_driver_id2 = {
|
||||
.config_aneg = b53_phy_config_aneg,
|
||||
.config_init = b53_phy_config_init,
|
||||
.read_status = b53_phy_read_status,
|
||||
.mdiodrv.driver = {
|
||||
.name = "bcm531xx",
|
||||
.of_match_table = b53_of_match_2,
|
||||
},
|
||||
};
|
||||
|
||||
/* BCM5365 */
|
||||
@@ -398,6 +426,10 @@ static struct phy_driver b53_phy_driver_id3 = {
|
||||
.config_aneg = b53_phy_config_aneg,
|
||||
.config_init = b53_phy_config_init,
|
||||
.read_status = b53_phy_read_status,
|
||||
.mdiodrv.driver = {
|
||||
.name = "bcm5365",
|
||||
.of_match_table = b53_of_match_3,
|
||||
},
|
||||
};
|
||||
|
||||
int __init b53_phy_driver_register(void)
|
||||
|
||||
@@ -205,7 +205,7 @@ static int b53_mmap_probe(struct platform_device *pdev)
|
||||
if (!pdata)
|
||||
return -EINVAL;
|
||||
|
||||
dev = b53_swconfig_switch_alloc(&pdev->dev, &b53_mmap_ops, pdata->regs);
|
||||
dev = b53_switch_alloc(&pdev->dev, &b53_mmap_ops, pdata->regs);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -214,7 +214,7 @@ static int b53_mmap_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, dev);
|
||||
|
||||
return b53_swconfig_switch_register(dev);
|
||||
return b53_switch_register(dev);
|
||||
}
|
||||
|
||||
static int b53_mmap_remove(struct platform_device *pdev)
|
||||
|
||||
@@ -183,12 +183,12 @@ static inline struct b53_device *sw_to_b53(struct switch_dev *sw)
|
||||
return container_of(sw, struct b53_device, sw_dev);
|
||||
}
|
||||
|
||||
struct b53_device *b53_swconfig_switch_alloc(struct device *base, struct b53_io_ops *ops,
|
||||
void *priv);
|
||||
struct b53_device *b53_switch_alloc(struct device *base, struct b53_io_ops *ops,
|
||||
void *priv);
|
||||
|
||||
int b53_swconfig_switch_detect(struct b53_device *dev);
|
||||
int b53_switch_detect(struct b53_device *dev);
|
||||
|
||||
int b53_swconfig_switch_register(struct b53_device *dev);
|
||||
int b53_switch_register(struct b53_device *dev);
|
||||
|
||||
static inline void b53_switch_remove(struct b53_device *dev)
|
||||
{
|
||||
|
||||
@@ -288,14 +288,14 @@ static int b53_spi_probe(struct spi_device *spi)
|
||||
struct b53_device *dev;
|
||||
int ret;
|
||||
|
||||
dev = b53_swconfig_switch_alloc(&spi->dev, &b53_spi_ops, spi);
|
||||
dev = b53_switch_alloc(&spi->dev, &b53_spi_ops, spi);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
if (spi->dev.platform_data)
|
||||
dev->pdata = spi->dev.platform_data;
|
||||
|
||||
ret = b53_swconfig_switch_register(dev);
|
||||
ret = b53_switch_register(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
||||
@@ -342,7 +342,7 @@ static int b53_srab_probe(struct platform_device *pdev)
|
||||
if (!pdata)
|
||||
return -EINVAL;
|
||||
|
||||
dev = b53_swconfig_switch_alloc(&pdev->dev, &b53_srab_ops, pdata->regs);
|
||||
dev = b53_switch_alloc(&pdev->dev, &b53_srab_ops, pdata->regs);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -351,7 +351,7 @@ static int b53_srab_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, dev);
|
||||
|
||||
return b53_swconfig_switch_register(dev);
|
||||
return b53_switch_register(dev);
|
||||
}
|
||||
|
||||
static int b53_srab_remove(struct platform_device *pdev)
|
||||
|
||||
446
feeds/mediatek-sdk/mediatek/files/drivers/net/phy/mvswitch.c
Normal file
446
feeds/mediatek-sdk/mediatek/files/drivers/net/phy/mvswitch.c
Normal file
@@ -0,0 +1,446 @@
|
||||
/*
|
||||
* Marvell 88E6060 switch driver
|
||||
* Copyright (c) 2008 Felix Fietkau <nbd@nbd.name>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License v2 as published by the
|
||||
* Free Software Foundation
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include "mvswitch.h"
|
||||
|
||||
/* Undefine this to use trailer mode instead.
|
||||
* I don't know if header mode works with all chips */
|
||||
#define HEADER_MODE 1
|
||||
|
||||
MODULE_DESCRIPTION("Marvell 88E6060 Switch driver");
|
||||
MODULE_AUTHOR("Felix Fietkau");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define MVSWITCH_MAGIC 0x88E6060
|
||||
|
||||
struct mvswitch_priv {
|
||||
netdev_features_t orig_features;
|
||||
u8 vlans[16];
|
||||
};
|
||||
|
||||
#define to_mvsw(_phy) ((struct mvswitch_priv *) (_phy)->priv)
|
||||
|
||||
static inline u16
|
||||
r16(struct phy_device *phydev, int addr, int reg)
|
||||
{
|
||||
struct mii_bus *bus = phydev->mdio.bus;
|
||||
|
||||
return bus->read(bus, addr, reg);
|
||||
}
|
||||
|
||||
static inline void
|
||||
w16(struct phy_device *phydev, int addr, int reg, u16 val)
|
||||
{
|
||||
struct mii_bus *bus = phydev->mdio.bus;
|
||||
|
||||
bus->write(bus, addr, reg, val);
|
||||
}
|
||||
|
||||
|
||||
static struct sk_buff *
|
||||
mvswitch_mangle_tx(struct net_device *dev, struct sk_buff *skb)
|
||||
{
|
||||
struct mvswitch_priv *priv;
|
||||
char *buf = NULL;
|
||||
u16 vid;
|
||||
|
||||
priv = dev->phy_ptr;
|
||||
if (unlikely(!priv))
|
||||
goto error;
|
||||
|
||||
if (unlikely(skb->len < 16))
|
||||
goto error;
|
||||
|
||||
#ifdef HEADER_MODE
|
||||
if (__vlan_hwaccel_get_tag(skb, &vid))
|
||||
goto error;
|
||||
|
||||
if (skb_cloned(skb) || (skb->len <= 62) || (skb_headroom(skb) < MV_HEADER_SIZE)) {
|
||||
if (pskb_expand_head(skb, MV_HEADER_SIZE, (skb->len < 62 ? 62 - skb->len : 0), GFP_ATOMIC))
|
||||
goto error_expand;
|
||||
if (skb->len < 62)
|
||||
skb->len = 62;
|
||||
}
|
||||
buf = skb_push(skb, MV_HEADER_SIZE);
|
||||
#else
|
||||
if (__vlan_get_tag(skb, &vid))
|
||||
goto error;
|
||||
|
||||
if (unlikely((vid > 15 || !priv->vlans[vid])))
|
||||
goto error;
|
||||
|
||||
if (skb->len <= 64) {
|
||||
if (pskb_expand_head(skb, 0, 64 + MV_TRAILER_SIZE - skb->len, GFP_ATOMIC))
|
||||
goto error_expand;
|
||||
|
||||
buf = skb->data + 64;
|
||||
skb->len = 64 + MV_TRAILER_SIZE;
|
||||
} else {
|
||||
if (skb_cloned(skb) || unlikely(skb_tailroom(skb) < 4)) {
|
||||
if (pskb_expand_head(skb, 0, 4, GFP_ATOMIC))
|
||||
goto error_expand;
|
||||
}
|
||||
buf = skb_put(skb, 4);
|
||||
}
|
||||
|
||||
/* move the ethernet header 4 bytes forward, overwriting the vlan tag */
|
||||
memmove(skb->data + 4, skb->data, 12);
|
||||
skb->data += 4;
|
||||
skb->len -= 4;
|
||||
skb->mac_header += 4;
|
||||
#endif
|
||||
|
||||
if (!buf)
|
||||
goto error;
|
||||
|
||||
|
||||
#ifdef HEADER_MODE
|
||||
/* prepend the tag */
|
||||
*((__be16 *) buf) = cpu_to_be16(
|
||||
((vid << MV_HEADER_VLAN_S) & MV_HEADER_VLAN_M) |
|
||||
((priv->vlans[vid] << MV_HEADER_PORTS_S) & MV_HEADER_PORTS_M)
|
||||
);
|
||||
#else
|
||||
/* append the tag */
|
||||
*((__be32 *) buf) = cpu_to_be32((
|
||||
(MV_TRAILER_OVERRIDE << MV_TRAILER_FLAGS_S) |
|
||||
((priv->vlans[vid] & MV_TRAILER_PORTS_M) << MV_TRAILER_PORTS_S)
|
||||
));
|
||||
#endif
|
||||
|
||||
return skb;
|
||||
|
||||
error_expand:
|
||||
if (net_ratelimit())
|
||||
printk("%s: failed to expand/update skb for the switch\n", dev->name);
|
||||
|
||||
error:
|
||||
/* any errors? drop the packet! */
|
||||
dev_kfree_skb_any(skb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
mvswitch_mangle_rx(struct net_device *dev, struct sk_buff *skb)
|
||||
{
|
||||
struct mvswitch_priv *priv;
|
||||
unsigned char *buf;
|
||||
int vlan = -1;
|
||||
int i;
|
||||
|
||||
priv = dev->phy_ptr;
|
||||
if (WARN_ON_ONCE(!priv))
|
||||
return;
|
||||
|
||||
#ifdef HEADER_MODE
|
||||
buf = skb->data;
|
||||
skb_pull(skb, MV_HEADER_SIZE);
|
||||
#else
|
||||
buf = skb->data + skb->len - MV_TRAILER_SIZE;
|
||||
if (buf[0] != 0x80)
|
||||
return;
|
||||
#endif
|
||||
|
||||
/* look for the vlan matching the incoming port */
|
||||
for (i = 0; i < ARRAY_SIZE(priv->vlans); i++) {
|
||||
if ((1 << buf[1]) & priv->vlans[i])
|
||||
vlan = i;
|
||||
}
|
||||
|
||||
if (vlan == -1)
|
||||
return;
|
||||
|
||||
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
mvswitch_wait_mask(struct phy_device *pdev, int addr, int reg, u16 mask, u16 val)
|
||||
{
|
||||
int i = 100;
|
||||
u16 r;
|
||||
|
||||
do {
|
||||
r = r16(pdev, addr, reg) & mask;
|
||||
if (r == val)
|
||||
return 0;
|
||||
} while(--i > 0);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int
|
||||
mvswitch_config_init(struct phy_device *pdev)
|
||||
{
|
||||
struct mvswitch_priv *priv = to_mvsw(pdev);
|
||||
struct net_device *dev = pdev->attached_dev;
|
||||
u8 vlmap = 0;
|
||||
int i;
|
||||
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
|
||||
printk("%s: Marvell 88E6060 PHY driver attached.\n", dev->name);
|
||||
linkmode_zero(pdev->supported);
|
||||
linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, pdev->supported);
|
||||
linkmode_copy(pdev->advertising, pdev->supported);
|
||||
dev->phy_ptr = priv;
|
||||
pdev->irq = PHY_POLL;
|
||||
#ifdef HEADER_MODE
|
||||
dev->flags |= IFF_PROMISC;
|
||||
#endif
|
||||
|
||||
/* initialize default vlans */
|
||||
for (i = 0; i < MV_PORTS; i++)
|
||||
priv->vlans[(i == MV_WANPORT ? 2 : 1)] |= (1 << i);
|
||||
|
||||
/* before entering reset, disable all ports */
|
||||
for (i = 0; i < MV_PORTS; i++)
|
||||
w16(pdev, MV_PORTREG(CONTROL, i), 0x00);
|
||||
|
||||
msleep(2); /* wait for the status change to settle in */
|
||||
|
||||
/* put the ATU in reset */
|
||||
w16(pdev, MV_SWITCHREG(ATU_CTRL), MV_ATUCTL_RESET);
|
||||
|
||||
i = mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_CTRL), MV_ATUCTL_RESET, 0);
|
||||
if (i < 0) {
|
||||
printk("%s: Timeout waiting for the switch to reset.\n", dev->name);
|
||||
return i;
|
||||
}
|
||||
|
||||
/* set the ATU flags */
|
||||
w16(pdev, MV_SWITCHREG(ATU_CTRL),
|
||||
MV_ATUCTL_NO_LEARN |
|
||||
MV_ATUCTL_ATU_1K |
|
||||
MV_ATUCTL_AGETIME(MV_ATUCTL_AGETIME_MIN) /* minimum without disabling ageing */
|
||||
);
|
||||
|
||||
/* initialize the cpu port */
|
||||
w16(pdev, MV_PORTREG(CONTROL, MV_CPUPORT),
|
||||
#ifdef HEADER_MODE
|
||||
MV_PORTCTRL_HEADER |
|
||||
#else
|
||||
MV_PORTCTRL_RXTR |
|
||||
MV_PORTCTRL_TXTR |
|
||||
#endif
|
||||
MV_PORTCTRL_ENABLED
|
||||
);
|
||||
/* wait for the phy change to settle in */
|
||||
msleep(2);
|
||||
for (i = 0; i < MV_PORTS; i++) {
|
||||
u8 pvid = 0;
|
||||
int j;
|
||||
|
||||
vlmap = 0;
|
||||
|
||||
/* look for the matching vlan */
|
||||
for (j = 0; j < ARRAY_SIZE(priv->vlans); j++) {
|
||||
if (priv->vlans[j] & (1 << i)) {
|
||||
vlmap = priv->vlans[j];
|
||||
pvid = j;
|
||||
}
|
||||
}
|
||||
/* leave port unconfigured if it's not part of a vlan */
|
||||
if (!vlmap)
|
||||
continue;
|
||||
|
||||
/* add the cpu port to the allowed destinations list */
|
||||
vlmap |= (1 << MV_CPUPORT);
|
||||
|
||||
/* take port out of its own vlan destination map */
|
||||
vlmap &= ~(1 << i);
|
||||
|
||||
/* apply vlan settings */
|
||||
w16(pdev, MV_PORTREG(VLANMAP, i),
|
||||
MV_PORTVLAN_PORTS(vlmap) |
|
||||
MV_PORTVLAN_ID(i)
|
||||
);
|
||||
|
||||
/* re-enable port */
|
||||
w16(pdev, MV_PORTREG(CONTROL, i),
|
||||
MV_PORTCTRL_ENABLED
|
||||
);
|
||||
}
|
||||
|
||||
w16(pdev, MV_PORTREG(VLANMAP, MV_CPUPORT),
|
||||
MV_PORTVLAN_ID(MV_CPUPORT)
|
||||
);
|
||||
|
||||
/* set the port association vector */
|
||||
for (i = 0; i <= MV_PORTS; i++) {
|
||||
w16(pdev, MV_PORTREG(ASSOC, i),
|
||||
MV_PORTASSOC_PORTS(1 << i)
|
||||
);
|
||||
}
|
||||
|
||||
/* init switch control */
|
||||
w16(pdev, MV_SWITCHREG(CTRL),
|
||||
MV_SWITCHCTL_MSIZE |
|
||||
MV_SWITCHCTL_DROP
|
||||
);
|
||||
|
||||
dev->eth_mangle_rx = mvswitch_mangle_rx;
|
||||
dev->eth_mangle_tx = mvswitch_mangle_tx;
|
||||
priv->orig_features = dev->features;
|
||||
|
||||
#ifdef HEADER_MODE
|
||||
dev->priv_flags |= IFF_NO_IP_ALIGN;
|
||||
dev->features |= NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX;
|
||||
#else
|
||||
dev->features |= NETIF_F_HW_VLAN_CTAG_RX;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mvswitch_read_status(struct phy_device *pdev)
|
||||
{
|
||||
pdev->speed = SPEED_100;
|
||||
pdev->duplex = DUPLEX_FULL;
|
||||
pdev->link = 1;
|
||||
|
||||
/* XXX ugly workaround: we can't force the switch
|
||||
* to gracefully handle hosts moving from one port to another,
|
||||
* so we have to regularly clear the ATU database */
|
||||
|
||||
/* wait for the ATU to become available */
|
||||
mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_OP), MV_ATUOP_INPROGRESS, 0);
|
||||
|
||||
/* flush the ATU */
|
||||
w16(pdev, MV_SWITCHREG(ATU_OP),
|
||||
MV_ATUOP_INPROGRESS |
|
||||
MV_ATUOP_FLUSH_ALL
|
||||
);
|
||||
|
||||
/* wait for operation to complete */
|
||||
mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_OP), MV_ATUOP_INPROGRESS, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mvswitch_aneg_done(struct phy_device *phydev)
|
||||
{
|
||||
return 1; /* Return any positive value */
|
||||
}
|
||||
|
||||
static int
|
||||
mvswitch_config_aneg(struct phy_device *phydev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
mvswitch_detach(struct phy_device *pdev)
|
||||
{
|
||||
struct mvswitch_priv *priv = to_mvsw(pdev);
|
||||
struct net_device *dev = pdev->attached_dev;
|
||||
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
dev->phy_ptr = NULL;
|
||||
dev->eth_mangle_rx = NULL;
|
||||
dev->eth_mangle_tx = NULL;
|
||||
dev->features = priv->orig_features;
|
||||
dev->priv_flags &= ~IFF_NO_IP_ALIGN;
|
||||
}
|
||||
|
||||
static void
|
||||
mvswitch_remove(struct phy_device *pdev)
|
||||
{
|
||||
struct mvswitch_priv *priv = to_mvsw(pdev);
|
||||
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
static int
|
||||
mvswitch_probe(struct phy_device *pdev)
|
||||
{
|
||||
struct mvswitch_priv *priv;
|
||||
|
||||
priv = kzalloc(sizeof(struct mvswitch_priv), GFP_KERNEL);
|
||||
if (priv == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
pdev->priv = priv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mvswitch_fixup(struct phy_device *dev)
|
||||
{
|
||||
struct mii_bus *bus = dev->mdio.bus;
|
||||
u16 reg;
|
||||
|
||||
if (dev->mdio.addr != 0x10)
|
||||
return 0;
|
||||
|
||||
reg = bus->read(bus, MV_PORTREG(IDENT, 0)) & MV_IDENT_MASK;
|
||||
if (reg != MV_IDENT_VALUE)
|
||||
return 0;
|
||||
|
||||
dev->phy_id = MVSWITCH_MAGIC;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct phy_driver mvswitch_driver = {
|
||||
.name = "Marvell 88E6060",
|
||||
.phy_id = MVSWITCH_MAGIC,
|
||||
.phy_id_mask = 0xffffffff,
|
||||
.features = PHY_BASIC_FEATURES,
|
||||
.probe = &mvswitch_probe,
|
||||
.remove = &mvswitch_remove,
|
||||
.detach = &mvswitch_detach,
|
||||
.config_init = &mvswitch_config_init,
|
||||
.config_aneg = &mvswitch_config_aneg,
|
||||
.aneg_done = &mvswitch_aneg_done,
|
||||
.read_status = &mvswitch_read_status,
|
||||
};
|
||||
|
||||
static int __init
|
||||
mvswitch_init(void)
|
||||
{
|
||||
phy_register_fixup_for_id(PHY_ANY_ID, mvswitch_fixup);
|
||||
return phy_driver_register(&mvswitch_driver, THIS_MODULE);
|
||||
}
|
||||
|
||||
static void __exit
|
||||
mvswitch_exit(void)
|
||||
{
|
||||
phy_driver_unregister(&mvswitch_driver);
|
||||
}
|
||||
|
||||
module_init(mvswitch_init);
|
||||
module_exit(mvswitch_exit);
|
||||
145
feeds/mediatek-sdk/mediatek/files/drivers/net/phy/mvswitch.h
Normal file
145
feeds/mediatek-sdk/mediatek/files/drivers/net/phy/mvswitch.h
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Marvell 88E6060 switch driver
|
||||
* Copyright (c) 2008 Felix Fietkau <nbd@nbd.name>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License v2 as published by the
|
||||
* Free Software Foundation
|
||||
*/
|
||||
#ifndef __MVSWITCH_H
|
||||
#define __MVSWITCH_H
|
||||
|
||||
#define MV_HEADER_SIZE 2
|
||||
#define MV_HEADER_PORTS_M 0x001f
|
||||
#define MV_HEADER_PORTS_S 0
|
||||
#define MV_HEADER_VLAN_M 0xf000
|
||||
#define MV_HEADER_VLAN_S 12
|
||||
|
||||
#define MV_TRAILER_SIZE 4
|
||||
#define MV_TRAILER_PORTS_M 0x1f
|
||||
#define MV_TRAILER_PORTS_S 16
|
||||
#define MV_TRAILER_FLAGS_S 24
|
||||
#define MV_TRAILER_OVERRIDE 0x80
|
||||
|
||||
|
||||
#define MV_PORTS 5
|
||||
#define MV_WANPORT 4
|
||||
#define MV_CPUPORT 5
|
||||
|
||||
#define MV_BASE 0x10
|
||||
|
||||
#define MV_PHYPORT_BASE (MV_BASE + 0x0)
|
||||
#define MV_PHYPORT(_n) (MV_PHYPORT_BASE + (_n))
|
||||
#define MV_SWITCHPORT_BASE (MV_BASE + 0x8)
|
||||
#define MV_SWITCHPORT(_n) (MV_SWITCHPORT_BASE + (_n))
|
||||
#define MV_SWITCHREGS (MV_BASE + 0xf)
|
||||
|
||||
enum {
|
||||
MV_PHY_CONTROL = 0x00,
|
||||
MV_PHY_STATUS = 0x01,
|
||||
MV_PHY_IDENT0 = 0x02,
|
||||
MV_PHY_IDENT1 = 0x03,
|
||||
MV_PHY_ANEG = 0x04,
|
||||
MV_PHY_LINK_ABILITY = 0x05,
|
||||
MV_PHY_ANEG_EXPAND = 0x06,
|
||||
MV_PHY_XMIT_NEXTP = 0x07,
|
||||
MV_PHY_LINK_NEXTP = 0x08,
|
||||
MV_PHY_CONTROL1 = 0x10,
|
||||
MV_PHY_STATUS1 = 0x11,
|
||||
MV_PHY_INTR_EN = 0x12,
|
||||
MV_PHY_INTR_STATUS = 0x13,
|
||||
MV_PHY_INTR_PORT = 0x14,
|
||||
MV_PHY_RECV_COUNTER = 0x16,
|
||||
MV_PHY_LED_PARALLEL = 0x16,
|
||||
MV_PHY_LED_STREAM = 0x17,
|
||||
MV_PHY_LED_CTRL = 0x18,
|
||||
MV_PHY_LED_OVERRIDE = 0x19,
|
||||
MV_PHY_VCT_CTRL = 0x1a,
|
||||
MV_PHY_VCT_STATUS = 0x1b,
|
||||
MV_PHY_CONTROL2 = 0x1e
|
||||
};
|
||||
#define MV_PHYREG(_type, _port) MV_PHYPORT(_port), MV_PHY_##_type
|
||||
|
||||
enum {
|
||||
MV_PORT_STATUS = 0x00,
|
||||
MV_PORT_IDENT = 0x03,
|
||||
MV_PORT_CONTROL = 0x04,
|
||||
MV_PORT_VLANMAP = 0x06,
|
||||
MV_PORT_ASSOC = 0x0b,
|
||||
MV_PORT_RXCOUNT = 0x10,
|
||||
MV_PORT_TXCOUNT = 0x11,
|
||||
};
|
||||
#define MV_PORTREG(_type, _port) MV_SWITCHPORT(_port), MV_PORT_##_type
|
||||
|
||||
enum {
|
||||
MV_PORTCTRL_BLOCK = (1 << 0),
|
||||
MV_PORTCTRL_LEARN = (2 << 0),
|
||||
MV_PORTCTRL_ENABLED = (3 << 0),
|
||||
MV_PORTCTRL_VLANTUN = (1 << 7), /* Enforce VLANs on packets */
|
||||
MV_PORTCTRL_RXTR = (1 << 8), /* Enable Marvell packet trailer for ingress */
|
||||
MV_PORTCTRL_HEADER = (1 << 11), /* Enable Marvell packet header mode for port */
|
||||
MV_PORTCTRL_TXTR = (1 << 14), /* Enable Marvell packet trailer for egress */
|
||||
MV_PORTCTRL_FORCEFL = (1 << 15), /* force flow control */
|
||||
};
|
||||
|
||||
#define MV_PORTVLAN_ID(_n) (((_n) & 0xf) << 12)
|
||||
#define MV_PORTVLAN_PORTS(_n) ((_n) & 0x3f)
|
||||
|
||||
#define MV_PORTASSOC_PORTS(_n) ((_n) & 0x1f)
|
||||
#define MV_PORTASSOC_MONITOR (1 << 15)
|
||||
|
||||
enum {
|
||||
MV_SWITCH_MAC0 = 0x01,
|
||||
MV_SWITCH_MAC1 = 0x02,
|
||||
MV_SWITCH_MAC2 = 0x03,
|
||||
MV_SWITCH_CTRL = 0x04,
|
||||
MV_SWITCH_ATU_CTRL = 0x0a,
|
||||
MV_SWITCH_ATU_OP = 0x0b,
|
||||
MV_SWITCH_ATU_DATA = 0x0c,
|
||||
MV_SWITCH_ATU_MAC0 = 0x0d,
|
||||
MV_SWITCH_ATU_MAC1 = 0x0e,
|
||||
MV_SWITCH_ATU_MAC2 = 0x0f,
|
||||
};
|
||||
#define MV_SWITCHREG(_type) MV_SWITCHREGS, MV_SWITCH_##_type
|
||||
|
||||
enum {
|
||||
MV_SWITCHCTL_EEIE = (1 << 0), /* EEPROM interrupt enable */
|
||||
MV_SWITCHCTL_PHYIE = (1 << 1), /* PHY interrupt enable */
|
||||
MV_SWITCHCTL_ATUDONE= (1 << 2), /* ATU done interrupt enable */
|
||||
MV_SWITCHCTL_ATUIE = (1 << 3), /* ATU interrupt enable */
|
||||
MV_SWITCHCTL_CTRMODE= (1 << 8), /* statistics for rx and tx errors */
|
||||
MV_SWITCHCTL_RELOAD = (1 << 9), /* reload registers from eeprom */
|
||||
MV_SWITCHCTL_MSIZE = (1 << 10), /* increase maximum frame size */
|
||||
MV_SWITCHCTL_DROP = (1 << 13), /* discard frames with excessive collisions */
|
||||
};
|
||||
|
||||
enum {
|
||||
#define MV_ATUCTL_AGETIME_MIN 16
|
||||
#define MV_ATUCTL_AGETIME_MAX 4080
|
||||
#define MV_ATUCTL_AGETIME(_n) ((((_n) / 16) & 0xff) << 4)
|
||||
MV_ATUCTL_ATU_256 = (0 << 12),
|
||||
MV_ATUCTL_ATU_512 = (1 << 12),
|
||||
MV_ATUCTL_ATU_1K = (2 << 12),
|
||||
MV_ATUCTL_ATUMASK = (3 << 12),
|
||||
MV_ATUCTL_NO_LEARN = (1 << 14),
|
||||
MV_ATUCTL_RESET = (1 << 15),
|
||||
};
|
||||
|
||||
enum {
|
||||
#define MV_ATUOP_DBNUM(_n) ((_n) & 0x0f)
|
||||
|
||||
MV_ATUOP_NOOP = (0 << 12),
|
||||
MV_ATUOP_FLUSH_ALL = (1 << 12),
|
||||
MV_ATUOP_FLUSH_U = (2 << 12),
|
||||
MV_ATUOP_LOAD_DB = (3 << 12),
|
||||
MV_ATUOP_GET_NEXT = (4 << 12),
|
||||
MV_ATUOP_FLUSH_DB = (5 << 12),
|
||||
MV_ATUOP_FLUSH_DB_UU= (6 << 12),
|
||||
|
||||
MV_ATUOP_INPROGRESS = (1 << 15),
|
||||
};
|
||||
|
||||
#define MV_IDENT_MASK 0xfff0
|
||||
#define MV_IDENT_VALUE 0x0600
|
||||
|
||||
#endif
|
||||
@@ -60,13 +60,11 @@ struct psb6970_priv {
|
||||
struct mutex reg_mutex;
|
||||
|
||||
/* all fields below are cleared on reset */
|
||||
struct_group(psb6970_priv_volatile,
|
||||
bool vlan;
|
||||
u16 vlan_id[PSB6970_MAX_VLANS];
|
||||
u8 vlan_table[PSB6970_MAX_VLANS];
|
||||
u8 vlan_tagged;
|
||||
u16 pvid[PSB6970_NUM_PORTS];
|
||||
);
|
||||
bool vlan;
|
||||
u16 vlan_id[PSB6970_MAX_VLANS];
|
||||
u8 vlan_table[PSB6970_MAX_VLANS];
|
||||
u8 vlan_tagged;
|
||||
u16 pvid[PSB6970_NUM_PORTS];
|
||||
};
|
||||
|
||||
#define to_psb6970(_dev) container_of(_dev, struct psb6970_priv, dev)
|
||||
@@ -274,8 +272,8 @@ static int psb6970_reset_switch(struct switch_dev *dev)
|
||||
|
||||
mutex_lock(&priv->reg_mutex);
|
||||
|
||||
memset(&priv->psb6970_priv_volatile, 0,
|
||||
sizeof(priv->psb6970_priv_volatile));
|
||||
memset(&priv->vlan, 0, sizeof(struct psb6970_priv) -
|
||||
offsetof(struct psb6970_priv, vlan));
|
||||
|
||||
for (i = 0; i < PSB6970_MAX_VLANS; i++)
|
||||
priv->vlan_id[i] = i;
|
||||
@@ -309,6 +307,7 @@ static const struct switch_dev_ops psb6970_ops = {
|
||||
static int psb6970_config_init(struct phy_device *pdev)
|
||||
{
|
||||
struct psb6970_priv *priv;
|
||||
struct net_device *dev = pdev->attached_dev;
|
||||
struct switch_dev *swdev;
|
||||
int ret;
|
||||
|
||||
@@ -356,6 +355,8 @@ static int psb6970_config_init(struct phy_device *pdev)
|
||||
goto done;
|
||||
}
|
||||
|
||||
dev->phy_ptr = priv;
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
menuconfig MIKROTIK
|
||||
bool "Platform support for MikroTik RouterBoard virtual devices"
|
||||
default n
|
||||
help
|
||||
Say Y here to get to see options for the MikroTik RouterBoard platform.
|
||||
This option alone does not add any kernel code.
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
|
||||
#include "routerboot.h"
|
||||
|
||||
#define RB_HARDCONFIG_VER "0.07"
|
||||
#define RB_HARDCONFIG_VER "0.06"
|
||||
#define RB_HC_PR_PFX "[rb_hardconfig] "
|
||||
|
||||
/* ID values for hardware settings */
|
||||
@@ -676,9 +676,10 @@ static ssize_t hc_wlan_data_bin_read(struct file *filp, struct kobject *kobj,
|
||||
return count;
|
||||
}
|
||||
|
||||
int rb_hardconfig_init(struct kobject *rb_kobj, struct mtd_info *mtd)
|
||||
int __init rb_hardconfig_init(struct kobject *rb_kobj)
|
||||
{
|
||||
struct kobject *hc_wlan_kobj;
|
||||
struct mtd_info *mtd;
|
||||
size_t bytes_read, buflen, outlen;
|
||||
const u8 *buf;
|
||||
void *outbuf;
|
||||
@@ -689,19 +690,20 @@ int rb_hardconfig_init(struct kobject *rb_kobj, struct mtd_info *mtd)
|
||||
hc_kobj = NULL;
|
||||
hc_wlan_kobj = NULL;
|
||||
|
||||
ret = __get_mtd_device(mtd);
|
||||
if (ret)
|
||||
// TODO allow override
|
||||
mtd = get_mtd_device_nm(RB_MTD_HARD_CONFIG);
|
||||
if (IS_ERR(mtd))
|
||||
return -ENODEV;
|
||||
|
||||
hc_buflen = mtd->size;
|
||||
hc_buf = kmalloc(hc_buflen, GFP_KERNEL);
|
||||
if (!hc_buf) {
|
||||
__put_mtd_device(mtd);
|
||||
put_mtd_device(mtd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = mtd_read(mtd, 0, hc_buflen, &bytes_read, hc_buf);
|
||||
__put_mtd_device(mtd);
|
||||
put_mtd_device(mtd);
|
||||
|
||||
if (ret)
|
||||
goto fail;
|
||||
@@ -816,10 +818,8 @@ fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void rb_hardconfig_exit(void)
|
||||
void __exit rb_hardconfig_exit(void)
|
||||
{
|
||||
kobject_put(hc_kobj);
|
||||
hc_kobj = NULL;
|
||||
kfree(hc_buf);
|
||||
hc_buf = NULL;
|
||||
}
|
||||
|
||||
@@ -56,12 +56,23 @@
|
||||
|
||||
#include "routerboot.h"
|
||||
|
||||
#define RB_SOFTCONFIG_VER "0.05"
|
||||
#define RB_SOFTCONFIG_VER "0.03"
|
||||
#define RB_SC_PR_PFX "[rb_softconfig] "
|
||||
|
||||
#define RB_SC_HAS_WRITE_SUPPORT true
|
||||
#define RB_SC_WMODE S_IWUSR
|
||||
#define RB_SC_RMODE S_IRUSR
|
||||
/*
|
||||
* mtd operations before 4.17 are asynchronous, not handled by this code
|
||||
* Also make the driver act read-only if 4K_SECTORS are not enabled, since they
|
||||
* are require to handle partial erasing of the small soft_config partition.
|
||||
*/
|
||||
#if defined(CONFIG_MTD_SPI_NOR_USE_4K_SECTORS)
|
||||
#define RB_SC_HAS_WRITE_SUPPORT true
|
||||
#define RB_SC_WMODE S_IWUSR
|
||||
#define RB_SC_RMODE S_IRUSR
|
||||
#else
|
||||
#define RB_SC_HAS_WRITE_SUPPORT false
|
||||
#define RB_SC_WMODE 0
|
||||
#define RB_SC_RMODE S_IRUSR
|
||||
#endif
|
||||
|
||||
/* ID values for software settings */
|
||||
#define RB_SCID_UART_SPEED 0x01 // u32*1
|
||||
@@ -694,8 +705,9 @@ mtdfail:
|
||||
|
||||
static struct kobj_attribute sc_kattrcommit = __ATTR(commit, RB_SC_RMODE|RB_SC_WMODE, sc_commit_show, sc_commit_store);
|
||||
|
||||
int rb_softconfig_init(struct kobject *rb_kobj, struct mtd_info *mtd)
|
||||
int __init rb_softconfig_init(struct kobject *rb_kobj)
|
||||
{
|
||||
struct mtd_info *mtd;
|
||||
size_t bytes_read, buflen;
|
||||
const u8 *buf;
|
||||
int i, ret;
|
||||
@@ -704,19 +716,20 @@ int rb_softconfig_init(struct kobject *rb_kobj, struct mtd_info *mtd)
|
||||
sc_buf = NULL;
|
||||
sc_kobj = NULL;
|
||||
|
||||
ret = __get_mtd_device(mtd);
|
||||
if (ret)
|
||||
// TODO allow override
|
||||
mtd = get_mtd_device_nm(RB_MTD_SOFT_CONFIG);
|
||||
if (IS_ERR(mtd))
|
||||
return -ENODEV;
|
||||
|
||||
sc_buflen = mtd->size;
|
||||
sc_buf = kmalloc(sc_buflen, GFP_KERNEL);
|
||||
if (!sc_buf) {
|
||||
__put_mtd_device(mtd);
|
||||
put_mtd_device(mtd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = mtd_read(mtd, 0, sc_buflen, &bytes_read, sc_buf);
|
||||
__put_mtd_device(mtd);
|
||||
put_mtd_device(mtd);
|
||||
|
||||
if (ret)
|
||||
goto fail;
|
||||
@@ -786,10 +799,8 @@ fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void rb_softconfig_exit(void)
|
||||
void __exit rb_softconfig_exit(void)
|
||||
{
|
||||
kobject_put(sc_kobj);
|
||||
sc_kobj = NULL;
|
||||
kfree(sc_buf);
|
||||
sc_buf = NULL;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
|
||||
#include "routerboot.h"
|
||||
|
||||
@@ -161,57 +160,25 @@ fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void routerboot_mtd_notifier_add(struct mtd_info *mtd)
|
||||
{
|
||||
/* Currently routerboot is only known to live on NOR flash */
|
||||
if (mtd->type != MTD_NORFLASH)
|
||||
return;
|
||||
|
||||
/*
|
||||
* We ignore the following return values and always register.
|
||||
* These init() routines are designed so that their failed state is
|
||||
* always manageable by the corresponding exit() calls.
|
||||
* Notifier is called with MTD mutex held: use __get/__put variants.
|
||||
* TODO: allow partition names override
|
||||
*/
|
||||
if (!strcmp(mtd->name, RB_MTD_HARD_CONFIG))
|
||||
rb_hardconfig_init(rb_kobj, mtd);
|
||||
else if (!strcmp(mtd->name, RB_MTD_SOFT_CONFIG))
|
||||
rb_softconfig_init(rb_kobj, mtd);
|
||||
}
|
||||
|
||||
static void routerboot_mtd_notifier_remove(struct mtd_info *mtd)
|
||||
{
|
||||
if (mtd->type != MTD_NORFLASH)
|
||||
return;
|
||||
|
||||
if (!strcmp(mtd->name, RB_MTD_HARD_CONFIG))
|
||||
rb_hardconfig_exit();
|
||||
else if (!strcmp(mtd->name, RB_MTD_SOFT_CONFIG))
|
||||
rb_softconfig_exit();
|
||||
}
|
||||
|
||||
/* Note: using a notifier prevents qualifying init()/exit() functions with __init/__exit */
|
||||
static struct mtd_notifier routerboot_mtd_notifier = {
|
||||
.add = routerboot_mtd_notifier_add,
|
||||
.remove = routerboot_mtd_notifier_remove,
|
||||
};
|
||||
|
||||
static int __init routerboot_init(void)
|
||||
{
|
||||
rb_kobj = kobject_create_and_add("mikrotik", firmware_kobj);
|
||||
if (!rb_kobj)
|
||||
return -ENOMEM;
|
||||
|
||||
register_mtd_user(&routerboot_mtd_notifier);
|
||||
/*
|
||||
* We ignore the following return values and always register.
|
||||
* These init() routines are designed so that their failed state is
|
||||
* always manageable by the corresponding exit() calls.
|
||||
*/
|
||||
rb_hardconfig_init(rb_kobj);
|
||||
rb_softconfig_init(rb_kobj);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit routerboot_exit(void)
|
||||
{
|
||||
unregister_mtd_user(&routerboot_mtd_notifier);
|
||||
/* Exit routines are idempotent */
|
||||
rb_softconfig_exit();
|
||||
rb_hardconfig_exit();
|
||||
kobject_put(rb_kobj); // recursive afaict
|
||||
|
||||
@@ -25,11 +25,11 @@
|
||||
int routerboot_tag_find(const u8 *bufhead, const size_t buflen, const u16 tag_id, u16 *pld_ofs, u16 *pld_len);
|
||||
int routerboot_rle_decode(const u8 *in, size_t inlen, u8 *out, size_t *outlen);
|
||||
|
||||
int rb_hardconfig_init(struct kobject *rb_kobj, struct mtd_info *mtd);
|
||||
void rb_hardconfig_exit(void);
|
||||
int __init rb_hardconfig_init(struct kobject *rb_kobj);
|
||||
void __exit rb_hardconfig_exit(void);
|
||||
|
||||
int rb_softconfig_init(struct kobject *rb_kobj, struct mtd_info *mtd);
|
||||
void rb_softconfig_exit(void);
|
||||
int __init rb_softconfig_init(struct kobject *rb_kobj);
|
||||
void __exit rb_softconfig_exit(void);
|
||||
|
||||
ssize_t routerboot_tag_show_string(const u8 *pld, u16 pld_len, char *buf);
|
||||
ssize_t routerboot_tag_show_u32s(const u8 *pld, u16 pld_len, char *buf);
|
||||
|
||||
@@ -1,744 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* SSB Fallback SPROM Driver
|
||||
*
|
||||
* Copyright (C) 2020 Álvaro Fernández Rojas <noltari@gmail.com>
|
||||
* Copyright (C) 2014 Jonas Gorski <jonas.gorski@gmail.com>
|
||||
* Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
|
||||
* Copyright (C) 2008 Florian Fainelli <f.fainelli@gmail.com>
|
||||
*/
|
||||
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/of_net.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/ssb/ssb.h>
|
||||
|
||||
#define SSB_FBS_MAX_SIZE 440
|
||||
|
||||
/* Get the word-offset for a SSB_SPROM_XXX define. */
|
||||
#define SPOFF(offset) ((offset) / sizeof(u16))
|
||||
/* Helper to extract some _offset, which is one of the SSB_SPROM_XXX defines. */
|
||||
#define SPEX16(_outvar, _offset, _mask, _shift) \
|
||||
out->_outvar = ((in[SPOFF(_offset)] & (_mask)) >> (_shift))
|
||||
#define SPEX32(_outvar, _offset, _mask, _shift) \
|
||||
out->_outvar = ((((u32)in[SPOFF((_offset)+2)] << 16 | \
|
||||
in[SPOFF(_offset)]) & (_mask)) >> (_shift))
|
||||
#define SPEX(_outvar, _offset, _mask, _shift) \
|
||||
SPEX16(_outvar, _offset, _mask, _shift)
|
||||
|
||||
#define SPEX_ARRAY8(_field, _offset, _mask, _shift) \
|
||||
do { \
|
||||
SPEX(_field[0], _offset + 0, _mask, _shift); \
|
||||
SPEX(_field[1], _offset + 2, _mask, _shift); \
|
||||
SPEX(_field[2], _offset + 4, _mask, _shift); \
|
||||
SPEX(_field[3], _offset + 6, _mask, _shift); \
|
||||
SPEX(_field[4], _offset + 8, _mask, _shift); \
|
||||
SPEX(_field[5], _offset + 10, _mask, _shift); \
|
||||
SPEX(_field[6], _offset + 12, _mask, _shift); \
|
||||
SPEX(_field[7], _offset + 14, _mask, _shift); \
|
||||
} while (0)
|
||||
|
||||
struct ssb_fbs {
|
||||
struct device *dev;
|
||||
struct list_head list;
|
||||
struct ssb_sprom sprom;
|
||||
u32 pci_bus;
|
||||
u32 pci_dev;
|
||||
bool devid_override;
|
||||
};
|
||||
|
||||
static DEFINE_SPINLOCK(ssb_fbs_lock);
|
||||
static struct list_head ssb_fbs_list = LIST_HEAD_INIT(ssb_fbs_list);
|
||||
|
||||
int ssb_get_fallback_sprom(struct ssb_bus *bus, struct ssb_sprom *out)
|
||||
{
|
||||
struct ssb_fbs *pos;
|
||||
u32 pci_bus, pci_dev;
|
||||
|
||||
if (bus->bustype != SSB_BUSTYPE_PCI)
|
||||
return -ENOENT;
|
||||
|
||||
pci_bus = bus->host_pci->bus->number;
|
||||
pci_dev = PCI_SLOT(bus->host_pci->devfn);
|
||||
|
||||
list_for_each_entry(pos, &ssb_fbs_list, list) {
|
||||
if (pos->pci_bus != pci_bus ||
|
||||
pos->pci_dev != pci_dev)
|
||||
continue;
|
||||
|
||||
if (pos->devid_override)
|
||||
bus->host_pci->device = pos->sprom.dev_id;
|
||||
|
||||
memcpy(out, &pos->sprom, sizeof(struct ssb_sprom));
|
||||
dev_info(pos->dev, "requested by [%x:%x]",
|
||||
pos->pci_bus, pos->pci_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
pr_err("unable to fill SPROM for [%x:%x]\n", pci_bus, pci_dev);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static s8 sprom_extract_antgain(u8 sprom_revision, const u16 *in, u16 offset,
|
||||
u16 mask, u16 shift)
|
||||
{
|
||||
u16 v;
|
||||
u8 gain;
|
||||
|
||||
v = in[SPOFF(offset)];
|
||||
gain = (v & mask) >> shift;
|
||||
if (gain == 0xFF)
|
||||
gain = 2; /* If unset use 2dBm */
|
||||
if (sprom_revision == 1) {
|
||||
/* Convert to Q5.2 */
|
||||
gain <<= 2;
|
||||
} else {
|
||||
/* Q5.2 Fractional part is stored in 0xC0 */
|
||||
gain = ((gain & 0xC0) >> 6) | ((gain & 0x3F) << 2);
|
||||
}
|
||||
|
||||
return (s8)gain;
|
||||
}
|
||||
|
||||
static void sprom_extract_r23(struct ssb_sprom *out, const u16 *in)
|
||||
{
|
||||
SPEX(boardflags_hi, SSB_SPROM2_BFLHI, 0xFFFF, 0);
|
||||
SPEX(opo, SSB_SPROM2_OPO, SSB_SPROM2_OPO_VALUE, 0);
|
||||
SPEX(pa1lob0, SSB_SPROM2_PA1LOB0, 0xFFFF, 0);
|
||||
SPEX(pa1lob1, SSB_SPROM2_PA1LOB1, 0xFFFF, 0);
|
||||
SPEX(pa1lob2, SSB_SPROM2_PA1LOB2, 0xFFFF, 0);
|
||||
SPEX(pa1hib0, SSB_SPROM2_PA1HIB0, 0xFFFF, 0);
|
||||
SPEX(pa1hib1, SSB_SPROM2_PA1HIB1, 0xFFFF, 0);
|
||||
SPEX(pa1hib2, SSB_SPROM2_PA1HIB2, 0xFFFF, 0);
|
||||
SPEX(maxpwr_ah, SSB_SPROM2_MAXP_A, SSB_SPROM2_MAXP_A_HI, 0);
|
||||
SPEX(maxpwr_al, SSB_SPROM2_MAXP_A, SSB_SPROM2_MAXP_A_LO,
|
||||
SSB_SPROM2_MAXP_A_LO_SHIFT);
|
||||
}
|
||||
|
||||
static void sprom_extract_r123(struct ssb_sprom *out, const u16 *in)
|
||||
{
|
||||
SPEX(et0phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0A, 0);
|
||||
SPEX(et1phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1A,
|
||||
SSB_SPROM1_ETHPHY_ET1A_SHIFT);
|
||||
SPEX(et0mdcport, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0M, 14);
|
||||
SPEX(et1mdcport, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1M, 15);
|
||||
SPEX(board_rev, SSB_SPROM1_BINF, SSB_SPROM1_BINF_BREV, 0);
|
||||
SPEX(board_type, SSB_SPROM1_SPID, 0xFFFF, 0);
|
||||
if (out->revision == 1)
|
||||
SPEX(country_code, SSB_SPROM1_BINF, SSB_SPROM1_BINF_CCODE,
|
||||
SSB_SPROM1_BINF_CCODE_SHIFT);
|
||||
SPEX(ant_available_a, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTA,
|
||||
SSB_SPROM1_BINF_ANTA_SHIFT);
|
||||
SPEX(ant_available_bg, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTBG,
|
||||
SSB_SPROM1_BINF_ANTBG_SHIFT);
|
||||
SPEX(pa0b0, SSB_SPROM1_PA0B0, 0xFFFF, 0);
|
||||
SPEX(pa0b1, SSB_SPROM1_PA0B1, 0xFFFF, 0);
|
||||
SPEX(pa0b2, SSB_SPROM1_PA0B2, 0xFFFF, 0);
|
||||
SPEX(pa1b0, SSB_SPROM1_PA1B0, 0xFFFF, 0);
|
||||
SPEX(pa1b1, SSB_SPROM1_PA1B1, 0xFFFF, 0);
|
||||
SPEX(pa1b2, SSB_SPROM1_PA1B2, 0xFFFF, 0);
|
||||
SPEX(gpio0, SSB_SPROM1_GPIOA, SSB_SPROM1_GPIOA_P0, 0);
|
||||
SPEX(gpio1, SSB_SPROM1_GPIOA, SSB_SPROM1_GPIOA_P1,
|
||||
SSB_SPROM1_GPIOA_P1_SHIFT);
|
||||
SPEX(gpio2, SSB_SPROM1_GPIOB, SSB_SPROM1_GPIOB_P2, 0);
|
||||
SPEX(gpio3, SSB_SPROM1_GPIOB, SSB_SPROM1_GPIOB_P3,
|
||||
SSB_SPROM1_GPIOB_P3_SHIFT);
|
||||
SPEX(maxpwr_a, SSB_SPROM1_MAXPWR, SSB_SPROM1_MAXPWR_A,
|
||||
SSB_SPROM1_MAXPWR_A_SHIFT);
|
||||
SPEX(maxpwr_bg, SSB_SPROM1_MAXPWR, SSB_SPROM1_MAXPWR_BG, 0);
|
||||
SPEX(itssi_a, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_A,
|
||||
SSB_SPROM1_ITSSI_A_SHIFT);
|
||||
SPEX(itssi_bg, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_BG, 0);
|
||||
SPEX(boardflags_lo, SSB_SPROM1_BFLLO, 0xFFFF, 0);
|
||||
|
||||
SPEX(alpha2[0], SSB_SPROM1_CCODE, 0xff00, 8);
|
||||
SPEX(alpha2[1], SSB_SPROM1_CCODE, 0x00ff, 0);
|
||||
|
||||
/* Extract the antenna gain values. */
|
||||
out->antenna_gain.a0 = sprom_extract_antgain(out->revision, in,
|
||||
SSB_SPROM1_AGAIN,
|
||||
SSB_SPROM1_AGAIN_BG,
|
||||
SSB_SPROM1_AGAIN_BG_SHIFT);
|
||||
out->antenna_gain.a1 = sprom_extract_antgain(out->revision, in,
|
||||
SSB_SPROM1_AGAIN,
|
||||
SSB_SPROM1_AGAIN_A,
|
||||
SSB_SPROM1_AGAIN_A_SHIFT);
|
||||
if (out->revision >= 2)
|
||||
sprom_extract_r23(out, in);
|
||||
}
|
||||
|
||||
/* Revs 4 5 and 8 have partially shared layout */
|
||||
static void sprom_extract_r458(struct ssb_sprom *out, const u16 *in)
|
||||
{
|
||||
SPEX(txpid2g[0], SSB_SPROM4_TXPID2G01,
|
||||
SSB_SPROM4_TXPID2G0, SSB_SPROM4_TXPID2G0_SHIFT);
|
||||
SPEX(txpid2g[1], SSB_SPROM4_TXPID2G01,
|
||||
SSB_SPROM4_TXPID2G1, SSB_SPROM4_TXPID2G1_SHIFT);
|
||||
SPEX(txpid2g[2], SSB_SPROM4_TXPID2G23,
|
||||
SSB_SPROM4_TXPID2G2, SSB_SPROM4_TXPID2G2_SHIFT);
|
||||
SPEX(txpid2g[3], SSB_SPROM4_TXPID2G23,
|
||||
SSB_SPROM4_TXPID2G3, SSB_SPROM4_TXPID2G3_SHIFT);
|
||||
|
||||
SPEX(txpid5gl[0], SSB_SPROM4_TXPID5GL01,
|
||||
SSB_SPROM4_TXPID5GL0, SSB_SPROM4_TXPID5GL0_SHIFT);
|
||||
SPEX(txpid5gl[1], SSB_SPROM4_TXPID5GL01,
|
||||
SSB_SPROM4_TXPID5GL1, SSB_SPROM4_TXPID5GL1_SHIFT);
|
||||
SPEX(txpid5gl[2], SSB_SPROM4_TXPID5GL23,
|
||||
SSB_SPROM4_TXPID5GL2, SSB_SPROM4_TXPID5GL2_SHIFT);
|
||||
SPEX(txpid5gl[3], SSB_SPROM4_TXPID5GL23,
|
||||
SSB_SPROM4_TXPID5GL3, SSB_SPROM4_TXPID5GL3_SHIFT);
|
||||
|
||||
SPEX(txpid5g[0], SSB_SPROM4_TXPID5G01,
|
||||
SSB_SPROM4_TXPID5G0, SSB_SPROM4_TXPID5G0_SHIFT);
|
||||
SPEX(txpid5g[1], SSB_SPROM4_TXPID5G01,
|
||||
SSB_SPROM4_TXPID5G1, SSB_SPROM4_TXPID5G1_SHIFT);
|
||||
SPEX(txpid5g[2], SSB_SPROM4_TXPID5G23,
|
||||
SSB_SPROM4_TXPID5G2, SSB_SPROM4_TXPID5G2_SHIFT);
|
||||
SPEX(txpid5g[3], SSB_SPROM4_TXPID5G23,
|
||||
SSB_SPROM4_TXPID5G3, SSB_SPROM4_TXPID5G3_SHIFT);
|
||||
|
||||
SPEX(txpid5gh[0], SSB_SPROM4_TXPID5GH01,
|
||||
SSB_SPROM4_TXPID5GH0, SSB_SPROM4_TXPID5GH0_SHIFT);
|
||||
SPEX(txpid5gh[1], SSB_SPROM4_TXPID5GH01,
|
||||
SSB_SPROM4_TXPID5GH1, SSB_SPROM4_TXPID5GH1_SHIFT);
|
||||
SPEX(txpid5gh[2], SSB_SPROM4_TXPID5GH23,
|
||||
SSB_SPROM4_TXPID5GH2, SSB_SPROM4_TXPID5GH2_SHIFT);
|
||||
SPEX(txpid5gh[3], SSB_SPROM4_TXPID5GH23,
|
||||
SSB_SPROM4_TXPID5GH3, SSB_SPROM4_TXPID5GH3_SHIFT);
|
||||
}
|
||||
|
||||
static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in)
|
||||
{
|
||||
static const u16 pwr_info_offset[] = {
|
||||
SSB_SPROM4_PWR_INFO_CORE0, SSB_SPROM4_PWR_INFO_CORE1,
|
||||
SSB_SPROM4_PWR_INFO_CORE2, SSB_SPROM4_PWR_INFO_CORE3
|
||||
};
|
||||
int i;
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) !=
|
||||
ARRAY_SIZE(out->core_pwr_info));
|
||||
|
||||
SPEX(et0phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET0A, 0);
|
||||
SPEX(et1phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET1A,
|
||||
SSB_SPROM4_ETHPHY_ET1A_SHIFT);
|
||||
SPEX(board_rev, SSB_SPROM4_BOARDREV, 0xFFFF, 0);
|
||||
SPEX(board_type, SSB_SPROM1_SPID, 0xFFFF, 0);
|
||||
if (out->revision == 4) {
|
||||
SPEX(alpha2[0], SSB_SPROM4_CCODE, 0xff00, 8);
|
||||
SPEX(alpha2[1], SSB_SPROM4_CCODE, 0x00ff, 0);
|
||||
SPEX(boardflags_lo, SSB_SPROM4_BFLLO, 0xFFFF, 0);
|
||||
SPEX(boardflags_hi, SSB_SPROM4_BFLHI, 0xFFFF, 0);
|
||||
SPEX(boardflags2_lo, SSB_SPROM4_BFL2LO, 0xFFFF, 0);
|
||||
SPEX(boardflags2_hi, SSB_SPROM4_BFL2HI, 0xFFFF, 0);
|
||||
} else {
|
||||
SPEX(alpha2[0], SSB_SPROM5_CCODE, 0xff00, 8);
|
||||
SPEX(alpha2[1], SSB_SPROM5_CCODE, 0x00ff, 0);
|
||||
SPEX(boardflags_lo, SSB_SPROM5_BFLLO, 0xFFFF, 0);
|
||||
SPEX(boardflags_hi, SSB_SPROM5_BFLHI, 0xFFFF, 0);
|
||||
SPEX(boardflags2_lo, SSB_SPROM5_BFL2LO, 0xFFFF, 0);
|
||||
SPEX(boardflags2_hi, SSB_SPROM5_BFL2HI, 0xFFFF, 0);
|
||||
}
|
||||
SPEX(ant_available_a, SSB_SPROM4_ANTAVAIL, SSB_SPROM4_ANTAVAIL_A,
|
||||
SSB_SPROM4_ANTAVAIL_A_SHIFT);
|
||||
SPEX(ant_available_bg, SSB_SPROM4_ANTAVAIL, SSB_SPROM4_ANTAVAIL_BG,
|
||||
SSB_SPROM4_ANTAVAIL_BG_SHIFT);
|
||||
SPEX(maxpwr_bg, SSB_SPROM4_MAXP_BG, SSB_SPROM4_MAXP_BG_MASK, 0);
|
||||
SPEX(itssi_bg, SSB_SPROM4_MAXP_BG, SSB_SPROM4_ITSSI_BG,
|
||||
SSB_SPROM4_ITSSI_BG_SHIFT);
|
||||
SPEX(maxpwr_a, SSB_SPROM4_MAXP_A, SSB_SPROM4_MAXP_A_MASK, 0);
|
||||
SPEX(itssi_a, SSB_SPROM4_MAXP_A, SSB_SPROM4_ITSSI_A,
|
||||
SSB_SPROM4_ITSSI_A_SHIFT);
|
||||
if (out->revision == 4) {
|
||||
SPEX(gpio0, SSB_SPROM4_GPIOA, SSB_SPROM4_GPIOA_P0, 0);
|
||||
SPEX(gpio1, SSB_SPROM4_GPIOA, SSB_SPROM4_GPIOA_P1,
|
||||
SSB_SPROM4_GPIOA_P1_SHIFT);
|
||||
SPEX(gpio2, SSB_SPROM4_GPIOB, SSB_SPROM4_GPIOB_P2, 0);
|
||||
SPEX(gpio3, SSB_SPROM4_GPIOB, SSB_SPROM4_GPIOB_P3,
|
||||
SSB_SPROM4_GPIOB_P3_SHIFT);
|
||||
} else {
|
||||
SPEX(gpio0, SSB_SPROM5_GPIOA, SSB_SPROM5_GPIOA_P0, 0);
|
||||
SPEX(gpio1, SSB_SPROM5_GPIOA, SSB_SPROM5_GPIOA_P1,
|
||||
SSB_SPROM5_GPIOA_P1_SHIFT);
|
||||
SPEX(gpio2, SSB_SPROM5_GPIOB, SSB_SPROM5_GPIOB_P2, 0);
|
||||
SPEX(gpio3, SSB_SPROM5_GPIOB, SSB_SPROM5_GPIOB_P3,
|
||||
SSB_SPROM5_GPIOB_P3_SHIFT);
|
||||
}
|
||||
|
||||
/* Extract the antenna gain values. */
|
||||
out->antenna_gain.a0 = sprom_extract_antgain(out->revision, in,
|
||||
SSB_SPROM4_AGAIN01,
|
||||
SSB_SPROM4_AGAIN0,
|
||||
SSB_SPROM4_AGAIN0_SHIFT);
|
||||
out->antenna_gain.a1 = sprom_extract_antgain(out->revision, in,
|
||||
SSB_SPROM4_AGAIN01,
|
||||
SSB_SPROM4_AGAIN1,
|
||||
SSB_SPROM4_AGAIN1_SHIFT);
|
||||
out->antenna_gain.a2 = sprom_extract_antgain(out->revision, in,
|
||||
SSB_SPROM4_AGAIN23,
|
||||
SSB_SPROM4_AGAIN2,
|
||||
SSB_SPROM4_AGAIN2_SHIFT);
|
||||
out->antenna_gain.a3 = sprom_extract_antgain(out->revision, in,
|
||||
SSB_SPROM4_AGAIN23,
|
||||
SSB_SPROM4_AGAIN3,
|
||||
SSB_SPROM4_AGAIN3_SHIFT);
|
||||
|
||||
/* Extract cores power info info */
|
||||
for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) {
|
||||
u16 o = pwr_info_offset[i];
|
||||
|
||||
SPEX(core_pwr_info[i].itssi_2g, o + SSB_SPROM4_2G_MAXP_ITSSI,
|
||||
SSB_SPROM4_2G_ITSSI, SSB_SPROM4_2G_ITSSI_SHIFT);
|
||||
SPEX(core_pwr_info[i].maxpwr_2g, o + SSB_SPROM4_2G_MAXP_ITSSI,
|
||||
SSB_SPROM4_2G_MAXP, 0);
|
||||
|
||||
SPEX(core_pwr_info[i].pa_2g[0], o + SSB_SPROM4_2G_PA_0, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_2g[1], o + SSB_SPROM4_2G_PA_1, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_2g[2], o + SSB_SPROM4_2G_PA_2, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_2g[3], o + SSB_SPROM4_2G_PA_3, ~0, 0);
|
||||
|
||||
SPEX(core_pwr_info[i].itssi_5g, o + SSB_SPROM4_5G_MAXP_ITSSI,
|
||||
SSB_SPROM4_5G_ITSSI, SSB_SPROM4_5G_ITSSI_SHIFT);
|
||||
SPEX(core_pwr_info[i].maxpwr_5g, o + SSB_SPROM4_5G_MAXP_ITSSI,
|
||||
SSB_SPROM4_5G_MAXP, 0);
|
||||
SPEX(core_pwr_info[i].maxpwr_5gh, o + SSB_SPROM4_5GHL_MAXP,
|
||||
SSB_SPROM4_5GH_MAXP, 0);
|
||||
SPEX(core_pwr_info[i].maxpwr_5gl, o + SSB_SPROM4_5GHL_MAXP,
|
||||
SSB_SPROM4_5GL_MAXP, SSB_SPROM4_5GL_MAXP_SHIFT);
|
||||
|
||||
SPEX(core_pwr_info[i].pa_5gl[0], o + SSB_SPROM4_5GL_PA_0, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_5gl[1], o + SSB_SPROM4_5GL_PA_1, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_5gl[2], o + SSB_SPROM4_5GL_PA_2, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_5gl[3], o + SSB_SPROM4_5GL_PA_3, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_5g[0], o + SSB_SPROM4_5G_PA_0, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_5g[1], o + SSB_SPROM4_5G_PA_1, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_5g[2], o + SSB_SPROM4_5G_PA_2, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_5g[3], o + SSB_SPROM4_5G_PA_3, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_5gh[0], o + SSB_SPROM4_5GH_PA_0, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_5gh[1], o + SSB_SPROM4_5GH_PA_1, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_5gh[2], o + SSB_SPROM4_5GH_PA_2, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_5gh[3], o + SSB_SPROM4_5GH_PA_3, ~0, 0);
|
||||
}
|
||||
|
||||
sprom_extract_r458(out, in);
|
||||
|
||||
/* TODO - get remaining rev 4 stuff needed */
|
||||
}
|
||||
|
||||
static void sprom_extract_r8(struct ssb_sprom *out, const u16 *in)
|
||||
{
|
||||
int i;
|
||||
u16 o;
|
||||
static const u16 pwr_info_offset[] = {
|
||||
SSB_SROM8_PWR_INFO_CORE0, SSB_SROM8_PWR_INFO_CORE1,
|
||||
SSB_SROM8_PWR_INFO_CORE2, SSB_SROM8_PWR_INFO_CORE3
|
||||
};
|
||||
BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) !=
|
||||
ARRAY_SIZE(out->core_pwr_info));
|
||||
|
||||
SPEX(board_rev, SSB_SPROM8_BOARDREV, 0xFFFF, 0);
|
||||
SPEX(board_type, SSB_SPROM1_SPID, 0xFFFF, 0);
|
||||
SPEX(alpha2[0], SSB_SPROM8_CCODE, 0xff00, 8);
|
||||
SPEX(alpha2[1], SSB_SPROM8_CCODE, 0x00ff, 0);
|
||||
SPEX(boardflags_lo, SSB_SPROM8_BFLLO, 0xFFFF, 0);
|
||||
SPEX(boardflags_hi, SSB_SPROM8_BFLHI, 0xFFFF, 0);
|
||||
SPEX(boardflags2_lo, SSB_SPROM8_BFL2LO, 0xFFFF, 0);
|
||||
SPEX(boardflags2_hi, SSB_SPROM8_BFL2HI, 0xFFFF, 0);
|
||||
SPEX(ant_available_a, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_A,
|
||||
SSB_SPROM8_ANTAVAIL_A_SHIFT);
|
||||
SPEX(ant_available_bg, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_BG,
|
||||
SSB_SPROM8_ANTAVAIL_BG_SHIFT);
|
||||
SPEX(maxpwr_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_MAXP_BG_MASK, 0);
|
||||
SPEX(itssi_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_ITSSI_BG,
|
||||
SSB_SPROM8_ITSSI_BG_SHIFT);
|
||||
SPEX(maxpwr_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_MAXP_A_MASK, 0);
|
||||
SPEX(itssi_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_ITSSI_A,
|
||||
SSB_SPROM8_ITSSI_A_SHIFT);
|
||||
SPEX(maxpwr_ah, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AH_MASK, 0);
|
||||
SPEX(maxpwr_al, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AL_MASK,
|
||||
SSB_SPROM8_MAXP_AL_SHIFT);
|
||||
SPEX(gpio0, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P0, 0);
|
||||
SPEX(gpio1, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P1,
|
||||
SSB_SPROM8_GPIOA_P1_SHIFT);
|
||||
SPEX(gpio2, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P2, 0);
|
||||
SPEX(gpio3, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P3,
|
||||
SSB_SPROM8_GPIOB_P3_SHIFT);
|
||||
SPEX(tri2g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI2G, 0);
|
||||
SPEX(tri5g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI5G,
|
||||
SSB_SPROM8_TRI5G_SHIFT);
|
||||
SPEX(tri5gl, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GL, 0);
|
||||
SPEX(tri5gh, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GH,
|
||||
SSB_SPROM8_TRI5GH_SHIFT);
|
||||
SPEX(rxpo2g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO2G, 0);
|
||||
SPEX(rxpo5g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO5G,
|
||||
SSB_SPROM8_RXPO5G_SHIFT);
|
||||
SPEX(rssismf2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMF2G, 0);
|
||||
SPEX(rssismc2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMC2G,
|
||||
SSB_SPROM8_RSSISMC2G_SHIFT);
|
||||
SPEX(rssisav2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISAV2G,
|
||||
SSB_SPROM8_RSSISAV2G_SHIFT);
|
||||
SPEX(bxa2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_BXA2G,
|
||||
SSB_SPROM8_BXA2G_SHIFT);
|
||||
SPEX(rssismf5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMF5G, 0);
|
||||
SPEX(rssismc5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMC5G,
|
||||
SSB_SPROM8_RSSISMC5G_SHIFT);
|
||||
SPEX(rssisav5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISAV5G,
|
||||
SSB_SPROM8_RSSISAV5G_SHIFT);
|
||||
SPEX(bxa5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_BXA5G,
|
||||
SSB_SPROM8_BXA5G_SHIFT);
|
||||
SPEX(pa0b0, SSB_SPROM8_PA0B0, 0xFFFF, 0);
|
||||
SPEX(pa0b1, SSB_SPROM8_PA0B1, 0xFFFF, 0);
|
||||
SPEX(pa0b2, SSB_SPROM8_PA0B2, 0xFFFF, 0);
|
||||
SPEX(pa1b0, SSB_SPROM8_PA1B0, 0xFFFF, 0);
|
||||
SPEX(pa1b1, SSB_SPROM8_PA1B1, 0xFFFF, 0);
|
||||
SPEX(pa1b2, SSB_SPROM8_PA1B2, 0xFFFF, 0);
|
||||
SPEX(pa1lob0, SSB_SPROM8_PA1LOB0, 0xFFFF, 0);
|
||||
SPEX(pa1lob1, SSB_SPROM8_PA1LOB1, 0xFFFF, 0);
|
||||
SPEX(pa1lob2, SSB_SPROM8_PA1LOB2, 0xFFFF, 0);
|
||||
SPEX(pa1hib0, SSB_SPROM8_PA1HIB0, 0xFFFF, 0);
|
||||
SPEX(pa1hib1, SSB_SPROM8_PA1HIB1, 0xFFFF, 0);
|
||||
SPEX(pa1hib2, SSB_SPROM8_PA1HIB2, 0xFFFF, 0);
|
||||
SPEX(cck2gpo, SSB_SPROM8_CCK2GPO, 0xFFFF, 0);
|
||||
SPEX32(ofdm2gpo, SSB_SPROM8_OFDM2GPO, 0xFFFFFFFF, 0);
|
||||
SPEX32(ofdm5glpo, SSB_SPROM8_OFDM5GLPO, 0xFFFFFFFF, 0);
|
||||
SPEX32(ofdm5gpo, SSB_SPROM8_OFDM5GPO, 0xFFFFFFFF, 0);
|
||||
SPEX32(ofdm5ghpo, SSB_SPROM8_OFDM5GHPO, 0xFFFFFFFF, 0);
|
||||
|
||||
/* Extract the antenna gain values. */
|
||||
out->antenna_gain.a0 = sprom_extract_antgain(out->revision, in,
|
||||
SSB_SPROM8_AGAIN01,
|
||||
SSB_SPROM8_AGAIN0,
|
||||
SSB_SPROM8_AGAIN0_SHIFT);
|
||||
out->antenna_gain.a1 = sprom_extract_antgain(out->revision, in,
|
||||
SSB_SPROM8_AGAIN01,
|
||||
SSB_SPROM8_AGAIN1,
|
||||
SSB_SPROM8_AGAIN1_SHIFT);
|
||||
out->antenna_gain.a2 = sprom_extract_antgain(out->revision, in,
|
||||
SSB_SPROM8_AGAIN23,
|
||||
SSB_SPROM8_AGAIN2,
|
||||
SSB_SPROM8_AGAIN2_SHIFT);
|
||||
out->antenna_gain.a3 = sprom_extract_antgain(out->revision, in,
|
||||
SSB_SPROM8_AGAIN23,
|
||||
SSB_SPROM8_AGAIN3,
|
||||
SSB_SPROM8_AGAIN3_SHIFT);
|
||||
|
||||
/* Extract cores power info info */
|
||||
for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) {
|
||||
o = pwr_info_offset[i];
|
||||
SPEX(core_pwr_info[i].itssi_2g, o + SSB_SROM8_2G_MAXP_ITSSI,
|
||||
SSB_SPROM8_2G_ITSSI, SSB_SPROM8_2G_ITSSI_SHIFT);
|
||||
SPEX(core_pwr_info[i].maxpwr_2g, o + SSB_SROM8_2G_MAXP_ITSSI,
|
||||
SSB_SPROM8_2G_MAXP, 0);
|
||||
|
||||
SPEX(core_pwr_info[i].pa_2g[0], o + SSB_SROM8_2G_PA_0, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_2g[1], o + SSB_SROM8_2G_PA_1, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_2g[2], o + SSB_SROM8_2G_PA_2, ~0, 0);
|
||||
|
||||
SPEX(core_pwr_info[i].itssi_5g, o + SSB_SROM8_5G_MAXP_ITSSI,
|
||||
SSB_SPROM8_5G_ITSSI, SSB_SPROM8_5G_ITSSI_SHIFT);
|
||||
SPEX(core_pwr_info[i].maxpwr_5g, o + SSB_SROM8_5G_MAXP_ITSSI,
|
||||
SSB_SPROM8_5G_MAXP, 0);
|
||||
SPEX(core_pwr_info[i].maxpwr_5gh, o + SSB_SPROM8_5GHL_MAXP,
|
||||
SSB_SPROM8_5GH_MAXP, 0);
|
||||
SPEX(core_pwr_info[i].maxpwr_5gl, o + SSB_SPROM8_5GHL_MAXP,
|
||||
SSB_SPROM8_5GL_MAXP, SSB_SPROM8_5GL_MAXP_SHIFT);
|
||||
|
||||
SPEX(core_pwr_info[i].pa_5gl[0], o + SSB_SROM8_5GL_PA_0, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_5gl[1], o + SSB_SROM8_5GL_PA_1, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_5gl[2], o + SSB_SROM8_5GL_PA_2, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_5g[0], o + SSB_SROM8_5G_PA_0, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_5g[1], o + SSB_SROM8_5G_PA_1, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_5g[2], o + SSB_SROM8_5G_PA_2, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_5gh[0], o + SSB_SROM8_5GH_PA_0, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_5gh[1], o + SSB_SROM8_5GH_PA_1, ~0, 0);
|
||||
SPEX(core_pwr_info[i].pa_5gh[2], o + SSB_SROM8_5GH_PA_2, ~0, 0);
|
||||
}
|
||||
|
||||
/* Extract FEM info */
|
||||
SPEX(fem.ghz2.tssipos, SSB_SPROM8_FEM2G,
|
||||
SSB_SROM8_FEM_TSSIPOS, SSB_SROM8_FEM_TSSIPOS_SHIFT);
|
||||
SPEX(fem.ghz2.extpa_gain, SSB_SPROM8_FEM2G,
|
||||
SSB_SROM8_FEM_EXTPA_GAIN, SSB_SROM8_FEM_EXTPA_GAIN_SHIFT);
|
||||
SPEX(fem.ghz2.pdet_range, SSB_SPROM8_FEM2G,
|
||||
SSB_SROM8_FEM_PDET_RANGE, SSB_SROM8_FEM_PDET_RANGE_SHIFT);
|
||||
SPEX(fem.ghz2.tr_iso, SSB_SPROM8_FEM2G,
|
||||
SSB_SROM8_FEM_TR_ISO, SSB_SROM8_FEM_TR_ISO_SHIFT);
|
||||
SPEX(fem.ghz2.antswlut, SSB_SPROM8_FEM2G,
|
||||
SSB_SROM8_FEM_ANTSWLUT, SSB_SROM8_FEM_ANTSWLUT_SHIFT);
|
||||
|
||||
SPEX(fem.ghz5.tssipos, SSB_SPROM8_FEM5G,
|
||||
SSB_SROM8_FEM_TSSIPOS, SSB_SROM8_FEM_TSSIPOS_SHIFT);
|
||||
SPEX(fem.ghz5.extpa_gain, SSB_SPROM8_FEM5G,
|
||||
SSB_SROM8_FEM_EXTPA_GAIN, SSB_SROM8_FEM_EXTPA_GAIN_SHIFT);
|
||||
SPEX(fem.ghz5.pdet_range, SSB_SPROM8_FEM5G,
|
||||
SSB_SROM8_FEM_PDET_RANGE, SSB_SROM8_FEM_PDET_RANGE_SHIFT);
|
||||
SPEX(fem.ghz5.tr_iso, SSB_SPROM8_FEM5G,
|
||||
SSB_SROM8_FEM_TR_ISO, SSB_SROM8_FEM_TR_ISO_SHIFT);
|
||||
SPEX(fem.ghz5.antswlut, SSB_SPROM8_FEM5G,
|
||||
SSB_SROM8_FEM_ANTSWLUT, SSB_SROM8_FEM_ANTSWLUT_SHIFT);
|
||||
|
||||
SPEX(leddc_on_time, SSB_SPROM8_LEDDC, SSB_SPROM8_LEDDC_ON,
|
||||
SSB_SPROM8_LEDDC_ON_SHIFT);
|
||||
SPEX(leddc_off_time, SSB_SPROM8_LEDDC, SSB_SPROM8_LEDDC_OFF,
|
||||
SSB_SPROM8_LEDDC_OFF_SHIFT);
|
||||
|
||||
SPEX(txchain, SSB_SPROM8_TXRXC, SSB_SPROM8_TXRXC_TXCHAIN,
|
||||
SSB_SPROM8_TXRXC_TXCHAIN_SHIFT);
|
||||
SPEX(rxchain, SSB_SPROM8_TXRXC, SSB_SPROM8_TXRXC_RXCHAIN,
|
||||
SSB_SPROM8_TXRXC_RXCHAIN_SHIFT);
|
||||
SPEX(antswitch, SSB_SPROM8_TXRXC, SSB_SPROM8_TXRXC_SWITCH,
|
||||
SSB_SPROM8_TXRXC_SWITCH_SHIFT);
|
||||
|
||||
SPEX(opo, SSB_SPROM8_OFDM2GPO, 0x00ff, 0);
|
||||
|
||||
SPEX_ARRAY8(mcs2gpo, SSB_SPROM8_2G_MCSPO, ~0, 0);
|
||||
SPEX_ARRAY8(mcs5gpo, SSB_SPROM8_5G_MCSPO, ~0, 0);
|
||||
SPEX_ARRAY8(mcs5glpo, SSB_SPROM8_5GL_MCSPO, ~0, 0);
|
||||
SPEX_ARRAY8(mcs5ghpo, SSB_SPROM8_5GH_MCSPO, ~0, 0);
|
||||
|
||||
SPEX(rawtempsense, SSB_SPROM8_RAWTS, SSB_SPROM8_RAWTS_RAWTEMP,
|
||||
SSB_SPROM8_RAWTS_RAWTEMP_SHIFT);
|
||||
SPEX(measpower, SSB_SPROM8_RAWTS, SSB_SPROM8_RAWTS_MEASPOWER,
|
||||
SSB_SPROM8_RAWTS_MEASPOWER_SHIFT);
|
||||
SPEX(tempsense_slope, SSB_SPROM8_OPT_CORRX,
|
||||
SSB_SPROM8_OPT_CORRX_TEMP_SLOPE,
|
||||
SSB_SPROM8_OPT_CORRX_TEMP_SLOPE_SHIFT);
|
||||
SPEX(tempcorrx, SSB_SPROM8_OPT_CORRX, SSB_SPROM8_OPT_CORRX_TEMPCORRX,
|
||||
SSB_SPROM8_OPT_CORRX_TEMPCORRX_SHIFT);
|
||||
SPEX(tempsense_option, SSB_SPROM8_OPT_CORRX,
|
||||
SSB_SPROM8_OPT_CORRX_TEMP_OPTION,
|
||||
SSB_SPROM8_OPT_CORRX_TEMP_OPTION_SHIFT);
|
||||
SPEX(freqoffset_corr, SSB_SPROM8_HWIQ_IQSWP,
|
||||
SSB_SPROM8_HWIQ_IQSWP_FREQ_CORR,
|
||||
SSB_SPROM8_HWIQ_IQSWP_FREQ_CORR_SHIFT);
|
||||
SPEX(iqcal_swp_dis, SSB_SPROM8_HWIQ_IQSWP,
|
||||
SSB_SPROM8_HWIQ_IQSWP_IQCAL_SWP,
|
||||
SSB_SPROM8_HWIQ_IQSWP_IQCAL_SWP_SHIFT);
|
||||
SPEX(hw_iqcal_en, SSB_SPROM8_HWIQ_IQSWP, SSB_SPROM8_HWIQ_IQSWP_HW_IQCAL,
|
||||
SSB_SPROM8_HWIQ_IQSWP_HW_IQCAL_SHIFT);
|
||||
|
||||
SPEX(bw40po, SSB_SPROM8_BW40PO, ~0, 0);
|
||||
SPEX(cddpo, SSB_SPROM8_CDDPO, ~0, 0);
|
||||
SPEX(stbcpo, SSB_SPROM8_STBCPO, ~0, 0);
|
||||
SPEX(bwduppo, SSB_SPROM8_BWDUPPO, ~0, 0);
|
||||
|
||||
SPEX(tempthresh, SSB_SPROM8_THERMAL, SSB_SPROM8_THERMAL_TRESH,
|
||||
SSB_SPROM8_THERMAL_TRESH_SHIFT);
|
||||
SPEX(tempoffset, SSB_SPROM8_THERMAL, SSB_SPROM8_THERMAL_OFFSET,
|
||||
SSB_SPROM8_THERMAL_OFFSET_SHIFT);
|
||||
SPEX(phycal_tempdelta, SSB_SPROM8_TEMPDELTA,
|
||||
SSB_SPROM8_TEMPDELTA_PHYCAL,
|
||||
SSB_SPROM8_TEMPDELTA_PHYCAL_SHIFT);
|
||||
SPEX(temps_period, SSB_SPROM8_TEMPDELTA, SSB_SPROM8_TEMPDELTA_PERIOD,
|
||||
SSB_SPROM8_TEMPDELTA_PERIOD_SHIFT);
|
||||
SPEX(temps_hysteresis, SSB_SPROM8_TEMPDELTA,
|
||||
SSB_SPROM8_TEMPDELTA_HYSTERESIS,
|
||||
SSB_SPROM8_TEMPDELTA_HYSTERESIS_SHIFT);
|
||||
sprom_extract_r458(out, in);
|
||||
|
||||
/* TODO - get remaining rev 8 stuff needed */
|
||||
}
|
||||
|
||||
static int sprom_extract(struct ssb_fbs *priv, const u16 *in, u16 size)
|
||||
{
|
||||
struct ssb_sprom *out = &priv->sprom;
|
||||
|
||||
memset(out, 0, sizeof(*out));
|
||||
|
||||
out->revision = in[size - 1] & 0x00FF;
|
||||
|
||||
switch (out->revision) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
sprom_extract_r123(out, in);
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
sprom_extract_r45(out, in);
|
||||
break;
|
||||
case 8:
|
||||
sprom_extract_r8(out, in);
|
||||
break;
|
||||
default:
|
||||
dev_warn(priv->dev,
|
||||
"Unsupported SPROM revision %d detected."
|
||||
" Will extract v1\n",
|
||||
out->revision);
|
||||
out->revision = 1;
|
||||
sprom_extract_r123(out, in);
|
||||
}
|
||||
|
||||
if (out->boardflags_lo == 0xFFFF)
|
||||
out->boardflags_lo = 0; /* per specs */
|
||||
if (out->boardflags_hi == 0xFFFF)
|
||||
out->boardflags_hi = 0; /* per specs */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ssb_fbs_fixup(struct ssb_fbs *priv, u16 *sprom)
|
||||
{
|
||||
struct device_node *node = priv->dev->of_node;
|
||||
u32 fixups, off, val;
|
||||
int i = 0;
|
||||
|
||||
if (!of_get_property(node, "brcm,sprom-fixups", &fixups))
|
||||
return;
|
||||
|
||||
fixups /= sizeof(u32);
|
||||
|
||||
dev_info(priv->dev, "patching SPROM with %u fixups...\n", fixups >> 1);
|
||||
|
||||
while (i < fixups) {
|
||||
if (of_property_read_u32_index(node, "brcm,sprom-fixups",
|
||||
i++, &off)) {
|
||||
dev_err(priv->dev, "error reading fixup[%u] offset\n",
|
||||
i - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (of_property_read_u32_index(node, "brcm,sprom-fixups",
|
||||
i++, &val)) {
|
||||
dev_err(priv->dev, "error reading fixup[%u] value\n",
|
||||
i - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
dev_dbg(priv->dev, "fixup[%d]=0x%04x\n", off, val);
|
||||
|
||||
sprom[off] = val;
|
||||
}
|
||||
}
|
||||
|
||||
static bool sprom_override_devid(struct ssb_fbs *priv, struct ssb_sprom *out,
|
||||
const u16 *in)
|
||||
{
|
||||
SPEX(dev_id, SSB_SPROM1_PID, 0xFFFF, 0);
|
||||
return !!out->dev_id;
|
||||
}
|
||||
|
||||
static int ssb_fbs_set(struct ssb_fbs *priv, struct device_node *node)
|
||||
{
|
||||
struct ssb_sprom *sprom = &priv->sprom;
|
||||
const struct firmware *fw;
|
||||
const char *sprom_name;
|
||||
int err;
|
||||
|
||||
if (of_property_read_string(node, "brcm,sprom", &sprom_name))
|
||||
sprom_name = NULL;
|
||||
|
||||
if (sprom_name) {
|
||||
err = request_firmware_direct(&fw, sprom_name, priv->dev);
|
||||
if (err)
|
||||
dev_err(priv->dev, "%s load error\n", sprom_name);
|
||||
} else {
|
||||
err = -ENOENT;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
sprom->revision = 0x02;
|
||||
sprom->board_rev = 0x0017;
|
||||
sprom->country_code = 0x00;
|
||||
sprom->ant_available_bg = 0x03;
|
||||
sprom->pa0b0 = 0x15ae;
|
||||
sprom->pa0b1 = 0xfa85;
|
||||
sprom->pa0b2 = 0xfe8d;
|
||||
sprom->pa1b0 = 0xffff;
|
||||
sprom->pa1b1 = 0xffff;
|
||||
sprom->pa1b2 = 0xffff;
|
||||
sprom->gpio0 = 0xff;
|
||||
sprom->gpio1 = 0xff;
|
||||
sprom->gpio2 = 0xff;
|
||||
sprom->gpio3 = 0xff;
|
||||
sprom->maxpwr_bg = 0x4c;
|
||||
sprom->itssi_bg = 0x00;
|
||||
sprom->boardflags_lo = 0x2848;
|
||||
sprom->boardflags_hi = 0x0000;
|
||||
priv->devid_override = false;
|
||||
|
||||
dev_warn(priv->dev, "using basic SPROM\n");
|
||||
} else {
|
||||
size_t size = min(fw->size, (size_t) SSB_FBS_MAX_SIZE);
|
||||
u16 tmp_sprom[SSB_FBS_MAX_SIZE >> 1];
|
||||
u32 i, j;
|
||||
|
||||
for (i = 0, j = 0; i < size; i += 2, j++)
|
||||
tmp_sprom[j] = (fw->data[i] << 8) | fw->data[i + 1];
|
||||
|
||||
release_firmware(fw);
|
||||
ssb_fbs_fixup(priv, tmp_sprom);
|
||||
sprom_extract(priv, tmp_sprom, size >> 1);
|
||||
|
||||
priv->devid_override = sprom_override_devid(priv, sprom,
|
||||
tmp_sprom);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ssb_fbs_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct ssb_fbs *priv;
|
||||
unsigned long flags;
|
||||
u8 mac[ETH_ALEN];
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->dev = dev;
|
||||
|
||||
ssb_fbs_set(priv, node);
|
||||
|
||||
of_property_read_u32(node, "pci-bus", &priv->pci_bus);
|
||||
of_property_read_u32(node, "pci-dev", &priv->pci_dev);
|
||||
|
||||
of_get_mac_address(node, mac);
|
||||
if (is_valid_ether_addr(mac)) {
|
||||
dev_info(dev, "mtd mac %pM\n", mac);
|
||||
} else {
|
||||
random_ether_addr(mac);
|
||||
dev_info(dev, "random mac %pM\n", mac);
|
||||
}
|
||||
|
||||
memcpy(priv->sprom.il0mac, mac, ETH_ALEN);
|
||||
memcpy(priv->sprom.et0mac, mac, ETH_ALEN);
|
||||
memcpy(priv->sprom.et1mac, mac, ETH_ALEN);
|
||||
memcpy(priv->sprom.et2mac, mac, ETH_ALEN);
|
||||
|
||||
spin_lock_irqsave(&ssb_fbs_lock, flags);
|
||||
list_add(&priv->list, &ssb_fbs_list);
|
||||
spin_unlock_irqrestore(&ssb_fbs_lock, flags);
|
||||
|
||||
dev_info(dev, "registered SPROM for [%x:%x]\n",
|
||||
priv->pci_bus, priv->pci_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ssb_fbs_of_match[] = {
|
||||
{ .compatible = "brcm,ssb-sprom", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ssb_fbs_of_match);
|
||||
|
||||
static struct platform_driver ssb_fbs_driver = {
|
||||
.probe = ssb_fbs_probe,
|
||||
.driver = {
|
||||
.name = "ssb-sprom",
|
||||
.of_match_table = ssb_fbs_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
int __init ssb_fbs_register(void)
|
||||
{
|
||||
return platform_driver_register(&ssb_fbs_driver);
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
#ifndef __MTK_BMT_H
|
||||
#define __MTK_BMT_H
|
||||
|
||||
#ifdef CONFIG_MTD_NAND_MTK_BMT
|
||||
int mtk_bmt_attach(struct mtd_info *mtd);
|
||||
void mtk_bmt_detach(struct mtd_info *mtd);
|
||||
#else
|
||||
static inline int mtk_bmt_attach(struct mtd_info *mtd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void mtk_bmt_detach(struct mtd_info *mtd)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user