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

usb: omap1: OTG controller driver

Transceivers need to manage OTG controller state on OMAP1 to enable
switching between peripheral and host modes. Provide a driver for that.

Signed-off-by: Aaro Koskinen <aaro.koskinen@iki.fi>
Signed-off-by: Felipe Balbi <balbi@ti.com>

authored by

Aaro Koskinen and committed by
Felipe Balbi
449d2ba6 339e0088

+180
+10
drivers/usb/phy/Kconfig
··· 142 142 optionally control of a D+ pullup GPIO as well as a VBUS 143 143 current limit regulator. 144 144 145 + config OMAP_OTG 146 + tristate "OMAP USB OTG controller driver" 147 + depends on ARCH_OMAP_OTG && EXTCON 148 + help 149 + Enable this to support some transceivers on OMAP1 platforms. OTG 150 + controller is needed to switch between host and peripheral modes. 151 + 152 + This driver can also be built as a module. If so, the module 153 + will be called omap-otg. 154 + 145 155 config USB_ISP1301 146 156 tristate "NXP ISP1301 USB transceiver support" 147 157 depends on USB || USB_GADGET
+1
drivers/usb/phy/Makefile
··· 15 15 obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o 16 16 obj-$(CONFIG_AM335X_CONTROL_USB) += phy-am335x-control.o 17 17 obj-$(CONFIG_AM335X_PHY_USB) += phy-am335x.o 18 + obj-$(CONFIG_OMAP_OTG) += phy-omap-otg.o 18 19 obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o 19 20 obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o 20 21 obj-$(CONFIG_SAMSUNG_USB2PHY) += phy-samsung-usb2.o
+169
drivers/usb/phy/phy-omap-otg.c
··· 1 + /* 2 + * OMAP OTG controller driver 3 + * 4 + * Based on code from tahvo-usb.c and isp1301_omap.c drivers. 5 + * 6 + * Copyright (C) 2005-2006 Nokia Corporation 7 + * Copyright (C) 2004 Texas Instruments 8 + * Copyright (C) 2004 David Brownell 9 + * 10 + * This file is subject to the terms and conditions of the GNU General 11 + * Public License. See the file "COPYING" in the main directory of this 12 + * archive for more details. 13 + * 14 + * This program is distributed in the hope that it will be useful, 15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 + * GNU General Public License for more details. 18 + */ 19 + 20 + #include <linux/io.h> 21 + #include <linux/err.h> 22 + #include <linux/extcon.h> 23 + #include <linux/kernel.h> 24 + #include <linux/module.h> 25 + #include <linux/interrupt.h> 26 + #include <linux/platform_device.h> 27 + #include <linux/platform_data/usb-omap1.h> 28 + 29 + struct otg_device { 30 + void __iomem *base; 31 + bool id; 32 + bool vbus; 33 + struct extcon_specific_cable_nb vbus_dev; 34 + struct extcon_specific_cable_nb id_dev; 35 + struct notifier_block vbus_nb; 36 + struct notifier_block id_nb; 37 + }; 38 + 39 + #define OMAP_OTG_CTRL 0x0c 40 + #define OMAP_OTG_ASESSVLD (1 << 20) 41 + #define OMAP_OTG_BSESSEND (1 << 19) 42 + #define OMAP_OTG_BSESSVLD (1 << 18) 43 + #define OMAP_OTG_VBUSVLD (1 << 17) 44 + #define OMAP_OTG_ID (1 << 16) 45 + #define OMAP_OTG_XCEIV_OUTPUTS \ 46 + (OMAP_OTG_ASESSVLD | OMAP_OTG_BSESSEND | OMAP_OTG_BSESSVLD | \ 47 + OMAP_OTG_VBUSVLD | OMAP_OTG_ID) 48 + 49 + static void omap_otg_ctrl(struct otg_device *otg_dev, u32 outputs) 50 + { 51 + u32 l; 52 + 53 + l = readl(otg_dev->base + OMAP_OTG_CTRL); 54 + l &= ~OMAP_OTG_XCEIV_OUTPUTS; 55 + l |= outputs; 56 + writel(l, otg_dev->base + OMAP_OTG_CTRL); 57 + } 58 + 59 + static void omap_otg_set_mode(struct otg_device *otg_dev) 60 + { 61 + if (!otg_dev->id && otg_dev->vbus) 62 + /* Set B-session valid. */ 63 + omap_otg_ctrl(otg_dev, OMAP_OTG_ID | OMAP_OTG_BSESSVLD); 64 + else if (otg_dev->vbus) 65 + /* Set A-session valid. */ 66 + omap_otg_ctrl(otg_dev, OMAP_OTG_ASESSVLD); 67 + else if (!otg_dev->id) 68 + /* Set B-session end to indicate no VBUS. */ 69 + omap_otg_ctrl(otg_dev, OMAP_OTG_ID | OMAP_OTG_BSESSEND); 70 + } 71 + 72 + static int omap_otg_id_notifier(struct notifier_block *nb, 73 + unsigned long event, void *ptr) 74 + { 75 + struct otg_device *otg_dev = container_of(nb, struct otg_device, id_nb); 76 + 77 + otg_dev->id = event; 78 + omap_otg_set_mode(otg_dev); 79 + 80 + return NOTIFY_DONE; 81 + } 82 + 83 + static int omap_otg_vbus_notifier(struct notifier_block *nb, 84 + unsigned long event, void *ptr) 85 + { 86 + struct otg_device *otg_dev = container_of(nb, struct otg_device, 87 + vbus_nb); 88 + 89 + otg_dev->vbus = event; 90 + omap_otg_set_mode(otg_dev); 91 + 92 + return NOTIFY_DONE; 93 + } 94 + 95 + static int omap_otg_probe(struct platform_device *pdev) 96 + { 97 + const struct omap_usb_config *config = pdev->dev.platform_data; 98 + struct otg_device *otg_dev; 99 + struct extcon_dev *extcon; 100 + int ret; 101 + u32 rev; 102 + 103 + if (!config || !config->extcon) 104 + return -ENODEV; 105 + 106 + extcon = extcon_get_extcon_dev(config->extcon); 107 + if (!extcon) 108 + return -EPROBE_DEFER; 109 + 110 + otg_dev = devm_kzalloc(&pdev->dev, sizeof(*otg_dev), GFP_KERNEL); 111 + if (!otg_dev) 112 + return -ENOMEM; 113 + 114 + otg_dev->base = devm_ioremap_resource(&pdev->dev, &pdev->resource[0]); 115 + if (IS_ERR(otg_dev->base)) 116 + return PTR_ERR(otg_dev->base); 117 + 118 + otg_dev->id_nb.notifier_call = omap_otg_id_notifier; 119 + otg_dev->vbus_nb.notifier_call = omap_otg_vbus_notifier; 120 + 121 + ret = extcon_register_interest(&otg_dev->id_dev, config->extcon, 122 + "USB-HOST", &otg_dev->id_nb); 123 + if (ret) 124 + return ret; 125 + 126 + ret = extcon_register_interest(&otg_dev->vbus_dev, config->extcon, 127 + "USB", &otg_dev->vbus_nb); 128 + if (ret) { 129 + extcon_unregister_interest(&otg_dev->id_dev); 130 + return ret; 131 + } 132 + 133 + otg_dev->id = extcon_get_cable_state(extcon, "USB-HOST"); 134 + otg_dev->vbus = extcon_get_cable_state(extcon, "USB"); 135 + omap_otg_set_mode(otg_dev); 136 + 137 + rev = readl(otg_dev->base); 138 + 139 + dev_info(&pdev->dev, 140 + "OMAP USB OTG controller rev %d.%d (%s, id=%d, vbus=%d)\n", 141 + (rev >> 4) & 0xf, rev & 0xf, config->extcon, otg_dev->id, 142 + otg_dev->vbus); 143 + 144 + return 0; 145 + } 146 + 147 + static int omap_otg_remove(struct platform_device *pdev) 148 + { 149 + struct otg_device *otg_dev = platform_get_drvdata(pdev); 150 + 151 + extcon_unregister_interest(&otg_dev->id_dev); 152 + extcon_unregister_interest(&otg_dev->vbus_dev); 153 + 154 + return 0; 155 + } 156 + 157 + static struct platform_driver omap_otg_driver = { 158 + .probe = omap_otg_probe, 159 + .remove = omap_otg_remove, 160 + .driver = { 161 + .owner = THIS_MODULE, 162 + .name = "omap_otg", 163 + }, 164 + }; 165 + module_platform_driver(omap_otg_driver); 166 + 167 + MODULE_DESCRIPTION("OMAP USB OTG controller driver"); 168 + MODULE_LICENSE("GPL"); 169 + MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");