Reviewed-by: Gilad Broner <gbro...@codeaurora.org>

> Adds support for configuring and reading the test bus and debug
> registers. This change also adds another vops in order to print the
> debug registers.
>
> Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>
>
> ---
>  drivers/scsi/ufs/ufs-qcom.c | 165
> +++++++++++++++++++++++++++++++++++++++++++-
>  drivers/scsi/ufs/ufs-qcom.h |  37 +++++++++-
>  drivers/scsi/ufs/ufshcd.c   |   2 +
>  drivers/scsi/ufs/ufshcd.h   |   8 +++
>  4 files changed, 208 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
> index b275a9a..1633808 100644
> --- a/drivers/scsi/ufs/ufs-qcom.c
> +++ b/drivers/scsi/ufs/ufs-qcom.c
> @@ -23,6 +23,24 @@
>  #include "unipro.h"
>  #include "ufs-qcom.h"
>  #include "ufshci.h"
> +#define UFS_QCOM_DEFAULT_DBG_PRINT_EN        \
> +     (UFS_QCOM_DBG_PRINT_REGS_EN | UFS_QCOM_DBG_PRINT_TEST_BUS_EN)
> +
> +enum {
> +     TSTBUS_UAWM,
> +     TSTBUS_UARM,
> +     TSTBUS_TXUC,
> +     TSTBUS_RXUC,
> +     TSTBUS_DFC,
> +     TSTBUS_TRLUT,
> +     TSTBUS_TMRLUT,
> +     TSTBUS_OCSC,
> +     TSTBUS_UTP_HCI,
> +     TSTBUS_COMBINED,
> +     TSTBUS_WRAPPER,
> +     TSTBUS_UNIPRO,
> +     TSTBUS_MAX,
> +};
>
>  static struct ufs_qcom_host *ufs_qcom_hosts[MAX_UFS_QCOM_HOSTS];
>
> @@ -30,6 +48,15 @@ static void ufs_qcom_get_speed_mode(struct
> ufs_pa_layer_attr *p, char *result);
>  static int ufs_qcom_get_bus_vote(struct ufs_qcom_host *host,
>               const char *speed_mode);
>  static int ufs_qcom_set_bus_vote(struct ufs_qcom_host *host, int vote);
> +static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host);
> +static void ufs_qcom_dump_regs(struct ufs_hba *hba, int offset, int len,
> +             char *prefix)
> +{
> +     print_hex_dump(KERN_ERR, prefix,
> +                     len > 4 ? DUMP_PREFIX_OFFSET : DUMP_PREFIX_NONE,
> +                     16, 4, (void __force *)hba->mmio_base + offset,
> +                     len * 4, false);
> +}
>
>  static int ufs_qcom_get_connected_tx_lanes(struct ufs_hba *hba, u32
> *tx_lanes)
>  {
> @@ -996,6 +1023,15 @@ static int ufs_qcom_init(struct ufs_hba *hba)
>       if (hba->dev->id < MAX_UFS_QCOM_HOSTS)
>               ufs_qcom_hosts[hba->dev->id] = host;
>
> +     host->dbg_print_en |= UFS_QCOM_DEFAULT_DBG_PRINT_EN;
> +     ufs_qcom_get_default_testbus_cfg(host);
> +     err = ufs_qcom_testbus_config(host);
> +     if (err) {
> +             dev_warn(dev, "%s: failed to configure the testbus %d\n",
> +                             __func__, err);
> +             err = 0;
> +     }
> +
>       goto out;
>
>  out_disable_phy:
> @@ -1025,12 +1061,134 @@ void ufs_qcom_clk_scale_notify(struct ufs_hba
> *hba)
>
>       if (!dev_req_params)
>               return;
> +}
> +
> +static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host)
> +{
> +     /* provide a legal default configuration */
> +     host->testbus.select_major = TSTBUS_UAWM;
> +     host->testbus.select_minor = 1;
> +}
> +
> +static bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host)
> +{
> +     if (host->testbus.select_major >= TSTBUS_MAX) {
> +             dev_err(host->hba->dev,
> +                     "%s: UFS_CFG1[TEST_BUS_SEL} may not equal 0x%05X\n",
> +                     __func__, host->testbus.select_major);
> +             return false;
> +     }
> +
> +     /*
> +      * Not performing check for each individual select_major
> +      * mappings of select_minor, since there is no harm in
> +      * configuring a non-existent select_minor
> +      */
> +     if (host->testbus.select_minor > 0x1F) {
> +             dev_err(host->hba->dev,
> +                     "%s: 0x%05X is not a legal testbus option\n",
> +                     __func__, host->testbus.select_minor);
> +             return false;
> +     }
> +
> +     return true;
> +}
> +
> +int ufs_qcom_testbus_config(struct ufs_qcom_host *host)
> +{
> +     int reg;
> +     int offset;
> +     u32 mask = TEST_BUS_SUB_SEL_MASK;
> +
> +     if (!host)
> +             return -EINVAL;
>
> -     ufs_qcom_cfg_timers(hba, dev_req_params->gear_rx,
> -                             dev_req_params->pwr_rx,
> -                             dev_req_params->hs_rate);
> +     if (!ufs_qcom_testbus_cfg_is_ok(host))
> +             return -EPERM;
> +
> +     switch (host->testbus.select_major) {
> +     case TSTBUS_UAWM:
> +             reg = UFS_TEST_BUS_CTRL_0;
> +             offset = 24;
> +             break;
> +     case TSTBUS_UARM:
> +             reg = UFS_TEST_BUS_CTRL_0;
> +             offset = 16;
> +             break;
> +     case TSTBUS_TXUC:
> +             reg = UFS_TEST_BUS_CTRL_0;
> +             offset = 8;
> +             break;
> +     case TSTBUS_RXUC:
> +             reg = UFS_TEST_BUS_CTRL_0;
> +             offset = 0;
> +             break;
> +     case TSTBUS_DFC:
> +             reg = UFS_TEST_BUS_CTRL_1;
> +             offset = 24;
> +             break;
> +     case TSTBUS_TRLUT:
> +             reg = UFS_TEST_BUS_CTRL_1;
> +             offset = 16;
> +             break;
> +     case TSTBUS_TMRLUT:
> +             reg = UFS_TEST_BUS_CTRL_1;
> +             offset = 8;
> +             break;
> +     case TSTBUS_OCSC:
> +             reg = UFS_TEST_BUS_CTRL_1;
> +             offset = 0;
> +             break;
> +     case TSTBUS_WRAPPER:
> +             reg = UFS_TEST_BUS_CTRL_2;
> +             offset = 16;
> +             break;
> +     case TSTBUS_COMBINED:
> +             reg = UFS_TEST_BUS_CTRL_2;
> +             offset = 8;
> +             break;
> +     case TSTBUS_UTP_HCI:
> +             reg = UFS_TEST_BUS_CTRL_2;
> +             offset = 0;
> +             break;
> +     case TSTBUS_UNIPRO:
> +             reg = UFS_UNIPRO_CFG;
> +             offset = 1;
> +             break;
> +     /*
> +      * No need for a default case, since
> +      * ufs_qcom_testbus_cfg_is_ok() checks that the configuration
> +      * is legal
> +      */
> +     }
> +     mask <<= offset;
> +
> +     pm_runtime_get_sync(host->hba->dev);
> +     ufshcd_hold(host->hba, false);
> +     ufshcd_rmwl(host->hba, TEST_BUS_SEL,
> +                 (u32)host->testbus.select_major << 19,
> +                 REG_UFS_CFG1);
> +     ufshcd_rmwl(host->hba, mask,
> +                 (u32)host->testbus.select_minor << offset,
> +                 reg);
> +     ufshcd_release(host->hba);
> +     pm_runtime_put_sync(host->hba->dev);
> +
> +     return 0;
>  }
>
> +static void ufs_qcom_testbus_read(struct ufs_hba *hba)
> +{
> +     ufs_qcom_dump_regs(hba, UFS_TEST_BUS, 1, "UFS_TEST_BUS ");
> +}
> +
> +static void ufs_qcom_dump_dbg_regs(struct ufs_hba *hba)
> +{
> +     ufs_qcom_dump_regs(hba, REG_UFS_SYS1CLK_1US, 16,
> +                     "HCI Vendor Specific Registers ");
> +
> +     ufs_qcom_testbus_read(hba);
> +}
>  /**
>   * struct ufs_hba_qcom_vops - UFS QCOM specific variant operations
>   *
> @@ -1049,6 +1207,7 @@ static struct ufs_hba_variant_ops ufs_hba_qcom_vops
> = {
>       .pwr_change_notify      = ufs_qcom_pwr_change_notify,
>       .suspend                = ufs_qcom_suspend,
>       .resume                 = ufs_qcom_resume,
> +     .dbg_register_dump      = ufs_qcom_dump_dbg_regs,
>  };
>
>  /**
> diff --git a/drivers/scsi/ufs/ufs-qcom.h b/drivers/scsi/ufs/ufs-qcom.h
> index db2c0a0..1b71a1b 100644
> --- a/drivers/scsi/ufs/ufs-qcom.h
> +++ b/drivers/scsi/ufs/ufs-qcom.h
> @@ -58,6 +58,16 @@ enum {
>       REG_UFS_CFG2                        = 0xE0,
>       REG_UFS_HW_VERSION                  = 0xE4,
>
> +     UFS_TEST_BUS                            = 0xE8,
> +     UFS_TEST_BUS_CTRL_0                     = 0xEC,
> +     UFS_TEST_BUS_CTRL_1                     = 0xF0,
> +     UFS_TEST_BUS_CTRL_2                     = 0xF4,
> +     UFS_UNIPRO_CFG                          = 0xF8,
> +
> +};
> +
> +/* QCOM UFS host controller vendor specific debug registers */
> +enum {
>       UFS_DBG_RD_REG_UAWM                     = 0x100,
>       UFS_DBG_RD_REG_UARM                     = 0x200,
>       UFS_DBG_RD_REG_TXUC                     = 0x300,
> @@ -73,6 +83,9 @@ enum {
>       UFS_UFS_DBG_RD_EDTL_RAM                 = 0x1900,
>  };
>
> +#define TEST_BUS_EN          BIT(18)
> +#define TEST_BUS_SEL         GENMASK(22, 19)
> +
>  /* bit definitions for REG_UFS_CFG2 register */
>  #define UAWM_HW_CGC_EN               (1 << 0)
>  #define UARM_HW_CGC_EN               (1 << 1)
> @@ -83,6 +96,9 @@ enum {
>  #define TMRLUT_HW_CGC_EN     (1 << 6)
>  #define OCSC_HW_CGC_EN               (1 << 7)
>
> +/* bit definition for UFS_UFS_TEST_BUS_CTRL_n */
> +#define TEST_BUS_SUB_SEL_MASK        0x1F  /* All XXX_SEL fields are 5 bits 
> wide
> */
> +
>  #define REG_UFS_CFG2_CGC_EN_ALL (UAWM_HW_CGC_EN | UARM_HW_CGC_EN |\
>                                TXUC_HW_CGC_EN | RXUC_HW_CGC_EN |\
>                                DFC_HW_CGC_EN | TRLUT_HW_CGC_EN |\
> @@ -106,6 +122,15 @@ enum ufs_qcom_phy_init_type {
>       UFS_PHY_INIT_CFG_RESTORE,
>  };
>
> +/* QCOM UFS debug print bit mask */
> +#define UFS_QCOM_DBG_PRINT_REGS_EN   BIT(0)
> +#define UFS_QCOM_DBG_PRINT_ICE_REGS_EN       BIT(1)
> +#define UFS_QCOM_DBG_PRINT_TEST_BUS_EN       BIT(2)
> +
> +#define UFS_QCOM_DBG_PRINT_ALL       \
> +     (UFS_QCOM_DBG_PRINT_REGS_EN | UFS_QCOM_DBG_PRINT_ICE_REGS_EN | \
> +      UFS_QCOM_DBG_PRINT_TEST_BUS_EN)
> +
>  static inline void
>  ufs_qcom_get_controller_revision(struct ufs_hba *hba,
>                                u8 *major, u16 *minor, u16 *step)
> @@ -157,8 +182,13 @@ struct ufs_hw_version {
>       u16 minor;
>       u8 major;
>  };
> -struct ufs_qcom_host {
>
> +struct ufs_qcom_testbus {
> +     u8 select_major;
> +     u8 select_minor;
> +};
> +
> +struct ufs_qcom_host {
>       /*
>        * Set this capability if host controller supports the QUniPro mode
>        * and if driver wants the Host controller to operate in QUniPro mode.
> @@ -179,12 +209,17 @@ struct ufs_qcom_host {
>       bool is_lane_clks_enabled;
>
>       struct ufs_hw_version hw_ver;
> +     /* Bitmask for enabling debug prints */
> +     u32 dbg_print_en;
> +     struct ufs_qcom_testbus testbus;
>  };
>
>  #define ufs_qcom_is_link_off(hba) ufshcd_is_link_off(hba)
>  #define ufs_qcom_is_link_active(hba) ufshcd_is_link_active(hba)
>  #define ufs_qcom_is_link_hibern8(hba) ufshcd_is_link_hibern8(hba)
>
> +int ufs_qcom_testbus_config(struct ufs_qcom_host *host);
> +
>  static inline bool ufs_qcom_cap_qunipro(struct ufs_qcom_host *host)
>  {
>       if (host->caps & UFS_QCOM_CAP_QUNIPRO)
> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
> index 2ef9834..52f9dad 100644
> --- a/drivers/scsi/ufs/ufshcd.c
> +++ b/drivers/scsi/ufs/ufshcd.c
> @@ -625,6 +625,7 @@ start:
>  out:
>       return rc;
>  }
> +EXPORT_SYMBOL_GPL(ufshcd_hold);
>
>  static void ufshcd_gate_work(struct work_struct *work)
>  {
> @@ -712,6 +713,7 @@ void ufshcd_release(struct ufs_hba *hba)
>       __ufshcd_release(hba);
>       spin_unlock_irqrestore(hba->host->host_lock, flags);
>  }
> +EXPORT_SYMBOL_GPL(ufshcd_release);
>
>  static ssize_t ufshcd_clkgate_delay_show(struct device *dev,
>               struct device_attribute *attr, char *buf)
> diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
> index f2aa47e..471c667 100644
> --- a/drivers/scsi/ufs/ufshcd.h
> +++ b/drivers/scsi/ufs/ufshcd.h
> @@ -259,6 +259,7 @@ struct ufs_pwr_mode_info {
>   *                   to be set.
>   * @suspend: called during host controller PM callback
>   * @resume: called during host controller PM callback
> + * @dbg_register_dump: used to dump controller debug information
>   */
>  struct ufs_hba_variant_ops {
>       const char *name;
> @@ -275,6 +276,7 @@ struct ufs_hba_variant_ops {
>                                       struct ufs_pa_layer_attr *);
>       int     (*suspend)(struct ufs_hba *, enum ufs_pm_op);
>       int     (*resume)(struct ufs_hba *, enum ufs_pm_op);
> +     void    (*dbg_register_dump)(struct ufs_hba *hba);
>  };
>
>  /* clock gating state  */
> @@ -773,4 +775,10 @@ static inline int ufshcd_vops_resume(struct ufs_hba
> *hba, enum ufs_pm_op op)
>       return 0;
>  }
>
> +static inline void ufshcd_vops_dbg_register_dump(struct ufs_hba *hba)
> +{
> +     if (hba->vops && hba->vops->dbg_register_dump)
> +             hba->vops->dbg_register_dump(hba);
> +}
> +
>  #endif /* End of Header */
> --
> 1.8.5.2
>
> --
> QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
> of Code Aurora Forum, hosted by The Linux Foundation
> --
> To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
> the body of a message to majord...@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>


-- 
Qualcomm Israel, on behalf of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to