Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.15-rc5 320 lines 8.5 kB view raw
1/* 2 * omap-control-phy.c - The PHY part of control module. 3 * 4 * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * Author: Kishon Vijay Abraham I <kishon@ti.com> 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 */ 18 19#include <linux/module.h> 20#include <linux/platform_device.h> 21#include <linux/slab.h> 22#include <linux/of.h> 23#include <linux/of_device.h> 24#include <linux/err.h> 25#include <linux/io.h> 26#include <linux/clk.h> 27#include <linux/phy/omap_control_phy.h> 28 29/** 30 * omap_control_phy_power - power on/off the phy using control module reg 31 * @dev: the control module device 32 * @on: 0 or 1, based on powering on or off the PHY 33 */ 34void omap_control_phy_power(struct device *dev, int on) 35{ 36 u32 val; 37 unsigned long rate; 38 struct omap_control_phy *control_phy; 39 40 if (IS_ERR(dev) || !dev) { 41 pr_err("%s: invalid device\n", __func__); 42 return; 43 } 44 45 control_phy = dev_get_drvdata(dev); 46 if (!control_phy) { 47 dev_err(dev, "%s: invalid control phy device\n", __func__); 48 return; 49 } 50 51 if (control_phy->type == OMAP_CTRL_TYPE_OTGHS) 52 return; 53 54 val = readl(control_phy->power); 55 56 switch (control_phy->type) { 57 case OMAP_CTRL_TYPE_USB2: 58 if (on) 59 val &= ~OMAP_CTRL_DEV_PHY_PD; 60 else 61 val |= OMAP_CTRL_DEV_PHY_PD; 62 break; 63 64 case OMAP_CTRL_TYPE_PIPE3: 65 rate = clk_get_rate(control_phy->sys_clk); 66 rate = rate/1000000; 67 68 if (on) { 69 val &= ~(OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK | 70 OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK); 71 val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON << 72 OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; 73 val |= rate << 74 OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT; 75 } else { 76 val &= ~OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK; 77 val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF << 78 OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; 79 } 80 break; 81 82 case OMAP_CTRL_TYPE_DRA7USB2: 83 if (on) 84 val &= ~OMAP_CTRL_USB2_PHY_PD; 85 else 86 val |= OMAP_CTRL_USB2_PHY_PD; 87 break; 88 89 case OMAP_CTRL_TYPE_AM437USB2: 90 if (on) { 91 val &= ~(AM437X_CTRL_USB2_PHY_PD | 92 AM437X_CTRL_USB2_OTG_PD); 93 val |= (AM437X_CTRL_USB2_OTGVDET_EN | 94 AM437X_CTRL_USB2_OTGSESSEND_EN); 95 } else { 96 val &= ~(AM437X_CTRL_USB2_OTGVDET_EN | 97 AM437X_CTRL_USB2_OTGSESSEND_EN); 98 val |= (AM437X_CTRL_USB2_PHY_PD | 99 AM437X_CTRL_USB2_OTG_PD); 100 } 101 break; 102 default: 103 dev_err(dev, "%s: type %d not recognized\n", 104 __func__, control_phy->type); 105 break; 106 } 107 108 writel(val, control_phy->power); 109} 110EXPORT_SYMBOL_GPL(omap_control_phy_power); 111 112/** 113 * omap_control_usb_host_mode - set AVALID, VBUSVALID and ID pin in grounded 114 * @ctrl_phy: struct omap_control_phy * 115 * 116 * Writes to the mailbox register to notify the usb core that a usb 117 * device has been connected. 118 */ 119static void omap_control_usb_host_mode(struct omap_control_phy *ctrl_phy) 120{ 121 u32 val; 122 123 val = readl(ctrl_phy->otghs_control); 124 val &= ~(OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND); 125 val |= OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID; 126 writel(val, ctrl_phy->otghs_control); 127} 128 129/** 130 * omap_control_usb_device_mode - set AVALID, VBUSVALID and ID pin in high 131 * impedance 132 * @ctrl_phy: struct omap_control_phy * 133 * 134 * Writes to the mailbox register to notify the usb core that it has been 135 * connected to a usb host. 136 */ 137static void omap_control_usb_device_mode(struct omap_control_phy *ctrl_phy) 138{ 139 u32 val; 140 141 val = readl(ctrl_phy->otghs_control); 142 val &= ~OMAP_CTRL_DEV_SESSEND; 143 val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_AVALID | 144 OMAP_CTRL_DEV_VBUSVALID; 145 writel(val, ctrl_phy->otghs_control); 146} 147 148/** 149 * omap_control_usb_set_sessionend - Enable SESSIONEND and IDIG to high 150 * impedance 151 * @ctrl_phy: struct omap_control_phy * 152 * 153 * Writes to the mailbox register to notify the usb core it's now in 154 * disconnected state. 155 */ 156static void omap_control_usb_set_sessionend(struct omap_control_phy *ctrl_phy) 157{ 158 u32 val; 159 160 val = readl(ctrl_phy->otghs_control); 161 val &= ~(OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID); 162 val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND; 163 writel(val, ctrl_phy->otghs_control); 164} 165 166/** 167 * omap_control_usb_set_mode - Calls to functions to set USB in one of host mode 168 * or device mode or to denote disconnected state 169 * @dev: the control module device 170 * @mode: The mode to which usb should be configured 171 * 172 * This is an API to write to the mailbox register to notify the usb core that 173 * a usb device has been connected. 174 */ 175void omap_control_usb_set_mode(struct device *dev, 176 enum omap_control_usb_mode mode) 177{ 178 struct omap_control_phy *ctrl_phy; 179 180 if (IS_ERR(dev) || !dev) 181 return; 182 183 ctrl_phy = dev_get_drvdata(dev); 184 185 if (!ctrl_phy) { 186 dev_err(dev, "Invalid control phy device\n"); 187 return; 188 } 189 190 if (ctrl_phy->type != OMAP_CTRL_TYPE_OTGHS) 191 return; 192 193 switch (mode) { 194 case USB_MODE_HOST: 195 omap_control_usb_host_mode(ctrl_phy); 196 break; 197 case USB_MODE_DEVICE: 198 omap_control_usb_device_mode(ctrl_phy); 199 break; 200 case USB_MODE_DISCONNECT: 201 omap_control_usb_set_sessionend(ctrl_phy); 202 break; 203 default: 204 dev_vdbg(dev, "invalid omap control usb mode\n"); 205 } 206} 207EXPORT_SYMBOL_GPL(omap_control_usb_set_mode); 208 209#ifdef CONFIG_OF 210 211static const enum omap_control_phy_type otghs_data = OMAP_CTRL_TYPE_OTGHS; 212static const enum omap_control_phy_type usb2_data = OMAP_CTRL_TYPE_USB2; 213static const enum omap_control_phy_type pipe3_data = OMAP_CTRL_TYPE_PIPE3; 214static const enum omap_control_phy_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2; 215static const enum omap_control_phy_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2; 216 217static const struct of_device_id omap_control_phy_id_table[] = { 218 { 219 .compatible = "ti,control-phy-otghs", 220 .data = &otghs_data, 221 }, 222 { 223 .compatible = "ti,control-phy-usb2", 224 .data = &usb2_data, 225 }, 226 { 227 .compatible = "ti,control-phy-pipe3", 228 .data = &pipe3_data, 229 }, 230 { 231 .compatible = "ti,control-phy-usb2-dra7", 232 .data = &dra7usb2_data, 233 }, 234 { 235 .compatible = "ti,control-phy-usb2-am437", 236 .data = &am437usb2_data, 237 }, 238 {}, 239}; 240MODULE_DEVICE_TABLE(of, omap_control_phy_id_table); 241#endif 242 243 244static int omap_control_phy_probe(struct platform_device *pdev) 245{ 246 struct resource *res; 247 const struct of_device_id *of_id; 248 struct omap_control_phy *control_phy; 249 250 of_id = of_match_device(of_match_ptr(omap_control_phy_id_table), 251 &pdev->dev); 252 if (!of_id) 253 return -EINVAL; 254 255 control_phy = devm_kzalloc(&pdev->dev, sizeof(*control_phy), 256 GFP_KERNEL); 257 if (!control_phy) { 258 dev_err(&pdev->dev, "unable to alloc memory for control phy\n"); 259 return -ENOMEM; 260 } 261 262 control_phy->dev = &pdev->dev; 263 control_phy->type = *(enum omap_control_phy_type *)of_id->data; 264 265 if (control_phy->type == OMAP_CTRL_TYPE_OTGHS) { 266 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 267 "otghs_control"); 268 control_phy->otghs_control = devm_ioremap_resource( 269 &pdev->dev, res); 270 if (IS_ERR(control_phy->otghs_control)) 271 return PTR_ERR(control_phy->otghs_control); 272 } else { 273 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 274 "power"); 275 control_phy->power = devm_ioremap_resource(&pdev->dev, res); 276 if (IS_ERR(control_phy->power)) { 277 dev_err(&pdev->dev, "Couldn't get power register\n"); 278 return PTR_ERR(control_phy->power); 279 } 280 } 281 282 if (control_phy->type == OMAP_CTRL_TYPE_PIPE3) { 283 control_phy->sys_clk = devm_clk_get(control_phy->dev, 284 "sys_clkin"); 285 if (IS_ERR(control_phy->sys_clk)) { 286 pr_err("%s: unable to get sys_clkin\n", __func__); 287 return -EINVAL; 288 } 289 } 290 291 dev_set_drvdata(control_phy->dev, control_phy); 292 293 return 0; 294} 295 296static struct platform_driver omap_control_phy_driver = { 297 .probe = omap_control_phy_probe, 298 .driver = { 299 .name = "omap-control-phy", 300 .owner = THIS_MODULE, 301 .of_match_table = of_match_ptr(omap_control_phy_id_table), 302 }, 303}; 304 305static int __init omap_control_phy_init(void) 306{ 307 return platform_driver_register(&omap_control_phy_driver); 308} 309subsys_initcall(omap_control_phy_init); 310 311static void __exit omap_control_phy_exit(void) 312{ 313 platform_driver_unregister(&omap_control_phy_driver); 314} 315module_exit(omap_control_phy_exit); 316 317MODULE_ALIAS("platform: omap_control_phy"); 318MODULE_AUTHOR("Texas Instruments Inc."); 319MODULE_DESCRIPTION("OMAP Control Module PHY Driver"); 320MODULE_LICENSE("GPL v2");