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

USB: chipidea: add imx usbmisc support

i.MX usb controllers share non-core registers, which may include
SoC specific controls. We turn it into a usbmisc device and usbmisc
driver set operations needed by ci13xxx_imx driver.

For example, Sabrelite board has bad over-current design, we can
usbmisc to disable over-current detection.

Acked-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Zhao <richard.zhao@freescale.com>
Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Richard Zhao and committed by
Greg Kroah-Hartman
d142d6be 08d9c74d

+274 -1
+5
Documentation/devicetree/bindings/usb/ci13xxx-imx.txt
··· 7 7 8 8 Optional properties: 9 9 - fsl,usbphy: phandler of usb phy that connects to the only one port 10 + - fsl,usbmisc: phandler of non-core register device, with one argument 11 + that indicate usb controller index 10 12 - vbus-supply: regulator for vbus 13 + - disable-over-current: disable over current detect 11 14 12 15 Examples: 13 16 usb@02184000 { /* USB OTG */ ··· 18 15 reg = <0x02184000 0x200>; 19 16 interrupts = <0 43 0x04>; 20 17 fsl,usbphy = <&usbphy1>; 18 + fsl,usbmisc = <&usbmisc 0>; 19 + disable-over-current; 21 20 };
+14
Documentation/devicetree/bindings/usb/usbmisc-imx.txt
··· 1 + * Freescale i.MX non-core registers 2 + 3 + Required properties: 4 + - #index-cells: Cells used to descibe usb controller index. Should be <1> 5 + - compatible: Should be one of below: 6 + "fsl,imx6q-usbmisc" for imx6q 7 + - reg: Should contain registers location and length 8 + 9 + Examples: 10 + usbmisc@02184800 { 11 + #index-cells = <1>; 12 + compatible = "fsl,imx6q-usbmisc"; 13 + reg = <0x02184800 0x200>; 14 + };
+1 -1
drivers/usb/chipidea/Makefile
··· 15 15 endif 16 16 17 17 ifneq ($(CONFIG_OF_DEVICE),) 18 - obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_imx.o 18 + obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_imx.o usbmisc_imx6q.o 19 19 endif
+64
drivers/usb/chipidea/ci13xxx_imx.c
··· 22 22 #include <linux/regulator/consumer.h> 23 23 24 24 #include "ci.h" 25 + #include "ci13xxx_imx.h" 25 26 26 27 #define pdev_to_phy(pdev) \ 27 28 ((struct usb_phy *)platform_get_drvdata(pdev)) ··· 34 33 struct clk *clk; 35 34 struct regulator *reg_vbus; 36 35 }; 36 + 37 + static const struct usbmisc_ops *usbmisc_ops; 38 + 39 + /* Common functions shared by usbmisc drivers */ 40 + 41 + int usbmisc_set_ops(const struct usbmisc_ops *ops) 42 + { 43 + if (usbmisc_ops) 44 + return -EBUSY; 45 + 46 + usbmisc_ops = ops; 47 + 48 + return 0; 49 + } 50 + EXPORT_SYMBOL_GPL(usbmisc_set_ops); 51 + 52 + void usbmisc_unset_ops(const struct usbmisc_ops *ops) 53 + { 54 + usbmisc_ops = NULL; 55 + } 56 + EXPORT_SYMBOL_GPL(usbmisc_unset_ops); 57 + 58 + int usbmisc_get_init_data(struct device *dev, struct usbmisc_usb_device *usbdev) 59 + { 60 + struct device_node *np = dev->of_node; 61 + struct of_phandle_args args; 62 + int ret; 63 + 64 + usbdev->dev = dev; 65 + 66 + ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells", 67 + 0, &args); 68 + if (ret) { 69 + dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n", 70 + ret); 71 + memset(usbdev, 0, sizeof(*usbdev)); 72 + return ret; 73 + } 74 + usbdev->index = args.args[0]; 75 + of_node_put(args.np); 76 + 77 + if (of_find_property(np, "disable-over-current", NULL)) 78 + usbdev->disable_oc = 1; 79 + 80 + return 0; 81 + } 82 + EXPORT_SYMBOL_GPL(usbmisc_get_init_data); 83 + 84 + /* End of common functions shared by usbmisc drivers*/ 37 85 38 86 static struct ci13xxx_platform_data ci13xxx_imx_platdata __devinitdata = { 39 87 .name = "ci13xxx_imx", ··· 100 50 struct resource *res; 101 51 struct regulator *reg_vbus; 102 52 int ret; 53 + 54 + if (of_find_property(pdev->dev.of_node, "fsl,usbmisc", NULL) 55 + && !usbmisc_ops) 56 + return -EPROBE_DEFER; 103 57 104 58 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 105 59 if (!data) { ··· 174 120 *pdev->dev.dma_mask = DMA_BIT_MASK(32); 175 121 dma_set_coherent_mask(&pdev->dev, *pdev->dev.dma_mask); 176 122 } 123 + 124 + if (usbmisc_ops && usbmisc_ops->init) { 125 + ret = usbmisc_ops->init(&pdev->dev); 126 + if (ret) { 127 + dev_err(&pdev->dev, 128 + "usbmisc init failed, ret=%d\n", ret); 129 + goto err; 130 + } 131 + } 132 + 177 133 plat_ci = ci13xxx_add_device(&pdev->dev, 178 134 pdev->resource, pdev->num_resources, 179 135 &ci13xxx_imx_platdata);
+28
drivers/usb/chipidea/ci13xxx_imx.h
··· 1 + /* 2 + * Copyright 2012 Freescale Semiconductor, Inc. 3 + * 4 + * The code contained herein is licensed under the GNU General Public 5 + * License. You may obtain a copy of the GNU General Public License 6 + * Version 2 or later at the following locations: 7 + * 8 + * http://www.opensource.org/licenses/gpl-license.html 9 + * http://www.gnu.org/copyleft/gpl.html 10 + */ 11 + 12 + /* Used to set SoC specific callbacks */ 13 + struct usbmisc_ops { 14 + /* It's called once when probe a usb device */ 15 + int (*init)(struct device *dev); 16 + }; 17 + 18 + struct usbmisc_usb_device { 19 + struct device *dev; /* usb controller device */ 20 + int index; 21 + 22 + int disable_oc:1; /* over current detect disabled */ 23 + }; 24 + 25 + int usbmisc_set_ops(const struct usbmisc_ops *ops); 26 + void usbmisc_unset_ops(const struct usbmisc_ops *ops); 27 + int 28 + usbmisc_get_init_data(struct device *dev, struct usbmisc_usb_device *usbdev);
+162
drivers/usb/chipidea/usbmisc_imx6q.c
··· 1 + /* 2 + * Copyright 2012 Freescale Semiconductor, Inc. 3 + * 4 + * The code contained herein is licensed under the GNU General Public 5 + * License. You may obtain a copy of the GNU General Public License 6 + * Version 2 or later at the following locations: 7 + * 8 + * http://www.opensource.org/licenses/gpl-license.html 9 + * http://www.gnu.org/copyleft/gpl.html 10 + */ 11 + 12 + #include <linux/module.h> 13 + #include <linux/of_platform.h> 14 + #include <linux/clk.h> 15 + #include <linux/err.h> 16 + #include <linux/io.h> 17 + 18 + #include "ci13xxx_imx.h" 19 + 20 + #define USB_DEV_MAX 4 21 + 22 + #define BM_OVER_CUR_DIS BIT(7) 23 + 24 + struct imx6q_usbmisc { 25 + void __iomem *base; 26 + spinlock_t lock; 27 + struct clk *clk; 28 + struct usbmisc_usb_device usbdev[USB_DEV_MAX]; 29 + }; 30 + 31 + static struct imx6q_usbmisc *usbmisc; 32 + 33 + static struct usbmisc_usb_device *get_usbdev(struct device *dev) 34 + { 35 + int i, ret; 36 + 37 + for (i = 0; i < USB_DEV_MAX; i++) { 38 + if (usbmisc->usbdev[i].dev == dev) 39 + return &usbmisc->usbdev[i]; 40 + else if (!usbmisc->usbdev[i].dev) 41 + break; 42 + } 43 + 44 + if (i >= USB_DEV_MAX) 45 + return ERR_PTR(-EBUSY); 46 + 47 + ret = usbmisc_get_init_data(dev, &usbmisc->usbdev[i]); 48 + if (ret) 49 + return ERR_PTR(ret); 50 + 51 + return &usbmisc->usbdev[i]; 52 + } 53 + 54 + static int usbmisc_imx6q_init(struct device *dev) 55 + { 56 + 57 + struct usbmisc_usb_device *usbdev; 58 + unsigned long flags; 59 + u32 reg; 60 + 61 + usbdev = get_usbdev(dev); 62 + if (IS_ERR(usbdev)) 63 + return PTR_ERR(usbdev); 64 + 65 + if (usbdev->disable_oc) { 66 + spin_lock_irqsave(&usbmisc->lock, flags); 67 + reg = readl(usbmisc->base + usbdev->index * 4); 68 + writel(reg | BM_OVER_CUR_DIS, 69 + usbmisc->base + usbdev->index * 4); 70 + spin_unlock_irqrestore(&usbmisc->lock, flags); 71 + } 72 + 73 + return 0; 74 + } 75 + 76 + static const struct usbmisc_ops imx6q_usbmisc_ops = { 77 + .init = usbmisc_imx6q_init, 78 + }; 79 + 80 + static const struct of_device_id usbmisc_imx6q_dt_ids[] = { 81 + { .compatible = "fsl,imx6q-usbmisc"}, 82 + { /* sentinel */ } 83 + }; 84 + 85 + static int __devinit usbmisc_imx6q_probe(struct platform_device *pdev) 86 + { 87 + struct resource *res; 88 + struct imx6q_usbmisc *data; 89 + int ret; 90 + 91 + if (usbmisc) 92 + return -EBUSY; 93 + 94 + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 95 + if (!data) 96 + return -ENOMEM; 97 + 98 + spin_lock_init(&data->lock); 99 + 100 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 101 + data->base = devm_request_and_ioremap(&pdev->dev, res); 102 + if (!data->base) 103 + return -EADDRNOTAVAIL; 104 + 105 + data->clk = devm_clk_get(&pdev->dev, NULL); 106 + if (IS_ERR(data->clk)) { 107 + dev_err(&pdev->dev, 108 + "failed to get clock, err=%ld\n", PTR_ERR(data->clk)); 109 + return PTR_ERR(data->clk); 110 + } 111 + 112 + ret = clk_prepare_enable(data->clk); 113 + if (ret) { 114 + dev_err(&pdev->dev, 115 + "clk_prepare_enable failed, err=%d\n", ret); 116 + return ret; 117 + } 118 + 119 + ret = usbmisc_set_ops(&imx6q_usbmisc_ops); 120 + if (ret) { 121 + clk_disable_unprepare(data->clk); 122 + return ret; 123 + } 124 + 125 + usbmisc = data; 126 + 127 + return 0; 128 + } 129 + 130 + static int __devexit usbmisc_imx6q_remove(struct platform_device *pdev) 131 + { 132 + usbmisc_unset_ops(&imx6q_usbmisc_ops); 133 + clk_disable_unprepare(usbmisc->clk); 134 + return 0; 135 + } 136 + 137 + static struct platform_driver usbmisc_imx6q_driver = { 138 + .probe = usbmisc_imx6q_probe, 139 + .remove = __devexit_p(usbmisc_imx6q_remove), 140 + .driver = { 141 + .name = "usbmisc_imx6q", 142 + .owner = THIS_MODULE, 143 + .of_match_table = usbmisc_imx6q_dt_ids, 144 + }, 145 + }; 146 + 147 + int __init usbmisc_imx6q_drv_init(void) 148 + { 149 + return platform_driver_register(&usbmisc_imx6q_driver); 150 + } 151 + subsys_initcall(usbmisc_imx6q_drv_init); 152 + 153 + void __exit usbmisc_imx6q_drv_exit(void) 154 + { 155 + platform_driver_unregister(&usbmisc_imx6q_driver); 156 + } 157 + module_exit(usbmisc_imx6q_drv_exit); 158 + 159 + MODULE_ALIAS("platform:usbmisc-imx6q"); 160 + MODULE_LICENSE("GPL v2"); 161 + MODULE_DESCRIPTION("driver for imx6q usb non-core registers"); 162 + MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");