]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/usb/phy/phy-samsung-usb.c
Merge v3.12-rc1 into kbuild/for-next
[karo-tx-linux.git] / drivers / usb / phy / phy-samsung-usb.c
1 /* linux/drivers/usb/phy/phy-samsung-usb.c
2  *
3  * Copyright (c) 2012 Samsung Electronics Co., Ltd.
4  *              http://www.samsung.com
5  *
6  * Author: Praveen Paneri <p.paneri@samsung.com>
7  *
8  * Samsung USB-PHY helper driver with common function calls;
9  * interacts with Samsung USB 2.0 PHY controller driver and later
10  * with Samsung USB 3.0 PHY driver.
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License version 2 as
14  * published by the Free Software Foundation.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  */
21
22 #include <linux/module.h>
23 #include <linux/platform_device.h>
24 #include <linux/clk.h>
25 #include <linux/device.h>
26 #include <linux/err.h>
27 #include <linux/io.h>
28 #include <linux/of.h>
29 #include <linux/of_address.h>
30 #include <linux/usb/samsung_usb_phy.h>
31
32 #include "phy-samsung-usb.h"
33
34 int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy)
35 {
36         struct device_node *usbphy_sys;
37
38         /* Getting node for system controller interface for usb-phy */
39         usbphy_sys = of_get_child_by_name(sphy->dev->of_node, "usbphy-sys");
40         if (!usbphy_sys) {
41                 dev_err(sphy->dev, "No sys-controller interface for usb-phy\n");
42                 return -ENODEV;
43         }
44
45         sphy->pmuregs = of_iomap(usbphy_sys, 0);
46
47         if (sphy->pmuregs == NULL) {
48                 dev_err(sphy->dev, "Can't get usb-phy pmu control register\n");
49                 goto err0;
50         }
51
52         sphy->sysreg = of_iomap(usbphy_sys, 1);
53
54         /*
55          * Not returning error code here, since this situation is not fatal.
56          * Few SoCs may not have this switch available
57          */
58         if (sphy->sysreg == NULL)
59                 dev_warn(sphy->dev, "Can't get usb-phy sysreg cfg register\n");
60
61         of_node_put(usbphy_sys);
62
63         return 0;
64
65 err0:
66         of_node_put(usbphy_sys);
67         return -ENXIO;
68 }
69 EXPORT_SYMBOL_GPL(samsung_usbphy_parse_dt);
70
71 /*
72  * Set isolation here for phy.
73  * Here 'on = true' would mean USB PHY block is isolated, hence
74  * de-activated and vice-versa.
75  */
76 void samsung_usbphy_set_isolation_4210(struct samsung_usbphy *sphy, bool on)
77 {
78         void __iomem *reg = NULL;
79         u32 reg_val;
80         u32 en_mask = 0;
81
82         if (!sphy->pmuregs) {
83                 dev_warn(sphy->dev, "Can't set pmu isolation\n");
84                 return;
85         }
86
87         if (sphy->phy_type == USB_PHY_TYPE_DEVICE) {
88                 reg = sphy->pmuregs + sphy->drv_data->devphy_reg_offset;
89                 en_mask = sphy->drv_data->devphy_en_mask;
90         } else if (sphy->phy_type == USB_PHY_TYPE_HOST) {
91                 reg = sphy->pmuregs + sphy->drv_data->hostphy_reg_offset;
92                 en_mask = sphy->drv_data->hostphy_en_mask;
93         }
94
95         reg_val = readl(reg);
96
97         if (on)
98                 reg_val &= ~en_mask;
99         else
100                 reg_val |= en_mask;
101
102         writel(reg_val, reg);
103
104         if (sphy->drv_data->cpu_type == TYPE_EXYNOS4X12) {
105                 writel(reg_val, sphy->pmuregs + EXYNOS4X12_PHY_HSIC_CTRL0);
106                 writel(reg_val, sphy->pmuregs + EXYNOS4X12_PHY_HSIC_CTRL1);
107         }
108 }
109 EXPORT_SYMBOL_GPL(samsung_usbphy_set_isolation_4210);
110
111 /*
112  * Configure the mode of working of usb-phy here: HOST/DEVICE.
113  */
114 void samsung_usbphy_cfg_sel(struct samsung_usbphy *sphy)
115 {
116         u32 reg;
117
118         if (!sphy->sysreg) {
119                 dev_warn(sphy->dev, "Can't configure specified phy mode\n");
120                 return;
121         }
122
123         reg = readl(sphy->sysreg);
124
125         if (sphy->phy_type == USB_PHY_TYPE_DEVICE)
126                 reg &= ~EXYNOS_USB20PHY_CFG_HOST_LINK;
127         else if (sphy->phy_type == USB_PHY_TYPE_HOST)
128                 reg |= EXYNOS_USB20PHY_CFG_HOST_LINK;
129
130         writel(reg, sphy->sysreg);
131 }
132 EXPORT_SYMBOL_GPL(samsung_usbphy_cfg_sel);
133
134 /*
135  * PHYs are different for USB Device and USB Host.
136  * This make sure that correct PHY type is selected before
137  * any operation on PHY.
138  */
139 int samsung_usbphy_set_type(struct usb_phy *phy,
140                                 enum samsung_usb_phy_type phy_type)
141 {
142         struct samsung_usbphy *sphy = phy_to_sphy(phy);
143
144         sphy->phy_type = phy_type;
145
146         return 0;
147 }
148 EXPORT_SYMBOL_GPL(samsung_usbphy_set_type);
149
150 int samsung_usbphy_rate_to_clksel_64xx(struct samsung_usbphy *sphy,
151                                                         unsigned long rate)
152 {
153         unsigned int clksel;
154
155         switch (rate) {
156         case 12 * MHZ:
157                 clksel = PHYCLK_CLKSEL_12M;
158                 break;
159         case 24 * MHZ:
160                 clksel = PHYCLK_CLKSEL_24M;
161                 break;
162         case 48 * MHZ:
163                 clksel = PHYCLK_CLKSEL_48M;
164                 break;
165         default:
166                 dev_err(sphy->dev,
167                         "Invalid reference clock frequency: %lu\n", rate);
168                 return -EINVAL;
169         }
170
171         return clksel;
172 }
173 EXPORT_SYMBOL_GPL(samsung_usbphy_rate_to_clksel_64xx);
174
175 int samsung_usbphy_rate_to_clksel_4x12(struct samsung_usbphy *sphy,
176                                                         unsigned long rate)
177 {
178         unsigned int clksel;
179
180         switch (rate) {
181         case 9600 * KHZ:
182                 clksel = FSEL_CLKSEL_9600K;
183                 break;
184         case 10 * MHZ:
185                 clksel = FSEL_CLKSEL_10M;
186                 break;
187         case 12 * MHZ:
188                 clksel = FSEL_CLKSEL_12M;
189                 break;
190         case 19200 * KHZ:
191                 clksel = FSEL_CLKSEL_19200K;
192                 break;
193         case 20 * MHZ:
194                 clksel = FSEL_CLKSEL_20M;
195                 break;
196         case 24 * MHZ:
197                 clksel = FSEL_CLKSEL_24M;
198                 break;
199         case 50 * MHZ:
200                 clksel = FSEL_CLKSEL_50M;
201                 break;
202         default:
203                 dev_err(sphy->dev,
204                         "Invalid reference clock frequency: %lu\n", rate);
205                 return -EINVAL;
206         }
207
208         return clksel;
209 }
210 EXPORT_SYMBOL_GPL(samsung_usbphy_rate_to_clksel_4x12);
211
212 /*
213  * Returns reference clock frequency selection value
214  */
215 int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy)
216 {
217         struct clk *ref_clk;
218         unsigned long rate;
219         int refclk_freq;
220
221         /*
222          * In exynos5250 USB host and device PHY use
223          * external crystal clock XXTI
224          */
225         if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250)
226                 ref_clk = clk_get(sphy->dev, "ext_xtal");
227         else
228                 ref_clk = clk_get(sphy->dev, "xusbxti");
229         if (IS_ERR(ref_clk)) {
230                 dev_err(sphy->dev, "Failed to get reference clock\n");
231                 return PTR_ERR(ref_clk);
232         }
233
234         rate = clk_get_rate(ref_clk);
235         refclk_freq = sphy->drv_data->rate_to_clksel(sphy, rate);
236
237         clk_put(ref_clk);
238
239         return refclk_freq;
240 }
241 EXPORT_SYMBOL_GPL(samsung_usbphy_get_refclk_freq);