aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Andrzej Siewior <bigeasy@linutronix.de>2013-07-30 15:43:45 -0400
committerFelipe Balbi <balbi@ti.com>2013-08-09 10:34:15 -0400
commit3bb869c8b3f1a11f1854cd74ebdeb60753614cf8 (patch)
tree124ca4bdf85a58f980e9f00913063191103c6070
parent53b6fc28ea8e9857b6141afb92f3683eab9568ba (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/Kconfig12
-rw-r--r--drivers/usb/phy/Makefile2
-rw-r--r--drivers/usb/phy/am35x-phy-control.h21
-rw-r--r--drivers/usb/phy/phy-am335x-control.c136
-rw-r--r--drivers/usb/phy/phy-am335x.c99
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
89config AM335X_CONTROL_USB
90 tristate
91
92config 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
89config SAMSUNG_USBPHY 101config 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
16obj-$(CONFIG_MV_U3D_PHY) += phy-mv-u3d-usb.o 16obj-$(CONFIG_MV_U3D_PHY) += phy-mv-u3d-usb.o
17obj-$(CONFIG_NOP_USB_XCEIV) += phy-generic.o 17obj-$(CONFIG_NOP_USB_XCEIV) += phy-generic.o
18obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o 18obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o
19obj-$(CONFIG_AM335X_CONTROL_USB) += phy-am335x-control.o
20obj-$(CONFIG_AM335X_PHY_USB) += phy-am335x.o
19obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o 21obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
20obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o 22obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o
21obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o 23obj-$(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
4struct 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
9static 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
14static 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
19struct 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
6struct 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
11struct 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
28static 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
59static const struct phy_control ctrl_am335x = {
60 .phy_power = am335x_phy_power,
61};
62
63static const struct of_device_id omap_control_usb_id_table[] = {
64 { .compatible = "ti,am335x-usb-ctrl-module", .data = &ctrl_am335x },
65 {}
66};
67MODULE_DEVICE_TABLE(of, omap_control_usb_id_table);
68
69static struct platform_driver am335x_control_driver;
70static 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
77struct 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}
92EXPORT_SYMBOL_GPL(am335x_get_phy_control);
93
94static 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
126static 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
135module_platform_driver(am335x_control_driver);
136MODULE_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
15struct am335x_phy {
16 struct usb_phy_gen_xceiv usb_phy_gen;
17 struct phy_control *phy_ctrl;
18 int id;
19};
20
21static 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
29static 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
36static 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
69err_add:
70 usb_phy_gen_cleanup_phy(&am_phy->usb_phy_gen);
71 return ret;
72}
73
74static 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
82static const struct of_device_id am335x_phy_ids[] = {
83 { .compatible = "ti,am335x-usb-phy" },
84 { }
85};
86MODULE_DEVICE_TABLE(of, am335x_phy_ids);
87
88static 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
98module_platform_driver(am335x_phy_driver);
99MODULE_LICENSE("GPL v2");