diff options
Diffstat (limited to 'drivers/ufs/host')
-rw-r--r-- | drivers/ufs/host/Kconfig | 1 | ||||
-rw-r--r-- | drivers/ufs/host/cdns-pltfrm.c | 27 | ||||
-rw-r--r-- | drivers/ufs/host/tc-dwc-g210-pci.c | 2 | ||||
-rw-r--r-- | drivers/ufs/host/tc-dwc-g210.c | 32 | ||||
-rw-r--r-- | drivers/ufs/host/ti-j721e-ufs.c | 2 | ||||
-rw-r--r-- | drivers/ufs/host/ufs-mediatek.c | 180 | ||||
-rw-r--r-- | drivers/ufs/host/ufs-mediatek.h | 33 | ||||
-rw-r--r-- | drivers/ufs/host/ufs-qcom.c | 176 | ||||
-rw-r--r-- | drivers/ufs/host/ufs-qcom.h | 4 | ||||
-rw-r--r-- | drivers/ufs/host/ufs-renesas.c | 4 | ||||
-rw-r--r-- | drivers/ufs/host/ufshcd-dwc.c | 22 | ||||
-rw-r--r-- | drivers/ufs/host/ufshcd-pci.c | 3 | ||||
-rw-r--r-- | drivers/ufs/host/ufshcd-pltfrm.c | 13 |
13 files changed, 413 insertions, 86 deletions
diff --git a/drivers/ufs/host/Kconfig b/drivers/ufs/host/Kconfig index 16624ba08050..580c8d0bd8bb 100644 --- a/drivers/ufs/host/Kconfig +++ b/drivers/ufs/host/Kconfig @@ -72,6 +72,7 @@ config SCSI_UFS_QCOM config SCSI_UFS_MEDIATEK tristate "Mediatek specific hooks to UFS controller platform driver" depends on SCSI_UFSHCD_PLATFORM && ARCH_MEDIATEK + depends on RESET_CONTROLLER select PHY_MTK_UFS select RESET_TI_SYSCON help diff --git a/drivers/ufs/host/cdns-pltfrm.c b/drivers/ufs/host/cdns-pltfrm.c index 26761425a76c..2491e7e87028 100644 --- a/drivers/ufs/host/cdns-pltfrm.c +++ b/drivers/ufs/host/cdns-pltfrm.c @@ -101,11 +101,10 @@ static void cdns_ufs_set_l4_attr(struct ufs_hba *hba) } /** - * cdns_ufs_set_hclkdiv() - * Sets HCLKDIV register value based on the core_clk + * cdns_ufs_set_hclkdiv() - set HCLKDIV register value based on the core_clk. * @hba: host controller instance * - * Return zero for success and non-zero for failure + * Return: zero for success and non-zero for failure. */ static int cdns_ufs_set_hclkdiv(struct ufs_hba *hba) { @@ -143,12 +142,11 @@ static int cdns_ufs_set_hclkdiv(struct ufs_hba *hba) } /** - * cdns_ufs_hce_enable_notify() - * Called before and after HCE enable bit is set. + * cdns_ufs_hce_enable_notify() - set HCLKDIV register * @hba: host controller instance * @status: notify stage (pre, post change) * - * Return zero for success and non-zero for failure + * Return: zero for success and non-zero for failure. */ static int cdns_ufs_hce_enable_notify(struct ufs_hba *hba, enum ufs_notify_change_status status) @@ -160,12 +158,10 @@ static int cdns_ufs_hce_enable_notify(struct ufs_hba *hba, } /** - * cdns_ufs_hibern8_notify() - * Called around hibern8 enter/exit. + * cdns_ufs_hibern8_notify() - save and restore L4 attributes. * @hba: host controller instance * @cmd: UIC Command * @status: notify stage (pre, post change) - * */ static void cdns_ufs_hibern8_notify(struct ufs_hba *hba, enum uic_cmd_dme cmd, enum ufs_notify_change_status status) @@ -177,12 +173,11 @@ static void cdns_ufs_hibern8_notify(struct ufs_hba *hba, enum uic_cmd_dme cmd, } /** - * cdns_ufs_link_startup_notify() - * Called before and after Link startup is carried out. + * cdns_ufs_link_startup_notify() - handle link startup. * @hba: host controller instance * @status: notify stage (pre, post change) * - * Return zero for success and non-zero for failure + * Return: zero for success and non-zero for failure. */ static int cdns_ufs_link_startup_notify(struct ufs_hba *hba, enum ufs_notify_change_status status) @@ -212,7 +207,7 @@ static int cdns_ufs_link_startup_notify(struct ufs_hba *hba, * cdns_ufs_init - performs additional ufs initialization * @hba: host controller instance * - * Returns status of initialization + * Return: status of initialization. */ static int cdns_ufs_init(struct ufs_hba *hba) { @@ -235,7 +230,7 @@ static int cdns_ufs_init(struct ufs_hba *hba) * cdns_ufs_m31_16nm_phy_initialization - performs m31 phy initialization * @hba: host controller instance * - * Always returns 0 + * Return: 0 (success). */ static int cdns_ufs_m31_16nm_phy_initialization(struct ufs_hba *hba) { @@ -284,7 +279,7 @@ MODULE_DEVICE_TABLE(of, cdns_ufs_of_match); * cdns_ufs_pltfrm_probe - probe routine of the driver * @pdev: pointer to platform device handle * - * Return zero for success and non-zero for failure + * Return: zero for success and non-zero for failure. */ static int cdns_ufs_pltfrm_probe(struct platform_device *pdev) { @@ -308,7 +303,7 @@ static int cdns_ufs_pltfrm_probe(struct platform_device *pdev) * cdns_ufs_pltfrm_remove - removes the ufs driver * @pdev: pointer to platform device handle * - * Always returns 0 + * Return: 0 (success). */ static int cdns_ufs_pltfrm_remove(struct platform_device *pdev) { diff --git a/drivers/ufs/host/tc-dwc-g210-pci.c b/drivers/ufs/host/tc-dwc-g210-pci.c index f96fe5855841..876781fd6861 100644 --- a/drivers/ufs/host/tc-dwc-g210-pci.c +++ b/drivers/ufs/host/tc-dwc-g210-pci.c @@ -51,7 +51,7 @@ static void tc_dwc_g210_pci_remove(struct pci_dev *pdev) * @pdev: pointer to PCI device handle * @id: PCI device id * - * Returns 0 on success, non-zero value on failure + * Return: 0 on success, non-zero value on failure. */ static int tc_dwc_g210_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) diff --git a/drivers/ufs/host/tc-dwc-g210.c b/drivers/ufs/host/tc-dwc-g210.c index deb93dbd83a4..0ac53cc8465e 100644 --- a/drivers/ufs/host/tc-dwc-g210.c +++ b/drivers/ufs/host/tc-dwc-g210.c @@ -17,11 +17,10 @@ #include "tc-dwc-g210.h" /** - * tc_dwc_g210_setup_40bit_rmmi() - * This function configures Synopsys TC specific atributes (40-bit RMMI) + * tc_dwc_g210_setup_40bit_rmmi() - configure 40-bit RMMI. * @hba: Pointer to drivers structure * - * Returns 0 on success or non-zero value on failure + * Return: 0 on success or non-zero value on failure. */ static int tc_dwc_g210_setup_40bit_rmmi(struct ufs_hba *hba) { @@ -81,11 +80,10 @@ static int tc_dwc_g210_setup_40bit_rmmi(struct ufs_hba *hba) } /** - * tc_dwc_g210_setup_20bit_rmmi_lane0() - * This function configures Synopsys TC 20-bit RMMI Lane 0 + * tc_dwc_g210_setup_20bit_rmmi_lane0() - configure 20-bit RMMI Lane 0. * @hba: Pointer to drivers structure * - * Returns 0 on success or non-zero value on failure + * Return: 0 on success or non-zero value on failure. */ static int tc_dwc_g210_setup_20bit_rmmi_lane0(struct ufs_hba *hba) { @@ -134,11 +132,10 @@ static int tc_dwc_g210_setup_20bit_rmmi_lane0(struct ufs_hba *hba) } /** - * tc_dwc_g210_setup_20bit_rmmi_lane1() - * This function configures Synopsys TC 20-bit RMMI Lane 1 + * tc_dwc_g210_setup_20bit_rmmi_lane1() - configure 20-bit RMMI Lane 1. * @hba: Pointer to drivers structure * - * Returns 0 on success or non-zero value on failure + * Return: 0 on success or non-zero value on failure. */ static int tc_dwc_g210_setup_20bit_rmmi_lane1(struct ufs_hba *hba) { @@ -211,11 +208,10 @@ out: } /** - * tc_dwc_g210_setup_20bit_rmmi() - * This function configures Synopsys TC specific atributes (20-bit RMMI) + * tc_dwc_g210_setup_20bit_rmmi() - configure 20-bit RMMI. * @hba: Pointer to drivers structure * - * Returns 0 on success or non-zero value on failure + * Return: 0 on success or non-zero value on failure. */ static int tc_dwc_g210_setup_20bit_rmmi(struct ufs_hba *hba) { @@ -251,12 +247,10 @@ out: } /** - * tc_dwc_g210_config_40_bit() - * This function configures Local (host) Synopsys 40-bit TC specific attributes - * + * tc_dwc_g210_config_40_bit() - configure 40-bit TC specific attributes. * @hba: Pointer to drivers structure * - * Returns 0 on success non-zero value on failure + * Return: 0 on success non-zero value on failure. */ int tc_dwc_g210_config_40_bit(struct ufs_hba *hba) { @@ -283,12 +277,10 @@ out: EXPORT_SYMBOL(tc_dwc_g210_config_40_bit); /** - * tc_dwc_g210_config_20_bit() - * This function configures Local (host) Synopsys 20-bit TC specific attributes - * + * tc_dwc_g210_config_20_bit() - configure 20-bit TC specific attributes. * @hba: Pointer to drivers structure * - * Returns 0 on success non-zero value on failure + * Return: 0 on success non-zero value on failure. */ int tc_dwc_g210_config_20_bit(struct ufs_hba *hba) { diff --git a/drivers/ufs/host/ti-j721e-ufs.c b/drivers/ufs/host/ti-j721e-ufs.c index 122d650d0810..117eb7da92ac 100644 --- a/drivers/ufs/host/ti-j721e-ufs.c +++ b/drivers/ufs/host/ti-j721e-ufs.c @@ -81,6 +81,8 @@ static const struct of_device_id ti_j721e_ufs_of_match[] = { { }, }; +MODULE_DEVICE_TABLE(of, ti_j721e_ufs_of_match); + static struct platform_driver ti_j721e_ufs_driver = { .probe = ti_j721e_ufs_probe, .remove = ti_j721e_ufs_remove, diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c index e68b05976f9e..2383ecd88f1c 100644 --- a/drivers/ufs/host/ufs-mediatek.c +++ b/drivers/ufs/host/ufs-mediatek.c @@ -14,6 +14,7 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_device.h> +#include <linux/of_platform.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> #include <linux/pm_qos.h> @@ -27,8 +28,14 @@ #include <ufs/unipro.h> #include "ufs-mediatek.h" +static int ufs_mtk_config_mcq(struct ufs_hba *hba, bool irq); + #define CREATE_TRACE_POINTS #include "ufs-mediatek-trace.h" +#undef CREATE_TRACE_POINTS + +#define MAX_SUPP_MAC 64 +#define MCQ_QUEUE_OFFSET(c) ((((c) >> 16) & 0xFF) * 0x200) static const struct ufs_dev_quirk ufs_mtk_dev_fixups[] = { { .wmanufacturerid = UFS_ANY_VENDOR, @@ -659,7 +666,7 @@ static void ufs_mtk_pwr_ctrl(struct ufs_hba *hba, bool on) * @on: If true, enable clocks else disable them. * @status: PRE_CHANGE or POST_CHANGE notify * - * Returns 0 on success, non-zero on failure. + * Return: 0 on success, non-zero on failure. */ static int ufs_mtk_setup_clocks(struct ufs_hba *hba, bool on, enum ufs_notify_change_status status) @@ -840,6 +847,37 @@ static void ufs_mtk_vreg_fix_vccqx(struct ufs_hba *hba) } } +static void ufs_mtk_init_mcq_irq(struct ufs_hba *hba) +{ + struct ufs_mtk_host *host = ufshcd_get_variant(hba); + struct platform_device *pdev; + int i; + int irq; + + host->mcq_nr_intr = UFSHCD_MAX_Q_NR; + pdev = container_of(hba->dev, struct platform_device, dev); + + for (i = 0; i < host->mcq_nr_intr; i++) { + /* irq index 0 is legacy irq, sq/cq irq start from index 1 */ + irq = platform_get_irq(pdev, i + 1); + if (irq < 0) { + host->mcq_intr_info[i].irq = MTK_MCQ_INVALID_IRQ; + goto failed; + } + host->mcq_intr_info[i].hba = hba; + host->mcq_intr_info[i].irq = irq; + dev_info(hba->dev, "get platform mcq irq: %d, %d\n", i, irq); + } + + return; +failed: + /* invalidate irq info */ + for (i = 0; i < host->mcq_nr_intr; i++) + host->mcq_intr_info[i].irq = MTK_MCQ_INVALID_IRQ; + + host->mcq_nr_intr = 0; +} + /** * ufs_mtk_init - find other essential mmio bases * @hba: host controller instance @@ -847,7 +885,7 @@ static void ufs_mtk_vreg_fix_vccqx(struct ufs_hba *hba) * Binds PHY with controller and powers up PHY enabling clocks * and regulators. * - * Returns -EPROBE_DEFER if binding fails, returns negative error + * Return: -EPROBE_DEFER if binding fails, returns negative error * on phy power up failure and returns zero on success. */ static int ufs_mtk_init(struct ufs_hba *hba) @@ -876,6 +914,8 @@ static int ufs_mtk_init(struct ufs_hba *hba) /* Initialize host capability */ ufs_mtk_init_host_caps(hba); + ufs_mtk_init_mcq_irq(hba); + err = ufs_mtk_bind_mphy(hba); if (err) goto out_variant_clear; @@ -1173,7 +1213,17 @@ static int ufs_mtk_link_set_hpm(struct ufs_hba *hba) else return err; - err = ufshcd_make_hba_operational(hba); + if (!hba->mcq_enabled) { + err = ufshcd_make_hba_operational(hba); + } else { + ufs_mtk_config_mcq(hba, false); + ufshcd_mcq_make_queues_operational(hba); + ufshcd_mcq_config_mac(hba, hba->nutrs); + /* Enable MCQ mode */ + ufshcd_writel(hba, ufshcd_readl(hba, REG_UFS_MEM_CFG) | 0x1, + REG_UFS_MEM_CFG); + } + if (err) return err; @@ -1497,6 +1547,121 @@ static int ufs_mtk_clk_scale_notify(struct ufs_hba *hba, bool scale_up, return 0; } +static int ufs_mtk_get_hba_mac(struct ufs_hba *hba) +{ + return MAX_SUPP_MAC; +} + +static int ufs_mtk_op_runtime_config(struct ufs_hba *hba) +{ + struct ufshcd_mcq_opr_info_t *opr; + int i; + + hba->mcq_opr[OPR_SQD].offset = REG_UFS_MTK_SQD; + hba->mcq_opr[OPR_SQIS].offset = REG_UFS_MTK_SQIS; + hba->mcq_opr[OPR_CQD].offset = REG_UFS_MTK_CQD; + hba->mcq_opr[OPR_CQIS].offset = REG_UFS_MTK_CQIS; + + for (i = 0; i < OPR_MAX; i++) { + opr = &hba->mcq_opr[i]; + opr->stride = REG_UFS_MCQ_STRIDE; + opr->base = hba->mmio_base + opr->offset; + } + + return 0; +} + +static int ufs_mtk_mcq_config_resource(struct ufs_hba *hba) +{ + struct ufs_mtk_host *host = ufshcd_get_variant(hba); + + /* fail mcq initialization if interrupt is not filled properly */ + if (!host->mcq_nr_intr) { + dev_info(hba->dev, "IRQs not ready. MCQ disabled."); + return -EINVAL; + } + + hba->mcq_base = hba->mmio_base + MCQ_QUEUE_OFFSET(hba->mcq_capabilities); + return 0; +} + +static irqreturn_t ufs_mtk_mcq_intr(int irq, void *__intr_info) +{ + struct ufs_mtk_mcq_intr_info *mcq_intr_info = __intr_info; + struct ufs_hba *hba = mcq_intr_info->hba; + struct ufs_hw_queue *hwq; + u32 events; + int qid = mcq_intr_info->qid; + + hwq = &hba->uhq[qid]; + + events = ufshcd_mcq_read_cqis(hba, qid); + if (events) + ufshcd_mcq_write_cqis(hba, events, qid); + + if (events & UFSHCD_MCQ_CQIS_TAIL_ENT_PUSH_STS) + ufshcd_mcq_poll_cqe_lock(hba, hwq); + + return IRQ_HANDLED; +} + +static int ufs_mtk_config_mcq_irq(struct ufs_hba *hba) +{ + struct ufs_mtk_host *host = ufshcd_get_variant(hba); + u32 irq, i; + int ret; + + for (i = 0; i < host->mcq_nr_intr; i++) { + irq = host->mcq_intr_info[i].irq; + if (irq == MTK_MCQ_INVALID_IRQ) { + dev_err(hba->dev, "invalid irq. %d\n", i); + return -ENOPARAM; + } + + host->mcq_intr_info[i].qid = i; + ret = devm_request_irq(hba->dev, irq, ufs_mtk_mcq_intr, 0, UFSHCD, + &host->mcq_intr_info[i]); + + dev_dbg(hba->dev, "request irq %d intr %s\n", irq, ret ? "failed" : ""); + + if (ret) { + dev_err(hba->dev, "Cannot request irq %d\n", ret); + return ret; + } + } + + return 0; +} + +static int ufs_mtk_config_mcq(struct ufs_hba *hba, bool irq) +{ + struct ufs_mtk_host *host = ufshcd_get_variant(hba); + int ret = 0; + + if (!host->mcq_set_intr) { + /* Disable irq option register */ + ufshcd_rmwl(hba, MCQ_INTR_EN_MSK, 0, REG_UFS_MMIO_OPT_CTRL_0); + + if (irq) { + ret = ufs_mtk_config_mcq_irq(hba); + if (ret) + return ret; + } + + host->mcq_set_intr = true; + } + + ufshcd_rmwl(hba, MCQ_AH8, MCQ_AH8, REG_UFS_MMIO_OPT_CTRL_0); + ufshcd_rmwl(hba, MCQ_INTR_EN_MSK, MCQ_MULTI_INTR_EN, REG_UFS_MMIO_OPT_CTRL_0); + + return 0; +} + +static int ufs_mtk_config_esi(struct ufs_hba *hba) +{ + return ufs_mtk_config_mcq(hba, true); +} + /* * struct ufs_hba_mtk_vops - UFS MTK specific variant operations * @@ -1520,13 +1685,18 @@ static const struct ufs_hba_variant_ops ufs_hba_mtk_vops = { .event_notify = ufs_mtk_event_notify, .config_scaling_param = ufs_mtk_config_scaling_param, .clk_scale_notify = ufs_mtk_clk_scale_notify, + /* mcq vops */ + .get_hba_mac = ufs_mtk_get_hba_mac, + .op_runtime_config = ufs_mtk_op_runtime_config, + .mcq_config_resource = ufs_mtk_mcq_config_resource, + .config_esi = ufs_mtk_config_esi, }; /** * ufs_mtk_probe - probe routine of the driver * @pdev: pointer to Platform device handle * - * Return zero for success and non-zero for failure + * Return: zero for success and non-zero for failure. */ static int ufs_mtk_probe(struct platform_device *pdev) { @@ -1566,7 +1736,7 @@ skip_reset: out: if (err) - dev_info(dev, "probe failed %d\n", err); + dev_err(dev, "probe failed %d\n", err); of_node_put(reset_node); return err; diff --git a/drivers/ufs/host/ufs-mediatek.h b/drivers/ufs/host/ufs-mediatek.h index 2fc6d7b87694..f76e80d91729 100644 --- a/drivers/ufs/host/ufs-mediatek.h +++ b/drivers/ufs/host/ufs-mediatek.h @@ -11,10 +11,26 @@ #include <linux/soc/mediatek/mtk_sip_svc.h> /* + * MCQ define and struct + */ +#define UFSHCD_MAX_Q_NR 8 +#define MTK_MCQ_INVALID_IRQ 0xFFFF + +/* REG_UFS_MMIO_OPT_CTRL_0 160h */ +#define EHS_EN BIT(0) +#define PFM_IMPV BIT(1) +#define MCQ_MULTI_INTR_EN BIT(2) +#define MCQ_CMB_INTR_EN BIT(3) +#define MCQ_AH8 BIT(4) + +#define MCQ_INTR_EN_MSK (MCQ_MULTI_INTR_EN | MCQ_CMB_INTR_EN) + +/* * Vendor specific UFSHCI Registers */ #define REG_UFS_XOUFS_CTRL 0x140 #define REG_UFS_REFCLK_CTRL 0x144 +#define REG_UFS_MMIO_OPT_CTRL_0 0x160 #define REG_UFS_EXTREG 0x2100 #define REG_UFS_MPHYCTRL 0x2200 #define REG_UFS_MTK_IP_VER 0x2240 @@ -26,6 +42,13 @@ #define REG_UFS_DEBUG_SEL_B2 0x22D8 #define REG_UFS_DEBUG_SEL_B3 0x22DC +#define REG_UFS_MTK_SQD 0x2800 +#define REG_UFS_MTK_SQIS 0x2814 +#define REG_UFS_MTK_CQD 0x281C +#define REG_UFS_MTK_CQIS 0x2824 + +#define REG_UFS_MCQ_STRIDE 0x30 + /* * Ref-clk control * @@ -136,6 +159,12 @@ struct ufs_mtk_hw_ver { u8 major; }; +struct ufs_mtk_mcq_intr_info { + struct ufs_hba *hba; + u32 irq; + u8 qid; +}; + struct ufs_mtk_host { struct phy *mphy; struct pm_qos_request pm_qos_req; @@ -155,6 +184,10 @@ struct ufs_mtk_host { u16 ref_clk_ungating_wait_us; u16 ref_clk_gating_wait_us; u32 ip_ver; + + bool mcq_set_intr; + int mcq_nr_intr; + struct ufs_mtk_mcq_intr_info mcq_intr_info[UFSHCD_MAX_Q_NR]; }; /* diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c index 8d6fd4c3324f..d1149b1c3ed5 100644 --- a/drivers/ufs/host/ufs-qcom.c +++ b/drivers/ufs/host/ufs-qcom.c @@ -7,6 +7,7 @@ #include <linux/time.h> #include <linux/clk.h> #include <linux/delay.h> +#include <linux/interconnect.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> @@ -46,6 +47,49 @@ enum { TSTBUS_MAX, }; +#define QCOM_UFS_MAX_GEAR 4 +#define QCOM_UFS_MAX_LANE 2 + +enum { + MODE_MIN, + MODE_PWM, + MODE_HS_RA, + MODE_HS_RB, + MODE_MAX, +}; + +static const struct __ufs_qcom_bw_table { + u32 mem_bw; + u32 cfg_bw; +} ufs_qcom_bw_table[MODE_MAX + 1][QCOM_UFS_MAX_GEAR + 1][QCOM_UFS_MAX_LANE + 1] = { + [MODE_MIN][0][0] = { 0, 0 }, /* Bandwidth values in KB/s */ + [MODE_PWM][UFS_PWM_G1][UFS_LANE_1] = { 922, 1000 }, + [MODE_PWM][UFS_PWM_G2][UFS_LANE_1] = { 1844, 1000 }, + [MODE_PWM][UFS_PWM_G3][UFS_LANE_1] = { 3688, 1000 }, + [MODE_PWM][UFS_PWM_G4][UFS_LANE_1] = { 7376, 1000 }, + [MODE_PWM][UFS_PWM_G1][UFS_LANE_2] = { 1844, 1000 }, + [MODE_PWM][UFS_PWM_G2][UFS_LANE_2] = { 3688, 1000 }, + [MODE_PWM][UFS_PWM_G3][UFS_LANE_2] = { 7376, 1000 }, + [MODE_PWM][UFS_PWM_G4][UFS_LANE_2] = { 14752, 1000 }, + [MODE_HS_RA][UFS_HS_G1][UFS_LANE_1] = { 127796, 1000 }, + [MODE_HS_RA][UFS_HS_G2][UFS_LANE_1] = { 255591, 1000 }, + [MODE_HS_RA][UFS_HS_G3][UFS_LANE_1] = { 1492582, 102400 }, + [MODE_HS_RA][UFS_HS_G4][UFS_LANE_1] = { 2915200, 204800 }, + [MODE_HS_RA][UFS_HS_G1][UFS_LANE_2] = { 255591, 1000 }, + [MODE_HS_RA][UFS_HS_G2][UFS_LANE_2] = { 511181, 1000 }, + [MODE_HS_RA][UFS_HS_G3][UFS_LANE_2] = { 1492582, 204800 }, + [MODE_HS_RA][UFS_HS_G4][UFS_LANE_2] = { 2915200, 409600 }, + [MODE_HS_RB][UFS_HS_G1][UFS_LANE_1] = { 149422, 1000 }, + [MODE_HS_RB][UFS_HS_G2][UFS_LANE_1] = { 298189, 1000 }, + [MODE_HS_RB][UFS_HS_G3][UFS_LANE_1] = { 1492582, 102400 }, + [MODE_HS_RB][UFS_HS_G4][UFS_LANE_1] = { 2915200, 204800 }, + [MODE_HS_RB][UFS_HS_G1][UFS_LANE_2] = { 298189, 1000 }, + [MODE_HS_RB][UFS_HS_G2][UFS_LANE_2] = { 596378, 1000 }, + [MODE_HS_RB][UFS_HS_G3][UFS_LANE_2] = { 1492582, 204800 }, + [MODE_HS_RB][UFS_HS_G4][UFS_LANE_2] = { 2915200, 409600 }, + [MODE_MAX][0][0] = { 7643136, 307200 }, +}; + static struct ufs_qcom_host *ufs_qcom_hosts[MAX_UFS_QCOM_HOSTS]; static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host); @@ -321,7 +365,7 @@ static void ufs_qcom_select_unipro_mode(struct ufs_qcom_host *host) ufs_qcom_cap_qunipro(host) ? QUNIPRO_SEL : 0, REG_UFS_CFG1); - if (host->hw_ver.major == 0x05) + if (host->hw_ver.major >= 0x05) ufshcd_rmwl(host->hba, QUNIPRO_G4_SEL, 0, REG_UFS_CFG0); /* make sure above configuration is applied before we return */ @@ -485,7 +529,7 @@ static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba, } /* - * Returns zero for success and non-zero in case of a failure + * Return: zero for success and non-zero in case of a failure. */ static int ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs, u32 rate, bool update_link_startup_timer) @@ -789,6 +833,51 @@ static void ufs_qcom_dev_ref_clk_ctrl(struct ufs_qcom_host *host, bool enable) } } +static int ufs_qcom_icc_set_bw(struct ufs_qcom_host *host, u32 mem_bw, u32 cfg_bw) +{ + struct device *dev = host->hba->dev; + int ret; + + ret = icc_set_bw(host->icc_ddr, 0, mem_bw); + if (ret < 0) { + dev_err(dev, "failed to set bandwidth request: %d\n", ret); + return ret; + } + + ret = icc_set_bw(host->icc_cpu, 0, cfg_bw); + if (ret < 0) { + dev_err(dev, "failed to set bandwidth request: %d\n", ret); + return ret; + } + + return 0; +} + +static struct __ufs_qcom_bw_table ufs_qcom_get_bw_table(struct ufs_qcom_host *host) +{ + struct ufs_pa_layer_attr *p = &host->dev_req_params; + int gear = max_t(u32, p->gear_rx, p->gear_tx); + int lane = max_t(u32, p->lane_rx, p->lane_tx); + + if (ufshcd_is_hs_mode(p)) { + if (p->hs_rate == PA_HS_MODE_B) + return ufs_qcom_bw_table[MODE_HS_RB][gear][lane]; + else + return ufs_qcom_bw_table[MODE_HS_RA][gear][lane]; + } else { + return ufs_qcom_bw_table[MODE_PWM][gear][lane]; + } +} + +static int ufs_qcom_icc_update_bw(struct ufs_qcom_host *host) +{ + struct __ufs_qcom_bw_table bw_table; + + bw_table = ufs_qcom_get_bw_table(host); + + return ufs_qcom_icc_set_bw(host, bw_table.mem_bw, bw_table.cfg_bw); +} + static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba, enum ufs_notify_change_status status, struct ufs_pa_layer_attr *dev_max_params, @@ -852,6 +941,8 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba, memcpy(&host->dev_req_params, dev_req_params, sizeof(*dev_req_params)); + ufs_qcom_icc_update_bw(host); + /* disable the device ref clock if entered PWM mode */ if (ufshcd_is_hs_mode(&hba->pwr_info) && !ufshcd_is_hs_mode(dev_req_params)) @@ -964,7 +1055,7 @@ static void ufs_qcom_set_caps(struct ufs_hba *hba) * @on: If true, enable clocks else disable them. * @status: PRE_CHANGE or POST_CHANGE notify * - * Returns 0 on success, non-zero on failure. + * Return: 0 on success, non-zero on failure. */ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on, enum ufs_notify_change_status status) @@ -981,7 +1072,9 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on, switch (status) { case PRE_CHANGE: - if (!on) { + if (on) { + ufs_qcom_icc_update_bw(host); + } else { if (!ufs_qcom_is_link_active(hba)) { /* disable device ref_clk */ ufs_qcom_dev_ref_clk_ctrl(host, false); @@ -993,6 +1086,9 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on, /* enable the device ref clock for HS mode*/ if (ufshcd_is_hs_mode(&hba->pwr_info)) ufs_qcom_dev_ref_clk_ctrl(host, true); + } else { + ufs_qcom_icc_set_bw(host, ufs_qcom_bw_table[MODE_MIN][0][0].mem_bw, + ufs_qcom_bw_table[MODE_MIN][0][0].cfg_bw); } break; } @@ -1031,6 +1127,34 @@ static const struct reset_control_ops ufs_qcom_reset_ops = { .deassert = ufs_qcom_reset_deassert, }; +static int ufs_qcom_icc_init(struct ufs_qcom_host *host) +{ + struct device *dev = host->hba->dev; + int ret; + + host->icc_ddr = devm_of_icc_get(dev, "ufs-ddr"); + if (IS_ERR(host->icc_ddr)) + return dev_err_probe(dev, PTR_ERR(host->icc_ddr), + "failed to acquire interconnect path\n"); + + host->icc_cpu = devm_of_icc_get(dev, "cpu-ufs"); + if (IS_ERR(host->icc_cpu)) + return dev_err_probe(dev, PTR_ERR(host->icc_cpu), + "failed to acquire interconnect path\n"); + + /* + * Set Maximum bandwidth vote before initializing the UFS controller and + * device. Ideally, a minimal interconnect vote would suffice for the + * initialization, but a max vote would allow faster initialization. + */ + ret = ufs_qcom_icc_set_bw(host, ufs_qcom_bw_table[MODE_MAX][0][0].mem_bw, + ufs_qcom_bw_table[MODE_MAX][0][0].cfg_bw); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to set bandwidth request\n"); + + return 0; +} + /** * ufs_qcom_init - bind phy with controller * @hba: host controller instance @@ -1038,7 +1162,7 @@ static const struct reset_control_ops ufs_qcom_reset_ops = { * Binds PHY with controller and powers up PHY enabling clocks * and regulators. * - * Returns -EPROBE_DEFER if binding fails, returns negative error + * Return: -EPROBE_DEFER if binding fails, returns negative error * on phy power up failure and returns zero on success. */ static int ufs_qcom_init(struct ufs_hba *hba) @@ -1085,6 +1209,10 @@ static int ufs_qcom_init(struct ufs_hba *hba) } } + err = ufs_qcom_icc_init(host); + if (err) + goto out_variant_clear; + host->device_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(host->device_reset)) { @@ -1254,6 +1382,10 @@ static int ufs_qcom_clk_scale_notify(struct ufs_hba *hba, struct ufs_pa_layer_attr *dev_req_params = &host->dev_req_params; int err = 0; + /* check the host controller state before sending hibern8 cmd */ + if (!ufshcd_is_hba_active(hba)) + return 0; + if (status == PRE_CHANGE) { err = ufshcd_uic_hibern8_enter(hba); if (err) @@ -1282,6 +1414,7 @@ static int ufs_qcom_clk_scale_notify(struct ufs_hba *hba, dev_req_params->pwr_rx, dev_req_params->hs_rate, false); + ufs_qcom_icc_update_bw(host); ufshcd_uic_hibern8_exit(hba); } @@ -1483,6 +1616,7 @@ static void ufs_qcom_config_scaling_param(struct ufs_hba *hba, struct devfreq_simple_ondemand_data *d) { p->polling_ms = 60; + p->timer = DEVFREQ_TIMER_DELAYED; d->upthreshold = 70; d->downdifferential = 5; } @@ -1643,11 +1777,12 @@ static void ufs_qcom_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg) ufshcd_mcq_config_esi(hba, msg); } -static irqreturn_t ufs_qcom_mcq_esi_handler(int irq, void *__hba) +static irqreturn_t ufs_qcom_mcq_esi_handler(int irq, void *data) { - struct ufs_hba *hba = __hba; - struct ufs_qcom_host *host = ufshcd_get_variant(hba); - u32 id = irq - host->esi_base; + struct msi_desc *desc = data; + struct device *dev = msi_desc_to_dev(desc); + struct ufs_hba *hba = dev_get_drvdata(dev); + u32 id = desc->msi_index; struct ufs_hw_queue *hwq = &hba->uhq[id]; ufshcd_mcq_write_cqis(hba, 0x1, id); @@ -1665,8 +1800,6 @@ static int ufs_qcom_config_esi(struct ufs_hba *hba) if (host->esi_enabled) return 0; - else if (host->esi_base < 0) - return -EINVAL; /* * 1. We only handle CQs as of now. @@ -1675,16 +1808,16 @@ static int ufs_qcom_config_esi(struct ufs_hba *hba) nr_irqs = hba->nr_hw_queues - hba->nr_queues[HCTX_TYPE_POLL]; ret = platform_msi_domain_alloc_irqs(hba->dev, nr_irqs, ufs_qcom_write_msi_msg); - if (ret) + if (ret) { + dev_err(hba->dev, "Failed to request Platform MSI %d\n", ret); goto out; + } + msi_lock_descs(hba->dev); msi_for_each_desc(desc, hba->dev, MSI_DESC_ALL) { - if (!desc->msi_index) - host->esi_base = desc->irq; - ret = devm_request_irq(hba->dev, desc->irq, ufs_qcom_mcq_esi_handler, - IRQF_SHARED, "qcom-mcq-esi", hba); + IRQF_SHARED, "qcom-mcq-esi", desc); if (ret) { dev_err(hba->dev, "%s: Fail to request IRQ for %d, err = %d\n", __func__, desc->irq, ret); @@ -1692,14 +1825,17 @@ static int ufs_qcom_config_esi(struct ufs_hba *hba) break; } } + msi_unlock_descs(hba->dev); if (ret) { /* Rewind */ + msi_lock_descs(hba->dev); msi_for_each_desc(desc, hba->dev, MSI_DESC_ALL) { if (desc == failed_desc) break; devm_free_irq(hba->dev, desc->irq, hba); } + msi_unlock_descs(hba->dev); platform_msi_domain_free_irqs(hba->dev); } else { if (host->hw_ver.major == 6 && host->hw_ver.minor == 0 && @@ -1712,12 +1848,8 @@ static int ufs_qcom_config_esi(struct ufs_hba *hba) } out: - if (ret) { - host->esi_base = -1; - dev_warn(hba->dev, "Failed to request Platform MSI %d\n", ret); - } else { + if (!ret) host->esi_enabled = true; - } return ret; } @@ -1757,7 +1889,7 @@ static const struct ufs_hba_variant_ops ufs_hba_qcom_vops = { * ufs_qcom_probe - probe routine of the driver * @pdev: pointer to Platform device handle * - * Return zero for success and non-zero for failure + * Return: zero for success and non-zero for failure. */ static int ufs_qcom_probe(struct platform_device *pdev) { diff --git a/drivers/ufs/host/ufs-qcom.h b/drivers/ufs/host/ufs-qcom.h index 6289ad5a42d0..d6f8e74bd538 100644 --- a/drivers/ufs/host/ufs-qcom.h +++ b/drivers/ufs/host/ufs-qcom.h @@ -206,6 +206,9 @@ struct ufs_qcom_host { struct clk *tx_l1_sync_clk; bool is_lane_clks_enabled; + struct icc_path *icc_ddr; + struct icc_path *icc_cpu; + #ifdef CONFIG_SCSI_UFS_CRYPTO struct qcom_ice *ice; #endif @@ -226,7 +229,6 @@ struct ufs_qcom_host { u32 hs_gear; - int esi_base; bool esi_enabled; }; diff --git a/drivers/ufs/host/ufs-renesas.c b/drivers/ufs/host/ufs-renesas.c index f8a5e79ed3b4..cc94970b86c9 100644 --- a/drivers/ufs/host/ufs-renesas.c +++ b/drivers/ufs/host/ufs-renesas.c @@ -12,7 +12,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_device.h> +#include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <ufs/ufshcd.h> @@ -359,7 +359,7 @@ static int ufs_renesas_init(struct ufs_hba *hba) { struct ufs_renesas_priv *priv; - priv = devm_kmalloc(hba->dev, sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(hba->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; ufshcd_set_variant(hba, priv); diff --git a/drivers/ufs/host/ufshcd-dwc.c b/drivers/ufs/host/ufshcd-dwc.c index e28a67e1e314..21b1cf912dcc 100644 --- a/drivers/ufs/host/ufshcd-dwc.c +++ b/drivers/ufs/host/ufshcd-dwc.c @@ -34,9 +34,7 @@ int ufshcd_dwc_dme_set_attrs(struct ufs_hba *hba, EXPORT_SYMBOL(ufshcd_dwc_dme_set_attrs); /** - * ufshcd_dwc_program_clk_div() - * This function programs the clk divider value. This value is needed to - * provide 1 microsecond tick to unipro layer. + * ufshcd_dwc_program_clk_div() - program clock divider. * @hba: Private Structure pointer * @divider_val: clock divider value to be programmed * @@ -47,11 +45,10 @@ static void ufshcd_dwc_program_clk_div(struct ufs_hba *hba, u32 divider_val) } /** - * ufshcd_dwc_link_is_up() - * Check if link is up + * ufshcd_dwc_link_is_up() - check if link is up. * @hba: private structure pointer * - * Returns 0 on success, non-zero value on failure + * Return: 0 on success, non-zero value on failure. */ static int ufshcd_dwc_link_is_up(struct ufs_hba *hba) { @@ -68,7 +65,9 @@ static int ufshcd_dwc_link_is_up(struct ufs_hba *hba) } /** - * ufshcd_dwc_connection_setup() + * ufshcd_dwc_connection_setup() - configure unipro attributes. + * @hba: pointer to drivers private data + * * This function configures both the local side (host) and the peer side * (device) unipro attributes to establish the connection to application/ * cport. @@ -76,9 +75,7 @@ static int ufshcd_dwc_link_is_up(struct ufs_hba *hba) * have this connection setup on reset. But invoking this function does no * harm and should be fine even working with any ufs device. * - * @hba: pointer to drivers private data - * - * Returns 0 on success non-zero value on failure + * Return: 0 on success non-zero value on failure. */ static int ufshcd_dwc_connection_setup(struct ufs_hba *hba) { @@ -107,12 +104,11 @@ static int ufshcd_dwc_connection_setup(struct ufs_hba *hba) } /** - * ufshcd_dwc_link_startup_notify() - * UFS Host DWC specific link startup sequence + * ufshcd_dwc_link_startup_notify() - program clock divider. * @hba: private structure pointer * @status: Callback notify status * - * Returns 0 on success, non-zero value on failure + * Return: 0 on success, non-zero value on failure. */ int ufshcd_dwc_link_startup_notify(struct ufs_hba *hba, enum ufs_notify_change_status status) diff --git a/drivers/ufs/host/ufshcd-pci.c b/drivers/ufs/host/ufshcd-pci.c index cf3987773051..248a49e5e7f3 100644 --- a/drivers/ufs/host/ufshcd-pci.c +++ b/drivers/ufs/host/ufshcd-pci.c @@ -524,7 +524,7 @@ static void ufshcd_pci_remove(struct pci_dev *pdev) * @pdev: pointer to PCI device handle * @id: PCI device id * - * Returns 0 on success, non-zero value on failure + * Return: 0 on success, non-zero value on failure. */ static int ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) @@ -590,6 +590,7 @@ static const struct dev_pm_ops ufshcd_pci_pm_ops = { }; static const struct pci_device_id ufshcd_pci_tbl[] = { + { PCI_VENDOR_ID_REDHAT, 0x0013, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VENDOR_ID_SAMSUNG, 0xC00C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VDEVICE(INTEL, 0x9DFA), (kernel_ulong_t)&ufs_intel_cnl_hba_vops }, { PCI_VDEVICE(INTEL, 0x4B41), (kernel_ulong_t)&ufs_intel_ehl_hba_vops }, diff --git a/drivers/ufs/host/ufshcd-pltfrm.c b/drivers/ufs/host/ufshcd-pltfrm.c index 0b7430033047..797a4dfe45d9 100644 --- a/drivers/ufs/host/ufshcd-pltfrm.c +++ b/drivers/ufs/host/ufshcd-pltfrm.c @@ -166,6 +166,8 @@ EXPORT_SYMBOL_GPL(ufshcd_populate_vreg); * If any of the supplies are not defined it is assumed that they are always-on * and hence return zero. If the property is defined but parsing is failed * then return corresponding error. + * + * Return: 0 upon success; < 0 upon failure. */ static int ufshcd_parse_regulator_info(struct ufs_hba *hba) { @@ -212,7 +214,7 @@ static void ufshcd_init_lanes_per_dir(struct ufs_hba *hba) * @dev_max: pointer to device attributes * @agreed_pwr: returned agreed attributes * - * Returns 0 on success, non-zero value on failure + * Return: 0 on success, non-zero value on failure. */ int ufshcd_get_pwr_dev_param(const struct ufs_dev_params *pltfrm_param, const struct ufs_pa_layer_attr *dev_max, @@ -305,8 +307,8 @@ EXPORT_SYMBOL_GPL(ufshcd_get_pwr_dev_param); void ufshcd_init_pwr_dev_param(struct ufs_dev_params *dev_param) { *dev_param = (struct ufs_dev_params){ - .tx_lanes = 2, - .rx_lanes = 2, + .tx_lanes = UFS_LANE_2, + .rx_lanes = UFS_LANE_2, .hs_rx_gear = UFS_HS_G3, .hs_tx_gear = UFS_HS_G3, .pwm_rx_gear = UFS_PWM_G4, @@ -326,7 +328,7 @@ EXPORT_SYMBOL_GPL(ufshcd_init_pwr_dev_param); * @pdev: pointer to Platform device handle * @vops: pointer to variant ops * - * Returns 0 on success, non-zero value on failure + * Return: 0 on success, non-zero value on failure. */ int ufshcd_pltfrm_init(struct platform_device *pdev, const struct ufs_hba_variant_ops *vops) @@ -373,7 +375,8 @@ int ufshcd_pltfrm_init(struct platform_device *pdev, err = ufshcd_init(hba, mmio_base, irq); if (err) { - dev_err(dev, "Initialization failed\n"); + dev_err_probe(dev, err, "Initialization failed with error %d\n", + err); goto dealloc_host; } |