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 |
Hans de Goede | ce15ed4 | 2016-06-10 11:46:25 +0200 | [diff] [blame] | 134 | * @arg0: phandle args[0] for phy's with #phy-cells >= 1, or -1 for |
| 135 | * phys which do not have phy-cells |
Bin Liu | 98bfb39 | 2015-11-03 11:51:15 -0600 | [diff] [blame] | 136 | * |
| 137 | * In dts a usb controller associates with phy devices. The function gets |
| 138 | * the string from property 'dr_mode' of the controller associated with the |
| 139 | * given phy device node, and returns the correspondig enum usb_dr_mode. |
| 140 | */ |
Hans de Goede | ce15ed4 | 2016-06-10 11:46:25 +0200 | [diff] [blame] | 141 | enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *np, int arg0) |
Bin Liu | 98bfb39 | 2015-11-03 11:51:15 -0600 | [diff] [blame] | 142 | { |
| 143 | struct device_node *controller = NULL; |
Hans de Goede | ce15ed4 | 2016-06-10 11:46:25 +0200 | [diff] [blame] | 144 | struct of_phandle_args args; |
Bin Liu | 98bfb39 | 2015-11-03 11:51:15 -0600 | [diff] [blame] | 145 | const char *dr_mode; |
| 146 | int index; |
| 147 | int err; |
| 148 | |
| 149 | do { |
| 150 | controller = of_find_node_with_property(controller, "phys"); |
| 151 | index = 0; |
| 152 | do { |
Hans de Goede | ce15ed4 | 2016-06-10 11:46:25 +0200 | [diff] [blame] | 153 | if (arg0 == -1) { |
| 154 | args.np = of_parse_phandle(controller, "phys", |
| 155 | index); |
| 156 | args.args_count = 0; |
| 157 | } else { |
| 158 | err = of_parse_phandle_with_args(controller, |
| 159 | "phys", "#phy-cells", |
| 160 | index, &args); |
| 161 | if (err) |
| 162 | break; |
| 163 | } |
| 164 | |
| 165 | of_node_put(args.np); |
| 166 | if (args.np == np && (args.args_count == 0 || |
| 167 | args.args[0] == arg0)) |
Bin Liu | 98bfb39 | 2015-11-03 11:51:15 -0600 | [diff] [blame] | 168 | goto finish; |
| 169 | index++; |
Hans de Goede | ce15ed4 | 2016-06-10 11:46:25 +0200 | [diff] [blame] | 170 | } while (args.np); |
Bin Liu | 98bfb39 | 2015-11-03 11:51:15 -0600 | [diff] [blame] | 171 | } while (controller); |
| 172 | |
| 173 | finish: |
| 174 | err = of_property_read_string(controller, "dr_mode", &dr_mode); |
| 175 | of_node_put(controller); |
| 176 | |
| 177 | if (err < 0) |
| 178 | return USB_DR_MODE_UNKNOWN; |
| 179 | |
| 180 | return usb_get_dr_mode_from_string(dr_mode); |
| 181 | } |
| 182 | EXPORT_SYMBOL_GPL(of_usb_get_dr_mode_by_phy); |
| 183 | |
| 184 | /** |
Peter Chen | 05f8b35 | 2014-08-19 09:51:55 +0800 | [diff] [blame] | 185 | * of_usb_host_tpl_support - to get if Targeted Peripheral List is supported |
| 186 | * for given targeted hosts (non-PC hosts) |
| 187 | * @np: Pointer to the given device_node |
| 188 | * |
| 189 | * The function gets if the targeted hosts support TPL or not |
| 190 | */ |
| 191 | bool of_usb_host_tpl_support(struct device_node *np) |
| 192 | { |
| 193 | if (of_find_property(np, "tpl-support", NULL)) |
| 194 | return true; |
| 195 | |
| 196 | return false; |
| 197 | } |
| 198 | EXPORT_SYMBOL_GPL(of_usb_host_tpl_support); |
Li Jun | 929412d | 2015-07-09 15:18:44 +0800 | [diff] [blame] | 199 | |
| 200 | /** |
| 201 | * of_usb_update_otg_caps - to update usb otg capabilities according to |
| 202 | * the passed properties in DT. |
| 203 | * @np: Pointer to the given device_node |
| 204 | * @otg_caps: Pointer to the target usb_otg_caps to be set |
| 205 | * |
| 206 | * The function updates the otg capabilities |
| 207 | */ |
| 208 | int of_usb_update_otg_caps(struct device_node *np, |
| 209 | struct usb_otg_caps *otg_caps) |
| 210 | { |
| 211 | u32 otg_rev; |
| 212 | |
| 213 | if (!otg_caps) |
| 214 | return -EINVAL; |
| 215 | |
| 216 | if (!of_property_read_u32(np, "otg-rev", &otg_rev)) { |
| 217 | switch (otg_rev) { |
| 218 | case 0x0100: |
| 219 | case 0x0120: |
| 220 | case 0x0130: |
| 221 | case 0x0200: |
| 222 | /* Choose the lesser one if it's already been set */ |
| 223 | if (otg_caps->otg_rev) |
| 224 | otg_caps->otg_rev = min_t(u16, otg_rev, |
| 225 | otg_caps->otg_rev); |
| 226 | else |
| 227 | otg_caps->otg_rev = otg_rev; |
| 228 | break; |
| 229 | default: |
| 230 | pr_err("%s: unsupported otg-rev: 0x%x\n", |
| 231 | np->full_name, otg_rev); |
| 232 | return -EINVAL; |
| 233 | } |
| 234 | } else { |
| 235 | /* |
| 236 | * otg-rev is mandatory for otg properties, if not passed |
| 237 | * we set it to be 0 and assume it's a legacy otg device. |
| 238 | * Non-dt platform can set it afterwards. |
| 239 | */ |
| 240 | otg_caps->otg_rev = 0; |
| 241 | } |
| 242 | |
| 243 | if (of_find_property(np, "hnp-disable", NULL)) |
| 244 | otg_caps->hnp_support = false; |
| 245 | if (of_find_property(np, "srp-disable", NULL)) |
| 246 | otg_caps->srp_support = false; |
| 247 | if (of_find_property(np, "adp-disable", NULL) || |
| 248 | (otg_caps->otg_rev < 0x0200)) |
| 249 | otg_caps->adp_support = false; |
| 250 | |
| 251 | return 0; |
| 252 | } |
| 253 | EXPORT_SYMBOL_GPL(of_usb_update_otg_caps); |
| 254 | |
Michael Grzeschik | 1c9af65 | 2013-06-13 17:59:55 +0300 | [diff] [blame] | 255 | #endif |
| 256 | |
Michal Nazarewicz | e538dfd | 2011-08-30 17:11:19 +0200 | [diff] [blame] | 257 | MODULE_LICENSE("GPL"); |