blob: 08b046f9491be1534bd0b4aed048a002d8c3eada [file] [log] [blame]
Richard Zhaod142d6b2012-09-12 14:58:05 +03001/*
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
Marc Kleine-Buddee6091082013-03-30 12:53:59 +020022#define MX6_BM_OVER_CUR_DIS BIT(7)
Richard Zhaod142d6b2012-09-12 14:58:05 +030023
Michael Grzeschika7bc2fd2013-03-30 12:53:56 +020024struct imx_usbmisc {
Richard Zhaod142d6b2012-09-12 14:58:05 +030025 void __iomem *base;
26 spinlock_t lock;
27 struct clk *clk;
28 struct usbmisc_usb_device usbdev[USB_DEV_MAX];
Marc Kleine-Buddee6091082013-03-30 12:53:59 +020029 const struct usbmisc_ops *ops;
Richard Zhaod142d6b2012-09-12 14:58:05 +030030};
31
Michael Grzeschika7bc2fd2013-03-30 12:53:56 +020032static struct imx_usbmisc *usbmisc;
Richard Zhaod142d6b2012-09-12 14:58:05 +030033
34static struct usbmisc_usb_device *get_usbdev(struct device *dev)
35{
36 int i, ret;
37
38 for (i = 0; i < USB_DEV_MAX; i++) {
39 if (usbmisc->usbdev[i].dev == dev)
40 return &usbmisc->usbdev[i];
41 else if (!usbmisc->usbdev[i].dev)
42 break;
43 }
44
45 if (i >= USB_DEV_MAX)
46 return ERR_PTR(-EBUSY);
47
48 ret = usbmisc_get_init_data(dev, &usbmisc->usbdev[i]);
49 if (ret)
50 return ERR_PTR(ret);
51
52 return &usbmisc->usbdev[i];
53}
54
55static int usbmisc_imx6q_init(struct device *dev)
56{
57
58 struct usbmisc_usb_device *usbdev;
59 unsigned long flags;
60 u32 reg;
61
62 usbdev = get_usbdev(dev);
63 if (IS_ERR(usbdev))
64 return PTR_ERR(usbdev);
65
66 if (usbdev->disable_oc) {
67 spin_lock_irqsave(&usbmisc->lock, flags);
68 reg = readl(usbmisc->base + usbdev->index * 4);
Marc Kleine-Buddee6091082013-03-30 12:53:59 +020069 writel(reg | MX6_BM_OVER_CUR_DIS,
Richard Zhaod142d6b2012-09-12 14:58:05 +030070 usbmisc->base + usbdev->index * 4);
71 spin_unlock_irqrestore(&usbmisc->lock, flags);
72 }
73
74 return 0;
75}
76
77static const struct usbmisc_ops imx6q_usbmisc_ops = {
78 .init = usbmisc_imx6q_init,
79};
80
Michael Grzeschika7bc2fd2013-03-30 12:53:56 +020081static const struct of_device_id usbmisc_imx_dt_ids[] = {
Marc Kleine-Buddee6091082013-03-30 12:53:59 +020082 {
83 .compatible = "fsl,imx6q-usbmisc",
84 .data = &imx6q_usbmisc_ops,
85 },
Richard Zhaod142d6b2012-09-12 14:58:05 +030086 { /* sentinel */ }
87};
88
Michael Grzeschika7bc2fd2013-03-30 12:53:56 +020089static int usbmisc_imx_probe(struct platform_device *pdev)
Richard Zhaod142d6b2012-09-12 14:58:05 +030090{
91 struct resource *res;
Michael Grzeschika7bc2fd2013-03-30 12:53:56 +020092 struct imx_usbmisc *data;
Richard Zhaod142d6b2012-09-12 14:58:05 +030093 int ret;
Marc Kleine-Buddee6091082013-03-30 12:53:59 +020094 struct of_device_id *tmp_dev;
Richard Zhaod142d6b2012-09-12 14:58:05 +030095
96 if (usbmisc)
97 return -EBUSY;
98
99 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
100 if (!data)
101 return -ENOMEM;
102
103 spin_lock_init(&data->lock);
104
105 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Thierry Reding148e1132013-01-21 11:09:22 +0100106 data->base = devm_ioremap_resource(&pdev->dev, res);
107 if (IS_ERR(data->base))
108 return PTR_ERR(data->base);
Richard Zhaod142d6b2012-09-12 14:58:05 +0300109
110 data->clk = devm_clk_get(&pdev->dev, NULL);
111 if (IS_ERR(data->clk)) {
112 dev_err(&pdev->dev,
113 "failed to get clock, err=%ld\n", PTR_ERR(data->clk));
114 return PTR_ERR(data->clk);
115 }
116
117 ret = clk_prepare_enable(data->clk);
118 if (ret) {
119 dev_err(&pdev->dev,
120 "clk_prepare_enable failed, err=%d\n", ret);
121 return ret;
122 }
123
Marc Kleine-Buddee6091082013-03-30 12:53:59 +0200124 tmp_dev = (struct of_device_id *)
125 of_match_device(usbmisc_imx_dt_ids, &pdev->dev);
126 data->ops = (const struct usbmisc_ops *)tmp_dev->data;
Marc Kleine-Budde00b9a1f92013-03-30 12:53:58 +0200127 usbmisc = data;
Marc Kleine-Buddee6091082013-03-30 12:53:59 +0200128 ret = usbmisc_set_ops(data->ops);
Richard Zhaod142d6b2012-09-12 14:58:05 +0300129 if (ret) {
Marc Kleine-Budde00b9a1f92013-03-30 12:53:58 +0200130 usbmisc = NULL;
Richard Zhaod142d6b2012-09-12 14:58:05 +0300131 clk_disable_unprepare(data->clk);
132 return ret;
133 }
134
Richard Zhaod142d6b2012-09-12 14:58:05 +0300135 return 0;
136}
137
Michael Grzeschika7bc2fd2013-03-30 12:53:56 +0200138static int usbmisc_imx_remove(struct platform_device *pdev)
Richard Zhaod142d6b2012-09-12 14:58:05 +0300139{
Marc Kleine-Buddee6091082013-03-30 12:53:59 +0200140 usbmisc_unset_ops(usbmisc->ops);
Richard Zhaod142d6b2012-09-12 14:58:05 +0300141 clk_disable_unprepare(usbmisc->clk);
Marc Kleine-Budded48a24d2013-03-30 12:53:57 +0200142 usbmisc = NULL;
Richard Zhaod142d6b2012-09-12 14:58:05 +0300143 return 0;
144}
145
Michael Grzeschika7bc2fd2013-03-30 12:53:56 +0200146static struct platform_driver usbmisc_imx_driver = {
147 .probe = usbmisc_imx_probe,
148 .remove = usbmisc_imx_remove,
Richard Zhaod142d6b2012-09-12 14:58:05 +0300149 .driver = {
Michael Grzeschika7bc2fd2013-03-30 12:53:56 +0200150 .name = "usbmisc_imx",
Richard Zhaod142d6b2012-09-12 14:58:05 +0300151 .owner = THIS_MODULE,
Michael Grzeschika7bc2fd2013-03-30 12:53:56 +0200152 .of_match_table = usbmisc_imx_dt_ids,
Richard Zhaod142d6b2012-09-12 14:58:05 +0300153 },
154};
155
Michael Grzeschika7bc2fd2013-03-30 12:53:56 +0200156int usbmisc_imx_drv_init(void)
Richard Zhaod142d6b2012-09-12 14:58:05 +0300157{
Michael Grzeschika7bc2fd2013-03-30 12:53:56 +0200158 return platform_driver_register(&usbmisc_imx_driver);
Richard Zhaod142d6b2012-09-12 14:58:05 +0300159}
Michael Grzeschika7bc2fd2013-03-30 12:53:56 +0200160subsys_initcall(usbmisc_imx_drv_init);
Richard Zhaod142d6b2012-09-12 14:58:05 +0300161
Michael Grzeschika7bc2fd2013-03-30 12:53:56 +0200162void usbmisc_imx_drv_exit(void)
Richard Zhaod142d6b2012-09-12 14:58:05 +0300163{
Michael Grzeschika7bc2fd2013-03-30 12:53:56 +0200164 platform_driver_unregister(&usbmisc_imx_driver);
Richard Zhaod142d6b2012-09-12 14:58:05 +0300165}
Michael Grzeschika7bc2fd2013-03-30 12:53:56 +0200166module_exit(usbmisc_imx_drv_exit);
Richard Zhaod142d6b2012-09-12 14:58:05 +0300167
Michael Grzeschika7bc2fd2013-03-30 12:53:56 +0200168MODULE_ALIAS("platform:usbmisc-imx");
Richard Zhaod142d6b2012-09-12 14:58:05 +0300169MODULE_LICENSE("GPL v2");
Michael Grzeschika7bc2fd2013-03-30 12:53:56 +0200170MODULE_DESCRIPTION("driver for imx usb non-core registers");
Richard Zhaod142d6b2012-09-12 14:58:05 +0300171MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");