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