root/drivers/scsi/ufs/cdns-pltfrm.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cdns_ufs_set_hclkdiv
  2. cdns_ufs_hce_enable_notify
  3. cdns_ufs_link_startup_notify
  4. cdns_ufs_init
  5. cdns_ufs_m31_16nm_phy_initialization
  6. cdns_ufs_pltfrm_probe
  7. cdns_ufs_pltfrm_remove

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Platform UFS Host driver for Cadence controller
   4  *
   5  * Copyright (C) 2018 Cadence Design Systems, Inc.
   6  *
   7  * Authors:
   8  *      Jan Kotas <jank@cadence.com>
   9  *
  10  */
  11 
  12 #include <linux/kernel.h>
  13 #include <linux/module.h>
  14 #include <linux/platform_device.h>
  15 #include <linux/of.h>
  16 #include <linux/time.h>
  17 
  18 #include "ufshcd-pltfrm.h"
  19 
  20 #define CDNS_UFS_REG_HCLKDIV    0xFC
  21 #define CDNS_UFS_REG_PHY_XCFGD1 0x113C
  22 
  23 /**
  24  * Sets HCLKDIV register value based on the core_clk
  25  * @hba: host controller instance
  26  *
  27  * Return zero for success and non-zero for failure
  28  */
  29 static int cdns_ufs_set_hclkdiv(struct ufs_hba *hba)
  30 {
  31         struct ufs_clk_info *clki;
  32         struct list_head *head = &hba->clk_list_head;
  33         unsigned long core_clk_rate = 0;
  34         u32 core_clk_div = 0;
  35 
  36         if (list_empty(head))
  37                 return 0;
  38 
  39         list_for_each_entry(clki, head, list) {
  40                 if (IS_ERR_OR_NULL(clki->clk))
  41                         continue;
  42                 if (!strcmp(clki->name, "core_clk"))
  43                         core_clk_rate = clk_get_rate(clki->clk);
  44         }
  45 
  46         if (!core_clk_rate) {
  47                 dev_err(hba->dev, "%s: unable to find core_clk rate\n",
  48                         __func__);
  49                 return -EINVAL;
  50         }
  51 
  52         core_clk_div = core_clk_rate / USEC_PER_SEC;
  53 
  54         ufshcd_writel(hba, core_clk_div, CDNS_UFS_REG_HCLKDIV);
  55         /**
  56          * Make sure the register was updated,
  57          * UniPro layer will not work with an incorrect value.
  58          */
  59         mb();
  60 
  61         return 0;
  62 }
  63 
  64 /**
  65  * Called before and after HCE enable bit is set.
  66  * @hba: host controller instance
  67  * @status: notify stage (pre, post change)
  68  *
  69  * Return zero for success and non-zero for failure
  70  */
  71 static int cdns_ufs_hce_enable_notify(struct ufs_hba *hba,
  72                                       enum ufs_notify_change_status status)
  73 {
  74         if (status != PRE_CHANGE)
  75                 return 0;
  76 
  77         return cdns_ufs_set_hclkdiv(hba);
  78 }
  79 
  80 /**
  81  * Called before and after Link startup is carried out.
  82  * @hba: host controller instance
  83  * @status: notify stage (pre, post change)
  84  *
  85  * Return zero for success and non-zero for failure
  86  */
  87 static int cdns_ufs_link_startup_notify(struct ufs_hba *hba,
  88                                         enum ufs_notify_change_status status)
  89 {
  90         if (status != PRE_CHANGE)
  91                 return 0;
  92 
  93         /*
  94          * Some UFS devices have issues if LCC is enabled.
  95          * So we are setting PA_Local_TX_LCC_Enable to 0
  96          * before link startup which will make sure that both host
  97          * and device TX LCC are disabled once link startup is
  98          * completed.
  99          */
 100         ufshcd_dme_set(hba, UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE), 0);
 101 
 102         /*
 103          * Disabling Autohibern8 feature in cadence UFS
 104          * to mask unexpected interrupt trigger.
 105          */
 106         hba->ahit = 0;
 107 
 108         return 0;
 109 }
 110 
 111 /**
 112  * cdns_ufs_init - performs additional ufs initialization
 113  * @hba: host controller instance
 114  *
 115  * Returns status of initialization
 116  */
 117 static int cdns_ufs_init(struct ufs_hba *hba)
 118 {
 119         int status = 0;
 120 
 121         if (hba->vops && hba->vops->phy_initialization)
 122                 status = hba->vops->phy_initialization(hba);
 123 
 124         return status;
 125 }
 126 
 127 /**
 128  * cdns_ufs_m31_16nm_phy_initialization - performs m31 phy initialization
 129  * @hba: host controller instance
 130  *
 131  * Always returns 0
 132  */
 133 static int cdns_ufs_m31_16nm_phy_initialization(struct ufs_hba *hba)
 134 {
 135         u32 data;
 136 
 137         /* Increase RX_Advanced_Min_ActivateTime_Capability */
 138         data = ufshcd_readl(hba, CDNS_UFS_REG_PHY_XCFGD1);
 139         data |= BIT(24);
 140         ufshcd_writel(hba, data, CDNS_UFS_REG_PHY_XCFGD1);
 141 
 142         return 0;
 143 }
 144 
 145 static const struct ufs_hba_variant_ops cdns_ufs_pltfm_hba_vops = {
 146         .name = "cdns-ufs-pltfm",
 147         .hce_enable_notify = cdns_ufs_hce_enable_notify,
 148         .link_startup_notify = cdns_ufs_link_startup_notify,
 149 };
 150 
 151 static const struct ufs_hba_variant_ops cdns_ufs_m31_16nm_pltfm_hba_vops = {
 152         .name = "cdns-ufs-pltfm",
 153         .init = cdns_ufs_init,
 154         .hce_enable_notify = cdns_ufs_hce_enable_notify,
 155         .link_startup_notify = cdns_ufs_link_startup_notify,
 156         .phy_initialization = cdns_ufs_m31_16nm_phy_initialization,
 157 };
 158 
 159 static const struct of_device_id cdns_ufs_of_match[] = {
 160         {
 161                 .compatible = "cdns,ufshc",
 162                 .data =  &cdns_ufs_pltfm_hba_vops,
 163         },
 164         {
 165                 .compatible = "cdns,ufshc-m31-16nm",
 166                 .data =  &cdns_ufs_m31_16nm_pltfm_hba_vops,
 167         },
 168         { },
 169 };
 170 
 171 MODULE_DEVICE_TABLE(of, cdns_ufs_of_match);
 172 
 173 /**
 174  * cdns_ufs_pltfrm_probe - probe routine of the driver
 175  * @pdev: pointer to platform device handle
 176  *
 177  * Return zero for success and non-zero for failure
 178  */
 179 static int cdns_ufs_pltfrm_probe(struct platform_device *pdev)
 180 {
 181         int err;
 182         const struct of_device_id *of_id;
 183         struct ufs_hba_variant_ops *vops;
 184         struct device *dev = &pdev->dev;
 185 
 186         of_id = of_match_node(cdns_ufs_of_match, dev->of_node);
 187         vops = (struct ufs_hba_variant_ops *)of_id->data;
 188 
 189         /* Perform generic probe */
 190         err = ufshcd_pltfrm_init(pdev, vops);
 191         if (err)
 192                 dev_err(dev, "ufshcd_pltfrm_init() failed %d\n", err);
 193 
 194         return err;
 195 }
 196 
 197 /**
 198  * cdns_ufs_pltfrm_remove - removes the ufs driver
 199  * @pdev: pointer to platform device handle
 200  *
 201  * Always returns 0
 202  */
 203 static int cdns_ufs_pltfrm_remove(struct platform_device *pdev)
 204 {
 205         struct ufs_hba *hba =  platform_get_drvdata(pdev);
 206 
 207         ufshcd_remove(hba);
 208         return 0;
 209 }
 210 
 211 static const struct dev_pm_ops cdns_ufs_dev_pm_ops = {
 212         .suspend         = ufshcd_pltfrm_suspend,
 213         .resume          = ufshcd_pltfrm_resume,
 214         .runtime_suspend = ufshcd_pltfrm_runtime_suspend,
 215         .runtime_resume  = ufshcd_pltfrm_runtime_resume,
 216         .runtime_idle    = ufshcd_pltfrm_runtime_idle,
 217 };
 218 
 219 static struct platform_driver cdns_ufs_pltfrm_driver = {
 220         .probe  = cdns_ufs_pltfrm_probe,
 221         .remove = cdns_ufs_pltfrm_remove,
 222         .driver = {
 223                 .name   = "cdns-ufshcd",
 224                 .pm     = &cdns_ufs_dev_pm_ops,
 225                 .of_match_table = cdns_ufs_of_match,
 226         },
 227 };
 228 
 229 module_platform_driver(cdns_ufs_pltfrm_driver);
 230 
 231 MODULE_AUTHOR("Jan Kotas <jank@cadence.com>");
 232 MODULE_DESCRIPTION("Cadence UFS host controller platform driver");
 233 MODULE_LICENSE("GPL v2");
 234 MODULE_VERSION(UFSHCD_DRIVER_VERSION);

/* [<][>][^][v][top][bottom][index][help] */