1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * OMAP OTG controller driver |
4 | * |
5 | * Based on code from tahvo-usb.c and isp1301_omap.c drivers. |
6 | * |
7 | * Copyright (C) 2005-2006 Nokia Corporation |
8 | * Copyright (C) 2004 Texas Instruments |
9 | * Copyright (C) 2004 David Brownell |
10 | */ |
11 | |
12 | #include <linux/io.h> |
13 | #include <linux/err.h> |
14 | #include <linux/extcon.h> |
15 | #include <linux/kernel.h> |
16 | #include <linux/module.h> |
17 | #include <linux/interrupt.h> |
18 | #include <linux/platform_device.h> |
19 | #include <linux/platform_data/usb-omap1.h> |
20 | |
21 | struct otg_device { |
22 | void __iomem *base; |
23 | bool id; |
24 | bool vbus; |
25 | struct extcon_dev *extcon; |
26 | struct notifier_block vbus_nb; |
27 | struct notifier_block id_nb; |
28 | }; |
29 | |
30 | #define OMAP_OTG_CTRL 0x0c |
31 | #define OMAP_OTG_ASESSVLD (1 << 20) |
32 | #define OMAP_OTG_BSESSEND (1 << 19) |
33 | #define OMAP_OTG_BSESSVLD (1 << 18) |
34 | #define OMAP_OTG_VBUSVLD (1 << 17) |
35 | #define OMAP_OTG_ID (1 << 16) |
36 | #define OMAP_OTG_XCEIV_OUTPUTS \ |
37 | (OMAP_OTG_ASESSVLD | OMAP_OTG_BSESSEND | OMAP_OTG_BSESSVLD | \ |
38 | OMAP_OTG_VBUSVLD | OMAP_OTG_ID) |
39 | |
40 | static void omap_otg_ctrl(struct otg_device *otg_dev, u32 outputs) |
41 | { |
42 | u32 l; |
43 | |
44 | l = readl(addr: otg_dev->base + OMAP_OTG_CTRL); |
45 | l &= ~OMAP_OTG_XCEIV_OUTPUTS; |
46 | l |= outputs; |
47 | writel(val: l, addr: otg_dev->base + OMAP_OTG_CTRL); |
48 | } |
49 | |
50 | static void omap_otg_set_mode(struct otg_device *otg_dev) |
51 | { |
52 | if (!otg_dev->id && otg_dev->vbus) |
53 | /* Set B-session valid. */ |
54 | omap_otg_ctrl(otg_dev, OMAP_OTG_ID | OMAP_OTG_BSESSVLD); |
55 | else if (otg_dev->vbus) |
56 | /* Set A-session valid. */ |
57 | omap_otg_ctrl(otg_dev, OMAP_OTG_ASESSVLD); |
58 | else if (!otg_dev->id) |
59 | /* Set B-session end to indicate no VBUS. */ |
60 | omap_otg_ctrl(otg_dev, OMAP_OTG_ID | OMAP_OTG_BSESSEND); |
61 | } |
62 | |
63 | static int omap_otg_id_notifier(struct notifier_block *nb, |
64 | unsigned long event, void *ptr) |
65 | { |
66 | struct otg_device *otg_dev = container_of(nb, struct otg_device, id_nb); |
67 | |
68 | otg_dev->id = event; |
69 | omap_otg_set_mode(otg_dev); |
70 | |
71 | return NOTIFY_DONE; |
72 | } |
73 | |
74 | static int omap_otg_vbus_notifier(struct notifier_block *nb, |
75 | unsigned long event, void *ptr) |
76 | { |
77 | struct otg_device *otg_dev = container_of(nb, struct otg_device, |
78 | vbus_nb); |
79 | |
80 | otg_dev->vbus = event; |
81 | omap_otg_set_mode(otg_dev); |
82 | |
83 | return NOTIFY_DONE; |
84 | } |
85 | |
86 | static int omap_otg_probe(struct platform_device *pdev) |
87 | { |
88 | const struct omap_usb_config *config = pdev->dev.platform_data; |
89 | struct otg_device *otg_dev; |
90 | struct extcon_dev *extcon; |
91 | int ret; |
92 | u32 rev; |
93 | |
94 | if (!config || !config->extcon) |
95 | return -ENODEV; |
96 | |
97 | extcon = extcon_get_extcon_dev(extcon_name: config->extcon); |
98 | if (IS_ERR(ptr: extcon)) |
99 | return PTR_ERR(ptr: extcon); |
100 | |
101 | otg_dev = devm_kzalloc(dev: &pdev->dev, size: sizeof(*otg_dev), GFP_KERNEL); |
102 | if (!otg_dev) |
103 | return -ENOMEM; |
104 | |
105 | otg_dev->base = devm_ioremap_resource(dev: &pdev->dev, res: &pdev->resource[0]); |
106 | if (IS_ERR(ptr: otg_dev->base)) |
107 | return PTR_ERR(ptr: otg_dev->base); |
108 | |
109 | otg_dev->extcon = extcon; |
110 | otg_dev->id_nb.notifier_call = omap_otg_id_notifier; |
111 | otg_dev->vbus_nb.notifier_call = omap_otg_vbus_notifier; |
112 | |
113 | ret = devm_extcon_register_notifier(dev: &pdev->dev, edev: extcon, |
114 | EXTCON_USB_HOST, nb: &otg_dev->id_nb); |
115 | if (ret) |
116 | return ret; |
117 | |
118 | ret = devm_extcon_register_notifier(dev: &pdev->dev, edev: extcon, |
119 | EXTCON_USB, nb: &otg_dev->vbus_nb); |
120 | if (ret) { |
121 | return ret; |
122 | } |
123 | |
124 | otg_dev->id = extcon_get_state(edev: extcon, EXTCON_USB_HOST); |
125 | otg_dev->vbus = extcon_get_state(edev: extcon, EXTCON_USB); |
126 | omap_otg_set_mode(otg_dev); |
127 | |
128 | rev = readl(addr: otg_dev->base); |
129 | |
130 | dev_info(&pdev->dev, |
131 | "OMAP USB OTG controller rev %d.%d (%s, id=%d, vbus=%d)\n" , |
132 | (rev >> 4) & 0xf, rev & 0xf, config->extcon, otg_dev->id, |
133 | otg_dev->vbus); |
134 | |
135 | platform_set_drvdata(pdev, data: otg_dev); |
136 | |
137 | return 0; |
138 | } |
139 | |
140 | static struct platform_driver omap_otg_driver = { |
141 | .probe = omap_otg_probe, |
142 | .driver = { |
143 | .name = "omap_otg" , |
144 | }, |
145 | }; |
146 | module_platform_driver(omap_otg_driver); |
147 | |
148 | MODULE_DESCRIPTION("OMAP USB OTG controller driver" ); |
149 | MODULE_LICENSE("GPL" ); |
150 | MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>" ); |
151 | |