1efe7daf2SSergei Shtylyov /* 2efe7daf2SSergei Shtylyov * OHCI HCD (Host Controller Driver) for USB. 3efe7daf2SSergei Shtylyov * 4efe7daf2SSergei Shtylyov * TI DA8xx (OMAP-L1x) Bus Glue 5efe7daf2SSergei Shtylyov * 6efe7daf2SSergei Shtylyov * Derived from: ohci-omap.c and ohci-s3c2410.c 7efe7daf2SSergei Shtylyov * Copyright (C) 2008-2009 MontaVista Software, Inc. <source@mvista.com> 8efe7daf2SSergei Shtylyov * 9efe7daf2SSergei Shtylyov * This file is licensed under the terms of the GNU General Public License 10efe7daf2SSergei Shtylyov * version 2. This program is licensed "as is" without any warranty of any 11efe7daf2SSergei Shtylyov * kind, whether express or implied. 12efe7daf2SSergei Shtylyov */ 13efe7daf2SSergei Shtylyov 146c21caa3SManjunath Goudar #include <linux/clk.h> 156c21caa3SManjunath Goudar #include <linux/io.h> 16efe7daf2SSergei Shtylyov #include <linux/interrupt.h> 17efe7daf2SSergei Shtylyov #include <linux/jiffies.h> 186c21caa3SManjunath Goudar #include <linux/kernel.h> 196c21caa3SManjunath Goudar #include <linux/module.h> 20efe7daf2SSergei Shtylyov #include <linux/platform_device.h> 216110c425SDavid Lechner #include <linux/phy/phy.h> 22ec2a0833SArnd Bergmann #include <linux/platform_data/usb-davinci.h> 23*c844ff74SAxel Haslam #include <linux/regulator/consumer.h> 246c21caa3SManjunath Goudar #include <linux/usb.h> 256c21caa3SManjunath Goudar #include <linux/usb/hcd.h> 266c21caa3SManjunath Goudar #include <asm/unaligned.h> 27efe7daf2SSergei Shtylyov 286c21caa3SManjunath Goudar #include "ohci.h" 296c21caa3SManjunath Goudar 306c21caa3SManjunath Goudar #define DRIVER_DESC "DA8XX" 31eacae5d2SAxel Haslam #define DRV_NAME "ohci-da8xx" 326c21caa3SManjunath Goudar 336c21caa3SManjunath Goudar static struct hc_driver __read_mostly ohci_da8xx_hc_driver; 346c21caa3SManjunath Goudar 356c21caa3SManjunath Goudar static int (*orig_ohci_hub_control)(struct usb_hcd *hcd, u16 typeReq, 366c21caa3SManjunath Goudar u16 wValue, u16 wIndex, char *buf, u16 wLength); 376c21caa3SManjunath Goudar static int (*orig_ohci_hub_status_data)(struct usb_hcd *hcd, char *buf); 38efe7daf2SSergei Shtylyov 39c7a4f9f3SAxel Haslam struct da8xx_ohci_hcd { 40*c844ff74SAxel Haslam struct usb_hcd *hcd; 41c7a4f9f3SAxel Haslam struct clk *usb11_clk; 42c7a4f9f3SAxel Haslam struct phy *usb11_phy; 43*c844ff74SAxel Haslam struct regulator *vbus_reg; 44*c844ff74SAxel Haslam struct notifier_block nb; 45*c844ff74SAxel Haslam unsigned int reg_enabled; 46c7a4f9f3SAxel Haslam }; 47c7a4f9f3SAxel Haslam 48c7a4f9f3SAxel Haslam #define to_da8xx_ohci(hcd) (struct da8xx_ohci_hcd *)(hcd_to_ohci(hcd)->priv) 49efe7daf2SSergei Shtylyov 50efe7daf2SSergei Shtylyov /* Over-current indicator change bitmask */ 51efe7daf2SSergei Shtylyov static volatile u16 ocic_mask; 52efe7daf2SSergei Shtylyov 53c7a4f9f3SAxel Haslam static int ohci_da8xx_enable(struct usb_hcd *hcd) 54efe7daf2SSergei Shtylyov { 55c7a4f9f3SAxel Haslam struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); 566110c425SDavid Lechner int ret; 57efe7daf2SSergei Shtylyov 58c7a4f9f3SAxel Haslam ret = clk_prepare_enable(da8xx_ohci->usb11_clk); 596110c425SDavid Lechner if (ret) 606110c425SDavid Lechner return ret; 61efe7daf2SSergei Shtylyov 62c7a4f9f3SAxel Haslam ret = phy_init(da8xx_ohci->usb11_phy); 636110c425SDavid Lechner if (ret) 646110c425SDavid Lechner goto err_phy_init; 65efe7daf2SSergei Shtylyov 66c7a4f9f3SAxel Haslam ret = phy_power_on(da8xx_ohci->usb11_phy); 676110c425SDavid Lechner if (ret) 686110c425SDavid Lechner goto err_phy_power_on; 69efe7daf2SSergei Shtylyov 706110c425SDavid Lechner return 0; 716110c425SDavid Lechner 726110c425SDavid Lechner err_phy_power_on: 73c7a4f9f3SAxel Haslam phy_exit(da8xx_ohci->usb11_phy); 746110c425SDavid Lechner err_phy_init: 75c7a4f9f3SAxel Haslam clk_disable_unprepare(da8xx_ohci->usb11_clk); 766110c425SDavid Lechner 776110c425SDavid Lechner return ret; 78efe7daf2SSergei Shtylyov } 79efe7daf2SSergei Shtylyov 80c7a4f9f3SAxel Haslam static void ohci_da8xx_disable(struct usb_hcd *hcd) 816110c425SDavid Lechner { 82c7a4f9f3SAxel Haslam struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); 83c7a4f9f3SAxel Haslam 84c7a4f9f3SAxel Haslam phy_power_off(da8xx_ohci->usb11_phy); 85c7a4f9f3SAxel Haslam phy_exit(da8xx_ohci->usb11_phy); 86c7a4f9f3SAxel Haslam clk_disable_unprepare(da8xx_ohci->usb11_clk); 87efe7daf2SSergei Shtylyov } 88efe7daf2SSergei Shtylyov 89f3c56fb3SAxel Haslam static int ohci_da8xx_set_power(struct usb_hcd *hcd, int on) 90f3c56fb3SAxel Haslam { 91*c844ff74SAxel Haslam struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); 92f3c56fb3SAxel Haslam struct device *dev = hcd->self.controller; 93f3c56fb3SAxel Haslam struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); 94*c844ff74SAxel Haslam int ret; 95f3c56fb3SAxel Haslam 96f3c56fb3SAxel Haslam if (hub && hub->set_power) 97f3c56fb3SAxel Haslam return hub->set_power(1, on); 98f3c56fb3SAxel Haslam 99*c844ff74SAxel Haslam if (!da8xx_ohci->vbus_reg) 100*c844ff74SAxel Haslam return 0; 101*c844ff74SAxel Haslam 102*c844ff74SAxel Haslam if (on && !da8xx_ohci->reg_enabled) { 103*c844ff74SAxel Haslam ret = regulator_enable(da8xx_ohci->vbus_reg); 104*c844ff74SAxel Haslam if (ret) { 105*c844ff74SAxel Haslam dev_err(dev, "Failed to enable regulator: %d\n", ret); 106*c844ff74SAxel Haslam return ret; 107*c844ff74SAxel Haslam } 108*c844ff74SAxel Haslam da8xx_ohci->reg_enabled = 1; 109*c844ff74SAxel Haslam 110*c844ff74SAxel Haslam } else if (!on && da8xx_ohci->reg_enabled) { 111*c844ff74SAxel Haslam ret = regulator_disable(da8xx_ohci->vbus_reg); 112*c844ff74SAxel Haslam if (ret) { 113*c844ff74SAxel Haslam dev_err(dev, "Failed to disable regulator: %d\n", ret); 114*c844ff74SAxel Haslam return ret; 115*c844ff74SAxel Haslam } 116*c844ff74SAxel Haslam da8xx_ohci->reg_enabled = 0; 117*c844ff74SAxel Haslam } 118*c844ff74SAxel Haslam 119f3c56fb3SAxel Haslam return 0; 120f3c56fb3SAxel Haslam } 121f3c56fb3SAxel Haslam 122f3c56fb3SAxel Haslam static int ohci_da8xx_get_power(struct usb_hcd *hcd) 123f3c56fb3SAxel Haslam { 124*c844ff74SAxel Haslam struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); 125f3c56fb3SAxel Haslam struct device *dev = hcd->self.controller; 126f3c56fb3SAxel Haslam struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); 127f3c56fb3SAxel Haslam 128f3c56fb3SAxel Haslam if (hub && hub->get_power) 129f3c56fb3SAxel Haslam return hub->get_power(1); 130f3c56fb3SAxel Haslam 131*c844ff74SAxel Haslam if (da8xx_ohci->vbus_reg) 132*c844ff74SAxel Haslam return regulator_is_enabled(da8xx_ohci->vbus_reg); 133*c844ff74SAxel Haslam 134f3c56fb3SAxel Haslam return 1; 135f3c56fb3SAxel Haslam } 136f3c56fb3SAxel Haslam 137f3c56fb3SAxel Haslam static int ohci_da8xx_get_oci(struct usb_hcd *hcd) 138f3c56fb3SAxel Haslam { 139*c844ff74SAxel Haslam struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); 140f3c56fb3SAxel Haslam struct device *dev = hcd->self.controller; 141f3c56fb3SAxel Haslam struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); 142*c844ff74SAxel Haslam unsigned int flags; 143*c844ff74SAxel Haslam int ret; 144f3c56fb3SAxel Haslam 145f3c56fb3SAxel Haslam if (hub && hub->get_oci) 146f3c56fb3SAxel Haslam return hub->get_oci(1); 147f3c56fb3SAxel Haslam 148*c844ff74SAxel Haslam if (!da8xx_ohci->vbus_reg) 149*c844ff74SAxel Haslam return 0; 150*c844ff74SAxel Haslam 151*c844ff74SAxel Haslam ret = regulator_get_error_flags(da8xx_ohci->vbus_reg, &flags); 152*c844ff74SAxel Haslam if (ret) 153*c844ff74SAxel Haslam return ret; 154*c844ff74SAxel Haslam 155*c844ff74SAxel Haslam if (flags & REGULATOR_ERROR_OVER_CURRENT) 156*c844ff74SAxel Haslam return 1; 157*c844ff74SAxel Haslam 158f3c56fb3SAxel Haslam return 0; 159f3c56fb3SAxel Haslam } 160f3c56fb3SAxel Haslam 161f3c56fb3SAxel Haslam static int ohci_da8xx_has_set_power(struct usb_hcd *hcd) 162f3c56fb3SAxel Haslam { 163*c844ff74SAxel Haslam struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); 164f3c56fb3SAxel Haslam struct device *dev = hcd->self.controller; 165f3c56fb3SAxel Haslam struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); 166f3c56fb3SAxel Haslam 167f3c56fb3SAxel Haslam if (hub && hub->set_power) 168f3c56fb3SAxel Haslam return 1; 169f3c56fb3SAxel Haslam 170*c844ff74SAxel Haslam if (da8xx_ohci->vbus_reg) 171*c844ff74SAxel Haslam return 1; 172*c844ff74SAxel Haslam 173f3c56fb3SAxel Haslam return 0; 174f3c56fb3SAxel Haslam } 175f3c56fb3SAxel Haslam 176f3c56fb3SAxel Haslam static int ohci_da8xx_has_oci(struct usb_hcd *hcd) 177f3c56fb3SAxel Haslam { 178*c844ff74SAxel Haslam struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); 179f3c56fb3SAxel Haslam struct device *dev = hcd->self.controller; 180f3c56fb3SAxel Haslam struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); 181f3c56fb3SAxel Haslam 182f3c56fb3SAxel Haslam if (hub && hub->get_oci) 183f3c56fb3SAxel Haslam return 1; 184f3c56fb3SAxel Haslam 185*c844ff74SAxel Haslam if (da8xx_ohci->vbus_reg) 186*c844ff74SAxel Haslam return 1; 187*c844ff74SAxel Haslam 188f3c56fb3SAxel Haslam return 0; 189f3c56fb3SAxel Haslam } 190f3c56fb3SAxel Haslam 191f3c56fb3SAxel Haslam static int ohci_da8xx_has_potpgt(struct usb_hcd *hcd) 192f3c56fb3SAxel Haslam { 193f3c56fb3SAxel Haslam struct device *dev = hcd->self.controller; 194f3c56fb3SAxel Haslam struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); 195f3c56fb3SAxel Haslam 196f3c56fb3SAxel Haslam if (hub && hub->potpgt) 197f3c56fb3SAxel Haslam return 1; 198f3c56fb3SAxel Haslam 199f3c56fb3SAxel Haslam return 0; 200f3c56fb3SAxel Haslam } 201f3c56fb3SAxel Haslam 202efe7daf2SSergei Shtylyov /* 203efe7daf2SSergei Shtylyov * Handle the port over-current indicator change. 204efe7daf2SSergei Shtylyov */ 205efe7daf2SSergei Shtylyov static void ohci_da8xx_ocic_handler(struct da8xx_ohci_root_hub *hub, 206efe7daf2SSergei Shtylyov unsigned port) 207efe7daf2SSergei Shtylyov { 208efe7daf2SSergei Shtylyov ocic_mask |= 1 << port; 209efe7daf2SSergei Shtylyov 210efe7daf2SSergei Shtylyov /* Once over-current is detected, the port needs to be powered down */ 211efe7daf2SSergei Shtylyov if (hub->get_oci(port) > 0) 212efe7daf2SSergei Shtylyov hub->set_power(port, 0); 213efe7daf2SSergei Shtylyov } 214efe7daf2SSergei Shtylyov 215*c844ff74SAxel Haslam static int ohci_da8xx_regulator_event(struct notifier_block *nb, 216*c844ff74SAxel Haslam unsigned long event, void *data) 217f3c56fb3SAxel Haslam { 218*c844ff74SAxel Haslam struct da8xx_ohci_hcd *da8xx_ohci = 219*c844ff74SAxel Haslam container_of(nb, struct da8xx_ohci_hcd, nb); 220f3c56fb3SAxel Haslam 221*c844ff74SAxel Haslam if (event & REGULATOR_EVENT_OVER_CURRENT) { 222*c844ff74SAxel Haslam ocic_mask |= 1 << 1; 223*c844ff74SAxel Haslam ohci_da8xx_set_power(da8xx_ohci->hcd, 0); 224*c844ff74SAxel Haslam } 225f3c56fb3SAxel Haslam 226f3c56fb3SAxel Haslam return 0; 227f3c56fb3SAxel Haslam } 228f3c56fb3SAxel Haslam 229*c844ff74SAxel Haslam static int ohci_da8xx_register_notify(struct usb_hcd *hcd) 230*c844ff74SAxel Haslam { 231*c844ff74SAxel Haslam struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); 232*c844ff74SAxel Haslam struct device *dev = hcd->self.controller; 233*c844ff74SAxel Haslam struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); 234*c844ff74SAxel Haslam int ret = 0; 235*c844ff74SAxel Haslam 236*c844ff74SAxel Haslam if (hub && hub->ocic_notify) { 237*c844ff74SAxel Haslam ret = hub->ocic_notify(ohci_da8xx_ocic_handler); 238*c844ff74SAxel Haslam } else if (da8xx_ohci->vbus_reg) { 239*c844ff74SAxel Haslam da8xx_ohci->nb.notifier_call = ohci_da8xx_regulator_event; 240*c844ff74SAxel Haslam ret = devm_regulator_register_notifier(da8xx_ohci->vbus_reg, 241*c844ff74SAxel Haslam &da8xx_ohci->nb); 242*c844ff74SAxel Haslam } 243*c844ff74SAxel Haslam 244*c844ff74SAxel Haslam if (ret) 245*c844ff74SAxel Haslam dev_err(dev, "Failed to register notifier: %d\n", ret); 246*c844ff74SAxel Haslam 247*c844ff74SAxel Haslam return ret; 248*c844ff74SAxel Haslam } 249*c844ff74SAxel Haslam 250f3c56fb3SAxel Haslam static void ohci_da8xx_unregister_notify(struct usb_hcd *hcd) 251f3c56fb3SAxel Haslam { 252f3c56fb3SAxel Haslam struct device *dev = hcd->self.controller; 253f3c56fb3SAxel Haslam struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); 254f3c56fb3SAxel Haslam 255f3c56fb3SAxel Haslam if (hub && hub->ocic_notify) 256f3c56fb3SAxel Haslam hub->ocic_notify(NULL); 257f3c56fb3SAxel Haslam } 258f3c56fb3SAxel Haslam 2596c21caa3SManjunath Goudar static int ohci_da8xx_reset(struct usb_hcd *hcd) 260efe7daf2SSergei Shtylyov { 261efe7daf2SSergei Shtylyov struct device *dev = hcd->self.controller; 262d4f09e28SJingoo Han struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); 263efe7daf2SSergei Shtylyov struct ohci_hcd *ohci = hcd_to_ohci(hcd); 264efe7daf2SSergei Shtylyov int result; 265efe7daf2SSergei Shtylyov u32 rh_a; 266efe7daf2SSergei Shtylyov 267efe7daf2SSergei Shtylyov dev_dbg(dev, "starting USB controller\n"); 268efe7daf2SSergei Shtylyov 269c7a4f9f3SAxel Haslam result = ohci_da8xx_enable(hcd); 2706110c425SDavid Lechner if (result < 0) 2716110c425SDavid Lechner return result; 272efe7daf2SSergei Shtylyov 273efe7daf2SSergei Shtylyov /* 274efe7daf2SSergei Shtylyov * DA8xx only have 1 port connected to the pins but the HC root hub 275efe7daf2SSergei Shtylyov * register A reports 2 ports, thus we'll have to override it... 276efe7daf2SSergei Shtylyov */ 277efe7daf2SSergei Shtylyov ohci->num_ports = 1; 278efe7daf2SSergei Shtylyov 2796c21caa3SManjunath Goudar result = ohci_setup(hcd); 2806110c425SDavid Lechner if (result < 0) { 281c7a4f9f3SAxel Haslam ohci_da8xx_disable(hcd); 282efe7daf2SSergei Shtylyov return result; 2836110c425SDavid Lechner } 284efe7daf2SSergei Shtylyov 285efe7daf2SSergei Shtylyov /* 286efe7daf2SSergei Shtylyov * Since we're providing a board-specific root hub port power control 287efe7daf2SSergei Shtylyov * and over-current reporting, we have to override the HC root hub A 288efe7daf2SSergei Shtylyov * register's default value, so that ohci_hub_control() could return 289efe7daf2SSergei Shtylyov * the correct hub descriptor... 290efe7daf2SSergei Shtylyov */ 291efe7daf2SSergei Shtylyov rh_a = ohci_readl(ohci, &ohci->regs->roothub.a); 292f3c56fb3SAxel Haslam if (ohci_da8xx_has_set_power(hcd)) { 293efe7daf2SSergei Shtylyov rh_a &= ~RH_A_NPS; 294efe7daf2SSergei Shtylyov rh_a |= RH_A_PSM; 295efe7daf2SSergei Shtylyov } 296f3c56fb3SAxel Haslam if (ohci_da8xx_has_oci(hcd)) { 297efe7daf2SSergei Shtylyov rh_a &= ~RH_A_NOCP; 298efe7daf2SSergei Shtylyov rh_a |= RH_A_OCPM; 299efe7daf2SSergei Shtylyov } 300f3c56fb3SAxel Haslam if (ohci_da8xx_has_potpgt(hcd)) { 301efe7daf2SSergei Shtylyov rh_a &= ~RH_A_POTPGT; 302efe7daf2SSergei Shtylyov rh_a |= hub->potpgt << 24; 303f3c56fb3SAxel Haslam } 304efe7daf2SSergei Shtylyov ohci_writel(ohci, rh_a, &ohci->regs->roothub.a); 305efe7daf2SSergei Shtylyov 306efe7daf2SSergei Shtylyov return result; 307efe7daf2SSergei Shtylyov } 308efe7daf2SSergei Shtylyov 309efe7daf2SSergei Shtylyov /* 310efe7daf2SSergei Shtylyov * Update the status data from the hub with the over-current indicator change. 311efe7daf2SSergei Shtylyov */ 312efe7daf2SSergei Shtylyov static int ohci_da8xx_hub_status_data(struct usb_hcd *hcd, char *buf) 313efe7daf2SSergei Shtylyov { 3146c21caa3SManjunath Goudar int length = orig_ohci_hub_status_data(hcd, buf); 315efe7daf2SSergei Shtylyov 316efe7daf2SSergei Shtylyov /* See if we have OCIC bit set on port 1 */ 317efe7daf2SSergei Shtylyov if (ocic_mask & (1 << 1)) { 318efe7daf2SSergei Shtylyov dev_dbg(hcd->self.controller, "over-current indicator change " 319efe7daf2SSergei Shtylyov "on port 1\n"); 320efe7daf2SSergei Shtylyov 321efe7daf2SSergei Shtylyov if (!length) 322efe7daf2SSergei Shtylyov length = 1; 323efe7daf2SSergei Shtylyov 324efe7daf2SSergei Shtylyov buf[0] |= 1 << 1; 325efe7daf2SSergei Shtylyov } 326efe7daf2SSergei Shtylyov return length; 327efe7daf2SSergei Shtylyov } 328efe7daf2SSergei Shtylyov 329efe7daf2SSergei Shtylyov /* 330efe7daf2SSergei Shtylyov * Look at the control requests to the root hub and see if we need to override. 331efe7daf2SSergei Shtylyov */ 332efe7daf2SSergei Shtylyov static int ohci_da8xx_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, 333efe7daf2SSergei Shtylyov u16 wIndex, char *buf, u16 wLength) 334efe7daf2SSergei Shtylyov { 335efe7daf2SSergei Shtylyov struct device *dev = hcd->self.controller; 336efe7daf2SSergei Shtylyov int temp; 337efe7daf2SSergei Shtylyov 338efe7daf2SSergei Shtylyov switch (typeReq) { 339efe7daf2SSergei Shtylyov case GetPortStatus: 340efe7daf2SSergei Shtylyov /* Check the port number */ 341efe7daf2SSergei Shtylyov if (wIndex != 1) 342efe7daf2SSergei Shtylyov break; 343efe7daf2SSergei Shtylyov 344efe7daf2SSergei Shtylyov dev_dbg(dev, "GetPortStatus(%u)\n", wIndex); 345efe7daf2SSergei Shtylyov 346efe7daf2SSergei Shtylyov temp = roothub_portstatus(hcd_to_ohci(hcd), wIndex - 1); 347efe7daf2SSergei Shtylyov 348efe7daf2SSergei Shtylyov /* The port power status (PPS) bit defaults to 1 */ 349f3c56fb3SAxel Haslam if (!ohci_da8xx_get_power(hcd)) 350efe7daf2SSergei Shtylyov temp &= ~RH_PS_PPS; 351efe7daf2SSergei Shtylyov 352efe7daf2SSergei Shtylyov /* The port over-current indicator (POCI) bit is always 0 */ 353f3c56fb3SAxel Haslam if (ohci_da8xx_get_oci(hcd) > 0) 354efe7daf2SSergei Shtylyov temp |= RH_PS_POCI; 355efe7daf2SSergei Shtylyov 356efe7daf2SSergei Shtylyov /* The over-current indicator change (OCIC) bit is 0 too */ 357efe7daf2SSergei Shtylyov if (ocic_mask & (1 << wIndex)) 358efe7daf2SSergei Shtylyov temp |= RH_PS_OCIC; 359efe7daf2SSergei Shtylyov 360efe7daf2SSergei Shtylyov put_unaligned(cpu_to_le32(temp), (__le32 *)buf); 361efe7daf2SSergei Shtylyov return 0; 362efe7daf2SSergei Shtylyov case SetPortFeature: 363efe7daf2SSergei Shtylyov temp = 1; 364efe7daf2SSergei Shtylyov goto check_port; 365efe7daf2SSergei Shtylyov case ClearPortFeature: 366efe7daf2SSergei Shtylyov temp = 0; 367efe7daf2SSergei Shtylyov 368efe7daf2SSergei Shtylyov check_port: 369efe7daf2SSergei Shtylyov /* Check the port number */ 370efe7daf2SSergei Shtylyov if (wIndex != 1) 371efe7daf2SSergei Shtylyov break; 372efe7daf2SSergei Shtylyov 373efe7daf2SSergei Shtylyov switch (wValue) { 374efe7daf2SSergei Shtylyov case USB_PORT_FEAT_POWER: 375efe7daf2SSergei Shtylyov dev_dbg(dev, "%sPortFeature(%u): %s\n", 376efe7daf2SSergei Shtylyov temp ? "Set" : "Clear", wIndex, "POWER"); 377efe7daf2SSergei Shtylyov 378f3c56fb3SAxel Haslam return ohci_da8xx_set_power(hcd, temp) ? -EPIPE : 0; 379efe7daf2SSergei Shtylyov case USB_PORT_FEAT_C_OVER_CURRENT: 380efe7daf2SSergei Shtylyov dev_dbg(dev, "%sPortFeature(%u): %s\n", 381efe7daf2SSergei Shtylyov temp ? "Set" : "Clear", wIndex, 382efe7daf2SSergei Shtylyov "C_OVER_CURRENT"); 383efe7daf2SSergei Shtylyov 384efe7daf2SSergei Shtylyov if (temp) 385efe7daf2SSergei Shtylyov ocic_mask |= 1 << wIndex; 386efe7daf2SSergei Shtylyov else 387efe7daf2SSergei Shtylyov ocic_mask &= ~(1 << wIndex); 388efe7daf2SSergei Shtylyov return 0; 389efe7daf2SSergei Shtylyov } 390efe7daf2SSergei Shtylyov } 391efe7daf2SSergei Shtylyov 3926c21caa3SManjunath Goudar return orig_ohci_hub_control(hcd, typeReq, wValue, 3936c21caa3SManjunath Goudar wIndex, buf, wLength); 394efe7daf2SSergei Shtylyov } 395efe7daf2SSergei Shtylyov 396efe7daf2SSergei Shtylyov /*-------------------------------------------------------------------------*/ 397efe7daf2SSergei Shtylyov 3986c21caa3SManjunath Goudar static int ohci_da8xx_probe(struct platform_device *pdev) 399efe7daf2SSergei Shtylyov { 400c7a4f9f3SAxel Haslam struct da8xx_ohci_hcd *da8xx_ohci; 401efe7daf2SSergei Shtylyov struct usb_hcd *hcd; 402efe7daf2SSergei Shtylyov struct resource *mem; 403efe7daf2SSergei Shtylyov int error, irq; 4046c21caa3SManjunath Goudar hcd = usb_create_hcd(&ohci_da8xx_hc_driver, &pdev->dev, 4056c21caa3SManjunath Goudar dev_name(&pdev->dev)); 406644db166SJingoo Han if (!hcd) 407644db166SJingoo Han return -ENOMEM; 408efe7daf2SSergei Shtylyov 409c7a4f9f3SAxel Haslam da8xx_ohci = to_da8xx_ohci(hcd); 410*c844ff74SAxel Haslam da8xx_ohci->hcd = hcd; 411c7a4f9f3SAxel Haslam 412c7a4f9f3SAxel Haslam da8xx_ohci->usb11_clk = devm_clk_get(&pdev->dev, "usb11"); 413c7a4f9f3SAxel Haslam if (IS_ERR(da8xx_ohci->usb11_clk)) { 414c7a4f9f3SAxel Haslam error = PTR_ERR(da8xx_ohci->usb11_clk); 415c7a4f9f3SAxel Haslam if (error != -EPROBE_DEFER) 416c7a4f9f3SAxel Haslam dev_err(&pdev->dev, "Failed to get clock.\n"); 417c7a4f9f3SAxel Haslam goto err; 418c7a4f9f3SAxel Haslam } 419c7a4f9f3SAxel Haslam 420c7a4f9f3SAxel Haslam da8xx_ohci->usb11_phy = devm_phy_get(&pdev->dev, "usb-phy"); 421c7a4f9f3SAxel Haslam if (IS_ERR(da8xx_ohci->usb11_phy)) { 422c7a4f9f3SAxel Haslam error = PTR_ERR(da8xx_ohci->usb11_phy); 423c7a4f9f3SAxel Haslam if (error != -EPROBE_DEFER) 424c7a4f9f3SAxel Haslam dev_err(&pdev->dev, "Failed to get phy.\n"); 425c7a4f9f3SAxel Haslam goto err; 426c7a4f9f3SAxel Haslam } 427c7a4f9f3SAxel Haslam 428*c844ff74SAxel Haslam da8xx_ohci->vbus_reg = devm_regulator_get_optional(&pdev->dev, "vbus"); 429*c844ff74SAxel Haslam if (IS_ERR(da8xx_ohci->vbus_reg)) { 430*c844ff74SAxel Haslam error = PTR_ERR(da8xx_ohci->vbus_reg); 431*c844ff74SAxel Haslam if (error == -ENODEV) { 432*c844ff74SAxel Haslam da8xx_ohci->vbus_reg = NULL; 433*c844ff74SAxel Haslam } else if (error == -EPROBE_DEFER) { 434*c844ff74SAxel Haslam goto err; 435*c844ff74SAxel Haslam } else { 436*c844ff74SAxel Haslam dev_err(&pdev->dev, "Failed to get regulator\n"); 437*c844ff74SAxel Haslam goto err; 438*c844ff74SAxel Haslam } 439*c844ff74SAxel Haslam } 440*c844ff74SAxel Haslam 441efe7daf2SSergei Shtylyov mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 442644db166SJingoo Han hcd->regs = devm_ioremap_resource(&pdev->dev, mem); 443644db166SJingoo Han if (IS_ERR(hcd->regs)) { 444644db166SJingoo Han error = PTR_ERR(hcd->regs); 445644db166SJingoo Han goto err; 446efe7daf2SSergei Shtylyov } 44754891d74SVarka Bhadram hcd->rsrc_start = mem->start; 44854891d74SVarka Bhadram hcd->rsrc_len = resource_size(mem); 449efe7daf2SSergei Shtylyov 450efe7daf2SSergei Shtylyov irq = platform_get_irq(pdev, 0); 451efe7daf2SSergei Shtylyov if (irq < 0) { 452efe7daf2SSergei Shtylyov error = -ENODEV; 453644db166SJingoo Han goto err; 454efe7daf2SSergei Shtylyov } 4556c21caa3SManjunath Goudar 456b5dd18d8SYong Zhang error = usb_add_hcd(hcd, irq, 0); 457efe7daf2SSergei Shtylyov if (error) 458644db166SJingoo Han goto err; 459efe7daf2SSergei Shtylyov 4603c9740a1SPeter Chen device_wakeup_enable(hcd->self.controller); 4613c9740a1SPeter Chen 462f3c56fb3SAxel Haslam error = ohci_da8xx_register_notify(hcd); 463f3c56fb3SAxel Haslam if (error) 464f3c56fb3SAxel Haslam goto err_remove_hcd; 465efe7daf2SSergei Shtylyov 466f3c56fb3SAxel Haslam return 0; 467f3c56fb3SAxel Haslam 468f3c56fb3SAxel Haslam err_remove_hcd: 469efe7daf2SSergei Shtylyov usb_remove_hcd(hcd); 470644db166SJingoo Han err: 471efe7daf2SSergei Shtylyov usb_put_hcd(hcd); 472efe7daf2SSergei Shtylyov return error; 473efe7daf2SSergei Shtylyov } 474efe7daf2SSergei Shtylyov 4756c21caa3SManjunath Goudar static int ohci_da8xx_remove(struct platform_device *pdev) 476efe7daf2SSergei Shtylyov { 4776c21caa3SManjunath Goudar struct usb_hcd *hcd = platform_get_drvdata(pdev); 478efe7daf2SSergei Shtylyov 479f3c56fb3SAxel Haslam ohci_da8xx_unregister_notify(hcd); 480efe7daf2SSergei Shtylyov usb_remove_hcd(hcd); 481efe7daf2SSergei Shtylyov usb_put_hcd(hcd); 482efe7daf2SSergei Shtylyov 483efe7daf2SSergei Shtylyov return 0; 484efe7daf2SSergei Shtylyov } 485efe7daf2SSergei Shtylyov 486efe7daf2SSergei Shtylyov #ifdef CONFIG_PM 487933bb1f0SMajunath Goudar static int ohci_da8xx_suspend(struct platform_device *pdev, 488933bb1f0SMajunath Goudar pm_message_t message) 489efe7daf2SSergei Shtylyov { 490933bb1f0SMajunath Goudar struct usb_hcd *hcd = platform_get_drvdata(pdev); 491efe7daf2SSergei Shtylyov struct ohci_hcd *ohci = hcd_to_ohci(hcd); 492933bb1f0SMajunath Goudar bool do_wakeup = device_may_wakeup(&pdev->dev); 493933bb1f0SMajunath Goudar int ret; 494933bb1f0SMajunath Goudar 495efe7daf2SSergei Shtylyov 496efe7daf2SSergei Shtylyov if (time_before(jiffies, ohci->next_statechange)) 497efe7daf2SSergei Shtylyov msleep(5); 498efe7daf2SSergei Shtylyov ohci->next_statechange = jiffies; 499efe7daf2SSergei Shtylyov 500933bb1f0SMajunath Goudar ret = ohci_suspend(hcd, do_wakeup); 501933bb1f0SMajunath Goudar if (ret) 502933bb1f0SMajunath Goudar return ret; 503933bb1f0SMajunath Goudar 504c7a4f9f3SAxel Haslam ohci_da8xx_disable(hcd); 505efe7daf2SSergei Shtylyov hcd->state = HC_STATE_SUSPENDED; 506933bb1f0SMajunath Goudar 507933bb1f0SMajunath Goudar return ret; 508efe7daf2SSergei Shtylyov } 509efe7daf2SSergei Shtylyov 510efe7daf2SSergei Shtylyov static int ohci_da8xx_resume(struct platform_device *dev) 511efe7daf2SSergei Shtylyov { 512efe7daf2SSergei Shtylyov struct usb_hcd *hcd = platform_get_drvdata(dev); 513efe7daf2SSergei Shtylyov struct ohci_hcd *ohci = hcd_to_ohci(hcd); 5146110c425SDavid Lechner int ret; 515efe7daf2SSergei Shtylyov 516efe7daf2SSergei Shtylyov if (time_before(jiffies, ohci->next_statechange)) 517efe7daf2SSergei Shtylyov msleep(5); 518efe7daf2SSergei Shtylyov ohci->next_statechange = jiffies; 519efe7daf2SSergei Shtylyov 520c7a4f9f3SAxel Haslam ret = ohci_da8xx_enable(hcd); 5216110c425SDavid Lechner if (ret) 5226110c425SDavid Lechner return ret; 5236110c425SDavid Lechner 524efe7daf2SSergei Shtylyov dev->dev.power.power_state = PMSG_ON; 525efe7daf2SSergei Shtylyov usb_hcd_resume_root_hub(hcd); 5266110c425SDavid Lechner 527efe7daf2SSergei Shtylyov return 0; 528efe7daf2SSergei Shtylyov } 529efe7daf2SSergei Shtylyov #endif 530efe7daf2SSergei Shtylyov 5316c21caa3SManjunath Goudar static const struct ohci_driver_overrides da8xx_overrides __initconst = { 5326c21caa3SManjunath Goudar .reset = ohci_da8xx_reset, 533c7a4f9f3SAxel Haslam .extra_priv_size = sizeof(struct da8xx_ohci_hcd), 5346c21caa3SManjunath Goudar }; 5356c21caa3SManjunath Goudar 536efe7daf2SSergei Shtylyov /* 537efe7daf2SSergei Shtylyov * Driver definition to register with platform structure. 538efe7daf2SSergei Shtylyov */ 539efe7daf2SSergei Shtylyov static struct platform_driver ohci_hcd_da8xx_driver = { 5406c21caa3SManjunath Goudar .probe = ohci_da8xx_probe, 5416c21caa3SManjunath Goudar .remove = ohci_da8xx_remove, 542efe7daf2SSergei Shtylyov .shutdown = usb_hcd_platform_shutdown, 543efe7daf2SSergei Shtylyov #ifdef CONFIG_PM 544efe7daf2SSergei Shtylyov .suspend = ohci_da8xx_suspend, 545efe7daf2SSergei Shtylyov .resume = ohci_da8xx_resume, 546efe7daf2SSergei Shtylyov #endif 547efe7daf2SSergei Shtylyov .driver = { 5486c21caa3SManjunath Goudar .name = DRV_NAME, 549efe7daf2SSergei Shtylyov }, 550efe7daf2SSergei Shtylyov }; 551ab59ac01SJan Luebbe 5526c21caa3SManjunath Goudar static int __init ohci_da8xx_init(void) 5536c21caa3SManjunath Goudar { 5546c21caa3SManjunath Goudar 5556c21caa3SManjunath Goudar if (usb_disabled()) 5566c21caa3SManjunath Goudar return -ENODEV; 5576c21caa3SManjunath Goudar 5586c21caa3SManjunath Goudar pr_info("%s: " DRIVER_DESC "\n", DRV_NAME); 5596c21caa3SManjunath Goudar ohci_init_driver(&ohci_da8xx_hc_driver, &da8xx_overrides); 5606c21caa3SManjunath Goudar 5616c21caa3SManjunath Goudar /* 5626c21caa3SManjunath Goudar * The Davinci da8xx HW has some unusual quirks, which require 5636c21caa3SManjunath Goudar * da8xx-specific workarounds. We override certain hc_driver 5646c21caa3SManjunath Goudar * functions here to achieve that. We explicitly do not enhance 5656c21caa3SManjunath Goudar * ohci_driver_overrides to allow this more easily, since this 5666c21caa3SManjunath Goudar * is an unusual case, and we don't want to encourage others to 5676c21caa3SManjunath Goudar * override these functions by making it too easy. 5686c21caa3SManjunath Goudar */ 5696c21caa3SManjunath Goudar 5706c21caa3SManjunath Goudar orig_ohci_hub_control = ohci_da8xx_hc_driver.hub_control; 5716c21caa3SManjunath Goudar orig_ohci_hub_status_data = ohci_da8xx_hc_driver.hub_status_data; 5726c21caa3SManjunath Goudar 5736c21caa3SManjunath Goudar ohci_da8xx_hc_driver.hub_status_data = ohci_da8xx_hub_status_data; 5746c21caa3SManjunath Goudar ohci_da8xx_hc_driver.hub_control = ohci_da8xx_hub_control; 5756c21caa3SManjunath Goudar 5766c21caa3SManjunath Goudar return platform_driver_register(&ohci_hcd_da8xx_driver); 5776c21caa3SManjunath Goudar } 5786c21caa3SManjunath Goudar module_init(ohci_da8xx_init); 5796c21caa3SManjunath Goudar 5806c21caa3SManjunath Goudar static void __exit ohci_da8xx_exit(void) 5816c21caa3SManjunath Goudar { 5826c21caa3SManjunath Goudar platform_driver_unregister(&ohci_hcd_da8xx_driver); 5836c21caa3SManjunath Goudar } 5846c21caa3SManjunath Goudar module_exit(ohci_da8xx_exit); 5856c21caa3SManjunath Goudar MODULE_DESCRIPTION(DRIVER_DESC); 5866c21caa3SManjunath Goudar MODULE_LICENSE("GPL"); 5876c21caa3SManjunath Goudar MODULE_ALIAS("platform:" DRV_NAME); 588