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 #include <linux/delay.h> 18 19 #include "ci13xxx_imx.h" 20 21 #define USB_DEV_MAX 4 22 23 #define MX25_USB_PHY_CTRL_OFFSET 0x08 24 #define MX25_BM_EXTERNAL_VBUS_DIVIDER BIT(23) 25 26 #define MX53_USB_OTG_PHY_CTRL_0_OFFSET 0x08 27 #define MX53_USB_UH2_CTRL_OFFSET 0x14 28 #define MX53_USB_UH3_CTRL_OFFSET 0x18 29 #define MX53_BM_OVER_CUR_DIS_H1 BIT(5) 30 #define MX53_BM_OVER_CUR_DIS_OTG BIT(8) 31 #define MX53_BM_OVER_CUR_DIS_UHx BIT(30) 32 33 #define MX6_BM_OVER_CUR_DIS BIT(7) 34 35 struct imx_usbmisc { 36 void __iomem *base; 37 spinlock_t lock; 38 struct clk *clk; 39 struct usbmisc_usb_device usbdev[USB_DEV_MAX]; 40 const struct usbmisc_ops *ops; 41 }; 42 43 static struct imx_usbmisc *usbmisc; 44 45 static struct usbmisc_usb_device *get_usbdev(struct device *dev) 46 { 47 int i, ret; 48 49 for (i = 0; i < USB_DEV_MAX; i++) { 50 if (usbmisc->usbdev[i].dev == dev) 51 return &usbmisc->usbdev[i]; 52 else if (!usbmisc->usbdev[i].dev) 53 break; 54 } 55 56 if (i >= USB_DEV_MAX) 57 return ERR_PTR(-EBUSY); 58 59 ret = usbmisc_get_init_data(dev, &usbmisc->usbdev[i]); 60 if (ret) 61 return ERR_PTR(ret); 62 63 return &usbmisc->usbdev[i]; 64 } 65 66 static int usbmisc_imx25_post(struct device *dev) 67 { 68 struct usbmisc_usb_device *usbdev; 69 void __iomem *reg; 70 unsigned long flags; 71 u32 val; 72 73 usbdev = get_usbdev(dev); 74 if (IS_ERR(usbdev)) 75 return PTR_ERR(usbdev); 76 77 reg = usbmisc->base + MX25_USB_PHY_CTRL_OFFSET; 78 79 if (usbdev->evdo) { 80 spin_lock_irqsave(&usbmisc->lock, flags); 81 val = readl(reg); 82 writel(val | MX25_BM_EXTERNAL_VBUS_DIVIDER, reg); 83 spin_unlock_irqrestore(&usbmisc->lock, flags); 84 usleep_range(5000, 10000); /* needed to stabilize voltage */ 85 } 86 87 return 0; 88 } 89 90 static int usbmisc_imx53_init(struct device *dev) 91 { 92 struct usbmisc_usb_device *usbdev; 93 void __iomem *reg = NULL; 94 unsigned long flags; 95 u32 val = 0; 96 97 usbdev = get_usbdev(dev); 98 if (IS_ERR(usbdev)) 99 return PTR_ERR(usbdev); 100 101 if (usbdev->disable_oc) { 102 spin_lock_irqsave(&usbmisc->lock, flags); 103 switch (usbdev->index) { 104 case 0: 105 reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET; 106 val = readl(reg) | MX53_BM_OVER_CUR_DIS_OTG; 107 break; 108 case 1: 109 reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET; 110 val = readl(reg) | MX53_BM_OVER_CUR_DIS_H1; 111 break; 112 case 2: 113 reg = usbmisc->base + MX53_USB_UH2_CTRL_OFFSET; 114 val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx; 115 break; 116 case 3: 117 reg = usbmisc->base + MX53_USB_UH3_CTRL_OFFSET; 118 val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx; 119 break; 120 } 121 if (reg && val) 122 writel(val, reg); 123 spin_unlock_irqrestore(&usbmisc->lock, flags); 124 } 125 126 return 0; 127 } 128 129 static int usbmisc_imx6q_init(struct device *dev) 130 { 131 132 struct usbmisc_usb_device *usbdev; 133 unsigned long flags; 134 u32 reg; 135 136 usbdev = get_usbdev(dev); 137 if (IS_ERR(usbdev)) 138 return PTR_ERR(usbdev); 139 140 if (usbdev->disable_oc) { 141 spin_lock_irqsave(&usbmisc->lock, flags); 142 reg = readl(usbmisc->base + usbdev->index * 4); 143 writel(reg | MX6_BM_OVER_CUR_DIS, 144 usbmisc->base + usbdev->index * 4); 145 spin_unlock_irqrestore(&usbmisc->lock, flags); 146 } 147 148 return 0; 149 } 150 151 static const struct usbmisc_ops imx25_usbmisc_ops = { 152 .post = usbmisc_imx25_post, 153 }; 154 155 static const struct usbmisc_ops imx53_usbmisc_ops = { 156 .init = usbmisc_imx53_init, 157 }; 158 159 static const struct usbmisc_ops imx6q_usbmisc_ops = { 160 .init = usbmisc_imx6q_init, 161 }; 162 163 static const struct of_device_id usbmisc_imx_dt_ids[] = { 164 { 165 .compatible = "fsl,imx25-usbmisc", 166 .data = &imx25_usbmisc_ops, 167 }, 168 { 169 .compatible = "fsl,imx53-usbmisc", 170 .data = &imx53_usbmisc_ops, 171 }, 172 { 173 .compatible = "fsl,imx6q-usbmisc", 174 .data = &imx6q_usbmisc_ops, 175 }, 176 { /* sentinel */ } 177 }; 178 179 static int usbmisc_imx_probe(struct platform_device *pdev) 180 { 181 struct resource *res; 182 struct imx_usbmisc *data; 183 int ret; 184 struct of_device_id *tmp_dev; 185 186 if (usbmisc) 187 return -EBUSY; 188 189 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 190 if (!data) 191 return -ENOMEM; 192 193 spin_lock_init(&data->lock); 194 195 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 196 data->base = devm_ioremap_resource(&pdev->dev, res); 197 if (IS_ERR(data->base)) 198 return PTR_ERR(data->base); 199 200 data->clk = devm_clk_get(&pdev->dev, NULL); 201 if (IS_ERR(data->clk)) { 202 dev_err(&pdev->dev, 203 "failed to get clock, err=%ld\n", PTR_ERR(data->clk)); 204 return PTR_ERR(data->clk); 205 } 206 207 ret = clk_prepare_enable(data->clk); 208 if (ret) { 209 dev_err(&pdev->dev, 210 "clk_prepare_enable failed, err=%d\n", ret); 211 return ret; 212 } 213 214 tmp_dev = (struct of_device_id *) 215 of_match_device(usbmisc_imx_dt_ids, &pdev->dev); 216 data->ops = (const struct usbmisc_ops *)tmp_dev->data; 217 usbmisc = data; 218 ret = usbmisc_set_ops(data->ops); 219 if (ret) { 220 usbmisc = NULL; 221 clk_disable_unprepare(data->clk); 222 return ret; 223 } 224 225 return 0; 226 } 227 228 static int usbmisc_imx_remove(struct platform_device *pdev) 229 { 230 usbmisc_unset_ops(usbmisc->ops); 231 clk_disable_unprepare(usbmisc->clk); 232 usbmisc = NULL; 233 return 0; 234 } 235 236 static struct platform_driver usbmisc_imx_driver = { 237 .probe = usbmisc_imx_probe, 238 .remove = usbmisc_imx_remove, 239 .driver = { 240 .name = "usbmisc_imx", 241 .owner = THIS_MODULE, 242 .of_match_table = usbmisc_imx_dt_ids, 243 }, 244 }; 245 246 int usbmisc_imx_drv_init(void) 247 { 248 return platform_driver_register(&usbmisc_imx_driver); 249 } 250 subsys_initcall(usbmisc_imx_drv_init); 251 252 void usbmisc_imx_drv_exit(void) 253 { 254 platform_driver_unregister(&usbmisc_imx_driver); 255 } 256 module_exit(usbmisc_imx_drv_exit); 257 258 MODULE_ALIAS("platform:usbmisc-imx"); 259 MODULE_LICENSE("GPL v2"); 260 MODULE_DESCRIPTION("driver for imx usb non-core registers"); 261 MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>"); 262