diff --git a/sys/dev/ufshci/ufshci_ctrlr.c b/sys/dev/ufshci/ufshci_ctrlr.c --- a/sys/dev/ufshci/ufshci_ctrlr.c +++ b/sys/dev/ufshci/ufshci_ctrlr.c @@ -79,12 +79,6 @@ return; } - /* Initialize UFS Power Mode */ - if (ufshci_dev_init_ufs_power_mode(ctrlr) != 0) { - ufshci_ctrlr_fail(ctrlr); - return; - } - /* Read Controller Descriptor (Device, Geometry) */ if (ufshci_dev_get_descriptor(ctrlr) != 0) { ufshci_ctrlr_fail(ctrlr); @@ -109,6 +103,12 @@ return; } + /* Initialize UFS Power Mode */ + if (ufshci_dev_init_ufs_power_mode(ctrlr) != 0) { + ufshci_ctrlr_fail(ctrlr); + return; + } + TSEXIT(); } diff --git a/sys/dev/ufshci/ufshci_dev.c b/sys/dev/ufshci/ufshci_dev.c --- a/sys/dev/ufshci/ufshci_dev.c +++ b/sys/dev/ufshci/ufshci_dev.c @@ -455,8 +455,20 @@ int ufshci_dev_init_ufs_power_mode(struct ufshci_controller *ctrlr) { - /* TODO: Need to implement */ + ctrlr->ufs_dev.power_mode_supported = false; + if (ctrlr->quirks & UFSHCI_QUIRK_SKIP_WELL_KNOWN_LUNS) + return (0); + + ctrlr->ufs_device_wlun_periph = ufshci_sim_find_periph(ctrlr, + UFSHCI_WLUN_UFS_DEVICE); + if (ctrlr->ufs_device_wlun_periph == NULL) { + ufshci_printf(ctrlr, + "Well-known LUN `UFS Device (0x50)` not found\n"); + return (0); + } + + ctrlr->ufs_dev.power_mode_supported = true; return (0); } diff --git a/sys/dev/ufshci/ufshci_private.h b/sys/dev/ufshci/ufshci_private.h --- a/sys/dev/ufshci/ufshci_private.h +++ b/sys/dev/ufshci/ufshci_private.h @@ -31,6 +31,8 @@ #include +#include + #include "ufshci.h" MALLOC_DECLARE(M_UFSHCI); @@ -247,6 +249,9 @@ uint32_t wb_user_space_config_option; uint8_t wb_dedicated_lu; uint32_t write_booster_flush_threshold; + + /* Power mode */ + bool power_mode_supported; }; /* @@ -274,6 +279,9 @@ struct cam_sim *ufshci_sim; struct cam_path *ufshci_path; + struct cam_periph *ufs_device_wlun_periph; + struct mtx ufs_device_wlun_mtx; + struct mtx sc_mtx; uint32_t sc_unit; uint8_t sc_name[16]; @@ -371,8 +379,14 @@ bool error); /* SIM */ +uint8_t ufshci_sim_translate_scsi_to_ufs_lun(lun_id_t scsi_lun); +uint64_t ufshci_sim_translate_ufs_to_scsi_lun(uint8_t ufs_lun); int ufshci_sim_attach(struct ufshci_controller *ctrlr); void ufshci_sim_detach(struct ufshci_controller *ctrlr); +struct cam_periph *ufshci_sim_find_periph(struct ufshci_controller *ctrlr, + uint8_t wlun); +int ufshci_sim_send_ssu(struct ufshci_controller *ctrlr, bool start, + int pwr_cond, bool immed); /* Controller */ int ufshci_ctrlr_construct(struct ufshci_controller *ctrlr, device_t dev); diff --git a/sys/dev/ufshci/ufshci_sim.c b/sys/dev/ufshci/ufshci_sim.c --- a/sys/dev/ufshci/ufshci_sim.c +++ b/sys/dev/ufshci/ufshci_sim.c @@ -10,9 +10,11 @@ #include #include #include +#include #include #include #include +#include #include "ufshci_private.h" @@ -76,7 +78,7 @@ * The SCSI LUN format and the UFS UPIU LUN format are different. * This function converts the SCSI LUN format to the UFS UPIU LUN format. */ -static uint8_t +uint8_t ufshci_sim_translate_scsi_to_ufs_lun(lun_id_t scsi_lun) { const int address_format_offset = 8; @@ -94,6 +96,19 @@ return (scsi_lun & UFSHCI_UPIU_UNIT_NUMBER_ID_MASK); } +uint64_t +ufshci_sim_translate_ufs_to_scsi_lun(uint8_t ufs_lun) +{ + /* Logical unit */ + if (!(ufs_lun & UFSHCI_UPIU_WLUN_ID_MASK)) { + return ufs_lun; + } + + /* Well known logical unit */ + return (((uint64_t)ufs_lun & ~UFSHCI_UPIU_WLUN_ID_MASK) | + (RPL_LUNDATA_ATYP_EXTLUN | RPL_LUNDATA_EXT_EAM_WK) << 8); +} + static void ufshchi_sim_scsiio(struct cam_sim *sim, union ccb *ccb) { @@ -396,3 +411,100 @@ ctrlr->ufshci_sim = NULL; } } + +struct cam_periph * +ufshci_sim_find_periph(struct ufshci_controller *ctrlr, uint8_t wlun) +{ + struct cam_path *path; + struct cam_periph *periph = NULL; + uint64_t scsi_lun; + uint64_t timeout; + + scsi_lun = ufshci_sim_translate_ufs_to_scsi_lun(wlun); + + if (xpt_create_path(&path, /*periph*/ NULL, + cam_sim_path(ctrlr->ufshci_sim), 0, scsi_lun) != CAM_REQ_CMP) { + return NULL; + } + + /* Wait for the perip device to be found */ + timeout = ticks + MSEC_2_TICKS(ctrlr->device_init_timeout_in_ms); + + while (1) { + xpt_path_lock(path); + periph = cam_periph_find(path, "pass"); + xpt_path_unlock(path); + + if (periph) { + xpt_free_path(path); + break; + } + + if (timeout - ticks < 0) { + ufshci_printf(ctrlr, + "Failed to find the Well known LUN(0x%x)\n", wlun); + break; + } + + pause_sbt("ufshci_find_periph", ustosbt(100), 0, C_PREL(1)); + } + + return periph; +} + +/* This function is called during suspend/resume. */ +int +ufshci_sim_send_ssu(struct ufshci_controller *ctrlr, bool start, + int power_condition, bool immed) +{ + struct cam_periph *periph = ctrlr->ufs_device_wlun_periph; + union ccb *ccb; + int err; + + /* Acquire periph reference */ + if (periph && cam_periph_acquire(periph) != 0) { + periph = NULL; + } + + if (periph == NULL) { + /* If the periph device does not exist, it will try to find it + * again */ + periph = ufshci_sim_find_periph(ctrlr, + (uint8_t)UFSHCI_WLUN_UFS_DEVICE); + if (periph) + ctrlr->ufs_device_wlun_periph = periph; + } + + if (periph == NULL) { + ufshci_printf(ctrlr, + "Well-known LUN `UFS Device (0x50)` not found\n"); + return ENODEV; + } + cam_periph_lock(periph); + ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); + if (!ccb) { + cam_periph_unlock(periph); + cam_periph_release(periph); + return ENOMEM; + } + + scsi_start_stop(&ccb->csio, + /*retries*/ 4, + /*cbfcnp*/ NULL, + /*tag_action*/ MSG_SIMPLE_Q_TAG, + /*start*/ start ? 1 : 0, + /*load_eject*/ 0, + /*immediate*/ immed ? 1 : 0, + /*power_condition*/ power_condition, SSD_MIN_SIZE, + ctrlr->device_init_timeout_in_ms); + + ccb->ccb_h.flags |= CAM_DIR_NONE | CAM_DEV_QFRZDIS; + + err = cam_periph_runccb(ccb, NULL, 0, SF_RETRY_UA, NULL); + + cam_periph_unlock(periph); + /* Release periph reference */ + cam_periph_release(periph); + + return (err == 0) ? 0 : EIO; +} diff --git a/sys/dev/ufshci/ufshci_sysctl.c b/sys/dev/ufshci/ufshci_sysctl.c --- a/sys/dev/ufshci/ufshci_sysctl.c +++ b/sys/dev/ufshci/ufshci_sysctl.c @@ -197,6 +197,10 @@ &dev->wb_user_space_config_option, 0, "WriteBooster preserve user space mode"); + SYSCTL_ADD_BOOL(ctrlr_ctx, ctrlr_list, OID_AUTO, "power_mode_supported", + CTLFLAG_RD, &dev->power_mode_supported, 0, + "Device power mode support"); + SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO, "timeout_period", CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, &ctrlr->timeout_period, 0, ufshci_sysctl_timeout_period, "IU",