linux/drivers/scsi/ufs/cdns-pltfrm.c
<<
>>
Prefs
   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 */
  29static 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 * Sets clocks used by the controller
  66 * @hba: host controller instance
  67 * @on: if true, enable clocks, otherwise disable
  68 * @status: notify stage (pre, post change)
  69 *
  70 * Return zero for success and non-zero for failure
  71 */
  72static int cdns_ufs_setup_clocks(struct ufs_hba *hba, bool on,
  73                                 enum ufs_notify_change_status status)
  74{
  75        if ((!on) || (status == PRE_CHANGE))
  76                return 0;
  77
  78        return cdns_ufs_set_hclkdiv(hba);
  79}
  80
  81/**
  82 * cdns_ufs_init - performs additional ufs initialization
  83 * @hba: host controller instance
  84 *
  85 * Returns status of initialization
  86 */
  87static int cdns_ufs_init(struct ufs_hba *hba)
  88{
  89        int status = 0;
  90
  91        if (hba->vops && hba->vops->phy_initialization)
  92                status = hba->vops->phy_initialization(hba);
  93
  94        return status;
  95}
  96
  97/**
  98 * cdns_ufs_m31_16nm_phy_initialization - performs m31 phy initialization
  99 * @hba: host controller instance
 100 *
 101 * Always returns 0
 102 */
 103static int cdns_ufs_m31_16nm_phy_initialization(struct ufs_hba *hba)
 104{
 105        u32 data;
 106
 107        /* Increase RX_Advanced_Min_ActivateTime_Capability */
 108        data = ufshcd_readl(hba, CDNS_UFS_REG_PHY_XCFGD1);
 109        data |= BIT(24);
 110        ufshcd_writel(hba, data, CDNS_UFS_REG_PHY_XCFGD1);
 111
 112        return 0;
 113}
 114
 115static const struct ufs_hba_variant_ops cdns_ufs_pltfm_hba_vops = {
 116        .name = "cdns-ufs-pltfm",
 117        .setup_clocks = cdns_ufs_setup_clocks,
 118};
 119
 120static const struct ufs_hba_variant_ops cdns_ufs_m31_16nm_pltfm_hba_vops = {
 121        .name = "cdns-ufs-pltfm",
 122        .init = cdns_ufs_init,
 123        .setup_clocks = cdns_ufs_setup_clocks,
 124        .phy_initialization = cdns_ufs_m31_16nm_phy_initialization,
 125};
 126
 127static const struct of_device_id cdns_ufs_of_match[] = {
 128        {
 129                .compatible = "cdns,ufshc",
 130                .data =  &cdns_ufs_pltfm_hba_vops,
 131        },
 132        {
 133                .compatible = "cdns,ufshc-m31-16nm",
 134                .data =  &cdns_ufs_m31_16nm_pltfm_hba_vops,
 135        },
 136        { },
 137};
 138
 139MODULE_DEVICE_TABLE(of, cdns_ufs_of_match);
 140
 141/**
 142 * cdns_ufs_pltfrm_probe - probe routine of the driver
 143 * @pdev: pointer to platform device handle
 144 *
 145 * Return zero for success and non-zero for failure
 146 */
 147static int cdns_ufs_pltfrm_probe(struct platform_device *pdev)
 148{
 149        int err;
 150        const struct of_device_id *of_id;
 151        struct ufs_hba_variant_ops *vops;
 152        struct device *dev = &pdev->dev;
 153
 154        of_id = of_match_node(cdns_ufs_of_match, dev->of_node);
 155        vops = (struct ufs_hba_variant_ops *)of_id->data;
 156
 157        /* Perform generic probe */
 158        err = ufshcd_pltfrm_init(pdev, vops);
 159        if (err)
 160                dev_err(dev, "ufshcd_pltfrm_init() failed %d\n", err);
 161
 162        return err;
 163}
 164
 165/**
 166 * cdns_ufs_pltfrm_remove - removes the ufs driver
 167 * @pdev: pointer to platform device handle
 168 *
 169 * Always returns 0
 170 */
 171static int cdns_ufs_pltfrm_remove(struct platform_device *pdev)
 172{
 173        struct ufs_hba *hba =  platform_get_drvdata(pdev);
 174
 175        ufshcd_remove(hba);
 176        return 0;
 177}
 178
 179static const struct dev_pm_ops cdns_ufs_dev_pm_ops = {
 180        .suspend         = ufshcd_pltfrm_suspend,
 181        .resume          = ufshcd_pltfrm_resume,
 182        .runtime_suspend = ufshcd_pltfrm_runtime_suspend,
 183        .runtime_resume  = ufshcd_pltfrm_runtime_resume,
 184        .runtime_idle    = ufshcd_pltfrm_runtime_idle,
 185};
 186
 187static struct platform_driver cdns_ufs_pltfrm_driver = {
 188        .probe  = cdns_ufs_pltfrm_probe,
 189        .remove = cdns_ufs_pltfrm_remove,
 190        .driver = {
 191                .name   = "cdns-ufshcd",
 192                .pm     = &cdns_ufs_dev_pm_ops,
 193                .of_match_table = cdns_ufs_of_match,
 194        },
 195};
 196
 197module_platform_driver(cdns_ufs_pltfrm_driver);
 198
 199MODULE_AUTHOR("Jan Kotas <jank@cadence.com>");
 200MODULE_DESCRIPTION("Cadence UFS host controller platform driver");
 201MODULE_LICENSE("GPL v2");
 202MODULE_VERSION(UFSHCD_DRIVER_VERSION);
 203