Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v3.11 241 lines 5.8 kB view raw
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 34int 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 65err0: 66 of_node_put(usbphy_sys); 67 return -ENXIO; 68} 69EXPORT_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 */ 76void 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} 109EXPORT_SYMBOL_GPL(samsung_usbphy_set_isolation_4210); 110 111/* 112 * Configure the mode of working of usb-phy here: HOST/DEVICE. 113 */ 114void 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} 132EXPORT_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 */ 139int 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} 148EXPORT_SYMBOL_GPL(samsung_usbphy_set_type); 149 150int 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} 173EXPORT_SYMBOL_GPL(samsung_usbphy_rate_to_clksel_64xx); 174 175int 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} 210EXPORT_SYMBOL_GPL(samsung_usbphy_rate_to_clksel_4x12); 211 212/* 213 * Returns reference clock frequency selection value 214 */ 215int 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} 241EXPORT_SYMBOL_GPL(samsung_usbphy_get_refclk_freq);