From: MIIX 3-1030 field investigation Subject: [PATCH] staging: r8723bs: add opt-in legacy SDIO timing workaround The Lenovo MIIX 3-1030 (DMI board Martini, RTL8723BS 024c:b723 on INT33BB:00 UID 2) can negotiate SDIO high-speed mode successfully while all subsequent host-to-card CMD53 data writes fail with CRC errors. Add two opt-in workarounds: force_sdio_legacy=1 Clear SDIO_SPEED_EHS before the first Realtek I/O operation and put the host into MMC_TIMING_LEGACY at 25 MHz. force_cmd52_write32=1 Use four CMD52 byte writes for aligned 32-bit register writes. Bulk data and the transmit FIFO continue to use CMD53. This patch was developed and tested against Ubuntu's 7.0.0-28 kernel. It may need context changes for other kernel versions. diff --git a/drivers/staging/rtl8723bs/hal/sdio_ops.c b/drivers/staging/rtl8723bs/hal/sdio_ops.c index c9cb20c61a2b..8c46230ac874 100644 --- a/drivers/staging/rtl8723bs/hal/sdio_ops.c +++ b/drivers/staging/rtl8723bs/hal/sdio_ops.c @@ -7,6 +7,11 @@ #include #include +static bool force_cmd52_write32; +module_param(force_cmd52_write32, bool, 0644); +MODULE_PARM_DESC(force_cmd52_write32, + "Use four CMD52 byte writes instead of a CMD53 32-bit write"); + /* */ /* Description: */ /* The following mapping is for SDIO host local register space. */ @@ -290,9 +295,11 @@ static s32 sdio_write32(struct intf_hdl *intfhdl, u32 addr, u32 val) /* 4 bytes alignment */ shift = ftaddr & 0x3; - if (shift == 0) { + if (shift == 0 && !force_cmd52_write32) { sd_write32(intfhdl, ftaddr, val, &err); } else { + if (force_cmd52_write32) + pr_info_once("r8723bs: forcing 32-bit SDIO writes through CMD52\n"); le_tmp = cpu_to_le32(val); err = sd_cmd52_write(intfhdl, ftaddr, 4, (u8 *)&le_tmp); } diff --git a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c index d664e254912cf..61c2aba9bd830 100644 --- a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c +++ b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c @@ -7,6 +7,14 @@ #include #include #include +#include +#include +#include + +static bool force_sdio_legacy; +module_param(force_sdio_legacy, bool, 0444); +MODULE_PARM_DESC(force_sdio_legacy, + "Disable SDIO high speed and use legacy 25 MHz timing"); #ifndef dev_to_sdio_func #define dev_to_sdio_func(d) container_of(d, struct sdio_func, dev) @@ -33,6 +41,57 @@ static const struct dev_pm_ops rtw_sdio_pm_ops = { .resume = rtw_sdio_resume, }; +static int rtw_force_sdio_legacy(struct sdio_func *func) +{ + struct mmc_host *host = func->card->host; + unsigned int old_quirks; + u8 speed; + u8 new_speed; + unsigned int old_clock; + unsigned int old_timing; + int err = 0; + + if (!force_sdio_legacy) + return 0; + + /* Reverse mmc/core/sdio.c:mmc_sdio_enable_hs() before Realtek I/O. */ + sdio_claim_host(func); + speed = sdio_f0_readb(func, SDIO_CCCR_SPEED, &err); + if (err) + goto release; + + new_speed = speed & ~SDIO_SPEED_EHS; + if (new_speed != speed) { + old_quirks = func->card->quirks; + func->card->quirks |= MMC_QUIRK_LENIENT_FN0; + sdio_f0_writeb(func, new_speed, SDIO_CCCR_SPEED, &err); + func->card->quirks = old_quirks; + if (err) + goto release; + } + + old_clock = host->ios.clock; + old_timing = host->ios.timing; + host->ios.timing = MMC_TIMING_LEGACY; + host->ios.clock = min(host->f_max, 25000000U); + host->ops->set_ios(host, &host->ios); + +release: + sdio_release_host(func); + if (err) { + dev_err(&func->dev, + "failed to force legacy SDIO timing at CCCR_SPEED (%d)\n", + err); + return err; + } + + dev_info(&func->dev, + "forced legacy SDIO (CCCR_SPEED %#02x -> %#02x, timing %u -> %u, clock %u -> %u)\n", + speed, new_speed, old_timing, host->ios.timing, + old_clock, host->ios.clock); + return 0; +} + static struct sdio_driver rtl8723bs_sdio_driver = { .probe = rtw_drv_init, .remove = rtw_dev_remove, @@ -349,6 +408,10 @@ static int rtw_drv_init( struct adapter *if1 = NULL; struct dvobj_priv *dvobj; + status = rtw_force_sdio_legacy(func); + if (status) + goto exit; + dvobj = sdio_dvobj_init(func); if (!dvobj) goto exit; -- 2.46.0