]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
misc: rtsx: usb card reader: add OCP support
authorRicky Wu <ricky_wu@realtek.com>
Tue, 12 Aug 2025 03:08:11 +0000 (11:08 +0800)
committerUlf Hansson <ulf.hansson@linaro.org>
Tue, 19 Aug 2025 12:34:15 +0000 (14:34 +0200)
This patch adds support for Over Current Protection (OCP) to the Realtek
USB card reader driver.

The OCP mechanism protects the hardware by detecting and handling current
overload conditions.
This implementation includes:

- Register configurations to enable OCP monitoring.
- Handling of OCP interrupt events and associated error reporting.
- Card power management changes in response to OCP triggers.

This enhancement improves the robustness of the driver when operating in
environments where electrical anomalies may occur, particularly with SD
and MS card interfaces.

Signed-off-by: Ricky Wu <ricky_wu@realtek.com>
Link: https://lore.kernel.org/r/20250812030811.2426112-1-ricky_wu@realtek.com
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
drivers/memstick/host/rtsx_usb_ms.c
drivers/misc/cardreader/rtsx_usb.c
drivers/mmc/host/rtsx_usb_sdmmc.c
include/linux/rtsx_usb.h

index 3878136227e49c10a979595a45a69aab187cd9b0..9389e9643c2455eb41182fcf3074055ca7d2b9f3 100644 (file)
@@ -216,7 +216,10 @@ static int ms_power_off(struct rtsx_usb_ms *host)
 
        rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_CLK_EN, MS_CLK_EN, 0);
        rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_OE, MS_OUTPUT_EN, 0);
-
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL,
+                       POWER_MASK, POWER_OFF);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL,
+                       POWER_MASK | LDO3318_PWR_MASK, POWER_OFF | LDO_SUSPEND);
        err = rtsx_usb_send_cmd(ucr, MODE_C, 100);
        if (err < 0)
                return err;
index d007a4455ce5ba319f70f66645224d5a24448041..1830e9ed252165db88d7dcdf084a4dd098a4f378 100644 (file)
@@ -552,6 +552,10 @@ static int rtsx_usb_reset_chip(struct rtsx_ucr *ucr)
        ret = rtsx_usb_send_cmd(ucr, MODE_C, 100);
        if (ret)
                return ret;
+       /* config OCP */
+       rtsx_usb_write_register(ucr, OCPCTL, MS_OCP_DETECT_EN, MS_OCP_DETECT_EN);
+       rtsx_usb_write_register(ucr, OCPPARA1, 0xF0, 0x50);
+       rtsx_usb_write_register(ucr, OCPPARA2, 0x7, 0x3);
 
        /* config non-crystal mode */
        rtsx_usb_read_register(ucr, CFG_MODE, &val);
@@ -722,6 +726,9 @@ static int rtsx_usb_suspend(struct usb_interface *intf, pm_message_t message)
                        if (val & (SD_CD | MS_CD)) {
                                device_for_each_child(&intf->dev, NULL, rtsx_usb_resume_child);
                                return -EAGAIN;
+                       } else {
+                               /* if the card does not exists, clear OCP status */
+                               rtsx_usb_write_register(ucr, OCPCTL, MS_OCP_CLEAR, MS_OCP_CLEAR);
                        }
                } else {
                        /* There is an ongoing operation*/
index c5f6b9df066b589bb70d22064a615ab21c94c83c..e1ed39c657c37ccd51b1d87a57ce0db8efcf633d 100644 (file)
@@ -48,7 +48,7 @@ struct rtsx_usb_sdmmc {
        bool                    ddr_mode;
 
        unsigned char           power_mode;
-
+       u16                     ocp_stat;
 #ifdef RTSX_USB_USE_LEDS_CLASS
        struct led_classdev     led;
        char                    led_name[32];
@@ -785,6 +785,9 @@ static int sdmmc_get_cd(struct mmc_host *mmc)
 
        mutex_unlock(&ucr->dev_mutex);
 
+       /* get OCP status */
+       host->ocp_stat = (val >> 4) & 0x03;
+
        /* Treat failed detection as non-exist */
        if (err)
                goto no_card;
@@ -795,6 +798,11 @@ static int sdmmc_get_cd(struct mmc_host *mmc)
        }
 
 no_card:
+       /* clear OCP status */
+       if (host->ocp_stat & (MS_OCP_NOW | MS_OCP_EVER)) {
+               rtsx_usb_write_register(ucr, OCPCTL, MS_OCP_CLEAR, MS_OCP_CLEAR);
+               host->ocp_stat = 0;
+       }
        host->card_exist = false;
        return 0;
 }
@@ -818,7 +826,11 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
                cmd->error = -ENOMEDIUM;
                goto finish_detect_card;
        }
-
+       /* check OCP stat */
+       if (host->ocp_stat & (MS_OCP_NOW | MS_OCP_EVER)) {
+               cmd->error = -ENOMEDIUM;
+               goto finish_detect_card;
+       }
        mutex_lock(&ucr->dev_mutex);
 
        mutex_lock(&host->host_mutex);
@@ -952,6 +964,10 @@ static int sd_power_on(struct rtsx_usb_sdmmc *host)
        struct rtsx_ucr *ucr = host->ucr;
        int err;
 
+       if (host->ocp_stat & (MS_OCP_NOW | MS_OCP_EVER)) {
+               dev_dbg(sdmmc_dev(host), "over current\n");
+               return -EIO;
+       }
        dev_dbg(sdmmc_dev(host), "%s\n", __func__);
        rtsx_usb_init_cmd(ucr);
        rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_SELECT, 0x07, SD_MOD_SEL);
@@ -977,9 +993,19 @@ static int sd_power_on(struct rtsx_usb_sdmmc *host)
 
        usleep_range(800, 1000);
 
+       rtsx_usb_init_cmd(ucr);
+       /* WA OCP issue: after OCP, there were problems with reopen card power */
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL, POWER_MASK, POWER_ON);
+       rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, FPDCTL, SSC_POWER_MASK, SSC_POWER_DOWN);
+       err = rtsx_usb_send_cmd(ucr, MODE_C, 100);
+       if (err)
+               return err;
+       msleep(20);
+       rtsx_usb_write_register(ucr, FPDCTL, SSC_POWER_MASK, SSC_POWER_ON);
+       usleep_range(180, 200);
        rtsx_usb_init_cmd(ucr);
        rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL,
-                       POWER_MASK|LDO3318_PWR_MASK, POWER_ON|LDO_ON);
+                       LDO3318_PWR_MASK, LDO_ON);
        rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_OE,
                        SD_OUTPUT_EN, SD_OUTPUT_EN);
 
@@ -1332,6 +1358,7 @@ static void rtsx_usb_init_host(struct rtsx_usb_sdmmc *host)
        mmc->max_req_size = 524288;
 
        host->power_mode = MMC_POWER_OFF;
+       host->ocp_stat = 0;
 }
 
 static int rtsx_usb_sdmmc_drv_probe(struct platform_device *pdev)
index f267a06c6b1e7ef3c5b87684fe32182fd28caa80..276b509c03e3646c1427c235f4a01d9aedd6d51b 100644 (file)
@@ -99,6 +99,17 @@ extern int rtsx_usb_card_exclusive_check(struct rtsx_ucr *ucr, int card);
 #define CD_MASK                (SD_CD | MS_CD | XD_CD)
 #define SD_WP          0x08
 
+/* OCPCTL */
+#define MS_OCP_DETECT_EN               0x08
+#define        MS_OCP_INT_EN                   0x04
+#define        MS_OCP_INT_CLR                  0x02
+#define        MS_OCP_CLEAR                    0x01
+
+/* OCPSTAT */
+#define MS_OCP_DETECT                  0x80
+#define MS_OCP_NOW                     0x02
+#define MS_OCP_EVER                    0x01
+
 /* reader command field offset & parameters */
 #define READ_REG_CMD           0
 #define WRITE_REG_CMD          1