linux/drivers/usb/dwc3/dwc3-exynos.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/**
   3 * dwc3-exynos.c - Samsung EXYNOS DWC3 Specific Glue layer
   4 *
   5 * Copyright (c) 2012 Samsung Electronics Co., Ltd.
   6 *              http://www.samsung.com
   7 *
   8 * Author: Anton Tikhomirov <av.tikhomirov@samsung.com>
   9 */
  10
  11#include <linux/module.h>
  12#include <linux/kernel.h>
  13#include <linux/slab.h>
  14#include <linux/platform_device.h>
  15#include <linux/clk.h>
  16#include <linux/of.h>
  17#include <linux/of_platform.h>
  18#include <linux/regulator/consumer.h>
  19
  20#define DWC3_EXYNOS_MAX_CLOCKS  4
  21
  22struct dwc3_exynos_driverdata {
  23        const char              *clk_names[DWC3_EXYNOS_MAX_CLOCKS];
  24        int                     num_clks;
  25        int                     suspend_clk_idx;
  26};
  27
  28struct dwc3_exynos {
  29        struct device           *dev;
  30
  31        const char              **clk_names;
  32        struct clk              *clks[DWC3_EXYNOS_MAX_CLOCKS];
  33        int                     num_clks;
  34        int                     suspend_clk_idx;
  35
  36        struct regulator        *vdd33;
  37        struct regulator        *vdd10;
  38};
  39
  40static int dwc3_exynos_remove_child(struct device *dev, void *unused)
  41{
  42        struct platform_device *pdev = to_platform_device(dev);
  43
  44        platform_device_unregister(pdev);
  45
  46        return 0;
  47}
  48
  49static int dwc3_exynos_probe(struct platform_device *pdev)
  50{
  51        struct dwc3_exynos      *exynos;
  52        struct device           *dev = &pdev->dev;
  53        struct device_node      *node = dev->of_node;
  54        const struct dwc3_exynos_driverdata *driver_data;
  55        int                     i, ret;
  56
  57        exynos = devm_kzalloc(dev, sizeof(*exynos), GFP_KERNEL);
  58        if (!exynos)
  59                return -ENOMEM;
  60
  61        driver_data = of_device_get_match_data(dev);
  62        exynos->dev = dev;
  63        exynos->num_clks = driver_data->num_clks;
  64        exynos->clk_names = (const char **)driver_data->clk_names;
  65        exynos->suspend_clk_idx = driver_data->suspend_clk_idx;
  66
  67        platform_set_drvdata(pdev, exynos);
  68
  69        for (i = 0; i < exynos->num_clks; i++) {
  70                exynos->clks[i] = devm_clk_get(dev, exynos->clk_names[i]);
  71                if (IS_ERR(exynos->clks[i])) {
  72                        dev_err(dev, "failed to get clock: %s\n",
  73                                exynos->clk_names[i]);
  74                        return PTR_ERR(exynos->clks[i]);
  75                }
  76        }
  77
  78        for (i = 0; i < exynos->num_clks; i++) {
  79                ret = clk_prepare_enable(exynos->clks[i]);
  80                if (ret) {
  81                        while (i-- > 0)
  82                                clk_disable_unprepare(exynos->clks[i]);
  83                        return ret;
  84                }
  85        }
  86
  87        if (exynos->suspend_clk_idx >= 0)
  88                clk_prepare_enable(exynos->clks[exynos->suspend_clk_idx]);
  89
  90        exynos->vdd33 = devm_regulator_get(dev, "vdd33");
  91        if (IS_ERR(exynos->vdd33)) {
  92                ret = PTR_ERR(exynos->vdd33);
  93                goto vdd33_err;
  94        }
  95        ret = regulator_enable(exynos->vdd33);
  96        if (ret) {
  97                dev_err(dev, "Failed to enable VDD33 supply\n");
  98                goto vdd33_err;
  99        }
 100
 101        exynos->vdd10 = devm_regulator_get(dev, "vdd10");
 102        if (IS_ERR(exynos->vdd10)) {
 103                ret = PTR_ERR(exynos->vdd10);
 104                goto vdd10_err;
 105        }
 106        ret = regulator_enable(exynos->vdd10);
 107        if (ret) {
 108                dev_err(dev, "Failed to enable VDD10 supply\n");
 109                goto vdd10_err;
 110        }
 111
 112        if (node) {
 113                ret = of_platform_populate(node, NULL, NULL, dev);
 114                if (ret) {
 115                        dev_err(dev, "failed to add dwc3 core\n");
 116                        goto populate_err;
 117                }
 118        } else {
 119                dev_err(dev, "no device node, failed to add dwc3 core\n");
 120                ret = -ENODEV;
 121                goto populate_err;
 122        }
 123
 124        return 0;
 125
 126populate_err:
 127        regulator_disable(exynos->vdd10);
 128vdd10_err:
 129        regulator_disable(exynos->vdd33);
 130vdd33_err:
 131        for (i = exynos->num_clks - 1; i >= 0; i--)
 132                clk_disable_unprepare(exynos->clks[i]);
 133
 134        if (exynos->suspend_clk_idx >= 0)
 135                clk_disable_unprepare(exynos->clks[exynos->suspend_clk_idx]);
 136
 137        return ret;
 138}
 139
 140static int dwc3_exynos_remove(struct platform_device *pdev)
 141{
 142        struct dwc3_exynos      *exynos = platform_get_drvdata(pdev);
 143        int i;
 144
 145        device_for_each_child(&pdev->dev, NULL, dwc3_exynos_remove_child);
 146
 147        for (i = exynos->num_clks - 1; i >= 0; i--)
 148                clk_disable_unprepare(exynos->clks[i]);
 149
 150        if (exynos->suspend_clk_idx >= 0)
 151                clk_disable_unprepare(exynos->clks[exynos->suspend_clk_idx]);
 152
 153        regulator_disable(exynos->vdd33);
 154        regulator_disable(exynos->vdd10);
 155
 156        return 0;
 157}
 158
 159static const struct dwc3_exynos_driverdata exynos5250_drvdata = {
 160        .clk_names = { "usbdrd30" },
 161        .num_clks = 1,
 162        .suspend_clk_idx = -1,
 163};
 164
 165static const struct dwc3_exynos_driverdata exynos5433_drvdata = {
 166        .clk_names = { "aclk", "susp_clk", "pipe_pclk", "phyclk" },
 167        .num_clks = 4,
 168        .suspend_clk_idx = 1,
 169};
 170
 171static const struct dwc3_exynos_driverdata exynos7_drvdata = {
 172        .clk_names = { "usbdrd30", "usbdrd30_susp_clk", "usbdrd30_axius_clk" },
 173        .num_clks = 3,
 174        .suspend_clk_idx = 1,
 175};
 176
 177static const struct of_device_id exynos_dwc3_match[] = {
 178        {
 179                .compatible = "samsung,exynos5250-dwusb3",
 180                .data = &exynos5250_drvdata,
 181        }, {
 182                .compatible = "samsung,exynos5433-dwusb3",
 183                .data = &exynos5433_drvdata,
 184        }, {
 185                .compatible = "samsung,exynos7-dwusb3",
 186                .data = &exynos7_drvdata,
 187        }, {
 188        }
 189};
 190MODULE_DEVICE_TABLE(of, exynos_dwc3_match);
 191
 192#ifdef CONFIG_PM_SLEEP
 193static int dwc3_exynos_suspend(struct device *dev)
 194{
 195        struct dwc3_exynos *exynos = dev_get_drvdata(dev);
 196        int i;
 197
 198        for (i = exynos->num_clks - 1; i >= 0; i--)
 199                clk_disable_unprepare(exynos->clks[i]);
 200
 201        regulator_disable(exynos->vdd33);
 202        regulator_disable(exynos->vdd10);
 203
 204        return 0;
 205}
 206
 207static int dwc3_exynos_resume(struct device *dev)
 208{
 209        struct dwc3_exynos *exynos = dev_get_drvdata(dev);
 210        int i, ret;
 211
 212        ret = regulator_enable(exynos->vdd33);
 213        if (ret) {
 214                dev_err(dev, "Failed to enable VDD33 supply\n");
 215                return ret;
 216        }
 217        ret = regulator_enable(exynos->vdd10);
 218        if (ret) {
 219                dev_err(dev, "Failed to enable VDD10 supply\n");
 220                return ret;
 221        }
 222
 223        for (i = 0; i < exynos->num_clks; i++) {
 224                ret = clk_prepare_enable(exynos->clks[i]);
 225                if (ret) {
 226                        while (i-- > 0)
 227                                clk_disable_unprepare(exynos->clks[i]);
 228                        return ret;
 229                }
 230        }
 231
 232        return 0;
 233}
 234
 235static const struct dev_pm_ops dwc3_exynos_dev_pm_ops = {
 236        SET_SYSTEM_SLEEP_PM_OPS(dwc3_exynos_suspend, dwc3_exynos_resume)
 237};
 238
 239#define DEV_PM_OPS      (&dwc3_exynos_dev_pm_ops)
 240#else
 241#define DEV_PM_OPS      NULL
 242#endif /* CONFIG_PM_SLEEP */
 243
 244static struct platform_driver dwc3_exynos_driver = {
 245        .probe          = dwc3_exynos_probe,
 246        .remove         = dwc3_exynos_remove,
 247        .driver         = {
 248                .name   = "exynos-dwc3",
 249                .of_match_table = exynos_dwc3_match,
 250                .pm     = DEV_PM_OPS,
 251        },
 252};
 253
 254module_platform_driver(dwc3_exynos_driver);
 255
 256MODULE_AUTHOR("Anton Tikhomirov <av.tikhomirov@samsung.com>");
 257MODULE_LICENSE("GPL v2");
 258MODULE_DESCRIPTION("DesignWare USB3 EXYNOS Glue Layer");
 259