diff options
author | Sebastian Andrzej Siewior <bigeasy@linutronix.de> | 2013-07-30 15:43:45 -0400 |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2013-08-09 10:34:15 -0400 |
commit | 3bb869c8b3f1a11f1854cd74ebdeb60753614cf8 (patch) | |
tree | 124ca4bdf85a58f980e9f00913063191103c6070 | |
parent | 53b6fc28ea8e9857b6141afb92f3683eab9568ba (diff) |
usb: phy: Add AM335x PHY driver
This driver is a redo of my earlier attempt. It uses parts of the
generic PHY driver and uses the new control driver for the register
the phy needs to power on/off the phy. It also enables easy access for
the wakeup register which is not yet implemented.
The difference between the omap attempt is:
- no static holding variable
- one global visible function which exports a struct with callbacks to
access the "control" registers.
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Felipe Balbi <balbi@ti.com>
-rw-r--r-- | drivers/usb/phy/Kconfig | 12 | ||||
-rw-r--r-- | drivers/usb/phy/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/phy/am35x-phy-control.h | 21 | ||||
-rw-r--r-- | drivers/usb/phy/phy-am335x-control.c | 136 | ||||
-rw-r--r-- | drivers/usb/phy/phy-am335x.c | 99 |
5 files changed, 270 insertions, 0 deletions
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index f5ea339ec155..f41c3e12c6a7 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig | |||
@@ -86,6 +86,18 @@ config OMAP_USB3 | |||
86 | This driver interacts with the "OMAP Control USB Driver" to power | 86 | This driver interacts with the "OMAP Control USB Driver" to power |
87 | on/off the PHY. | 87 | on/off the PHY. |
88 | 88 | ||
89 | config AM335X_CONTROL_USB | ||
90 | tristate | ||
91 | |||
92 | config AM335X_PHY_USB | ||
93 | tristate "AM335x USB PHY Driver" | ||
94 | select USB_PHY | ||
95 | select AM335X_CONTROL_USB | ||
96 | select NOP_USB_XCEIV | ||
97 | help | ||
98 | This driver provides PHY support for that phy which part for the | ||
99 | AM335x SoC. | ||
100 | |||
89 | config SAMSUNG_USBPHY | 101 | config SAMSUNG_USBPHY |
90 | tristate | 102 | tristate |
91 | help | 103 | help |
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 24c5816409fd..dae321de3b2e 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile | |||
@@ -16,6 +16,8 @@ obj-$(CONFIG_ISP1301_OMAP) += phy-isp1301-omap.o | |||
16 | obj-$(CONFIG_MV_U3D_PHY) += phy-mv-u3d-usb.o | 16 | obj-$(CONFIG_MV_U3D_PHY) += phy-mv-u3d-usb.o |
17 | obj-$(CONFIG_NOP_USB_XCEIV) += phy-generic.o | 17 | obj-$(CONFIG_NOP_USB_XCEIV) += phy-generic.o |
18 | obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o | 18 | obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o |
19 | obj-$(CONFIG_AM335X_CONTROL_USB) += phy-am335x-control.o | ||
20 | obj-$(CONFIG_AM335X_PHY_USB) += phy-am335x.o | ||
19 | obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o | 21 | obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o |
20 | obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o | 22 | obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o |
21 | obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o | 23 | obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o |
diff --git a/drivers/usb/phy/am35x-phy-control.h b/drivers/usb/phy/am35x-phy-control.h new file mode 100644 index 000000000000..b96594d1962c --- /dev/null +++ b/drivers/usb/phy/am35x-phy-control.h | |||
@@ -0,0 +1,21 @@ | |||
1 | #ifndef _AM335x_PHY_CONTROL_H_ | ||
2 | #define _AM335x_PHY_CONTROL_H_ | ||
3 | |||
4 | struct phy_control { | ||
5 | void (*phy_power)(struct phy_control *phy_ctrl, u32 id, bool on); | ||
6 | void (*phy_wkup)(struct phy_control *phy_ctrl, u32 id, bool on); | ||
7 | }; | ||
8 | |||
9 | static inline void phy_ctrl_power(struct phy_control *phy_ctrl, u32 id, bool on) | ||
10 | { | ||
11 | phy_ctrl->phy_power(phy_ctrl, id, on); | ||
12 | } | ||
13 | |||
14 | static inline void phy_ctrl_wkup(struct phy_control *phy_ctrl, u32 id, bool on) | ||
15 | { | ||
16 | phy_ctrl->phy_wkup(phy_ctrl, id, on); | ||
17 | } | ||
18 | |||
19 | struct phy_control *am335x_get_phy_control(struct device *dev); | ||
20 | |||
21 | #endif | ||
diff --git a/drivers/usb/phy/phy-am335x-control.c b/drivers/usb/phy/phy-am335x-control.c new file mode 100644 index 000000000000..35494f1c33b6 --- /dev/null +++ b/drivers/usb/phy/phy-am335x-control.c | |||
@@ -0,0 +1,136 @@ | |||
1 | #include <linux/module.h> | ||
2 | #include <linux/platform_device.h> | ||
3 | #include <linux/of.h> | ||
4 | #include <linux/io.h> | ||
5 | |||
6 | struct phy_control { | ||
7 | void (*phy_power)(struct phy_control *phy_ctrl, u32 id, bool on); | ||
8 | void (*phy_wkup)(struct phy_control *phy_ctrl, u32 id, bool on); | ||
9 | }; | ||
10 | |||
11 | struct am335x_control_usb { | ||
12 | struct device *dev; | ||
13 | void __iomem *phy_reg; | ||
14 | void __iomem *wkup; | ||
15 | spinlock_t lock; | ||
16 | struct phy_control phy_ctrl; | ||
17 | }; | ||
18 | |||
19 | #define AM335X_USB0_CTRL 0x0 | ||
20 | #define AM335X_USB1_CTRL 0x8 | ||
21 | #define AM335x_USB_WKUP 0x0 | ||
22 | |||
23 | #define USBPHY_CM_PWRDN (1 << 0) | ||
24 | #define USBPHY_OTG_PWRDN (1 << 1) | ||
25 | #define USBPHY_OTGVDET_EN (1 << 19) | ||
26 | #define USBPHY_OTGSESSEND_EN (1 << 20) | ||
27 | |||
28 | static void am335x_phy_power(struct phy_control *phy_ctrl, u32 id, bool on) | ||
29 | { | ||
30 | struct am335x_control_usb *usb_ctrl; | ||
31 | u32 val; | ||
32 | u32 reg; | ||
33 | |||
34 | usb_ctrl = container_of(phy_ctrl, struct am335x_control_usb, phy_ctrl); | ||
35 | |||
36 | switch (id) { | ||
37 | case 0: | ||
38 | reg = AM335X_USB0_CTRL; | ||
39 | break; | ||
40 | case 1: | ||
41 | reg = AM335X_USB1_CTRL; | ||
42 | break; | ||
43 | default: | ||
44 | __WARN(); | ||
45 | return; | ||
46 | } | ||
47 | |||
48 | val = readl(usb_ctrl->phy_reg + reg); | ||
49 | if (on) { | ||
50 | val &= ~(USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN); | ||
51 | val |= USBPHY_OTGVDET_EN | USBPHY_OTGSESSEND_EN; | ||
52 | } else { | ||
53 | val |= USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN; | ||
54 | } | ||
55 | |||
56 | writel(val, usb_ctrl->phy_reg + reg); | ||
57 | } | ||
58 | |||
59 | static const struct phy_control ctrl_am335x = { | ||
60 | .phy_power = am335x_phy_power, | ||
61 | }; | ||
62 | |||
63 | static const struct of_device_id omap_control_usb_id_table[] = { | ||
64 | { .compatible = "ti,am335x-usb-ctrl-module", .data = &ctrl_am335x }, | ||
65 | {} | ||
66 | }; | ||
67 | MODULE_DEVICE_TABLE(of, omap_control_usb_id_table); | ||
68 | |||
69 | static struct platform_driver am335x_control_driver; | ||
70 | static int match(struct device *dev, void *data) | ||
71 | { | ||
72 | struct device_node *node = (struct device_node *)data; | ||
73 | return dev->of_node == node && | ||
74 | dev->driver == &am335x_control_driver.driver; | ||
75 | } | ||
76 | |||
77 | struct phy_control *am335x_get_phy_control(struct device *dev) | ||
78 | { | ||
79 | struct device_node *node; | ||
80 | struct am335x_control_usb *ctrl_usb; | ||
81 | |||
82 | node = of_parse_phandle(dev->of_node, "ti,ctrl_mod", 0); | ||
83 | if (!node) | ||
84 | return NULL; | ||
85 | |||
86 | dev = bus_find_device(&platform_bus_type, NULL, node, match); | ||
87 | ctrl_usb = dev_get_drvdata(dev); | ||
88 | if (!ctrl_usb) | ||
89 | return NULL; | ||
90 | return &ctrl_usb->phy_ctrl; | ||
91 | } | ||
92 | EXPORT_SYMBOL_GPL(am335x_get_phy_control); | ||
93 | |||
94 | static int am335x_control_usb_probe(struct platform_device *pdev) | ||
95 | { | ||
96 | struct resource *res; | ||
97 | struct am335x_control_usb *ctrl_usb; | ||
98 | const struct of_device_id *of_id; | ||
99 | const struct phy_control *phy_ctrl; | ||
100 | |||
101 | of_id = of_match_node(omap_control_usb_id_table, pdev->dev.of_node); | ||
102 | if (!of_id) | ||
103 | return -EINVAL; | ||
104 | |||
105 | phy_ctrl = of_id->data; | ||
106 | |||
107 | ctrl_usb = devm_kzalloc(&pdev->dev, sizeof(*ctrl_usb), GFP_KERNEL); | ||
108 | if (!ctrl_usb) { | ||
109 | dev_err(&pdev->dev, "unable to alloc memory for control usb\n"); | ||
110 | return -ENOMEM; | ||
111 | } | ||
112 | |||
113 | ctrl_usb->dev = &pdev->dev; | ||
114 | |||
115 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl"); | ||
116 | ctrl_usb->phy_reg = devm_ioremap_resource(&pdev->dev, res); | ||
117 | if (IS_ERR(ctrl_usb->phy_reg)) | ||
118 | return PTR_ERR(ctrl_usb->phy_reg); | ||
119 | spin_lock_init(&ctrl_usb->lock); | ||
120 | ctrl_usb->phy_ctrl = *phy_ctrl; | ||
121 | |||
122 | dev_set_drvdata(ctrl_usb->dev, ctrl_usb); | ||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | static struct platform_driver am335x_control_driver = { | ||
127 | .probe = am335x_control_usb_probe, | ||
128 | .driver = { | ||
129 | .name = "am335x-control-usb", | ||
130 | .owner = THIS_MODULE, | ||
131 | .of_match_table = of_match_ptr(omap_control_usb_id_table), | ||
132 | }, | ||
133 | }; | ||
134 | |||
135 | module_platform_driver(am335x_control_driver); | ||
136 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/usb/phy/phy-am335x.c b/drivers/usb/phy/phy-am335x.c new file mode 100644 index 000000000000..c4d614d1f173 --- /dev/null +++ b/drivers/usb/phy/phy-am335x.c | |||
@@ -0,0 +1,99 @@ | |||
1 | #include <linux/module.h> | ||
2 | #include <linux/platform_device.h> | ||
3 | #include <linux/dma-mapping.h> | ||
4 | #include <linux/usb/otg.h> | ||
5 | #include <linux/usb/usb_phy_gen_xceiv.h> | ||
6 | #include <linux/slab.h> | ||
7 | #include <linux/clk.h> | ||
8 | #include <linux/regulator/consumer.h> | ||
9 | #include <linux/of.h> | ||
10 | #include <linux/of_address.h> | ||
11 | |||
12 | #include "am35x-phy-control.h" | ||
13 | #include "phy-generic.h" | ||
14 | |||
15 | struct am335x_phy { | ||
16 | struct usb_phy_gen_xceiv usb_phy_gen; | ||
17 | struct phy_control *phy_ctrl; | ||
18 | int id; | ||
19 | }; | ||
20 | |||
21 | static int am335x_init(struct usb_phy *phy) | ||
22 | { | ||
23 | struct am335x_phy *am_phy = dev_get_drvdata(phy->dev); | ||
24 | |||
25 | phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, true); | ||
26 | return 0; | ||
27 | } | ||
28 | |||
29 | static void am335x_shutdown(struct usb_phy *phy) | ||
30 | { | ||
31 | struct am335x_phy *am_phy = dev_get_drvdata(phy->dev); | ||
32 | |||
33 | phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, false); | ||
34 | } | ||
35 | |||
36 | static int am335x_phy_probe(struct platform_device *pdev) | ||
37 | { | ||
38 | struct am335x_phy *am_phy; | ||
39 | struct device *dev = &pdev->dev; | ||
40 | int ret; | ||
41 | |||
42 | am_phy = devm_kzalloc(dev, sizeof(*am_phy), GFP_KERNEL); | ||
43 | if (!am_phy) | ||
44 | return -ENOMEM; | ||
45 | |||
46 | am_phy->phy_ctrl = am335x_get_phy_control(dev); | ||
47 | if (!am_phy->phy_ctrl) | ||
48 | return -EPROBE_DEFER; | ||
49 | am_phy->id = of_alias_get_id(pdev->dev.of_node, "phy"); | ||
50 | if (am_phy->id < 0) { | ||
51 | dev_err(&pdev->dev, "Missing PHY id: %d\n", am_phy->id); | ||
52 | return am_phy->id; | ||
53 | } | ||
54 | |||
55 | ret = usb_phy_gen_create_phy(dev, &am_phy->usb_phy_gen, | ||
56 | USB_PHY_TYPE_USB2, 0, false, false); | ||
57 | if (ret) | ||
58 | return ret; | ||
59 | |||
60 | ret = usb_add_phy_dev(&am_phy->usb_phy_gen.phy); | ||
61 | if (ret) | ||
62 | goto err_add; | ||
63 | am_phy->usb_phy_gen.phy.init = am335x_init; | ||
64 | am_phy->usb_phy_gen.phy.shutdown = am335x_shutdown; | ||
65 | |||
66 | platform_set_drvdata(pdev, am_phy); | ||
67 | return 0; | ||
68 | |||
69 | err_add: | ||
70 | usb_phy_gen_cleanup_phy(&am_phy->usb_phy_gen); | ||
71 | return ret; | ||
72 | } | ||
73 | |||
74 | static int am335x_phy_remove(struct platform_device *pdev) | ||
75 | { | ||
76 | struct am335x_phy *am_phy = platform_get_drvdata(pdev); | ||
77 | |||
78 | usb_remove_phy(&am_phy->usb_phy_gen.phy); | ||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | static const struct of_device_id am335x_phy_ids[] = { | ||
83 | { .compatible = "ti,am335x-usb-phy" }, | ||
84 | { } | ||
85 | }; | ||
86 | MODULE_DEVICE_TABLE(of, am335x_phy_ids); | ||
87 | |||
88 | static struct platform_driver am335x_phy_driver = { | ||
89 | .probe = am335x_phy_probe, | ||
90 | .remove = am335x_phy_remove, | ||
91 | .driver = { | ||
92 | .name = "am335x-phy-driver", | ||
93 | .owner = THIS_MODULE, | ||
94 | .of_match_table = of_match_ptr(am335x_phy_ids), | ||
95 | }, | ||
96 | }; | ||
97 | |||
98 | module_platform_driver(am335x_phy_driver); | ||
99 | MODULE_LICENSE("GPL v2"); | ||