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 "ci_hdrc_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 MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids); 179 180 static int usbmisc_imx_probe(struct platform_device *pdev) 181 { 182 struct resource *res; 183 struct imx_usbmisc *data; 184 int ret; 185 struct of_device_id *tmp_dev; 186 187 if (usbmisc) 188 return -EBUSY; 189 190 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 191 if (!data) 192 return -ENOMEM; 193 194 spin_lock_init(&data->lock); 195 196 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 197 data->base = devm_ioremap_resource(&pdev->dev, res); 198 if (IS_ERR(data->base)) 199 return PTR_ERR(data->base); 200 201 data->clk = devm_clk_get(&pdev->dev, NULL); 202 if (IS_ERR(data->clk)) { 203 dev_err(&pdev->dev, 204 "failed to get clock, err=%ld\n", PTR_ERR(data->clk)); 205 return PTR_ERR(data->clk); 206 } 207 208 ret = clk_prepare_enable(data->clk); 209 if (ret) { 210 dev_err(&pdev->dev, 211 "clk_prepare_enable failed, err=%d\n", ret); 212 return ret; 213 } 214 215 tmp_dev = (struct of_device_id *) 216 of_match_device(usbmisc_imx_dt_ids, &pdev->dev); 217 data->ops = (const struct usbmisc_ops *)tmp_dev->data; 218 usbmisc = data; 219 ret = usbmisc_set_ops(data->ops); 220 if (ret) { 221 usbmisc = NULL; 222 clk_disable_unprepare(data->clk); 223 return ret; 224 } 225 226 return 0; 227 } 228 229 static int usbmisc_imx_remove(struct platform_device *pdev) 230 { 231 usbmisc_unset_ops(usbmisc->ops); 232 clk_disable_unprepare(usbmisc->clk); 233 usbmisc = NULL; 234 return 0; 235 } 236 237 static struct platform_driver usbmisc_imx_driver = { 238 .probe = usbmisc_imx_probe, 239 .remove = usbmisc_imx_remove, 240 .driver = { 241 .name = "usbmisc_imx", 242 .owner = THIS_MODULE, 243 .of_match_table = usbmisc_imx_dt_ids, 244 }, 245 }; 246 247 module_platform_driver(usbmisc_imx_driver); 248 249 MODULE_ALIAS("platform:usbmisc-imx"); 250 MODULE_LICENSE("GPL v2"); 251 MODULE_DESCRIPTION("driver for imx usb non-core registers"); 252 MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>"); 253