Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Universal Flash Storage Host controller Platform bus based glue driver
0004  * Copyright (C) 2011-2013 Samsung India Software Operations
0005  *
0006  * Authors:
0007  *  Santosh Yaraganavi <santosh.sy@samsung.com>
0008  *  Vinayak Holikatti <h.vinayak@samsung.com>
0009  */
0010 
0011 #include <linux/module.h>
0012 #include <linux/platform_device.h>
0013 #include <linux/pm_runtime.h>
0014 #include <linux/of.h>
0015 
0016 #include <ufs/ufshcd.h>
0017 #include "ufshcd-pltfrm.h"
0018 #include <ufs/unipro.h>
0019 
0020 #define UFSHCD_DEFAULT_LANES_PER_DIRECTION      2
0021 
0022 static int ufshcd_parse_clock_info(struct ufs_hba *hba)
0023 {
0024     int ret = 0;
0025     int cnt;
0026     int i;
0027     struct device *dev = hba->dev;
0028     struct device_node *np = dev->of_node;
0029     const char *name;
0030     u32 *clkfreq = NULL;
0031     struct ufs_clk_info *clki;
0032     int len = 0;
0033     size_t sz = 0;
0034 
0035     if (!np)
0036         goto out;
0037 
0038     cnt = of_property_count_strings(np, "clock-names");
0039     if (!cnt || (cnt == -EINVAL)) {
0040         dev_info(dev, "%s: Unable to find clocks, assuming enabled\n",
0041                 __func__);
0042     } else if (cnt < 0) {
0043         dev_err(dev, "%s: count clock strings failed, err %d\n",
0044                 __func__, cnt);
0045         ret = cnt;
0046     }
0047 
0048     if (cnt <= 0)
0049         goto out;
0050 
0051     if (!of_get_property(np, "freq-table-hz", &len)) {
0052         dev_info(dev, "freq-table-hz property not specified\n");
0053         goto out;
0054     }
0055 
0056     if (len <= 0)
0057         goto out;
0058 
0059     sz = len / sizeof(*clkfreq);
0060     if (sz != 2 * cnt) {
0061         dev_err(dev, "%s len mismatch\n", "freq-table-hz");
0062         ret = -EINVAL;
0063         goto out;
0064     }
0065 
0066     clkfreq = devm_kcalloc(dev, sz, sizeof(*clkfreq),
0067                    GFP_KERNEL);
0068     if (!clkfreq) {
0069         ret = -ENOMEM;
0070         goto out;
0071     }
0072 
0073     ret = of_property_read_u32_array(np, "freq-table-hz",
0074             clkfreq, sz);
0075     if (ret && (ret != -EINVAL)) {
0076         dev_err(dev, "%s: error reading array %d\n",
0077                 "freq-table-hz", ret);
0078         return ret;
0079     }
0080 
0081     for (i = 0; i < sz; i += 2) {
0082         ret = of_property_read_string_index(np, "clock-names", i/2,
0083                             &name);
0084         if (ret)
0085             goto out;
0086 
0087         clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL);
0088         if (!clki) {
0089             ret = -ENOMEM;
0090             goto out;
0091         }
0092 
0093         clki->min_freq = clkfreq[i];
0094         clki->max_freq = clkfreq[i+1];
0095         clki->name = devm_kstrdup(dev, name, GFP_KERNEL);
0096         if (!clki->name) {
0097             ret = -ENOMEM;
0098             goto out;
0099         }
0100 
0101         if (!strcmp(name, "ref_clk"))
0102             clki->keep_link_active = true;
0103         dev_dbg(dev, "%s: min %u max %u name %s\n", "freq-table-hz",
0104                 clki->min_freq, clki->max_freq, clki->name);
0105         list_add_tail(&clki->list, &hba->clk_list_head);
0106     }
0107 out:
0108     return ret;
0109 }
0110 
0111 static bool phandle_exists(const struct device_node *np,
0112                const char *phandle_name, int index)
0113 {
0114     struct device_node *parse_np = of_parse_phandle(np, phandle_name, index);
0115 
0116     if (parse_np)
0117         of_node_put(parse_np);
0118 
0119     return parse_np != NULL;
0120 }
0121 
0122 #define MAX_PROP_SIZE 32
0123 int ufshcd_populate_vreg(struct device *dev, const char *name,
0124              struct ufs_vreg **out_vreg)
0125 {
0126     char prop_name[MAX_PROP_SIZE];
0127     struct ufs_vreg *vreg = NULL;
0128     struct device_node *np = dev->of_node;
0129 
0130     if (!np) {
0131         dev_err(dev, "%s: non DT initialization\n", __func__);
0132         goto out;
0133     }
0134 
0135     snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", name);
0136     if (!phandle_exists(np, prop_name, 0)) {
0137         dev_info(dev, "%s: Unable to find %s regulator, assuming enabled\n",
0138                 __func__, prop_name);
0139         goto out;
0140     }
0141 
0142     vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
0143     if (!vreg)
0144         return -ENOMEM;
0145 
0146     vreg->name = devm_kstrdup(dev, name, GFP_KERNEL);
0147     if (!vreg->name)
0148         return -ENOMEM;
0149 
0150     snprintf(prop_name, MAX_PROP_SIZE, "%s-max-microamp", name);
0151     if (of_property_read_u32(np, prop_name, &vreg->max_uA)) {
0152         dev_info(dev, "%s: unable to find %s\n", __func__, prop_name);
0153         vreg->max_uA = 0;
0154     }
0155 out:
0156     *out_vreg = vreg;
0157     return 0;
0158 }
0159 EXPORT_SYMBOL_GPL(ufshcd_populate_vreg);
0160 
0161 /**
0162  * ufshcd_parse_regulator_info - get regulator info from device tree
0163  * @hba: per adapter instance
0164  *
0165  * Get regulator info from device tree for vcc, vccq, vccq2 power supplies.
0166  * If any of the supplies are not defined it is assumed that they are always-on
0167  * and hence return zero. If the property is defined but parsing is failed
0168  * then return corresponding error.
0169  */
0170 static int ufshcd_parse_regulator_info(struct ufs_hba *hba)
0171 {
0172     int err;
0173     struct device *dev = hba->dev;
0174     struct ufs_vreg_info *info = &hba->vreg_info;
0175 
0176     err = ufshcd_populate_vreg(dev, "vdd-hba", &info->vdd_hba);
0177     if (err)
0178         goto out;
0179 
0180     err = ufshcd_populate_vreg(dev, "vcc", &info->vcc);
0181     if (err)
0182         goto out;
0183 
0184     err = ufshcd_populate_vreg(dev, "vccq", &info->vccq);
0185     if (err)
0186         goto out;
0187 
0188     err = ufshcd_populate_vreg(dev, "vccq2", &info->vccq2);
0189 out:
0190     return err;
0191 }
0192 
0193 void ufshcd_pltfrm_shutdown(struct platform_device *pdev)
0194 {
0195     ufshcd_shutdown((struct ufs_hba *)platform_get_drvdata(pdev));
0196 }
0197 EXPORT_SYMBOL_GPL(ufshcd_pltfrm_shutdown);
0198 
0199 static void ufshcd_init_lanes_per_dir(struct ufs_hba *hba)
0200 {
0201     struct device *dev = hba->dev;
0202     int ret;
0203 
0204     ret = of_property_read_u32(dev->of_node, "lanes-per-direction",
0205         &hba->lanes_per_direction);
0206     if (ret) {
0207         dev_dbg(hba->dev,
0208             "%s: failed to read lanes-per-direction, ret=%d\n",
0209             __func__, ret);
0210         hba->lanes_per_direction = UFSHCD_DEFAULT_LANES_PER_DIRECTION;
0211     }
0212 }
0213 
0214 /**
0215  * ufshcd_get_pwr_dev_param - get finally agreed attributes for
0216  *                            power mode change
0217  * @pltfrm_param: pointer to platform parameters
0218  * @dev_max: pointer to device attributes
0219  * @agreed_pwr: returned agreed attributes
0220  *
0221  * Returns 0 on success, non-zero value on failure
0222  */
0223 int ufshcd_get_pwr_dev_param(const struct ufs_dev_params *pltfrm_param,
0224                  const struct ufs_pa_layer_attr *dev_max,
0225                  struct ufs_pa_layer_attr *agreed_pwr)
0226 {
0227     int min_pltfrm_gear;
0228     int min_dev_gear;
0229     bool is_dev_sup_hs = false;
0230     bool is_pltfrm_max_hs = false;
0231 
0232     if (dev_max->pwr_rx == FAST_MODE)
0233         is_dev_sup_hs = true;
0234 
0235     if (pltfrm_param->desired_working_mode == UFS_HS_MODE) {
0236         is_pltfrm_max_hs = true;
0237         min_pltfrm_gear = min_t(u32, pltfrm_param->hs_rx_gear,
0238                     pltfrm_param->hs_tx_gear);
0239     } else {
0240         min_pltfrm_gear = min_t(u32, pltfrm_param->pwm_rx_gear,
0241                     pltfrm_param->pwm_tx_gear);
0242     }
0243 
0244     /*
0245      * device doesn't support HS but
0246      * pltfrm_param->desired_working_mode is HS,
0247      * thus device and pltfrm_param don't agree
0248      */
0249     if (!is_dev_sup_hs && is_pltfrm_max_hs) {
0250         pr_info("%s: device doesn't support HS\n",
0251             __func__);
0252         return -ENOTSUPP;
0253     } else if (is_dev_sup_hs && is_pltfrm_max_hs) {
0254         /*
0255          * since device supports HS, it supports FAST_MODE.
0256          * since pltfrm_param->desired_working_mode is also HS
0257          * then final decision (FAST/FASTAUTO) is done according
0258          * to pltfrm_params as it is the restricting factor
0259          */
0260         agreed_pwr->pwr_rx = pltfrm_param->rx_pwr_hs;
0261         agreed_pwr->pwr_tx = agreed_pwr->pwr_rx;
0262     } else {
0263         /*
0264          * here pltfrm_param->desired_working_mode is PWM.
0265          * it doesn't matter whether device supports HS or PWM,
0266          * in both cases pltfrm_param->desired_working_mode will
0267          * determine the mode
0268          */
0269         agreed_pwr->pwr_rx = pltfrm_param->rx_pwr_pwm;
0270         agreed_pwr->pwr_tx = agreed_pwr->pwr_rx;
0271     }
0272 
0273     /*
0274      * we would like tx to work in the minimum number of lanes
0275      * between device capability and vendor preferences.
0276      * the same decision will be made for rx
0277      */
0278     agreed_pwr->lane_tx = min_t(u32, dev_max->lane_tx,
0279                     pltfrm_param->tx_lanes);
0280     agreed_pwr->lane_rx = min_t(u32, dev_max->lane_rx,
0281                     pltfrm_param->rx_lanes);
0282 
0283     /* device maximum gear is the minimum between device rx and tx gears */
0284     min_dev_gear = min_t(u32, dev_max->gear_rx, dev_max->gear_tx);
0285 
0286     /*
0287      * if both device capabilities and vendor pre-defined preferences are
0288      * both HS or both PWM then set the minimum gear to be the chosen
0289      * working gear.
0290      * if one is PWM and one is HS then the one that is PWM get to decide
0291      * what is the gear, as it is the one that also decided previously what
0292      * pwr the device will be configured to.
0293      */
0294     if ((is_dev_sup_hs && is_pltfrm_max_hs) ||
0295         (!is_dev_sup_hs && !is_pltfrm_max_hs)) {
0296         agreed_pwr->gear_rx =
0297             min_t(u32, min_dev_gear, min_pltfrm_gear);
0298     } else if (!is_dev_sup_hs) {
0299         agreed_pwr->gear_rx = min_dev_gear;
0300     } else {
0301         agreed_pwr->gear_rx = min_pltfrm_gear;
0302     }
0303     agreed_pwr->gear_tx = agreed_pwr->gear_rx;
0304 
0305     agreed_pwr->hs_rate = pltfrm_param->hs_rate;
0306 
0307     return 0;
0308 }
0309 EXPORT_SYMBOL_GPL(ufshcd_get_pwr_dev_param);
0310 
0311 void ufshcd_init_pwr_dev_param(struct ufs_dev_params *dev_param)
0312 {
0313     *dev_param = (struct ufs_dev_params){
0314         .tx_lanes = 2,
0315         .rx_lanes = 2,
0316         .hs_rx_gear = UFS_HS_G3,
0317         .hs_tx_gear = UFS_HS_G3,
0318         .pwm_rx_gear = UFS_PWM_G4,
0319         .pwm_tx_gear = UFS_PWM_G4,
0320         .rx_pwr_pwm = SLOW_MODE,
0321         .tx_pwr_pwm = SLOW_MODE,
0322         .rx_pwr_hs = FAST_MODE,
0323         .tx_pwr_hs = FAST_MODE,
0324         .hs_rate = PA_HS_MODE_B,
0325         .desired_working_mode = UFS_HS_MODE,
0326     };
0327 }
0328 EXPORT_SYMBOL_GPL(ufshcd_init_pwr_dev_param);
0329 
0330 /**
0331  * ufshcd_pltfrm_init - probe routine of the driver
0332  * @pdev: pointer to Platform device handle
0333  * @vops: pointer to variant ops
0334  *
0335  * Returns 0 on success, non-zero value on failure
0336  */
0337 int ufshcd_pltfrm_init(struct platform_device *pdev,
0338                const struct ufs_hba_variant_ops *vops)
0339 {
0340     struct ufs_hba *hba;
0341     void __iomem *mmio_base;
0342     int irq, err;
0343     struct device *dev = &pdev->dev;
0344 
0345     mmio_base = devm_platform_ioremap_resource(pdev, 0);
0346     if (IS_ERR(mmio_base)) {
0347         err = PTR_ERR(mmio_base);
0348         goto out;
0349     }
0350 
0351     irq = platform_get_irq(pdev, 0);
0352     if (irq < 0) {
0353         err = irq;
0354         goto out;
0355     }
0356 
0357     err = ufshcd_alloc_host(dev, &hba);
0358     if (err) {
0359         dev_err(dev, "Allocation failed\n");
0360         goto out;
0361     }
0362 
0363     hba->vops = vops;
0364 
0365     err = ufshcd_parse_clock_info(hba);
0366     if (err) {
0367         dev_err(dev, "%s: clock parse failed %d\n",
0368                 __func__, err);
0369         goto dealloc_host;
0370     }
0371     err = ufshcd_parse_regulator_info(hba);
0372     if (err) {
0373         dev_err(dev, "%s: regulator init failed %d\n",
0374                 __func__, err);
0375         goto dealloc_host;
0376     }
0377 
0378     ufshcd_init_lanes_per_dir(hba);
0379 
0380     err = ufshcd_init(hba, mmio_base, irq);
0381     if (err) {
0382         dev_err(dev, "Initialization failed\n");
0383         goto dealloc_host;
0384     }
0385 
0386     pm_runtime_set_active(dev);
0387     pm_runtime_enable(dev);
0388 
0389     return 0;
0390 
0391 dealloc_host:
0392     ufshcd_dealloc_host(hba);
0393 out:
0394     return err;
0395 }
0396 EXPORT_SYMBOL_GPL(ufshcd_pltfrm_init);
0397 
0398 MODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>");
0399 MODULE_AUTHOR("Vinayak Holikatti <h.vinayak@samsung.com>");
0400 MODULE_DESCRIPTION("UFS host controller Platform bus based glue driver");
0401 MODULE_LICENSE("GPL");