2 * Rockchip usb PHY driver
4 * Copyright (C) 2014 Roy Li <lyz@rock-chips.com>
5 * Copyright (C) 2014 ROCKCHIP, Inc.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
17 #include <linux/clk.h>
19 #include <linux/kernel.h>
20 #include <linux/module.h>
21 #include <linux/mutex.h>
23 #include <linux/of_address.h>
24 #include <linux/phy/phy.h>
25 #include <linux/platform_device.h>
26 #include <linux/regulator/consumer.h>
27 #include <linux/reset.h>
28 #include <linux/regmap.h>
29 #include <linux/mfd/syscon.h>
31 #define ROCKCHIP_RK3288_UOC(n) (0x320 + n * 0x14)
33 #define SIDDQ_MSK (1 << (13 + 16))
34 #define SIDDQ_ON (1 << 13)
35 #define SIDDQ_OFF (0 << 13)
44 struct rockchip_usb_phy {
45 struct regmap *reg_base;
46 unsigned int reg_offset;
51 static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy,
54 return regmap_write(phy->reg_base, phy->reg_offset,
55 SIDDQ_MSK | (siddq ? SIDDQ_ON : SIDDQ_OFF));
58 static int rockchip_usb_phy_power_off(struct phy *_phy)
60 struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
63 /* Power down usb phy analog blocks by set siddq 1*/
64 ret = rockchip_usb_phy_power(phy, 1);
68 clk_disable_unprepare(phy->clk);
75 static int rockchip_usb_phy_power_on(struct phy *_phy)
77 struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
80 ret = clk_prepare_enable(phy->clk);
84 /* Power up usb phy analog blocks by set siddq 0*/
85 ret = rockchip_usb_phy_power(phy, 0);
92 static struct phy *rockchip_usb_phy_xlate(struct device *dev,
93 struct of_phandle_args *args)
95 struct rockchip_usb_phy *phy_array = dev_get_drvdata(dev);
97 if (WARN_ON(args->args[0] == 0 || args->args[0] >= RK3288_NUM_PHYS))
98 return ERR_PTR(-ENODEV);
100 return (phy_array + args->args[0])->phy;
103 static struct phy_ops ops = {
104 .power_on = rockchip_usb_phy_power_on,
105 .power_off = rockchip_usb_phy_power_off,
106 .owner = THIS_MODULE,
109 static int rockchip_usb_phy_probe(struct platform_device *pdev)
111 struct device *dev = &pdev->dev;
112 struct rockchip_usb_phy *rk_phy;
113 struct rockchip_usb_phy *phy_array;
114 struct phy_provider *phy_provider;
119 grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
121 dev_err(&pdev->dev, "Missing rockchip,grf property\n");
125 phy_array = devm_kzalloc(dev, RK3288_NUM_PHYS * sizeof(*rk_phy),
130 for (i = 0; i < RK3288_NUM_PHYS; i++) {
131 rk_phy = &phy_array[i];
133 rk_phy->reg_base = grf;
135 rk_phy->reg_offset = ROCKCHIP_RK3288_UOC(i);
137 snprintf(clk_name, sizeof(clk_name), "usbphy%d", i);
138 rk_phy->clk = devm_clk_get(dev, clk_name);
139 if (IS_ERR(rk_phy->clk)) {
140 dev_warn(dev, "failed to get clock %s\n", clk_name);
144 rk_phy->phy = devm_phy_create(dev, NULL, &ops, NULL);
145 if (IS_ERR(rk_phy->phy)) {
146 dev_err(dev, "failed to create PHY %d\n", i);
147 return PTR_ERR(rk_phy->phy);
149 phy_set_drvdata(rk_phy->phy, rk_phy);
152 platform_set_drvdata(pdev, phy_array);
154 phy_provider = devm_of_phy_provider_register(dev,
155 rockchip_usb_phy_xlate);
156 return PTR_ERR_OR_ZERO(phy_provider);
159 static const struct of_device_id rockchip_usb_phy_dt_ids[] = {
160 { .compatible = "rockchip,rk3288-usb-phy" },
164 MODULE_DEVICE_TABLE(of, rockchip_usb_phy_dt_ids);
166 static struct platform_driver rockchip_usb_driver = {
167 .probe = rockchip_usb_phy_probe,
169 .name = "rockchip-usb-phy",
170 .owner = THIS_MODULE,
171 .of_match_table = rockchip_usb_phy_dt_ids,
175 module_platform_driver(rockchip_usb_driver);
177 MODULE_AUTHOR("Roy Li <lyz@rock-chips.com>");
178 MODULE_DESCRIPTION("Rockchip USB 2.0 PHY driver");
179 MODULE_LICENSE("GPL v2");