Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v3.11-rc4 289 lines 8.0 kB view raw
1/* 2 * omap-control-usb.c - The USB 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/err.h> 24#include <linux/io.h> 25#include <linux/clk.h> 26#include <linux/usb/omap_control_usb.h> 27 28static struct omap_control_usb *control_usb; 29 30/** 31 * omap_get_control_dev - returns the device pointer for this control device 32 * 33 * This API should be called to get the device pointer for this control 34 * module device. This device pointer should be used for called other 35 * exported API's in this driver. 36 * 37 * To be used by PHY driver and glue driver. 38 */ 39struct device *omap_get_control_dev(void) 40{ 41 if (!control_usb) 42 return ERR_PTR(-ENODEV); 43 44 return control_usb->dev; 45} 46EXPORT_SYMBOL_GPL(omap_get_control_dev); 47 48/** 49 * omap_control_usb3_phy_power - power on/off the serializer using control 50 * module 51 * @dev: the control module device 52 * @on: 0 to off and 1 to on based on powering on or off the PHY 53 * 54 * usb3 PHY driver should call this API to power on or off the PHY. 55 */ 56void omap_control_usb3_phy_power(struct device *dev, bool on) 57{ 58 u32 val; 59 unsigned long rate; 60 struct omap_control_usb *control_usb = dev_get_drvdata(dev); 61 62 if (control_usb->type != OMAP_CTRL_DEV_TYPE2) 63 return; 64 65 rate = clk_get_rate(control_usb->sys_clk); 66 rate = rate/1000000; 67 68 val = readl(control_usb->phy_power); 69 70 if (on) { 71 val &= ~(OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK | 72 OMAP_CTRL_USB_PWRCTL_CLK_FREQ_MASK); 73 val |= OMAP_CTRL_USB3_PHY_TX_RX_POWERON << 74 OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT; 75 val |= rate << OMAP_CTRL_USB_PWRCTL_CLK_FREQ_SHIFT; 76 } else { 77 val &= ~OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK; 78 val |= OMAP_CTRL_USB3_PHY_TX_RX_POWEROFF << 79 OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT; 80 } 81 82 writel(val, control_usb->phy_power); 83} 84EXPORT_SYMBOL_GPL(omap_control_usb3_phy_power); 85 86/** 87 * omap_control_usb_phy_power - power on/off the phy using control module reg 88 * @dev: the control module device 89 * @on: 0 or 1, based on powering on or off the PHY 90 */ 91void omap_control_usb_phy_power(struct device *dev, int on) 92{ 93 u32 val; 94 struct omap_control_usb *control_usb = dev_get_drvdata(dev); 95 96 val = readl(control_usb->dev_conf); 97 98 if (on) 99 val &= ~OMAP_CTRL_DEV_PHY_PD; 100 else 101 val |= OMAP_CTRL_DEV_PHY_PD; 102 103 writel(val, control_usb->dev_conf); 104} 105EXPORT_SYMBOL_GPL(omap_control_usb_phy_power); 106 107/** 108 * omap_control_usb_host_mode - set AVALID, VBUSVALID and ID pin in grounded 109 * @ctrl_usb: struct omap_control_usb * 110 * 111 * Writes to the mailbox register to notify the usb core that a usb 112 * device has been connected. 113 */ 114static void omap_control_usb_host_mode(struct omap_control_usb *ctrl_usb) 115{ 116 u32 val; 117 118 val = readl(ctrl_usb->otghs_control); 119 val &= ~(OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND); 120 val |= OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID; 121 writel(val, ctrl_usb->otghs_control); 122} 123 124/** 125 * omap_control_usb_device_mode - set AVALID, VBUSVALID and ID pin in high 126 * impedance 127 * @ctrl_usb: struct omap_control_usb * 128 * 129 * Writes to the mailbox register to notify the usb core that it has been 130 * connected to a usb host. 131 */ 132static void omap_control_usb_device_mode(struct omap_control_usb *ctrl_usb) 133{ 134 u32 val; 135 136 val = readl(ctrl_usb->otghs_control); 137 val &= ~OMAP_CTRL_DEV_SESSEND; 138 val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_AVALID | 139 OMAP_CTRL_DEV_VBUSVALID; 140 writel(val, ctrl_usb->otghs_control); 141} 142 143/** 144 * omap_control_usb_set_sessionend - Enable SESSIONEND and IDIG to high 145 * impedance 146 * @ctrl_usb: struct omap_control_usb * 147 * 148 * Writes to the mailbox register to notify the usb core it's now in 149 * disconnected state. 150 */ 151static void omap_control_usb_set_sessionend(struct omap_control_usb *ctrl_usb) 152{ 153 u32 val; 154 155 val = readl(ctrl_usb->otghs_control); 156 val &= ~(OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID); 157 val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND; 158 writel(val, ctrl_usb->otghs_control); 159} 160 161/** 162 * omap_control_usb_set_mode - Calls to functions to set USB in one of host mode 163 * or device mode or to denote disconnected state 164 * @dev: the control module device 165 * @mode: The mode to which usb should be configured 166 * 167 * This is an API to write to the mailbox register to notify the usb core that 168 * a usb device has been connected. 169 */ 170void omap_control_usb_set_mode(struct device *dev, 171 enum omap_control_usb_mode mode) 172{ 173 struct omap_control_usb *ctrl_usb; 174 175 if (IS_ERR(dev) || control_usb->type != OMAP_CTRL_DEV_TYPE1) 176 return; 177 178 ctrl_usb = dev_get_drvdata(dev); 179 180 switch (mode) { 181 case USB_MODE_HOST: 182 omap_control_usb_host_mode(ctrl_usb); 183 break; 184 case USB_MODE_DEVICE: 185 omap_control_usb_device_mode(ctrl_usb); 186 break; 187 case USB_MODE_DISCONNECT: 188 omap_control_usb_set_sessionend(ctrl_usb); 189 break; 190 default: 191 dev_vdbg(dev, "invalid omap control usb mode\n"); 192 } 193} 194EXPORT_SYMBOL_GPL(omap_control_usb_set_mode); 195 196static int omap_control_usb_probe(struct platform_device *pdev) 197{ 198 struct resource *res; 199 struct device_node *np = pdev->dev.of_node; 200 struct omap_control_usb_platform_data *pdata = pdev->dev.platform_data; 201 202 control_usb = devm_kzalloc(&pdev->dev, sizeof(*control_usb), 203 GFP_KERNEL); 204 if (!control_usb) { 205 dev_err(&pdev->dev, "unable to alloc memory for control usb\n"); 206 return -ENOMEM; 207 } 208 209 if (np) { 210 of_property_read_u32(np, "ti,type", &control_usb->type); 211 } else if (pdata) { 212 control_usb->type = pdata->type; 213 } else { 214 dev_err(&pdev->dev, "no pdata present\n"); 215 return -EINVAL; 216 } 217 218 control_usb->dev = &pdev->dev; 219 220 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 221 "control_dev_conf"); 222 control_usb->dev_conf = devm_ioremap_resource(&pdev->dev, res); 223 if (IS_ERR(control_usb->dev_conf)) 224 return PTR_ERR(control_usb->dev_conf); 225 226 if (control_usb->type == OMAP_CTRL_DEV_TYPE1) { 227 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 228 "otghs_control"); 229 control_usb->otghs_control = devm_ioremap_resource( 230 &pdev->dev, res); 231 if (IS_ERR(control_usb->otghs_control)) 232 return PTR_ERR(control_usb->otghs_control); 233 } 234 235 if (control_usb->type == OMAP_CTRL_DEV_TYPE2) { 236 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 237 "phy_power_usb"); 238 control_usb->phy_power = devm_ioremap_resource( 239 &pdev->dev, res); 240 if (IS_ERR(control_usb->phy_power)) 241 return PTR_ERR(control_usb->phy_power); 242 243 control_usb->sys_clk = devm_clk_get(control_usb->dev, 244 "sys_clkin"); 245 if (IS_ERR(control_usb->sys_clk)) { 246 pr_err("%s: unable to get sys_clkin\n", __func__); 247 return -EINVAL; 248 } 249 } 250 251 252 dev_set_drvdata(control_usb->dev, control_usb); 253 254 return 0; 255} 256 257#ifdef CONFIG_OF 258static const struct of_device_id omap_control_usb_id_table[] = { 259 { .compatible = "ti,omap-control-usb" }, 260 {} 261}; 262MODULE_DEVICE_TABLE(of, omap_control_usb_id_table); 263#endif 264 265static struct platform_driver omap_control_usb_driver = { 266 .probe = omap_control_usb_probe, 267 .driver = { 268 .name = "omap-control-usb", 269 .owner = THIS_MODULE, 270 .of_match_table = of_match_ptr(omap_control_usb_id_table), 271 }, 272}; 273 274static int __init omap_control_usb_init(void) 275{ 276 return platform_driver_register(&omap_control_usb_driver); 277} 278subsys_initcall(omap_control_usb_init); 279 280static void __exit omap_control_usb_exit(void) 281{ 282 platform_driver_unregister(&omap_control_usb_driver); 283} 284module_exit(omap_control_usb_exit); 285 286MODULE_ALIAS("platform: omap_control_usb"); 287MODULE_AUTHOR("Texas Instruments Inc."); 288MODULE_DESCRIPTION("OMAP Control Module USB Driver"); 289MODULE_LICENSE("GPL v2");