Merge "scsi: ufs-qcom: dump additional testbus registers"
This commit is contained in:
commit
a10e3ceacb
7 changed files with 98 additions and 11 deletions
|
@ -152,6 +152,7 @@ struct ufs_qcom_phy {
|
||||||
* and writes to QSERDES_RX_SIGDET_CNTRL attribute
|
* and writes to QSERDES_RX_SIGDET_CNTRL attribute
|
||||||
* @configure_lpm: pointer to a function that configures the phy
|
* @configure_lpm: pointer to a function that configures the phy
|
||||||
* for low power mode.
|
* for low power mode.
|
||||||
|
* @dbg_register_dump: pointer to a function that dumps phy registers for debug.
|
||||||
*/
|
*/
|
||||||
struct ufs_qcom_phy_specific_ops {
|
struct ufs_qcom_phy_specific_ops {
|
||||||
int (*calibrate_phy)(struct ufs_qcom_phy *phy, bool is_rate_B);
|
int (*calibrate_phy)(struct ufs_qcom_phy *phy, bool is_rate_B);
|
||||||
|
@ -161,6 +162,7 @@ struct ufs_qcom_phy_specific_ops {
|
||||||
void (*ctrl_rx_linecfg)(struct ufs_qcom_phy *phy, bool ctrl);
|
void (*ctrl_rx_linecfg)(struct ufs_qcom_phy *phy, bool ctrl);
|
||||||
void (*power_control)(struct ufs_qcom_phy *phy, bool val);
|
void (*power_control)(struct ufs_qcom_phy *phy, bool val);
|
||||||
int (*configure_lpm)(struct ufs_qcom_phy *phy, bool enable);
|
int (*configure_lpm)(struct ufs_qcom_phy *phy, bool enable);
|
||||||
|
void (*dbg_register_dump)(struct ufs_qcom_phy *phy);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy);
|
struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy);
|
||||||
|
@ -184,5 +186,6 @@ int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
|
||||||
void ufs_qcom_phy_write_tbl(struct ufs_qcom_phy *ufs_qcom_phy,
|
void ufs_qcom_phy_write_tbl(struct ufs_qcom_phy *ufs_qcom_phy,
|
||||||
struct ufs_qcom_phy_calibration *tbl,
|
struct ufs_qcom_phy_calibration *tbl,
|
||||||
int tbl_size);
|
int tbl_size);
|
||||||
|
void ufs_qcom_phy_dump_regs(struct ufs_qcom_phy *phy,
|
||||||
|
int offset, int len, char *prefix);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -194,6 +194,22 @@ out:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ufs_qcom_phy_qmp_v3_dbg_register_dump(struct ufs_qcom_phy *phy)
|
||||||
|
{
|
||||||
|
ufs_qcom_phy_dump_regs(phy, COM_BASE, COM_SIZE,
|
||||||
|
"PHY QSERDES COM Registers ");
|
||||||
|
ufs_qcom_phy_dump_regs(phy, PHY_BASE, PHY_SIZE,
|
||||||
|
"PHY Registers ");
|
||||||
|
ufs_qcom_phy_dump_regs(phy, RX_BASE(0), RX_SIZE,
|
||||||
|
"PHY RX0 Registers ");
|
||||||
|
ufs_qcom_phy_dump_regs(phy, TX_BASE(0), TX_SIZE,
|
||||||
|
"PHY TX0 Registers ");
|
||||||
|
ufs_qcom_phy_dump_regs(phy, RX_BASE(1), RX_SIZE,
|
||||||
|
"PHY RX1 Registers ");
|
||||||
|
ufs_qcom_phy_dump_regs(phy, TX_BASE(1), TX_SIZE,
|
||||||
|
"PHY TX1 Registers ");
|
||||||
|
}
|
||||||
|
|
||||||
struct phy_ops ufs_qcom_phy_qmp_v3_phy_ops = {
|
struct phy_ops ufs_qcom_phy_qmp_v3_phy_ops = {
|
||||||
.init = ufs_qcom_phy_qmp_v3_init,
|
.init = ufs_qcom_phy_qmp_v3_init,
|
||||||
.exit = ufs_qcom_phy_exit,
|
.exit = ufs_qcom_phy_exit,
|
||||||
|
@ -210,6 +226,7 @@ struct ufs_qcom_phy_specific_ops phy_v3_ops = {
|
||||||
.ctrl_rx_linecfg = ufs_qcom_phy_qmp_v3_ctrl_rx_linecfg,
|
.ctrl_rx_linecfg = ufs_qcom_phy_qmp_v3_ctrl_rx_linecfg,
|
||||||
.power_control = ufs_qcom_phy_qmp_v3_power_control,
|
.power_control = ufs_qcom_phy_qmp_v3_power_control,
|
||||||
.configure_lpm = ufs_qcom_phy_qmp_v3_configure_lpm,
|
.configure_lpm = ufs_qcom_phy_qmp_v3_configure_lpm,
|
||||||
|
.dbg_register_dump = ufs_qcom_phy_qmp_v3_dbg_register_dump,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int ufs_qcom_phy_qmp_v3_probe(struct platform_device *pdev)
|
static int ufs_qcom_phy_qmp_v3_probe(struct platform_device *pdev)
|
||||||
|
|
|
@ -18,10 +18,18 @@
|
||||||
#include "phy-qcom-ufs-i.h"
|
#include "phy-qcom-ufs-i.h"
|
||||||
|
|
||||||
/* QCOM UFS PHY control registers */
|
/* QCOM UFS PHY control registers */
|
||||||
#define COM_OFF(x) (0x000 + x)
|
#define COM_BASE 0x000
|
||||||
#define PHY_OFF(x) (0xC00 + x)
|
#define COM_SIZE 0x18C
|
||||||
#define TX_OFF(n, x) (0x400 + (0x400 * n) + x)
|
#define PHY_BASE 0xC00
|
||||||
#define RX_OFF(n, x) (0x600 + (0x400 * n) + x)
|
#define PHY_SIZE 0x1DC
|
||||||
|
#define TX_BASE(n) (0x400 + (0x400 * n))
|
||||||
|
#define TX_SIZE 0x128
|
||||||
|
#define RX_BASE(n) (0x600 + (0x400 * n))
|
||||||
|
#define RX_SIZE 0x1FC
|
||||||
|
#define COM_OFF(x) (COM_BASE + x)
|
||||||
|
#define PHY_OFF(x) (PHY_BASE + x)
|
||||||
|
#define TX_OFF(n, x) (TX_BASE(n) + x)
|
||||||
|
#define RX_OFF(n, x) (RX_BASE(n) + x)
|
||||||
|
|
||||||
/* UFS PHY QSERDES COM registers */
|
/* UFS PHY QSERDES COM registers */
|
||||||
#define QSERDES_COM_ATB_SEL1 COM_OFF(0x00)
|
#define QSERDES_COM_ATB_SEL1 COM_OFF(0x00)
|
||||||
|
|
|
@ -788,3 +788,21 @@ int ufs_qcom_phy_configure_lpm(struct phy *generic_phy, bool enable)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(ufs_qcom_phy_configure_lpm);
|
EXPORT_SYMBOL(ufs_qcom_phy_configure_lpm);
|
||||||
|
|
||||||
|
void ufs_qcom_phy_dump_regs(struct ufs_qcom_phy *phy, int offset,
|
||||||
|
int len, char *prefix)
|
||||||
|
{
|
||||||
|
print_hex_dump(KERN_ERR, prefix,
|
||||||
|
len > 4 ? DUMP_PREFIX_OFFSET : DUMP_PREFIX_NONE,
|
||||||
|
16, 4, phy->mmio + offset, len, false);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(ufs_qcom_phy_dump_regs);
|
||||||
|
|
||||||
|
void ufs_qcom_phy_dbg_register_dump(struct phy *generic_phy)
|
||||||
|
{
|
||||||
|
struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
|
||||||
|
|
||||||
|
if (ufs_qcom_phy->phy_spec_ops->dbg_register_dump)
|
||||||
|
ufs_qcom_phy->phy_spec_ops->dbg_register_dump(ufs_qcom_phy);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(ufs_qcom_phy_dbg_register_dump);
|
||||||
|
|
|
@ -2382,17 +2382,21 @@ void ufs_qcom_print_hw_debug_reg_all(struct ufs_hba *hba, void *priv,
|
||||||
|
|
||||||
static void ufs_qcom_enable_test_bus(struct ufs_qcom_host *host)
|
static void ufs_qcom_enable_test_bus(struct ufs_qcom_host *host)
|
||||||
{
|
{
|
||||||
if (host->dbg_print_en & UFS_QCOM_DBG_PRINT_TEST_BUS_EN)
|
if (host->dbg_print_en & UFS_QCOM_DBG_PRINT_TEST_BUS_EN) {
|
||||||
|
ufshcd_rmwl(host->hba, UFS_REG_TEST_BUS_EN,
|
||||||
|
UFS_REG_TEST_BUS_EN, REG_UFS_CFG1);
|
||||||
ufshcd_rmwl(host->hba, TEST_BUS_EN, TEST_BUS_EN, REG_UFS_CFG1);
|
ufshcd_rmwl(host->hba, TEST_BUS_EN, TEST_BUS_EN, REG_UFS_CFG1);
|
||||||
else
|
} else {
|
||||||
|
ufshcd_rmwl(host->hba, UFS_REG_TEST_BUS_EN, 0, REG_UFS_CFG1);
|
||||||
ufshcd_rmwl(host->hba, TEST_BUS_EN, 0, REG_UFS_CFG1);
|
ufshcd_rmwl(host->hba, TEST_BUS_EN, 0, REG_UFS_CFG1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host)
|
static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host)
|
||||||
{
|
{
|
||||||
/* provide a legal default configuration */
|
/* provide a legal default configuration */
|
||||||
host->testbus.select_major = TSTBUS_UAWM;
|
host->testbus.select_major = TSTBUS_UNIPRO;
|
||||||
host->testbus.select_minor = 1;
|
host->testbus.select_minor = 37;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host)
|
static bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host)
|
||||||
|
@ -2409,7 +2413,7 @@ static bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host)
|
||||||
* mappings of select_minor, since there is no harm in
|
* mappings of select_minor, since there is no harm in
|
||||||
* configuring a non-existent select_minor
|
* configuring a non-existent select_minor
|
||||||
*/
|
*/
|
||||||
if (host->testbus.select_minor > 0x1F) {
|
if (host->testbus.select_minor > 0xFF) {
|
||||||
dev_err(host->hba->dev,
|
dev_err(host->hba->dev,
|
||||||
"%s: 0x%05X is not a legal testbus option\n",
|
"%s: 0x%05X is not a legal testbus option\n",
|
||||||
__func__, host->testbus.select_minor);
|
__func__, host->testbus.select_minor);
|
||||||
|
@ -2478,7 +2482,8 @@ int ufs_qcom_testbus_config(struct ufs_qcom_host *host)
|
||||||
break;
|
break;
|
||||||
case TSTBUS_UNIPRO:
|
case TSTBUS_UNIPRO:
|
||||||
reg = UFS_UNIPRO_CFG;
|
reg = UFS_UNIPRO_CFG;
|
||||||
offset = 1;
|
offset = 20;
|
||||||
|
mask = 0xFFF;
|
||||||
break;
|
break;
|
||||||
/*
|
/*
|
||||||
* No need for a default case, since
|
* No need for a default case, since
|
||||||
|
@ -2497,6 +2502,11 @@ int ufs_qcom_testbus_config(struct ufs_qcom_host *host)
|
||||||
(u32)host->testbus.select_minor << offset,
|
(u32)host->testbus.select_minor << offset,
|
||||||
reg);
|
reg);
|
||||||
ufs_qcom_enable_test_bus(host);
|
ufs_qcom_enable_test_bus(host);
|
||||||
|
/*
|
||||||
|
* Make sure the test bus configuration is
|
||||||
|
* committed before returning.
|
||||||
|
*/
|
||||||
|
mb();
|
||||||
ufshcd_release(host->hba, false);
|
ufshcd_release(host->hba, false);
|
||||||
pm_runtime_put_sync(host->hba->dev);
|
pm_runtime_put_sync(host->hba->dev);
|
||||||
|
|
||||||
|
@ -2508,15 +2518,44 @@ static void ufs_qcom_testbus_read(struct ufs_hba *hba)
|
||||||
ufs_qcom_dump_regs(hba, UFS_TEST_BUS, 1, "UFS_TEST_BUS ");
|
ufs_qcom_dump_regs(hba, UFS_TEST_BUS, 1, "UFS_TEST_BUS ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ufs_qcom_print_unipro_testbus(struct ufs_hba *hba)
|
||||||
|
{
|
||||||
|
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
|
||||||
|
u32 *testbus = NULL;
|
||||||
|
int i, nminor = 256, testbus_len = nminor * sizeof(u32);
|
||||||
|
|
||||||
|
testbus = kmalloc(testbus_len, GFP_KERNEL);
|
||||||
|
if (!testbus)
|
||||||
|
return;
|
||||||
|
|
||||||
|
host->testbus.select_major = TSTBUS_UNIPRO;
|
||||||
|
for (i = 0; i < nminor; i++) {
|
||||||
|
host->testbus.select_minor = i;
|
||||||
|
ufs_qcom_testbus_config(host);
|
||||||
|
testbus[i] = ufshcd_readl(hba, UFS_TEST_BUS);
|
||||||
|
}
|
||||||
|
print_hex_dump(KERN_ERR, "UNIPRO_TEST_BUS ", DUMP_PREFIX_OFFSET,
|
||||||
|
16, 4, testbus, testbus_len, false);
|
||||||
|
kfree(testbus);
|
||||||
|
}
|
||||||
|
|
||||||
static void ufs_qcom_dump_dbg_regs(struct ufs_hba *hba)
|
static void ufs_qcom_dump_dbg_regs(struct ufs_hba *hba)
|
||||||
{
|
{
|
||||||
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
|
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
|
||||||
|
struct phy *phy = host->generic_phy;
|
||||||
|
|
||||||
ufs_qcom_dump_regs(hba, REG_UFS_SYS1CLK_1US, 16,
|
ufs_qcom_dump_regs(hba, REG_UFS_SYS1CLK_1US, 16,
|
||||||
"HCI Vendor Specific Registers ");
|
"HCI Vendor Specific Registers ");
|
||||||
|
|
||||||
|
/* sleep a bit intermittently as we are dumping too much data */
|
||||||
ufs_qcom_print_hw_debug_reg_all(hba, NULL, ufs_qcom_dump_regs_wrapper);
|
ufs_qcom_print_hw_debug_reg_all(hba, NULL, ufs_qcom_dump_regs_wrapper);
|
||||||
|
usleep_range(1000, 1100);
|
||||||
ufs_qcom_testbus_read(hba);
|
ufs_qcom_testbus_read(hba);
|
||||||
|
usleep_range(1000, 1100);
|
||||||
|
ufs_qcom_print_unipro_testbus(hba);
|
||||||
|
usleep_range(1000, 1100);
|
||||||
|
ufs_qcom_phy_dbg_register_dump(phy);
|
||||||
|
usleep_range(1000, 1100);
|
||||||
ufs_qcom_ice_print_regs(host);
|
ufs_qcom_ice_print_regs(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -100,6 +100,7 @@ enum {
|
||||||
#define QUNIPRO_SEL UFS_BIT(0)
|
#define QUNIPRO_SEL UFS_BIT(0)
|
||||||
#define TEST_BUS_EN BIT(18)
|
#define TEST_BUS_EN BIT(18)
|
||||||
#define TEST_BUS_SEL GENMASK(22, 19)
|
#define TEST_BUS_SEL GENMASK(22, 19)
|
||||||
|
#define UFS_REG_TEST_BUS_EN BIT(30)
|
||||||
|
|
||||||
/* bit definitions for REG_UFS_CFG2 register */
|
/* bit definitions for REG_UFS_CFG2 register */
|
||||||
#define UAWM_HW_CGC_EN (1 << 0)
|
#define UAWM_HW_CGC_EN (1 << 0)
|
||||||
|
|
|
@ -58,5 +58,6 @@ void ufs_qcom_phy_save_controller_version(struct phy *phy,
|
||||||
u8 major, u16 minor, u16 step);
|
u8 major, u16 minor, u16 step);
|
||||||
const char *ufs_qcom_phy_name(struct phy *phy);
|
const char *ufs_qcom_phy_name(struct phy *phy);
|
||||||
int ufs_qcom_phy_configure_lpm(struct phy *generic_phy, bool enable);
|
int ufs_qcom_phy_configure_lpm(struct phy *generic_phy, bool enable);
|
||||||
|
void ufs_qcom_phy_dbg_register_dump(struct phy *generic_phy);
|
||||||
|
|
||||||
#endif /* PHY_QCOM_UFS_H_ */
|
#endif /* PHY_QCOM_UFS_H_ */
|
||||||
|
|
Loading…
Add table
Reference in a new issue