18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Intel Wireless WiMAX Connection 2400m 48c2ecf20Sopenharmony_ci * Linux driver model glue for USB device, reset & fw upload 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com> 78c2ecf20Sopenharmony_ci * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> 88c2ecf20Sopenharmony_ci * Yanir Lubetkin <yanirx.lubetkin@intel.com> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * See i2400m-usb.h for a general description of this driver. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * This file implements driver model glue, and hook ups for the 138c2ecf20Sopenharmony_ci * generic driver to implement the bus-specific functions (device 148c2ecf20Sopenharmony_ci * communication setup/tear down, firmware upload and resetting). 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * ROADMAP 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * i2400mu_probe() 198c2ecf20Sopenharmony_ci * alloc_netdev()... 208c2ecf20Sopenharmony_ci * i2400mu_netdev_setup() 218c2ecf20Sopenharmony_ci * i2400mu_init() 228c2ecf20Sopenharmony_ci * i2400m_netdev_setup() 238c2ecf20Sopenharmony_ci * i2400m_setup()... 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * i2400mu_disconnect 268c2ecf20Sopenharmony_ci * i2400m_release() 278c2ecf20Sopenharmony_ci * free_netdev() 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * i2400mu_suspend() 308c2ecf20Sopenharmony_ci * i2400m_cmd_enter_powersave() 318c2ecf20Sopenharmony_ci * i2400mu_notification_release() 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * i2400mu_resume() 348c2ecf20Sopenharmony_ci * i2400mu_notification_setup() 358c2ecf20Sopenharmony_ci * 368c2ecf20Sopenharmony_ci * i2400mu_bus_dev_start() Called by i2400m_dev_start() [who is 378c2ecf20Sopenharmony_ci * i2400mu_tx_setup() called by i2400m_setup()] 388c2ecf20Sopenharmony_ci * i2400mu_rx_setup() 398c2ecf20Sopenharmony_ci * i2400mu_notification_setup() 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * i2400mu_bus_dev_stop() Called by i2400m_dev_stop() [who is 428c2ecf20Sopenharmony_ci * i2400mu_notification_release() called by i2400m_release()] 438c2ecf20Sopenharmony_ci * i2400mu_rx_release() 448c2ecf20Sopenharmony_ci * i2400mu_tx_release() 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci * i2400mu_bus_reset() Called by i2400m_reset 478c2ecf20Sopenharmony_ci * __i2400mu_reset() 488c2ecf20Sopenharmony_ci * __i2400mu_send_barker() 498c2ecf20Sopenharmony_ci * usb_reset_device() 508c2ecf20Sopenharmony_ci */ 518c2ecf20Sopenharmony_ci#include "i2400m-usb.h" 528c2ecf20Sopenharmony_ci#include <linux/wimax/i2400m.h> 538c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 548c2ecf20Sopenharmony_ci#include <linux/slab.h> 558c2ecf20Sopenharmony_ci#include <linux/module.h> 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define D_SUBMODULE usb 598c2ecf20Sopenharmony_ci#include "usb-debug-levels.h" 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic char i2400mu_debug_params[128]; 628c2ecf20Sopenharmony_cimodule_param_string(debug, i2400mu_debug_params, sizeof(i2400mu_debug_params), 638c2ecf20Sopenharmony_ci 0644); 648c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, 658c2ecf20Sopenharmony_ci "String of space-separated NAME:VALUE pairs, where NAMEs " 668c2ecf20Sopenharmony_ci "are the different debug submodules and VALUE are the " 678c2ecf20Sopenharmony_ci "initial debug value to set."); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* Our firmware file name */ 708c2ecf20Sopenharmony_cistatic const char *i2400mu_bus_fw_names_5x50[] = { 718c2ecf20Sopenharmony_ci#define I2400MU_FW_FILE_NAME_v1_5 "i2400m-fw-usb-1.5.sbcf" 728c2ecf20Sopenharmony_ci I2400MU_FW_FILE_NAME_v1_5, 738c2ecf20Sopenharmony_ci#define I2400MU_FW_FILE_NAME_v1_4 "i2400m-fw-usb-1.4.sbcf" 748c2ecf20Sopenharmony_ci I2400MU_FW_FILE_NAME_v1_4, 758c2ecf20Sopenharmony_ci NULL, 768c2ecf20Sopenharmony_ci}; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic const char *i2400mu_bus_fw_names_6050[] = { 808c2ecf20Sopenharmony_ci#define I6050U_FW_FILE_NAME_v1_5 "i6050-fw-usb-1.5.sbcf" 818c2ecf20Sopenharmony_ci I6050U_FW_FILE_NAME_v1_5, 828c2ecf20Sopenharmony_ci NULL, 838c2ecf20Sopenharmony_ci}; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic 878c2ecf20Sopenharmony_ciint i2400mu_bus_dev_start(struct i2400m *i2400m) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci int result; 908c2ecf20Sopenharmony_ci struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m); 918c2ecf20Sopenharmony_ci struct device *dev = &i2400mu->usb_iface->dev; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci d_fnstart(3, dev, "(i2400m %p)\n", i2400m); 948c2ecf20Sopenharmony_ci result = i2400mu_tx_setup(i2400mu); 958c2ecf20Sopenharmony_ci if (result < 0) 968c2ecf20Sopenharmony_ci goto error_usb_tx_setup; 978c2ecf20Sopenharmony_ci result = i2400mu_rx_setup(i2400mu); 988c2ecf20Sopenharmony_ci if (result < 0) 998c2ecf20Sopenharmony_ci goto error_usb_rx_setup; 1008c2ecf20Sopenharmony_ci result = i2400mu_notification_setup(i2400mu); 1018c2ecf20Sopenharmony_ci if (result < 0) 1028c2ecf20Sopenharmony_ci goto error_notif_setup; 1038c2ecf20Sopenharmony_ci d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); 1048c2ecf20Sopenharmony_ci return result; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cierror_notif_setup: 1078c2ecf20Sopenharmony_ci i2400mu_rx_release(i2400mu); 1088c2ecf20Sopenharmony_cierror_usb_rx_setup: 1098c2ecf20Sopenharmony_ci i2400mu_tx_release(i2400mu); 1108c2ecf20Sopenharmony_cierror_usb_tx_setup: 1118c2ecf20Sopenharmony_ci d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); 1128c2ecf20Sopenharmony_ci return result; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic 1178c2ecf20Sopenharmony_civoid i2400mu_bus_dev_stop(struct i2400m *i2400m) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m); 1208c2ecf20Sopenharmony_ci struct device *dev = &i2400mu->usb_iface->dev; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci d_fnstart(3, dev, "(i2400m %p)\n", i2400m); 1238c2ecf20Sopenharmony_ci i2400mu_notification_release(i2400mu); 1248c2ecf20Sopenharmony_ci i2400mu_rx_release(i2400mu); 1258c2ecf20Sopenharmony_ci i2400mu_tx_release(i2400mu); 1268c2ecf20Sopenharmony_ci d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/* 1318c2ecf20Sopenharmony_ci * Sends a barker buffer to the device 1328c2ecf20Sopenharmony_ci * 1338c2ecf20Sopenharmony_ci * This helper will allocate a kmalloced buffer and use it to transmit 1348c2ecf20Sopenharmony_ci * (then free it). Reason for this is that other arches cannot use 1358c2ecf20Sopenharmony_ci * stack/vmalloc/text areas for DMA transfers. 1368c2ecf20Sopenharmony_ci * 1378c2ecf20Sopenharmony_ci * Error recovery here is simpler: anything is considered a hard error 1388c2ecf20Sopenharmony_ci * and will move the reset code to use a last-resort bus-based reset. 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_cistatic 1418c2ecf20Sopenharmony_ciint __i2400mu_send_barker(struct i2400mu *i2400mu, 1428c2ecf20Sopenharmony_ci const __le32 *barker, 1438c2ecf20Sopenharmony_ci size_t barker_size, 1448c2ecf20Sopenharmony_ci unsigned endpoint) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *epd = NULL; 1478c2ecf20Sopenharmony_ci int pipe, actual_len, ret; 1488c2ecf20Sopenharmony_ci struct device *dev = &i2400mu->usb_iface->dev; 1498c2ecf20Sopenharmony_ci void *buffer; 1508c2ecf20Sopenharmony_ci int do_autopm = 1; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci ret = usb_autopm_get_interface(i2400mu->usb_iface); 1538c2ecf20Sopenharmony_ci if (ret < 0) { 1548c2ecf20Sopenharmony_ci dev_err(dev, "RESET: can't get autopm: %d\n", ret); 1558c2ecf20Sopenharmony_ci do_autopm = 0; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci ret = -ENOMEM; 1588c2ecf20Sopenharmony_ci buffer = kmalloc(barker_size, GFP_KERNEL); 1598c2ecf20Sopenharmony_ci if (buffer == NULL) 1608c2ecf20Sopenharmony_ci goto error_kzalloc; 1618c2ecf20Sopenharmony_ci epd = usb_get_epd(i2400mu->usb_iface, endpoint); 1628c2ecf20Sopenharmony_ci pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress); 1638c2ecf20Sopenharmony_ci memcpy(buffer, barker, barker_size); 1648c2ecf20Sopenharmony_ciretry: 1658c2ecf20Sopenharmony_ci ret = usb_bulk_msg(i2400mu->usb_dev, pipe, buffer, barker_size, 1668c2ecf20Sopenharmony_ci &actual_len, 200); 1678c2ecf20Sopenharmony_ci switch (ret) { 1688c2ecf20Sopenharmony_ci case 0: 1698c2ecf20Sopenharmony_ci if (actual_len != barker_size) { /* Too short? drop it */ 1708c2ecf20Sopenharmony_ci dev_err(dev, "E: %s: short write (%d B vs %zu " 1718c2ecf20Sopenharmony_ci "expected)\n", 1728c2ecf20Sopenharmony_ci __func__, actual_len, barker_size); 1738c2ecf20Sopenharmony_ci ret = -EIO; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci break; 1768c2ecf20Sopenharmony_ci case -EPIPE: 1778c2ecf20Sopenharmony_ci /* 1788c2ecf20Sopenharmony_ci * Stall -- maybe the device is choking with our 1798c2ecf20Sopenharmony_ci * requests. Clear it and give it some time. If they 1808c2ecf20Sopenharmony_ci * happen to often, it might be another symptom, so we 1818c2ecf20Sopenharmony_ci * reset. 1828c2ecf20Sopenharmony_ci * 1838c2ecf20Sopenharmony_ci * No error handling for usb_clear_halt(0; if it 1848c2ecf20Sopenharmony_ci * works, the retry works; if it fails, this switch 1858c2ecf20Sopenharmony_ci * does the error handling for us. 1868c2ecf20Sopenharmony_ci */ 1878c2ecf20Sopenharmony_ci if (edc_inc(&i2400mu->urb_edc, 1888c2ecf20Sopenharmony_ci 10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) { 1898c2ecf20Sopenharmony_ci dev_err(dev, "E: %s: too many stalls in " 1908c2ecf20Sopenharmony_ci "URB; resetting device\n", __func__); 1918c2ecf20Sopenharmony_ci usb_queue_reset_device(i2400mu->usb_iface); 1928c2ecf20Sopenharmony_ci /* fallthrough */ 1938c2ecf20Sopenharmony_ci } else { 1948c2ecf20Sopenharmony_ci usb_clear_halt(i2400mu->usb_dev, pipe); 1958c2ecf20Sopenharmony_ci msleep(10); /* give the device some time */ 1968c2ecf20Sopenharmony_ci goto retry; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci fallthrough; 1998c2ecf20Sopenharmony_ci case -EINVAL: /* while removing driver */ 2008c2ecf20Sopenharmony_ci case -ENODEV: /* dev disconnect ... */ 2018c2ecf20Sopenharmony_ci case -ENOENT: /* just ignore it */ 2028c2ecf20Sopenharmony_ci case -ESHUTDOWN: /* and exit */ 2038c2ecf20Sopenharmony_ci case -ECONNRESET: 2048c2ecf20Sopenharmony_ci ret = -ESHUTDOWN; 2058c2ecf20Sopenharmony_ci break; 2068c2ecf20Sopenharmony_ci default: /* Some error? */ 2078c2ecf20Sopenharmony_ci if (edc_inc(&i2400mu->urb_edc, 2088c2ecf20Sopenharmony_ci EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) { 2098c2ecf20Sopenharmony_ci dev_err(dev, "E: %s: maximum errors in URB " 2108c2ecf20Sopenharmony_ci "exceeded; resetting device\n", 2118c2ecf20Sopenharmony_ci __func__); 2128c2ecf20Sopenharmony_ci usb_queue_reset_device(i2400mu->usb_iface); 2138c2ecf20Sopenharmony_ci } else { 2148c2ecf20Sopenharmony_ci dev_warn(dev, "W: %s: cannot send URB: %d\n", 2158c2ecf20Sopenharmony_ci __func__, ret); 2168c2ecf20Sopenharmony_ci goto retry; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci kfree(buffer); 2208c2ecf20Sopenharmony_cierror_kzalloc: 2218c2ecf20Sopenharmony_ci if (do_autopm) 2228c2ecf20Sopenharmony_ci usb_autopm_put_interface(i2400mu->usb_iface); 2238c2ecf20Sopenharmony_ci return ret; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci/* 2288c2ecf20Sopenharmony_ci * Reset a device at different levels (warm, cold or bus) 2298c2ecf20Sopenharmony_ci * 2308c2ecf20Sopenharmony_ci * @i2400m: device descriptor 2318c2ecf20Sopenharmony_ci * @reset_type: soft, warm or bus reset (I2400M_RT_WARM/SOFT/BUS) 2328c2ecf20Sopenharmony_ci * 2338c2ecf20Sopenharmony_ci * Warm and cold resets get a USB reset if they fail. 2348c2ecf20Sopenharmony_ci * 2358c2ecf20Sopenharmony_ci * Warm reset: 2368c2ecf20Sopenharmony_ci * 2378c2ecf20Sopenharmony_ci * The device will be fully reset internally, but won't be 2388c2ecf20Sopenharmony_ci * disconnected from the USB bus (so no reenumeration will 2398c2ecf20Sopenharmony_ci * happen). Firmware upload will be necessary. 2408c2ecf20Sopenharmony_ci * 2418c2ecf20Sopenharmony_ci * The device will send a reboot barker in the notification endpoint 2428c2ecf20Sopenharmony_ci * that will trigger the driver to reinitialize the state 2438c2ecf20Sopenharmony_ci * automatically from notif.c:i2400m_notification_grok() into 2448c2ecf20Sopenharmony_ci * i2400m_dev_bootstrap_delayed(). 2458c2ecf20Sopenharmony_ci * 2468c2ecf20Sopenharmony_ci * Cold and bus (USB) reset: 2478c2ecf20Sopenharmony_ci * 2488c2ecf20Sopenharmony_ci * The device will be fully reset internally, disconnected from the 2498c2ecf20Sopenharmony_ci * USB bus an a reenumeration will happen. Firmware upload will be 2508c2ecf20Sopenharmony_ci * necessary. Thus, we don't do any locking or struct 2518c2ecf20Sopenharmony_ci * reinitialization, as we are going to be fully disconnected and 2528c2ecf20Sopenharmony_ci * reenumerated. 2538c2ecf20Sopenharmony_ci * 2548c2ecf20Sopenharmony_ci * Note we need to return -ENODEV if a warm reset was requested and we 2558c2ecf20Sopenharmony_ci * had to resort to a bus reset. See i2400m_op_reset(), wimax_reset() 2568c2ecf20Sopenharmony_ci * and wimax_dev->op_reset. 2578c2ecf20Sopenharmony_ci * 2588c2ecf20Sopenharmony_ci * WARNING: no driver state saved/fixed 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_cistatic 2618c2ecf20Sopenharmony_ciint i2400mu_bus_reset(struct i2400m *i2400m, enum i2400m_reset_type rt) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci int result; 2648c2ecf20Sopenharmony_ci struct i2400mu *i2400mu = 2658c2ecf20Sopenharmony_ci container_of(i2400m, struct i2400mu, i2400m); 2668c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 2678c2ecf20Sopenharmony_ci static const __le32 i2400m_WARM_BOOT_BARKER[4] = { 2688c2ecf20Sopenharmony_ci cpu_to_le32(I2400M_WARM_RESET_BARKER), 2698c2ecf20Sopenharmony_ci cpu_to_le32(I2400M_WARM_RESET_BARKER), 2708c2ecf20Sopenharmony_ci cpu_to_le32(I2400M_WARM_RESET_BARKER), 2718c2ecf20Sopenharmony_ci cpu_to_le32(I2400M_WARM_RESET_BARKER), 2728c2ecf20Sopenharmony_ci }; 2738c2ecf20Sopenharmony_ci static const __le32 i2400m_COLD_BOOT_BARKER[4] = { 2748c2ecf20Sopenharmony_ci cpu_to_le32(I2400M_COLD_RESET_BARKER), 2758c2ecf20Sopenharmony_ci cpu_to_le32(I2400M_COLD_RESET_BARKER), 2768c2ecf20Sopenharmony_ci cpu_to_le32(I2400M_COLD_RESET_BARKER), 2778c2ecf20Sopenharmony_ci cpu_to_le32(I2400M_COLD_RESET_BARKER), 2788c2ecf20Sopenharmony_ci }; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci d_fnstart(3, dev, "(i2400m %p rt %u)\n", i2400m, rt); 2818c2ecf20Sopenharmony_ci if (rt == I2400M_RT_WARM) 2828c2ecf20Sopenharmony_ci result = __i2400mu_send_barker( 2838c2ecf20Sopenharmony_ci i2400mu, i2400m_WARM_BOOT_BARKER, 2848c2ecf20Sopenharmony_ci sizeof(i2400m_WARM_BOOT_BARKER), 2858c2ecf20Sopenharmony_ci i2400mu->endpoint_cfg.bulk_out); 2868c2ecf20Sopenharmony_ci else if (rt == I2400M_RT_COLD) 2878c2ecf20Sopenharmony_ci result = __i2400mu_send_barker( 2888c2ecf20Sopenharmony_ci i2400mu, i2400m_COLD_BOOT_BARKER, 2898c2ecf20Sopenharmony_ci sizeof(i2400m_COLD_BOOT_BARKER), 2908c2ecf20Sopenharmony_ci i2400mu->endpoint_cfg.reset_cold); 2918c2ecf20Sopenharmony_ci else if (rt == I2400M_RT_BUS) { 2928c2ecf20Sopenharmony_ci result = usb_reset_device(i2400mu->usb_dev); 2938c2ecf20Sopenharmony_ci switch (result) { 2948c2ecf20Sopenharmony_ci case 0: 2958c2ecf20Sopenharmony_ci case -EINVAL: /* device is gone */ 2968c2ecf20Sopenharmony_ci case -ENODEV: 2978c2ecf20Sopenharmony_ci case -ENOENT: 2988c2ecf20Sopenharmony_ci case -ESHUTDOWN: 2998c2ecf20Sopenharmony_ci result = 0; 3008c2ecf20Sopenharmony_ci break; /* We assume the device is disconnected */ 3018c2ecf20Sopenharmony_ci default: 3028c2ecf20Sopenharmony_ci dev_err(dev, "USB reset failed (%d), giving up!\n", 3038c2ecf20Sopenharmony_ci result); 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci } else { 3068c2ecf20Sopenharmony_ci result = -EINVAL; /* shut gcc up in certain arches */ 3078c2ecf20Sopenharmony_ci BUG(); 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci if (result < 0 3108c2ecf20Sopenharmony_ci && result != -EINVAL /* device is gone */ 3118c2ecf20Sopenharmony_ci && rt != I2400M_RT_BUS) { 3128c2ecf20Sopenharmony_ci /* 3138c2ecf20Sopenharmony_ci * Things failed -- resort to lower level reset, that 3148c2ecf20Sopenharmony_ci * we queue in another context; the reason for this is 3158c2ecf20Sopenharmony_ci * that the pre and post reset functionality requires 3168c2ecf20Sopenharmony_ci * the i2400m->init_mutex; RT_WARM and RT_COLD can 3178c2ecf20Sopenharmony_ci * come from areas where i2400m->init_mutex is taken. 3188c2ecf20Sopenharmony_ci */ 3198c2ecf20Sopenharmony_ci dev_err(dev, "%s reset failed (%d); trying USB reset\n", 3208c2ecf20Sopenharmony_ci rt == I2400M_RT_WARM ? "warm" : "cold", result); 3218c2ecf20Sopenharmony_ci usb_queue_reset_device(i2400mu->usb_iface); 3228c2ecf20Sopenharmony_ci result = -ENODEV; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci d_fnend(3, dev, "(i2400m %p rt %u) = %d\n", i2400m, rt, result); 3258c2ecf20Sopenharmony_ci return result; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic void i2400mu_get_drvinfo(struct net_device *net_dev, 3298c2ecf20Sopenharmony_ci struct ethtool_drvinfo *info) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci struct i2400m *i2400m = net_dev_to_i2400m(net_dev); 3328c2ecf20Sopenharmony_ci struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m); 3338c2ecf20Sopenharmony_ci struct usb_device *udev = i2400mu->usb_dev; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); 3368c2ecf20Sopenharmony_ci strlcpy(info->fw_version, i2400m->fw_name ? : "", 3378c2ecf20Sopenharmony_ci sizeof(info->fw_version)); 3388c2ecf20Sopenharmony_ci usb_make_path(udev, info->bus_info, sizeof(info->bus_info)); 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic const struct ethtool_ops i2400mu_ethtool_ops = { 3428c2ecf20Sopenharmony_ci .get_drvinfo = i2400mu_get_drvinfo, 3438c2ecf20Sopenharmony_ci .get_link = ethtool_op_get_link, 3448c2ecf20Sopenharmony_ci}; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic 3478c2ecf20Sopenharmony_civoid i2400mu_netdev_setup(struct net_device *net_dev) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci struct i2400m *i2400m = net_dev_to_i2400m(net_dev); 3508c2ecf20Sopenharmony_ci struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m); 3518c2ecf20Sopenharmony_ci i2400mu_init(i2400mu); 3528c2ecf20Sopenharmony_ci i2400m_netdev_setup(net_dev); 3538c2ecf20Sopenharmony_ci net_dev->ethtool_ops = &i2400mu_ethtool_ops; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci/* 3588c2ecf20Sopenharmony_ci * Debug levels control; see debug.h 3598c2ecf20Sopenharmony_ci */ 3608c2ecf20Sopenharmony_cistruct d_level D_LEVEL[] = { 3618c2ecf20Sopenharmony_ci D_SUBMODULE_DEFINE(usb), 3628c2ecf20Sopenharmony_ci D_SUBMODULE_DEFINE(fw), 3638c2ecf20Sopenharmony_ci D_SUBMODULE_DEFINE(notif), 3648c2ecf20Sopenharmony_ci D_SUBMODULE_DEFINE(rx), 3658c2ecf20Sopenharmony_ci D_SUBMODULE_DEFINE(tx), 3668c2ecf20Sopenharmony_ci}; 3678c2ecf20Sopenharmony_cisize_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic 3708c2ecf20Sopenharmony_civoid i2400mu_debugfs_add(struct i2400mu *i2400mu) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct dentry *dentry = i2400mu->i2400m.wimax_dev.debugfs_dentry; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci dentry = debugfs_create_dir("i2400m-usb", dentry); 3758c2ecf20Sopenharmony_ci i2400mu->debugfs_dentry = dentry; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci d_level_register_debugfs("dl_", usb, dentry); 3788c2ecf20Sopenharmony_ci d_level_register_debugfs("dl_", fw, dentry); 3798c2ecf20Sopenharmony_ci d_level_register_debugfs("dl_", notif, dentry); 3808c2ecf20Sopenharmony_ci d_level_register_debugfs("dl_", rx, dentry); 3818c2ecf20Sopenharmony_ci d_level_register_debugfs("dl_", tx, dentry); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* Don't touch these if you don't know what you are doing */ 3848c2ecf20Sopenharmony_ci debugfs_create_u8("rx_size_auto_shrink", 0600, dentry, 3858c2ecf20Sopenharmony_ci &i2400mu->rx_size_auto_shrink); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci debugfs_create_size_t("rx_size", 0600, dentry, &i2400mu->rx_size); 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic struct device_type i2400mu_type = { 3928c2ecf20Sopenharmony_ci .name = "wimax", 3938c2ecf20Sopenharmony_ci}; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci/* 3968c2ecf20Sopenharmony_ci * Probe a i2400m interface and register it 3978c2ecf20Sopenharmony_ci * 3988c2ecf20Sopenharmony_ci * @iface: USB interface to link to 3998c2ecf20Sopenharmony_ci * @id: USB class/subclass/protocol id 4008c2ecf20Sopenharmony_ci * @returns: 0 if ok, < 0 errno code on error. 4018c2ecf20Sopenharmony_ci * 4028c2ecf20Sopenharmony_ci * Alloc a net device, initialize the bus-specific details and then 4038c2ecf20Sopenharmony_ci * calls the bus-generic initialization routine. That will register 4048c2ecf20Sopenharmony_ci * the wimax and netdev devices, upload the firmware [using 4058c2ecf20Sopenharmony_ci * _bus_bm_*()], call _bus_dev_start() to finalize the setup of the 4068c2ecf20Sopenharmony_ci * communication with the device and then will start to talk to it to 4078c2ecf20Sopenharmony_ci * finnish setting it up. 4088c2ecf20Sopenharmony_ci */ 4098c2ecf20Sopenharmony_cistatic 4108c2ecf20Sopenharmony_ciint i2400mu_probe(struct usb_interface *iface, 4118c2ecf20Sopenharmony_ci const struct usb_device_id *id) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci int result; 4148c2ecf20Sopenharmony_ci struct net_device *net_dev; 4158c2ecf20Sopenharmony_ci struct device *dev = &iface->dev; 4168c2ecf20Sopenharmony_ci struct i2400m *i2400m; 4178c2ecf20Sopenharmony_ci struct i2400mu *i2400mu; 4188c2ecf20Sopenharmony_ci struct usb_device *usb_dev = interface_to_usbdev(iface); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci if (iface->cur_altsetting->desc.bNumEndpoints < 4) 4218c2ecf20Sopenharmony_ci return -ENODEV; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (usb_dev->speed != USB_SPEED_HIGH) 4248c2ecf20Sopenharmony_ci dev_err(dev, "device not connected as high speed\n"); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* Allocate instance [calls i2400m_netdev_setup() on it]. */ 4278c2ecf20Sopenharmony_ci result = -ENOMEM; 4288c2ecf20Sopenharmony_ci net_dev = alloc_netdev(sizeof(*i2400mu), "wmx%d", NET_NAME_UNKNOWN, 4298c2ecf20Sopenharmony_ci i2400mu_netdev_setup); 4308c2ecf20Sopenharmony_ci if (net_dev == NULL) { 4318c2ecf20Sopenharmony_ci dev_err(dev, "no memory for network device instance\n"); 4328c2ecf20Sopenharmony_ci goto error_alloc_netdev; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci SET_NETDEV_DEV(net_dev, dev); 4358c2ecf20Sopenharmony_ci SET_NETDEV_DEVTYPE(net_dev, &i2400mu_type); 4368c2ecf20Sopenharmony_ci i2400m = net_dev_to_i2400m(net_dev); 4378c2ecf20Sopenharmony_ci i2400mu = container_of(i2400m, struct i2400mu, i2400m); 4388c2ecf20Sopenharmony_ci i2400m->wimax_dev.net_dev = net_dev; 4398c2ecf20Sopenharmony_ci i2400mu->usb_dev = usb_get_dev(usb_dev); 4408c2ecf20Sopenharmony_ci i2400mu->usb_iface = iface; 4418c2ecf20Sopenharmony_ci usb_set_intfdata(iface, i2400mu); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci i2400m->bus_tx_block_size = I2400MU_BLK_SIZE; 4448c2ecf20Sopenharmony_ci /* 4458c2ecf20Sopenharmony_ci * Room required in the Tx queue for USB message to accommodate 4468c2ecf20Sopenharmony_ci * a smallest payload while allocating header space is 16 bytes. 4478c2ecf20Sopenharmony_ci * Adding this room for the new tx message increases the 4488c2ecf20Sopenharmony_ci * possibilities of including any payload with size <= 16 bytes. 4498c2ecf20Sopenharmony_ci */ 4508c2ecf20Sopenharmony_ci i2400m->bus_tx_room_min = I2400MU_BLK_SIZE; 4518c2ecf20Sopenharmony_ci i2400m->bus_pl_size_max = I2400MU_PL_SIZE_MAX; 4528c2ecf20Sopenharmony_ci i2400m->bus_setup = NULL; 4538c2ecf20Sopenharmony_ci i2400m->bus_dev_start = i2400mu_bus_dev_start; 4548c2ecf20Sopenharmony_ci i2400m->bus_dev_stop = i2400mu_bus_dev_stop; 4558c2ecf20Sopenharmony_ci i2400m->bus_release = NULL; 4568c2ecf20Sopenharmony_ci i2400m->bus_tx_kick = i2400mu_bus_tx_kick; 4578c2ecf20Sopenharmony_ci i2400m->bus_reset = i2400mu_bus_reset; 4588c2ecf20Sopenharmony_ci i2400m->bus_bm_retries = I2400M_USB_BOOT_RETRIES; 4598c2ecf20Sopenharmony_ci i2400m->bus_bm_cmd_send = i2400mu_bus_bm_cmd_send; 4608c2ecf20Sopenharmony_ci i2400m->bus_bm_wait_for_ack = i2400mu_bus_bm_wait_for_ack; 4618c2ecf20Sopenharmony_ci i2400m->bus_bm_mac_addr_impaired = 0; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci switch (id->idProduct) { 4648c2ecf20Sopenharmony_ci case USB_DEVICE_ID_I6050: 4658c2ecf20Sopenharmony_ci case USB_DEVICE_ID_I6050_2: 4668c2ecf20Sopenharmony_ci case USB_DEVICE_ID_I6150: 4678c2ecf20Sopenharmony_ci case USB_DEVICE_ID_I6150_2: 4688c2ecf20Sopenharmony_ci case USB_DEVICE_ID_I6150_3: 4698c2ecf20Sopenharmony_ci case USB_DEVICE_ID_I6250: 4708c2ecf20Sopenharmony_ci i2400mu->i6050 = 1; 4718c2ecf20Sopenharmony_ci break; 4728c2ecf20Sopenharmony_ci default: 4738c2ecf20Sopenharmony_ci break; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci if (i2400mu->i6050) { 4778c2ecf20Sopenharmony_ci i2400m->bus_fw_names = i2400mu_bus_fw_names_6050; 4788c2ecf20Sopenharmony_ci i2400mu->endpoint_cfg.bulk_out = 0; 4798c2ecf20Sopenharmony_ci i2400mu->endpoint_cfg.notification = 3; 4808c2ecf20Sopenharmony_ci i2400mu->endpoint_cfg.reset_cold = 2; 4818c2ecf20Sopenharmony_ci i2400mu->endpoint_cfg.bulk_in = 1; 4828c2ecf20Sopenharmony_ci } else { 4838c2ecf20Sopenharmony_ci i2400m->bus_fw_names = i2400mu_bus_fw_names_5x50; 4848c2ecf20Sopenharmony_ci i2400mu->endpoint_cfg.bulk_out = 0; 4858c2ecf20Sopenharmony_ci i2400mu->endpoint_cfg.notification = 1; 4868c2ecf20Sopenharmony_ci i2400mu->endpoint_cfg.reset_cold = 2; 4878c2ecf20Sopenharmony_ci i2400mu->endpoint_cfg.bulk_in = 3; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 4908c2ecf20Sopenharmony_ci iface->needs_remote_wakeup = 1; /* autosuspend (15s delay) */ 4918c2ecf20Sopenharmony_ci device_init_wakeup(dev, 1); 4928c2ecf20Sopenharmony_ci pm_runtime_set_autosuspend_delay(&usb_dev->dev, 15000); 4938c2ecf20Sopenharmony_ci usb_enable_autosuspend(usb_dev); 4948c2ecf20Sopenharmony_ci#endif 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci result = i2400m_setup(i2400m, I2400M_BRI_MAC_REINIT); 4978c2ecf20Sopenharmony_ci if (result < 0) { 4988c2ecf20Sopenharmony_ci dev_err(dev, "cannot setup device: %d\n", result); 4998c2ecf20Sopenharmony_ci goto error_setup; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci i2400mu_debugfs_add(i2400mu); 5028c2ecf20Sopenharmony_ci return 0; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_cierror_setup: 5058c2ecf20Sopenharmony_ci usb_set_intfdata(iface, NULL); 5068c2ecf20Sopenharmony_ci usb_put_dev(i2400mu->usb_dev); 5078c2ecf20Sopenharmony_ci free_netdev(net_dev); 5088c2ecf20Sopenharmony_cierror_alloc_netdev: 5098c2ecf20Sopenharmony_ci return result; 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci/* 5148c2ecf20Sopenharmony_ci * Disconnect a i2400m from the system. 5158c2ecf20Sopenharmony_ci * 5168c2ecf20Sopenharmony_ci * i2400m_stop() has been called before, so al the rx and tx contexts 5178c2ecf20Sopenharmony_ci * have been taken down already. Make sure the queue is stopped, 5188c2ecf20Sopenharmony_ci * unregister netdev and i2400m, free and kill. 5198c2ecf20Sopenharmony_ci */ 5208c2ecf20Sopenharmony_cistatic 5218c2ecf20Sopenharmony_civoid i2400mu_disconnect(struct usb_interface *iface) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci struct i2400mu *i2400mu = usb_get_intfdata(iface); 5248c2ecf20Sopenharmony_ci struct i2400m *i2400m = &i2400mu->i2400m; 5258c2ecf20Sopenharmony_ci struct net_device *net_dev = i2400m->wimax_dev.net_dev; 5268c2ecf20Sopenharmony_ci struct device *dev = &iface->dev; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci d_fnstart(3, dev, "(iface %p i2400m %p)\n", iface, i2400m); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci debugfs_remove_recursive(i2400mu->debugfs_dentry); 5318c2ecf20Sopenharmony_ci i2400m_release(i2400m); 5328c2ecf20Sopenharmony_ci usb_set_intfdata(iface, NULL); 5338c2ecf20Sopenharmony_ci usb_put_dev(i2400mu->usb_dev); 5348c2ecf20Sopenharmony_ci free_netdev(net_dev); 5358c2ecf20Sopenharmony_ci d_fnend(3, dev, "(iface %p i2400m %p) = void\n", iface, i2400m); 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci/* 5408c2ecf20Sopenharmony_ci * Get the device ready for USB port or system standby and hibernation 5418c2ecf20Sopenharmony_ci * 5428c2ecf20Sopenharmony_ci * USB port and system standby are handled the same. 5438c2ecf20Sopenharmony_ci * 5448c2ecf20Sopenharmony_ci * When the system hibernates, the USB device is powered down and then 5458c2ecf20Sopenharmony_ci * up, so we don't really have to do much here, as it will be seen as 5468c2ecf20Sopenharmony_ci * a reconnect. Still for simplicity we consider this case the same as 5478c2ecf20Sopenharmony_ci * suspend, so that the device has a chance to do notify the base 5488c2ecf20Sopenharmony_ci * station (if connected). 5498c2ecf20Sopenharmony_ci * 5508c2ecf20Sopenharmony_ci * So at the end, the three cases require common handling. 5518c2ecf20Sopenharmony_ci * 5528c2ecf20Sopenharmony_ci * If at the time of this call the device's firmware is not loaded, 5538c2ecf20Sopenharmony_ci * nothing has to be done. Note we can be "loose" about not reading 5548c2ecf20Sopenharmony_ci * i2400m->updown under i2400m->init_mutex. If it happens to change 5558c2ecf20Sopenharmony_ci * inmediately, other parts of the call flow will fail and effectively 5568c2ecf20Sopenharmony_ci * catch it. 5578c2ecf20Sopenharmony_ci * 5588c2ecf20Sopenharmony_ci * If the firmware is loaded, we need to: 5598c2ecf20Sopenharmony_ci * 5608c2ecf20Sopenharmony_ci * - tell the device to go into host interface power save mode, wait 5618c2ecf20Sopenharmony_ci * for it to ack 5628c2ecf20Sopenharmony_ci * 5638c2ecf20Sopenharmony_ci * This is quite more interesting than it is; we need to execute a 5648c2ecf20Sopenharmony_ci * command, but this time, we don't want the code in usb-{tx,rx}.c 5658c2ecf20Sopenharmony_ci * to call the usb_autopm_get/put_interface() barriers as it'd 5668c2ecf20Sopenharmony_ci * deadlock, so we need to decrement i2400mu->do_autopm, that acts 5678c2ecf20Sopenharmony_ci * as a poor man's semaphore. Ugly, but it works. 5688c2ecf20Sopenharmony_ci * 5698c2ecf20Sopenharmony_ci * As well, the device might refuse going to sleep for whichever 5708c2ecf20Sopenharmony_ci * reason. In this case we just fail. For system suspend/hibernate, 5718c2ecf20Sopenharmony_ci * we *can't* fail. We check PMSG_IS_AUTO to see if the 5728c2ecf20Sopenharmony_ci * suspend call comes from the USB stack or from the system and act 5738c2ecf20Sopenharmony_ci * in consequence. 5748c2ecf20Sopenharmony_ci * 5758c2ecf20Sopenharmony_ci * - stop the notification endpoint polling 5768c2ecf20Sopenharmony_ci */ 5778c2ecf20Sopenharmony_cistatic 5788c2ecf20Sopenharmony_ciint i2400mu_suspend(struct usb_interface *iface, pm_message_t pm_msg) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci int result = 0; 5818c2ecf20Sopenharmony_ci struct device *dev = &iface->dev; 5828c2ecf20Sopenharmony_ci struct i2400mu *i2400mu = usb_get_intfdata(iface); 5838c2ecf20Sopenharmony_ci unsigned is_autosuspend = 0; 5848c2ecf20Sopenharmony_ci struct i2400m *i2400m = &i2400mu->i2400m; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 5878c2ecf20Sopenharmony_ci if (PMSG_IS_AUTO(pm_msg)) 5888c2ecf20Sopenharmony_ci is_autosuspend = 1; 5898c2ecf20Sopenharmony_ci#endif 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci d_fnstart(3, dev, "(iface %p pm_msg %u)\n", iface, pm_msg.event); 5928c2ecf20Sopenharmony_ci rmb(); /* see i2400m->updown's documentation */ 5938c2ecf20Sopenharmony_ci if (i2400m->updown == 0) 5948c2ecf20Sopenharmony_ci goto no_firmware; 5958c2ecf20Sopenharmony_ci if (i2400m->state == I2400M_SS_DATA_PATH_CONNECTED && is_autosuspend) { 5968c2ecf20Sopenharmony_ci /* ugh -- the device is connected and this suspend 5978c2ecf20Sopenharmony_ci * request is an autosuspend one (not a system standby 5988c2ecf20Sopenharmony_ci * / hibernate). 5998c2ecf20Sopenharmony_ci * 6008c2ecf20Sopenharmony_ci * The only way the device can go to standby is if the 6018c2ecf20Sopenharmony_ci * link with the base station is in IDLE mode; that 6028c2ecf20Sopenharmony_ci * were the case, we'd be in status 6038c2ecf20Sopenharmony_ci * I2400M_SS_CONNECTED_IDLE. But we are not. 6048c2ecf20Sopenharmony_ci * 6058c2ecf20Sopenharmony_ci * If we *tell* him to go power save now, it'll reset 6068c2ecf20Sopenharmony_ci * as a precautionary measure, so if this is an 6078c2ecf20Sopenharmony_ci * autosuspend thing, say no and it'll come back 6088c2ecf20Sopenharmony_ci * later, when the link is IDLE 6098c2ecf20Sopenharmony_ci */ 6108c2ecf20Sopenharmony_ci result = -EBADF; 6118c2ecf20Sopenharmony_ci d_printf(1, dev, "fw up, link up, not-idle, autosuspend: " 6128c2ecf20Sopenharmony_ci "not entering powersave\n"); 6138c2ecf20Sopenharmony_ci goto error_not_now; 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci d_printf(1, dev, "fw up: entering powersave\n"); 6168c2ecf20Sopenharmony_ci atomic_dec(&i2400mu->do_autopm); 6178c2ecf20Sopenharmony_ci result = i2400m_cmd_enter_powersave(i2400m); 6188c2ecf20Sopenharmony_ci atomic_inc(&i2400mu->do_autopm); 6198c2ecf20Sopenharmony_ci if (result < 0 && !is_autosuspend) { 6208c2ecf20Sopenharmony_ci /* System suspend, can't fail */ 6218c2ecf20Sopenharmony_ci dev_err(dev, "failed to suspend, will reset on resume\n"); 6228c2ecf20Sopenharmony_ci result = 0; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci if (result < 0) 6258c2ecf20Sopenharmony_ci goto error_enter_powersave; 6268c2ecf20Sopenharmony_ci i2400mu_notification_release(i2400mu); 6278c2ecf20Sopenharmony_ci d_printf(1, dev, "powersave requested\n"); 6288c2ecf20Sopenharmony_cierror_enter_powersave: 6298c2ecf20Sopenharmony_cierror_not_now: 6308c2ecf20Sopenharmony_cino_firmware: 6318c2ecf20Sopenharmony_ci d_fnend(3, dev, "(iface %p pm_msg %u) = %d\n", 6328c2ecf20Sopenharmony_ci iface, pm_msg.event, result); 6338c2ecf20Sopenharmony_ci return result; 6348c2ecf20Sopenharmony_ci} 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_cistatic 6388c2ecf20Sopenharmony_ciint i2400mu_resume(struct usb_interface *iface) 6398c2ecf20Sopenharmony_ci{ 6408c2ecf20Sopenharmony_ci int ret = 0; 6418c2ecf20Sopenharmony_ci struct device *dev = &iface->dev; 6428c2ecf20Sopenharmony_ci struct i2400mu *i2400mu = usb_get_intfdata(iface); 6438c2ecf20Sopenharmony_ci struct i2400m *i2400m = &i2400mu->i2400m; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci d_fnstart(3, dev, "(iface %p)\n", iface); 6468c2ecf20Sopenharmony_ci rmb(); /* see i2400m->updown's documentation */ 6478c2ecf20Sopenharmony_ci if (i2400m->updown == 0) { 6488c2ecf20Sopenharmony_ci d_printf(1, dev, "fw was down, no resume needed\n"); 6498c2ecf20Sopenharmony_ci goto out; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci d_printf(1, dev, "fw was up, resuming\n"); 6528c2ecf20Sopenharmony_ci i2400mu_notification_setup(i2400mu); 6538c2ecf20Sopenharmony_ci /* USB has flow control, so we don't need to give it time to 6548c2ecf20Sopenharmony_ci * come back; otherwise, we'd use something like a get-state 6558c2ecf20Sopenharmony_ci * command... */ 6568c2ecf20Sopenharmony_ciout: 6578c2ecf20Sopenharmony_ci d_fnend(3, dev, "(iface %p) = %d\n", iface, ret); 6588c2ecf20Sopenharmony_ci return ret; 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_cistatic 6638c2ecf20Sopenharmony_ciint i2400mu_reset_resume(struct usb_interface *iface) 6648c2ecf20Sopenharmony_ci{ 6658c2ecf20Sopenharmony_ci int result; 6668c2ecf20Sopenharmony_ci struct device *dev = &iface->dev; 6678c2ecf20Sopenharmony_ci struct i2400mu *i2400mu = usb_get_intfdata(iface); 6688c2ecf20Sopenharmony_ci struct i2400m *i2400m = &i2400mu->i2400m; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci d_fnstart(3, dev, "(iface %p)\n", iface); 6718c2ecf20Sopenharmony_ci result = i2400m_dev_reset_handle(i2400m, "device reset on resume"); 6728c2ecf20Sopenharmony_ci d_fnend(3, dev, "(iface %p) = %d\n", iface, result); 6738c2ecf20Sopenharmony_ci return result < 0 ? result : 0; 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci/* 6788c2ecf20Sopenharmony_ci * Another driver or user space is triggering a reset on the device 6798c2ecf20Sopenharmony_ci * which contains the interface passed as an argument. Cease IO and 6808c2ecf20Sopenharmony_ci * save any device state you need to restore. 6818c2ecf20Sopenharmony_ci * 6828c2ecf20Sopenharmony_ci * If you need to allocate memory here, use GFP_NOIO or GFP_ATOMIC, if 6838c2ecf20Sopenharmony_ci * you are in atomic context. 6848c2ecf20Sopenharmony_ci */ 6858c2ecf20Sopenharmony_cistatic 6868c2ecf20Sopenharmony_ciint i2400mu_pre_reset(struct usb_interface *iface) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci struct i2400mu *i2400mu = usb_get_intfdata(iface); 6898c2ecf20Sopenharmony_ci return i2400m_pre_reset(&i2400mu->i2400m); 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci/* 6948c2ecf20Sopenharmony_ci * The reset has completed. Restore any saved device state and begin 6958c2ecf20Sopenharmony_ci * using the device again. 6968c2ecf20Sopenharmony_ci * 6978c2ecf20Sopenharmony_ci * If you need to allocate memory here, use GFP_NOIO or GFP_ATOMIC, if 6988c2ecf20Sopenharmony_ci * you are in atomic context. 6998c2ecf20Sopenharmony_ci */ 7008c2ecf20Sopenharmony_cistatic 7018c2ecf20Sopenharmony_ciint i2400mu_post_reset(struct usb_interface *iface) 7028c2ecf20Sopenharmony_ci{ 7038c2ecf20Sopenharmony_ci struct i2400mu *i2400mu = usb_get_intfdata(iface); 7048c2ecf20Sopenharmony_ci return i2400m_post_reset(&i2400mu->i2400m); 7058c2ecf20Sopenharmony_ci} 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_cistatic 7098c2ecf20Sopenharmony_cistruct usb_device_id i2400mu_id_table[] = { 7108c2ecf20Sopenharmony_ci { USB_DEVICE(0x8086, USB_DEVICE_ID_I6050) }, 7118c2ecf20Sopenharmony_ci { USB_DEVICE(0x8086, USB_DEVICE_ID_I6050_2) }, 7128c2ecf20Sopenharmony_ci { USB_DEVICE(0x8087, USB_DEVICE_ID_I6150) }, 7138c2ecf20Sopenharmony_ci { USB_DEVICE(0x8087, USB_DEVICE_ID_I6150_2) }, 7148c2ecf20Sopenharmony_ci { USB_DEVICE(0x8087, USB_DEVICE_ID_I6150_3) }, 7158c2ecf20Sopenharmony_ci { USB_DEVICE(0x8086, USB_DEVICE_ID_I6250) }, 7168c2ecf20Sopenharmony_ci { USB_DEVICE(0x8086, 0x0181) }, 7178c2ecf20Sopenharmony_ci { USB_DEVICE(0x8086, 0x1403) }, 7188c2ecf20Sopenharmony_ci { USB_DEVICE(0x8086, 0x1405) }, 7198c2ecf20Sopenharmony_ci { USB_DEVICE(0x8086, 0x0180) }, 7208c2ecf20Sopenharmony_ci { USB_DEVICE(0x8086, 0x0182) }, 7218c2ecf20Sopenharmony_ci { USB_DEVICE(0x8086, 0x1406) }, 7228c2ecf20Sopenharmony_ci { USB_DEVICE(0x8086, 0x1403) }, 7238c2ecf20Sopenharmony_ci { }, 7248c2ecf20Sopenharmony_ci}; 7258c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, i2400mu_id_table); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_cistatic 7298c2ecf20Sopenharmony_cistruct usb_driver i2400mu_driver = { 7308c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 7318c2ecf20Sopenharmony_ci .suspend = i2400mu_suspend, 7328c2ecf20Sopenharmony_ci .resume = i2400mu_resume, 7338c2ecf20Sopenharmony_ci .reset_resume = i2400mu_reset_resume, 7348c2ecf20Sopenharmony_ci .probe = i2400mu_probe, 7358c2ecf20Sopenharmony_ci .disconnect = i2400mu_disconnect, 7368c2ecf20Sopenharmony_ci .pre_reset = i2400mu_pre_reset, 7378c2ecf20Sopenharmony_ci .post_reset = i2400mu_post_reset, 7388c2ecf20Sopenharmony_ci .id_table = i2400mu_id_table, 7398c2ecf20Sopenharmony_ci .supports_autosuspend = 1, 7408c2ecf20Sopenharmony_ci}; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_cistatic 7438c2ecf20Sopenharmony_ciint __init i2400mu_driver_init(void) 7448c2ecf20Sopenharmony_ci{ 7458c2ecf20Sopenharmony_ci d_parse_params(D_LEVEL, D_LEVEL_SIZE, i2400mu_debug_params, 7468c2ecf20Sopenharmony_ci "i2400m_usb.debug"); 7478c2ecf20Sopenharmony_ci return usb_register(&i2400mu_driver); 7488c2ecf20Sopenharmony_ci} 7498c2ecf20Sopenharmony_cimodule_init(i2400mu_driver_init); 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_cistatic 7538c2ecf20Sopenharmony_civoid __exit i2400mu_driver_exit(void) 7548c2ecf20Sopenharmony_ci{ 7558c2ecf20Sopenharmony_ci usb_deregister(&i2400mu_driver); 7568c2ecf20Sopenharmony_ci} 7578c2ecf20Sopenharmony_cimodule_exit(i2400mu_driver_exit); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ciMODULE_AUTHOR("Intel Corporation <linux-wimax@intel.com>"); 7608c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for USB based Intel Wireless WiMAX Connection 2400M " 7618c2ecf20Sopenharmony_ci "(5x50 & 6050)"); 7628c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 7638c2ecf20Sopenharmony_ciMODULE_FIRMWARE(I2400MU_FW_FILE_NAME_v1_5); 7648c2ecf20Sopenharmony_ciMODULE_FIRMWARE(I6050U_FW_FILE_NAME_v1_5); 765