Merge Official Source

Signed-off-by: Tianling Shen <cnsztl@immortalwrt.org>
This commit is contained in:
Tianling Shen 2021-11-26 19:24:30 +08:00
commit a6bb9d5a68
No known key found for this signature in database
GPG Key ID: 6850B6345C862176
32 changed files with 1097 additions and 140 deletions

View File

@ -0,0 +1,35 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Mon, 22 Nov 2021 21:39:38 +0100
Subject: [PATCH] mac80211: fix rate control for retransmitted frames
Since retransmission clears info->control, rate control needs to be called
again, otherwise the driver might crash due to invalid rates.
Cc: stable@vger.kernel.org # 5.14+
Reported-by: Aaro Koskinen <aaro.koskinen@iki.fi>
Reported-by: Robert W <rwbugreport@lost-in-the-void.net>
Fixes: 03c3911d2d67 ("mac80211: call ieee80211_tx_h_rate_ctrl() when dequeue")
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1825,15 +1825,15 @@ static int invoke_tx_handlers_late(struc
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
ieee80211_tx_result res = TX_CONTINUE;
+ if (!ieee80211_hw_check(&tx->local->hw, HAS_RATE_CONTROL))
+ CALL_TXH(ieee80211_tx_h_rate_ctrl);
+
if (unlikely(info->flags & IEEE80211_TX_INTFL_RETRANSMISSION)) {
__skb_queue_tail(&tx->skbs, tx->skb);
tx->skb = NULL;
goto txh_done;
}
- if (!ieee80211_hw_check(&tx->local->hw, HAS_RATE_CONTROL))
- CALL_TXH(ieee80211_tx_h_rate_ctrl);
-
CALL_TXH(ieee80211_tx_h_michael_mic_add);
CALL_TXH(ieee80211_tx_h_sequence);
CALL_TXH(ieee80211_tx_h_fragment);

View File

@ -0,0 +1,44 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Wed, 24 Nov 2021 10:30:41 +0100
Subject: [PATCH] mac80211: fix regression in SSN handling of addba tx
Some drivers that do their own sequence number allocation (e.g. ath9k) rely
on being able to modify params->ssn on starting tx ampdu sessions.
This was broken by a change that modified it to use sta->tid_seq[tid] instead.
Cc: stable@vger.kernel.org
Fixes: 31d8bb4e07f8 ("mac80211: agg-tx: refactor sending addba")
Reported-by: Eneas U de Queiroz <cotequeiroz@gmail.com>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -480,8 +480,7 @@ static void ieee80211_send_addba_with_ti
/* send AddBA request */
ieee80211_send_addba_request(sdata, sta->sta.addr, tid,
- tid_tx->dialog_token,
- sta->tid_seq[tid] >> 4,
+ tid_tx->dialog_token, tid_tx->ssn,
buf_size, tid_tx->timeout);
WARN_ON(test_and_set_bit(HT_AGG_STATE_SENT_ADDBA, &tid_tx->state));
@@ -523,6 +522,7 @@ void ieee80211_tx_ba_session_handle_star
params.ssn = sta->tid_seq[tid] >> 4;
ret = drv_ampdu_action(local, sdata, &params);
+ tid_tx->ssn = params.ssn;
if (ret == IEEE80211_AMPDU_TX_START_DELAY_ADDBA) {
return;
} else if (ret == IEEE80211_AMPDU_TX_START_IMMEDIATE) {
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -199,6 +199,7 @@ struct tid_ampdu_tx {
u8 stop_initiator;
bool tx_stop;
u16 buf_size;
+ u16 ssn;
u16 failed_bar_ssn;
bool bar_pending;

View File

@ -0,0 +1,62 @@
From: Xing Song <xing.song@mediatek.com>
Date: Tue, 23 Nov 2021 11:31:23 +0800
Subject: [PATCH] mac80211: set up the fwd_skb->dev for mesh forwarding
Mesh forwarding requires that the fwd_skb->dev is set up for TX handling,
otherwise the following warning will be generated, so set it up for the
pending frames.
[ 72.835674 ] WARNING: CPU: 0 PID: 1193 at __skb_flow_dissect+0x284/0x1298
[ 72.842379 ] Modules linked in: ksmbd pppoe ppp_async l2tp_ppp ...
[ 72.962020 ] CPU: 0 PID: 1193 Comm: kworker/u5:1 Tainted: P S 5.4.137 #0
[ 72.969938 ] Hardware name: MT7622_MT7531 RFB (DT)
[ 72.974659 ] Workqueue: napi_workq napi_workfn
[ 72.979025 ] pstate: 60000005 (nZCv daif -PAN -UAO)
[ 72.983822 ] pc : __skb_flow_dissect+0x284/0x1298
[ 72.988444 ] lr : __skb_flow_dissect+0x54/0x1298
[ 72.992977 ] sp : ffffffc010c738c0
[ 72.996293 ] x29: ffffffc010c738c0 x28: 0000000000000000
[ 73.001615 ] x27: 000000000000ffc2 x26: ffffff800c2eb818
[ 73.006937 ] x25: ffffffc010a987c8 x24: 00000000000000ce
[ 73.012259 ] x23: ffffffc010c73a28 x22: ffffffc010a99c60
[ 73.017581 ] x21: 000000000000ffc2 x20: ffffff80094da800
[ 73.022903 ] x19: 0000000000000000 x18: 0000000000000014
[ 73.028226 ] x17: 00000000084d16af x16: 00000000d1fc0bab
[ 73.033548 ] x15: 00000000715f6034 x14: 000000009dbdd301
[ 73.038870 ] x13: 00000000ea4dcbc3 x12: 0000000000000040
[ 73.044192 ] x11: 000000000eb00ff0 x10: 0000000000000000
[ 73.049513 ] x9 : 000000000eb00073 x8 : 0000000000000088
[ 73.054834 ] x7 : 0000000000000000 x6 : 0000000000000001
[ 73.060155 ] x5 : 0000000000000000 x4 : 0000000000000000
[ 73.065476 ] x3 : ffffffc010a98000 x2 : 0000000000000000
[ 73.070797 ] x1 : 0000000000000000 x0 : 0000000000000000
[ 73.076120 ] Call trace:
[ 73.078572 ] __skb_flow_dissect+0x284/0x1298
[ 73.082846 ] __skb_get_hash+0x7c/0x228
[ 73.086629 ] ieee80211_txq_may_transmit+0x7fc/0x17b8 [mac80211]
[ 73.092564 ] ieee80211_tx_prepare_skb+0x20c/0x268 [mac80211]
[ 73.098238 ] ieee80211_tx_pending+0x144/0x330 [mac80211]
[ 73.103560 ] tasklet_action_common.isra.16+0xb4/0x158
[ 73.108618 ] tasklet_action+0x2c/0x38
[ 73.112286 ] __do_softirq+0x168/0x3b0
[ 73.115954 ] do_softirq.part.15+0x88/0x98
[ 73.119969 ] __local_bh_enable_ip+0xb0/0xb8
[ 73.124156 ] napi_workfn+0x58/0x90
[ 73.127565 ] process_one_work+0x20c/0x478
[ 73.131579 ] worker_thread+0x50/0x4f0
[ 73.135249 ] kthread+0x124/0x128
[ 73.138484 ] ret_from_fork+0x10/0x1c
Signed-off-by: Xing Song <xing.song@mediatek.com>
---
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2947,6 +2947,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80
if (!fwd_skb)
goto out;
+ fwd_skb->dev = sdata->dev;
fwd_hdr = (struct ieee80211_hdr *) fwd_skb->data;
fwd_hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_RETRY);
info = IEEE80211_SKB_CB(fwd_skb);

View File

@ -0,0 +1,26 @@
From: Xing Song <xing.song@mediatek.com>
Date: Mon, 1 Nov 2021 10:46:57 +0800
Subject: [PATCH] mac80211: do not access the IV when it was stripped
ieee80211_get_keyid() will return false value if IV has been stripped,
such as return 0 for IP/ARP frames due to LLC header, and return -EINVAL
for disassociation frames due to its length... etc. Don't try to access
it if it's not present.
Signed-off-by: Xing Song <xing.song@mediatek.com>
Link: https://lore.kernel.org/r/20211101024657.143026-1-xing.song@mediatek.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1952,7 +1952,8 @@ ieee80211_rx_h_decrypt(struct ieee80211_
int keyid = rx->sta->ptk_idx;
sta_ptk = rcu_dereference(rx->sta->ptk[keyid]);
- if (ieee80211_has_protected(fc)) {
+ if (ieee80211_has_protected(fc) &&
+ !(status->flag & RX_FLAG_IV_STRIPPED)) {
cs = rx->sta->cipher_scheme;
keyid = ieee80211_get_keyid(rx->skb, cs);

View File

@ -0,0 +1,49 @@
From: Johannes Berg <johannes.berg@intel.com>
Date: Tue, 9 Nov 2021 10:02:04 +0100
Subject: [PATCH] mac80211: fix radiotap header generation
In commit 8c89f7b3d3f2 ("mac80211: Use flex-array for radiotap header
bitmap") we accidentally pointed the position to the wrong place, so
we overwrite a present bitmap, and thus cause all kinds of trouble.
To see the issue, note that the previous code read:
pos = (void *)(it_present + 1);
The requirement now is that we need to calculate pos via it_optional,
to not trigger the compiler hardening checks, as:
pos = (void *)&rthdr->it_optional[...];
Rewriting the original expression, we get (obviously, since that just
adds "+ x - x" terms):
pos = (void *)(it_present + 1 + rthdr->it_optional - rthdr->it_optional)
and moving the "+ rthdr->it_optional" outside to be used as an array:
pos = (void *)&rthdr->it_optional[it_present + 1 - rthdr->it_optional];
The original is off by one, fix it.
Cc: stable@vger.kernel.org
Fixes: 8c89f7b3d3f2 ("mac80211: Use flex-array for radiotap header bitmap")
Reported-by: Sid Hayn <sidhayn@gmail.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Tested-by: Sid Hayn <sidhayn@gmail.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
Link: https://lore.kernel.org/r/20211109100203.c61007433ed6.I1dade57aba7de9c4f48d68249adbae62636fd98c@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -364,7 +364,7 @@ ieee80211_add_rx_radiotap_header(struct
* the compiler to think we have walked past the end of the
* struct member.
*/
- pos = (void *)&rthdr->it_optional[it_present - rthdr->it_optional];
+ pos = (void *)&rthdr->it_optional[it_present + 1 - rthdr->it_optional];
/* the order of the following fields is important */

View File

@ -8,9 +8,9 @@ PKG_LICENSE_FILES:=
PKG_SOURCE_URL:=https://github.com/openwrt/mt76
PKG_SOURCE_PROTO:=git
PKG_SOURCE_DATE:=2021-11-22
PKG_SOURCE_VERSION:=b1d0ad2e74fe8cad563556bb8781d5d92b24b690
PKG_MIRROR_HASH:=7507fdc31aa5f3d90bfffa3c7db4957c4ac16df8426e1b017b112e7e6c97735b
PKG_SOURCE_DATE:=2021-11-23
PKG_SOURCE_VERSION:=99225b985cbcab4707589f1fa313436f4bf1e368
PKG_MIRROR_HASH:=6444c7d49d778c7621b03f0f201ce41f6dc9ac00dedb29c66478360b4fd60492
PKG_MAINTAINER:=Felix Fietkau <nbd@nbd.name>
PKG_USE_NINJA:=0

View File

@ -197,12 +197,12 @@ ismounted() {
for dirname in $EXTRA_MOUNT ; do
case "$filename" in
"${dirname}/"* | "${dirname}" )
return 1
return 0
;;
esac
done
return 0
return 1
}
append_addnhosts() {

View File

@ -0,0 +1,34 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Mon, 18 Feb 2019 12:57:11 +0100
Subject: [PATCH] mesh: allow processing authentication frames in blocked state
If authentication fails repeatedly e.g. because of a weak signal, the link
can end up in blocked state. If one of the nodes tries to establish a link
again before it is unblocked on the other side, it will block the link to
that other side. The same happens on the other side when it unblocks the
link. In that scenario, the link never recovers on its own.
To fix this, allow restarting authentication even if the link is in blocked
state, but don't initiate the attempt until the blocked period is over.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -3761,15 +3761,6 @@ static void handle_auth(struct hostapd_d
seq_ctrl);
return;
}
-#ifdef CONFIG_MESH
- if ((hapd->conf->mesh & MESH_ENABLED) &&
- sta->plink_state == PLINK_BLOCKED) {
- wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR
- " is blocked - drop Authentication frame",
- MAC2STR(mgmt->sa));
- return;
- }
-#endif /* CONFIG_MESH */
#ifdef CONFIG_PASN
if (auth_alg == WLAN_AUTH_PASN &&
(sta->flags & WLAN_STA_ASSOC)) {

View File

@ -126,7 +126,7 @@
if (res == HOSTAPD_ACL_PENDING)
return;
@@ -5454,7 +5466,7 @@ static void handle_assoc(struct hostapd_
@@ -5445,7 +5457,7 @@ static void handle_assoc(struct hostapd_
int resp = WLAN_STATUS_SUCCESS;
u16 reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE;
const u8 *pos;
@ -135,7 +135,7 @@
struct sta_info *sta;
u8 *tmp = NULL;
#ifdef CONFIG_FILS
@@ -5667,6 +5679,11 @@ static void handle_assoc(struct hostapd_
@@ -5658,6 +5670,11 @@ static void handle_assoc(struct hostapd_
left = res;
}
#endif /* CONFIG_FILS */
@ -147,7 +147,7 @@
/* followed by SSID and Supported rates; and HT capabilities if 802.11n
* is used */
@@ -5765,6 +5782,13 @@ static void handle_assoc(struct hostapd_
@@ -5756,6 +5773,13 @@ static void handle_assoc(struct hostapd_
}
#endif /* CONFIG_FILS */
@ -161,7 +161,7 @@
fail:
/*
@@ -5858,6 +5882,7 @@ static void handle_disassoc(struct hosta
@@ -5849,6 +5873,7 @@ static void handle_disassoc(struct hosta
wpa_printf(MSG_DEBUG, "disassocation: STA=" MACSTR " reason_code=%d",
MAC2STR(mgmt->sa),
le_to_host16(mgmt->u.disassoc.reason_code));
@ -169,7 +169,7 @@
sta = ap_get_sta(hapd, mgmt->sa);
if (sta == NULL) {
@@ -5927,6 +5952,8 @@ static void handle_deauth(struct hostapd
@@ -5918,6 +5943,8 @@ static void handle_deauth(struct hostapd
/* Clear the PTKSA cache entries for PASN */
ptksa_cache_flush(hapd->ptksa, mgmt->sa, WPA_CIPHER_NONE);

View File

@ -0,0 +1,33 @@
--- a/src/common/wpa_ctrl.c
+++ b/src/common/wpa_ctrl.c
@@ -135,7 +135,7 @@ try_again:
return NULL;
}
tries++;
-#ifdef ANDROID
+
/* Set client socket file permissions so that bind() creates the client
* socket with these permissions and there is no need to try to change
* them with chmod() after bind() which would have potential issues with
@@ -147,7 +147,7 @@ try_again:
* operations to allow the response to go through. Those are using the
* no-deference-symlinks version to avoid races. */
fchmod(ctrl->s, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
-#endif /* ANDROID */
+
if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
sizeof(ctrl->local)) < 0) {
if (errno == EADDRINUSE && tries < 2) {
@@ -165,7 +165,11 @@ try_again:
return NULL;
}
-#ifdef ANDROID
+#ifndef ANDROID
+ /* Set group even if we do not have privileges to change owner */
+ lchown(ctrl->local.sun_path, -1, 101);
+ lchown(ctrl->local.sun_path, 101, 101);
+#else
/* Set group even if we do not have privileges to change owner */
lchown(ctrl->local.sun_path, -1, AID_WIFI);
lchown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI);

View File

@ -65,7 +65,7 @@
wpabuf_free(sta->hs20_ie);
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -4136,13 +4136,11 @@ static u16 copy_supp_rates(struct hostap
@@ -4127,13 +4127,11 @@ static u16 copy_supp_rates(struct hostap
static u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ext_capab_ie, size_t ext_capab_ie_len)
{

View File

@ -425,6 +425,7 @@ hostapd_bss_get_status(struct ubus_context *ctx, struct ubus_object *obj,
blobmsg_add_u32(&b, "freq", hapd->iface->freq);
blobmsg_add_u32(&b, "channel", ieee80211_frequency_to_channel(hapd->iface->freq));
blobmsg_add_u32(&b, "beacon_interval", hapd->iconf->beacon_int);
snprintf(phy_name, 17, "%s", hapd->iface->phy);
blobmsg_add_string(&b, "phy", phy_name);

View File

@ -12,9 +12,9 @@ PKG_RELEASE:=$(AUTORELEASE)
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL=$(PROJECT_GIT)/project/procd.git
PKG_MIRROR_HASH:=0ff42d965b6c07fe9d1731395623b78967b83bbeb85deda900dd430a2883643f
PKG_SOURCE_DATE:=2021-11-11
PKG_SOURCE_VERSION:=9d1431e1309e1acee06c016acc08cb7650b1d0af
PKG_MIRROR_HASH:=39f24aa076a61a3d570f4e5f7a3f38eb6b6e3b96a967c8be7f0dc3e3ffaeed1a
PKG_SOURCE_DATE:=2021-11-23
PKG_SOURCE_VERSION:=01ac2c4500cb0c7934640e6d2e5f99b08483bdf4
CMAKE_INSTALL:=1
PKG_LICENSE:=GPL-2.0

View File

@ -7,9 +7,19 @@
[ "has", "MINOR" ]
],
[
[ "if",
[ "eq", "DEVNAME", "null" ],
[
[ "makedev", "/dev/%DEVNAME%", "0666" ],
[ "exec", "/bin/ln", "-s", "/proc/self/fd/0", "/dev/stdin" ],
[ "exec", "/bin/ln", "-s", "/proc/self/fd/1", "/dev/stdout" ],
[ "exec", "/bin/ln", "-s", "/proc/self/fd/2", "/dev/stderr" ],
[ "return" ]
]
],
[ "if",
[ "eq", "DEVNAME",
[ "null", "full", "ptmx", "zero", "tty", "net", "random", "urandom" ]
[ "full", "ptmx", "zero", "tty", "net", "random", "urandom" ]
],
[
[ "makedev", "/dev/%DEVNAME%", "0666" ],

View File

@ -0,0 +1,131 @@
From d3bc6269e21fc474763708e79c7a118740befb94 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
Date: Tue, 26 Oct 2021 11:37:16 +0200
Subject: [PATCH] phy: bcm-ns-usb2: support updated DT binding with PHY reg
space
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Updated DT binding maps just a PHY's register space instead of the whole
DMU block. Accessing a common CRU reg is handled using syscon &
regmap.
The old binding has been deprecated and remains supported as a fallback
method.
Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
Link: https://lore.kernel.org/r/20211026093716.5567-1-zajec5@gmail.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
drivers/phy/broadcom/phy-bcm-ns-usb2.c | 52 +++++++++++++++++++++-----
1 file changed, 43 insertions(+), 9 deletions(-)
--- a/drivers/phy/broadcom/phy-bcm-ns-usb2.c
+++ b/drivers/phy/broadcom/phy-bcm-ns-usb2.c
@@ -9,17 +9,23 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
+#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
+#include <linux/regmap.h>
#include <linux/slab.h>
struct bcm_ns_usb2 {
struct device *dev;
struct clk *ref_clk;
struct phy *phy;
+ struct regmap *clkset;
+ void __iomem *base;
+
+ /* Deprecated binding */
void __iomem *dmu;
};
@@ -27,7 +33,6 @@ static int bcm_ns_usb2_phy_init(struct p
{
struct bcm_ns_usb2 *usb2 = phy_get_drvdata(phy);
struct device *dev = usb2->dev;
- void __iomem *dmu = usb2->dmu;
u32 ref_clk_rate, usb2ctl, usb_pll_ndiv, usb_pll_pdiv;
int err = 0;
@@ -44,7 +49,10 @@ static int bcm_ns_usb2_phy_init(struct p
goto err_clk_off;
}
- usb2ctl = readl(dmu + BCMA_DMU_CRU_USB2_CONTROL);
+ if (usb2->base)
+ usb2ctl = readl(usb2->base);
+ else
+ usb2ctl = readl(usb2->dmu + BCMA_DMU_CRU_USB2_CONTROL);
if (usb2ctl & BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_MASK) {
usb_pll_pdiv = usb2ctl;
@@ -58,15 +66,24 @@ static int bcm_ns_usb2_phy_init(struct p
usb_pll_ndiv = (1920000000 * usb_pll_pdiv) / ref_clk_rate;
/* Unlock DMU PLL settings with some magic value */
- writel(0x0000ea68, dmu + BCMA_DMU_CRU_CLKSET_KEY);
+ if (usb2->clkset)
+ regmap_write(usb2->clkset, 0, 0x0000ea68);
+ else
+ writel(0x0000ea68, usb2->dmu + BCMA_DMU_CRU_CLKSET_KEY);
/* Write USB 2.0 PLL control setting */
usb2ctl &= ~BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_MASK;
usb2ctl |= usb_pll_ndiv << BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_SHIFT;
- writel(usb2ctl, dmu + BCMA_DMU_CRU_USB2_CONTROL);
+ if (usb2->base)
+ writel(usb2ctl, usb2->base);
+ else
+ writel(usb2ctl, usb2->dmu + BCMA_DMU_CRU_USB2_CONTROL);
/* Lock DMU PLL settings */
- writel(0x00000000, dmu + BCMA_DMU_CRU_CLKSET_KEY);
+ if (usb2->clkset)
+ regmap_write(usb2->clkset, 0, 0x00000000);
+ else
+ writel(0x00000000, usb2->dmu + BCMA_DMU_CRU_CLKSET_KEY);
err_clk_off:
clk_disable_unprepare(usb2->ref_clk);
@@ -91,11 +108,28 @@ static int bcm_ns_usb2_probe(struct plat
return -ENOMEM;
usb2->dev = dev;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmu");
- usb2->dmu = devm_ioremap_resource(dev, res);
- if (IS_ERR(usb2->dmu)) {
- dev_err(dev, "Failed to map DMU regs\n");
- return PTR_ERR(usb2->dmu);
+ if (of_find_property(dev->of_node, "brcm,syscon-clkset", NULL)) {
+ usb2->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(usb2->base)) {
+ dev_err(dev, "Failed to map control reg\n");
+ return PTR_ERR(usb2->base);
+ }
+
+ usb2->clkset = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "brcm,syscon-clkset");
+ if (IS_ERR(usb2->clkset)) {
+ dev_err(dev, "Failed to lookup clkset regmap\n");
+ return PTR_ERR(usb2->clkset);
+ }
+ } else {
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmu");
+ usb2->dmu = devm_ioremap_resource(dev, res);
+ if (IS_ERR(usb2->dmu)) {
+ dev_err(dev, "Failed to map DMU regs\n");
+ return PTR_ERR(usb2->dmu);
+ }
+
+ dev_warn(dev, "using deprecated DT binding\n");
}
usb2->ref_clk = devm_clk_get(dev, "phy-ref-clk");

View File

@ -0,0 +1,52 @@
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
Date: Tue, 23 Nov 2021 09:55:06 +0100
Subject: [PATCH] ARM: dts: BCM5301X: use non-deprecated USB 2.0 PHY binding
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The new binding covers a single reg and uses syscon to reference shared
register.
References: 55b9b741712d ("dt-bindings: phy: brcm,ns-usb2-phy: bind just a PHY block")
Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
---
arch/arm/boot/dts/bcm5301x.dtsi | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
--- a/arch/arm/boot/dts/bcm5301x.dtsi
+++ b/arch/arm/boot/dts/bcm5301x.dtsi
@@ -148,15 +148,6 @@
};
};
- usb2_phy: usb2-phy@1800c000 {
- compatible = "brcm,ns-usb2-phy";
- reg = <0x1800c000 0x1000>;
- reg-names = "dmu";
- #phy-cells = <0>;
- clocks = <&genpll BCM_NSP_GENPLL_USB_PHY_REF_CLK>;
- clock-names = "phy-ref-clk";
- };
-
axi@18000000 {
compatible = "brcm,bus-axi";
reg = <0x18000000 0x1000>;
@@ -450,7 +441,16 @@
"sata1", "sata2";
};
- syscon@180 {
+ usb2_phy: phy@164 {
+ compatible = "brcm,ns-usb2-phy";
+ reg = <0x164 0x4>;
+ brcm,syscon-clkset = <&cru_clkset>;
+ clocks = <&genpll BCM_NSP_GENPLL_USB_PHY_REF_CLK>;
+ clock-names = "phy-ref-clk";
+ #phy-cells = <0>;
+ };
+
+ cru_clkset: syscon@180 {
compatible = "brcm,cru-clkset", "syscon";
reg = <0x180 0x4>;
};

View File

@ -0,0 +1,62 @@
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
Date: Tue, 23 Nov 2021 13:13:05 +0100
Subject: [PATCH] ARM: dts: BCM5301X: Switch back to old clock nodes names
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
First of all using the same node name prefix resulted in trying to
register 2 clocks under the same "clock-controller" name:
[ 0.000000] __clk_core_init: clk clock-controller already initialized
[ 0.000000] ------------[ cut here ]------------
[ 0.000000] WARNING: CPU: 0 PID: 0 at drivers/clk/bcm/clk-iproc-pll.c:802 iproc_pll_clk_setup+0x4c8/0x4f4
[ 0.000000] Modules linked in:
[ 0.000000] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 5.10.80 #0
[ 0.000000] Hardware name: BCM5301X
[ 0.000000] [<c0108410>] (unwind_backtrace) from [<c0104bc4>] (show_stack+0x10/0x14)
[ 0.000000] [<c0104bc4>] (show_stack) from [<c03dca28>] (dump_stack+0x94/0xa8)
[ 0.000000] [<c03dca28>] (dump_stack) from [<c0118440>] (__warn+0xb8/0x114)
[ 0.000000] [<c0118440>] (__warn) from [<c0118504>] (warn_slowpath_fmt+0x68/0x78)
[ 0.000000] [<c0118504>] (warn_slowpath_fmt) from [<c043281c>] (iproc_pll_clk_setup+0x4c8/0x4f4)
[ 0.000000] [<c043281c>] (iproc_pll_clk_setup) from [<c0818c04>] (nsp_genpll_clk_init+0x30/0x38)
[ 0.000000] [<c0818c04>] (nsp_genpll_clk_init) from [<c0818634>] (of_clk_init+0x118/0x1f8)
[ 0.000000] [<c0818634>] (of_clk_init) from [<c08039b0>] (time_init+0x24/0x30)
[ 0.000000] [<c08039b0>] (time_init) from [<c0800d14>] (start_kernel+0x398/0x50c)
[ 0.000000] [<c0800d14>] (start_kernel) from [<00000000>] (0x0)
[ 0.000000] ---[ end trace fe236bfe9559ee50 ]---
Secondly using any other names than "lcpll0" and "genpll" breaks output
clocks:
$ cat /sys/kernel/debug/clk/usbclk/clk_rate
0
For some reason iproc_clk_recalc_rate() gets called with "parent_rate"
argument 0 whenever clocks aren't named "lcpll0" and "genpll".
Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
---
arch/arm/boot/dts/bcm5301x.dtsi | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
--- a/arch/arm/boot/dts/bcm5301x.dtsi
+++ b/arch/arm/boot/dts/bcm5301x.dtsi
@@ -421,7 +421,7 @@
#address-cells = <1>;
#size-cells = <1>;
- lcpll0: clock-controller@100 {
+ lcpll0: lcpll0@100 {
#clock-cells = <1>;
compatible = "brcm,nsp-lcpll0";
reg = <0x100 0x14>;
@@ -430,7 +430,7 @@
"sdio", "ddr_phy";
};
- genpll: clock-controller@140 {
+ genpll: genpll@140 {
#clock-cells = <1>;
compatible = "brcm,nsp-genpll";
reg = <0x140 0x24>;

View File

@ -160,6 +160,7 @@ CONFIG_PINCTRL=y
CONFIG_POWER_RESET=y
CONFIG_POWER_RESET_SYSCON=y
CONFIG_RATIONAL=y
CONFIG_REALTEK_OTTO_WDT=y
CONFIG_REALTEK_PHY=y
CONFIG_REALTEK_SOC_PHY=y
CONFIG_REGMAP=y
@ -190,5 +191,6 @@ CONFIG_TIMER_PROBE=y
CONFIG_TINY_SRCU=y
CONFIG_USE_GENERIC_EARLY_PRINTK_8250=y
CONFIG_USE_OF=y
CONFIG_WATCHDOG_CORE=y
CONFIG_ZLIB_DEFLATE=y
CONFIG_ZLIB_INFLATE=y

View File

@ -8,10 +8,6 @@
/ {
compatible = "realtek,rtl838x-soc";
chosen {
bootargs = "console=ttyS0,115200";
};
memory@0 {
device_type = "memory";
reg = <0x0 0x8000000>;

View File

@ -13,10 +13,6 @@
led-upgrade = &led_sys;
};
chosen {
bootargs = "console=ttyS0,115200";
};
memory@0 {
device_type = "memory";
reg = <0x0 0x8000000>;

View File

@ -16,10 +16,6 @@
led-upgrade = &led_sys;
};
chosen {
bootargs = "console=ttyS0,115200";
};
memory@0 {
device_type = "memory";
reg = <0x0 0x8000000>;

View File

@ -16,10 +16,6 @@
led-upgrade = &led_power;
};
chosen {
bootargs = "console=ttyS0,115200";
};
memory@0 {
device_type = "memory";
reg = <0x0 0x8000000>;

View File

@ -13,10 +13,6 @@
led-upgrade = &led_power;
};
chosen {
bootargs = "console=ttyS0,115200";
};
memory@0 {
device_type = "memory";
reg = <0x0 0x8000000>;

View File

@ -9,10 +9,6 @@
compatible = "inaba,aml2-17gp", "realtek,rtl838x-soc";
model = "INABA Abaniact AML2-17GP";
chosen {
bootargs = "console=ttyS0,115200";
};
memory@0 {
device_type = "memory";
reg = <0x0 0x8000000>;

View File

@ -65,7 +65,13 @@
};
chosen {
bootargs = "console=ttyS0,38400";
bootargs = "console=ttyS0,115200";
};
lx_clk: lx_clk {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <200000000>;
};
cpuintc: cpuintc {
@ -116,7 +122,7 @@
compatible = "ns16550a";
reg = <0x2000 0x100>;
clock-frequency = <200000000>;
clocks = <&lx_clk>;
interrupt-parent = <&intc>;
interrupts = <31>;
@ -134,7 +140,7 @@
compatible = "ns16550a";
reg = <0x2100 0x100>;
clock-frequency = <200000000>;
clocks = <&lx_clk>;
interrupt-parent = <&intc>;
interrupts = <30>;
@ -147,6 +153,20 @@
status = "disabled";
};
watchdog0: watchdog@3150 {
compatible = "realtek,rtl8380-wdt";
reg = <0x3150 0xc>;
realtek,reset-mode = "soc";
clocks = <&lx_clk>;
timeout-sec = <30>;
interrupt-parent = <&intc>;
interrupt-names = "phase1", "phase2";
interrupts = <19>, <18>;
};
gpio0: gpio-controller@3500 {
compatible = "realtek,rtl8380-gpio", "realtek,otto-gpio";
reg = <0x3500 0x20>;

View File

@ -70,7 +70,7 @@
};
chosen {
bootargs = "console=ttyS0,38400";
bootargs = "console=ttyS0,115200";
};
cpuintc: cpuintc {
@ -80,11 +80,10 @@
interrupt-controller;
};
osc: oscillator {
lx_clk: lx_clk {
compatible = "fixed-clock";
#clock-cells = <1>;
#clock-cells = <0>;
clock-frequency = <175000000>;
clock-output-names = "osc";
};
soc: soc {
@ -110,7 +109,9 @@
<10 &cpuintc 1>, /* TC3 */
<9 &cpuintc 1>, /* TC2 */
<8 &cpuintc 1>, /* TC1 */
<7 &cpuintc 5>; /* TC0 */
<7 &cpuintc 5>, /* TC0 */
<6 &cpuintc 5>, /* WDT_IP2 */
<5 &cpuintc 4>; /* WDT_IP1 */
};
timer: timer@3200 {
@ -119,7 +120,7 @@
interrupt-parent = <&intc>;
interrupts = <8>;
interrupt-names = "ostimer";
clocks = <&osc 0>;
clocks = <&lx_clk>;
};
spi0: spi@1200 {
@ -134,7 +135,7 @@
compatible = "ns16550a";
reg = <0x2000 0x100>;
clock-frequency = <175000000>;
clocks = <&lx_clk>;
interrupt-parent = <&intc>;
interrupts = <30>;
@ -149,7 +150,7 @@
compatible = "ns16550a";
reg = <0x2100 0x100>;
clock-frequency = <175000000>;
clocks = <&lx_clk>;
interrupt-parent = <&intc>;
interrupts = <31>;
@ -162,6 +163,20 @@
status = "disabled";
};
watchdog0: watchdog@3260 {
compatible = "realtek,rtl9300-wdt";
reg = <0x3260 0xc>;
realtek,reset-mode = "soc";
clocks = <&lx_clk>;
timeout-sec = <30>;
interrupt-parent = <&intc>;
interrupt-names = "phase1", "phase2";
interrupts = <5>, <6>;
};
gpio0: gpio-controller@3500 {
compatible = "realtek,rtl8380-gpio", "realtek,otto-gpio";
reg = <0x3500 0x20>;

View File

@ -20,7 +20,6 @@
#include <asm/addrspace.h>
#include <asm/io.h>
#include <asm/bootinfo.h>
#include <asm/reboot.h>
#include <asm/time.h>
#include <asm/prom.h>
#include <asm/smp-ops.h>
@ -29,100 +28,20 @@
extern struct rtl83xx_soc_info soc_info;
u32 pll_reset_value;
static void rtl838x_restart(char *command)
{
u32 pll = sw_r32(RTL838X_PLL_CML_CTRL);
pr_info("System restart.\n");
pr_info("PLL control register: %x, applying reset value %x\n",
pll, pll_reset_value);
sw_w32(3, RTL838X_INT_RW_CTRL);
sw_w32(pll_reset_value, RTL838X_PLL_CML_CTRL);
sw_w32(0, RTL838X_INT_RW_CTRL);
/* Reset Global Control1 Register */
sw_w32(1, RTL838X_RST_GLB_CTRL_1);
}
static void rtl839x_restart(char *command)
{
/* SoC reset vector (in flash memory): on RTL839x platform preferred way to reset */
void (*f)(void) = (void *) 0xbfc00000;
pr_info("System restart.\n");
/* Reset SoC */
sw_w32(0xFFFFFFFF, RTL839X_RST_GLB_CTRL);
/* and call reset vector */
f();
/* If this fails, halt the CPU */
while
(1);
}
static void rtl930x_restart(char *command)
{
pr_info("System restart.\n");
sw_w32(0x1, RTL930X_RST_GLB_CTRL_0);
while
(1);
}
static void rtl931x_restart(char *command)
{
u32 v;
pr_info("System restart.\n");
sw_w32(1, RTL931X_RST_GLB_CTRL);
v = sw_r32(RTL931X_RST_GLB_CTRL);
sw_w32(0x101, RTL931X_RST_GLB_CTRL);
msleep(15);
sw_w32(v, RTL931X_RST_GLB_CTRL);
msleep(15);
sw_w32(0x101, RTL931X_RST_GLB_CTRL);
}
static void rtl838x_halt(void)
{
pr_info("System halted.\n");
while
(1);
}
static void __init rtl838x_setup(void)
{
pr_info("Registering _machine_restart\n");
_machine_restart = rtl838x_restart;
_machine_halt = rtl838x_halt;
/* This PLL value needs to be restored before a reset and will then be
* preserved over a SoC reset. A wrong value prevents the SoC from
* connecting to the SPI flash controller at boot and reading the
* reset routine */
pll_reset_value = sw_r32(RTL838X_PLL_CML_CTRL);
/* Setup System LED. Bit 15 then allows to toggle it */
sw_w32_mask(0, 3 << 16, RTL838X_LED_GLB_CTRL);
}
static void __init rtl839x_setup(void)
{
pr_info("Registering _machine_restart\n");
_machine_restart = rtl839x_restart;
_machine_halt = rtl838x_halt;
/* Setup System LED. Bit 14 of RTL839X_LED_GLB_CTRL then allows to toggle it */
sw_w32_mask(0, 3 << 15, RTL839X_LED_GLB_CTRL);
}
static void __init rtl930x_setup(void)
{
pr_info("Registering _machine_restart\n");
_machine_restart = rtl930x_restart;
_machine_halt = rtl838x_halt;
if (soc_info.id == 0x9302)
sw_w32_mask(0, 3 << 13, RTL9302_LED_GLB_CTRL);
else
@ -131,9 +50,6 @@ static void __init rtl930x_setup(void)
static void __init rtl931x_setup(void)
{
pr_info("Registering _machine_restart\n");
_machine_restart = rtl931x_restart;
_machine_halt = rtl838x_halt;
sw_w32_mask(0, 3 << 12, RTL931X_LED_GLB_CTRL);
}
@ -142,7 +58,6 @@ void __init plat_mem_setup(void)
void *dtb;
set_io_port_base(KSEG1);
_machine_restart = rtl838x_restart;
if (fw_passed_dtb) /* UHI interface */
dtb = (void *)fw_passed_dtb;

View File

@ -630,6 +630,7 @@ static const struct rtl838x_reg rtl931x_reg = {
static void rtl838x_hw_reset(struct rtl838x_eth_priv *priv)
{
u32 int_saved, nbuf;
u32 reset_mask;
int i, pos;
pr_info("RESETTING %x, CPU_PORT %d\n", priv->family_id, priv->cpu_port);
@ -662,15 +663,17 @@ static void rtl838x_hw_reset(struct rtl838x_eth_priv *priv)
sw_w32(0xffffffff, priv->r->dma_if_intr_sts);
}
/* Reset NIC */
/* Reset NIC (SW_NIC_RST) and queues (SW_Q_RST) */
if (priv->family_id == RTL9300_FAMILY_ID || priv->family_id == RTL9310_FAMILY_ID)
sw_w32(0x4, priv->r->rst_glb_ctrl);
reset_mask = 0x6;
else
sw_w32(0x8, priv->r->rst_glb_ctrl);
reset_mask = 0xc;
sw_w32(reset_mask, priv->r->rst_glb_ctrl);
do { /* Wait for reset of NIC and Queues done */
udelay(20);
} while (sw_r32(priv->r->rst_glb_ctrl) & 0xc);
} while (sw_r32(priv->r->rst_glb_ctrl) & reset_mask);
mdelay(100);
/* Setup Head of Line */

View File

@ -0,0 +1,456 @@
From 2dbf0c6e0eebf523008c15794434d2d1a9b1260e Mon Sep 17 00:00:00 2001
Message-Id: <2dbf0c6e0eebf523008c15794434d2d1a9b1260e.1636018117.git.sander@svanheule.net>
In-Reply-To: <cover.1636018117.git.sander@svanheule.net>
References: <cover.1636018117.git.sander@svanheule.net>
From: Sander Vanheule <sander@svanheule.net>
Date: Sun, 3 Oct 2021 09:25:27 +0200
Subject: [PATCH v3 2/2] watchdog: Add Realtek Otto watchdog timer
Realtek MIPS SoCs (platform name Otto) have a watchdog timer with
pretimeout notifitication support. The WDT can (partially) hard reset,
or soft reset the SoC.
This driver implements all features as described in the devicetree
binding, except the phase2 interrupt, and also functions as a restart
handler. The cpu reset mode is considered to be a "warm" restart, since
this mode does not reset all peripherals. Being an embedded system
though, the "cpu" and "software" modes will still cause the bootloader
to run on restart.
It is not known how a forced system reset can be disabled on the
supported platforms. This means that the phase2 interrupt will only fire
at the same time as reset, so implementing phase2 is of little use.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
MAINTAINERS | 7 +
drivers/watchdog/Kconfig | 13 +
drivers/watchdog/Makefile | 1 +
drivers/watchdog/realtek_otto_wdt.c | 361 ++++++++++++++++++++++++++++
4 files changed, 382 insertions(+)
create mode 100644 drivers/watchdog/realtek_otto_wdt.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 2c9070aeba2a..54c8f788d3e5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15991,6 +15991,13 @@ S: Maintained
F: include/sound/rt*.h
F: sound/soc/codecs/rt*
+REALTEK OTTO WATCHDOG
+M: Sander Vanheule <sander@svanheule.net>
+L: linux-watchdog@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/watchdog/realtek,otto-wdt.yaml
+F: driver/watchdog/realtek_otto_wdt.c
+
REALTEK RTL83xx SMI DSA ROUTER CHIPS
M: Linus Walleij <linus.walleij@linaro.org>
S: Maintained
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index bf59faeb3de1..d308e13a9aa1 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -954,6 +954,19 @@ config RTD119X_WATCHDOG
Say Y here to include support for the watchdog timer in
Realtek RTD1295 SoCs.
+config REALTEK_OTTO_WDT
+ tristate "Realtek Otto MIPS watchdog support"
+ depends on MACH_REALTEK_RTL || COMPILE_TEST
+ depends on COMMON_CLK
+ select WATCHDOG_CORE
+ default MACH_REALTEK_RTL
+ help
+ Say Y here to include support for the watchdog timer on Realtek
+ RTL838x, RTL839x, RTL930x SoCs. This watchdog has pretimeout
+ notifications and system reset on timeout.
+
+ When built as a module this will be called realtek_otto_wdt.
+
config SPRD_WATCHDOG
tristate "Spreadtrum watchdog support"
depends on ARCH_SPRD || COMPILE_TEST
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 1bd2d6f37c53..a8dccf819163 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -171,6 +171,7 @@ obj-$(CONFIG_IMGPDC_WDT) += imgpdc_wdt.o
obj-$(CONFIG_MT7621_WDT) += mt7621_wdt.o
obj-$(CONFIG_PIC32_WDT) += pic32-wdt.o
obj-$(CONFIG_PIC32_DMT) += pic32-dmt.o
+obj-$(CONFIG_REALTEK_OTTO_WDT) += realtek_otto_wdt.o
# PARISC Architecture
diff --git a/drivers/watchdog/realtek_otto_wdt.c b/drivers/watchdog/realtek_otto_wdt.c
new file mode 100644
index 000000000000..48bc8dfefc25
--- /dev/null
+++ b/drivers/watchdog/realtek_otto_wdt.c
@@ -0,0 +1,361 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Realtek Otto MIPS platform watchdog
+ *
+ * Watchdog timer that will reset the system after timeout, using the selected
+ * reset mode.
+ *
+ * Counter scaling and timeouts:
+ * - Base prescale of (2 << 25), providing tick duration T_0: 168ms @ 200MHz
+ * - PRESCALE: logarithmic prescaler adding a factor of {1, 2, 4, 8}
+ * - Phase 1: Times out after (PHASE1 + 1) × PRESCALE × T_0
+ * Generates an interrupt, WDT cannot be stopped after phase 1
+ * - Phase 2: starts after phase 1, times out after (PHASE2 + 1) × PRESCALE × T_0
+ * Resets the system according to RST_MODE
+ */
+
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/math.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/reboot.h>
+#include <linux/watchdog.h>
+
+#define OTTO_WDT_REG_CNTR 0x0
+#define OTTO_WDT_CNTR_PING BIT(31)
+
+#define OTTO_WDT_REG_INTR 0x4
+#define OTTO_WDT_INTR_PHASE_1 BIT(31)
+#define OTTO_WDT_INTR_PHASE_2 BIT(30)
+
+#define OTTO_WDT_REG_CTRL 0x8
+#define OTTO_WDT_CTRL_ENABLE BIT(31)
+#define OTTO_WDT_CTRL_PRESCALE GENMASK(30, 29)
+#define OTTO_WDT_CTRL_PHASE1 GENMASK(26, 22)
+#define OTTO_WDT_CTRL_PHASE2 GENMASK(19, 15)
+#define OTTO_WDT_CTRL_RST_MODE GENMASK(1, 0)
+#define OTTO_WDT_MODE_SOC 0
+#define OTTO_WDT_MODE_CPU 1
+#define OTTO_WDT_MODE_SOFTWARE 2
+#define OTTO_WDT_CTRL_DEFAULT OTTO_WDT_MODE_CPU
+
+#define OTTO_WDT_PRESCALE_MAX 3
+
+/*
+ * One higher than the max values contained in PHASE{1,2}, since a value of 0
+ * corresponds to one tick.
+ */
+#define OTTO_WDT_PHASE_TICKS_MAX 32
+
+/*
+ * The maximum reset delay is actually 2×32 ticks, but that would require large
+ * pretimeout values for timeouts longer than 32 ticks. Limit the maximum timeout
+ * to 32 + 1 to ensure small pretimeout values can be configured as expected.
+ */
+#define OTTO_WDT_TIMEOUT_TICKS_MAX (OTTO_WDT_PHASE_TICKS_MAX + 1)
+
+struct otto_wdt_ctrl {
+ struct watchdog_device wdev;
+ struct device *dev;
+ void __iomem *base;
+ struct clk *clk;
+ int irq_phase1;
+};
+
+static int otto_wdt_start(struct watchdog_device *wdev)
+{
+ struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+ u32 v;
+
+ v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
+ v |= OTTO_WDT_CTRL_ENABLE;
+ iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
+
+ return 0;
+}
+
+static int otto_wdt_stop(struct watchdog_device *wdev)
+{
+ struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+ u32 v;
+
+ v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
+ v &= ~OTTO_WDT_CTRL_ENABLE;
+ iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
+
+ return 0;
+}
+
+static int otto_wdt_ping(struct watchdog_device *wdev)
+{
+ struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+
+ iowrite32(OTTO_WDT_CNTR_PING, ctrl->base + OTTO_WDT_REG_CNTR);
+
+ return 0;
+}
+
+static int otto_wdt_tick_ms(struct otto_wdt_ctrl *ctrl, int prescale)
+{
+ unsigned int rate_khz = clk_get_rate(ctrl->clk) / 1000;
+
+ if (!rate_khz)
+ return 0;
+
+ return DIV_ROUND_CLOSEST(1 << (25 + prescale), rate_khz);
+}
+
+/*
+ * The timer asserts the PHASE1/PHASE2 IRQs when the number of ticks exceeds
+ * the value stored in those fields. This means each phase will run for at least
+ * one tick, so small values need to be clamped to correctly reflect the timeout.
+ */
+static inline unsigned int div_round_ticks(unsigned int val, unsigned int tick_duration,
+ unsigned int min_ticks)
+{
+ return max(min_ticks, DIV_ROUND_UP(val, tick_duration));
+}
+
+static int otto_wdt_determine_timeouts(struct watchdog_device *wdev, unsigned int timeout,
+ unsigned int pretimeout)
+{
+ struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+ unsigned int pretimeout_ms = pretimeout * 1000;
+ unsigned int timeout_ms = timeout * 1000;
+ unsigned int prescale_next = 0;
+ unsigned int phase1_ticks;
+ unsigned int phase2_ticks;
+ unsigned int total_ticks;
+ unsigned int prescale;
+ unsigned int tick_ms;
+ u32 v;
+
+ do {
+ prescale = prescale_next;
+ if (prescale > OTTO_WDT_PRESCALE_MAX)
+ return -EINVAL;
+
+ tick_ms = otto_wdt_tick_ms(ctrl, prescale);
+ total_ticks = div_round_ticks(timeout_ms, tick_ms, 2);
+ phase1_ticks = div_round_ticks(timeout_ms - pretimeout_ms, tick_ms, 1);
+ phase2_ticks = total_ticks - phase1_ticks;
+
+ prescale_next++;
+ } while (phase1_ticks > OTTO_WDT_PHASE_TICKS_MAX
+ || phase2_ticks > OTTO_WDT_PHASE_TICKS_MAX);
+
+ v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
+
+ v &= ~(OTTO_WDT_CTRL_PRESCALE | OTTO_WDT_CTRL_PHASE1 | OTTO_WDT_CTRL_PHASE2);
+ v |= FIELD_PREP(OTTO_WDT_CTRL_PHASE1, phase1_ticks - 1);
+ v |= FIELD_PREP(OTTO_WDT_CTRL_PHASE2, phase2_ticks - 1);
+ v |= FIELD_PREP(OTTO_WDT_CTRL_PRESCALE, prescale);
+
+ iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
+
+ timeout_ms = total_ticks * tick_ms;
+ ctrl->wdev.timeout = timeout_ms / 1000;
+
+ pretimeout_ms = phase2_ticks * tick_ms;
+ ctrl->wdev.pretimeout = pretimeout_ms / 1000;
+
+ return 0;
+}
+
+static int otto_wdt_set_timeout(struct watchdog_device *wdev, unsigned int val)
+{
+ return otto_wdt_determine_timeouts(wdev, val, min(wdev->pretimeout, val - 1));
+}
+
+static int otto_wdt_set_pretimeout(struct watchdog_device *wdev, unsigned int val)
+{
+ return otto_wdt_determine_timeouts(wdev, wdev->timeout, val);
+}
+
+static int otto_wdt_restart(struct watchdog_device *wdev, unsigned long reboot_mode,
+ void *data)
+{
+ struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+ u32 reset_mode;
+ u32 v;
+
+ disable_irq(ctrl->irq_phase1);
+
+ switch (reboot_mode) {
+ case REBOOT_SOFT:
+ reset_mode = OTTO_WDT_MODE_SOFTWARE;
+ break;
+ case REBOOT_WARM:
+ reset_mode = OTTO_WDT_MODE_CPU;
+ break;
+ default:
+ reset_mode = OTTO_WDT_MODE_SOC;
+ break;
+ }
+
+ /* Configure for shortest timeout and wait for reset to occur */
+ v = FIELD_PREP(OTTO_WDT_CTRL_RST_MODE, reset_mode) | OTTO_WDT_CTRL_ENABLE;
+ iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
+
+ mdelay(3 * otto_wdt_tick_ms(ctrl, 0));
+
+ return 0;
+}
+
+static irqreturn_t otto_wdt_phase1_isr(int irq, void *dev_id)
+{
+ struct otto_wdt_ctrl *ctrl = dev_id;
+
+ iowrite32(OTTO_WDT_INTR_PHASE_1, ctrl->base + OTTO_WDT_REG_INTR);
+ dev_crit(ctrl->dev, "phase 1 timeout\n");
+ watchdog_notify_pretimeout(&ctrl->wdev);
+
+ return IRQ_HANDLED;
+}
+
+static const struct watchdog_ops otto_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = otto_wdt_start,
+ .stop = otto_wdt_stop,
+ .ping = otto_wdt_ping,
+ .set_timeout = otto_wdt_set_timeout,
+ .set_pretimeout = otto_wdt_set_pretimeout,
+ .restart = otto_wdt_restart,
+};
+
+static const struct watchdog_info otto_wdt_info = {
+ .identity = "Realtek Otto watchdog timer",
+ .options = WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE |
+ WDIOF_SETTIMEOUT |
+ WDIOF_PRETIMEOUT,
+};
+
+static int otto_wdt_probe_reset_mode(struct otto_wdt_ctrl *ctrl)
+{
+ static const char *mode_property = "realtek,reset-mode";
+ const struct fwnode_handle *node = ctrl->dev->fwnode;
+ int mode_count;
+ u32 mode;
+ u32 v;
+
+ if (!node)
+ return -ENXIO;
+
+ mode_count = fwnode_property_string_array_count(node, mode_property);
+ if (mode_count < 0)
+ return mode_count;
+ else if (mode_count == 0)
+ return 0;
+ else if (mode_count != 1)
+ return -EINVAL;
+
+ if (fwnode_property_match_string(node, mode_property, "soc") == 0)
+ mode = OTTO_WDT_MODE_SOC;
+ else if (fwnode_property_match_string(node, mode_property, "cpu") == 0)
+ mode = OTTO_WDT_MODE_CPU;
+ else if (fwnode_property_match_string(node, mode_property, "software") == 0)
+ mode = OTTO_WDT_MODE_SOFTWARE;
+ else
+ return -EINVAL;
+
+ v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
+ v &= ~OTTO_WDT_CTRL_RST_MODE;
+ v |= FIELD_PREP(OTTO_WDT_CTRL_RST_MODE, mode);
+ iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
+
+ return 0;
+}
+
+static int otto_wdt_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct otto_wdt_ctrl *ctrl;
+ unsigned int max_tick_ms;
+ int ret;
+
+ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl)
+ return -ENOMEM;
+
+ ctrl->dev = dev;
+ ctrl->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(ctrl->base))
+ return PTR_ERR(ctrl->base);
+
+ /* Clear any old interrupts and reset initial state */
+ iowrite32(OTTO_WDT_INTR_PHASE_1 | OTTO_WDT_INTR_PHASE_2,
+ ctrl->base + OTTO_WDT_REG_INTR);
+ iowrite32(OTTO_WDT_CTRL_DEFAULT, ctrl->base + OTTO_WDT_REG_CTRL);
+
+ ctrl->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(ctrl->clk))
+ return dev_err_probe(dev, PTR_ERR(ctrl->clk), "Failed to get clock\n");
+
+ ctrl->irq_phase1 = platform_get_irq_byname(pdev, "phase1");
+ if (ctrl->irq_phase1 < 0)
+ return dev_err_probe(dev, ctrl->irq_phase1, "phase1 IRQ not found\n");
+
+ ret = devm_request_irq(dev, ctrl->irq_phase1, otto_wdt_phase1_isr, 0,
+ "realtek-otto-wdt", ctrl);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get IRQ for phase1\n");
+
+ ret = otto_wdt_probe_reset_mode(ctrl);
+ if (ret)
+ return dev_err_probe(dev, ret, "Invalid reset mode specified\n");
+
+ ctrl->wdev.parent = dev;
+ ctrl->wdev.info = &otto_wdt_info;
+ ctrl->wdev.ops = &otto_wdt_ops;
+
+ /*
+ * Since pretimeout cannot be disabled, min. timeout is twice the
+ * subsystem resolution. max. timeout is ca. 43s at a bus clock of 200MHz.
+ */
+ ctrl->wdev.min_timeout = 2;
+ max_tick_ms = otto_wdt_tick_ms(ctrl, OTTO_WDT_PRESCALE_MAX);
+ ctrl->wdev.max_hw_heartbeat_ms = max_tick_ms * OTTO_WDT_TIMEOUT_TICKS_MAX;
+ ctrl->wdev.timeout = min(30U, ctrl->wdev.max_hw_heartbeat_ms / 1000);
+
+ watchdog_set_drvdata(&ctrl->wdev, ctrl);
+ watchdog_init_timeout(&ctrl->wdev, 0, dev);
+ watchdog_stop_on_reboot(&ctrl->wdev);
+ watchdog_set_restart_priority(&ctrl->wdev, 128);
+
+ ret = otto_wdt_determine_timeouts(&ctrl->wdev, ctrl->wdev.timeout, 1);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to set timeout\n");
+
+ return devm_watchdog_register_device(dev, &ctrl->wdev);
+}
+
+static const struct of_device_id otto_wdt_ids[] = {
+ { .compatible = "realtek,rtl8380-wdt" },
+ { .compatible = "realtek,rtl8390-wdt" },
+ { .compatible = "realtek,rtl9300-wdt" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, otto_wdt_ids);
+
+static struct platform_driver otto_wdt_driver = {
+ .probe = otto_wdt_probe,
+ .driver = {
+ .name = "realtek-otto-watchdog",
+ .of_match_table = otto_wdt_ids,
+ },
+};
+module_platform_driver(otto_wdt_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sander Vanheule <sander@svanheule.net>");
+MODULE_DESCRIPTION("Realtek Otto watchdog timer driver");
--
2.31.1

View File

@ -0,0 +1,17 @@
Index: linux-5.10.76/drivers/watchdog/Kconfig
===================================================================
--- linux-5.10.76.orig/drivers/watchdog/Kconfig
+++ linux-5.10.76/drivers/watchdog/Kconfig
@@ -997,10 +997,10 @@ config RTD119X_WATCHDOG
config REALTEK_OTTO_WDT
tristate "Realtek Otto MIPS watchdog support"
- depends on MACH_REALTEK_RTL || COMPILE_TEST
+ depends on RTL838X
depends on COMMON_CLK
select WATCHDOG_CORE
- default MACH_REALTEK_RTL
+ default RTL838X
help
Say Y here to include support for the watchdog timer on Realtek
RTL838x, RTL839x, RTL930x SoCs. This watchdog has pretimeout

View File

@ -0,0 +1,13 @@
Index: linux-5.10.72/drivers/watchdog/realtek_otto_wdt.c
===================================================================
--- linux-5.10.72.orig/drivers/watchdog/realtek_otto_wdt.c
+++ linux-5.10.72/drivers/watchdog/realtek_otto_wdt.c
@@ -21,7 +21,7 @@
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h>
-#include <linux/math.h>
+#include <linux/kernel.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>

View File

@ -14,9 +14,10 @@ LLVM_VERSION := $(shell cat $(STAGING_DIR_HOST)/llvm-bpf/.llvm-version)
LLVM_BPF_PREFIX := llvm-bpf-$(LLVM_VERSION).$(HOST_OS)-$(HOST_ARCH)
LLVM_TAR := $(BIN_DIR)/$(LLVM_BPF_PREFIX).tar.xz
$(LLVM_TAR): $(HOST_STAMP_INSTALLED)
$(LLVM_TAR): $(STAGING_DIR_HOST)/llvm-bpf/.llvm-version
tar -C $(STAGING_DIR_HOST) \
-I '$(STAGING_DIR_HOST)/bin/xz -7e -T$(if $(filter 1,$(NPROC)),2,0)' \
$(if $(SOURCE_DATE_EPOCH),--mtime="@$(SOURCE_DATE_EPOCH)") \
-cf $@.tmp llvm-bpf $(LLVM_BPF_PREFIX)
mv $@.tmp $@