Loading drivers/scsi/ufs/debugfs.c +128 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ #include <linux/random.h> #include "debugfs.h" #include "unipro.h" enum field_width { BYTE = 1, Loading Loading @@ -599,6 +600,123 @@ static const struct file_operations ufsdbg_dump_device_desc = { .read = seq_read, }; static ssize_t ufsdbg_power_mode_show(struct seq_file *file, void *data) { struct ufs_hba *hba = (struct ufs_hba *)file->private; char *names[] = { "INVALID MODE", "FAST MODE", "SLOW MODE", "INVALID MODE", "FASTAUTO MODE", "SLOWAUTO MODE", "INVALID MODE", }; /* Print current status */ seq_puts(file, "UFS current power mode [RX, TX]:"); seq_printf(file, "gear=[%d,%d], lane=[%d,%d], pwr=[%s,%s], rate = %c", hba->pwr_info.gear_rx, hba->pwr_info.gear_tx, hba->pwr_info.lane_rx, hba->pwr_info.lane_tx, names[hba->pwr_info.pwr_rx], names[hba->pwr_info.pwr_tx], hba->pwr_info.hs_rate == PA_HS_MODE_B ? 'B' : 'A'); seq_puts(file, "\n\n"); /* Print usage */ seq_puts(file, "To change power mode write 'GGLLMM' where:\n" "G - selected gear\n" "L - number of lanes\n" "M - power mode:\n" "\t1 = fast mode\n" "\t2 = slow mode\n" "\t4 = fast-auto mode\n" "\t5 = slow-auto mode\n" "first letter is for RX, second letter is for TX.\n\n"); return 0; } static bool ufsdbg_power_mode_validate(struct ufs_pa_layer_attr *pwr_mode) { if (pwr_mode->gear_rx < UFS_PWM_G1 || pwr_mode->gear_rx > UFS_PWM_G7 || pwr_mode->gear_tx < UFS_PWM_G1 || pwr_mode->gear_tx > UFS_PWM_G7 || pwr_mode->lane_rx < 1 || pwr_mode->lane_rx > 2 || pwr_mode->lane_tx < 1 || pwr_mode->lane_tx > 2 || (pwr_mode->pwr_rx != FAST_MODE && pwr_mode->pwr_rx != SLOW_MODE && pwr_mode->pwr_rx != FASTAUTO_MODE && pwr_mode->pwr_rx != SLOWAUTO_MODE) || (pwr_mode->pwr_tx != FAST_MODE && pwr_mode->pwr_tx != SLOW_MODE && pwr_mode->pwr_tx != FASTAUTO_MODE && pwr_mode->pwr_tx != SLOWAUTO_MODE)) return false; return true; } static ssize_t ufsdbg_power_mode_write(struct file *file, const char __user *ubuf, size_t cnt, loff_t *ppos) { struct ufs_hba *hba = file->f_mapping->host->i_private; struct ufs_pa_layer_attr pwr_mode; char pwr_mode_str[BUFF_LINE_CAPACITY] = {0}; loff_t buff_pos = 0; int ret; int idx = 0; ret = simple_write_to_buffer(pwr_mode_str, BUFF_LINE_CAPACITY, &buff_pos, ubuf, cnt); pwr_mode.gear_rx = pwr_mode_str[idx++] - '0'; pwr_mode.gear_tx = pwr_mode_str[idx++] - '0'; pwr_mode.lane_rx = pwr_mode_str[idx++] - '0'; pwr_mode.lane_tx = pwr_mode_str[idx++] - '0'; pwr_mode.pwr_rx = pwr_mode_str[idx++] - '0'; pwr_mode.pwr_tx = pwr_mode_str[idx++] - '0'; /* * Switching between rates is not currently supported so use the * current rate. * TODO: add rate switching if and when it is supported in the future */ pwr_mode.hs_rate = hba->pwr_info.hs_rate; /* Validate user input */ if (!ufsdbg_power_mode_validate(&pwr_mode)) return -EINVAL; pr_debug( "%s: new power mode requested [RX,TX]: Gear=[%d,%d] Lanes=[%d,%d], Mode=[%d,%d]\n", __func__, pwr_mode.gear_rx, pwr_mode.gear_tx, pwr_mode.lane_rx, pwr_mode.lane_tx, pwr_mode.pwr_rx, pwr_mode.pwr_tx); ret = ufshcd_config_pwr_mode(hba, &pwr_mode); if (ret == -EBUSY) dev_err(hba->dev, "%s: ufshcd_config_pwr_mode failed: system is busy, try again\n", __func__); else if (ret) dev_err(hba->dev, "%s: ufshcd_config_pwr_mode failed, ret=%d\n", __func__, ret); return cnt; } static int ufsdbg_power_mode_open(struct inode *inode, struct file *file) { return single_open(file, ufsdbg_power_mode_show, inode->i_private); } static const struct file_operations ufsdbg_power_mode_desc = { .open = ufsdbg_power_mode_open, .read = seq_read, .write = ufsdbg_power_mode_write, }; void ufsdbg_add_debugfs(struct ufs_hba *hba) { if (!hba) { Loading Loading @@ -672,6 +790,16 @@ void ufsdbg_add_debugfs(struct ufs_hba *hba) goto err; } hba->debugfs_files.power_mode = debugfs_create_file("power_mode", S_IRUSR | S_IWUSR, hba->debugfs_files.debugfs_root, hba, &ufsdbg_power_mode_desc); if (!hba->debugfs_files.power_mode) { dev_err(hba->dev, "%s: NULL power_mode_desc file, exiting", __func__); goto err; } ufsdbg_setup_fault_injection(hba); return; Loading drivers/scsi/ufs/ufshcd.c +54 −16 Original line number Diff line number Diff line Loading @@ -110,6 +110,9 @@ /* UIC command timeout, unit: ms */ #define UIC_CMD_TIMEOUT 500 /* Retries waiting for doorbells to clear */ #define POWER_MODE_RETRIES 10 /* NOP OUT retries waiting for NOP IN response */ #define NOP_OUT_RETRIES 10 /* Timeout after 30 msecs if NOP OUT hangs without response */ Loading Loading @@ -278,8 +281,6 @@ static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on); static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba); static int ufshcd_uic_hibern8_enter(struct ufs_hba *hba); static int ufshcd_host_reset_and_restore(struct ufs_hba *hba); static int ufshcd_config_pwr_mode(struct ufs_hba *hba, struct ufs_pa_layer_attr *desired_pwr_mode); static inline void ufshcd_enable_irq(struct ufs_hba *hba) { Loading Loading @@ -1215,15 +1216,12 @@ ufshcd_wait_for_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) * @uic_cmd: UIC command * * Identical to ufshcd_send_uic_cmd() expect mutex. Must be called * with mutex held. * with mutex held and host_lock locked. * Returns 0 only if success. */ static int __ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) { int ret; unsigned long flags; if (!ufshcd_ready_for_uic_cmd(hba)) { dev_err(hba->dev, "Controller not ready to accept UIC commands\n"); Loading @@ -1232,13 +1230,9 @@ __ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) init_completion(&uic_cmd->done); spin_lock_irqsave(hba->host->host_lock, flags); ufshcd_dispatch_uic_cmd(hba, uic_cmd); spin_unlock_irqrestore(hba->host->host_lock, flags); ret = ufshcd_wait_for_uic_cmd(hba, uic_cmd); return ret; return 0; } /** Loading @@ -1252,12 +1246,19 @@ static int ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) { int ret; unsigned long flags; ufshcd_hold(hba, false); pm_runtime_get_sync(hba->dev); mutex_lock(&hba->uic_cmd_mutex); spin_lock_irqsave(hba->host->host_lock, flags); ret = __ufshcd_send_uic_cmd(hba, uic_cmd); mutex_unlock(&hba->uic_cmd_mutex); spin_unlock_irqrestore(hba->host->host_lock, flags); if (!ret) ret = ufshcd_wait_for_uic_cmd(hba, uic_cmd); mutex_unlock(&hba->uic_cmd_mutex); pm_runtime_put_sync(hba->dev); ufshcd_release(hba); return ret; } Loading Loading @@ -2592,16 +2593,53 @@ int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd) unsigned long flags; u8 status; int ret; u32 tm_doorbell; u32 tr_doorbell; bool uic_ready; int retries = POWER_MODE_RETRIES; ufshcd_hold(hba, false); pm_runtime_get_sync(hba->dev); mutex_lock(&hba->uic_cmd_mutex); init_completion(&uic_async_done); /* * Before changing the power mode there should be no outstanding * tasks/transfer requests. Verify by checking the doorbell registers * are clear. */ do { spin_lock_irqsave(hba->host->host_lock, flags); hba->uic_async_done = &uic_async_done; uic_ready = ufshcd_ready_for_uic_cmd(hba); tm_doorbell = ufshcd_readl(hba, REG_UTP_TASK_REQ_DOOR_BELL); tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); if (!tm_doorbell && !tr_doorbell && uic_ready) break; spin_unlock_irqrestore(hba->host->host_lock, flags); schedule(); retries--; } while (retries && (tm_doorbell || tr_doorbell || !uic_ready)); if (!retries) { dev_err(hba->dev, "%s: too many retries waiting for doorbell to clear (tm=0x%x, tr=0x%x, uicrdy=%d)\n", __func__, tm_doorbell, tr_doorbell, uic_ready); ret = -EBUSY; goto out; } hba->uic_async_done = &uic_async_done; ret = __ufshcd_send_uic_cmd(hba, cmd); spin_unlock_irqrestore(hba->host->host_lock, flags); if (ret) { dev_err(hba->dev, "pwr ctrl cmd 0x%x with mode 0x%x uic error %d\n", cmd->command, cmd->argument3, ret); goto out; } ret = ufshcd_wait_for_uic_cmd(hba, cmd); if (ret) { dev_err(hba->dev, "pwr ctrl cmd 0x%x with mode 0x%x uic error %d\n", Loading Loading @@ -2630,7 +2668,7 @@ out: hba->uic_async_done = NULL; spin_unlock_irqrestore(hba->host->host_lock, flags); mutex_unlock(&hba->uic_cmd_mutex); pm_runtime_put_sync(hba->dev); ufshcd_release(hba); return ret; } Loading Loading @@ -2812,7 +2850,7 @@ static int ufshcd_get_max_pwr_mode(struct ufs_hba *hba) * @hba: per-adapter instance * @desired_pwr_mode: desired power configuration */ static int ufshcd_config_pwr_mode(struct ufs_hba *hba, int ufshcd_config_pwr_mode(struct ufs_hba *hba, struct ufs_pa_layer_attr *desired_pwr_mode) { struct ufs_pa_layer_attr final_params = { 0 }; Loading drivers/scsi/ufs/ufshcd.h +3 −0 Original line number Diff line number Diff line Loading @@ -241,6 +241,7 @@ struct debugfs_files { struct dentry *show_hba; struct dentry *host_regs; struct dentry *dump_dev_desc; struct dentry *power_mode; #ifdef CONFIG_UFS_FAULT_INJECTION struct fault_attr fail_attr; #endif Loading Loading @@ -652,6 +653,8 @@ extern int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel, u8 attr_set, u32 mib_val, u8 peer); extern int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel, u32 *mib_val, u8 peer); extern int ufshcd_config_pwr_mode(struct ufs_hba *hba, struct ufs_pa_layer_attr *desired_pwr_mode); /* UIC command interfaces for DME primitives */ #define DME_LOCAL 0 Loading Loading
drivers/scsi/ufs/debugfs.c +128 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ #include <linux/random.h> #include "debugfs.h" #include "unipro.h" enum field_width { BYTE = 1, Loading Loading @@ -599,6 +600,123 @@ static const struct file_operations ufsdbg_dump_device_desc = { .read = seq_read, }; static ssize_t ufsdbg_power_mode_show(struct seq_file *file, void *data) { struct ufs_hba *hba = (struct ufs_hba *)file->private; char *names[] = { "INVALID MODE", "FAST MODE", "SLOW MODE", "INVALID MODE", "FASTAUTO MODE", "SLOWAUTO MODE", "INVALID MODE", }; /* Print current status */ seq_puts(file, "UFS current power mode [RX, TX]:"); seq_printf(file, "gear=[%d,%d], lane=[%d,%d], pwr=[%s,%s], rate = %c", hba->pwr_info.gear_rx, hba->pwr_info.gear_tx, hba->pwr_info.lane_rx, hba->pwr_info.lane_tx, names[hba->pwr_info.pwr_rx], names[hba->pwr_info.pwr_tx], hba->pwr_info.hs_rate == PA_HS_MODE_B ? 'B' : 'A'); seq_puts(file, "\n\n"); /* Print usage */ seq_puts(file, "To change power mode write 'GGLLMM' where:\n" "G - selected gear\n" "L - number of lanes\n" "M - power mode:\n" "\t1 = fast mode\n" "\t2 = slow mode\n" "\t4 = fast-auto mode\n" "\t5 = slow-auto mode\n" "first letter is for RX, second letter is for TX.\n\n"); return 0; } static bool ufsdbg_power_mode_validate(struct ufs_pa_layer_attr *pwr_mode) { if (pwr_mode->gear_rx < UFS_PWM_G1 || pwr_mode->gear_rx > UFS_PWM_G7 || pwr_mode->gear_tx < UFS_PWM_G1 || pwr_mode->gear_tx > UFS_PWM_G7 || pwr_mode->lane_rx < 1 || pwr_mode->lane_rx > 2 || pwr_mode->lane_tx < 1 || pwr_mode->lane_tx > 2 || (pwr_mode->pwr_rx != FAST_MODE && pwr_mode->pwr_rx != SLOW_MODE && pwr_mode->pwr_rx != FASTAUTO_MODE && pwr_mode->pwr_rx != SLOWAUTO_MODE) || (pwr_mode->pwr_tx != FAST_MODE && pwr_mode->pwr_tx != SLOW_MODE && pwr_mode->pwr_tx != FASTAUTO_MODE && pwr_mode->pwr_tx != SLOWAUTO_MODE)) return false; return true; } static ssize_t ufsdbg_power_mode_write(struct file *file, const char __user *ubuf, size_t cnt, loff_t *ppos) { struct ufs_hba *hba = file->f_mapping->host->i_private; struct ufs_pa_layer_attr pwr_mode; char pwr_mode_str[BUFF_LINE_CAPACITY] = {0}; loff_t buff_pos = 0; int ret; int idx = 0; ret = simple_write_to_buffer(pwr_mode_str, BUFF_LINE_CAPACITY, &buff_pos, ubuf, cnt); pwr_mode.gear_rx = pwr_mode_str[idx++] - '0'; pwr_mode.gear_tx = pwr_mode_str[idx++] - '0'; pwr_mode.lane_rx = pwr_mode_str[idx++] - '0'; pwr_mode.lane_tx = pwr_mode_str[idx++] - '0'; pwr_mode.pwr_rx = pwr_mode_str[idx++] - '0'; pwr_mode.pwr_tx = pwr_mode_str[idx++] - '0'; /* * Switching between rates is not currently supported so use the * current rate. * TODO: add rate switching if and when it is supported in the future */ pwr_mode.hs_rate = hba->pwr_info.hs_rate; /* Validate user input */ if (!ufsdbg_power_mode_validate(&pwr_mode)) return -EINVAL; pr_debug( "%s: new power mode requested [RX,TX]: Gear=[%d,%d] Lanes=[%d,%d], Mode=[%d,%d]\n", __func__, pwr_mode.gear_rx, pwr_mode.gear_tx, pwr_mode.lane_rx, pwr_mode.lane_tx, pwr_mode.pwr_rx, pwr_mode.pwr_tx); ret = ufshcd_config_pwr_mode(hba, &pwr_mode); if (ret == -EBUSY) dev_err(hba->dev, "%s: ufshcd_config_pwr_mode failed: system is busy, try again\n", __func__); else if (ret) dev_err(hba->dev, "%s: ufshcd_config_pwr_mode failed, ret=%d\n", __func__, ret); return cnt; } static int ufsdbg_power_mode_open(struct inode *inode, struct file *file) { return single_open(file, ufsdbg_power_mode_show, inode->i_private); } static const struct file_operations ufsdbg_power_mode_desc = { .open = ufsdbg_power_mode_open, .read = seq_read, .write = ufsdbg_power_mode_write, }; void ufsdbg_add_debugfs(struct ufs_hba *hba) { if (!hba) { Loading Loading @@ -672,6 +790,16 @@ void ufsdbg_add_debugfs(struct ufs_hba *hba) goto err; } hba->debugfs_files.power_mode = debugfs_create_file("power_mode", S_IRUSR | S_IWUSR, hba->debugfs_files.debugfs_root, hba, &ufsdbg_power_mode_desc); if (!hba->debugfs_files.power_mode) { dev_err(hba->dev, "%s: NULL power_mode_desc file, exiting", __func__); goto err; } ufsdbg_setup_fault_injection(hba); return; Loading
drivers/scsi/ufs/ufshcd.c +54 −16 Original line number Diff line number Diff line Loading @@ -110,6 +110,9 @@ /* UIC command timeout, unit: ms */ #define UIC_CMD_TIMEOUT 500 /* Retries waiting for doorbells to clear */ #define POWER_MODE_RETRIES 10 /* NOP OUT retries waiting for NOP IN response */ #define NOP_OUT_RETRIES 10 /* Timeout after 30 msecs if NOP OUT hangs without response */ Loading Loading @@ -278,8 +281,6 @@ static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on); static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba); static int ufshcd_uic_hibern8_enter(struct ufs_hba *hba); static int ufshcd_host_reset_and_restore(struct ufs_hba *hba); static int ufshcd_config_pwr_mode(struct ufs_hba *hba, struct ufs_pa_layer_attr *desired_pwr_mode); static inline void ufshcd_enable_irq(struct ufs_hba *hba) { Loading Loading @@ -1215,15 +1216,12 @@ ufshcd_wait_for_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) * @uic_cmd: UIC command * * Identical to ufshcd_send_uic_cmd() expect mutex. Must be called * with mutex held. * with mutex held and host_lock locked. * Returns 0 only if success. */ static int __ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) { int ret; unsigned long flags; if (!ufshcd_ready_for_uic_cmd(hba)) { dev_err(hba->dev, "Controller not ready to accept UIC commands\n"); Loading @@ -1232,13 +1230,9 @@ __ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) init_completion(&uic_cmd->done); spin_lock_irqsave(hba->host->host_lock, flags); ufshcd_dispatch_uic_cmd(hba, uic_cmd); spin_unlock_irqrestore(hba->host->host_lock, flags); ret = ufshcd_wait_for_uic_cmd(hba, uic_cmd); return ret; return 0; } /** Loading @@ -1252,12 +1246,19 @@ static int ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) { int ret; unsigned long flags; ufshcd_hold(hba, false); pm_runtime_get_sync(hba->dev); mutex_lock(&hba->uic_cmd_mutex); spin_lock_irqsave(hba->host->host_lock, flags); ret = __ufshcd_send_uic_cmd(hba, uic_cmd); mutex_unlock(&hba->uic_cmd_mutex); spin_unlock_irqrestore(hba->host->host_lock, flags); if (!ret) ret = ufshcd_wait_for_uic_cmd(hba, uic_cmd); mutex_unlock(&hba->uic_cmd_mutex); pm_runtime_put_sync(hba->dev); ufshcd_release(hba); return ret; } Loading Loading @@ -2592,16 +2593,53 @@ int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd) unsigned long flags; u8 status; int ret; u32 tm_doorbell; u32 tr_doorbell; bool uic_ready; int retries = POWER_MODE_RETRIES; ufshcd_hold(hba, false); pm_runtime_get_sync(hba->dev); mutex_lock(&hba->uic_cmd_mutex); init_completion(&uic_async_done); /* * Before changing the power mode there should be no outstanding * tasks/transfer requests. Verify by checking the doorbell registers * are clear. */ do { spin_lock_irqsave(hba->host->host_lock, flags); hba->uic_async_done = &uic_async_done; uic_ready = ufshcd_ready_for_uic_cmd(hba); tm_doorbell = ufshcd_readl(hba, REG_UTP_TASK_REQ_DOOR_BELL); tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); if (!tm_doorbell && !tr_doorbell && uic_ready) break; spin_unlock_irqrestore(hba->host->host_lock, flags); schedule(); retries--; } while (retries && (tm_doorbell || tr_doorbell || !uic_ready)); if (!retries) { dev_err(hba->dev, "%s: too many retries waiting for doorbell to clear (tm=0x%x, tr=0x%x, uicrdy=%d)\n", __func__, tm_doorbell, tr_doorbell, uic_ready); ret = -EBUSY; goto out; } hba->uic_async_done = &uic_async_done; ret = __ufshcd_send_uic_cmd(hba, cmd); spin_unlock_irqrestore(hba->host->host_lock, flags); if (ret) { dev_err(hba->dev, "pwr ctrl cmd 0x%x with mode 0x%x uic error %d\n", cmd->command, cmd->argument3, ret); goto out; } ret = ufshcd_wait_for_uic_cmd(hba, cmd); if (ret) { dev_err(hba->dev, "pwr ctrl cmd 0x%x with mode 0x%x uic error %d\n", Loading Loading @@ -2630,7 +2668,7 @@ out: hba->uic_async_done = NULL; spin_unlock_irqrestore(hba->host->host_lock, flags); mutex_unlock(&hba->uic_cmd_mutex); pm_runtime_put_sync(hba->dev); ufshcd_release(hba); return ret; } Loading Loading @@ -2812,7 +2850,7 @@ static int ufshcd_get_max_pwr_mode(struct ufs_hba *hba) * @hba: per-adapter instance * @desired_pwr_mode: desired power configuration */ static int ufshcd_config_pwr_mode(struct ufs_hba *hba, int ufshcd_config_pwr_mode(struct ufs_hba *hba, struct ufs_pa_layer_attr *desired_pwr_mode) { struct ufs_pa_layer_attr final_params = { 0 }; Loading
drivers/scsi/ufs/ufshcd.h +3 −0 Original line number Diff line number Diff line Loading @@ -241,6 +241,7 @@ struct debugfs_files { struct dentry *show_hba; struct dentry *host_regs; struct dentry *dump_dev_desc; struct dentry *power_mode; #ifdef CONFIG_UFS_FAULT_INJECTION struct fault_attr fail_attr; #endif Loading Loading @@ -652,6 +653,8 @@ extern int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel, u8 attr_set, u32 mib_val, u8 peer); extern int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel, u32 *mib_val, u8 peer); extern int ufshcd_config_pwr_mode(struct ufs_hba *hba, struct ufs_pa_layer_attr *desired_pwr_mode); /* UIC command interfaces for DME primitives */ #define DME_LOCAL 0 Loading