On Tue, Nov 22, 2022 at 08:10:22PM -0800, Asutosh Das wrote: > Runtime and operation registers are defined per Submission > and Completion queue. > The location of these registers is not defined in the spec; > meaning the offsets and stride may vary for different > HC vendors. Establish the stride, base address and doorbell > address offsets from vendor host driver and program it. > > Co-developed-by: Can Guo <quic_cang@xxxxxxxxxxx> > Signed-off-by: Can Guo <quic_cang@xxxxxxxxxxx> > Signed-off-by: Asutosh Das <quic_asutoshd@xxxxxxxxxxx> Reviewed-by: Manivannan Sadhasivam <mani@xxxxxxxxxx> Thanks, Mani > Reviewed-by: Bart Van Assche <bvanassche@xxxxxxx> > --- > drivers/ufs/core/ufs-mcq.c | 101 +++++++++++++++++++++++++++++++++++++++++ > drivers/ufs/core/ufshcd-priv.h | 11 +++++ > drivers/ufs/core/ufshcd.c | 28 ++++++++++++ > drivers/ufs/host/ufs-qcom.c | 24 ++++++++++ > include/ufs/ufshcd.h | 52 +++++++++++++++++++++ > include/ufs/ufshci.h | 31 +++++++++++++ > 6 files changed, 247 insertions(+) > > diff --git a/drivers/ufs/core/ufs-mcq.c b/drivers/ufs/core/ufs-mcq.c > index 51f0e40..ebecc47 100644 > --- a/drivers/ufs/core/ufs-mcq.c > +++ b/drivers/ufs/core/ufs-mcq.c > @@ -17,6 +17,8 @@ > #define UFS_MCQ_MIN_READ_QUEUES 0 > #define UFS_MCQ_NUM_DEV_CMD_QUEUES 1 > #define UFS_MCQ_MIN_POLL_QUEUES 0 > +#define QUEUE_EN_OFFSET 31 > +#define QUEUE_ID_OFFSET 16 > > #define MAX_DEV_CMD_ENTRIES 2 > #define MCQ_CFG_MAC_MASK GENMASK(16, 8) > @@ -25,6 +27,7 @@ > #define MCQ_SQATTR_OFFSET(c) \ > ((((c) >> 16) & MCQ_QCFGPTR_MASK) * MCQ_QCFGPTR_UNIT) > #define MCQ_QCFG_SIZE 0x40 > +#define MCQ_ENTRY_SIZE_IN_DWORD 8 > > static int rw_queue_count_set(const char *val, const struct kernel_param *kp) > { > @@ -120,6 +123,24 @@ int ufshcd_mcq_decide_queue_depth(struct ufs_hba *hba) > return min_t(int, mac, hba->dev_info.bqueuedepth); > } > > +/** > + * ufshcd_mcq_config_mac - Set the #Max Activ Cmds. > + * @hba - per adapter instance > + * @max_active_cmds - maximum # of active commands to the device at any time. > + * > + * The controller won't send more than the max_active_cmds to the device at > + * any time. > + */ > +void ufshcd_mcq_config_mac(struct ufs_hba *hba, u32 max_active_cmds) > +{ > + u32 val; > + > + val = ufshcd_readl(hba, REG_UFS_MCQ_CFG); > + val &= ~MCQ_CFG_MAC_MASK; > + val |= FIELD_PREP(MCQ_CFG_MAC_MASK, max_active_cmds); > + ufshcd_writel(hba, val, REG_UFS_MCQ_CFG); > +} > + > static int ufshcd_mcq_config_resource(struct ufs_hba *hba) > { > struct platform_device *pdev = to_platform_device(hba->dev); > @@ -279,6 +300,80 @@ int ufshcd_mcq_memory_alloc(struct ufs_hba *hba) > return 0; > } > > +/* Operation and runtime registers configuration */ > +#define MCQ_CFG_n(r, i) ((r) + MCQ_QCFG_SIZE * (i)) > +#define MCQ_OPR_OFFSET_n(p, i) \ > + (hba->mcq_opr[(p)].offset + hba->mcq_opr[(p)].stride * (i)) > + > +static void __iomem *mcq_opr_base(struct ufs_hba *hba, > + enum ufshcd_mcq_opr n, int i) > +{ > + struct ufshcd_mcq_opr_info_t *opr = &hba->mcq_opr[n]; > + > + return opr->base + opr->stride * i; > +} > + > +void ufshcd_mcq_make_queues_operational(struct ufs_hba *hba) > +{ > + struct ufs_hw_queue *hwq; > + u16 qsize; > + int i; > + > + for (i = 0; i < hba->nr_hw_queues; i++) { > + hwq = &hba->uhq[i]; > + hwq->id = i; > + qsize = hwq->max_entries * MCQ_ENTRY_SIZE_IN_DWORD - 1; > + > + /* Submission Queue Lower Base Address */ > + ufsmcq_writelx(hba, lower_32_bits(hwq->sqe_dma_addr), > + MCQ_CFG_n(REG_SQLBA, i)); > + /* Submission Queue Upper Base Address */ > + ufsmcq_writelx(hba, upper_32_bits(hwq->sqe_dma_addr), > + MCQ_CFG_n(REG_SQUBA, i)); > + /* Submission Queue Doorbell Address Offset */ > + ufsmcq_writelx(hba, MCQ_OPR_OFFSET_n(OPR_SQD, i), > + MCQ_CFG_n(REG_SQDAO, i)); > + /* Submission Queue Interrupt Status Address Offset */ > + ufsmcq_writelx(hba, MCQ_OPR_OFFSET_n(OPR_SQIS, i), > + MCQ_CFG_n(REG_SQISAO, i)); > + > + /* Completion Queue Lower Base Address */ > + ufsmcq_writelx(hba, lower_32_bits(hwq->cqe_dma_addr), > + MCQ_CFG_n(REG_CQLBA, i)); > + /* Completion Queue Upper Base Address */ > + ufsmcq_writelx(hba, upper_32_bits(hwq->cqe_dma_addr), > + MCQ_CFG_n(REG_CQUBA, i)); > + /* Completion Queue Doorbell Address Offset */ > + ufsmcq_writelx(hba, MCQ_OPR_OFFSET_n(OPR_CQD, i), > + MCQ_CFG_n(REG_CQDAO, i)); > + /* Completion Queue Interrupt Status Address Offset */ > + ufsmcq_writelx(hba, MCQ_OPR_OFFSET_n(OPR_CQIS, i), > + MCQ_CFG_n(REG_CQISAO, i)); > + > + /* Save the base addresses for quicker access */ > + hwq->mcq_sq_head = mcq_opr_base(hba, OPR_SQD, i) + REG_SQHP; > + hwq->mcq_sq_tail = mcq_opr_base(hba, OPR_SQD, i) + REG_SQTP; > + hwq->mcq_cq_head = mcq_opr_base(hba, OPR_CQD, i) + REG_CQHP; > + hwq->mcq_cq_tail = mcq_opr_base(hba, OPR_CQD, i) + REG_CQTP; > + > + /* Enable Tail Entry Push Status interrupt only for non-poll queues */ > + if (i < hba->nr_hw_queues - hba->nr_queues[HCTX_TYPE_POLL]) > + writel(1, mcq_opr_base(hba, OPR_CQIS, i) + REG_CQIE); > + > + /* Completion Queue Enable|Size to Completion Queue Attribute */ > + ufsmcq_writel(hba, (1 << QUEUE_EN_OFFSET) | qsize, > + MCQ_CFG_n(REG_CQATTR, i)); > + > + /* > + * Submission Qeueue Enable|Size|Completion Queue ID to > + * Submission Queue Attribute > + */ > + ufsmcq_writel(hba, (1 << QUEUE_EN_OFFSET) | qsize | > + (i << QUEUE_ID_OFFSET), > + MCQ_CFG_n(REG_SQATTR, i)); > + } > +} > + > int ufshcd_mcq_init(struct ufs_hba *hba) > { > struct ufs_hw_queue *hwq; > @@ -292,6 +387,12 @@ int ufshcd_mcq_init(struct ufs_hba *hba) > if (ret) > return ret; > > + ret = ufshcd_mcq_vops_op_runtime_config(hba); > + if (ret) { > + dev_err(hba->dev, "Operation runtime config failed, ret=%d\n", > + ret); > + return ret; > + } > hba->uhq = devm_kzalloc(hba->dev, > hba->nr_hw_queues * sizeof(struct ufs_hw_queue), > GFP_KERNEL); > diff --git a/drivers/ufs/core/ufshcd-priv.h b/drivers/ufs/core/ufshcd-priv.h > index 4d2bde2..08963e1 100644 > --- a/drivers/ufs/core/ufshcd-priv.h > +++ b/drivers/ufs/core/ufshcd-priv.h > @@ -64,6 +64,9 @@ void ufshcd_auto_hibern8_update(struct ufs_hba *hba, u32 ahit); > int ufshcd_mcq_init(struct ufs_hba *hba); > int ufshcd_mcq_decide_queue_depth(struct ufs_hba *hba); > int ufshcd_mcq_memory_alloc(struct ufs_hba *hba); > +void ufshcd_mcq_make_queues_operational(struct ufs_hba *hba); > +void ufshcd_mcq_config_mac(struct ufs_hba *hba, u32 max_active_cmds); > +void ufshcd_mcq_select_mcq_mode(struct ufs_hba *hba); > > #define SD_ASCII_STD true > #define SD_RAW false > @@ -237,6 +240,14 @@ static inline int ufshcd_mcq_vops_get_hba_mac(struct ufs_hba *hba) > return -EOPNOTSUPP; > } > > +static inline int ufshcd_mcq_vops_op_runtime_config(struct ufs_hba *hba) > +{ > + if (hba->vops && hba->vops->op_runtime_config) > + return hba->vops->op_runtime_config(hba); > + > + return -EOPNOTSUPP; > +} > + > extern const struct ufs_pm_lvl_states ufs_pm_lvl_states[]; > > /** > diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c > index 45686e8..042ecf04 100644 > --- a/drivers/ufs/core/ufshcd.c > +++ b/drivers/ufs/core/ufshcd.c > @@ -43,6 +43,12 @@ > #define UFSHCD_ENABLE_INTRS (UTP_TRANSFER_REQ_COMPL |\ > UTP_TASK_REQ_COMPL |\ > UFSHCD_ERROR_MASK) > + > +#define UFSHCD_ENABLE_MCQ_INTRS (UTP_TASK_REQ_COMPL |\ > + UFSHCD_ERROR_MASK |\ > + MCQ_CQ_EVENT_STATUS) > + > + > /* UIC command timeout, unit: ms */ > #define UIC_CMD_TIMEOUT 500 > > @@ -8252,6 +8258,20 @@ static int ufshcd_alloc_mcq(struct ufs_hba *hba) > return ret; > } > > +static void ufshcd_config_mcq(struct ufs_hba *hba) > +{ > + ufshcd_enable_intr(hba, UFSHCD_ENABLE_MCQ_INTRS); > + ufshcd_mcq_make_queues_operational(hba); > + ufshcd_mcq_config_mac(hba, hba->nutrs); > + > + hba->host->can_queue = hba->nutrs - UFSHCD_NUM_RESERVED; > + hba->reserved_slot = hba->nutrs - UFSHCD_NUM_RESERVED; > + dev_info(hba->dev, "MCQ configured, nr_queues=%d, io_queues=%d, read_queue=%d, poll_queues=%d, queue_depth=%d\n", > + hba->nr_hw_queues, hba->nr_queues[HCTX_TYPE_DEFAULT], > + hba->nr_queues[HCTX_TYPE_READ], hba->nr_queues[HCTX_TYPE_POLL], > + hba->nutrs); > +} > + > /** > * ufshcd_probe_hba - probe hba to detect device and initialize it > * @hba: per-adapter instance > @@ -8281,6 +8301,10 @@ static int ufshcd_probe_hba(struct ufs_hba *hba, bool init_dev_params) > /* UniPro link is active now */ > ufshcd_set_link_active(hba); > > + /* Reconfigure MCQ upon reset */ > + if (is_mcq_enabled(hba) && !init_dev_params) > + ufshcd_config_mcq(hba); > + > /* Verify device initialization by sending NOP OUT UPIU */ > ret = ufshcd_verify_dev_init(hba); > if (ret) > @@ -8313,9 +8337,13 @@ static int ufshcd_probe_hba(struct ufs_hba *hba, bool init_dev_params) > dev_err(hba->dev, "scsi_add_host failed\n"); > goto out; > } > + /* MCQ may be disabled if ufshcd_alloc_mcq() fails */ > + if (use_mcq_mode) > + ufshcd_config_mcq(hba); > } > } > > + > ufshcd_tune_unipro_params(hba); > > /* UFS device is also active now */ > diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c > index 7bd3c37..dd53e85 100644 > --- a/drivers/ufs/host/ufs-qcom.c > +++ b/drivers/ufs/host/ufs-qcom.c > @@ -1425,6 +1425,29 @@ static void ufs_qcom_config_scaling_param(struct ufs_hba *hba, > } > #endif > > +static int ufs_qcom_op_runtime_config(struct ufs_hba *hba) > +{ > + struct ufshcd_res_info *mem_res, *sqdao_res; > + struct ufshcd_mcq_opr_info_t *opr; > + int i; > + > + mem_res = &hba->res[RES_UFS]; > + sqdao_res = &hba->res[RES_MCQ_SQD]; > + > + if (!mem_res->base || !sqdao_res->base) > + return -EINVAL; > + > + for (i = 0; i < OPR_MAX; i++) { > + opr = &hba->mcq_opr[i]; > + opr->offset = sqdao_res->resource->start - > + mem_res->resource->start + 0x40 * i; > + opr->stride = 0x100; > + opr->base = sqdao_res->base + 0x40 * i; > + } > + > + return 0; > +} > + > static int ufs_qcom_get_hba_mac(struct ufs_hba *hba) > { > /* Qualcomm HC supports up to 64 */ > @@ -1455,6 +1478,7 @@ static const struct ufs_hba_variant_ops ufs_hba_qcom_vops = { > .config_scaling_param = ufs_qcom_config_scaling_param, > .program_key = ufs_qcom_ice_program_key, > .get_hba_mac = ufs_qcom_get_hba_mac, > + .op_runtime_config = ufs_qcom_op_runtime_config, > }; > > /** > diff --git a/include/ufs/ufshcd.h b/include/ufs/ufshcd.h > index e478bab..76972fc 100644 > --- a/include/ufs/ufshcd.h > +++ b/include/ufs/ufshcd.h > @@ -298,6 +298,7 @@ struct ufs_pwr_mode_info { > * @program_key: program or evict an inline encryption key > * @event_notify: called to notify important events > * @get_hba_mac: called to get vendor specific mac value, mandatory for mcq mode > + * @op_runtime_config: called to config Operation and runtime regs Pointers > */ > struct ufs_hba_variant_ops { > const char *name; > @@ -337,6 +338,7 @@ struct ufs_hba_variant_ops { > void (*event_notify)(struct ufs_hba *hba, > enum ufs_event_type evt, void *data); > int (*get_hba_mac)(struct ufs_hba *hba); > + int (*op_runtime_config)(struct ufs_hba *hba); > }; > > /* clock gating state */ > @@ -750,6 +752,27 @@ enum ufshcd_res { > }; > > /** > + * struct ufshcd_mcq_opr_info_t - Operation and Runtime registers > + * > + * @offset: Doorbell Address Offset > + * @stride: Steps proportional to queue [0...31] > + * @base: base address > + */ > +struct ufshcd_mcq_opr_info_t { > + unsigned long offset; > + unsigned long stride; > + void __iomem *base; > +}; > + > +enum ufshcd_mcq_opr { > + OPR_SQD, > + OPR_SQIS, > + OPR_CQD, > + OPR_CQIS, > + OPR_MAX, > +}; > + > +/** > * struct ufs_hba - per adapter private structure > * @mmio_base: UFSHCI base register address > * @ucdl_base_addr: UFS Command Descriptor base address > @@ -859,6 +882,7 @@ enum ufshcd_res { > * ufshcd_resume_complete() > * @ext_iid_sup: is EXT_IID is supported by UFSHC > * @mcq_sup: is mcq supported by UFSHC > + * @mcq_enabled: is mcq ready to accept requests > * @nr_hw_queues: number of hardware queues configured > * @nr_queues: number of Queues of different queue types > * @res: array of resource info of MCQ registers > @@ -1016,29 +1040,47 @@ struct ufs_hba { > bool complete_put; > bool ext_iid_sup; > bool mcq_sup; > + bool mcq_enabled; > unsigned int nr_hw_queues; > unsigned int nr_queues[HCTX_MAX_TYPES]; > struct ufshcd_res_info res[RES_MAX]; > void __iomem *mcq_base; > struct ufs_hw_queue *uhq; > struct ufs_hw_queue *dev_cmd_queue; > + struct ufshcd_mcq_opr_info_t mcq_opr[OPR_MAX]; > }; > > /** > + * @mcq_sq_head: base address of submission queue head pointer > + * @mcq_sq_tail: base address of submission queue tail pointer > + * @mcq_cq_head: base address of completion queue head pointer > + * @mcq_cq_tail: base address of completion queue tail pointer > * @sqe_base_addr: submission queue entry base address > * @sqe_dma_addr: submission queue dma address > * @cqe_base_addr: completion queue base address > * @cqe_dma_addr: completion queue dma address > * @max_entries: max number of slots in this hardware queue > + * @id: hardware queue ID > */ > struct ufs_hw_queue { > + void __iomem *mcq_sq_head; > + void __iomem *mcq_sq_tail; > + void __iomem *mcq_cq_head; > + void __iomem *mcq_cq_tail; > + > void *sqe_base_addr; > dma_addr_t sqe_dma_addr; > struct cq_entry *cqe_base_addr; > dma_addr_t cqe_dma_addr; > u32 max_entries; > + u32 id; > }; > > +static inline bool is_mcq_enabled(struct ufs_hba *hba) > +{ > + return hba->mcq_enabled; > +} > + > /* Returns true if clocks can be gated. Otherwise false */ > static inline bool ufshcd_is_clkgating_allowed(struct ufs_hba *hba) > { > @@ -1094,6 +1136,16 @@ static inline bool ufshcd_enable_wb_if_scaling_up(struct ufs_hba *hba) > return hba->caps & UFSHCD_CAP_WB_WITH_CLK_SCALING; > } > > +#define ufsmcq_writel(hba, val, reg) \ > + writel((val), (hba)->mcq_base + (reg)) > +#define ufsmcq_readl(hba, reg) \ > + readl((hba)->mcq_base + (reg)) > + > +#define ufsmcq_writelx(hba, val, reg) \ > + writel_relaxed((val), (hba)->mcq_base + (reg)) > +#define ufsmcq_readlx(hba, reg) \ > + readl_relaxed((hba)->mcq_base + (reg)) > + > #define ufshcd_writel(hba, val, reg) \ > writel((val), (hba)->mmio_base + (reg)) > #define ufshcd_readl(hba, reg) \ > diff --git a/include/ufs/ufshci.h b/include/ufs/ufshci.h > index 1aae5b2..c85bdf2 100644 > --- a/include/ufs/ufshci.h > +++ b/include/ufs/ufshci.h > @@ -57,6 +57,7 @@ enum { > REG_UFS_CCAP = 0x100, > REG_UFS_CRYPTOCAP = 0x104, > > + REG_UFS_MEM_CFG = 0x300, > REG_UFS_MCQ_CFG = 0x380, > UFSHCI_CRYPTO_REG_SPACE_SIZE = 0x400, > }; > @@ -78,6 +79,35 @@ enum { > MASK_EXT_IID_SUPPORT = 0x00000400, > }; > > +enum { > + REG_SQATTR = 0x0, > + REG_SQLBA = 0x4, > + REG_SQUBA = 0x8, > + REG_SQDAO = 0xC, > + REG_SQISAO = 0x10, > + > + REG_CQATTR = 0x20, > + REG_CQLBA = 0x24, > + REG_CQUBA = 0x28, > + REG_CQDAO = 0x2C, > + REG_CQISAO = 0x30, > +}; > + > +enum { > + REG_SQHP = 0x0, > + REG_SQTP = 0x4, > +}; > + > +enum { > + REG_CQHP = 0x0, > + REG_CQTP = 0x4, > +}; > + > +enum { > + REG_CQIS = 0x0, > + REG_CQIE = 0x4, > +}; > + > #define UFS_MASK(mask, offset) ((mask) << (offset)) > > /* UFS Version 08h */ > @@ -134,6 +164,7 @@ static inline u32 ufshci_version(u32 major, u32 minor) > #define CONTROLLER_FATAL_ERROR 0x10000 > #define SYSTEM_BUS_FATAL_ERROR 0x20000 > #define CRYPTO_ENGINE_FATAL_ERROR 0x40000 > +#define MCQ_CQ_EVENT_STATUS 0x100000 > > #define UFSHCD_UIC_HIBERN8_MASK (UIC_HIBERNATE_ENTER |\ > UIC_HIBERNATE_EXIT) > -- > 2.7.4 > -- மணிவண்ணன் சதாசிவம்