Recently the rtw88 driver has gained locking support for the "slow" bus
types (USB, SDIO) as part of USB support. Thanks to everyone who helped
make this happen!
Based on the USB work (especially the locking part and various
bugfixes) this series adds support for SDIO based cards. It's the
result of a collaboration between Jernej and myself. Neither of us has
access to the rtw88 datasheets. All of our work is based on studying
the RTL8822BS and RTL8822CS vendor drivers and trial and error.
Jernej and myself have tested this with RTL8822BS and RTL8822CS cards.
Other users have confirmed that RTL8821CS support is working as well.
RTL8723DS may also work (we tried our best to handle rtw_chip_wcpu_11n
where needed) but has not been tested at this point.
Jernej's results with a RTL8822BS:
- Main functionality works
- Had a case where no traffic got across the link until he issued a
scan
My results with a RTL8822CS:
- 2.4GHz and 5GHz bands are both working
- TX throughput on a 5GHz network is between 50 Mbit/s and 90 Mbit/s
- RX throughput on a 5GHz network is at 19 Mbit/s
- Sometimes there are frequent reconnects (once every 1-5 minutes)
after the link has been up for a long time (multiple hours). Today
I was unable to reproduce this though (I only had reconnect in 8
hours).
Why is this an RFC?
- It needs a through review especially by the rtw88 maintainers
- It's not clear to me how the "mmc: sdio" patch will be merged (will
Ulf take this or can we merge it thorugh the rtw88/linux wireless
driver tree?)
- Any comments / debugging hints on the reconnect / no traffic issues
(see above) are welcome
- My understanding is that there's a discussion about the rtw88 Kconfig
symbols. We're adding four new ones within this series. It's not
clear to me what the conclusion is on this topic though.
- As with most patches: testing is very welcome. If things are working
fine then a Tested-by is appreciated (with some details about the
card, throughput, ...). If something doesn't work for you: please
still report back so we can investigate that problem!
Jernej Skrabec (2):
rtw88: ps: Increase LEAVE_LPS_TRY_CNT for SDIO based chipsets
rtw88: Add support for the SDIO based RTL8822BS chipset
Martin Blumenstingl (17):
rtw88: mac: Use existing interface mask macros in rtw_pwr_seq_parser()
rtw88: pci: Change type of rtw_hw_queue_mapping() and ac_to_hwq to
enum
rtw88: pci: Change queue datatype from u8 to enum rtw_tx_queue_type
rtw88: Move enum rtw_tx_queue_type mapping code to tx.{c,h}
mmc: sdio: add Realtek SDIO vendor ID and various wifi device IDs
rtw88: rtw8821c: Add support for parsing the RTL8821CS (SDIO) efuse
rtw88: rtw8822b: Add support for parsing the RTL8822BS (SDIO) efuse
rtw88: rtw8822c: Add support for parsing the RTL8822CS (SDIO) efuse
rtw88: hci: Add an optional power_switch() callback to rtw_hci_ops
rtw88: mac: Add support for the SDIO HCI in rtw_pwr_seq_parser()
rtw88: mac: Add support for the SDIO HCI in the TX/page table setup
rtw88: sdio: Add HCI implementation for SDIO based chipsets
rtw88: mac: Add support for SDIO specifics in the power on sequence
rtw88: main: Add the rpwm_addr and cpwm_addr for SDIO based chipsets
rtw88: main: Reserve 8 bytes of extra TX headroom for SDIO based cards
rtw88: Add support for the SDIO based RTL8822CS chipset
rtw88: Add support for the SDIO based RTL8821CS chipset
drivers/net/wireless/realtek/rtw88/Kconfig | 36 +
drivers/net/wireless/realtek/rtw88/Makefile | 12 +
drivers/net/wireless/realtek/rtw88/debug.h | 1 +
drivers/net/wireless/realtek/rtw88/hci.h | 8 +
drivers/net/wireless/realtek/rtw88/mac.c | 62 +-
drivers/net/wireless/realtek/rtw88/mac.h | 1 -
drivers/net/wireless/realtek/rtw88/main.c | 9 +-
drivers/net/wireless/realtek/rtw88/pci.c | 50 +-
drivers/net/wireless/realtek/rtw88/ps.h | 2 +-
drivers/net/wireless/realtek/rtw88/reg.h | 10 +
drivers/net/wireless/realtek/rtw88/rtw8821c.c | 9 +
drivers/net/wireless/realtek/rtw88/rtw8821c.h | 6 +
.../net/wireless/realtek/rtw88/rtw8821cs.c | 34 +
drivers/net/wireless/realtek/rtw88/rtw8822b.c | 10 +
drivers/net/wireless/realtek/rtw88/rtw8822b.h | 6 +
.../net/wireless/realtek/rtw88/rtw8822bs.c | 34 +
drivers/net/wireless/realtek/rtw88/rtw8822c.c | 9 +
drivers/net/wireless/realtek/rtw88/rtw8822c.h | 6 +
.../net/wireless/realtek/rtw88/rtw8822cs.c | 34 +
drivers/net/wireless/realtek/rtw88/sdio.c | 1242 +++++++++++++++++
drivers/net/wireless/realtek/rtw88/sdio.h | 175 +++
drivers/net/wireless/realtek/rtw88/tx.c | 41 +
drivers/net/wireless/realtek/rtw88/tx.h | 3 +
include/linux/mmc/sdio_ids.h | 9 +
24 files changed, 1763 insertions(+), 46 deletions(-)
create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8821cs.c
create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8822bs.c
create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8822cs.c
create mode 100644 drivers/net/wireless/realtek/rtw88/sdio.c
create mode 100644 drivers/net/wireless/realtek/rtw88/sdio.h
--
2.39.0
rtw_hw_queue_mapping() and ac_to_hwq[] hold values of type enum
rtw_tx_queue_type. Change their types to reflect this to make it easier
to understand this part of the code.
While here, also change the array to be static const as it is not
supposed to be modified at runtime.
Signed-off-by: Martin Blumenstingl <[email protected]>
---
drivers/net/wireless/realtek/rtw88/pci.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c
index 0975d27240e4..45ce7e624c03 100644
--- a/drivers/net/wireless/realtek/rtw88/pci.c
+++ b/drivers/net/wireless/realtek/rtw88/pci.c
@@ -669,7 +669,7 @@ static void rtw_pci_deep_ps(struct rtw_dev *rtwdev, bool enter)
spin_unlock_bh(&rtwpci->irq_lock);
}
-static u8 ac_to_hwq[] = {
+static const enum rtw_tx_queue_type ac_to_hwq[] = {
[IEEE80211_AC_VO] = RTW_TX_QUEUE_VO,
[IEEE80211_AC_VI] = RTW_TX_QUEUE_VI,
[IEEE80211_AC_BE] = RTW_TX_QUEUE_BE,
@@ -678,12 +678,12 @@ static u8 ac_to_hwq[] = {
static_assert(ARRAY_SIZE(ac_to_hwq) == IEEE80211_NUM_ACS);
-static u8 rtw_hw_queue_mapping(struct sk_buff *skb)
+static enum rtw_tx_queue_type rtw_hw_queue_mapping(struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
__le16 fc = hdr->frame_control;
u8 q_mapping = skb_get_queue_mapping(skb);
- u8 queue;
+ enum rtw_tx_queue_type queue;
if (unlikely(ieee80211_is_beacon(fc)))
queue = RTW_TX_QUEUE_BCN;
--
2.39.0
Add the SDIO vendor ID for Realtek and some device IDs extracted from
their GPL vendor driver. This will be useful in the future when the
rtw88 driver gains support for these chips.
Signed-off-by: Martin Blumenstingl <[email protected]>
---
include/linux/mmc/sdio_ids.h | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h
index 74f9d9a6d330..bba39d4565da 100644
--- a/include/linux/mmc/sdio_ids.h
+++ b/include/linux/mmc/sdio_ids.h
@@ -111,6 +111,15 @@
#define SDIO_VENDOR_ID_MICROCHIP_WILC 0x0296
#define SDIO_DEVICE_ID_MICROCHIP_WILC1000 0x5347
+#define SDIO_VENDOR_ID_REALTEK 0x024c
+#define SDIO_DEVICE_ID_REALTEK_RTW8723BS 0xb723
+#define SDIO_DEVICE_ID_REALTEK_RTW8723DS 0xd723
+#define SDIO_DEVICE_ID_REALTEK_RTW8821BS 0xb821
+#define SDIO_DEVICE_ID_REALTEK_RTW8821CS 0xc821
+#define SDIO_DEVICE_ID_REALTEK_RTW8821DS 0xd821
+#define SDIO_DEVICE_ID_REALTEK_RTW8822BS 0xb822
+#define SDIO_DEVICE_ID_REALTEK_RTW8822CS 0xc822
+
#define SDIO_VENDOR_ID_SIANO 0x039a
#define SDIO_DEVICE_ID_SIANO_NOVA_B0 0x0201
#define SDIO_DEVICE_ID_SIANO_NICE 0x0202
--
2.39.0
This code is not specific to the PCIe bus type but can be re-used by USB
and SDIO bus types. Move it to tx.{c,h} to avoid code-duplication in the
future. While here, add checking of the ac argument in
rtw_tx_ac_to_hwq() so we're not accessing entries beyond the end of the
array.
Signed-off-by: Martin Blumenstingl <[email protected]>
---
drivers/net/wireless/realtek/rtw88/pci.c | 35 ++------------------
drivers/net/wireless/realtek/rtw88/tx.c | 41 ++++++++++++++++++++++++
drivers/net/wireless/realtek/rtw88/tx.h | 3 ++
3 files changed, 46 insertions(+), 33 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c
index 5492107fc85b..b4bd831c9845 100644
--- a/drivers/net/wireless/realtek/rtw88/pci.c
+++ b/drivers/net/wireless/realtek/rtw88/pci.c
@@ -670,37 +670,6 @@ static void rtw_pci_deep_ps(struct rtw_dev *rtwdev, bool enter)
spin_unlock_bh(&rtwpci->irq_lock);
}
-static const enum rtw_tx_queue_type ac_to_hwq[] = {
- [IEEE80211_AC_VO] = RTW_TX_QUEUE_VO,
- [IEEE80211_AC_VI] = RTW_TX_QUEUE_VI,
- [IEEE80211_AC_BE] = RTW_TX_QUEUE_BE,
- [IEEE80211_AC_BK] = RTW_TX_QUEUE_BK,
-};
-
-static_assert(ARRAY_SIZE(ac_to_hwq) == IEEE80211_NUM_ACS);
-
-static enum rtw_tx_queue_type rtw_hw_queue_mapping(struct sk_buff *skb)
-{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- __le16 fc = hdr->frame_control;
- u8 q_mapping = skb_get_queue_mapping(skb);
- enum rtw_tx_queue_type queue;
-
- if (unlikely(ieee80211_is_beacon(fc)))
- queue = RTW_TX_QUEUE_BCN;
- else if (unlikely(ieee80211_is_mgmt(fc) || ieee80211_is_ctl(fc)))
- queue = RTW_TX_QUEUE_MGMT;
- else if (is_broadcast_ether_addr(hdr->addr1) ||
- is_multicast_ether_addr(hdr->addr1))
- queue = RTW_TX_QUEUE_HI0;
- else if (WARN_ON_ONCE(q_mapping >= ARRAY_SIZE(ac_to_hwq)))
- queue = ac_to_hwq[IEEE80211_AC_BE];
- else
- queue = ac_to_hwq[q_mapping];
-
- return queue;
-}
-
static void rtw_pci_release_rsvd_page(struct rtw_pci *rtwpci,
struct rtw_pci_tx_ring *ring)
{
@@ -798,7 +767,7 @@ static void rtw_pci_flush_queues(struct rtw_dev *rtwdev, u32 queues, bool drop)
} else {
for (i = 0; i < rtwdev->hw->queues; i++)
if (queues & BIT(i))
- pci_queues |= BIT(ac_to_hwq[i]);
+ pci_queues |= BIT(rtw_tx_ac_to_hwq(i));
}
__rtw_pci_flush_queues(rtwdev, pci_queues, drop);
@@ -952,7 +921,7 @@ static int rtw_pci_tx_write(struct rtw_dev *rtwdev,
struct rtw_tx_pkt_info *pkt_info,
struct sk_buff *skb)
{
- enum rtw_tx_queue_type queue = rtw_hw_queue_mapping(skb);
+ enum rtw_tx_queue_type queue = rtw_tx_queue_mapping(skb);
struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv;
struct rtw_pci_tx_ring *ring;
int ret;
diff --git a/drivers/net/wireless/realtek/rtw88/tx.c b/drivers/net/wireless/realtek/rtw88/tx.c
index ab39245e9c2f..bb5c7492c98b 100644
--- a/drivers/net/wireless/realtek/rtw88/tx.c
+++ b/drivers/net/wireless/realtek/rtw88/tx.c
@@ -682,3 +682,44 @@ void rtw_txq_cleanup(struct rtw_dev *rtwdev, struct ieee80211_txq *txq)
list_del_init(&rtwtxq->list);
spin_unlock_bh(&rtwdev->txq_lock);
}
+
+static const enum rtw_tx_queue_type ac_to_hwq[] = {
+ [IEEE80211_AC_VO] = RTW_TX_QUEUE_VO,
+ [IEEE80211_AC_VI] = RTW_TX_QUEUE_VI,
+ [IEEE80211_AC_BE] = RTW_TX_QUEUE_BE,
+ [IEEE80211_AC_BK] = RTW_TX_QUEUE_BK,
+};
+
+static_assert(ARRAY_SIZE(ac_to_hwq) == IEEE80211_NUM_ACS);
+
+enum rtw_tx_queue_type rtw_tx_ac_to_hwq(enum ieee80211_ac_numbers ac)
+{
+ if (WARN_ON(unlikely(ac >= IEEE80211_NUM_ACS)))
+ return RTW_TX_QUEUE_BE;
+
+ return ac_to_hwq[ac];
+}
+EXPORT_SYMBOL(rtw_tx_ac_to_hwq);
+
+enum rtw_tx_queue_type rtw_tx_queue_mapping(struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ __le16 fc = hdr->frame_control;
+ u8 q_mapping = skb_get_queue_mapping(skb);
+ enum rtw_tx_queue_type queue;
+
+ if (unlikely(ieee80211_is_beacon(fc)))
+ queue = RTW_TX_QUEUE_BCN;
+ else if (unlikely(ieee80211_is_mgmt(fc) || ieee80211_is_ctl(fc)))
+ queue = RTW_TX_QUEUE_MGMT;
+ else if (is_broadcast_ether_addr(hdr->addr1) ||
+ is_multicast_ether_addr(hdr->addr1))
+ queue = RTW_TX_QUEUE_HI0;
+ else if (WARN_ON_ONCE(q_mapping >= ARRAY_SIZE(ac_to_hwq)))
+ queue = ac_to_hwq[IEEE80211_AC_BE];
+ else
+ queue = ac_to_hwq[q_mapping];
+
+ return queue;
+}
+EXPORT_SYMBOL(rtw_tx_queue_mapping);
diff --git a/drivers/net/wireless/realtek/rtw88/tx.h b/drivers/net/wireless/realtek/rtw88/tx.h
index a2f3ac326041..197d5868c8ad 100644
--- a/drivers/net/wireless/realtek/rtw88/tx.h
+++ b/drivers/net/wireless/realtek/rtw88/tx.h
@@ -131,6 +131,9 @@ rtw_tx_write_data_h2c_get(struct rtw_dev *rtwdev,
struct rtw_tx_pkt_info *pkt_info,
u8 *buf, u32 size);
+enum rtw_tx_queue_type rtw_tx_ac_to_hwq(enum ieee80211_ac_numbers ac);
+enum rtw_tx_queue_type rtw_tx_queue_mapping(struct sk_buff *skb);
+
static inline
void fill_txdesc_checksum_common(u8 *txdesc, size_t words)
{
--
2.39.0
The efuse of the SDIO RTL8822BS chip has only one known member: the mac
address is at offset 0x11a. Add a struct rtw8822bs_efuse describing this
and use it for copying the mac address when the SDIO bus is used.
Signed-off-by: Martin Blumenstingl <[email protected]>
---
drivers/net/wireless/realtek/rtw88/rtw8822b.c | 10 ++++++++++
drivers/net/wireless/realtek/rtw88/rtw8822b.h | 6 ++++++
2 files changed, 16 insertions(+)
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822b.c b/drivers/net/wireless/realtek/rtw88/rtw8822b.c
index 74dfb89b2c94..4ed5b98fab23 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822b.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.c
@@ -26,10 +26,17 @@ static void rtw8822be_efuse_parsing(struct rtw_efuse *efuse,
ether_addr_copy(efuse->addr, map->e.mac_addr);
}
+static void rtw8822bs_efuse_parsing(struct rtw_efuse *efuse,
+ struct rtw8822b_efuse *map)
+{
+ ether_addr_copy(efuse->addr, map->s.mac_addr);
+}
+
static void rtw8822bu_efuse_parsing(struct rtw_efuse *efuse,
struct rtw8822b_efuse *map)
{
ether_addr_copy(efuse->addr, map->u.mac_addr);
+
}
static int rtw8822b_read_efuse(struct rtw_dev *rtwdev, u8 *log_map)
@@ -62,6 +69,9 @@ static int rtw8822b_read_efuse(struct rtw_dev *rtwdev, u8 *log_map)
case RTW_HCI_TYPE_PCIE:
rtw8822be_efuse_parsing(efuse, map);
break;
+ case RTW_HCI_TYPE_SDIO:
+ rtw8822bs_efuse_parsing(efuse, map);
+ break;
case RTW_HCI_TYPE_USB:
rtw8822bu_efuse_parsing(efuse, map);
break;
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822b.h b/drivers/net/wireless/realtek/rtw88/rtw8822b.h
index 01d3644e0c94..f84bfb6b0df9 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822b.h
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.h
@@ -65,6 +65,11 @@ struct rtw8822be_efuse {
u8 res7;
};
+struct rtw8822bs_efuse {
+ u8 res4[0x4a]; /* 0xd0 */
+ u8 mac_addr[ETH_ALEN]; /* 0x11a */
+};
+
struct rtw8822b_efuse {
__le16 rtl_id;
u8 res0[0x0e];
@@ -94,6 +99,7 @@ struct rtw8822b_efuse {
union {
struct rtw8822bu_efuse u;
struct rtw8822be_efuse e;
+ struct rtw8822bs_efuse s;
};
};
--
2.39.0
rtw_pwr_seq_parser() needs to know about the HCI bus interface mask for
the SDIO bus so it can parse the chip state change sequences.
Signed-off-by: Martin Blumenstingl <[email protected]>
---
drivers/net/wireless/realtek/rtw88/mac.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/net/wireless/realtek/rtw88/mac.c b/drivers/net/wireless/realtek/rtw88/mac.c
index bf1291902661..c04938691add 100644
--- a/drivers/net/wireless/realtek/rtw88/mac.c
+++ b/drivers/net/wireless/realtek/rtw88/mac.c
@@ -222,6 +222,9 @@ static int rtw_pwr_seq_parser(struct rtw_dev *rtwdev,
case RTW_HCI_TYPE_USB:
intf_mask = RTW_PWR_INTF_USB_MSK;
break;
+ case RTW_HCI_TYPE_SDIO:
+ intf_mask = RTW_PWR_INTF_SDIO_MSK;
+ break;
default:
return -EINVAL;
}
--
2.39.0
This makes it easier to understand which values are allowed for the
"queue" variable.
Signed-off-by: Martin Blumenstingl <[email protected]>
---
drivers/net/wireless/realtek/rtw88/pci.c | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c
index 45ce7e624c03..5492107fc85b 100644
--- a/drivers/net/wireless/realtek/rtw88/pci.c
+++ b/drivers/net/wireless/realtek/rtw88/pci.c
@@ -30,7 +30,8 @@ static u32 rtw_pci_tx_queue_idx_addr[] = {
[RTW_TX_QUEUE_H2C] = RTK_PCI_TXBD_IDX_H2CQ,
};
-static u8 rtw_pci_get_tx_qsel(struct sk_buff *skb, u8 queue)
+static u8 rtw_pci_get_tx_qsel(struct sk_buff *skb,
+ enum rtw_tx_queue_type queue)
{
switch (queue) {
case RTW_TX_QUEUE_BCN:
@@ -542,7 +543,7 @@ static int rtw_pci_setup(struct rtw_dev *rtwdev)
static void rtw_pci_dma_release(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci)
{
struct rtw_pci_tx_ring *tx_ring;
- u8 queue;
+ enum rtw_tx_queue_type queue;
rtw_pci_reset_trx_ring(rtwdev);
for (queue = 0; queue < RTK_MAX_TX_QUEUE_NUM; queue++) {
@@ -608,8 +609,8 @@ static void rtw_pci_deep_ps_enter(struct rtw_dev *rtwdev)
{
struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv;
struct rtw_pci_tx_ring *tx_ring;
+ enum rtw_tx_queue_type queue;
bool tx_empty = true;
- u8 queue;
if (rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_TX_WAKE))
goto enter_deep_ps;
@@ -803,7 +804,8 @@ static void rtw_pci_flush_queues(struct rtw_dev *rtwdev, u32 queues, bool drop)
__rtw_pci_flush_queues(rtwdev, pci_queues, drop);
}
-static void rtw_pci_tx_kick_off_queue(struct rtw_dev *rtwdev, u8 queue)
+static void rtw_pci_tx_kick_off_queue(struct rtw_dev *rtwdev,
+ enum rtw_tx_queue_type queue)
{
struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv;
struct rtw_pci_tx_ring *ring;
@@ -822,7 +824,7 @@ static void rtw_pci_tx_kick_off_queue(struct rtw_dev *rtwdev, u8 queue)
static void rtw_pci_tx_kick_off(struct rtw_dev *rtwdev)
{
struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv;
- u8 queue;
+ enum rtw_tx_queue_type queue;
for (queue = 0; queue < RTK_MAX_TX_QUEUE_NUM; queue++)
if (test_and_clear_bit(queue, rtwpci->tx_queued))
@@ -831,7 +833,8 @@ static void rtw_pci_tx_kick_off(struct rtw_dev *rtwdev)
static int rtw_pci_tx_write_data(struct rtw_dev *rtwdev,
struct rtw_tx_pkt_info *pkt_info,
- struct sk_buff *skb, u8 queue)
+ struct sk_buff *skb,
+ enum rtw_tx_queue_type queue)
{
struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv;
const struct rtw_chip_info *chip = rtwdev->chip;
@@ -949,9 +952,9 @@ static int rtw_pci_tx_write(struct rtw_dev *rtwdev,
struct rtw_tx_pkt_info *pkt_info,
struct sk_buff *skb)
{
+ enum rtw_tx_queue_type queue = rtw_hw_queue_mapping(skb);
struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv;
struct rtw_pci_tx_ring *ring;
- u8 queue = rtw_hw_queue_mapping(skb);
int ret;
ret = rtw_pci_tx_write_data(rtwdev, pkt_info, skb, queue);
--
2.39.0
The efuse of the SDIO RTL8821CS chip has only one known member: the mac
address is at offset 0x11a. Add a struct rtw8821cs_efuse describing this
and use it for copying the mac address when the SDIO bus is used.
Signed-off-by: Martin Blumenstingl <[email protected]>
---
drivers/net/wireless/realtek/rtw88/rtw8821c.c | 9 +++++++++
drivers/net/wireless/realtek/rtw88/rtw8821c.h | 6 ++++++
2 files changed, 15 insertions(+)
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821c.c b/drivers/net/wireless/realtek/rtw88/rtw8821c.c
index 17f800f6efbd..dd01b22f9770 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8821c.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8821c.c
@@ -26,6 +26,12 @@ static void rtw8821ce_efuse_parsing(struct rtw_efuse *efuse,
ether_addr_copy(efuse->addr, map->e.mac_addr);
}
+static void rtw8821cs_efuse_parsing(struct rtw_efuse *efuse,
+ struct rtw8821c_efuse *map)
+{
+ ether_addr_copy(efuse->addr, map->s.mac_addr);
+}
+
static void rtw8821cu_efuse_parsing(struct rtw_efuse *efuse,
struct rtw8821c_efuse *map)
{
@@ -74,6 +80,9 @@ static int rtw8821c_read_efuse(struct rtw_dev *rtwdev, u8 *log_map)
case RTW_HCI_TYPE_PCIE:
rtw8821ce_efuse_parsing(efuse, map);
break;
+ case RTW_HCI_TYPE_SDIO:
+ rtw8821cs_efuse_parsing(efuse, map);
+ break;
case RTW_HCI_TYPE_USB:
rtw8821cu_efuse_parsing(efuse, map);
break;
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821c.h b/drivers/net/wireless/realtek/rtw88/rtw8821c.h
index 1c81260f3a54..1deea54575b5 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8821c.h
+++ b/drivers/net/wireless/realtek/rtw88/rtw8821c.h
@@ -65,6 +65,11 @@ struct rtw8821ce_efuse {
u8 res7;
};
+struct rtw8821cs_efuse {
+ u8 res4[0x4a]; /* 0xd0 */
+ u8 mac_addr[ETH_ALEN]; /* 0x11a */
+};
+
struct rtw8821c_efuse {
__le16 rtl_id;
u8 res0[0x0e];
@@ -93,6 +98,7 @@ struct rtw8821c_efuse {
u8 res[3];
union {
struct rtw8821ce_efuse e;
+ struct rtw8821cs_efuse s;
struct rtw8821cu_efuse u;
};
};
--
2.39.0
Replace the magic numbers for the intf_mask with their existing
RTW_PWR_INTF_PCI_MSK and RTW_PWR_INTF_USB_MSK macros to make the code
easier to understand.
Signed-off-by: Martin Blumenstingl <[email protected]>
---
drivers/net/wireless/realtek/rtw88/mac.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw88/mac.c b/drivers/net/wireless/realtek/rtw88/mac.c
index 98777f294945..4e5c194aac29 100644
--- a/drivers/net/wireless/realtek/rtw88/mac.c
+++ b/drivers/net/wireless/realtek/rtw88/mac.c
@@ -217,10 +217,10 @@ static int rtw_pwr_seq_parser(struct rtw_dev *rtwdev,
cut_mask = cut_version_to_mask(cut);
switch (rtw_hci_type(rtwdev)) {
case RTW_HCI_TYPE_PCIE:
- intf_mask = BIT(2);
+ intf_mask = RTW_PWR_INTF_PCI_MSK;
break;
case RTW_HCI_TYPE_USB:
- intf_mask = BIT(1);
+ intf_mask = RTW_PWR_INTF_USB_MSK;
break;
default:
return -EINVAL;
--
2.39.0
Add a sub-driver for SDIO based chipsets which implements the following
functionality:
- register accessors for 8, 16 and 32 bits for all states of the card
(including usage of 4x 8 bit access for one 32 bit buffer if the card
is not fully powered on yet - or if it's fully powered on then 1x 32
bit access is used)
- checking whether there's space in the TX FIFO queue to transmit data
- transfers from the host to the device for actual network traffic,
reserved pages (for firmware download) and H2C (host-to-card)
transfers
- receiving data from the device
- deep power saving state
The transmit path is optimized so DMA-capable SDIO host controllers can
directly use the buffers provided because the buffer's physical
addresses are 8 byte aligned.
The receive path is prepared to support RX aggregation where the
chipset combines multiple MAC frames into one bigger buffer to reduce
SDIO transfer overhead.
Co-developed-by: Jernej Skrabec <[email protected]>
Signed-off-by: Jernej Skrabec <[email protected]>
Signed-off-by: Martin Blumenstingl <[email protected]>
---
drivers/net/wireless/realtek/rtw88/Kconfig | 3 +
drivers/net/wireless/realtek/rtw88/Makefile | 3 +
drivers/net/wireless/realtek/rtw88/debug.h | 1 +
drivers/net/wireless/realtek/rtw88/mac.h | 1 -
drivers/net/wireless/realtek/rtw88/reg.h | 10 +
drivers/net/wireless/realtek/rtw88/sdio.c | 1242 +++++++++++++++++++
drivers/net/wireless/realtek/rtw88/sdio.h | 175 +++
7 files changed, 1434 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/wireless/realtek/rtw88/sdio.c
create mode 100644 drivers/net/wireless/realtek/rtw88/sdio.h
diff --git a/drivers/net/wireless/realtek/rtw88/Kconfig b/drivers/net/wireless/realtek/rtw88/Kconfig
index 651ab56d9c6b..cdf9cb478ee2 100644
--- a/drivers/net/wireless/realtek/rtw88/Kconfig
+++ b/drivers/net/wireless/realtek/rtw88/Kconfig
@@ -16,6 +16,9 @@ config RTW88_CORE
config RTW88_PCI
tristate
+config RTW88_SDIO
+ tristate
+
config RTW88_USB
tristate
diff --git a/drivers/net/wireless/realtek/rtw88/Makefile b/drivers/net/wireless/realtek/rtw88/Makefile
index fe7293ee87b4..892cad60ba31 100644
--- a/drivers/net/wireless/realtek/rtw88/Makefile
+++ b/drivers/net/wireless/realtek/rtw88/Makefile
@@ -59,5 +59,8 @@ rtw88_8821cu-objs := rtw8821cu.o
obj-$(CONFIG_RTW88_PCI) += rtw88_pci.o
rtw88_pci-objs := pci.o
+obj-$(CONFIG_RTW88_SDIO) += rtw88_sdio.o
+rtw88_sdio-objs := sdio.o
+
obj-$(CONFIG_RTW88_USB) += rtw88_usb.o
rtw88_usb-objs := usb.o
diff --git a/drivers/net/wireless/realtek/rtw88/debug.h b/drivers/net/wireless/realtek/rtw88/debug.h
index 066792dd96af..a9149c6c2b48 100644
--- a/drivers/net/wireless/realtek/rtw88/debug.h
+++ b/drivers/net/wireless/realtek/rtw88/debug.h
@@ -24,6 +24,7 @@ enum rtw_debug_mask {
RTW_DBG_ADAPTIVITY = 0x00008000,
RTW_DBG_HW_SCAN = 0x00010000,
RTW_DBG_STATE = 0x00020000,
+ RTW_DBG_SDIO = 0x00040000,
RTW_DBG_ALL = 0xffffffff
};
diff --git a/drivers/net/wireless/realtek/rtw88/mac.h b/drivers/net/wireless/realtek/rtw88/mac.h
index 3172aa5ac4de..58c3dccc14bb 100644
--- a/drivers/net/wireless/realtek/rtw88/mac.h
+++ b/drivers/net/wireless/realtek/rtw88/mac.h
@@ -7,7 +7,6 @@
#define RTW_HW_PORT_NUM 5
#define cut_version_to_mask(cut) (0x1 << ((cut) + 1))
-#define SDIO_LOCAL_OFFSET 0x10250000
#define DDMA_POLLING_COUNT 1000
#define C2H_PKT_BUF 256
#define REPORT_BUF 128
diff --git a/drivers/net/wireless/realtek/rtw88/reg.h b/drivers/net/wireless/realtek/rtw88/reg.h
index 8852b24d6c2a..4ea2c6b491e9 100644
--- a/drivers/net/wireless/realtek/rtw88/reg.h
+++ b/drivers/net/wireless/realtek/rtw88/reg.h
@@ -185,6 +185,9 @@
(((x) & BIT_MASK_TXDMA_VIQ_MAP) << BIT_SHIFT_TXDMA_VIQ_MAP)
#define REG_TXDMA_PQ_MAP 0x010C
#define BIT_RXDMA_ARBBW_EN BIT(0)
+#define BIT_RXSHFT_EN BIT(1)
+#define BIT_RXDMA_AGG_EN BIT(2)
+#define BIT_TXDMA_BW_EN BIT(3)
#define BIT_SHIFT_TXDMA_BEQ_MAP 8
#define BIT_MASK_TXDMA_BEQ_MAP 0x3
#define BIT_TXDMA_BEQ_MAP(x) \
@@ -283,10 +286,17 @@
#define REG_H2C_TAIL 0x0248
#define REG_H2C_READ_ADDR 0x024C
#define REG_H2C_INFO 0x0254
+#define REG_RXDMA_AGG_PG_TH 0x0280
+#define BIT_SHIFT_DMA_AGG_TO_V1 8
+#define BIT_EN_PRE_CALC BIT(29)
#define REG_RXPKT_NUM 0x0284
#define BIT_RXDMA_REQ BIT(19)
#define BIT_RW_RELEASE BIT(18)
#define BIT_RXDMA_IDLE BIT(17)
+#define REG_RXDMA_STATUS 0x0288
+#define REG_RXDMA_DPR 0x028C
+#define REG_RXDMA_MODE 0x0290
+#define BIT_DMA_MODE BIT(1)
#define REG_RXPKTNUM 0x02B0
#define REG_INT_MIG 0x0304
diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c
new file mode 100644
index 000000000000..0e637ff2293f
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw88/sdio.c
@@ -0,0 +1,1242 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Copyright (C) 2021 Martin Blumenstingl <[email protected]>
+ * Copyright (C) 2021 Jernej Skrabec <[email protected]>
+ *
+ * Based on rtw88/pci.c:
+ * Copyright(c) 2018-2019 Realtek Corporation
+ */
+
+#include <linux/module.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sdio_func.h>
+#include "sdio.h"
+#include "reg.h"
+#include "tx.h"
+#include "rx.h"
+#include "fw.h"
+#include "ps.h"
+#include "debug.h"
+
+#define RTW_SDIO_INDIRECT_RW_RETRIES 50
+
+static bool rtw_sdio_is_bus_addr(u32 addr)
+{
+ return (addr & RTW_SDIO_BUS_MSK) != 0;
+}
+
+static bool rtw_sdio_bus_claim_needed(struct rtw_sdio *rtwsdio)
+{
+ return !rtwsdio->irq_thread ||
+ rtwsdio->irq_thread != current;
+}
+
+static u32 rtw_sdio_to_bus_offset(struct rtw_dev *rtwdev, u32 addr)
+{
+ switch (addr & RTW_SDIO_BUS_MSK) {
+ case WLAN_IOREG_OFFSET:
+ addr &= WLAN_IOREG_REG_MSK;
+ addr |= FIELD_PREP(REG_SDIO_CMD_ADDR_MSK,
+ REG_SDIO_CMD_ADDR_MAC_REG);
+ break;
+ case SDIO_LOCAL_OFFSET:
+ addr &= SDIO_LOCAL_REG_MSK;
+ addr |= FIELD_PREP(REG_SDIO_CMD_ADDR_MSK,
+ REG_SDIO_CMD_ADDR_SDIO_REG);
+ break;
+ default:
+ rtw_warn(rtwdev, "Cannot convert addr 0x%08x to bus offset",
+ addr);
+ }
+
+ return addr;
+}
+
+static void rtw_sdio_writel(struct rtw_sdio *rtwsdio, u32 val,
+ u32 addr, int *ret)
+{
+ u8 buf[4];
+ int i;
+
+ if (!(addr & 3) && rtwsdio->is_powered_on) {
+ sdio_writel(rtwsdio->sdio_func, val, addr, ret);
+ return;
+ }
+
+ *(__le32 *)buf = cpu_to_le32(val);
+
+ for (i = 0; i < 4; i++) {
+ sdio_writeb(rtwsdio->sdio_func, buf[i], addr + i, ret);
+ if (*ret)
+ return;
+ }
+}
+
+static u32 rtw_sdio_readl(struct rtw_sdio *rtwsdio, u32 addr, int *ret)
+{
+ u8 buf[4];
+ int i;
+
+ if (!(addr & 3) && rtwsdio->is_powered_on)
+ return sdio_readl(rtwsdio->sdio_func, addr, ret);
+
+ for (i = 0; i < 4; i++) {
+ buf[i] = sdio_readb(rtwsdio->sdio_func, addr + i, ret);
+ if (*ret)
+ return 0;
+ }
+
+ return le32_to_cpu(*(__le32 *)buf);
+}
+
+static u8 rtw_sdio_read_indirect8(struct rtw_dev *rtwdev, u32 addr, int *ret)
+{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ u32 reg_cfg, reg_data;
+ int retry;
+ u8 tmp;
+
+ reg_cfg = rtw_sdio_to_bus_offset(rtwdev, REG_SDIO_INDIRECT_REG_CFG);
+ reg_data = rtw_sdio_to_bus_offset(rtwdev, REG_SDIO_INDIRECT_REG_DATA);
+
+ rtw_sdio_writel(rtwsdio, BIT(19) | addr, reg_cfg, ret);
+ if (*ret)
+ return 0;
+
+ for (retry = 0; retry < RTW_SDIO_INDIRECT_RW_RETRIES; retry++) {
+ tmp = sdio_readb(rtwsdio->sdio_func, reg_cfg + 2, ret);
+ if (!ret && tmp & BIT(4))
+ break;
+ }
+
+ if (*ret)
+ return 0;
+
+ return sdio_readb(rtwsdio->sdio_func, reg_data, ret);
+}
+
+static int rtw_sdio_read_indirect_bytes(struct rtw_dev *rtwdev, u32 addr,
+ u8 *buf, int count)
+{
+ int i, ret;
+
+ for (i = 0; i < count; i++) {
+ buf[0] = rtw_sdio_read_indirect8(rtwdev, addr + i, &ret);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static u32 rtw_sdio_read_indirect32(struct rtw_dev *rtwdev, u32 addr, int *ret)
+{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ u32 reg_cfg, reg_data, val;
+ int retry;
+
+ reg_cfg = rtw_sdio_to_bus_offset(rtwdev, REG_SDIO_INDIRECT_REG_CFG);
+ reg_data = rtw_sdio_to_bus_offset(rtwdev, REG_SDIO_INDIRECT_REG_DATA);
+
+ rtw_sdio_writel(rtwsdio, BIT(19) | BIT(17) | addr, reg_cfg, ret);
+ if (*ret)
+ return 0;
+
+ for (retry = 0; retry < RTW_SDIO_INDIRECT_RW_RETRIES; retry++) {
+ val = sdio_readb(rtwsdio->sdio_func, reg_cfg + 2, ret);
+ if (!ret && (val & BIT(4)))
+ break;
+ }
+
+ if (!*ret)
+ val = rtw_sdio_readl(rtwsdio, reg_data, ret);
+
+ return val;
+}
+
+static u8 rtw_sdio_read8(struct rtw_dev *rtwdev, u32 addr)
+{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ bool direct, bus_claim;
+ int ret;
+ u8 val;
+
+ bus_claim = rtw_sdio_bus_claim_needed(rtwsdio);
+ direct = rtw_sdio_is_bus_addr(addr);
+
+ if (bus_claim)
+ sdio_claim_host(rtwsdio->sdio_func);
+
+ if (direct) {
+ addr = rtw_sdio_to_bus_offset(rtwdev, addr);
+ val = sdio_readb(rtwsdio->sdio_func, addr, &ret);
+ } else {
+ val = rtw_sdio_read_indirect8(rtwdev, addr, &ret);
+ }
+
+ if (bus_claim)
+ sdio_release_host(rtwsdio->sdio_func);
+
+ if (ret)
+ rtw_warn(rtwdev, "sdio read8 failed (0x%x): %d", addr, ret);
+
+ return val;
+}
+
+static u16 rtw_sdio_read16(struct rtw_dev *rtwdev, u32 addr)
+{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ bool direct, bus_claim;
+ u8 buf[2];
+ int ret;
+ u16 val;
+
+ bus_claim = rtw_sdio_bus_claim_needed(rtwsdio);
+ direct = rtw_sdio_is_bus_addr(addr);
+
+ if (bus_claim)
+ sdio_claim_host(rtwsdio->sdio_func);
+
+ if (direct) {
+ addr = rtw_sdio_to_bus_offset(rtwdev, addr);
+ buf[0] = sdio_readb(rtwsdio->sdio_func, addr, &ret);
+ if (!ret)
+ buf[1] = sdio_readb(rtwsdio->sdio_func, addr + 1, &ret);
+ val = le16_to_cpu(*(__le16 *)buf);
+ } else if (addr & 1) {
+ ret = rtw_sdio_read_indirect_bytes(rtwdev, addr, buf, 2);
+ val = le16_to_cpu(*(__le16 *)buf);
+ } else {
+ val = rtw_sdio_read_indirect32(rtwdev, addr, &ret);
+ }
+
+ if (bus_claim)
+ sdio_release_host(rtwsdio->sdio_func);
+
+ if (ret)
+ rtw_warn(rtwdev, "sdio read16 failed (0x%x): %d", addr, ret);
+
+ return val;
+}
+
+static u32 rtw_sdio_read32(struct rtw_dev *rtwdev, u32 addr)
+{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ bool direct, bus_claim;
+ u8 buf[4];
+ u32 val;
+ int ret;
+
+ bus_claim = rtw_sdio_bus_claim_needed(rtwsdio);
+ direct = rtw_sdio_is_bus_addr(addr);
+
+ if (bus_claim)
+ sdio_claim_host(rtwsdio->sdio_func);
+
+ if (direct) {
+ addr = rtw_sdio_to_bus_offset(rtwdev, addr);
+ val = rtw_sdio_readl(rtwsdio, addr, &ret);
+ } else if (addr & 3) {
+ ret = rtw_sdio_read_indirect_bytes(rtwdev, addr, buf, 4);
+ val = le32_to_cpu(*(__le32 *)buf);
+ } else {
+ val = rtw_sdio_read_indirect32(rtwdev, addr, &ret);
+ }
+
+ if (bus_claim)
+ sdio_release_host(rtwsdio->sdio_func);
+
+ if (ret)
+ rtw_warn(rtwdev, "sdio read32 failed (0x%x): %d", addr, ret);
+
+ return val;
+}
+
+static u32 rtw_sdio_to_write_address(struct rtw_dev *rtwdev, u32 addr)
+{
+ if (!rtw_sdio_is_bus_addr(addr))
+ addr |= WLAN_IOREG_OFFSET;
+
+ return rtw_sdio_to_bus_offset(rtwdev, addr);
+}
+
+static void rtw_sdio_write8(struct rtw_dev *rtwdev, u32 addr, u8 val)
+{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ bool bus_claim;
+ int ret;
+
+ addr = rtw_sdio_to_write_address(rtwdev, addr);
+ bus_claim = rtw_sdio_bus_claim_needed(rtwsdio);
+
+ if (bus_claim)
+ sdio_claim_host(rtwsdio->sdio_func);
+
+ sdio_writeb(rtwsdio->sdio_func, val, addr, &ret);
+
+ if (bus_claim)
+ sdio_release_host(rtwsdio->sdio_func);
+
+ if (ret)
+ rtw_warn(rtwdev, "sdio write8 failed (0x%x): %d", addr, ret);
+}
+
+static void rtw_sdio_write16(struct rtw_dev *rtwdev, u32 addr, u16 val)
+{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ bool bus_claim;
+ int ret;
+
+ addr = rtw_sdio_to_write_address(rtwdev, addr);
+ bus_claim = rtw_sdio_bus_claim_needed(rtwsdio);
+
+ if (bus_claim)
+ sdio_claim_host(rtwsdio->sdio_func);
+
+ sdio_writeb(rtwsdio->sdio_func, val, addr, &ret);
+ if (!ret)
+ sdio_writeb(rtwsdio->sdio_func, val >> 8, addr + 1, &ret);
+
+ if (bus_claim)
+ sdio_release_host(rtwsdio->sdio_func);
+
+ if (ret)
+ rtw_warn(rtwdev, "sdio write16 failed (0x%x): %d", addr, ret);
+}
+
+static void rtw_sdio_write32(struct rtw_dev *rtwdev, u32 addr, u32 val)
+{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ bool bus_claim;
+ int ret;
+
+ addr = rtw_sdio_to_write_address(rtwdev, addr);
+ bus_claim = rtw_sdio_bus_claim_needed(rtwsdio);
+
+ if (bus_claim)
+ sdio_claim_host(rtwsdio->sdio_func);
+
+ rtw_sdio_writel(rtwsdio, val, addr, &ret);
+
+ if (bus_claim)
+ sdio_release_host(rtwsdio->sdio_func);
+
+ if (ret)
+ rtw_warn(rtwdev, "sdio write32 failed (0x%x): %d", addr, ret);
+}
+
+static u32 rtw_sdio_get_tx_addr(struct rtw_dev *rtwdev, size_t size,
+ enum rtw_tx_queue_type queue)
+{
+ u32 txaddr;
+
+ switch (queue) {
+ case RTW_TX_QUEUE_BCN:
+ case RTW_TX_QUEUE_H2C:
+ case RTW_TX_QUEUE_HI0:
+ txaddr = FIELD_PREP(REG_SDIO_CMD_ADDR_MSK,
+ REG_SDIO_CMD_ADDR_TXFF_HIGH);
+ break;
+ case RTW_TX_QUEUE_VI:
+ case RTW_TX_QUEUE_VO:
+ txaddr = FIELD_PREP(REG_SDIO_CMD_ADDR_MSK,
+ REG_SDIO_CMD_ADDR_TXFF_NORMAL);
+ break;
+ case RTW_TX_QUEUE_BE:
+ case RTW_TX_QUEUE_BK:
+ txaddr = FIELD_PREP(REG_SDIO_CMD_ADDR_MSK,
+ REG_SDIO_CMD_ADDR_TXFF_LOW);
+ break;
+ case RTW_TX_QUEUE_MGMT:
+ txaddr = FIELD_PREP(REG_SDIO_CMD_ADDR_MSK,
+ REG_SDIO_CMD_ADDR_TXFF_EXTRA);
+ break;
+ default:
+ rtw_warn(rtwdev, "Unsupported queue for TX addr: 0x%02x\n",
+ queue);
+ return 0;
+ }
+
+ txaddr += DIV_ROUND_UP(size, 4);
+
+ return txaddr;
+};
+
+static int rtw_sdio_read_port(struct rtw_dev *rtwdev, u8 *buf, size_t count)
+{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ u32 rxaddr = rtwsdio->rx_addr++;
+ int ret;
+
+ ret = sdio_memcpy_fromio(rtwsdio->sdio_func, buf,
+ RTW_SDIO_ADDR_RX_RX0FF_GEN(rxaddr), count);
+ if (ret)
+ rtw_warn(rtwdev,
+ "Failed to read %lu byte(s) from SDIO port 0x%08x",
+ count, rxaddr);
+
+ return ret;
+}
+
+static int rtw_sdio_check_free_txpg(struct rtw_dev *rtwdev, u8 queue,
+ size_t count)
+{
+ unsigned int pages_free, pages_needed;
+
+ if (rtw_chip_wcpu_11n(rtwdev)) {
+ u32 free_txpg;
+
+ free_txpg = rtw_sdio_read32(rtwdev, REG_SDIO_FREE_TXPG);
+
+ switch (queue) {
+ case RTW_TX_QUEUE_BCN:
+ case RTW_TX_QUEUE_H2C:
+ case RTW_TX_QUEUE_HI0:
+ case RTW_TX_QUEUE_MGMT:
+ /* high */
+ pages_free = free_txpg & 0xff;
+ break;
+ case RTW_TX_QUEUE_VI:
+ case RTW_TX_QUEUE_VO:
+ /* normal */
+ pages_free = (free_txpg >> 8) & 0xff;
+ break;
+ case RTW_TX_QUEUE_BE:
+ case RTW_TX_QUEUE_BK:
+ /* low */
+ pages_free = (free_txpg >> 16) & 0xff;
+ break;
+ default:
+ rtw_warn(rtwdev, "Unknown mapping for queue %u\n", queue);
+ break;
+ }
+
+ /* add the pages from the public queue */
+ pages_free += (free_txpg >> 24) & 0xff;
+ } else {
+ u32 free_txpg[3];
+
+ free_txpg[0] = rtw_sdio_read32(rtwdev, REG_SDIO_FREE_TXPG);
+ free_txpg[1] = rtw_sdio_read32(rtwdev, REG_SDIO_FREE_TXPG + 4);
+ free_txpg[2] = rtw_sdio_read32(rtwdev, REG_SDIO_FREE_TXPG + 8);
+
+ switch (queue) {
+ case RTW_TX_QUEUE_BCN:
+ case RTW_TX_QUEUE_H2C:
+ case RTW_TX_QUEUE_HI0:
+ /* high */
+ pages_free = free_txpg[0] & 0xfff;
+ break;
+ case RTW_TX_QUEUE_VI:
+ case RTW_TX_QUEUE_VO:
+ /* normal */
+ pages_free = (free_txpg[0] >> 16) & 0xfff;
+ break;
+ case RTW_TX_QUEUE_BE:
+ case RTW_TX_QUEUE_BK:
+ /* low */
+ pages_free = free_txpg[1] & 0xfff;
+ break;
+ case RTW_TX_QUEUE_MGMT:
+ /* extra */
+ pages_free = free_txpg[2] & 0xfff;
+ break;
+ default:
+ rtw_warn(rtwdev, "Unknown mapping for queue %u\n", queue);
+ return -EINVAL;
+ }
+
+ /* add the pages from the public queue */
+ pages_free += (free_txpg[1] >> 16) & 0xfff;
+ }
+
+ pages_needed = DIV_ROUND_UP(count, rtwdev->chip->page_size);
+
+ if (pages_needed > pages_free) {
+ rtw_dbg(rtwdev, RTW_DBG_SDIO,
+ "Not enough free pages (%u needed, %u free) in queue %u for %zu bytes\n",
+ pages_needed, pages_free, queue, count);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static int rtw_sdio_write_port(struct rtw_dev *rtwdev, struct sk_buff *skb,
+ enum rtw_tx_queue_type queue)
+{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ bool bus_claim;
+ size_t txsize;
+ u32 txaddr;
+ int ret;
+
+ txaddr = rtw_sdio_get_tx_addr(rtwdev, skb->len, queue);
+ if (!txaddr)
+ return -EINVAL;
+
+ txsize = sdio_align_size(rtwsdio->sdio_func, skb->len);
+
+ ret = rtw_sdio_check_free_txpg(rtwdev, queue, txsize);
+ if (ret)
+ return ret;
+
+ if (!IS_ALIGNED((unsigned long)skb->data, RTW_SDIO_DATA_PTR_ALIGN))
+ rtw_warn(rtwdev, "Got unaligned SKB in %s() for queue %u\n",
+ __func__, queue);
+
+ bus_claim = rtw_sdio_bus_claim_needed(rtwsdio);
+
+ if (bus_claim)
+ sdio_claim_host(rtwsdio->sdio_func);
+
+ ret = sdio_memcpy_toio(rtwsdio->sdio_func, txaddr, skb->data, txsize);
+
+ if (bus_claim)
+ sdio_release_host(rtwsdio->sdio_func);
+
+ if (ret)
+ rtw_warn(rtwdev,
+ "Failed to write %lu byte(s) to SDIO port 0x%08x",
+ txsize, txaddr);
+
+ return ret;
+}
+
+static void rtw_sdio_init(struct rtw_dev *rtwdev)
+{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+
+ rtwsdio->irq_mask = REG_SDIO_HIMR_RX_REQUEST | REG_SDIO_HIMR_CPWM1;
+}
+
+static void rtw_sdio_rx_aggregation(struct rtw_dev *rtwdev, bool enable)
+{
+ u8 size, timeout;
+
+ if (enable) {
+ if (rtwdev->chip->id == RTW_CHIP_TYPE_8822C) {
+ size = 0xff;
+ timeout = 0x20;
+ } else {
+ size = 0x6;
+ timeout = 0x6;
+ }
+
+ /* Make the firmware honor the size limit configured below */
+ rtw_write32_set(rtwdev, REG_RXDMA_AGG_PG_TH, BIT_EN_PRE_CALC);
+
+ rtw_write8_set(rtwdev, REG_TXDMA_PQ_MAP, BIT_RXDMA_AGG_EN);
+
+ rtw_write16(rtwdev, REG_RXDMA_AGG_PG_TH, size |
+ (timeout << BIT_SHIFT_DMA_AGG_TO_V1));
+
+ rtw_write8_set(rtwdev, REG_RXDMA_MODE, BIT_DMA_MODE);
+ } else {
+ rtw_write32_clr(rtwdev, REG_RXDMA_AGG_PG_TH, BIT_EN_PRE_CALC);
+ rtw_write8_clr(rtwdev, REG_TXDMA_PQ_MAP, BIT_RXDMA_AGG_EN);
+ rtw_write8_clr(rtwdev, REG_RXDMA_MODE, BIT_DMA_MODE);
+ }
+}
+
+static void rtw_sdio_enable_interrupt(struct rtw_dev *rtwdev)
+{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+
+ rtw_write32(rtwdev, REG_SDIO_HIMR, rtwsdio->irq_mask);
+}
+
+static void rtw_sdio_disable_interrupt(struct rtw_dev *rtwdev)
+{
+ rtw_write32(rtwdev, REG_SDIO_HIMR, 0x0);
+}
+
+static u8 rtw_sdio_get_tx_qsel(struct rtw_dev *rtwdev, struct sk_buff *skb,
+ u8 queue)
+{
+ switch (queue) {
+ case RTW_TX_QUEUE_BCN:
+ return TX_DESC_QSEL_BEACON;
+ case RTW_TX_QUEUE_H2C:
+ return TX_DESC_QSEL_H2C;
+ case RTW_TX_QUEUE_MGMT:
+ if (rtw_chip_wcpu_11n(rtwdev))
+ return TX_DESC_QSEL_HIGH;
+ else
+ return TX_DESC_QSEL_MGMT;
+ case RTW_TX_QUEUE_HI0:
+ return TX_DESC_QSEL_HIGH;
+ default:
+ return skb->priority;
+ }
+};
+
+static int rtw_sdio_setup(struct rtw_dev *rtwdev)
+{
+ /* nothing to do */
+ return 0;
+}
+
+static int rtw_sdio_start(struct rtw_dev *rtwdev)
+{
+ rtw_sdio_rx_aggregation(rtwdev, false);
+ rtw_sdio_enable_interrupt(rtwdev);
+
+ return 0;
+}
+
+static void rtw_sdio_stop(struct rtw_dev *rtwdev)
+{
+ rtw_sdio_disable_interrupt(rtwdev);
+}
+
+static void rtw_sdio_deep_ps_enter(struct rtw_dev *rtwdev)
+{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ bool tx_empty = true;
+ u8 queue;
+
+ if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_TX_WAKE)) {
+ /* Deep PS state is not allowed to TX-DMA */
+ for (queue = 0; queue < RTK_MAX_TX_QUEUE_NUM; queue++) {
+ /* BCN queue is rsvd page, does not have DMA interrupt
+ * H2C queue is managed by firmware
+ */
+ if (queue == RTW_TX_QUEUE_BCN ||
+ queue == RTW_TX_QUEUE_H2C)
+ continue;
+
+ /* check if there is any skb DMAing */
+ if (skb_queue_len(&rtwsdio->tx_queue[queue])) {
+ tx_empty = false;
+ break;
+ }
+ }
+ }
+
+ if (!tx_empty) {
+ rtw_dbg(rtwdev, RTW_DBG_PS,
+ "TX path not empty, cannot enter deep power save state\n");
+ return;
+ }
+
+ set_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags);
+ rtw_power_mode_change(rtwdev, true);
+}
+
+static void rtw_sdio_deep_ps_leave(struct rtw_dev *rtwdev)
+{
+ if (test_and_clear_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags))
+ rtw_power_mode_change(rtwdev, false);
+}
+
+static void rtw_sdio_deep_ps(struct rtw_dev *rtwdev, bool enter)
+{
+ if (enter && !test_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags))
+ rtw_sdio_deep_ps_enter(rtwdev);
+
+ if (!enter && test_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags))
+ rtw_sdio_deep_ps_leave(rtwdev);
+}
+
+static void rtw_sdio_tx_kick_off(struct rtw_dev *rtwdev)
+{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+
+ queue_work(rtwsdio->txwq, &rtwsdio->tx_handler_data->work);
+}
+
+static void rtw_sdio_link_ps(struct rtw_dev *rtwdev, bool enter)
+{
+ /* nothing to do */
+}
+
+static void rtw_sdio_interface_cfg(struct rtw_dev *rtwdev)
+{
+ u32 val;
+
+ rtw_read32(rtwdev, REG_SDIO_FREE_TXPG);
+
+ val = rtw_read32(rtwdev, REG_SDIO_TX_CTRL);
+ val &= 0xfff8;
+ rtw_write32(rtwdev, REG_SDIO_TX_CTRL, val);
+}
+
+static void rtw_sdio_power_switch(struct rtw_dev *rtwdev, bool on)
+{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+
+ rtwsdio->is_powered_on = on;
+}
+
+static struct rtw_sdio_tx_data *rtw_sdio_get_tx_data(struct sk_buff *skb)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ BUILD_BUG_ON(sizeof(struct rtw_sdio_tx_data) >
+ sizeof(info->status.status_driver_data));
+
+ return (struct rtw_sdio_tx_data *)info->status.status_driver_data;
+}
+
+static void rtw_sdio_tx_skb_prepare(struct rtw_dev *rtwdev,
+ struct rtw_tx_pkt_info *pkt_info,
+ struct sk_buff *skb,
+ enum rtw_tx_queue_type queue)
+{
+ const struct rtw_chip_info *chip = rtwdev->chip;
+ unsigned long data_addr, aligned_addr;
+ size_t offset;
+ u8 *pkt_desc;
+
+ pkt_desc = skb_push(skb, chip->tx_pkt_desc_sz);
+
+ data_addr = (unsigned long)pkt_desc;
+ aligned_addr = ALIGN(data_addr, RTW_SDIO_DATA_PTR_ALIGN);
+
+ if (data_addr != aligned_addr) {
+ /* Ensure that the start of the pkt_desc is always aligned at
+ * RTW_SDIO_DATA_PTR_ALIGN.
+ */
+ offset = RTW_SDIO_DATA_PTR_ALIGN - (aligned_addr - data_addr);
+
+ pkt_desc = skb_push(skb, offset);
+
+ /* By inserting padding to align the start of the pkt_desc we
+ * need to inform the firmware that the actual data starts at
+ * a different offset than normal.
+ */
+ pkt_info->offset += offset;
+ }
+
+ memset(pkt_desc, 0, chip->tx_pkt_desc_sz);
+
+ pkt_info->qsel = rtw_sdio_get_tx_qsel(rtwdev, skb, queue);
+
+ rtw_tx_fill_tx_desc(pkt_info, skb);
+ chip->ops->fill_txdesc_checksum(rtwdev, pkt_info, pkt_desc);
+}
+
+static int rtw_sdio_write_data(struct rtw_dev *rtwdev,
+ struct rtw_tx_pkt_info *pkt_info,
+ struct sk_buff *skb,
+ enum rtw_tx_queue_type queue)
+{
+ int ret;
+
+ rtw_sdio_tx_skb_prepare(rtwdev, pkt_info, skb, queue);
+
+ ret = rtw_sdio_write_port(rtwdev, skb, queue);
+ dev_kfree_skb_any(skb);
+
+ return ret;
+}
+
+static int rtw_sdio_write_data_rsvd_page(struct rtw_dev *rtwdev, u8 *buf,
+ u32 size)
+{
+ struct rtw_tx_pkt_info pkt_info = {};
+ struct sk_buff *skb;
+
+ skb = rtw_tx_write_data_rsvd_page_get(rtwdev, &pkt_info, buf, size);
+ if (!skb)
+ return -ENOMEM;
+
+ return rtw_sdio_write_data(rtwdev, &pkt_info, skb, RTW_TX_QUEUE_BCN);
+}
+
+static int rtw_sdio_write_data_h2c(struct rtw_dev *rtwdev, u8 *buf, u32 size)
+{
+ struct rtw_tx_pkt_info pkt_info = {};
+ struct sk_buff *skb;
+
+ skb = rtw_tx_write_data_h2c_get(rtwdev, &pkt_info, buf, size);
+ if (!skb)
+ return -ENOMEM;
+
+ return rtw_sdio_write_data(rtwdev, &pkt_info, skb, RTW_TX_QUEUE_H2C);
+}
+
+static int rtw_sdio_tx_write(struct rtw_dev *rtwdev,
+ struct rtw_tx_pkt_info *pkt_info,
+ struct sk_buff *skb)
+{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ enum rtw_tx_queue_type queue = rtw_tx_queue_mapping(skb);
+ struct rtw_sdio_tx_data *tx_data;
+
+ rtw_sdio_tx_skb_prepare(rtwdev, pkt_info, skb, queue);
+
+ tx_data = rtw_sdio_get_tx_data(skb);
+ tx_data->sn = pkt_info->sn;
+
+ skb_queue_tail(&rtwsdio->tx_queue[queue], skb);
+
+ return 0;
+}
+
+static void rtw_sdio_tx_err_isr(struct rtw_dev *rtwdev)
+{
+ u32 val = rtw_read32(rtwdev, REG_TXDMA_STATUS);
+
+ rtw_write32(rtwdev, REG_TXDMA_STATUS, val);
+}
+
+static void rtw_sdio_rx_skb(struct rtw_dev *rtwdev, struct sk_buff *skb,
+ u32 pkt_offset, struct rtw_rx_pkt_stat *pkt_stat,
+ struct ieee80211_rx_status *rx_status)
+{
+ memcpy(IEEE80211_SKB_RXCB(skb), rx_status, sizeof(*rx_status));
+
+ if (pkt_stat->is_c2h) {
+ skb_put(skb, pkt_stat->pkt_len + pkt_offset);
+ rtw_fw_c2h_cmd_rx_irqsafe(rtwdev, pkt_offset, skb);
+ return;
+ }
+
+ skb_put(skb, pkt_stat->pkt_len);
+ skb_reserve(skb, pkt_offset);
+
+ rtw_rx_stats(rtwdev, pkt_stat->vif, skb);
+
+ ieee80211_rx_irqsafe(rtwdev->hw, skb);
+}
+
+static void rtw_sdio_rxfifo_recv(struct rtw_dev *rtwdev, u32 rx_len)
+{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ const struct rtw_chip_info *chip = rtwdev->chip;
+ u32 pkt_desc_sz = chip->rx_pkt_desc_sz;
+ struct ieee80211_rx_status rx_status;
+ struct rtw_rx_pkt_stat pkt_stat;
+ struct sk_buff *skb, *split_skb;
+ u32 pkt_offset, curr_pkt_len;
+ size_t bufsz;
+ u8 *rx_desc;
+ int ret;
+
+ bufsz = sdio_align_size(rtwsdio->sdio_func, rx_len);
+
+ skb = dev_alloc_skb(bufsz);
+ if (!skb)
+ return;
+
+ ret = rtw_sdio_read_port(rtwdev, skb->data, bufsz);
+ if (ret) {
+ dev_kfree_skb_any(skb);
+ return;
+ }
+
+ while (true) {
+ rx_desc = skb->data;
+ chip->ops->query_rx_desc(rtwdev, rx_desc, &pkt_stat,
+ &rx_status);
+ pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz +
+ pkt_stat.shift;
+
+ curr_pkt_len = ALIGN(pkt_offset + pkt_stat.pkt_len,
+ RTW_SDIO_DATA_PTR_ALIGN);
+
+ if ((curr_pkt_len + pkt_desc_sz) >= rx_len) {
+ /* Use the original skb (with it's adjusted offset)
+ * when processing the last (or even the only) entry to
+ * have it's memory freed automatically.
+ */
+ rtw_sdio_rx_skb(rtwdev, skb, pkt_offset, &pkt_stat,
+ &rx_status);
+ break;
+ }
+
+ split_skb = dev_alloc_skb(curr_pkt_len);
+ if (!split_skb) {
+ rtw_sdio_rx_skb(rtwdev, skb, pkt_offset, &pkt_stat,
+ &rx_status);
+ break;
+ }
+
+ skb_copy_header(split_skb, skb);
+ memcpy(split_skb->data, skb->data, curr_pkt_len);
+
+ rtw_sdio_rx_skb(rtwdev, split_skb, pkt_offset, &pkt_stat,
+ &rx_status);
+
+ /* Move to the start of the next RX descriptor */
+ skb_reserve(skb, curr_pkt_len);
+ rx_len -= curr_pkt_len;
+ }
+}
+
+static void rtw_sdio_rx_isr(struct rtw_dev *rtwdev)
+{
+ u32 rx_len;
+
+ while (true) {
+ if (rtw_chip_wcpu_11n(rtwdev))
+ rx_len = rtw_read16(rtwdev, REG_SDIO_RX0_REQ_LEN);
+ else
+ rx_len = rtw_read32(rtwdev, REG_SDIO_RX0_REQ_LEN);
+
+ if (!rx_len)
+ break;
+
+ rtw_sdio_rxfifo_recv(rtwdev, rx_len);
+ }
+}
+
+static void rtw_sdio_handle_interrupt(struct sdio_func *sdio_func)
+{
+ struct ieee80211_hw *hw = sdio_get_drvdata(sdio_func);
+ struct rtw_dev *rtwdev = hw->priv;
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ u32 hisr;
+
+ rtwsdio->irq_thread = current;
+
+ hisr = rtw_read32(rtwdev, REG_SDIO_HISR);
+
+ if (hisr & REG_SDIO_HISR_TXERR)
+ rtw_sdio_tx_err_isr(rtwdev);
+ if (hisr & REG_SDIO_HISR_RX_REQUEST) {
+ hisr &= ~REG_SDIO_HISR_RX_REQUEST;
+ rtw_sdio_rx_isr(rtwdev);
+ }
+
+ rtw_write32(rtwdev, REG_SDIO_HISR, hisr);
+
+ rtwsdio->irq_thread = NULL;
+}
+
+static int __maybe_unused rtw_sdio_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int __maybe_unused rtw_sdio_resume(struct device *dev)
+{
+ return 0;
+}
+
+SIMPLE_DEV_PM_OPS(rtw_sdio_pm_ops, rtw_sdio_suspend, rtw_sdio_resume);
+EXPORT_SYMBOL(rtw_sdio_pm_ops);
+
+static int rtw_sdio_claim(struct rtw_dev *rtwdev, struct sdio_func *sdio_func)
+{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ int ret;
+
+ sdio_claim_host(sdio_func);
+
+ ret = sdio_enable_func(sdio_func);
+ if (ret) {
+ rtw_err(rtwdev, "Failed to enable SDIO func");
+ goto err_release_host;
+ }
+
+ ret = sdio_set_block_size(sdio_func, RTW_SDIO_BLOCK_SIZE);
+ if (ret) {
+ rtw_err(rtwdev, "Failed to set SDIO block size to 512");
+ goto err_disable_func;
+ }
+
+ rtwsdio->sdio_func = sdio_func;
+
+ rtwsdio->sdio3_bus_mode = mmc_card_uhs(sdio_func->card);
+
+ sdio_set_drvdata(sdio_func, rtwdev->hw);
+ SET_IEEE80211_DEV(rtwdev->hw, &sdio_func->dev);
+
+ sdio_release_host(sdio_func);
+
+ return 0;
+
+err_disable_func:
+ sdio_disable_func(sdio_func);
+err_release_host:
+ sdio_release_host(sdio_func);
+ return ret;
+}
+
+static void rtw_sdio_declaim(struct rtw_dev *rtwdev,
+ struct sdio_func *sdio_func)
+{
+ sdio_claim_host(sdio_func);
+ sdio_disable_func(sdio_func);
+ sdio_release_host(sdio_func);
+}
+
+static struct rtw_hci_ops rtw_sdio_ops = {
+ .tx_write = rtw_sdio_tx_write,
+ .tx_kick_off = rtw_sdio_tx_kick_off,
+ .setup = rtw_sdio_setup,
+ .start = rtw_sdio_start,
+ .stop = rtw_sdio_stop,
+ .deep_ps = rtw_sdio_deep_ps,
+ .link_ps = rtw_sdio_link_ps,
+ .interface_cfg = rtw_sdio_interface_cfg,
+
+ .power_switch = rtw_sdio_power_switch,
+
+ .read8 = rtw_sdio_read8,
+ .read16 = rtw_sdio_read16,
+ .read32 = rtw_sdio_read32,
+ .write8 = rtw_sdio_write8,
+ .write16 = rtw_sdio_write16,
+ .write32 = rtw_sdio_write32,
+ .write_data_rsvd_page = rtw_sdio_write_data_rsvd_page,
+ .write_data_h2c = rtw_sdio_write_data_h2c,
+};
+
+static int rtw_sdio_request_irq(struct rtw_dev *rtwdev,
+ struct sdio_func *sdio_func)
+{
+ int ret;
+
+ sdio_claim_host(sdio_func);
+ ret = sdio_claim_irq(sdio_func, &rtw_sdio_handle_interrupt);
+ sdio_release_host(sdio_func);
+
+ if (ret) {
+ rtw_err(rtwdev, "failed to claim SDIO IRQ");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void rtw_sdio_indicate_tx_status(struct rtw_dev *rtwdev,
+ struct sk_buff *skb)
+{
+ struct rtw_sdio_tx_data *tx_data = rtw_sdio_get_tx_data(skb);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hw *hw = rtwdev->hw;
+
+ /* enqueue to wait for tx report */
+ if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) {
+ rtw_tx_report_enqueue(rtwdev, skb, tx_data->sn);
+ return;
+ }
+
+ /* always ACK for others, then they won't be marked as drop */
+ ieee80211_tx_info_clear_status(info);
+ if (info->flags & IEEE80211_TX_CTL_NO_ACK)
+ info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED;
+ else
+ info->flags |= IEEE80211_TX_STAT_ACK;
+
+ ieee80211_tx_status_irqsafe(hw, skb);
+}
+
+static void rtw_sdio_process_tx_queue(struct rtw_dev *rtwdev,
+ enum rtw_tx_queue_type queue)
+{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ struct sk_buff *skb;
+ int ret;
+
+ while (true) {
+ skb = skb_dequeue(&rtwsdio->tx_queue[queue]);
+ if (!skb)
+ break;
+
+ ret = rtw_sdio_write_port(rtwdev, skb, queue);
+ if (ret) {
+ skb_queue_head(&rtwsdio->tx_queue[queue], skb);
+ break;
+ }
+
+ if (queue <= RTW_TX_QUEUE_VO)
+ rtw_sdio_indicate_tx_status(rtwdev, skb);
+ else
+ dev_kfree_skb_any(skb);
+ }
+}
+
+static void rtw_sdio_tx_handler(struct work_struct *work)
+{
+ struct rtw_sdio_work_data *work_data =
+ container_of(work, struct rtw_sdio_work_data, work);
+ struct rtw_dev *rtwdev = work_data->rtwdev;
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ bool has_more_tx_data;
+ int queue;
+
+ if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_TX_WAKE))
+ rtw_sdio_deep_ps_leave(rtwdev);
+
+ do {
+ has_more_tx_data = false;
+
+ for (queue = RTK_MAX_TX_QUEUE_NUM - 1; queue >= 0; queue--) {
+ rtw_sdio_process_tx_queue(rtwdev, queue);
+
+ if (!skb_queue_empty(&rtwsdio->tx_queue[queue]))
+ has_more_tx_data = true;
+ }
+ } while (has_more_tx_data);
+}
+
+static void rtw_sdio_free_irq(struct rtw_dev *rtwdev,
+ struct sdio_func *sdio_func)
+{
+ sdio_release_irq(sdio_func);
+}
+
+static int rtw_sdio_init_tx(struct rtw_dev *rtwdev)
+{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ int i;
+
+ rtwsdio->txwq = create_singlethread_workqueue("rtw88_sdio: tx wq");
+ if (!rtwsdio->txwq) {
+ rtw_err(rtwdev, "failed to create TX work queue\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < RTK_MAX_TX_QUEUE_NUM; i++)
+ skb_queue_head_init(&rtwsdio->tx_queue[i]);
+ rtwsdio->tx_handler_data = kmalloc(sizeof(*rtwsdio->tx_handler_data),
+ GFP_KERNEL);
+ if (!rtwsdio->tx_handler_data)
+ goto err_destroy_wq;
+
+ rtwsdio->tx_handler_data->rtwdev = rtwdev;
+ INIT_WORK(&rtwsdio->tx_handler_data->work, rtw_sdio_tx_handler);
+
+ return 0;
+
+err_destroy_wq:
+ destroy_workqueue(rtwsdio->txwq);
+ return -ENOMEM;
+}
+
+static void rtw_sdio_deinit_tx(struct rtw_dev *rtwdev)
+{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ int i;
+
+ for (i = 0; i < RTK_MAX_TX_QUEUE_NUM; i++)
+ skb_queue_purge(&rtwsdio->tx_queue[i]);
+
+ flush_workqueue(rtwsdio->txwq);
+ destroy_workqueue(rtwsdio->txwq);
+ kfree(rtwsdio->tx_handler_data);
+}
+
+int rtw_sdio_probe(struct sdio_func *sdio_func,
+ const struct sdio_device_id *id)
+{
+ struct ieee80211_hw *hw;
+ struct rtw_dev *rtwdev;
+ int drv_data_size;
+ int ret;
+
+ drv_data_size = sizeof(struct rtw_dev) + sizeof(struct rtw_sdio);
+ hw = ieee80211_alloc_hw(drv_data_size, &rtw_ops);
+ if (!hw) {
+ dev_err(&sdio_func->dev, "failed to allocate hw");
+ return -ENOMEM;
+ }
+
+ rtwdev = hw->priv;
+ rtwdev->hw = hw;
+ rtwdev->dev = &sdio_func->dev;
+ rtwdev->chip = (struct rtw_chip_info *)id->driver_data;
+ rtwdev->hci.ops = &rtw_sdio_ops;
+ rtwdev->hci.type = RTW_HCI_TYPE_SDIO;
+
+ ret = rtw_core_init(rtwdev);
+ if (ret)
+ goto err_release_hw;
+
+ rtw_dbg(rtwdev, RTW_DBG_SDIO,
+ "rtw88 SDIO probe: vendor=0x%04x device=%04x class=%02x",
+ id->vendor, id->device, id->class);
+
+ ret = rtw_sdio_claim(rtwdev, sdio_func);
+ if (ret) {
+ rtw_err(rtwdev, "failed to claim SDIO device");
+ goto err_deinit_core;
+ }
+
+ rtw_sdio_init(rtwdev);
+
+ ret = rtw_sdio_init_tx(rtwdev);
+ if (ret) {
+ rtw_err(rtwdev, "failed to init SDIO TX queue\n");
+ goto err_sdio_declaim;
+ }
+
+ ret = rtw_chip_info_setup(rtwdev);
+ if (ret) {
+ rtw_err(rtwdev, "failed to setup chip information");
+ goto err_destroy_txwq;
+ }
+
+ ret = rtw_register_hw(rtwdev, hw);
+ if (ret) {
+ rtw_err(rtwdev, "failed to register hw");
+ goto err_destroy_txwq;
+ }
+
+ ret = rtw_sdio_request_irq(rtwdev, sdio_func);
+ if (ret)
+ goto err_unregister_hw;
+
+ return 0;
+
+err_unregister_hw:
+ rtw_unregister_hw(rtwdev, hw);
+err_destroy_txwq:
+ rtw_sdio_deinit_tx(rtwdev);
+err_sdio_declaim:
+ rtw_sdio_declaim(rtwdev, sdio_func);
+err_deinit_core:
+ rtw_core_deinit(rtwdev);
+err_release_hw:
+ ieee80211_free_hw(hw);
+
+ return ret;
+}
+EXPORT_SYMBOL(rtw_sdio_probe);
+
+void rtw_sdio_remove(struct sdio_func *sdio_func)
+{
+ struct ieee80211_hw *hw = sdio_get_drvdata(sdio_func);
+ struct rtw_dev *rtwdev;
+
+ if (!hw)
+ return;
+
+ rtwdev = hw->priv;
+
+ rtw_unregister_hw(rtwdev, hw);
+ rtw_sdio_disable_interrupt(rtwdev);
+ rtw_sdio_declaim(rtwdev, sdio_func);
+ rtw_sdio_free_irq(rtwdev, sdio_func);
+ rtw_sdio_deinit_tx(rtwdev);
+ rtw_core_deinit(rtwdev);
+ ieee80211_free_hw(hw);
+}
+EXPORT_SYMBOL(rtw_sdio_remove);
+
+void rtw_sdio_shutdown(struct device *dev)
+{
+ struct sdio_func *sdio_func = dev_to_sdio_func(dev);
+ struct ieee80211_hw *hw = sdio_get_drvdata(sdio_func);
+ const struct rtw_chip_info *chip;
+ struct rtw_dev *rtwdev;
+
+ if (!hw)
+ return;
+
+ rtwdev = hw->priv;
+ chip = rtwdev->chip;
+
+ if (chip->ops->shutdown)
+ chip->ops->shutdown(rtwdev);
+}
+EXPORT_SYMBOL(rtw_sdio_shutdown);
+
+MODULE_AUTHOR("Martin Blumenstingl");
+MODULE_AUTHOR("Jernej Skrabec");
+MODULE_DESCRIPTION("Realtek 802.11ac wireless SDIO driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/realtek/rtw88/sdio.h b/drivers/net/wireless/realtek/rtw88/sdio.h
new file mode 100644
index 000000000000..7339e35f808a
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw88/sdio.h
@@ -0,0 +1,175 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/* Copyright (C) 2021 Martin Blumenstingl <[email protected]>
+ * Copyright (C) 2021 Jernej Skrabec <[email protected]>
+ */
+
+#ifndef __REG_SDIO_H_
+#define __REG_SDIO_H_
+
+#include "main.h"
+
+/* I/O bus domain address mapping */
+#define SDIO_LOCAL_OFFSET 0x10250000
+#define WLAN_IOREG_OFFSET 0x10260000
+#define FIRMWARE_FIFO_OFFSET 0x10270000
+#define TX_HIQ_OFFSET 0x10310000
+#define TX_MIQ_OFFSET 0x10320000
+#define TX_LOQ_OFFSET 0x10330000
+#define TX_EPQ_OFFSET 0x10350000
+#define RX_RX0FF_OFFSET 0x10340000
+
+#define RTW_SDIO_BUS_MSK 0xffff0000
+#define SDIO_LOCAL_REG_MSK 0x00000fff
+#define WLAN_IOREG_REG_MSK 0x0000ffff
+
+/* SDIO Tx Control */
+#define REG_SDIO_TX_CTRL (SDIO_LOCAL_OFFSET + 0x0000)
+
+/*SDIO status timeout*/
+#define REG_SDIO_TIMEOUT (SDIO_LOCAL_OFFSET + 0x0002)
+
+/* SDIO Host Interrupt Mask */
+#define REG_SDIO_HIMR (SDIO_LOCAL_OFFSET + 0x0014)
+#define REG_SDIO_HIMR_RX_REQUEST BIT(0)
+#define REG_SDIO_HIMR_AVAL BIT(1)
+#define REG_SDIO_HIMR_TXERR BIT(2)
+#define REG_SDIO_HIMR_RXERR BIT(3)
+#define REG_SDIO_HIMR_TXFOVW BIT(4)
+#define REG_SDIO_HIMR_RXFOVW BIT(5)
+#define REG_SDIO_HIMR_TXBCNOK BIT(6)
+#define REG_SDIO_HIMR_TXBCNERR BIT(7)
+#define REG_SDIO_HIMR_BCNERLY_INT BIT(16)
+#define REG_SDIO_HIMR_C2HCMD BIT(17)
+#define REG_SDIO_HIMR_CPWM1 BIT(18)
+#define REG_SDIO_HIMR_CPWM2 BIT(19)
+#define REG_SDIO_HIMR_HSISR_IND BIT(20)
+#define REG_SDIO_HIMR_GTINT3_IND BIT(21)
+#define REG_SDIO_HIMR_GTINT4_IND BIT(22)
+#define REG_SDIO_HIMR_PSTIMEOUT BIT(23)
+#define REG_SDIO_HIMR_OCPINT BIT(24)
+#define REG_SDIO_HIMR_ATIMEND BIT(25)
+#define REG_SDIO_HIMR_ATIMEND_E BIT(26)
+#define REG_SDIO_HIMR_CTWEND BIT(27)
+/* the following two are RTL8188 SDIO Specific */
+#define REG_SDIO_HIMR_MCU_ERR BIT(28)
+#define REG_SDIO_HIMR_TSF_BIT32_TOGGLE BIT(29)
+
+/* SDIO Host Interrupt Service Routine */
+#define REG_SDIO_HISR (SDIO_LOCAL_OFFSET + 0x0018)
+#define REG_SDIO_HISR_RX_REQUEST BIT(0)
+#define REG_SDIO_HISR_AVAL BIT(1)
+#define REG_SDIO_HISR_TXERR BIT(2)
+#define REG_SDIO_HISR_RXERR BIT(3)
+#define REG_SDIO_HISR_TXFOVW BIT(4)
+#define REG_SDIO_HISR_RXFOVW BIT(5)
+#define REG_SDIO_HISR_TXBCNOK BIT(6)
+#define REG_SDIO_HISR_TXBCNERR BIT(7)
+#define REG_SDIO_HISR_BCNERLY_INT BIT(16)
+#define REG_SDIO_HISR_C2HCMD BIT(17)
+#define REG_SDIO_HISR_CPWM1 BIT(18)
+#define REG_SDIO_HISR_CPWM2 BIT(19)
+#define REG_SDIO_HISR_HSISR_IND BIT(20)
+#define REG_SDIO_HISR_GTINT3_IND BIT(21)
+#define REG_SDIO_HISR_GTINT4_IND BIT(22)
+#define REG_SDIO_HISR_PSTIMEOUT BIT(23)
+#define REG_SDIO_HISR_OCPINT BIT(24)
+#define REG_SDIO_HISR_ATIMEND BIT(25)
+#define REG_SDIO_HISR_ATIMEND_E BIT(26)
+#define REG_SDIO_HISR_CTWEND BIT(27)
+/* the following two are RTL8188 SDIO Specific */
+#define REG_SDIO_HISR_MCU_ERR BIT(28)
+#define REG_SDIO_HISR_TSF_BIT32_TOGGLE BIT(29)
+
+/* HCI Current Power Mode */
+#define REG_SDIO_HCPWM (SDIO_LOCAL_OFFSET + 0x0019)
+/* RXDMA Request Length */
+#define REG_SDIO_RX0_REQ_LEN (SDIO_LOCAL_OFFSET + 0x001C)
+/* OQT Free Page */
+#define REG_SDIO_OQT_FREE_PG (SDIO_LOCAL_OFFSET + 0x001E)
+/* Free Tx Buffer Page */
+#define REG_SDIO_FREE_TXPG (SDIO_LOCAL_OFFSET + 0x0020)
+/* HCI Current Power Mode 1 */
+#define REG_SDIO_HCPWM1 (SDIO_LOCAL_OFFSET + 0x0024)
+/* HCI Current Power Mode 2 */
+#define REG_SDIO_HCPWM2 (SDIO_LOCAL_OFFSET + 0x0026)
+/* Free Tx Page Sequence */
+#define REG_SDIO_FREE_TXPG_SEQ (SDIO_LOCAL_OFFSET + 0x0028)
+/* HTSF Informaion */
+#define REG_SDIO_HTSFR_INFO (SDIO_LOCAL_OFFSET + 0x0030)
+#define REG_SDIO_HCPWM1_V2 (SDIO_LOCAL_OFFSET + 0x0038)
+/* H2C */
+#define REG_SDIO_H2C (SDIO_LOCAL_OFFSET + 0x0060)
+/* HCI Request Power Mode 1 */
+#define REG_SDIO_HRPWM1 (SDIO_LOCAL_OFFSET + 0x0080)
+/* HCI Request Power Mode 2 */
+#define REG_SDIO_HRPWM2 (SDIO_LOCAL_OFFSET + 0x0082)
+/* HCI Power Save Clock */
+#define REG_SDIO_HPS_CLKR (SDIO_LOCAL_OFFSET + 0x0084)
+/* SDIO HCI Suspend Control */
+#define REG_SDIO_HSUS_CTRL (SDIO_LOCAL_OFFSET + 0x0086)
+/* SDIO Host Extension Interrupt Mask Always */
+#define REG_SDIO_HIMR_ON (SDIO_LOCAL_OFFSET + 0x0090)
+/* SDIO Host Extension Interrupt Status Always */
+#define REG_SDIO_HISR_ON (SDIO_LOCAL_OFFSET + 0x0091)
+
+#define REG_SDIO_INDIRECT_REG_CFG (SDIO_LOCAL_OFFSET + 0x0040)
+#define REG_SDIO_INDIRECT_REG_DATA (SDIO_LOCAL_OFFSET + 0x0044)
+
+/* Sdio Address for SDIO Local Reg, TRX FIFO, MAC Reg */
+#define REG_SDIO_CMD_ADDR_MSK GENMASK(16, 13)
+#define REG_SDIO_CMD_ADDR_SDIO_REG 0
+#define REG_SDIO_CMD_ADDR_MAC_REG 8
+#define REG_SDIO_CMD_ADDR_TXFF_HIGH 4
+#define REG_SDIO_CMD_ADDR_TXFF_LOW 6
+#define REG_SDIO_CMD_ADDR_TXFF_NORMAL 5
+#define REG_SDIO_CMD_ADDR_TXFF_EXTRA 7
+#define REG_SDIO_CMD_ADDR_RXFF 7
+
+#define RTW_SDIO_BLOCK_SIZE 512
+#define RTW_SDIO_ADDR_RX_RX0FF_GEN(_id) (0x0e000 | ((_id) & 0x3))
+
+#define RTW_SDIO_DATA_PTR_ALIGN 8
+
+struct sdio_func;
+struct sdio_device_id;
+
+struct rtw_sdio_tx_data {
+ u8 sn;
+};
+
+struct rtw_sdio_work_data {
+ struct work_struct work;
+ struct rtw_dev *rtwdev;
+};
+
+struct rtw_sdio {
+ struct sdio_func *sdio_func;
+
+ u32 irq_mask;
+ u8 rx_addr;
+ bool sdio3_bus_mode;
+ bool is_powered_on;
+
+ void *irq_thread;
+
+ struct workqueue_struct *txwq;
+
+ struct sk_buff_head tx_queue[RTK_MAX_TX_QUEUE_NUM];
+ struct rtw_sdio_work_data *tx_handler_data;
+};
+
+extern const struct dev_pm_ops rtw_sdio_pm_ops;
+
+int rtw_sdio_probe(struct sdio_func *sdio_func,
+ const struct sdio_device_id *id);
+void rtw_sdio_remove(struct sdio_func *sdio_func);
+void rtw_sdio_shutdown(struct device *dev);
+
+static inline bool rtw_sdio_is_sdio30_supported(struct rtw_dev *rtwdev)
+{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+
+ return rtwsdio->sdio3_bus_mode;
+}
+
+#endif
--
2.39.0
Add the code specific to SDIO HCI in the MAC power on sequence. This is
based on the RTL8822BS and RTL8822CS vendor drivers.
Co-developed-by: Jernej Skrabec <[email protected]>
Signed-off-by: Jernej Skrabec <[email protected]>
Signed-off-by: Martin Blumenstingl <[email protected]>
---
drivers/net/wireless/realtek/rtw88/mac.c | 41 ++++++++++++++++++++++--
1 file changed, 39 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw88/mac.c b/drivers/net/wireless/realtek/rtw88/mac.c
index 8e1fa824b32b..ad71f9838d1d 100644
--- a/drivers/net/wireless/realtek/rtw88/mac.c
+++ b/drivers/net/wireless/realtek/rtw88/mac.c
@@ -7,6 +7,7 @@
#include "reg.h"
#include "fw.h"
#include "debug.h"
+#include "sdio.h"
void rtw_set_channel_mac(struct rtw_dev *rtwdev, u8 channel, u8 bw,
u8 primary_ch_idx)
@@ -60,6 +61,7 @@ EXPORT_SYMBOL(rtw_set_channel_mac);
static int rtw_mac_pre_system_cfg(struct rtw_dev *rtwdev)
{
+ unsigned int retry;
u32 value32;
u8 value8;
@@ -77,6 +79,26 @@ static int rtw_mac_pre_system_cfg(struct rtw_dev *rtwdev)
case RTW_HCI_TYPE_PCIE:
rtw_write32_set(rtwdev, REG_HCI_OPT_CTRL, BIT_USB_SUS_DIS);
break;
+ case RTW_HCI_TYPE_SDIO:
+ rtw_write8_clr(rtwdev, REG_SDIO_HSUS_CTRL, BIT(0));
+
+ for (retry = 0; retry < RTW_PWR_POLLING_CNT; retry++) {
+ if (rtw_read8(rtwdev, REG_SDIO_HSUS_CTRL) & BIT(1))
+ break;
+
+ usleep_range(10, 50);
+ }
+
+ if (retry == RTW_PWR_POLLING_CNT) {
+ rtw_err(rtwdev, "failed to poll REG_SDIO_HSUS_CTRL[1]");
+ return -ETIMEDOUT;
+ }
+
+ if (rtw_sdio_is_sdio30_supported(rtwdev))
+ rtw_write8_set(rtwdev, REG_HCI_OPT_CTRL + 2, BIT(2));
+ else
+ rtw_write8_clr(rtwdev, REG_HCI_OPT_CTRL + 2, BIT(2));
+ break;
case RTW_HCI_TYPE_USB:
break;
default:
@@ -248,6 +270,7 @@ static int rtw_mac_power_switch(struct rtw_dev *rtwdev, bool pwr_on)
{
const struct rtw_chip_info *chip = rtwdev->chip;
const struct rtw_pwr_seq_cmd **pwr_seq;
+ u32 imr;
u8 rpwm;
bool cur_pwr;
@@ -278,12 +301,19 @@ static int rtw_mac_power_switch(struct rtw_dev *rtwdev, bool pwr_on)
*/
rtw_hci_power_switch(rtwdev, false);
+ imr = rtw_read32(rtwdev, REG_SDIO_HIMR);
+ rtw_write32(rtwdev, REG_SDIO_HIMR, 0);
+
pwr_seq = pwr_on ? chip->pwr_on_seq : chip->pwr_off_seq;
- if (rtw_pwr_seq_parser(rtwdev, pwr_seq))
+ if (rtw_pwr_seq_parser(rtwdev, pwr_seq)) {
+ rtw_write32(rtwdev, REG_SDIO_HIMR, imr);
return -EINVAL;
+ }
rtw_hci_power_switch(rtwdev, pwr_on);
+ rtw_write32(rtwdev, REG_SDIO_HIMR, imr);
+
return 0;
}
@@ -450,6 +480,9 @@ static void download_firmware_reg_backup(struct rtw_dev *rtwdev,
rtw_write16(rtwdev, REG_FIFOPAGE_INFO_1, 0x200);
rtw_write32(rtwdev, REG_RQPN_CTRL_2, bckp[bckp_idx - 1].val);
+ if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_SDIO)
+ rtw_read32(rtwdev, REG_SDIO_FREE_TXPG);
+
/* Disable beacon related functions */
tmp = rtw_read8(rtwdev, REG_BCN_CTRL);
bckp[bckp_idx].len = 1;
@@ -1062,8 +1095,12 @@ static int txdma_queue_mapping(struct rtw_dev *rtwdev)
if (rtw_chip_wcpu_11ac(rtwdev))
rtw_write32(rtwdev, REG_H2CQ_CSR, BIT_H2CQ_FULL);
- if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB)
+ if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_SDIO) {
+ rtw_read32(rtwdev, REG_SDIO_FREE_TXPG);
+ rtw_write32(rtwdev, REG_SDIO_TX_CTRL, 0);
+ } else if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB) {
rtw_write8_set(rtwdev, REG_TXDMA_PQ_MAP, BIT_RXDMA_ARBBW_EN);
+ }
return 0;
}
--
2.39.0
From: Jernej Skrabec <[email protected]>
Increase LEAVE_LPS_TRY_CNT to give SDIO based chipsets more time to
enter or leave deep sleep mode. SDIO communication is often slower than
PCIe transfers so extra time is needed.
Signed-off-by: Jernej Skrabec <[email protected]>
Signed-off-by: Martin Blumenstingl <[email protected]>
---
drivers/net/wireless/realtek/rtw88/ps.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/wireless/realtek/rtw88/ps.h b/drivers/net/wireless/realtek/rtw88/ps.h
index c194386f6db5..b79bef32b169 100644
--- a/drivers/net/wireless/realtek/rtw88/ps.h
+++ b/drivers/net/wireless/realtek/rtw88/ps.h
@@ -12,7 +12,7 @@
#define POWER_TX_WAKE BIT(1)
#define POWER_MODE_LCLK BIT(0)
-#define LEAVE_LPS_TRY_CNT 5
+#define LEAVE_LPS_TRY_CNT 10
#define LEAVE_LPS_TIMEOUT msecs_to_jiffies(100)
int rtw_enter_ips(struct rtw_dev *rtwdev);
--
2.39.0
From: Jernej Skrabec <[email protected]>
Wire up RTL8822BS chipset support using the new rtw88 SDIO HCI code as
well as the existing RTL8822B chipset code.
Signed-off-by: Jernej Skrabec <[email protected]>
Signed-off-by: Martin Blumenstingl <[email protected]>
---
drivers/net/wireless/realtek/rtw88/Kconfig | 11 ++++++
drivers/net/wireless/realtek/rtw88/Makefile | 3 ++
.../net/wireless/realtek/rtw88/rtw8822bs.c | 34 +++++++++++++++++++
3 files changed, 48 insertions(+)
create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8822bs.c
diff --git a/drivers/net/wireless/realtek/rtw88/Kconfig b/drivers/net/wireless/realtek/rtw88/Kconfig
index cdf9cb478ee2..0cfc68dcc416 100644
--- a/drivers/net/wireless/realtek/rtw88/Kconfig
+++ b/drivers/net/wireless/realtek/rtw88/Kconfig
@@ -45,6 +45,17 @@ config RTW88_8822BE
802.11ac PCIe wireless network adapter
+config RTW88_8822BS
+ tristate "Realtek 8822BS SDIO wireless network adapter"
+ depends on MMC
+ select RTW88_CORE
+ select RTW88_SDIO
+ select RTW88_8822B
+ help
+ Select this option will enable support for 8822BS chipset
+
+ 802.11ac SDIO wireless network adapter
+
config RTW88_8822BU
tristate "Realtek 8822BU USB wireless network adapter"
depends on USB
diff --git a/drivers/net/wireless/realtek/rtw88/Makefile b/drivers/net/wireless/realtek/rtw88/Makefile
index 892cad60ba31..2b8f4dd9707f 100644
--- a/drivers/net/wireless/realtek/rtw88/Makefile
+++ b/drivers/net/wireless/realtek/rtw88/Makefile
@@ -26,6 +26,9 @@ rtw88_8822b-objs := rtw8822b.o rtw8822b_table.o
obj-$(CONFIG_RTW88_8822BE) += rtw88_8822be.o
rtw88_8822be-objs := rtw8822be.o
+obj-$(CONFIG_RTW88_8822BS) += rtw88_8822bs.o
+rtw88_8822bs-objs := rtw8822bs.o
+
obj-$(CONFIG_RTW88_8822BU) += rtw88_8822bu.o
rtw88_8822bu-objs := rtw8822bu.o
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822bs.c b/drivers/net/wireless/realtek/rtw88/rtw8822bs.c
new file mode 100644
index 000000000000..4c74ad2d2e5e
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822bs.c
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+// Copyright(c) Jernej Skrabec <[email protected]>
+
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/module.h>
+#include "sdio.h"
+#include "rtw8822b.h"
+
+static const struct sdio_device_id rtw_8822bs_id_table[] = {
+ {
+ SDIO_DEVICE(SDIO_VENDOR_ID_REALTEK,
+ SDIO_DEVICE_ID_REALTEK_RTW8822BS),
+ .driver_data = (kernel_ulong_t)&rtw8822b_hw_spec,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(sdio, rtw_8822bs_id_table);
+
+static struct sdio_driver rtw_8822bs_driver = {
+ .name = "rtw_8822bs",
+ .probe = rtw_sdio_probe,
+ .remove = rtw_sdio_remove,
+ .id_table = rtw_8822bs_id_table,
+ .drv = {
+ .pm = &rtw_sdio_pm_ops,
+ .shutdown = rtw_sdio_shutdown,
+ }
+};
+module_sdio_driver(rtw_8822bs_driver);
+
+MODULE_AUTHOR("Jernej Skrabec <[email protected]>");
+MODULE_DESCRIPTION("Realtek 802.11ac wireless 8822bs driver");
+MODULE_LICENSE("Dual BSD/GPL");
--
2.39.0
Wire up RTL8821CS chipset support using the new rtw88 SDIO HCI code as
well as the existing RTL8821C chipset code.
Signed-off-by: Martin Blumenstingl <[email protected]>
---
drivers/net/wireless/realtek/rtw88/Kconfig | 11 ++++++
drivers/net/wireless/realtek/rtw88/Makefile | 3 ++
.../net/wireless/realtek/rtw88/rtw8821cs.c | 34 +++++++++++++++++++
3 files changed, 48 insertions(+)
create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8821cs.c
diff --git a/drivers/net/wireless/realtek/rtw88/Kconfig b/drivers/net/wireless/realtek/rtw88/Kconfig
index 6b65da81127f..29eb2f8e0eb7 100644
--- a/drivers/net/wireless/realtek/rtw88/Kconfig
+++ b/drivers/net/wireless/realtek/rtw88/Kconfig
@@ -133,6 +133,17 @@ config RTW88_8821CE
802.11ac PCIe wireless network adapter
+config RTW88_8821CS
+ tristate "Realtek 8821CS SDIO wireless network adapter"
+ depends on MMC
+ select RTW88_CORE
+ select RTW88_SDIO
+ select RTW88_8821C
+ help
+ Select this option will enable support for 8821CS chipset
+
+ 802.11ac SDIO wireless network adapter
+
config RTW88_8821CU
tristate "Realtek 8821CU USB wireless network adapter"
depends on USB
diff --git a/drivers/net/wireless/realtek/rtw88/Makefile b/drivers/net/wireless/realtek/rtw88/Makefile
index 6105c2745bda..82979b30ae8d 100644
--- a/drivers/net/wireless/realtek/rtw88/Makefile
+++ b/drivers/net/wireless/realtek/rtw88/Makefile
@@ -59,6 +59,9 @@ rtw88_8821c-objs := rtw8821c.o rtw8821c_table.o
obj-$(CONFIG_RTW88_8821CE) += rtw88_8821ce.o
rtw88_8821ce-objs := rtw8821ce.o
+obj-$(CONFIG_RTW88_8821CS) += rtw88_8821cs.o
+rtw88_8821cs-objs := rtw8821cs.o
+
obj-$(CONFIG_RTW88_8821CU) += rtw88_8821cu.o
rtw88_8821cu-objs := rtw8821cu.o
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821cs.c b/drivers/net/wireless/realtek/rtw88/rtw8821cs.c
new file mode 100644
index 000000000000..61f82b38cda4
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw88/rtw8821cs.c
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+// Copyright(c) Martin Blumenstingl <[email protected]>
+
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/module.h>
+#include "sdio.h"
+#include "rtw8821c.h"
+
+static const struct sdio_device_id rtw_8821cs_id_table[] = {
+ {
+ SDIO_DEVICE(SDIO_VENDOR_ID_REALTEK,
+ SDIO_DEVICE_ID_REALTEK_RTW8821CS),
+ .driver_data = (kernel_ulong_t)&rtw8821c_hw_spec,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(sdio, rtw_8821cs_id_table);
+
+static struct sdio_driver rtw_8821cs_driver = {
+ .name = "rtw_8821cs",
+ .probe = rtw_sdio_probe,
+ .remove = rtw_sdio_remove,
+ .id_table = rtw_8821cs_id_table,
+ .drv = {
+ .pm = &rtw_sdio_pm_ops,
+ .shutdown = rtw_sdio_shutdown,
+ }
+};
+module_sdio_driver(rtw_8821cs_driver);
+
+MODULE_AUTHOR("Martin Blumenstingl <[email protected]>");
+MODULE_DESCRIPTION("Realtek 802.11ac wireless 8821cs driver");
+MODULE_LICENSE("Dual BSD/GPL");
--
2.39.0
For SDIO host controllers with DMA support the TX buffer physical memory
address need to be aligned at an 8-byte boundary. Reserve 8 bytes of
extra TX headroom so we can align the data without re-allocating the
transmit buffer.
While here, also remove the TODO comment regarding extra headroom for
USB and SDIO. For SDIO the extra headroom is now handled and for USB it
was not needed so far.
Signed-off-by: Martin Blumenstingl <[email protected]>
---
drivers/net/wireless/realtek/rtw88/main.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c
index 9435cb43d1dc..bcdf1f8c8450 100644
--- a/drivers/net/wireless/realtek/rtw88/main.c
+++ b/drivers/net/wireless/realtek/rtw88/main.c
@@ -2161,9 +2161,11 @@ int rtw_register_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw)
int max_tx_headroom = 0;
int ret;
- /* TODO: USB & SDIO may need extra room? */
max_tx_headroom = rtwdev->chip->tx_pkt_desc_sz;
+ if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_SDIO)
+ max_tx_headroom += RTW_SDIO_DATA_PTR_ALIGN;
+
hw->extra_tx_headroom = max_tx_headroom;
hw->queues = IEEE80211_NUM_ACS;
hw->txq_data_size = sizeof(struct rtw_txq);
--
2.39.0
Wire up RTL8822CS chipset support using the new rtw88 SDIO HCI code as
well as the existing RTL8822C chipset code.
Signed-off-by: Martin Blumenstingl <[email protected]>
---
drivers/net/wireless/realtek/rtw88/Kconfig | 11 ++++++
drivers/net/wireless/realtek/rtw88/Makefile | 3 ++
.../net/wireless/realtek/rtw88/rtw8822cs.c | 34 +++++++++++++++++++
3 files changed, 48 insertions(+)
create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8822cs.c
diff --git a/drivers/net/wireless/realtek/rtw88/Kconfig b/drivers/net/wireless/realtek/rtw88/Kconfig
index 0cfc68dcc416..6b65da81127f 100644
--- a/drivers/net/wireless/realtek/rtw88/Kconfig
+++ b/drivers/net/wireless/realtek/rtw88/Kconfig
@@ -78,6 +78,17 @@ config RTW88_8822CE
802.11ac PCIe wireless network adapter
+config RTW88_8822CS
+ tristate "Realtek 8822CS SDIO wireless network adapter"
+ depends on MMC
+ select RTW88_CORE
+ select RTW88_SDIO
+ select RTW88_8822C
+ help
+ Select this option will enable support for 8822CS chipset
+
+ 802.11ac SDIO wireless network adapter
+
config RTW88_8822CU
tristate "Realtek 8822CU USB wireless network adapter"
depends on USB
diff --git a/drivers/net/wireless/realtek/rtw88/Makefile b/drivers/net/wireless/realtek/rtw88/Makefile
index 2b8f4dd9707f..6105c2745bda 100644
--- a/drivers/net/wireless/realtek/rtw88/Makefile
+++ b/drivers/net/wireless/realtek/rtw88/Makefile
@@ -38,6 +38,9 @@ rtw88_8822c-objs := rtw8822c.o rtw8822c_table.o
obj-$(CONFIG_RTW88_8822CE) += rtw88_8822ce.o
rtw88_8822ce-objs := rtw8822ce.o
+obj-$(CONFIG_RTW88_8822CS) += rtw88_8822cs.o
+rtw88_8822cs-objs := rtw8822cs.o
+
obj-$(CONFIG_RTW88_8822CU) += rtw88_8822cu.o
rtw88_8822cu-objs := rtw8822cu.o
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822cs.c b/drivers/net/wireless/realtek/rtw88/rtw8822cs.c
new file mode 100644
index 000000000000..3d7279d70aa9
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822cs.c
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+// Copyright(c) Martin Blumenstingl <[email protected]>
+
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/module.h>
+#include "sdio.h"
+#include "rtw8822c.h"
+
+static const struct sdio_device_id rtw_8822cs_id_table[] = {
+ {
+ SDIO_DEVICE(SDIO_VENDOR_ID_REALTEK,
+ SDIO_DEVICE_ID_REALTEK_RTW8822CS),
+ .driver_data = (kernel_ulong_t)&rtw8822c_hw_spec,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(sdio, rtw_8822cs_id_table);
+
+static struct sdio_driver rtw_8822cs_driver = {
+ .name = "rtw_8822cs",
+ .probe = rtw_sdio_probe,
+ .remove = rtw_sdio_remove,
+ .id_table = rtw_8822cs_id_table,
+ .drv = {
+ .pm = &rtw_sdio_pm_ops,
+ .shutdown = rtw_sdio_shutdown,
+ }
+};
+module_sdio_driver(rtw_8822cs_driver);
+
+MODULE_AUTHOR("Martin Blumenstingl <[email protected]>");
+MODULE_DESCRIPTION("Realtek 802.11ac wireless 8822cs driver");
+MODULE_LICENSE("Dual BSD/GPL");
--
2.39.0
Initialize the rpwm_addr and cpwm_addr for power-saving support on SDIO
based chipsets.
Signed-off-by: Martin Blumenstingl <[email protected]>
---
drivers/net/wireless/realtek/rtw88/main.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c
index 888427cf3bdf..9435cb43d1dc 100644
--- a/drivers/net/wireless/realtek/rtw88/main.c
+++ b/drivers/net/wireless/realtek/rtw88/main.c
@@ -18,6 +18,7 @@
#include "debug.h"
#include "bf.h"
#include "sar.h"
+#include "sdio.h"
bool rtw_disable_lps_deep_mode;
EXPORT_SYMBOL(rtw_disable_lps_deep_mode);
@@ -1783,6 +1784,10 @@ static int rtw_chip_parameter_setup(struct rtw_dev *rtwdev)
rtwdev->hci.rpwm_addr = 0x03d9;
rtwdev->hci.cpwm_addr = 0x03da;
break;
+ case RTW_HCI_TYPE_SDIO:
+ rtwdev->hci.rpwm_addr = REG_SDIO_HRPWM1;
+ rtwdev->hci.cpwm_addr = REG_SDIO_HCPWM1_V2;
+ break;
case RTW_HCI_TYPE_USB:
rtwdev->hci.rpwm_addr = 0xfe58;
rtwdev->hci.cpwm_addr = 0xfe57;
--
2.39.0
> -----Original Message-----
> From: Martin Blumenstingl <[email protected]>
> Sent: Wednesday, December 28, 2022 7:30 AM
> To: [email protected]
> Cc: Yan-Hsuan Chuang <[email protected]>; Kalle Valo <[email protected]>; Ulf Hansson
> <[email protected]>; [email protected]; [email protected];
> [email protected]; Chris Morgan <[email protected]>; Nitin Gupta <[email protected]>;
> Neo Jou <[email protected]>; Ping-Ke Shih <[email protected]>; Jernej Skrabec <[email protected]>;
> Martin Blumenstingl <[email protected]>
> Subject: [RFC PATCH v1 06/19] rtw88: rtw8821c: Add support for parsing the RTL8821CS (SDIO) efuse
>
> The efuse of the SDIO RTL8821CS chip has only one known member: the mac
> address is at offset 0x11a. Add a struct rtw8821cs_efuse describing this
> and use it for copying the mac address when the SDIO bus is used.
>
> Signed-off-by: Martin Blumenstingl <[email protected]>
> ---
> drivers/net/wireless/realtek/rtw88/rtw8821c.c | 9 +++++++++
> drivers/net/wireless/realtek/rtw88/rtw8821c.h | 6 ++++++
> 2 files changed, 15 insertions(+)
>
> diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821c.c
> b/drivers/net/wireless/realtek/rtw88/rtw8821c.c
> index 17f800f6efbd..dd01b22f9770 100644
> --- a/drivers/net/wireless/realtek/rtw88/rtw8821c.c
> +++ b/drivers/net/wireless/realtek/rtw88/rtw8821c.c
> @@ -26,6 +26,12 @@ static void rtw8821ce_efuse_parsing(struct rtw_efuse *efuse,
> ether_addr_copy(efuse->addr, map->e.mac_addr);
> }
>
> +static void rtw8821cs_efuse_parsing(struct rtw_efuse *efuse,
> + struct rtw8821c_efuse *map)
> +{
> + ether_addr_copy(efuse->addr, map->s.mac_addr);
> +}
> +
> static void rtw8821cu_efuse_parsing(struct rtw_efuse *efuse,
> struct rtw8821c_efuse *map)
> {
> @@ -74,6 +80,9 @@ static int rtw8821c_read_efuse(struct rtw_dev *rtwdev, u8 *log_map)
> case RTW_HCI_TYPE_PCIE:
> rtw8821ce_efuse_parsing(efuse, map);
> break;
> + case RTW_HCI_TYPE_SDIO:
> + rtw8821cs_efuse_parsing(efuse, map);
> + break;
> case RTW_HCI_TYPE_USB:
> rtw8821cu_efuse_parsing(efuse, map);
> break;
> diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821c.h
> b/drivers/net/wireless/realtek/rtw88/rtw8821c.h
> index 1c81260f3a54..1deea54575b5 100644
> --- a/drivers/net/wireless/realtek/rtw88/rtw8821c.h
> +++ b/drivers/net/wireless/realtek/rtw88/rtw8821c.h
> @@ -65,6 +65,11 @@ struct rtw8821ce_efuse {
> u8 res7;
> };
>
> +struct rtw8821cs_efuse {
> + u8 res4[0x4a]; /* 0xd0 */
> + u8 mac_addr[ETH_ALEN]; /* 0x11a */
> +};
> +
This struct should be __packed, as well as rtw8821c_efuse.
Would you mind to create additional patch to add __packed to these struct of
efuse layout?
> struct rtw8821c_efuse {
> __le16 rtl_id;
> u8 res0[0x0e];
> @@ -93,6 +98,7 @@ struct rtw8821c_efuse {
> u8 res[3];
> union {
> struct rtw8821ce_efuse e;
> + struct rtw8821cs_efuse s;
> struct rtw8821cu_efuse u;
> };
> };
> --
> 2.39.0
> -----Original Message-----
> From: Martin Blumenstingl <[email protected]>
> Sent: Wednesday, December 28, 2022 7:30 AM
> To: [email protected]
> Cc: Yan-Hsuan Chuang <[email protected]>; Kalle Valo <[email protected]>; Ulf Hansson
> <[email protected]>; [email protected]; [email protected];
> [email protected]; Chris Morgan <[email protected]>; Nitin Gupta <[email protected]>;
> Neo Jou <[email protected]>; Ping-Ke Shih <[email protected]>; Jernej Skrabec <[email protected]>;
> Martin Blumenstingl <[email protected]>
> Subject: [RFC PATCH v1 12/19] rtw88: sdio: Add HCI implementation for SDIO based chipsets
>
> Add a sub-driver for SDIO based chipsets which implements the following
> functionality:
> - register accessors for 8, 16 and 32 bits for all states of the card
> (including usage of 4x 8 bit access for one 32 bit buffer if the card
> is not fully powered on yet - or if it's fully powered on then 1x 32
> bit access is used)
> - checking whether there's space in the TX FIFO queue to transmit data
> - transfers from the host to the device for actual network traffic,
> reserved pages (for firmware download) and H2C (host-to-card)
> transfers
> - receiving data from the device
> - deep power saving state
>
> The transmit path is optimized so DMA-capable SDIO host controllers can
> directly use the buffers provided because the buffer's physical
> addresses are 8 byte aligned.
>
> The receive path is prepared to support RX aggregation where the
> chipset combines multiple MAC frames into one bigger buffer to reduce
> SDIO transfer overhead.
>
> Co-developed-by: Jernej Skrabec <[email protected]>
> Signed-off-by: Jernej Skrabec <[email protected]>
> Signed-off-by: Martin Blumenstingl <[email protected]>
> ---
> drivers/net/wireless/realtek/rtw88/Kconfig | 3 +
> drivers/net/wireless/realtek/rtw88/Makefile | 3 +
> drivers/net/wireless/realtek/rtw88/debug.h | 1 +
> drivers/net/wireless/realtek/rtw88/mac.h | 1 -
> drivers/net/wireless/realtek/rtw88/reg.h | 10 +
> drivers/net/wireless/realtek/rtw88/sdio.c | 1242 +++++++++++++++++++
> drivers/net/wireless/realtek/rtw88/sdio.h | 175 +++
> 7 files changed, 1434 insertions(+), 1 deletion(-)
> create mode 100644 drivers/net/wireless/realtek/rtw88/sdio.c
> create mode 100644 drivers/net/wireless/realtek/rtw88/sdio.h
>
[...]
> +
> +static void rtw_sdio_writel(struct rtw_sdio *rtwsdio, u32 val,
> + u32 addr, int *ret)
> +{
> + u8 buf[4];
> + int i;
> +
> + if (!(addr & 3) && rtwsdio->is_powered_on) {
> + sdio_writel(rtwsdio->sdio_func, val, addr, ret);
> + return;
> + }
> +
> + *(__le32 *)buf = cpu_to_le32(val);
> +
> + for (i = 0; i < 4; i++) {
> + sdio_writeb(rtwsdio->sdio_func, buf[i], addr + i, ret);
> + if (*ret)
Do you need some messages to know something wrong?
> + return;
> + }
> +}
> +
> +static u32 rtw_sdio_readl(struct rtw_sdio *rtwsdio, u32 addr, int *ret)
> +{
> + u8 buf[4];
> + int i;
> +
> + if (!(addr & 3) && rtwsdio->is_powered_on)
> + return sdio_readl(rtwsdio->sdio_func, addr, ret);
> +
> + for (i = 0; i < 4; i++) {
> + buf[i] = sdio_readb(rtwsdio->sdio_func, addr + i, ret);
> + if (*ret)
> + return 0;
> + }
> +
> + return le32_to_cpu(*(__le32 *)buf);
> +}
> +
> +static u8 rtw_sdio_read_indirect8(struct rtw_dev *rtwdev, u32 addr, int *ret)
> +{
> + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> + u32 reg_cfg, reg_data;
> + int retry;
> + u8 tmp;
> +
> + reg_cfg = rtw_sdio_to_bus_offset(rtwdev, REG_SDIO_INDIRECT_REG_CFG);
> + reg_data = rtw_sdio_to_bus_offset(rtwdev, REG_SDIO_INDIRECT_REG_DATA);
> +
> + rtw_sdio_writel(rtwsdio, BIT(19) | addr, reg_cfg, ret);
> + if (*ret)
> + return 0;
> +
> + for (retry = 0; retry < RTW_SDIO_INDIRECT_RW_RETRIES; retry++) {
> + tmp = sdio_readb(rtwsdio->sdio_func, reg_cfg + 2, ret);
> + if (!ret && tmp & BIT(4))
'ret' is pointer, do you need '*' ?
if (!*ret && tmp & BIT(4))
As I look into sdio_readb(), it use 'int *err_ret' as arugment.
Would you like to change ' int *ret' to 'int *err_ret'?
It could help to misunderstand.
> + break;
> + }
> +
> + if (*ret)
> + return 0;
> +
> + return sdio_readb(rtwsdio->sdio_func, reg_data, ret);
> +}
> +
[...]
> +
> +static void rtw_sdio_rx_aggregation(struct rtw_dev *rtwdev, bool enable)
> +{
> + u8 size, timeout;
> +
> + if (enable) {
> + if (rtwdev->chip->id == RTW_CHIP_TYPE_8822C) {
> + size = 0xff;
> + timeout = 0x20;
> + } else {
> + size = 0x6;
> + timeout = 0x6;
> + }
> +
> + /* Make the firmware honor the size limit configured below */
> + rtw_write32_set(rtwdev, REG_RXDMA_AGG_PG_TH, BIT_EN_PRE_CALC);
> +
> + rtw_write8_set(rtwdev, REG_TXDMA_PQ_MAP, BIT_RXDMA_AGG_EN);
> +
> + rtw_write16(rtwdev, REG_RXDMA_AGG_PG_TH, size |
> + (timeout << BIT_SHIFT_DMA_AGG_TO_V1));
BIT_RXDMA_AGG_PG_TH GENMASK(7, 0) // for size
BIT_DMA_AGG_TO_V1 GENMASK(15, 8) // for timeout
> +
> + rtw_write8_set(rtwdev, REG_RXDMA_MODE, BIT_DMA_MODE);
> + } else {
> + rtw_write32_clr(rtwdev, REG_RXDMA_AGG_PG_TH, BIT_EN_PRE_CALC);
> + rtw_write8_clr(rtwdev, REG_TXDMA_PQ_MAP, BIT_RXDMA_AGG_EN);
> + rtw_write8_clr(rtwdev, REG_RXDMA_MODE, BIT_DMA_MODE);
> + }
> +}
> +
> +static void rtw_sdio_enable_interrupt(struct rtw_dev *rtwdev)
> +{
> + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> +
> + rtw_write32(rtwdev, REG_SDIO_HIMR, rtwsdio->irq_mask);
> +}
> +
> +static void rtw_sdio_disable_interrupt(struct rtw_dev *rtwdev)
> +{
> + rtw_write32(rtwdev, REG_SDIO_HIMR, 0x0);
> +}
> +
> +static u8 rtw_sdio_get_tx_qsel(struct rtw_dev *rtwdev, struct sk_buff *skb,
> + u8 queue)
> +{
> + switch (queue) {
> + case RTW_TX_QUEUE_BCN:
> + return TX_DESC_QSEL_BEACON;
> + case RTW_TX_QUEUE_H2C:
> + return TX_DESC_QSEL_H2C;
> + case RTW_TX_QUEUE_MGMT:
> + if (rtw_chip_wcpu_11n(rtwdev))
> + return TX_DESC_QSEL_HIGH;
> + else
> + return TX_DESC_QSEL_MGMT;
> + case RTW_TX_QUEUE_HI0:
> + return TX_DESC_QSEL_HIGH;
> + default:
> + return skb->priority;
> + }
> +};
no need ';'
[...]
> +
> +static void rtw_sdio_rx_isr(struct rtw_dev *rtwdev)
> +{
> + u32 rx_len;
> +
> + while (true) {
add a limit to prevent infinite loop.
> + if (rtw_chip_wcpu_11n(rtwdev))
> + rx_len = rtw_read16(rtwdev, REG_SDIO_RX0_REQ_LEN);
> + else
> + rx_len = rtw_read32(rtwdev, REG_SDIO_RX0_REQ_LEN);
> +
> + if (!rx_len)
> + break;
> +
> + rtw_sdio_rxfifo_recv(rtwdev, rx_len);
> + }
> +}
> +
[...]
> +
> +static void rtw_sdio_process_tx_queue(struct rtw_dev *rtwdev,
> + enum rtw_tx_queue_type queue)
> +{
> + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> + struct sk_buff *skb;
> + int ret;
> +
> + while (true) {
Can we have a limit?
> + skb = skb_dequeue(&rtwsdio->tx_queue[queue]);
> + if (!skb)
> + break;
> +
> + ret = rtw_sdio_write_port(rtwdev, skb, queue);
> + if (ret) {
> + skb_queue_head(&rtwsdio->tx_queue[queue], skb);
> + break;
> + }
> +
> + if (queue <= RTW_TX_QUEUE_VO)
> + rtw_sdio_indicate_tx_status(rtwdev, skb);
> + else
> + dev_kfree_skb_any(skb);
> + }
> +}
> +
[...]
Hi Ping-Ke,
as always: thank you so much for taking time to go through this!
On Wed, Dec 28, 2022 at 10:39 AM Ping-Ke Shih <[email protected]> wrote:
[...]
>
> > +
> > +static void rtw_sdio_writel(struct rtw_sdio *rtwsdio, u32 val,
> > + u32 addr, int *ret)
> > +{
> > + u8 buf[4];
> > + int i;
> > +
> > + if (!(addr & 3) && rtwsdio->is_powered_on) {
> > + sdio_writel(rtwsdio->sdio_func, val, addr, ret);
> > + return;
> > + }
> > +
> > + *(__le32 *)buf = cpu_to_le32(val);
> > +
> > + for (i = 0; i < 4; i++) {
> > + sdio_writeb(rtwsdio->sdio_func, buf[i], addr + i, ret);
> > + if (*ret)
>
> Do you need some messages to know something wrong?
It's not obvious but we're already logging that something went wrong.
The messages are logged in rtw_sdio_{read,write}{8,16,32}.
We do this because there's multiple ways to access data (direct,
indirect, ...) and some of them require multiple register operations.
So we print one message in the end.
[...]
> > +static u8 rtw_sdio_read_indirect8(struct rtw_dev *rtwdev, u32 addr, int *ret)
> > +{
> > + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> > + u32 reg_cfg, reg_data;
> > + int retry;
> > + u8 tmp;
> > +
> > + reg_cfg = rtw_sdio_to_bus_offset(rtwdev, REG_SDIO_INDIRECT_REG_CFG);
> > + reg_data = rtw_sdio_to_bus_offset(rtwdev, REG_SDIO_INDIRECT_REG_DATA);
> > +
> > + rtw_sdio_writel(rtwsdio, BIT(19) | addr, reg_cfg, ret);
> > + if (*ret)
> > + return 0;
> > +
> > + for (retry = 0; retry < RTW_SDIO_INDIRECT_RW_RETRIES; retry++) {
> > + tmp = sdio_readb(rtwsdio->sdio_func, reg_cfg + 2, ret);
> > + if (!ret && tmp & BIT(4))
>
> 'ret' is pointer, do you need '*' ?
Well spotted - thank you!
[...]
> As I look into sdio_readb(), it use 'int *err_ret' as arugment.
> Would you like to change ' int *ret' to 'int *err_ret'?
> It could help to misunderstand.
Sure, I'll do that
[...]
> > + rtw_write16(rtwdev, REG_RXDMA_AGG_PG_TH, size |
> > + (timeout << BIT_SHIFT_DMA_AGG_TO_V1));
>
> BIT_RXDMA_AGG_PG_TH GENMASK(7, 0) // for size
> BIT_DMA_AGG_TO_V1 GENMASK(15, 8) // for timeout
Thanks, I'll use these
[...]
> > +static void rtw_sdio_rx_isr(struct rtw_dev *rtwdev)
> > +{
> > + u32 rx_len;
> > +
> > + while (true) {
>
> add a limit to prevent infinite loop.
Do you have any recommendations on how many packets to pull in one go?
My thinking is: pulling to little data at once can hurt performance
[...]
>
> > +
> > +static void rtw_sdio_process_tx_queue(struct rtw_dev *rtwdev,
> > + enum rtw_tx_queue_type queue)
> > +{
> > + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> > + struct sk_buff *skb;
> > + int ret;
> > +
> > + while (true) {
>
> Can we have a limit?
Similar to the question above: do you have any recommendations on how
many packets (per queue) to send in one go?
Best regards,
Martin
> -----Original Message-----
> From: Martin Blumenstingl <[email protected]>
> Sent: Wednesday, December 28, 2022 8:00 PM
> To: Ping-Ke Shih <[email protected]>
> Cc: [email protected]; Yan-Hsuan Chuang <[email protected]>; Kalle Valo
> <[email protected]>; Ulf Hansson <[email protected]>; [email protected];
> [email protected]; [email protected]; Chris Morgan <[email protected]>; Nitin Gupta
> <[email protected]>; Neo Jou <[email protected]>; Jernej Skrabec <[email protected]>
> Subject: Re: [RFC PATCH v1 12/19] rtw88: sdio: Add HCI implementation for SDIO based chipsets
>
> Hi Ping-Ke,
>
> as always: thank you so much for taking time to go through this!
>
> On Wed, Dec 28, 2022 at 10:39 AM Ping-Ke Shih <[email protected]> wrote:
[...]
> > > +static void rtw_sdio_rx_isr(struct rtw_dev *rtwdev)
> > > +{
> > > + u32 rx_len;
> > > +
> > > + while (true) {
> >
> > add a limit to prevent infinite loop.
> Do you have any recommendations on how many packets to pull in one go?
> My thinking is: pulling to little data at once can hurt performance
This is to prevent unexpected things happen, and normally we receive/send
all packets at once like while(true) code. So, maybe we can have rough limit,
like 512 that would be enough for most cases. To have accurate number, you
can do experiments with the highest performance to see the loop count in
real usage, and 5 or 10 times count as limit.
However, when I reviewed rtw88 USB patches, I found we can't always break
the loop, because it could only one chance to free skb(s). So, you should
make sure we really can break the loop logically, and then decide which
limit is suitable for us.
>
> [...]
> >
> > > +
> > > +static void rtw_sdio_process_tx_queue(struct rtw_dev *rtwdev,
> > > + enum rtw_tx_queue_type queue)
> > > +{
> > > + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> > > + struct sk_buff *skb;
> > > + int ret;
> > > +
> > > + while (true) {
> >
> > Can we have a limit?
> Similar to the question above: do you have any recommendations on how
> many packets (per queue) to send in one go?
>
Ping-Ke
> -----Original Message-----
> From: Martin Blumenstingl <[email protected]>
> Sent: Wednesday, December 28, 2022 7:30 AM
> To: [email protected]
> Cc: Yan-Hsuan Chuang <[email protected]>; Kalle Valo <[email protected]>; Ulf Hansson
> <[email protected]>; [email protected]; [email protected];
> [email protected]; Chris Morgan <[email protected]>; Nitin Gupta <[email protected]>;
> Neo Jou <[email protected]>; Ping-Ke Shih <[email protected]>; Jernej Skrabec <[email protected]>;
> Martin Blumenstingl <[email protected]>
> Subject: [RFC PATCH v1 18/19] rtw88: Add support for the SDIO based RTL8822CS chipset
>
> Wire up RTL8822CS chipset support using the new rtw88 SDIO HCI code as
> well as the existing RTL8822C chipset code.
>
> Signed-off-by: Martin Blumenstingl <[email protected]>
> ---
> drivers/net/wireless/realtek/rtw88/Kconfig | 11 ++++++
> drivers/net/wireless/realtek/rtw88/Makefile | 3 ++
> .../net/wireless/realtek/rtw88/rtw8822cs.c | 34 +++++++++++++++++++
> 3 files changed, 48 insertions(+)
> create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8822cs.c
>
[...]
> diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822cs.c
> b/drivers/net/wireless/realtek/rtw88/rtw8822cs.c
> new file mode 100644
> index 000000000000..3d7279d70aa9
> --- /dev/null
> +++ b/drivers/net/wireless/realtek/rtw88/rtw8822cs.c
> @@ -0,0 +1,34 @@
> +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
> +// Copyright(c) Martin Blumenstingl <[email protected]>
Normally, we should use '/* ... */' style comment. The exception is
'// SPDX-License-Identifier: ...' in *.c
Therefore, here should be:
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/* Copyright(c) Martin Blumenstingl <[email protected]>
*/
As well as other rtw88*s.c
--
Ping-Ke
> -----Original Message-----
> From: Martin Blumenstingl <[email protected]>
> Sent: Wednesday, December 28, 2022 7:30 AM
> To: [email protected]
> Cc: Yan-Hsuan Chuang <[email protected]>; Kalle Valo <[email protected]>; Ulf Hansson
> <[email protected]>; [email protected]; [email protected];
> [email protected]; Chris Morgan <[email protected]>; Nitin Gupta <[email protected]>;
> Neo Jou <[email protected]>; Ping-Ke Shih <[email protected]>; Jernej Skrabec <[email protected]>;
> Martin Blumenstingl <[email protected]>
> Subject: [RFC PATCH v1 00/19] rtw88: Add SDIO support
>
> Recently the rtw88 driver has gained locking support for the "slow" bus
> types (USB, SDIO) as part of USB support. Thanks to everyone who helped
> make this happen!
>
> Based on the USB work (especially the locking part and various
> bugfixes) this series adds support for SDIO based cards. It's the
> result of a collaboration between Jernej and myself. Neither of us has
> access to the rtw88 datasheets. All of our work is based on studying
> the RTL8822BS and RTL8822CS vendor drivers and trial and error.
>
> Jernej and myself have tested this with RTL8822BS and RTL8822CS cards.
> Other users have confirmed that RTL8821CS support is working as well.
> RTL8723DS may also work (we tried our best to handle rtw_chip_wcpu_11n
> where needed) but has not been tested at this point.
>
> Jernej's results with a RTL8822BS:
> - Main functionality works
> - Had a case where no traffic got across the link until he issued a
> scan
>
> My results with a RTL8822CS:
> - 2.4GHz and 5GHz bands are both working
> - TX throughput on a 5GHz network is between 50 Mbit/s and 90 Mbit/s
> - RX throughput on a 5GHz network is at 19 Mbit/s
I have a suggestion about RX throughput, please check below registers with
vendor driver:
REG_RXDMA_AGG_PG_TH
REG_TXDMA_PQ_MAP(0x10c) BIT_RXDMA_AGG_EN (bit2)
REG_RXDMA_MODE(0290) BIT_DMA_MODE (bit1)
Try to adjust AGG_PG_TH to see if it can help.
--
Ping-Ke
Hi Ping-Ke,
On Thu, Dec 29, 2022 at 2:15 AM Ping-Ke Shih <[email protected]> wrote:
[...]
> > + if (rtw_sdio_is_sdio30_supported(rtwdev))
> > + rtw_write8_set(rtwdev, REG_HCI_OPT_CTRL + 2, BIT(2));
>
> BIT_USB_LPM_ACT_EN BIT(10) // reg_addr +2, so bit >> 8
The ones above are clear to me, thank you.
But for this one I have a question: don't we need BIT(18) for this one
and then bit >> 16?
reg_addr + 0: bits 0..7
reg_addr + 1: bits 8..15
reg_addr + 2: bits 16..23
Best regards,
Martin
Hi Ping-Ke,
thanks again for all your input!
On Thu, Dec 29, 2022 at 5:19 AM Ping-Ke Shih <[email protected]> wrote:
[...]
> > - RX throughput on a 5GHz network is at 19 Mbit/s
>
> I have a suggestion about RX throughput, please check below registers with
> vendor driver:
>
> REG_RXDMA_AGG_PG_TH
> REG_TXDMA_PQ_MAP(0x10c) BIT_RXDMA_AGG_EN (bit2)
> REG_RXDMA_MODE(0290) BIT_DMA_MODE (bit1)
Unfortunately I didn't manage to get the vendor driver to work with
mainline Linux.
The Android installation on my board (which is how it was shipped)
uses the vendor driver but unlike some Amlogic code the Realtek
(vendor) wireless driver does not allow reading arbitrary registers
through sysfs.
So I can't check the values that the vendor driver uses.
> Try to adjust AGG_PG_TH to see if it can help.
I tried a few values and I can say that it does change the RX
throughput, but the result is always lower than 19 Mbit/s, meaning
that it's worse than RX aggregation disabled (on my RTL8822CS).
Currently we're disabling RX aggregation in the driver. But Jernej
mentioned previously that for his RTL8822BS he found that RX
aggregation seems to improve performance.
Independent of this I did some investigation on my own and found that
when reducing the TX throughput the RX throughput increases.
For this I tried using ieee80211_{stop,wake}_queue() in the sdio.c HCI
sub-driver.
RX throughput is now at 23.5 Mbit/s (that +25% compared to before) on
my RTL8822CS (with RX aggregation still disabled, just like in the 19
Mbit/s test).
Unfortunately TX throughput is now way below 10 Mbit/s.
Additionally I think that the antenna of my board is worse than my
access point's antenna. So TX from rtw88 to my AP may be faster
(because the AP can "hear better") than RX (rtw88 "hearing is worse").
For today I'm tired and will stop here.
Best regards,
Martin
[0] https://github.com/xdarklight/linux/commit/3f2e6b9cd40dc785b5c72dbc9c8b471a2e205344
On Fri, 2022-12-30 at 00:18 +0100, Martin Blumenstingl wrote:
> Hi Ping-Ke,
>
> thanks again for all your input!
>
> On Thu, Dec 29, 2022 at 5:19 AM Ping-Ke Shih <[email protected]> wrote:
> [...]
> > > - RX throughput on a 5GHz network is at 19 Mbit/s
> >
> > I have a suggestion about RX throughput, please check below registers with
> > vendor driver:
> >
> > REG_RXDMA_AGG_PG_TH
> > REG_TXDMA_PQ_MAP(0x10c) BIT_RXDMA_AGG_EN (bit2)
> > REG_RXDMA_MODE(0290) BIT_DMA_MODE (bit1)
> Unfortunately I didn't manage to get the vendor driver to work with
> mainline Linux.
> The Android installation on my board (which is how it was shipped)
> uses the vendor driver but unlike some Amlogic code the Realtek
> (vendor) wireless driver does not allow reading arbitrary registers
> through sysfs.
> So I can't check the values that the vendor driver uses.
>
> > Try to adjust AGG_PG_TH to see if it can help.
> I tried a few values and I can say that it does change the RX
> throughput, but the result is always lower than 19 Mbit/s, meaning
> that it's worse than RX aggregation disabled (on my RTL8822CS).
> Currently we're disabling RX aggregation in the driver. But Jernej
> mentioned previously that for his RTL8822BS he found that RX
> aggregation seems to improve performance.
>
> Independent of this I did some investigation on my own and found that
> when reducing the TX throughput the RX throughput increases.
> For this I tried using ieee80211_{stop,wake}_queue() in the sdio.c HCI
> sub-driver.
> RX throughput is now at 23.5 Mbit/s (that +25% compared to before) on
> my RTL8822CS (with RX aggregation still disabled, just like in the 19
> Mbit/s test).
> Unfortunately TX throughput is now way below 10 Mbit/s.
>
> Additionally I think that the antenna of my board is worse than my
> access point's antenna. So TX from rtw88 to my AP may be faster
> (because the AP can "hear better") than RX (rtw88 "hearing is worse").
>
Without equipment like CAT-C, it is hard to investigate SDIO usb
aggregation, so I suggest to capture WiFi packets in the air to make
sure things work as expected. After that, we can focus on bus
aggregation tuning.
The instructions to use another WiFi card to capture packets are:
1. sudo iw dev wlan0 interface add mon0 type monitor
2. sudo wireshark // select mon0 to capture
Please check AMPDU and AMSDU size during doing TX/RX throughput test.
Normally, expected AMSDU size is 3000+ bytes, and AMPDU number is
around 32 MSDUs. If RX is too slow resulting in buffer overflow,
AP could resend (check sequence number and 'R' bit, or BA of 8822CS).
Also, check TX/RX rates to know if RF calibration and PHY dynamic
mechanism work well. Normally, with 50cm distance long from AP,
it must yield the highest rate, no doubt.
I hope this can narrow down the problems you met.
---
Ping-Ke
On Wed, 28 Dec 2022 at 00:30, Martin Blumenstingl
<[email protected]> wrote:
>
> Add the SDIO vendor ID for Realtek and some device IDs extracted from
> their GPL vendor driver. This will be useful in the future when the
> rtw88 driver gains support for these chips.
>
> Signed-off-by: Martin Blumenstingl <[email protected]>
I assume it's easier if Kalle picks up this patch, along with the series. So:
Acked-by: Ulf Hansson <[email protected]>
Kind regards
Uffe
> ---
> include/linux/mmc/sdio_ids.h | 9 +++++++++
> 1 file changed, 9 insertions(+)
>
> diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h
> index 74f9d9a6d330..bba39d4565da 100644
> --- a/include/linux/mmc/sdio_ids.h
> +++ b/include/linux/mmc/sdio_ids.h
> @@ -111,6 +111,15 @@
> #define SDIO_VENDOR_ID_MICROCHIP_WILC 0x0296
> #define SDIO_DEVICE_ID_MICROCHIP_WILC1000 0x5347
>
> +#define SDIO_VENDOR_ID_REALTEK 0x024c
> +#define SDIO_DEVICE_ID_REALTEK_RTW8723BS 0xb723
> +#define SDIO_DEVICE_ID_REALTEK_RTW8723DS 0xd723
> +#define SDIO_DEVICE_ID_REALTEK_RTW8821BS 0xb821
> +#define SDIO_DEVICE_ID_REALTEK_RTW8821CS 0xc821
> +#define SDIO_DEVICE_ID_REALTEK_RTW8821DS 0xd821
> +#define SDIO_DEVICE_ID_REALTEK_RTW8822BS 0xb822
> +#define SDIO_DEVICE_ID_REALTEK_RTW8822CS 0xc822
> +
> #define SDIO_VENDOR_ID_SIANO 0x039a
> #define SDIO_DEVICE_ID_SIANO_NOVA_B0 0x0201
> #define SDIO_DEVICE_ID_SIANO_NICE 0x0202
> --
> 2.39.0
>
On Wed, 28 Dec 2022 at 00:30, Martin Blumenstingl
<[email protected]> wrote:
>
> Add a sub-driver for SDIO based chipsets which implements the following
> functionality:
> - register accessors for 8, 16 and 32 bits for all states of the card
> (including usage of 4x 8 bit access for one 32 bit buffer if the card
> is not fully powered on yet - or if it's fully powered on then 1x 32
> bit access is used)
> - checking whether there's space in the TX FIFO queue to transmit data
> - transfers from the host to the device for actual network traffic,
> reserved pages (for firmware download) and H2C (host-to-card)
> transfers
> - receiving data from the device
> - deep power saving state
>
> The transmit path is optimized so DMA-capable SDIO host controllers can
> directly use the buffers provided because the buffer's physical
> addresses are 8 byte aligned.
>
> The receive path is prepared to support RX aggregation where the
> chipset combines multiple MAC frames into one bigger buffer to reduce
> SDIO transfer overhead.
>
> Co-developed-by: Jernej Skrabec <[email protected]>
> Signed-off-by: Jernej Skrabec <[email protected]>
> Signed-off-by: Martin Blumenstingl <[email protected]>
This looks good to me, so feel free to add:
Reviewed-by: Ulf Hansson <[email protected]>
Kind regards
Uffe
> ---
> drivers/net/wireless/realtek/rtw88/Kconfig | 3 +
> drivers/net/wireless/realtek/rtw88/Makefile | 3 +
> drivers/net/wireless/realtek/rtw88/debug.h | 1 +
> drivers/net/wireless/realtek/rtw88/mac.h | 1 -
> drivers/net/wireless/realtek/rtw88/reg.h | 10 +
> drivers/net/wireless/realtek/rtw88/sdio.c | 1242 +++++++++++++++++++
> drivers/net/wireless/realtek/rtw88/sdio.h | 175 +++
> 7 files changed, 1434 insertions(+), 1 deletion(-)
> create mode 100644 drivers/net/wireless/realtek/rtw88/sdio.c
> create mode 100644 drivers/net/wireless/realtek/rtw88/sdio.h
>
> diff --git a/drivers/net/wireless/realtek/rtw88/Kconfig b/drivers/net/wireless/realtek/rtw88/Kconfig
> index 651ab56d9c6b..cdf9cb478ee2 100644
> --- a/drivers/net/wireless/realtek/rtw88/Kconfig
> +++ b/drivers/net/wireless/realtek/rtw88/Kconfig
> @@ -16,6 +16,9 @@ config RTW88_CORE
> config RTW88_PCI
> tristate
>
> +config RTW88_SDIO
> + tristate
> +
> config RTW88_USB
> tristate
>
> diff --git a/drivers/net/wireless/realtek/rtw88/Makefile b/drivers/net/wireless/realtek/rtw88/Makefile
> index fe7293ee87b4..892cad60ba31 100644
> --- a/drivers/net/wireless/realtek/rtw88/Makefile
> +++ b/drivers/net/wireless/realtek/rtw88/Makefile
> @@ -59,5 +59,8 @@ rtw88_8821cu-objs := rtw8821cu.o
> obj-$(CONFIG_RTW88_PCI) += rtw88_pci.o
> rtw88_pci-objs := pci.o
>
> +obj-$(CONFIG_RTW88_SDIO) += rtw88_sdio.o
> +rtw88_sdio-objs := sdio.o
> +
> obj-$(CONFIG_RTW88_USB) += rtw88_usb.o
> rtw88_usb-objs := usb.o
> diff --git a/drivers/net/wireless/realtek/rtw88/debug.h b/drivers/net/wireless/realtek/rtw88/debug.h
> index 066792dd96af..a9149c6c2b48 100644
> --- a/drivers/net/wireless/realtek/rtw88/debug.h
> +++ b/drivers/net/wireless/realtek/rtw88/debug.h
> @@ -24,6 +24,7 @@ enum rtw_debug_mask {
> RTW_DBG_ADAPTIVITY = 0x00008000,
> RTW_DBG_HW_SCAN = 0x00010000,
> RTW_DBG_STATE = 0x00020000,
> + RTW_DBG_SDIO = 0x00040000,
>
> RTW_DBG_ALL = 0xffffffff
> };
> diff --git a/drivers/net/wireless/realtek/rtw88/mac.h b/drivers/net/wireless/realtek/rtw88/mac.h
> index 3172aa5ac4de..58c3dccc14bb 100644
> --- a/drivers/net/wireless/realtek/rtw88/mac.h
> +++ b/drivers/net/wireless/realtek/rtw88/mac.h
> @@ -7,7 +7,6 @@
>
> #define RTW_HW_PORT_NUM 5
> #define cut_version_to_mask(cut) (0x1 << ((cut) + 1))
> -#define SDIO_LOCAL_OFFSET 0x10250000
> #define DDMA_POLLING_COUNT 1000
> #define C2H_PKT_BUF 256
> #define REPORT_BUF 128
> diff --git a/drivers/net/wireless/realtek/rtw88/reg.h b/drivers/net/wireless/realtek/rtw88/reg.h
> index 8852b24d6c2a..4ea2c6b491e9 100644
> --- a/drivers/net/wireless/realtek/rtw88/reg.h
> +++ b/drivers/net/wireless/realtek/rtw88/reg.h
> @@ -185,6 +185,9 @@
> (((x) & BIT_MASK_TXDMA_VIQ_MAP) << BIT_SHIFT_TXDMA_VIQ_MAP)
> #define REG_TXDMA_PQ_MAP 0x010C
> #define BIT_RXDMA_ARBBW_EN BIT(0)
> +#define BIT_RXSHFT_EN BIT(1)
> +#define BIT_RXDMA_AGG_EN BIT(2)
> +#define BIT_TXDMA_BW_EN BIT(3)
> #define BIT_SHIFT_TXDMA_BEQ_MAP 8
> #define BIT_MASK_TXDMA_BEQ_MAP 0x3
> #define BIT_TXDMA_BEQ_MAP(x) \
> @@ -283,10 +286,17 @@
> #define REG_H2C_TAIL 0x0248
> #define REG_H2C_READ_ADDR 0x024C
> #define REG_H2C_INFO 0x0254
> +#define REG_RXDMA_AGG_PG_TH 0x0280
> +#define BIT_SHIFT_DMA_AGG_TO_V1 8
> +#define BIT_EN_PRE_CALC BIT(29)
> #define REG_RXPKT_NUM 0x0284
> #define BIT_RXDMA_REQ BIT(19)
> #define BIT_RW_RELEASE BIT(18)
> #define BIT_RXDMA_IDLE BIT(17)
> +#define REG_RXDMA_STATUS 0x0288
> +#define REG_RXDMA_DPR 0x028C
> +#define REG_RXDMA_MODE 0x0290
> +#define BIT_DMA_MODE BIT(1)
> #define REG_RXPKTNUM 0x02B0
>
> #define REG_INT_MIG 0x0304
> diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c
> new file mode 100644
> index 000000000000..0e637ff2293f
> --- /dev/null
> +++ b/drivers/net/wireless/realtek/rtw88/sdio.c
> @@ -0,0 +1,1242 @@
> +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
> +/* Copyright (C) 2021 Martin Blumenstingl <[email protected]>
> + * Copyright (C) 2021 Jernej Skrabec <[email protected]>
> + *
> + * Based on rtw88/pci.c:
> + * Copyright(c) 2018-2019 Realtek Corporation
> + */
> +
> +#include <linux/module.h>
> +#include <linux/mmc/host.h>
> +#include <linux/mmc/sdio_func.h>
> +#include "sdio.h"
> +#include "reg.h"
> +#include "tx.h"
> +#include "rx.h"
> +#include "fw.h"
> +#include "ps.h"
> +#include "debug.h"
> +
> +#define RTW_SDIO_INDIRECT_RW_RETRIES 50
> +
> +static bool rtw_sdio_is_bus_addr(u32 addr)
> +{
> + return (addr & RTW_SDIO_BUS_MSK) != 0;
> +}
> +
> +static bool rtw_sdio_bus_claim_needed(struct rtw_sdio *rtwsdio)
> +{
> + return !rtwsdio->irq_thread ||
> + rtwsdio->irq_thread != current;
> +}
> +
> +static u32 rtw_sdio_to_bus_offset(struct rtw_dev *rtwdev, u32 addr)
> +{
> + switch (addr & RTW_SDIO_BUS_MSK) {
> + case WLAN_IOREG_OFFSET:
> + addr &= WLAN_IOREG_REG_MSK;
> + addr |= FIELD_PREP(REG_SDIO_CMD_ADDR_MSK,
> + REG_SDIO_CMD_ADDR_MAC_REG);
> + break;
> + case SDIO_LOCAL_OFFSET:
> + addr &= SDIO_LOCAL_REG_MSK;
> + addr |= FIELD_PREP(REG_SDIO_CMD_ADDR_MSK,
> + REG_SDIO_CMD_ADDR_SDIO_REG);
> + break;
> + default:
> + rtw_warn(rtwdev, "Cannot convert addr 0x%08x to bus offset",
> + addr);
> + }
> +
> + return addr;
> +}
> +
> +static void rtw_sdio_writel(struct rtw_sdio *rtwsdio, u32 val,
> + u32 addr, int *ret)
> +{
> + u8 buf[4];
> + int i;
> +
> + if (!(addr & 3) && rtwsdio->is_powered_on) {
> + sdio_writel(rtwsdio->sdio_func, val, addr, ret);
> + return;
> + }
> +
> + *(__le32 *)buf = cpu_to_le32(val);
> +
> + for (i = 0; i < 4; i++) {
> + sdio_writeb(rtwsdio->sdio_func, buf[i], addr + i, ret);
> + if (*ret)
> + return;
> + }
> +}
> +
> +static u32 rtw_sdio_readl(struct rtw_sdio *rtwsdio, u32 addr, int *ret)
> +{
> + u8 buf[4];
> + int i;
> +
> + if (!(addr & 3) && rtwsdio->is_powered_on)
> + return sdio_readl(rtwsdio->sdio_func, addr, ret);
> +
> + for (i = 0; i < 4; i++) {
> + buf[i] = sdio_readb(rtwsdio->sdio_func, addr + i, ret);
> + if (*ret)
> + return 0;
> + }
> +
> + return le32_to_cpu(*(__le32 *)buf);
> +}
> +
> +static u8 rtw_sdio_read_indirect8(struct rtw_dev *rtwdev, u32 addr, int *ret)
> +{
> + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> + u32 reg_cfg, reg_data;
> + int retry;
> + u8 tmp;
> +
> + reg_cfg = rtw_sdio_to_bus_offset(rtwdev, REG_SDIO_INDIRECT_REG_CFG);
> + reg_data = rtw_sdio_to_bus_offset(rtwdev, REG_SDIO_INDIRECT_REG_DATA);
> +
> + rtw_sdio_writel(rtwsdio, BIT(19) | addr, reg_cfg, ret);
> + if (*ret)
> + return 0;
> +
> + for (retry = 0; retry < RTW_SDIO_INDIRECT_RW_RETRIES; retry++) {
> + tmp = sdio_readb(rtwsdio->sdio_func, reg_cfg + 2, ret);
> + if (!ret && tmp & BIT(4))
> + break;
> + }
> +
> + if (*ret)
> + return 0;
> +
> + return sdio_readb(rtwsdio->sdio_func, reg_data, ret);
> +}
> +
> +static int rtw_sdio_read_indirect_bytes(struct rtw_dev *rtwdev, u32 addr,
> + u8 *buf, int count)
> +{
> + int i, ret;
> +
> + for (i = 0; i < count; i++) {
> + buf[0] = rtw_sdio_read_indirect8(rtwdev, addr + i, &ret);
> + if (ret)
> + break;
> + }
> +
> + return ret;
> +}
> +
> +static u32 rtw_sdio_read_indirect32(struct rtw_dev *rtwdev, u32 addr, int *ret)
> +{
> + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> + u32 reg_cfg, reg_data, val;
> + int retry;
> +
> + reg_cfg = rtw_sdio_to_bus_offset(rtwdev, REG_SDIO_INDIRECT_REG_CFG);
> + reg_data = rtw_sdio_to_bus_offset(rtwdev, REG_SDIO_INDIRECT_REG_DATA);
> +
> + rtw_sdio_writel(rtwsdio, BIT(19) | BIT(17) | addr, reg_cfg, ret);
> + if (*ret)
> + return 0;
> +
> + for (retry = 0; retry < RTW_SDIO_INDIRECT_RW_RETRIES; retry++) {
> + val = sdio_readb(rtwsdio->sdio_func, reg_cfg + 2, ret);
> + if (!ret && (val & BIT(4)))
> + break;
> + }
> +
> + if (!*ret)
> + val = rtw_sdio_readl(rtwsdio, reg_data, ret);
> +
> + return val;
> +}
> +
> +static u8 rtw_sdio_read8(struct rtw_dev *rtwdev, u32 addr)
> +{
> + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> + bool direct, bus_claim;
> + int ret;
> + u8 val;
> +
> + bus_claim = rtw_sdio_bus_claim_needed(rtwsdio);
> + direct = rtw_sdio_is_bus_addr(addr);
> +
> + if (bus_claim)
> + sdio_claim_host(rtwsdio->sdio_func);
> +
> + if (direct) {
> + addr = rtw_sdio_to_bus_offset(rtwdev, addr);
> + val = sdio_readb(rtwsdio->sdio_func, addr, &ret);
> + } else {
> + val = rtw_sdio_read_indirect8(rtwdev, addr, &ret);
> + }
> +
> + if (bus_claim)
> + sdio_release_host(rtwsdio->sdio_func);
> +
> + if (ret)
> + rtw_warn(rtwdev, "sdio read8 failed (0x%x): %d", addr, ret);
> +
> + return val;
> +}
> +
> +static u16 rtw_sdio_read16(struct rtw_dev *rtwdev, u32 addr)
> +{
> + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> + bool direct, bus_claim;
> + u8 buf[2];
> + int ret;
> + u16 val;
> +
> + bus_claim = rtw_sdio_bus_claim_needed(rtwsdio);
> + direct = rtw_sdio_is_bus_addr(addr);
> +
> + if (bus_claim)
> + sdio_claim_host(rtwsdio->sdio_func);
> +
> + if (direct) {
> + addr = rtw_sdio_to_bus_offset(rtwdev, addr);
> + buf[0] = sdio_readb(rtwsdio->sdio_func, addr, &ret);
> + if (!ret)
> + buf[1] = sdio_readb(rtwsdio->sdio_func, addr + 1, &ret);
> + val = le16_to_cpu(*(__le16 *)buf);
> + } else if (addr & 1) {
> + ret = rtw_sdio_read_indirect_bytes(rtwdev, addr, buf, 2);
> + val = le16_to_cpu(*(__le16 *)buf);
> + } else {
> + val = rtw_sdio_read_indirect32(rtwdev, addr, &ret);
> + }
> +
> + if (bus_claim)
> + sdio_release_host(rtwsdio->sdio_func);
> +
> + if (ret)
> + rtw_warn(rtwdev, "sdio read16 failed (0x%x): %d", addr, ret);
> +
> + return val;
> +}
> +
> +static u32 rtw_sdio_read32(struct rtw_dev *rtwdev, u32 addr)
> +{
> + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> + bool direct, bus_claim;
> + u8 buf[4];
> + u32 val;
> + int ret;
> +
> + bus_claim = rtw_sdio_bus_claim_needed(rtwsdio);
> + direct = rtw_sdio_is_bus_addr(addr);
> +
> + if (bus_claim)
> + sdio_claim_host(rtwsdio->sdio_func);
> +
> + if (direct) {
> + addr = rtw_sdio_to_bus_offset(rtwdev, addr);
> + val = rtw_sdio_readl(rtwsdio, addr, &ret);
> + } else if (addr & 3) {
> + ret = rtw_sdio_read_indirect_bytes(rtwdev, addr, buf, 4);
> + val = le32_to_cpu(*(__le32 *)buf);
> + } else {
> + val = rtw_sdio_read_indirect32(rtwdev, addr, &ret);
> + }
> +
> + if (bus_claim)
> + sdio_release_host(rtwsdio->sdio_func);
> +
> + if (ret)
> + rtw_warn(rtwdev, "sdio read32 failed (0x%x): %d", addr, ret);
> +
> + return val;
> +}
> +
> +static u32 rtw_sdio_to_write_address(struct rtw_dev *rtwdev, u32 addr)
> +{
> + if (!rtw_sdio_is_bus_addr(addr))
> + addr |= WLAN_IOREG_OFFSET;
> +
> + return rtw_sdio_to_bus_offset(rtwdev, addr);
> +}
> +
> +static void rtw_sdio_write8(struct rtw_dev *rtwdev, u32 addr, u8 val)
> +{
> + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> + bool bus_claim;
> + int ret;
> +
> + addr = rtw_sdio_to_write_address(rtwdev, addr);
> + bus_claim = rtw_sdio_bus_claim_needed(rtwsdio);
> +
> + if (bus_claim)
> + sdio_claim_host(rtwsdio->sdio_func);
> +
> + sdio_writeb(rtwsdio->sdio_func, val, addr, &ret);
> +
> + if (bus_claim)
> + sdio_release_host(rtwsdio->sdio_func);
> +
> + if (ret)
> + rtw_warn(rtwdev, "sdio write8 failed (0x%x): %d", addr, ret);
> +}
> +
> +static void rtw_sdio_write16(struct rtw_dev *rtwdev, u32 addr, u16 val)
> +{
> + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> + bool bus_claim;
> + int ret;
> +
> + addr = rtw_sdio_to_write_address(rtwdev, addr);
> + bus_claim = rtw_sdio_bus_claim_needed(rtwsdio);
> +
> + if (bus_claim)
> + sdio_claim_host(rtwsdio->sdio_func);
> +
> + sdio_writeb(rtwsdio->sdio_func, val, addr, &ret);
> + if (!ret)
> + sdio_writeb(rtwsdio->sdio_func, val >> 8, addr + 1, &ret);
> +
> + if (bus_claim)
> + sdio_release_host(rtwsdio->sdio_func);
> +
> + if (ret)
> + rtw_warn(rtwdev, "sdio write16 failed (0x%x): %d", addr, ret);
> +}
> +
> +static void rtw_sdio_write32(struct rtw_dev *rtwdev, u32 addr, u32 val)
> +{
> + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> + bool bus_claim;
> + int ret;
> +
> + addr = rtw_sdio_to_write_address(rtwdev, addr);
> + bus_claim = rtw_sdio_bus_claim_needed(rtwsdio);
> +
> + if (bus_claim)
> + sdio_claim_host(rtwsdio->sdio_func);
> +
> + rtw_sdio_writel(rtwsdio, val, addr, &ret);
> +
> + if (bus_claim)
> + sdio_release_host(rtwsdio->sdio_func);
> +
> + if (ret)
> + rtw_warn(rtwdev, "sdio write32 failed (0x%x): %d", addr, ret);
> +}
> +
> +static u32 rtw_sdio_get_tx_addr(struct rtw_dev *rtwdev, size_t size,
> + enum rtw_tx_queue_type queue)
> +{
> + u32 txaddr;
> +
> + switch (queue) {
> + case RTW_TX_QUEUE_BCN:
> + case RTW_TX_QUEUE_H2C:
> + case RTW_TX_QUEUE_HI0:
> + txaddr = FIELD_PREP(REG_SDIO_CMD_ADDR_MSK,
> + REG_SDIO_CMD_ADDR_TXFF_HIGH);
> + break;
> + case RTW_TX_QUEUE_VI:
> + case RTW_TX_QUEUE_VO:
> + txaddr = FIELD_PREP(REG_SDIO_CMD_ADDR_MSK,
> + REG_SDIO_CMD_ADDR_TXFF_NORMAL);
> + break;
> + case RTW_TX_QUEUE_BE:
> + case RTW_TX_QUEUE_BK:
> + txaddr = FIELD_PREP(REG_SDIO_CMD_ADDR_MSK,
> + REG_SDIO_CMD_ADDR_TXFF_LOW);
> + break;
> + case RTW_TX_QUEUE_MGMT:
> + txaddr = FIELD_PREP(REG_SDIO_CMD_ADDR_MSK,
> + REG_SDIO_CMD_ADDR_TXFF_EXTRA);
> + break;
> + default:
> + rtw_warn(rtwdev, "Unsupported queue for TX addr: 0x%02x\n",
> + queue);
> + return 0;
> + }
> +
> + txaddr += DIV_ROUND_UP(size, 4);
> +
> + return txaddr;
> +};
> +
> +static int rtw_sdio_read_port(struct rtw_dev *rtwdev, u8 *buf, size_t count)
> +{
> + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> + u32 rxaddr = rtwsdio->rx_addr++;
> + int ret;
> +
> + ret = sdio_memcpy_fromio(rtwsdio->sdio_func, buf,
> + RTW_SDIO_ADDR_RX_RX0FF_GEN(rxaddr), count);
> + if (ret)
> + rtw_warn(rtwdev,
> + "Failed to read %lu byte(s) from SDIO port 0x%08x",
> + count, rxaddr);
> +
> + return ret;
> +}
> +
> +static int rtw_sdio_check_free_txpg(struct rtw_dev *rtwdev, u8 queue,
> + size_t count)
> +{
> + unsigned int pages_free, pages_needed;
> +
> + if (rtw_chip_wcpu_11n(rtwdev)) {
> + u32 free_txpg;
> +
> + free_txpg = rtw_sdio_read32(rtwdev, REG_SDIO_FREE_TXPG);
> +
> + switch (queue) {
> + case RTW_TX_QUEUE_BCN:
> + case RTW_TX_QUEUE_H2C:
> + case RTW_TX_QUEUE_HI0:
> + case RTW_TX_QUEUE_MGMT:
> + /* high */
> + pages_free = free_txpg & 0xff;
> + break;
> + case RTW_TX_QUEUE_VI:
> + case RTW_TX_QUEUE_VO:
> + /* normal */
> + pages_free = (free_txpg >> 8) & 0xff;
> + break;
> + case RTW_TX_QUEUE_BE:
> + case RTW_TX_QUEUE_BK:
> + /* low */
> + pages_free = (free_txpg >> 16) & 0xff;
> + break;
> + default:
> + rtw_warn(rtwdev, "Unknown mapping for queue %u\n", queue);
> + break;
> + }
> +
> + /* add the pages from the public queue */
> + pages_free += (free_txpg >> 24) & 0xff;
> + } else {
> + u32 free_txpg[3];
> +
> + free_txpg[0] = rtw_sdio_read32(rtwdev, REG_SDIO_FREE_TXPG);
> + free_txpg[1] = rtw_sdio_read32(rtwdev, REG_SDIO_FREE_TXPG + 4);
> + free_txpg[2] = rtw_sdio_read32(rtwdev, REG_SDIO_FREE_TXPG + 8);
> +
> + switch (queue) {
> + case RTW_TX_QUEUE_BCN:
> + case RTW_TX_QUEUE_H2C:
> + case RTW_TX_QUEUE_HI0:
> + /* high */
> + pages_free = free_txpg[0] & 0xfff;
> + break;
> + case RTW_TX_QUEUE_VI:
> + case RTW_TX_QUEUE_VO:
> + /* normal */
> + pages_free = (free_txpg[0] >> 16) & 0xfff;
> + break;
> + case RTW_TX_QUEUE_BE:
> + case RTW_TX_QUEUE_BK:
> + /* low */
> + pages_free = free_txpg[1] & 0xfff;
> + break;
> + case RTW_TX_QUEUE_MGMT:
> + /* extra */
> + pages_free = free_txpg[2] & 0xfff;
> + break;
> + default:
> + rtw_warn(rtwdev, "Unknown mapping for queue %u\n", queue);
> + return -EINVAL;
> + }
> +
> + /* add the pages from the public queue */
> + pages_free += (free_txpg[1] >> 16) & 0xfff;
> + }
> +
> + pages_needed = DIV_ROUND_UP(count, rtwdev->chip->page_size);
> +
> + if (pages_needed > pages_free) {
> + rtw_dbg(rtwdev, RTW_DBG_SDIO,
> + "Not enough free pages (%u needed, %u free) in queue %u for %zu bytes\n",
> + pages_needed, pages_free, queue, count);
> + return -EBUSY;
> + }
> +
> + return 0;
> +}
> +
> +static int rtw_sdio_write_port(struct rtw_dev *rtwdev, struct sk_buff *skb,
> + enum rtw_tx_queue_type queue)
> +{
> + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> + bool bus_claim;
> + size_t txsize;
> + u32 txaddr;
> + int ret;
> +
> + txaddr = rtw_sdio_get_tx_addr(rtwdev, skb->len, queue);
> + if (!txaddr)
> + return -EINVAL;
> +
> + txsize = sdio_align_size(rtwsdio->sdio_func, skb->len);
> +
> + ret = rtw_sdio_check_free_txpg(rtwdev, queue, txsize);
> + if (ret)
> + return ret;
> +
> + if (!IS_ALIGNED((unsigned long)skb->data, RTW_SDIO_DATA_PTR_ALIGN))
> + rtw_warn(rtwdev, "Got unaligned SKB in %s() for queue %u\n",
> + __func__, queue);
> +
> + bus_claim = rtw_sdio_bus_claim_needed(rtwsdio);
> +
> + if (bus_claim)
> + sdio_claim_host(rtwsdio->sdio_func);
> +
> + ret = sdio_memcpy_toio(rtwsdio->sdio_func, txaddr, skb->data, txsize);
> +
> + if (bus_claim)
> + sdio_release_host(rtwsdio->sdio_func);
> +
> + if (ret)
> + rtw_warn(rtwdev,
> + "Failed to write %lu byte(s) to SDIO port 0x%08x",
> + txsize, txaddr);
> +
> + return ret;
> +}
> +
> +static void rtw_sdio_init(struct rtw_dev *rtwdev)
> +{
> + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> +
> + rtwsdio->irq_mask = REG_SDIO_HIMR_RX_REQUEST | REG_SDIO_HIMR_CPWM1;
> +}
> +
> +static void rtw_sdio_rx_aggregation(struct rtw_dev *rtwdev, bool enable)
> +{
> + u8 size, timeout;
> +
> + if (enable) {
> + if (rtwdev->chip->id == RTW_CHIP_TYPE_8822C) {
> + size = 0xff;
> + timeout = 0x20;
> + } else {
> + size = 0x6;
> + timeout = 0x6;
> + }
> +
> + /* Make the firmware honor the size limit configured below */
> + rtw_write32_set(rtwdev, REG_RXDMA_AGG_PG_TH, BIT_EN_PRE_CALC);
> +
> + rtw_write8_set(rtwdev, REG_TXDMA_PQ_MAP, BIT_RXDMA_AGG_EN);
> +
> + rtw_write16(rtwdev, REG_RXDMA_AGG_PG_TH, size |
> + (timeout << BIT_SHIFT_DMA_AGG_TO_V1));
> +
> + rtw_write8_set(rtwdev, REG_RXDMA_MODE, BIT_DMA_MODE);
> + } else {
> + rtw_write32_clr(rtwdev, REG_RXDMA_AGG_PG_TH, BIT_EN_PRE_CALC);
> + rtw_write8_clr(rtwdev, REG_TXDMA_PQ_MAP, BIT_RXDMA_AGG_EN);
> + rtw_write8_clr(rtwdev, REG_RXDMA_MODE, BIT_DMA_MODE);
> + }
> +}
> +
> +static void rtw_sdio_enable_interrupt(struct rtw_dev *rtwdev)
> +{
> + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> +
> + rtw_write32(rtwdev, REG_SDIO_HIMR, rtwsdio->irq_mask);
> +}
> +
> +static void rtw_sdio_disable_interrupt(struct rtw_dev *rtwdev)
> +{
> + rtw_write32(rtwdev, REG_SDIO_HIMR, 0x0);
> +}
> +
> +static u8 rtw_sdio_get_tx_qsel(struct rtw_dev *rtwdev, struct sk_buff *skb,
> + u8 queue)
> +{
> + switch (queue) {
> + case RTW_TX_QUEUE_BCN:
> + return TX_DESC_QSEL_BEACON;
> + case RTW_TX_QUEUE_H2C:
> + return TX_DESC_QSEL_H2C;
> + case RTW_TX_QUEUE_MGMT:
> + if (rtw_chip_wcpu_11n(rtwdev))
> + return TX_DESC_QSEL_HIGH;
> + else
> + return TX_DESC_QSEL_MGMT;
> + case RTW_TX_QUEUE_HI0:
> + return TX_DESC_QSEL_HIGH;
> + default:
> + return skb->priority;
> + }
> +};
> +
> +static int rtw_sdio_setup(struct rtw_dev *rtwdev)
> +{
> + /* nothing to do */
> + return 0;
> +}
> +
> +static int rtw_sdio_start(struct rtw_dev *rtwdev)
> +{
> + rtw_sdio_rx_aggregation(rtwdev, false);
> + rtw_sdio_enable_interrupt(rtwdev);
> +
> + return 0;
> +}
> +
> +static void rtw_sdio_stop(struct rtw_dev *rtwdev)
> +{
> + rtw_sdio_disable_interrupt(rtwdev);
> +}
> +
> +static void rtw_sdio_deep_ps_enter(struct rtw_dev *rtwdev)
> +{
> + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> + bool tx_empty = true;
> + u8 queue;
> +
> + if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_TX_WAKE)) {
> + /* Deep PS state is not allowed to TX-DMA */
> + for (queue = 0; queue < RTK_MAX_TX_QUEUE_NUM; queue++) {
> + /* BCN queue is rsvd page, does not have DMA interrupt
> + * H2C queue is managed by firmware
> + */
> + if (queue == RTW_TX_QUEUE_BCN ||
> + queue == RTW_TX_QUEUE_H2C)
> + continue;
> +
> + /* check if there is any skb DMAing */
> + if (skb_queue_len(&rtwsdio->tx_queue[queue])) {
> + tx_empty = false;
> + break;
> + }
> + }
> + }
> +
> + if (!tx_empty) {
> + rtw_dbg(rtwdev, RTW_DBG_PS,
> + "TX path not empty, cannot enter deep power save state\n");
> + return;
> + }
> +
> + set_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags);
> + rtw_power_mode_change(rtwdev, true);
> +}
> +
> +static void rtw_sdio_deep_ps_leave(struct rtw_dev *rtwdev)
> +{
> + if (test_and_clear_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags))
> + rtw_power_mode_change(rtwdev, false);
> +}
> +
> +static void rtw_sdio_deep_ps(struct rtw_dev *rtwdev, bool enter)
> +{
> + if (enter && !test_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags))
> + rtw_sdio_deep_ps_enter(rtwdev);
> +
> + if (!enter && test_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags))
> + rtw_sdio_deep_ps_leave(rtwdev);
> +}
> +
> +static void rtw_sdio_tx_kick_off(struct rtw_dev *rtwdev)
> +{
> + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> +
> + queue_work(rtwsdio->txwq, &rtwsdio->tx_handler_data->work);
> +}
> +
> +static void rtw_sdio_link_ps(struct rtw_dev *rtwdev, bool enter)
> +{
> + /* nothing to do */
> +}
> +
> +static void rtw_sdio_interface_cfg(struct rtw_dev *rtwdev)
> +{
> + u32 val;
> +
> + rtw_read32(rtwdev, REG_SDIO_FREE_TXPG);
> +
> + val = rtw_read32(rtwdev, REG_SDIO_TX_CTRL);
> + val &= 0xfff8;
> + rtw_write32(rtwdev, REG_SDIO_TX_CTRL, val);
> +}
> +
> +static void rtw_sdio_power_switch(struct rtw_dev *rtwdev, bool on)
> +{
> + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> +
> + rtwsdio->is_powered_on = on;
> +}
> +
> +static struct rtw_sdio_tx_data *rtw_sdio_get_tx_data(struct sk_buff *skb)
> +{
> + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> +
> + BUILD_BUG_ON(sizeof(struct rtw_sdio_tx_data) >
> + sizeof(info->status.status_driver_data));
> +
> + return (struct rtw_sdio_tx_data *)info->status.status_driver_data;
> +}
> +
> +static void rtw_sdio_tx_skb_prepare(struct rtw_dev *rtwdev,
> + struct rtw_tx_pkt_info *pkt_info,
> + struct sk_buff *skb,
> + enum rtw_tx_queue_type queue)
> +{
> + const struct rtw_chip_info *chip = rtwdev->chip;
> + unsigned long data_addr, aligned_addr;
> + size_t offset;
> + u8 *pkt_desc;
> +
> + pkt_desc = skb_push(skb, chip->tx_pkt_desc_sz);
> +
> + data_addr = (unsigned long)pkt_desc;
> + aligned_addr = ALIGN(data_addr, RTW_SDIO_DATA_PTR_ALIGN);
> +
> + if (data_addr != aligned_addr) {
> + /* Ensure that the start of the pkt_desc is always aligned at
> + * RTW_SDIO_DATA_PTR_ALIGN.
> + */
> + offset = RTW_SDIO_DATA_PTR_ALIGN - (aligned_addr - data_addr);
> +
> + pkt_desc = skb_push(skb, offset);
> +
> + /* By inserting padding to align the start of the pkt_desc we
> + * need to inform the firmware that the actual data starts at
> + * a different offset than normal.
> + */
> + pkt_info->offset += offset;
> + }
> +
> + memset(pkt_desc, 0, chip->tx_pkt_desc_sz);
> +
> + pkt_info->qsel = rtw_sdio_get_tx_qsel(rtwdev, skb, queue);
> +
> + rtw_tx_fill_tx_desc(pkt_info, skb);
> + chip->ops->fill_txdesc_checksum(rtwdev, pkt_info, pkt_desc);
> +}
> +
> +static int rtw_sdio_write_data(struct rtw_dev *rtwdev,
> + struct rtw_tx_pkt_info *pkt_info,
> + struct sk_buff *skb,
> + enum rtw_tx_queue_type queue)
> +{
> + int ret;
> +
> + rtw_sdio_tx_skb_prepare(rtwdev, pkt_info, skb, queue);
> +
> + ret = rtw_sdio_write_port(rtwdev, skb, queue);
> + dev_kfree_skb_any(skb);
> +
> + return ret;
> +}
> +
> +static int rtw_sdio_write_data_rsvd_page(struct rtw_dev *rtwdev, u8 *buf,
> + u32 size)
> +{
> + struct rtw_tx_pkt_info pkt_info = {};
> + struct sk_buff *skb;
> +
> + skb = rtw_tx_write_data_rsvd_page_get(rtwdev, &pkt_info, buf, size);
> + if (!skb)
> + return -ENOMEM;
> +
> + return rtw_sdio_write_data(rtwdev, &pkt_info, skb, RTW_TX_QUEUE_BCN);
> +}
> +
> +static int rtw_sdio_write_data_h2c(struct rtw_dev *rtwdev, u8 *buf, u32 size)
> +{
> + struct rtw_tx_pkt_info pkt_info = {};
> + struct sk_buff *skb;
> +
> + skb = rtw_tx_write_data_h2c_get(rtwdev, &pkt_info, buf, size);
> + if (!skb)
> + return -ENOMEM;
> +
> + return rtw_sdio_write_data(rtwdev, &pkt_info, skb, RTW_TX_QUEUE_H2C);
> +}
> +
> +static int rtw_sdio_tx_write(struct rtw_dev *rtwdev,
> + struct rtw_tx_pkt_info *pkt_info,
> + struct sk_buff *skb)
> +{
> + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> + enum rtw_tx_queue_type queue = rtw_tx_queue_mapping(skb);
> + struct rtw_sdio_tx_data *tx_data;
> +
> + rtw_sdio_tx_skb_prepare(rtwdev, pkt_info, skb, queue);
> +
> + tx_data = rtw_sdio_get_tx_data(skb);
> + tx_data->sn = pkt_info->sn;
> +
> + skb_queue_tail(&rtwsdio->tx_queue[queue], skb);
> +
> + return 0;
> +}
> +
> +static void rtw_sdio_tx_err_isr(struct rtw_dev *rtwdev)
> +{
> + u32 val = rtw_read32(rtwdev, REG_TXDMA_STATUS);
> +
> + rtw_write32(rtwdev, REG_TXDMA_STATUS, val);
> +}
> +
> +static void rtw_sdio_rx_skb(struct rtw_dev *rtwdev, struct sk_buff *skb,
> + u32 pkt_offset, struct rtw_rx_pkt_stat *pkt_stat,
> + struct ieee80211_rx_status *rx_status)
> +{
> + memcpy(IEEE80211_SKB_RXCB(skb), rx_status, sizeof(*rx_status));
> +
> + if (pkt_stat->is_c2h) {
> + skb_put(skb, pkt_stat->pkt_len + pkt_offset);
> + rtw_fw_c2h_cmd_rx_irqsafe(rtwdev, pkt_offset, skb);
> + return;
> + }
> +
> + skb_put(skb, pkt_stat->pkt_len);
> + skb_reserve(skb, pkt_offset);
> +
> + rtw_rx_stats(rtwdev, pkt_stat->vif, skb);
> +
> + ieee80211_rx_irqsafe(rtwdev->hw, skb);
> +}
> +
> +static void rtw_sdio_rxfifo_recv(struct rtw_dev *rtwdev, u32 rx_len)
> +{
> + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> + const struct rtw_chip_info *chip = rtwdev->chip;
> + u32 pkt_desc_sz = chip->rx_pkt_desc_sz;
> + struct ieee80211_rx_status rx_status;
> + struct rtw_rx_pkt_stat pkt_stat;
> + struct sk_buff *skb, *split_skb;
> + u32 pkt_offset, curr_pkt_len;
> + size_t bufsz;
> + u8 *rx_desc;
> + int ret;
> +
> + bufsz = sdio_align_size(rtwsdio->sdio_func, rx_len);
> +
> + skb = dev_alloc_skb(bufsz);
> + if (!skb)
> + return;
> +
> + ret = rtw_sdio_read_port(rtwdev, skb->data, bufsz);
> + if (ret) {
> + dev_kfree_skb_any(skb);
> + return;
> + }
> +
> + while (true) {
> + rx_desc = skb->data;
> + chip->ops->query_rx_desc(rtwdev, rx_desc, &pkt_stat,
> + &rx_status);
> + pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz +
> + pkt_stat.shift;
> +
> + curr_pkt_len = ALIGN(pkt_offset + pkt_stat.pkt_len,
> + RTW_SDIO_DATA_PTR_ALIGN);
> +
> + if ((curr_pkt_len + pkt_desc_sz) >= rx_len) {
> + /* Use the original skb (with it's adjusted offset)
> + * when processing the last (or even the only) entry to
> + * have it's memory freed automatically.
> + */
> + rtw_sdio_rx_skb(rtwdev, skb, pkt_offset, &pkt_stat,
> + &rx_status);
> + break;
> + }
> +
> + split_skb = dev_alloc_skb(curr_pkt_len);
> + if (!split_skb) {
> + rtw_sdio_rx_skb(rtwdev, skb, pkt_offset, &pkt_stat,
> + &rx_status);
> + break;
> + }
> +
> + skb_copy_header(split_skb, skb);
> + memcpy(split_skb->data, skb->data, curr_pkt_len);
> +
> + rtw_sdio_rx_skb(rtwdev, split_skb, pkt_offset, &pkt_stat,
> + &rx_status);
> +
> + /* Move to the start of the next RX descriptor */
> + skb_reserve(skb, curr_pkt_len);
> + rx_len -= curr_pkt_len;
> + }
> +}
> +
> +static void rtw_sdio_rx_isr(struct rtw_dev *rtwdev)
> +{
> + u32 rx_len;
> +
> + while (true) {
> + if (rtw_chip_wcpu_11n(rtwdev))
> + rx_len = rtw_read16(rtwdev, REG_SDIO_RX0_REQ_LEN);
> + else
> + rx_len = rtw_read32(rtwdev, REG_SDIO_RX0_REQ_LEN);
> +
> + if (!rx_len)
> + break;
> +
> + rtw_sdio_rxfifo_recv(rtwdev, rx_len);
> + }
> +}
> +
> +static void rtw_sdio_handle_interrupt(struct sdio_func *sdio_func)
> +{
> + struct ieee80211_hw *hw = sdio_get_drvdata(sdio_func);
> + struct rtw_dev *rtwdev = hw->priv;
> + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> + u32 hisr;
> +
> + rtwsdio->irq_thread = current;
> +
> + hisr = rtw_read32(rtwdev, REG_SDIO_HISR);
> +
> + if (hisr & REG_SDIO_HISR_TXERR)
> + rtw_sdio_tx_err_isr(rtwdev);
> + if (hisr & REG_SDIO_HISR_RX_REQUEST) {
> + hisr &= ~REG_SDIO_HISR_RX_REQUEST;
> + rtw_sdio_rx_isr(rtwdev);
> + }
> +
> + rtw_write32(rtwdev, REG_SDIO_HISR, hisr);
> +
> + rtwsdio->irq_thread = NULL;
> +}
> +
> +static int __maybe_unused rtw_sdio_suspend(struct device *dev)
> +{
> + return 0;
> +}
> +
> +static int __maybe_unused rtw_sdio_resume(struct device *dev)
> +{
> + return 0;
> +}
> +
> +SIMPLE_DEV_PM_OPS(rtw_sdio_pm_ops, rtw_sdio_suspend, rtw_sdio_resume);
> +EXPORT_SYMBOL(rtw_sdio_pm_ops);
> +
> +static int rtw_sdio_claim(struct rtw_dev *rtwdev, struct sdio_func *sdio_func)
> +{
> + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> + int ret;
> +
> + sdio_claim_host(sdio_func);
> +
> + ret = sdio_enable_func(sdio_func);
> + if (ret) {
> + rtw_err(rtwdev, "Failed to enable SDIO func");
> + goto err_release_host;
> + }
> +
> + ret = sdio_set_block_size(sdio_func, RTW_SDIO_BLOCK_SIZE);
> + if (ret) {
> + rtw_err(rtwdev, "Failed to set SDIO block size to 512");
> + goto err_disable_func;
> + }
> +
> + rtwsdio->sdio_func = sdio_func;
> +
> + rtwsdio->sdio3_bus_mode = mmc_card_uhs(sdio_func->card);
> +
> + sdio_set_drvdata(sdio_func, rtwdev->hw);
> + SET_IEEE80211_DEV(rtwdev->hw, &sdio_func->dev);
> +
> + sdio_release_host(sdio_func);
> +
> + return 0;
> +
> +err_disable_func:
> + sdio_disable_func(sdio_func);
> +err_release_host:
> + sdio_release_host(sdio_func);
> + return ret;
> +}
> +
> +static void rtw_sdio_declaim(struct rtw_dev *rtwdev,
> + struct sdio_func *sdio_func)
> +{
> + sdio_claim_host(sdio_func);
> + sdio_disable_func(sdio_func);
> + sdio_release_host(sdio_func);
> +}
> +
> +static struct rtw_hci_ops rtw_sdio_ops = {
> + .tx_write = rtw_sdio_tx_write,
> + .tx_kick_off = rtw_sdio_tx_kick_off,
> + .setup = rtw_sdio_setup,
> + .start = rtw_sdio_start,
> + .stop = rtw_sdio_stop,
> + .deep_ps = rtw_sdio_deep_ps,
> + .link_ps = rtw_sdio_link_ps,
> + .interface_cfg = rtw_sdio_interface_cfg,
> +
> + .power_switch = rtw_sdio_power_switch,
> +
> + .read8 = rtw_sdio_read8,
> + .read16 = rtw_sdio_read16,
> + .read32 = rtw_sdio_read32,
> + .write8 = rtw_sdio_write8,
> + .write16 = rtw_sdio_write16,
> + .write32 = rtw_sdio_write32,
> + .write_data_rsvd_page = rtw_sdio_write_data_rsvd_page,
> + .write_data_h2c = rtw_sdio_write_data_h2c,
> +};
> +
> +static int rtw_sdio_request_irq(struct rtw_dev *rtwdev,
> + struct sdio_func *sdio_func)
> +{
> + int ret;
> +
> + sdio_claim_host(sdio_func);
> + ret = sdio_claim_irq(sdio_func, &rtw_sdio_handle_interrupt);
> + sdio_release_host(sdio_func);
> +
> + if (ret) {
> + rtw_err(rtwdev, "failed to claim SDIO IRQ");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static void rtw_sdio_indicate_tx_status(struct rtw_dev *rtwdev,
> + struct sk_buff *skb)
> +{
> + struct rtw_sdio_tx_data *tx_data = rtw_sdio_get_tx_data(skb);
> + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> + struct ieee80211_hw *hw = rtwdev->hw;
> +
> + /* enqueue to wait for tx report */
> + if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) {
> + rtw_tx_report_enqueue(rtwdev, skb, tx_data->sn);
> + return;
> + }
> +
> + /* always ACK for others, then they won't be marked as drop */
> + ieee80211_tx_info_clear_status(info);
> + if (info->flags & IEEE80211_TX_CTL_NO_ACK)
> + info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED;
> + else
> + info->flags |= IEEE80211_TX_STAT_ACK;
> +
> + ieee80211_tx_status_irqsafe(hw, skb);
> +}
> +
> +static void rtw_sdio_process_tx_queue(struct rtw_dev *rtwdev,
> + enum rtw_tx_queue_type queue)
> +{
> + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> + struct sk_buff *skb;
> + int ret;
> +
> + while (true) {
> + skb = skb_dequeue(&rtwsdio->tx_queue[queue]);
> + if (!skb)
> + break;
> +
> + ret = rtw_sdio_write_port(rtwdev, skb, queue);
> + if (ret) {
> + skb_queue_head(&rtwsdio->tx_queue[queue], skb);
> + break;
> + }
> +
> + if (queue <= RTW_TX_QUEUE_VO)
> + rtw_sdio_indicate_tx_status(rtwdev, skb);
> + else
> + dev_kfree_skb_any(skb);
> + }
> +}
> +
> +static void rtw_sdio_tx_handler(struct work_struct *work)
> +{
> + struct rtw_sdio_work_data *work_data =
> + container_of(work, struct rtw_sdio_work_data, work);
> + struct rtw_dev *rtwdev = work_data->rtwdev;
> + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> + bool has_more_tx_data;
> + int queue;
> +
> + if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_TX_WAKE))
> + rtw_sdio_deep_ps_leave(rtwdev);
> +
> + do {
> + has_more_tx_data = false;
> +
> + for (queue = RTK_MAX_TX_QUEUE_NUM - 1; queue >= 0; queue--) {
> + rtw_sdio_process_tx_queue(rtwdev, queue);
> +
> + if (!skb_queue_empty(&rtwsdio->tx_queue[queue]))
> + has_more_tx_data = true;
> + }
> + } while (has_more_tx_data);
> +}
> +
> +static void rtw_sdio_free_irq(struct rtw_dev *rtwdev,
> + struct sdio_func *sdio_func)
> +{
> + sdio_release_irq(sdio_func);
> +}
> +
> +static int rtw_sdio_init_tx(struct rtw_dev *rtwdev)
> +{
> + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> + int i;
> +
> + rtwsdio->txwq = create_singlethread_workqueue("rtw88_sdio: tx wq");
> + if (!rtwsdio->txwq) {
> + rtw_err(rtwdev, "failed to create TX work queue\n");
> + return -ENOMEM;
> + }
> +
> + for (i = 0; i < RTK_MAX_TX_QUEUE_NUM; i++)
> + skb_queue_head_init(&rtwsdio->tx_queue[i]);
> + rtwsdio->tx_handler_data = kmalloc(sizeof(*rtwsdio->tx_handler_data),
> + GFP_KERNEL);
> + if (!rtwsdio->tx_handler_data)
> + goto err_destroy_wq;
> +
> + rtwsdio->tx_handler_data->rtwdev = rtwdev;
> + INIT_WORK(&rtwsdio->tx_handler_data->work, rtw_sdio_tx_handler);
> +
> + return 0;
> +
> +err_destroy_wq:
> + destroy_workqueue(rtwsdio->txwq);
> + return -ENOMEM;
> +}
> +
> +static void rtw_sdio_deinit_tx(struct rtw_dev *rtwdev)
> +{
> + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> + int i;
> +
> + for (i = 0; i < RTK_MAX_TX_QUEUE_NUM; i++)
> + skb_queue_purge(&rtwsdio->tx_queue[i]);
> +
> + flush_workqueue(rtwsdio->txwq);
> + destroy_workqueue(rtwsdio->txwq);
> + kfree(rtwsdio->tx_handler_data);
> +}
> +
> +int rtw_sdio_probe(struct sdio_func *sdio_func,
> + const struct sdio_device_id *id)
> +{
> + struct ieee80211_hw *hw;
> + struct rtw_dev *rtwdev;
> + int drv_data_size;
> + int ret;
> +
> + drv_data_size = sizeof(struct rtw_dev) + sizeof(struct rtw_sdio);
> + hw = ieee80211_alloc_hw(drv_data_size, &rtw_ops);
> + if (!hw) {
> + dev_err(&sdio_func->dev, "failed to allocate hw");
> + return -ENOMEM;
> + }
> +
> + rtwdev = hw->priv;
> + rtwdev->hw = hw;
> + rtwdev->dev = &sdio_func->dev;
> + rtwdev->chip = (struct rtw_chip_info *)id->driver_data;
> + rtwdev->hci.ops = &rtw_sdio_ops;
> + rtwdev->hci.type = RTW_HCI_TYPE_SDIO;
> +
> + ret = rtw_core_init(rtwdev);
> + if (ret)
> + goto err_release_hw;
> +
> + rtw_dbg(rtwdev, RTW_DBG_SDIO,
> + "rtw88 SDIO probe: vendor=0x%04x device=%04x class=%02x",
> + id->vendor, id->device, id->class);
> +
> + ret = rtw_sdio_claim(rtwdev, sdio_func);
> + if (ret) {
> + rtw_err(rtwdev, "failed to claim SDIO device");
> + goto err_deinit_core;
> + }
> +
> + rtw_sdio_init(rtwdev);
> +
> + ret = rtw_sdio_init_tx(rtwdev);
> + if (ret) {
> + rtw_err(rtwdev, "failed to init SDIO TX queue\n");
> + goto err_sdio_declaim;
> + }
> +
> + ret = rtw_chip_info_setup(rtwdev);
> + if (ret) {
> + rtw_err(rtwdev, "failed to setup chip information");
> + goto err_destroy_txwq;
> + }
> +
> + ret = rtw_register_hw(rtwdev, hw);
> + if (ret) {
> + rtw_err(rtwdev, "failed to register hw");
> + goto err_destroy_txwq;
> + }
> +
> + ret = rtw_sdio_request_irq(rtwdev, sdio_func);
> + if (ret)
> + goto err_unregister_hw;
> +
> + return 0;
> +
> +err_unregister_hw:
> + rtw_unregister_hw(rtwdev, hw);
> +err_destroy_txwq:
> + rtw_sdio_deinit_tx(rtwdev);
> +err_sdio_declaim:
> + rtw_sdio_declaim(rtwdev, sdio_func);
> +err_deinit_core:
> + rtw_core_deinit(rtwdev);
> +err_release_hw:
> + ieee80211_free_hw(hw);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL(rtw_sdio_probe);
> +
> +void rtw_sdio_remove(struct sdio_func *sdio_func)
> +{
> + struct ieee80211_hw *hw = sdio_get_drvdata(sdio_func);
> + struct rtw_dev *rtwdev;
> +
> + if (!hw)
> + return;
> +
> + rtwdev = hw->priv;
> +
> + rtw_unregister_hw(rtwdev, hw);
> + rtw_sdio_disable_interrupt(rtwdev);
> + rtw_sdio_declaim(rtwdev, sdio_func);
> + rtw_sdio_free_irq(rtwdev, sdio_func);
> + rtw_sdio_deinit_tx(rtwdev);
> + rtw_core_deinit(rtwdev);
> + ieee80211_free_hw(hw);
> +}
> +EXPORT_SYMBOL(rtw_sdio_remove);
> +
> +void rtw_sdio_shutdown(struct device *dev)
> +{
> + struct sdio_func *sdio_func = dev_to_sdio_func(dev);
> + struct ieee80211_hw *hw = sdio_get_drvdata(sdio_func);
> + const struct rtw_chip_info *chip;
> + struct rtw_dev *rtwdev;
> +
> + if (!hw)
> + return;
> +
> + rtwdev = hw->priv;
> + chip = rtwdev->chip;
> +
> + if (chip->ops->shutdown)
> + chip->ops->shutdown(rtwdev);
> +}
> +EXPORT_SYMBOL(rtw_sdio_shutdown);
> +
> +MODULE_AUTHOR("Martin Blumenstingl");
> +MODULE_AUTHOR("Jernej Skrabec");
> +MODULE_DESCRIPTION("Realtek 802.11ac wireless SDIO driver");
> +MODULE_LICENSE("Dual BSD/GPL");
> diff --git a/drivers/net/wireless/realtek/rtw88/sdio.h b/drivers/net/wireless/realtek/rtw88/sdio.h
> new file mode 100644
> index 000000000000..7339e35f808a
> --- /dev/null
> +++ b/drivers/net/wireless/realtek/rtw88/sdio.h
> @@ -0,0 +1,175 @@
> +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
> +/* Copyright (C) 2021 Martin Blumenstingl <[email protected]>
> + * Copyright (C) 2021 Jernej Skrabec <[email protected]>
> + */
> +
> +#ifndef __REG_SDIO_H_
> +#define __REG_SDIO_H_
> +
> +#include "main.h"
> +
> +/* I/O bus domain address mapping */
> +#define SDIO_LOCAL_OFFSET 0x10250000
> +#define WLAN_IOREG_OFFSET 0x10260000
> +#define FIRMWARE_FIFO_OFFSET 0x10270000
> +#define TX_HIQ_OFFSET 0x10310000
> +#define TX_MIQ_OFFSET 0x10320000
> +#define TX_LOQ_OFFSET 0x10330000
> +#define TX_EPQ_OFFSET 0x10350000
> +#define RX_RX0FF_OFFSET 0x10340000
> +
> +#define RTW_SDIO_BUS_MSK 0xffff0000
> +#define SDIO_LOCAL_REG_MSK 0x00000fff
> +#define WLAN_IOREG_REG_MSK 0x0000ffff
> +
> +/* SDIO Tx Control */
> +#define REG_SDIO_TX_CTRL (SDIO_LOCAL_OFFSET + 0x0000)
> +
> +/*SDIO status timeout*/
> +#define REG_SDIO_TIMEOUT (SDIO_LOCAL_OFFSET + 0x0002)
> +
> +/* SDIO Host Interrupt Mask */
> +#define REG_SDIO_HIMR (SDIO_LOCAL_OFFSET + 0x0014)
> +#define REG_SDIO_HIMR_RX_REQUEST BIT(0)
> +#define REG_SDIO_HIMR_AVAL BIT(1)
> +#define REG_SDIO_HIMR_TXERR BIT(2)
> +#define REG_SDIO_HIMR_RXERR BIT(3)
> +#define REG_SDIO_HIMR_TXFOVW BIT(4)
> +#define REG_SDIO_HIMR_RXFOVW BIT(5)
> +#define REG_SDIO_HIMR_TXBCNOK BIT(6)
> +#define REG_SDIO_HIMR_TXBCNERR BIT(7)
> +#define REG_SDIO_HIMR_BCNERLY_INT BIT(16)
> +#define REG_SDIO_HIMR_C2HCMD BIT(17)
> +#define REG_SDIO_HIMR_CPWM1 BIT(18)
> +#define REG_SDIO_HIMR_CPWM2 BIT(19)
> +#define REG_SDIO_HIMR_HSISR_IND BIT(20)
> +#define REG_SDIO_HIMR_GTINT3_IND BIT(21)
> +#define REG_SDIO_HIMR_GTINT4_IND BIT(22)
> +#define REG_SDIO_HIMR_PSTIMEOUT BIT(23)
> +#define REG_SDIO_HIMR_OCPINT BIT(24)
> +#define REG_SDIO_HIMR_ATIMEND BIT(25)
> +#define REG_SDIO_HIMR_ATIMEND_E BIT(26)
> +#define REG_SDIO_HIMR_CTWEND BIT(27)
> +/* the following two are RTL8188 SDIO Specific */
> +#define REG_SDIO_HIMR_MCU_ERR BIT(28)
> +#define REG_SDIO_HIMR_TSF_BIT32_TOGGLE BIT(29)
> +
> +/* SDIO Host Interrupt Service Routine */
> +#define REG_SDIO_HISR (SDIO_LOCAL_OFFSET + 0x0018)
> +#define REG_SDIO_HISR_RX_REQUEST BIT(0)
> +#define REG_SDIO_HISR_AVAL BIT(1)
> +#define REG_SDIO_HISR_TXERR BIT(2)
> +#define REG_SDIO_HISR_RXERR BIT(3)
> +#define REG_SDIO_HISR_TXFOVW BIT(4)
> +#define REG_SDIO_HISR_RXFOVW BIT(5)
> +#define REG_SDIO_HISR_TXBCNOK BIT(6)
> +#define REG_SDIO_HISR_TXBCNERR BIT(7)
> +#define REG_SDIO_HISR_BCNERLY_INT BIT(16)
> +#define REG_SDIO_HISR_C2HCMD BIT(17)
> +#define REG_SDIO_HISR_CPWM1 BIT(18)
> +#define REG_SDIO_HISR_CPWM2 BIT(19)
> +#define REG_SDIO_HISR_HSISR_IND BIT(20)
> +#define REG_SDIO_HISR_GTINT3_IND BIT(21)
> +#define REG_SDIO_HISR_GTINT4_IND BIT(22)
> +#define REG_SDIO_HISR_PSTIMEOUT BIT(23)
> +#define REG_SDIO_HISR_OCPINT BIT(24)
> +#define REG_SDIO_HISR_ATIMEND BIT(25)
> +#define REG_SDIO_HISR_ATIMEND_E BIT(26)
> +#define REG_SDIO_HISR_CTWEND BIT(27)
> +/* the following two are RTL8188 SDIO Specific */
> +#define REG_SDIO_HISR_MCU_ERR BIT(28)
> +#define REG_SDIO_HISR_TSF_BIT32_TOGGLE BIT(29)
> +
> +/* HCI Current Power Mode */
> +#define REG_SDIO_HCPWM (SDIO_LOCAL_OFFSET + 0x0019)
> +/* RXDMA Request Length */
> +#define REG_SDIO_RX0_REQ_LEN (SDIO_LOCAL_OFFSET + 0x001C)
> +/* OQT Free Page */
> +#define REG_SDIO_OQT_FREE_PG (SDIO_LOCAL_OFFSET + 0x001E)
> +/* Free Tx Buffer Page */
> +#define REG_SDIO_FREE_TXPG (SDIO_LOCAL_OFFSET + 0x0020)
> +/* HCI Current Power Mode 1 */
> +#define REG_SDIO_HCPWM1 (SDIO_LOCAL_OFFSET + 0x0024)
> +/* HCI Current Power Mode 2 */
> +#define REG_SDIO_HCPWM2 (SDIO_LOCAL_OFFSET + 0x0026)
> +/* Free Tx Page Sequence */
> +#define REG_SDIO_FREE_TXPG_SEQ (SDIO_LOCAL_OFFSET + 0x0028)
> +/* HTSF Informaion */
> +#define REG_SDIO_HTSFR_INFO (SDIO_LOCAL_OFFSET + 0x0030)
> +#define REG_SDIO_HCPWM1_V2 (SDIO_LOCAL_OFFSET + 0x0038)
> +/* H2C */
> +#define REG_SDIO_H2C (SDIO_LOCAL_OFFSET + 0x0060)
> +/* HCI Request Power Mode 1 */
> +#define REG_SDIO_HRPWM1 (SDIO_LOCAL_OFFSET + 0x0080)
> +/* HCI Request Power Mode 2 */
> +#define REG_SDIO_HRPWM2 (SDIO_LOCAL_OFFSET + 0x0082)
> +/* HCI Power Save Clock */
> +#define REG_SDIO_HPS_CLKR (SDIO_LOCAL_OFFSET + 0x0084)
> +/* SDIO HCI Suspend Control */
> +#define REG_SDIO_HSUS_CTRL (SDIO_LOCAL_OFFSET + 0x0086)
> +/* SDIO Host Extension Interrupt Mask Always */
> +#define REG_SDIO_HIMR_ON (SDIO_LOCAL_OFFSET + 0x0090)
> +/* SDIO Host Extension Interrupt Status Always */
> +#define REG_SDIO_HISR_ON (SDIO_LOCAL_OFFSET + 0x0091)
> +
> +#define REG_SDIO_INDIRECT_REG_CFG (SDIO_LOCAL_OFFSET + 0x0040)
> +#define REG_SDIO_INDIRECT_REG_DATA (SDIO_LOCAL_OFFSET + 0x0044)
> +
> +/* Sdio Address for SDIO Local Reg, TRX FIFO, MAC Reg */
> +#define REG_SDIO_CMD_ADDR_MSK GENMASK(16, 13)
> +#define REG_SDIO_CMD_ADDR_SDIO_REG 0
> +#define REG_SDIO_CMD_ADDR_MAC_REG 8
> +#define REG_SDIO_CMD_ADDR_TXFF_HIGH 4
> +#define REG_SDIO_CMD_ADDR_TXFF_LOW 6
> +#define REG_SDIO_CMD_ADDR_TXFF_NORMAL 5
> +#define REG_SDIO_CMD_ADDR_TXFF_EXTRA 7
> +#define REG_SDIO_CMD_ADDR_RXFF 7
> +
> +#define RTW_SDIO_BLOCK_SIZE 512
> +#define RTW_SDIO_ADDR_RX_RX0FF_GEN(_id) (0x0e000 | ((_id) & 0x3))
> +
> +#define RTW_SDIO_DATA_PTR_ALIGN 8
> +
> +struct sdio_func;
> +struct sdio_device_id;
> +
> +struct rtw_sdio_tx_data {
> + u8 sn;
> +};
> +
> +struct rtw_sdio_work_data {
> + struct work_struct work;
> + struct rtw_dev *rtwdev;
> +};
> +
> +struct rtw_sdio {
> + struct sdio_func *sdio_func;
> +
> + u32 irq_mask;
> + u8 rx_addr;
> + bool sdio3_bus_mode;
> + bool is_powered_on;
> +
> + void *irq_thread;
> +
> + struct workqueue_struct *txwq;
> +
> + struct sk_buff_head tx_queue[RTK_MAX_TX_QUEUE_NUM];
> + struct rtw_sdio_work_data *tx_handler_data;
> +};
> +
> +extern const struct dev_pm_ops rtw_sdio_pm_ops;
> +
> +int rtw_sdio_probe(struct sdio_func *sdio_func,
> + const struct sdio_device_id *id);
> +void rtw_sdio_remove(struct sdio_func *sdio_func);
> +void rtw_sdio_shutdown(struct device *dev);
> +
> +static inline bool rtw_sdio_is_sdio30_supported(struct rtw_dev *rtwdev)
> +{
> + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
> +
> + return rtwsdio->sdio3_bus_mode;
> +}
> +
> +#endif
> --
> 2.39.0
>
On Wed, Dec 28, 2022 at 12:30:20AM +0100, Martin Blumenstingl wrote:
> Wire up RTL8821CS chipset support using the new rtw88 SDIO HCI code as
> well as the existing RTL8821C chipset code.
>
Unfortunately, this doesn't work for me. I applied it on top of 6.2-rc2
master and I get errors during probe (it appears the firmware never
loads).
Relevant dmesg logs are as follows:
[ 0.989545] mmc2: new high speed SDIO card at address 0001
[ 0.989993] rtw_8821cs mmc2:0001:1: Firmware version 24.8.0, H2C version 12
[ 1.005684] rtw_8821cs mmc2:0001:1: sdio write32 failed (0x14): -110
[ 1.005737] rtw_8821cs mmc2:0001:1: sdio read32 failed (0x1080): -110
[ 1.005789] rtw_8821cs mmc2:0001:1: sdio write32 failed (0x11080): -110
[ 1.005840] rtw_8821cs mmc2:0001:1: sdio read8 failed (0x3): -110
[ 1.005920] rtw_8821cs mmc2:0001:1: sdio read8 failed (0x1103): -110
[ 1.005998] rtw_8821cs mmc2:0001:1: sdio read32 failed (0x80): -110
[ 1.006078] rtw_8821cs mmc2:0001:1: sdio read32 failed (0x1700): -110
The error of "sdio read32 failed (0x1700): -110" then repeats several
hundred times, then I get this:
[ 1.066294] rtw_8821cs mmc2:0001:1: failed to download firmware
[ 1.066367] rtw_8821cs mmc2:0001:1: sdio read16 failed (0x80): -110
[ 1.066417] rtw_8821cs mmc2:0001:1: sdio read8 failed (0x100): -110
[ 1.066697] rtw_8821cs mmc2:0001:1: failed to setup chip efuse info
[ 1.066703] rtw_8821cs mmc2:0001:1: failed to setup chip information
[ 1.066839] rtw_8821cs: probe of mmc2:0001:1 failed with error -16
The hardware I am using is an rtl8821cs that I can confirm was working
with a previous driver.
Thank you.
> Signed-off-by: Martin Blumenstingl <[email protected]>
> ---
> drivers/net/wireless/realtek/rtw88/Kconfig | 11 ++++++
> drivers/net/wireless/realtek/rtw88/Makefile | 3 ++
> .../net/wireless/realtek/rtw88/rtw8821cs.c | 34 +++++++++++++++++++
> 3 files changed, 48 insertions(+)
> create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8821cs.c
>
> diff --git a/drivers/net/wireless/realtek/rtw88/Kconfig b/drivers/net/wireless/realtek/rtw88/Kconfig
> index 6b65da81127f..29eb2f8e0eb7 100644
> --- a/drivers/net/wireless/realtek/rtw88/Kconfig
> +++ b/drivers/net/wireless/realtek/rtw88/Kconfig
> @@ -133,6 +133,17 @@ config RTW88_8821CE
>
> 802.11ac PCIe wireless network adapter
>
> +config RTW88_8821CS
> + tristate "Realtek 8821CS SDIO wireless network adapter"
> + depends on MMC
> + select RTW88_CORE
> + select RTW88_SDIO
> + select RTW88_8821C
> + help
> + Select this option will enable support for 8821CS chipset
> +
> + 802.11ac SDIO wireless network adapter
> +
> config RTW88_8821CU
> tristate "Realtek 8821CU USB wireless network adapter"
> depends on USB
> diff --git a/drivers/net/wireless/realtek/rtw88/Makefile b/drivers/net/wireless/realtek/rtw88/Makefile
> index 6105c2745bda..82979b30ae8d 100644
> --- a/drivers/net/wireless/realtek/rtw88/Makefile
> +++ b/drivers/net/wireless/realtek/rtw88/Makefile
> @@ -59,6 +59,9 @@ rtw88_8821c-objs := rtw8821c.o rtw8821c_table.o
> obj-$(CONFIG_RTW88_8821CE) += rtw88_8821ce.o
> rtw88_8821ce-objs := rtw8821ce.o
>
> +obj-$(CONFIG_RTW88_8821CS) += rtw88_8821cs.o
> +rtw88_8821cs-objs := rtw8821cs.o
> +
> obj-$(CONFIG_RTW88_8821CU) += rtw88_8821cu.o
> rtw88_8821cu-objs := rtw8821cu.o
>
> diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821cs.c b/drivers/net/wireless/realtek/rtw88/rtw8821cs.c
> new file mode 100644
> index 000000000000..61f82b38cda4
> --- /dev/null
> +++ b/drivers/net/wireless/realtek/rtw88/rtw8821cs.c
> @@ -0,0 +1,34 @@
> +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
> +// Copyright(c) Martin Blumenstingl <[email protected]>
> +
> +#include <linux/mmc/sdio_func.h>
> +#include <linux/mmc/sdio_ids.h>
> +#include <linux/module.h>
> +#include "sdio.h"
> +#include "rtw8821c.h"
> +
> +static const struct sdio_device_id rtw_8821cs_id_table[] = {
> + {
> + SDIO_DEVICE(SDIO_VENDOR_ID_REALTEK,
> + SDIO_DEVICE_ID_REALTEK_RTW8821CS),
> + .driver_data = (kernel_ulong_t)&rtw8821c_hw_spec,
> + },
> + {}
> +};
> +MODULE_DEVICE_TABLE(sdio, rtw_8821cs_id_table);
> +
> +static struct sdio_driver rtw_8821cs_driver = {
> + .name = "rtw_8821cs",
> + .probe = rtw_sdio_probe,
> + .remove = rtw_sdio_remove,
> + .id_table = rtw_8821cs_id_table,
> + .drv = {
> + .pm = &rtw_sdio_pm_ops,
> + .shutdown = rtw_sdio_shutdown,
> + }
> +};
> +module_sdio_driver(rtw_8821cs_driver);
> +
> +MODULE_AUTHOR("Martin Blumenstingl <[email protected]>");
> +MODULE_DESCRIPTION("Realtek 802.11ac wireless 8821cs driver");
> +MODULE_LICENSE("Dual BSD/GPL");
> --
> 2.39.0
>
Hi Chris,
On Wed, Jan 4, 2023 at 12:01 AM Chris Morgan <[email protected]> wrote:
>
> On Wed, Dec 28, 2022 at 12:30:20AM +0100, Martin Blumenstingl wrote:
> > Wire up RTL8821CS chipset support using the new rtw88 SDIO HCI code as
> > well as the existing RTL8821C chipset code.
> >
>
> Unfortunately, this doesn't work for me. I applied it on top of 6.2-rc2
> master and I get errors during probe (it appears the firmware never
> loads).
That's unfortunate.
> Relevant dmesg logs are as follows:
>
> [ 0.989545] mmc2: new high speed SDIO card at address 0001
> [ 0.989993] rtw_8821cs mmc2:0001:1: Firmware version 24.8.0, H2C version 12
> [ 1.005684] rtw_8821cs mmc2:0001:1: sdio write32 failed (0x14): -110
> [ 1.005737] rtw_8821cs mmc2:0001:1: sdio read32 failed (0x1080): -110
> [ 1.005789] rtw_8821cs mmc2:0001:1: sdio write32 failed (0x11080): -110
> [ 1.005840] rtw_8821cs mmc2:0001:1: sdio read8 failed (0x3): -110
> [ 1.005920] rtw_8821cs mmc2:0001:1: sdio read8 failed (0x1103): -110
> [ 1.005998] rtw_8821cs mmc2:0001:1: sdio read32 failed (0x80): -110
> [ 1.006078] rtw_8821cs mmc2:0001:1: sdio read32 failed (0x1700): -110
The error starts with a write to register 0x14 (REG_SDIO_HIMR), which
happens right after configuring RX aggregation.
Can you please try two modifications inside
drivers/net/wireless/realtek/rtw88/sdio.c:
1. inside the rtw_sdio_start() function: change
"rtw_sdio_rx_aggregation(rtwdev, false);" to
"rtw_sdio_rx_aggregation(rtwdev, true);"
2. if 1) does not work: remove the call to rtw_sdio_rx_aggregation()
from rtw_sdio_start()
Best regards,
Martin
On Wed, Jan 04, 2023 at 04:40:51PM +0100, Martin Blumenstingl wrote:
> Hi Chris,
>
> On Wed, Jan 4, 2023 at 12:01 AM Chris Morgan <[email protected]> wrote:
> >
> > On Wed, Dec 28, 2022 at 12:30:20AM +0100, Martin Blumenstingl wrote:
> > > Wire up RTL8821CS chipset support using the new rtw88 SDIO HCI code as
> > > well as the existing RTL8821C chipset code.
> > >
> >
> > Unfortunately, this doesn't work for me. I applied it on top of 6.2-rc2
> > master and I get errors during probe (it appears the firmware never
> > loads).
> That's unfortunate.
>
> > Relevant dmesg logs are as follows:
> >
> > [ 0.989545] mmc2: new high speed SDIO card at address 0001
> > [ 0.989993] rtw_8821cs mmc2:0001:1: Firmware version 24.8.0, H2C version 12
> > [ 1.005684] rtw_8821cs mmc2:0001:1: sdio write32 failed (0x14): -110
> > [ 1.005737] rtw_8821cs mmc2:0001:1: sdio read32 failed (0x1080): -110
> > [ 1.005789] rtw_8821cs mmc2:0001:1: sdio write32 failed (0x11080): -110
> > [ 1.005840] rtw_8821cs mmc2:0001:1: sdio read8 failed (0x3): -110
> > [ 1.005920] rtw_8821cs mmc2:0001:1: sdio read8 failed (0x1103): -110
> > [ 1.005998] rtw_8821cs mmc2:0001:1: sdio read32 failed (0x80): -110
> > [ 1.006078] rtw_8821cs mmc2:0001:1: sdio read32 failed (0x1700): -110
> The error starts with a write to register 0x14 (REG_SDIO_HIMR), which
> happens right after configuring RX aggregation.
> Can you please try two modifications inside
> drivers/net/wireless/realtek/rtw88/sdio.c:
> 1. inside the rtw_sdio_start() function: change
> "rtw_sdio_rx_aggregation(rtwdev, false);" to
> "rtw_sdio_rx_aggregation(rtwdev, true);"
No change, still receive identical issue.
> 2. if 1) does not work: remove the call to rtw_sdio_rx_aggregation()
> from rtw_sdio_start()
>
Same here, still receive identical issue.
>
> Best regards,
> Martin
On Wed, Jan 4, 2023 at 6:05 PM Chris Morgan <[email protected]> wrote:
[...]
> > > [ 0.989545] mmc2: new high speed SDIO card at address 0001
> > > [ 0.989993] rtw_8821cs mmc2:0001:1: Firmware version 24.8.0, H2C version 12
> > > [ 1.005684] rtw_8821cs mmc2:0001:1: sdio write32 failed (0x14): -110
> > > [ 1.005737] rtw_8821cs mmc2:0001:1: sdio read32 failed (0x1080): -110
> > > [ 1.005789] rtw_8821cs mmc2:0001:1: sdio write32 failed (0x11080): -110
> > > [ 1.005840] rtw_8821cs mmc2:0001:1: sdio read8 failed (0x3): -110
> > > [ 1.005920] rtw_8821cs mmc2:0001:1: sdio read8 failed (0x1103): -110
> > > [ 1.005998] rtw_8821cs mmc2:0001:1: sdio read32 failed (0x80): -110
> > > [ 1.006078] rtw_8821cs mmc2:0001:1: sdio read32 failed (0x1700): -110
> > The error starts with a write to register 0x14 (REG_SDIO_HIMR), which
> > happens right after configuring RX aggregation.
> > Can you please try two modifications inside
> > drivers/net/wireless/realtek/rtw88/sdio.c:
> > 1. inside the rtw_sdio_start() function: change
> > "rtw_sdio_rx_aggregation(rtwdev, false);" to
> > "rtw_sdio_rx_aggregation(rtwdev, true);"
>
> No change, still receive identical issue.
>
> > 2. if 1) does not work: remove the call to rtw_sdio_rx_aggregation()
> > from rtw_sdio_start()
> >
>
> Same here, still receive identical issue.
Thanks for testing and for reporting back!
Looking back at it again: I think I mis-interpreted your error output.
I think it's actually failing in __rtw_mac_init_system_cfg()
Can you please try the latest code from [0] (ignoring any changes I
recommended previously)?
There's two bug fixes in there (compared to this series) which may
solve the issue that you are seeing:
- fix typos to use "if (!*err_ret ..." (to read the error code)
instead of "if (!err_ret ..." (which just checks if a non-null pointer
was passed) in rtw_sdio_read_indirect{8,32}
- change buf[0] to buf[i] in rtw_sdio_read_indirect_bytes
These fixes will be part of v2 of this series anyways.
Best regards,
Martin
[0] https://github.com/xdarklight/linux/tree/d115a8631d208996510822f0805df5dfc8dfb548
On 04/01/2023 01:01, Chris Morgan wrote:
> On Wed, Dec 28, 2022 at 12:30:20AM +0100, Martin Blumenstingl wrote:
>> Wire up RTL8821CS chipset support using the new rtw88 SDIO HCI code as
>> well as the existing RTL8821C chipset code.
>>
>
> Unfortunately, this doesn't work for me. I applied it on top of 6.2-rc2
> master and I get errors during probe (it appears the firmware never
> loads).
>
> Relevant dmesg logs are as follows:
>
> [ 0.989545] mmc2: new high speed SDIO card at address 0001
> [ 0.989993] rtw_8821cs mmc2:0001:1: Firmware version 24.8.0, H2C version 12
> [ 1.005684] rtw_8821cs mmc2:0001:1: sdio write32 failed (0x14): -110
> [ 1.005737] rtw_8821cs mmc2:0001:1: sdio read32 failed (0x1080): -110
> [ 1.005789] rtw_8821cs mmc2:0001:1: sdio write32 failed (0x11080): -110
> [ 1.005840] rtw_8821cs mmc2:0001:1: sdio read8 failed (0x3): -110
> [ 1.005920] rtw_8821cs mmc2:0001:1: sdio read8 failed (0x1103): -110
> [ 1.005998] rtw_8821cs mmc2:0001:1: sdio read32 failed (0x80): -110
> [ 1.006078] rtw_8821cs mmc2:0001:1: sdio read32 failed (0x1700): -110
>
> The error of "sdio read32 failed (0x1700): -110" then repeats several
> hundred times, then I get this:
>
> [ 1.066294] rtw_8821cs mmc2:0001:1: failed to download firmware
> [ 1.066367] rtw_8821cs mmc2:0001:1: sdio read16 failed (0x80): -110
> [ 1.066417] rtw_8821cs mmc2:0001:1: sdio read8 failed (0x100): -110
> [ 1.066697] rtw_8821cs mmc2:0001:1: failed to setup chip efuse info
> [ 1.066703] rtw_8821cs mmc2:0001:1: failed to setup chip information
> [ 1.066839] rtw_8821cs: probe of mmc2:0001:1 failed with error -16
>
> The hardware I am using is an rtl8821cs that I can confirm was working
> with a previous driver.
>
> Thank you.
>
The USB-based RTL8811CU also doesn't work, with suspiciously similar
errors:
Dec 25 21:43:37 home kernel: rtw_8821cu 1-2:1.0: Firmware version 24.11.0, H2C version 12
Dec 25 21:43:37 home kernel: rtw_8821cu 1-2:1.0 wlp0s20f0u2: renamed from wlan0
Dec 25 21:43:40 home kernel: rtw_8821cu 1-2:1.0: read register 0x5 failed with -110
Dec 25 21:43:41 home kernel: rtw_8821cu 1-2:1.0: read register 0x20 failed with -110
Dec 25 21:44:11 home kernel: rtw_8821cu 1-2:1.0: write register 0x20 failed with -110
Dec 25 21:44:12 home kernel: rtw_8821cu 1-2:1.0: read register 0x7c failed with -110
Dec 25 21:44:43 home kernel: rtw_8821cu 1-2:1.0: write register 0x7c failed with -110
Dec 25 21:44:44 home kernel: rtw_8821cu 1-2:1.0: read register 0x1080 failed with -110
Dec 25 21:45:16 home kernel: rtw_8821cu 1-2:1.0: write register 0x1080 failed with -110
Dec 25 21:46:47 home kernel: rtw_8821cu 1-3:1.0: Firmware version 24.11.0, H2C version 12
Dec 25 21:46:47 home kernel: rtw_8821cu 1-3:1.0 wlp0s20f0u3: renamed from wlan0
Dec 25 21:46:55 home kernel: rtw_8821cu 1-3:1.0: failed to poll offset=0x5 mask=0x1 value=0x0
Dec 25 21:46:55 home kernel: rtw_8821cu 1-3:1.0: mac power on failed
Dec 25 21:46:55 home kernel: rtw_8821cu 1-3:1.0: failed to power on mac
I tested with https://github.com/lwfinger/rtw88/ which should have the
same code as wireless-next currently.
Am 04.01.23 um 20:59 schrieb Bitterblue Smith:
> The USB-based RTL8811CU also doesn't work, with suspiciously similar
> errors:
>
> Dec 25 21:43:37 home kernel: rtw_8821cu 1-2:1.0: Firmware version 24.11.0, H2C version 12
> Dec 25 21:43:37 home kernel: rtw_8821cu 1-2:1.0 wlp0s20f0u2: renamed from wlan0
> Dec 25 21:43:40 home kernel: rtw_8821cu 1-2:1.0: read register 0x5 failed with -110
> Dec 25 21:43:41 home kernel: rtw_8821cu 1-2:1.0: read register 0x20 failed with -110
> Dec 25 21:44:11 home kernel: rtw_8821cu 1-2:1.0: write register 0x20 failed with -110
> Dec 25 21:44:12 home kernel: rtw_8821cu 1-2:1.0: read register 0x7c failed with -110
> Dec 25 21:44:43 home kernel: rtw_8821cu 1-2:1.0: write register 0x7c failed with -110
> Dec 25 21:44:44 home kernel: rtw_8821cu 1-2:1.0: read register 0x1080 failed with -110
> Dec 25 21:45:16 home kernel: rtw_8821cu 1-2:1.0: write register 0x1080 failed with -110
Same for me: I saw very similar read/write failures with my "Realtek
Semiconductor Corp. 802.11ac NIC" (ID 0bda:c811) after applying Ping-Ke's patch
for rfe 38 (see my message to linux-wireless on Dec 29).
Felix
On 1/4/23 13:59, Bitterblue Smith wrote:
> I tested with https://github.com/lwfinger/rtw88/ which should have the
> same code as wireless-next currently.
I just rechecked. My repo was missing some changes from wireless-next. It now
matches.
Larry
On Wed, Jan 04, 2023 at 06:23:24PM +0100, Martin Blumenstingl wrote:
> On Wed, Jan 4, 2023 at 6:05 PM Chris Morgan <[email protected]> wrote:
> [...]
> > > > [ 0.989545] mmc2: new high speed SDIO card at address 0001
> > > > [ 0.989993] rtw_8821cs mmc2:0001:1: Firmware version 24.8.0, H2C version 12
> > > > [ 1.005684] rtw_8821cs mmc2:0001:1: sdio write32 failed (0x14): -110
> > > > [ 1.005737] rtw_8821cs mmc2:0001:1: sdio read32 failed (0x1080): -110
> > > > [ 1.005789] rtw_8821cs mmc2:0001:1: sdio write32 failed (0x11080): -110
> > > > [ 1.005840] rtw_8821cs mmc2:0001:1: sdio read8 failed (0x3): -110
> > > > [ 1.005920] rtw_8821cs mmc2:0001:1: sdio read8 failed (0x1103): -110
> > > > [ 1.005998] rtw_8821cs mmc2:0001:1: sdio read32 failed (0x80): -110
> > > > [ 1.006078] rtw_8821cs mmc2:0001:1: sdio read32 failed (0x1700): -110
> > > The error starts with a write to register 0x14 (REG_SDIO_HIMR), which
> > > happens right after configuring RX aggregation.
> > > Can you please try two modifications inside
> > > drivers/net/wireless/realtek/rtw88/sdio.c:
> > > 1. inside the rtw_sdio_start() function: change
> > > "rtw_sdio_rx_aggregation(rtwdev, false);" to
> > > "rtw_sdio_rx_aggregation(rtwdev, true);"
> >
> > No change, still receive identical issue.
> >
> > > 2. if 1) does not work: remove the call to rtw_sdio_rx_aggregation()
> > > from rtw_sdio_start()
> > >
> >
> > Same here, still receive identical issue.
> Thanks for testing and for reporting back!
>
> Looking back at it again: I think I mis-interpreted your error output.
> I think it's actually failing in __rtw_mac_init_system_cfg()
>
> Can you please try the latest code from [0] (ignoring any changes I
> recommended previously)?
> There's two bug fixes in there (compared to this series) which may
> solve the issue that you are seeing:
> - fix typos to use "if (!*err_ret ..." (to read the error code)
> instead of "if (!err_ret ..." (which just checks if a non-null pointer
> was passed) in rtw_sdio_read_indirect{8,32}
> - change buf[0] to buf[i] in rtw_sdio_read_indirect_bytes
>
> These fixes will be part of v2 of this series anyways.
That still doesn't fix it, I receive the same error. I'm using an older
patch series of yours (that I can't seem to find on github anymore),
so I'll see if I can compare the older series that works with this one
and find out the root cause.
Thank you.
>
>
> Best regards,
> Martin
>
>
> [0] https://github.com/xdarklight/linux/tree/d115a8631d208996510822f0805df5dfc8dfb548
On Wed, Jan 04, 2023 at 09:59:35PM +0200, Bitterblue Smith wrote:
> On 04/01/2023 01:01, Chris Morgan wrote:
> > On Wed, Dec 28, 2022 at 12:30:20AM +0100, Martin Blumenstingl wrote:
> >> Wire up RTL8821CS chipset support using the new rtw88 SDIO HCI code as
> >> well as the existing RTL8821C chipset code.
> >>
> >
> > Unfortunately, this doesn't work for me. I applied it on top of 6.2-rc2
> > master and I get errors during probe (it appears the firmware never
> > loads).
> >
> > Relevant dmesg logs are as follows:
> >
> > [ 0.989545] mmc2: new high speed SDIO card at address 0001
> > [ 0.989993] rtw_8821cs mmc2:0001:1: Firmware version 24.8.0, H2C version 12
> > [ 1.005684] rtw_8821cs mmc2:0001:1: sdio write32 failed (0x14): -110
> > [ 1.005737] rtw_8821cs mmc2:0001:1: sdio read32 failed (0x1080): -110
> > [ 1.005789] rtw_8821cs mmc2:0001:1: sdio write32 failed (0x11080): -110
> > [ 1.005840] rtw_8821cs mmc2:0001:1: sdio read8 failed (0x3): -110
> > [ 1.005920] rtw_8821cs mmc2:0001:1: sdio read8 failed (0x1103): -110
> > [ 1.005998] rtw_8821cs mmc2:0001:1: sdio read32 failed (0x80): -110
> > [ 1.006078] rtw_8821cs mmc2:0001:1: sdio read32 failed (0x1700): -110
> >
> > The error of "sdio read32 failed (0x1700): -110" then repeats several
> > hundred times, then I get this:
> >
> > [ 1.066294] rtw_8821cs mmc2:0001:1: failed to download firmware
> > [ 1.066367] rtw_8821cs mmc2:0001:1: sdio read16 failed (0x80): -110
> > [ 1.066417] rtw_8821cs mmc2:0001:1: sdio read8 failed (0x100): -110
> > [ 1.066697] rtw_8821cs mmc2:0001:1: failed to setup chip efuse info
> > [ 1.066703] rtw_8821cs mmc2:0001:1: failed to setup chip information
> > [ 1.066839] rtw_8821cs: probe of mmc2:0001:1 failed with error -16
> >
> > The hardware I am using is an rtl8821cs that I can confirm was working
> > with a previous driver.
> >
> > Thank you.
> >
> The USB-based RTL8811CU also doesn't work, with suspiciously similar
> errors:
>
> Dec 25 21:43:37 home kernel: rtw_8821cu 1-2:1.0: Firmware version 24.11.0, H2C version 12
> Dec 25 21:43:37 home kernel: rtw_8821cu 1-2:1.0 wlp0s20f0u2: renamed from wlan0
> Dec 25 21:43:40 home kernel: rtw_8821cu 1-2:1.0: read register 0x5 failed with -110
Is this the very first register access or are there other register
accesses before that actually do work?
Sascha
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
On 05/01/2023 10:01, Sascha Hauer wrote:
> On Wed, Jan 04, 2023 at 09:59:35PM +0200, Bitterblue Smith wrote:
>> On 04/01/2023 01:01, Chris Morgan wrote:
>>> On Wed, Dec 28, 2022 at 12:30:20AM +0100, Martin Blumenstingl wrote:
>>>> Wire up RTL8821CS chipset support using the new rtw88 SDIO HCI code as
>>>> well as the existing RTL8821C chipset code.
>>>>
>>>
>>> Unfortunately, this doesn't work for me. I applied it on top of 6.2-rc2
>>> master and I get errors during probe (it appears the firmware never
>>> loads).
>>>
>>> Relevant dmesg logs are as follows:
>>>
>>> [ 0.989545] mmc2: new high speed SDIO card at address 0001
>>> [ 0.989993] rtw_8821cs mmc2:0001:1: Firmware version 24.8.0, H2C version 12
>>> [ 1.005684] rtw_8821cs mmc2:0001:1: sdio write32 failed (0x14): -110
>>> [ 1.005737] rtw_8821cs mmc2:0001:1: sdio read32 failed (0x1080): -110
>>> [ 1.005789] rtw_8821cs mmc2:0001:1: sdio write32 failed (0x11080): -110
>>> [ 1.005840] rtw_8821cs mmc2:0001:1: sdio read8 failed (0x3): -110
>>> [ 1.005920] rtw_8821cs mmc2:0001:1: sdio read8 failed (0x1103): -110
>>> [ 1.005998] rtw_8821cs mmc2:0001:1: sdio read32 failed (0x80): -110
>>> [ 1.006078] rtw_8821cs mmc2:0001:1: sdio read32 failed (0x1700): -110
>>>
>>> The error of "sdio read32 failed (0x1700): -110" then repeats several
>>> hundred times, then I get this:
>>>
>>> [ 1.066294] rtw_8821cs mmc2:0001:1: failed to download firmware
>>> [ 1.066367] rtw_8821cs mmc2:0001:1: sdio read16 failed (0x80): -110
>>> [ 1.066417] rtw_8821cs mmc2:0001:1: sdio read8 failed (0x100): -110
>>> [ 1.066697] rtw_8821cs mmc2:0001:1: failed to setup chip efuse info
>>> [ 1.066703] rtw_8821cs mmc2:0001:1: failed to setup chip information
>>> [ 1.066839] rtw_8821cs: probe of mmc2:0001:1 failed with error -16
>>>
>>> The hardware I am using is an rtl8821cs that I can confirm was working
>>> with a previous driver.
>>>
>>> Thank you.
>>>
>> The USB-based RTL8811CU also doesn't work, with suspiciously similar
>> errors:
>>
>> Dec 25 21:43:37 home kernel: rtw_8821cu 1-2:1.0: Firmware version 24.11.0, H2C version 12
>> Dec 25 21:43:37 home kernel: rtw_8821cu 1-2:1.0 wlp0s20f0u2: renamed from wlan0
>> Dec 25 21:43:40 home kernel: rtw_8821cu 1-2:1.0: read register 0x5 failed with -110
>
> Is this the very first register access or are there other register
> accesses before that actually do work?
>
> Sascha
>
It's not the first register access. rtw_mac_power_switch() runs a few
times before things fail.
On 04/01/2023 22:14, Larry Finger wrote:
> On 1/4/23 13:59, Bitterblue Smith wrote:
>> I tested with https://github.com/lwfinger/rtw88/ which should have the
>> same code as wireless-next currently.
>
> I just rechecked. My repo was missing some changes from wireless-next. It now matches.
>
> Larry
>
>
Did you mean to push some commits? The latest one is still from December.
(Jakub, a question for you below)
Martin Blumenstingl <[email protected]> writes:
> Recently the rtw88 driver has gained locking support for the "slow" bus
> types (USB, SDIO) as part of USB support. Thanks to everyone who helped
> make this happen!
>
> Based on the USB work (especially the locking part and various
> bugfixes) this series adds support for SDIO based cards. It's the
> result of a collaboration between Jernej and myself. Neither of us has
> access to the rtw88 datasheets. All of our work is based on studying
> the RTL8822BS and RTL8822CS vendor drivers and trial and error.
>
> Jernej and myself have tested this with RTL8822BS and RTL8822CS cards.
> Other users have confirmed that RTL8821CS support is working as well.
> RTL8723DS may also work (we tried our best to handle rtw_chip_wcpu_11n
> where needed) but has not been tested at this point.
Very nice, good work.
Our recommendation is to have maximum of 10-12 patches per patchset to
make it easier for us maintainers, any chance to split the patchset into
two? For example, get the easy preparation patches into wireless-next
first and later submit the actual SDIO support.
[...]
> Why is this an RFC?
[...]
> - My understanding is that there's a discussion about the rtw88 Kconfig
> symbols. We're adding four new ones within this series. It's not
> clear to me what the conclusion is on this topic though.
Yeah, there were no conclusions about that. Jakub, do you have any
opinions? For example, do we keep per device Kconfig options (eg.
CONFIG_RTW88_8822BS, RTW88_8822CS and so on) or should we have only one
more bus level option (eg. CONFIG_RTW88_SDIO)? rtw88 now uses the former
and IIRC so does mt76. ath10k/ath11k/ath12k again use the latter :)
--
https://patchwork.kernel.org/project/linux-wireless/list/
https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches
On Mon, 16 Jan 2023 18:01:05 +0200 Kalle Valo wrote:
> > - My understanding is that there's a discussion about the rtw88 Kconfig
> > symbols. We're adding four new ones within this series. It's not
> > clear to me what the conclusion is on this topic though.
>
> Yeah, there were no conclusions about that. Jakub, do you have any
> opinions? For example, do we keep per device Kconfig options (eg.
> CONFIG_RTW88_8822BS, RTW88_8822CS and so on) or should we have only one
> more bus level option (eg. CONFIG_RTW88_SDIO)? rtw88 now uses the former
> and IIRC so does mt76. ath10k/ath11k/ath12k again use the latter :)
No strong feelings. Larry (IIRC) provided a fair justification for
the RTW symbols. If the module binary grows noticeably then having
the kconfig does indeed make sense.
Jakub Kicinski <[email protected]> writes:
> On Mon, 16 Jan 2023 18:01:05 +0200 Kalle Valo wrote:
>> > - My understanding is that there's a discussion about the rtw88 Kconfig
>> > symbols. We're adding four new ones within this series. It's not
>> > clear to me what the conclusion is on this topic though.
>>
>> Yeah, there were no conclusions about that. Jakub, do you have any
>> opinions? For example, do we keep per device Kconfig options (eg.
>> CONFIG_RTW88_8822BS, RTW88_8822CS and so on) or should we have only one
>> more bus level option (eg. CONFIG_RTW88_SDIO)? rtw88 now uses the former
>> and IIRC so does mt76. ath10k/ath11k/ath12k again use the latter :)
>
> No strong feelings. Larry (IIRC) provided a fair justification for
> the RTW symbols. If the module binary grows noticeably then having
> the kconfig does indeed make sense.
Thanks, makes sense. So the plan is that rtw88 continues to use per
device Kconfig symbols with SDIO.
--
https://patchwork.kernel.org/project/linux-wireless/list/
https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches