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

usb: host: ohci-st: Add OHCI driver support for ST STB devices

This patch adds the glue code required to ensure the on-chip OHCI
controller works on STi consumer electronics SoC's from STMicroelectronics.

It mainly manages the setting and enabling of the relevant clocks and manages
the reset / power signals to the IP block.

Signed-off-by: Peter Griffin <peter.griffin@linaro.org>
Reviewed-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Peter Griffin and committed by
Greg Kroah-Hartman
d1158372 e47c5a09

+358
+8
drivers/usb/host/Kconfig
··· 761 761 762 762 If unsure, say N. 763 763 764 + config USB_HCD_ST 765 + tristate "ST USB driver for ST SoC Series" 766 + depends on ARCH_STI && OF 767 + select GENERIC_PHY 768 + help 769 + Enable support for the on-chip OHCI & EHCI controller found on 770 + STMicroelectronics consumer electronics SoC's. 771 + 764 772 config USB_HCD_TEST_MODE 765 773 bool "HCD test mode support" 766 774 ---help---
+1
drivers/usb/host/Makefile
··· 77 77 obj-$(CONFIG_USB_FUSBH200_HCD) += fusbh200-hcd.o 78 78 obj-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o 79 79 obj-$(CONFIG_USB_MAX3421_HCD) += max3421-hcd.o 80 + obj-$(CONFIG_USB_HCD_ST) += ehci-st.o ohci-st.o
+349
drivers/usb/host/ohci-st.c
··· 1 + /* 2 + * ST OHCI driver 3 + * 4 + * Copyright (C) 2014 STMicroelectronics – All Rights Reserved 5 + * 6 + * Author: Peter Griffin <peter.griffin@linaro.org> 7 + * 8 + * Derived from ohci-platform.c 9 + * 10 + * This program is free software; you can redistribute it and/or modify 11 + * it under the terms of the GNU General Public License version 2 as 12 + * published by the Free Software Foundation. 13 + */ 14 + 15 + #include <linux/clk.h> 16 + #include <linux/dma-mapping.h> 17 + #include <linux/hrtimer.h> 18 + #include <linux/io.h> 19 + #include <linux/kernel.h> 20 + #include <linux/module.h> 21 + #include <linux/err.h> 22 + #include <linux/phy/phy.h> 23 + #include <linux/platform_device.h> 24 + #include <linux/reset.h> 25 + #include <linux/usb/ohci_pdriver.h> 26 + #include <linux/usb.h> 27 + #include <linux/usb/hcd.h> 28 + 29 + #include "ohci.h" 30 + 31 + #define USB_MAX_CLKS 3 32 + 33 + struct st_ohci_platform_priv { 34 + struct clk *clks[USB_MAX_CLKS]; 35 + struct clk *clk48; 36 + struct reset_control *rst; 37 + struct reset_control *pwr; 38 + struct phy *phy; 39 + }; 40 + 41 + #define DRIVER_DESC "OHCI STMicroelectronics driver" 42 + 43 + #define hcd_to_ohci_priv(h) \ 44 + ((struct st_ohci_platform_priv *)hcd_to_ohci(h)->priv) 45 + 46 + static const char hcd_name[] = "ohci-st"; 47 + 48 + static int st_ohci_platform_power_on(struct platform_device *dev) 49 + { 50 + struct usb_hcd *hcd = platform_get_drvdata(dev); 51 + struct st_ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); 52 + int clk, ret; 53 + 54 + ret = reset_control_deassert(priv->pwr); 55 + if (ret) 56 + return ret; 57 + 58 + ret = reset_control_deassert(priv->rst); 59 + if (ret) 60 + goto err_assert_power; 61 + 62 + /* some SoCs don't have a dedicated 48Mhz clock, but those that do 63 + need the rate to be explicitly set */ 64 + if (priv->clk48) { 65 + ret = clk_set_rate(priv->clk48, 48000000); 66 + if (ret) 67 + goto err_assert_reset; 68 + } 69 + 70 + for (clk = 0; clk < USB_MAX_CLKS && priv->clks[clk]; clk++) { 71 + ret = clk_prepare_enable(priv->clks[clk]); 72 + if (ret) 73 + goto err_disable_clks; 74 + } 75 + 76 + ret = phy_init(priv->phy); 77 + if (ret) 78 + goto err_disable_clks; 79 + 80 + ret = phy_power_on(priv->phy); 81 + if (ret) 82 + goto err_exit_phy; 83 + 84 + return 0; 85 + 86 + err_exit_phy: 87 + phy_exit(priv->phy); 88 + err_disable_clks: 89 + while (--clk >= 0) 90 + clk_disable_unprepare(priv->clks[clk]); 91 + err_assert_reset: 92 + reset_control_assert(priv->rst); 93 + err_assert_power: 94 + reset_control_assert(priv->pwr); 95 + 96 + return ret; 97 + } 98 + 99 + static void st_ohci_platform_power_off(struct platform_device *dev) 100 + { 101 + struct usb_hcd *hcd = platform_get_drvdata(dev); 102 + struct st_ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); 103 + 104 + int clk; 105 + 106 + reset_control_assert(priv->pwr); 107 + 108 + reset_control_assert(priv->rst); 109 + 110 + phy_power_off(priv->phy); 111 + 112 + phy_exit(priv->phy); 113 + 114 + for (clk = USB_MAX_CLKS - 1; clk >= 0; clk--) 115 + if (priv->clks[clk]) 116 + clk_disable_unprepare(priv->clks[clk]); 117 + } 118 + 119 + static struct hc_driver __read_mostly ohci_platform_hc_driver; 120 + 121 + static const struct ohci_driver_overrides platform_overrides __initconst = { 122 + .product_desc = "ST OHCI controller", 123 + .extra_priv_size = sizeof(struct st_ohci_platform_priv), 124 + }; 125 + 126 + static struct usb_ohci_pdata ohci_platform_defaults = { 127 + .power_on = st_ohci_platform_power_on, 128 + .power_suspend = st_ohci_platform_power_off, 129 + .power_off = st_ohci_platform_power_off, 130 + }; 131 + 132 + static int st_ohci_platform_probe(struct platform_device *dev) 133 + { 134 + struct usb_hcd *hcd; 135 + struct resource *res_mem; 136 + struct usb_ohci_pdata *pdata = &ohci_platform_defaults; 137 + struct st_ohci_platform_priv *priv; 138 + struct ohci_hcd *ohci; 139 + int err, irq, clk = 0; 140 + 141 + if (usb_disabled()) 142 + return -ENODEV; 143 + 144 + irq = platform_get_irq(dev, 0); 145 + if (irq < 0) { 146 + dev_err(&dev->dev, "no irq provided"); 147 + return irq; 148 + } 149 + 150 + res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0); 151 + if (!res_mem) { 152 + dev_err(&dev->dev, "no memory resource provided"); 153 + return -ENXIO; 154 + } 155 + 156 + hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev, 157 + dev_name(&dev->dev)); 158 + if (!hcd) 159 + return -ENOMEM; 160 + 161 + platform_set_drvdata(dev, hcd); 162 + dev->dev.platform_data = pdata; 163 + priv = hcd_to_ohci_priv(hcd); 164 + ohci = hcd_to_ohci(hcd); 165 + 166 + priv->phy = devm_phy_get(&dev->dev, "usb"); 167 + if (IS_ERR(priv->phy)) { 168 + err = PTR_ERR(priv->phy); 169 + goto err_put_hcd; 170 + } 171 + 172 + for (clk = 0; clk < USB_MAX_CLKS; clk++) { 173 + priv->clks[clk] = of_clk_get(dev->dev.of_node, clk); 174 + if (IS_ERR(priv->clks[clk])) { 175 + err = PTR_ERR(priv->clks[clk]); 176 + if (err == -EPROBE_DEFER) 177 + goto err_put_clks; 178 + priv->clks[clk] = NULL; 179 + break; 180 + } 181 + } 182 + 183 + /* some SoCs don't have a dedicated 48Mhz clock, but those that 184 + do need the rate to be explicitly set */ 185 + priv->clk48 = devm_clk_get(&dev->dev, "clk48"); 186 + if (IS_ERR(priv->clk48)) { 187 + dev_info(&dev->dev, "48MHz clk not found\n"); 188 + priv->clk48 = NULL; 189 + } 190 + 191 + priv->pwr = devm_reset_control_get_optional(&dev->dev, "power"); 192 + if (IS_ERR(priv->pwr)) { 193 + err = PTR_ERR(priv->pwr); 194 + goto err_put_clks; 195 + } 196 + 197 + priv->rst = devm_reset_control_get_optional(&dev->dev, "softreset"); 198 + if (IS_ERR(priv->rst)) { 199 + err = PTR_ERR(priv->rst); 200 + goto err_put_clks; 201 + } 202 + 203 + if (pdata->power_on) { 204 + err = pdata->power_on(dev); 205 + if (err < 0) 206 + goto err_power; 207 + } 208 + 209 + hcd->rsrc_start = res_mem->start; 210 + hcd->rsrc_len = resource_size(res_mem); 211 + 212 + hcd->regs = devm_ioremap_resource(&dev->dev, res_mem); 213 + if (IS_ERR(hcd->regs)) { 214 + err = PTR_ERR(hcd->regs); 215 + goto err_power; 216 + } 217 + err = usb_add_hcd(hcd, irq, IRQF_SHARED); 218 + if (err) 219 + goto err_power; 220 + 221 + device_wakeup_enable(hcd->self.controller); 222 + 223 + platform_set_drvdata(dev, hcd); 224 + 225 + return err; 226 + 227 + err_power: 228 + if (pdata->power_off) 229 + pdata->power_off(dev); 230 + 231 + err_put_clks: 232 + while (--clk >= 0) 233 + clk_put(priv->clks[clk]); 234 + err_put_hcd: 235 + if (pdata == &ohci_platform_defaults) 236 + dev->dev.platform_data = NULL; 237 + 238 + usb_put_hcd(hcd); 239 + 240 + return err; 241 + } 242 + 243 + static int st_ohci_platform_remove(struct platform_device *dev) 244 + { 245 + struct usb_hcd *hcd = platform_get_drvdata(dev); 246 + struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev); 247 + struct st_ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); 248 + int clk; 249 + 250 + usb_remove_hcd(hcd); 251 + 252 + if (pdata->power_off) 253 + pdata->power_off(dev); 254 + 255 + 256 + for (clk = 0; clk < USB_MAX_CLKS && priv->clks[clk]; clk++) 257 + clk_put(priv->clks[clk]); 258 + 259 + usb_put_hcd(hcd); 260 + 261 + if (pdata == &ohci_platform_defaults) 262 + dev->dev.platform_data = NULL; 263 + 264 + return 0; 265 + } 266 + 267 + #ifdef CONFIG_PM_SLEEP 268 + 269 + static int st_ohci_suspend(struct device *dev) 270 + { 271 + struct usb_hcd *hcd = dev_get_drvdata(dev); 272 + struct usb_ohci_pdata *pdata = dev->platform_data; 273 + struct platform_device *pdev = 274 + container_of(dev, struct platform_device, dev); 275 + bool do_wakeup = device_may_wakeup(dev); 276 + int ret; 277 + 278 + ret = ohci_suspend(hcd, do_wakeup); 279 + if (ret) 280 + return ret; 281 + 282 + if (pdata->power_suspend) 283 + pdata->power_suspend(pdev); 284 + 285 + return ret; 286 + } 287 + 288 + static int st_ohci_resume(struct device *dev) 289 + { 290 + struct usb_hcd *hcd = dev_get_drvdata(dev); 291 + struct usb_ohci_pdata *pdata = dev_get_platdata(dev); 292 + struct platform_device *pdev = 293 + container_of(dev, struct platform_device, dev); 294 + int err; 295 + 296 + if (pdata->power_on) { 297 + err = pdata->power_on(pdev); 298 + if (err < 0) 299 + return err; 300 + } 301 + 302 + ohci_resume(hcd, false); 303 + return 0; 304 + } 305 + 306 + static SIMPLE_DEV_PM_OPS(st_ohci_pm_ops, st_ohci_suspend, st_ohci_resume); 307 + 308 + #endif /* CONFIG_PM_SLEEP */ 309 + 310 + static const struct of_device_id st_ohci_platform_ids[] = { 311 + { .compatible = "st,st-ohci-300x", }, 312 + { /* sentinel */ } 313 + }; 314 + MODULE_DEVICE_TABLE(of, st_ohci_platform_ids); 315 + 316 + static struct platform_driver ohci_platform_driver = { 317 + .probe = st_ohci_platform_probe, 318 + .remove = st_ohci_platform_remove, 319 + .shutdown = usb_hcd_platform_shutdown, 320 + .driver = { 321 + .name = "st-ohci", 322 + #ifdef CONFIG_PM_SLEEP 323 + .pm = &st_ohci_pm_ops, 324 + #endif 325 + .of_match_table = st_ohci_platform_ids, 326 + } 327 + }; 328 + 329 + static int __init ohci_platform_init(void) 330 + { 331 + if (usb_disabled()) 332 + return -ENODEV; 333 + 334 + pr_info("%s: " DRIVER_DESC "\n", hcd_name); 335 + 336 + ohci_init_driver(&ohci_platform_hc_driver, &platform_overrides); 337 + return platform_driver_register(&ohci_platform_driver); 338 + } 339 + module_init(ohci_platform_init); 340 + 341 + static void __exit ohci_platform_cleanup(void) 342 + { 343 + platform_driver_unregister(&ohci_platform_driver); 344 + } 345 + module_exit(ohci_platform_cleanup); 346 + 347 + MODULE_DESCRIPTION(DRIVER_DESC); 348 + MODULE_AUTHOR("Peter Griffin <peter.griffin@linaro.org>"); 349 + MODULE_LICENSE("GPL");