diff options
author | Yaniv Gardi <ygardi@codeaurora.org> | 2015-10-28 13:15:50 +0200 |
---|---|---|
committer | Martin K. Petersen <martin.petersen@oracle.com> | 2015-11-09 18:01:58 -0500 |
commit | 6e3fd44d7b7638e0f7e3331eaf7f90f3a629f3e7 (patch) | |
tree | ed87dbce836a6775e30974c591804d82aa0dd4e7 /drivers/scsi/ufs/ufs-qcom.c | |
parent | 47555a5c8a11a423e6767f942941c745766c99a2 (diff) |
scsi: ufs-qcom: add debug prints for test bus
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.
Reviewed-by: Subhash Jadavani <subhashj@codeaurora.org>
Reviewed-by: Gilad Broner <gbroner@codeaurora.org>
Signed-off-by: Yaniv Gardi <ygardi@codeaurora.org>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Diffstat (limited to 'drivers/scsi/ufs/ufs-qcom.c')
-rw-r--r-- | drivers/scsi/ufs/ufs-qcom.c | 165 |
1 files changed, 162 insertions, 3 deletions
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index b275a9a3b4d5..16338083b0d3 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, }; /** |