1/* 2 * Universal Flash Storage Host controller Platform bus based glue driver 3 * 4 * This code is based on drivers/scsi/ufs/ufshcd-pltfrm.c 5 * Copyright (C) 2011-2013 Samsung India Software Operations 6 * 7 * Authors: 8 * Santosh Yaraganavi <santosh.sy@samsung.com> 9 * Vinayak Holikatti <h.vinayak@samsung.com> 10 * 11 * This program is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU General Public License 13 * as published by the Free Software Foundation; either version 2 14 * of the License, or (at your option) any later version. 15 * See the COPYING file in the top-level directory or visit 16 * <http://www.gnu.org/licenses/gpl-2.0.html> 17 * 18 * This program is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * GNU General Public License for more details. 22 * 23 * This program is provided "AS IS" and "WITH ALL FAULTS" and 24 * without warranty of any kind. You are solely responsible for 25 * determining the appropriateness of using and distributing 26 * the program and assume all risks associated with your exercise 27 * of rights with respect to the program, including but not limited 28 * to infringement of third party rights, the risks and costs of 29 * program errors, damage to or loss of data, programs or equipment, 30 * and unavailability or interruption of operations. Under no 31 * circumstances will the contributor of this Program be liable for 32 * any damages of any kind arising from your use or distribution of 33 * this program. 34 */ 35 36#include <linux/platform_device.h> 37#include <linux/pm_runtime.h> 38#include <linux/of.h> 39 40#include "ufshcd.h" 41 42static const struct of_device_id ufs_of_match[]; 43static struct ufs_hba_variant_ops *get_variant_ops(struct device *dev) 44{ 45 if (dev->of_node) { 46 const struct of_device_id *match; 47 48 match = of_match_node(ufs_of_match, dev->of_node); 49 if (match) 50 return (struct ufs_hba_variant_ops *)match->data; 51 } 52 53 return NULL; 54} 55 56static int ufshcd_parse_clock_info(struct ufs_hba *hba) 57{ 58 int ret = 0; 59 int cnt; 60 int i; 61 struct device *dev = hba->dev; 62 struct device_node *np = dev->of_node; 63 char *name; 64 u32 *clkfreq = NULL; 65 struct ufs_clk_info *clki; 66 int len = 0; 67 size_t sz = 0; 68 69 if (!np) 70 goto out; 71 72 INIT_LIST_HEAD(&hba->clk_list_head); 73 74 cnt = of_property_count_strings(np, "clock-names"); 75 if (!cnt || (cnt == -EINVAL)) { 76 dev_info(dev, "%s: Unable to find clocks, assuming enabled\n", 77 __func__); 78 } else if (cnt < 0) { 79 dev_err(dev, "%s: count clock strings failed, err %d\n", 80 __func__, cnt); 81 ret = cnt; 82 } 83 84 if (cnt <= 0) 85 goto out; 86 87 if (!of_get_property(np, "freq-table-hz", &len)) { 88 dev_info(dev, "freq-table-hz property not specified\n"); 89 goto out; 90 } 91 92 if (len <= 0) 93 goto out; 94 95 sz = len / sizeof(*clkfreq); 96 if (sz != 2 * cnt) { 97 dev_err(dev, "%s len mismatch\n", "freq-table-hz"); 98 ret = -EINVAL; 99 goto out; 100 } 101 102 clkfreq = devm_kzalloc(dev, sz * sizeof(*clkfreq), 103 GFP_KERNEL); 104 if (!clkfreq) { 105 ret = -ENOMEM; 106 goto out; 107 } 108 109 ret = of_property_read_u32_array(np, "freq-table-hz", 110 clkfreq, sz); 111 if (ret && (ret != -EINVAL)) { 112 dev_err(dev, "%s: error reading array %d\n", 113 "freq-table-hz", ret); 114 return ret; 115 } 116 117 for (i = 0; i < sz; i += 2) { 118 ret = of_property_read_string_index(np, 119 "clock-names", i/2, (const char **)&name); 120 if (ret) 121 goto out; 122 123 clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL); 124 if (!clki) { 125 ret = -ENOMEM; 126 goto out; 127 } 128 129 clki->min_freq = clkfreq[i]; 130 clki->max_freq = clkfreq[i+1]; 131 clki->name = kstrdup(name, GFP_KERNEL); 132 dev_dbg(dev, "%s: min %u max %u name %s\n", "freq-table-hz", 133 clki->min_freq, clki->max_freq, clki->name); 134 list_add_tail(&clki->list, &hba->clk_list_head); 135 } 136out: 137 return ret; 138} 139 140#define MAX_PROP_SIZE 32 141static int ufshcd_populate_vreg(struct device *dev, const char *name, 142 struct ufs_vreg **out_vreg) 143{ 144 int ret = 0; 145 char prop_name[MAX_PROP_SIZE]; 146 struct ufs_vreg *vreg = NULL; 147 struct device_node *np = dev->of_node; 148 149 if (!np) { 150 dev_err(dev, "%s: non DT initialization\n", __func__); 151 goto out; 152 } 153 154 snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", name); 155 if (!of_parse_phandle(np, prop_name, 0)) { 156 dev_info(dev, "%s: Unable to find %s regulator, assuming enabled\n", 157 __func__, prop_name); 158 goto out; 159 } 160 161 vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL); 162 if (!vreg) 163 return -ENOMEM; 164 165 vreg->name = kstrdup(name, GFP_KERNEL); 166 167 /* if fixed regulator no need further initialization */ 168 snprintf(prop_name, MAX_PROP_SIZE, "%s-fixed-regulator", name); 169 if (of_property_read_bool(np, prop_name)) 170 goto out; 171 172 snprintf(prop_name, MAX_PROP_SIZE, "%s-max-microamp", name); 173 ret = of_property_read_u32(np, prop_name, &vreg->max_uA); 174 if (ret) { 175 dev_err(dev, "%s: unable to find %s err %d\n", 176 __func__, prop_name, ret); 177 goto out_free; 178 } 179 180 vreg->min_uA = 0; 181 if (!strcmp(name, "vcc")) { 182 if (of_property_read_bool(np, "vcc-supply-1p8")) { 183 vreg->min_uV = UFS_VREG_VCC_1P8_MIN_UV; 184 vreg->max_uV = UFS_VREG_VCC_1P8_MAX_UV; 185 } else { 186 vreg->min_uV = UFS_VREG_VCC_MIN_UV; 187 vreg->max_uV = UFS_VREG_VCC_MAX_UV; 188 } 189 } else if (!strcmp(name, "vccq")) { 190 vreg->min_uV = UFS_VREG_VCCQ_MIN_UV; 191 vreg->max_uV = UFS_VREG_VCCQ_MAX_UV; 192 } else if (!strcmp(name, "vccq2")) { 193 vreg->min_uV = UFS_VREG_VCCQ2_MIN_UV; 194 vreg->max_uV = UFS_VREG_VCCQ2_MAX_UV; 195 } 196 197 goto out; 198 199out_free: 200 devm_kfree(dev, vreg); 201 vreg = NULL; 202out: 203 if (!ret) 204 *out_vreg = vreg; 205 return ret; 206} 207 208/** 209 * ufshcd_parse_regulator_info - get regulator info from device tree 210 * @hba: per adapter instance 211 * 212 * Get regulator info from device tree for vcc, vccq, vccq2 power supplies. 213 * If any of the supplies are not defined it is assumed that they are always-on 214 * and hence return zero. If the property is defined but parsing is failed 215 * then return corresponding error. 216 */ 217static int ufshcd_parse_regulator_info(struct ufs_hba *hba) 218{ 219 int err; 220 struct device *dev = hba->dev; 221 struct ufs_vreg_info *info = &hba->vreg_info; 222 223 err = ufshcd_populate_vreg(dev, "vdd-hba", &info->vdd_hba); 224 if (err) 225 goto out; 226 227 err = ufshcd_populate_vreg(dev, "vcc", &info->vcc); 228 if (err) 229 goto out; 230 231 err = ufshcd_populate_vreg(dev, "vccq", &info->vccq); 232 if (err) 233 goto out; 234 235 err = ufshcd_populate_vreg(dev, "vccq2", &info->vccq2); 236out: 237 return err; 238} 239 240#ifdef CONFIG_PM 241/** 242 * ufshcd_pltfrm_suspend - suspend power management function 243 * @dev: pointer to device handle 244 * 245 * Returns 0 if successful 246 * Returns non-zero otherwise 247 */ 248static int ufshcd_pltfrm_suspend(struct device *dev) 249{ 250 return ufshcd_system_suspend(dev_get_drvdata(dev)); 251} 252 253/** 254 * ufshcd_pltfrm_resume - resume power management function 255 * @dev: pointer to device handle 256 * 257 * Returns 0 if successful 258 * Returns non-zero otherwise 259 */ 260static int ufshcd_pltfrm_resume(struct device *dev) 261{ 262 return ufshcd_system_resume(dev_get_drvdata(dev)); 263} 264 265static int ufshcd_pltfrm_runtime_suspend(struct device *dev) 266{ 267 return ufshcd_runtime_suspend(dev_get_drvdata(dev)); 268} 269static int ufshcd_pltfrm_runtime_resume(struct device *dev) 270{ 271 return ufshcd_runtime_resume(dev_get_drvdata(dev)); 272} 273static int ufshcd_pltfrm_runtime_idle(struct device *dev) 274{ 275 return ufshcd_runtime_idle(dev_get_drvdata(dev)); 276} 277#else /* !CONFIG_PM */ 278#define ufshcd_pltfrm_suspend NULL 279#define ufshcd_pltfrm_resume NULL 280#define ufshcd_pltfrm_runtime_suspend NULL 281#define ufshcd_pltfrm_runtime_resume NULL 282#define ufshcd_pltfrm_runtime_idle NULL 283#endif /* CONFIG_PM */ 284 285static void ufshcd_pltfrm_shutdown(struct platform_device *pdev) 286{ 287 ufshcd_shutdown((struct ufs_hba *)platform_get_drvdata(pdev)); 288} 289 290/** 291 * ufshcd_pltfrm_probe - probe routine of the driver 292 * @pdev: pointer to Platform device handle 293 * 294 * Returns 0 on success, non-zero value on failure 295 */ 296static int ufshcd_pltfrm_probe(struct platform_device *pdev) 297{ 298 struct ufs_hba *hba; 299 void __iomem *mmio_base; 300 struct resource *mem_res; 301 int irq, err; 302 struct device *dev = &pdev->dev; 303 304 mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 305 mmio_base = devm_ioremap_resource(dev, mem_res); 306 if (IS_ERR(*(void **)&mmio_base)) { 307 err = PTR_ERR(*(void **)&mmio_base); 308 goto out; 309 } 310 311 irq = platform_get_irq(pdev, 0); 312 if (irq < 0) { 313 dev_err(dev, "IRQ resource not available\n"); 314 err = -ENODEV; 315 goto out; 316 } 317 318 err = ufshcd_alloc_host(dev, &hba); 319 if (err) { 320 dev_err(&pdev->dev, "Allocation failed\n"); 321 goto out; 322 } 323 324 hba->vops = get_variant_ops(&pdev->dev); 325 326 err = ufshcd_parse_clock_info(hba); 327 if (err) { 328 dev_err(&pdev->dev, "%s: clock parse failed %d\n", 329 __func__, err); 330 goto out; 331 } 332 err = ufshcd_parse_regulator_info(hba); 333 if (err) { 334 dev_err(&pdev->dev, "%s: regulator init failed %d\n", 335 __func__, err); 336 goto out; 337 } 338 339 pm_runtime_set_active(&pdev->dev); 340 pm_runtime_enable(&pdev->dev); 341 342 err = ufshcd_init(hba, mmio_base, irq); 343 if (err) { 344 dev_err(dev, "Intialization failed\n"); 345 goto out_disable_rpm; 346 } 347 348 platform_set_drvdata(pdev, hba); 349 350 return 0; 351 352out_disable_rpm: 353 pm_runtime_disable(&pdev->dev); 354 pm_runtime_set_suspended(&pdev->dev); 355out: 356 return err; 357} 358 359/** 360 * ufshcd_pltfrm_remove - remove platform driver routine 361 * @pdev: pointer to platform device handle 362 * 363 * Returns 0 on success, non-zero value on failure 364 */ 365static int ufshcd_pltfrm_remove(struct platform_device *pdev) 366{ 367 struct ufs_hba *hba = platform_get_drvdata(pdev); 368 369 pm_runtime_get_sync(&(pdev)->dev); 370 ufshcd_remove(hba); 371 return 0; 372} 373 374static const struct of_device_id ufs_of_match[] = { 375 { .compatible = "jedec,ufs-1.1"}, 376 {}, 377}; 378 379static const struct dev_pm_ops ufshcd_dev_pm_ops = { 380 .suspend = ufshcd_pltfrm_suspend, 381 .resume = ufshcd_pltfrm_resume, 382 .runtime_suspend = ufshcd_pltfrm_runtime_suspend, 383 .runtime_resume = ufshcd_pltfrm_runtime_resume, 384 .runtime_idle = ufshcd_pltfrm_runtime_idle, 385}; 386 387static struct platform_driver ufshcd_pltfrm_driver = { 388 .probe = ufshcd_pltfrm_probe, 389 .remove = ufshcd_pltfrm_remove, 390 .shutdown = ufshcd_pltfrm_shutdown, 391 .driver = { 392 .name = "ufshcd", 393 .pm = &ufshcd_dev_pm_ops, 394 .of_match_table = ufs_of_match, 395 }, 396}; 397 398module_platform_driver(ufshcd_pltfrm_driver); 399 400MODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>"); 401MODULE_AUTHOR("Vinayak Holikatti <h.vinayak@samsung.com>"); 402MODULE_DESCRIPTION("UFS host controller Pltform bus based glue driver"); 403MODULE_LICENSE("GPL"); 404MODULE_VERSION(UFSHCD_DRIVER_VERSION); 405