Michal Nazarewicz | e538dfd | 2011-08-30 17:11:19 +0200 | [diff] [blame] | 1 | /* |
| 2 | * Provides code common for host and device side USB. |
| 3 | * |
| 4 | * This program is free software; you can redistribute it and/or |
| 5 | * modify it under the terms of the GNU General Public License as |
| 6 | * published by the Free Software Foundation, version 2. |
| 7 | * |
| 8 | * If either host side (ie. CONFIG_USB=y) or device side USB stack |
| 9 | * (ie. CONFIG_USB_GADGET=y) is compiled in the kernel, this module is |
| 10 | * compiled-in as well. Otherwise, if either of the two stacks is |
| 11 | * compiled as module, this file is compiled as module as well. |
| 12 | */ |
| 13 | |
| 14 | #include <linux/kernel.h> |
| 15 | #include <linux/module.h> |
Michael Grzeschik | 1c9af65 | 2013-06-13 17:59:55 +0300 | [diff] [blame] | 16 | #include <linux/of.h> |
Michal Nazarewicz | e538dfd | 2011-08-30 17:11:19 +0200 | [diff] [blame] | 17 | #include <linux/usb/ch9.h> |
Michael Grzeschik | 1c9af65 | 2013-06-13 17:59:55 +0300 | [diff] [blame] | 18 | #include <linux/usb/of.h> |
Felipe Balbi | 7009bdd7 | 2013-03-07 10:45:56 +0200 | [diff] [blame] | 19 | #include <linux/usb/otg.h> |
Bin Liu | 98bfb39 | 2015-11-03 11:51:15 -0600 | [diff] [blame] | 20 | #include <linux/of_platform.h> |
Felipe Balbi | 7009bdd7 | 2013-03-07 10:45:56 +0200 | [diff] [blame] | 21 | |
| 22 | const char *usb_otg_state_string(enum usb_otg_state state) |
| 23 | { |
| 24 | static const char *const names[] = { |
| 25 | [OTG_STATE_A_IDLE] = "a_idle", |
| 26 | [OTG_STATE_A_WAIT_VRISE] = "a_wait_vrise", |
| 27 | [OTG_STATE_A_WAIT_BCON] = "a_wait_bcon", |
| 28 | [OTG_STATE_A_HOST] = "a_host", |
| 29 | [OTG_STATE_A_SUSPEND] = "a_suspend", |
| 30 | [OTG_STATE_A_PERIPHERAL] = "a_peripheral", |
| 31 | [OTG_STATE_A_WAIT_VFALL] = "a_wait_vfall", |
| 32 | [OTG_STATE_A_VBUS_ERR] = "a_vbus_err", |
| 33 | [OTG_STATE_B_IDLE] = "b_idle", |
| 34 | [OTG_STATE_B_SRP_INIT] = "b_srp_init", |
| 35 | [OTG_STATE_B_PERIPHERAL] = "b_peripheral", |
| 36 | [OTG_STATE_B_WAIT_ACON] = "b_wait_acon", |
| 37 | [OTG_STATE_B_HOST] = "b_host", |
| 38 | }; |
| 39 | |
| 40 | if (state < 0 || state >= ARRAY_SIZE(names)) |
| 41 | return "UNDEFINED"; |
| 42 | |
| 43 | return names[state]; |
| 44 | } |
| 45 | EXPORT_SYMBOL_GPL(usb_otg_state_string); |
Michal Nazarewicz | e538dfd | 2011-08-30 17:11:19 +0200 | [diff] [blame] | 46 | |
Felipe Balbi | 1494a1f | 2013-06-30 13:56:45 +0300 | [diff] [blame] | 47 | static const char *const speed_names[] = { |
| 48 | [USB_SPEED_UNKNOWN] = "UNKNOWN", |
| 49 | [USB_SPEED_LOW] = "low-speed", |
| 50 | [USB_SPEED_FULL] = "full-speed", |
| 51 | [USB_SPEED_HIGH] = "high-speed", |
| 52 | [USB_SPEED_WIRELESS] = "wireless", |
| 53 | [USB_SPEED_SUPER] = "super-speed", |
Mathias Nyman | 8a1b272 | 2015-12-10 09:59:25 +0200 | [diff] [blame] | 54 | [USB_SPEED_SUPER_PLUS] = "super-speed-plus", |
Felipe Balbi | 1494a1f | 2013-06-30 13:56:45 +0300 | [diff] [blame] | 55 | }; |
| 56 | |
Michal Nazarewicz | e538dfd | 2011-08-30 17:11:19 +0200 | [diff] [blame] | 57 | const char *usb_speed_string(enum usb_device_speed speed) |
| 58 | { |
Felipe Balbi | 1494a1f | 2013-06-30 13:56:45 +0300 | [diff] [blame] | 59 | if (speed < 0 || speed >= ARRAY_SIZE(speed_names)) |
Michal Nazarewicz | e538dfd | 2011-08-30 17:11:19 +0200 | [diff] [blame] | 60 | speed = USB_SPEED_UNKNOWN; |
Felipe Balbi | 1494a1f | 2013-06-30 13:56:45 +0300 | [diff] [blame] | 61 | return speed_names[speed]; |
Michal Nazarewicz | e538dfd | 2011-08-30 17:11:19 +0200 | [diff] [blame] | 62 | } |
| 63 | EXPORT_SYMBOL_GPL(usb_speed_string); |
| 64 | |
Heikki Krogerus | 63863b9 | 2015-09-21 11:14:32 +0300 | [diff] [blame] | 65 | enum usb_device_speed usb_get_maximum_speed(struct device *dev) |
| 66 | { |
| 67 | const char *maximum_speed; |
Heikki Krogerus | efc2cd7 | 2016-03-17 14:22:38 -0700 | [diff] [blame] | 68 | int ret; |
Heikki Krogerus | 63863b9 | 2015-09-21 11:14:32 +0300 | [diff] [blame] | 69 | |
Heikki Krogerus | efc2cd7 | 2016-03-17 14:22:38 -0700 | [diff] [blame] | 70 | ret = device_property_read_string(dev, "maximum-speed", &maximum_speed); |
| 71 | if (ret < 0) |
Heikki Krogerus | 63863b9 | 2015-09-21 11:14:32 +0300 | [diff] [blame] | 72 | return USB_SPEED_UNKNOWN; |
| 73 | |
Heikki Krogerus | efc2cd7 | 2016-03-17 14:22:38 -0700 | [diff] [blame] | 74 | ret = match_string(speed_names, ARRAY_SIZE(speed_names), maximum_speed); |
Heikki Krogerus | 63863b9 | 2015-09-21 11:14:32 +0300 | [diff] [blame] | 75 | |
Heikki Krogerus | efc2cd7 | 2016-03-17 14:22:38 -0700 | [diff] [blame] | 76 | return (ret < 0) ? USB_SPEED_UNKNOWN : ret; |
Heikki Krogerus | 63863b9 | 2015-09-21 11:14:32 +0300 | [diff] [blame] | 77 | } |
| 78 | EXPORT_SYMBOL_GPL(usb_get_maximum_speed); |
| 79 | |
Felipe Balbi | d1e3d75 | 2013-01-24 22:29:48 +0200 | [diff] [blame] | 80 | const char *usb_state_string(enum usb_device_state state) |
| 81 | { |
| 82 | static const char *const names[] = { |
| 83 | [USB_STATE_NOTATTACHED] = "not attached", |
| 84 | [USB_STATE_ATTACHED] = "attached", |
| 85 | [USB_STATE_POWERED] = "powered", |
| 86 | [USB_STATE_RECONNECTING] = "reconnecting", |
| 87 | [USB_STATE_UNAUTHENTICATED] = "unauthenticated", |
| 88 | [USB_STATE_DEFAULT] = "default", |
Peter Chen | f9076e2 | 2014-04-09 14:48:58 +0800 | [diff] [blame] | 89 | [USB_STATE_ADDRESS] = "addressed", |
Felipe Balbi | d1e3d75 | 2013-01-24 22:29:48 +0200 | [diff] [blame] | 90 | [USB_STATE_CONFIGURED] = "configured", |
| 91 | [USB_STATE_SUSPENDED] = "suspended", |
| 92 | }; |
| 93 | |
| 94 | if (state < 0 || state >= ARRAY_SIZE(names)) |
| 95 | return "UNKNOWN"; |
| 96 | |
| 97 | return names[state]; |
| 98 | } |
| 99 | EXPORT_SYMBOL_GPL(usb_state_string); |
| 100 | |
Michael Grzeschik | 1c9af65 | 2013-06-13 17:59:55 +0300 | [diff] [blame] | 101 | static const char *const usb_dr_modes[] = { |
| 102 | [USB_DR_MODE_UNKNOWN] = "", |
| 103 | [USB_DR_MODE_HOST] = "host", |
| 104 | [USB_DR_MODE_PERIPHERAL] = "peripheral", |
| 105 | [USB_DR_MODE_OTG] = "otg", |
| 106 | }; |
| 107 | |
Bin Liu | 98bfb39 | 2015-11-03 11:51:15 -0600 | [diff] [blame] | 108 | static enum usb_dr_mode usb_get_dr_mode_from_string(const char *str) |
| 109 | { |
Heikki Krogerus | efc2cd7 | 2016-03-17 14:22:38 -0700 | [diff] [blame] | 110 | int ret; |
Bin Liu | 98bfb39 | 2015-11-03 11:51:15 -0600 | [diff] [blame] | 111 | |
Heikki Krogerus | efc2cd7 | 2016-03-17 14:22:38 -0700 | [diff] [blame] | 112 | ret = match_string(usb_dr_modes, ARRAY_SIZE(usb_dr_modes), str); |
| 113 | return (ret < 0) ? USB_DR_MODE_UNKNOWN : ret; |
Bin Liu | 98bfb39 | 2015-11-03 11:51:15 -0600 | [diff] [blame] | 114 | } |
| 115 | |
Heikki Krogerus | 06e7114 | 2015-09-21 11:14:34 +0300 | [diff] [blame] | 116 | enum usb_dr_mode usb_get_dr_mode(struct device *dev) |
Michael Grzeschik | 1c9af65 | 2013-06-13 17:59:55 +0300 | [diff] [blame] | 117 | { |
| 118 | const char *dr_mode; |
Bin Liu | 98bfb39 | 2015-11-03 11:51:15 -0600 | [diff] [blame] | 119 | int err; |
Michael Grzeschik | 1c9af65 | 2013-06-13 17:59:55 +0300 | [diff] [blame] | 120 | |
Heikki Krogerus | 06e7114 | 2015-09-21 11:14:34 +0300 | [diff] [blame] | 121 | err = device_property_read_string(dev, "dr_mode", &dr_mode); |
Michael Grzeschik | 1c9af65 | 2013-06-13 17:59:55 +0300 | [diff] [blame] | 122 | if (err < 0) |
| 123 | return USB_DR_MODE_UNKNOWN; |
| 124 | |
Bin Liu | 98bfb39 | 2015-11-03 11:51:15 -0600 | [diff] [blame] | 125 | return usb_get_dr_mode_from_string(dr_mode); |
Michael Grzeschik | 1c9af65 | 2013-06-13 17:59:55 +0300 | [diff] [blame] | 126 | } |
Heikki Krogerus | 06e7114 | 2015-09-21 11:14:34 +0300 | [diff] [blame] | 127 | EXPORT_SYMBOL_GPL(usb_get_dr_mode); |
Felipe Balbi | 1494a1f | 2013-06-30 13:56:45 +0300 | [diff] [blame] | 128 | |
Heikki Krogerus | 06e7114 | 2015-09-21 11:14:34 +0300 | [diff] [blame] | 129 | #ifdef CONFIG_OF |
Felipe Balbi | 1494a1f | 2013-06-30 13:56:45 +0300 | [diff] [blame] | 130 | /** |
Bin Liu | 98bfb39 | 2015-11-03 11:51:15 -0600 | [diff] [blame] | 131 | * of_usb_get_dr_mode_by_phy - Get dual role mode for the controller device |
| 132 | * which is associated with the given phy device_node |
| 133 | * @np: Pointer to the given phy device_node |
| 134 | * |
| 135 | * In dts a usb controller associates with phy devices. The function gets |
| 136 | * the string from property 'dr_mode' of the controller associated with the |
| 137 | * given phy device node, and returns the correspondig enum usb_dr_mode. |
| 138 | */ |
| 139 | enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *phy_np) |
| 140 | { |
| 141 | struct device_node *controller = NULL; |
| 142 | struct device_node *phy; |
| 143 | const char *dr_mode; |
| 144 | int index; |
| 145 | int err; |
| 146 | |
| 147 | do { |
| 148 | controller = of_find_node_with_property(controller, "phys"); |
| 149 | index = 0; |
| 150 | do { |
| 151 | phy = of_parse_phandle(controller, "phys", index); |
| 152 | of_node_put(phy); |
| 153 | if (phy == phy_np) |
| 154 | goto finish; |
| 155 | index++; |
| 156 | } while (phy); |
| 157 | } while (controller); |
| 158 | |
| 159 | finish: |
| 160 | err = of_property_read_string(controller, "dr_mode", &dr_mode); |
| 161 | of_node_put(controller); |
| 162 | |
| 163 | if (err < 0) |
| 164 | return USB_DR_MODE_UNKNOWN; |
| 165 | |
| 166 | return usb_get_dr_mode_from_string(dr_mode); |
| 167 | } |
| 168 | EXPORT_SYMBOL_GPL(of_usb_get_dr_mode_by_phy); |
| 169 | |
| 170 | /** |
Peter Chen | 05f8b35 | 2014-08-19 09:51:55 +0800 | [diff] [blame] | 171 | * of_usb_host_tpl_support - to get if Targeted Peripheral List is supported |
| 172 | * for given targeted hosts (non-PC hosts) |
| 173 | * @np: Pointer to the given device_node |
| 174 | * |
| 175 | * The function gets if the targeted hosts support TPL or not |
| 176 | */ |
| 177 | bool of_usb_host_tpl_support(struct device_node *np) |
| 178 | { |
| 179 | if (of_find_property(np, "tpl-support", NULL)) |
| 180 | return true; |
| 181 | |
| 182 | return false; |
| 183 | } |
| 184 | EXPORT_SYMBOL_GPL(of_usb_host_tpl_support); |
Li Jun | 929412d | 2015-07-09 15:18:44 +0800 | [diff] [blame] | 185 | |
| 186 | /** |
| 187 | * of_usb_update_otg_caps - to update usb otg capabilities according to |
| 188 | * the passed properties in DT. |
| 189 | * @np: Pointer to the given device_node |
| 190 | * @otg_caps: Pointer to the target usb_otg_caps to be set |
| 191 | * |
| 192 | * The function updates the otg capabilities |
| 193 | */ |
| 194 | int of_usb_update_otg_caps(struct device_node *np, |
| 195 | struct usb_otg_caps *otg_caps) |
| 196 | { |
| 197 | u32 otg_rev; |
| 198 | |
| 199 | if (!otg_caps) |
| 200 | return -EINVAL; |
| 201 | |
| 202 | if (!of_property_read_u32(np, "otg-rev", &otg_rev)) { |
| 203 | switch (otg_rev) { |
| 204 | case 0x0100: |
| 205 | case 0x0120: |
| 206 | case 0x0130: |
| 207 | case 0x0200: |
| 208 | /* Choose the lesser one if it's already been set */ |
| 209 | if (otg_caps->otg_rev) |
| 210 | otg_caps->otg_rev = min_t(u16, otg_rev, |
| 211 | otg_caps->otg_rev); |
| 212 | else |
| 213 | otg_caps->otg_rev = otg_rev; |
| 214 | break; |
| 215 | default: |
| 216 | pr_err("%s: unsupported otg-rev: 0x%x\n", |
| 217 | np->full_name, otg_rev); |
| 218 | return -EINVAL; |
| 219 | } |
| 220 | } else { |
| 221 | /* |
| 222 | * otg-rev is mandatory for otg properties, if not passed |
| 223 | * we set it to be 0 and assume it's a legacy otg device. |
| 224 | * Non-dt platform can set it afterwards. |
| 225 | */ |
| 226 | otg_caps->otg_rev = 0; |
| 227 | } |
| 228 | |
| 229 | if (of_find_property(np, "hnp-disable", NULL)) |
| 230 | otg_caps->hnp_support = false; |
| 231 | if (of_find_property(np, "srp-disable", NULL)) |
| 232 | otg_caps->srp_support = false; |
| 233 | if (of_find_property(np, "adp-disable", NULL) || |
| 234 | (otg_caps->otg_rev < 0x0200)) |
| 235 | otg_caps->adp_support = false; |
| 236 | |
| 237 | return 0; |
| 238 | } |
| 239 | EXPORT_SYMBOL_GPL(of_usb_update_otg_caps); |
| 240 | |
Michael Grzeschik | 1c9af65 | 2013-06-13 17:59:55 +0300 | [diff] [blame] | 241 | #endif |
| 242 | |
Michal Nazarewicz | e538dfd | 2011-08-30 17:11:19 +0200 | [diff] [blame] | 243 | MODULE_LICENSE("GPL"); |