soc/tegra: pmc: Provide USB sleepwalk register map
authorJC Kuo <jckuo@nvidia.com>
Wed, 20 Jan 2021 07:34:07 +0000 (15:34 +0800)
committerThierry Reding <treding@nvidia.com>
Fri, 26 Mar 2021 12:10:25 +0000 (13:10 +0100)
This commit implements a register map which grants USB (UTMI and HSIC)
sleepwalk registers access to USB PHY drivers. The USB sleepwalk logic
is in PMC hardware block but USB PHY drivers have the best knowledge
of proper programming sequence.

Signed-off-by: JC Kuo <jckuo@nvidia.com>
Acked-by: Thierry Reding <treding@nvidia.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
drivers/soc/tegra/pmc.c

index df9a5ca..2e7692d 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/platform_device.h>
 #include <linux/pm_domain.h>
 #include <linux/reboot.h>
+#include <linux/regmap.h>
 #include <linux/reset.h>
 #include <linux/seq_file.h>
 #include <linux/slab.h>
 
 #define PMC_PWR_DET_VALUE              0xe4
 
+#define PMC_USB_DEBOUNCE_DEL           0xec
+#define PMC_USB_AO                     0xf0
+
 #define PMC_SCRATCH41                  0x140
 
 #define PMC_WAKE2_MASK                 0x160
 #define IO_DPD2_STATUS                 0x1c4
 #define SEL_DPD_TIM                    0x1c8
 
+#define PMC_UTMIP_UHSIC_TRIGGERS       0x1ec
+#define PMC_UTMIP_UHSIC_SAVED_STATE    0x1f0
+
+#define PMC_UTMIP_TERM_PAD_CFG         0x1f8
+#define PMC_UTMIP_UHSIC_SLEEP_CFG      0x1fc
+#define PMC_UTMIP_UHSIC_FAKE           0x218
+
 #define PMC_SCRATCH54                  0x258
 #define  PMC_SCRATCH54_DATA_SHIFT      8
 #define  PMC_SCRATCH54_ADDR_SHIFT      0
 #define  PMC_SCRATCH55_CHECKSUM_SHIFT  16
 #define  PMC_SCRATCH55_I2CSLV1_SHIFT   0
 
+#define  PMC_UTMIP_UHSIC_LINE_WAKEUP   0x26c
+
+#define PMC_UTMIP_BIAS_MASTER_CNTRL    0x270
+#define PMC_UTMIP_MASTER_CONFIG                0x274
+#define PMC_UTMIP_UHSIC2_TRIGGERS      0x27c
+#define PMC_UTMIP_MASTER2_CONFIG       0x29c
+
 #define GPU_RG_CNTRL                   0x2d4
 
+#define PMC_UTMIP_PAD_CFG0             0x4c0
+#define PMC_UTMIP_UHSIC_SLEEP_CFG1     0x4d0
+#define PMC_UTMIP_SLEEPWALK_P3         0x4e0
 /* Tegra186 and later */
 #define WAKE_AOWAKE_CNTRL(x) (0x000 + ((x) << 2))
 #define WAKE_AOWAKE_CNTRL_LEVEL (1 << 3)
@@ -334,6 +355,7 @@ struct tegra_pmc_soc {
        const struct pmc_clk_init_data *pmc_clks_data;
        unsigned int num_pmc_clks;
        bool has_blink_output;
+       bool has_usb_sleepwalk;
 };
 
 /**
@@ -2443,6 +2465,67 @@ static void tegra_pmc_clock_register(struct tegra_pmc *pmc,
                         err);
 }
 
+static const struct regmap_range pmc_usb_sleepwalk_ranges[] = {
+       regmap_reg_range(PMC_USB_DEBOUNCE_DEL, PMC_USB_AO),
+       regmap_reg_range(PMC_UTMIP_UHSIC_TRIGGERS, PMC_UTMIP_UHSIC_SAVED_STATE),
+       regmap_reg_range(PMC_UTMIP_TERM_PAD_CFG, PMC_UTMIP_UHSIC_FAKE),
+       regmap_reg_range(PMC_UTMIP_UHSIC_LINE_WAKEUP, PMC_UTMIP_UHSIC_LINE_WAKEUP),
+       regmap_reg_range(PMC_UTMIP_BIAS_MASTER_CNTRL, PMC_UTMIP_MASTER_CONFIG),
+       regmap_reg_range(PMC_UTMIP_UHSIC2_TRIGGERS, PMC_UTMIP_MASTER2_CONFIG),
+       regmap_reg_range(PMC_UTMIP_PAD_CFG0, PMC_UTMIP_UHSIC_SLEEP_CFG1),
+       regmap_reg_range(PMC_UTMIP_SLEEPWALK_P3, PMC_UTMIP_SLEEPWALK_P3),
+};
+
+static const struct regmap_access_table pmc_usb_sleepwalk_table = {
+       .yes_ranges = pmc_usb_sleepwalk_ranges,
+       .n_yes_ranges = ARRAY_SIZE(pmc_usb_sleepwalk_ranges),
+};
+
+static int tegra_pmc_regmap_readl(void *context, unsigned int offset, unsigned int *value)
+{
+       struct tegra_pmc *pmc = context;
+
+       *value = tegra_pmc_readl(pmc, offset);
+       return 0;
+}
+
+static int tegra_pmc_regmap_writel(void *context, unsigned int offset, unsigned int value)
+{
+       struct tegra_pmc *pmc = context;
+
+       tegra_pmc_writel(pmc, value, offset);
+       return 0;
+}
+
+static const struct regmap_config usb_sleepwalk_regmap_config = {
+       .name = "usb_sleepwalk",
+       .reg_bits = 32,
+       .val_bits = 32,
+       .reg_stride = 4,
+       .fast_io = true,
+       .rd_table = &pmc_usb_sleepwalk_table,
+       .wr_table = &pmc_usb_sleepwalk_table,
+       .reg_read = tegra_pmc_regmap_readl,
+       .reg_write = tegra_pmc_regmap_writel,
+};
+
+static int tegra_pmc_regmap_init(struct tegra_pmc *pmc)
+{
+       struct regmap *regmap;
+       int err;
+
+       if (pmc->soc->has_usb_sleepwalk) {
+               regmap = devm_regmap_init(pmc->dev, NULL, pmc, &usb_sleepwalk_regmap_config);
+               if (IS_ERR(regmap)) {
+                       err = PTR_ERR(regmap);
+                       dev_err(pmc->dev, "failed to allocate register map (%d)\n", err);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
 static int tegra_pmc_probe(struct platform_device *pdev)
 {
        void __iomem *base;
@@ -2548,6 +2631,10 @@ static int tegra_pmc_probe(struct platform_device *pdev)
        if (err)
                goto cleanup_restart_handler;
 
+       err = tegra_pmc_regmap_init(pmc);
+       if (err < 0)
+               goto cleanup_restart_handler;
+
        err = tegra_powergate_init(pmc, pdev->dev.of_node);
        if (err < 0)
                goto cleanup_powergates;
@@ -2706,6 +2793,7 @@ static const struct tegra_pmc_soc tegra20_pmc_soc = {
        .pmc_clks_data = NULL,
        .num_pmc_clks = 0,
        .has_blink_output = true,
+       .has_usb_sleepwalk = false,
 };
 
 static const char * const tegra30_powergates[] = {
@@ -2764,6 +2852,7 @@ static const struct tegra_pmc_soc tegra30_pmc_soc = {
        .pmc_clks_data = tegra_pmc_clks_data,
        .num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data),
        .has_blink_output = true,
+       .has_usb_sleepwalk = false,
 };
 
 static const char * const tegra114_powergates[] = {
@@ -2818,6 +2907,7 @@ static const struct tegra_pmc_soc tegra114_pmc_soc = {
        .pmc_clks_data = tegra_pmc_clks_data,
        .num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data),
        .has_blink_output = true,
+       .has_usb_sleepwalk = false,
 };
 
 static const char * const tegra124_powergates[] = {
@@ -2932,6 +3022,7 @@ static const struct tegra_pmc_soc tegra124_pmc_soc = {
        .pmc_clks_data = tegra_pmc_clks_data,
        .num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data),
        .has_blink_output = true,
+       .has_usb_sleepwalk = true,
 };
 
 static const char * const tegra210_powergates[] = {
@@ -3059,6 +3150,7 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = {
        .pmc_clks_data = tegra_pmc_clks_data,
        .num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data),
        .has_blink_output = true,
+       .has_usb_sleepwalk = true,
 };
 
 #define TEGRA186_IO_PAD_TABLE(_pad)                                          \
@@ -3214,6 +3306,7 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = {
        .pmc_clks_data = NULL,
        .num_pmc_clks = 0,
        .has_blink_output = false,
+       .has_usb_sleepwalk = false,
 };
 
 #define TEGRA194_IO_PAD_TABLE(_pad)                                              \
@@ -3347,6 +3440,7 @@ static const struct tegra_pmc_soc tegra194_pmc_soc = {
        .pmc_clks_data = NULL,
        .num_pmc_clks = 0,
        .has_blink_output = false,
+       .has_usb_sleepwalk = false,
 };
 
 static const struct tegra_pmc_regs tegra234_pmc_regs = {