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